diff --git a/.github/scripts/check-sp11-integration.sh b/.github/scripts/check-sp11-integration.sh index cec4fada2b72d7..abb6b1665e9366 100755 --- a/.github/scripts/check-sp11-integration.sh +++ b/.github/scripts/check-sp11-integration.sh @@ -3,7 +3,14 @@ set -euo pipefail -readonly SP11_BASE_COMMIT="87bcf07d1ed79960c0f2e769ada5b8b05fd35c48" +readonly SP11_BASE_COMMIT="e57ec2987d4540fa89280d349f79c4d9bcd7cf27" +# This exact checkpoint imports the rebased upstream while retaining the +# original SP11 history. Its tree was reconstructed from the 32 merged SP11 +# commits plus the existing Denali EC-reset GPIO reservation. No held topic +# is included. Only this immutable merge may bypass the linear-topic rule; +# its complete SP11 delta remains covered by the integration checks above. +readonly SP11_REFRESH_COMMIT="eea05165923210e93de26484b040f3f3b398b435" +readonly SP11_PREVIOUS_BETA_COMMIT="bf631f9a13b3f6bd622645ba95165c8098932c95" readonly SP11_RANGE="${SP11_BASE_COMMIT}...HEAD" die() { @@ -11,6 +18,26 @@ die() { exit 1 } +reviewed_private_defaults_warning() { + local commit="$1" output="$2" diagnostics patch_id blob_id + + # The Denali producer patch mutates a private defaults copy before regmap + # initialization. CONST_STRUCT is inapplicable to that writable argument. + # Pin both the patch and exact resulting source. Stable patch IDs ignore + # whitespace, including meaningful whitespace inside string literals. + # Accept exactly this one diagnostic; other findings still fail closed. + diagnostics="$(grep -E '^(ERROR|WARNING|CHECK):' <<<"$output" || true)" + [[ "$diagnostics" == \ + 'WARNING:CONST_STRUCT: struct reg_default should normally be const' ]] || + return 1 + blob_id="$(git rev-parse --verify \ + "${commit}:sound/soc/codecs/lpass-wsa-macro.c")" || return 1 + [[ "$blob_id" == af26d75c4017cb672722bb9e742b7c7fa24ec57e ]] || return 1 + patch_id="$(git diff --no-ext-diff "${commit}^" "$commit" -- | + git patch-id --stable)" || return 1 + [[ "${patch_id%% *}" == 5a64ac327ac508b199b27da018f14ac2d61ad0f4 ]] +} + git rev-parse --is-inside-work-tree >/dev/null 2>&1 || die "run this check from the repository worktree" @@ -151,6 +178,21 @@ if [[ -n "${review_base}" || -n "${review_head}" ]]; then while IFS= read -r commit; do commit_and_parents=() read -r -a commit_and_parents <<<"$(git rev-list --parents -n 1 "${commit}")" + if [[ "${commit}" == "${SP11_REFRESH_COMMIT}" ]]; then + ((${#commit_and_parents[@]} == 3)) || + die "approved upstream refresh must have two parents" + [[ "${commit_and_parents[1]}" == "${SP11_PREVIOUS_BETA_COMMIT}" ]] || + die "approved upstream refresh has the wrong beta parent" + [[ "${commit_and_parents[2]}" == "${SP11_BASE_COMMIT}" ]] || + die "approved upstream refresh has the wrong upstream parent" + git merge-base --is-ancestor "${review_base}" \ + "${commit_and_parents[1]}" || + die "approved upstream refresh does not retain the review base" + review_count=$((review_count + 1)) + printf 'Approved upstream refresh %s is checked as an integration delta.\n' \ + "${commit}" + continue + fi ((${#commit_and_parents[@]} == 2)) || die "topic range must be linear; merge commit found: ${commit}" @@ -168,9 +210,15 @@ if [[ -n "${review_base}" || -n "${review_head}" ]]; then )" printf '%s\n' "${commit_checkpatch_output}" if grep -qE '^(ERROR|WARNING|CHECK):' <<<"${commit_checkpatch_output}"; then - die "per-commit source checkpatch findings in ${commit}" + reviewed_private_defaults_warning "$commit" \ + "$commit_checkpatch_output" || + die "per-commit source checkpatch findings in ${commit}" + printf 'Reviewed private-defaults warning retained for %s.\n' "$commit" fi - done < <(git rev-list --reverse "${review_base}..${review_head}") + # Upstream was rebased; its imported history is not an SP11 topic series. + # Exclude only the pinned upstream ancestry, never arbitrary merge parents. + done < <(git rev-list --reverse "${review_base}..${review_head}" \ + "^${SP11_BASE_COMMIT}") ((review_count > 0)) || die "pull request contains no topic commits" printf 'Per-commit source checks passed for %d topic commits.\n' \ diff --git a/Documentation/ABI/testing/sysfs-bus-nvdimm b/Documentation/ABI/testing/sysfs-bus-nvdimm index 64eb8f4c6a41f2..46dafd8482b9ed 100644 --- a/Documentation/ABI/testing/sysfs-bus-nvdimm +++ b/Documentation/ABI/testing/sysfs-bus-nvdimm @@ -48,7 +48,8 @@ What: /sys/bus/nd/devices/nmemX/cxl/id Date: November 2022 KernelVersion: 6.2 Contact: Dave Jiang -Description: (RO) Show the id (serial) of the device. This is CXL specific. +Description: (RO) Show the id (serial) of the device, formatted as an + unsigned 64-bit decimal value. This is CXL specific. What: /sys/bus/nd/devices/nmemX/cxl/provider Date: November 2022 diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-damon b/Documentation/ABI/testing/sysfs-kernel-mm-damon index 4fdec63a47d4aa..59010885618cc5 100644 --- a/Documentation/ABI/testing/sysfs-kernel-mm-damon +++ b/Documentation/ABI/testing/sysfs-kernel-mm-damon @@ -112,7 +112,7 @@ Description: Writing a value to this file sets the update interval of the DAMON context in microseconds as the value. Reading this file returns the value. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/intrvals_goal/access_bp +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/intervals_goal/access_bp Date: Feb 2025 Contact: SJ Park Description: Writing a value to this file sets the monitoring intervals @@ -120,7 +120,7 @@ Description: Writing a value to this file sets the monitoring intervals the given time interval (aggrs in same directory), in bp (1/10,000). Reading this file returns the value. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/intrvals_goal/aggrs +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/intervals_goal/aggrs Date: Feb 2025 Contact: SJ Park Description: Writing a value to this file sets the time interval to achieve @@ -128,14 +128,14 @@ Description: Writing a value to this file sets the time interval to achieve access events ratio (access_bp in same directory) within. Reading this file returns the value. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/intrvals_goal/min_sample_us +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/intervals_goal/min_sample_us Date: Feb 2025 Contact: SJ Park Description: Writing a value to this file sets the minimum value of auto-tuned sampling interval in microseconds. Reading this file returns the value. -What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/intrvals_goal/max_sample_us +What: /sys/kernel/mm/damon/admin/kdamonds//contexts//monitoring_attrs/intervals/intervals_goal/max_sample_us Date: Feb 2025 Contact: SJ Park Description: Writing a value to this file sets the maximum value of diff --git a/Documentation/admin-guide/blockdev/zoned_loop.rst b/Documentation/admin-guide/blockdev/zoned_loop.rst index f4f1f3121bf953..95974425ca0546 100644 --- a/Documentation/admin-guide/blockdev/zoned_loop.rst +++ b/Documentation/admin-guide/blockdev/zoned_loop.rst @@ -30,11 +30,10 @@ indicates the position of the write pointer of the zone. When resetting a sequential zone, its backing file size is truncated to zero. Conversely, for a zone finish operation, the backing file is truncated to the -zone size. With this, the maximum capacity of a zloop zoned block device created -can be larger configured to be larger than the storage space available on the -backing file system. Of course, for such configuration, writing more data than -the storage space available on the backing file system will result in write -errors. +zone capacity. With this, a zloop zoned block device can be configured with a +larger capacity than the storage space available on the backing file system. Of +course, for such configuration, writing more data than the storage space +available on the backing file system will result in write errors. The zoned loop block device driver implements a complete zone transition state machine. That is, zones can be empty, implicitly opened, explicitly opened, diff --git a/Documentation/admin-guide/cgroup-v1/memory.rst b/Documentation/admin-guide/cgroup-v1/memory.rst index 7db63c0029221e..7d2a44af52c932 100644 --- a/Documentation/admin-guide/cgroup-v1/memory.rst +++ b/Documentation/admin-guide/cgroup-v1/memory.rst @@ -47,7 +47,6 @@ Features: - pages are linked to per-memcg LRU exclusively, and there is no global LRU. - optionally, memory+swap usage can be accounted and limited. - hierarchical accounting - - soft limit - moving (recharging) account at moving a task is selectable. - usage threshold notifier - memory pressure notifier @@ -76,10 +75,9 @@ Brief summary of control files. memory.memsw.failcnt show the number of memory+Swap hits limits memory.max_usage_in_bytes show max memory usage recorded memory.memsw.max_usage_in_bytes show max memory+Swap usage recorded - memory.soft_limit_in_bytes set/show soft limit of memory usage - This knob is not available on CONFIG_PREEMPT_RT systems. - This knob is deprecated and shouldn't be - used. + memory.soft_limit_in_bytes This knob is deprecated and has no effect. + Writes are ignored and reads always + return the maximum value. memory.stat show various statistics memory.use_hierarchy set/show hierarchical account enabled This knob is deprecated and shouldn't be @@ -340,9 +338,6 @@ memory.kmem.usage_in_bytes, or in a separate counter when it makes sense. The main "kmem" counter is fed into the main counter, so kmem charges will also be visible from the user counter. -Currently no soft limit is implemented for kernel memory. It is future work -to trigger slab reclaim when those limits are reached. - 2.7.1 Current Kernel Memory resources accounted ----------------------------------------------- @@ -710,42 +705,10 @@ For compatibility reasons writing 1 to memory.use_hierarchy will always pass:: THIS IS DEPRECATED! -Soft limits allow for greater sharing of memory. The idea behind soft limits -is to allow control groups to use as much of the memory as needed, provided - -a. There is no memory contention -b. They do not exceed their hard limit - -When the system detects memory contention or low memory, control groups -are pushed back to their soft limits. If the soft limit of each control -group is very high, they are pushed back as much as possible to make -sure that one control group does not starve the others of memory. - -Please note that soft limits is a best-effort feature; it comes with -no guarantees, but it does its best to make sure that when memory is -heavily contended for, memory is allocated based on the soft limit -hints/setup. Currently soft limit based reclaim is set up such that -it gets invoked from balance_pgdat (kswapd). - -7.1 Interface -------------- - -Soft limits can be setup by using the following commands (in this example we -assume a soft limit of 256 MiB):: - - # echo 256M > memory.soft_limit_in_bytes - -If we want to change this to 1G, we can at any time use:: +Writing to memory.soft_limit_in_bytes has no effect and reading it will +always return the maximum value. - # echo 1G > memory.soft_limit_in_bytes - -.. note:: - Soft limits take effect over a long period of time, since they involve - reclaiming memory for balancing between memory cgroups - -.. note:: - It is recommended to set the soft limit always below the hard limit, - otherwise the hard limit will take precedence. +Use memory.low and memory.min in cgroup v2 instead. .. _cgroup-v1-memory-move-charges: diff --git a/Documentation/devicetree/bindings/arm/qcom.yaml b/Documentation/devicetree/bindings/arm/qcom.yaml index d8b3d2c722fcc6..19f4ef29266934 100644 --- a/Documentation/devicetree/bindings/arm/qcom.yaml +++ b/Documentation/devicetree/bindings/arm/qcom.yaml @@ -1137,6 +1137,11 @@ properties: - const: qcom,x1e001de - const: qcom,x1e80100 + - items: + - const: lenovo,hamoa-ideacentre-mini-01q8x10 + - const: lenovo,ideacentre-mini-01q8x10 + - const: qcom,x1e80100 + - items: - enum: - lenovo,thinkpad-t14s-lcd @@ -1174,7 +1179,6 @@ properties: - hp,elitebook-6-g1q - hp,elitebook-ultra-g1q - hp,omnibook-x14 - - lenovo,ideacentre-mini-01q8x10 - lenovo,yoga-slim7x - microsoft,romulus13 - microsoft,romulus15 @@ -1199,6 +1203,11 @@ properties: - const: microsoft,denali - const: qcom,x1e80100 + - items: + - const: lenovo,purwa-ideacentre-mini-01q8x10 + - const: lenovo,ideacentre-mini-01q8x10 + - const: qcom,x1p42100 + - items: - const: microsoft,surface-pro-12in - const: qcom,x1p42100 @@ -1222,6 +1231,13 @@ properties: - const: qcom,x1p64100 - const: qcom,x1e80100 + - items: + - enum: + - lenovo,ideapad-slim5x-lcd + - lenovo,ideapad-slim5x-oled + - const: lenovo,ideapad-5 + - const: qcom,x1p42100 + - items: - enum: - acer,swift-go14-01 diff --git a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml index b59c4ce30b8b6b..279c9cd3034ad1 100644 --- a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml +++ b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml @@ -117,7 +117,9 @@ allOf: compatible: not: contains: - const: fsl,imx91-isi + enum: + - fsl,imx8ulp-isi + - fsl,imx91-isi then: required: - fsl,blk-ctrl diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index 2c59501c33c511..6875c1515512bd 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -29,6 +29,9 @@ properties: - qcom,sm8650-sndcard - qcom,sm8750-sndcard - const: qcom,sm8450-sndcard + - items: + - const: microsoft,denali-sndcard + - const: qcom,x1e80100-sndcard - enum: - dell,xps13-9345-sndcard - fairphone,fp4-sndcard diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml index 866c5e780fb0aa..9744cea5ee42b8 100644 --- a/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml @@ -40,8 +40,39 @@ properties: minItems: 6 maxItems: 6 + qcom,enable-cps: + type: boolean + description: Enable the dedicated CPS SoundWire source DAI on data port 6. + + qcom,cps-offset1: + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 255 + description: | + Slave-only SoundWire DP6 OffsetCtrl1 override for CPS. This does not + change the physical master-port schedule selected by qcom,port-mapping. + + qcom,visense-channel-mask: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 255 + description: | + Native WSA data-port-5 channel-enable mask for the VISENSE protection + sidechain. If absent, the codec default is used. + + qcom,speaker-load-ohms: + enum: [4, 8] + description: | + Nominal speaker load impedance in ohms. A 4-ohm load selects the + matching PA gain and PBR thresholds when the amplifier reports a 2S + VPHX supply. If absent, the existing 8-ohm defaults are retained. + '#sound-dai-cells': - const: 0 + enum: [0, 1] + description: | + Zero selects the legacy single speaker-playback DAI. With one cell, + DAI 0 is speaker playback, DAI 1 is the render-coupled VISENSE + protection-feedback sidechain, and DAI 2 is the dedicated CPS feedback + sidechain when qcom,enable-cps is present. vdd-1p8-supply: true vdd-io-supply: true @@ -75,7 +106,7 @@ examples: pinctrl-names = "default"; pinctrl-0 = <&spkr_2_sd_n_active>; powerdown-gpios = <&lpass_tlmm 18 GPIO_ACTIVE_LOW>; - #sound-dai-cells = <0>; + #sound-dai-cells = <1>; sound-name-prefix = "SpkrRight"; vdd-1p8-supply = <&vreg_l15b_1p8>; vdd-io-supply = <&vreg_l3g_1p2>; diff --git a/Documentation/devicetree/bindings/usb/parade,ps8830.yaml b/Documentation/devicetree/bindings/usb/parade,ps8830.yaml index 44242907461eb0..eaeab1c01a594e 100644 --- a/Documentation/devicetree/bindings/usb/parade,ps8830.yaml +++ b/Documentation/devicetree/bindings/usb/parade,ps8830.yaml @@ -66,12 +66,6 @@ properties: Sideband Use (SBU) AUX lines endpoint to the Type-C connector for the purpose of handling altmode muxing and orientation switching. - parade,disable-usb4: - type: boolean - description: - When present, the retimer rejects USB4 mode (returns -EOPNOTSUPP). - This forces the Type-C stack to fall back to USB3 + DP Alternate Mode. - required: - compatible - reg diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst index c0dd35f1af12b2..e4882b73120e32 100644 --- a/Documentation/filesystems/fscrypt.rst +++ b/Documentation/filesystems/fscrypt.rst @@ -1238,6 +1238,10 @@ astute users may notice some differences in behavior: - DAX (Direct Access) is not supported on encrypted files. +- Encrypted files cannot be used directly as swap files. To swap to + an encrypted file, set up a loopback device on top of it. + Alternatively, encrypted swap can use a dm-crypt device. + - The maximum length of an encrypted symlink is 2 bytes shorter than the maximum length of an unencrypted symlink. For example, on an EXT4 filesystem with a 4K block size, unencrypted symlinks can be up diff --git a/Documentation/trace/hisi-ptt.rst b/Documentation/trace/hisi-ptt.rst index 6eef28ebb0c7b9..f6a2655f99e57f 100644 --- a/Documentation/trace/hisi-ptt.rst +++ b/Documentation/trace/hisi-ptt.rst @@ -285,20 +285,20 @@ according to the format described previously (take 8DW as an example): [...perf headers and other information] . ... HISI PTT data: size 4194304 bytes . 00000000: 00 00 00 00 Prefix - . 00000004: 01 00 00 60 Header DW0 - . 00000008: 0f 1e 00 01 Header DW1 - . 0000000c: 04 00 00 00 Header DW2 - . 00000010: 40 00 81 02 Header DW3 - . 00000014: 33 c0 04 00 Time + . 00000004: 60 00 00 01 Header DW0 + . 00000008: 01 00 1e 0f Header DW1 + . 0000000c: 00 00 00 04 Header DW2 + . 00000010: 02 81 00 40 Header DW3 + . 00000014: 00 04 c0 33 Time . 00000020: 00 00 00 00 Prefix - . 00000024: 01 00 00 60 Header DW0 - . 00000028: 0f 1e 00 01 Header DW1 - . 0000002c: 04 00 00 00 Header DW2 - . 00000030: 40 00 81 02 Header DW3 - . 00000034: 02 00 00 00 Time + . 00000024: 60 00 00 01 Header DW0 + . 00000028: 01 00 1e 0f Header DW1 + . 0000002c: 00 00 00 04 Header DW2 + . 00000030: 02 81 00 40 Header DW3 + . 00000034: 00 00 00 02 Time . 00000040: 00 00 00 00 Prefix - . 00000044: 01 00 00 60 Header DW0 - . 00000048: 0f 1e 00 01 Header DW1 - . 0000004c: 04 00 00 00 Header DW2 - . 00000050: 40 00 81 02 Header DW3 + . 00000044: 60 00 00 01 Header DW0 + . 00000048: 01 00 1e 0f Header DW1 + . 0000004c: 00 00 00 04 Header DW2 + . 00000050: 02 81 00 40 Header DW3 [...] diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst index 38a4136d7220e3..0a7da2f168507e 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst @@ -80,15 +80,25 @@ Colorimetry Control IDs - ``white_point_x`` - Specifies the normalized x chromaticity coordinate of the white point of the mastering display in increments of 0.00002. + When both ``white_point_x`` and ``white_point_y`` are zero, + the white point chromaticity is unknown. If either coordinate is + non-zero, both coordinates shall be within their valid ranges. * - __u16 - ``white_point_y`` - Specifies the normalized y chromaticity coordinate of the white point of the mastering display in increments of 0.00002. + When both ``white_point_x`` and ``white_point_y`` are zero, + the white point chromaticity is unknown. If either coordinate is + non-zero, both coordinates shall be within their valid ranges. * - __u32 - ``max_luminance`` - Specifies the nominal maximum display luminance of the mastering display in units of 0.0001 cd/m\ :sup:`2`. + A value of zero indicates that the nominal maximum display + luminance is unknown. * - __u32 - ``min_luminance`` - - specifies the nominal minimum display luminance of the mastering + - Specifies the nominal minimum display luminance of the mastering display in units of 0.0001 cd/m\ :sup:`2`. + A value of zero indicates that the nominal minimum display + luminance is unknown. diff --git a/Makefile b/Makefile index 2ebb1fbb7421f4..9b9e330e0fb9ac 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 7 PATCHLEVEL = 2 -SUBLEVEL = 2 +SUBLEVEL = 5 EXTRAVERSION = NAME = Baby Opossum Posse @@ -1092,6 +1092,16 @@ endif export CC_FLAGS_SCS endif +ifdef CONFIG_RUST_INLINE_HELPERS +# `rustc` normally emits traps for unreachable paths during code generation. +# With inline helpers, Clang performs code generation from the linked bitcode +# instead, so request the same behavior explicitly. Otherwise `objtool` may +# follow an impossible Rust path into the next function. +CC_FLAGS_RUST_INLINE_HELPERS := -mllvm -trap-unreachable \ + -mllvm -no-trap-after-noreturn +export CC_FLAGS_RUST_INLINE_HELPERS +endif + ifdef CONFIG_LTO_CLANG ifdef CONFIG_LTO_CLANG_FULL CC_FLAGS_LTO := -flto @@ -1127,7 +1137,8 @@ endif ifdef CONFIG_RUST # Always pass -Zsanitizer-cfi-normalize-integers as CONFIG_RUST selects # CONFIG_CFI_ICALL_NORMALIZE_INTEGERS. - RUSTC_FLAGS_CFI := -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers + # Disable function merging as LLVM incorrectly merges functions with different KCFI types. + RUSTC_FLAGS_CFI := -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers -Zmerge-functions=disabled KBUILD_RUSTFLAGS += $(RUSTC_FLAGS_CFI) export RUSTC_FLAGS_CFI endif diff --git a/arch/alpha/include/uapi/asm/fpu.h b/arch/alpha/include/uapi/asm/fpu.h index cea9eafa056fc0..d28dc36786e271 100644 --- a/arch/alpha/include/uapi/asm/fpu.h +++ b/arch/alpha/include/uapi/asm/fpu.h @@ -101,7 +101,12 @@ ieee_swcr_to_fpcr(unsigned long sw) | IEEE_TRAP_ENABLE_OVF)) << 48; fp |= (~sw & (IEEE_TRAP_ENABLE_UNF | IEEE_TRAP_ENABLE_INE)) << 57; fp |= (sw & IEEE_MAP_UMZ ? FPCR_UNDZ | FPCR_UNFD : 0); - fp |= (~sw & IEEE_TRAP_ENABLE_DNO) << 41; + /* + * Disable denormal operand traps only when denormal inputs are to be + * flushed to zero. Otherwise they must keep trapping, so that /S + * instructions reach the kernel emulation handler. + */ + fp |= (sw & IEEE_MAP_DMZ ? FPCR_DNOD : 0); return fp; } @@ -116,7 +121,6 @@ ieee_fpcr_to_swcr(unsigned long fp) | IEEE_TRAP_ENABLE_OVF); sw |= (~fp >> 57) & (IEEE_TRAP_ENABLE_UNF | IEEE_TRAP_ENABLE_INE); sw |= (fp >> 47) & IEEE_MAP_UMZ; - sw |= (~fp >> 41) & IEEE_TRAP_ENABLE_DNO; return sw; } diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 94dbc470cd6c00..7050f0f7fe3d72 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -224,17 +224,17 @@ int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size) switch(size) { case 1: - outb(port, val); + outb(val, port); return 1; case 2: if (port & 1) return -EINVAL; - outw(port, val); + outw(val, port); return 2; case 4: if (port & 3) return -EINVAL; - outl(port, val); + outl(val, port); return 4; } return -EINVAL; diff --git a/arch/alpha/kernel/sys_marvel.c b/arch/alpha/kernel/sys_marvel.c index 1f99b03effc2ee..a37707e05e34e9 100644 --- a/arch/alpha/kernel/sys_marvel.c +++ b/arch/alpha/kernel/sys_marvel.c @@ -263,6 +263,18 @@ init_io7_irqs(struct io7 *io7, */ printk(" Interrupts reported to CPU at PE %u\n", boot_cpuid); + /* Set up the lsi irqs. */ + for (i = 0; i < 128; ++i) { + irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq); + irq_set_status_flags(base + i, IRQ_LEVEL); + } + + /* Set up the msi irqs. */ + for (i = 128; i < (128 + 512); ++i) { + irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq); + irq_set_status_flags(base + i, IRQ_LEVEL); + } + raw_spin_lock(&io7->irq_lock); /* set up the error irqs */ @@ -272,12 +284,6 @@ init_io7_irqs(struct io7 *io7, io7_redirect_irq(io7, &io7->csrs->STV_CTL.csr, boot_cpuid); io7_redirect_irq(io7, &io7->csrs->HEI_CTL.csr, boot_cpuid); - /* Set up the lsi irqs. */ - for (i = 0; i < 128; ++i) { - irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq); - irq_set_status_flags(i, IRQ_LEVEL); - } - /* Disable the implemented irqs in hardware. */ for (i = 0; i < 0x60; ++i) init_one_io7_lsi(io7, i, boot_cpuid); @@ -285,13 +291,6 @@ init_io7_irqs(struct io7 *io7, init_one_io7_lsi(io7, 0x74, boot_cpuid); init_one_io7_lsi(io7, 0x75, boot_cpuid); - - /* Set up the msi irqs. */ - for (i = 128; i < (128 + 512); ++i) { - irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq); - irq_set_status_flags(i, IRQ_LEVEL); - } - for (i = 0; i < 16; ++i) init_one_io7_msi(io7, i, boot_cpuid); diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index 7004397937cfda..7cd20e5f9ee03e 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -166,12 +166,12 @@ static long dummy_emul(void) { return 0; } long (*alpha_fp_emul_imprecise)(struct pt_regs *regs, unsigned long writemask) = (void *)dummy_emul; EXPORT_SYMBOL_GPL(alpha_fp_emul_imprecise); -long (*alpha_fp_emul) (unsigned long pc) +long (*alpha_fp_emul) (unsigned long pc, unsigned long summary) = (void *)dummy_emul; EXPORT_SYMBOL_GPL(alpha_fp_emul); #else long alpha_fp_emul_imprecise(struct pt_regs *regs, unsigned long writemask); -long alpha_fp_emul (unsigned long pc); +long alpha_fp_emul (unsigned long pc, unsigned long summary); #endif asmlinkage void @@ -185,7 +185,7 @@ do_entArith(unsigned long summary, unsigned long write_mask, emulate the instruction. If the processor supports precise exceptions, we don't have to search. */ if (!amask(AMASK_PRECISE_TRAP)) - si_code = alpha_fp_emul(regs->pc - 4); + si_code = alpha_fp_emul(regs->pc - 4, summary); else si_code = alpha_fp_emul_imprecise(regs, write_mask); if (si_code == 0) diff --git a/arch/alpha/math-emu/math.c b/arch/alpha/math-emu/math.c index 68d420bfd3c0d7..e3f2df3729e3ad 100644 --- a/arch/alpha/math-emu/math.c +++ b/arch/alpha/math-emu/math.c @@ -52,13 +52,13 @@ MODULE_DESCRIPTION("FP Software completion module"); MODULE_LICENSE("GPL v2"); extern long (*alpha_fp_emul_imprecise)(struct pt_regs *, unsigned long); -extern long (*alpha_fp_emul) (unsigned long pc); +extern long (*alpha_fp_emul) (unsigned long pc, unsigned long summary); static long (*save_emul_imprecise)(struct pt_regs *, unsigned long); -static long (*save_emul) (unsigned long pc); +static long (*save_emul) (unsigned long pc, unsigned long summary); long do_alpha_fp_emul_imprecise(struct pt_regs *, unsigned long); -long do_alpha_fp_emul(unsigned long); +long do_alpha_fp_emul(unsigned long, unsigned long); static int alpha_fp_emul_init_module(void) { @@ -86,7 +86,22 @@ module_exit(alpha_fp_emul_cleanup_module); /* - * Emulate the floating point instruction at address PC. Returns -1 if the + * Exception bits of the exception summary register (EXC_SUM). Bit 0 is the + * software completion bit; bits 1 through 5 report the exceptions the + * hardware attributed to the trapping instruction, and lie at the same + * positions as the corresponding IEEE_TRAP_ENABLE_* bits. + */ +#define EXC_SUM_INV (1UL << 1) +#define EXC_SUM_DZE (1UL << 2) +#define EXC_SUM_OVF (1UL << 3) +#define EXC_SUM_UNF (1UL << 4) +#define EXC_SUM_INE (1UL << 5) +#define EXC_SUM_MASK (EXC_SUM_INV | EXC_SUM_DZE | EXC_SUM_OVF \ + | EXC_SUM_UNF | EXC_SUM_INE) + +/* + * Emulate the floating point instruction at address PC. SUMMARY is the + * exception summary register the trap was delivered with. Returns -1 if the * instruction to be emulated is illegal (such as with the opDEC trap), else * the SI_CODE for a SIGFPE signal, else 0 if everything's ok. * @@ -95,7 +110,7 @@ module_exit(alpha_fp_emul_cleanup_module); * stick the result of the operation into the appropriate register. */ long -alpha_fp_emul (unsigned long pc) +alpha_fp_emul (unsigned long pc, unsigned long summary) { FP_DECL_EX; FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); @@ -300,12 +315,56 @@ alpha_fp_emul (unsigned long pc) swcr |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT); current_thread_info()->ieee_state |= (_fex << IEEE_STATUS_TO_EXCSUM_SHIFT); + } - /* Update hardware control register. */ - fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); - fpcr |= ieee_swcr_to_fpcr(swcr); - wrfpcr(fpcr); + /* + * EV6 records exception status bits in the FPCR before delivering the + * software completion trap, and swcr_update_status() above merged them + * into SWCR. Some can be wrong for the instruction we just emulated: + * a CVTTS of a value exactly representable as a subnormal sets FPCR_UNF + * even though the result is exact. Clear the exceptions the trap + * reported but that soft-fp did not raise. + */ + if (implver() == IMPLVER_EV6) { + unsigned long spurious = summary & EXC_SUM_MASK; + + if (spurious & (EXC_SUM_UNF | EXC_SUM_OVF)) { + /* + * EXC_SUM reports only the underflow or overflow, + * but the hardware sets INE alongside it in the FPCR. + */ + spurious |= EXC_SUM_INE; + } else if (!spurious) { + /* + * No exception reported, so this was a denormal + * operand trap, for which INE and UNF can be + * fabricated as well. + */ + spurious = EXC_SUM_INE | EXC_SUM_UNF; + } + /* + * Never clear an exception software has confirmed. Every + * instruction that genuinely raises one traps for software + * completion and is recorded in ieee_state above, so a bit + * found there -- including one just set from _fex -- belongs + * to this or an earlier instruction and must survive. + */ + spurious &= ~(current_thread_info()->ieee_state + >> IEEE_STATUS_TO_EXCSUM_SHIFT); + + swcr &= ~(spurious << IEEE_STATUS_TO_EXCSUM_SHIFT); + } + + /* + * Update hardware control register. This has to happen even when + * soft-fp raised nothing, to clear any fabricated bits. + */ + fpcr &= (~FPCR_MASK | FPCR_DYN_MASK); + fpcr |= ieee_swcr_to_fpcr(swcr); + wrfpcr(fpcr); + + if (_fex) { /* Do we generate a signal? */ _fex = _fex & swcr & IEEE_TRAP_ENABLE_MASK; si_code = 0; @@ -387,9 +446,16 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask) break; } if (!write_mask) { - /* Re-execute insns in the trap-shadow. */ + /* + * Re-execute insns in the trap-shadow. Pass no + * exception summary: it describes the trap, which + * was taken anywhere in the shadow, and so is not + * attribution for this instruction. Nothing is + * lost, since only EV6 -- which traps precisely and + * never comes this way -- needs it. + */ regs->pc = trigger_pc + 4; - si_code = alpha_fp_emul(trigger_pc); + si_code = alpha_fp_emul(trigger_pc, 0); goto egress; } trigger_pc -= 4; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 9187240a02db54..b8aa3fcce1078e 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -98,7 +98,7 @@ config ARM select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE if ARM_LPAE select HAVE_ARM_SMCCC if CPU_V7 - select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 + select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 && !CPU_32v3 select HAVE_CONTEXT_TRACKING_USER select HAVE_C_RECORDMCOUNT select HAVE_BUILDTIME_MCOUNT_SORT diff --git a/arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts b/arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts index 5ec77c5912f8e6..9fa86bb6438ecf 100644 --- a/arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts +++ b/arch/arm64/boot/dts/qcom/hamoa-iot-evk.dts @@ -762,8 +762,6 @@ orientation-switch; retimer-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -879,8 +877,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm64/boot/dts/qcom/hamoa-lenovo-ideacentre-mini-01q8x10.dts b/arch/arm64/boot/dts/qcom/hamoa-lenovo-ideacentre-mini-01q8x10.dts index b3d4cf7e0fd54b..0f1b3ee3ab3b96 100644 --- a/arch/arm64/boot/dts/qcom/hamoa-lenovo-ideacentre-mini-01q8x10.dts +++ b/arch/arm64/boot/dts/qcom/hamoa-lenovo-ideacentre-mini-01q8x10.dts @@ -10,7 +10,7 @@ / { model = "Lenovo IdeaCentre Mini 01Q8X10 (Hamoa)"; - compatible = "lenovo,hamoa-ideacentre-mini-01q8x10", "lenovo,thinkpad-t14s", "qcom,x1e80100"; + compatible = "lenovo,hamoa-ideacentre-mini-01q8x10", "lenovo,ideacentre-mini-01q8x10", "lenovo,thinkpad-t14s", "qcom,x1e80100"; }; //&iris { diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi index fa540d8c2615dc..ba907760fa6d62 100644 --- a/arch/arm64/boot/dts/qcom/kodiak.dtsi +++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi @@ -191,9 +191,12 @@ qcom,vmid = ; }; - adsp_rpc_remote_heap_mem: adsp-rpc-remote-heap@9cb80000 { - reg = <0x0 0x9cb80000 0x0 0x800000>; - no-map; + adsp_rpc_remote_heap_mem: adsp-rpc-remote-heap { + compatible = "shared-dma-pool"; + alloc-ranges = <0x0 0x80000000 0x0 0x80000000>; + reusable; + alignment = <0x0 0x400000>; + size = <0x0 0x800000>; }; }; diff --git a/arch/arm64/boot/dts/qcom/purwa-iot-evk.dts b/arch/arm64/boot/dts/qcom/purwa-iot-evk.dts index 9ce080b9ae8966..ad503beec1d3d8 100644 --- a/arch/arm64/boot/dts/qcom/purwa-iot-evk.dts +++ b/arch/arm64/boot/dts/qcom/purwa-iot-evk.dts @@ -703,8 +703,6 @@ orientation-switch; retimer-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -762,8 +760,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -869,8 +865,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm64/boot/dts/qcom/purwa-lenovo-ideacentre-mini-01q8x10.dts b/arch/arm64/boot/dts/qcom/purwa-lenovo-ideacentre-mini-01q8x10.dts index f44bd2d5de15fe..8168cb0ecf46e9 100644 --- a/arch/arm64/boot/dts/qcom/purwa-lenovo-ideacentre-mini-01q8x10.dts +++ b/arch/arm64/boot/dts/qcom/purwa-lenovo-ideacentre-mini-01q8x10.dts @@ -14,7 +14,7 @@ / { model = "Lenovo IdeaCentre Mini 01Q8X10 (Purwa)"; - compatible = "lenovo,purwa-ideacentre-mini-01q8x10", "lenovo,thinkpad-t14s", "qcom,x1e80100"; + compatible = "lenovo,purwa-ideacentre-mini-01q8x10", "lenovo,ideacentre-mini-01q8x10", "lenovo,thinkpad-t14s", "qcom,x1e80100"; }; //&iris { diff --git a/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts b/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts index 0f23eaef01f213..b290f0f4c0e107 100644 --- a/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts +++ b/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts @@ -151,7 +151,7 @@ interrupts-extended = <&tlmm 80 IRQ_TYPE_LEVEL_LOW>; - irq-gpios = <&tlmm 80 IRQ_TYPE_LEVEL_LOW>; + irq-gpios = <&tlmm 80 GPIO_ACTIVE_LOW>; reset-gpios = <&tlmm 71 GPIO_ACTIVE_HIGH>; AVDD28-supply = <&ts_vdd_supply>; VDDIO-supply = <&ts_vddio_supply>; diff --git a/arch/arm64/boot/dts/qcom/x1-acer-swift-14.dtsi b/arch/arm64/boot/dts/qcom/x1-acer-swift-14.dtsi index c60ca872dbfa91..67d28256ee69ae 100644 --- a/arch/arm64/boot/dts/qcom/x1-acer-swift-14.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-acer-swift-14.dtsi @@ -758,8 +758,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -863,8 +861,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm64/boot/dts/qcom/x1-asus-vivobook-s15.dtsi b/arch/arm64/boot/dts/qcom/x1-asus-vivobook-s15.dtsi index e01cb20c1b9fe0..208109c6fa7a2a 100644 --- a/arch/arm64/boot/dts/qcom/x1-asus-vivobook-s15.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-asus-vivobook-s15.dtsi @@ -915,8 +915,6 @@ orientation-switch; retimer-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1037,8 +1035,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1357,7 +1353,6 @@ &tlmm { gpio-reserved-ranges = <34 2>, /* Unused */ <44 4>, /* SPI (TPM) */ - <65 1>, /* EC reset */ <238 1>; /* UFS Reset */ edp_reg_en: edp-reg-en-state { diff --git a/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi b/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi index 7c619eb5b03df3..be5eaad99013dc 100644 --- a/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-asus-zenbook-a14.dtsi @@ -975,10 +975,10 @@ pinctrl-0 = <&rtmr0_default>; pinctrl-names = "default"; - retimer-switch; orientation-switch; + retimer-switch; - parade,disable-usb4; + qcom,disable-usb4; ports { #address-cells = <1>; @@ -1080,7 +1080,7 @@ retimer-switch; orientation-switch; - parade,disable-usb4; + qcom,disable-usb4; ports { #address-cells = <1>; @@ -1417,7 +1417,6 @@ &tlmm { gpio-reserved-ranges = <44 4>, /* SPI11, TZ Protected */ - <65 1>, /* EC reset */ <90 1>; /* Unknown, TZ Protected */ cam_indicator_en: cam-indicator-en-state { diff --git a/arch/arm64/boot/dts/qcom/x1-crd.dtsi b/arch/arm64/boot/dts/qcom/x1-crd.dtsi index fb5986f8954dcc..b3bb88279cc26a 100644 --- a/arch/arm64/boot/dts/qcom/x1-crd.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-crd.dtsi @@ -1160,8 +1160,6 @@ orientation-switch; retimer-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1219,8 +1217,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1309,8 +1305,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1747,7 +1741,6 @@ &tlmm { gpio-reserved-ranges = <34 2>, /* Unused */ <44 4>, /* SPI (TPM) */ - <65 1>, /* EC reset */ <238 1>; /* UFS Reset */ cam_rgb_default: cam-rgb-default-state { diff --git a/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi b/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi index c0c0ead90fe6d6..884981728f3167 100644 --- a/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi @@ -590,6 +590,7 @@ regulator-min-microvolt = <1200000>; regulator-max-microvolt = <1200000>; regulator-initial-mode = ; + regulator-always-on; }; vreg_l13b_3p0: ldo13 { @@ -611,6 +612,7 @@ regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-initial-mode = ; + regulator-always-on; }; }; @@ -911,8 +913,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1008,8 +1008,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1366,7 +1364,6 @@ &tlmm { gpio-reserved-ranges = <44 4>, /* SPI11 (TPM) */ - <65 1>, /* EC reset */ <76 4>, /* SPI19 (TZ Protected) */ <80 2>, /* I2C20 (Battery SMBus) */ <238 1>; /* UFS Reset */ diff --git a/arch/arm64/boot/dts/qcom/x1-hp-omnibook-x14.dtsi b/arch/arm64/boot/dts/qcom/x1-hp-omnibook-x14.dtsi index e442c9b0a187f5..dba8cee43e2947 100644 --- a/arch/arm64/boot/dts/qcom/x1-hp-omnibook-x14.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-hp-omnibook-x14.dtsi @@ -917,8 +917,6 @@ orientation-switch; retimer-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1289,7 +1287,6 @@ &tlmm { gpio-reserved-ranges = <34 2>, /* Unused */ <44 4>, /* SPI (TPM) */ - <65 1>, /* EC reset */ <72 2>, /* Secure EC I2C connection (?) */ <238 1>; /* UFS Reset */ diff --git a/arch/arm64/boot/dts/qcom/x1-lenovo-ideacentre-mini-01q8x10.dtsi b/arch/arm64/boot/dts/qcom/x1-lenovo-ideacentre-mini-01q8x10.dtsi index e3d22d551c5428..ad81181d39c25f 100644 --- a/arch/arm64/boot/dts/qcom/x1-lenovo-ideacentre-mini-01q8x10.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-lenovo-ideacentre-mini-01q8x10.dtsi @@ -600,8 +600,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi b/arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi index b12117d0ff1cbd..49ccfb0fd3a6f6 100644 --- a/arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi @@ -322,18 +322,21 @@ }; sound { - compatible = "qcom,x1e80100-sndcard"; + compatible = "microsoft,denali-sndcard", + "qcom,x1e80100-sndcard"; model = "X1E80100-Microsoft-Surface-Pro-11"; audio-routing = "SpkrLeft IN", "WSA WSA_SPK1 OUT", "SpkrRight IN", "WSA WSA_SPK2 OUT", "VA DMIC0", "vdd-micb", - "VA DMIC1", "vdd-micb"; + "VA DMIC1", "vdd-micb", + "TX DMIC0", "vdd-micb", + "TX DMIC1", "vdd-micb"; wsa-dai-link { link-name = "WSA Playback"; codec { - sound-dai = <&left_spkr>, <&right_spkr>, + sound-dai = <&left_spkr 0>, <&right_spkr 0>, <&swr0 0>, <&lpass_wsamacro 0>; }; @@ -346,6 +349,51 @@ }; }; + wsa-vi-dai-link { + link-name = "WSA VI Protection"; + + /* + * One two-channel Windows-equivalent feedback transport: + * amplifier VISENSE ports 10/11 -> WSA macro VI -> + * SoundWire DIN0 -> AudioReach WSA TX0. + */ + codec { + sound-dai = <&left_spkr 1>, <&right_spkr 1>, + <&swr0 9>, <&lpass_wsamacro 2>; + }; + + cpu { + sound-dai = <&q6apmbedai WSA_CODEC_DMA_TX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + wsa-cps-dai-link { + link-name = "WSA CPS Protection"; + + /* + * Dedicated CPS sidechain: both WSA8845 DP6 sources share + * physical SoundWire master port 13. swr0 DAI 10 is only + * the independent ASoC/SoundWire stream handle; qcom,port-mapping + * below remains authoritative for the physical master ports. + */ + codec { + sound-dai = <&left_spkr 2>, <&right_spkr 2>, + <&swr0 10>, <&lpass_wsamacro 4>; + }; + + cpu { + sound-dai = <&q6apmbedai WSA_CODEC_DMA_TX_1>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + va-dai-link { link-name = "VA Capture"; @@ -361,6 +409,22 @@ sound-dai = <&q6apm>; }; }; + + tx-dai-link { + link-name = "TX Capture"; + + codec { + sound-dai = <&lpass_txmacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai TX_CODEC_DMA_TX_3>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; }; wcn7850-pmu { @@ -774,8 +838,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -839,8 +901,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1142,11 +1202,15 @@ compatible = "sdw20217020400"; reg = <0 0>; reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; - #sound-dai-cells = <0>; + #sound-dai-cells = <1>; sound-name-prefix = "SpkrLeft"; vdd-1p8-supply = <&vreg_l15b_1p8>; vdd-io-supply = <&vreg_l12b_1p2>; qcom,port-mapping = <1 2 3 7 10 13>; + qcom,speaker-load-ohms = <4>; + qcom,visense-channel-mask = <3>; + qcom,enable-cps; + qcom,cps-offset1 = <0>; }; /* WSA8845, Right Speaker */ @@ -1154,11 +1218,15 @@ compatible = "sdw20217020400"; reg = <0 1>; reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; - #sound-dai-cells = <0>; + #sound-dai-cells = <1>; sound-name-prefix = "SpkrRight"; vdd-1p8-supply = <&vreg_l15b_1p8>; vdd-io-supply = <&vreg_l12b_1p2>; qcom,port-mapping = <4 5 6 7 11 13>; + qcom,speaker-load-ohms = <4>; + qcom,visense-channel-mask = <3>; + qcom,enable-cps; + qcom,cps-offset1 = <25>; }; }; diff --git a/arch/arm64/boot/dts/qcom/x1e001de-devkit-jg.dts b/arch/arm64/boot/dts/qcom/x1e001de-devkit-jg.dts index 297a84a7f70f50..0a5326f4beb927 100644 --- a/arch/arm64/boot/dts/qcom/x1e001de-devkit-jg.dts +++ b/arch/arm64/boot/dts/qcom/x1e001de-devkit-jg.dts @@ -983,8 +983,6 @@ pinctrl-0 = <&rtmr2_default>; pinctrl-names = "default"; - parade,disable-usb4; - orientation-switch; retimer-switch; @@ -1045,8 +1043,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1138,8 +1134,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; diff --git a/arch/arm64/boot/dts/qcom/x1e001de-devkit.dts b/arch/arm64/boot/dts/qcom/x1e001de-devkit.dts index 6ffef04a7f7c74..1027f49643dddb 100644 --- a/arch/arm64/boot/dts/qcom/x1e001de-devkit.dts +++ b/arch/arm64/boot/dts/qcom/x1e001de-devkit.dts @@ -1073,8 +1073,6 @@ orientation-switch; retimer-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1132,8 +1130,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1225,8 +1221,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1705,8 +1699,7 @@ }; &tlmm { - gpio-reserved-ranges = <44 4>, /* SPI (TPM) */ - <65 1>; /* EC reset */ + gpio-reserved-ranges = <44 4>; /* SPI (TPM) */ edp_reg_en: edp-reg-en-state { pins = "gpio70"; diff --git a/arch/arm64/boot/dts/qcom/x1e001de-devkit_pcie3_hdmi.dts b/arch/arm64/boot/dts/qcom/x1e001de-devkit_pcie3_hdmi.dts index 0adebabfe8b636..a24ca99c24cde1 100644 --- a/arch/arm64/boot/dts/qcom/x1e001de-devkit_pcie3_hdmi.dts +++ b/arch/arm64/boot/dts/qcom/x1e001de-devkit_pcie3_hdmi.dts @@ -894,8 +894,6 @@ orientation-switch; retimer-switch; - parade,disable-usb4; - ps8830,boot-on; ports { @@ -953,8 +951,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ps8830,boot-on; ports { @@ -1046,8 +1042,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ps8830,boot-on; ports { diff --git a/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi b/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi index 046400e7675122..0c933d1155857b 100644 --- a/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi +++ b/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi @@ -1166,8 +1166,6 @@ orientation-switch; retimer-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1298,8 +1296,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1700,7 +1696,6 @@ &tlmm { gpio-reserved-ranges = <34 2>, /* GPIO34 = unused, GPIO35 = TPM interrupt */ <44 4>, /* SPI11 (Nuvoton NPCT760 TPM) */ - <65 1>, /* EC reset */ <72 2>, /* I2C18 (Secure EC I2C connection) */ <238 1>; /* UFS Reset (unused) */ diff --git a/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts b/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts index 8fb9b85b7d6885..bbe169a58f3cf7 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-dell-xps13-9345.dts @@ -869,8 +869,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -955,8 +953,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1394,7 +1390,6 @@ gpio-reserved-ranges = <44 4>, /* SPI11 (TPM) */ <65 1>, /* EC Reset */ <76 4>, /* SPI19 (TZ Protected) */ - <65 1>, /* EC reset */ <238 1>; /* UFS Reset */ cam_rgb_default: cam-rgb-default-state { diff --git a/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts b/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts index 6dd0a94108a34e..4ea4ec7769eba2 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts @@ -1051,8 +1051,6 @@ orientation-switch; retimer-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1110,8 +1108,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1185,8 +1181,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1560,7 +1554,6 @@ &tlmm { gpio-reserved-ranges = <34 2>, /* Unused */ <44 4>, /* SPI (TPM) */ - <65 1>, /* EC reset */ <238 1>; /* UFS Reset */ cam_rgb_default: cam-rgb-default-state { diff --git a/arch/arm64/boot/dts/qcom/x1e80100-medion-sprchrgd-14-s1.dts b/arch/arm64/boot/dts/qcom/x1e80100-medion-sprchrgd-14-s1.dts index f9b107a4398219..8e88d1af9b5f07 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-medion-sprchrgd-14-s1.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-medion-sprchrgd-14-s1.dts @@ -857,8 +857,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1250,9 +1248,7 @@ gpio-reserved-ranges = <28 4>, /* Unused */ <34 2>, /* Unused */ <44 4>, /* SPI (TPM) */ - <65 1>, /* EC reset */ - <72 2>, /* Secure EC I2C connection (?) */ - <238 1>; /* UFS Reset */ + <72 2>; /* Secure EC I2C connection (?) */ edp_reg_en: edp-reg-en-state { pins = "gpio54"; diff --git a/arch/arm64/boot/dts/qcom/x1e80100-microsoft-romulus.dtsi b/arch/arm64/boot/dts/qcom/x1e80100-microsoft-romulus.dtsi index 55f87eb6332656..13d0f09e0c2a1b 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-microsoft-romulus.dtsi +++ b/arch/arm64/boot/dts/qcom/x1e80100-microsoft-romulus.dtsi @@ -902,8 +902,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -986,8 +984,6 @@ retimer-switch; orientation-switch; - parade,disable-usb4; - ports { #address-cells = <1>; #size-cells = <0>; @@ -1328,7 +1324,6 @@ &tlmm { gpio-reserved-ranges = <44 4>, /* SPI (TPM) */ - <65 1>, /* EC reset */ <238 1>; /* UFS Reset */ hall_int_n_default: hall-int-n-state { diff --git a/arch/arm64/boot/dts/qcom/x1e80100-qcp.dts b/arch/arm64/boot/dts/qcom/x1e80100-qcp.dts index 19ea1add016c95..78380fec8839eb 100644 --- a/arch/arm64/boot/dts/qcom/x1e80100-qcp.dts +++ b/arch/arm64/boot/dts/qcom/x1e80100-qcp.dts @@ -1180,7 +1180,6 @@ &tlmm { gpio-reserved-ranges = <33 3>, /* Unused */ <44 4>, /* SPI (TPM) */ - <65 1>, /* EC reset */ <238 1>; /* UFS Reset */ edp_reg_en: edp-reg-en-state { diff --git a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-5-2in1.dts b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-5-2in1.dts index a11602cfee2fe6..f07ee4b456d471 100644 --- a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-5-2in1.dts +++ b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-5-2in1.dts @@ -56,6 +56,12 @@ #sound-dai-cells = <1>; }; + camera { + compatible = "regulator-dummy-consumer"; + + vdd-supply = <&vreg_cam_5p0>; + }; + hdmi-bridge { compatible = "parade,ps185hdm"; @@ -369,6 +375,23 @@ }; }; + vreg_cam_5p0: regulator-cam-5p0 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_CAM_5P0"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + + gpio = <&tlmm 44 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&cam_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + regulator-always-on; + }; + vreg_edp_3p3: regulator-edp-3p3 { compatible = "regulator-fixed"; @@ -1302,10 +1325,15 @@ &tlmm { gpio-reserved-ranges = <34 2>, /* Unused */ - <44 4>, /* SPI (TPM) */ - <65 1>, /* EC reset */ <72 2>; /* Secure EC I2C connection (?) */ + cam_reg_en: cam-reg-en-state { + pins = "gpio44"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + eusb3_reset_n: eusb3-reset-n-state { pins = "gpio6"; function = "gpio"; diff --git a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-5.dtsi b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-5.dtsi index 60b8202269da2c..b126e8c5e48d27 100644 --- a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-5.dtsi +++ b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-5.dtsi @@ -1156,8 +1156,6 @@ &tlmm { gpio-reserved-ranges = <34 2>, /* Unused */ - <44 4>, /* SPI (TPM) */ - <65 1>, /* EC reset */ <72 2>; /* Secure EC I2C connection (?) */ eusb3_reset_n: eusb3-reset-n-state { diff --git a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-slim5x-lcd.dts b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-slim5x-lcd.dts index 762e3da470966f..243d1fdd51942e 100644 --- a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-slim5x-lcd.dts +++ b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-slim5x-lcd.dts @@ -7,53 +7,11 @@ /dts-v1/; -#include -#include -#include -#include -#include - -#include "purwa.dtsi" -#include "hamoa-pmics.dtsi" - -/delete-node/ &pmc8380_6; -/delete-node/ &pmc8380_6_thermal; +#include "x1p42100-lenovo-ideapad-slim5x.dtsi" / { model = "Lenovo IdeaPad Slim 5x 14Q8X9 (LCD)"; - compatible = "lenovo,ideapad-slim5x-lcd", "lenovo,thinkpad-t14s", "qcom,x1p42100"; - chassis-type = "laptop"; - - aliases { - serial0 = &uart21; - serial1 = &uart14; - }; - - wcd938x: audio-codec { - compatible = "qcom,wcd9385-codec"; - - pinctrl-0 = <&wcd_default>; - pinctrl-names = "default"; - - qcom,micbias1-microvolt = <1800000>; - qcom,micbias2-microvolt = <1800000>; - qcom,micbias3-microvolt = <1800000>; - qcom,micbias4-microvolt = <1800000>; - qcom,mbhc-buttons-vthreshold-microvolt = <75000 150000 237000 500000 500000 500000 500000 500000>; - qcom,mbhc-headset-vthreshold-microvolt = <1700000>; - qcom,mbhc-headphone-vthreshold-microvolt = <50000>; - qcom,rx-device = <&wcd_rx>; - qcom,tx-device = <&wcd_tx>; - - reset-gpios = <&tlmm 191 GPIO_ACTIVE_LOW>; - - vdd-buck-supply = <&vreg_l15b_1p8>; - vdd-rxtx-supply = <&vreg_l15b_1p8>; - vdd-io-supply = <&vreg_l15b_1p8>; - vdd-mic-bias-supply = <&vreg_bob1>; - - #sound-dai-cells = <1>; - }; + compatible = "lenovo,ideapad-slim5x-lcd", "lenovo,ideapad-5", "lenovo,thinkpad-t14s", "qcom,x1p42100"; backlight: backlight { compatible = "pwm-backlight"; @@ -61,1652 +19,12 @@ power-supply = <&vreg_edp_3p3>; }; - - hdmi-bridge { - compatible = "parade,ps185hdm"; - - pinctrl-0 = <&hdmi_hpd_default>; - pinctrl-names = "default"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - hdmi_bridge_dp_in: endpoint { - remote-endpoint = <&usb_1_ss2_qmpphy_out_dp>; - }; - }; - - port@1 { - reg = <1>; - - hdmi_bridge_tmds_out: endpoint { - remote-endpoint = <&hdmi_con>; - }; - }; - }; - }; - - hdmi-connector { - compatible = "hdmi-connector"; - type = "a"; - - port { - hdmi_con: endpoint { - remote-endpoint = <&hdmi_bridge_tmds_out>; - }; - }; - }; - - gpio-keys { - compatible = "gpio-keys"; - - pinctrl-0 = <&hall_int_n_default>; - pinctrl-names = "default"; - - switch-lid { - gpios = <&tlmm 92 GPIO_ACTIVE_LOW>; - linux,input-type = ; - linux,code = ; - wakeup-source; - wakeup-event-action = ; - }; - }; - - pmic-glink { - compatible = "qcom,x1e80100-pmic-glink", - "qcom,sm8550-pmic-glink", - "qcom,pmic-glink"; - #address-cells = <1>; - #size-cells = <0>; - orientation-gpios = <&tlmm 121 GPIO_ACTIVE_HIGH>, - <&tlmm 123 GPIO_ACTIVE_HIGH>; - - /* Display-adjacent port */ - connector@0 { - compatible = "usb-c-connector"; - reg = <0>; - power-role = "dual"; - data-role = "host"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - pmic_glink_ss0_hs_in: endpoint { - remote-endpoint = <&usb_1_ss0_dwc3_hs>; - }; - }; - - port@1 { - reg = <1>; - - pmic_glink_ss0_ss_in: endpoint { - remote-endpoint = <&usb_1_ss0_qmpphy_out>; - }; - }; - - port@2 { - reg = <2>; - - pmic_glink_ss0_sbu: endpoint { - remote-endpoint = <&usb_1_ss0_sbu_mux>; - }; - }; - }; - }; - - /* User-adjacent port */ - connector@1 { - compatible = "usb-c-connector"; - reg = <1>; - power-role = "dual"; - data-role = "host"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - pmic_glink_ss1_hs_in: endpoint { - remote-endpoint = <&usb_1_ss1_dwc3_hs>; - }; - }; - - port@1 { - reg = <1>; - - pmic_glink_ss1_ss_in: endpoint { - remote-endpoint = <&usb_1_ss1_qmpphy_out>; - }; - }; - - port@2 { - reg = <2>; - - pmic_glink_ss1_sbu: endpoint { - remote-endpoint = <&usb_1_ss1_sbu_mux>; - }; - }; - }; - }; - }; - - sound { - compatible = "qcom,x1e80100-sndcard"; - model = "X1E80100-LENOVO-Ideapad-5"; - audio-routing = "SpkrLeft IN", "WSA WSA_SPK1 OUT", - "SpkrRight IN", "WSA WSA_SPK2 OUT", - "IN1_HPHL", "HPHL_OUT", - "IN2_HPHR", "HPHR_OUT", - "AMIC2", "MIC BIAS2", - "VA DMIC0", "MIC BIAS1", - "VA DMIC1", "MIC BIAS1", - "VA DMIC0", "VA MIC BIAS1", - "VA DMIC1", "VA MIC BIAS1", - "TX SWR_INPUT1", "ADC2_OUTPUT"; - - displayport-0-dai-link { - link-name = "DisplayPort0 Playback"; - - codec { - sound-dai = <&mdss_dp0>; - }; - - cpu { - sound-dai = <&q6apmbedai DISPLAY_PORT_RX_0>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - - displayport-1-dai-link { - link-name = "DisplayPort1 Playback"; - - codec { - sound-dai = <&mdss_dp1>; - }; - - cpu { - sound-dai = <&q6apmbedai DISPLAY_PORT_RX_1>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - - displayport-2-dai-link { - link-name = "DisplayPort2 Playback"; - - codec { - sound-dai = <&mdss_dp2>; - }; - - cpu { - sound-dai = <&q6apmbedai DISPLAY_PORT_RX_2>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - - wcd-playback-dai-link { - link-name = "WCD Playback"; - - codec { - sound-dai = <&wcd938x 0>, <&swr1 0>, <&lpass_rxmacro 0>; - }; - - cpu { - sound-dai = <&q6apmbedai RX_CODEC_DMA_RX_0>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - - wcd-capture-dai-link { - link-name = "WCD Capture"; - - codec { - sound-dai = <&wcd938x 1>, <&swr2 1>, <&lpass_txmacro 0>; - }; - - cpu { - sound-dai = <&q6apmbedai TX_CODEC_DMA_TX_3>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - - wsa-dai-link { - link-name = "WSA Playback"; - - codec { - sound-dai = <&left_spkr>, <&right_spkr>, <&swr0 0>, <&lpass_wsamacro 0>; - }; - - cpu { - sound-dai = <&q6apmbedai WSA_CODEC_DMA_RX_0>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - - va-dai-link { - link-name = "VA Capture"; - - codec { - sound-dai = <&lpass_vamacro 0>; - }; - - cpu { - sound-dai = <&q6apmbedai VA_CODEC_DMA_TX_0>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - }; - - usb-1-ss0-sbu-mux { - compatible = "onnn,fsusb42", "gpio-sbu-mux"; - - enable-gpios = <&tlmm 168 GPIO_ACTIVE_LOW>; - select-gpios = <&tlmm 167 GPIO_ACTIVE_HIGH>; - - pinctrl-0 = <&usb_1_ss0_sbu_default>; - pinctrl-names = "default"; - - mode-switch; - orientation-switch; - - port { - usb_1_ss0_sbu_mux: endpoint { - remote-endpoint = <&pmic_glink_ss0_sbu>; - }; - }; - }; - - usb-1-ss1-sbu-mux { - compatible = "onnn,fsusb42", "gpio-sbu-mux"; - - enable-gpios = <&tlmm 179 GPIO_ACTIVE_LOW>; - select-gpios = <&tlmm 178 GPIO_ACTIVE_HIGH>; - - pinctrl-0 = <&usb_1_ss1_sbu_default>; - pinctrl-names = "default"; - - mode-switch; - orientation-switch; - - port { - usb_1_ss1_sbu_mux: endpoint { - remote-endpoint = <&pmic_glink_ss1_sbu>; - }; - }; - }; - - vreg_edp_3p3: regulator-edp-3p3 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_EDP_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&tlmm 70 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&edp_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - regulator-always-on; - }; - - vreg_misc_3p3: regulator-misc-3p3 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_MISC_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&pm8550ve_8_gpios 6 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&misc_3p3_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - regulator-always-on; - }; - - vreg_nvme: regulator-nvme { - compatible = "regulator-fixed"; - - regulator-name = "VREG_NVME_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&tlmm 18 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&nvme_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - }; - - vreg_wcn_0p95: regulator-wcn-0p95 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_WCN_0P95"; - regulator-min-microvolt = <950000>; - regulator-max-microvolt = <950000>; - - vin-supply = <&vreg_wcn_3p3>; - }; - - vreg_wcn_1p9: regulator-wcn-1p9 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_WCN_1P9"; - regulator-min-microvolt = <1900000>; - regulator-max-microvolt = <1900000>; - - vin-supply = <&vreg_wcn_3p3>; - }; - - vreg_wcn_3p3: regulator-wcn-3p3 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_WCN_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&tlmm 214 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&wcn_sw_en>; - pinctrl-names = "default"; - - regulator-boot-on; - }; - - vph_pwr: regulator-vph-pwr { - compatible = "regulator-fixed"; - - regulator-name = "vph_pwr"; - regulator-min-microvolt = <3700000>; - regulator-max-microvolt = <3700000>; - - regulator-always-on; - regulator-boot-on; - }; - - wcn7850-pmu { - compatible = "qcom,wcn7850-pmu"; - - vdd-supply = <&vreg_wcn_0p95>; - vddio-supply = <&vreg_l15b_1p8>; - vddaon-supply = <&vreg_wcn_0p95>; - vdddig-supply = <&vreg_wcn_0p95>; - vddrfa1p2-supply = <&vreg_wcn_1p9>; - vddrfa1p8-supply = <&vreg_wcn_1p9>; - - wlan-enable-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>; - bt-enable-gpios = <&tlmm 116 GPIO_ACTIVE_HIGH>; - - pinctrl-0 = <&wcn_wlan_bt_en>; - pinctrl-names = "default"; - - regulators { - vreg_pmu_rfa_cmn: ldo0 { - regulator-name = "vreg_pmu_rfa_cmn"; - }; - - vreg_pmu_aon_0p59: ldo1 { - regulator-name = "vreg_pmu_aon_0p59"; - }; - - vreg_pmu_wlcx_0p8: ldo2 { - regulator-name = "vreg_pmu_wlcx_0p8"; - }; - - vreg_pmu_wlmx_0p85: ldo3 { - regulator-name = "vreg_pmu_wlmx_0p85"; - }; - - vreg_pmu_btcmx_0p85: ldo4 { - regulator-name = "vreg_pmu_btcmx_0p85"; - }; - - vreg_pmu_rfa_0p8: ldo5 { - regulator-name = "vreg_pmu_rfa_0p8"; - }; - - vreg_pmu_rfa_1p2: ldo6 { - regulator-name = "vreg_pmu_rfa_1p2"; - }; - - vreg_pmu_rfa_1p8: ldo7 { - regulator-name = "vreg_pmu_rfa_1p8"; - }; - - vreg_pmu_pcie_0p9: ldo8 { - regulator-name = "vreg_pmu_pcie_0p9"; - }; - - vreg_pmu_pcie_1p8: ldo9 { - regulator-name = "vreg_pmu_pcie_1p8"; - }; - }; - }; -}; - -&apps_rsc { - regulators-0 { - compatible = "qcom,pm8550-rpmh-regulators"; - qcom,pmic-id = "b"; - - vdd-bob1-supply = <&vph_pwr>; - vdd-bob2-supply = <&vph_pwr>; - vdd-l1-l4-l10-supply = <&vreg_s4c_1p8>; - vdd-l2-l13-l14-supply = <&vreg_bob1>; - vdd-l5-l16-supply = <&vreg_bob1>; - vdd-l6-l7-supply = <&vreg_bob2>; - vdd-l8-l9-supply = <&vreg_bob1>; - vdd-l12-supply = <&vreg_s5j_1p2>; - vdd-l15-supply = <&vreg_s4c_1p8>; - vdd-l17-supply = <&vreg_bob2>; - - vreg_bob1: bob1 { - regulator-name = "vreg_bob1"; - regulator-min-microvolt = <3008000>; - regulator-max-microvolt = <3960000>; - regulator-initial-mode = ; - }; - - vreg_bob2: bob2 { - regulator-name = "vreg_bob2"; - regulator-min-microvolt = <2504000>; - regulator-max-microvolt = <3008000>; - regulator-initial-mode = ; - }; - - vreg_l1b_1p8: ldo1 { - regulator-name = "vreg_l1b_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - }; - - vreg_l2b_3p0: ldo2 { - regulator-name = "vreg_l2b_3p0"; - regulator-min-microvolt = <3072000>; - regulator-max-microvolt = <3072000>; - regulator-initial-mode = ; - }; - - vreg_l4b_1p8: ldo4 { - regulator-name = "vreg_l4b_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - vreg_l6b_1p8: ldo6 { - regulator-name = "vreg_l6b_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <2960000>; - regulator-initial-mode = ; - }; - - vreg_l8b_3p0: ldo8 { - regulator-name = "vreg_l8b_3p0"; - regulator-min-microvolt = <3072000>; - regulator-max-microvolt = <3072000>; - regulator-initial-mode = ; - }; - - vreg_l9b_2p9: ldo9 { - regulator-name = "vreg_l9b_2p9"; - regulator-min-microvolt = <2960000>; - regulator-max-microvolt = <2960000>; - regulator-initial-mode = ; - }; - - vreg_l10b_1p8: ldo10 { - regulator-name = "vreg_l10b_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - }; - - vreg_l12b_1p2: ldo12 { - regulator-name = "vreg_l12b_1p2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-initial-mode = ; - regulator-always-on; - }; - - vreg_l13b_3p0: ldo13 { - regulator-name = "vreg_l13b_3p0"; - regulator-min-microvolt = <3072000>; - regulator-max-microvolt = <3072000>; - regulator-initial-mode = ; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - vreg_l14b_3p0: ldo14 { - regulator-name = "vreg_l14b_3p0"; - regulator-min-microvolt = <3072000>; - regulator-max-microvolt = <3072000>; - regulator-initial-mode = ; - }; - - vreg_l15b_1p8: ldo15 { - regulator-name = "vreg_l15b_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - regulator-always-on; - }; - - vreg_l17b_2p5: ldo17 { - regulator-name = "vreg_l17b_2p5"; - regulator-min-microvolt = <2504000>; - regulator-max-microvolt = <2504000>; - regulator-initial-mode = ; - }; - }; - - regulators-1 { - compatible = "qcom,pm8550ve-rpmh-regulators"; - qcom,pmic-id = "c"; - - vdd-l1-supply = <&vreg_s5j_1p2>; - vdd-l2-supply = <&vreg_s1f_0p7>; - vdd-l3-supply = <&vreg_s1f_0p7>; - vdd-s4-supply = <&vph_pwr>; - - vreg_s4c_1p8: smps4 { - regulator-name = "vreg_s4c_1p8"; - regulator-min-microvolt = <1856000>; - regulator-max-microvolt = <2000000>; - regulator-initial-mode = ; - }; - - vreg_l1c_1p2: ldo1 { - regulator-name = "vreg_l1c_1p2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-initial-mode = ; - }; - - vreg_l2c_0p8: ldo2 { - regulator-name = "vreg_l2c_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <880000>; - regulator-initial-mode = ; - }; - - vreg_l3c_0p8: ldo3 { - regulator-name = "vreg_l3c_0p8"; - regulator-min-microvolt = <912000>; - regulator-max-microvolt = <912000>; - regulator-initial-mode = ; - }; - }; - - regulators-2 { - compatible = "qcom,pmc8380-rpmh-regulators"; - qcom,pmic-id = "d"; - - vdd-l1-supply = <&vreg_s1f_0p7>; - vdd-l2-supply = <&vreg_s1f_0p7>; - vdd-l3-supply = <&vreg_s4c_1p8>; - vdd-s1-supply = <&vph_pwr>; - - vreg_l1d_0p8: ldo1 { - regulator-name = "vreg_l1d_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <880000>; - regulator-initial-mode = ; - }; - - vreg_l2d_0p9: ldo2 { - regulator-name = "vreg_l2d_0p9"; - regulator-min-microvolt = <912000>; - regulator-max-microvolt = <912000>; - regulator-initial-mode = ; - }; - - vreg_l3d_1p8: ldo3 { - regulator-name = "vreg_l3d_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - }; - }; - - regulators-3 { - compatible = "qcom,pmc8380-rpmh-regulators"; - qcom,pmic-id = "e"; - - vdd-l2-supply = <&vreg_s1f_0p7>; - vdd-l3-supply = <&vreg_s5j_1p2>; - - vreg_l2e_0p8: ldo2 { - regulator-name = "vreg_l2e_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <880000>; - regulator-initial-mode = ; - }; - - vreg_l3e_1p2: ldo3 { - regulator-name = "vreg_l3e_1p2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-initial-mode = ; - }; - }; - - regulators-4 { - compatible = "qcom,pmc8380-rpmh-regulators"; - qcom,pmic-id = "f"; - - vdd-l1-supply = <&vreg_s5j_1p2>; - vdd-l2-supply = <&vreg_s5j_1p2>; - vdd-l3-supply = <&vreg_s5j_1p2>; - vdd-s1-supply = <&vph_pwr>; - - vreg_s1f_0p7: smps1 { - regulator-name = "vreg_s1f_0p7"; - regulator-min-microvolt = <700000>; - regulator-max-microvolt = <1100000>; - regulator-initial-mode = ; - }; - }; - - regulators-6 { - compatible = "qcom,pm8550ve-rpmh-regulators"; - qcom,pmic-id = "i"; - - vdd-l1-supply = <&vreg_s4c_1p8>; - vdd-l2-supply = <&vreg_s5j_1p2>; - vdd-l3-supply = <&vreg_s1f_0p7>; - vdd-s1-supply = <&vph_pwr>; - vdd-s2-supply = <&vph_pwr>; - - vreg_l1i_1p8: ldo1 { - regulator-name = "vreg_l1i_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - regulator-always-on; - }; - - vreg_l2i_1p2: ldo2 { - regulator-name = "vreg_l2i_1p2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-initial-mode = ; - }; - - vreg_l3i_0p8: ldo3 { - regulator-name = "vreg_l3i_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <880000>; - regulator-initial-mode = ; - }; - - vreg_s1i_0p9: smps1 { - regulator-name = "vreg_s1i_0p9"; - regulator-min-microvolt = <900000>; - regulator-max-microvolt = <920000>; - regulator-initial-mode = ; - }; - - vreg_s2i_1p0: smps2 { - regulator-name = "vreg_s2i_1p0"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1100000>; - regulator-initial-mode = ; - }; - }; - - regulators-7 { - compatible = "qcom,pm8550ve-rpmh-regulators"; - qcom,pmic-id = "j"; - - vdd-l1-supply = <&vreg_s1f_0p7>; - vdd-l2-supply = <&vreg_s5j_1p2>; - vdd-l3-supply = <&vreg_s1f_0p7>; - vdd-s5-supply = <&vph_pwr>; - - vreg_s5j_1p2: smps5 { - regulator-name = "vreg_s5j_1p2"; - regulator-min-microvolt = <1256000>; - regulator-max-microvolt = <1304000>; - regulator-initial-mode = ; - }; - - vreg_l1j_0p8: ldo1 { - regulator-name = "vreg_l1j_0p8"; - regulator-min-microvolt = <912000>; - regulator-max-microvolt = <912000>; - regulator-initial-mode = ; - }; - - vreg_l2j_1p2: ldo2 { - regulator-name = "vreg_l2j_1p2"; - regulator-min-microvolt = <1256000>; - regulator-max-microvolt = <1256000>; - regulator-initial-mode = ; - }; - - vreg_l3j_0p8: ldo3 { - regulator-name = "vreg_l3j_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <880000>; - regulator-initial-mode = ; - }; - }; -}; - -&gpu { - status = "okay"; -}; - -&gpu_zap_shader { - firmware-name = "qcom/x1p42100/LENOVO/83HL/qcdxkmsucpurwa.mbn"; -}; - -&i2c0 { - clock-frequency = <400000>; - - pinctrl-0 = <&qup_i2c0_data_clk>, <&tpad_default>, <&kybd_default>; - pinctrl-names = "default"; - status = "okay"; - - /* ELAN06FA */ - touchpad@15 { - compatible = "hid-over-i2c"; - reg = <0x15>; - - hid-descr-addr = <0x1>; - interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; - - vdd-supply = <&vreg_misc_3p3>; - vddl-supply = <&vreg_l12b_1p2>; - - wakeup-source; - }; - - /* CIRQ1080 or SYNA2BA6 */ - touchpad@2c { - compatible = "hid-over-i2c"; - reg = <0x2c>; - - hid-descr-addr = <0x20>; - interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; - - vdd-supply = <&vreg_misc_3p3>; - vddl-supply = <&vreg_l12b_1p2>; - - wakeup-source; - }; - - /* FTCS0038 */ - touchpad@38 { - compatible = "hid-over-i2c"; - reg = <0x38>; - - hid-descr-addr = <0x1>; - interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; - - vdd-supply = <&vreg_misc_3p3>; - vddl-supply = <&vreg_l12b_1p2>; - - wakeup-source; - }; - - keyboard@3a { - compatible = "hid-over-i2c"; - reg = <0x3a>; - - hid-descr-addr = <0x1>; - interrupts-extended = <&tlmm 67 IRQ_TYPE_LEVEL_LOW>; - - vdd-supply = <&vreg_misc_3p3>; - vddl-supply = <&vreg_l12b_1p2>; - - wakeup-source; - }; - - /* GXTP5100 */ - touchpad@5d { - compatible = "hid-over-i2c"; - reg = <0x5d>; - - hid-descr-addr = <0x1>; - interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; - - vdd-supply = <&vreg_misc_3p3>; - vddl-supply = <&vreg_l12b_1p2>; - - wakeup-source; - }; -}; - -&i2c1 { - clock-frequency = <400000>; - - status = "okay"; - - /* 3d PS883x */ -}; - -&i2c3 { - clock-frequency = <400000>; - - status = "okay"; - - /* 1st PS883x */ }; -&i2c4 { - clock-frequency = <400000>; - - status = "okay"; - - /* unknown but active on Windows */ -}; - -&i2c5 { - clock-frequency = <400000>; - - status = "okay"; - - eusb5_repeater: redriver@43 { - compatible = "nxp,ptn3222"; - reg = <0x43>; - #phy-cells = <0>; - - vdd3v3-supply = <&vreg_l13b_3p0>; - vdd1v8-supply = <&vreg_l4b_1p8>; - - reset-gpios = <&tlmm 7 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&eusb5_reset_n>; - pinctrl-names = "default"; - }; - - eusb3_repeater: redriver@47 { - compatible = "nxp,ptn3222"; - reg = <0x47>; - #phy-cells = <0>; - - vdd3v3-supply = <&vreg_l13b_3p0>; - vdd1v8-supply = <&vreg_l4b_1p8>; - - reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&eusb3_reset_n>; - pinctrl-names = "default"; - }; - - eusb9_repeater: redriver@4b { - compatible = "nxp,ptn3222"; - reg = <0x4b>; - #phy-cells = <0>; - - vdd3v3-supply = <&vreg_l13b_3p0>; - vdd1v8-supply = <&vreg_l4b_1p8>; - - reset-gpios = <&tlmm 9 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&eusb9_reset_n>; - pinctrl-names = "default"; - }; - - eusb6_repeater: redriver@4f { - compatible = "nxp,ptn3222"; - reg = <0x4f>; - #phy-cells = <0>; - - vdd3v3-supply = <&vreg_l13b_3p0>; - vdd1v8-supply = <&vreg_l4b_1p8>; - - reset-gpios = <&tlmm 184 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&eusb6_reset_n>; - pinctrl-names = "default"; - }; -}; - -&i2c7 { - clock-frequency = <400000>; - - status = "okay"; - - /* 2nd PS883x */ -}; - -&i2c8 { - clock-frequency = <400000>; - - status = "okay"; - - /* unknown but active on Windows */ -}; - -//&iris { -// firmware-name = "qcom/x1p42100/LENOVO/83HL/qcvss8380.mbn"; -// status = "okay"; -//}; - -&lpass_tlmm { - spkr_01_sd_n_active: spkr-01-sd-n-active-state { - pins = "gpio12"; - function = "gpio"; - drive-strength = <16>; - bias-disable; - output-low; - }; -}; - -&lpass_vamacro { - pinctrl-0 = <&dmic01_default>; - pinctrl-names = "default"; - - vdd-micb-supply = <&vreg_l1b_1p8>; - qcom,dmic-sample-rate = <2400000>; -}; - -&mdss { - status = "okay"; -}; - -&mdss_dp0 { - sound-name-prefix = "DisplayPort0"; - - status = "okay"; -}; - -&mdss_dp0_out { - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; -}; - -&mdss_dp1 { - sound-name-prefix = "DisplayPort1"; - - status = "okay"; -}; - -&mdss_dp1_out { - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; -}; - -&mdss_dp2 { - sound-name-prefix = "DisplayPort2"; - - status = "okay"; -}; - -&mdss_dp2_out { - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; -}; - -&mdss_dp3 { - /delete-property/ #sound-dai-cells; - - pinctrl-0 = <&edp0_hpd_default>; - pinctrl-names = "default"; - - status = "okay"; - - aux-bus { - panel { - compatible = "edp-panel"; - power-supply = <&vreg_edp_3p3>; - - backlight = <&backlight>; - - port { - edp_panel_in: endpoint { - remote-endpoint = <&mdss_dp3_out>; - }; - }; - }; - }; -}; - -&mdss_dp3_out { - data-lanes = <0 1 2 3>; - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; - - remote-endpoint = <&edp_panel_in>; -}; - -&mdss_dp3_phy { - vdda-phy-supply = <&vreg_l3j_0p8>; - vdda-pll-supply = <&vreg_l2j_1p2>; - - status = "okay"; -}; - -&pcie3 { - pinctrl-0 = <&pcie3_default>; - pinctrl-names = "default"; - - max-link-speed = <1>; - status = "okay"; -}; - -&pcie3_phy { - vdda-phy-supply = <&vreg_l3c_0p8>; - vdda-pll-supply = <&vreg_l3e_1p2>; - vdda-qref-supply = <&vreg_l3j_0p8>; - - status = "okay"; -}; - -&pcie3_port0 { - perst-gpios = <&tlmm 143 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 145 GPIO_ACTIVE_LOW>; -}; - -&pcie4 { - pinctrl-0 = <&pcie4_default>; - pinctrl-names = "default"; - - status = "okay"; -}; - -&pcie4_phy { - vdda-phy-supply = <&vreg_l3i_0p8>; - vdda-pll-supply = <&vreg_l3e_1p2>; - - status = "okay"; -}; - -&pcie4_port0 { - reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; - - wifi@0 { - compatible = "pci17cb,1107"; - reg = <0x10000 0x0 0x0 0x0 0x0>; - - vddaon-supply = <&vreg_pmu_aon_0p59>; - vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; - vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; - vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; - vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; - vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; - vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; - vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; - vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; - }; -}; - -&pcie6a { - vddpe-3v3-supply = <&vreg_nvme>; - - pinctrl-0 = <&pcie6a_default>; - pinctrl-names = "default"; - - status = "okay"; -}; - -&pcie6a_phy { - vdda-phy-supply = <&vreg_l1d_0p8>; - vdda-pll-supply = <&vreg_l2j_1p2>; - - status = "okay"; -}; - -&pcie6a_port0 { - reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; -}; - -&pm8550ve_8_gpios { - misc_3p3_reg_en: misc-3p3-reg-en-state { - pins = "gpio6"; - function = "normal"; - bias-disable; - drive-push-pull; - input-disable; - output-enable; - power-source = <1>; /* 1.8 V */ - qcom,drive-strength = ; - }; +&panel { + backlight = <&backlight>; }; &pmk8550_pwm { status = "okay"; }; - -&qupv3_0 { - status = "okay"; -}; - -&qupv3_1 { - status = "okay"; -}; - -&qupv3_2 { - status = "okay"; -}; - -&gpi_dma0 { - status = "okay"; -}; - -&gpi_dma1 { - status = "okay"; -}; - -&gpi_dma2 { - status = "okay"; -}; - -&remoteproc_adsp { - firmware-name = "qcom/x1p42100/LENOVO/83HL/qcadsp8380.mbn", - "qcom/x1p42100/LENOVO/83HL/adsp_dtbs.elf"; - - status = "okay"; -}; - -&remoteproc_cdsp { - firmware-name = "qcom/x1p42100/LENOVO/83HL/qccdsp8380.mbn", - "qcom/x1p42100/LENOVO/83HL/cdsp_dtbs.elf"; - - status = "okay"; -}; - -&smb2360_0 { - status = "okay"; -}; - -&smb2360_0_eusb2_repeater { - vdd18-supply = <&vreg_l3d_1p8>; - vdd3-supply = <&vreg_l2b_3p0>; -}; - -&smb2360_1 { - status = "okay"; -}; - -&smb2360_1_eusb2_repeater { - vdd18-supply = <&vreg_l3d_1p8>; - vdd3-supply = <&vreg_l14b_3p0>; -}; - -&spi10 { - status = "disabled"; - - /* Unknown device */ -}; - -&swr0 { - status = "okay"; - - pinctrl-0 = <&wsa_swr_active>, <&spkr_01_sd_n_active>; - pinctrl-names = "default"; - - /* WSA8845, Left Speaker */ - left_spkr: speaker@0,0 { - compatible = "sdw20217020400"; - reg = <0 0>; - reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; - #sound-dai-cells = <0>; - sound-name-prefix = "SpkrLeft"; - vdd-1p8-supply = <&vreg_l15b_1p8>; - vdd-io-supply = <&vreg_l12b_1p2>; - qcom,port-mapping = <1 2 3 7 10 13>; - }; - - /* WSA8845, Right Speaker */ - right_spkr: speaker@0,1 { - compatible = "sdw20217020400"; - reg = <0 1>; - reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; - #sound-dai-cells = <0>; - sound-name-prefix = "SpkrRight"; - vdd-1p8-supply = <&vreg_l15b_1p8>; - vdd-io-supply = <&vreg_l12b_1p2>; - qcom,port-mapping = <4 5 6 7 11 13>; - }; -}; - -&swr1 { - status = "okay"; - - /* WCD9385 RX */ - wcd_rx: codec@0,4 { - compatible = "sdw20217010d00"; - reg = <0 4>; - qcom,rx-port-mapping = <1 2 3 4 5>; - }; -}; - -&swr2 { - status = "okay"; - - /* WCD9385 TX */ - wcd_tx: codec@0,3 { - compatible = "sdw20217010d00"; - reg = <0 3>; - qcom,tx-port-mapping = <2 2 3 4>; - }; -}; - -&tlmm { - gpio-reserved-ranges = <34 2>, /* Unused */ - <44 4>, - <72 2>; /* Secure EC I2C connection (?) */ - - eusb3_reset_n: eusb3-reset-n-state { - pins = "gpio6"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - output-low; - }; - - eusb5_reset_n: eusb5-reset-n-state { - pins = "gpio7"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - output-low; - }; - - eusb6_reset_n: eusb6-reset-n-state { - pins = "gpio184"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - output-low; - }; - - eusb9_reset_n: eusb9-reset-n-state { - pins = "gpio9"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - output-low; - }; - - edp_reg_en: edp-reg-en-state { - pins = "gpio70"; - function = "gpio"; - drive-strength = <16>; - bias-disable; - }; - - hall_int_n_default: hall-int-n-state { - pins = "gpio92"; - function = "gpio"; - bias-disable; - }; - - hdmi_hpd_default: hdmi-hpd-default-state { - pins = "gpio126"; - function = "usb2_dp"; - bias-disable; - }; - - kybd_default: kybd-default-state { - pins = "gpio67"; - function = "gpio"; - bias-disable; - }; - - nvme_reg_en: nvme-reg-en-state { - pins = "gpio18"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - - pcie3_default: pcie3-default-state { - clkreq-n-pins { - pins = "gpio144"; - function = "pcie3_clk"; - drive-strength = <2>; - bias-pull-up; - }; - - perst-n-pins { - pins = "gpio143"; - function = "gpio"; - drive-strength = <2>; - bias-pull-down; - }; - - wake-n-pins { - pins = "gpio145"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - }; - - pcie4_default: pcie4-default-state { - clkreq-n-pins { - pins = "gpio147"; - function = "pcie4_clk"; - drive-strength = <2>; - bias-pull-up; - }; - - perst-n-pins { - pins = "gpio146"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - - wake-n-pins { - pins = "gpio148"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - }; - - pcie6a_default: pcie6a-default-state { - clkreq-n-pins { - pins = "gpio153"; - function = "pcie6a_clk"; - drive-strength = <2>; - bias-pull-up; - }; - - perst-n-pins { - pins = "gpio152"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - - wake-n-pins { - pins = "gpio154"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - }; - - sdc2_card_det_n: sdc2-card-det-state { - pins = "gpio71"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - - tpad_default: tpad-default-state { - pins = "gpio3"; - function = "gpio"; - bias-pull-up; - }; - - usb_1_ss0_sbu_default: usb-1-ss0-sbu-state { - oe-n-pins { - pins = "gpio168"; - function = "gpio"; - bias-pull-up; - drive-strength = <2>; - }; - - sel-pins { - pins = "gpio167"; - function = "gpio"; - bias-pull-up; - drive-strength = <2>; - }; - - }; - - usb_1_ss1_sbu_default: usb-1-ss1-sbu-state { - oe-n-pins { - pins = "gpio179"; - function = "gpio"; - bias-pull-up; - drive-strength = <2>; - }; - - sel-pins { - pins = "gpio178"; - function = "gpio"; - bias-pull-up; - drive-strength = <2>; - }; - }; - - wcd_default: wcd-reset-n-active-state { - pins = "gpio191"; - function = "gpio"; - drive-strength = <16>; - bias-disable; - output-low; - }; - - wcn_sw_en: wcn-sw-en-state { - pins = "gpio214"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - - wcn_wlan_bt_en: wcn-wlan-bt-en-state { - pins = "gpio116", "gpio117"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; -}; - -&uart14 { - status = "okay"; - - bluetooth { - compatible = "qcom,wcn7850-bt"; - max-speed = <3200000>; - - vddaon-supply = <&vreg_pmu_aon_0p59>; - vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; - vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; - vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; - vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; - vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; - vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; - }; -}; - -&usb_1_ss0_hsphy { - vdd-supply = <&vreg_l3j_0p8>; - vdda12-supply = <&vreg_l2j_1p2>; - - phys = <&smb2360_0_eusb2_repeater>; - - status = "okay"; -}; - -&usb_1_ss0_qmpphy { - vdda-phy-supply = <&vreg_l2j_1p2>; - vdda-pll-supply = <&vreg_l1j_0p8>; - - status = "okay"; -}; - -&usb_1_ss0 { - dr_mode = "host"; - - status = "okay"; -}; - -&usb_1_ss0_dwc3_hs { - remote-endpoint = <&pmic_glink_ss0_hs_in>; -}; - -&usb_1_ss0_qmpphy_out { - remote-endpoint = <&pmic_glink_ss0_ss_in>; -}; - -&usb_1_ss1_hsphy { - vdd-supply = <&vreg_l3j_0p8>; - vdda12-supply = <&vreg_l2j_1p2>; - - phys = <&smb2360_1_eusb2_repeater>; - - status = "okay"; -}; - -&usb_1_ss1_qmpphy { - vdda-phy-supply = <&vreg_l2j_1p2>; - vdda-pll-supply = <&vreg_l2d_0p9>; - - status = "okay"; -}; - -&usb_1_ss1 { - status = "okay"; - - dr_mode = "host"; -}; - -&usb_1_ss1_dwc3_hs { - remote-endpoint = <&pmic_glink_ss1_hs_in>; -}; - -&usb_1_ss1_qmpphy_out { - remote-endpoint = <&pmic_glink_ss1_ss_in>; -}; - -&usb_1_ss2 { - dr_mode = "host"; - maximum-speed = "high-speed"; - phys = <&usb_1_ss2_hsphy>; - phy-names = "usb2-phy"; - qcom,select-utmi-as-pipe-clk; - status = "okay"; - /delete-property/ port@1; -}; - -&usb_1_ss2_hsphy { - vdd-supply = <&vreg_l3j_0p8>; - vdda12-supply = <&vreg_l2j_1p2>; - - phys = <&eusb9_repeater>; - - status = "okay"; -}; - -&usb_1_ss2_qmpphy { - vdda-phy-supply = <&vreg_l2j_1p2>; - vdda-pll-supply = <&vreg_l2d_0p9>; - - /delete-property/ mode-switch; - /delete-property/ orientation-switch; - - status = "okay"; - - ports { - port@0 { - #address-cells = <1>; - #size-cells = <0>; - - /delete-node/ endpoint; - - usb_1_ss2_qmpphy_out_dp: endpoint@0 { - reg = <0>; - - data-lanes = <3 2 1 0>; - remote-endpoint = <&hdmi_bridge_dp_in>; - }; - }; - }; -}; - -&usb_2 { - dr_mode = "host"; - - status = "okay"; -}; - -&usb_2_hsphy { - vdd-supply = <&vreg_l2e_0p8>; - vdda12-supply = <&vreg_l3e_1p2>; - - phys = <&eusb5_repeater>; - - status = "okay"; -}; - -&usb_mp { - status = "okay"; -}; - -&usb_mp_hsphy0 { - vdd-supply = <&vreg_l2e_0p8>; - vdda12-supply = <&vreg_l3e_1p2>; - - phys = <&eusb3_repeater>; - - status = "okay"; -}; - -&usb_mp_qmpphy0 { - vdda-phy-supply = <&vreg_l3e_1p2>; - vdda-pll-supply = <&vreg_l3c_0p8>; - - status = "okay"; -}; - -&usb_mp_hsphy1 { - vdd-supply = <&vreg_l2e_0p8>; - vdda12-supply = <&vreg_l3e_1p2>; - - phys = <&eusb6_repeater>; - - status = "okay"; -}; - -&usb_mp_qmpphy1 { - vdda-phy-supply = <&vreg_l3e_1p2>; - vdda-pll-supply = <&vreg_l3c_0p8>; - - status = "okay"; -}; diff --git a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-slim5x-oled.dts b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-slim5x-oled.dts index de7422c87011e4..2487c99b34798c 100644 --- a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-slim5x-oled.dts +++ b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-slim5x-oled.dts @@ -7,1695 +7,9 @@ /dts-v1/; -#include -#include -#include -#include -#include - -#include "purwa.dtsi" -#include "hamoa-pmics.dtsi" - -/delete-node/ &pmc8380_6; -/delete-node/ &pmc8380_6_thermal; +#include "x1p42100-lenovo-ideapad-slim5x.dtsi" / { model = "Lenovo IdeaPad Slim 5x 14Q8X9 (OLED)"; - compatible = "lenovo,ideapad-slim5x-oled", "lenovo,thinkpad-t14s", "qcom,x1p42100"; - chassis-type = "laptop"; - - aliases { - serial0 = &uart21; - serial1 = &uart14; - }; - - wcd938x: audio-codec { - compatible = "qcom,wcd9385-codec"; - - pinctrl-0 = <&wcd_default>; - pinctrl-names = "default"; - - qcom,micbias1-microvolt = <1800000>; - qcom,micbias2-microvolt = <1800000>; - qcom,micbias3-microvolt = <1800000>; - qcom,micbias4-microvolt = <1800000>; - qcom,mbhc-buttons-vthreshold-microvolt = <75000 150000 237000 500000 500000 500000 500000 500000>; - qcom,mbhc-headset-vthreshold-microvolt = <1700000>; - qcom,mbhc-headphone-vthreshold-microvolt = <50000>; - qcom,rx-device = <&wcd_rx>; - qcom,tx-device = <&wcd_tx>; - - reset-gpios = <&tlmm 191 GPIO_ACTIVE_LOW>; - - vdd-buck-supply = <&vreg_l15b_1p8>; - vdd-rxtx-supply = <&vreg_l15b_1p8>; - vdd-io-supply = <&vreg_l15b_1p8>; - vdd-mic-bias-supply = <&vreg_bob1>; - - #sound-dai-cells = <1>; - }; - - - hdmi-bridge { - compatible = "parade,ps185hdm"; - - pinctrl-0 = <&hdmi_hpd_default>; - pinctrl-names = "default"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - hdmi_bridge_dp_in: endpoint { - remote-endpoint = <&usb_1_ss2_qmpphy_out_dp>; - }; - }; - - port@1 { - reg = <1>; - - hdmi_bridge_tmds_out: endpoint { - remote-endpoint = <&hdmi_con>; - }; - }; - }; - }; - - hdmi-connector { - compatible = "hdmi-connector"; - type = "a"; - - port { - hdmi_con: endpoint { - remote-endpoint = <&hdmi_bridge_tmds_out>; - }; - }; - }; - - gpio-keys { - compatible = "gpio-keys"; - - pinctrl-0 = <&hall_int_n_default>; - pinctrl-names = "default"; - - switch-lid { - gpios = <&tlmm 92 GPIO_ACTIVE_LOW>; - linux,input-type = ; - linux,code = ; - wakeup-source; - wakeup-event-action = ; - }; - }; - - pmic-glink { - compatible = "qcom,x1e80100-pmic-glink", - "qcom,sm8550-pmic-glink", - "qcom,pmic-glink"; - #address-cells = <1>; - #size-cells = <0>; - orientation-gpios = <&tlmm 121 GPIO_ACTIVE_HIGH>, - <&tlmm 123 GPIO_ACTIVE_HIGH>; - - /* Display-adjacent port */ - connector@0 { - compatible = "usb-c-connector"; - reg = <0>; - power-role = "dual"; - data-role = "host"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - pmic_glink_ss0_hs_in: endpoint { - remote-endpoint = <&usb_1_ss0_dwc3_hs>; - }; - }; - - port@1 { - reg = <1>; - - pmic_glink_ss0_ss_in: endpoint { - remote-endpoint = <&usb_1_ss0_qmpphy_out>; - }; - }; - - port@2 { - reg = <2>; - - pmic_glink_ss0_sbu: endpoint { - remote-endpoint = <&usb_1_ss0_sbu_mux>; - }; - }; - }; - }; - - /* User-adjacent port */ - connector@1 { - compatible = "usb-c-connector"; - reg = <1>; - power-role = "dual"; - data-role = "host"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - pmic_glink_ss1_hs_in: endpoint { - remote-endpoint = <&usb_1_ss1_dwc3_hs>; - }; - }; - - port@1 { - reg = <1>; - - pmic_glink_ss1_ss_in: endpoint { - remote-endpoint = <&usb_1_ss1_qmpphy_out>; - }; - }; - - port@2 { - reg = <2>; - - pmic_glink_ss1_sbu: endpoint { - remote-endpoint = <&usb_1_ss1_sbu_mux>; - }; - }; - }; - }; - }; - - sound { - compatible = "qcom,x1e80100-sndcard"; - model = "X1E80100-LENOVO-Ideapad-5"; - audio-routing = "SpkrLeft IN", "WSA WSA_SPK1 OUT", - "SpkrRight IN", "WSA WSA_SPK2 OUT", - "IN1_HPHL", "HPHL_OUT", - "IN2_HPHR", "HPHR_OUT", - "AMIC2", "MIC BIAS2", - "VA DMIC0", "MIC BIAS1", - "VA DMIC1", "MIC BIAS1", - "VA DMIC0", "VA MIC BIAS1", - "VA DMIC1", "VA MIC BIAS1", - "TX SWR_INPUT1", "ADC2_OUTPUT"; - - displayport-0-dai-link { - link-name = "DisplayPort0 Playback"; - - codec { - sound-dai = <&mdss_dp0>; - }; - - cpu { - sound-dai = <&q6apmbedai DISPLAY_PORT_RX_0>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - - displayport-1-dai-link { - link-name = "DisplayPort1 Playback"; - - codec { - sound-dai = <&mdss_dp1>; - }; - - cpu { - sound-dai = <&q6apmbedai DISPLAY_PORT_RX_1>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - - displayport-2-dai-link { - link-name = "DisplayPort2 Playback"; - - codec { - sound-dai = <&mdss_dp2>; - }; - - cpu { - sound-dai = <&q6apmbedai DISPLAY_PORT_RX_2>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - - wcd-playback-dai-link { - link-name = "WCD Playback"; - - codec { - sound-dai = <&wcd938x 0>, <&swr1 0>, <&lpass_rxmacro 0>; - }; - - cpu { - sound-dai = <&q6apmbedai RX_CODEC_DMA_RX_0>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - - wcd-capture-dai-link { - link-name = "WCD Capture"; - - codec { - sound-dai = <&wcd938x 1>, <&swr2 1>, <&lpass_txmacro 0>; - }; - - cpu { - sound-dai = <&q6apmbedai TX_CODEC_DMA_TX_3>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - - wsa-dai-link { - link-name = "WSA Playback"; - - codec { - sound-dai = <&left_spkr>, <&right_spkr>, <&swr0 0>, <&lpass_wsamacro 0>; - }; - - cpu { - sound-dai = <&q6apmbedai WSA_CODEC_DMA_RX_0>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - - va-dai-link { - link-name = "VA Capture"; - - codec { - sound-dai = <&lpass_vamacro 0>; - }; - - cpu { - sound-dai = <&q6apmbedai VA_CODEC_DMA_TX_0>; - }; - - platform { - sound-dai = <&q6apm>; - }; - }; - }; - - usb-1-ss0-sbu-mux { - compatible = "onnn,fsusb42", "gpio-sbu-mux"; - - enable-gpios = <&tlmm 168 GPIO_ACTIVE_LOW>; - select-gpios = <&tlmm 167 GPIO_ACTIVE_HIGH>; - - pinctrl-0 = <&usb_1_ss0_sbu_default>; - pinctrl-names = "default"; - - mode-switch; - orientation-switch; - - port { - usb_1_ss0_sbu_mux: endpoint { - remote-endpoint = <&pmic_glink_ss0_sbu>; - }; - }; - }; - - usb-1-ss1-sbu-mux { - compatible = "onnn,fsusb42", "gpio-sbu-mux"; - - enable-gpios = <&tlmm 179 GPIO_ACTIVE_LOW>; - select-gpios = <&tlmm 178 GPIO_ACTIVE_HIGH>; - - pinctrl-0 = <&usb_1_ss1_sbu_default>; - pinctrl-names = "default"; - - mode-switch; - orientation-switch; - - port { - usb_1_ss1_sbu_mux: endpoint { - remote-endpoint = <&pmic_glink_ss1_sbu>; - }; - }; - }; - - vreg_edp_3p3: regulator-edp-3p3 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_EDP_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&tlmm 70 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&edp_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - regulator-always-on; - }; - - vreg_misc_3p3: regulator-misc-3p3 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_MISC_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&pm8550ve_8_gpios 6 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&misc_3p3_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - regulator-always-on; - }; - - vreg_nvme: regulator-nvme { - compatible = "regulator-fixed"; - - regulator-name = "VREG_NVME_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&tlmm 18 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&nvme_reg_en>; - pinctrl-names = "default"; - - regulator-boot-on; - }; - - vreg_wcn_0p95: regulator-wcn-0p95 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_WCN_0P95"; - regulator-min-microvolt = <950000>; - regulator-max-microvolt = <950000>; - - vin-supply = <&vreg_wcn_3p3>; - }; - - vreg_wcn_1p9: regulator-wcn-1p9 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_WCN_1P9"; - regulator-min-microvolt = <1900000>; - regulator-max-microvolt = <1900000>; - - vin-supply = <&vreg_wcn_3p3>; - }; - - vreg_wcn_3p3: regulator-wcn-3p3 { - compatible = "regulator-fixed"; - - regulator-name = "VREG_WCN_3P3"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - - gpio = <&tlmm 214 GPIO_ACTIVE_HIGH>; - enable-active-high; - - pinctrl-0 = <&wcn_sw_en>; - pinctrl-names = "default"; - - regulator-boot-on; - }; - - vph_pwr: regulator-vph-pwr { - compatible = "regulator-fixed"; - - regulator-name = "vph_pwr"; - regulator-min-microvolt = <3700000>; - regulator-max-microvolt = <3700000>; - - regulator-always-on; - regulator-boot-on; - }; - - wcn7850-pmu { - compatible = "qcom,wcn7850-pmu"; - - vdd-supply = <&vreg_wcn_0p95>; - vddio-supply = <&vreg_l15b_1p8>; - vddaon-supply = <&vreg_wcn_0p95>; - vdddig-supply = <&vreg_wcn_0p95>; - vddrfa1p2-supply = <&vreg_wcn_1p9>; - vddrfa1p8-supply = <&vreg_wcn_1p9>; - - wlan-enable-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>; - bt-enable-gpios = <&tlmm 116 GPIO_ACTIVE_HIGH>; - - pinctrl-0 = <&wcn_wlan_bt_en>; - pinctrl-names = "default"; - - regulators { - vreg_pmu_rfa_cmn: ldo0 { - regulator-name = "vreg_pmu_rfa_cmn"; - }; - - vreg_pmu_aon_0p59: ldo1 { - regulator-name = "vreg_pmu_aon_0p59"; - }; - - vreg_pmu_wlcx_0p8: ldo2 { - regulator-name = "vreg_pmu_wlcx_0p8"; - }; - - vreg_pmu_wlmx_0p85: ldo3 { - regulator-name = "vreg_pmu_wlmx_0p85"; - }; - - vreg_pmu_btcmx_0p85: ldo4 { - regulator-name = "vreg_pmu_btcmx_0p85"; - }; - - vreg_pmu_rfa_0p8: ldo5 { - regulator-name = "vreg_pmu_rfa_0p8"; - }; - - vreg_pmu_rfa_1p2: ldo6 { - regulator-name = "vreg_pmu_rfa_1p2"; - }; - - vreg_pmu_rfa_1p8: ldo7 { - regulator-name = "vreg_pmu_rfa_1p8"; - }; - - vreg_pmu_pcie_0p9: ldo8 { - regulator-name = "vreg_pmu_pcie_0p9"; - }; - - vreg_pmu_pcie_1p8: ldo9 { - regulator-name = "vreg_pmu_pcie_1p8"; - }; - }; - }; -}; - -&apps_rsc { - regulators-0 { - compatible = "qcom,pm8550-rpmh-regulators"; - qcom,pmic-id = "b"; - - vdd-bob1-supply = <&vph_pwr>; - vdd-bob2-supply = <&vph_pwr>; - vdd-l1-l4-l10-supply = <&vreg_s4c_1p8>; - vdd-l2-l13-l14-supply = <&vreg_bob1>; - vdd-l5-l16-supply = <&vreg_bob1>; - vdd-l6-l7-supply = <&vreg_bob2>; - vdd-l8-l9-supply = <&vreg_bob1>; - vdd-l12-supply = <&vreg_s5j_1p2>; - vdd-l15-supply = <&vreg_s4c_1p8>; - vdd-l17-supply = <&vreg_bob2>; - - vreg_bob1: bob1 { - regulator-name = "vreg_bob1"; - regulator-min-microvolt = <3008000>; - regulator-max-microvolt = <3960000>; - regulator-initial-mode = ; - }; - - vreg_bob2: bob2 { - regulator-name = "vreg_bob2"; - regulator-min-microvolt = <2504000>; - regulator-max-microvolt = <3008000>; - regulator-initial-mode = ; - }; - - vreg_l1b_1p8: ldo1 { - regulator-name = "vreg_l1b_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - }; - - vreg_l2b_3p0: ldo2 { - regulator-name = "vreg_l2b_3p0"; - regulator-min-microvolt = <3072000>; - regulator-max-microvolt = <3072000>; - regulator-initial-mode = ; - }; - - vreg_l4b_1p8: ldo4 { - regulator-name = "vreg_l4b_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - vreg_l6b_1p8: ldo6 { - regulator-name = "vreg_l6b_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <2960000>; - regulator-initial-mode = ; - }; - - vreg_l8b_3p0: ldo8 { - regulator-name = "vreg_l8b_3p0"; - regulator-min-microvolt = <3072000>; - regulator-max-microvolt = <3072000>; - regulator-initial-mode = ; - }; - - vreg_l9b_2p9: ldo9 { - regulator-name = "vreg_l9b_2p9"; - regulator-min-microvolt = <2960000>; - regulator-max-microvolt = <2960000>; - regulator-initial-mode = ; - }; - - vreg_l10b_1p8: ldo10 { - regulator-name = "vreg_l10b_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - }; - - vreg_l12b_1p2: ldo12 { - regulator-name = "vreg_l12b_1p2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-initial-mode = ; - regulator-always-on; - }; - - vreg_l13b_3p0: ldo13 { - regulator-name = "vreg_l13b_3p0"; - regulator-min-microvolt = <3072000>; - regulator-max-microvolt = <3072000>; - regulator-initial-mode = ; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - vreg_l14b_3p0: ldo14 { - regulator-name = "vreg_l14b_3p0"; - regulator-min-microvolt = <3072000>; - regulator-max-microvolt = <3072000>; - regulator-initial-mode = ; - }; - - vreg_l15b_1p8: ldo15 { - regulator-name = "vreg_l15b_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - regulator-always-on; - }; - - vreg_l17b_2p5: ldo17 { - regulator-name = "vreg_l17b_2p5"; - regulator-min-microvolt = <2504000>; - regulator-max-microvolt = <2504000>; - regulator-initial-mode = ; - }; - }; - - regulators-1 { - compatible = "qcom,pm8550ve-rpmh-regulators"; - qcom,pmic-id = "c"; - - vdd-l1-supply = <&vreg_s5j_1p2>; - vdd-l2-supply = <&vreg_s1f_0p7>; - vdd-l3-supply = <&vreg_s1f_0p7>; - vdd-s4-supply = <&vph_pwr>; - - vreg_s4c_1p8: smps4 { - regulator-name = "vreg_s4c_1p8"; - regulator-min-microvolt = <1856000>; - regulator-max-microvolt = <2000000>; - regulator-initial-mode = ; - }; - - vreg_l1c_1p2: ldo1 { - regulator-name = "vreg_l1c_1p2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-initial-mode = ; - }; - - vreg_l2c_0p8: ldo2 { - regulator-name = "vreg_l2c_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <880000>; - regulator-initial-mode = ; - }; - - vreg_l3c_0p8: ldo3 { - regulator-name = "vreg_l3c_0p8"; - regulator-min-microvolt = <912000>; - regulator-max-microvolt = <912000>; - regulator-initial-mode = ; - }; - }; - - regulators-2 { - compatible = "qcom,pmc8380-rpmh-regulators"; - qcom,pmic-id = "d"; - - vdd-l1-supply = <&vreg_s1f_0p7>; - vdd-l2-supply = <&vreg_s1f_0p7>; - vdd-l3-supply = <&vreg_s4c_1p8>; - vdd-s1-supply = <&vph_pwr>; - - vreg_l1d_0p8: ldo1 { - regulator-name = "vreg_l1d_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <880000>; - regulator-initial-mode = ; - }; - - vreg_l2d_0p9: ldo2 { - regulator-name = "vreg_l2d_0p9"; - regulator-min-microvolt = <912000>; - regulator-max-microvolt = <912000>; - regulator-initial-mode = ; - }; - - vreg_l3d_1p8: ldo3 { - regulator-name = "vreg_l3d_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - }; - }; - - regulators-3 { - compatible = "qcom,pmc8380-rpmh-regulators"; - qcom,pmic-id = "e"; - - vdd-l2-supply = <&vreg_s1f_0p7>; - vdd-l3-supply = <&vreg_s5j_1p2>; - - vreg_l2e_0p8: ldo2 { - regulator-name = "vreg_l2e_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <880000>; - regulator-initial-mode = ; - }; - - vreg_l3e_1p2: ldo3 { - regulator-name = "vreg_l3e_1p2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-initial-mode = ; - }; - }; - - regulators-4 { - compatible = "qcom,pmc8380-rpmh-regulators"; - qcom,pmic-id = "f"; - - vdd-l1-supply = <&vreg_s5j_1p2>; - vdd-l2-supply = <&vreg_s5j_1p2>; - vdd-l3-supply = <&vreg_s5j_1p2>; - vdd-s1-supply = <&vph_pwr>; - - vreg_s1f_0p7: smps1 { - regulator-name = "vreg_s1f_0p7"; - regulator-min-microvolt = <700000>; - regulator-max-microvolt = <1100000>; - regulator-initial-mode = ; - }; - }; - - regulators-6 { - compatible = "qcom,pm8550ve-rpmh-regulators"; - qcom,pmic-id = "i"; - - vdd-l1-supply = <&vreg_s4c_1p8>; - vdd-l2-supply = <&vreg_s5j_1p2>; - vdd-l3-supply = <&vreg_s1f_0p7>; - vdd-s1-supply = <&vph_pwr>; - vdd-s2-supply = <&vph_pwr>; - - vreg_l1i_1p8: ldo1 { - regulator-name = "vreg_l1i_1p8"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-initial-mode = ; - regulator-always-on; - }; - - vreg_l2i_1p2: ldo2 { - regulator-name = "vreg_l2i_1p2"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-initial-mode = ; - }; - - vreg_l3i_0p8: ldo3 { - regulator-name = "vreg_l3i_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <880000>; - regulator-initial-mode = ; - }; - - vreg_s1i_0p9: smps1 { - regulator-name = "vreg_s1i_0p9"; - regulator-min-microvolt = <900000>; - regulator-max-microvolt = <920000>; - regulator-initial-mode = ; - }; - - vreg_s2i_1p0: smps2 { - regulator-name = "vreg_s2i_1p0"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1100000>; - regulator-initial-mode = ; - }; - }; - - regulators-7 { - compatible = "qcom,pm8550ve-rpmh-regulators"; - qcom,pmic-id = "j"; - - vdd-l1-supply = <&vreg_s1f_0p7>; - vdd-l2-supply = <&vreg_s5j_1p2>; - vdd-l3-supply = <&vreg_s1f_0p7>; - vdd-s5-supply = <&vph_pwr>; - - vreg_s5j_1p2: smps5 { - regulator-name = "vreg_s5j_1p2"; - regulator-min-microvolt = <1256000>; - regulator-max-microvolt = <1304000>; - regulator-initial-mode = ; - }; - - vreg_l1j_0p8: ldo1 { - regulator-name = "vreg_l1j_0p8"; - regulator-min-microvolt = <912000>; - regulator-max-microvolt = <912000>; - regulator-initial-mode = ; - }; - - vreg_l2j_1p2: ldo2 { - regulator-name = "vreg_l2j_1p2"; - regulator-min-microvolt = <1256000>; - regulator-max-microvolt = <1256000>; - regulator-initial-mode = ; - }; - - vreg_l3j_0p8: ldo3 { - regulator-name = "vreg_l3j_0p8"; - regulator-min-microvolt = <880000>; - regulator-max-microvolt = <880000>; - regulator-initial-mode = ; - }; - }; -}; - -&gpu { - status = "okay"; -}; - -&gpu_zap_shader { - firmware-name = "qcom/x1p42100/LENOVO/83HL/qcdxkmsucpurwa.mbn"; -}; - -&i2c0 { - clock-frequency = <400000>; - - pinctrl-0 = <&qup_i2c0_data_clk>, <&tpad_default>, <&kybd_default>; - pinctrl-names = "default"; - status = "okay"; - - /* ELAN06FA */ - touchpad@15 { - compatible = "hid-over-i2c"; - reg = <0x15>; - - hid-descr-addr = <0x1>; - interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; - - vdd-supply = <&vreg_misc_3p3>; - vddl-supply = <&vreg_l12b_1p2>; - - wakeup-source; - }; - - /* CIRQ1080 or SYNA2BA6 */ - touchpad@2c { - compatible = "hid-over-i2c"; - reg = <0x2c>; - - hid-descr-addr = <0x20>; - interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; - - vdd-supply = <&vreg_misc_3p3>; - vddl-supply = <&vreg_l12b_1p2>; - - wakeup-source; - }; - - /* FTCS0038 */ - touchpad@38 { - compatible = "hid-over-i2c"; - reg = <0x38>; - - hid-descr-addr = <0x1>; - interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; - - vdd-supply = <&vreg_misc_3p3>; - vddl-supply = <&vreg_l12b_1p2>; - - wakeup-source; - }; - - keyboard@3a { - compatible = "hid-over-i2c"; - reg = <0x3a>; - - hid-descr-addr = <0x1>; - interrupts-extended = <&tlmm 67 IRQ_TYPE_LEVEL_LOW>; - - vdd-supply = <&vreg_misc_3p3>; - vddl-supply = <&vreg_l12b_1p2>; - - wakeup-source; - }; - - /* GXTP5100 */ - touchpad@5d { - compatible = "hid-over-i2c"; - reg = <0x5d>; - - hid-descr-addr = <0x1>; - interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; - - vdd-supply = <&vreg_misc_3p3>; - vddl-supply = <&vreg_l12b_1p2>; - - wakeup-source; - }; -}; - -&i2c1 { - clock-frequency = <400000>; - - status = "okay"; - - /* 3d PS883x */ -}; - -&i2c3 { - clock-frequency = <400000>; - - status = "okay"; - - /* 1st PS883x */ -}; - -&i2c4 { - clock-frequency = <400000>; - - status = "okay"; - - /* unknown but active on Windows */ -}; - -&i2c5 { - clock-frequency = <400000>; - - status = "okay"; - - eusb5_repeater: redriver@43 { - compatible = "nxp,ptn3222"; - reg = <0x43>; - #phy-cells = <0>; - - vdd3v3-supply = <&vreg_l13b_3p0>; - vdd1v8-supply = <&vreg_l4b_1p8>; - - reset-gpios = <&tlmm 7 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&eusb5_reset_n>; - pinctrl-names = "default"; - }; - - eusb3_repeater: redriver@47 { - compatible = "nxp,ptn3222"; - reg = <0x47>; - #phy-cells = <0>; - - vdd3v3-supply = <&vreg_l13b_3p0>; - vdd1v8-supply = <&vreg_l4b_1p8>; - - reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&eusb3_reset_n>; - pinctrl-names = "default"; - }; - - eusb9_repeater: redriver@4b { - compatible = "nxp,ptn3222"; - reg = <0x4b>; - #phy-cells = <0>; - - vdd3v3-supply = <&vreg_l13b_3p0>; - vdd1v8-supply = <&vreg_l4b_1p8>; - - reset-gpios = <&tlmm 9 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&eusb9_reset_n>; - pinctrl-names = "default"; - }; - - eusb6_repeater: redriver@4f { - compatible = "nxp,ptn3222"; - reg = <0x4f>; - #phy-cells = <0>; - - vdd3v3-supply = <&vreg_l13b_3p0>; - vdd1v8-supply = <&vreg_l4b_1p8>; - - reset-gpios = <&tlmm 184 GPIO_ACTIVE_LOW>; - - pinctrl-0 = <&eusb6_reset_n>; - pinctrl-names = "default"; - }; -}; - -&i2c7 { - clock-frequency = <400000>; - - status = "okay"; - - /* 2nd PS883x */ -}; - -&i2c8 { - clock-frequency = <400000>; - - status = "okay"; - - /* unknown but active on Windows */ -}; - -//&iris { -// firmware-name = "qcom/x1p42100/LENOVO/83HL/qcvss8380.mbn"; -// status = "okay"; -//}; - -&lpass_tlmm { - spkr_01_sd_n_active: spkr-01-sd-n-active-state { - pins = "gpio12"; - function = "gpio"; - drive-strength = <16>; - bias-disable; - output-low; - }; -}; - -&lpass_vamacro { - pinctrl-0 = <&dmic01_default>; - pinctrl-names = "default"; - - vdd-micb-supply = <&vreg_l1b_1p8>; - qcom,dmic-sample-rate = <2400000>; -}; - -&mdss { - status = "okay"; -}; - -&mdss_dp0 { - sound-name-prefix = "DisplayPort0"; - - status = "okay"; -}; - -&mdss_dp0_out { - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; -}; - -&mdss_dp1 { - sound-name-prefix = "DisplayPort1"; - - status = "okay"; -}; - -&mdss_dp1_out { - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; -}; - -&mdss_dp2 { - sound-name-prefix = "DisplayPort2"; - - status = "okay"; -}; - -&mdss_dp2_out { - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; -}; - -&mdss_dp3 { - /delete-property/ #sound-dai-cells; - - pinctrl-0 = <&edp0_hpd_default>; - pinctrl-names = "default"; - - status = "okay"; - - aux-bus { - panel { - compatible = "edp-panel"; - - power-supply = <&vreg_edp_3p3>; - - port { - edp_panel_in: endpoint { - remote-endpoint = <&mdss_dp3_out>; - }; - }; - }; - }; -}; - -&mdss_dp3_out { - data-lanes = <0 1 2 3>; - link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; - - remote-endpoint = <&edp_panel_in>; -}; - -&mdss_dp3_phy { - vdda-phy-supply = <&vreg_l3j_0p8>; - vdda-pll-supply = <&vreg_l2j_1p2>; - - status = "okay"; -}; - -&pcie3 { - pinctrl-0 = <&pcie3_default>; - pinctrl-names = "default"; - - max-link-speed = <1>; - status = "okay"; -}; - -&pcie3_phy { - vdda-phy-supply = <&vreg_l3c_0p8>; - vdda-pll-supply = <&vreg_l3e_1p2>; - vdda-qref-supply = <&vreg_l3j_0p8>; - - status = "okay"; -}; - -&pcie3_port0 { - perst-gpios = <&tlmm 143 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 145 GPIO_ACTIVE_LOW>; -}; - -&pcie4 { - pinctrl-0 = <&pcie4_default>; - pinctrl-names = "default"; - - status = "okay"; -}; - -&pcie4_phy { - vdda-phy-supply = <&vreg_l3i_0p8>; - vdda-pll-supply = <&vreg_l3e_1p2>; - - status = "okay"; -}; - -&pcie4_port0 { - reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; - - wifi@0 { - compatible = "pci17cb,1107"; - reg = <0x10000 0x0 0x0 0x0 0x0>; - - vddaon-supply = <&vreg_pmu_aon_0p59>; - vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; - vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; - vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; - vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; - vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; - vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; - vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; - vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; - }; -}; - -&pcie6a { - vddpe-3v3-supply = <&vreg_nvme>; - - pinctrl-0 = <&pcie6a_default>; - pinctrl-names = "default"; - - status = "okay"; -}; - -&pcie6a_phy { - vdda-phy-supply = <&vreg_l1d_0p8>; - vdda-pll-supply = <&vreg_l2j_1p2>; - - status = "okay"; -}; - -&pcie6a_port0 { - reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; - wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; -}; - -&pm8550ve_8_gpios { - misc_3p3_reg_en: misc-3p3-reg-en-state { - pins = "gpio6"; - function = "normal"; - bias-disable; - drive-push-pull; - input-disable; - output-enable; - power-source = <1>; /* 1.8 V */ - qcom,drive-strength = ; - }; -}; - -&qupv3_0 { - status = "okay"; -}; - -&qupv3_1 { - status = "okay"; -}; - -&qupv3_2 { - status = "okay"; -}; - -&gpi_dma0 { - status = "okay"; -}; - -&gpi_dma1 { - status = "okay"; -}; - -&gpi_dma2 { - status = "okay"; -}; - -&remoteproc_adsp { - firmware-name = "qcom/x1p42100/LENOVO/83HL/qcadsp8380.mbn", - "qcom/x1p42100/LENOVO/83HL/adsp_dtbs.elf"; - - status = "okay"; -}; - -&remoteproc_cdsp { - firmware-name = "qcom/x1p42100/LENOVO/83HL/qccdsp8380.mbn", - "qcom/x1p42100/LENOVO/83HL/cdsp_dtbs.elf"; - - status = "okay"; -}; - -&smb2360_0 { - status = "okay"; -}; - -&smb2360_0_eusb2_repeater { - vdd18-supply = <&vreg_l3d_1p8>; - vdd3-supply = <&vreg_l2b_3p0>; -}; - -&smb2360_1 { - status = "okay"; -}; - -&smb2360_1_eusb2_repeater { - vdd18-supply = <&vreg_l3d_1p8>; - vdd3-supply = <&vreg_l14b_3p0>; -}; - -&spi10 { - status = "disabled"; - - /* Unknown device */ -}; - -&swr0 { - status = "okay"; - - pinctrl-0 = <&wsa_swr_active>, <&spkr_01_sd_n_active>; - pinctrl-names = "default"; - - /* WSA8845, Left Speaker */ - left_spkr: speaker@0,0 { - compatible = "sdw20217020400"; - reg = <0 0>; - reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; - #sound-dai-cells = <0>; - sound-name-prefix = "SpkrLeft"; - vdd-1p8-supply = <&vreg_l15b_1p8>; - vdd-io-supply = <&vreg_l12b_1p2>; - qcom,port-mapping = <1 2 3 7 10 13>; - }; - - /* WSA8845, Right Speaker */ - right_spkr: speaker@0,1 { - compatible = "sdw20217020400"; - reg = <0 1>; - reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; - #sound-dai-cells = <0>; - sound-name-prefix = "SpkrRight"; - vdd-1p8-supply = <&vreg_l15b_1p8>; - vdd-io-supply = <&vreg_l12b_1p2>; - qcom,port-mapping = <4 5 6 7 11 13>; - }; -}; - -&swr1 { - status = "okay"; - - /* WCD9385 RX */ - wcd_rx: codec@0,4 { - compatible = "sdw20217010d00"; - reg = <0 4>; - qcom,rx-port-mapping = <1 2 3 4 5>; - }; -}; - -&swr2 { - status = "okay"; - - /* WCD9385 TX */ - wcd_tx: codec@0,3 { - compatible = "sdw20217010d00"; - reg = <0 3>; - qcom,tx-port-mapping = <2 2 3 4>; - }; -}; - -&tlmm { - gpio-reserved-ranges = <34 2>, /* Unused */ - <44 4>, - <72 2>; /* Secure EC I2C connection (?) */ - - eusb3_reset_n: eusb3-reset-n-state { - pins = "gpio6"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - output-low; - }; - - eusb5_reset_n: eusb5-reset-n-state { - pins = "gpio7"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - output-low; - }; - - eusb6_reset_n: eusb6-reset-n-state { - pins = "gpio184"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - output-low; - }; - - eusb9_reset_n: eusb9-reset-n-state { - pins = "gpio9"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - output-low; - }; - - edp_reg_en: edp-reg-en-state { - pins = "gpio70"; - function = "gpio"; - drive-strength = <16>; - bias-disable; - }; - - hall_int_n_default: hall-int-n-state { - pins = "gpio92"; - function = "gpio"; - bias-disable; - }; - - hdmi_hpd_default: hdmi-hpd-default-state { - pins = "gpio126"; - function = "usb2_dp"; - bias-disable; - }; - - kybd_default: kybd-default-state { - pins = "gpio67"; - function = "gpio"; - bias-disable; - }; - - nvme_reg_en: nvme-reg-en-state { - pins = "gpio18"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - - pcie3_default: pcie3-default-state { - clkreq-n-pins { - pins = "gpio144"; - function = "pcie3_clk"; - drive-strength = <2>; - bias-pull-up; - }; - - perst-n-pins { - pins = "gpio143"; - function = "gpio"; - drive-strength = <2>; - bias-pull-down; - }; - - wake-n-pins { - pins = "gpio145"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - }; - - pcie4_default: pcie4-default-state { - clkreq-n-pins { - pins = "gpio147"; - function = "pcie4_clk"; - drive-strength = <2>; - bias-pull-up; - }; - - perst-n-pins { - pins = "gpio146"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - - wake-n-pins { - pins = "gpio148"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - }; - - pcie6a_default: pcie6a-default-state { - clkreq-n-pins { - pins = "gpio153"; - function = "pcie6a_clk"; - drive-strength = <2>; - bias-pull-up; - }; - - perst-n-pins { - pins = "gpio152"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - - wake-n-pins { - pins = "gpio154"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - }; - - sdc2_card_det_n: sdc2-card-det-state { - pins = "gpio71"; - function = "gpio"; - drive-strength = <2>; - bias-pull-up; - }; - - tpad_default: tpad-default-state { - pins = "gpio3"; - function = "gpio"; - bias-pull-up; - }; - - usb_1_ss0_sbu_default: usb-1-ss0-sbu-state { - oe-n-pins { - pins = "gpio168"; - function = "gpio"; - bias-pull-up; - drive-strength = <2>; - }; - - sel-pins { - pins = "gpio167"; - function = "gpio"; - bias-pull-up; - drive-strength = <2>; - }; - - }; - - usb_1_ss1_sbu_default: usb-1-ss1-sbu-state { - oe-n-pins { - pins = "gpio179"; - function = "gpio"; - bias-pull-up; - drive-strength = <2>; - }; - - sel-pins { - pins = "gpio178"; - function = "gpio"; - bias-pull-up; - drive-strength = <2>; - }; - }; - - wcd_default: wcd-reset-n-active-state { - pins = "gpio191"; - function = "gpio"; - drive-strength = <16>; - bias-disable; - output-low; - }; - - wcn_sw_en: wcn-sw-en-state { - pins = "gpio214"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; - - wcn_wlan_bt_en: wcn-wlan-bt-en-state { - pins = "gpio116", "gpio117"; - function = "gpio"; - drive-strength = <2>; - bias-disable; - }; -}; - -&uart14 { - status = "okay"; - - bluetooth { - compatible = "qcom,wcn7850-bt"; - max-speed = <3200000>; - - vddaon-supply = <&vreg_pmu_aon_0p59>; - vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; - vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; - vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; - vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; - vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; - vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; - }; -}; - -&usb_1_ss0_hsphy { - vdd-supply = <&vreg_l3j_0p8>; - vdda12-supply = <&vreg_l2j_1p2>; - - phys = <&smb2360_0_eusb2_repeater>; - - status = "okay"; -}; - -&usb_1_ss0_qmpphy { - vdda-phy-supply = <&vreg_l2j_1p2>; - vdda-pll-supply = <&vreg_l1j_0p8>; - - status = "okay"; -}; - -&usb_1_ss0 { - dr_mode = "host"; - - status = "okay"; -}; - -&usb_1_ss0_dwc3_hs { - remote-endpoint = <&pmic_glink_ss0_hs_in>; -}; - -&usb_1_ss0_qmpphy_out { - remote-endpoint = <&pmic_glink_ss0_ss_in>; -}; - -&usb_1_ss1_hsphy { - vdd-supply = <&vreg_l3j_0p8>; - vdda12-supply = <&vreg_l2j_1p2>; - - phys = <&smb2360_1_eusb2_repeater>; - - status = "okay"; -}; - -&usb_1_ss1_qmpphy { - vdda-phy-supply = <&vreg_l2j_1p2>; - vdda-pll-supply = <&vreg_l2d_0p9>; - - status = "okay"; -}; - -&usb_1_ss1 { - dr_mode = "host"; - - status = "okay"; -}; - -&usb_1_ss1_dwc3_hs { - remote-endpoint = <&pmic_glink_ss1_hs_in>; -}; - -&usb_1_ss1_qmpphy_out { - remote-endpoint = <&pmic_glink_ss1_ss_in>; -}; - -&usb_1_ss2 { - dr_mode = "host"; - maximum-speed = "high-speed"; - phys = <&usb_1_ss2_hsphy>; - phy-names = "usb2-phy"; - qcom,select-utmi-as-pipe-clk; - status = "okay"; - /delete-property/ port@1; -}; - -&usb_1_ss2_hsphy { - vdd-supply = <&vreg_l3j_0p8>; - vdda12-supply = <&vreg_l2j_1p2>; - - phys = <&eusb9_repeater>; - - status = "okay"; -}; - -&usb_1_ss2_qmpphy { - vdda-phy-supply = <&vreg_l2j_1p2>; - vdda-pll-supply = <&vreg_l2d_0p9>; - - /delete-property/ mode-switch; - /delete-property/ orientation-switch; - - status = "okay"; - - ports { - port@0 { - #address-cells = <1>; - #size-cells = <0>; - - /delete-node/ endpoint; - - usb_1_ss2_qmpphy_out_dp: endpoint@0 { - reg = <0>; - - data-lanes = <3 2 1 0>; - remote-endpoint = <&hdmi_bridge_dp_in>; - }; - }; - }; -}; - -&usb_2 { - dr_mode = "host"; - - status = "okay"; -}; - -&usb_2_hsphy { - vdd-supply = <&vreg_l2e_0p8>; - vdda12-supply = <&vreg_l3e_1p2>; - - phys = <&eusb5_repeater>; - - status = "okay"; -}; - -&usb_mp { - status = "okay"; -}; - -&usb_mp_hsphy0 { - vdd-supply = <&vreg_l2e_0p8>; - vdda12-supply = <&vreg_l3e_1p2>; - - phys = <&eusb3_repeater>; - - status = "okay"; -}; - -&usb_mp_qmpphy0 { - vdda-phy-supply = <&vreg_l3e_1p2>; - vdda-pll-supply = <&vreg_l3c_0p8>; - - status = "okay"; -}; - -&usb_mp_hsphy1 { - vdd-supply = <&vreg_l2e_0p8>; - vdda12-supply = <&vreg_l3e_1p2>; - - phys = <&eusb6_repeater>; - - status = "okay"; -}; - -&usb_mp_qmpphy1 { - vdda-phy-supply = <&vreg_l3e_1p2>; - vdda-pll-supply = <&vreg_l3c_0p8>; - - status = "okay"; + compatible = "lenovo,ideapad-slim5x-oled", "lenovo,ideapad-5", "lenovo,thinkpad-t14s", "qcom,x1p42100"; }; diff --git a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-slim5x.dtsi b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-slim5x.dtsi new file mode 100644 index 00000000000000..e3c6f4db4532ab --- /dev/null +++ b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-ideapad-slim5x.dtsi @@ -0,0 +1,1688 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2024, Linaro Limited + * Copyright (c) 2026 Jens Glathe + */ + +#include +#include +#include +#include +#include + +#include "purwa.dtsi" +#include "hamoa-pmics.dtsi" + +/delete-node/ &pmc8380_6; +/delete-node/ &pmc8380_6_thermal; + +/ { + chassis-type = "laptop"; + + aliases { + serial0 = &uart21; + serial1 = &uart14; + }; + + wcd938x: audio-codec { + compatible = "qcom,wcd9385-codec"; + + pinctrl-0 = <&wcd_default>; + pinctrl-names = "default"; + + qcom,micbias1-microvolt = <1800000>; + qcom,micbias2-microvolt = <1800000>; + qcom,micbias3-microvolt = <1800000>; + qcom,micbias4-microvolt = <1800000>; + qcom,mbhc-buttons-vthreshold-microvolt = <75000 150000 237000 500000 500000 500000 500000 500000>; + qcom,mbhc-headset-vthreshold-microvolt = <1700000>; + qcom,mbhc-headphone-vthreshold-microvolt = <50000>; + qcom,rx-device = <&wcd_rx>; + qcom,tx-device = <&wcd_tx>; + + reset-gpios = <&tlmm 191 GPIO_ACTIVE_LOW>; + + vdd-buck-supply = <&vreg_l15b_1p8>; + vdd-rxtx-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l15b_1p8>; + vdd-mic-bias-supply = <&vreg_bob1>; + + #sound-dai-cells = <1>; + }; + + hdmi-bridge { + compatible = "parade,ps185hdm"; + + pinctrl-0 = <&hdmi_hpd_default>; + pinctrl-names = "default"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + hdmi_bridge_dp_in: endpoint { + remote-endpoint = <&usb_1_ss2_qmpphy_out_dp>; + }; + }; + + port@1 { + reg = <1>; + + hdmi_bridge_tmds_out: endpoint { + remote-endpoint = <&hdmi_con>; + }; + }; + }; + }; + + hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con: endpoint { + remote-endpoint = <&hdmi_bridge_tmds_out>; + }; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-0 = <&hall_int_n_default>; + pinctrl-names = "default"; + + switch-lid { + gpios = <&tlmm 92 GPIO_ACTIVE_LOW>; + linux,input-type = ; + linux,code = ; + wakeup-source; + wakeup-event-action = ; + }; + }; + + pmic-glink { + compatible = "qcom,x1e80100-pmic-glink", + "qcom,sm8550-pmic-glink", + "qcom,pmic-glink"; + #address-cells = <1>; + #size-cells = <0>; + orientation-gpios = <&tlmm 121 GPIO_ACTIVE_HIGH>, + <&tlmm 123 GPIO_ACTIVE_HIGH>; + + /* Display-adjacent port */ + connector@0 { + compatible = "usb-c-connector"; + reg = <0>; + power-role = "dual"; + data-role = "host"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pmic_glink_ss0_hs_in: endpoint { + remote-endpoint = <&usb_1_ss0_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + pmic_glink_ss0_ss_in: endpoint { + remote-endpoint = <&usb_1_ss0_qmpphy_out>; + }; + }; + + port@2 { + reg = <2>; + + pmic_glink_ss0_sbu: endpoint { + remote-endpoint = <&usb_1_ss0_sbu_mux>; + }; + }; + }; + }; + + /* User-adjacent port */ + connector@1 { + compatible = "usb-c-connector"; + reg = <1>; + power-role = "dual"; + data-role = "host"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + pmic_glink_ss1_hs_in: endpoint { + remote-endpoint = <&usb_1_ss1_dwc3_hs>; + }; + }; + + port@1 { + reg = <1>; + + pmic_glink_ss1_ss_in: endpoint { + remote-endpoint = <&usb_1_ss1_qmpphy_out>; + }; + }; + + port@2 { + reg = <2>; + + pmic_glink_ss1_sbu: endpoint { + remote-endpoint = <&usb_1_ss1_sbu_mux>; + }; + }; + }; + }; + }; + + sound { + compatible = "qcom,x1e80100-sndcard"; + model = "X1E80100-LENOVO-Ideapad-5"; + audio-routing = "SpkrLeft IN", "WSA WSA_SPK1 OUT", + "SpkrRight IN", "WSA WSA_SPK2 OUT", + "IN1_HPHL", "HPHL_OUT", + "IN2_HPHR", "HPHR_OUT", + "AMIC2", "MIC BIAS2", + "VA DMIC0", "MIC BIAS1", + "VA DMIC1", "MIC BIAS1", + "VA DMIC0", "VA MIC BIAS1", + "VA DMIC1", "VA MIC BIAS1", + "TX SWR_INPUT1", "ADC2_OUTPUT"; + + displayport-0-dai-link { + link-name = "DisplayPort0 Playback"; + + codec { + sound-dai = <&mdss_dp0>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + displayport-1-dai-link { + link-name = "DisplayPort1 Playback"; + + codec { + sound-dai = <&mdss_dp1>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_1>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + displayport-2-dai-link { + link-name = "DisplayPort2 Playback"; + + codec { + sound-dai = <&mdss_dp2>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_2>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + wcd-playback-dai-link { + link-name = "WCD Playback"; + + codec { + sound-dai = <&wcd938x 0>, <&swr1 0>, <&lpass_rxmacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai RX_CODEC_DMA_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + wcd-capture-dai-link { + link-name = "WCD Capture"; + + codec { + sound-dai = <&wcd938x 1>, <&swr2 1>, <&lpass_txmacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai TX_CODEC_DMA_TX_3>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + wsa-dai-link { + link-name = "WSA Playback"; + + codec { + sound-dai = <&left_spkr>, <&right_spkr>, <&swr0 0>, <&lpass_wsamacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai WSA_CODEC_DMA_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + va-dai-link { + link-name = "VA Capture"; + + codec { + sound-dai = <&lpass_vamacro 0>; + }; + + cpu { + sound-dai = <&q6apmbedai VA_CODEC_DMA_TX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + }; + + usb-1-ss0-sbu-mux { + compatible = "onnn,fsusb42", "gpio-sbu-mux"; + + enable-gpios = <&tlmm 168 GPIO_ACTIVE_LOW>; + select-gpios = <&tlmm 167 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&usb_1_ss0_sbu_default>; + pinctrl-names = "default"; + + mode-switch; + orientation-switch; + + port { + usb_1_ss0_sbu_mux: endpoint { + remote-endpoint = <&pmic_glink_ss0_sbu>; + }; + }; + }; + + usb-1-ss1-sbu-mux { + compatible = "onnn,fsusb42", "gpio-sbu-mux"; + + enable-gpios = <&tlmm 179 GPIO_ACTIVE_LOW>; + select-gpios = <&tlmm 178 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&usb_1_ss1_sbu_default>; + pinctrl-names = "default"; + + mode-switch; + orientation-switch; + + port { + usb_1_ss1_sbu_mux: endpoint { + remote-endpoint = <&pmic_glink_ss1_sbu>; + }; + }; + }; + + vreg_edp_3p3: regulator-edp-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_EDP_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 70 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&edp_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + regulator-always-on; + }; + + vreg_misc_3p3: regulator-misc-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_MISC_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&pm8550ve_8_gpios 6 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&misc_3p3_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + regulator-always-on; + }; + + vreg_nvme: regulator-nvme { + compatible = "regulator-fixed"; + + regulator-name = "VREG_NVME_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 18 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&nvme_reg_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vreg_wcn_0p95: regulator-wcn-0p95 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_0P95"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <950000>; + + vin-supply = <&vreg_wcn_3p3>; + }; + + vreg_wcn_1p9: regulator-wcn-1p9 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_1P9"; + regulator-min-microvolt = <1900000>; + regulator-max-microvolt = <1900000>; + + vin-supply = <&vreg_wcn_3p3>; + }; + + vreg_wcn_3p3: regulator-wcn-3p3 { + compatible = "regulator-fixed"; + + regulator-name = "VREG_WCN_3P3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + + gpio = <&tlmm 214 GPIO_ACTIVE_HIGH>; + enable-active-high; + + pinctrl-0 = <&wcn_sw_en>; + pinctrl-names = "default"; + + regulator-boot-on; + }; + + vph_pwr: regulator-vph-pwr { + compatible = "regulator-fixed"; + + regulator-name = "vph_pwr"; + regulator-min-microvolt = <3700000>; + regulator-max-microvolt = <3700000>; + + regulator-always-on; + regulator-boot-on; + }; + + wcn7850-pmu { + compatible = "qcom,wcn7850-pmu"; + + vdd-supply = <&vreg_wcn_0p95>; + vddio-supply = <&vreg_l15b_1p8>; + vddaon-supply = <&vreg_wcn_0p95>; + vdddig-supply = <&vreg_wcn_0p95>; + vddrfa1p2-supply = <&vreg_wcn_1p9>; + vddrfa1p8-supply = <&vreg_wcn_1p9>; + + wlan-enable-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>; + bt-enable-gpios = <&tlmm 116 GPIO_ACTIVE_HIGH>; + + pinctrl-0 = <&wcn_wlan_bt_en>; + pinctrl-names = "default"; + + regulators { + vreg_pmu_rfa_cmn: ldo0 { + regulator-name = "vreg_pmu_rfa_cmn"; + }; + + vreg_pmu_aon_0p59: ldo1 { + regulator-name = "vreg_pmu_aon_0p59"; + }; + + vreg_pmu_wlcx_0p8: ldo2 { + regulator-name = "vreg_pmu_wlcx_0p8"; + }; + + vreg_pmu_wlmx_0p85: ldo3 { + regulator-name = "vreg_pmu_wlmx_0p85"; + }; + + vreg_pmu_btcmx_0p85: ldo4 { + regulator-name = "vreg_pmu_btcmx_0p85"; + }; + + vreg_pmu_rfa_0p8: ldo5 { + regulator-name = "vreg_pmu_rfa_0p8"; + }; + + vreg_pmu_rfa_1p2: ldo6 { + regulator-name = "vreg_pmu_rfa_1p2"; + }; + + vreg_pmu_rfa_1p8: ldo7 { + regulator-name = "vreg_pmu_rfa_1p8"; + }; + + vreg_pmu_pcie_0p9: ldo8 { + regulator-name = "vreg_pmu_pcie_0p9"; + }; + + vreg_pmu_pcie_1p8: ldo9 { + regulator-name = "vreg_pmu_pcie_1p8"; + }; + }; + }; +}; + +&apps_rsc { + regulators-0 { + compatible = "qcom,pm8550-rpmh-regulators"; + qcom,pmic-id = "b"; + + vdd-bob1-supply = <&vph_pwr>; + vdd-bob2-supply = <&vph_pwr>; + vdd-l1-l4-l10-supply = <&vreg_s4c_1p8>; + vdd-l2-l13-l14-supply = <&vreg_bob1>; + vdd-l5-l16-supply = <&vreg_bob1>; + vdd-l6-l7-supply = <&vreg_bob2>; + vdd-l8-l9-supply = <&vreg_bob1>; + vdd-l12-supply = <&vreg_s5j_1p2>; + vdd-l15-supply = <&vreg_s4c_1p8>; + vdd-l17-supply = <&vreg_bob2>; + + vreg_bob1: bob1 { + regulator-name = "vreg_bob1"; + regulator-min-microvolt = <3008000>; + regulator-max-microvolt = <3960000>; + regulator-initial-mode = ; + }; + + vreg_bob2: bob2 { + regulator-name = "vreg_bob2"; + regulator-min-microvolt = <2504000>; + regulator-max-microvolt = <3008000>; + regulator-initial-mode = ; + }; + + vreg_l1b_1p8: ldo1 { + regulator-name = "vreg_l1b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l2b_3p0: ldo2 { + regulator-name = "vreg_l2b_3p0"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + + vreg_l4b_1p8: ldo4 { + regulator-name = "vreg_l4b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vreg_l6b_1p8: ldo6 { + regulator-name = "vreg_l6b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2960000>; + regulator-initial-mode = ; + }; + + vreg_l8b_3p0: ldo8 { + regulator-name = "vreg_l8b_3p0"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + + vreg_l9b_2p9: ldo9 { + regulator-name = "vreg_l9b_2p9"; + regulator-min-microvolt = <2960000>; + regulator-max-microvolt = <2960000>; + regulator-initial-mode = ; + }; + + vreg_l10b_1p8: ldo10 { + regulator-name = "vreg_l10b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + + vreg_l12b_1p2: ldo12 { + regulator-name = "vreg_l12b_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + regulator-always-on; + }; + + vreg_l13b_3p0: ldo13 { + regulator-name = "vreg_l13b_3p0"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + + regulator-state-mem { + regulator-on-in-suspend; + }; + }; + + vreg_l14b_3p0: ldo14 { + regulator-name = "vreg_l14b_3p0"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = ; + }; + + vreg_l15b_1p8: ldo15 { + regulator-name = "vreg_l15b_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + regulator-always-on; + }; + + vreg_l17b_2p5: ldo17 { + regulator-name = "vreg_l17b_2p5"; + regulator-min-microvolt = <2504000>; + regulator-max-microvolt = <2504000>; + regulator-initial-mode = ; + }; + }; + + regulators-1 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + qcom,pmic-id = "c"; + + vdd-l1-supply = <&vreg_s5j_1p2>; + vdd-l2-supply = <&vreg_s1f_0p7>; + vdd-l3-supply = <&vreg_s1f_0p7>; + vdd-s4-supply = <&vph_pwr>; + + vreg_s4c_1p8: smps4 { + regulator-name = "vreg_s4c_1p8"; + regulator-min-microvolt = <1856000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = ; + }; + + vreg_l1c_1p2: ldo1 { + regulator-name = "vreg_l1c_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + + vreg_l2c_0p8: ldo2 { + regulator-name = "vreg_l2c_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + regulator-initial-mode = ; + }; + + vreg_l3c_0p8: ldo3 { + regulator-name = "vreg_l3c_0p8"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + }; + }; + + regulators-2 { + compatible = "qcom,pmc8380-rpmh-regulators"; + qcom,pmic-id = "d"; + + vdd-l1-supply = <&vreg_s1f_0p7>; + vdd-l2-supply = <&vreg_s1f_0p7>; + vdd-l3-supply = <&vreg_s4c_1p8>; + vdd-s1-supply = <&vph_pwr>; + + vreg_l1d_0p8: ldo1 { + regulator-name = "vreg_l1d_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + regulator-initial-mode = ; + }; + + vreg_l2d_0p9: ldo2 { + regulator-name = "vreg_l2d_0p9"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + }; + + vreg_l3d_1p8: ldo3 { + regulator-name = "vreg_l3d_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + }; + }; + + regulators-3 { + compatible = "qcom,pmc8380-rpmh-regulators"; + qcom,pmic-id = "e"; + + vdd-l2-supply = <&vreg_s1f_0p7>; + vdd-l3-supply = <&vreg_s5j_1p2>; + + vreg_l2e_0p8: ldo2 { + regulator-name = "vreg_l2e_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + regulator-initial-mode = ; + }; + + vreg_l3e_1p2: ldo3 { + regulator-name = "vreg_l3e_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + }; + + regulators-4 { + compatible = "qcom,pmc8380-rpmh-regulators"; + qcom,pmic-id = "f"; + + vdd-l1-supply = <&vreg_s5j_1p2>; + vdd-l2-supply = <&vreg_s5j_1p2>; + vdd-l3-supply = <&vreg_s5j_1p2>; + vdd-s1-supply = <&vph_pwr>; + + vreg_s1f_0p7: smps1 { + regulator-name = "vreg_s1f_0p7"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1100000>; + regulator-initial-mode = ; + }; + }; + + regulators-6 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + qcom,pmic-id = "i"; + + vdd-l1-supply = <&vreg_s4c_1p8>; + vdd-l2-supply = <&vreg_s5j_1p2>; + vdd-l3-supply = <&vreg_s1f_0p7>; + vdd-s1-supply = <&vph_pwr>; + vdd-s2-supply = <&vph_pwr>; + + vreg_l1i_1p8: ldo1 { + regulator-name = "vreg_l1i_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = ; + regulator-always-on; + }; + + vreg_l2i_1p2: ldo2 { + regulator-name = "vreg_l2i_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = ; + }; + + vreg_l3i_0p8: ldo3 { + regulator-name = "vreg_l3i_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + regulator-initial-mode = ; + }; + + vreg_s1i_0p9: smps1 { + regulator-name = "vreg_s1i_0p9"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <920000>; + regulator-initial-mode = ; + }; + + vreg_s2i_1p0: smps2 { + regulator-name = "vreg_s2i_1p0"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1100000>; + regulator-initial-mode = ; + }; + }; + + regulators-7 { + compatible = "qcom,pm8550ve-rpmh-regulators"; + qcom,pmic-id = "j"; + + vdd-l1-supply = <&vreg_s1f_0p7>; + vdd-l2-supply = <&vreg_s5j_1p2>; + vdd-l3-supply = <&vreg_s1f_0p7>; + vdd-s5-supply = <&vph_pwr>; + + vreg_s5j_1p2: smps5 { + regulator-name = "vreg_s5j_1p2"; + regulator-min-microvolt = <1256000>; + regulator-max-microvolt = <1304000>; + regulator-initial-mode = ; + }; + + vreg_l1j_0p8: ldo1 { + regulator-name = "vreg_l1j_0p8"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = ; + }; + + vreg_l2j_1p2: ldo2 { + regulator-name = "vreg_l2j_1p2"; + regulator-min-microvolt = <1256000>; + regulator-max-microvolt = <1256000>; + regulator-initial-mode = ; + }; + + vreg_l3j_0p8: ldo3 { + regulator-name = "vreg_l3j_0p8"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + regulator-initial-mode = ; + }; + }; +}; + +&gpu { + status = "okay"; +}; + +&gpu_zap_shader { + firmware-name = "qcom/x1p42100/LENOVO/83HL/qcdxkmsucpurwa.mbn"; +}; + +&i2c0 { + clock-frequency = <400000>; + + pinctrl-0 = <&qup_i2c0_data_clk>, <&tpad_default>, <&kybd_default>; + pinctrl-names = "default"; + status = "okay"; + + /* ELAN06FA */ + touchpad@15 { + compatible = "hid-over-i2c"; + reg = <0x15>; + + hid-descr-addr = <0x1>; + interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; + + vdd-supply = <&vreg_misc_3p3>; + vddl-supply = <&vreg_l12b_1p2>; + + wakeup-source; + }; + + /* CIRQ1080 or SYNA2BA6 */ + touchpad@2c { + compatible = "hid-over-i2c"; + reg = <0x2c>; + + hid-descr-addr = <0x20>; + interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; + + vdd-supply = <&vreg_misc_3p3>; + vddl-supply = <&vreg_l12b_1p2>; + + wakeup-source; + }; + + /* FTCS0038 */ + touchpad@38 { + compatible = "hid-over-i2c"; + reg = <0x38>; + + hid-descr-addr = <0x1>; + interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; + + vdd-supply = <&vreg_misc_3p3>; + vddl-supply = <&vreg_l12b_1p2>; + + wakeup-source; + }; + + keyboard@3a { + compatible = "hid-over-i2c"; + reg = <0x3a>; + + hid-descr-addr = <0x1>; + interrupts-extended = <&tlmm 67 IRQ_TYPE_LEVEL_LOW>; + + vdd-supply = <&vreg_misc_3p3>; + vddl-supply = <&vreg_l12b_1p2>; + + wakeup-source; + }; + + /* GXTP5100 */ + touchpad@5d { + compatible = "hid-over-i2c"; + reg = <0x5d>; + + hid-descr-addr = <0x1>; + interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>; + + vdd-supply = <&vreg_misc_3p3>; + vddl-supply = <&vreg_l12b_1p2>; + + wakeup-source; + }; +}; + +&i2c1 { + clock-frequency = <400000>; + + status = "okay"; + + /* 3d PS883x */ +}; + +&i2c3 { + clock-frequency = <400000>; + + status = "okay"; + + /* 1st PS883x */ +}; + +&i2c4 { + clock-frequency = <400000>; + + status = "okay"; + + /* unknown but active on Windows */ +}; + +&i2c5 { + clock-frequency = <400000>; + + status = "okay"; + + eusb5_repeater: redriver@43 { + compatible = "nxp,ptn3222"; + reg = <0x43>; + #phy-cells = <0>; + + vdd3v3-supply = <&vreg_l13b_3p0>; + vdd1v8-supply = <&vreg_l4b_1p8>; + + reset-gpios = <&tlmm 7 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&eusb5_reset_n>; + pinctrl-names = "default"; + }; + + eusb3_repeater: redriver@47 { + compatible = "nxp,ptn3222"; + reg = <0x47>; + #phy-cells = <0>; + + vdd3v3-supply = <&vreg_l13b_3p0>; + vdd1v8-supply = <&vreg_l4b_1p8>; + + reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&eusb3_reset_n>; + pinctrl-names = "default"; + }; + + eusb9_repeater: redriver@4b { + compatible = "nxp,ptn3222"; + reg = <0x4b>; + #phy-cells = <0>; + + vdd3v3-supply = <&vreg_l13b_3p0>; + vdd1v8-supply = <&vreg_l4b_1p8>; + + reset-gpios = <&tlmm 9 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&eusb9_reset_n>; + pinctrl-names = "default"; + }; + + eusb6_repeater: redriver@4f { + compatible = "nxp,ptn3222"; + reg = <0x4f>; + #phy-cells = <0>; + + vdd3v3-supply = <&vreg_l13b_3p0>; + vdd1v8-supply = <&vreg_l4b_1p8>; + + reset-gpios = <&tlmm 184 GPIO_ACTIVE_LOW>; + + pinctrl-0 = <&eusb6_reset_n>; + pinctrl-names = "default"; + }; +}; + +&i2c7 { + clock-frequency = <400000>; + + status = "okay"; + + /* 2nd PS883x */ +}; + +&i2c8 { + clock-frequency = <400000>; + + status = "okay"; + + /* unknown but active on Windows */ +}; + +//&iris { +// firmware-name = "qcom/x1p42100/LENOVO/83HL/qcvss8380.mbn"; +// status = "okay"; +//}; + +&lpass_tlmm { + spkr_01_sd_n_active: spkr-01-sd-n-active-state { + pins = "gpio12"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; +}; + +&lpass_vamacro { + pinctrl-0 = <&dmic01_default>; + pinctrl-names = "default"; + + vdd-micb-supply = <&vreg_l1b_1p8>; + qcom,dmic-sample-rate = <2400000>; +}; + +&mdss { + status = "okay"; +}; + +&mdss_dp0 { + sound-name-prefix = "DisplayPort0"; + + status = "okay"; +}; + +&mdss_dp0_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp1 { + sound-name-prefix = "DisplayPort1"; + + status = "okay"; +}; + +&mdss_dp1_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp2 { + sound-name-prefix = "DisplayPort2"; + + status = "okay"; +}; + +&mdss_dp2_out { + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; +}; + +&mdss_dp3 { + /delete-property/ #sound-dai-cells; + + pinctrl-0 = <&edp0_hpd_default>; + pinctrl-names = "default"; + + status = "okay"; + + aux-bus { + panel:panel { + compatible = "edp-panel"; + power-supply = <&vreg_edp_3p3>; + + port { + edp_panel_in: endpoint { + remote-endpoint = <&mdss_dp3_out>; + }; + }; + }; + }; +}; + +&mdss_dp3_out { + data-lanes = <0 1 2 3>; + link-frequencies = /bits/ 64 <1620000000 2700000000 5400000000 8100000000>; + + remote-endpoint = <&edp_panel_in>; +}; + +&mdss_dp3_phy { + vdda-phy-supply = <&vreg_l3j_0p8>; + vdda-pll-supply = <&vreg_l2j_1p2>; + + status = "okay"; +}; + +&pcie3 { + pinctrl-0 = <&pcie3_default>; + pinctrl-names = "default"; + + max-link-speed = <1>; + status = "okay"; +}; + +&pcie3_phy { + vdda-phy-supply = <&vreg_l3c_0p8>; + vdda-pll-supply = <&vreg_l3e_1p2>; + vdda-qref-supply = <&vreg_l3j_0p8>; + + status = "okay"; +}; + +&pcie3_port0 { + reset-gpios = <&tlmm 143 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 145 GPIO_ACTIVE_LOW>; +}; + +&pcie4 { + pinctrl-0 = <&pcie4_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie4_phy { + vdda-phy-supply = <&vreg_l3i_0p8>; + vdda-pll-supply = <&vreg_l3e_1p2>; + + status = "okay"; +}; + +&pcie4_port0 { + reset-gpios = <&tlmm 146 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 148 GPIO_ACTIVE_LOW>; + + wifi@0 { + compatible = "pci17cb,1107"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>; + vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>; + }; +}; + +&pcie6a { + vddpe-3v3-supply = <&vreg_nvme>; + + pinctrl-0 = <&pcie6a_default>; + pinctrl-names = "default"; + + status = "okay"; +}; + +&pcie6a_phy { + vdda-phy-supply = <&vreg_l1d_0p8>; + vdda-pll-supply = <&vreg_l2j_1p2>; + + status = "okay"; +}; + +&pcie6a_port0 { + reset-gpios = <&tlmm 152 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 154 GPIO_ACTIVE_LOW>; +}; + +&pm8550ve_8_gpios { + misc_3p3_reg_en: misc-3p3-reg-en-state { + pins = "gpio6"; + function = "normal"; + bias-disable; + drive-push-pull; + input-disable; + output-enable; + power-source = <1>; /* 1.8 V */ + qcom,drive-strength = ; + }; +}; + +&qupv3_0 { + status = "okay"; +}; + +&qupv3_1 { + status = "okay"; +}; + +&qupv3_2 { + status = "okay"; +}; + +&gpi_dma0 { + status = "okay"; +}; + +&gpi_dma1 { + status = "okay"; +}; + +&gpi_dma2 { + status = "okay"; +}; + +&remoteproc_adsp { + firmware-name = "qcom/x1p42100/LENOVO/83HL/qcadsp8380.mbn", + "qcom/x1p42100/LENOVO/83HL/adsp_dtbs.elf"; + + status = "okay"; +}; + +&remoteproc_cdsp { + firmware-name = "qcom/x1p42100/LENOVO/83HL/qccdsp8380.mbn", + "qcom/x1p42100/LENOVO/83HL/cdsp_dtbs.elf"; + + status = "okay"; +}; + +&smb2360_0 { + status = "okay"; +}; + +&smb2360_0_eusb2_repeater { + vdd18-supply = <&vreg_l3d_1p8>; + vdd3-supply = <&vreg_l2b_3p0>; +}; + +&smb2360_1 { + status = "okay"; +}; + +&smb2360_1_eusb2_repeater { + vdd18-supply = <&vreg_l3d_1p8>; + vdd3-supply = <&vreg_l14b_3p0>; +}; + +&spi10 { + status = "disabled"; + + /* Unknown device */ +}; + +&swr0 { + status = "okay"; + + pinctrl-0 = <&wsa_swr_active>, <&spkr_01_sd_n_active>; + pinctrl-names = "default"; + + /* WSA8845, Left Speaker */ + left_spkr: speaker@0,0 { + compatible = "sdw20217020400"; + reg = <0 0>; + reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "SpkrLeft"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l12b_1p2>; + qcom,port-mapping = <1 2 3 7 10 13>; + }; + + /* WSA8845, Right Speaker */ + right_spkr: speaker@0,1 { + compatible = "sdw20217020400"; + reg = <0 1>; + reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>; + #sound-dai-cells = <0>; + sound-name-prefix = "SpkrRight"; + vdd-1p8-supply = <&vreg_l15b_1p8>; + vdd-io-supply = <&vreg_l12b_1p2>; + qcom,port-mapping = <4 5 6 7 11 13>; + }; +}; + +&swr1 { + status = "okay"; + + /* WCD9385 RX */ + wcd_rx: codec@0,4 { + compatible = "sdw20217010d00"; + reg = <0 4>; + qcom,rx-port-mapping = <1 2 3 4 5>; + }; +}; + +&swr2 { + status = "okay"; + + /* WCD9385 TX */ + wcd_tx: codec@0,3 { + compatible = "sdw20217010d00"; + reg = <0 3>; + qcom,tx-port-mapping = <2 2 3 4>; + }; +}; + +&tlmm { + gpio-reserved-ranges = <34 2>, /* Unused */ + <44 4>, + <72 2>; /* Secure EC I2C connection (?) */ + + eusb3_reset_n: eusb3-reset-n-state { + pins = "gpio6"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-low; + }; + + eusb5_reset_n: eusb5-reset-n-state { + pins = "gpio7"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-low; + }; + + eusb6_reset_n: eusb6-reset-n-state { + pins = "gpio184"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-low; + }; + + eusb9_reset_n: eusb9-reset-n-state { + pins = "gpio9"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + output-low; + }; + + edp_reg_en: edp-reg-en-state { + pins = "gpio70"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + }; + + hall_int_n_default: hall-int-n-state { + pins = "gpio92"; + function = "gpio"; + bias-disable; + }; + + hdmi_hpd_default: hdmi-hpd-default-state { + pins = "gpio126"; + function = "usb2_dp"; + bias-disable; + }; + + kybd_default: kybd-default-state { + pins = "gpio67"; + function = "gpio"; + bias-disable; + }; + + nvme_reg_en: nvme-reg-en-state { + pins = "gpio18"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + pcie3_default: pcie3-default-state { + clkreq-n-pins { + pins = "gpio144"; + function = "pcie3_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio143"; + function = "gpio"; + drive-strength = <2>; + bias-pull-down; + }; + + wake-n-pins { + pins = "gpio145"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie4_default: pcie4-default-state { + clkreq-n-pins { + pins = "gpio147"; + function = "pcie4_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio146"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio148"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie6a_default: pcie6a-default-state { + clkreq-n-pins { + pins = "gpio153"; + function = "pcie6a_clk"; + drive-strength = <2>; + bias-pull-up; + }; + + perst-n-pins { + pins = "gpio152"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wake-n-pins { + pins = "gpio154"; + function = "gpio"; + drive-strength = <2>; + bias-pull-up; + }; + }; + + tpad_default: tpad-default-state { + pins = "gpio3"; + function = "gpio"; + bias-pull-up; + }; + + usb_1_ss0_sbu_default: usb-1-ss0-sbu-state { + oe-n-pins { + pins = "gpio168"; + function = "gpio"; + bias-pull-up; + drive-strength = <2>; + }; + + sel-pins { + pins = "gpio167"; + function = "gpio"; + bias-pull-up; + drive-strength = <2>; + }; + + }; + + usb_1_ss1_sbu_default: usb-1-ss1-sbu-state { + oe-n-pins { + pins = "gpio179"; + function = "gpio"; + bias-pull-up; + drive-strength = <2>; + }; + + sel-pins { + pins = "gpio178"; + function = "gpio"; + bias-pull-up; + drive-strength = <2>; + }; + }; + + wcd_default: wcd-reset-n-active-state { + pins = "gpio191"; + function = "gpio"; + drive-strength = <16>; + bias-disable; + output-low; + }; + + wcn_sw_en: wcn-sw-en-state { + pins = "gpio214"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + + wcn_wlan_bt_en: wcn-wlan-bt-en-state { + pins = "gpio116", "gpio117"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; +}; + +&uart14 { + status = "okay"; + + bluetooth { + compatible = "qcom,wcn7850-bt"; + max-speed = <3200000>; + + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + }; +}; + +&usb_1_ss0_hsphy { + vdd-supply = <&vreg_l3j_0p8>; + vdda12-supply = <&vreg_l2j_1p2>; + + phys = <&smb2360_0_eusb2_repeater>; + + status = "okay"; +}; + +&usb_1_ss0_qmpphy { + vdda-phy-supply = <&vreg_l2j_1p2>; + vdda-pll-supply = <&vreg_l1j_0p8>; + + status = "okay"; +}; + +&usb_1_ss0 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_1_ss0_dwc3_hs { + remote-endpoint = <&pmic_glink_ss0_hs_in>; +}; + +&usb_1_ss0_qmpphy_out { + remote-endpoint = <&pmic_glink_ss0_ss_in>; +}; + +&usb_1_ss1_hsphy { + vdd-supply = <&vreg_l3j_0p8>; + vdda12-supply = <&vreg_l2j_1p2>; + + phys = <&smb2360_1_eusb2_repeater>; + + status = "okay"; +}; + +&usb_1_ss1_qmpphy { + vdda-phy-supply = <&vreg_l2j_1p2>; + vdda-pll-supply = <&vreg_l2d_0p9>; + + status = "okay"; +}; + +&usb_1_ss1 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_1_ss1_dwc3_hs { + remote-endpoint = <&pmic_glink_ss1_hs_in>; +}; + +&usb_1_ss1_qmpphy_out { + remote-endpoint = <&pmic_glink_ss1_ss_in>; +}; + +&usb_1_ss2 { + dr_mode = "host"; + maximum-speed = "high-speed"; + phys = <&usb_1_ss2_hsphy>; + phy-names = "usb2-phy"; + qcom,select-utmi-as-pipe-clk; + status = "okay"; + /delete-node/ port@1; +}; + +&usb_1_ss2_hsphy { + vdd-supply = <&vreg_l3j_0p8>; + vdda12-supply = <&vreg_l2j_1p2>; + + phys = <&eusb9_repeater>; + + status = "okay"; +}; + +&usb_1_ss2_qmpphy { + vdda-phy-supply = <&vreg_l2j_1p2>; + vdda-pll-supply = <&vreg_l2d_0p9>; + + /delete-property/ mode-switch; + /delete-property/ orientation-switch; + + status = "okay"; + + ports { + port@0 { + #address-cells = <1>; + #size-cells = <0>; + + /delete-node/ endpoint; + + usb_1_ss2_qmpphy_out_dp: endpoint@0 { + reg = <0>; + + data-lanes = <3 2 1 0>; + remote-endpoint = <&hdmi_bridge_dp_in>; + }; + }; + }; +}; + +&usb_2 { + dr_mode = "host"; + + status = "okay"; +}; + +&usb_2_hsphy { + vdd-supply = <&vreg_l2e_0p8>; + vdda12-supply = <&vreg_l3e_1p2>; + + phys = <&eusb5_repeater>; + + status = "okay"; +}; + +&usb_mp { + status = "okay"; +}; + +&usb_mp_hsphy0 { + vdd-supply = <&vreg_l2e_0p8>; + vdda12-supply = <&vreg_l3e_1p2>; + + phys = <&eusb3_repeater>; + + status = "okay"; +}; + +&usb_mp_qmpphy0 { + vdda-phy-supply = <&vreg_l3e_1p2>; + vdda-pll-supply = <&vreg_l3c_0p8>; + + status = "okay"; +}; + +&usb_mp_hsphy1 { + vdd-supply = <&vreg_l2e_0p8>; + vdda12-supply = <&vreg_l3e_1p2>; + + phys = <&eusb6_repeater>; + + status = "okay"; +}; + +&usb_mp_qmpphy1 { + vdda-phy-supply = <&vreg_l3e_1p2>; + vdda-pll-supply = <&vreg_l3c_0p8>; + + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-thinkbook-16.dts b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-thinkbook-16.dts index 44b7be59a42887..1768f5594ed163 100644 --- a/arch/arm64/boot/dts/qcom/x1p42100-lenovo-thinkbook-16.dts +++ b/arch/arm64/boot/dts/qcom/x1p42100-lenovo-thinkbook-16.dts @@ -1416,7 +1416,6 @@ &tlmm { gpio-reserved-ranges = <34 2>, /* Unused */ - <65 1>, /* EC reset */ <72 2>; /* Secure EC I2C connection (?) */ cam_reg_en: cam-reg-en-state { diff --git a/arch/arm64/boot/dts/qcom/x1p42100-microsoft-sp12in.dts b/arch/arm64/boot/dts/qcom/x1p42100-microsoft-sp12in.dts index 257e429bbec7a4..6c2129a0d6ad00 100644 --- a/arch/arm64/boot/dts/qcom/x1p42100-microsoft-sp12in.dts +++ b/arch/arm64/boot/dts/qcom/x1p42100-microsoft-sp12in.dts @@ -1010,8 +1010,7 @@ &tlmm { gpio-reserved-ranges = <34 2>, /* Unused */ - <44 4>, /* SPI (TPM) */ - <65 1>; /* EC reset */ + <44 4>; /* SPI (TPM) */ edp_reg_en: edp-reg-en-state { pins = "gpio70"; diff --git a/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi b/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi index add917af5de783..f14f9eca7d3423 100644 --- a/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi +++ b/arch/arm64/boot/dts/rockchip/px30-cobra.dtsi @@ -35,7 +35,7 @@ compatible = "mmc-pwrseq-emmc"; pinctrl-0 = <&emmc_reset>; pinctrl-names = "default"; - reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_LOW>; }; gpio-leds { diff --git a/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi b/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi index 192791993f0591..02200de695d31b 100644 --- a/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi +++ b/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi @@ -33,7 +33,7 @@ compatible = "mmc-pwrseq-emmc"; pinctrl-0 = <&emmc_reset>; pinctrl-names = "default"; - reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_LOW>; }; gpio-leds { diff --git a/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi b/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi index 973b4c5880e24d..29794216592d80 100644 --- a/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi +++ b/arch/arm64/boot/dts/rockchip/px30-ringneck.dtsi @@ -26,7 +26,7 @@ compatible = "mmc-pwrseq-emmc"; pinctrl-0 = <&emmc_reset>; pinctrl-names = "default"; - reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_LOW>; }; leds { diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts index 8d26bd9b75003c..d46cdfe3f7849a 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts @@ -734,12 +734,6 @@ }; }; - wifi { - wifi_host_wake_l: wifi-host-wake-l { - rockchip,pins = <4 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>; - }; - }; - wireless-bluetooth { bt_wake_pin: bt-wake-pin { rockchip,pins = <2 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>; @@ -766,19 +760,7 @@ pinctrl-names = "default"; pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>; sd-uhs-sdr104; - #address-cells = <1>; - #size-cells = <0>; status = "okay"; - - brcmf: wifi@1 { - compatible = "brcm,bcm4329-fmac"; - reg = <1>; - interrupt-parent = <&gpio4>; - interrupts = ; - interrupt-names = "host-wake"; - pinctrl-names = "default"; - pinctrl-0 = <&wifi_host_wake_l>; - }; }; &pwm0 { diff --git a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts index 4f2831097624e4..f6d62ee51f1b91 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts @@ -136,6 +136,18 @@ <3 RK_PD7 1 &pcfg_pull_none>; }; +&i2s0_8ch_bus_bclk_off { + rockchip,pins = + <3 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>, + <3 RK_PD1 1 &pcfg_pull_none>, + <3 RK_PD2 1 &pcfg_pull_none>, + <3 RK_PD3 1 &pcfg_pull_none>, + <3 RK_PD4 1 &pcfg_pull_none>, + <3 RK_PD5 1 &pcfg_pull_none>, + <3 RK_PD6 1 &pcfg_pull_none>, + <3 RK_PD7 1 &pcfg_pull_none>; +}; + &i2s1 { pinctrl-names = "default"; pinctrl-0 = <&i2s_8ch_mclk_pin>, <&i2s1_2ch_bus>; diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts b/arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts index d534d662c40f11..99853880aaac6f 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-roc-pc.dts @@ -23,16 +23,19 @@ compatible = "simple-audio-card"; pinctrl-names = "default"; pinctrl-0 = <&hp_detect>; + simple-audio-card,aux-devs = <&headphones_amp>; simple-audio-card,name = "rockchip,es8388"; - simple-audio-card,bitclock-master = <&masterdai>; + simple-audio-card,bitclock-master = <&cpudai>; simple-audio-card,format = "i2s"; - simple-audio-card,frame-master = <&masterdai>; + simple-audio-card,frame-master = <&cpudai>; simple-audio-card,hp-det-gpios = <&gpio1 RK_PA6 GPIO_ACTIVE_LOW>; simple-audio-card,mclk-fs = <256>; simple-audio-card,pin-switches = "Headphones"; simple-audio-card,routing = - "Headphones", "LOUT1", - "Headphones", "ROUT1", + "Headphones", "Headphone Amp OUTL", + "Headphones", "Headphone Amp OUTR", + "Headphone Amp INL", "LOUT2", + "Headphone Amp INR", "ROUT2", "LINPUT1", "Microphone Jack", "RINPUT1", "Microphone Jack", "LINPUT2", "Onboard Microphone", @@ -47,11 +50,17 @@ system-clock-frequency = <12288000>; }; - simple-audio-card,cpu { + cpudai: simple-audio-card,cpu { sound-dai = <&i2s0_8ch>; }; }; + headphones_amp: audio-amplifier-headphones { + compatible = "simple-audio-amplifier"; + enable-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>; + sound-name-prefix = "Headphone Amp"; + }; + chosen { stdout-path = "serial2:1500000n8"; }; @@ -327,12 +336,12 @@ es8388: audio-codec@11 { compatible = "everest,es8388", "everest,es8328"; reg = <0x11>; - clocks = <&cru I2S1_8CH_MCLKOUT>; + clocks = <&cru I2S0_8CH_MCLKOUT>; AVDD-supply = <&vcc_3v3_s0>; DVDD-supply = <&vcc_1v8_s0>; HPVDD-supply = <&vcc_3v3_s0>; PVDD-supply = <&vcc_3v3_s0>; - assigned-clocks = <&cru I2S1_8CH_MCLKOUT>; + assigned-clocks = <&cru I2S0_8CH_MCLKOUT>; assigned-clock-rates = <12288000>; #sound-dai-cells = <0>; }; diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h index 71493b760b8394..3c008821219c26 100644 --- a/arch/arm64/include/asm/cpu.h +++ b/arch/arm64/include/asm/cpu.h @@ -78,5 +78,6 @@ void __init cpuinfo_store_boot_cpu(void); void __init init_cpu_features(struct cpuinfo_arm64 *info); void update_cpu_features(int cpu, struct cpuinfo_arm64 *info, struct cpuinfo_arm64 *boot); +bool gmid_el1_accessible(const struct cpuinfo_arm64 *info); #endif /* __ASM_CPU_H */ diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index a57870fa96db5b..f6a7200700ccfd 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -627,13 +627,6 @@ static inline bool id_aa64pfr1_mpamfrac(u64 pfr1) return val > 0; } -static inline bool id_aa64pfr1_mte(u64 pfr1) -{ - u32 val = cpuid_feature_extract_unsigned_field(pfr1, ID_AA64PFR1_EL1_MTE_SHIFT); - - return val >= ID_AA64PFR1_EL1_MTE_MTE2; -} - void __init setup_boot_cpu_features(void); void __init setup_system_features(void); void __init setup_user_features(void); diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index bae2c4f92ef5c0..ac16f96c878d69 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -411,9 +411,6 @@ struct kvm_arch { /* Masks for VNCR-backed and general EL2 sysregs */ struct kvm_sysreg_masks *sysreg_masks; - /* Count the number of VNCR_EL2 currently mapped */ - atomic_t vncr_map_count; - /* * For an untrusted host VM, 'pkvm.handle' is used to lookup * the associated pKVM instance in the hypervisor. diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h index 012d711034d172..1ed7083358096d 100644 --- a/arch/arm64/include/asm/kvm_nested.h +++ b/arch/arm64/include/asm/kvm_nested.h @@ -291,6 +291,13 @@ static inline u64 decode_range_tlbi(u64 val, u64 *range, u16 *asid) base = (val & GENMASK(36, 0)) << shift; + /* + * We only deal with at most 48bit VA/IPA, so 48 is where we + * sign-extend from. Should we support FEAT_L{VP}A* at some point, + * this will need to be revisited. + */ + base = (u64)sign_extend64(base, 48); + if (asid) *asid = FIELD_GET(TLBIR_ASID_MASK, val); @@ -298,6 +305,12 @@ static inline u64 decode_range_tlbi(u64 val, u64 *range, u16 *asid) num = FIELD_GET(GENMASK(43, 39), val); *range = __TLBI_RANGE_PAGES(num, scale) << shift; + /* Cap the range to the correct half of the address space */ + if (!(base & BIT(48))) + *range = min(*range, (BIT(48) - base)); + else + *range = min(*range, ~base + 1); + return base; } @@ -388,6 +401,8 @@ struct s1_walk_result { bool failed; }; +#define S1_MMU_DISABLED (-127) + static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw) { wr->fst = fst; @@ -396,6 +411,11 @@ static inline void fail_s1_walk(struct s1_walk_result *wr, u8 fst, bool s1ptw) wr->failed = true; } +static inline bool s1_walk_translated(struct s1_walk_result *wr) +{ + return wr->level != S1_MMU_DISABLED; +} + int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, struct s1_walk_result *wr, u64 va); int __kvm_find_s1_desc_level(struct kvm_vcpu *vcpu, u64 va, u64 ipa, diff --git a/arch/arm64/kernel/compat_alignment.c b/arch/arm64/kernel/compat_alignment.c index b68e1d328d4cb9..9b58d0aa38d225 100644 --- a/arch/arm64/kernel/compat_alignment.c +++ b/arch/arm64/kernel/compat_alignment.c @@ -114,8 +114,8 @@ do_alignment_ldrdstrd(unsigned long addr, u32 instr, struct pt_regs *regs) static int do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs) { - unsigned int rd, rn, nr_regs, regbits; - unsigned long eaddr, newaddr; + unsigned int rd, rn, regbits; + unsigned long eaddr, newaddr, nr_regs; unsigned int val; /* count the number of registers in the mask to be transferred */ diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index 1995e1198648e8..b0937e3f6fb468 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -82,7 +82,7 @@ is_affected_midr_range(const struct arm64_cpu_capabilities *entry, int scope) for (i = 0; i < target_impl_cpu_num; i++) { if (__is_affected_midr_range(entry, target_impl_cpus[i].midr, - target_impl_cpus[i].midr)) + target_impl_cpus[i].revidr)) return true; } return false; diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 9a22df0c5120f3..103e70d683ca20 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -1176,6 +1176,33 @@ static bool detect_ftr_has_mpam(void) return id_aa64pfr0_mpam(pfr0) || id_aa64pfr1_mpamfrac(pfr1); } +bool gmid_el1_accessible(const struct cpuinfo_arm64 *info) +{ + const struct arm64_ftr_bits *ftrp; + s64 mte, ovr; + u64 ftr_mask; + + /* No ID register reflects CONFIG_ARM64_MTE. */ + if (!IS_ENABLED(CONFIG_ARM64_MTE)) + return false; + + for (ftrp = ftr_id_aa64pfr1; ftrp->width; ftrp++) { + if (ftrp->shift == ID_AA64PFR1_EL1_MTE_SHIFT) + break; + } + + ftr_mask = arm64_ftr_mask(ftrp); + mte = arm64_ftr_value(ftrp, info->reg_id_aa64pfr1); + + /* The boot CPU runs before init_cpu_ftr_reg() strips unsafe overrides. */ + if ((id_aa64pfr1_override.mask & ftr_mask) == ftr_mask) { + ovr = arm64_ftr_value(ftrp, id_aa64pfr1_override.val); + mte = arm64_ftr_safe_value(ftrp, ovr, mte); + } + + return mte >= ID_AA64PFR1_EL1_MTE_MTE2; +} + void __init init_cpu_features(struct cpuinfo_arm64 *info) { /* Before we start using the tables, make sure it is sorted */ @@ -1228,7 +1255,7 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info) init_cpu_ftr_reg(SYS_MPAMIDR_EL1, info->reg_mpamidr); } - if (id_aa64pfr1_mte(info->reg_id_aa64pfr1)) + if (gmid_el1_accessible(info)) init_cpu_ftr_reg(SYS_GMID_EL1, info->reg_gmid); } @@ -1490,11 +1517,9 @@ void update_cpu_features(int cpu, * they read/write depends on the GMID_EL1.BS field. Check that the * value is the same on all CPUs. */ - if (IS_ENABLED(CONFIG_ARM64_MTE) && - id_aa64pfr1_mte(info->reg_id_aa64pfr1)) { + if (gmid_el1_accessible(info)) taint |= check_update_ftr_reg(SYS_GMID_EL1, cpu, info->reg_gmid, boot->reg_gmid); - } /* * If we don't have AArch32 at all then skip the checks entirely diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index d50e2a9b066b31..45c63f3d75c53d 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -502,7 +502,7 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) info->reg_id_aa64smfr0 = read_cpuid(ID_AA64SMFR0_EL1); info->reg_id_aa64fpfr0 = read_cpuid(ID_AA64FPFR0_EL1); - if (id_aa64pfr1_mte(info->reg_id_aa64pfr1)) + if (gmid_el1_accessible(info)) info->reg_gmid = read_cpuid(GMID_EL1); if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) diff --git a/arch/arm64/kernel/proton-pack.c b/arch/arm64/kernel/proton-pack.c index 7bb6553fec087a..3bcf86154d95cd 100644 --- a/arch/arm64/kernel/proton-pack.c +++ b/arch/arm64/kernel/proton-pack.c @@ -1023,6 +1023,11 @@ static int __init parse_spectre_bhb_param(char *str) } early_param("nospectre_bhb", parse_spectre_bhb_param); +static bool spectre_bhb_mitigations_off(void) +{ + return __nospectre_bhb || cpu_mitigations_off(); +} + void spectre_bhb_enable_mitigation(const struct arm64_cpu_capabilities *entry) { bp_hardening_cb_t cpu_cb; @@ -1036,6 +1041,8 @@ void spectre_bhb_enable_mitigation(const struct arm64_cpu_capabilities *entry) /* No point mitigating Spectre-BHB alone. */ } else if (!IS_ENABLED(CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY)) { /* Do nothing */ + } else if (spectre_bhb_mitigations_off()) { + /* Mitigation disabled on the command line */ } else if (supports_ecbhb(SCOPE_LOCAL_CPU)) { state = SPECTRE_MITIGATED; set_bit(BHB_HW, &system_bhb_mitigations); @@ -1201,6 +1208,6 @@ void spectre_print_disabled_mitigations(void) if (spectre_v4_mitigations_off()) pr_info("spectre-v4 %s", spectre_disabled_suffix); - if (__nospectre_bhb || cpu_mitigations_off()) + if (spectre_bhb_mitigations_off()) pr_info("spectre-bhb %s", spectre_disabled_suffix); } diff --git a/arch/arm64/kvm/at.c b/arch/arm64/kvm/at.c index 640f2dc00a8bad..0926426b879899 100644 --- a/arch/arm64/kvm/at.c +++ b/arch/arm64/kvm/at.c @@ -11,8 +11,6 @@ #include #include -#define S1_MMU_DISABLED (-127) - static int get_ia_size(struct s1_walk_info *wi) { return 64 - wi->txsz; diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c index bbe9cebd3d9d57..c09b1d411c5843 100644 --- a/arch/arm64/kvm/hyp/vhe/switch.c +++ b/arch/arm64/kvm/hyp/vhe/switch.c @@ -427,8 +427,7 @@ static bool kvm_hyp_handle_tlbi_el2(struct kvm_vcpu *vcpu, u64 *exit_code) * If we have to check for any VNCR mapping being invalidated, * go back to the slow path for further processing. */ - if (vcpu_el2_e2h_is_set(vcpu) && vcpu_el2_tge_is_set(vcpu) && - atomic_read(&vcpu->kvm->arch.vncr_map_count)) + if (vcpu_el2_e2h_is_set(vcpu) && vcpu_el2_tge_is_set(vcpu)) return false; __kvm_skip_instr(vcpu); diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index dfb96edbdc43c6..550c9bd3dbe7d9 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -27,7 +27,7 @@ struct vncr_tlb { bool hpa_writable; /* -1 when not mapped on a CPU */ - int cpu; + atomic_t cpu; /* * true if the TLB is valid. Can only be changed with the @@ -48,7 +48,6 @@ void kvm_init_nested(struct kvm *kvm) { kvm->arch.nested_mmus = NULL; kvm->arch.nested_mmus_size = 0; - atomic_set(&kvm->arch.vncr_map_count, 0); } static int init_nested_s2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu) @@ -506,7 +505,7 @@ int kvm_walk_nested_s2(struct kvm_vcpu *vcpu, phys_addr_t gipa, return ret; } -static unsigned int ttl_to_size(u8 ttl) +static unsigned int __ttl_to_size(u8 ttl) { int level = ttl & 3; int gran = (ttl >> 2) & 3; @@ -562,10 +561,22 @@ static unsigned int ttl_to_size(u8 ttl) return max_size; } -static u8 pgshift_level_to_ttl(u16 shift, u8 level) +static unsigned int ttl_to_size(u8 ttl) +{ + return __ttl_to_size(ttl) ?: SZ_1G; +} + +static u8 pgshift_level_to_ttl(u16 shift, s8 level) { u8 ttl; + /* + * If we don't have a proper level, fallback to the maximum + * size. + */ + if (level < 0) + return 0; + switch(shift) { case 12: ttl = TLBI_TTL_TG_4K; @@ -676,7 +687,11 @@ unsigned long compute_tlb_inval_range(struct kvm_s2_mmu *mmu, u64 val) ttl = get_guest_mapping_ttl(mmu, addr); } - max_size = ttl_to_size(ttl); + /* + * Don't use the default 1GB fallback, as we can adapt to the + * max mapping size we allow at S2. + */ + max_size = __ttl_to_size(ttl); if (!max_size) { /* Compute the maximum extent of the invalidation */ @@ -879,18 +894,41 @@ void kvm_vcpu_load_hw_mmu(struct kvm_vcpu *vcpu) } } +/* + * Unmapping an L1 VNCR can happen concurrently without the mmu lock being + * effective (vcpu_put() vs TLBI handling). The atomic_xchg below ensures + * that only one CPU sets it to -1 while getting a valid CPU number back. + */ +static int unmap_l1_vncr(struct vncr_tlb *vt) +{ + int cpu = atomic_xchg_relaxed(&vt->cpu, -1); + + if (cpu != -1) + clear_fixmap(vncr_fixmap(cpu)); + + return cpu; +} + static void this_cpu_reset_vncr_fixmap(struct kvm_vcpu *vcpu) { if (!host_data_test_flag(L1_VNCR_MAPPED)) return; - BUG_ON(vcpu->arch.vncr_tlb->cpu != smp_processor_id()); BUG_ON(is_hyp_ctxt(vcpu)); - clear_fixmap(vncr_fixmap(vcpu->arch.vncr_tlb->cpu)); - vcpu->arch.vncr_tlb->cpu = -1; + /* + * Unconditionally unmap the local VNCR if we have lost the race + * against a concurrent TLBI. Otherwise we could end-up running + * another vcpu with VNCR still mapped if the TLBI thread is + * preempted between the exchange and the clear_fixmap(). + * + * Note that we do not care about the TLBI nuking the fixmap behind + * the back of an running vcpu. This will only generate a fault and + * possibly a retranslation. + */ + if (unmap_l1_vncr(vcpu->arch.vncr_tlb) == -1) + clear_fixmap(vncr_fixmap(smp_processor_id())); host_data_clear_flag(L1_VNCR_MAPPED); - atomic_dec(&vcpu->kvm->arch.vncr_map_count); } void kvm_vcpu_put_hw_mmu(struct kvm_vcpu *vcpu) @@ -981,8 +1019,21 @@ u16 get_asid_by_regime(struct kvm_vcpu *vcpu, enum trans_regime regime) static void invalidate_vncr(struct vncr_tlb *vt) { vt->valid = false; - if (vt->cpu != -1) - clear_fixmap(vncr_fixmap(vt->cpu)); + unmap_l1_vncr(vt); +} + +static bool vncr_tlb_intersects(struct vncr_tlb *vt, u64 addr, + u64 scope_start, u64 scope_size) +{ + u64 tlb_size, tlb_start, tlb_end, scope_end; + + tlb_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, vt->wr.level)); + + tlb_start = addr & ~(tlb_size - 1); + tlb_end = tlb_start + tlb_size - 1; + scope_end = scope_start + scope_size - 1; + + return !(tlb_end < scope_start || tlb_start > scope_end); } /* @@ -1007,19 +1058,15 @@ static void kvm_invalidate_vncr_ipa(struct kvm *kvm, u64 start, u64 end) if (!kvm_has_feat(kvm, ID_AA64MMFR4_EL1, NV_frac, NV2_ONLY)) return; - kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) { - u64 ipa_start, ipa_end, ipa_size; - - ipa_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, - vt->wr.level)); - ipa_start = vt->wr.pa & ~(ipa_size - 1); - ipa_end = ipa_start + ipa_size; - - if (ipa_end <= start || ipa_start >= end) - continue; - - invalidate_vncr(vt); - } + /* + * Note that invalidating the VNCR on the back of an MMU notifier + * doesn't require messing with the invalidation counter for a + * parallel walk. The notifier itself will have bumped the counter, + * making sure we rewalk. + */ + kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) + if (vncr_tlb_intersects(vt, vt->wr.pa, start, end - start)) + invalidate_vncr(vt); } struct s1e2_tlbi_scope { @@ -1044,29 +1091,29 @@ static void invalidate_vncr_va(struct kvm *kvm, lockdep_assert_held_write(&kvm->mmu_lock); - kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) { - u64 va_start, va_end, va_size; - - va_size = ttl_to_size(pgshift_level_to_ttl(vt->wi.pgshift, - vt->wr.level)); - va_start = vt->gva & ~(va_size - 1); - va_end = va_start + va_size; + /* + * We might be performing a parallel S1 walk, so bump up the + * invalidation counter even in the absence of an actual VNCR TLB + * invalidation, as this could indicate that the guest has gone + * through a BBM sequence. + */ + kvm->mmu_invalidate_seq++; + smp_wmb(); + kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) { switch (scope->type) { case TLBI_ALL: break; case TLBI_VA: - if (va_end <= scope->va || - va_start >= (scope->va + scope->size)) + if (!vncr_tlb_intersects(vt, vt->gva, scope->va, scope->size)) continue; if (vt->wr.nG && vt->wr.asid != scope->asid) continue; break; case TLBI_VAA: - if (va_end <= scope->va || - va_start >= (scope->va + scope->size)) + if (!vncr_tlb_intersects(vt, vt->gva, scope->va, scope->size)) continue; break; @@ -1126,8 +1173,6 @@ static void compute_s1_tlbi_range(struct kvm_vcpu *vcpu, u32 inst, u64 val, case OP_TLBI_VALE1OSNXS: scope->type = TLBI_VA; scope->size = ttl_to_size(FIELD_GET(TLBI_TTL_MASK, val)); - if (!scope->size) - scope->size = SZ_1G; scope->va = tlbi_va_s1_to_va(val) & ~(scope->size - 1); scope->asid = FIELD_GET(TLBIR_ASID_MASK, val); break; @@ -1154,8 +1199,6 @@ static void compute_s1_tlbi_range(struct kvm_vcpu *vcpu, u32 inst, u64 val, case OP_TLBI_VAALE1OSNXS: scope->type = TLBI_VAA; scope->size = ttl_to_size(FIELD_GET(TLBI_TTL_MASK, val)); - if (!scope->size) - scope->size = SZ_1G; scope->va = tlbi_va_s1_to_va(val) & ~(scope->size - 1); break; case OP_TLBI_RVAE2: @@ -1391,15 +1434,15 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) va = read_vncr_el2(vcpu); + mmu_seq = vcpu->kvm->mmu_invalidate_seq; + smp_rmb(); + ret = __kvm_translate_va(vcpu, &vt->wi, &vt->wr, va); if (ret) return ret; write_fault = kvm_is_write_fault(vcpu); - mmu_seq = vcpu->kvm->mmu_invalidate_seq; - smp_rmb(); - gfn = vt->wr.pa >> PAGE_SHIFT; memslot = gfn_to_memslot(vcpu->kvm, gfn); if (!memslot) { @@ -1447,7 +1490,7 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) vt->hpa = pfn << PAGE_SHIFT; vt->hpa_writable = writable; vt->valid = true; - vt->cpu = -1; + atomic_set(&vt->cpu, -1); kvm_make_request(KVM_REQ_MAP_L1_VNCR_EL2, vcpu); kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw && vt->hpa_writable); @@ -1568,14 +1611,16 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu) if (!vt->valid) return; + /* We cache the MMU state in the TLB. Check that it matches. */ + if (!!(vcpu_read_sys_reg(vcpu, SCTLR_EL2) & SCTLR_ELx_M) != s1_walk_translated(&vt->wr)) + return; + if (read_vncr_el2(vcpu) != vt->gva) return; if (vt->wr.nG && get_asid_by_regime(vcpu, TR_EL20) != vt->wr.asid) return; - vt->cpu = smp_processor_id(); - if (vt->hpa_writable && vt->wr.pw && vt->wr.pr) prot = PAGE_KERNEL; else if (vt->wr.pr) @@ -1590,9 +1635,9 @@ static void kvm_map_l1_vncr(struct kvm_vcpu *vcpu) * FIXME: WO doesn't work at all, need POE support in the kernel. */ if (pgprot_val(prot) != pgprot_val(PAGE_NONE)) { - __set_fixmap(vncr_fixmap(vt->cpu), vt->hpa, prot); + atomic_set(&vt->cpu, smp_processor_id()); + __set_fixmap(vncr_fixmap(atomic_read(&vt->cpu)), vt->hpa, prot); host_data_set_flag(L1_VNCR_MAPPED); - atomic_inc(&vcpu->kvm->arch.vncr_map_count); } } diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 5d5c579d457902..797e888bf93946 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -4057,6 +4057,7 @@ static bool handle_ripas2e1is(struct kvm_vcpu *vcpu, struct sys_reg_params *p, u32 sys_encoding = sys_insn(p->Op0, p->Op1, p->CRn, p->CRm, p->Op2); u64 vttbr = vcpu_read_sys_reg(vcpu, VTTBR_EL2); u64 base, range; + int pa_bits; if (!kvm_supported_tlbi_ipas2_op(vcpu, sys_encoding)) return undef_access(vcpu, p, r); @@ -4068,6 +4069,16 @@ static bool handle_ripas2e1is(struct kvm_vcpu *vcpu, struct sys_reg_params *p, */ base = decode_range_tlbi(p->regval, &range, NULL); + /* + * Ignore TLBIs that start out of PA_bits range, and cap the + * invalidation to the [base:bit(PA_bits)] interval. + */ + pa_bits = kvm_get_pa_bits(vcpu->kvm); + if (fls64(base) > pa_bits) + return true; + + range = min(range, BIT_ULL(pa_bits) - base); + kvm_s2_mmu_iterate_by_vmid(vcpu->kvm, get_vmid(vttbr), &(union tlbi_info) { .range = { diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 907057881b26aa..fdac6e24762da5 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -176,6 +176,7 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) } kvm->arch.vgic.vgic_model = 0; + kvm->arch.vgic.in_kernel = false; goto out_unlock; } diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 36ab3e49291541..ed281fbf008b9b 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -2119,6 +2119,14 @@ static int vgic_its_save_ite(struct vgic_its *its, struct its_device *dev, u32 next_offset; u64 val; + /* + * MAPC with V=0 keeps the ITEs mapped but drops their collection, + * and with it the ICID. Save a zeroed entry, which the restore path + * reads back as invalid. + */ + if (!ite->collection) + return vgic_its_write_entry_lock(its, gpa, 0ULL, ite); + next_offset = compute_next_eventid_offset(&dev->itt_head, ite); val = ((u64)next_offset << KVM_ITS_ITE_NEXT_SHIFT) | ((u64)ite->irq->intid << KVM_ITS_ITE_PINTID_SHIFT) | diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c index cafa3cb32bda6f..7182f63fc9382c 100644 --- a/arch/arm64/kvm/vgic/vgic-v2.c +++ b/arch/arm64/kvm/vgic/vgic-v2.c @@ -170,8 +170,9 @@ void vgic_v2_deactivate(struct kvm_vcpu *vcpu, u32 val) /* Make sure we're in the same context as LR handling */ local_irq_save(flags); + /* Guest-supplied INTID: out of range yields no irq, so ignore it */ irq = vgic_get_vcpu_irq(vcpu, val); - if (WARN_ON_ONCE(!irq)) + if (!irq) goto out; /* See the corresponding v3 code for the rationale */ diff --git a/arch/arm64/kvm/vgic/vgic-v3-nested.c b/arch/arm64/kvm/vgic/vgic-v3-nested.c index 5c69fa615823c1..e3e84d7f5ad510 100644 --- a/arch/arm64/kvm/vgic/vgic-v3-nested.c +++ b/arch/arm64/kvm/vgic/vgic-v3-nested.c @@ -152,7 +152,7 @@ static void vgic_compute_mi_state(struct kvm_vcpu *vcpu, struct mi_state *mi_sta eisr |= BIT(i); if (!(lr & ICH_LR_STATE)) elrsr |= BIT(i); - pend |= (lr & ICH_LR_PENDING_BIT); + pend |= (lr & ICH_LR_STATE) == ICH_LR_PENDING_BIT; } mi_state->eisr = eisr; diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c index 7aa417440f6a2f..0e1c121766ae12 100644 --- a/arch/arm64/kvm/vgic/vgic-v3.c +++ b/arch/arm64/kvm/vgic/vgic-v3.c @@ -617,9 +617,13 @@ int vgic_v3_save_pending_tables(struct kvm *kvm) bool is_pending; bool stored; + irq = vgic_get_irq(kvm, index); + if (!irq) + continue; + vcpu = irq->target_vcpu; if (!vcpu) - continue; + goto put_irq; pendbase = GICR_PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser); @@ -630,7 +634,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm) if (ptr != last_ptr) { ret = kvm_read_guest_lock(kvm, ptr, &val, 1); if (ret) - goto out; + goto put_irq; last_ptr = ptr; } @@ -642,7 +646,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm) vgic_v4_get_vlpi_state(irq, &is_pending); if (stored == is_pending) - continue; + goto put_irq; if (is_pending) val |= 1 << bit_nr; @@ -650,6 +654,8 @@ int vgic_v3_save_pending_tables(struct kvm *kvm) val &= ~(1 << bit_nr); ret = vgic_write_guest_lock(kvm, ptr, &val, 1); +put_irq: + vgic_put_irq(kvm, irq); if (ret) goto out; } diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 0b52557652be66..75c3e463df2ef2 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -154,6 +155,9 @@ static void show_pte(unsigned long addr) pr_alert("%s pgtable: %luk pages, %llu-bit VAs, pgdp=%016lx\n", mm == &init_mm ? "swapper" : "user", PAGE_SIZE / SZ_1K, vabits_actual, mm_to_pgd_phys(mm)); + + guard(irqsave)(); + pgdp = pgd_offset(mm, addr); pgd = READ_ONCE(*pgdp); pr_alert("[%016lx] pgd=%016llx", addr, pgd_val(pgd)); @@ -167,25 +171,25 @@ static void show_pte(unsigned long addr) if (pgd_none(pgd) || pgd_bad(pgd)) break; - p4dp = p4d_offset(pgdp, addr); + p4dp = p4d_offset_lockless(pgdp, pgd, addr); p4d = READ_ONCE(*p4dp); pr_cont(", p4d=%016llx", p4d_val(p4d)); if (p4d_none(p4d) || p4d_bad(p4d)) break; - pudp = pud_offset(p4dp, addr); + pudp = pud_offset_lockless(p4dp, p4d, addr); pud = READ_ONCE(*pudp); pr_cont(", pud=%016llx", pud_val(pud)); if (pud_none(pud) || pud_bad(pud)) break; - pmdp = pmd_offset(pudp, addr); + pmdp = pmd_offset_lockless(pudp, pud, addr); pmd = READ_ONCE(*pmdp); pr_cont(", pmd=%016llx", pmd_val(pmd)); if (pmd_none(pmd) || pmd_bad(pmd)) break; - ptep = pte_offset_map(pmdp, addr); + ptep = pte_offset_map(&pmd, addr); if (!ptep) break; diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index d8d25232501791..14d31e9a3d705f 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -175,7 +175,7 @@ config LOONGARCH select HAVE_RELIABLE_STACKTRACE if UNWINDER_ORC select HAVE_RETHOOK select HAVE_RSEQ - select HAVE_RUST + select HAVE_RUST if !KASAN select HAVE_SAMPLE_FTRACE_DIRECT select HAVE_SAMPLE_FTRACE_DIRECT_MULTI select HAVE_SETUP_PER_CPU_AREA if NUMA diff --git a/arch/loongarch/include/asm/kvm_dmsintc.h b/arch/loongarch/include/asm/kvm_dmsintc.h index 5a71b9ccbe78ec..7c0158764d844f 100644 --- a/arch/loongarch/include/asm/kvm_dmsintc.h +++ b/arch/loongarch/include/asm/kvm_dmsintc.h @@ -20,6 +20,7 @@ struct dmsintc_state { }; int kvm_loongarch_register_dmsintc_device(void); +void kvm_loongarch_unregister_dmsintc_device(void); void dmsintc_inject_irq(struct kvm_vcpu *vcpu); int dmsintc_set_irq(struct kvm *kvm, u64 addr, int data, int level); int dmsintc_deliver_msi_to_vcpu(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 vector, int level); diff --git a/arch/loongarch/include/asm/kvm_eiointc.h b/arch/loongarch/include/asm/kvm_eiointc.h index 8b7a2fa3f7f868..9633fbfc066d66 100644 --- a/arch/loongarch/include/asm/kvm_eiointc.h +++ b/arch/loongarch/include/asm/kvm_eiointc.h @@ -79,6 +79,7 @@ struct loongarch_eiointc { }; int kvm_loongarch_register_eiointc_device(void); +void kvm_loongarch_unregister_eiointc_device(void); void eiointc_set_irq(struct loongarch_eiointc *s, int irq, int level); #endif /* __ASM_KVM_EIOINTC_H */ diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index 23cfbecebbd79a..3538a199aac62e 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -125,6 +125,7 @@ struct kvm_arch { unsigned int pte_shifts[MAX_PGTABLE_LEVELS]; unsigned int root_level; spinlock_t phyid_map_lock; + spinlock_t pv_setting_lock; struct kvm_phyid_map *phyid_map; /* Enabled PV features */ unsigned long pv_features; diff --git a/arch/loongarch/include/asm/kvm_ipi.h b/arch/loongarch/include/asm/kvm_ipi.h index 060163dfb4a380..d1d72d4bb8d15e 100644 --- a/arch/loongarch/include/asm/kvm_ipi.h +++ b/arch/loongarch/include/asm/kvm_ipi.h @@ -41,5 +41,6 @@ struct ipi_state { #define IOCSR_ANY_SEND 0x158 int kvm_loongarch_register_ipi_device(void); +void kvm_loongarch_unregister_ipi_device(void); #endif diff --git a/arch/loongarch/include/asm/kvm_pch_pic.h b/arch/loongarch/include/asm/kvm_pch_pic.h index e74b3b742634d0..887b0431fd2068 100644 --- a/arch/loongarch/include/asm/kvm_pch_pic.h +++ b/arch/loongarch/include/asm/kvm_pch_pic.h @@ -70,6 +70,7 @@ struct loongarch_pch_pic { struct kvm_kernel_irq_routing_entry; int kvm_loongarch_register_pch_pic_device(void); +void kvm_loongarch_unregister_pch_pic_device(void); void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level); int pch_msi_set_irq(struct kvm *kvm, struct kvm_kernel_irq_routing_entry *e, int level); diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h index 223528c04d7392..a2191044435d2e 100644 --- a/arch/loongarch/include/asm/pgtable.h +++ b/arch/loongarch/include/asm/pgtable.h @@ -96,7 +96,7 @@ struct vm_area_struct; #ifdef CONFIG_64BIT #define MODULES_VADDR (vm_map_base + PCI_IOSIZE + (2 * PAGE_SIZE)) -#define MODULES_END (MODULES_VADDR + SZ_256M) +#define MODULES_END (MODULES_VADDR + SZ_2G) /* 256MB for text, rest for data */ #ifdef CONFIG_KFENCE #define KFENCE_AREA_SIZE (((CONFIG_KFENCE_NUM_OBJECTS + 1) * 2 + 2) * PAGE_SIZE) @@ -125,6 +125,13 @@ struct vm_area_struct; #endif +/* Needed to limit get_free_mem_region() */ +#ifndef CONFIG_SPARSEMEM +#define DIRECT_MAP_PHYSMEM_END ((1ULL << (cpu_pabits + 1)) - 1) +#else +#define DIRECT_MAP_PHYSMEM_END min((1ULL << (cpu_pabits + 1)) - 1, (1ULL << MAX_PHYSMEM_BITS) - 1) +#endif + #define ptep_get(ptep) READ_ONCE(*(ptep)) #define pmdp_get(pmdp) READ_ONCE(*(pmdp)) diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 873e90990771b0..cb454ee92b2043 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -202,7 +203,7 @@ static void __init acpi_process_madt(void) int pptt_enabled; static int acpi_nr_packages; -static int acpi_package_ids[MAX_PACKAGES]; +static int acpi_package_ids[MAX(MAX_PACKAGES, KVM_MAX_VCPUS)]; int __init parse_acpi_topology(void) { diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c index 1985ed30dd16fd..ddfefea1747270 100644 --- a/arch/loongarch/kernel/kprobes.c +++ b/arch/loongarch/kernel/kprobes.c @@ -275,6 +275,9 @@ bool kprobe_singlestep_handler(struct pt_regs *regs) struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); unsigned long addr = instruction_pointer(regs); + if (!cur) + return false; + if (cur && (kcb->kprobe_status & (KPROBE_HIT_SS | KPROBE_REENTER)) && ((unsigned long)&cur->ainsn.insn[1] == addr)) { restore_local_irqflag(kcb, regs); diff --git a/arch/loongarch/kernel/rethook_trampoline.S b/arch/loongarch/kernel/rethook_trampoline.S index 2e009fbea53f2d..1601894446845d 100644 --- a/arch/loongarch/kernel/rethook_trampoline.S +++ b/arch/loongarch/kernel/rethook_trampoline.S @@ -24,7 +24,6 @@ cfi_st t6, PT_R18 cfi_st t7, PT_R19 cfi_st t8, PT_R20 - cfi_st u0, PT_R21 cfi_st fp, PT_R22 cfi_st s0, PT_R23 cfi_st s1, PT_R24 @@ -59,7 +58,6 @@ cfi_ld t6, PT_R18 cfi_ld t7, PT_R19 cfi_ld t8, PT_R20 - cfi_ld u0, PT_R21 cfi_ld fp, PT_R22 cfi_ld s0, PT_R23 cfi_ld s1, PT_R24 diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index 8572b63478bb98..482ba17bcd3d82 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -481,7 +481,6 @@ int kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst) srcu_read_unlock(&vcpu->kvm->srcu, idx); if (!ret) { kvm_complete_mmio_read(vcpu, run); - update_pc(&vcpu->arch); vcpu->mmio_needed = 0; return EMULATE_DONE; } diff --git a/arch/loongarch/kvm/intc/dmsintc.c b/arch/loongarch/kvm/intc/dmsintc.c index de25735ce03946..f5d3cdf291c324 100644 --- a/arch/loongarch/kvm/intc/dmsintc.c +++ b/arch/loongarch/kvm/intc/dmsintc.c @@ -19,8 +19,7 @@ void dmsintc_inject_irq(struct kvm_vcpu *vcpu) for (i = 0; i < 4; i++) { old = atomic64_read(&(ds->vector_map[i])); - if (old) - vector[i] = atomic64_xchg(&(ds->vector_map[i]), 0); + vector[i] = old ? atomic64_xchg(&(ds->vector_map[i]), 0) : 0; } if (vector[0]) { @@ -180,3 +179,8 @@ int kvm_loongarch_register_dmsintc_device(void) { return kvm_register_device_ops(&kvm_dmsintc_dev_ops, KVM_DEV_TYPE_LOONGARCH_DMSINTC); } + +void kvm_loongarch_unregister_dmsintc_device(void) +{ + kvm_unregister_device_ops(KVM_DEV_TYPE_LOONGARCH_DMSINTC); +} diff --git a/arch/loongarch/kvm/intc/eiointc.c b/arch/loongarch/kvm/intc/eiointc.c index 2b14485d14a708..5fd9d4c9238e18 100644 --- a/arch/loongarch/kvm/intc/eiointc.c +++ b/arch/loongarch/kvm/intc/eiointc.c @@ -691,3 +691,8 @@ int kvm_loongarch_register_eiointc_device(void) { return kvm_register_device_ops(&kvm_eiointc_dev_ops, KVM_DEV_TYPE_LOONGARCH_EIOINTC); } + +void kvm_loongarch_unregister_eiointc_device(void) +{ + kvm_unregister_device_ops(KVM_DEV_TYPE_LOONGARCH_EIOINTC); +} diff --git a/arch/loongarch/kvm/intc/ipi.c b/arch/loongarch/kvm/intc/ipi.c index 4fa0897d7bdb00..1e613741d7b8ed 100644 --- a/arch/loongarch/kvm/intc/ipi.c +++ b/arch/loongarch/kvm/intc/ipi.c @@ -466,3 +466,8 @@ int kvm_loongarch_register_ipi_device(void) { return kvm_register_device_ops(&kvm_ipi_dev_ops, KVM_DEV_TYPE_LOONGARCH_IPI); } + +void kvm_loongarch_unregister_ipi_device(void) +{ + kvm_unregister_device_ops(KVM_DEV_TYPE_LOONGARCH_IPI); +} diff --git a/arch/loongarch/kvm/intc/pch_pic.c b/arch/loongarch/kvm/intc/pch_pic.c index 175a630aceb416..40a4aa3000d62f 100644 --- a/arch/loongarch/kvm/intc/pch_pic.c +++ b/arch/loongarch/kvm/intc/pch_pic.c @@ -78,6 +78,9 @@ int pch_msi_set_irq(struct kvm *kvm, struct kvm_kernel_irq_routing_entry *e, int return dmsintc_set_irq(kvm, msg_addr, e->msi.data, level); } + if (e->msi.data >= EIOINTC_IRQS) + return -EINVAL; + eiointc_set_irq(kvm->arch.eiointc, e->msi.data, level); return 0; @@ -500,3 +503,8 @@ int kvm_loongarch_register_pch_pic_device(void) { return kvm_register_device_ops(&kvm_pch_pic_dev_ops, KVM_DEV_TYPE_LOONGARCH_PCHPIC); } + +void kvm_loongarch_unregister_pch_pic_device(void) +{ + kvm_unregister_device_ops(KVM_DEV_TYPE_LOONGARCH_PCHPIC); +} diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c index 3e1005526f4bd6..236523d2449d86 100644 --- a/arch/loongarch/kvm/main.c +++ b/arch/loongarch/kvm/main.c @@ -385,27 +385,52 @@ static int kvm_loongarch_env_init(void) /* Register LoongArch IPI interrupt controller interface. */ ret = kvm_loongarch_register_ipi_device(); if (ret) - return ret; + goto err_env; /* Register LoongArch EIOINTC interrupt controller interface. */ ret = kvm_loongarch_register_eiointc_device(); if (ret) - return ret; + goto err_ipi; /* Register LoongArch PCH-PIC interrupt controller interface. */ ret = kvm_loongarch_register_pch_pic_device(); if (ret) - return ret; + goto err_eiointc; /* Register LoongArch DMSINTC interrupt contrroller interface */ - if (cpu_has_msgint) + if (cpu_has_msgint) { ret = kvm_loongarch_register_dmsintc_device(); + if (ret) + goto err_pch_pic; + } + + return 0; + +err_pch_pic: + kvm_loongarch_unregister_pch_pic_device(); +err_eiointc: + kvm_loongarch_unregister_eiointc_device(); +err_ipi: + kvm_loongarch_unregister_ipi_device(); +err_env: + kvm_unregister_perf_callbacks(); + kfree(kvm_loongarch_ops); + kvm_loongarch_ops = NULL; + free_percpu(vmcs); + vmcs = NULL; return ret; } static void kvm_loongarch_env_exit(void) { + if (cpu_has_msgint) + kvm_loongarch_unregister_dmsintc_device(); + + kvm_loongarch_unregister_pch_pic_device(); + kvm_loongarch_unregister_eiointc_device(); + kvm_loongarch_unregister_ipi_device(); + if (vmcs) free_percpu(vmcs); @@ -428,7 +453,11 @@ static int kvm_loongarch_init(void) if (r) return r; - return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); + r = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); + if (r) + kvm_loongarch_env_exit(); + + return r; } static void kvm_loongarch_exit(void) diff --git a/arch/loongarch/kvm/mmu.c b/arch/loongarch/kvm/mmu.c index e104897aa53285..4ca7c6ea8f088a 100644 --- a/arch/loongarch/kvm/mmu.c +++ b/arch/loongarch/kvm/mmu.c @@ -383,6 +383,16 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, const struct kvm_memory_slot hva_t hva_start; size_t size, gpa_offset, hva_offset; + /* + * The generic code allocates a fresh, zeroed memslot for every change, + * so the arch flags computed below must be carried over when only the + * userspace flags change, e.g. when dirty logging is toggled. + */ + if (change == KVM_MR_FLAGS_ONLY) { + new->arch = old->arch; + return 0; + } + if ((change != KVM_MR_MOVE) && (change != KVM_MR_CREATE)) return 0; /* diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 20c207d80e31ae..ae6a6e30ab8267 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -300,10 +300,10 @@ static int kvm_pre_enter_guest(struct kvm_vcpu *vcpu) */ local_irq_disable(); kvm_deliver_exception(vcpu); + kvm_check_vpid(vcpu); /* Make sure the vcpu mode has been written */ smp_store_mb(vcpu->mode, IN_GUEST_MODE); kvm_deliver_intr(vcpu); - kvm_check_vpid(vcpu); /* * Called after function kvm_check_vpid() @@ -1165,10 +1165,14 @@ static int kvm_loongarch_cpucfg_set_attr(struct kvm_vcpu *vcpu, return -EINVAL; /* All vCPUs need set the same PV features */ + spin_lock(&kvm->arch.pv_setting_lock); if ((kvm->arch.pv_features & LOONGARCH_PV_FEAT_UPDATED) - && ((kvm->arch.pv_features & valid) != val)) + && ((kvm->arch.pv_features & valid) != val)) { + spin_unlock(&kvm->arch.pv_setting_lock); return -EINVAL; + } kvm->arch.pv_features = val | LOONGARCH_PV_FEAT_UPDATED; + spin_unlock(&kvm->arch.pv_setting_lock); return 0; default: return -ENXIO; diff --git a/arch/loongarch/kvm/vm.c b/arch/loongarch/kvm/vm.c index 1317c718f896af..86ba7d54e81f8f 100644 --- a/arch/loongarch/kvm/vm.c +++ b/arch/loongarch/kvm/vm.c @@ -76,6 +76,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) return -ENOMEM; } spin_lock_init(&kvm->arch.phyid_map_lock); + spin_lock_init(&kvm->arch.pv_setting_lock); kvm_init_vmcs(kvm); kvm_vm_init_features(kvm); diff --git a/arch/loongarch/mm/init.c b/arch/loongarch/mm/init.c index 3407030f3e7a2a..4b46c5d30708d8 100644 --- a/arch/loongarch/mm/init.c +++ b/arch/loongarch/mm/init.c @@ -237,15 +237,26 @@ pte_t invalid_pte_table[PTRS_PER_PTE] __page_aligned_bss; EXPORT_SYMBOL(invalid_pte_table); #if defined(CONFIG_EXECMEM) && defined(MODULES_VADDR) +#define MODULES_TEXT_START (MODULES_VADDR) +#define MODULES_TEXT_END (MODULES_VADDR + SZ_256M) +#define MODULES_DATA_START (MODULES_VADDR + SZ_256M) +#define MODULES_DATA_END (MODULES_END) + static struct execmem_info execmem_info __ro_after_init; struct execmem_info __init *execmem_arch_setup(void) { execmem_info = (struct execmem_info){ .ranges = { - [EXECMEM_DEFAULT] = { - .start = MODULES_VADDR, - .end = MODULES_END, + [EXECMEM_MODULE_TEXT] = { + .start = MODULES_TEXT_START, + .end = MODULES_TEXT_END, + .pgprot = PAGE_KERNEL, + .alignment = 1, + }, + [EXECMEM_MODULE_DATA] = { + .start = MODULES_DATA_START, + .end = MODULES_DATA_END, .pgprot = PAGE_KERNEL, .alignment = 1, }, diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index 29c281bef28efb..45eee761df9eef 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -124,6 +124,9 @@ static void prepare_bpf_tail_call_cnt(struct jit_ctx *ctx, int *store_offset) * | tcc | * +-------------------------+ * | tcc_ptr | + * +-------------------------+ + * | arena | + * | (optional) | * +-------------------------+ <--BPF_REG_FP * | prog->aux->stack_depth | * | (optional) | @@ -145,7 +148,7 @@ static void build_prologue(struct jit_ctx *ctx) stack_adjust += sizeof(long) * 2; if (ctx->arena_vm_start) - stack_adjust += 8; + stack_adjust += sizeof(long); stack_adjust = round_up(stack_adjust, 16); stack_adjust += bpf_stack_adjust; @@ -194,13 +197,13 @@ static void build_prologue(struct jit_ctx *ctx) store_offset -= sizeof(long); emit_insn(ctx, std, LOONGARCH_GPR_S5, LOONGARCH_GPR_SP, store_offset); + prepare_bpf_tail_call_cnt(ctx, &store_offset); + if (ctx->arena_vm_start) { store_offset -= sizeof(long); emit_insn(ctx, std, REG_ARENA, LOONGARCH_GPR_SP, store_offset); } - prepare_bpf_tail_call_cnt(ctx, &store_offset); - emit_insn(ctx, addid, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, stack_adjust); if (bpf_stack_adjust) @@ -241,21 +244,18 @@ static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call) load_offset -= sizeof(long); emit_insn(ctx, ldd, LOONGARCH_GPR_S5, LOONGARCH_GPR_SP, load_offset); + /* Only restore the TCC state into REG_TCC from the higher slot */ + load_offset -= sizeof(long); + emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, load_offset); + + /* Skip the unused local 'tcc_ptr' slot to align with arena */ + load_offset -= sizeof(long); + if (ctx->arena_vm_start) { load_offset -= sizeof(long); emit_insn(ctx, ldd, REG_ARENA, LOONGARCH_GPR_SP, load_offset); } - /* - * When push into the stack, follow the order of tcc then tcc_ptr. - * When pop from the stack, first pop tcc_ptr then followed by tcc. - */ - load_offset -= 2 * sizeof(long); - emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, load_offset); - - load_offset += sizeof(long); - emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, load_offset); - emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, stack_adjust); if (!is_tail_call) { @@ -290,17 +290,13 @@ bool bpf_jit_supports_far_kfunc_call(void) static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) { - int off, tc_ninsn = 0; + int off, jmp_offset; int tcc_ptr_off = BPF_TAIL_CALL_CNT_PTR_STACK_OFF(ctx->stack_size); u8 a1 = LOONGARCH_GPR_A1; u8 a2 = LOONGARCH_GPR_A2; u8 t1 = LOONGARCH_GPR_T1; u8 t2 = LOONGARCH_GPR_T2; u8 t3 = LOONGARCH_GPR_T3; - const int idx0 = ctx->idx; - -#define cur_offset (ctx->idx - idx0) -#define jmp_offset (tc_ninsn - (cur_offset)) /* * a0: &ctx @@ -310,12 +306,12 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) * if (index >= array->map.max_entries) * goto out; */ - tc_ninsn = insn ? ctx->offset[insn+1] - ctx->offset[insn] : ctx->offset[0]; emit_zext_32(ctx, a2, true); off = offsetof(struct bpf_array, map.max_entries); emit_insn(ctx, ldwu, t1, a1, off); /* bgeu $a2, $t1, jmp_offset */ + jmp_offset = ctx->image ? (ctx->offset[insn + 1] - ctx->idx) : 0; if (emit_tailcall_jmp(ctx, BPF_JGE, a2, t1, jmp_offset) < 0) goto toofar; @@ -326,6 +322,7 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, tcc_ptr_off); emit_insn(ctx, ldd, t3, REG_TCC, 0); emit_insn(ctx, addid, t2, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT); + jmp_offset = ctx->image ? (ctx->offset[insn + 1] - ctx->idx) : 0; if (emit_tailcall_jmp(ctx, BPF_JSGE, t3, t2, jmp_offset) < 0) goto toofar; @@ -340,6 +337,7 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) off = offsetof(struct bpf_array, ptrs); emit_insn(ctx, ldd, t2, t2, off); /* beq $t2, $zero, jmp_offset */ + jmp_offset = ctx->image ? (ctx->offset[insn + 1] - ctx->idx) : 0; if (emit_tailcall_jmp(ctx, BPF_JEQ, t2, LOONGARCH_GPR_ZERO, jmp_offset) < 0) goto toofar; @@ -355,8 +353,6 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) toofar: pr_info_once("tail_call: jump too far\n"); return -1; -#undef cur_offset -#undef jmp_offset } static void emit_store_stack_imm64(struct jit_ctx *ctx, int reg, int stack_off, u64 imm64) @@ -738,7 +734,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext move_reg(ctx, t1, src); emit_zext_32(ctx, t1, true); move_imm(ctx, dst, (ctx->user_vm_start >> 32) << 32, false); - emit_insn(ctx, beq, t1, LOONGARCH_GPR_ZERO, 1); + emit_insn(ctx, beq, t1, LOONGARCH_GPR_ZERO, 2); emit_insn(ctx, or, t1, dst, t1); move_reg(ctx, dst, t1); break; diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S index c7e90b09645e4d..18e68680471eb9 100644 --- a/arch/openrisc/kernel/entry.S +++ b/arch/openrisc/kernel/entry.S @@ -1223,15 +1223,50 @@ _no_syscall_trace: * */ +/* Keep this literal; hi()/lo() can't use the UL-suffixed TASK_SIZE. */ +#define OR1K_ATOMIC_ADDR_LIMIT 0x7ffffffc + ENTRY(sys_or1k_atomic) /* FIXME: This ignores r3 and always does an XCHG */ + + /* Check both user pointers before accessing them. */ + l.movhi r13,hi(OR1K_ATOMIC_ADDR_LIMIT) + l.ori r13,r13,lo(OR1K_ATOMIC_ADDR_LIMIT) + l.sfgtu r4,r13 + l.bf 9f + l.nop + l.sfgtu r5,r13 + l.bf 9f + l.nop + DISABLE_INTERRUPTS(r17,r19) - l.lwz r29,0(r4) - l.lwz r27,0(r5) - l.sw 0(r4),r27 - l.sw 0(r5),r29 +10: l.lwz r29,0(r4) +11: l.lwz r27,0(r5) +12: l.sw 0(r4),r27 +13: l.sw 0(r5),r29 ENABLE_INTERRUPTS(r17) l.jr r9 l.or r11,r0,r0 + /* + * Either pointer was outside user space, or turned out to be + * unmapped/inaccessible when we actually touched it. + */ +9: l.jr r9 + l.addi r11,r0,-EFAULT + + .section .fixup, "ax" +14: + ENABLE_INTERRUPTS(r17) + l.j 9b + l.nop + .previous + + .section __ex_table, "a" + .long 10b, 14b + .long 11b, 14b + .long 12b, 14b + .long 13b, 14b + .previous + /* ============================================================[ EOF ]=== */ diff --git a/arch/parisc/kernel/head.S b/arch/parisc/kernel/head.S index 9188c8d8743704..e84d7d6e1eb946 100644 --- a/arch/parisc/kernel/head.S +++ b/arch/parisc/kernel/head.S @@ -105,6 +105,7 @@ $iodc_panic: or %r10,%r10,%r10 /* qemu idle sleep */ msg1: .ascii "Can't boot kernel which was built for PA8x00 CPUs on this machine.\r\n" msg1_end: + .align 4 $cpu_ok: #endif diff --git a/arch/powerpc/include/asm/papr-watchdog.h b/arch/powerpc/include/asm/papr-watchdog.h new file mode 100644 index 00000000000000..bf876fc2caae55 --- /dev/null +++ b/arch/powerpc/include/asm/papr-watchdog.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_POWERPC_PAPR_WATCHDOG_H +#define _ASM_POWERPC_PAPR_WATCHDOG_H + +/* + * H_WATCHDOG Input + * + * R4: "flags": + * + * Bits 48-55: "operation" + */ +#define PSERIES_WDTF_OP_START 0x100UL /* start timer */ +#define PSERIES_WDTF_OP_STOP 0x200UL /* stop timer */ +#define PSERIES_WDTF_OP_QUERY 0x300UL /* query timer capabilities */ + +/* + * Bits 56-63: "timeoutAction" (for "Start Watchdog" only) + */ +#define PSERIES_WDTF_ACTION_HARD_POWEROFF 0x1UL /* poweroff */ +#define PSERIES_WDTF_ACTION_HARD_RESTART 0x2UL /* restart */ +#define PSERIES_WDTF_ACTION_DUMP_RESTART 0x3UL /* dump + restart */ + +/* + * R5: "watchdogNumber": + * PAPR says use -1 (all ones) to stop all watchdogs. + */ +#define PSERIES_WDT_NUM_ALL ((unsigned long)-1) + +/* + * H_WATCHDOG Output + * + * R3: Return code + * + * H_SUCCESS The operation completed. + * + * H_BUSY The hypervisor is too busy; retry the operation. + * + * H_PARAMETER The given "flags" are somehow invalid. Either the + * "operation" or "timeoutAction" is invalid, or a + * reserved bit is set. + * + * H_P2 The given "watchdogNumber" is zero or exceeds the + * supported maximum value. + * + * H_P3 The given "timeoutInMs" is below the supported + * minimum value. + * + * H_NOOP The given "watchdogNumber" is already stopped. + * + * H_HARDWARE The operation failed for ineffable reasons. + * + * H_FUNCTION The H_WATCHDOG hypercall is not supported by this + * hypervisor. + * + * R4: + * + * - For the "Query Watchdog Capabilities" operation, a 64-bit + * structure: + */ +#define PSERIES_WDTQ_MIN_TIMEOUT(cap) (((cap) >> 48) & 0xffff) +#define PSERIES_WDTQ_MAX_NUMBER(cap) (((cap) >> 32) & 0xffff) + +#endif /* _ASM_POWERPC_PAPR_WATCHDOG_H */ diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 3449dd2b577d4a..b9ab9df1e2bccc 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -1357,6 +1357,18 @@ #define PVR_ARCH_31 0x0f000006 #define PVR_ARCH_31_P11 0x0f000007 +/* + * Kernel-internal sentinel for invalid processor compatibility modes. + * PAPR specifies that the first byte of a valid logical PVR value is + * 0x0f. So 0xffffffff lies permanently outside the PAPR-defined range + * and is safe to repurpose. KVM stores it in vcpu->arch.arch_compat + * when userspace requests an unsupported compatibility mode (e.g., + * Power11 PVR on a Power11 host booted in Power10 compat). + * kvmppc_sanity_check() detects this and prevents the vCPU from + * running with an unsupported arch_compat. + */ +#define PVR_ARCH_INVALID 0xffffffff + /* Macros for setting and retrieving special purpose registers */ #ifndef __ASSEMBLER__ diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index ebc82f2744760a..2e9b102628d530 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -627,19 +627,14 @@ int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size) return -ENXIO; addr = hose->io_base_virt + port; - /* WARNING: The generic code is idiotic. It gets passed a pointer - * to what can be a 1, 2 or 4 byte quantity and always reads that - * as a u32, which means that we have to correct the location of - * the data read within those 32 bits for size 1 and 2 - */ switch(size) { case 1: - out_8(addr, val >> 24); + out_8(addr, val); return 1; case 2: if (port & 1) return -EINVAL; - out_le16(addr, val >> 16); + out_le16(addr, val); return 2; case 4: if (port & 3) diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c index 8c72e12ea44e5a..6075b1c88511a0 100644 --- a/arch/powerpc/kexec/file_load_64.c +++ b/arch/powerpc/kexec/file_load_64.c @@ -664,7 +664,7 @@ unsigned int kexec_extra_fdt_size_ppc64(struct kimage *image, struct crash_mem * extra_size += (cpu_nodes - boot_cpu_node_count) * cpu_node_size(); /* Consider extra space for reserved memory ranges if any */ - if (rmem->nr_ranges > 0) + if (rmem && rmem->nr_ranges > 0) extra_size += sizeof(struct fdt_reserve_entry) * rmem->nr_ranges; return extra_size + kdump_extra_fdt_size_ppc64(image, cpu_nodes); diff --git a/arch/powerpc/kexec/ranges.c b/arch/powerpc/kexec/ranges.c index 867135560e5c8b..eb45e89502caee 100644 --- a/arch/powerpc/kexec/ranges.c +++ b/arch/powerpc/kexec/ranges.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -105,19 +106,16 @@ static void __merge_memory_ranges(struct crash_mem *mem_rngs) struct range *ranges; int i, idx; - if (!mem_rngs) + if (!mem_rngs || mem_rngs->nr_ranges <= 1) return; idx = 0; - ranges = &(mem_rngs->ranges[0]); + ranges = mem_rngs->ranges; for (i = 1; i < mem_rngs->nr_ranges; i++) { - if (ranges[i].start <= (ranges[i-1].end + 1)) - ranges[idx].end = ranges[i].end; + if (ranges[i].start <= (ranges[idx].end + 1)) + ranges[idx].end = max(ranges[idx].end, ranges[i].end); else { idx++; - if (i == idx) - continue; - ranges[idx] = ranges[i]; } } diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 61dbeea317f3b7..f9380ef657500d 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -446,7 +446,19 @@ static int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat) guest_pcr_bit = PCR_ARCH_300; break; case PVR_ARCH_31: + guest_pcr_bit = PCR_ARCH_31; + break; case PVR_ARCH_31_P11: + /* + * Need to check this for ISA 3.1, as Power10 and + * Power11 share the same PCR. For any subsequent ISA + * versions, this will be taken care of by the guest vs + * host PCR comparison below. + */ + if (!cpu_has_feature(CPU_FTR_P11_PVR)) { + arch_compat = PVR_ARCH_INVALID; + goto out; + } guest_pcr_bit = PCR_ARCH_31; break; default: @@ -469,6 +481,7 @@ static int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat) return -EINVAL; } +out: spin_lock(&vc->lock); vc->arch_compat = arch_compat; kvmhv_nestedv2_mark_dirty(vcpu, KVMPPC_GSID_LOGICAL_PVR); @@ -479,7 +492,7 @@ static int kvmppc_set_arch_compat(struct kvm_vcpu *vcpu, u32 arch_compat) vc->pcr = (host_pcr_bit - guest_pcr_bit) | PCR_MASK; spin_unlock(&vc->lock); - return 0; + return kvmppc_sanity_check(vcpu); } static void kvmppc_dump_regs(struct kvm_vcpu *vcpu) diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 00302399fc37b4..b6b83fe3233f35 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -258,6 +258,12 @@ int kvmppc_sanity_check(struct kvm_vcpu *vcpu) if (!vcpu->arch.pvr) goto out; +#if defined(CONFIG_KVM_BOOK3S_HV_POSSIBLE) + if (vcpu->arch.vcore && + vcpu->arch.vcore->arch_compat == PVR_ARCH_INVALID) + goto out; +#endif + /* PAPR only works with book3s_64 */ if ((vcpu->arch.cpu_type != KVM_CPU_3S_64) && vcpu->arch.papr_enabled) goto out; diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c index 10aced261cff4d..cf692b2b5f7bc5 100644 --- a/arch/powerpc/mm/book3s64/radix_pgtable.c +++ b/arch/powerpc/mm/book3s64/radix_pgtable.c @@ -1314,7 +1314,6 @@ int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn, * covering out both edges. */ unsigned long addr; - unsigned long addr_pfn = start_pfn; unsigned long next; pgd_t *pgd; p4d_t *p4d; @@ -1335,7 +1334,6 @@ int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn, if (pmd_leaf(READ_ONCE(*pmd))) { /* existing huge mapping. Skip the range */ - addr_pfn += (PMD_SIZE >> PAGE_SHIFT); next = pmd_addr_end(addr, end); continue; } @@ -1348,11 +1346,11 @@ int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn, * page whose VMEMMAP_RESERVE_NR pages were mapped and * this request fall in those pages. */ - addr_pfn += 1; next = addr + PAGE_SIZE; continue; } else { unsigned long nr_pages = pgmap_vmemmap_nr(pgmap); + unsigned long addr_pfn = page_to_pfn((struct page *)addr); unsigned long pfn_offset = addr_pfn - ALIGN_DOWN(addr_pfn, nr_pages); pte_t *tail_page_pte; @@ -1376,7 +1374,6 @@ int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn, if (!pte) return -ENOMEM; - addr_pfn += 2; next = addr + 2 * PAGE_SIZE; continue; } @@ -1392,7 +1389,6 @@ int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn, return -ENOMEM; vmemmap_verify(pte, node, addr, addr + PAGE_SIZE); - addr_pfn += 1; next = addr + PAGE_SIZE; continue; } @@ -1402,7 +1398,6 @@ int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn, return -ENOMEM; vmemmap_verify(pte, node, addr, addr + PAGE_SIZE); - addr_pfn += 1; next = addr + PAGE_SIZE; continue; } diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index da72a30ab8657e..973f58771d9636 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -1471,7 +1471,7 @@ static int __init pmac_i2c_create_platform_devices(void) if (bus->platform_dev == NULL) return -ENOMEM; bus->platform_dev->dev.platform_data = bus; - bus->platform_dev->dev.of_node = bus->busnode; + bus->platform_dev->dev.of_node = of_node_get(bus->busnode); platform_device_add(bus->platform_dev); } diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 3e1f915fe4f66d..272e9aab66d4f6 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -812,18 +812,11 @@ static struct device_node *pci_dma_find(struct device_node *dn, /* parse DMA window property. During normal system boot, only default * DMA window is passed in OF. But, for kdump, a dedicated adapter might - * have both default and DDW in FDT. In this scenario, DDW takes precedence - * over default window. + * have both default and DDW in FDT. In this scenario, default window + * takes precedence over DDW. For a dedicated adapter, default window will + * potentially have more unused TCEs. */ - if (ddw_win) { - struct dynamic_dma_window_prop *p; - - p = (struct dynamic_dma_window_prop *)ddw_prop; - prop->liobn = p->liobn; - prop->dma_base = p->dma_base; - prop->tce_shift = p->tce_shift; - prop->window_shift = p->window_shift; - } else if (default_win) { + if (default_win) { unsigned long offset, size, liobn; of_parse_dma_window(rdn, default_prop, &liobn, &offset, &size); @@ -832,6 +825,14 @@ static struct device_node *pci_dma_find(struct device_node *dn, prop->dma_base = cpu_to_be64(offset); prop->tce_shift = cpu_to_be32(IOMMU_PAGE_SHIFT_4K); prop->window_shift = cpu_to_be32(order_base_2(size)); + } else { + struct dynamic_dma_window_prop *p; + + p = (struct dynamic_dma_window_prop *)ddw_prop; + prop->liobn = p->liobn; + prop->dma_base = p->dma_base; + prop->tce_shift = p->tce_shift; + prop->window_shift = p->window_shift; } return rdn; diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 1223dc9612429a..f29e7547c99566 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -77,6 +77,7 @@ #include #include #include +#include #include "pseries.h" @@ -185,14 +186,37 @@ static void __init fwnmi_init(void) #endif } +static void pseries_crash_stop_watchdogs(void) +{ + long rc; + + rc = plpar_hcall_norets_notrace(H_WATCHDOG, PSERIES_WDTF_OP_STOP, + PSERIES_WDT_NUM_ALL); + if (rc != H_SUCCESS && rc != H_NOOP) + pr_warn("Could not stop watchdogs before kdump rc=%ld\n", rc); +} + /* * Affix a device for the first timer to the platform bus if * we have firmware support for the H_WATCHDOG hypercall. */ static __init int pseries_wdt_init(void) { - if (firmware_has_feature(FW_FEATURE_WATCHDOG)) - platform_device_register_simple("pseries-wdt", 0, NULL, 0); + struct platform_device *pdev; + + if (!firmware_has_feature(FW_FEATURE_WATCHDOG)) + return 0; + + pdev = platform_device_register_simple("pseries-wdt", 0, NULL, 0); + + if (IS_ERR(pdev)) { + pr_err("Failed to register pseries-wdt platform device\n"); + return PTR_ERR(pdev); + } + + if (crash_shutdown_register(pseries_crash_stop_watchdogs)) + pr_warn("Could not register watchdog crash shutdown handler\n"); + return 0; } machine_subsys_initcall(pseries, pseries_wdt_init); diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts index 72f77e9edd63db..d2abda5f5383aa 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -204,14 +204,14 @@ regulators { buck1 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; buck2 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; diff --git a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts index 2fc8d653378637..c800153077a80c 100644 --- a/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts +++ b/arch/riscv/boot/dts/spacemit/k1-milkv-jupiter.dts @@ -211,14 +211,14 @@ regulators { buck1 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; buck2 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; diff --git a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts index 0d809e4ad3b1f7..96623454116eb7 100644 --- a/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts +++ b/arch/riscv/boot/dts/spacemit/k1-musepi-pro.dts @@ -180,14 +180,14 @@ regulators { buck1 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; buck2 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts index b13a8d6a2670e3..564a48c70b5d31 100644 --- a/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts +++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-r2s.dts @@ -126,14 +126,14 @@ regulators { buck1 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; buck2 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; diff --git a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts index 7c49bce427f307..b907a1396b21ff 100644 --- a/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts +++ b/arch/riscv/boot/dts/spacemit/k1-orangepi-rv2.dts @@ -164,14 +164,14 @@ regulators { buck1 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; buck2 { regulator-min-microvolt = <500000>; - regulator-max-microvolt = <3450000>; + regulator-max-microvolt = <1050000>; regulator-ramp-delay = <5000>; regulator-always-on; }; diff --git a/arch/riscv/include/asm/acpi.h b/arch/riscv/include/asm/acpi.h index 26ab37c171bcf6..f4db04f5bbe758 100644 --- a/arch/riscv/include/asm/acpi.h +++ b/arch/riscv/include/asm/acpi.h @@ -12,6 +12,8 @@ #ifndef _ASM_ACPI_H #define _ASM_ACPI_H +#include + /* Basic configuration for ACPI */ #ifdef CONFIG_ACPI @@ -67,6 +69,23 @@ int acpi_get_riscv_isa(struct acpi_table_header *table, void acpi_get_cbo_block_size(struct acpi_table_header *table, u32 *cbom_size, u32 *cboz_size, u32 *cbop_size); + +/* + * RISC-V Functional Fixed Hardware Specification Version v1.0.1, + * Chapter 3.1.2, Table 4: Arch. Context Lost Flags + */ +#define RISCV_LPI_HART_TIMER_CTXT_LOST BIT(0) + +static inline unsigned int arch_get_idle_state_flags(u32 arch_flags) +{ + if (arch_flags & RISCV_LPI_HART_TIMER_CTXT_LOST) + return CPUIDLE_FLAG_TIMER_STOP; + + return 0; +} + +#define arch_get_idle_state_flags arch_get_idle_state_flags + #else static inline void acpi_init_rintc_map(void) { } static inline struct acpi_madt_rintc *acpi_cpu_get_madt_rintc(int cpu) diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index 36201613d2cafe..67f1a03716597a 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -288,18 +287,9 @@ static void check_vector_unaligned_access(struct work_struct *work __always_unus __free_pages(page, MISALIGNED_BUFFER_ORDER); } -/* Measure unaligned access speed on all CPUs present at boot in parallel. */ -static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) -{ - schedule_on_each_cpu(check_vector_unaligned_access); - riscv_hwprobe_complete_async_probe(); - - return 0; -} #else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */ -static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) +static void check_vector_unaligned_access(struct work_struct *work __always_unused) { - return 0; } #endif @@ -387,12 +377,7 @@ static int __init check_unaligned_access_all_cpus(void) per_cpu(vector_misaligned_access, cpu) = unaligned_vector_speed_param; } else if (!check_vector_unaligned_access_emulated_all_cpus() && IS_ENABLED(CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS)) { - riscv_hwprobe_register_async_probe(); - if (IS_ERR(kthread_run(vec_check_unaligned_access_speed_all_cpus, - NULL, "vec_check_unaligned_access_speed_all_cpus"))) { - pr_warn("Failed to create vec_unalign_check kthread\n"); - riscv_hwprobe_complete_async_probe(); - } + schedule_on_each_cpu(check_vector_unaligned_access); } /* diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c index 2025b664961c80..a7f948410d5310 100644 --- a/arch/riscv/kvm/vcpu_pmu.c +++ b/arch/riscv/kvm/vcpu_pmu.c @@ -12,7 +12,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -479,13 +481,14 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low unsigned long flags, struct kvm_vcpu_sbi_return *retdata) { struct riscv_pmu_event_info *einfo = NULL; - int shmem_size = num_events * sizeof(*einfo); + size_t shmem_size; gpa_t shmem; u32 eidx, etype; u64 econfig; int ret; - if (flags != 0 || (saddr_low & (SZ_16 - 1) || num_events == 0)) { + if (flags != 0 || (saddr_low & (SZ_16 - 1)) || num_events == 0 || + check_mul_overflow(num_events, sizeof(*einfo), &shmem_size)) { ret = SBI_ERR_INVALID_PARAM; goto out; } @@ -500,7 +503,8 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low } } - einfo = kzalloc(shmem_size, GFP_KERNEL); + einfo = kvcalloc(num_events, sizeof(*einfo), + GFP_KERNEL_ACCOUNT | __GFP_NOWARN); if (!einfo) { ret = SBI_ERR_FAILURE; goto out; @@ -512,7 +516,7 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low goto free_mem; } - for (int i = 0; i < num_events; i++) { + for (unsigned long i = 0; i < num_events; i++) { eidx = einfo[i].event_idx; etype = kvm_pmu_get_perf_event_type(eidx); econfig = kvm_pmu_get_perf_event_config(eidx, einfo[i].event_data); @@ -525,7 +529,7 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low ret = SBI_ERR_INVALID_ADDRESS; free_mem: - kfree(einfo); + kvfree(einfo); out: retdata->err_val = ret; diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c index f9d5347ba966b7..0942116e59bc10 100644 --- a/arch/riscv/net/bpf_jit_comp64.c +++ b/arch/riscv/net/bpf_jit_comp64.c @@ -2114,7 +2114,15 @@ bool bpf_jit_supports_ptr_xchg(void) bool bpf_jit_supports_arena(void) { - return true; + /* + * The arena range tree uses kmalloc_nolock(), which needs + * cmpxchg128, provided by ZACAS on riscv. + */ +#ifdef system_has_cmpxchg128 + return system_has_cmpxchg128(); +#else + return false; +#endif } bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena) diff --git a/arch/s390/include/asm/nmi.h b/arch/s390/include/asm/nmi.h index 6454c153185441..7919b2b9ac9cc8 100644 --- a/arch/s390/include/asm/nmi.h +++ b/arch/s390/include/asm/nmi.h @@ -22,6 +22,7 @@ #define MCCK_CODE_SYSTEM_DAMAGE BIT(63) #define MCCK_CODE_EXT_DAMAGE BIT(63 - 5) #define MCCK_CODE_CP BIT(63 - 9) +#define MCCK_CODE_CK BIT(63 - 11) #define MCCK_CODE_STG_ERROR BIT(63 - 16) #define MCCK_CODE_STG_KEY_ERROR BIT(63 - 18) #define MCCK_CODE_STG_DEGRAD BIT(63 - 19) @@ -33,6 +34,8 @@ #define MCCK_CODE_FC_VALID BIT(63 - 43) #define MCCK_CODE_CPU_TIMER_VALID BIT(63 - 46) +#define MCCK_CODE_NO_GUEST (MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE | MCCK_CODE_CK) + #ifndef __ASSEMBLER__ union mci { diff --git a/arch/s390/include/asm/percpu.h b/arch/s390/include/asm/percpu.h index 1d955dd0defa3f..feb58acf87d69f 100644 --- a/arch/s390/include/asm/percpu.h +++ b/arch/s390/include/asm/percpu.h @@ -107,8 +107,8 @@ " .endif\n" \ ".endr\n" \ ".endm\n" \ - ALTERNATIVE("GEN_MVIY " __stringify(disp) " " __stringify(reg) "\n", \ - "GEN_MVIY " __stringify(dispalt) " " __stringify(reg) "\n", \ + ALTERNATIVE("GEN_MVIY " disp ", " reg "\n", \ + "GEN_MVIY " dispalt ", " reg "\n", \ ALT_FEATURE(MFEATURE_LOWCORE)) \ ".purgem GEN_MVIY\n" diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index e17a59d4d5a41c..17297a8b63d951 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c @@ -344,8 +344,7 @@ static void notrace s390_backup_mcck_info(struct pt_regs *regs) sie_page = container_of(sie_block, struct sie_page, sie_block); mcck_backup = &sie_page->mcck_info; - mcck_backup->mcic = get_lowcore()->mcck_interruption_code & - ~(MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE); + mcck_backup->mcic = get_lowcore()->mcck_interruption_code & ~MCCK_CODE_NO_GUEST; mcck_backup->ext_damage_code = get_lowcore()->external_damage_code; mcck_backup->failing_storage_address = get_lowcore()->failing_storage_address; } @@ -357,8 +356,6 @@ NOKPROBE_SYMBOL(s390_backup_mcck_info); #define ED_STP_ISLAND 6 /* External damage STP island check */ #define ED_STP_SYNC 7 /* External damage STP sync check */ -#define MCCK_CODE_NO_GUEST (MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE) - /* * machine check handler. */ diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 2076ac22e2c497..2a8d4cc1d6a2ca 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -110,6 +110,7 @@ struct cpu_cf_ptr { static struct cpu_cf_root { /* Anchor to per CPU data */ refcount_t refcnt; /* Overall active events */ + unsigned int tskctx; /* Users tracking all CPUs (cpu == -1) */ struct cpu_cf_ptr __percpu *cfptr; } cpu_cf_root; @@ -118,13 +119,15 @@ static struct cpu_cf_root { /* Anchor to per CPU data */ * user space in task context with perf_event_open() and close() * system calls. * - * This mutex serializes functions cpum_cf_alloc_cpu() called at event - * initialization via cpumf_pmu_event_init() and function cpum_cf_free_cpu() - * called at event removal via call back function hw_perf_event_destroy() - * when the event is deleted. They are serialized to enforce correct - * bookkeeping of pointer and reference counts anchored by - * struct cpu_cf_root and the access to cpu_cf_root::refcnt and the - * per CPU pointers stored in cpu_cf_root::cfptr. + * This mutex serializes the allocation and removal of the per CPU counter + * data via cpum_cf_alloc_cpu() and cpum_cf_free_cpu(). They are called with + * this mutex held at event initialization via cpumf_pmu_event_init(), at + * event removal via call back function hw_perf_event_destroy() when the + * event is deleted, and from the CPU hotplug prepare/dead callbacks. The + * mutex enforces correct bookkeeping of pointer and reference counts + * anchored by struct cpu_cf_root and protects the access to + * cpu_cf_root::refcnt, cpu_cf_root::tskctx and the per CPU pointers + * stored in cpu_cf_root::cfptr. */ static DEFINE_MUTEX(pmc_reserve_mutex); @@ -167,12 +170,14 @@ static void cpum_cf_reset_cpu(void *flags) } /* Free per CPU data when the last event is removed. */ -static void cpum_cf_free_root(void) +static void cpum_cf_free_root(unsigned int num) { - if (!refcount_dec_and_test(&cpu_cf_root.refcnt)) + struct cpu_cf_ptr __percpu *p = cpu_cf_root.cfptr; + + if (!refcount_sub_and_test(num, &cpu_cf_root.refcnt)) return; - free_percpu(cpu_cf_root.cfptr); cpu_cf_root.cfptr = NULL; + free_percpu(p); irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); on_each_cpu(cpum_cf_reset_cpu, NULL, 1); debug_sprintf_event(cf_dbg, 4, "%s root.refcnt %u cfptr %d\n", @@ -186,17 +191,17 @@ static void cpum_cf_free_root(void) * CPUs possible, which might be larger than the number of CPUs currently * online. */ -static int cpum_cf_alloc_root(void) +static int cpum_cf_alloc_root(unsigned int num) { int rc = 0; - if (refcount_inc_not_zero(&cpu_cf_root.refcnt)) + if (refcount_add_not_zero(num, &cpu_cf_root.refcnt)) return rc; /* The memory is already zeroed. */ cpu_cf_root.cfptr = alloc_percpu(struct cpu_cf_ptr); if (cpu_cf_root.cfptr) { - refcount_set(&cpu_cf_root.refcnt, 1); + refcount_set(&cpu_cf_root.refcnt, num); on_each_cpu(cpum_cf_reset_cpu, NULL, 1); irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); } else { @@ -206,20 +211,23 @@ static int cpum_cf_alloc_root(void) return rc; } -/* Free CPU counter data structure for a PMU */ -static void cpum_cf_free_cpu(int cpu) +/* + * Remove num references to the CPU counter data structure of a PMU. + * Called with pmc_reserve_mutex held. + */ +static void cpum_cf_free_cpu(int cpu, unsigned int num) { struct cpu_cf_events *cpuhw; struct cpu_cf_ptr *p; - mutex_lock(&pmc_reserve_mutex); + lockdep_assert_held(&pmc_reserve_mutex); /* * When invoked via CPU hotplug handler, there might be no events * installed or that particular CPU might not have an * event installed. This anchor pointer can be NULL! */ if (!cpu_cf_root.cfptr) - goto out; + return; p = per_cpu_ptr(cpu_cf_root.cfptr, cpu); cpuhw = p->cpucf; /* @@ -227,28 +235,29 @@ static void cpum_cf_free_cpu(int cpu) * installed on that CPU, but on different CPUs. */ if (!cpuhw) - goto out; + return; - if (refcount_dec_and_test(&cpuhw->refcnt)) { - kfree(cpuhw); + if (refcount_sub_and_test(num, &cpuhw->refcnt)) { p->cpucf = NULL; + kfree(cpuhw); } - cpum_cf_free_root(); -out: - mutex_unlock(&pmc_reserve_mutex); + cpum_cf_free_root(num); } -/* Allocate CPU counter data structure for a PMU. Called under mutex lock. */ -static int cpum_cf_alloc_cpu(int cpu) +/* + * Add num references to the CPU counter data structure of a PMU and + * allocate it when necessary. Called with pmc_reserve_mutex held. + */ +static int cpum_cf_alloc_cpu(int cpu, unsigned int num) { struct cpu_cf_events *cpuhw; struct cpu_cf_ptr *p; int rc; - mutex_lock(&pmc_reserve_mutex); - rc = cpum_cf_alloc_root(); + lockdep_assert_held(&pmc_reserve_mutex); + rc = cpum_cf_alloc_root(num); if (rc) - goto unlock; + return rc; p = per_cpu_ptr(cpu_cf_root.cfptr, cpu); cpuhw = p->cpucf; @@ -256,12 +265,12 @@ static int cpum_cf_alloc_cpu(int cpu) cpuhw = kzalloc_obj(*cpuhw); if (cpuhw) { p->cpucf = cpuhw; - refcount_set(&cpuhw->refcnt, 1); + refcount_set(&cpuhw->refcnt, num); } else { rc = -ENOMEM; } } else { - refcount_inc(&cpuhw->refcnt); + refcount_add(num, &cpuhw->refcnt); } if (rc) { /* @@ -269,10 +278,8 @@ static int cpum_cf_alloc_cpu(int cpu) * cpu_cf_event in not created, its destroy() function is not * invoked. Adjust the reference counter for the anchor. */ - cpum_cf_free_root(); + cpum_cf_free_root(num); } -unlock: - mutex_unlock(&pmc_reserve_mutex); return rc; } @@ -284,39 +291,70 @@ static int cpum_cf_alloc_cpu(int cpu) * perf_event_open() with task context and /dev/hwctr interface. * If cpu is non-zero install event on this CPU only. This setup handles * perf_event_open() with CPU context. + * Users with cpu == -1 are counted in cpu_cf_root::tskctx. The CPU hotplug + * prepare and dead callbacks use this count to install and remove the per + * CPU counter data on a new or dying CPU. */ -static int cpum_cf_alloc(int cpu) +static int cpum_cf_alloc_cpuslocked(int cpu) { cpumask_var_t mask; int rc; + lockdep_assert_cpus_held(); if (cpu == -1) { if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) return -ENOMEM; + mutex_lock(&pmc_reserve_mutex); for_each_online_cpu(cpu) { - rc = cpum_cf_alloc_cpu(cpu); + rc = cpum_cf_alloc_cpu(cpu, 1); if (rc) { for_each_cpu(cpu, mask) - cpum_cf_free_cpu(cpu); + cpum_cf_free_cpu(cpu, 1); break; } cpumask_set_cpu(cpu, mask); } + if (!rc) + cpu_cf_root.tskctx++; + mutex_unlock(&pmc_reserve_mutex); free_cpumask_var(mask); } else { - rc = cpum_cf_alloc_cpu(cpu); + mutex_lock(&pmc_reserve_mutex); + rc = cpum_cf_alloc_cpu(cpu, 1); + mutex_unlock(&pmc_reserve_mutex); } return rc; } -static void cpum_cf_free(int cpu) +static int cpum_cf_alloc(int cpu) +{ + int rc; + + cpus_read_lock(); + rc = cpum_cf_alloc_cpuslocked(cpu); + cpus_read_unlock(); + return rc; +} + +static void cpum_cf_free_cpuslocked(int cpu) { + lockdep_assert_cpus_held(); + mutex_lock(&pmc_reserve_mutex); if (cpu == -1) { + cpu_cf_root.tskctx--; for_each_online_cpu(cpu) - cpum_cf_free_cpu(cpu); + cpum_cf_free_cpu(cpu, 1); } else { - cpum_cf_free_cpu(cpu); + cpum_cf_free_cpu(cpu, 1); } + mutex_unlock(&pmc_reserve_mutex); +} + +static void cpum_cf_free(int cpu) +{ + cpus_read_lock(); + cpum_cf_free_cpuslocked(cpu); + cpus_read_unlock(); } #define CF_DIAG_CTRSET_DEF 0xfeef /* Counter set header mark */ @@ -1090,53 +1128,67 @@ static refcount_t cfset_opencnt = REFCOUNT_INIT(0); /* Access count */ static DEFINE_MUTEX(cfset_ctrset_mutex); /* - * CPU hotplug handles only /dev/hwctr device. - * For perf_event_open() the CPU hotplug handling is done on kernel common - * code: + * CPU hotplug handling: + * + * cpum_cf_prepare_cpu() and cpum_cf_dead_cpu() run while the new or dying + * CPU is offline. They create and remove the per CPU counter data for all + * users tracking every CPU (cpu == -1), that is perf_event_open() events + * with task context and /dev/hwctr device sessions. Each such user holds + * one reference to the per CPU counter data of each CPU. Therefore install + * and remove one reference per user, tracked in cpu_cf_root::tskctx. This + * guarantees the per CPU counter data exists before the new CPU executes + * its first task and is removed only after the dying CPU is gone. + * + * cpum_cf_online_cpu() and cpum_cf_offline_cpu() run while the new or + * dying CPU is online. They handle only the counter set state of open + * /dev/hwctr device sessions on that CPU. For perf_event_open() events + * nothing is done: * - CPU add: Nothing is done since a file descriptor can not be created * and returned to the user. * - CPU delete: Handled by common code via pmu_disable(), pmu_stop() and - * pmu_delete(). The event itself is removed when the file descriptor is - * closed. + * pmu_delete(). During task exit processing of grouped perf events + * triggered by CPU hotplug processing, pmu_disable() is called as part + * of perf context removal process. The event itself is removed when the + * event file descriptor is closed. */ +static int cpum_cf_prepare_cpu(unsigned int cpu) +{ + int rc = 0; + + mutex_lock(&pmc_reserve_mutex); + if (cpu_cf_root.tskctx) + rc = cpum_cf_alloc_cpu(cpu, cpu_cf_root.tskctx); + mutex_unlock(&pmc_reserve_mutex); + return rc; +} + +static int cpum_cf_dead_cpu(unsigned int cpu) +{ + mutex_lock(&pmc_reserve_mutex); + if (cpu_cf_root.tskctx) + cpum_cf_free_cpu(cpu, cpu_cf_root.tskctx); + mutex_unlock(&pmc_reserve_mutex); + return 0; +} + static int cfset_online_cpu(unsigned int cpu); static int cpum_cf_online_cpu(unsigned int cpu) { - int rc = 0; - - /* - * Ignore notification for perf_event_open(). - * Handle only /dev/hwctr device sessions. - */ mutex_lock(&cfset_ctrset_mutex); - if (refcount_read(&cfset_opencnt)) { - rc = cpum_cf_alloc_cpu(cpu); - if (!rc) - cfset_online_cpu(cpu); - } + if (refcount_read(&cfset_opencnt)) + cfset_online_cpu(cpu); mutex_unlock(&cfset_ctrset_mutex); - return rc; + return 0; } static int cfset_offline_cpu(unsigned int cpu); static int cpum_cf_offline_cpu(unsigned int cpu) { - /* - * During task exit processing of grouped perf events triggered by CPU - * hotplug processing, pmu_disable() is called as part of perf context - * removal process. Therefore do not trigger event removal now for - * perf_event_open() created events. Perf common code triggers event - * destruction when the event file descriptor is closed. - * - * Handle only /dev/hwctr device sessions. - */ mutex_lock(&cfset_ctrset_mutex); - if (refcount_read(&cfset_opencnt)) { + if (refcount_read(&cfset_opencnt)) cfset_offline_cpu(cpu); - cpum_cf_free_cpu(cpu); - } mutex_unlock(&cfset_ctrset_mutex); return 0; } @@ -1183,7 +1235,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code, static int cfset_init(void); static int __init cpumf_pmu_init(void) { - int rc; + int state, rc; /* Extract counter measurement facility information */ if (!cpum_cf_avail() || qctri(&cpumf_ctr_info)) @@ -1225,11 +1277,24 @@ static int __init cpumf_pmu_init(void) cfset_init(); } + rc = cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, + "perf/s390/cf:prepare", + cpum_cf_prepare_cpu, cpum_cf_dead_cpu); + if (rc < 0) + goto out3; + state = rc; + rc = cpuhp_setup_state(CPUHP_AP_PERF_S390_CF_ONLINE, "perf/s390/cf:online", cpum_cf_online_cpu, cpum_cf_offline_cpu); - return rc; + if (rc < 0) + goto out4; + return 0; +out4: + cpuhp_remove_state(state); +out3: + perf_pmu_unregister(&cpumf_pmu); out2: debug_unregister_view(cf_dbg, &debug_sprintf_view); debug_unregister(cf_dbg); @@ -1385,6 +1450,7 @@ static void cfset_all_stop(struct cfset_request *req) */ static int cfset_release(struct inode *inode, struct file *file) { + cpus_read_lock(); mutex_lock(&cfset_ctrset_mutex); /* Open followed by close/exit has no private_data */ if (file->private_data) { @@ -1395,9 +1461,10 @@ static int cfset_release(struct inode *inode, struct file *file) } if (refcount_dec_and_test(&cfset_opencnt)) { /* Last close */ on_each_cpu(cfset_release_cpu, NULL, 1); - cpum_cf_free(-1); + cpum_cf_free_cpuslocked(-1); } mutex_unlock(&cfset_ctrset_mutex); + cpus_read_unlock(); return 0; } @@ -1416,15 +1483,17 @@ static int cfset_open(struct inode *inode, struct file *file) return -EPERM; file->private_data = NULL; + cpus_read_lock(); mutex_lock(&cfset_ctrset_mutex); if (!refcount_inc_not_zero(&cfset_opencnt)) { /* First open */ - rc = cpum_cf_alloc(-1); + rc = cpum_cf_alloc_cpuslocked(-1); if (!rc) { cfset_session_init(); refcount_set(&cfset_opencnt, 1); } } mutex_unlock(&cfset_ctrset_mutex); + cpus_read_unlock(); /* nonseekable_open() never fails */ return rc ?: nonseekable_open(inode, file); diff --git a/arch/s390/kvm/guestdbg.c b/arch/s390/kvm/guestdbg.c index 69835e1d4f207e..9a6149e310bbff 100644 --- a/arch/s390/kvm/guestdbg.c +++ b/arch/s390/kvm/guestdbg.c @@ -184,7 +184,7 @@ static int __import_wp_info(struct kvm_vcpu *vcpu, if (wp_info->len < 0 || wp_info->len > MAX_WP_SIZE) return -EINVAL; - wp_info->old_data = kmalloc(bp_data->len, GFP_KERNEL_ACCOUNT); + wp_info->old_data = kmalloc(wp_info->len, GFP_KERNEL_ACCOUNT); if (!wp_info->old_data) return -ENOMEM; /* try to backup the original value */ @@ -252,7 +252,7 @@ int kvm_s390_import_bp_data(struct kvm_vcpu *vcpu, ret = __import_wp_info(vcpu, &bp_data[i], &wp_info[nr_wp]); if (ret) - goto error; + goto error_wp; nr_wp++; break; case KVM_HW_BP: @@ -267,7 +267,12 @@ int kvm_s390_import_bp_data(struct kvm_vcpu *vcpu, vcpu->arch.guestdbg.hw_bp_info = bp_info; vcpu->arch.guestdbg.nr_hw_wp = nr_wp; vcpu->arch.guestdbg.hw_wp_info = wp_info; + kfree(bp_data); return 0; + +error_wp: + while (nr_wp--) + kfree(wp_info[nr_wp].old_data); error: kfree(bp_data); kfree(wp_info); diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 8f24bcd1a6d360..d33dd58f4a4fde 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -3108,9 +3108,7 @@ static int set_adapter_int(struct kvm_kernel_irq_routing_entry *e, void kvm_s390_reinject_machine_check(struct kvm_vcpu *vcpu, struct mcck_volatile_info *mcck_info) { - struct kvm_s390_interrupt_info inti; - struct kvm_s390_irq irq; - struct kvm_s390_mchk_info *mchk; + struct kvm_s390_irq irq = {}; union mci mci; __u64 cr14 = 0; /* upper bits are not used */ int rc; @@ -3129,20 +3127,14 @@ void kvm_s390_reinject_machine_check(struct kvm_vcpu *vcpu, if (mci.w) cr14 |= CR14_WARNING_SUBMASK; - mchk = mci.ck ? &inti.mchk : &irq.u.mchk; - mchk->cr14 = cr14; - mchk->mcic = mcck_info->mcic; - mchk->ext_damage_code = mcck_info->ext_damage_code; - mchk->failing_storage_address = mcck_info->failing_storage_address; - if (mci.ck) { - /* Inject the floating machine check */ - inti.type = KVM_S390_MCHK; - rc = __inject_vm(vcpu->kvm, &inti); - } else { - /* Inject the machine check to specified vcpu */ - irq.type = KVM_S390_MCHK; - rc = kvm_s390_inject_vcpu(vcpu, &irq); - } + irq.u.mchk.cr14 = cr14; + irq.u.mchk.mcic = mcck_info->mcic; + irq.u.mchk.ext_damage_code = mcck_info->ext_damage_code; + irq.u.mchk.failing_storage_address = mcck_info->failing_storage_address; + + /* Inject the machine check to specified vcpu */ + irq.type = KVM_S390_MCHK; + rc = kvm_s390_inject_vcpu(vcpu, &irq); WARN_ON_ONCE(rc); } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 518a69c55e85c9..17aa38710170a3 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -562,11 +562,12 @@ static void __kvm_s390_exit(void) static int kvm_s390_keyop(struct kvm_s390_mmu_cache *mc, struct kvm *kvm, int op, unsigned long addr, union skey skey) { - union asce asce = kvm->arch.gmap->asce; gfn_t gfn = gpa_to_gfn(addr); + union asce asce; int r; guard(read_lock)(&kvm->mmu_lock); + asce = kvm->arch.gmap->asce; switch (op) { case KVM_S390_KEYOP_SSKE: @@ -3410,6 +3411,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) trace_kvm_s390_destroy_vcpu(vcpu->vcpu_id); kvm_s390_clear_local_irqs(vcpu); kvm_clear_async_pf_completion_queue(vcpu); + kvm_s390_clear_bp_data(vcpu); if (!kvm_is_ucontrol(vcpu->kvm)) sca_del_vcpu(vcpu); kvm_s390_update_topology_change_report(vcpu->kvm, 1); @@ -4248,8 +4250,10 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, /* enforce guest PER */ kvm_s390_set_cpuflags(vcpu, CPUSTAT_P); - if (dbg->control & KVM_GUESTDBG_USE_HW_BP) - rc = kvm_s390_import_bp_data(vcpu, dbg); + if (dbg->control & KVM_GUESTDBG_USE_HW_BP) { + scoped_guard(srcu, &vcpu->kvm->srcu) + rc = kvm_s390_import_bp_data(vcpu, dbg); + } } else { kvm_s390_clear_cpuflags(vcpu, CPUSTAT_P); vcpu->arch.guestdbg.last_bp = 0; @@ -4474,8 +4478,8 @@ int kvm_s390_try_set_tod_clock(struct kvm *kvm, const struct kvm_s390_vm_tod_clo static void __kvm_inject_pfault_token(struct kvm_vcpu *vcpu, bool start_token, unsigned long token) { - struct kvm_s390_interrupt inti; - struct kvm_s390_irq irq; + struct kvm_s390_interrupt inti = {}; + struct kvm_s390_irq irq = {}; struct kvm_s390_interrupt_info *inti_mem = NULL; int ret = 0; @@ -5069,7 +5073,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) pr_err_ratelimited("can't run stopped vcpu %d\n", vcpu->vcpu_id); rc = -EINVAL; - goto out; + goto out_sigset; } kernel_fpu_begin(&fpu, KERNEL_FPC | KERNEL_VXR); @@ -5099,9 +5103,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) store_regs(vcpu); kernel_fpu_end(&fpu, KERNEL_FPC | KERNEL_VXR); + vcpu->stat.exit_userspace++; + +out_sigset: kvm_sigset_deactivate(vcpu); - vcpu->stat.exit_userspace++; out: vcpu_put(vcpu); return rc; @@ -5732,7 +5738,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp, r = kvm_s390_handle_pv_vcpu_dump(vcpu, &cmd); /* Always copy over UV rc / rrc data */ - if (copy_to_user((__u8 __user *)argp, &cmd.rc, + if (copy_to_user(argp + offsetof(struct kvm_pv_cmd, rc), &cmd.rc, sizeof(cmd.rc) + sizeof(cmd.rrc))) r = -EFAULT; break; diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c index eea24562e7db2b..0a7d8dfab6b793 100644 --- a/arch/s390/kvm/vsie.c +++ b/arch/s390/kvm/vsie.c @@ -173,6 +173,7 @@ static int setup_apcb10(struct kvm_vcpu *vcpu, struct kvm_s390_apcb1 *apcb_s, sizeof(struct kvm_s390_apcb0))) return -EFAULT; + memset(apcb_s, 0, sizeof(*apcb_s)); apcb_s->apm[0] = apcb_h->apm[0] & tmp.apm[0]; apcb_s->aqm[0] = apcb_h->aqm[0] & tmp.aqm[0] & 0xffff000000000000UL; apcb_s->adm[0] = apcb_h->adm[0] & tmp.adm[0] & 0xffff000000000000UL; diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c index 29b6f1ed59ec54..f904a636d449ba 100644 --- a/arch/x86/coco/tdx/tdx.c +++ b/arch/x86/coco/tdx/tdx.c @@ -694,8 +694,8 @@ static bool handle_in(struct pt_regs *regs, int size, int port) .r13 = PORT_READ, .r14 = port, }; - u64 mask = GENMASK(BITS_PER_BYTE * size, 0); bool success; + u64 val; /* * Emulate the I/O read via hypercall. More info about ABI can be found @@ -703,18 +703,16 @@ static bool handle_in(struct pt_regs *regs, int size, int port) * "TDG.VP.VMCALL". */ success = !__tdx_hypercall(&args); + val = success ? args.r11 : 0; - /* Update part of the register affected by the emulated instruction */ - regs->ax &= ~mask; - if (success) - regs->ax |= args.r11 & mask; + insn_assign_reg(®s->ax, val, size); return success; } static bool handle_out(struct pt_regs *regs, int size, int port) { - u64 mask = GENMASK(BITS_PER_BYTE * size, 0); + u64 mask = GENMASK(BITS_PER_BYTE * size - 1, 0); /* * Emulate the I/O write via hypercall. More info about ABI can be found diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index 2b35483e2b7073..465c414f145da0 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -7947,12 +7947,6 @@ __init int intel_pmu_init(void) x86_add_quirk(intel_arch_events_quirk); /* Install first, so it runs last */ - if (version >= 5) { - x86_pmu.intel_cap.anythread_deprecated = edx.split.anythread_deprecated; - if (x86_pmu.intel_cap.anythread_deprecated) - pr_cont(" AnyThread deprecated, "); - } - /* The perf side of core PMU is ready to support the mediated vPMU. */ x86_get_pmu(smp_processor_id())->capabilities |= PERF_PMU_CAP_MEDIATED_VPMU; @@ -8829,8 +8823,10 @@ __init int intel_pmu_init(void) &x86_pmu.intel_ctrl); /* AnyThread may be deprecated on arch perfmon v5 or later */ - if (x86_pmu.intel_cap.anythread_deprecated) + if (version >= 5 && edx.split.anythread_deprecated) { x86_pmu.format_attrs = intel_arch_formats_attr; + pr_cont("AnyThread deprecated, "); + } intel_pmu_check_event_constraints_all(NULL); diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c index cae2e02fe6cc4d..52d14927a1917c 100644 --- a/arch/x86/events/intel/lbr.c +++ b/arch/x86/events/intel/lbr.c @@ -1213,7 +1213,7 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc) { u64 from, to; int br_sel = cpuc->br_sel; - int i, j, type, to_plm; + int i, j, type, from_plm, to_plm; bool compress = false; /* if sampling all branches, then nothing to filter */ @@ -1245,8 +1245,14 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc) type |= X86_BR_NO_TX; } - /* if type does not correspond, then discard */ - if (type == X86_BR_NONE || (br_sel & type) != type) { + from_plm = kernel_ip(from) ? X86_BR_KERNEL : X86_BR_USER; + /* + * If type does not correspond, then discard. + * Specifically reject entries whose from address is in + * kernel space when only X86_BR_USER is requested. + */ + if (type == X86_BR_NONE || (br_sel & type) != type || + (!(br_sel & X86_BR_KERNEL) && (from_plm & X86_BR_KERNEL))) { cpuc->lbr_entries[i].from = 0; compress = true; } diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h index eae24bb35dc149..5902a297daa15c 100644 --- a/arch/x86/events/perf_event.h +++ b/arch/x86/events/perf_event.h @@ -668,7 +668,7 @@ union perf_capabilities { u64 perf_metrics:1; u64 pebs_output_pt_available:1; u64 pebs_timing_info:1; - u64 anythread_deprecated:1; + u64 __reserved:1; u64 rdpmc_metrics_clear:1; }; u64 capabilities; diff --git a/arch/x86/include/asm/barrier.h b/arch/x86/include/asm/barrier.h index db70832232d4a4..3e78bb76153ce0 100644 --- a/arch/x86/include/asm/barrier.h +++ b/arch/x86/include/asm/barrier.h @@ -17,7 +17,7 @@ #define rmb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "lfence", \ X86_FEATURE_XMM2) ::: "memory", "cc") #define wmb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "sfence", \ - X86_FEATURE_XMM2) ::: "memory", "cc") + X86_FEATURE_XMM) ::: "memory", "cc") #else #define __mb() asm volatile("mfence":::"memory") #define __rmb() asm volatile("lfence":::"memory") diff --git a/arch/x86/include/asm/insn-eval.h b/arch/x86/include/asm/insn-eval.h index 4733e9064ee555..ae05647a0afbf1 100644 --- a/arch/x86/include/asm/insn-eval.h +++ b/arch/x86/include/asm/insn-eval.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #define INSN_CODE_SEG_ADDR_SZ(params) ((params >> 4) & 0xf) @@ -46,4 +47,39 @@ enum insn_mmio_type insn_decode_mmio(struct insn *insn, int *bytes); bool insn_is_nop(struct insn *insn); +/* + * Write @val into *@reg following the x86 rules for writes to + * general-purpose registers (Intel SDM Vol. 1, "General-Purpose + * Registers in 64-Bit Mode"): an 8- or 16-bit write leaves the rest of + * the register untouched, a 32-bit write zero-extends the result into + * the upper 32 bits, and a 64-bit write replaces the whole register. + * + * @bytes is the width of the write, not a property of the instruction: + * an instruction that, say, sign-extends a 32-bit immediate into a + * 64-bit register does a 64-bit write here. + * + * @reg need not be 8-byte aligned: KVM's instruction emulator offsets + * the pointer by one byte to address the high-byte registers (AH, CH, + * DH, BH). Use narrow stores for the sub-word cases so the access + * width matches @bytes and the adjacent bytes are left alone. + */ +static inline void insn_assign_reg(unsigned long *reg, u64 val, int bytes) +{ + switch (bytes) { + case 1: + *(u8 *)reg = (u8)val; + break; + case 2: + *(u16 *)reg = (u16)val; + break; + case 4: + /* A 32-bit write zero-extends into the upper 32 bits. */ + *reg = (u32)val; + break; + case 8: + *reg = val; + break; + } +} + #endif /* _ASM_X86_INSN_EVAL_H */ diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h index 83dc5086138b39..ccf23b3f0e1c2c 100644 --- a/arch/x86/include/asm/kvm-x86-ops.h +++ b/arch/x86/include/asm/kvm-x86-ops.h @@ -134,6 +134,7 @@ KVM_X86_OP_OPTIONAL(mem_enc_unregister_region) KVM_X86_OP_OPTIONAL(vm_copy_enc_context_from) KVM_X86_OP_OPTIONAL(vm_move_enc_context_from) KVM_X86_OP_OPTIONAL(guest_memory_reclaimed) +KVM_X86_OP_OPTIONAL(reload_vmsa) KVM_X86_OP(get_feature_msr) KVM_X86_OP(check_emulate_instruction) KVM_X86_OP(apic_init_signal_blocked) @@ -148,6 +149,9 @@ KVM_X86_OP_OPTIONAL(alloc_apic_backing_page) KVM_X86_OP_OPTIONAL_RET0(gmem_prepare) KVM_X86_OP_OPTIONAL_RET0(gmem_max_mapping_level) KVM_X86_OP_OPTIONAL(gmem_invalidate) +#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE +KVM_X86_OP_OPTIONAL(gmem_invalidate_range) +#endif #endif #undef KVM_X86_OP diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 6db5b5f79df9b8..2dfd6199a10c62 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -128,6 +128,8 @@ KVM_ARCH_REQ_FLAGS(31, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) #define KVM_REQ_HV_TLB_FLUSH \ KVM_ARCH_REQ_FLAGS(32, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) +#define KVM_REQ_VMSA_PAGE_RELOAD \ + KVM_ARCH_REQ_FLAGS(33, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) #define KVM_REQ_UPDATE_PROTECTED_GUEST_STATE \ KVM_ARCH_REQ_FLAGS(34, KVM_REQUEST_WAIT) @@ -1985,6 +1987,7 @@ struct kvm_x86_ops { int (*vm_copy_enc_context_from)(struct kvm *kvm, unsigned int source_fd); int (*vm_move_enc_context_from)(struct kvm *kvm, unsigned int source_fd); void (*guest_memory_reclaimed)(struct kvm *kvm); + void (*reload_vmsa)(struct kvm_vcpu *vcpu); int (*get_feature_msr)(u32 msr, u64 *data); @@ -2009,6 +2012,9 @@ struct kvm_x86_ops { void *(*alloc_apic_backing_page)(struct kvm_vcpu *vcpu); int (*gmem_prepare)(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order); void (*gmem_invalidate)(kvm_pfn_t start, kvm_pfn_t end); +#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE + void (*gmem_invalidate_range)(struct kvm *kvm, struct kvm_gfn_range *range); +#endif int (*gmem_max_mapping_level)(struct kvm *kvm, kvm_pfn_t pfn, bool is_private); }; diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index b566ab5c751566..c6dcb5ac48afec 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -24,6 +24,7 @@ #include "kvm_emulate.h" #include #include +#include #include #include #include @@ -439,25 +440,6 @@ static void assign_masked(ulong *dest, ulong src, ulong mask) *dest = (*dest & ~mask) | (src & mask); } -static void assign_register(unsigned long *reg, u64 val, int bytes) -{ - /* The 4-byte case *is* correct: in 64-bit mode we zero-extend. */ - switch (bytes) { - case 1: - *(u8 *)reg = (u8)val; - break; - case 2: - *(u16 *)reg = (u16)val; - break; - case 4: - *reg = (u32)val; - break; /* 64b: zero-extend */ - case 8: - *reg = val; - break; - } -} - static inline unsigned long ad_mask(struct x86_emulate_ctxt *ctxt) { return (1UL << (ctxt->ad_bytes << 3)) - 1; @@ -505,7 +487,7 @@ register_address_increment(struct x86_emulate_ctxt *ctxt, int reg, int inc) { ulong *preg = reg_rmw(ctxt, reg); - assign_register(preg, *preg + inc, ctxt->ad_bytes); + insn_assign_reg(preg, *preg + inc, ctxt->ad_bytes); } static void rsp_increment(struct x86_emulate_ctxt *ctxt, int inc) @@ -1767,7 +1749,7 @@ static int load_segment_descriptor(struct x86_emulate_ctxt *ctxt, static void write_register_operand(struct operand *op) { - return assign_register(op->addr.reg, op->val, op->bytes); + return insn_assign_reg(op->addr.reg, op->val, op->bytes); } static int writeback(struct x86_emulate_ctxt *ctxt, struct operand *op) @@ -2008,7 +1990,7 @@ static int em_popa(struct x86_emulate_ctxt *ctxt) rc = emulate_pop(ctxt, &val, ctxt->op_bytes); if (rc != X86EMUL_CONTINUE) break; - assign_register(reg_rmw(ctxt, reg), val, ctxt->op_bytes); + insn_assign_reg(reg_rmw(ctxt, reg), val, ctxt->op_bytes); --reg; } return rc; diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index d8736b5dfcd3a8..5f43ae345f87cf 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -626,6 +626,18 @@ static enum hrtimer_restart stimer_timer_callback(struct hrtimer *timer) return HRTIMER_NORESTART; } +/* + * Translate a stimer expiry given in 100ns reference ticks into an + * an absolute deadline. Saturates on overflow. + */ +static ktime_t stimer_add_delta(ktime_t now, u64 delta_100ns) +{ + if (delta_100ns >= KTIME_MAX / 100) + return KTIME_MAX; + + return ktime_add_safe(now, 100 * delta_100ns); +} + /* * stimer_start() assumptions: * a) stimer->count is not equal to 0 @@ -635,6 +647,7 @@ static int stimer_start(struct kvm_vcpu_hv_stimer *stimer) { u64 time_now; ktime_t ktime_now; + ktime_t deadline; time_now = get_time_ref_counter(hv_stimer_to_vcpu(stimer)->kvm); ktime_now = ktime_get(); @@ -657,10 +670,8 @@ static int stimer_start(struct kvm_vcpu_hv_stimer *stimer) stimer->index, time_now, stimer->exp_time); - hrtimer_start(&stimer->timer, - ktime_add_ns(ktime_now, - 100 * (stimer->exp_time - time_now)), - HRTIMER_MODE_ABS); + deadline = stimer_add_delta(ktime_now, stimer->exp_time - time_now); + hrtimer_start(&stimer->timer, deadline, HRTIMER_MODE_ABS); return 0; } stimer->exp_time = stimer->count; @@ -679,9 +690,9 @@ static int stimer_start(struct kvm_vcpu_hv_stimer *stimer) stimer->index, time_now, stimer->count); - hrtimer_start(&stimer->timer, - ktime_add_ns(ktime_now, 100 * (stimer->count - time_now)), - HRTIMER_MODE_ABS); + deadline = stimer_add_delta(ktime_now, stimer->count - time_now); + hrtimer_start(&stimer->timer, deadline, HRTIMER_MODE_ABS); + return 0; } diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index a61750f8e1e373..51ba46aa6b7e51 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -1245,18 +1245,9 @@ struct rmap_iterator { int pos; /* index of the sptep */ }; -/* - * Iteration must be started by this function. This should also be used after - * removing/dropping sptes from the rmap link because in such cases the - * information in the iterator may not be valid. - * - * Returns sptep if found, NULL otherwise. - */ -static u64 *rmap_get_first(struct kvm_rmap_head *rmap_head, - struct rmap_iterator *iter) +static u64 *__rmap_get_first(unsigned long rmap_val, + struct rmap_iterator *iter) { - unsigned long rmap_val = kvm_rmap_get(rmap_head); - if (!rmap_val) return NULL; @@ -1270,6 +1261,19 @@ static u64 *rmap_get_first(struct kvm_rmap_head *rmap_head, return iter->desc->sptes[iter->pos]; } +/* + * Iteration must be started by this function. This should also be used after + * removing/dropping sptes from the rmap link because in such cases the + * information in the iterator may not be valid. + * + * Returns sptep if found, NULL otherwise. + */ +static u64 *rmap_get_first(struct kvm_rmap_head *rmap_head, + struct rmap_iterator *iter) +{ + return __rmap_get_first(kvm_rmap_get(rmap_head), iter); +} + /* * Must be used with a valid iterator: e.g. after rmap_get_first(). * @@ -1304,8 +1308,9 @@ static u64 *rmap_get_next(struct rmap_iterator *iter) __for_each_rmap_spte(_rmap_head_, _iter_, _sptep_) \ if (!WARN_ON_ONCE(!is_shadow_present_pte(*(_sptep_)))) \ -#define for_each_rmap_spte_lockless(_rmap_head_, _iter_, _sptep_, _spte_) \ - __for_each_rmap_spte(_rmap_head_, _iter_, _sptep_) \ +#define for_each_rmap_spte_lockless(_rmap_val_, _iter_, _sptep_, _spte_) \ + for (_sptep_ = __rmap_get_first(_rmap_val_, _iter_); \ + _sptep_; _sptep_ = rmap_get_next(_iter_)) \ if (is_shadow_present_pte(_spte_ = mmu_spte_get_lockless(sptep))) static void drop_spte(struct kvm *kvm, u64 *sptep) @@ -1743,7 +1748,7 @@ static bool kvm_rmap_age_gfn_range(struct kvm *kvm, rmap_head = gfn_to_rmap(gfn, level, range->slot); rmap_val = kvm_rmap_lock_readonly(rmap_head); - for_each_rmap_spte_lockless(rmap_head, &iter, sptep, spte) { + for_each_rmap_spte_lockless(rmap_val, &iter, sptep, spte) { if (!is_accessed_spte(spte)) continue; @@ -6896,20 +6901,11 @@ static void kvm_zap_obsolete_pages(struct kvm *kvm) kvm_mmu_commit_zap_page(kvm, &invalid_list); } -/* - * Fast invalidate all shadow pages and use lock-break technique - * to zap obsolete pages. - * - * It's required when memslot is being deleted or VM is being - * destroyed, in these cases, we should ensure that KVM MMU does - * not use any resource of the being-deleted slot or all slots - * after calling the function. - */ -static void kvm_mmu_zap_all_fast(struct kvm *kvm) +static void __kvm_mmu_zap_all_fast_front_half(struct kvm *kvm) { lockdep_assert_held(&kvm->slots_lock); + lockdep_assert_held_write(&kvm->mmu_lock); - write_lock(&kvm->mmu_lock); trace_kvm_mmu_zap_all_fast(kvm); /* @@ -6946,8 +6942,12 @@ static void kvm_mmu_zap_all_fast(struct kvm *kvm) kvm_make_all_cpus_request(kvm, KVM_REQ_MMU_FREE_OBSOLETE_ROOTS); kvm_zap_obsolete_pages(kvm); +} - write_unlock(&kvm->mmu_lock); +static void __kvm_mmu_zap_all_fast_back_half(struct kvm *kvm) +{ + lockdep_assert_held(&kvm->slots_lock); + lockdep_assert_not_held(&kvm->mmu_lock); /* * Zap the invalidated TDP MMU roots, all SPTEs must be dropped before @@ -6961,6 +6961,24 @@ static void kvm_mmu_zap_all_fast(struct kvm *kvm) kvm_tdp_mmu_zap_invalidated_roots(kvm, true); } +/* + * Fast invalidate all shadow pages and use lock-break technique + * to zap obsolete pages. + * + * It's required when memslot is being deleted or VM is being + * destroyed, in these cases, we should ensure that KVM MMU does + * not use any resource of the being-deleted slot or all slots + * after calling the function. + */ +static void kvm_mmu_zap_all_fast(struct kvm *kvm) +{ + write_lock(&kvm->mmu_lock); + __kvm_mmu_zap_all_fast_front_half(kvm); + write_unlock(&kvm->mmu_lock); + + __kvm_mmu_zap_all_fast_back_half(kvm); +} + int kvm_mmu_init_vm(struct kvm *kvm) { int r, i; @@ -7535,8 +7553,8 @@ static void kvm_mmu_zap_memslot_pages_and_flush(struct kvm *kvm, kvm_mmu_remote_flush_or_zap(kvm, &invalid_list, flush); } -static void kvm_mmu_zap_memslot(struct kvm *kvm, - struct kvm_memory_slot *slot) +void kvm_arch_flush_shadow_memslot(struct kvm *kvm, + struct kvm_memory_slot *slot) { struct kvm_gfn_range range = { .slot = slot, @@ -7545,27 +7563,28 @@ static void kvm_mmu_zap_memslot(struct kvm *kvm, .may_block = true, .attr_filter = KVM_FILTER_PRIVATE | KVM_FILTER_SHARED, }; + bool zap_all = kvm->arch.vm_type == KVM_X86_DEFAULT_VM && + kvm_check_has_quirk(kvm, KVM_X86_QUIRK_SLOT_ZAP_ALL); bool flush; write_lock(&kvm->mmu_lock); - flush = kvm_unmap_gfn_range(kvm, &range); - kvm_mmu_zap_memslot_pages_and_flush(kvm, slot, flush); - write_unlock(&kvm->mmu_lock); -} -static inline bool kvm_memslot_flush_zap_all(struct kvm *kvm) -{ - return kvm->arch.vm_type == KVM_X86_DEFAULT_VM && - kvm_check_has_quirk(kvm, KVM_X86_QUIRK_SLOT_ZAP_ALL); -} +#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE + if (slot->gmem.file) + kvm_arch_gmem_invalidate_range(kvm, &range); +#endif -void kvm_arch_flush_shadow_memslot(struct kvm *kvm, - struct kvm_memory_slot *slot) -{ - if (kvm_memslot_flush_zap_all(kvm)) - kvm_mmu_zap_all_fast(kvm); - else - kvm_mmu_zap_memslot(kvm, slot); + if (zap_all) { + __kvm_mmu_zap_all_fast_front_half(kvm); + } else { + flush = kvm_unmap_gfn_range(kvm, &range); + kvm_mmu_zap_memslot_pages_and_flush(kvm, slot, flush); + } + + write_unlock(&kvm->mmu_lock); + + if (zap_all) + __kvm_mmu_zap_all_fast_back_half(kvm); } void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen) diff --git a/arch/x86/kvm/mmu/tdp_iter.h b/arch/x86/kvm/mmu/tdp_iter.h index 364c5da6c49912..f898d8d0d93c2b 100644 --- a/arch/x86/kvm/mmu/tdp_iter.h +++ b/arch/x86/kvm/mmu/tdp_iter.h @@ -19,6 +19,13 @@ static inline u64 kvm_tdp_mmu_read_spte(tdp_ptep_t sptep) return READ_ONCE(*rcu_dereference(sptep)); } +/* + * WARNING! mmu_lock must be held for write when using the "write atomic" or + * "clear bits atomic" APIs, otherwise KVM could overwrite the "wrong" old SPTE + * value, i.e. clobber an update from a different CPU. The only exception is + * when KVM is freezing a leaf SPTE for removal, in which case KVM doesn't care + * about the exact old SPTE value (KVM will react to the actual old value). + */ static inline u64 kvm_tdp_mmu_write_spte_atomic(tdp_ptep_t sptep, u64 new_spte) { KVM_MMU_WARN_ON(is_ept_ve_possible(new_spte)); diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index c1cbae65d239f5..d95af13d922a7e 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -1334,19 +1334,17 @@ static void kvm_tdp_mmu_age_spte(struct kvm *kvm, struct tdp_iter *iter) if (WARN_ON_ONCE(is_mirror_sptep(iter->sptep))) return; - if (spte_ad_enabled(iter->old_spte)) { - iter->old_spte = tdp_mmu_clear_spte_bits_atomic(iter->sptep, - shadow_accessed_mask); + if (spte_ad_enabled(iter->old_spte)) new_spte = iter->old_spte & ~shadow_accessed_mask; - } else { + else new_spte = mark_spte_for_access_track(iter->old_spte); - /* - * It is safe for the following cmpxchg to fail. Leave the - * Accessed bit set, as the spte is most likely young anyway. - */ - if (__tdp_mmu_set_spte_atomic(kvm, iter, new_spte)) - return; - } + + /* + * Don't bother retrying if another CPU modified the SPTE, the SPTE is + * either being zapped or is likely still in-use, i.e. is still young. + */ + if (__tdp_mmu_set_spte_atomic(kvm, iter, new_spte)) + return; trace_kvm_tdp_mmu_spte_changed(iter->as_id, iter->gfn, iter->level, iter->old_spte, new_spte); diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 0f0ea7896af5bf..1ed78f7703400a 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -1283,9 +1283,28 @@ static void *sev_dbg_crypt_slow_alloc(struct page *page, unsigned long __va, if (WARN_ON_ONCE((*pa & PAGE_MASK) != ((*pa + *nr_bytes - 1) & PAGE_MASK))) return NULL; + /* + * If SNP is enabled, i.e. the RMP is active, allocate a full page to + * prevent concurrent accesses to the page. As required by firmware, + * the PSP driver updates the RMP to temporarily transfer ownership of + * the page to Firmware while the {DE,EN}CRYPT operation is in-progress, + * and so concurrent software accesses to the page will encounter + * seemingly spurious RMP #PF violations + */ + if (cc_platform_has(CC_ATTR_HOST_SEV_SNP)) + return (void *)__get_free_page(GFP_KERNEL); + return kmalloc(*nr_bytes, GFP_KERNEL); } +static void sev_dbg_crypt_slow_free(void *buf) +{ + if (cc_platform_has(CC_ATTR_HOST_SEV_SNP)) + free_page((unsigned long)buf); + else + kfree(buf); +} + static int sev_dbg_decrypt_slow(struct kvm *kvm, unsigned long src, struct page *src_p, unsigned long dst, unsigned int len, int *err) @@ -1307,7 +1326,7 @@ static int sev_dbg_decrypt_slow(struct kvm *kvm, unsigned long src, if (copy_to_user((void __user *)dst, buf + (src & 15), len)) r = -EFAULT; out: - kfree(buf); + sev_dbg_crypt_slow_free(buf); return r; } @@ -1340,7 +1359,7 @@ static int sev_dbg_encrypt_slow(struct kvm *kvm, unsigned long src, r = sev_issue_dbg_cmd(kvm, __sme_set(__pa(buf)), dst_pa, nr_bytes, KVM_SEV_DBG_ENCRYPT, err); out: - kfree(buf); + sev_dbg_crypt_slow_free(buf); return r; } @@ -2757,8 +2776,12 @@ int sev_mem_enc_register_region(struct kvm *kvm, if (!region) return -ENOMEM; + /* + * Do NOT specify FOLL_WRITE, as KVM isn't using the pinned pages to + * write memory, and FOLL_LONGTERM itself triggers CoW unshare. + */ region->pages = sev_pin_memory(kvm, range->addr, range->size, ®ion->npages, - FOLL_WRITE | FOLL_LONGTERM); + FOLL_LONGTERM); if (IS_ERR(region->pages)) { ret = PTR_ERR(region->pages); goto e_free; @@ -3998,30 +4021,25 @@ static int snp_begin_psc(struct vcpu_svm *svm) return snp_do_psc(svm); } -/* - * Invoked as part of svm_vcpu_reset() processing of an init event. - */ -static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu) +static void __sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa) { struct vcpu_svm *svm = to_svm(vcpu); struct kvm_memory_slot *slot; + struct kvm *kvm = vcpu->kvm; + gfn_t gfn = gpa_to_gfn(gpa); + unsigned long mmu_seq; struct page *page; kvm_pfn_t pfn; - gfn_t gfn; - - guard(mutex)(&svm->sev_es.snp_vmsa_mutex); - - if (!svm->sev_es.snp_ap_waiting_for_reset) - return; - - svm->sev_es.snp_ap_waiting_for_reset = false; - /* Mark the vCPU as offline and not runnable */ - vcpu->arch.pv.pv_unhalted = false; - kvm_set_mp_state(vcpu, KVM_MP_STATE_HALTED); + lockdep_assert_held(&svm->sev_es.snp_vmsa_mutex); - /* Clear use of the VMSA */ + /* + * Clear use of the VMSA. Ensure snp_guest_vmsa_gpa is written exactly + * once, as it is read locklessly when responding to gfn invalidations. + * Pairs with the READ_ONCE() in sev_gmem_invalidate_range(). + */ svm->vmcb->control.vmsa_pa = INVALID_PAGE; + WRITE_ONCE(svm->sev_es.snp_guest_vmsa_gpa, INVALID_PAGE); /* * When replacing the VMSA during SEV-SNP AP creation, @@ -4029,16 +4047,16 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu) */ vmcb_mark_all_dirty(svm->vmcb); - if (!VALID_PAGE(svm->sev_es.snp_vmsa_gpa)) + if (!VALID_PAGE(gpa)) return; - gfn = gpa_to_gfn(svm->sev_es.snp_vmsa_gpa); - svm->sev_es.snp_vmsa_gpa = INVALID_PAGE; - slot = gfn_to_memslot(vcpu->kvm, gfn); if (!slot) return; + mmu_seq = kvm->mmu_invalidate_seq; + smp_rmb(); + /* * The new VMSA will be private memory guest memory, so retrieve the * PFN from the gmem backend. @@ -4057,18 +4075,64 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu) */ svm->sev_es.snp_has_guest_vmsa = true; - /* Use the new VMSA */ - svm->vmcb->control.vmsa_pa = pfn_to_hpa(pfn); + read_lock(&kvm->mmu_lock); + /* + * Save the guest-provided GPA. If retry is needed, then KVM will try + * again with the same GPA. If the VMSA is usable, then KVM needs to + * track the GPA so that the VMSA can be reloaded if the backing page + * for the GPA is invalidated. + */ + svm->sev_es.snp_guest_vmsa_gpa = gpa; + if (mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn)) + kvm_make_request(KVM_REQ_VMSA_PAGE_RELOAD, vcpu); + else + svm->vmcb->control.vmsa_pa = pfn_to_hpa(pfn); + read_unlock(&kvm->mmu_lock); + + kvm_release_page_clean(page); +} + +/* + * Invoked as part of svm_vcpu_reset() processing of an init event. + */ +static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu) +{ + struct vcpu_svm *svm = to_svm(vcpu); + gpa_t gpa; + + guard(mutex)(&svm->sev_es.snp_vmsa_mutex); - /* Mark the vCPU as runnable */ - kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE); + if (!svm->sev_es.snp_ap_waiting_for_reset) + return; + + svm->sev_es.snp_ap_waiting_for_reset = false; + + /* Mark the vCPU as offline and not runnable */ + vcpu->arch.pv.pv_unhalted = false; + kvm_set_mp_state(vcpu, KVM_MP_STATE_HALTED); + + gpa = svm->sev_es.snp_pending_vmsa_gpa; + svm->sev_es.snp_pending_vmsa_gpa = INVALID_PAGE; + + __sev_snp_reload_vmsa(vcpu, gpa); /* - * gmem pages aren't currently migratable, but if this ever changes - * then care should be taken to ensure svm->sev_es.vmsa is pinned - * through some other means. + * Mark the vCPU as runnable for CREATE requests, indicated by a valid + * VMSA GPA, even if installing the VMSA failed, so that KVM_RUN will + * fail instead of blocking indefinitely and hanging the vCPU, e.g. if + * the backing guest_memfd page is unavailable. */ - kvm_release_page_clean(page); + if (VALID_PAGE(gpa)) + kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE); +} + +void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu) +{ + struct vcpu_sev_es_state *sev_es = &to_svm(vcpu)->sev_es; + + guard(mutex)(&sev_es->snp_vmsa_mutex); + + __sev_snp_reload_vmsa(vcpu, sev_es->snp_guest_vmsa_gpa); } static int sev_snp_ap_creation(struct vcpu_svm *svm) @@ -4124,10 +4188,10 @@ static int sev_snp_ap_creation(struct vcpu_svm *svm) return -EINVAL; } - target_svm->sev_es.snp_vmsa_gpa = svm->vmcb->control.exit_info_2; + target_svm->sev_es.snp_pending_vmsa_gpa = svm->vmcb->control.exit_info_2; break; case SVM_VMGEXIT_AP_DESTROY: - target_svm->sev_es.snp_vmsa_gpa = INVALID_PAGE; + target_svm->sev_es.snp_pending_vmsa_gpa = INVALID_PAGE; break; default: vcpu_unimpl(vcpu, "vmgexit: invalid AP creation request [%#x] from guest\n", @@ -4810,6 +4874,8 @@ int sev_vcpu_create(struct kvm_vcpu *vcpu) return -ENOMEM; svm->sev_es.vmsa = page_address(vmsa_page); + svm->sev_es.snp_pending_vmsa_gpa = INVALID_PAGE; + svm->sev_es.snp_guest_vmsa_gpa = INVALID_PAGE; vcpu->arch.guest_tsc_protected = snp_is_secure_tsc_enabled(vcpu->kvm); @@ -5198,6 +5264,41 @@ void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end) } } +void sev_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range) +{ + struct kvm_vcpu *vcpu; + unsigned long i; + + lockdep_assert_held_write(&kvm->mmu_lock); + + /* + * An unstable result for "is SNP" is a-ok here, thanks to mmu_lock. + * The vCPU's VMSA GPA is invalidated before the vCPU is made visible + * to other tasks, and can only become valid while holding mmu_lock, + * after the VM is fully committed to being an SNP VM. + */ + if (!____sev_snp_guest(kvm)) + return; + + kvm_for_each_vcpu(i, vcpu, kvm) { + /* + * Read snp_guest_vmsa_gpa without taking the vCPU's VMSA mutex + * (or its generic mutex) as mmu_lock is held, i.e. this task + * can't sleep. The VMSA is invalidated outside of mmu_lock, + * but can only become valid inside of mmu_lock, i.e. the below + * can get false positives, but not false negatives. A false + * positive is benign, as a spurious request simply forces the + * vCPU to re-establish its VMSA. + */ + gpa_t gpa = READ_ONCE(to_svm(vcpu)->sev_es.snp_guest_vmsa_gpa); + + if (VALID_PAGE(gpa) && + gpa_to_gfn(gpa) >= range->start && + gpa_to_gfn(gpa) < range->end) + kvm_make_request_and_kick(KVM_REQ_VMSA_PAGE_RELOAD, vcpu); + } +} + int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private) { int level, rc; diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 60495447f0ca02..03fb9d7231c8d6 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -5463,9 +5463,15 @@ struct kvm_x86_ops svm_x86_ops __initdata = { .mem_enc_register_region = sev_mem_enc_register_region, .mem_enc_unregister_region = sev_mem_enc_unregister_region, .guest_memory_reclaimed = sev_guest_memory_reclaimed, + .reload_vmsa = sev_snp_reload_vmsa, .vm_copy_enc_context_from = sev_vm_copy_enc_context_from, .vm_move_enc_context_from = sev_vm_move_enc_context_from, + + .gmem_prepare = sev_gmem_prepare, + .gmem_invalidate = sev_gmem_invalidate, + .gmem_invalidate_range = sev_gmem_invalidate_range, + .gmem_max_mapping_level = sev_gmem_max_mapping_level, #endif .check_emulate_instruction = svm_check_emulate_instruction, @@ -5477,10 +5483,6 @@ struct kvm_x86_ops svm_x86_ops __initdata = { .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, .vcpu_get_apicv_inhibit_reasons = avic_vcpu_get_apicv_inhibit_reasons, .alloc_apic_backing_page = svm_alloc_apic_backing_page, - - .gmem_prepare = sev_gmem_prepare, - .gmem_invalidate = sev_gmem_invalidate, - .gmem_max_mapping_level = sev_gmem_max_mapping_level, }; /* @@ -5656,10 +5658,6 @@ static __init int svm_hardware_setup(void) if (nested) { pr_info("Nested Virtualization enabled\n"); - kvm_enable_efer_bits(EFER_SVME); - if (!boot_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ)) - kvm_enable_efer_bits(EFER_LMSLE); - r = nested_svm_init_msrpm_merge_offsets(); if (r) return r; diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index d63e5878988aad..cd30740e99849d 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -272,7 +272,8 @@ struct vcpu_sev_es_state { u64 ghcb_registered_gpa; struct mutex snp_vmsa_mutex; /* Used to handle concurrent updates of VMSA. */ - gpa_t snp_vmsa_gpa; + gpa_t snp_pending_vmsa_gpa; + gpa_t snp_guest_vmsa_gpa; bool snp_ap_waiting_for_reset; bool snp_has_guest_vmsa; }; @@ -996,6 +997,7 @@ static inline struct page *snp_safe_alloc_page(void) { return snp_safe_alloc_page_node(numa_node_id(), GFP_KERNEL_ACCOUNT); } +void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu); int sev_vcpu_create(struct kvm_vcpu *vcpu); void sev_free_vcpu(struct kvm_vcpu *vcpu); @@ -1010,6 +1012,7 @@ extern unsigned int max_sev_asid; void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code); int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order); void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end); +void sev_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range); int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private); struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu); void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_save_area *vmsa); @@ -1035,16 +1038,6 @@ static inline int sev_cpu_init(struct svm_cpu_data *sd) { return 0; } static inline int sev_dev_get_attr(u32 group, u64 attr, u64 *val) { return -ENXIO; } #define max_sev_asid 0 static inline void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) {} -static inline int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order) -{ - return 0; -} -static inline void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end) {} -static inline int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private) -{ - return 0; -} - static inline struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu) { return NULL; diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index ddf6df7bee93b2..ed28cf6b6efc2c 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -1326,6 +1326,9 @@ static void nested_vmx_transition_tlb_flush(struct kvm_vcpu *vcpu, * is the VPID incorporated into the MMU context. I.e. KVM must assume * that the new vpid12 has never been used and thus represents a new * guest ASID that cannot have entries in the TLB. + * + * Note, last_vpid is initialized as 0, so the first nested VM-Enter + * after VMXON will always flush the TLB to avoid using stale entries. */ if (is_vmenter && vmcs12->virtual_processor_id != vmx->nested.last_vpid) { vmx->nested.last_vpid = vmcs12->virtual_processor_id; @@ -3760,6 +3763,14 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, vmentry_fail_vmexit_guest_mode: if (vmcs12->cpu_based_vm_exec_control & CPU_BASED_USE_TSC_OFFSETTING) vcpu->arch.tsc_offset -= vmcs12->tsc_offset; + + /* + * Handle any TLB flush requests that were queued for L2 if KVM made it + * far enough along to switch to L2 context. Note, loading host state + * will generate any flushes for L1 required by VM-Exit. + */ + kvm_service_local_tlb_flush_requests(vcpu); + leave_guest_mode(vcpu); vmentry_fail_vmexit: @@ -5073,8 +5084,9 @@ void __nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason, /* trying to cancel vmlaunch/vmresume is a bug */ kvm_warn_on_nested_run_pending(vcpu); -#ifdef CONFIG_KVM_HYPERV + /* Note, "checking" the request also clears the request. */ if (kvm_check_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu)) { +#ifdef CONFIG_KVM_HYPERV /* * KVM_REQ_GET_NESTED_STATE_PAGES is also used to map * Enlightened VMCS after migration and we still need to @@ -5082,8 +5094,8 @@ void __nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason, * the first L2 run. */ (void)nested_get_evmcs_page(vcpu); - } #endif + } /* Service pending TLB flush requests for L2 before switching to L1. */ kvm_service_local_tlb_flush_requests(vcpu); @@ -5432,6 +5444,13 @@ static int enter_vmx_operation(struct kvm_vcpu *vcpu) vmx->nested.vpid02 = allocate_vpid(); + /* + * Clear last_vpid to ensure that the VPID is flushed on the first + * nested VM-Enter. Otherwise, stale TLB entries from a previous life of + * the VPID (e.g. different vCPU or even different VM) could be used. + */ + vmx->nested.last_vpid = 0; + vmx->nested.vmcs02_initialized = false; vmx->nested.vmxon = true; @@ -6059,8 +6078,8 @@ static int handle_invvpid(struct kvm_vcpu *vcpu) u64 vpid; u64 gla; } operand; - u16 vpid02; int r, gpr_index; + int cpu; if (!(vmx->nested.msrs.secondary_ctls_high & SECONDARY_EXEC_ENABLE_VPID) || @@ -6094,42 +6113,34 @@ static int handle_invvpid(struct kvm_vcpu *vcpu) return kvm_handle_memory_failure(vcpu, r, &e); if (operand.vpid >> 16) - return nested_vmx_fail(vcpu, - VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID); + return nested_vmx_fail(vcpu, VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID); + + if (type != VMX_VPID_EXTENT_ALL_CONTEXT && !operand.vpid) + return nested_vmx_fail(vcpu, VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID); + + /* LAM doesn't apply to addresses that are inputs to TLB invalidation. */ + if (type == VMX_VPID_EXTENT_INDIVIDUAL_ADDR && + is_noncanonical_invlpg_address(operand.gla, vcpu)) + return nested_vmx_fail(vcpu, VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID); /* * Always flush the effective vpid02, i.e. never flush the current VPID * and never explicitly flush vpid01. INVVPID targets a VPID, not a * VMCS, and so whether or not the current vmcs12 has VPID enabled is * irrelevant (and there may not be a loaded vmcs12). + * + * If vmcs02 was last loaded on a different pCPU, then defer the flush + * by invalidating the nested VPID tracking to ensure that KVM performs + * the invalidation on the correct pCPU. */ - vpid02 = nested_get_vpid02(vcpu); - switch (type) { - case VMX_VPID_EXTENT_INDIVIDUAL_ADDR: - /* - * LAM doesn't apply to addresses that are inputs to TLB - * invalidation. - */ - if (!operand.vpid || - is_noncanonical_invlpg_address(operand.gla, vcpu)) - return nested_vmx_fail(vcpu, - VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID); - vpid_sync_vcpu_addr(vpid02, operand.gla); - break; - case VMX_VPID_EXTENT_SINGLE_CONTEXT: - case VMX_VPID_EXTENT_SINGLE_NON_GLOBAL: - if (!operand.vpid) - return nested_vmx_fail(vcpu, - VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID); - vpid_sync_context(vpid02); - break; - case VMX_VPID_EXTENT_ALL_CONTEXT: - vpid_sync_context(vpid02); - break; - default: - WARN_ON_ONCE(1); - return kvm_skip_emulated_instruction(vcpu); - } + cpu = get_cpu(); + if (cpu != vmx->nested.vmcs02.cpu) + vmx->nested.last_vpid = 0; + else if (type == VMX_VPID_EXTENT_INDIVIDUAL_ADDR) + vpid_sync_vcpu_addr(nested_get_vpid02(vcpu), operand.gla); + else + vpid_sync_context(nested_get_vpid02(vcpu)); + put_cpu(); /* * Sync the shadow page tables if EPT is disabled, L1 is invalidating diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 47cb9eba113b1c..cb94b87df98989 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6736,7 +6736,10 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, break; fallthrough; case KVM_CAP_DISABLE_QUIRKS: - kvm->arch.disabled_quirks |= cap->args[0] & kvm_caps.supported_quirks; + mutex_lock(&kvm->lock); + WRITE_ONCE(kvm->arch.disabled_quirks, + kvm->arch.disabled_quirks | (cap->args[0] & kvm_caps.supported_quirks)); + mutex_unlock(&kvm->lock); r = 0; break; case KVM_CAP_SPLIT_IRQCHIP: { @@ -10012,6 +10015,12 @@ static void kvm_setup_efer_caps(void) if (kvm_cpu_cap_has(X86_FEATURE_AUTOIBRS)) kvm_enable_efer_bits(EFER_AUTOIBRS); + + if (kvm_cpu_cap_has(X86_FEATURE_SVM)) { + kvm_enable_efer_bits(EFER_SVME); + if (!boot_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ)) + kvm_enable_efer_bits(EFER_LMSLE); + } } static inline void kvm_ops_update(struct kvm_x86_init_ops *ops) @@ -11278,6 +11287,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) goto out; } } + if (kvm_check_request(KVM_REQ_VMSA_PAGE_RELOAD, vcpu)) + kvm_x86_call(reload_vmsa)(vcpu); } if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win || @@ -14142,6 +14153,10 @@ void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end) { kvm_x86_call(gmem_invalidate)(start, end); } +void kvm_arch_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range) +{ + kvm_x86_call(gmem_invalidate_range)(kvm, range); +} #endif #endif diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 9de577ef9c9741..ad9d2aacb87d2c 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -367,7 +367,7 @@ static inline bool vcpu_match_mmio_gpa(struct kvm_vcpu *vcpu, gpa_t gpa) static inline bool kvm_check_has_quirk(struct kvm *kvm, u64 quirk) { - return !(kvm->arch.disabled_quirks & quirk); + return !(READ_ONCE(kvm->arch.disabled_quirks) & quirk); } static __always_inline void kvm_request_l1tf_flush_l1d(void) diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index de7515ea1beae8..276d076d299381 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -1887,7 +1887,7 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int * EMIT_mov(dst_reg, src_reg); #ifdef CONFIG_SMP /* add , gs:[] */ - EMIT2(0x65, add_1mod(0x48, dst_reg)); + EMIT2(0x65, add_2mod(0x48, 0, dst_reg)); EMIT3(0x03, add_2reg(0x04, 0, dst_reg), 0x25); EMIT((u32)(unsigned long)&this_cpu_off, 4); #endif diff --git a/block/bio.c b/block/bio.c index 6a2f6fc3413e89..5018a6fc2f36b7 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1221,10 +1221,45 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter, return 0; } +#ifdef CONFIG_DEBUG_KERNEL +static inline bool bio_iov_bvec_aligned(const struct bio *bio, + unsigned mem_align_mask) +{ + struct bvec_iter iter; + struct bio_vec bv; + + /* + * Correct callers never break the alignment requirements, so this + * exhaustive check is only paid for in debug builds. + */ + for_each_mp_bvec(bv, bio->bi_io_vec, iter, bio->bi_iter) + if ((bv.bv_offset | bv.bv_len) & mem_align_mask) + return false; + return true; +} +#else +static inline bool bio_iov_bvec_aligned(const struct bio *bio, + unsigned mem_align_mask) +{ + /* + * We forward the bio_vec as-is, so ITER_BVEC callers must provide + * segments already aligned to the device's DMA alignment. The only + * unchecked user-controllable offset that reaches here is an io_uring + * registered buffer where just the first segment can be unaligned + * (the rest is virtually contiguous), so checking only that one is + * sufficient to know if the entire vector is valid. + */ + return !(mp_bvec_iter_offset(bio->bi_io_vec, bio->bi_iter) & + mem_align_mask); +} +#endif + /** * bio_iov_iter_get_pages - add user or kernel pages to a bio * @bio: bio to add pages to * @iter: iov iterator describing the region to be added + * @mem_align_mask: the mask the source address and length must be aligned to, + * 0 for no requirement * @len_align_mask: the mask to align the total size to, 0 for any length * * This takes either an iterator pointing to user memory, or one pointing to @@ -1243,7 +1278,7 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter, * is returned only if 0 pages could be pinned. */ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, - unsigned len_align_mask) + unsigned mem_align_mask, unsigned len_align_mask) { iov_iter_extraction_t flags = 0; @@ -1252,6 +1287,10 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, if (iov_iter_is_bvec(iter)) { bio_iov_bvec_set(bio, iter); + + if (!bio_iov_bvec_aligned(bio, mem_align_mask)) + return -EINVAL; + iov_iter_advance(iter, bio->bi_iter.bi_size); return 0; } @@ -1266,8 +1305,19 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec, BIO_MAX_SIZE - bio->bi_iter.bi_size, - &bio->bi_vcnt, bio->bi_max_vecs, flags); + &bio->bi_vcnt, bio->bi_max_vecs, + mem_align_mask, flags); if (ret <= 0) { + /* + * A misaligned vector fails the whole I/O. Release any + * pages pinned by earlier iterations before returning + * since this bio won't be submitted to release them. + */ + if (ret == -EINVAL) { + bio_release_pages(bio, false); + bio_clear_flag(bio, BIO_PAGE_PINNED); + bio->bi_vcnt = 0; + } if (!bio->bi_vcnt) return ret; break; @@ -1380,7 +1430,7 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter, do { ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec + 1, len, - &bio->bi_vcnt, bio->bi_max_vecs - 1, 0); + &bio->bi_vcnt, bio->bi_max_vecs - 1, 0, 0); if (ret <= 0) { if (!bio->bi_vcnt) goto out_folio_put; diff --git a/block/blk-map.c b/block/blk-map.c index d1d6bbe0ecf1f9..615d29bb840eb9 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -274,7 +274,7 @@ static int bio_map_user_iov(struct request *rq, struct iov_iter *iter, * No alignment requirements on our part to support arbitrary * passthrough commands. */ - ret = bio_iov_iter_get_pages(bio, iter, 0); + ret = bio_iov_iter_get_pages(bio, iter, 0, 0); if (ret) goto out_put; ret = blk_rq_append_bio(rq, bio); diff --git a/block/fops.c b/block/fops.c index 15783a6180dec1..a84450d79b3cc9 100644 --- a/block/fops.c +++ b/block/fops.c @@ -46,7 +46,7 @@ static bool blkdev_dio_invalid(struct block_device *bdev, struct kiocb *iocb, static inline int blkdev_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, struct block_device *bdev) { - return bio_iov_iter_get_pages(bio, iter, + return bio_iov_iter_get_pages(bio, iter, bdev_dma_alignment(bdev), bdev_logical_block_size(bdev) - 1); } diff --git a/block/genhd.c b/block/genhd.c index e8ce0cabf392ca..10ca8b4d6eea7e 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -447,6 +447,13 @@ static int __add_disk(struct device *parent, struct gendisk *disk, bdev_set_flag(disk->part0, BD_HAS_SUBMIT_BIO); } + /* + * We do not support partitions with zoned block devices, so do not try + * to scan the partitions table. + */ + if (blk_queue_is_zoned(disk->queue)) + disk->flags |= GENHD_FL_NO_PART; + /* * If the driver provides an explicit major number it also must provide * the number of minors numbers supported, and those will be used to @@ -681,6 +688,7 @@ static bool __blk_mark_disk_dead(struct gendisk *disk) */ void blk_mark_disk_dead(struct gendisk *disk) { + blk_queue_flag_set(QUEUE_FLAG_DYING, disk->queue); __blk_mark_disk_dead(disk); blk_report_disk_dead(disk, true); } diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c index 03395b89cc61e3..b377f4f7558da3 100644 --- a/crypto/krb5/krb5_api.c +++ b/crypto/krb5/krb5_api.c @@ -263,10 +263,10 @@ struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *kr goto err; } - kfree(keys.data); + kfree_sensitive(keys.data); return ci; err: - kfree(keys.data); + kfree_sensitive(keys.data); return ERR_PTR(ret); } EXPORT_SYMBOL(crypto_krb5_prepare_encryption); @@ -333,10 +333,10 @@ struct crypto_shash *crypto_krb5_prepare_checksum(const struct krb5_enctype *krb goto err; } - kfree(keys.data); + kfree_sensitive(keys.data); return ci; err: - kfree(keys.data); + kfree_sensitive(keys.data); return ERR_PTR(ret); } EXPORT_SYMBOL(crypto_krb5_prepare_checksum); diff --git a/debian.qcom-x1e/changelog b/debian.qcom-x1e/changelog index 934a74781332bd..e67a6137146703 100644 --- a/debian.qcom-x1e/changelog +++ b/debian.qcom-x1e/changelog @@ -1,3 +1,82 @@ +linux-qcom-x1e (7.2.5-jg-0sp11v5) resolute; urgency=medium + + * Qualification-only camera dependency carrier on the pinned v4 reference. + * Replay the seven held CAMSS ports with original authors and provenance. + * Preserve v4 audio, input, DT sources and non-Stubble packaging policy. + * Use a fresh ABI and separate bundle; retain v4 and the v23 recovery kernel. + * RAW packing/layout, capture/LED, teardown, errors and resume remain untested. + * Keep non-Denali, audio protection and contributor qualification gates open. + * No installation, hardware acceptance or final integration is implied. + + -- Leon Silcott Thu, 01 Oct 2026 18:05:00 +0100 + +linux-qcom-x1e (7.2.5-jg-0sp11v4) resolute; urgency=medium + + * Qualification-only SP11 render transport repair; supersede the v3 test. + * Restore Denali DAC, COMP and BOOST port extensions in private sink properties. + * Preserve generic WSA884x properties, speaker protection and pinned userspace. + * Retain the refreshed 7.2.5 base, GPI ordering and previous audio repairs. + * Use a fresh ABI to isolate this candidate from the failed v3 hardware test. + * Preserve source DTB and non-Stubble packaging policy for Lexr delivery. + * Hardware qualification remains open; retain the known-good recovery kernel. + * Do not merge this test carrier or infer speaker safety from offline checks. + + -- Leon Silcott Wed, 30 Sep 2026 01:26:40 +0100 + +linux-qcom-x1e (7.2.5-jg-0sp11v3) resolute; urgency=medium + + * Qualification-only SP11 audio repair build; supersede the failed v2 test. + * Restore guarded calibration, protection-query and feedback-tail behavior. + * Restore Denali WSA producer stages and runtime clock ownership. + * Correct the WSA softclip register layout and retain failure guards. + * Allocate a fresh ABI for the changed AudioReach module symbol checksums. + * Preserve source DTB and non-Stubble packaging policy for Lexr delivery. + * Hardware qualification remains open; retain the known-good recovery kernel. + * Do not merge this test carrier or infer full feature parity from builds. + + -- Leon Silcott Tue, 29 Sep 2026 07:35:00 +0100 + +linux-qcom-x1e (7.2.5-jg-0sp11v2) resolute; urgency=medium + + * Qualification-only SP11 TX/Golden audio dependency-group build. + * Retain the refreshed 7.2.5 base and the v1 GPI ordering repair. + * Include the guarded TX DMIC and Golden playback parity ports. + * Preserve source DTB and non-Stubble packaging policy for Lexr delivery. + * No suspend, audio safety, or full hardware-parity approval is implied. + * Retain the known-good recovery kernel and do not merge this test carrier. + + -- Leon Silcott Tue, 29 Sep 2026 02:15:00 +0100 + +linux-qcom-x1e (7.2.5-jg-0) resolute; urgency=medium + + * usb: typec: mux: ps883x: increase delay after writing config regs + * usb: typec: mux: ps883x: support TYPEC_DP_STATE_F like qmp-combo + * rebase the whole stack to v7.2.5 + + -- Jens Glathe Wed, 16 Sep 2026 22:34:59 +0200 + +linux-qcom-x1e (7.2.3-jg-1) resolute; urgency=medium + + * arm64: dts: qcom: x1-ideacentre-mini-01q8x10: remove qcom,disable-usb4 + * usb: typec: mux: ps883x: v2 follow-up — drop DT USB4 quirk, align DP path + + -- Jens Glathe Sun, 13 Sep 2026 17:52:43 +0200 + +linux-qcom-x1e (7.2.3-jg-0) resolute; urgency=medium + + * arm64: dts: qcom: Add Lenovo IdeaPad Slim 5x 14Q8X9 whole series [https://lore.kernel.org/all/20260906-x1-ideapad-slim5x-v1-0-7ccffbe6bc4a@oldschoolsolutions.biz/] + * arm64: dts: qcom: IdeaCentre Mini 01Q8X10 Hamoa + Purwa whole series [https://lore.kernel.org/all/20260905-x1-ideacentre-mini-prettify-v1-0-9a87882973bb@oldschoolsolutions.biz/] + * rebase the whole stack to v7.2.3 + + -- Jens Glathe Sun, 06 Sep 2026 21:44:20 +0200 + +linux-qcom-x1e (7.2.2-jg-2) resolute; urgency=medium + + * arm64: dts: qcom: x1p42100-ideapad-5-2in1: add CTIA/OMTP mux + * REMOVE arm64: dts: qcom: hamoa: Mark the EC reset gpio as reserved on all boards whole series [Abel Vesa ] + + -- Jens Glathe Sat, 05 Sep 2026 06:45:03 +0200 + linux-qcom-x1e (7.2.2-jg-1) resolute; urgency=medium * arm64: dts: qcom: x1-lenovo-ideacentre-mini-01q8x10: discriminate compatible strings @@ -7,7 +86,7 @@ linux-qcom-x1e (7.2.2-jg-1) resolute; urgency=medium * arm64: dts: qcom: x1-denali: add volume-down key [Leon Silcott ] * Ubuntu: change default generation to nostubble * arm64: dts: qcom: hamoa: Mark the EC reset gpio as reserved on all boards whole series [Abel Vesa ] - * drm: handle IRQ_HPD events correctly whole series [Dmitry Baryshkov ] + * drm: handle IRQ_HPD events correctly whole series [Dmitry Baryshkov ] [https://lore.kernel.org/all/20260608-hpd-irq-events-v4-0-30b62b335487@oss.qualcomm.com/] * drm/msm/dp: Handle IRQ HPD as a sink request [Xilin Wu ] * drm/msm/dp: Mark the link bad when an active sink is replugged [Xilin Wu ] diff --git a/debian.qcom-x1e/config/annotations b/debian.qcom-x1e/config/annotations index bc5acb0ccee9da..f67c2f4185e34e 100644 --- a/debian.qcom-x1e/config/annotations +++ b/debian.qcom-x1e/config/annotations @@ -2726,12 +2726,10 @@ CONFIG_CRYPTO_DEV_SUN4I_SS_DEBUG policy<{'arm64': 'n'}> CONFIG_CRYPTO_DEV_SUN8I_CE policy<{'arm64': 'm'}> CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG policy<{'arm64': 'n'}> CONFIG_CRYPTO_DEV_SUN8I_CE_HASH policy<{'arm64': 'y'}> -CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG policy<{'arm64': 'y'}> CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG policy<{'arm64': 'y'}> CONFIG_CRYPTO_DEV_SUN8I_SS policy<{'arm64': 'm'}> CONFIG_CRYPTO_DEV_SUN8I_SS_DEBUG policy<{'arm64': 'n'}> CONFIG_CRYPTO_DEV_SUN8I_SS_HASH policy<{'arm64': 'y'}> -CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG policy<{'arm64': 'y'}> CONFIG_CRYPTO_DEV_TEGRA policy<{'arm64': 'm'}> CONFIG_CRYPTO_DEV_TI_DTHEV2 policy<{'arm64': 'n'}> CONFIG_CRYPTO_DEV_VIRTIO policy<{'arm64': 'm'}> diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 1c63eff0a4a855..2a16de96e6a4ea 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -1247,6 +1247,9 @@ static int amdxdna_flush_bo(struct amdxdna_gem_obj *abo, u64 offset, u64 size) return -EINVAL; size = min(abo->mem.size, end) - offset; + if (!size) + return 0; + if (is_import_bo(abo)) drm_clflush_sg(abo->base.sgt); else if (amdxdna_gem_vmap(abo)) diff --git a/drivers/accel/ethosu/ethosu_drv.c b/drivers/accel/ethosu/ethosu_drv.c index ed9c748a54ad56..b2901eb8a7a03e 100644 --- a/drivers/accel/ethosu/ethosu_drv.c +++ b/drivers/accel/ethosu/ethosu_drv.c @@ -342,6 +342,8 @@ static int ethosu_probe(struct platform_device *pdev) dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); ethosudev->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ethosudev->regs)) + return PTR_ERR(ethosudev->regs); ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, ðosudev->clks); if (ethosudev->num_clks < 0) diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c index 1e2465279aae68..1e4b65f6293348 100644 --- a/drivers/accel/ethosu/ethosu_job.c +++ b/drivers/accel/ethosu/ethosu_job.c @@ -152,6 +152,13 @@ static void ethosu_job_err_cleanup(struct ethosu_job *job) drm_gem_object_put(job->cmd_bo); + if (job->done_fence) { + if (dma_fence_was_initialized(job->done_fence)) + dma_fence_put(job->done_fence); + else + dma_fence_free(job->done_fence); + } + kfree(job); } @@ -162,7 +169,6 @@ static void ethosu_job_cleanup(struct kref *ref) pm_runtime_put_autosuspend(job->dev->base.dev); - dma_fence_put(job->done_fence); dma_fence_put(job->inference_done_fence); ethosu_job_err_cleanup(job); @@ -393,7 +399,7 @@ static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file ejob->done_fence = kzalloc_obj(*ejob->done_fence); if (!ejob->done_fence) { ret = -ENOMEM; - goto out_cleanup_job; + goto out_put_job; } ret = drm_sched_job_init(&ejob->base, diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c index ac51bff39833fa..3141f210fcd1b5 100644 --- a/drivers/accel/rocket/rocket_job.c +++ b/drivers/accel/rocket/rocket_job.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,7 @@ rocket_copy_tasks(struct drm_device *dev, fail: kvfree(rjob->tasks); + rjob->tasks = NULL; return ret; } @@ -188,14 +190,19 @@ static int rocket_job_push(struct rocket_job *job) struct rocket_device *rdev = job->rdev; struct drm_gem_object **bos; struct ww_acquire_ctx acquire_ctx; + u32 bo_count; int ret = 0; - bos = kvmalloc_array(job->in_bo_count + job->out_bo_count, sizeof(void *), - GFP_KERNEL); + if (check_add_overflow(job->in_bo_count, job->out_bo_count, &bo_count)) + return -EINVAL; + + bos = kvmalloc_array(bo_count, sizeof(*bos), GFP_KERNEL); + if (!bos) + return -ENOMEM; memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *)); memcpy(&bos[job->in_bo_count], job->out_bos, job->out_bo_count * sizeof(void *)); - ret = drm_gem_lock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx); + ret = drm_gem_lock_reservations(bos, bo_count, &acquire_ctx); if (ret) goto err; @@ -220,7 +227,7 @@ static int rocket_job_push(struct rocket_job *job) rocket_attach_object_fences(job->out_bos, job->out_bo_count, job->inference_done_fence); err_unlock: - drm_gem_unlock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx); + drm_gem_unlock_reservations(bos, bo_count, &acquire_ctx); err: kvfree(bos); @@ -310,13 +317,13 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job) dma_fence_put(job->done_fence); job->done_fence = dma_fence_get(fence); - ret = pm_runtime_get_sync(core->dev); + ret = pm_runtime_resume_and_get(core->dev); if (ret < 0) - return fence; + goto err_put_fences; ret = iommu_attach_group(job->domain->domain, core->iommu_group); if (ret < 0) - return fence; + goto err_put_pm; scoped_guard(mutex, &core->job_lock) { core->in_flight_job = job; @@ -324,6 +331,14 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job) } return fence; + +err_put_pm: + pm_runtime_put(core->dev); +err_put_fences: + dma_fence_put(job->done_fence); + job->done_fence = NULL; + dma_fence_put(fence); + return ERR_PTR(ret); } static void rocket_job_handle_irq(struct rocket_core *core) @@ -549,6 +564,7 @@ static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file kref_init(&rjob->refcount); rjob->rdev = rdev; + rjob->domain = rocket_iommu_domain_get(file_priv); ret = drm_sched_job_init(&rjob->base, &file_priv->sched_entity, @@ -574,8 +590,6 @@ static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file rjob->out_bo_count = job->out_bo_handle_count; - rjob->domain = rocket_iommu_domain_get(file_priv); - ret = rocket_job_push(rjob); if (ret) goto out_cleanup_job; diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c index 78a77dd789a2e3..a632a65e90371a 100644 --- a/drivers/accessibility/speakup/main.c +++ b/drivers/accessibility/speakup/main.c @@ -2444,6 +2444,7 @@ static int __init speakup_init(void) mutex_lock(&spk_mutex); synth_release(); mutex_unlock(&spk_mutex); + spk_ttyio_unregister_ldisc(); speakup_kobj_exit(); error_kobjects: diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index a09636a4168ec5..e3c77a65f61c8d 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,42 @@ static struct notifier_block acpi_platform_notifier = { .notifier_call = acpi_platform_device_remove_notify, }; +static unsigned int acpi_platform_adjust_resources(struct acpi_device *adev, + struct resource *new_res, + struct resource *resources, + unsigned int count) +{ + unsigned int i; + + if (!(new_res->flags & (IORESOURCE_IO | IORESOURCE_MEM))) + return count; + + for (i = 0; i < count; ) { + struct resource *res = &resources[i]; + + /* + * Look for overlaps of resources of the same type that would + * cause resource insertion to fail down the road. + */ + if (__resource_contains_unbound(res, new_res) || + __resource_contains_unbound(new_res, res) || + resource_type(new_res) != resource_type(res) || + !resource_union(new_res, res, new_res)) { + i++; + continue; + } + + dev_info(&adev->dev, "%pR expanded due to overlap\n", new_res); + /* + * Eliminate the previously processed resource that overlapped + * with the new one because it is not necessary any more. + */ + memmove(res, res + 1, (--count - i) * sizeof(*res)); + } + + return count; +} + static void acpi_platform_fill_resource(struct acpi_device *adev, const struct resource *src, struct resource *dest) { @@ -151,10 +188,14 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, return ERR_PTR(-ENOMEM); } count = 0; - list_for_each_entry(rentry, &resource_list, node) + list_for_each_entry(rentry, &resource_list, node) { + count = acpi_platform_adjust_resources(adev, + rentry->res, + resources, + count); acpi_platform_fill_resource(adev, rentry->res, &resources[count++]); - + } acpi_dev_free_resource_list(&resource_list); } } diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index fc43df083738c4..45459a4cafae30 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,8 @@ static bool acpi_tad_rt_is_invalid(struct acpi_tad_rt *rt) rt->daylight > 3; } +static DEFINE_MUTEX(acpi_tad_aml_lock); + static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt) { acpi_handle handle = ACPI_HANDLE(dev); @@ -113,6 +116,8 @@ static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt) if (PM_RUNTIME_ACQUIRE_ERR(&pm)) return -ENXIO; + guard(mutex)(&acpi_tad_aml_lock); + status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval); if (ACPI_FAILURE(status) || retval) return -EIO; @@ -124,30 +129,27 @@ static int acpi_tad_evaluate_grt(struct device *dev, struct acpi_tad_rt *rt) { acpi_handle handle = ACPI_HANDLE(dev); struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER }; - union acpi_object *out_obj; - struct acpi_tad_rt *data; acpi_status status; int ret = -EIO; - status = acpi_evaluate_object(handle, "_GRT", NULL, &output); - if (ACPI_FAILURE(status)) - goto out_free; - - out_obj = output.pointer; - if (out_obj->type != ACPI_TYPE_BUFFER) - goto out_free; - - if (out_obj->buffer.length != sizeof(*rt)) - goto out_free; - - data = (struct acpi_tad_rt *)(out_obj->buffer.pointer); - if (!data->valid) - goto out_free; - - memcpy(rt, data, sizeof(*rt)); - ret = 0; + guard(mutex)(&acpi_tad_aml_lock); -out_free: + status = acpi_evaluate_object(handle, "_GRT", NULL, &output); + if (ACPI_SUCCESS(status)) { + union acpi_object *out_obj; + + out_obj = output.pointer; + if (out_obj->type == ACPI_TYPE_BUFFER && + out_obj->buffer.length == sizeof(*rt)) { + struct acpi_tad_rt *data; + + data = (struct acpi_tad_rt *)(out_obj->buffer.pointer); + if (data->valid) { + memcpy(rt, data, sizeof(*rt)); + ret = 0; + } + } + } ACPI_FREE(output.pointer); return ret; } @@ -193,6 +195,8 @@ static int __acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id, args[0].integer.value = timer_id; args[1].integer.value = value; + guard(mutex)(&acpi_tad_aml_lock); + status = acpi_evaluate_integer(handle, method, &arg_list, &retval); if (ACPI_FAILURE(status) || retval) return -EIO; @@ -215,6 +219,8 @@ static int __acpi_tad_wake_read(struct device *dev, char *method, u32 timer_id, args[0].integer.value = timer_id; + guard(mutex)(&acpi_tad_aml_lock); + status = acpi_evaluate_integer(handle, method, &arg_list, retval); if (ACPI_FAILURE(status)) return -EIO; @@ -416,6 +422,8 @@ static int acpi_tad_clear_status(struct device *dev, u32 timer_id) if (PM_RUNTIME_ACQUIRE_ERR(&pm)) return -ENXIO; + guard(mutex)(&acpi_tad_aml_lock); + status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval); if (ACPI_FAILURE(status) || retval) return -EIO; @@ -456,6 +464,8 @@ static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id) if (PM_RUNTIME_ACQUIRE_ERR(&pm)) return -ENXIO; + guard(mutex)(&acpi_tad_aml_lock); + status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval); if (ACPI_FAILURE(status)) return -EIO; diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index bf65e346153145..15ab797641cfcc 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -108,7 +108,7 @@ static inline u64 erst_get_timeout(void) if (erst_erange.attr & ERST_RANGE_SLOW) { timeout = ((erst_erange.timings & ERST_EXEC_TIMING_MAX_MASK) >> - ERST_EXEC_TIMING_MAX_SHIFT) * NSEC_PER_MSEC; + ERST_EXEC_TIMING_MAX_SHIFT) * NSEC_PER_USEC; if (timeout < FIRMWARE_TIMEOUT) timeout = FIRMWARE_TIMEOUT; } diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 3236a3ce79d6bd..c40df9a3b37cda 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -576,7 +576,7 @@ static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, return false; p = (char *)(err + 1); - length -= sizeof(err); + length -= sizeof(*err); for (i = 0; i < err->err_info_num; i++) { struct cper_arm_err_info *err_info; @@ -749,7 +749,7 @@ static DEFINE_KFIFO(cxl_cper_prot_err_fifo, struct cxl_cper_prot_err_work_data, CXL_CPER_PROT_ERR_FIFO_DEPTH); /* Synchronize schedule_work() with cxl_cper_prot_err_work changes */ -static DEFINE_SPINLOCK(cxl_cper_prot_err_work_lock); +static DEFINE_RAW_SPINLOCK(cxl_cper_prot_err_work_lock); struct work_struct *cxl_cper_prot_err_work; static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err, @@ -761,7 +761,7 @@ static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err, if (cxl_cper_sec_prot_err_valid(prot_err)) return; - guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock); + guard(raw_spinlock_irqsave)(&cxl_cper_prot_err_work_lock); if (!cxl_cper_prot_err_work) return; @@ -780,10 +780,11 @@ static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err, int cxl_cper_register_prot_err_work(struct work_struct *work) { - if (cxl_cper_prot_err_work) - return -EINVAL; + guard(raw_spinlock_irqsave)(&cxl_cper_prot_err_work_lock); - guard(spinlock)(&cxl_cper_prot_err_work_lock); + if (WARN_ONCE(cxl_cper_prot_err_work, + "CPER-CXL kfifo consumer already registered\n")) + return -EINVAL; cxl_cper_prot_err_work = work; return 0; } @@ -791,11 +792,18 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_register_prot_err_work, "CXL"); int cxl_cper_unregister_prot_err_work(struct work_struct *work) { - if (cxl_cper_prot_err_work != work) - return -EINVAL; + scoped_guard(raw_spinlock_irqsave, &cxl_cper_prot_err_work_lock) { + if (WARN_ONCE(cxl_cper_prot_err_work != work, + "CPER-CXL kfifo consumer mismatch on unregister\n")) + return -EINVAL; + cxl_cper_prot_err_work = NULL; + } + + cancel_work_sync(work); + + /* Discard stale entries so they are not replayed on next module load */ + kfifo_reset(&cxl_cper_prot_err_fifo); - guard(spinlock)(&cxl_cper_prot_err_work_lock); - cxl_cper_prot_err_work = NULL; return 0; } EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_prot_err_work, "CXL"); @@ -811,7 +819,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_prot_err_kfifo_get, "CXL"); DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH); /* Synchronize schedule_work() with cxl_cper_work changes */ -static DEFINE_SPINLOCK(cxl_cper_work_lock); +static DEFINE_RAW_SPINLOCK(cxl_cper_work_lock); struct work_struct *cxl_cper_work; static void cxl_cper_post_event(enum cxl_event_type event_type, @@ -831,7 +839,7 @@ static void cxl_cper_post_event(enum cxl_event_type event_type, return; } - guard(spinlock_irqsave)(&cxl_cper_work_lock); + guard(raw_spinlock_irqsave)(&cxl_cper_work_lock); if (!cxl_cper_work) return; @@ -849,10 +857,11 @@ static void cxl_cper_post_event(enum cxl_event_type event_type, int cxl_cper_register_work(struct work_struct *work) { - if (cxl_cper_work) + guard(raw_spinlock_irqsave)(&cxl_cper_work_lock); + if (WARN_ONCE(cxl_cper_work, + "CXL CPER kfifo consumer already registered\n")) return -EINVAL; - guard(spinlock)(&cxl_cper_work_lock); cxl_cper_work = work; return 0; } @@ -860,11 +869,18 @@ EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, "CXL"); int cxl_cper_unregister_work(struct work_struct *work) { - if (cxl_cper_work != work) - return -EINVAL; + scoped_guard(raw_spinlock_irqsave, &cxl_cper_work_lock) { + if (WARN_ONCE(cxl_cper_work != work, + "CXL CPER kfifo consumer mismatch on unregister\n")) + return -EINVAL; + cxl_cper_work = NULL; + } + + cancel_work_sync(work); + + /* Discard stale entries so they are not replayed on next module load */ + kfifo_reset(&cxl_cper_fifo); - guard(spinlock)(&cxl_cper_work_lock); - cxl_cper_work = NULL; return 0; } EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, "CXL"); diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index f5e0eb299610b9..f8fa0d9a1f4c79 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,9 @@ struct acpi_battery { struct device *phys_dev; struct notifier_block pm_nb; struct list_head list; + unsigned long flags; + + struct mutex property_lock; /* Protects properties below. */ unsigned long update_time; int revision; int rate_now; @@ -123,7 +127,6 @@ struct acpi_battery { char oem_info[MAX_STRING_LENGTH]; int state; int power_unit; - unsigned long flags; }; #define to_acpi_battery(x) power_supply_get_drvdata(x) @@ -180,20 +183,6 @@ static bool acpi_battery_is_degraded(struct acpi_battery *battery) battery->full_charge_capacity < battery->design_capacity; } -static int acpi_battery_handle_discharging(struct acpi_battery *battery) -{ - /* - * Some devices wrongly report discharging if the battery's charge level - * was above the device's start charging threshold atm the AC adapter - * was plugged in and the device thus did not start a new charge cycle. - */ - if ((battery_ac_is_broken || power_supply_is_system_supplied()) && - battery->rate_now == 0) - return POWER_SUPPLY_STATUS_NOT_CHARGING; - - return POWER_SUPPLY_STATUS_DISCHARGING; -} - static int acpi_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -201,15 +190,41 @@ static int acpi_battery_get_property(struct power_supply *psy, int full_capacity = ACPI_BATTERY_VALUE_UNKNOWN, ret = 0; struct acpi_battery *battery = to_acpi_battery(psy); - if (acpi_battery_present(battery)) { - /* run battery update only if it is present */ - acpi_battery_get_state(battery); - } else if (psp != POWER_SUPPLY_PROP_PRESENT) - return -ENODEV; + /* run battery update only if it is present */ + if (!acpi_battery_present(battery)) { + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 0; + return 0; + default: + return -ENODEV; + } + } + + mutex_lock(&battery->property_lock); + + acpi_battery_get_state(battery); + switch (psp) { case POWER_SUPPLY_PROP_STATUS: + /* + * Some devices wrongly report discharging if the battery's charge level + * was above the device's start charging threshold atm the AC adapter + * was plugged in and the device thus did not start a new charge cycle. + */ if (battery->state & ACPI_BATTERY_STATE_DISCHARGING) - val->intval = acpi_battery_handle_discharging(battery); + if (battery->rate_now != 0) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } else if (battery_ac_is_broken) { + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + mutex_unlock(&battery->property_lock); + + val->intval = power_supply_is_system_supplied() + ? POWER_SUPPLY_STATUS_NOT_CHARGING + : POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + } else if (battery->state & ACPI_BATTERY_STATE_CHARGING) /* Validate the status by checking the current. */ if (battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN && @@ -311,6 +326,8 @@ static int acpi_battery_get_property(struct power_supply *psy, default: ret = -EINVAL; } + + mutex_unlock(&battery->property_lock); return ret; } @@ -533,6 +550,8 @@ static int acpi_battery_get_info(struct acpi_battery *battery) int use_bix; int result = -ENODEV; + lockdep_assert_held(&battery->property_lock); + if (!acpi_battery_present(battery)) return 0; @@ -572,6 +591,8 @@ static int acpi_battery_get_state(struct acpi_battery *battery) acpi_status status = 0; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + lockdep_assert_held(&battery->property_lock); + if (!acpi_battery_present(battery)) return 0; @@ -625,6 +646,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery) { acpi_status status = 0; + lockdep_assert_held(&battery->property_lock); + if (!acpi_battery_present(battery) || !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags)) return -ENODEV; @@ -642,6 +665,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery) static int acpi_battery_init_alarm(struct acpi_battery *battery) { + lockdep_assert_held(&battery->property_lock); + /* See if alarms are supported, and if so, set default */ if (!acpi_has_method(battery->device->handle, "_BTP")) { clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags); @@ -659,6 +684,8 @@ static ssize_t acpi_battery_alarm_show(struct device *dev, { struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); + guard(mutex)(&battery->property_lock); + return sysfs_emit(buf, "%d\n", battery->alarm * 1000); } @@ -668,9 +695,15 @@ static ssize_t acpi_battery_alarm_store(struct device *dev, { unsigned long x; struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); + int err; - if (sscanf(buf, "%lu\n", &x) == 1) - battery->alarm = x/1000; + err = kstrtoul(buf, 10, &x); + if (err) + return err; + + guard(mutex)(&battery->property_lock); + + battery->alarm = x / 1000; if (acpi_battery_present(battery)) acpi_battery_set_alarm(battery); return count; @@ -854,12 +887,17 @@ static int sysfs_add_battery(struct acpi_battery *battery) .no_wakeup_source = true, }; bool full_cap_broken = false; + int power_unit; - if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) && - !ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity)) - full_cap_broken = true; + scoped_guard(mutex, &battery->property_lock) { + power_unit = battery->power_unit; - if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) { + if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) && + !ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity)) + full_cap_broken = true; + } + + if (power_unit == ACPI_BATTERY_POWER_UNIT_MA) { if (full_cap_broken) { battery->bat_desc.properties = charge_battery_full_cap_broken_props; @@ -913,6 +951,9 @@ static void sysfs_remove_battery(struct acpi_battery *battery) static void find_battery(const struct dmi_header *dm, void *private) { struct acpi_battery *battery = (struct acpi_battery *)private; + + lockdep_assert_held(&battery->property_lock); + /* Note: the hardcoded offsets below have been extracted from * the source code of dmidecode. */ @@ -944,6 +985,8 @@ static void find_battery(const struct dmi_header *dm, void *private) */ static void acpi_battery_quirks(struct acpi_battery *battery) { + lockdep_assert_held(&battery->property_lock); + if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)) return; @@ -996,30 +1039,38 @@ static void acpi_battery_quirks(struct acpi_battery *battery) static int acpi_battery_update(struct acpi_battery *battery, bool resume) { int result = acpi_battery_get_status(battery); + bool wakeup; if (result) return result; if (!acpi_battery_present(battery)) { sysfs_remove_battery(battery); - battery->update_time = 0; + scoped_guard(mutex, &battery->property_lock) + battery->update_time = 0; return 0; } if (resume) return 0; - if (!battery->update_time) { - result = acpi_battery_get_info(battery); + scoped_guard(mutex, &battery->property_lock) { + if (!battery->update_time) { + result = acpi_battery_get_info(battery); + if (result) + return result; + acpi_battery_init_alarm(battery); + } + + result = acpi_battery_get_state(battery); if (result) return result; - acpi_battery_init_alarm(battery); - } + acpi_battery_quirks(battery); - result = acpi_battery_get_state(battery); - if (result) - return result; - acpi_battery_quirks(battery); + wakeup = ((battery->state & ACPI_BATTERY_STATE_CRITICAL) || + (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) && + (battery->capacity_now <= battery->alarm))); + } if (!battery->bat) { result = sysfs_add_battery(battery); @@ -1031,9 +1082,7 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume) * Wakeup the system if battery is critical low * or lower than the alarm level */ - if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) || - (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) && - (battery->capacity_now <= battery->alarm))) + if (wakeup) acpi_pm_wakeup_event(battery->phys_dev); return result; @@ -1046,12 +1095,14 @@ static void acpi_battery_refresh(struct acpi_battery *battery) if (!battery->bat) return; - power_unit = battery->power_unit; + scoped_guard(mutex, &battery->property_lock) { + power_unit = battery->power_unit; - acpi_battery_get_info(battery); + acpi_battery_get_info(battery); - if (power_unit == battery->power_unit) - return; + if (power_unit == battery->power_unit) + return; + } /* The battery has changed its reporting units. */ sysfs_remove_battery(battery); @@ -1106,17 +1157,21 @@ static int battery_notify(struct notifier_block *nb, } else { int result; - result = acpi_battery_get_info(battery); - if (result) - return result; + scoped_guard(mutex, &battery->property_lock) { + result = acpi_battery_get_info(battery); + if (result) + return result; + } result = sysfs_add_battery(battery); if (result) return result; } - acpi_battery_init_alarm(battery); - acpi_battery_get_state(battery); + scoped_guard(mutex, &battery->property_lock) { + acpi_battery_init_alarm(battery); + acpi_battery_get_state(battery); + } } return 0; @@ -1258,6 +1313,10 @@ static int acpi_battery_probe(struct platform_device *pdev) if (result) return result; + result = devm_mutex_init(&pdev->dev, &battery->property_lock); + if (result) + return result; + if (acpi_has_method(battery->device->handle, "_BIX")) set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags); diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 53d09ca98f0668..becb7e442b30e1 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1316,15 +1316,30 @@ static int cppc_set_reg_val(int cpu, enum cppc_regs reg_idx, u64 val) return cpc_write(cpu, reg, val); } +static bool cppc_desired_perf_readable(const struct cpc_desc *cpc_desc) +{ + return cpc_desc->version < CPPC_V4_REV; +} + /** * cppc_get_desired_perf - Get the desired performance register value. * @cpunum: CPU from which to get desired performance. * @desired_perf: Return address. * - * Return: 0 for success, -EIO otherwise. + * Return: 0 for success, -EOPNOTSUPP for _CPC revision 4 or later, and a + * negative errno otherwise. */ int cppc_get_desired_perf(int cpunum, u64 *desired_perf) { + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); + + if (!cpc_desc) + return -ENODEV; + + /* _CPC revision 4 no longer specifies Desired Performance as readable. */ + if (!cppc_desired_perf_readable(cpc_desc)) + return -EOPNOTSUPP; + return cppc_get_reg_val(cpunum, DESIRED_PERF, desired_perf); } EXPORT_SYMBOL_GPL(cppc_get_desired_perf); @@ -1830,12 +1845,14 @@ int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) u64 desired_perf = 0, min = 0, max = 0, energy_perf = 0, auto_sel = 0; int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); struct cppc_pcc_data *pcc_ss_data = NULL; + bool read_desired_perf; int ret = 0, regs_in_pcc = 0; if (!cpc_desc) { pr_debug("No CPC descriptor for CPU:%d\n", cpu); return -ENODEV; } + read_desired_perf = cppc_desired_perf_readable(cpc_desc); if (!perf_ctrls) { pr_debug("Invalid perf_ctrls pointer\n"); @@ -1849,7 +1866,8 @@ int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE]; /* Are any of the regs PCC ?*/ - if (CPC_IN_PCC(desired_perf_reg) || CPC_IN_PCC(min_perf_reg) || + if ((read_desired_perf && CPC_IN_PCC(desired_perf_reg)) || + CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg) || CPC_IN_PCC(energy_perf_reg) || CPC_IN_PCC(auto_sel_reg)) { if (pcc_ss_id < 0) { @@ -1881,7 +1899,7 @@ int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) } perf_ctrls->min_perf = min; - if (CPC_SUPPORTED(desired_perf_reg)) { + if (read_desired_perf && CPC_SUPPORTED(desired_perf_reg)) { ret = cpc_read(cpu, desired_perf_reg, &desired_perf); if (ret) goto out_err; diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c index 6283105bb0e8b2..9afd2c52fdbd88 100644 --- a/drivers/acpi/pfr_update.c +++ b/drivers/acpi/pfr_update.c @@ -120,7 +120,7 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr, struct pfru_device *pfru_dev) { acpi_handle handle = ACPI_HANDLE(pfru_dev->parent_dev); - union acpi_object *out_obj; + union acpi_object *out_obj, *elem; int ret = -EINVAL; out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, @@ -150,7 +150,9 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr, goto free_acpi_buffer; } - cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX].integer.value; + elem = out_obj->package.elements; + + cap_hdr->status = elem[CAP_STATUS_IDX].integer.value; if (cap_hdr->status != DSM_SUCCEED) { ret = -EBUSY; dev_dbg(pfru_dev->parent_dev, "Query cap Error Status:%d\n", @@ -158,29 +160,30 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr, goto free_acpi_buffer; } - cap_hdr->update_cap = out_obj->package.elements[CAP_UPDATE_IDX].integer.value; + if (elem[CAP_CODE_TYPE_IDX].buffer.length > sizeof(cap_hdr->code_type) || + elem[CAP_DRV_TYPE_IDX].buffer.length > sizeof(cap_hdr->drv_type) || + elem[CAP_PLAT_ID_IDX].buffer.length > sizeof(cap_hdr->platform_id) || + elem[CAP_OEM_ID_IDX].buffer.length > sizeof(cap_hdr->oem_id)) + goto free_acpi_buffer; + + cap_hdr->update_cap = elem[CAP_UPDATE_IDX].integer.value; memcpy(&cap_hdr->code_type, - out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.pointer, - out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.length); - cap_hdr->fw_version = - out_obj->package.elements[CAP_FW_VER_IDX].integer.value; - cap_hdr->code_rt_version = - out_obj->package.elements[CAP_CODE_RT_VER_IDX].integer.value; + elem[CAP_CODE_TYPE_IDX].buffer.pointer, + elem[CAP_CODE_TYPE_IDX].buffer.length); + cap_hdr->fw_version = elem[CAP_FW_VER_IDX].integer.value; + cap_hdr->code_rt_version = elem[CAP_CODE_RT_VER_IDX].integer.value; memcpy(&cap_hdr->drv_type, - out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.pointer, - out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.length); - cap_hdr->drv_rt_version = - out_obj->package.elements[CAP_DRV_RT_VER_IDX].integer.value; - cap_hdr->drv_svn = - out_obj->package.elements[CAP_DRV_SVN_IDX].integer.value; + elem[CAP_DRV_TYPE_IDX].buffer.pointer, + elem[CAP_DRV_TYPE_IDX].buffer.length); + cap_hdr->drv_rt_version = elem[CAP_DRV_RT_VER_IDX].integer.value; + cap_hdr->drv_svn = elem[CAP_DRV_SVN_IDX].integer.value; memcpy(&cap_hdr->platform_id, - out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.pointer, - out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.length); + elem[CAP_PLAT_ID_IDX].buffer.pointer, + elem[CAP_PLAT_ID_IDX].buffer.length); memcpy(&cap_hdr->oem_id, - out_obj->package.elements[CAP_OEM_ID_IDX].buffer.pointer, - out_obj->package.elements[CAP_OEM_ID_IDX].buffer.length); - cap_hdr->oem_info_len = - out_obj->package.elements[CAP_OEM_INFO_IDX].buffer.length; + elem[CAP_OEM_ID_IDX].buffer.pointer, + elem[CAP_OEM_ID_IDX].buffer.length); + cap_hdr->oem_info_len = elem[CAP_OEM_INFO_IDX].buffer.length; ret = 0; diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 58f512f8952a92..9b8c0935001cb3 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -2614,6 +2614,51 @@ static irqreturn_t ahci_thunderx_irq_handler(int irq, void *dev_instance) } #endif +/* + * The Marvell 88SE6111/6121/6145 ("Thor") family stops reporting interrupts + * for a port when HOST_IRQ_STAT is cleared while PxIS still holds bits: PxIS + * keeps its content, HOST_IRQ_STAT reads back as 0, the port is never looked + * at again and the command in flight only ends in a timeout. On a 88SE6121 + * this makes every SATA-2 or SATA-3 disk fail to IDENTIFY, while SATA-1 disks + * happen to win the race often enough to work. + * + * Clearing the host status before servicing the ports avoids it. Marvell's + * own driver for these chips does the same and says so ("clear global before + * channel"), and ahci_xgene handles its broken edge latch the same way. The + * price is at most one spurious interrupt per valid one, which is why this is + * not the generic behaviour - see AHCI 1.1 section 10.6.2. + * + * Link: https://bugzilla.kernel.org/show_bug.cgi?id=216094 + */ +static irqreturn_t ahci_mv_irq_handler(int irq, void *dev_instance) +{ + struct ata_host *host = dev_instance; + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + unsigned int rc; + u32 irq_stat, irq_masked; + + irq_stat = readl(mmio + HOST_IRQ_STAT); + if (!irq_stat) + return IRQ_NONE; + + irq_masked = irq_stat & hpriv->port_map; + + spin_lock(&host->lock); + + /* + * Use the unmasked value to clear the interrupt, as a spurious pending + * event on a dummy port might cause a screaming IRQ. + */ + writel(irq_stat, mmio + HOST_IRQ_STAT); + + rc = ahci_handle_port_intr(host, irq_masked); + + spin_unlock(&host->lock); + + return IRQ_RETVAL(rc); +} + static void ahci_remap_check(struct pci_dev *pdev, int bar, struct ahci_host_priv *hpriv) { @@ -2917,6 +2962,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return -ENOMEM; hpriv->flags |= (unsigned long)pi.private_data; + /* the Marvell "Thor" family needs HOST_IRQ_STAT cleared first */ + if (board_id == board_ahci_mv) + hpriv->irq_handler = ahci_mv_irq_handler; + /* MCP65 revision A1 and A2 can't do MSI */ if (board_id == board_ahci_mcp65 && (pdev->revision == 0xa1 || pdev->revision == 0xa2)) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index d2160ee7ca7d4b..f0bb4068d14d92 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3545,17 +3545,13 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) static size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 trmax, u64 sector, u32 count) { - struct scsi_device *sdp = cmd->device; - size_t len = sdp->sector_size; + size_t len = ATA_SECT_SIZE; size_t r; __le64 *buf; u32 i = 0; unsigned long flags; - WARN_ON(len > ATA_SCSI_RBUF_SIZE); - - if (len > ATA_SCSI_RBUF_SIZE) - len = ATA_SCSI_RBUF_SIZE; + BUILD_BUG_ON(ATA_SECT_SIZE > ATA_SCSI_RBUF_SIZE); spin_lock_irqsave(&ata_scsi_rbuf_lock, flags); buf = ((void *)ata_scsi_rbuf); @@ -3590,13 +3586,11 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) { struct ata_taskfile *tf = &qc->tf; struct scsi_cmnd *scmd = qc->scsicmd; - struct scsi_device *sdp = scmd->device; - size_t len = sdp->sector_size; struct ata_device *dev = qc->dev; const u8 *cdb = scmd->cmnd; u64 block; u32 n_block; - const u32 trmax = len >> 3; + const u32 trmax = ATA_MAX_TRIM_RNUM; u32 size; u16 fp; u8 bp = 0xff; @@ -3640,13 +3634,13 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) goto invalid_param_len; /* - * size must match sector size in bytes - * For DATA SET MANAGEMENT TRIM in ACS-2 nsect (aka count) - * is defined as number of 512 byte blocks to be transferred. + * The TRIM descriptor is a single 512-byte page, which is the maximum + * WRITE SAME length advertised in the Block Limits VPD page. For DATA + * SET MANAGEMENT TRIM the COUNT field (aka nsect) is the number of + * 512-byte blocks to be transferred. */ - size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block); - if (size != len) + if (size != ATA_SECT_SIZE) goto invalid_param_len; if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) { @@ -3672,6 +3666,12 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) ATA_TFLAG_WRITE; ata_qc_set_pc_nbytes(qc); + /* + * The DSM TRIM payload is a single 512-byte page, which may be smaller + * than the WRITE SAME data-out buffer (one logical block); only + * transfer that page so the length matches the COUNT field. + */ + qc->nbytes = size; return 0; diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c index 09020bb8ad15fa..ae2692ec396396 100644 --- a/drivers/auxdisplay/charlcd.c +++ b/drivers/auxdisplay/charlcd.c @@ -595,6 +595,16 @@ static int charlcd_init(struct charlcd *lcd) return 0; } +static void charlcd_deinit(struct charlcd *lcd) +{ + struct charlcd_priv *priv = charlcd_to_priv(lcd); + + if (lcd->ops->backlight) { + cancel_delayed_work_sync(&priv->bl_work); + lcd->ops->backlight(lcd, CHARLCD_OFF); + } +} + struct charlcd *charlcd_alloc(unsigned int drvdata_size) { struct charlcd_priv *priv; @@ -654,8 +664,10 @@ int charlcd_register(struct charlcd *lcd) return ret; ret = misc_register(&charlcd_dev); - if (ret) + if (ret) { + charlcd_deinit(lcd); return ret; + } the_charlcd = lcd; register_reboot_notifier(&panel_notifier); @@ -665,16 +677,11 @@ EXPORT_SYMBOL_GPL(charlcd_register); int charlcd_unregister(struct charlcd *lcd) { - struct charlcd_priv *priv = charlcd_to_priv(lcd); - unregister_reboot_notifier(&panel_notifier); charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-"); misc_deregister(&charlcd_dev); the_charlcd = NULL; - if (lcd->ops->backlight) { - cancel_delayed_work_sync(&priv->bl_work); - priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF); - } + charlcd_deinit(lcd); return 0; } diff --git a/drivers/base/arch_numa.c b/drivers/base/arch_numa.c index c99f2ab105e5b7..a36c939b8e9052 100644 --- a/drivers/base/arch_numa.c +++ b/drivers/base/arch_numa.c @@ -105,6 +105,18 @@ static void __init setup_node_to_cpumask_map(void) if (nr_node_ids == MAX_NUMNODES) setup_nr_node_ids(); + /* + * This check should never be true but it makes it clear to compilers + * that node_to_cpumask_map is bound by nr_node_ids, avoiding false + * positive fortify warnings when accessing node_to_cpumask_map in the + * for loop below. + */ + if (unlikely(nr_node_ids > MAX_NUMNODES)) { + pr_err("nr_node_ids (%u) is larger than MAX_NUMNODES (%u)\n", + nr_node_ids, MAX_NUMNODES); + return; + } + /* allocate and clear the mapping */ for (node = 0; node < nr_node_ids; node++) { alloc_bootmem_cpumask_var(&node_to_cpumask_map[node]); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index f71467f6ada4ee..4ed2e9b7e99b70 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -2234,6 +2234,7 @@ static int device_prepare(struct device *dev, pm_message_t state) if (ret < 0) { suspend_report_result(dev, callback, ret); + pm_runtime_unblock(dev); pm_runtime_put(dev); return ret; } diff --git a/drivers/base/property.c b/drivers/base/property.c index e08eadd66f4f9f..e0f570f48bc320 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -807,18 +807,31 @@ struct fwnode_handle * fwnode_get_next_child_node(const struct fwnode_handle *fwnode, struct fwnode_handle *child) { + const struct fwnode_handle *parent; + struct fwnode_handle *child_parent __free(fwnode_handle) = NULL; struct fwnode_handle *next; - if (IS_ERR_OR_NULL(fwnode)) + /* + * If this function is in a loop and the previous iteration returned + * an child from fwnode->secondary, then we need to use the secondary + * as parent rather than @fwnode. + */ + if (child) { + child_parent = fwnode_get_parent(child); + parent = child_parent; + } else { + parent = fwnode; + } + if (IS_ERR_OR_NULL(parent)) return NULL; /* Try to find a child in primary fwnode */ - next = fwnode_call_ptr_op(fwnode, get_next_child_node, child); + next = fwnode_call_ptr_op(parent, get_next_child_node, child); if (next) return next; /* When no more children in primary, continue with secondary */ - return fwnode_call_ptr_op(fwnode->secondary, get_next_child_node, child); + return fwnode_get_next_child_node(parent->secondary, NULL); } EXPORT_SYMBOL_GPL(fwnode_get_next_child_node); diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index 2a22f9dc1f2f15..037e08cb97ce04 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -2651,6 +2651,12 @@ static int ublk_ch_mmap(struct file *filp, struct vm_area_struct *vma) if (vma->vm_flags & VM_WRITE) return -EPERM; + /* + * The per-queue command buffer is kernel-written ABI; prevent + * the daemon from upgrading to writable via mprotect(). + */ + vm_flags_clear(vma, VM_MAYWRITE); + end = UBLKSRV_CMD_BUF_OFFSET + ub->dev_info.nr_hw_queues * max_sz; if (phys_off < UBLKSRV_CMD_BUF_OFFSET || phys_off >= end) return -EINVAL; diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c index 55eeb6aac0ea3b..1333fae05ac5dd 100644 --- a/drivers/block/zloop.c +++ b/drivers/block/zloop.c @@ -478,7 +478,8 @@ static int zloop_finish_zone(struct zloop_device *zlo, unsigned int zone_no) zone->cond == BLK_ZONE_COND_FULL) goto unlock; - if (vfs_truncate(&zone->file->f_path, zlo->zone_size << SECTOR_SHIFT)) { + if (vfs_truncate(&zone->file->f_path, + zlo->zone_capacity << SECTOR_SHIFT)) { set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags); ret = -EIO; goto unlock; diff --git a/drivers/block/zram/backend_deflate.c b/drivers/block/zram/backend_deflate.c index f92a52a720d1e2..b3f7d08b49d934 100644 --- a/drivers/block/zram/backend_deflate.c +++ b/drivers/block/zram/backend_deflate.c @@ -24,8 +24,16 @@ static int deflate_setup_params(struct zcomp_params *params) { if (params->level == ZCOMP_PARAM_NOT_SET) params->level = Z_DEFAULT_COMPRESSION; - if (params->deflate.winbits == ZCOMP_PARAM_NOT_SET) + if (params->deflate.winbits == ZCOMP_PARAM_NOT_SET) { params->deflate.winbits = DEFLATE_DEF_WINBITS; + } else { + s32 wb = params->deflate.winbits; + + if ((wb < -15 || wb > -9) && (wb < 9 || wb > 15)) { + pr_err("invalid deflate winbits: %d\n", wb); + return -EINVAL; + } + } return 0; } diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index ace65c586072ba..6cb44e229f1ece 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -57,14 +57,12 @@ static size_t huge_class_size; static const struct block_device_operations zram_devops; static void slot_free(struct zram *zram, u32 index); -#define slot_dep_map(zram, index) (&(zram)->table[(index)].dep_map) -static void slot_lock_init(struct zram *zram, u32 index) +static void slot_lock_init(struct zram *zram) { static struct lock_class_key __key; - lockdep_init_map(slot_dep_map(zram, index), "zram->table[index].lock", - &__key, 0); + lockdep_init_map(&zram->table_lock_map, "zram->table[index].lock", &__key, 0); } /* @@ -83,9 +81,9 @@ static __must_check bool slot_trylock(struct zram *zram, u32 index) { unsigned long *lock = &zram->table[index].__lock; - if (!test_and_set_bit_lock(ZRAM_ENTRY_LOCK, lock)) { - mutex_acquire(slot_dep_map(zram, index), 0, 1, _RET_IP_); - lock_acquired(slot_dep_map(zram, index), _RET_IP_); + if (!test_and_set_bit_lock(ZRAM_ENTRY_LOCK_BIT, lock)) { + mutex_acquire(&zram->table_lock_map, 0, 1, _RET_IP_); + lock_acquired(&zram->table_lock_map, _RET_IP_); return true; } @@ -96,17 +94,17 @@ static void slot_lock(struct zram *zram, u32 index) { unsigned long *lock = &zram->table[index].__lock; - mutex_acquire(slot_dep_map(zram, index), 0, 0, _RET_IP_); - wait_on_bit_lock(lock, ZRAM_ENTRY_LOCK, TASK_UNINTERRUPTIBLE); - lock_acquired(slot_dep_map(zram, index), _RET_IP_); + mutex_acquire(&zram->table_lock_map, 0, 0, _RET_IP_); + wait_on_bit_lock(lock, ZRAM_ENTRY_LOCK_BIT, TASK_UNINTERRUPTIBLE); + lock_acquired(&zram->table_lock_map, _RET_IP_); } static void slot_unlock(struct zram *zram, u32 index) { unsigned long *lock = &zram->table[index].__lock; - mutex_release(slot_dep_map(zram, index), _RET_IP_); - clear_and_wake_up_bit(ZRAM_ENTRY_LOCK, lock); + mutex_release(&zram->table_lock_map, _RET_IP_); + clear_and_wake_up_bit(ZRAM_ENTRY_LOCK_BIT, lock); } static inline bool init_done(struct zram *zram) @@ -1244,8 +1242,8 @@ static ssize_t writeback_store(struct device *dev, const char *buf, size_t len) { struct zram *zram = dev_to_zram(dev); - u64 nr_pages = zram->disksize >> PAGE_SHIFT; - unsigned long lo = 0, hi = nr_pages; + u64 nr_pages; + unsigned long lo = 0, hi; struct zram_pp_ctl *pp_ctl = NULL; struct zram_wb_ctl *wb_ctl = NULL; char *args, *param, *val; @@ -1259,6 +1257,9 @@ static ssize_t writeback_store(struct device *dev, if (!zram->backing_dev) return -ENODEV; + nr_pages = zram->disksize >> PAGE_SHIFT; + hi = nr_pages; + pp_ctl = init_pp_ctl(); if (!pp_ctl) return -ENOMEM; @@ -1549,7 +1550,7 @@ static ssize_t read_block_state(struct file *file, char __user *buf, char *kbuf; ssize_t index, written = 0; struct zram *zram = file->private_data; - unsigned long nr_pages = zram->disksize >> PAGE_SHIFT; + unsigned long nr_pages; kbuf = kvmalloc(count, GFP_KERNEL); if (!kbuf) @@ -1561,6 +1562,8 @@ static ssize_t read_block_state(struct file *file, char __user *buf, return -EINVAL; } + nr_pages = zram->disksize >> PAGE_SHIFT; + for (index = *ppos; index < nr_pages; index++) { int copied; @@ -1984,7 +1987,7 @@ static void zram_meta_free(struct zram *zram, u64 disksize) static bool zram_meta_alloc(struct zram *zram, u64 disksize) { - size_t num_pages, index; + size_t num_pages; num_pages = disksize >> PAGE_SHIFT; zram->table = vzalloc(array_size(num_pages, sizeof(*zram->table))); @@ -2001,8 +2004,7 @@ static bool zram_meta_alloc(struct zram *zram, u64 disksize) if (!huge_class_size) huge_class_size = zs_huge_class_size(zram->mem_pool); - for (index = 0; index < num_pages; index++) - slot_lock_init(zram, index); + slot_lock_init(zram); return true; } @@ -2836,6 +2838,7 @@ static void zram_destroy_comps(struct zram *zram) zram->comp_algs[prio] = NULL; zram_comp_params_reset(zram); + comp_algorithm_set(zram, ZRAM_PRIMARY_COMP, default_compressor); } static void zram_reset_device(struct zram *zram) @@ -2853,8 +2856,6 @@ static void zram_reset_device(struct zram *zram) zram_destroy_comps(zram); memset(&zram->stats, 0, sizeof(zram->stats)); reset_bdev(zram); - - comp_algorithm_set(zram, ZRAM_PRIMARY_COMP, default_compressor); } static ssize_t disksize_store(struct device *dev, struct device_attribute *attr, diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 08d1774c15db61..996bb99ea8bbfe 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -15,6 +15,7 @@ #ifndef _ZRAM_DRV_H_ #define _ZRAM_DRV_H_ +#include #include #include @@ -57,6 +58,19 @@ enum zram_pageflags { __NR_ZRAM_PAGEFLAGS, }; +/* + * The slot lock is a bit-wait lock on the whole __lock word, while + * flags and ac_time alias that word as two u32s. The lock bit must + * land in the slot that ZRAM_ENTRY_LOCK reserves in attr.flags; on + * 64-bit big-endian the flags word maps to the upper half of __lock, + * so the bit position has to be shifted up. + */ +#if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN) +#define ZRAM_ENTRY_LOCK_BIT (ZRAM_ENTRY_LOCK + 32) +#else +#define ZRAM_ENTRY_LOCK_BIT ZRAM_ENTRY_LOCK +#endif + /* * Allocated for each disk page. We use bit-lock (ZRAM_ENTRY_LOCK bit * of flags) to save memory. There can be plenty of entries and standard @@ -74,7 +88,6 @@ struct zram_table_entry { #endif } attr; }; - struct lockdep_map dep_map; }; struct zram_stats { @@ -107,6 +120,7 @@ struct zram_stats { struct zram { struct zram_table_entry *table; + struct lockdep_map table_lock_map; struct zs_pool *mem_pool; struct zcomp *comps[ZRAM_MAX_COMPS]; struct zcomp_params params[ZRAM_MAX_COMPS]; diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 7f54d2d2d13a04..03fa9409e3ee49 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -1343,19 +1343,6 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) if (!btrtl_dev->ic_info) return; - switch (btrtl_dev->project_id) { - case CHIP_ID_8761B: - /* RTL8761B/BU reports HCI version 5.1 but does not support - * the LE Extended Scan commands (Opcode 0x2042), causing - * repeated -EBUSY failures when BlueZ attempts extended - * scanning while a connection is active. - */ - hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN); - break; - default: - break; - } - switch (btrtl_dev->ic_info->lmp_subver) { case RTL_ROM_LMP_8703B: /* 8723CS reports two pages for local ext features, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index a5de6ccf915821..9160ca43679ea0 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -68,6 +68,7 @@ static struct usb_driver btusb_driver; #define BTUSB_INTEL_NO_WBS_SUPPORT BIT(26) #define BTUSB_ACTIONS_SEMI BIT(27) #define BTUSB_BARROT BIT(28) +#define BTUSB_BROKEN_EXT_SCAN BIT(29) static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -616,6 +617,10 @@ static const struct usb_device_id quirks_table[] = { { USB_DEVICE(0x0489, 0xe130), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + /* Realtek 8761BU Bluetooth devices */ + { USB_DEVICE(0x0bda, 0xa728), .driver_info = BTUSB_REALTEK | + BTUSB_BROKEN_EXT_SCAN }, + /* Realtek Bluetooth devices */ { USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01), .driver_info = BTUSB_REALTEK }, @@ -851,6 +856,12 @@ static const struct usb_device_id quirks_table[] = { { USB_DEVICE(0x37ad, 0x0600), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + /* Additional Realtek 8761CU Bluetooth devices */ + { USB_DEVICE(0x0b05, 0x1bef), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0b05, 0x1d70), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, + /* Additional Realtek 8821AE Bluetooth devices */ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3414), .driver_info = BTUSB_REALTEK }, @@ -4406,6 +4417,9 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_INVALID_LE_STATES) hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_STATES); + if (id->driver_info & BTUSB_BROKEN_EXT_SCAN) + hci_set_quirk(hdev, HCI_QUIRK_BROKEN_EXT_SCAN); + if (id->driver_info & BTUSB_DIGIANSWER) { data->cmdreq_type = USB_TYPE_VENDOR; hci_set_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE); diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 1a4fc3882fd2a7..cc419e1960de56 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -547,6 +547,7 @@ static int bcm_close(struct hci_uart *hu) if (IS_ENABLED(CONFIG_PM) && bdev->irq_acquired) { devm_free_irq(bdev->dev, bdev->irq, bdev); device_init_wakeup(bdev->dev, false); + pm_runtime_dont_use_autosuspend(bdev->dev); pm_runtime_disable(bdev->dev); } diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c index 925d0a6359453e..66d49b4715440a 100644 --- a/drivers/bluetooth/hci_bcm4377.c +++ b/drivers/bluetooth/hci_bcm4377.c @@ -2490,6 +2490,7 @@ static const struct bcm4377_hw bcm4377_hw_variants[] = { .has_bar0_core2_window2 = true, .broken_mws_transport_config = true, .broken_le_coded = true, + .broken_le_ext_adv_report_phy = true, .send_calibration = bcm4378_send_calibration, .send_ptb = bcm4378_send_ptb, }, diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 93cdde9818401a..a5e7e9b0ea7463 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -1023,8 +1023,10 @@ static void h5_btrtl_open(struct h5 *h5) static void h5_btrtl_close(struct h5 *h5) { - if (!test_bit(H5_WAKEUP_DISABLE, &h5->flags)) + if (!test_bit(H5_WAKEUP_DISABLE, &h5->flags)) { + pm_runtime_dont_use_autosuspend(&h5->hu->serdev->dev); pm_runtime_disable(&h5->hu->serdev->dev); + } gpiod_set_value_cansleep(h5->device_wake_gpio, 0); gpiod_set_value_cansleep(h5->enable_gpio, 0); diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index c31105b91e4767..1919547ff2a1ce 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -345,6 +345,7 @@ static int intel_set_power(struct hci_uart *hu, bool powered) devm_free_irq(&idev->pdev->dev, idev->irq, idev); device_wakeup_disable(&idev->pdev->dev); + pm_runtime_dont_use_autosuspend(&idev->pdev->dev); pm_runtime_disable(&idev->pdev->dev); } } diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 2ad42c3bbaac78..27644d2e53f8ca 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -451,7 +451,7 @@ static int hci_uart_setup(struct hci_dev *hdev) if (IS_ERR(skb)) { BT_ERR("%s: Reading local version information failed (%ld)", hdev->name, PTR_ERR(skb)); - return 0; + return PTR_ERR(skb); } if (skb->len != sizeof(*ver)) { diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c index 593d9cefbbf925..d2eaf2f12aa278 100644 --- a/drivers/bluetooth/hci_serdev.c +++ b/drivers/bluetooth/hci_serdev.c @@ -221,7 +221,7 @@ static int hci_uart_setup(struct hci_dev *hdev) if (IS_ERR(skb)) { bt_dev_err(hdev, "Reading local version info failed (%ld)", PTR_ERR(skb)); - return 0; + return PTR_ERR(skb); } if (skb->len != sizeof(*ver)) diff --git a/drivers/cdx/cdx.c b/drivers/cdx/cdx.c index d3d23024726215..a118a89a7779d6 100644 --- a/drivers/cdx/cdx.c +++ b/drivers/cdx/cdx.c @@ -738,7 +738,6 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num) sysfs_bin_attr_init(res_attr); - cdx_dev->res_attr[num] = res_attr; sprintf(res_attr_name, "resource%d", num); res_attr->mmap = cdx_mmap_resource; @@ -747,8 +746,12 @@ static int cdx_create_res_attr(struct cdx_device *cdx_dev, int num) res_attr->size = cdx_resource_len(cdx_dev, num); res_attr->private = (void *)(unsigned long)num; ret = sysfs_create_bin_file(&cdx_dev->dev.kobj, res_attr); - if (ret) + if (ret) { kfree(res_attr); + return ret; + } + + cdx_dev->res_attr[num] = res_attr; return ret; } diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c index 9a8c00586ab096..f5bfe54c01dc79 100644 --- a/drivers/char/hw_random/stm32-rng.c +++ b/drivers/char/hw_random/stm32-rng.c @@ -368,11 +368,6 @@ static int stm32_rng_init(struct hwrng *rng) return 0; } -static void stm32_rng_remove(struct platform_device *ofdev) -{ - pm_runtime_disable(&ofdev->dev); -} - static int __maybe_unused stm32_rng_runtime_suspend(struct device *dev) { struct stm32_rng_private *priv = dev_get_drvdata(dev); @@ -590,7 +585,9 @@ static int stm32_rng_probe(struct platform_device *ofdev) pm_runtime_set_autosuspend_delay(dev, 100); pm_runtime_use_autosuspend(dev); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; return devm_hwrng_register(dev, &priv->rng); } @@ -602,7 +599,6 @@ static struct platform_driver stm32_rng_driver = { .of_match_table = stm32_rng_match, }, .probe = stm32_rng_probe, - .remove = stm32_rng_remove, }; module_platform_driver(stm32_rng_driver); diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c index 680ff15c30ab9a..e4c50d9ae3e135 100644 --- a/drivers/char/ipmi/ipmb_dev_int.c +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -141,13 +141,14 @@ static ssize_t ipmb_write(struct file *file, const char __user *buf, u8 msg[MAX_MSG_LEN]; ssize_t ret; - if (count > sizeof(msg)) + if (!count || count > sizeof(msg)) return -EINVAL; if (copy_from_user(&msg, buf, count)) return -EFAULT; - if (count < msg[0]) + if (msg[IPMB_MSG_LEN_IDX] < IPMB_REQUEST_LEN_MIN || + count < (size_t)msg[IPMB_MSG_LEN_IDX] + 1) return -EINVAL; rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]); diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index ab4c85f3d6fe8f..0330d8412a0e73 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -1391,6 +1391,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user) } } mutex_unlock(&intf->cmd_rcvrs_mutex); + synchronize_rcu(); while (rcvrs) { rcvr = rcvrs; rcvrs = rcvr->next; @@ -3740,6 +3741,7 @@ int ipmi_add_smi(struct module *owner, sysfs_attr_init(&intf->maintenance_mode_devattr.attr); rv = device_create_file(intf->si_dev, &intf->maintenance_mode_devattr); if (rv) { + device_remove_file(intf->si_dev, &intf->nr_msgs_devattr); device_remove_file(intf->si_dev, &intf->nr_users_devattr); goto out_err_bmc_reg; } @@ -3757,12 +3759,14 @@ int ipmi_add_smi(struct module *owner, out_err_bmc_reg: ipmi_bmc_unregister(intf); out_err_started: + intf->in_shutdown = true; if (intf->handlers->shutdown) intf->handlers->shutdown(intf->send_info); out_err: list_del(&intf->link); mutex_unlock(&ipmi_interfaces_mutex); mutex_unlock(&smi_watchers_mutex); + cancel_work_sync(&intf->smi_work); kref_put(&intf->refcount, intf_free); return rv; diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index d44903b299294d..aa3673002f3aa6 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -182,8 +182,10 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value, timeout); if (rc > 0) return 0; - /* At this point we know that the SINT pin is asserted, so we - * do not need to do i2c_nuvoton_check_status */ + + disable_irq(priv->irq); + if (rc < 0) + return rc; } else { unsigned long ten_msec, stop; bool status_valid; diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig index 2c09fd729bab04..fd8440122ec264 100644 --- a/drivers/clk/mediatek/Kconfig +++ b/drivers/clk/mediatek/Kconfig @@ -1006,6 +1006,7 @@ config COMMON_CLK_MT8196 tristate "Clock driver for MediaTek MT8196" depends on ARM64 || COMPILE_TEST select COMMON_CLK_MEDIATEK + select REGMAP_MMIO default ARCH_MEDIATEK help This driver supports MediaTek MT8196 basic clocks. diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index 39af7b1868e320..7a7b104182ff83 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -1393,7 +1393,7 @@ static struct clk_regmap gxbb_32k_clk_sel = { .name = "32k_clk_sel", .ops = &clk_regmap_mux_ops, .parent_data = gxbb_32k_clk_parents, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gxbb_32k_clk_parents), .flags = CLK_SET_RATE_PARENT, }, }; diff --git a/drivers/clk/microchip/clk-mpfs.c b/drivers/clk/microchip/clk-mpfs.c index ee58304913ef1d..7f044775aea81b 100644 --- a/drivers/clk/microchip/clk-mpfs.c +++ b/drivers/clk/microchip/clk-mpfs.c @@ -285,7 +285,7 @@ static int mpfs_cfg_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned mask = clk_div_mask(cfg->width) << cfg->shift; val = divider_setting << cfg->shift; - regmap_update_bits(cfg->map, cfg->map_offset, val, mask); + regmap_update_bits(cfg->map, cfg->map_offset, mask, val); return 0; } diff --git a/drivers/clk/qcom/gcc-mdm9607.c b/drivers/clk/qcom/gcc-mdm9607.c index 07f1b78d737a15..4b749c99586b73 100644 --- a/drivers/clk/qcom/gcc-mdm9607.c +++ b/drivers/clk/qcom/gcc-mdm9607.c @@ -26,7 +26,6 @@ enum { P_XO, - P_BIMC, P_GPLL0, P_GPLL1, P_GPLL2, @@ -121,7 +120,7 @@ static struct clk_alpha_pll gpll2_early = { .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr = { .enable_reg = 0x45000, - .enable_mask = BIT(3), /* Yeah, apparently it's not 2 */ + .enable_mask = BIT(3), /* BIT(2) is used for BIMC PLL */ .hw.init = &(struct clk_init_data) { .name = "gpll2_early", @@ -158,20 +157,6 @@ static const struct clk_parent_data gcc_xo_gpll0_gpll2[] = { { .hw = &gpll2.clkr.hw }, }; -static const struct parent_map gcc_xo_gpll0_gpll1_gpll2_map[] = { - { P_XO, 0 }, - { P_GPLL0, 1 }, - { P_GPLL1, 2 }, - { P_GPLL2, 3 }, -}; - -static const struct clk_parent_data gcc_xo_gpll0_gpll1_gpll2[] = { - { .fw_name = "xo" }, - { .hw = &gpll0.clkr.hw }, - { .hw = &gpll1_vote.hw }, - { .hw = &gpll2.clkr.hw }, -}; - static const struct freq_tbl ftbl_apss_ahb_clk[] = { F(19200000, P_XO, 1, 0, 0), F(50000000, P_GPLL0, 16, 0, 0), @@ -192,80 +177,6 @@ static struct clk_rcg2 apss_ahb_clk_src = { }, }; -static struct clk_pll bimc_pll = { - .l_reg = 0x23004, - .m_reg = 0x23008, - .n_reg = 0x2300c, - .config_reg = 0x23010, - .mode_reg = 0x23000, - .status_reg = 0x2301c, - .status_bit = 17, - .clkr.hw.init = &(struct clk_init_data){ - .name = "bimc_pll", - .parent_data = &(const struct clk_parent_data){ - .fw_name = "xo", - }, - .num_parents = 1, - .ops = &clk_pll_ops, - }, -}; - -static struct clk_regmap bimc_pll_vote = { - .enable_reg = 0x45000, - .enable_mask = BIT(3), - .hw.init = &(struct clk_init_data){ - .name = "bimc_pll_vote", - .parent_hws = (const struct clk_hw *[]){ &bimc_pll.clkr.hw }, - .num_parents = 1, - .ops = &clk_pll_vote_ops, - }, -}; - -static const struct parent_map gcc_xo_gpll0_bimc_map[] = { - { P_XO, 0 }, - { P_GPLL0, 1 }, - { P_BIMC, 2 }, -}; - -static const struct clk_parent_data gcc_xo_gpll0_bimc[] = { - { .fw_name = "xo" }, - { .hw = &gpll0.clkr.hw }, - { .hw = &bimc_pll_vote.hw }, -}; - -static const struct freq_tbl ftbl_pcnoc_bfdcd_clk_src[] = { - F(19200000, P_XO, 1, 0, 0), - F(50000000, P_GPLL0, 16, 0, 0), - F(100000000, P_GPLL0, 8, 0, 0), - { } -}; - -static struct clk_rcg2 pcnoc_bfdcd_clk_src = { - .cmd_rcgr = 0x27000, - .freq_tbl = ftbl_pcnoc_bfdcd_clk_src, - .hid_width = 5, - .parent_map = gcc_xo_gpll0_bimc_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "pcnoc_bfdcd_clk_src", - .parent_data = gcc_xo_gpll0_bimc, - .num_parents = ARRAY_SIZE(gcc_xo_gpll0_bimc), - .ops = &clk_rcg2_ops, - .flags = CLK_IS_CRITICAL, - }, -}; - -static struct clk_rcg2 system_noc_bfdcd_clk_src = { - .cmd_rcgr = 0x26004, - .hid_width = 5, - .parent_map = gcc_xo_gpll0_bimc_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "system_noc_bfdcd_clk_src", - .parent_data = gcc_xo_gpll0_bimc, - .num_parents = ARRAY_SIZE(gcc_xo_gpll0_bimc), - .ops = &clk_rcg2_ops, - }, -}; - static const struct freq_tbl ftbl_gcc_blsp1_qup1_6_i2c_apps_clk[] = { F(19200000, P_XO, 1, 0, 0), F(50000000, P_GPLL0, 16, 0, 0), @@ -674,26 +585,6 @@ static struct clk_rcg2 sdcc2_apps_clk_src = { }, }; -static const struct freq_tbl ftbl_gcc_apss_tcu_clk[] = { - F(155000000, P_GPLL2, 6, 0, 0), - F(310000000, P_GPLL2, 3, 0, 0), - F(400000000, P_GPLL0, 2, 0, 0), - { } -}; - -static struct clk_rcg2 apss_tcu_clk_src = { - .cmd_rcgr = 0x1207c, - .hid_width = 5, - .parent_map = gcc_xo_gpll0_gpll1_gpll2_map, - .freq_tbl = ftbl_gcc_apss_tcu_clk, - .clkr.hw.init = &(struct clk_init_data){ - .name = "apss_tcu_clk_src", - .parent_data = gcc_xo_gpll0_gpll1_gpll2, - .num_parents = 4, - .ops = &clk_rcg2_ops, - }, -}; - static const struct freq_tbl ftbl_gcc_usb_hs_system_clk[] = { F(19200000, P_XO, 1, 0, 0), F(57140000, P_GPLL0, 14, 0, 0), @@ -781,8 +672,6 @@ static struct clk_branch gcc_blsp1_ahb_clk = { .enable_mask = BIT(10), .hw.init = &(struct clk_init_data){ .name = "gcc_blsp1_ahb_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, .ops = &clk_branch2_ops, }, }, @@ -790,9 +679,10 @@ static struct clk_branch gcc_blsp1_ahb_clk = { static struct clk_branch gcc_blsp1_sleep_clk = { .halt_reg = 0x1004, + .halt_check = BRANCH_HALT_VOTED, .clkr = { - .enable_reg = 0x1004, - .enable_mask = BIT(0), + .enable_reg = 0x45004, + .enable_mask = BIT(9), .hw.init = &(struct clk_init_data){ .name = "gcc_blsp1_sleep_clk", .parent_data = &(const struct clk_parent_data){ @@ -1083,8 +973,6 @@ static struct clk_branch gcc_boot_rom_ahb_clk = { .enable_mask = BIT(7), .hw.init = &(struct clk_init_data){ .name = "gcc_boot_rom_ahb_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, .ops = &clk_branch2_ops, }, }, @@ -1098,9 +986,6 @@ static struct clk_branch gcc_crypto_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_crypto_ahb_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1114,9 +999,6 @@ static struct clk_branch gcc_crypto_axi_clk = { .enable_mask = BIT(1), .hw.init = &(struct clk_init_data){ .name = "gcc_crypto_axi_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1190,9 +1072,6 @@ static struct clk_branch gcc_mss_cfg_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_mss_cfg_ahb_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1220,9 +1099,6 @@ static struct clk_branch gcc_pdm_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_pdm_ahb_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1236,9 +1112,6 @@ static struct clk_branch gcc_prng_ahb_clk = { .enable_mask = BIT(8), .hw.init = &(struct clk_init_data){ .name = "gcc_prng_ahb_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1251,9 +1124,6 @@ static struct clk_branch gcc_sdcc1_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_sdcc1_ahb_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1281,9 +1151,6 @@ static struct clk_branch gcc_sdcc2_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_sdcc2_ahb_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1304,19 +1171,6 @@ static struct clk_branch gcc_sdcc2_apps_clk = { }, }; -static struct clk_rcg2 bimc_ddr_clk_src = { - .cmd_rcgr = 0x32004, - .hid_width = 5, - .parent_map = gcc_xo_gpll0_bimc_map, - .clkr.hw.init = &(struct clk_init_data){ - .name = "bimc_ddr_clk_src", - .parent_data = gcc_xo_gpll0_bimc, - .num_parents = 3, - .ops = &clk_rcg2_ops, - .flags = CLK_GET_RATE_NOCACHE, - }, -}; - static struct clk_branch gcc_mss_q6_bimc_axi_clk = { .halt_reg = 0x49004, .clkr = { @@ -1324,9 +1178,6 @@ static struct clk_branch gcc_mss_q6_bimc_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_mss_q6_bimc_axi_clk", - .parent_hws = (const struct clk_hw *[]){ &bimc_ddr_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1340,8 +1191,6 @@ static struct clk_branch gcc_apss_tcu_clk = { .enable_mask = BIT(1), .hw.init = &(struct clk_init_data){ .name = "gcc_apss_tcu_clk", - .parent_hws = (const struct clk_hw *[]){ &bimc_ddr_clk_src.clkr.hw }, - .num_parents = 1, .ops = &clk_branch2_ops, }, }, @@ -1355,9 +1204,6 @@ static struct clk_branch gcc_smmu_cfg_clk = { .enable_mask = BIT(12), .hw.init = &(struct clk_init_data){ .name = "gcc_smmu_cfg_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1405,9 +1251,6 @@ static struct clk_branch gcc_usb_hs_phy_cfg_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_usb_hs_phy_cfg_ahb_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1420,9 +1263,6 @@ static struct clk_branch gcc_usb_hs_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_usb_hs_ahb_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, - .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -1451,23 +1291,19 @@ static struct clk_branch gcc_apss_ahb_clk = { .enable_mask = BIT(14), .hw.init = &(struct clk_init_data){ .name = "gcc_apss_ahb_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, .ops = &clk_branch2_ops, }, }, }; static struct clk_branch gcc_apss_axi_clk = { - .halt_reg = 0x4601c, + .halt_reg = 0x46020, .halt_check = BRANCH_HALT_VOTED, .clkr = { .enable_reg = 0x45004, .enable_mask = BIT(13), .hw.init = &(struct clk_init_data){ .name = "gcc_apss_axi_clk", - .parent_hws = (const struct clk_hw *[]){ &pcnoc_bfdcd_clk_src.clkr.hw }, - .num_parents = 1, .ops = &clk_branch2_ops, }, }, @@ -1480,11 +1316,6 @@ static struct clk_regmap *gcc_mdm9607_clocks[] = { [GPLL1_VOTE] = &gpll1_vote, [GPLL2] = &gpll2.clkr, [GPLL2_EARLY] = &gpll2_early.clkr, - [BIMC_PLL] = &bimc_pll.clkr, - [BIMC_PLL_VOTE] = &bimc_pll_vote, - [BIMC_DDR_CLK_SRC] = &bimc_ddr_clk_src.clkr, - [PCNOC_BFDCD_CLK_SRC] = &pcnoc_bfdcd_clk_src.clkr, - [SYSTEM_NOC_BFDCD_CLK_SRC] = &system_noc_bfdcd_clk_src.clkr, [APSS_AHB_CLK_SRC] = &apss_ahb_clk_src.clkr, [BLSP1_QUP1_I2C_APPS_CLK_SRC] = &blsp1_qup1_i2c_apps_clk_src.clkr, [BLSP1_QUP1_SPI_APPS_CLK_SRC] = &blsp1_qup1_spi_apps_clk_src.clkr, @@ -1511,7 +1342,6 @@ static struct clk_regmap *gcc_mdm9607_clocks[] = { [PDM2_CLK_SRC] = &pdm2_clk_src.clkr, [SDCC1_APPS_CLK_SRC] = &sdcc1_apps_clk_src.clkr, [SDCC2_APPS_CLK_SRC] = &sdcc2_apps_clk_src.clkr, - [APSS_TCU_CLK_SRC] = &apss_tcu_clk_src.clkr, [USB_HS_SYSTEM_CLK_SRC] = &usb_hs_system_clk_src.clkr, [GCC_BLSP1_AHB_CLK] = &gcc_blsp1_ahb_clk.clkr, [GCC_BLSP1_SLEEP_CLK] = &gcc_blsp1_sleep_clk.clkr, @@ -1567,7 +1397,7 @@ static const struct qcom_reset_map gcc_mdm9607_resets[] = { [USB_HS_HSIC_BCR] = { 0x3d05c }, [GCC_MSS_RESTART] = { 0x3e000 }, [USB_HS_BCR] = { 0x41000 }, - [USB2_HS_PHY_ONLY_BCR] = { 0x41034 }, + [USB2_HS_PHY_ONLY_BCR] = { .reg = 0x41034, .udelay = 15 }, [QUSB2_PHY_BCR] = { 0x4103c }, }; diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c index 9c7c6b23ce3271..e91406e6f52734 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -1589,9 +1589,10 @@ static struct clk_branch gcc_blsp1_ahb_clk = { static struct clk_branch gcc_blsp1_sleep_clk = { .halt_reg = 0x01004, + .halt_check = BRANCH_HALT_VOTED, .clkr = { - .enable_reg = 0x01004, - .enable_mask = BIT(0), + .enable_reg = 0x45004, + .enable_mask = BIT(9), .hw.init = &(struct clk_init_data){ .name = "gcc_blsp1_sleep_clk", .parent_data = &(const struct clk_parent_data){ diff --git a/drivers/clk/qcom/gcc-msm8939.c b/drivers/clk/qcom/gcc-msm8939.c index 45193b3d714bab..ffd7f14fcbaf8b 100644 --- a/drivers/clk/qcom/gcc-msm8939.c +++ b/drivers/clk/qcom/gcc-msm8939.c @@ -1929,9 +1929,10 @@ static struct clk_branch gcc_blsp1_ahb_clk = { static struct clk_branch gcc_blsp1_sleep_clk = { .halt_reg = 0x01004, + .halt_check = BRANCH_HALT_VOTED, .clkr = { - .enable_reg = 0x01004, - .enable_mask = BIT(0), + .enable_reg = 0x45004, + .enable_mask = BIT(9), .hw.init = &(struct clk_init_data){ .name = "gcc_blsp1_sleep_clk", .ops = &clk_branch2_ops, diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c index 86953f9ffee388..86a6870cc2ee35 100644 --- a/drivers/clk/rockchip/clk-rk3588.c +++ b/drivers/clk/rockchip/clk-rk3588.c @@ -2102,7 +2102,7 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { COMPOSITE(DCLK_VOP1_SRC, "dclk_vop1_src", gpll_cpll_v0pll_aupll_p, 0, RK3588_CLKSEL_CON(111), 14, 2, MFLAGS, 9, 5, DFLAGS, RK3588_CLKGATE_CON(52), 11, GFLAGS), - COMPOSITE(DCLK_VOP2_SRC, "dclk_vop2_src", gpll_cpll_v0pll_aupll_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + COMPOSITE(DCLK_VOP2_SRC, "dclk_vop2_src", gpll_cpll_v0pll_aupll_p, CLK_SET_RATE_NO_REPARENT, RK3588_CLKSEL_CON(112), 5, 2, MFLAGS, 0, 5, DFLAGS, RK3588_CLKGATE_CON(52), 12, GFLAGS), COMPOSITE_NODIV(DCLK_VOP0, "dclk_vop0", dclk_vop0_p, diff --git a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c index 4a0136349f71af..dbef983eb42520 100644 --- a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c +++ b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c @@ -663,8 +663,8 @@ static int clk_wzrd_determine_rate_all(struct clk_hw *hw, d = divider->d; o = divider->o; - req->rate = div_u64(req->best_parent_rate * (m * 1000 + divider->m_frac), - d * (o * 1000 + divider->o_frac)); + req->rate = mult_frac(req->best_parent_rate, m * 1000 + divider->m_frac, + d * (o * 1000 + divider->o_frac)); return 0; } diff --git a/drivers/clocksource/timer-nxp-pit.c b/drivers/clocksource/timer-nxp-pit.c index bc5157e2ba57c6..2f70d1d5e21b22 100644 --- a/drivers/clocksource/timer-nxp-pit.c +++ b/drivers/clocksource/timer-nxp-pit.c @@ -328,8 +328,10 @@ static int pit_timer_init(struct device_node *np) if (pit_instances == max_pit_instances) { ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "PIT timer:starting", pit_clockevent_starting_cpu, NULL); - if (ret < 0) + if (ret < 0) { + pit_clockevent_per_cpu_exit(pit, pit_instances); goto out_pit_clocksource_unregister; + } } return 0; diff --git a/drivers/clocksource/timer-sun4i.c b/drivers/clocksource/timer-sun4i.c index 7bdcc60ad43cb5..c2d04ab7cf2d38 100644 --- a/drivers/clocksource/timer-sun4i.c +++ b/drivers/clocksource/timer-sun4i.c @@ -208,7 +208,7 @@ static int __init sun4i_timer_init(struct device_node *node) sun4i_timer_clear_interrupt(timer_of_base(&to)); clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), - TIMER_SYNC_TICKS, 0xffffffff); + TIMER_SYNC_TICKS + 1, 0xffffffff); /* Enable timer0 interrupt */ val = readl(timer_of_base(&to) + TIMER_IRQ_EN_REG); diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 638e5bf72185a7..3f64f266e695b6 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -249,21 +249,19 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy) return -ENODEV; } - ret = dev_pm_opp_of_add_table(cpu_dev); - if (ret < 0) { - dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret); - return ret; - } + priv = kzalloc_obj(*priv); + if (!priv) + return -ENOMEM; ret = apple_soc_cpufreq_find_cluster(policy, ®_base, &info); if (ret) { dev_err(cpu_dev, "%s: failed to get cluster info: %d\n", __func__, ret); - return ret; + goto out_free_priv; } - ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus); - if (ret) { - dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", __func__, ret); + ret = dev_pm_opp_of_cpumask_add_table(policy->cpus); + if (ret < 0) { + dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret); goto out_iounmap; } @@ -271,19 +269,13 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy) if (ret <= 0) { dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); ret = -EPROBE_DEFER; - goto out_free_opp; - } - - priv = kzalloc_obj(*priv); - if (!priv) { - ret = -ENOMEM; - goto out_free_opp; + goto out_free_table; } ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); if (ret) { dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); - goto out_free_priv; + goto out_free_table; } /* Get OPP levels (p-state indexes) and stash them in driver_data */ @@ -318,12 +310,12 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy) out_free_cpufreq_table: dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); -out_free_priv: - kfree(priv); -out_free_opp: - dev_pm_opp_remove_all_dynamic(cpu_dev); +out_free_table: + dev_pm_opp_of_cpumask_remove_table(policy->cpus); out_iounmap: iounmap(reg_base); +out_free_priv: + kfree(priv); return ret; } @@ -332,7 +324,7 @@ static void apple_soc_cpufreq_exit(struct cpufreq_policy *policy) struct apple_cpu_priv *priv = policy->driver_data; dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); - dev_pm_opp_remove_all_dynamic(priv->cpu_dev); + dev_pm_opp_of_cpumask_remove_table(policy->cpus); iounmap(priv->reg_base); kfree(priv); } diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 6fe0e972952af3..80893844353ca1 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -836,7 +836,7 @@ static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) * value first as some platforms may update the actual delivered perf * there; if failed, resort to the cached desired perf. */ - if (cppc_get_desired_perf(cpu, &delivered_perf)) + if (cppc_get_desired_perf(cpu, &delivered_perf) || !delivered_perf) delivered_perf = cpu_data->perf_ctrls.desired_perf; return cppc_perf_to_khz(&cpu_data->perf_caps, delivered_perf); diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c index dcf20ea5ef5e3a..b250d0dde76072 100644 --- a/drivers/cpuidle/cpuidle-psci.c +++ b/drivers/cpuidle/cpuidle-psci.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -428,14 +428,14 @@ static int psci_idle_init_cpu(struct device *dev, int cpu) * to register cpuidle driver then rollback to cancel all CPUs * registration. */ -static int psci_cpuidle_probe(struct faux_device *fdev) +static int psci_cpuidle_probe(struct platform_device *pdev) { int cpu, ret; struct cpuidle_driver *drv; struct cpuidle_device *dev; for_each_present_cpu(cpu) { - ret = psci_idle_init_cpu(&fdev->dev, cpu); + ret = psci_idle_init_cpu(&pdev->dev, cpu); if (ret) goto out_fail; } @@ -455,36 +455,26 @@ static int psci_cpuidle_probe(struct faux_device *fdev) return ret; } -static struct faux_device_ops psci_cpuidle_ops = { +static struct platform_driver psci_cpuidle_driver = { .probe = psci_cpuidle_probe, + .driver = { + .name = "psci-cpuidle", + }, }; -static bool __init dt_idle_state_present(void) -{ - struct device_node *cpu_node __free(device_node) = - of_cpu_device_node_get(cpumask_first(cpu_possible_mask)); - if (!cpu_node) - return false; - - struct device_node *state_node __free(device_node) = - of_get_cpu_state_node(cpu_node, 0); - if (!state_node) - return false; - - return !!of_match_node(psci_idle_state_match, state_node); -} - static int __init psci_idle_init(void) { - struct faux_device *fdev; + struct platform_device *pdev; + int ret; - if (!dt_idle_state_present()) - return 0; + ret = platform_driver_register(&psci_cpuidle_driver); + if (ret) + return ret; - fdev = faux_device_create("psci-cpuidle", NULL, &psci_cpuidle_ops); - if (!fdev) { - pr_err("Failed to create psci-cpuidle device\n"); - return -ENODEV; + pdev = platform_device_register_simple("psci-cpuidle", -1, NULL, 0); + if (IS_ERR(pdev)) { + platform_driver_unregister(&psci_cpuidle_driver); + return PTR_ERR(pdev); } return 0; diff --git a/drivers/cpuidle/dt_idle_genpd.c b/drivers/cpuidle/dt_idle_genpd.c index d292975cc46816..ed41a90eeeb714 100644 --- a/drivers/cpuidle/dt_idle_genpd.c +++ b/drivers/cpuidle/dt_idle_genpd.c @@ -99,7 +99,7 @@ struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np, if (!pd) goto out; - pd->name = kasprintf(GFP_KERNEL, "%pOF", np); + pd->name = kstrdup(kbasename(of_node_full_name(np)), GFP_KERNEL); if (!pd->name) goto free_pd; @@ -112,7 +112,6 @@ struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np, goto free_name; pd->free_states = pd_free_states; - pd->name = kbasename(pd->name); pd->states = states; pd->state_count = state_count; diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 216a00bad5d706..eb834d15d61493 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -641,7 +641,6 @@ config CRYPTO_DEV_QCOM_RNG tristate "Qualcomm Random Number Generator Driver" depends on ARCH_QCOM || COMPILE_TEST depends on HW_RANDOM - select CRYPTO_RNG help This driver provides support for the Random Number Generator hardware found on Qualcomm SoCs. diff --git a/drivers/crypto/allwinner/Kconfig b/drivers/crypto/allwinner/Kconfig index 06ea0e9fe6f22f..d86ae005fbe2ed 100644 --- a/drivers/crypto/allwinner/Kconfig +++ b/drivers/crypto/allwinner/Kconfig @@ -70,14 +70,6 @@ config CRYPTO_DEV_SUN8I_CE_HASH help Say y to enable support for hash algorithms. -config CRYPTO_DEV_SUN8I_CE_PRNG - bool "Support for Allwinner Crypto Engine PRNG" - depends on CRYPTO_DEV_SUN8I_CE - select CRYPTO_RNG - help - Select this option if you want to provide kernel-side support for - the Pseudo-Random Number Generator found in the Crypto Engine. - config CRYPTO_DEV_SUN8I_CE_TRNG bool "Support for Allwinner Crypto Engine TRNG" depends on CRYPTO_DEV_SUN8I_CE @@ -113,14 +105,6 @@ config CRYPTO_DEV_SUN8I_SS_DEBUG This will create /sys/kernel/debug/sun8i-ss/stats for displaying the number of requests per flow and per algorithm. -config CRYPTO_DEV_SUN8I_SS_PRNG - bool "Support for Allwinner Security System PRNG" - depends on CRYPTO_DEV_SUN8I_SS - select CRYPTO_RNG - help - Select this option if you want to provide kernel-side support for - the Pseudo-Random Number Generator found in the Security System. - config CRYPTO_DEV_SUN8I_SS_HASH bool "Enable support for hash on sun8i-ss" depends on CRYPTO_DEV_SUN8I_SS diff --git a/drivers/crypto/allwinner/sun8i-ce/Makefile b/drivers/crypto/allwinner/sun8i-ce/Makefile index 0842eb2d9408d1..ea708b427e2e4e 100644 --- a/drivers/crypto/allwinner/sun8i-ce/Makefile +++ b/drivers/crypto/allwinner/sun8i-ce/Makefile @@ -1,5 +1,4 @@ obj-$(CONFIG_CRYPTO_DEV_SUN8I_CE) += sun8i-ce.o sun8i-ce-y += sun8i-ce-core.o sun8i-ce-cipher.o sun8i-ce-$(CONFIG_CRYPTO_DEV_SUN8I_CE_HASH) += sun8i-ce-hash.o -sun8i-ce-$(CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG) += sun8i-ce-prng.o sun8i-ce-$(CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG) += sun8i-ce-trng.o diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c index f3b58ed6aed0dc..c6402e87f8a078 100644 --- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c +++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -49,7 +48,6 @@ static const struct ce_variant ce_h3_variant = { { "mod", 50000000, 0 }, }, .esr = ESR_H3, - .prng = CE_ALG_PRNG, .trng = CE_ID_NOTSUPP, }; @@ -66,7 +64,6 @@ static const struct ce_variant ce_h5_variant = { { "mod", 300000000, 0 }, }, .esr = ESR_H5, - .prng = CE_ALG_PRNG, .trng = CE_ID_NOTSUPP, }; @@ -80,7 +77,6 @@ static const struct ce_variant ce_h6_variant = { }, .cipher_t_dlen_in_bytes = true, .hash_t_dlen_in_bits = true, - .prng_t_dlen_in_bytes = true, .trng_t_dlen_in_bytes = true, .ce_clks = { { "bus", 0, 200000000 }, @@ -88,7 +84,6 @@ static const struct ce_variant ce_h6_variant = { { "ram", 0, 400000000 }, }, .esr = ESR_H6, - .prng = CE_ALG_PRNG_V2, .trng = CE_ALG_TRNG_V2, }; @@ -102,7 +97,6 @@ static const struct ce_variant ce_h616_variant = { }, .cipher_t_dlen_in_bytes = true, .hash_t_dlen_in_bits = true, - .prng_t_dlen_in_bytes = true, .trng_t_dlen_in_bytes = true, .needs_word_addresses = true, .ce_clks = { @@ -112,7 +106,6 @@ static const struct ce_variant ce_h616_variant = { { "trng", 0, 0 }, }, .esr = ESR_H6, - .prng = CE_ALG_PRNG_V2, .trng = CE_ALG_TRNG_V2, }; @@ -129,7 +122,6 @@ static const struct ce_variant ce_a64_variant = { { "mod", 300000000, 0 }, }, .esr = ESR_A64, - .prng = CE_ALG_PRNG, .trng = CE_ID_NOTSUPP, }; @@ -148,7 +140,6 @@ static const struct ce_variant ce_d1_variant = { { "trng", 0, 0 }, }, .esr = ESR_D1, - .prng = CE_ALG_PRNG, .trng = CE_ALG_TRNG, }; @@ -165,7 +156,6 @@ static const struct ce_variant ce_r40_variant = { { "mod", 300000000, 0 }, }, .esr = ESR_R40, - .prng = CE_ALG_PRNG, .trng = CE_ID_NOTSUPP, }; @@ -614,25 +604,6 @@ static struct sun8i_ce_alg_template ce_algs[] = { }, }, #endif -#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG -{ - .type = CRYPTO_ALG_TYPE_RNG, - .alg.rng = { - .base = { - .cra_name = "stdrng", - .cra_driver_name = "sun8i-ce-prng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct sun8i_ce_rng_tfm_ctx), - .cra_module = THIS_MODULE, - .cra_init = sun8i_ce_prng_init, - .cra_exit = sun8i_ce_prng_exit, - }, - .generate = sun8i_ce_prng_generate, - .seed = sun8i_ce_prng_seed, - .seedsize = PRNG_SEED_SIZE, - } -}, -#endif }; static int sun8i_ce_debugfs_show(struct seq_file *seq, void *v) @@ -693,14 +664,6 @@ static int sun8i_ce_debugfs_show(struct seq_file *seq, void *v) seq_printf(seq, "\tFallback due to SG numbers: %lu\n", ce_algs[i].stat_fb_maxsg); break; -#endif -#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG - case CRYPTO_ALG_TYPE_RNG: - seq_printf(seq, "%s %s reqs=%lu bytes=%lu\n", - ce_algs[i].alg.rng.base.cra_driver_name, - ce_algs[i].alg.rng.base.cra_name, - ce_algs[i].stat_req, ce_algs[i].stat_bytes); - break; #endif } } @@ -930,25 +893,6 @@ static int sun8i_ce_register_algs(struct sun8i_ce_dev *ce) return err; } break; -#endif -#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG - case CRYPTO_ALG_TYPE_RNG: - if (ce->variant->prng == CE_ID_NOTSUPP) { - dev_info(ce->dev, - "DEBUG: Algo of %s not supported\n", - ce_algs[i].alg.rng.base.cra_name); - ce_algs[i].ce = NULL; - break; - } - dev_info(ce->dev, "Register %s\n", - ce_algs[i].alg.rng.base.cra_name); - err = crypto_register_rng(&ce_algs[i].alg.rng); - if (err) { - dev_err(ce->dev, "Fail to register %s\n", - ce_algs[i].alg.rng.base.cra_name); - ce_algs[i].ce = NULL; - } - break; #endif default: ce_algs[i].ce = NULL; @@ -977,13 +921,6 @@ static void sun8i_ce_unregister_algs(struct sun8i_ce_dev *ce) ce_algs[i].alg.hash.base.halg.base.cra_name); crypto_engine_unregister_ahash(&ce_algs[i].alg.hash); break; -#endif -#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG - case CRYPTO_ALG_TYPE_RNG: - dev_info(ce->dev, "Unregister %d %s\n", i, - ce_algs[i].alg.rng.base.cra_name); - crypto_unregister_rng(&ce_algs[i].alg.rng); - break; #endif } } diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c deleted file mode 100644 index d0a1ac66738bfb..00000000000000 --- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * sun8i-ce-prng.c - hardware cryptographic offloader for - * Allwinner H3/A64/H5/H2+/H6/R40 SoC - * - * Copyright (C) 2015-2020 Corentin Labbe - * - * This file handle the PRNG - * - * You could find a link for the datasheet in Documentation/arch/arm/sunxi.rst - */ -#include "sun8i-ce.h" -#include -#include -#include - -int sun8i_ce_prng_init(struct crypto_tfm *tfm) -{ - struct sun8i_ce_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm); - - memset(ctx, 0, sizeof(struct sun8i_ce_rng_tfm_ctx)); - return 0; -} - -void sun8i_ce_prng_exit(struct crypto_tfm *tfm) -{ - struct sun8i_ce_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm); - - kfree_sensitive(ctx->seed); - ctx->seed = NULL; - ctx->slen = 0; -} - -int sun8i_ce_prng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - struct sun8i_ce_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm); - - if (ctx->seed && ctx->slen != slen) { - kfree_sensitive(ctx->seed); - ctx->slen = 0; - ctx->seed = NULL; - } - if (!ctx->seed) - ctx->seed = kmalloc(slen, GFP_KERNEL | GFP_DMA); - if (!ctx->seed) - return -ENOMEM; - - memcpy(ctx->seed, seed, slen); - ctx->slen = slen; - - return 0; -} - -int sun8i_ce_prng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int dlen) -{ - struct sun8i_ce_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm); - struct rng_alg *alg = crypto_rng_alg(tfm); - struct sun8i_ce_alg_template *algt; - struct sun8i_ce_dev *ce; - dma_addr_t dma_iv, dma_dst; - int err = 0; - int flow = 3; - unsigned int todo; - struct sun8i_ce_flow *chan; - struct ce_task *cet; - u32 common, sym; - void *d; - - algt = container_of(alg, struct sun8i_ce_alg_template, alg.rng); - ce = algt->ce; - - if (ctx->slen == 0) { - dev_err(ce->dev, "not seeded\n"); - return -EINVAL; - } - - /* we want dlen + seedsize rounded up to a multiple of PRNG_DATA_SIZE */ - todo = dlen + ctx->slen + PRNG_DATA_SIZE * 2; - todo -= todo % PRNG_DATA_SIZE; - - d = kzalloc(todo, GFP_KERNEL | GFP_DMA); - if (!d) { - err = -ENOMEM; - goto err_mem; - } - - dev_dbg(ce->dev, "%s PRNG slen=%u dlen=%u todo=%u multi=%u\n", __func__, - slen, dlen, todo, todo / PRNG_DATA_SIZE); - -#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG - algt->stat_req++; - algt->stat_bytes += todo; -#endif - - dma_iv = dma_map_single(ce->dev, ctx->seed, ctx->slen, DMA_TO_DEVICE); - if (dma_mapping_error(ce->dev, dma_iv)) { - dev_err(ce->dev, "Cannot DMA MAP IV\n"); - err = -EFAULT; - goto err_iv; - } - - dma_dst = dma_map_single(ce->dev, d, todo, DMA_FROM_DEVICE); - if (dma_mapping_error(ce->dev, dma_dst)) { - dev_err(ce->dev, "Cannot DMA MAP DST\n"); - err = -EFAULT; - goto err_dst; - } - - err = pm_runtime_resume_and_get(ce->dev); - if (err < 0) - goto err_pm; - - mutex_lock(&ce->rnglock); - chan = &ce->chanlist[flow]; - - cet = &chan->tl[0]; - memset(cet, 0, sizeof(struct ce_task)); - - cet->t_id = cpu_to_le32(flow); - common = ce->variant->prng | CE_COMM_INT; - cet->t_common_ctl = cpu_to_le32(common); - - /* recent CE (H6) need length in bytes, in word otherwise */ - if (ce->variant->prng_t_dlen_in_bytes) - cet->t_dlen = cpu_to_le32(todo); - else - cet->t_dlen = cpu_to_le32(todo / 4); - - sym = PRNG_LD; - cet->t_sym_ctl = cpu_to_le32(sym); - cet->t_asym_ctl = 0; - - cet->t_key = desc_addr_val_le32(ce, dma_iv); - cet->t_iv = desc_addr_val_le32(ce, dma_iv); - - cet->t_dst[0].addr = desc_addr_val_le32(ce, dma_dst); - cet->t_dst[0].len = cpu_to_le32(todo / 4); - - err = sun8i_ce_run_task(ce, 3, "PRNG"); - mutex_unlock(&ce->rnglock); - - pm_runtime_put(ce->dev); - -err_pm: - dma_unmap_single(ce->dev, dma_dst, todo, DMA_FROM_DEVICE); -err_dst: - dma_unmap_single(ce->dev, dma_iv, ctx->slen, DMA_TO_DEVICE); - - if (!err) { - memcpy(dst, d, dlen); - memcpy(ctx->seed, d + dlen, ctx->slen); - } -err_iv: - kfree_sensitive(d); -err_mem: - return err; -} diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h index 71f5a0cd3d4582..468d99bf5bf660 100644 --- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h +++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -58,9 +57,7 @@ #define CE_ALG_SHA384 20 #define CE_ALG_SHA512 21 #define CE_ALG_TRNG 48 -#define CE_ALG_PRNG 49 #define CE_ALG_TRNG_V2 0x1c -#define CE_ALG_PRNG_V2 0x1d /* Used in ce_variant */ #define CE_ID_NOTSUPP 0xFF @@ -96,10 +93,6 @@ #define ESR_H6 4 #define ESR_D1 5 -#define PRNG_DATA_SIZE (160 / 8) -#define PRNG_SEED_SIZE DIV_ROUND_UP(175, 8) -#define PRNG_LD BIT(17) - #define CE_DIE_ID_SHIFT 16 #define CE_DIE_ID_MASK 0x07 @@ -136,13 +129,10 @@ struct ce_clock { * bytes or words * @hash_t_dlen_in_bytes: Does the request size for hash is in * bits or words - * @prng_t_dlen_in_bytes: Does the request size for PRNG is in - * bytes or words * @trng_t_dlen_in_bytes: Does the request size for TRNG is in * bytes or words * @ce_clks: list of clocks needed by this variant * @esr: The type of error register - * @prng: The CE_ALG_XXX value for the PRNG * @trng: The CE_ALG_XXX value for the TRNG */ struct ce_variant { @@ -151,12 +141,10 @@ struct ce_variant { u32 op_mode[CE_ID_OP_MAX]; bool cipher_t_dlen_in_bytes; bool hash_t_dlen_in_bits; - bool prng_t_dlen_in_bytes; bool trng_t_dlen_in_bytes; bool needs_word_addresses; struct ce_clock ce_clks[CE_MAX_CLOCKS]; int esr; - unsigned char prng; unsigned char trng; }; @@ -327,16 +315,6 @@ struct sun8i_ce_hash_reqctx { struct ahash_request fallback_req; // keep at the end }; -/* - * struct sun8i_ce_prng_ctx - context for PRNG TFM - * @seed: The seed to use - * @slen: The size of the seed - */ -struct sun8i_ce_rng_tfm_ctx { - void *seed; - unsigned int slen; -}; - /* * struct sun8i_ce_alg_template - crypto_alg template * @type: the CRYPTO_ALG_TYPE for this template @@ -357,7 +335,6 @@ struct sun8i_ce_alg_template { union { struct skcipher_engine_alg skcipher; struct ahash_engine_alg hash; - struct rng_alg rng; } alg; unsigned long stat_req; unsigned long stat_fb; @@ -398,11 +375,5 @@ int sun8i_ce_hash_finup(struct ahash_request *areq); int sun8i_ce_hash_digest(struct ahash_request *areq); int sun8i_ce_hash_run(struct crypto_engine *engine, void *breq); -int sun8i_ce_prng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int dlen); -int sun8i_ce_prng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen); -void sun8i_ce_prng_exit(struct crypto_tfm *tfm); -int sun8i_ce_prng_init(struct crypto_tfm *tfm); - int sun8i_ce_hwrng_register(struct sun8i_ce_dev *ce); void sun8i_ce_hwrng_unregister(struct sun8i_ce_dev *ce); diff --git a/drivers/crypto/allwinner/sun8i-ss/Makefile b/drivers/crypto/allwinner/sun8i-ss/Makefile index aabfd893c81745..2d6458a42e5832 100644 --- a/drivers/crypto/allwinner/sun8i-ss/Makefile +++ b/drivers/crypto/allwinner/sun8i-ss/Makefile @@ -1,4 +1,3 @@ obj-$(CONFIG_CRYPTO_DEV_SUN8I_SS) += sun8i-ss.o sun8i-ss-y += sun8i-ss-core.o sun8i-ss-cipher.o -sun8i-ss-$(CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG) += sun8i-ss-prng.o sun8i-ss-$(CONFIG_CRYPTO_DEV_SUN8I_SS_HASH) += sun8i-ss-hash.o diff --git a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c index 59c9bc45ec0f21..0b22fcddb882c0 100644 --- a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c +++ b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c @@ -11,7 +11,6 @@ */ #include -#include #include #include #include @@ -283,25 +282,6 @@ static struct sun8i_ss_alg_template ss_algs[] = { .do_one_request = sun8i_ss_handle_cipher_request, }, }, -#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG -{ - .type = CRYPTO_ALG_TYPE_RNG, - .alg.rng = { - .base = { - .cra_name = "stdrng", - .cra_driver_name = "sun8i-ss-prng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct sun8i_ss_rng_tfm_ctx), - .cra_module = THIS_MODULE, - .cra_init = sun8i_ss_prng_init, - .cra_exit = sun8i_ss_prng_exit, - }, - .generate = sun8i_ss_prng_generate, - .seed = sun8i_ss_prng_seed, - .seedsize = PRNG_SEED_SIZE, - } -}, -#endif #ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_HASH { .type = CRYPTO_ALG_TYPE_AHASH, .ss_algo_id = SS_ID_HASH_MD5, @@ -501,14 +481,6 @@ static int sun8i_ss_debugfs_show(struct seq_file *seq, void *v) seq_printf(seq, "\tFallback due to SG numbers: %lu\n", ss_algs[i].stat_fb_sgnum); break; -#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG - case CRYPTO_ALG_TYPE_RNG: - seq_printf(seq, "%s %s reqs=%lu tsize=%lu\n", - ss_algs[i].alg.rng.base.cra_driver_name, - ss_algs[i].alg.rng.base.cra_name, - ss_algs[i].stat_req, ss_algs[i].stat_bytes); - break; -#endif #ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_HASH case CRYPTO_ALG_TYPE_AHASH: seq_printf(seq, "%s %s reqs=%lu fallback=%lu\n", @@ -711,16 +683,6 @@ static int sun8i_ss_register_algs(struct sun8i_ss_dev *ss) return err; } break; -#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG - case CRYPTO_ALG_TYPE_RNG: - err = crypto_register_rng(&ss_algs[i].alg.rng); - if (err) { - dev_err(ss->dev, "Fail to register %s\n", - ss_algs[i].alg.rng.base.cra_name); - ss_algs[i].ss = NULL; - } - break; -#endif #ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_HASH case CRYPTO_ALG_TYPE_AHASH: id = ss_algs[i].ss_algo_id; @@ -764,13 +726,6 @@ static void sun8i_ss_unregister_algs(struct sun8i_ss_dev *ss) ss_algs[i].alg.skcipher.base.base.cra_name); crypto_engine_unregister_skcipher(&ss_algs[i].alg.skcipher); break; -#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG - case CRYPTO_ALG_TYPE_RNG: - dev_info(ss->dev, "Unregister %d %s\n", i, - ss_algs[i].alg.rng.base.cra_name); - crypto_unregister_rng(&ss_algs[i].alg.rng); - break; -#endif #ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_HASH case CRYPTO_ALG_TYPE_AHASH: dev_info(ss->dev, "Unregister %d %s\n", i, diff --git a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c deleted file mode 100644 index a923cfc6553f42..00000000000000 --- a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * sun8i-ss-prng.c - hardware cryptographic offloader for - * Allwinner A80/A83T SoC - * - * Copyright (C) 2015-2020 Corentin Labbe - * - * This file handle the PRNG found in the SS - * - * You could find a link for the datasheet in Documentation/arch/arm/sunxi.rst - */ -#include "sun8i-ss.h" -#include -#include -#include -#include -#include - -int sun8i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - struct sun8i_ss_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm); - - if (ctx->seed && ctx->slen != slen) { - kfree_sensitive(ctx->seed); - ctx->slen = 0; - ctx->seed = NULL; - } - if (!ctx->seed) - ctx->seed = kmalloc(slen, GFP_KERNEL); - if (!ctx->seed) - return -ENOMEM; - - memcpy(ctx->seed, seed, slen); - ctx->slen = slen; - - return 0; -} - -int sun8i_ss_prng_init(struct crypto_tfm *tfm) -{ - struct sun8i_ss_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm); - - memset(ctx, 0, sizeof(struct sun8i_ss_rng_tfm_ctx)); - return 0; -} - -void sun8i_ss_prng_exit(struct crypto_tfm *tfm) -{ - struct sun8i_ss_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm); - - kfree_sensitive(ctx->seed); - ctx->seed = NULL; - ctx->slen = 0; -} - -int sun8i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int dlen) -{ - struct sun8i_ss_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm); - struct rng_alg *alg = crypto_rng_alg(tfm); - struct sun8i_ss_alg_template *algt; - unsigned int todo_with_padding; - struct sun8i_ss_dev *ss; - dma_addr_t dma_iv, dma_dst; - unsigned int todo; - int err = 0; - int flow; - void *d; - u32 v; - - algt = container_of(alg, struct sun8i_ss_alg_template, alg.rng); - ss = algt->ss; - - if (ctx->slen == 0) { - dev_err(ss->dev, "The PRNG is not seeded\n"); - return -EINVAL; - } - - /* The SS does not give an updated seed, so we need to get a new one. - * So we will ask for an extra PRNG_SEED_SIZE data. - * We want dlen + seedsize rounded up to a multiple of PRNG_DATA_SIZE - */ - todo = dlen + PRNG_SEED_SIZE + PRNG_DATA_SIZE; - todo -= todo % PRNG_DATA_SIZE; - - todo_with_padding = ALIGN(todo, dma_get_cache_alignment()); - if (todo_with_padding < todo || todo < dlen) - return -EOVERFLOW; - - d = kzalloc(todo_with_padding, GFP_KERNEL); - if (!d) - return -ENOMEM; - - flow = sun8i_ss_get_engine_number(ss); - -#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_DEBUG - algt->stat_req++; - algt->stat_bytes += todo; -#endif - - v = SS_ALG_PRNG | SS_PRNG_CONTINUE | SS_START; - if (flow) - v |= SS_FLOW1; - else - v |= SS_FLOW0; - - dma_iv = dma_map_single(ss->dev, ctx->seed, ctx->slen, DMA_TO_DEVICE); - if (dma_mapping_error(ss->dev, dma_iv)) { - dev_err(ss->dev, "Cannot DMA MAP IV\n"); - err = -EFAULT; - goto err_free; - } - - dma_dst = dma_map_single(ss->dev, d, todo, DMA_FROM_DEVICE); - if (dma_mapping_error(ss->dev, dma_dst)) { - dev_err(ss->dev, "Cannot DMA MAP DST\n"); - err = -EFAULT; - goto err_iv; - } - - err = pm_runtime_resume_and_get(ss->dev); - if (err < 0) - goto err_pm; - err = 0; - - mutex_lock(&ss->mlock); - writel(dma_iv, ss->base + SS_IV_ADR_REG); - /* the PRNG act badly (failing rngtest) without SS_KEY_ADR_REG set */ - writel(dma_iv, ss->base + SS_KEY_ADR_REG); - writel(dma_dst, ss->base + SS_DST_ADR_REG); - writel(todo / 4, ss->base + SS_LEN_ADR_REG); - - reinit_completion(&ss->flows[flow].complete); - ss->flows[flow].status = 0; - /* Be sure all data is written before enabling the task */ - wmb(); - - writel(v, ss->base + SS_CTL_REG); - - wait_for_completion_interruptible_timeout(&ss->flows[flow].complete, - msecs_to_jiffies(todo)); - if (ss->flows[flow].status == 0) { - dev_err(ss->dev, "DMA timeout for PRNG (size=%u)\n", todo); - err = -EFAULT; - } - /* Since cipher and hash use the linux/cryptoengine and that we have - * a cryptoengine per flow, we are sure that they will issue only one - * request per flow. - * Since the cryptoengine wait for completion before submitting a new - * one, the mlock could be left just after the final writel. - * But cryptoengine cannot handle crypto_rng, so we need to be sure - * nothing will use our flow. - * The easiest way is to grab mlock until the hardware end our requests. - * We could have used a per flow lock, but this would increase - * complexity. - * The drawback is that no request could be handled for the other flow. - */ - mutex_unlock(&ss->mlock); - - pm_runtime_put(ss->dev); - -err_pm: - dma_unmap_single(ss->dev, dma_dst, todo, DMA_FROM_DEVICE); -err_iv: - dma_unmap_single(ss->dev, dma_iv, ctx->slen, DMA_TO_DEVICE); - - if (!err) { - memcpy(dst, d, dlen); - /* Update seed */ - memcpy(ctx->seed, d + dlen, ctx->slen); - } -err_free: - kfree_sensitive(d); - - return err; -} diff --git a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h index 3fc86225edaf36..289fb22abfa27d 100644 --- a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h +++ b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -27,7 +26,6 @@ #define SS_ALG_DES (1 << 2) #define SS_ALG_3DES (2 << 2) #define SS_ALG_MD5 (3 << 2) -#define SS_ALG_PRNG (4 << 2) #define SS_ALG_SHA1 (6 << 2) #define SS_ALG_SHA224 (7 << 2) #define SS_ALG_SHA256 (8 << 2) @@ -68,8 +66,6 @@ #define SS_FLOW0 BIT(30) #define SS_FLOW1 BIT(31) -#define SS_PRNG_CONTINUE BIT(18) - #define MAX_SG 8 #define MAXFLOW 2 @@ -79,9 +75,6 @@ #define SS_DIE_ID_SHIFT 20 #define SS_DIE_ID_MASK 0x07 -#define PRNG_DATA_SIZE (160 / 8) -#define PRNG_SEED_SIZE DIV_ROUND_UP(175, 8) - #define MAX_PAD_SIZE 4096 /* @@ -213,16 +206,6 @@ struct sun8i_cipher_tfm_ctx { struct crypto_skcipher *fallback_tfm; }; -/* - * struct sun8i_ss_prng_ctx - context for PRNG TFM - * @seed: The seed to use - * @slen: The size of the seed - */ -struct sun8i_ss_rng_tfm_ctx { - void *seed; - unsigned int slen; -}; - /* * struct sun8i_ss_hash_tfm_ctx - context for an ahash TFM * @fallback_tfm: pointer to the fallback TFM @@ -274,7 +257,6 @@ struct sun8i_ss_alg_template { struct sun8i_ss_dev *ss; union { struct skcipher_engine_alg skcipher; - struct rng_alg rng; struct ahash_engine_alg hash; } alg; unsigned long stat_req; @@ -300,11 +282,6 @@ int sun8i_ss_skencrypt(struct skcipher_request *areq); int sun8i_ss_get_engine_number(struct sun8i_ss_dev *ss); int sun8i_ss_run_task(struct sun8i_ss_dev *ss, struct sun8i_cipher_req_ctx *rctx, const char *name); -int sun8i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src, - unsigned int slen, u8 *dst, unsigned int dlen); -int sun8i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen); -int sun8i_ss_prng_init(struct crypto_tfm *tfm); -void sun8i_ss_prng_exit(struct crypto_tfm *tfm); int sun8i_ss_hash_init_tfm(struct crypto_ahash *tfm); void sun8i_ss_hash_exit_tfm(struct crypto_ahash *tfm); diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c index 9da9dd6585dfc6..4dc34c5bc0f6c4 100644 --- a/drivers/crypto/atmel-ecc.c +++ b/drivers/crypto/atmel-ecc.c @@ -27,15 +27,14 @@ static struct atmel_ecc_driver_data driver_data; /** * struct atmel_ecdh_ctx - transformation context - * @client : pointer to i2c client device - * @fallback : used for unsupported curves or when user wants to use its own - * private key. - * @public_key : generated when calling set_secret(). It's the responsibility - * of the user to not call set_secret() while - * generate_public_key() or compute_shared_secret() are in flight. - * @curve_id : elliptic curve id - * @do_fallback: true when the device doesn't support the curve or when the user - * wants to use its own private key. + * @client: I2C client device + * @fallback: ECDH fallback used for caller-provided private keys + * @public_key: cached public key for the device-generated private key + * @curve_id: elliptic curve id + * @do_fallback: true when ECDH operations should use @fallback + * + * The caller must not invoke set_secret() while generate_public_key() + * or compute_shared_secret() are in flight. */ struct atmel_ecdh_ctx { struct i2c_client *client; @@ -55,7 +54,7 @@ static void atmel_ecdh_done(struct atmel_i2c_work_data *work_data, void *areq, if (status) goto free_work_data; - /* might want less than we've got */ + /* copy only as much as requested, capped at 32 bytes */ n_sz = min(ATMEL_ECC_NIST_P256_N_SIZE, req->dst_len); /* copy the shared secret */ @@ -64,15 +63,15 @@ static void atmel_ecdh_done(struct atmel_i2c_work_data *work_data, void *areq, if (copied != n_sz) status = -EINVAL; - /* fall through */ free_work_data: kfree_sensitive(work_data); kpp_request_complete(req, status); } /* - * A random private key is generated and stored in the device. The device - * returns the pair public key. + * If no private key is provided, generate one in the device and cache + * the corresponding public key. The generated private key never leaves + * the device. */ static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf, unsigned int len) @@ -83,10 +82,9 @@ static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf, struct ecdh params; int ret = -ENOMEM; - /* free the old public key, if any */ kfree(ctx->public_key); - /* make sure you don't free the old public key twice */ ctx->public_key = NULL; + ctx->do_fallback = false; if (crypto_ecdh_decode_key(buf, len, ¶ms) < 0) { dev_err(&ctx->client->dev, "crypto_ecdh_decode_key failed\n"); @@ -94,33 +92,25 @@ static int atmel_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf, } if (params.key_size) { - /* fallback to ecdh software implementation */ - ctx->do_fallback = true; - return crypto_kpp_set_secret(ctx->fallback, buf, len); + ret = crypto_kpp_set_secret(ctx->fallback, buf, len); + ctx->do_fallback = !ret; + return ret; } cmd = kmalloc_obj(*cmd); if (!cmd) return -ENOMEM; - /* - * The device only supports NIST P256 ECC keys. The public key size will - * always be the same. Use a macro for the key size to avoid unnecessary - * computations. - */ public_key = kmalloc(ATMEL_ECC_PUBKEY_SIZE, GFP_KERNEL); if (!public_key) goto free_cmd; - ctx->do_fallback = false; - atmel_i2c_init_genkey_cmd(cmd, DATA_SLOT_2); ret = atmel_i2c_send_receive(ctx->client, cmd); if (ret) goto free_public_key; - /* save the public key */ memcpy(public_key, &cmd->data[RSP_DATA_IDX], ATMEL_ECC_PUBKEY_SIZE); ctx->public_key = public_key; @@ -149,7 +139,7 @@ static int atmel_ecdh_generate_public_key(struct kpp_request *req) if (!ctx->public_key) return -EINVAL; - /* might want less than we've got */ + /* copy only as much as requested, capped at 64 bytes */ nbytes = min(ATMEL_ECC_PUBKEY_SIZE, req->dst_len); /* public key was saved at private key generation */ @@ -175,7 +165,7 @@ static int atmel_ecdh_compute_shared_secret(struct kpp_request *req) return crypto_kpp_compute_shared_secret(req); } - /* must have exactly two points to be on the curve */ + /* A P-256 public key must contain two 32-byte coordinates */ if (req->src_len != ATMEL_ECC_PUBKEY_SIZE) return -EINVAL; diff --git a/drivers/crypto/atmel-tdes.c b/drivers/crypto/atmel-tdes.c index 3c0eacacfc87bb..48cd537f001e82 100644 --- a/drivers/crypto/atmel-tdes.c +++ b/drivers/crypto/atmel-tdes.c @@ -462,14 +462,13 @@ static int atmel_tdes_crypt_start(struct atmel_tdes_dev *dd) IS_ALIGNED(dd->out_sg->length, dd->ctx->block_size); fast = in && out; - if (sg_dma_len(dd->in_sg) != sg_dma_len(dd->out_sg)) + if (dd->in_sg->length != dd->out_sg->length) fast = 0; } if (fast) { - count = min_t(size_t, dd->total, sg_dma_len(dd->in_sg)); - count = min_t(size_t, count, sg_dma_len(dd->out_sg)); + count = min_t(size_t, dd->total, dd->in_sg->length); err = dma_map_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE); if (!err) { diff --git a/drivers/crypto/intel/iaa/iaa_crypto_main.c b/drivers/crypto/intel/iaa/iaa_crypto_main.c index f62b994e18e580..cdfe00464fa36c 100644 --- a/drivers/crypto/intel/iaa/iaa_crypto_main.c +++ b/drivers/crypto/intel/iaa/iaa_crypto_main.c @@ -2,6 +2,7 @@ /* Copyright(c) 2021 Intel Corporation. All rights rsvd. */ #include +#include #include #include #include @@ -996,6 +997,19 @@ static int deflate_generic_decompress(struct acomp_req *req) return ret; } +static int deflate_generic_compress(struct acomp_req *req) +{ + ACOMP_FBREQ_ON_STACK(fbreq, req); + int ret; + + ret = crypto_acomp_compress(fbreq); + req->dlen = fbreq->dlen; + + update_total_sw_comp_calls(); + + return ret; +} + static int iaa_remap_for_verify(struct device *dev, struct iaa_wq *iaa_wq, struct acomp_req *req, dma_addr_t *src_addr, dma_addr_t *dst_addr); @@ -1044,13 +1058,17 @@ static void iaa_desc_complete(struct idxd_desc *idxd_desc, pr_warn("%s: falling back to deflate-generic decompress, " "analytics error code %x\n", __func__, idxd_desc->iax_completion->error_code); + dma_unmap_sg(dev, ctx->req->dst, sg_nents(ctx->req->dst), + DMA_FROM_DEVICE); + dma_unmap_sg(dev, ctx->req->src, 1, DMA_TO_DEVICE); + ret = deflate_generic_decompress(ctx->req); if (ret) { dev_dbg(dev, "%s: deflate-generic failed ret=%d\n", __func__, ret); err = -EIO; - goto err; } + goto out; } else { err = -EIO; goto err; @@ -1433,19 +1451,9 @@ static int iaa_decompress(struct crypto_tfm *tfm, struct acomp_req *req, ret = check_completion(dev, idxd_desc->iax_completion, false, false); if (ret) { dev_dbg(dev, "%s: check_completion failed ret=%d\n", __func__, ret); - if (idxd_desc->iax_completion->status == IAA_ANALYTICS_ERROR) { - pr_warn("%s: falling back to deflate-generic decompress, " - "analytics error code %x\n", __func__, - idxd_desc->iax_completion->error_code); - ret = deflate_generic_decompress(req); - if (ret) { - dev_dbg(dev, "%s: deflate-generic failed ret=%d\n", - __func__, ret); - goto err; - } - } else { - goto err; - } + if (idxd_desc->iax_completion->status == IAA_ANALYTICS_ERROR) + ret = -EAGAIN; + goto err; } else { req->dlen = idxd_desc->iax_completion->output_size; } @@ -1472,7 +1480,7 @@ static int iaa_comp_acompress(struct acomp_req *req) struct iaa_compression_ctx *compression_ctx; struct crypto_tfm *tfm = req->base.tfm; dma_addr_t src_addr, dst_addr; - int nr_sgs, cpu, ret = 0; + int cpu, ret = 0; struct iaa_wq *iaa_wq; struct idxd_wq *wq; struct device *dev; @@ -1484,11 +1492,15 @@ static int iaa_comp_acompress(struct acomp_req *req) return -ENODEV; } - if (!req->src || !req->slen) { - pr_debug("invalid src, not compressing\n"); + if (!req->src || !req->slen || !req->dst) { + pr_debug("invalid req, not compressing\n"); return -EINVAL; } + /* Fall back to software if src or dst has multiple sg entries */ + if (sg_nents(req->src) > 1 || sg_nents(req->dst) > 1) + return deflate_generic_compress(req); + cpu = get_cpu(); wq = wq_table_next_wq(cpu); put_cpu(); @@ -1507,30 +1519,25 @@ static int iaa_comp_acompress(struct acomp_req *req) dev = &wq->idxd->pdev->dev; - nr_sgs = dma_map_sg(dev, req->src, sg_nents(req->src), DMA_TO_DEVICE); - if (nr_sgs <= 0 || nr_sgs > 1) { - dev_dbg(dev, "couldn't map src sg for iaa device %d," - " wq %d: ret=%d\n", iaa_wq->iaa_device->idxd->id, - iaa_wq->wq->id, ret); - ret = -EIO; - goto out; + if (!dma_map_sg(dev, req->src, 1, DMA_TO_DEVICE)) { + dev_dbg(dev, "couldn't map src sg for iaa device %d, wq %d\n", + iaa_wq->iaa_device->idxd->id, iaa_wq->wq->id); + iaa_wq_put(wq); + return deflate_generic_compress(req); } src_addr = sg_dma_address(req->src); - dev_dbg(dev, "dma_map_sg, src_addr %llx, nr_sgs %d, req->src %p," - " req->slen %d, sg_dma_len(sg) %d\n", src_addr, nr_sgs, + dev_dbg(dev, "map src %llx req->src %p slen %d sg_len %d\n", src_addr, req->src, req->slen, sg_dma_len(req->src)); - nr_sgs = dma_map_sg(dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); - if (nr_sgs <= 0 || nr_sgs > 1) { - dev_dbg(dev, "couldn't map dst sg for iaa device %d," - " wq %d: ret=%d\n", iaa_wq->iaa_device->idxd->id, - iaa_wq->wq->id, ret); - ret = -EIO; - goto err_map_dst; + if (!dma_map_sg(dev, req->dst, 1, DMA_FROM_DEVICE)) { + dev_dbg(dev, "couldn't map dst sg for iaa device %d, wq %d\n", + iaa_wq->iaa_device->idxd->id, iaa_wq->wq->id); + dma_unmap_sg(dev, req->src, 1, DMA_TO_DEVICE); + iaa_wq_put(wq); + return deflate_generic_compress(req); } dst_addr = sg_dma_address(req->dst); - dev_dbg(dev, "dma_map_sg, dst_addr %llx, nr_sgs %d, req->dst %p," - " req->dlen %d, sg_dma_len(sg) %d\n", dst_addr, nr_sgs, + dev_dbg(dev, "map dst %llx req->dst %p dlen %d sg_len %d\n", dst_addr, req->dst, req->dlen, sg_dma_len(req->dst)); ret = iaa_compress(tfm, req, wq, src_addr, req->slen, dst_addr, @@ -1550,8 +1557,8 @@ static int iaa_comp_acompress(struct acomp_req *req) if (ret) dev_dbg(dev, "asynchronous compress verification failed ret=%d\n", ret); - dma_unmap_sg(dev, req->dst, sg_nents(req->dst), DMA_TO_DEVICE); - dma_unmap_sg(dev, req->src, sg_nents(req->src), DMA_FROM_DEVICE); + dma_unmap_sg(dev, req->dst, 1, DMA_TO_DEVICE); + dma_unmap_sg(dev, req->src, 1, DMA_FROM_DEVICE); goto out; } @@ -1559,9 +1566,8 @@ static int iaa_comp_acompress(struct acomp_req *req) if (ret) dev_dbg(dev, "asynchronous compress failed ret=%d\n", ret); - dma_unmap_sg(dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); -err_map_dst: - dma_unmap_sg(dev, req->src, sg_nents(req->src), DMA_TO_DEVICE); + dma_unmap_sg(dev, req->dst, 1, DMA_FROM_DEVICE); + dma_unmap_sg(dev, req->src, 1, DMA_TO_DEVICE); out: iaa_wq_put(wq); @@ -1572,7 +1578,7 @@ static int iaa_comp_adecompress(struct acomp_req *req) { struct crypto_tfm *tfm = req->base.tfm; dma_addr_t src_addr, dst_addr; - int nr_sgs, cpu, ret = 0; + int cpu, ret = 0; struct iaa_wq *iaa_wq; struct device *dev; struct idxd_wq *wq; @@ -1582,11 +1588,15 @@ static int iaa_comp_adecompress(struct acomp_req *req) return -ENODEV; } - if (!req->src || !req->slen) { - pr_debug("invalid src, not decompressing\n"); + if (!req->src || !req->slen || !req->dst) { + pr_debug("invalid req, not decompressing\n"); return -EINVAL; } + /* Fall back to software if src or dst has multiple sg entries */ + if (sg_nents(req->src) > 1 || sg_nents(req->dst) > 1) + return deflate_generic_decompress(req); + cpu = get_cpu(); wq = wq_table_next_wq(cpu); put_cpu(); @@ -1605,30 +1615,25 @@ static int iaa_comp_adecompress(struct acomp_req *req) dev = &wq->idxd->pdev->dev; - nr_sgs = dma_map_sg(dev, req->src, sg_nents(req->src), DMA_TO_DEVICE); - if (nr_sgs <= 0 || nr_sgs > 1) { - dev_dbg(dev, "couldn't map src sg for iaa device %d," - " wq %d: ret=%d\n", iaa_wq->iaa_device->idxd->id, - iaa_wq->wq->id, ret); - ret = -EIO; - goto out; + if (!dma_map_sg(dev, req->src, 1, DMA_TO_DEVICE)) { + dev_dbg(dev, "couldn't map src sg for iaa device %d, wq %d\n", + iaa_wq->iaa_device->idxd->id, iaa_wq->wq->id); + iaa_wq_put(wq); + return deflate_generic_decompress(req); } src_addr = sg_dma_address(req->src); - dev_dbg(dev, "dma_map_sg, src_addr %llx, nr_sgs %d, req->src %p," - " req->slen %d, sg_dma_len(sg) %d\n", src_addr, nr_sgs, + dev_dbg(dev, "map src %llx req->src %p slen %d sg_len %d\n", src_addr, req->src, req->slen, sg_dma_len(req->src)); - nr_sgs = dma_map_sg(dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); - if (nr_sgs <= 0 || nr_sgs > 1) { - dev_dbg(dev, "couldn't map dst sg for iaa device %d," - " wq %d: ret=%d\n", iaa_wq->iaa_device->idxd->id, - iaa_wq->wq->id, ret); - ret = -EIO; - goto err_map_dst; + if (!dma_map_sg(dev, req->dst, 1, DMA_FROM_DEVICE)) { + dev_dbg(dev, "couldn't map dst sg for iaa device %d, wq %d\n", + iaa_wq->iaa_device->idxd->id, iaa_wq->wq->id); + dma_unmap_sg(dev, req->src, 1, DMA_TO_DEVICE); + iaa_wq_put(wq); + return deflate_generic_decompress(req); } dst_addr = sg_dma_address(req->dst); - dev_dbg(dev, "dma_map_sg, dst_addr %llx, nr_sgs %d, req->dst %p," - " req->dlen %d, sg_dma_len(sg) %d\n", dst_addr, nr_sgs, + dev_dbg(dev, "map dst %llx req->dst %p dlen %d sg_len %d\n", dst_addr, req->dst, req->dlen, sg_dma_len(req->dst)); ret = iaa_decompress(tfm, req, wq, src_addr, req->slen, @@ -1636,15 +1641,16 @@ static int iaa_comp_adecompress(struct acomp_req *req) if (ret == -EINPROGRESS) return ret; - if (ret != 0) + if (ret != 0 && ret != -EAGAIN) dev_dbg(dev, "asynchronous decompress failed ret=%d\n", ret); - dma_unmap_sg(dev, req->dst, sg_nents(req->dst), DMA_FROM_DEVICE); -err_map_dst: - dma_unmap_sg(dev, req->src, sg_nents(req->src), DMA_TO_DEVICE); -out: + dma_unmap_sg(dev, req->dst, 1, DMA_FROM_DEVICE); + dma_unmap_sg(dev, req->src, 1, DMA_TO_DEVICE); iaa_wq_put(wq); + if (ret == -EAGAIN) + ret = deflate_generic_decompress(req); + return ret; } diff --git a/drivers/crypto/intel/iaa/iaa_crypto_stats.c b/drivers/crypto/intel/iaa/iaa_crypto_stats.c index f5cc3d29ca19e8..2f2ed88c881204 100644 --- a/drivers/crypto/intel/iaa/iaa_crypto_stats.c +++ b/drivers/crypto/intel/iaa/iaa_crypto_stats.c @@ -19,6 +19,7 @@ static atomic64_t total_comp_calls; static atomic64_t total_decomp_calls; +static atomic64_t total_sw_comp_calls; static atomic64_t total_sw_decomp_calls; static atomic64_t total_comp_bytes_out; static atomic64_t total_decomp_bytes_in; @@ -43,6 +44,11 @@ void update_total_decomp_calls(void) atomic64_inc(&total_decomp_calls); } +void update_total_sw_comp_calls(void) +{ + atomic64_inc(&total_sw_comp_calls); +} + void update_total_sw_decomp_calls(void) { atomic64_inc(&total_sw_decomp_calls); @@ -104,6 +110,7 @@ static void reset_iaa_crypto_stats(void) { atomic64_set(&total_comp_calls, 0); atomic64_set(&total_decomp_calls, 0); + atomic64_set(&total_sw_comp_calls, 0); atomic64_set(&total_sw_decomp_calls, 0); atomic64_set(&total_comp_bytes_out, 0); atomic64_set(&total_decomp_bytes_in, 0); @@ -174,6 +181,8 @@ static int global_stats_show(struct seq_file *m, void *v) atomic64_read(&total_comp_calls)); seq_printf(m, " total_decomp_calls: %llu\n", atomic64_read(&total_decomp_calls)); + seq_printf(m, " total_sw_comp_calls: %llu\n", + atomic64_read(&total_sw_comp_calls)); seq_printf(m, " total_sw_decomp_calls: %llu\n", atomic64_read(&total_sw_decomp_calls)); seq_printf(m, " total_comp_bytes_out: %llu\n", diff --git a/drivers/crypto/intel/iaa/iaa_crypto_stats.h b/drivers/crypto/intel/iaa/iaa_crypto_stats.h index 3787a5f507eb2a..6e0c6f9939bfa2 100644 --- a/drivers/crypto/intel/iaa/iaa_crypto_stats.h +++ b/drivers/crypto/intel/iaa/iaa_crypto_stats.h @@ -11,6 +11,7 @@ void iaa_crypto_debugfs_cleanup(void); void update_total_comp_calls(void); void update_total_comp_bytes_out(int n); void update_total_decomp_calls(void); +void update_total_sw_comp_calls(void); void update_total_sw_decomp_calls(void); void update_total_decomp_bytes_in(int n); void update_completion_einval_errs(void); @@ -29,6 +30,7 @@ static inline void iaa_crypto_debugfs_cleanup(void) {} static inline void update_total_comp_calls(void) {} static inline void update_total_comp_bytes_out(int n) {} static inline void update_total_decomp_calls(void) {} +static inline void update_total_sw_comp_calls(void) {} static inline void update_total_sw_decomp_calls(void) {} static inline void update_total_decomp_bytes_in(int n) {} static inline void update_completion_einval_errs(void) {} diff --git a/drivers/crypto/mxs-dcp.c b/drivers/crypto/mxs-dcp.c index 133ebc99823626..595b2fd84667ec 100644 --- a/drivers/crypto/mxs-dcp.c +++ b/drivers/crypto/mxs-dcp.c @@ -353,7 +353,7 @@ static int mxs_dcp_aes_block_crypt(struct crypto_async_request *arq) for_each_sg(req->src, src, sg_nents(req->src), i) { src_buf = sg_virt(src); - len = sg_dma_len(src); + len = src->length; tlen += len; limit_hit = tlen > req->cryptlen; diff --git a/drivers/crypto/qce/aead.c b/drivers/crypto/qce/aead.c index 9cb11fada2c4dd..a9642c67f47614 100644 --- a/drivers/crypto/qce/aead.c +++ b/drivers/crypto/qce/aead.c @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include #include "aead.h" @@ -198,7 +196,7 @@ qce_aead_ccm_prepare_buf_assoclen(struct aead_request *req) /* Get the msg */ msg_sg = scatterwalk_ffwd(__sg, req->src, req->assoclen); - rctx->adata = kzalloc((ALIGN(assoclen, 16) + MAX_CCM_ADATA_HEADER_LEN) * + rctx->adata = kzalloc(ALIGN(assoclen + MAX_CCM_ADATA_HEADER_LEN, 16) * sizeof(unsigned char), GFP_ATOMIC); if (!rctx->adata) return -ENOMEM; @@ -592,7 +590,6 @@ static int qce_aead_setkey(struct crypto_aead *tfm, const u8 *key, unsigned int struct qce_aead_ctx *ctx = crypto_aead_ctx(tfm); struct crypto_authenc_keys authenc_keys; unsigned long flags = to_aead_tmpl(tfm)->alg_flags; - u32 _key[6]; int err; err = crypto_authenc_extractkeys(&authenc_keys, key, keylen); @@ -603,26 +600,7 @@ static int qce_aead_setkey(struct crypto_aead *tfm, const u8 *key, unsigned int authenc_keys.authkeylen > QCE_MAX_KEY_SIZE) return -EINVAL; - if (IS_DES(flags)) { - err = verify_aead_des_key(tfm, authenc_keys.enckey, authenc_keys.enckeylen); - if (err) - return err; - } else if (IS_3DES(flags)) { - err = verify_aead_des3_key(tfm, authenc_keys.enckey, authenc_keys.enckeylen); - if (err) - return err; - /* - * The crypto engine does not support any two keys - * being the same for triple des algorithms. The - * verify_skcipher_des3_key does not check for all the - * below conditions. Schedule fallback in this case. - */ - memcpy(_key, authenc_keys.enckey, DES3_EDE_KEY_SIZE); - if (!((_key[0] ^ _key[2]) | (_key[1] ^ _key[3])) || - !((_key[2] ^ _key[4]) | (_key[3] ^ _key[5])) || - !((_key[0] ^ _key[4]) | (_key[1] ^ _key[5]))) - ctx->need_fallback = true; - } else if (IS_AES(flags)) { + if (IS_AES(flags)) { /* No random key sizes */ if (authenc_keys.enckeylen != AES_KEYSIZE_128 && authenc_keys.enckeylen != AES_KEYSIZE_192 && @@ -693,38 +671,6 @@ struct qce_aead_def { }; static const struct qce_aead_def aead_def[] = { - { - .flags = QCE_ALG_DES | QCE_MODE_CBC | QCE_HASH_SHA1_HMAC, - .name = "authenc(hmac(sha1),cbc(des))", - .drv_name = "authenc-hmac-sha1-cbc-des-qce", - .blocksize = DES_BLOCK_SIZE, - .ivsize = DES_BLOCK_SIZE, - .maxauthsize = SHA1_DIGEST_SIZE, - }, - { - .flags = QCE_ALG_3DES | QCE_MODE_CBC | QCE_HASH_SHA1_HMAC, - .name = "authenc(hmac(sha1),cbc(des3_ede))", - .drv_name = "authenc-hmac-sha1-cbc-3des-qce", - .blocksize = DES3_EDE_BLOCK_SIZE, - .ivsize = DES3_EDE_BLOCK_SIZE, - .maxauthsize = SHA1_DIGEST_SIZE, - }, - { - .flags = QCE_ALG_DES | QCE_MODE_CBC | QCE_HASH_SHA256_HMAC, - .name = "authenc(hmac(sha256),cbc(des))", - .drv_name = "authenc-hmac-sha256-cbc-des-qce", - .blocksize = DES_BLOCK_SIZE, - .ivsize = DES_BLOCK_SIZE, - .maxauthsize = SHA256_DIGEST_SIZE, - }, - { - .flags = QCE_ALG_3DES | QCE_MODE_CBC | QCE_HASH_SHA256_HMAC, - .name = "authenc(hmac(sha256),cbc(des3_ede))", - .drv_name = "authenc-hmac-sha256-cbc-3des-qce", - .blocksize = DES3_EDE_BLOCK_SIZE, - .ivsize = DES3_EDE_BLOCK_SIZE, - .maxauthsize = SHA256_DIGEST_SIZE, - }, { .flags = QCE_ALG_AES | QCE_MODE_CBC | QCE_HASH_SHA256_HMAC, .name = "authenc(hmac(sha256),cbc(aes))", diff --git a/drivers/crypto/qce/common.c b/drivers/crypto/qce/common.c index 54a78a57f63028..3081d765a0f2c5 100644 --- a/drivers/crypto/qce/common.c +++ b/drivers/crypto/qce/common.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include "cipher.h" @@ -115,18 +114,16 @@ static u32 qce_auth_cfg(unsigned long flags, u32 key_size, u32 auth_size) cfg |= AUTH_KEY_SZ_AES256 << AUTH_KEY_SIZE_SHIFT; } - if (IS_SHA1(flags) || IS_SHA1_HMAC(flags)) - cfg |= AUTH_SIZE_SHA1 << AUTH_SIZE_SHIFT; - else if (IS_SHA256(flags) || IS_SHA256_HMAC(flags)) + if (IS_SHA256(flags) || IS_SHA256_HMAC(flags)) cfg |= AUTH_SIZE_SHA256 << AUTH_SIZE_SHIFT; else if (IS_CMAC(flags)) cfg |= AUTH_SIZE_ENUM_16_BYTES << AUTH_SIZE_SHIFT; else if (IS_CCM(flags)) cfg |= (auth_size - 1) << AUTH_SIZE_SHIFT; - if (IS_SHA1(flags) || IS_SHA256(flags)) + if (IS_SHA256(flags)) cfg |= AUTH_MODE_HASH << AUTH_MODE_SHIFT; - else if (IS_SHA1_HMAC(flags) || IS_SHA256_HMAC(flags)) + else if (IS_SHA256_HMAC(flags)) cfg |= AUTH_MODE_HMAC << AUTH_MODE_SHIFT; else if (IS_CCM(flags)) cfg |= AUTH_MODE_CCM << AUTH_MODE_SHIFT; @@ -191,7 +188,7 @@ static int qce_setup_regs_ahash(struct crypto_async_request *async_req) else qce_cpu_to_be32p_array(auth, rctx->digest, digestsize); - iv_words = (IS_SHA1(rctx->flags) || IS_SHA1_HMAC(rctx->flags)) ? 5 : 8; + iv_words = 8; qce_write_array(qce, REG_AUTH_IV0, (u32 *)auth, iv_words); if (rctx->first_blk) @@ -243,19 +240,8 @@ static u32 qce_encr_cfg(unsigned long flags, u32 aes_key_size) if (IS_AES(flags)) cfg |= ENCR_ALG_AES << ENCR_ALG_SHIFT; - else if (IS_DES(flags) || IS_3DES(flags)) - cfg |= ENCR_ALG_DES << ENCR_ALG_SHIFT; - - if (IS_DES(flags)) - cfg |= ENCR_KEY_SZ_DES << ENCR_KEY_SZ_SHIFT; - - if (IS_3DES(flags)) - cfg |= ENCR_KEY_SZ_3DES << ENCR_KEY_SZ_SHIFT; switch (flags & QCE_MODE_MASK) { - case QCE_MODE_ECB: - cfg |= ENCR_MODE_ECB << ENCR_MODE_SHIFT; - break; case QCE_MODE_CBC: cfg |= ENCR_MODE_CBC << ENCR_MODE_SHIFT; break; @@ -340,13 +326,7 @@ static int qce_setup_regs_skcipher(struct crypto_async_request *async_req) encr_cfg = qce_encr_cfg(flags, keylen); - if (IS_DES(flags)) { - enciv_words = 2; - enckey_words = 2; - } else if (IS_3DES(flags)) { - enciv_words = 2; - enckey_words = 6; - } else if (IS_AES(flags)) { + if (IS_AES(flags)) { if (IS_XTS(flags)) qce_xtskey(qce, ctx->enc_key, ctx->enc_keylen, rctx->cryptlen); @@ -357,14 +337,12 @@ static int qce_setup_regs_skcipher(struct crypto_async_request *async_req) qce_write_array(qce, REG_ENCR_KEY0, (u32 *)enckey, enckey_words); - if (!IS_ECB(flags)) { - if (IS_XTS(flags)) - qce_xts_swapiv(enciv, rctx->iv, ivsize); - else - qce_cpu_to_be32p_array(enciv, rctx->iv, ivsize); + if (IS_XTS(flags)) + qce_xts_swapiv(enciv, rctx->iv, ivsize); + else + qce_cpu_to_be32p_array(enciv, rctx->iv, ivsize); - qce_write_array(qce, REG_CNTR0_IV0, (u32 *)enciv, enciv_words); - } + qce_write_array(qce, REG_CNTR0_IV0, (u32 *)enciv, enciv_words); if (IS_ENCRYPT(flags)) encr_cfg |= BIT(ENCODE_SHIFT); @@ -393,10 +371,6 @@ static int qce_setup_regs_skcipher(struct crypto_async_request *async_req) #endif #ifdef CONFIG_CRYPTO_DEV_QCE_AEAD -static const u32 std_iv_sha1[SHA256_DIGEST_SIZE / sizeof(u32)] = { - SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4, 0, 0, 0 -}; - static const u32 std_iv_sha256[SHA256_DIGEST_SIZE / sizeof(u32)] = { SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3, SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7 @@ -473,13 +447,8 @@ static int qce_setup_regs_aead(struct crypto_async_request *async_req) /* Write initial authentication IV only for HMAC algorithms */ if (IS_SHA_HMAC(rctx->flags)) { /* Write default authentication iv */ - if (IS_SHA1_HMAC(rctx->flags)) { - auth_ivsize = SHA1_DIGEST_SIZE; - memcpy(authiv, std_iv_sha1, auth_ivsize); - } else if (IS_SHA256_HMAC(rctx->flags)) { - auth_ivsize = SHA256_DIGEST_SIZE; - memcpy(authiv, std_iv_sha256, auth_ivsize); - } + auth_ivsize = SHA256_DIGEST_SIZE; + memcpy(authiv, std_iv_sha256, auth_ivsize); authiv_words = auth_ivsize / sizeof(u32); qce_write_array(qce, REG_AUTH_IV0, (u32 *)authiv, authiv_words); } else if (IS_CCM(rctx->flags)) { diff --git a/drivers/crypto/qce/common.h b/drivers/crypto/qce/common.h index 02e63ad9f24557..9cd2e6ed8bbb0f 100644 --- a/drivers/crypto/qce/common.h +++ b/drivers/crypto/qce/common.h @@ -22,7 +22,7 @@ /* IV length in bytes */ #define QCE_AES_IV_LENGTH AES_BLOCK_SIZE -/* max of AES_BLOCK_SIZE, DES3_EDE_BLOCK_SIZE */ +/* max of AES_BLOCK_SIZE */ #define QCE_MAX_IV_SIZE AES_BLOCK_SIZE /* maximum nonce bytes */ @@ -33,14 +33,10 @@ #define QCE_MAX_ALIGN_SIZE 64 /* cipher algorithms */ -#define QCE_ALG_DES BIT(0) -#define QCE_ALG_3DES BIT(1) #define QCE_ALG_AES BIT(2) /* hash and hmac algorithms */ -#define QCE_HASH_SHA1 BIT(3) #define QCE_HASH_SHA256 BIT(4) -#define QCE_HASH_SHA1_HMAC BIT(5) #define QCE_HASH_SHA256_HMAC BIT(6) #define QCE_HASH_AES_CMAC BIT(7) @@ -58,21 +54,15 @@ #define QCE_ENCRYPT BIT(30) #define QCE_DECRYPT BIT(31) -#define IS_DES(flags) (flags & QCE_ALG_DES) -#define IS_3DES(flags) (flags & QCE_ALG_3DES) #define IS_AES(flags) (flags & QCE_ALG_AES) -#define IS_SHA1(flags) (flags & QCE_HASH_SHA1) #define IS_SHA256(flags) (flags & QCE_HASH_SHA256) -#define IS_SHA1_HMAC(flags) (flags & QCE_HASH_SHA1_HMAC) #define IS_SHA256_HMAC(flags) (flags & QCE_HASH_SHA256_HMAC) #define IS_CMAC(flags) (flags & QCE_HASH_AES_CMAC) -#define IS_SHA(flags) (IS_SHA1(flags) || IS_SHA256(flags)) -#define IS_SHA_HMAC(flags) \ - (IS_SHA1_HMAC(flags) || IS_SHA256_HMAC(flags)) +#define IS_SHA(flags) IS_SHA256(flags) +#define IS_SHA_HMAC(flags) IS_SHA256_HMAC(flags) #define IS_CBC(mode) (mode & QCE_MODE_CBC) -#define IS_ECB(mode) (mode & QCE_MODE_ECB) #define IS_CTR(mode) (mode & QCE_MODE_CTR) #define IS_XTS(mode) (mode & QCE_MODE_XTS) #define IS_CCM(mode) (mode & QCE_MODE_CCM) diff --git a/drivers/crypto/qce/regs-v5.h b/drivers/crypto/qce/regs-v5.h index d59ed279890621..431a7db1a4e721 100644 --- a/drivers/crypto/qce/regs-v5.h +++ b/drivers/crypto/qce/regs-v5.h @@ -203,7 +203,6 @@ #define AUTH_SIZE_SHIFT 9 #define AUTH_SIZE_MASK GENMASK(13, 9) -#define AUTH_SIZE_SHA1 0 #define AUTH_SIZE_SHA256 1 #define AUTH_SIZE_ENUM_1_BYTES 0 #define AUTH_SIZE_ENUM_2_BYTES 1 @@ -284,15 +283,12 @@ #define ENCR_KEY_SZ_SHIFT 3 #define ENCR_KEY_SZ_MASK GENMASK(5, 3) -#define ENCR_KEY_SZ_DES 0 -#define ENCR_KEY_SZ_3DES 1 #define ENCR_KEY_SZ_AES128 0 #define ENCR_KEY_SZ_AES256 2 #define ENCR_ALG_SHIFT 0 #define ENCR_ALG_MASK GENMASK(2, 0) #define ENCR_ALG_NONE 0 -#define ENCR_ALG_DES 1 #define ENCR_ALG_AES 2 #define ENCR_ALG_KASUMI 4 #define ENCR_ALG_SNOW_3G 5 diff --git a/drivers/crypto/qce/sha.c b/drivers/crypto/qce/sha.c index 1b37121cbcdcb7..cd9391219b1f3a 100644 --- a/drivers/crypto/qce/sha.c +++ b/drivers/crypto/qce/sha.c @@ -25,10 +25,6 @@ struct qce_sha_saved_state { static LIST_HEAD(ahash_algs); -static const u32 std_iv_sha1[SHA256_DIGEST_SIZE / sizeof(u32)] = { - SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4, 0, 0, 0 -}; - static const u32 std_iv_sha256[SHA256_DIGEST_SIZE / sizeof(u32)] = { SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3, SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7 @@ -349,9 +345,7 @@ static int qce_ahash_hmac_setkey(struct crypto_ahash *tfm, const u8 *key, return 0; } - if (digestsize == SHA1_DIGEST_SIZE) - alg_name = "sha1-qce"; - else if (digestsize == SHA256_DIGEST_SIZE) + if (digestsize == SHA256_DIGEST_SIZE) alg_name = "sha256-qce"; else return -EINVAL; @@ -412,15 +406,6 @@ struct qce_ahash_def { }; static const struct qce_ahash_def ahash_def[] = { - { - .flags = QCE_HASH_SHA1, - .name = "sha1", - .drv_name = "sha1-qce", - .digestsize = SHA1_DIGEST_SIZE, - .blocksize = SHA1_BLOCK_SIZE, - .statesize = sizeof(struct qce_sha_saved_state), - .std_iv = std_iv_sha1, - }, { .flags = QCE_HASH_SHA256, .name = "sha256", @@ -430,15 +415,6 @@ static const struct qce_ahash_def ahash_def[] = { .statesize = sizeof(struct qce_sha_saved_state), .std_iv = std_iv_sha256, }, - { - .flags = QCE_HASH_SHA1_HMAC, - .name = "hmac(sha1)", - .drv_name = "hmac-sha1-qce", - .digestsize = SHA1_DIGEST_SIZE, - .blocksize = SHA1_BLOCK_SIZE, - .statesize = sizeof(struct qce_sha_saved_state), - .std_iv = std_iv_sha1, - }, { .flags = QCE_HASH_SHA256_HMAC, .name = "hmac(sha256)", @@ -476,9 +452,7 @@ static int qce_ahash_register_one(const struct qce_ahash_def *def, alg->halg.digestsize = def->digestsize; alg->halg.statesize = def->statesize; - if (IS_SHA1(def->flags)) - tmpl->hash_zero = sha1_zero_message_hash; - else if (IS_SHA256(def->flags)) + if (IS_SHA256(def->flags)) tmpl->hash_zero = sha256_zero_message_hash; base = &alg->halg.base; diff --git a/drivers/crypto/qce/sha.h b/drivers/crypto/qce/sha.h index a22695361f1654..cb822fc334dc18 100644 --- a/drivers/crypto/qce/sha.h +++ b/drivers/crypto/qce/sha.h @@ -7,7 +7,6 @@ #define _SHA_H_ #include -#include #include #include "common.h" diff --git a/drivers/crypto/qce/skcipher.c b/drivers/crypto/qce/skcipher.c index db0b648a56eb1a..ff4ee9541f7cb0 100644 --- a/drivers/crypto/qce/skcipher.c +++ b/drivers/crypto/qce/skcipher.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include "cipher.h" @@ -209,51 +208,6 @@ static int qce_skcipher_setkey(struct crypto_skcipher *ablk, const u8 *key, return ret; } -static int qce_des_setkey(struct crypto_skcipher *ablk, const u8 *key, - unsigned int keylen) -{ - struct qce_cipher_ctx *ctx = crypto_skcipher_ctx(ablk); - int err; - - err = verify_skcipher_des_key(ablk, key); - if (err) - return err; - - ctx->enc_keylen = keylen; - memcpy(ctx->enc_key, key, keylen); - return 0; -} - -static int qce_des3_setkey(struct crypto_skcipher *ablk, const u8 *key, - unsigned int keylen) -{ - struct qce_cipher_ctx *ctx = crypto_skcipher_ctx(ablk); - u32 _key[6]; - int err; - - err = verify_skcipher_des3_key(ablk, key); - if (err) - return err; - - /* - * The crypto engine does not support any two keys - * being the same for triple des algorithms. The - * verify_skcipher_des3_key does not check for all the - * below conditions. Return -ENOKEY in case any two keys - * are the same. Revisit to see if a fallback cipher - * is needed to handle this condition. - */ - memcpy(_key, key, DES3_EDE_KEY_SIZE); - if (!((_key[0] ^ _key[2]) | (_key[1] ^ _key[3])) || - !((_key[2] ^ _key[4]) | (_key[3] ^ _key[5])) || - !((_key[0] ^ _key[4]) | (_key[1] ^ _key[5]))) - return -ENOKEY; - - ctx->enc_keylen = keylen; - memcpy(ctx->enc_key, key, keylen); - return 0; -} - static int qce_skcipher_crypt(struct skcipher_request *req, int encrypt) { struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); @@ -276,7 +230,7 @@ static int qce_skcipher_crypt(struct skcipher_request *req, int encrypt) * ECB and CBC algorithms require message lengths to be * multiples of block size. */ - if (IS_ECB(rctx->flags) || IS_CBC(rctx->flags)) + if (IS_CBC(rctx->flags)) if (!IS_ALIGNED(req->cryptlen, blocksize)) return -EINVAL; @@ -359,15 +313,6 @@ struct qce_skcipher_def { }; static const struct qce_skcipher_def skcipher_def[] = { - { - .flags = QCE_ALG_AES | QCE_MODE_ECB, - .name = "ecb(aes)", - .drv_name = "ecb-aes-qce", - .blocksize = AES_BLOCK_SIZE, - .ivsize = 0, - .min_keysize = AES_MIN_KEY_SIZE, - .max_keysize = AES_MAX_KEY_SIZE, - }, { .flags = QCE_ALG_AES | QCE_MODE_CBC, .name = "cbc(aes)", @@ -396,42 +341,6 @@ static const struct qce_skcipher_def skcipher_def[] = { .min_keysize = AES_MIN_KEY_SIZE * 2, .max_keysize = AES_MAX_KEY_SIZE * 2, }, - { - .flags = QCE_ALG_DES | QCE_MODE_ECB, - .name = "ecb(des)", - .drv_name = "ecb-des-qce", - .blocksize = DES_BLOCK_SIZE, - .ivsize = 0, - .min_keysize = DES_KEY_SIZE, - .max_keysize = DES_KEY_SIZE, - }, - { - .flags = QCE_ALG_DES | QCE_MODE_CBC, - .name = "cbc(des)", - .drv_name = "cbc-des-qce", - .blocksize = DES_BLOCK_SIZE, - .ivsize = DES_BLOCK_SIZE, - .min_keysize = DES_KEY_SIZE, - .max_keysize = DES_KEY_SIZE, - }, - { - .flags = QCE_ALG_3DES | QCE_MODE_ECB, - .name = "ecb(des3_ede)", - .drv_name = "ecb-3des-qce", - .blocksize = DES3_EDE_BLOCK_SIZE, - .ivsize = 0, - .min_keysize = DES3_EDE_KEY_SIZE, - .max_keysize = DES3_EDE_KEY_SIZE, - }, - { - .flags = QCE_ALG_3DES | QCE_MODE_CBC, - .name = "cbc(des3_ede)", - .drv_name = "cbc-3des-qce", - .blocksize = DES3_EDE_BLOCK_SIZE, - .ivsize = DES3_EDE_BLOCK_SIZE, - .min_keysize = DES3_EDE_KEY_SIZE, - .max_keysize = DES3_EDE_KEY_SIZE, - }, }; static int qce_skcipher_register_one(const struct qce_skcipher_def *def, @@ -455,9 +364,7 @@ static int qce_skcipher_register_one(const struct qce_skcipher_def *def, alg->ivsize = def->ivsize; alg->min_keysize = def->min_keysize; alg->max_keysize = def->max_keysize; - alg->setkey = IS_3DES(def->flags) ? qce_des3_setkey : - IS_DES(def->flags) ? qce_des_setkey : - qce_skcipher_setkey; + alg->setkey = qce_skcipher_setkey; alg->encrypt = qce_skcipher_encrypt; alg->decrypt = qce_skcipher_decrypt; diff --git a/drivers/crypto/qcom-rng.c b/drivers/crypto/qcom-rng.c index 150e5802e35158..4d046caafe5bbf 100644 --- a/drivers/crypto/qcom-rng.c +++ b/drivers/crypto/qcom-rng.c @@ -3,10 +3,8 @@ // // Based on msm-rng.c and downstream driver -#include #include #include -#include #include #include #include @@ -32,24 +30,15 @@ #define QCOM_TRNG_QUALITY 1024 struct qcom_rng { - struct mutex lock; void __iomem *base; struct clk *clk; struct hwrng hwrng; - struct qcom_rng_match_data *match_data; -}; - -struct qcom_rng_ctx { - struct qcom_rng *rng; }; struct qcom_rng_match_data { - bool skip_init; bool hwrng_support; }; -static struct qcom_rng *qcom_rng_dev; - static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max) { unsigned int currsize = 0; @@ -65,8 +54,6 @@ static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max) return ret; val = readl_relaxed(rng->base + PRNG_DATA_OUT); - if (!val) - return -EINVAL; if ((max - currsize) >= WORD_SZ) { memcpy(data, &val, WORD_SZ); @@ -82,35 +69,11 @@ static int qcom_rng_read(struct qcom_rng *rng, u8 *data, unsigned int max) return currsize; } -static int qcom_rng_generate(struct crypto_rng *tfm, - const u8 *src, unsigned int slen, - u8 *dstn, unsigned int dlen) +static int qcom_hwrng_init(struct hwrng *hwrng) { - struct qcom_rng_ctx *ctx = crypto_rng_ctx(tfm); - struct qcom_rng *rng = ctx->rng; - int ret; - - ret = clk_prepare_enable(rng->clk); - if (ret) - return ret; - - mutex_lock(&rng->lock); - - ret = qcom_rng_read(rng, dstn, dlen); - - mutex_unlock(&rng->lock); - clk_disable_unprepare(rng->clk); - - if (ret >= 0) - ret = 0; - - return ret; -} + struct qcom_rng *qrng = container_of(hwrng, struct qcom_rng, hwrng); -static int qcom_rng_seed(struct crypto_rng *tfm, const u8 *seed, - unsigned int slen) -{ - return 0; + return clk_prepare_enable(qrng->clk); } static int qcom_hwrng_read(struct hwrng *hwrng, void *data, size_t max, bool wait) @@ -120,74 +83,33 @@ static int qcom_hwrng_read(struct hwrng *hwrng, void *data, size_t max, bool wai return qcom_rng_read(qrng, data, max); } -static int qcom_rng_enable(struct qcom_rng *rng) +static void qcom_hwrng_cleanup(struct hwrng *hwrng) { - u32 val; - int ret; - - ret = clk_prepare_enable(rng->clk); - if (ret) - return ret; - - /* Enable PRNG only if it is not already enabled */ - val = readl_relaxed(rng->base + PRNG_CONFIG); - if (val & PRNG_CONFIG_HW_ENABLE) - goto already_enabled; - - val = readl_relaxed(rng->base + PRNG_LFSR_CFG); - val &= ~PRNG_LFSR_CFG_MASK; - val |= PRNG_LFSR_CFG_CLOCKS; - writel(val, rng->base + PRNG_LFSR_CFG); - - val = readl_relaxed(rng->base + PRNG_CONFIG); - val |= PRNG_CONFIG_HW_ENABLE; - writel(val, rng->base + PRNG_CONFIG); - -already_enabled: - clk_disable_unprepare(rng->clk); - - return 0; -} - -static int qcom_rng_init(struct crypto_tfm *tfm) -{ - struct qcom_rng_ctx *ctx = crypto_tfm_ctx(tfm); - - ctx->rng = qcom_rng_dev; - - if (!ctx->rng->match_data->skip_init) - return qcom_rng_enable(ctx->rng); + struct qcom_rng *qrng = container_of(hwrng, struct qcom_rng, hwrng); - return 0; + clk_disable_unprepare(qrng->clk); } -static struct rng_alg qcom_rng_alg = { - .generate = qcom_rng_generate, - .seed = qcom_rng_seed, - .seedsize = 0, - .base = { - .cra_name = "stdrng", - .cra_driver_name = "qcom-rng", - .cra_flags = CRYPTO_ALG_TYPE_RNG, - .cra_priority = 300, - .cra_ctxsize = sizeof(struct qcom_rng_ctx), - .cra_module = THIS_MODULE, - .cra_init = qcom_rng_init, - } -}; - static int qcom_rng_probe(struct platform_device *pdev) { + const struct qcom_rng_match_data *match_data; struct qcom_rng *rng; int ret; + match_data = device_get_match_data(&pdev->dev); + if (match_data == NULL || !match_data->hwrng_support) { + dev_info(&pdev->dev, "TRNG support not detected\n"); + /* + * In this case the driver does nothing except the dev_info(), + * but bind the device anyway to avoid effects on GCC state. + */ + return 0; + } + rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); if (!rng) return -ENOMEM; - platform_set_drvdata(pdev, rng); - mutex_init(&rng->lock); - rng->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rng->base)) return PTR_ERR(rng->base); @@ -196,53 +118,26 @@ static int qcom_rng_probe(struct platform_device *pdev) if (IS_ERR(rng->clk)) return PTR_ERR(rng->clk); - rng->match_data = (struct qcom_rng_match_data *)device_get_match_data(&pdev->dev); - - qcom_rng_dev = rng; - ret = crypto_register_rng(&qcom_rng_alg); - if (ret) { - dev_err(&pdev->dev, "Register crypto rng failed: %d\n", ret); - qcom_rng_dev = NULL; - return ret; - } - - if (rng->match_data->hwrng_support) { - rng->hwrng.name = "qcom_hwrng"; - rng->hwrng.read = qcom_hwrng_read; - rng->hwrng.quality = QCOM_TRNG_QUALITY; - ret = devm_hwrng_register(&pdev->dev, &rng->hwrng); - if (ret) { - dev_err(&pdev->dev, "Register hwrng failed: %d\n", ret); - qcom_rng_dev = NULL; - goto fail; - } - } - - return ret; -fail: - crypto_unregister_rng(&qcom_rng_alg); + rng->hwrng.name = "qcom_hwrng"; + rng->hwrng.init = qcom_hwrng_init; + rng->hwrng.read = qcom_hwrng_read; + rng->hwrng.cleanup = qcom_hwrng_cleanup; + rng->hwrng.quality = QCOM_TRNG_QUALITY; + ret = devm_hwrng_register(&pdev->dev, &rng->hwrng); + if (ret) + dev_err(&pdev->dev, "Register hwrng failed: %d\n", ret); return ret; } -static void qcom_rng_remove(struct platform_device *pdev) -{ - crypto_unregister_rng(&qcom_rng_alg); - - qcom_rng_dev = NULL; -} - static struct qcom_rng_match_data qcom_prng_match_data = { - .skip_init = false, .hwrng_support = false, }; static struct qcom_rng_match_data qcom_prng_ee_match_data = { - .skip_init = true, .hwrng_support = false, }; static struct qcom_rng_match_data qcom_trng_match_data = { - .skip_init = true, .hwrng_support = true, }; @@ -262,7 +157,6 @@ MODULE_DEVICE_TABLE(of, qcom_rng_of_match); static struct platform_driver qcom_rng_driver = { .probe = qcom_rng_probe, - .remove = qcom_rng_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = qcom_rng_of_match, diff --git a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c index d8d452cac3910b..64ea141f018cb0 100644 --- a/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c +++ b/drivers/crypto/virtio/virtio_crypto_akcipher_algs.c @@ -88,7 +88,8 @@ static void virtio_crypto_dataq_akcipher_callback(struct virtio_crypto_request * } /* actual length may be less than dst buffer */ - akcipher_req->dst_len = len - sizeof(vc_req->status); + akcipher_req->dst_len = min_t(unsigned int, len - sizeof(vc_req->status), + akcipher_req->dst_len); sg_copy_from_buffer(akcipher_req->dst, sg_nents(akcipher_req->dst), vc_akcipher_req->dst_buf, akcipher_req->dst_len); virtio_crypto_akcipher_finalize_req(vc_akcipher_req, akcipher_req, error); diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c index 85185af46b72d8..738a89863ee896 100644 --- a/drivers/cxl/core/features.c +++ b/drivers/cxl/core/features.c @@ -649,7 +649,13 @@ static void *cxlctl_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope, struct cxl_memdev *cxlmd = fwctl_to_memdev(fwctl_dev); struct cxl_features_state *cxlfs = to_cxlfs(cxlmd->cxlds); const struct fwctl_rpc_cxl *rpc_in = in; - u16 opcode = rpc_in->opcode; + u16 opcode; + + if (in_len < sizeof(rpc_in->hdr) || + rpc_in->op_size > in_len - sizeof(rpc_in->hdr)) + return ERR_PTR(-EINVAL); + + opcode = rpc_in->opcode; if (!cxlctl_validate_hw_command(cxlfs, rpc_in, scope, opcode)) return ERR_PTR(-EINVAL); diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index 7c6c5b7450a54a..1fa1f78565e310 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -11,7 +11,6 @@ #include "core.h" #include "trace.h" -#include "mce.h" static bool cxl_raw_allow_all; @@ -1526,7 +1525,6 @@ struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial, u16 dvsec) { struct cxl_memdev_state *mds; - int rc; mds = devm_cxl_dev_state_create(dev, CXL_DEVTYPE_CLASSMEM, serial, dvsec, struct cxl_memdev_state, cxlds, @@ -1538,12 +1536,6 @@ struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial, mutex_init(&mds->event.log_lock); - rc = devm_cxl_register_mce_notifier(dev, &mds->mce_notifier); - if (rc == -EOPNOTSUPP) - dev_warn(dev, "CXL MCE unsupported\n"); - else if (rc) - return ERR_PTR(rc); - return mds; } EXPORT_SYMBOL_NS_GPL(cxl_memdev_state_create, "CXL"); diff --git a/drivers/cxl/core/mce.c b/drivers/cxl/core/mce.c index ff8d078c6ca1e9..65fed913b22179 100644 --- a/drivers/cxl/core/mce.c +++ b/drivers/cxl/core/mce.c @@ -4,16 +4,16 @@ #include #include #include -#include +#include +#include "core.h" #include "mce.h" static int cxl_handle_mce(struct notifier_block *nb, unsigned long val, void *data) { - struct cxl_memdev_state *mds = container_of(nb, struct cxl_memdev_state, - mce_notifier); - struct cxl_memdev *cxlmd = mds->cxlds.cxlmd; - struct cxl_port *endpoint = cxlmd->endpoint; + struct cxl_region *cxlr = container_of(nb, struct cxl_region, + mce_notifier); + struct cxl_region_params *p = &cxlr->params; struct mce *mce = data; u64 spa, spa_alias; unsigned long pfn; @@ -21,26 +21,25 @@ static int cxl_handle_mce(struct notifier_block *nb, unsigned long val, if (!mce || !mce_usable_address(mce)) return NOTIFY_DONE; - if (!endpoint) - return NOTIFY_DONE; - spa = mce->addr & MCI_ADDR_PHYSADDR; - pfn = spa >> PAGE_SHIFT; - if (!pfn_valid(pfn)) + if (!cxl_resource_contains_addr(p->res, spa)) return NOTIFY_DONE; - spa_alias = cxl_port_get_spa_cache_alias(endpoint, spa); - if (spa_alias == ~0ULL) - return NOTIFY_DONE; + if (spa >= p->res->start + p->cache_size) + spa_alias = spa - p->cache_size; + else + spa_alias = spa + p->cache_size; pfn = spa_alias >> PAGE_SHIFT; + if (!pfn_valid(pfn)) + return NOTIFY_DONE; /* * Take down the aliased memory page. The original memory page flagged * by the MCE will be taken cared of by the standard MCE handler. */ - dev_emerg(mds->cxlds.dev, "Offlining aliased SPA address0: %#llx\n", + dev_emerg(&cxlr->dev, "Offlining aliased SPA address0: %#llx\n", spa_alias); if (!memory_failure(pfn, 0)) set_mce_nospec(pfn); diff --git a/drivers/cxl/core/pmem.c b/drivers/cxl/core/pmem.c index 68462e38a977b1..5a3bb7e8a1f14a 100644 --- a/drivers/cxl/core/pmem.c +++ b/drivers/cxl/core/pmem.c @@ -219,12 +219,14 @@ static struct cxl_nvdimm *cxl_nvdimm_alloc(struct cxl_nvdimm_bridge *cxl_nvb, dev->bus = &cxl_bus_type; dev->type = &cxl_nvdimm_type; /* - * A "%llx" string is 17-bytes vs dimm_id that is max - * NVDIMM_KEY_DESC_LEN + * dev_id is the nvdimm dimm_id used for security key lookup. + * It must match id_show(), which emits the CXL serial as an + * unsigned decimal. A u64 decimal string is at most 20 digits + * plus NUL. */ - BUILD_BUG_ON(sizeof(cxl_nvd->dev_id) < 17 || + BUILD_BUG_ON(sizeof(cxl_nvd->dev_id) < 21 || sizeof(cxl_nvd->dev_id) > NVDIMM_KEY_DESC_LEN); - sprintf(cxl_nvd->dev_id, "%llx", cxlmd->cxlds->serial); + sprintf(cxl_nvd->dev_id, "%llu", cxlmd->cxlds->serial); return cxl_nvd; } diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c index 99fb00949c2fac..bc74d4848132ec 100644 --- a/drivers/cxl/core/ras.c +++ b/drivers/cxl/core/ras.c @@ -137,7 +137,6 @@ int cxl_ras_init(void) void cxl_ras_exit(void) { cxl_cper_unregister_prot_err_work(&cxl_cper_prot_err_work); - cancel_work_sync(&cxl_cper_prot_err_work); } static void cxl_dport_map_ras(struct cxl_dport *dport) diff --git a/drivers/cxl/core/ras_rch.c b/drivers/cxl/core/ras_rch.c index 0a8b3b9b638841..e0e01aa5eba6cd 100644 --- a/drivers/cxl/core/ras_rch.c +++ b/drivers/cxl/core/ras_rch.c @@ -58,13 +58,28 @@ void cxl_disable_rch_root_ints(struct cxl_dport *dport) static bool cxl_rch_get_aer_info(void __iomem *aer_base, struct aer_capability_regs *aer_regs) { - int read_cnt = sizeof(struct aer_capability_regs) / sizeof(u32); + /* + * Bound the copy to the physically-defined AER registers (header + * through the 16-byte Header Log). struct aer_capability_regs is a + * software layout whose embedded struct pcie_tlp_log is larger than + * the on-wire AER capability; copying sizeof(*aer_regs) would + * over-read the RCRB-mapped MMIO block. + */ + int read_cnt = (PCI_ERR_HEADER_LOG + 16) / sizeof(u32); u32 *aer_regs_buf = (u32 *)aer_regs; int n; if (!aer_base) return false; + /* + * Zero the destination so the software-only tail fields + * (e.g. header_log.header_len) are deterministic rather than + * left as uninitialized stack, which could drive a bogus loop + * length in pcie_print_tlp_log(). + */ + memset(aer_regs, 0, sizeof(*aer_regs)); + /* Use readl() to guarantee 32-bit accesses */ for (n = 0; n < read_cnt; n++) aer_regs_buf[n] = readl(aer_base + n * sizeof(u32)); @@ -79,11 +94,11 @@ static bool cxl_rch_get_aer_info(void __iomem *aer_base, static bool cxl_rch_get_aer_severity(struct aer_capability_regs *aer_regs, int *severity) { - if (aer_regs->uncor_status & ~aer_regs->uncor_mask) { - if (aer_regs->uncor_status & PCI_ERR_ROOT_FATAL_RCV) - *severity = AER_FATAL; - else - *severity = AER_NONFATAL; + u32 uncor_status = aer_regs->uncor_status & ~aer_regs->uncor_mask; + + if (uncor_status) { + *severity = (uncor_status & aer_regs->uncor_severity) ? + AER_FATAL : AER_NONFATAL; return true; } diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 1e211542b6b641..578622240401d4 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -15,6 +15,7 @@ #include #include #include "core.h" +#include "mce.h" /** * DOC: cxl core region @@ -3859,34 +3860,6 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled) } EXPORT_SYMBOL_NS_GPL(cxl_add_to_region, "CXL"); -u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa) -{ - struct cxl_region_ref *iter; - unsigned long index; - - if (!endpoint) - return ~0ULL; - - guard(rwsem_write)(&cxl_rwsem.region); - - xa_for_each(&endpoint->regions, index, iter) { - struct cxl_region_params *p = &iter->region->params; - - if (cxl_resource_contains_addr(p->res, spa)) { - if (!p->cache_size) - return ~0ULL; - - if (spa >= p->res->start + p->cache_size) - return spa - p->cache_size; - - return spa + p->cache_size; - } - } - - return ~0ULL; -} -EXPORT_SYMBOL_NS_GPL(cxl_port_get_spa_cache_alias, "CXL"); - static int is_system_ram(struct resource *res, void *arg) { struct cxl_region *cxlr = arg; @@ -4217,6 +4190,20 @@ static int cxl_region_probe(struct device *dev) if (rc) return rc; + /* + * Regions fronted by an extended linear cache need the MCE notifier to + * offline the aliased page on a memory error. + */ + if (p->cache_size) { + rc = devm_cxl_register_mce_notifier(&cxlr->dev, + &cxlr->mce_notifier); + if (rc == -EOPNOTSUPP) + dev_warn(&cxlr->dev, + "CONFIG_CXL_MCE disabled, MCE notifier not registered\n"); + else if (rc) + return rc; + } + rc = cxl_region_setup_poison(cxlr); if (rc) return rc; diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index c0e5308e4d1b84..cab8ce39f46533 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -478,6 +478,7 @@ struct cxl_region_params { * @coord: QoS access coordinates for the region * @node_notifier: notifier for setting the access coordinates to node * @adist_notifier: notifier for calculating the abstract distance of node + * @mce_notifier: notifier for MCE */ struct cxl_region { struct device dev; @@ -493,6 +494,7 @@ struct cxl_region { struct access_coordinate coord[ACCESS_COORDINATE_MAX]; struct notifier_block node_notifier; struct notifier_block adist_notifier; + struct notifier_block mce_notifier; }; struct cxl_nvdimm_bridge { @@ -503,7 +505,8 @@ struct cxl_nvdimm_bridge { struct nvdimm_bus_descriptor nd_desc; }; -#define CXL_DEV_ID_LEN 19 +/* Holds a u64 serial as a decimal string: up to 20 digits + NUL */ +#define CXL_DEV_ID_LEN 21 enum { CXL_NVD_F_INVALIDATED = 0, @@ -870,7 +873,6 @@ bool is_cxl_pmem_region(struct device *dev); struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev); int cxl_add_to_region(struct cxl_endpoint_decoder *cxled); struct cxl_dax_region *to_cxl_dax_region(struct device *dev); -u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa); bool cxl_region_contains_resource(const struct resource *res); #else static inline bool is_cxl_pmem_region(struct device *dev) @@ -889,11 +891,6 @@ static inline struct cxl_dax_region *to_cxl_dax_region(struct device *dev) { return NULL; } -static inline u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, - u64 spa) -{ - return 0; -} static inline bool cxl_region_contains_resource(const struct resource *res) { return false; diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index ed419d0c59f2f1..c288e624c1f68d 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -431,7 +431,6 @@ static inline struct cxl_dev_state *mbox_to_cxlds(struct cxl_mailbox *cxl_mbox) * @poison: poison driver state info * @security: security driver state info * @fw: firmware upload / activation state - * @mce_notifier: MCE notifier * * See CXL 3.0 8.2.9.8.2 Capacity Configuration and Label Storage for * details on capacity parameters. @@ -451,7 +450,6 @@ struct cxl_memdev_state { struct cxl_poison_state poison; struct cxl_security_state security; struct cxl_fw_state fw; - struct notifier_block mce_notifier; }; static inline struct cxl_memdev_state * diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index 267c679b0b3c20..7c6faee7f85ed5 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -1083,7 +1083,6 @@ static int __init cxl_pci_driver_init(void) static void __exit cxl_pci_driver_exit(void) { cxl_cper_unregister_work(&cxl_cper_work); - cancel_work_sync(&cxl_cper_work); pci_unregister_driver(&cxl_pci_driver); } diff --git a/drivers/cxl/pmem.c b/drivers/cxl/pmem.c index 261dff7ced9f15..a9f50281875d83 100644 --- a/drivers/cxl/pmem.c +++ b/drivers/cxl/pmem.c @@ -52,7 +52,7 @@ static ssize_t id_show(struct device *dev, struct device_attribute *attr, char * struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm); struct cxl_dev_state *cxlds = cxl_nvd->cxlmd->cxlds; - return sysfs_emit(buf, "%lld\n", cxlds->serial); + return sysfs_emit(buf, "%llu\n", cxlds->serial); } static DEVICE_ATTR_RO(id); diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index d504c636dc294e..4c9add51f9ef55 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -803,6 +803,26 @@ int dma_buf_fd(struct dma_buf *dmabuf, int flags) } EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF"); +/** + * dma_buf_fd_install - install a reserved fd for a dma-buf + * @dmabuf: [in] pointer to dma_buf + * @fd: [in] fd reserved with get_unused_fd_flags() + * + * Publishes a previously reserved fd into the caller's fd table. + * Must only be called after all fallible work (e.g. copy_to_user) + * has succeeded, as it cannot be undone safely once called. + * + * The caller is responsible for having emitted the trace event + * (via dma_buf_fd() or get_unused_fd_flags() + this function) + * before calling this. + */ +void dma_buf_fd_install(struct dma_buf *dmabuf, int fd) +{ + DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd); + fd_install(fd, dmabuf->file); +} +EXPORT_SYMBOL_NS_GPL(dma_buf_fd_install, "DMA_BUF"); + /** * dma_buf_get - returns the struct dma_buf related to an fd * @fd: [in] fd associated with the struct dma_buf to be returned diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c index a76bf3f8b071a3..43c32fb283130e 100644 --- a/drivers/dma-buf/dma-heap.c +++ b/drivers/dma-buf/dma-heap.c @@ -55,33 +55,6 @@ MODULE_PARM_DESC(mem_accounting, "Enable cgroup-based memory accounting for dma-buf heap allocations (default=false)."); EXPORT_SYMBOL_NS_GPL(mem_accounting, "DMA_BUF_HEAP"); -static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len, - u32 fd_flags, - u64 heap_flags) -{ - struct dma_buf *dmabuf; - int fd; - - /* - * Allocations from all heaps have to begin - * and end on page boundaries. - */ - len = PAGE_ALIGN(len); - if (!len) - return -EINVAL; - - dmabuf = heap->ops->allocate(heap, len, fd_flags, heap_flags); - if (IS_ERR(dmabuf)) - return PTR_ERR(dmabuf); - - fd = dma_buf_fd(dmabuf, fd_flags); - if (fd < 0) { - dma_buf_put(dmabuf); - /* just return, as put will call release and that will free */ - } - return fd; -} - static int dma_heap_open(struct inode *inode, struct file *file) { struct dma_heap *heap; @@ -99,30 +72,42 @@ static int dma_heap_open(struct inode *inode, struct file *file) return 0; } -static long dma_heap_ioctl_allocate(struct file *file, void *data) +static struct dma_buf *dma_heap_ioctl_allocate(struct file *file, void *data) { struct dma_heap_allocation_data *heap_allocation = data; struct dma_heap *heap = file->private_data; + struct dma_buf *dmabuf; int fd; + size_t len; if (heap_allocation->fd) - return -EINVAL; + return ERR_PTR(-EINVAL); if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS) - return -EINVAL; + return ERR_PTR(-EINVAL); if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS) - return -EINVAL; + return ERR_PTR(-EINVAL); + + len = PAGE_ALIGN(heap_allocation->len); + if (!len) + return ERR_PTR(-EINVAL); + + dmabuf = heap->ops->allocate(heap, len, heap_allocation->fd_flags, + heap_allocation->heap_flags); - fd = dma_heap_buffer_alloc(heap, heap_allocation->len, - heap_allocation->fd_flags, - heap_allocation->heap_flags); - if (fd < 0) - return fd; + if (IS_ERR(dmabuf)) + return dmabuf; + + fd = get_unused_fd_flags(heap_allocation->fd_flags); + if (fd < 0) { + dma_buf_put(dmabuf); + return ERR_PTR(fd); + } heap_allocation->fd = fd; - return 0; + return dmabuf; } static unsigned int dma_heap_ioctl_cmds[] = { @@ -138,6 +123,8 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd, unsigned int in_size, out_size, drv_size, ksize; int nr = _IOC_NR(ucmd); int ret = 0; + int fd; + struct dma_buf *dmabuf; if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds)) return -EINVAL; @@ -174,15 +161,28 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd, switch (kcmd) { case DMA_HEAP_IOCTL_ALLOC: - ret = dma_heap_ioctl_allocate(file, kdata); + dmabuf = dma_heap_ioctl_allocate(file, kdata); + + if (IS_ERR(dmabuf)) { + ret = PTR_ERR(dmabuf); + break; + } + + fd = ((struct dma_heap_allocation_data *)kdata)->fd; + if (copy_to_user((void __user *)arg, kdata, out_size) != 0) { + put_unused_fd(fd); + dma_buf_put(dmabuf); + ret = -EFAULT; + } else { + dma_buf_fd_install(dmabuf, fd); + } + break; default: ret = -ENOTTY; goto err; } - if (copy_to_user((void __user *)arg, kdata, out_size) != 0) - ret = -EFAULT; err: if (kdata != stack_kdata) kfree(kdata); diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index 89a4c498a17b8e..18ec188c991194 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -297,7 +297,8 @@ static int dw_edma_device_resume(struct dma_chan *dchan) err = -EPERM; } else { chan->status = EDMA_ST_BUSY; - dw_edma_start_transfer(chan); + if (!dw_edma_start_transfer(chan)) + chan->status = EDMA_ST_IDLE; } return err; @@ -655,10 +656,16 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) unsigned long flags; spin_lock_irqsave(&chan->vc.lock, flags); + if (chan->status == EDMA_ST_PAUSE) { + spin_unlock_irqrestore(&chan->vc.lock, flags); + return; + } + vd = vchan_next_desc(&chan->vc); if (vd) { switch (chan->request) { case EDMA_REQ_NONE: + case EDMA_REQ_PAUSE: desc = vd2dw_edma_desc(vd); if (!desc->chunks_alloc) { dw_hdma_set_callback_result(vd, @@ -667,6 +674,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) vchan_cookie_complete(vd); } + if (chan->request == EDMA_REQ_PAUSE) { + chan->request = EDMA_REQ_NONE; + chan->status = EDMA_ST_PAUSE; + break; + } + /* Continue transferring if there are remaining chunks or issued requests. */ chan->status = dw_edma_start_transfer(chan) ? EDMA_ST_BUSY : EDMA_ST_IDLE; @@ -679,11 +692,6 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) chan->status = EDMA_ST_IDLE; break; - case EDMA_REQ_PAUSE: - chan->request = EDMA_REQ_NONE; - chan->status = EDMA_ST_PAUSE; - break; - default: break; } @@ -755,6 +763,7 @@ static int dw_edma_emul_irq_alloc(struct dw_edma *dw) return virq; irq_set_chip_and_handler(virq, &dw_edma_emul_irqchip, handle_level_irq); + irq_set_status_flags(virq, IRQ_LEVEL); irq_set_chip_data(virq, dw); irq_set_noprobe(virq); @@ -929,7 +938,6 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) else irq->rd_mask |= BIT(chan->id); - irq->dw = dw; memcpy(&chan->msi, &irq->msi, sizeof(chan->msi)); dev_vdbg(dev, "MSI:\t\tChannel %s[%u] addr=0x%.8x%.8x, data=0x%.8x\n", @@ -1010,6 +1018,7 @@ static int dw_edma_irq_request(struct dw_edma *dw, if (chip->nr_irqs == 1) { /* Common IRQ shared among all channels */ irq = chip->ops->irq_vector(dev, 0); + dw->irq[0].dw = dw; err = request_irq(irq, dw_edma_interrupt_common, IRQF_SHARED, dw->name, &dw->irq[0]); if (err) { @@ -1032,6 +1041,7 @@ static int dw_edma_irq_request(struct dw_edma *dw, for (i = 0; i < (*wr_alloc + *rd_alloc); i++) { irq = chip->ops->irq_vector(dev, i); + dw->irq[i].dw = dw; err = request_irq(irq, i < *wr_alloc ? dw_edma_interrupt_write : diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c index 632abb8b481cfe..2beec876b18428 100644 --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c @@ -79,7 +79,7 @@ static enum dma_status dw_hdma_v0_core_ch_status(struct dw_edma_chan *chan) u32 tmp; tmp = FIELD_GET(HDMA_V0_CH_STATUS_MASK, - GET_CH_32(dw, chan->id, chan->dir, ch_stat)); + GET_CH_32(dw, chan->dir, chan->id, ch_stat)); if (tmp == 1) return DMA_IN_PROGRESS; diff --git a/drivers/dma/fsl-edma-trace.h b/drivers/dma/fsl-edma-trace.h index d3541301a24706..45d964a3726d65 100644 --- a/drivers/dma/fsl-edma-trace.h +++ b/drivers/dma/fsl-edma-trace.h @@ -19,14 +19,16 @@ DECLARE_EVENT_CLASS(edma_log_io, __field(struct fsl_edma_engine *, edma) __field(void __iomem *, addr) __field(u32, value) + __field(void __iomem *, membase) ), TP_fast_assign( __entry->edma = edma; __entry->addr = addr; __entry->value = value; + __entry->membase = edma->membase; ), TP_printk("offset %08x: value %08x", - (u32)(__entry->addr - __entry->edma->membase), __entry->value) + (u32)(__entry->addr - __entry->membase), __entry->value) ); DEFINE_EVENT(edma_log_io, edma_readl, diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c index b7678b174ecfa6..47bebc5bfa3757 100644 --- a/drivers/dma/qcom/gpi.c +++ b/drivers/dma/qcom/gpi.c @@ -662,8 +662,14 @@ static inline u32 gpi_read_reg(struct gpii *gpii, void __iomem *addr) static phys_addr_t gpi_read_ev_rp(struct gpii *gpii) { - u32 lsb = gpi_read_reg(gpii, gpii->ev_ring_rp_lsb_reg); - u32 msb; + u32 lsb, msb; + + /* + * The event read pointer publishes entries written by the device into + * coherent memory. Use ordered MMIO reads so those writes are visible + * before the caller consumes an event from the ring. + */ + lsb = readl(gpii->ev_ring_rp_lsb_reg); /* * Denali publishes and may inspect the event ring before either QSPI @@ -674,7 +680,7 @@ static phys_addr_t gpi_read_ev_rp(struct gpii *gpii) if (!gpii_has_denali_qspi(gpii)) return lsb; - msb = gpi_read_reg(gpii, gpii->ev_ring_rp_lsb_reg + 4); + msb = readl(gpii->ev_ring_rp_lsb_reg + 4); return ((phys_addr_t)msb << 32) | lsb; } diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index ee7154e1d51a22..87eebf53b4f79d 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -2307,7 +2307,7 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = { { .compatible = "lenovo,ideacentre-mini-01q8x10" }, { .compatible = "lenovo,ideapad-5-2in1" }, { .compatible = "lenovo,ideapad-slim3" }, - { .compatible = "lenovo,ideapad-slim5x" }, + { .compatible = "lenovo,ideapad-5" }, { .compatible = "lenovo,thinkbook-16" }, { .compatible = "lenovo,thinkpad-t14s" }, { .compatible = "lenovo,thinkpad-x13s", }, diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c index 44badfd11e1b0a..070c025ee21549 100644 --- a/drivers/fpga/altera-cvp.c +++ b/drivers/fpga/altera-cvp.c @@ -16,6 +16,7 @@ #include #include #include +#include #define CVP_BAR 0 /* BAR used for data transfer in memory mode */ #define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */ @@ -261,7 +262,7 @@ static int altera_cvp_v2_wait_for_credit(struct fpga_manager *mgr, static int altera_cvp_send_block(struct altera_cvp_conf *conf, const u32 *data, size_t len) { - u32 mask, words = len / sizeof(u32); + u32 words = len / sizeof(u32); int i, remainder; for (i = 0; i < words; i++) @@ -270,9 +271,10 @@ static int altera_cvp_send_block(struct altera_cvp_conf *conf, /* write up to 3 trailing bytes, if any */ remainder = len % sizeof(u32); if (remainder) { - mask = BIT(remainder * 8) - 1; - if (mask) - conf->write_data(conf, *data & mask); + u32 word = 0; + + memcpy(&word, data, remainder); + conf->write_data(conf, word); } return 0; diff --git a/drivers/fpga/dfl-fme-perf.c b/drivers/fpga/dfl-fme-perf.c index 7422d2bc6f3742..db56d52411efc2 100644 --- a/drivers/fpga/dfl-fme-perf.c +++ b/drivers/fpga/dfl-fme-perf.c @@ -925,6 +925,8 @@ static int fme_perf_pmu_register(struct platform_device *pdev, PERF_PMU_CAP_NO_EXCLUDE; name = devm_kasprintf(priv->dev, GFP_KERNEL, "dfl_fme%d", pdev->id); + if (!name) + return -ENOMEM; ret = perf_pmu_register(pmu, name, -1); if (ret) diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c index 0a295ccf1644a8..b8ec2e6f615fad 100644 --- a/drivers/fpga/stratix10-soc.c +++ b/drivers/fpga/stratix10-soc.c @@ -195,20 +195,18 @@ static int s10_ops_write_init(struct fpga_manager *mgr, ret = s10_svc_send_msg(priv, COMMAND_RECONFIG, &ctype, sizeof(ctype)); if (ret < 0) - goto init_done; + goto init_error; - ret = wait_for_completion_timeout( - &priv->status_return_completion, S10_RECONFIG_TIMEOUT); - if (!ret) { + if (!wait_for_completion_timeout(&priv->status_return_completion, + S10_RECONFIG_TIMEOUT)) { dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n"); ret = -ETIMEDOUT; - goto init_done; + goto init_error; } - ret = 0; if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) { ret = -ETIMEDOUT; - goto init_done; + goto init_error; } /* Allocate buffers from the service layer's pool. */ @@ -217,14 +215,16 @@ static int s10_ops_write_init(struct fpga_manager *mgr, if (IS_ERR(kbuf)) { s10_free_buffers(mgr); ret = PTR_ERR(kbuf); - goto init_done; + goto init_error; } priv->svc_bufs[i].buf = kbuf; priv->svc_bufs[i].lock = 0; } -init_done: + return 0; + +init_error: stratix10_svc_done(priv->chan); return ret; } @@ -342,6 +342,9 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf, if (!s10_free_buffers(mgr)) dev_err(dev, "%s not all buffers were freed\n", __func__); + if (ret < 0) + stratix10_svc_done(priv->chan); + return ret; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index f0f516a79424fe..194e53880eddfb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -1795,6 +1795,12 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu( size >>= 1; aligned_size = PAGE_ALIGN(size); + /* reject AQL queue with size < 2 */ + if (!aligned_size) { + ret = -EINVAL; + goto err_alignment_size; + } + (*mem)->alloc_flags = flags; amdgpu_sync_create(&(*mem)->sync); @@ -1886,6 +1892,7 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu( amdgpu_amdkfd_unreserve_mem_limit(adev, aligned_size, flags, xcp_id); err_reserve_limit: amdgpu_sync_free(&(*mem)->sync); +err_alignment_size: mutex_destroy(&(*mem)->lock); if (gobj) drm_gem_object_put(gobj); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index 45dfe1c8a11b59..a4d79484f6d91a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -298,10 +298,10 @@ amdgpu_devcoredump_print_ibs(struct drm_printer *p, amdgpu_res_first(abo->tbo.resource, offset, coredump->ibs[i].ib_size_dw * 4, &cursor); while (cursor.remaining) { - amdgpu_device_mm_access(adev, cursor.start / 4, - &ib_content[off], cursor.size / 4, + amdgpu_device_mm_access(adev, cursor.start, + &ib_content[off], cursor.size, false); - off += cursor.size; + off += cursor.size / 4; amdgpu_res_next(&cursor, cursor.size); } emit_content = true; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index e84b75c03c6f90..ddfc51a92d79d3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -607,6 +607,13 @@ void amdgpu_device_detect_runtime_pm_mode(struct amdgpu_device *adev) int bamaco_support; adev->pm.rpm_mode = AMDGPU_RUNPM_NONE; + if (pci_is_thunderbolt_attached(adev->pdev) || + dev_is_removable(&adev->pdev->dev)) { + dev_info(adev->dev, + "Runtime PM disabled for externally attached device\n"); + return; + } + bamaco_support = amdgpu_device_supports_baco(adev); switch (amdgpu_runtime_pm) { @@ -2537,7 +2544,11 @@ static int amdgpu_device_ip_init(struct amdgpu_device *adev) if (r) goto init_failed; - amdgpu_ttm_enable_buffer_funcs(adev); + /* If SDMA is not brought up during hwini, the ttm buffer funcs enablement + * is delayed after reset-on-init completes. + */ + if (amdgpu_ip_member_of_hwini(adev, AMD_IP_BLOCK_TYPE_SDMA)) + amdgpu_ttm_enable_buffer_funcs(adev); /* Don't init kfd if whole hive need to be reset during init */ if (adev->init_lvl->level != AMDGPU_INIT_LEVEL_MINIMAL_XGMI) { @@ -3745,6 +3756,14 @@ static void amdgpu_device_sys_interface_fini(struct amdgpu_device *adev) amdgpu_ptl_sysfs_fini(adev); } +static bool +amdgpu_device_should_register_switcheroo(struct amdgpu_device *adev, bool px) +{ + return !pci_is_thunderbolt_attached(adev->pdev) && + (px || (!dev_is_removable(&adev->pdev->dev) && + apple_gmux_detect(NULL, NULL))); +} + /** * amdgpu_device_init - initialize the driver * @@ -4194,8 +4213,7 @@ int amdgpu_device_init(struct amdgpu_device *adev, px = amdgpu_device_supports_px(adev); - if (px || (!dev_is_removable(&adev->pdev->dev) && - apple_gmux_detect(NULL, NULL))) + if (amdgpu_device_should_register_switcheroo(adev, px)) vga_switcheroo_register_client(adev->pdev, &amdgpu_switcheroo_ops, px); @@ -4359,8 +4377,7 @@ void amdgpu_device_fini_sw(struct amdgpu_device *adev) px = amdgpu_device_supports_px(adev); - if (px || (!dev_is_removable(&adev->pdev->dev) && - apple_gmux_detect(NULL, NULL))) + if (amdgpu_device_should_register_switcheroo(adev, px)) vga_switcheroo_unregister_client(adev->pdev); if (px) @@ -5202,6 +5219,31 @@ int amdgpu_device_pre_asic_reset(struct amdgpu_device *adev, amdgpu_fence_driver_force_completion(ring, fence); } + /* + * KIQ rings are polling-fence/no_scheduler like MES, so realign their + * fence too (one ring per XCC), otherwise the first post-reset KIQ + * submission polls forever on a stale seq. + */ + for (i = 0; i < AMDGPU_MAX_GC_INSTANCES; i++) { + struct amdgpu_ring *kiq_ring = &adev->gfx.kiq[i].ring; + + if (kiq_ring->fence_drv.initialized && kiq_ring->sched.ready) + amdgpu_fence_driver_force_completion(kiq_ring, fence); + } + + /* + * MES scheduler rings have no drm scheduler, so they are missed by the + * loop above. Realign their polling fence too (one per XCC), otherwise the + * first post-reset submission polls forever on a stale seq. sched.ready is + * only set while the driver owns the ring. + */ + for (i = 0; i < AMDGPU_MAX_MES_INST_PIPES; i++) { + struct amdgpu_ring *mes_ring = &adev->mes.ring[i]; + + if (mes_ring->fence_drv.initialized && mes_ring->sched.ready) + amdgpu_fence_driver_force_completion(mes_ring, fence); + } + amdgpu_fence_driver_isr_toggle(adev, false); r = amdgpu_reset_prepare_hwcontext(adev, reset_context); @@ -5315,8 +5357,6 @@ int amdgpu_device_reinit_after_reset(struct amdgpu_reset_context *reset_context) if (r) goto out; - amdgpu_ttm_enable_buffer_funcs(tmp_adev); - r = amdgpu_device_ip_resume_phase3(tmp_adev); if (r) goto out; @@ -6826,8 +6866,8 @@ struct dma_fence *amdgpu_device_enforce_isolation(struct amdgpu_device *adev, struct amdgpu_ring *ring, struct amdgpu_job *job) { - struct amdgpu_isolation *isolation = &adev->isolation[ring->xcp_id]; struct drm_sched_fence *f = job->base.s_fence; + struct amdgpu_isolation *isolation; struct dma_fence *dep; void *owner; int r; @@ -6840,6 +6880,9 @@ struct dma_fence *amdgpu_device_enforce_isolation(struct amdgpu_device *adev, ring->funcs->type != AMDGPU_RING_TYPE_COMPUTE) return NULL; + isolation = &adev->isolation[ring->xcp_id == AMDGPU_XCP_NO_PARTITION ? + 0 : ring->xcp_id]; + /* * All submissions where enforce isolation is false are handled as if * they come from a single client. Use ~0l as the owner to distinct it diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 1aed121f4ddb67..e814701bc8fd43 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2548,6 +2548,7 @@ amdgpu_pci_remove(struct pci_dev *pdev) if (adev->pm.rpm_mode != AMDGPU_RUNPM_NONE) { pm_runtime_get_sync(dev->dev); pm_runtime_forbid(dev->dev); + pm_runtime_dont_use_autosuspend(dev->dev); } amdgpu_driver_unload_kms(dev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index 7139cb3b2db5b3..5c5c0fc76aeb84 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -396,6 +396,12 @@ static bool psp_get_runtime_db_entry(struct amdgpu_device *adev, bool ret = false; int i; + /* + * Runtime DB is for dGPUs only. + */ + if (adev->flags & AMD_IS_APU) + return false; + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 6) || amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 12) || amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 14) || diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index de3dbc95e376ca..5ca302e62af7bd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -517,7 +517,8 @@ int amdgpu_uvd_resume(struct amdgpu_device *adev) } memset_io(ptr, 0, size); /* to restore uvd fence seq */ - amdgpu_fence_driver_force_completion(&adev->uvd.inst[i].ring, NULL); + if (adev->uvd.inst[i].ring.fence_drv.initialized) + amdgpu_fence_driver_force_completion(&adev->uvd.inst[i].ring, NULL); } } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 1baad7624f1f98..816800f596cd40 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -773,7 +773,9 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool need_pipe_sync) { struct amdgpu_device *adev = ring->adev; - struct amdgpu_isolation *isolation = &adev->isolation[ring->xcp_id]; + struct amdgpu_isolation *isolation = + &adev->isolation[ring->xcp_id == AMDGPU_XCP_NO_PARTITION ? + 0 : ring->xcp_id]; unsigned vmhub = ring->vm_hub; struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; struct amdgpu_vmid *id = &id_mgr->ids[job->vmid]; @@ -2062,7 +2064,7 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev, after->start = eaddr + 1; after->last = tmp->last; after->offset = tmp->offset; - after->offset += (after->start - tmp->start) << PAGE_SHIFT; + after->offset += (after->start - tmp->start) << AMDGPU_GPU_PAGE_SHIFT; after->flags = tmp->flags; after->bo_va = tmp->bo_va; list_add(&after->list, &tmp->bo_va->invalids); @@ -3109,7 +3111,7 @@ static void amdgpu_debugfs_vm_bo_status_info(struct seq_file *m, id = 0; seq_puts(m, "\tIdle BOs:\n"); - list_for_each_entry(base, &lists->needs_update, vm_status) { + list_for_each_entry(base, &lists->idle, vm_status) { if (!base->bo) continue; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 2a241a5b12c420..ac3f71d77140a5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -918,9 +918,6 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev) struct ttm_resource_manager *man = &mgr->manager; int err; - man->cg = drmm_cgroup_register_region(adev_to_drm(adev), "vram", adev->gmc.real_vram_size); - if (IS_ERR(man->cg)) - return PTR_ERR(man->cg); ttm_resource_manager_init(man, &adev->mman.bdev, adev->gmc.real_vram_size); @@ -935,6 +932,10 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev) if (err) return err; + man->cg = drmm_cgroup_register_region(adev_to_drm(adev), "vram", adev->gmc.real_vram_size); + if (IS_ERR(man->cg)) + return PTR_ERR(man->cg); + ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, &mgr->manager); ttm_resource_manager_set_used(man, true); return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c index e63d05c477a080..adbfdee01e8048 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c @@ -1659,6 +1659,9 @@ static void amdgpu_xgmi_reset_on_init_work(struct work_struct *work) amdgpu_device_unlock_reset_domain(tmp_adev->reset_domain); list_for_each_entry(tmp_adev, &hive->device_list, gmc.xgmi.head) { + /* Enable ttm buffers funcs after the reset lock has been dropped. */ + amdgpu_ttm_enable_buffer_funcs(tmp_adev); + r = amdgpu_ras_init_badpage_info(tmp_adev); if (r && r != -EHWPOISON) dev_err(tmp_adev->dev, diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 3b12eb27a2533e..047f5f891b8a41 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -1646,10 +1646,10 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block) case IP_VERSION(11, 0, 2): case IP_VERSION(11, 0, 3): if (!adev->gfx.disable_uq && - adev->gfx.me_fw_version >= 2420 && - adev->gfx.pfp_fw_version >= 2580 && - adev->gfx.mec_fw_version >= 2650 && - adev->mes.fw_version[0] >= 120) { + adev->gfx.me_fw_version >= 3090 && + adev->gfx.pfp_fw_version >= 3190 && + adev->gfx.mec_fw_version >= 3450 && + adev->mes.fw_version[0] >= 147) { adev->userq_funcs[AMDGPU_HW_IP_GFX] = &userq_mes_funcs; adev->userq_funcs[AMDGPU_HW_IP_COMPUTE] = &userq_mes_funcs; } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index 9d7a75670beee1..e418390353b1b7 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -1434,10 +1434,10 @@ static int gfx_v12_0_sw_init(struct amdgpu_ip_block *ip_block) case IP_VERSION(12, 0, 0): case IP_VERSION(12, 0, 1): if (!adev->gfx.disable_uq && - adev->gfx.me_fw_version >= 2780 && - adev->gfx.pfp_fw_version >= 2840 && - adev->gfx.mec_fw_version >= 3050 && - adev->mes.fw_version[0] >= 123) { + adev->gfx.me_fw_version >= 3090 && + adev->gfx.pfp_fw_version >= 3190 && + adev->gfx.mec_fw_version >= 3450 && + adev->mes.fw_version[0] >= 147) { adev->userq_funcs[AMDGPU_HW_IP_GFX] = &userq_mes_funcs; adev->userq_funcs[AMDGPU_HW_IP_COMPUTE] = &userq_mes_funcs; } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 70ba81e6b4d437..180ed00bb95900 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -4533,9 +4533,11 @@ static int gfx_v8_0_mqd_init(struct amdgpu_ring *ring) /* set static priority for a queue/ring */ gfx_v8_0_mqd_set_priority(ring, mqd); tmp = RREG32(mmCP_HQD_QUANTUM); - tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_EN, 1); - tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_SCALE, 1); - tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_DURATION, 10); + if (ring != &adev->gfx.kiq[0].ring) { + tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_EN, 1); + tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_SCALE, 1); + tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_DURATION, 10); + } mqd->cp_hqd_quantum = tmp; /* map_queues packet doesn't need activate the queue, diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c index 81bba3ec2a9378..00d8f35846f22c 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c @@ -1964,9 +1964,13 @@ static int vcn_v3_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, len_dw = msg[1] / 4; num_buffers = msg[2]; - /* Verify that all indices fit within the claimed length. Each index is 4 DWORDs */ - if (num_buffers > len_dw || 6 + num_buffers * 4 > len_dw) { - DRM_ERROR("VCN message has too many buffers!\n"); + /* Verify that all indices fit within the claimed length. + * There are 6 dwords in the header before the first buffer. + * Each buffer has 4 dwords. Any trailing dwords after the + * last buffer are ignored. + */ + if (len_dw < 6 || num_buffers > (len_dw - 6) / 4) { + DRM_ERROR("Invalid VCN message!\n"); r = -EINVAL; goto out; } diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c index 894780669f9cf6..a12652dc3a3802 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c @@ -1880,9 +1880,13 @@ static int vcn_v4_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, len_dw = msg[1] / 4; num_buffers = msg[2]; - /* Verify that all indices fit within the claimed length. Each index is 4 DWORDs */ - if (num_buffers > len_dw || 6 + num_buffers * 4 > len_dw) { - DRM_ERROR("VCN message has too many buffers!\n"); + /* Verify that all indices fit within the claimed length. + * There are 6 dwords in the header before the first buffer. + * Each buffer has 4 dwords. Any trailing dwords after the + * last buffer are ignored. + */ + if (len_dw < 6 || num_buffers > (len_dw - 6) / 4) { + DRM_ERROR("Invalid VCN message!\n"); r = -EINVAL; goto out; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index e1689b3d2add98..6738cd1d4f155d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1200,7 +1200,8 @@ static int kfd_ioctl_alloc_memory_of_gpu(struct file *filep, if (flags & KFD_IOC_ALLOC_MEM_FLAGS_AQL_QUEUE_MEM) size >>= 1; - atomic64_add(PAGE_ALIGN(size), &pdd->vram_usage); + size = PAGE_ALIGN(size); + atomic64_add(size, &pdd->vram_usage); } mutex_unlock(&p->mutex); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 67137679a901b7..8b522fede60ae4 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -796,6 +796,11 @@ static int create_queue_nocpsch(struct device_queue_manager *dqm, mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type( q->properties.type)]; + if (qd && !mqd_mgr->restore_mqd) { + pr_debug("restore_mqd not implemented for this GPU\n"); + retval = -EOPNOTSUPP; + goto deallocate_vmid; + } if (q->properties.type == KFD_QUEUE_TYPE_COMPUTE) { retval = allocate_hqd(dqm, q); if (retval) @@ -1375,6 +1380,14 @@ static int evict_process_queues_cpsch(struct device_queue_manager *dqm, } } + /* + * Heavy-weight TLB flush after MES removes queues to ensure + * in-flight memory accesses complete before memory is freed/migrated. + * HWS does this automatically, MES does not. + */ + if (dqm->dev->kfd->shared_resources.enable_mes) + kfd_flush_tlb(pdd); + if (!dqm->dev->kfd->shared_resources.enable_mes) { pdd->last_evict_timestamp = get_jiffies_64(); retval = execute_queues_cpsch(dqm, @@ -2154,6 +2167,11 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q, mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type( q->properties.type)]; + if (qd && !mqd_mgr->restore_mqd) { + pr_debug("restore_mqd not implemented for this GPU\n"); + retval = -EOPNOTSUPP; + goto out_deallocate_doorbell; + } if (q->properties.type == KFD_QUEUE_TYPE_SDMA || q->properties.type == KFD_QUEUE_TYPE_SDMA_XGMI) @@ -3651,8 +3669,11 @@ int suspend_queues(struct kfd_process *p, if (!per_device_suspended) { dqm_unlock(dqm); mutex_unlock(&p->event_mutex); - if (total_suspended) + if (total_suspended) { amdgpu_amdkfd_debug_mem_fence(dqm->dev->adev); + /* Heavy-weight TLB flush after MES suspends queues */ + kfd_flush_tlb(pdd); + } continue; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index 7cd236c1ff75fe..f0dc0c00de822c 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -134,7 +134,7 @@ svm_migrate_copy_memory_gart(struct amdgpu_device *adev, dma_addr_t *sys, u64 gart_s, gart_d; struct dma_fence *next; u64 size; - int r; + int r = 0; ring = to_amdgpu_ring(adev->mman.buffer_funcs_scheds[0]); entity = &adev->mman.move_entities[0]; @@ -258,15 +258,6 @@ svm_migrate_get_sys_page(struct vm_area_struct *vma, unsigned long addr) return page; } -static void svm_migrate_put_sys_page(unsigned long addr) -{ - struct page *page; - - page = pfn_to_page(addr >> PAGE_SHIFT); - unlock_page(page); - put_page(page); -} - static unsigned long svm_migrate_successful_pages(struct migrate_vma *migrate) { unsigned long mpages = 0; @@ -293,7 +284,7 @@ svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, dma_addr_t *src; u64 *dst; u64 i, j; - int r; + int r = 0; pr_debug("svms 0x%p [0x%lx 0x%lx 0x%llx]\n", prange->svms, prange->start, prange->last, ttm_res_offset); @@ -319,6 +310,7 @@ svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, DMA_BIDIRECTIONAL); r = dma_mapping_error(dev, src[i]); if (r) { + src[i] = 0; dev_err(dev, "%s: fail %d dma_map_page\n", __func__, r); goto out_free_vram_pages; @@ -343,7 +335,8 @@ svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, pr_debug_ratelimited("dma mapping src to 0x%llx, pfn 0x%lx\n", src[i] >> PAGE_SHIFT, page_to_pfn(spage)); - if (j >= (cursor.size >> PAGE_SHIFT) - 1 && i < npages - 1) { + /* accumulated j + 1 pages reach end of current drm_buddy_block */ + if (j + 1 >= (cursor.size >> PAGE_SHIFT)) { r = svm_migrate_copy_memory_gart(adev, src + i - j, dst + i - j, j + 1, FROM_RAM_TO_VRAM, @@ -357,7 +350,8 @@ svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, } } - r = svm_migrate_copy_memory_gart(adev, src + i - j, dst + i - j, j, + if (j > 0) + r = svm_migrate_copy_memory_gart(adev, src + i - j, dst + i - j, j, FROM_RAM_TO_VRAM, mfence); out_free_vram_pages: @@ -591,9 +585,10 @@ svm_migrate_copy_to_ram(struct amdgpu_device *adev, struct svm_range *prange, dma_addr_t *scratch, u64 npages) { struct device *dev = adev->dev; - u64 *src; + struct page *dpage = NULL; dma_addr_t *dst; - struct page *dpage; + u64 *src; + u64 i = 0, j; u64 addr; int r = 0; @@ -647,6 +642,7 @@ svm_migrate_copy_to_ram(struct amdgpu_device *adev, struct svm_range *prange, r = dma_mapping_error(dev, dst[i]); if (r) { dev_err(adev->dev, "%s: fail %d dma_map_page\n", __func__, r); + dst[i] = 0; goto out_oom; } @@ -654,17 +650,39 @@ svm_migrate_copy_to_ram(struct amdgpu_device *adev, struct svm_range *prange, dst[i] >> PAGE_SHIFT, page_to_pfn(dpage)); migrate->dst[i] = migrate_pfn(page_to_pfn(dpage)); + + dpage = NULL; j++; } - r = svm_migrate_copy_memory_gart(adev, dst + i - j, src + i - j, j, - FROM_VRAM_TO_RAM, mfence); - + if (j > 0) + r = svm_migrate_copy_memory_gart(adev, dst + i - j, src + i - j, j, + FROM_VRAM_TO_RAM, mfence); out_oom: if (r) { pr_debug("failed %d copy to ram\n", r); + + /* first release current dpage when dma_map_page fail */ + if (dpage) { + unlock_page(dpage); + put_page(dpage); + } + + /* release previous allocated sys pages and unmap dma address */ while (i--) { - svm_migrate_put_sys_page(dst[i]); + + if (dst[i]) { + dma_unmap_page(dev, dst[i], PAGE_SIZE, + DMA_BIDIRECTIONAL); + dst[i] = 0; + } + + dpage = migrate_pfn_to_page(migrate->dst[i]); + if (!dpage) + continue; + + unlock_page(dpage); + put_page(dpage); migrate->dst[i] = 0; } } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index 503176f0020405..236089aef75a6d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -1148,13 +1148,13 @@ int pqm_debugfs_mqds(struct seq_file *m, void *data) mqd_mgr = q->device->dqm->mqd_mgrs[mqd_type]; size = mqd_mgr->mqd_stride(mqd_mgr, &q->properties); - } - for (xcc = 0; xcc < num_xccs; xcc++) { - mqd = q->mqd + size * xcc; - r = mqd_mgr->debugfs_show_mqd(m, mqd); - if (r != 0) - break; + for (xcc = 0; xcc < num_xccs; xcc++) { + mqd = q->mqd + size * xcc; + r = mqd_mgr->debugfs_show_mqd(m, mqd); + if (r != 0) + break; + } } } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 941c1a31282498..f059bed728f0ad 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3511,6 +3511,9 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block) res = amdgpu_dm_commit_zero_streams(dm->dc); if (res != DC_OK) { drm_err(adev_to_drm(adev), "Failed to commit zero streams: %d\n", res); + dc_state_release(dm->cached_dc_state); + dm->cached_dc_state = NULL; + mutex_unlock(&dm->dc_lock); return -EINVAL; } @@ -3824,6 +3827,9 @@ static int dm_resume(struct amdgpu_ip_block *ip_block) r = dm_dmub_hw_init(adev); if (r) { drm_err(adev_to_drm(adev), "DMUB interface failed to initialize: status=%d\n", r); + dc_state_release(dm->cached_dc_state); + dm->cached_dc_state = NULL; + mutex_unlock(&dm->dc_lock); return r; } @@ -7846,6 +7852,39 @@ amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force) return status; } +/* + * Apple Studio Display exposes two SST DP links for a 2x1 tiled panel. + * The primary tile advertises the full 5120x2880 mode (with DSC on the + * bandwidth-sufficient link) while the secondary carries a per-tile + * 2560x2880 timing on a insufficient bandwidth link. Hide the secondary + * connector from userspace so compositors configure a single 5K stream + * on the primary link only. + */ +static bool amdgpu_dm_hide_secondary_tile_from_userspace(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + + if (!aconnector->dc_sink) + return false; + + if (!aconnector->dc_sink->edid_caps.panel_patch.disable_second_tile) + return false; + + drm_edid_connector_update(connector, aconnector->drm_edid); + + if (!connector->has_tile) + return false; + + if (!connector->tile_h_loc && !connector->tile_v_loc) + return false; + + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] hiding secondary Apple Studio Display tile from userspace\n", + connector->base.id, connector->name); + + return true; +} + /** * amdgpu_dm_connector_detect() - Detect whether a DRM connector is connected to a display * @@ -7889,6 +7928,9 @@ amdgpu_dm_connector_detect(struct drm_connector *connector, bool force) (!aconnector->dc_sink || aconnector->dc_sink->edid_caps.analog)) return amdgpu_dm_connector_poll(aconnector, force); + if (amdgpu_dm_hide_secondary_tile_from_userspace(connector)) + return connector_status_disconnected; + return (aconnector->dc_sink ? connector_status_connected : connector_status_disconnected); } @@ -9080,6 +9122,47 @@ static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder, } } +/* + * The Apple Studio Display primary tile advertises both the full 5120x2880 + * mode and the per-tile 2560x2880 timing. As the secondary tile is hidden from + * userspace (see amdgpu_dm_hide_secondary_tile_from_userspace()), drop the + * per-tile timing from the primary connector so compositors only pick the full + * 5K mode. + */ +static void amdgpu_dm_prune_primary_tile_modes(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + struct drm_display_mode *mode, *t; + + if (!aconnector->dc_sink) + return; + + if (!aconnector->dc_sink->edid_caps.panel_patch.disable_second_tile) + return; + + if (!connector->has_tile) + return; + + /* Only prune the per-tile timing from the primary tile. */ + if (connector->tile_h_loc || connector->tile_v_loc) + return; + + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { + if (mode->hdisplay != connector->tile_h_size || + mode->vdisplay != connector->tile_v_size) + continue; + + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] pruning per-tile %dx%d timing from primary Apple Studio Display tile\n", + connector->base.id, connector->name, + mode->hdisplay, mode->vdisplay); + + list_del(&mode->head); + drm_mode_destroy(connector->dev, mode); + aconnector->num_modes--; + } +} + static void amdgpu_set_panel_orientation(struct drm_connector *connector) { struct drm_encoder *encoder; @@ -9122,6 +9205,8 @@ static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, amdgpu_dm_connector->num_modes = drm_edid_connector_add_modes(connector); + amdgpu_dm_prune_primary_tile_modes(connector); + /* sorting the probed modes before calling function * amdgpu_dm_get_native_mode() since EDID can have * more than one preferred mode. The modes that are diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c index 86086d10c5435a..46a74f8954303e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c @@ -470,6 +470,12 @@ bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size) uint32_t expected; int delta; + /* A LUT with fewer than two entries can't be interpolated and would + * divide by zero below (size - 1); it can't be treated as linear. + */ + if (size < 2) + return false; + for (i = 0; i < size; i++) { /* All color values should equal */ if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue)) @@ -1467,6 +1473,13 @@ __set_dm_plane_degamma(struct drm_plane_state *plane_state, degamma_lut = __extract_blob_lut(dm_plane_state->degamma_lut, °amma_size); + if (degamma_lut && degamma_size != MAX_COLOR_LUT_ENTRIES) { + drm_dbg(plane_state->state->dev, + "Invalid Plane Degamma LUT size. Should be %u but got %u.\n", + MAX_COLOR_LUT_ENTRIES, degamma_size); + return -EINVAL; + } + has_degamma_lut = degamma_lut && !__is_lut_linear(degamma_lut, degamma_size); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c index 4c164ae4a4f9c3..8304e8897a3eda 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c @@ -535,15 +535,27 @@ static bool enable_assr(void *handle, struct dc_link *link) static void update_config(void *handle, struct cp_psp_stream_config *config) { struct hdcp_workqueue *hdcp_work = handle; - struct amdgpu_dm_connector *aconnector = config->dm_stream_ctx; - int link_index = aconnector->dc_link->link_index; - unsigned int conn_index = aconnector->base.index; - struct mod_hdcp_display *display = &hdcp_work[link_index].display; - struct mod_hdcp_link *link = &hdcp_work[link_index].link; - struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index]; + struct amdgpu_dm_connector *aconnector; + const struct dc *dc; + int link_index; + unsigned int conn_index; + struct mod_hdcp_display *display; + struct mod_hdcp_link *link; + struct hdcp_workqueue *hdcp_w; struct dc_sink *sink = NULL; bool link_is_hdcp14 = false; - const struct dc *dc = aconnector->dc_link->dc; + + aconnector = config->dm_stream_ctx; + if (!aconnector || !aconnector->dc_link) + return; + + link_index = aconnector->dc_link->link_index; + display = &hdcp_work[link_index].display; + link = &hdcp_work[link_index].link; + hdcp_w = &hdcp_work[link_index]; + + conn_index = aconnector->base.index; + dc = aconnector->dc_link->dc; if (config->dpms_off) { hdcp_remove_display(hdcp_work, link_index, aconnector); @@ -581,7 +593,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) link->dp.mst_enabled = config->mst_enabled; link->dp.dp2_enabled = config->dp2_enabled; link->dp.usb4_enabled = config->usb4_enabled; - if (aconnector->dc_sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) + if (sink && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) link->hdmi.frl_enabled = config->frl_enabled; display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION; link->adjust.auth_delay = 2; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index 6be7f6edd0b293..41aa8f9bb152bb 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -134,6 +134,17 @@ static void apply_edid_quirks(struct dc_link *link, struct edid *edid, drm_dbg_driver(dev, "Skip PHY SSC reduction on panel id %X\n", panel_id); link->wa_flags.skip_phy_ssc_reduction = true; break; + /* + * Workaround for Apple Studio Display which exposes a 2x1 tiled panel + * over two SST DP links. Hide the secondary tile from userspace so + * compositors drive a single 5K stream on the primary link only. + */ + case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE3A): + case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE42): + case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE46): + drm_dbg_driver(dev, "Hiding secondary tile on panel id %X\n", panel_id); + edid_caps->panel_patch.disable_second_tile = true; + break; default: return; } diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index 4ed1efa172709a..6482e275e63bf6 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -187,6 +187,7 @@ struct dc_panel_patch { unsigned int skip_audio_sab_check; unsigned int mst_start_top_delay; unsigned int remove_sink_ext_caps; + bool disable_second_tile; unsigned int disable_colorimetry; uint8_t blankstream_before_otg_off; bool oled_optimize_display_on; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h index 11e295253f72e8..51725ca16a28fe 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h @@ -1748,8 +1748,8 @@ struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_param bool UnboundedRequestEnabled; unsigned int CompressedBufferSizeInkByte; bool max_outstanding_when_urgent_expected; - const unsigned int max_outstanding_requests; - const unsigned int max_request_size_bytes; + unsigned int max_outstanding_requests; + unsigned int max_request_size_bytes; const unsigned int *meta_row_height_l; const unsigned int *meta_row_height_c; const enum dml2_pstate_method *uclk_pstate_switch_modes; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c index 166f10b8862f14..c82886323a5182 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c @@ -301,6 +301,7 @@ void dml2_init_socbb_params(struct dml2_context *dml2, const struct dc *in_dc, s out->smn_latency_us = 2; out->dispclk_dppclk_vco_speed_mhz = 3600; out->pct_ideal_dram_bw_after_urgent_pixel_only = 65.0; + out->gpuvm_min_page_size_kbytes = 4; break; diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c index b6262e43ca02a8..4c27052b62f733 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c @@ -632,7 +632,7 @@ static bool construct_phy(struct dc_link *link, DC_LOG_DC("BIOS object table - hpd_gpio id: %d", enc_init_data.hpd_gpio->id); DC_LOG_DC("BIOS object table - hpd_gpio en: %d", enc_init_data.hpd_gpio->en); - } else { + } else if (link->ctx->dce_version > DCN_VERSION_4_01) { struct graphics_object_hpd_info hpd_info; if (link->ctx->dc_bios->funcs->get_hpd_info(link->ctx->dc_bios, link->link_id, &hpd_info) == BP_RESULT_OK) { diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c index dd854d9926925b..f43fc4b78a8d32 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c @@ -103,6 +103,11 @@ static int get_estimated_bw(struct dc_link *link) { uint8_t bw_estimated_bw = 0; + if (link->dpia_bw_alloc_config.bw_granularity == 0) { + DC_LOG_ERROR("%s: BW granularity is zero!\n", __func__); + return 0; + } + core_link_read_dpcd( link, ESTIMATED_BW, diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 17d5caedb32e0a..9eb6cf8a321f25 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -3389,7 +3389,8 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, break; default: dev_err(dev, "reg-io-width must be 1 or 4\n"); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto err_res; } iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index e997917819e8ea..7b951af5ab3206 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -1563,6 +1563,8 @@ static void complete_signaling(struct drm_device *dev, * to prevent a double free in drm_atomic_commit_clear. */ if (event && (event->base.fence || event->base.file_priv)) { + if (crtc_state->commit && crtc_state->commit->abort_completion) + drm_crtc_commit_put(crtc_state->commit); drm_event_cancel_free(dev, &event->base); crtc_state->event = NULL; } diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 675675480da49e..e890052061f30e 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -473,6 +473,22 @@ void drm_dev_exit(int idx) } EXPORT_SYMBOL(drm_dev_exit); +/* + * Mark the device as unplugged and wait for any in-flight drm_dev_enter() + * critical sections to complete. + */ +static void drm_dev_synchronize_unplug(struct drm_device *dev) +{ + /* + * After synchronizing any critical read section is guaranteed to see + * the new value of ->unplugged, and any critical section which might + * still have seen the old value of ->unplugged is guaranteed to have + * finished. + */ + dev->unplugged = true; + synchronize_srcu(&drm_unplug_srcu); +} + /** * drm_dev_unplug - unplug a DRM device * @dev: DRM device @@ -485,15 +501,7 @@ EXPORT_SYMBOL(drm_dev_exit); */ void drm_dev_unplug(struct drm_device *dev) { - /* - * After synchronizing any critical read section is guaranteed to see - * the new value of ->unplugged, and any critical section which might - * still have seen the old value of ->unplugged is guaranteed to have - * finished. - */ - dev->unplugged = true; - synchronize_srcu(&drm_unplug_srcu); - + drm_dev_synchronize_unplug(dev); drm_dev_unregister(dev); /* Clear all CPU mappings pointing to this device */ @@ -1091,6 +1099,7 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) goto err_minors; dev->registered = true; + dev->unplugged = false; if (driver->load) { ret = driver->load(dev, flags); @@ -1118,6 +1127,13 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) if (dev->driver->unload) dev->driver->unload(dev); err_minors: + /* + * If a minor was registered before the failure, userspace could have + * opened it and entered a drm_dev_enter() critical section. Ensure all + * such sections complete before we clean up. + */ + drm_dev_synchronize_unplug(dev); + remove_compat_control_link(dev); drm_minor_unregister(dev, DRM_MINOR_ACCEL); drm_minor_unregister(dev, DRM_MINOR_PRIMARY); diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index 892b325fa99b8f..05eb7254028f76 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -383,6 +383,58 @@ drm_pagemap_migrate_map_system_pages(struct device *dev, return 0; } +/** + * drm_pagemap_migrate_populate_src_pages() - Populate the source page array + * @pages: Array of source pages to populate + * @src_mpfn: Source array of migrate PFNs + * @dst_mpfn: Destination array of migrate PFNs + * @npages: Number of pages in the arrays + * + * Populate @pages with the device pages the copy callback is to read from. + * + * Entries are normally only populated at the head of each source folio, with + * the copy callback deriving the rest of the folio from the order recorded in + * the corresponding drm_pagemap_addr. That does not work where + * drm_pagemap_migrate_populate_ram_pfn() had to demote a higher-order source + * folio to order-0 destination folios: the drm_pagemap_addr entries are then + * per-page, and the copy callback needs a source page for each of them. + * Populate every entry for those ranges. + * + * Note that the source folio itself is only split later, by + * migrate_vma_pages() / migrate_device_pages(), so its order cannot be used to + * detect the demotion - the destination has to be inspected instead. + */ +static void drm_pagemap_migrate_populate_src_pages(struct page **pages, + unsigned long *src_mpfn, + unsigned long *dst_mpfn, + unsigned long npages) +{ + unsigned long i; + + for (i = 0; i < npages;) { + struct page *page = migrate_pfn_to_page(src_mpfn[i]); + unsigned int order = 0; + unsigned long j, nr; + + if (!page) { + i++; + continue; + } + + order = folio_order(page_folio(page)); + nr = NR_PAGES(order); + + if (order && !(dst_mpfn[i] & MIGRATE_PFN_COMPOUND)) { + for (j = 0; j < nr && i + j < npages; j++) + pages[i + j] = folio_page(page_folio(page), j); + } else { + pages[i] = page; + } + + i += nr; + } +} + /** * drm_pagemap_migrate_unmap_pages() - Unmap pages previously mapped for GPU SVM migration * @dev: The device for which the pages were mapped @@ -875,6 +927,7 @@ static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas, struct page *page = NULL, *src_page; struct folio *folio; unsigned int order = 0; + gfp_t gfp = GFP_HIGHUSER; if (!(src_mpfn[i] & MIGRATE_PFN_MIGRATE)) goto next; @@ -891,11 +944,51 @@ static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas, order = folio_order(page_folio(src_page)); - /* TODO: Support fallback to single pages if THP allocation fails */ + /* + * A large source folio is always collected whole, at its head + * page, PMD aligned and flagged MIGRATE_PFN_COMPOUND: anything + * else is split before it reaches us, either by + * migrate_vma_collect_pmd() or, for the eviction path, by + * migrate_device_pfns(). Both the order-0 fallback below and + * drm_pagemap_migrate_populate_src_pages() rely on that, as + * they index the folio from @i. + */ + WARN_ON_ONCE(order && + (src_page != folio_page(page_folio(src_page), 0) || + !(src_mpfn[i] & MIGRATE_PFN_COMPOUND))); + + if (order) + gfp |= __GFP_NOWARN; + if (vas) - folio = vma_alloc_folio(GFP_HIGHUSER, order, vas, addr); + folio = vma_alloc_folio(gfp, order, vas, addr); else - folio = folio_alloc(GFP_HIGHUSER, order); + folio = folio_alloc(gfp, order); + + if (!folio && order) { + /* + * Higher-order allocation failed, fall back to + * order-0 allocations for the entire range covered + * by the original higher-order allocation, without + * setting MIGRATE_PFN_COMPOUND, until we move past + * that range. + */ + unsigned long nr = NR_PAGES(order); + unsigned long j; + + gfp &= ~__GFP_NOWARN; + for (j = 0; j < nr && i < npages; j++, i++, addr += PAGE_SIZE) { + folio = vas ? + vma_alloc_folio(gfp, 0, vas, addr) : + folio_alloc(gfp, 0); + if (!folio) + goto free_pages; + + page = folio_page(folio, 0); + mpfn[i] = migrate_pfn(page_to_pfn(page)); + } + continue; + } if (!folio) goto free_pages; @@ -940,11 +1033,11 @@ static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas, if (!page) goto next_put; + order = folio_order(page_folio(page)); + put_page(page); mpfn[i] = 0; - order = folio_order(page_folio(page)); - next_put: i += NR_PAGES(order); } @@ -1120,7 +1213,7 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation) unsigned long *src, *dst; struct drm_pagemap_addr *pagemap_addr; void *buf; - int i, err = 0; + int err = 0; unsigned int retry_count = 2; npages = devmem_allocation->size >> PAGE_SHIFT; @@ -1160,15 +1253,7 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation) if (err) goto err_finalize; - for (i = 0; i < npages;) { - unsigned int order = 0; - - pages[i] = migrate_pfn_to_page(src[i]); - if (pages[i]) - order = folio_order(page_folio(pages[i])); - - i += NR_PAGES(order); - } + drm_pagemap_migrate_populate_src_pages(pages, src, dst, npages); err = ops->copy_to_ram(pages, pagemap_addr, npages, NULL); if (err) @@ -1235,7 +1320,7 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas, struct drm_pagemap_addr *pagemap_addr; unsigned long start, end; void *buf; - int i, err = 0; + int err = 0; zdd = drm_pagemap_page_zone_device_data(page); if (time_before64(get_jiffies_64(), zdd->devmem_allocation->timeslice_expiration)) @@ -1290,15 +1375,8 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas, if (err) goto err_finalize; - for (i = 0; i < npages;) { - unsigned int order = 0; - - pages[i] = migrate_pfn_to_page(migrate.src[i]); - if (pages[i]) - order = folio_order(page_folio(pages[i])); - - i += NR_PAGES(order); - } + drm_pagemap_migrate_populate_src_pages(pages, migrate.src, migrate.dst, + npages); err = ops->copy_to_ram(pages, pagemap_addr, npages, NULL); if (err) diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c index ea0cca58b7c892..8141c3a1e30ad9 100644 --- a/drivers/gpu/drm/gud/gud_connector.c +++ b/drivers/gpu/drm/gud/gud_connector.c @@ -396,8 +396,16 @@ static int gud_connector_add_tv_mode(struct gud_device *gdrm, struct drm_connect } num_modes = ret / GUD_CONNECTOR_TV_MODE_NAME_LEN; - for (i = 0; i < num_modes; i++) - modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN]; + for (i = 0; i < num_modes; i++) { + char *mode = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN]; + + if (!memchr(mode, '\0', GUD_CONNECTOR_TV_MODE_NAME_LEN)) { + ret = -EIO; + goto free; + } + + modes[i] = mode; + } ret = drm_mode_create_tv_properties_legacy(connector->dev, num_modes, modes); free: diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c index 3066dc9ebc6472..2e6e189bec1a09 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c @@ -72,46 +72,28 @@ static int hibmc_get_best_clock_idx(const struct drm_display_mode *mode) static int hibmc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_commit *state) { - struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, - plane); - struct drm_framebuffer *fb = new_plane_state->fb; - struct drm_crtc *crtc = new_plane_state->crtc; - struct drm_crtc_state *crtc_state; - u32 src_w = new_plane_state->src_w >> 16; - u32 src_h = new_plane_state->src_h >> 16; - - if (!crtc || !fb) - return 0; + struct drm_plane_state *new_plane_state = + drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc_state *new_crtc_state = NULL; + int ret; - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); + if (new_plane_state->crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc); - if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) { - drm_dbg_atomic(plane->dev, "scale not support\n"); - return -EINVAL; - } - - if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) { - drm_dbg_atomic(plane->dev, "crtc_x/y of drm_plane state is invalid\n"); - return -EINVAL; - } - - if (!crtc_state->enable) + ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, true); + if (ret) + return ret; + else if (!new_plane_state->visible) return 0; - if (new_plane_state->crtc_x + new_plane_state->crtc_w > - crtc_state->adjusted_mode.hdisplay || - new_plane_state->crtc_y + new_plane_state->crtc_h > - crtc_state->adjusted_mode.vdisplay) { - drm_dbg_atomic(plane->dev, "visible portion of plane is invalid\n"); - return -EINVAL; - } - if (new_plane_state->fb->pitches[0] % 128 != 0) { drm_dbg_atomic(plane->dev, "wrong stride with 128-byte aligned\n"); return -EINVAL; } + return 0; } @@ -153,10 +135,8 @@ static void hibmc_plane_atomic_update(struct drm_plane *plane, } static const u32 channel_formats1[] = { - DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888, - DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, - DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888, - DRM_FORMAT_ABGR8888 + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, }; static const struct drm_plane_funcs hibmc_plane_funcs = { diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index 97b6c7c533b74c..a2f83bcd0ab3ef 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -530,13 +530,18 @@ static int i9xx_check_cursor(struct intel_crtc_state *crtc_state, } static void i9xx_cursor_disable_sel_fetch_arm(struct intel_dsb *dsb, - struct intel_plane *plane, - const struct intel_crtc_state *crtc_state) + struct intel_plane *plane) { struct intel_display *display = to_intel_display(plane); enum pipe pipe = plane->pipe; - if (!crtc_state->enable_psr2_sel_fetch) + /* + * Clear this whenever the hardware has selective fetch, not just when + * the current state uses it. The cursor may have been enabled with + * selective fetch earlier and had its enable bit orphaned when the + * feature was switched off. + */ + if (!HAS_PSR2_SEL_FETCH(display)) return; intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), 0); @@ -586,7 +591,7 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb, if (crtc_state->enable_psr2_su_region_et) wa_16021440873(dsb, plane, crtc_state, plane_state); else - i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state); + i9xx_cursor_disable_sel_fetch_arm(dsb, plane); } } @@ -695,7 +700,7 @@ static void i9xx_cursor_update_arm(struct intel_dsb *dsb, if (plane_state) i9xx_cursor_update_sel_fetch_arm(dsb, plane, crtc_state, plane_state); else - i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state); + i9xx_cursor_disable_sel_fetch_arm(dsb, plane); if (plane->cursor.base != base || plane->cursor.size != fbc_ctl || diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c index 164b7d61c9a313..4369c8eeaac25a 100644 --- a/drivers/gpu/drm/i915/display/skl_universal_plane.c +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c @@ -879,13 +879,18 @@ skl_plane_disable_arm(struct intel_dsb *dsb, } static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb, - struct intel_plane *plane, - const struct intel_crtc_state *crtc_state) + struct intel_plane *plane) { struct intel_display *display = to_intel_display(plane); enum pipe pipe = plane->pipe; - if (!crtc_state->enable_psr2_sel_fetch) + /* + * Clear this whenever the hardware has selective fetch, not just when + * the current state uses it. The plane may have been enabled with + * selective fetch earlier and had its enable bit orphaned when the + * feature was switched off. + */ + if (!HAS_PSR2_SEL_FETCH(display)) return; intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0); @@ -921,7 +926,7 @@ icl_plane_disable_arm(struct intel_dsb *dsb, skl_write_plane_wm(dsb, plane, crtc_state); - icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state); + icl_plane_disable_sel_fetch_arm(dsb, plane); if (plane_has_normalizer(plane)) intel_de_write_dsb(display, dsb, @@ -1641,7 +1646,7 @@ static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb, intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), SEL_FETCH_PLANE_CTL_ENABLE); else - icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state); + icl_plane_disable_sel_fetch_arm(dsb, plane); } static void diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index 82415af47d5408..2f03f95945f1b9 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -958,6 +958,9 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) (struct intel_device_info *) ent->driver_data; int err; + if (!intel_info) + return -ENODEV; + if (intel_info->require_force_probe && !id_forced(pdev->device)) { dev_info(&pdev->dev, "Your graphics device %04x is not properly supported by i915 in this\n" diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index d4727836052c88..2685cc3d035905 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -668,12 +668,24 @@ static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi) int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi) { + long rounded_byte_clk_rate; + if (!msm_host->mode) { pr_err("%s: mode not set\n", __func__); return -EINVAL; } dsi_calc_pclk(msm_host, is_bonded_dsi); + + rounded_byte_clk_rate = clk_round_rate(msm_host->byte_clk, + msm_host->byte_clk_rate); + if (rounded_byte_clk_rate < 0) { + pr_err("%s: failed to round byte clock rate, %ld\n", + __func__, rounded_byte_clk_rate); + return rounded_byte_clk_rate; + } + + msm_host->byte_clk_rate = rounded_byte_clk_rate; msm_host->esc_clk_rate = clk_get_rate(msm_host->esc_clk); return 0; } diff --git a/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h b/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h index 7903d7470d1942..01145db32c5346 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h @@ -87,4 +87,5 @@ int gp102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct int gv100_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **); int tu102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **); int ga102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **); +int gb202_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **); #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index 598513f6044930..f142f631059666 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -90,6 +90,14 @@ nouveau_channel_del(struct nouveau_channel **pchan) { struct nouveau_channel *chan = *pchan; if (chan) { + /* + * Drop the kill-event subscription first. Its handler + * dereferences chan->fence, which the fence context teardown + * below frees, so leaving it armed across the teardown leaves + * a window for a use-after-free. + */ + nvif_event_dtor(&chan->kill); + if (chan->fence) nouveau_fence(chan->cli->drm)->context_del(chan); @@ -100,7 +108,6 @@ nouveau_channel_del(struct nouveau_channel **pchan) nvif_object_dtor(&chan->nvsw); nvif_object_dtor(&chan->gart); nvif_object_dtor(&chan->vram); - nvif_event_dtor(&chan->kill); nvif_object_dtor(&chan->user); nvif_mem_dtor(&chan->mem_userd); nouveau_vma_del(&chan->sema.vma); diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c index 9442ec6e1f6c13..ad4570c50be70c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dmem.c +++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c @@ -267,7 +267,7 @@ static vm_fault_t nouveau_dmem_migrate_to_ram(struct vm_fault *vmf) nouveau_fence_new(&fence, dmem->migrate.chan); migrate_vma_pages(&args); nouveau_dmem_fence_done(&fence); - dma_unmap_page(drm->dev->dev, dma_info.dma_addr, PAGE_SIZE, + dma_unmap_page(drm->dev->dev, dma_info.dma_addr, dma_info.size, DMA_BIDIRECTIONAL); done: migrate_vma_finalize(&args); @@ -279,11 +279,25 @@ static vm_fault_t nouveau_dmem_migrate_to_ram(struct vm_fault *vmf) static void nouveau_dmem_folio_split(struct folio *head, struct folio *tail) { + struct nouveau_dmem_chunk *chunk; + struct nouveau_dmem *dmem; + if (tail == NULL) return; tail->pgmap = head->pgmap; tail->mapping = head->mapping; folio_set_zone_device_data(tail, folio_zone_device_data(head)); + + /* + * The split hands out a new independently-freeable folio that will + * later be released via nouveau_dmem_folio_free(); account for it so + * chunk->callocated stays balanced. + */ + chunk = nouveau_page_to_chunk(&head->page); + dmem = chunk->drm->dmem; + spin_lock(&dmem->lock); + chunk->callocated++; + spin_unlock(&dmem->lock); } static const struct dev_pagemap_ops nouveau_dmem_pagemap_ops = { @@ -772,7 +786,7 @@ static unsigned long nouveau_dmem_migrate_copy_one(struct nouveau_drm *drm, return mpfn; out_dma_unmap: - dma_unmap_page(dev, dma_info->dma_addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + dma_unmap_page(dev, dma_info->dma_addr, dma_info->size, DMA_BIDIRECTIONAL); out_free_page: nouveau_dmem_page_free_locked(drm, dpage); out: diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 20dba02d6175bc..c5a24dff4b69b4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -313,11 +313,20 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem, rep->offset = nvbo->offset; if (vmm->vmm.object.oclass >= NVIF_CLASS_VMM_NV50 && !nouveau_cli_uvmm(cli)) { + int ret; + + ret = ttm_bo_reserve(&nvbo->bo, false, false, NULL); + if (ret) + return ret; + vma = nouveau_vma_find(nvbo, vmm); - if (!vma) + if (!vma) { + ttm_bo_unreserve(&nvbo->bo); return -EINVAL; + } rep->offset = vma->addr; + ttm_bo_unreserve(&nvbo->bo); } else rep->offset = 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c index fa3b4ebf38a830..2bd0376193aeed 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -72,9 +72,7 @@ nouveau_sgdma_create_ttm(struct ttm_buffer_object *bo, uint32_t page_flags) struct nouveau_sgdma_be *nvbe; enum ttm_caching caching; - if (nvbo->force_coherent) - caching = ttm_uncached; - else if (drm->agp.bridge) + if (nvbo->force_coherent || drm->agp.bridge) caching = ttm_write_combined; else caching = ttm_cached; diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c index f5e4756b4de4ae..fc125fd44a9b7e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c @@ -1319,6 +1319,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job, op->va.range); if (!op->reg || op->reg->dirty) { ret = -ENOENT; + op->reg = NULL; goto unwind_continue; } @@ -1327,6 +1328,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job, op->va.range); if (IS_ERR(op->ops)) { ret = PTR_ERR(op->ops); + op->reg = NULL; goto unwind_continue; } @@ -1473,6 +1475,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job, op->va.range); break; case OP_UNMAP_SPARSE: + op->reg->dirty = false; __nouveau_uvma_region_insert(uvmm, op->reg); nouveau_uvmm_sm_unmap_prepare_unwind(uvmm, &op->new, op->ops); @@ -1489,7 +1492,8 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job, break; } - drm_gpuva_ops_free(&uvmm->base, op->ops); + if (!IS_ERR_OR_NULL(op->ops)) + drm_gpuva_ops_free(&uvmm->base, op->ops); op->ops = NULL; op->reg = NULL; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index ea62dc97f11894..96c8a5b299999c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -2846,7 +2846,7 @@ nv1b2_chipset = { .pci = { 0x00000001, gh100_pci_new }, .timer = { 0x00000001, gk20a_timer_new }, .vfn = { 0x00000001, ga100_vfn_new }, - .disp = { 0x00000001, ga102_disp_new }, + .disp = { 0x00000001, gb202_disp_new }, .fifo = { 0x00000001, ga102_fifo_new }, }; @@ -2862,7 +2862,7 @@ nv1b3_chipset = { .pci = { 0x00000001, gh100_pci_new }, .timer = { 0x00000001, gk20a_timer_new }, .vfn = { 0x00000001, ga100_vfn_new }, - .disp = { 0x00000001, ga102_disp_new }, + .disp = { 0x00000001, gb202_disp_new }, .fifo = { 0x00000001, ga102_fifo_new }, }; @@ -2878,7 +2878,7 @@ nv1b5_chipset = { .pci = { 0x00000001, gh100_pci_new }, .timer = { 0x00000001, gk20a_timer_new }, .vfn = { 0x00000001, ga100_vfn_new }, - .disp = { 0x00000001, ga102_disp_new }, + .disp = { 0x00000001, gb202_disp_new }, .fifo = { 0x00000001, ga102_fifo_new }, }; @@ -2894,7 +2894,7 @@ nv1b6_chipset = { .pci = { 0x00000001, gh100_pci_new }, .timer = { 0x00000001, gk20a_timer_new }, .vfn = { 0x00000001, ga100_vfn_new }, - .disp = { 0x00000001, ga102_disp_new }, + .disp = { 0x00000001, gb202_disp_new }, .fifo = { 0x00000001, ga102_fifo_new }, }; @@ -2910,7 +2910,7 @@ nv1b7_chipset = { .pci = { 0x00000001, gh100_pci_new }, .timer = { 0x00000001, gk20a_timer_new }, .vfn = { 0x00000001, ga100_vfn_new }, - .disp = { 0x00000001, ga102_disp_new }, + .disp = { 0x00000001, gb202_disp_new }, .fifo = { 0x00000001, ga102_fifo_new }, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild index e1aecd3fe96c10..98d6ca5ac311e7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild @@ -27,6 +27,7 @@ nvkm-y += nvkm/engine/disp/gp102.o nvkm-y += nvkm/engine/disp/gv100.o nvkm-y += nvkm/engine/disp/tu102.o nvkm-y += nvkm/engine/disp/ga102.o +nvkm-y += nvkm/engine/disp/gb202.o nvkm-y += nvkm/engine/disp/udisp.o nvkm-y += nvkm/engine/disp/uconn.o diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c index ab0a85c9243047..820834b5ee9b2f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c @@ -144,12 +144,23 @@ ga102_disp = { }, }; +static const struct nvkm_disp_func +ga102_gsp_disp = { + .uevent = &gv100_disp_chan_uevent, + .ramht_size = 0x2000, + .gsp.intr = tu102_disp_intr, + .gsp.head = &tu102_gsp_head, + .gsp.hdmi_gcp = tu102_sor_hdmi_gcp, + .gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi, + .gsp.hdmi_infoframe_vsi = gv100_sor_hdmi_infoframe_vsi, +}; + int ga102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_disp **pdisp) { if (nvkm_gsp_rm(device->gsp)) - return r535_disp_new(&ga102_disp, device, type, inst, pdisp); + return r535_disp_new(&ga102_gsp_disp, device, type, inst, pdisp); return nvkm_disp_new_(&ga102_disp, device, type, inst, pdisp); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c new file mode 100644 index 00000000000000..d0360610f9fad3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2026 Valve Corp. + */ +#include "priv.h" +#include "head.h" +#include "ior.h" + +#include + +/* GB20x (NVD5.0) reorganised the SF HDMI packet units. The AVI unit is + * unchanged from GV100, but the legacy VSI unit is gone. Vendor infoframes + * are sent through the shared generic infoframe units instead. Register + * layout per NVIDIA's clc971.h/clca71.h, programming sequence per + * nvhdmipkt_C971.c:programAdvancedInfoframeC971(). + */ +static void +gb202_sor_hdmi_infoframe_vsi(struct nvkm_ior *ior, int head, void *data, u32 size) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 hoff = head * 0x400; + /* Generic infoframe unit 1, the slot NVIDIA's driver uses for the VSI. */ + const u32 ctrl = 0x6f0138 + hoff; + u8 buf[36] = {}; + int i; + + /* Disable the unit and wait for it to go idle. */ + nvkm_mask(device, ctrl, 0x00000001, 0x00000000); + if (nvkm_msec(device, 2000, + if (!(nvkm_rd32(device, ctrl) & 0x00400000)) + break; + ) < 0) + return; + + if (!size) + return; + + /* Clear SENT status, and point the data port at unit 1's slot. */ + nvkm_mask(device, ctrl, 0x00800000, 0x00800000); + nvkm_wr32(device, 0x6f03f0 + hoff, 0x00000001); + + /* The data port takes the raw packet, except that a zero is inserted + * in HB3 after the three header bytes. A slot is 9 dwords (HB0-3 plus + * up to 32 payload bytes). An HDMI infoframe carries at most PB0-27, + * so the tail stays zero, and we always write the whole slot. + */ + size = min_t(u32, size, 31); + memcpy(buf, data, min_t(u32, size, 3)); + if (size > 3) + memcpy(&buf[4], (u8 *)data + 3, size - 3); + + for (i = 0; i < 36; i += 4) { + nvkm_wr32(device, 0x6f03f4 + hoff, buf[i + 0] | buf[i + 1] << 8 | + buf[i + 2] << 16 | + (u32)buf[i + 3] << 24); + } + + /* No flip ID or scanline matching. */ + nvkm_wr32(device, 0x6f013c + hoff, 0x00000000); + + /* ENABLE | RUN_MODE=ALWAYS | LOC=VBLANK | OFFSET=1 | SIZE=0. */ + nvkm_wr32(device, ctrl, 0x00000041); + + /* Audio priority low (the init value). */ + nvkm_wr32(device, 0x6f03f8 + hoff, 0x00000002); +} + +/* General Control Packet AVMute bracket. The GCP unit moved to slot 1 on + * NVD5.0. Only SB0 (the AVMute bit) is ours to write so we must not do a + * full write here: SB1 carries the deep-color CD/PP fields, and SB1_CTRL + * (bit 24, new with clc871.h) controls where their generation happens (HW + * or driver) on these chips, with the default being HW. + */ +static void +gb202_sor_hdmi_gcp(struct nvkm_ior *sor, int head, bool enable) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hdmi = head * 0x400; + + nvkm_mask(device, 0x6f0040 + hdmi, 0x00000001, 0x00000000); + nvkm_mask(device, 0x6f004c + hdmi, 0x000000ff, !enable ? 0x00000001 : + 0x00000010); + nvkm_mask(device, 0x6f0040 + hdmi, 0x00000001, 0x00000001); +} + +/* Same core-channel state mirror as gv100_head_state() (assembly at 0x680000, + * armed at +0x8000, per-head method offsets unchanged), but NVD5.0 spaces + * heads 0x800 apart (see NVCA7D_HEAD_SET_*(a) in clca7d.h). + */ +static void +gb202_head_state(struct nvkm_head *head, struct nvkm_head_state *state) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + const u32 hoff = (state == &head->arm) * 0x8000 + head->id * 0x800; + u32 data; + + data = nvkm_rd32(device, 0x682064 + hoff); + state->vtotal = (data & 0xffff0000) >> 16; + state->htotal = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x682068 + hoff); + state->vsynce = (data & 0xffff0000) >> 16; + state->hsynce = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x68206c + hoff); + state->vblanke = (data & 0xffff0000) >> 16; + state->hblanke = (data & 0x0000ffff); + data = nvkm_rd32(device, 0x682070 + hoff); + state->vblanks = (data & 0xffff0000) >> 16; + state->hblanks = (data & 0x0000ffff); + /* Bit 31 is ADJ1000DIV1001, not a HERTZ bit. We don't have enough bits + * to add the full clock in hz on Blackwell (35 bits), but state->hz + * is unused and obsolete under GSP so this is fine. + */ + state->hz = nvkm_rd32(device, 0x68200c + hoff) & 0x7fffffff; + + data = nvkm_rd32(device, 0x682004 + hoff); + switch ((data & 0x000000f0) >> 4) { + case 5: state->or.depth = 30; break; + case 4: state->or.depth = 24; break; + case 1: state->or.depth = 18; break; + default: + state->or.depth = 18; + WARN_ON(1); + break; + } +} + +/* NVD5.0 (GB20x and later) moved the RM head-timing interrupt enable to + * the low-latency vector's EN1 block. The event latch is unchanged. + */ +static void +gb202_head_vblank_put(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + + nvkm_mask(device, 0x611ef0 + (head->id * 4), 0x00000002, 0x00000000); +} + +static void +gb202_head_vblank_get(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + + nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002); + nvkm_mask(device, 0x611ef0 + (head->id * 4), 0x00000002, 0x00000002); +} + +static irqreturn_t +gb202_disp_intr(struct nvkm_inth *inth) +{ + struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth); + irqreturn_t ret = tu102_disp_intr(inth); + + /* The FE interrupt vectors are message-based on NVD5.0. Re-arm the + * low-latency vector so it fires again for any event that latched + * while we were servicing. + */ + nvkm_wr32(disp->engine.subdev.device, 0x611f34, 0x00000001); + return ret; +} + +static const struct nvkm_head_func +gb202_gsp_head = { + .state = gb202_head_state, + .rgpos = gv100_head_rgpos, + .vblank_get = gb202_head_vblank_get, + .vblank_put = gb202_head_vblank_put, +}; + +/* GB20x is GSP-only. This table supplies the register programming the + * GSP-RM display path needs from the chip. + */ +static const struct nvkm_disp_func +gb202_gsp_disp = { + .uevent = &gv100_disp_chan_uevent, + .ramht_size = 0x2000, + /* Head timing arrives on the dedicated low-latency vector. */ + .gsp.intr = gb202_disp_intr, + .gsp.intr_low_latency = true, + .gsp.head = &gb202_gsp_head, + .gsp.hdmi_gcp = gb202_sor_hdmi_gcp, + /* The legacy AVI unit is unchanged on GB20x. */ + .gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi, + .gsp.hdmi_infoframe_vsi = gb202_sor_hdmi_infoframe_vsi, +}; + +int +gb202_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_disp **pdisp) +{ + return r535_disp_new(&gb202_gsp_disp, device, type, inst, pdisp); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c index dbd984da750147..0608266188d3ea 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c @@ -253,7 +253,7 @@ gv100_head_vblank_get(struct nvkm_head *head) nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000004, 0x00000004); } -static void +void gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline) { struct nvkm_device *device = head->disp->engine.subdev.device; @@ -263,7 +263,7 @@ gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline) *hline = nvkm_rd32(device, 0x616334 + hoff) & 0x0000ffff; } -static void +void gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state) { struct nvkm_device *device = head->disp->engine.subdev.device; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h index 856252bf559a46..5976498da90947 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h @@ -53,6 +53,10 @@ void gf119_head_rgclk(struct nvkm_head *, int); int gv100_head_cnt(struct nvkm_disp *, unsigned long *); int gv100_head_new(struct nvkm_disp *, int id); +void gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state); +void gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline); + +extern const struct nvkm_head_func tu102_gsp_head; #define HEAD_MSG(h,l,f,a...) do { \ struct nvkm_head *_h = (h); \ diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h index 3ba04bead2f9cf..5d682a774f2dd3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -194,6 +194,7 @@ void gv100_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32); void gv100_sor_dp_watermark(struct nvkm_ior *, int, u8); extern const struct nvkm_ior_func_hda gv100_sor_hda; +void tu102_sor_hdmi_gcp(struct nvkm_ior *, int, bool); void tu102_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16); int nv50_pior_cnt(struct nvkm_disp *, unsigned long *); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h index a3fd7cb7c48839..fde321dbd7c8f2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h @@ -5,6 +5,8 @@ #include #include struct nvkm_head; +struct nvkm_head_func; +struct nvkm_ior; struct nvkm_outp; struct dcb_output; @@ -34,6 +36,20 @@ struct nvkm_disp_func { int (*new)(struct nvkm_disp *, int id); } wndw, head, dac, sor, pior; + /* Register programming that the GSP-RM display path (rm/r535) needs from + * the chip, everything else on that path goes through RM. The hooks are + * called unconditionally and the head table is handed to nvkm_head_new_(). + */ + struct { + irqreturn_t (*intr)(struct nvkm_inth *); + /* Head-timing interrupts arrive on a second DISP vector. */ + bool intr_low_latency; + const struct nvkm_head_func *head; + void (*hdmi_gcp)(struct nvkm_ior *, int head, bool enable); + void (*hdmi_infoframe_avi)(struct nvkm_ior *, int head, void *data, u32 size); + void (*hdmi_infoframe_vsi)(struct nvkm_ior *, int head, void *data, u32 size); + } gsp; + u16 ramht_size; struct nvkm_sclass root; @@ -72,6 +88,7 @@ int gv100_disp_wndw_cnt(struct nvkm_disp *, unsigned long *); int gv100_disp_caps_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **); int tu102_disp_init(struct nvkm_disp *); +irqreturn_t tu102_disp_intr(struct nvkm_inth *); void nv50_disp_dptmds_war_2(struct nvkm_disp *, struct dcb_output *); void nv50_disp_dptmds_war_3(struct nvkm_disp *, struct dcb_output *); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c index dcb9f8ba374ca6..f6c163072ff666 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c @@ -30,6 +30,21 @@ #include +/* General Control Packet: bracket an audio enable/disable with AVMute + * through the legacy GCP SF unit. Used by the GSP-RM path, which sends the + * equivalent packet via RM as well but keeps the direct write in sync. + */ +void +tu102_sor_hdmi_gcp(struct nvkm_ior *sor, int head, bool enable) +{ + struct nvkm_device *device = sor->disp->engine.subdev.device; + const u32 hdmi = head * 0x400; + + nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000); + nvkm_wr32(device, 0x6f00cc + hdmi, !enable ? 0x00000001 : 0x00000010); + nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000001); +} + void tu102_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned) { @@ -104,6 +119,64 @@ tu102_sor_new(struct nvkm_disp *disp, int id) return nvkm_ior_new_(&tu102_sor, disp, SOR, id, hda & BIT(id)); } +/* The GSP-RM display path leaves head-timing (vblank) interrupts and their + * enables to us. These program the RM head-timing line (bit 1 of the + * per-head enable, not the bit nvkm's own gv100 path uses). + */ +static void +tu102_head_vblank_put(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + + nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000000); +} + +static void +tu102_head_vblank_get(struct nvkm_head *head) +{ + struct nvkm_device *device = head->disp->engine.subdev.device; + + nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002); + nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002); +} + +const struct nvkm_head_func +tu102_gsp_head = { + .state = gv100_head_state, + .rgpos = gv100_head_rgpos, + .vblank_get = tu102_head_vblank_get, + .vblank_put = tu102_head_vblank_put, +}; + +static void +tu102_disp_intr_head_timing(struct nvkm_disp *disp, int head) +{ + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + u32 stat = nvkm_rd32(device, 0x611c00 + (head * 0x04)); + + if (stat & 0x00000002) { + nvkm_disp_vblank(disp, head); + + nvkm_wr32(device, 0x611800 + (head * 0x04), 0x00000002); + } +} + +irqreturn_t +tu102_disp_intr(struct nvkm_inth *inth) +{ + struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth); + struct nvkm_subdev *subdev = &disp->engine.subdev; + struct nvkm_device *device = subdev->device; + unsigned long mask = nvkm_rd32(device, 0x611ec0) & 0x000000ff; + int head; + + for_each_set_bit(head, &mask, 8) + tu102_disp_intr_head_timing(disp, head); + + return IRQ_HANDLED; +} + int tu102_disp_init(struct nvkm_disp *disp) { @@ -230,12 +303,23 @@ tu102_disp = { }, }; +static const struct nvkm_disp_func +tu102_gsp_disp = { + .uevent = &gv100_disp_chan_uevent, + .ramht_size = 0x2000, + .gsp.intr = tu102_disp_intr, + .gsp.head = &tu102_gsp_head, + .gsp.hdmi_gcp = tu102_sor_hdmi_gcp, + .gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi, + .gsp.hdmi_infoframe_vsi = gv100_sor_hdmi_infoframe_vsi, +}; + int tu102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_disp **pdisp) { if (nvkm_gsp_rm(device->gsp)) - return r535_disp_new(&tu102_disp, device, type, inst, pdisp); + return r535_disp_new(&tu102_gsp_disp, device, type, inst, pdisp); return nvkm_disp_new_(&tu102_disp, device, type, inst, pdisp); } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c index 1155f079b0c3de..f5f22173fc2c12 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c @@ -400,16 +400,16 @@ r535_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable) r535_sor_dp_audio_mute(sor, false); } -static void -r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn) +static int +r535_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn) { struct nvkm_disp *disp = sor->disp; struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl; ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom, NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl)); - if (WARN_ON(IS_ERR(ctrl))) - return; + if (IS_ERR(ctrl)) + return PTR_ERR(ctrl); ctrl->subDeviceInstance = 0; ctrl->head = head; @@ -429,12 +429,20 @@ r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u ctrl->MST.sendACT = 0; ctrl->MST.singleHeadMSTPipeline = 0; ctrl->MST.bEnableAudioOverRightPanel = 0; - WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl)); + return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl); +} + +static void +r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn) +{ + const struct nvkm_rm_api *rmapi = sor->disp->engine.subdev.device->gsp->rm->api; + + WARN_ON(rmapi->disp->dp.vcpi(sor, head, slot, slot_nr, pbn, aligned_pbn)); } static int -r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef, - u32 watermark, u32 hblanksym, u32 vblanksym) +r535_dp_sst(struct nvkm_ior *sor, int head, bool ef, + u32 watermark, u32 hblanksym, u32 vblanksym) { struct nvkm_disp *disp = sor->disp; struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl; @@ -461,6 +469,15 @@ r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef, return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl); } +static int +r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef, + u32 watermark, u32 hblanksym, u32 vblanksym) +{ + const struct nvkm_rm_api *rmapi = sor->disp->engine.subdev.device->gsp->rm->api; + + return rmapi->disp->dp.sst(sor, head, ef, watermark, hblanksym, vblanksym); +} + static const struct nvkm_ior_func_dp r535_sor_dp = { .sst = r535_sor_dp_sst, @@ -545,16 +562,21 @@ r535_sor_hdmi_ctrl_audio(struct nvkm_outp *outp, bool enable) static void r535_sor_hdmi_audio(struct nvkm_ior *sor, int head, bool enable) { - struct nvkm_device *device = sor->disp->engine.subdev.device; - const u32 hdmi = head * 0x400; - r535_sor_hdmi_ctrl_audio(sor->asy.outp, enable); r535_sor_hdmi_ctrl_audio_mute(sor->asy.outp, !enable); + sor->disp->func->gsp.hdmi_gcp(sor, head, enable); +} + +static void +r535_sor_hdmi_infoframe_avi(struct nvkm_ior *sor, int head, void *data, u32 size) +{ + sor->disp->func->gsp.hdmi_infoframe_avi(sor, head, data, size); +} - /* General Control (GCP). */ - nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000); - nvkm_wr32(device, 0x6f00cc + hdmi, !enable ? 0x00000001 : 0x00000010); - nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000001); +static void +r535_sor_hdmi_infoframe_vsi(struct nvkm_ior *sor, int head, void *data, u32 size) +{ + sor->disp->func->gsp.hdmi_infoframe_vsi(sor, head, data, size); } static void @@ -582,8 +604,8 @@ r535_sor_hdmi = { .ctrl = r535_sor_hdmi_ctrl, .scdc = r535_sor_hdmi_scdc, /*TODO: SF_USER -> KMS. */ - .infoframe_avi = gv100_sor_hdmi_infoframe_avi, - .infoframe_vsi = gv100_sor_hdmi_infoframe_vsi, + .infoframe_avi = r535_sor_hdmi_infoframe_avi, + .infoframe_vsi = r535_sor_hdmi_infoframe_vsi, .audio = r535_sor_hdmi_audio, }; @@ -608,35 +630,6 @@ r535_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask) return 4; } -static void -r535_head_vblank_put(struct nvkm_head *head) -{ - struct nvkm_device *device = head->disp->engine.subdev.device; - - nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000000); -} - -static void -r535_head_vblank_get(struct nvkm_head *head) -{ - struct nvkm_device *device = head->disp->engine.subdev.device; - - nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002); - nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002); -} - -static void -r535_head_state(struct nvkm_head *head, struct nvkm_head_state *state) -{ -} - -static const struct nvkm_head_func -r535_head = { - .state = r535_head_state, - .vblank_get = r535_head_vblank_get, - .vblank_put = r535_head_vblank_put, -}; - static struct nvkm_conn * r535_conn_new(struct nvkm_disp *disp, u32 id) { @@ -1408,35 +1401,6 @@ static const struct nvkm_event_func r535_disp_event = { }; -static void -r535_disp_intr_head_timing(struct nvkm_disp *disp, int head) -{ - struct nvkm_subdev *subdev = &disp->engine.subdev; - struct nvkm_device *device = subdev->device; - u32 stat = nvkm_rd32(device, 0x611c00 + (head * 0x04)); - - if (stat & 0x00000002) { - nvkm_disp_vblank(disp, head); - - nvkm_wr32(device, 0x611800 + (head * 0x04), 0x00000002); - } -} - -static irqreturn_t -r535_disp_intr(struct nvkm_inth *inth) -{ - struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth); - struct nvkm_subdev *subdev = &disp->engine.subdev; - struct nvkm_device *device = subdev->device; - unsigned long mask = nvkm_rd32(device, 0x611ec0) & 0x000000ff; - int head; - - for_each_set_bit(head, &mask, 8) - r535_disp_intr_head_timing(disp, head); - - return IRQ_HANDLED; -} - static void r535_disp_fini(struct nvkm_disp *disp, bool suspend) { @@ -1663,7 +1627,7 @@ r535_disp_oneinit(struct nvkm_disp *disp) nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl); for_each_set_bit(i, &disp->head.mask, disp->head.nr) { - ret = nvkm_head_new_(&r535_head, disp, i); + ret = nvkm_head_new_(disp->func->gsp.head, disp, i); if (ret) return ret; } @@ -1707,12 +1671,20 @@ r535_disp_oneinit(struct nvkm_disp *disp) if (ret) return ret; - ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type, disp->engine.subdev.inst); + /* Chips that raise head-timing interrupts on a separate low-latency + * vector report it as a second DISP interrupt table entry, exposed + * as instance 1 by the RM engine-index translation (see + * r570_gsp_xlat_mc_engine_idx()). Their high-latency vector + * (instance 0) is left unhandled as no event nouveau enables is + * routed to it, and without a handler it stays masked. + */ + ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type, + disp->func->gsp.intr_low_latency ? 1 : disp->engine.subdev.inst); if (ret < 0) return ret; ret = nvkm_inth_add(&device->vfn->intr, ret, NVKM_INTR_PRIO_NORMAL, &disp->engine.subdev, - r535_disp_intr, &disp->engine.subdev.inth); + disp->func->gsp.intr, &disp->engine.subdev.inth); if (ret) return ret; @@ -1745,6 +1717,7 @@ r535_disp_new(const struct nvkm_disp_func *hw, struct nvkm_device *device, rm->uevent = hw->uevent; rm->sor.cnt = r535_sor_cnt; rm->sor.new = r535_sor_new; + rm->gsp = hw->gsp; rm->ramht_size = hw->ramht_size; rm->root.oclass = gpu->disp.class.root; @@ -1786,6 +1759,8 @@ r535_disp = { .dp = { .get_caps = r535_dp_get_caps, .set_indexed_link_rates = r535_dp_set_indexed_link_rates, + .sst = r535_dp_sst, + .vcpi = r535_dp_vcpi, }, .chan = { .set_pushbuf = r535_disp_chan_set_pushbuf, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c index a96e31c2d80b23..8a23837f356e9e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c @@ -5,6 +5,7 @@ #include #include +#include #include #include "nvhw/drf.h" @@ -74,6 +75,67 @@ r570_disp_chan_set_pushbuf(struct nvkm_disp *disp, s32 oclass, int inst, struct return nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl); } +static int +r570_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn) +{ + struct nvkm_disp *disp = sor->disp; + NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl; + + ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom, + NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl)); + if (IS_ERR(ctrl)) + return PTR_ERR(ctrl); + + ctrl->subDeviceInstance = 0; + ctrl->head = head; + ctrl->sorIndex = sor->id; + ctrl->dpLink = sor->asy.link == 2; + ctrl->bEnableOverride = 1; + ctrl->bMST = 1; + ctrl->hBlankSym = 0; + ctrl->vBlankSym = 0; + ctrl->colorFormat = 0; + ctrl->bEnableTwoHeadOneOr = 0; + ctrl->singleHeadMultistreamMode = 0; + ctrl->MST.slotStart = slot; + ctrl->MST.slotEnd = slot + slot_nr - 1; + ctrl->MST.PBN = pbn; + ctrl->MST.Timeslice = aligned_pbn; + ctrl->MST.sendACT = 0; + ctrl->MST.singleHeadMSTPipeline = 0; + ctrl->MST.bEnableAudioOverRightPanel = 0; + return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl); +} + +static int +r570_dp_sst(struct nvkm_ior *sor, int head, bool ef, + u32 watermark, u32 hblanksym, u32 vblanksym) +{ + struct nvkm_disp *disp = sor->disp; + NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl; + + ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom, + NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl)); + if (IS_ERR(ctrl)) + return PTR_ERR(ctrl); + + ctrl->subDeviceInstance = 0; + ctrl->head = head; + ctrl->sorIndex = sor->id; + ctrl->dpLink = sor->asy.link == 2; + ctrl->bEnableOverride = 1; + ctrl->bMST = 0; + ctrl->hBlankSym = hblanksym; + ctrl->vBlankSym = vblanksym; + ctrl->colorFormat = 0; + ctrl->bEnableTwoHeadOneOr = 0; + ctrl->SST.bEnhancedFraming = ef; + ctrl->SST.tuSize = 64; + ctrl->SST.waterMark = watermark; + ctrl->SST.bEnableAudioOverRightPanel = 0; + return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl); +} + static int r570_dp_set_indexed_link_rates(struct nvkm_outp *outp) { @@ -255,6 +317,8 @@ r570_disp = { .dp = { .get_caps = r570_dp_get_caps, .set_indexed_link_rates = r570_dp_set_indexed_link_rates, + .sst = r570_dp_sst, + .vcpi = r570_dp_vcpi, }, .chan = { .set_pushbuf = r570_disp_chan_set_pushbuf, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c index 996941c668ba9e..1488771c63fc47 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c @@ -44,6 +44,15 @@ r570_gsp_xlat_mc_engine_idx(u32 mc_engine_idx, enum nvkm_subdev_type *ptype, int *ptype = NVKM_ENGINE_DISP; *pinst = 0; return true; + case MC_ENGINE_IDX_DISP_LOW: + /* GB20x+ report a separate low-latency display vector, used + * for head-timing interrupts. Expose it as a second DISP + * interrupt instance. r535_disp_oneinit() attaches the + * handler to it when the chip's gsp.intr_low_latency is set. + */ + *ptype = NVKM_ENGINE_DISP; + *pinst = 1; + return true; case MC_ENGINE_IDX_CE0 ... MC_ENGINE_IDX_CE19: *ptype = NVKM_ENGINE_CE; *pinst = mc_engine_idx - MC_ENGINE_IDX_CE0; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h index 06e972835d7784..742b25a2a12df7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h @@ -256,6 +256,8 @@ typedef struct NV0073_CTRL_DP_CTRL_PARAMS { NvU32 eightLaneDpcdBaseAddr; } NV0073_CTRL_DP_CTRL_PARAMS; +#define NV0073_CTRL_CMD_DP_CONFIG_STREAM (0x731362U) /* finn: Evaluated from "(FINN_NV04_DISPLAY_COMMON_DP_INTERFACE_ID << 8) | NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS_MESSAGE_ID" */ + typedef struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS { NvU32 subDeviceInstance; NvU32 head; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h index a9af94adf9efc6..fcd0221dcea1ea 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h @@ -6,6 +6,7 @@ #ifndef __NVKM_RM_H__ #define __NVKM_RM_H__ #include "handles.h" +struct nvkm_ior; struct nvkm_outp; struct r535_gr; @@ -93,6 +94,10 @@ struct nvkm_rm_api { struct { int (*get_caps)(struct nvkm_disp *, int *link_bw, bool *mst, bool *wm); int (*set_indexed_link_rates)(struct nvkm_outp *); + int (*sst)(struct nvkm_ior *, int head, bool ef, + u32 watermark, u32 hblanksym, u32 vblanksym); + int (*vcpi)(struct nvkm_ior *, int head, + u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn); } dp; struct { diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 0d8792de7747a7..38888a75f81d46 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -831,6 +831,13 @@ static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel) return 0; } +static void panel_edp_put_adapter(void *_adap) +{ + struct i2c_adapter *adap = _adap; + + put_device(&adap->dev); +} + static int panel_edp_probe(struct device *dev, const struct panel_desc *desc, struct drm_dp_aux *aux) { @@ -878,6 +885,11 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc, if (!panel->ddc) return -EPROBE_DEFER; + + err = devm_add_action_or_reset(dev, panel_edp_put_adapter, + panel->ddc); + if (err) + return err; } else if (aux) { panel->ddc = &aux->ddc; } @@ -889,7 +901,7 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc, err = drm_panel_of_backlight(&panel->base); if (err) - goto err_finished_ddc_init; + return err; /* * We use runtime PM for prepare / unprepare since those power the panel @@ -936,9 +948,6 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc, err_finished_pm_runtime: pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); -err_finished_ddc_init: - if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc)) - put_device(&panel->ddc->dev); return err; } @@ -982,8 +991,6 @@ static void panel_edp_remove(struct device *dev) pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); - if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc)) - put_device(&panel->ddc->dev); drm_edid_free(panel->drm_edid); panel->drm_edid = NULL; diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index fa88b2f9da76dc..10b7e12c1f1eda 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -707,7 +707,8 @@ static int panthor_fw_read_build_info(struct panthor_device *ptdev, return ret; if (hdr.meta_start > fw->size || - hdr.meta_start + hdr.meta_size > fw->size) { + hdr.meta_size > fw->size - hdr.meta_start || + hdr.meta_size <= header_len) { drm_err(&ptdev->base, "Firmware build info corrupt\n"); /* We don't need the build info, so continue */ return 0; @@ -894,14 +895,15 @@ static int panthor_init_cs_iface(struct panthor_device *ptdev, struct panthor_fw_csg_iface *csg_iface = panthor_fw_get_csg_iface(ptdev, csg_idx); struct panthor_fw_cs_iface *cs_iface = &ptdev->fw->iface.streams[csg_idx][cs_idx]; u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem); - u32 iface_offset = CSF_GROUP_CONTROL_OFFSET + - (csg_idx * glb_iface->control->group_stride) + + u64 iface_offset = CSF_GROUP_CONTROL_OFFSET + + ((u64)csg_idx * glb_iface->control->group_stride) + CSF_STREAM_CONTROL_OFFSET + - (cs_idx * csg_iface->control->stream_stride); + ((u64)cs_idx * csg_iface->control->stream_stride); struct panthor_fw_cs_iface *first_cs_iface = panthor_fw_get_cs_iface(ptdev, 0, 0); - if (iface_offset + sizeof(*cs_iface) >= shared_section_sz) + if (iface_offset > shared_section_sz || + sizeof(*cs_iface->control) > shared_section_sz - iface_offset) return -EINVAL; spin_lock_init(&cs_iface->lock); @@ -951,10 +953,12 @@ static int panthor_init_csg_iface(struct panthor_device *ptdev, struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); struct panthor_fw_csg_iface *csg_iface = &ptdev->fw->iface.groups[csg_idx]; u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem); - u32 iface_offset = CSF_GROUP_CONTROL_OFFSET + (csg_idx * glb_iface->control->group_stride); + u64 iface_offset = CSF_GROUP_CONTROL_OFFSET + + ((u64)csg_idx * glb_iface->control->group_stride); unsigned int i; - if (iface_offset + sizeof(*csg_iface) >= shared_section_sz) + if (iface_offset > shared_section_sz || + sizeof(*csg_iface->control) > shared_section_sz - iface_offset) return -EINVAL; spin_lock_init(&csg_iface->lock); @@ -1006,11 +1010,15 @@ static u32 panthor_get_instr_features(struct panthor_device *ptdev) static int panthor_fw_init_ifaces(struct panthor_device *ptdev) { struct panthor_fw_global_iface *glb_iface = &ptdev->fw->iface.global; + u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem); unsigned int i; if (!ptdev->fw->shared_section->mem->kmap) return -EINVAL; + if (sizeof(*glb_iface->control) > shared_section_sz) + return -EINVAL; + spin_lock_init(&glb_iface->lock); glb_iface->control = ptdev->fw->shared_section->mem->kmap; diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index cae92a3ae8a41d..784f7000fad268 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -864,12 +864,13 @@ static int ssd132x_update_rect(struct ssd130x_device *ssd130x, */ /* Set column start and end */ - ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_COL_RANGE, x / segment_width, columns - 1); + ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_COL_RANGE, x / segment_width, + x / segment_width + columns - 1); if (ret < 0) return ret; /* Set row start and end */ - ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_ROW_RANGE, y, rows - 1); + ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_ROW_RANGE, y, y + rows - 1); if (ret < 0) return ret; @@ -915,12 +916,12 @@ static int ssd133x_update_rect(struct ssd130x_device *ssd130x, */ /* Set column start and end */ - ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_COL_RANGE, x, columns - 1); + ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_COL_RANGE, x, x + columns - 1); if (ret < 0) return ret; /* Set row start and end */ - ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_ROW_RANGE, y, rows - 1); + ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_ROW_RANGE, y, y + rows - 1); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index 03e46a2af64639..adf5b6e09ca647 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -686,7 +686,7 @@ static int sun4i_backend_init_sat(struct device *dev) { ret = clk_prepare_enable(backend->sat_clk); if (ret) { dev_err(dev, "Couldn't enable the SAT clock\n"); - return ret; + goto err_assert_reset; } return 0; diff --git a/drivers/gpu/drm/sysfb/ofdrm.c b/drivers/gpu/drm/sysfb/ofdrm.c index 819aed466727e6..9d60db45139c3a 100644 --- a/drivers/gpu/drm/sysfb/ofdrm.c +++ b/drivers/gpu/drm/sysfb/ofdrm.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -238,7 +239,7 @@ static bool is_avivo(u32 vendor, u32 device) /* This will match most R5xx */ return (vendor == PCI_VENDOR_ID_ATI) && ((device >= PCI_VENDOR_ID_ATI_R520 && device < 0x7800) || - (PCI_VENDOR_ID_ATI_R600 >= 0x9400)); + (device >= PCI_VENDOR_ID_ATI_R600)); } static enum ofdrm_model display_get_model_of(struct drm_device *dev, struct device_node *of_node) @@ -913,7 +914,10 @@ static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv, return ERR_PTR(-EINVAL); } - fb_size = linebytes * height; + if (check_mul_overflow(linebytes, height, &fb_size)) { + drm_err(dev, "framebuffer size exceeds maximum\n"); + return ERR_PTR(-EINVAL); + } /* * Try to figure out the address of the framebuffer. Unfortunately, Open diff --git a/drivers/gpu/drm/sysfb/simpledrm.c b/drivers/gpu/drm/sysfb/simpledrm.c index fc168920f2c641..ca8a8d86094e41 100644 --- a/drivers/gpu/drm/sysfb/simpledrm.c +++ b/drivers/gpu/drm/sysfb/simpledrm.c @@ -48,13 +48,6 @@ simplefb_get_validated_int(struct drm_device *dev, const char *name, return drm_sysfb_get_validated_int(dev, name, value, INT_MAX); } -static int -simplefb_get_validated_int0(struct drm_device *dev, const char *name, - uint32_t value) -{ - return drm_sysfb_get_validated_int0(dev, name, value, INT_MAX); -} - static const struct drm_format_info * simplefb_get_validated_format(struct drm_device *dev, const char *format_name) { @@ -88,14 +81,14 @@ static int simplefb_get_width_pd(struct drm_device *dev, const struct simplefb_platform_data *pd) { - return simplefb_get_validated_int0(dev, "width", pd->width); + return drm_sysfb_get_validated_int0(dev, "width", pd->width, U16_MAX); } static int simplefb_get_height_pd(struct drm_device *dev, const struct simplefb_platform_data *pd) { - return simplefb_get_validated_int0(dev, "height", pd->height); + return drm_sysfb_get_validated_int0(dev, "height", pd->height, U16_MAX); } static int @@ -144,7 +137,7 @@ simplefb_get_width_of(struct drm_device *dev, struct device_node *of_node) if (ret) return ret; - return simplefb_get_validated_int0(dev, "width", width); + return drm_sysfb_get_validated_int0(dev, "width", width, U16_MAX); } static int @@ -155,7 +148,7 @@ simplefb_get_height_of(struct drm_device *dev, struct device_node *of_node) if (ret) return ret; - return simplefb_get_validated_int0(dev, "height", height); + return drm_sysfb_get_validated_int0(dev, "height", height, U16_MAX); } static int @@ -200,6 +193,39 @@ simplefb_get_memory_of(struct drm_device *dev, struct device_node *of_node) return res; } +static int __simplefb_get_panel_size_mm_of(struct drm_device *dev, + struct device_node *of_panel_node, + const char *name) +{ + int ret; + u32 value; + + ret = of_property_read_u32(of_panel_node, name, &value); + if (ret) { + drm_dbg(dev, "simplefb: cannot parse panel %s: error %d\n", + name, ret); + return ret; + } else if (value > U16_MAX) { + drm_dbg(dev, "simplefb: panel %s of %u exceeds maximum value\n", + name, value); + return -EINVAL; + } + + return value; +} + +static int simplefb_get_panel_width_mm_of(struct drm_device *dev, + struct device_node *of_panel_node) +{ + return __simplefb_get_panel_size_mm_of(dev, of_panel_node, "width-mm"); +} + +static int simplefb_get_panel_height_mm_of(struct drm_device *dev, + struct device_node *of_panel_node) +{ + return __simplefb_get_panel_size_mm_of(dev, of_panel_node, "height-mm"); +} + /* * Simple Framebuffer device */ @@ -601,7 +627,7 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, struct drm_sysfb_device *sysfb; struct drm_device *dev; int width, height, stride; - int width_mm = 0, height_mm = 0; + u16 width_mm = 0, height_mm = 0; struct device_node *panel_node; const struct drm_format_info *format; struct resource *res, *mem = NULL; @@ -665,8 +691,18 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, return ERR_CAST(mem); panel_node = of_parse_phandle(of_node, "panel", 0); if (panel_node) { - simplefb_read_u32_of(dev, panel_node, "width-mm", &width_mm); - simplefb_read_u32_of(dev, panel_node, "height-mm", &height_mm); + /* + * Ignore errors from parsing the physical panel + * size. Using the pre-initialized sizes of 0 will + * make drm_sysfb_mode() calculate a default physical + * size based on a resolution of 96 dpi. + */ + ret = simplefb_get_panel_width_mm_of(dev, panel_node); + if (ret > 0) + width_mm = ret; + ret = simplefb_get_panel_height_mm_of(dev, panel_node); + if (ret > 0) + height_mm = ret; of_node_put(panel_node); } } else { @@ -674,9 +710,15 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, return ERR_PTR(-ENODEV); } if (!stride) { - stride = drm_format_info_min_pitch(format, 0, width); - if (drm_WARN_ON(dev, !stride)) + u64 pitch = drm_format_info_min_pitch(format, 0, width); + + if (drm_WARN_ON(dev, !pitch)) { + return ERR_PTR(-EINVAL); /* driver bug */ + } else if (pitch > INT_MAX) { + drm_warn(dev, "stride of %llu exceeds maximum\n", pitch); return ERR_PTR(-EINVAL); + } + stride = pitch; } sysfb->fb_mode = drm_sysfb_mode(width, height, width_mm, height_mm); diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index e7648ad3e6159a..6142f90d43e2e1 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -917,6 +917,7 @@ int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt, { struct ttm_pool_tt_restore *restore = tt->restore; struct ttm_pool_alloc_state alloc; + int ret; if (WARN_ON(!ttm_tt_is_backed_up(tt))) return -EINVAL; @@ -940,14 +941,22 @@ int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt, } else { alloc = restore->snapshot_alloc; if (ttm_pool_restore_valid(restore)) { - int ret = ttm_pool_restore_commit(restore, tt->backup, - ctx, &alloc); + ret = ttm_pool_restore_commit(restore, tt->backup, + ctx, &alloc); if (ret) return ret; } - if (!alloc.remaining_pages) + if (!alloc.remaining_pages) { + ret = ttm_pool_apply_caching(&alloc); + if (ret) + return ret; + + kfree(tt->restore); + tt->restore = NULL; + return 0; + } } return __ttm_pool_alloc(pool, tt, ctx, &alloc, restore); diff --git a/drivers/gpu/drm/xe/xe_vram.c b/drivers/gpu/drm/xe/xe_vram.c index 23eb7edbdd5725..56cff1e44530b0 100644 --- a/drivers/gpu/drm/xe/xe_vram.c +++ b/drivers/gpu/drm/xe/xe_vram.c @@ -89,12 +89,28 @@ static int get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size, u64 *poffset) offset = offset_hi << 32; /* HW view bits 39:32 */ offset |= offset_lo << 6; /* HW view bits 31:6 */ offset *= num_enabled; /* convert to SW view */ - offset = round_up(offset, SZ_128K); /* SW must round up to nearest 128K */ - /* We don't expect any holes */ - xe_assert_msg(xe, offset == (xe_mmio_read64_2x32(>_to_tile(gt)->mmio, GSMBASE) - - ccs_size), - "Hole between CCS and GSM.\n"); + drm_info(&xe->drm, "FLAT_CCS base:%llx, aligned:%s\n", offset, + str_yes_no(IS_ALIGNED(offset, SZ_128K))); + + /* + * Everything below this offset is handed to the VRAM + * allocator, so it has to be the *first* address the + * compression hardware owns, rounded down. Rounding it up + * publishes CCS storage as free memory. + */ + offset = round_down(offset, SZ_4K); + + /* + * CCS storage must not run into GSM. The old check compared + * the offset against GSMBASE - ccs_size for equality, which + * could not fail: that value is 128K aligned, so it agreed + * with the rounded-up offset even when the base was not 128K + * aligned - exactly the case this fixes. + */ + xe_assert_msg(xe, offset + ccs_size <= + xe_mmio_read64_2x32(>_to_tile(gt)->mmio, GSMBASE), + "CCS overlaps GSM.\n"); } else { reg = xe_gt_mcr_unicast_read_any(gt, XEHP_FLAT_CCS_BASE_ADDR); offset = (u64)REG_FIELD_GET(XEHP_FLAT_CCS_PTR, reg) * SZ_64K; diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c index 702c22fae136aa..049df064776109 100644 --- a/drivers/hid/bpf/hid_bpf_struct_ops.c +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c @@ -250,6 +250,11 @@ static void hid_bpf_unreg(void *kdata, struct bpf_link *link) mutex_lock(&hdev->bpf.prog_list_lock); + if (!ops->hdev) { + mutex_unlock(&hdev->bpf.prog_list_lock); + return; + } + list_del_rcu(&ops->list); synchronize_srcu(&hdev->bpf.srcu); ops->hdev = NULL; @@ -310,13 +315,17 @@ static struct bpf_struct_ops bpf_hid_bpf_ops = { void __hid_bpf_ops_destroy_device(struct hid_device *hdev) { struct hid_bpf_ops *e; + int count = 0; - rcu_read_lock(); - list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) { - hid_put_device(hdev); + mutex_lock(&hdev->bpf.prog_list_lock); + list_for_each_entry(e, &hdev->bpf.prog_list, list) { e->hdev = NULL; + count++; } - rcu_read_unlock(); + mutex_unlock(&hdev->bpf.prog_list_lock); + + while (count--) + hid_put_device(hdev); } static int __init hid_bpf_struct_ops_init(void) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index bf7dd0fbf2496f..a5f232cc2b66e3 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -91,6 +91,9 @@ struct apple_sc_backlight { struct hid_device *hdev; }; +/* T2 VHCI re-enumerates the internal keyboard across system resume. */ +static int apple_backlight_resume_brightness = -1; + struct apple_backlight_config_report { u8 report_id; u8 version; @@ -825,6 +828,7 @@ static int apple_backlight_led_set(struct led_classdev *led_cdev, static int apple_backlight_init(struct hid_device *hdev) { int ret; + int brightness; struct apple_sc *asc = hid_get_drvdata(hdev); struct apple_backlight_config_report *rep; @@ -860,13 +864,20 @@ static int apple_backlight_init(struct hid_device *hdev) asc->backlight->cdev.name = "apple::kbd_backlight"; asc->backlight->cdev.max_brightness = rep->backlight_on_max; asc->backlight->cdev.brightness_set_blocking = apple_backlight_led_set; - asc->backlight->cdev.flags = LED_CORE_SUSPENDRESUME; + /* VHCI re-enumeration restores the cached brightness in the next probe. */ + + brightness = READ_ONCE(apple_backlight_resume_brightness); + if (brightness < 0) + brightness = LED_OFF; + else + brightness = min_t(int, brightness, rep->backlight_on_max); - ret = apple_backlight_set(hdev, 0, 0); + ret = apple_backlight_set(hdev, brightness, 0); if (ret < 0) { hid_err(hdev, "backlight set request failed: %d\n", ret); goto cleanup_and_exit; } + asc->backlight->cdev.brightness = brightness; ret = devm_led_classdev_register(&hdev->dev, &asc->backlight->cdev); @@ -999,6 +1010,9 @@ static void apple_remove(struct hid_device *hdev) if (asc->quirks & APPLE_RDESC_BATTERY) timer_delete_sync(&asc->battery_timer); + if (asc->backlight) + WRITE_ONCE(apple_backlight_resume_brightness, + asc->backlight->cdev.brightness); hid_hw_stop(hdev); } diff --git a/drivers/hid/hid-corsair-void.c b/drivers/hid/hid-corsair-void.c index 5e9a5b8f7f167b..071a663a6c2686 100644 --- a/drivers/hid/hid-corsair-void.c +++ b/drivers/hid/hid-corsair-void.c @@ -92,6 +92,9 @@ #define CORSAIR_VOID_STATUS_REPORT_ID 0x64 #define CORSAIR_VOID_FIRMWARE_REPORT_ID 0x66 +#define CORSAIR_VOID_STATUS_REPORT_SIZE 5 +#define CORSAIR_VOID_FIRMWARE_REPORT_SIZE 5 + #define CORSAIR_VOID_USB_SIDETONE_REQUEST 0x1 #define CORSAIR_VOID_USB_SIDETONE_REQUEST_TYPE 0x21 #define CORSAIR_VOID_USB_SIDETONE_VALUE 0x200 @@ -742,6 +745,13 @@ static int corsair_void_raw_event(struct hid_device *hid_dev, /* Description of packets are documented at the top of this file */ if (hid_report->id == CORSAIR_VOID_STATUS_REPORT_ID) { + if (size < CORSAIR_VOID_STATUS_REPORT_SIZE) { + hid_warn_ratelimited(hid_dev, + "unexpected status report of size %d", + size); + return 1; + } + drvdata->mic_up = FIELD_GET(CORSAIR_VOID_MIC_MASK, data[2]); drvdata->connected = (data[3] == CORSAIR_VOID_WIRELESS_CONNECTED) || drvdata->is_wired; @@ -750,6 +760,13 @@ static int corsair_void_raw_event(struct hid_device *hid_dev, FIELD_GET(CORSAIR_VOID_CAPACITY_MASK, data[2]), data[3], data[4]); } else if (hid_report->id == CORSAIR_VOID_FIRMWARE_REPORT_ID) { + if (size < CORSAIR_VOID_FIRMWARE_REPORT_SIZE) { + hid_warn_ratelimited(hid_dev, + "unexpected firmware report of size %d", + size); + return 1; + } + drvdata->fw_receiver_major = data[1]; drvdata->fw_receiver_minor = data[2]; drvdata->fw_headset_major = data[3]; diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c index e4ddd8e9293b68..d52ce3531ab706 100644 --- a/drivers/hid/hid-mcp2221.c +++ b/drivers/hid/hid-mcp2221.c @@ -343,7 +343,7 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp, ret = mcp_send_data_req_status(mcp, mcp->txbuf, 4); if (ret) - return ret; + goto out; mcp->rxbuf_idx = 0; @@ -365,7 +365,7 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp, } else { usleep_range(980, 1000); mcp_cancel_last_cmd(mcp); - return ret; + goto out; } } else { retries = 0; @@ -375,6 +375,10 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp, usleep_range(980, 1000); ret = mcp_chk_last_cmd_status_free_bus(mcp); +out: + mcp->rxbuf = NULL; + mcp->rxbuf_size = 0; + return ret; } @@ -861,6 +865,9 @@ static int mcp2221_raw_event(struct hid_device *hdev, u8 *buf; struct mcp2221 *mcp = hid_get_drvdata(hdev); + if (size < 4) + return 0; + switch (data[0]) { case MCP2221_I2C_WR_DATA: @@ -926,6 +933,10 @@ static int mcp2221_raw_event(struct hid_device *hdev, mcp->status = -EINVAL; break; } + if (4 + data[3] > size) { + mcp->status = -EINVAL; + break; + } buf = mcp->rxbuf; memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]); mcp->rxbuf_idx = mcp->rxbuf_idx + data[3]; @@ -1049,6 +1060,8 @@ static void mcp2221_hid_unregister(void *ptr) { struct hid_device *hdev = ptr; + if (hdev->io_started) + hid_device_io_stop(hdev); hid_hw_close(hdev); hid_hw_stop(hdev); } diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c index 085847a92e07dd..1f7dfe60e9ba2b 100644 --- a/drivers/hid/hid-picolcd_debugfs.c +++ b/drivers/hid/hid-picolcd_debugfs.c @@ -99,6 +99,15 @@ static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u, ret = resp->raw_data[2]; if (ret > s) ret = s; + /* + * raw_data[2] is a device-supplied length; also clamp it to + * what picolcd_raw_event() actually stored (raw_size), or a + * hostile device overruns the raw_data[] buffer. + */ + if (ret > resp->raw_size - 3) + ret = resp->raw_size - 3; + if (ret < 0) + ret = 0; if (copy_to_user(u, resp->raw_data+3, ret)) ret = -EFAULT; else diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index d4af17fdba4670..d55a0388895fb1 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -235,7 +235,23 @@ static int rmi_hid_read_block(struct rmi_transport_dev *xport, u16 addr, break; } - read_input_count = data->readReport[1]; + read_input_count = min_t(int, data->readReport[1], + data->input_report_size - 2); + if (!read_input_count) { + /* + * A zero length reply advances neither + * bytes_read nor bytes_needed, and because a + * reply did arrive the wait above does not + * time out either, so a device answering 0 + * forever would spin here indefinitely with + * page_mutex held. + */ + hid_warn(hdev, "%s: zero-length read reply\n", + __func__); + clear_bit(RMI_READ_DATA_PENDING, &data->flags); + ret = -EIO; + break; + } memcpy(buf + bytes_read, &data->readReport[2], min(read_input_count, bytes_needed)); @@ -271,6 +287,11 @@ static int rmi_hid_write_block(struct rmi_transport_dev *xport, u16 addr, goto exit; } + if (len + 4 > data->output_report_size) { + ret = -EINVAL; + goto exit; + } + data->writeReport[0] = RMI_WRITE_REPORT_ID; data->writeReport[1] = len; data->writeReport[2] = addr & 0xFF; @@ -666,8 +687,16 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } - if (id->driver_data) - data->device_flags = id->driver_data; + /* + * RMI_DEVICE can only mean "this probe validated the RMI reports and + * allocated writeReport": every bail-out to start below skips that + * allocation, and device_flags left carrying RMI_DEVICE from + * driver_data would send rmi_input_configured() into rmi_set_page() + * with writeReport still NULL. A bind through the new_id sysfs + * attribute can supply driver_data with the bit set, so do not let + * driver_data grant it. + */ + data->device_flags = id->driver_data & ~RMI_DEVICE; /* * Check for the RMI specific report ids. If they are misisng @@ -696,6 +725,17 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) data->output_report_size = hid_report_len(output_report); + /* + * The write reports built by this driver occupy 6 bytes and the read + * handshake looks at the first 3 bytes of an input report, so refuse + * to drive a device whose reports cannot hold them. + */ + if (data->output_report_size < 6 || data->input_report_size < 3) { + hid_err(hdev, "rmi reports too small (out=%u in=%u)\n", + data->output_report_size, data->input_report_size); + goto start; + } + data->device_flags |= RMI_DEVICE; alloc_size = data->output_report_size + data->input_report_size; diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index d6fff53d4ee7ab..4f15eb95103968 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -70,6 +70,15 @@ static struct roccat_device *devices[ROCCAT_MAX_DEVICES]; /* protects modifications of devices array */ static DEFINE_MUTEX(devices_lock); +static void roccat_free_device(struct roccat_device *device) +{ + int i; + + for (i = 0; i < ROCCAT_CBUF_SIZE; i++) + kfree(device->cbuf[i].value); + kfree(device); +} + static ssize_t roccat_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { @@ -226,7 +235,7 @@ static int roccat_release(struct inode *inode, struct file *file) hid_hw_power(device->hid, PM_HINT_NORMAL); hid_hw_close(device->hid); } else { - kfree(device); + roccat_free_device(device); } } @@ -374,7 +383,7 @@ void roccat_disconnect(int minor) hid_hw_close(device->hid); wake_up_interruptible(&device->wait); } else { - kfree(device); + roccat_free_device(device); } } EXPORT_SYMBOL_GPL(roccat_disconnect); diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c index 6b0da2e0e1c93e..c2b425afd951be 100644 --- a/drivers/hid/hid-sensor-custom.c +++ b/drivers/hid/hid-sensor-custom.c @@ -609,7 +609,7 @@ static int hid_sensor_custom_add_attributes(struct hid_sensor_custom &sensor_inst->fields[i]. hid_custom_attribute_group); if (ret) - break; + goto err_remove_groups; /* For power or report field store indexes */ if (sensor_inst->fields[i].attribute.attrib_id == @@ -621,6 +621,13 @@ static int hid_sensor_custom_add_attributes(struct hid_sensor_custom } return ret; + +err_remove_groups: + while (--i >= 0) + sysfs_remove_group(&sensor_inst->pdev->dev.kobj, + &sensor_inst->fields[i].hid_custom_attribute_group); + kfree(sensor_inst->fields); + return ret; } static void hid_sensor_custom_remove_attributes(struct hid_sensor_custom * diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 34f710c465b80a..6470a290ebfc5b 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -239,12 +239,17 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, u32 field_index, int buffer_size, void *buffer) { struct hid_report *report; + struct hid_field *field; struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); - int report_size; + size_t field_size; + size_t report_size; + size_t copied = 0; + size_t to_copy; int ret = 0; - u8 *val_ptr; - int buffer_index = 0; - int i; + unsigned int i; + + if (!buffer || buffer_size <= 0) + return -EINVAL; memset(buffer, 0, buffer_size); @@ -258,26 +263,29 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); hid_hw_wait(hsdev->hdev); + field = report->field[field_index]; + /* calculate number of bytes required to read this field */ - report_size = DIV_ROUND_UP(report->field[field_index]->report_size, - 8) * - report->field[field_index]->report_count; - if (!report_size) { + field_size = DIV_ROUND_UP(field->report_size, 8); + /* HID core stores each parsed report value in a __s32 slot. */ + if (!field_size || field_size > sizeof(field->value[0])) { + ret = -EINVAL; + goto done_proc; + } + if (field->report_count > SIZE_MAX / field_size) { ret = -EINVAL; goto done_proc; } - ret = min(report_size, buffer_size); - val_ptr = (u8 *)report->field[field_index]->value; - for (i = 0; i < report->field[field_index]->report_count; ++i) { - if (buffer_index >= ret) - break; + report_size = field_size * field->report_count; + report_size = min_t(size_t, report_size, buffer_size); - memcpy(&((u8 *)buffer)[buffer_index], val_ptr, - report->field[field_index]->report_size / 8); - val_ptr += sizeof(__s32); - buffer_index += (report->field[field_index]->report_size / 8); + for (i = 0; i < field->report_count && copied < report_size; ++i) { + to_copy = min(field_size, report_size - copied); + memcpy(&((u8 *)buffer)[copied], &field->value[i], to_copy); + copied += to_copy; } + ret = copied; done_proc: mutex_unlock(&data->mutex); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index e75246d29e16d1..af1ad2c2337079 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -29,6 +29,7 @@ * There will be no PIN request from the device. */ +#include #include #include #include @@ -569,14 +570,12 @@ static void sony_set_leds(struct sony_sc *sc); static inline void sony_schedule_work(struct sony_sc *sc, enum sony_worker which) { - unsigned long flags; - switch (which) { case SONY_WORKER_STATE: - spin_lock_irqsave(&sc->lock, flags); - if (!sc->defer_initialization && sc->state_worker_initialized) - schedule_work(&sc->state_worker); - spin_unlock_irqrestore(&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { + if (!sc->defer_initialization && sc->state_worker_initialized) + schedule_work(&sc->state_worker); + } break; } } @@ -949,7 +948,6 @@ static const u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc, static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size) { static const u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 }; - unsigned long flags; int offset; u8 index; u8 battery_capacity; @@ -972,10 +970,10 @@ static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size) battery_status = POWER_SUPPLY_STATUS_DISCHARGING; } - spin_lock_irqsave(&sc->lock, flags); - sc->battery_capacity = battery_capacity; - sc->battery_status = battery_status; - spin_unlock_irqrestore(&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { + sc->battery_capacity = battery_capacity; + sc->battery_status = battery_status; + } if (sc->quirks & SIXAXIS_CONTROLLER) { int val; @@ -1092,7 +1090,6 @@ static void rb4_ps5_guitar_parse_report(struct sony_sc *sc, u8 *rd, int size) u8 battery_data; u8 battery_capacity; u8 battery_status; - unsigned long flags; /* * Rock Band 4 PS5 guitars have whammy and @@ -1132,10 +1129,10 @@ static void rb4_ps5_guitar_parse_report(struct sony_sc *sc, u8 *rd, int size) break; } - spin_lock_irqsave(&sc->lock, flags); - sc->battery_capacity = battery_capacity; - sc->battery_status = battery_status; - spin_unlock_irqrestore(&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { + sc->battery_capacity = battery_capacity; + sc->battery_status = battery_status; + } input_sync(sc->input_dev); } @@ -1869,15 +1866,14 @@ static int sony_battery_get_property(struct power_supply *psy, union power_supply_propval *val) { struct sony_sc *sc = power_supply_get_drvdata(psy); - unsigned long flags; int ret = 0; u8 battery_capacity; int battery_status; - spin_lock_irqsave(&sc->lock, flags); - battery_capacity = sc->battery_capacity; - battery_status = sc->battery_status; - spin_unlock_irqrestore(&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { + battery_capacity = sc->battery_capacity; + battery_status = sc->battery_status; + } switch (psp) { case POWER_SUPPLY_PROP_PRESENT: @@ -1959,10 +1955,9 @@ static inline int sony_compare_connection_type(struct sony_sc *sc0, static int sony_check_add_dev_list(struct sony_sc *sc) { struct sony_sc *entry; - unsigned long flags; int ret; - spin_lock_irqsave(&sony_dev_list_lock, flags); + guard(spinlock_irqsave)(&sony_dev_list_lock); list_for_each_entry(entry, &sony_device_list, list_node) { ret = memcmp(sc->mac_address, entry->mac_address, @@ -1976,27 +1971,23 @@ static int sony_check_add_dev_list(struct sony_sc *sc) "controller with MAC address %pMR already connected\n", sc->mac_address); } - goto unlock; + goto out; } } ret = 0; list_add(&(sc->list_node), &sony_device_list); -unlock: - spin_unlock_irqrestore(&sony_dev_list_lock, flags); +out: return ret; } static void sony_remove_dev_list(struct sony_sc *sc) { - unsigned long flags; + guard(spinlock_irqsave)(&sony_dev_list_lock); - if (sc->list_node.next) { - spin_lock_irqsave(&sony_dev_list_lock, flags); - list_del(&(sc->list_node)); - spin_unlock_irqrestore(&sony_dev_list_lock, flags); - } + if (!list_empty(&sc->list_node)) + list_del_init(&sc->list_node); } static int sony_get_bt_devaddr(struct sony_sc *sc) @@ -2123,16 +2114,21 @@ static inline void sony_init_output_report(struct sony_sc *sc, static inline void sony_cancel_work_sync(struct sony_sc *sc) { - unsigned long flags; - if (sc->state_worker_initialized) { - spin_lock_irqsave(&sc->lock, flags); - sc->state_worker_initialized = 0; - spin_unlock_irqrestore(&sc->lock, flags); + scoped_guard(spinlock_irqsave, &sc->lock) { + sc->state_worker_initialized = 0; + } cancel_work_sync(&sc->state_worker); } } +static void sony_cleanup(struct sony_sc *sc) +{ + sony_cancel_work_sync(sc); + sony_remove_dev_list(sc); + sony_release_device_id(sc); +} + static int sony_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { @@ -2306,9 +2302,7 @@ static int sony_input_configured(struct hid_device *hdev, err_close: hid_hw_close(hdev); err_stop: - sony_cancel_work_sync(sc); - sony_remove_dev_list(sc); - sony_release_device_id(sc); + sony_cleanup(sc); return ret; } @@ -2332,6 +2326,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) return -ENOMEM; spin_lock_init(&sc->lock); + INIT_LIST_HEAD(&sc->list_node); + sc->device_id = -1; sc->quirks = quirks; hid_set_drvdata(hdev, sc); @@ -2360,6 +2356,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_hw_start(hdev, connect_mask); if (ret) { hid_err(hdev, "hw start failed\n"); + sony_cleanup(sc); return ret; } @@ -2414,7 +2411,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) err: usb_free_urb(sc->ghl_urb); - + sony_cleanup(sc); hid_hw_stop(hdev); return ret; } @@ -2424,18 +2421,14 @@ static void sony_remove(struct hid_device *hdev) struct sony_sc *sc = hid_get_drvdata(hdev); if (sc->quirks & (GHL_GUITAR_PS3WIIU | GHL_GUITAR_PS4)) { - timer_delete_sync(&sc->ghl_poke_timer); + /* poison, not kill: a pending timer must not re-submit during teardown */ + usb_poison_urb(sc->ghl_urb); + timer_shutdown_sync(&sc->ghl_poke_timer); usb_free_urb(sc->ghl_urb); } hid_hw_close(hdev); - - sony_cancel_work_sync(sc); - - sony_remove_dev_list(sc); - - sony_release_device_id(sc); - + sony_cleanup(sc); hid_hw_stop(hdev); } diff --git a/drivers/hid/hid-universal-pidff.c b/drivers/hid/hid-universal-pidff.c index 549dac555d4088..60180467a0bfd9 100644 --- a/drivers/hid/hid-universal-pidff.c +++ b/drivers/hid/hid-universal-pidff.c @@ -104,12 +104,14 @@ static int universal_pidff_probe(struct hid_device *hdev, error = init_function(hdev, id->driver_data); if (error) { hid_warn(hdev, "Error initialising force feedback\n"); - goto err; + goto err_stop; } hid_info(hdev, "Universal pidff driver loaded successfully!"); return 0; +err_stop: + hid_hw_stop(hdev); err: return error; } diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c index 46d3e9a019995f..4126c2408d9869 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -774,6 +774,7 @@ static void quicki2c_remove(struct pci_dev *pdev) quicki2c_hid_remove(qcdev); quicki2c_dma_deinit(qcdev); + pm_runtime_dont_use_autosuspend(qcdev->dev); pm_runtime_get_noresume(qcdev->dev); quicki2c_dev_deinit(qcdev); diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c index 4ae2e1718b30cf..5d35edf2b6d86b 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c @@ -555,7 +555,14 @@ static int quickspi_alloc_report_buf(struct quickspi_device *qsdev) max_report_len = max(le16_to_cpu(qsdev->dev_desc.max_output_len), le16_to_cpu(qsdev->dev_desc.max_input_len)); - qsdev->report_buf = devm_kzalloc(qsdev->dev, max_report_len, GFP_KERNEL); + /* + * write_cmd_to_txdma() writes the output report header ahead of the + * content in this buffer, so it has to hold both. + */ + qsdev->report_buf_size = HIDSPI_OUTPUT_REPORT_SIZE(max_report_len); + + qsdev->report_buf = devm_kzalloc(qsdev->dev, qsdev->report_buf_size, + GFP_KERNEL); if (!qsdev->report_buf) return -ENOMEM; @@ -713,6 +720,7 @@ static void quickspi_remove(struct pci_dev *pdev) quickspi_hid_remove(qsdev); quickspi_dma_deinit(qsdev); + pm_runtime_dont_use_autosuspend(qsdev->dev); pm_runtime_get_noresume(qsdev->dev); quickspi_dev_deinit(qsdev); diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h index bf5e18f5a5f425..0ed964bfe3dd2d 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h @@ -157,6 +157,7 @@ struct quickspi_device { u8 *report_descriptor; u8 *input_buf; u8 *report_buf; + u32 report_buf_size; u32 report_len; wait_queue_head_t reset_ack_wq; diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c index 91d5807b4a8302..a60a0a7f16aad3 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c @@ -61,7 +61,7 @@ static int quickspi_hid_raw_request(struct hid_device *hid, switch (reqtype) { case HID_REQ_GET_REPORT: - ret = quickspi_get_report(qsdev, rtype, reportnum, buf); + ret = quickspi_get_report(qsdev, rtype, reportnum, buf, len); break; case HID_REQ_SET_REPORT: ret = quickspi_set_report(qsdev, rtype, reportnum, buf, len); diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c index cb19057f1191ba..847c5ec55569a2 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c @@ -30,6 +30,9 @@ static int write_cmd_to_txdma(struct quickspi_device *qsdev, write_buf = (struct output_report *)qsdev->report_buf; + if (HIDSPI_OUTPUT_REPORT_SIZE(report_buf_len) > qsdev->report_buf_size) + return -EINVAL; + write_buf->output_hdr.report_type = report_type; write_buf->output_hdr.content_len = cpu_to_le16(report_buf_len); write_buf->output_hdr.content_id = report_id; @@ -342,10 +345,12 @@ int reset_tic(struct quickspi_device *qsdev) } int quickspi_get_report(struct quickspi_device *qsdev, - u8 report_type, unsigned int report_id, void *buf) + u8 report_type, unsigned int report_id, void *buf, + u32 buf_len) { int rep_type; int ret; + u32 report_len; if (report_type == HID_INPUT_REPORT) { rep_type = GET_INPUT_REPORT; @@ -372,9 +377,17 @@ int quickspi_get_report(struct quickspi_device *qsdev, } qsdev->get_report_cmpl = false; - memcpy(buf, qsdev->report_buf, qsdev->report_len); + /* quickspi_handle_input_data() updates this from IRQ context. */ + report_len = READ_ONCE(qsdev->report_len); + if (report_len > buf_len) { + dev_err_once(qsdev->dev, "Get report response too big, %u vs %u\n", + report_len, buf_len); + return -EINVAL; + } + + memcpy(buf, qsdev->report_buf, report_len); - return qsdev->report_len; + return report_len; } int quickspi_set_report(struct quickspi_device *qsdev, diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h index 775e29c1ed1366..8a2338bee80856 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h @@ -12,7 +12,7 @@ struct quickspi_device; void quickspi_handle_input_data(struct quickspi_device *qsdev, u32 buf_len); int quickspi_get_report(struct quickspi_device *qsdev, u8 report_type, - unsigned int report_id, void *buf); + unsigned int report_id, void *buf, u32 buf_len); int quickspi_set_report(struct quickspi_device *qsdev, u8 report_type, unsigned int report_id, void *buf, u32 buf_len); int quickspi_get_report_descriptor(struct quickspi_device *qsdev); diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index da1f0ea85625dc..af76e49fd0f258 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1548,6 +1548,19 @@ static int wacom_intuos_pro2_bt_irq(struct wacom_wac *wacom, size_t len) return 0; } + if (wacom->features.type == INTUOSP2_BT || + wacom->features.type == INTUOSP2S_BT) { + if (len < 286) { + dev_warn(wacom->pen_input->dev.parent, + "Pro2 BT report too short: %zu bytes\n", len); + return 0; + } + } else if (len < 46) { + dev_warn(wacom->pen_input->dev.parent, + "Pro2 BT report too short: %zu bytes\n", len); + return 0; + } + wacom_intuos_pro2_bt_pen(wacom); if (wacom->features.type == INTUOSP2_BT || wacom->features.type == INTUOSP2S_BT) { diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c index eeacc427fd6590..47826315d7d628 100644 --- a/drivers/hsi/controllers/omap_ssi_core.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -502,6 +502,12 @@ static int ssi_probe(struct platform_device *pd) pm_runtime_enable(&pd->dev); + ssi->device.dma_mask = &ssi->device.coherent_dma_mask; + + err = dma_set_mask_and_coherent(&ssi->device, DMA_BIT_MASK(32)); + if (err) + goto out2; + err = ssi_hw_init(ssi); if (err < 0) goto out2; diff --git a/drivers/hwmon/max6621.c b/drivers/hwmon/max6621.c index e86ec6d237ca63..1a644d78714314 100644 --- a/drivers/hwmon/max6621.c +++ b/drivers/hwmon/max6621.c @@ -17,8 +17,8 @@ #define MAX6621_DRV_NAME "max6621" #define MAX6621_TEMP_INPUT_REG_NUM 9 -#define MAX6621_TEMP_INPUT_MIN -127000 -#define MAX6621_TEMP_INPUT_MAX 128000 +#define MAX6621_TEMP_INPUT_MIN -128000 +#define MAX6621_TEMP_INPUT_MAX 127000 #define MAX6621_TEMP_ALERT_CHAN_SHIFT 1 #define MAX6621_TEMP_S0D0_REG 0x00 @@ -239,7 +239,7 @@ max6621_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, if (ret) return ret; - *val = (regval >> MAX6621_REG_TEMP_SHIFT) * + *val = ((s16)regval >> MAX6621_REG_TEMP_SHIFT) * 1000L; break; @@ -254,7 +254,7 @@ max6621_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, if (ret) return ret; - *val = regval * 1000L; + *val = (s16)regval * 1000L; break; case hwmon_temp_crit_alarm: diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 762109307b869f..b3c67e96a82a49 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -717,26 +717,19 @@ static DEVICE_ATTR_RW(cntr_rld_event); static ssize_t cntr_val_show(struct device *dev, struct device_attribute *attr, char *buf) { - int i, ret = 0; u32 val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; if (!coresight_get_mode(drvdata->csdev)) { spin_lock(&drvdata->spinlock); - for (i = 0; i < drvdata->nr_cntr; i++) - ret += sprintf(buf, "counter %d: %x\n", - i, config->cntr_val[i]); + val = config->cntr_val[config->cntr_idx]; spin_unlock(&drvdata->spinlock); - return ret; - } - - for (i = 0; i < drvdata->nr_cntr; i++) { - val = etm_readl(drvdata, ETMCNTVRn(i)); - ret += sprintf(buf, "counter %d: %x\n", i, val); + } else { + val = etm_readl(drvdata, ETMCNTVRn(config->cntr_idx)); } - return ret; + return sysfs_emit(buf, "%#x\n", val); } static ssize_t cntr_val_store(struct device *dev, diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 94c371c4913571..b5d851281fbf08 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -171,13 +171,13 @@ static bool hisi_ptt_wait_trace_hw_idle(struct hisi_ptt *hisi_ptt) HISI_PTT_WAIT_TRACE_TIMEOUT_US); } -static void hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt) +static bool hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt) { u32 val; - readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS, - val, !val, HISI_PTT_RESET_POLL_INTERVAL_US, - HISI_PTT_RESET_TIMEOUT_US); + return !readl_poll_timeout_atomic(hisi_ptt->iobase + HISI_PTT_TRACE_WR_STS, + val, !val, HISI_PTT_RESET_POLL_INTERVAL_US, + HISI_PTT_RESET_TIMEOUT_US); } static void hisi_ptt_trace_end(struct hisi_ptt *hisi_ptt) @@ -202,14 +202,18 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) return -EBUSY; } - ctrl->started = true; - /* Reset the DMA before start tracing */ val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); val |= HISI_PTT_TRACE_CTRL_RST; writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); - hisi_ptt_wait_dma_reset_done(hisi_ptt); + if (!hisi_ptt_wait_dma_reset_done(hisi_ptt)) { + pci_err(hisi_ptt->pdev, "timed out waiting for DMA reset\n"); + val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + val &= ~HISI_PTT_TRACE_CTRL_RST; + writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); + return -ETIMEDOUT; + } val = readl(hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); val &= ~HISI_PTT_TRACE_CTRL_RST; @@ -234,6 +238,8 @@ static int hisi_ptt_trace_start(struct hisi_ptt *hisi_ptt) if (!hisi_ptt->trace_ctrl.is_port) val |= HISI_PTT_TRACE_CTRL_FILTER_MODE; + ctrl->started = true; + /* Start the Trace */ val |= HISI_PTT_TRACE_CTRL_EN; writel(val, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 6d6e81242f74e1..447af5523c2e4f 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -272,7 +272,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = { { "HISI02A1", 0 }, { "HISI02A2", 0 }, { "HISI02A3", 0 }, - { "HJMC3001", 0 }, + { "HJMC3001", ACCESS_INTR_MASK }, { "HYGO0010", ACCESS_INTR_MASK }, { "INT33C2", 0 }, { "INT33C3", 0 }, diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 4e07babea9c3f4..eee4fdcd9df31a 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -839,7 +839,7 @@ static int mxs_i2c_probe(struct platform_device *pdev) } /* Setup the DMA */ - i2c->dmach = dma_request_chan(dev, "rx-tx"); + i2c->dmach = devm_dma_request_chan(dev, "rx-tx"); if (IS_ERR(i2c->dmach)) { return dev_err_probe(dev, PTR_ERR(i2c->dmach), "Failed to request dma\n"); @@ -877,9 +877,6 @@ static void mxs_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&i2c->adapter); - if (i2c->dmach) - dma_release_channel(i2c->dmach); - writel(MXS_I2C_CTRL0_SFTRST, i2c->regs + MXS_I2C_CTRL0_SET); } diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index bdeda3979c4814..25b6e4e9e3fa31 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -595,9 +595,11 @@ static int cci_probe(struct platform_device *pdev) goto disable_clocks; pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); + ret = devm_pm_runtime_set_active_enabled(dev); + if (ret) + goto disable_clocks; + pm_runtime_use_autosuspend(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); for (i = 0; i < cci->data->num_masters; i++) { if (!cci->master[i].cci) @@ -613,8 +615,6 @@ static int cci_probe(struct platform_device *pdev) return 0; error_i2c: - pm_runtime_disable(dev); - pm_runtime_dont_use_autosuspend(dev); for (--i ; i >= 0; i--) { if (cci->master[i].cci) { @@ -640,9 +640,6 @@ static void cci_remove(struct platform_device *pdev) cci_halt(cci, i); } } - - pm_runtime_disable(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); } static const struct cci_data cci_v1_data = { diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index d2f5055b0b1086..9a02e57c5a4bd0 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -177,8 +177,8 @@ static const struct geni_i2c_clk_fld geni_i2c_clk_map_19p2mhz[] = { /* source_clock = 32 MHz */ static const struct geni_i2c_clk_fld geni_i2c_clk_map_32mhz[] = { - { I2C_MAX_STANDARD_MODE_FREQ, 8, 14, 18, 38 }, - { I2C_MAX_FAST_MODE_FREQ, 4, 3, 9, 19 }, + { I2C_MAX_STANDARD_MODE_FREQ, 12, 9, 10, 26 }, + { I2C_MAX_FAST_MODE_FREQ, 4, 3, 9, 19 }, { I2C_MAX_FAST_MODE_PLUS_FREQ, 2, 3, 5, 15 }, {} }; diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 3ec04787a7373f..17cf3d225268fe 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1815,6 +1815,12 @@ void i2c_del_adapter(struct i2c_adapter *adap) return; } + /* + * This drains any in-flight writers, so all + * clients will be caught by i2c_deregister_clients(). + */ + device_remove_file(&adap->dev, &dev_attr_new_device); + i2c_acpi_remove_space_handler(adap); i2c_deregister_clients(adap); diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 681a201c239ba0..68a4c34b5987d5 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -408,6 +408,7 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, return 0; err_free_priv: + of_node_put(priv->adap.dev.of_node); kfree(priv); return ret; } diff --git a/drivers/i2c/muxes/i2c-demux-pinctrl.c b/drivers/i2c/muxes/i2c-demux-pinctrl.c index f2a1f47449789c..2403c0bf7c4319 100644 --- a/drivers/i2c/muxes/i2c-demux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-demux-pinctrl.c @@ -247,6 +247,7 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev) props[i].value = devm_kstrdup(&pdev->dev, "ok", GFP_KERNEL); if (!props[i].name || !props[i].value) { err = -ENOMEM; + of_node_put(adap_np); goto err_rollback; } props[i].length = 3; diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c index 101eaa77de6860..a3778282e84c28 100644 --- a/drivers/i3c/device.c +++ b/drivers/i3c/device.c @@ -309,7 +309,7 @@ EXPORT_SYMBOL_GPL(i3c_device_match_id); */ u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev) { - return i3c_dev_get_master(dev->desc)->this->info.hdr_cap | BIT(I3C_SDR); + return i3c_bus_to_i3c_master(dev->bus)->this->info.hdr_cap | BIT(I3C_SDR); } EXPORT_SYMBOL_GPL(i3c_device_get_supported_xfer_mode); diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h index 0f1f3f76662379..86a36b951e0d57 100644 --- a/drivers/i3c/internals.h +++ b/drivers/i3c/internals.h @@ -72,4 +72,9 @@ static inline void i3c_readl_fifo(const void __iomem *addr, void *buf, } } +static inline struct i3c_master_controller *i3c_bus_to_i3c_master(struct i3c_bus *i3cbus) +{ + return container_of(i3cbus, struct i3c_master_controller, bus); +} + #endif /* I3C_INTERNAL_H */ diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index f1be38a640ca11..b51b159fadeed2 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -97,12 +97,6 @@ void i3c_bus_normaluse_unlock(struct i3c_bus *bus) up_read(&bus->lock); } -static struct i3c_master_controller * -i3c_bus_to_i3c_master(struct i3c_bus *i3cbus) -{ - return container_of(i3cbus, struct i3c_master_controller, bus); -} - static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev) { return container_of(dev, struct i3c_master_controller, dev); @@ -2977,11 +2971,12 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master) if (!i3cdev->dev) continue; - i3cdev->dev->desc = NULL; - if (device_is_registered(&i3cdev->dev->dev)) + if (device_is_registered(&i3cdev->dev->dev)) { + get_device(&i3cdev->dev->dev); device_unregister(&i3cdev->dev->dev); - else - put_device(&i3cdev->dev->dev); + } + i3cdev->dev->desc = NULL; + put_device(&i3cdev->dev->dev); i3cdev->dev = NULL; } } diff --git a/drivers/i3c/master/adi-i3c-master.c b/drivers/i3c/master/adi-i3c-master.c index 047081c9f06439..e29aac28695775 100644 --- a/drivers/i3c/master/adi-i3c-master.c +++ b/drivers/i3c/master/adi-i3c-master.c @@ -964,17 +964,9 @@ static int adi_i3c_master_probe(struct platform_device *pdev) writel(0x00, master->regs + REG_ENABLE); writel(0x00, master->regs + REG_IRQ_MASK); - ret = devm_request_irq(&pdev->dev, irq, adi_i3c_master_irq, 0, - dev_name(&pdev->dev), master); - if (ret) - return ret; - platform_set_drvdata(pdev, master); master->free_rr_slots = GENMASK(ADI_MAX_DEVS, 1); - - writel(REG_IRQ_PENDING_CMDR, master->regs + REG_IRQ_MASK); - spin_lock_init(&master->ibi.lock); master->ibi.num_slots = 15; master->ibi.slots = devm_kcalloc(&pdev->dev, master->ibi.num_slots, @@ -986,6 +978,13 @@ static int adi_i3c_master_probe(struct platform_device *pdev) spin_lock_init(&master->xferqueue.lock); INIT_LIST_HEAD(&master->xferqueue.list); + ret = devm_request_irq(&pdev->dev, irq, adi_i3c_master_irq, 0, + dev_name(&pdev->dev), master); + if (ret) + return ret; + + writel(REG_IRQ_PENDING_CMDR, master->regs + REG_IRQ_MASK); + return i3c_master_register(&master->base, &pdev->dev, &adi_i3c_master_ops, false); } diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c index f39c449922ca26..c459e40fd5ffc3 100644 --- a/drivers/i3c/master/renesas-i3c.c +++ b/drivers/i3c/master/renesas-i3c.c @@ -260,11 +260,11 @@ struct renesas_i3c { u32 dyn_addr; u32 i2c_STDBR; u32 i3c_STDBR; + u32 extbr; unsigned long rate; u8 addrs[RENESAS_I3C_MAX_DEVS]; struct renesas_i3c_xferqueue xferqueue; void __iomem *regs; - u32 *DATBASn; struct clk_bulk_data *clks; struct reset_control *presetn; struct reset_control *tresetn; @@ -433,6 +433,21 @@ static void renesas_i3c_enqueue_xfer(struct renesas_i3c *i3c, struct renesas_i3c } } +static void renesas_i3c_irqs_mask_and_clear_locked(struct renesas_i3c *i3c) +{ + /* Disable all the interrupts. */ + renesas_writel(i3c->regs, BIE, 0); + renesas_writel(i3c->regs, NTIE, 0); + + /* Clear normal transfer status flags. */ + renesas_writel(i3c->regs, NTST, 0); + + /* Clear bus status flags. */ + renesas_writel(i3c->regs, BST, 0); + /* Read back registers to confirm writes have fully propagated. */ + renesas_readl(i3c->regs, BST); +} + static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer) { unsigned long time_left; @@ -607,10 +622,9 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m) renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR); /* Extended Bit Rate setting */ - renesas_writel(i3c->regs, EXTBR, EXTBR_EBRLO(od_low_ticks) | - EXTBR_EBRHO(od_high_ticks) | - EXTBR_EBRLP(pp_low_ticks) | - EXTBR_EBRHP(pp_high_ticks)); + i3c->extbr = EXTBR_EBRLO(od_low_ticks) | EXTBR_EBRHO(od_high_ticks) | + EXTBR_EBRLP(pp_low_ticks) | EXTBR_EBRHP(pp_high_ticks); + renesas_writel(i3c->regs, EXTBR, i3c->extbr); renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks)); i3c->refclk_div = cks; @@ -689,7 +703,11 @@ static int renesas_i3c_daa(struct i3c_master_controller *m) renesas_i3c_wait_xfer(i3c, xfer); - newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, 0); + if (cmd->rx_count >= i3c->maxdevs) + newdevs = 0; + else + newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, 0); + newdevs &= ~olddevs; for (pos = 0; pos < i3c->maxdevs; pos++) { @@ -892,10 +910,26 @@ static int renesas_i3c_reattach_i3c_dev(struct i3c_dev_desc *dev, struct i3c_master_controller *m = i3c_dev_get_master(dev); struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + int pos; + + pos = renesas_i3c_get_free_pos(i3c); + + if (data->index > pos && pos >= 0) { + renesas_writel(i3c->regs, DATBAS(data->index), 0); + i3c->addrs[data->index] = 0; + i3c->free_pos |= BIT(data->index); + + data->index = pos; + i3c->free_pos &= ~BIT(data->index); + } i3c->addrs[data->index] = dev->info.dyn_addr ? dev->info.dyn_addr : dev->info.static_addr; + renesas_writel(i3c->regs, DATBAS(data->index), + DATBAS_DVSTAD(dev->info.static_addr) | + datbas_dvdyad_with_parity(i3c->addrs[data->index])); + return 0; } @@ -905,6 +939,8 @@ static void renesas_i3c_detach_i3c_dev(struct i3c_dev_desc *dev) struct i3c_master_controller *m = i3c_dev_get_master(dev); struct renesas_i3c *i3c = to_renesas_i3c(m); + renesas_writel(i3c->regs, DATBAS(data->index), 0); + i3c_dev_set_master_data(dev, NULL); i3c->addrs[data->index] = 0; i3c->free_pos |= BIT(data->index); @@ -1014,6 +1050,11 @@ static irqreturn_t renesas_i3c_tx_isr(int irq, void *data) scoped_guard(spinlock, &i3c->xferqueue.lock) { xfer = i3c->xferqueue.cur; + if (!xfer) { + renesas_i3c_irqs_mask_and_clear_locked(i3c); + return IRQ_HANDLED; + } + cmd = xfer->cmds; if (xfer->is_i2c_xfer) { @@ -1054,6 +1095,11 @@ static irqreturn_t renesas_i3c_resp_isr(int irq, void *data) scoped_guard(spinlock, &i3c->xferqueue.lock) { xfer = i3c->xferqueue.cur; + if (!xfer) { + renesas_i3c_irqs_mask_and_clear_locked(i3c); + return IRQ_HANDLED; + } + cmd = xfer->cmds; /* Clear the Respone Queue Full status flag*/ @@ -1138,6 +1184,11 @@ static irqreturn_t renesas_i3c_tend_isr(int irq, void *data) scoped_guard(spinlock, &i3c->xferqueue.lock) { xfer = i3c->xferqueue.cur; + if (!xfer) { + renesas_i3c_irqs_mask_and_clear_locked(i3c); + return IRQ_HANDLED; + } + cmd = xfer->cmds; if (xfer->is_i2c_xfer) { @@ -1184,6 +1235,11 @@ static irqreturn_t renesas_i3c_rx_isr(int irq, void *data) scoped_guard(spinlock, &i3c->xferqueue.lock) { xfer = i3c->xferqueue.cur; + if (!xfer) { + renesas_i3c_irqs_mask_and_clear_locked(i3c); + return IRQ_HANDLED; + } + cmd = xfer->cmds; if (xfer->is_i2c_xfer) { @@ -1234,15 +1290,13 @@ static irqreturn_t renesas_i3c_stop_isr(int irq, void *data) struct renesas_i3c_xfer *xfer; scoped_guard(spinlock, &i3c->xferqueue.lock) { - xfer = i3c->xferqueue.cur; - - /* read back registers to confirm writes have fully propagated */ - renesas_writel(i3c->regs, BST, 0); - renesas_readl(i3c->regs, BST); - renesas_writel(i3c->regs, BIE, 0); - renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0 | NTST_RDBFF0); + renesas_i3c_irqs_mask_and_clear_locked(i3c); renesas_clear_bit(i3c->regs, SCSTRCTL, SCSTRCTL_RWE); + xfer = i3c->xferqueue.cur; + if (!xfer) + return IRQ_HANDLED; + xfer->ret = 0; complete(&xfer->comp); } @@ -1259,6 +1313,11 @@ static irqreturn_t renesas_i3c_start_isr(int irq, void *data) scoped_guard(spinlock, &i3c->xferqueue.lock) { xfer = i3c->xferqueue.cur; + if (!xfer) { + renesas_i3c_irqs_mask_and_clear_locked(i3c); + return IRQ_HANDLED; + } + cmd = xfer->cmds; if (xfer->is_i2c_xfer) { @@ -1365,12 +1424,6 @@ static int renesas_i3c_probe(struct platform_device *pdev) i3c->maxdevs = RENESAS_I3C_MAX_DEVS; i3c->free_pos = GENMASK(i3c->maxdevs - 1, 0); - /* Allocate dynamic Device Address Table backup. */ - i3c->DATBASn = devm_kzalloc(&pdev->dev, sizeof(u32) * i3c->maxdevs, - GFP_KERNEL); - if (!i3c->DATBASn) - return -ENOMEM; - return i3c_master_register(&i3c->base, &pdev->dev, &renesas_i3c_ops, false); } @@ -1381,17 +1434,13 @@ static void renesas_i3c_remove(struct platform_device *pdev) i3c_master_unregister(&i3c->base); } -static int renesas_i3c_suspend_noirq(struct device *dev) +static int renesas_i3c_suspend(struct device *dev) { struct renesas_i3c *i3c = dev_get_drvdata(dev); - int i, ret; + int ret; i2c_mark_adapter_suspended(&i3c->base.i2c); - /* Store Device Address Table values. */ - for (i = 0; i < i3c->maxdevs; i++) - i3c->DATBASn[i] = renesas_readl(i3c->regs, DATBAS(i)); - ret = reset_control_assert(i3c->presetn); if (ret) goto err_mark_resumed; @@ -1412,50 +1461,61 @@ static int renesas_i3c_suspend_noirq(struct device *dev) return ret; } -static int renesas_i3c_resume_noirq(struct device *dev) +static int renesas_i3c_resume(struct device *dev) { struct renesas_i3c *i3c = dev_get_drvdata(dev); - int i, ret; + int ret; - ret = reset_control_deassert(i3c->presetn); + ret = reset_control_deassert(i3c->tresetn); if (ret) return ret; - ret = reset_control_deassert(i3c->tresetn); + ret = reset_control_deassert(i3c->presetn); if (ret) - goto err_presetn; + goto err_tresetn; ret = clk_bulk_enable(i3c->num_clks, i3c->clks); if (ret) - goto err_tresetn; + goto err_presetn; + + ret = renesas_i3c_reset(i3c); + if (ret) + goto err_clks_disable; /* Re-store I3C registers value. */ + renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR); + renesas_writel(i3c->regs, EXTBR, i3c->extbr); renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(i3c->refclk_div)); renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYADV | MSDVAD_MDYAD(i3c->dyn_addr)); - /* Restore Device Address Table values. */ - for (i = 0; i < i3c->maxdevs; i++) - renesas_writel(i3c->regs, DATBAS(i), i3c->DATBASn[i]); - /* I3C hw init. */ renesas_i3c_hw_init(i3c); + ret = i3c_master_do_daa_ext(&i3c->base, true); + if (ret) + dev_err(dev, "DAA failed on resume, ret=%d", ret); + i2c_mark_adapter_resumed(&i3c->base.i2c); + /* + * I3C devices may have retained their dynamic address anyway. Do not + * fail the resume because of DAA error. + */ return 0; -err_tresetn: - reset_control_assert(i3c->tresetn); +err_clks_disable: + clk_bulk_disable(i3c->num_clks, i3c->clks); err_presetn: reset_control_assert(i3c->presetn); +err_tresetn: + reset_control_assert(i3c->tresetn); return ret; } static const struct dev_pm_ops renesas_i3c_pm_ops = { - NOIRQ_SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend_noirq, - renesas_i3c_resume_noirq) + SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend, renesas_i3c_resume) }; static const struct of_device_id renesas_i3c_of_ids[] = { diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index 93805df8a9406e..71f1fc6933233a 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -455,14 +455,22 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master, buf = slot->data; while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS)) && - slot->len < SVC_I3C_FIFO_SIZE) { + slot->len < dev->ibi->max_payload_len) { mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL); count = SVC_I3C_MDATACTRL_RXCOUNT(mdatactrl); + count = min(count, dev->ibi->max_payload_len - slot->len); readsb(master->regs + SVC_I3C_MRDATAB, buf, count); slot->len += count; buf += count; } + /* + * The device may have sent more than the requested payload. Drop the + * extra bytes so they do not leak into the next transfer. + */ + if (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS))) + writel(SVC_I3C_MDATACTRL_FLUSHRB, master->regs + SVC_I3C_MDATACTRL); + master->ibi.tbq_slot = slot; return 0; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 3755a81c1efdb7..c6d3cf68b1f71e 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1076,6 +1076,7 @@ config MAX1363 config MAX14001 tristate "Analog Devices MAX14001/MAX14002 ADC driver" depends on SPI + select REGMAP help Say yes here to build support for Analog Devices MAX14001/MAX14002 Configurable, Isolated 10-bit ADCs for Multi-Range Binary Inputs. @@ -1086,6 +1087,7 @@ config MAX14001 config MAX34408 tristate "Maxim max34408/max344089 ADC driver" depends on I2C + select REGMAP_I2C help Say yes here to build support for Maxim max34408/max34409 current sense monitor with 8-bits ADC interface with overcurrent delay/threshold and diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index 0797d64aeaecae..967144eee3eb35 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -697,6 +697,11 @@ static int ad4080_setup_channel(struct ad4080_state *st, unsigned int ch) if (ret) return ret; + ret = iio_backend_data_size_set(st->back[ch], + st->info->channels[0].scan_type.realbits); + if (ret) + return ret; + if (!st->lvds_cnv_en) return 0; diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 26b9c75bd4d8ea..589f03618ecf3a 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -53,6 +53,10 @@ #define AXI_AD485X_PACKET_FORMAT_24BIT 0x1 #define AXI_AD485X_PACKET_FORMAT_32BIT 0x2 #define AXI_AD408X_CNTRL_3_FILTER_EN_MSK BIT(0) +#define AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(3, 2) +#define AXI_AD408X_PACKET_FORMAT_20BIT 0x0 +#define AXI_AD408X_PACKET_FORMAT_16BIT 0x1 +#define AXI_AD408X_PACKET_FORMAT_14BIT 0x2 #define ADI_AXI_ADC_REG_SYNC_STATUS 0x0068 #define ADI_AXI_ADC_SYNC_STATUS_ADC_SYNC_MSK BIT(0) @@ -436,6 +440,31 @@ static int axi_adc_ad408x_filter_type_set(struct iio_backend *back, AXI_AD408X_CNTRL_3_FILTER_EN_MSK); } +static int axi_adc_ad408x_data_size_set(struct iio_backend *back, + unsigned int size) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + unsigned int val; + + switch (size) { + case 20: + val = AXI_AD408X_PACKET_FORMAT_20BIT; + break; + case 16: + val = AXI_AD408X_PACKET_FORMAT_16BIT; + break; + case 14: + val = AXI_AD408X_PACKET_FORMAT_14BIT; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK, + FIELD_PREP(AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK, val)); +} + static int axi_adc_ad408x_interface_data_align(struct iio_backend *back, u32 timeout_us) { @@ -659,6 +688,7 @@ static const struct iio_backend_ops adi_ad408x_ops = { .free_buffer = axi_adc_free_buffer, .data_sample_trigger = axi_adc_data_sample_trigger, .filter_type_set = axi_adc_ad408x_filter_type_set, + .data_size_set = axi_adc_ad408x_data_size_set, .interface_data_align = axi_adc_ad408x_interface_data_align, .num_lanes_set = axi_adc_num_lanes_set, .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access), diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c index 68bdd2f30badaf..f0bab94462f469 100644 --- a/drivers/iio/adc/pac1921.c +++ b/drivers/iio/adc/pac1921.c @@ -1037,7 +1037,7 @@ static irqreturn_t pac1921_trigger_handler(int irq, void *p) iio_for_each_active_channel(idev, bit) { u16 val; - ret = pac1921_read_res(priv, idev->channels[ch].address, &val); + ret = pac1921_read_res(priv, idev->channels[bit].address, &val); if (ret) goto done; diff --git a/drivers/iio/adc/ti-ads7138.c b/drivers/iio/adc/ti-ads7138.c index af87f5f19a0fac..da82a6947b2b1b 100644 --- a/drivers/iio/adc/ti-ads7138.c +++ b/drivers/iio/adc/ti-ads7138.c @@ -227,6 +227,26 @@ static int ads7138_osr_to_bits(int osr) return -EINVAL; } +static int ads7138_read_statistics(const struct i2c_client *client, u8 reg, + u8 *out_values, u8 length) +{ + int ret; + + /* Disable statistics update so the value is not updated mid read */ + ret = ads7138_i2c_clear_bit(client, ADS7138_REG_GENERAL_CFG, + ADS7138_GENERAL_CFG_STATS_EN); + if (ret) + return ret; + + ret = ads7138_i2c_read_block(client, reg, out_values, length); + if (ret) + return ret; + + /* Enable statistics update after read */ + return ads7138_i2c_set_bit(client, ADS7138_REG_GENERAL_CFG, + ADS7138_GENERAL_CFG_STATS_EN); +} + static int ads7138_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -236,28 +256,32 @@ static int ads7138_read_raw(struct iio_dev *indio_dev, u8 values[2]; switch (mask) { + /* + * Reading the statistics registers reinitializes them. This is + * unfortunate but necessary to prevent data races. + */ case IIO_CHAN_INFO_RAW: - ret = ads7138_i2c_read_block(data->client, - ADS7138_REG_RECENT_LSB_CH(chan->channel), - values, ARRAY_SIZE(values)); + ret = ads7138_read_statistics(data->client, + ADS7138_REG_RECENT_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); if (ret) return ret; *val = get_unaligned_le16(values); return IIO_VAL_INT; case IIO_CHAN_INFO_PEAK: - ret = ads7138_i2c_read_block(data->client, - ADS7138_REG_MAX_LSB_CH(chan->channel), - values, ARRAY_SIZE(values)); + ret = ads7138_read_statistics(data->client, + ADS7138_REG_MAX_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); if (ret) return ret; *val = get_unaligned_le16(values); return IIO_VAL_INT; case IIO_CHAN_INFO_TROUGH: - ret = ads7138_i2c_read_block(data->client, - ADS7138_REG_MIN_LSB_CH(chan->channel), - values, ARRAY_SIZE(values)); + ret = ads7138_read_statistics(data->client, + ADS7138_REG_MIN_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); if (ret) return ret; diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index 1e8adbe1790d06..1d4a9919df1a0f 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -87,7 +86,6 @@ struct atlas_data { struct iio_trigger *trig; const struct atlas_device *chip; struct regmap *regmap; - struct irq_work work; unsigned int interrupt_enabled; /* 96-bit data + 32-bit pad + 64-bit timestamp */ __be32 buffer[6] __aligned(8); @@ -412,7 +410,11 @@ static int atlas_buffer_postenable(struct iio_dev *indio_dev) if (ret) return ret; - return atlas_set_interrupt(data, true); + ret = atlas_set_interrupt(data, true); + if (ret) + pm_runtime_put_autosuspend(&data->client->dev); + + return ret; } static int atlas_buffer_predisable(struct iio_dev *indio_dev) @@ -436,13 +438,6 @@ static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = { .predisable = atlas_buffer_predisable, }; -static void atlas_work_handler(struct irq_work *work) -{ - struct atlas_data *data = container_of(work, struct atlas_data, work); - - iio_trigger_poll(data->trig); -} - static irqreturn_t atlas_trigger_handler(int irq, void *private) { struct iio_poll_func *pf = private; @@ -469,7 +464,7 @@ static irqreturn_t atlas_interrupt_handler(int irq, void *private) struct iio_dev *indio_dev = private; struct atlas_data *data = iio_priv(indio_dev); - irq_work_queue(&data->work); + iio_trigger_poll_nested(data->trig); return IRQ_HANDLED; } @@ -665,8 +660,6 @@ static int atlas_probe(struct i2c_client *client) goto unregister_trigger; } - init_irq_work(&data->work, atlas_work_handler); - if (client->irq > 0) { /* interrupt pin toggles on new conversion */ ret = devm_request_threaded_irq(&client->dev, client->irq, diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c index f10bbebc29e4ef..379c1c4af8d883 100644 --- a/drivers/iio/chemical/sgp30.c +++ b/drivers/iio/chemical/sgp30.c @@ -548,6 +548,9 @@ static int sgp_probe(struct i2c_client *client) data->iaq_thread = kthread_run(sgp_iaq_threadfn, data, "%s-iaq", data->client->name); + if (IS_ERR(data->iaq_thread)) + return dev_err_probe(dev, PTR_ERR(data->iaq_thread), + "failed to start IAQ thread\n"); return 0; } diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 657c68e75542e4..14a246729d2bcf 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -552,6 +552,7 @@ config MCP4728 config MCP47FEB02 tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 DAC driver" depends on I2C + select REGMAP_I2C help Say yes here if you want to build the driver for the Microchip: - 8-bit DAC: diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c index 02a124ac4855db..f865843aa4393e 100644 --- a/drivers/iio/dac/ad3552r-hs.c +++ b/drivers/iio/dac/ad3552r-hs.c @@ -590,7 +590,7 @@ static ssize_t ad3552r_hs_show_data_source_avail(struct file *f, int i; for (i = 0; i < ARRAY_SIZE(dbgfs_attr_source); i++) { - len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", + len += scnprintf(buf + len, sizeof(buf) - len, "%s ", dbgfs_attr_source[i]); } buf[len - 1] = '\n'; diff --git a/drivers/iio/dac/ad5446-i2c.c b/drivers/iio/dac/ad5446-i2c.c index 9797fc3e57a9b2..286695e240c363 100644 --- a/drivers/iio/dac/ad5446-i2c.c +++ b/drivers/iio/dac/ad5446-i2c.c @@ -83,7 +83,7 @@ static const struct of_device_id ad5446_i2c_of_ids[] = { { .compatible = "adi,ad5622", .data = &ad5622_chip_info }, { } }; -MODULE_DEVICE_TABLE(OF, ad5446_i2c_of_ids); +MODULE_DEVICE_TABLE(of, ad5446_i2c_of_ids); static struct i2c_driver ad5446_i2c_driver = { .driver = { diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c index 7e80c0eb5cc1ac..60bb672b70e20e 100644 --- a/drivers/iio/dac/m62332.c +++ b/drivers/iio/dac/m62332.c @@ -32,6 +32,7 @@ static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel) { struct m62332_data *data = iio_priv(indio_dev); struct i2c_client *client = data->client; + bool enabling, disabling; u8 outbuf[2]; int res; @@ -43,7 +44,10 @@ static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel) mutex_lock(&data->mutex); - if (val) { + enabling = val && !data->raw[channel]; + disabling = !val && data->raw[channel]; + + if (enabling) { res = regulator_enable(data->vcc); if (res) goto out; @@ -52,14 +56,17 @@ static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel) res = i2c_master_send(client, outbuf, ARRAY_SIZE(outbuf)); if (res >= 0 && res != ARRAY_SIZE(outbuf)) res = -EIO; - if (res < 0) + if (res < 0) { + if (enabling) + regulator_disable(data->vcc); goto out; + } - data->raw[channel] = val; - - if (!val) + if (disabling) regulator_disable(data->vcc); + data->raw[channel] = val; + mutex_unlock(&data->mutex); return 0; diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index d84e04e4b43142..07c0f9011c6371 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -356,7 +356,7 @@ static int mpu3050_read_raw(struct iio_dev *indio_dev, goto out_read_raw_unlock; } - *val = be16_to_cpu(raw_val); + *val = (s16)be16_to_cpu(raw_val); ret = IIO_VAL_INT; goto out_read_raw_unlock; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c index 2e72c7ba94dd17..5321033daebcb7 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_fusion.c @@ -112,6 +112,11 @@ int st_lsm6dsx_fusion_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) return err; } + if (enable) + hw->enable_mask |= BIT(ST_LSM6DSX_ID_FUSION); + else + hw->enable_mask &= ~BIT(ST_LSM6DSX_ID_FUSION); + return st_lsm6dsx_fusion_page_disable(hw); } diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 531fc4ccc15de0..2c9ec93dff475e 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -47,9 +47,6 @@ struct iio_dmabuf_priv { u64 context; - /* Spinlock used for locking the dma_fence */ - spinlock_t lock; - struct dma_buf_attachment *attach; struct sg_table *sgt; enum dma_data_direction dir; @@ -57,7 +54,12 @@ struct iio_dmabuf_priv { }; struct iio_dma_fence { + /* + * Must remain the first member so the default release callback can pass + * the fence directly to dma_fence_free(). + */ struct dma_fence base; + spinlock_t lock; /* protects base */ struct iio_dmabuf_priv *priv; struct work_struct work; }; @@ -1619,12 +1621,16 @@ static int iio_buffer_chrdev_release(struct inode *inode, struct file *filep) wake_up(&buffer->pollq); - guard(mutex)(&buffer->dmabufs_mutex); - - /* Close all attached DMABUFs */ - list_for_each_entry_safe(priv, tmp, &buffer->dmabufs, entry) { - list_del_init(&priv->entry); - iio_buffer_dmabuf_put(priv->attach); + /* + * The mutex must be unlocked before iio_device_put(), which might drop the + * last reference and free the buffer. + */ + scoped_guard(mutex, &buffer->dmabufs_mutex) { + /* Close all attached DMABUFs */ + list_for_each_entry_safe(priv, tmp, &buffer->dmabufs, entry) { + list_del_init(&priv->entry); + iio_buffer_dmabuf_put(priv->attach); + } } kfree(ib); @@ -1702,7 +1708,6 @@ static int iio_buffer_attach_dmabuf(struct iio_dev_buffer_pair *ib, if (!priv) return -ENOMEM; - spin_lock_init(&priv->lock); priv->context = dma_fence_context_alloc(1); dmabuf = dma_buf_get(fd); @@ -1827,18 +1832,9 @@ iio_buffer_dma_fence_get_driver_name(struct dma_fence *fence) return "iio"; } -static void iio_buffer_dma_fence_release(struct dma_fence *fence) -{ - struct iio_dma_fence *iio_fence = - container_of(fence, struct iio_dma_fence, base); - - kfree(iio_fence); -} - static const struct dma_fence_ops iio_buffer_dma_fence_ops = { .get_driver_name = iio_buffer_dma_fence_get_driver_name, .get_timeline_name = iio_buffer_dma_fence_get_driver_name, - .release = iio_buffer_dma_fence_release, }; static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib, @@ -1892,6 +1888,8 @@ static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib, goto err_attachment_put; } + spin_lock_init(&fence->lock); + fence->priv = priv; seqno = atomic_add_return(1, &priv->seqno); @@ -1902,7 +1900,7 @@ static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib, * the dma_fence. */ dma_fence_init(&fence->base, &iio_buffer_dma_fence_ops, - &priv->lock, priv->context, seqno); + &fence->lock, priv->context, seqno); ret = iio_dma_resv_lock(dmabuf, nonblock); if (ret) diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c index 5ca4c87524fe2a..d582bda4d84760 100644 --- a/drivers/iio/light/apds9306.c +++ b/drivers/iio/light/apds9306.c @@ -469,9 +469,9 @@ static int apds9306_read_data(struct apds9306_data *data, int *val, int reg) int status = 0; u8 buff[3]; - ret = pm_runtime_resume_and_get(data->dev); - if (ret) - return ret; + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(data->dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return PM_RUNTIME_ACQUIRE_ERR(&pm); ret = regmap_field_read(rf->intg_time, &intg_time_idx); if (ret) @@ -535,8 +535,6 @@ static int apds9306_read_data(struct apds9306_data *data, int *val, int reg) *val = get_unaligned_le24(&buff); - pm_runtime_put_autosuspend(data->dev); - return 0; } diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c index 2590fc8fd1547f..b32a94028f091e 100644 --- a/drivers/iio/light/cm32181.c +++ b/drivers/iio/light/cm32181.c @@ -368,7 +368,7 @@ static int cm32181_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_CALIBSCALE: cm32181->calibscale = val; - return val; + return 0; case IIO_CHAN_INFO_INT_TIME: ret = cm32181_write_als_it(cm32181, val2); return ret; diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index a8db514cca5ef5..05773e24931beb 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -669,7 +669,7 @@ static int gp2ap002_runtime_resume(struct device *dev) ret = regulator_enable(gp2ap002->vio); if (ret) { dev_err(dev, "failed to enable VIO regulator in resume path\n"); - return ret; + goto out_disable_vdd; } msleep(20); @@ -677,13 +677,19 @@ static int gp2ap002_runtime_resume(struct device *dev) ret = gp2ap002_init(gp2ap002); if (ret) { dev_err(dev, "re-initialization failed\n"); - return ret; + goto out_disable_vio; } /* Re-activate the IRQ */ enable_irq(gp2ap002->irq); return 0; + +out_disable_vio: + regulator_disable(gp2ap002->vio); +out_disable_vdd: + regulator_disable(gp2ap002->vdd); + return ret; } static DEFINE_RUNTIME_DEV_PM_OPS(gp2ap002_dev_pm_ops, gp2ap002_runtime_suspend, diff --git a/drivers/iio/light/ltrf216a.c b/drivers/iio/light/ltrf216a.c index 3f34ddc911b4ad..0a8851342039ee 100644 --- a/drivers/iio/light/ltrf216a.c +++ b/drivers/iio/light/ltrf216a.c @@ -247,11 +247,10 @@ static int ltrf216a_get_lux(struct ltrf216a_data *data) return ret; greendata = ltrf216a_read_data(data, LTRF216A_ALS_DATA_0); + ltrf216a_set_power_state(data, false); if (greendata < 0) return greendata; - ltrf216a_set_power_state(data, false); - lux = greendata * data->info->lux_multiplier * LTRF216A_WIN_FAC; return lux; diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c index dd152d921b48e3..aa3d87995b7c65 100644 --- a/drivers/iio/light/opt4001.c +++ b/drivers/iio/light/opt4001.c @@ -39,7 +39,7 @@ #define OPT4001_CTRL_OPER_MODE_MASK GENMASK(5, 4) #define OPT4001_CTRL_LATCH_MASK GENMASK(3, 3) #define OPT4001_CTRL_INT_POL_MASK GENMASK(2, 2) -#define OPT4001_CTRL_FAULT_COUNT GENMASK(0, 1) +#define OPT4001_CTRL_FAULT_COUNT_MASK GENMASK(1, 0) /* OPT4001 constants */ #define OPT4001_DEVICE_ID_VAL 0x121 @@ -173,6 +173,7 @@ static int opt4001_read_lux_value(struct iio_dev *indio_dev, u8 crc; u8 calc_crc; u64 lux_raw; + u32 rem; int ret; ret = regmap_read(chip->regmap, OPT4001_LIGHT1_MSB, &light1); @@ -199,8 +200,8 @@ static int opt4001_read_lux_value(struct iio_dev *indio_dev, lux_raw = lux_raw << exp; lux_raw = lux_raw * chip->chip_info->mul; - *val = div_u64_rem(lux_raw, chip->chip_info->div, val2); - *val2 = *val2 * 100; + *val = div_u64_rem(lux_raw, chip->chip_info->div, &rem); + *val2 = rem * 100; return IIO_VAL_INT_PLUS_NANO; } @@ -222,33 +223,14 @@ static int opt4001_set_conf(struct opt4001_chip *chip) return ret; } -static int opt4001_power_down(struct opt4001_chip *chip) -{ - struct device *dev = &chip->client->dev; - int ret; - unsigned int reg; - - ret = regmap_read(chip->regmap, OPT4001_DEVICE_ID, ®); - if (ret) { - dev_err(dev, "Failed to read configuration\n"); - return ret; - } - - /* MODE_OFF is 0x0 so just set bits to 0 */ - reg &= ~OPT4001_CTRL_OPER_MODE_MASK; - - ret = regmap_write(chip->regmap, OPT4001_CTRL, reg); - if (ret) - dev_err(dev, "Failed to set configuration to power down\n"); - - return ret; -} - static void opt4001_chip_off_action(void *data) { struct opt4001_chip *chip = data; + int ret; - opt4001_power_down(chip); + ret = regmap_clear_bits(chip->regmap, OPT4001_CTRL, OPT4001_CTRL_OPER_MODE_MASK); + if (ret) + dev_err(&chip->client->dev, "Failed to power down\n"); } static const struct iio_chan_spec opt4001_channels[] = { @@ -287,6 +269,9 @@ static int opt4001_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_INT_TIME: + if (val) + return -EINVAL; + int_time = opt4001_als_time_to_index(val2); if (int_time < 0) return int_time; diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c index c391ad3271c631..d248db1c42e8a3 100644 --- a/drivers/iio/light/opt4060.c +++ b/drivers/iio/light/opt4060.c @@ -632,6 +632,9 @@ static int opt4060_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_INT_TIME: + if (val) + return -EINVAL; + int_time = opt4060_als_time_to_index(val2); if (int_time < 0) return int_time; @@ -805,7 +808,7 @@ static int opt4060_get_thresholds(struct opt4060_chip *chip, u32 *th_lo, u32 *th ret = regmap_read(chip->regmap, OPT4060_THRESHOLD_HIGH, ®val); if (ret) { - dev_err(chip->dev, "Failed to read THRESHOLD_LOW.\n"); + dev_err(chip->dev, "Failed to read THRESHOLD_HIGH.\n"); return ret; } *th_hi = opt4060_calc_val_from_th_reg(regval); diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index f45af72a055432..45bdb8c7670f06 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -845,7 +845,6 @@ static const struct iio_info dps310_info = { static int dps310_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct dps310_data *data; struct iio_dev *iio; int rc; @@ -858,7 +857,7 @@ static int dps310_probe(struct i2c_client *client) data->client = client; mutex_init(&data->lock); - iio->name = id->name; + iio->name = DPS310_DEV_NAME; iio->channels = dps310_channels; iio->num_channels = ARRAY_SIZE(dps310_channels); iio->info = &dps310_info; diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c index 16e112b796ba9e..2929ea8e4e01b5 100644 --- a/drivers/iio/pressure/mpl115.c +++ b/drivers/iio/pressure/mpl115.c @@ -203,9 +203,9 @@ int mpl115_probe(struct device *dev, const char *name, if (data->shutdown) { /* Enable runtime PM */ - pm_runtime_get_noresume(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + ret = pm_runtime_set_active(dev); + if (ret) + return ret; /* * As the device takes 3 ms to come up with a fresh @@ -215,7 +215,10 @@ int mpl115_probe(struct device *dev, const char *name, */ pm_runtime_set_autosuspend_delay(dev, 2000); pm_runtime_use_autosuspend(dev); - pm_runtime_put(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; dev_dbg(dev, "low-power mode enabled"); } else diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 7be50bdebfcbcd..e03f3f0dee04cd 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -330,6 +330,7 @@ static int srf04_probe(struct platform_device *pdev) if (ret) { dev_err(data->dev, "pm_runtime_set_active: %d\n", ret); iio_device_unregister(indio_dev); + return ret; } pm_runtime_enable(data->dev); diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index 54a00253de1148..58d4c02739323b 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -243,7 +243,7 @@ static int hid_temperature_probe(struct platform_device *pdev) if (ret) goto error_remove_trigger; - ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); + ret = iio_device_register(indio_dev); if (ret) goto error_remove_callback; @@ -263,6 +263,7 @@ static void hid_temperature_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct temperature_state *temp_st = iio_priv(indio_dev); + iio_device_unregister(indio_dev); sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE); hid_sensor_remove_trigger(indio_dev, &temp_st->common_attributes); } diff --git a/drivers/infiniband/core/ib_core_uverbs.c b/drivers/infiniband/core/ib_core_uverbs.c index dbbc0875132a30..41c84ffe8c0930 100644 --- a/drivers/infiniband/core/ib_core_uverbs.c +++ b/drivers/infiniband/core/ib_core_uverbs.c @@ -424,6 +424,9 @@ static uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata) lockdep_assert_held(&bundle->ufile->device->disassociate_srcu); + if (!bundle->method_elm) + return NULL; + return srcu_dereference(bundle->method_elm->handler, &bundle->ufile->device->disassociate_srcu); } diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 878561fa1cb557..ac29dfa69bb3a6 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -1404,7 +1404,10 @@ static int ucma_set_ib_path(struct ucma_context *ctx, memset(&event, 0, sizeof event); event.event = RDMA_CM_EVENT_ROUTE_RESOLVED; - return ucma_event_handler(ctx->cm_id, &event); + rdma_lock_handler(ctx->cm_id); + ret = ucma_event_handler(ctx->cm_id, &event); + rdma_unlock_handler(ctx->cm_id); + return ret; } static int ucma_set_option_ib(struct ucma_context *ctx, int optname, @@ -1776,6 +1779,13 @@ static ssize_t ucma_write_cm_event(struct ucma_file *file, goto out; } + rdma_lock_handler(ctx->cm_id); + if (!ctx->uid) { + kfree(uevent); + ret = -EINVAL; + goto err_unlock; + } + uevent->ctx = ctx; uevent->resp.uid = ctx->uid; uevent->resp.id = ctx->id; @@ -1789,6 +1799,8 @@ static ssize_t ucma_write_cm_event(struct ucma_file *file, mutex_unlock(&ctx->file->mut); wake_up_interruptible(&ctx->file->poll_wait); +err_unlock: + rdma_unlock_handler(ctx->cm_id); out: ucma_put_ctx(ctx); return ret; diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 3ccf58e96aedeb..0d88b2ee68ffa1 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -556,6 +556,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, bundle.ufile = file; bundle.context = NULL; /* only valid if bundle has uobject */ bundle.uobject = NULL; + bundle.method_elm = NULL; if (!method_elm->is_ex) { size_t in_len = hdr.in_words * 4 - sizeof(hdr); size_t out_len = hdr.out_words * 4; diff --git a/drivers/infiniband/core/uverbs_std_types_mr.c b/drivers/infiniband/core/uverbs_std_types_mr.c index 570b9656801d72..0c72f801e0d32d 100644 --- a/drivers/infiniband/core/uverbs_std_types_mr.c +++ b/drivers/infiniband/core/uverbs_std_types_mr.c @@ -364,7 +364,8 @@ static int UVERBS_HANDLER(UVERBS_METHOD_REG_MR)( dmah, attrs); else mr = pd->device->ops.reg_user_mr(pd, addr, length, iova, - access_flags, dmah, NULL); + access_flags, dmah, + &attrs->driver_udata); if (IS_ERR(mr)) return PTR_ERR(mr); @@ -527,7 +528,8 @@ DECLARE_UVERBS_NAMED_METHOD( UA_MANDATORY), UVERBS_ATTR_PTR_OUT(UVERBS_ATTR_REG_MR_RESP_RKEY, UVERBS_ATTR_TYPE(u32), - UA_MANDATORY)); + UA_MANDATORY), + UVERBS_ATTR_UHW()); DECLARE_UVERBS_NAMED_METHOD_DESTROY( UVERBS_METHOD_MR_DESTROY, diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index 102c5646b9ed61..c1815972aecff7 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -951,6 +951,12 @@ void c4iw_dealloc(struct uld_ctx *ctx) static void c4iw_remove(struct uld_ctx *ctx) { pr_debug("c4iw_dev %p\n", ctx->dev); + + /* c4iw_register_device() may still be using ctx->dev. */ + cancel_work_sync(&ctx->reg_work); + if (!ctx->dev) + return; + debugfs_remove_recursive(ctx->dev->debugfs_root); c4iw_unregister_device(ctx->dev); c4iw_dealloc(ctx); diff --git a/drivers/infiniband/hw/ionic/ionic_hw_stats.c b/drivers/infiniband/hw/ionic/ionic_hw_stats.c index f72c9837e135ef..4f0a2dedbdfe78 100644 --- a/drivers/infiniband/hw/ionic/ionic_hw_stats.c +++ b/drivers/infiniband/hw/ionic/ionic_hw_stats.c @@ -235,35 +235,34 @@ static int ionic_get_hw_stats(struct ib_device *ibdev, static struct rdma_hw_stats * ionic_counter_alloc_stats(struct rdma_counter *counter) { + struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter); struct ionic_ibdev *dev = to_ionic_ibdev(counter->device); - struct ionic_counter *cntr; - int err; + struct rdma_hw_stats *stats; + int id; - cntr = kzalloc_obj(*cntr); - if (!cntr) - return NULL; - - /* buffer for current values from the device */ cntr->vals = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!cntr->vals) - goto err_vals; + return NULL; - err = xa_alloc(&dev->counter_stats->xa_counters, &counter->id, - cntr, - XA_LIMIT(0, IONIC_MAX_QPID), - GFP_KERNEL); - if (err) - goto err_xa; + id = ida_alloc_max(&dev->counter_stats->counter_ida, + IONIC_MAX_QPID, GFP_KERNEL); + if (id < 0) + goto err_ida; - INIT_LIST_HEAD(&cntr->qp_list); + counter->id = id; + + stats = rdma_alloc_hw_stats_struct(dev->counter_stats->stats_hdrs, + dev->counter_stats->queue_stats_count, + RDMA_HW_STATS_DEFAULT_LIFESPAN); + if (!stats) + goto err_hw_stats; + + return stats; - return rdma_alloc_hw_stats_struct(dev->counter_stats->stats_hdrs, - dev->counter_stats->queue_stats_count, - RDMA_HW_STATS_DEFAULT_LIFESPAN); -err_xa: +err_hw_stats: + ida_free(&dev->counter_stats->counter_ida, id); +err_ida: kfree(cntr->vals); -err_vals: - kfree(cntr); return NULL; } @@ -271,14 +270,10 @@ ionic_counter_alloc_stats(struct rdma_counter *counter) static int ionic_counter_dealloc(struct rdma_counter *counter) { struct ionic_ibdev *dev = to_ionic_ibdev(counter->device); - struct ionic_counter *cntr; - - cntr = xa_erase(&dev->counter_stats->xa_counters, counter->id); - if (!cntr) - return -EINVAL; + struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter); + ida_free(&dev->counter_stats->counter_ida, counter->id); kfree(cntr->vals); - kfree(cntr); return 0; } @@ -287,13 +282,8 @@ static int ionic_counter_bind_qp(struct rdma_counter *counter, struct ib_qp *ibqp, u32 port) { - struct ionic_ibdev *dev = to_ionic_ibdev(counter->device); + struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter); struct ionic_qp *qp = to_ionic_qp(ibqp); - struct ionic_counter *cntr; - - cntr = xa_load(&dev->counter_stats->xa_counters, counter->id); - if (!cntr) - return -EINVAL; list_add_tail(&qp->qp_list_counter, &cntr->qp_list); ibqp->counter = counter; @@ -313,29 +303,23 @@ static int ionic_counter_unbind_qp(struct ib_qp *ibqp, u32 port) return 0; } -static int ionic_get_qp_stats(struct ib_device *ibdev, - struct rdma_hw_stats *hw_stats, - u32 counter_id) +static int ionic_counter_update_stats(struct rdma_counter *counter) { - struct ionic_ibdev *dev = to_ionic_ibdev(ibdev); - struct ionic_counter_stats *cs; - struct ionic_counter *cntr; + struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter); + struct ionic_ibdev *dev = to_ionic_ibdev(counter->device); + struct ionic_counter_stats *cs = dev->counter_stats; dma_addr_t hw_stats_dma; struct ionic_qp *qp; int rc, stat_i = 0; - cs = dev->counter_stats; - cntr = xa_load(&cs->xa_counters, counter_id); - if (!cntr) - return -EINVAL; - hw_stats_dma = dma_map_single(dev->lif_cfg.hwdev, cntr->vals, PAGE_SIZE, DMA_FROM_DEVICE); rc = dma_mapping_error(dev->lif_cfg.hwdev, hw_stats_dma); if (rc) return rc; - memset(hw_stats->value, 0, sizeof(u64) * hw_stats->num_counters); + memset(counter->stats->value, 0, + sizeof(u64) * counter->stats->num_counters); list_for_each_entry(qp, &cntr->qp_list, qp_list_counter) { rc = ionic_hw_stats_cmd(dev, hw_stats_dma, PAGE_SIZE, @@ -345,7 +329,7 @@ static int ionic_get_qp_stats(struct ib_device *ibdev, goto err_cmd; for (stat_i = 0; stat_i < cs->queue_stats_count; ++stat_i) - hw_stats->value[stat_i] += + counter->stats->value[stat_i] += ionic_v1_stat_val(&cs->hdr[stat_i], cntr->vals, PAGE_SIZE); @@ -360,11 +344,6 @@ static int ionic_get_qp_stats(struct ib_device *ibdev, return rc; } -static int ionic_counter_update_stats(struct rdma_counter *counter) -{ - return ionic_get_qp_stats(counter->device, counter->stats, counter->id); -} - static int ionic_alloc_counters(struct ionic_ibdev *dev) { struct ionic_counter_stats *cs = dev->counter_stats; @@ -424,12 +403,22 @@ static const struct ib_device_ops ionic_hw_stats_ops = { .get_hw_stats = ionic_get_hw_stats, }; +static void ionic_counter_init(struct rdma_counter *counter) +{ + struct ionic_rdma_counter *cntr = to_ionic_rdma_counter(counter); + + INIT_LIST_HEAD(&cntr->qp_list); +} + static const struct ib_device_ops ionic_counter_stats_ops = { .counter_alloc_stats = ionic_counter_alloc_stats, .counter_dealloc = ionic_counter_dealloc, .counter_bind_qp = ionic_counter_bind_qp, .counter_unbind_qp = ionic_counter_unbind_qp, .counter_update_stats = ionic_counter_update_stats, + .counter_init = ionic_counter_init, + + INIT_RDMA_OBJ_SIZE(rdma_counter, ionic_rdma_counter, rdma_counter), }; void ionic_stats_init(struct ionic_ibdev *dev) @@ -458,7 +447,7 @@ void ionic_stats_init(struct ionic_ibdev *dev) return; } - xa_init_flags(&dev->counter_stats->xa_counters, XA_FLAGS_ALLOC); + ida_init(&dev->counter_stats->counter_ida); ib_set_device_ops(&dev->ibdev, &ionic_counter_stats_ops); } @@ -467,7 +456,7 @@ void ionic_stats_init(struct ionic_ibdev *dev) void ionic_stats_cleanup(struct ionic_ibdev *dev) { if (dev->counter_stats) { - xa_destroy(&dev->counter_stats->xa_counters); + ida_destroy(&dev->counter_stats->counter_ida); kfree(dev->counter_stats->hdr); kfree(dev->counter_stats->stats_hdrs); kfree(dev->counter_stats); diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.h b/drivers/infiniband/hw/ionic/ionic_ibdev.h index 63828240d6590b..1c43344ec93ce1 100644 --- a/drivers/infiniband/hw/ionic/ionic_ibdev.h +++ b/drivers/infiniband/hw/ionic/ionic_ibdev.h @@ -331,14 +331,21 @@ struct ionic_counter_stats { int queue_stats_count; struct ionic_v1_stat *hdr; struct rdma_stat_desc *stats_hdrs; - struct xarray xa_counters; + struct ida counter_ida; }; -struct ionic_counter { +struct ionic_rdma_counter { + struct rdma_counter rdma_counter; void *vals; struct list_head qp_list; }; +static inline struct ionic_rdma_counter * +to_ionic_rdma_counter(struct rdma_counter *counter) +{ + return container_of(counter, struct ionic_rdma_counter, rdma_counter); +} + static inline struct ionic_ibdev *to_ionic_ibdev(struct ib_device *ibdev) { return container_of(ibdev, struct ionic_ibdev, ibdev); diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c index f3cd281c3a2f86..45846dc2e89b2d 100644 --- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c +++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c @@ -70,7 +70,7 @@ void ionic_fill_lif_cfg(struct ionic_lif *lif, struct ionic_lif_cfg *cfg) * eq_count is tunable; see ionic_eq_count */ cfg->aq_count = le32_to_cpu(ident->rdma.aq_qtype.qid_count); - cfg->eq_count = le32_to_cpu(ident->rdma.eq_qtype.qid_count); + cfg->eq_count = lif->ionic->neqs_per_lif; cfg->cq_count = le32_to_cpu(ident->rdma.cq_qtype.qid_count); cfg->qp_count = le32_to_cpu(ident->rdma.sq_qtype.qid_count); cfg->dbid_count = le32_to_cpu(lif->ionic->ident.dev.ndbpgs_per_lif); diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index f3dff1aea96afc..370d98385c9347 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -172,6 +172,7 @@ static void free_rd_atomic_resources(struct rxe_qp *qp) } kfree(qp->resp.resources); qp->resp.resources = NULL; + qp->resp.res = NULL; } } @@ -707,13 +708,24 @@ int rxe_qp_from_attr(struct rxe_qp *qp, struct ib_qp_attr *attr, int mask, int max_dest_rd_atomic = attr->max_dest_rd_atomic ? roundup_pow_of_two(attr->max_dest_rd_atomic) : 0; - qp->attr.max_dest_rd_atomic = max_dest_rd_atomic; - + /* + * Not gated by IB_QP_STATE, so the responder task is live. + * Quiesce recv_task like rxe_qp_reset() before swapping the + * rd_atomic array, so rxe_receiver() cannot race the free/ + * realloc. + */ + rxe_disable_task(&qp->recv_task); free_rd_atomic_resources(qp); - + qp->attr.max_dest_rd_atomic = max_dest_rd_atomic; err = alloc_rd_atomic_resources(qp, max_dest_rd_atomic); + /* + * On ENOMEM leave recv_task quiesced: qp->resp.resources is + * NULL and rxe_prepare_res()/find_resource() would deref it. + * Re-enable only after a fresh array is installed. + */ if (err) return err; + rxe_enable_task(&qp->recv_task); } if (mask & IB_QP_EN_SQD_ASYNC_NOTIFY) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 73a9db2c7537ff..4aa991a5410183 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -548,7 +548,7 @@ struct icc_path *of_icc_get_by_index(struct device *dev, int idx) path->name = kasprintf(GFP_KERNEL, "%s-%s", src_data->node->name, dst_data->node->name); if (!path->name) { - kfree(path); + icc_put(path); path = ERR_PTR(-ENOMEM); } @@ -646,8 +646,9 @@ struct icc_path *icc_get(struct device *dev, const char *src, const char *dst) path->name = kasprintf(GFP_KERNEL, "%s-%s", src_node->name, dst_node->name); if (!path->name) { - kfree(path); - path = ERR_PTR(-ENOMEM); + mutex_unlock(&icc_lock); + icc_put(path); + return ERR_PTR(-ENOMEM); } out: mutex_unlock(&icc_lock); diff --git a/drivers/iommu/amd/ppr.c b/drivers/iommu/amd/ppr.c index 1f8d2823bea42c..80369ca1e316ef 100644 --- a/drivers/iommu/amd/ppr.c +++ b/drivers/iommu/amd/ppr.c @@ -151,7 +151,7 @@ static void iommu_call_iopf_notifier(struct amd_iommu *iommu, u64 *raw) /* Submit event */ iommu_report_device_fault(&pdev->dev, &event); - + pci_dev_put(pdev); return; out: @@ -159,6 +159,7 @@ static void iommu_call_iopf_notifier(struct amd_iommu *iommu, u64 *raw) amd_iommu_complete_ppr(&pdev->dev, PPR_PASID(raw[0]), IOMMU_PAGE_RESP_FAILURE, PPR_TAG(raw[0]) & 0x1FF); + pci_dev_put(pdev); } void amd_iommu_poll_ppr_log(struct amd_iommu *iommu) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c index 1ed8a6f29dc445..4cd60d3c450acc 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c @@ -92,16 +92,6 @@ void arm_smmu_make_sva_cd(struct arm_smmu_cd *target, target->data[1] = cpu_to_le64(virt_to_phys(mm->pgd) & CTXDESC_CD_1_TTB0_MASK); - - /* - * Enable Hardware Access and Dirty updates (DBM) if supported. - * This is safe to enable by default, as PTE_WRITE and PTE_DBM - * share the same bit. - */ - if (master->smmu->features & ARM_SMMU_FEAT_HA) - target->data[0] |= cpu_to_le64(CTXDESC_CD_0_TCR_HA); - if (master->smmu->features & ARM_SMMU_FEAT_HD) - target->data[0] |= cpu_to_le64(CTXDESC_CD_0_TCR_HD); } else { target->data[0] |= cpu_to_le64(CTXDESC_CD_0_TCR_EPD0); @@ -114,6 +104,17 @@ void arm_smmu_make_sva_cd(struct arm_smmu_cd *target, target->data[0] &= cpu_to_le64(~(CTXDESC_CD_0_S | CTXDESC_CD_0_R)); } + /* + * Enable Hardware Access and Dirty updates (DBM) if supported. This is + * safe to enable by default, as PTE_WRITE and PTE_DBM share the same bit, + * while the EPD0 config can't get as far as fetching any PTEs anyway. + */ + if (master->smmu->features & ARM_SMMU_FEAT_HA) + target->data[0] |= cpu_to_le64(CTXDESC_CD_0_TCR_HA); + if (master->smmu->features & ARM_SMMU_FEAT_HD) + target->data[0] |= cpu_to_le64(CTXDESC_CD_0_TCR_HD); + if (master->smmu->features & ARM_SMMU_FEAT_HAFT && system_supports_haft()) + target->data[1] |= cpu_to_le64(CTXDESC_CD_1_HAFT); /* * MAIR value is pretty much constant and global, so we can just get it @@ -211,6 +212,9 @@ bool arm_smmu_sva_supported(struct arm_smmu_device *smmu) if (system_supports_bbml2_noabort()) feat_mask |= ARM_SMMU_FEAT_BBML2; + if (system_supports_haft()) + feat_mask |= ARM_SMMU_FEAT_HAFT; + if ((smmu->features & feat_mask) != feat_mask) return false; diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 10f105a0b29fad..5f933d806a1478 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -4398,6 +4398,20 @@ int arm_smmu_cmdq_init(struct arm_smmu_device *smmu, return 0; } +static void arm_smmu_free_iopf_action(void *data) +{ + struct iopf_queue *queue = data; + + iopf_queue_free(queue); +} + +static void arm_smmu_destroy_vmid_map(void *data) +{ + struct ida *ida = data; + + ida_destroy(ida); +} + static int arm_smmu_init_queues(struct arm_smmu_device *smmu) { int ret; @@ -4425,6 +4439,11 @@ static int arm_smmu_init_queues(struct arm_smmu_device *smmu) smmu->evtq.iopf = iopf_queue_alloc(dev_name(smmu->dev)); if (!smmu->evtq.iopf) return -ENOMEM; + ret = devm_add_action_or_reset(smmu->dev, + arm_smmu_free_iopf_action, + smmu->evtq.iopf); + if (ret) + return ret; } /* priq */ @@ -4503,7 +4522,8 @@ static int arm_smmu_init_strtab(struct arm_smmu_device *smmu) ida_init(&smmu->vmid_map); - return 0; + return devm_add_action_or_reset(smmu->dev, arm_smmu_destroy_vmid_map, + &smmu->vmid_map); } static int arm_smmu_init_structures(struct arm_smmu_device *smmu) @@ -4716,6 +4736,13 @@ static int arm_smmu_device_disable(struct arm_smmu_device *smmu) return ret; } +static void arm_smmu_disable_action(void *data) +{ + struct arm_smmu_device *smmu = data; + + arm_smmu_device_disable(smmu); +} + static void arm_smmu_write_strtab(struct arm_smmu_device *smmu) { struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; @@ -4921,10 +4948,14 @@ static void arm_smmu_device_iidr_probe(struct arm_smmu_device *smmu) static void arm_smmu_get_httu(struct arm_smmu_device *smmu, u32 reg) { - u32 fw_features = smmu->features & (ARM_SMMU_FEAT_HA | ARM_SMMU_FEAT_HD); + u32 fw_features = smmu->features & (ARM_SMMU_FEAT_HA | ARM_SMMU_FEAT_HD | + ARM_SMMU_FEAT_HAFT); u32 hw_features = 0; switch (FIELD_GET(IDR0_HTTU, reg)) { + case IDR0_HTTU_ACCESS_DIRTY_HAFT: + hw_features |= ARM_SMMU_FEAT_HAFT; + fallthrough; case IDR0_HTTU_ACCESS_DIRTY: hw_features |= ARM_SMMU_FEAT_HD; fallthrough; @@ -5256,6 +5287,9 @@ static int arm_smmu_device_acpi_probe(struct platform_device *pdev, smmu->features |= ARM_SMMU_FEAT_COHERENCY; switch (FIELD_GET(ACPI_IORT_SMMU_V3_HTTU_OVERRIDE, iort_smmu->flags)) { + case IDR0_HTTU_ACCESS_DIRTY_HAFT: + smmu->features |= ARM_SMMU_FEAT_HAFT; + fallthrough; case IDR0_HTTU_ACCESS_DIRTY: smmu->features |= ARM_SMMU_FEAT_HD; fallthrough; @@ -5472,7 +5506,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev) /* Initialise in-memory data structures */ ret = arm_smmu_init_structures(smmu); if (ret) - goto err_free_iopf; + return ret; /* Record our private device structure */ platform_set_drvdata(pdev, smmu); @@ -5482,30 +5516,30 @@ static int arm_smmu_device_probe(struct platform_device *pdev) /* Reset the device */ ret = arm_smmu_device_reset(smmu); + if (ret) { + arm_smmu_device_disable(smmu); + return ret; + } + + /* Register last so it unwinds first, while the CMDQ is still up. */ + ret = devm_add_action_or_reset(smmu->dev, arm_smmu_disable_action, smmu); if (ret) - goto err_disable; + return ret; /* And we're up. Go go go! */ ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL, "smmu3.%pa", &ioaddr); if (ret) - goto err_disable; + return ret; ret = iommu_device_register(&smmu->iommu, &arm_smmu_ops, dev); if (ret) { dev_err(dev, "Failed to register iommu\n"); - goto err_free_sysfs; + iommu_device_sysfs_remove(&smmu->iommu); + return ret; } return 0; - -err_free_sysfs: - iommu_device_sysfs_remove(&smmu->iommu); -err_disable: - arm_smmu_device_disable(smmu); -err_free_iopf: - iopf_queue_free(smmu->evtq.iopf); - return ret; } static void arm_smmu_device_remove(struct platform_device *pdev) @@ -5514,9 +5548,6 @@ static void arm_smmu_device_remove(struct platform_device *pdev) iommu_device_unregister(&smmu->iommu); iommu_device_sysfs_remove(&smmu->iommu); - arm_smmu_device_disable(smmu); - iopf_queue_free(smmu->evtq.iopf); - ida_destroy(&smmu->vmid_map); } static void arm_smmu_device_shutdown(struct platform_device *pdev) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h index 1c4877ada1eef0..6859570248e9e8 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h @@ -40,6 +40,7 @@ struct arm_vsmmu; #define IDR0_HTTU GENMASK(7, 6) #define IDR0_HTTU_ACCESS 1 #define IDR0_HTTU_ACCESS_DIRTY 2 +#define IDR0_HTTU_ACCESS_DIRTY_HAFT 3 #define IDR0_COHACC (1 << 4) #define IDR0_TTF GENMASK(3, 2) #define IDR0_TTF_AARCH64 2 @@ -369,6 +370,7 @@ static inline unsigned int arm_smmu_cdtab_l2_idx(unsigned int ssid) #define CTXDESC_CD_0_ASET (1UL << 47) #define CTXDESC_CD_0_ASID GENMASK_ULL(63, 48) +#define CTXDESC_CD_1_HAFT (1UL << 3) #define CTXDESC_CD_1_TTB0_MASK GENMASK_ULL(51, 4) /* @@ -922,6 +924,7 @@ struct arm_smmu_device { #define ARM_SMMU_FEAT_HD (1 << 22) #define ARM_SMMU_FEAT_S2FWB (1 << 23) #define ARM_SMMU_FEAT_BBML2 (1 << 24) +#define ARM_SMMU_FEAT_HAFT (1 << 25) u32 features; #define ARM_SMMU_OPT_SKIP_PREFETCH (1 << 0) diff --git a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c index aaf9ce38bd93f0..e5f52c0f6e9adf 100644 --- a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +++ b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c @@ -56,6 +56,8 @@ #define VINTF_ENABLED BIT(0) #define TEGRA241_VINTF_SID_MATCH(s) (0x0040 + 0x4*(s)) +#define VINTF_SID_MATCH_VIRT_SID GENMASK(20, 1) +#define VINTF_SID_MATCH_ENABLE BIT(0) #define TEGRA241_VINTF_SID_REPLACE(s) (0x0080 + 0x4*(s)) #define TEGRA241_VINTF_LVCMDQ_ERR_MAP_64(m) \ @@ -1189,7 +1191,7 @@ static int tegra241_vintf_init_vsid(struct iommufd_vdevice *vdev) u64 virt_sid = vdev->virt_id; int sidx; - if (virt_sid > UINT_MAX) + if (virt_sid > FIELD_MAX(VINTF_SID_MATCH_VIRT_SID)) return -EINVAL; WARN_ON_ONCE(master->num_streams != 1); @@ -1201,7 +1203,9 @@ static int tegra241_vintf_init_vsid(struct iommufd_vdevice *vdev) return sidx; writel(stream->id, REG_VINTF(vintf, SID_REPLACE(sidx))); - writel(virt_sid << 1 | 0x1, REG_VINTF(vintf, SID_MATCH(sidx))); + writel(FIELD_PREP(VINTF_SID_MATCH_VIRT_SID, virt_sid) | + VINTF_SID_MATCH_ENABLE, + REG_VINTF(vintf, SID_MATCH(sidx))); dev_dbg(vintf->cmdqv->dev, "VINTF%u: allocated SID_REPLACE%d for pSID=%x, vSID=%x\n", vintf->idx, sidx, stream->id, (u32)virt_sid); diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h index c2752151c80af1..07ec2b3ab986a6 100644 --- a/drivers/iommu/generic_pt/iommu_pt.h +++ b/drivers/iommu/generic_pt/iommu_pt.h @@ -224,7 +224,7 @@ phys_addr_t DOMAIN_NS(iova_to_phys)(struct iommu_domain *domain, ret = make_range(common_from_iommu(iommu_table), &range, iova, 1); if (ret) - return ret; + return 0; ret = pt_walk_range(&range, __iova_to_phys, &res); /* PHYS_ADDR_MAX would be a better error code */ diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c index 767ec092accd75..e32685402f74b7 100644 --- a/drivers/iommu/intel/dmar.c +++ b/drivers/iommu/intel/dmar.c @@ -915,6 +915,18 @@ dmar_validate_one_drhd(struct acpi_dmar_header *entry, void *arg) return 0; } +static bool dmar_required(void) +{ + /* tboot supersedes any user/platform opt */ + if (!intel_iommu_tboot_noforce && tboot_enabled()) + return true; + + if (!no_iommu && (!dmar_disabled || dmar_platform_optin())) + return true; + + return false; +} + void __init detect_intel_iommu(void) { int ret; @@ -928,8 +940,7 @@ void __init detect_intel_iommu(void) if (!ret) ret = dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl, &validate_drhd_cb); - if (!ret && !no_iommu && !iommu_detected && - (!dmar_disabled || dmar_platform_optin())) { + if (!ret && !iommu_detected && dmar_required()) { iommu_detected = 1; /* Make sure ACS will be enabled */ pci_request_acs(); diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 6a778fb21b97c9..d7e186d889a180 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -65,7 +65,7 @@ static int rwbf_quirk; * (used when kernel is launched w/ TXT) */ static int force_on = 0; -static int intel_iommu_tboot_noforce; +int intel_iommu_tboot_noforce; static int no_platform_optin; #define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry)) @@ -2498,10 +2498,11 @@ static bool has_external_pci(void) static int __init platform_optin_force_iommu(void) { - if (!dmar_platform_optin() || no_platform_optin || !has_external_pci()) + if (no_iommu || !dmar_platform_optin() || no_platform_optin || + !has_external_pci()) return 0; - if (no_iommu || dmar_disabled) + if (dmar_disabled) pr_info("Intel-IOMMU force enabled due to platform opt in\n"); /* @@ -2512,7 +2513,6 @@ static int __init platform_optin_force_iommu(void) iommu_set_default_passthrough(false); dmar_disabled = 0; - no_iommu = 0; return 1; } diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h index 775f1c4ae34632..2cee36138d6e1e 100644 --- a/drivers/iommu/intel/iommu.h +++ b/drivers/iommu/intel/iommu.h @@ -1354,6 +1354,7 @@ static inline bool ecmd_has_pmu_essential(struct intel_iommu *iommu) extern int dmar_disabled; extern int intel_iommu_enabled; +extern int intel_iommu_tboot_noforce; #else static inline int iommu_calculate_agaw(struct intel_iommu *iommu) { @@ -1366,6 +1367,7 @@ static inline int iommu_calculate_max_sagaw(struct intel_iommu *iommu) #define dmar_disabled (1) #define intel_iommu_enabled (0) #define intel_iommu_sm (0) +#define intel_iommu_tboot_noforce (0) #endif static inline const char *decode_prq_descriptor(char *str, size_t size, diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c index bc7c7232a43e2d..9742cb593577df 100644 --- a/drivers/iommu/iommu-sva.c +++ b/drivers/iommu/iommu-sva.c @@ -145,8 +145,8 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm list_add(&domain->next, &iommu_mm->sva_domains); out: refcount_set(&handle->users, 1); - mutex_unlock(&iommu_sva_lock); handle->dev = dev; + mutex_unlock(&iommu_sva_lock); return handle; out_free_domain: diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index e8f13dcebbde50..23a531595835f4 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -390,7 +390,7 @@ int iommu_mock_device_add(struct device *dev, struct iommu_device *iommu) rc = device_add(dev); if (rc) - iommu_fwspec_free(dev); + dev_iommu_free(dev); return rc; } EXPORT_SYMBOL_GPL(iommu_mock_device_add); diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 2895e5370910fe..d488c23fd35383 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -1307,8 +1307,8 @@ void iommufd_access_notify_unmap(struct io_pagetable *iopt, unsigned long iova, xa_lock(&ioas->iopt.access_list); xa_for_each(&ioas->iopt.access_list, index, access) { - if (!iommufd_lock_obj(&access->obj) || - iommufd_access_is_internal(access)) + if (iommufd_access_is_internal(access) || + !iommufd_lock_obj(&access->obj)) continue; xa_unlock(&ioas->iopt.access_list); diff --git a/drivers/iommu/iommufd/ioas.c b/drivers/iommu/iommufd/ioas.c index 71bffece84b593..3459776871e6c6 100644 --- a/drivers/iommu/iommufd/ioas.c +++ b/drivers/iommu/iommufd/ioas.c @@ -427,6 +427,8 @@ static int iommufd_take_all_iova_rwsem(struct iommufd_ctx *ictx, rc = xa_err(xa_store(ioas_list, index, ioas, GFP_KERNEL)); if (rc) { + up_write(&ioas->iopt.iova_rwsem); + refcount_dec(&ioas->obj.users); iommufd_release_all_iova_rwsem(ictx, ioas_list); return rc; } diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c index af07c642a5260e..727b59799d5f08 100644 --- a/drivers/iommu/iommufd/selftest.c +++ b/drivers/iommu/iommufd/selftest.c @@ -177,6 +177,7 @@ struct mock_dev { struct device dev; struct mock_viommu *viommu; struct rw_semaphore viommu_rwsem; + struct rw_semaphore iopf_rwsem; unsigned long flags; unsigned long vdev_id; int id; @@ -997,6 +998,7 @@ static struct mock_dev *mock_dev_create(unsigned long dev_flags) return ERR_PTR(-ENOMEM); init_rwsem(&mdev->viommu_rwsem); + init_rwsem(&mdev->iopf_rwsem); device_initialize(&mdev->dev); mdev->flags = dev_flags; mdev->dev.release = mock_dev_release; @@ -1022,7 +1024,9 @@ static struct mock_dev *mock_dev_create(unsigned long dev_flags) goto err_put; } + down_write(&mdev->iopf_rwsem); rc = iommu_mock_device_add(&mdev->dev, &mock_iommu.iommu_dev); + up_write(&mdev->iopf_rwsem); if (rc) goto err_put; return mdev; @@ -1077,7 +1081,9 @@ static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd, } sobj->idev.idev = idev; + down_write(&sobj->idev.mock_dev->iopf_rwsem); rc = iommufd_device_attach(idev, IOMMU_NO_PASID, &pt_id); + up_write(&sobj->idev.mock_dev->iopf_rwsem); if (rc) goto out_unbind; @@ -1092,7 +1098,9 @@ static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd, return 0; out_detach: + down_write(&sobj->idev.mock_dev->iopf_rwsem); iommufd_device_detach(idev, IOMMU_NO_PASID); + up_write(&sobj->idev.mock_dev->iopf_rwsem); out_unbind: iommufd_device_unbind(idev); out_mdev: @@ -1136,7 +1144,9 @@ static int iommufd_test_mock_domain_replace(struct iommufd_ucmd *ucmd, if (IS_ERR(sobj)) return PTR_ERR(sobj); + down_write(&sobj->idev.mock_dev->iopf_rwsem); rc = iommufd_device_replace(sobj->idev.idev, IOMMU_NO_PASID, &pt_id); + up_write(&sobj->idev.mock_dev->iopf_rwsem); if (rc) goto out_sobj; @@ -1739,10 +1749,16 @@ static int iommufd_test_trigger_iopf(struct iommufd_ucmd *ucmd, { struct iopf_fault event = {}; struct iommufd_device *idev; + struct mock_dev *mdev; idev = iommufd_get_device(ucmd, cmd->trigger_iopf.dev_id); if (IS_ERR(idev)) return PTR_ERR(idev); + if (!iommufd_selftest_is_mock_dev(idev->dev)) { + iommufd_put_object(ucmd->ictx, &idev->obj); + return -EINVAL; + } + mdev = to_mock_dev(idev->dev); event.fault.prm.flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE; if (cmd->trigger_iopf.pasid != IOMMU_NO_PASID) @@ -1753,7 +1769,9 @@ static int iommufd_test_trigger_iopf(struct iommufd_ucmd *ucmd, event.fault.prm.grpid = cmd->trigger_iopf.grpid; event.fault.prm.perm = cmd->trigger_iopf.perm; + down_read(&mdev->iopf_rwsem); iommu_report_device_fault(idev->dev, &event); + up_read(&mdev->iopf_rwsem); iommufd_put_object(ucmd->ictx, &idev->obj); return 0; @@ -1861,14 +1879,19 @@ static int iommufd_test_pasid_attach(struct iommufd_ucmd *ucmd, if (IS_ERR(sobj)) return PTR_ERR(sobj); + down_write(&sobj->idev.mock_dev->iopf_rwsem); rc = iommufd_device_attach(sobj->idev.idev, cmd->pasid_attach.pasid, &cmd->pasid_attach.pt_id); + up_write(&sobj->idev.mock_dev->iopf_rwsem); if (rc) goto out_sobj; rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd)); - if (rc) + if (rc) { + down_write(&sobj->idev.mock_dev->iopf_rwsem); iommufd_device_detach(sobj->idev.idev, cmd->pasid_attach.pasid); + up_write(&sobj->idev.mock_dev->iopf_rwsem); + } out_sobj: iommufd_put_object(ucmd->ictx, &sobj->obj); @@ -1885,8 +1908,10 @@ static int iommufd_test_pasid_replace(struct iommufd_ucmd *ucmd, if (IS_ERR(sobj)) return PTR_ERR(sobj); + down_write(&sobj->idev.mock_dev->iopf_rwsem); rc = iommufd_device_replace(sobj->idev.idev, cmd->pasid_attach.pasid, &cmd->pasid_attach.pt_id); + up_write(&sobj->idev.mock_dev->iopf_rwsem); if (rc) goto out_sobj; @@ -1906,7 +1931,9 @@ static int iommufd_test_pasid_detach(struct iommufd_ucmd *ucmd, if (IS_ERR(sobj)) return PTR_ERR(sobj); + down_write(&sobj->idev.mock_dev->iopf_rwsem); iommufd_device_detach(sobj->idev.idev, cmd->pasid_detach.pasid); + up_write(&sobj->idev.mock_dev->iopf_rwsem); iommufd_put_object(ucmd->ictx, &sobj->obj); return 0; } @@ -1917,7 +1944,9 @@ void iommufd_selftest_destroy(struct iommufd_object *obj) switch (sobj->type) { case TYPE_IDEV: + down_write(&sobj->idev.mock_dev->iopf_rwsem); iommufd_device_detach(sobj->idev.idev, IOMMU_NO_PASID); + up_write(&sobj->idev.mock_dev->iopf_rwsem); iommufd_device_unbind(sobj->idev.idev); mock_dev_destroy(sobj->idev.mock_dev); break; diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index 0ad5ff431d5b6e..d0d926be7495cf 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -784,19 +784,25 @@ static int msm_iommu_probe(struct platform_device *pdev) "msm-smmu.%pa", &ioaddr); if (ret) { pr_err("Could not add msm-smmu at %pa to sysfs\n", &ioaddr); - return ret; + goto err_remove_list; } ret = iommu_device_register(&iommu->iommu, &msm_iommu_ops, &pdev->dev); if (ret) { pr_err("Could not register msm-smmu at %pa\n", &ioaddr); - return ret; + goto err_remove_sysfs; } pr_info("device mapped at %p, irq %d with %d ctx banks\n", iommu->base, iommu->irq, iommu->ncb); return ret; + +err_remove_sysfs: + iommu_device_sysfs_remove(&iommu->iommu); +err_remove_list: + list_del(&iommu->dev_node); + return ret; } static const struct of_device_id msm_iommu_dt_match[] = { diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c index 6f69f4e5dbacda..12919836dadba4 100644 --- a/drivers/irqchip/irq-mbigen.c +++ b/drivers/irqchip/irq-mbigen.c @@ -64,20 +64,6 @@ struct mbigen_device { void __iomem *base; }; -static inline unsigned int get_mbigen_node_offset(unsigned int nid) -{ - unsigned int offset = nid * MBIGEN_NODE_OFFSET; - - /* - * To avoid touched clear register in unexpected way, we need to directly - * skip clear register when access to more than 10 mbigen nodes. - */ - if (nid >= (REG_MBIGEN_CLEAR_OFFSET / MBIGEN_NODE_OFFSET)) - offset += MBIGEN_NODE_OFFSET; - - return offset; -} - static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq) { unsigned int nid, pin; @@ -86,7 +72,8 @@ static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq) nid = hwirq / IRQS_PER_MBIGEN_NODE + 1; pin = hwirq % IRQS_PER_MBIGEN_NODE; - return pin * 4 + get_mbigen_node_offset(nid) + REG_MBIGEN_VEC_OFFSET; + return pin * 4 + nid * MBIGEN_NODE_OFFSET + + REG_MBIGEN_VEC_OFFSET; } static inline void get_mbigen_type_reg(irq_hw_number_t hwirq, @@ -101,7 +88,8 @@ static inline void get_mbigen_type_reg(irq_hw_number_t hwirq, *mask = 1 << (irq_ofst % 32); ofst = irq_ofst / 32 * 4; - *addr = ofst + get_mbigen_node_offset(nid) + REG_MBIGEN_TYPE_OFFSET; + *addr = ofst + nid * MBIGEN_NODE_OFFSET + + REG_MBIGEN_TYPE_OFFSET; } static inline void get_mbigen_clear_reg(irq_hw_number_t hwirq, diff --git a/drivers/irqchip/irq-stm32mp-exti.c b/drivers/irqchip/irq-stm32mp-exti.c index bf3a2def69cad0..a19e91fbd0100f 100644 --- a/drivers/irqchip/irq-stm32mp-exti.c +++ b/drivers/irqchip/irq-stm32mp-exti.c @@ -22,7 +22,7 @@ #define IRQS_PER_BANK 32 -#define HWSPNLCK_TIMEOUT 1000 /* usec */ +#define HWSPNLCK_TIMEOUT_MS 1 #define EXTI_EnCIDCFGR(n) (0x180 + (n) * 4) #define EXTI_HWCFGR1 0x3f0 @@ -376,7 +376,7 @@ static int stm32mp_exti_set_type(struct irq_data *d, unsigned int type) raw_spin_lock(&chip_data->rlock); if (hwlock) { - err = hwspin_lock_timeout_in_atomic(hwlock, HWSPNLCK_TIMEOUT); + err = hwspin_lock_timeout_in_atomic(hwlock, HWSPNLCK_TIMEOUT_MS); if (err) { pr_err("%s can't get hwspinlock (%d)\n", __func__, err); goto unlock; diff --git a/drivers/mailbox/qcom-ipcc.c b/drivers/mailbox/qcom-ipcc.c index d957d989c0ceeb..dead907aa7a282 100644 --- a/drivers/mailbox/qcom-ipcc.c +++ b/drivers/mailbox/qcom-ipcc.c @@ -167,7 +167,7 @@ static struct mbox_chan *qcom_ipcc_mbox_xlate(struct mbox_controller *mbox, { struct qcom_ipcc *ipcc = to_qcom_ipcc(mbox); struct qcom_ipcc_chan_info *mchan; - struct mbox_chan *chan; + struct mbox_chan *chan, *free_chan = NULL; struct device *dev; int chan_id; @@ -180,16 +180,21 @@ static struct mbox_chan *qcom_ipcc_mbox_xlate(struct mbox_controller *mbox, chan = &ipcc->chans[chan_id]; mchan = chan->con_priv; - if (!mchan) - break; - else if (mchan->client_id == ph->args[0] && - mchan->signal_id == ph->args[1]) + if (!mchan) { + /* Keep scanning past holes to reject duplicate channel requests. */ + if (!free_chan) + free_chan = chan; + } else if (mchan->client_id == ph->args[0] && + mchan->signal_id == ph->args[1]) { return ERR_PTR(-EBUSY); + } } - if (chan_id >= mbox->num_chans) + if (!free_chan) return ERR_PTR(-EBUSY); + chan = free_chan; + mchan = devm_kzalloc(dev, sizeof(*mchan), GFP_KERNEL); if (!mchan) return ERR_PTR(-ENOMEM); diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index a458b9fd2fcd62..d58eed96a8ff57 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -1287,11 +1287,11 @@ static void free_buffer(struct dm_buffer *b) * dm-io completion routine. It just calls b->bio.bi_end_io, pretending * that the request was handled directly with bio interface. */ -static void dmio_complete(unsigned long error, void *context) +static void dmio_complete(unsigned long error, unsigned long unsup, void *context) { struct dm_buffer *b = context; - b->end_io(b, unlikely(error != 0) ? BLK_STS_IOERR : 0); + b->end_io(b, unlikely(error != 0) ? BLK_STS_IOERR : unlikely(unsup != 0) ? BLK_STS_NOTSUPP : 0); } static void use_dmio(struct dm_buffer *b, enum req_op op, sector_t sector, @@ -1319,7 +1319,7 @@ static void use_dmio(struct dm_buffer *b, enum req_op op, sector_t sector, io_req.mem.ptr.vma = (char *)b->data + offset; } - r = dm_io(&io_req, 1, ®ion, NULL, ioprio); + r = dm_io(&io_req, 1, ®ion, NULL, NULL, ioprio); if (unlikely(r)) b->end_io(b, errno_to_blk_status(r)); } @@ -2220,7 +2220,7 @@ int dm_bufio_issue_flush(struct dm_bufio_client *c) if (WARN_ON_ONCE(dm_bufio_in_request())) return -EINVAL; - return dm_io(&io_req, 1, &io_reg, NULL, IOPRIO_DEFAULT); + return dm_io(&io_req, 1, &io_reg, NULL, NULL, IOPRIO_DEFAULT); } EXPORT_SYMBOL_GPL(dm_bufio_issue_flush); @@ -2246,7 +2246,7 @@ int dm_bufio_issue_discard(struct dm_bufio_client *c, sector_t block, sector_t c if (WARN_ON_ONCE(dm_bufio_in_request())) return -EINVAL; /* discards are optional */ - return dm_io(&io_req, 1, &io_reg, NULL, IOPRIO_DEFAULT); + return dm_io(&io_req, 1, &io_reg, NULL, NULL, IOPRIO_DEFAULT); } EXPORT_SYMBOL_GPL(dm_bufio_issue_discard); diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index 7fe4d19ade4f03..ea499adca4ce2a 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -1034,6 +1034,7 @@ static int metadata_checkpoint(struct era_metadata *md) static int metadata_take_snap(struct era_metadata *md) { int r, inc; + dm_block_t location; struct dm_block *clone; if (md->metadata_snap != SUPERBLOCK_LOCATION) { @@ -1071,7 +1072,9 @@ static int metadata_take_snap(struct era_metadata *md) r = dm_sm_inc_block(md->sm, md->writeset_tree_root); if (r) { DMERR("%s: couldn't inc writeset tree root", __func__); + location = dm_block_location(clone); dm_tm_unlock(md->tm, clone); + dm_sm_dec_block(md->sm, location); return r; } @@ -1079,7 +1082,9 @@ static int metadata_take_snap(struct era_metadata *md) if (r) { DMERR("%s: couldn't inc era tree root", __func__); dm_sm_dec_block(md->sm, md->writeset_tree_root); + location = dm_block_location(clone); dm_tm_unlock(md->tm, clone); + dm_sm_dec_block(md->sm, location); return r; } diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index 1f2593f113f642..81d3f42c4f48c7 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -562,7 +562,7 @@ static int sync_rw_sb(struct dm_integrity_c *ic, blk_opf_t opf) } } - r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) return r; @@ -1035,12 +1035,14 @@ static void encrypt_journal(struct dm_integrity_c *ic, bool encrypt, unsigned in return crypt_journal(ic, encrypt, section, n_sections, comp); } -static void complete_journal_io(unsigned long error, void *context) +static void complete_journal_io(unsigned long error, unsigned long unsup, void *context) { struct journal_completion *comp = context; if (unlikely(error != 0)) dm_integrity_io_error(comp->ic, "writing journal", -EIO); + else if (unlikely(unsup != 0)) + dm_integrity_io_error(comp->ic, "writing journal", -EOPNOTSUPP); complete_journal_op(comp); } @@ -1055,7 +1057,7 @@ static void rw_journal_sectors(struct dm_integrity_c *ic, blk_opf_t opf, if (unlikely(dm_integrity_failed(ic))) { if (comp) - complete_journal_io(-1UL, comp); + complete_journal_io(-1UL, -1UL, comp); return; } @@ -1080,13 +1082,13 @@ static void rw_journal_sectors(struct dm_integrity_c *ic, blk_opf_t opf, io_loc.sector = ic->start + SB_SECTORS + sector; io_loc.count = n_sectors; - r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) { dm_integrity_io_error(ic, (opf & REQ_OP_MASK) == REQ_OP_READ ? "reading journal" : "writing journal", r); if (comp) { WARN_ONCE(1, "asynchronous dm_io failed: %d", r); - complete_journal_io(-1UL, comp); + complete_journal_io(-1UL, -1UL, comp); } } } @@ -1177,7 +1179,7 @@ static void copy_from_journal(struct dm_integrity_c *ic, unsigned int section, u BUG_ON((target | n_sectors | offset) & (unsigned int)(ic->sectors_per_block - 1)); if (unlikely(dm_integrity_failed(ic))) { - fn(-1UL, data); + fn(-1UL, -1UL, data); return; } @@ -1197,10 +1199,10 @@ static void copy_from_journal(struct dm_integrity_c *ic, unsigned int section, u io_loc.sector = target; io_loc.count = n_sectors; - r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) { WARN_ONCE(1, "asynchronous dm_io failed: %d", r); - fn(-1UL, data); + fn(-1UL, -1UL, data); } } @@ -1493,12 +1495,14 @@ struct flush_request { struct completion comp; }; -static void flush_notify(unsigned long error, void *fr_) +static void flush_notify(unsigned long error, unsigned long unsup, void *fr_) { struct flush_request *fr = fr_; if (unlikely(error != 0)) dm_integrity_io_error(fr->ic, "flushing disk cache", -EIO); + else if (unlikely(unsup != 0)) + dm_integrity_io_error(fr->ic, "flushing disk cache", -EOPNOTSUPP); complete(&fr->comp); } @@ -1521,7 +1525,7 @@ static void dm_integrity_flush_buffers(struct dm_integrity_c *ic, bool flush_dat fr.io_reg.count = 0; fr.ic = ic; init_completion(&fr.comp); - r = dm_io(&fr.io_req, 1, &fr.io_reg, NULL, IOPRIO_DEFAULT); + r = dm_io(&fr.io_req, 1, &fr.io_reg, NULL, NULL, IOPRIO_DEFAULT); BUG_ON(r); } @@ -1837,7 +1841,7 @@ static noinline void integrity_recheck(struct dm_integrity_io *dio, char *checks buffer_offset = (sector - io_loc.sector) << SECTOR_SHIFT; io_loc.count = round_up(io_loc.count, alignment); - r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) { dio->bi_status = errno_to_blk_status(r); goto free_ret; @@ -2890,7 +2894,7 @@ static void integrity_commit(struct work_struct *w) } } -static void complete_copy_from_journal(unsigned long error, void *context) +static void complete_copy_from_journal(unsigned long error, unsigned long unsup, void *context) { struct journal_io *io = context; struct journal_completion *comp = io->comp; @@ -2900,6 +2904,8 @@ static void complete_copy_from_journal(unsigned long error, void *context) mempool_free(io, &ic->journal_io_mempool); if (unlikely(error != 0)) dm_integrity_io_error(ic, "copying from journal", -EIO); + else if (unlikely(unsup != 0)) + dm_integrity_io_error(ic, "copying from journal", -EOPNOTSUPP); complete_journal_op(comp); } @@ -3215,7 +3221,7 @@ static void integrity_recalc(struct work_struct *w) io_loc.sector = get_data_sector(ic, area, offset); io_loc.count = n_sectors; - r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) { dm_integrity_io_error(ic, "reading data", r); goto err; diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 1db565b376200a..777b917be40020 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -33,6 +33,7 @@ struct dm_io_client { */ struct io { unsigned long error_bits; + unsigned long unsup_bits; atomic_t count; struct dm_io_client *client; io_notify_fn callback; @@ -119,6 +120,7 @@ static void retrieve_io_and_region_from_bio(struct bio *bio, struct io **io, static void complete_io(struct io *io) { unsigned long error_bits = io->error_bits; + unsigned long unsup_bits = io->unsup_bits; io_notify_fn fn = io->callback; void *context = io->context; @@ -127,13 +129,17 @@ static void complete_io(struct io *io) io->vma_invalidate_size); mempool_free(io, &io->client->pool); - fn(error_bits, context); + fn(error_bits, unsup_bits, context); } static void dec_count(struct io *io, unsigned int region, blk_status_t error) { - if (error) - set_bit(region, &io->error_bits); + if (unlikely(error)) { + if (error == BLK_STS_NOTSUPP || error == BLK_STS_INVAL) + set_bit(region, &io->unsup_bits); + else + set_bit(region, &io->error_bits); + } if (atomic_dec_and_test(&io->count)) complete_io(io); @@ -170,12 +176,11 @@ struct dpages { struct page **p, unsigned long *len, unsigned int *offset); void (*next_page)(struct dpages *dp); - union { - unsigned int context_u; - struct bvec_iter context_bi; - }; + unsigned int context_u; void *context_ptr; + struct bio *orig_bio; + void *vma_invalidate_address; unsigned long vma_invalidate_size; }; @@ -210,44 +215,6 @@ static void list_dp_init(struct dpages *dp, struct page_list *pl, unsigned int o dp->context_ptr = pl; } -/* - * Functions for getting the pages from a bvec. - */ -static void bio_get_page(struct dpages *dp, struct page **p, - unsigned long *len, unsigned int *offset) -{ - struct bio_vec bvec = bvec_iter_bvec((struct bio_vec *)dp->context_ptr, - dp->context_bi); - - *p = bvec.bv_page; - *len = bvec.bv_len; - *offset = bvec.bv_offset; - - /* avoid figuring it out again in bio_next_page() */ - dp->context_bi.bi_sector = (sector_t)bvec.bv_len; -} - -static void bio_next_page(struct dpages *dp) -{ - unsigned int len = (unsigned int)dp->context_bi.bi_sector; - - bvec_iter_advance((struct bio_vec *)dp->context_ptr, - &dp->context_bi, len); -} - -static void bio_dp_init(struct dpages *dp, struct bio *bio) -{ - dp->get_page = bio_get_page; - dp->next_page = bio_next_page; - - /* - * We just use bvec iterator to retrieve pages, so it is ok to - * access the bvec table directly here - */ - dp->context_ptr = bio->bi_io_vec; - dp->context_bi = bio->bi_iter; -} - /* * Functions for getting the pages from a VMA. */ @@ -332,6 +299,21 @@ static void do_region(const blk_opf_t opf, unsigned int region, return; } + if (dp->orig_bio) { + bio = bio_alloc_clone(where->bdev, dp->orig_bio, GFP_NOIO, + &io->client->bios); + bio->bi_iter.bi_sector = where->sector; + bio->bi_iter.bi_size = where->count << SECTOR_SHIFT; + bio->bi_opf = opf; + bio->bi_end_io = endio; + bio->bi_ioprio = ioprio; + store_io_and_region_in_bio(bio, io, region); + + atomic_inc(&io->count); + submit_bio(bio); + return; + } + /* * where->count may be zero if op holds a flush and we need to * send a zero-sized flush. @@ -418,6 +400,7 @@ static void async_io(struct dm_io_client *client, unsigned int num_regions, io = mempool_alloc(&client->pool, GFP_NOIO); io->error_bits = 0; + io->unsup_bits = 0; atomic_set(&io->count, 1); /* see dispatch_io() */ io->client = client; io->callback = fn; @@ -431,20 +414,23 @@ static void async_io(struct dm_io_client *client, unsigned int num_regions, struct sync_io { unsigned long error_bits; + unsigned long unsup_bits; struct completion wait; }; -static void sync_io_complete(unsigned long error, void *context) +static void sync_io_complete(unsigned long error, unsigned long unsup, void *context) { struct sync_io *sio = context; sio->error_bits = error; + sio->unsup_bits = unsup; complete(&sio->wait); } static int sync_io(struct dm_io_client *client, unsigned int num_regions, struct dm_io_region *where, blk_opf_t opf, struct dpages *dp, - unsigned long *error_bits, unsigned short ioprio) + unsigned long *error_bits, unsigned long *unsup_bits, + unsigned short ioprio) { struct sync_io sio; @@ -457,8 +443,10 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions, if (error_bits) *error_bits = sio.error_bits; + if (unsup_bits) + *unsup_bits = sio.unsup_bits; - return sio.error_bits ? -EIO : 0; + return sio.error_bits ? -EIO : sio.unsup_bits ? -EOPNOTSUPP : 0; } static int dp_init(struct dm_io_request *io_req, struct dpages *dp, @@ -468,6 +456,7 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp, dp->vma_invalidate_address = NULL; dp->vma_invalidate_size = 0; + dp->orig_bio = NULL; switch (io_req->mem.type) { case DM_IO_PAGE_LIST: @@ -475,7 +464,11 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp, break; case DM_IO_BIO: - bio_dp_init(dp, io_req->mem.ptr.bio); + /* + * The destination bios clone this bio's biovec directly, so + * there are no per-page accessors to set up here. + */ + dp->orig_bio = io_req->mem.ptr.bio; break; case DM_IO_VMA: @@ -500,7 +493,7 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp, int dm_io(struct dm_io_request *io_req, unsigned int num_regions, struct dm_io_region *where, unsigned long *sync_error_bits, - unsigned short ioprio) + unsigned long *sync_unsup_bits, unsigned short ioprio) { int r; struct dpages dp; @@ -516,7 +509,8 @@ int dm_io(struct dm_io_request *io_req, unsigned int num_regions, if (!io_req->notify.fn) return sync_io(io_req->client, num_regions, where, - io_req->bi_opf, &dp, sync_error_bits, ioprio); + io_req->bi_opf, &dp, sync_error_bits, + sync_unsup_bits, ioprio); async_io(io_req->client, num_regions, where, io_req->bi_opf, &dp, io_req->notify.fn, io_req->notify.context, ioprio); diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index 96c8b8ff61c20b..5c9c24a3dcd999 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c @@ -517,16 +517,16 @@ static int run_complete_job(struct kcopyd_job *job) return 0; } -static void complete_io(unsigned long error, void *context) +static void complete_io(unsigned long error, unsigned long unsup, void *context) { struct kcopyd_job *job = context; struct dm_kcopyd_client *kc = job->kc; io_job_finish(kc->throttle); - if (error) { + if (unlikely((error | unsup) != 0)) { if (op_is_write(job->op)) - job->write_err |= error; + job->write_err |= error | unsup; else job->read_err = 1; @@ -578,9 +578,9 @@ static int run_io_job(struct kcopyd_job *job) io_job_start(job->kc->throttle); if (job->op == REQ_OP_READ) - r = dm_io(&io_req, 1, &job->source, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &job->source, NULL, NULL, IOPRIO_DEFAULT); else - r = dm_io(&io_req, job->num_dests, job->dests, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, job->num_dests, job->dests, NULL, NULL, IOPRIO_DEFAULT); return r; } diff --git a/drivers/md/dm-log-userspace-base.c b/drivers/md/dm-log-userspace-base.c index 9d5918eb0a30e0..3ca35aa4e057d1 100644 --- a/drivers/md/dm-log-userspace-base.c +++ b/drivers/md/dm-log-userspace-base.c @@ -139,6 +139,7 @@ static int build_constructor_string(struct dm_target *ti, str_size += strlen(argv[i]) + 1; /* +1 for space between args */ str_size += 20; /* Max number of chars in a printed u64 number */ + str_size++; /* For NUL-terminator */ str = kzalloc(str_size, GFP_KERNEL); if (!str) { diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 2ddeb4250c5929..4c114fc5ef8375 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -300,7 +300,7 @@ static int rw_header(struct log_c *lc, enum req_op op) { lc->io_req.bi_opf = op; - return dm_io(&lc->io_req, 1, &lc->header_location, NULL, IOPRIO_DEFAULT); + return dm_io(&lc->io_req, 1, &lc->header_location, NULL, NULL, IOPRIO_DEFAULT); } static int flush_header(struct log_c *lc) @@ -313,7 +313,7 @@ static int flush_header(struct log_c *lc) lc->io_req.bi_opf = REQ_OP_WRITE | REQ_PREFLUSH; - return dm_io(&lc->io_req, 1, &null_location, NULL, IOPRIO_DEFAULT); + return dm_io(&lc->io_req, 1, &null_location, NULL, NULL, IOPRIO_DEFAULT); } static int read_header(struct log_c *log) diff --git a/drivers/md/dm-pcache/cache.c b/drivers/md/dm-pcache/cache.c index bb1ada31e4836c..e68dacb69459c3 100644 --- a/drivers/md/dm-pcache/cache.c +++ b/drivers/md/dm-pcache/cache.c @@ -119,6 +119,10 @@ int cache_pos_decode(struct pcache_cache *cache, return -EIO; pos->cache_seg = &cache->segments[latest.cache_seg_id]; + + if (latest.seg_off >= pos->cache_seg->segment.data_size) + return -EIO; + pos->seg_off = latest.seg_off; *seq = latest.header.seq; *index = (latest_addr - pos_onmedia); @@ -247,6 +251,13 @@ static int get_seg_id(struct pcache_cache *cache, } else { *seg_id = cache->cache_info.seg_id; } + + if (*seg_id >= cache_dev->seg_num) { + pcache_dev_err(pcache, "invalid segment id %u from cache device (seg_num %u)\n", + *seg_id, cache_dev->seg_num); + ret = -EIO; + goto err; + } } return 0; err: @@ -262,6 +273,13 @@ static int cache_segs_init(struct pcache_cache *cache) int ret; u32 i; + if (cache_info->n_segs > cache->cache_dev->seg_num) { + pcache_dev_err(CACHE_TO_PCACHE(cache), + "cache_info n_segs %u exceeds cache device segments %u\n", + cache_info->n_segs, cache->cache_dev->seg_num); + return -EIO; + } + for (i = 0; i < cache_info->n_segs; i++) { ret = get_seg_id(cache, prev_cache_seg, new_cache, &seg_id); if (ret) diff --git a/drivers/md/dm-pcache/cache.h b/drivers/md/dm-pcache/cache.h index 27613b56be54c2..afc112b79496ac 100644 --- a/drivers/md/dm-pcache/cache.h +++ b/drivers/md/dm-pcache/cache.h @@ -491,6 +491,27 @@ static inline u32 cache_key_data_crc(struct pcache_cache_key *key) return crc32c(PCACHE_CRC_SEED, data, key->len); } +/** + * kset_onmedia_valid - Validate a kset header read from the cache device. + * @kset_onmedia: Pointer to the kset copied from on-media metadata. + * + * The magic and CRC are attacker-computable (fixed public seed). A non-last + * kset stores key_num keys inline, and cache_kset_crc() and the replay loop + * read struct_size(.., data, key_num) bytes from a buffer sized for + * PCACHE_KSET_KEYS_MAX keys, so key_num must be bounded before any such use. + */ +static inline bool kset_onmedia_valid(struct pcache_cache_kset_onmedia *kset_onmedia) +{ + if (kset_onmedia->magic != PCACHE_KSET_MAGIC) + return false; + + if (!(kset_onmedia->flags & PCACHE_KSET_FLAGS_LAST) && + kset_onmedia->key_num > PCACHE_KSET_KEYS_MAX) + return false; + + return true; +} + static inline u32 cache_kset_crc(struct pcache_cache_kset_onmedia *kset_onmedia) { u32 crc_size; diff --git a/drivers/md/dm-pcache/cache_dev.c b/drivers/md/dm-pcache/cache_dev.c index ece689e6ce59d8..f0259353ee3978 100644 --- a/drivers/md/dm-pcache/cache_dev.c +++ b/drivers/md/dm-pcache/cache_dev.c @@ -242,6 +242,8 @@ int cache_dev_start(struct dm_pcache *pcache) struct pcache_cache_dev *cache_dev = &pcache->cache_dev; struct pcache_sb sb; bool format = false; + u32 seg_num; + u64 max_segs; int ret; mutex_init(&cache_dev->seg_lock); @@ -269,7 +271,25 @@ int cache_dev_start(struct dm_pcache *pcache) goto dax_release; cache_dev->sb_flags = le32_to_cpu(sb.flags); - ret = cache_dev_init(cache_dev, le32_to_cpu(sb.seg_num)); + + /* + * seg_num is read from the crc32c-only superblock, so whoever supplies + * the cache device controls it. It is the ceiling every later on-media + * segment id is validated against, so bound it against what the device + * physically holds before it is trusted, or a forged seg_num lets a + * segment id address past the DAX mapping. + */ + seg_num = le32_to_cpu(sb.seg_num); + max_segs = (bdev_nr_bytes(cache_dev->dm_dev->bdev) - PCACHE_SEGMENTS_OFF) / + PCACHE_SEG_SIZE; + if (seg_num == 0 || seg_num > max_segs || seg_num > PCACHE_CACHE_SEGS_MAX) { + pcache_dev_err(pcache, "invalid seg_num %u from cache device (device holds %llu, max %u)\n", + seg_num, max_segs, (u32)PCACHE_CACHE_SEGS_MAX); + ret = -EIO; + goto dax_release; + } + + ret = cache_dev_init(cache_dev, seg_num); if (ret) goto dax_release; diff --git a/drivers/md/dm-pcache/cache_gc.c b/drivers/md/dm-pcache/cache_gc.c index 94f8b276a02128..3088cfef951bc1 100644 --- a/drivers/md/dm-pcache/cache_gc.c +++ b/drivers/md/dm-pcache/cache_gc.c @@ -37,18 +37,18 @@ static bool need_gc(struct pcache_cache *cache, struct pcache_cache_pos *dirty_t kset_onmedia = (struct pcache_cache_kset_onmedia *)cache->gc_kset_onmedia_buf; - to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - key_tail->seg_off); + to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(key_tail)); ret = copy_mc_to_kernel(kset_onmedia, key_addr, to_copy); if (ret) { pcache_dev_err(pcache, "error to read kset: %d", ret); return false; } - /* Check if kset_onmedia is corrupted */ - if (kset_onmedia->magic != PCACHE_KSET_MAGIC) { - pcache_dev_debug(pcache, "gc error: magic is not as expected. key_tail: %u:%u magic: %llx, expected: %llx\n", + /* Reject a corrupted or out-of-bounds kset before reading its keys */ + if (!kset_onmedia_valid(kset_onmedia)) { + pcache_dev_debug(pcache, "gc error: invalid kset. key_tail: %u:%u magic: %llx, key_num: %u\n", key_tail->cache_seg->cache_seg_id, key_tail->seg_off, - kset_onmedia->magic, PCACHE_KSET_MAGIC); + kset_onmedia->magic, kset_onmedia->key_num); return false; } diff --git a/drivers/md/dm-pcache/cache_key.c b/drivers/md/dm-pcache/cache_key.c index e068e878231b42..51dd1ccf4e2fde 100644 --- a/drivers/md/dm-pcache/cache_key.c +++ b/drivers/md/dm-pcache/cache_key.c @@ -97,6 +97,14 @@ int cache_key_decode(struct pcache_cache *cache, key->cache_pos.cache_seg = &cache->segments[key_onmedia->cache_seg_id]; key->cache_pos.seg_off = key_onmedia->cache_seg_off; + if ((u64)key->cache_pos.seg_off + key->len > + key->cache_pos.cache_seg->segment.data_size) { + pcache_dev_err(pcache, "key seg_off %u + len %u exceeds segment data size %u\n", + key->cache_pos.seg_off, key->len, + key->cache_pos.cache_seg->segment.data_size); + return -EIO; + } + key->seg_gen = key_onmedia->seg_gen; key->flags = key_onmedia->flags; @@ -728,18 +736,17 @@ static int kset_replay(struct pcache_cache *cache, struct pcache_cache_kset_onme goto err; } - __set_bit(key->cache_pos.cache_seg->cache_seg_id, cache->seg_map); - /* Check if the segment generation is valid for insertion. */ if (key->seg_gen < key->cache_pos.cache_seg->gen) { cache_key_put(key); - } else { - cache_subtree = get_subtree(&cache->req_key_tree, key->off); - spin_lock(&cache_subtree->tree_lock); - cache_key_insert(&cache->req_key_tree, key, true); - spin_unlock(&cache_subtree->tree_lock); + continue; } + __set_bit(key->cache_pos.cache_seg->cache_seg_id, cache->seg_map); + cache_subtree = get_subtree(&cache->req_key_tree, key->off); + spin_lock(&cache_subtree->tree_lock); + cache_key_insert(&cache->req_key_tree, key, true); + spin_unlock(&cache_subtree->tree_lock); cache_seg_get(key->cache_pos.cache_seg); } @@ -754,7 +761,7 @@ int cache_replay(struct pcache_cache *cache) struct pcache_cache_pos pos_tail; struct pcache_cache_pos *pos; struct pcache_cache_kset_onmedia *kset_onmedia; - u32 to_copy, count = 0; + u32 to_copy, count = 0, last_hops = 0; int ret = 0; kset_onmedia = kzalloc(PCACHE_KSET_ONMEDIA_SIZE_MAX, GFP_KERNEL); @@ -771,14 +778,14 @@ int cache_replay(struct pcache_cache *cache) __set_bit(pos->cache_seg->cache_seg_id, cache->seg_map); while (true) { - to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - pos->seg_off); + to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(pos)); ret = copy_mc_to_kernel(kset_onmedia, cache_pos_addr(pos), to_copy); if (ret) { ret = -EIO; goto out; } - if (kset_onmedia->magic != PCACHE_KSET_MAGIC || + if (!kset_onmedia_valid(kset_onmedia) || kset_onmedia->crc != cache_kset_crc(kset_onmedia)) { break; } @@ -789,6 +796,11 @@ int cache_replay(struct pcache_cache *cache) pcache_dev_debug(pcache, "last kset replay, next: %u\n", kset_onmedia->next_cache_seg_id); + if (++last_hops > cache->n_segs) { + ret = -EIO; + goto out; + } + next_seg = &cache->segments[kset_onmedia->next_cache_seg_id]; pos->cache_seg = next_seg; diff --git a/drivers/md/dm-pcache/cache_segment.c b/drivers/md/dm-pcache/cache_segment.c index 9d92e2b067ed04..c698ebbc626d27 100644 --- a/drivers/md/dm-pcache/cache_segment.c +++ b/drivers/md/dm-pcache/cache_segment.c @@ -243,8 +243,16 @@ struct pcache_cache_segment *get_cache_segment(struct pcache_cache *cache) spin_lock(&cache->seg_map_lock); again: - seg_id = find_next_zero_bit(cache->seg_map, cache->n_segs, cache->last_cache_seg); - if (seg_id == cache->n_segs) { + /* + * Only allocate initialized segments. cache_segs_init() initializes + * cache_info.n_segs of the cache->n_segs device segments; a forged + * smaller cache_info.n_segs leaves the rest as zeroed structs whose data + * pointer is NULL. Bounding the search to cache_info.n_segs keeps such a + * segment from reaching cache_kset_close(), which writes through it. + */ + seg_id = find_next_zero_bit(cache->seg_map, cache->cache_info.n_segs, + cache->last_cache_seg); + if (seg_id == cache->cache_info.n_segs) { /* reset the hint of ->last_cache_seg and retry */ if (cache->last_cache_seg) { cache->last_cache_seg = 0; diff --git a/drivers/md/dm-pcache/cache_writeback.c b/drivers/md/dm-pcache/cache_writeback.c index 87a82b3fe8363a..3df8687e2382f7 100644 --- a/drivers/md/dm-pcache/cache_writeback.c +++ b/drivers/md/dm-pcache/cache_writeback.c @@ -48,18 +48,18 @@ static inline bool is_cache_clean(struct pcache_cache *cache, struct pcache_cach addr = cache_pos_addr(dirty_tail); kset_onmedia = (struct pcache_cache_kset_onmedia *)cache->wb_kset_onmedia_buf; - to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - dirty_tail->seg_off); + to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(dirty_tail)); ret = copy_mc_to_kernel(kset_onmedia, addr, to_copy); if (ret) { pcache_dev_err(pcache, "error to read kset: %d", ret); return true; } - /* Check if the magic number matches the expected value */ - if (kset_onmedia->magic != PCACHE_KSET_MAGIC) { - pcache_dev_debug(pcache, "dirty_tail: %u:%u magic: %llx, not expected: %llx\n", + /* Reject a corrupted or out-of-bounds kset before reading its keys */ + if (!kset_onmedia_valid(kset_onmedia)) { + pcache_dev_debug(pcache, "dirty_tail: %u:%u invalid kset magic: %llx, key_num: %u\n", dirty_tail->cache_seg->cache_seg_id, dirty_tail->seg_off, - kset_onmedia->magic, PCACHE_KSET_MAGIC); + kset_onmedia->magic, kset_onmedia->key_num); return true; } diff --git a/drivers/md/dm-pcache/dm_pcache.c b/drivers/md/dm-pcache/dm_pcache.c index d5cfd162c06335..645fc27d82ba4a 100644 --- a/drivers/md/dm-pcache/dm_pcache.c +++ b/drivers/md/dm-pcache/dm_pcache.c @@ -439,13 +439,13 @@ static int dm_pcache_message(struct dm_target *ti, unsigned int argc, char **argv, char *result, unsigned int maxlen) { struct dm_pcache *pcache = ti->private; - unsigned long val; + u8 val; if (argc != 2) goto err; if (!strcasecmp(argv[0], "gc_percent")) { - if (kstrtoul(argv[1], 10, &val)) + if (kstrtou8(argv[1], 10, &val)) goto err; return pcache_cache_set_gc_percent(&pcache->cache, val); diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index de5c00704e69c6..da2a5e2002ecaf 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -258,7 +258,7 @@ static void fail_mirror(struct mirror *m, enum dm_raid1_error error_type) static int mirror_flush(struct dm_target *ti) { struct mirror_set *ms = ti->private; - unsigned long error_bits; + unsigned long error_bits, unsup_bits; unsigned int i; struct dm_io_region io[MAX_NR_MIRRORS]; @@ -277,8 +277,8 @@ static int mirror_flush(struct dm_target *ti) } error_bits = -1; - dm_io(&io_req, ms->nr_mirrors, io, &error_bits, IOPRIO_DEFAULT); - if (unlikely(error_bits != 0)) { + dm_io(&io_req, ms->nr_mirrors, io, &error_bits, &unsup_bits, IOPRIO_DEFAULT); + if (unlikely((error_bits | unsup_bits) != 0)) { for (i = 0; i < ms->nr_mirrors; i++) if (test_bit(i, &error_bits)) fail_mirror(ms->mirror + i, @@ -511,7 +511,7 @@ static void hold_bio(struct mirror_set *ms, struct bio *bio) * Reads *--------------------------------------------------------------- */ -static void read_callback(unsigned long error, void *context) +static void read_callback(unsigned long error, unsigned long unsup, void *context) { struct bio *bio = context; struct mirror *m; @@ -520,6 +520,8 @@ static void read_callback(unsigned long error, void *context) bio_set_m(bio, NULL); if (likely(!error)) { + if (unlikely(unsup != 0)) + bio->bi_status = BLK_STS_INVAL; bio_endio(bio); return; } @@ -553,7 +555,7 @@ static void read_async_bio(struct mirror *m, struct bio *bio) map_region(&io, m, bio); bio_set_m(bio, m); - BUG_ON(dm_io(&io_req, 1, &io, NULL, IOPRIO_DEFAULT)); + BUG_ON(dm_io(&io_req, 1, &io, NULL, NULL, IOPRIO_DEFAULT)); } static inline int region_in_sync(struct mirror_set *ms, region_t region, @@ -600,7 +602,7 @@ static void do_reads(struct mirror_set *ms, struct bio_list *reads) * NOSYNC: increment pending, just write to the default mirror *--------------------------------------------------------------------- */ -static void write_callback(unsigned long error, void *context) +static void write_callback(unsigned long error, unsigned long unsup, void *context) { unsigned int i; struct bio *bio = context; @@ -617,7 +619,7 @@ static void write_callback(unsigned long error, void *context) * This way we handle both writes to SYNC and NOSYNC * regions with the same code. */ - if (likely(!error)) { + if (likely(!(error | unsup))) { bio_endio(bio); return; } @@ -632,6 +634,12 @@ static void write_callback(unsigned long error, void *context) return; } + if (!error && unsup) { + bio->bi_status = BLK_STS_INVAL; + bio_endio(bio); + return; + } + for (i = 0; i < ms->nr_mirrors; i++) if (test_bit(i, &error)) fail_mirror(ms->mirror + i, DM_RAID1_WRITE_ERROR); @@ -680,7 +688,7 @@ static void do_write(struct mirror_set *ms, struct bio *bio) */ bio_set_m(bio, get_default_mirror(ms)); - BUG_ON(dm_io(&io_req, ms->nr_mirrors, io, NULL, IOPRIO_DEFAULT)); + BUG_ON(dm_io(&io_req, ms->nr_mirrors, io, NULL, NULL, IOPRIO_DEFAULT)); } static void do_writes(struct mirror_set *ms, struct bio_list *writes) @@ -1262,7 +1270,7 @@ static int mirror_end_io(struct dm_target *ti, struct bio *bio, return DM_ENDIO_DONE; } - if (*error == BLK_STS_NOTSUPP) + if (*error == BLK_STS_NOTSUPP || *error == BLK_STS_INVAL) goto out; if (bio->bi_opf & REQ_RAHEAD) diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index aa239ccda270e9..e86d929b0e9518 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c @@ -223,7 +223,7 @@ static void do_metadata(struct work_struct *work) { struct mdata_req *req = container_of(work, struct mdata_req, work); - req->result = dm_io(req->io_req, 1, req->where, NULL, IOPRIO_DEFAULT); + req->result = dm_io(req->io_req, 1, req->where, NULL, NULL, IOPRIO_DEFAULT); } /* @@ -247,7 +247,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, blk_opf_t opf, struct mdata_req req; if (!metadata) - return dm_io(&io_req, 1, &where, NULL, IOPRIO_DEFAULT); + return dm_io(&io_req, 1, &where, NULL, NULL, IOPRIO_DEFAULT); req.where = &where; req.io_req = &io_req; diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index 5df710061a1165..beabbe3b39f3bd 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -178,8 +178,10 @@ static void dm_stat_free(struct rcu_head *head) kfree(s->program_id); kfree(s->aux_data); for_each_possible_cpu(cpu) { - dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size); - dm_kvfree(s->stat_percpu[cpu], s->percpu_alloc_size); + if (s->stat_percpu[cpu]) { + dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size); + dm_kvfree(s->stat_percpu[cpu], s->percpu_alloc_size); + } } dm_kvfree(s->stat_shared[0].tmp.histogram, s->histogram_alloc_size); dm_kvfree(s, s->shared_alloc_size); diff --git a/drivers/md/dm-switch.c b/drivers/md/dm-switch.c index 5952f02de1e626..e5b507b4fa7bf8 100644 --- a/drivers/md/dm-switch.c +++ b/drivers/md/dm-switch.c @@ -184,7 +184,7 @@ static void switch_region_table_write(struct switch_ctx *sctx, unsigned long reg pte = sctx->region_table[region_index]; pte &= ~((((region_table_slot_t)1 << sctx->region_table_entry_bits) - 1) << bit); pte |= (region_table_slot_t)value << bit; - sctx->region_table[region_index] = pte; + WRITE_ONCE(sctx->region_table[region_index], pte); } /* diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 1b076309125403..c67dea40c9cd38 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -395,7 +395,7 @@ static noinline int verity_recheck(struct dm_verity *v, struct dm_verity_io *io, io_loc.bdev = v->data_dev->bdev; io_loc.sector = cur_block << (v->data_dev_block_bits - SECTOR_SHIFT); io_loc.count = 1 << (v->data_dev_block_bits - SECTOR_SHIFT); - r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) goto free_ret; diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c index 493f5202ad0426..f02305de1c882c 100644 --- a/drivers/md/dm-writecache.c +++ b/drivers/md/dm-writecache.c @@ -474,12 +474,14 @@ struct io_notify { atomic_t count; }; -static void writecache_notify_io(unsigned long error, void *context) +static void writecache_notify_io(unsigned long error, unsigned long unsup, void *context) { struct io_notify *endio = context; if (unlikely(error != 0)) writecache_error(endio->wc, -EIO, "error writing metadata"); + else if (unlikely(unsup != 0)) + writecache_error(endio->wc, -EOPNOTSUPP, "error writing metadata"); BUG_ON(atomic_read(&endio->count) <= 0); if (atomic_dec_and_test(&endio->count)) complete(&endio->c); @@ -530,11 +532,11 @@ static void ssd_commit_flushed(struct dm_writecache *wc, bool wait_for_ios) req.notify.context = &endio; /* writing via async dm-io (implied by notify.fn above) won't return an error */ - (void) dm_io(&req, 1, ®ion, NULL, IOPRIO_DEFAULT); + (void) dm_io(&req, 1, ®ion, NULL, NULL, IOPRIO_DEFAULT); i = j; } - writecache_notify_io(0, &endio); + writecache_notify_io(0, 0, &endio); wait_for_completion_io(&endio.c); if (wait_for_ios) @@ -567,7 +569,7 @@ static void ssd_commit_superblock(struct dm_writecache *wc) req.notify.fn = NULL; req.notify.context = NULL; - r = dm_io(&req, 1, ®ion, NULL, IOPRIO_DEFAULT); + r = dm_io(&req, 1, ®ion, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) writecache_error(wc, r, "error writing superblock"); } @@ -595,7 +597,7 @@ static void writecache_disk_flush(struct dm_writecache *wc, struct dm_dev *dev) req.client = wc->dm_io; req.notify.fn = NULL; - r = dm_io(&req, 1, ®ion, NULL, IOPRIO_DEFAULT); + r = dm_io(&req, 1, ®ion, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) writecache_error(wc, r, "error flushing metadata: %d", r); } @@ -989,7 +991,7 @@ static int writecache_read_metadata(struct dm_writecache *wc, sector_t n_sectors req.client = wc->dm_io; req.notify.fn = NULL; - return dm_io(&req, 1, ®ion, NULL, IOPRIO_DEFAULT); + return dm_io(&req, 1, ®ion, NULL, NULL, IOPRIO_DEFAULT); } static void writecache_resume(struct dm_target *ti) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index d413bfaf352782..eb9e32995b79c9 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2630,9 +2630,10 @@ int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t) */ mutex_lock(&md->table_devices_lock); r = add_disk(md->disk); - mutex_unlock(&md->table_devices_lock); - if (r) + if (r) { + mutex_unlock(&md->table_devices_lock); return r; + } /* * Register the holder relationship for devices added before the disk @@ -2643,18 +2644,21 @@ int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t) if (r) goto out_undo_holders; } + mutex_unlock(&md->table_devices_lock); r = dm_sysfs_init(md); if (r) - goto out_undo_holders; + goto lock_out_undo_holders; md->type = type; + return 0; +lock_out_undo_holders: + mutex_lock(&md->table_devices_lock); out_undo_holders: list_for_each_entry_continue_reverse(td, &md->table_devices, list) bd_unlink_disk_holder(td->dm_dev.bdev, md->disk); - mutex_lock(&md->table_devices_lock); del_gendisk(md->disk); mutex_unlock(&md->table_devices_lock); return r; @@ -3140,7 +3144,7 @@ int dm_resume(struct mapped_device *md) r = -EINVAL; mutex_lock_nested(&md->suspend_lock, SINGLE_DEPTH_NESTING); - if (!dm_suspended_md(md)) + if (!dm_suspended_md(md) || test_bit(DMF_FREEING, &md->flags)) goto out; if (dm_suspended_internally_md(md)) { diff --git a/drivers/md/md.c b/drivers/md/md.c index d1465bcd86c812..4dd133445539aa 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1920,6 +1920,13 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_ rdev->bb_page, REQ_OP_READ, true)) return -EIO; bbp = (__le64 *)page_address(rdev->bb_page); + + /* check for badblocks api. */ + if (sb->bblog_shift >= BITS_PER_TYPE(sector_t)) { + pr_err("md: %pg: bogus bblog_shift %u for badblocks.\n", + rdev->bdev, sb->bblog_shift); + return -EINVAL; + } rdev->badblocks.shift = sb->bblog_shift; for (i = 0 ; i < (sectors << (9-3)) ; i++, bbp++) { u64 bb = le64_to_cpu(*bbp); diff --git a/drivers/md/persistent-data/dm-array.c b/drivers/md/persistent-data/dm-array.c index 8f8792e5580639..961fa3c1439a5b 100644 --- a/drivers/md/persistent-data/dm-array.c +++ b/drivers/md/persistent-data/dm-array.c @@ -38,6 +38,14 @@ struct array_block { */ #define CSUM_XOR 595846735 +/* + * Each array block can hold this many values. + */ +static uint32_t calc_max_entries(size_t value_size, size_t size_of_block) +{ + return (size_of_block - sizeof(struct array_block)) / value_size; +} + static void array_block_prepare_for_write(const struct dm_block_validator *v, struct dm_block *b, size_t size_of_block) @@ -55,6 +63,7 @@ static int array_block_check(const struct dm_block_validator *v, size_t size_of_block) { struct array_block *bh_le = dm_block_data(b); + uint32_t nr_entries, max_entries, value_size; __le32 csum_disk; if (dm_block_location(b) != le64_to_cpu(bh_le->blocknr)) { @@ -74,6 +83,26 @@ static int array_block_check(const struct dm_block_validator *v, return -EILSEQ; } + nr_entries = le32_to_cpu(bh_le->nr_entries); + max_entries = le32_to_cpu(bh_le->max_entries); + value_size = le32_to_cpu(bh_le->value_size); + + if (!value_size) { + DMERR_LIMIT("%s failed: value_size is zero", __func__); + return -EILSEQ; + } + + if (max_entries != calc_max_entries(value_size, size_of_block)) { + DMERR_LIMIT("%s failed: max_entries %u invalid for value_size %u", + __func__, max_entries, value_size); + return -EILSEQ; + } + + if (nr_entries > max_entries) { + DMERR_LIMIT("%s failed: too many entries", __func__); + return -EILSEQ; + } + return 0; } @@ -138,14 +167,6 @@ static void dec_ablock_entries(struct dm_array_info *info, struct array_block *a on_entries(info, ab, vt->dec); } -/* - * Each array block can hold this many values. - */ -static uint32_t calc_max_entries(size_t value_size, size_t size_of_block) -{ - return (size_of_block - sizeof(struct array_block)) / value_size; -} - /* * Allocate a new array block. The caller will need to unlock block. */ @@ -225,6 +246,14 @@ static int get_ablock(struct dm_array_info *info, dm_block_t b, return r; *ab = dm_block_data(*block); + if (le32_to_cpu((*ab)->value_size) != info->value_type.size) { + DMERR_LIMIT("%s failed: value_size %u != wanted %u", __func__, + le32_to_cpu((*ab)->value_size), + info->value_type.size); + dm_tm_unlock(info->btree_info.tm, *block); + return -EILSEQ; + } + return 0; } @@ -287,6 +316,14 @@ static int __shadow_ablock(struct dm_array_info *info, dm_block_t b, return r; *ab = dm_block_data(*block); + if (le32_to_cpu((*ab)->value_size) != info->value_type.size) { + DMERR_LIMIT("%s failed: value_size %u != wanted %u", __func__, + le32_to_cpu((*ab)->value_size), + info->value_type.size); + dm_tm_unlock(info->btree_info.tm, *block); + return -EILSEQ; + } + if (inc) inc_ablock_entries(info, *ab); diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 0a3cfdd3f5df80..54cddb3a98cd62 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -3365,7 +3365,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr, struct md_rdev *rdev = conf->mirrors[j].rdev; if (rdev == NULL || test_bit(Faulty, &rdev->flags)) { - still_degraded = false; + still_degraded = true; break; } } diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index a90cb84a4b4d0b..de0c4fcd8dfeb0 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -965,7 +965,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, */ mutex_unlock(&adap->lock); err = wait_for_completion_killable(&data->c); - cancel_delayed_work_sync(&data->work); + disable_delayed_work_sync(&data->work); mutex_lock(&adap->lock); if (err) @@ -2219,9 +2219,13 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, * Unprocessed messages are aborted if userspace isn't doing * any processing either. */ + mutex_lock(&adap->lock); if (!is_broadcast && !is_reply && !adap->follower_cnt && - !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT) + !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT) { + mutex_unlock(&adap->lock); return cec_feature_abort(adap, msg); + } + mutex_unlock(&adap->lock); break; } @@ -2234,10 +2238,12 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, * Send to the exclusive follower if there is one, otherwise send * to all followers. */ + mutex_lock(&adap->lock); if (adap->cec_follower) cec_queue_msg_fh(adap->cec_follower, msg); else cec_queue_msg_followers(adap, msg); + mutex_unlock(&adap->lock); return 0; } diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 0fcd3b5e60c8da..90a98f322f5ee3 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -371,9 +371,7 @@ void cec_unregister_adapter(struct cec_adapter *adap) return; #ifdef CONFIG_MEDIA_CEC_RC - /* Note: rc_unregister also calls rc_free */ rc_unregister_device(adap->rc); - adap->rc = NULL; #endif debugfs_remove_recursive(adap->cec_dir); #ifdef CONFIG_CEC_NOTIFIER diff --git a/drivers/media/cec/platform/meson/ao-cec-g12a.c b/drivers/media/cec/platform/meson/ao-cec-g12a.c index 41f5b8669cb01b..2c914f0008b508 100644 --- a/drivers/media/cec/platform/meson/ao-cec-g12a.c +++ b/drivers/media/cec/platform/meson/ao-cec-g12a.c @@ -405,6 +405,7 @@ static int meson_ao_cec_g12a_write(void *context, unsigned int addr, } static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = { + .name = "core", .reg_bits = 8, .val_bits = 8, .reg_read = meson_ao_cec_g12a_read, diff --git a/drivers/media/cec/platform/stm32/stm32-cec.c b/drivers/media/cec/platform/stm32/stm32-cec.c index 1ec0cece0a5b71..8c2fc232202de2 100644 --- a/drivers/media/cec/platform/stm32/stm32-cec.c +++ b/drivers/media/cec/platform/stm32/stm32-cec.c @@ -132,7 +132,8 @@ static void stm32_rx_done(struct stm32_cec *cec, u32 status) u32 val; regmap_read(cec->regmap, CEC_RXDR, &val); - cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF; + if (cec->rx_msg.len < CEC_MAX_MSG_SIZE) + cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF; } if (cec->irq_status & RXEND) { diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c index 3381d86096a1ff..3c6ce6f3d93e72 100644 --- a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c +++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c @@ -657,7 +657,8 @@ static void extron_process_received(struct extron_port *port, const char *data) if (!port || port->disconnected) return; - if (len < 5 || (len - 2) % 3 || data[len - 2] != '*') + if (len < 5 || ((len - 2) / 3 > sizeof(msg.msg)) || + (len - 2) % 3 || data[len - 2] != '*') goto malformed; while (*data != '*') { diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index c564485e3bbb55..0330e7f0881a79 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -906,9 +906,12 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) goto err; mutex_unlock(&dev->v4l2_lock); + return 0; err: + rtl2832_sdr_free_urbs(dev); + rtl2832_sdr_free_stream_bufs(dev); rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); mutex_unlock(&dev->v4l2_lock); @@ -1477,14 +1480,22 @@ static void rtl2832_sdr_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "\n"); - mutex_lock(&dev->vb_queue_lock); + /* + * vb2_video_unregister_device() releases the vb2 queue, which + * triggers rtl2832_sdr_stop_streaming() if streaming is active. + * stop_streaming() uses dev->udev to free URBs and coherent DMA + * stream buffers via usb_free_coherent(), so it must run before + * dev->udev is cleared. vb2_video_unregister_device() locks + * vb_queue_lock internally and stop_streaming() locks v4l2_lock, + * so neither may be held by the caller. + */ + v4l2_device_disconnect(&dev->v4l2_dev); + vb2_video_unregister_device(&dev->vdev); + mutex_lock(&dev->v4l2_lock); - /* No need to keep the urbs around after disconnection */ dev->udev = NULL; - v4l2_device_disconnect(&dev->v4l2_dev); - video_unregister_device(&dev->vdev); mutex_unlock(&dev->v4l2_lock); - mutex_unlock(&dev->vb_queue_lock); + v4l2_device_put(&dev->v4l2_dev); module_put(pdev->dev.parent->driver->owner); } diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index dd991c2ee70029..f51f9b98775922 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -1290,7 +1290,7 @@ static int alvium_set_ctrl_auto_exposure(struct alvium_dev *alvium, bool on) struct device *dev = &alvium->i2c_client->dev; int ret; - ret = alvium_write_hshake(alvium, REG_BCRM_WHITE_BALANCE_AUTO_RW, + ret = alvium_write_hshake(alvium, REG_BCRM_EXPOSURE_AUTO_RW, on ? 0x02 : 0x00); if (ret) { dev_err(dev, "Fail to set autoexposure reg\n"); diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 27a5c212a527fa..ac59908f57aae6 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1822,12 +1822,13 @@ static int imx355_probe(struct i2c_client *client) */ pm_runtime_set_active(imx355->dev); pm_runtime_enable(imx355->dev); - pm_runtime_idle(imx355->dev); ret = v4l2_async_register_subdev_sensor(&imx355->sd); if (ret < 0) goto error_media_entity_runtime_pm; + pm_runtime_idle(imx355->dev); + return 0; error_media_entity_runtime_pm: diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index 0b424c17e8804c..c3b22b238ea0bd 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -686,7 +686,7 @@ static int imx415_set_testpattern(struct imx415 *sensor, int val) cci_write(sensor->regmap, IMX415_DIG_CLP_MODE, 0x01, &ret); cci_write(sensor->regmap, IMX415_WRJ_OPEN, 0x01, &ret); } - return 0; + return ret; } static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) @@ -720,7 +720,7 @@ static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) ret = cci_write(sensor->regmap, IMX415_VMAX, format->height + ctrl->val, NULL); if (ret) - return ret; + break; /* * Exposure is set based on VMAX which has just changed, so * program exposure register as well diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c index 143dcfe104456d..0150e4d296af63 100644 --- a/drivers/media/i2c/ov02a10.c +++ b/drivers/media/i2c/ov02a10.c @@ -820,18 +820,16 @@ static int ov02a10_check_hwcfg(struct device *dev, struct ov02a10 *ov02a10) if (!ep) return -ENXIO; + /* Optional indication of MIPI clock voltage unit */ + if (!fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage", + &clk_volt)) + ov02a10->mipi_clock_voltage = clk_volt; + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); if (ret) return ret; - /* Optional indication of MIPI clock voltage unit */ - ret = fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage", - &clk_volt); - - if (!ret) - ov02a10->mipi_clock_voltage = clk_volt; - for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { if (link_freq_menu_items[i] == diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index c2e02f19181697..c09ad663f9de1a 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1116,10 +1116,8 @@ static void ov7740_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); - mutex_destroy(&ov7740->mutex); - v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler); - media_entity_cleanup(&ov7740->subdev.entity); v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&ov7740->subdev.entity); ov7740_free_controls(ov7740); pm_runtime_get_sync(&client->dev); diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 5b6f897a74fcdb..5abe51d21f89cc 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -648,7 +648,8 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl) ctrl->val ? OV9282_OUTPUT_ENABLE6_STROBE : 0, NULL); break; case V4L2_CID_FLASH_DURATION: - ret = cci_write(ov9282->regmap, OV9282_REG_STROBE_FRAME_SPAN, ctrl->val, NULL); + ret = cci_write(ov9282->regmap, OV9282_REG_STROBE_FRAME_SPAN, + ov9282_us_to_flash_duration(ov9282, ctrl->val), NULL); break; default: dev_err(ov9282->dev, "Invalid control %d", ctrl->id); diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c index 78d18c0281541b..6c0c821babc7d3 100644 --- a/drivers/media/i2c/vd55g1.c +++ b/drivers/media/i2c/vd55g1.c @@ -60,7 +60,10 @@ #define VD55G1_PATGEN_ENABLE BIT(0) #define VD55G1_REG_MANUAL_ANALOG_GAIN CCI_REG8(0x0501) #define VD55G1_REG_MANUAL_COARSE_EXPOSURE CCI_REG16_LE(0x0502) -#define VD55G1_REG_MANUAL_DIGITAL_GAIN CCI_REG16_LE(0x0504) +#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH0 CCI_REG16_LE(0x0504) +#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH1 CCI_REG16_LE(0x0506) +#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH2 CCI_REG16_LE(0x0508) +#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH3 CCI_REG16_LE(0x050a) #define VD55G1_REG_APPLIED_COARSE_EXPOSURE CCI_REG16_LE(0x00e8) #define VD55G1_REG_APPLIED_ANALOG_GAIN CCI_REG16_LE(0x00ea) #define VD55G1_REG_APPLIED_DIGITAL_GAIN CCI_REG16_LE(0x00ec) @@ -114,9 +117,8 @@ #define VD55G1_WIDTH 804 #define VD55G1_HEIGHT 704 -#define VD55G1_MODE_DEF 0 +#define VD55G1_MODE_IDX_DEF 0 #define VD55G1_NB_GPIOS 4 -#define VD55G1_MBUS_CODE_DEF 0 #define VD55G1_DGAIN_DEF 256 #define VD55G1_AGAIN_DEF 19 #define VD55G1_EXPO_MAX_TERM 64 @@ -634,7 +636,7 @@ static u32 vd55g1_get_fmt_code(struct vd55g1 *sensor, u32 code) goto adapt_bayer_pattern; } } - dev_warn(sensor->dev, "Unsupported mbus format\n"); + dev_warn(sensor->dev, "Unsupported mbus format: 0x%x\n", code); return code; @@ -850,9 +852,16 @@ static int vd55g1_update_expo_cluster(struct vd55g1 *sensor, bool is_auto) vd55g1_write(sensor, VD55G1_REG_MANUAL_ANALOG_GAIN, sensor->again_ctrl->val, &ret); - if (!is_auto && sensor->dgain_ctrl->is_new) - vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN, + if (!is_auto && sensor->dgain_ctrl->is_new) { + vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH0, sensor->dgain_ctrl->val, &ret); + vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH1, + sensor->dgain_ctrl->val, &ret); + vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH2, + sensor->dgain_ctrl->val, &ret); + vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH3, + sensor->dgain_ctrl->val, &ret); + } return ret; } @@ -1347,6 +1356,7 @@ static int vd55g1_init_state(struct v4l2_subdev *sd, { struct vd55g1 *sensor = to_vd55g1(sd); struct v4l2_subdev_format fmt = { 0 }; + int code; struct v4l2_subdev_route routes[] = { { .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE } }; @@ -1361,9 +1371,13 @@ static int vd55g1_init_state(struct v4l2_subdev *sd, if (ret) return ret; - vd55g1_update_pad_fmt(sensor, &vd55g1_supported_modes[VD55G1_MODE_DEF], - vd55g1_get_fmt_code(sensor, VD55G1_MBUS_CODE_DEF), - &fmt.format); + if (sensor->id == VD55G1_MODEL_ID_VD55G1) + code = vd55g1_mbus_formats_mono[0]; + else + code = vd55g1_mbus_formats_bayer[0][0]; + vd55g1_update_pad_fmt(sensor, + &vd55g1_supported_modes[VD55G1_MODE_IDX_DEF], + vd55g1_get_fmt_code(sensor, code), &fmt.format); return vd55g1_set_pad_fmt(sd, sd_state, &fmt); } diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 6b50fb422a614a..4811a048952463 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -522,8 +522,12 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data, "%s-vid-cap", data->v4l2_dev.name); ret = PTR_ERR_OR_ZERO(data->kthread_vid_cap); - if (!ret) - return 0; + if (ret) { + data->kthread_vid_cap = NULL; + goto error_rpm_put; + } + + return 0; error_rpm_put: pm_runtime_put_autosuspend(dev); diff --git a/drivers/media/pci/cobalt/cobalt-alsa-main.c b/drivers/media/pci/cobalt/cobalt-alsa-main.c index 7bb7f13c70c0d9..9ed547cd40af5e 100644 --- a/drivers/media/pci/cobalt/cobalt-alsa-main.c +++ b/drivers/media/pci/cobalt/cobalt-alsa-main.c @@ -135,7 +135,6 @@ int cobalt_alsa_init(struct cobalt_stream *s) err_exit_free: if (sc != NULL) snd_card_free(sc); - kfree(cobsc); err_exit: return ret; } diff --git a/drivers/media/pci/cx23885/cimax2.c b/drivers/media/pci/cx23885/cimax2.c index 15491898453485..723509fa1e065e 100644 --- a/drivers/media/pci/cx23885/cimax2.c +++ b/drivers/media/pci/cx23885/cimax2.c @@ -528,6 +528,7 @@ void netup_ci_exit(struct cx23885_tsport *port) if (NULL == state->ca.data) return; + cancel_work_sync(&state->work); dvb_ca_en50221_release(&state->ca); kfree(state); } diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 897bf1275b8484..c08c5780078c32 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -300,9 +300,11 @@ static u32 ipu_bridge_parse_rotation(struct acpi_device *adev, { const struct dmi_system_id *dmi_id; - dmi_id = dmi_first_match(upside_down_sensor_dmi_ids); - if (dmi_id && acpi_dev_hid_match(adev, dmi_id->driver_data)) - return 180; + /* A machine may have one entry per sensor, so check all matches. */ + for (dmi_id = dmi_first_match(upside_down_sensor_dmi_ids); dmi_id; + dmi_id = dmi_first_match(dmi_id + 1)) + if (acpi_dev_hid_match(adev, dmi_id->driver_data)) + return 180; switch (ssdb->degree) { case IPU_SENSOR_ROTATION_NORMAL: diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c index c9cdeb7054d750..24db2763de5443 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -761,6 +761,7 @@ static int isys_notifier_init(struct ipu6_isys *isys) err_parse: fwnode_handle_put(ep); + v4l2_async_nf_cleanup(&isys->notifier); return ret; } diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 6bcde506adf506..db65506ea885b2 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -878,6 +878,9 @@ static int get_resources(struct saa7164_dev *dev) if (request_mem_region(pci_resource_start(dev->pci, 2), pci_resource_len(dev->pci, 2), dev->name)) return 0; + + release_mem_region(pci_resource_start(dev->pci, 0), + pci_resource_len(dev->pci, 0)); } printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n", @@ -1000,8 +1003,7 @@ static int saa7164_dev_setup(struct saa7164_dev *dev) dev->name, dev->pci->subsystem_vendor, dev->pci->subsystem_device); - saa7164_devcount--; - return -ENODEV; + goto err_devlist; } /* PCI/e allocations */ @@ -1039,7 +1041,7 @@ static int saa7164_dev_setup(struct saa7164_dev *dev) iounmap(dev->lmmio); err_ioremap_bar0: release_resources(dev); - +err_devlist: scoped_guard(mutex, &devlist) { list_del(&dev->devlist); } diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c index f707bdc1fb0f1d..b8285df6447388 100644 --- a/drivers/media/pci/zoran/zoran_card.c +++ b/drivers/media/pci/zoran/zoran_card.c @@ -885,7 +885,7 @@ static int zoran_init_video_device(struct zoran *zr, struct video_device *video_ static void zoran_exit_video_devices(struct zoran *zr) { video_unregister_device(zr->video_dev); - kfree(zr->video_dev); + zr->video_dev = NULL; } static int zoran_init_video_devices(struct zoran *zr) diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c index 0b3d58b9f2f7ae..2ba4ae20f82923 100644 --- a/drivers/media/platform/amphion/venc.c +++ b/drivers/media/platform/amphion/venc.c @@ -973,8 +973,6 @@ static int venc_start_session(struct vpu_inst *inst, u32 type) venc->ready_count = 0; venc->stopped = false; vpu_process_output_buffer(inst); - if (venc->frame_count == 0) - dev_err(inst->dev, "[%d] there is no input when starting\n", inst->id); return 0; error: diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index 94a389b3f8333b..f28e9f4354acaf 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -698,6 +698,8 @@ static int __mali_c55_power_on(struct mali_c55 *mali_c55) mali_c55->resets); if (ret) { dev_err(mali_c55->dev, "failed to deassert resets\n"); + clk_bulk_disable_unprepare(ARRAY_SIZE(mali_c55->clks), + mali_c55->clks); return ret; } diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-params.c b/drivers/media/platform/arm/mali-c55/mali-c55-params.c index de0e9d898db7b9..33e2232ec8f5bf 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-params.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-params.c @@ -212,6 +212,7 @@ mali_c55_params_aexp_hist_weights(struct mali_c55 *mali_c55, val = params->zone_weights[MALI_C55_MAX_ZONES - 1]; addr = base + MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET + (4 * 56); + mali_c55_ctx_write(mali_c55, addr, val & MALI_C55_AEXP_HIST_ZONE_WEIGHT_MASK); } static void mali_c55_params_digital_gain(struct mali_c55 *mali_c55, diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h index f098effde7b497..4cd13b702d9f0d 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h +++ b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h @@ -173,7 +173,7 @@ enum mali_c55_interrupts { #define MALI_C55_AEXP_HIST_SWITCH_MASK GENMASK(14, 13) #define MALI_C55_AEXP_HIST_SWITCH(x) ((x) << 13) #define MALI_C55_AEXP_IHIST_DISABLE_MASK BIT(16) -#define MALI_C55_AEXP_IHIST_DISABLE (0x01 << 12) +#define MALI_C55_AEXP_IHIST_DISABLE (0x01 << 16) #define MALI_C55_AEXP_SRC_MASK BIT(24) #define MALI_C55_REG_TPG_CH0 0x18ed8 diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c index c4f46651dcee3a..6706939b4a9020 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c @@ -15,7 +15,7 @@ #include "mali-c55-registers.h" /* Scaling factor in Q4.20 format. */ -#define MALI_C55_RSZ_SCALER_FACTOR (1U << 20) +#define MALI_C55_RSZ_SCALER_FACTOR BIT_ULL(20) #define MALI_C55_RSZ_COEFS_BANKS 8 #define MALI_C55_RSZ_COEFS_ENTRIES 64 diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c index 8d28ba0b59a399..cc7627e9a51a83 100644 --- a/drivers/media/platform/broadcom/bcm2835-unicam.c +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -2158,7 +2158,8 @@ static int unicam_video_link_validate(struct media_link *link) * In order to allow the applications using the old behaviour to * run, let's accept the old combination, but warn about it. */ - if (fmtinfo->fourcc != fmt->pixelformat) { + if (fmt->pixelformat != fmtinfo->fourcc && + fmt->pixelformat != fmtinfo->unpacked_fourcc) { if ((fmt->pixelformat == V4L2_PIX_FMT_BGR24 && format->code == MEDIA_BUS_FMT_BGR888_1X24) || (fmt->pixelformat == V4L2_PIX_FMT_RGB24 && diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index bb2ba9204a8365..6564cf3ec739b9 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -5,6 +5,7 @@ * Copyright (C) 2021-2023 CHIPS&MEDIA INC */ +#include #include #include "wave5-helper.h" @@ -474,7 +475,10 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst) v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); } - inst->queuing_fail = false; + if (inst->queuing_fail) { + inst->queuing_fail = false; + v4l2_m2m_try_schedule(m2m_ctx); + } } static int wave5_vpu_dec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) @@ -819,7 +823,15 @@ static int wave5_vpu_dec_stop(struct vpu_instance *inst) * calls do not block on a mutex while inside this spinlock. */ spin_unlock_irqrestore(&inst->state_spinlock, flags); + /* + * V4L2_DEC_CMD_STOP can arrive while the device is runtime + * suspended (e.g. on pipeline teardown). Setting the EOS flag + * accesses VPU registers via send_firmware_command(), so the + * device must be resumed first to avoid an asynchronous SError. + */ + pm_runtime_resume_and_get(inst->dev->dev); ret = wave5_vpu_dec_set_eos_on_firmware(inst); + pm_runtime_put_autosuspend(inst->dev->dev); if (ret) return ret; @@ -1403,6 +1415,7 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { struct dec_initial_info *initial_info = &inst->codec_info->dec_info.initial_info; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; if (inst->state == VPU_INST_STATE_STOP) ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ); @@ -1410,6 +1423,7 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count goto return_buffers; if (inst->state == VPU_INST_STATE_INIT_SEQ && + p_dec_info->initial_info_obtained && inst->dev->product_code == WAVE521C_CODE) { if (initial_info->luma_bitdepth != 8) { dev_info(inst->dev->dev, "%s: no support for %d bit depth", @@ -1418,7 +1432,6 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count goto return_buffers; } } - } pm_runtime_put_autosuspend(inst->dev->dev); return ret; @@ -1536,15 +1549,15 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q) { struct vpu_instance *inst = vb2_get_drv_priv(q); struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; - - bool check_cmd = TRUE; + unsigned long timeout; dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type); pm_runtime_resume_and_get(inst->dev->dev); inst->empty_queue = true; - while (check_cmd) { + + timeout = jiffies + msecs_to_jiffies(VPU_DEC_STOP_TIMEOUT); + while (true) { struct queue_status_info q_status; - struct dec_output_info dec_output_info; wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); if ((inst->state == VPU_INST_STATE_STOP || @@ -1553,8 +1566,10 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q) q_status.report_queue_count == 0) break; - if (wave5_vpu_dec_get_output_info(inst, &dec_output_info)) - dev_dbg(inst->dev->dev, "there is no output info\n"); + if (time_after(jiffies, timeout)) + break; + + usleep_range(1000, 2000); } v4l2_m2m_update_stop_streaming_state(m2m_ctx, q); @@ -1651,6 +1666,7 @@ static void wave5_vpu_dec_device_run(void *priv) struct queue_status_info q_status; u32 fail_res = 0; int ret = 0; + bool cmd_issued = false; dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__); pm_runtime_resume_and_get(inst->dev->dev); @@ -1662,9 +1678,13 @@ static void wave5_vpu_dec_device_run(void *priv) } else if (!inst->eos && inst->queuing_num == 0 && inst->state == VPU_INST_STATE_PIC_RUN) { - dev_dbg(inst->dev->dev, "%s: no bitstream for feeding, so skip ", __func__); - inst->empty_queue = true; - goto finish_job_and_return; + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); + if (q_status.instance_queue_count == v4l2_m2m_num_src_bufs_ready(m2m_ctx)) { + dev_dbg(inst->dev->dev, "%s: no bitstream, skip\n", + __func__); + inst->empty_queue = true; + goto finish_job_and_return; + } } } @@ -1744,6 +1764,7 @@ static void wave5_vpu_dec_device_run(void *priv) inst->retry = false; if (!inst->eos) inst->queuing_num--; + cmd_issued = true; } break; default: @@ -1761,8 +1782,16 @@ static void wave5_vpu_dec_device_run(void *priv) * in power and CPU time. * If EOS is passed, device_run will not call job_finish no more, it is called * only if HW is idle status in order to reduce overhead. + * + * Deferring job_finish() is only safe when this run actually queued a + * DEC_PIC command (cmd_issued): that guarantees a completion IRQ, and + * thus a later finish_decode(), will release the shared job slot. When + * device_run() is entered with no command to issue (e.g. a job that was + * queued while draining but reached the STOP state by the time it ran), + * no IRQ follows, so finish the job here to avoid leaking the slot and + * stalling every instance sharing the VPU. */ - if (!inst->sent_eos) + if (!inst->sent_eos || !cmd_issued) v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); } @@ -1776,11 +1805,22 @@ static void wave5_vpu_dec_job_abort(void *priv) if (ret) return; + /* + * job_abort() runs from the STREAMOFF path and may be called while the + * device is runtime suspended. Setting the EOS flag talks to the + * firmware (send_firmware_command() accesses VPU registers), so the + * device must be resumed first; otherwise the register access faults + * with an asynchronous SError. + */ + pm_runtime_resume_and_get(inst->dev->dev); + ret = wave5_vpu_dec_set_eos_on_firmware(inst); if (ret) dev_warn(inst->dev->dev, "Setting EOS for the bitstream, fail: %d\n", ret); + pm_runtime_put_autosuspend(inst->dev->dev); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); } diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index e6c94b6f26711a..f9fcdf4c224b30 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -1494,7 +1494,8 @@ static const struct vb2_ops wave5_vpu_enc_vb2_ops = { .stop_streaming = wave5_vpu_enc_stop_streaming, }; -static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt, +static void wave5_set_default_format(struct vpu_instance *inst, + struct v4l2_pix_format_mplane *src_fmt, struct v4l2_pix_format_mplane *dst_fmt) { src_fmt->pixelformat = enc_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt; @@ -1506,6 +1507,7 @@ static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt, wave5_update_pix_fmt(dst_fmt, VPU_FMT_TYPE_CODEC, W5_DEF_ENC_PIC_WIDTH, W5_DEF_ENC_PIC_HEIGHT, &enc_frmsize[VPU_FMT_TYPE_CODEC]); + inst->std = wave5_to_vpu_std(dst_fmt->pixelformat, inst->type); } static int wave5_vpu_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) @@ -1770,7 +1772,7 @@ static int wave5_vpu_open_enc(struct file *filp) inst->v4l2_fh.ctrl_handler = v4l2_ctrl_hdl; v4l2_ctrl_handler_setup(v4l2_ctrl_hdl); - wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt); + wave5_set_default_format(inst, &inst->src_fmt, &inst->dst_fmt); inst->conf_win.width = inst->dst_fmt.width; inst->conf_win.height = inst->dst_fmt.height; inst->colorspace = V4L2_COLORSPACE_REC709; diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h index 4ebd48d5550ea6..e04f2dbf3b6557 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h @@ -59,7 +59,7 @@ // application specific configuration #define VPU_ENC_TIMEOUT 60000 #define VPU_DEC_TIMEOUT 60000 -#define VPU_DEC_STOP_TIMEOUT 10 +#define VPU_DEC_STOP_TIMEOUT 300 // for WAVE encoder #define USE_SRC_PRP_AXI 0 diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index 8f4da4cf55d26e..f412aadb5996b1 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -294,7 +294,11 @@ static int mdp_probe(struct platform_device *pdev) goto err_destroy_clock_wq; } mdp->scp = platform_get_drvdata(mm_pdev); - put_device(&mm_pdev->dev); + } + + if (!mdp->scp) { + ret = -EPROBE_DEFER; + goto err_destroy_clock_wq; } mdp->rproc_handle = scp_get_rproc(mdp->scp); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index c580c831972ec3..01374b7af84388 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -107,7 +107,7 @@ static int __mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd, return ret; /* The memory input can be routed to the first pipeline only. */ - for_each_active_route(&state->routing, route) { + for_each_active_route(routing, route) { if (route->sink_pad == xbar->num_sinks - 1 && route->source_pad != xbar->num_sinks) { dev_dbg(xbar->isi->dev, @@ -145,10 +145,10 @@ mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar *xbar, */ for_each_active_route(&state->routing, route) { if (route->source_pad != source_pad || - !(source_streams & BIT(route->source_stream))) + !(source_streams & BIT_ULL(route->source_stream))) continue; - sink_streams |= BIT(route->sink_stream); + sink_streams |= BIT_ULL(route->sink_stream); sink_pad = route->sink_pad; } diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index fe4adfa3a1f011..056603055c8162 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -151,7 +151,7 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = { .fourcc = V4L2_PIX_FMT_XBGR32, .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT | MXC_ISI_VIDEO_M2M_CAP, - .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8, + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XRGB8, .isi_out_format = CHNL_IMG_CTRL_FORMAT_XRGB888, .mem_planes = 1, .color_planes = 1, diff --git a/drivers/media/platform/qcom/camss/camss-csid-680.c b/drivers/media/platform/qcom/camss/camss-csid-680.c index b510e85b3d7c6d..31e500f22677c1 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-680.c +++ b/drivers/media/platform/qcom/camss/camss-csid-680.c @@ -161,6 +161,7 @@ #define CSID_RDI_FRM_DROP_PERIOD(rdi) (0x544 + 0x100 * (rdi)) #define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi) (0x548 + 0x100 * (rdi)) #define CSID_RDI_IRQ_SUBSAMPLE_PERIOD(rdi) (0x54c + 0x100 * (rdi)) +#define CSID_RDI_RPP_HCROP(rdi) (0x550 + 0x100 * (rdi)) #define CSID_RDI_PIX_DROP_PATTERN(rdi) (0x558 + 0x100 * (rdi)) #define CSID_RDI_PIX_DROP_PERIOD(rdi) (0x55c + 0x100 * (rdi)) #define CSID_RDI_LINE_DROP_PATTERN(rdi) (0x560 + 0x100 * (rdi)) @@ -248,9 +249,12 @@ static void __csid_configure_top(struct csid_device *csid) static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc) { struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port]; + struct v4l2_mbus_framefmt *sink_format = &csid->fmt[MSM_CSID_PAD_SINK]; const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); + bool sp11_imx681 = csid_is_sp11_imx681_format(csid, sink_format); + bool crop = csid_is_cphy_raw10_3844(csid, sink_format); u8 lane_cnt = csid->phy.lane_cnt; u8 dt_id; u32 val; @@ -258,8 +262,9 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 if (!lane_cnt) lane_cnt = 4; - val = 0; - writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port)); + /* Preserve the existing reset-value path outside the qualified route. */ + if (!sp11_imx681) + writel(0, csid->base + CSID_RDI_FRM_DROP_PERIOD(port)); /* * DT_ID is a two bit bitfield that is concatenated with @@ -274,9 +279,13 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 * CID : VC 3:0 << 2 | DT_ID 1:0 */ dt_id = port & 0x03; + val = 0; - /* note: for non-RDI path, this should be format->decode_format */ - val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; + /* Decoding is required for the explicitly selected receiver crop. */ + if (crop) + val |= format->decode_format << RDI_CFG0_DECODE_FORMAT; + else + val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; val |= format->data_type << RDI_CFG0_DATA_TYPE; val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; val |= dt_id << RDI_CFG0_DT_ID; @@ -285,14 +294,31 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 val = RDI_CFG1_TIMESTAMP_STB_FRAME; val |= RDI_CFG1_BYTE_CNTR_EN; val |= RDI_CFG1_TIMESTAMP_EN; - val |= RDI_CFG1_DROP_H_EN; - val |= RDI_CFG1_DROP_V_EN; - val |= RDI_CFG1_CROP_H_EN; - val |= RDI_CFG1_CROP_V_EN; + if (!sp11_imx681) { + val |= RDI_CFG1_DROP_H_EN; + val |= RDI_CFG1_DROP_V_EN; + val |= RDI_CFG1_CROP_H_EN; + val |= RDI_CFG1_CROP_V_EN; + } val |= RDI_CFG1_PACKING_MIPI; + if (crop) { + /* C-PHY RAW10 3844 is the IMX681 route: crop to 4800-byte rows. */ + writel(3839 << 16, csid->base + CSID_RDI_RPP_HCROP(port)); + val |= RDI_CFG1_CROP_H_EN; + } writel(val, csid->base + CSID_RDI_CFG1(port)); + if (sp11_imx681) { + /* Program an explicit keep-all state for every drop engine. */ + writel(1, csid->base + CSID_RDI_FRM_DROP_PERIOD(port)); + writel(0, csid->base + CSID_RDI_FRM_DROP_PATTERN(port)); + writel(1, csid->base + CSID_RDI_PIX_DROP_PERIOD(port)); + writel(0, csid->base + CSID_RDI_PIX_DROP_PATTERN(port)); + writel(1, csid->base + CSID_RDI_LINE_DROP_PERIOD(port)); + writel(0, csid->base + CSID_RDI_LINE_DROP_PATTERN(port)); + } + val = 0; writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port)); @@ -310,6 +336,44 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 writel(val, csid->base + CSID_RDI_CFG0(port)); } +static void csid_log_status(struct csid_device *csid) +{ + struct v4l2_mbus_framefmt *sink_format = + &csid->fmt[MSM_CSID_PAD_SINK]; + int i; + + if (csid->camss->res->version != CAMSS_X1E80100 || + !csid_is_sp11_imx681_format(csid, sink_format)) + return; + + dev_info(csid->camss->dev, + "CSID%u IMX681 stop: RX_CFG0=%#010x RX_CFG1=%#010x TOTAL_PKTS=%u ECC=%u CRC=%u\n", + csid->id, readl(csid->base + CSID_CSI2_RX_CFG0), + readl(csid->base + CSID_CSI2_RX_CFG1), + readl(csid->base + CSID_CSI2_RX_TOTAL_PKTS_RCVD), + readl(csid->base + CSID_CSI2_RX_STATS_ECC), + readl(csid->base + CSID_CSI2_RX_CRC_ERRORS)); + dev_info(csid->camss->dev, + "CSID%u IMX681 IRQ: TOP=%#010x BUF_DONE=%#010x RX=%#010x\n", + csid->id, readl(csid->base + CSID_TOP_IRQ_STATUS), + readl(csid->base + CSID_BUF_DONE_IRQ_STATUS), + readl(csid->base + CSID_CSI2_RX_IRQ_STATUS)); + + for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { + if (!(csid->phy.en_vc & BIT(i))) + continue; + + dev_info(csid->camss->dev, + "CSID%u IMX681 linked RDI%d: IRQ=%#010x CFG0=%#010x CTRL=%#010x CFG1=%#010x HCROP=%#010x\n", + csid->id, i, + readl(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i)), + readl(csid->base + CSID_RDI_CFG0(i)), + readl(csid->base + CSID_RDI_CTRL(i)), + readl(csid->base + CSID_RDI_CFG1(i)), + readl(csid->base + CSID_RDI_RPP_HCROP(i))); + } +} + static void csid_configure_stream(struct csid_device *csid, u8 enable) { int i; @@ -439,6 +503,7 @@ static void csid_subdev_init(struct csid_device *csid) {} const struct csid_hw_ops csid_ops_680 = { .configure_testgen_pattern = NULL, .configure_stream = csid_configure_stream, + .log_status = csid_log_status, .hw_version = csid_hw_version, .isr = csid_isr, .reset = csid_reset, diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 86d0e24069a9aa..a615ac1541288f 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -532,6 +532,28 @@ const struct csid_format_info *csid_get_fmt_entry(const struct csid_format_info return &formats[0]; } +bool csid_is_cphy_raw10_3844(struct csid_device *csid, + const struct v4l2_mbus_framefmt *format) +{ + return format->width == 3844 && + csid_is_sp11_imx681_format(csid, format); +} + +bool csid_is_sp11_imx681_format(struct csid_device *csid, + const struct v4l2_mbus_framefmt *format) +{ + const struct csid_format_info *fmt; + + fmt = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, format->code); + + /* These explicit transport/geometry signatures are unique to the IMX681. */ + return csid->camss->res->version == CAMSS_X1E80100 && + csid->phy.bus_type == V4L2_MBUS_CSI2_CPHY && + (format->width == 3840 || format->width == 3844) && + format->height == 2640 && fmt->data_type == MIPI_CSI2_DT_RAW10; +} + /* * csid_set_clock_rates - Calculate and set clock rates on CSID module * @csiphy: CSID device @@ -781,6 +803,10 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) return -ENOLINK; } + /* Sample status before pipeline PM turns the CSID clocks off. */ + if (!enable && csid->res->hw_ops->log_status) + csid->res->hw_ops->log_status(csid); + if (csid->phy.need_vc_update) { csid->res->hw_ops->configure_stream(csid, enable); csid->phy.need_vc_update = false; @@ -849,13 +875,17 @@ static void csid_try_format(struct csid_device *csid, case MSM_CSID_PAD_SRC: if (csid->testgen.nmodes == CSID_PAYLOAD_MODE_DISABLED || csid->testgen_mode->cur.val == 0) { - /* Test generator is disabled, */ - /* keep pad formats in sync */ + const struct v4l2_mbus_framefmt *sink_format; u32 code = fmt->code; - *fmt = *__csid_get_format(csid, sd_state, - MSM_CSID_PAD_SINK, which); + /* Test generator is disabled, */ + /* keep pad formats in sync */ + sink_format = __csid_get_format(csid, sd_state, + MSM_CSID_PAD_SINK, which); + *fmt = *sink_format; fmt->code = csid->res->hw_ops->src_pad_code(csid, fmt->code, 0, code); + if (csid_is_cphy_raw10_3844(csid, sink_format)) + fmt->width = 3840; } else { /* Test generator is enabled, set format on source */ /* pad to allow test generator usage */ diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index b98973d7edd524..7a91ff319ae966 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -82,6 +82,12 @@ struct csid_hw_ops { */ void (*configure_stream)(struct csid_device *csid, u8 enable); + /* + * log_status - Log a read-only hardware status snapshot while powered + * @csid: CSID device + */ + void (*log_status)(struct csid_device *csid); + /* * configure_testgen_pattern - Validates and configures output pattern mode * of test pattern generator @@ -197,6 +203,11 @@ const struct csid_format_info *csid_get_fmt_entry(const struct csid_format_info unsigned int nformats, u32 code); +bool csid_is_cphy_raw10_3844(struct csid_device *csid, + const struct v4l2_mbus_framefmt *format); +bool csid_is_sp11_imx681_format(struct csid_device *csid, + const struct v4l2_mbus_framefmt *format); + int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, const struct camss_subdev_resources *res, u8 id); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-680.c b/drivers/media/platform/qcom/camss/camss-vfe-680.c index 99036e7c1e763a..54600ecab9885c 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-680.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-680.c @@ -38,6 +38,7 @@ #define VFE_BUS_WRITE_CLIENT_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x400 : 0xe00) + (c) * 0x100) #define VFE_BUS_WRITE_CLIENT_CFG_EN BIT(0) +#define VFE_BUS_WRITE_CLIENT_CFG_MODE_MIPI_RAW BIT(16) #define VFE_BUS_IMAGE_ADDR(vfe, c) ((vfe_is_lite(vfe) ? 0x404 : 0xe04) + (c) * 0x100) #define VFE_BUS_FRAME_INCR(vfe, c) ((vfe_is_lite(vfe) ? 0x408 : 0xe08) + (c) * 0x100) #define VFE_BUS_IMAGE_CFG0(vfe, c) ((vfe_is_lite(vfe) ? 0x40c : 0xe0c) + (c) * 0x100) @@ -152,12 +153,15 @@ static void vfe_wm_update(struct vfe_device *vfe, u8 rdi, u32 addr, writel(addr, vfe->base + VFE_BUS_IMAGE_ADDR(vfe, wm)); } +static bool vfe_is_imx681_raw(struct vfe_device *vfe, u8 rdi); + static void vfe_wm_start(struct vfe_device *vfe, u8 rdi, struct vfe_line *line) { struct v4l2_pix_format_mplane *pix = &line->video_out.active_fmt.fmt.pix_mp; u32 stride = pix->plane_fmt[0].bytesperline; u32 cfg; + u32 wm_cfg = VFE_BUS_WRITE_CLIENT_CFG_EN; u8 wm; cfg = VFE_BUS_IMAGE_CFG0_DATA(pix->height, stride); @@ -185,18 +189,60 @@ static void vfe_wm_start(struct vfe_device *vfe, u8 rdi, struct vfe_line *line) /* We don't process IRQs for VFE in RDI mode at the moment */ vfe_disable_irq(vfe); - /* Enable WM */ - writel(VFE_BUS_WRITE_CLIENT_CFG_EN, + if (vfe_is_imx681_raw(vfe, rdi)) + wm_cfg |= VFE_BUS_WRITE_CLIENT_CFG_MODE_MIPI_RAW; + + /* Enable WM, selecting MIPI RAW only for the qualified IMX681 route. */ + writel(wm_cfg, vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm)); dev_dbg(vfe->camss->dev, "RDI%d WM:%d width %d height %d stride %d\n", rdi, wm, pix->width, pix->height, stride); } +static bool vfe_is_imx681_raw(struct vfe_device *vfe, u8 rdi) +{ + struct v4l2_pix_format_mplane *pix; + + if (vfe->camss->res->version != CAMSS_X1E80100 || + vfe_is_lite(vfe) || rdi >= vfe->res->line_num) + return false; + + pix = &vfe->line[rdi].video_out.active_fmt.fmt.pix_mp; + + return pix->pixelformat == V4L2_PIX_FMT_SRGGB10P && + pix->width == 3840 && pix->height == 2640 && + pix->plane_fmt[0].bytesperline == 4800; +} + static void vfe_wm_stop(struct vfe_device *vfe, u8 rdi) { u8 wm = RDI_WM(rdi); + if (vfe_is_imx681_raw(vfe, rdi)) { + dev_info(vfe->camss->dev, + "VFE%u IMX681 RDI%u WM%u stop: CFG=%#010x ADDR=%#010x INCR=%#010x IMG=%#010x/%#010x/%#010x PACKER=%#010x\n", + vfe->id, rdi, wm, + readl(vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm)), + readl(vfe->base + VFE_BUS_IMAGE_ADDR(vfe, wm)), + readl(vfe->base + VFE_BUS_FRAME_INCR(vfe, wm)), + readl(vfe->base + VFE_BUS_IMAGE_CFG0(vfe, wm)), + readl(vfe->base + VFE_BUS_IMAGE_CFG1(vfe, wm)), + readl(vfe->base + VFE_BUS_IMAGE_CFG2(vfe, wm)), + readl(vfe->base + VFE_BUS_PACKER_CFG(vfe, wm))); + dev_info(vfe->camss->dev, + "VFE%u IMX681 IRQ: TOP=%#010x/%#010x BUS=%#010x/%#010x VIOL=%#010x OVERFLOW=%#010x IMAGE_VIOL=%#010x\n", + vfe->id, + readl(vfe->base + VFE_TOP_IRQn_STATUS(vfe, 0)), + readl(vfe->base + VFE_TOP_IRQn_STATUS(vfe, 1)), + readl(vfe->base + VFE_BUS_IRQn_STATUS(vfe, 0)), + readl(vfe->base + VFE_BUS_IRQn_STATUS(vfe, 1)), + readl(vfe->base + VFE_BUS_WR_VIOLATION_STATUS(vfe)), + readl(vfe->base + VFE_BUS_WR_OVERFLOW_STATUS(vfe)), + readl(vfe->base + + VFE_BUS_WR_IMAGE_VIOLATION_STATUS(vfe))); + } + writel(0, vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm)); } diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 0852eb6f13154c..7ff03ebb5450db 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -249,11 +249,43 @@ static int video_prepare_streaming(struct vb2_queue *q) return ret; } +static void video_stop_subdevices(struct camss_video *video, + struct media_entity *last_started) +{ + struct video_device *vdev = &video->vdev; + struct media_entity *entity = &vdev->entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + int ret; + + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret < 0 && ret != -ENOIOCTLCMD) + dev_err(video->camss->dev, + "Video pipeline stop failed: %d\n", ret); + + if (last_started && entity == last_started) + break; + } +} + static int video_start_streaming(struct vb2_queue *q, unsigned int count) { struct camss_video *video = vb2_get_drv_priv(q); struct video_device *vdev = &video->vdev; struct media_entity *entity; + struct media_entity *last_started = NULL; struct media_pad *pad; struct v4l2_subdev *subdev; int ret; @@ -284,11 +316,15 @@ static int video_start_streaming(struct vb2_queue *q, unsigned int count) ret = v4l2_subdev_call(subdev, video, s_stream, 1); if (ret < 0 && ret != -ENOIOCTLCMD) goto error; + if (ret >= 0) + last_started = entity; } return 0; error: + if (last_started) + video_stop_subdevices(video, last_started); video_device_pipeline_stop(vdev); flush_buffers: @@ -301,31 +337,8 @@ static void video_stop_streaming(struct vb2_queue *q) { struct camss_video *video = vb2_get_drv_priv(q); struct video_device *vdev = &video->vdev; - struct media_entity *entity; - struct media_pad *pad; - struct v4l2_subdev *subdev; - int ret; - - entity = &vdev->entity; - while (1) { - pad = &entity->pads[0]; - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - break; - pad = media_pad_remote_pad_first(pad); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - break; - - entity = pad->entity; - subdev = media_entity_to_v4l2_subdev(entity); - - ret = v4l2_subdev_call(subdev, video, s_stream, 0); - - if (ret) { - dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret); - return; - } - } + video_stop_subdevices(video, NULL); video_device_pipeline_stop(vdev); diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 10e33b8a73f607..391e1fc5f6e2ec 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -1293,7 +1293,7 @@ int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_ int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; - u32 gop_size = inst->fw_caps[GOP_SIZE].value; + u32 gop_size = inst->fw_caps[cap_id].value; u32 b_frame = inst->fw_caps[B_FRAME].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; struct hfi_intra_period intra_period; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c index ca1545d28b5310..eff9216bb50143 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -171,7 +171,9 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .max = (1 << 16) - 1, .step_or_mask = 1, .value = 30, - .set = iris_set_u32 + .hfi_id = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_intra_period, }, { .cap_id = ENTROPY_MODE, @@ -240,7 +242,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .step_or_mask = 1, .value = 0, .hfi_id = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH, - .flags = CAP_FLAG_OUTPUT_PORT, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, .set = iris_set_ir_period_gen1, }, { @@ -281,16 +283,6 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .value = 0, .flags = CAP_FLAG_OUTPUT_PORT, }, - { - .cap_id = INTRA_PERIOD, - .min = 0, - .max = 1, - .step_or_mask = 1, - .value = 0, - .hfi_id = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD, - .flags = CAP_FLAG_OUTPUT_PORT, - .set = iris_set_intra_period, - }, { .cap_id = LAYER_ENABLE, .min = 0, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index c9256f2323dc45..ff48333ad08989 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -165,7 +165,6 @@ enum platform_inst_fw_cap_type { USE_LTR, MARK_LTR, B_FRAME, - INTRA_PERIOD, LAYER_ENABLE, LAYER_TYPE_H264, LAYER_TYPE_HEVC, diff --git a/drivers/media/platform/qcom/iris/iris_state.c b/drivers/media/platform/qcom/iris/iris_state.c index d14472414750dc..e991f34916ec6e 100644 --- a/drivers/media/platform/qcom/iris/iris_state.c +++ b/drivers/media/platform/qcom/iris/iris_state.c @@ -60,9 +60,9 @@ int iris_inst_change_state(struct iris_inst *inst, return -EINVAL; change_state: - inst->state = request_state; dev_dbg(inst->core->dev, "state changed from %x to %x\n", inst->state, request_state); + inst->state = request_state; return 0; } diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index 14d63dc76c9bac..1c052ca235bec9 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -449,14 +449,21 @@ static int iris_enum_frameintervals(struct file *filp, void *fh, static int iris_querycap(struct file *filp, void *fh, struct v4l2_capability *cap) { + struct iris_core *core = video_drvdata(filp); struct iris_inst *inst = iris_get_inst(filp); + char *info; strscpy(cap->driver, IRIS_DRV_NAME, sizeof(cap->driver)); - if (inst->domain == DECODER) + if (inst->domain == DECODER) { strscpy(cap->card, "Iris Decoder", sizeof(cap->card)); - else + info = "dec"; + } else { strscpy(cap->card, "Iris Encoder", sizeof(cap->card)); + info = "enc"; + } + snprintf(cap->bus_info, sizeof(cap->bus_info), + "plat:%s:%s", dev_name(core->dev), info); return 0; } diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index ab41da1f47c820..e4847c10770971 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -237,7 +237,7 @@ void iris_vpu_power_off(struct iris_core *core) iris_unset_icc_bw(core); if (!iris_vpu_watchdog(core, core->intr_status)) - disable_irq_nosync(core->irq); + disable_irq(core->irq); } int iris_vpu_power_on_controller(struct iris_core *core) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index b1657443f23f6d..d07845f37c559d 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -85,7 +85,7 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data) type++; } - return sizeof(*mode); + return mode->num_entries * sizeof(u32) + sizeof(*mode); } static void fill_profile_level(struct hfi_plat_caps *cap, const void *data, @@ -146,7 +146,7 @@ parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data) for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, fill_caps, caps_arr, num_caps); - return sizeof(*caps); + return num_caps * sizeof(*cap) + sizeof(u32); } static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts, @@ -171,7 +171,7 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) u32 entries = fmt->format_entries; unsigned int i = 0; u32 num_planes = 0; - u32 size; + u32 size = 2 * sizeof(u32); while (entries) { num_planes = pinfo->num_planes; @@ -186,6 +186,7 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) if (pinfo->num_planes > MAX_PLANES) break; + size += sizeof(*constr) * num_planes + 2 * sizeof(u32); pinfo = (void *)pinfo + sizeof(*constr) * num_planes + 2 * sizeof(u32); entries--; @@ -193,8 +194,6 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, fill_raw_fmts, rawfmts, i); - size = fmt->format_entries * (sizeof(*constr) * num_planes + 2 * sizeof(u32)) - + 2 * sizeof(u32); return size; } diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 5185a547461d76..91eda503424836 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -851,6 +851,11 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, v4l2_fill_pixfmt(pix, pix->pixelformat, pix->width, pix->height); + if (info->has_stride) { + pix->bytesperline = ALIGN(pix->bytesperline, RZG2L_CRU_STRIDE_ALIGN); + pix->sizeimage = pix->bytesperline * pix->height; + } + dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n", pix->width, pix->height, pix->bytesperline, pix->sizeimage); } diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.c b/drivers/media/platform/rockchip/rga/rga3-hw.c index ca1c268303dd40..c4a71306278ba1 100644 --- a/drivers/media/platform/rockchip/rga/rga3-hw.c +++ b/drivers/media/platform/rockchip/rga/rga3-hw.c @@ -450,10 +450,14 @@ static void *rga3_adjust_and_map_format(struct rga_ctx *ctx, other_format = is_output ? &ctx->out.pix : &ctx->in.pix; other_format_info = v4l2_format_info(other_format->pixelformat); - if ((v4l2_is_format_rgb(format_info) && - v4l2_is_format_yuv(other_format_info)) || - (v4l2_is_format_yuv(format_info) && - v4l2_is_format_rgb(other_format_info))) { + /* + * Only apply the quantization restrictions when we need to + * convert between RGB and YUV. Otherwise there is no point + * to limit the quantization for operations like scaling or + * rotations. + */ + if (v4l2_is_format_yuv(format_info) && + v4l2_is_format_rgb(other_format_info)) { /* * The RGA3 only supports BT601, BT709 and BT2020 RGB<->YUV conversions * Additionally BT709 and BT2020 only support limited range YUV. diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c index f89602075121b4..9c4a6093af324c 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c @@ -209,6 +209,9 @@ static void st_ref_pic_set_prediction(struct rkvdec_hevc_run *run, int idx, int i, j; int dPoc; + if ((unsigned int)rps_data->delta_idx_minus1 + 1 > idx) + return; + ref_rps_idx = st_rps_idx - (rps_data->delta_idx_minus1 + 1); /* 7-59 */ delta_rps = (1 - 2 * rps_data->delta_rps_sign) * (rps_data->abs_delta_rps_minus1 + 1); /* 7-60 */ diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c index 1d1e9bfef8e96e..061281f903f3c7 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c @@ -278,12 +278,12 @@ static const struct rkvdec_ctrl_desc vdpu38x_hevc_ctrl_descs[] = { { .cfg.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS, .cfg.ops = &rkvdec_ctrl_ops, - .cfg.dims = { 65 }, + .cfg.dims = { 64 }, }, { .cfg.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS, .cfg.ops = &rkvdec_ctrl_ops, - .cfg.dims = { 65 }, + .cfg.dims = { 32 }, }, }; @@ -1818,8 +1818,8 @@ static int rkvdec_probe(struct platform_device *pdev) vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -ENXIO; + if (irq < 0) + return irq; ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, rkvdec_irq_handler, IRQF_ONESHOT, diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c index e56a95f53ea958..ccb688f4d7afd3 100644 --- a/drivers/media/platform/ti/vpe/vip.c +++ b/drivers/media/platform/ti/vpe/vip.c @@ -815,6 +815,22 @@ static void clear_irqs(struct vip_dev *dev, int irq_num, int list_num) vpdma_clear_list_stat(dev->shared->vpdma, irq_num, dev->slice_id); } +/* + * Quiesce recovery work and per-list IRQs before releasing stream resources. + * disable_work_sync() prevents the overflow handler from requeueing recovery + * work. Mask and synchronize IRQs afterwards because a running worker may + * have re-enabled them before exiting. + */ +static void vip_quiesce_stream(struct vip_stream *stream) +{ + struct vip_dev *dev = stream->port->dev; + + disable_work_sync(&stream->recovery_work); + disable_irqs(dev, dev->slice_id, stream->list_num); + clear_irqs(dev, dev->slice_id, stream->list_num); + synchronize_irq(dev->irq); +} + static void populate_desc_list(struct vip_stream *stream) { struct vip_port *port = stream->port; @@ -2429,6 +2445,7 @@ static int vip_start_streaming(struct vb2_queue *vq, unsigned int count) goto err; stream->num_recovery = 0; + enable_work(&stream->recovery_work); clear_irqs(dev, dev->slice_id, stream->list_num); enable_irqs(dev, dev->slice_id, stream->list_num); @@ -2453,13 +2470,17 @@ static void vip_stop_streaming(struct vb2_queue *vq) struct vip_dev *dev = port->dev; int ret; + /* + * A running recovery worker may re-enable the parser, so quiesce it + * and its IRQ handler before stopping the parser or releasing the + * descriptor list. + */ + vip_quiesce_stream(stream); + vip_parser_stop_imm(port, true); vip_enable_parser(port, false); unset_fmt_params(stream); - disable_irqs(dev, dev->slice_id, stream->list_num); - clear_irqs(dev, dev->slice_id, stream->list_num); - if (port->subdev) { ret = v4l2_subdev_call(port->subdev, video, s_stream, 0); if (ret) @@ -3075,6 +3096,8 @@ static int alloc_stream(struct vip_port *port, int stream_id, int vfl_type) goto do_free_hwlist; INIT_WORK(&stream->recovery_work, vip_overflow_recovery_work); + /* Start disabled; vip_start_streaming() enables it before IRQs. */ + disable_work(&stream->recovery_work); INIT_LIST_HEAD(&stream->vidq); @@ -3140,6 +3163,13 @@ static void free_stream(struct vip_stream *stream) return; dev = stream->port->dev; + /* + * Quiesce the IRQ handler and recovery worker, then drop the stream + * from cap_streams[], before releasing stream-owned resources. + */ + vip_quiesce_stream(stream); + stream->port->cap_streams[stream->stream_id] = NULL; + /* Free up the Drop queue */ list_for_each_safe(pos, q, &stream->dropq) { buf = list_entry(pos, @@ -3151,7 +3181,6 @@ static void free_stream(struct vip_stream *stream) video_unregister_device(stream->vfd); vpdma_hwlist_release(dev->shared->vpdma, stream->list_num); - stream->port->cap_streams[stream->stream_id] = NULL; kfree(stream); } diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index cb4c56bf0752a8..28e840a7e5b8c3 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -344,22 +344,25 @@ static int sunxi_ir_probe(struct platform_device *pdev) ir->irq = platform_get_irq(pdev, 0); if (ir->irq < 0) { ret = ir->irq; - goto exit_free_dev; + goto exit_unregister_dev; } ret = devm_request_irq(dev, ir->irq, sunxi_ir_irq, 0, SUNXI_IR_DEV, ir); if (ret) { dev_err(dev, "failed request irq\n"); - goto exit_free_dev; + goto exit_unregister_dev; } ret = sunxi_ir_hw_init(dev); if (ret) - goto exit_free_dev; + goto exit_unregister_dev; dev_info(dev, "initialized sunXi IR driver\n"); return 0; +exit_unregister_dev: + rc_unregister_device(ir->rc); + exit_free_dev: rc_free_device(ir->rc); diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index 318e8330f16aef..ff9d50fb05fd13 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -63,11 +63,11 @@ struct pixfmt_info { }; static const struct v4l2_fwht_pixfmt_info pixfmt_fwht = { - V4L2_PIX_FMT_FWHT, 0, 3, 1, 1, 1, 1, 1, 0, 1 + V4L2_PIX_FMT_FWHT, 0, 4, 1, 1, 1, 1, 1, 0, 1 }; static const struct v4l2_fwht_pixfmt_info pixfmt_stateless_fwht = { - V4L2_PIX_FMT_FWHT_STATELESS, 0, 3, 1, 1, 1, 1, 1, 0, 1 + V4L2_PIX_FMT_FWHT_STATELESS, 0, 4, 1, 1, 1, 1, 1, 0, 1 }; static void vicodec_dev_release(struct device *dev) diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c index e0c6bc9f8f20ca..ac60c9b5418e9f 100644 --- a/drivers/media/test-drivers/vimc/vimc-capture.c +++ b/drivers/media/test-drivers/vimc/vimc-capture.c @@ -175,8 +175,8 @@ static int vimc_capture_enum_framesizes(struct file *file, void *priv, if (fsize->index) return -EINVAL; - /* Only accept code in the pix map table */ - vpix = vimc_pix_map_by_code(fsize->pixel_format); + /* Only accept pixel_format in the pix map table */ + vpix = vimc_pix_map_by_pixelformat(fsize->pixel_format); if (!vpix) return -EINVAL; diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c index 7bb945ba098983..bd7ed50fb6e856 100644 --- a/drivers/media/tuners/tda18250.c +++ b/drivers/media/tuners/tda18250.c @@ -440,8 +440,8 @@ static int tda18250_pll_calc(struct dvb_frontend *fe, u8 *rdiv, goto err; exp = (uval & 0x70) >> 4; - if (exp > 5) - exp = 0; + if (exp == 0 || exp > 5) + exp = 1; lopd = 1 << (exp - 1); scale = uval & 0x0f; fvco = lopd * scale * ((c->frequency / 1000) + dev->if_frequency); diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 57edb42463e810..358a66ab8e48a6 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -464,14 +464,21 @@ static void airspy_disconnect(struct usb_interface *intf) dev_dbg(s->dev, "\n"); - mutex_lock(&s->vb_queue_lock); + /* + * vb2_video_unregister_device() releases the vb2 queue, which + * triggers airspy_stop_streaming() if streaming is active. + * stop_streaming() dereferences s->udev via airspy_ctrl_msg() and + * airspy_free_stream_bufs(), so it must run before s->udev is + * cleared. vb2_video_unregister_device() locks vb_queue_lock + * internally and stop_streaming() locks v4l2_lock, so neither may + * be held by the caller. + */ + v4l2_device_disconnect(&s->v4l2_dev); + vb2_video_unregister_device(&s->vdev); + mutex_lock(&s->v4l2_lock); - /* No need to keep the urbs around after disconnection */ s->udev = NULL; - v4l2_device_disconnect(&s->v4l2_dev); - video_unregister_device(&s->vdev); mutex_unlock(&s->v4l2_lock); - mutex_unlock(&s->vb_queue_lock); v4l2_device_put(&s->v4l2_dev); } diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 2cd4e333bc4b01..70aa99fead2780 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -898,7 +898,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, if (rc) return rc; - if (vb2_is_busy(&dev->vidq)) { + if (vb2_is_busy(&dev->vidq) || vb2_is_busy(&dev->vbiq)) { dev_err(dev->dev, "%s: queue busy\n", __func__); return -EBUSY; } @@ -933,7 +933,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) if (dev->norm == norm) return 0; - if (vb2_is_busy(&dev->vidq)) + if (vb2_is_busy(&dev->vidq) || vb2_is_busy(&dev->vbiq)) return -EBUSY; dev->norm = norm; diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index fbfb74eab47593..a6881f7f8e1be0 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3651,6 +3651,7 @@ static void request_module_async(struct work_struct *work) * intf. Don't register extensions twice on those devices. */ if (dev->is_audio_only) { + em28xx_init_extension(dev); #if defined(CONFIG_MODULES) && defined(MODULE) request_module("em28xx-alsa"); #endif @@ -3884,8 +3885,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, retval = -ENODEV; goto err_deinit_media; } - em28xx_init_extension(dev); - return 0; } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 5bbb082dbed9ba..3d6b196b848305 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1265,6 +1265,8 @@ void em28xx_close_extension(struct em28xx *dev) ops->fini(dev); } } + if (dev->dev_next) + list_del(&dev->dev_next->devlist); list_del(&dev->devlist); mutex_unlock(&em28xx_devlist_mutex); } diff --git a/drivers/media/usb/go7007/snd-go7007.c b/drivers/media/usb/go7007/snd-go7007.c index e4b9f37be77b47..01d547ad678d46 100644 --- a/drivers/media/usb/go7007/snd-go7007.c +++ b/drivers/media/usb/go7007/snd-go7007.c @@ -195,6 +195,13 @@ static const struct snd_device_ops go7007_snd_device_ops = { .dev_free = go7007_snd_free, }; +static void go7007_snd_card_free(struct snd_card *card) +{ + struct go7007 *go = card->private_data; + + v4l2_device_put(&go->v4l2_dev); +} + int go7007_snd_init(struct go7007 *go) { static int dev; @@ -245,6 +252,8 @@ int go7007_snd_init(struct go7007 *go) gosnd->substream = NULL; go->snd_context = gosnd; v4l2_device_get(&go->v4l2_dev); + gosnd->card->private_data = go; + gosnd->card->private_free = go7007_snd_card_free; ++dev; return 0; @@ -263,7 +272,6 @@ int go7007_snd_remove(struct go7007 *go) snd_card_disconnect(gosnd->card); snd_card_free_when_closed(gosnd->card); - v4l2_device_put(&go->v4l2_dev); return 0; } EXPORT_SYMBOL(go7007_snd_remove); diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 0b8182edf8e4bb..58658dd3e457a3 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -617,6 +617,12 @@ static void s2255_fillbuff(struct s2255_vc *vc, break; case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_MJPEG: + if (jpgsize < 0 || + jpgsize > vb2_plane_size(&buf->vb.vb2_buf, 0)) { + dprintk(dev, 1, "bad JPEG frame size %d\n", + jpgsize); + break; + } vb2_set_plane_payload(&buf->vb.vb2_buf, 0, jpgsize); memcpy(vbuf, tmpbuf, jpgsize); break; @@ -2277,6 +2283,11 @@ static int s2255_probe(struct usb_interface *interface, } /* check the firmware is valid */ fw_size = dev->fw_data->fw->size; + if (fw_size < 8) { + dev_err(&interface->dev, "Firmware invalid: too small.\n"); + retval = -ENODEV; + goto errorFWMARKER; + } pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8]; if (*pdata != S2255_FW_MARKER) { diff --git a/drivers/media/usb/usbtv/usbtv-audio.c b/drivers/media/usb/usbtv/usbtv-audio.c index 333bd305a4f9fa..ae0a14e5ed2aba 100644 --- a/drivers/media/usb/usbtv/usbtv-audio.c +++ b/drivers/media/usb/usbtv/usbtv-audio.c @@ -317,6 +317,13 @@ static const struct snd_pcm_ops snd_usbtv_pcm_ops = { .pointer = snd_usbtv_pointer, }; +static void usbtv_audio_card_free(struct snd_card *card) +{ + struct usbtv *usbtv = card->private_data; + + v4l2_device_put(&usbtv->v4l2_dev); +} + int usbtv_audio_init(struct usbtv *usbtv) { int rv; @@ -331,6 +338,10 @@ int usbtv_audio_init(struct usbtv *usbtv) if (rv < 0) return rv; + v4l2_device_get(&usbtv->v4l2_dev); + card->private_data = usbtv; + card->private_free = usbtv_audio_card_free; + strscpy(card->driver, usbtv->dev->driver->name, sizeof(card->driver)); strscpy(card->shortname, "usbtv", sizeof(card->shortname)); snprintf(card->longname, sizeof(card->longname), diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 888a2e213b0885..0aa4265a67829a 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -392,7 +392,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, err_call_unbind: v4l2_async_nf_call_unbind(notifier, sd, asc); - list_del(&asc->asc_subdev_entry); err_unregister_subdev: if (registered) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index ba047d7d86010b..5b8a594fb9e24e 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -16,6 +16,9 @@ static const union v4l2_ctrl_ptr ptr_null; +#define V4L2_HEVC_MAX_SHORT_TERM_REF_PIC_SETS 64 +#define V4L2_HEVC_MAX_LONG_TERM_REF_PICS_SPS 32 + static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes) { @@ -1214,6 +1217,10 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_HEVC_SPS: p_hevc_sps = p; + if (p_hevc_sps->num_short_term_ref_pic_sets > + V4L2_HEVC_MAX_SHORT_TERM_REF_PIC_SETS) + return -EINVAL; + if (!(p_hevc_sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED)) { p_hevc_sps->pcm_sample_bit_depth_luma_minus1 = 0; p_hevc_sps->pcm_sample_bit_depth_chroma_minus1 = 0; @@ -1224,6 +1231,9 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, if (!(p_hevc_sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT)) p_hevc_sps->num_long_term_ref_pics_sps = 0; + else if (p_hevc_sps->num_long_term_ref_pics_sps > + V4L2_HEVC_MAX_LONG_TERM_REF_PICS_SPS) + return -EINVAL; break; case V4L2_CTRL_TYPE_HEVC_PPS: @@ -1280,6 +1290,11 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, if (p_hevc_st_rps->flags & ~V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_INTER_REF_PIC_SET_PRED) return -EINVAL; + if (p_hevc_st_rps->num_negative_pics > 16 || + p_hevc_st_rps->num_positive_pics > 16 || + p_hevc_st_rps->num_negative_pics + + p_hevc_st_rps->num_positive_pics > 16) + return -EINVAL; break; case V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS: @@ -1307,24 +1322,41 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, return -EINVAL; } - if (p_hdr10_mastering->white_point_x < - V4L2_HDR10_MASTERING_WHITE_POINT_X_LOW || - p_hdr10_mastering->white_point_x > - V4L2_HDR10_MASTERING_WHITE_POINT_X_HIGH || - p_hdr10_mastering->white_point_y < - V4L2_HDR10_MASTERING_WHITE_POINT_Y_LOW || - p_hdr10_mastering->white_point_y > - V4L2_HDR10_MASTERING_WHITE_POINT_Y_HIGH) - return -EINVAL; - - if (p_hdr10_mastering->max_display_mastering_luminance < - V4L2_HDR10_MASTERING_MAX_LUMA_LOW || - p_hdr10_mastering->max_display_mastering_luminance > - V4L2_HDR10_MASTERING_MAX_LUMA_HIGH || - p_hdr10_mastering->min_display_mastering_luminance < - V4L2_HDR10_MASTERING_MIN_LUMA_LOW || - p_hdr10_mastering->min_display_mastering_luminance > - V4L2_HDR10_MASTERING_MIN_LUMA_HIGH) + /* + * SMPTE ST 2086 Annex A documents that CTA 861-G uses + * (0, 0) to indicate that the white point chromaticity + * is unknown. + */ + if (p_hdr10_mastering->white_point_x || + p_hdr10_mastering->white_point_y) { + if (p_hdr10_mastering->white_point_x < + V4L2_HDR10_MASTERING_WHITE_POINT_X_LOW || + p_hdr10_mastering->white_point_x > + V4L2_HDR10_MASTERING_WHITE_POINT_X_HIGH || + p_hdr10_mastering->white_point_y < + V4L2_HDR10_MASTERING_WHITE_POINT_Y_LOW || + p_hdr10_mastering->white_point_y > + V4L2_HDR10_MASTERING_WHITE_POINT_Y_HIGH) + return -EINVAL; + } + + /* + * SMPTE ST 2086 Annex A documents that CTA 861-G uses zero + * maximum and minimum luminance values to indicate that + * the corresponding values are unknown. + */ + if (p_hdr10_mastering->max_display_mastering_luminance && + (p_hdr10_mastering->max_display_mastering_luminance < + V4L2_HDR10_MASTERING_MAX_LUMA_LOW || + p_hdr10_mastering->max_display_mastering_luminance > + V4L2_HDR10_MASTERING_MAX_LUMA_HIGH)) + return -EINVAL; + + if (p_hdr10_mastering->min_display_mastering_luminance && + (p_hdr10_mastering->min_display_mastering_luminance < + V4L2_HDR10_MASTERING_MIN_LUMA_LOW || + p_hdr10_mastering->min_display_mastering_luminance > + V4L2_HDR10_MASTERING_MIN_LUMA_HIGH)) return -EINVAL; /* The following restriction comes from ITU-T Rec. H.265 spec */ diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 5516b2bbb08f09..d750bf10febec1 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -1071,25 +1071,25 @@ int __video_register_device(struct video_device *vdev, vdev->dev.class = &video_class; vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); vdev->dev.parent = vdev->dev_parent; - vdev->dev.release = v4l2_device_release; dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); - - /* Increase v4l2_device refcount */ - v4l2_device_get(vdev->v4l2_dev); - mutex_lock(&videodev_lock); ret = device_register(&vdev->dev); if (ret < 0) { mutex_unlock(&videodev_lock); pr_err("%s: device_register failed\n", __func__); - put_device(&vdev->dev); - return ret; + goto cleanup; } + /* Register the release callback that will be called when the last + reference to the device goes away. */ + vdev->dev.release = v4l2_device_release; if (nr != -1 && nr != vdev->num && warn_if_nr_in_use) pr_warn("%s: requested %s%d, got %s\n", __func__, name_base, nr, video_device_node_name(vdev)); + /* Increase v4l2_device refcount */ + v4l2_device_get(vdev->v4l2_dev); + /* Part 5: Register the entity. */ ret = video_register_media_controller(vdev); diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 62a3a452f7884d..570df3c415b0a4 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -633,6 +633,8 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, if (!link->remote_node) goto err_put_remote_endpoint; + fwnode_handle_put(fwnode); + return 0; err_put_remote_endpoint: diff --git a/drivers/mfd/cgbc-core.c b/drivers/mfd/cgbc-core.c index 10bb4b414c34d2..2becaf797646da 100644 --- a/drivers/mfd/cgbc-core.c +++ b/drivers/mfd/cgbc-core.c @@ -364,9 +364,9 @@ static void cgbc_remove(struct platform_device *pdev) { struct cgbc_device_data *cgbc = platform_get_drvdata(pdev); - cgbc_session_release(cgbc); - mfd_remove_devices(&pdev->dev); + + cgbc_session_release(cgbc); } static struct platform_driver cgbc_driver = { diff --git a/drivers/mfd/qnap-mcu.c b/drivers/mfd/qnap-mcu.c index 8de974ddac3e72..93a3dd93404dd9 100644 --- a/drivers/mfd/qnap-mcu.c +++ b/drivers/mfd/qnap-mcu.c @@ -56,6 +56,7 @@ struct qnap_mcu_reply { * @reply: Reply data structure * @variant: Device variant specific information * @version: MCU firmware version + * @rx: Receive buffer the reply is assembled in */ struct qnap_mcu { struct serdev_device *serdev; @@ -63,6 +64,7 @@ struct qnap_mcu { struct qnap_mcu_reply reply; const struct qnap_mcu_variant *variant; u8 version[QNAP_MCU_VERSION_LEN]; + u8 rx[QNAP_MCU_RX_BUFFER_SIZE]; }; /* @@ -214,19 +216,18 @@ int qnap_mcu_exec(struct qnap_mcu *mcu, const u8 *cmd_data, size_t cmd_data_size, u8 *reply_data, size_t reply_data_size) { - unsigned char rx[QNAP_MCU_RX_BUFFER_SIZE]; size_t length = reply_data_size + QNAP_MCU_CHECKSUM_SIZE; struct qnap_mcu_reply *reply = &mcu->reply; int ret = 0; - if (length > sizeof(rx)) { + if (length > sizeof(mcu->rx)) { dev_err(&mcu->serdev->dev, "expected data too big for receive buffer"); return -EINVAL; } guard(mutex)(&mcu->bus_lock); - reply->data = rx; + reply->data = mcu->rx; reply->length = length; reply->received = 0; reinit_completion(&reply->done); @@ -242,15 +243,15 @@ int qnap_mcu_exec(struct qnap_mcu *mcu, return -ETIMEDOUT; } - if (!qnap_mcu_verify_checksum(rx, reply->received)) { + if (!qnap_mcu_verify_checksum(mcu->rx, reply->received)) { dev_err(&mcu->serdev->dev, "Invalid Checksum received from controller\n"); return -EPROTO; } - if (qnap_mcu_reply_is_any_error(mcu, rx, reply->received)) + if (qnap_mcu_reply_is_any_error(mcu, mcu->rx, reply->received)) return -EPROTO; - memcpy(reply_data, rx, reply_data_size); + memcpy(reply_data, mcu->rx, reply_data_size); return 0; } diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index b5bda477ebfc26..77888965d91ed8 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -1627,6 +1627,7 @@ static void sm501_pci_remove(struct pci_dev *dev) release_mem_region(sm->io_res->start, 0x100); pci_disable_device(dev); + kfree(sm); } static void sm501_plat_remove(struct platform_device *dev) @@ -1637,6 +1638,7 @@ static void sm501_plat_remove(struct platform_device *dev) iounmap(sm->regs); release_mem_region(sm->io_res->start, 0x100); + kfree(sm); } static const struct pci_device_id sm501_pci_tbl[] = { diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index eb6c2a78d3c7b7..d7a9e12f557586 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1712,24 +1712,20 @@ static int fastrpc_dmabuf_alloc(struct fastrpc_user *fl, char __user *argp) return err; } - bp.fd = dma_buf_fd(buf->dmabuf, O_ACCMODE); + bp.fd = get_unused_fd_flags(O_ACCMODE); if (bp.fd < 0) { dma_buf_put(buf->dmabuf); - return -EINVAL; + return bp.fd; } if (copy_to_user(argp, &bp, sizeof(bp))) { - /* - * The usercopy failed, but we can't do much about it, as - * dma_buf_fd() already called fd_install() and made the - * file descriptor accessible for the current process. It - * might already be closed and dmabuf no longer valid when - * we reach this point. Therefore "leak" the fd and rely on - * the process exit path to do any required cleanup. - */ + put_unused_fd(bp.fd); + dma_buf_put(buf->dmabuf); return -EFAULT; } + dma_buf_fd_install(buf->dmabuf, bp.fd); + return 0; } diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c index 3960506eb7ab3d..c3b474450d159a 100644 --- a/drivers/misc/nsm.c +++ b/drivers/misc/nsm.c @@ -243,7 +243,7 @@ static int nsm_sendrecv_msg_locked(struct nsm *nsm) goto cleanup; } - msg->resp.len = len; + msg->resp.len = min_t(unsigned int, len, sizeof(msg->resp.data)); rc = 0; diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 8c049f8355cdf9..4910d0f444bab7 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -1153,10 +1153,16 @@ static int via_sd_probe(struct pci_dev *pcidev, ret = mmc_add_host(mmc); if (ret) - goto unmap; + goto free_irq; return 0; +free_irq: + writeb(0x0, sdhost->pcictrl_mmiobase + VIA_CRDR_PCIINTCTRL); + free_irq(pcidev->irq, sdhost); + cancel_work_sync(&sdhost->carddet_work); + /* carddet_work may re-enable the interrupt via via_reset_pcictrl(). */ + writeb(0x0, sdhost->pcictrl_mmiobase + VIA_CRDR_PCIINTCTRL); unmap: iounmap(sdhost->mmiobase); release: @@ -1199,6 +1205,10 @@ static void via_sd_remove(struct pci_dev *pcidev) free_irq(pcidev->irq, sdhost); + cancel_work_sync(&sdhost->carddet_work); + /* carddet_work may re-enable the interrupt via via_reset_pcictrl(). */ + writeb(0x0, sdhost->pcictrl_mmiobase + VIA_CRDR_PCIINTCTRL); + timer_delete_sync(&sdhost->timer); cancel_work_sync(&sdhost->finish_bh_work); diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 39df7ce8f55f75..1da7ef06a7a974 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -392,6 +392,9 @@ static void mtdoops_notify_remove(struct mtd_info *mtd) cxt->mtd = NULL; flush_work(&cxt->work_erase); flush_work(&cxt->work_write); + vfree(cxt->oops_page_used); + cxt->oops_page_used = NULL; + cxt->oops_pages = 0; } diff --git a/drivers/mtd/nand/ecc-realtek.c b/drivers/mtd/nand/ecc-realtek.c index 7d003fd72027a1..76d75e2107df49 100644 --- a/drivers/mtd/nand/ecc-realtek.c +++ b/drivers/mtd/nand/ecc-realtek.c @@ -450,6 +450,7 @@ static const struct of_device_id rtl_ecc_of_ids[] = { }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, rtl_ecc_of_ids); static struct platform_driver rtl_ecc_driver = { .driver = { diff --git a/drivers/mtd/nand/raw/nand_onfi.c b/drivers/mtd/nand/raw/nand_onfi.c index cd3ad373883e9f..b5e304c3573821 100644 --- a/drivers/mtd/nand/raw/nand_onfi.c +++ b/drivers/mtd/nand/raw/nand_onfi.c @@ -35,16 +35,21 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip, struct nand_onfi_params *p) { struct nand_device *base = &chip->base; + struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_props requirements; struct onfi_ext_param_page *ep; struct onfi_ext_section *s; struct onfi_ext_ecc_info *ecc; + size_t remaining, section_len; uint8_t *cursor; int ret; int len; int i; len = le16_to_cpu(p->ext_param_page_length) * 16; + if (len < sizeof(*ep)) + return -EINVAL; + ep = kmalloc(len, GFP_KERNEL); if (!ep) return -ENOMEM; @@ -77,11 +82,29 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip, /* find the ECC section. */ cursor = (uint8_t *)(ep + 1); + remaining = len - sizeof(*ep); for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { s = ep->sections + i; - if (s->type == ONFI_SECTION_TYPE_2) + section_len = s->length * 16; + if (section_len > remaining) { + dev_dbg(&mtd->dev, + "ONFI extended parameter section %d exceeds page\n", + i); + goto ext_out; + } + + if (s->type == ONFI_SECTION_TYPE_2) { + if (section_len < sizeof(*ecc)) { + dev_dbg(&mtd->dev, + "ONFI extended parameter ECC section %d is too short\n", + i); + goto ext_out; + } break; - cursor += s->length * 16; + } + + cursor += section_len; + remaining -= section_len; } if (i == ONFI_EXT_SECTION_MAX) { pr_debug("We can not find the ECC section.\n"); diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c index 06f8f1e14b9c2c..7d43506b1654d5 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -914,7 +914,6 @@ static int pl35x_nand_init_hw_ecc_controller(struct pl35x_nandc *nfc, chip->ecc.steps = mtd->writesize / chip->ecc.size; chip->ecc.read_page = pl35x_nand_read_page_hwecc; chip->ecc.write_page = pl35x_nand_write_page_hwecc; - chip->ecc.write_page_raw = nand_monolithic_write_page_raw; pl35x_smc_set_ecc_pg_size(nfc, chip, mtd->writesize); nfc->ecc_buf = devm_kmalloc(nfc->dev, chip->ecc.bytes * chip->ecc.steps, @@ -981,7 +980,6 @@ static int pl35x_nand_attach_chip(struct nand_chip *chip) case NAND_ECC_ENGINE_TYPE_NONE: case NAND_ECC_ENGINE_TYPE_SOFT: dev_dbg(nfc->dev, "Using software ECC (Hamming 1-bit/512B)\n"); - chip->ecc.write_page_raw = nand_monolithic_write_page_raw; break; case NAND_ECC_ENGINE_TYPE_ON_HOST: dev_dbg(nfc->dev, "Using hardware ECC\n"); @@ -995,6 +993,9 @@ static int pl35x_nand_attach_chip(struct nand_chip *chip) return -EINVAL; } + chip->ecc.read_page_raw = nand_monolithic_read_page_raw; + chip->ecc.write_page_raw = nand_monolithic_write_page_raw; + return 0; } diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 02647565c8ba92..ca74c9c333e7d3 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -237,6 +237,18 @@ struct sunxi_nand_hw_ecc { u32 ecc_ctl; }; +#define SUNXI_NFC_TIMING_STEPS 4 + +/* Delay arrays contain internal NDFC clock cycles for field values 0 to 3. */ +struct sunxi_nfc_timings { + /* Internal clock cycles used by T1-T4, T7 and T11. */ + u8 setup_cycles; + s32 tWB[SUNXI_NFC_TIMING_STEPS]; + s32 tADL[SUNXI_NFC_TIMING_STEPS]; + s32 tWHR[SUNXI_NFC_TIMING_STEPS]; + s32 tRHW[SUNXI_NFC_TIMING_STEPS]; +}; + /** * struct sunxi_nand_chip - stores NAND chip device related information * @@ -301,6 +313,7 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) * bytes to write * @nuser_data_tab: Size of @user_data_len_tab * @sram_size: Size of the NAND controller SRAM + * @timings: Controller timing characteristics */ struct sunxi_nfc_caps { bool has_mdma; @@ -327,6 +340,7 @@ struct sunxi_nfc_caps { unsigned int nuser_data_tab; unsigned int max_ecc_steps; int sram_size; + const struct sunxi_nfc_timings *timings; }; /** @@ -1667,8 +1681,21 @@ static int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page) return nand_prog_page_end_op(nand); } -static const s32 tWB_lut[] = {6, 12, 16, 20}; -static const s32 tRHW_lut[] = {4, 8, 12, 20}; +static const struct sunxi_nfc_timings sun4i_a10_nfc_timings = { + .setup_cycles = 1, + .tWB = { 6, 12, 16, 20 }, + .tADL = { 7, 15, 23, 31 }, + .tWHR = { 7, 15, 23, 31 }, + .tRHW = { 4, 8, 12, 20 }, +}; + +static const struct sunxi_nfc_timings sun50i_h616_nfc_timings = { + .setup_cycles = 2, + .tWB = { 28, 44, 60, 76 }, + .tADL = { 0, 12, 28, 44 }, + .tWHR = { 0, 12, 28, 44 }, + .tRHW = { 8, 24, 40, 56 }, +}; static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, u32 clk_period) @@ -1693,6 +1720,7 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, { struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + const struct sunxi_nfc_timings *nfc_timings = nfc->caps->timings; const struct nand_sdr_timings *timings; u32 min_clk_period = 0; s32 tWB, tADL, tWHR, tRHW, tCAD; @@ -1703,20 +1731,28 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, return -ENOTSUPP; /* T1 <=> tCLS */ - if (timings->tCLS_min > min_clk_period) - min_clk_period = timings->tCLS_min; + if (timings->tCLS_min > + min_clk_period * nfc_timings->setup_cycles) + min_clk_period = DIV_ROUND_UP(timings->tCLS_min, + nfc_timings->setup_cycles); /* T2 <=> tCLH */ - if (timings->tCLH_min > min_clk_period) - min_clk_period = timings->tCLH_min; + if (timings->tCLH_min > + min_clk_period * nfc_timings->setup_cycles) + min_clk_period = DIV_ROUND_UP(timings->tCLH_min, + nfc_timings->setup_cycles); /* T3 <=> tCS */ - if (timings->tCS_min > min_clk_period) - min_clk_period = timings->tCS_min; + if (timings->tCS_min > + min_clk_period * nfc_timings->setup_cycles) + min_clk_period = DIV_ROUND_UP(timings->tCS_min, + nfc_timings->setup_cycles); /* T4 <=> tCH */ - if (timings->tCH_min > min_clk_period) - min_clk_period = timings->tCH_min; + if (timings->tCH_min > + min_clk_period * nfc_timings->setup_cycles) + min_clk_period = DIV_ROUND_UP(timings->tCH_min, + nfc_timings->setup_cycles); /* T5 <=> tWP */ if (timings->tWP_min > min_clk_period) @@ -1727,8 +1763,10 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, min_clk_period = timings->tWH_min; /* T7 <=> tALS */ - if (timings->tALS_min > min_clk_period) - min_clk_period = timings->tALS_min; + if (timings->tALS_min > + min_clk_period * nfc_timings->setup_cycles) + min_clk_period = DIV_ROUND_UP(timings->tALS_min, + nfc_timings->setup_cycles); /* T8 <=> tDS */ if (timings->tDS_min > min_clk_period) @@ -1743,8 +1781,10 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3); /* T11 <=> tALH */ - if (timings->tALH_min > min_clk_period) - min_clk_period = timings->tALH_min; + if (timings->tALH_min > + min_clk_period * nfc_timings->setup_cycles) + min_clk_period = DIV_ROUND_UP(timings->tALH_min, + nfc_timings->setup_cycles); /* T12 <=> tRP */ if (timings->tRP_min > min_clk_period) @@ -1763,17 +1803,25 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); /* T16 - T19 + tCAD */ - if (timings->tWB_max > (min_clk_period * 20)) - min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20); - - if (timings->tADL_min > (min_clk_period * 32)) - min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32); - - if (timings->tWHR_min > (min_clk_period * 32)) - min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32); - - if (timings->tRHW_min > (min_clk_period * 20)) - min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20); + if (timings->tWB_max > + (min_clk_period * nfc_timings->tWB[SUNXI_NFC_TIMING_STEPS - 1])) + min_clk_period = DIV_ROUND_UP(timings->tWB_max, + nfc_timings->tWB[SUNXI_NFC_TIMING_STEPS - 1]); + + if (timings->tADL_min > + (min_clk_period * nfc_timings->tADL[SUNXI_NFC_TIMING_STEPS - 1])) + min_clk_period = DIV_ROUND_UP(timings->tADL_min, + nfc_timings->tADL[SUNXI_NFC_TIMING_STEPS - 1]); + + if (timings->tWHR_min > + (min_clk_period * nfc_timings->tWHR[SUNXI_NFC_TIMING_STEPS - 1])) + min_clk_period = DIV_ROUND_UP(timings->tWHR_min, + nfc_timings->tWHR[SUNXI_NFC_TIMING_STEPS - 1]); + + if (timings->tRHW_min > + (min_clk_period * nfc_timings->tRHW[SUNXI_NFC_TIMING_STEPS - 1])) + min_clk_period = DIV_ROUND_UP(timings->tRHW_min, + nfc_timings->tRHW[SUNXI_NFC_TIMING_STEPS - 1]); /* * In non-EDO, tREA should be less than tRP to guarantee that the @@ -1789,26 +1837,28 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, if (timings->tREA_max > min_clk_period && !timings->tRLOH_min) min_clk_period = timings->tREA_max; - tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, + tWB = sunxi_nand_lookup_timing(nfc_timings->tWB, timings->tWB_max, min_clk_period); if (tWB < 0) { dev_err(nfc->dev, "unsupported tWB\n"); return tWB; } - tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3; - if (tADL > 3) { + tADL = sunxi_nand_lookup_timing(nfc_timings->tADL, + timings->tADL_min, min_clk_period); + if (tADL < 0) { dev_err(nfc->dev, "unsupported tADL\n"); - return -EINVAL; + return tADL; } - tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3; - if (tWHR > 3) { + tWHR = sunxi_nand_lookup_timing(nfc_timings->tWHR, + timings->tWHR_min, min_clk_period); + if (tWHR < 0) { dev_err(nfc->dev, "unsupported tWHR\n"); - return -EINVAL; + return tWHR; } - tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min, + tRHW = sunxi_nand_lookup_timing(nfc_timings->tRHW, timings->tRHW_min, min_clk_period); if (tRHW < 0) { dev_err(nfc->dev, "unsupported tRHW\n"); @@ -2595,6 +2645,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10), .max_ecc_steps = 16, .sram_size = 1024, + .timings = &sun4i_a10_nfc_timings, }; static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { @@ -2617,6 +2668,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10), .max_ecc_steps = 16, .sram_size = 1024, + .timings = &sun4i_a10_nfc_timings, }; static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = { @@ -2641,6 +2693,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = { .nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6), .max_ecc_steps = 32, .sram_size = 8192, + .timings = &sun50i_h616_nfc_timings, }; static const struct of_device_id sunxi_nfc_ids[] = { diff --git a/drivers/mtd/parsers/afs.c b/drivers/mtd/parsers/afs.c index 26116694c821b4..7ab3d50f565ecc 100644 --- a/drivers/mtd/parsers/afs.c +++ b/drivers/mtd/parsers/afs.c @@ -235,6 +235,9 @@ static int afs_parse_v2_partition(struct mtd_info *mtd, pr_debug("Parsing v2 partition @%08x-%08x\n", off, off + mtd->erasesize); + if (mtd->erasesize < sizeof(footer)) + return -EINVAL; + /* First read the footer */ ptr = off + mtd->erasesize - sizeof(footer); ret = mtd_read(mtd, ptr, sizeof(footer), &sz, (u_char *)footer); @@ -245,6 +248,8 @@ static int afs_parse_v2_partition(struct mtd_info *mtd, } name = (char *) &footer[0]; version = footer[9]; + if (footer[8] > mtd->erasesize - sizeof(footer)) + return -EINVAL; ptr = off + mtd->erasesize - sizeof(footer) - footer[8]; pr_debug("found image \"%s\", version %08x, info @%08x\n", @@ -278,6 +283,8 @@ static int afs_parse_v2_partition(struct mtd_info *mtd, entrypoint = imginfo[pad]; attributes = imginfo[pad+1]; region_count = imginfo[pad+2]; + if (region_count > (ARRAY_SIZE(imginfo) - pad - 3) / 4) + return -EINVAL; block_start = imginfo[20]; block_end = imginfo[21]; diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c index 9dd50b20c000a0..d2e92bab55d29d 100644 --- a/drivers/net/dsa/realtek/rtl83xx.c +++ b/drivers/net/dsa/realtek/rtl83xx.c @@ -321,7 +321,7 @@ void rtl83xx_reset_assert(struct realtek_priv *priv) "Failed to assert the switch reset control: %pe\n", ERR_PTR(ret)); - gpiod_set_value(priv->reset, true); + gpiod_set_value_cansleep(priv->reset, true); } void rtl83xx_reset_deassert(struct realtek_priv *priv) @@ -334,7 +334,7 @@ void rtl83xx_reset_deassert(struct realtek_priv *priv) "Failed to deassert the switch reset control: %pe\n", ERR_PTR(ret)); - gpiod_set_value(priv->reset, false); + gpiod_set_value_cansleep(priv->reset, false); } /** diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 208a894d6190c6..39eb6ab5f8058b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -13473,10 +13473,13 @@ static int bnx2x_init_firmware(struct bnx2x *bp) iro_alloc_err: kfree(bp->init_ops_offsets); + bp->init_ops_offsets = NULL; init_offsets_alloc_err: kfree(bp->init_ops); + bp->init_ops = NULL; init_ops_alloc_err: kfree(bp->init_data); + bp->init_data = NULL; request_firmware_exit: release_firmware(bp->firmware); bp->firmware = NULL; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index bc7b37cb74a717..54bae193d1f637 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -485,6 +485,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) struct bnxt_sw_tx_bd *tx_buf; __le32 lflags = 0; skb_frag_t *frag; + netdev_tx_t ret; i = skb_get_queue_mapping(skb); if (unlikely(i >= bp->tx_nr_rings)) { @@ -501,17 +502,19 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) if (skb_shinfo(skb)->nr_frags > TX_MAX_FRAGS) { netdev_warn_once(dev, "SKB has too many (%d) fragments, max supported is %d. SKB will be linearized.\n", skb_shinfo(skb)->nr_frags, TX_MAX_FRAGS); - if (skb_linearize(skb)) { - dev_kfree_skb_any(skb); - dev_core_stats_tx_dropped_inc(dev); - return NETDEV_TX_OK; - } + if (skb_linearize(skb)) + goto tx_free; } #endif if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) && - !(bp->flags & BNXT_FLAG_UDP_GSO_CAP)) - return bnxt_sw_udp_gso_xmit(bp, txr, txq, skb); + !(bp->flags & BNXT_FLAG_UDP_GSO_CAP)) { + ret = bnxt_sw_udp_gso_xmit(bp, txr, txq, skb); + if (txr->kick_pending) + bnxt_txr_db_kick(bp, txr, txr->tx_prod); + + return ret; + } free_size = bnxt_tx_avail(bp, txr); if (unlikely(free_size < skb_shinfo(skb)->nr_frags + 2)) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c index f317f60414e813..f7e18bea0fb84f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c @@ -223,9 +223,7 @@ netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp, netdev_tx_sent_queue(txq, skb->len); WRITE_ONCE(txr->tx_prod, prod); - /* Sync BDs before doorbell */ - wmb(); - bnxt_db_write(bp, &txr->tx_db, prod); + txr->kick_pending = 1; if (unlikely(bnxt_tx_avail(bp, txr) <= bp->tx_wake_thresh)) netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr), diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c index 74526002d52bdf..42027665f2a93f 100644 --- a/drivers/net/ethernet/ibm/emac/mal.c +++ b/drivers/net/ethernet/ibm/emac/mal.c @@ -35,6 +35,7 @@ int mal_register_commac(struct mal_instance *mal, struct mal_commac *commac) { unsigned long flags; + netdev_lock(mal->napi.dev); spin_lock_irqsave(&mal->lock, flags); MAL_DBG(mal, "reg(%08x, %08x)" NL, @@ -44,18 +45,20 @@ int mal_register_commac(struct mal_instance *mal, struct mal_commac *commac) if ((mal->tx_chan_mask & commac->tx_chan_mask) || (mal->rx_chan_mask & commac->rx_chan_mask)) { spin_unlock_irqrestore(&mal->lock, flags); + netdev_unlock(mal->napi.dev); printk(KERN_WARNING "mal%d: COMMAC channels conflict!\n", mal->index); return -EBUSY; } if (list_empty(&mal->list)) - napi_enable(&mal->napi); + napi_enable_locked(&mal->napi); mal->tx_chan_mask |= commac->tx_chan_mask; mal->rx_chan_mask |= commac->rx_chan_mask; list_add(&commac->list, &mal->list); spin_unlock_irqrestore(&mal->lock, flags); + netdev_unlock(mal->napi.dev); return 0; } @@ -64,7 +67,9 @@ void mal_unregister_commac(struct mal_instance *mal, struct mal_commac *commac) { unsigned long flags; + bool disable_napi; + netdev_lock(mal->napi.dev); spin_lock_irqsave(&mal->lock, flags); MAL_DBG(mal, "unreg(%08x, %08x)" NL, @@ -73,10 +78,12 @@ void mal_unregister_commac(struct mal_instance *mal, mal->tx_chan_mask &= ~commac->tx_chan_mask; mal->rx_chan_mask &= ~commac->rx_chan_mask; list_del_init(&commac->list); - if (list_empty(&mal->list)) - napi_disable(&mal->napi); + disable_napi = list_empty(&mal->list); spin_unlock_irqrestore(&mal->lock, flags); + if (disable_napi) + napi_disable_locked(&mal->napi); + netdev_unlock(mal->napi.dev); } int mal_set_rcbs(struct mal_instance *mal, int channel, unsigned long size) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 6fbc0441c4b845..6fc6605d20549b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -2263,6 +2263,11 @@ static void mlx5e_handle_rx_cqe_mpwrq_shampo(struct mlx5e_rq *rq, struct mlx5_cq data_offset = wqe_offset & (page_size - 1); page_idx = wqe_offset >> rq->mpwqe.page_shift; + if (unlikely(cqe_bcnt <= ETH_ZLEN + 2 * VLAN_HLEN)) { + match = false; + flush = true; + } + if (*skb && !(match && mlx5e_hw_gro_skb_has_enough_space(*skb, data_bcnt, page_size))) { diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index 58d3e55def486b..dc804e11156415 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -6221,7 +6221,7 @@ static int nv_suspend(struct device *device) netif_device_detach(dev); /* save non-pci configuration space */ - for (i = 0; i <= np->register_size/sizeof(u32); i++) + for (i = 0; i < np->register_size/sizeof(u32); i++) np->saved_config_space[i] = readl(base + i*sizeof(u32)); return 0; @@ -6236,7 +6236,7 @@ static int nv_resume(struct device *device) int i, rc = 0; /* restore non-pci configuration space */ - for (i = 0; i <= np->register_size/sizeof(u32); i++) + for (i = 0; i < np->register_size/sizeof(u32); i++) writel(np->saved_config_space[i], base+i*sizeof(u32)); if (np->driver_data & DEV_NEED_MSI_FIX) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 042a75f3406082..0e7a0c2c176517 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -303,10 +303,10 @@ enum qede_agg_state { }; struct qede_agg_info { - /* rx_buf is a data buffer that can be placed / consumed from rx bd - * chain. It has two purposes: We will preallocate the data buffer - * for each aggregation when we open the interface and will place this - * buffer on the rx-bd-ring when we receive TPA_START. We don't want + /* buffer is used to retain the Rx consumer descriptor when a TPA + * session starts. If the SKB allocation fails during TPA_START, + * we use this saved buffer to safely recycle the physical page + * back into the rx-bd-ring via qede_reuse_page(). We don't want * to be in a state where allocation fails, as we can't reuse the * consumer buffer in the rx-chain since FW may still be writing to it * (since header needs to be modified for TPA). diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index c11e0d8f98aa5c..a02624f6d6d59c 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -850,6 +850,7 @@ static void qede_tpa_start(struct qede_dev *edev, pad, false); tpa_info->buffer.page_offset = sw_rx_data_cons->page_offset; tpa_info->buffer.mapping = sw_rx_data_cons->mapping; + tpa_info->buffer.data = sw_rx_data_cons->data; if (unlikely(!tpa_info->skb)) { DP_NOTICE(edev, "Failed to allocate SKB for gro\n"); diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h index 5e56ec9b1013ab..3ee4c61081890c 100644 --- a/drivers/net/ethernet/renesas/ravb.h +++ b/drivers/net/ethernet/renesas/ravb.h @@ -1028,6 +1028,7 @@ struct ravb_ptp_perout { struct ravb_ptp { struct ptp_clock *clock; struct ptp_clock_info info; + int phc_index; u32 default_addend; u32 current_addend; int extts[N_EXT_TS]; @@ -1123,6 +1124,8 @@ struct ravb_private { int msg_enable; int speed; int emac_irq; + int err_irq; + int mgmt_irq; unsigned no_avb_link:1; unsigned avb_link_active_low:1; diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 5f88733094d0fc..ea1c7e536791e8 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -1779,7 +1779,7 @@ static int ravb_get_ts_info(struct net_device *ndev, (1 << HWTSTAMP_FILTER_NONE) | (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | (1 << HWTSTAMP_FILTER_ALL); - info->phc_index = ptp_clock_index(priv->ptp.clock); + info->phc_index = READ_ONCE(priv->ptp.phc_index); } return 0; @@ -2885,11 +2885,13 @@ static int ravb_setup_irqs(struct ravb_private *priv) return error; if (info->err_mgmt_irqs) { - error = ravb_setup_irq(priv, "err_a", "err_a", NULL, ravb_multi_interrupt); + error = ravb_setup_irq(priv, "err_a", "err_a", &priv->err_irq, + ravb_multi_interrupt); if (error) return error; - error = ravb_setup_irq(priv, "mgmt_a", "mgmt_a", NULL, ravb_multi_interrupt); + error = ravb_setup_irq(priv, "mgmt_a", "mgmt_a", &priv->mgmt_irq, + ravb_multi_interrupt); if (error) return error; } @@ -2953,6 +2955,7 @@ static int ravb_probe(struct platform_device *pdev) priv->rstc = rstc; priv->ndev = ndev; priv->pdev = pdev; + priv->ptp.phc_index = -1; priv->num_tx_ring[RAVB_BE] = BE_TX_RING_SIZE; priv->num_rx_ring[RAVB_BE] = BE_RX_RING_SIZE; if (info->nc_queues) { diff --git a/drivers/net/ethernet/renesas/ravb_ptp.c b/drivers/net/ethernet/renesas/ravb_ptp.c index 226c6c0ab945b6..43218bc15b1519 100644 --- a/drivers/net/ethernet/renesas/ravb_ptp.c +++ b/drivers/net/ethernet/renesas/ravb_ptp.c @@ -289,16 +289,17 @@ static const struct ptp_clock_info ravb_ptp_info = { void ravb_ptp_interrupt(struct net_device *ndev) { struct ravb_private *priv = netdev_priv(ndev); + struct ptp_clock *clock = READ_ONCE(priv->ptp.clock); u32 gis = ravb_read(ndev, GIS); gis &= ravb_read(ndev, GIC); - if (gis & GIS_PTCF) { + if ((gis & GIS_PTCF) && clock) { struct ptp_clock_event event; event.type = PTP_CLOCK_EXTTS; event.index = 0; event.timestamp = ravb_read(ndev, GCPT); - ptp_clock_event(priv->ptp.clock, &event); + ptp_clock_event(clock, &event); } if (gis & GIS_PTMF) { struct ravb_ptp_perout *perout = priv->ptp.perout; @@ -315,6 +316,7 @@ void ravb_ptp_interrupt(struct net_device *ndev) void ravb_ptp_init(struct net_device *ndev, struct platform_device *pdev) { struct ravb_private *priv = netdev_priv(ndev); + struct ptp_clock *clock; unsigned long flags; priv->ptp.info = ravb_ptp_info; @@ -327,15 +329,45 @@ void ravb_ptp_init(struct net_device *ndev, struct platform_device *pdev) ravb_modify(ndev, GCCR, GCCR_TCSS, GCCR_TCSS_ADJGPTP); spin_unlock_irqrestore(&priv->lock, flags); - priv->ptp.clock = ptp_clock_register(&priv->ptp.info, &pdev->dev); + clock = ptp_clock_register(&priv->ptp.info, &pdev->dev); + if (IS_ERR(clock)) { + netdev_err(ndev, "failed to register PTP clock: %pe\n", clock); + clock = NULL; + } + + WRITE_ONCE(priv->ptp.clock, clock); + if (clock) + WRITE_ONCE(priv->ptp.phc_index, ptp_clock_index(clock)); +} + +static void ravb_ptp_disable(struct net_device *ndev) +{ + ravb_write(ndev, 0, GIC); + ravb_write(ndev, 0, GIS); +} + +static void ravb_ptp_sync_irqs(struct net_device *ndev) +{ + struct ravb_private *priv = netdev_priv(ndev); + + synchronize_irq(ndev->irq); + if (priv->info->err_mgmt_irqs) { + synchronize_irq(priv->err_irq); + synchronize_irq(priv->mgmt_irq); + } } void ravb_ptp_stop(struct net_device *ndev) { struct ravb_private *priv = netdev_priv(ndev); + struct ptp_clock *clock; - ravb_write(ndev, 0, GIC); - ravb_write(ndev, 0, GIS); + WRITE_ONCE(priv->ptp.phc_index, -1); + clock = xchg(&priv->ptp.clock, NULL); + + ravb_ptp_disable(ndev); + ravb_ptp_sync_irqs(ndev); - ptp_clock_unregister(priv->ptp.clock); + if (clock) + ptp_clock_unregister(clock); } diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 9a12cc53da0021..2f6e77cce385fa 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -108,6 +109,7 @@ struct gtp_net { }; static u32 gtp_h_initval; +static DEFINE_MUTEX(gtp_pdp_lock); static struct genl_family gtp_genl_family; @@ -151,7 +153,8 @@ static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid, u16 family) head = >p->tid_hash[gtp0_hashfn(tid) % gtp->hash_size]; - hlist_for_each_entry_rcu(pdp, head, hlist_tid) { + hlist_for_each_entry_rcu(pdp, head, hlist_tid, + lockdep_is_held(>p_pdp_lock)) { if (pdp->af == family && pdp->gtp_version == GTP_V0 && pdp->u.v0.tid == tid) @@ -168,7 +171,8 @@ static struct pdp_ctx *gtp1_pdp_find(struct gtp_dev *gtp, u32 tid, u16 family) head = >p->tid_hash[gtp1u_hashfn(tid) % gtp->hash_size]; - hlist_for_each_entry_rcu(pdp, head, hlist_tid) { + hlist_for_each_entry_rcu(pdp, head, hlist_tid, + lockdep_is_held(>p_pdp_lock)) { if (pdp->af == family && pdp->gtp_version == GTP_V1 && pdp->u.v1.i_tei == tid) @@ -185,7 +189,8 @@ static struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr) head = >p->addr_hash[ipv4_hashfn(ms_addr) % gtp->hash_size]; - hlist_for_each_entry_rcu(pdp, head, hlist_addr) { + hlist_for_each_entry_rcu(pdp, head, hlist_addr, + lockdep_is_held(>p_pdp_lock)) { if (pdp->af == AF_INET && pdp->ms.addr.s_addr == ms_addr) return pdp; @@ -220,7 +225,8 @@ static struct pdp_ctx *ipv6_pdp_find(struct gtp_dev *gtp, head = >p->addr_hash[ipv6_hashfn(ms_addr) % gtp->hash_size]; - hlist_for_each_entry_rcu(pdp, head, hlist_addr) { + hlist_for_each_entry_rcu(pdp, head, hlist_addr, + lockdep_is_held(>p_pdp_lock)) { if (pdp->af == AF_INET6 && ipv6_pdp_addr_equal(&pdp->ms.addr6, ms_addr)) return pdp; @@ -1555,9 +1561,11 @@ static void gtp_dellink(struct net_device *dev, struct list_head *head) struct pdp_ctx *pctx; int i; + mutex_lock(>p_pdp_lock); for (i = 0; i < gtp->hash_size; i++) hlist_for_each_entry_safe(pctx, next, >p->tid_hash[i], hlist_tid) pdp_context_delete(pctx); + mutex_unlock(>p_pdp_lock); list_del(>p->list); unregister_netdevice_queue(dev, head); @@ -2053,6 +2061,7 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) goto out_unlock; } + mutex_lock(>p_pdp_lock); pctx = gtp_pdp_add(gtp, sk, info); if (IS_ERR(pctx)) { err = PTR_ERR(pctx); @@ -2060,6 +2069,7 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) gtp_tunnel_notify(pctx, GTP_CMD_NEWPDP, GFP_KERNEL); err = 0; } + mutex_unlock(>p_pdp_lock); out_unlock: rtnl_unlock(); @@ -2134,6 +2144,8 @@ static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[GTPA_VERSION]) return -EINVAL; + mutex_lock(>p_pdp_lock); + rcu_read_lock(); pctx = gtp_find_pdp(sock_net(skb->sk), info->attrs); @@ -2154,6 +2166,7 @@ static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) out_unlock: rcu_read_unlock(); + mutex_unlock(>p_pdp_lock); return err; } diff --git a/drivers/net/ipa/ipa_modem.c b/drivers/net/ipa/ipa_modem.c index 9b136f6b8b4ae8..d84c1dbd3b1a84 100644 --- a/drivers/net/ipa/ipa_modem.c +++ b/drivers/net/ipa/ipa_modem.c @@ -266,13 +266,29 @@ void ipa_modem_suspend(struct net_device *netdev) * the modem. We can't enable the queue directly in ipa_modem_resume() * because transmits restart the instant the queue is awakened; but the * device power state won't be ACTIVE until *after* ipa_modem_resume() - * returns. + * returns. A transmit restarted before that would stop the queue + * again and get -EINPROGRESS from pm_runtime_get(), and with this + * work having already run, nothing would ever wake the queue again. + * So wait for the resume to complete before waking the queue. */ static void ipa_modem_wake_queue_work(struct work_struct *work) { struct ipa_priv *priv = container_of(work, struct ipa_priv, work); + struct device *dev = priv->ipa->dev; + int ret; + + ret = pm_runtime_get_sync(dev); + /* Wake the queue even if the device could not be resumed, so + * that pending packets are dropped by the transmit path rather + * than stranded behind a stopped queue. + */ netif_wake_queue(priv->tx->netdev); + + if (ret < 0) + pm_runtime_put_noidle(dev); + else + (void)pm_runtime_put_autosuspend(dev); } /** ipa_modem_resume() - resume callback for runtime_pm diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index 029a4a532a104e..9c171697e76209 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -144,6 +144,9 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, goto enqueue_again; } + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += len; + new_skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN); if (!new_skb) { ndev->stats.rx_dropped++; @@ -155,13 +158,7 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, skb->ip_summed = CHECKSUM_NONE; skb_record_rx_queue(skb, q->qid); - if (netif_rx(skb) == NET_RX_DROP) { - ndev->stats.rx_errors++; - ndev->stats.rx_dropped++; - } else { - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += len; - } + netif_rx(skb); skb = new_skb; @@ -199,8 +196,10 @@ static int __ntb_netdev_maybe_stop_tx(struct net_device *netdev, static int ntb_netdev_maybe_stop_tx(struct net_device *ndev, struct ntb_netdev_queue *q, int size) { - if (__netif_subqueue_stopped(ndev, q->qid) || - (ntb_transport_tx_free_entry(q->qp) >= size)) + if (__netif_subqueue_stopped(ndev, q->qid)) + return -EBUSY; + + if (ntb_transport_tx_free_entry(q->qp) >= size) return 0; return __ntb_netdev_maybe_stop_tx(ndev, q, size); @@ -256,21 +255,30 @@ static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb, q = &dev->queues[qid]; - ntb_netdev_maybe_stop_tx(ndev, q, tx_stop); + if (unlikely(ntb_netdev_maybe_stop_tx(ndev, q, tx_stop))) + return NETDEV_TX_BUSY; rc = ntb_transport_tx_enqueue(q->qp, skb, skb->data, skb->len); - if (rc) - goto err; + if (rc) { + if (rc == -EAGAIN || rc == -EBUSY) { + netif_stop_subqueue(ndev, q->qid); + mod_timer(&q->tx_timer, + jiffies + usecs_to_jiffies(tx_time)); + return NETDEV_TX_BUSY; + } + + goto drop; + } /* check for next submit */ ntb_netdev_maybe_stop_tx(ndev, q, tx_stop); return NETDEV_TX_OK; -err: +drop: + dev_kfree_skb_any(skb); ndev->stats.tx_dropped++; - ndev->stats.tx_errors++; - return NETDEV_TX_BUSY; + return NETDEV_TX_OK; } static void ntb_netdev_tx_timer(struct timer_list *t) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 18d2ead97aa547..7397169236fdfe 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -965,7 +965,7 @@ static unsigned int phylink_inband_caps(struct phylink *pl, return 0; pcs = pl->mac_ops->mac_select_pcs(pl->config, interface); - if (!pcs) + if (IS_ERR_OR_NULL(pcs)) return 0; return phylink_pcs_inband_caps(pcs, interface); diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index faae711cf793d3..85b2438e892351 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -628,9 +628,15 @@ static void sl_uninit(struct net_device *dev) struct slip *sl = netdev_priv(dev); sl_free_bufs(sl); + /* Drop the slip_devs[] entry here rather than from the destructor: + * ndo_uninit runs under RTNL, so it cannot race sl_sync(). + */ + slip_devs[dev->base_addr] = NULL; } -/* Hook the destructor so we can free slip devices at the right point in time */ +/* Only for the slip_open() error path: register_netdevice() can fail before + * ndo_init, and then ndo_uninit is not called either. + */ static void sl_free_netdev(struct net_device *dev) { int i = dev->base_addr; @@ -657,7 +663,6 @@ static void sl_setup(struct net_device *dev) { dev->netdev_ops = &sl_netdev_ops; dev->needs_free_netdev = true; - dev->priv_destructor = sl_free_netdev; dev->hard_header_len = 0; dev->addr_len = 0; @@ -908,7 +913,7 @@ static void slip_close(struct tty_struct *tty) #endif /* Flush network side */ unregister_netdev(sl->dev); - /* This will complete via sl_free_netdev */ + /* sl_uninit() has dropped the slip_devs[] entry by now */ } static void slip_hangup(struct tty_struct *tty) diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index 98893732bc6e46..2a1728621887aa 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -626,6 +626,14 @@ static int tbnet_alloc_tx_buffers(struct tbnet *net) return 0; } +static void tbnet_connect_failed(struct tbnet *net) +{ + /* Leave login_received set: only the peer can make it true again. */ + mutex_lock(&net->connection_lock); + net->login_sent = false; + mutex_unlock(&net->connection_lock); +} + static void tbnet_connected_work(struct work_struct *work) { struct tbnet *net = container_of(work, typeof(*net), connected_work); @@ -647,6 +655,9 @@ static void tbnet_connected_work(struct work_struct *work) ret = tb_xdomain_alloc_in_hopid(net->xd, net->remote_transmit_path); if (ret != net->remote_transmit_path) { netdev_err(net->dev, "failed to allocate Rx HopID\n"); + if (ret >= 0) + tb_xdomain_release_in_hopid(net->xd, ret); + tbnet_connect_failed(net); return; } @@ -691,6 +702,7 @@ static void tbnet_connected_work(struct work_struct *work) tb_ring_stop(net->rx_ring.ring); tb_ring_stop(net->tx_ring.ring); tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path); + tbnet_connect_failed(net); } static void tbnet_login_work(struct work_struct *work) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index fed9dfdfcc3bc0..5bbe3123979e00 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1107,11 +1107,16 @@ static netdev_features_t tun_net_fix_features(struct net_device *dev, static void tun_set_headroom(struct net_device *dev, int new_hr) { struct tun_struct *tun = netdev_priv(dev); + size_t max_headroom; - if (new_hr < NET_SKB_PAD) - new_hr = NET_SKB_PAD; + max_headroom = min_t(size_t, SKB_MAX_HEAD(0), U16_MAX - 1); - tun->align = new_hr; + if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP) + max_headroom -= ETH_HLEN + NET_IP_ALIGN; + else + max_headroom -= 1; + + tun->align = clamp_t(int, new_hr, NET_SKB_PAD, max_headroom); } static void @@ -1822,7 +1827,13 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, switch (tun->flags & TUN_TYPE_MASK) { case IFF_TUN: if (tun->flags & IFF_NO_PI) { - u8 ip_version = skb->len ? (skb->data[0] >> 4) : 0; + u8 ip_version; + + if (!pskb_may_pull(skb, 1)) { + err = -EINVAL; + goto drop; + } + ip_version = skb->data[0] >> 4; switch (ip_version) { case 4: @@ -1842,7 +1853,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, skb->dev = tun->dev; break; case IFF_TAP: - if (frags && !pskb_may_pull(skb, ETH_HLEN)) { + if (!pskb_may_pull(skb, ETH_HLEN)) { err = -ENOMEM; drop_reason = SKB_DROP_REASON_HDR_TRUNC; goto drop; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 94cdb61dca83db..8178a8758cd3fc 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1360,6 +1360,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */ {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ {QMI_FIXED_INTF(0x2357, 0x9000, 4)}, /* TP-LINK MA260 */ + {QMI_QUIRK_SET_DTR(0x1bc7, 0x0991, 0)}, /* Telit FE990D50 */ {QMI_QUIRK_SET_DTR(0x1bc7, 0x1031, 3)}, /* Telit LE910C1-EUX */ {QMI_QUIRK_SET_DTR(0x1bc7, 0x1034, 2)}, /* Telit LE910C4-WWX */ {QMI_QUIRK_SET_DTR(0x1bc7, 0x1037, 4)}, /* Telit LE910C4-WWX */ diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 843210bdbaa3d8..2597c4ff3d96ab 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -3245,18 +3245,19 @@ vxlan_fdb_flush_match_remotes(struct vxlan_fdb *f, struct vxlan_dev *vxlan, const struct vxlan_fdb_flush_desc *desc, bool *p_destroy_fdb) { - bool remotes_flushed = false; struct vxlan_rdst *rd, *tmp; list_for_each_entry_safe(rd, tmp, &f->remotes, list) { if (!vxlan_fdb_flush_remote_matches(desc, rd)) continue; + if (list_is_singular(&f->remotes)) { + *p_destroy_fdb = true; + return; + } + vxlan_fdb_dst_destroy(vxlan, f, rd, true); - remotes_flushed = true; } - - *p_destroy_fdb = remotes_flushed && list_empty(&f->remotes); } /* Purge the forwarding table */ diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index cc0f2c45fc3a30..62f663c0daa265 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -754,6 +754,11 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len + assoc_resp_ie_offset; + if (assoc_req_len < assoc_req_ie_offset) + assoc_req_len = assoc_req_ie_offset; + if (assoc_resp_len < assoc_resp_ie_offset) + assoc_resp_len = assoc_resp_ie_offset; + assoc_req_len -= assoc_req_ie_offset; assoc_resp_len -= assoc_resp_ie_offset; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 9f7ed1d293a066..381801af3ac982 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -1827,17 +1827,18 @@ brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) if (bus->rxctl) { brcmf_err("last control frame is being processed.\n"); spin_unlock_bh(&bus->rxctl_lock); - vfree(buf); goto done; } bus->rxctl = buf + doff; bus->rxctl_orig = buf; bus->rxlen = len - doff; spin_unlock_bh(&bus->rxctl_lock); + buf = NULL; done: /* Awake any waiters */ brcmf_sdio_dcmd_resp_wake(bus); + vfree(buf); } /* Pad read to blocksize for efficiency */ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index ca5a8140908afb..6bd5b6d84b2a9b 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -1511,10 +1511,10 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, priv->workqueue = NULL; out_uninit_drv: iwl_uninit_drv(priv); -out_free_eeprom_blob: - kfree(priv->eeprom_blob); out_free_eeprom: kfree(priv->nvm_data); +out_free_eeprom_blob: + kfree(priv->eeprom_blob); out_leave_trans: iwl_trans_op_mode_leave(priv->trans); out_free_hw: diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index 2d56ead01b4c32..3719a1231a3725 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -57,6 +57,18 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n", status); mwifiex_cancel_all_pending_cmd(adapter); + + /* The command response path writes through cmd_node->data_buf. + * On an interrupted wait, the caller can return and release a + * stack-allocated data_buf before a late firmware response is + * processed. Detach the caller-owned buffer from the current + * command so a late response cannot corrupt freed stack memory. + */ + spin_lock_bh(&adapter->mwifiex_cmd_lock); + if (adapter->curr_cmd == cmd_queued) + adapter->curr_cmd->data_buf = NULL; + spin_unlock_bh(&adapter->mwifiex_cmd_lock); + return status; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index fc619acbb40d0b..67f56e428d9a15 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -1239,11 +1239,12 @@ static int mt7615_suspend(struct ieee80211_hw *hw, cancel_delayed_work_sync(&dev->pm.ps_work); mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); + cancel_delayed_work_sync(&phy->scan_work); + cancel_delayed_work_sync(&phy->mt76->mac_work); + mt7615_mutex_acquire(dev); clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); - cancel_delayed_work_sync(&phy->scan_work); - cancel_delayed_work_sync(&phy->mt76->mac_work); set_bit(MT76_STATE_SUSPEND, &phy->mt76->state); ieee80211_iterate_active_interfaces(hw, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index e8fe86f9330972..bbb2fedacb25af 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -2917,8 +2917,15 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset, u8 *read_buf) return ret; res = (struct mt7915_mcu_eeprom_info *)skb->data; - if (!buf) - buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr); + if (!buf) { + u32 addr = le32_to_cpu(res->addr); + + if (addr > dev->mt76.eeprom.size - MT7915_EEPROM_BLOCK_SIZE) { + dev_kfree_skb(skb); + return -EINVAL; + } + buf = dev->mt76.eeprom.data + addr; + } memcpy(buf, res->data, MT7915_EEPROM_BLOCK_SIZE); dev_kfree_skb(skb); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 6b0cd1996ecb38..9b58ffec36493d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1323,6 +1323,7 @@ void mt7925_mac_reset_work(struct work_struct *work) cancel_delayed_work_sync(&dev->mphy.mac_work); cancel_delayed_work_sync(&pm->ps_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); cancel_work_sync(&pm->wake_work); for (i = 0; i < 10; i++) { @@ -1434,6 +1435,10 @@ int mt7925_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, if (!wcid) wcid = &dev->mt76.global_wcid; + err = skb_cow_head(skb, MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE); + if (err) + return err; + if (sta) { struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 2b6cc8e253c0e9..3beb2c1fbec9c5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -1640,6 +1640,7 @@ static int mt7925_suspend(struct ieee80211_hw *hw, cancel_delayed_work_sync(&phy->mt76->mac_work); cancel_delayed_work_sync(&dev->pm.ps_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); mt792x_mutex_acquire(dev); @@ -2484,10 +2485,19 @@ static void mt7925_channel_switch_rx_beacon(struct ieee80211_hw *hw, } } +static void mt7925_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + cancel_delayed_work_sync(&dev->mlo_pm_work); + + mt792x_stop(hw, suspend); +} + const struct ieee80211_ops mt7925_ops = { .tx = mt792x_tx, .start = mt7925_start, - .stop = mt792x_stop, + .stop = mt7925_stop, .add_interface = mt7925_add_interface, .remove_interface = mt792x_remove_interface, .config = mt7925_config, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index ea64303283edbe..084baadfa18336 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -48,6 +48,7 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev) mt76_for_each_q_rx(&dev->mt76, i) napi_disable(&dev->mt76.napi[i]); cancel_delayed_work_sync(&pm->ps_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); cancel_work_sync(&pm->wake_work); cancel_work_sync(&dev->reset_work); @@ -517,6 +518,7 @@ static int mt7925_pci_suspend(struct device *device) dev->hif_resumed = false; flush_work(&dev->reset_work); cancel_delayed_work_sync(&pm->ps_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); cancel_work_sync(&pm->wake_work); mt7925_roc_abort_sync(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c index e9f58492bf7df8..a798164b9a0545 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c @@ -269,6 +269,7 @@ static int mt7925u_suspend(struct usb_interface *intf, pm_message_t state) pm->suspended = true; dev->hif_resumed = false; flush_work(&dev->reset_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); mt76_connac_mcu_set_hif_suspend(&dev->mt76, true, false); ret = wait_event_timeout(dev->wait, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c index ac05f7d75d63d6..ec33521db564bd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c @@ -150,6 +150,12 @@ mt7996_eeprom_check_or_use_default(struct mt7996_dev *dev, bool use_default) goto out; } + if (fw->size < MT7996_EEPROM_SIZE) { + dev_err(dev->mt76.dev, "Invalid default bin size\n"); + ret = -EINVAL; + goto out; + } + if (!use_default && mt7996_eeprom_variant_valid(dev, fw->data)) goto out; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 0eebc8182ca9aa..730fd8e2fa0577 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -1239,6 +1239,30 @@ mt7996_tx_check_aggr(struct ieee80211_link_sta *link_sta, clear_bit(tid, &wcid->ampdu_state); } +static void +mt7996_txp_skb_unmap(struct mt76_dev *mdev, struct mt76_txwi_cache *t) +{ + u8 *txwi_ptr = mt76_get_txwi_ptr(mdev, t); + __le32 *txwi = (__le32 *)txwi_ptr; + __le32 *txp; + dma_addr_t addr; + u32 val; + + if (!(le32_to_cpu(txwi[7]) & MT_TXD7_MAC_TXD)) { + mt76_connac_txp_skb_unmap(mdev, t); + return; + } + + txp = (__le32 *)(txwi_ptr + MT_TXD_SIZE); + val = le32_to_cpu(txp[3]); + addr = le32_to_cpu(txp[2]); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + addr |= (dma_addr_t)FIELD_GET(MT_TXP3_DMA_ADDR_H, val) << 32; +#endif + dma_unmap_single(mdev->dma_dev, addr, FIELD_GET(MT_TXP_BUF_LEN, val), + DMA_TO_DEVICE); +} + static void mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t, struct ieee80211_link_sta *link_sta, @@ -1248,7 +1272,7 @@ mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t, __le32 *txwi; u16 wcid_idx; - mt76_connac_txp_skb_unmap(mdev, t); + mt7996_txp_skb_unmap(mdev, t); if (!t->skb) goto out; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 2e83f4b79c8704..a1bae5db85007d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -4352,11 +4352,18 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_l event = (struct mt7996_mcu_eeprom_access_event *)skb->data; if (event->valid) { u32 ret_len = le32_to_cpu(event->eeprom.ext_eeprom.data_len); + u32 block = mode == EEPROM_MODE_EXT ? MT7996_EXT_EEPROM_BLOCK_SIZE : + MT7996_EEPROM_BLOCK_SIZE; addr = le32_to_cpu(event->addr); - if (!buf) + if (!buf) { + if (addr > dev->mt76.eeprom.size - block) { + dev_kfree_skb(skb); + return -EINVAL; + } buf = (u8 *)dev->mt76.eeprom.data + addr; + } switch (mode) { case EEPROM_MODE_EFUSE: diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c index 070c0431c4821c..4a5989172a5e3e 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c @@ -1652,7 +1652,7 @@ static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom) static void rtl8180_eeprom_read(struct rtl8180_priv *priv) { - struct eeprom_93cx6 eeprom; + struct eeprom_93cx6 eeprom = {}; int eeprom_cck_table_adr; u16 eeprom_val; int i; diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c index 1d21c468a23685..a766712187f77c 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -1445,7 +1445,7 @@ static int rtl8187_probe(struct usb_interface *intf, struct usb_device *udev = interface_to_usbdev(intf); struct ieee80211_hw *dev; struct rtl8187_priv *priv; - struct eeprom_93cx6 eeprom; + struct eeprom_93cx6 eeprom = {}; struct ieee80211_channel *channel; const char *chip_name; u16 txpwr, reg; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 646fe76b086e75..4f4bda3e0ce263 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -5838,14 +5838,19 @@ static void rtl8xxxu_queue_rx_urb(struct rtl8xxxu_priv *priv, { struct sk_buff *skb; unsigned long flags; - int pending = 0; spin_lock_irqsave(&priv->rx_urb_lock, flags); if (!priv->shutdown) { list_add_tail(&rx_urb->list, &priv->rx_urb_pending_list); priv->rx_urb_pending_count++; - pending = priv->rx_urb_pending_count; + /* + * Arm the worker under rx_urb_lock so this is atomic with the + * shutdown check: moving it out of the lock would let a + * completion arm the work after rtl8xxxu_stop() canceled it. + */ + if (priv->rx_urb_pending_count > RTL8XXXU_RX_URB_PENDING_WATER) + schedule_work(&priv->rx_urb_wq); } else { skb = (struct sk_buff *)rx_urb->urb.context; dev_kfree_skb_irq(skb); @@ -5853,9 +5858,6 @@ static void rtl8xxxu_queue_rx_urb(struct rtl8xxxu_priv *priv, } spin_unlock_irqrestore(&priv->rx_urb_lock, flags); - - if (pending > RTL8XXXU_RX_URB_PENDING_WATER) - schedule_work(&priv->rx_urb_wq); } static void rtl8xxxu_rx_urb_work(struct work_struct *work) @@ -7507,6 +7509,13 @@ static void rtl8xxxu_stop(struct ieee80211_hw *hw, bool suspend) priv->shutdown = true; spin_unlock_irqrestore(&priv->rx_urb_lock, flags); + /* + * Cancel before killing rx_anchor: the worker re-anchors every URB + * it drained via rtl8xxxu_submit_rx_urb(), so a worker still running + * after the kill could submit a URB that escapes it. + */ + cancel_work_sync(&priv->rx_urb_wq); + usb_kill_anchored_urbs(&priv->rx_anchor); usb_kill_anchored_urbs(&priv->tx_anchor); if (priv->usb_interrupts) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c index 7bafb051d5ec95..88e8cc590c544b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c @@ -145,8 +145,10 @@ static int rtl92du_init_sw_vars(struct ieee80211_hw *hw) /* for firmware buf */ rtlpriv->rtlhal.pfirmware = kmalloc(0x8000, GFP_KERNEL); - if (!rtlpriv->rtlhal.pfirmware) - return -ENOMEM; + if (!rtlpriv->rtlhal.pfirmware) { + err = -ENOMEM; + goto error; + } rtlpriv->max_fw_size = 0x8000; pr_info("Driver for Realtek RTL8192DU WLAN interface\n"); @@ -160,10 +162,14 @@ static int rtl92du_init_sw_vars(struct ieee80211_hw *hw) pr_err("Failed to request firmware!\n"); kfree(rtlpriv->rtlhal.pfirmware); rtlpriv->rtlhal.pfirmware = NULL; - return err; + goto error; } return 0; + +error: + rtl92du_deinit_shared_data(hw); + return err; } static void rtl92du_deinit_sw_vars(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c index 743ce0cfffe6dd..c608c51f1b785c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c @@ -106,7 +106,8 @@ void rtl92du_tx_fill_desc(struct ieee80211_hw *hw, if (sta) { sta_entry = (struct rtl_sta_info *)sta->drv_priv; tid = ieee80211_get_tid(hdr); - agg_state = sta_entry->tids[tid].agg.agg_state; + if (tid < MAX_TID_COUNT) + agg_state = sta_entry->tids[tid].agg.agg_state; ampdu_density = sta->deflink.ht_cap.ampdu_density; } diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index a3046722891242..69f2840fed095d 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1834,7 +1834,7 @@ int rtw_pci_probe(struct pci_dev *pdev, ret = rtw_pci_napi_init(rtwdev); if (ret) { rtw_err(rtwdev, "failed to setup NAPI\n"); - goto err_pci_declaim; + goto err_destroy_rsrc; } ret = rtw_chip_info_setup(rtwdev); @@ -1866,6 +1866,8 @@ int rtw_pci_probe(struct pci_dev *pdev, err_destroy_pci: rtw_pci_napi_deinit(rtwdev); + +err_destroy_rsrc: rtw_pci_destroy(rtwdev, pdev); err_pci_declaim: diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c index 9d747a060b984c..8786bbb421c2b9 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -619,6 +619,7 @@ static int rtw_txq_push_skb(struct rtw_dev *rtwdev, ret = rtw_hci_tx_write(rtwdev, &pkt_info, skb); if (ret) { rtw_err(rtwdev, "failed to write TX skb to HCI\n"); + ieee80211_free_txskb(rtwdev->hw, skb); return ret; } return 0; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 5547888d7e67a4..a85b9c39c25708 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5373,6 +5373,7 @@ enum rtw89_flags { RTW89_FLAG_CHANGING_INTERFACE, RTW89_FLAG_HW_RFKILL_STATE, RTW89_FLAG_UNPLUGGED, + RTW89_FLAG_SHUTDOWN, NUM_OF_RTW89_FLAGS, }; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index aade5c5b79e82d..9baedfde708542 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -1979,7 +1979,8 @@ static void rtw89_ops_rfkill_poll(struct ieee80211_hw *hw) lockdep_assert_wiphy(hw->wiphy); /* wl_disable GPIO get floating when entering LPS */ - if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) + if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags) || + test_bit(RTW89_FLAG_SHUTDOWN, rtwdev->flags)) return; rtw89_core_rfkill_poll(rtwdev, false); diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 102bae4881805b..61c54b52be1f5f 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -4874,6 +4874,19 @@ void rtw89_pci_remove(struct pci_dev *pdev) } EXPORT_SYMBOL(rtw89_pci_remove); +void rtw89_pci_shutdown(struct pci_dev *pdev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct rtw89_dev *rtwdev; + + if (!hw) + return; + + rtwdev = hw->priv; + set_bit(RTW89_FLAG_SHUTDOWN, rtwdev->flags); +} +EXPORT_SYMBOL(rtw89_pci_shutdown); + MODULE_AUTHOR("Realtek Corporation"); MODULE_DESCRIPTION("Realtek PCI 802.11ax wireless driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index c3f2d0df5846d8..0728120d721ffc 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -1751,6 +1751,7 @@ struct pci_device_id; int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id); void rtw89_pci_remove(struct pci_dev *pdev); +void rtw89_pci_shutdown(struct pci_dev *pdev); void rtw89_pci_basic_cfg(struct rtw89_dev *rtwdev, bool resume); void rtw89_pci_ops_reset(struct rtw89_dev *rtwdev); int rtw89_pci_ltr_set(struct rtw89_dev *rtwdev, bool en); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851be.c b/drivers/net/wireless/realtek/rtw89/rtw8851be.c index 640672eb0d268a..61e6c997b206f8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851be.c @@ -93,6 +93,7 @@ static struct pci_driver rtw89_8851be_driver = { .id_table = rtw89_8851be_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c index 64306cdc1ee481..e6fbcf75f1ad32 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ae.c @@ -95,6 +95,7 @@ static struct pci_driver rtw89_8852ae_driver = { .id_table = rtw89_8852ae_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852be.c b/drivers/net/wireless/realtek/rtw89/rtw8852be.c index 5bc0a6a99d1d2b..25463342ac890c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852be.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852be.c @@ -97,6 +97,7 @@ static struct pci_driver rtw89_8852be_driver = { .id_table = rtw89_8852be_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c index 49a72ca835ac7e..bf22be739f399e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bte.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bte.c @@ -99,6 +99,7 @@ static struct pci_driver rtw89_8852bte_driver = { .id_table = rtw89_8852bte_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c index 3c64c0539205cf..b8d4d9f0268699 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852ce.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852ce.c @@ -122,6 +122,7 @@ static struct pci_driver rtw89_8852ce_driver = { .id_table = rtw89_8852ce_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c index 5527a8db393b5c..54bcd3f3d00822 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922ae.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922ae.c @@ -111,6 +111,7 @@ static struct pci_driver rtw89_8922ae_driver = { .id_table = rtw89_8922ae_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops_be, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922de.c b/drivers/net/wireless/realtek/rtw89/rtw8922de.c index a1a81c338be3f6..65476a35a323cd 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922de.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922de.c @@ -111,6 +111,7 @@ static struct pci_driver rtw89_8922de_driver = { .id_table = rtw89_8922de_id_table, .probe = rtw89_pci_probe, .remove = rtw89_pci_remove, + .shutdown = rtw89_pci_shutdown, .driver.pm = &rtw89_pm_ops_be, .err_handler = &rtw89_pci_err_handler, }; diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index f59f926d4bfaa9..f9caa1a653c5b1 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -1719,9 +1719,16 @@ static void ntb_transport_rxc_db(unsigned long data) static void ntb_tx_copy_callback(void *data, const struct dmaengine_result *res) { + struct ntb_payload_header __iomem *hdr; struct ntb_queue_entry *entry = data; - struct ntb_transport_qp *qp = entry->qp; - struct ntb_payload_header __iomem *hdr = entry->tx_hdr; + struct ntb_transport_qp *qp; + unsigned int len; + void *cb_data; + + qp = entry->qp; + hdr = entry->tx_hdr; + cb_data = entry->cb_data; + len = entry->len; /* we need to check DMA results if we are using DMA */ if (res) { @@ -1768,15 +1775,13 @@ static void ntb_tx_copy_callback(void *data, * "link down" or similar. Since no payload is being sent in these * cases, there is nothing to add to the completion queue. */ - if (entry->len > 0) { - qp->tx_bytes += entry->len; - - if (qp->tx_handler) - qp->tx_handler(qp, qp->cb_data, entry->cb_data, - entry->len); - } + if (len > 0) + qp->tx_bytes += len; ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry, &qp->tx_free_q); + + if (len > 0 && qp->tx_handler) + qp->tx_handler(qp, qp->cb_data, cb_data, len); } static void ntb_memcpy_tx_on_stack(struct ntb_queue_entry *entry, void __iomem *offset) @@ -1950,15 +1955,6 @@ static int ntb_process_tx(struct ntb_transport_qp *qp, return -EAGAIN; } - if (entry->len > qp->tx_max_frame - sizeof(struct ntb_payload_header)) { - if (qp->tx_handler) - qp->tx_handler(qp, qp->cb_data, NULL, -EIO); - - ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry, - &qp->tx_free_q); - return 0; - } - ntb_async_tx(qp, entry); qp->tx_pkts++; @@ -2348,9 +2344,11 @@ int ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data, if (!qp || !len) return -EINVAL; - /* If the qp link is down already, just ignore. */ if (!qp->link_is_up) - return 0; + return -ENOLINK; + + if (len > qp->tx_max_frame - sizeof(struct ntb_payload_header)) + return -EMSGSIZE; entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q); if (!entry) { diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index 7e1112960d7f88..380e352dc3ccd2 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -883,6 +883,14 @@ static int discover_arenas(struct btt *btt) arena->external_lba_start = cur_nlba; parse_arena_meta(arena, super, cur_off); + if (arena->nfree < btt->nd_region->num_lanes) { + dev_err(to_dev(arena), + "nfree %u smaller than lane count %d\n", + arena->nfree, btt->nd_region->num_lanes); + ret = -ENODEV; + goto out; + } + ret = log_set_indices(arena); if (ret) { dev_err(to_dev(arena), diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c index 4218e3ac4a2ab1..ec12ce72cfe2ca 100644 --- a/drivers/nvdimm/label.c +++ b/drivers/nvdimm/label.c @@ -202,7 +202,7 @@ static int __nd_label_validate(struct nvdimm_drvdata *ndd) } nslot = __le32_to_cpu(nsindex[i]->nslot); - if (nslot * sizeof_namespace_label(ndd) + if ((u64)nslot * sizeof_namespace_label(ndd) + 2 * sizeof_namespace_index(ndd) > ndd->nsarea.config_size) { dev_dbg(dev, "nsindex%d nslot: %u invalid, config_size: %#x\n", diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 90ef027d7012ce..7574c00e7471d6 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2448,9 +2448,26 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns, if (!nvme_update_disk_info(ns, id, nvm, &lim)) capacity = 0; + /* + * A failed zone info query leaves zi zero-initialized, so skip the + * zoned limits update instead of configuring the queue from it. + * During a revalidation that keeps the zone geometry the queue was + * last validated with; on a first scan the namespace is registered + * without zoned limits, so that it is still available as a handle + * for admin commands. + */ if (IS_ENABLED(CONFIG_BLK_DEV_ZONED) && - ns->head->ids.csi == NVME_CSI_ZNS) - nvme_update_zone_info(ns, &lim, &zi); + ns->head->ids.csi == NVME_CSI_ZNS) { + if (zi.zone_size) + nvme_update_zone_info(ns, &lim, &zi); + else + dev_warn(ns->ctrl->device, + "zone info query failed for nsid %u, %s\n", + ns->head->ns_id, + blk_queue_is_zoned(ns->disk->queue) ? + "keeping the previous zone limits" : + "not enabling zoned mode"); + } if ((ns->ctrl->vwc & NVME_CTRL_VWC_PRESENT) && !info->no_vwc) lim.features |= BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA; @@ -4296,6 +4313,9 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, struct nvme_ns_info *info) last_path = true; } mutex_unlock(&ctrl->subsys->lock); + + /* guarantee not available in head->list */ + synchronize_srcu(&ns->head->srcu); if (last_path) nvme_put_ns_head(ns->head); nvme_put_ns_head(ns->head); @@ -5196,7 +5216,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, BUILD_BUG_ON(NVME_DSM_MAX_RANGES * sizeof(struct nvme_dsm_range) > PAGE_SIZE); - ctrl->discard_page = alloc_page(GFP_KERNEL); + ctrl->discard_page = alloc_page(GFP_KERNEL | __GFP_ZERO); if (!ctrl->discard_page) { ret = -ENOMEM; goto out; diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c index 91b9cb5f45f9f4..749d95f2b99cf8 100644 --- a/drivers/nvme/host/fabrics.c +++ b/drivers/nvme/host/fabrics.c @@ -1029,6 +1029,7 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts, } if (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) { pr_err("Invalid DH-CHAP secret %s\n", p); + kfree_sensitive(p); ret = -EINVAL; goto out; } @@ -1043,6 +1044,7 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts, } if (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) { pr_err("Invalid DH-CHAP secret %s\n", p); + kfree_sensitive(p); ret = -EINVAL; goto out; } diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 04363b9c4489e0..3082a17320b8f8 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2318,7 +2318,7 @@ nvme_fc_create_hw_io_queues(struct nvme_fc_ctrl *ctrl, u16 qsize) return 0; delete_queues: - for (; i > 0; i--) + for (--i; i > 0; i--) __nvme_fc_delete_hw_queue(ctrl, &ctrl->queues[i], i); return ret; } @@ -2358,9 +2358,15 @@ nvme_fc_ctrl_free(struct kref *ref) struct nvme_fc_ctrl *ctrl = container_of(ref, struct nvme_fc_ctrl, ref); unsigned long flags; + bool owns_opts; - /* remove from rport list */ + /* + * Presence on the rport list means nvme_fc_init_ctrl() completed, + * and with it ownership of the fabrics options passed to it. If it + * failed instead, the options still belong to nvmf_create_ctrl(). + */ spin_lock_irqsave(&ctrl->rport->lock, flags); + owns_opts = !list_empty(&ctrl->ctrl_list); list_del(&ctrl->ctrl_list); spin_unlock_irqrestore(&ctrl->rport->lock, flags); @@ -2370,7 +2376,7 @@ nvme_fc_ctrl_free(struct kref *ref) nvme_fc_rport_put(ctrl->rport); ida_free(&nvme_fc_ctrl_cnt, ctrl->cnum); - if (ctrl->ctrl.opts) + if (owns_opts) nvmf_free_options(ctrl->ctrl.opts); kfree(ctrl); } @@ -3569,14 +3575,14 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) { dev_err(ctrl->ctrl.device, "NVME-FC{%d}: failed to init ctrl state\n", ctrl->cnum); - goto fail_ctrl; + goto fail_unlist; } if (!queue_delayed_work(nvme_wq, &ctrl->connect_work, 0)) { dev_err(ctrl->ctrl.device, "NVME-FC{%d}: failed to schedule initial connect\n", ctrl->cnum); - goto fail_ctrl; + goto fail_unlist; } flush_delayed_work(&ctrl->connect_work); @@ -3587,14 +3593,22 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, return &ctrl->ctrl; +fail_unlist: + /* + * Leaving the list hands the options back to nvmf_create_ctrl(); + * see nvme_fc_ctrl_free(). Re-init so that list_empty() there + * reports the controller as unlisted. + */ + spin_lock_irqsave(&rport->lock, flags); + list_del_init(&ctrl->ctrl_list); + spin_unlock_irqrestore(&rport->lock, flags); + fail_ctrl: nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_DELETING); cancel_work_sync(&ctrl->ioerr_work); cancel_work_sync(&ctrl->ctrl.reset_work); cancel_delayed_work_sync(&ctrl->connect_work); - ctrl->ctrl.opts = NULL; - if (ctrl->ctrl.admin_tagset) nvme_remove_admin_tag_set(&ctrl->ctrl); /* initiate nvme ctrl ref counting teardown */ diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 69932d640b5379..d094717c17a014 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2400,6 +2400,7 @@ static int nvme_pci_configure_admin_queue(struct nvme_dev *dev) result = queue_request_irq(nvmeq); if (result) { dev->online_queues--; + nvme_disable_ctrl(&dev->ctrl, false); return result; } diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index a416269feadf2f..7717c730a66fe6 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -80,6 +80,7 @@ struct nvme_tcp_request { struct bio *curr_bio; struct iov_iter iter; + u32 data_recvd; /* send state */ size_t offset; @@ -108,6 +109,7 @@ struct nvme_tcp_queue { struct mutex queue_lock; struct mutex send_mutex; + struct mutex pf_cache_lock; struct llist_head req_list; struct list_head send_list; @@ -550,9 +552,11 @@ static int nvme_tcp_init_request(struct blk_mq_tag_set *set, struct nvme_tcp_queue *queue = &ctrl->queues[queue_idx]; u8 hdgst = nvme_tcp_hdgst_len(queue); + mutex_lock(&queue->pf_cache_lock); req->pdu = page_frag_alloc(&queue->pf_cache, sizeof(struct nvme_tcp_cmd_pdu) + hdgst, GFP_KERNEL | __GFP_ZERO); + mutex_unlock(&queue->pf_cache_lock); if (!req->pdu) return -ENOMEM; @@ -624,6 +628,29 @@ static void nvme_tcp_error_recovery(struct nvme_ctrl *ctrl) queue_delayed_work(nvme_reset_wq, &to_tcp_ctrl(ctrl)->err_work, delay); } +/* + * NVMe has no short read: a read that completes successfully must + * have transferred everything it asked for. + */ +static bool nvme_tcp_data_in_short(struct nvme_tcp_queue *queue, + struct request *rq) +{ + struct nvme_tcp_request *req = blk_mq_rq_to_pdu(rq); + + if (le16_to_cpu(req->status) >> 1) + return false; + if (req_op(rq) != REQ_OP_READ || !req->data_len) + return false; + if (likely(req->data_recvd == req->data_len)) + return false; + + dev_err(queue->ctrl->ctrl.device, + "queue %d tag %#x short data-in: got %u of %u\n", + nvme_tcp_queue_id(queue), rq->tag, + req->data_recvd, req->data_len); + return true; +} + static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue, struct nvme_completion *cqe) { @@ -643,6 +670,9 @@ static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue, if (req->status == cpu_to_le16(NVME_SC_SUCCESS)) req->status = cqe->status; + if (unlikely(nvme_tcp_data_in_short(queue, rq))) + return -EPROTO; + if (!nvme_try_complete_req(rq, req->status, cqe->result)) nvme_complete_rq(rq); queue->nr_cqe++; @@ -653,6 +683,7 @@ static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue, static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue, struct nvme_tcp_data_pdu *pdu) { + struct nvme_tcp_request *req; struct request *rq; rq = nvme_find_rq(nvme_tcp_tagset(queue), pdu->command_id); @@ -663,7 +694,15 @@ static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue, return -ENOENT; } - if (!blk_rq_payload_bytes(rq)) { + if (rq_data_dir(rq) != READ) { + dev_err(queue->ctrl->ctrl.device, + "queue %d tag %#x unexpected data for a write\n", + nvme_tcp_queue_id(queue), rq->tag); + return -EIO; + } + + req = blk_mq_rq_to_pdu(rq); + if (!blk_rq_payload_bytes(rq) || !req->curr_bio || !req->data_len) { dev_err(queue->ctrl->ctrl.device, "queue %d tag %#x unexpected data\n", nvme_tcp_queue_id(queue), rq->tag); @@ -757,6 +796,13 @@ static int nvme_tcp_handle_r2t(struct nvme_tcp_queue *queue, } req = blk_mq_rq_to_pdu(rq); + if (unlikely(rq_data_dir(rq) != WRITE)) { + dev_err(queue->ctrl->ctrl.device, + "req %d unexpected r2t for a non-write command\n", + rq->tag); + return -EPROTO; + } + if (unlikely(!r2t_length)) { dev_err(queue->ctrl->ctrl.device, "req %d r2t len is %u, probably a bug...\n", @@ -965,6 +1011,7 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb, *len -= recv_len; *offset += recv_len; queue->data_remaining -= recv_len; + req->data_recvd += recv_len; } if (!queue->data_remaining) { @@ -973,6 +1020,8 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb, queue->ddgst_remaining = NVME_TCP_DIGEST_LENGTH; } else { if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS) { + if (unlikely(nvme_tcp_data_in_short(queue, rq))) + return -EPROTO; nvme_tcp_end_request(rq, le16_to_cpu(req->status)); queue->nr_cqe++; @@ -1021,6 +1070,9 @@ static int nvme_tcp_recv_ddgst(struct nvme_tcp_queue *queue, pdu->command_id); struct nvme_tcp_request *req = blk_mq_rq_to_pdu(rq); + if (unlikely(nvme_tcp_data_in_short(queue, rq))) + return -EPROTO; + nvme_tcp_end_request(rq, le16_to_cpu(req->status)); queue->nr_cqe++; } @@ -1429,9 +1481,11 @@ static int nvme_tcp_alloc_async_req(struct nvme_tcp_ctrl *ctrl) struct nvme_tcp_request *async = &ctrl->async_req; u8 hdgst = nvme_tcp_hdgst_len(queue); + mutex_lock(&queue->pf_cache_lock); async->pdu = page_frag_alloc(&queue->pf_cache, sizeof(struct nvme_tcp_cmd_pdu) + hdgst, GFP_KERNEL | __GFP_ZERO); + mutex_unlock(&queue->pf_cache_lock); if (!async->pdu) return -ENOMEM; @@ -1473,6 +1527,7 @@ static void nvme_tcp_free_queue(struct nvme_ctrl *nctrl, int qid) kfree(queue->pdu); mutex_destroy(&queue->send_mutex); mutex_destroy(&queue->queue_lock); + mutex_destroy(&queue->pf_cache_lock); #ifdef CONFIG_DEBUG_LOCK_ALLOC lockdep_unregister_key(&queue->nvme_tcp_sk_key); @@ -1800,6 +1855,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid, INIT_LIST_HEAD(&queue->send_list); mutex_init(&queue->send_mutex); INIT_WORK(&queue->io_work, nvme_tcp_io_work); + mutex_init(&queue->pf_cache_lock); if (qid > 0) queue->cmnd_capsule_len = nctrl->ioccsz * 16; @@ -1940,6 +1996,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid, err_destroy_mutex: mutex_destroy(&queue->send_mutex); mutex_destroy(&queue->queue_lock); + mutex_destroy(&queue->pf_cache_lock); return ret; } @@ -2756,6 +2813,7 @@ static blk_status_t nvme_tcp_setup_cmd_pdu(struct nvme_ns *ns, req->status = cpu_to_le16(NVME_SC_SUCCESS); req->offset = 0; req->data_sent = 0; + req->data_recvd = 0; req->pdu_len = 0; req->pdu_sent = 0; req->h2cdata_left = 0; diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c index edb9627d97b096..a55319bcdbd13a 100644 --- a/drivers/nvme/target/auth.c +++ b/drivers/nvme/target/auth.c @@ -238,6 +238,12 @@ void nvmet_auth_sq_free(struct nvmet_sq *sq) sq->dhchap_skey = NULL; } +void nvmet_auth_sq_destroy(struct nvmet_sq *sq) +{ + cancel_delayed_work_sync(&sq->auth_expired_work); + nvmet_auth_sq_free(sq); +} + void nvmet_destroy_auth(struct nvmet_ctrl *ctrl) { ctrl->shash_id = 0; diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index 4477c4d6b1eed5..7e565a84d341af 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -977,7 +977,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq) wait_for_completion(&sq->confirm_done); wait_for_completion(&sq->free_done); percpu_ref_exit(&sq->ref); - nvmet_auth_sq_free(sq); + nvmet_auth_sq_destroy(sq); nvmet_cq_put(sq->cq); /* diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index aaba745e3c2145..1645f4c818afa2 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -915,6 +915,7 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, bool reset); void nvmet_auth_sq_init(struct nvmet_sq *sq); void nvmet_destroy_auth(struct nvmet_ctrl *ctrl); void nvmet_auth_sq_free(struct nvmet_sq *sq); +void nvmet_auth_sq_destroy(struct nvmet_sq *sq); int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id); bool nvmet_check_auth_status(struct nvmet_req *req); int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response, @@ -941,6 +942,7 @@ static inline void nvmet_auth_sq_init(struct nvmet_sq *sq) } static inline void nvmet_destroy_auth(struct nvmet_ctrl *ctrl) {}; static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {}; +static inline void nvmet_auth_sq_destroy(struct nvmet_sq *sq) {}; static inline bool nvmet_check_auth_status(struct nvmet_req *req) { return true; diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index e4f603b2ace78f..e5981017526204 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -103,6 +103,7 @@ enum nvmet_tcp_recv_state { enum { NVMET_TCP_F_INIT_FAILED = (1 << 0), + NVMET_TCP_F_R2T_SENT = (1 << 1), }; struct nvmet_tcp_cmd { @@ -776,6 +777,7 @@ static int nvmet_try_send_r2t(struct nvmet_tcp_cmd *cmd, bool last_in_batch) return -EAGAIN; cmd->queue->snd_cmd = NULL; + cmd->flags |= NVMET_TCP_F_R2T_SENT; return 1; } @@ -1009,6 +1011,12 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue) cmd = &queue->connect; } + if (unlikely(!(cmd->flags & NVMET_TCP_F_R2T_SENT))) { + pr_err("queue %d: unsolicited H2CData (ttag %u)\n", + queue->idx, data->ttag); + goto err_proto; + } + if (le32_to_cpu(data->data_offset) != cmd->rbytes_done) { pr_err("ttag %u unexpected data offset %u (expected %u)\n", data->ttag, le32_to_cpu(data->data_offset), @@ -1244,6 +1252,8 @@ static int nvmet_tcp_try_recv_pdu(struct nvmet_tcp_queue *queue) } queue->left = hdr->hlen - queue->offset + hdgst; + if (queue->left > sizeof(queue->pdu) - queue->offset) + return -EPROTO; goto recv; } diff --git a/drivers/of/base.c b/drivers/of/base.c index 6e7a42dedad3f2..477017ed6f49b2 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1967,7 +1967,7 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) /* walk the alias backwards to extract the id and work out * the 'stem' string */ - while (isdigit(*(end-1)) && end > start) + while (end > start && isdigit(*(end - 1))) end--; len = end - start; diff --git a/drivers/parisc/eisa.c b/drivers/parisc/eisa.c index 9eab974e6baf63..b5b5413bfd1e9e 100644 --- a/drivers/parisc/eisa.c +++ b/drivers/parisc/eisa.c @@ -442,6 +442,11 @@ static int __init eisa_irq_setup(char *str) val = (int) simple_strtoul(cur, &pe, 0); if (val > 15 || val < 0) { printk(KERN_ERR "eisa: EISA irq value are 0-15\n"); + cur = strchr(cur, ','); + if (cur) + cur++; + else + break; continue; } if (val == 2) { diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index 7a4da9fae1eaa8..71b0ebc0e4727c 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -401,7 +401,7 @@ static int meson_pcie_probe(struct platform_device *pdev) return PTR_ERR(mp->phy); } - mp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + mp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(mp->reset_gpio)) { dev_err(dev, "get reset gpio failed\n"); return PTR_ERR(mp->reset_gpio); diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index cfc8fa403dad66..89816a2bd7cd3a 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -2119,6 +2119,7 @@ static bool hv_pcie_init_dev_msi_info(struct device *dev, struct irq_domain *dom info->ops->msi_prepare = hv_msi_prepare; chip->irq_set_affinity = irq_chip_set_affinity_parent; + chip->irq_retrigger = irq_chip_retrigger_hierarchy; if (IS_ENABLED(CONFIG_X86)) chip->flags |= IRQCHIP_MOVE_DEFERRED; diff --git a/drivers/pci/controller/plda/pcie-plda-host.c b/drivers/pci/controller/plda/pcie-plda-host.c index f9a34f323ad8c5..fd1a11b4c8eb81 100644 --- a/drivers/pci/controller/plda/pcie-plda-host.c +++ b/drivers/pci/controller/plda/pcie-plda-host.c @@ -419,6 +419,8 @@ static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port) return plda_allocate_msi_domains(port); } +static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie); + int plda_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port, const struct plda_event *event) @@ -440,14 +442,17 @@ int plda_init_interrupts(struct platform_device *pdev, } port->irq = platform_get_irq(pdev, 0); - if (port->irq < 0) - return -ENODEV; + if (port->irq < 0) { + ret = -ENODEV; + goto err_irq_domain_deinit; + } for_each_set_bit(i, &port->events_bitmap, port->num_events) { event_irq = irq_create_mapping(port->event_domain, i); if (!event_irq) { dev_err(dev, "failed to map hwirq %d\n", i); - return -ENXIO; + ret = -ENXIO; + goto err_irq_domain_deinit; } if (event->request_event_irq) @@ -459,7 +464,7 @@ int plda_init_interrupts(struct platform_device *pdev, if (ret) { dev_err(dev, "failed to request IRQ %d\n", event_irq); - return ret; + goto err_irq_domain_deinit; } } @@ -467,7 +472,8 @@ int plda_init_interrupts(struct platform_device *pdev, event->intx_event); if (!port->intx_irq) { dev_err(dev, "failed to map INTx interrupt\n"); - return -ENXIO; + ret = -ENXIO; + goto err_irq_domain_deinit; } /* Plug the INTx chained handler */ @@ -475,8 +481,11 @@ int plda_init_interrupts(struct platform_device *pdev, port->msi_irq = irq_create_mapping(port->event_domain, event->msi_event); - if (!port->msi_irq) - return -ENXIO; + if (!port->msi_irq) { + dev_err(dev, "failed to map MSI interrupt\n"); + ret = -ENXIO; + goto err_irq_domain_deinit; + } /* Plug the MSI chained handler */ irq_set_chained_handler_and_data(port->msi_irq, plda_handle_msi, port); @@ -485,6 +494,11 @@ int plda_init_interrupts(struct platform_device *pdev, irq_set_chained_handler_and_data(port->irq, plda_handle_event, port); return 0; + +err_irq_domain_deinit: + plda_pcie_irq_domain_deinit(port); + + return ret; } EXPORT_SYMBOL_GPL(plda_init_interrupts); @@ -559,9 +573,27 @@ EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems); static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie) { - irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); - irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL); - irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL); + u32 i, event_irq; + + if (pcie->irq > 0) + irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); + if (pcie->msi_irq > 0) + irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL); + if (pcie->intx_irq > 0) + irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL); + + for_each_set_bit(i, &pcie->events_bitmap, pcie->num_events) { + event_irq = irq_find_mapping(pcie->event_domain, i); + if (event_irq) { + devm_free_irq(pcie->dev, event_irq, pcie); + irq_dispose_mapping(event_irq); + } + } + + if (pcie->intx_irq) + irq_dispose_mapping(pcie->intx_irq); + if (pcie->msi_irq) + irq_dispose_mapping(pcie->msi_irq); irq_domain_remove(pcie->msi.dev_domain); diff --git a/drivers/pci/controller/plda/pcie-starfive.c b/drivers/pci/controller/plda/pcie-starfive.c index 298036c3e7f949..628f8c8d67471d 100644 --- a/drivers/pci/controller/plda/pcie-starfive.c +++ b/drivers/pci/controller/plda/pcie-starfive.c @@ -304,12 +304,14 @@ static int starfive_pcie_host_init(struct plda_pcie_rp *plda) ret = starfive_pcie_clk_rst_init(pcie); if (ret) - return ret; + goto err_disable_phy; if (pcie->vpcie3v3) { ret = regulator_enable(pcie->vpcie3v3); - if (ret) + if (ret) { dev_err_probe(dev, ret, "failed to enable vpcie3v3 regulator\n"); + goto err_clk_rst; + } } if (pcie->reset_gpio) @@ -379,6 +381,13 @@ static int starfive_pcie_host_init(struct plda_pcie_rp *plda) dev_info(dev, "port link down\n"); return 0; + +err_clk_rst: + starfive_pcie_clk_rst_deinit(pcie); +err_disable_phy: + starfive_pcie_disable_phy(pcie); + + return ret; } static const struct plda_pcie_host_ops sf_host_ops = { diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index ff92a5c301b81d..37299d59f906f0 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c @@ -808,7 +808,7 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn) if (dn->child && PCI_DN(dn->child)) php_slot->slot_no = PCI_SLOT(PCI_DN(dn->child)->devfn); else - php_slot->slot_no = -1; /* Placeholder slot */ + php_slot->slot_no = PCI_SLOT_PLACEHOLDER; /* Placeholder slot */ kref_init(&php_slot->kref); php_slot->state = PNV_PHP_STATE_INITIALIZED; diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c index 67362e5b99719e..92eabf5f61b9c8 100644 --- a/drivers/pci/hotplug/rpaphp_slot.c +++ b/drivers/pci/hotplug/rpaphp_slot.c @@ -84,7 +84,7 @@ int rpaphp_register_slot(struct slot *slot) struct hotplug_slot *php_slot = &slot->hotplug_slot; u32 my_index; int retval; - int slotno = -1; + int slotno = PCI_SLOT_PLACEHOLDER; dbg("%s registering slot:path[%pOF] index[%x], name[%s] pdomain[%x] type[%d]\n", __func__, slot->dn, slot->index, slot->name, diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c index 209373c92e9e10..80a9db417dc876 100644 --- a/drivers/pci/msi/msi.c +++ b/drivers/pci/msi/msi.c @@ -870,6 +870,7 @@ void __pci_restore_msix_state(struct pci_dev *dev) { struct msi_desc *entry; bool write_msg; + u16 cmd; if (!dev->msix_enabled) return; @@ -879,6 +880,14 @@ void __pci_restore_msix_state(struct pci_dev *dev) pci_msix_clear_and_set_ctrl(dev, 0, PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL); + /* + * The restored device state may not have Memory Space enabled. + * Since the MSI-X Table and PBA are in Memory Space, enable it + * while restoring them. + */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY); + write_msg = arch_restore_msi_irqs(dev); scoped_guard (msi_descs_lock, &dev->dev) { @@ -889,6 +898,7 @@ void __pci_restore_msix_state(struct pci_dev *dev) } } + pci_write_config_word(dev, PCI_COMMAND, cmd); pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); } diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 5ec0b245a69bdf..1be627913a9bec 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -718,7 +718,7 @@ static ssize_t pci_read_config(struct file *filp, struct kobject *kobj, else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) size = 128; - if (off > size) + if (off >= size) return 0; if (off + count > size) { size -= off; @@ -799,7 +799,7 @@ static ssize_t pci_write_config(struct file *filp, struct kobject *kobj, add_taint(TAINT_USER, LOCKDEP_STILL_OK); } - if (off > dev->cfg_size) + if (off >= dev->cfg_size) return 0; if (off + count > dev->cfg_size) { size = dev->cfg_size - off; @@ -888,12 +888,30 @@ static ssize_t pci_read_legacy_io(struct file *filp, struct kobject *kobj, char *buf, loff_t off, size_t count) { struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); + u32 val = 0; + int ret; /* Only support 1, 2 or 4 byte accesses */ if (count != 1 && count != 2 && count != 4) return -EINVAL; - return pci_legacy_read(bus, off, (u32 *)buf, count); + ret = pci_legacy_read(bus, off, &val, count); + if (ret < 0) + return ret; + + switch (count) { + case 1: + buf[0] = *(u8 *)&val; + break; + case 2: + put_unaligned_le16(*(u16 *)&val, buf); + break; + case 4: + put_unaligned_le32(val, buf); + break; + } + + return ret; } /** @@ -913,12 +931,24 @@ static ssize_t pci_write_legacy_io(struct file *filp, struct kobject *kobj, char *buf, loff_t off, size_t count) { struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); + u32 val; - /* Only support 1, 2 or 4 byte accesses */ - if (count != 1 && count != 2 && count != 4) + /* Only support 1, 2 or 4 byte accesses. */ + switch (count) { + case 1: + val = *(u8 *)buf; + break; + case 2: + val = get_unaligned_le16(buf); + break; + case 4: + val = get_unaligned_le32(buf); + break; + default: return -EINVAL; + } - return pci_legacy_write(bus, off, *(u32 *)buf, count); + return pci_legacy_write(bus, off, val, count); } /** diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 77b17b13ee6154..350bae907ebf8c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4897,8 +4897,9 @@ static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, bool probe) static int pci_dev_reset_slot_function(struct pci_dev *dev, bool probe) { - if (dev->multifunction || dev->subordinate || !dev->slot || - dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET) + if (dev->subordinate || !dev->slot || + dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET || + (dev->multifunction && !dev->slot->per_func_slot)) return -ENOTTY; return pci_reset_hotplug_slot(dev->slot->hotplug, probe); diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index c4fd9c0b2a5482..fbb764ffb916dd 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -428,23 +428,32 @@ void pci_aer_exit(struct pci_dev *dev) #define AER_AGENT_REQUESTER 1 #define AER_AGENT_COMPLETER 2 #define AER_AGENT_TRANSMITTER 3 +#define AER_AGENT_COMPONENT 4 #define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \ - 0 : (PCI_ERR_UNC_COMP_TIME|PCI_ERR_UNC_UNSUP)) + 0 : PCI_ERR_UNC_COMP_TIME) #define AER_AGENT_COMPLETER_MASK(t) ((t == AER_CORRECTABLE) ? \ 0 : PCI_ERR_UNC_COMP_ABORT) #define AER_AGENT_TRANSMITTER_MASK(t) ((t == AER_CORRECTABLE) ? \ - (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : 0) + (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : \ + (PCI_ERR_UNC_POISON_BLK|PCI_ERR_UNC_ATOMEG| \ + PCI_ERR_UNC_DMWR_BLK|PCI_ERR_UNC_XLAT_BLK| \ + PCI_ERR_UNC_TLPPRE)) +#define AER_AGENT_COMPONENT_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_INTERNAL|PCI_ERR_COR_LOG_OVER) : \ + (PCI_ERR_UNC_INTN|PCI_ERR_UNC_SURPDN)) #define AER_GET_AGENT(t, e) \ ((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \ (e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \ (e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \ + (e & AER_AGENT_COMPONENT_MASK(t)) ? AER_AGENT_COMPONENT : \ AER_AGENT_RECEIVER) #define AER_PHYSICAL_LAYER_ERROR 0 #define AER_DATA_LINK_LAYER_ERROR 1 #define AER_TRANSACTION_LAYER_ERROR 2 +#define AER_GENERAL_ERROR 3 #define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ PCI_ERR_COR_RCVR : 0) @@ -452,11 +461,14 @@ void pci_aer_exit(struct pci_dev *dev) (PCI_ERR_COR_BAD_TLP| \ PCI_ERR_COR_BAD_DLLP| \ PCI_ERR_COR_REP_ROLL| \ - PCI_ERR_COR_REP_TIMER) : PCI_ERR_UNC_DLP) + PCI_ERR_COR_REP_TIMER) : (PCI_ERR_UNC_DLP|PCI_ERR_UNC_SURPDN)) +#define AER_GENERAL_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_INTERNAL|PCI_ERR_COR_LOG_OVER) : PCI_ERR_UNC_INTN) #define AER_GET_LAYER_ERROR(t, e) \ ((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \ (e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \ + (e & AER_GENERAL_ERROR_MASK(t)) ? AER_GENERAL_ERROR : \ AER_TRANSACTION_LAYER_ERROR) /* @@ -471,7 +483,8 @@ static const char * const aer_error_severity_string[] = { static const char *aer_error_layer[] = { "Physical Layer", "Data Link Layer", - "Transaction Layer" + "Transaction Layer", + "General", }; static const char *aer_correctable_error_string[] = { @@ -548,7 +561,8 @@ static const char *aer_agent_string[] = { "Receiver ID", "Requester ID", "Completer ID", - "Transmitter ID" + "Transmitter ID", + "Component ID", }; #define aer_stats_dev_attr(name, stats_array, strings_array, \ @@ -942,7 +956,8 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity, status = aer->uncor_status; mask = aer->uncor_mask; info.level = KERN_ERR; - tlp_header_valid = tlp_header_logged(status, aer->cap_control); + tlp_header_valid = tlp_header_logged(status & ~mask, + aer->cap_control); } info.status = status; @@ -1336,7 +1351,7 @@ int aer_get_device_error_info(struct aer_err_info *info, int i) pci_read_config_dword(dev, aer + PCI_ERR_CAP, &aercc); info->first_error = PCI_ERR_CAP_FEP(aercc); - if (tlp_header_logged(info->status, aercc)) { + if (tlp_header_logged(info->status & ~info->mask, aercc)) { info->tlp_header_valid = 1; pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG, aer + PCI_ERR_PREFIX_LOG, diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 71ad289fcb8e31..f5fd860a0540b4 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include "pci.h" @@ -39,13 +41,16 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf, * undefined locations (think of Intel PIIX4 as a typical example). */ - if (capable(CAP_SYS_ADMIN)) + if (file_ns_capable(file, &init_user_ns, CAP_SYS_ADMIN)) size = dev->cfg_size; else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) size = 128; else size = 64; + if (!nbytes) + return 0; + if (pos >= size) return 0; if (nbytes >= size) @@ -122,6 +127,15 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, if (ret) return ret; + if (!nbytes) + return 0; + + if (resource_is_exclusive(&dev->driver_exclusive_resource, pos, nbytes)) { + pci_warn_once(dev, "%s: Unexpected write to kernel-exclusive config offset %x", + current->comm, pos); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + } + if (pos >= size) return 0; if (nbytes >= size) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index e7b3fda85327ba..95335fb44bd523 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2522,6 +2522,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10f1, quirk_disable_aspm_l0s); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10f4, quirk_disable_aspm_l0s); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1508, quirk_disable_aspm_l0s); +/* Realtek RTS525A generates a Replay Timer Timeout storm when L0s is enabled. */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x525a, quirk_disable_aspm_l0s); + static void quirk_disable_aspm_l0s_l1(struct pci_dev *dev) { pcie_aspm_remove_cap(dev, @@ -5376,7 +5379,7 @@ static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev) if (!(mpc & INTEL_MPC_REG_IRBNCE)) { pci_info(dev, "Enabling MPC IRBNCE\n"); mpc |= INTEL_MPC_REG_IRBNCE; - pci_write_config_word(dev, INTEL_MPC_REG, mpc); + pci_write_config_dword(dev, INTEL_MPC_REG, mpc); } } @@ -6278,6 +6281,10 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0xb404, pci_fixup_pericom_acs_store_forward); DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0xb404, pci_fixup_pericom_acs_store_forward); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0x2608, + pci_fixup_pericom_acs_store_forward); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0x2608, + pci_fixup_pericom_acs_store_forward); static void nvidia_ion_ahci_fixup(struct pci_dev *pdev) { diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 6d5cd37bfb1e73..897223f01f6ac4 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -37,7 +37,7 @@ static const struct sysfs_ops pci_slot_sysfs_ops = { static ssize_t address_read_file(struct pci_slot *slot, char *buf) { - if (slot->number == 0xff) + if (slot->number == PCI_SLOT_PLACEHOLDER) return sysfs_emit(buf, "%04x:%02x\n", pci_domain_nr(slot->bus), slot->bus->number); @@ -72,6 +72,23 @@ static ssize_t cur_speed_read_file(struct pci_slot *slot, char *buf) return bus_speed_read(slot->bus->cur_bus_speed, buf); } +static bool pci_dev_matches_slot(struct pci_dev *dev, struct pci_slot *slot) +{ + if (slot->per_func_slot) + return dev->devfn == slot->number; + + return slot->number == PCI_SLOT_ALL_DEVICES || + PCI_SLOT(dev->devfn) == slot->number; +} + +static bool pci_slot_enabled_per_func(void) +{ + if (IS_ENABLED(CONFIG_S390)) + return true; + + return false; +} + static void pci_slot_release(struct kobject *kobj) { struct pci_dev *dev; @@ -82,8 +99,7 @@ static void pci_slot_release(struct kobject *kobj) down_read(&pci_bus_sem); list_for_each_entry(dev, &slot->bus->devices, bus_list) - if (slot->number == PCI_SLOT_ALL_DEVICES || - PCI_SLOT(dev->devfn) == slot->number) + if (pci_dev_matches_slot(dev, slot)) dev->slot = NULL; up_read(&pci_bus_sem); @@ -187,8 +203,7 @@ void pci_dev_assign_slot(struct pci_dev *dev) mutex_lock(&pci_slot_mutex); list_for_each_entry(slot, &dev->bus->slots, list) - if (slot->number == PCI_SLOT_ALL_DEVICES || - PCI_SLOT(dev->devfn) == slot->number) + if (pci_dev_matches_slot(dev, slot)) dev->slot = slot; mutex_unlock(&pci_slot_mutex); } @@ -210,7 +225,7 @@ static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) /** * pci_create_slot - create or increment refcount for physical PCI slot * @parent: struct pci_bus of parent bridge - * @slot_nr: PCI_SLOT(pci_dev->devfn), -1 for placeholder, or + * @slot_nr: PCI_SLOT(pci_dev->devfn), PCI_SLOT_PLACEHOLDER for placeholder, or * PCI_SLOT_ALL_DEVICES * @name: user visible string presented in /sys/bus/pci/slots/ * @hotplug: set if caller is hotplug driver, NULL otherwise @@ -236,15 +251,16 @@ static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) * In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify * a slot. There is one notable exception - pSeries (rpaphp), where the * @slot_nr cannot be determined until a device is actually inserted into - * the slot. In this scenario, the caller may pass -1 for @slot_nr. + * the slot. In this scenario, the caller may pass PCI_SLOT_PLACEHOLDER for @slot_nr. * * The following semantics are imposed when the caller passes @slot_nr == - * -1. First, we no longer check for an existing %struct pci_slot, as there - * may be many slots with @slot_nr of -1. The other change in semantics is - * user-visible, which is the 'address' parameter presented in sysfs will - * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the - * %struct pci_bus and bb is the bus number. In other words, the devfn of - * the 'placeholder' slot will not be displayed. + * PCI_SLOT_PLACEHOLDER. First, we no longer check for an existing %struct + * pci_slot, as there may be many slots with @slot_nr of + * PCI_SLOT_PLACEHOLDER. The other change in semantics is user-visible, + * which is the 'address' parameter presented in sysfs will consist solely + * of a dddd:bb tuple, where dddd is the PCI domain of the %struct pci_bus + * and bb is the bus number. In other words, the devfn of the 'placeholder' + * slot will not be displayed. * * Bus-wide slots: * For PCIe hotplug, the physical slot encompasses the entire secondary @@ -267,7 +283,7 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, mutex_lock(&pci_slot_mutex); - if (slot_nr == -1) + if (slot_nr == PCI_SLOT_PLACEHOLDER) goto placeholder; /* @@ -298,6 +314,9 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, slot->bus = pci_bus_get(parent); slot->number = slot_nr; + if (pci_slot_enabled_per_func()) + slot->per_func_slot = 1; + slot->kobj.kset = pci_slots_kset; slot_name = make_slot_name(name); @@ -318,8 +337,7 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, down_read(&pci_bus_sem); list_for_each_entry(dev, &parent->devices, bus_list) - if (slot_nr == PCI_SLOT_ALL_DEVICES || - PCI_SLOT(dev->devfn) == slot_nr) + if (pci_dev_matches_slot(dev, slot)) dev->slot = slot; up_read(&pci_bus_sem); diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c index b05d80e849a144..9a33c06d6fc39d 100644 --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -173,9 +173,9 @@ static struct typec_switch_dev *tca_blk_get_typec_switch(struct platform_device return sw; } -static void tca_blk_put_typec_switch(struct typec_switch_dev *sw) +static void tca_blk_put_typec_switch(void *data) { - typec_switch_unregister(sw); + typec_switch_unregister(data); } static void tca_blk_orientation_set(struct tca_blk *tca, @@ -248,6 +248,7 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev, struct device *dev = &pdev->dev; struct resource *res; struct tca_blk *tca; + int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) @@ -266,17 +267,11 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev, tca->orientation = TYPEC_ORIENTATION_NORMAL; tca->sw = tca_blk_get_typec_switch(pdev, imx_phy); - return tca; -} - -static void imx95_usb_phy_put_tca(struct imx8mq_usb_phy *imx_phy) -{ - struct tca_blk *tca = imx_phy->tca; - - if (!tca) - return; + ret = devm_add_action_or_reset(&pdev->dev, tca_blk_put_typec_switch, tca->sw); + if (ret) + return ERR_PTR(ret); - tca_blk_put_typec_switch(tca->sw); + return tca; } static u32 phy_tx_vref_tune_from_property(u32 percent) @@ -741,9 +736,7 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) static void imx8mq_usb_phy_remove(struct platform_device *pdev) { - struct imx8mq_usb_phy *imx_phy = platform_get_drvdata(pdev); - imx95_usb_phy_put_tca(imx_phy); } static struct platform_driver imx8mq_usb_phy_driver = { diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c index cbd780556da8fd..18f5f582d1d7a9 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c @@ -1526,7 +1526,7 @@ static const struct regmap_config samsung_mipi_dcphy_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .max_register = 0x10000, + .max_register = 0xfffc, }; static struct phy *samsung_mipi_dcphy_xlate(struct device *dev, diff --git a/drivers/platform/chrome/cros_ec_sensorhub_ring.c b/drivers/platform/chrome/cros_ec_sensorhub_ring.c index a10579144c34b3..e613dce244302f 100644 --- a/drivers/platform/chrome/cros_ec_sensorhub_ring.c +++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c @@ -475,6 +475,21 @@ cros_ec_sensor_ring_process_event(struct cros_ec_sensorhub *sensorhub, fifo_timestamp, *current_timestamp, now); + + /* + * A standalone timestamp event typically has a sensor_num of + * 0xff. Return early here to prevent it from hitting the + * bounds check below and spamming the logs. + */ + return false; + } + + /* Skip event if sensor_num from EC is out of bounds. */ + if (in->sensor_num >= sensorhub->sensor_num) { + dev_warn_ratelimited(sensorhub->dev, + "Invalid sensor number %u from EC\n", + in->sensor_num); + return false; } if (in->flags & MOTIONSENSE_SENSOR_FLAG_ODR) { @@ -502,10 +517,6 @@ cros_ec_sensor_ring_process_event(struct cros_ec_sensorhub *sensorhub, return true; } - if (in->flags & MOTIONSENSE_SENSOR_FLAG_TIMESTAMP) - /* If we just have a timestamp, skip this entry. */ - return false; - /* Regular sample */ out->sensor_id = in->sensor_num; trace_cros_ec_sensorhub_data(in->sensor_num, diff --git a/drivers/platform/x86/amd/pmc/mp1_stb.c b/drivers/platform/x86/amd/pmc/mp1_stb.c index 753d630f3283df..1ec0e599df7f3b 100644 --- a/drivers/platform/x86/amd/pmc/mp1_stb.c +++ b/drivers/platform/x86/amd/pmc/mp1_stb.c @@ -157,7 +157,7 @@ static int amd_stb_debugfs_open_v2(struct inode *inode, struct file *filp) struct amd_pmc_dev *dev = filp->f_inode->i_private; u32 fsize, num_samples, val, stb_rdptr_offset = 0; struct amd_stb_v2_data *stb_data_arr; - int ret; + int ret = 0; /* Write dummy postcode while reading the STB buffer */ ret = amd_stb_write(dev, AMD_PMC_STB_DUMMY_PC); @@ -176,22 +176,24 @@ static int amd_stb_debugfs_open_v2(struct inode *inode, struct file *filp) * the enhanced dram size. Note that we land here only for the * platforms that support enhanced dram size reporting. */ - if (dump_custom_stb) - return amd_stb_handle_efr(filp); + if (dump_custom_stb) { + ret = amd_stb_handle_efr(filp); + goto out; + } /* Get the num_samples to calculate the last push location */ ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->stb_arg.s2d_msg_id, true); - /* Clear msg_port for other SMU operation */ - dev->msg_port = MSG_PORT_PMC; if (ret) { dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret); - return ret; + goto out; } fsize = min(num_samples, S2D_TELEMETRY_BYTES_MAX); stb_data_arr = kmalloc_flex(*stb_data_arr, data, fsize); - if (!stb_data_arr) - return -ENOMEM; + if (!stb_data_arr) { + ret = -ENOMEM; + goto out; + } stb_data_arr->size = fsize; @@ -214,7 +216,10 @@ static int amd_stb_debugfs_open_v2(struct inode *inode, struct file *filp) filp->private_data = stb_data_arr; - return 0; +out: + /* Restore the default message port for subsequent SMU operations */ + dev->msg_port = MSG_PORT_PMC; + return ret; } static ssize_t amd_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size, @@ -289,7 +294,7 @@ int amd_stb_s2d_init(struct amd_pmc_dev *dev) u32 phys_addr_low, phys_addr_hi; u64 stb_phys_addr; u32 size = 0; - int ret; + int ret = 0; if (!enable_stb) return 0; @@ -306,27 +311,42 @@ int amd_stb_s2d_init(struct amd_pmc_dev *dev) /* Spill to DRAM feature uses separate SMU message port */ dev->msg_port = MSG_PORT_S2D; - amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->stb_arg.s2d_msg_id, true); - if (size != S2D_TELEMETRY_BYTES_MAX) - return -EIO; + ret = amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->stb_arg.s2d_msg_id, true); + if (ret) + goto out; + if (size != S2D_TELEMETRY_BYTES_MAX) { + ret = -EIO; + goto out; + } - /* Get DRAM size */ - ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->stb_arg.s2d_msg_id, true); - if (ret || !dev->dram_size) + /* Get DRAM size; fall back to the default if the query fails */ + if (amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->stb_arg.s2d_msg_id, true) || + !dev->dram_size) dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX; /* Get STB DRAM address */ - amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->stb_arg.s2d_msg_id, true); - amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->stb_arg.s2d_msg_id, true); + ret = amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, + dev->stb_arg.s2d_msg_id, true); + if (ret) + goto out; + ret = amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, + dev->stb_arg.s2d_msg_id, true); + if (ret) + goto out; stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low); - - /* Clear msg_port for other SMU operation */ - dev->msg_port = MSG_PORT_PMC; + if (!stb_phys_addr) { + dev_err(dev->dev, "S2D phys addr query returned invalid address\n"); + ret = -ENXIO; + goto out; + } dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size); if (!dev->stb_virt_addr) - return -ENOMEM; + ret = -ENOMEM; - return 0; +out: + /* Restore the default message port for subsequent SMU operations */ + dev->msg_port = MSG_PORT_PMC; + return ret; } diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index a37083cdb9083f..542b5d04515ae7 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -941,13 +941,17 @@ static int amd_pmc_probe(struct platform_device *pdev) amd_pmc_dbgfs_register(dev); err = amd_stb_s2d_init(dev); if (err) - goto err_pci_dev_put; + goto err_dbgfs_unregister; if (IS_ENABLED(CONFIG_AMD_MP2_STB)) amd_mp2_stb_init(dev); pm_report_max_hw_sleep(U64_MAX); return 0; +err_dbgfs_unregister: + amd_pmc_dbgfs_unregister(dev); + if (IS_ENABLED(CONFIG_SUSPEND)) + acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops); err_pci_dev_put: pci_dev_put(rdev); return err; diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c b/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c index db278ff4cc4d45..154e115af4b4c6 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c @@ -84,7 +84,6 @@ int set_attribute(const char *a_name, const char *a_value) if (ret < 0) goto out; - print_hex_dump_bytes("set attribute data: ", DUMP_PREFIX_NONE, buffer, buffer_size); ret = call_biosattributes_interface(wmi_priv.bios_attr_wdev, buffer, buffer_size, SETATTRIBUTE_METHOD_ID); diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c index 27fd6cd2152908..309634c1cc20ee 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c @@ -85,7 +85,7 @@ int hp_get_string_from_buffer(u8 **buffer, u32 *buffer_size, char *dst, u32 dst_ * bytes. */ conv_dst_size = size; - if (size > dst_size) + if (size >= dst_size) conv_dst_size = dst_size - 1; /* @@ -661,12 +661,17 @@ static int hp_init_bios_package_attribute(enum hp_wmi_data_type attr_type, int ret = 0; /* Take action appropriate to each ACPI TYPE */ - if (obj->package.count < min_elements) { - pr_err("ACPI-package does not have enough elements: %d < %d\n", - obj->package.count, min_elements); + if (obj->package.count < COMMON_ELEM_CNT) { + pr_err("ACPI-package is missing common elements: %d < %d\n", + obj->package.count, COMMON_ELEM_CNT); goto pack_attr_exit; } + if (obj->package.count < min_elements) { + pr_warn("ACPI-package has fewer elements than expected: %d < %d, parsing available elements\n", + obj->package.count, min_elements); + } + elements = obj->package.elements; /* sanity checking */ @@ -731,26 +736,31 @@ static int hp_init_bios_package_attribute(enum hp_wmi_data_type attr_type, switch (attr_type) { case HPWMI_STRING_TYPE: ret = hp_populate_string_package_data(elements, + obj->package.count, instance_id, attr_name_kobj); break; case HPWMI_INTEGER_TYPE: ret = hp_populate_integer_package_data(elements, + obj->package.count, instance_id, attr_name_kobj); break; case HPWMI_ENUMERATION_TYPE: ret = hp_populate_enumeration_package_data(elements, + obj->package.count, instance_id, attr_name_kobj); break; case HPWMI_ORDERED_LIST_TYPE: ret = hp_populate_ordered_list_package_data(elements, + obj->package.count, instance_id, attr_name_kobj); break; case HPWMI_PASSWORD_TYPE: ret = hp_populate_password_package_data(elements, + obj->package.count, instance_id, attr_name_kobj); break; diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.h b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.h index f1eec0e4ba075b..ac57d6eab4c358 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.h +++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.h @@ -279,6 +279,9 @@ enum hp_wmi_data_elements { PSWD_ENCODINGS = 13, PSWD_IS_SET = 14, PSWD_ELEM_CNT = 15, + + /* Minimum elements shared by all attribute types (NAME..SECURITY_LEVEL) */ + COMMON_ELEM_CNT = SECURITY_LEVEL + 1, }; #define GET_INSTANCE_ID(type) \ @@ -401,6 +404,7 @@ int hp_populate_string_buffer_data(u8 *buffer_ptr, u32 *buffer_size, int hp_alloc_string_data(void); void hp_exit_string_attributes(void); int hp_populate_string_package_data(union acpi_object *str_obj, + int str_obj_count, int instance_id, struct kobject *attr_name_kobj); @@ -411,6 +415,7 @@ int hp_populate_integer_buffer_data(u8 *buffer_ptr, u32 *buffer_size, int hp_alloc_integer_data(void); void hp_exit_integer_attributes(void); int hp_populate_integer_package_data(union acpi_object *integer_obj, + int integer_obj_count, int instance_id, struct kobject *attr_name_kobj); @@ -421,6 +426,7 @@ int hp_populate_enumeration_buffer_data(u8 *buffer_ptr, u32 *buffer_size, int hp_alloc_enumeration_data(void); void hp_exit_enumeration_attributes(void); int hp_populate_enumeration_package_data(union acpi_object *enum_obj, + int enum_obj_count, int instance_id, struct kobject *attr_name_kobj); @@ -432,6 +438,7 @@ int hp_populate_ordered_list_buffer_data(u8 *buffer_ptr, int hp_alloc_ordered_list_data(void); void hp_exit_ordered_list_attributes(void); int hp_populate_ordered_list_package_data(union acpi_object *order_obj, + int order_obj_count, int instance_id, struct kobject *attr_name_kobj); @@ -440,6 +447,7 @@ int hp_populate_password_buffer_data(u8 *buffer_ptr, u32 *buffer_size, int instance_id, struct kobject *attr_name_kobj); int hp_populate_password_package_data(union acpi_object *password_obj, + int password_obj_count, int instance_id, struct kobject *attr_name_kobj); int hp_alloc_password_data(void); diff --git a/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c index af4d1920d48809..446dd18d2cee4f 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c @@ -163,10 +163,11 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum /* Check that both expected and read object type match */ if (expected_enum_types[eloc] != enum_obj[elem].type) { - pr_err("Error expected type %d for elem %d, but got type %d instead\n", - expected_enum_types[eloc], elem, enum_obj[elem].type); + pr_warn("Unexpected element type at elem %d: expected %d, got %d, skipping\n", + elem, expected_enum_types[eloc], enum_obj[elem].type); kfree(str_value); - return -EIO; + str_value = NULL; + continue; } /* Assign appropriate element value to corresponding field */ @@ -227,6 +228,8 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum kfree(str_value); str_value = NULL; } + if (size) + elem += size - 1; break; case SECURITY_LEVEL: @@ -280,6 +283,8 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum kfree(str_value); str_value = NULL; } + if (size) + elem += (size < MAX_VALUES_SIZE ? size : MAX_VALUES_SIZE) - 1; break; default: pr_warn("Invalid element: %d found in Enumeration attribute or data may be malformed\n", elem); @@ -300,10 +305,12 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum * Populate all properties of an instance under enumeration attribute * * @enum_obj: ACPI object with enumeration data + * @enum_obj_count: Number of elements in @enum_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ int hp_populate_enumeration_package_data(union acpi_object *enum_obj, + int enum_obj_count, int instance_id, struct kobject *attr_name_kobj) { @@ -312,7 +319,7 @@ int hp_populate_enumeration_package_data(union acpi_object *enum_obj, enum_data->attr_name_kobj = attr_name_kobj; hp_populate_enumeration_elements_from_package(enum_obj, - enum_obj->package.count, + enum_obj_count, instance_id); hp_update_attribute_permissions(enum_data->common.is_readonly, &enumeration_current_val); diff --git a/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c index d96e160953e393..a2790706644801 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c @@ -243,6 +243,8 @@ static int hp_populate_integer_elements_from_package(union acpi_object *integer_ kfree(str_value); str_value = NULL; } + if (size) + elem += size - 1; break; case SECURITY_LEVEL: @@ -275,10 +277,12 @@ static int hp_populate_integer_elements_from_package(union acpi_object *integer_ * Populate all properties of an instance under integer attribute * * @integer_obj: ACPI object with integer data + * @integer_obj_count: Number of elements in @integer_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ int hp_populate_integer_package_data(union acpi_object *integer_obj, + int integer_obj_count, int instance_id, struct kobject *attr_name_kobj) { @@ -286,7 +290,7 @@ int hp_populate_integer_package_data(union acpi_object *integer_obj, integer_data->attr_name_kobj = attr_name_kobj; hp_populate_integer_elements_from_package(integer_obj, - integer_obj->package.count, + integer_obj_count, instance_id); hp_update_attribute_permissions(integer_data->common.is_readonly, &integer_current_val); diff --git a/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c index f09489a085c860..5bf8d40bdf8167 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c @@ -145,7 +145,7 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord if (!order_obj) return -EINVAL; - for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT; elem++, eloc++) { + for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT && elem < order_obj_count; elem++, eloc++) { switch (order_obj[elem].type) { case ACPI_TYPE_STRING: @@ -232,6 +232,8 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord kfree(str_value); str_value = NULL; } + if (size) + elem += size - 1; break; case SECURITY_LEVEL: @@ -261,7 +263,9 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord * Ordered list data is stored in hex and comma separated format * Convert the data and split it to show each element */ - ret = hp_convert_hexstr_to_str(str_value, value_len, &tmpstr, &tmp_len); + ret = hp_convert_hexstr_to_str(order_obj[elem].string.pointer, + order_obj[elem].string.length, + &tmpstr, &tmp_len); if (ret) goto exit_list; @@ -298,10 +302,12 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord * Populate all properties of an instance under ordered_list attribute * * @order_obj: ACPI object with ordered_list data + * @order_obj_count: Number of elements in @order_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ -int hp_populate_ordered_list_package_data(union acpi_object *order_obj, int instance_id, +int hp_populate_ordered_list_package_data(union acpi_object *order_obj, int order_obj_count, + int instance_id, struct kobject *attr_name_kobj) { struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id]; @@ -309,7 +315,7 @@ int hp_populate_ordered_list_package_data(union acpi_object *order_obj, int inst ordered_list_data->attr_name_kobj = attr_name_kobj; hp_populate_ordered_list_elements_from_package(order_obj, - order_obj->package.count, + order_obj_count, instance_id); hp_update_attribute_permissions(ordered_list_data->common.is_readonly, &ordered_list_current_val); diff --git a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c index 4d79eb8056a5d8..9821403e42b644 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c @@ -66,7 +66,7 @@ static int validate_password_input(int instance_id, const char *buf) struct password_data *password_data = &bioscfg_drv.password_data[instance_id]; length = strlen(buf); - if (buf[length - 1] == '\n') + if (length > 0 && buf[length - 1] == '\n') length--; if (length > MAX_PASSWD_SIZE) @@ -123,7 +123,7 @@ static ssize_t new_password_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { - return store_password_instance(kobj, buf, count, true); + return store_password_instance(kobj, buf, count, false); } static struct kobj_attribute password_new_password = __ATTR_WO(new_password); @@ -321,6 +321,8 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor str_value = NULL; } + if (size) + elem += size - 1; break; case SECURITY_LEVEL: password_data->common.security_level = int_value; @@ -362,6 +364,8 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor str_value = NULL; } + if (size) + elem += size - 1; break; case PSWD_IS_SET: password_data->is_enabled = int_value; @@ -385,10 +389,12 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor * Populate all properties for an instance under password attribute * * @password_obj: ACPI object with password data + * @password_obj_count: Number of elements in @password_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ -int hp_populate_password_package_data(union acpi_object *password_obj, int instance_id, +int hp_populate_password_package_data(union acpi_object *password_obj, int password_obj_count, + int instance_id, struct kobject *attr_name_kobj) { struct password_data *password_data = &bioscfg_drv.password_data[instance_id]; @@ -396,7 +402,7 @@ int hp_populate_password_package_data(union acpi_object *password_obj, int insta password_data->attr_name_kobj = attr_name_kobj; hp_populate_password_elements_from_package(password_obj, - password_obj->package.count, + password_obj_count, instance_id); hp_friendly_user_name_update(password_data->common.path, diff --git a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c index 2b00a14792e921..4d94e48c1a4c80 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c @@ -238,7 +238,7 @@ static ssize_t sk_store(struct kobject *kobj, ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_SK, HPWMI_SECUREPLATFORM, (void *)bioscfg_drv.spm_data.signing_key, - count, 0); + length, 0); if (!ret) { bioscfg_drv.spm_data.mechanism = SIGNING_KEY; @@ -274,7 +274,7 @@ static ssize_t kek_store(struct kobject *kobj, ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_KEK, HPWMI_SECUREPLATFORM, (void *)bioscfg_drv.spm_data.endorsement_key, - count, 0); + length, 0); if (!ret) { bioscfg_drv.spm_data.mechanism = ENDORSEMENT_KEY; diff --git a/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c index fe5a9a3a4ef174..f3dfba270f7e81 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c @@ -233,6 +233,8 @@ static int hp_populate_string_elements_from_package(union acpi_object *string_ob kfree(str_value); str_value = NULL; } + if (size) + elem += size - 1; break; case SECURITY_LEVEL: @@ -263,10 +265,12 @@ static int hp_populate_string_elements_from_package(union acpi_object *string_ob * Populate all properties of an instance under string attribute * * @string_obj: ACPI object with string data + * @string_obj_count: Number of elements in @string_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ int hp_populate_string_package_data(union acpi_object *string_obj, + int string_obj_count, int instance_id, struct kobject *attr_name_kobj) { @@ -275,7 +279,7 @@ int hp_populate_string_package_data(union acpi_object *string_obj, string_data->attr_name_kobj = attr_name_kobj; hp_populate_string_elements_from_package(string_obj, - string_obj->package.count, + string_obj_count, instance_id); hp_update_attribute_permissions(string_data->common.is_readonly, diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 8ba286ed87216e..94147102cca43a 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -265,6 +265,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D87") }, .driver_data = (void *)&omen_v1_no_ec_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8DD6") }, + .driver_data = (void *)&omen_v1_no_ec_thermal_params, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8E35") }, .driver_data = (void *)&omen_v1_legacy_thermal_params, diff --git a/drivers/platform/x86/intel/int1092/intel_sar.c b/drivers/platform/x86/intel/int1092/intel_sar.c index 849f7b415c1e59..7263114f0b3d07 100644 --- a/drivers/platform/x86/intel/int1092/intel_sar.c +++ b/drivers/platform/x86/intel/int1092/intel_sar.c @@ -91,8 +91,10 @@ static acpi_status parse_package(struct wwan_sar_context *context, union acpi_ob item->package.count <= data->total_dev_mode) return AE_ERROR; - data->device_mode_info = kmalloc_objs(struct wwan_device_mode_info, - data->total_dev_mode); + data->device_mode_info = devm_kmalloc_array(&context->sar_device->dev, + data->total_dev_mode, + sizeof(*data->device_mode_info), + GFP_KERNEL); if (!data->device_mode_info) return AE_ERROR; @@ -253,7 +255,7 @@ static int sar_probe(struct platform_device *device) if (!handle) return -ENODEV; - context = kzalloc_obj(*context); + context = devm_kzalloc(&device->dev, sizeof(*context), GFP_KERNEL); if (!context) return -ENOMEM; @@ -264,7 +266,7 @@ static int sar_probe(struct platform_device *device) result = guid_parse(SAR_DSM_UUID, &context->guid); if (result) { dev_err(&device->dev, "SAR UUID parse error: %d\n", result); - goto r_free; + return result; } for (reg = 0; reg < MAX_REGULATORY; reg++) @@ -272,43 +274,29 @@ static int sar_probe(struct platform_device *device) if (sar_get_device_mode(device) != AE_OK) { dev_err(&device->dev, "Failed to get device mode\n"); - result = -EIO; - goto r_free; + return -EIO; } result = sysfs_create_group(&device->dev.kobj, &intcsar_group); if (result) { dev_err(&device->dev, "sysfs creation failed\n"); - goto r_free; + return result; } if (acpi_install_notify_handler(ACPI_HANDLE(&device->dev), ACPI_DEVICE_NOTIFY, sar_notify, (void *)device) != AE_OK) { dev_err(&device->dev, "Failed acpi_install_notify_handler\n"); - result = -EIO; - goto r_sys; + sysfs_remove_group(&device->dev.kobj, &intcsar_group); + return -EIO; } return 0; - -r_sys: - sysfs_remove_group(&device->dev.kobj, &intcsar_group); -r_free: - kfree(context); - return result; } static void sar_remove(struct platform_device *device) { - struct wwan_sar_context *context = dev_get_drvdata(&device->dev); - int reg; - acpi_remove_notify_handler(ACPI_HANDLE(&device->dev), ACPI_DEVICE_NOTIFY, sar_notify); sysfs_remove_group(&device->dev.kobj, &intcsar_group); - for (reg = 0; reg < MAX_REGULATORY; reg++) - kfree(context->config_data[reg].device_mode_info); - - kfree(context); } static struct platform_driver sar_driver = { diff --git a/drivers/platform/x86/intel/ishtp_eclite.c b/drivers/platform/x86/intel/ishtp_eclite.c index 93ac8b2dbf3878..bca7e217878b4a 100644 --- a/drivers/platform/x86/intel/ishtp_eclite.c +++ b/drivers/platform/x86/intel/ishtp_eclite.c @@ -600,13 +600,16 @@ static int ecl_ishtp_cl_probe(struct ishtp_cl_device *cl_device) rv = acpi_opregion_init(opr_dev); if (rv) { dev_err(cl_data_to_dev(opr_dev), "ACPI opregion init failed\n"); - goto err_exit; + goto err_put; } /* Reprobe devices depending on ECLite - battery, fan, etc. */ acpi_dev_clear_dependencies(opr_dev->adev); return 0; + +err_put: + acpi_dev_put(opr_dev->adev); err_exit: ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_DISCONNECTING); ishtp_cl_disconnect(ecl_ishtp_cl); diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index 24334ae70d8252..66dd974720beab 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -336,8 +336,11 @@ static int sst_add_perf_profiles(struct auxiliary_device *auxdev, int i; pd_info->perf_levels = devm_kcalloc(dev, levels, sizeof(struct perf_level), GFP_KERNEL); - if (!pd_info->perf_levels) - return 0; + if (!pd_info->perf_levels) { + pd_info->pp_header.allowed_level_mask = 0; + pd_info->pp_header.level_en_mask = 0; + return -ENOMEM; + } pd_info->ratio_unit = pd_info->pp_header.ratio_unit; pd_info->avx_levels = SST_MAX_AVX_LEVELS; @@ -367,7 +370,7 @@ static int sst_add_perf_profiles(struct auxiliary_device *auxdev, static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domain_info *pd_info) { struct device *dev = &auxdev->dev; - int i, mask, levels; + int i, ret, mask, levels; *((u64 *)&pd_info->sst_header) = readq(pd_info->sst_base); pd_info->sst_header.cp_offset *= 8; @@ -399,8 +402,12 @@ static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domai levels = i; mask <<= 1; } + + ret = sst_add_perf_profiles(auxdev, pd_info, levels + 1); + if (ret) + return ret; + pd_info->max_level = levels; - sst_add_perf_profiles(auxdev, pd_info, levels + 1); return 0; } @@ -599,6 +606,9 @@ static bool disable_dynamic_sst_features(void) #define SST_CP_PRIORITY_TYPE_START 1 #define SST_CP_PRIORITY_TYPE_WIDTH 1 +#define SST_CP_MAX_ENABLE 1 +#define SST_CP_MAX_PRIORITY_TYPE 1 + static long isst_if_core_power_state(void __user *argp) { struct tpmi_per_power_domain_info *power_domain_info; @@ -618,6 +628,10 @@ static long isst_if_core_power_state(void __user *argp) if (power_domain_info->write_blocked || !capable(CAP_SYS_ADMIN)) return -EPERM; + if (core_power.enable > SST_CP_MAX_ENABLE || + core_power.priority_type > SST_CP_MAX_PRIORITY_TYPE) + return -EINVAL; + _write_cp_info("cp_enable", core_power.enable, SST_CP_CONTROL_OFFSET, SST_CP_ENABLE_START, SST_CP_ENABLE_WIDTH, SST_MUL_FACTOR_NONE) _write_cp_info("cp_prio_type", core_power.priority_type, SST_CP_CONTROL_OFFSET, @@ -649,6 +663,11 @@ static long isst_if_core_power_state(void __user *argp) #define SST_CLOS_CONFIG_MAX_START 16 #define SST_CLOS_CONFIG_MAX_WIDTH 8 +#define SST_MAX_CLOS 3 + +#define SST_MAX_FREQ 0xff +#define SST_CLOS_MAX_PRIORITY 0x0f + static long isst_if_clos_param(void __user *argp) { struct tpmi_per_power_domain_info *power_domain_info; @@ -657,6 +676,9 @@ static long isst_if_clos_param(void __user *argp) if (copy_from_user(&clos_param, argp, sizeof(clos_param))) return -EFAULT; + if (clos_param.clos > SST_MAX_CLOS) + return -EINVAL; + power_domain_info = get_instance(clos_param.socket_id, clos_param.power_domain_id); if (!power_domain_info) return -EINVAL; @@ -665,6 +687,15 @@ static long isst_if_clos_param(void __user *argp) if (power_domain_info->write_blocked || !capable(CAP_SYS_ADMIN)) return -EPERM; + if (!in_range(clos_param.min_freq_mhz / SST_MUL_FACTOR_FREQ, 0, SST_MAX_FREQ + 1)) + return -EINVAL; + + if (!in_range(clos_param.max_freq_mhz / SST_MUL_FACTOR_FREQ, 0, SST_MAX_FREQ + 1)) + return -EINVAL; + + if (!in_range(clos_param.prop_prio, 0, SST_CLOS_MAX_PRIORITY + 1)) + return -EINVAL; + _write_cp_info("clos.min_freq", clos_param.min_freq_mhz, (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), SST_CLOS_CONFIG_MIN_START, SST_CLOS_CONFIG_MIN_WIDTH, @@ -703,6 +734,8 @@ static long isst_if_clos_param(void __user *argp) #define SST_CLOS_ASSOC_CPUS_PER_REG 16 #define SST_CLOS_ASSOC_BITS_PER_CPU 4 +#define SST_CLOS_ASSOC_MAX_LOGICAL_CPU 63 + static long isst_if_clos_assoc(void __user *argp) { struct isst_if_clos_assoc_cmds assoc_cmds; @@ -729,7 +762,13 @@ static long isst_if_clos_assoc(void __user *argp) if (copy_from_user(&clos_assoc, ptr, sizeof(clos_assoc))) return -EFAULT; - if (clos_assoc.socket_id > topology_max_packages()) + if (clos_assoc.clos > SST_MAX_CLOS) + return -EINVAL; + + if (clos_assoc.socket_id >= topology_max_packages()) + return -EINVAL; + + if (clos_assoc.logical_cpu > SST_CLOS_ASSOC_MAX_LOGICAL_CPU) return -EINVAL; cpu = clos_assoc.logical_cpu; @@ -747,6 +786,8 @@ static long isst_if_clos_assoc(void __user *argp) pkg_id = clos_assoc.socket_id; sst_inst = isst_common.sst_inst[pkg_id]; + if (!sst_inst) + return -EINVAL; punit_id = map_partition_power_domain_id(sst_inst, punit_id, &part); if (punit_id < 0) @@ -845,6 +886,7 @@ static long isst_if_clos_assoc(void __user *argp) #define SST_PP_FEATURE_STATE_START 8 #define SST_PP_FEATURE_STATE_WIDTH 8 +#define SST_PP_FEATURE_STATE_VALID_MASK GENMASK(1, 0) #define SST_BF_FEATURE_SUPPORTED_START 12 #define SST_BF_FEATURE_SUPPORTED_WIDTH 1 @@ -877,7 +919,7 @@ static int isst_if_get_perf_level(void __user *argp) SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH, SST_MUL_FACTOR_NONE) perf_level.enabled = !!(power_domain_info->sst_header.cap_mask & BIT(1)); - level_mask = perf_level.level_mask; + level_mask = perf_level.level_mask & power_domain_info->pp_header.level_en_mask; perf_level.sst_bf_support = 0; for_each_set_bit(level, &level_mask, BITS_PER_BYTE) { /* @@ -932,6 +974,9 @@ static int isst_if_set_perf_level(void __user *argp) if (!power_domain_info) return -EINVAL; + if (perf_level.level > power_domain_info->max_level) + return -EINVAL; + if (power_domain_info->write_blocked || !capable(CAP_SYS_ADMIN)) return -EPERM; @@ -995,6 +1040,9 @@ static int isst_if_set_perf_feature(void __user *argp) if (power_domain_info->write_blocked || !capable(CAP_SYS_ADMIN)) return -EPERM; + if (perf_feature.feature & ~SST_PP_FEATURE_STATE_VALID_MASK) + return -EINVAL; + _write_pp_info("perf_feature", perf_feature.feature, SST_PP_CONTROL_OFFSET, SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH, SST_MUL_FACTOR_NONE) @@ -1261,6 +1309,12 @@ static int isst_if_get_perf_level_mask(void __user *argp) if (!power_domain_info) return -EINVAL; + if (cpumask.level > power_domain_info->max_level) + return -EINVAL; + + if (!(power_domain_info->pp_header.level_en_mask & BIT(cpumask.level))) + return -EINVAL; + _read_pp_level_info("mask", mask, cpumask.level, SST_PP_INFO_2_OFFSET, SST_PP_RSLVD_CORE_MASK_START, SST_PP_RSLVD_CORE_MASK_WIDTH, SST_MUL_FACTOR_NONE) @@ -1306,6 +1360,9 @@ static int isst_if_get_base_freq_info(void __user *argp) if (base_freq.level > power_domain_info->max_level) return -EINVAL; + if (!(power_domain_info->pp_header.level_en_mask & BIT(base_freq.level))) + return -EINVAL; + _read_bf_level_info("p1_high", base_freq.high_base_freq_mhz, base_freq.level, SST_BF_INFO_0_OFFSET, SST_BF_P1_HIGH_START, SST_BF_P1_HIGH_WIDTH, SST_MUL_FACTOR_FREQ) @@ -1342,6 +1399,12 @@ static int isst_if_get_base_freq_mask(void __user *argp) if (!power_domain_info) return -EINVAL; + if (cpumask.level > power_domain_info->max_level) + return -EINVAL; + + if (!(power_domain_info->pp_header.level_en_mask & BIT(cpumask.level))) + return -EINVAL; + _read_bf_level_info("BF-cpumask", mask, cpumask.level, SST_BF_INFO_1_OFFSET, P1_HI_CORE_MASK_START, P1_HI_CORE_MASK_WIDTH, SST_MUL_FACTOR_NONE) @@ -1370,6 +1433,8 @@ static int isst_if_get_tpmi_instance_count(void __user *argp) return -EINVAL; sst_inst = isst_common.sst_inst[tpmi_inst.socket_id]; + if (!sst_inst) + return -EINVAL; tpmi_inst.count = isst_instance_count(sst_inst); @@ -1437,6 +1502,9 @@ static int isst_if_get_turbo_freq_info(void __user *argp) if (turbo_freq.level > power_domain_info->max_level) return -EINVAL; + if (!(power_domain_info->pp_header.level_en_mask & BIT(turbo_freq.level))) + return -EINVAL; + turbo_freq.max_buckets = TRL_MAX_BUCKETS; turbo_freq.max_trl_levels = TRL_MAX_LEVELS; turbo_freq.max_clip_freqs = SST_TF_MAX_LP_CLIP_RATIOS; diff --git a/drivers/platform/x86/lenovo/think-lmi.c b/drivers/platform/x86/lenovo/think-lmi.c index e215e86e3db736..a0e3fa766e37b5 100644 --- a/drivers/platform/x86/lenovo/think-lmi.c +++ b/drivers/platform/x86/lenovo/think-lmi.c @@ -438,14 +438,13 @@ static ssize_t current_password_store(struct kobject *kobj, struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); size_t pwdlen; - pwdlen = strlen(buf); + /* Strip newline; setting password won't work if one is present. */ + pwdlen = strchrnul(buf, '\n') - buf; /* pwdlen == 0 is allowed to clear the password */ if (pwdlen && ((pwdlen < setting->minlen) || (pwdlen > setting->maxlen))) return -EINVAL; - strscpy(setting->password, buf, setting->maxlen); - /* Strip out CR if one is present, setting password won't work if it is present */ - strreplace(setting->password, '\n', '\0'); + strscpy(setting->password, buf, pwdlen + 1); return count; } @@ -745,6 +744,8 @@ static ssize_t certificate_thumbprint_show(struct kobject *kobj, struct kobj_att return -EOPNOTSUPP; for (i = 0; i < ARRAY_SIZE(thumbtypes); i++) { + ssize_t ret; + if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) { /* Format: 'SVC | SMC, Thumbtype' */ wmistr = kasprintf(GFP_KERNEL, "%s,%s", @@ -756,8 +757,12 @@ static ssize_t certificate_thumbprint_show(struct kobject *kobj, struct kobj_att } if (!wmistr) return -ENOMEM; - count += cert_thumbprint(buf, wmistr, count); + + ret = cert_thumbprint(buf, wmistr, count); kfree(wmistr); + if (ret < 0) + return ret; + count = ret; } return count; @@ -1456,6 +1461,10 @@ static void tlmi_release_attr(void) /* Free up any saved signatures */ kfree(tlmi_priv.pwd_admin->signature); kfree(tlmi_priv.pwd_admin->save_signature); + if (tlmi_priv.pwd_system) { + kfree(tlmi_priv.pwd_system->signature); + kfree(tlmi_priv.pwd_system->save_signature); + } /* Authentication structures */ list_for_each_entry_safe(pos, n, &tlmi_priv.authentication_kset->list, entry) diff --git a/drivers/platform/x86/lenovo/ymc.c b/drivers/platform/x86/lenovo/ymc.c index 1b73a55f1b89c7..015e046b0fcea8 100644 --- a/drivers/platform/x86/lenovo/ymc.c +++ b/drivers/platform/x86/lenovo/ymc.c @@ -8,6 +8,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include +#include #include #include #include @@ -20,6 +22,8 @@ #define LENOVO_YMC_QUERY_INSTANCE 0 #define LENOVO_YMC_QUERY_METHOD 0x01 +#define LENOVO_YMC_STATE_MASK GENMASK(7, 0) + static bool force; module_param(force, bool, 0444); MODULE_PARM_DESC(force, "Force loading on boards without a convertible DMI chassis-type"); @@ -85,7 +89,9 @@ static void lenovo_ymc_notify(struct wmi_device *wdev, union acpi_object *data) "WMI event data is not an integer\n"); goto free_obj; } - code = obj->integer.value; + + /* strip upper bits (e.g. 0x50000) on newer devices */ + code = FIELD_GET(LENOVO_YMC_STATE_MASK, obj->integer.value); if (!sparse_keymap_report_event(priv->input_dev, code, 1, true)) dev_warn(&wdev->dev, "Unknown key %d pressed\n", code); diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 719add753cb3da..49098d82d6e02f 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -360,7 +360,16 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc) } else pr_err("Invalid HKEY.SINF data\n"); } - pcc->sinf[hkey->package.count] = -1; + /* + * pcc->sinf[] has pcc->num_sifr elements (valid indices + * 0..num_sifr-1). On DSDTs where SINF's package count equals + * num_sifr exactly -- the off-by-one case probe()'s num_sifr++ + * already allocates a spare element for -- there is no room left + * for this trailing sentinel; nothing reads it back, so just skip + * the write rather than running one element past the flex array. + */ + if (hkey->package.count < pcc->num_sifr) + pcc->sinf[hkey->package.count] = -1; end: kfree(buffer.pointer); diff --git a/drivers/pmdomain/mediatek/Kconfig b/drivers/pmdomain/mediatek/Kconfig index 8923e6516441cd..a2eb704a773c9f 100644 --- a/drivers/pmdomain/mediatek/Kconfig +++ b/drivers/pmdomain/mediatek/Kconfig @@ -43,9 +43,12 @@ config MTK_MFG_PM_DOMAIN This driver is required for the Mali GPU to work at all on MT8196 and MT6991. +endmenu + config AIROHA_CPU_PM_DOMAIN tristate "Airoha CPU power domain" default ARCH_AIROHA + depends on ARCH_AIROHA || COMPILE_TEST depends on HAVE_ARM_SMCCC depends on PM select PM_GENERIC_DOMAINS @@ -54,5 +57,3 @@ config AIROHA_CPU_PM_DOMAIN CPU frequency and power is controlled by ATF with SMC command to set performance states. - -endmenu diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index ffc46de8d07a19..63120e703923f0 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -245,13 +245,9 @@ static struct rpmhpd *eliza_rpmhpds[] = { [RPMHPD_GFX] = &gfx, [RPMHPD_LCX] = &lcx, [RPMHPD_LMX] = &lmx, - [RPMHPD_MMCX] = &mmcx, - [RPMHPD_MMCX_AO] = &mmcx_ao, [RPMHPD_MSS] = &mss, [RPMHPD_MX] = &mx, [RPMHPD_MX_AO] = &mx_ao, - [RPMHPD_MXC] = &mxc, - [RPMHPD_MXC_AO] = &mxc_ao, [RPMHPD_NSP] = &nsp, }; diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index eb5c1ae68e440b..cfcc05dfe28d6d 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -3054,6 +3054,20 @@ static void ab8500_fg_unbind(struct device *dev, struct device *master, flush_workqueue(di->fg_wq); } +/* Disable, not cancel: works stay disabled so nothing can re-arm them. */ +static void ab8500_fg_destroy_workqueue(void *data) +{ + struct ab8500_fg *di = data; + + disable_work_sync(&di->fg_acc_cur_work); + disable_work_sync(&di->fg_work); + disable_delayed_work_sync(&di->fg_reinit_work); + disable_delayed_work_sync(&di->fg_low_bat_work); + disable_delayed_work_sync(&di->fg_check_hw_failure_work); + disable_delayed_work_sync(&di->fg_periodic_work); + destroy_workqueue(di->fg_wq); +} + static const struct component_ops ab8500_fg_component_ops = { .bind = ab8500_fg_bind, .unbind = ab8500_fg_unbind, @@ -3155,6 +3169,11 @@ static int ab8500_fg_probe(struct platform_device *pdev) return PTR_ERR(di->fg_psy); } + /* Registered after fg_psy, before the IRQs: devm frees IRQ -> workqueue -> fg_psy. */ + ret = devm_add_action_or_reset(dev, ab8500_fg_destroy_workqueue, di); + if (ret) + return ret; + di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer); /* @@ -3167,22 +3186,16 @@ static int ab8500_fg_probe(struct platform_device *pdev) /* Register primary interrupt handlers */ for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq); i++) { irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name); - if (irq < 0) { - destroy_workqueue(di->fg_wq); + if (irq < 0) return irq; - } ret = devm_request_threaded_irq(dev, irq, NULL, ab8500_fg_irq[i].isr, IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, ab8500_fg_irq[i].name, di); - if (ret != 0) { - dev_err(dev, "failed to request %s IRQ %d: %d\n", - ab8500_fg_irq[i].name, irq, ret); - destroy_workqueue(di->fg_wq); + if (ret != 0) return ret; - } dev_dbg(dev, "Requested %s IRQ %d: %d\n", ab8500_fg_irq[i].name, irq, ret); } @@ -3196,7 +3209,6 @@ static int ab8500_fg_probe(struct platform_device *pdev) ret = ab8500_fg_sysfs_init(di); if (ret) { dev_err(dev, "failed to create sysfs entry\n"); - destroy_workqueue(di->fg_wq); return ret; } @@ -3204,7 +3216,6 @@ static int ab8500_fg_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "failed to create FG psy\n"); ab8500_fg_sysfs_exit(di); - destroy_workqueue(di->fg_wq); return ret; } @@ -3224,7 +3235,6 @@ static void ab8500_fg_remove(struct platform_device *pdev) { struct ab8500_fg *di = platform_get_drvdata(pdev); - destroy_workqueue(di->fg_wq); component_del(&pdev->dev, &ab8500_fg_component_ops); list_del(&di->node); ab8500_fg_sysfs_exit(di); diff --git a/drivers/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c index 72f1bfea8d5415..54dd9d4ec39cd5 100644 --- a/drivers/power/supply/bq24257_charger.c +++ b/drivers/power/supply/bq24257_charger.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -1003,10 +1004,6 @@ static int bq24257_probe(struct i2c_client *client) if (bq->info->chip == BQ24250) bq->iilimit_autoset_enable = false; - if (bq->iilimit_autoset_enable) - INIT_DELAYED_WORK(&bq->iilimit_setup_work, - bq24257_iilimit_setup_work); - /* * The BQ24250 doesn't have a dedicated Power Good (PG) pin so let's * not probe for it and instead use a SW-based approach to determine @@ -1047,6 +1044,14 @@ static int bq24257_probe(struct i2c_client *client) return ret; } + if (bq->iilimit_autoset_enable) { + ret = devm_delayed_work_autocancel(dev, + &bq->iilimit_setup_work, + bq24257_iilimit_setup_work); + if (ret) + return ret; + } + ret = devm_request_threaded_irq(dev, client->irq, NULL, bq24257_irq_handler_thread, IRQF_TRIGGER_FALLING | @@ -1064,9 +1069,6 @@ static void bq24257_remove(struct i2c_client *client) { struct bq24257_device *bq = i2c_get_clientdata(client); - if (bq->iilimit_autoset_enable) - cancel_delayed_work_sync(&bq->iilimit_setup_work); - bq24257_field_write(bq, F_RESET, 1); /* reset to defaults */ } diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c index 5a6634fde83768..43e1da849df420 100644 --- a/drivers/power/supply/bq256xx_charger.c +++ b/drivers/power/supply/bq256xx_charger.c @@ -896,6 +896,8 @@ static void bq256xx_charger_reset(void *data) if (!IS_ERR_OR_NULL(bq->usb3_phy)) usb_unregister_notifier(bq->usb3_phy, &bq->usb_nb); + + cancel_work_sync(&bq->usb_work); } static int bq256xx_set_charger_property(struct power_supply *psy, @@ -1721,24 +1723,12 @@ static int bq256xx_probe(struct i2c_client *client) return ret; } - ret = devm_add_action_or_reset(dev, bq256xx_charger_reset, bq); - if (ret) - return ret; + INIT_WORK(&bq->usb_work, bq256xx_usb_work); + bq->usb_nb.notifier_call = bq256xx_usb_notifier; /* OTG reporting */ bq->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); - if (!IS_ERR_OR_NULL(bq->usb2_phy)) { - INIT_WORK(&bq->usb_work, bq256xx_usb_work); - bq->usb_nb.notifier_call = bq256xx_usb_notifier; - usb_register_notifier(bq->usb2_phy, &bq->usb_nb); - } - bq->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); - if (!IS_ERR_OR_NULL(bq->usb3_phy)) { - INIT_WORK(&bq->usb_work, bq256xx_usb_work); - bq->usb_nb.notifier_call = bq256xx_usb_notifier; - usb_register_notifier(bq->usb3_phy, &bq->usb_nb); - } ret = bq256xx_power_supply_init(bq, &psy_cfg, dev); if (ret) { @@ -1746,6 +1736,17 @@ static int bq256xx_probe(struct i2c_client *client) return ret; } + /* Register after the power supplies so devm runs it first. */ + ret = devm_add_action_or_reset(dev, bq256xx_charger_reset, bq); + if (ret) + return ret; + + if (!IS_ERR_OR_NULL(bq->usb2_phy)) + usb_register_notifier(bq->usb2_phy, &bq->usb_nb); + + if (!IS_ERR_OR_NULL(bq->usb3_phy)) + usb_register_notifier(bq->usb3_phy, &bq->usb_nb); + if (client->irq) { ret = devm_request_threaded_irq(dev, client->irq, NULL, bq256xx_irq_handler_thread, diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 180bc137a86325..741a45cc5f35dd 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -1389,6 +1389,14 @@ static int bq25890_fw_read_u32_props(struct bq25890_device *bq) return 0; } +static void bq25890_release_secondary_chrg(void *data) +{ + struct bq25890_device *bq = data; + + power_supply_put(bq->secondary_chrg); + bq->secondary_chrg = NULL; +} + static int bq25890_fw_probe(struct bq25890_device *bq) { int ret; @@ -1401,6 +1409,10 @@ static int bq25890_fw_probe(struct bq25890_device *bq) bq->secondary_chrg = power_supply_get_by_name(str); if (!bq->secondary_chrg) return -EPROBE_DEFER; + + ret = devm_add_action_or_reset(bq->dev, bq25890_release_secondary_chrg, bq); + if (ret) + return ret; } /* Optional, left at 0 if property is not present */ diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 71b6e49a835f83..fd4cd9cd2ce2aa 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1014,6 +1014,29 @@ static int charger_extcon_init(struct charger_manager *cm, return 0; } +static int charger_manager_get_regulators(struct charger_manager *cm) +{ + struct charger_desc *desc = cm->desc; + struct charger_regulator *charger; + int i, ret; + + for (i = 0; i < desc->num_charger_regulators; i++) { + charger = &desc->charger_regulators[i]; + charger->consumer = regulator_get(cm->dev, + charger->regulator_name); + if (IS_ERR(charger->consumer)) { + dev_err(cm->dev, "Cannot find charger(%s)\n", + charger->regulator_name); + ret = PTR_ERR(charger->consumer); + while (i-- > 0) + regulator_put(desc->charger_regulators[i].consumer); + return ret; + } + charger->cm = cm; + } + return 0; +} + /** * charger_manager_register_extcon - Register extcon device to receive state * of charger cable. @@ -1036,15 +1059,6 @@ static int charger_manager_register_extcon(struct charger_manager *cm) for (i = 0; i < desc->num_charger_regulators; i++) { charger = &desc->charger_regulators[i]; - charger->consumer = regulator_get(cm->dev, - charger->regulator_name); - if (IS_ERR(charger->consumer)) { - dev_err(cm->dev, "Cannot find charger(%s)\n", - charger->regulator_name); - return PTR_ERR(charger->consumer); - } - charger->cm = cm; - for (j = 0; j < charger->num_cables; j++) { struct charger_cable *cable = &charger->cables[j]; @@ -1580,13 +1594,23 @@ static int charger_manager_probe(struct platform_device *pdev) } psy_cfg.attr_grp = desc->sysfs_groups; + /* + * Acquire charger regulators before exposing the sysfs entries, so + * userspace cannot reach externally_control before the regulators + * (and charger->cm) are available. Mirrors the order in remove(). + */ + ret = charger_manager_get_regulators(cm); + if (ret < 0) + return ret; + cm->charger_psy = power_supply_register(&pdev->dev, &cm->charger_psy_desc, &psy_cfg); if (IS_ERR(cm->charger_psy)) { dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n", cm->charger_psy_desc.name); - return PTR_ERR(cm->charger_psy); + ret = PTR_ERR(cm->charger_psy); + goto err_regulator; } /* Register extcon device for charger cable */ @@ -1620,11 +1644,11 @@ static int charger_manager_probe(struct platform_device *pdev) return 0; err_reg_extcon: + power_supply_unregister(cm->charger_psy); +err_regulator: for (i = 0; i < desc->num_charger_regulators; i++) regulator_put(desc->charger_regulators[i].consumer); - power_supply_unregister(cm->charger_psy); - return ret; } @@ -1642,12 +1666,12 @@ static void charger_manager_remove(struct platform_device *pdev) cancel_work_sync(&setup_polling); cancel_delayed_work_sync(&cm_monitor_work); - for (i = 0 ; i < desc->num_charger_regulators ; i++) - regulator_put(desc->charger_regulators[i].consumer); + try_charger_enable(cm, false); power_supply_unregister(cm->charger_psy); - try_charger_enable(cm, false); + for (i = 0 ; i < desc->num_charger_regulators ; i++) + regulator_put(desc->charger_regulators[i].consumer); } static const struct platform_device_id charger_manager_id[] = { diff --git a/drivers/power/supply/cros_usbpd-charger.c b/drivers/power/supply/cros_usbpd-charger.c index c1cbe6e5476e98..69d227ceb5c9ec 100644 --- a/drivers/power/supply/cros_usbpd-charger.c +++ b/drivers/power/supply/cros_usbpd-charger.c @@ -124,6 +124,11 @@ static int cros_usbpd_charger_get_num_ports(struct charger_data *charger) if (ret < 0) return ret; + if (resp.port_count > EC_USB_PD_MAX_PORTS) { + dev_warn(charger->dev, "Charge port count out of bounds\n"); + return EC_USB_PD_MAX_PORTS; + } + return resp.port_count; } @@ -137,6 +142,11 @@ static int cros_usbpd_charger_get_usbpd_num_ports(struct charger_data *charger) if (ret < 0) return ret; + if (resp.num_ports > EC_USB_PD_MAX_PORTS) { + dev_warn(charger->dev, "USB PD port count out of bounds\n"); + return EC_USB_PD_MAX_PORTS; + } + return resp.num_ports; } @@ -588,10 +598,13 @@ static int cros_usbpd_charger_probe(struct platform_device *pd) /* * Sanity checks on the number of ports: - * there should be at most 1 dedicated port + * there should be at most 1 dedicated port, and the count must + * not exceed the maximum number of supported ports + * (EC_USB_PD_MAX_PORTS). */ if (charger->num_charger_ports < charger->num_usbpd_ports || - charger->num_charger_ports > (charger->num_usbpd_ports + 1)) { + charger->num_charger_ports > (charger->num_usbpd_ports + 1) || + charger->num_charger_ports > EC_USB_PD_MAX_PORTS) { dev_err(dev, "Unexpected number of charge port count\n"); ret = -EPROTO; goto fail_nowarn; diff --git a/drivers/power/supply/lp8727_charger.c b/drivers/power/supply/lp8727_charger.c index 641e0445a4109d..4537b7b413ddc2 100644 --- a/drivers/power/supply/lp8727_charger.c +++ b/drivers/power/supply/lp8727_charger.c @@ -280,10 +280,10 @@ static int lp8727_setup_irq(struct lp8727_chg *pchg) static void lp8727_release_irq(struct lp8727_chg *pchg) { - cancel_delayed_work_sync(&pchg->work); - if (pchg->irq) free_irq(pchg->irq, pchg); + + cancel_delayed_work_sync(&pchg->work); } static enum power_supply_property lp8727_charger_prop[] = { diff --git a/drivers/power/supply/lp8788-charger.c b/drivers/power/supply/lp8788-charger.c index f0a680c155c485..1bc2c150fb9127 100644 --- a/drivers/power/supply/lp8788-charger.c +++ b/drivers/power/supply/lp8788-charger.c @@ -710,8 +710,8 @@ static void lp8788_charger_remove(struct platform_device *pdev) { struct lp8788_charger *pchg = platform_get_drvdata(pdev); - flush_work(&pchg->charger_work); lp8788_irq_unregister(pdev, pchg); + cancel_work_sync(&pchg->charger_work); } static struct platform_driver lp8788_charger_driver = { diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 03ac569e185c54..41744191a3638d 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -192,8 +192,11 @@ static int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell) static int max17040_get_vcell(struct max17040_chip *chip) { u32 vcell; + int ret; - regmap_read(chip->regmap, MAX17040_VCELL, &vcell); + ret = regmap_read(chip->regmap, MAX17040_VCELL, &vcell); + if (ret) + return ret; return max17040_raw_vcell_to_uvolts(chip, vcell); } @@ -201,8 +204,11 @@ static int max17040_get_vcell(struct max17040_chip *chip) static int max17040_get_soc(struct max17040_chip *chip) { u32 soc; + int ret; - regmap_read(chip->regmap, MAX17040_SOC, &soc); + ret = regmap_read(chip->regmap, MAX17040_SOC, &soc); + if (ret) + return ret; return soc >> (chip->quirk_double_soc ? 9 : 8); } @@ -261,7 +267,11 @@ static int max17040_get_of_data(struct max17040_chip *chip) static void max17040_check_changes(struct max17040_chip *chip) { - chip->soc = max17040_get_soc(chip); + int soc; + + soc = max17040_get_soc(chip); + if (soc >= 0) + chip->soc = soc; } static void max17040_queue_work(struct max17040_chip *chip) @@ -396,10 +406,16 @@ static int max17040_get_property(struct power_supply *psy, val->intval = max17040_get_online(chip); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = max17040_get_vcell(chip); + ret = max17040_get_vcell(chip); + if (ret < 0) + return ret; + val->intval = ret; break; case POWER_SUPPLY_PROP_CAPACITY: - val->intval = max17040_get_soc(chip); + ret = max17040_get_soc(chip); + if (ret < 0) + return ret; + val->intval = ret; break; case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: val->intval = chip->low_soc_alert; @@ -458,16 +474,12 @@ static const struct power_supply_desc max17040_battery_desc = { static int max17040_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); - struct i2c_adapter *adapter = client->adapter; struct power_supply_config psy_cfg = {}; struct max17040_chip *chip; enum chip_id chip_id; bool enable_irq = false; int ret; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) - return -EIO; - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; @@ -568,7 +580,7 @@ static int max17040_suspend(struct device *dev) // disable soc alert to prevent wakeup max17040_set_soc_alert(chip, 0); else - cancel_delayed_work(&chip->work); + cancel_delayed_work_sync(&chip->work); if (client->irq && device_may_wakeup(dev)) enable_irq_wake(client->irq); diff --git a/drivers/power/supply/pf1550-charger.c b/drivers/power/supply/pf1550-charger.c index 41036f4cb64a0a..38cac156d2e683 100644 --- a/drivers/power/supply/pf1550-charger.c +++ b/drivers/power/supply/pf1550-charger.c @@ -514,7 +514,7 @@ static int pf1550_reg_init(struct pf1550_charger *chg) * a battery. The other supported mode is mode 2, the charger is turned * on to charge a battery when present. */ - if (power_supply_get_battery_info(chg->charger, &info)) { + if (!power_supply_get_battery_info(chg->charger, &info)) { ret = regmap_write(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_OPER, PF1550_CHG_BAT_ON); diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c index d282d969bfe924..2915d161760441 100644 --- a/drivers/power/supply/qcom_battmgr.c +++ b/drivers/power/supply/qcom_battmgr.c @@ -5,6 +5,7 @@ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include +#include #include #include #include @@ -1235,7 +1236,7 @@ static void qcom_battmgr_sc8280xp_strcpy(char *dest, const char *src) memcpy(dest, src + 1, len); dest[len] = '\0'; } else { - memcpy(dest, src, BATTMGR_STRING_LEN); + strscpy(dest, src, BATTMGR_STRING_LEN); } } @@ -1652,7 +1653,6 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev, psy_cfg_supply.supplied_to = qcom_battmgr_battery; psy_cfg_supply.num_supplicants = 1; - INIT_WORK(&battmgr->enable_work, qcom_battmgr_enable_worker); mutex_init(&battmgr->lock); init_completion(&battmgr->ack); @@ -1715,6 +1715,11 @@ static int qcom_battmgr_probe(struct auxiliary_device *adev, "failed to register wireless charing power supply\n"); } + ret = devm_work_autocancel(dev, &battmgr->enable_work, + qcom_battmgr_enable_worker); + if (ret) + return ret; + battmgr->client = devm_pmic_glink_client_alloc(dev, PMIC_GLINK_OWNER_BATTMGR, qcom_battmgr_callback, qcom_battmgr_pdr_notify, diff --git a/drivers/power/supply/rt9455_charger.c b/drivers/power/supply/rt9455_charger.c index 7045d2908148be..515bb3b2d67a42 100644 --- a/drivers/power/supply/rt9455_charger.c +++ b/drivers/power/supply/rt9455_charger.c @@ -1582,6 +1582,19 @@ static const struct regmap_config rt9455_regmap_config = { .cache_type = REGCACHE_MAPLE, }; +static void rt9455_cancel_all_delayed_works(void *data) +{ + struct rt9455_info *info = data; + + /* + * Both pwr_rdy_work and batt_presence_work can queue + * max_charging_time_work, so cancel them first. + */ + cancel_delayed_work_sync(&info->pwr_rdy_work); + cancel_delayed_work_sync(&info->batt_presence_work); + cancel_delayed_work_sync(&info->max_charging_time_work); +} + static int rt9455_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; @@ -1672,6 +1685,10 @@ static int rt9455_probe(struct i2c_client *client) goto put_usb_notifier; } + ret = devm_add_action_or_reset(dev, rt9455_cancel_all_delayed_works, info); + if (ret) + goto put_usb_notifier; + ret = devm_request_threaded_irq(dev, client->irq, NULL, rt9455_irq_handler_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, @@ -1712,10 +1729,6 @@ static void rt9455_remove(struct i2c_client *client) if (info->nb.notifier_call) usb_unregister_notifier(info->usb_phy, &info->nb); #endif - - cancel_delayed_work_sync(&info->pwr_rdy_work); - cancel_delayed_work_sync(&info->max_charging_time_work); - cancel_delayed_work_sync(&info->batt_presence_work); } static const struct i2c_device_id rt9455_i2c_id_table[] = { diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c index 04216b2bfb6c35..e5b4d72720de54 100644 --- a/drivers/power/supply/twl4030_charger.c +++ b/drivers/power/supply/twl4030_charger.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1002,8 +1003,15 @@ static int twl4030_bci_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bci); - INIT_WORK(&bci->work, twl4030_bci_usb_work); - INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker); + ret = devm_delayed_work_autocancel(&pdev->dev, &bci->current_worker, + twl4030_current_worker); + if (ret) + return ret; + + ret = devm_work_autocancel(&pdev->dev, &bci->work, + twl4030_bci_usb_work); + if (ret) + return ret; bci->channel_vac = devm_iio_channel_get(&pdev->dev, "vac"); if (IS_ERR(bci->channel_vac)) { diff --git a/drivers/power/supply/ucs1002_power.c b/drivers/power/supply/ucs1002_power.c index 3f44cc902f84da..ca58c214e93e39 100644 --- a/drivers/power/supply/ucs1002_power.c +++ b/drivers/power/supply/ucs1002_power.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -640,7 +641,10 @@ static int ucs1002_probe(struct i2c_client *client) } info->health = POWER_SUPPLY_HEALTH_GOOD; - INIT_DELAYED_WORK(&info->health_poll, ucs1002_health_poll); + ret = devm_delayed_work_autocancel(dev, &info->health_poll, + ucs1002_health_poll); + if (ret) + return ret; if (irq_a_det > 0) { ret = devm_request_threaded_irq(dev, irq_a_det, NULL, diff --git a/drivers/ptp/ptp_vmclock.c b/drivers/ptp/ptp_vmclock.c index bb0e14bac9f2ab..8ba14bbec15e4d 100644 --- a/drivers/ptp/ptp_vmclock.c +++ b/drivers/ptp/ptp_vmclock.c @@ -378,6 +378,12 @@ static int vmclock_miscdev_mmap(struct file *fp, struct vm_area_struct *vma) */ vm_flags_clear(vma, VM_MAYWRITE); + /* + * Restrict the read-only mapping so it cannot be upgraded to + * writable later with mprotect(). + */ + vm_flags_clear(vma, VM_MAYWRITE); + if (vma->vm_end - vma->vm_start != PAGE_SIZE || vma->vm_pgoff) return -EINVAL; diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c index 009b3b595bbf54..ad82c2108a567d 100644 --- a/drivers/rapidio/devices/rio_mport_cdev.c +++ b/drivers/rapidio/devices/rio_mport_cdev.c @@ -564,9 +564,13 @@ static void dma_req_free(struct kref *ref) } if (req->map) { - mutex_lock(&req->map->md->buf_mutex); - kref_put(&req->map->ref, mport_release_mapping); - mutex_unlock(&req->map->md->buf_mutex); + struct rio_mport_mapping *map = req->map; + struct mport_dev *md = map->md; + + mutex_lock(&md->buf_mutex); + req->map = NULL; + kref_put(&map->ref, mport_release_mapping); + mutex_unlock(&md->buf_mutex); } kref_put(&priv->dma_ref, mport_release_dma); diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c index da378bfdba4066..85e2aaa1a06bd7 100644 --- a/drivers/regulator/as3722-regulator.c +++ b/drivers/regulator/as3722-regulator.c @@ -600,7 +600,6 @@ static int as3722_get_regulator_dt_data(struct platform_device *pdev, ret = of_regulator_match(&pdev->dev, np, as3722_regulator_matches, ARRAY_SIZE(as3722_regulator_matches)); - of_node_put(np); if (ret < 0) { dev_err(&pdev->dev, "Parsing of regulator node failed: %d\n", ret); diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index cc85fbe8b77c37..90651f4d18281d 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -582,7 +582,6 @@ static int max8998_pmic_dt_parse_pdata(struct max8998_dev *iodev, } pdata->num_regulators = rdata - pdata->regulators; - of_node_put(reg_np); of_node_put(regulators_np); pdata->buck_voltage_lock = of_property_read_bool(pmic_np, "max8998,pmic-buck-voltage-lock"); diff --git a/drivers/regulator/qcom-refgen-regulator.c b/drivers/regulator/qcom-refgen-regulator.c index 299ac3c8c3bc3d..6a3795469927ec 100644 --- a/drivers/regulator/qcom-refgen-regulator.c +++ b/drivers/regulator/qcom-refgen-regulator.c @@ -66,7 +66,7 @@ static const struct regulator_desc sdm845_refgen_desc = { .enable_time = 5, .name = "refgen", .owner = THIS_MODULE, - .type = REGULATOR_VOLTAGE, + .type = REGULATOR_CURRENT, .ops = &(const struct regulator_ops) { .enable = qcom_sdm845_refgen_enable, .disable = qcom_sdm845_refgen_disable, @@ -82,7 +82,7 @@ static const struct regulator_desc sm8250_refgen_desc = { .enable_time = 5, .name = "refgen", .owner = THIS_MODULE, - .type = REGULATOR_VOLTAGE, + .type = REGULATOR_CURRENT, .ops = &(const struct regulator_ops) { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index 85a74c9ec52160..436656bdfa8be3 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -36,6 +36,7 @@ struct mtk_scp *scp_get(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *scp_node; struct platform_device *scp_pdev; + struct mtk_scp *scp; scp_node = of_parse_phandle(dev->of_node, "mediatek,scp", 0); if (!scp_node) { @@ -51,7 +52,13 @@ struct mtk_scp *scp_get(struct platform_device *pdev) return NULL; } - return platform_get_drvdata(scp_pdev); + scp = platform_get_drvdata(scp_pdev); + if (!scp) { + put_device(&scp_pdev->dev); + return NULL; + } + + return scp; } EXPORT_SYMBOL_GPL(scp_get); diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 89b134b331fba4..3b2f65f2194d79 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -243,17 +243,14 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) ret = qcom_mdt_pas_load(pas->dtb_pas_ctx, pas->dtb_firmware, pas->dtb_firmware_name, pas->dtb_mem_region, &pas->dtb_mem_reloc); - if (ret) - goto release_dtb_metadata; + if (ret) { + qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); + release_firmware(pas->dtb_firmware); + return ret; + } } return 0; - -release_dtb_metadata: - qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); - release_firmware(pas->dtb_firmware); - - return ret; } static void qcom_pas_unmap_carveout(struct rproc *rproc, phys_addr_t mem_phys, size_t size) diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c index 62adc4db23170a..35bb03e67ae8a1 100644 --- a/drivers/rpmsg/qcom_glink_smem.c +++ b/drivers/rpmsg/qcom_glink_smem.c @@ -103,6 +103,13 @@ static void glink_smem_rx_peek(struct qcom_glink_pipe *np, if (tail >= pipe->native.length) tail -= pipe->native.length; + /* + * Order the availability (head) read in glink_smem_rx_avail() + * against the FIFO payload read below, so APPS never consumes + * stale data the remote has not yet published. + */ + rmb(); + len = min_t(size_t, count, pipe->native.length - tail); if (len) memcpy_fromio(data, pipe->fifo + tail, len); diff --git a/drivers/rtc/rtc-rzn1.c b/drivers/rtc/rtc-rzn1.c index 305f10a8a85b65..919ed0fb1e2309 100644 --- a/drivers/rtc/rtc-rzn1.c +++ b/drivers/rtc/rtc-rzn1.c @@ -234,13 +234,24 @@ static int rzn1_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) if (ret) return ret; + ctl1 = readl(rtc->base + RZN1_RTC_CTL1); + alrm->enabled = !!(ctl1 & (RZN1_RTC_CTL1_ALME | RZN1_RTC_CTL1_1SE)); + min = readl(rtc->base + RZN1_RTC_ALM); hour = readl(rtc->base + RZN1_RTC_ALH); - wday = readl(rtc->base + RZN1_RTC_ALW); tm->tm_sec = 0; tm->tm_min = bcd2bin(min); tm->tm_hour = bcd2bin(hour); + + /* + * If wday is zero, no bit is set in RZN1_RTC_ALW. This is the + * register's power-on reset value. + */ + wday = readl(rtc->base + RZN1_RTC_ALW); + if (!wday) + return 0; + delta_days = ((fls(wday) - 1) - tm->tm_wday + 7) % 7; tm->tm_wday = fls(wday) - 1; @@ -249,9 +260,6 @@ static int rzn1_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) rtc_time64_to_tm(alarm, tm); } - ctl1 = readl(rtc->base + RZN1_RTC_CTL1); - alrm->enabled = !!(ctl1 & (RZN1_RTC_CTL1_ALME | RZN1_RTC_CTL1_1SE)); - return 0; } @@ -260,7 +268,6 @@ static int rzn1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) struct rzn1_rtc *rtc = dev_get_drvdata(dev); struct rtc_time *tm = &alrm->time, tm_now; unsigned long alarm, farest; - unsigned int days_ahead, wday; int ret; ret = rzn1_rtc_read_time(dev, &tm_now); @@ -273,13 +280,14 @@ static int rzn1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) if (time_after(alarm, farest)) return -ERANGE; - /* Convert alarm day into week day */ - days_ahead = tm->tm_mday - tm_now.tm_mday; - wday = (tm_now.tm_wday + days_ahead) % 7; + /* Disable alarm interrupts before reprogramming the alarm. */ + ret = rzn1_rtc_alarm_irq_enable(dev, 0); + if (ret) + return ret; writel(bin2bcd(tm->tm_min), rtc->base + RZN1_RTC_ALM); writel(bin2bcd(tm->tm_hour), rtc->base + RZN1_RTC_ALH); - writel(BIT(wday), rtc->base + RZN1_RTC_ALW); + writel(BIT(tm->tm_wday), rtc->base + RZN1_RTC_ALW); rtc->tm_alarm = alrm->time; @@ -464,6 +472,10 @@ static int rzn1_rtc_probe(struct platform_device *pdev) } irq = platform_get_irq_byname_optional(pdev, "pps"); + if (irq == -EPROBE_DEFER) { + ret = irq; + goto dis_runtime_pm; + } if (irq >= 0) ret = devm_request_irq(&pdev->dev, irq, rzn1_rtc_1s_irq, 0, "RZN1 RTC 1s", rtc); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index d8d912a3b3fecc..56ef38243f8235 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1698,8 +1698,10 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, return; } if (rq_data_dir(req) == READ) { - device->discipline->ese_read(cqr, irb); - cqr->status = DASD_CQR_SUCCESS; + if (device->discipline->ese_read(cqr, irb)) + cqr->status = DASD_CQR_ERROR; + else + cqr->status = DASD_CQR_SUCCESS; cqr->stopclk = now; dasd_device_clear_timer(device); dasd_schedule_device_bh(device); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index d356a9f8f016fd..b64ca714b53e7c 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1492,6 +1492,8 @@ static void dasd_eckd_reset_path(struct dasd_device *device, __u8 pm) struct dasd_eckd_private *private = device->private; unsigned long flags; + if (!private) + return; if (!private->fcx_max_data) private->fcx_max_data = get_fcx_max_data(device); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); @@ -1647,6 +1649,9 @@ static int dasd_eckd_is_ese(struct dasd_device *device) { struct dasd_eckd_private *private = device->private; + if (!private) + return 0; + return private->vsq.vol_info.ese; } @@ -1654,6 +1659,9 @@ static int dasd_eckd_ext_pool_id(struct dasd_device *device) { struct dasd_eckd_private *private = device->private; + if (!private) + return 0; + return private->vsq.extent_pool_id; } @@ -1667,6 +1675,9 @@ static int dasd_eckd_space_configured(struct dasd_device *device) struct dasd_eckd_private *private = device->private; int rc; + if (!private) + return 0; + rc = dasd_eckd_read_vol_info(device); return rc ? : private->vsq.space_configured; @@ -1681,6 +1692,9 @@ static int dasd_eckd_space_allocated(struct dasd_device *device) struct dasd_eckd_private *private = device->private; int rc; + if (!private) + return 0; + rc = dasd_eckd_read_vol_info(device); return rc ? : private->vsq.space_allocated; @@ -1690,6 +1704,9 @@ static int dasd_eckd_logical_capacity(struct dasd_device *device) { struct dasd_eckd_private *private = device->private; + if (!private) + return 0; + return private->vsq.logical_capacity; } @@ -1832,7 +1849,11 @@ static int dasd_eckd_read_ext_pool_info(struct dasd_device *device) static int dasd_eckd_ext_size(struct dasd_device *device) { struct dasd_eckd_private *private = device->private; - struct dasd_ext_pool_sum eps = private->eps; + struct dasd_ext_pool_sum eps; + + if (!private) + return 0; + eps = private->eps; if (!eps.flags.extent_size_valid) return 0; @@ -1848,6 +1869,9 @@ static int dasd_eckd_ext_pool_warn_thrshld(struct dasd_device *device) { struct dasd_eckd_private *private = device->private; + if (!private) + return 0; + return private->eps.warn_thrshld; } @@ -1855,6 +1879,9 @@ static int dasd_eckd_ext_pool_cap_at_warnlevel(struct dasd_device *device) { struct dasd_eckd_private *private = device->private; + if (!private) + return 0; + return private->eps.flags.capacity_at_warnlevel; } @@ -1865,6 +1892,9 @@ static int dasd_eckd_ext_pool_oos(struct dasd_device *device) { struct dasd_eckd_private *private = device->private; + if (!private) + return 0; + return private->eps.flags.pool_oos; } @@ -5938,8 +5968,11 @@ static int dasd_eckd_query_host_access(struct dasd_device *device, struct ccw1 *ccw; int rc; + if (!private) + return -ENODEV; + /* not available for HYPER PAV alias devices */ - if (!device->block && private->lcu->pav == HYPER_PAV) + if (!device->block && private->lcu && private->lcu->pav == HYPER_PAV) return -EOPNOTSUPP; /* may not be supported by the storage server */ @@ -6804,6 +6837,9 @@ static int dasd_eckd_hpf_enabled(struct dasd_device *device) { struct dasd_eckd_private *private = device->private; + if (!private) + return 0; + return private->fcx_max_data ? 1 : 0; } diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index 89d7516b9ec86f..468f0b2cc34233 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -123,6 +123,7 @@ struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr) int success; unsigned long startclk, stopclk; struct dasd_device *startdev; + unsigned int proc_bytes; BUG_ON(cqr->refers == NULL || cqr->function == NULL); @@ -130,6 +131,7 @@ struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr) startclk = cqr->startclk; stopclk = cqr->stopclk; startdev = cqr->startdev; + proc_bytes = cqr->proc_bytes; /* free all ERPs - but NOT the original cqr */ while (cqr->refers != NULL) { @@ -147,6 +149,7 @@ struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr) cqr->startclk = startclk; cqr->stopclk = stopclk; cqr->startdev = startdev; + cqr->proc_bytes = proc_bytes; if (success) cqr->status = DASD_CQR_DONE; else { diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index 44b3a1dcc1b365..940c0ff668beef 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -48,15 +48,19 @@ static void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q); * 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM * guest's APCB. * 2. kvm->lock: required to update a guest's APCB - * 3. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev + * 3. kvm->arch.crypto.pqap_hook_rwsem: required to update pqap_hook and + * serialize against PQAP intercepts + * 4. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev * - * Note: If @kvm is NULL, the KVM lock will not be taken. + * Note: If @kvm is NULL, the KVM lock and pqap_hook_rwsem will not be taken. */ static inline void get_update_locks_for_kvm(struct kvm *kvm) { mutex_lock(&matrix_dev->guests_lock); - if (kvm) + if (kvm) { mutex_lock(&kvm->lock); + down_write(&kvm->arch.crypto.pqap_hook_rwsem); + } mutex_lock(&matrix_dev->mdevs_lock); } @@ -68,16 +72,19 @@ static inline void get_update_locks_for_kvm(struct kvm *kvm) * * The proper unlocking order is: * 1. matrix_dev->mdevs_lock - * 2. kvm->lock - * 3. matrix_dev->guests_lock + * 2. kvm->arch.crypto.pqap_hook_rwsem + * 3. kvm->lock + * 4. matrix_dev->guests_lock * - * Note: If @kvm is NULL, the KVM lock will not be released. + * Note: If @kvm is NULL, the KVM lock and pqap_hook_rwsem will not be released. */ static inline void release_update_locks_for_kvm(struct kvm *kvm) { mutex_unlock(&matrix_dev->mdevs_lock); - if (kvm) + if (kvm) { + up_write(&kvm->arch.crypto.pqap_hook_rwsem); mutex_unlock(&kvm->lock); + } mutex_unlock(&matrix_dev->guests_lock); } @@ -800,12 +807,17 @@ static int vfio_ap_mdev_probe(struct mdev_device *mdev) ret = vfio_register_emulated_iommu_dev(&matrix_mdev->vdev); if (ret) goto err_put_vdev; - matrix_mdev->req_trigger = NULL; - matrix_mdev->cfg_chg_trigger = NULL; + + /* + * Take the matrix_dev->guests_lock mutex before adding the matrix_mdev + * to the mdev_list. All functions that traverse the list must also hold + * this lock to guard against additions to or removals from the list + * while it is being traversed. + */ + mutex_lock(&matrix_dev->guests_lock); dev_set_drvdata(&mdev->dev, matrix_mdev); - mutex_lock(&matrix_dev->mdevs_lock); list_add(&matrix_mdev->node, &matrix_dev->mdev_list); - mutex_unlock(&matrix_dev->mdevs_lock); + mutex_unlock(&matrix_dev->guests_lock); return 0; err_put_vdev: @@ -1821,26 +1833,17 @@ static const struct attribute_group *vfio_ap_mdev_attr_groups[] = { static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev, struct kvm *kvm) { - struct ap_matrix_mdev *m; - if (kvm->arch.crypto.crycbd) { - down_write(&kvm->arch.crypto.pqap_hook_rwsem); - kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook; - up_write(&kvm->arch.crypto.pqap_hook_rwsem); - get_update_locks_for_kvm(kvm); - - list_for_each_entry(m, &matrix_dev->mdev_list, node) { - if (m != matrix_mdev && m->kvm == kvm) { - release_update_locks_for_kvm(kvm); - return -EPERM; - } + if (kvm->arch.crypto.pqap_hook) { + release_update_locks_for_kvm(kvm); + return -EPERM; } + kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook; kvm_get_kvm(kvm); matrix_mdev->kvm = kvm; vfio_ap_mdev_update_guest_apcb(matrix_mdev); - release_update_locks_for_kvm(kvm); } @@ -1883,18 +1886,15 @@ static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev) struct kvm *kvm = matrix_mdev->kvm; if (kvm && kvm->arch.crypto.crycbd) { - down_write(&kvm->arch.crypto.pqap_hook_rwsem); - kvm->arch.crypto.pqap_hook = NULL; - up_write(&kvm->arch.crypto.pqap_hook_rwsem); - get_update_locks_for_kvm(kvm); + kvm->arch.crypto.pqap_hook = NULL; kvm_arch_crypto_clear_masks(kvm); vfio_ap_mdev_reset_queues(matrix_mdev); - kvm_put_kvm(kvm); matrix_mdev->kvm = NULL; release_update_locks_for_kvm(kvm); + kvm_put_kvm(kvm); } } @@ -2297,6 +2297,8 @@ static struct ap_matrix_mdev *vfio_ap_mdev_for_queue(struct vfio_ap_queue *q) unsigned long apid = AP_QID_CARD(q->apqn); unsigned long apqi = AP_QID_QUEUE(q->apqn); + lockdep_assert_held(&matrix_dev->guests_lock); + list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) { if (test_bit_inv(apid, matrix_mdev->matrix.apm) && test_bit_inv(apqi, matrix_mdev->matrix.aqm)) @@ -2316,8 +2318,26 @@ static ssize_t status_show(struct device *dev, struct ap_matrix_mdev *matrix_mdev; struct ap_device *apdev = to_ap_dev(dev); + mutex_lock(&matrix_dev->guests_lock); mutex_lock(&matrix_dev->mdevs_lock); q = dev_get_drvdata(&apdev->device); + + /* + * Make sure the drvdata has been set before proceeding. There is a + * possibility that the drvdata was not set if the vfio_ap_queue object + * could not be allocated when the queue device was probed. In that case, + * the locks used in vfio_ap_mdev_probe_queue() are released prior to + * removing the sysfs status attribute to avoid a lockdep + * splat. That opens a very small window where the status attribute is + * still available without the vfio_ap_queue object having been + * stored in the device drvdata. In that case, indicate the queue is not + * assigned. + */ + if (!q) { + nchars = sysfs_emit(buf, "%s\n", AP_QUEUE_UNASSIGNED); + goto done; + } + matrix_mdev = vfio_ap_mdev_for_queue(q); /* If the queue is assigned to the matrix mediated device, then @@ -2342,7 +2362,9 @@ static ssize_t status_show(struct device *dev, nchars = sysfs_emit(buf, "%s\n", AP_QUEUE_UNASSIGNED); } +done: mutex_unlock(&matrix_dev->mdevs_lock); + mutex_unlock(&matrix_dev->guests_lock); return nchars; } @@ -2415,14 +2437,17 @@ void vfio_ap_mdev_unregister(void) int vfio_ap_mdev_probe_queue(struct ap_device *apdev) { - int ret; + int ret, apqn; struct vfio_ap_queue *q; DECLARE_BITMAP(apm_filtered, AP_DEVICES); struct ap_matrix_mdev *matrix_mdev; + apqn = to_ap_queue(&apdev->device)->qid; + matrix_mdev = get_update_locks_by_apqn(apqn); + ret = sysfs_create_group(&apdev->device.kobj, &vfio_queue_attr_group); if (ret) - return ret; + goto err_release_locks; q = kzalloc_obj(*q); if (!q) { @@ -2430,11 +2455,10 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev) goto err_remove_group; } - q->apqn = to_ap_queue(&apdev->device)->qid; + q->apqn = apqn; q->saved_isc = VFIO_AP_ISC_INVALID; memset(&q->reset_status, 0, sizeof(q->reset_status)); INIT_WORK(&q->reset_work, apq_reset_check); - matrix_mdev = get_update_locks_by_apqn(q->apqn); if (matrix_mdev) { vfio_ap_mdev_link_queue(matrix_mdev, q); @@ -2463,8 +2487,13 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev) return ret; err_remove_group: + release_update_locks_for_mdev(matrix_mdev); sysfs_remove_group(&apdev->device.kobj, &vfio_queue_attr_group); return ret; + +err_release_locks: + release_update_locks_for_mdev(matrix_mdev); + return ret; } void vfio_ap_mdev_remove_queue(struct ap_device *apdev) @@ -2559,24 +2588,28 @@ static void vfio_ap_mdev_hot_unplug_cfg(struct ap_matrix_mdev *matrix_mdev, unsigned long *aqrem, unsigned long *cdrem) { - int do_hotplug = 0; + bool do_hotplug = false; - if (!bitmap_empty(aprem, AP_DEVICES)) { - do_hotplug |= bitmap_andnot(matrix_mdev->shadow_apcb.apm, - matrix_mdev->shadow_apcb.apm, - aprem, AP_DEVICES); + if (bitmap_intersects(matrix_mdev->shadow_apcb.apm, aprem, AP_DEVICES)) { + bitmap_andnot(matrix_mdev->shadow_apcb.apm, + matrix_mdev->shadow_apcb.apm, + aprem, AP_DEVICES); + do_hotplug = true; } - if (!bitmap_empty(aqrem, AP_DOMAINS)) { - do_hotplug |= bitmap_andnot(matrix_mdev->shadow_apcb.aqm, - matrix_mdev->shadow_apcb.aqm, - aqrem, AP_DEVICES); + if (bitmap_intersects(matrix_mdev->shadow_apcb.aqm, aqrem, AP_DOMAINS)) { + bitmap_andnot(matrix_mdev->shadow_apcb.aqm, + matrix_mdev->shadow_apcb.aqm, + aqrem, AP_DOMAINS); + do_hotplug = true; } - if (!bitmap_empty(cdrem, AP_DOMAINS)) - do_hotplug |= bitmap_andnot(matrix_mdev->shadow_apcb.adm, - matrix_mdev->shadow_apcb.adm, - cdrem, AP_DOMAINS); + if (bitmap_intersects(matrix_mdev->shadow_apcb.adm, cdrem, AP_DOMAINS)) { + bitmap_andnot(matrix_mdev->shadow_apcb.adm, + matrix_mdev->shadow_apcb.adm, + cdrem, AP_DOMAINS); + do_hotplug = true; + } if (do_hotplug) vfio_ap_mdev_update_guest_apcb(matrix_mdev); @@ -2603,28 +2636,41 @@ static void vfio_ap_mdev_cfg_remove(unsigned long *ap_remove, DECLARE_BITMAP(aprem, AP_DEVICES); DECLARE_BITMAP(aqrem, AP_DOMAINS); DECLARE_BITMAP(cdrem, AP_DOMAINS); - int do_remove = 0; + int do_remove; + /* + * It is safe to traverse this list here because the + * required guard - matrix_dev->guests_lock - is taken in the + * vfio_ap_on_cfg_changed function prior to this function getting + * called. + */ list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) { - mutex_lock(&matrix_mdev->kvm->lock); + /* + * The mdevs_lock must be held to access fields within matrix_mdev, + * and kvm->lock must be taken before mdevs_lock to satisfy the lock + * ordering requirement and prevent a lockdep splat. + */ + if (matrix_mdev->kvm) + mutex_lock(&matrix_mdev->kvm->lock); mutex_lock(&matrix_dev->mdevs_lock); - do_remove |= bitmap_and(aprem, ap_remove, - matrix_mdev->matrix.apm, - AP_DEVICES); + do_remove = bitmap_and(aprem, ap_remove, + matrix_mdev->matrix.apm, + AP_DEVICES); do_remove |= bitmap_and(aqrem, aq_remove, matrix_mdev->matrix.aqm, AP_DOMAINS); - do_remove |= bitmap_andnot(cdrem, cd_remove, - matrix_mdev->matrix.adm, - AP_DOMAINS); + do_remove |= bitmap_and(cdrem, cd_remove, + matrix_mdev->matrix.adm, + AP_DOMAINS); if (do_remove) vfio_ap_mdev_hot_unplug_cfg(matrix_mdev, aprem, aqrem, cdrem); mutex_unlock(&matrix_dev->mdevs_lock); - mutex_unlock(&matrix_mdev->kvm->lock); + if (matrix_mdev->kvm) + mutex_unlock(&matrix_mdev->kvm->lock); } } @@ -2748,13 +2794,27 @@ static void vfio_ap_mdev_cfg_add(unsigned long *apm_add, unsigned long *aqm_add, vfio_ap_filter_apid_by_qtype(apm_add, aqm_add); + /* + * It is safe to traverse this list here because the + * required guard - matrix_dev->guests_lock - is taken in the + * vfio_ap_on_cfg_changed function prior to this function getting + * called. + */ list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) { + /* + * The mdevs_lock must be held in order to access fields + * within matrix_mdev + */ + mutex_lock(&matrix_dev->mdevs_lock); + bitmap_and(matrix_mdev->apm_add, matrix_mdev->matrix.apm, apm_add, AP_DEVICES); bitmap_and(matrix_mdev->aqm_add, matrix_mdev->matrix.aqm, aqm_add, AP_DOMAINS); bitmap_and(matrix_mdev->adm_add, matrix_mdev->matrix.adm, adm_add, AP_DEVICES); + + mutex_unlock(&matrix_dev->mdevs_lock); } } @@ -2807,6 +2867,10 @@ void vfio_ap_on_cfg_changed(struct ap_config_info *cur_cfg_info, if (!cur_cfg_info || !prev_cfg_info) return; + /* + * Take the guests_lock mutex here to guard access to the + * matrix_dev->mdev_list in the two functions called below. + */ mutex_lock(&matrix_dev->guests_lock); vfio_ap_mdev_on_cfg_remove(cur_cfg_info, prev_cfg_info); @@ -2821,8 +2885,14 @@ static void vfio_ap_mdev_hot_plug_cfg(struct ap_matrix_mdev *matrix_mdev) DECLARE_BITMAP(apm_filtered, AP_DEVICES); bool filter_domains, filter_adapters, filter_cdoms, do_hotplug = false; - mutex_lock(&matrix_mdev->kvm->lock); - mutex_lock(&matrix_dev->mdevs_lock); + /* + * Zero out the apm_filtered bitmap in case there are no adapters or + * domains to be added, but only control domains. In that case, + * vfio_ap_mdev_filter_matrix() - which initializes apm_filtered - will + * not get called and the reset_queues_for_apids will crash because it + * will access an uninitialized bitmap. + */ + bitmap_zero(apm_filtered, AP_DEVICES); filter_adapters = bitmap_intersects(matrix_mdev->matrix.apm, matrix_mdev->apm_add, AP_DEVICES); @@ -2841,9 +2911,6 @@ static void vfio_ap_mdev_hot_plug_cfg(struct ap_matrix_mdev *matrix_mdev) vfio_ap_mdev_update_guest_apcb(matrix_mdev); reset_queues_for_apids(matrix_mdev, apm_filtered); - - mutex_unlock(&matrix_dev->mdevs_lock); - mutex_unlock(&matrix_mdev->kvm->lock); } void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info, @@ -2854,15 +2921,29 @@ void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info, mutex_lock(&matrix_dev->guests_lock); list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) { + /* + * The mdevs_lock must be held to access fields within matrix_mdev, + * and kvm->lock must be taken before mdevs_lock to satisfy the lock + * ordering requirement and prevent a lockdep splat. + */ + if (matrix_mdev->kvm) + mutex_lock(&matrix_mdev->kvm->lock); + mutex_lock(&matrix_dev->mdevs_lock); + if (bitmap_empty(matrix_mdev->apm_add, AP_DEVICES) && bitmap_empty(matrix_mdev->aqm_add, AP_DOMAINS) && bitmap_empty(matrix_mdev->adm_add, AP_DOMAINS)) - continue; + goto do_unlock; vfio_ap_mdev_hot_plug_cfg(matrix_mdev); bitmap_clear(matrix_mdev->apm_add, 0, AP_DEVICES); bitmap_clear(matrix_mdev->aqm_add, 0, AP_DOMAINS); bitmap_clear(matrix_mdev->adm_add, 0, AP_DOMAINS); + +do_unlock: + mutex_unlock(&matrix_dev->mdevs_lock); + if (matrix_mdev->kvm) + mutex_unlock(&matrix_mdev->kvm->lock); } mutex_unlock(&matrix_dev->guests_lock); diff --git a/drivers/scsi/fnic/fip.c b/drivers/scsi/fnic/fip.c index 132f00512ee161..28c9861b650169 100644 --- a/drivers/scsi/fnic/fip.c +++ b/drivers/scsi/fnic/fip.c @@ -139,7 +139,7 @@ void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph) FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "process_vlan_resp: FIP VLAN %d\n", vid); - vlan = kzalloc_obj(*vlan); + vlan = kzalloc_obj(*vlan, GFP_ATOMIC); if (!vlan) { /* retry from timer */ diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index ecd365d78ae3c1..d83abded2039ba 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -1973,12 +1973,23 @@ megasas_set_nvme_device_properties(struct scsi_device *sdev, { struct megasas_instance *instance; u32 mr_nvme_pg_size; + u64 max_prp_io; instance = (struct megasas_instance *)sdev->host->hostdata; mr_nvme_pg_size = max_t(u32, instance->nvme_page_size, MR_DEFAULT_NVME_PAGE_SIZE); - lim->max_hw_sectors = max_io_size / 512; + /* + * megasas_make_prp_nvme() builds the PRP list in cmd->sg_frame without + * bounding it against that buffer, and spends one entry per page of + * it on the chain pointer. Cap the transfer at what the buffer holds, + * less one page for lists that start off a page boundary. + */ + max_prp_io = (u64)((instance->max_chain_frame_sz / sizeof(u64)) - + (instance->max_chain_frame_sz / mr_nvme_pg_size) - 1) * + mr_nvme_pg_size; + + lim->max_hw_sectors = min_t(u64, max_io_size, max_prp_io) >> SECTOR_SHIFT; lim->virt_boundary_mask = mr_nvme_pg_size - 1; } diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index e93ea76b565e5a..54b35893261a63 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -1029,8 +1029,8 @@ static u32 pm8001_request_msix(struct pm8001_hba_info *pm8001_ha) &(pm8001_ha->irq_vector[i])); if (rc) { for (j = 0; j < i; j++) { - free_irq(pci_irq_vector(pm8001_ha->pdev, i), - &(pm8001_ha->irq_vector[i])); + free_irq(pci_irq_vector(pm8001_ha->pdev, j), + &pm8001_ha->irq_vector[j]); } pci_free_irq_vectors(pm8001_ha->pdev); break; diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 6a05ce195aa05b..14c3efd0e21093 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -816,7 +816,9 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, "Unable to allocate memory for VPD information update.\n"); return -ENOMEM; } + mutex_lock(&ha->optrom_mutex); ha->isp_ops->get_flash_version(vha, tmp_data); + mutex_unlock(&ha->optrom_mutex); vfree(tmp_data); break; } @@ -1660,10 +1662,8 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr, rval = qla2x00_get_firmware_state(vha, state); mutex_unlock(&vha->hw->optrom_mutex); out: - if (rval != QLA_SUCCESS) { + if (rval != QLA_SUCCESS) memset(state, -1, sizeof(state)); - rval = qla2x00_get_firmware_state(vha, state); - } return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", state[0], state[1], state[2], state[3], state[4], state[5]); diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c index 5e910b5ca670d8..5b15fc28ddd7b6 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.c +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -1562,12 +1562,12 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job) scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; int rval = 0; - uint8_t bsg[DMA_POOL_SIZE]; + uint8_t bsg[DMA_POOL_SIZE] = {}; struct qla_image_version_list *list = (void *)bsg; struct qla_image_version *image; uint32_t count; dma_addr_t sfp_dma; - void *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + void *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); if (!sfp) { bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = @@ -1580,6 +1580,13 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job) image = list->version; count = list->count; + + if (struct_size(list, version, count) > sizeof(bsg)) { + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_INVALID_PARAM; + goto dealloc; + } + while (count--) { memcpy(sfp, &image->field_info, sizeof(image->field_info)); rval = qla2x00_write_sfp(vha, sfp_dma, sfp, @@ -1615,10 +1622,10 @@ qla2x00_read_fru_status(struct bsg_job *bsg_job) scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; int rval = 0; - uint8_t bsg[DMA_POOL_SIZE]; + uint8_t bsg[DMA_POOL_SIZE] = {}; struct qla_status_reg *sr = (void *)bsg; dma_addr_t sfp_dma; - uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); if (!sfp) { bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = @@ -1666,10 +1673,10 @@ qla2x00_write_fru_status(struct bsg_job *bsg_job) scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; int rval = 0; - uint8_t bsg[DMA_POOL_SIZE]; + uint8_t bsg[DMA_POOL_SIZE] = {}; struct qla_status_reg *sr = (void *)bsg; dma_addr_t sfp_dma; - uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); if (!sfp) { bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = @@ -1713,10 +1720,10 @@ qla2x00_write_i2c(struct bsg_job *bsg_job) scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; int rval = 0; - uint8_t bsg[DMA_POOL_SIZE]; + uint8_t bsg[DMA_POOL_SIZE] = {}; struct qla_i2c_access *i2c = (void *)bsg; dma_addr_t sfp_dma; - uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); if (!sfp) { bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = @@ -1727,6 +1734,12 @@ qla2x00_write_i2c(struct bsg_job *bsg_job) sg_copy_to_buffer(bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c)); + if (i2c->length > sizeof(i2c->buffer)) { + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_INVALID_PARAM; + goto dealloc; + } + memcpy(sfp, i2c->buffer, i2c->length); rval = qla2x00_write_sfp(vha, sfp_dma, sfp, i2c->device, i2c->offset, i2c->length, i2c->option); @@ -1759,10 +1772,10 @@ qla2x00_read_i2c(struct bsg_job *bsg_job) scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; int rval = 0; - uint8_t bsg[DMA_POOL_SIZE]; + uint8_t bsg[DMA_POOL_SIZE] = {}; struct qla_i2c_access *i2c = (void *)bsg; dma_addr_t sfp_dma; - uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); if (!sfp) { bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = @@ -1773,6 +1786,12 @@ qla2x00_read_i2c(struct bsg_job *bsg_job) sg_copy_to_buffer(bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c)); + if (i2c->length > sizeof(i2c->buffer)) { + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_INVALID_PARAM; + goto dealloc; + } + rval = qla2x00_read_sfp(vha, sfp_dma, sfp, i2c->device, i2c->offset, i2c->length, i2c->option); @@ -2395,7 +2414,7 @@ qla2x00_do_dport_diagnostics(struct bsg_job *bsg_job) !IS_QLA28XX(vha->hw)) return -EPERM; - dd = kmalloc_obj(*dd); + dd = kzalloc_obj(*dd); if (!dd) { ql_log(ql_log_warn, vha, 0x70db, "Failed to allocate memory for dport.\n"); @@ -3365,9 +3384,8 @@ static int qla28xx_validate_flash_image(struct bsg_job *bsg_job) bsg_reply->result = DID_OK << 16; bsg_reply->reply_payload_rcv_len = 0; bsg_job->reply_len = sizeof(struct fc_bsg_reply); - if (!rval) - bsg_job_done(bsg_job, bsg_reply->result, - bsg_reply->reply_payload_rcv_len); + bsg_job_done(bsg_job, bsg_reply->result, + bsg_reply->reply_payload_rcv_len); return QLA_SUCCESS; } diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index a7e3ec9bba47f2..c6b8e6d798c63e 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -16,7 +16,7 @@ * | | | 0x2127-0x2128 | * | Queue Command and IO tracing | 0x3074 | 0x300b | * | | | 0x3027-0x3028 | - * | | | 0x303d-0x3041 | + * | | | 0x303e-0x3041 | * | | | 0x302e,0x3033 | * | | | 0x3036,0x3038 | * | | | 0x303a | diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index 43970caca7b390..cb4278b88e1d0e 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -510,7 +510,9 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer, return PTR_ERR(buf); } - enable = kstrtoul(buf, 0, 0); + rc = kstrtoul(buf, 0, &enable); + if (rc) + goto out_free; rc = count; mutex_lock(&ha->fce_mutex); diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c index eccedb38a51546..6caae48510b381 100644 --- a/drivers/scsi/qla2xxx/qla_edif.c +++ b/drivers/scsi/qla2xxx/qla_edif.c @@ -3492,6 +3492,9 @@ void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, struct scsi_cmnd *cmd = GET_CMD_SP(sp); uint32_t handle; + if (!cmd) + return; + handle = (uint32_t)LSW(sts24->handle); /* find out if this status iosb is for a scsi read */ diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 880cd73feaca45..ed46217ce26e1d 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -157,8 +157,8 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt, break; case CS_PORT_LOGGED_OUT: if (IS_FWI2_CAPABLE(ha)) { - if (le16_to_cpu(ms_pkt->loop_id.extended) == - NPH_SNS) + if (le16_to_cpu(((struct ct_entry_24xx *) + ms_pkt)->nport_handle) == NPH_SNS) lid_is_sns = true; } else { if (le16_to_cpu(ms_pkt->loop_id.extended) == diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index e746c9274cdeda..d38bf74ea7a543 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -228,7 +228,7 @@ qla2x00_async_iocb_timeout(void *data) srb_t *sp = data; fc_port_t *fcport = sp->fcport; struct srb_iocb *lio = &sp->u.iocb_cmd; - int rc, h; + int rc, h, found; unsigned long flags; if (fcport) { @@ -251,6 +251,7 @@ qla2x00_async_iocb_timeout(void *data) lio->u.logio.data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ? QLA_LOGIO_LOGIN_RETRIED : 0; + found = 0; spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags); for (h = 1; h < sp->qpair->req->num_outstanding_cmds; h++) { @@ -258,11 +259,19 @@ qla2x00_async_iocb_timeout(void *data) sp) { sp->qpair->req->outstanding_cmds[h] = NULL; + found = 1; break; } } spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags); - sp->done(sp, QLA_FUNCTION_TIMEOUT); + /* + * Only complete the command if this path removed it + * from outstanding_cmds. Otherwise the ISR already + * completed it and a second sp->done() would race the + * submitter's freeing of the on-stack completion. + */ + if (found) + sp->done(sp, QLA_FUNCTION_TIMEOUT); } break; case SRB_LOGOUT_CMD: @@ -275,6 +284,7 @@ qla2x00_async_iocb_timeout(void *data) default: rc = qla24xx_async_abort_cmd(sp, false); if (rc) { + found = 0; spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags); for (h = 1; h < sp->qpair->req->num_outstanding_cmds; h++) { @@ -282,11 +292,19 @@ qla2x00_async_iocb_timeout(void *data) sp) { sp->qpair->req->outstanding_cmds[h] = NULL; + found = 1; break; } } spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags); - sp->done(sp, QLA_FUNCTION_TIMEOUT); + /* + * Only complete the command if this path removed it + * from outstanding_cmds. Otherwise the ISR already + * completed it and a second sp->done() would race the + * submitter's freeing of the on-stack completion. + */ + if (found) + sp->done(sp, QLA_FUNCTION_TIMEOUT); } break; } @@ -3764,11 +3782,27 @@ int qla2x00_alloc_fce_trace(scsi_qla_host_t *vha) void qla2x00_free_fce_trace(struct qla_hw_data *ha) { - if (!ha->fce) + void *fce; + dma_addr_t fce_dma; + unsigned long flags; + + /* + * Unpublish ha->fce under hardware_lock so a firmware dump in + * progress (which reads ha->fce under the same lock) cannot race + * with the buffer being freed. + */ + spin_lock_irqsave(&ha->hardware_lock, flags); + if (!ha->fce) { + spin_unlock_irqrestore(&ha->hardware_lock, flags); return; - dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, ha->fce_dma); + } + fce = ha->fce; + fce_dma = ha->fce_dma; ha->fce = NULL; ha->fce_dma = 0; + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + dma_free_coherent(&ha->pdev->dev, FCE_SIZE, fce, fce_dma); } static void @@ -10026,11 +10060,28 @@ int qla2xxx_delete_qpair(struct scsi_qla_host *vha, struct qla_qpair *qpair) { int ret = QLA_FUNCTION_FAILED; struct qla_hw_data *ha = qpair->hw; + struct rsp_que *rsp = qpair->rsp; qpair->delete_in_progress = 1; qla_free_buf_pool(qpair); + /* + * The response-queue interrupt schedules qla_do_work(), which + * dereferences qpair->rsp->req. Release the interrupt and flush + * any pending work before the request queue is freed below so a + * late completion cannot touch the freed request queue. The + * firmware queue-delete order (request then response) is kept. + */ + if (rsp && rsp->msix && rsp->msix->have_irq) { + free_irq(rsp->msix->vector, rsp->msix->handle); + rsp->msix->have_irq = 0; + rsp->msix->in_use = 0; + rsp->msix->handle = NULL; + } + if (rsp && ha->wq) + cancel_work_sync(&qpair->q_work); + ret = qla25xx_delete_req_que(vha, qpair->req); if (ret != QLA_SUCCESS) goto fail; diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 47fbd830ff1630..abf0db7f72d5dd 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -54,6 +54,19 @@ qla2x00_debounce_register(volatile __le16 __iomem *addr) return (first); } +static inline u8 +qla_calc_queue_count(u16 msix_count) +{ + /* + * Request/response queues are bounded by the MSI-X vector count less + * the mailbox vector. These counters are u8, so a board advertising + * e.g. 257 vectors would truncate msix_count - 1 (256) to 0 and hand + * kzalloc_objs() a zero count (ZERO_SIZE_PTR), faulting on the first + * ha->req_q_map[0] store. Clamp into [1, QLA_MAX_QUEUES - 1]. + */ + return clamp_t(u16, msix_count - 1, 1, QLA_MAX_QUEUES - 1); +} + static inline void qla2x00_poll(struct rsp_que *rsp) { diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 33776330956c93..612e00ca1ada05 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -205,6 +205,17 @@ void __qla_consume_iocb(struct scsi_qla_host *vha, struct purex_entry_24xx *purex = *pkt; entry_count_remaining = purex->entry_count; + + /* + * The caller already advanced ring_ptr past the head IOCB, so mark + * the head processed and account for it here, then consume only the + * continuation IOCBs that follow. + */ + ((response_t *)purex)->signature = RESPONSE_PROCESSED; + /* flush signature */ + wmb(); + --entry_count_remaining; + while (entry_count_remaining > 0) { new_pkt = rsp_q->ring_ptr; *pkt = new_pkt; @@ -3406,6 +3417,14 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) return; } + /* Everything below is the SCSI fast path; reject other SRB types. */ + if (sp->type != SRB_SCSI_CMD) { + ql_dbg(ql_dbg_io, vha, 0x303d, + "Unexpected SRB type %x for status IOCB, sp %p.\n", + sp->type, sp); + return; + } + /* Fast path completion. */ qla_chk_edif_rx_sa_delete_pending(vha, sp, sts24); sp->qpair->cmd_completion_cnt++; @@ -3463,6 +3482,18 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) { /* Sense data lies beyond any FCP RESPONSE data. */ if (IS_FWI2_CAPABLE(ha)) { + /* + * A hostile or buggy target may report an + * rsp_info_len larger than the IOCB data area. + * Clamp it so the par_sense_len subtraction cannot + * underflow and walk sense_data out of bounds. + */ + if (rsp_info_len > par_sense_len) { + ql_log(ql_log_warn, fcport->vha, 0x3107, + "Truncating bogus rsp_info_len 0x%x to 0x%x.\n", + rsp_info_len, par_sense_len); + rsp_info_len = par_sense_len; + } sense_data += rsp_info_len; par_sense_len -= rsp_info_len; } @@ -3778,10 +3809,12 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt) "iocb type %xh with error status %xh, handle %xh, rspq id %d\n", pkt->entry_type, pkt->entry_status, pkt->handle, rsp->id); - if (que >= ha->max_req_queues || !ha->req_q_map[que]) + if (que >= ha->max_req_queues) goto fatal; req = ha->req_q_map[que]; + if (!req) + goto fatal; if (pkt->entry_status & RF_BUSY) res = DID_BUS_BUSY << 16; @@ -4548,10 +4581,10 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) ha->msix_count = ret; /* Recalculate queue values */ if (ha->mqiobase && (ql2xmqsupport || ql2xnvmeenable)) { - ha->max_req_queues = ha->msix_count - 1; + ha->max_req_queues = qla_calc_queue_count(ha->msix_count); /* ATIOQ needs 1 vector. That's 1 less QPair */ - if (QLA_TGT_MODE_ENABLED()) + if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1) ha->max_req_queues--; ha->max_rsp_queues = ha->max_req_queues; diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 44e310f1a3708f..f604aab8b161ec 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -2262,6 +2262,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states) if (!ha->flags.fw_started) return QLA_FUNCTION_FAILED; + memset(&mc, 0, sizeof(mc)); + mcp->mb[0] = MBC_GET_FIRMWARE_STATE; mcp->out_mb = MBX_0; if (IS_FWI2_CAPABLE(vha->hw)) @@ -4203,6 +4205,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, list_for_each_entry(vp, &ha->vp_list, list) { if (rptid_entry->vp_idx == vp->vp_idx) { found = 1; + atomic_inc(&vp->vref_count); break; } } @@ -4211,7 +4214,9 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, if (!found) return; + spin_lock_irqsave(&ha->vport_slock, flags); qla_update_host_map(vp, id); + spin_unlock_irqrestore(&ha->vport_slock, flags); /* * Cannot configure here as we are still sitting on the @@ -4220,6 +4225,10 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, set_bit(VP_IDX_ACQUIRED, &vp->vp_flags); set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags); set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags); + + spin_lock_irqsave(&ha->vport_slock, flags); + atomic_dec(&vp->vref_count); + spin_unlock_irqrestore(&ha->vport_slock, flags); } set_bit(VP_DPC_NEEDED, &vha->dpc_flags); qla2xxx_wake_dpc(vha); @@ -4330,10 +4339,10 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha) if (rval != QLA_SUCCESS) { ql_dbg(ql_dbg_mbx, vha, 0x10bd, "Failed to issue VP config IOCB (%x).\n", rval); - } else if (vpmod->comp_status != 0) { + } else if (vpmod->entry_status != 0) { ql_dbg(ql_dbg_mbx, vha, 0x10be, "Failed to complete IOCB -- error status (%x).\n", - vpmod->comp_status); + vpmod->entry_status); rval = QLA_FUNCTION_FAILED; } else if (vpmod->comp_status != cpu_to_le16(CS_COMPLETE)) { ql_dbg(ql_dbg_mbx, vha, 0x10bf, @@ -6490,6 +6499,7 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; dma_addr_t dd_dma; + void *dd; if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw)) @@ -6498,15 +6508,12 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha, ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x119f, "Entered %s.\n", __func__); - dd_dma = dma_map_single(&vha->hw->pdev->dev, - dd_buf, size, DMA_FROM_DEVICE); - if (dma_mapping_error(&vha->hw->pdev->dev, dd_dma)) { - ql_log(ql_log_warn, vha, 0x1194, "Failed to map dma buffer.\n"); + dd = dma_alloc_coherent(&vha->hw->pdev->dev, size, &dd_dma, GFP_KERNEL); + if (!dd) { + ql_log(ql_log_warn, vha, 0x1194, "Failed to allocate dma buffer.\n"); return QLA_MEMORY_ALLOC_FAILED; } - memset(dd_buf, 0, size); - mcp->mb[0] = MBC_DPORT_DIAGNOSTICS; mcp->mb[1] = options; mcp->mb[2] = MSW(LSD(dd_dma)); @@ -6528,8 +6535,9 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha, "Done %s.\n", __func__); } - dma_unmap_single(&vha->hw->pdev->dev, dd_dma, - size, DMA_FROM_DEVICE); + memcpy(dd_buf, dd, size); + + dma_free_coherent(&vha->hw->pdev->dev, size, dd, dd_dma); return rval; } diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c index 2531e71c39dc8d..b84fe71ea340da 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.c +++ b/drivers/scsi/qla2xxx/qla_nvme.c @@ -374,6 +374,7 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport, srb_t *sp; int rval = QLA_FUNCTION_FAILED; uint8_t cnt = 0; + unsigned long flags; if (!fcport || fcport->deleted) goto out; @@ -440,7 +441,11 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport, a.vp_idx = vha->vp_idx; a.nport_handle = uctx->nport_handle; a.xchg_address = uctx->exchange_address; - qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true); + if (ha->flags.fw_started) { + spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags); + qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true); + spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags); + } kfree(uctx); return rval; } @@ -463,8 +468,8 @@ static void qla_nvme_ls_abort(struct nvme_fc_local_port *lport, } spin_unlock_irqrestore(&priv->cmd_lock, flags); - INIT_WORK(&priv->abort_work, qla_nvme_abort_work); - schedule_work(&priv->abort_work); + if (!schedule_work(&priv->abort_work)) + kref_put(&priv->sp->cmd_kref, priv->sp->put_fn); } static int qla_nvme_ls_req(struct nvme_fc_local_port *lport, @@ -501,6 +506,7 @@ static int qla_nvme_ls_req(struct nvme_fc_local_port *lport, priv->sp = sp; kref_init(&sp->cmd_kref); spin_lock_init(&priv->cmd_lock); + INIT_WORK(&priv->abort_work, qla_nvme_abort_work); nvme = &sp->u.iocb_cmd; priv->fd = fd; nvme->u.nvme.desc = fd; @@ -545,8 +551,8 @@ static void qla_nvme_fcp_abort(struct nvme_fc_local_port *lport, } spin_unlock_irqrestore(&priv->cmd_lock, flags); - INIT_WORK(&priv->abort_work, qla_nvme_abort_work); - schedule_work(&priv->abort_work); + if (!schedule_work(&priv->abort_work)) + kref_put(&priv->sp->cmd_kref, priv->sp->put_fn); } static inline int qla2x00_start_nvme_mq(srb_t *sp) @@ -811,6 +817,7 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport, kref_init(&sp->cmd_kref); spin_lock_init(&priv->cmd_lock); + INIT_WORK(&priv->abort_work, qla_nvme_abort_work); sp->priv = priv; priv->sp = sp; sp->type = SRB_NVME_CMD; @@ -1127,6 +1134,10 @@ static void qla_nvme_lsrjt_pt_iocb(struct scsi_qla_host *vha, lsrjt_iocb->rx_byte_count = 0; } +/* + * Allocates from and advances the request ring, so the caller must hold + * qp->qp_lock_ptr (the response-queue caller already holds it). + */ static int qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp, struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate) @@ -1183,6 +1194,7 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item) { struct qla_nvme_unsol_ctx *uctx = item->purls_context; struct qla_nvme_lsrjt_pt_arg a; + unsigned long flags; int ret = 1; #if (IS_ENABLED(CONFIG_NVME_FC)) @@ -1195,7 +1207,14 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item) a.vp_idx = vha->vp_idx; a.nport_handle = uctx->nport_handle; a.xchg_address = uctx->exchange_address; - qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true); + if (vha->hw->flags.fw_started) { + spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr, + flags); + qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, + true); + spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr, + flags); + } list_del(&uctx->elem); kfree(uctx); } diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 72b1c28e4dae02..d853b4006f4e79 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -2127,7 +2127,7 @@ qla2x00_iospace_config(struct qla_hw_data *ha) ha->msix_count = msix + 1; /* Max queues are bounded by available msix vectors */ /* MB interrupt uses 1 vector */ - ha->max_req_queues = ha->msix_count - 1; + ha->max_req_queues = qla_calc_queue_count(ha->msix_count); ha->max_rsp_queues = ha->max_req_queues; /* Queue pairs is the max value minus the base queue pair */ ha->max_qpairs = ha->max_rsp_queues - 1; @@ -2213,10 +2213,10 @@ qla83xx_iospace_config(struct qla_hw_data *ha) */ if (ql2xmqsupport || ql2xnvmeenable) { /* MB interrupt uses 1 vector */ - ha->max_req_queues = ha->msix_count - 1; + ha->max_req_queues = qla_calc_queue_count(ha->msix_count); /* ATIOQ needs 1 vector. That's 1 less QPair */ - if (QLA_TGT_MODE_ENABLED()) + if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1) ha->max_req_queues--; ha->max_rsp_queues = ha->max_req_queues; @@ -3933,8 +3933,6 @@ qla2x00_remove_one(struct pci_dev *pdev) qla2x00_dfs_remove(base_vha); - qla84xx_put_chip(base_vha); - /* Disable timer */ if (base_vha->timer_active) qla2x00_stop_timer(base_vha); @@ -3959,6 +3957,8 @@ qla2x00_remove_one(struct pci_dev *pdev) scsi_remove_host(base_vha->host); + qla84xx_put_chip(base_vha); + qla2x00_free_device(base_vha); qla2x00_clear_drv_active(ha); @@ -6828,8 +6828,6 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work) qla2x00_dfs_remove(base_vha); - qla84xx_put_chip(base_vha); - if (base_vha->timer_active) qla2x00_stop_timer(base_vha); @@ -6847,6 +6845,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work) scsi_remove_host(base_vha->host); + qla84xx_put_chip(base_vha); + base_vha->flags.init_done = 0; qla25xx_delete_queues(base_vha); qla2x00_free_fcports(base_vha); diff --git a/drivers/scsi/scsi_bsg.c b/drivers/scsi/scsi_bsg.c index e80dec53174eac..5eec248a77a6b3 100644 --- a/drivers/scsi/scsi_bsg.c +++ b/drivers/scsi/scsi_bsg.c @@ -18,6 +18,7 @@ struct scsi_bsg_uring_cmd_pdu { struct bio *bio; /* mapped user buffer, unmap in task work */ struct request *req; /* block request, freed in task work */ u64 response_addr; /* user space response buffer address */ + u32 max_response_len; /* user response buffer size */ }; static_assert(sizeof(struct scsi_bsg_uring_cmd_pdu) <= sizeof_field(struct io_uring_cmd, pdu)); @@ -45,8 +46,8 @@ static void scsi_bsg_uring_task_cb(struct io_tw_req tw_req, io_tw_token_t tw) if (scsi_status_is_check_condition(scmd->result)) { driver_status = DRIVER_SENSE; if (pdu->response_addr) - sense_len_wr = min_t(u8, scmd->sense_len, - SCSI_SENSE_BUFFERSIZE); + sense_len_wr = min_t(unsigned int, pdu->max_response_len, + scmd->sense_len); } if (sense_len_wr) { @@ -76,12 +77,10 @@ static enum rq_end_io_ret scsi_bsg_uring_cmd_done(struct request *req, static int scsi_bsg_map_user_buffer(struct request *req, struct io_uring_cmd *ioucmd, - unsigned int issue_flags, gfp_t gfp_mask) + unsigned int issue_flags, gfp_t gfp_mask, + bool is_write, u64 buf_addr, + unsigned long buf_len) { - const struct bsg_uring_cmd *cmd = io_uring_sqe128_cmd(ioucmd->sqe, struct bsg_uring_cmd); - bool is_write = cmd->dout_xfer_len > 0; - u64 buf_addr = is_write ? cmd->dout_xferp : cmd->din_xferp; - unsigned long buf_len = is_write ? cmd->dout_xfer_len : cmd->din_xfer_len; struct iov_iter iter; int ret; @@ -104,21 +103,28 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, struct io_uring_cmd *iouc unsigned int issue_flags, bool open_for_write) { struct scsi_bsg_uring_cmd_pdu *pdu = scsi_bsg_uring_cmd_pdu(ioucmd); - const struct bsg_uring_cmd *cmd = io_uring_sqe128_cmd(ioucmd->sqe, struct bsg_uring_cmd); + const struct bsg_uring_cmd *cmd = + io_uring_sqe128_cmd(ioucmd->sqe, struct bsg_uring_cmd); struct scsi_cmnd *scmd; struct request *req; blk_mq_req_flags_t blk_flags = 0; gfp_t gfp_mask = GFP_KERNEL; + u64 request = READ_ONCE(cmd->request); + u32 request_len = READ_ONCE(cmd->request_len); + u64 dout_xferp = READ_ONCE(cmd->dout_xferp); + u32 dout_xfer_len = READ_ONCE(cmd->dout_xfer_len); + u64 din_xferp = READ_ONCE(cmd->din_xferp); + u32 din_xfer_len = READ_ONCE(cmd->din_xfer_len); int ret; if (cmd->protocol != BSG_PROTOCOL_SCSI || cmd->subprotocol != BSG_SUB_PROTOCOL_SCSI_CMD) return -EINVAL; - if (!cmd->request || cmd->request_len == 0) + if (!request || request_len == 0) return -EINVAL; - if (cmd->dout_xfer_len && cmd->din_xfer_len) { + if (dout_xfer_len && din_xfer_len) { pr_warn_once("BIDI support in bsg has been removed.\n"); return -EOPNOTSUPP; } @@ -131,20 +137,20 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, struct io_uring_cmd *iouc gfp_mask = GFP_NOWAIT; } - req = scsi_alloc_request(q, cmd->dout_xfer_len ? + req = scsi_alloc_request(q, dout_xfer_len ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, blk_flags); if (IS_ERR(req)) return PTR_ERR(req); scmd = blk_mq_rq_to_pdu(req); - if (cmd->request_len > sizeof(scmd->cmnd)) { + if (request_len > sizeof(scmd->cmnd)) { ret = -EINVAL; goto out_free_req; } - scmd->cmd_len = cmd->request_len; + scmd->cmd_len = request_len; scmd->allowed = SG_DEFAULT_RETRIES; - if (copy_from_user(scmd->cmnd, uptr64(cmd->request), cmd->request_len)) { + if (copy_from_user(scmd->cmnd, uptr64(request), request_len)) { ret = -EFAULT; goto out_free_req; } @@ -155,11 +161,16 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, struct io_uring_cmd *iouc } pdu->response_addr = cmd->response; - scmd->sense_len = cmd->max_response_len ? - min(cmd->max_response_len, SCSI_SENSE_BUFFERSIZE) : SCSI_SENSE_BUFFERSIZE; + pdu->max_response_len = cmd->max_response_len; - if (cmd->dout_xfer_len || cmd->din_xfer_len) { - ret = scsi_bsg_map_user_buffer(req, ioucmd, issue_flags, gfp_mask); + if (dout_xfer_len || din_xfer_len) { + bool is_write = dout_xfer_len > 0; + u64 buf_addr = is_write ? dout_xferp : din_xferp; + unsigned long buf_len = is_write ? dout_xfer_len : din_xfer_len; + + ret = scsi_bsg_map_user_buffer(req, ioucmd, issue_flags, + gfp_mask, is_write, buf_addr, + buf_len); if (ret) goto out_free_req; pdu->bio = req->bio; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index daeb3693fe559a..480a0bf2aff339 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1187,8 +1187,10 @@ blk_status_t scsi_alloc_sgtables(struct scsi_cmnd *cmd) if (blk_rq_bytes(rq) & rq->q->limits.dma_pad_mask) { unsigned int pad_len = (rq->q->limits.dma_pad_mask & ~blk_rq_bytes(rq)) + 1; + unsigned int data_len = last_sg->length; last_sg->length += pad_len; + sg_zero_buffer(last_sg, 1, pad_len, data_len); cmd->extra_len += pad_len; } diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index 9b0bba64e91e43..73a77763210d72 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -79,7 +80,13 @@ static int qepic_get_irq(struct irq_desc *desc) static void qepic_cascade(struct irq_desc *desc) { + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + generic_handle_irq(qepic_get_irq(desc)); + + chained_irq_exit(chip, desc); } static int qepic_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c index ea7f83916d8d8d..f348be37cc2c80 100644 --- a/drivers/soc/qcom/apr.c +++ b/drivers/soc/qcom/apr.c @@ -74,15 +74,35 @@ int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt) } EXPORT_SYMBOL_GPL(apr_send_pkt); -void gpr_free_port(gpr_port_t *port) +static void pkt_router_svc_put(struct pkt_router_svc *svc) +{ + if (refcount_dec_and_test(&svc->callback_refs)) + complete(&svc->callbacks_drained); +} + +static void pkt_router_svc_remove(struct pkt_router_svc *svc) { - struct packet_router *gpr = port->pr; + struct packet_router *pr = svc->pr; unsigned long flags; - spin_lock_irqsave(&gpr->svcs_lock, flags); - idr_remove(&gpr->svcs_idr, port->id); - spin_unlock_irqrestore(&gpr->svcs_lock, flags); + spin_lock_irqsave(&pr->svcs_lock, flags); + idr_remove(&pr->svcs_idr, svc->id); + spin_unlock_irqrestore(&pr->svcs_lock, flags); + + if (!refcount_dec_and_test(&svc->callback_refs)) + wait_for_completion(&svc->callbacks_drained); +} +/** + * gpr_free_port() - unregister a dynamic GPR port after callbacks drain + * @port: dynamic port returned by gpr_alloc_port() + * + * Context: process context, and never from @port's own callback. + */ +void gpr_free_port(gpr_port_t *port) +{ + might_sleep(); + pkt_router_svc_remove(port); kfree(port); } EXPORT_SYMBOL_GPL(gpr_free_port); @@ -105,6 +125,8 @@ gpr_port_t *gpr_alloc_port(struct apr_device *gdev, struct device *dev, svc->priv = priv; svc->dev = dev; spin_lock_init(&svc->lock); + refcount_set(&svc->callback_refs, 1); + init_completion(&svc->callbacks_drained); spin_lock(&pr->svcs_lock); id = idr_alloc_cyclic(&pr->svcs_idr, svc, GPR_DYNAMIC_PORT_START, @@ -191,7 +213,7 @@ static int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf { uint16_t hdr_size, msg_type, ver, svc_id; struct pkt_router_svc *svc; - struct apr_device *adev; + struct apr_device *adev = NULL; struct apr_driver *adrv = NULL; struct apr_resp_pkt resp; struct apr_hdr *hdr; @@ -232,6 +254,8 @@ static int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf svc_id = hdr->dest_svc; spin_lock_irqsave(&apr->svcs_lock, flags); svc = idr_find(&apr->svcs_idr, svc_id); + if (svc && !refcount_inc_not_zero(&svc->callback_refs)) + svc = NULL; if (svc && svc->dev->driver) { adev = svc_to_apr_device(svc); adrv = to_apr_driver(adev->dev.driver); @@ -239,6 +263,8 @@ static int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf spin_unlock_irqrestore(&apr->svcs_lock, flags); if (!adrv || !adev) { + if (svc) + pkt_router_svc_put(svc); dev_err(apr->dev, "APR: service is not registered (%d)\n", svc_id); return -EINVAL; @@ -255,6 +281,7 @@ static int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf resp.payload = buf + hdr_size; adrv->callback(adev, &resp); + pkt_router_svc_put(svc); return 0; } @@ -298,6 +325,8 @@ static int gpr_do_rx_callback(struct packet_router *gpr, struct apr_rx_buf *abuf spin_lock_irqsave(&gpr->svcs_lock, flags); svc = idr_find(&gpr->svcs_idr, hdr->dest_port); + if (svc && !refcount_inc_not_zero(&svc->callback_refs)) + svc = NULL; spin_unlock_irqrestore(&gpr->svcs_lock, flags); if (!svc) { @@ -308,6 +337,7 @@ static int gpr_do_rx_callback(struct packet_router *gpr, struct apr_rx_buf *abuf if (svc->callback) svc->callback(&resp, svc->priv, 0); + pkt_router_svc_put(svc); return 0; } @@ -378,13 +408,10 @@ static void apr_device_remove(struct device *dev) { struct apr_device *adev = to_apr_device(dev); struct apr_driver *adrv = to_apr_driver(dev->driver); - struct packet_router *apr = dev_get_drvdata(adev->dev.parent); + pkt_router_svc_remove(&adev->svc); if (adrv->remove) adrv->remove(adev); - spin_lock(&apr->svcs_lock); - idr_remove(&apr->svcs_idr, adev->svc.id); - spin_unlock(&apr->svcs_lock); } static int apr_uevent(const struct device *dev, struct kobj_uevent_env *env) @@ -428,6 +455,8 @@ static int apr_add_device(struct device *dev, struct device_node *np, svc->priv = adev; svc->dev = dev; spin_lock_init(&svc->lock); + refcount_set(&svc->callback_refs, 1); + init_completion(&svc->callbacks_drained); adev->domain_id = domain_id; @@ -458,7 +487,7 @@ static int apr_add_device(struct device *dev, struct device_node *np, spin_unlock(&apr->svcs_lock); if (ret < 0) { dev_err(dev, "idr_alloc failed: %d\n", ret); - goto out; + goto free_adev; } /* Protection domain is optional, it does not exist on older platforms */ @@ -466,7 +495,7 @@ static int apr_add_device(struct device *dev, struct device_node *np, 1, &adev->service_path); if (ret < 0 && ret != -EINVAL) { dev_err(dev, "Failed to read second value of qcom,protection-domain\n"); - goto out; + goto remove_svc; } dev_info(dev, "Adding APR/GPR dev: %s\n", dev_name(&adev->dev)); @@ -474,10 +503,16 @@ static int apr_add_device(struct device *dev, struct device_node *np, ret = device_register(&adev->dev); if (ret) { dev_err(dev, "device_register failed: %d\n", ret); + pkt_router_svc_remove(svc); put_device(&adev->dev); } -out: + return ret; + +remove_svc: + pkt_router_svc_remove(svc); +free_adev: + kfree(adev); return ret; } diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index 15636a8dc907a8..107fa2057654ac 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -154,8 +154,6 @@ struct se_fw_hdr { /*Magic numbers*/ #define SE_MAGIC_NUM 0x57464553 -#define MAX_GENI_CFG_RAMn_CNT 455 - #define MI_PBT_NON_PAGED_SEGMENT 0x0 #define MI_PBT_HASH_SEGMENT 0x2 #define MI_PBT_NOTUSED_SEGMENT 0x3 @@ -1224,24 +1222,27 @@ EXPORT_SYMBOL_GPL(geni_se_resources_init); /** * geni_find_protocol_fw() - Locate and validate SE firmware for a protocol. - * @dev: Pointer to the device structure. + * @se: Pointer to the serial engine structure. * @fw: Pointer to the firmware image. * @protocol: Expected serial engine protocol type. * * Identifies the appropriate firmware image or configuration required for a - * specific communication protocol instance running on a Qualcomm GENI - * controller. + * specific communication protocol instance running on a Qualcomm GENI + * controller. Validates the firmware size against the hardware PROG_RAM_DEPTH + * read from SE_HW_PARAM_2. * * Return: pointer to a valid 'struct se_fw_hdr' if found, or NULL otherwise. */ -static struct se_fw_hdr *geni_find_protocol_fw(struct device *dev, const struct firmware *fw, +static struct se_fw_hdr *geni_find_protocol_fw(struct geni_se *se, const struct firmware *fw, enum geni_se_protocol_type protocol) { + struct device *dev = se->dev; const struct elf32_hdr *ehdr; const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; struct se_fw_hdr *sefw; u32 fw_end, cfg_idx_end, cfg_val_end; + u32 prog_ram_depth; u16 fw_size; int i; @@ -1300,10 +1301,11 @@ static struct se_fw_hdr *geni_find_protocol_fw(struct device *dev, const struct sefw->fw_size_in_items = cpu_to_le16(fw_size); } - if (fw_size >= MAX_GENI_CFG_RAMn_CNT) { - dev_err(dev, - "Firmware size (%u) exceeds max allowed RAMn count (%u)\n", - fw_size, MAX_GENI_CFG_RAMn_CNT); + prog_ram_depth = FIELD_GET(PROG_RAM_DEPTH_MSK, + readl_relaxed(se->base + SE_HW_PARAM_2)); + if (fw_size >= prog_ram_depth) { + dev_err(dev, "Firmware size (%u) exceeds RAM size (%u)\n", + fw_size, prog_ram_depth); continue; } @@ -1427,7 +1429,7 @@ static int geni_load_se_fw(struct geni_se *se, const struct firmware *fw, int ret; struct se_fw_hdr *hdr; - hdr = geni_find_protocol_fw(se->dev, fw, protocol); + hdr = geni_find_protocol_fw(se, fw, protocol); if (!hdr) return -EINVAL; diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 8115c64dd48e2f..bc3f862878a4b2 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -117,6 +117,8 @@ int sdw_find_col_index(int col); * @ch_mask: Channel mask * @transport_params: Transport parameters * @port_params: Port parameters + * @transport_params_override_mask: Bitmask of per-slave transport overrides + * @transport_params_override: Per-slave transport override values * @port_node: List node for Master or Slave port_list * @lane: Which lane is used * @@ -128,6 +130,8 @@ struct sdw_port_runtime { int ch_mask; struct sdw_transport_params transport_params; struct sdw_port_params port_params; + unsigned int transport_params_override_mask; + struct sdw_transport_params transport_params_override; struct list_head port_node; unsigned int lane; }; diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 3d8f5a81eff195..cd4234eb55aa16 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1116,10 +1116,23 @@ static int qcom_swrm_port_enable(struct sdw_bus *bus, ctrl->reg_read(ctrl, reg, &val); - if (enable_ch->enable) + if (enable_ch->enable) { + /* + * The Denali amplifiers require the Windows-observed active + * Offset2 value on their VI and CPS feedback ports. Keep the + * inactive transport description intact and alter only the value + * written while enabling these device-scoped ports. + */ + if (of_machine_is_compatible("microsoft,denali") && + bus->controller_id == MASTER_ID_WSA && + (enable_ch->port_num == 10 || enable_ch->port_num == 11 || + enable_ch->port_num == 13)) + val &= ~(0xff << SWRM_DP_PORT_CTRL_OFFSET2_SHFT); + val |= (enable_ch->ch_mask << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); - else + } else { val &= ~(0xff << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); + } return ctrl->reg_write(ctrl, reg, val); } @@ -1217,10 +1230,37 @@ static void qcom_swrm_stream_free_ports(struct qcom_swrm_ctrl *ctrl, mutex_unlock(&ctrl->port_lock); } +#define SWRM_DENALI_VI_DAI 9 +#define SWRM_DENALI_CPS_DAI 10 + +static bool qcom_swrm_is_denali_feedback_dai(struct qcom_swrm_ctrl *ctrl, int dai_id) +{ + return of_machine_is_compatible("microsoft,denali") && + ctrl->bus.controller_id == MASTER_ID_WSA && + (dai_id == SWRM_DENALI_VI_DAI || + dai_id == SWRM_DENALI_CPS_DAI); +} + +static bool qcom_swrm_merge_port(struct sdw_port_config *pconfig, int nports, + int port, struct sdw_port_runtime *p_rt) +{ + int i; + + for (i = 0; i < nports; i++) { + if (pconfig[i].num != port) + continue; + + pconfig[i].ch_mask |= p_rt->ch_mask; + return true; + } + + return false; +} + static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, struct sdw_stream_runtime *stream, - struct snd_pcm_hw_params *params, - int direction) + struct snd_pcm_hw_params *params, + int direction, int dai_id) { struct sdw_stream_config sconfig; struct sdw_master_runtime *m_rt; @@ -1235,7 +1275,15 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, if (!pconfig) return -ENOMEM; - if (direction == SNDRV_PCM_STREAM_CAPTURE) + /* + * Direction is a property of the controller data port. A speaker VI + * sidechain is intentionally exposed as a companion playback BE so + * DPCM starts it with render, while the physical DIN port still carries + * data from the SoundWire slaves into the master. + */ + if (qcom_swrm_is_denali_feedback_dai(ctrl, dai_id)) + sconfig.direction = SDW_DATA_DIR_TX; + else if (direction == SNDRV_PCM_STREAM_CAPTURE) sconfig.direction = SDW_DATA_DIR_TX; else sconfig.direction = SDW_DATA_DIR_RX; @@ -1275,6 +1323,12 @@ static int qcom_swrm_stream_alloc_ports(struct qcom_swrm_ctrl *ctrl, dev_err(ctrl->dev, "All ports busy\n"); return -EBUSY; } + + /* Both Denali amplifiers use one master CPS port. */ + if (qcom_swrm_is_denali_feedback_dai(ctrl, dai_id) && + qcom_swrm_merge_port(pconfig, nports, pn, p_rt)) + continue; + set_bit(pn, port_mask); pconfig[nports].num = pn; pconfig[nports].ch_mask = p_rt->ch_mask; @@ -1298,7 +1352,7 @@ static int qcom_swrm_hw_params(struct snd_pcm_substream *substream, int ret; ret = qcom_swrm_stream_alloc_ports(ctrl, sruntime, params, - substream->stream); + substream->stream, dai->id); if (ret) qcom_swrm_stream_free_ports(ctrl, sruntime); @@ -1404,6 +1458,20 @@ static int qcom_swrm_register_dais(struct qcom_swrm_ctrl *ctrl) stream->rates = SNDRV_PCM_RATE_48000; stream->formats = SNDRV_PCM_FMTBIT_S16_LE; + if (qcom_swrm_is_denali_feedback_dai(ctrl, i)) { + dais[i].playback = (struct snd_soc_pcm_stream) { + .channels_min = 1, + .channels_max = 1, + .rates = i == SWRM_DENALI_VI_DAI ? + SNDRV_PCM_RATE_8000 : SNDRV_PCM_RATE_24000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }; + dais[i].playback.stream_name = + devm_kasprintf(dev, GFP_KERNEL, "SoundWire Feedback%d", i); + if (!dais[i].playback.stream_name) + return -ENOMEM; + } + dais[i].ops = &qcom_swrm_pdm_dai_ops; dais[i].id = i; } diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 5d20e95a1e23eb..b00d3354f40c40 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -62,6 +62,65 @@ int sdw_find_row_index(int row) } EXPORT_SYMBOL(sdw_find_row_index); +static int sdw_program_simple_ext(struct sdw_bus *bus, struct sdw_slave *slave, + struct sdw_transport_params *t_params, + u32 registers) +{ + u32 blockctrl3, samplectrl2, hctrl, offsetctrl2; + u16 wbuf; + int ret; + + if (bus->params.next_bank) { + blockctrl3 = SDW_DPN_BLOCKCTRL3_B1(t_params->port_num); + samplectrl2 = SDW_DPN_SAMPLECTRL2_B1(t_params->port_num); + hctrl = SDW_DPN_HCTRL_B1(t_params->port_num); + offsetctrl2 = SDW_DPN_OFFSETCTRL2_B1(t_params->port_num); + } else { + blockctrl3 = SDW_DPN_BLOCKCTRL3_B0(t_params->port_num); + samplectrl2 = SDW_DPN_SAMPLECTRL2_B0(t_params->port_num); + hctrl = SDW_DPN_HCTRL_B0(t_params->port_num); + offsetctrl2 = SDW_DPN_OFFSETCTRL2_B0(t_params->port_num); + } + + if (registers & SDW_DPN_SIMPLE_TRANSPORT_BLOCKCTRL3) { + ret = sdw_write_no_pm(slave, blockctrl3, t_params->blk_pkg_mode); + if (ret < 0) + return ret; + } + + if (registers & SDW_DPN_SIMPLE_TRANSPORT_SAMPLECTRL2) { + wbuf = FIELD_GET(SDW_DPN_SAMPLECTRL_HIGH, + t_params->sample_interval - 1); + ret = sdw_write_no_pm(slave, samplectrl2, wbuf); + if (ret < 0) + return ret; + } + + if (registers & SDW_DPN_SIMPLE_TRANSPORT_HCTRL) { + wbuf = FIELD_PREP(SDW_DPN_HCTRL_HSTART, t_params->hstart); + wbuf |= FIELD_PREP(SDW_DPN_HCTRL_HSTOP, t_params->hstop); + ret = sdw_write_no_pm(slave, hctrl, wbuf); + if (ret < 0) + return ret; + } + + if (registers & SDW_DPN_SIMPLE_TRANSPORT_OFFSETCTRL2) { + ret = sdw_write_no_pm(slave, offsetctrl2, t_params->offset2); + if (ret < 0) + return ret; + } + + return 0; +} + +static void sdw_apply_slave_transport_overrides(struct sdw_port_runtime *p_rt) +{ + if (p_rt->transport_params_override_mask & + SDW_PORT_CONFIG_OVERRIDE_OFFSET1) + p_rt->transport_params.offset1 = + p_rt->transport_params_override.offset1; +} + static int _sdw_program_slave_port_params(struct sdw_bus *bus, struct sdw_slave *slave, struct sdw_transport_params *t_params, @@ -137,6 +196,7 @@ static int sdw_program_slave_port_params(struct sdw_bus *bus, struct sdw_slave_prop *slave_prop = &s_rt->slave->prop; u32 addr1, addr2, addr3, addr4, addr5, addr6; enum sdw_dpn_type port_type; + u32 simple_transport_registers = 0; bool read_only_wordlength; int ret; u8 wbuf; @@ -144,6 +204,12 @@ static int sdw_program_slave_port_params(struct sdw_bus *bus, if (s_rt->slave->is_mockup_device) return 0; + /* Bus compute_params() establishes the common master schedule. Apply + * explicitly requested slave-only deltas immediately before the slave is + * programmed so master transport state remains shared. + */ + sdw_apply_slave_transport_overrides(p_rt); + if (t_params->port_num) { struct sdw_dpn_prop *dpn_prop; @@ -154,6 +220,7 @@ static int sdw_program_slave_port_params(struct sdw_bus *bus, read_only_wordlength = dpn_prop->read_only_wordlength; port_type = dpn_prop->type; + simple_transport_registers = dpn_prop->simple_transport_registers; } else { read_only_wordlength = false; port_type = SDW_DPN_FULL; @@ -239,7 +306,14 @@ static int sdw_program_slave_port_params(struct sdw_bus *bus, } } - if (port_type != SDW_DPN_SIMPLE) { + if (port_type == SDW_DPN_SIMPLE && simple_transport_registers) { + ret = sdw_program_simple_ext(bus, s_rt->slave, t_params, + simple_transport_registers); + if (ret < 0) + dev_err(&s_rt->slave->dev, + "Extended SIMPLE transport reg write failed for port: %d\n", + t_params->port_num); + } else if (port_type != SDW_DPN_SIMPLE) { ret = _sdw_program_slave_port_params(bus, s_rt->slave, t_params, port_type); if (ret < 0) @@ -983,6 +1057,10 @@ static int sdw_port_config(struct sdw_port_runtime *p_rt, { p_rt->ch_mask = port_config[port_index].ch_mask; p_rt->num = port_config[port_index].num; + p_rt->transport_params_override_mask = + port_config[port_index].transport_params_override_mask; + p_rt->transport_params_override = + port_config[port_index].transport_params_override; /* * TODO: Check port capabilities for requested configuration diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index 58012e1b5ae741..fd7b9d5d968651 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -915,7 +915,13 @@ static int bcm63xx_hsspi_resume(struct device *dev) } } - spi_controller_resume(host); + ret = spi_controller_resume(host); + if (ret) { + if (bs->pll_clk) + clk_disable_unprepare(bs->pll_clk); + clk_disable_unprepare(bs->clk); + return ret; + } return 0; } diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 43d7b54e3ae85a..74c06934cd2ddd 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -650,7 +650,11 @@ static int bcm63xx_spi_resume(struct device *dev) if (ret) return ret; - spi_controller_resume(host); + ret = spi_controller_resume(host); + if (ret) { + clk_disable_unprepare(bs->clk); + return ret; + } return 0; } diff --git a/drivers/spi/spi-bcmbca-hsspi.c b/drivers/spi/spi-bcmbca-hsspi.c index 09c1472ae4fa15..d4491058b4561b 100644 --- a/drivers/spi/spi-bcmbca-hsspi.c +++ b/drivers/spi/spi-bcmbca-hsspi.c @@ -594,7 +594,13 @@ static int bcmbca_hsspi_resume(struct device *dev) } } - spi_controller_resume(host); + ret = spi_controller_resume(host); + if (ret) { + if (bs->pll_clk) + clk_disable_unprepare(bs->pll_clk); + clk_disable_unprepare(bs->clk); + return ret; + } return 0; } diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index d9e6b4b87c899f..12b3a3b29a89f2 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1231,6 +1231,8 @@ void spi_unmap_buf(struct spi_controller *ctlr, struct device *dev, spi_unmap_buf_attrs(ctlr, dev, sgt, dir, 0); } +static int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg); + static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) { struct device *tx_dev, *rx_dev; @@ -1254,7 +1256,13 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) else rx_dev = ctlr->dev.parent; - ret = -ENOMSG; + /* + * Store the devices before mapping so partial failures can be unwound + * with the device that created each mapping. + */ + ctlr->cur_tx_dma_dev = tx_dev; + ctlr->cur_rx_dma_dev = rx_dev; + list_for_each_entry(xfer, &msg->transfers, transfer_list) { /* The sync is done before each transfer. */ unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC; @@ -1267,8 +1275,8 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE, attrs); - if (ret != 0) - return ret; + if (ret) + goto unwind; xfer->tx_sg_mapped = true; } @@ -1277,25 +1285,19 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) ret = spi_map_buf_attrs(ctlr, rx_dev, &xfer->rx_sg, xfer->rx_buf, xfer->len, DMA_FROM_DEVICE, attrs); - if (ret != 0) { - spi_unmap_buf_attrs(ctlr, tx_dev, - &xfer->tx_sg, DMA_TO_DEVICE, - attrs); - - return ret; - } + if (ret) + goto unwind; xfer->rx_sg_mapped = true; } } - /* No transfer has been mapped, bail out with success */ - if (ret) - return 0; - - ctlr->cur_rx_dma_dev = rx_dev; - ctlr->cur_tx_dma_dev = tx_dev; return 0; + +unwind: + __spi_unmap_msg(ctlr, msg); + + return ret; } static int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg) diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c index f1f9f6fbc00e52..1d7186eecd23ed 100644 --- a/drivers/staging/greybus/hid.c +++ b/drivers/staging/greybus/hid.c @@ -256,7 +256,7 @@ static int __gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf, if (report_id && ret >= 0) ret++; /* add report_id to the number of transferred bytes */ - return 0; + return ret; } static int gb_hid_raw_request(struct hid_device *hid, unsigned char reportnum, diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c index cb2f49f3e0fa31..bf262c01a2b803 100644 --- a/drivers/staging/media/ipu7/ipu7-isys.c +++ b/drivers/staging/media/ipu7/ipu7-isys.c @@ -773,6 +773,7 @@ static int isys_probe(struct auxiliary_device *auxdev, return 0; out_cleanup: + isys_notifier_cleanup(isys); isys_unregister_devices(isys); out_cleanup_fw: ipu7_fw_isys_release(isys); diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index a039d925c0fe56..c18bf352b1996c 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -504,8 +504,8 @@ vdec_try_fmt_common(struct amvdec_session *sess, u32 size, case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: fmt_out = find_format(fmts, size, pixmp->pixelformat); if (!fmt_out) { - pixmp->pixelformat = V4L2_PIX_FMT_MPEG2; - fmt_out = find_format(fmts, size, pixmp->pixelformat); + pixmp->pixelformat = fmts[0].pixfmt; + fmt_out = &fmts[0]; } break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index bbd186b8035b20..96acd52e380c87 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -285,8 +285,10 @@ static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx) ctrl_size = sizeof(ctrl) * CEDRUS_CONTROLS_COUNT + 1; ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL); - if (!ctx->ctrls) + if (!ctx->ctrls) { + v4l2_ctrl_handler_free(hdl); return -ENOMEM; + } j = 0; for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) { diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 456134a9e8cf26..f461e117305e04 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -1257,6 +1257,7 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi) struct device_node *parent; struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; unsigned int lanes; + int err; int ret = 0; ports = of_get_child_by_name(node, "ports"); @@ -1267,8 +1268,8 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi) if (!of_node_name_eq(port, "port")) continue; - ret = of_property_read_u32(port, "reg", &port_num); - if (ret < 0) + err = of_property_read_u32(port, "reg", &port_num); + if (err < 0) continue; if (port_num > vi->soc->vi_max_channels) { @@ -1289,10 +1290,10 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi) ep = of_graph_get_endpoint_by_regs(parent, 0, 0); of_node_put(parent); - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), + err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep); of_node_put(ep); - if (ret) + if (err) continue; lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; diff --git a/drivers/staging/media/tegra-video/vip.c b/drivers/staging/media/tegra-video/vip.c index 9ff1f1750a1545..5fba11e31e1d06 100644 --- a/drivers/staging/media/tegra-video/vip.c +++ b/drivers/staging/media/tegra-video/vip.c @@ -126,7 +126,7 @@ static int tegra_vip_channel_of_parse(struct tegra_vip *vip) if (!ep) { err = -EINVAL; dev_err_probe(dev, err, "%pOF: error getting endpoint node\n", np); - goto err_node_put; + return err; } fwh = of_fwnode_handle(ep); @@ -134,14 +134,14 @@ static int tegra_vip_channel_of_parse(struct tegra_vip *vip) of_node_put(ep); if (err) { dev_err_probe(dev, err, "%pOF: failed to parse v4l2 endpoint\n", np); - goto err_node_put; + return err; } num_pads = of_graph_get_endpoint_count(np); if (num_pads != TEGRA_VIP_PADS_NUM) { err = -EINVAL; dev_err_probe(dev, err, "%pOF: need 2 pads, got %d\n", np, num_pads); - goto err_node_put; + return err; } vip->chan.of_node = of_node_get(np); @@ -149,10 +149,6 @@ static int tegra_vip_channel_of_parse(struct tegra_vip *vip) vip->chan.pads[TEGRA_VIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; return 0; - -err_node_put: - of_node_put(np); - return err; } static int tegra_vip_channel_init(struct tegra_vip *vip) diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c index e60ed33598662c..b6e7de23520476 100644 --- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c @@ -737,6 +737,10 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_att u16 attr_data_len = get_unaligned_be16(attr_ptr + 2); u16 attr_len = attr_data_len + 4; + /* Reject attributes whose claimed length runs past the IE */ + if (attr_ptr + attr_len > wps_ie + wps_ielen) + break; + if (attr_id == target_attr_id) { target_attr_ptr = attr_ptr; @@ -1145,6 +1149,9 @@ int rtw_action_frame_parse(const u8 *frame, u32 frame_len, u8 *category, u8 *act u8 c; u8 a = ACT_PUBLIC_MAX; + if (frame_len < sizeof(struct ieee80211_hdr_3addr) + 2) + return false; + fc = le16_to_cpu(((struct ieee80211_hdr_3addr *)frame)->frame_control); if ((fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) != diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c index 1196ec011455b8..7bdc5fe6dc8af7 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c @@ -1980,6 +1980,9 @@ int rtw_restruct_wmm_ie(struct adapter *adapter, u8 *in_ie, u8 *out_ie, uint in_ break; } + if (i + 1 >= in_len) + break; + i += (in_ie[i + 1] + 2); /* to the next IE element */ } diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index aafc94bcb6350c..c282b6a7029684 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -47,7 +47,7 @@ static struct iscsi_login *iscsi_login_init_conn(struct iscsit_conn *conn) login->conn = conn; login->first_request = 1; - login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL); + login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS + 1, GFP_KERNEL); if (!login->req_buf) { pr_err("Unable to allocate memory for response buffer.\n"); goto out_login; diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 166dbf4c40616e..ab8f81650710e8 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -690,6 +690,14 @@ static void target_fabric_port_unlink( } core_dev_del_lun(se_tpg, lun); + + if (tf->tf_ops->fabric_post_unlink) { + /* + * Allow fabrics to release state that must remain valid until + * core_dev_del_lun() has drained all active LUN references. + */ + tf->tf_ops->fabric_post_unlink(se_tpg, lun); + } } static void target_fabric_port_release(struct config_item *item) diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 5aaacbc5347869..b0e88b6a9352a5 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -832,12 +832,12 @@ static int imx_thermal_runtime_resume(struct device *dev) ret = regmap_write(map, socdata->sensor_ctrl + REG_CLR, socdata->power_down_mask); if (ret) - return ret; + goto disable_clk; ret = regmap_write(map, socdata->sensor_ctrl + REG_SET, socdata->measure_temp_mask); if (ret) - return ret; + goto disable_clk; /* * According to the temp sensor designers, it may require up to ~17us @@ -846,6 +846,11 @@ static int imx_thermal_runtime_resume(struct device *dev) usleep_range(20, 50); return 0; + +disable_clk: + clk_disable_unprepare(data->thermal_clk); + + return ret; } static const struct dev_pm_ops imx_thermal_pm_ops = { diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c index 35439ec5f8bcbd..297724e81593fe 100644 --- a/drivers/thermal/qoriq_thermal.c +++ b/drivers/thermal/qoriq_thermal.c @@ -415,11 +415,20 @@ static int qoriq_tmu_resume(struct device *dev) if (data->ver > TMU_VER1) { ret = regmap_clear_bits(data->regmap, REGS_TMR, TMR_CMD); if (ret) - return ret; + goto disable_clk; } /* Enable monitoring */ - return regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME); + ret = regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME); + if (ret) + goto disable_clk; + + return 0; + +disable_clk: + clk_disable_unprepare(data->clk); + + return ret; } static DEFINE_SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 251a50c8aa3872..b0f34a6e7d4f84 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -2080,6 +2081,9 @@ static const struct uart_ops imx_uart_pops = { static struct imx_port *imx_uart_ports[UART_NR]; +/* Held across uart_add/remove_one_port(); console callbacks must not take it. */ +static DEFINE_MUTEX(imx_uart_ports_lock); + #if IS_ENABLED(CONFIG_SERIAL_IMX_CONSOLE) static void imx_uart_console_putchar(struct uart_port *port, unsigned char ch) { @@ -2632,11 +2636,19 @@ static int imx_uart_probe(struct platform_device *pdev) } } - imx_uart_ports[sport->port.line] = sport; - platform_set_drvdata(pdev, sport); - ret = uart_add_one_port(&imx_uart_uart_driver, &sport->port); + scoped_guard(mutex, &imx_uart_ports_lock) { + if (imx_uart_ports[sport->port.line]) { + ret = -EBUSY; + } else { + imx_uart_ports[sport->port.line] = sport; + ret = uart_add_one_port(&imx_uart_uart_driver, + &sport->port); + if (ret) + imx_uart_ports[sport->port.line] = NULL; + } + } err_clk: clk_disable_unprepare(sport->clk_ipg); @@ -2648,7 +2660,9 @@ static void imx_uart_remove(struct platform_device *pdev) { struct imx_port *sport = platform_get_drvdata(pdev); + guard(mutex)(&imx_uart_ports_lock); uart_remove_one_port(&imx_uart_uart_driver, &sport->port); + imx_uart_ports[sport->port.line] = NULL; } static void imx_uart_restore_context(struct imx_port *sport) diff --git a/drivers/usb/c67x00/c67x00-sched.c b/drivers/usb/c67x00/c67x00-sched.c index a832f5696f2a90..ae6c94f41cb3c2 100644 --- a/drivers/usb/c67x00/c67x00-sched.c +++ b/drivers/usb/c67x00/c67x00-sched.c @@ -761,13 +761,13 @@ static int c67x00_add_iso_urb(struct c67x00_hcd *c67x00, struct urb *urb) ret); urb->iso_frame_desc[urbp->cnt].actual_length = 0; urb->iso_frame_desc[urbp->cnt].status = ret; - if (urbp->cnt + 1 == urb->number_of_packets) - c67x00_giveback_urb(c67x00, urb, 0); } urbp->ep_data->next_frame = frame_add(urbp->ep_data->next_frame, urb->interval); urbp->cnt++; + if (ret && urbp->cnt == urb->number_of_packets) + c67x00_giveback_urb(c67x00, urb, 0); } return 0; } diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index 7a516e509198b8..e84405352caaba 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -1338,7 +1338,6 @@ static int cdnsp_run(struct cdnsp_device *pdev, cdnsp_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); - ret = cdnsp_start(pdev); if (ret) { ret = -ENODEV; @@ -1837,6 +1836,82 @@ static void cdnsp_get_rev_cap(struct cdnsp_device *pdev) readl(&pdev->rev_cap->tx_buff_size)); } +static void cdnsp_set_event_deq(struct cdnsp_device *pdev) +{ + dma_addr_t deq; + u64 temp; + + deq = cdnsp_trb_virt_to_dma(pdev->event_ring->deq_seg, + pdev->event_ring->dequeue); + + /* Update controller event ring dequeue pointer */ + temp = cdnsp_read_64(&pdev->ir_set->erst_dequeue); + temp &= ERST_PTR_MASK; + + /* + * Don't clear the EHB bit (which is RW1C) because + * there might be more events to service. + */ + temp &= ~ERST_EHB; + + cdnsp_write_64(((u64)deq & (u64)~ERST_PTR_MASK) | temp, + &pdev->ir_set->erst_dequeue); +} + +static void cdnsp_add_interrupter(struct cdnsp_device *pdev) +{ + u64 erst_base; + u32 erst_size; + + /* Set ERST count with the number of entries in the segment table. */ + erst_size = readl(&pdev->ir_set->erst_size); + erst_size &= ERST_SIZE_MASK; + erst_size |= ERST_NUM_SEGS; + writel(erst_size, &pdev->ir_set->erst_size); + + /* Set the segment table base address. */ + erst_base = cdnsp_read_64(&pdev->ir_set->erst_base); + erst_base &= ERST_PTR_MASK; + erst_base |= (pdev->erst.erst_dma_addr & (u64)~ERST_PTR_MASK); + cdnsp_write_64(erst_base, &pdev->ir_set->erst_base); + + /* Set the event ring dequeue address. */ + cdnsp_set_event_deq(pdev); +} + +/* Set up basic CDNSP registers */ +static void cdnsp_init(struct cdnsp_device *pdev) +{ + unsigned int val; + u64 val_64; + + val = readl(&pdev->op_regs->config_reg); + val |= ((val & ~MAX_DEVS) | CDNSP_DEV_MAX_SLOTS) | CONFIG_U3E; + writel(val, &pdev->op_regs->config_reg); + + /* Initialize the Command ring */ + cdnsp_ring_init(pdev, pdev->cmd_ring); + + /* Set the address in the Command Ring Control register */ + val_64 = cdnsp_read_64(&pdev->op_regs->cmd_ring); + val_64 = (val_64 & (u64)CMD_RING_RSVD_BITS) | + (pdev->cmd_ring->first_seg->dma & (u64)~CMD_RING_RSVD_BITS) | + pdev->cmd_ring->cycle_state; + cdnsp_write_64(val_64, &pdev->op_regs->cmd_ring); + + /* Set Device Context Base Address Array pointer */ + cdnsp_write_64(pdev->dcbaa->dma, &pdev->op_regs->dcbaa_ptr); + + /* Set Doorbell array pointer */ + val = readl(&pdev->cap_regs->db_off); + val &= DBOFF_MASK; + pdev->dba = (void __iomem *)pdev->cap_regs + val; + + /* Initialize the Primary interrupter */ + cdnsp_ring_init(pdev, pdev->event_ring); + cdnsp_add_interrupter(pdev); +} + static int cdnsp_gen_setup(struct cdnsp_device *pdev) { int ret; @@ -1902,6 +1977,8 @@ static int cdnsp_gen_setup(struct cdnsp_device *pdev) if (ret) return ret; + cdnsp_init(pdev); + /* * Software workaround for U1: after transition * to U1 the controller starts gating clock, and in some cases, @@ -2031,9 +2108,6 @@ static int cdnsp_gadget_suspend(struct cdns *cdns, bool do_wakeup) struct cdnsp_device *pdev = cdns->gadget_dev; unsigned long flags; - if (pdev->link_state == XDEV_U3) - return 0; - spin_lock_irqsave(&pdev->lock, flags); cdnsp_disconnect_gadget(pdev); cdnsp_stop(pdev); @@ -2047,12 +2121,38 @@ static int cdnsp_gadget_resume(struct cdns *cdns, bool lost_power) struct cdnsp_device *pdev = cdns->gadget_dev; enum usb_device_speed max_speed; unsigned long flags; + bool context_lost; + u32 val; int ret; if (!pdev->gadget_driver) return 0; spin_lock_irqsave(&pdev->lock, flags); + val = readl(&pdev->port3x_regs->mode_2); + context_lost = !!(val & CFG_3XPORT_U1_PIPE_CLK_GATE_EN) || lost_power; + + if (context_lost) { + cdnsp_halt(pdev); + cdnsp_set_apb_timeout_value(pdev); + + /* Reset the internal controller memory state and registers. */ + ret = cdnsp_reset(pdev); + if (ret) + goto unlock; + + val = readl(&pdev->port3x_regs->mode_2); + val &= ~CFG_3XPORT_U1_PIPE_CLK_GATE_EN; + writel(val, &pdev->port3x_regs->mode_2); + + cdnsp_clear_cmd_ring(pdev); + + memset(pdev->event_ring->first_seg->trbs, 0, + sizeof(union cdnsp_trb) * (TRBS_PER_SEGMENT)); + + cdnsp_init(pdev); + } + max_speed = pdev->gadget_driver->max_speed; /* Limit speed if necessary. */ @@ -2060,9 +2160,10 @@ static int cdnsp_gadget_resume(struct cdns *cdns, bool lost_power) ret = cdnsp_run(pdev, max_speed); - if (pdev->link_state == XDEV_U3) + if (!context_lost && pdev->link_state == XDEV_U3) __cdnsp_gadget_wakeup(pdev); +unlock: spin_unlock_irqrestore(&pdev->lock, flags); return ret; diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h index c44bca348a419d..c3ae5040f9ccc8 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.h +++ b/drivers/usb/cdns3/cdnsp-gadget.h @@ -1510,6 +1510,7 @@ int cdnsp_endpoint_init(struct cdnsp_device *pdev, int cdnsp_ring_expansion(struct cdnsp_device *pdev, struct cdnsp_ring *ring, unsigned int num_trbs, gfp_t flags); +void cdnsp_ring_init(struct cdnsp_device *pdev, struct cdnsp_ring *ring); struct cdnsp_ring *cdnsp_dma_to_transfer_ring(struct cdnsp_ep *ep, u64 address); int cdnsp_alloc_stream_info(struct cdnsp_device *pdev, struct cdnsp_ep *pep, diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c index 83f3384b735dfb..419309c8439e37 100644 --- a/drivers/usb/cdns3/cdnsp-mem.c +++ b/drivers/usb/cdns3/cdnsp-mem.c @@ -394,13 +394,6 @@ static struct cdnsp_ring *cdnsp_ring_alloc(struct cdnsp_device *pdev, if (ret) goto fail; - /* Only event ring does not use link TRB. */ - if (type != TYPE_EVENT) - ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= - cpu_to_le32(LINK_TOGGLE); - - cdnsp_initialize_ring_info(ring); - trace_cdnsp_ring_alloc(ring); return ring; fail: kfree(ring); @@ -603,6 +596,7 @@ int cdnsp_alloc_stream_info(struct cdnsp_device *pdev, if (!cur_ring) goto cleanup_rings; + cdnsp_ring_init(pdev, cur_ring); cur_ring->stream_id = cur_stream; cur_ring->trb_address_map = &stream_info->trb_address_map; @@ -698,6 +692,8 @@ static int cdnsp_alloc_priv_device(struct cdnsp_device *pdev) if (!pdev->eps[0].ring) goto fail; + cdnsp_ring_init(pdev, pdev->eps[0].ring); + /* Point to output device context in dcbaa. */ pdev->dcbaa->dev_context_ptrs[1] = cpu_to_le64(pdev->out_ctx.dma); pdev->cmd.in_ctx = &pdev->in_ctx; @@ -991,6 +987,8 @@ int cdnsp_endpoint_init(struct cdnsp_device *pdev, if (!pep->ring) return -ENOMEM; + cdnsp_ring_init(pdev, pep->ring); + pep->skip = false; /* Fill the endpoint context */ @@ -1096,28 +1094,6 @@ void cdnsp_mem_cleanup(struct cdnsp_device *pdev) pdev->active_port = NULL; } -static void cdnsp_set_event_deq(struct cdnsp_device *pdev) -{ - dma_addr_t deq; - u64 temp; - - deq = cdnsp_trb_virt_to_dma(pdev->event_ring->deq_seg, - pdev->event_ring->dequeue); - - /* Update controller event ring dequeue pointer */ - temp = cdnsp_read_64(&pdev->ir_set->erst_dequeue); - temp &= ERST_PTR_MASK; - - /* - * Don't clear the EHB bit (which is RW1C) because - * there might be more events to service. - */ - temp &= ~ERST_EHB; - - cdnsp_write_64(((u64)deq & (u64)~ERST_PTR_MASK) | temp, - &pdev->ir_set->erst_dequeue); -} - static void cdnsp_add_in_port(struct cdnsp_device *pdev, struct cdnsp_port *port, __le32 __iomem *addr) @@ -1226,6 +1202,36 @@ static int cdnsp_setup_port_arrays(struct cdnsp_device *pdev) return 0; } +static void cdnsp_initialize_ring_segments(struct cdnsp_device *pdev, struct cdnsp_ring *ring) +{ + struct cdnsp_segment *seg; + + /* Only event ring does not use link TRB. */ + if (ring->type == TYPE_EVENT) + return; + + seg = ring->first_seg; + + while (seg) { + struct cdnsp_segment *next = seg->next; + + cdnsp_link_segments(pdev, seg, next, ring->type); + if (next == ring->first_seg) + break; + + seg = next; + } + + ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= cpu_to_le32(LINK_TOGGLE); +} + +void cdnsp_ring_init(struct cdnsp_device *pdev, struct cdnsp_ring *ring) +{ + cdnsp_initialize_ring_segments(pdev, ring); + cdnsp_initialize_ring_info(ring); + trace_cdnsp_ring_alloc(ring); +} + /* * Initialize memory for CDNSP (one-time init). * @@ -1237,10 +1243,8 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) { struct device *dev = pdev->dev; int ret = -ENOMEM; - unsigned int val; dma_addr_t dma; u32 page_size; - u64 val_64; /* * Use 4K pages, since that's common and the minimum the @@ -1248,10 +1252,6 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) */ page_size = 1 << 12; - val = readl(&pdev->op_regs->config_reg); - val |= ((val & ~MAX_DEVS) | CDNSP_DEV_MAX_SLOTS) | CONFIG_U3E; - writel(val, &pdev->op_regs->config_reg); - /* * Doorbell array must be physically contiguous * and 64-byte (cache line) aligned. @@ -1263,8 +1263,6 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) pdev->dcbaa->dma = dma; - cdnsp_write_64(dma, &pdev->op_regs->dcbaa_ptr); - /* * Initialize the ring segment pool. The ring must be a contiguous * structure comprised of TRBs. The TRBs must be 16 byte aligned, @@ -1290,17 +1288,6 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) if (!pdev->cmd_ring) goto destroy_device_pool; - /* Set the address in the Command Ring Control register */ - val_64 = cdnsp_read_64(&pdev->op_regs->cmd_ring); - val_64 = (val_64 & (u64)CMD_RING_RSVD_BITS) | - (pdev->cmd_ring->first_seg->dma & (u64)~CMD_RING_RSVD_BITS) | - pdev->cmd_ring->cycle_state; - cdnsp_write_64(val_64, &pdev->op_regs->cmd_ring); - - val = readl(&pdev->cap_regs->db_off); - val &= DBOFF_MASK; - pdev->dba = (void __iomem *)pdev->cap_regs + val; - /* Set ir_set to interrupt register set 0 */ pdev->ir_set = &pdev->run_regs->ir_set[0]; @@ -1317,21 +1304,6 @@ int cdnsp_mem_init(struct cdnsp_device *pdev) if (ret) goto free_event_ring; - /* Set ERST count with the number of entries in the segment table. */ - val = readl(&pdev->ir_set->erst_size); - val &= ERST_SIZE_MASK; - val |= ERST_NUM_SEGS; - writel(val, &pdev->ir_set->erst_size); - - /* Set the segment table base address. */ - val_64 = cdnsp_read_64(&pdev->ir_set->erst_base); - val_64 &= ERST_PTR_MASK; - val_64 |= (pdev->erst.erst_dma_addr & (u64)~ERST_PTR_MASK); - cdnsp_write_64(val_64, &pdev->ir_set->erst_base); - - /* Set the event ring dequeue address. */ - cdnsp_set_event_deq(pdev); - ret = cdnsp_setup_port_arrays(pdev); if (ret) goto free_erst; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index e191934623c736..486ced74405e3f 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1113,7 +1113,6 @@ static int usbdev_release(struct inode *inode, struct file *file) if (!ps->suspend_allowed) usb_autosuspend_device(dev); usb_unlock_device(dev); - usb_put_dev(dev); put_pid(ps->disc_pid); put_cred(ps->cred); @@ -1122,6 +1121,7 @@ static int usbdev_release(struct inode *inode, struct file *file) free_async(as); as = async_getcompleted(ps); } + usb_put_dev(dev); kfree(ps); return 0; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index d92bf887739d08..068c7bea310201 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -753,10 +753,12 @@ void usb_wakeup_notification(struct usb_device *hdev, { struct usb_hub *hub; struct usb_port *port_dev; + unsigned long flags; if (!hdev) return; + spin_lock_irqsave(&device_state_lock, flags); hub = usb_hub_to_struct_hub(hdev); if (hub) { port_dev = hub->ports[portnum - 1]; @@ -766,6 +768,7 @@ void usb_wakeup_notification(struct usb_device *hdev, set_bit(portnum, hub->wakeup_bits); kick_hub_wq(hub); } + spin_unlock_irqrestore(&device_state_lock, flags); } EXPORT_SYMBOL_GPL(usb_wakeup_notification); @@ -991,10 +994,12 @@ static int hub_hub_status(struct usb_hub *hub, mutex_lock(&hub->status_mutex); ret = get_hub_status(hub->hdev, &hub->status->hub); - if (ret < 0) { + if (ret < (int)sizeof(hub->status->hub)) { if (ret != -ENODEV) dev_err(hub->intfdev, "%s failed (err = %d)\n", __func__, ret); + if (ret >= 0) + ret = -EIO; } else { *status = le16_to_cpu(hub->status->hub.wHubStatus); *change = le16_to_cpu(hub->status->hub.wHubChange); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index c8b02c27d27d4f..102169b404196c 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -4680,6 +4680,7 @@ static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on) { struct dwc2_hsotg *hsotg = to_hsotg(gadget); unsigned long flags; + int ret = 0; dev_dbg(hsotg->dev, "%s: is_on: %d op_state: %d\n", __func__, is_on, hsotg->op_state); @@ -4691,6 +4692,13 @@ static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on) } spin_lock_irqsave(&hsotg->lock, flags); + if (hsotg->in_ppd) { + ret = dwc2_exit_partial_power_down(hsotg, 0, true); + if (ret) { + dev_err(hsotg->dev, "exit partial_power_down failed\n"); + goto exit; + } + } if (is_on) { hsotg->enabled = 1; dwc2_hsotg_core_init_disconnected(hsotg, false); @@ -4704,9 +4712,10 @@ static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on) } hsotg->gadget.speed = USB_SPEED_UNKNOWN; +exit: spin_unlock_irqrestore(&hsotg->lock, flags); - return 0; + return ret; } static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) diff --git a/drivers/usb/dwc3/dwc3-google.c b/drivers/usb/dwc3/dwc3-google.c index 60ee4cc99b286b..a01ca23cb6a8ad 100644 --- a/drivers/usb/dwc3/dwc3-google.c +++ b/drivers/usb/dwc3/dwc3-google.c @@ -442,6 +442,7 @@ static int dwc3_google_probe(struct platform_device *pdev) probe_data.dwc = &google->dwc; probe_data.res = res; probe_data.ignore_clocks_and_resets = true; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; ret = dwc3_core_probe(&probe_data); if (ret) { ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index bfe616194dfad4..310b5ffb236aa2 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -304,7 +304,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc) dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP; if (dwc->connected) - dwc3_stop_active_transfer(dwc3_ep, true, true); + dwc3_stop_active_transfer(dwc3_ep, false, true); else dwc3_remove_requests(dwc, dwc3_ep, -ESHUTDOWN); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1082e9c9afaa89..e46db7032089f1 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1004,7 +1004,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) * controller to generate an ERDY to initiate the * stream. */ - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); /* * All stream eps will reinitiate stream on NoStream @@ -1032,7 +1032,7 @@ void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep, int status) { struct dwc3_request *req; - dwc3_stop_active_transfer(dep, true, false); + dwc3_stop_active_transfer(dep, false, false); /* If endxfer is delayed, avoid unmapping requests */ if (dep->flags & DWC3_EP_DELAY_STOP) @@ -1720,7 +1720,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) if (ret == -EAGAIN) return ret; - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); list_for_each_entry_safe(req, tmp, &dep->started_list, list) dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED); @@ -1757,6 +1757,11 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc) * the controller won't update the TRB progress on command * completion. It also won't clear the HWO bit in the TRB. * The command will also not complete immediately in that case. + * + * Older programming guide revisions recommended setting ForceRM to 1 + * when ending a transfer. Newer programming guide revisions now + * recommend keeping ForceRM cleared, and TRBs are properly updated + * on command completion. */ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt) { @@ -1882,7 +1887,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep) * to wait for the next XferNotReady to test the command again */ if (cmd_status == 0) { - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); return 0; } } @@ -2165,7 +2170,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, struct dwc3_request *t; /* wait until it is processed */ - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); /* * Remove any started request if the transfer is @@ -2242,7 +2247,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) return 0; } - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); list_for_each_entry_safe(req, tmp, &dep->started_list, list) dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED); @@ -3357,7 +3362,7 @@ static void dwc3_nostream_work(struct work_struct *work) dwc3_send_gadget_generic_command(dwc, cmd, dep->number); } else { dep->flags |= DWC3_EP_DELAY_START; - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); spin_unlock_irqrestore(&dwc->lock, flags); return; } @@ -3497,6 +3502,7 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) } dwc3_debugfs_remove_endpoint_dir(dep); + cancel_delayed_work_sync(&dep->nostream_work); kfree(dep); } } @@ -3714,7 +3720,7 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep, if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && list_empty(&dep->started_list) && (list_empty(&dep->pending_list) || status == -EXDEV)) - dwc3_stop_active_transfer(dep, true, true); + dwc3_stop_active_transfer(dep, false, true); else if (dwc3_gadget_ep_should_continue(dep)) if (__dwc3_gadget_kick_transfer(dep) == 0) no_started_trb = false; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 44218be1e6768e..4ec37c1fdd22e0 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -548,6 +548,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED) return -EIDRM; +retry: /* Acquire mutex */ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); if (ret < 0) @@ -582,10 +583,15 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, break; } - if (wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq, - ffs->ev.count)) { - ret = -EINTR; - break; + if (!ffs->ev.count) { + spin_unlock_irq(&ffs->ev.waitq.lock); + mutex_unlock(&ffs->mutex); + + if (wait_event_interruptible_exclusive(ffs->ev.waitq, + ffs->ev.count)) + return -EINTR; + + goto retry; } /* unlocks spinlock */ @@ -867,9 +873,15 @@ static void ffs_user_copy_worker(struct work_struct *work) bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD; if (io_data->read && ret > 0) { - kthread_use_mm(io_data->mm); - ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data); - kthread_unuse_mm(io_data->mm); + if (mmget_not_zero(io_data->mm)) { + kthread_use_mm(io_data->mm); + ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data); + kthread_unuse_mm(io_data->mm); + mmput(io_data->mm); + } else { + ret = -EFAULT; + } + mmdrop(io_data->mm); } io_data->kiocb->ki_complete(io_data->kiocb, ret); @@ -1264,16 +1276,22 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) kiocb->private = p; - if (p->aio) + if (p->aio) { + mmgrab(p->mm); kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); + } res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; - if (p->aio) + if (p->aio) { + kiocb->ki_complete(kiocb, res); + mmdrop(p->mm); kfree(p); - else + return -EIOCBQUEUED; + } else { *from = p->data; + } return res; } @@ -1308,16 +1326,21 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) kiocb->private = p; - if (p->aio) + if (p->aio) { + mmgrab(p->mm); kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); + } res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; if (p->aio) { + kiocb->ki_complete(kiocb, res); + mmdrop(p->mm); kfree(p->to_free); kfree(p); + return -EIOCBQUEUED; } else { *to = p->data; } diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index b7b06cb79ff5e2..8a6ae944d436c4 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -2748,6 +2748,9 @@ int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n) struct fsg_buffhd *bh, *buffhds; int i; + if (n < 2) + return -EINVAL; + buffhds = kzalloc_objs(*buffhds, n); if (!buffhds) return -ENOMEM; @@ -2961,7 +2964,7 @@ EXPORT_SYMBOL_GPL(fsg_common_create_lun); int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg) { - char buf[8]; /* enough for 100000000 different numbers, decimal */ + char buf[14]; int i, rc; fsg_common_remove_luns(common); diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index fba8cf787d6c1c..63fb6ee70a3d65 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -879,7 +879,6 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0; midi->gadget = cdev->gadget; - INIT_WORK(&midi->work, f_midi_in_work); status = f_midi_register_card(midi); if (status < 0) goto fail_register; @@ -1377,6 +1376,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) status = -ENOMEM; goto midi_free; } + INIT_WORK(&midi->work, f_midi_in_work); midi->out_ports = opts->out_ports; midi->index = opts->index; midi->buflen = opts->buflen; diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index 19fdac0243439c..5b8b18281989c0 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -1145,7 +1145,7 @@ static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep) if (!usb_ep->reqs) return -EINVAL; - for (i = 0; i < midi2->info.num_reqs; i++) { + for (i = 0; i < usb_ep->num_reqs; i++) { if (usb_ep->reqs[i].req) continue; usb_ep->reqs[i].req = alloc_ep_req(usb_ep->usb_ep, @@ -1160,10 +1160,9 @@ static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep) /* Free allocated requests */ static void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep) { - struct f_midi2 *midi2 = usb_ep->card; int i; - for (i = 0; i < midi2->info.num_reqs; i++) { + for (i = 0; i < usb_ep->num_reqs; i++) { if (!usb_ep->reqs[i].req) continue; free_ep_req(usb_ep->usb_ep, usb_ep->reqs[i].req); @@ -2178,13 +2177,13 @@ static ssize_t f_midi2_opts_bool_store(struct f_midi2_opts *opts, /* generic show/store for string */ static ssize_t f_midi2_opts_str_show(struct f_midi2_opts *opts, - const char *str, char *page) + const char **strp, char *page) { int result = 0; mutex_lock(&opts->lock); - if (str) - result = scnprintf(page, PAGE_SIZE, "%s\n", str); + if (*strp) + result = scnprintf(page, PAGE_SIZE, "%s\n", *strp); mutex_unlock(&opts->lock); return result; } @@ -2278,7 +2277,7 @@ static ssize_t f_midi2_block_opts_name_show(struct config_item *item, { struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); - return f_midi2_opts_str_show(opts->ep->opts, opts->info.name, page); + return f_midi2_opts_str_show(opts->ep->opts, &opts->info.name, page); } static ssize_t f_midi2_block_opts_name_store(struct config_item *item, @@ -2435,7 +2434,7 @@ static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \ char *page) \ { \ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \ - return f_midi2_opts_str_show(opts->opts, opts->info.name, page);\ + return f_midi2_opts_str_show(opts->opts, &opts->info.name, page);\ } \ \ static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \ @@ -2473,6 +2472,7 @@ static void f_midi2_ep_opts_release(struct config_item *item) { struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); + configfs_remove_default_groups(&opts->group); kfree(opts->info.ep_name); kfree(opts->info.product_id); kfree(opts); @@ -2589,7 +2589,7 @@ static ssize_t f_midi2_opts_iface_name_show(struct config_item *item, { struct f_midi2_opts *opts = to_f_midi2_opts(item); - return f_midi2_opts_str_show(opts, opts->info.iface_name, page); + return f_midi2_opts_str_show(opts, &opts->info.iface_name, page); } static ssize_t f_midi2_opts_iface_name_store(struct config_item *item, @@ -2639,6 +2639,7 @@ static void f_midi2_free_inst(struct usb_function_instance *f) opts = container_of(f, struct f_midi2_opts, func_inst); + configfs_remove_default_groups(&opts->func_inst.group); kfree(opts->info.iface_name); kfree(opts); } diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index b3fa5a17fd2dba..9e6d4f39900a27 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -1675,19 +1675,25 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn, opts = container_of(tpg_instances[i].func_inst, struct f_tcm_opts, func_inst); - mutex_lock(&opts->dep_lock); - if (!opts->ready) - goto unlock_dep; + if (!READ_ONCE(opts->ready)) + goto unlock_inst; if (opts->has_dep) { if (!try_module_get(opts->dependent)) - goto unlock_dep; + goto unlock_inst; } else { + /* + * configfs_depend_item_unlocked() may acquire the configfs + * root inode lock when the target belongs to a different + * subsystem. Calling it under dep_lock would create a + * circular dependency: + * dep_lock -> configfs inode lock -> su_mutex -> dep_lock + */ ret = configfs_depend_item_unlocked( wwn->wwn_group.cg_subsys, &opts->func_inst.group.cg_item); if (ret) - goto unlock_dep; + goto unlock_inst; } tpg = kzalloc_obj(struct usbg_tpg); @@ -1714,7 +1720,6 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn, tpg_instances[i].tpg = tpg; tpg->fi = tpg_instances[i].func_inst; - mutex_unlock(&opts->dep_lock); mutex_unlock(&tpg_instances_lock); return &tpg->se_tpg; @@ -1727,8 +1732,6 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn, module_put(opts->dependent); else configfs_undepend_item_unlocked(&opts->func_inst.group.cg_item); -unlock_dep: - mutex_unlock(&opts->dep_lock); unlock_inst: mutex_unlock(&tpg_instances_lock); @@ -2024,7 +2027,7 @@ static const struct target_core_fabric_ops usbg_ops = { .fabric_enable_tpg = usbg_enable_tpg, .fabric_drop_tpg = usbg_drop_tpg, .fabric_post_link = usbg_port_link, - .fabric_pre_unlink = usbg_port_unlink, + .fabric_post_unlink = usbg_port_unlink, .fabric_init_nodeacl = usbg_init_nodeacl, .tfc_wwn_attrs = usbg_wwn_attrs, @@ -2666,9 +2669,7 @@ static int tcm_set_name(struct usb_function_instance *f, const char *name) pr_debug("tcm: Activating %s\n", name); - mutex_lock(&opts->dep_lock); - opts->ready = true; - mutex_unlock(&opts->dep_lock); + WRITE_ONCE(opts->ready, true); return 0; } diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 73dc7e42875ffb..d1bf3ea751973d 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -889,9 +889,12 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) v4l2_error: v4l2_device_unregister(&uvc->v4l2_dev); error: - if (uvc->control_req) + if (uvc->control_req) { usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); + uvc->control_req = NULL; + } kfree(uvc->control_buf); + uvc->control_buf = NULL; usb_free_all_descriptors(f); return ret; @@ -1075,7 +1078,9 @@ static void uvc_function_unbind(struct usb_configuration *c, uvc->vdev_release_done = NULL; usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); + uvc->control_req = NULL; kfree(uvc->control_buf); + uvc->control_buf = NULL; usb_free_all_descriptors(f); } diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index e53f2927b53985..391419c208d458 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -1177,6 +1177,20 @@ static struct snd_kcontrol_new u_audio_controls[] = { }, }; +static void u_audio_card_free(struct snd_card *card) +{ + struct snd_uac_chip *uac = card->private_data; + + if (!uac) + return; + + kfree(uac->p_prm.reqs); + kfree(uac->c_prm.reqs); + kfree(uac->p_prm.rbuf); + kfree(uac->c_prm.rbuf); + kfree(uac); +} + int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, const char *card_name) { @@ -1256,6 +1270,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, goto fail; uac->card = card; + card->private_data = uac; + card->private_free = u_audio_card_free; /* * Create first PCM device @@ -1424,6 +1440,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, snd_fail: snd_card_free(card); + return err; + fail: kfree(uac->p_prm.reqs); kfree(uac->c_prm.reqs); @@ -1449,12 +1467,6 @@ void g_audio_cleanup(struct g_audio *g_audio) card = uac->card; if (card) snd_card_free_when_closed(card); - - kfree(uac->p_prm.reqs); - kfree(uac->c_prm.reqs); - kfree(uac->p_prm.rbuf); - kfree(uac->c_prm.rbuf); - kfree(uac); } EXPORT_SYMBOL_GPL(g_audio_cleanup); diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 2f9700b3f1b64f..9ba09118bb74ac 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -821,7 +821,7 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) /* Allocate a kthread for asynchronous hw submit handler. */ video->kworker = kthread_run_worker(0, "UVCG"); if (IS_ERR(video->kworker)) { - uvcg_err(&video->uvc->func, "failed to create UVCG kworker\n"); + uvcg_err(&uvc->func, "failed to create UVCG kworker\n"); return PTR_ERR(video->kworker); } diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c index 203361a64212d1..70e31c40e26738 100644 --- a/drivers/usb/gadget/functions.c +++ b/drivers/usb/gadget/functions.c @@ -70,7 +70,7 @@ void usb_put_function_instance(struct usb_function_instance *fi) { struct module *mod; - if (!fi) + if (!fi || !fi->fd) return; mod = fi->fd->mod; diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index d87a8ab5151072..12819e8c265d47 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1251,10 +1251,11 @@ ep0_poll (struct file *fd, poll_table *wait) static long gadget_dev_ioctl (struct file *fd, unsigned code, unsigned long value) { struct dev_data *dev = fd->private_data; - struct usb_gadget *gadget = dev->gadget; + struct usb_gadget *gadget; long ret = -ENOTTY; spin_lock_irq(&dev->lock); + gadget = dev->gadget; if (dev->state == STATE_DEV_OPENED || dev->state == STATE_DEV_UNBOUND) { /* Not bound to a UDC */ diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 5aa360ba4f0391..8e5d7fb7154920 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1794,6 +1794,19 @@ static void at91udc_of_init(struct at91_udc *udc, struct device_node *np) udc->caps = match->data; } +/* + * The work handler re-arms this timer, so shut the timer down before + * draining the work; otherwise it restarts the polling cycle. + */ +static void at91_udc_shutdown_vbus_timer(struct at91_udc *udc) +{ + if (!(udc->board.vbus_pin && udc->board.vbus_polled)) + return; + + timer_shutdown_sync(&udc->vbus_timer); + cancel_work_sync(&udc->vbus_timer_work); +} + static int at91udc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1907,7 +1920,7 @@ static int at91udc_probe(struct platform_device *pdev) } retval = usb_add_gadget_udc(dev, &udc->gadget); if (retval) - goto err_unprepare_iclk; + goto err_shutdown_vbus; dev_set_drvdata(dev, udc); device_init_wakeup(dev, 1); create_debug_file(udc); @@ -1915,6 +1928,8 @@ static int at91udc_probe(struct platform_device *pdev) INFO("%s version %s\n", driver_name, DRIVER_VERSION); return 0; +err_shutdown_vbus: + at91_udc_shutdown_vbus_timer(udc); err_unprepare_iclk: clk_unprepare(udc->iclk); err_unprepare_fclk: @@ -1933,6 +1948,9 @@ static void at91udc_remove(struct platform_device *pdev) DBG("remove\n"); usb_del_gadget_udc(&udc->gadget); + + at91_udc_shutdown_vbus_timer(udc); + if (udc->driver) { dev_err(&pdev->dev, "Driver still in use but removing anyhow\n"); diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c index db842a6de643de..0ee3ed185d9c3b 100644 --- a/drivers/usb/gadget/udc/snps_udc_plat.c +++ b/drivers/usb/gadget/udc/snps_udc_plat.c @@ -159,10 +159,9 @@ static int udc_plat_probe(struct platform_device *pdev) if (of_property_present(dev->of_node, "extcon")) { udc->edev = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(udc->edev)) { - if (PTR_ERR(udc->edev) == -EPROBE_DEFER) - return -EPROBE_DEFER; - dev_err(dev, "Invalid or missing extcon\n"); ret = PTR_ERR(udc->edev); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Invalid or missing extcon\n"); goto exit_phy; } diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c index 2e7384c6b6ec20..7e44e62d6ea93e 100644 --- a/drivers/usb/host/xhci-dbgtty.c +++ b/drivers/usb/host/xhci-dbgtty.c @@ -628,8 +628,8 @@ int dbc_tty_init(void) dbc_tty_driver = tty_alloc_driver(64, TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV); if (IS_ERR(dbc_tty_driver)) { - idr_destroy(&dbc_tty_minors); - return PTR_ERR(dbc_tty_driver); + ret = PTR_ERR(dbc_tty_driver); + goto fail; } dbc_tty_driver->driver_name = "dbc_serial"; @@ -649,10 +649,17 @@ int dbc_tty_init(void) ret = tty_register_driver(dbc_tty_driver); if (ret) { pr_err("Can't register dbc tty driver\n"); - tty_driver_kref_put(dbc_tty_driver); - idr_destroy(&dbc_tty_minors); + goto fail_put; } + return ret; + +fail_put: + tty_driver_kref_put(dbc_tty_driver); +fail: + idr_destroy(&dbc_tty_minors); + dbc_tty_driver = NULL; + return ret; } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 4f98d8269625b8..8908f5c27e5265 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -842,21 +842,18 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, usb_hcd_giveback_urb(hcd, urb, status); } -static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, - struct xhci_ring *ring, struct xhci_td *td) +static void xhci_unmap_one_bounce_buffer(struct xhci_hcd *xhci, + struct xhci_ring *ring, struct xhci_td *td, + struct xhci_segment *seg) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - struct xhci_segment *seg = td->bounce_seg; struct urb *urb = td->urb; size_t len; - if (!ring || !seg || !urb) - return; - if (usb_urb_dir_out(urb)) { dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len, DMA_TO_DEVICE); - return; + goto done; } dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len, @@ -872,10 +869,29 @@ static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, memcpy(urb->transfer_buffer + seg->bounce_offs, seg->bounce_buf, seg->bounce_len); } +done: seg->bounce_len = 0; seg->bounce_offs = 0; } +static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, + struct xhci_ring *ring, struct xhci_td *td) +{ + struct xhci_segment *seg; + int i = 0; + + if (!td->bounce_seg || !ring || !td->urb) + return; + + /* td->bounce_seg is the last one bounced, unmap them all */ + for (seg = td->start_seg; i++ < ring->num_segs; seg = seg->next) { + if (seg->bounce_len) + xhci_unmap_one_bounce_buffer(xhci, ring, td, seg); + if (seg == td->bounce_seg) + break; + } +} + static void xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_ring *ep_ring, int status) { @@ -2025,7 +2041,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) vdev = xhci->devs[port->slot_id]; /* We might get interrupts after shared_hcd is removed */ - if (port->rhub == &xhci->usb3_rhub && xhci->shared_hcd == NULL) { + if (port->rhub == &xhci->usb3_rhub && xhci_get_usb3_hcd(xhci) == NULL) { xhci_dbg(xhci, "ignore port event for removed USB3 hcd\n"); bogus_port_status = true; goto cleanup; @@ -2600,6 +2616,17 @@ static bool xhci_spurious_success_tx_event(struct xhci_hcd *xhci, } } +static struct xhci_td *find_td_by_dma(struct xhci_ring *ep_ring, dma_addr_t dma) +{ + struct xhci_td *td; + + if (dma) + list_for_each_entry(td, &ep_ring->td_list, td_list) + if (trb_in_td(td, dma)) + return td; + return NULL; +} + /* * If this function returns an error condition, it means it got a Transfer * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address. @@ -2794,8 +2821,11 @@ static int handle_tx_event(struct xhci_hcd *xhci, xhci_dequeue_td(xhci, td, ep_ring, td->status); } - /* If the TRB pointer is NULL, missed TDs will be skipped on the next event */ - if (trb_comp_code == COMP_MISSED_SERVICE_ERROR && !ep_trb_dma) + /* + * We don't know how many TDs were missed when ep_trb_dma is zero (as permitted by + * xHCI 1.0) or bogus. Bail out leaving ep->skip set, next event will sort it out. + */ + if (trb_comp_code == COMP_MISSED_SERVICE_ERROR && !find_td_by_dma(ep_ring, ep_trb_dma)) return 0; if (list_empty(&ep_ring->td_list)) { @@ -3674,7 +3704,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, &trb_buff_len, ring->enq_seg)) { send_addr = ring->enq_seg->bounce_dma; - /* assuming TD won't span 2 segs */ + /* TD bounced at least, and last on this seg */ td->bounce_seg = ring->enq_seg; } } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 091c82ca8ee295..5c759ad28199fd 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5433,6 +5433,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) struct device *dev = hcd->self.sysdev; int retval; u32 hcs_params1; + u32 hc_capbase; /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; @@ -5453,15 +5454,19 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) mutex_init(&xhci->mutex); xhci->main_hcd = hcd; xhci->cap_regs = hcd->regs; - xhci->op_regs = hcd->regs + - HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)); + hc_capbase = readl(&xhci->cap_regs->hc_capbase); + if (hc_capbase == U32_MAX) { + xhci_warn(xhci, "Host controller not accessible, removed?\n"); + return -ENODEV; + } + xhci->op_regs = hcd->regs + HC_LENGTH(hc_capbase); xhci->run_regs = hcd->regs + (readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK); /* Cache read-only capability registers */ hcs_params1 = readl(&xhci->cap_regs->hcs_params1); xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2); xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3); - xhci->hci_version = HC_VERSION(readl(&xhci->cap_regs->hc_capbase)); + xhci->hci_version = HC_VERSION(hc_capbase); xhci->hcc_params = readl(&xhci->cap_regs->hcc_params); if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index ca287b770e8cbd..f7caa1c5cbb799 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -1000,13 +1000,13 @@ static int __init usb_mdc800_init (void) mdc800->downloaded = 0; mdc800->written = 0; - mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL); + mdc800->irq_urb_buffer=kzalloc (8, GFP_KERNEL); if (!mdc800->irq_urb_buffer) goto cleanup_on_fail; mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL); if (!mdc800->write_urb_buffer) goto cleanup_on_fail; - mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL); + mdc800->download_urb_buffer=kzalloc (64, GFP_KERNEL); if (!mdc800->download_urb_buffer) goto cleanup_on_fail; diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 98071b25ac076e..8759df49be2873 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -3054,6 +3054,7 @@ static struct usb_driver usbtest_driver = { .disconnect = usbtest_disconnect, .suspend = usbtest_suspend, .resume = usbtest_resume, + .no_dynamic_id = 1, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index 35d79f11b03d32..1986f7e9d9be2c 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -46,7 +46,7 @@ static const char driver_name[] = "fsl-usb2-otg"; -const pm_message_t otg_suspend_state = { +static const pm_message_t otg_suspend_state = { .event = 1, }; @@ -57,11 +57,11 @@ static struct fsl_otg *fsl_otg_dev; static int srp_wait_done; /* FSM timers */ -struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, +static struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr; /* Driver specific timers */ -struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr, +static struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr, *b_srp_wait_tmr, *a_wait_enum_tmr; static struct list_head active_timers; @@ -102,7 +102,7 @@ static void (*_fsl_writel)(u32 v, unsigned __iomem *p); #define fsl_writel(val, addr) writel(val, addr) #endif /* CONFIG_PPC32 */ -int write_ulpi(u8 addr, u8 data) +static int write_ulpi(u8 addr, u8 data) { u32 temp; @@ -115,7 +115,7 @@ int write_ulpi(u8 addr, u8 data) /* Operations that will be called from OTG Finite State Machine */ /* Charge vbus for vbus pulsing in SRP */ -void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on) +static void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on) { u32 tmp; @@ -133,7 +133,7 @@ void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on) } /* Discharge vbus through a resistor to ground */ -void fsl_otg_dischrg_vbus(int on) +static void fsl_otg_dischrg_vbus(int on) { u32 tmp; @@ -151,7 +151,7 @@ void fsl_otg_dischrg_vbus(int on) } /* A-device driver vbus, controlled through PP bit in PORTSC */ -void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on) +static void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on) { u32 tmp; @@ -169,7 +169,7 @@ void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on) * Pull-up D+, signalling connect by periperal. Also used in * data-line pulsing in SRP */ -void fsl_otg_loc_conn(struct otg_fsm *fsm, int on) +static void fsl_otg_loc_conn(struct otg_fsm *fsm, int on) { u32 tmp; @@ -188,7 +188,7 @@ void fsl_otg_loc_conn(struct otg_fsm *fsm, int on) * port. In host mode, controller will automatically send SOF. * Suspend will block the data on the port. */ -void fsl_otg_loc_sof(struct otg_fsm *fsm, int on) +static void fsl_otg_loc_sof(struct otg_fsm *fsm, int on) { u32 tmp; @@ -203,7 +203,7 @@ void fsl_otg_loc_sof(struct otg_fsm *fsm, int on) } /* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ -void fsl_otg_start_pulse(struct otg_fsm *fsm) +static void fsl_otg_start_pulse(struct otg_fsm *fsm) { u32 tmp; @@ -219,7 +219,7 @@ void fsl_otg_start_pulse(struct otg_fsm *fsm) fsl_otg_add_timer(fsm, b_data_pulse_tmr); } -void b_data_pulse_end(unsigned long foo) +static void b_data_pulse_end(unsigned long foo) { #ifdef HA_DATA_PULSE #else @@ -230,7 +230,7 @@ void b_data_pulse_end(unsigned long foo) fsl_otg_pulse_vbus(); } -void fsl_otg_pulse_vbus(void) +static void fsl_otg_pulse_vbus(void) { srp_wait_done = 0; fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 1); @@ -238,7 +238,7 @@ void fsl_otg_pulse_vbus(void) fsl_otg_add_timer(&fsl_otg_dev->fsm, b_vbus_pulse_tmr); } -void b_vbus_pulse_end(unsigned long foo) +static void b_vbus_pulse_end(unsigned long foo) { fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 0); @@ -251,7 +251,7 @@ void b_vbus_pulse_end(unsigned long foo) fsl_otg_add_timer(&fsl_otg_dev->fsm, b_srp_wait_tmr); } -void b_srp_end(unsigned long foo) +static void b_srp_end(unsigned long foo) { fsl_otg_dischrg_vbus(0); srp_wait_done = 1; @@ -266,7 +266,7 @@ void b_srp_end(unsigned long foo) * a_host will start by SRP. It needs to set b_hnp_enable before * actually suspending to start HNP */ -void a_wait_enum(unsigned long foo) +static void a_wait_enum(unsigned long foo) { VDBG("a_wait_enum timeout\n"); if (!fsl_otg_dev->phy.otg->host->b_hnp_enable) @@ -276,13 +276,13 @@ void a_wait_enum(unsigned long foo) } /* The timeout callback function to set time out bit */ -void set_tmout(unsigned long indicator) +static void set_tmout(unsigned long indicator) { *(int *)indicator = 1; } /* Initialize timers */ -int fsl_otg_init_timers(struct otg_fsm *fsm) +static int fsl_otg_init_timers(struct otg_fsm *fsm) { /* FSM used timers */ a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, @@ -339,7 +339,7 @@ int fsl_otg_init_timers(struct otg_fsm *fsm) } /* Uninitialize timers */ -void fsl_otg_uninit_timers(void) +static void fsl_otg_uninit_timers(void) { /* FSM used timers */ kfree(a_wait_vrise_tmr); @@ -391,7 +391,7 @@ static struct fsl_otg_timer *fsl_otg_get_timer(enum otg_fsm_timer t) } /* Add timer to timer list */ -void fsl_otg_add_timer(struct otg_fsm *fsm, void *gtimer) +static void fsl_otg_add_timer(struct otg_fsm *fsm, void *gtimer) { struct fsl_otg_timer *timer = gtimer; struct fsl_otg_timer *tmp_timer; @@ -421,7 +421,7 @@ static void fsl_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) } /* Remove timer from the timer list; clear timeout status */ -void fsl_otg_del_timer(struct otg_fsm *fsm, void *gtimer) +static void fsl_otg_del_timer(struct otg_fsm *fsm, void *gtimer) { struct fsl_otg_timer *timer = gtimer; struct fsl_otg_timer *tmp_timer, *del_tmp; @@ -443,7 +443,7 @@ static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) } /* Reset controller, not reset the bus */ -void otg_reset_controller(void) +static void otg_reset_controller(void) { u32 command; @@ -455,7 +455,7 @@ void otg_reset_controller(void) } /* Call suspend/resume routines in host driver */ -int fsl_otg_start_host(struct otg_fsm *fsm, int on) +static int fsl_otg_start_host(struct otg_fsm *fsm, int on) { struct usb_otg *otg = fsm->otg; struct device *dev; @@ -522,7 +522,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on) * Call suspend and resume function in udc driver * to stop and start udc driver. */ -int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) +static int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) { struct usb_otg *otg = fsm->otg; struct device *dev; @@ -704,7 +704,7 @@ static int fsl_otg_start_hnp(struct usb_otg *otg) * intact. It needs to have knowledge of some USB interrupts * such as port change. */ -irqreturn_t fsl_otg_isr(int irq, void *dev_id) +static irqreturn_t fsl_otg_isr(int irq, void *dev_id) { struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg; @@ -830,7 +830,7 @@ static int fsl_otg_conf(struct platform_device *pdev) } /* OTG Initialization */ -int usb_otg_start(struct platform_device *pdev) +static int usb_otg_start(struct platform_device *pdev) { struct fsl_otg *p_otg; struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2); @@ -1002,7 +1002,7 @@ static void fsl_otg_remove(struct platform_device *pdev) pdata->exit(pdev); } -struct platform_driver fsl_otg_driver = { +static struct platform_driver fsl_otg_driver = { .probe = fsl_otg_probe, .remove = fsl_otg_remove, .driver = { diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h index 95bfe7f1b83a97..b754077875c391 100644 --- a/drivers/usb/phy/phy-fsl-usb.h +++ b/drivers/usb/phy/phy-fsl-usb.h @@ -373,6 +373,6 @@ struct fsl_otg_config { #define FSL_OTG_NAME "fsl-usb2-otg" -void fsl_otg_add_timer(struct otg_fsm *fsm, void *timer); -void fsl_otg_del_timer(struct otg_fsm *fsm, void *timer); -void fsl_otg_pulse_vbus(void); +static void fsl_otg_add_timer(struct otg_fsm *fsm, void *timer); +static void fsl_otg_del_timer(struct otg_fsm *fsm, void *timer); +static void fsl_otg_pulse_vbus(void); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 580f06f5ce5e3b..e4ad14375d6a73 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2689,12 +2689,26 @@ static void option_instat_callback(struct urb *urb) dev_dbg(dev, "%s: NULL req_pkt\n", __func__); return; } + + if (urb->actual_length < sizeof(*req_pkt)) { + dev_err(dev, "%s: short packet: %u bytes\n", __func__, + urb->actual_length); + return; + } + if ((req_pkt->bRequestType == 0xA1) && (req_pkt->bRequest == 0x20)) { + unsigned char signals; int old_dcd_state; - unsigned char signals = *((unsigned char *) - urb->transfer_buffer + - sizeof(struct usb_ctrlrequest)); + + if (urb->actual_length < sizeof(*req_pkt) + 1) { + dev_err(dev, "%s: short interrupt transfer: %u bytes\n", + __func__, urb->actual_length); + return; + } + + signals = *((unsigned char *)urb->transfer_buffer + + sizeof(*req_pkt)); dev_dbg(dev, "%s: signal x%x\n", __func__, signals); diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index c11d64bf08fbae..f610aef6bf59b1 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -235,18 +235,6 @@ static void spcp8x5_set_work_mode(struct usb_serial_port *port, u16 value, dev_err(&port->dev, "failed to set work mode: %d\n", ret); } -static int spcp8x5_carrier_raised(struct usb_serial_port *port) -{ - u8 msr; - int ret; - - ret = spcp8x5_get_msr(port, &msr); - if (ret || msr & MSR_STATUS_LINE_DCD) - return 1; - - return 0; -} - static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on) { struct spcp8x5_private *priv = usb_get_serial_port_data(port); @@ -458,7 +446,6 @@ static struct usb_serial_driver spcp8x5_device = { .num_bulk_out = 1, .open = spcp8x5_open, .dtr_rts = spcp8x5_dtr_rts, - .carrier_raised = spcp8x5_carrier_raised, .set_termios = spcp8x5_set_termios, .init_termios = spcp8x5_init_termios, .tiocmget = spcp8x5_tiocmget, diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index ed49a3bc859ce5..895f90c7a3faeb 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -2357,7 +2357,9 @@ static int ene_ub6250_probe(struct usb_interface *intf, return result; /* probe card type */ + mutex_lock(&us->dev_mutex); result = ene_get_card_type(us, REG_CARD_STATUS, info->bbuf); + mutex_unlock(&us->dev_mutex); if (result != USB_STOR_XFER_GOOD) { usb_stor_disconnect(intf); return USB_STOR_TRANSPORT_ERROR; diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index af038b897c6be2..c4b28744693b72 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -916,7 +916,6 @@ static int realtek_cr_autosuspend_setup(struct us_data *us) us->proto_handler = rts51x_invoke_transport; chip->timer_expires = 0; - timer_setup(&chip->rts51x_suspend_timer, rts51x_suspend_timer_fn, 0); fw5895_init(us); /* enable autosuspend function of the usb device */ @@ -934,10 +933,7 @@ static void realtek_cr_destructor(void *extra) return; #ifdef CONFIG_REALTEK_AUTOPM - if (ss_en) { - timer_delete(&chip->rts51x_suspend_timer); - chip->timer_expires = 0; - } + timer_shutdown_sync(&chip->rts51x_suspend_timer); #endif kfree(chip->status); } @@ -982,6 +978,9 @@ static int init_realtek_cr(struct us_data *us) us->extra = chip; us->extra_destructor = realtek_cr_destructor; +#ifdef CONFIG_REALTEK_AUTOPM + timer_setup(&chip->rts51x_suspend_timer, rts51x_suspend_timer_fn, 0); +#endif us->max_lun = chip->max_lun = rts51x_get_max_lun(us); chip->us = us; diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c index 32250b94262a90..601d39ee17c47e 100644 --- a/drivers/usb/typec/altmodes/thunderbolt.c +++ b/drivers/usb/typec/altmodes/thunderbolt.c @@ -303,6 +303,8 @@ static void tbt_altmode_remove(struct typec_altmode *alt) { struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt); + disable_work_sync(&tbt->work); + for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) { if (tbt->plug[i]) typec_altmode_put_plug(tbt->plug[i]); diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index 3e39b800e6b5f4..4eec90c82bae4b 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -62,6 +62,7 @@ struct hd3ss3220 { int id_irq; struct regulator *vbus; + bool vbus_enabled; }; static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode) @@ -208,7 +209,7 @@ static void hd3ss3220_regulator_control(struct hd3ss3220 *hd3ss3220, bool on) { int ret; - if (regulator_is_enabled(hd3ss3220->vbus) == on) + if (hd3ss3220->vbus_enabled == on) return; if (on) @@ -216,9 +217,13 @@ static void hd3ss3220_regulator_control(struct hd3ss3220 *hd3ss3220, bool on) else ret = regulator_disable(hd3ss3220->vbus); - if (ret) + if (ret) { dev_err(hd3ss3220->dev, - "vbus regulator %s failed: %d\n", on ? "disable" : "enable", ret); + "vbus regulator %s failed: %d\n", on ? "enable" : "disable", ret); + return; + } + + hd3ss3220->vbus_enabled = on; } static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220) diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c index 152b2b397f396f..2ec57cfe748a9c 100644 --- a/drivers/usb/typec/mux/ps883x.c +++ b/drivers/usb/typec/mux/ps883x.c @@ -7,11 +7,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -41,6 +43,22 @@ #define CONN_STATUS_2_TBT_UNIDIR_LSRX_ACT_LT BIT(4) #define CONN_STATUS_2_USB4_CONNECTED BIT(7) +/* + * Platforms where the USB4 / DP-tunneling stack is not ready yet. Rejecting + * USB4 here lets the Type-C stack fall back to USB3 + DP Alt Mode instead of + * negotiating USB4 and then failing to drive DisplayPort. + * + * This is a temporary, kernel-contained quirk (not DT ABI). Drop the entries + * once the corresponding USB4 support is complete. + */ +static const char * const ps883x_disable_usb4_compats[] = { + "qcom,x1e80100", + "qcom,x1p42100", + "qcom,hamoa", + "qcom,purwa", + NULL, +}; + struct ps883x_retimer { struct i2c_client *client; struct gpio_desc *reset_gpio; @@ -65,6 +83,18 @@ struct ps883x_retimer { bool disable_usb4; }; +static bool ps883x_should_disable_usb4(void) +{ + const char * const *compat; + + for (compat = ps883x_disable_usb4_compats; *compat; compat++) { + if (of_machine_is_compatible(*compat)) + return true; + } + + return false; +} + static int ps883x_enable_vregs(struct ps883x_retimer *retimer) { struct device *dev = &retimer->client->dev; @@ -185,30 +215,18 @@ static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, return ret; } - usleep_range(20000, 30000); + /* + * The retimer needs time after the connection-status registers are + * written for the analog front-end (PLLs, lane training) to settle. + * Without this, DisplayPort Alt Mode hotplug is unreliable on some + * docks (e.g. Lenovo 40B0). Qualcomm firmware on platforms that + * program this chip from an MCU uses a similar post-config delay. + */ + fsleep(30000); return 0; } -static void ps883x_apply_dp_altmode(int *cfg0, int *cfg1, int dp_state) -{ - *cfg1 |= CONN_STATUS_1_DP_CONNECTED | CONN_STATUS_1_DP_HPD_LEVEL; - - switch (dp_state) { - case TYPEC_DP_STATE_D: - case TYPEC_DP_STATE_F: - *cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; - fallthrough; - case TYPEC_DP_STATE_C: - case TYPEC_DP_STATE_E: - *cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED | - CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D; - break; - default: - break; - } -} - static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state *state) { struct typec_thunderbolt_data *tb_data; @@ -224,7 +242,21 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state if (state->alt) { switch (state->alt->svid) { case USB_TYPEC_DP_SID: - ps883x_apply_dp_altmode(&cfg0, &cfg1, state->mode); + cfg1 |= CONN_STATUS_1_DP_CONNECTED | + CONN_STATUS_1_DP_HPD_LEVEL; + + switch (state->mode) { + case TYPEC_DP_STATE_D: + case TYPEC_DP_STATE_F: + cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; + fallthrough; + case TYPEC_DP_STATE_C: + cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED | + CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D; + break; + default: /* MODE_E */ + break; + } break; case USB_TYPEC_TBT_SID: tb_data = state->data; @@ -258,13 +290,9 @@ static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; break; case TYPEC_MODE_USB4: - if (retimer->disable_usb4) { - dev_info(&retimer->client->dev, - "USB4 disabled via DT property, rejecting USB4 mode\n"); + if (retimer->disable_usb4) return -EOPNOTSUPP; - } - /* Normal USB4 handling */ eudo_data = state->data; cfg2 |= CONN_STATUS_2_USB4_CONNECTED; @@ -385,6 +413,7 @@ static int ps883x_retimer_probe(struct i2c_client *client) struct typec_switch_desc sw_desc = { }; struct typec_retimer_desc rtmr_desc = { }; struct ps883x_retimer *retimer; + unsigned int val; int ret; retimer = devm_kzalloc(dev, sizeof(*retimer), GFP_KERNEL); @@ -393,7 +422,9 @@ static int ps883x_retimer_probe(struct i2c_client *client) retimer->client = client; - retimer->disable_usb4 = device_property_read_bool(dev, "parade,disable-usb4"); + retimer->disable_usb4 = ps883x_should_disable_usb4(); + if (retimer->disable_usb4) + dev_info(dev, "USB4 disabled until platform USB4 support is complete\n"); mutex_init(&retimer->lock); @@ -456,6 +487,16 @@ static int ps883x_retimer_probe(struct i2c_client *client) /* firmware initialization delay */ msleep(60); + + /* make sure device is accessible */ + ret = regmap_read(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, + &val); + if (ret) { + dev_err(dev, "failed to read conn_status_0: %d\n", ret); + if (ret == -ENXIO) + ret = -EIO; + goto err_clk_disable; + } } /* Keep the retimer in reset until a Type-C notification comes */ diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c index e6b28648f44076..926fa017ebafc4 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c @@ -543,6 +543,8 @@ static void qcom_pmic_typec_pdphy_stop(struct pmic_typec *tcpm) for (i = 0; i < pmic_typec_pdphy->nr_irqs; i++) disable_irq(pmic_typec_pdphy->irq_data[i].irq); + cancel_work_sync(&pmic_typec_pdphy->reset_work); + qcom_pmic_typec_pdphy_reset_on(pmic_typec_pdphy); regulator_disable(pmic_typec_pdphy->vdd_pdphy); diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c index bf985efe1cd6be..d3523435f3e018 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c @@ -683,6 +683,9 @@ static int qcom_pmic_typec_port_start(struct pmic_typec *tcpm, enable_irq(pmic_typec_port->irq_data[i].irq); done: + if (ret) + disable_delayed_work_sync(&pmic_typec_port->cc_debounce_dwork); + return ret; } @@ -693,6 +696,8 @@ static void qcom_pmic_typec_port_stop(struct pmic_typec *tcpm) for (i = 0; i < pmic_typec_port->nr_irqs; i++) disable_irq(pmic_typec_port->irq_data[i].irq); + + disable_delayed_work_sync(&pmic_typec_port->cc_debounce_dwork); } int qcom_pmic_typec_port_probe(struct platform_device *pdev, diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 7ac7000b2d1392..6717ac914c6a60 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -38,6 +38,7 @@ struct tcpci { struct regmap *regmap; unsigned int alert_mask; + unsigned int rx_type_mask; bool controls_vbus; @@ -488,6 +489,8 @@ static int tcpci_set_pd_rx(struct tcpc_dev *tcpc, bool enable) if (tcpci->data->cable_comm_capable) reg |= TCPC_RX_DETECT_SOP1; } + + tcpci->rx_type_mask = reg; ret = regmap_write(tcpci->regmap, TCPC_RX_DETECT, reg); if (ret < 0) return ret; @@ -749,6 +752,7 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci) if (status & TCPC_ALERT_RX_STATUS) { struct pd_message msg; unsigned int cnt, payload_cnt; + enum tcpm_transmit_type rx_type; u16 header; regmap_read(tcpci->regmap, TCPC_RX_BYTE_CNT, &cnt); @@ -773,10 +777,16 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci) regmap_raw_read(tcpci->regmap, TCPC_RX_DATA, &msg.payload, payload_cnt); + ret = regmap_read(tcpci->regmap, TCPC_RX_BUF_FRAME_TYPE, &rx_type); + if (ret) + return ret; + /* Read complete, clear RX status alert bit */ tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS); - tcpm_pd_receive(tcpci->port, &msg, TCPC_TX_SOP); + rx_type &= TCPC_RX_BUF_FRAME_TYPE_MASK; + if (tcpci->rx_type_mask & BIT(rx_type)) + tcpm_pd_receive(tcpci->port, &msg, rx_type); } if (tcpci->data->vbus_vsafe0v && (status & TCPC_ALERT_EXTENDED_STATUS)) { diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 89eec20a2064ce..e49ab6d54a5deb 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -6921,16 +6921,32 @@ static void tcpm_pd_event_handler(struct kthread_work *work) } } if (events & TCPM_SOURCING_VBUS) { - tcpm_log(port, "sourcing vbus"); /* * In fast role swap case TCPC autonomously sources vbus. Set vbus_source - * true as TCPM wouldn't have called tcpm_set_vbus. + * true conditionally as TCPM wouldn't have called tcpm_set_vbus. + * If TCPM calls tcpm_set_vbus to source vbus, vbus_source would already + * be true. * - * When vbus is sourced on the command on TCPM i.e. TCPM called - * tcpm_set_vbus to source vbus, vbus_source would already be true. + * When TCPM_FRS_EVENT and TCPM_SOURCING_VBUS arrive simultaneously, + * handling TCPM_FRS_EVENT above transitions the state to AMS_START + * with upcoming_state FR_SWAP_SEND. */ - port->vbus_source = true; - _tcpm_pd_vbus_on(port); + + if (tcpm_port_is_source(port) || + tcpm_port_is_debug_source(port) || + (port->state == AMS_START && port->upcoming_state == FR_SWAP_SEND) || + port->state == FR_SWAP_SEND || + port->state == FR_SWAP_SEND_TIMEOUT || + port->state == FR_SWAP_SNK_SRC_TRANSITION_TO_OFF || + port->state == FR_SWAP_SNK_SRC_NEW_SINK_READY || + port->state == FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED) { + tcpm_log(port, "sourcing vbus"); + port->vbus_source = true; + _tcpm_pd_vbus_on(port); + } else { + tcpm_log(port, "Discarding sourcing vbus! Invalid state %s", + tcpm_states[port->state]); + } } if (events & TCPM_PORT_CLEAN) { tcpm_log(port, "port clean"); diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index d5ee0af9058b9e..159d1a2f9a1f5b 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -114,7 +114,6 @@ struct tps6598x_intel_vid_status_reg { __le32 attention_vdo; __le16 enter_vdo; __le16 device_mode; - __le16 cable_mode; } __packed; /* Standard Task return codes */ @@ -700,9 +699,19 @@ static void cd321x_typec_update_mode(struct tps6598x *tps, struct cd321x_status cd321x->state.mode == TYPEC_TBT_MODE) return; - tbt_data.cable_mode = le16_to_cpu(st->intel_vid_status.cable_mode); - tbt_data.device_mode = le16_to_cpu(st->intel_vid_status.device_mode); - tbt_data.enter_vdo = le16_to_cpu(st->intel_vid_status.enter_vdo); + tbt_data.cable_mode = TBT_MODE | + TBT_SET_CABLE_SPEED(TPS_DATA_STATUS_TBT_CABLE_SPEED(st->data_status)) | + TBT_SET_CABLE_ROUNDED(TPS_DATA_STATUS_TBT_CABLE_GEN(st->data_status)); + if (st->data_status & TPS_DATA_STATUS_OPTICAL_CABLE) + tbt_data.cable_mode |= TBT_CABLE_OPTICAL; + if (st->data_status & TPS_DATA_STATUS_ACTIVE_LINK_TRAIN) + tbt_data.cable_mode |= TBT_CABLE_LINK_TRAINING; + if (st->data_status & TPS_DATA_STATUS_ACTIVE_CABLE) + tbt_data.cable_mode |= TBT_CABLE_ACTIVE_PASSIVE; + tbt_data.device_mode = TBT_MODE | + (u32)le16_to_cpu(st->intel_vid_status.device_mode) << 16; + tbt_data.enter_vdo = + (u32)le16_to_cpu(st->intel_vid_status.enter_vdo) << 16; cd321x->state.alt = cd321x->port_altmode_tbt; cd321x->state.mode = TYPEC_TBT_MODE; cd321x->state.data = &tbt_data; diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h index 03edbb77bbd6d8..d172c84ada74ef 100644 --- a/drivers/usb/typec/tipd/tps6598x.h +++ b/drivers/usb/typec/tipd/tps6598x.h @@ -206,10 +206,10 @@ #define TPS_DATA_STATUS_DP_PIN_ASSIGNMENT(x) \ TPS_FIELD_GET(TPS_DATA_STATUS_DP_PIN_ASSIGNMENT_MASK, (x)) #define TPS_DATA_STATUS_TBT_CABLE_SPEED_MASK GENMASK(27, 25) -#define TPS_DATA_STATUS_TBT_CABLE_SPEED \ +#define TPS_DATA_STATUS_TBT_CABLE_SPEED(x) \ TPS_FIELD_GET(TPS_DATA_STATUS_TBT_CABLE_SPEED_MASK, (x)) #define TPS_DATA_STATUS_TBT_CABLE_GEN_MASK GENMASK(29, 28) -#define TPS_DATA_STATUS_TBT_CABLE_GEN \ +#define TPS_DATA_STATUS_TBT_CABLE_GEN(x) \ TPS_FIELD_GET(TPS_DATA_STATUS_TBT_CABLE_GEN_MASK, (x)) /* Map data status to DP spec assignments */ diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 7067f2561b8465..8d2032d0762ca8 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -74,7 +74,7 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) cur = 0xff; } - if (cur != 0xff) { + if (cur < UCSI_MAX_ALTMODES) { ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY; goto err_unlock; } diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 12c95c2ed99e25..41c60885738f0a 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -91,7 +91,8 @@ int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, if (ret) goto out_clear_bit; - if (!wait_for_completion_timeout(&ucsi->complete, 5 * HZ)) + if (!wait_for_completion_timeout(&ucsi->complete, + msecs_to_jiffies(UCSI_TIMEOUT_MS))) ret = -ETIMEDOUT; out_clear_bit: diff --git a/drivers/video/backlight/aw99706.c b/drivers/video/backlight/aw99706.c index 18299faf06ad46..9ac8ba50a9ea76 100644 --- a/drivers/video/backlight/aw99706.c +++ b/drivers/video/backlight/aw99706.c @@ -130,23 +130,23 @@ static const struct aw99706_dt_prop aw99706_dt_props[] = { AW99706_CFG0_REG, AW99706_DIM_MODE_MASK, 1, }, { - "awinic,sw-freq", aw99706_dt_property_lookup, + "awinic,sw-freq-hz", aw99706_dt_property_lookup, aw99706_sw_freq_tbl, ARRAY_SIZE(aw99706_sw_freq_tbl), AW99706_CFG1_REG, AW99706_SW_FREQ_MASK, 750000, }, { - "awinic,sw-ilmt", aw99706_dt_property_lookup, + "awinic,sw-ilmt-microamp", aw99706_dt_property_lookup, aw99706_sw_ilmt_tbl, ARRAY_SIZE(aw99706_sw_ilmt_tbl), AW99706_CFG1_REG, AW99706_SW_ILMT_MASK, 3000000, }, { - "awinic,iled-max", aw99706_dt_property_iled_max_convert, + "awinic,iled-max-microamp", aw99706_dt_property_iled_max_convert, NULL, 0, AW99706_CFG2_REG, AW99706_ILED_MAX_MASK, 20000, }, { - "awinic,uvlo-thres", aw99706_dt_property_lookup, + "awinic,uvlo-thres-microvolt", aw99706_dt_property_lookup, aw99706_ulvo_thres_tbl, ARRAY_SIZE(aw99706_ulvo_thres_tbl), AW99706_CFG2_REG, AW99706_UVLOSEL_MASK, 2200000, }, @@ -335,7 +335,7 @@ static int aw99706_bl_update_status(struct backlight_device *bl) { struct aw99706_device *aw = bl_get_data(bl); - return aw99706_update_brightness(aw, bl->props.brightness); + return aw99706_update_brightness(aw, backlight_get_brightness(bl)); } static const struct backlight_ops aw99706_bl_ops = { diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c index 5e7963b4aa93c9..8d1dcd43711399 100644 --- a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c +++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c @@ -1150,14 +1150,14 @@ static int dsicm_probe(struct platform_device *pdev) dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; + mutex_init(&ddata->lock); + r = omapdss_register_display(dssdev); if (r) { dev_err(dev, "Failed to register panel\n"); goto err_reg; } - mutex_init(&ddata->lock); - atomic_set(&ddata->do_update, 0); ddata->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW); diff --git a/drivers/video/fbdev/pvr2fb.c b/drivers/video/fbdev/pvr2fb.c index 9428716e2dc40e..a6e7abca7a06f8 100644 --- a/drivers/video/fbdev/pvr2fb.c +++ b/drivers/video/fbdev/pvr2fb.c @@ -639,7 +639,7 @@ static irqreturn_t __maybe_unused pvr2fb_interrupt(int irq, void *dev_id) } #ifdef CONFIG_PVR2_DMA -static ssize_t pvr2fb_write(struct fb_info *info, const char *buf, +static ssize_t pvr2fb_write(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos) { unsigned long dst, start, end, len; @@ -1077,7 +1077,7 @@ static struct pvr2_board { #ifdef CONFIG_PCI { pvr2fb_pci_init, pvr2fb_pci_exit, "PCI PVR2" }, #endif - { 0, }, + { }, }; static int __init pvr2fb_init(void) diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 644b8d97b38118..c4fdecafd85607 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -72,6 +73,13 @@ struct ssd1307fb_par { struct i2c_client *client; u32 height; struct fb_info *info; + /* Pending damage, with exclusive x2/y2, protected by damage_lock. */ + spinlock_t damage_lock; + bool damage_pending; + u32 damage_x1; + u32 damage_x2; + u32 damage_y1; + u32 damage_y2; u8 lookup_table[4]; u32 page_offset; u32 col_offset; @@ -302,19 +310,49 @@ static int ssd1307fb_blank(int blank_mode, struct fb_info *info) return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); } +static void ssd1307fb_schedule_damage(struct fb_info *info, u32 x, u32 y, + u32 width, u32 height) +{ + struct ssd1307fb_par *par = info->par; + unsigned long flags; + u32 x2, y2; + + if (!width || !height || x >= par->width || y >= par->height) + return; + + x2 = x + min(width, par->width - x); + y2 = y + min(height, par->height - y); + + spin_lock_irqsave(&par->damage_lock, flags); + if (par->damage_pending) { + par->damage_x1 = min(par->damage_x1, x); + par->damage_y1 = min(par->damage_y1, y); + par->damage_x2 = max(par->damage_x2, x2); + par->damage_y2 = max(par->damage_y2, y2); + } else { + par->damage_x1 = x; + par->damage_y1 = y; + par->damage_x2 = x2; + par->damage_y2 = y2; + par->damage_pending = true; + } + spin_unlock_irqrestore(&par->damage_lock, flags); + + /* Advance an already-pending mmap update as well. */ + mod_delayed_work(system_wq, &info->deferred_work, 0); +} + static void ssd1307fb_defio_damage_range(struct fb_info *info, off_t off, size_t len) { struct ssd1307fb_par *par = info->par; - ssd1307fb_update_display(par); + ssd1307fb_schedule_damage(info, 0, 0, par->width, par->height); } static void ssd1307fb_defio_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u32 height) { - struct ssd1307fb_par *par = info->par; - - ssd1307fb_update_rect(par, x, y, width, height); + ssd1307fb_schedule_damage(info, x, y, width, height); } FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(ssd1307fb, @@ -329,7 +367,30 @@ static const struct fb_ops ssd1307fb_ops = { static void ssd1307fb_deferred_io(struct fb_info *info, struct list_head *pagereflist) { - ssd1307fb_update_display(info->par); + struct ssd1307fb_par *par = info->par; + unsigned long flags; + u32 x, y, width, height; + + spin_lock_irqsave(&par->damage_lock, flags); + if (!list_empty(pagereflist)) { + x = 0; + y = 0; + width = par->width; + height = par->height; + par->damage_pending = false; + } else if (par->damage_pending) { + x = par->damage_x1; + y = par->damage_y1; + width = par->damage_x2 - par->damage_x1; + height = par->damage_y2 - par->damage_y1; + par->damage_pending = false; + } else { + spin_unlock_irqrestore(&par->damage_lock, flags); + return; + } + spin_unlock_irqrestore(&par->damage_lock, flags); + + ssd1307fb_update_rect(par, x, y, width, height); } static int ssd1307fb_init(struct ssd1307fb_par *par) @@ -601,6 +662,7 @@ static int ssd1307fb_probe(struct i2c_client *client) par = info->par; par->info = info; par->client = client; + spin_lock_init(&par->damage_lock); par->device_info = device_get_match_data(dev); diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c index 9d82326c744f22..ccc9dbc25813cf 100644 --- a/drivers/video/fbdev/uvesafb.c +++ b/drivers/video/fbdev/uvesafb.c @@ -1907,6 +1907,8 @@ static int uvesafb_init(void) err = 0; } } + if (err) + cn_del_callback(&uvesafb_cn_id); return err; } diff --git a/drivers/video/sticore.c b/drivers/video/sticore.c index 0d37e4b10447ec..1d4477f2045049 100644 --- a/drivers/video/sticore.c +++ b/drivers/video/sticore.c @@ -325,7 +325,7 @@ static void sti_rom_copy(unsigned long base, unsigned long count, void *dest) -static char default_sti_path[21] __read_mostly; +static char default_sti_path[32] __read_mostly; #ifndef MODULE static int __init sti_setup(char *str) @@ -1148,6 +1148,26 @@ static void sti_init_roms(void) pr_info("STI GSC/PCI core graphics driver " STI_DRIVERVERSION "\n"); + /* + * Find default console by hardware path which is either stored in + * console entry in stable storage or alternatively from console path + * in PAGE0 used by BCH and PDC. + */ + if (!default_sti_path[0]) { + struct pdc_module_path conspath; + struct device *dev = NULL; + + if (pdc_stable_read(0x60, &conspath, sizeof(conspath)) == PDC_OK) + dev = hwpath_to_device(&conspath.path); + if (!dev) + dev = hwpath_to_device(&PAGE0->mem_cons.dp.path); + if (dev && dev_is_pci(dev)) + print_pci_hwpath(to_pci_dev(dev), default_sti_path); + else if (dev && !dev_is_pci(dev)) + print_pa_hwpath(to_parisc_device(dev), default_sti_path); + pr_debug("default graphic card: %s\n", default_sti_path); + } + /* Register drivers for native & PCI cards */ register_parisc_driver(&pa_sti_driver); WARN_ON(pci_register_driver(&pci_sti_driver)); diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c index e53bc41bde3ca4..b638963d4b5951 100644 --- a/drivers/w1/slaves/w1_ds28e17.c +++ b/drivers/w1/slaves/w1_ds28e17.c @@ -389,6 +389,10 @@ static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter, * another simple read in that case. */ if (msgs[i+1].flags & I2C_M_RECV_LEN) { + if (msgs[i+1].buf[0] > I2C_SMBUS_BLOCK_MAX) { + i = -EPROTO; + goto error; + } result = w1_f19_i2c_read(sl, msgs[i+1].addr, &(msgs[i+1].buf[1]), msgs[i+1].buf[0]); if (result < 0) { @@ -415,6 +419,10 @@ static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter, * another simple read in that case. */ if (msgs[i].flags & I2C_M_RECV_LEN) { + if (msgs[i].buf[0] > I2C_SMBUS_BLOCK_MAX) { + i = -EPROTO; + goto error; + } result = w1_f19_i2c_read(sl, msgs[i].addr, &(msgs[i].buf[1]), diff --git a/drivers/watchdog/pseries-wdt.c b/drivers/watchdog/pseries-wdt.c index 48d67f7c972a34..e97b943e1d3c50 100644 --- a/drivers/watchdog/pseries-wdt.c +++ b/drivers/watchdog/pseries-wdt.c @@ -12,61 +12,10 @@ #include #include #include +#include #define DRV_NAME "pseries-wdt" -/* - * H_WATCHDOG Input - * - * R4: "flags": - * - * Bits 48-55: "operation" - */ -#define PSERIES_WDTF_OP_START 0x100UL /* start timer */ -#define PSERIES_WDTF_OP_STOP 0x200UL /* stop timer */ -#define PSERIES_WDTF_OP_QUERY 0x300UL /* query timer capabilities */ - -/* - * Bits 56-63: "timeoutAction" (for "Start Watchdog" only) - */ -#define PSERIES_WDTF_ACTION_HARD_POWEROFF 0x1UL /* poweroff */ -#define PSERIES_WDTF_ACTION_HARD_RESTART 0x2UL /* restart */ -#define PSERIES_WDTF_ACTION_DUMP_RESTART 0x3UL /* dump + restart */ - -/* - * H_WATCHDOG Output - * - * R3: Return code - * - * H_SUCCESS The operation completed. - * - * H_BUSY The hypervisor is too busy; retry the operation. - * - * H_PARAMETER The given "flags" are somehow invalid. Either the - * "operation" or "timeoutAction" is invalid, or a - * reserved bit is set. - * - * H_P2 The given "watchdogNumber" is zero or exceeds the - * supported maximum value. - * - * H_P3 The given "timeoutInMs" is below the supported - * minimum value. - * - * H_NOOP The given "watchdogNumber" is already stopped. - * - * H_HARDWARE The operation failed for ineffable reasons. - * - * H_FUNCTION The H_WATCHDOG hypercall is not supported by this - * hypervisor. - * - * R4: - * - * - For the "Query Watchdog Capabilities" operation, a 64-bit - * structure: - */ -#define PSERIES_WDTQ_MIN_TIMEOUT(cap) (((cap) >> 48) & 0xffff) -#define PSERIES_WDTQ_MAX_NUMBER(cap) (((cap) >> 32) & 0xffff) - static const unsigned long pseries_wdt_action[] = { [0] = PSERIES_WDTF_ACTION_HARD_POWEROFF, [1] = PSERIES_WDTF_ACTION_HARD_RESTART, diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index e7f1d4ca6d7538..e7f74ea7cd5ebc 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -679,7 +679,7 @@ void xen_free_ballooned_pages(unsigned int nr_pages, struct page **pages) } EXPORT_SYMBOL(xen_free_ballooned_pages); -static int __init balloon_add_regions(void) +static int __init balloon_add_regions(bool append) { unsigned long start_pfn, pages; unsigned long pfn, extra_pfn_end; @@ -703,19 +703,26 @@ static int __init balloon_add_regions(void) balloon_append(pfn_to_page(pfn)); /* - * Extra regions are accounted for in the physmap, but need - * decreasing from current_pages and target_pages to balloon - * down the initial allocation, because they are already - * accounted for in total_pages. + * There are two different use-cases depending on how the + * initial memory target is fetched. For PVH dom0 and PV the + * target is usually set to reflect the domain assigned memory, + * and hence extra regions need adding. + * + * OTOH for HVM and PVH domU the target is set to the amount of + * RAM reported in the memory map, and hence extra regions need + * subtracting to reflect the real memory usage. */ pages = extra_pfn_end - start_pfn; - if (pages >= balloon_stats.current_pages || - pages >= balloon_stats.target_pages) { + if (append) { + balloon_stats.total_pages += pages; + } else if (pages >= balloon_stats.current_pages || + pages >= balloon_stats.target_pages) { WARN(1, "Extra pages underflow current target"); return -ERANGE; + } else { + balloon_stats.current_pages -= pages; + balloon_stats.target_pages -= pages; } - balloon_stats.current_pages -= pages; - balloon_stats.target_pages -= pages; } return 0; @@ -726,6 +733,7 @@ static int __init balloon_init(void) struct task_struct *task; long current_pages = 0; domid_t domid = DOMID_SELF; + bool append = true; int rc; if (!xen_domain()) @@ -745,6 +753,7 @@ static int __init balloon_init(void) } else { if (xen_unpopulated_pages >= get_num_physpages()) goto underflow; + append = false; current_pages = get_num_physpages() - xen_unpopulated_pages; } @@ -767,7 +776,7 @@ static int __init balloon_init(void) register_sysctl_init("xen/balloon", balloon_table); #endif - rc = balloon_add_regions(); + rc = balloon_add_regions(append); if (rc) return rc; diff --git a/fs/backing-file.c b/fs/backing-file.c index 080c99696cd0dc..cc101143f92129 100644 --- a/fs/backing-file.c +++ b/fs/backing-file.c @@ -35,7 +35,7 @@ struct file *backing_file_open(const struct file *user_file, int flags, const struct path *real_path, const struct cred *cred) { - const struct path *user_path = &user_file->f_path; + const struct path *user_path = file_user_path(user_file); struct file *f; int error; diff --git a/fs/btrfs/direct-io.c b/fs/btrfs/direct-io.c index 460326d34143cf..19a1259b3b2f24 100644 --- a/fs/btrfs/direct-io.c +++ b/fs/btrfs/direct-io.c @@ -281,17 +281,24 @@ static int btrfs_get_blocks_direct_write(struct extent_map **map, em2 = btrfs_create_dio_extent(BTRFS_I(inode), dio_data, start, &file_extent, type); btrfs_dec_nocow_writers(bg); - if (type == BTRFS_ORDERED_PREALLOC) { - btrfs_free_extent_map(em); - *map = em2; - em = em2; - } - if (IS_ERR(em2)) { ret = PTR_ERR(em2); + btrfs_free_extent_map(em); + *map = NULL; goto out; } + /* + * True NOCOW writes don't need to create a new extent map, + * while PREALLOC writes must replace the existing one. + */ + if (em2) { + ASSERT(type == BTRFS_ORDERED_PREALLOC); + btrfs_free_extent_map(em); + *map = em2; + em = em2; + } + dio_data->nocow_done = true; } else { /* Our caller expects us to free the input extent map. */ diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index f032f0858f40f3..45c4d4a3b29b17 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -299,6 +300,25 @@ static noinline void unlock_delalloc_folio(const struct inode *inode, PAGE_UNLOCK); } +#ifdef CONFIG_BTRFS_DEBUG +/* + * Writeback must write-protect a folio when locking it for IO, before + * anything consumes its data (zeroing, inline copy, compression, + * checksumming). If this fails, then an mmap writer would be able to + * modify the data concurrently while we need it to be stable. + */ +void btrfs_check_folio_write_protected(struct folio *folio) +{ + if (folio_mkclean(folio)) { + const struct btrfs_inode *inode = BTRFS_I(folio->mapping->host); + + DEBUG_WARN("writable mmap PTEs, root %llu ino %llu pos %llu order %u", + btrfs_root_id(inode->root), btrfs_ino(inode), folio_pos(folio), + folio_order(folio)); + } +} +#endif + static noinline int lock_delalloc_folios(struct inode *inode, struct folio *locked_folio, u64 start, u64 end) @@ -332,6 +352,8 @@ static noinline int lock_delalloc_folios(struct inode *inode, folio_unlock(folio); goto out; } + /* Locked for writeback; revoke writable mmap PTEs before using the data. */ + folio_mkclean(folio); range_start = max_t(u64, folio_pos(folio), start); range_len = min_t(u64, folio_next_pos(folio), end + 1) - range_start; btrfs_folio_set_lock(fs_info, folio, range_start, range_len); @@ -1893,6 +1915,13 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode, ASSERT(end <= folio_end, "start=%llu len=%u folio_start=%llu folio_size=%zu", start, len, folio_start, folio_size(folio)); + /* + * We are about to checksum and write out the data, so it must not be + * mmap writeable, or we could corrupt the data and end up with invalid + * checksums. + */ + btrfs_check_folio_write_protected(folio); + /* Truncate the submit bitmap to the current range. */ if (start > folio_start) bitmap_clear(bio_ctrl->submit_bitmap, 0, @@ -2703,6 +2732,8 @@ static int extent_write_cache_pages(struct address_space *mapping, continue; } + /* Locked for writeback; revoke writable mmap PTEs before using the data. */ + folio_mkclean(folio); ret = extent_writepage(folio, bio_ctrl); if (ret < 0) { done = true; diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 9896e15ddc406c..86992533769938 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -255,6 +255,11 @@ bool try_release_extent_mapping(struct folio *folio, gfp_t mask); int try_release_extent_buffer(struct folio *folio); int btrfs_read_folio(struct file *file, struct folio *folio); +#ifdef CONFIG_BTRFS_DEBUG +void btrfs_check_folio_write_protected(struct folio *folio); +#else +static inline void btrfs_check_folio_write_protected(struct folio *folio) { } +#endif void extent_write_locked_range(struct inode *inode, const struct folio *locked_folio, u64 start, u64 end, struct writeback_control *wbc, bool pages_dirty); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 2534cd9284d540..39a01f9cf6b515 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -775,19 +775,28 @@ static inline void inode_should_defrag(struct btrfs_inode *inode, static int extent_range_clear_dirty_for_io(struct btrfs_inode *inode, u64 start, u64 end) { + pgoff_t index = start >> PAGE_SHIFT; const pgoff_t end_index = end >> PAGE_SHIFT; struct folio *folio; int ret = 0; - for (pgoff_t index = start >> PAGE_SHIFT; index <= end_index; index++) { + while (index <= end_index) { folio = filemap_get_folio(inode->vfs_inode.i_mapping, index); if (IS_ERR(folio)) { if (!ret) ret = PTR_ERR(folio); + index++; continue; } + /* + * We are about to compress the folio, so it must not be mmap + * writeable or we could corrupt the data as we attempt to + * compress it. + */ + btrfs_check_folio_write_protected(folio); btrfs_folio_clamp_clear_dirty(inode->root->fs_info, folio, start, end + 1 - start); + index = folio_next_index(folio); folio_put(folio); } return ret; @@ -877,11 +886,6 @@ static void compress_file_range(struct btrfs_work *work) inode_should_defrag(inode, start, end, end - start + 1, SZ_16K); - /* - * We need to call clear_page_dirty_for_io on each page in the range. - * Otherwise applications with the file mmap'd can wander in and change - * the page contents while we are compressing them. - */ ret = extent_range_clear_dirty_for_io(inode, start, end); /* @@ -2317,6 +2321,13 @@ static int run_delalloc_inline(struct btrfs_inode *inode, struct folio *locked_f int ret; ASSERT(folio_pos(locked_folio) == 0); + /* + * If an mmap writer could modify the folio while we copy it into an + * inline extent we might see only part of their modification then + * wrongly mark it clean again after copying, losing that write. So the + * folio must be write protected here. + */ + btrfs_check_folio_write_protected(locked_folio); if (btrfs_inode_can_compress(inode) && inode_need_compress(inode, 0, blocksize, true)) { diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index baa645e9881263..a537036303a476 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -356,14 +356,21 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap, inode_flags |= BTRFS_INODE_NODATACOW; } } else { - /* - * Revert back under same assumptions as above - */ - if (S_ISREG(inode->vfs_inode.i_mode)) { - if (inode->vfs_inode.i_size == 0) - inode_flags &= ~(BTRFS_INODE_NODATACOW | - BTRFS_INODE_NODATASUM); - } else { + /* We can only change NODATACOW for zero-sized regular file. */ + if (S_ISREG(inode->vfs_inode.i_mode) && (inode->vfs_inode.i_size == 0)) { + inode_flags &= ~BTRFS_INODE_NODATACOW; + /* + * There is currently no way to change NODATASUM flag + * through fileattr API. If we unconditionally keep the + * current NODATASUM flag, chattr +C then chattr -C will + * keep the NODATASUM flag, and no way to remove that + * flag. + * + * So respect the current mount option for NODATASUM flag. + */ + if (!btrfs_test_opt(fs_info, NODATASUM)) + inode_flags &= ~BTRFS_INODE_NODATASUM; + } else if (!S_ISREG(inode->vfs_inode.i_mode)) { inode_flags &= ~BTRFS_INODE_NODATACOW; } } diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index fc5c14b5adad70..4eca2b5e4a9247 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -5555,6 +5555,24 @@ static noinline_for_stack int mark_garbage_root(struct btrfs_root *root) return ret; } +static void release_recovered_fs_roots(struct list_head *roots, bool drop_reloc_refs) +{ + struct btrfs_root *root; + struct btrfs_root *next; + + list_for_each_entry_safe(root, next, roots, reloc_dirty_list) { + list_del_init(&root->reloc_dirty_list); + if (drop_reloc_refs) { + struct btrfs_root *reloc_root = root->reloc_root; + + ASSERT(reloc_root); + root->reloc_root = NULL; + btrfs_put_root(reloc_root); + } + btrfs_put_root(root); + } +} + /* * recover relocation interrupted by system crash. * @@ -5564,6 +5582,7 @@ static noinline_for_stack int mark_garbage_root(struct btrfs_root *root) int btrfs_recover_relocation(struct btrfs_fs_info *fs_info) { LIST_HEAD(reloc_roots); + LIST_HEAD(recovered_roots); struct btrfs_key key; struct btrfs_root *fs_root; struct btrfs_root *reloc_root; @@ -5680,7 +5699,7 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info) ret = PTR_ERR(fs_root); list_add_tail(&reloc_root->root_list, &reloc_roots); btrfs_end_transaction(trans); - goto out_unset; + goto out_drop_reloc_refs; } ret = __add_reloc_root(reloc_root, rc); @@ -5689,15 +5708,17 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info) list_add_tail(&reloc_root->root_list, &reloc_roots); btrfs_put_root(fs_root); btrfs_end_transaction(trans); - goto out_unset; + goto out_drop_reloc_refs; } + ASSERT(list_empty(&fs_root->reloc_dirty_list)); fs_root->reloc_root = btrfs_grab_root(reloc_root); - btrfs_put_root(fs_root); + list_add_tail(&fs_root->reloc_dirty_list, &recovered_roots); } ret = btrfs_commit_transaction(trans); if (ret) - goto out_unset; + goto out_drop_reloc_refs; + release_recovered_fs_roots(&recovered_roots, false); merge_reloc_roots(rc); @@ -5713,6 +5734,8 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info) ret2 = clean_dirty_subvols(rc); if (ret2 < 0 && !ret) ret = ret2; +out_drop_reloc_refs: + release_recovered_fs_roots(&recovered_roots, true); out_unset: unset_reloc_control(rc); reloc_chunk_end(fs_info); diff --git a/fs/buffer.c b/fs/buffer.c index 9af5f061a1f85a..955ab07b34eb80 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2177,6 +2177,7 @@ void block_commit_write(struct folio *folio, size_t from, size_t to) { size_t block_start, block_end; bool partial = false; + bool uptodate = folio_test_uptodate(folio); unsigned blocksize; struct buffer_head *bh, *head; @@ -2199,6 +2200,8 @@ void block_commit_write(struct folio *folio, size_t from, size_t to) clear_buffer_new(bh); block_start = block_end; + if (uptodate && block_start >= to) + break; bh = bh->b_this_page; } while (bh != head); diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index ecf33b66610c87..e9f561bedf4755 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -1426,6 +1426,16 @@ void ceph_shift_unused_folios_left(struct folio_batch *fbatch) fbatch->nr = n; } +static void ceph_undo_wrbuffer_claim(struct inode *inode, struct folio *folio) +{ + struct ceph_snap_context *snapc = folio_detach_private(folio); + + if (!snapc) + return; + ceph_put_wrbuffer_cap_refs(ceph_inode(inode), 1, snapc); + ceph_put_snap_context(snapc); +} + static int ceph_submit_write(struct address_space *mapping, struct writeback_control *wbc, @@ -1489,6 +1499,7 @@ int ceph_submit_write(struct address_space *mapping, if (!page) continue; + ceph_undo_wrbuffer_claim(inode, page_folio(page)); redirty_page_for_writepage(wbc, page); unlock_page(page); } diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index d7283fb54cec4d..bb5ef0eacd8f32 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -979,6 +979,27 @@ int __ceph_caps_revoking_other(struct ceph_inode_info *ci, return 0; } +/* + * Return true if any cap of this inode holds caps which the MDS has + * revoked, but which we have not released yet. + */ +static bool __ceph_is_any_revoking(const struct ceph_inode_info *ci) +{ + const struct rb_node *p; + + lockdep_assert_held(&ci->i_ceph_lock); + + for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { + const struct ceph_cap *cap = + rb_entry(p, struct ceph_cap, ci_node); + + if (cap->implemented & ~cap->issued) + return true; + } + + return false; +} + int __ceph_caps_used(struct ceph_inode_info *ci) { int used = 0; @@ -1421,6 +1442,9 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap, cap->implemented &= cap->issued | used; cap->mds_wanted = want; + if ((ci->i_ceph_flags & CEPH_I_FLUSH_FORCE) != 0 && !__ceph_is_any_revoking(ci)) + clear_bit(CEPH_I_FLUSH_FORCE_BIT, &ci->i_ceph_flags); + arg->session = cap->session; arg->ino = ceph_vino(inode).ino; arg->cid = cap->cap_id; @@ -2038,6 +2062,14 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags) if (ci->i_ceph_flags & CEPH_I_FLUSH) flags |= CHECK_CAPS_FLUSH; + /* + * A revoke whose response was deferred (see handle_cap_grant()) must + * still be acknowledged. Replay the forced flush here so that even a + * check triggered by writeback/invalidation completion sends a cap + * message to the MDS. + */ + if (ci->i_ceph_flags & CEPH_I_FLUSH_FORCE) + flags |= CHECK_CAPS_FLUSH_FORCE; retry: /* Caps wanted by virtue of active open files. */ file_wanted = __ceph_caps_file_wanted(ci); @@ -2589,9 +2621,14 @@ static void __kick_flushing_caps(struct ceph_mds_client *mdsc, } } - list_for_each_entry(cf, &ci->i_cap_flush_list, i_list) { - if (cf->tid < first_tid) + cf = list_first_entry(&ci->i_cap_flush_list, struct ceph_cap_flush, i_list); + while (&cf->i_list != &ci->i_cap_flush_list) { + struct ceph_cap_flush *next; + + if (cf->tid < first_tid) { + cf = list_next_entry(cf, i_list); continue; + } cap = ci->i_auth_cap; if (!(cap && cap->session == session)) { @@ -2601,6 +2638,7 @@ static void __kick_flushing_caps(struct ceph_mds_client *mdsc, } first_tid = cf->tid + 1; + next = list_next_entry(cf, i_list); if (!cf->is_capsnap) { struct cap_msg_args arg; @@ -2641,6 +2679,7 @@ static void __kick_flushing_caps(struct ceph_mds_client *mdsc, } spin_lock(&ci->i_ceph_lock); + cf = next; } } @@ -3757,13 +3796,30 @@ static void handle_cap_grant(struct inode *inode, BUG_ON(cap->issued & ~cap->implemented); /* don't let check_caps skip sending a response to MDS for revoke msgs */ - if (!revoke_wait && le32_to_cpu(grant->op) == CEPH_CAP_OP_REVOKE) { - cap->mds_wanted = 0; - flags |= CHECK_CAPS_FLUSH_FORCE; - if (cap == ci->i_auth_cap) - check_caps = 1; /* check auth cap only */ - else - check_caps = 2; /* check all caps */ + if (le32_to_cpu(grant->op) == CEPH_CAP_OP_REVOKE) { + if (revoke_wait) { + /* + * We can't ack the revoke yet: the response is deferred + * until the writeback or cache invalidation queued above + * completes. Set the CEPH_I_FLUSH_FORCE flag to remember + * that a forced cap message is owed so that deferred + * completion (ceph_put_wrbuffer_cap_refs() or the + * invalidate worker, both of which call ceph_check_caps()) + * actually sends one, even if by then the revoked caps look + * unused, the inode is retaining caps, or the MDS has + * re-granted them. Without this, the cap message is never + * sent and the MDS hangs ("isn't responding to + * mclientcaps(revoke)"). + */ + set_bit(CEPH_I_FLUSH_FORCE_BIT, &ci->i_ceph_flags); + } else { + cap->mds_wanted = 0; + flags |= CHECK_CAPS_FLUSH_FORCE; + if (cap == ci->i_auth_cap) + check_caps = 1; /* check auth cap only */ + else + check_caps = 2; /* check all caps */ + } } if (extra_info->inline_version > 0 && diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c index 64d2407592772e..1cc11f77413c14 100644 --- a/fs/ceph/crypto.c +++ b/fs/ceph/crypto.c @@ -298,6 +298,11 @@ int ceph_encode_encrypted_dname(struct inode *parent, char *buf, int elen) * Otherwise, base64 decode the string, and then ask fscrypt to format it * for userland presentation. * + * Though the fscrypt/crypto subsystems broadly expect all buffers to be in the + * linear-mapped region, this function slightly relaxes those requirements: + * fname->ctext, fname->name, and oname->name may be vmalloc(), but not + * tname->name. + * * Returns 0 on success or negative error code on error. */ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname, @@ -305,11 +310,15 @@ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname, { struct inode *dir = fname->dir; struct fscrypt_str _tname = FSTR_INIT(NULL, 0); + struct fscrypt_str _oname; struct fscrypt_str iname; char *name = fname->name; int name_len = fname->name_len; int ret; + if (WARN_ON_ONCE(tname && is_vmalloc_addr(tname->name))) + return -EIO; + /* Sanity check that the resulting name will fit in the buffer */ if (fname->name_len > NAME_MAX || fname->ctext_len > NAME_MAX) return -EIO; @@ -350,31 +359,50 @@ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname, goto out_inode; } + if ((!tname || !tname->name) && + (fname->ctext_len == 0 || + unlikely(is_vmalloc_addr(fname->ctext)) || + unlikely(is_vmalloc_addr(oname->name)))) { + ret = fscrypt_fname_alloc_buffer(NAME_MAX, &_tname); + if (ret) + goto out_inode; + tname = &_tname; + } + if (fname->ctext_len == 0) { int declen; - if (!tname) { - ret = fscrypt_fname_alloc_buffer(NAME_MAX, &_tname); - if (ret) - goto out_inode; - tname = &_tname; - } - - declen = base64_decode(name, name_len, - tname->name, false, BASE64_IMAP); + declen = base64_decode(name, name_len, tname->name, false, + BASE64_IMAP); if (declen <= 0) { ret = -EIO; goto out; } iname.name = tname->name; iname.len = declen; + } else if (unlikely(is_vmalloc_addr(fname->ctext))) { + memcpy(tname->name, fname->ctext, fname->ctext_len); + + iname.name = tname->name; + iname.len = fname->ctext_len; } else { iname.name = fname->ctext; iname.len = fname->ctext_len; } - ret = fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, oname); - if (!ret && (dir != fname->dir)) { + _oname.name = unlikely(is_vmalloc_addr(oname->name)) ? + tname->name : oname->name; + _oname.len = oname->len; + + ret = fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, &_oname); + if (ret) + goto out; + + if (unlikely(is_vmalloc_addr(oname->name))) + memcpy(oname->name, _oname.name, _oname.len); + oname->len = _oname.len; + + if (dir != fname->dir) { char tmp_buf[BASE64_CHARS(NAME_MAX)]; name_len = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%llu", diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index ef9e92e362d3ea..40326517481cf8 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -1764,11 +1764,11 @@ static int __dir_lease_check(const struct dentry *dentry, if (ret > 0) { if (time_before(jiffies, di->time + lwc->dir_lease_ttl)) return STOP; + if (!lwc->expire_dir_lease) + return KEEP; /* Move dentry to tail of dir lease list if we don't want * to delete it. So dentries in the list are checked in a * round robin manner */ - if (!lwc->expire_dir_lease) - return TOUCH; if (dentry->d_lockref.count > 0 || (di->flags & CEPH_DENTRY_REFERENCED)) return TOUCH; @@ -1795,7 +1795,7 @@ int ceph_trim_dentries(struct ceph_mds_client *mdsc) lwc.dir_lease = false; lwc.nr_to_scan = CEPH_CAPS_PER_RELEASE * 2; freed = __dentry_leases_walk(mdsc, &lwc); - if (!lwc.nr_to_scan) /* more invalid leases */ + if (freed > 0 && !lwc.nr_to_scan) /* more invalid leases */ return -EAGAIN; if (lwc.nr_to_scan < CEPH_CAPS_PER_RELEASE) @@ -1805,6 +1805,10 @@ int ceph_trim_dentries(struct ceph_mds_client *mdsc) lwc.expire_dir_lease = freed < count; lwc.dir_lease_ttl = mdsc->fsc->mount_options->caps_wanted_delay_max * HZ; freed +=__dentry_leases_walk(mdsc, &lwc); + if (freed == 0 && count == 0) + /* no progress possible currently, retry futile */ + return 0; + if (!lwc.nr_to_scan) /* more to check */ return -EAGAIN; diff --git a/fs/ceph/export.c b/fs/ceph/export.c index b2f2af1046791d..debb9634b9e3d9 100644 --- a/fs/ceph/export.c +++ b/fs/ceph/export.c @@ -442,6 +442,16 @@ static struct dentry *ceph_fh_to_parent(struct super_block *sb, return dentry; } +static int ceph_export_copy_name(char *name, const char *src, u32 len) +{ + if (len > NAME_MAX) + return -ENAMETOOLONG; + + memcpy(name, src, len); + name[len] = '\0'; + return 0; +} + static int __get_snap_name(struct dentry *parent, char *name, struct dentry *child) { @@ -513,9 +523,8 @@ static int __get_snap_name(struct dentry *parent, char *name, BUG_ON(!rde->inode.in); if (ceph_snap(inode) == le64_to_cpu(rde->inode.in->snapid)) { - memcpy(name, rde->name, rde->name_len); - name[rde->name_len] = '\0'; - err = 0; + err = ceph_export_copy_name(name, rde->name, + rde->name_len); goto out; } } @@ -580,8 +589,8 @@ static int ceph_get_name(struct dentry *parent, char *name, rinfo = &req->r_reply_info; if (!IS_ENCRYPTED(dir)) { - memcpy(name, rinfo->dname, rinfo->dname_len); - name[rinfo->dname_len] = 0; + err = ceph_export_copy_name(name, rinfo->dname, + rinfo->dname_len); } else { struct fscrypt_str oname = FSTR_INIT(NULL, 0); struct ceph_fname fname = { .dir = dir, @@ -595,10 +604,9 @@ static int ceph_get_name(struct dentry *parent, char *name, goto out; err = ceph_fname_to_usr(&fname, NULL, &oname, NULL); - if (!err) { - memcpy(name, oname.name, oname.len); - name[oname.len] = 0; - } + if (!err) + err = ceph_export_copy_name(name, oname.name, + oname.len); ceph_fname_free_buffer(dir, &oname); } out: diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 3c692ad02c857d..0b6ff5b704f6d3 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -541,9 +541,13 @@ static int parse_reply_info_readdir(void **p, void *end, * to do the base64_decode in-place. It's * safe because the decoded string should * always be shorter, which is 3/4 of origin - * string. + * string. If this message was allocated with + * vmalloc() (happens, but rarely), leave it + * NULL and let ceph_fname_to_usr() allocate + * suitable temporary working space instead. */ - tname.name = _name; + if (likely(!is_vmalloc_addr(_name))) + tname.name = _name; /* * Set oname to _name too, and this will be @@ -615,10 +619,36 @@ static int parse_reply_info_filelock(void **p, void *end, #define DELEGATED_INO_AVAILABLE xa_mk_value(1) +static int ceph_insert_deleg_ino(struct ceph_mds_session *s, u64 ino) +{ + struct ceph_client *cl = s->s_mdsc->fsc->client; + int err; + + /* + * Cap how many delegated inodes a single session may hold. This is + * the only place that grows the count, so atomic_add_unless() bounds + * it at exactly CEPH_MAX_DELEG_INOS; s_num_deleg_inos can never exceed + * that. + */ + if (!atomic_add_unless(&s->s_num_deleg_inos, 1, CEPH_MAX_DELEG_INOS)) { + pr_warn_ratelimited_client(cl, + "MDS session already holds %d delegated inodes\n", + CEPH_MAX_DELEG_INOS); + return -EOVERFLOW; + } + + err = xa_insert(&s->s_delegated_inos, ino, DELEGATED_INO_AVAILABLE, + GFP_KERNEL); + if (err) + atomic_dec(&s->s_num_deleg_inos); + return err; +} + static int ceph_parse_deleg_inos(void **p, void *end, struct ceph_mds_session *s) { struct ceph_client *cl = s->s_mdsc->fsc->client; + u64 msg_deleg_inos = 0; u32 sets; ceph_decode_32_safe(p, end, sets, bad); @@ -636,16 +666,34 @@ static int ceph_parse_deleg_inos(void **p, void *end, start, len); continue; } + + /* + * Bound the number of inodes one reply may delegate. + * ceph_insert_deleg_ino() separately caps the per-session + * population, so this only has to stop one reply from spinning + * the insert loop under an attacker-controlled len. + */ + if (len > (u64)CEPH_MAX_DELEG_INOS || + msg_deleg_inos > (u64)CEPH_MAX_DELEG_INOS - len) { + pr_warn_ratelimited_client(cl, + "MDS reply delegates too many inodes (have %llu, +%llu, max %d)\n", + msg_deleg_inos, len, CEPH_MAX_DELEG_INOS); + return -EIO; + } + msg_deleg_inos += len; + while (len--) { - int err = xa_insert(&s->s_delegated_inos, start++, - DELEGATED_INO_AVAILABLE, - GFP_KERNEL); + int err = ceph_insert_deleg_ino(s, start++); + if (!err) { doutc(cl, "added delegated inode 0x%llx\n", start - 1); } else if (err == -EBUSY) { pr_warn_client(cl, "MDS delegated inode 0x%llx more than once.\n", start - 1); + } else if (err == -EOVERFLOW) { + /* ceph_insert_deleg_ino() already warned. */ + return -EIO; } else { return err; } @@ -663,16 +711,17 @@ u64 ceph_get_deleg_ino(struct ceph_mds_session *s) xa_for_each(&s->s_delegated_inos, ino, val) { val = xa_erase(&s->s_delegated_inos, ino); - if (val == DELEGATED_INO_AVAILABLE) + if (val == DELEGATED_INO_AVAILABLE) { + atomic_dec(&s->s_num_deleg_inos); return ino; + } } return 0; } int ceph_restore_deleg_ino(struct ceph_mds_session *s, u64 ino) { - return xa_insert(&s->s_delegated_inos, ino, DELEGATED_INO_AVAILABLE, - GFP_KERNEL); + return ceph_insert_deleg_ino(s, ino); } #else /* BITS_PER_LONG == 64 */ /* @@ -1059,6 +1108,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, INIT_LIST_HEAD(&s->s_waiting); INIT_LIST_HEAD(&s->s_unsafe); xa_init(&s->s_delegated_inos); + atomic_set(&s->s_num_deleg_inos, 0); INIT_LIST_HEAD(&s->s_cap_releases); INIT_WORK(&s->s_cap_release_work, ceph_cap_release_work); @@ -4441,7 +4491,9 @@ static void handle_session(struct ceph_mds_session *session, pr_err_client(cl, "No memory for path\n"); goto fail; } - ceph_decode_copy(&p, cap_auths[i].match.path, _len); + ceph_decode_copy_safe(&p, end, + cap_auths[i].match.path, + _len, bad); /* Remove the tailing '/' */ while (_len && cap_auths[i].match.path[_len - 1] == '/') { @@ -4458,7 +4510,9 @@ static void handle_session(struct ceph_mds_session *session, pr_err_client(cl, "No memory for fs_name\n"); goto fail; } - ceph_decode_copy(&p, cap_auths[i].match.fs_name, _len); + ceph_decode_copy_safe(&p, end, + cap_auths[i].match.fs_name, + _len, bad); } ceph_decode_8_safe(&p, end, cap_auths[i].match.root_squash, bad); @@ -5106,6 +5160,7 @@ static int send_mds_reconnect(struct ceph_mds_client *mdsc, /* Serialized by s_mutex against concurrent ceph_get_deleg_ino(). */ xa_destroy(&session->s_delegated_inos); + atomic_set(&session->s_num_deleg_inos, 0); if (session->s_state == CEPH_MDS_SESSION_CLOSED || session->s_state == CEPH_MDS_SESSION_REJECTED) { pr_info_client(cl, "mds%d skipping reconnect, session %s\n", @@ -5834,9 +5889,11 @@ static void check_new_map(struct ceph_mds_client *mdsc, ceph_mdsmap_get_addr(newmap, i), sizeof(struct ceph_entity_addr))) { /* just close it */ + ceph_get_mds_session(s); mutex_unlock(&mdsc->mutex); mutex_lock(&s->s_mutex); mutex_lock(&mdsc->mutex); + ceph_put_mds_session(s); ceph_con_close(&s->s_con); mutex_unlock(&s->s_mutex); s->s_state = CEPH_MDS_SESSION_RESTARTING; @@ -5851,6 +5908,7 @@ static void check_new_map(struct ceph_mds_client *mdsc, newstate >= CEPH_MDS_STATE_RECONNECT) { int rc; + ceph_get_mds_session(s); mutex_unlock(&mdsc->mutex); clear_bit(i, targets); rc = send_mds_reconnect(mdsc, s); @@ -5859,6 +5917,7 @@ static void check_new_map(struct ceph_mds_client *mdsc, "mds%d reconnect failed: %d\n", i, rc); mutex_lock(&mdsc->mutex); + ceph_put_mds_session(s); } /* @@ -5871,9 +5930,11 @@ static void check_new_map(struct ceph_mds_client *mdsc, pr_info_client(cl, "mds%d recovery completed\n", s->s_mds); kick_requests(mdsc, i); + ceph_get_mds_session(s); mutex_unlock(&mdsc->mutex); mutex_lock(&s->s_mutex); mutex_lock(&mdsc->mutex); + ceph_put_mds_session(s); ceph_kick_flushing_caps(mdsc, s); mutex_unlock(&s->s_mutex); wake_up_session_caps(s, RECONNECT); @@ -6532,11 +6593,13 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask) doutc(cl, "tpath '%s', mask %d, caller_uid %d, caller_gid %d\n", tpath, mask, caller_uid, caller_gid); + mutex_lock(&mdsc->mutex); for (i = 0; i < mdsc->s_cap_auths_num; i++) { struct ceph_mds_cap_auth *s = &mdsc->s_cap_auths[i]; err = ceph_mds_auth_match(mdsc, s, cred, tpath); if (err < 0) { + mutex_unlock(&mdsc->mutex); put_cred(cred); return err; } else if (err > 0) { @@ -6558,6 +6621,7 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask) doutc(cl, "root_squash_perms %d, rw_perms_s %p\n", root_squash_perms, rw_perms_s); if (root_squash_perms && rw_perms_s == NULL) { + mutex_unlock(&mdsc->mutex); doutc(cl, "access allowed\n"); return 0; } @@ -6572,6 +6636,7 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask) !!(mask & MAY_READ), !!(mask & MAY_WRITE)); } doutc(cl, "access denied\n"); + mutex_unlock(&mdsc->mutex); return -EACCES; } diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 0ece4c9e3529f5..e7a262c9c2ab4b 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -300,6 +300,7 @@ struct ceph_mds_session { struct list_head s_waiting; /* waiting requests */ struct list_head s_unsafe; /* unsafe requests */ struct xarray s_delegated_inos; + atomic_t s_num_deleg_inos; }; /* @@ -603,6 +604,7 @@ struct ceph_mds_client { struct rw_semaphore pool_perm_rwsem; struct rb_root pool_perm_tree; + /* protected by mutex */ u32 s_cap_auths_num; struct ceph_mds_cap_auth *s_cap_auths; diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index ddb4ce17b2b27a..69ff46cd02f2cd 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -127,6 +128,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p, u8 mdsmap_v; u16 mdsmap_ev; u32 target; + size_t export_targets_len; m = kzalloc_obj(*m, GFP_NOFS); if (!m) @@ -225,8 +227,11 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p, *p += namelen; if (info_v >= 2) { ceph_decode_32_safe(p, end, num_export_targets, bad); + export_targets_len = size_mul(num_export_targets, + sizeof(u32)); + ceph_decode_need(p, end, export_targets_len, bad); pexport_targets = *p; - *p += num_export_targets * sizeof(u32); + *p += export_targets_len; } else { num_export_targets = 0; } @@ -265,6 +270,10 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p, goto nomem; for (j = 0; j < num_export_targets; j++) { target = ceph_decode_32(&pexport_targets); + if (target >= CEPH_MAX_MDS) { + err = -EIO; + goto corrupt; + } info->export_targets[j] = target; } } else { diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 1d6aab060780aa..38f86294182cf5 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -641,6 +641,15 @@ static inline int ceph_ino_compare(struct inode *inode, void *data) #define CEPH_MDS_INO_LOG_OFFSET (2 * CEPH_MAX_MDS) #define CEPH_INO_SYSTEM_BASE ((6*CEPH_MAX_MDS) + (CEPH_MAX_MDS * CEPH_NUM_STRAY)) +/* + * Upper bound on the number of delegated inodes a single MDS session may + * hold. The MDS normally hands out a small preallocation window (the + * userspace mds_client_prealloc_inos option defaults to 1000) and refills + * it as the client consumes entries. This leaves generous headroom while + * bounding the CPU and memory a malformed delegation interval can consume. + */ +#define CEPH_MAX_DELEG_INOS 8192 + static inline bool ceph_vino_is_reserved(const struct ceph_vino vino) { if (vino.ino >= CEPH_INO_SYSTEM_BASE || @@ -687,6 +696,10 @@ static inline struct inode *ceph_find_inode(struct super_block *sb, #define CEPH_I_ASYNC_CREATE_BIT (12) /* async create in flight for this */ #define CEPH_I_SHUTDOWN_BIT (13) /* inode is no longer usable */ #define CEPH_I_ASYNC_CHECK_CAPS_BIT (14) /* check caps after async creating finishes */ +#define CEPH_I_FLUSH_FORCE_BIT (15) /* a revoke's response was deferred; + * force a cap message to the MDS once + * the deferred work completes + */ #define CEPH_I_DIR_ORDERED (1 << CEPH_I_DIR_ORDERED_BIT) #define CEPH_I_FLUSH (1 << CEPH_I_FLUSH_BIT) @@ -699,6 +712,7 @@ static inline struct inode *ceph_find_inode(struct super_block *sb, #define CEPH_I_ODIRECT (1 << CEPH_I_ODIRECT_BIT) #define CEPH_I_ASYNC_CREATE (1 << CEPH_I_ASYNC_CREATE_BIT) #define CEPH_I_SHUTDOWN (1 << CEPH_I_SHUTDOWN_BIT) +#define CEPH_I_FLUSH_FORCE (1 << CEPH_I_FLUSH_FORCE_BIT) /* * Masks of ceph inode work. diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 860fc8e1867df5..cc4ffbbcb719a7 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -848,6 +848,7 @@ static int __build_xattrs(struct inode *inode) name = p; p += len; ceph_decode_32_safe(&p, end, len, bad); + ceph_decode_need(&p, end, len, bad); val = p; p += len; diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index edd6aafbfbaaf3..08de6652a4f311 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -273,7 +273,8 @@ static int debugfs_locked_down(struct inode *inode, (!real_fops || (!real_fops->unlocked_ioctl && !real_fops->compat_ioctl && - !real_fops->mmap))) + !real_fops->mmap && + !real_fops->mmap_prepare))) return 0; if (security_locked_down(LOCKDOWN_DEBUGFS)) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 74b02b55e3f6d0..e67119b6029c75 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1197,7 +1197,7 @@ static int ecryptfs_read_headers_virt(char *page_virt, } else set_default_header_data(crypt_stat); rc = ecryptfs_parse_packet_set(crypt_stat, (page_virt + offset), - ecryptfs_dentry); + PAGE_SIZE - offset, ecryptfs_dentry); out: return rc; } diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index f4f56a92bd5635..7d2488a10b1798 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -580,7 +580,8 @@ int ecryptfs_generate_key_packet_set(char *dest_base, size_t *len, size_t max); int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, - unsigned char *src, struct dentry *ecryptfs_dentry); + unsigned char *src, size_t src_size, + struct dentry *ecryptfs_dentry); int ecryptfs_truncate(struct dentry *dentry, loff_t new_length); ssize_t ecryptfs_getxattr_lower(struct dentry *lower_dentry, struct inode *lower_inode, diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index ebebc9551f1ff8..51651314b7a620 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -894,6 +894,12 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size, "rc = [%d]\n", __func__, rc); goto out; } + if (s->parsed_tag_70_packet_size < (ECRYPTFS_SIG_SIZE + 2)) { + ecryptfs_printk(KERN_WARNING, "Invalid packet size [%zd]\n", + s->parsed_tag_70_packet_size); + rc = -EINVAL; + goto out; + } s->block_aligned_filename_size = (s->parsed_tag_70_packet_size - ECRYPTFS_SIG_SIZE - 1); if ((1 + s->packet_size_len + s->parsed_tag_70_packet_size) @@ -1384,10 +1390,20 @@ parse_tag_3_packet(struct ecryptfs_crypt_stat *crypt_stat, } (*new_auth_tok)->session_key.encrypted_key_size = (body_size - (ECRYPTFS_SALT_SIZE + 5)); + /* + * Although encrypted_key_size is copied into the + * encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES] buffer here, + * it later bounds operations on a smaller buffer: + * decrypt_passphrase_encrypted_session_key() sets decrypted_key_size = + * encrypted_key_size and decrypts into + * decrypted_key[ECRYPTFS_MAX_KEY_BYTES], then memcpy's into + * crypt_stat->key[ECRYPTFS_MAX_KEY_BYTES]. Limit to + * ECRYPTFS_MAX_KEY_BYTES to protect those smaller buffers. + */ if ((*new_auth_tok)->session_key.encrypted_key_size - > ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES) { + > ECRYPTFS_MAX_KEY_BYTES) { printk(KERN_WARNING "Tag 3 packet contains key larger " - "than ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES\n"); + "than ECRYPTFS_MAX_KEY_BYTES\n"); rc = -EINVAL; goto out_free; } @@ -1537,7 +1553,7 @@ parse_tag_11_packet(unsigned char *data, unsigned char *contents, } (*packet_size) += length_size; (*tag_11_contents_size) = (body_size - 14); - if (unlikely((*packet_size) + body_size + 1 > max_packet_size)) { + if (unlikely((*packet_size) + body_size > max_packet_size)) { printk(KERN_ERR "Packet size exceeds max\n"); rc = -EINVAL; goto out; @@ -1704,6 +1720,7 @@ decrypt_passphrase_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok, * ecryptfs_parse_packet_set * @crypt_stat: The cryptographic context * @src: Virtual address of region of memory containing the packets + * @src_size: Size of the packet set buffer * @ecryptfs_dentry: The eCryptfs dentry associated with the packet set * * Get crypt_stat to have the file's session key if the requisite key @@ -1714,7 +1731,7 @@ decrypt_passphrase_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok, * conditions. */ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, - unsigned char *src, + unsigned char *src, size_t src_size, struct dentry *ecryptfs_dentry) { size_t i = 0; @@ -1736,7 +1753,11 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, * added the our &auth_tok_list */ next_packet_is_auth_tok_packet = 1; while (next_packet_is_auth_tok_packet) { - size_t max_packet_size = ((PAGE_SIZE - 8) - i); + size_t max_packet_size; + + if (i >= src_size) + break; + max_packet_size = src_size - i; switch (src[i]) { case ECRYPTFS_TAG_3_PACKET_TYPE: @@ -1751,12 +1772,16 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, goto out_wipe_list; } i += packet_size; + if (i > src_size) { + rc = -EIO; + goto out_wipe_list; + } rc = parse_tag_11_packet((unsigned char *)&src[i], sig_tmp_space, ECRYPTFS_SIG_SIZE, &tag_11_contents_size, &tag_11_packet_size, - max_packet_size); + src_size - i); if (rc) { ecryptfs_printk(KERN_ERR, "No valid " "(ecryptfs-specific) literal " @@ -1768,6 +1793,10 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, goto out_wipe_list; } i += tag_11_packet_size; + if (i > src_size) { + rc = -EIO; + goto out_wipe_list; + } if (ECRYPTFS_SIG_SIZE != tag_11_contents_size) { ecryptfs_printk(KERN_ERR, "Expected " "signature of size [%d]; " @@ -1793,6 +1822,10 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, goto out_wipe_list; } i += packet_size; + if (i > src_size) { + rc = -EIO; + goto out_wipe_list; + } crypt_stat->flags |= ECRYPTFS_ENCRYPTED; break; case ECRYPTFS_TAG_11_PACKET_TYPE: diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c index 03c60f0850ca61..73c7b8215e667f 100644 --- a/fs/ecryptfs/messaging.c +++ b/fs/ecryptfs/messaging.c @@ -166,6 +166,7 @@ int ecryptfs_exorcise_daemon(struct ecryptfs_daemon *daemon) mutex_unlock(&daemon->mux); goto out; } + mutex_lock(&ecryptfs_msg_ctx_lists_mux); list_for_each_entry_safe(msg_ctx, msg_ctx_tmp, &daemon->msg_ctx_out_queue, daemon_out_list) { list_del(&msg_ctx->daemon_out_list); @@ -174,6 +175,7 @@ int ecryptfs_exorcise_daemon(struct ecryptfs_daemon *daemon) "the out queue of a dying daemon\n", __func__); ecryptfs_msg_ctx_alloc_to_free(msg_ctx); } + mutex_unlock(&ecryptfs_msg_ctx_lists_mux); hlist_del(&daemon->euid_chain); mutex_unlock(&daemon->mux); kfree_sensitive(daemon); @@ -284,9 +286,16 @@ ecryptfs_send_message_locked(char *data, int data_len, u8 msg_type, mutex_unlock(&ecryptfs_msg_ctx_lists_mux); rc = ecryptfs_send_miscdev(data, data_len, *msg_ctx, msg_type, 0, daemon); - if (rc) + if (rc) { printk(KERN_ERR "%s: Error attempting to send message to " "userspace daemon; rc = [%d]\n", __func__, rc); + mutex_lock(&ecryptfs_msg_ctx_lists_mux); + mutex_lock(&(*msg_ctx)->mux); + ecryptfs_msg_ctx_alloc_to_free(*msg_ctx); + mutex_unlock(&(*msg_ctx)->mux); + mutex_unlock(&ecryptfs_msg_ctx_lists_mux); + *msg_ctx = NULL; + } out: return rc; } diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c index 5a7d081499227a..68804399a5dfc8 100644 --- a/fs/ecryptfs/miscdev.c +++ b/fs/ecryptfs/miscdev.c @@ -360,7 +360,7 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf, u32 seq; size_t packet_size, packet_size_length; char *data; - unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE]; + unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE] = { }; ssize_t rc; if (count == 0) { @@ -376,7 +376,8 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf, } if (copy_from_user(packet_size_peek, &buf[PKT_LEN_OFFSET], - sizeof(packet_size_peek))) { + min_t(size_t, count - PKT_LEN_OFFSET, + sizeof(packet_size_peek)))) { printk(KERN_WARNING "%s: Error while inspecting packet size\n", __func__); return -EFAULT; diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c index 3bc21d677564d1..686b2b4a9cb563 100644 --- a/fs/ecryptfs/super.c +++ b/fs/ecryptfs/super.c @@ -150,6 +150,13 @@ static int ecryptfs_show_options(struct seq_file *m, struct dentry *root) if (mount_crypt_stat->global_default_cipher_key_size) seq_printf(m, ",ecryptfs_key_bytes=%zd", mount_crypt_stat->global_default_cipher_key_size); + if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) { + seq_printf(m, ",ecryptfs_fn_cipher=%s", + mount_crypt_stat->global_default_fn_cipher_name); + if (mount_crypt_stat->global_default_fn_cipher_key_bytes) + seq_printf(m, ",ecryptfs_fn_key_bytes=%zd", + mount_crypt_stat->global_default_fn_cipher_key_bytes); + } if (mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED) seq_printf(m, ",ecryptfs_passthrough"); if (mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED) diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 733c19571f1cfe..8d33f11db2a1b3 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -89,12 +89,30 @@ static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf) /* Some UEFI firmware does not implement QueryVariableInfo() */ storage_space = remaining_space = 0; if (efi_rt_services_supported(EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO)) { - status = efivar_query_variable_info(attr, &storage_space, - &remaining_space, - &max_variable_size); - if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) - pr_warn_ratelimited("query_variable_info() failed: 0x%lx\n", - status); + static DEFINE_RATELIMIT_STATE(_rs, 2 * HZ, 5); + static u64 storage, remaining; + static DEFINE_SPINLOCK(lock); + + if (!__ratelimit(&_rs)) { + ratelimit_set_flags(&_rs, RATELIMIT_MSG_ON_RELEASE); + + spin_lock(&lock); + storage_space = storage; + remaining_space = remaining; + spin_unlock(&lock); + } else { + status = efivar_query_variable_info(attr, &storage_space, + &remaining_space, + &max_variable_size); + if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) + pr_warn("query_variable_info() failed: 0x%lx\n", + status); + + spin_lock(&lock); + storage = storage_space; + remaining = remaining_space; + spin_unlock(&lock); + } } /* diff --git a/fs/erofs/zutil.c b/fs/erofs/zutil.c index a0ce9226bc0292..6b9bfa2ef3b65f 100644 --- a/fs/erofs/zutil.c +++ b/fs/erofs/zutil.c @@ -79,6 +79,8 @@ int z_erofs_gbuf_growsize(unsigned int nrpages) for (i = 0; i < z_erofs_gbuf_count; ++i) { gbuf = &z_erofs_gbufpool[i]; + if (gbuf->nrpages >= nrpages) + continue; tmp_pages = kzalloc_objs(*tmp_pages, nrpages); if (!tmp_pages) goto out; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 29808629cce56f..269b1c9fba5f77 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1258,12 +1258,9 @@ static int ext2_setsize(struct inode *inode, loff_t newsize) filemap_invalidate_unlock(inode->i_mapping); inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); - if (inode_needs_sync(inode)) { - mmb_sync(&EXT2_I(inode)->i_metadata_bhs); + mark_inode_dirty(inode); + if (inode_needs_sync(inode)) sync_inode_metadata(inode, 1); - } else { - mark_inode_dirty(inode); - } return 0; } diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index e55d16abf422f4..be63f89402a381 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -777,6 +777,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, /* Update the inode. */ EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0; inode_set_ctime_current(inode); + mark_inode_dirty(inode); if (IS_SYNC(inode)) { error = sync_inode_metadata(inode, 1); /* In case sync failed due to ENOSPC the inode was actually @@ -789,8 +790,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, } goto cleanup; } - } else - mark_inode_dirty(inode); + } error = 0; if (old_bh && old_bh != new_bh) { diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index a765fda7153639..631269c6f94d78 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1323,10 +1323,11 @@ struct folio *f2fs_get_read_data_folio(struct inode *inode, pgoff_t index, if (folio_test_large(folio)) { pgoff_t folio_index = mapping_align_index(mapping, index); + unsigned long nr_pages = folio_nr_pages(folio); f2fs_folio_put(folio, true); invalidate_inode_pages2_range(mapping, folio_index, - folio_index + folio_nr_pages(folio) - 1); + folio_index + nr_pages - 1); f2fs_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT); goto retry; } @@ -1532,8 +1533,11 @@ static int __allocate_data_block(struct dnode_of_data *dn, int seg_type) old_blkaddr = dn->data_blkaddr; err = f2fs_allocate_data_block(sbi, NULL, old_blkaddr, &dn->data_blkaddr, &sum, seg_type, NULL); - if (err) + if (err) { + if (old_blkaddr == NULL_ADDR) + dec_valid_block_count(sbi, dn->inode, count); return err; + } if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) f2fs_invalidate_internal_cache(sbi, old_blkaddr, 1); diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index af88db8fdb7112..ff379aff44726e 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -352,10 +352,6 @@ static void update_mem_info(struct f2fs_sb_info *sbi) get_cache: si->cache_mem = 0; - /* build gc */ - if (sbi->gc_thread) - si->cache_mem += sizeof(struct f2fs_gc_kthread); - /* build merge flush thread */ if (SM_I(sbi)->fcc_info) si->cache_mem += sizeof(struct flush_cmd_control); diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index a9563f7fcd8842..fd0e2cd31a814a 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -320,6 +320,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, de = find_in_block(dir, dentry_folio, fname, &max_slots, use_hash); if (IS_ERR(de)) { + f2fs_folio_put(dentry_folio, false); *res_folio = ERR_CAST(de); de = NULL; break; @@ -460,7 +461,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, folio_mark_dirty(folio); inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); - f2fs_mark_inode_dirty_sync(dir, false); + f2fs_mark_inode_dirty_sync(dir, true); f2fs_folio_put(folio, true); } @@ -615,7 +616,7 @@ void f2fs_update_parent_metadata(struct inode *dir, struct inode *inode, clear_inode_flag(inode, FI_NEW_INODE); } inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); - f2fs_mark_inode_dirty_sync(dir, false); + f2fs_mark_inode_dirty_sync(dir, true); if (F2FS_I(dir)->i_current_depth != current_depth) f2fs_i_depth_write(dir, current_depth); @@ -927,7 +928,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct folio *folio, f2fs_folio_put(folio, true); inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); - f2fs_mark_inode_dirty_sync(dir, false); + f2fs_mark_inode_dirty_sync(dir, true); if (inode) f2fs_drop_nlink(dir, inode); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8f3e632f315ccd..8011bbdf2c6888 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1748,6 +1748,33 @@ struct decompress_io_ctx { #define MAX_COMPRESS_LOG_SIZE 8 #define MAX_COMPRESS_WINDOW_SIZE(log_size) ((PAGE_SIZE) << (log_size)) +struct f2fs_gc_kthread { + struct task_struct *f2fs_gc_task; + wait_queue_head_t gc_wait_queue_head; + + /* for gc sleep time */ + unsigned int urgent_sleep_time; + unsigned int min_sleep_time; + unsigned int max_sleep_time; + unsigned int no_gc_sleep_time; + + /* for changing gc mode */ + bool gc_wake; + + /* for GC_MERGE mount option */ + wait_queue_head_t fggc_wq; /* + * caller of f2fs_balance_fs() + * will wait on this wait queue. + */ + + /* for gc control for zoned devices */ + unsigned int no_zoned_gc_percent; + unsigned int boost_zoned_gc_percent; + unsigned int valid_thresh_ratio; + unsigned int boost_gc_multiple; + unsigned int boost_gc_greedy; +}; + struct f2fs_sb_info { struct super_block *sb; /* pointer to VFS super block */ struct proc_dir_entry *s_proc; /* proc entry */ @@ -1883,7 +1910,7 @@ struct f2fs_sb_info { * semaphore for GC, avoid * race between GC and GC or CP */ - struct f2fs_gc_kthread *gc_thread; /* GC thread */ + struct f2fs_gc_kthread gc_thread; /* GC thread */ struct atgc_management am; /* atgc management */ unsigned int cur_victim_sec; /* current victim section num */ unsigned int gc_mode; /* current GC state */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 4b52c56d71f07f..67fcea7408b1be 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -36,23 +36,69 @@ #include #include -static void f2fs_zero_post_eof_page(struct inode *inode, - loff_t new_size, bool lock) +static int fill_zero(struct inode *inode, pgoff_t index, + loff_t start, loff_t len); + +static int do_zero_post_eof_page(struct inode *inode, loff_t new_size) +{ + loff_t old_size = i_size_read(inode); + unsigned int offset, len; + pgoff_t index; + int err; + + offset = old_size & (PAGE_SIZE - 1); + + if (!offset) + return 0; + + len = min_t(loff_t, PAGE_SIZE - offset, new_size - old_size); + index = old_size >> PAGE_SHIFT; + + if (f2fs_has_inline_data(inode)) { + /* data post eof should be always zero */ + if (new_size <= MAX_INLINE_DATA(inode)) + return 0; + err = f2fs_convert_inline_inode(inode); + if (err) + return err; + } + + err = fill_zero(inode, index, offset, len); + if (err) + return err; + return filemap_write_and_wait_range(inode->i_mapping, + old_size, old_size + len - 1); +} + +static int f2fs_zero_post_eof_page(struct inode *inode, + loff_t new_size, bool lock, bool writeback) { loff_t old_size = i_size_read(inode); + bool strict = + F2FS_OPTION(F2FS_I_SB(inode)).fsync_mode == FSYNC_MODE_STRICT; if (old_size >= new_size) - return; + return 0; - if (mapping_empty(inode->i_mapping)) - return; + if (!strict && mapping_empty(inode->i_mapping)) + return 0; if (lock) filemap_invalidate_lock(inode->i_mapping); /* zero or drop pages only in range of [old_size, new_size] */ - truncate_inode_pages_range(inode->i_mapping, old_size, new_size); + truncate_inode_pages_range(inode->i_mapping, old_size, new_size - 1); if (lock) filemap_invalidate_unlock(inode->i_mapping); + + if (!writeback || !strict) + return 0; + /* + * In fsync_mode=strict, when we expand an unaligned EOF size, we + * should zero post EOF data and writeback the data immediately, + * so that it can avoid exposing stale data after metadata flush + * and POR. + */ + return do_zero_post_eof_page(inode, new_size); } static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf) @@ -132,7 +178,10 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf) f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); - f2fs_zero_post_eof_page(inode, (folio->index + 1) << PAGE_SHIFT, true); + err = f2fs_zero_post_eof_page(inode, + (folio->index + 1) << PAGE_SHIFT, true, false); + if (err) + goto out_pagefault; file_update_time(vmf->vma->vm_file); filemap_invalidate_lock_shared(inode->i_mapping); @@ -189,7 +238,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf) out_sem: filemap_invalidate_unlock_shared(inode->i_mapping); - +out_pagefault: sb_end_pagefault(inode->i_sb); out: ret = vmf_fs_error(err); @@ -1107,17 +1156,23 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, !IS_ALIGNED(attr->ia_size, F2FS_BLK_TO_BYTES(fi->i_cluster_size))) return -EINVAL; - /* - * To prevent scattered pin block generation, we don't allow - * smaller/equal size unaligned truncation for pinned file. - * We only support overwrite IO to pinned file, so don't - * care about larger size truncation. - */ - if (f2fs_is_pinned_file(inode) && - attr->ia_size <= i_size_read(inode) && - !IS_ALIGNED(attr->ia_size, - F2FS_BLK_TO_BYTES(CAP_BLKS_PER_SEC(sbi)))) - return -EINVAL; + + if (f2fs_is_pinned_file(inode)) { + /* + * It may break section-aligned fallocate recovery + * mechanism, so do not allow larger size truncation. + */ + if (attr->ia_size > i_size_read(inode)) + return -EINVAL; + /* + * To prevent scattered pin block generation, we don't + * allow smaller/equal size unaligned truncation for + * pinned file. + */ + else if (!IS_ALIGNED(attr->ia_size, + F2FS_BLK_TO_BYTES(CAP_BLKS_PER_SEC(sbi)))) + return -EINVAL; + } } if (is_quota_modification(idmap, inode, attr)) { @@ -1169,8 +1224,12 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, f2fs_down_write(&fi->i_gc_rwsem[WRITE]); filemap_invalidate_lock(inode->i_mapping); - if (attr->ia_size > old_size) - f2fs_zero_post_eof_page(inode, attr->ia_size, false); + if (attr->ia_size > old_size) { + err = f2fs_zero_post_eof_page(inode, + attr->ia_size, false, true); + if (err) + goto err_out; + } truncate_setsize(inode, attr->ia_size); if (attr->ia_size <= old_size) @@ -1179,6 +1238,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, * do not trim all blocks after i_size if target size is * larger than i_size. */ +err_out: filemap_invalidate_unlock(inode->i_mapping); f2fs_up_write(&fi->i_gc_rwsem[WRITE]); if (err) @@ -1290,7 +1350,9 @@ static int f2fs_punch_hole(struct inode *inode, loff_t offset, loff_t len) if (ret) return ret; - f2fs_zero_post_eof_page(inode, offset + len, true); + ret = f2fs_zero_post_eof_page(inode, offset + len, true, false); + if (ret) + return ret; pg_start = ((unsigned long long) offset) >> PAGE_SHIFT; pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT; @@ -1577,7 +1639,9 @@ static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len) f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); filemap_invalidate_lock(inode->i_mapping); - f2fs_zero_post_eof_page(inode, offset + len, false); + ret = f2fs_zero_post_eof_page(inode, offset + len, false, false); + if (ret) + goto out_unlock; f2fs_lock_op(sbi, &lc); f2fs_drop_extent_tree(inode); @@ -1585,6 +1649,7 @@ static int f2fs_do_collapse(struct inode *inode, loff_t offset, loff_t len) ret = __exchange_data_block(inode, inode, end, start, nrpages - end, true); f2fs_unlock_op(sbi, &lc); +out_unlock: filemap_invalidate_unlock(inode->i_mapping); f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); return ret; @@ -1617,11 +1682,14 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len) /* write out all moved pages, if possible */ filemap_invalidate_lock(inode->i_mapping); - filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); + ret = filemap_write_and_wait_range(inode->i_mapping, offset, LLONG_MAX); + if (ret) + goto out_unlock; truncate_pagecache(inode, offset); new_size = i_size_read(inode) - len; ret = f2fs_truncate_blocks(inode, new_size, true); +out_unlock: filemap_invalidate_unlock(inode->i_mapping); if (!ret) f2fs_i_size_write(inode, new_size); @@ -1703,7 +1771,9 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len, if (ret) return ret; - f2fs_zero_post_eof_page(inode, offset + len, true); + ret = f2fs_zero_post_eof_page(inode, offset + len, true, false); + if (ret) + return ret; pg_start = ((unsigned long long) offset) >> PAGE_SHIFT; pg_end = ((unsigned long long) offset + len) >> PAGE_SHIFT; @@ -1838,7 +1908,9 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); filemap_invalidate_lock(mapping); - f2fs_zero_post_eof_page(inode, offset + len, false); + ret = f2fs_zero_post_eof_page(inode, offset + len, false, false); + if (ret) + goto out_unlock; truncate_pagecache(inode, offset); while (!ret && idx > pg_start) { @@ -1856,6 +1928,7 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len) idx + delta, nr, false); f2fs_unlock_op(sbi, &lc); } +out_unlock: filemap_invalidate_unlock(mapping); f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); if (ret) @@ -1898,7 +1971,9 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset, if (err) return err; - f2fs_zero_post_eof_page(inode, offset + len, true); + err = f2fs_zero_post_eof_page(inode, offset + len, true, true); + if (err) + return err; f2fs_balance_fs(sbi, true); @@ -1919,8 +1994,9 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset, block_t sec_len; if (map.m_lblk % sec_blks) { - map.m_lblk = rounddown(map.m_lblk, sec_blks); - map.m_len = pg_end - map.m_lblk; + pg_start = rounddown(map.m_lblk, sec_blks); + map.m_lblk = pg_start; + map.m_len = pg_end - pg_start; if (off_end) map.m_len++; } @@ -3083,6 +3159,9 @@ static int f2fs_ioc_defragment(struct file *filp, unsigned long arg) if (f2fs_readonly(sbi->sb)) return -EROFS; + if (IS_DEVICE_ALIASING(inode)) + return -EOPNOTSUPP; + if (copy_from_user(&range, (struct f2fs_defragment __user *)arg, sizeof(range))) return -EFAULT; @@ -3121,8 +3200,9 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, struct inode *dst = file_inode(file_out); struct f2fs_sb_info *sbi = F2FS_I_SB(src); struct f2fs_lock_context lc; - size_t olen = len, dst_max_i_size = 0; - size_t dst_osize; + size_t olen = len; + loff_t dst_max_i_size = 0; + loff_t dst_osize, dst_end; int ret; if (file_in->f_path.mnt != file_out->f_path.mnt || @@ -3135,7 +3215,8 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, if (!S_ISREG(src->i_mode) || !S_ISREG(dst->i_mode)) return -EINVAL; - if (IS_ENCRYPTED(src) || IS_ENCRYPTED(dst)) + if (IS_ENCRYPTED(src) || IS_ENCRYPTED(dst) || + IS_DEVICE_ALIASING(src) || IS_DEVICE_ALIASING(dst)) return -EOPNOTSUPP; if (pos_out < 0 || pos_in < 0) @@ -3144,8 +3225,6 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, if (src == dst) { if (pos_in == pos_out) return 0; - if (pos_out > pos_in && pos_out < pos_in + len) - return -EINVAL; } inode_lock(src); @@ -3171,6 +3250,8 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, goto out_unlock; if (len == 0) olen = len = src->i_size - pos_in; + if (src == dst && pos_out > pos_in && pos_out < pos_in + len) + goto out_unlock; if (pos_in + len == src->i_size) len = ALIGN(src->i_size, F2FS_BLKSIZE) - pos_in; if (len == 0) { @@ -3179,8 +3260,15 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in, } dst_osize = dst->i_size; - if (pos_out + olen > dst->i_size) - dst_max_i_size = pos_out + olen; + if (olen > LLONG_MAX - pos_out) + goto out_unlock; + dst_end = pos_out + olen; + if (dst_end > dst->i_size) { + ret = inode_newsize_ok(dst, dst_end); + if (ret) + goto out_unlock; + dst_max_i_size = dst_end; + } /* verify the end result is block aligned */ if (!IS_ALIGNED(pos_in, F2FS_BLKSIZE) || @@ -4473,7 +4561,7 @@ static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len) page_idx = folio_next_index(folio); } while (page_len < len); - do { + while (redirty_idx < page_idx) { folio = filemap_lock_folio(mapping, redirty_idx); /* It will never fail, when folio has pinned above */ @@ -4486,7 +4574,7 @@ static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len) redirty_idx = folio_next_index(folio); folio_unlock(folio); folio_put_refs(folio, 2); - } while (redirty_idx < page_idx); + } return ret; } @@ -5002,8 +5090,10 @@ static ssize_t f2fs_write_checks(struct kiocb *iocb, struct iov_iter *from) if (err) return err; - f2fs_zero_post_eof_page(inode, - iocb->ki_pos + iov_iter_count(from), true); + err = f2fs_zero_post_eof_page(inode, + iocb->ki_pos + iov_iter_count(from), true, true); + if (err) + return err; return count; } diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index ffaa7ba76a1bd2..1eda8186197471 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -31,9 +31,9 @@ static unsigned int count_bits(const unsigned long *addr, static int gc_thread_func(void *data) { struct f2fs_sb_info *sbi = data; - struct f2fs_gc_kthread *gc_th = sbi->gc_thread; - wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head; - wait_queue_head_t *fggc_wq = &sbi->gc_thread->fggc_wq; + struct f2fs_gc_kthread *gc_th = &sbi->gc_thread; + wait_queue_head_t *wq = &sbi->gc_thread.gc_wait_queue_head; + wait_queue_head_t *fggc_wq = &sbi->gc_thread.fggc_wq; unsigned int wait_ms; struct f2fs_gc_control gc_control = { .victim_segno = NULL_SEGNO, @@ -193,13 +193,9 @@ static int gc_thread_func(void *data) int f2fs_start_gc_thread(struct f2fs_sb_info *sbi) { - struct f2fs_gc_kthread *gc_th; + struct f2fs_gc_kthread *gc_th = &sbi->gc_thread; dev_t dev = sbi->sb->s_bdev->bd_dev; - gc_th = f2fs_kmalloc(sbi, sizeof(struct f2fs_gc_kthread), GFP_KERNEL); - if (!gc_th) - return -ENOMEM; - gc_th->urgent_sleep_time = DEF_GC_THREAD_URGENT_SLEEP_TIME; gc_th->valid_thresh_ratio = DEF_GC_THREAD_VALID_THRESH_RATIO; gc_th->boost_gc_multiple = BOOST_GC_MULTIPLE; @@ -221,16 +217,14 @@ int f2fs_start_gc_thread(struct f2fs_sb_info *sbi) gc_th->gc_wake = false; - sbi->gc_thread = gc_th; - init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head); - init_waitqueue_head(&sbi->gc_thread->fggc_wq); - sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi, + init_waitqueue_head(&gc_th->gc_wait_queue_head); + init_waitqueue_head(&gc_th->fggc_wq); + gc_th->f2fs_gc_task = kthread_run(gc_thread_func, sbi, "f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev)); if (IS_ERR(gc_th->f2fs_gc_task)) { int err = PTR_ERR(gc_th->f2fs_gc_task); - kfree(gc_th); - sbi->gc_thread = NULL; + gc_th->f2fs_gc_task = NULL; return err; } @@ -241,14 +235,14 @@ int f2fs_start_gc_thread(struct f2fs_sb_info *sbi) void f2fs_stop_gc_thread(struct f2fs_sb_info *sbi) { - struct f2fs_gc_kthread *gc_th = sbi->gc_thread; + struct f2fs_gc_kthread *gc_th = &sbi->gc_thread; - if (!gc_th) + if (!gc_th->f2fs_gc_task) return; + kthread_stop(gc_th->f2fs_gc_task); + gc_th->f2fs_gc_task = NULL; wake_up_all(&gc_th->fggc_wq); - kfree(gc_th); - sbi->gc_thread = NULL; } static int select_gc_type(struct f2fs_sb_info *sbi, int gc_type) @@ -796,7 +790,7 @@ int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result, if (one_time) { p.one_time_gc = one_time; if (has_enough_free_secs(sbi, 0, NR_PERSISTENT_LOG)) - valid_thresh_ratio = sbi->gc_thread->valid_thresh_ratio; + valid_thresh_ratio = sbi->gc_thread.valid_thresh_ratio; } retry: @@ -1807,9 +1801,9 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, if (f2fs_sb_has_blkzoned(sbi) && !has_enough_free_blocks(sbi, - sbi->gc_thread->boost_zoned_gc_percent)) + sbi->gc_thread.boost_zoned_gc_percent)) window_granularity *= - sbi->gc_thread->boost_gc_multiple; + sbi->gc_thread.boost_gc_multiple; end_segno = start_segno + window_granularity; } @@ -2222,7 +2216,7 @@ static int free_segment_range(struct f2fs_sb_info *sbi, mutex_unlock(&DIRTY_I(sbi)->seglist_lock); /* Move out cursegs from the target range */ - for (type = CURSEG_HOT_DATA; type < NR_CURSEG_PERSIST_TYPE; type++) { + for (type = CURSEG_HOT_DATA; type < NR_CURSEG_TYPE; type++) { err = f2fs_allocate_segment_for_resize(sbi, type, start, end); if (err) goto out; diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index 6c4d4567571eb5..b015742fb455b4 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -45,32 +45,7 @@ #define NR_GC_CHECKPOINT_SECS (3) /* data/node/dentry sections */ -struct f2fs_gc_kthread { - struct task_struct *f2fs_gc_task; - wait_queue_head_t gc_wait_queue_head; - - /* for gc sleep time */ - unsigned int urgent_sleep_time; - unsigned int min_sleep_time; - unsigned int max_sleep_time; - unsigned int no_gc_sleep_time; - - /* for changing gc mode */ - bool gc_wake; - - /* for GC_MERGE mount option */ - wait_queue_head_t fggc_wq; /* - * caller of f2fs_balance_fs() - * will wait on this wait queue. - */ - /* for gc control for zoned devices */ - unsigned int no_zoned_gc_percent; - unsigned int boost_zoned_gc_percent; - unsigned int valid_thresh_ratio; - unsigned int boost_gc_multiple; - unsigned int boost_gc_greedy; -}; struct gc_inode_list { struct list_head ilist; @@ -197,6 +172,6 @@ static inline bool need_to_boost_gc(struct f2fs_sb_info *sbi) { if (f2fs_sb_has_blkzoned(sbi)) return !has_enough_free_blocks(sbi, - sbi->gc_thread->boost_zoned_gc_percent); + sbi->gc_thread.boost_zoned_gc_percent); return has_enough_invalid_blocks(sbi); } diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index e2f7bedf155258..aec06fb4fd76b2 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -732,7 +732,7 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, f2fs_folio_put(folio, true); inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); - f2fs_mark_inode_dirty_sync(dir, false); + f2fs_mark_inode_dirty_sync(dir, true); if (inode) f2fs_drop_nlink(dir, inode); diff --git a/fs/f2fs/iostat.c b/fs/f2fs/iostat.c index ae265e3e9b2c98..12d4e18a6a50e4 100644 --- a/fs/f2fs/iostat.c +++ b/fs/f2fs/iostat.c @@ -332,6 +332,12 @@ void f2fs_destroy_iostat_processing(void) int f2fs_init_iostat(struct f2fs_sb_info *sbi) { + /* + * The f2fs_iostat tracepoint emits a fixed number of read folio order + * buckets; make sure every order fits so none is silently dropped. + */ + BUILD_BUG_ON(NR_PAGE_ORDERS > F2FS_IOSTAT_RD_FOLIO_ORDERS); + /* init iostat info */ spin_lock_init(&sbi->iostat_lock); spin_lock_init(&sbi->iostat_lat_lock); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index cac03b8e91a15e..a8db3928c8b557 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -707,15 +707,16 @@ static int f2fs_symlink(struct mnt_idmap *idmap, struct inode *dir, * performance regression. */ if (!err) { - filemap_write_and_wait_range(inode->i_mapping, 0, - disk_link.len - 1); + err = filemap_write_and_wait_range(inode->i_mapping, 0, + disk_link.len - 1); - if (IS_DIRSYNC(dir)) + if (!err && IS_DIRSYNC(dir)) f2fs_sync_fs(sbi->sb, 1); - } else { - f2fs_unlink(dir, dentry); } + if (err) + f2fs_unlink(dir, dentry); + f2fs_balance_fs(sbi, true); goto out_free_encrypted_link; @@ -1076,7 +1077,7 @@ static int f2fs_rename(struct mnt_idmap *idmap, struct inode *old_dir, f2fs_up_write(&F2FS_I(old_inode)->i_sem); inode_set_ctime_current(old_inode); - f2fs_mark_inode_dirty_sync(old_inode, false); + f2fs_mark_inode_dirty_sync(old_inode, true); f2fs_delete_entry(old_entry, old_folio, old_dir, NULL); old_folio = NULL; @@ -1246,7 +1247,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_i_links_write(old_dir, old_nlink > 0); f2fs_up_write(&F2FS_I(old_dir)->i_sem); } - f2fs_mark_inode_dirty_sync(old_dir, false); + f2fs_mark_inode_dirty_sync(old_dir, true); /* update directory entry info of new dir inode */ f2fs_set_link(new_dir, new_entry, new_folio, old_inode); @@ -1265,7 +1266,7 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_i_links_write(new_dir, new_nlink > 0); f2fs_up_write(&F2FS_I(new_dir)->i_sem); } - f2fs_mark_inode_dirty_sync(new_dir, false); + f2fs_mark_inode_dirty_sync(new_dir, true); if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT) { f2fs_add_ino_entry(sbi, old_dir->i_ino, TRANS_DIR_INO); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 38917e4a731943..1671535fbb10ad 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -660,6 +660,7 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid, __builtin_return_address(0), ni->ino, ni->nid, ni->blk_addr, ni->version, ni->flag); f2fs_handle_error(sbi, ERROR_INCONSISTENT_NAT); + return -EFSCORRUPTED; } /* cache nat entry */ @@ -1617,7 +1618,7 @@ static struct folio *__get_node_folio(struct f2fs_sb_info *sbi, pgoff_t nid, if (!err) return folio; out_err: - folio_clear_uptodate(folio); + clear_node_folio_dirty(folio); out_put_err: /* ENOENT comes from read_node_folio which is not an error. */ if (err != -ENOENT) @@ -1789,7 +1790,7 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync, /* get old block addr of this node page */ nid = nid_of_node(folio); - if (f2fs_sanity_check_node_footer(sbi, folio, nid, + if (f2fs_sanity_check_node_footer(sbi, folio, folio->index, NODE_TYPE_REGULAR, false)) { fserror_report_metadata(sbi->sb, -EFSCORRUPTED, GFP_NOFS); f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_CORRUPTED_NID); @@ -2009,6 +2010,11 @@ int f2fs_fsync_node_pages(struct f2fs_sb_info *sbi, struct inode *inode, f2fs_debug(sbi, "Retry to write fsync mark: ino=%u, idx=%lx", ino, last_folio->index); folio_lock(last_folio); + if (unlikely(!is_node_folio(last_folio))) { + f2fs_folio_put(last_folio, true); + ret = -EAGAIN; + goto out; + } f2fs_folio_wait_writeback(last_folio, NODE, true, true); folio_mark_dirty(last_folio); folio_unlock(last_folio); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 89af8407b66733..3fecfdbd59586e 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -158,6 +158,22 @@ static int init_recovered_filename(const struct inode *dir, return 0; } +static const char *recover_printable_name(struct inode *inode, + struct f2fs_inode *raw, + int *name_len) +{ + static const char encrypted_name[] = ""; + + if (file_enc_name(inode)) { + *name_len = sizeof(encrypted_name) - 1; + return encrypted_name; + } + + *name_len = min_t(unsigned int, le32_to_cpu(raw->i_namelen), + F2FS_NAME_LEN); + return raw->i_name; +} + static int recover_dentry(struct inode *inode, struct folio *ifolio, struct list_head *dir_list) { @@ -170,7 +186,8 @@ static int recover_dentry(struct inode *inode, struct folio *ifolio, struct inode *dir, *einode; struct fsync_inode_entry *entry; int err = 0; - char *name; + const char *name; + int name_len; entry = get_fsync_inode(dir_list, pino); if (!entry) { @@ -229,12 +246,9 @@ static int recover_dentry(struct inode *inode, struct folio *ifolio, out_put: f2fs_folio_put(folio, false); out: - if (file_enc_name(inode)) - name = ""; - else - name = raw_inode->i_name; - f2fs_notice(F2FS_I_SB(inode), "%s: ino = %x, name = %s, dir = %llu, err = %d", - __func__, ino_of_node(ifolio), name, + name = recover_printable_name(inode, raw_inode, &name_len); + f2fs_notice(F2FS_I_SB(inode), "%s: ino = %x, name = %.*s, dir = %llu, err = %d", + __func__, ino_of_node(ifolio), name_len, name, IS_ERR(dir) ? 0 : dir->i_ino, err); return err; } @@ -282,7 +296,8 @@ static int recover_inode(struct inode *inode, struct folio *folio) { struct f2fs_inode *raw = F2FS_INODE(folio); struct f2fs_inode_info *fi = F2FS_I(inode); - char *name; + const char *name; + int name_len; int err; inode->i_mode = le16_to_cpu(raw->i_mode); @@ -331,13 +346,11 @@ static int recover_inode(struct inode *inode, struct folio *folio) f2fs_mark_inode_dirty_sync(inode, true); - if (file_enc_name(inode)) - name = ""; - else - name = F2FS_INODE(folio)->i_name; + name = recover_printable_name(inode, raw, &name_len); - f2fs_notice(F2FS_I_SB(inode), "recover_inode: ino = %x, name = %s, inline = %x", - ino_of_node(folio), name, raw->i_inline); + f2fs_notice(F2FS_I_SB(inode), "%s: ino = %x, name = %.*s, inline = %x", + __func__, ino_of_node(folio), name_len, name, + raw->i_inline); return 0; } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index d71ddb3ee91845..091bfb170d615f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -452,15 +452,14 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) f2fs_submit_merged_write(sbi, DATA); f2fs_submit_all_merged_ipu_writes(sbi); - if (test_opt(sbi, GC_MERGE) && sbi->gc_thread && - sbi->gc_thread->f2fs_gc_task) { + if (test_opt(sbi, GC_MERGE) && sbi->gc_thread.f2fs_gc_task) { DEFINE_WAIT(wait); - prepare_to_wait(&sbi->gc_thread->fggc_wq, &wait, + prepare_to_wait(&sbi->gc_thread.fggc_wq, &wait, TASK_UNINTERRUPTIBLE); - wake_up(&sbi->gc_thread->gc_wait_queue_head); + wake_up(&sbi->gc_thread.gc_wait_queue_head); io_schedule(); - finish_wait(&sbi->gc_thread->fggc_wq, &wait); + finish_wait(&sbi->gc_thread.fggc_wq, &wait); } else { struct f2fs_gc_control gc_control = { .victim_segno = NULL_SEGNO, @@ -3362,10 +3361,13 @@ int f2fs_allocate_pinning_section(struct f2fs_sb_info *sbi) err = f2fs_gc_range(sbi, 0, sbi->first_seq_zone_segno - 1, true, ZONED_PIN_SEC_REQUIRED_COUNT); f2fs_up_write_trace(&sbi->gc_lock, &lc); - - gc_required = false; - if (!err) + if (err) + return err; + err = f2fs_sync_fs(sbi->sb, 1); + if (!err) { + gc_required = false; goto retry; + } } return err; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9760e4efeffeca..f7e2dcb58b4db3 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2943,11 +2943,11 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb) if ((flags & SB_RDONLY) || (F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_OFF && !test_opt(sbi, GC_MERGE))) { - if (sbi->gc_thread) { + if (sbi->gc_thread.f2fs_gc_task) { f2fs_stop_gc_thread(sbi); need_restart_gc = true; } - } else if (!sbi->gc_thread) { + } else if (!sbi->gc_thread.f2fs_gc_task) { err = f2fs_start_gc_thread(sbi); if (err) goto restore_opts; diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 665687244c939c..aa1621419932a6 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -75,7 +75,7 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a, static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) { if (struct_type == GC_THREAD) - return (unsigned char *)sbi->gc_thread; + return (unsigned char *)&sbi->gc_thread; else if (struct_type == SM_INFO) return (unsigned char *)SM_I(sbi); else if (struct_type == DCC_INFO) @@ -557,7 +557,7 @@ static ssize_t __sbi_store(struct f2fs_attr *a, return -EINVAL; cprc->ckpt_thread_ioprio = IOPRIO_PRIO_VALUE(class, level); - if (test_opt(sbi, MERGE_CHECKPOINT)) { + if (cprc->f2fs_issue_ckpt) { ret = set_task_ioprio(cprc->f2fs_issue_ckpt, cprc->ckpt_thread_ioprio); if (ret) @@ -664,20 +664,20 @@ static ssize_t __sbi_store(struct f2fs_attr *a, sbi->gc_mode = GC_NORMAL; } else if (t == 1) { sbi->gc_mode = GC_URGENT_HIGH; - if (sbi->gc_thread) { - sbi->gc_thread->gc_wake = true; + if (sbi->gc_thread.f2fs_gc_task) { + sbi->gc_thread.gc_wake = true; wake_up_interruptible_all( - &sbi->gc_thread->gc_wait_queue_head); + &sbi->gc_thread.gc_wait_queue_head); wake_up_discard_thread(sbi, true); } } else if (t == 2) { sbi->gc_mode = GC_URGENT_LOW; } else if (t == 3) { sbi->gc_mode = GC_URGENT_MID; - if (sbi->gc_thread) { - sbi->gc_thread->gc_wake = true; + if (sbi->gc_thread.f2fs_gc_task) { + sbi->gc_thread.gc_wake = true; wake_up_interruptible_all( - &sbi->gc_thread->gc_wait_queue_head); + &sbi->gc_thread.gc_wait_queue_head); } } else { return -EINVAL; @@ -934,14 +934,14 @@ static ssize_t __sbi_store(struct f2fs_attr *a, if (!strcmp(a->attr.name, "gc_boost_gc_multiple")) { if (t < 1 || t > SEGS_PER_SEC(sbi)) return -EINVAL; - sbi->gc_thread->boost_gc_multiple = (unsigned int)t; + sbi->gc_thread.boost_gc_multiple = (unsigned int)t; return count; } if (!strcmp(a->attr.name, "gc_boost_gc_greedy")) { if (t > GC_GREEDY) return -EINVAL; - sbi->gc_thread->boost_gc_greedy = (unsigned int)t; + sbi->gc_thread.boost_gc_greedy = (unsigned int)t; return count; } @@ -989,8 +989,8 @@ static ssize_t __sbi_store(struct f2fs_attr *a, if (sbi->cprc_info.f2fs_issue_ckpt) set_user_nice(sbi->cprc_info.f2fs_issue_ckpt, PRIO_TO_NICE(sbi->critical_task_priority)); - if (sbi->gc_thread && sbi->gc_thread->f2fs_gc_task) - set_user_nice(sbi->gc_thread->f2fs_gc_task, + if (sbi->gc_thread.f2fs_gc_task) + set_user_nice(sbi->gc_thread.f2fs_gc_task, PRIO_TO_NICE(sbi->critical_task_priority)); return count; } @@ -1007,13 +1007,15 @@ static ssize_t f2fs_sbi_store(struct f2fs_attr *a, ssize_t ret; bool gc_entry = (!strcmp(a->attr.name, "gc_urgent") || a->struct_type == GC_THREAD); + bool thread_entry = !strcmp(a->attr.name, "ckpt_thread_ioprio") || + !strcmp(a->attr.name, "critical_task_priority"); - if (gc_entry) { + if (gc_entry || thread_entry) { if (!down_read_trylock(&sbi->sb->s_umount)) return -EAGAIN; } ret = __sbi_store(a, sbi, buf, count); - if (gc_entry) + if (gc_entry || thread_entry) up_read(&sbi->sb->s_umount); return ret; diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index ed33e5110f2ab1..6728d1488cadb9 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -119,7 +119,7 @@ static int f2fs_xattr_advise_set(const struct xattr_handler *handler, unsigned char old_advise = F2FS_I(inode)->i_advise; unsigned char new_advise; - if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) + if (!inode_owner_or_capable(idmap, inode)) return -EPERM; if (value == NULL) return -EINVAL; diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 3027ef53af214c..2a0fea26a99a8f 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -133,7 +133,11 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster) ret = fat_ent_read(inode, &fatent, last); if (ret >= 0) { int wait = inode_needs_sync(inode); + int old = ret; + ret = fat_ent_write(inode, &fatent, new_dclus, wait); + if (ret < 0) + fat_ent_write(inode, &fatent, old, wait); fatent_brelse(&fatent); } if (ret < 0) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 5763a7cd3b37fa..e7da62a7293dcb 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -75,6 +75,9 @@ void fuse_chan_set_initialized(struct fuse_chan *fch, struct fuse_chan_param *pa fch->minor = param->minor; fch->max_write = param->max_write; fch->max_pages = param->max_pages; + + if (param->io_uring_enabled) + fuse_uring_conn_init(fch); } /* Make sure stores before this are seen on another CPU */ @@ -85,7 +88,13 @@ void fuse_chan_set_initialized(struct fuse_chan *fch, struct fuse_chan_param *pa static bool fuse_block_alloc(struct fuse_chan *fch, bool for_background) { - return !fch->initialized || (for_background && fch->blocked) || + if (!fch->initialized) + return true; + + /* Pairs with smp_wmb() in fuse_chan_set_initialized() */ + smp_rmb(); + + return (for_background && fch->blocked) || (fch->io_uring && fch->connected && !fuse_uring_ready(fch)); } @@ -120,9 +129,6 @@ static struct fuse_req *fuse_get_req(struct fuse_chan *fch, bool for_background) goto out; } - /* Matches smp_wmb() in fuse_chan_set_initialized() */ - smp_rmb(); - err = -ENOTCONN; if (!fch->connected) goto out; @@ -240,7 +246,8 @@ void fuse_dev_queue_forget(struct fuse_iqueue *fiq, void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) { spin_lock(&fiq->lock); - if (list_empty(&req->intr_entry)) { + /* Repeat FR_SENT test after obtaining the lock to prevent race with fuse_resend() */ + if (list_empty(&req->intr_entry) && test_bit(FR_SENT, &req->flags)) { list_add_tail(&req->intr_entry, &fiq->interrupts); /* * Pairs with smp_mb() implied by test_and_set_bit() @@ -411,11 +418,6 @@ void fuse_chan_set_fc(struct fuse_chan *fch, struct fuse_conn *fc) fch->conn = fc; } -void fuse_chan_io_uring_enable(struct fuse_chan *fch) -{ - fch->io_uring = 1; -} - void fuse_pqueue_init(struct fuse_pqueue *fpq) { spin_lock_init(&fpq->lock); @@ -725,7 +727,7 @@ static void request_wait_answer(struct fuse_req *req) if (req->args->abort_on_kill) { fuse_chan_abort(fch, false); - return; + goto wait_for_finish; } if (test_bit(FR_URING, &req->flags)) @@ -736,6 +738,7 @@ static void request_wait_answer(struct fuse_req *req) return; } +wait_for_finish: /* * Either request is already in userspace, or it was forced. * Wait it out. @@ -1760,7 +1763,7 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, void fuse_chan_resend(struct fuse_chan *fch) { struct fuse_dev *fud; - struct fuse_req *req, *next; + struct fuse_req *req; struct fuse_iqueue *fiq = &fch->iq; LIST_HEAD(to_queue); unsigned int i; @@ -1775,24 +1778,20 @@ void fuse_chan_resend(struct fuse_chan *fch) struct fuse_pqueue *fpq = &fud->pq; spin_lock(&fpq->lock); - for (i = 0; i < FUSE_PQ_HASH_SIZE; i++) - list_splice_tail_init(&fpq->processing[i], &to_queue); + for (i = 0; i < FUSE_PQ_HASH_SIZE; i++) { + struct list_head *this_queue = &fpq->processing[i]; + + list_for_each_entry(req, this_queue, list) + clear_bit(FR_SENT, &req->flags); + list_splice_tail_init(this_queue, &to_queue); + } spin_unlock(&fpq->lock); } spin_unlock(&fch->lock); - list_for_each_entry_safe(req, next, &to_queue, list) { - set_bit(FR_PENDING, &req->flags); - clear_bit(FR_SENT, &req->flags); - /* mark the request as resend request */ - req->in.h.unique |= FUSE_UNIQUE_RESEND; - } - spin_lock(&fiq->lock); if (!fiq->connected) { spin_unlock(&fiq->lock); - list_for_each_entry(req, &to_queue, list) - clear_bit(FR_PENDING, &req->flags); fuse_dev_end_requests(&to_queue); return; } @@ -1801,6 +1800,10 @@ void fuse_chan_resend(struct fuse_chan *fch) * intr_entry on fiq->interrupts after the request is re-queued. */ list_for_each_entry(req, &to_queue, list) { + set_bit(FR_PENDING, &req->flags); + /* mark the request as resend request */ + req->in.h.unique |= FUSE_UNIQUE_RESEND; + if (test_bit(FR_INTERRUPTED, &req->flags)) list_del_init(&req->intr_entry); } diff --git a/fs/fuse/dev.h b/fs/fuse/dev.h index aed69fd14c41df..8d25378c091830 100644 --- a/fs/fuse/dev.h +++ b/fs/fuse/dev.h @@ -22,6 +22,7 @@ struct fuse_chan_param { unsigned int minor; unsigned int max_write; unsigned int max_pages; + bool io_uring_enabled; }; struct fuse_chan *fuse_chan_new(void); @@ -34,7 +35,6 @@ void fuse_chan_max_background_set(struct fuse_chan *fch, unsigned int val); unsigned int fuse_chan_num_waiting(struct fuse_chan *fch); void fuse_chan_set_fc(struct fuse_chan *fch, struct fuse_conn *fc); void fuse_chan_set_initialized(struct fuse_chan *fch, struct fuse_chan_param *param); -void fuse_chan_io_uring_enable(struct fuse_chan *fch); ssize_t fuse_chan_send(struct fuse_chan *fch, struct fuse_args *args); int fuse_chan_send_bg(struct fuse_chan *fch, struct fuse_args *args, gfp_t gfp_flags); int fuse_chan_send_notify_reply(struct fuse_chan *fch, struct fuse_args *args, u64 unique); diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c index 77c8cec43d9c65..82da15a9cc787a 100644 --- a/fs/fuse/dev_uring.c +++ b/fs/fuse/dev_uring.c @@ -204,7 +204,7 @@ void fuse_uring_destruct(struct fuse_chan *fch) return; for (qid = 0; qid < ring->nr_queues; qid++) { - struct fuse_ring_queue *queue = ring->queues[qid]; + struct fuse_ring_queue *queue = READ_ONCE(ring->queues[qid]); struct fuse_ring_ent *ent, *next; if (!queue) @@ -223,7 +223,7 @@ void fuse_uring_destruct(struct fuse_chan *fch) kfree(queue->fpq.processing); kfree(queue); - ring->queues[qid] = NULL; + WRITE_ONCE(ring->queues[qid], NULL); } kfree(ring->queues); @@ -238,7 +238,6 @@ static struct fuse_ring *fuse_uring_create(struct fuse_chan *fch) { struct fuse_ring *ring; size_t nr_queues = num_possible_cpus(); - struct fuse_ring *res = NULL; size_t max_payload_size; ring = kzalloc_obj(*ring, GFP_KERNEL_ACCOUNT); @@ -258,12 +257,6 @@ static struct fuse_ring *fuse_uring_create(struct fuse_chan *fch) spin_unlock(&fch->lock); goto out_err; } - if (fch->ring) { - /* race, another thread created the ring in the meantime */ - spin_unlock(&fch->lock); - res = fch->ring; - goto out_err; - } init_waitqueue_head(&ring->stop_waitq); @@ -278,7 +271,13 @@ static struct fuse_ring *fuse_uring_create(struct fuse_chan *fch) out_err: kfree(ring->queues); kfree(ring); - return res; + return NULL; +} + +void fuse_uring_conn_init(struct fuse_chan *fch) +{ + if (fuse_uring_create(fch)) + fch->io_uring = 1; } static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring, @@ -321,9 +320,11 @@ static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring, } /* - * write_once and lock as the caller mostly doesn't take the lock at all + * fch->lock serializes concurrent creators for this qid. + * smp_store_release() are for the lockless readers who must see a + * fully initialized queue after &ring->queues[qid] is set */ - WRITE_ONCE(ring->queues[qid], queue); + smp_store_release(&ring->queues[qid], queue); spin_unlock(&fch->lock); return queue; @@ -434,7 +435,7 @@ static void fuse_uring_log_ent_state(struct fuse_ring *ring) struct fuse_ring_ent *ent; for (qid = 0; qid < ring->nr_queues; qid++) { - struct fuse_ring_queue *queue = ring->queues[qid]; + struct fuse_ring_queue *queue = READ_ONCE(ring->queues[qid]); if (!queue) continue; @@ -744,6 +745,7 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent, { struct fuse_ring_queue *queue = ent->queue; struct fuse_ring *ring = queue->ring; + struct fuse_in_header in_header; int err; err = -EIO; @@ -765,8 +767,9 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent, } /* copy fuse_in_header */ - return copy_header_to_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->in.h, - sizeof(req->in.h)); + in_header = req->in.h; + return copy_header_to_ring(ent, FUSE_URING_HEADER_IN_OUT, &in_header, + sizeof(in_header)); } static int fuse_uring_prepare_send(struct fuse_ring_ent *ent, @@ -871,11 +874,13 @@ static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req, unsigned int issue_flags) { struct fuse_ring *ring = ent->queue->ring; + struct fuse_out_header out_header; ssize_t err = -EFAULT; - if (copy_header_from_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->out.h, - sizeof(req->out.h))) + if (copy_header_from_ring(ent, FUSE_URING_HEADER_IN_OUT, &out_header, + sizeof(out_header))) goto out; + req->out.h = out_header; err = fuse_uring_out_header_has_err(&req->out.h, req); if (err) { @@ -967,7 +972,7 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags, if (qid >= ring->nr_queues) return -EINVAL; - queue = ring->queues[qid]; + queue = READ_ONCE(ring->queues[qid]); if (!queue) return err; fpq = &queue->fpq; @@ -1035,7 +1040,7 @@ static bool is_ring_ready(struct fuse_ring *ring, int current_qid) if (current_qid == qid) continue; - queue = ring->queues[qid]; + queue = READ_ONCE(ring->queues[qid]); if (!queue) { ready = false; break; @@ -1176,26 +1181,21 @@ static int fuse_uring_register(struct io_uring_cmd *cmd, struct fuse_ring *ring = smp_load_acquire(&fch->ring); struct fuse_ring_queue *queue; struct fuse_ring_ent *ent; - int err; unsigned int qid = READ_ONCE(cmd_req->qid); - err = -ENOMEM; - if (!ring) { - ring = fuse_uring_create(fch); - if (!ring) - return err; - } + if (!ring) + return -EINVAL; if (qid >= ring->nr_queues) { pr_info_ratelimited("fuse: Invalid ring qid %u\n", qid); return -EINVAL; } - queue = ring->queues[qid]; + queue = READ_ONCE(ring->queues[qid]); if (!queue) { queue = fuse_uring_create_queue(ring, qid); if (!queue) - return err; + return -ENOMEM; } /* @@ -1330,7 +1330,7 @@ static struct fuse_ring_queue *fuse_uring_task_to_queue(struct fuse_ring *ring) ring->nr_queues)) qid = 0; - queue = ring->queues[qid]; + queue = READ_ONCE(ring->queues[qid]); WARN_ONCE(!queue, "Missing queue for qid %d\n", qid); return queue; diff --git a/fs/fuse/dev_uring_i.h b/fs/fuse/dev_uring_i.h index 55f8d04e4b0b2d..d721a4fc0215c5 100644 --- a/fs/fuse/dev_uring_i.h +++ b/fs/fuse/dev_uring_i.h @@ -135,6 +135,7 @@ struct fuse_ring { bool ready; }; +void fuse_uring_conn_init(struct fuse_chan *fch); void fuse_uring_stop_queues(struct fuse_ring *ring); void fuse_uring_abort_end_requests(struct fuse_ring *ring); int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags); @@ -174,6 +175,10 @@ static inline bool fuse_uring_ready(struct fuse_chan *fch) #else /* CONFIG_FUSE_IO_URING */ +static inline void fuse_uring_conn_init(struct fuse_chan *fch) +{ +} + static inline void fuse_uring_abort(struct fuse_chan *fch) { } diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 0e2a1039fa43ea..48763bc192f3c4 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -2161,10 +2161,8 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry, filemap_invalidate_lock(mapping); fault_blocked = true; err = fuse_dax_break_layouts(inode, 0, -1); - if (err) { - filemap_invalidate_unlock(mapping); - return err; - } + if (err) + goto unlock; } if (attr->ia_valid & ATTR_OPEN) { @@ -2191,7 +2189,7 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ATTR_TIMES_SET)) { err = write_inode_now(inode, true); if (err) - return err; + goto unlock; fuse_set_nowrite(inode); fuse_release_nowrite(inode); @@ -2299,6 +2297,7 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry, clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); +unlock: if (fault_blocked) filemap_invalidate_unlock(mapping); return err; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index ceada75310b880..e7b2a839f08144 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -272,7 +272,7 @@ static int fuse_open(struct inode *inode, struct file *file) filemap_invalidate_lock(inode->i_mapping); err = fuse_dax_break_layouts(inode, 0, -1); if (err) - goto out_inode_unlock; + goto out_unlock; } if (is_wb_truncate || dax_truncate) @@ -296,9 +296,9 @@ static int fuse_open(struct inode *inode, struct file *file) else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) invalidate_inode_pages2(inode->i_mapping); } +out_unlock: if (dax_truncate) filemap_invalidate_unlock(inode->i_mapping); -out_inode_unlock: if (is_wb_truncate || dax_truncate) inode_unlock(inode); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index d975073c602963..ce4891a04a3789 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1272,6 +1272,7 @@ static void process_init_reply(struct fuse_args *args, int error) struct fuse_mount *fm = ia->fm; struct fuse_conn *fc = fm->fc; struct fuse_init_out *arg = &ia->out; + bool io_uring_enabled = false; bool ok = true; if (error || arg->major != FUSE_KERNEL_VERSION) @@ -1402,7 +1403,7 @@ static void process_init_reply(struct fuse_args *args, int error) ok = false; } if (flags & FUSE_OVER_IO_URING && fuse_uring_enabled()) - fuse_chan_io_uring_enable(fc->chan); + io_uring_enabled = true; if (flags & FUSE_REQUEST_TIMEOUT) timeout = arg->request_timeout; @@ -1432,6 +1433,7 @@ static void process_init_reply(struct fuse_args *args, int error) .minor = fc->minor, .max_write = fc->max_write, .max_pages = fc->max_pages, + .io_uring_enabled = io_uring_enabled, }; fuse_chan_set_initialized(fc->chan, &cp); } diff --git a/fs/iomap/bio.c b/fs/iomap/bio.c index dc8ac7e370a5c3..30ef78a66b4f09 100644 --- a/fs/iomap/bio.c +++ b/fs/iomap/bio.c @@ -179,7 +179,7 @@ int iomap_bio_read_folio_range_sync(const struct iomap_iter *iter, if (srcmap->flags & IOMAP_F_INTEGRITY) fs_bio_integrity_alloc(&bio); error = submit_bio_wait(&bio); - if (srcmap->flags & IOMAP_F_INTEGRITY) { + if (bio_integrity(&bio)) { if (!error) error = fs_bio_integrity_verify(&bio, sector, len); fs_bio_integrity_free(&bio); diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index e2cd5f92babedb..01daed391856c5 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -358,6 +358,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter, iomap_max_bio_size(&iter->iomap), alignment); else ret = bio_iov_iter_get_pages(bio, dio->submit.iter, + bdev_dma_alignment(bio->bi_bdev), alignment - 1); if (unlikely(ret)) goto out_put_bio; diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 1508e2f5446211..5132737120108c 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -358,15 +358,16 @@ int jbd2_cleanup_journal_tail(journal_t *journal) /* * journal_shrink_one_cp_list * - * Find all the written-back checkpoint buffers in the given list - * and try to release them. If the whole transaction is released, set - * the 'released' parameter. Return the number of released checkpointed - * buffers. + * Find written-back checkpoint buffers in the given list and try to release + * them. If 'nr_to_scan' is set, scan at most that many buffers. If the whole + * transaction is released, set the 'released' parameter. Return the number of + * released checkpointed buffers. * * Called with j_list_lock held. */ static unsigned long journal_shrink_one_cp_list(struct journal_head *jh, enum jbd2_shrink_type type, + unsigned long *nr_to_scan, bool *released) { struct journal_head *last_jh; @@ -375,13 +376,15 @@ static unsigned long journal_shrink_one_cp_list(struct journal_head *jh, int ret; *released = false; - if (!jh) + if (!jh || (nr_to_scan && !*nr_to_scan)) return 0; last_jh = jh->b_cpprev; do { jh = next_jh; next_jh = jh->b_cpnext; + if (nr_to_scan) + (*nr_to_scan)--; if (type == JBD2_SHRINK_DESTROY) { ret = __jbd2_journal_remove_checkpoint(jh); @@ -389,7 +392,7 @@ static unsigned long journal_shrink_one_cp_list(struct journal_head *jh, ret = jbd2_journal_try_remove_checkpoint(jh); if (ret < 0) { if (type == JBD2_SHRINK_BUSY_SKIP) - continue; + goto next; break; } } @@ -400,9 +403,10 @@ static unsigned long journal_shrink_one_cp_list(struct journal_head *jh, break; } +next: if (need_resched()) break; - } while (jh != last_jh); + } while (jh != last_jh && (!nr_to_scan || *nr_to_scan)); return nr_freed; } @@ -424,7 +428,6 @@ unsigned long jbd2_journal_shrink_checkpoint_list(journal_t *journal, tid_t first_tid = 0, last_tid = 0, next_tid = 0; tid_t tid = 0; unsigned long nr_freed = 0; - unsigned long freed; bool first_set = false; again: @@ -457,10 +460,9 @@ unsigned long jbd2_journal_shrink_checkpoint_list(journal_t *journal, next_transaction = transaction->t_cpnext; tid = transaction->t_tid; - freed = journal_shrink_one_cp_list(transaction->t_checkpoint_list, - JBD2_SHRINK_BUSY_SKIP, &released); - nr_freed += freed; - (*nr_to_scan) -= min(*nr_to_scan, freed); + nr_freed += journal_shrink_one_cp_list(transaction->t_checkpoint_list, + JBD2_SHRINK_BUSY_SKIP, + nr_to_scan, &released); if (*nr_to_scan == 0) break; if (need_resched() || spin_needbreak(&journal->j_list_lock)) @@ -516,7 +518,7 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal, transaction = next_transaction; next_transaction = transaction->t_cpnext; journal_shrink_one_cp_list(transaction->t_checkpoint_list, - type, &released); + type, NULL, &released); /* * This function only frees up some memory if possible so we * dont have an obligation to finish processing. Bail out if diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index f06faf577cea7c..f8018bfe9c6460 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -487,9 +487,12 @@ static const struct file_lock_operations nlmclnt_lock_ops = { static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host) { fl->fl_u.nfs_fl.state = 0; + fl->fl_ops = NULL; fl->fl_u.nfs_fl.owner = nlmclnt_find_lockowner(host, fl->c.flc_owner); INIT_LIST_HEAD(&fl->fl_u.nfs_fl.list); + if (!fl->fl_u.nfs_fl.owner) + return; fl->fl_ops = &nlmclnt_lock_ops; } diff --git a/fs/lockd/lockd.h b/fs/lockd/lockd.h index e418a50c418035..14cc952fe81a24 100644 --- a/fs/lockd/lockd.h +++ b/fs/lockd/lockd.h @@ -314,7 +314,7 @@ void nsm_release(struct nsm_handle *nsm); * This is used in garbage collection and resource reclaim * A return value != 0 means destroy the lock/block/share */ -typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref); +typedef int (*nlm_host_match_fn_t)(void *owner, struct nlm_host *ref); /* * Server-side lock handling diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 490551369ef2a9..ee90e743064afb 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -47,7 +47,7 @@ static struct svc_program nlmsvc_program; -const struct nlmsvc_binding *nlmsvc_ops; +const struct nlmsvc_binding __rcu *nlmsvc_ops; EXPORT_SYMBOL_GPL(nlmsvc_ops); static DEFINE_MUTEX(nlmsvc_mutex); @@ -142,7 +142,7 @@ lockd(void *vrqstp) nlmsvc_retry_blocked(rqstp); svc_recv(rqstp, 0); } - if (nlmsvc_ops) + if (rcu_access_pointer(nlmsvc_ops)) nlmsvc_invalidate_all(); nlm_shutdown_hosts(); cancel_delayed_work_sync(&ln->grace_period_end); diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 78e675470c4b28..b73004a7987e8f 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -128,7 +128,7 @@ nlm4svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored) { struct nlm_host *host; - if (!nlmsvc_ops) + if (!rcu_access_pointer(nlmsvc_ops)) return NULL; host = nlmsvc_lookup_host(rqstp, caller.data, caller.len); if (!host) @@ -872,7 +872,8 @@ static __be32 nlm4svc_proc_granted_msg(struct svc_rqst *rqstp) struct nlm4_testargs_wrapper *argp = rqstp->rq_argp; struct nlm_host *host; - host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false); + host = nlmsvc_lookup_host(rqstp, argp->xdrgen.alock.caller_name.data, + argp->xdrgen.alock.caller_name.len); if (!host) return rpc_system_err; @@ -894,7 +895,7 @@ static __be32 nlm4svc_proc_granted_res(struct svc_rqst *rqstp) { struct nlm4_res_wrapper *argp = rqstp->rq_argp; - if (!nlmsvc_ops) + if (!rcu_access_pointer(nlmsvc_ops)) return rpc_success; if (nlm4_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie)) diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 386a881b520fd9..d410b8c698937f 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -133,7 +133,7 @@ nlm3svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored) { struct nlm_host *host; - if (!nlmsvc_ops) + if (!rcu_access_pointer(nlmsvc_ops)) return NULL; host = nlmsvc_lookup_host(rqstp, caller.data, caller.len); if (!host) @@ -924,7 +924,7 @@ static __be32 nlmsvc_proc_granted_res(struct svc_rqst *rqstp) { struct nlm_res_wrapper *argp = rqstp->rq_argp; - if (!nlmsvc_ops) + if (!rcu_access_pointer(nlmsvc_ops)) return rpc_success; if (nlm_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie)) diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index a0d1a6fbf61ee9..ffab76278bb26d 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -90,22 +90,35 @@ int lock_to_openmode(struct file_lock *lock) static __be32 nlm_do_fopen(struct svc_rqst *rqstp, struct nlm_file *file, int mode) { + const struct nlmsvc_binding *ops; __be32 nlmerr = nlm__int__failed; __be32 deferred = 0; int error; int m; + rcu_read_lock(); + ops = rcu_dereference(nlmsvc_ops); + if (!ops || !try_module_get(ops->owner)) { + rcu_read_unlock(); + return nlm__int__failed; + } + rcu_read_unlock(); + for (m = O_RDONLY; m <= O_WRONLY; m++) { struct file **fp = &file->f_file[m]; if (mode != O_RDWR && mode != m) continue; - if (*fp) + if (*fp) { + module_put(ops->owner); return nlm_granted; + } - error = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, m); - if (!error) + error = ops->fopen(rqstp, &file->f_handle, fp, m); + if (!error) { + module_put(ops->owner); return nlm_granted; + } dprintk("lockd: open failed (errno %d)\n", error); switch (error) { @@ -122,6 +135,7 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp, } } + module_put(ops->owner); return deferred ? deferred : nlmerr; } @@ -185,6 +199,33 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, goto out_unlock; } +/* + * Release the struct file references held by a nlm_file. + */ +static void nlm_release_files(struct nlm_file *file) +{ + const struct nlmsvc_binding *ops; + bool have_ops; + + rcu_read_lock(); + ops = rcu_dereference(nlmsvc_ops); + have_ops = ops && try_module_get(ops->owner); + rcu_read_unlock(); + + if (have_ops) { + if (file->f_file[O_RDONLY]) + ops->fclose(file->f_file[O_RDONLY]); + if (file->f_file[O_WRONLY]) + ops->fclose(file->f_file[O_WRONLY]); + module_put(ops->owner); + } else { + if (file->f_file[O_RDONLY]) + fput(file->f_file[O_RDONLY]); + if (file->f_file[O_WRONLY]) + fput(file->f_file[O_WRONLY]); + } +} + /* * Delete a file after having released all locks, blocks and shares */ @@ -194,10 +235,7 @@ nlm_delete_file(struct nlm_file *file) nlm_debug_print_file("closing file", file); if (!hlist_unhashed(&file->f_list)) { hlist_del(&file->f_list); - if (file->f_file[O_RDONLY]) - nlmsvc_ops->fclose(file->f_file[O_RDONLY]); - if (file->f_file[O_WRONLY]) - nlmsvc_ops->fclose(file->f_file[O_WRONLY]); + nlm_release_files(file); kfree(file); } else { printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); @@ -312,12 +350,10 @@ nlm_file_inuse(struct nlm_file *file) return 0; } -static void nlm_close_files(struct nlm_file *file) +static void nlm_file_release(struct nlm_file *file) { - if (file->f_file[O_RDONLY]) - nlmsvc_ops->fclose(file->f_file[O_RDONLY]); - if (file->f_file[O_WRONLY]) - nlmsvc_ops->fclose(file->f_file[O_WRONLY]); + if (!nlm_file_inuse(file)) + nlm_delete_file(file); } /* @@ -327,32 +363,41 @@ static int nlm_traverse_files(void *data, nlm_host_match_fn_t match, int (*is_failover_file)(void *data, struct nlm_file *file)) { - struct hlist_node *next; - struct nlm_file *file; + struct nlm_file *file, *next; int i, ret = 0; mutex_lock(&nlm_file_mutex); for (i = 0; i < FILE_NRHASH; i++) { - hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) { - if (is_failover_file && !is_failover_file(data, file)) - continue; + file = hlist_entry_safe(nlm_files[i].first, + struct nlm_file, f_list); + if (file) file->f_count++; - mutex_unlock(&nlm_file_mutex); - - /* Traverse locks, blocks and shares of this file - * and update file->f_locks count */ - if (nlm_inspect_file(data, file, match)) - ret = 1; + while (file) { + /* + * Pin the next neighbour before we drop the mutex + * for nlm_inspect_file(); a concurrent + * nlm_release_file() under the same mutex would + * otherwise be free to unlink and kfree it during + * the unlock window, leaving us to dereference a + * freed slab when we walked to next afterwards. + */ + next = hlist_entry_safe(file->f_list.next, + struct nlm_file, f_list); + if (next) + next->f_count++; + + if (!is_failover_file || is_failover_file(data, file)) { + mutex_unlock(&nlm_file_mutex); + + if (nlm_inspect_file(data, file, match)) + ret = 1; + + mutex_lock(&nlm_file_mutex); + } - mutex_lock(&nlm_file_mutex); file->f_count--; - /* No more references to this file. Let go of it. */ - if (list_empty(&file->f_blocks) && !file->f_locks - && !file->f_shares && !file->f_count) { - hlist_del(&file->f_list); - nlm_close_files(file); - kfree(file); - } + nlm_file_release(file); + file = next; } } mutex_unlock(&nlm_file_mutex); @@ -512,7 +557,7 @@ EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb); static int nlmsvc_match_ip(void *datap, struct nlm_host *host) { - return rpc_cmp_addr(nlm_srcaddr(host), datap); + return rpc_cmp_addr(nlm_srcaddr(datap), (struct sockaddr *)host); } /** diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index ff4e9fd38e83f2..bc282b744f34fa 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -231,8 +231,9 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) cb_info->users++; err_net: if (!cb_info->users) { + xprt_svc_shutdown_bc(xprt); svc_set_num_threads(cb_info->serv, 0, 0); - svc_destroy(&cb_info->serv); + xprt_svc_destroy_nullify_bc(xprt, &cb_info->serv); } err_create: mutex_unlock(&nfs_callback_mutex); @@ -254,6 +255,7 @@ void nfs_callback_down(int minorversion, struct net *net, struct rpc_xprt *xprt) mutex_lock(&nfs_callback_mutex); serv = cb_info->serv; + xprt_svc_shutdown_bc(xprt); nfs_callback_down_net(minorversion, serv, net); cb_info->users--; if (cb_info->users == 0) { diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 71c271a1700a92..819e435c3a42b7 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -915,20 +915,22 @@ static int nfs4_server_common_setup(struct nfs_server *server, return error; /* data servers support only a subset of NFSv4.1 */ - if (is_ds_only_client(server->nfs_client)) - return -EPROTONOSUPPORT; + if (is_ds_only_client(server->nfs_client)) { + error = -EPROTONOSUPPORT; + goto out_free_delegation_hash; + } /* We must ensure the session is initialised first */ error = nfs4_init_session(server->nfs_client); if (error < 0) - return error; + goto out_free_delegation_hash; nfs_server_set_init_caps(server); /* Probe the root fh to retrieve its FSID and filehandle */ error = nfs4_get_rootfh(server, mntfh, auth_probe); if (error < 0) - return error; + goto out_free_delegation_hash; dprintk("Server FSID: %llx:%llx\n", (unsigned long long) server->fsid.major, @@ -937,7 +939,7 @@ static int nfs4_server_common_setup(struct nfs_server *server, error = nfs_probe_server(server, mntfh); if (error < 0) - return error; + goto out_free_delegation_hash; nfs4_session_limit_rwsize(server); nfs4_session_limit_xasize(server); @@ -949,6 +951,11 @@ static int nfs4_server_common_setup(struct nfs_server *server, server->mount_time = jiffies; server->destroy = nfs4_destroy_server; return 0; + +out_free_delegation_hash: + kfree(server->delegation_hash_table); + server->delegation_hash_table = NULL; + return error; } /* diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 7715e2bd5871bb..b1a30a16287f3e 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1389,7 +1389,7 @@ pnfs_layout_need_return(struct pnfs_layout_hdr *lo) return false; return pnfs_mark_layout_stateid_return(lo, &lo->plh_return_segs, lo->plh_return_iomode, - lo->plh_return_seq) != EBUSY; + lo->plh_return_seq) != -EBUSY; } static void pnfs_layoutreturn_before_put_layout_hdr(struct pnfs_layout_hdr *lo) @@ -2643,6 +2643,7 @@ pnfs_layout_process(struct nfs4_layoutget *lgp) spin_unlock(&ino->i_lock); lseg->pls_layout = lo; NFS_SERVER(ino)->pnfs_curr_ld->free_lseg(lseg); + pnfs_free_lseg_list(&free_me); return ERR_PTR(-EAGAIN); } diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c index dd715cdb6c0431..85aa03a7b020c5 100644 --- a/fs/nfs_common/nfslocalio.c +++ b/fs/nfs_common/nfslocalio.c @@ -292,8 +292,22 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid, localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt, cred, nfs_fh, pnf, fmode); if (!IS_ERR(localio) && nfs_uuid_add_file(uuid, nfl) < 0) { - /* Delete the cached file when racing with nfs_uuid_put() */ + /* + * Delete the cached file when racing with nfs_uuid_put(). + * Since nfl->nfs_uuid was never published via + * rcu_assign_pointer(), nfs_close_local_fh() will early-return + * and cannot clean up after us. Drop the slot's file ref and + * its paired net ref, then drop the caller-owned nfsd_file ref + * (+1) and the entry-time nfsd_net ref carried via nf->nf_net, + * and return -ENXIO so the caller never dereferences the + * now-cleared localio. + */ + struct nfsd_file __rcu *tmp = + (struct nfsd_file __force __rcu *)localio; + nfs_to_nfsd_file_put_local(pnf); + nfs_to_nfsd_file_put_local(&tmp); + localio = ERR_PTR(-ENXIO); } nfs_to_nfsd_net_put(net); diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c index 24511c3208db9e..1ea2bfd5182555 100644 --- a/fs/nfsd/filecache.c +++ b/fs/nfsd/filecache.c @@ -55,6 +55,17 @@ /* We only care about NFSD_MAY_READ/WRITE for this cache */ #define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE|NFSD_MAY_LOCALIO) +/* If the shrinker runs between calls to list_lru_walk_node() in + * nfsd_file_gc(), the "remaining" count will be wrong. This could + * result in premature freeing of some files. This may not matter much + * but is easy to fix with this spinlock which temporarily disables + * the shrinker. + * + * It also serializes callers of nfsd_file_dispose_list_delayed() + * against per-net shutdown. + */ +static DEFINE_SPINLOCK(nfsd_gc_lock); + static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits); static DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions); static DEFINE_PER_CPU(unsigned long, nfsd_file_allocations); @@ -62,11 +73,6 @@ static DEFINE_PER_CPU(unsigned long, nfsd_file_releases); static DEFINE_PER_CPU(unsigned long, nfsd_file_total_age); static DEFINE_PER_CPU(unsigned long, nfsd_file_evictions); -struct nfsd_fcache_disposal { - spinlock_t lock; - struct list_head freeme; -}; - static struct kmem_cache *nfsd_file_slab; static struct kmem_cache *nfsd_file_mark_slab; static struct list_lru nfsd_file_lru; @@ -327,8 +333,11 @@ static void nfsd_file_lru_add(struct nfsd_file *nf) refcount_inc(&nf->nf_ref); if (list_lru_add_obj(&nfsd_file_lru, &nf->nf_lru)) trace_nfsd_file_lru_add(nf); - else - WARN_ON(1); + else { + refcount_dec(&nf->nf_ref); + WARN_ON_ONCE(1); + return; + } nfsd_file_schedule_laundrette(); } @@ -419,25 +428,31 @@ nfsd_file_dispose_list(struct list_head *dispose) } /** - * nfsd_file_dispose_list_delayed - move list of dead files to net's freeme list + * nfsd_file_dispose_list_delayed - queue dead files for nfsd thread disposal * @dispose: list of nfsd_files to be disposed * - * Transfers each file to the "freeme" list for its nfsd_net, to eventually - * be disposed of by the per-net garbage collector. + * Transfers each file to the dispose list in its nfsd_net and wakes an nfsd + * thread to do the actual close. This keeps the cost of fput() in the nfsd + * threads rather than in the shrinker or GC worker. + * + * All callers must hold nfsd_gc_lock, so that nfsd_file_cache_shutdown_net() + * can synchronize against them before draining the per-net dispose list. + * This guarantees nf_net is still live when we call net_generic(). */ static void nfsd_file_dispose_list_delayed(struct list_head *dispose) { - while(!list_empty(dispose)) { + lockdep_assert_held(&nfsd_gc_lock); + + while (!list_empty(dispose)) { struct nfsd_file *nf = list_first_entry(dispose, struct nfsd_file, nf_gc); struct nfsd_net *nn = net_generic(nf->nf_net, nfsd_net_id); - struct nfsd_fcache_disposal *l = nn->fcache_disposal; struct svc_serv *serv; - spin_lock(&l->lock); - list_move_tail(&nf->nf_gc, &l->freeme); - spin_unlock(&l->lock); + spin_lock(&nn->fcache_dispose_lock); + list_move_tail(&nf->nf_gc, &nn->fcache_dispose_list); + spin_unlock(&nn->fcache_dispose_lock); /* * The filecache laundrette is shut down after the @@ -461,21 +476,28 @@ nfsd_file_dispose_list_delayed(struct list_head *dispose) */ void nfsd_file_net_dispose(struct nfsd_net *nn) { - struct nfsd_fcache_disposal *l = nn->fcache_disposal; - - if (!list_empty(&l->freeme)) { + if (!list_empty(&nn->fcache_dispose_list)) { LIST_HEAD(dispose); int i; - spin_lock(&l->lock); - for (i = 0; i < 8 && !list_empty(&l->freeme); i++) - list_move(l->freeme.next, &dispose); - spin_unlock(&l->lock); - if (!list_empty(&l->freeme)) - /* Wake up another thread to share the work + spin_lock(&nn->fcache_dispose_lock); + for (i = 0; i < 8 && !list_empty(&nn->fcache_dispose_list); i++) + list_move(nn->fcache_dispose_list.next, &dispose); + spin_unlock(&nn->fcache_dispose_lock); + if (!list_empty(&nn->fcache_dispose_list)) { + /* + * Wake up another thread to share the work * *before* doing any actual disposing. + * + * The filecache laundrette is shut down after + * the nn->nfsd_serv pointer is cleared, but + * before the svc_serv is freed. */ - svc_wake_up(nn->nfsd_serv); + struct svc_serv *serv = nn->nfsd_serv; + + if (serv) + svc_wake_up(serv); + } nfsd_file_dispose_list(&dispose); } } @@ -552,13 +574,6 @@ nfsd_file_gc_cb(struct list_head *item, struct list_lru_one *lru, return nfsd_file_lru_cb(item, lru, arg); } -/* If the shrinker runs between calls to list_lru_walk_node() in - * nfsd_file_gc(), the "remaining" count will be wrong. This could - * result in premature freeing of some files. This may not matter much - * but is easy to fix with this spinlock which temporarily disables - * the shrinker. - */ -static DEFINE_SPINLOCK(nfsd_gc_lock); static void nfsd_file_gc(void) { @@ -581,9 +596,9 @@ nfsd_file_gc(void) remaining = 0; } } + nfsd_file_dispose_list_delayed(&dispose); spin_unlock(&nfsd_gc_lock); trace_nfsd_file_gc_removed(ret, list_lru_count(&nfsd_file_lru)); - nfsd_file_dispose_list_delayed(&dispose); } static void @@ -611,9 +626,9 @@ nfsd_file_lru_scan(struct shrinker *s, struct shrink_control *sc) ret = list_lru_shrink_walk(&nfsd_file_lru, sc, nfsd_file_lru_cb, &dispose); + nfsd_file_dispose_list_delayed(&dispose); spin_unlock(&nfsd_gc_lock); trace_nfsd_file_shrinker_removed(ret, list_lru_count(&nfsd_file_lru)); - nfsd_file_dispose_list_delayed(&dispose); return ret; } @@ -686,11 +701,11 @@ nfsd_file_queue_for_close(struct inode *inode, struct list_head *dispose) } /** - * nfsd_file_close_inode - attempt a delayed close of a nfsd_file + * nfsd_file_close_inode - attempt a deferred close of a nfsd_file * @inode: inode of the file to attempt to remove * * Close out any open nfsd_files that can be reaped for @inode. The - * actual freeing is deferred to the dispose_list_delayed infrastructure. + * actual freeing is deferred to the nfsd service threads. * * This is used by the fsnotify callbacks and setlease notifier. */ @@ -699,8 +714,10 @@ nfsd_file_close_inode(struct inode *inode) { LIST_HEAD(dispose); + spin_lock(&nfsd_gc_lock); nfsd_file_queue_for_close(inode, &dispose); nfsd_file_dispose_list_delayed(&dispose); + spin_unlock(&nfsd_gc_lock); } /** @@ -940,42 +957,14 @@ __nfsd_file_cache_purge(struct net *net) nfsd_file_dispose_list(&dispose); } -static struct nfsd_fcache_disposal * -nfsd_alloc_fcache_disposal(void) -{ - struct nfsd_fcache_disposal *l; - - l = kmalloc_obj(*l); - if (!l) - return NULL; - spin_lock_init(&l->lock); - INIT_LIST_HEAD(&l->freeme); - return l; -} - -static void -nfsd_free_fcache_disposal(struct nfsd_fcache_disposal *l) -{ - nfsd_file_dispose_list(&l->freeme); - kfree(l); -} - -static void -nfsd_free_fcache_disposal_net(struct net *net) -{ - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - struct nfsd_fcache_disposal *l = nn->fcache_disposal; - - nfsd_free_fcache_disposal(l); -} - int nfsd_file_cache_start_net(struct net *net) { struct nfsd_net *nn = net_generic(net, nfsd_net_id); - nn->fcache_disposal = nfsd_alloc_fcache_disposal(); - return nn->fcache_disposal ? 0 : -ENOMEM; + spin_lock_init(&nn->fcache_dispose_lock); + INIT_LIST_HEAD(&nn->fcache_dispose_list); + return 0; } /** @@ -994,8 +983,18 @@ nfsd_file_cache_purge(struct net *net) void nfsd_file_cache_shutdown_net(struct net *net) { + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + nfsd_file_cache_purge(net); - nfsd_free_fcache_disposal_net(net); + /* + * Ensure any in-progress shrinker, GC, or fsnotify/lease callback + * (all of which hold nfsd_gc_lock while calling + * nfsd_file_dispose_list_delayed()) has fully completed before + * draining the per-net dispose list. + */ + spin_lock(&nfsd_gc_lock); + spin_unlock(&nfsd_gc_lock); + nfsd_file_dispose_list(&nn->fcache_dispose_list); } void @@ -1227,7 +1226,7 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct net *net, nf->nf_mark = nfsd_file_mark_find_or_create(inode); if (type != S_IFREG || nf->nf_mark) { - if (file) { + if (file && (file->f_mode & FMODE_OPENED)) { get_file(file); nf->nf_file = file; status = nfs_ok; @@ -1374,12 +1373,12 @@ nfsd_file_acquire_local(struct net *net, struct svc_cred *cred, * @rqstp: the RPC transaction being executed * @fhp: the NFS filehandle of the file just created * @may_flags: NFSD_MAY_ settings for the file - * @file: cached, already-open file (may be NULL) + * @file: cached, already-open file (may be NULL or not yet opened) * @pnf: OUT: new or found "struct nfsd_file" object * * Acquire a nfsd_file object that is not GC'ed. If one doesn't already exist, - * and @file is non-NULL, use it to instantiate a new nfsd_file instead of - * opening a new one. + * and @file has FMODE_OPENED set, use it to instantiate a new nfsd_file + * instead of opening a new one. * * Return values: * %nfs_ok - @pnf points to an nfsd_file with its reference diff --git a/fs/nfsd/flexfilelayoutxdr.c b/fs/nfsd/flexfilelayoutxdr.c index f9f7e38cba13fb..374e52d3064a65 100644 --- a/fs/nfsd/flexfilelayoutxdr.c +++ b/fs/nfsd/flexfilelayoutxdr.c @@ -30,19 +30,24 @@ nfsd4_ff_encode_layoutget(struct xdr_stream *xdr, struct ff_idmap uid; struct ff_idmap gid; - fh_len = 4 + fl->fh.size; + fh_len = 4 + xdr_align_size(fl->fh.size); uid.len = sprintf(uid.buf, "%u", from_kuid(&init_user_ns, fl->uid)); gid.len = sprintf(gid.buf, "%u", from_kgid(&init_user_ns, fl->gid)); - /* 8 + len for recording the length, name, and padding */ - ds_len = 20 + sizeof(stateid_opaque_t) + 4 + fh_len + - 8 + uid.len + 8 + gid.len; + /* data server entry: deviceid + efficiency + stateid + fh list + + * user + group + flags + stats_collect_hint + */ + ds_len = 16 + 4 + 4 + sizeof(stateid_opaque_t) + 4 + fh_len + + 4 + xdr_align_size(uid.len) + + 4 + xdr_align_size(gid.len) + + 4 + 4; + /* mirror: ds_count + ds */ mirror_len = 4 + ds_len; - /* The layout segment */ - len = 20 + mirror_len; + /* stripe_unit + mirror_count + mirror */ + len = 12 + mirror_len; p = xdr_reserve_space(xdr, sizeof(__be32) + len); if (!p) @@ -94,7 +99,8 @@ nfsd4_ff_encode_getdeviceinfo(struct xdr_stream *xdr, } /* len + padding for two strings */ - addr_len = 16 + da->netaddr.netid_len + da->netaddr.addr_len; + addr_len = 8 + xdr_align_size(da->netaddr.netid_len) + + xdr_align_size(da->netaddr.addr_len); ver_len = 20; len = 4 + ver_len + 4 + addr_len; diff --git a/fs/nfsd/localio.c b/fs/nfsd/localio.c index be710d809a3ba3..c3eb0557b3e1b5 100644 --- a/fs/nfsd/localio.c +++ b/fs/nfsd/localio.c @@ -97,11 +97,15 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom, } nfsd_file_get(localio); again: + rcu_read_lock(); new = unrcu_pointer(cmpxchg(pnf, NULL, RCU_INITIALIZER(localio))); if (new) { /* Some other thread installed an nfsd_file */ - if (nfsd_file_get(new) == NULL) + if (nfsd_file_get(new) == NULL) { + rcu_read_unlock(); goto again; + } + rcu_read_unlock(); /* * Drop the ref we were going to install (both file and * net) and the one we were going to return (only file). @@ -110,6 +114,8 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom, nfsd_net_put(net); nfsd_file_put(localio); localio = new; + } else { + rcu_read_unlock(); } } else nfsd_net_put(net); diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index 6fe1325815e0e9..72a5b499839d81 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -92,6 +92,7 @@ nlm_fclose(struct file *filp) } static const struct nlmsvc_binding nfsd_nlm_ops = { + .owner = THIS_MODULE, .fopen = nlm_fopen, /* open file for locking */ .fclose = nlm_fclose, /* close file */ }; @@ -100,11 +101,12 @@ void nfsd_lockd_init(void) { dprintk("nfsd: initializing lockd\n"); - nlmsvc_ops = &nfsd_nlm_ops; + rcu_assign_pointer(nlmsvc_ops, &nfsd_nlm_ops); } void nfsd_lockd_shutdown(void) { - nlmsvc_ops = NULL; + RCU_INIT_POINTER(nlmsvc_ops, NULL); + synchronize_rcu(); } diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index 27da1a3edacbf3..a7bd7b67fa4f94 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -28,6 +28,16 @@ struct cld_net; struct nfsd_net_cb; struct nfsd4_client_tracking_ops; +enum nfsd_net_flag { + NFSD_NET_GRACE_ENDED, + NFSD_NET_GRACE_END_FORCED, + NFSD_NET_IN_GRACE, + NFSD_NET_SOMEBODY_RECLAIMED, + NFSD_NET_TRACK_RECLAIM_COMPLETES, + NFSD_NET_UP, + NFSD_NET_LOCKD_UP, +}; + enum { /* cache misses due only to checksum comparison failures */ NFSD_STATS_PAYLOAD_MISSES, @@ -66,9 +76,9 @@ struct nfsd_net { struct cache_detail *nametoid_cache; struct lock_manager nfsd4_manager; - bool grace_ended; - bool grace_end_forced; + unsigned long flags; time64_t boot_time; + time64_t boot_time_bt; /* same instant in CLOCK_BOOTTIME */ struct dentry *nfsd_client_dir; @@ -84,6 +94,7 @@ struct nfsd_net { */ struct list_head *reclaim_str_hashtbl; int reclaim_str_hashtbl_size; + struct rw_semaphore reclaim_str_hashtbl_lock; struct list_head *conf_id_hashtbl; struct rb_root conf_name_tree; struct list_head *unconf_id_hashtbl; @@ -96,12 +107,16 @@ struct nfsd_net { * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time * for last close replay. * - * All of the above fields are protected by the client_mutex. + * reclaim_str_hashtbl[], reclaim_str_hashtbl_size are protected by + * reclaim_str_hashtbl_lock. + * + * All of the remaining fields are protected by the client_lock. */ struct list_head client_lru; struct list_head close_lru; - /* protects del_recall_lru and delegation hash/unhash */ + /* protects del_recall_lru and delegation hash/unhash; + * nests outside client_lock */ spinlock_t deleg_lock ____cacheline_aligned; struct list_head del_recall_lru; @@ -110,26 +125,21 @@ struct nfsd_net { struct delayed_work laundromat_work; - /* client_lock protects the client lru list and session hash table */ + /* client_lock protects the client lru list and session hash + * table; nests inside deleg_lock */ spinlock_t client_lock; /* protects blocked_locks_lru */ spinlock_t blocked_locks_lock; struct file *rec_file; - bool in_grace; const struct nfsd4_client_tracking_ops *client_tracking_ops; time64_t nfsd4_lease; time64_t nfsd4_grace; - bool somebody_reclaimed; - bool track_reclaim_completes; atomic_t nr_reclaim_complete; - bool nfsd_net_up; - bool lockd_up; - seqlock_t writeverf_lock; unsigned char writeverf[8]; @@ -209,7 +219,8 @@ struct nfsd_net { /* utsname taken from the process that starts the server */ char nfsd_name[UNX_MAXNODENAME+1]; - struct nfsd_fcache_disposal *fcache_disposal; + spinlock_t fcache_dispose_lock; + struct list_head fcache_dispose_list; siphash_key_t siphash_key; diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index 76305b86c1a95d..2998640f259ded 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c @@ -115,14 +115,19 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp) inode_lock(inode); - error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_ACCESS, - argp->acl_access); - if (error) - goto out_drop_lock; - error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_DEFAULT, - argp->acl_default); - if (error) - goto out_drop_lock; + error = 0; + if (argp->mask & NFS_ACL) { + error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, + ACL_TYPE_ACCESS, argp->acl_access); + if (error) + goto out_drop_lock; + } + if (argp->mask & NFS_DFACL) { + error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, + ACL_TYPE_DEFAULT, argp->acl_default); + if (error) + goto out_drop_lock; + } inode_unlock(inode); @@ -248,22 +253,21 @@ nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr) if (!svcxdr_encode_stat(xdr, resp->status)) return false; - - if (dentry == NULL || d_really_is_negative(dentry)) - return true; - inode = d_inode(dentry); - - if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat)) - return false; - if (xdr_stream_encode_u32(xdr, resp->mask) < 0) - return false; - - if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access, - resp->mask & NFS_ACL, 0)) - return false; - if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default, - resp->mask & NFS_DFACL, NFS_ACL_DEFAULT)) - return false; + switch (resp->status) { + case nfs_ok: + inode = d_inode(dentry); + if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat)) + return false; + if (xdr_stream_encode_u32(xdr, resp->mask) < 0) + return false; + if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access, + resp->mask & NFS_ACL, 0)) + return false; + if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default, + resp->mask & NFS_DFACL, NFS_ACL_DEFAULT)) + return false; + break; + } return true; } diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c index e87731380be85c..a87f9d7f32be26 100644 --- a/fs/nfsd/nfs3acl.c +++ b/fs/nfsd/nfs3acl.c @@ -105,12 +105,17 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp) inode_lock(inode); - error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_ACCESS, - argp->acl_access); - if (error) - goto out_drop_lock; - error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_DEFAULT, - argp->acl_default); + error = 0; + if (argp->mask & NFS_ACL) { + error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, + ACL_TYPE_ACCESS, argp->acl_access); + if (error) + goto out_drop_lock; + } + if (argp->mask & NFS_DFACL) { + error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, + ACL_TYPE_DEFAULT, argp->acl_default); + } out_drop_lock: inode_unlock(inode); diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index aeda7a802bdf7a..617a70d1329268 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -29,6 +29,25 @@ static int nfs3_ftypes[] = { S_IFIFO, /* NF3FIFO */ }; +/* + * Reject a client-supplied atime or mtime whose nanoseconds field is out + * of range. Such a value is well-formed on the wire but is not a valid + * timespec64, and storing it verbatim can corrupt on-disk timestamps. + * tv_nsec is a long, so it is cast to unsigned long (the same width) to + * catch both an over-large value and one that became negative when an + * out-of-range u32 wire nseconds was assigned to a 32-bit long. + */ +static bool nfsd3_time_in_range(const struct iattr *iap) +{ + if ((iap->ia_valid & ATTR_ATIME_SET) && + (unsigned long)iap->ia_atime.tv_nsec >= NSEC_PER_SEC) + return false; + if ((iap->ia_valid & ATTR_MTIME_SET) && + (unsigned long)iap->ia_mtime.tv_nsec >= NSEC_PER_SEC) + return false; + return true; +} + static __be32 nfsd3_map_status(__be32 status) { switch (status) { @@ -101,9 +120,14 @@ nfsd3_proc_setattr(struct svc_rqst *rqstp) SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); + if (!nfsd3_time_in_range(&argp->attrs)) { + resp->status = nfserr_inval; + goto out; + } if (argp->check_guard) guardtime = &argp->guardtime; resp->status = nfsd_setattr(rqstp, &resp->fh, &attrs, guardtime); +out: resp->status = nfsd3_map_status(resp->status); return rpc_success; } @@ -265,6 +289,8 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, trace_nfsd_vfs_create(rqstp, fhp, S_IFREG, argp->name, argp->len); + if (!nfsd3_time_in_range(iap)) + return nfserr_inval; if (isdotent(argp->name, argp->len)) return nfserr_exist; if (!(iap->ia_valid & ATTR_MODE)) @@ -400,8 +426,13 @@ nfsd3_proc_mkdir(struct svc_rqst *rqstp) argp->attrs.ia_valid &= ~ATTR_SIZE; fh_copy(&resp->dirfh, &argp->fh); fh_init(&resp->fh, NFS3_FHSIZE); + if (!nfsd3_time_in_range(&argp->attrs)) { + resp->status = nfserr_inval; + goto out; + } resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len, &attrs, S_IFDIR, 0, &resp->fh); +out: resp->status = nfsd3_map_status(resp->status); return rpc_success; } @@ -415,6 +446,10 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp) .na_iattr = &argp->attrs, }; + if (!nfsd3_time_in_range(&argp->attrs)) { + resp->status = nfserr_inval; + goto out; + } if (argp->tlen == 0) { resp->status = nfserr_inval; goto out; @@ -471,6 +506,11 @@ nfsd3_proc_mknod(struct svc_rqst *rqstp) goto out; } + if (!nfsd3_time_in_range(&argp->attrs)) { + resp->status = nfserr_inval; + goto out; + } + type = nfs3_ftypes[argp->ftype]; resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len, &attrs, type, rdev, &resp->fh); diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 50827405468d2f..7c868afc329e55 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -108,6 +108,8 @@ static int decode_cb_fattr4(struct xdr_stream *xdr, uint32_t *bitmap, if (!xdrgen_decode_fattr4_time_deleg_access(xdr, &access)) return -EIO; + if (access.nseconds >= NSEC_PER_SEC) + return -EIO; fattr->ncf_cb_atime.tv_sec = access.seconds; fattr->ncf_cb_atime.tv_nsec = access.nseconds; @@ -117,6 +119,8 @@ static int decode_cb_fattr4(struct xdr_stream *xdr, uint32_t *bitmap, if (!xdrgen_decode_fattr4_time_deleg_modify(xdr, &modify)) return -EIO; + if (modify.nseconds >= NSEC_PER_SEC) + return -EIO; fattr->ncf_cb_mtime.tv_sec = modify.seconds; fattr->ncf_cb_mtime.tv_nsec = modify.nseconds; @@ -456,13 +460,20 @@ static void encode_cb_sequence4args(struct xdr_stream *xdr, const struct nfsd4_callback *cb, struct nfs4_cb_compound_hdr *hdr) { - struct nfsd4_session *session = cb->cb_clp->cl_cb_session; + struct nfsd4_session *session; struct nfsd4_referring_call_list *rcl; __be32 *p; if (hdr->minorversion == 0) return; + rcu_read_lock(); + session = rcu_dereference(cb->cb_clp->cl_cb_session); + if (!session) { + rcu_read_unlock(); + return; + } + encode_nfs_cb_opnum4(xdr, OP_CB_SEQUENCE); encode_sessionid4(xdr, session); @@ -478,6 +489,7 @@ static void encode_cb_sequence4args(struct xdr_stream *xdr, encode_referring_call_list4(xdr, rcl); hdr->nops++; + rcu_read_unlock(); } static void update_cb_slot_table(struct nfsd4_session *ses, u32 target) @@ -529,21 +541,32 @@ static void update_cb_slot_table(struct nfsd4_session *ses, u32 target) static int decode_cb_sequence4resok(struct xdr_stream *xdr, struct nfsd4_callback *cb) { - struct nfsd4_session *session = cb->cb_clp->cl_cb_session; + struct nfsd4_session *session; int status = -ESERVERFAULT; __be32 *p; u32 seqid, slotid, target; + rcu_read_lock(); + session = rcu_dereference(cb->cb_clp->cl_cb_session); + if (!session) { + rcu_read_unlock(); + cb->cb_seq_status = -NFS4ERR_BADSESSION; + return -NFS4ERR_BADSESSION; + } + /* * If the server returns different values for sessionID, slotID or * sequence number, the server is looney tunes. */ p = xdr_inline_decode(xdr, NFS4_MAX_SESSIONID_LEN + 4 + 4 + 4 + 4); - if (unlikely(p == NULL)) + if (unlikely(p == NULL)) { + rcu_read_unlock(); goto out_overflow; + } if (memcmp(p, session->se_sessionid.data, NFS4_MAX_SESSIONID_LEN)) { dprintk("NFS: %s Invalid session id\n", __func__); + rcu_read_unlock(); goto out; } p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN); @@ -551,12 +574,14 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr, seqid = be32_to_cpup(p++); if (seqid != session->se_cb_seq_nr[cb->cb_held_slot]) { dprintk("NFS: %s Invalid sequence number\n", __func__); + rcu_read_unlock(); goto out; } slotid = be32_to_cpup(p++); if (slotid != cb->cb_held_slot) { dprintk("NFS: %s Invalid slotid\n", __func__); + rcu_read_unlock(); goto out; } @@ -564,6 +589,7 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr, target = be32_to_cpup(p++); update_cb_slot_table(session, target); + rcu_read_unlock(); status = 0; out: cb->cb_seq_status = status; @@ -1150,9 +1176,8 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c } else { if (!conn->cb_xprt || !ses) return -EINVAL; - clp->cl_cb_session = ses; args.bc_xprt = conn->cb_xprt; - args.prognumber = clp->cl_cb_session->se_cb_prog; + args.prognumber = ses->se_cb_prog; args.protocol = conn->cb_xprt->xpt_class->xcl_ident | XPRT_TRANSPORT_BC; args.authflavor = ses->se_cb_sec.flavor; @@ -1170,8 +1195,10 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c return -ENOMEM; } - if (clp->cl_minorversion != 0) + if (clp->cl_minorversion != 0) { clp->cl_cb_conn.cb_xprt = conn->cb_xprt; + rcu_assign_pointer(clp->cl_cb_session, ses); + } clp->cl_cb_client = client; clp->cl_cb_cred = cred; rcu_read_lock(); @@ -1278,18 +1305,33 @@ static int grab_slot(struct nfsd4_session *ses) static bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task) { struct nfs4_client *clp = cb->cb_clp; - struct nfsd4_session *ses = clp->cl_cb_session; + struct nfsd4_session *ses; if (cb->cb_held_slot >= 0) return true; + + rcu_read_lock(); + ses = rcu_dereference(clp->cl_cb_session); + if (!ses) { + rcu_read_unlock(); + rpc_sleep_on(&clp->cl_cb_waitq, task, NULL); + return false; + } cb->cb_held_slot = grab_slot(ses); if (cb->cb_held_slot < 0) { + rcu_read_unlock(); rpc_sleep_on(&clp->cl_cb_waitq, task, NULL); /* Race breaker */ - cb->cb_held_slot = grab_slot(ses); + rcu_read_lock(); + ses = rcu_dereference(clp->cl_cb_session); + if (ses) + cb->cb_held_slot = grab_slot(ses); + rcu_read_unlock(); if (cb->cb_held_slot < 0) return false; rpc_wake_up_queued_task(&clp->cl_cb_waitq, task); + } else { + rcu_read_unlock(); } return true; } @@ -1297,12 +1339,17 @@ static bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task) static void nfsd41_cb_release_slot(struct nfsd4_callback *cb) { struct nfs4_client *clp = cb->cb_clp; - struct nfsd4_session *ses = clp->cl_cb_session; + struct nfsd4_session *ses; if (cb->cb_held_slot >= 0) { - spin_lock(&ses->se_lock); - ses->se_cb_slot_avail |= BIT(cb->cb_held_slot); - spin_unlock(&ses->se_lock); + rcu_read_lock(); + ses = rcu_dereference(clp->cl_cb_session); + if (ses) { + spin_lock(&ses->se_lock); + ses->se_cb_slot_avail |= BIT(cb->cb_held_slot); + spin_unlock(&ses->se_lock); + } + rcu_read_unlock(); cb->cb_held_slot = -1; rpc_wake_up_next(&clp->cl_cb_waitq); } @@ -1434,22 +1481,35 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) trace_nfsd_cb_rpc_prepare(clp); cb->cb_seq_status = 1; cb->cb_status = 0; - if (minorversion && !nfsd41_cb_get_slot(cb, task)) - return; + if (minorversion) { + if (!rcu_access_pointer(clp->cl_cb_session)) { + rpc_exit(task, -EIO); + return; + } + if (!nfsd41_cb_get_slot(cb, task)) + return; + } rpc_call_start(task); } /* Returns true if CB_COMPOUND processing should continue */ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback *cb) { - struct nfsd4_session *session = cb->cb_clp->cl_cb_session; + struct nfsd4_session *session; bool ret = false; if (cb->cb_held_slot < 0) goto requeue; + rcu_read_lock(); + session = rcu_dereference(cb->cb_clp->cl_cb_session); + if (!session) { + rcu_read_unlock(); + goto requeue; + } + /* This is the operation status code for CB_SEQUENCE */ - trace_nfsd_cb_seq_status(task, cb); + trace_nfsd_cb_seq_status(task, cb, session); switch (cb->cb_seq_status) { case 0: /* @@ -1481,12 +1541,16 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback fallthrough; case -NFS4ERR_BADSESSION: nfsd4_mark_cb_fault(cb->cb_clp); + rcu_read_unlock(); goto requeue; case -NFS4ERR_DELAY: cb->cb_seq_status = 1; - if (RPC_SIGNALLED(task) || !rpc_restart_call(task)) + if (RPC_SIGNALLED(task) || !rpc_restart_call(task)) { + rcu_read_unlock(); goto requeue; + } rpc_delay(task, 2 * HZ); + rcu_read_unlock(); return false; case -NFS4ERR_SEQ_MISORDERED: case -NFS4ERR_BADSLOT: @@ -1498,11 +1562,13 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback */ nfsd4_mark_cb_fault(cb->cb_clp); cb->cb_held_slot = -1; + rcu_read_unlock(); goto retry_nowait; default: nfsd4_mark_cb_fault(cb->cb_clp); } - trace_nfsd_cb_free_slot(task, cb); + trace_nfsd_cb_free_slot(task, cb, session); + rcu_read_unlock(); nfsd41_cb_release_slot(cb); return ret; retry_nowait: @@ -1624,7 +1690,15 @@ static struct nfsd4_conn * __nfsd4_find_backchannel(struct nfs4_client *clp) * Note there isn't a lot of locking in this code; instead we depend on * the fact that it is run from clp->cl_callback_wq, which won't run two * work items at once. So, for example, clp->cl_callback_wq handles all - * access of cl_cb_client and all calls to rpc_create or rpc_shutdown_client. + * access of cl_cb_client, and all calls to rpc_create or + * rpc_shutdown_client. + * + * cl_cb_session is written only from cl_callback_wq (via + * rcu_assign_pointer) and read from rpciod under rcu_read_lock (via + * rcu_dereference) by encode_cb_sequence4args(), decode_cb_sequence4resok(), + * nfsd4_cb_sequence_done(), and the cb-slot helpers. Sessions are freed + * with kfree_rcu() so that rpciod readers in an RCU read-side critical + * section never dereference a freed session. */ static void nfsd4_process_cb_update(struct nfsd4_callback *cb) { @@ -1676,6 +1750,7 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb) nfsd4_mark_cb_down(clp); if (c) svc_xprt_put(c->cn_xprt); + rcu_assign_pointer(clp->cl_cb_session, ses); return; } } diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c index f34320e4c2f4af..279ff1e9dffbe6 100644 --- a/fs/nfsd/nfs4layouts.c +++ b/fs/nfsd/nfs4layouts.c @@ -247,13 +247,20 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate, nfsd4_init_cb(&ls->ls_recall, clp, &nfsd4_cb_layout_ops, NFSPROC4_CLNT_CB_LAYOUT); - if (parent->sc_type == SC_TYPE_DELEG) + if (parent->sc_type == SC_TYPE_DELEG) { + spin_lock(&fp->fi_lock); ls->ls_file = nfsd_file_get(fp->fi_deleg_file); - else + spin_unlock(&fp->fi_lock); + } else { ls->ls_file = find_any_file(fp); - BUG_ON(!ls->ls_file); + } + if (!ls->ls_file) { + nfs4_put_stid(stp); + return NULL; + } ls->ls_fenced = false; + ls->ls_fence_inflight = false; ls->ls_fence_delay = 0; INIT_DELAYED_WORK(&ls->ls_fence_work, nfsd4_layout_fence_worker); @@ -791,15 +798,6 @@ nfsd4_layout_fence_worker(struct work_struct *work) struct nfs4_client *clp; struct nfsd_net *nn; - /* - * The workqueue clears WORK_STRUCT_PENDING before invoking - * this callback. Re-arm immediately so that - * delayed_work_pending() returns true while the fence - * operation is in progress, preventing - * lm_breaker_timedout() from taking a duplicate reference. - */ - mod_delayed_work(system_dfl_wq, &ls->ls_fence_work, 0); - spin_lock(&ls->ls_lock); if (list_empty(&ls->ls_layouts)) { spin_unlock(&ls->ls_lock); @@ -809,6 +807,9 @@ nfsd4_layout_fence_worker(struct work_struct *work) nfsd4_close_layout(ls); ls->ls_fenced = true; + spin_lock(&ls->ls_lock); + ls->ls_fence_inflight = false; + spin_unlock(&ls->ls_lock); nfs4_put_stid(&ls->ls_stid); return; } @@ -894,18 +895,26 @@ nfsd4_layout_lm_breaker_timedout(struct file_lease *fl) if ((!nfsd4_layout_ops[ls->ls_layout_type]->fence_client) || ls->ls_fenced) return true; - if (delayed_work_pending(&ls->ls_fence_work)) - return false; /* * Make sure layout has not been returned yet before - * taking a reference count on the layout stateid. + * taking a reference count on the layout stateid. The + * ls_fence_inflight flag is set together with the sc_count + * increment under ls_lock so that a fence worker invocation + * already in progress (which has cleared WORK_STRUCT_PENDING + * but not yet reached dispose:) cannot be coalesced with a + * fresh schedule that takes an extra unmatched reference. */ spin_lock(&ls->ls_lock); + if (ls->ls_fence_inflight) { + spin_unlock(&ls->ls_lock); + return false; + } if (list_empty(&ls->ls_layouts) || !refcount_inc_not_zero(&ls->ls_stid.sc_count)) { spin_unlock(&ls->ls_lock); return true; } + ls->ls_fence_inflight = true; spin_unlock(&ls->ls_lock); mod_delayed_work(system_dfl_wq, &ls->ls_fence_work, 0); diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 8561540ab2db98..939749709e416d 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -306,10 +306,6 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out; } - status = fh_compose(resfhp, fhp->fh_export, child, fhp); - if (status != nfs_ok) - goto out; - v_mtime = 0; v_atime = 0; if (nfsd4_create_is_exclusive(open->op_createmode)) { @@ -335,6 +331,10 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, if (status != nfs_ok) goto out; + status = fh_compose(resfhp, fhp->fh_export, child, fhp); + if (status != nfs_ok) + goto out; + switch (open->op_createmode) { case NFS4_CREATE_UNCHECKED: if (!d_is_reg(child)) @@ -385,6 +385,10 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, open->op_created = true; fh_fill_post_attrs(fhp); + status = fh_compose(resfhp, fhp->fh_export, child, fhp); + if (status != nfs_ok) + goto out; + /* A newly created file already has a file size of zero. */ if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0)) iap->ia_valid &= ~ATTR_SIZE; @@ -663,7 +667,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, pr_warn("nfsd4_process_open2 failed to open newly-created file: status=%u\n", be32_to_cpu(status)); if (reclaim && !status) - nn->somebody_reclaimed = true; + set_bit(NFSD_NET_SOMEBODY_RECLAIMED, &nn->flags); out: if (open->op_filp) { fput(open->op_filp); @@ -677,8 +681,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfsd4_cleanup_open_state(cstate, open); nfsd4_bump_seqid(cstate, status); out_err: - posix_acl_release(open->op_dpacl); - posix_acl_release(open->op_pacl); return status; } @@ -700,6 +702,13 @@ static __be32 nfsd4_open_omfg(struct svc_rqst *rqstp, struct nfsd4_compound_stat return nfsd4_open(rqstp, cstate, &op->u); } +static void +nfsd4_open_release(union nfsd4_op_u *u) +{ + posix_acl_release(u->open.op_dpacl); + posix_acl_release(u->open.op_pacl); +} + /* * filehandle-manipulating ops. */ @@ -839,6 +848,20 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (status) goto out_aftermask; + /* Sanitize cr_type to avoid returning ATTRNOTSUPP. */ + switch (create->cr_type) { + case NF4LNK: + case NF4BLK: + case NF4CHR: + case NF4SOCK: + case NF4FIFO: + case NF4DIR: + break; + default: + status = nfserr_badtype; + goto out_aftermask; + } + if (create->cr_acl) { if (attrs.na_dpacl || attrs.na_pacl) { status = nfserr_inval; @@ -846,6 +869,8 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } status = nfsd4_acl_to_attr(create->cr_type, create->cr_acl, &attrs); + if (status != nfs_ok) + goto out_aftermask; } current->fs->umask = create->cr_umask; switch (create->cr_type) { @@ -1253,7 +1278,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (deleg_attrs) { status = nfserr_bad_stateid; - if (st->sc_type & SC_TYPE_DELEG) { + if (st && (st->sc_type & SC_TYPE_DELEG)) { struct nfs4_delegation *dp = delegstateid(st); /* Only for *_ATTRS_DELEG flavors */ @@ -1494,6 +1519,9 @@ static void nfs4_put_copy(struct nfsd4_copy *copy) { if (!refcount_dec_and_test(©->refcount)) return; + /* Drop the task_struct pinned in nfsd4_copy(); NULL on sync copies. */ + if (copy->copy_task) + put_task_struct(copy->copy_task); kfree(copy->cp_src); kfree(copy); } @@ -1503,20 +1531,18 @@ static void release_copy_files(struct nfsd4_copy *copy); static void nfsd4_stop_copy(struct nfsd4_copy *copy) { trace_nfsd_copy_async_cancel(copy); - if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags)) { - kthread_stop(copy->copy_task); - if (!test_bit(NFSD4_COPY_F_CB_ERROR, ©->cp_flags)) - copy->nfserr = nfs_ok; - set_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags); - } - /* - * The copy was removed from async_copies before this function - * was called, so the reaper cannot clean it up. Release files - * here regardless of who won the STOPPED race. If the thread - * set STOPPED, it has finished using the files. If STOPPED - * was set here, kthread_stop() waited for the thread to exit. + * Join the kthread before releasing its resources. The task_struct is + * pinned in nfsd4_copy(), so kthread_stop() is safe even after the + * one-shot kthread has exited. The caller already unlinked the copy, + * so this runs once per copy. */ + set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags); + kthread_stop(copy->copy_task); + if (!test_bit(NFSD4_COPY_F_CB_ERROR, ©->cp_flags)) + copy->nfserr = nfs_ok; + set_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags); + release_copy_files(copy); nfs4_put_copy(copy); } @@ -1530,7 +1556,13 @@ static struct nfsd4_copy *nfsd4_unhash_copy(struct nfs4_client *clp) copy = list_first_entry(&clp->async_copies, struct nfsd4_copy, copies); refcount_inc(©->refcount); - copy->cp_clp = NULL; + /* + * Unlinking hides the copy from the reaper, so drop its + * s2s_cp_stateids entry here while cp_clp is still valid. + */ + nfs4_free_copy_state(copy); + /* Pairs with smp_load_acquire() in nfsd4_send_cb_offload(). */ + smp_store_release(©->cp_clp, NULL); if (!list_empty(©->copies)) list_del_init(©->copies); } @@ -1542,8 +1574,11 @@ void nfsd4_shutdown_copy(struct nfs4_client *clp) { struct nfsd4_copy *copy; - while ((copy = nfsd4_unhash_copy(clp)) != NULL) + while ((copy = nfsd4_unhash_copy(clp)) != NULL) { nfsd4_stop_copy(copy); + /* Reaper can't reach the unhashed copy; drop its membership ref. */ + nfs4_put_copy(copy); + } } static bool nfsd4_copy_on_sb(const struct nfsd4_copy *copy, @@ -1562,6 +1597,10 @@ static bool nfsd4_copy_on_sb(const struct nfsd4_copy *copy, * nfsd4_cancel_copy_by_sb - cancel async copy operations on @sb * @net: net namespace containing the copy operations * @sb: targeted superblock + * + * Context: Caller must hold nfsd_mutex with NFSD_NET_UP set. Outside + * that window nn->conf_id_hashtbl is unallocated or freed, + * so the walk would dereference a NULL or dangling pointer. */ void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb) { @@ -1571,6 +1610,7 @@ void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb) unsigned int idhashval; LIST_HEAD(to_cancel); + lockdep_assert_held(&nfsd_mutex); spin_lock(&nn->client_lock); for (idhashval = 0; idhashval < CLIENT_HASH_SIZE; idhashval++) { struct list_head *head = &nn->conf_id_hashtbl[idhashval]; @@ -1606,7 +1646,11 @@ void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb) struct nfs4_client *clp = copy->cp_clp; list_del_init(©->copies); + /* Reaper can't reach it; drop the s2s entry while cp_clp is valid. */ + nfs4_free_copy_state(copy); nfsd4_stop_copy(copy); + /* Drop the membership ref the reaper would have dropped. */ + nfs4_put_copy(copy); nfsd4_put_client(clp); } } @@ -1901,6 +1945,8 @@ static void nfsd4_cb_offload_release(struct nfsd4_callback *cb) set_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags); nfsd4_put_client(cb->cb_clp); + /* Drop the copy reference taken in nfsd4_send_cb_offload(). */ + nfs4_put_copy(copy); } static int nfsd4_cb_offload_done(struct nfsd4_callback *cb, @@ -1950,6 +1996,7 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy, /* See RFC 7862 p.67: */ if (bytes_total == 0) bytes_total = ULLONG_MAX; + since = READ_ONCE(dst->f_wb_err); do { /* Only async copies can be stopped here */ if (kthread_should_stop()) @@ -1965,13 +2012,14 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy, } while (bytes_total > 0 && nfsd4_copy_is_async(copy)); /* for a non-zero asynchronous copy do a commit of data */ if (nfsd4_copy_is_async(copy) && copy->cp_res.wr_bytes_written > 0) { - since = READ_ONCE(dst->f_wb_err); end = copy->cp_dst_pos + copy->cp_res.wr_bytes_written - 1; status = vfs_fsync_range(dst, copy->cp_dst_pos, end, 0); if (!status) status = filemap_check_wb_err(dst->f_mapping, since); if (!status) set_bit(NFSD4_COPY_F_COMMITTED, ©->cp_flags); + else if (status != -EAGAIN && status != -ESTALE) + nfsd_reset_write_verifier(copy->cp_nn); } return bytes_copied; } @@ -2030,28 +2078,29 @@ static void release_copy_files(struct nfsd4_copy *copy) } } +/* + * Called from the reaper and from nfsd4_copy()'s error path; in both + * cases the copy is already unreachable from clp->async_copies. + */ static void cleanup_async_copy(struct nfsd4_copy *copy) { nfs4_free_copy_state(copy); release_copy_files(copy); - if (copy->cp_clp) { - spin_lock(©->cp_clp->async_lock); - if (!list_empty(©->copies)) - list_del_init(©->copies); - spin_unlock(©->cp_clp->async_lock); - } nfs4_put_copy(copy); } static void nfsd4_send_cb_offload(struct nfsd4_copy *copy) { struct nfsd4_cb_offload *cbo = ©->cp_cb_offload; - struct nfs4_client *clp = copy->cp_clp; + struct nfs4_client *clp; /* - * cp_clp is NULL when called via nfsd4_shutdown_copy() during - * client destruction. Skip the callback; the client is gone. + * Pairs with smp_store_release(&cp_clp) in find_async_copy() and + * nfsd4_unhash_copy(); the set_bit/clear_bit writers are unordered. + * cp_clp is NULL once the copy was canceled; skip the callback, the + * canceling path owns the notification. */ + clp = smp_load_acquire(©->cp_clp); if (!clp) { set_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags); return; @@ -2063,10 +2112,12 @@ static void nfsd4_send_cb_offload(struct nfsd4_copy *copy) cbo->co_retries = 5; /* - * Hold a reference on the client while the callback is in flight. - * Released in nfsd4_cb_offload_release(). + * Hold the client and the copy across the in-flight callback; co_cb is + * embedded in the copy, so it must outlive the callback. Both are + * dropped in nfsd4_cb_offload_release(). */ kref_get(&clp->cl_nfsdfs.cl_ref); + refcount_inc(©->refcount); nfsd4_init_cb(&cbo->co_cb, clp, &nfsd4_cb_offload_ops, NFSPROC4_CLNT_CB_OFFLOAD); @@ -2118,16 +2169,20 @@ static int nfsd4_do_async_copy(void *data) do_callback: if (!test_bit(NFSD4_COPY_F_CB_ERROR, ©->cp_flags)) copy->nfserr = nfserr; - /* The kthread exits forthwith. Ensure that a subsequent - * OFFLOAD_CANCEL won't try to kill it again. */ - set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags); - + /* + * Don't set NFSD4_COPY_F_STOPPED here: it tells a teardown caller it + * may skip kthread_stop(), which would then release nf_dst and the + * client while still in use. Only nfsd4_stop_copy() sets it, after + * joining. + */ set_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags); trace_nfsd_copy_async_done(copy); atomic_dec(©->cp_nn->pending_async_copies); if (copy->cp_res.wr_bytes_written > 0 && copy->attr_update) nfsd_update_cmtime_attr(copy->nf_dst->nf_file, 0); nfsd4_send_cb_offload(copy); + /* Drop the kthread's reference (taken in nfsd4_copy()); copy may be freed after this. */ + nfs4_put_copy(copy); return 0; } @@ -2153,21 +2208,21 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } status = nfsd4_setup_inter_ssc(rqstp, cstate, copy); if (status) { - trace_nfsd_copy_done(copy, status); - return nfserr_offload_denied; + status = nfserr_offload_denied; + goto out; } } else { trace_nfsd_copy_intra(copy); status = nfsd4_setup_intra_ssc(rqstp, cstate, copy); - if (status) { - trace_nfsd_copy_done(copy, status); - return status; - } + if (status) + goto out; } memcpy(©->fh, &cstate->current_fh.fh_handle, sizeof(struct knfsd_fh)); if (nfsd4_copy_is_async(copy)) { + struct task_struct *task; + async_copy = kzalloc_obj(struct nfsd4_copy); if (!async_copy) goto out_err; @@ -2182,11 +2237,12 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, async_copy->cp_src = kmalloc_obj(*async_copy->cp_src); if (!async_copy->cp_src) goto out_dec_async_copy_err; - if (!nfs4_init_copy_state(nn, copy)) + dup_copy_fields(copy, async_copy); + + if (!nfs4_init_copy_state(nn, async_copy)) goto out_dec_async_copy_err; - memcpy(&result->cb_stateid, ©->cp_stateid.cs_stid, + memcpy(&result->cb_stateid, &async_copy->cp_stateid.cs_stid, sizeof(result->cb_stateid)); - dup_copy_fields(copy, async_copy); if ((READ_ONCE(copy->nf_dst->nf_file->f_mode) & FMODE_NOCMTIME) != 0) async_copy->attr_update = true; @@ -2195,15 +2251,27 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, NFS4_MAX_SESSIONID_LEN); async_copy->cp_cb_offload.co_referring_slotid = cstate->slot->sl_index; async_copy->cp_cb_offload.co_referring_seqno = cstate->slot->sl_seqid; - async_copy->copy_task = kthread_create(nfsd4_do_async_copy, - async_copy, "%s", "copy thread"); - if (IS_ERR(async_copy->copy_task)) + task = kthread_create(nfsd4_do_async_copy, async_copy, + "%s", "copy thread"); + if (IS_ERR(task)) goto out_dec_async_copy_err; + /* + * Pin the task_struct so kthread_stop() is safe after this + * one-shot kthread exits. Released by nfs4_put_copy(). + */ + get_task_struct(task); + async_copy->copy_task = task; + /* + * Take the kthread's ref and wake it before publishing, so the + * publisher touches async_copy no further and teardown can + * drain it. + */ + refcount_inc(&async_copy->refcount); + wake_up_process(async_copy->copy_task); spin_lock(&async_copy->cp_clp->async_lock); list_add(&async_copy->copies, &async_copy->cp_clp->async_copies); spin_unlock(&async_copy->cp_clp->async_lock); - wake_up_process(async_copy->copy_task); status = nfs_ok; } else { status = nfsd4_do_copy(copy, copy->nf_src->nf_file, @@ -2257,8 +2325,18 @@ find_async_copy(struct nfs4_client *clp, stateid_t *stateid) spin_lock(&clp->async_lock); copy = find_async_copy_locked(clp, stateid); - if (copy) + if (copy) { refcount_inc(©->refcount); + nfs4_free_copy_state(copy); + /* + * Mirror nfsd4_unhash_copy(): unlink and clear cp_clp under + * async_lock so the reaper can't reach it. Caller drops the + * membership ref after nfsd4_stop_copy(). + */ + smp_store_release(©->cp_clp, NULL); + if (!list_empty(©->copies)) + list_del_init(©->copies); + } spin_unlock(&clp->async_lock); return copy; } @@ -2277,8 +2355,11 @@ nfsd4_offload_cancel(struct svc_rqst *rqstp, struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); return manage_cpntf_state(nn, &os->stateid, clp, NULL); - } else + } else { nfsd4_stop_copy(copy); + /* find_async_copy() unlinked it from the reaper; drop the membership ref. */ + nfs4_put_copy(copy); + } return nfs_ok; } @@ -2292,7 +2373,6 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); struct nfs4_stid *stid = NULL; struct nfs4_cpntf_state *cps; - struct nfs4_client *clp = cstate->clp; status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, &cn->cpn_src_stateid, RD_STATE, NULL, @@ -2306,12 +2386,14 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, cn->cpn_lease_time.tv_nsec = 0; status = nfserrno(-ENOMEM); + /* + * The returned cps is published and fully initialized, and carries an + * extra reference for us; drop it once we are done with it. + */ cps = nfs4_alloc_init_cpntf_state(nn, stid); if (!cps) goto out; memcpy(&cn->cpn_cnr_stateid, &cps->cp_stateid.cs_stid, sizeof(stateid_t)); - memcpy(&cps->cp_p_stateid, &stid->sc_stateid, sizeof(stateid_t)); - memcpy(&cps->cp_p_clid, &clp->cl_clientid, sizeof(clientid_t)); /* For now, only return one server address in cpn_src, the * address used by the client to connect to this server. @@ -2320,10 +2402,11 @@ nfsd4_copy_notify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfsd4_set_netaddr((struct sockaddr *)&rqstp->rq_daddr, &cn->cpn_src->u.nl4_addr); WARN_ON_ONCE(status); - if (status) { - nfs4_put_cpntf_state(nn, cps); - goto out; - } + /* + * Drop our extra reference. The membership reference keeps the entry + * alive for a later inter-server READ, or until the laundromat reaps it. + */ + nfs4_put_cpntf_state(nn, cps); out: nfs4_put_stid(stid); return status; @@ -3119,9 +3202,22 @@ nfsd4_proc_compound(struct svc_rqst *rqstp) op->status = nfsd4_open_omfg(rqstp, cstate, op); goto encode_op; } - if (!current_fh->fh_dentry && - !HAS_FH_FLAG(current_fh, NFSD4_FH_FOREIGN)) { - if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) { + if (!current_fh->fh_dentry) { + if (HAS_FH_FLAG(current_fh, NFSD4_FH_FOREIGN)) { + /* + * FOREIGN fh from inter-SSC PUTFH: only + * SAVEFH may proceed with a NULL fh_dentry. + * Per RFC 7862 S15.2.3, validation of a + * foreign fh is deferred to the operation + * that consumes it, and NFS4ERR_STALE is + * returned at that point. + */ + if (op->opnum != OP_SAVEFH && + !(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) { + op->status = nfserr_stale; + goto encode_op; + } + } else if (!(op->opdesc->op_flags & ALLOWED_WITHOUT_FH)) { op->status = nfserr_nofilehandle; goto encode_op; } @@ -3185,6 +3281,9 @@ nfsd4_proc_compound(struct svc_rqst *rqstp) status = op->status; } + if (op->opdesc && op->opdesc->op_release) + op->opdesc->op_release(&op->u); + trace_nfsd_compound_status(args->client_opcnt, resp->opcnt, status, nfsd4_op_name(op->opnum)); @@ -3507,7 +3606,7 @@ static u32 nfsd4_get_dir_delegation_rsize(const struct svc_rqst *rqstp, op_encode_stateid_maxsz + 2 /* gddr_notification */ + 2 /* gddr_child_attributes */ + - 2 /* gddr_dir_attributes */); + 2 /* gddr_dir_attributes */) * sizeof(__be32); } #ifdef CONFIG_NFSD_PNFS @@ -3684,6 +3783,7 @@ static const struct nfsd4_operation nfsd4_ops[] = { }, [OP_OPEN] = { .op_func = nfsd4_open, + .op_release = nfsd4_open_release, .op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING, .op_name = "OP_OPEN", .op_rsize_bop = nfsd4_open_rsize, diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 6ea25a52d2f4e4..d513971fb119d5 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -167,7 +167,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) end_creating(dentry); out: if (status == 0) { - if (nn->in_grace) + if (test_bit(NFSD_NET_IN_GRACE, &nn->flags)) __nfsd4_create_reclaim_record_grace(clp, dname, nn); vfs_fsync(nn->rec_file, 0); } else { @@ -285,10 +285,12 @@ __nfsd4_remove_reclaim_record_grace(const char *dname, int len, return; } name.len = len; + down_write(&nn->reclaim_str_hashtbl_lock); crp = nfsd4_find_reclaim_client(name, nn); - kfree(name.data); if (crp) nfs4_remove_reclaim_record(crp, nn); + up_write(&nn->reclaim_str_hashtbl_lock); + kfree(name.data); } static void @@ -317,7 +319,7 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp) nfs4_reset_creds(original_cred); if (status == 0) { vfs_fsync(nn->rec_file, 0); - if (nn->in_grace) + if (test_bit(NFSD_NET_IN_GRACE, &nn->flags)) __nfsd4_remove_reclaim_record_grace(dname, HEXDIR_LEN, nn); } @@ -373,7 +375,7 @@ nfsd4_recdir_purge_old(struct nfsd_net *nn) { int status; - nn->in_grace = false; + clear_bit(NFSD_NET_IN_GRACE, &nn->flags); if (!nn->rec_file) return; status = mnt_want_write_file(nn->rec_file); @@ -455,7 +457,7 @@ nfsd4_init_recdir(struct net *net) nfs4_reset_creds(original_cred); if (!status) - nn->in_grace = true; + set_bit(NFSD_NET_IN_GRACE, &nn->flags); return status; } @@ -484,6 +486,7 @@ nfs4_legacy_state_init(struct net *net) for (i = 0; i < CLIENT_HASH_SIZE; i++) INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]); nn->reclaim_str_hashtbl_size = 0; + init_rwsem(&nn->reclaim_str_hashtbl_lock); return 0; } @@ -598,13 +601,16 @@ nfsd4_check_legacy_client(struct nfs4_client *clp) goto out_enoent; } name.len = HEXDIR_LEN; + down_read(&nn->reclaim_str_hashtbl_lock); crp = nfsd4_find_reclaim_client(name, nn); - kfree(name.data); if (crp) { set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); crp->cr_clp = clp; - return 0; } + up_read(&nn->reclaim_str_hashtbl_lock); + kfree(name.data); + if (crp) + return 0; out_enoent: return -ENOENT; @@ -1176,6 +1182,7 @@ nfsd4_cld_check(struct nfs4_client *clp) return 0; /* look for it in the reclaim hashtable otherwise */ + down_read(&nn->reclaim_str_hashtbl_lock); crp = nfsd4_find_reclaim_client(clp->cl_name, nn); if (crp) goto found; @@ -1191,6 +1198,7 @@ nfsd4_cld_check(struct nfs4_client *clp) if (!name.data) { dprintk("%s: failed to allocate memory for name.data!\n", __func__); + up_read(&nn->reclaim_str_hashtbl_lock); return -ENOENT; } name.len = HEXDIR_LEN; @@ -1201,9 +1209,11 @@ nfsd4_cld_check(struct nfs4_client *clp) } #endif + up_read(&nn->reclaim_str_hashtbl_lock); return -ENOENT; found: crp->cr_clp = clp; + up_read(&nn->reclaim_str_hashtbl_lock); return 0; } @@ -1215,6 +1225,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp) struct cld_net *cn = nn->cld_net; #endif struct nfs4_client_reclaim *crp; + unsigned int princhashlen; char *principal = NULL; /* did we already find that this client is stable? */ @@ -1222,6 +1233,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp) return 0; /* look for it in the reclaim hashtable otherwise */ + down_read(&nn->reclaim_str_hashtbl_lock); crp = nfsd4_find_reclaim_client(clp->cl_name, nn); if (crp) goto found; @@ -1237,6 +1249,7 @@ nfsd4_cld_check_v2(struct nfs4_client *clp) if (!name.data) { dprintk("%s: failed to allocate memory for name.data\n", __func__); + up_read(&nn->reclaim_str_hashtbl_lock); return -ENOENT; } name.len = HEXDIR_LEN; @@ -1247,23 +1260,31 @@ nfsd4_cld_check_v2(struct nfs4_client *clp) } #endif + up_read(&nn->reclaim_str_hashtbl_lock); return -ENOENT; found: - if (crp->cr_princhash.len) { + princhashlen = crp->cr_princhash.len; + if (princhashlen) { u8 digest[SHA256_DIGEST_SIZE]; + u8 *pdata; if (clp->cl_cred.cr_raw_principal) principal = clp->cl_cred.cr_raw_principal; else if (clp->cl_cred.cr_principal) principal = clp->cl_cred.cr_principal; - if (principal == NULL) + if (principal == NULL) { + up_read(&nn->reclaim_str_hashtbl_lock); return -ENOENT; + } sha256(principal, strlen(principal), digest); - if (memcmp(crp->cr_princhash.data, digest, - crp->cr_princhash.len)) + pdata = crp->cr_princhash.data; + if (memcmp(pdata, digest, princhashlen)) { + up_read(&nn->reclaim_str_hashtbl_lock); return -ENOENT; + } } crp->cr_clp = clp; + up_read(&nn->reclaim_str_hashtbl_lock); return 0; } @@ -1362,7 +1383,8 @@ nfs4_cld_state_init(struct net *net) for (i = 0; i < CLIENT_HASH_SIZE; i++) INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]); nn->reclaim_str_hashtbl_size = 0; - nn->track_reclaim_completes = true; + init_rwsem(&nn->reclaim_str_hashtbl_lock); + set_bit(NFSD_NET_TRACK_RECLAIM_COMPLETES, &nn->flags); atomic_set(&nn->nr_reclaim_complete, 0); return 0; @@ -1373,7 +1395,7 @@ nfs4_cld_state_shutdown(struct net *net) { struct nfsd_net *nn = net_generic(net, nfsd_net_id); - nn->track_reclaim_completes = false; + clear_bit(NFSD_NET_TRACK_RECLAIM_COMPLETES, &nn->flags); kfree(nn->reclaim_str_hashtbl); } diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index a42f34842d7764..6180f6dc8995b4 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -202,18 +202,28 @@ renew_client_locked(struct nfs4_client *clp) clp->cl_state = NFSD4_ACTIVE; } +/* + * Finish a cl_rpc_users unpin with the client_lock held. A + * revocation walk clears @renew so the client whose state it is + * revoking is not revived; every other caller renews the lease of + * a still-active client. + */ +static void __put_client_locked(struct nfs4_client *clp, bool renew) +{ + if (is_client_expired(clp)) + wake_up_all(&expiry_wq); + else if (renew) + renew_client_locked(clp); +} + static void put_client_renew_locked(struct nfs4_client *clp) { struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); lockdep_assert_held(&nn->client_lock); - if (!atomic_dec_and_test(&clp->cl_rpc_users)) - return; - if (!is_client_expired(clp)) - renew_client_locked(clp); - else - wake_up_all(&expiry_wq); + if (atomic_dec_and_test(&clp->cl_rpc_users)) + __put_client_locked(clp, true); } static void put_client_renew(struct nfs4_client *clp) @@ -222,10 +232,27 @@ static void put_client_renew(struct nfs4_client *clp) if (!atomic_dec_and_lock(&clp->cl_rpc_users, &nn->client_lock)) return; - if (!is_client_expired(clp)) - renew_client_locked(clp); - else - wake_up_all(&expiry_wq); + __put_client_locked(clp, true); + spin_unlock(&nn->client_lock); +} + +static void put_client_no_renew_locked(struct nfs4_client *clp) +{ + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); + + lockdep_assert_held(&nn->client_lock); + + if (atomic_dec_and_test(&clp->cl_rpc_users)) + __put_client_locked(clp, false); +} + +static void put_client_no_renew(struct nfs4_client *clp) +{ + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); + + if (!atomic_dec_and_lock(&clp->cl_rpc_users, &nn->client_lock)) + return; + __put_client_locked(clp, false); spin_unlock(&nn->client_lock); } @@ -326,6 +353,16 @@ free_blocked_lock(struct nfsd4_blocked_lock *nbl) kref_put(&nbl->nbl_kref, free_nbl); } +/* A blocked lock's flc_owner is its nfs4_lockowner. */ +static struct nfs4_client * +nbl_client(struct nfsd4_blocked_lock *nbl) +{ + struct nfs4_lockowner *lo; + + lo = (struct nfs4_lockowner *)nbl->nbl_lock.c.flc_owner; + return lo->lo_owner.so_client; +} + static void remove_blocked_locks(struct nfs4_lockowner *lo) { @@ -939,7 +976,7 @@ struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *sla * Create a unique stateid_t to represent each COPY. */ static int nfs4_init_cp_state(struct nfsd_net *nn, copy_stateid_t *stid, - unsigned char cs_type) + unsigned char cs_type, struct nfs4_stid *p_stid) { int new_id; @@ -949,19 +986,34 @@ static int nfs4_init_cp_state(struct nfsd_net *nn, copy_stateid_t *stid, idr_preload(GFP_KERNEL); spin_lock(&nn->s2s_cp_lock); new_id = idr_alloc_cyclic(&nn->s2s_cp_stateids, stid, 0, 0, GFP_NOWAIT); - stid->cs_stid.si_opaque.so_id = new_id; - stid->cs_stid.si_generation = 1; + if (new_id >= 0) { + stid->cs_stid.si_opaque.so_id = new_id; + stid->cs_stid.si_generation = 1; + /* + * Set cs_type and link onto sc_cp_list under the same lock + * that installed the IDR entry, so a concurrent + * manage_cpntf_state() sees either no entry or a fully + * linked cp_list. + */ + stid->cs_type = cs_type; + if (p_stid) { + struct nfs4_cpntf_state *cps = + container_of(stid, struct nfs4_cpntf_state, + cp_stateid); + + list_add(&cps->cp_list, &p_stid->sc_cp_list); + } + } spin_unlock(&nn->s2s_cp_lock); idr_preload_end(); if (new_id < 0) return 0; - stid->cs_type = cs_type; return 1; } int nfs4_init_copy_state(struct nfsd_net *nn, struct nfsd4_copy *copy) { - return nfs4_init_cp_state(nn, ©->cp_stateid, NFS4_COPY_STID); + return nfs4_init_cp_state(nn, ©->cp_stateid, NFS4_COPY_STID, NULL); } struct nfs4_cpntf_state *nfs4_alloc_init_cpntf_state(struct nfsd_net *nn, @@ -972,13 +1024,21 @@ struct nfs4_cpntf_state *nfs4_alloc_init_cpntf_state(struct nfsd_net *nn, cps = kzalloc_obj(struct nfs4_cpntf_state); if (!cps) return NULL; + /* So a stale list_del_init() before linking is a no-op. */ + INIT_LIST_HEAD(&cps->cp_list); cps->cpntf_time = ktime_get_boottime_seconds(); - refcount_set(&cps->cp_stateid.cs_count, 1); - if (!nfs4_init_cp_state(nn, &cps->cp_stateid, NFS4_COPYNOTIFY_STID)) + /* + * Fully initialize the entry before nfs4_init_cp_state() publishes it, + * since a concurrent OFFLOAD_CANCEL could then free it. Take an extra + * reference for the caller (dropped with nfs4_put_cpntf_state()). + */ + memcpy(&cps->cp_p_stateid, &p_stid->sc_stateid, sizeof(stateid_t)); + memcpy(&cps->cp_p_clid, &p_stid->sc_client->cl_clientid, + sizeof(clientid_t)); + refcount_set(&cps->cp_stateid.cs_count, 2); + if (!nfs4_init_cp_state(nn, &cps->cp_stateid, NFS4_COPYNOTIFY_STID, + p_stid)) goto out_free; - spin_lock(&nn->s2s_cp_lock); - list_add(&cps->cp_list, &p_stid->sc_cp_list); - spin_unlock(&nn->s2s_cp_lock); return cps; out_free: kfree(cps); @@ -998,18 +1058,66 @@ void nfs4_free_copy_state(struct nfsd4_copy *copy) spin_unlock(&nn->s2s_cp_lock); } +/* + * Drop the parent's reference on an already-unlinked cpntf entry. If a + * concurrent holder still owns a reference, its nfs4_put_cpntf_state() does + * the final free. + * + * nn->s2s_cp_lock must be held. + */ +static void put_cpntf_state_unlinked_locked(struct nfs4_cpntf_state *cps) +{ + WARN_ON_ONCE(cps->cp_stateid.cs_type != NFS4_COPYNOTIFY_STID); + WARN_ON_ONCE(!list_empty(&cps->cp_list)); + + if (refcount_dec_and_test(&cps->cp_stateid.cs_count)) + kfree(cps); +} + +/* + * Unhash from the IDR and sc_cp_list. Gated on list_empty() to avoid + * evicting a recycled so_id. + */ +static void nfsd4_unhash_cpntf_state(struct nfsd_net *nn, struct nfs4_cpntf_state *cps) +{ + lockdep_assert_held(&nn->s2s_cp_lock); + + if (!list_empty(&cps->cp_list)) { + list_del_init(&cps->cp_list); + idr_remove(&nn->s2s_cp_stateids, cps->cp_stateid.cs_stid.si_opaque.so_id); + } +} + +/* + * Revoke a copy-notify stateid: unlink it from the IDR and sc_cp_list first + * so no new finder can discover it, then drop the membership reference. Every + * revoke path (cancel, laundromat, drain) must use this rather than + * _free_cpntf_state_locked(), which unlinks only at refcount zero and so could + * let a second revoke free the entry under a concurrent reader. + * + * nn->s2s_cp_lock must be held. + */ +static void revoke_cpntf_state_locked(struct nfsd_net *nn, + struct nfs4_cpntf_state *cps) +{ + nfsd4_unhash_cpntf_state(nn, cps); + put_cpntf_state_unlinked_locked(cps); +} + static void nfs4_free_cpntf_statelist(struct net *net, struct nfs4_stid *stid) { - struct nfs4_cpntf_state *cps; + struct nfs4_cpntf_state *cps, *tmp; struct nfsd_net *nn; nn = net_generic(net, nfsd_net_id); spin_lock(&nn->s2s_cp_lock); - while (!list_empty(&stid->sc_cp_list)) { - cps = list_first_entry(&stid->sc_cp_list, - struct nfs4_cpntf_state, cp_list); - _free_cpntf_state_locked(nn, cps); - } + /* + * Revoke unlinks each entry before dropping the parent's reference, so + * the drain terminates in one pass per entry regardless of cs_count; a + * concurrent holder does the final kfree via nfs4_put_cpntf_state(). + */ + list_for_each_entry_safe(cps, tmp, &stid->sc_cp_list, cp_list) + revoke_cpntf_state_locked(nn, cps); spin_unlock(&nn->s2s_cp_lock); } @@ -1873,14 +1981,20 @@ static void revoke_one_stid(struct nfsd_net *nn, struct nfs4_client *clp, * being released. Thus nfsd will no longer prevent the filesystem from being * unmounted. * - * The clients which own the states will subsequently being notified that the + * The clients which own the states will subsequently be notified that the * states have been "admin-revoked". + * + * Context: Caller must hold nfsd_mutex with NFSD_NET_UP set. Outside + * that window nn->conf_id_hashtbl is unallocated or freed, + * so the walk would dereference a NULL or dangling pointer. */ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) { unsigned int idhashval; unsigned int sc_types; + lockdep_assert_held(&nfsd_mutex); + sc_types = SC_TYPE_OPEN | SC_TYPE_LOCK | SC_TYPE_DELEG | SC_TYPE_LAYOUT; spin_lock(&nn->client_lock); @@ -1889,9 +2003,19 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) struct nfs4_client *clp; retry: list_for_each_entry(clp, head, cl_idhash) { - struct nfs4_stid *stid = find_one_sb_stid(clp, sb, - sc_types); + struct nfs4_stid *stid; + + /* + * force_expire_client() ignores cl_rpc_users once + * its wait_event() has passed, so pinning cannot + * keep an already-expiring client alive; the + * expiry path revokes its states instead. + */ + if (is_client_expired(clp)) + continue; + stid = find_one_sb_stid(clp, sb, sc_types); if (stid) { + atomic_inc(&clp->cl_rpc_users); spin_unlock(&nn->client_lock); revoke_one_stid(nn, clp, stid); nfs4_put_stid(stid); @@ -1904,6 +2028,7 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) */ nn->nfs40_last_revoke = ktime_get_boottime_seconds(); + put_client_no_renew_locked(clp); goto retry; } } @@ -1946,12 +2071,18 @@ static struct nfs4_stid *find_one_export_stid(struct nfs4_client *clp, * * Userspace (exportfs -u) sends this after removing the last client * for a path, enabling the underlying filesystem to be unmounted. + * + * Context: Caller must hold nfsd_mutex with NFSD_NET_UP set. Outside + * that window nn->conf_id_hashtbl is unallocated or freed, + * so the walk would dereference a NULL or dangling pointer. */ void nfsd4_revoke_export_states(struct nfsd_net *nn, const struct path *path) { unsigned int idhashval; unsigned int sc_types; + lockdep_assert_held(&nfsd_mutex); + sc_types = SC_TYPE_OPEN | SC_TYPE_LOCK | SC_TYPE_DELEG | SC_TYPE_LAYOUT; spin_lock(&nn->client_lock); @@ -1960,10 +2091,14 @@ void nfsd4_revoke_export_states(struct nfsd_net *nn, const struct path *path) struct nfs4_client *clp; retry: list_for_each_entry(clp, head, cl_idhash) { - struct nfs4_stid *stid = find_one_export_stid( - clp, path, - sc_types); + struct nfs4_stid *stid; + + /* Skip or pin clp as in nfsd4_revoke_states(). */ + if (is_client_expired(clp)) + continue; + stid = find_one_export_stid(clp, path, sc_types); if (stid) { + atomic_inc(&clp->cl_rpc_users); spin_unlock(&nn->client_lock); revoke_one_stid(nn, clp, stid); nfs4_put_stid(stid); @@ -1971,6 +2106,7 @@ void nfsd4_revoke_export_states(struct nfsd_net *nn, const struct path *path) if (clp->cl_minorversion == 0) nn->nfs40_last_revoke = ktime_get_boottime_seconds(); + put_client_no_renew_locked(clp); goto retry; } } @@ -2295,7 +2431,7 @@ static void __free_session(struct nfsd4_session *ses) { free_session_slots(ses, 0); xa_destroy(&ses->se_slots); - kfree(ses); + kfree_rcu(ses, rcu_head); } static void free_session(struct nfsd4_session *ses) @@ -2641,14 +2777,24 @@ __destroy_client(struct nfs4_client *clp) release_openowner(oo); } for (i = 0; i < OWNER_HASH_SIZE; i++) { - struct nfs4_stateowner *so, *tmp; + struct nfs4_stateowner *so; - list_for_each_entry_safe(so, tmp, &clp->cl_ownerstr_hashtbl[i], - so_strhash) { + spin_lock(&clp->cl_lock); + while (!list_empty(&clp->cl_ownerstr_hashtbl[i])) { + so = list_first_entry(&clp->cl_ownerstr_hashtbl[i], + struct nfs4_stateowner, so_strhash); /* Should be no openowners at this point */ WARN_ON_ONCE(so->so_is_open_owner); + nfs4_get_stateowner(so); + unhash_lockowner_locked(lockowner(so)); + spin_unlock(&clp->cl_lock); + remove_blocked_locks(lockowner(so)); + nfs4_put_stateowner(so); + + spin_lock(&clp->cl_lock); } + spin_unlock(&clp->cl_lock); } nfsd4_return_all_client_layouts(clp); nfsd4_shutdown_copy(clp); @@ -2675,16 +2821,23 @@ static void inc_reclaim_complete(struct nfs4_client *clp) { struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - if (!nn->track_reclaim_completes) + if (!test_bit(NFSD_NET_TRACK_RECLAIM_COMPLETES, &nn->flags)) return; - if (!nfsd4_find_reclaim_client(clp->cl_name, nn)) + + down_read(&nn->reclaim_str_hashtbl_lock); + if (!nfsd4_find_reclaim_client(clp->cl_name, nn)) { + up_read(&nn->reclaim_str_hashtbl_lock); return; + } if (atomic_inc_return(&nn->nr_reclaim_complete) == nn->reclaim_str_hashtbl_size) { + up_read(&nn->reclaim_str_hashtbl_lock); printk(KERN_INFO "NFSD: all clients done reclaiming, ending NFSv4 grace period (net %x)\n", clp->net->ns.inum); nfsd4_end_grace(nn); + return; } + up_read(&nn->reclaim_str_hashtbl_lock); } static void expire_client(struct nfs4_client *clp) @@ -3259,6 +3412,11 @@ static void force_expire_client(struct nfs4_client *clp) trace_nfsd_clid_admin_expired(&clp->cl_clientid); + /* + * cl_time is cleared under client_lock before the wait so a + * revocation walk pinning cl_rpc_users under it either skips + * this client or is seen by this wait_event(). + */ spin_lock(&nn->client_lock); clp->cl_time = 0; spin_unlock(&nn->client_lock); @@ -3414,7 +3572,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, clp->cl_time = ktime_get_boottime_seconds(); copy_verf(clp, verf); memcpy(&clp->cl_addr, sa, sizeof(struct sockaddr_storage)); - clp->cl_cb_session = NULL; + RCU_INIT_POINTER(clp->cl_cb_session, NULL); clp->net = net; clp->cl_nfsd_dentry = nfsd_client_mkdir( nn, &clp->cl_nfsdfs, @@ -4496,6 +4654,19 @@ static void nfsd4_construct_sequence_response(struct nfsd4_session *session, seq->status_flags |= SEQ4_STATUS_ADMIN_STATE_REVOKED; } +static bool nfsd4_slots_inuse(struct nfsd4_session *ses, int from) +{ + int i; + + for (i = from; i < ses->se_fchannel.maxreqs; i++) { + struct nfsd4_slot *slot = xa_load(&ses->se_slots, i); + + if (slot->sl_flags & NFSD4_SLOT_INUSE) + return true; + } + return false; +} + __be32 nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, union nfsd4_op_u *u) @@ -4575,7 +4746,9 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (session->se_target_maxslots < session->se_fchannel.maxreqs && slot->sl_generation == session->se_slot_gen && - seq->maxslots <= session->se_target_maxslots) + seq->maxslots <= session->se_target_maxslots && + seq->slotid < session->se_target_maxslots && + !nfsd4_slots_inuse(session, session->se_target_maxslots)) /* Client acknowledged our reduce maxreqs */ free_session_slots(session, session->se_target_maxslots); @@ -5018,8 +5191,6 @@ nfsd4_init_leases_net(struct nfsd_net *nn) nn->nfsd4_lease = 90; /* default lease time */ nn->nfsd4_grace = 90; - nn->somebody_reclaimed = false; - nn->track_reclaim_completes = false; nn->clverifier_counter = get_random_u32(); nn->clientid_base = get_random_u32(); nn->clientid_counter = nn->clientid_base + 1; @@ -5172,6 +5343,7 @@ static void nfsd4_drop_revoked_stid(struct nfs4_stid *s) case SC_TYPE_DELEG: dp = delegstateid(s); list_del_init(&dp->dl_recall_lru); + s->sc_status |= SC_STATUS_FREED; spin_unlock(&cl->cl_lock); nfs4_put_stid(s); break; @@ -5615,8 +5787,10 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp) refcount_inc(&dp->dl_stid.sc_count); queued = nfsd4_run_cb(&dp->dl_recall); WARN_ON_ONCE(!queued); - if (!queued) + if (!queued) { refcount_dec(&dp->dl_stid.sc_count); + clear_bit(NFSD4_CALLBACK_RUNNING, &dp->dl_recall.cb_flags); + } } /* Called from break_lease() with flc_lock held. */ @@ -6728,12 +6902,21 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, static void nfsd4_end_grace(struct nfsd_net *nn) { - /* do nothing if grace period already ended */ - if (nn->grace_ended) + /* + * nfsd4_end_grace() can be entered concurrently from the + * laundromat workqueue and from an nfsd compound thread + * handling RECLAIM_COMPLETE. Without serialization, both + * callers can observe NFSD_NET_GRACE_ENDED clear and proceed + * into nfsd4_record_grace_done(). For tracking ops whose + * grace_done drains reclaim_str_hashtbl, that results in + * list corruption and a double free of every + * nfs4_client_reclaim entry. Use an atomic test-and-set so + * exactly one caller proceeds. + */ + if (test_and_set_bit(NFSD_NET_GRACE_ENDED, &nn->flags)) return; trace_nfsd_grace_complete(nn); - nn->grace_ended = true; /* * If the server goes down again right now, an NFSv4 * client will still be allowed to reclaim after it comes back up, @@ -6774,10 +6957,10 @@ bool nfsd4_force_end_grace(struct nfsd_net *nn) { if (!nn->client_tracking_ops) return false; - if (READ_ONCE(nn->grace_ended)) + if (test_bit(NFSD_NET_GRACE_ENDED, &nn->flags)) return false; /* laundromat_work must be initialised now, though it might be disabled */ - WRITE_ONCE(nn->grace_end_forced, true); + set_bit(NFSD_NET_GRACE_END_FORCED, &nn->flags); /* mod_delayed_work() doesn't queue work after * nfs4_state_shutdown_net() has called disable_delayed_work_sync() */ @@ -6791,18 +6974,22 @@ bool nfsd4_force_end_grace(struct nfsd_net *nn) */ static bool clients_still_reclaiming(struct nfsd_net *nn) { - time64_t double_grace_period_end = nn->boot_time + + time64_t double_grace_period_end = nn->boot_time_bt + 2 * nn->nfsd4_lease; - if (READ_ONCE(nn->grace_end_forced)) - return false; - if (nn->track_reclaim_completes && - atomic_read(&nn->nr_reclaim_complete) == - nn->reclaim_str_hashtbl_size) + if (test_bit(NFSD_NET_GRACE_END_FORCED, &nn->flags)) return false; - if (!nn->somebody_reclaimed) + if (test_bit(NFSD_NET_TRACK_RECLAIM_COMPLETES, &nn->flags)) { + int size; + + down_read(&nn->reclaim_str_hashtbl_lock); + size = nn->reclaim_str_hashtbl_size; + up_read(&nn->reclaim_str_hashtbl_lock); + if (atomic_read(&nn->nr_reclaim_complete) == size) + return false; + } + if (!test_and_clear_bit(NFSD_NET_SOMEBODY_RECLAIMED, &nn->flags)) return false; - nn->somebody_reclaimed = false; /* * If we've given them *two* lease times to reclaim, and they're * still not done, give up: @@ -6859,30 +7046,36 @@ static void nfsd4_ssc_shutdown_umount(struct nfsd_net *nn) static void nfsd4_ssc_expire_umount(struct nfsd_net *nn) { bool do_wakeup = false; - struct nfsd4_ssc_umount_item *ni = NULL; - struct nfsd4_ssc_umount_item *tmp; + struct nfsd4_ssc_umount_item *ni; +restart: spin_lock(&nn->nfsd_ssc_lock); - list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) { - if (time_after(jiffies, ni->nsui_expire)) { - if (refcount_read(&ni->nsui_refcnt) > 1) - continue; + list_for_each_entry(ni, &nn->nfsd_ssc_mount_list, nsui_list) { + if (!time_after(jiffies, ni->nsui_expire)) + break; + if (refcount_read(&ni->nsui_refcnt) > 1) + continue; - /* mark being unmount */ - ni->nsui_busy = true; - spin_unlock(&nn->nfsd_ssc_lock); - mntput(ni->nsui_vfsmount); - spin_lock(&nn->nfsd_ssc_lock); + /* Prevent concurrent setup during unmount */ + ni->nsui_busy = true; + spin_unlock(&nn->nfsd_ssc_lock); + mntput(ni->nsui_vfsmount); + spin_lock(&nn->nfsd_ssc_lock); - /* waiters need to start from begin of list */ - list_del(&ni->nsui_list); - kfree(ni); + /* Force concurrent scanners to restart */ + list_del(&ni->nsui_list); + kfree(ni); - /* wakeup ssc_connect waiters */ - do_wakeup = true; - continue; - } - break; + /* wakeup ssc_connect waiters */ + do_wakeup = true; + /* + * Concurrent nfsd4_ssc_cancel_dul() can free any item + * on the list under nfsd_ssc_lock while mntput() runs + * above. Restart from the head; the list is short and + * the expire worker is periodic, so this is cheap. + */ + spin_unlock(&nn->nfsd_ssc_lock); + goto restart; } if (do_wakeup) wake_up_all(&nn->nfsd_ssc_waitq); @@ -7032,16 +7225,20 @@ static void nfs40_clean_admin_revoked(struct nfsd_net *nn, if (atomic_read(&clp->cl_admin_revoked) == 0) continue; + if (is_client_expired(clp)) + continue; spin_lock(&clp->cl_lock); idr_for_each_entry_ul(&clp->cl_stateids, stid, tmp, id) if (stid->sc_status & SC_STATUS_ADMIN_REVOKED) { refcount_inc(&stid->sc_count); + atomic_inc(&clp->cl_rpc_users); spin_unlock(&nn->client_lock); /* this function drops ->cl_lock */ nfsd4_drop_revoked_stid(stid); nfs4_put_stid(stid); spin_lock(&nn->client_lock); + put_client_no_renew_locked(clp); goto retry; } spin_unlock(&clp->cl_lock); @@ -7062,6 +7259,7 @@ nfs4_laundromat(struct nfsd_net *nn) .new_timeo = nn->nfsd4_lease }; struct nfs4_cpntf_state *cps; + struct nfs4_client *clp; copy_stateid_t *cps_t; int i; @@ -7076,7 +7274,7 @@ nfs4_laundromat(struct nfsd_net *nn) cps = container_of(cps_t, struct nfs4_cpntf_state, cp_stateid); if (cps->cp_stateid.cs_type == NFS4_COPYNOTIFY_STID && state_expired(<, cps->cpntf_time)) - _free_cpntf_state_locked(nn, cps); + revoke_cpntf_state_locked(nn, cps); } spin_unlock(&nn->s2s_cp_lock); nfsd4_async_copy_reaper(nn); @@ -7090,6 +7288,18 @@ nfs4_laundromat(struct nfsd_net *nn) dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); if (!state_expired(<, dp->dl_time)) break; + clp = dp->dl_stid.sc_client; + spin_lock(&nn->client_lock); + if (is_client_expired(clp)) { + spin_unlock(&nn->client_lock); + continue; + } + /* + * Pin without reviving: get_client_locked() would + * flip a courtesy client back to NFSD4_ACTIVE. + */ + atomic_inc(&clp->cl_rpc_users); + spin_unlock(&nn->client_lock); refcount_inc(&dp->dl_stid.sc_count); unhash_delegation_locked(dp, SC_STATUS_REVOKED); list_add(&dp->dl_recall_lru, &reaplist); @@ -7098,8 +7308,10 @@ nfs4_laundromat(struct nfsd_net *nn) while (!list_empty(&reaplist)) { dp = list_first_entry(&reaplist, struct nfs4_delegation, dl_recall_lru); + clp = dp->dl_stid.sc_client; list_del_init(&dp->dl_recall_lru); revoke_delegation(dp); + put_client_no_renew(clp); } spin_lock(&nn->client_lock); @@ -7109,11 +7321,16 @@ nfs4_laundromat(struct nfsd_net *nn) if (!state_expired(<, oo->oo_time)) break; list_del_init(&oo->oo_close_lru); + clp = oo->oo_owner.so_client; + if (is_client_expired(clp)) + continue; stp = oo->oo_last_closed_stid; oo->oo_last_closed_stid = NULL; + atomic_inc(&clp->cl_rpc_users); spin_unlock(&nn->client_lock); nfs4_put_stid(&stp->st_stid); spin_lock(&nn->client_lock); + put_client_no_renew_locked(clp); } spin_unlock(&nn->client_lock); @@ -7129,22 +7346,29 @@ nfs4_laundromat(struct nfsd_net *nn) * indefinitely once the lock does become free. */ BUG_ON(!list_empty(&reaplist)); + spin_lock(&nn->client_lock); spin_lock(&nn->blocked_locks_lock); - while (!list_empty(&nn->blocked_locks_lru)) { - nbl = list_first_entry(&nn->blocked_locks_lru, - struct nfsd4_blocked_lock, nbl_lru); + list_for_each_safe(pos, next, &nn->blocked_locks_lru) { + nbl = list_entry(pos, struct nfsd4_blocked_lock, nbl_lru); if (!state_expired(<, nbl->nbl_time)) break; + clp = nbl_client(nbl); + if (is_client_expired(clp)) + continue; + atomic_inc(&clp->cl_rpc_users); list_move(&nbl->nbl_lru, &reaplist); list_del_init(&nbl->nbl_list); } spin_unlock(&nn->blocked_locks_lock); + spin_unlock(&nn->client_lock); while (!list_empty(&reaplist)) { nbl = list_first_entry(&reaplist, struct nfsd4_blocked_lock, nbl_lru); + clp = nbl_client(nbl); list_del_init(&nbl->nbl_lru); free_blocked_lock(nbl); + put_client_no_renew(clp); } #ifdef CONFIG_NFSD_V4_2_INTER_SSC /* service the server-to-server copy delayed unmount list */ @@ -7195,12 +7419,12 @@ deleg_reaper(struct nfsd_net *nn) continue; if (atomic_read(&clp->cl_delegs_in_recall)) continue; - if (test_and_set_bit(NFSD4_CALLBACK_RUNNING, &clp->cl_ra->ra_cb.cb_flags)) - continue; if (ktime_get_boottime_seconds() - clp->cl_ra_time < 5) continue; if (clp->cl_cb_state != NFSD4_CB_UP) continue; + if (test_and_set_bit(NFSD4_CALLBACK_RUNNING, &clp->cl_ra->ra_cb.cb_flags)) + continue; /* release in nfsd4_cb_recall_any_release */ kref_get(&clp->cl_nfsdfs.cl_ref); @@ -7463,16 +7687,14 @@ nfs4_check_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfs4_stid *s, out: return status; } -static void -_free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps) + +static void _free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps) { WARN_ON_ONCE(cps->cp_stateid.cs_type != NFS4_COPYNOTIFY_STID); - if (!refcount_dec_and_test(&cps->cp_stateid.cs_count)) - return; - list_del(&cps->cp_list); - idr_remove(&nn->s2s_cp_stateids, - cps->cp_stateid.cs_stid.si_opaque.so_id); - kfree(cps); + if (refcount_dec_and_test(&cps->cp_stateid.cs_count)) { + nfsd4_unhash_cpntf_state(nn, cps); + kfree(cps); + } } /* * A READ from an inter server to server COPY will have a @@ -7497,10 +7719,20 @@ __be32 manage_cpntf_state(struct nfsd_net *nn, stateid_t *st, state = NULL; goto unlock; } - if (!clp) + if (!clp) { refcount_inc(&state->cp_stateid.cs_count); - else - _free_cpntf_state_locked(nn, state); + } else if (memcmp(&clp->cl_clientid, &state->cp_p_clid, + sizeof(clientid_t))) { + /* + * OFFLOAD_CANCEL: only the creating client may cancel. + * so_id is guessable, so without this check any client + * could free another's cpntf state. + */ + state = NULL; + goto unlock; + } else { + revoke_cpntf_state_locked(nn, state); + } } unlock: spin_unlock(&nn->s2s_cp_lock); @@ -7787,7 +8019,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, return status; stp = openlockstateid(s); if (nfsd4_cstate_assign_replay(cstate, stp->st_stateowner) == -EAGAIN) { - nfs4_put_stateowner(stp->st_stateowner); + nfs4_put_stid(&stp->st_stid); goto retry; } @@ -8036,6 +8268,10 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (status) goto put_stateid; + status = nfs4_check_fh(&cstate->current_fh, &dp->dl_stid); + if (status) + goto put_stateid; + trace_nfsd_deleg_return(stateid); destroy_delegation(dp); smp_mb__after_atomic(); @@ -8506,6 +8742,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfserr_no_grace; if (!locks_in_grace(net) && lock->lk_reclaim) goto out; + if (lock->lk_reclaim && + test_bit(NFSD4_CLIENT_RECLAIM_COMPLETE, &cstate->clp->cl_flags)) + goto out; if (lock->lk_reclaim) flags |= FL_RECLAIM; @@ -8542,10 +8781,11 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; } - if (lock->lk_type & (NFS4_READW_LT | NFS4_WRITEW_LT) && - nfsd4_has_session(cstate) && - locks_can_async_lock(nf->nf_file->f_op)) - flags |= FL_SLEEP; + if ((lock->lk_type == NFS4_READW_LT || + lock->lk_type == NFS4_WRITEW_LT) && + nfsd4_has_session(cstate) && + locks_can_async_lock(nf->nf_file->f_op)) + flags |= FL_SLEEP; nbl = find_or_allocate_block(lock_sop, &fp->fi_fhandle, nn); if (!nbl) { @@ -8587,7 +8827,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_inc_and_copy_stateid(&lock->lk_resp_stateid, &lock_stp->st_stid); status = 0; if (lock->lk_reclaim) - nn->somebody_reclaimed = true; + set_bit(NFSD_NET_SOMEBODY_RECLAIMED, &nn->flags); break; case FILE_LOCK_DEFERRED: kref_put(&nbl->nbl_kref, free_nbl); @@ -8963,9 +9203,13 @@ bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn) { struct nfs4_client_reclaim *crp; + bool found; + down_read(&nn->reclaim_str_hashtbl_lock); crp = nfsd4_find_reclaim_client(name, nn); - return (crp && crp->cr_clp); + found = (crp && crp->cr_clp); + up_read(&nn->reclaim_str_hashtbl_lock); + return found; } /* @@ -8978,10 +9222,39 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, unsigned int strhashval; struct nfs4_client_reclaim *crp; + down_write(&nn->reclaim_str_hashtbl_lock); + + /* + * A reclaim record for this client name may already exist (for + * example, populated at boot from the recovery directory before + * an in-grace RECLAIM_COMPLETE or an nfsdcld downcall delivers + * the same name). Dedup here so reclaim_str_hashtbl_size stays + * equal to the number of distinct client names; inc_reclaim_complete + * relies on that equality to end the grace period via the fast path. + */ + crp = nfsd4_find_reclaim_client(name, nn); + if (crp) { + if (princhash.len && crp->cr_princhash.len == 0) { + void *pdata = kmemdup(princhash.data, princhash.len, + GFP_KERNEL); + if (pdata) { + crp->cr_princhash.data = pdata; + crp->cr_princhash.len = princhash.len; + } else { + dprintk("%s: failed to allocate memory for princhash.data!\n", + __func__); + crp = NULL; + } + } + up_write(&nn->reclaim_str_hashtbl_lock); + return crp; + } + name.data = kmemdup(name.data, name.len, GFP_KERNEL); if (!name.data) { dprintk("%s: failed to allocate memory for name.data!\n", __func__); + up_write(&nn->reclaim_str_hashtbl_lock); return NULL; } if (princhash.len) { @@ -8990,6 +9263,7 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, dprintk("%s: failed to allocate memory for princhash.data!\n", __func__); kfree(name.data); + up_write(&nn->reclaim_str_hashtbl_lock); return NULL; } } else @@ -9009,6 +9283,7 @@ nfs4_client_to_reclaim(struct xdr_netobj name, struct xdr_netobj princhash, kfree(name.data); kfree(princhash.data); } + up_write(&nn->reclaim_str_hashtbl_lock); return crp; } @@ -9028,6 +9303,7 @@ nfs4_release_reclaim(struct nfsd_net *nn) struct nfs4_client_reclaim *crp = NULL; int i; + down_write(&nn->reclaim_str_hashtbl_lock); for (i = 0; i < CLIENT_HASH_SIZE; i++) { while (!list_empty(&nn->reclaim_str_hashtbl[i])) { crp = list_entry(nn->reclaim_str_hashtbl[i].next, @@ -9036,6 +9312,7 @@ nfs4_release_reclaim(struct nfsd_net *nn) } } WARN_ON_ONCE(nn->reclaim_str_hashtbl_size); + up_write(&nn->reclaim_str_hashtbl_lock); } /* @@ -9113,8 +9390,9 @@ static int nfs4_state_create_net(struct net *net) nn->conf_name_tree = RB_ROOT; nn->unconf_name_tree = RB_ROOT; nn->boot_time = ktime_get_real_seconds(); - nn->grace_ended = false; - nn->grace_end_forced = false; + nn->boot_time_bt = ktime_get_boottime_seconds(); + clear_bit(NFSD_NET_GRACE_ENDED, &nn->flags); + clear_bit(NFSD_NET_GRACE_END_FORCED, &nn->flags); nn->nfsd4_manager.block_opens = true; INIT_LIST_HEAD(&nn->nfsd4_manager.list); INIT_LIST_HEAD(&nn->client_lru); @@ -9200,7 +9478,8 @@ nfs4_state_start_net(struct net *net) nfsd4_client_tracking_init(net); /* safe for laundromat to run now */ enable_delayed_work(&nn->laundromat_work); - if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0) + if (test_bit(NFSD_NET_TRACK_RECLAIM_COMPLETES, &nn->flags) && + nn->reclaim_str_hashtbl_size == 0) goto skip_grace; printk(KERN_INFO "NFSD: starting %lld-second grace period (net %x)\n", nn->nfsd4_grace, net->ns.inum); diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index e17488a911f765..6a0a97d39bf6a6 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -449,9 +449,18 @@ nfsd4_decode_posixacl(struct nfsd4_compoundargs *argp, struct posix_acl **acl) if (xdr_stream_decode_u32(argp->xdr, &count) < 0) return nfserr_bad_xdr; + /* + * The NFSv4 POSIX ACL draft doesn't define a max number of ACE's, but + * the NFSACL spec does. For NFSv4, cap the number of entries to the v3 + * limit, as we want to ensure that ACLs set via NFSv4 POSIX ACL + * extensions are retrievable via NFSACL. + */ + if (count > NFS_ACL_MAX_ENTRIES) + return nfserr_inval; + *acl = posix_acl_alloc(count, GFP_KERNEL); if (*acl == NULL) - return nfserr_resource; + return nfserr_jukebox; (*acl)->a_count = count; for (ace = (*acl)->a_entries; ace < (*acl)->a_entries + count; ace++) { @@ -628,6 +637,8 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen, if (!xdrgen_decode_fattr4_time_deleg_access(argp->xdr, &access)) return nfserr_bad_xdr; + if (access.nseconds >= NSEC_PER_SEC) + return nfserr_inval; iattr->ia_atime.tv_sec = access.seconds; iattr->ia_atime.tv_nsec = access.nseconds; iattr->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET | ATTR_DELEG; @@ -637,6 +648,8 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen, if (!xdrgen_decode_fattr4_time_deleg_modify(argp->xdr, &modify)) return nfserr_bad_xdr; + if (modify.nseconds >= NSEC_PER_SEC) + return nfserr_inval; iattr->ia_mtime.tv_sec = modify.seconds; iattr->ia_mtime.tv_nsec = modify.nseconds; iattr->ia_ctime.tv_sec = modify.seconds; @@ -955,6 +968,10 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u) case NF4LNK: if (xdr_stream_decode_u32(argp->xdr, &create->cr_datalen) < 0) return nfserr_bad_xdr; + if (create->cr_datalen == 0) + return nfserr_inval; + if (create->cr_datalen > NFS4_MAXPATHLEN) + return nfserr_nametoolong; p = xdr_inline_decode(argp->xdr, create->cr_datalen); if (!p) return nfserr_bad_xdr; @@ -2106,6 +2123,7 @@ static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp, { struct nfs42_netaddr *naddr; __be32 *p; + u32 str_len; if (xdr_stream_decode_u32(argp->xdr, &ns->nl4_type) < 0) return nfserr_bad_xdr; @@ -2135,6 +2153,18 @@ static __be32 nfsd4_decode_nl4_server(struct nfsd4_compoundargs *argp, return nfserr_bad_xdr; memcpy(naddr->addr, p, naddr->addr_len); break; + case NL4_NAME: + case NL4_URL: + /* + * Well-formed XDR, but only NL4_NETADDR is supported. Consume + * the utf8str_cis to keep the stream aligned, then return + * NFS4ERR_NOTSUPP rather than the misleading NFS4ERR_BADXDR. + */ + if (xdr_stream_decode_u32(argp->xdr, &str_len) < 0) + return nfserr_bad_xdr; + if (!xdr_inline_decode(argp->xdr, str_len)) + return nfserr_bad_xdr; + return nfserr_notsupp; default: return nfserr_bad_xdr; } @@ -6390,9 +6420,6 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) write_bytes_to_xdr_buf(xdr->buf, op_status_offset, &op->status, XDR_UNIT); release: - if (opdesc && opdesc->op_release) - opdesc->op_release(&op->u); - /* * Account for pages consumed while encoding this operation. * The xdr_stream primitives don't manage rq_next_page. @@ -6424,9 +6451,12 @@ void nfsd4_release_compoundargs(struct svc_rqst *rqstp) { struct nfsd4_compoundargs *args = rqstp->rq_argp; + args->opcnt = 0; if (args->ops != args->iops) { - vfree(args->ops); + void *old_ops = args->ops; + args->ops = args->iops; + kvfree_rcu_mightsleep(old_ops); } while (args->to_free) { struct svcxdr_tmpbuf *tb = args->to_free; diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c index 154468ceccdc12..a9fa9dd410b2fa 100644 --- a/fs/nfsd/nfscache.c +++ b/fs/nfsd/nfscache.c @@ -200,14 +200,14 @@ int nfsd_reply_cache_init(struct nfsd_net *nn) nn->nfsd_reply_cache_shrinker->seeks = 1; nn->nfsd_reply_cache_shrinker->private_data = nn; - shrinker_register(nn->nfsd_reply_cache_shrinker); - for (i = 0; i < hashsize; i++) { INIT_LIST_HEAD(&nn->drc_hashtbl[i].lru_head); spin_lock_init(&nn->drc_hashtbl[i].cache_lock); } nn->drc_hashsize = hashsize; + shrinker_register(nn->nfsd_reply_cache_shrinker); + return 0; out_shrinker: kvfree(nn->drc_hashtbl); @@ -275,7 +275,7 @@ nfsd_prune_bucket_locked(struct nfsd_net *nn, struct nfsd_drc_bucket *b, nfsd_cacherep_unlink_locked(nn, b, rp); list_add(&rp->c_lru, dispose); - if (max && ++freed > max) + if (max && ++freed >= max) break; } } diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index fa92e31d19d6f7..bc16fc7ca24f33 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -296,14 +296,15 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size) * 2. Is that directory a mount point, or * 3. Is that directory the root of an exported file system? */ - nfsd4_cancel_copy_by_sb(netns(file), path.dentry->d_sb); error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb); mutex_lock(&nfsd_mutex); nn = net_generic(netns(file), nfsd_net_id); - if (nn->nfsd_serv) + if (test_bit(NFSD_NET_UP, &nn->flags)) { + nfsd4_cancel_copy_by_sb(netns(file), path.dentry->d_sb); nfsd4_revoke_states(nn, path.dentry->d_sb); - else + } else { error = -EINVAL; + } mutex_unlock(&nfsd_mutex); path_put(&path); @@ -420,6 +421,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size) char *mesg = buf; int rv; struct net *net = netns(file); + struct nfsd_net *nn = net_generic(net, nfsd_net_id); if (size > 0) { int newthreads; @@ -430,7 +432,10 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size) return -EINVAL; trace_nfsd_ctl_threads(net, newthreads); mutex_lock(&nfsd_mutex); - rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL); + if (newthreads > 0 || nn->nfsd_serv != NULL) + rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL); + else + rv = 0; mutex_unlock(&nfsd_mutex); if (rv < 0) return rv; @@ -1111,7 +1116,7 @@ static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size) } return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%c\n", - nn->grace_ended ? 'Y' : 'N'); + test_bit(NFSD_NET_GRACE_ENDED, &nn->flags) ? 'Y' : 'N'); } #endif @@ -1414,8 +1419,8 @@ static int create_proc_exports_entry(void) unsigned int nfsd_net_id; struct nfsd_genl_rqstp { - struct sockaddr rq_daddr; - struct sockaddr rq_saddr; + struct sockaddr_storage rq_daddr; + struct sockaddr_storage rq_saddr; unsigned long rq_flags; ktime_t rq_stime; __be32 rq_xid; @@ -1448,9 +1453,9 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb, nla_put_s64(skb, NFSD_A_RPC_STATUS_SERVICE_TIME, ktime_to_us(genl_rqstp->rq_stime), NFSD_A_RPC_STATUS_PAD)) - return -ENOBUFS; + goto out_cancel; - switch (genl_rqstp->rq_saddr.sa_family) { + switch (genl_rqstp->rq_saddr.ss_family) { case AF_INET: { const struct sockaddr_in *s_in, *d_in; @@ -1464,7 +1469,7 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb, s_in->sin_port) || nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT, d_in->sin_port)) - return -ENOBUFS; + goto out_cancel; break; } case AF_INET6: { @@ -1480,7 +1485,7 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb, s_in->sin6_port) || nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT, d_in->sin6_port)) - return -ENOBUFS; + goto out_cancel; break; } } @@ -1488,10 +1493,14 @@ static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb, for (i = 0; i < genl_rqstp->rq_opcnt; i++) if (nla_put_u32(skb, NFSD_A_RPC_STATUS_COMPOUND_OPS, genl_rqstp->rq_opnum[i])) - return -ENOBUFS; + goto out_cancel; genlmsg_end(skb, hdr); return 0; + +out_cancel: + genlmsg_cancel(skb, hdr); + return -ENOBUFS; } /** @@ -1519,18 +1528,28 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb, for (i = 0; i < nn->nfsd_serv->sv_nrpools; i++) { struct svc_rqst *rqstp; + long thread_skip = 0; if (i < cb->args[0]) /* already consumed */ continue; + /* + * The saved thread index only applies to the pool the dump + * was resumed in. Subsequent pools must start from thread 0, + * otherwise their first cb->args[1] threads are silently + * skipped. + */ + if (i == cb->args[0]) + thread_skip = cb->args[1]; + rqstp_index = 0; list_for_each_entry_rcu(rqstp, &nn->nfsd_serv->sv_pools[i].sp_all_threads, rq_all) { - struct nfsd_genl_rqstp genl_rqstp; + struct nfsd_genl_rqstp genl_rqstp = {}; unsigned int status_counter; - if (rqstp_index++ < cb->args[1]) /* already consumed */ + if (rqstp_index++ < thread_skip) /* already consumed */ continue; /* * Acquire rq_status_counter before parsing the rqst @@ -1551,9 +1570,9 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb, genl_rqstp.rq_stime = rqstp->rq_stime; genl_rqstp.rq_opcnt = 0; memcpy(&genl_rqstp.rq_daddr, svc_daddr(rqstp), - sizeof(struct sockaddr)); + sizeof(struct sockaddr_storage)); memcpy(&genl_rqstp.rq_saddr, svc_addr(rqstp), - sizeof(struct sockaddr)); + sizeof(struct sockaddr_storage)); #ifdef CONFIG_NFSD_V4 if (rqstp->rq_vers == NFS4_VERSION && @@ -1572,17 +1591,26 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb, #endif /* CONFIG_NFSD_V4 */ /* - * Acquire rq_status_counter before reporting the rqst - * fields to the user. + * Read-side load-load fence: order the field reads + * above before the counter re-read below, mirroring + * the smp_rmb() in the standard seqcount retry. The + * begin-side smp_load_acquire() above pairs with the + * smp_store_release() in nfsd_dispatch(). */ - if (smp_load_acquire(&rqstp->rq_status_counter) != - status_counter) + smp_rmb(); + if (READ_ONCE(rqstp->rq_status_counter) != status_counter) continue; ret = nfsd_genl_rpc_status_compose_msg(skb, cb, &genl_rqstp); - if (ret) + if (ret) { + if (skb->len) { + cb->args[0] = i; + cb->args[1] = rqstp_index - 1; + ret = skb->len; + } goto out; + } } } @@ -1943,6 +1971,60 @@ int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info) return err; } +/** + * nfsd_nl_validate_listeners - sanity-check the listener list from userland + * @info: netlink metadata and command arguments + * + * Walk every NFSD_A_SERVER_SOCK_ADDR attribute and confirm that each entry + * is well-formed: it parses against the policy, carries both an address and + * a transport name, and the address is long enough for its family. Doing + * this up front lets the callers below assume every entry is valid and + * guarantees we make no changes when the request is malformed. + * + * Return: 0 if every entry is valid, or a negative errno otherwise. + */ +static int nfsd_nl_validate_listeners(struct genl_info *info) +{ + const struct nlattr *attr; + int rem; + + nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_SOCK_ADDR, info->nlhdr, + GENL_HDRLEN, rem) { + struct nlattr *tb[NFSD_A_SOCK_MAX + 1]; + struct sockaddr *sa; + int err; + + err = nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr, + nfsd_sock_nl_policy, info->extack); + if (err < 0) + return err; + + if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME]) + return -EINVAL; + + sa = nla_data(tb[NFSD_A_SOCK_ADDR]); + if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(sa->sa_family)) + return -EINVAL; + + switch (sa->sa_family) { + case AF_INET: + if (nla_len(tb[NFSD_A_SOCK_ADDR]) < + sizeof(struct sockaddr_in)) + return -EINVAL; + break; + case AF_INET6: + if (nla_len(tb[NFSD_A_SOCK_ADDR]) < + sizeof(struct sockaddr_in6)) + return -EINVAL; + break; + default: + return -EAFNOSUPPORT; + } + } + + return 0; +} + /** * nfsd_nl_listener_set_doit - set the nfs running sockets * @skb: reply buffer @@ -1961,6 +2043,15 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info) bool delete = false; int err, rem; + /* + * Validate the entire listener list before making any changes, so a + * malformed request fails cleanly without creating a serv or touching + * the existing listeners. + */ + err = nfsd_nl_validate_listeners(info); + if (err) + return err; + mutex_lock(&nfsd_mutex); err = nfsd_create_serv(net); @@ -1987,16 +2078,11 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info) const char *xcl_name; struct sockaddr *sa; + /* validated up front in nfsd_nl_validate_listeners() */ if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr, nfsd_sock_nl_policy, info->extack) < 0) continue; - if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME]) - continue; - - if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(*sa)) - continue; - xcl_name = nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]); sa = nla_data(tb[NFSD_A_SOCK_ADDR]); @@ -2048,16 +2134,11 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info) struct sockaddr *sa; int ret; + /* validated up front in nfsd_nl_validate_listeners() */ if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr, nfsd_sock_nl_policy, info->extack) < 0) continue; - if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME]) - continue; - - if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(*sa)) - continue; - xcl_name = nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]); sa = nla_data(tb[NFSD_A_SOCK_ADDR]); @@ -2343,7 +2424,7 @@ int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb, error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb); mutex_lock(&nfsd_mutex); - if (nn->nfsd_serv) { + if (test_bit(NFSD_NET_UP, &nn->flags)) { nfsd4_cancel_copy_by_sb(net, path.dentry->d_sb); nfsd4_revoke_states(nn, path.dentry->d_sb); } else { @@ -2390,7 +2471,7 @@ int nfsd_nl_unlock_export_doit(struct sk_buff *skb, struct genl_info *info) return error; mutex_lock(&nfsd_mutex); - if (nn->nfsd_serv) { + if (test_bit(NFSD_NET_UP, &nn->flags)) { nfsd_file_close_export(net, &path); nfsd4_revoke_export_states(nn, &path); } else @@ -2512,11 +2593,12 @@ static int __init init_nfsd(void) { int retval; - nfsd_debugfs_init(); - retval = nfsd4_init_slabs(); if (retval) return retval; + + nfsd_debugfs_init(); + retval = nfsd4_init_pnfs(); if (retval) goto out_free_slabs; @@ -2561,8 +2643,8 @@ static int __init init_nfsd(void) out_free_pnfs: nfsd4_exit_pnfs(); out_free_slabs: - nfsd4_free_slabs(); nfsd_debugfs_exit(); + nfsd4_free_slabs(); return retval; } @@ -2577,9 +2659,9 @@ static void __exit exit_nfsd(void) unregister_pernet_subsys(&nfsd_net_ops); nfsd_drc_slab_free(); nfsd_lockd_shutdown(); - nfsd4_free_slabs(); nfsd4_exit_pnfs(); nfsd_debugfs_exit(); + nfsd4_free_slabs(); } MODULE_AUTHOR("Olaf Kirch "); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 429ca5c6ec087b..ab53de1c280d5a 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -70,10 +70,8 @@ nfsd_mode_check(struct dentry *dentry, umode_t requested) if (requested == 0) /* the caller doesn't care */ return nfs_ok; if (mode == requested) { - if (mode == S_IFDIR && !d_can_lookup(dentry)) { - WARN_ON_ONCE(1); + if (mode == S_IFDIR && !d_can_lookup(dentry)) return nfserr_notdir; - } return nfs_ok; } if (mode == S_IFLNK) { @@ -344,15 +342,19 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct net *net, if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOWCC) fhp->fh_no_wcc = true; fhp->fh_64bit_cookies = true; - if (exp->ex_flags & NFSEXP_V4ROOT) + if (exp->ex_flags & NFSEXP_V4ROOT) { + dput(dentry); goto out; + } break; case NFS_FHSIZE: fhp->fh_no_wcc = true; if (EX_WGATHER(exp)) fhp->fh_use_wgather = true; - if (exp->ex_flags & NFSEXP_V4ROOT) + if (exp->ex_flags & NFSEXP_V4ROOT) { + dput(dentry); goto out; + } } fhp->fh_dentry = dentry; diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 8873033d1e82f4..a73d5c259cd93a 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -82,6 +82,7 @@ nfsd_proc_setattr(struct svc_rqst *rqstp) .na_iattr = iap, }; struct svc_fh *fhp; + int hosterr; dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n", SVCFH_fmt(&argp->fh), @@ -117,6 +118,12 @@ nfsd_proc_setattr(struct svc_rqst *rqstp) if (resp->status != nfs_ok) goto out; + hosterr = fh_want_write(fhp); + if (hosterr) { + resp->status = nfserrno(hosterr); + goto out; + } + if (delta < 0) delta = -delta; if (delta < MAX_TOUCH_TIME_ERROR && diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 4f1ab3222a4d47..5e0a0c1cbb9108 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -351,7 +351,7 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred) struct nfsd_net *nn = net_generic(net, nfsd_net_id); int ret; - if (nn->nfsd_net_up) + if (test_bit(NFSD_NET_UP, &nn->flags)) return 0; ret = nfsd_startup_generic(); @@ -364,11 +364,11 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred) goto out_socks; } - if (nfsd_needs_lockd(nn) && !nn->lockd_up) { + if (nfsd_needs_lockd(nn) && !test_bit(NFSD_NET_LOCKD_UP, &nn->flags)) { ret = lockd_up(net, cred); if (ret) goto out_socks; - nn->lockd_up = true; + set_bit(NFSD_NET_LOCKD_UP, &nn->flags); } ret = nfsd_file_cache_start_net(net); @@ -386,7 +386,7 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred) if (ret) goto out_reply_cache; - nn->nfsd_net_up = true; + set_bit(NFSD_NET_UP, &nn->flags); return 0; out_reply_cache: @@ -394,9 +394,9 @@ static int nfsd_startup_net(struct net *net, const struct cred *cred) out_filecache: nfsd_file_cache_shutdown_net(net); out_lockd: - if (nn->lockd_up) { + if (test_bit(NFSD_NET_LOCKD_UP, &nn->flags)) { lockd_down(net); - nn->lockd_up = false; + clear_bit(NFSD_NET_LOCKD_UP, &nn->flags); } out_socks: nfsd_shutdown_generic(); @@ -407,7 +407,7 @@ static void nfsd_shutdown_net(struct net *net) { struct nfsd_net *nn = net_generic(net, nfsd_net_id); - if (nn->nfsd_net_up) { + if (test_bit(NFSD_NET_UP, &nn->flags)) { percpu_ref_kill_and_confirm(&nn->nfsd_net_ref, nfsd_net_done); wait_for_completion(&nn->nfsd_net_confirm_done); @@ -415,18 +415,18 @@ static void nfsd_shutdown_net(struct net *net) nfs4_state_shutdown_net(net); nfsd_reply_cache_shutdown(nn); nfsd_file_cache_shutdown_net(net); - if (nn->lockd_up) { + if (test_bit(NFSD_NET_LOCKD_UP, &nn->flags)) { lockd_down(net); - nn->lockd_up = false; + clear_bit(NFSD_NET_LOCKD_UP, &nn->flags); } wait_for_completion(&nn->nfsd_net_free_done); } percpu_ref_exit(&nn->nfsd_net_ref); - if (nn->nfsd_net_up) + if (test_bit(NFSD_NET_UP, &nn->flags)) nfsd_shutdown_generic(); - nn->nfsd_net_up = false; + clear_bit(NFSD_NET_UP, &nn->flags); } static DEFINE_SPINLOCK(nfsd_notifier_lock); @@ -815,7 +815,7 @@ nfsd_acl_init_request(struct svc_rqst *rqstp, ret->mismatch.lovers = NFSD_ACL_NRVERS; for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++) { - if (nfsd_support_acl_version(rqstp->rq_vers) && + if (nfsd_support_acl_version(i) && nfsd_vers(nn, i, NFSD_TEST)) { ret->mismatch.lovers = i; break; @@ -825,7 +825,7 @@ nfsd_acl_init_request(struct svc_rqst *rqstp, return rpc_prog_unavail; ret->mismatch.hivers = NFSD_ACL_MINVERS; for (i = NFSD_ACL_NRVERS - 1; i >= NFSD_ACL_MINVERS; i--) { - if (nfsd_support_acl_version(rqstp->rq_vers) && + if (nfsd_support_acl_version(i) && nfsd_vers(nn, i, NFSD_TEST)) { ret->mismatch.hivers = i; break; @@ -960,6 +960,20 @@ nfsd(void *vrqstp) return 0; } +/* + * Set rq_status_counter back to an even value, indicating that the rqstp + * fields are no longer meaningful to a lockless reader. This pairs with the + * odd-valued store made once the request has been decoded, and must run on + * every return path that follows it so that the seq-lock like protocol used + * by nfsd_nl_rpc_status_get_dumpit() is not left permanently odd. The store + * also advances the counter so a concurrent reader detects the transition. + */ +static void nfsd_status_counter_set_idle(struct svc_rqst *rqstp) +{ + smp_store_release(&rqstp->rq_status_counter, + (rqstp->rq_status_counter | 1) + 1); +} + /** * nfsd_dispatch - Process an NFS or NFSACL or LOCALIO Request * @rqstp: incoming request @@ -1022,14 +1036,9 @@ int nfsd_dispatch(struct svc_rqst *rqstp) if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream)) goto out_encode_err; - /* - * Release rq_status_counter setting it to an even value after the rpc - * request has been properly processed. - */ - smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1); - nfsd_cache_update(rqstp, rp, ntli->ntli_cachetype, nfs_reply); out_cached_reply: + nfsd_status_counter_set_idle(rqstp); return 1; out_decode_err: @@ -1040,12 +1049,14 @@ int nfsd_dispatch(struct svc_rqst *rqstp) out_update_drop: nfsd_cache_update(rqstp, rp, RC_NOCACHE, NULL); out_dropit: + nfsd_status_counter_set_idle(rqstp); return 0; out_encode_err: trace_nfsd_cant_encode_err(rqstp); nfsd_cache_update(rqstp, rp, RC_NOCACHE, NULL); *statp = rpc_system_err; + nfsd_status_counter_set_idle(rqstp); return 1; } diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index ae71e062131758..86c9dd4b334bd2 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -9,6 +9,16 @@ #include "xdr.h" #include "auth.h" +/* + * Sun convention: a sattr time-useconds field of one full second (an + * otherwise out-of-range value) means "set this time to the current + * server time." It's needed to make permissions checks for the "touch" + * program across NFSv2 mounts work correctly. See description of + * sattr in section 6.1 of "NFS Illustrated" by Brent Callaghan, + * Addison-Wesley, ISBN 0-201-32750-5 + */ +#define NFS2_SATTR_SET_TO_SERVER_TIME (1000000) + /* * Mapping of S_IF* types to NFS file types */ @@ -172,27 +182,29 @@ svcxdr_decode_sattr(struct svc_rqst *rqstp, struct xdr_stream *xdr, tmp1 = be32_to_cpup(p++); tmp2 = be32_to_cpup(p++); if (tmp1 != (u32)-1 && tmp2 != (u32)-1) { + /* + * Range test here to prevent the multiplication from + * wrapping to a valid (but incorrect) value on 32-bit + * platforms. + */ + if (tmp2 > NFS2_SATTR_SET_TO_SERVER_TIME) + return false; iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET; iap->ia_atime.tv_sec = tmp1; iap->ia_atime.tv_nsec = tmp2 * NSEC_PER_USEC; + if (tmp2 == NFS2_SATTR_SET_TO_SERVER_TIME) + iap->ia_valid &= ~ATTR_ATIME_SET; } tmp1 = be32_to_cpup(p++); tmp2 = be32_to_cpup(p++); if (tmp1 != (u32)-1 && tmp2 != (u32)-1) { + if (tmp2 > NFS2_SATTR_SET_TO_SERVER_TIME) + return false; iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET; iap->ia_mtime.tv_sec = tmp1; iap->ia_mtime.tv_nsec = tmp2 * NSEC_PER_USEC; - /* - * Passing the invalid value useconds=1000000 for mtime - * is a Sun convention for "set both mtime and atime to - * current server time". It's needed to make permissions - * checks for the "touch" program across v2 mounts to - * Solaris and Irix boxes work correctly. See description of - * sattr in section 6.1 of "NFS Illustrated" by - * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5 - */ - if (tmp2 == 1000000) + if (tmp2 == NFS2_SATTR_SET_TO_SERVER_TIME) iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET); } diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index dec83e92650d11..f44ea672670f58 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -384,6 +384,7 @@ struct nfsd4_session { u16 se_slot_gen; bool se_dead; u32 se_target_maxslots; + struct rcu_head rcu_head; }; /* formatted contents of nfs4_sessionid */ @@ -496,7 +497,7 @@ struct nfs4_client { #define NFSD4_CB_FAULT 3 int cl_cb_state; struct nfsd4_callback cl_cb_null; - struct nfsd4_session *cl_cb_session; + struct nfsd4_session __rcu *cl_cb_session; /* for all client information that callback code might need: */ spinlock_t cl_lock; @@ -754,6 +755,7 @@ struct nfs4_layout_stateid { struct delayed_work ls_fence_work; unsigned int ls_fence_delay; bool ls_fenced; + bool ls_fence_inflight; }; static inline struct nfs4_layout_stateid *layoutstateid(struct nfs4_stid *s) diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h index 1c5a1e50f94651..33953d38314e9b 100644 --- a/fs/nfsd/trace.h +++ b/fs/nfsd/trace.h @@ -271,7 +271,7 @@ TRACE_EVENT_CONDITION(nfsd_fh_verify, TP_CONDITION(rqstp != NULL), TP_STRUCT__entry( __field(unsigned int, netns_ino) - __sockaddr(server, rqstp->rq_xprt->xpt_remotelen) + __sockaddr(server, rqstp->rq_xprt->xpt_locallen) __sockaddr(client, rqstp->rq_xprt->xpt_remotelen) __field(u32, xid) __field(u32, fh_hash) @@ -310,7 +310,7 @@ TRACE_EVENT_CONDITION(nfsd_fh_verify_err, TP_CONDITION(rqstp != NULL && error), TP_STRUCT__entry( __field(unsigned int, netns_ino) - __sockaddr(server, rqstp->rq_xprt->xpt_remotelen) + __sockaddr(server, rqstp->rq_xprt->xpt_locallen) __sockaddr(client, rqstp->rq_xprt->xpt_remotelen) __field(u32, xid) __field(u32, fh_hash) @@ -1727,9 +1727,10 @@ DEFINE_NFSD_CB_LIFETIME_EVENT(bc_shutdown); TRACE_EVENT(nfsd_cb_seq_status, TP_PROTO( const struct rpc_task *task, - const struct nfsd4_callback *cb + const struct nfsd4_callback *cb, + const struct nfsd4_session *session ), - TP_ARGS(task, cb), + TP_ARGS(task, cb, session), TP_STRUCT__entry( __field(unsigned int, task_id) __field(unsigned int, client_id) @@ -1741,8 +1742,6 @@ TRACE_EVENT(nfsd_cb_seq_status, __field(int, seq_status) ), TP_fast_assign( - const struct nfs4_client *clp = cb->cb_clp; - const struct nfsd4_session *session = clp->cl_cb_session; const struct nfsd4_sessionid *sid = (struct nfsd4_sessionid *)&session->se_sessionid; @@ -1768,9 +1767,10 @@ TRACE_EVENT(nfsd_cb_seq_status, TRACE_EVENT(nfsd_cb_free_slot, TP_PROTO( const struct rpc_task *task, - const struct nfsd4_callback *cb + const struct nfsd4_callback *cb, + const struct nfsd4_session *session ), - TP_ARGS(task, cb), + TP_ARGS(task, cb, session), TP_STRUCT__entry( __field(unsigned int, task_id) __field(unsigned int, client_id) @@ -1781,8 +1781,6 @@ TRACE_EVENT(nfsd_cb_free_slot, __field(u32, slot_seqno) ), TP_fast_assign( - const struct nfs4_client *clp = cb->cb_clp; - const struct nfsd4_session *session = clp->cl_cb_session; const struct nfsd4_sessionid *sid = (struct nfsd4_sessionid *)&session->se_sessionid; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 1e89c7ff9493b8..f73012bc742f80 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -137,8 +137,10 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, follow_flags = LOOKUP_AUTOMOUNT; err = follow_down(&path, follow_flags); - if (err < 0) + if (err < 0) { + path_put(&path); goto out; + } if (path.mnt == exp->ex_path.mnt && path.dentry == dentry && nfsd_mountpoint(dentry, exp) == 2) { /* This is only a mountpoint in some other namespace */ @@ -419,21 +421,22 @@ nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap) } static __be32 -nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp, - struct iattr *iap) +nfsd_may_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, + struct iattr *iap) { struct inode *inode = d_inode(fhp->fh_dentry); - if (iap->ia_size < inode->i_size) { - __be32 err; + if (iap->ia_size >= i_size_read(inode)) + return nfs_ok; - err = nfsd_permission(&rqstp->rq_cred, - fhp->fh_export, fhp->fh_dentry, - NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE); - if (err) - return err; - } - return nfserrno(get_write_access(inode)); + return nfsd_permission(&rqstp->rq_cred, fhp->fh_export, fhp->fh_dentry, + NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE); +} + +static __be32 +nfsd_get_write_access(struct svc_fh *fhp) +{ + return nfserrno(get_write_access(d_inode(fhp->fh_dentry))); } static int __nfsd_setattr(struct dentry *dentry, struct iattr *iap) @@ -560,12 +563,17 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, * setattr call. */ if (size_change) { - err = nfsd_get_write_access(rqstp, fhp, iap); + err = nfsd_get_write_access(fhp); if (err) return err; } inode_lock(inode); + if (size_change) { + err = nfsd_may_truncate(rqstp, fhp, iap); + if (err) + goto out_unlock; + } err = fh_fill_pre_attrs(fhp); if (err) goto out_unlock; @@ -1374,6 +1382,7 @@ nfsd_direct_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file = nf->nf_file; unsigned int nsegs, i; ssize_t host_err; + size_t expected; nsegs = nfsd_write_dio_iters_init(nf, rqstp->rq_bvec, nvecs, kiocb, *cnt, segments); @@ -1395,11 +1404,13 @@ nfsd_direct_write(struct svc_rqst *rqstp, struct svc_fh *fhp, kiocb->ki_flags |= IOCB_DONTCACHE; } + expected = iov_iter_count(&segments[i].iter); + host_err = vfs_iocb_iter_write(file, kiocb, &segments[i].iter); if (host_err < 0) return host_err; *cnt += host_err; - if (host_err < segments[i].iter.count) + if (host_err < (ssize_t)expected) break; /* partial write */ } diff --git a/fs/nilfs2/bmap.c b/fs/nilfs2/bmap.c index 5f0f1f283af020..83f6ea30cc8b53 100644 --- a/fs/nilfs2/bmap.c +++ b/fs/nilfs2/bmap.c @@ -175,7 +175,7 @@ static int nilfs_bmap_do_delete(struct nilfs_bmap *bmap, __u64 key) return ret; } - return bmap->b_ops->bop_delete(bmap, key); + return bmap->b_ops->bop_delete(bmap, key, false); } /** diff --git a/fs/nilfs2/bmap.h b/fs/nilfs2/bmap.h index 4656df3927228e..a72f3c308a5d29 100644 --- a/fs/nilfs2/bmap.h +++ b/fs/nilfs2/bmap.h @@ -63,7 +63,7 @@ struct nilfs_bmap_operations { int (*bop_lookup_contig)(const struct nilfs_bmap *, __u64, __u64 *, unsigned int); int (*bop_insert)(struct nilfs_bmap *, __u64, __u64); - int (*bop_delete)(struct nilfs_bmap *, __u64); + int (*bop_delete)(struct nilfs_bmap *bmap, __u64 key, bool deform); void (*bop_clear)(struct nilfs_bmap *); int (*bop_propagate)(struct nilfs_bmap *, struct buffer_head *); diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 64d5f7c5ab440d..64bac66af25b4c 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -1425,6 +1425,28 @@ static void nilfs_btree_shrink(struct nilfs_bmap *btree, path[level].bp_bh = NULL; } +/** + * nilfs_btree_discard - discard the last node for the mapping transformation + * @btree: bmap struct of btree + * @path: array of nilfs_btree_path struct + * @level: level of the B-tree node being operated on + * @keyp: argument for passing a key (unused) + * @ptrp: argument for passing a pointer (unused) + */ +static void nilfs_btree_discard(struct nilfs_bmap *btree, + struct nilfs_btree_path *path, int level, + __u64 *keyp, __u64 *ptrp) +{ + struct nilfs_btree_node *root = nilfs_btree_get_root(btree); + + nilfs_btree_node_delete(root, 0, NULL, NULL, + NILFS_BTREE_ROOT_NCHILDREN_MAX); + nilfs_btree_node_set_level(root, level); + + nilfs_btnode_delete(path[level].bp_bh); + path[level].bp_bh = NULL; +} + static void nilfs_btree_nop(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 *keyp, __u64 *ptrp) @@ -1435,7 +1457,7 @@ static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int *levelp, struct nilfs_bmap_stats *stats, - struct inode *dat) + struct inode *dat, bool deform) { struct buffer_head *bh; struct nilfs_btree_node *node, *parent, *sib; @@ -1522,15 +1544,17 @@ static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree, if (nilfs_btree_node_get_nchildren(node) - 1 <= NILFS_BTREE_ROOT_NCHILDREN_MAX) { path[level].bp_op = nilfs_btree_shrink; - stats->bs_nblocks += 2; - level++; - path[level].bp_op = nilfs_btree_nop; - goto shrink_root_child; + } else if (deform) { + path[level].bp_op = nilfs_btree_discard; } else { path[level].bp_op = nilfs_btree_do_delete; stats->bs_nblocks++; goto out; } + stats->bs_nblocks += 2; + level++; + path[level].bp_op = nilfs_btree_nop; + goto shrink_root_child; } } @@ -1581,7 +1605,7 @@ static void nilfs_btree_commit_delete(struct nilfs_bmap *btree, nilfs_bmap_set_dirty(btree); } -static int nilfs_btree_delete(struct nilfs_bmap *btree, __u64 key) +static int nilfs_btree_delete(struct nilfs_bmap *btree, __u64 key, bool deform) { struct nilfs_btree_path *path; @@ -1601,7 +1625,8 @@ static int nilfs_btree_delete(struct nilfs_bmap *btree, __u64 key) dat = NILFS_BMAP_USE_VBN(btree) ? nilfs_bmap_get_dat(btree) : NULL; - ret = nilfs_btree_prepare_delete(btree, path, &level, &stats, dat); + ret = nilfs_btree_prepare_delete(btree, path, &level, &stats, dat, + deform); if (ret < 0) goto out; nilfs_btree_commit_delete(btree, path, level, dat); diff --git a/fs/nilfs2/direct.c b/fs/nilfs2/direct.c index 8bd0b1374e25f8..b8643d3aa2f88a 100644 --- a/fs/nilfs2/direct.c +++ b/fs/nilfs2/direct.c @@ -144,7 +144,7 @@ static int nilfs_direct_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) return ret; } -static int nilfs_direct_delete(struct nilfs_bmap *bmap, __u64 key) +static int nilfs_direct_delete(struct nilfs_bmap *bmap, __u64 key, bool deform) { union nilfs_bmap_ptr_req req; struct inode *dat; @@ -234,7 +234,7 @@ int nilfs_direct_delete_and_convert(struct nilfs_bmap *bmap, /* no need to allocate any resource for conversion */ /* delete */ - ret = bmap->b_ops->bop_delete(bmap, key); + ret = bmap->b_ops->bop_delete(bmap, key, true); if (ret < 0) return ret; diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index a3555bebad63e2..b05b6d3abb870e 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -600,8 +600,7 @@ static struct fanotify_event *fanotify_alloc_perm_event(const void *data, pevent->hdr.len = 0; pevent->state = FAN_EVENT_INIT; pevent->path = *path; - /* NULL ppos means no range info */ - pevent->ppos = range ? &range->pos : NULL; + pevent->pos = range ? range->pos : FANOTIFY_NO_RANGE; pevent->count = range ? range->count : 0; path_get(path); diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index a0619e7694d571..3710543dbf8278 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -428,6 +428,8 @@ FANOTIFY_ME(struct fanotify_event *event) return container_of(event, struct fanotify_mnt_event, fae); } +#define FANOTIFY_NO_RANGE ((loff_t)-1) + /* * Structure for permission fanotify events. It gets allocated and freed in * fanotify_handle_event() since we wait there for user response. When the @@ -438,7 +440,7 @@ FANOTIFY_ME(struct fanotify_event *event) struct fanotify_perm_event { struct fanotify_event fae; struct path path; - const loff_t *ppos; /* optional file range info */ + loff_t pos; /* FANOTIFY_NO_RANGE if unavailable */ size_t count; u32 response; /* userspace answer to the event */ unsigned short state; /* state of the event */ @@ -468,7 +470,7 @@ static inline bool fanotify_event_has_access_range(struct fanotify_event *event) if (!(event->mask & FANOTIFY_PRE_CONTENT_EVENTS)) return false; - return FANOTIFY_PERM(event)->ppos; + return FANOTIFY_PERM(event)->pos != FANOTIFY_NO_RANGE; } static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index b604e3da58ad2e..7278286f5a6d96 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -675,12 +675,9 @@ static size_t copy_range_info_to_user(struct fanotify_event *event, if (WARN_ON_ONCE(info_len > count)) return -EFAULT; - if (WARN_ON_ONCE(!pevent->ppos)) - return -EINVAL; - info.hdr.info_type = FAN_EVENT_INFO_TYPE_RANGE; info.hdr.len = info_len; - info.offset = *(pevent->ppos); + info.offset = pevent->pos; info.count = pevent->count; if (copy_to_user(buf, &info, info_len)) @@ -1316,16 +1313,18 @@ static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark, static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, __u32 mask, unsigned int fan_flags) { + __u32 old_mask; bool recalc; spin_lock(&fsn_mark->lock); - if (!(fan_flags & FANOTIFY_MARK_IGNORE_BITS)) + if (!(fan_flags & FANOTIFY_MARK_IGNORE_BITS)) { + old_mask = fsn_mark->mask; fsn_mark->mask |= mask; - else + recalc = old_mask != fsn_mark->mask; + } else { fsn_mark->ignore_mask |= mask; - - recalc = fsnotify_calc_mask(fsn_mark) & - ~fsnotify_conn_mask(fsn_mark->connector); + recalc = true; + } recalc |= fanotify_mark_update_flags(fsn_mark, fan_flags); spin_unlock(&fsn_mark->lock); diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index ed37491c161897..5f19c24ec187ff 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -539,7 +539,6 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, { struct fsnotify_mark *fsn_mark; struct inotify_inode_mark *i_mark; - __u32 old_mask, new_mask; int replace = !(arg & IN_MASK_ADD); int create = (arg & IN_MASK_CREATE); int ret; @@ -555,27 +554,15 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); spin_lock(&fsn_mark->lock); - old_mask = fsn_mark->mask; if (replace) { fsn_mark->mask = 0; fsn_mark->flags &= ~INOTIFY_MARK_FLAGS; } fsn_mark->mask |= inotify_arg_to_mask(inode, arg); fsn_mark->flags |= inotify_arg_to_flags(arg); - new_mask = fsn_mark->mask; spin_unlock(&fsn_mark->lock); - if (old_mask != new_mask) { - /* more bits in old than in new? */ - int dropped = (old_mask & ~new_mask); - /* more bits in this fsn_mark than the inode's mask? */ - int do_inode = (new_mask & ~READ_ONCE(inode->i_fsnotify_mask)); - - /* update the inode with this new fsn_mark */ - if (dropped || do_inode) - fsnotify_recalc_mask(fsn_mark->connector); - - } + fsnotify_recalc_mask(fsn_mark->connector); /* return the wd */ ret = i_mark->wd; diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 58f32aac5f6146..0c147b99d4eea2 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -693,6 +693,8 @@ static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a) u32 attr_len; u32 min_len; u16 mp_offset; + u16 name_offset; + u32 name_end; attr_len = le32_to_cpu(a->length); min_len = offsetof(struct attr_record, data.non_resident.initialized_size) + @@ -701,7 +703,27 @@ static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a) return false; mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset); - return mp_offset >= min_len && mp_offset <= attr_len; + if (mp_offset < min_len || mp_offset > attr_len) + return false; + + if (a->name_length) { + name_offset = le16_to_cpu(a->name_offset); + + if (name_offset < min_len || name_offset >= attr_len) + return false; + + name_end = name_offset + a->name_length * sizeof(__le16); + if (name_end > attr_len || name_end > mp_offset) + return false; + } + + /* Ensure there's room for the compressed_size field if needed. */ + if (!(a->flags & (ATTR_IS_SPARSE | ATTR_COMPRESSION_MASK)) && + attr_len - mp_offset < + sizeof(a->data.non_resident.compressed_size)) + return false; + + return true; } static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol, diff --git a/fs/ntfs/lcnalloc.c b/fs/ntfs/lcnalloc.c index 835a041023a2ac..aa2e017a43844b 100644 --- a/fs/ntfs/lcnalloc.c +++ b/fs/ntfs/lcnalloc.c @@ -298,7 +298,12 @@ struct runlist_element *ntfs_cluster_alloc(struct ntfs_volume *vol, const s64 st clusters = count; rlpos = rlsize = 0; mapping = lcnbmp_vi->i_mapping; - i_size = i_size_read(lcnbmp_vi); + /* + * lcn_empty_bits_per_page is sized from nr_clusters, but $Bitmap can + * cover more clusters than that; bound the scan by the array. + */ + i_size = min_t(s64, i_size_read(lcnbmp_vi), + ((s64)vol->nr_clusters + 7) >> 3); while (1) { ntfs_debug("Start of outer while loop: done_zones 0x%x, search_zone %i, pass %i, zone_start 0x%llx, zone_end 0x%llx, bmp_initial_pos 0x%llx, bmp_pos 0x%llx, rlpos %i, rlsize %i.", done_zones, search_zone, pass, diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c index 8e0fd400e7f789..00373e450ea7ac 100644 --- a/fs/ntfs/runlist.c +++ b/fs/ntfs/runlist.c @@ -772,6 +772,25 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume * return ERR_PTR(-EIO); } + /* + * An empty mapping-pairs array is valid only for a zero-length + * attribute. + */ + if (!*buf && + (vcn || + le64_to_cpu(attr->data.non_resident.highest_vcn) != + (u64)(vcn - 1) || + le64_to_cpu(attr->data.non_resident.allocated_size) || + le64_to_cpu(attr->data.non_resident.data_size) || + le64_to_cpu(attr->data.non_resident.initialized_size))) { + ntfs_error(vol->sb, "Invalid empty mapping pairs array."); + return ERR_PTR(-EIO); + } + if (!vcn && !*buf && old_runlist && old_runlist->rl) { + *new_rl_count = old_runlist->count; + return old_runlist->rl; + } + /* Current position in runlist array. */ rlpos = 0; /* Allocate first page and set current runlist size to one page. */ @@ -884,12 +903,41 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume * ntfs_error(vol->sb, "lcn == -1"); } #endif + /* Check lcn is within the volume. */ + if (unlikely(lcn >= (s64)vol->nr_clusters)) { + ntfs_error(vol->sb, + "LCN >= nr_clusters in mapping pairs array."); + goto err_out; + } + /* Check lcn is not below -1. */ if (unlikely(lcn < -1)) { ntfs_error(vol->sb, "Invalid s64 < -1 in mapping pairs array."); goto err_out; } + if (lcn >= 0) { + s64 run_end; + + /* + * Ensure that the run stays within the volume. + * A valid starting LCN is not sufficient because + * the run length comes from disk. + */ + if (unlikely(check_add_overflow(lcn, + rl[rlpos].length, + &run_end))) { + ntfs_error(vol->sb, + "Run length overflow in mapping pairs array."); + goto err_out; + } + if (unlikely(run_end > (s64)vol->nr_clusters)) { + ntfs_error(vol->sb, + "Run extends beyond volume boundary."); + goto err_out; + } + } + /* chkdsk accepts zero-sized runs only for holes */ if ((lcn != -1) && !rl[rlpos].length) { ntfs_error(vol->sb, diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 8abe7bee4c0d9b..cd8fa2c133700c 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -645,7 +645,7 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol, { unsigned int sectors_per_cluster, sectors_per_cluster_bits, nr_hidden_sects; int clusters_per_mft_record, clusters_per_index_record; - s64 ll; + u64 ll; vol->sector_size = le16_to_cpu(b->bpb.bytes_per_sector); vol->sector_size_bits = ffs(vol->sector_size) - 1; @@ -755,23 +755,23 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol, * the same as it is much faster on 32-bit CPUs. */ ll = le64_to_cpu(b->number_of_sectors) >> sectors_per_cluster_bits; - if ((u64)ll >= 1ULL << 32) { + if (ll >= 1ULL << 32) { ntfs_error(vol->sb, "Cannot handle 64-bit clusters."); return false; } vol->nr_clusters = ll; ntfs_debug("vol->nr_clusters = 0x%llx", vol->nr_clusters); ll = le64_to_cpu(b->mft_lcn); - if (ll >= vol->nr_clusters) { - ntfs_error(vol->sb, "MFT LCN (%lli, 0x%llx) is beyond end of volume. Weird.", + if (ll >= (u64)vol->nr_clusters) { + ntfs_error(vol->sb, "MFT LCN (%llu, 0x%llx) is beyond end of volume. Weird.", ll, ll); return false; } vol->mft_lcn = ll; ntfs_debug("vol->mft_lcn = 0x%llx", vol->mft_lcn); ll = le64_to_cpu(b->mftmirr_lcn); - if (ll >= vol->nr_clusters) { - ntfs_error(vol->sb, "MFTMirr LCN (%lli, 0x%llx) is beyond end of volume. Weird.", + if (ll >= (u64)vol->nr_clusters) { + ntfs_error(vol->sb, "MFTMirr LCN (%llu, 0x%llx) is beyond end of volume. Weird.", ll, ll); return false; } diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 2b49bc0775582e..2c18cc1b4fb74e 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -768,10 +768,23 @@ int ni_create_attr_list(struct ntfs_inode *ni) rs = sbi->record_size; /* - * Skip estimating exact memory requirement. - * Looks like one record_size is always enough. + * Compute the exact size of the attribute list. Each attribute in the + * record yields one ATTR_LIST_ENTRY of le_size(name_len) bytes. The + * minimum on-disk attribute is SIZEOF_RESIDENT (0x18) bytes, but an + * unnamed one expands to le_size(0) (0x20) here, so a record crafted + * with many such attributes needs more than a single record_size; the + * previous fixed kzalloc(record_size) could therefore be overflowed by + * an attacker-controlled record. */ - le = kzalloc(al_aligned(rs), GFP_NOFS); + lsize = 0; + attr = NULL; + while ((attr = mi_enum_attr(ni, &ni->mi, attr))) + lsize += le_size(attr->name_len); + + if (!lsize) + return -EINVAL; + + le = kzalloc(al_aligned(lsize), GFP_NOFS); if (!le) return -ENOMEM; @@ -781,7 +794,6 @@ int ni_create_attr_list(struct ntfs_inode *ni) attr = NULL; nb = 0; free_b = 0; - attr = NULL; for (; (attr = mi_enum_attr(ni, &ni->mi, attr)); le = Add2Ptr(le, sz)) { sz = le_size(attr->name_len); @@ -2443,6 +2455,15 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, err = unc_size; else if (!unc_size || unc_size > frame_size) err = -EINVAL; + else if (unc_size < frame_size) { + /* + * Partial decompress: zero the [unc_size, frame_size) + * tail. decompress_lznt() leaves it untouched, so + * without this the freshly vmapped pages would expose + * uninitialized kernel memory to userspace. + */ + memset(frame_mem + unc_size, 0, frame_size - unc_size); + } } if (!err && valid_size < frame_vbo + frame_size) { size_t ok = valid_size - frame_vbo; diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index f038c799e7ac49..c759841b74309a 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -647,6 +647,14 @@ static inline void *enum_rstbl(struct RESTART_TABLE *t, void *c) return NULL; } +/* + * dp_range_ok - true if [j, j + count) fits in a page_lcns[cap] array. + */ +static inline bool dp_range_ok(size_t j, u32 count, u32 cap) +{ + return j < cap && count <= cap - j; +} + /* * find_dp - Search for a @vcn in Dirty Page Table. */ @@ -789,6 +797,20 @@ static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes) return true; } +static bool check_dp_table(const struct RESTART_TABLE *dptbl) +{ + u32 rsize = le16_to_cpu(dptbl->size); + struct DIR_PAGE_ENTRY *dp = NULL; + + while ((dp = enum_rstbl((struct RESTART_TABLE *)dptbl, dp))) { + if (struct_size(dp, page_lcns, le32_to_cpu(dp->lcns_follow)) > + rsize) + return false; + } + + return true; +} + /* * free_rsttbl_idx - Free a previously allocated index a Restart Table. */ @@ -4295,6 +4317,11 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) goto out; } + if (!check_dp_table(rt)) { + err = -EINVAL; + goto out; + } + dptbl = kmemdup(rt, t32, GFP_NOFS); if (!dptbl) { err = -ENOMEM; @@ -5087,6 +5114,13 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) /* Shorten length by any Lcns which were deleted. */ saved_len = dlen; + if (!dp_range_ok(le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn), + le16_to_cpu(lrh->lcns_follow), + le32_to_cpu(dp->lcns_follow))) { + err = -EINVAL; + goto out; + } + for (i = le16_to_cpu(lrh->lcns_follow); i; i--) { size_t j; u32 alen, voff; diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index d12784aaaa4b10..ea68dc0887f623 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -43,6 +43,14 @@ static DECLARE_RWSEM(o2hb_callback_sem); * whenever any of the threads sees activity from the node in its region. */ static DEFINE_SPINLOCK(o2hb_live_lock); +/* + * Serializes region pin/unpin dependency management (o2hb_dependent_users + * and the o2nm_depend_item()/o2nm_undepend_item() calls). o2hb_region_pin() + * has to drop o2hb_live_lock across the sleeping o2nm_depend_item(), so the + * spinlock alone can no longer keep pin and unpin mutually exclusive; this + * mutex, taken outside o2hb_live_lock, does. + */ +static DEFINE_MUTEX(o2hb_dependency_mutex); static struct list_head o2hb_live_slots[O2NM_MAX_NODES]; static unsigned long o2hb_live_node_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)]; static LIST_HEAD(o2hb_node_events); @@ -138,7 +146,7 @@ static unsigned int o2hb_dependent_users; * In global heartbeat mode, we pin/unpin all o2hb regions. This solution * works for both file system and userdlm domains. */ -static int o2hb_region_pin(const char *region_uuid); +static int o2hb_region_pin(const char *region_uuid, bool from_callback); static void o2hb_region_unpin(const char *region_uuid); /* Only sets a new threshold if there are no active regions. @@ -2142,6 +2150,7 @@ static void o2hb_heartbeat_group_drop_item(struct config_group *group, * If global heartbeat active and there are dependent users, * pin all regions if quorum region count <= CUT_OFF */ + mutex_lock(&o2hb_dependency_mutex); spin_lock(&o2hb_live_lock); if (!o2hb_dependent_users) @@ -2149,10 +2158,11 @@ static void o2hb_heartbeat_group_drop_item(struct config_group *group, if (bitmap_weight(o2hb_quorum_region_bitmap, O2NM_MAX_REGIONS) <= O2HB_PIN_CUT_OFF) - o2hb_region_pin(NULL); + o2hb_region_pin(NULL, true); unlock: spin_unlock(&o2hb_live_lock); + mutex_unlock(&o2hb_dependency_mutex); } static ssize_t o2hb_heartbeat_group_dead_threshold_show(struct config_item *item, @@ -2290,48 +2300,113 @@ EXPORT_SYMBOL_GPL(o2hb_setup_callback); * In local, we only pin the matching region. In global we pin all the active * regions. */ -static int o2hb_region_pin(const char *region_uuid) +static int o2hb_region_pin(const char *region_uuid, bool from_callback) { - int ret = 0, found = 0; - struct o2hb_region *reg; + int ret = 0, found; + struct o2hb_region *reg, *pinned; char *uuid; assert_spin_locked(&o2hb_live_lock); - list_for_each_entry(reg, &o2hb_all_regions, hr_all_item) { - if (reg->hr_item_dropped) - continue; + do { + found = 0; + pinned = NULL; - uuid = config_item_name(®->hr_item); + list_for_each_entry(reg, &o2hb_all_regions, hr_all_item) { + if (reg->hr_item_dropped) + continue; - /* local heartbeat */ - if (region_uuid) { - if (strcmp(region_uuid, uuid)) + uuid = config_item_name(®->hr_item); + + /* local heartbeat */ + if (region_uuid) { + if (strcmp(region_uuid, uuid)) + continue; + found = 1; + } + + if (reg->hr_item_pinned || reg->hr_item_dropped) { + if (found) + break; continue; - found = 1; + } + + /* + * Found a region that needs pinning. Take a reference + * so it stays alive while we drop the lock below. + */ + pinned = reg; + config_item_get(®->hr_item); + break; } - if (reg->hr_item_pinned || reg->hr_item_dropped) - goto skip_pin; + if (!pinned) + break; + + uuid = config_item_name(&pinned->hr_item); + + /* + * o2nm_depend_item() -> configfs_depend_item() can sleep (it + * takes the configfs root inode rwsem), so it must not run + * under o2hb_live_lock. Drop the lock across it; @pinned is + * kept alive by the reference taken above. The region list may + * change while unlocked, so we rescan from the top afterwards. + */ + spin_unlock(&o2hb_live_lock); /* Ignore ENOENT only for local hb (userdlm domain) */ - ret = o2nm_depend_item(®->hr_item); + if (from_callback) + ret = o2nm_depend_item_unlocked(&pinned->hr_item); + else + ret = o2nm_depend_item(&pinned->hr_item); + + spin_lock(&o2hb_live_lock); if (!ret) { - mlog(ML_CLUSTER, "Pin region %s\n", uuid); - reg->hr_item_pinned = 1; - } else { - if (ret == -ENOENT && found) - ret = 0; - else { - mlog(ML_ERROR, "Pin region %s fails with %d\n", - uuid, ret); + /* + * o2hb_live_lock was dropped across o2nm_depend_item(). + * o2hb_set_quorum_device() runs in the heartbeat thread + * without o2hb_dependency_mutex, so for global heartbeat + * it may have crossed O2HB_PIN_CUT_OFF and unpinned the + * regions while we slept. If that happened this pin is + * no longer wanted; undo it and stop rather than + * resurrecting it on the rescan below. + */ + if (!region_uuid && + bitmap_weight(o2hb_quorum_region_bitmap, + O2NM_MAX_REGIONS) > O2HB_PIN_CUT_OFF) { + o2nm_undepend_item(&pinned->hr_item); + spin_unlock(&o2hb_live_lock); + config_item_put(&pinned->hr_item); + spin_lock(&o2hb_live_lock); break; } + mlog(ML_CLUSTER, "Pin region %s\n", uuid); + pinned->hr_item_pinned = 1; + } else if (ret == -ENOENT && (found || !region_uuid)) { + /* + * For local hb (found): ignore ENOENT from userdlm + * domains as before. For global hb (!region_uuid): + * the region may have been detached from configfs + * while the lock was dropped — skip it and continue + * pinning the remaining regions. + */ + ret = 0; + } else { + mlog(ML_ERROR, "Pin region %s fails with %d\n", + uuid, ret); } -skip_pin: - if (found) - break; - } + + /* + * config_item_put() may drop the last reference and run + * o2hb_region_release(), which also grabs o2hb_live_lock and + * can sleep, so it must happen with the lock released. + */ + spin_unlock(&o2hb_live_lock); + config_item_put(&pinned->hr_item); + spin_lock(&o2hb_live_lock); + + /* local hb pins a single matching region */ + } while (!ret && !region_uuid); return ret; } @@ -2376,12 +2451,13 @@ static int o2hb_region_inc_user(const char *region_uuid) { int ret = 0; + mutex_lock(&o2hb_dependency_mutex); spin_lock(&o2hb_live_lock); /* local heartbeat */ if (!o2hb_global_heartbeat_active()) { - ret = o2hb_region_pin(region_uuid); - goto unlock; + ret = o2hb_region_pin(region_uuid, false); + goto unlock; } /* @@ -2393,16 +2469,23 @@ static int o2hb_region_inc_user(const char *region_uuid) goto unlock; if (bitmap_weight(o2hb_quorum_region_bitmap, - O2NM_MAX_REGIONS) <= O2HB_PIN_CUT_OFF) - ret = o2hb_region_pin(NULL); + O2NM_MAX_REGIONS) <= O2HB_PIN_CUT_OFF) { + ret = o2hb_region_pin(NULL, false); + if (ret) { + o2hb_region_unpin(NULL); + o2hb_dependent_users--; + } + } unlock: spin_unlock(&o2hb_live_lock); + mutex_unlock(&o2hb_dependency_mutex); return ret; } static void o2hb_region_dec_user(const char *region_uuid) { + mutex_lock(&o2hb_dependency_mutex); spin_lock(&o2hb_live_lock); /* local heartbeat */ @@ -2421,6 +2504,7 @@ static void o2hb_region_dec_user(const char *region_uuid) unlock: spin_unlock(&o2hb_live_lock); + mutex_unlock(&o2hb_dependency_mutex); } int o2hb_register_callback(const char *region_uuid, diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index 40256315455089..21154e4baddf4d 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -777,6 +777,12 @@ int o2nm_depend_item(struct config_item *item) return configfs_depend_item(&o2nm_cluster_group.cs_subsys, item); } +int o2nm_depend_item_unlocked(struct config_item *item) +{ + return configfs_depend_item_unlocked(&o2nm_cluster_group.cs_subsys, + item); +} + void o2nm_undepend_item(struct config_item *item) { configfs_undepend_item(item); diff --git a/fs/ocfs2/cluster/nodemanager.h b/fs/ocfs2/cluster/nodemanager.h index 3490e77a952d54..2f72f56996bd9e 100644 --- a/fs/ocfs2/cluster/nodemanager.h +++ b/fs/ocfs2/cluster/nodemanager.h @@ -64,6 +64,7 @@ void o2nm_node_get(struct o2nm_node *node); void o2nm_node_put(struct o2nm_node *node); int o2nm_depend_item(struct config_item *item); +int o2nm_depend_item_unlocked(struct config_item *item); void o2nm_undepend_item(struct config_item *item); int o2nm_depend_this_node(void); void o2nm_undepend_this_node(void); diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index d7fc3cccf2f4b4..5ee894966b12a9 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -625,6 +625,28 @@ static int ocfs2_validate_dx_root(struct super_block *sb, le16_to_cpu(el->l_count)); goto bail; } + } else { + struct ocfs2_dx_entry_list *dl_list = &dx_root->dr_entries; + + if (le16_to_cpu(dl_list->de_count) != + ocfs2_dx_entries_per_root(sb)) { + ret = ocfs2_error(sb, + "Dir Index Root # %llu has invalid de_count %u (expected %u)\n", + (unsigned long long)le64_to_cpu(dx_root->dr_blkno), + le16_to_cpu(dl_list->de_count), + ocfs2_dx_entries_per_root(sb)); + goto bail; + } + + if (le16_to_cpu(dl_list->de_num_used) > + le16_to_cpu(dl_list->de_count)) { + ret = ocfs2_error(sb, + "Dir Index Root # %llu has invalid de_num_used %u (de_count %u)\n", + (unsigned long long)le64_to_cpu(dx_root->dr_blkno), + le16_to_cpu(dl_list->de_num_used), + le16_to_cpu(dl_list->de_count)); + goto bail; + } } bail: @@ -664,10 +686,25 @@ static int ocfs2_validate_dx_leaf(struct super_block *sb, return ret; } - if (!OCFS2_IS_VALID_DX_LEAF(dx_leaf)) { - ret = ocfs2_error(sb, "Dir Index Leaf has bad signature %.*s\n", - 7, dx_leaf->dl_signature); - } + if (!OCFS2_IS_VALID_DX_LEAF(dx_leaf)) + return ocfs2_error(sb, "Dir Index Leaf has bad signature %.*s\n", + 7, dx_leaf->dl_signature); + + if (le16_to_cpu(dx_leaf->dl_list.de_count) != + ocfs2_dx_entries_per_leaf(sb)) + return ocfs2_error(sb, + "Dir Index Leaf # %llu has invalid de_count %u (expected %u)\n", + (unsigned long long)le64_to_cpu(dx_leaf->dl_blkno), + le16_to_cpu(dx_leaf->dl_list.de_count), + ocfs2_dx_entries_per_leaf(sb)); + + if (le16_to_cpu(dx_leaf->dl_list.de_num_used) > + le16_to_cpu(dx_leaf->dl_list.de_count)) + return ocfs2_error(sb, + "Dir Index Leaf # %llu has invalid de_num_used %u (de_count %u)\n", + (unsigned long long)le64_to_cpu(dx_leaf->dl_blkno), + le16_to_cpu(dx_leaf->dl_list.de_num_used), + le16_to_cpu(dx_leaf->dl_list.de_count)); return ret; } @@ -1917,7 +1954,7 @@ static int ocfs2_dir_foreach_blk_el(struct inode *inode, i += le16_to_cpu(de->rec_len); } offset = i; - ctx->pos = (ctx->pos & ~(sb->s_blocksize - 1)) + ctx->pos = (ctx->pos & ~((loff_t)sb->s_blocksize - 1)) | offset; *f_version = inode_query_iversion(inode); } diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c index aee3b4c56dcc68..612969867ff924 100644 --- a/fs/ocfs2/dlm/dlmmaster.c +++ b/fs/ocfs2/dlm/dlmmaster.c @@ -3099,6 +3099,12 @@ int dlm_migrate_request_handler(struct o2net_msg *msg, u32 len, void *data, name = migrate->name; namelen = migrate->namelen; + if (namelen > DLM_LOCKID_NAME_MAX) { + mlog(ML_ERROR, "%s: invalid name length %u in migrate request\n", + dlm->name, namelen); + ret = -EINVAL; + goto leave; + } hash = dlm_lockid_hash(name, namelen); /* preallocate.. if this fails, abort */ diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index 9b97bf73df2289..9d4a2695b9594d 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -1357,6 +1357,15 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data, if (!dlm_grab(dlm)) return -EINVAL; + if (mres->lockname_len > DLM_LOCKID_NAME_MAX || + mres->num_locks > DLM_MAX_MIGRATABLE_LOCKS || + be16_to_cpu(msg->data_len) < struct_size(mres, ml, mres->num_locks)) { + mlog(ML_ERROR, "%s: invalid lockres migration message from %u\n", + dlm->name, mres->master); + dlm_put(dlm); + return -EINVAL; + } + if (!dlm_joined(dlm)) { mlog(ML_ERROR, "Domain %s not joined! " "lockres %.*s, master %u\n", diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 7323bde70caaf4..d9f22b4a265461 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -116,6 +116,33 @@ static int ocfs2_validate_refcount_block(struct super_block *sb, le32_to_cpu(rb->rf_fs_generation)); goto out; } + + /* + * rf_records (rl_count/rl_used/rl_recs[]) is only meaningful when + * this block is not an interior tree block (OCFS2_REFCOUNT_TREE_FL); + * in that case the same union bytes hold an extent list (rf_list) + * instead, which is validated by ocfs2_validate_extent_block(). + */ + if (!(le32_to_cpu(rb->rf_flags) & OCFS2_REFCOUNT_TREE_FL)) { + if (le16_to_cpu(rb->rf_records.rl_count) != + ocfs2_refcount_recs_per_rb(sb)) { + rc = ocfs2_error(sb, + "Refcount block #%llu has an invalid rl_count of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(rb->rf_records.rl_count)); + goto out; + } + + if (le16_to_cpu(rb->rf_records.rl_used) > + le16_to_cpu(rb->rf_records.rl_count)) { + rc = ocfs2_error(sb, + "Refcount block #%llu has an invalid rl_used of %u (rl_count %u)\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(rb->rf_records.rl_used), + le16_to_cpu(rb->rf_records.rl_count)); + goto out; + } + } out: return rc; } @@ -3360,10 +3387,9 @@ static int ocfs2_replace_cow(struct ocfs2_cow_context *context) cow_start += num_clusters; } - if (ocfs2_dealloc_has_cluster(&context->dealloc)) { + if (ocfs2_dealloc_has_cluster(&context->dealloc)) ocfs2_schedule_truncate_log_flush(osb, 1); - ocfs2_run_deallocs(osb, &context->dealloc); - } + ocfs2_run_deallocs(osb, &context->dealloc); return ret; } @@ -3846,10 +3872,9 @@ static int ocfs2_attach_refcount_tree(struct inode *inode, ocfs2_unlock_refcount_tree(osb, ref_tree, 1); brelse(ref_root_bh); - if (!ret && ocfs2_dealloc_has_cluster(&dealloc)) { + if (!ret && ocfs2_dealloc_has_cluster(&dealloc)) ocfs2_schedule_truncate_log_flush(osb, 1); - ocfs2_run_deallocs(osb, &dealloc); - } + ocfs2_run_deallocs(osb, &dealloc); out: /* * Empty the extent map so that we may get the right extent @@ -4135,10 +4160,9 @@ static int ocfs2_create_reflink_node(struct inode *s_inode, ocfs2_unlock_refcount_tree(osb, ref_tree, 1); brelse(ref_root_bh); out: - if (ocfs2_dealloc_has_cluster(&dealloc)) { + if (ocfs2_dealloc_has_cluster(&dealloc)) ocfs2_schedule_truncate_log_flush(osb, 1); - ocfs2_run_deallocs(osb, &dealloc); - } + ocfs2_run_deallocs(osb, &dealloc); return ret; } @@ -4691,10 +4715,9 @@ loff_t ocfs2_reflink_remap_blocks(struct inode *s_inode, } out: - if (ocfs2_dealloc_has_cluster(&dealloc)) { + if (ocfs2_dealloc_has_cluster(&dealloc)) ocfs2_schedule_truncate_log_flush(osb, 1); - ocfs2_run_deallocs(osb, &dealloc); - } + ocfs2_run_deallocs(osb, &dealloc); return ret; } diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index a4a2b87a45fe3b..20c3aec6b9873c 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -2759,7 +2759,7 @@ static int _ocfs2_reclaim_suballoc_to_main(handle_t *handle, fe->i_clusters = cpu_to_le32(tmp_used - le16_to_cpu(cl->cl_cpg)); spin_lock(&OCFS2_I(alloc_inode)->ip_lock); - OCFS2_I(alloc_inode)->ip_clusters -= le32_to_cpu(fe->i_clusters); + OCFS2_I(alloc_inode)->ip_clusters = le32_to_cpu(fe->i_clusters); fe->i_size = cpu_to_le64(ocfs2_clusters_to_bytes(alloc_inode->i_sb, le32_to_cpu(fe->i_clusters))); spin_unlock(&OCFS2_I(alloc_inode)->ip_lock); diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 5989351aff935c..f1b3aaab6a7140 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -7241,10 +7241,9 @@ int ocfs2_reflink_xattrs(struct inode *old_inode, ref_tree, 1); brelse(ref_root_bh); - if (ocfs2_dealloc_has_cluster(&dealloc)) { + if (ocfs2_dealloc_has_cluster(&dealloc)) ocfs2_schedule_truncate_log_flush(OCFS2_SB(old_inode->i_sb), 1); - ocfs2_run_deallocs(OCFS2_SB(old_inode->i_sb), &dealloc); - } + ocfs2_run_deallocs(OCFS2_SB(old_inode->i_sb), &dealloc); out: return ret; diff --git a/fs/orangefs/devorangefs-req.c b/fs/orangefs/devorangefs-req.c index 33ee8cb32f8314..e5c60da7e6773c 100644 --- a/fs/orangefs/devorangefs-req.c +++ b/fs/orangefs/devorangefs-req.c @@ -474,6 +474,7 @@ static ssize_t orangefs_devreq_write_iter(struct kiocb *iocb, op->downcall.trailer_size, iter)) { gossip_err("%s: failed to copy trailer.\n", __func__); vfree(op->downcall.trailer_buf); + op->downcall.trailer_buf = NULL; goto Efault; } diff --git a/fs/orangefs/orangefs-debugfs.c b/fs/orangefs/orangefs-debugfs.c index 69bd73a2b5564d..9f94919a6bc626 100644 --- a/fs/orangefs/orangefs-debugfs.c +++ b/fs/orangefs/orangefs-debugfs.c @@ -569,6 +569,7 @@ static int orangefs_prepare_cdm_array(char *debug_array_string) cds_delimiter = strchr(cds_head, '\n'); *cds_delimiter = '\0'; + cds_head = skip_spaces(cds_head); keyword_len = strcspn(cds_head, " "); cdm_array[i].keyword = kzalloc(keyword_len + 1, GFP_KERNEL); diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index a033743dbf5159..a8d47b1e644b87 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -188,7 +188,6 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent, if (!err && ofs->casefold != ovl_dentry_casefolded(newdentry)) { pr_warn_ratelimited("wrong inherited casefold (%pd2)\n", newdentry); - end_creating(newdentry); err = -EINVAL; } break; diff --git a/fs/pidfs.c b/fs/pidfs.c index b57ecc96e96728..a6a643f15d08a9 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -532,6 +532,7 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct task_struct *task __free(put_task) = NULL; struct nsproxy *nsp __free(put_nsproxy) = NULL; struct ns_common *ns_common = NULL; + int error; if (!pidfs_ioctl_valid(cmd)) return -ENOIOCTLCMD; @@ -555,20 +556,33 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (arg) return -EINVAL; + /* + * We're trying to open a file descriptor to the namespace so perform a + * filesystem cred ptrace check. Hold @task's exec_update_lock for the + * duration of the ptrace check and the namespace lookup so that the + * credentials used for the access decision match those of @task at the + * time its namespace is read, preventing a concurrent execve() from + * swapping the task's credentials in between the check and the use. We + * mirror nsfs behavior. + */ + error = down_read_killable(&task->signal->exec_update_lock); + if (error) + return error; + + if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) { + error = -EACCES; + goto out_unlock; + } + scoped_guard(task_lock, task) { nsp = task->nsproxy; if (nsp) get_nsproxy(nsp); } - if (!nsp) - return -ESRCH; /* just pretend it didn't exist */ - - /* - * We're trying to open a file descriptor to the namespace so perform a - * filesystem cred ptrace check. Also, we mirror nsfs behavior. - */ - if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) - return -EACCES; + if (!nsp) { + error = -ESRCH; /* just pretend it didn't exist */ + goto out_unlock; + } switch (cmd) { /* Namespaces that hang of nsproxy. */ @@ -650,11 +664,16 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) #endif break; default: - return -ENOIOCTLCMD; + error = -ENOIOCTLCMD; } - if (!ns_common) - return -EOPNOTSUPP; + if (!error && !ns_common) + error = -EOPNOTSUPP; + +out_unlock: + up_read(&task->signal->exec_update_lock); + if (error) + return error; /* open_namespace() unconditionally consumes the reference */ return open_namespace(ns_common); diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index a1dacc7d8f74b6..cb3a397b109ce2 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -1413,8 +1413,19 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, */ lock_two_nondirectories(target_inode, src_inode); - if (len == 0) - len = src_inode->i_size - off; + if (len == 0) { + loff_t src_size = i_size_read(src_inode); + + if (off > src_size) { + rc = -EINVAL; + goto unlock; + } + len = src_size - off; + if (!len) { + rc = 0; + goto unlock; + } + } cifs_dbg(FYI, "clone range\n"); diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h index 854e672a4e37a5..651670c19c2b20 100644 --- a/fs/smb/client/cifsfs.h +++ b/fs/smb/client/cifsfs.h @@ -147,6 +147,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, struct file *src_file, long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg); void cifs_setsize(struct inode *inode, loff_t offset); +void cifs_resize_file_locked(struct inode *inode, loff_t offset); struct fs_context; struct smb3_fs_context; diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index 1f77512252e772..f5aad5f61dce10 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -615,6 +615,11 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, tcon->tid = smb_buffer_response->Tid; bcc_ptr = pByteArea(smb_buffer_response); bytes_left = get_bcc(smb_buffer_response); + if (bytes_left < 2) { + rc = smb_EIO2(smb_eio_trace_tcon_bcc_too_small, + bytes_left, 2); + goto out; + } length = strnlen(bcc_ptr, bytes_left - 2); if (smb_buffer->Flags2 & SMBFLG2_UNICODE) is_unicode = true; @@ -670,6 +675,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, reset_cifs_unix_caps(xid, tcon, NULL, NULL); } } +out: cifs_buf_release(smb_buffer); return rc; } diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c index 8cd93cd2f00f98..86dba25b7a5a80 100644 --- a/fs/smb/client/dfs_cache.c +++ b/fs/smb/client/dfs_cache.c @@ -122,6 +122,8 @@ static inline void free_tgts(struct cache_entry *ce) kfree(t->name); kfree(t); } + + WRITE_ONCE(ce->tgthint, NULL); } static inline void flush_cache_ent(struct cache_entry *ce) @@ -869,13 +871,22 @@ int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nl goto out_free_path; } - if (ref) - rc = setup_referral(path, ce, ref, get_tgt_name(ce)); - else + if (ref) { + char *target = get_tgt_name(ce); + + if (IS_ERR(target)) { + rc = PTR_ERR(target); + goto out_unlock; + } + rc = setup_referral(path, ce, ref, target); + } else { rc = 0; + } + if (!rc && tgt_list) rc = get_targets(ce, tgt_list); +out_unlock: up_read(&htable_rw_lock); out_free_path: @@ -915,10 +926,17 @@ int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, goto out_unlock; } - if (ref) - rc = setup_referral(path, ce, ref, get_tgt_name(ce)); - else + if (ref) { + char *target = get_tgt_name(ce); + + if (IS_ERR(target)) { + rc = PTR_ERR(target); + goto out_unlock; + } + rc = setup_referral(path, ce, ref, target); + } else { rc = 0; + } if (!rc && tgt_list) rc = get_targets(ce, tgt_list); @@ -959,7 +977,8 @@ void dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt t = READ_ONCE(ce->tgthint); - if (unlikely(!strcasecmp(it->it_name, t->name))) + /* Check 't' in case ce->tgthint was cleared by free_tgts() */ + if (t && unlikely(!strcasecmp(it->it_name, t->name))) goto out_unlock; list_for_each_entry(t, &ce->tlist, list) { diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index ac89c1ba56b1f4..389083f9ce003f 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -1016,6 +1016,7 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry) if (!rc) { netfs_resize_file(&cinode->netfs, 0, true); cifs_setsize(inode, 0); + cifs_invalidate_cache(inode, 0); } } if (cfile) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 0afff761aab9cb..aec8ed8881a6c3 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -3057,9 +3057,27 @@ void cifs_setsize(struct inode *inode, loff_t offset) inode->i_blocks = blocks; spin_unlock(&inode->i_lock); inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); + if (offset > old_size) + pagecache_isize_extended(inode, old_size, offset); truncate_pagecache(inode, offset); netfs_wait_for_outstanding_io(inode); - fscache_resize_cookie(cifs_inode_cookie(inode), offset); +} + +void cifs_resize_file_locked(struct inode *inode, loff_t offset) +{ + struct fscache_cookie *cookie = cifs_inode_cookie(inode); + + lockdep_assert_held_write(&inode->i_rwsem); + + netfs_resize_file(netfs_inode(inode), offset, true); + cifs_setsize(inode, offset); + + if (!cookie) + return; + + fscache_use_cookie(cookie, true); + fscache_resize_cookie(cookie, offset); + cifs_fscache_unuse_inode_cookie(inode, true); } int cifs_file_set_size(const unsigned int xid, struct dentry *dentry, @@ -3100,6 +3118,7 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry, size, false); cifs_dbg(FYI, "%s: set_file_size: rc = %d\n", __func__, rc); cifsFileInfo_put(open_file); + tcon = NULL; } } @@ -3125,10 +3144,8 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry, cifs_put_tlink(tlink); set_size_out: - if (rc == 0) { - netfs_resize_file(&cifsInode->netfs, size, true); - cifs_setsize(inode, size); - } + if (rc == 0) + cifs_resize_file_locked(inode, size); return rc; } @@ -3214,9 +3231,13 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) attrs->ia_valid &= ~(ATTR_CTIME | ATTR_MTIME); } - /* skip mode change if it's just for clearing setuid/setgid */ - if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) - attrs->ia_valid &= ~ATTR_MODE; + /* + * This function is only called when Unix extensions are in effect, + * so the mode is always sent to and stored on the server. Do not + * skip the mode change when clearing setuid/setgid bits: dropping + * ATTR_MODE here would leave those bits set on the server after a + * write, which is a security issue. + */ args = kmalloc_obj(*args); if (args == NULL) { @@ -3425,8 +3446,23 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) attrs->ia_valid &= ~(ATTR_UID | ATTR_GID); } - /* skip mode change if it's just for clearing setuid/setgid */ - if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) + /* + * Skip the mode change if it is only being done to clear the + * setuid/setgid bits *and* the mode is emulated via the DOS + * read-only attribute (the default, non-ACL case), which cannot + * represent the setuid/setgid bits anyway. + * + * When the mode is instead stored on the server - i.e. with the + * cifsacl or modefromsid mount options (via an ACL) or with the + * SMB3.1.1 POSIX extensions - the cleared mode must be pushed to + * the server. Dropping ATTR_MODE here would leave the setuid/ + * setgid bit set on the server after a write, which is a security + * issue (the bits are not stripped as they are on local + * filesystems). + */ + if ((attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) && + !((sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) || + cifs_sb_master_tcon(cifs_sb)->posix_extensions)) attrs->ia_valid &= ~ATTR_MODE; if (attrs->ia_valid & ATTR_MODE) { diff --git a/fs/smb/client/smb1misc.c b/fs/smb/client/smb1misc.c index ba56023010d84f..cdfbbff24b72ad 100644 --- a/fs/smb/client/smb1misc.c +++ b/fs/smb/client/smb1misc.c @@ -80,7 +80,8 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) (struct smb_com_transaction_change_notify_rsp *)buf; struct file_notify_information *pnotify; __u32 data_offset = 0; - size_t len = srv->total_read - srv->pdu_size; + /* total_read excludes the RFC1002 preamble */ + size_t len = srv->total_read; if (get_bcc(buf) > sizeof(struct file_notify_information)) { data_offset = le32_to_cpu(pSMBr->DataOffset); diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c index dc5a8c1da62316..954c1a8ef724a3 100644 --- a/fs/smb/client/smb1ops.c +++ b/fs/smb/client/smb1ops.c @@ -717,7 +717,7 @@ static int cifs_query_path_info(const unsigned int xid, ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_DEV_SIZE); memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1); data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 + - SMB2_WSL_XATTR_MODE_SIZE, 4); + SMB2_WSL_XATTR_DEV_SIZE, 4); rc = 0; } else if (rc >= 0) { /* It is an error if EA $LXDEV has wrong size. */ diff --git a/fs/smb/client/smb1transport.c b/fs/smb/client/smb1transport.c index 966f2cf83a5174..42e95cc1bd8924 100644 --- a/fs/smb/client/smb1transport.c +++ b/fs/smb/client/smb1transport.c @@ -375,12 +375,31 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len) data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); - /* validate target area */ data_area_of_src = (char *)&pSMBs->hdr.Protocol + get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); data_area_of_tgt += total_in_tgt; + /* + * DataOffset fields are server-supplied and not validated against + * buffer bounds; check both data pointers before mutating the + * target header. + */ + if (data_area_of_tgt < (char *)target_hdr + + sizeof(struct smb_t2_rsp) + sizeof(__le16) || + data_area_of_tgt + total_in_src > + (char *)target_hdr + CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { + cifs_dbg(VFS, "%s: target data area out of bounds\n", __func__); + return -EPROTO; + } + if (data_area_of_src < second_buf + + sizeof(struct smb_t2_rsp) + sizeof(__le16) || + data_area_of_src + total_in_src > + second_buf + smbCalcSize((struct smb_hdr *)second_buf)) { + cifs_dbg(VFS, "%s: secondary data area out of bounds\n", __func__); + return -EPROTO; + } + total_in_tgt += total_in_src; /* is the result too big for the field? */ if (total_in_tgt > USHRT_MAX) { @@ -430,10 +449,18 @@ bool cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, char *buf, int malformed) { - if (malformed) - return false; - if (check2ndT2(buf) <= 0) + if (malformed || check2ndT2(buf) <= 0) { + /* mid->multiRsp blocks the server buf detach in handle_mid(); + * returning false here would leak resp_buf and leave a dangling + * server->smallbuf/bigbuf after the user thread frees resp_buf. + */ + if (mid->multiRsp) { + mid->multiEnd = true; + dequeue_mid(server, mid, true); + return true; + } return false; + } mid->multiRsp = true; if (mid->resp_buf) { /* merge response - fix up 1st*/ diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c index f35b6488d810d0..fb2fccbe86677e 100644 --- a/fs/smb/client/smb2file.c +++ b/fs/smb/client/smb2file.c @@ -61,7 +61,10 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov) cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n", __func__, le32_to_cpu(p->ErrorId)); - len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8); + len = le32_to_cpu(p->ErrorDataLength); + if (len > end - ((u8 *)p + sizeof(*p))) + return ERR_PTR(-EINVAL); + len = ALIGN(len, 8); if (len > end - ((u8 *)p + sizeof(*p))) return ERR_PTR(-EINVAL); diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index 213bc298cdf22e..2946391bb992d0 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -40,9 +40,11 @@ static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov) buf = (struct reparse_data_buffer *)((u8 *)io + off); len = sizeof(*buf); - rdlen = le16_to_cpu(buf->ReparseDataLength); + if (count < len) + return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, 0)); - if (count < len || count < rdlen + len) + rdlen = le16_to_cpu(buf->ReparseDataLength); + if (count < rdlen + len) return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, rdlen)); return buf; } diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 192649fec25d54..0e872d58fae7ca 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -2222,8 +2222,7 @@ smb2_duplicate_extents(const unsigned int xid, rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false); if (rc) goto duplicate_extents_out; - netfs_resize_file(netfs_inode(inode), dest_off + len, true); - cifs_setsize(inode, dest_off + len); + cifs_resize_file_locked(inode, dest_off + len); } rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, trgtfile->fid.volatile_fid, @@ -3776,8 +3775,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, } new_eof = off + len; - netfs_resize_file(&cifsi->netfs, new_eof, true); - cifs_setsize(inode, new_eof); + cifs_resize_file_locked(inode, new_eof); qrc = SMB2_query_info(xid, tcon, cfile->fid.persistent_fid, @@ -3825,8 +3823,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, if (rc) goto out; - netfs_resize_file(&cifsi->netfs, new_eof, true); - cifs_setsize(inode, new_eof); + cifs_resize_file_locked(inode, new_eof); qrc = SMB2_query_info(xid, tcon, cfile->fid.persistent_fid, diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h index 5b21ad3c15fb51..12241abb8e2ebc 100644 --- a/fs/smb/client/trace.h +++ b/fs/smb/client/trace.h @@ -133,6 +133,7 @@ EM(smb_eio_trace_sym_slash, "sym_slash") \ EM(smb_eio_trace_sym_target_len, "sym_target_len") \ EM(smb_eio_trace_symlink_file_size, "symlink_file_size") \ + EM(smb_eio_trace_tcon_bcc_too_small, "tcon_bcc_too_small") \ EM(smb_eio_trace_tdis_in_reconnect, "tdis_in_reconnect") \ EM(smb_eio_trace_tx_chained_async, "tx_chained_async") \ EM(smb_eio_trace_tx_compress_failed, "tx_compress_failed") \ @@ -213,7 +214,7 @@ #define EM(a, b) a, #define E_(a, b) a -enum smb_eio_trace { smb_eio_traces } __mode(byte); +enum smb_eio_trace { smb_eio_traces }; enum smb3_rw_credits_trace { smb3_rw_credits_traces } __mode(byte); enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte); diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 79787099afdc68..32f2f8b219ba66 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -878,31 +878,69 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) ksmbd_conn_put(conn); } +/* + * Select and pin the connection used for an oplock break before doing any + * allocations which may sleep. The caller of oplock_break() holds a live + * reference on ci (a file being opened, a file being operated on, or an + * explicit ksmbd_inode_lookup_lock() reference in the parent lease break + * paths), so the inode cannot be freed during the call and its lock is + * reachable without dereferencing opinfo->o_fp, which is not pinned by + * the oplock reference and may be freed by a concurrent close. + * + * opinfo->conn is cleared under ci->m_lock by session_fd_check() when the + * durable handle owning the oplock is disconnected, reassigned by + * ksmbd_reopen_durable_fd() under the same lock, and the last + * ksmbd_conn_put() of the old connection frees it. Holding the read lock + * excludes both writers, so the connection cannot be freed while it is + * selected. + */ +static struct ksmbd_conn *smb2_oplock_break_conn_get(struct oplock_info *opinfo, + struct ksmbd_inode *ci) +{ + struct ksmbd_conn *conn; + + down_read(&ci->m_lock); + conn = READ_ONCE(opinfo->conn); + if (conn && !ksmbd_conn_releasing(conn)) + conn = ksmbd_conn_get(conn); + else + conn = NULL; + up_read(&ci->m_lock); + + return conn; +} + /** * smb2_oplock_break_noti() - send smb2 exclusive/batch to level2 oplock * break command from server to client * @opinfo: oplock info object + * @ci: inode owning the break target's oplock list, pinned by + * the caller * * Return: 0 on success, otherwise error */ -static int smb2_oplock_break_noti(struct oplock_info *opinfo) +static int smb2_oplock_break_noti(struct oplock_info *opinfo, + struct ksmbd_inode *ci) { struct ksmbd_conn *conn; struct oplock_break_info *br_info; int ret = 0; struct ksmbd_work *work; - conn = READ_ONCE(opinfo->conn); + conn = smb2_oplock_break_conn_get(opinfo, ci); if (!conn) return ksmbd_invalidate_durable_fd(opinfo->fid); work = ksmbd_alloc_work_struct(); - if (!work) + if (!work) { + ksmbd_conn_put(conn); return -ENOMEM; + } br_info = kmalloc_obj(struct oplock_break_info, KSMBD_DEFAULT_GFP); if (!br_info) { ksmbd_free_work_struct(work); + ksmbd_conn_put(conn); return -ENOMEM; } @@ -911,7 +949,8 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) br_info->open_trunc = opinfo->open_trunc; work->request_buf = (char *)br_info; - work->conn = ksmbd_conn_get(conn); + /* Transfer the reference acquired by smb2_oplock_break_conn_get(). */ + work->conn = conn; work->sess = opinfo->sess; ksmbd_conn_r_count_inc(conn); @@ -1074,9 +1113,9 @@ static void wait_lease_breaking(struct oplock_info *opinfo) } } -static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, - struct ksmbd_work *in_work, bool share_break) -{ +static int oplock_break(struct oplock_info *brk_opinfo, struct ksmbd_inode *ci, + int req_op_level, struct ksmbd_work *in_work, + bool share_break, bool sync_lease_break){ int err = 0; bool sent_interim = false; @@ -1196,7 +1235,7 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, brk_opinfo->op_state = OPLOCK_ACK_WAIT; } - err = smb2_oplock_break_noti(brk_opinfo); + err = smb2_oplock_break_noti(brk_opinfo, ci); ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); if (brk_opinfo->op_state == OPLOCK_CLOSING) @@ -1224,13 +1263,14 @@ static int oplock_break_add(struct list_head *head, struct oplock_info *opinfo) return 0; } -static void oplock_break_drain_none(struct list_head *head) +static void oplock_break_drain_none(struct list_head *head, + struct ksmbd_inode *ci) { struct oplock_break_entry *ent, *tmp; list_for_each_entry_safe(ent, tmp, head, list) { - oplock_break(ent->opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false); - list_del(&ent->list); + oplock_break(ent->opinfo, ci, SMB2_OPLOCK_LEVEL_NONE, NULL, + false, false); list_del(&ent->list); opinfo_put(ent->opinfo); kfree(ent); } @@ -1377,7 +1417,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, } up_read(&p_ci->m_lock); - oplock_break_drain_none(&brk_list); + oplock_break_drain_none(&brk_list, p_ci); ksmbd_inode_put(p_ci); } @@ -1421,7 +1461,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) } up_read(&p_ci->m_lock); - oplock_break_drain_none(&brk_list); + oplock_break_drain_none(&brk_list, p_ci); ksmbd_inode_put(p_ci); } @@ -1541,9 +1581,8 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, prev_durable_detached = prev_op_snapshot.durable_detached; prev_fid = prev_op_snapshot.fid; - err = oplock_break(prev_opinfo, break_level, work, - share_ret < 0 && prev_opinfo->is_lease); - if (prev_durable_detached || (prev_durable_open && err == -ENOENT)) + err = oplock_break(prev_opinfo, ci, break_level, work, + share_ret < 0 && prev_opinfo->is_lease, false); if (prev_durable_detached || (prev_durable_open && err == -ENOENT)) ksmbd_invalidate_durable_fd(prev_fid); opinfo_put(prev_opinfo); if (err == -EAGAIN) { @@ -1640,8 +1679,8 @@ static bool smb_break_all_write_oplock(struct ksmbd_work *work, } brk_opinfo->open_trunc = is_trunc; - oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work, false); - sent_break = true; + oplock_break(brk_opinfo, fp->f_ci, SMB2_OPLOCK_LEVEL_II, work, false, + false); sent_break = true; opinfo_put(brk_opinfo); return sent_break; @@ -1727,11 +1766,11 @@ static void __smb_break_all_levII_oplock(struct ksmbd_work *work, brk_op->level = SMB2_OPLOCK_LEVEL_NONE; brk_op->op_state = OPLOCK_STATE_NONE; } else { - oplock_break(brk_op, + oplock_break(brk_op, ci, brk_op->is_lease && !is_trunc ? SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE, send_interim && !sent_interim ? work : NULL, - false); + false, false); } sent_interim = true; list_del(&ent->list); diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 76f63f9adc7254..e02e254645e202 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -7226,13 +7226,18 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) } aux_payload_buf = - kvmalloc(rpc_resp->payload_sz, KSMBD_DEFAULT_GFP); + kvmalloc(ALIGN(rpc_resp->payload_sz, 8), + KSMBD_DEFAULT_GFP); if (!aux_payload_buf) { err = -ENOMEM; goto out; } memcpy(aux_payload_buf, rpc_resp->payload, rpc_resp->payload_sz); + if (rpc_resp->payload_sz & 7) + memset(aux_payload_buf + rpc_resp->payload_sz, 0, + ALIGN(rpc_resp->payload_sz, 8) - + rpc_resp->payload_sz); nbytes = rpc_resp->payload_sz; err = ksmbd_iov_pin_rsp_read(work, (void *)rsp, diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index a141025581afe2..9bd8dce660afa1 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -1722,7 +1722,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) down_write(&ci->m_lock); list_for_each_entry_rcu(op, &ci->m_op_list, op_entry, lockdep_is_held(&ci->m_lock)) { - if (op->conn) + if (op->conn || op->o_fp != fp) continue; op->conn = ksmbd_conn_get(fp->conn); op->sess = work->sess; diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index a524584353279f..9ce620a310169a 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -438,6 +438,8 @@ static inline struct eventfs_inode *init_ei(struct eventfs_inode *ei, const char if (!ei->name) return NULL; kref_init(&ei->kref); + INIT_LIST_HEAD(&ei->children); + INIT_LIST_HEAD(&ei->list); return ei; } @@ -725,8 +727,6 @@ struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode ei->entries = entries; ei->nr_entries = size; ei->data = data; - INIT_LIST_HEAD(&ei->children); - INIT_LIST_HEAD(&ei->list); scoped_guard(mutex, &eventfs_mutex) { if (!parent->is_freed) @@ -798,9 +798,6 @@ struct eventfs_inode *eventfs_create_events_dir(const char *name, struct dentry ei->attr.uid = uid; ei->attr.gid = gid; - INIT_LIST_HEAD(&ei->children); - INIT_LIST_HEAD(&ei->list); - ti = get_tracefs(inode); ti->flags |= TRACEFS_EVENT_INODE; ti->private = ei; diff --git a/fs/ubifs/auth.c b/fs/ubifs/auth.c index a4a0158f712d3d..1f770795ee70f3 100644 --- a/fs/ubifs/auth.c +++ b/fs/ubifs/auth.c @@ -217,7 +217,7 @@ int ubifs_sb_verify_signature(struct ubifs_info *c, signode = snod->node; - if (le32_to_cpu(signode->len) > snod->len + sizeof(struct ubifs_sig_node)) { + if (le32_to_cpu(signode->len) > snod->len - sizeof(struct ubifs_sig_node)) { ubifs_err(c, "invalid signature len %d", le32_to_cpu(signode->len)); err = -EINVAL; goto out_destroy; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 67bcf83758c8fb..c519644acfd7de 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -531,7 +531,7 @@ static int udf_do_extend_file(struct inode *inode, sb->s_blocksize - 1) & ~(sb->s_blocksize - 1)); iinfo->i_lenExtents = (iinfo->i_lenExtents + sb->s_blocksize - 1) & - ~(sb->s_blocksize - 1); + ~((u64)sb->s_blocksize - 1); } add = 0; diff --git a/fs/udf/partition.c b/fs/udf/partition.c index 2b85c9501bed89..ad8dcedca263db 100644 --- a/fs/udf/partition.c +++ b/fs/udf/partition.c @@ -55,7 +55,7 @@ uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, map = &sbi->s_partmaps[partition]; vdata = &map->s_type_specific.s_virtual; - if (block > vdata->s_num_entries) { + if (block >= vdata->s_num_entries) { udf_debug("Trying to access block beyond end of VAT (%u max %u)\n", block, vdata->s_num_entries); return 0xFFFFFFFF; diff --git a/include/kunit/run-in-irq-context.h b/include/kunit/run-in-irq-context.h index bfe60d6cf28d86..3802b6fb218edd 100644 --- a/include/kunit/run-in-irq-context.h +++ b/include/kunit/run-in-irq-context.h @@ -38,11 +38,13 @@ static enum hrtimer_restart kunit_irq_test_timer_func(struct hrtimer *timer) softirq_calls = atomic_read(&state->softirq_func_calls); /* - * If the timer is firing too often for the softirq or task to ever have - * a chance to run, increase the timer interval. This is needed on very - * slow systems. + * If the hrtimer is running much faster than the bh_work or the task, + * then it is firing too fast and might be starving those contexts as + * well as the actual system timer tick. Increase the interval. */ - if (hardirq_calls >= 20 && (softirq_calls == 0 || task_calls == 0)) + if (hardirq_calls >= 20 && + (hardirq_calls / 2 > softirq_calls || + hardirq_calls / 2 > task_calls)) state->interval = ktime_add_ns(state->interval, 250); if (!state->func(state->test_specific_state)) diff --git a/include/linux/bio.h b/include/linux/bio.h index 8f33f717b14f5b..ce34ea49ef3582 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -477,7 +477,7 @@ int bdev_rw_virt(struct block_device *bdev, sector_t sector, void *data, size_t len, enum req_op op); int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, - unsigned len_align_mask); + unsigned mem_align_mask, unsigned len_align_mask); void bio_iov_bvec_set(struct bio *bio, const struct iov_iter *iter); void __bio_release_pages(struct bio *bio, bool mark_dirty); diff --git a/include/linux/compaction.h b/include/linux/compaction.h index f29ef0653546ed..66a2f70e9e019d 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h @@ -58,6 +58,7 @@ enum compact_result { }; struct alloc_context; /* in mm/internal.h */ +struct capture_control; /* in mm/internal.h */ /* * Number of free order-0 pages that should be available above given watermark @@ -92,7 +93,7 @@ extern int fragmentation_index(struct zone *zone, unsigned int order); extern enum compact_result try_to_compact_pages(gfp_t gfp_mask, unsigned int order, unsigned int alloc_flags, const struct alloc_context *ac, enum compact_priority prio, - struct page **page); + struct capture_control *capc); extern void reset_isolation_suitable(pg_data_t *pgdat); extern bool compaction_suitable(struct zone *zone, int order, unsigned long watermark, int highest_zoneidx); diff --git a/include/linux/dm-io.h b/include/linux/dm-io.h index 7b2968612b7e69..67468389406493 100644 --- a/include/linux/dm-io.h +++ b/include/linux/dm-io.h @@ -27,7 +27,7 @@ struct page_list { struct page *page; }; -typedef void (*io_notify_fn)(unsigned int long error, void *context); +typedef void (*io_notify_fn)(unsigned long int error, unsigned long int unsup, void *context); enum dm_io_mem_type { DM_IO_PAGE_LIST,/* Page list */ @@ -80,8 +80,8 @@ void dm_io_client_destroy(struct dm_io_client *client); * error occurred doing io to the corresponding region. */ int dm_io(struct dm_io_request *io_req, unsigned int num_regions, - struct dm_io_region *region, unsigned int long *sync_error_bits, - unsigned short ioprio); + struct dm_io_region *region, unsigned long int *sync_error_bits, + unsigned long int *sync_unsup_bits, unsigned short ioprio); #endif /* __KERNEL__ */ #endif /* _LINUX_DM_IO_H */ diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index d1203da56fc5f7..d15b2b31d3c91e 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -567,6 +567,7 @@ void dma_buf_unpin(struct dma_buf_attachment *attach); struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info); int dma_buf_fd(struct dma_buf *dmabuf, int flags); +void dma_buf_fd_install(struct dma_buf *dmabuf, int fd); struct dma_buf *dma_buf_get(int fd); void dma_buf_put(struct dma_buf *dmabuf); diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h index 416a3352261fe8..f7740c5f6967c8 100644 --- a/include/linux/entry-common.h +++ b/include/linux/entry-common.h @@ -96,6 +96,9 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l ret = arch_ptrace_report_syscall_entry(regs); if (ret || (work & SYSCALL_WORK_SYSCALL_EMU)) return -1L; + + /* ptrace might have changed work flags */ + work = READ_ONCE(current_thread_info()->syscall_work); } /* Do seccomp after ptrace, to catch any tracer changes. */ diff --git a/include/linux/font.h b/include/linux/font.h index 6845f02d739a0d..877efe165d2abc 100644 --- a/include/linux/font.h +++ b/include/linux/font.h @@ -49,6 +49,8 @@ static inline unsigned int font_glyph_pitch(unsigned int width) * scanlines, which is usually the glyph's height in scanlines. Fonts * coming from user space can sometimes have a different vertical pitch * with empty scanlines between two adjacent glyphs. + * + * Returns: the number of bytes per glyph */ static inline unsigned int font_glyph_size(unsigned int width, unsigned int vpitch) { @@ -60,7 +62,7 @@ static inline unsigned int font_glyph_size(unsigned int width, unsigned int vpit */ /** - * font_data_t - Raw font data + * typedef font_data_t - Raw font data * * Values of type font_data_t store a pointer to raw font data. The format * is monochrome. Each bit sets a pixel of a stored glyph. Font data does diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 02bc5027523a8e..bd76a16a63afb2 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -866,8 +866,9 @@ unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec); unsigned long ftrace_get_addr_curr(struct dyn_ftrace *rec); extern ftrace_func_t ftrace_trace_function; +struct trace_array; -int ftrace_regex_open(struct ftrace_ops *ops, int flag, +int ftrace_regex_open(struct trace_array *tr, struct ftrace_ops *ops, int flag, struct inode *inode, struct file *file); ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos); @@ -1077,7 +1078,7 @@ static inline unsigned long ftrace_location(unsigned long ip) * have them defined when ftrace is not enabled, but these * functions may still be called. Use a macro instead of inline. */ -#define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; }) +#define ftrace_regex_open(tr, ops, flag, inode, file) ({ -ENODEV; }) #define ftrace_set_early_filter(ops, buf, enable) do { } while (0) #define ftrace_set_filter_ip(ops, ip, remove, reset) ({ -ENODEV; }) #define ftrace_set_filter_ips(ops, ips, cnt, remove, reset) ({ -ENODEV; }) diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 2abaf99321e90d..f883c03bd96532 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -171,6 +171,7 @@ extern int movable_gigantic_pages __read_mostly; extern int sysctl_hugetlb_shm_group __read_mostly; extern struct list_head huge_boot_pages[MAX_NUMNODES]; +void hugetlb_bootmem_struct_page_init(void); void hugetlb_bootmem_alloc(void); extern nodemask_t hugetlb_bootmem_nodes; void hugetlb_bootmem_set_nodes(void); @@ -1261,6 +1262,8 @@ static inline void hugetlb_count_sub(long l, struct mm_struct *mm) { } +pte_t huge_ptep_get(struct mm_struct *mm, unsigned long addr, pte_t *ptep); + static inline pte_t huge_ptep_clear_flush(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) { @@ -1293,6 +1296,10 @@ static inline bool hugetlbfs_pagecache_present( static inline void hugetlb_bootmem_alloc(void) { } + +static inline void hugetlb_bootmem_struct_page_init(void) +{ +} #endif /* CONFIG_HUGETLB_PAGE */ static inline spinlock_t *huge_pte_lock(struct hstate *h, diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 8c4f3bb2442950..e6de7ae55bda0a 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -181,6 +181,7 @@ struct kprobe_blacklist_entry { struct list_head list; unsigned long start_addr; unsigned long end_addr; + struct rcu_head rcu; }; #ifdef CONFIG_KPROBES diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index ab8cfaec82d31e..c00fc1740ce581 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -2608,6 +2608,7 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, #ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end); +void kvm_arch_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range); #endif #ifdef CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY diff --git a/include/linux/list.h b/include/linux/list.h index 09d979976b3b86..59f8aa0905d3be 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -150,10 +150,13 @@ static inline bool __list_del_entry_valid(struct list_head *entry) * * This is only for internal list manipulation where we know * the prev/next entries already! + * + * Must be inlined to ensure it can be safely called + * with initdata arguments. */ -static inline void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next) +static __always_inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) { if (!__list_add_valid(new, prev, next)) return; @@ -171,8 +174,12 @@ static inline void __list_add(struct list_head *new, * * Insert a new entry after the specified head. * This is good for implementing stacks. + * + * Must be inlined to ensure it can be safely called + * with initdata arguments. */ -static inline void list_add(struct list_head *new, struct list_head *head) +static __always_inline void list_add(struct list_head *new, + struct list_head *head) { __list_add(new, head, head->next); } diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h index b614e0deea7267..db8207d4059f30 100644 --- a/include/linux/lockd/bind.h +++ b/include/linux/lockd/bind.h @@ -16,17 +16,23 @@ struct svc_rqst; struct rpc_task; struct rpc_clnt; struct super_block; +struct module; -/* - * This is the set of functions for lockd->nfsd communication +/** + * struct nlmsvc_binding - lockd -> nfsd callback table + * @owner: module that provides this binding. + * @fopen: open a file by NFS file handle on behalf of an NLM request. + * @fclose: close a file that was previously opened via @fopen. + * Implementations MUST be semantically equivalent to fput(). */ struct nlmsvc_binding { + struct module *owner; int (*fopen)(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp, int flags); void (*fclose)(struct file *filp); }; -extern const struct nlmsvc_binding *nlmsvc_ops; +extern const struct nlmsvc_binding __rcu *nlmsvc_ops; /* * Similar to nfs_client_initdata, but without the NFS-specific diff --git a/include/linux/pci.h b/include/linux/pci.h index e02373320d81a5..813f11cbb7d7ed 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -79,14 +79,18 @@ * and, if ARI Forwarding is enabled, functions may appear to be on multiple * devices. */ -#define PCI_SLOT_ALL_DEVICES 0xfe +#define PCI_SLOT_ALL_DEVICES 0xfeff + +/* Used to identify a slot as a placeholder */ +#define PCI_SLOT_PLACEHOLDER 0xffff /* pci_slot represents a physical slot */ struct pci_slot { struct pci_bus *bus; /* Bus this slot is on */ struct list_head list; /* Node in list of slots */ struct hotplug_slot *hotplug; /* Hotplug info (move here) */ - unsigned char number; /* Device nr, or PCI_SLOT_ALL_DEVICES */ + u16 number; /* Device nr, or PCI_SLOT_ALL_DEVICES */ + unsigned int per_func_slot:1; /* Allow per function slot */ struct kobject kobj; }; diff --git a/include/linux/sched/rt.h b/include/linux/sched/rt.h index 4e3338103654c5..922935cc338335 100644 --- a/include/linux/sched/rt.h +++ b/include/linux/sched/rt.h @@ -52,8 +52,10 @@ static inline bool rt_or_dl_task_policy(struct task_struct *tsk) #ifdef CONFIG_RT_MUTEXES extern void rt_mutex_pre_schedule(void); +extern void rt_mutex_futex_pre_schedule(void); extern void rt_mutex_schedule(void); extern void rt_mutex_post_schedule(void); +extern void rt_mutex_futex_post_schedule(void); /* * Must hold either p->pi_lock or task_rq(p)->lock. diff --git a/include/linux/sched/user.h b/include/linux/sched/user.h index 4cc52698e214e2..8d7e5521f7cdd9 100644 --- a/include/linux/sched/user.h +++ b/include/linux/sched/user.h @@ -25,7 +25,8 @@ struct user_struct { #if defined(CONFIG_PERF_EVENTS) || defined(CONFIG_BPF_SYSCALL) || \ defined(CONFIG_NET) || defined(CONFIG_IO_URING) || \ - defined(CONFIG_VFIO_PCI_ZDEV_KVM) || IS_ENABLED(CONFIG_IOMMUFD) + defined(CONFIG_VFIO_PCI_ZDEV_KVM) || IS_ENABLED(CONFIG_IOMMUFD) || \ + defined(CONFIG_SECRETMEM) atomic_long_t locked_vm; #endif #ifdef CONFIG_WATCH_QUEUE diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 22eda1d54a0e84..ed6a2bc23db51c 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3126,6 +3126,30 @@ static inline void skb_set_transport_header(struct sk_buff *skb, skb->transport_header += offset; } +/** + * skb_set_transport_header_careful - conditionally set transport header + * @skb: buffer to alter + * @offset: offset to add to skb->data + * + * Hardened version of skb_set_transport_header(). + * + * Returns: true if the operation was a success. + */ +static inline bool __must_check +skb_set_transport_header_careful(struct sk_buff *skb, const int offset) +{ + long thoff = skb->data - skb->head + offset; + + if (unlikely(thoff != (typeof(skb->transport_header))thoff)) + return false; + + if (unlikely(thoff == (typeof(skb->transport_header))~0U)) + return false; + + skb->transport_header = thoff; + return true; +} + static inline unsigned char *skb_network_header(const struct sk_buff *skb) { return skb->head + skb->network_header; diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h index 909e84f84e0cfa..15c18100494294 100644 --- a/include/linux/soc/qcom/apr.h +++ b/include/linux/soc/qcom/apr.h @@ -3,6 +3,8 @@ #ifndef __QCOM_APR_H_ #define __QCOM_APR_H_ +#include +#include #include #include #include @@ -129,6 +131,8 @@ struct pkt_router_svc { gpr_port_cb callback; struct packet_router *pr; spinlock_t lock; + refcount_t callback_refs; + struct completion callbacks_drained; int id; void *priv; }; diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h index c5e6ab85df0999..0991ef7ec09288 100644 --- a/include/linux/soc/qcom/geni-se.h +++ b/include/linux/soc/qcom/geni-se.h @@ -124,6 +124,7 @@ struct geni_se { #define SE_DMA_RX_FSM_RST 0xd58 #define SE_HW_PARAM_0 0xe24 #define SE_HW_PARAM_1 0xe28 +#define SE_HW_PARAM_2 0xe2c /* GENI_FORCE_DEFAULT_REG fields */ #define FORCE_DEFAULT BIT(0) @@ -291,6 +292,9 @@ struct geni_se { #define RX_FIFO_DEPTH_MSK GENMASK(21, 16) #define RX_FIFO_DEPTH_SHFT 16 +/* SE_HW_PARAM_2 fields */ +#define PROG_RAM_DEPTH_MSK GENMASK(10, 0) + #define HW_VER_MAJOR_MASK GENMASK(31, 28) #define HW_VER_MAJOR_SHFT 28 #define HW_VER_MINOR_MASK GENMASK(27, 16) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 79dd44922fbcae..b3c74c876b83fa 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -305,7 +305,14 @@ struct sdw_dp0_prop { * @read_only_wordlength: Read Only wordlength field in DPN_BlockCtrl1 register * @simple_ch_prep_sm: If the port supports simplified channel prepare state * machine + * @simple_transport_registers: Optional banked transport registers implemented + * by a SIMPLE port in addition to the mandatory SIMPLE register set. */ +#define SDW_DPN_SIMPLE_TRANSPORT_BLOCKCTRL3 BIT(0) +#define SDW_DPN_SIMPLE_TRANSPORT_SAMPLECTRL2 BIT(1) +#define SDW_DPN_SIMPLE_TRANSPORT_HCTRL BIT(2) +#define SDW_DPN_SIMPLE_TRANSPORT_OFFSETCTRL2 BIT(3) + struct sdw_dpn_prop { u32 num; u32 max_word; @@ -330,6 +337,7 @@ struct sdw_dpn_prop { bool block_pack_mode; bool read_only_wordlength; bool simple_ch_prep_sm; + u32 simple_transport_registers; }; /** @@ -890,10 +898,18 @@ void sdw_show_ping_status(struct sdw_bus *bus, bool sync_delay); * * @num: Port number * @ch_mask: channels mask for port + * @transport_params_override_mask: Bitmask selecting slave transport fields + * which override values computed by the bus master. Master port parameters are + * never affected by these per-slave overrides. + * @transport_params_override: Values for the selected transport fields. */ +#define SDW_PORT_CONFIG_OVERRIDE_OFFSET1 BIT(0) + struct sdw_port_config { unsigned int num; unsigned int ch_mask; + unsigned int transport_params_override_mask; + struct sdw_transport_params transport_params_override; }; /** diff --git a/include/linux/sunrpc/bc_xprt.h b/include/linux/sunrpc/bc_xprt.h index 98939cb664cfa8..59d0cc889beba5 100644 --- a/include/linux/sunrpc/bc_xprt.h +++ b/include/linux/sunrpc/bc_xprt.h @@ -32,6 +32,7 @@ int xprt_setup_bc(struct rpc_xprt *xprt, unsigned int min_reqs); void xprt_destroy_bc(struct rpc_xprt *xprt, unsigned int max_reqs); void xprt_free_bc_rqst(struct rpc_rqst *req); unsigned int xprt_bc_max_slots(struct rpc_xprt *xprt); +void xprt_svc_shutdown_bc(struct rpc_xprt *xprt); void xprt_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv); /* @@ -71,6 +72,10 @@ static inline void xprt_free_bc_request(struct rpc_rqst *req) { } +static inline void xprt_svc_shutdown_bc(struct rpc_xprt *xprt) +{ +} + static inline void xprt_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv) { svc_destroy(serv); diff --git a/include/linux/sunrpc/svc_rdma_pcl.h b/include/linux/sunrpc/svc_rdma_pcl.h index 7516ad0fae8082..655681cf8fedc5 100644 --- a/include/linux/sunrpc/svc_rdma_pcl.h +++ b/include/linux/sunrpc/svc_rdma_pcl.h @@ -97,7 +97,7 @@ pcl_next_chunk(const struct svc_rdma_pcl *pcl, struct svc_rdma_chunk *chunk) */ #define pcl_for_each_segment(pos, chunk) \ for (pos = &(chunk)->ch_segments[0]; \ - pos <= &(chunk)->ch_segments[(chunk)->ch_segcount - 1]; \ + pos < &(chunk)->ch_segments[(chunk)->ch_segcount]; \ pos++) /** diff --git a/include/linux/uio.h b/include/linux/uio.h index a9bc5b3067e320..fe2e985d74d24b 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -389,9 +389,17 @@ ssize_t iov_iter_extract_pages(struct iov_iter *i, struct page ***pages, size_t maxsize, unsigned int maxpages, iov_iter_extraction_t extraction_flags, size_t *offset0); +/* + * Block-layer consumers (e.g. bio_iov_iter_get_pages()) require that the + * segments of an ITER_BVEC iterator are already aligned to the target device's + * DMA alignment, and forward them as-is. In-kernel users that build their own + * bvecs must not create sub-aligned segments; iov_iter_extract_bvecs() enforces + * the same for the segments it extracts via @mem_align_mask. + */ ssize_t iov_iter_extract_bvecs(struct iov_iter *iter, struct bio_vec *bv, size_t max_size, unsigned short *nr_vecs, - unsigned short max_vecs, iov_iter_extraction_t extraction_flags); + unsigned short max_vecs, unsigned mem_align_mask, + iov_iter_extraction_t extraction_flags); /** * iov_iter_extract_will_pin - Indicate how pages from the iterator will be retained diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h index f7f5cfbdef120c..9b46a6bc762ccd 100644 --- a/include/linux/usb/tcpci.h +++ b/include/linux/usb/tcpci.h @@ -144,6 +144,7 @@ #define TCPC_RX_BUF_FRAME_TYPE 0x31 #define TCPC_RX_BUF_FRAME_TYPE_SOP 0 #define TCPC_RX_BUF_FRAME_TYPE_SOP1 1 +#define TCPC_RX_BUF_FRAME_TYPE_MASK GENMASK(2, 0) #define TCPC_RX_HDR 0x32 #define TCPC_RX_DATA 0x34 /* through 0x4f */ diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index d87dc7f77f4e8a..a39c20c77efcda 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -62,7 +62,7 @@ struct vm_struct { #ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC unsigned int page_order; #endif - unsigned int nr_pages; + unsigned long nr_pages; phys_addr_t phys_addr; const void *caller; unsigned long requested_size; diff --git a/include/net/ip.h b/include/net/ip.h index 7f2fe1a8401b11..a8f57b4f4aa23f 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -506,6 +506,31 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst, return res; } +/* Configured/administrative MTU of a route, for advertising the TCP MSS. + * + * Unlike ip_dst_mtu_maybe_forward(), this deliberately ignores the + * ICMP-learned path MTU (rt->rt_pmtu). The advertised MSS bounds what the + * peer may send to us and must reflect our receive capability (the device or + * route-configured MTU), not a path MTU learned on the reverse (send) + * direction, which may not apply to the peer->us path and outlives the fnhe + * for the whole connection. See RFC 2923 section 2.3 and the comment above + * tcp_advertise_mss(). + */ +static inline unsigned int ip_dst_mtu_configured(const struct dst_entry *dst) +{ + unsigned int mtu, res; + + rcu_read_lock(); + mtu = dst_metric_raw(dst, RTAX_MTU); + if (!mtu) + mtu = READ_ONCE(dst_dev_rcu(dst)->mtu); + mtu = min_t(unsigned int, mtu, IP_MAX_MTU); + res = mtu - lwtunnel_headroom(dst->lwtstate, mtu); + rcu_read_unlock(); + + return res; +} + static inline unsigned int ip_skb_dst_mtu(struct sock *sk, const struct sk_buff *skb) { diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 09ffe0f13ce752..ac1acc0b74368c 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -385,6 +385,43 @@ static inline unsigned int ip6_dst_mtu_maybe_forward(const struct dst_entry *dst return mtu - lwtunnel_headroom(dst->lwtstate, mtu); } +/* Configured/administrative MTU of a route, for advertising the TCP MSS. + * + * Unlike ip6_dst_mtu_maybe_forward(), this ignores any ICMPv6-learned path + * MTU (which is kept on the RTF_CACHE exception route) and returns the MTU of + * the underlying route (fib6_pmtu) or the egress device. The advertised MSS + * bounds what the peer may send to us and must reflect our receive + * capability, not a path MTU learned on the reverse (send) direction. See + * RFC 2923 section 2.3 and the comment above tcp_advertise_mss(). + */ +static inline unsigned int ip6_dst_mtu_configured(const struct dst_entry *dst) +{ + const struct rt6_info *rt = dst_rt6_info(dst); + const struct fib6_info *from; + struct inet6_dev *idev; + unsigned int mtu = 0; + + rcu_read_lock(); + /* IPv6 keeps the learned PMTU and the configured MTU in the same + * RTAX_MTU slot: the learned value sits on this (possibly RTF_CACHE) + * dst, the configured one on the underlying route. Reach the latter + * via ->from (fib6_pmtu), populated by ip6_route_info_create(). + */ + from = rcu_dereference(rt->from); + if (from) + mtu = from->fib6_pmtu; + if (!mtu) { + mtu = IPV6_MIN_MTU; + idev = __in6_dev_get(dst_dev_rcu(dst)); + if (idev) + mtu = max_t(unsigned int, mtu, READ_ONCE(idev->cnf.mtu6)); + } + rcu_read_unlock(); + + mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); + return mtu - lwtunnel_headroom(dst->lwtstate, mtu); +} + u32 ip6_mtu_from_fib6(const struct fib6_result *res, const struct in6_addr *daddr, const struct in6_addr *saddr); diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index ff98129ff5dd3c..9dcd47a3bce6c4 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -657,8 +657,7 @@ struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md, int skb_tunnel_check_pmtu(struct sk_buff *skb, struct dst_entry *encap_dst, int headroom, bool reply); -static inline void ip_tunnel_adj_headroom(struct net_device *dev, - unsigned int headroom) +static inline unsigned int ip_tunnel_limit_headroom(unsigned int headroom) { /* we must cap headroom to some upperlimit, else pskb_expand_head * will overflow header offsets in skb_headers_offset_update(). @@ -668,6 +667,14 @@ static inline void ip_tunnel_adj_headroom(struct net_device *dev, if (headroom > max_allowed) headroom = max_allowed; + return headroom; +} + +static inline void ip_tunnel_adj_headroom(struct net_device *dev, + unsigned int headroom) +{ + headroom = ip_tunnel_limit_headroom(headroom); + if (headroom > READ_ONCE(dev->needed_headroom)) WRITE_ONCE(dev->needed_headroom, headroom); } diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index cccc662561aac3..b21f23b736fd20 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -2057,7 +2057,7 @@ struct sctp_association { force_delay:1; __u8 strreset_enable; - __u8 strreset_outstanding; /* request param count on the fly */ + __u8 strreset_outstanding; /* request param bitmask on the fly */ __u32 strreset_outseq; /* Update after receiving response */ __u32 strreset_inseq; /* Update after receiving request */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 2c5b889530b556..670c20876f265c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1782,6 +1782,11 @@ static inline int tcp_full_space(const struct sock *sk) return tcp_win_from_space(sk, READ_ONCE(sk->sk_rcvbuf)); } +static inline u32 tcp_dst_advmss(const struct dst_entry *dst) +{ + return max_t(u32, dst_metric_advmss(dst), TCP_MIN_MSS); +} + static inline void __tcp_adjust_rcv_ssthresh(struct sock *sk, u32 new_ssthresh) { int unused_mem = sk_unused_reserved_mem(sk); diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index e9039e73d058a7..390ace5bb252b2 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h @@ -95,6 +95,8 @@ struct target_core_fabric_ops { struct se_lun *); void (*fabric_pre_unlink)(struct se_portal_group *, struct se_lun *); + void (*fabric_post_unlink)(struct se_portal_group *se_tpg, + struct se_lun *lun); struct se_tpg_np *(*fabric_make_np)(struct se_portal_group *, struct config_group *, const char *); void (*fabric_drop_np)(struct se_tpg_np *); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 270c1a2c24c44a..1dd9fc5afc46f8 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -2114,6 +2114,14 @@ DEFINE_EVENT(f2fs_zip_end, f2fs_decompress_pages_end, ); #ifdef CONFIG_F2FS_IOSTAT +/* + * Number of read folio order buckets emitted by the f2fs_iostat tracepoint. + * TP_printk() cannot loop, so the field count is fixed here and must be >= + * the largest possible NR_PAGE_ORDERS (14 on arm64 with 64K pages). The + * BUILD_BUG_ON() in f2fs_update_read_folio_count() enforces this. + */ +#define F2FS_IOSTAT_RD_FOLIO_ORDERS 16 + TRACE_EVENT(f2fs_iostat, TP_PROTO(struct f2fs_sb_info *sbi, unsigned long long *iostat, @@ -2151,7 +2159,7 @@ TRACE_EVENT(f2fs_iostat, __field(unsigned long long, fs_mrio) __field(unsigned long long, fs_discard) __field(unsigned long long, fs_reset_zone) - __array(unsigned long long, read_folio_count, 11) + __array(unsigned long long, read_folio_count, F2FS_IOSTAT_RD_FOLIO_ORDERS) ), TP_fast_assign( @@ -2186,7 +2194,8 @@ TRACE_EVENT(f2fs_iostat, __entry->fs_reset_zone = iostat[FS_ZONE_RESET_IO]; memset(__entry->read_folio_count, 0, sizeof(__entry->read_folio_count)); memcpy(__entry->read_folio_count, read_folio_count, - sizeof(unsigned long long) * min_t(int, NR_PAGE_ORDERS, 11)); + sizeof(unsigned long long) * + min_t(int, NR_PAGE_ORDERS, F2FS_IOSTAT_RD_FOLIO_ORDERS)); ), TP_printk("dev = (%d,%d), " @@ -2201,7 +2210,8 @@ TRACE_EVENT(f2fs_iostat, "fs [data=%llu, (gc_data=%llu, cdata=%llu), " "node=%llu, meta=%llu], " "read_folio_count [0=%llu, 1=%llu, 2=%llu, 3=%llu, 4=%llu, " - "5=%llu, 6=%llu, 7=%llu, 8=%llu, 9=%llu, 10=%llu]", + "5=%llu, 6=%llu, 7=%llu, 8=%llu, 9=%llu, 10=%llu, 11=%llu, " + "12=%llu, 13=%llu, 14=%llu, 15=%llu]", show_dev(__entry->dev), __entry->app_wio, __entry->app_dio, __entry->app_bio, __entry->app_mio, __entry->app_bcdio, __entry->app_mcdio, __entry->fs_dio, __entry->fs_cdio, @@ -2218,7 +2228,9 @@ TRACE_EVENT(f2fs_iostat, __entry->read_folio_count[4], __entry->read_folio_count[5], __entry->read_folio_count[6], __entry->read_folio_count[7], __entry->read_folio_count[8], __entry->read_folio_count[9], - __entry->read_folio_count[10]) + __entry->read_folio_count[10], __entry->read_folio_count[11], + __entry->read_folio_count[12], __entry->read_folio_count[13], + __entry->read_folio_count[14], __entry->read_folio_count[15]) ); #ifndef __F2FS_IOSTAT_LATENCY_TYPE diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index 7ffe2ef127ee74..9c1102ebf06e0c 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -351,6 +351,7 @@ struct landlock_net_port_attr { * device. * - %LANDLOCK_ACCESS_FS_MAKE_DIR: Create (or rename) a directory. * - %LANDLOCK_ACCESS_FS_MAKE_REG: Create (or rename or link) a regular file. + * This also guards the creation of whiteout objects as used in OverlayFS. * - %LANDLOCK_ACCESS_FS_MAKE_SOCK: Create (or rename or link) a UNIX domain * socket. * - %LANDLOCK_ACCESS_FS_MAKE_FIFO: Create (or rename or link) a named pipe. diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h index 6b8102eaa121f1..748733c77c4f9c 100644 --- a/include/uapi/sound/snd_ar_tokens.h +++ b/include/uapi/sound/snd_ar_tokens.h @@ -96,6 +96,10 @@ enum ar_event_types { * APM_PROC_DOMAIN_ID_SDSP = 4, * APM_PROC_DOMAIN_ID_CDSP = 5 * + * %AR_TKN_U32_CONTAINER_PARENT_ID: Parent container instance ID. + * + * %AR_TKN_U32_CONTAINER_HEAP_ID: Container heap ID. + * * %AR_TKN_U32_MODULE_ID: Module ID * * %AR_TKN_U32_MODULE_INSTANCE_ID: Module Instance ID. @@ -112,6 +116,14 @@ enum ar_event_types { * * %AR_TKN_U32_MODULE_DST_IN_PORT_ID: Destination module input port ID * + * %AR_TKN_U32_MODULE_SPEAKER_PROTECTION_BYPASS: + * Leave speaker-protection modules + * in their default disabled state. + * + * %AR_TKN_U32_MODULE_INTEGRATED_BACKEND_ID: + * Backend DAI ID represented inside + * the same graph as the frontend. + * * %AR_TKN_U32_MODULE_HW_IF_IDX: Interface index types for I2S/LPAIF * * %AR_TKN_U32_MODULE_HW_IF_TYPE: Interface type @@ -186,6 +198,8 @@ enum ar_event_types { #define AR_TKN_U32_CONTAINER_STACK_SIZE 102 #define AR_TKN_U32_CONTAINER_GRAPH_POS 103 #define AR_TKN_U32_CONTAINER_PROC_DOMAIN 104 +#define AR_TKN_U32_CONTAINER_PARENT_ID 105 +#define AR_TKN_U32_CONTAINER_HEAP_ID 106 /* Module Tokens */ #define AR_TKN_U32_MODULE_ID 200 @@ -239,8 +253,24 @@ enum ar_event_types { #define AR_TKN_U32_MODULE_LOG_CODE 259 #define AR_TKN_U32_MODULE_LOG_TAP_POINT_ID 260 #define AR_TKN_U32_MODULE_LOG_MODE 261 +#define AR_TKN_U32_MODULE_SPEAKER_PROTECTION_BYPASS 262 +#define AR_TKN_U32_MODULE_INTEGRATED_BACKEND_ID 263 #define SND_SOC_AR_TPLG_MODULE_CFG_TYPE 0x01001006 +#define SND_SOC_AR_TPLG_CTRL_LINK_CFG_TYPE 0x08001061 + +/* Ordered private-data classes emitted by the protected FullIO topology. */ +#define SND_SOC_AR_TPLG_GRAPH_CAL_CFG_TYPE 0x53503101 +#define SND_SOC_AR_TPLG_RENDER_EP_CFG_TYPE 0x53503102 +#define SND_SOC_AR_TPLG_SP_TAG_CFG_TYPE 0x53503103 +#define SND_SOC_AR_TPLG_SPVI_TAG_CFG_TYPE 0x53503104 +#define SND_SOC_AR_TPLG_VI_EP_CFG_TYPE 0x53503105 +#define SND_SOC_AR_TPLG_PROTECTION_DYNAMIC_CFG_TYPE 0x53503106 +#define SND_SOC_AR_TPLG_VOLUME_GAIN_CFG_TYPE 0x53503107 +#define SND_SOC_AR_TPLG_VOLUME_FILTER_CFG_TYPE 0x53503108 +#define SND_SOC_AR_TPLG_VOLUME_MUTE_CFG_TYPE 0x53503109 +#define SND_SOC_AR_TPLG_CHANNEL_MIXER_CFG_TYPE 0x5350310a + struct audioreach_module_priv_data { __le32 size; /* size in bytes of the array, including all elements */ __le32 type; /* SND_SOC_AR_TPLG_MODULE_CFG_TYPE */ diff --git a/io_uring/query.c b/io_uring/query.c index 88a32573699244..f2b5e19e5af678 100644 --- a/io_uring/query.c +++ b/io_uring/query.c @@ -76,6 +76,9 @@ static int io_handle_query_entry(union io_query_data *data, void __user *uhdr, if (copy_from_user(&hdr, uhdr, sizeof(hdr))) return -EFAULT; + /* copy_struct_to_user() zeros up to usize bytes */ + if (hdr.size > PAGE_SIZE) + return -E2BIG; usize = hdr.size; hdr.size = min(hdr.size, IO_MAX_QUERY_SIZE); udata = u64_to_user_ptr(hdr.query_data); diff --git a/io_uring/waitid.c b/io_uring/waitid.c index 76af129ba8cadf..2b756017423f0e 100644 --- a/io_uring/waitid.c +++ b/io_uring/waitid.c @@ -125,7 +125,7 @@ static void io_waitid_remove_wq(struct io_kiocb *req) } } -static void io_waitid_complete(struct io_kiocb *req, int ret) +static void io_waitid_complete(struct io_kiocb *req, int ret, bool copy_si) { struct io_waitid *iw = io_kiocb_to_cmd(req, struct io_waitid); @@ -137,13 +137,16 @@ static void io_waitid_complete(struct io_kiocb *req, int ret) hlist_del_init(&req->hash_node); io_waitid_remove_wq(req); - ret = io_waitid_finish(req, ret); + if (copy_si) + ret = io_waitid_finish(req, ret); + else + io_waitid_free(req); if (ret < 0) req_set_fail(req); io_req_set_res(req, ret, 0); } -static bool __io_waitid_cancel(struct io_kiocb *req) +static bool __io_waitid_cancel(struct io_kiocb *req, bool copy_si) { struct io_waitid *iw = io_kiocb_to_cmd(req, struct io_waitid); @@ -159,21 +162,32 @@ static bool __io_waitid_cancel(struct io_kiocb *req) if (atomic_fetch_inc(&iw->refs) & IO_WAITID_REF_MASK) return false; - io_waitid_complete(req, -ECANCELED); + io_waitid_complete(req, -ECANCELED, copy_si); io_req_queue_tw_complete(req, -ECANCELED); return true; } +static bool io_waitid_cancel_cb(struct io_kiocb *req) +{ + return __io_waitid_cancel(req, true); +} + +static bool io_waitid_cancel_nocopy_cb(struct io_kiocb *req) +{ + return __io_waitid_cancel(req, false); +} + int io_waitid_cancel(struct io_ring_ctx *ctx, struct io_cancel_data *cd, unsigned int issue_flags) { - return io_cancel_remove(ctx, cd, issue_flags, &ctx->waitid_list, __io_waitid_cancel); + return io_cancel_remove(ctx, cd, issue_flags, &ctx->waitid_list, io_waitid_cancel_cb); } bool io_waitid_remove_all(struct io_ring_ctx *ctx, struct io_uring_task *tctx, bool cancel_all) { - return io_cancel_remove_all(ctx, tctx, &ctx->waitid_list, cancel_all, __io_waitid_cancel); + return io_cancel_remove_all(ctx, tctx, &ctx->waitid_list, cancel_all, + tctx ? io_waitid_cancel_cb : io_waitid_cancel_nocopy_cb); } static inline bool io_waitid_drop_issue_ref(struct io_kiocb *req) @@ -202,6 +216,11 @@ static void io_waitid_cb(struct io_tw_req tw_req, io_tw_token_t tw) int ret; io_tw_lock(ctx, tw); + if (unlikely(tw.cancel)) { + io_waitid_complete(req, -ECANCELED, false); + io_req_task_complete(tw_req, tw); + return; + } ret = __do_wait(&iwa->wo); @@ -229,7 +248,7 @@ static void io_waitid_cb(struct io_tw_req tw_req, io_tw_token_t tw) } } - io_waitid_complete(req, ret); + io_waitid_complete(req, ret, true); io_req_task_complete(tw_req, tw); } diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 7f791afe579178..f526456252148f 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1023,7 +1023,6 @@ static inline int audit_add_rule(struct audit_entry *entry) int audit_del_rule(struct audit_entry *entry) { struct audit_entry *e; - struct audit_tree *tree = entry->rule.tree; struct list_head *list; int ret = 0; #ifdef CONFIG_AUDITSYSCALL @@ -1071,9 +1070,6 @@ int audit_del_rule(struct audit_entry *entry) out: mutex_unlock(&audit_filter_mutex); - if (tree) - audit_put_tree(tree); /* that's the temporary one */ - return ret; } @@ -1158,6 +1154,8 @@ int audit_rule_change(int type, int seq, void *data, size_t datasz) } if (err || type == AUDIT_DEL_RULE) { + if (type == AUDIT_DEL_RULE && entry->rule.tree) + audit_put_tree(entry->rule.tree); if (entry->rule.exe) audit_remove_mark(entry->rule.exe); audit_free_rule(entry); diff --git a/kernel/bpf/bloom_filter.c b/kernel/bpf/bloom_filter.c index b73336c976b7c3..c6e7559b07de0e 100644 --- a/kernel/bpf/bloom_filter.c +++ b/kernel/bpf/bloom_filter.c @@ -41,7 +41,7 @@ static long bloom_map_peek_elem(struct bpf_map *map, void *value) for (i = 0; i < bloom->nr_hash_funcs; i++) { h = hash(bloom, value, map->value_size, i); - if (!test_bit(h, bloom->bitset)) + if (!test_bit(h % BITS_PER_LONG, bloom->bitset + BIT_WORD(h))) return -ENOENT; } @@ -57,9 +57,13 @@ static long bloom_map_push_elem(struct bpf_map *map, void *value, u64 flags) if (flags != BPF_ANY) return -EINVAL; + /* + * On 32-bit architectures, hashes larger than INT_MAX would be + * treated as negative by set_bit(). + */ for (i = 0; i < bloom->nr_hash_funcs; i++) { h = hash(bloom, value, map->value_size, i); - set_bit(h, bloom->bitset); + set_bit(h % BITS_PER_LONG, bloom->bitset + BIT_WORD(h)); } return 0; @@ -94,9 +98,10 @@ static int bloom_map_alloc_check(union bpf_attr *attr) static struct bpf_map *bloom_map_alloc(union bpf_attr *attr) { - u32 bitset_bytes, bitset_mask, nr_hash_funcs, nr_bits; + u32 bitset_mask, nr_hash_funcs, nr_bits; int numa_node = bpf_map_attr_numa_node(attr); struct bpf_bloom_filter *bloom; + u64 bitset_bytes; if (attr->key_size != 0 || attr->value_size == 0 || attr->max_entries == 0 || @@ -127,22 +132,16 @@ static struct bpf_map *bloom_map_alloc(union bpf_attr *attr) if (check_mul_overflow(attr->max_entries, nr_hash_funcs, &nr_bits) || check_mul_overflow(nr_bits / 5, (u32)7, &nr_bits) || nr_bits > (1UL << 31)) { - /* The bit array size is 2^32 bits but to avoid overflowing the - * u32, we use U32_MAX, which will round up to the equivalent - * number of bytes - */ - bitset_bytes = BITS_TO_BYTES(U32_MAX); bitset_mask = U32_MAX; } else { if (nr_bits <= BITS_PER_LONG) nr_bits = BITS_PER_LONG; else nr_bits = roundup_pow_of_two(nr_bits); - bitset_bytes = BITS_TO_BYTES(nr_bits); bitset_mask = nr_bits - 1; } - bitset_bytes = roundup(bitset_bytes, sizeof(unsigned long)); + bitset_bytes = BITS_TO_LONGS((u64)bitset_mask + 1) * sizeof(unsigned long); bloom = bpf_map_area_alloc(sizeof(*bloom) + bitset_bytes, numa_node); if (!bloom) diff --git a/kernel/bpf/percpu_freelist.c b/kernel/bpf/percpu_freelist.c index 632762b5729989..06ce588d13a39d 100644 --- a/kernel/bpf/percpu_freelist.c +++ b/kernel/bpf/percpu_freelist.c @@ -17,6 +17,8 @@ int pcpu_freelist_init(struct pcpu_freelist *s) raw_res_spin_lock_init(&head->lock); head->first = NULL; } + raw_res_spin_lock_init(&s->extralist.lock); + s->extralist.first = NULL; return 0; } @@ -46,22 +48,28 @@ void __pcpu_freelist_push(struct pcpu_freelist *s, struct pcpu_freelist_node *node) { struct pcpu_freelist_head *head; - int cpu; + int cpu, this_cpu; if (___pcpu_freelist_push(this_cpu_ptr(s->freelist), node)) return; + this_cpu = raw_smp_processor_id(); while (true) { - for_each_cpu_wrap(cpu, cpu_possible_mask, raw_smp_processor_id()) { - if (cpu == raw_smp_processor_id()) + for_each_cpu_wrap(cpu, cpu_possible_mask, this_cpu) { + if (cpu == this_cpu) continue; + head = per_cpu_ptr(s->freelist, cpu); - if (raw_res_spin_lock(&head->lock)) - continue; - pcpu_freelist_push_node(head, node); - raw_res_spin_unlock(&head->lock); - return; + if (___pcpu_freelist_push(head, node)) + return; } + + /* + * Push cannot fail. Use the extra list when none of the + * per-CPU freelists can accept the node. + */ + if (___pcpu_freelist_push(&s->extralist, node)) + return; } } @@ -117,6 +125,17 @@ static struct pcpu_freelist_node *___pcpu_freelist_pop(struct pcpu_freelist *s) } raw_res_spin_unlock(&head->lock); } + + /* Per-CPU lists are empty or unavailable, try the extra list. */ + head = &s->extralist; + if (!READ_ONCE(head->first)) + return NULL; + if (raw_res_spin_lock(&head->lock)) + return NULL; + node = head->first; + if (node) + WRITE_ONCE(head->first, node->next); + raw_res_spin_unlock(&head->lock); return node; } diff --git a/kernel/bpf/percpu_freelist.h b/kernel/bpf/percpu_freelist.h index 914798b7496729..980cf2884fd274 100644 --- a/kernel/bpf/percpu_freelist.h +++ b/kernel/bpf/percpu_freelist.h @@ -14,6 +14,7 @@ struct pcpu_freelist_head { struct pcpu_freelist { struct pcpu_freelist_head __percpu *freelist; + struct pcpu_freelist_head extralist; }; struct pcpu_freelist_node { diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index 41fe87d7302f22..9b086ecbf15949 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -504,78 +504,115 @@ get_callchain_entry_for_task(struct task_struct *task, u32 max_depth) #endif } -static long __bpf_get_stackid(struct bpf_map *map, - struct perf_callchain_entry *trace, u64 flags) +struct stackid { + struct stack_map_bucket *bucket; + u64 *ips; + u32 nr; + u32 len; + u32 hash; + u32 id; + bool hash_matches; +}; + +static int stackid_init(struct stackid *stackid, struct bpf_map *map, + struct perf_callchain_entry *trace, u64 flags) { struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); - struct stack_map_bucket *bucket, *new_bucket, *old_bucket; - u32 hash, id, trace_nr, trace_len, i, max_depth; u32 skip = flags & BPF_F_SKIP_FIELD_MASK; - bool user = flags & BPF_F_USER_STACK; - u64 *ips; - bool hash_matches; + u32 max_depth; if (trace->nr <= skip) /* skipping more than usable stack trace */ return -EFAULT; max_depth = stack_map_calculate_max_depth(map->value_size, stack_map_data_size(map), flags); - trace_nr = min_t(u32, trace->nr - skip, max_depth - skip); - trace_len = trace_nr * sizeof(u64); - ips = trace->ip + skip; - hash = jhash2((u32 *)ips, trace_len / sizeof(u32), 0); - id = hash & (smap->n_buckets - 1); - bucket = READ_ONCE(smap->buckets[id]); + stackid->nr = min_t(u32, trace->nr - skip, max_depth - skip); + stackid->len = stackid->nr * sizeof(u64); + stackid->ips = trace->ip + skip; + stackid->hash = jhash2((u32 *)stackid->ips, stackid->len / sizeof(u32), 0); + stackid->id = stackid->hash & (smap->n_buckets - 1); + stackid->bucket = READ_ONCE(smap->buckets[stackid->id]); + stackid->hash_matches = stackid->bucket && stackid->bucket->hash == stackid->hash; + return 0; +} + +static int stackid_fastpath(struct stackid *stackid, struct bpf_map *map, + struct perf_callchain_entry *trace, u64 flags) +{ + int err; + + err = stackid_init(stackid, map, trace, flags); + if (err) + return err; - hash_matches = bucket && bucket->hash == hash; /* fast cmp */ - if (hash_matches && flags & BPF_F_FAST_STACK_CMP) - return id; + if (stackid->hash_matches && flags & BPF_F_FAST_STACK_CMP) + return stackid->id; + + if (stack_map_use_build_id(map)) + return -ENOENT; + if (stackid->hash_matches && stackid->bucket->nr == stackid->nr && + memcmp(stackid->bucket->data, stackid->ips, stackid->len) == 0) + return stackid->id; + if (stackid->bucket && !(flags & BPF_F_REUSE_STACKID)) + return -EEXIST; + return -ENOENT; +} + +static struct stack_map_bucket * +stackid_new_bucket(struct stackid *stackid, struct bpf_map *map) +{ + struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); + struct bpf_stack_build_id *id_offs; + struct stack_map_bucket *bucket; + u32 i; + + bucket = (struct stack_map_bucket *) pcpu_freelist_pop(&smap->freelist); + if (unlikely(!bucket)) + return NULL; + + if (stack_map_use_build_id(map)) { + id_offs = (struct bpf_stack_build_id *)bucket->data; + for (i = 0; i < stackid->nr; i++) + id_offs[i].ip = stackid->ips[i]; + } else { + memcpy(bucket->data, stackid->ips, stackid->len); + } + + bucket->hash = stackid->hash; + bucket->nr = stackid->nr; + return bucket; +} + +static long stackid_install(struct stackid *stackid, struct bpf_map *map, + struct stack_map_bucket *new_bucket, u64 flags) +{ + struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); + bool user = flags & BPF_F_USER_STACK; + struct stack_map_bucket *old_bucket; + u32 trace_len; if (stack_map_use_build_id(map)) { struct bpf_stack_build_id *id_offs; - /* for build_id+offset, pop a bucket before slow cmp */ - new_bucket = (struct stack_map_bucket *) - pcpu_freelist_pop(&smap->freelist); - if (unlikely(!new_bucket)) - return -ENOMEM; - new_bucket->nr = trace_nr; id_offs = (struct bpf_stack_build_id *)new_bucket->data; - for (i = 0; i < trace_nr; i++) - id_offs[i].ip = ips[i]; - stack_map_get_build_id_offset(id_offs, trace_nr, user, false /* !may_fault */); - trace_len = trace_nr * sizeof(struct bpf_stack_build_id); - if (hash_matches && bucket->nr == trace_nr && - memcmp(bucket->data, new_bucket->data, trace_len) == 0) { + stack_map_get_build_id_offset(id_offs, stackid->nr, user, false /* !may_fault */); + trace_len = stackid->nr * sizeof(struct bpf_stack_build_id); + if (stackid->hash_matches && stackid->bucket->nr == stackid->nr && + memcmp(stackid->bucket->data, new_bucket->data, trace_len) == 0) { pcpu_freelist_push(&smap->freelist, &new_bucket->fnode); - return id; + return stackid->id; } - if (bucket && !(flags & BPF_F_REUSE_STACKID)) { + if (stackid->bucket && !(flags & BPF_F_REUSE_STACKID)) { pcpu_freelist_push(&smap->freelist, &new_bucket->fnode); return -EEXIST; } - } else { - if (hash_matches && bucket->nr == trace_nr && - memcmp(bucket->data, ips, trace_len) == 0) - return id; - if (bucket && !(flags & BPF_F_REUSE_STACKID)) - return -EEXIST; - - new_bucket = (struct stack_map_bucket *) - pcpu_freelist_pop(&smap->freelist); - if (unlikely(!new_bucket)) - return -ENOMEM; - memcpy(new_bucket->data, ips, trace_len); } - new_bucket->hash = hash; - new_bucket->nr = trace_nr; - - old_bucket = xchg(&smap->buckets[id], new_bucket); + old_bucket = xchg(&smap->buckets[stackid->id], new_bucket); if (old_bucket) pcpu_freelist_push(&smap->freelist, &old_bucket->fnode); - return id; + return stackid->id; } BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map, @@ -583,23 +620,36 @@ BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map, { u32 elem_size = stack_map_data_size(map); bool user = flags & BPF_F_USER_STACK; + struct stack_map_bucket *new_bucket; struct perf_callchain_entry *trace; + struct stackid stackid; bool kernel = !user; u32 max_depth; + int err; if (unlikely(flags & ~(BPF_F_SKIP_FIELD_MASK | BPF_F_USER_STACK | BPF_F_FAST_STACK_CMP | BPF_F_REUSE_STACKID))) return -EINVAL; max_depth = stack_map_calculate_max_depth(map->value_size, elem_size, flags); - trace = get_perf_callchain(regs, kernel, user, max_depth, - false, false, 0); - if (unlikely(!trace)) - /* couldn't fetch the stack trace */ - return -EFAULT; + scoped_guard(preempt) { + trace = get_perf_callchain(regs, kernel, user, max_depth, + false, false, 0); + if (unlikely(!trace)) + /* couldn't fetch the stack trace */ + return -EFAULT; + + err = stackid_fastpath(&stackid, map, trace, flags); + if (err != -ENOENT) + return err; + + new_bucket = stackid_new_bucket(&stackid, map); + if (!new_bucket) + return -ENOMEM; + } - return __bpf_get_stackid(map, trace, flags); + return stackid_install(&stackid, map, new_bucket, flags); } const struct bpf_func_proto bpf_get_stackid_proto = { @@ -627,7 +677,9 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpf_perf_event_data_kern *, ctx, struct bpf_map *, map, u64, flags) { struct perf_event *event = ctx->event; + struct stack_map_bucket *new_bucket; struct perf_callchain_entry *trace; + struct stackid stackid; bool kernel, user; __u64 nr_kernel; int ret; @@ -653,7 +705,6 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpf_perf_event_data_kern *, ctx, if (kernel) { trace->nr = nr_kernel; - ret = __bpf_get_stackid(map, trace, flags); } else { /* user */ u64 skip = flags & BPF_F_SKIP_FIELD_MASK; @@ -662,12 +713,22 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpf_perf_event_data_kern *, ctx, return -EFAULT; flags = (flags & ~BPF_F_SKIP_FIELD_MASK) | skip; - ret = __bpf_get_stackid(map, trace, flags); } + ret = stackid_fastpath(&stackid, map, trace, flags); + if (ret != -ENOENT) + goto out; + + new_bucket = stackid_new_bucket(&stackid, map); + if (new_bucket) { + trace->nr = nr; + return stackid_install(&stackid, map, new_bucket, flags); + } + ret = -ENOMEM; + +out: /* restore nr */ trace->nr = nr; - return ret; } @@ -718,6 +779,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task, max_depth = stack_map_calculate_max_depth(size, elem_size, flags); + preempt_disable(); if (may_fault) rcu_read_lock(); /* need RCU for perf's callchain below */ @@ -734,6 +796,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task, if (unlikely(!trace) || trace->nr < skip) { if (may_fault) rcu_read_unlock(); + preempt_enable(); goto err_fault; } @@ -754,6 +817,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task, /* trace/ips should not be dereferenced after this point */ if (may_fault) rcu_read_unlock(); + preempt_enable(); if (user_build_id) stack_map_get_build_id_offset(buf, trace_nr, user, may_fault); diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c index f754079a287d61..66093460584e8c 100644 --- a/kernel/dma/contiguous.c +++ b/kernel/dma/contiguous.c @@ -140,21 +140,22 @@ static bool numa_cma_configured __initdata; static int __init early_numa_cma(char *p) { int nid, count = 0; - unsigned long tmp; + unsigned long node; + phys_addr_t size; char *s = p; while (*s) { - if (sscanf(s, "%lu%n", &tmp, &count) != 1) + if (sscanf(s, "%lu%n", &node, &count) != 1) break; if (s[count] == ':') { - if (tmp >= MAX_NUMNODES) + if (node >= MAX_NUMNODES) break; - nid = array_index_nospec(tmp, MAX_NUMNODES); + nid = array_index_nospec(node, MAX_NUMNODES); s += count + 1; - tmp = memparse(s, &s); - numa_cma_size[nid] = tmp; + size = memparse(s, &s); + numa_cma_size[nid] = size; if (*s == ',') s++; @@ -207,9 +208,10 @@ static void __init dma_numa_cma_reserve(void) pernuma_size_bytes = cma_get_size(dma_contiguous_default_area); for_each_node(nid) { - int size, ret; + phys_addr_t size; char name[CMA_MAX_NAME]; struct cma **cma; + int ret; if (!node_online(nid)) { if (pernuma_size_bytes || numa_cma_size[nid]) diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c index 4391b797d4db81..b4cb2c03e5d7c7 100644 --- a/kernel/dma/direct.c +++ b/kernel/dma/direct.c @@ -164,22 +164,21 @@ static bool dma_direct_use_pool(struct device *dev, gfp_t gfp) return !gfpflags_allow_blocking(gfp) && !is_swiotlb_for_alloc(dev); } -static void *dma_direct_alloc_from_pool(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp) +static struct page *dma_direct_alloc_from_pool(struct device *dev, size_t size, + dma_addr_t *dma_handle, void **cpu_addr, gfp_t gfp) { struct page *page; u64 phys_limit; - void *ret; if (WARN_ON_ONCE(!IS_ENABLED(CONFIG_DMA_COHERENT_POOL))) return NULL; gfp |= dma_direct_optimal_gfp_mask(dev, &phys_limit); - page = dma_alloc_from_pool(dev, size, &ret, gfp, dma_coherent_ok); + page = dma_alloc_from_pool(dev, size, cpu_addr, gfp, dma_coherent_ok); if (!page) return NULL; *dma_handle = phys_to_dma_direct(dev, page_to_phys(page)); - return ret; + return page; } static void *dma_direct_alloc_no_mapping(struct device *dev, size_t size, @@ -247,8 +246,11 @@ void *dma_direct_alloc(struct device *dev, size_t size, * the atomic pools instead if we aren't allowed block. */ if ((remap || force_dma_unencrypted(dev)) && - dma_direct_use_pool(dev, gfp)) - return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp); + dma_direct_use_pool(dev, gfp)) { + page = dma_direct_alloc_from_pool(dev, size, dma_handle, + &ret, gfp); + return page ? ret : NULL; + } /* we always manually zero the memory once we are done */ page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, true); @@ -357,7 +359,7 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size, void *ret; if (force_dma_unencrypted(dev) && dma_direct_use_pool(dev, gfp)) - return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp); + return dma_direct_alloc_from_pool(dev, size, dma_handle, &ret, gfp); page = __dma_direct_alloc_pages(dev, size, gfp, false); if (!page) diff --git a/kernel/events/core.c b/kernel/events/core.c index a3a6c36182ba86..d0075f2b4f1c5a 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7034,7 +7034,6 @@ static void perf_mmap_close(struct vm_area_struct *vma) mapped_f unmapped = get_mapped(event, event_unmapped); struct perf_buffer *rb = ring_buffer_get(event); struct user_struct *mmap_user = rb->mmap_user; - bool detach_rest = false; /* FIXIES vs perf_pmu_unregister() */ if (unmapped) @@ -7065,17 +7064,18 @@ static void perf_mmap_close(struct vm_area_struct *vma) mutex_unlock(&rb->aux_mutex); } - if (refcount_dec_and_test(&rb->mmap_count)) - detach_rest = true; - - if (!refcount_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex)) - goto out_put; - - ring_buffer_attach(event, NULL); - mutex_unlock(&event->mmap_mutex); + /* + * Drop references in reverse order of perf_mmap() to prevent + * rb revival after rb->mmap_count reaches zero. + */ + if (refcount_dec_and_mutex_lock(&event->mmap_count, + &event->mmap_mutex)) { + ring_buffer_attach(event, NULL); + mutex_unlock(&event->mmap_mutex); + } /* If there's still other mmap()s of this buffer, we're done. */ - if (!detach_rest) + if (!refcount_dec_and_test(&rb->mmap_count)) goto out_put; /* diff --git a/kernel/futex/pi.c b/kernel/futex/pi.c index 88788e584ec8af..98f1b962e59a09 100644 --- a/kernel/futex/pi.c +++ b/kernel/futex/pi.c @@ -1070,17 +1070,11 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl * Caution; releasing @hb in-scope. The hb->lock is still locked * while the reference is dropped. The reference can not be dropped * after the unlock because if a user initiated resize is in progress - * then we might need to wake him. This can not be done after the - * rt_mutex_pre_schedule() invocation. The hb will remain valid because - * the thread, performing resize, will block on hb->lock during - * the requeue. + * then we might need to wake him. The hb will remain valid + * because the thread, performing resize, will block on + * hb->lock during the requeue. */ futex_private_hash_put(no_free_ptr(hbr.fph)); - /* - * Must be done before we enqueue the waiter, here is unfortunately - * under the hb lock, but that *should* work because it does nothing. - */ - rt_mutex_pre_schedule(); rt_mutex_init_waiter(&rt_waiter); @@ -1146,10 +1140,6 @@ int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int tryl * the */ futex_q_lockptr_lock(&q); - /* - * Waiter is unqueued. - */ - rt_mutex_post_schedule(); no_block: /* * Fixup the pi_state owner and possibly acquire the lock if we diff --git a/kernel/futex/requeue.c b/kernel/futex/requeue.c index 79823ad136830a..b3f4a4bccb1263 100644 --- a/kernel/futex/requeue.c +++ b/kernel/futex/requeue.c @@ -154,8 +154,16 @@ static inline void futex_requeue_pi_complete(struct futex_q *q, int locked) } while (!atomic_try_cmpxchg(&q->requeue_state, &old, new)); #ifdef CONFIG_PREEMPT_RT - /* If the waiter interleaved with the requeue let it know */ - if (unlikely(old == Q_REQUEUE_PI_WAIT)) + /* + * The waiter in futex_requeue_pi_wakeup_sync() can interleave with the + * wake below: It will assign Q_REQUEUE_PI_IN_PROGRESS and here it will + * be updated to Q_REQUEUE_PI_LOCKED (locked = 1). The rcuwait_wait_event() + * will already read Q_REQUEUE_PI_LOCKED and skip the schedule() invocation, + * leading to an access of futex_q::requeue_wait after the waiter returned. + * In this case only we skip the wake here and rely on following wake in + * requeue_pi_wake_futex() to perform the wake if needed. + */ + if (unlikely(old == Q_REQUEUE_PI_WAIT) && new != Q_REQUEUE_PI_LOCKED) rcuwait_wake_up(&q->requeue_wait); #endif } diff --git a/kernel/kprobes.c b/kernel/kprobes.c index bfc89083daa933..6337da5cab9e7e 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1447,8 +1447,14 @@ static bool __within_kprobe_blacklist(unsigned long addr) /* * If 'kprobe_blacklist' is defined, check the address and * reject any probe registration in the prohibited area. + * Note: this can return true during transition period where + * (start_addr, end_addr) in the black list is shrinking + * but old entry has not been removed yet. This is acceptable + * because the worst case is that we reject more probes than + * we should. */ - list_for_each_entry(ent, &kprobe_blacklist, list) { + guard(rcu)(); + list_for_each_entry_rcu(ent, &kprobe_blacklist, list) { if (addr >= ent->start_addr && addr < ent->end_addr) return true; } @@ -2509,7 +2515,7 @@ int kprobe_add_ksym_blacklist(unsigned long entry) ent->start_addr = entry; ent->end_addr = entry + size; INIT_LIST_HEAD(&ent->list); - list_add_tail(&ent->list, &kprobe_blacklist); + list_add_tail_rcu(&ent->list, &kprobe_blacklist); return (int)size; } @@ -2603,8 +2609,8 @@ static void kprobe_remove_area_blacklist(unsigned long start, unsigned long end) list_for_each_entry_safe(ent, n, &kprobe_blacklist, list) { if (ent->start_addr < start || ent->start_addr >= end) continue; - list_del(&ent->list); - kfree(ent); + list_del_rcu(&ent->list); + kfree_rcu(ent, rcu); } } diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index 175c08a6e41e3f..6fad9152387a90 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -501,7 +501,7 @@ static int __init kho_preserved_memory_reserve(phys_addr_t phys, struct page *page; u64 sz; - sz = 1 << (order + PAGE_SHIFT); + sz = 1UL << (order + PAGE_SHIFT); page = kho_get_preserved_page(phys, order); /* Reserve the memory preserved in KHO in memblock */ diff --git a/kernel/locking/rtmutex_api.c b/kernel/locking/rtmutex_api.c index 5d48d64725b125..eb18b094473c9a 100644 --- a/kernel/locking/rtmutex_api.c +++ b/kernel/locking/rtmutex_api.c @@ -423,6 +423,7 @@ int __sched rt_mutex_wait_proxy_lock(struct rt_mutex_base *lock, { int ret; + rt_mutex_futex_pre_schedule(); raw_spin_lock_irq(&lock->wait_lock); /* sleep on the mutex */ set_current_state(TASK_INTERRUPTIBLE); @@ -433,6 +434,7 @@ int __sched rt_mutex_wait_proxy_lock(struct rt_mutex_base *lock, */ fixup_rt_mutex_waiters(lock, true); raw_spin_unlock_irq(&lock->wait_lock); + rt_mutex_futex_post_schedule(); return ret; } diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c index 0fc11e45df9b91..f23126d804b25a 100644 --- a/kernel/module/kallsyms.c +++ b/kernel/module/kallsyms.c @@ -258,17 +258,25 @@ static const char *find_kallsyms_symbol(struct module *mod, unsigned int i, best = 0; unsigned long nextval, bestval; struct mod_kallsyms *kallsyms = rcu_dereference(mod->kallsyms); - struct module_memory *mod_mem; + struct module_memory *mod_mem = NULL; - /* At worse, next value is at end of module */ - if (within_module_init(addr, mod)) - mod_mem = &mod->mem[MOD_INIT_TEXT]; - else - mod_mem = &mod->mem[MOD_TEXT]; + for_each_mod_mem_type(type) { +#ifndef CONFIG_KALLSYMS_ALL + if (!mod_mem_type_is_text(type)) + continue; +#endif + if (within_module_mem_type(addr, mod, type)) { + mod_mem = &mod->mem[type]; + break; + } + } - nextval = (unsigned long)mod_mem->base + mod_mem->size; + if (!mod_mem) + return NULL; - bestval = kallsyms_symbol_value(&kallsyms->symtab[best]); + /* Initialize bounds within memory region the address belongs to. */ + nextval = (unsigned long)mod_mem->base + mod_mem->size; + bestval = (unsigned long)mod_mem->base - 1; /* * Scan for closest preceding symbol, and next symbol. (ELF diff --git a/kernel/module/main.c b/kernel/module/main.c index 46dd8d25a6058e..7cbc8f0e28c614 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -2011,6 +2011,7 @@ static int elf_validity_cache_sechdrs(struct load_info *info) * Specifically checks: * * * Section name table index is inbounds of section headers + * * Section name table type is SHT_STRTAB * * Section name table is not empty * * Section name table is NUL terminated * * All section name offsets are inbounds of the section @@ -2038,6 +2039,11 @@ static int elf_validity_cache_secstrings(struct load_info *info) strhdr = &info->sechdrs[info->hdr->e_shstrndx]; + if (strhdr->sh_type != SHT_STRTAB) { + pr_err("Invalid ELF section name table type: %u\n", strhdr->sh_type); + return -ENOEXEC; + } + /* * The section name table must be NUL-terminated, as required * by the spec. This makes strcmp and pr_* calls that access @@ -2204,7 +2210,7 @@ static int elf_validity_cache_index_sym(struct load_info *info) * Must have &load_info->index.sym populated. * * Looks at the symbol table's associated string table, makes sure it is - * in-bounds, and caches it. + * in-bounds and of type SHT_STRTAB, and caches it. * * Return: %0 if valid, %-ENOEXEC on failure. */ @@ -2218,6 +2224,12 @@ static int elf_validity_cache_index_str(struct load_info *info) return -ENOEXEC; } + if (info->sechdrs[str_idx].sh_type != SHT_STRTAB) { + pr_err("Invalid ELF symbol string table type: %u\n", + info->sechdrs[str_idx].sh_type); + return -ENOEXEC; + } + info->index.str = str_idx; return 0; } diff --git a/kernel/params.c b/kernel/params.c index a668863a4bb639..939ef84a46ffb0 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -261,6 +261,7 @@ EXPORT_SYMBOL_GPL(param_set_uint_minmax); int param_set_charp(const char *val, const struct kernel_param *kp) { + char *tmp; size_t len, maxlen = 1024; len = strnlen(val, maxlen + 1); @@ -269,19 +270,20 @@ int param_set_charp(const char *val, const struct kernel_param *kp) return -ENOSPC; } - maybe_kfree_parameter(*(char **)kp->arg); - /* * This is a hack. We can't kmalloc() in early boot, and we * don't need to; this mangled commandline is preserved. */ if (slab_is_available()) { - *(char **)kp->arg = kmalloc_parameter(len + 1); - if (!*(char **)kp->arg) + tmp = kmalloc_parameter(len + 1); + if (!tmp) return -ENOMEM; - strcpy(*(char **)kp->arg, val); + memcpy(tmp, val, len + 1); } else - *(const char **)kp->arg = val; + tmp = (char *)val; + + maybe_kfree_parameter(*(char **)kp->arg); + *(char **)kp->arg = tmp; return 0; } diff --git a/kernel/pid.c b/kernel/pid.c index d01d0dd7114b27..95b8ccfa826906 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -766,29 +766,6 @@ static struct ctl_table_root pid_table_root = { .set_ownership = pid_table_root_set_ownership, }; -static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffer, - size_t *lenp, loff_t *ppos) -{ - struct pid *new_pid; - pid_t tmp_pid; - int r; - struct ctl_table tmp_table = *table; - - tmp_pid = pid_vnr(cad_pid); - tmp_table.data = &tmp_pid; - - r = proc_dointvec(&tmp_table, write, buffer, lenp, ppos); - if (r || !write) - return r; - - new_pid = find_get_pid(tmp_pid); - if (!new_pid) - return -ESRCH; - - put_pid(xchg(&cad_pid, new_pid)); - return 0; -} - static const struct ctl_table pid_table[] = { { .procname = "pid_max", @@ -799,14 +776,6 @@ static const struct ctl_table pid_table[] = { .extra1 = &pid_max_min, .extra2 = &pid_max_max, }, -#ifdef CONFIG_PROC_SYSCTL - { - .procname = "cad_pid", - .maxlen = sizeof(int), - .mode = 0600, - .proc_handler = proc_do_cad_pid, - }, -#endif }; #endif diff --git a/kernel/reboot.c b/kernel/reboot.c index 695c33e75efd96..f070c5c1103a9c 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -1366,6 +1366,29 @@ static struct attribute *reboot_attrs[] = { }; #ifdef CONFIG_SYSCTL +static int proc_do_cad_pid(const struct ctl_table *table, int write, void *buffer, + size_t *lenp, loff_t *ppos) +{ + struct ctl_table tmp_table = *table; + struct pid *new_pid; + pid_t tmp_pid; + int r; + + tmp_pid = pid_vnr(cad_pid); + tmp_table.data = &tmp_pid; + + r = proc_dointvec(&tmp_table, write, buffer, lenp, ppos); + if (r || !write) + return r; + + new_pid = find_get_pid(tmp_pid); + if (!new_pid) + return -ESRCH; + + put_pid(xchg(&cad_pid, new_pid)); + return 0; +} + static const struct ctl_table kern_reboot_table[] = { { .procname = "poweroff_cmd", @@ -1381,6 +1404,12 @@ static const struct ctl_table kern_reboot_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "cad_pid", + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = proc_do_cad_pid, + }, }; static void __init kernel_reboot_sysctls_init(void) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 96226707c2f613..87caa1290fad69 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -443,6 +443,17 @@ static void __sched_core_flip(bool enabled) sched_core_lock(cpu, &flags); + /* + * A core-wide selection may have the shared rq lock temporarily + * released by a lock-dropping ->pick_task(). Flipping would + * rebind rq_lockp() under it. Wait it out. + */ + while (cpu_rq(cpu)->core->core_pick_in_flight) { + sched_core_unlock(cpu, &flags); + cpu_relax(); + sched_core_lock(cpu, &flags); + } + for_each_cpu(t, smt_mask) cpu_rq(t)->core_enabled = enabled; @@ -6223,7 +6234,7 @@ pick_next_task(struct rq *rq, struct rq_flags *rf) unsigned long cookie; int i, cpu, occ = 0; struct rq *rq_i; - bool need_sync; + bool need_sync = false; if (!sched_core_enabled(rq)) return __pick_next_task(rq, rf); @@ -6242,6 +6253,8 @@ pick_next_task(struct rq *rq, struct rq_flags *rf) return __pick_next_task(rq, rf); } + rq->core->core_pick_in_flight++; + /* * If there were no {en,de}queues since we picked (IOW, the task * pointers are all still valid), and we haven't scheduled the last @@ -6266,7 +6279,9 @@ pick_next_task(struct rq *rq, struct rq_flags *rf) prev_balance(rq, rf); smt_mask = cpu_smt_mask(cpu); - need_sync = !!rq->core->core_cookie; + +restart: + need_sync |= !!rq->core->core_cookie; /* reset state */ rq->core->core_cookie = 0UL; @@ -6301,10 +6316,15 @@ pick_next_task(struct rq *rq, struct rq_flags *rf) * and there are no cookied tasks running on siblings. */ if (!need_sync) { -restart_single: next = pick_task(rq, rf); - if (unlikely(next == RETRY_TASK)) - goto restart_single; + if (unlikely(next == RETRY_TASK)) { + /* rq lock may have been dropped, clocks invalidated */ + core_clock_updated = false; + if (!(rq->clock_update_flags & RQCF_UPDATED)) + update_rq_clock(rq); + goto restart; + } + if (!next->core_cookie) { rq->core_pick = NULL; rq->core_dl_server = NULL; @@ -6324,7 +6344,6 @@ pick_next_task(struct rq *rq, struct rq_flags *rf) * * Tie-break prio towards the current CPU */ -restart_multi: max = NULL; for_each_cpu_wrap(i, smt_mask, cpu) { rq_i = cpu_rq(i); @@ -6338,8 +6357,13 @@ pick_next_task(struct rq *rq, struct rq_flags *rf) update_rq_clock(rq_i); p = pick_task(rq_i, rf); - if (unlikely(p == RETRY_TASK)) - goto restart_multi; + if (unlikely(p == RETRY_TASK)) { + /* rq lock may have been dropped, clocks invalidated */ + core_clock_updated = false; + if (!(rq->clock_update_flags & RQCF_UPDATED)) + update_rq_clock(rq); + goto restart; + } rq_i->core_pick = p; rq_i->core_dl_server = rq_i->dl_server; @@ -6445,6 +6469,7 @@ pick_next_task(struct rq *rq, struct rq_flags *rf) } out_set_next: + rq->core->core_pick_in_flight--; put_prev_set_next_task(rq, rq->donor, next); if (rq->core->core_forceidle_count && next == rq->idle) queue_core_balance(rq); @@ -6639,6 +6664,13 @@ static void sched_core_cpu_deactivate(unsigned int cpu) core_rq->core_forceidle_seq = rq->core_forceidle_seq; core_rq->core_forceidle_occupation = rq->core_forceidle_occupation; + /* + * A stale leftover would bias the count forever if this CPU later + * returns as its own leader. Move, don't copy. + */ + core_rq->core_pick_in_flight = rq->core_pick_in_flight; + rq->core_pick_in_flight = 0; + /* * Accounting edge for forced idle is handled in pick_next_task(). * Don't need another one here, since the hotplug thread shouldn't @@ -7601,6 +7633,17 @@ void rt_mutex_pre_schedule(void) sched_submit_work(current); } +/* + * Used within the futex syscall context, skips sched_submit_work() because none + * its work will be done. Asserts ensure that it is indeed the case. + */ +void rt_mutex_futex_pre_schedule(void) +{ + lockdep_assert(!(current->flags & (PF_WQ_WORKER | PF_IO_WORKER))); + lockdep_assert(!current->plug); + lockdep_assert(!fetch_and_set(current->sched_rt_mutex, 1)); +} + void rt_mutex_schedule(void) { lockdep_assert(current->sched_rt_mutex); @@ -7613,6 +7656,11 @@ void rt_mutex_post_schedule(void) lockdep_assert(fetch_and_set(current->sched_rt_mutex, 0)); } +void rt_mutex_futex_post_schedule(void) +{ + lockdep_assert(fetch_and_set(current->sched_rt_mutex, 0)); +} + /* * rt_mutex_setprio - set the current priority of a task * @p: task to boost @@ -9053,6 +9101,7 @@ void __init sched_init(void) rq->core_forceidle_count = 0; rq->core_forceidle_occupation = 0; rq->core_forceidle_start = 0; + rq->core_pick_in_flight = 0; rq->core_cookie = 0UL; #endif diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index dff4ee04694c2b..614ff0d33c0165 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -61,6 +61,17 @@ static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu); /************************ Governor internals ***********************/ +static void sugov_update_rate_limit_us(struct sugov_policy *sg_policy) +{ + /* + * Cast rate_limit_us before multiplication to force 64-bit arithmetic. + * Otherwise, on 32-bit platforms, both operands are converted to + * 32-bit unsigned long and the multiplication may overflow. + */ + sg_policy->freq_update_delay_ns = + (s64)sg_policy->tunables->rate_limit_us * NSEC_PER_USEC; +} + static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time) { s64 delta_ns; @@ -606,7 +617,7 @@ rate_limit_us_store(struct gov_attr_set *attr_set, const char *buf, size_t count tunables->rate_limit_us = rate_limit_us; list_for_each_entry(sg_policy, &attr_set->policy_list, tunables_hook) - sg_policy->freq_update_delay_ns = rate_limit_us * NSEC_PER_USEC; + sugov_update_rate_limit_us(sg_policy); return count; } @@ -848,7 +859,7 @@ static int sugov_start(struct cpufreq_policy *policy) void (*uu)(struct update_util_data *data, u64 time, unsigned int flags); unsigned int cpu; - sg_policy->freq_update_delay_ns = sg_policy->tunables->rate_limit_us * NSEC_PER_USEC; + sugov_update_rate_limit_us(sg_policy); sg_policy->last_freq_update_time = 0; sg_policy->next_freq = 0; sg_policy->work_in_progress = false; diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 18183062f751c9..d7c4b62e712e5e 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -479,12 +479,27 @@ static bool rq_is_open(struct rq *rq, u64 enq_flags) */ DEFINE_PER_CPU(struct rq *, scx_locked_rq_state); +/* + * Under core scheduling, a pick that releases the rq lock invalidates the + * core-wide selection it is part of. Count the releases so that the core-sched + * pick can tell whether one happened across dispatch. + */ +static void scx_rq_lock_drop(struct rq *rq) +{ + lockdep_assert_rq_held(rq); +#ifdef CONFIG_SCHED_CORE + if (sched_core_enabled(rq)) + rq->scx.lock_drop_seq++; +#endif +} + static void switch_rq_lock(struct rq *from, struct rq *to) { bool tracked = scx_locked_rq() == from; if (tracked) update_locked_rq(NULL); + scx_rq_lock_drop(from); raw_spin_rq_unlock(from); raw_spin_rq_lock(to); if (tracked) @@ -1133,6 +1148,7 @@ static void deferred_irq_workfn(struct irq_work *irq_work) raw_spin_rq_lock(rq); run_deferred(rq); + scx_rq_lock_drop(rq); raw_spin_rq_unlock(rq); } @@ -2430,12 +2446,14 @@ static bool consume_remote_task(struct rq *this_rq, struct task_struct *p, u64 enq_flags, struct scx_dispatch_q *dsq, struct rq *src_rq) { + scx_rq_lock_drop(this_rq); raw_spin_rq_unlock(this_rq); if (unlink_dsq_and_lock_src_rq(p, dsq, src_rq)) { move_remote_task_to_local_dsq(p, enq_flags, src_rq, this_rq); return true; } else { + scx_rq_lock_drop(src_rq); raw_spin_rq_unlock(src_rq); raw_spin_rq_lock(this_rq); return false; @@ -2737,7 +2755,7 @@ static void finish_dispatch(struct scx_sched *sch, struct rq *rq, BUG_ON(!(p->scx.flags & SCX_TASK_QUEUED)); - dsq = find_dsq_for_dispatch(sch, this_rq(), dsq_id, task_cpu(p)); + dsq = find_dsq_for_dispatch(sch, rq, dsq_id, task_cpu(p)); if (dsq->id == SCX_DSQ_LOCAL) dispatch_to_local_dsq(sch, rq, dsq, p, enq_flags); @@ -2774,12 +2792,20 @@ static inline void maybe_queue_balance_callback(struct rq *rq) rq->scx.flags &= ~SCX_RQ_BAL_CB_PENDING; } +/* what dispatch concluded, consumed by the pick that follows */ +enum scx_dsp_verdict { + SCX_DSP_NONE, /* nothing to run */ + SCX_DSP_LOCAL, /* local DSQ has tasks */ + SCX_DSP_PREV, /* keep running @prev */ + SCX_DSP_RETRY, /* pick helpers only: restart the pick */ +}; + /* * One user of this function is scx_bpf_dispatch() which can be called * recursively as sub-sched dispatches nest. Always inline to reduce stack usage * from the call frame. */ -static __always_inline bool +static __always_inline enum scx_dsp_verdict scx_dispatch_sched(struct scx_sched *sch, struct rq *rq, struct task_struct *prev, bool nested) { @@ -2790,12 +2816,15 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq, scx_task_on_sched(sch, prev); if (consume_global_dsq(sch, rq)) - return true; + return SCX_DSP_LOCAL; if (bypass_dsp_enabled(sch)) { /* if @sch is bypassing, only the bypass DSQs are active */ - if (scx_bypassing(sch, cpu)) - return consume_dispatch_q(sch, rq, bypass_dsq(sch, cpu), 0); + if (scx_bypassing(sch, cpu)) { + if (consume_dispatch_q(sch, rq, bypass_dsq(sch, cpu), 0)) + return SCX_DSP_LOCAL; + return SCX_DSP_NONE; + } #ifdef CONFIG_EXT_SUB_SCHED /* @@ -2815,13 +2844,13 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq, if (!(pcpu->bypass_host_seq++ % SCX_BYPASS_HOST_NTH) && consume_dispatch_q(sch, rq, bypass_dsq(sch, cpu), 0)) { __scx_add_event(sch, SCX_EV_SUB_BYPASS_DISPATCH, 1); - return true; + return SCX_DSP_LOCAL; } #endif /* CONFIG_EXT_SUB_SCHED */ } if (unlikely(!SCX_HAS_OP(sch, dispatch)) || !scx_rq_online(rq)) - return false; + return SCX_DSP_NONE; dspc->rq = rq; @@ -2848,14 +2877,12 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq, flush_dispatch_buf(sch, rq); - if ((prev->scx.flags & SCX_TASK_QUEUED) && prev->scx.slice) { - rq->scx.flags |= SCX_RQ_BAL_KEEP; - return true; - } + if ((prev->scx.flags & SCX_TASK_QUEUED) && prev->scx.slice) + return SCX_DSP_PREV; if (rq->scx.local_dsq.nr) - return true; + return SCX_DSP_LOCAL; if (consume_global_dsq(sch, rq)) - return true; + return SCX_DSP_LOCAL; /* * ops.dispatch() can trap us in this loop by repeatedly @@ -2877,20 +2904,20 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq, * queued. Without this fallback, bypassed tasks could stall if the host * scheduler's ops.dispatch() doesn't yield any tasks. */ - if (bypass_dsp_enabled(sch)) - return consume_dispatch_q(sch, rq, bypass_dsq(sch, cpu), 0); + if (bypass_dsp_enabled(sch) && consume_dispatch_q(sch, rq, bypass_dsq(sch, cpu), 0)) + return SCX_DSP_LOCAL; - return false; + return SCX_DSP_NONE; } -static int balance_one(struct rq *rq, struct task_struct *prev) +static enum scx_dsp_verdict balance_one(struct rq *rq, struct task_struct *prev) { struct scx_sched *sch = scx_root; + enum scx_dsp_verdict verdict; s32 cpu = cpu_of(rq); lockdep_assert_rq_held(rq); rq->scx.flags |= SCX_RQ_IN_BALANCE; - rq->scx.flags &= ~SCX_RQ_BAL_KEEP; if ((sch->ops.flags & SCX_OPS_HAS_CPU_PREEMPT) && unlikely(rq->scx.cpu_released)) { @@ -2920,16 +2947,19 @@ static int balance_one(struct rq *rq, struct task_struct *prev) */ if ((prev->scx.flags & SCX_TASK_QUEUED) && prev->scx.slice && !scx_bypassing(sch, cpu)) { - rq->scx.flags |= SCX_RQ_BAL_KEEP; + verdict = SCX_DSP_PREV; goto has_tasks; } } /* if there already are tasks to run, nothing to do */ - if (rq->scx.local_dsq.nr) + if (rq->scx.local_dsq.nr) { + verdict = SCX_DSP_LOCAL; goto has_tasks; + } - if (scx_dispatch_sched(sch, rq, prev, false)) + verdict = scx_dispatch_sched(sch, rq, prev, false); + if (verdict != SCX_DSP_NONE) goto has_tasks; /* @@ -2938,12 +2968,12 @@ static int balance_one(struct rq *rq, struct task_struct *prev) */ if ((prev->scx.flags & SCX_TASK_QUEUED) && (!(sch->ops.flags & SCX_OPS_ENQ_LAST) || scx_bypassing(sch, cpu))) { - rq->scx.flags |= SCX_RQ_BAL_KEEP; __scx_add_event(sch, SCX_EV_DISPATCH_KEEP_LAST, 1); + verdict = SCX_DSP_PREV; goto has_tasks; } rq->scx.flags &= ~SCX_RQ_IN_BALANCE; - return false; + return SCX_DSP_NONE; has_tasks: /* @@ -2960,7 +2990,7 @@ static int balance_one(struct rq *rq, struct task_struct *prev) schedule_reenq_local(rq, 0); rq->scx.flags &= ~SCX_RQ_IN_BALANCE; - return true; + return verdict; } static void set_next_task_scx(struct rq *rq, struct task_struct *p, bool first) @@ -3113,12 +3143,12 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p, * ops.enqueue() that @p is the only one available for this cpu, * which should trigger an explicit follow-up scheduling event. * - * Core scheduling can force this CPU idle while @p stays - * runnable. @p's cookie then won't match the core's, so skip - * the warning in that case. + * Under core scheduling, a pick dispatches only when nothing is + * locally runnable and can legitimately go idle with @p still + * runnable (see do_pick_task_scx()). */ if (next && sched_class_above(&ext_sched_class, next->sched_class)) { - WARN_ON_ONCE(sched_cpu_cookie_match(rq, p) && + WARN_ON_ONCE(!sched_core_enabled(rq) && !(sch->ops.flags & SCX_OPS_ENQ_LAST)); do_enqueue_task(rq, p, SCX_ENQ_LAST, -1); } else { @@ -3133,11 +3163,26 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p, static void kick_sync_wait_bal_cb(struct rq *rq) { - struct scx_kick_syncs __rcu *ks = __this_cpu_read(scx_kick_syncs); - unsigned long *ksyncs = rcu_dereference_sched(ks)->syncs; + struct scx_kick_syncs __rcu *ks; + unsigned long *ksyncs; bool waited; s32 cpu; + /* + * This callback is queued and normally flushed within @rq's own + * scheduling pass. However, dispatch can drop the rq lock while it sits + * queued, and lock takers in that window (the sched class change paths, + * the scx task iterator) flush pending balance callbacks on release, + * running this one on a foreign CPU whose snapshots are unrelated. The + * kicked CPUs are already on their way to advance the kick_syncs being + * waited on. Don't get in the way. + */ + if (unlikely(cpu_of(rq) != smp_processor_id())) + return; + + ks = __this_cpu_read(scx_kick_syncs); + ksyncs = rcu_dereference_sched(ks)->syncs; + /* * Drop rq lock and enable IRQs while waiting. IRQs must be enabled * — a target CPU may be waiting for us to process an IPI (e.g. TLB @@ -3160,6 +3205,7 @@ static void kick_sync_wait_bal_cb(struct rq *rq) continue; } + scx_rq_lock_drop(rq); raw_spin_rq_unlock_irq(rq); while (READ_ONCE(cpu_rq(cpu)->scx.kick_sync) == ksyncs[cpu]) { smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1); @@ -3179,27 +3225,23 @@ static struct task_struct *first_local_task(struct rq *rq) struct task_struct, scx.dsq_list.node); } -static struct task_struct * -do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx) +/* + * Run dispatch and queue the follow-up work for a pick. + */ +static enum scx_dsp_verdict dispatch_pick(struct rq *rq, struct rq_flags *rf, + struct task_struct *prev) { - struct task_struct *prev = rq->curr; - bool keep_prev; - struct task_struct *p; - - /* see kick_sync_wait_bal_cb() */ - smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1); - - rq_modified_begin(rq, &ext_sched_class); + enum scx_dsp_verdict verdict; rq_unpin_lock(rq, rf); - balance_one(rq, prev); + verdict = balance_one(rq, prev); rq_repin_lock(rq, rf); maybe_queue_balance_callback(rq); /* - * Defer to a balance callback which can drop rq lock and enable - * IRQs. Waiting directly in the pick path would deadlock against - * CPUs sending us IPIs (e.g. TLB flushes) while we wait for them. + * Defer to a balance callback which can drop rq lock and enable IRQs. + * Waiting directly in the pick path would deadlock against CPUs sending + * us IPIs (e.g. TLB flushes) while we wait for them. */ if (unlikely(rq->scx.kick_sync_pending)) { rq->scx.kick_sync_pending = false; @@ -3207,10 +3249,102 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx) kick_sync_wait_bal_cb); } + if (unlikely(verdict == SCX_DSP_PREV && prev->sched_class != &ext_sched_class)) { + WARN_ON_ONCE(scx_enable_state() == SCX_ENABLED); + verdict = SCX_DSP_LOCAL; + } + + return verdict; +} + +#ifdef CONFIG_SCHED_CORE +/* + * Dispatch for a pick when core scheduling is enabled. The selection picks for + * all SMT siblings and the rq_i->core_pick state it builds must stay atomic + * throughout. If the dispatch released the rq lock, anything can have happened + * in between - return %SCX_DSP_RETRY to restart the selection against current + * state. + */ +static enum scx_dsp_verdict dispatch_core_pick(struct rq *rq, struct rq_flags *rf, + struct task_struct *prev) +{ + enum scx_dsp_verdict verdict; + u32 seq = rq->scx.lock_drop_seq; + + /* another dispatch is in flight on @rq, let that handle it */ + if (rq->scx.flags & SCX_RQ_IN_BALANCE) + return SCX_DSP_NONE; + + rq_unpin_lock(rq, rf); + + verdict = balance_one(rq, prev); + + if (cpu_of(rq) == smp_processor_id()) { + maybe_queue_balance_callback(rq); + + /* see dispatch_pick() */ + if (unlikely(rq->scx.kick_sync_pending)) { + rq->scx.kick_sync_pending = false; + queue_balance_callback(rq, &rq->scx.kick_sync_bal_cb, + kick_sync_wait_bal_cb); + } + } else if (unlikely(rq->scx.flags & SCX_RQ_BAL_CB_PENDING)) { + /* + * Balance callbacks must run in the context that queued them, + * so they can't be queued on another CPU's rq. Run the deferred + * work directly instead. + */ + rq->scx.flags &= ~SCX_RQ_BAL_CB_PENDING; + run_deferred(rq); + } + + rq_repin_lock(rq, rf); + + /* if balance_one() released the rq lock, restart the selection */ + if (rq->scx.lock_drop_seq != seq) + return SCX_DSP_RETRY; + + /* see dispatch_pick() */ + if (unlikely(verdict == SCX_DSP_PREV && + prev->sched_class != &ext_sched_class)) { + WARN_ON_ONCE(scx_enable_state() == SCX_ENABLED); + verdict = SCX_DSP_LOCAL; + } + + return verdict; +} +#else /* CONFIG_SCHED_CORE */ +static enum scx_dsp_verdict dispatch_core_pick(struct rq *rq, struct rq_flags *rf, + struct task_struct *prev) +{ + return SCX_DSP_NONE; +} +#endif /* CONFIG_SCHED_CORE */ + +static struct task_struct * +do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx) +{ + struct task_struct *prev = rq->curr; + enum scx_dsp_verdict verdict; + struct task_struct *p; + + /* see kick_sync_wait_bal_cb() */ + smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1); + + rq_modified_begin(rq, &ext_sched_class); + + if (sched_core_enabled(rq)) + verdict = dispatch_core_pick(rq, rf, prev); + else + verdict = dispatch_pick(rq, rf, prev); + + if (verdict == SCX_DSP_RETRY) + return RETRY_TASK; + /* - * If any higher-priority sched class enqueued a runnable task on - * this rq during balance_one(), abort and return RETRY_TASK, so - * that the scheduler loop can restart. + * If any higher-priority sched class enqueued a runnable task on this + * rq during balance_one(), abort and return RETRY_TASK, so that the + * scheduler loop can restart. * * If @force_scx is true, always try to pick a SCHED_EXT task, * regardless of any higher-priority sched classes activity. @@ -3218,19 +3352,11 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx) if (!force_scx && rq_modified_above(rq, &ext_sched_class)) return RETRY_TASK; - keep_prev = rq->scx.flags & SCX_RQ_BAL_KEEP; - if (unlikely(keep_prev && - prev->sched_class != &ext_sched_class)) { - WARN_ON_ONCE(scx_enable_state() == SCX_ENABLED); - keep_prev = false; - } - /* - * If balance_one() is telling us to keep running @prev, replenish slice - * if necessary and keep running @prev. Otherwise, pop the first one - * from the local DSQ. + * If we're keeping @prev, replenish slice if necessary and keep running + * @prev. Otherwise, pop the first one from the local DSQ. */ - if (keep_prev) { + if (verdict == SCX_DSP_PREV) { p = prev; if (!p->scx.slice) refill_task_slice_dfl(scx_task_sched(p), p); @@ -3305,6 +3431,8 @@ void ext_server_init(struct rq *rq) * * When ops.core_sched_before() is enabled, @p->scx.core_sched_at is used to * implement FIFO ordering within each local DSQ. See pick_task_scx(). + * + * Return: %true if @a should run after @b. */ bool scx_prio_less(const struct task_struct *a, const struct task_struct *b, bool in_fi) @@ -3313,6 +3441,10 @@ bool scx_prio_less(const struct task_struct *a, const struct task_struct *b, struct scx_sched *sch_b = scx_task_sched(b); /* + * scx_prio_less() returns whether @a should run after @b while + * ops.core_sched_before() returns whether its first argument should run + * before the second. Swap the arguments. + * * The const qualifiers are dropped from task_struct pointers when * calling ops.core_sched_before(). Accesses are controlled by the * verifier. @@ -3321,8 +3453,8 @@ bool scx_prio_less(const struct task_struct *a, const struct task_struct *b, !scx_bypassing(sch_a, task_cpu(a))) return SCX_CALL_OP_2TASKS_RET(sch_a, core_sched_before, task_rq(a), - (struct task_struct *)a, - (struct task_struct *)b); + (struct task_struct *)b, + (struct task_struct *)a); else return time_after64(a->scx.core_sched_at, b->scx.core_sched_at); } @@ -3877,7 +4009,7 @@ void scx_post_fork(struct task_struct *p) void scx_cancel_fork(struct task_struct *p) { - if (scx_enabled()) { + if (scx_init_task_enabled) { struct rq *rq; struct rq_flags rf; @@ -4258,8 +4390,10 @@ static void reenq_user(struct rq *rq, struct scx_dispatch_q *dsq, u64 reenq_flag task_rq = task_rq(p); if (locked_rq != task_rq) { - if (locked_rq) + if (locked_rq) { + scx_rq_lock_drop(locked_rq); raw_spin_rq_unlock(locked_rq); + } if (unlikely(!raw_spin_rq_trylock(task_rq))) { raw_spin_unlock(&dsq->lock); raw_spin_rq_lock(task_rq); @@ -4285,6 +4419,7 @@ static void reenq_user(struct rq *rq, struct scx_dispatch_q *dsq, u64 reenq_flag p->scx.flags &= ~SCX_TASK_REENQ_REASON_MASK; if (!(++nr_enqueued % SCX_TASK_ITER_BATCH)) { + scx_rq_lock_drop(locked_rq); raw_spin_rq_unlock(locked_rq); locked_rq = NULL; cpu_relax(); @@ -4297,8 +4432,10 @@ static void reenq_user(struct rq *rq, struct scx_dispatch_q *dsq, u64 reenq_flag raw_spin_unlock(&dsq->lock); if (locked_rq != rq) { - if (locked_rq) + if (locked_rq) { + scx_rq_lock_drop(locked_rq); raw_spin_rq_unlock(locked_rq); + } raw_spin_rq_lock(rq); } } @@ -4309,7 +4446,7 @@ static void process_deferred_reenq_users(struct rq *rq) while (true) { struct scx_dispatch_q *dsq; - u64 reenq_flags; + u64 dsq_id, reenq_flags; scoped_guard (raw_spinlock, &rq->scx.deferred_reenq_lock) { struct scx_deferred_reenq_user *dru = @@ -4332,7 +4469,12 @@ static void process_deferred_reenq_users(struct rq *rq) /* see schedule_dsq_reenq() */ smp_mb(); - BUG_ON(dsq->id & SCX_DSQ_FLAG_BUILTIN); + /* destroy_dsq() may have raced and invalidated @dsq, nothing to reenq */ + dsq_id = READ_ONCE(dsq->id); + if (unlikely(dsq_id == SCX_DSQ_INVALID)) + continue; + + BUG_ON(dsq_id & SCX_DSQ_FLAG_BUILTIN); reenq_user(rq, dsq, reenq_flags); } } @@ -5350,6 +5492,7 @@ static u32 bypass_lb_cpu(struct scx_sched *sch, s32 donor, if (!(nr_balanced % SCX_BYPASS_LB_BATCH) && n) { list_move_tail(&cursor.node, &n->scx.dsq_list.node); raw_spin_unlock(&donor_dsq->lock); + scx_rq_lock_drop(donor_rq); raw_spin_rq_unlock_irq(donor_rq); cpu_relax(); raw_spin_rq_lock_irq(donor_rq); @@ -5360,6 +5503,7 @@ static u32 bypass_lb_cpu(struct scx_sched *sch, s32 donor, list_del_init(&cursor.node); raw_spin_unlock(&donor_dsq->lock); + scx_rq_lock_drop(donor_rq); raw_spin_rq_unlock_irq(donor_rq); return nr_balanced; @@ -5573,7 +5717,7 @@ static void disable_bypass_dsp(struct scx_sched *sch) * * - ops.dispatch() is ignored. * - * - balance_one() does not set %SCX_RQ_BAL_KEEP on non-zero slice as slice + * - balance_one() does not report %SCX_DSP_PREV on non-zero slice as slice * can't be trusted. Whenever a tick triggers, the running task is rotated to * the tail of the queue with core_sched_at touched. * @@ -5650,6 +5794,7 @@ static void scx_bypass(struct scx_sched *sch, bool bypass) * sees scx_bypassing() before moving tasks to SCX. */ if (!scx_enabled()) { + scx_rq_lock_drop(rq); raw_spin_rq_unlock(rq); continue; } @@ -5676,6 +5821,7 @@ static void scx_bypass(struct scx_sched *sch, bool bypass) if (cpu_online(cpu) || cpu == smp_processor_id()) resched_curr(rq); + scx_rq_lock_drop(rq); raw_spin_rq_unlock(rq); } @@ -8403,6 +8549,7 @@ static bool kick_one_cpu(s32 cpu, struct rq *this_rq, unsigned long *ksyncs) cpumask_clear_cpu(cpu, this_scx->cpus_to_wait); } + scx_rq_lock_drop(rq); raw_spin_rq_unlock_irqrestore(rq, flags); return should_wait; @@ -8419,6 +8566,7 @@ static void kick_one_cpu_if_idle(s32 cpu, struct rq *this_rq) (cpu_online(cpu) || cpu == cpu_of(this_rq))) resched_curr(rq); + scx_rq_lock_drop(rq); raw_spin_rq_unlock_irqrestore(rq, flags); } @@ -8456,6 +8604,7 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work) raw_spin_rq_lock(this_rq); this_scx->kick_sync_pending = true; resched_curr(this_rq); + scx_rq_lock_drop(this_rq); raw_spin_rq_unlock(this_rq); } } @@ -8865,9 +9014,8 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, { struct scx_dispatch_q *src_dsq = kit->dsq, *dst_dsq; struct scx_sched *sch; - struct rq *this_rq, *src_rq, *locked_rq; + struct rq *p_rq, *src_rq, *locked_rq; bool dispatched = false; - bool in_balance; unsigned long flags; /* @@ -8897,24 +9045,28 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, } /* - * Can be called from either ops.dispatch() locking this_rq() or any - * context where no rq lock is held. If latter, lock @p's task_rq which - * we'll likely need anyway. + * Can be called from either ops.dispatch() holding the dispatched rq's + * lock or any context where no rq lock is held. If latter, lock @p's + * task_rq which we'll likely need anyway. */ src_rq = task_rq(p); local_irq_save(flags); - this_rq = this_rq(); - in_balance = this_rq->scx.flags & SCX_RQ_IN_BALANCE; - if (in_balance) { - if (this_rq != src_rq) - switch_rq_lock(this_rq, src_rq); + /* + * Under core scheduling, dispatch can run for a sibling rq, so the + * locked rq is not necessarily this CPU's. + */ + locked_rq = scx_locked_rq(); + + if (locked_rq) { + if (locked_rq != src_rq) + switch_rq_lock(locked_rq, src_rq); } else { raw_spin_rq_lock(src_rq); } - locked_rq = src_rq; + p_rq = src_rq; raw_spin_lock(&src_dsq->lock); /* did someone else get to it while we dropped the locks? */ @@ -8924,7 +9076,7 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, } /* @p is still on $src_dsq and stable, determine the destination */ - dst_dsq = find_dsq_for_dispatch(sch, this_rq, dsq_id, task_cpu(p)); + dst_dsq = find_dsq_for_dispatch(sch, locked_rq ?: this_rq(), dsq_id, task_cpu(p)); /* * Apply vtime and slice updates before moving so that the new time is @@ -8937,14 +9089,15 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit, p->scx.slice = kit->slice; /* execute move */ - locked_rq = move_task_between_dsqs(sch, p, enq_flags, src_dsq, dst_dsq); + p_rq = move_task_between_dsqs(sch, p, enq_flags, src_dsq, dst_dsq); dispatched = true; out: - if (in_balance) { - if (this_rq != locked_rq) - switch_rq_lock(locked_rq, this_rq); + if (locked_rq) { + if (locked_rq != p_rq) + switch_rq_lock(p_rq, locked_rq); } else { - raw_spin_rq_unlock_irqrestore(locked_rq, flags); + scx_rq_lock_drop(p_rq); + raw_spin_rq_unlock_irqrestore(p_rq, flags); } kit->cursor.flags &= ~(__SCX_DSQ_ITER_HAS_SLICE | @@ -9182,7 +9335,7 @@ __bpf_kfunc bool scx_bpf_dsq_move_vtime(struct bpf_iter_scx_dsq *it__iter, */ __bpf_kfunc bool scx_bpf_sub_dispatch(u64 cgroup_id, const struct bpf_prog_aux *aux) { - struct rq *this_rq = this_rq(); + struct rq *rq = scx_locked_rq(); struct scx_sched *parent, *child; guard(rcu)(); @@ -9201,8 +9354,8 @@ __bpf_kfunc bool scx_bpf_sub_dispatch(u64 cgroup_id, const struct bpf_prog_aux * return false; } - return scx_dispatch_sched(child, this_rq, this_rq->scx.sub_dispatch_prev, - true); + return scx_dispatch_sched(child, rq, rq->scx.sub_dispatch_prev, true) != + SCX_DSP_NONE; } #endif /* CONFIG_EXT_SUB_SCHED */ @@ -9423,9 +9576,11 @@ static void scx_kick_cpu(struct scx_sched *sch, s32 cpu, u64 flags) if (raw_spin_rq_trylock(target_rq)) { if (can_skip_idle_kick(target_rq)) { + scx_rq_lock_drop(target_rq); raw_spin_rq_unlock(target_rq); goto out; } + scx_rq_lock_drop(target_rq); raw_spin_rq_unlock(target_rq); } cpumask_set_cpu(cpu, this_rq->scx.cpus_to_kick_if_idle); @@ -9496,6 +9651,10 @@ __bpf_kfunc s32 scx_bpf_kick_cid(s32 cid, u64 flags, const struct bpf_prog_aux * * * Return the number of tasks in the DSQ matching @dsq_id. If not found, * -%ENOENT is returned. + * + * %SCX_DSQ_LOCAL resolves to the local DSQ of the rq the current scheduler + * operation is locked to - e.g. the rq being dispatched for in ops.dispatch() - + * or the calling CPU's when no rq is locked. */ __bpf_kfunc s32 scx_bpf_dsq_nr_queued(u64 dsq_id, const struct bpf_prog_aux *aux) { @@ -9512,7 +9671,7 @@ __bpf_kfunc s32 scx_bpf_dsq_nr_queued(u64 dsq_id, const struct bpf_prog_aux *aux } if (dsq_id == SCX_DSQ_LOCAL) { - ret = READ_ONCE(this_rq()->scx.local_dsq.nr); + ret = READ_ONCE((scx_locked_rq() ?: this_rq())->scx.local_dsq.nr); goto out; } else if ((dsq_id & SCX_DSQ_LOCAL_ON) == SCX_DSQ_LOCAL_ON) { s32 cpu = scx_cpu_ret(sch, dsq_id & SCX_DSQ_LOCAL_CPU_MASK); @@ -9691,10 +9850,15 @@ __bpf_kfunc struct task_struct *scx_bpf_dsq_peek(u64 dsq_id, * - User DSQs * * Re-enqueues are performed asynchronously. Can be called from anywhere. + * + * %SCX_DSQ_LOCAL resolves to the local DSQ of the rq the current scheduler + * operation is locked to - e.g. the rq being dispatched for in ops.dispatch() - + * or the calling CPU's when no rq is locked. */ __bpf_kfunc void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags, const struct bpf_prog_aux *aux) { + struct rq *locked_rq = scx_locked_rq(); struct scx_sched *sch; struct scx_dispatch_q *dsq; @@ -9713,8 +9877,8 @@ __bpf_kfunc void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags, if (!(reenq_flags & __SCX_REENQ_FILTER_MASK)) reenq_flags |= SCX_REENQ_ANY; - dsq = find_dsq_for_dispatch(sch, this_rq(), dsq_id, smp_processor_id()); - schedule_dsq_reenq(sch, dsq, reenq_flags, scx_locked_rq()); + dsq = find_dsq_for_dispatch(sch, locked_rq ?: this_rq(), dsq_id, smp_processor_id()); + schedule_dsq_reenq(sch, dsq, reenq_flags, locked_rq); } /** diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 56acf502ba260a..f76e6f7f4a0147 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -784,7 +784,6 @@ enum scx_rq_flags { */ SCX_RQ_ONLINE = 1 << 0, SCX_RQ_CAN_STOP_TICK = 1 << 1, - SCX_RQ_BAL_KEEP = 1 << 3, /* balance decided to keep current */ SCX_RQ_CLK_VALID = 1 << 5, /* RQ clock is fresh and valid */ SCX_RQ_BAL_CB_PENDING = 1 << 6, /* must queue a cb after dispatching */ @@ -804,6 +803,9 @@ struct scx_rq { bool cpu_released; u32 flags; u32 nr_immed; /* ENQ_IMMED tasks on local_dsq */ +#ifdef CONFIG_SCHED_CORE + u32 lock_drop_seq; /* nr dispatch lock releases */ +#endif u64 clock; /* current per-rq clock -- see scx_bpf_now() */ cpumask_var_t cpus_to_kick; cpumask_var_t cpus_to_kick_if_idle; @@ -1358,6 +1360,7 @@ struct rq { unsigned int core_forceidle_seq; unsigned int core_forceidle_occupation; u64 core_forceidle_start; + unsigned int core_pick_in_flight; #endif /* CONFIG_SCHED_CORE */ /* Scratch cpumask to be temporarily used under rq_lock */ diff --git a/kernel/signal.c b/kernel/signal.c index bbc0fd4cc4d7c1..c53f202da9daaa 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1181,6 +1181,7 @@ static inline bool has_si_pid_and_uid(struct kernel_siginfo *info) int send_signal_locked(int sig, struct kernel_siginfo *info, struct task_struct *t, enum pid_type type) { + struct kernel_siginfo rewritten; /* Should SIGKILL or SIGSTOP be received by a pid namespace init? */ bool force = false; @@ -1194,6 +1195,9 @@ int send_signal_locked(int sig, struct kernel_siginfo *info, /* SIGKILL and SIGSTOP is special or has ids */ struct user_namespace *t_user_ns; + rewritten = *info; + info = &rewritten; + rcu_read_lock(); t_user_ns = task_cred_xxx(t, user_ns); if (current_user_ns() != t_user_ns) { diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 2cd0172d051606..05a905e64fa1e3 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -368,10 +368,9 @@ static int parse(struct nlattr *na, struct cpumask *mask) return -E2BIG; if (len < 1) return -EINVAL; - data = kmalloc(len, GFP_KERNEL); + data = nla_strdup(na, GFP_KERNEL); if (!data) return -ENOMEM; - nla_strscpy(data, na, len); ret = cpulist_parse(data, mask); kfree(data); return ret; diff --git a/kernel/time/itimer.c b/kernel/time/itimer.c index 7c6110e964e7ec..03a32dffc56cc3 100644 --- a/kernel/time/itimer.c +++ b/kernel/time/itimer.c @@ -100,7 +100,7 @@ static int do_getitimer(int which, struct itimerspec64 *value) static int put_itimerval(struct __kernel_old_itimerval __user *o, const struct itimerspec64 *i) { - struct __kernel_old_itimerval v; + struct __kernel_old_itimerval v = {}; v.it_interval.tv_sec = i->it_interval.tv_sec; v.it_interval.tv_usec = i->it_interval.tv_nsec / NSEC_PER_USEC; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index b1b5ec43c0f27b..696bb119c56ef2 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -2943,10 +2943,12 @@ static int __do_adjtimex(struct tk_data *tkd, struct __kernel_timex *txc, return ret; add_device_randomness(txc, sizeof(*txc)); - if (!aux_clock) + if (!aux_clock) { ktime_get_real_ts64(&ts); - else - tk_get_aux_ts64(tkd->timekeeper.id, &ts); + } else { + if (!tk_get_aux_ts64(tkd->timekeeper.id, &ts)) + return -ENODEV; + } add_device_randomness(&ts, sizeof(ts)); diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 655a8c6cd84d71..ae9abf14688e01 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -2492,6 +2492,7 @@ static void migrate_timer_list(struct timer_base *new_base, struct hlist_head *h timer = hlist_entry(head->first, struct timer_list, entry); detach_timer(timer, false); timer->flags = (timer->flags & ~TIMER_BASEMASK) | cpu; + debug_timer_activate(timer); internal_add_timer(new_base, timer); } } diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 7c50f8ae5a0c42..a4ecba1518ded3 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -75,6 +75,8 @@ .func_hash = &opsname.local_hash, \ .local_hash.regex_lock = __MUTEX_INITIALIZER(opsname.local_hash.regex_lock), \ .subop_list = LIST_HEAD_INIT(opsname.subop_list), +/* Used only to synchronize the initialization of ftrace_ops */ +static DEFINE_MUTEX(ops_mutex); #else #define INIT_OPS_HASH(opsname) #endif @@ -159,11 +161,18 @@ const struct ftrace_ops ftrace_nop_ops = { static inline void ftrace_ops_init(struct ftrace_ops *ops) { #ifdef CONFIG_DYNAMIC_FTRACE - if (!(ops->flags & FTRACE_OPS_FL_INITIALIZED)) { + unsigned long flags = smp_load_acquire(&ops->flags); + + if (!(flags & FTRACE_OPS_FL_INITIALIZED)) { + guard(mutex)(&ops_mutex); + /* Could have been initialized before lock taken */ + if (unlikely(ops->flags & FTRACE_OPS_FL_INITIALIZED)) + return; mutex_init(&ops->local_hash.regex_lock); INIT_LIST_HEAD(&ops->subop_list); ops->func_hash = &ops->local_hash; - ops->flags |= FTRACE_OPS_FL_INITIALIZED; + flags = ops->flags | FTRACE_OPS_FL_INITIALIZED; + smp_store_release(&ops->flags, flags); } #endif } @@ -4677,7 +4686,8 @@ ftrace_avail_addrs_open(struct inode *inode, struct file *file) /** * ftrace_regex_open - initialize function tracer filter files - * @ops: The ftrace_ops that hold the hash filters + * @tr: The trace_array that holds the ftrace_ops [optional] + * @ops: The ftrace_ops that hold the hash filters [optional] * @flag: The type of filter to process * @inode: The inode, usually passed in to your open routine * @file: The file, usually passed in to your open routine @@ -4691,26 +4701,45 @@ ftrace_avail_addrs_open(struct inode *inode, struct file *file) * tracing_lseek() should be used as the lseek routine, and * release must call ftrace_regex_release(). * + * Note, If @tr is not NULL, its reference has to be taken before + * @ops may be referenced. + * If @ops is NULL and @tr is not, then @tr->ops is used. + * If @tr is NULL and @ops is not then @ops->private is uesd for @tr. + * If both @tr and @ops are NULL, then the &global_ops is + * to be used, and @tr will be the global_ops.private pointer. + * * Returns: 0 on success or a negative errno value on failure */ int -ftrace_regex_open(struct ftrace_ops *ops, int flag, +ftrace_regex_open(struct trace_array *tr, struct ftrace_ops *ops, int flag, struct inode *inode, struct file *file) { - struct ftrace_iterator *iter; + struct ftrace_iterator *iter = NULL; struct ftrace_hash *hash; struct list_head *mod_head; - struct trace_array *tr = ops->private; - int ret = -ENOMEM; - - ftrace_ops_init(ops); + int ret = -ENODEV; if (unlikely(ftrace_disabled)) return -ENODEV; + if (!tr) { + if (!ops) + ops = &global_ops; + tr = ops->private; + } + if (tracing_check_open_get_tr(tr)) return -ENODEV; + if (!ops) + ops = tr->ops; + + if (WARN_ON_ONCE(!ops)) + goto out; + + ftrace_ops_init(ops); + + ret = -ENOMEM; iter = kzalloc_obj(*iter); if (!iter) goto out; @@ -4788,21 +4817,19 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, static int ftrace_filter_open(struct inode *inode, struct file *file) { - struct ftrace_ops *ops = inode->i_private; + struct trace_array *tr = inode->i_private; - /* Checks for tracefs lockdown */ - return ftrace_regex_open(ops, - FTRACE_ITER_FILTER | FTRACE_ITER_DO_PROBES, - inode, file); + return ftrace_regex_open(tr, NULL, + FTRACE_ITER_FILTER | FTRACE_ITER_DO_PROBES, + inode, file); } static int ftrace_notrace_open(struct inode *inode, struct file *file) { - struct ftrace_ops *ops = inode->i_private; + struct trace_array *tr = inode->i_private; - /* Checks for tracefs lockdown */ - return ftrace_regex_open(ops, FTRACE_ITER_NOTRACE, + return ftrace_regex_open(tr, NULL, FTRACE_ITER_NOTRACE, inode, file); } @@ -7492,15 +7519,15 @@ static const struct file_operations ftrace_graph_notrace_fops = { }; #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ -void ftrace_create_filter_files(struct ftrace_ops *ops, +void ftrace_create_filter_files(struct trace_array *tr, struct dentry *parent) { trace_create_file("set_ftrace_filter", TRACE_MODE_WRITE, parent, - ops, &ftrace_filter_fops); + tr, &ftrace_filter_fops); trace_create_file("set_ftrace_notrace", TRACE_MODE_WRITE, parent, - ops, &ftrace_notrace_fops); + tr, &ftrace_notrace_fops); } /* @@ -7525,7 +7552,6 @@ void ftrace_destroy_filter_files(struct ftrace_ops *ops) static __init int ftrace_init_dyn_tracefs(struct dentry *d_tracer) { - trace_create_file("available_filter_functions", TRACE_MODE_READ, d_tracer, NULL, &ftrace_avail_fops); @@ -7538,7 +7564,7 @@ static __init int ftrace_init_dyn_tracefs(struct dentry *d_tracer) trace_create_file("touched_functions", TRACE_MODE_READ, d_tracer, NULL, &ftrace_touched_fops); - ftrace_create_filter_files(&global_ops, d_tracer); + ftrace_create_filter_files(NULL, d_tracer); #ifdef CONFIG_FUNCTION_GRAPH_TRACER trace_create_file("set_graph_function", TRACE_MODE_WRITE, d_tracer, diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 2667992f0aa294..eb01f1d6506665 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -391,6 +391,17 @@ static __always_inline unsigned int rb_page_size(struct buffer_page *bpage) return rb_data_page_size(bpage->page); } +/** + * rb_page_capacity - Get the capacity of a buffer page + * @bpage: The buffer page + * + * Return: The maximum size available for events in the given buffer page. + */ +static __always_inline unsigned int rb_page_capacity(struct buffer_page *bpage) +{ + return (PAGE_SIZE << bpage->order) - BUF_PAGE_HDR_SIZE; +} + static void free_buffer_page(struct buffer_page *bpage) { /* Range pages are not to be freed */ @@ -507,7 +518,7 @@ struct ring_buffer_per_cpu { raw_spinlock_t reader_lock; /* serialize readers */ arch_spinlock_t lock; struct lock_class_key lock_key; - struct buffer_data_page *free_page; + struct buffer_data_read_page free_page; unsigned long nr_pages; unsigned int current_context; struct list_head *pages; @@ -586,11 +597,42 @@ struct trace_buffer { struct ring_buffer_meta *meta; - unsigned int subbuf_size; unsigned int subbuf_order; unsigned int max_data_size; }; +static __always_inline unsigned int rb_subbuf_size(struct trace_buffer *buffer) +{ + return PAGE_SIZE << buffer->subbuf_order; +} + +/** + * rb_subbuf_capacity - Get the capacity of a subbuffer + * @buffer: A trace buffer + * + * Unsafe to use without holding trace_buffer::mutex or with resizing enabled. + * Consider rb_page_capacity() instead. + * + * Return: The maximum size available for events in a trace buffer subbuffer. + */ +static __always_inline unsigned int rb_subbuf_capacity(struct trace_buffer *buffer) +{ + return rb_subbuf_size(buffer) - BUF_PAGE_HDR_SIZE; +} + +/** + * rb_subbuf_start - Get the start address of a subbuffer + * @buffer: A trace buffer + * @addr: An address of an event on a subbuffer + * + * Return: The start of the subbuffer for where @addr sits + */ +static __always_inline +unsigned long rb_subbuf_start(struct trace_buffer *buffer, unsigned long addr) +{ + return addr & ~((unsigned long)(rb_subbuf_size(buffer) - 1)); +} + struct ring_buffer_iter { struct ring_buffer_per_cpu *cpu_buffer; unsigned long head; @@ -630,7 +672,7 @@ int ring_buffer_print_page_header(struct trace_buffer *buffer, struct trace_seq trace_seq_printf(s, "\tfield: char data;\t" "offset:%u;\tsize:%u;\tsigned:%u;\n", (unsigned int)offsetof(typeof(field), data), - (unsigned int)(buffer ? buffer->subbuf_size : + (unsigned int)(buffer ? rb_subbuf_capacity(buffer) : PAGE_SIZE - BUF_PAGE_HDR_SIZE), (unsigned int)is_signed_type(char)); @@ -1620,7 +1662,7 @@ rb_range_align_subbuf(unsigned long addr, int subbuf_size, int nr_subbufs) */ static void *rb_range_meta(struct trace_buffer *buffer, int nr_pages, int cpu) { - int subbuf_size = buffer->subbuf_size + BUF_PAGE_HDR_SIZE; + int subbuf_size = rb_subbuf_size(buffer); struct ring_buffer_cpu_meta *meta; struct ring_buffer_meta *bmeta; unsigned long ptr; @@ -2432,8 +2474,8 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, bpage->id = i + 1; cpu_buffer->subbuf_ids[i + 1] = bpage; } else { - int order = cpu_buffer->buffer->subbuf_order; - bpage->page = alloc_cpu_data(cpu_buffer->cpu, order); + bpage->page = alloc_cpu_data(cpu_buffer->cpu, + cpu_buffer->buffer->subbuf_order); if (!bpage->page) goto free_pages; } @@ -2556,8 +2598,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) bpage->range = 1; cpu_buffer->subbuf_ids[0] = bpage; } else { - int order = cpu_buffer->buffer->subbuf_order; - bpage->page = alloc_cpu_data(cpu, order); + bpage->page = alloc_cpu_data(cpu, bpage->order); if (!bpage->page) goto fail_free_reader; } @@ -2631,7 +2672,7 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer) free_buffer_page(bpage); } - free_page((unsigned long)cpu_buffer->free_page); + free_pages((unsigned long)cpu_buffer->free_page.data, cpu_buffer->free_page.order); kfree(cpu_buffer); } @@ -2731,10 +2772,9 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, buffer->subbuf_order = order; subbuf_size = (PAGE_SIZE << order); - buffer->subbuf_size = subbuf_size - BUF_PAGE_HDR_SIZE; /* Max payload is buffer page size - header (8bytes) */ - buffer->max_data_size = buffer->subbuf_size - (sizeof(u32) * 2); + buffer->max_data_size = rb_subbuf_capacity(buffer) - (sizeof(u32) * 2); buffer->flags = flags; buffer->clock = trace_clock_local; @@ -2818,9 +2858,8 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, if (nr_pages < 2) goto fail_free_buffers; } else { - /* need at least two pages */ - nr_pages = DIV_ROUND_UP(size, buffer->subbuf_size); + nr_pages = DIV_ROUND_UP(size, rb_subbuf_capacity(buffer)); if (nr_pages < 2) nr_pages = 2; } @@ -3203,7 +3242,7 @@ static void update_pages_handler(struct work_struct *work) * @size: the new size. * @cpu_id: the cpu buffer to resize * - * Minimum size is 2 * buffer->subbuf_size. + * Minimum size is 2 * rb_subbuf_capacity(buffer). * * Returns 0 on success and < 0 on failure. */ @@ -3225,12 +3264,6 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size, !cpumask_test_cpu(cpu_id, buffer->cpumask)) return 0; - nr_pages = DIV_ROUND_UP(size, buffer->subbuf_size); - - /* we need a minimum of two pages */ - if (nr_pages < 2) - nr_pages = 2; - /* * Keep CPUs from coming online while resizing to synchronize * with new per CPU buffers being created. @@ -3241,6 +3274,12 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size, mutex_lock(&buffer->mutex); atomic_inc(&buffer->resizing); + nr_pages = DIV_ROUND_UP(size, rb_subbuf_capacity(buffer)); + + /* we need a minimum of two pages */ + if (nr_pages < 2) + nr_pages = 2; + if (cpu_id == RING_BUFFER_ALL_CPUS) { /* * Don't succeed if resizing is disabled, as a reader might be @@ -3513,7 +3552,7 @@ rb_event_index(struct ring_buffer_per_cpu *cpu_buffer, struct ring_buffer_event { unsigned long addr = (unsigned long)event; - addr &= (PAGE_SIZE << cpu_buffer->buffer->subbuf_order) - 1; + addr &= (unsigned long)rb_subbuf_size(cpu_buffer->buffer) - 1; return addr - BUF_PAGE_HDR_SIZE; } @@ -3755,8 +3794,8 @@ static inline void rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer, unsigned long tail, struct rb_event_info *info) { - unsigned long bsize = READ_ONCE(cpu_buffer->buffer->subbuf_size); struct buffer_page *tail_page = info->tail_page; + unsigned long bsize = rb_page_capacity(tail_page); struct ring_buffer_event *event; unsigned long length = info->length; @@ -4101,8 +4140,7 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, new_index = rb_event_index(cpu_buffer, event); old_index = new_index + rb_event_ts_length(event); - addr = (unsigned long)event; - addr &= ~((PAGE_SIZE << cpu_buffer->buffer->subbuf_order) - 1); + addr = rb_subbuf_start(cpu_buffer->buffer, (unsigned long)event); bpage = READ_ONCE(cpu_buffer->tail_page); @@ -4767,7 +4805,7 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, tail = write - info->length; /* See if we shot pass the end of this buffer page */ - if (unlikely(write > cpu_buffer->buffer->subbuf_size)) { + if (unlikely(write > rb_page_capacity(tail_page))) { check_buffer(cpu_buffer, info, CHECK_FULL_PAGE); return rb_move_tail(cpu_buffer, tail, info); } @@ -5012,7 +5050,7 @@ rb_decrement_entry(struct ring_buffer_per_cpu *cpu_buffer, struct buffer_page *bpage = cpu_buffer->commit_page; struct buffer_page *start; - addr &= ~((PAGE_SIZE << cpu_buffer->buffer->subbuf_order) - 1); + addr = rb_subbuf_start(cpu_buffer->buffer, addr); /* Do the likely case first */ if (likely(bpage->page == (void *)addr)) { @@ -5753,8 +5791,11 @@ __rb_get_reader_page_from_remote(struct ring_buffer_per_cpu *cpu_buffer) prev_reader = cpu_buffer->subbuf_ids[cpu_buffer->meta_page->reader.id]; - WARN_ON_ONCE(cpu_buffer->remote->swap_reader_page(cpu_buffer->cpu, - cpu_buffer->remote->priv)); + if (cpu_buffer->remote->swap_reader_page(cpu_buffer->cpu, + cpu_buffer->remote->priv)) { + pr_warn_ratelimited("Remote reader page swap failed\n"); + return NULL; + } /* nr_pages doesn't include the reader page */ if (WARN_ON_ONCE(cpu_buffer->meta_page->reader.id > cpu_buffer->nr_pages)) return NULL; @@ -5799,7 +5840,6 @@ static struct buffer_page * __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer) { int max_loops = cpu_buffer->ring_meta ? cpu_buffer->nr_pages : 3; - unsigned long bsize = READ_ONCE(cpu_buffer->buffer->subbuf_size); struct buffer_page *reader = NULL; unsigned long overwrite; unsigned long flags; @@ -5947,7 +5987,7 @@ __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer) #define USECS_WAIT 1000000 for (nr_loops = 0; nr_loops < USECS_WAIT; nr_loops++) { /* If the write is past the end of page, a writer is still updating it */ - if (likely(!reader || rb_page_write(reader) <= bsize)) + if (likely(!reader || rb_page_write(reader) <= rb_page_capacity(reader))) break; udelay(1); @@ -6380,36 +6420,44 @@ EXPORT_SYMBOL_GPL(ring_buffer_consume); struct ring_buffer_iter * ring_buffer_read_start(struct trace_buffer *buffer, int cpu, gfp_t flags) { + struct ring_buffer_iter *iter __free(kfree) = kzalloc_obj(*iter, flags); struct ring_buffer_per_cpu *cpu_buffer; - struct ring_buffer_iter *iter; - if (!cpumask_test_cpu(cpu, buffer->cpumask)) - return NULL; - - iter = kzalloc_obj(*iter, flags); if (!iter) return NULL; - /* Holds the entire event: data and meta data */ - iter->event_size = buffer->subbuf_size; - iter->event = kmalloc(iter->event_size, flags); - if (!iter->event) { - kfree(iter); + if (!cpumask_test_cpu(cpu, buffer->cpumask)) return NULL; - } cpu_buffer = buffer->buffers[cpu]; - iter->cpu_buffer = cpu_buffer; + /* + * Only KDB is using GFP_ATOMIC, for the others, lock the buffer to + * prevent concurrent resizing. + */ + if (gfpflags_allow_blocking(flags)) + mutex_lock(&buffer->mutex); atomic_inc(&cpu_buffer->resize_disabled); + if (gfpflags_allow_blocking(flags)) + mutex_unlock(&buffer->mutex); + + /* Holds the entire event: data and meta data. */ + iter->event_size = rb_page_capacity(READ_ONCE(cpu_buffer->reader_page)); + iter->event = kmalloc(iter->event_size, flags); + if (!iter->event) { + atomic_dec(&cpu_buffer->resize_disabled); + return NULL; + } + iter->cpu_buffer = cpu_buffer; + guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock); arch_spin_lock(&cpu_buffer->lock); rb_iter_reset(iter); arch_spin_unlock(&cpu_buffer->lock); - return iter; + return_ptr(iter); } EXPORT_SYMBOL_GPL(ring_buffer_read_start); @@ -6463,7 +6511,7 @@ unsigned long ring_buffer_size(struct trace_buffer *buffer, int cpu) if (!cpumask_test_cpu(cpu, buffer->cpumask)) return 0; - return buffer->subbuf_size * buffer->buffers[cpu]->nr_pages; + return rb_subbuf_capacity(buffer) * buffer->buffers[cpu]->nr_pages; } EXPORT_SYMBOL_GPL(ring_buffer_size); @@ -6962,9 +7010,9 @@ ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu) local_irq_save(flags); arch_spin_lock(&cpu_buffer->lock); - if (cpu_buffer->free_page) { - bpage->data = cpu_buffer->free_page; - cpu_buffer->free_page = NULL; + if (cpu_buffer->free_page.data) { + *bpage = cpu_buffer->free_page; + cpu_buffer->free_page.data = NULL; } arch_spin_unlock(&cpu_buffer->lock); @@ -6973,7 +7021,7 @@ ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu) if (bpage->data) { rb_init_data_page(bpage->data); } else { - bpage->data = alloc_cpu_data(cpu, cpu_buffer->buffer->subbuf_order); + bpage->data = alloc_cpu_data(cpu, bpage->order); if (!bpage->data) { kfree(bpage); return ERR_PTR(-ENOMEM); @@ -7016,8 +7064,8 @@ void ring_buffer_free_read_page(struct trace_buffer *buffer, int cpu, local_irq_save(flags); arch_spin_lock(&cpu_buffer->lock); - if (!cpu_buffer->free_page) { - cpu_buffer->free_page = dpage; + if (!cpu_buffer->free_page.data) { + cpu_buffer->free_page = *data_page; dpage = NULL; } @@ -7094,15 +7142,15 @@ int ring_buffer_read_page(struct trace_buffer *buffer, if (!data_page || !data_page->data) return -1; - if (data_page->order != buffer->subbuf_order) - return -1; - dpage = data_page->data; if (!dpage) return -1; guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock); + if (data_page->order != cpu_buffer->reader_page->order) + return -1; + reader = rb_get_reader_page(cpu_buffer); if (!reader) return -1; @@ -7134,15 +7182,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer, unsigned int event_size; unsigned int flags = 0; - /* - * If a full page is expected, this can still be returned - * if there's been a previous partial read and the - * rest of the page can be read and the commit page is off - * the reader page. - */ - if (full && - (!read || (len < (size - read)) || - cpu_buffer->reader_page == cpu_buffer->commit_page)) + /* If a full page is requested, it cannot be the commit page */ + if (full && cpu_buffer->reader_page == cpu_buffer->commit_page) return -1; if (len > (size - read)) @@ -7228,7 +7269,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, * missed events, then record it there. */ if (missed_events > 0 && - buffer->subbuf_size - size >= sizeof(missed_events)) { + rb_page_capacity(reader) - size >= sizeof(missed_events)) { memcpy(&dpage->data[size], &missed_events, sizeof(missed_events)); local_add(RB_MISSED_STORED, &dpage->commit); @@ -7248,8 +7289,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer, /* * This page may be off to user land. Zero it out here. */ - if (size < buffer->subbuf_size) - memset(&dpage->data[size], 0, buffer->subbuf_size - size); + if (size < rb_page_capacity(reader)) + memset(&dpage->data[size], 0, rb_page_capacity(reader) - size); return read; } @@ -7275,7 +7316,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_read_page_data); */ int ring_buffer_subbuf_size_get(struct trace_buffer *buffer) { - return buffer->subbuf_size + BUF_PAGE_HDR_SIZE; + return rb_subbuf_size(buffer); } EXPORT_SYMBOL_GPL(ring_buffer_subbuf_size_get); @@ -7320,7 +7361,8 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) { struct ring_buffer_per_cpu *cpu_buffer; struct buffer_page *bpage, *tmp; - int old_order, old_size; + unsigned int old_capacity; + int old_order; int nr_pages; int psize; int err; @@ -7329,9 +7371,6 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) if (!buffer || order < 0) return -EINVAL; - if (buffer->subbuf_order == order) - return 0; - psize = (1 << order) * PAGE_SIZE; if (psize <= BUF_PAGE_HDR_SIZE) return -EINVAL; @@ -7340,18 +7379,21 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) if (psize > RB_WRITE_MASK + 1) return -EINVAL; - old_order = buffer->subbuf_order; - old_size = buffer->subbuf_size; - /* prevent another thread from changing buffer sizes */ guard(mutex)(&buffer->mutex); + + old_order = buffer->subbuf_order; + if (old_order == order) + return 0; + + old_capacity = rb_subbuf_capacity(buffer); + atomic_inc(&buffer->record_disabled); /* Make sure all commits have finished */ synchronize_rcu(); buffer->subbuf_order = order; - buffer->subbuf_size = psize - BUF_PAGE_HDR_SIZE; /* Make sure all new buffers are allocated, before deleting the old ones */ for_each_buffer_cpu(buffer, cpu) { @@ -7367,8 +7409,8 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) } /* Update the number of pages to match the new size */ - nr_pages = old_size * buffer->buffers[cpu]->nr_pages; - nr_pages = DIV_ROUND_UP(nr_pages, buffer->subbuf_size); + nr_pages = old_capacity * buffer->buffers[cpu]->nr_pages; + nr_pages = DIV_ROUND_UP(nr_pages, rb_subbuf_capacity(buffer)); /* we need a minimum of two pages */ if (nr_pages < 2) @@ -7390,7 +7432,7 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) } for_each_buffer_cpu(buffer, cpu) { - struct buffer_data_page *old_free_data_page; + struct buffer_data_read_page old_free_data_page; struct list_head old_pages; unsigned long flags; @@ -7431,8 +7473,10 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) cpu_buffer->nr_pages = cpu_buffer->nr_pages_to_update; cpu_buffer->nr_pages_to_update = 0; + arch_spin_lock(&cpu_buffer->lock); old_free_data_page = cpu_buffer->free_page; - cpu_buffer->free_page = NULL; + cpu_buffer->free_page.data = NULL; + arch_spin_unlock(&cpu_buffer->lock); rb_head_page_activate(cpu_buffer); @@ -7443,7 +7487,7 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) list_del_init(&bpage->list); free_buffer_page(bpage); } - free_pages((unsigned long)old_free_data_page, old_order); + free_pages((unsigned long)old_free_data_page.data, old_free_data_page.order); rb_check_pages(cpu_buffer); } @@ -7454,7 +7498,6 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) error: buffer->subbuf_order = old_order; - buffer->subbuf_size = old_size; atomic_dec(&buffer->record_disabled); @@ -7532,7 +7575,7 @@ static void rb_setup_ids_meta_page(struct ring_buffer_per_cpu *cpu_buffer, meta->meta_struct_len = sizeof(*meta); meta->nr_subbufs = nr_subbufs; - meta->subbuf_size = cpu_buffer->buffer->subbuf_size + BUF_PAGE_HDR_SIZE; + meta->subbuf_size = rb_subbuf_size(cpu_buffer->buffer); meta->meta_page_size = meta->subbuf_size; rb_update_meta_page(cpu_buffer); @@ -7894,7 +7937,7 @@ int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu) * missed events, then record it there. */ commit = rb_page_size(reader); - if (buffer->subbuf_size - commit >= sizeof(missed_events)) { + if (rb_subbuf_capacity(buffer) - commit >= sizeof(missed_events)) { memcpy(&dpage->data[commit], &missed_events, sizeof(missed_events)); local_add(RB_MISSED_STORED, &dpage->commit); @@ -7926,7 +7969,7 @@ int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu) out: /* Some archs do not have data cache coherency between kernel and user-space */ flush_kernel_vmap_range(cpu_buffer->reader_page->page, - buffer->subbuf_size + BUF_PAGE_HDR_SIZE); + rb_subbuf_size(buffer)); rb_update_meta_page(cpu_buffer); diff --git a/kernel/trace/simple_ring_buffer.c b/kernel/trace/simple_ring_buffer.c index f4642f5adda334..49913bb0057a7e 100644 --- a/kernel/trace/simple_ring_buffer.c +++ b/kernel/trace/simple_ring_buffer.c @@ -160,8 +160,8 @@ int simple_ring_buffer_swap_reader_page(struct simple_rb_per_cpu *cpu_buffer) overrun = cpu_buffer->meta->overrun; } while (!simple_bpage_unset_head_link(last, reader, SIMPLE_RB_LINK_NORMAL) && retry--); - if (!retry) - return -EINVAL; + if (retry < 0) + return -EBUSY; cpu_buffer->head_page = simple_bpage_from_link(reader->link.next); cpu_buffer->head_page->link.prev = &reader->link; diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 395238b2b715c1..a6d5d1786469cf 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -7840,11 +7840,70 @@ trace_options_core_write(struct file *filp, const char __user *ubuf, size_t cnt, return cnt; } +/* + * The tr_index is the address of a trace_array->trace_flags_index[] + * element that holds the index of the trace flag. But since the + * trace_array reference has not been taken yet, it cannot be referenced + * as it could have been freed by a rmdir of the instance the trace_array + * represents. + * + * Search the list of trace_arrays and compare the tr_index to the + * address of the entire trace_array trace_flags_index array for each + * trace_array in the list. If one is matched, then take the reference + * and return it. If not, the trace_array no longer exits. + */ +static int trace_array_options_get(void *tr_index) +{ + struct trace_array *tr; + int ret; + + ret = security_locked_down(LOCKDOWN_TRACEFS); + if (ret) + return ret; + + if (tracing_disabled) + return -ENODEV; + + guard(mutex)(&trace_types_lock); + list_for_each_entry(tr, &ftrace_trace_arrays, list) { + if (tr_index >= (void *)&tr->trace_flags_index[0] && + tr_index < (void *)&tr->trace_flags_index[TRACE_FLAGS_MAX_SIZE]) + return __trace_array_get(tr); + } + return -ENODEV; +} + +static int trace_options_open(struct inode *inode, struct file *filp) +{ + void *tr_index = inode->i_private; + + if (trace_array_options_get(tr_index) < 0) + return -ENODEV; + + filp->private_data = tr_index; + + return 0; +} + +static int trace_options_release(struct inode *inode, struct file *filp) +{ + void *tr_index = filp->private_data; + struct trace_array *tr; + unsigned int index; + + get_tr_index(tr_index, &tr, &index); + + trace_array_put(tr); + + return 0; +} + static const struct file_operations trace_options_core_fops = { - .open = tracing_open_generic, - .read = trace_options_core_read, - .write = trace_options_core_write, - .llseek = generic_file_llseek, + .open = trace_options_open, + .read = trace_options_core_read, + .write = trace_options_core_write, + .llseek = generic_file_llseek, + .release = trace_options_release, }; struct dentry *trace_create_file(const char *name, @@ -8214,6 +8273,8 @@ buffer_subbuf_size_write(struct file *filp, const char __user *ubuf, /* Do not allow tracing while changing the order of the ring buffer */ tracing_stop_tr(tr); + trace_access_lock(RING_BUFFER_ALL_CPUS); + old_order = ring_buffer_subbuf_order_get(tr->array_buffer.buffer); if (old_order == order) goto out; @@ -8253,6 +8314,7 @@ buffer_subbuf_size_write(struct file *filp, const char __user *ubuf, #endif (*ppos)++; out: + trace_access_unlock(RING_BUFFER_ALL_CPUS); if (ret) cnt = ret; tracing_start_tr(tr); @@ -9726,7 +9788,8 @@ __init static void enable_instances(void) tr = trace_array_create_systems(name, NULL, addr, size); if (IS_ERR(tr)) { - pr_warn("Tracing: Failed to create instance buffer %s\n", curr_str); + pr_warn("Tracing: Failed to create instance buffer '%s' (%ld)\n", name, + PTR_ERR(tr)); continue; } diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index bf77331f56a4dd..191a64c885daba 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -1340,7 +1340,7 @@ extern void clear_ftrace_function_probes(struct trace_array *tr); int register_ftrace_command(struct ftrace_func_command *cmd); int unregister_ftrace_command(struct ftrace_func_command *cmd); -void ftrace_create_filter_files(struct ftrace_ops *ops, +void ftrace_create_filter_files(struct trace_array *tr, struct dentry *parent); void ftrace_destroy_filter_files(struct ftrace_ops *ops); @@ -1363,11 +1363,12 @@ static inline void clear_ftrace_function_probes(struct trace_array *tr) { } +static inline void ftrace_create_filter_files(struct trace_array *tr, + struct dentry *parent) { } /* * The ops parameter passed in is usually undefined. * This must be a macro. */ -#define ftrace_create_filter_files(ops, parent) do { } while (0) #define ftrace_destroy_filter_files(ops) do { } while (0) #endif /* CONFIG_FUNCTION_TRACER && CONFIG_DYNAMIC_FTRACE */ diff --git a/kernel/trace/trace_btf.c b/kernel/trace/trace_btf.c index 00172f301f25a5..d3ba356d5503e6 100644 --- a/kernel/trace/trace_btf.c +++ b/kernel/trace/trace_btf.c @@ -74,24 +74,24 @@ const struct btf_member *btf_find_struct_member(struct btf *btf, { struct btf_anon_stack *anon_stack; const struct btf_member *member; + const struct btf_type *mtype; u32 tid, cur_offset = 0; const char *name; int i, top = 0; + if (!btf_type_is_struct(type)) + return ERR_PTR(-EINVAL); + anon_stack = kzalloc_objs(*anon_stack, BTF_ANON_STACK_MAX); if (!anon_stack) return ERR_PTR(-ENOMEM); retry: - if (!btf_type_is_struct(type)) { - member = ERR_PTR(-EINVAL); - goto out; - } - for_each_member(i, type, member) { if (!member->name_off) { /* Anonymous union/struct: push it for later use */ - if (btf_type_skip_modifiers(btf, member->type, &tid) && + mtype = btf_type_skip_modifiers(btf, member->type, &tid); + if (mtype && btf_type_is_struct(mtype) && top < BTF_ANON_STACK_MAX) { anon_stack[top].tid = tid; anon_stack[top++].offset = diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 3650d84d4f16c2..640529409021f5 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -2654,14 +2654,14 @@ static const struct file_operations ftrace_show_event_filters_fops = { .open = ftrace_event_show_filters_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = ftrace_event_release, }; static const struct file_operations ftrace_show_event_triggers_fops = { .open = ftrace_event_show_triggers_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = ftrace_event_release, }; static const struct file_operations ftrace_set_event_pid_fops = { @@ -2819,7 +2819,17 @@ ftrace_event_set_open(struct inode *inode, struct file *file) static int ftrace_event_show_filters_open(struct inode *inode, struct file *file) { - return ftrace_event_open(inode, file, &show_show_event_filters_seq_ops); + struct trace_array *tr = inode->i_private; + int ret; + + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; + + ret = ftrace_event_open(inode, file, &show_show_event_filters_seq_ops); + if (ret < 0) + trace_array_put(tr); + return ret; } /** @@ -2833,7 +2843,17 @@ ftrace_event_show_filters_open(struct inode *inode, struct file *file) static int ftrace_event_show_triggers_open(struct inode *inode, struct file *file) { - return ftrace_event_open(inode, file, &show_show_event_triggers_seq_ops); + struct trace_array *tr = inode->i_private; + int ret; + + ret = tracing_check_open_get_tr(tr); + if (ret) + return ret; + + ret = ftrace_event_open(inode, file, &show_show_event_triggers_seq_ops); + if (ret < 0) + trace_array_put(tr); + return ret; } static int @@ -4901,6 +4921,8 @@ static __init void event_test_stuff(void) struct task_struct *test_thread; test_thread = kthread_run(event_test_thread, NULL, "test-events"); + if (WARN_ON(IS_ERR(test_thread))) + return; msleep(1); kthread_stop(test_thread); } diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 58d28cd1afa3b0..6bf00710fd100b 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -6641,8 +6641,10 @@ static int hist_register_trigger(char *glob, tracing_set_filter_buffering(file->tr, true); } - if (named_data) + if (named_data) { + remove_hist_vars(hist_data); destroy_hist_data(hist_data); + } out: return ret; } diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c index 8c82ecb735f415..6b89d225b189e4 100644 --- a/kernel/trace/trace_events_user.c +++ b/kernel/trace/trace_events_user.c @@ -868,6 +868,9 @@ void user_event_mm_dup(struct task_struct *t, struct user_event_mm *old_mm) struct user_event_mm *mm = user_event_mm_alloc(t); struct user_event_enabler *enabler; + /* On failure, do not free parent's copy */ + t->user_event_mm = NULL; + if (!mm) return; diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index cd37f201375813..c879d43a5fbb22 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c @@ -101,7 +101,7 @@ int ftrace_create_function_files(struct trace_array *tr, return ret; } - ftrace_create_filter_files(tr->ops, parent); + ftrace_create_filter_files(tr, parent); return 0; } diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c index 0aa2514a6593b6..e7f4e523587d08 100644 --- a/kernel/trace/trace_stack.c +++ b/kernel/trace/trace_stack.c @@ -499,7 +499,7 @@ stack_trace_filter_open(struct inode *inode, struct file *file) struct ftrace_ops *ops = inode->i_private; /* Checks for tracefs lockdown */ - return ftrace_regex_open(ops, FTRACE_ITER_FILTER, + return ftrace_regex_open(NULL, ops, FTRACE_ITER_FILTER, inode, file); } diff --git a/lib/iov_iter.c b/lib/iov_iter.c index c2484551a4e863..34a52e9ba9e1b5 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1904,6 +1904,8 @@ static unsigned int get_contig_folio_len(struct page **pages, * @max_size: maximum size to extract from @iter * @nr_vecs: number of vectors in @bv (on in and output) * @max_vecs: maximum vectors in @bv, including those filled before calling + * @mem_align_mask: reject with -EINVAL if the source address or + * length is not aligned to this mask * @extraction_flags: flags to qualify request * * Like iov_iter_extract_pages(), but returns physically contiguous ranges @@ -1915,14 +1917,19 @@ static unsigned int get_contig_folio_len(struct page **pages, */ ssize_t iov_iter_extract_bvecs(struct iov_iter *iter, struct bio_vec *bv, size_t max_size, unsigned short *nr_vecs, - unsigned short max_vecs, iov_iter_extraction_t extraction_flags) + unsigned short max_vecs, unsigned mem_align_mask, + iov_iter_extraction_t extraction_flags) { + unsigned long start = (unsigned long)iter_iov_addr(iter); unsigned short entries_left = max_vecs - *nr_vecs; unsigned short nr_pages, i = 0; size_t left, offset, len; struct page **pages; ssize_t size; + if ((start | iter_iov_len(iter)) & mem_align_mask) + return -EINVAL; + /* * Move page array up in the allocated memory for the bio vecs as far as * possible so that we can start filling biovecs from the beginning diff --git a/lib/once.c b/lib/once.c index d801bfa945e631..0a0a919156e05e 100644 --- a/lib/once.c +++ b/lib/once.c @@ -93,6 +93,6 @@ void __do_once_sleepable_done(bool *done, struct static_key_true *once_key, { *done = true; mutex_unlock(&once_mutex); - static_branch_disable(once_key); + once_disable_jump(once_key, mod); } EXPORT_SYMBOL(__do_once_sleepable_done); diff --git a/lib/ucs2_string.c b/lib/ucs2_string.c index dfb4f2358cabfe..ca5c615d5bef24 100644 --- a/lib/ucs2_string.c +++ b/lib/ucs2_string.c @@ -8,7 +8,7 @@ ucs2_strnlen(const ucs2_char_t *s, size_t maxlength) { unsigned long length = 0; - while (*s++ != 0 && length < maxlength) + while (length < maxlength && *s++ != 0) length++; return length; } diff --git a/lib/xarray.c b/lib/xarray.c index 9a8b4916540cf1..bfe7bef80f34eb 100644 --- a/lib/xarray.c +++ b/lib/xarray.c @@ -1053,6 +1053,9 @@ void xas_split_alloc(struct xa_state *xas, void *entry, unsigned int order, if (xas->xa_shift + XA_CHUNK_SHIFT > order) return; + if (xas->xa->xa_flags & XA_FLAGS_ACCOUNT) + gfp |= __GFP_ACCOUNT; + do { struct xa_node *node; diff --git a/mm/compaction.c b/mm/compaction.c index f08765ade014ce..65fd08aabb2c27 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -1381,12 +1381,44 @@ static bool suitable_migration_source(struct compact_control *cc, if (pageblock_skip_persistent(page)) return false; - if ((cc->mode != MIGRATE_ASYNC) || !cc->direct_compaction) + /* + * Background compaction produces blocks for the zone at + * large, with no particular allocation context. Allow all + * block types, including CMA. + */ + if (!cc->direct_compaction) return true; block_mt = get_pageblock_migratetype(page); - if (cc->migratetype == MIGRATE_MOVABLE) + /* + * CMA pages can only be taken by ALLOC_CMA requests. For anybody + * else, vacating a CMA block consumes free pages the caller + * could have used, and produces free pages it cannot. + */ + if (is_migrate_cma(block_mt) && !(cc->alloc_flags & ALLOC_CMA)) + return false; + + /* + * Per default, scans are restricted to blocks compatible with + * the request, to prevent cross-contamination. Once + * compaction priority escalates to synchronous scans, though, + * scan all blocks to try to make forward progress. For + * movable request, this likely helps little: there shouldn't + * be many migratable pages inside non-movable blocks besides + * allocator fallbacks. For non-movable requests, this helps a + * lot, as they can finally scan movable blocks. + */ + if (cc->mode != MIGRATE_ASYNC) + return true; + + /* + * Prevent migratetype == MIGRATE_MOVABLE || cc->order >= pageblock_order) return is_migrate_movable(block_mt); else return block_mt == cc->migratetype; @@ -1974,12 +2006,12 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc) return pfn; /* - * Only allow kcompactd and direct requests for movable pages to - * quickly clear out a MOVABLE pageblock for allocation. This - * reduces the risk that a large movable pageblock is freed for - * an unmovable/reclaimable small allocation. + * Prevent direct_compaction && cc->migratetype != MIGRATE_MOVABLE) + if (cc->direct_compaction && cc->migratetype != MIGRATE_MOVABLE && + cc->order < pageblock_order) return pfn; /* @@ -2770,9 +2802,8 @@ compact_zone(struct compact_control *cc, struct capture_control *capc) static enum compact_result compact_zone_order(struct zone *zone, int order, gfp_t gfp_mask, enum compact_priority prio, unsigned int alloc_flags, int highest_zoneidx, - struct page **capture) + struct capture_control *capc) { - enum compact_result ret; struct compact_control cc = { .order = order, .search_order = order, @@ -2787,54 +2818,24 @@ static enum compact_result compact_zone_order(struct zone *zone, int order, .ignore_skip_hint = (prio == MIN_COMPACT_PRIORITY), .ignore_block_suitable = (prio == MIN_COMPACT_PRIORITY) }; - struct capture_control capc = { - .cc = &cc, - .page = NULL, - }; - /* - * Make sure the structs are really initialized before we expose the - * capture control, in case we are interrupted and the interrupt handler - * frees a page. - */ - barrier(); - WRITE_ONCE(current->capture_control, &capc); - - ret = compact_zone(&cc, &capc); - - /* - * Make sure we hide capture control first before we read the captured - * page pointer, otherwise an interrupt could free and capture a page - * and we would leak it. - */ - WRITE_ONCE(current->capture_control, NULL); - *capture = READ_ONCE(capc.page); - /* - * Technically, it is also possible that compaction is skipped but - * the page is still captured out of luck(IRQ came and freed the page). - * Returning COMPACT_SUCCESS in such cases helps in properly accounting - * the COMPACT[STALL|FAIL] when compaction is skipped. - */ - if (*capture) - ret = COMPACT_SUCCESS; - - return ret; + return compact_zone(&cc, capc); } /** * try_to_compact_pages - Direct compact to satisfy a high-order allocation * @gfp_mask: The GFP mask of the current allocation - * @order: The order of the current allocation + * @order: The order to try to make available * @alloc_flags: The allocation flags of the current allocation * @ac: The context of current allocation * @prio: Determines how hard direct compaction should try to succeed - * @capture: Pointer to free page created by compaction will be stored here + * @capc: Free page capture bypassing the freelist * * This is the main entry point for direct page compaction. */ enum compact_result try_to_compact_pages(gfp_t gfp_mask, unsigned int order, unsigned int alloc_flags, const struct alloc_context *ac, - enum compact_priority prio, struct page **capture) + enum compact_priority prio, struct capture_control *capc) { struct zoneref *z; struct zone *zone; @@ -2861,8 +2862,17 @@ enum compact_result try_to_compact_pages(gfp_t gfp_mask, unsigned int order, continue; } + WRITE_ONCE(capc->zone, zone); + status = compact_zone_order(zone, order, gfp_mask, prio, - alloc_flags, ac->highest_zoneidx, capture); + alloc_flags, ac->highest_zoneidx, capc); + + WRITE_ONCE(capc->zone, NULL); + + /* Stop if a page has been captured */ + if (READ_ONCE(capc->page)) + status = COMPACT_SUCCESS; + rc = max(status, rc); /* The allocation should succeed, stop compacting */ diff --git a/mm/damon/core.c b/mm/damon/core.c index cff932b3317d08..a86812d457c14a 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -579,6 +579,7 @@ struct damos *damon_new_scheme(struct damos_access_pattern *pattern, INIT_LIST_HEAD(&scheme->ops_filters); scheme->stat = (struct damos_stat){}; scheme->max_nr_snapshots = 0; + scheme->last_applied = NULL; INIT_LIST_HEAD(&scheme->list); scheme->quota = *(damos_quota_init(quota)); @@ -1670,7 +1671,7 @@ static unsigned long damon_region_sz_limit(struct damon_ctx *ctx) return sz; } -static void damon_split_region_at(struct damon_target *t, +static int damon_split_region_at(struct damon_target *t, struct damon_region *r, unsigned long sz_r); /* @@ -1696,11 +1697,13 @@ static unsigned long damon_apply_min_nr_regions(struct damon_ctx *ctx) damon_for_each_target(t, ctx) { damon_for_each_region_safe(r, next, t) { while (damon_sz_region(r) > max_region_sz) { - damon_split_region_at(t, r, max_region_sz); + if (damon_split_region_at(t, r, max_region_sz)) + goto out; r = damon_next_region(r); } } } +out: return max_region_sz; } @@ -3163,15 +3166,20 @@ static void kdamond_merge_regions(struct damon_ctx *c, unsigned int threshold, max_thres = c->attrs.aggr_interval / (c->attrs.sample_interval ? c->attrs.sample_interval : 1); - do { + while (true) { nr_regions = 0; damon_for_each_target(t, c) { damon_merge_regions_of(t, threshold, sz_limit); nr_regions += damon_nr_regions(t); } - threshold = max(1, threshold * 2); - } while (nr_regions > c->attrs.max_nr_regions && - threshold / 2 < max_thres); + if (nr_regions <= c->attrs.max_nr_regions || + max_thres <= threshold) + break; + if (threshold < max_thres / 2) + threshold = max(1, threshold * 2); + else + threshold = max_thres; + } } #ifdef CONFIG_DAMON_DEBUG_SANITY @@ -3194,8 +3202,10 @@ static void damon_verify_split_region_at(struct damon_region *r, * * r the region to be split * sz_r size of the first sub-region that will be made + * + * Return: 0 on success, negative error code otherwise. */ -static void damon_split_region_at(struct damon_target *t, +static int damon_split_region_at(struct damon_target *t, struct damon_region *r, unsigned long sz_r) { struct damon_region *new; @@ -3203,7 +3213,7 @@ static void damon_split_region_at(struct damon_target *t, damon_verify_split_region_at(r, sz_r); new = damon_new_region(r->ar.start + sz_r, r->ar.end); if (!new) - return; + return -ENOMEM; r->ar.end = new->ar.start; @@ -3215,6 +3225,7 @@ static void damon_split_region_at(struct damon_target *t, memcpy(new->probe_hits, r->probe_hits, sizeof(r->probe_hits)); damon_insert_region(new, r, damon_next_region(r), t); + return 0; } /* Split every region in the given target into 'nr_subs' regions */ diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c index 0bcad6b1e5b9e9..6a969b1d29871e 100644 --- a/mm/damon/ops-common.c +++ b/mm/damon/ops-common.c @@ -111,8 +111,9 @@ int damon_hot_score(struct damon_ctx *c, struct damon_region *r, unsigned int age_weight = s->quota.weight_age; int hotness; - freq_subscore = r->nr_accesses * DAMON_MAX_SUBSCORE / - damon_max_nr_accesses(&c->attrs); + freq_subscore = mult_frac(r->nr_accesses_bp / 10000, + DAMON_MAX_SUBSCORE, + damon_max_nr_accesses(&c->attrs)); age_in_sec = (unsigned long)r->age * c->attrs.aggr_interval / 1000000; if (age_in_sec) diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c index f7613ce279a24c..b3507b1f62499a 100644 --- a/mm/damon/paddr.c +++ b/mm/damon/paddr.c @@ -65,7 +65,7 @@ static void damon_pa_prepare_access_checks(struct damon_ctx *ctx) } } -static bool damon_pa_young(phys_addr_t paddr, unsigned long *folio_sz) +static bool damon_pa_young(phys_addr_t paddr) { struct folio *folio = damon_get_folio(PHYS_PFN(paddr)); bool accessed; @@ -74,7 +74,6 @@ static bool damon_pa_young(phys_addr_t paddr, unsigned long *folio_sz) return false; accessed = damon_folio_young(folio); - *folio_sz = folio_size(folio); folio_put(folio); return accessed; } @@ -82,23 +81,12 @@ static bool damon_pa_young(phys_addr_t paddr, unsigned long *folio_sz) static void __damon_pa_check_access(struct damon_region *r, struct damon_attrs *attrs, unsigned long addr_unit) { - static phys_addr_t last_addr; - static unsigned long last_folio_sz = PAGE_SIZE; - static bool last_accessed; + bool accessed; phys_addr_t sampling_addr = damon_pa_phys_addr( r->sampling_addr, addr_unit); - /* If the region is in the last checked page, reuse the result */ - if (ALIGN_DOWN(last_addr, last_folio_sz) == - ALIGN_DOWN(sampling_addr, last_folio_sz)) { - damon_update_region_access_rate(r, last_accessed, attrs); - return; - } - - last_accessed = damon_pa_young(sampling_addr, &last_folio_sz); - damon_update_region_access_rate(r, last_accessed, attrs); - - last_addr = sampling_addr; + accessed = damon_pa_young(sampling_addr); + damon_update_region_access_rate(r, accessed, attrs); } static unsigned int damon_pa_check_accesses(struct damon_ctx *ctx) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 3cbeccd436e40f..e2c8716be6c9ca 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -332,6 +332,7 @@ static void damon_sysfs_scheme_regions_rm_dirs( list_for_each_entry_safe(r, next, ®ions->regions_list, list) { damos_sysfs_region_rm_dirs(r); list_del(&r->list); + kobject_del(&r->kobj); kobject_put(&r->kobj); regions->nr_regions--; } @@ -911,8 +912,10 @@ static void damon_sysfs_scheme_filters_rm_dirs( struct damon_sysfs_scheme_filter **filters_arr = filters->filters_arr; int i; - for (i = 0; i < filters->nr; i++) + for (i = 0; i < filters->nr; i++) { + kobject_del(&filters_arr[i]->kobj); kobject_put(&filters_arr[i]->kobj); + } filters->nr = 0; kfree(filters_arr); filters->filters_arr = NULL; @@ -1460,8 +1463,10 @@ static void damos_sysfs_quota_goals_rm_dirs( struct damos_sysfs_quota_goal **goals_arr = goals->goals_arr; int i; - for (i = 0; i < goals->nr; i++) + for (i = 0; i < goals->nr; i++) { + kobject_del(&goals_arr[i]->kobj); kobject_put(&goals_arr[i]->kobj); + } goals->nr = 0; kfree(goals_arr); goals->goals_arr = NULL; @@ -2138,8 +2143,10 @@ static void damos_sysfs_dests_rm_dirs( struct damos_sysfs_dest **dests_arr = dests->dests_arr; int i; - for (i = 0; i < dests->nr; i++) + for (i = 0; i < dests->nr; i++) { + kobject_del(&dests_arr[i]->kobj); kobject_put(&dests_arr[i]->kobj); + } dests->nr = 0; kfree(dests_arr); dests->dests_arr = NULL; @@ -2681,6 +2688,7 @@ void damon_sysfs_schemes_rm_dirs(struct damon_sysfs_schemes *schemes) for (i = 0; i < schemes->nr; i++) { damon_sysfs_scheme_rm_dirs(schemes_arr[i]); + kobject_del(&schemes_arr[i]->kobj); kobject_put(&schemes_arr[i]->kobj); } schemes->nr = 0; @@ -2722,13 +2730,15 @@ static int damon_sysfs_schemes_add_dirs(struct damon_sysfs_schemes *schemes, goto out; err = damon_sysfs_scheme_add_dirs(scheme); if (err) - goto out; + goto del_out; schemes_arr[i] = scheme; schemes->nr++; } return 0; +del_out: + kobject_del(&scheme->kobj); out: damon_sysfs_schemes_rm_dirs(schemes); kobject_put(&scheme->kobj); diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index a9e187158067d4..26cdad603ad06d 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -105,8 +105,10 @@ static void damon_sysfs_regions_rm_dirs(struct damon_sysfs_regions *regions) struct damon_sysfs_region **regions_arr = regions->regions_arr; int i; - for (i = 0; i < regions->nr; i++) + for (i = 0; i < regions->nr; i++) { + kobject_del(®ions_arr[i]->kobj); kobject_put(®ions_arr[i]->kobj); + } regions->nr = 0; kfree(regions_arr); regions->regions_arr = NULL; @@ -331,6 +333,7 @@ static void damon_sysfs_targets_rm_dirs(struct damon_sysfs_targets *targets) for (i = 0; i < targets->nr; i++) { damon_sysfs_target_rm_dirs(targets_arr[i]); + kobject_del(&targets_arr[i]->kobj); kobject_put(&targets_arr[i]->kobj); } targets->nr = 0; @@ -369,13 +372,15 @@ static int damon_sysfs_targets_add_dirs(struct damon_sysfs_targets *targets, err = damon_sysfs_target_add_dirs(target); if (err) - goto out; + goto del_out; targets_arr[i] = target; targets->nr++; } return 0; +del_out: + kobject_del(&target->kobj); out: damon_sysfs_targets_rm_dirs(targets); kobject_put(&target->kobj); @@ -1640,6 +1645,7 @@ static void damon_sysfs_contexts_rm_dirs(struct damon_sysfs_contexts *contexts) for (i = 0; i < contexts->nr; i++) { damon_sysfs_context_rm_dirs(contexts_arr[i]); + kobject_del(&contexts_arr[i]->kobj); kobject_put(&contexts_arr[i]->kobj); } contexts->nr = 0; @@ -1678,13 +1684,15 @@ static int damon_sysfs_contexts_add_dirs(struct damon_sysfs_contexts *contexts, err = damon_sysfs_context_add_dirs(context); if (err) - goto out; + goto del_out; contexts_arr[i] = context; contexts->nr++; } return 0; +del_out: + kobject_del(&context->kobj); out: damon_sysfs_contexts_rm_dirs(contexts); kobject_put(&context->kobj); @@ -2042,16 +2050,18 @@ static inline bool damon_sysfs_kdamond_running( static int damon_sysfs_apply_inputs(struct damon_ctx *ctx, struct damon_sysfs_context *sys_ctx) { + enum damon_ops_id ops_id; int err; - err = damon_select_ops(ctx, sys_ctx->ops_id); + ops_id = READ_ONCE(sys_ctx->ops_id); + err = damon_select_ops(ctx, ops_id); if (err) return err; - ctx->addr_unit = sys_ctx->addr_unit; + ctx->addr_unit = READ_ONCE(sys_ctx->addr_unit); /* addr_unit is respected by only DAMON_OPS_PADDR */ - if (sys_ctx->ops_id == DAMON_OPS_PADDR) + if (ops_id == DAMON_OPS_PADDR) ctx->min_region_sz = max( - DAMON_MIN_REGION_SZ / sys_ctx->addr_unit, 1); + DAMON_MIN_REGION_SZ / ctx->addr_unit, 1); ctx->pause = sys_ctx->pause; err = damon_sysfs_set_attrs(ctx, sys_ctx->attrs); if (err) @@ -2499,6 +2509,7 @@ static void damon_sysfs_kdamonds_rm_dirs(struct damon_sysfs_kdamonds *kdamonds) for (i = 0; i < kdamonds->nr; i++) { damon_sysfs_kdamond_rm_dirs(kdamonds_arr[i]); + kobject_del(&kdamonds_arr[i]->kobj); kobject_put(&kdamonds_arr[i]->kobj); } kdamonds->nr = 0; @@ -2553,13 +2564,15 @@ static int damon_sysfs_kdamonds_add_dirs(struct damon_sysfs_kdamonds *kdamonds, err = damon_sysfs_kdamond_add_dirs(kdamond); if (err) - goto out; + goto del_out; kdamonds_arr[i] = kdamond; kdamonds->nr++; } return 0; +del_out: + kobject_del(&kdamond->kobj); out: damon_sysfs_kdamonds_rm_dirs(kdamonds); kobject_put(&kdamond->kobj); diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h index fcf7c7fadb5fe5..fbcc882dccc94b 100644 --- a/mm/damon/tests/core-kunit.h +++ b/mm/damon/tests/core-kunit.h @@ -157,6 +157,10 @@ static void damon_test_split_at(struct kunit *test) r->age = 10; damon_add_region(r, t); damon_split_region_at(t, r, 25); + KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 2); + if (damon_nr_regions(t) != 2) + goto out; + KUNIT_EXPECT_EQ(test, r->ar.start, 0ul); KUNIT_EXPECT_EQ(test, r->ar.end, 25ul); @@ -169,6 +173,7 @@ static void damon_test_split_at(struct kunit *test) KUNIT_EXPECT_EQ(test, r->last_nr_accesses, r_new->last_nr_accesses); KUNIT_EXPECT_EQ(test, r->age, r_new->age); +out: damon_free_target(t); } @@ -259,11 +264,14 @@ static void damon_test_merge_regions_of(struct kunit *test) damon_merge_regions_of(t, 9, 9999); /* 0-112, 114-130, 130-156, 156-170, 170-230, 230-10170 */ KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 6u); + if (damon_nr_regions(t) != 6) + goto out; for (i = 0; i < 6; i++) { r = __nth_region_of(t, i); KUNIT_EXPECT_EQ(test, r->ar.start, saddrs[i]); KUNIT_EXPECT_EQ(test, r->ar.end, eaddrs[i]); } +out: damon_free_target(t); } @@ -725,6 +733,7 @@ static void damos_test_commit_quota_goals_for(struct kunit *test, struct damos_quota_goal *goal, *next; bool skip = true; int i; + int nr_dst = 0, nr_src = 0; INIT_LIST_HEAD(&dst.goals); INIT_LIST_HEAD(&src.goals); @@ -747,6 +756,14 @@ static void damos_test_commit_quota_goals_for(struct kunit *test, damos_commit_quota_goals(&dst, &src); + damos_for_each_quota_goal(goal, &dst) + nr_dst++; + damos_for_each_quota_goal(goal, &src) + nr_src++; + KUNIT_EXPECT_EQ(test, nr_dst, nr_src); + if (nr_dst != nr_src) + goto out; + i = 0; damos_for_each_quota_goal(goal, (&dst)) { KUNIT_EXPECT_EQ(test, goal->metric, src_goals[i].metric); @@ -894,6 +911,8 @@ static void damos_test_commit_dests_for(struct kunit *test, skip = false; KUNIT_EXPECT_EQ(test, dst.nr_dests, src_nr_dests); + if (dst.nr_dests != src_nr_dests) + goto out; for (i = 0; i < dst.nr_dests; i++) { KUNIT_EXPECT_EQ(test, dst.node_id_arr[i], src_node_id_arr[i]); KUNIT_EXPECT_EQ(test, dst.weight_arr[i], src_weight_arr[i]); @@ -1152,14 +1171,19 @@ static void damon_test_commit_target_regions_for(struct kunit *test, kunit_skip(test, "src target setup fail"); } damon_commit_target_regions(dst_target, src_target, 1); + + KUNIT_EXPECT_EQ(test, damon_nr_regions(dst_target), nr_expect_regions); + if (damon_nr_regions(dst_target) != nr_expect_regions) + goto out; + i = 0; damon_for_each_region(r, dst_target) { KUNIT_EXPECT_EQ(test, r->ar.start, expect_start_end[i][0]); KUNIT_EXPECT_EQ(test, r->ar.end, expect_start_end[i][1]); i++; } - KUNIT_EXPECT_EQ(test, damon_nr_regions(dst_target), nr_expect_regions); - KUNIT_EXPECT_EQ(test, i, nr_expect_regions); + +out: damon_free_target(dst_target); damon_free_target(src_target); } @@ -1251,6 +1275,8 @@ static void damos_test_filter_out(struct kunit *test) KUNIT_EXPECT_EQ(test, r->ar.start, 1); KUNIT_EXPECT_EQ(test, r->ar.end, 2); KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 2); + if (damon_nr_regions(t) != 2) + goto out; r2 = damon_next_region(r); KUNIT_EXPECT_EQ(test, r2->ar.start, 2); KUNIT_EXPECT_EQ(test, r2->ar.end, 4); @@ -1265,11 +1291,14 @@ static void damos_test_filter_out(struct kunit *test) KUNIT_EXPECT_EQ(test, r->ar.start, 2); KUNIT_EXPECT_EQ(test, r->ar.end, 6); KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 2); + if (damon_nr_regions(t) != 2) + goto out; r2 = damon_next_region(r); KUNIT_EXPECT_EQ(test, r2->ar.start, 6); KUNIT_EXPECT_EQ(test, r2->ar.end, 8); damon_destroy_region(r2, t); +out: damon_free_target(t); damos_free_filter(f); } diff --git a/mm/damon/tests/vaddr-kunit.h b/mm/damon/tests/vaddr-kunit.h index 61f844336ffb50..6a95441d193aeb 100644 --- a/mm/damon/tests/vaddr-kunit.h +++ b/mm/damon/tests/vaddr-kunit.h @@ -158,12 +158,17 @@ static void damon_do_test_apply_three_regions(struct kunit *test, kunit_skip(test, "second damon_set_regions() fail"); } + KUNIT_EXPECT_EQ(test, damon_nr_regions(t), nr_expected / 2); + if (damon_nr_regions(t) != nr_expected / 2) + goto out; + for (i = 0; i < nr_expected / 2; i++) { r = __nth_region_of(t, i); KUNIT_EXPECT_EQ(test, r->ar.start, expected[i * 2]); KUNIT_EXPECT_EQ(test, r->ar.end, expected[i * 2 + 1]); } +out: damon_destroy_target(t, NULL); } diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c index 2c1c1952c008d0..404785128ffd71 100644 --- a/mm/damon/vaddr.c +++ b/mm/damon/vaddr.c @@ -382,8 +382,6 @@ static void damon_va_prepare_access_checks(struct damon_ctx *ctx) } struct damon_young_walk_private { - /* size of the folio for the access checked virtual memory address */ - unsigned long *folio_sz; bool young; }; @@ -410,7 +408,6 @@ static int damon_young_pmd_entry(pmd_t *pmd, unsigned long addr, mmu_notifier_test_young(walk->mm, addr)) priv->young = true; - *priv->folio_sz = HPAGE_PMD_SIZE; huge_out: spin_unlock(ptl); return 0; @@ -429,7 +426,6 @@ static int damon_young_pmd_entry(pmd_t *pmd, unsigned long addr, if (pte_young(ptent) || !folio_test_idle(folio) || mmu_notifier_test_young(walk->mm, addr)) priv->young = true; - *priv->folio_sz = folio_size(folio); out: pte_unmap_unlock(pte, ptl); return 0; @@ -457,7 +453,6 @@ static int damon_young_hugetlb_entry(pte_t *pte, unsigned long hmask, if (pte_young(entry) || !folio_test_idle(folio) || mmu_notifier_test_young(walk->mm, addr)) priv->young = true; - *priv->folio_sz = huge_page_size(h); folio_put(folio); @@ -469,11 +464,9 @@ static int damon_young_hugetlb_entry(pte_t *pte, unsigned long hmask, #define damon_young_hugetlb_entry NULL #endif /* CONFIG_HUGETLB_PAGE */ -static bool damon_va_young(struct mm_struct *mm, unsigned long addr, - unsigned long *folio_sz) +static bool damon_va_young(struct mm_struct *mm, unsigned long addr) { struct damon_young_walk_private arg = { - .folio_sz = folio_sz, .young = false, }; @@ -493,29 +486,18 @@ static bool damon_va_young(struct mm_struct *mm, unsigned long addr, * r the region to be checked */ static void __damon_va_check_access(struct mm_struct *mm, - struct damon_region *r, bool same_target, + struct damon_region *r, struct damon_attrs *attrs) { - static unsigned long last_addr; - static unsigned long last_folio_sz = PAGE_SIZE; - static bool last_accessed; + bool accessed; if (!mm) { damon_update_region_access_rate(r, false, attrs); return; } - /* If the region is in the last checked page, reuse the result */ - if (same_target && (ALIGN_DOWN(last_addr, last_folio_sz) == - ALIGN_DOWN(r->sampling_addr, last_folio_sz))) { - damon_update_region_access_rate(r, last_accessed, attrs); - return; - } - - last_accessed = damon_va_young(mm, r->sampling_addr, &last_folio_sz); - damon_update_region_access_rate(r, last_accessed, attrs); - - last_addr = r->sampling_addr; + accessed = damon_va_young(mm, r->sampling_addr); + damon_update_region_access_rate(r, accessed, attrs); } static unsigned int damon_va_check_accesses(struct damon_ctx *ctx) @@ -524,16 +506,12 @@ static unsigned int damon_va_check_accesses(struct damon_ctx *ctx) struct mm_struct *mm; struct damon_region *r; unsigned int max_nr_accesses = 0; - bool same_target; damon_for_each_target(t, ctx) { mm = damon_get_mm(t); - same_target = false; damon_for_each_region(r, t) { - __damon_va_check_access(mm, r, same_target, - &ctx->attrs); + __damon_va_check_access(mm, r, &ctx->attrs); max_nr_accesses = max(r->nr_accesses, max_nr_accesses); - same_target = true; } if (mm) mmput(mm); diff --git a/mm/gup.c b/mm/gup.c index 0692119b790431..bde05664fe9a9e 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -2273,6 +2273,7 @@ static unsigned long collect_longterm_unpinnable_folios( for (folio = pofs_get_folio(pofs, i); folio; folio = pofs_next_folio(folio, pofs, &i)) { + const int pin_refs = folio_has_pincount(folio) ? 1 : GUP_PIN_COUNTING_BIAS; if (folio_is_longterm_pinnable(folio)) continue; @@ -2287,15 +2288,20 @@ static unsigned long collect_longterm_unpinnable_folios( continue; } + /* + * We drain not only to make the folio_isolate_lru() succeed, + * but also to remove any other folio references from LRU + * caches. + */ if (drained == 0 && folio_may_be_lru_cached(folio) && folio_ref_count(folio) != - folio_expected_ref_count(folio) + 1) { + folio_expected_ref_count(folio) + pin_refs) { lru_add_drain(); drained = 1; } if (drained == 1 && folio_may_be_lru_cached(folio) && folio_ref_count(folio) != - folio_expected_ref_count(folio) + 1) { + folio_expected_ref_count(folio) + pin_refs) { lru_add_drain_all(); drained = 2; } diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 5d94fa4c74fdb6..0dbed3ba5c8a19 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2340,8 +2340,8 @@ bool madvise_free_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma, goto out; if (unlikely(!pmd_present(orig_pmd))) { - VM_BUG_ON(thp_migration_supported() && - !pmd_is_migration_entry(orig_pmd)); + VM_WARN_ON_ONCE(!pmd_is_migration_entry(orig_pmd) && + !pmd_is_device_private_entry(orig_pmd)); goto out; } @@ -2414,6 +2414,8 @@ static void zap_huge_pmd_folio(struct mm_struct *mm, struct vm_area_struct *vma, add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR); + if (is_present && pmd_dirty(pmdval)) + folio_mark_dirty(folio); if (is_present && pmd_young(pmdval) && likely(vma_has_recency(vma))) folio_mark_accessed(folio); @@ -4027,34 +4029,42 @@ static int __folio_split(struct folio *folio, unsigned int new_order, XA_STATE(xas, &folio->mapping->i_pages, folio->index); struct folio *end_folio = folio_next(folio); bool is_anon = folio_test_anon(folio); + struct mem_cgroup *memcg, *old_memcg; struct address_space *mapping = NULL; struct anon_vma *anon_vma = NULL; int old_order = folio_order(folio); struct folio *new_folio, *next; int nr_shmem_dropped = 0; enum ttu_flags ttu_flags = 0; - int ret; pgoff_t end = 0; + int ret; VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio); VM_WARN_ON_ONCE_FOLIO(!folio_test_large(folio), folio); if (folio != page_folio(split_at) || folio != page_folio(lock_at)) { ret = -EINVAL; - goto out; + goto out_no_memcg; } if (new_order >= old_order) { ret = -EINVAL; - goto out; + goto out_no_memcg; } ret = folio_check_splittable(folio, new_order, split_type); if (ret) { VM_WARN_ONCE(ret == -EINVAL, "Tried to split an unsplittable folio"); - goto out; + goto out_no_memcg; } + /* + * switch to folio's memcg as xarray node allocation can happen and + * needs to charge to it. + */ + memcg = get_mem_cgroup_from_folio(folio); + old_memcg = set_active_memcg(memcg); + if (is_anon) { /* * The caller does not necessarily hold an mmap_lock that would @@ -4197,6 +4207,10 @@ static int __folio_split(struct folio *folio, unsigned int new_order, if (mapping) i_mmap_unlock_read(mapping); out: + /* restore to caller's old_memcg */ + set_active_memcg(old_memcg); + mem_cgroup_put(memcg); +out_no_memcg: xas_destroy(&xas); if (is_pmd_order(old_order)) count_vm_event(!ret ? THP_SPLIT_PAGE : THP_SPLIT_PAGE_FAILED); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index e93c4d2456aa46..ca080806de17ae 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -2002,7 +2002,8 @@ int dissolve_free_hugetlb_folio(struct folio *folio) if (h->surplus_huge_pages_node[folio_nid(folio)]) adjust_surplus = true; remove_hugetlb_folio(h, folio, adjust_surplus); - h->max_huge_pages--; + if (!adjust_surplus) + h->max_huge_pages--; spin_unlock_irq(&hugetlb_lock); /* @@ -2022,7 +2023,8 @@ int dissolve_free_hugetlb_folio(struct folio *folio) if (rc) { spin_lock_irq(&hugetlb_lock); add_hugetlb_folio(h, folio, adjust_surplus); - h->max_huge_pages++; + if (!adjust_surplus) + h->max_huge_pages++; goto out; } } else { @@ -3353,7 +3355,7 @@ static void __init gather_bootmem_prealloc_parallel(unsigned long start, gather_bootmem_prealloc_node(nid); } -static void __init gather_bootmem_prealloc(void) +void __init hugetlb_bootmem_struct_page_init(void) { struct padata_mt_job job = { .thread_fn = gather_bootmem_prealloc_parallel, @@ -3365,6 +3367,31 @@ static void __init gather_bootmem_prealloc(void) .max_threads = num_node_state(N_MEMORY), .numa_aware = true, }; +#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP + struct zone *zone; + + for_each_zone(zone) { + for (int i = 0; i < NR_VMEMMAP_TAILS; i++) { + struct page *tail, *p; + unsigned int order; + + tail = zone->vmemmap_tails[i]; + if (!tail) + continue; + + order = i + VMEMMAP_TAIL_MIN_ORDER; + p = page_to_virt(tail); + /* + * prep_and_add_bootmem_folios() can access pageblock + * flags on bootmem HugeTLB pages, so initialize the + * shared tail struct pages here before bootmem folios + * start using them. + */ + for (int j = 0; j < PAGE_SIZE / sizeof(struct page); j++) + init_compound_tail(p + j, NULL, order, zone); + } + } +#endif padata_do_multithreaded(&job); } @@ -3557,7 +3584,7 @@ static unsigned long __init hugetlb_pages_alloc_boot(struct hstate *h) * - For gigantic pages, this is called early in the boot process and * pages are allocated from memblock allocated or something similar. * Gigantic pages are actually added to pools later with the routine - * gather_bootmem_prealloc. + * hugetlb_bootmem_struct_page_init. * - For non-gigantic pages, this is called later in the boot process after * all of mm is up and functional. Pages are allocated from buddy and * then added to hugetlb pools. @@ -4127,7 +4154,6 @@ static int __init hugetlb_init(void) } hugetlb_init_hstates(); - gather_bootmem_prealloc(); report_hugepages(); hugetlb_sysfs_init(); @@ -5157,6 +5183,7 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, bool adjust_reservation; unsigned long last_addr_mask; + i_mmap_assert_write_locked(vma->vm_file->f_mapping); WARN_ON(!is_vm_hugetlb_page(vma)); BUG_ON(start & ~huge_page_mask(h)); BUG_ON(end & ~huge_page_mask(h)); @@ -5248,7 +5275,10 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, /* * Restore the reservation for anonymous page, otherwise the - * backing page could be stolen by someone. + * backing page could be stolen by someone. Restore only on the + * last unmap, otherwise the owner could empty its resv map + * while the folio is still mapped by a child. Note that holding + * i_mmap_lock_write is needed to check the number of mappings. * If there we are freeing a surplus, do not set the restore * reservation bit. */ @@ -5256,7 +5286,7 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, spin_lock_irq(&hugetlb_lock); if (!h->surplus_huge_pages && __vma_private_lock(vma) && - folio_test_anon(folio)) { + !folio_mapped(folio) && folio_test_anon(folio)) { folio_set_hugetlb_restore_reserve(folio); /* Reservation to be adjusted after the spin lock */ adjust_reservation = true; @@ -7215,14 +7245,14 @@ void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int re * There is no need to transfer the per-node surplus state * when we do not cross the node. */ - if (new_nid == old_nid) - return; - spin_lock_irq(&hugetlb_lock); - if (h->surplus_huge_pages_node[old_nid]) { - h->surplus_huge_pages_node[old_nid]--; - h->surplus_huge_pages_node[new_nid]++; + if (new_nid != old_nid) { + spin_lock_irq(&hugetlb_lock); + if (h->surplus_huge_pages_node[old_nid]) { + h->surplus_huge_pages_node[old_nid]--; + h->surplus_huge_pages_node[new_nid]++; + } + spin_unlock_irq(&hugetlb_lock); } - spin_unlock_irq(&hugetlb_lock); } /* diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index e0083de1ca82b5..ecb6e0b7819a0d 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -97,6 +97,7 @@ static void hugetlb_cgroup_init(struct hugetlb_cgroup *h_cgroup, struct page_counter *fault, *fault_parent = NULL; struct page_counter *rsvd, *rsvd_parent = NULL; unsigned long limit; + int ret; if (parent_h_cgroup) { fault_parent = hugetlb_cgroup_counter_from_cgroup( @@ -118,8 +119,10 @@ static void hugetlb_cgroup_init(struct hugetlb_cgroup *h_cgroup, limit = round_down(PAGE_COUNTER_MAX, pages_per_huge_page(&hstates[idx])); - VM_BUG_ON(page_counter_set_max(fault, limit)); - VM_BUG_ON(page_counter_set_max(rsvd, limit)); + ret = page_counter_set_max(fault, limit); + VM_WARN_ON_ONCE(ret); + ret = page_counter_set_max(rsvd, limit); + VM_WARN_ON_ONCE(ret); } } diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c index 39344d6c78d84e..3e22161686d2f5 100644 --- a/mm/hugetlb_cma.c +++ b/mm/hugetlb_cma.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -30,15 +31,25 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask, int node; struct folio *folio; struct page *page = NULL; + const nodemask_t *nmask; + unsigned int cpuset_mems_cookie; if (!hugetlb_cma_size) return NULL; - if (hugetlb_cma[nid]) +retry_cpuset: + if (!nodemask) { + cpuset_mems_cookie = read_mems_allowed_begin(); + nmask = &cpuset_current_mems_allowed; + } else { + nmask = nodemask; + } + + if (hugetlb_cma[nid] && node_isset(nid, *nmask)) page = cma_alloc_frozen_compound(hugetlb_cma[nid], order); if (!page && !(gfp_mask & __GFP_THISNODE)) { - for_each_node_mask(node, *nodemask) { + for_each_node_mask(node, *nmask) { if (node == nid || !hugetlb_cma[node]) continue; @@ -48,8 +59,12 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask, } } - if (!page) + if (!page) { + if (!nodemask && + unlikely(read_mems_allowed_retry(cpuset_mems_cookie))) + goto retry_cpuset; return NULL; + } folio = page_folio(page); folio_set_hugetlb_cma(folio); diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c index 133b46dfb09f5c..ea6af85bfec13c 100644 --- a/mm/hugetlb_vmemmap.c +++ b/mm/hugetlb_vmemmap.c @@ -635,12 +635,12 @@ static void __hugetlb_vmemmap_optimize_folios(struct hstate *h, * mirrored tail page structs RO. */ spfn = (unsigned long)&folio->page; - epfn = spfn + pages_per_huge_page(h); + epfn = spfn + hugetlb_vmemmap_size(h); vmemmap_wrprotect_hvo(spfn, epfn, folio_nid(folio), HUGETLB_VMEMMAP_RESERVE_SIZE); - register_page_bootmem_memmap(pfn_to_section_nr(spfn), + register_page_bootmem_memmap(pfn_to_section_nr(folio_pfn(folio)), &folio->page, - HUGETLB_VMEMMAP_RESERVE_SIZE); + HUGETLB_VMEMMAP_RESERVE_PAGES); continue; } @@ -870,27 +870,10 @@ static const struct ctl_table hugetlb_vmemmap_sysctls[] = { static int __init hugetlb_vmemmap_init(void) { const struct hstate *h; - struct zone *zone; /* HUGETLB_VMEMMAP_RESERVE_SIZE should cover all used struct pages */ BUILD_BUG_ON(__NR_USED_SUBPAGE > HUGETLB_VMEMMAP_RESERVE_PAGES); - for_each_zone(zone) { - for (int i = 0; i < NR_VMEMMAP_TAILS; i++) { - struct page *tail, *p; - unsigned int order; - - tail = zone->vmemmap_tails[i]; - if (!tail) - continue; - - order = i + VMEMMAP_TAIL_MIN_ORDER; - p = page_to_virt(tail); - for (int j = 0; j < PAGE_SIZE / sizeof(struct page); j++) - init_compound_tail(p + j, NULL, order, zone); - } - } - for_each_hstate(h) { if (hugetlb_vmemmap_optimizable(h)) { register_sysctl_init("vm", hugetlb_vmemmap_sysctls); diff --git a/mm/internal.h b/mm/internal.h index 181e79f1d6a207..5acba3470659d9 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -1059,7 +1059,15 @@ struct compact_control { * immediately when one is created during the free path. */ struct capture_control { - struct compact_control *cc; + struct zone *zone; + int migratetype; + /* + * Allocation request order. May differ from the compaction + * order: defrag_mode promotes sub-block allocations to + * pageblock-order compaction; capture still matches at the + * original allocation order so prep_new_page() is consistent. + */ + int order; struct page *page; }; diff --git a/mm/kasan/quarantine.c b/mm/kasan/quarantine.c index 6958aa713c67ee..16f4e67beee82b 100644 --- a/mm/kasan/quarantine.c +++ b/mm/kasan/quarantine.c @@ -355,7 +355,12 @@ void kasan_quarantine_remove_cache(struct kmem_cache *cache) */ on_each_cpu(per_cpu_remove_cache, cache, 1); - for_each_online_cpu(cpu) { + /* + * A CPU can go offline after on_each_cpu() returns, leaving cache + * objects on that CPU's shrink list. Scan all possible CPUs to + * drain those lists. + */ + for_each_possible_cpu(cpu) { sq = per_cpu_ptr(&shrink_qlist, cpu); raw_spin_lock_irqsave(&sq->lock, flags); qlist_move_cache(&sq->qlist, &to_free, cache); diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 617bca76db49b6..2d0aee0312f246 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -452,12 +452,6 @@ int hugepage_madvise(struct vm_area_struct *vma, case MADV_HUGEPAGE: *vm_flags &= ~VM_NOHUGEPAGE; *vm_flags |= VM_HUGEPAGE; - /* - * If the vma become good for khugepaged to scan, - * register it here without waiting a page fault that - * may not happen any time soon. - */ - khugepaged_enter_vma(vma, *vm_flags); break; case MADV_NOHUGEPAGE: *vm_flags &= ~VM_HUGEPAGE; diff --git a/mm/kmemleak.c b/mm/kmemleak.c index e196f53f9b4620..e3c9dbde014ba5 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -1525,22 +1525,25 @@ static int scan_should_stop(void) /* * Scan a memory block (exclusive range) for valid pointers and add those - * found to the gray list. + * found to the gray list. Return non-zero if the scan was interrupted. */ -static void scan_block(void *_start, void *_end, - struct kmemleak_object *scanned) +static int scan_block(void *_start, void *_end, + struct kmemleak_object *scanned) { unsigned long *ptr; unsigned long *start = PTR_ALIGN(_start, BYTES_PER_POINTER); unsigned long *end = _end - (BYTES_PER_POINTER - 1); unsigned long flags; + int stop = 0; raw_spin_lock_irqsave(&kmemleak_lock, flags); for (ptr = start; ptr < end; ptr++) { unsigned long pointer; - if (scan_should_stop()) + if (scan_should_stop()) { + stop = 1; break; + } kasan_disable_current(); pointer = *(unsigned long *)kasan_reset_tag((void *)ptr); @@ -1550,6 +1553,8 @@ static void scan_block(void *_start, void *_end, pointer_update_refs(scanned, pointer, OBJECT_PERCPU); } raw_spin_unlock_irqrestore(&kmemleak_lock, flags); + + return stop; } /* @@ -1564,7 +1569,7 @@ static void scan_large_block(void *start, void *end) next = min(start + MAX_SCAN_SIZE, end); scan_block(start, next, NULL); start = next; - cond_resched(); + cond_resched_tasks_rcu_qs(); } } #endif @@ -1599,7 +1604,7 @@ static void scan_object(struct kmemleak_object *object) scan_block(start, end, object); raw_spin_unlock_irqrestore(&object->lock, flags); - cond_resched(); + cond_resched_tasks_rcu_qs(); raw_spin_lock_irqsave(&object->lock, flags); if (!(object->flags & OBJECT_ALLOCATED)) break; @@ -1621,7 +1626,7 @@ static void scan_object(struct kmemleak_object *object) break; raw_spin_unlock_irqrestore(&object->lock, flags); - cond_resched(); + cond_resched_tasks_rcu_qs(); raw_spin_lock_irqsave(&object->lock, flags); } while (object->flags & OBJECT_ALLOCATED); } else { @@ -1649,7 +1654,7 @@ static void scan_gray_list(void) */ object = list_entry(gray_list.next, typeof(*object), gray_list); while (&object->gray_list != &gray_list) { - cond_resched(); + cond_resched_tasks_rcu_qs(); /* may add new objects to the list */ if (!scan_should_stop()) @@ -1684,7 +1689,7 @@ static void kmemleak_cond_resched(struct kmemleak_object *object) raw_spin_unlock_irq(&kmemleak_lock); rcu_read_unlock(); - cond_resched(); + cond_resched_tasks_rcu_qs(); rcu_read_lock(); raw_spin_lock_irq(&kmemleak_lock); @@ -1696,6 +1701,43 @@ static void kmemleak_cond_resched(struct kmemleak_object *object) put_object(object); } +/* + * Scan all task kernel stacks, rescheduling between tasks. Each task is looked + * up and pinned within its own RCU read-side section, so no lock is held across + * the scan and the walk cannot trip the soft lockup watchdog. + */ +static void kmemleak_scan_task_stacks(void) +{ + struct pid *pid; + int nr = 1; + int stop = 0; + + do { + struct task_struct *p = NULL; + + rcu_read_lock(); + pid = find_ge_pid(nr, &init_pid_ns); + if (pid) { + nr = pid_nr(pid) + 1; + p = pid_task(pid, PIDTYPE_PID); + if (p) + get_task_struct(p); + } + rcu_read_unlock(); + + if (p) { + void *stack = try_get_task_stack(p); + + if (stack) { + stop = scan_block(stack, stack + THREAD_SIZE, NULL); + put_task_stack(p); + } + put_task_struct(p); + } + cond_resched_tasks_rcu_qs(); + } while (pid && !stop); +} + /* * Print one leak inline. The hex dump is gated on OBJECT_ALLOCATED so it * does not touch user memory that was freed concurrently; the rest of the @@ -1866,7 +1908,7 @@ static void kmemleak_scan(void) struct page *page = pfn_to_online_page(pfn); if (!(pfn & 63)) - cond_resched(); + cond_resched_tasks_rcu_qs(); if (!page) continue; @@ -1885,19 +1927,8 @@ static void kmemleak_scan(void) /* * Scanning the task stacks (may introduce false negatives). */ - if (kmemleak_stack_scan) { - struct task_struct *p, *g; - - rcu_read_lock(); - for_each_process_thread(g, p) { - void *stack = try_get_task_stack(p); - if (stack) { - scan_block(stack, stack + THREAD_SIZE, NULL); - put_task_stack(p); - } - } - rcu_read_unlock(); - } + if (kmemleak_stack_scan) + kmemleak_scan_task_stacks(); /* * Scan the objects already referenced from the sections scanned diff --git a/mm/madvise.c b/mm/madvise.c index 77552b03d318d9..65e40fc8699db4 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -177,6 +177,14 @@ static int madvise_update_vma(vm_flags_t new_flags, /* vm_flags is protected by the mmap_lock held in write mode. */ vma_start_write(vma); vma->flags = new_vma_flags; + /* + * If the vma become good for khugepaged to scan, + * register it here without waiting a page fault that + * may not happen any time soon. + */ + if (vma_flags_test(&new_vma_flags, VMA_HUGEPAGE_BIT)) + khugepaged_enter_vma(vma, vma_flags_to_legacy(new_vma_flags)); + if (set_new_anon_name) return replace_anon_vma_name(vma, anon_name); @@ -388,8 +396,8 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd, goto huge_unlock; if (unlikely(!pmd_present(orig_pmd))) { - VM_BUG_ON(thp_migration_supported() && - !pmd_is_migration_entry(orig_pmd)); + VM_WARN_ON_ONCE(!pmd_is_migration_entry(orig_pmd) && + !pmd_is_device_private_entry(orig_pmd)); goto huge_unlock; } diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c index 7650692115678c..f1a64d0604a590 100644 --- a/mm/memcontrol-v1.c +++ b/mm/memcontrol-v1.c @@ -95,7 +95,6 @@ enum { RES_LIMIT, RES_MAX_USAGE, RES_FAILCNT, - RES_SOFT_LIMIT, }; #ifdef CONFIG_LOCKDEP @@ -1181,13 +1180,13 @@ static ssize_t memcg_write_event_control(struct kernfs_open_file *of, event->unregister_event = mem_cgroup_usage_unregister_event; } else if (!strcmp(name, "memory.oom_control")) { pr_warn_once("oom_control is deprecated and will be removed. " - "Please report your usecase to linux-mm-@kvack.org" + "Please report your usecase to linux-mm@kvack.org" " if you depend on this functionality.\n"); event->register_event = mem_cgroup_oom_register_event; event->unregister_event = mem_cgroup_oom_unregister_event; } else if (!strcmp(name, "memory.pressure_level")) { pr_warn_once("pressure_level is deprecated and will be removed. " - "Please report your usecase to linux-mm-@kvack.org " + "Please report your usecase to linux-mm@kvack.org " "if you depend on this functionality.\n"); event->register_event = vmpressure_register_event; event->unregister_event = vmpressure_unregister_event; @@ -1589,6 +1588,30 @@ static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css, return -EINVAL; } +static u64 mem_cgroup_soft_limit_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + return (u64)PAGE_COUNTER_MAX * PAGE_SIZE; +} + +static ssize_t mem_cgroup_soft_limit_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) +{ + unsigned long nr_pages; + int ret; + + ret = page_counter_memparse(strstrip(buf), "-1", &nr_pages); + if (ret) + return ret; + + pr_warn_once("soft_limit_in_bytes is deprecated and will be removed. " + "Writing any value to this file has no effect. " + "Please report your usecase to linux-mm@kvack.org if you " + "depend on this functionality.\n"); + + return nbytes; +} + static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css, struct cftype *cft) { @@ -1625,8 +1648,6 @@ static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css, return (u64)counter->watermark * PAGE_SIZE; case RES_FAILCNT: return counter->failcnt; - case RES_SOFT_LIMIT: - return (u64)READ_ONCE(memcg->soft_limit) * PAGE_SIZE; default: BUG(); } @@ -1721,17 +1742,6 @@ static ssize_t mem_cgroup_write(struct kernfs_open_file *of, break; } break; - case RES_SOFT_LIMIT: - if (IS_ENABLED(CONFIG_PREEMPT_RT)) { - ret = -EOPNOTSUPP; - } else { - pr_warn_once("soft_limit_in_bytes is deprecated and will be removed. " - "Please report your usecase to linux-mm@kvack.org if you " - "depend on this functionality.\n"); - WRITE_ONCE(memcg->soft_limit, nr_pages); - ret = 0; - } - break; } return ret ?: nbytes; } @@ -2040,7 +2050,7 @@ static int mem_cgroup_oom_control_write(struct cgroup_subsys_state *css, struct mem_cgroup *memcg = mem_cgroup_from_css(css); pr_warn_once("oom_control is deprecated and will be removed. " - "Please report your usecase to linux-mm-@kvack.org if you " + "Please report your usecase to linux-mm@kvack.org if you " "depend on this functionality.\n"); /* cannot set to root cgroup and only 0 and 1 are allowed */ @@ -2085,9 +2095,8 @@ struct cftype mem_cgroup_legacy_files[] = { }, { .name = "soft_limit_in_bytes", - .private = MEMFILE_PRIVATE(_MEM, RES_SOFT_LIMIT), - .write = mem_cgroup_write, - .read_u64 = mem_cgroup_read_u64, + .write = mem_cgroup_soft_limit_write, + .read_u64 = mem_cgroup_soft_limit_read, }, { .name = "failcnt", diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 6dc4888a90f3fd..405faa4b847226 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2648,6 +2648,19 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask, if (!gfpflags_allow_blocking(gfp_mask)) goto nomem; + /* + * OOM victim still needs to charge memory to exit. OOM reaper should + * help but it might fail on mmap_lock contention. If the victim is a + * large thread group then all exiting threads might compete on oom_lock + * just to learn that there is nothing really killable anymore. Bail + * out early and fail the charge to expedite their exit. They are + * considered fully reclaimed by the oom reaper and they shouldn't + * contribute further charges. + */ + if (tsk_is_oom_victim(current) && + mm_flags_test(MMF_OOM_SKIP, current->signal->oom_mm)) + goto nomem; + __memcg_memory_event(mem_over_limit, MEMCG_MAX, allow_spinning); raised_max_event = true; @@ -2907,10 +2920,9 @@ struct mem_cgroup *mem_cgroup_from_virt(void *p) return folio_memcg_check(virt_to_folio(p)); } -static struct obj_cgroup *__get_obj_cgroup_from_memcg(struct mem_cgroup *memcg) +static struct obj_cgroup *__get_obj_cgroup_from_memcg(struct mem_cgroup *memcg, + int nid) { - int nid = numa_node_id(); - for (; memcg; memcg = parent_mem_cgroup(memcg)) { struct obj_cgroup *objcg = rcu_dereference(memcg->nodeinfo[nid]->objcg); @@ -2921,12 +2933,13 @@ static struct obj_cgroup *__get_obj_cgroup_from_memcg(struct mem_cgroup *memcg) return NULL; } -static inline struct obj_cgroup *get_obj_cgroup_from_memcg(struct mem_cgroup *memcg) +static inline struct obj_cgroup *get_obj_cgroup_from_memcg(struct mem_cgroup *memcg, + int nid) { struct obj_cgroup *objcg; rcu_read_lock(); - objcg = __get_obj_cgroup_from_memcg(memcg); + objcg = __get_obj_cgroup_from_memcg(memcg, nid); rcu_read_unlock(); return objcg; @@ -2970,7 +2983,7 @@ static struct obj_cgroup *current_objcg_update(void) rcu_read_lock(); memcg = mem_cgroup_from_task(current); - objcg = __get_obj_cgroup_from_memcg(memcg); + objcg = __get_obj_cgroup_from_memcg(memcg, numa_node_id()); rcu_read_unlock(); /* @@ -4180,9 +4193,11 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) page_counter_init(&memcg->swap, &parent->swap, false); #ifdef CONFIG_MEMCG_V1 memcg->memory.track_failcnt = !memcg_on_dfl; + memcg->memsw.track_failcnt = !memcg_on_dfl; WRITE_ONCE(memcg->oom_kill_disable, READ_ONCE(parent->oom_kill_disable)); page_counter_init(&memcg->kmem, &parent->kmem, false); page_counter_init(&memcg->tcpmem, &parent->tcpmem, false); + memcg->tcpmem.track_failcnt = !memcg_on_dfl; #endif } else { init_memcg_stats(); @@ -4438,6 +4453,7 @@ static void flush_nmi_stats(struct mem_cgroup *memcg, struct mem_cgroup *parent, int index = memcg_stats_index(MEMCG_KMEM); memcg->vmstats->state[index] += kmem; + memcg->vmstats->state_local[index] += kmem; if (parent) parent->vmstats->state_pending[index] += kmem; } @@ -4455,9 +4471,11 @@ static void flush_nmi_stats(struct mem_cgroup *memcg, struct mem_cgroup *parent, int index = memcg_stats_index(NR_SLAB_RECLAIMABLE_B); lstats->state[index] += slab; + lstats->state_local[index] += slab; if (plstats) plstats->state_pending[index] += slab; memcg->vmstats->state[index] += slab; + memcg->vmstats->state_local[index] += slab; if (parent) parent->vmstats->state_pending[index] += slab; } @@ -4466,9 +4484,11 @@ static void flush_nmi_stats(struct mem_cgroup *memcg, struct mem_cgroup *parent, int index = memcg_stats_index(NR_SLAB_UNRECLAIMABLE_B); lstats->state[index] += slab; + lstats->state_local[index] += slab; if (plstats) plstats->state_pending[index] += slab; memcg->vmstats->state[index] += slab; + memcg->vmstats->state_local[index] += slab; if (parent) parent->vmstats->state_pending[index] += slab; } @@ -4788,6 +4808,9 @@ static ssize_t memory_high_write(struct kernfs_open_file *of, unsigned long nr_pages = page_counter_read(&memcg->memory); unsigned long reclaimed; + if (high != READ_ONCE(memcg->memory.high)) + break; + if (nr_pages <= high) break; @@ -4839,6 +4862,9 @@ static ssize_t memory_max_write(struct kernfs_open_file *of, for (;;) { unsigned long nr_pages = page_counter_read(&memcg->memory); + if (max != READ_ONCE(memcg->memory.max)) + break; + if (nr_pages <= max) break; @@ -5120,7 +5146,7 @@ static int charge_memcg(struct folio *folio, struct mem_cgroup *memcg, int ret = 0; struct obj_cgroup *objcg; - objcg = get_obj_cgroup_from_memcg(memcg); + objcg = get_obj_cgroup_from_memcg(memcg, folio_nid(folio)); /* Do not account at the root objcg level. */ if (!obj_cgroup_is_root(objcg)) ret = try_charge_memcg(memcg, gfp, folio_nr_pages(folio)); @@ -5319,6 +5345,46 @@ void __mem_cgroup_uncharge_folios(struct folio_batch *folios) uncharge_batch(&ug); } +/* + * An LRU folio must hold the objcg belonging to its own node. + * + * memcg_reparent_objcgs() reparents a dying cgroup one node at a time: the + * folios on that node's LRU lists move to the parent and that node's objcg is + * redirected to the parent, atomically under the node's lru_lock. + * folio_lruvec_lock() relies on this to provide a stable folio<->lruvec + * binding. If a folio holds another node's objcg, its list membership and its + * lruvec resolution change in separate lock sections, and an LRU operation in + * between can re-add the folio to, and strand it on, the LRU list of a dead + * memcg. + * + * So when migration transfers the memcg state to a folio on another node, + * re-derive the objcg for the destination node. If the memcg is dying and the + * destination node has already been reparented, the lookup walks up to the + * nearest live ancestor - which is also where that node's LRU lists went. + * + * Returns the objcg to commit to @new, with a reference for the caller. + */ +static struct obj_cgroup *get_migration_objcg(struct folio *old, + struct folio *new) +{ + struct obj_cgroup *old_objcg, *new_objcg; + int new_nid = folio_nid(new); + + old_objcg = get_obj_cgroup_from_folio(old); + + if (folio_nid(old) == new_nid) + return old_objcg; + + rcu_read_lock(); + new_objcg = __get_obj_cgroup_from_memcg(obj_cgroup_memcg(old_objcg), + new_nid); + rcu_read_unlock(); + + obj_cgroup_put(old_objcg); + + return new_objcg; +} + /** * mem_cgroup_replace_folio - Charge a folio's replacement. * @old: Currently circulating folio. @@ -5347,21 +5413,28 @@ void mem_cgroup_replace_folio(struct folio *old, struct folio *new) if (folio_memcg_charged(new)) return; - objcg = folio_objcg(old); - VM_WARN_ON_ONCE_FOLIO(!objcg, old); - if (!objcg) + VM_WARN_ON_ONCE_FOLIO(!folio_objcg(old), old); + if (!folio_objcg(old)) return; + objcg = get_migration_objcg(old, new); + rcu_read_lock(); memcg = obj_cgroup_memcg(objcg); - /* Force-charge the new page. The old one will be freed soon */ + + /* + * Force-charge the new page. The old one will be freed soon. + * + * The rootness of the committed objcg decides whether the final + * uncharge of @new goes through the page counters (see + * uncharge_folio()); charge them only if the uncharge will. + */ if (!obj_cgroup_is_root(objcg)) { page_counter_charge(&memcg->memory, nr_pages); if (do_memsw_account()) page_counter_charge(&memcg->memsw, nr_pages); } - obj_cgroup_get(objcg); commit_charge(new, objcg); memcg1_commit_charge(new, memcg); rcu_read_unlock(); @@ -5373,14 +5446,15 @@ void mem_cgroup_replace_folio(struct folio *old, struct folio *new) * @new: Replacement folio. * * Transfer the memcg data from the old folio to the new folio for migration. - * The old folio's data info will be cleared. Note that the memory counters - * will remain unchanged throughout the process. + * The old folio's data info will be cleared. The memory counters remain + * unchanged, unless the charge moves out of a fully reparented ancestry + * and has to be settled (see below). * * Both folios must be locked, @new->mapping must be set up. */ void mem_cgroup_migrate(struct folio *old, struct folio *new) { - struct obj_cgroup *objcg; + struct obj_cgroup *objcg, *new_objcg; VM_BUG_ON_FOLIO(!folio_test_locked(old), old); VM_BUG_ON_FOLIO(!folio_test_locked(new), new); @@ -5401,12 +5475,30 @@ void mem_cgroup_migrate(struct folio *old, struct folio *new) if (!objcg) return; - /* Transfer the charge and the objcg ref */ - commit_charge(new, objcg); + new_objcg = get_migration_objcg(old, new); + + /* + * @old was charged through a non-root objcg, so its charge is in the + * page counters. If the re-derivation walked up to the root objcg - + * @old's entire ancestry is dying and already reparented - the final + * uncharge of @new will skip the page counters (see uncharge_folio()). + * Settle them now: this is @old's eventual uncharge, moved up to the + * point where its charge record ends. + */ + if (obj_cgroup_is_root(new_objcg) && !obj_cgroup_is_root(objcg)) { + rcu_read_lock(); + memcg_uncharge(obj_cgroup_memcg(objcg), folio_nr_pages(old)); + rcu_read_unlock(); + } + + commit_charge(new, new_objcg); /* Warning should never happen, so don't worry about refcount non-0 */ WARN_ON_ONCE(folio_unqueue_deferred_split(old)); old->memcg_data = 0; + + /* @new holds its own reference now, drop @old's */ + obj_cgroup_put(objcg); } DEFINE_STATIC_KEY_FALSE(memcg_sockets_enabled_key); diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 36699fabd3c223..9d7cb63d327324 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -653,12 +653,14 @@ static void queue_folios_pmd(pmd_t *pmd, struct mm_walk *walk) { struct folio *folio; struct queue_pages *qp = walk->private; + pmd_t pmdval = pmdp_get(pmd); - if (unlikely(pmd_is_migration_entry(*pmd))) { - qp->nr_failed++; + if (unlikely(!pmd_present(pmdval))) { + if (pmd_is_migration_entry(pmdval)) + qp->nr_failed++; return; } - folio = pmd_folio(*pmd); + folio = pmd_folio(pmdval); if (is_huge_zero_folio(folio)) { walk->action = ACTION_CONTINUE; return; @@ -2057,24 +2059,15 @@ struct mempolicy *get_vma_policy(struct vm_area_struct *vma, bool vma_policy_mof(struct vm_area_struct *vma) { struct mempolicy *pol; + pgoff_t ilx; + bool mof; - if (vma->vm_ops && vma->vm_ops->get_policy) { - bool ret = false; - pgoff_t ilx; /* ignored here */ - - pol = vma->vm_ops->get_policy(vma, vma->vm_start, &ilx); - if (pol && (pol->flags & MPOL_F_MOF)) - ret = true; - mpol_cond_put(pol); - - return ret; - } - - pol = vma->vm_policy; + pol = __get_vma_policy(vma, vma->vm_start, &ilx); if (!pol) pol = get_task_policy(current); - - return pol->flags & MPOL_F_MOF; + mof = pol->flags & MPOL_F_MOF; + mpol_cond_put(pol); + return mof; } bool apply_policy_zone(struct mempolicy *policy, enum zone_type zone) @@ -2683,7 +2676,7 @@ static unsigned long alloc_pages_bulk_weighted_interleave(gfp_t gfp, prev_node = node; /* create a local copy of node weights to operate on outside rcu */ - weights = kzalloc(nr_node_ids, GFP_KERNEL); + weights = kmalloc(nr_node_ids, gfp & GFP_RECLAIM_MASK); if (!weights) return total_allocated; diff --git a/mm/migrate.c b/mm/migrate.c index dd15a84b2a52ae..35381ce36a8f59 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -371,7 +371,11 @@ static bool remove_migration_pte(struct folio *folio, continue; } #endif - old_pte = ptep_get(pvmw.pte); + if (folio_test_hugetlb(folio)) + old_pte = huge_ptep_get(vma->vm_mm, pvmw.address, + pvmw.pte); + else + old_pte = ptep_get(pvmw.pte); if (rmap_walk_arg->map_unused_to_zeropage && try_to_map_unused_to_zeropage(&pvmw, folio, old_pte, idx)) continue; @@ -1830,7 +1834,7 @@ static int migrate_pages_batch(struct list_head *from, is_thp = folio_test_pmd_mappable(folio); nr_pages = folio_nr_pages(folio); - cond_resched(); + cond_resched_tasks_rcu_qs(); /* * The rare folio on the deferred split list should diff --git a/mm/migrate_device.c b/mm/migrate_device.c index 908d2d4ec43ac9..52ba68f2c9151f 100644 --- a/mm/migrate_device.c +++ b/mm/migrate_device.c @@ -1183,6 +1183,13 @@ static void __migrate_device_pages(unsigned long *src_pfns, MIGRATE_PFN_COMPOUND); goto next; } + + /* + * reset nr so that only first after-split folio + * is processed below + */ + VM_WARN_ON_ONCE(folio_test_large(folio)); + nr = 1; } else if ((src_pfns[i] & MIGRATE_PFN_MIGRATE) && (dst_pfns[i] & MIGRATE_PFN_COMPOUND) && !(src_pfns[i] & MIGRATE_PFN_COMPOUND)) { @@ -1222,6 +1229,12 @@ static void __migrate_device_pages(unsigned long *src_pfns, folio = page_folio(migrate_pfn_to_page(src_pfns[i+j])); newfolio = page_folio(migrate_pfn_to_page(dst_pfns[i+j])); + /* + * folio_free_swap() removed the folio from the swap + * cache. Refresh the saved mapping before migration. + */ + mapping = folio_mapping(folio); + r = folio_migrate_mapping(mapping, newfolio, folio, extra_cnt); if (r) src_pfns[i+j] &= ~MIGRATE_PFN_MIGRATE; @@ -1400,6 +1413,15 @@ int migrate_device_range(unsigned long *src_pfns, unsigned long start, src_pfns[i] = migrate_device_pfn_lock(pfn); nr = folio_nr_pages(folio); + if (nr > npages - i) { + if (src_pfns[i] & MIGRATE_PFN_MIGRATE) { + folio_unlock(folio); + folio_put(folio); + } + memset(&src_pfns[i], 0, + (npages - i) * sizeof(*src_pfns)); + break; + } if (nr > 1) { src_pfns[i] |= MIGRATE_PFN_COMPOUND; for (j = 1; j < nr; j++) @@ -1434,6 +1456,15 @@ int migrate_device_pfns(unsigned long *src_pfns, unsigned long npages) src_pfns[i] = migrate_device_pfn_lock(src_pfns[i]); nr = folio_nr_pages(folio); + if (nr > npages - i) { + if (src_pfns[i] & MIGRATE_PFN_MIGRATE) { + folio_unlock(folio); + folio_put(folio); + } + memset(&src_pfns[i], 0, + (npages - i) * sizeof(*src_pfns)); + break; + } if (nr > 1) { src_pfns[i] |= MIGRATE_PFN_COMPOUND; for (j = 1; j < nr; j++) diff --git a/mm/mm_init.c b/mm/mm_init.c index 498d62c4ece392..ab428508186925 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -2214,10 +2214,13 @@ bool __init deferred_grow_zone(struct zone *zone, unsigned int order) } /* - * There were no pages to initialize and free which means the zone's - * memory map is completely initialized. + * The loop only tests spfn before entering an iteration, so on exit it + * may point up to a section past the end of the zone. When it does, + * the rest of the zone has already been handed to + * deferred_init_memmap_chunk() and nothing is left to initialize. */ - pgdat->first_deferred_pfn = nr_pages ? spfn : ULONG_MAX; + pgdat->first_deferred_pfn = + spfn < zone_end_pfn(zone) ? spfn : ULONG_MAX; pgdat_resize_unlock(pgdat, &flags); @@ -2330,6 +2333,7 @@ void __init page_alloc_init_late(void) /* Reinit limits that are based on free pages after the kernel is up */ files_maxfiles_init(); #endif + hugetlb_bootmem_struct_page_init(); /* Accounting of total+free memory is stable at this point. */ mem_init_print_info(); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index ee902a468c2f5b..73645d2eda6f4d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -721,14 +721,14 @@ static inline struct capture_control *task_capc(struct zone *zone) return unlikely(capc) && !(current->flags & PF_KTHREAD) && !capc->page && - capc->cc->zone == zone ? capc : NULL; + capc->zone == zone ? capc : NULL; } static inline bool compaction_capture(struct capture_control *capc, struct page *page, int order, int migratetype) { - if (!capc || order != capc->cc->order) + if (!capc || order != capc->order) return false; /* Do not accidentally pollute CMA or isolated regions*/ @@ -744,12 +744,12 @@ compaction_capture(struct capture_control *capc, struct page *page, * have trouble finding a high-order free page. */ if (order < pageblock_order && migratetype == MIGRATE_MOVABLE && - capc->cc->migratetype != MIGRATE_MOVABLE) + capc->migratetype != MIGRATE_MOVABLE) return false; - if (migratetype != capc->cc->migratetype) - trace_mm_page_alloc_extfrag(page, capc->cc->order, order, - capc->cc->migratetype, migratetype); + if (migratetype != capc->migratetype) + trace_mm_page_alloc_extfrag(page, capc->order, order, + capc->migratetype, migratetype); capc->page = page; return true; @@ -2967,8 +2967,10 @@ static void __free_frozen_pages(struct page *page, unsigned int order, migratetype = MIGRATE_MOVABLE; } - if (unlikely((fpi_flags & FPI_TRYLOCK) && IS_ENABLED(CONFIG_PREEMPT_RT) - && (in_nmi() || in_hardirq()))) { + if (unlikely((fpi_flags & FPI_TRYLOCK) && + ((IS_ENABLED(CONFIG_PREEMPT_RT) && + (in_nmi() || in_hardirq())) || + (!IS_ENABLED(CONFIG_SMP) && in_nmi())))) { add_page_to_zone_llist(zone, page, order); return; } @@ -4146,18 +4148,67 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, struct page *page = NULL; unsigned long pflags; unsigned int noreclaim_flag; + struct capture_control capc = { + .zone = NULL, + .migratetype = ac->migratetype, + .order = order, + .page = NULL, + }; + int compact_order = order; - if (!order) + /* + * If fallbacks are not permitted (defrag_mode), we either + * need to reclaim space in a block of matching type, or clear + * out an entire block to allow __rmqueue_claim() to convert. + * + * Reclaim by itself is primarily freeing space in movable + * blocks, since that's where the LRU pages live. So this + * works for movable requests, but not for others. + * + * For those, promote the order to help make blocks, instead + * of spinning in reclaim alone unproductively. + */ + if ((alloc_flags & ALLOC_NOFRAGMENT) && ac->migratetype != MIGRATE_MOVABLE) + compact_order = max(order, pageblock_order); + + if (!compact_order) return NULL; psi_memstall_enter(&pflags); delayacct_compact_start(); + fs_reclaim_acquire(gfp_mask); noreclaim_flag = memalloc_noreclaim_save(); - *compact_result = try_to_compact_pages(gfp_mask, order, alloc_flags, ac, - prio, &page); + /* + * Make sure the structs are really initialized before we expose the + * capture control, in case we are interrupted and the interrupt handler + * frees a page. + */ + barrier(); + WRITE_ONCE(current->capture_control, &capc); + + *compact_result = try_to_compact_pages(gfp_mask, compact_order, + alloc_flags, ac, prio, &capc); + + /* + * Make sure we hide capture control first before we read the captured + * page pointer, otherwise an interrupt could free and capture a page + * and we would leak it. + */ + WRITE_ONCE(current->capture_control, NULL); + page = READ_ONCE(capc.page); + + /* + * Technically, it is also possible that compaction is skipped but + * the page is still captured out of luck(IRQ came and freed the page). + * Returning COMPACT_SUCCESS in such cases helps in properly accounting + * the COMPACT[STALL|FAIL] when compaction is skipped. + */ + if (page) + *compact_result = COMPACT_SUCCESS; memalloc_noreclaim_restore(noreclaim_flag); + fs_reclaim_release(gfp_mask); psi_memstall_leave(&pflags); delayacct_compact_end(); @@ -4182,7 +4233,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, struct zone *zone = page_zone(page); zone->compact_blockskip_flush = false; - compaction_defer_reset(zone, order, true); + compaction_defer_reset(zone, compact_order, true); count_vm_event(COMPACTSUCCESS); return page; } @@ -4422,9 +4473,14 @@ __alloc_pages_direct_reclaim(gfp_t gfp_mask, unsigned int order, struct page *page = NULL; unsigned long pflags; bool drained = false; + int reclaim_order = order; + + /* Match the slowpath compaction promotion in __alloc_pages_direct_compact */ + if ((alloc_flags & ALLOC_NOFRAGMENT) && ac->migratetype != MIGRATE_MOVABLE) + reclaim_order = max(order, pageblock_order); psi_memstall_enter(&pflags); - *did_some_progress = __perform_reclaim(gfp_mask, order, ac); + *did_some_progress = __perform_reclaim(gfp_mask, reclaim_order, ac); if (unlikely(!(*did_some_progress))) goto out; diff --git a/mm/page_owner.c b/mm/page_owner.c index 2dddcb6510aa1e..d8d264eb01b017 100644 --- a/mm/page_owner.c +++ b/mm/page_owner.c @@ -513,6 +513,7 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret, { #ifdef CONFIG_MEMCG unsigned long memcg_data; + struct obj_cgroup *objcg; struct mem_cgroup *memcg; bool online; char name[80]; @@ -522,11 +523,14 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret, if (!memcg_data || PageTail(page)) goto out_unlock; - if (memcg_data & MEMCG_DATA_OBJEXTS) + if (memcg_data & MEMCG_DATA_OBJEXTS) { ret += scnprintf(kbuf + ret, count - ret, "Slab cache page\n"); + goto out_unlock; + } - memcg = page_memcg_check(page); + objcg = (void *)(memcg_data & ~OBJEXTS_FLAGS_MASK); + memcg = objcg ? obj_cgroup_memcg(objcg) : NULL; if (!memcg) goto out_unlock; @@ -534,7 +538,7 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret, cgroup_name(memcg->css.cgroup, name, sizeof(name)); ret += scnprintf(kbuf + ret, count - ret, "Charged %sto %smemcg %s\n", - PageMemcgKmem(page) ? "(via objcg) " : "", + (memcg_data & MEMCG_DATA_KMEM) ? "(via objcg) " : "", online ? "" : "offline ", name); out_unlock: diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c index bac2eb5de63d61..d7670ba4147bff 100644 --- a/mm/page_vma_mapped.c +++ b/mm/page_vma_mapped.c @@ -107,7 +107,13 @@ static bool map_pte(struct page_vma_mapped_walk *pvmw, pmd_t *pmdvalp, static bool check_pte(struct page_vma_mapped_walk *pvmw, unsigned long pte_nr) { unsigned long pfn; - pte_t ptent = ptep_get(pvmw->pte); + pte_t ptent; + + if (is_vm_hugetlb_page(pvmw->vma)) + ptent = huge_ptep_get(pvmw->vma->vm_mm, pvmw->address, + pvmw->pte); + else + ptent = ptep_get(pvmw->pte); if (pvmw->flags & PVMW_MIGRATION) { const softleaf_t entry = softleaf_from_pte(ptent); diff --git a/mm/pagewalk.c b/mm/pagewalk.c index 5d87c632a25507..d3bfece3193366 100644 --- a/mm/pagewalk.c +++ b/mm/pagewalk.c @@ -126,6 +126,7 @@ static int walk_pmd_range(pud_t *pud, unsigned long addr, unsigned long end, pmd = pmd_offset(pud, addr); do { again: + walk->action = ACTION_SUBTREE; next = pmd_addr_end(addr, end); if (pmd_none(*pmd)) { if (has_install) @@ -138,8 +139,6 @@ static int walk_pmd_range(pud_t *pud, unsigned long addr, unsigned long end, continue; } - walk->action = ACTION_SUBTREE; - /* * This implies that each ->pmd_entry() handler * needs to know about pmd_trans_huge() pmds @@ -196,6 +195,7 @@ static int walk_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end, pud = pud_offset(p4d, addr); do { again: + walk->action = ACTION_SUBTREE; next = pud_addr_end(addr, end); if (pud_none(*pud)) { if (has_install) @@ -208,8 +208,6 @@ static int walk_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end, continue; } - walk->action = ACTION_SUBTREE; - if (ops->pud_entry) err = ops->pud_entry(pud, addr, next, walk); if (err) diff --git a/mm/rmap.c b/mm/rmap.c index 1c77d5dc06e9fa..143cf80d95f42b 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -2095,14 +2095,19 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma, /* Unexpected PMD-mapped THP? */ VM_BUG_ON_FOLIO(!pvmw.pte, folio); - /* - * Handle PFN swap PTEs, such as device-exclusive ones, that - * actually map pages. - */ - pteval = ptep_get(pvmw.pte); + address = pvmw.address; + if (folio_test_hugetlb(folio)) { + pteval = huge_ptep_get(mm, address, pvmw.pte); + } else { + pteval = ptep_get(pvmw.pte); + } if (likely(pte_present(pteval))) { pfn = pte_pfn(pteval); } else { + /* + * Handle PFN swap PTEs, such as device-exclusive ones, + * that actually map pages. + */ const softleaf_t entry = softleaf_from_pte(pteval); pfn = softleaf_to_pfn(entry); @@ -2110,7 +2115,6 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma, } subpage = folio_page(folio, pfn - folio_pfn(folio)); - address = pvmw.address; anon_exclusive = folio_test_anon(folio) && PageAnonExclusive(subpage); @@ -2501,14 +2505,18 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma, /* Unexpected PMD-mapped THP? */ VM_BUG_ON_FOLIO(!pvmw.pte, folio); - /* - * Handle PFN swap PTEs, such as device-exclusive ones, that - * actually map pages. - */ - pteval = ptep_get(pvmw.pte); + address = pvmw.address; + if (folio_test_hugetlb(folio)) + pteval = huge_ptep_get(mm, address, pvmw.pte); + else + pteval = ptep_get(pvmw.pte); if (likely(pte_present(pteval))) { pfn = pte_pfn(pteval); } else { + /* + * Handle PFN swap PTEs, such as device-exclusive ones, + * that actually map pages. + */ const softleaf_t entry = softleaf_from_pte(pteval); pfn = softleaf_to_pfn(entry); @@ -2516,7 +2524,6 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma, } subpage = folio_page(folio, pfn - folio_pfn(folio)); - address = pvmw.address; anon_exclusive = folio_test_anon(folio) && PageAnonExclusive(subpage); diff --git a/mm/secretmem.c b/mm/secretmem.c index 4877c262cb1f6f..5411324fec1ef3 100644 --- a/mm/secretmem.c +++ b/mm/secretmem.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include @@ -47,10 +49,69 @@ bool secretmem_active(void) return !!atomic_read(&secretmem_users); } +struct secretmem_inode_state { + struct user_struct *user; + atomic_long_t nr_pages_accounted; +}; + +static bool __secretmem_account_pages(struct user_struct *user, + unsigned long nr_pages) +{ + unsigned long page_limit, cur_pages, new_pages; + + if (!nr_pages) + return true; + + page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + + cur_pages = atomic_long_read(&user->locked_vm); + do { + new_pages = cur_pages + nr_pages; + if (new_pages > page_limit) + return false; + } while (!atomic_long_try_cmpxchg(&user->locked_vm, + &cur_pages, new_pages)); + return true; +} + +static bool secretmem_account_folio(struct secretmem_inode_state *state, + const struct folio *folio) +{ + const unsigned long nr_pages = folio_nr_pages(folio); + + if (!__secretmem_account_pages(state->user, nr_pages)) + return false; + + atomic_long_add(nr_pages, &state->nr_pages_accounted); + return true; +} + +static void __secretmem_unaccount_pages(struct secretmem_inode_state *state, + unsigned long nr_pages) +{ + atomic_long_sub(nr_pages, &state->user->locked_vm); + atomic_long_sub(nr_pages, &state->nr_pages_accounted); +} + +static void secretmem_unaccount_folio(struct secretmem_inode_state *state, + struct folio *folio) +{ + __secretmem_unaccount_pages(state, folio_nr_pages(folio)); +} + +static void secretmem_unaccount_all_folios(struct secretmem_inode_state *state) +{ + const unsigned long nr_pages_accounted = + atomic_long_read(&state->nr_pages_accounted); + + __secretmem_unaccount_pages(state, nr_pages_accounted); +} + static vm_fault_t secretmem_fault(struct vm_fault *vmf) { struct address_space *mapping = vmf->vma->vm_file->f_mapping; struct inode *inode = file_inode(vmf->vma->vm_file); + struct secretmem_inode_state *state = inode->i_private; pgoff_t offset = vmf->pgoff; gfp_t gfp = vmf->gfp_mask; unsigned long addr; @@ -72,8 +133,15 @@ static vm_fault_t secretmem_fault(struct vm_fault *vmf) goto out; } + if (!secretmem_account_folio(state, folio)) { + folio_put(folio); + ret = VM_FAULT_SIGBUS; + goto out; + } + err = set_direct_map_invalid_noflush(folio_page(folio, 0)); if (err) { + secretmem_unaccount_folio(state, folio); folio_put(folio); ret = vmf_error(err); goto out; @@ -82,6 +150,7 @@ static vm_fault_t secretmem_fault(struct vm_fault *vmf) __folio_mark_uptodate(folio); err = filemap_add_folio(mapping, folio, offset, gfp); if (unlikely(err)) { + secretmem_unaccount_folio(state, folio); /* * If a split of large page was required, it * already happened when we marked the page invalid @@ -112,22 +181,30 @@ static const struct vm_operations_struct secretmem_vm_ops = { .fault = secretmem_fault, }; +static void secretmem_destroy_inode_priv(struct inode *inode) +{ + struct secretmem_inode_state *state = inode->i_private; + + secretmem_unaccount_all_folios(state); + free_uid(state->user); + kfree(state); + inode->i_private = NULL; +} + static int secretmem_release(struct inode *inode, struct file *file) { atomic_dec(&secretmem_users); + secretmem_destroy_inode_priv(inode); + return 0; } static int secretmem_mmap_prepare(struct vm_area_desc *desc) { - const unsigned long len = vma_desc_size(desc); - if (!vma_desc_test_any(desc, VMA_SHARED_BIT, VMA_MAYSHARE_BIT)) return -EINVAL; - vma_desc_set_flags(desc, VMA_LOCKED_BIT, VMA_DONTDUMP_BIT); - if (!mlock_future_ok(desc->mm, /*is_vma_locked=*/ true, len)) - return -EAGAIN; + vma_desc_set_flags(desc, VMA_DONTDUMP_BIT); desc->vm_ops = &secretmem_vm_ops; return 0; @@ -187,20 +264,40 @@ static const struct inode_operations secretmem_iops = { static struct vfsmount *secretmem_mnt; +static int secretmem_init_inode_priv(struct inode *inode) +{ + struct secretmem_inode_state *state; + + state = kzalloc_obj(*state); + if (!state) + return -ENOMEM; + + state->user = get_uid(current_user()); + inode->i_private = state; + return 0; +} + static struct file *secretmem_file_create(unsigned long flags) { struct file *file; struct inode *inode; const char *anon_name = "[secretmem]"; + int err; inode = anon_inode_make_secure_inode(secretmem_mnt->mnt_sb, anon_name, NULL); if (IS_ERR(inode)) return ERR_CAST(inode); + err = secretmem_init_inode_priv(inode); + if (err) + goto err_free_inode; + file = alloc_file_pseudo(inode, secretmem_mnt, "secretmem", O_RDWR | O_LARGEFILE, &secretmem_fops); - if (IS_ERR(file)) - goto err_free_inode; + if (IS_ERR(file)) { + err = PTR_ERR(file); + goto err_free_priv; + } mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER); mapping_set_unevictable(inode->i_mapping); @@ -215,10 +312,11 @@ static struct file *secretmem_file_create(unsigned long flags) atomic_inc(&secretmem_users); return file; - +err_free_priv: + secretmem_destroy_inode_priv(inode); err_free_inode: iput(inode); - return file; + return ERR_PTR(err); } SYSCALL_DEFINE1(memfd_secret, unsigned int, flags) diff --git a/mm/slub.c b/mm/slub.c index 0337e60db5ace0..5e88c520522b0b 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -333,14 +333,20 @@ enum track_item { TRACK_ALLOC, TRACK_FREE }; #ifdef SLAB_SUPPORTS_SYSFS static int sysfs_slab_add(struct kmem_cache *); +static int __init slab_kset_init(void); +static void __init slab_sysfs_process_aliases(void); #else static inline int sysfs_slab_add(struct kmem_cache *s) { return 0; } +static inline int slab_kset_init(void) { return 0; } +static inline void slab_sysfs_process_aliases(void) { } #endif #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_SLUB_DEBUG) static void debugfs_slab_add(struct kmem_cache *); +static void __init slab_debugfs_root_init(void); #else static inline void debugfs_slab_add(struct kmem_cache *s) { } +static inline void slab_debugfs_root_init(void) { } #endif enum add_mode { @@ -5147,7 +5153,7 @@ void kmem_cache_return_sheaf(struct kmem_cache *s, gfp_t gfp, * simply flush and free it. */ if (!barn || data_race(barn->nr_full) >= MAX_FULL_SHEAVES || - refill_sheaf(s, sheaf, gfp)) { + refill_sheaf(s, sheaf, gfp | __GFP_NOMEMALLOC | __GFP_NOWARN)) { sheaf_flush_unused(s, sheaf); free_empty_sheaf(s, sheaf); return; @@ -9734,28 +9740,20 @@ int sysfs_slab_alias(struct kmem_cache *s, const char *name) return 0; } -static int __init slab_sysfs_init(void) +static int __init slab_kset_init(void) { - struct kmem_cache *s; - int err; - - mutex_lock(&slab_mutex); - slab_kset = kset_create_and_add("slab", NULL, kernel_kobj); if (!slab_kset) { - mutex_unlock(&slab_mutex); pr_err("Cannot register slab subsystem.\n"); return -ENOMEM; } - slab_state = FULL; + return 0; +} - list_for_each_entry(s, &slab_caches, list) { - err = sysfs_slab_add(s); - if (err) - pr_err("SLUB: Unable to add boot slab %s to sysfs\n", - s->name); - } +static void __init slab_sysfs_process_aliases(void) +{ + int err; while (alias_list) { struct saved_alias *al = alias_list; @@ -9767,12 +9765,41 @@ static int __init slab_sysfs_init(void) al->name); kfree(al); } +} +#endif /* SLAB_SUPPORTS_SYSFS */ + +#if defined(SLAB_SUPPORTS_SYSFS) || \ + (defined(CONFIG_SLUB_DEBUG) && defined(CONFIG_DEBUG_FS)) +static int __init slab_late_init(void) +{ + struct kmem_cache *s; + int err; + mutex_lock(&slab_mutex); + + err = slab_kset_init(); + if (err) + goto out; + + slab_debugfs_root_init(); + slab_state = FULL; + + list_for_each_entry(s, &slab_caches, list) { + if (sysfs_slab_add(s)) + pr_err("SLUB: Unable to add boot slab %s to sysfs\n", + s->name); + + if (s->flags & SLAB_STORE_USER) + debugfs_slab_add(s); + } + + slab_sysfs_process_aliases(); +out: mutex_unlock(&slab_mutex); - return 0; + return err; } -late_initcall(slab_sysfs_init); -#endif /* SLAB_SUPPORTS_SYSFS */ +late_initcall(slab_late_init); +#endif #if defined(CONFIG_SLUB_DEBUG) && defined(CONFIG_DEBUG_FS) static int slab_debugfs_show(struct seq_file *seq, void *v) @@ -9964,23 +9991,16 @@ static void debugfs_slab_add(struct kmem_cache *s) void debugfs_slab_release(struct kmem_cache *s) { + if (unlikely(!slab_debugfs_root)) + return; + debugfs_lookup_and_remove(s->name, slab_debugfs_root); } -static int __init slab_debugfs_init(void) +static void __init slab_debugfs_root_init(void) { - struct kmem_cache *s; - slab_debugfs_root = debugfs_create_dir("slab", NULL); - - list_for_each_entry(s, &slab_caches, list) - if (s->flags & SLAB_STORE_USER) - debugfs_slab_add(s); - - return 0; - } -__initcall(slab_debugfs_init); #endif /* * The /proc/slabinfo ABI diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index ebd3ac997f645b..50df73b8f74789 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c @@ -342,8 +342,8 @@ static __meminit struct page *vmemmap_get_tail(unsigned int order, struct zone * * * Any initialization done here will be overwritten by memmap_init(). * - * hugetlb_vmemmap_init() will take care of initialization after - * memmap_init(). + * hugetlb_bootmem_struct_page_init() will take care of initialization + * after memmap_init(). */ p = vmemmap_alloc_block_zero(PAGE_SIZE, node); diff --git a/mm/swapfile.c b/mm/swapfile.c index 78b49b0658adde..5bb24031e913be 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1893,11 +1893,11 @@ struct swap_info_struct *get_swap_device(swp_entry_t entry) return si; bad_nofile: - pr_err("%s: %s%08lx\n", __func__, Bad_file, entry.val); + pr_err_ratelimited("%s: %s%08lx\n", __func__, Bad_file, entry.val); out: return NULL; put_out: - pr_err("%s: %s%08lx\n", __func__, Bad_offset, entry.val); + pr_err_ratelimited("%s: %s%08lx\n", __func__, Bad_offset, entry.val); percpu_ref_put(&si->users); return NULL; } @@ -2196,7 +2196,14 @@ void swap_free_hibernation_slot(swp_entry_t entry) ci = swap_cluster_lock(si, offset); __swap_cluster_put_entry(ci, offset % SWAPFILE_CLUSTER); - __swap_cluster_free_entries(si, ci, offset % SWAPFILE_CLUSTER, 1); + /* + * A slot with a folio in the swap cache is freed when the folio + * leaves the cache, the same rule swap_put_entries_cluster() follows. + * Readahead can put a folio here, and freeing the slot now would + * leave that folio with no entry behind it. + */ + if (!swp_tb_is_folio(__swap_table_get(ci, offset % SWAPFILE_CLUSTER))) + __swap_cluster_free_entries(si, ci, offset % SWAPFILE_CLUSTER, 1); swap_cluster_unlock(ci); /* In theory readahead might add it to the swap cache by accident */ @@ -3650,6 +3657,13 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) error = -EBUSY; goto bad_swap_unlock_inode; } + if (IS_ENCRYPTED(inode)) { + pr_warn_once( + "Filesystem-level encrypted swapfile '%s' is unsupported. Create a loop device over it, or use dm-crypt\n", + name->name); + error = -EINVAL; + goto bad_swap_unlock_inode; + } /* * The swap subsystem needs a major overhaul to support this. @@ -3857,7 +3871,7 @@ int swap_dup_entry_direct(swp_entry_t entry) si = swap_entry_to_info(entry); if (WARN_ON_ONCE(!si)) { - pr_err("%s%08lx\n", Bad_file, entry.val); + pr_err_ratelimited("%s%08lx\n", Bad_file, entry.val); return -EINVAL; } diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index c3adedaaf7d547..ec5d511bd1791d 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -2085,8 +2085,10 @@ static ssize_t move_pages(struct userfaultfd_ctx *ctx, unsigned long dst_start, } if (err) { - if (err == -EAGAIN) + if (err == -EAGAIN) { + err = 0; continue; + } break; } diff --git a/mm/vmalloc.c b/mm/vmalloc.c index b15459500d7be3..ae3bb63ff3ce11 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -3361,7 +3361,7 @@ struct vm_struct *remove_vm_area(const void *addr) static inline void set_area_direct_map(const struct vm_struct *area, int (*set_direct_map)(struct page *page)) { - int i; + unsigned long i; /* HUGE_VMALLOC passes small pages to set_direct_map */ for (i = 0; i < area->nr_pages; i++) @@ -3377,7 +3377,7 @@ static void vm_reset_perms(struct vm_struct *area) unsigned long start = ULONG_MAX, end = 0; unsigned int page_order = vm_area_page_order(area); int flush_dmap = 0; - int i; + unsigned long i; /* * Find the start and end range of the direct mappings to make sure that @@ -3450,10 +3450,10 @@ void vfree_atomic(const void *addr) * Caller is responsible for unmapping (vunmap_range) and KASAN * poisoning before calling this. */ -static void vm_area_free_pages(struct vm_struct *vm, unsigned int start_idx, - unsigned int end_idx) +static void vm_area_free_pages(struct vm_struct *vm, unsigned long start_idx, + unsigned long end_idx) { - unsigned int i; + unsigned long i; if (!(vm->flags & VM_MAP_PUT_PAGES)) { for (i = start_idx; i < end_idx; i++) @@ -3665,12 +3665,12 @@ static inline gfp_t vmalloc_gfp_adjust(gfp_t flags, const bool large) return flags; } -static inline unsigned int +static inline unsigned long vm_area_alloc_pages(gfp_t gfp, int nid, - unsigned int order, unsigned int nr_pages, struct page **pages) + unsigned int order, unsigned long nr_pages, struct page **pages) { - unsigned int nr_allocated = 0; - unsigned int nr_remaining = nr_pages; + unsigned long nr_allocated = 0; + unsigned long nr_remaining = nr_pages; unsigned int max_attempt_order = MAX_PAGE_ORDER; struct page *page; int i; @@ -3718,7 +3718,7 @@ vm_area_alloc_pages(gfp_t gfp, int nid, if (!order) { while (nr_allocated < nr_pages) { unsigned int nr, nr_pages_request; - int i; + unsigned long i; /* * A maximum allowed request is hard-coded and is 100 @@ -3726,7 +3726,7 @@ vm_area_alloc_pages(gfp_t gfp, int nid, * long preemption off scenario in the bulk-allocator * so the range is [1:100]. */ - nr_pages_request = min(100U, nr_pages - nr_allocated); + nr_pages_request = min(100UL, nr_pages - nr_allocated); /* memory allocation should consider mempolicy, we can't * wrongly use nearest node when nid == NUMA_NO_NODE, @@ -3872,12 +3872,12 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, unsigned long addr = (unsigned long)area->addr; unsigned long size = get_vm_area_size(area); unsigned long array_size; - unsigned int nr_small_pages = size >> PAGE_SHIFT; + unsigned long nr_small_pages = size >> PAGE_SHIFT; unsigned int page_order; unsigned int flags; int ret; - array_size = (unsigned long)nr_small_pages * sizeof(struct page *); + array_size = nr_small_pages * sizeof(struct page *); /* __GFP_NOFAIL and "noblock" flags are mutually exclusive. */ if (!gfpflags_allow_blocking(gfp_mask)) @@ -4375,7 +4375,7 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align } if (size <= old_size) { - unsigned int new_nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; + unsigned long new_nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; /* Zero out "freed" memory, potentially for future realloc. */ if (want_init_on_free() || want_init_on_alloc(flags)) @@ -4404,7 +4404,7 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align !(vm->flags & (VM_FLUSH_RESET_PERMS | VM_USERMAP)) && gfp_has_io_fs(flags)) { unsigned long addr = (unsigned long)kasan_reset_tag(p); - unsigned int old_nr_pages = vm->nr_pages; + unsigned long old_nr_pages = vm->nr_pages; /* * Use the node lock to synchronize with concurrent @@ -4417,16 +4417,13 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align spin_unlock(&vn->busy.lock); /* Notify kmemleak of the reduced allocation size before unmapping. */ - kmemleak_free_part( - (void *)addr + ((unsigned long)new_nr_pages - << PAGE_SHIFT), - (unsigned long)(old_nr_pages - new_nr_pages) - << PAGE_SHIFT); + kmemleak_free_part((void *)addr + + (new_nr_pages << PAGE_SHIFT), + (old_nr_pages - new_nr_pages) + << PAGE_SHIFT); - vunmap_range(addr + ((unsigned long)new_nr_pages - << PAGE_SHIFT), - addr + ((unsigned long)old_nr_pages - << PAGE_SHIFT)); + vunmap_range(addr + (new_nr_pages << PAGE_SHIFT), + addr + (old_nr_pages << PAGE_SHIFT)); vm_area_free_pages(vm, new_nr_pages, old_nr_pages); } @@ -5251,7 +5248,7 @@ bool vmalloc_dump_obj(void *object) struct vmap_area *va; struct vmap_node *vn; unsigned long addr; - unsigned int nr_pages; + unsigned long nr_pages; addr = PAGE_ALIGN((unsigned long) object); vn = addr_to_node(addr); @@ -5271,7 +5268,7 @@ bool vmalloc_dump_obj(void *object) nr_pages = vm->nr_pages; spin_unlock(&vn->busy.lock); - pr_cont(" %u-page vmalloc region starting at %#lx allocated at %pS\n", + pr_cont(" %lu-page vmalloc region starting at %#lx allocated at %pS\n", nr_pages, addr, caller); return true; @@ -5289,16 +5286,17 @@ bool vmalloc_dump_obj(void *object) static void show_numa_info(struct seq_file *m, struct vm_struct *v, unsigned int *counters) { - unsigned int nr; unsigned int step = 1U << vm_area_page_order(v); + unsigned long i; + unsigned int nr; if (!counters) return; memset(counters, 0, nr_node_ids * sizeof(unsigned int)); - for (nr = 0; nr < v->nr_pages; nr += step) - counters[page_to_nid(v->pages[nr])] += step; + for (i = 0; i < v->nr_pages; i += step) + counters[page_to_nid(v->pages[i])] += step; for_each_node_state(nr, N_HIGH_MEMORY) if (counters[nr]) seq_printf(m, " N%u=%u", nr, counters[nr]); @@ -5356,7 +5354,7 @@ static int vmalloc_info_show(struct seq_file *m, void *p) seq_printf(m, " %pS", v->caller); if (v->nr_pages) - seq_printf(m, " pages=%d", v->nr_pages); + seq_printf(m, " pages=%lu", v->nr_pages); if (v->phys_addr) seq_printf(m, " phys=%pa", &v->phys_addr); diff --git a/mm/vmscan.c b/mm/vmscan.c index 56708d1d2dfd54..ee3efc6213f49d 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -198,6 +198,13 @@ struct scan_control { */ int vm_swappiness = 60; +static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg) +{ + if (sc->proactive && sc->proactive_swappiness) + return *sc->proactive_swappiness; + return mem_cgroup_swappiness(memcg); +} + #ifdef CONFIG_MEMCG /* Returns true for reclaim through cgroup limits or cgroup interfaces. */ @@ -238,13 +245,6 @@ static bool writeback_throttling_sane(struct scan_control *sc) #endif return false; } - -static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg) -{ - if (sc->proactive && sc->proactive_swappiness) - return *sc->proactive_swappiness; - return mem_cgroup_swappiness(memcg); -} #else static bool cgroup_reclaim(struct scan_control *sc) { @@ -260,11 +260,6 @@ static bool writeback_throttling_sane(struct scan_control *sc) { return true; } - -static int sc_swappiness(struct scan_control *sc, struct mem_cgroup *memcg) -{ - return READ_ONCE(vm_swappiness); -} #endif static void set_task_reclaim_state(struct task_struct *task, @@ -4573,7 +4568,6 @@ void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_control *sc, int tier_idx) { - bool success; int gen = folio_lru_gen(folio); int type = folio_is_file_lru(folio); int zone = folio_zonenum(folio); @@ -4585,15 +4579,9 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c VM_WARN_ON_ONCE_FOLIO(gen >= MAX_NR_GENS, folio); - /* unevictable */ - if (!folio_evictable(folio)) { - success = lru_gen_del_folio(lruvec, folio, true); - VM_WARN_ON_ONCE_FOLIO(!success, folio); - folio_set_unevictable(folio); - lruvec_add_folio(lruvec, folio); - __count_vm_events(UNEVICTABLE_PGCULLED, delta); - return true; - } + /* unevictable: let it through and the generic path will cull it */ + if (!folio_evictable(folio)) + return false; /* promoted */ if (gen != lru_gen_from_seq(lrugen->min_seq[type])) { @@ -4843,11 +4831,9 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec, list_for_each_entry_safe_reverse(folio, next, &list, lru) { DEFINE_MIN_SEQ(lruvec); - if (!folio_evictable(folio)) { - list_del(&folio->lru); - folio_putback_lru(folio); + /* move_folios_to_lru() culls unevictable folios via folio_putback_lru() */ + if (!folio_evictable(folio)) continue; - } /* retry folios that may have missed folio_rotate_reclaimable() */ if (!skip_retry && !folio_test_active(folio) && !folio_mapped(folio) && @@ -5927,7 +5913,7 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) } } - cond_resched(); + cond_resched_tasks_rcu_qs(); if (nr_reclaimed < nr_to_reclaim || proportional_reclaim) continue; diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index 83f5820c45f93e..41b03a9ecec25f 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c @@ -478,6 +478,11 @@ static int get_size_class_index(int size) return min_t(int, ZS_SIZE_CLASSES - 1, idx); } +static struct size_class *lookup_size_class(struct zs_pool *pool, size_t size) +{ + return pool->size_class[get_size_class_index(size + ZS_HANDLE_SIZE)]; +} + static inline void class_stat_add(struct size_class *class, int type, unsigned long cnt) { @@ -1021,7 +1026,7 @@ unsigned int zs_lookup_class_index(struct zs_pool *pool, unsigned int size) { struct size_class *class; - class = pool->size_class[get_size_class_index(size)]; + class = lookup_size_class(pool, size); return class->index; } @@ -1311,9 +1316,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t gfp, if (!handle) return (unsigned long)ERR_PTR(-ENOMEM); - /* extra space in chunk to keep the handle */ - size += ZS_HANDLE_SIZE; - class = pool->size_class[get_size_class_index(size)]; + class = lookup_size_class(pool, size); /* class->lock effectively protects the zpage migration */ spin_lock(&class->lock); diff --git a/mm/zswap.c b/mm/zswap.c index 761cd699e0a3e6..0bdb999e46b6cf 100644 --- a/mm/zswap.c +++ b/mm/zswap.c @@ -1356,11 +1356,12 @@ static void shrink_worker(struct work_struct *w) } while (memcg && !mem_cgroup_tryget_online(memcg)); spin_unlock(&zswap_shrink_lock); - if (!memcg) { - /* - * Continue shrinking without incrementing failures if - * we found candidate memcgs in the last tree walk. - */ + /* + * A NULL memcg ends a full hierarchy pass (except when memcg is + * disabled, where it is always NULL: fall through to the root LRU). + * Count a failure only if the last pass found no candidates. + */ + if (!memcg && !mem_cgroup_disabled()) { if (!attempts && ++failures == MAX_RECLAIM_RETRIES) break; @@ -1379,7 +1380,7 @@ static void shrink_worker(struct work_struct *w) * and failures. */ if (ret == -ENOENT) - continue; + goto resched; ++attempts; if (ret && ++failures == MAX_RECLAIM_RETRIES) diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index ec2569b3f8dac6..2859cbac3f266b 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -502,26 +502,6 @@ static const struct header_ops vlan_header_ops = { .parse_protocol = vlan_parse_protocol, }; -static int vlan_passthru_hard_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, - const void *daddr, const void *saddr, - unsigned int len) -{ - struct vlan_dev_priv *vlan = vlan_dev_priv(dev); - struct net_device *real_dev = vlan->real_dev; - - if (saddr == NULL) - saddr = dev->dev_addr; - - return dev_hard_header(skb, real_dev, type, daddr, saddr, len); -} - -static const struct header_ops vlan_passthru_header_ops = { - .create = vlan_passthru_hard_header, - .parse = eth_header_parse, - .parse_protocol = vlan_parse_protocol, -}; - static const struct device_type vlan_type = { .name = "vlan", }; @@ -580,14 +560,10 @@ static int vlan_dev_init(struct net_device *dev) dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid; #endif - dev->needed_headroom = real_dev->needed_headroom; - if (vlan_hw_offload_capable(real_dev->features, vlan->vlan_proto)) { - dev->header_ops = &vlan_passthru_header_ops; - dev->hard_header_len = real_dev->hard_header_len; - } else { - dev->header_ops = &vlan_header_ops; - dev->hard_header_len = real_dev->hard_header_len + VLAN_HLEN; - } + dev->needed_headroom = real_dev->needed_headroom + VLAN_HLEN; + dev->needed_tailroom = real_dev->needed_tailroom; + dev->header_ops = &vlan_header_ops; + dev->hard_header_len = real_dev->hard_header_len; dev->netdev_ops = &vlan_netdev_ops; @@ -1029,10 +1005,9 @@ static void vlan_transfer_features(struct net_device *dev, netif_inherit_tso_max(vlandev, dev); - if (vlan_hw_offload_capable(dev->features, vlan->vlan_proto)) - vlandev->hard_header_len = dev->hard_header_len; - else - vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN; + vlandev->needed_headroom = dev->needed_headroom + VLAN_HLEN; + vlandev->needed_tailroom = dev->needed_tailroom; + vlandev->hard_header_len = dev->hard_header_len; #if IS_ENABLED(CONFIG_FCOE) vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid; diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 5c73f6ba16cff3..7a31bc21bf87fd 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -324,11 +324,6 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw) } spin_unlock_bh(list_lock); } - - /* all claims gone, initialize CRC */ - spin_lock_bh(&backbone_gw->crc_lock); - backbone_gw->crc = BATADV_BLA_CRC_INIT; - spin_unlock_bh(&backbone_gw->crc_lock); } /** @@ -942,26 +937,18 @@ static bool batadv_handle_unclaim(struct batadv_priv *bat_priv, const u8 *backbone_addr, const u8 *claim_addr, unsigned short vid) { - struct batadv_bla_backbone_gw *backbone_gw; - /* unclaim in any case if it is our own */ if (primary_if && batadv_compare_eth(backbone_addr, primary_if->net_dev->dev_addr)) batadv_bla_send_claim(bat_priv, claim_addr, vid, BATADV_CLAIM_TYPE_UNCLAIM); - backbone_gw = batadv_backbone_hash_find(bat_priv, backbone_addr, vid); - - if (!backbone_gw) - return true; - /* this must be an UNCLAIM frame */ batadv_dbg(BATADV_DBG_BLA, bat_priv, "%s(): UNCLAIM %pM on vid %d (sent by %pM)...\n", __func__, - claim_addr, batadv_print_vid(vid), backbone_gw->orig); + claim_addr, batadv_print_vid(vid), backbone_addr); batadv_bla_del_claim(bat_priv, claim_addr, vid); - batadv_backbone_gw_put(backbone_gw); return true; } diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index a6fe4820f65b98..6ca946da927583 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -250,7 +250,10 @@ static u8 *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size) */ static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size) { - return *(__force __be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN); + u8 *src = batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN; + __be32 *ip = (__force __be32 *)src; + + return get_unaligned(ip); } /** @@ -275,8 +278,9 @@ static u8 *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size) static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size) { u8 *dst = batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN * 2 + 4; + __be32 *ip = (__force __be32 *)dst; - return *(__force __be32 *)dst; + return get_unaligned(ip); } /** diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 67bed3ee77e7e7..7918886e6f94bd 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -450,6 +450,10 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev, if (!skb) goto err_put; + /* Merged fragments re-enter here with reused skb metadata. */ + skb->dev = dev; + skb->skb_iif = dev->ifindex; + /* packet should hold at least type and version */ if (unlikely(!pskb_may_pull(skb, 2))) goto err_free; diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index fbfd99268de47e..91913271be3cb1 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -260,6 +260,8 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, if (batadv_compare_eth(ethhdr->h_dest, ectp_addr)) goto dropped; + batadv_skb_set_priority(skb, 0); + gw_mode = READ_ONCE(bat_priv->gw.mode); if (is_multicast_ether_addr(ethhdr->h_dest)) { /* if gw mode is off, broadcast every packet */ @@ -293,6 +295,9 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, send: if (do_bcast && !is_broadcast_ether_addr(ethhdr->h_dest)) { + /* WARNING batadv_mcast_forw_mode might add more headers + * in front of the skb. and might even reallocate the skb + */ forw_mode = batadv_mcast_forw_mode(bat_priv, skb, vid, &mcast_is_routable); switch (forw_mode) { @@ -310,8 +315,6 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, } } - batadv_skb_set_priority(skb, 0); - /* ethernet packet should be broadcasted */ if (do_bcast) { primary_if = batadv_primary_if_get_selected(bat_priv); diff --git a/net/batman-adv/multicast_forw.c b/net/batman-adv/multicast_forw.c index 1404a3b7adfb1f..c99579dc1ca98c 100644 --- a/net/batman-adv/multicast_forw.c +++ b/net/batman-adv/multicast_forw.c @@ -1100,8 +1100,11 @@ static int batadv_mcast_forw_expand_head(struct batadv_priv *bat_priv, return -EINVAL; } - if (skb_headroom(skb) < hdr_size && - pskb_expand_head(skb, hdr_size, 0, GFP_ATOMIC) < 0) + if (skb_cow(skb, hdr_size) < 0) + return -ENOMEM; + + /* batadv_mcast_forw_scrape() + batadv_mcast_forw_packet() require linearized skb */ + if (skb_linearize(skb) < 0) return -ENOMEM; return 0; diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index 1c9fb21985f6a5..c384db16bcac13 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -433,8 +433,11 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, return NET_RX_SUCCESS; tvlv_offset = (unsigned char *)tvlv_value - skb->data; + if (!skb_set_transport_header_careful(skb, + tvlv_offset + tvlv_value_len)) + return -EINVAL; + skb_set_network_header(skb, tvlv_offset); - skb_set_transport_header(skb, tvlv_offset + tvlv_value_len); return tvlv_handler->mcast_handler(bat_priv, skb); } diff --git a/net/bluetooth/eir.c b/net/bluetooth/eir.c index 1de5f9df6eec00..a55696820b227d 100644 --- a/net/bluetooth/eir.c +++ b/net/bluetooth/eir.c @@ -369,6 +369,7 @@ u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr) void *eir_get_service_data(u8 *eir, size_t eir_len, u16 uuid, size_t *len) { + const u8 *eir_end = eir + eir_len; size_t dlen; while ((eir = eir_get_data(eir, eir_len, EIR_SERVICE_DATA, &dlen))) { @@ -381,7 +382,7 @@ void *eir_get_service_data(u8 *eir, size_t eir_len, u16 uuid, size_t *len) } eir += dlen; - eir_len -= dlen; + eir_len = eir_end - eir; } return NULL; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index b1f911fd4ad6a6..7e4d737a795f9b 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1381,7 +1381,8 @@ static void hci_le_conn_failed(struct hci_conn *conn, u8 status) /* Enable advertising in case this was a failed connection * attempt as a peripheral. */ - hci_enable_advertising(hdev); + if (conn->role == HCI_ROLE_SLAVE) + hci_enable_advertising(hdev); } /* This function requires the caller holds hdev->lock */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5ba9fe8261ec8a..f346505c3f88cb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -4091,7 +4091,7 @@ static int hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb) if (READ_ONCE(hdev->req_status) == HCI_REQ_PEND && !hci_dev_test_and_set_flag(hdev, HCI_CMD_PENDING)) { kfree_skb(hdev->req_skb); - hdev->req_skb = skb_clone(hdev->sent_cmd, GFP_KERNEL); + hdev->req_skb = skb_get(hdev->sent_cmd); } return err; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2b41ad873c16e2..f502be73d8c5ed 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -5724,10 +5724,11 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status, hci_dev_lock(hdev); hci_store_wake_reason(hdev, bdaddr, bdaddr_type); - /* All controllers implicitly stop advertising in the event of a - * connection, so ensure that the state bit is cleared. + /* Advertising stops when a connection is created. On a failed + * connection it keeps running, so leave the state bit alone. */ - hci_dev_clear_flag(hdev, HCI_LE_ADV); + if (!status) + hci_dev_clear_flag(hdev, HCI_LE_ADV); /* Check for existing connection: * diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 8ccc3b22ef5bc6..f44f02ddb94e54 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -5374,6 +5374,7 @@ int hci_dev_open_sync(struct hci_dev *hdev) if (hdev->req_skb) { kfree_skb(hdev->req_skb); hdev->req_skb = NULL; + hci_dev_clear_flag(hdev, HCI_CMD_PENDING); } clear_bit(HCI_RUNNING, &hdev->flags); @@ -5558,6 +5559,7 @@ int hci_dev_close_sync(struct hci_dev *hdev) if (hdev->req_skb) { kfree_skb(hdev->req_skb); hdev->req_skb = NULL; + hci_dev_clear_flag(hdev, HCI_CMD_PENDING); } clear_bit(HCI_RUNNING, &hdev->flags); diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index aa2ce78f56a2ce..75bfd5938b2ea7 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -2277,6 +2277,14 @@ static void iso_conn_ready(struct iso_conn *conn) lock_sock(parent); + /* The listener may have been closed concurrently. */ + if (parent->sk_state != BT_LISTEN || + sock_flag(parent, SOCK_ZAPPED)) { + release_sock(parent); + sock_put(parent); + return; + } + sk = iso_sock_alloc(sock_net(parent), NULL, BTPROTO_ISO, GFP_ATOMIC, 0); if (!sk) { diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 9cdfea666a2c6a..5d150e8623d54d 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -2213,9 +2213,13 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt); + rfcomm_lock(); + s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst); - if (!s) + if (!s) { + rfcomm_unlock(); return; + } list_for_each_entry_safe(d, n, &s->dlcs, list) { if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) { @@ -2247,6 +2251,8 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) set_bit(RFCOMM_AUTH_REJECT, &d->flags); } + rfcomm_unlock(); + rfcomm_schedule(); } diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 00aa9b2879d6e2..ec0339e204bda4 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -4312,8 +4312,8 @@ void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on) if (br_vlan_is_master(vlan)) { br = vlan->br; - if (!br_vlan_is_brentry(vlan) || - (on && + if (on && + (!br_vlan_is_brentry(vlan) || br_multicast_ctx_vlan_global_disabled(&vlan->br_mcast_ctx))) return; diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 28d76c2f6b3e2f..f36ce5ae75680a 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -5802,6 +5803,31 @@ static inline void convert_extent_map(struct ceph_sparse_read *sr) } #endif +static bool sparse_extent_map_valid(struct ceph_sparse_read *sr) +{ + u64 req_end, pos; + int i; + + if (check_add_overflow(sr->sr_req_off, sr->sr_req_len, &req_end)) + return false; + + pos = sr->sr_req_off; + for (i = 0; i < sr->sr_count; i++) { + struct ceph_sparse_extent *ext = &sr->sr_extent[i]; + u64 end; + + if (ext->off < pos) + return false; + if (check_add_overflow(ext->off, ext->len, &end)) + return false; + if (end > req_end) + return false; + pos = end; + } + + return true; +} + static int osd_sparse_read(struct ceph_connection *con, struct ceph_msg_data_cursor *cursor, char **pbuf) @@ -5852,6 +5878,10 @@ static int osd_sparse_read(struct ceph_connection *con, fallthrough; case CEPH_SPARSE_READ_DATA_LEN: convert_extent_map(sr); + if (!sparse_extent_map_valid(sr)) { + pr_warn_ratelimited("invalid sparse extent map\n"); + return -EREMOTEIO; + } ret = sizeof(sr->sr_datalen); *pbuf = (char *)&sr->sr_datalen; sr->sr_state = CEPH_SPARSE_READ_DATA_PRE; diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index d6282f0bcff8aa..cf34b35c9a901d 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -517,6 +517,8 @@ static struct crush_map *crush_decode(void *pbyval, void *end) ceph_decode_need(p, end, 4*sizeof(u32), bad); b->id = ceph_decode_32(p); + if (b->id != -1 - i) + goto bad; b->type = ceph_decode_16(p); if (b->type == 0) goto bad; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ba3dbac80fb497..04776a1123342d 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1417,10 +1417,13 @@ EXPORT_SYMBOL(skb_dump); * * Report xmit error if a device callback is tracking this skb. * skb must be freed afterwards. + * + * Does nothing for a cloned skb: the zerocopy state lives in + * skb_shinfo(), which the clones share. */ void skb_tx_error(struct sk_buff *skb) { - if (skb) { + if (skb && !skb_cloned(skb)) { skb_zcopy_downgrade_managed(skb); skb_zcopy_clear(skb, true); } @@ -3907,10 +3910,9 @@ skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen) skb_len_add(to, len + plen); - if (unlikely(skb_orphan_frags(from, GFP_ATOMIC))) { - skb_tx_error(from); + if (unlikely(skb_orphan_frags(from, GFP_ATOMIC))) return -ENOMEM; - } + skb_zerocopy_clone(to, from, GFP_ATOMIC); for (i = 0; i < skb_shinfo(from)->nr_frags; i++) { diff --git a/net/core/xdp.c b/net/core/xdp.c index 0194e69da339a5..1d679e8fd64937 100644 --- a/net/core/xdp.c +++ b/net/core/xdp.c @@ -585,7 +585,7 @@ struct xdp_frame *xdp_convert_zc_to_xdp_frame(struct xdp_buff *xdp) xdp->data - xdp->data_meta; totsize = xdp->data_end - xdp->data + metasize; - if (sizeof(*xdpf) + totsize > PAGE_SIZE) + if (sizeof(*xdpf) + totsize > SKB_WITH_OVERHEAD(PAGE_SIZE)) return NULL; page = dev_alloc_page(); @@ -602,7 +602,7 @@ struct xdp_frame *xdp_convert_zc_to_xdp_frame(struct xdp_buff *xdp) xdpf->data = addr + metasize; xdpf->len = totsize - metasize; - xdpf->headroom = 0; + xdpf->headroom = metasize; xdpf->metasize = metasize; xdpf->frame_sz = PAGE_SIZE; xdpf->mem_type = MEM_TYPE_PAGE_ORDER0; diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 68697cb95a462b..d1ff0328175058 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -317,7 +317,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev) mtu = min(tdev->mtu, IP_MAX_MTU); } - dev->needed_headroom = t_hlen + hlen; + dev->needed_headroom = ip_tunnel_limit_headroom(t_hlen + hlen); mtu -= t_hlen + (dev->type == ARPHRD_ETHER ? dev->hard_header_len : 0); if (mtu < IPV4_MIN_MTU) diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 1ee520ec35dec2..1c5ad56568c25f 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -250,7 +250,7 @@ static int ipip_tunnel_rcv(struct sk_buff *skb, u8 ipproto) tun_dst = ip_tun_rx_dst(skb, flags, 0, 0); if (!tun_dst) - return 0; + goto drop; ip_tunnel_md_udp_encap(skb, &tun_dst->u.tun_info); } skb_reset_mac_header(skb); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 1d9a4ac14fcef9..e5f2b1c6150d2f 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -2213,6 +2213,9 @@ int ip_mr_input(struct sk_buff *skb) if (IPCB(skb)->flags & IPSKB_FORWARDED) goto dont_forward; + if (!local) + skb_orphan(skb); + mrt = ipmr_rt_fib_lookup(net, skb); if (IS_ERR(mrt)) { kfree_skb(skb); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index b82401a6baed71..f07231447883f3 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1358,8 +1358,8 @@ static unsigned int ipv4_default_advmss(const struct dst_entry *dst) rcu_read_lock(); net = dst_dev_net_rcu(dst); - advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size, - net->ipv4.ip_rt_min_advmss); + advmss = max_t(unsigned int, ip_dst_mtu_configured(dst) - header_size, + net->ipv4.ip_rt_min_advmss); rcu_read_unlock(); return min(advmss, IPV4_MAX_PMTU - header_size); diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index e4ec60a3349635..bb7bbc20ba3fad 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -1160,6 +1160,15 @@ void tcp_ao_connect_init(struct sock *sk) l3index = l3mdev_master_ifindex_by_index(sock_net(sk), sk->sk_bound_dev_if); + hlist_for_each_entry(key, &ao_info->head, node) { + if (tcp_ao_key_cmp(key, l3index, addr, key->prefixlen, + family, -1, -1)) { + /* pairs with tcp_inbound_ao_hash() */ + synchronize_rcu(); + break; + } + } + hlist_for_each_entry_safe(key, next, &ao_info->head, node) { if (!tcp_ao_key_cmp(key, l3index, addr, key->prefixlen, family, -1, -1)) continue; @@ -1187,12 +1196,7 @@ void tcp_ao_connect_init(struct sock *sk) ao_info->lisn = htonl(tp->write_seq); ao_info->snd_sne = 0; } else { - /* Can't happen: tcp_connect() verifies that there's - * at least one tcp-ao key that matches the remote peer. - */ - WARN_ON_ONCE(1); - rcu_assign_pointer(tp->ao_info, NULL); - kfree(ao_info); + tcp_ao_destroy_sock(sk, false); } } @@ -1825,6 +1829,9 @@ static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, if (cmd.ifindex && !(cmd.keyflags & TCP_AO_KEYF_IFINDEX)) return -EINVAL; + if (cmd.keyflags & TCP_AO_KEYF_IFINDEX) + l3index = cmd.ifindex; + ao_info = setsockopt_ao_info(sk); if (IS_ERR(ao_info)) return PTR_ERR(ao_info); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 7f413f509d7dce..497b1a0370cda3 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1736,7 +1736,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb, tcp_ca_openreq_child(newsk, dst); tcp_sync_mss(newsk, dst4_mtu(dst)); - newtp->advmss = tcp_mss_clamp(tcp_sk(sk), dst_metric_advmss(dst)); + newtp->advmss = tcp_mss_clamp(tcp_sk(sk), tcp_dst_advmss(dst)); tcp_initialize_rcv_mss(newsk); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 6ab3e3a0b43173..fb901b368ecc71 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -440,7 +440,7 @@ void tcp_openreq_init_rwin(struct request_sock *req, u32 rcv_wnd; int mss; - mss = tcp_mss_clamp(tp, dst_metric_advmss(dst)); + mss = tcp_mss_clamp(tp, tcp_dst_advmss(dst)); window_clamp = READ_ONCE(tp->window_clamp); /* Set this up on the first call only */ req->rsk_window_clamp = window_clamp ? : dst_metric(dst, RTAX_WINDOW); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index d7c1444b5e304b..7b761edf86ee91 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -143,7 +143,7 @@ static __u16 tcp_advertise_mss(struct sock *sk) int mss = tp->advmss; if (dst) { - unsigned int metric = dst_metric_advmss(dst); + unsigned int metric = tcp_dst_advmss(dst); if (metric < mss) { mss = metric; @@ -3972,7 +3972,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, } skb_dst_set(skb, dst); - mss = tcp_mss_clamp(tp, dst_metric_advmss(dst)); + mss = tcp_mss_clamp(tp, tcp_dst_advmss(dst)); memset(&opts, 0, sizeof(opts)); now = tcp_clock_ns(); @@ -4127,7 +4127,7 @@ static void tcp_connect_init(struct sock *sk) if (!tp->window_clamp) WRITE_ONCE(tp->window_clamp, dst_metric(dst, RTAX_WINDOW)); - tp->advmss = tcp_mss_clamp(tp, dst_metric_advmss(dst)); + tp->advmss = tcp_mss_clamp(tp, tcp_dst_advmss(dst)); tcp_initialize_rcv_mss(sk); diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 76f7a2de9108eb..c96f7e0d0a4815 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -232,26 +232,28 @@ static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *des * Rearrange the destination address in @iph and the addresses in @rthdr * so that they appear in the order they will at the final destination. * See Appendix A2 of RFC 2402 for details. + * + * Return: 0 on success, -EINVAL if segments_left exceeds the number of + * addresses described by hdrlen. */ -static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) +static int ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) { - int segments, segments_left; + unsigned int segments, segments_left; struct in6_addr *addrs; struct in6_addr final_addr; segments_left = rthdr->segments_left; if (segments_left == 0) - return; - rthdr->segments_left = 0; + return 0; - /* The value of rthdr->hdrlen has been verified either by the system - * call if it is locally generated, or by ipv6_rthdr_rcv() for incoming - * packets. So we can assume that it is even and that segments is - * greater than or equal to segments_left. - * - * For the same reason we can assume that this option is of type 0. + /* Raw locally generated packets can reach AH6 without the invariant + * required by the rt0-style address rearrangement below. */ segments = rthdr->hdrlen >> 1; + if (segments_left > segments) + return -EINVAL; + + rthdr->segments_left = 0; addrs = ((struct rt0_hdr *)rthdr)->addr; final_addr = addrs[segments - 1]; @@ -261,6 +263,8 @@ static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr) addrs[0] = iph->daddr; iph->daddr = final_addr; + + return 0; } static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) @@ -273,6 +277,7 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) } exthdr = { .iph = iph }; char *end = exthdr.raw + len; int nexthdr = iph->nexthdr; + int err; exthdr.iph++; @@ -292,7 +297,9 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) break; case NEXTHDR_ROUTING: - ipv6_rearrange_rthdr(iph, exthdr.rth); + err = ipv6_rearrange_rthdr(iph, exthdr.rth); + if (err) + return err; break; default: diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 9c677eb1d1a610..51941ad656a36e 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -368,23 +368,16 @@ static void seg6_update_csum(struct sk_buff *skb) (__be32 *)addr); } -static int ipv6_srh_rcv(struct sk_buff *skb) +static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev) { struct inet6_skb_parm *opt = IP6CB(skb); struct net *net = dev_net(skb->dev); struct ipv6_sr_hdr *hdr; - struct inet6_dev *idev; struct in6_addr *addr; int accept_seg6; hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb); - idev = __in6_dev_get(skb->dev); - if (!idev) { - kfree_skb(skb); - return -1; - } - accept_seg6 = min(READ_ONCE(net->ipv6.devconf_all->seg6_enabled), READ_ONCE(idev->cnf.seg6_enabled)); @@ -485,12 +478,11 @@ static int ipv6_srh_rcv(struct sk_buff *skb) return -1; } -static int ipv6_rpl_srh_rcv(struct sk_buff *skb) +static int ipv6_rpl_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev) { struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr; struct inet6_skb_parm *opt = IP6CB(skb); struct net *net = dev_net(skb->dev); - struct inet6_dev *idev; struct ipv6hdr *oldhdr; unsigned int chdr_len; unsigned char *buf; @@ -499,8 +491,6 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb) u64 n = 0; u32 r; - idev = __in6_dev_get(skb->dev); - accept_rpl_seg = min(READ_ONCE(net->ipv6.devconf_all->rpl_seg_enabled), READ_ONCE(idev->cnf.rpl_seg_enabled)); if (!accept_rpl_seg) { @@ -689,10 +679,14 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb) switch (hdr->type) { case IPV6_SRCRT_TYPE_4: /* segment routing */ - return ipv6_srh_rcv(skb); + if (!idev) + goto disabled; + return ipv6_srh_rcv(skb, idev); case IPV6_SRCRT_TYPE_3: /* rpl segment routing */ - return ipv6_rpl_srh_rcv(skb); + if (!idev) + goto disabled; + return ipv6_rpl_srh_rcv(skb, idev); default: break; } @@ -837,6 +831,10 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb) icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb_network_header(skb)); return -1; + +disabled: + kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED); + return -1; } static const struct inet6_protocol rthdr_protocol = { diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index b843116e9b703c..200d0ba1a40e71 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1137,13 +1137,11 @@ static void ip6gre_tnl_link_config_route(struct ip6_tnl *t, int set_mtu, return; if (rt->dst.dev) { - unsigned short dst_len = rt->dst.dev->hard_header_len + - t_hlen; + unsigned int headroom; - if (t->dev->header_ops) - dev->hard_header_len = dst_len; - else - dev->needed_headroom = dst_len; + headroom = rt->dst.dev->hard_header_len + t_hlen; + headroom = ip_tunnel_limit_headroom(headroom); + dev->needed_headroom = headroom; if (set_mtu) { int mtu = rt->dst.dev->mtu - t_hlen; @@ -1171,8 +1169,8 @@ static int ip6gre_calc_hlen(struct ip6_tnl *tunnel) t_hlen = tunnel->hlen + sizeof(struct ipv6hdr); - if (tunnel->dev->header_ops) - tunnel->dev->hard_header_len = LL_MAX_HEADER + t_hlen; + if (tunnel->dev->header_ops && tunnel->dev->type == ARPHRD_IP6GRE) + tunnel->dev->hard_header_len = t_hlen; else tunnel->dev->needed_headroom = LL_MAX_HEADER + t_hlen; diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 8972863c93ee52..d332ec60f9150e 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -622,6 +622,7 @@ int ip6_mc_input(struct sk_buff *skb) if (deliver) { skb2 = skb_clone(skb, GFP_ATOMIC); } else { + skb_orphan(skb); skb2 = skb; skb = NULL; } diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index ebf83f090376ce..cc96bb8b706e05 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1236,19 +1236,8 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield, */ max_headroom += LL_RESERVED_SPACE(tdev); - if (skb_headroom(skb) < max_headroom || skb_shared(skb) || - (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { - struct sk_buff *new_skb; - - new_skb = skb_realloc_headroom(skb, max_headroom); - if (!new_skb) - goto tx_err_dst_release; - - if (skb->sk) - skb_set_owner_w(new_skb, skb->sk); - consume_skb(skb); - skb = new_skb; - } + if (skb_cow_head(skb, max_headroom)) + goto tx_err_dst_release; if (t->parms.collect_md) { if (t->encap.type != TUNNEL_ENCAP_NONE) @@ -1525,8 +1514,11 @@ static void ip6_tnl_link_config(struct ip6_tnl *t) tdev = __dev_get_by_index(t->net, p->link); if (tdev) { - dev->needed_headroom = tdev->hard_header_len + - tdev->needed_headroom + t_hlen; + unsigned int headroom; + + headroom = tdev->hard_header_len + tdev->needed_headroom; + headroom += t_hlen; + dev->needed_headroom = ip_tunnel_limit_headroom(headroom); mtu = min_t(unsigned int, tdev->mtu, IP6_MAX_MTU); mtu = mtu - t_hlen; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 5968ce5ad15082..a698f322db1cac 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3261,7 +3261,7 @@ void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) static unsigned int ip6_default_advmss(const struct dst_entry *dst) { - unsigned int mtu = dst6_mtu(dst); + unsigned int mtu = ip6_dst_mtu_configured(dst); struct net *net; mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); @@ -6029,7 +6029,7 @@ static int rt6_nh_dump_exceptions(struct fib6_nh *nh, void *arg) return 0; for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { - hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { + hlist_for_each_entry_rcu(rt6_ex, &bucket->chain, hlist) { if (w->skip) { w->skip--; continue; diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index 2b41e4c0dddd1d..7b52122201858d 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -256,6 +256,22 @@ static bool decap_and_validate(struct sk_buff *skb, int proto) if (iptunnel_pull_offloads(skb)) return false; + if (proto == IPPROTO_IPIP) { + int iif = IP6CB(skb)->iif; + + memset(IPCB(skb), 0, sizeof(*IPCB(skb))); + IPCB(skb)->iif = iif; + } else if (proto == IPPROTO_IPV6) { + bool l3slave = ipv6_l3mdev_skb(IP6CB(skb)->flags); + int iif = IP6CB(skb)->iif; + + memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); + IP6CB(skb)->iif = iif; + IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); + if (l3slave) + IP6CB(skb)->flags |= IP6SKB_L3SLAVE; + } + return true; } diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index a38b24fb838424..19b7fa8d1a2a07 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1131,7 +1131,7 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev) WRITE_ONCE(dev->mtu, mtu); hlen = tdev->hard_header_len + tdev->needed_headroom; } - dev->needed_headroom = t_hlen + hlen; + dev->needed_headroom = ip_tunnel_limit_headroom(t_hlen + hlen); } static void ipip6_tunnel_update(struct ip_tunnel *t, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 9e9155b1b3aa75..df9c29eb5c1f40 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1487,7 +1487,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * tcp_ca_openreq_child(newsk, dst); tcp_sync_mss(newsk, dst6_mtu(dst)); - newtp->advmss = tcp_mss_clamp(tcp_sk(sk), dst_metric_advmss(dst)); + newtp->advmss = tcp_mss_clamp(tcp_sk(sk), tcp_dst_advmss(dst)); tcp_initialize_rcv_mss(newsk); diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index ea047bab65e7c5..4e5cc9da6e068e 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -2079,6 +2079,8 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev, sk = NULL; read_lock(&iucv_sk_list.lock); sk_for_each(sk, &iucv_sk_list.head) { + if (iucv_sk(sk)->hs_dev != dev) + continue; if (trans_hdr->flags == AF_IUCV_FLAG_SYN) { if ((!memcmp(&iucv_sk(sk)->src_name, trans_hdr->destAppName, 8)) && diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index 59457c0c14aab8..fbdd3d055dbc7c 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -248,8 +248,8 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info kfree(tunnel); goto out; } - ret = l2tp_tunnel_notify(&l2tp_nl_family, info, tunnel, - L2TP_CMD_TUNNEL_CREATE); + l2tp_tunnel_notify(&l2tp_nl_family, info, tunnel, + L2TP_CMD_TUNNEL_CREATE); l2tp_tunnel_put(tunnel); out: @@ -305,8 +305,8 @@ static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info goto out; } - ret = l2tp_tunnel_notify(&l2tp_nl_family, info, - tunnel, L2TP_CMD_TUNNEL_MODIFY); + l2tp_tunnel_notify(&l2tp_nl_family, info, + tunnel, L2TP_CMD_TUNNEL_MODIFY); l2tp_tunnel_put(tunnel); @@ -645,8 +645,8 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf session = l2tp_session_get(net, tunnel->sock, tunnel->version, tunnel_id, session_id); if (session) { - ret = l2tp_session_notify(&l2tp_nl_family, info, session, - L2TP_CMD_SESSION_CREATE); + l2tp_session_notify(&l2tp_nl_family, info, session, + L2TP_CMD_SESSION_CREATE); l2tp_session_put(session); } } @@ -710,8 +710,8 @@ static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *inf if (info->attrs[L2TP_ATTR_RECV_TIMEOUT]) session->reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]); - ret = l2tp_session_notify(&l2tp_nl_family, info, - session, L2TP_CMD_SESSION_MODIFY); + l2tp_session_notify(&l2tp_nl_family, info, + session, L2TP_CMD_SESSION_MODIFY); l2tp_session_put(session); diff --git a/net/mctp/route.c b/net/mctp/route.c index 1f3dccbb7aedba..b19c63a5691a87 100644 --- a/net/mctp/route.c +++ b/net/mctp/route.c @@ -998,14 +998,29 @@ int mctp_route_lookup(struct net *net, unsigned int dnet, mtu = mtu ?: rt->mtu; if (rt->dst_type == MCTP_ROUTE_DIRECT) { - mctp_eid_t saddr = mctp_dev_saddr(rt->dev); + mctp_eid_t saddr; + + /* rt->dev may be going away concurrently: its last + * reference is dropped in mctp_dev_put(), which frees + * mdev->addrs that mctp_dev_saddr() reads, and + * mctp_dst_from_route() takes a reference on it. Pin + * it before use, and skip a device that is already + * dead rather than resurrecting it. + */ + if (!refcount_inc_not_zero(&rt->dev->refs)) + break; + + saddr = mctp_dev_saddr(rt->dev); /* cannot do gateway-ed routes without a src */ - if (saddr == MCTP_ADDR_NULL && depth != 0) + if (saddr == MCTP_ADDR_NULL && depth != 0) { + mctp_dev_put(rt->dev); break; + } if (dst) mctp_dst_from_route(dst, daddr, saddr, mtu, rt); + mctp_dev_put(rt->dev); rc = 0; break; diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 961be5054a039b..17b78dcbf8abcd 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -221,6 +221,7 @@ static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb) if (pskb_may_pull(skb, mpls_hdr_len + sizeof(struct iphdr))) { const struct iphdr *v4hdr; + hdr = mpls_hdr(skb) + label_index; v4hdr = (const struct iphdr *)(hdr + 1); if (v4hdr->version == 4) { hash = jhash_3words(ntohl(v4hdr->saddr), @@ -231,6 +232,7 @@ static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb) sizeof(struct ipv6hdr))) { const struct ipv6hdr *v6hdr; + hdr = mpls_hdr(skb) + label_index; v6hdr = (const struct ipv6hdr *)(hdr + 1); hash = __ipv6_addr_jhash(&v6hdr->saddr, hash); hash = __ipv6_addr_jhash(&v6hdr->daddr, hash); diff --git a/net/mptcp/syncookies.c b/net/mptcp/syncookies.c index 7f22526346a7e6..b5cac570112219 100644 --- a/net/mptcp/syncookies.c +++ b/net/mptcp/syncookies.c @@ -118,6 +118,7 @@ bool mptcp_token_join_cookie_init_state(struct mptcp_subflow_request_sock *subfl subflow_req->local_nonce = e->local_nonce; subflow_req->backup = e->backup; subflow_req->remote_id = e->join_id; + subflow_req->local_id = e->local_id; subflow_req->token = e->token; subflow_req->msk = msk; spin_unlock_bh(&join_entry_locks[i]); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index af357f6c5070b1..3a7c8f7a630415 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -8715,18 +8715,17 @@ static int nf_tables_delobj(struct sk_buff *skb, const struct nfnl_info *info, return nft_delobj(&ctx, obj); } -static void -__nft_obj_notify(struct net *net, const struct nft_table *table, - struct nft_object *obj, u32 portid, u32 seq, int event, - u16 flags, int family, int report, gfp_t gfp) +static struct sk_buff * +nft_obj_notify_alloc(struct net *net, const struct nft_table *table, + struct nft_object *obj, u32 portid, u32 seq, int event, + u16 flags, int family, int report, gfp_t gfp) { - struct nftables_pernet *nft_net = nft_pernet(net); struct sk_buff *skb; int err; if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) - return; + return NULL; skb = nlmsg_new(NLMSG_GOODSIZE, gfp); if (skb == NULL) @@ -8740,10 +8739,10 @@ __nft_obj_notify(struct net *net, const struct nft_table *table, goto err; } - nft_notify_enqueue(skb, report, &nft_net->notify_list); - return; + return skb; err: nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, -ENOBUFS); + return NULL; } void nft_obj_notify(struct net *net, const struct nft_table *table, @@ -8752,6 +8751,7 @@ void nft_obj_notify(struct net *net, const struct nft_table *table, { char *buf = kasprintf(gfp, "%s:%u", table->name, nft_base_seq(net)); + struct sk_buff *skb; audit_log_nfcfg(buf, family, @@ -8762,17 +8762,27 @@ void nft_obj_notify(struct net *net, const struct nft_table *table, gfp); kfree(buf); - __nft_obj_notify(net, table, obj, portid, seq, event, - flags, family, report, gfp); + /* Called from the packet path, holding no mutex: notify_list is + * serialised by commit_mutex, so send this notification directly. + */ + skb = nft_obj_notify_alloc(net, table, obj, portid, seq, event, + flags, family, report, gfp); + if (skb) + nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, gfp); } EXPORT_SYMBOL_GPL(nft_obj_notify); static void nf_tables_obj_notify(const struct nft_ctx *ctx, struct nft_object *obj, int event) { - __nft_obj_notify(ctx->net, ctx->table, obj, ctx->portid, - ctx->seq, event, ctx->flags, ctx->family, - ctx->report, GFP_KERNEL); + struct nftables_pernet *nft_net = nft_pernet(ctx->net); + struct sk_buff *skb; + + skb = nft_obj_notify_alloc(ctx->net, ctx->table, obj, ctx->portid, + ctx->seq, event, ctx->flags, ctx->family, + ctx->report, GFP_KERNEL); + if (skb) + nft_notify_enqueue(skb, ctx->report, &nft_net->notify_list); } /* diff --git a/net/netfilter/nft_set_pipapo_avx2.c b/net/netfilter/nft_set_pipapo_avx2.c index b3f105520a85bd..21f5be68c70369 100644 --- a/net/netfilter/nft_set_pipapo_avx2.c +++ b/net/netfilter/nft_set_pipapo_avx2.c @@ -1134,6 +1134,7 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m, struct nft_pipapo_scratch *scratch; const struct nft_pipapo_field *f; unsigned long *res, *fill, *map; + struct nft_pipapo_elem *e; bool map_index; int ret = 0; int i; @@ -1207,14 +1208,11 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m, next_match: if (ret < 0) { scratch->map_index = map_index; - kernel_fpu_end(); - __local_unlock_nested_bh(&scratch->bh_lock); - return NULL; + e = NULL; + goto out; } if (last) { - struct nft_pipapo_elem *e; - e = f->mt[ret].e; if (unlikely(__nft_set_elem_expired(&e->ext, tstamp) || !nft_set_elem_active(&e->ext, genmask))) { @@ -1224,9 +1222,7 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m, } scratch->map_index = map_index; - kernel_fpu_end(); - __local_unlock_nested_bh(&scratch->bh_lock); - return e; + goto out; } map_index = !map_index; @@ -1234,9 +1230,12 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m, data += NFT_PIPAPO_GROUPS_PADDED_SIZE(f); } + e = NULL; +out: + asm volatile("vzeroupper"); kernel_fpu_end(); __local_unlock_nested_bh(&scratch->bh_lock); - return NULL; + return e; } /** diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index 95697d4e16e64e..37c90580ecb8fd 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -933,10 +933,14 @@ static int ovs_ct_check_limit(struct net *net, const struct ovs_conntrack_info *info) { struct ovs_net *ovs_net = net_generic(net, ovs_net_id); - const struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info; + const struct ovs_ct_limit_info *ct_limit_info; u32 per_zone_limit, connections; u32 conncount_key; + ct_limit_info = rcu_dereference(ovs_net->ct_limit_info); + if (!ct_limit_info) + return 0; + conncount_key = info->zone.id; per_zone_limit = ct_limit_get(ct_limit_info, info->zone.id); @@ -1585,40 +1589,55 @@ static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info) #if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) static int ovs_ct_limit_init(struct net *net, struct ovs_net *ovs_net) { + struct ovs_ct_limit_info *info; int i, err; - ovs_net->ct_limit_info = kmalloc_obj(*ovs_net->ct_limit_info); - if (!ovs_net->ct_limit_info) + info = kmalloc_obj(*info); + if (!info) return -ENOMEM; - ovs_net->ct_limit_info->default_limit = OVS_CT_LIMIT_DEFAULT; - ovs_net->ct_limit_info->limits = + info->default_limit = OVS_CT_LIMIT_DEFAULT; + info->limits = kmalloc_objs(struct hlist_head, CT_LIMIT_HASH_BUCKETS); - if (!ovs_net->ct_limit_info->limits) { - kfree(ovs_net->ct_limit_info); + if (!info->limits) { + kfree(info); return -ENOMEM; } for (i = 0; i < CT_LIMIT_HASH_BUCKETS; i++) - INIT_HLIST_HEAD(&ovs_net->ct_limit_info->limits[i]); + INIT_HLIST_HEAD(&info->limits[i]); - ovs_net->ct_limit_info->data = nf_conncount_init(net, sizeof(u32)); + info->data = nf_conncount_init(net, sizeof(u32)); - if (IS_ERR(ovs_net->ct_limit_info->data)) { - err = PTR_ERR(ovs_net->ct_limit_info->data); - kfree(ovs_net->ct_limit_info->limits); - kfree(ovs_net->ct_limit_info); + if (IS_ERR(info->data)) { + err = PTR_ERR(info->data); + kfree(info->limits); + kfree(info); pr_err("openvswitch: failed to init nf_conncount %d\n", err); return err; } + rcu_assign_pointer(ovs_net->ct_limit_info, info); return 0; } -static void ovs_ct_limit_exit(struct net *net, struct ovs_net *ovs_net) +static void *ovs_ct_limit_exit_start(struct ovs_net *ovs_net) { - const struct ovs_ct_limit_info *info = ovs_net->ct_limit_info; + return rcu_replace_pointer(ovs_net->ct_limit_info, NULL, + lockdep_ovsl_is_held()); +} + +/* The CT limit state must be detached by ovs_ct_limit_exit_start() and an + * RCU grace period must elapse before this function runs. The pernet core + * guarantees the grace period between the .pre_exit and .exit callbacks. + */ +static void ovs_ct_limit_exit_finish(struct net *net, void *data) +{ + const struct ovs_ct_limit_info *info = data; int i; + if (!info) + return; + nf_conncount_destroy(net, info->data); for (i = 0; i < CT_LIMIT_HASH_BUCKETS; ++i) { struct hlist_head *head = &info->limits[i]; @@ -1626,7 +1645,7 @@ static void ovs_ct_limit_exit(struct net *net, struct ovs_net *ovs_net) struct hlist_node *next; hlist_for_each_entry_safe(ct_limit, next, head, hlist_node) - kfree_rcu(ct_limit, rcu); + kfree(ct_limit); } kfree(info->limits); kfree(info); @@ -1665,12 +1684,13 @@ static bool check_zone_id(int zone_id, u16 *pzone) return false; } -static int ovs_ct_limit_set_zone_limit(struct nlattr *nla_zone_limit, - struct ovs_ct_limit_info *info) +static int ovs_ct_limit_set_zone_limit(struct ovs_net *ovs_net, + struct nlattr *nla_zone_limit) { struct ovs_zone_limit *zone_limit; - int rem; + struct ovs_ct_limit_info *info; u16 zone; + int rem; rem = NLA_ALIGN(nla_len(nla_zone_limit)); zone_limit = (struct ovs_zone_limit *)nla_data(nla_zone_limit); @@ -1679,6 +1699,7 @@ static int ovs_ct_limit_set_zone_limit(struct nlattr *nla_zone_limit, if (unlikely(zone_limit->zone_id == OVS_ZONE_LIMIT_DEFAULT_ZONE)) { ovs_lock(); + info = ovsl_dereference(ovs_net->ct_limit_info); info->default_limit = zone_limit->limit; ovs_unlock(); } else if (unlikely(!check_zone_id( @@ -1695,6 +1716,7 @@ static int ovs_ct_limit_set_zone_limit(struct nlattr *nla_zone_limit, ct_limit->limit = zone_limit->limit; ovs_lock(); + info = ovsl_dereference(ovs_net->ct_limit_info); ct_limit_set(info, ct_limit); ovs_unlock(); } @@ -1709,12 +1731,13 @@ static int ovs_ct_limit_set_zone_limit(struct nlattr *nla_zone_limit, return 0; } -static int ovs_ct_limit_del_zone_limit(struct nlattr *nla_zone_limit, - struct ovs_ct_limit_info *info) +static int ovs_ct_limit_del_zone_limit(struct ovs_net *ovs_net, + struct nlattr *nla_zone_limit) { struct ovs_zone_limit *zone_limit; - int rem; + struct ovs_ct_limit_info *info; u16 zone; + int rem; rem = NLA_ALIGN(nla_len(nla_zone_limit)); zone_limit = (struct ovs_zone_limit *)nla_data(nla_zone_limit); @@ -1723,6 +1746,7 @@ static int ovs_ct_limit_del_zone_limit(struct nlattr *nla_zone_limit, if (unlikely(zone_limit->zone_id == OVS_ZONE_LIMIT_DEFAULT_ZONE)) { ovs_lock(); + info = ovsl_dereference(ovs_net->ct_limit_info); info->default_limit = OVS_CT_LIMIT_DEFAULT; ovs_unlock(); } else if (unlikely(!check_zone_id( @@ -1730,6 +1754,7 @@ static int ovs_ct_limit_del_zone_limit(struct nlattr *nla_zone_limit, OVS_NLERR(true, "zone id is out of range"); } else { ovs_lock(); + info = ovsl_dereference(ovs_net->ct_limit_info); ct_limit_del(info, zone); ovs_unlock(); } @@ -1773,6 +1798,7 @@ static int __ovs_ct_limit_get_zone_limit(struct net *net, return nla_put_nohdr(reply, sizeof(zone_limit), &zone_limit); } +/* Called with RCU read lock held. */ static int ovs_ct_limit_get_zone_limit(struct net *net, struct nlattr *nla_zone_limit, struct ovs_ct_limit_info *info, @@ -1796,12 +1822,10 @@ static int ovs_ct_limit_get_zone_limit(struct net *net, &zone))) { OVS_NLERR(true, "zone id is out of range"); } else { - rcu_read_lock(); limit = ct_limit_get(info, zone); err = __ovs_ct_limit_get_zone_limit( net, info->data, zone, limit, reply); - rcu_read_unlock(); if (err) return err; } @@ -1816,6 +1840,7 @@ static int ovs_ct_limit_get_zone_limit(struct net *net, return 0; } +/* Called with RCU read lock held. */ static int ovs_ct_limit_get_all_zone_limit(struct net *net, struct ovs_ct_limit_info *info, struct sk_buff *reply) @@ -1828,19 +1853,16 @@ static int ovs_ct_limit_get_all_zone_limit(struct net *net, if (err) return err; - rcu_read_lock(); for (i = 0; i < CT_LIMIT_HASH_BUCKETS; ++i) { head = &info->limits[i]; hlist_for_each_entry_rcu(ct_limit, head, hlist_node) { err = __ovs_ct_limit_get_zone_limit(net, info->data, ct_limit->zone, ct_limit->limit, reply); if (err) - goto exit_err; + return err; } } -exit_err: - rcu_read_unlock(); return err; } @@ -1850,7 +1872,6 @@ static int ovs_ct_limit_cmd_set(struct sk_buff *skb, struct genl_info *info) struct sk_buff *reply; struct ovs_header *ovs_reply_header; struct ovs_net *ovs_net = net_generic(sock_net(skb->sk), ovs_net_id); - struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info; int err; reply = ovs_ct_limit_cmd_reply_start(info, OVS_CT_LIMIT_CMD_SET, @@ -1863,8 +1884,8 @@ static int ovs_ct_limit_cmd_set(struct sk_buff *skb, struct genl_info *info) goto exit_err; } - err = ovs_ct_limit_set_zone_limit(a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT], - ct_limit_info); + err = ovs_ct_limit_set_zone_limit(ovs_net, + a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT]); if (err) goto exit_err; @@ -1884,7 +1905,6 @@ static int ovs_ct_limit_cmd_del(struct sk_buff *skb, struct genl_info *info) struct sk_buff *reply; struct ovs_header *ovs_reply_header; struct ovs_net *ovs_net = net_generic(sock_net(skb->sk), ovs_net_id); - struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info; int err; reply = ovs_ct_limit_cmd_reply_start(info, OVS_CT_LIMIT_CMD_DEL, @@ -1897,8 +1917,8 @@ static int ovs_ct_limit_cmd_del(struct sk_buff *skb, struct genl_info *info) goto exit_err; } - err = ovs_ct_limit_del_zone_limit(a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT], - ct_limit_info); + err = ovs_ct_limit_del_zone_limit(ovs_net, + a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT]); if (err) goto exit_err; @@ -1918,7 +1938,7 @@ static int ovs_ct_limit_cmd_get(struct sk_buff *skb, struct genl_info *info) struct ovs_header *ovs_reply_header; struct net *net = sock_net(skb->sk); struct ovs_net *ovs_net = net_generic(net, ovs_net_id); - struct ovs_ct_limit_info *ct_limit_info = ovs_net->ct_limit_info; + struct ovs_ct_limit_info *ct_limit_info; int err; reply = ovs_ct_limit_cmd_reply_start(info, OVS_CT_LIMIT_CMD_GET, @@ -1932,18 +1952,19 @@ static int ovs_ct_limit_cmd_get(struct sk_buff *skb, struct genl_info *info) goto exit_err; } + rcu_read_lock(); + ct_limit_info = rcu_dereference(ovs_net->ct_limit_info); if (a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT]) { err = ovs_ct_limit_get_zone_limit( net, a[OVS_CT_LIMIT_ATTR_ZONE_LIMIT], ct_limit_info, reply); - if (err) - goto exit_err; } else { err = ovs_ct_limit_get_all_zone_limit(net, ct_limit_info, reply); - if (err) - goto exit_err; } + rcu_read_unlock(); + if (err) + goto exit_err; nla_nest_end(reply, nla_reply); genlmsg_end(reply, ovs_reply_header); @@ -2001,6 +2022,7 @@ int ovs_ct_init(struct net *net) { unsigned int n_bits = sizeof(struct ovs_key_ct_labels) * BITS_PER_BYTE; struct ovs_net *ovs_net = net_generic(net, ovs_net_id); + int err = 0; if (nf_connlabels_get(net, n_bits - 1)) { ovs_net->xt_label = false; @@ -2010,18 +2032,36 @@ int ovs_ct_init(struct net *net) } #if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) - return ovs_ct_limit_init(net, ovs_net); -#else - return 0; + err = ovs_ct_limit_init(net, ovs_net); + if (err && ovs_net->xt_label) + nf_connlabels_put(net); #endif + return err; } -void ovs_ct_exit(struct net *net) +/* Must be called with ovs_mutex held. Detaches the RCU-protected + * ct_limit_info and stores it in ovs_net->ct_limit_exit_data for + * ovs_ct_exit_finish() to complete the teardown after an RCU grace period. + */ +void ovs_ct_exit_start(struct net *net __maybe_unused) +{ +#if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) + struct ovs_net *ovs_net = net_generic(net, ovs_net_id); + + ovs_net->ct_limit_exit_data = ovs_ct_limit_exit_start(ovs_net); +#endif +} + +/* Completes the CT limit teardown. The pernet core guarantees an RCU + * grace period between detaching the state in ovs_ct_exit_start() and + * this call, so no RCU readers remain. + */ +void ovs_ct_exit_finish(struct net *net) { struct ovs_net *ovs_net = net_generic(net, ovs_net_id); #if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) - ovs_ct_limit_exit(net, ovs_net); + ovs_ct_limit_exit_finish(net, ovs_net->ct_limit_exit_data); #endif if (ovs_net->xt_label) diff --git a/net/openvswitch/conntrack.h b/net/openvswitch/conntrack.h index 317e525c8a113f..ab21d032fde3d7 100644 --- a/net/openvswitch/conntrack.h +++ b/net/openvswitch/conntrack.h @@ -14,7 +14,8 @@ enum ovs_key_attr; #if IS_ENABLED(CONFIG_NF_CONNTRACK) int ovs_ct_init(struct net *); -void ovs_ct_exit(struct net *); +void ovs_ct_exit_start(struct net *net); +void ovs_ct_exit_finish(struct net *net); bool ovs_ct_verify(struct net *, enum ovs_key_attr attr); int ovs_ct_copy_action(struct net *, const struct nlattr *, const struct sw_flow_key *, struct sw_flow_actions **, @@ -40,7 +41,8 @@ void ovs_ct_free_action(const struct nlattr *a); static inline int ovs_ct_init(struct net *net) { return 0; } -static inline void ovs_ct_exit(struct net *net) { } +static inline void ovs_ct_exit_start(struct net *net) { } +static inline void ovs_ct_exit_finish(struct net *net) { } static inline bool ovs_ct_verify(struct net *net, int attr) { diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index ae69b2cabab9e5..b601e4859de920 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -285,6 +285,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) consume_skb(skb); break; default: + skb_tx_error(skb); kfree_skb(skb); break; } @@ -601,8 +602,6 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid); user_skb = NULL; out: - if (err) - skb_tx_error(skb); consume_skb(user_skb); consume_skb(nskb); @@ -1473,33 +1472,34 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) goto unlock; } - ovs_flow_tbl_remove(&dp->table, flow); - ovs_unlock(); - - reply = ovs_flow_cmd_alloc_info((const struct sw_flow_actions __force *) flow->sf_acts, + reply = ovs_flow_cmd_alloc_info(ovsl_dereference(flow->sf_acts), &flow->id, info, false, ufid_flags); - if (likely(reply)) { - if (!IS_ERR(reply)) { - rcu_read_lock(); /*To keep RCU checker happy. */ - err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, - reply, info->snd_portid, - info->snd_seq, 0, - OVS_FLOW_CMD_DEL, - ufid_flags); - rcu_read_unlock(); - if (WARN_ON_ONCE(err < 0)) { - kfree_skb(reply); - goto out_free; - } + if (IS_ERR(reply)) { + netlink_set_err(sock_net(skb->sk)->genl_sock, 0, 0, + PTR_ERR(reply)); + reply = NULL; + } - ovs_notify(&dp_flow_genl_family, reply, info); - } else { - netlink_set_err(sock_net(skb->sk)->genl_sock, 0, 0, - PTR_ERR(reply)); + if (likely(reply)) { + err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, + reply, info->snd_portid, + info->snd_seq, 0, + OVS_FLOW_CMD_DEL, ufid_flags); + if (WARN_ON_ONCE(err < 0)) { + kfree_skb(reply); + reply = NULL; } } + /* Removal has to happen after ovs_flow_cmd_fill_info(), as it uses + * the flow->mask that can be scheduled to be freed by the + * ovs_flow_tbl_remove() and we're not holding the RCU read lock. + */ + ovs_flow_tbl_remove(&dp->table, flow); + ovs_unlock(); + + if (likely(reply)) + ovs_notify(&dp_flow_genl_family, reply, info); -out_free: ovs_flow_free(flow, true); return 0; unlock: @@ -2756,6 +2756,13 @@ static void __net_exit list_vports_from_net(struct net *net, struct net *dnet, } } +static void __net_exit ovs_pre_exit_net(struct net *dnet) +{ + ovs_lock(); + ovs_ct_exit_start(dnet); + ovs_unlock(); +} + static void __net_exit ovs_exit_net(struct net *dnet) { struct datapath *dp, *dp_next; @@ -2766,7 +2773,7 @@ static void __net_exit ovs_exit_net(struct net *dnet) ovs_lock(); - ovs_ct_exit(dnet); + ovs_ct_exit_finish(dnet); list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node) __dp_destroy(dp); @@ -2790,6 +2797,7 @@ static void __net_exit ovs_exit_net(struct net *dnet) static struct pernet_operations ovs_net_ops = { .init = ovs_init_net, + .pre_exit = ovs_pre_exit_net, .exit = ovs_exit_net, .id = &ovs_net_id, .size = sizeof(struct ovs_net), diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 696640e88fa7e3..b2c2b8da12d40b 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -164,7 +164,10 @@ struct dp_upcall_info { * Protected by genl_mutex. * @dp_notify_work: A work notifier to handle port unregistering. * @masks_rebalance: A work to periodically optimize flow table caches. - * @ct_limit_info: A hash table of conntrack zone connection limits. + * @ct_limit_info: Hash table of conntrack zone connection limits. Protected + * by RCU; updates and teardown are serialized by ovs_mutex. May be NULL during + * netns teardown. + * @ct_limit_exit_data: CT limit state detached at .pre_exit, freed at .exit. * @xt_label: Whether connlables are configured for the network or not. */ struct ovs_net { @@ -172,7 +175,8 @@ struct ovs_net { struct work_struct dp_notify_work; struct delayed_work masks_rebalance; #if IS_ENABLED(CONFIG_NETFILTER_CONNCOUNT) - struct ovs_ct_limit_info *ct_limit_info; + struct ovs_ct_limit_info __rcu *ct_limit_info; + struct ovs_ct_limit_info *ct_limit_exit_data; #endif bool xt_label; }; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 1168bd6b09cd6c..b22cda32213636 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -88,6 +88,7 @@ #include #include #include +#include #ifdef CONFIG_INET #include #endif @@ -1341,6 +1342,8 @@ static void packet_sock_destruct(struct sock *sk) WARN_ON(atomic_read(&sk->sk_rmem_alloc)); WARN_ON(refcount_read(&sk->sk_wmem_alloc)); + packet_free_pending(pkt_sk(sk)); + if (!sock_flag(sk, SOCK_DEAD)) { pr_err("Attempt to release alive packet socket: %p\n", sk); return; @@ -2534,11 +2537,11 @@ static void tpacket_destruct_skb(struct sk_buff *skb) __u32 ts; ph = skb_zcopy_get_nouarg(skb); - packet_dec_pending(&po->tx_ring); ts = __packet_set_timestamp(po, ph, skb); __packet_set_status(po, ph, TP_STATUS_AVAILABLE | ts); + packet_dec_pending(&po->tx_ring); complete(&po->skb_completion); } @@ -3207,7 +3210,6 @@ static int packet_release(struct socket *sock) /* Purge queues */ skb_queue_purge(&sk->sk_receive_queue); - packet_free_pending(po); sock_put(sk); return 0; @@ -4370,11 +4372,26 @@ static const struct vm_operations_struct packet_mmap_ops = { .close = packet_mm_close, }; +struct packet_pg_vec { + struct packet_pg_vec_free *deferred; + unsigned int order; + unsigned int len; + struct pgv pg_vec[] __counted_by(len); +}; + +struct packet_pg_vec_free { + struct delayed_work work; + struct sock *sk; + struct packet_pg_vec *vec; +}; + static void free_pg_vec(struct pgv *pg_vec, unsigned int order, unsigned int len) { + struct packet_pg_vec *vec; int i; + vec = container_of_const(pg_vec, struct packet_pg_vec, pg_vec[0]); for (i = 0; i < len; i++) { if (likely(pg_vec[i].buffer)) { if (is_vmalloc_addr(pg_vec[i].buffer)) @@ -4385,7 +4402,46 @@ static void free_pg_vec(struct pgv *pg_vec, unsigned int order, pg_vec[i].buffer = NULL; } } - kfree(pg_vec); + kfree(vec->deferred); + kfree(vec); +} + +static void packet_free_pg_vec_work(struct work_struct *work) +{ + struct packet_pg_vec_free *deferred; + struct packet_pg_vec *vec; + struct sock *sk; + + deferred = container_of_const(to_delayed_work(work), + struct packet_pg_vec_free, work); + vec = deferred->vec; + sk = deferred->sk; + if (sk_wmem_alloc_get(sk)) { + queue_delayed_work(system_long_wq, &deferred->work, 1); + return; + } + + free_pg_vec(vec->pg_vec, vec->order, vec->len); + sock_put(sk); +} + +static void packet_free_tx_ring(struct sock *sk, struct pgv *pg_vec, + unsigned int order, unsigned int len) +{ + struct packet_pg_vec_free *deferred; + struct packet_pg_vec *vec; + + vec = container_of_const(pg_vec, struct packet_pg_vec, pg_vec[0]); + deferred = vec->deferred; + if (!deferred || !sk_wmem_alloc_get(sk)) { + free_pg_vec(pg_vec, order, len); + return; + } + + /* A detached ring's pending count can miss late skb destructors. */ + deferred->sk = sk; + sock_hold(sk); + queue_delayed_work(system_long_wq, &deferred->work, 0); } static char *alloc_one_pg_vec_page(unsigned long order) @@ -4413,20 +4469,35 @@ static char *alloc_one_pg_vec_page(unsigned long order) return NULL; } -static struct pgv *alloc_pg_vec(struct tpacket_req *req, int order) +static struct pgv *alloc_pg_vec(struct tpacket_req *req, int order, bool tx_ring) { unsigned int block_nr = req->tp_block_nr; + struct packet_pg_vec *vec; struct pgv *pg_vec; int i; - pg_vec = kzalloc_objs(struct pgv, block_nr, GFP_KERNEL | __GFP_NOWARN); - if (unlikely(!pg_vec)) - goto out; + vec = kzalloc_flex(*vec, pg_vec, block_nr, GFP_KERNEL | __GFP_NOWARN); + if (unlikely(!vec)) + return NULL; + vec->order = order; + vec->len = block_nr; + pg_vec = vec->pg_vec; for (i = 0; i < block_nr; i++) { pg_vec[i].buffer = alloc_one_pg_vec_page(order); if (unlikely(!pg_vec[i].buffer)) goto out_free_pgvec; + + if (tx_ring && !vec->deferred && + is_vmalloc_addr(pg_vec[i].buffer)) { + vec->deferred = kzalloc_obj(*vec->deferred, + GFP_KERNEL | __GFP_NOWARN); + if (!vec->deferred) + goto out_free_pgvec; + vec->deferred->vec = vec; + INIT_DELAYED_WORK(&vec->deferred->work, + packet_free_pg_vec_work); + } } out: @@ -4509,7 +4580,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, err = -ENOMEM; order = get_order(req->tp_block_size); - pg_vec = alloc_pg_vec(req, order); + pg_vec = alloc_pg_vec(req, order, tx_ring); if (unlikely(!pg_vec)) goto out; switch (po->tp_version) { @@ -4561,6 +4632,9 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, err = -EBUSY; mutex_lock(&po->pg_vec_lock); if (closing || atomic_long_read(&po->mapped) == 0) { + if (tx_ring && !closing && packet_read_pending(rb)) + goto out_unlock; + err = 0; spin_lock_bh(&rb_queue->lock); swap(rb->pg_vec, pg_vec); @@ -4582,6 +4656,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, pr_err("packet_mmap: vma is busy: %ld\n", atomic_long_read(&po->mapped)); } +out_unlock: mutex_unlock(&po->pg_vec_lock); spin_lock(&po->bind_lock); @@ -4603,7 +4678,10 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, out_free_pg_vec: if (pg_vec) { bitmap_free(rx_owner_map); - free_pg_vec(pg_vec, order, req->tp_block_nr); + if (tx_ring && closing) + packet_free_tx_ring(sk, pg_vec, order, req->tp_block_nr); + else + free_pg_vec(pg_vec, order, req->tp_block_nr); } out: return err; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index ef2b4bf5156417..4539dc2c6d3805 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -1278,7 +1278,7 @@ static void transition_one_qdisc(struct net_device *dev, rcu_assign_pointer(dev_queue->qdisc, new_qdisc); if (need_watchdog_p) { - WRITE_ONCE(dev_queue->trans_start, 0); + WRITE_ONCE(dev_queue->trans_start, jiffies); *need_watchdog_p = 1; } } diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c index a024c08432471d..5f988b3a8814ff 100644 --- a/net/sctp/inqueue.c +++ b/net/sctp/inqueue.c @@ -71,8 +71,11 @@ void sctp_inq_free(struct sctp_inq *queue) */ void sctp_inq_push(struct sctp_inq *q, struct sctp_chunk *chunk) { - /* Directly call the packet handling routine. */ - if (chunk->rcvr->dead) { + /* Directly call the packet handling routine. Drop the chunk if the + * receiver or the transport it was looked up on is gone. + */ + if (chunk->rcvr->dead || + (chunk->transport && chunk->transport->dead)) { sctp_chunk_free(chunk); return; } diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 424f10a6fdba9b..94716406d602ce 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1332,6 +1332,10 @@ static int sctp_cmd_interpreter(enum sctp_event_type event_type, sctp_outq_uncork(&asoc->outqueue, gfp); local_cork = 0; } + /* No chunk left in this packet may use this asoc. */ + if (event_type == SCTP_EVENT_T_CHUNK && + chunk->asoc == asoc) + chunk->pdiscard = 1; /* Delete the current association. */ sctp_cmd_delete_tcb(commands, asoc); asoc = NULL; diff --git a/net/sctp/stream.c b/net/sctp/stream.c index 34ffe6c945a4bb..e4354d58f20b91 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -22,6 +22,15 @@ #include #include +#define SCTP_STRRESET_MASK(type) \ + BIT(ntohs(type) - ntohs(SCTP_PARAM_RESET_OUT_REQUEST)) +#define SCTP_STRRESET_TEST(asoc, type) \ + ((asoc)->strreset_outstanding & SCTP_STRRESET_MASK(type)) +#define SCTP_STRRESET_SET(asoc, type) \ + ((asoc)->strreset_outstanding |= SCTP_STRRESET_MASK(type)) +#define SCTP_STRRESET_CLEAR(asoc, type) \ + ((asoc)->strreset_outstanding &= ~SCTP_STRRESET_MASK(type)) + static void sctp_stream_shrink_out(struct sctp_stream *stream, __u16 outcnt) { struct sctp_association *asoc; @@ -372,7 +381,10 @@ int sctp_send_reset_streams(struct sctp_association *asoc, goto out; } - asoc->strreset_outstanding = out + in; + if (out) + SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_OUT_REQUEST); + if (in) + SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_IN_REQUEST); out: return retval; @@ -417,7 +429,7 @@ int sctp_send_reset_assoc(struct sctp_association *asoc) return retval; } - asoc->strreset_outstanding = 1; + SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_TSN_REQUEST); return 0; } @@ -474,7 +486,10 @@ int sctp_send_add_streams(struct sctp_association *asoc, goto out; } - asoc->strreset_outstanding = !!out + !!in; + if (out) + SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_ADD_OUT_STREAMS); + if (in) + SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_ADD_IN_STREAMS); out: return retval; @@ -482,13 +497,13 @@ int sctp_send_add_streams(struct sctp_association *asoc, static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param( struct sctp_association *asoc, __be32 resp_seq, - __be16 type) + __be16 type, bool match_seq) { struct sctp_chunk *chunk = asoc->strreset_chunk; struct sctp_reconf_chunk *hdr; union sctp_params param; - if (!chunk) + if (!chunk || !chunk->transport) return NULL; hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr; @@ -499,7 +514,7 @@ static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param( */ struct sctp_strreset_tsnreq *req = param.v; - if ((!resp_seq || req->request_seq == resp_seq) && + if ((!match_seq || req->request_seq == resp_seq) && (!type || type == req->param_hdr.type)) return param.v; } @@ -564,13 +579,14 @@ struct sctp_chunk *sctp_process_strreset_outreq( if (asoc->strreset_chunk) { if (!sctp_chunk_lookup_strreset_param( asoc, outreq->response_seq, - SCTP_PARAM_RESET_IN_REQUEST)) { + SCTP_PARAM_RESET_IN_REQUEST, true) || + !SCTP_STRRESET_TEST(asoc, SCTP_PARAM_RESET_IN_REQUEST)) { /* same process with outstanding isn't 0 */ result = SCTP_STRRESET_ERR_IN_PROGRESS; goto out; } - asoc->strreset_outstanding--; + SCTP_STRRESET_CLEAR(asoc, SCTP_PARAM_RESET_IN_REQUEST); asoc->strreset_outseq++; if (!asoc->strreset_outstanding) { @@ -669,7 +685,7 @@ struct sctp_chunk *sctp_process_strreset_inreq( SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED; asoc->strreset_chunk = chunk; - asoc->strreset_outstanding = 1; + SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_OUT_REQUEST); sctp_chunk_hold(asoc->strreset_chunk); result = SCTP_STRRESET_PERFORMED; @@ -816,13 +832,14 @@ struct sctp_chunk *sctp_process_strreset_addstrm_out( if (asoc->strreset_chunk) { if (!sctp_chunk_lookup_strreset_param( - asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) { + asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS, false) || + !SCTP_STRRESET_TEST(asoc, SCTP_PARAM_RESET_ADD_IN_STREAMS)) { /* same process with outstanding isn't 0 */ result = SCTP_STRRESET_ERR_IN_PROGRESS; goto out; } - asoc->strreset_outstanding--; + SCTP_STRRESET_CLEAR(asoc, SCTP_PARAM_RESET_ADD_IN_STREAMS); asoc->strreset_outseq++; if (!asoc->strreset_outstanding) { @@ -899,7 +916,7 @@ struct sctp_chunk *sctp_process_strreset_addstrm_in( goto out; asoc->strreset_chunk = chunk; - asoc->strreset_outstanding = 1; + SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_ADD_OUT_STREAMS); sctp_chunk_hold(asoc->strreset_chunk); stream->outcnt = outcnt; @@ -927,8 +944,9 @@ struct sctp_chunk *sctp_process_strreset_resp( struct sctp_paramhdr *req; __u32 result; - req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0); - if (!req) + req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0, + true); + if (!req || !SCTP_STRRESET_TEST(asoc, req->type)) return NULL; result = ntohl(resp->result); @@ -1078,7 +1096,7 @@ struct sctp_chunk *sctp_process_strreset_resp( nums, 0, GFP_ATOMIC); } - asoc->strreset_outstanding--; + SCTP_STRRESET_CLEAR(asoc, req->type); asoc->strreset_outseq++; /* remove everything for this reconf request */ diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 00403175b7406a..cff910cedbfccb 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -3233,7 +3233,8 @@ int smc_ioctl(struct socket *sock, unsigned int cmd, return -EINVAL; } if (smc->sk.sk_state == SMC_INIT || - smc->sk.sk_state == SMC_CLOSED) + smc->sk.sk_state == SMC_CLOSED || + !READ_ONCE(smc->conn.sndbuf_desc)) answ = 0; else answ = smc->conn.sndbuf_desc->len - diff --git a/net/smc/smc.h b/net/smc/smc.h index 52145df83f6e7b..427b6d63b9931b 100644 --- a/net/smc/smc.h +++ b/net/smc/smc.h @@ -277,9 +277,9 @@ struct smc_connection { * 0 for SMC-R, 32 for SMC-D */ u64 peer_token; /* SMC-D token of peer */ - u8 killed : 1; /* abnormal termination */ - u8 freed : 1; /* normal termination */ - u8 out_of_sync : 1; /* out of sync with peer */ + u8 killed; /* abnormal termination */ + u8 freed; /* normal termination */ + u8 out_of_sync; /* out of sync with peer */ }; struct smc_sock { /* smc sock container */ diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index b4208cb186c5e4..04aedd957543a5 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -1148,13 +1148,13 @@ struct smc_link *smc_switch_conns(struct smc_link_group *lgr, read_unlock_bh(&lgr->conns_lock); /* pre-fetch buffer outside of send_lock, might sleep */ rc = smc_cdc_get_free_slot(conn, to_lnk, &wr_buf, NULL, &pend); - if (rc) - goto err_out; - /* avoid race with smcr_tx_sndbuf_nonempty() */ - spin_lock_bh(&conn->send_lock); - smc_switch_link_and_count(conn, to_lnk); - rc = smc_switch_cursor(smc, pend, wr_buf); - spin_unlock_bh(&conn->send_lock); + if (!rc) { + /* avoid race with smcr_tx_sndbuf_nonempty() */ + spin_lock_bh(&conn->send_lock); + smc_switch_link_and_count(conn, to_lnk); + rc = smc_switch_cursor(smc, pend, wr_buf); + spin_unlock_bh(&conn->send_lock); + } sock_put(&smc->sk); if (rc) goto err_out; @@ -1209,14 +1209,16 @@ static void smcd_buf_detach(struct smc_connection *conn) { struct smcd_dev *smcd = conn->lgr->smcd; u64 peer_token = conn->peer_token; + struct smc_buf_desc *buf_desc; if (!conn->sndbuf_desc) return; smc_ism_detach_dmb(smcd, peer_token); - kfree(conn->sndbuf_desc); + buf_desc = conn->sndbuf_desc; conn->sndbuf_desc = NULL; + kfree(buf_desc); } static void smc_buf_unuse(struct smc_connection *conn, @@ -1268,11 +1270,10 @@ void smc_conn_free(struct smc_connection *conn) goto lgr_put; if (lgr->is_smcd) { - if (!list_empty(&lgr->list)) - smc_ism_unset_conn(conn); + smc_ism_unset_conn(conn); + tasklet_kill(&conn->rx_tsklet); if (smc_ism_support_dmb_nocopy(lgr->smcd)) smcd_buf_detach(conn); - tasklet_kill(&conn->rx_tsklet); } else { smc_cdc_wait_pend_tx_wr(conn); if (current_work() != &conn->abort_work) @@ -1525,12 +1526,12 @@ static void smc_conn_kill(struct smc_connection *conn, bool soft) smc_sk_wake_ups(smc); if (conn->lgr->is_smcd) { smc_ism_unset_conn(conn); - if (smc_ism_support_dmb_nocopy(conn->lgr->smcd)) - smcd_buf_detach(conn); if (soft) tasklet_kill(&conn->rx_tsklet); else tasklet_unlock_wait(&conn->rx_tsklet); + if (smc_ism_support_dmb_nocopy(conn->lgr->smcd)) + smcd_buf_detach(conn); } else { smc_cdc_wait_pend_tx_wr(conn); } diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c index aa6d83af55eda0..481b0495b0d315 100644 --- a/net/smc/smc_llc.c +++ b/net/smc/smc_llc.c @@ -157,6 +157,7 @@ struct smc_llc_msg_confirm_rkey { /* type 0x06 */ }; #define SMC_LLC_DEL_RKEY_MAX 8 +#define SMC_LLC_DEL_RKEY_V2_INLINE 9 #define SMC_LLC_FLAG_RKEY_RETRY 0x10 #define SMC_LLC_FLAG_RKEY_NEG 0x20 @@ -177,6 +178,15 @@ struct smc_llc_msg_delete_rkey_v2 { /* type 0x29 */ __be32 rkey[]; }; +/* the leading rkeys of a DELETE_RKEY_V2 fit into union smc_llc_msg */ +struct smc_llc_msg_delete_rkey_v2_inline { /* type 0x29 */ + struct smc_llc_hdr hd; + u8 num_rkeys; + u8 num_inval_rkeys; + u8 reserved[2]; + __be32 rkey[SMC_LLC_DEL_RKEY_V2_INLINE]; +}; + union smc_llc_msg { struct smc_llc_msg_confirm_link confirm_link; struct smc_llc_msg_add_link add_link; @@ -186,6 +196,7 @@ union smc_llc_msg { struct smc_llc_msg_confirm_rkey confirm_rkey; struct smc_llc_msg_delete_rkey delete_rkey; + struct smc_llc_msg_delete_rkey_v2_inline delete_rkey_v2; struct smc_llc_msg_test_link test_link; struct { @@ -194,15 +205,25 @@ union smc_llc_msg { } raw; }; +static_assert(SMC_LLC_DEL_RKEY_V2_INLINE == + (sizeof(union smc_llc_msg) - + offsetof(struct smc_llc_msg_delete_rkey_v2, rkey)) / + sizeof(__be32)); +static_assert(offsetof(struct smc_llc_msg_delete_rkey_v2_inline, rkey) == + offsetof(struct smc_llc_msg_delete_rkey_v2, rkey)); + #define SMC_LLC_FLAG_RESP 0x80 struct smc_llc_qentry { struct list_head list; struct smc_link *link; + u16 body_len; union smc_llc_msg msg; + u8 body[] __counted_by(body_len); }; -static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc); +static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc, + u32 byte_len); struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow) { @@ -998,15 +1019,20 @@ static int smc_llc_cli_conf_link(struct smc_link *link, static void smc_llc_save_add_link_rkeys(struct smc_link *link, struct smc_link *link_new, - u8 *llc_msg) + struct smc_llc_qentry *qentry) { + const u32 rt_off = offsetof(struct smc_llc_msg_add_link_v2_ext, rt); struct smc_llc_msg_add_link_v2_ext *ext; struct smc_link_group *lgr = link->lgr; int max, i; - ext = (struct smc_llc_msg_add_link_v2_ext *)(llc_msg + - SMC_WR_TX_SIZE); + /* the rkey count itself is only there if enough bytes arrived */ + if (qentry->body_len < rt_off) + return; + ext = (struct smc_llc_msg_add_link_v2_ext *)qentry->body; max = min_t(u8, ext->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2); + max = min_t(u32, max, (qentry->body_len - rt_off) / + sizeof(ext->rt[0])); down_write(&lgr->rmbs_lock); for (i = 0; i < max; i++) { smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, @@ -1099,9 +1125,7 @@ int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry) if (rc) goto out_clear_lnk; if (lgr->smc_version == SMC_V2) { - u8 *llc_msg = smc_link_shared_v2_rxbuf(link) ? - (u8 *)lgr->wr_rx_buf_v2 : (u8 *)llc; - smc_llc_save_add_link_rkeys(link, lnk_new, llc_msg); + smc_llc_save_add_link_rkeys(link, lnk_new, qentry); } else { rc = smc_llc_cli_rkey_exchange(link, lnk_new); if (rc) { @@ -1481,7 +1505,7 @@ int smc_llc_srv_add_link(struct smc_link *link, } add_llc = &qentry->msg.add_link; if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) { - smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); + smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); rc = -ENOLINK; goto out_err; } @@ -1492,7 +1516,8 @@ int smc_llc_srv_add_link(struct smc_link *link, lgr_new_t = SMC_LGR_ASYMMETRIC_PEER; } smc_llc_save_add_link_info(link_new, add_llc); - smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); + /* add_llc still points into qentry, so only detach it here */ + smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); rc = smc_ib_ready_link(link_new); if (rc) @@ -1501,9 +1526,7 @@ int smc_llc_srv_add_link(struct smc_link *link, if (rc) goto out_err; if (lgr->smc_version == SMC_V2) { - u8 *llc_msg = smc_link_shared_v2_rxbuf(link) ? - (u8 *)lgr->wr_rx_buf_v2 : (u8 *)add_llc; - smc_llc_save_add_link_rkeys(link, link_new, llc_msg); + smc_llc_save_add_link_rkeys(link, link_new, qentry); } else { rc = smc_llc_srv_rkey_exchange(link, link_new); if (rc) @@ -1512,14 +1535,14 @@ int smc_llc_srv_add_link(struct smc_link *link, rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t); if (rc) goto out_err; - kfree(ini); - return 0; + goto out; out_err: if (link_new) { link_new->state = SMC_LNK_INACTIVE; smcr_link_clear(link_new, false); } out: + kfree(qentry); kfree(ini); if (send_req_add_link_resp) smc_llc_send_req_add_link_response(req_qentry); @@ -1552,7 +1575,8 @@ void smc_llc_add_link_local(struct smc_link *link) add_llc.hd.common.llc_type = SMC_LLC_ADD_LINK; smc_llc_init_msg_hdr(&add_llc.hd, link->lgr, sizeof(add_llc)); /* no dev and port needed */ - smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc); + smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc, + sizeof(union smc_llc_msg)); } /* worker to process an add link message */ @@ -1588,7 +1612,8 @@ void smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id) del_llc.link_num = del_link_id; del_llc.reason = htonl(SMC_LLC_DEL_LOST_PATH); del_llc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; - smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc); + smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc, + sizeof(union smc_llc_msg)); } static void smc_llc_process_cli_delete_link(struct smc_link_group *lgr) @@ -1810,19 +1835,28 @@ static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr) link = qentry->link; if (lgr->smc_version == SMC_V2) { - struct smc_llc_msg_delete_rkey_v2 *llcv2; + struct smc_llc_msg_delete_rkey_v2_inline *llcv2; - if (smc_link_shared_v2_rxbuf(link)) { - memcpy(lgr->wr_rx_buf_v2, llc, sizeof(*llc)); - llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)lgr->wr_rx_buf_v2; - } else { - llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)llc; - } + /* The leading SMC_LLC_DEL_RKEY_V2_INLINE rkeys are declared in + * the message itself, any further ones were received into + * qentry->body. + */ + llcv2 = &qentry->msg.delete_rkey_v2; llcv2->num_inval_rkeys = 0; max = min_t(u8, llcv2->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2); + max = min_t(u32, max, SMC_LLC_DEL_RKEY_V2_INLINE + + qentry->body_len / sizeof(__be32)); for (i = 0; i < max; i++) { - if (smc_rtoken_delete(link, llcv2->rkey[i])) + __be32 rkey; + + if (i < SMC_LLC_DEL_RKEY_V2_INLINE) + rkey = llcv2->rkey[i]; + else + memcpy(&rkey, qentry->body + + (i - SMC_LLC_DEL_RKEY_V2_INLINE) * + sizeof(rkey), sizeof(rkey)); + if (smc_rtoken_delete(link, rkey)) llcv2->num_inval_rkeys++; } memset(&llc->rkey[0], 0, sizeof(llc->rkey)); @@ -2064,18 +2098,52 @@ static void smc_llc_rx_response(struct smc_link *link, wake_up(&link->lgr->llc_msg_waiter); } -static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc) +/* the longest tail either reader of qentry->body can use */ +static u32 smc_llc_max_body_len(union smc_llc_msg *llc) +{ + switch (llc->raw.hdr.common.llc_type) { + case SMC_LLC_ADD_LINK: + return offsetof(struct smc_llc_msg_add_link_v2_ext, rt) + + SMC_LLC_RKEYS_PER_MSG_V2 * + sizeof(struct smc_llc_msg_add_link_cont_rt); + case SMC_LLC_DELETE_RKEY: + return (SMC_LLC_RKEYS_PER_MSG_V2 - + SMC_LLC_DEL_RKEY_V2_INLINE) * sizeof(__be32); + default: + return 0; + } +} + +static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc, + u32 byte_len) { struct smc_link_group *lgr = link->lgr; struct smc_llc_qentry *qentry; unsigned long flags; + u16 body_len = 0; + + /* V2 messages can be longer than the inline union smc_llc_msg. Carry + * the remainder in the qentry itself, so that its lifetime and its + * length match the message the peer actually sent. + */ + if (lgr->smc_version == SMC_V2 && byte_len > SMC_WR_TX_SIZE) + body_len = min_t(u32, byte_len, SMC_WR_BUF_V2_SIZE) - + SMC_WR_TX_SIZE; + body_len = min_t(u32, body_len, smc_llc_max_body_len(llc)); - qentry = kmalloc_obj(*qentry, GFP_ATOMIC); + qentry = kmalloc_flex(*qentry, body, body_len, GFP_ATOMIC); if (!qentry) return; + qentry->body_len = body_len; qentry->link = link; INIT_LIST_HEAD(&qentry->list); memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg)); + if (body_len) { + u8 *src = smc_link_shared_v2_rxbuf(link) ? + (u8 *)lgr->wr_rx_buf_v2 : (u8 *)llc; + + memcpy(qentry->body, src + SMC_WR_TX_SIZE, body_len); + } /* process responses immediately */ if ((llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) && @@ -2107,7 +2175,7 @@ static void smc_llc_rx_handler(struct ib_wc *wc, void *buf) return; /* invalid message */ } - smc_llc_enqueue(link, llc); + smc_llc_enqueue(link, llc, wc->byte_len); } /***************************** worker, utils *********************************/ diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c index 5c9e4d8b57de4d..197fddc6271f05 100644 --- a/net/smc/smc_rx.c +++ b/net/smc/smc_rx.c @@ -115,16 +115,15 @@ static void smc_rx_pipe_buf_release(struct pipe_inode_info *pipe, struct pipe_buffer *buf) { struct smc_spd_priv *priv = (struct smc_spd_priv *)buf->private; + struct smc_connection *conn = &priv->smc->conn; struct smc_sock *smc = priv->smc; - struct smc_connection *conn; struct sock *sk = &smc->sk; - if (sk->sk_state == SMC_CLOSED || - sk->sk_state == SMC_PEERFINCLOSEWAIT || - sk->sk_state == SMC_APPFINCLOSEWAIT) - goto out; - conn = &smc->conn; lock_sock(sk); + if (conn->freed) { + release_sock(sk); + goto out; + } smc_rx_update_cons(smc, priv->len); release_sock(sk); if (atomic_sub_and_test(priv->len, &conn->splice_pending)) diff --git a/net/smc/smc_tx.h b/net/smc/smc_tx.h index a59f370b8b4327..610a945aefd699 100644 --- a/net/smc/smc_tx.h +++ b/net/smc/smc_tx.h @@ -20,11 +20,15 @@ static inline int smc_tx_prepared_sends(struct smc_connection *conn) { + struct smc_buf_desc *sndbuf_desc = READ_ONCE(conn->sndbuf_desc); union smc_host_cursor sent, prep; + if (!sndbuf_desc) + return 0; + smc_curs_copy(&sent, &conn->tx_curs_sent, conn); smc_curs_copy(&prep, &conn->tx_curs_prep, conn); - return smc_curs_diff(conn->sndbuf_desc->len, &sent, &prep); + return smc_curs_diff(sndbuf_desc->len, &sent, &prep); } void smc_tx_pending(struct smc_connection *conn); diff --git a/net/smc/smc_wr.c b/net/smc/smc_wr.c index 59c92b46945c52..97ba46893b17fb 100644 --- a/net/smc/smc_wr.c +++ b/net/smc/smc_wr.c @@ -602,9 +602,9 @@ static void smc_wr_init_sge(struct smc_link *lnk) /* With SMC-Rv2 there can be messages larger than SMC_WR_TX_SIZE. * Each ib_recv_wr gets 2 sges, the second one is a spillover buffer - * and the same buffer for all sges. When a larger message arrived then - * the content of the first small sge is copied to the beginning of - * the larger spillover buffer, allowing easy data mapping. + * and the same buffer for all sges. The spillover sge starts at + * SMC_WR_TX_SIZE, so the leading bytes of that buffer are never + * written. */ for (i = 0; i < lnk->wr_rx_cnt; i++) { int x = i * lnk->wr_rx_sge_cnt; diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 9d3fb6848f4001..8ddc65e894dab8 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -2072,7 +2072,11 @@ gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred, goto unwrap_failed; opaque_len = be32_to_cpup(p++); offset = (u8 *)(p) - (u8 *)head->iov_base; - if (offset + opaque_len > rcv_buf->len) + if (offset > rcv_buf->len) + goto unwrap_failed; + if (opaque_len > rcv_buf->len - offset) + goto unwrap_failed; + if (opaque_len <= GSS_KRB5_TOK_HDR_LEN) goto unwrap_failed; maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c index b5fb70419faaa3..4d12d49434c285 100644 --- a/net/sunrpc/auth_gss/gss_krb5_unseal.c +++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c @@ -89,6 +89,9 @@ gss_krb5_verify_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *message_buffer, dprintk("RPC: %s\n", __func__); + if (read_token->len < GSS_KRB5_TOK_HDR_LEN + cksum_len) + return GSS_S_DEFECTIVE_TOKEN; + memcpy(&be16_ptr, (char *) ptr, 2); if (be16_to_cpu(be16_ptr) != KG2_TOK_MIC) return GSS_S_DEFECTIVE_TOKEN; diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c index ac4b32df42b965..d3f61c4b5a1375 100644 --- a/net/sunrpc/auth_gss/gss_krb5_wrap.c +++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c @@ -73,6 +73,8 @@ static void _rotate_left(struct xdr_buf *buf, unsigned int shift) int shifted = 0; int this_shift; + if (!buf->len) + return; shift %= buf->len; while (shifted < shift) { this_shift = min(shift - shifted, LOCAL_BUF_LEN); @@ -85,6 +87,8 @@ static void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift) { struct xdr_buf subbuf; + if (buf->len <= base) + return; xdr_buf_subsegment(buf, &subbuf, base, buf->len - base); _rotate_left(&subbuf, shift); } @@ -154,6 +158,9 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len, dprintk("RPC: %s\n", __func__); + if (len - offset <= GSS_KRB5_TOK_HDR_LEN) + return GSS_S_DEFECTIVE_TOKEN; + ptr = buf->head[0].iov_base + offset; if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_WRAP) @@ -220,14 +227,16 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len, * head buffer space rather than that actually occupied. */ movelen = min_t(unsigned int, buf->head[0].iov_len, len); + if (movelen < offset + GSS_KRB5_TOK_HDR_LEN + headskip) + return GSS_S_DEFECTIVE_TOKEN; movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip; - BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen > - buf->head[0].iov_len); memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen); buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip; buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip); /* Trim off the trailing "extra count" and checksum blob */ + if (ec + GSS_KRB5_TOK_HDR_LEN + tailskip > buf->len - offset) + return GSS_S_DEFECTIVE_TOKEN; xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip); *align = XDR_QUADLEN(GSS_KRB5_TOK_HDR_LEN + headskip); diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c index 0fa4778620d9a6..b7f70b1adb18a6 100644 --- a/net/sunrpc/auth_gss/gss_rpc_upcall.c +++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c @@ -121,12 +121,6 @@ static int gssp_rpc_create(struct net *net, struct rpc_clnt **_clnt) return result; } -void init_gssp_clnt(struct sunrpc_net *sn) -{ - mutex_init(&sn->gssp_lock); - sn->gssp_clnt = NULL; -} - int set_gssp_clnt(struct net *net) { struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.h b/net/sunrpc/auth_gss/gss_rpc_upcall.h index 31e96344167e32..b3c2b2b9079855 100644 --- a/net/sunrpc/auth_gss/gss_rpc_upcall.h +++ b/net/sunrpc/auth_gss/gss_rpc_upcall.h @@ -29,7 +29,6 @@ int gssp_accept_sec_context_upcall(struct net *net, struct gssp_upcall_data *data); void gssp_free_upcall_data(struct gssp_upcall_data *data); -void init_gssp_clnt(struct sunrpc_net *); int set_gssp_clnt(struct net *); void clear_gssp_clnt(struct sunrpc_net *); diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c index fceee648d545b2..0549edae1ebee4 100644 --- a/net/sunrpc/auth_gss/gss_rpc_xdr.c +++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c @@ -222,7 +222,8 @@ static int gssx_dec_linux_creds(struct xdr_stream *xdr, return 0; out_free_groups: - groups_free(creds->cr_group_info); + put_group_info(creds->cr_group_info); + creds->cr_group_info = NULL; return err; } @@ -230,6 +231,7 @@ static int gssx_dec_option_array(struct xdr_stream *xdr, struct gssx_option_array *oa) { struct svc_cred *creds; + bool creds_decoded = false; u32 count, i; __be32 *p; int err; @@ -242,12 +244,12 @@ static int gssx_dec_option_array(struct xdr_stream *xdr, return 0; /* we recognize only 1 currently: CREDS_VALUE */ - oa->count = 1; - oa->data = kmalloc_obj(struct gssx_option); if (!oa->data) return -ENOMEM; + oa->count = 1; + creds = kzalloc_obj(struct svc_cred); if (!creds) { err = -ENOMEM; @@ -280,9 +282,14 @@ static int gssx_dec_option_array(struct xdr_stream *xdr, if (length == sizeof(CREDS_VALUE) && memcmp(p, CREDS_VALUE, sizeof(CREDS_VALUE)) == 0) { /* We have creds here. parse them */ + if (creds_decoded) { + err = -EINVAL; + goto free_creds; + } err = gssx_dec_linux_creds(xdr, creds); if (err) goto free_creds; + creds_decoded = true; oa->data[0].value.len = 1; /* presence */ } else { /* consume uninteresting buffer */ @@ -294,8 +301,10 @@ static int gssx_dec_option_array(struct xdr_stream *xdr, return 0; free_creds: + free_svc_cred(creds); kfree(creds); free_oa: + oa->count = 0; kfree(oa->data); oa->data = NULL; return err; diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index d14209031e1807..967e9d53080d79 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -949,6 +949,8 @@ svcauth_gss_unwrap_priv(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx) } if (len > xdr_stream_remaining(xdr)) goto unwrap_failed; + if (len <= GSS_KRB5_TOK_HDR_LEN) + goto unwrap_failed; offset = xdr_stream_pos(xdr); saved_len = buf->len; @@ -1466,7 +1468,6 @@ static int create_use_gss_proxy_proc_entry(struct net *net) &use_gss_proxy_proc_ops, net); if (!*p) return -ENOMEM; - init_gssp_clnt(sn); return 0; } @@ -1573,6 +1574,9 @@ svcauth_gss_decode_credbody(struct xdr_stream *xdr, u32 body_len; __be32 *p; + /* Early-return paths leave deterministic state, not stale residue. */ + memset(gc, 0, sizeof(*gc)); + p = xdr_inline_decode(xdr, XDR_UNIT); if (!p) return false; @@ -1942,6 +1946,8 @@ svcauth_gss_release(struct svc_rqst *rqstp) if (!gsd) goto out; + if (rqstp->rq_auth_stat != rpc_auth_ok) + goto out; gc = &gsd->clcred; if (gc->gc_proc != RPC_GSS_PROC_DATA) goto out; diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index 0ffa4d01a93891..1482b06e0f38b9 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -25,20 +25,39 @@ unsigned int xprt_bc_max_slots(struct rpc_xprt *xprt) } /* - * Helper function to nullify backchannel server pointer in transport. - * We need to synchronize setting the pointer to NULL (done so after - * the backchannel server is shutdown) with the usage of that pointer - * by the backchannel request processing routines - * xprt_complete_bc_request() and rpcrdma_bc_receive_call(). + * Close the backchannel producer side, drain any requests still + * queued on sv_cb_list, then destroy the callback service. */ void xprt_svc_destroy_nullify_bc(struct rpc_xprt *xprt, struct svc_serv **serv) { - spin_lock(&xprt->bc_pa_lock); + struct svc_serv *bc_serv = *serv; + struct rpc_rqst *req; + + xprt_svc_shutdown_bc(xprt); + while ((req = lwq_dequeue(&bc_serv->sv_cb_list, struct rpc_rqst, + rq_bc_list)) != NULL) { + atomic_dec(&req->rq_xprt->bc_slot_count); + xprt_free_bc_request(req); + } svc_destroy(serv); +} +EXPORT_SYMBOL_GPL(xprt_svc_destroy_nullify_bc); + +/* + * Clear the backchannel server pointer in the transport. The NULL + * store is serialized under bc_pa_lock against readers of + * xprt->bc_serv in xprt_complete_bc_request() and + * rpcrdma_bc_receive_call(). Clearing it before the callback service + * is stopped prevents a producer from enqueueing onto a service that + * is being torn down. + */ +void xprt_svc_shutdown_bc(struct rpc_xprt *xprt) +{ + spin_lock(&xprt->bc_pa_lock); xprt->bc_serv = NULL; spin_unlock(&xprt->bc_pa_lock); } -EXPORT_SYMBOL_GPL(xprt_svc_destroy_nullify_bc); +EXPORT_SYMBOL_GPL(xprt_svc_shutdown_bc); /* * Helper routines that track the number of preallocation elements @@ -393,7 +412,12 @@ void xprt_enqueue_bc_request(struct rpc_rqst *req) if (bc_serv) { lwq_enqueue(&req->rq_bc_list, &bc_serv->sv_cb_list); svc_pool_wake_idle_thread(&bc_serv->sv_pools[0]); + spin_unlock(&xprt->bc_pa_lock); + return; } spin_unlock(&xprt->bc_pa_lock); + + atomic_dec(&xprt->bc_slot_count); + xprt_free_bc_request(req); } EXPORT_SYMBOL_GPL(xprt_enqueue_bc_request); diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 391037f1529287..1bc04109d213d7 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -430,10 +430,9 @@ void sunrpc_destroy_cache_detail(struct cache_detail *cd) list_del_init(&cd->others); spin_unlock(&cd->hash_lock); spin_unlock(&cache_list_lock); - if (list_empty(&cache_list)) { - /* module must be being unloaded so its safe to kill the worker */ - cancel_delayed_work_sync(&cache_cleaner); - } + cancel_delayed_work_sync(&cache_cleaner); + if (!list_empty(&cache_list)) + queue_delayed_work(system_power_efficient_wq, &cache_cleaner, 0); } EXPORT_SYMBOL_GPL(sunrpc_destroy_cache_detail); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index efa26899bc7dcd..6cedc824cf8268 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -96,7 +96,10 @@ static void rpc_unregister_client(struct rpc_clnt *clnt) static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) { - rpc_remove_client_dir(clnt); + if (clnt->pipefs_sb) { + rpc_remove_client_dir(clnt); + clnt->pipefs_sb = NULL; + } } static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) @@ -177,19 +180,28 @@ static int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event) } static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event, - struct super_block *sb) + struct super_block *sb) { + int err = 0; + switch (event) { case RPC_PIPEFS_MOUNT: - return rpc_setup_pipedir_sb(sb, clnt); + clnt->pipefs_sb = sb; + err = rpc_setup_pipedir_sb(sb, clnt); + if (err) + clnt->pipefs_sb = NULL; + break; case RPC_PIPEFS_UMOUNT: - __rpc_clnt_remove_pipedir(clnt); + if (clnt->pipefs_sb == sb) { + __rpc_clnt_remove_pipedir(clnt); + clnt->pipefs_sb = NULL; + } break; default: printk(KERN_ERR "%s: unknown event: %ld\n", __func__, event); return -ENOTSUPP; } - return 0; + return err; } static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event, diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index ab88ce46afb556..1a3884a0376a40 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -57,6 +57,7 @@ static __net_init int sunrpc_init_net(struct net *net) INIT_LIST_HEAD(&sn->all_clients); spin_lock_init(&sn->rpc_client_lock); spin_lock_init(&sn->rpcb_clnt_lock); + mutex_init(&sn->gssp_lock); return 0; err_pipefs: diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index ae9ec4bf34f729..f85914ccb01a43 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -352,7 +352,7 @@ static int svc_pool_map_get_node(unsigned int pidx) if (m->mode == SVC_POOL_PERNODE) return m->pool_to[pidx]; } - return numa_mem_id(); + return NUMA_NO_NODE; } /* * Set the given thread's cpus_allowed mask so that it @@ -402,6 +402,7 @@ struct svc_pool *svc_pool_for_cpu(struct svc_serv *serv) struct svc_pool_map *m = &svc_pool_map; int cpu = raw_smp_processor_id(); unsigned int pidx = 0; + unsigned int i; if (serv->sv_nrpools <= 1) return serv->sv_pools; @@ -414,8 +415,34 @@ struct svc_pool *svc_pool_for_cpu(struct svc_serv *serv) pidx = m->to_pool[cpu_to_node(cpu)]; break; } + pidx %= serv->sv_nrpools; + + /* + * It's possible to have a pool with no threads. Userland can just set + * things up this way directly. Also, when threads are autodistributed + * they are spread evenly across the pools, but when there are fewer + * threads than pools some pools can end up with none. + * + * A transport enqueued on a threadless pool would never be picked up, + * since each thread only services its own pool. Fall back to the next + * populated pool, trading NUMA locality for a guarantee that the + * transport is serviced. + */ + for (i = 0; i < serv->sv_nrpools; i++) { + struct svc_pool *pool = &serv->sv_pools[pidx]; + + /* This is set under the service mutex and rarely ever + * changes. A data race here is harmless. + */ + if (data_race(pool->sp_nrthreads)) + return pool; + + if (++pidx >= serv->sv_nrpools) + pidx = 0; + } - return &serv->sv_pools[pidx % serv->sv_nrpools]; + /* No pool has any threads; nothing can service the transport. */ + return &serv->sv_pools[pidx]; } static int svc_rpcb_setup(struct svc_serv *serv, struct net *net) @@ -476,6 +503,35 @@ __svc_init_bc(struct svc_serv *serv) } #endif +static int svc_pool_init_counters(struct svc_pool *pool) +{ + int err; + + err = percpu_counter_init(&pool->sp_messages_arrived, 0, GFP_KERNEL); + if (err) + return err; + err = percpu_counter_init(&pool->sp_sockets_queued, 0, GFP_KERNEL); + if (err) + goto err_sockets; + err = percpu_counter_init(&pool->sp_threads_woken, 0, GFP_KERNEL); + if (err) + goto err_threads; + return 0; + +err_threads: + percpu_counter_destroy(&pool->sp_sockets_queued); +err_sockets: + percpu_counter_destroy(&pool->sp_messages_arrived); + return err; +} + +static void svc_pool_destroy_counters(struct svc_pool *pool) +{ + percpu_counter_destroy(&pool->sp_messages_arrived); + percpu_counter_destroy(&pool->sp_sockets_queued); + percpu_counter_destroy(&pool->sp_threads_woken); +} + /* * Create an RPC service */ @@ -540,12 +596,18 @@ __svc_create(struct svc_program *prog, int nprogs, struct svc_stat *stats, INIT_LIST_HEAD(&pool->sp_all_threads); init_llist_head(&pool->sp_idle_threads); - percpu_counter_init(&pool->sp_messages_arrived, 0, GFP_KERNEL); - percpu_counter_init(&pool->sp_sockets_queued, 0, GFP_KERNEL); - percpu_counter_init(&pool->sp_threads_woken, 0, GFP_KERNEL); + if (svc_pool_init_counters(pool)) + goto out_err; } return serv; + +out_err: + while (i--) + svc_pool_destroy_counters(&serv->sv_pools[i]); + kfree(serv->sv_pools); + kfree(serv); + return NULL; } /** @@ -624,9 +686,7 @@ svc_destroy(struct svc_serv **servp) for (i = 0; i < serv->sv_nrpools; i++) { struct svc_pool *pool = &serv->sv_pools[i]; - percpu_counter_destroy(&pool->sp_messages_arrived); - percpu_counter_destroy(&pool->sp_sockets_queued); - percpu_counter_destroy(&pool->sp_threads_woken); + svc_pool_destroy_counters(pool); } kfree(serv->sv_pools); kfree(serv); @@ -683,6 +743,15 @@ svc_release_buffer(struct svc_rqst *rqstp) } } +static void svc_rqst_free_rcu(struct rcu_head *head) +{ + struct svc_rqst *rqstp = container_of(head, struct svc_rqst, rq_rcu_head); + + kfree(rqstp->rq_resp); + kfree(rqstp->rq_argp); + kfree(rqstp); +} + static void svc_rqst_free(struct svc_rqst *rqstp) { @@ -691,10 +760,8 @@ svc_rqst_free(struct svc_rqst *rqstp) svc_release_buffer(rqstp); if (rqstp->rq_scratch_folio) folio_put(rqstp->rq_scratch_folio); - kfree(rqstp->rq_resp); - kfree(rqstp->rq_argp); kfree(rqstp->rq_auth_data); - kfree_rcu(rqstp, rq_rcu_head); + call_rcu(&rqstp->rq_rcu_head, svc_rqst_free_rcu); } static struct svc_rqst * @@ -711,7 +778,9 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node) rqstp->rq_server = serv; rqstp->rq_pool = pool; - rqstp->rq_scratch_folio = __folio_alloc_node(GFP_KERNEL, 0, node); + rqstp->rq_scratch_folio = __folio_alloc_node(GFP_KERNEL, 0, + node == NUMA_NO_NODE ? + numa_mem_id() : node); if (!rqstp->rq_scratch_folio) goto out_enomem; diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index fa6a30b5f0463f..cb2ef428651f6e 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -2049,7 +2049,7 @@ void xdr_buf_trim(struct xdr_buf *buf, unsigned int len) trim -= cur; } fix_len: - buf->len -= (len - trim); + buf->len -= min_t(unsigned int, buf->len, len - trim); } EXPORT_SYMBOL_GPL(xdr_buf_trim); diff --git a/net/sunrpc/xprtrdma/ib_client.c b/net/sunrpc/xprtrdma/ib_client.c index de49ad02053d84..188f7a13397f23 100644 --- a/net/sunrpc/xprtrdma/ib_client.c +++ b/net/sunrpc/xprtrdma/ib_client.c @@ -51,7 +51,11 @@ static struct rpcrdma_device *rpcrdma_get_client_data(struct ib_device *device) * to be invoked when the device is removed, unless this notification * is unregistered first. * - * On failure, a negative errno is returned. + * On failure, a negative errno is returned. rn->rn_done is left + * NULL on every failure path (it is armed before xa_alloc but + * cleared again if xa_alloc fails), so the @rn may safely be + * passed to rpcrdma_rn_unregister() without a separate + * registered/unregistered flag in the caller. */ int rpcrdma_rn_register(struct ib_device *device, struct rpcrdma_notification *rn, @@ -62,10 +66,21 @@ int rpcrdma_rn_register(struct ib_device *device, if (!rd || test_bit(RPCRDMA_RD_F_REMOVING, &rd->rd_flags)) return -ENETUNREACH; - if (xa_alloc(&rd->rd_xa, &rn->rn_index, rn, xa_limit_32b, GFP_KERNEL) < 0) + /* + * Arm rn_done before xa_alloc() publishes @rn: once @rn is + * visible in rd_xa, a concurrent rpcrdma_remove_one() can + * call rn->rn_done(), so the pointer must already be set. + * + * Restore NULL if xa_alloc() fails. rn_done doubles as the + * registration sentinel for rpcrdma_rn_unregister(); a stale + * value would unregister an @rn that was never inserted. + */ + rn->rn_done = done; + if (xa_alloc(&rd->rd_xa, &rn->rn_index, rn, xa_limit_32b, GFP_KERNEL) < 0) { + rn->rn_done = NULL; return -ENOMEM; + } kref_get(&rd->rd_kref); - rn->rn_done = done; trace_rpcrdma_client_register(device, rn); return 0; } @@ -83,6 +98,10 @@ static void rpcrdma_rn_release(struct kref *kref) * rpcrdma_rn_unregister - stop device removal notifications * @device: monitored device * @rn: notification object that no longer wishes to be notified + * + * It is safe to call this on an @rn whose registration never + * completed or failed; rn_done == NULL is treated as + * never-registered and the call is a no-op. */ void rpcrdma_rn_unregister(struct ib_device *device, struct rpcrdma_notification *rn) @@ -92,6 +111,21 @@ void rpcrdma_rn_unregister(struct ib_device *device, if (!rd) return; + /* + * rn_done is the registration sentinel: rpcrdma_rn_register + * leaves it NULL on every failure path, clearing it again if + * xa_alloc fails, so a non-NULL rn_done marks a completed + * registration. A NULL rn_done means this notification was + * never registered (or its registration failed) or has + * already been unregistered, and the call is a no-op. + * Without this guard, rn_index == 0 from a kzalloc'd + * parent would erase another caller's slot 0 and underflow + * rd_kref. + */ + if (!rn->rn_done) + return; + rn->rn_done = NULL; + trace_rpcrdma_client_unregister(device, rn); xa_erase(&rd->rd_xa, rn->rn_index); kref_put(&rd->rd_kref, rpcrdma_rn_release); diff --git a/net/sunrpc/xprtrdma/svc_rdma_pcl.c b/net/sunrpc/xprtrdma/svc_rdma_pcl.c index 1f8f7dad8b6f8b..18d1045799cef4 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_pcl.c +++ b/net/sunrpc/xprtrdma/svc_rdma_pcl.c @@ -213,7 +213,6 @@ bool pcl_alloc_write(struct svc_rdma_recv_ctxt *rctxt, chunk = pcl_alloc_chunk(segcount, 0); if (!chunk) return false; - list_add_tail(&chunk->ch_list, &pcl->cl_chunks); for (j = 0; j < segcount; j++) { segment = &chunk->ch_segments[j]; @@ -225,6 +224,7 @@ bool pcl_alloc_write(struct svc_rdma_recv_ctxt *rctxt, chunk->ch_length += segment->rs_length; chunk->ch_segcount++; } + list_add_tail(&chunk->ch_list, &pcl->cl_chunks); } return true; } diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index fe9bf0371b6ec0..8e71ceec11df96 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -440,11 +440,14 @@ static void svc_rdma_build_arg_xdr(struct svc_rqst *rqstp, * to the first byte past the Read list. rc_read_pcl and * rc_call_pcl cl_count fields are set to the number of * Read segments in the list. - * %false: Read list is corrupt. @rctxt's xdr_stream is left in an - * unknown state. + * %false: Read list is corrupt or exceeds the page budget. @rctxt's + * xdr_stream is left in an unknown state. */ static bool xdr_count_read_segments(struct svc_rdma_recv_ctxt *rctxt, __be32 *p) { + unsigned int maxlen = rctxt->rc_maxpages << PAGE_SHIFT; + unsigned int total_len = 0; + rctxt->rc_call_pcl.cl_count = 0; rctxt->rc_read_pcl.cl_count = 0; while (xdr_item_is_present(p)) { @@ -458,6 +461,11 @@ static bool xdr_count_read_segments(struct svc_rdma_recv_ctxt *rctxt, __be32 *p) xdr_decode_read_segment(p, &position, &handle, &length, &offset); + if (length > maxlen) + return false; + total_len += length; + if (PAGE_ALIGN(total_len) > maxlen) + return false; if (position) { if (position & 3) return false; @@ -508,10 +516,13 @@ static bool xdr_check_write_chunk(struct svc_rdma_recv_ctxt *rctxt) return false; /* Before trusting the segcount value enough to use it in - * a computation, perform a simple range check. This is an - * arbitrary but sensible limit (ie, not architectural). + * a computation, perform a simple range check. A zero + * segcount describes no remote buffer and is rejected so + * downstream consumers never see a degenerate ch_segcount==0 + * chunk. The upper bound is an arbitrary but sensible limit + * (ie, not architectural). */ - if (unlikely(segcount > rctxt->rc_maxpages)) + if (segcount == 0 || unlikely(segcount > rctxt->rc_maxpages)) return false; p = xdr_inline_decode(&rctxt->rc_stream, diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index f7fd22cc4a5982..9aaaade99e6e45 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -795,7 +795,7 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp, len -= seg_len; if (len && ((head->rc_curpage + 1) > rqstp->rq_maxpages)) - goto out_overrun; + goto out_put; } ret = svc_rdma_rw_ctx_init(rdma, ctxt, segment->rs_offset, @@ -809,7 +809,8 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp, cc->cc_sqecount += ret; return 0; -out_overrun: +out_put: + svc_rdma_put_rw_ctxt(rdma, ctxt); trace_svcrdma_page_overrun_err(&cc->cc_cid, head->rc_curpage); return -EINVAL; } @@ -847,7 +848,7 @@ static int svc_rdma_build_read_chunk(struct svc_rqst *rqstp, * svc_rdma_copy_inline_range - Copy part of the inline content into pages * @rqstp: RPC transaction context * @head: context for ongoing I/O - * @offset: offset into the Receive buffer of region to copy + * @offset: offset into the inline content of region to copy * @remaining: length of region to copy * * Take a page at a time from rqstp->rq_pages and copy the inline @@ -864,9 +865,13 @@ static int svc_rdma_copy_inline_range(struct svc_rqst *rqstp, unsigned int offset, unsigned int remaining) { - unsigned char *dst, *src = head->rc_recv_buf; + unsigned char *dst, *src = head->rc_saved_arg.head[0].iov_base; + unsigned int inline_len = head->rc_saved_arg.head[0].iov_len; unsigned int page_no, numpages; + if (offset > inline_len || remaining > inline_len - offset) + return -EINVAL; + numpages = PAGE_ALIGN(head->rc_pageoff + remaining) >> PAGE_SHIFT; for (page_no = 0; page_no < numpages; page_no++) { unsigned int page_len; @@ -917,9 +922,10 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp, { const struct svc_rdma_pcl *pcl = &head->rc_read_pcl; struct svc_rdma_chunk *chunk, *next; - unsigned int start, length; + unsigned int inline_len, start, length; int ret; + inline_len = head->rc_saved_arg.head[0].iov_len; start = 0; chunk = pcl_first_chunk(pcl); length = chunk->ch_position; @@ -937,6 +943,8 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp, break; start += length; + if (head->rc_readbytes > next->ch_position) + return -EINVAL; length = next->ch_position - head->rc_readbytes; ret = svc_rdma_copy_inline_range(rqstp, head, start, length); if (ret < 0) @@ -944,7 +952,9 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp, } start += length; - length = head->rc_byte_len - start; + if (start > inline_len) + return -EINVAL; + length = inline_len - start; return svc_rdma_copy_inline_range(rqstp, head, start, length); } @@ -969,8 +979,12 @@ svc_rdma_read_multiple_chunks(struct svc_rqst *rqstp, static int svc_rdma_read_data_item(struct svc_rqst *rqstp, struct svc_rdma_recv_ctxt *head) { - return svc_rdma_build_read_chunk(rqstp, head, - pcl_first_chunk(&head->rc_read_pcl)); + struct svc_rdma_chunk *chunk = pcl_first_chunk(&head->rc_read_pcl); + + if (chunk->ch_position > head->rc_saved_arg.head[0].iov_len) + return -EINVAL; + + return svc_rdma_build_read_chunk(rqstp, head, chunk); } /** @@ -996,17 +1010,20 @@ static int svc_rdma_read_chunk_range(struct svc_rqst *rqstp, const struct svc_rdma_segment *segment; int ret; + if (!length) + return 0; + ret = -EINVAL; pcl_for_each_segment(segment, chunk) { struct svc_rdma_segment dummy; - if (offset > segment->rs_length) { + if (offset >= segment->rs_length) { offset -= segment->rs_length; continue; } dummy.rs_handle = segment->rs_handle; - dummy.rs_length = min_t(u32, length, segment->rs_length) - offset; + dummy.rs_length = min_t(u32, length, segment->rs_length - offset); dummy.rs_offset = segment->rs_offset + offset; ret = svc_rdma_build_read_segment(rqstp, head, &dummy); @@ -1015,6 +1032,8 @@ static int svc_rdma_read_chunk_range(struct svc_rqst *rqstp, head->rc_readbytes += dummy.rs_length; length -= dummy.rs_length; + if (!length) + break; offset = 0; } return ret; @@ -1039,14 +1058,17 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp, pcl_first_chunk(&head->rc_call_pcl); const struct svc_rdma_pcl *pcl = &head->rc_read_pcl; struct svc_rdma_chunk *chunk, *next; - unsigned int start, length; + unsigned int call_len, start, length; int ret; if (pcl_is_empty(pcl)) return svc_rdma_build_read_chunk(rqstp, head, call_chunk); + call_len = call_chunk->ch_length; start = 0; chunk = pcl_first_chunk(pcl); + if (chunk->ch_position > call_len) + return -EINVAL; length = chunk->ch_position; ret = svc_rdma_read_chunk_range(rqstp, head, call_chunk, start, length); @@ -1063,6 +1085,10 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp, break; start += length; + if (next->ch_position > call_len) + return -EINVAL; + if (head->rc_readbytes > next->ch_position) + return -EINVAL; length = next->ch_position - head->rc_readbytes; ret = svc_rdma_read_chunk_range(rqstp, head, call_chunk, start, length); @@ -1071,7 +1097,9 @@ static int svc_rdma_read_call_chunk(struct svc_rqst *rqstp, } start += length; - length = call_chunk->ch_length - start; + if (start > call_len) + return -EINVAL; + length = call_len - start; return svc_rdma_read_chunk_range(rqstp, head, call_chunk, start, length); } diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index 7f6d17bf8c1fdf..c09659b17351b6 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -825,20 +825,21 @@ static int svc_rdma_xb_count_sges(const struct xdr_buf *xdr, } /** - * svc_rdma_pull_up_needed - Determine whether to use pull-up + * svc_rdma_check_pull_up - Determine whether to use pull-up * @rdma: controlling transport * @sctxt: send_ctxt for the Send WR * @write_pcl: Write chunk list provided by client * @xdr: xdr_buf containing RPC message to transmit * * Returns: - * %true if pull-up must be used - * %false otherwise + * %1 if pull-up must be used + * %0 if pull-up is not needed + * %-E2BIG if the reply is too large to be pulled up */ -static bool svc_rdma_pull_up_needed(const struct svcxprt_rdma *rdma, - const struct svc_rdma_send_ctxt *sctxt, - const struct svc_rdma_pcl *write_pcl, - const struct xdr_buf *xdr) +static int svc_rdma_check_pull_up(const struct svcxprt_rdma *rdma, + const struct svc_rdma_send_ctxt *sctxt, + const struct svc_rdma_pcl *write_pcl, + const struct xdr_buf *xdr) { /* Resources needed for the transport header */ struct svc_rdma_pullup_data args = { @@ -850,11 +851,22 @@ static bool svc_rdma_pull_up_needed(const struct svcxprt_rdma *rdma, ret = pcl_process_nonpayloads(write_pcl, xdr, svc_rdma_xb_count_sges, &args); if (ret < 0) - return false; + return 0; if (args.pd_length < RPCRDMA_PULLUP_THRESH) - return true; - return args.pd_num_sges >= rdma->sc_max_send_sges; + return 1; + if (args.pd_num_sges < rdma->sc_max_send_sges) + return 0; + + /* + * The reply has too many SGEs to Send inline, so it has to be + * linearized into sc_xprt_buf. That buffer holds only + * sc_max_req_size bytes, so a larger reply cannot be pulled up. + * RFC 8166 Section 4.5.3 requires responding with ERR_CHUNK. + */ + if (args.pd_length > rdma->sc_max_req_size) + return -E2BIG; + return 1; } /** @@ -910,7 +922,7 @@ static int svc_rdma_xb_linearize(const struct xdr_buf *xdr, * Assemble the elements of @xdr into the transport header buffer. * * Assumptions: - * pull_up_needed has determined that @xdr will fit in the buffer. + * check_pull_up has determined that @xdr will fit in the buffer. * * Returns: * %0 if pull-up was successful @@ -945,6 +957,7 @@ static int svc_rdma_pull_up_reply_msg(const struct svcxprt_rdma *rdma, * * Returns: * %0 if DMA mapping was successful. + * %-E2BIG if the reply is too large to be pulled up * %-EMSGSIZE if a buffer manipulation problem occurred * %-EIO if DMA mapping failed * @@ -960,6 +973,7 @@ int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, .md_rdma = rdma, .md_ctxt = sctxt, }; + int ret; /* Set up the (persistently-mapped) transport header SGE. */ sctxt->sc_send_wr.num_sge = 1; @@ -974,7 +988,10 @@ int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, /* For pull-up, svc_rdma_send() will sync the transport header. * No additional DMA mapping is necessary. */ - if (svc_rdma_pull_up_needed(rdma, sctxt, write_pcl, xdr)) + ret = svc_rdma_check_pull_up(rdma, sctxt, write_pcl, xdr); + if (ret < 0) + return ret; + if (ret) return svc_rdma_pull_up_reply_msg(rdma, sctxt, write_pcl, xdr); return pcl_process_nonpayloads(write_pcl, xdr, @@ -1162,7 +1179,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) &rctxt->rc_reply_pcl, sctxt, &rqstp->rq_res); if (ret < 0) - goto reply_chunk; + goto send_err; rc_size = ret; } @@ -1183,10 +1200,10 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) ret = svc_rdma_send_reply_msg(rdma, sctxt, rctxt, rqstp); if (ret < 0) - goto put_ctxt; + goto send_err; return 0; -reply_chunk: +send_err: if (ret != -E2BIG && ret != -EINVAL) goto put_ctxt; diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 7ca71741106b18..093371f9d24591 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -43,6 +43,7 @@ */ #include +#include #include #include #include @@ -245,12 +246,16 @@ svc_rdma_parse_connect_private(struct svcxprt_rdma *newxprt, * structure for the listening endpoint. * * This function creates a new xprt for the new connection and enqueues it on - * the accept queue for the listent xprt. When the listen thread is kicked, it + * the accept queue for the listen xprt. When the listen thread is kicked, it * will call the recvfrom method on the listen xprt which will accept the new * connection. + * + * Return values: + * %0: Do not destroy @new_cma_id + * %1: Destroy @new_cma_id (allocation failure) */ -static void handle_connect_req(struct rdma_cm_id *new_cma_id, - struct rdma_conn_param *param) +static int handle_connect_req(struct rdma_cm_id *new_cma_id, + struct rdma_conn_param *param) { struct svcxprt_rdma *listen_xprt = new_cma_id->context; struct svcxprt_rdma *newxprt; @@ -260,7 +265,7 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id, listen_xprt->sc_xprt.xpt_net, ibdev_to_node(new_cma_id->device)); if (!newxprt) - return; + return 1; newxprt->sc_cm_id = new_cma_id; new_cma_id->context = newxprt; svc_rdma_parse_connect_private(newxprt, param); @@ -294,6 +299,7 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id, set_bit(XPT_CONN, &listen_xprt->sc_xprt.xpt_flags); svc_xprt_enqueue(&listen_xprt->sc_xprt); + return 0; } /** @@ -317,14 +323,14 @@ static int svc_rdma_listen_handler(struct rdma_cm_id *cma_id, switch (event->event) { case RDMA_CM_EVENT_CONNECT_REQUEST: - handle_connect_req(cma_id, &event->param.conn); - break; + return handle_connect_req(cma_id, &event->param.conn); case RDMA_CM_EVENT_ADDR_CHANGE: listen_id = svc_rdma_create_listen_id(cma_rdma->xpt_net, sap, cma_xprt); if (IS_ERR(listen_id)) { pr_err("Listener dead, address change failed for device %s\n", cma_id->device->name); + cma_xprt->sc_cm_id = NULL; } else cma_xprt->sc_cm_id = listen_id; return 1; @@ -387,7 +393,13 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv, listen_id = svc_rdma_create_listen_id(net, sa, cma_xprt); if (IS_ERR(listen_id)) { - kfree(cma_xprt); + /* _svc_xprt_create() acquired one module reference and + * puts it on xpo_create failure. svc_xprt_free() puts + * a second one when the kref drops to zero. Take a + * compensating reference so both puts are balanced. + */ + __module_get(cma_xprt->sc_xprt.xpt_class->xcl_owner); + svc_xprt_put(&cma_xprt->sc_xprt); return ERR_CAST(listen_id); } cma_xprt->sc_cm_id = listen_id; @@ -598,13 +610,26 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) return &newxprt->sc_xprt; errout: - /* Take a reference in case the DTO handler runs */ - svc_xprt_get(&newxprt->sc_xprt); - if (newxprt->sc_qp && !IS_ERR(newxprt->sc_qp)) - ib_destroy_qp(newxprt->sc_qp); - rdma_destroy_id(newxprt->sc_cm_id); - rpcrdma_rn_unregister(dev, &newxprt->sc_rn); - /* This call to put will destroy the transport */ + /* + * Drop the kref_init birth reference. svc_xprt_free will + * dispatch xpo_free = svc_rdma_free, which tears down sc_qp, + * sc_sq_cq, sc_rq_cq, and sc_pd under existing IS_ERR/NULL + * guards, and sc_rn under the rn_done sentinel guard inside + * rpcrdma_rn_unregister. + * + * sc_cm_id is destroyed unconditionally by svc_rdma_free; that + * is safe here because sc_cm_id is non-NULL by caller invariant + * on every path that reaches this errout: handle_connect_req + * installs newxprt->sc_cm_id before queueing the new xprt for + * accept, and svc_rdma_accept has already dereferenced it above + * the first goto errout. + * + * svc_handle_xprt() drops its pre-acquired module reference when + * ->xpo_accept() returns NULL. Take a replacement reference before + * freeing @newxprt, because svc_xprt_free() drops the module + * reference associated with @newxprt. + */ + __module_get(newxprt->sc_xprt.xpt_class->xcl_owner); svc_xprt_put(&newxprt->sc_xprt); return NULL; } @@ -614,7 +639,8 @@ static void svc_rdma_detach(struct svc_xprt *xprt) struct svcxprt_rdma *rdma = container_of(xprt, struct svcxprt_rdma, sc_xprt); - rdma_disconnect(rdma->sc_cm_id); + if (rdma->sc_cm_id) + rdma_disconnect(rdma->sc_cm_id); /* * Most close paths go through svc_rdma_xprt_deferred_close(), @@ -634,10 +660,15 @@ static void svc_rdma_free(struct svc_xprt *xprt) { struct svcxprt_rdma *rdma = container_of(xprt, struct svcxprt_rdma, sc_xprt); - struct ib_device *device = rdma->sc_cm_id->device; + struct ib_device *device; might_sleep(); + if (!rdma->sc_cm_id) + goto out_free; + + device = rdma->sc_cm_id->device; + /* This blocks until the Completion Queues are empty */ if (rdma->sc_qp && !IS_ERR(rdma->sc_qp)) ib_drain_qp(rdma->sc_qp); @@ -662,11 +693,13 @@ static void svc_rdma_free(struct svc_xprt *xprt) if (rdma->sc_pd && !IS_ERR(rdma->sc_pd)) ib_dealloc_pd(rdma->sc_pd); + if (!test_bit(XPT_LISTENER, &rdma->sc_xprt.xpt_flags)) + rpcrdma_rn_unregister(device, &rdma->sc_rn); + /* Destroy the CM ID */ rdma_destroy_id(rdma->sc_cm_id); - if (!test_bit(XPT_LISTENER, &rdma->sc_xprt.xpt_flags)) - rpcrdma_rn_unregister(device, &rdma->sc_rn); +out_free: kfree(rdma); } diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 359407aae03e6d..5f7955e4c4045f 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2648,7 +2648,17 @@ static int xs_tls_handshake_sync(struct rpc_xprt *lower_xprt, struct xprtsec_par rc = wait_for_completion_interruptible_timeout(&lower_transport->handshake_done, XS_TLS_HANDSHAKE_TO); if (rc <= 0) { - tls_handshake_cancel(sk); + if (!tls_handshake_cancel(sk)) { + /* + * Cancellation lost to handshake_complete(): the + * callback still owns its xprt reference and is in + * flight. Wait for it to finish before returning. + */ + wait_for_completion(&lower_transport->handshake_done); + if (rc == 0) + rc = -ETIMEDOUT; + goto out; + } if (rc == 0) rc = -ETIMEDOUT; goto out_put_xprt; diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index 37bb06a8e8f5b5..f11d0528fc4315 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -531,7 +531,8 @@ static int tls_push_data(struct sock *sk, if (!size) { last_record: tls_push_record_flags = flags; - if (flags & MSG_MORE) { + if ((flags & MSG_MORE) && + record->num_frags < MAX_SKB_FRAGS - 1) { more = true; break; } diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c index 96c9fe8d357c6c..4f9aa9c4c3aa59 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -872,10 +872,10 @@ static void virtio_vsock_remove(struct virtio_device *vdev) /* Other works can be queued before 'config->del_vqs()', so we flush * all works before to free the vsock object to avoid use after free. */ - flush_work(&vsock->rx_work); flush_work(&vsock->tx_work); flush_work(&vsock->event_work); flush_work(&vsock->send_pkt_work); + flush_work(&vsock->rx_work); mutex_unlock(&the_virtio_vsock_mutex); diff --git a/net/xfrm/espintcp.c b/net/xfrm/espintcp.c index 374e1b96443839..674aedc5af5a2c 100644 --- a/net/xfrm/espintcp.c +++ b/net/xfrm/espintcp.c @@ -37,6 +37,11 @@ static void handle_esp(struct sk_buff *skb, struct sock *sk) rcu_read_lock(); skb->dev = dev_get_by_index_rcu(sock_net(sk), skb->skb_iif); + if (!skb->dev) { + XFRM_INC_STATS(sock_net(sk), LINUX_MIB_XFRMINERROR); + kfree_skb(skb); + goto out; + } local_bh_disable(); #if IS_ENABLED(CONFIG_IPV6) if (sk->sk_family == AF_INET6) @@ -45,6 +50,7 @@ static void handle_esp(struct sk_buff *skb, struct sock *sk) #endif xfrm4_rcv_encap(skb, IPPROTO_ESP, 0, TCP_ENCAP_ESPINTCP); local_bh_enable(); +out: rcu_read_unlock(); } @@ -515,7 +521,8 @@ static void espintcp_close(struct sock *sk, long timeout) strp_stop(&ctx->strp); sk->sk_prot = &tcp_prot; - barrier(); + + synchronize_rcu(); disable_work_sync(&ctx->work); strp_done(&ctx->strp); diff --git a/net/xfrm/xfrm_nat_keepalive.c b/net/xfrm/xfrm_nat_keepalive.c index eb1b6f67739e1d..5cd6d43164dbcd 100644 --- a/net/xfrm/xfrm_nat_keepalive.c +++ b/net/xfrm/xfrm_nat_keepalive.c @@ -155,25 +155,50 @@ static void nat_keepalive_send(struct nat_keepalive *ka) } } +enum { + NAT_KEEPALIVE_BATCH_SIZE = 16, + NAT_KEEPALIVE_BATCH_FULL = 1, +}; + struct nat_keepalive_work_ctx { + struct xfrm_state *batch[NAT_KEEPALIVE_BATCH_SIZE]; + unsigned int nr; time64_t next_run; time64_t now; }; -static int nat_keepalive_work_single(struct xfrm_state *x, int count, void *ptr) +static int nat_keepalive_work_collect(struct xfrm_state *x, int count, void *ptr) { struct nat_keepalive_work_ctx *ctx = ptr; + + if (!READ_ONCE(x->nat_keepalive_interval)) + return 0; + + if (ctx->nr == ARRAY_SIZE(ctx->batch)) + return NAT_KEEPALIVE_BATCH_FULL; + + xfrm_state_hold(x); + ctx->batch[ctx->nr++] = x; + return 0; +} + +static void nat_keepalive_work_single(struct xfrm_state *x, + struct nat_keepalive_work_ctx *ctx) +{ bool send_keepalive = false; struct nat_keepalive ka; - time64_t next_run; + time64_t next_run = 0; u32 interval; int delta; + spin_lock_bh(&x->lock); + + if (x->km.state == XFRM_STATE_DEAD) + goto out; + interval = x->nat_keepalive_interval; if (!interval) - return 0; - - spin_lock(&x->lock); + goto out; delta = (int)(ctx->now - x->lastused); if (delta < interval) { @@ -187,14 +212,14 @@ static int nat_keepalive_work_single(struct xfrm_state *x, int count, void *ptr) send_keepalive = true; } - spin_unlock(&x->lock); +out: + spin_unlock_bh(&x->lock); if (send_keepalive) nat_keepalive_send(&ka); - if (!ctx->next_run || next_run < ctx->next_run) + if (next_run && (!ctx->next_run || next_run < ctx->next_run)) ctx->next_run = next_run; - return 0; } static void nat_keepalive_work(struct work_struct *work) @@ -202,13 +227,23 @@ static void nat_keepalive_work(struct work_struct *work) struct nat_keepalive_work_ctx ctx; struct xfrm_state_walk walk; struct net *net; + int err, i; ctx.next_run = 0; ctx.now = ktime_get_real_seconds(); net = container_of(work, struct net, xfrm.nat_keepalive_work.work); xfrm_state_walk_init(&walk, IPPROTO_ESP, NULL); - xfrm_state_walk(net, &walk, nat_keepalive_work_single, &ctx); + do { + ctx.nr = 0; + err = xfrm_state_walk(net, &walk, nat_keepalive_work_collect, &ctx); + local_bh_disable(); + for (i = 0; i < ctx.nr; i++) { + nat_keepalive_work_single(ctx.batch[i], &ctx); + xfrm_state_put(ctx.batch[i]); + } + local_bh_enable(); + } while (err == NAT_KEEPALIVE_BATCH_FULL); xfrm_state_walk_done(&walk, net); if (ctx.next_run) schedule_delayed_work(&net->xfrm.nat_keepalive_work, diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index d6db63304ba6bd..6266a92cf30208 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -940,7 +940,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo, attrs[XFRMA_ALG_AUTH_TRUNC], extack))) goto error; - if (!x->props.aalgo) { + if (!x->aalg) { if ((err = attach_auth(&x->aalg, &x->props.aalgo, attrs[XFRMA_ALG_AUTH], extack))) goto error; diff --git a/rust/Makefile b/rust/Makefile index 80aefe3603be12..674909f63907e1 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -350,7 +350,8 @@ rusttestlib-pin_init: $(src)/pin-init/src/lib.rs rusttestlib-macros \ rusttestlib-kernel: private rustc_target_flags = --extern ffi \ --extern build_error --extern macros --extern pin_init \ --extern bindings --extern uapi \ - --extern zerocopy --extern zerocopy_derive + --extern zerocopy=$(objtree)/$(obj)/test/libzerocopy.rlib \ + --extern zerocopy_derive=$(objtree)/$(obj)/test/$(libzerocopy_derive_name) rusttestlib-kernel: $(src)/kernel/lib.rs rusttestlib-bindings rusttestlib-uapi \ rusttestlib-build_error rusttestlib-pin_init $(obj)/$(libmacros_name) \ $(obj)/bindings.o rusttestlib-zerocopy rusttestlib-zerocopy_derive FORCE @@ -644,7 +645,8 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L -Zunstable-options \ $(if $(link_helper),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \ $(obj)/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \ - $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \ + $(CC) $(CLANG_FLAGS) $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS)) \ + $(CC_FLAGS_RUST_INLINE_HELPERS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \ $(cmd_ld_single)) \ $(if $(rustc_objcopy),;$(OBJCOPY) $(rustc_objcopy) $@) \ $(cmd_objtool) diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs index ed943960f85164..3566f0234ca403 100644 --- a/rust/kernel/bug.rs +++ b/rust/kernel/bug.rs @@ -8,6 +8,7 @@ #[macro_export] #[doc(hidden)] +#[cfg(not(testlib))] #[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))] #[cfg(CONFIG_DEBUG_BUGVERBOSE)] macro_rules! warn_flags { @@ -47,12 +48,17 @@ macro_rules! warn_flags { #[macro_export] #[doc(hidden)] +#[cfg(not(testlib))] #[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))] #[cfg(not(CONFIG_DEBUG_BUGVERBOSE))] macro_rules! warn_flags { ($file:expr, $flags:expr) => { const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags; + if false { + _ = $file; + } + // SAFETY: // - `flags` and `size` are all compile-time constants, preventing // any invalid memory access. @@ -73,14 +79,19 @@ macro_rules! warn_flags { #[macro_export] #[doc(hidden)] +#[cfg(not(testlib))] #[cfg(all(CONFIG_BUG, CONFIG_UML))] macro_rules! warn_flags { ($file:expr, $flags:expr) => { + if false { + _ = $file; + } + // SAFETY: It is always safe to call `warn_slowpath_fmt()` // with a valid null-terminated string. unsafe { $crate::bindings::warn_slowpath_fmt( - $crate::c_str!(::core::file!()).as_char_ptr(), + $crate::str::CStrExt::as_char_ptr($crate::c_str!(::core::file!())), line!() as $crate::ffi::c_int, $flags as $crate::ffi::c_uint, ::core::ptr::null(), @@ -91,9 +102,15 @@ macro_rules! warn_flags { #[macro_export] #[doc(hidden)] +#[cfg(not(testlib))] #[cfg(all(CONFIG_BUG, any(CONFIG_LOONGARCH, CONFIG_ARM)))] macro_rules! warn_flags { ($file:expr, $flags:expr) => { + if false { + _ = $file; + _ = $flags; + } + // SAFETY: It is always safe to call `WARN_ON()`. unsafe { $crate::bindings::WARN_ON(true) } }; @@ -101,9 +118,14 @@ macro_rules! warn_flags { #[macro_export] #[doc(hidden)] -#[cfg(not(CONFIG_BUG))] +#[cfg(any(testlib, not(CONFIG_BUG)))] macro_rules! warn_flags { - ($file:expr, $flags:expr) => {}; + ($file:expr, $flags:expr) => { + if false { + _ = $file; + _ = $flags; + } + }; } #[doc(hidden)] @@ -118,14 +140,14 @@ macro_rules! warn_on { let cond = $cond; #[cfg(CONFIG_DEBUG_BUGVERBOSE_DETAILED)] - const _COND_STR: &str = concat!("[", stringify!($cond), "] ", file!()); + const COND_STR: &str = concat!("[", stringify!($cond), "] ", file!()); #[cfg(not(CONFIG_DEBUG_BUGVERBOSE_DETAILED))] - const _COND_STR: &str = file!(); + const COND_STR: &str = file!(); if cond { const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN); - $crate::warn_flags!(_COND_STR, WARN_ON_FLAGS); + $crate::warn_flags!(COND_STR, WARN_ON_FLAGS); } cond }}; diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 8ff8aedf251a86..73ff8575712c30 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -21,7 +21,8 @@ use crate::{ sync::{ aref::ARef, rcu, - Arc, // + Arc, + Completion, // }, types::{ ForeignOwnable, @@ -37,6 +38,8 @@ struct Inner { node: Opaque, #[pin] data: Revocable, + #[pin] + revocation: Completion, } /// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to @@ -53,6 +56,10 @@ struct Inner { /// After the [`Devres`] has been unbound it is not possible to access the encapsulated resource /// anymore. /// +/// When a [`Devres`] is dropped, it is guaranteed that `T` has been fully dropped by the time +/// [`Devres::drop`] returns, even if a concurrent revocation through the release callback is in +/// progress. +/// /// [`Devres`] users should make sure to simply free the corresponding backing resource in `T`'s /// [`Drop`] implementation. /// @@ -218,6 +225,7 @@ impl Devres { }; }), data <- Revocable::new(data), + revocation <- Completion::new(), }), GFP_KERNEL, )?; @@ -255,7 +263,14 @@ impl Devres { // SAFETY: `inner` is a valid `Inner` pointer. let inner = unsafe { &*inner }; - inner.data.revoke(); + if inner.data.revoke() { + inner.revocation.complete_all(); + } else { + // Devres::drop() is concurrently revoking; wait for it to finish `drop_in_place()` + // before returning to `devres_release_all()`, ensuring `T` is fully torn down before + // the device finishes unbinding. + inner.revocation.wait_for_completion(); + } } #[allow(clippy::missing_safety_doc)] @@ -355,6 +370,8 @@ impl Drop for Devres { // SAFETY: When `drop` runs, it is guaranteed that nobody is accessing the revocable data // anymore, hence it is safe not to wait for the grace period to finish. if unsafe { self.data().revoke_nosync() } { + self.inner.revocation.complete_all(); + // We revoked `self.data` before devres did, hence try to remove it. if self.remove_node() { // SAFETY: In `Self::new` we have taken an additional reference count of `self.data` @@ -362,6 +379,10 @@ impl Drop for Devres { // this additional reference count. drop(unsafe { Arc::from_raw(Arc::as_ptr(&self.inner)) }); } + } else { + // The release callback is concurrently revoking; wait for it to finish + // `drop_in_place()` of the wrapped object before returning. + self.inner.revocation.wait_for_completion(); } } } diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 200def84fb69e0..556424b88fb947 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -1005,7 +1005,11 @@ impl debugfs::BinaryWriter for Coherent { return Ok(0); }; - let count = self.size().saturating_sub(offset_val).min(writer.len()); + if offset_val >= self.size() { + return Ok(0); + } + + let count = (self.size() - offset_val).min(writer.len()); writer.write_dma(self, offset_val, count)?; diff --git a/rust/kernel/drm/ioctl.rs b/rust/kernel/drm/ioctl.rs index cf328101dde4ef..ccf4150d83b69d 100644 --- a/rust/kernel/drm/ioctl.rs +++ b/rust/kernel/drm/ioctl.rs @@ -135,6 +135,12 @@ macro_rules! declare_drm_ioctls { // dev/file match the current driver these ioctls are being declared // for, and it's not clear how to enforce this within the type system. let dev = $crate::drm::device::Device::from_raw(raw_dev); + + // Enforce that the handler accepts higher-ranked + // lifetimes, preventing it from requiring 'static + // references that could escape this scope. + let _: for<'a> fn(&'a _, &'a mut _, &'a _) -> _ = $func; + // SAFETY: The ioctl argument has size `_IOC_SIZE(cmd)`, which we // asserted above matches the size of this type, and all bit patterns of // UAPI structs must be valid. diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs index 73afbc51ba33a1..cd7d9664ff5b96 100644 --- a/rust/kernel/fmt.rs +++ b/rust/kernel/fmt.rs @@ -43,7 +43,14 @@ use core::fmt::{ UpperExp, UpperHex, // }; -impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, Pointer, LowerExp, UpperExp); +impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, LowerExp, UpperExp); + +impl Pointer for Adapter<&T> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(self.0, f) + } +} /// A copy of [`core::fmt::Display`] that allows us to implement it for foreign types. /// diff --git a/rust/kernel/list.rs b/rust/kernel/list.rs index 406e3a028c5585..0f367264ee2ebc 100644 --- a/rust/kernel/list.rs +++ b/rust/kernel/list.rs @@ -249,7 +249,7 @@ pub use self::arc_field::{ /// assert_eq!(list.iter().count(), 3); /// } /// -/// // Pop the items from the list using `pop_front()` and verify the content. +/// // Pop the items from the list using `pop_back()` and verify the content. /// { /// assert_eq!(list.pop_back().ok_or(EINVAL)?.value.foo(), ("a", 15)); /// assert_eq!(list.pop_back().ok_or(EINVAL)?.value.foo(), ("a", 32)); diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index dafe77782d7975..6d766c22e61b59 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -13,7 +13,10 @@ use core::{ }; use kernel::{ - num::Integer, + num::{ + Integer, + Unsigned, // + }, prelude::*, // }; @@ -174,13 +177,16 @@ fn fits_within(value: T, num_bits: u32) -> bool { /// // `u8` (regardless of the passed value). /// // let _ = Bounded::::from(10u8); /// -/// // Booleans can be converted into single-bit `Bounded`s. +/// // Booleans can be converted into unsigned `Bounded`s. /// /// let v = Bounded::::from(false); /// assert_eq!(v.get(), 0); /// /// let v = Bounded::::from(true); /// assert_eq!(v.get(), 1); +/// +/// // This does not build because `i8` is signed. +/// // let _ = Bounded::::from(true); /// ``` /// /// Infallible conversions from a [`Bounded`] to a primitive integer are also supported, and @@ -203,12 +209,16 @@ fn fits_within(value: T, num_bits: u32) -> bool { /// let _v = Bounded::::new::<10>(); /// // assert_eq!(u8::from(_v), 10); /// -/// // Single-bit `Bounded`s can be converted into a boolean. +/// // Unsigned single-bit `Bounded`s can be converted into a boolean. /// let v = Bounded::::new::<1>(); /// assert_eq!(bool::from(v), true); /// /// let v = Bounded::::new::<0>(); /// assert_eq!(bool::from(v), false); +/// +/// // This does not build because `i8` is signed. +/// // let v = Bounded::::new::<-1>(); +/// // let _ = bool::from(v); /// ``` /// /// Fallible conversions from any primitive integer to any [`Bounded`] are also supported using the @@ -485,6 +495,7 @@ where /// assert_eq!(v_shifted.get(), 0xff); /// ``` pub fn shr(self) -> Bounded { + const_assert!(SHIFT < T::BITS); const { assert!(RES + SHIFT >= N) } // SAFETY: We shift the value right by `SHIFT`, reducing the number of bits needed to @@ -1077,31 +1088,33 @@ impl_into_primitive!( i8 i16 i32 i64 isize ); -// Single-bit `Bounded`s can be converted from/to a boolean. +// Unsigned single-bit `Bounded`s can be converted to a boolean. impl From> for bool where - T: Integer + Zeroable, + T: Integer + Zeroable, { fn from(value: Bounded) -> Self { value.get() != Zeroable::zeroed() } } +// Booleans can be converted to unsigned `Bounded`s. + impl From for Bounded where - T: Integer + From, + T: Integer + From, { fn from(value: bool) -> Self { - // SAFETY: A boolean can be represented using a single bit, and thus fits within any - // integer type for any `N` > 0. + // SAFETY: A boolean is represented by `0` or `1`, so it fits within any valid unsigned + // `Bounded` width. unsafe { Self::__new(T::from(value)) } } } impl Bounded where - T: Integer + Zeroable, + T: Integer + Zeroable, { /// Converts this [`Bounded`] into a [`bool`]. /// diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index b8463823aed956..54fc46d460b6b8 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -441,22 +441,25 @@ impl Delta { /// to the value in the [`Delta`]. #[inline] pub fn as_micros_ceil(self) -> i64 { + // Only positive values need to be rounded up: truncating division already + // rounds towards zero, i.e. up, for negative values. + // + // The usual `(nanos + d - 1) / d` is not used because the addition overflows + // once `nanos` exceeds `i64::MAX - (d - 1)`; saturating the addition instead + // would drop the rounding bias and return a result one unit too small. let n = self.as_nanos(); - let n = if n >= 0 { - n.saturating_add(NSEC_PER_USEC - 1) - } else { - n - }; + + let (n, add) = if n > 0 { (n - 1, 1) } else { (n, 0) }; #[cfg(CONFIG_64BIT)] { - n / NSEC_PER_USEC + n / NSEC_PER_USEC + add } #[cfg(not(CONFIG_64BIT))] // SAFETY: It is always safe to call `ktime_to_us()` with any value. unsafe { - bindings::ktime_to_us(n) + bindings::ktime_to_us(n) + add } } diff --git a/samples/damon/mtier.c b/samples/damon/mtier.c index dae6929f827c28..1c7c9c2b47a0db 100644 --- a/samples/damon/mtier.c +++ b/samples/damon/mtier.c @@ -180,6 +180,7 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote) static int damon_sample_mtier_start(void) { struct damon_ctx *ctx; + int err; ctx = damon_sample_mtier_build_ctx(true); if (!ctx) @@ -191,12 +192,21 @@ static int damon_sample_mtier_start(void) return -ENOMEM; } ctxs[1] = ctx; - return damon_start(ctxs, 2, true); + err = damon_start(ctxs, 2, true); + if (!err) + return 0; + + if (damon_is_running(ctxs[0])) + damon_stop(ctxs, 1); + damon_destroy_ctx(ctxs[0]); + damon_destroy_ctx(ctxs[1]); + return err; } static void damon_sample_mtier_stop(void) { - damon_stop(ctxs, 2); + damon_stop(ctxs, 1); + damon_stop(&ctxs[1], 1); damon_destroy_ctx(ctxs[0]); damon_destroy_ctx(ctxs[1]); } diff --git a/samples/damon/prcl.c b/samples/damon/prcl.c index b7c50f2656ce72..edeae145c4a8af 100644 --- a/samples/damon/prcl.c +++ b/samples/damon/prcl.c @@ -106,11 +106,18 @@ static int damon_sample_prcl_start(void) damon_set_schemes(ctx, &scheme, 1); err = damon_start(&ctx, 1, true); - if (err) + if (err) { + damon_destroy_ctx(ctx); return err; + } repeat_call_control.data = ctx; - return damon_call(ctx, &repeat_call_control); + err = damon_call(ctx, &repeat_call_control); + if (err) { + damon_stop(&ctx, 1); + damon_destroy_ctx(ctx); + } + return err; } static void damon_sample_prcl_stop(void) diff --git a/samples/damon/wsse.c b/samples/damon/wsse.c index 799ad44439434a..ff5e8a890f448c 100644 --- a/samples/damon/wsse.c +++ b/samples/damon/wsse.c @@ -87,10 +87,17 @@ static int damon_sample_wsse_start(void) target->pid = target_pidp; err = damon_start(&ctx, 1, true); - if (err) + if (err) { + damon_destroy_ctx(ctx); return err; + } repeat_call_control.data = ctx; - return damon_call(ctx, &repeat_call_control); + err = damon_call(ctx, &repeat_call_control); + if (err) { + damon_stop(&ctx, 1); + damon_destroy_ctx(ctx); + } + return err; } static void damon_sample_wsse_stop(void) diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 9117457432463e..0b71f759eb5c95 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -346,7 +346,8 @@ quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@ cmd_rustc_o_rs = $(rust_common_cmd) --emit=$(if $(CONFIG_RUST_INLINE_HELPERS),llvm-bc=$(patsubst %.o,%.bc,$@),obj=$@) $< \ $(if $(CONFIG_RUST_INLINE_HELPERS),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \ $(objtree)/rust/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \ - $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \ + $(CC) $(CLANG_FLAGS) $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS)) \ + $(CC_FLAGS_RUST_INLINE_HELPERS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \ $(cmd_ld_single)) \ $(cmd_objtool) diff --git a/scripts/rust_is_available.sh b/scripts/rust_is_available.sh index a063a929be6173..a1b5a0ea8a2060 100755 --- a/scripts/rust_is_available.sh +++ b/scripts/rust_is_available.sh @@ -208,6 +208,20 @@ if [ "$bindgen_libclang_cversion" -lt "$bindgen_libclang_min_cversion" ]; then exit 1 fi +if [ "$bindgen_libclang_cversion" -ge 2200000 ] && + [ "$rust_bindings_generator_cversion" -lt 7201 ]; then + # Distributions may have patched the issue. + if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang_22.h | grep -q 'pub foo'; then + echo >&2 "***" + echo >&2 "*** Rust bindings generator '$BINDGEN' < 0.72.1 together with libclang >= 22" + echo >&2 "*** may not work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/3278)." + echo >&2 "*** Your bindgen version: $rust_bindings_generator_version" + echo >&2 "*** Your libclang version: $bindgen_libclang_version" + echo >&2 "***" + warning=1 + fi +fi + # If the C compiler is Clang, then we can also check whether its version # matches the `libclang` version used by the Rust bindings generator. # diff --git a/scripts/rust_is_available_bindgen_libclang_22.h b/scripts/rust_is_available_bindgen_libclang_22.h new file mode 100644 index 00000000000000..6b33544c14a810 --- /dev/null +++ b/scripts/rust_is_available_bindgen_libclang_22.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +struct S; +struct S { + int foo; +}; diff --git a/scripts/rust_is_available_test.py b/scripts/rust_is_available_test.py index d6d54b7ea42a0d..e14cf2c5695e3a 100755 --- a/scripts/rust_is_available_test.py +++ b/scripts/rust_is_available_test.py @@ -54,16 +54,23 @@ def generate_rustc(cls, stdout): """) @classmethod - def generate_bindgen(cls, version_stdout, libclang_stderr): + def generate_bindgen(cls, version_stdout, libclang_stderr, libclang_22_patched=False): if libclang_stderr is None: libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})" else: libclang_case = f"print({repr(libclang_stderr)}, file=sys.stderr)" + if libclang_22_patched: + libclang_22_case = "print('pub foo: ::std::os::raw::c_int,')" + else: + libclang_22_case = "pass" + return cls.generate_executable(f"""#!/usr/bin/env python3 import sys if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv): {libclang_case} +elif "rust_is_available_bindgen_libclang_22.h" in " ".join(sys.argv): + {libclang_22_case} else: print({repr(version_stdout)}) """) @@ -248,6 +255,27 @@ def test_bindgen_libclang_old_version(self): result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen }) self.assertIn(f"libclang (used by the Rust bindings generator '{bindgen}') is too old.", result.stderr) + def test_bindgen_bad_libclang_22(self): + for (bindgen_version, libclang_version, expected_not_patched) in ( + ("0.71.1", "21.1.0", self.Expected.SUCCESS), + ("0.71.1", "22.0.0", self.Expected.SUCCESS_WITH_WARNINGS), + ("0.71.1", "22.1.0", self.Expected.SUCCESS_WITH_WARNINGS), + + ("0.72.0", "22.0.0", self.Expected.SUCCESS_WITH_WARNINGS), + + ("0.72.1", "22.0.0", self.Expected.SUCCESS), + ): + with self.subTest(bindgen_version=bindgen_version, libclang_version=libclang_version): + cc = self.generate_clang(f"clang version {libclang_version}") + libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {libclang_version} [-W#pragma-messages], err: false" + bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr) + result = self.run_script(expected_not_patched, { "BINDGEN": bindgen, "CC": cc }) + if expected_not_patched == self.Expected.SUCCESS_WITH_WARNINGS: + self.assertIn(f"Rust bindings generator '{bindgen}' < 0.72.1 together with libclang >= 22", result.stderr) + + bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_22_patched=True) + result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc }) + def test_clang_matches_bindgen_libclang_different_bindgen(self): bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 999.0.0 [-W#pragma-messages], err: false") result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen }) diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h index 2b6098149b15e2..0e8b67159f56da 100644 --- a/security/apparmor/include/cred.h +++ b/security/apparmor/include/cred.h @@ -222,13 +222,9 @@ static inline struct aa_label *begin_current_label_crit_section(void) { struct aa_label *label = aa_current_raw_label(); - might_sleep(); - if (label_is_stale(label)) { label = aa_get_newest_label(label); - if (aa_replace_current_label(label) == 0) - /* task cred will keep the reference */ - aa_put_label(label); + aa_schedule_stale_label_replacement(); } return label; diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index da331076b40ede..8c8bf33a1c7df3 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -23,7 +23,7 @@ struct aa_ruleset; #define LOCAL_VEC_ENTRIES 8 #define DEFINE_VEC(T, V) \ - struct aa_ ## T *(_ ## V ## _localtmp)[LOCAL_VEC_ENTRIES]; \ + struct aa_ ## T *(_ ## V ## _localtmp)[LOCAL_VEC_ENTRIES + 1]; \ struct aa_ ## T **(V) #define vec_setup(T, V, N, GFP) \ @@ -31,10 +31,10 @@ struct aa_ruleset; if ((N) <= LOCAL_VEC_ENTRIES) { \ typeof(N) i; \ (V) = (_ ## V ## _localtmp); \ - for (i = 0; i < (N); i++) \ + for (i = 0; i <= (N); i++) \ (V)[i] = NULL; \ } else \ - (V) = kzalloc(sizeof(struct aa_ ## T *) * (N), (GFP)); \ + (V) = kzalloc_objs(struct aa_ ## T *, (N) + 1, (GFP)); \ (V) ? 0 : -ENOMEM; \ }) diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h index ff8bed8f60b274..f1c9d6472c6ec6 100644 --- a/security/apparmor/include/task.h +++ b/security/apparmor/include/task.h @@ -21,15 +21,22 @@ static inline struct aa_task_ctx *task_ctx(struct task_struct *task) * @onexec: profile to transition to on next exec (MAY BE NULL) * @previous: profile the task may return to (MAY BE NULL) * @token: magic value the task must know for returning to @previous_profile + * @label_replacement_tw: for aa_schedule_stale_label_replacement() + * @label_replacement_pending: is @label_replacement_tw pending? + * + * When changing this, check if aa_dup_task_ctx() needs to be updated. */ struct aa_task_ctx { struct aa_label *nnp; struct aa_label *onexec; struct aa_label *previous; u64 token; + struct callback_head label_replacement_tw; + bool label_replacement_pending; }; int aa_replace_current_label(struct aa_label *label); +void aa_schedule_stale_label_replacement(void); void aa_set_current_onexec(struct aa_label *label, bool stack); int aa_set_current_hat(struct aa_label *label, u64 token); int aa_restore_previous_label(u64 cookie); @@ -56,10 +63,10 @@ static inline void aa_free_task_ctx(struct aa_task_ctx *ctx) static inline void aa_dup_task_ctx(struct aa_task_ctx *new, const struct aa_task_ctx *old) { - *new = *old; - aa_get_label(new->nnp); - aa_get_label(new->previous); - aa_get_label(new->onexec); + new->nnp = aa_get_label(old->nnp); + new->onexec = aa_get_label(old->onexec); + new->previous = aa_get_label(old->previous); + new->token = old->token; } /** diff --git a/security/apparmor/task.c b/security/apparmor/task.c index 88c13c5fcf977d..ef578c31962718 100644 --- a/security/apparmor/task.c +++ b/security/apparmor/task.c @@ -14,6 +14,7 @@ #include #include +#include #include "include/path.h" #include "include/audit.h" @@ -89,6 +90,32 @@ int aa_replace_current_label(struct aa_label *label) return 0; } +static void aa_replace_stale_label_tw_func(struct callback_head *tw) +{ + struct aa_task_ctx *ctx = task_ctx(current); + struct aa_label *label; + + ctx->label_replacement_pending = false; + label = aa_current_raw_label(); + if (!label_is_stale(label)) + return; + label = aa_get_newest_label(label); + aa_replace_current_label(label); + aa_put_label(label); +} + +/* replace the current task's stale label on syscall return */ +void aa_schedule_stale_label_replacement(void) +{ + struct aa_task_ctx *ctx = task_ctx(current); + + if (ctx->label_replacement_pending) + return; + init_task_work(&ctx->label_replacement_tw, aa_replace_stale_label_tw_func); + if (task_work_add(current, &ctx->label_replacement_tw, TWA_RESUME) == 0) + ctx->label_replacement_pending = true; +} + /** * aa_set_current_onexec - set the tasks change_profile to happen onexec diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index ced2e131b061bf..b280488e15fcb0 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -748,6 +748,8 @@ static int validate_hash_algo(struct dentry *dentry, return -EACCES; path = dentry_path(dentry, pathbuf, PATH_MAX); + if (IS_ERR(path)) + path = NULL; integrity_audit_msg(AUDIT_INTEGRITY_DATA, d_inode(dentry), path, "set_data", errmsg, -EACCES, 0); diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index 13513819991e72..8f57c6111e7e41 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -987,9 +987,9 @@ static int __init trusted_tpm_init(void) static void trusted_tpm_exit(void) { if (chip) { + unregister_key_type(&key_type_trusted); put_device(&chip->dev); kfree(digests); - unregister_key_type(&key_type_trusted); } } diff --git a/security/landlock/errata/abi-1.h b/security/landlock/errata/abi-1.h index 3f099555f0596e..315ea7e0fe5049 100644 --- a/security/landlock/errata/abi-1.h +++ b/security/landlock/errata/abi-1.h @@ -22,3 +22,26 @@ * from their original mount points. */ LANDLOCK_ERRATUM(3) + +/** + * DOC: erratum_4 + * + * Erratum 4: Creation of whiteout objects + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This fix changes the access rights required for the creation of whiteout + * objects through :manpage:`mknod(2)`, :manpage:`renameat2(2)`, or + * :manpage:`link(2)`. Creating whiteout objects is now guarded by + * ``LANDLOCK_ACCESS_FS_MAKE_REG`` instead of ``LANDLOCK_ACCESS_FS_MAKE_CHAR``. + * + * Whiteout objects are used in OverlayFS to mark the absence of a file in an + * upper file system. Despite being created with ``S_IFCHR``, whiteout objects + * do not count as character devices. + * + * Impact: + * + * Sandboxed programs that create OverlayFS whiteouts (such as fuse-overlayfs) + * now require ``LANDLOCK_ACCESS_FS_MAKE_REG`` instead of + * ``LANDLOCK_ACCESS_FS_MAKE_CHAR``. + */ +LANDLOCK_ERRATUM(4) diff --git a/security/landlock/fs.c b/security/landlock/fs.c index f7e5e4ef9eac32..8fc7f82a374aca 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -983,7 +984,8 @@ static int current_check_access_path(const struct path *const path, return -EACCES; } -static __attribute_const__ access_mask_t get_mode_access(const umode_t mode) +static __attribute_const__ access_mask_t get_mode_access(const umode_t mode, + const dev_t dev) { switch (mode & S_IFMT) { case S_IFLNK: @@ -991,6 +993,9 @@ static __attribute_const__ access_mask_t get_mode_access(const umode_t mode) case S_IFDIR: return LANDLOCK_ACCESS_FS_MAKE_DIR; case S_IFCHR: + /* Whiteout objects are guarded with MAKE_REG. */ + if (dev == WHITEOUT_DEV) + return LANDLOCK_ACCESS_FS_MAKE_REG; return LANDLOCK_ACCESS_FS_MAKE_CHAR; case S_IFBLK: return LANDLOCK_ACCESS_FS_MAKE_BLOCK; @@ -1007,6 +1012,13 @@ static __attribute_const__ access_mask_t get_mode_access(const umode_t mode) } } +static access_mask_t get_dentry_access(const struct dentry *const dentry) +{ + const struct inode *const inode = d_backing_inode(dentry); + + return get_mode_access(inode->i_mode, inode->i_rdev); +} + static access_mask_t maybe_remove(const struct dentry *const dentry) { if (d_is_negative(dentry)) @@ -1093,6 +1105,7 @@ static bool collect_domain_accesses(const struct landlock_ruleset *const domain, * @new_dentry: Destination file or directory. * @removable: Sets to true if it is a rename operation. * @exchange: Sets to true if it is a rename operation with RENAME_EXCHANGE. + * @whiteout: Sets to true if it is a rename operation with RENAME_WHITEOUT. * * Because of its unprivileged constraints, Landlock relies on file hierarchies * (and not only inodes) to tie access rights to files. Being able to link or @@ -1140,7 +1153,8 @@ static bool collect_domain_accesses(const struct landlock_ruleset *const domain, static int current_check_refer_path(struct dentry *const old_dentry, const struct path *const new_dir, struct dentry *const new_dentry, - const bool removable, const bool exchange) + const bool removable, const bool exchange, + const bool whiteout) { const struct landlock_cred_security *const subject = landlock_get_applicable_subject(current_cred(), any_fs, NULL); @@ -1159,18 +1173,25 @@ static int current_check_refer_path(struct dentry *const old_dentry, if (exchange) { if (unlikely(d_is_negative(new_dentry))) return -ENOENT; - access_request_parent1 = - get_mode_access(d_backing_inode(new_dentry)->i_mode); + access_request_parent1 = get_dentry_access(new_dentry); } else { access_request_parent1 = 0; } - access_request_parent2 = - get_mode_access(d_backing_inode(old_dentry)->i_mode); + access_request_parent2 = get_dentry_access(old_dentry); if (removable) { access_request_parent1 |= maybe_remove(old_dentry); access_request_parent2 |= maybe_remove(new_dentry); } + /* + * In case of renameat2(2) with RENAME_WHITEOUT, a whiteout object is + * created in the source location, so we require an additional access + * right there. + */ + if (whiteout) + access_request_parent1 |= + get_mode_access(S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV); + /* The mount points are the same for old and new paths, cf. EXDEV. */ if (old_dentry->d_parent == new_dir->dentry) { /* @@ -1520,7 +1541,7 @@ static int hook_path_link(struct dentry *const old_dentry, struct dentry *const new_dentry) { return current_check_refer_path(old_dentry, new_dir, new_dentry, false, - false); + false, false); } static int hook_path_rename(const struct path *const old_dir, @@ -1531,7 +1552,8 @@ static int hook_path_rename(const struct path *const old_dir, { /* old_dir refers to old_dentry->d_parent and new_dir->mnt */ return current_check_refer_path(old_dentry, new_dir, new_dentry, true, - !!(flags & RENAME_EXCHANGE)); + !!(flags & RENAME_EXCHANGE), + !!(flags & RENAME_WHITEOUT)); } static int hook_path_mkdir(const struct path *const dir, @@ -1544,7 +1566,8 @@ static int hook_path_mknod(const struct path *const dir, struct dentry *const dentry, const umode_t mode, const unsigned int dev) { - return current_check_access_path(dir, get_mode_access(mode)); + return current_check_access_path( + dir, get_mode_access(mode, new_decode_dev(dev))); } static int hook_path_symlink(const struct path *const dir, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 5c662b7c0d52c2..b885045c5b8c4b 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1964,7 +1964,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, { struct smack_known **blob; struct smack_known *skp; - struct smack_known *tkp = smk_of_task(smack_cred(tsk->cred)); + struct smack_known *tkp = smk_of_task_struct_obj(tsk); const struct cred *tcred; struct file *file; int rc; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index c9d0c2bb55b269..fa6723a6e82711 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1468,6 +1468,8 @@ static int snd_pcm_pre_start(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->state != SNDRV_PCM_STATE_PREPARED) return -EBADFD; + if (atomic_read(&runtime->buffer_accessing) < 0) + return -EBADFD; /* during hw_params, hw_free or prepare */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !snd_pcm_playback_data(substream)) return -EPIPE; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 4dfd9d53e6d3a2..62addab22efe9a 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -782,7 +782,7 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, substream->framing = framing; substream->clock_type = clock_type; } - return 0; + return err; } EXPORT_SYMBOL(snd_rawmidi_input_params); diff --git a/sound/core/ump_convert.c b/sound/core/ump_convert.c index 0fe13d03165686..85cc67de633062 100644 --- a/sound/core/ump_convert.c +++ b/sound/core/ump_convert.c @@ -258,6 +258,7 @@ static int cvt_legacy_sysex_to_ump(struct ump_cvt_to_ump *cvt, else status = UMP_SYSEX_STATUS_CONTINUE; *data = ump_compose(UMP_MSG_TYPE_DATA, group, status, cvt->len); + data[1] = 0; offset = 8; for (i = 0; i < cvt->len; i++) { *data |= cvt->buf[i] << offset; diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 06bfe09eae1a24..fd5bbf4615e20e 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -1802,6 +1802,12 @@ static int loopback_probe(struct platform_device *devptr) int dev = devptr->id; int err; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, sizeof(struct loopback), &card); if (err < 0) diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c index c217c427bf1e0e..b93291ad067acf 100644 --- a/sound/drivers/mpu401/mpu401.c +++ b/sound/drivers/mpu401/mpu401.c @@ -89,6 +89,12 @@ static int snd_mpu401_probe(struct platform_device *devptr) int err; struct snd_card *card; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + if (port[dev] == SNDRV_AUTO_PORT) { dev_err(&devptr->dev, "specify port\n"); return -EINVAL; diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index 36e9eab204cac1..cefaa00b83e708 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -900,6 +900,12 @@ static int snd_mts64_probe(struct platform_device *pdev) p = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); + if (dev < 0) { + dev_warn(&pdev->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index dcc0899cfb99ab..03ed3c3c27fb7e 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -697,6 +697,12 @@ static int snd_portman_probe(struct platform_device *pdev) p = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); + if (dev < 0) { + dev_warn(&pdev->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 3c28961091b12e..cc9c325df910b8 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -846,6 +846,12 @@ static int snd_serial_probe(struct platform_device *devptr) int err; int dev = devptr->id; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + switch (adaptor[dev]) { case SNDRV_SERIAL_SOUNDCANVAS: ins[dev] = 1; diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index a204f42d10265f..70e16105f1c4ae 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -75,6 +75,12 @@ static int snd_virmidi_probe(struct platform_device *devptr) int idx, err; int dev = devptr->id; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, sizeof(struct snd_card_virmidi), &card); if (err < 0) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 48a2e7f2ebb15c..3c18f7cae047a6 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7442,6 +7442,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8dfd, "HP EliteBook 6 G1a 16", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e0d, "HP EliteBook 6 G1a 14 (AD3Q9ET#UUG)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e13, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), @@ -7949,6 +7950,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + /* Lenovo Yoga Slim 7 14AKP10 shares PCI SSID 17aa:38b4 with Legion Slim 7 + * 16IRH8; use codec SSID to distinguish them + */ + HDA_CODEC_QUIRK(0x17aa, 0x391a, "Lenovo Yoga Slim 7 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x38b4, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2), HDA_CODEC_QUIRK(0x17aa, 0x391c, "Lenovo Yoga 7 2-in-1 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x391d, "Lenovo Yoga 7 2-in-1 16AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), @@ -7989,7 +7994,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3911, "Lenovo Yoga Pro 7 14IAH10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3912, "Lenovo Xiaoxin 14 GT", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), - SND_PCI_QUIRK(0x17aa, 0x391a, "Lenovo Yoga Slim 7 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), @@ -8055,6 +8059,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d05, 0x3031, "TongFang X6AR55xU", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x3033, "TongFang X6SP45xU", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6KK45xU", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x30ba, "TongFang XxAF5xxx", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c index 2cf3664a35ff09..a640292c4f100f 100644 --- a/sound/hda/codecs/realtek/alc662.c +++ b/sound/hda/codecs/realtek/alc662.c @@ -852,6 +852,7 @@ static const struct hda_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26), SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x1e63, 0x6d9a, "F+ FLAPTOP r", ALC897_FIXUP_HP_HSMIC_VERB), #if 0 /* Below is a quirk table taken from the old code. diff --git a/sound/hda/core/ext/stream.c b/sound/hda/core/ext/stream.c index 4c7506d49f55a6..517bd151fcc38b 100644 --- a/sound/hda/core/ext/stream.c +++ b/sound/hda/core/ext/stream.c @@ -210,8 +210,8 @@ void snd_hdac_ext_stream_reset(struct hdac_ext_stream *hext_stream) break; udelay(3); } while (--timeout); - val &= ~AZX_PPLCCTL_STRST; - writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); + snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, + AZX_PPLCCTL_STRST, 0); udelay(3); timeout = 50; diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index fb40476c6c91dc..a9625aedf2a477 100644 --- a/sound/parisc/harmony.c +++ b/sound/parisc/harmony.c @@ -868,6 +868,9 @@ snd_harmony_create(struct snd_card *card, goto free_and_ret; } + spin_lock_init(&h->mixer_lock); + spin_lock_init(&h->lock); + err = request_irq(padev->irq, snd_harmony_interrupt, 0, "harmony", h); if (err) { @@ -877,9 +880,6 @@ snd_harmony_create(struct snd_card *card, } h->irq = padev->irq; - spin_lock_init(&h->mixer_lock); - spin_lock_init(&h->lock); - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, h, &ops); if (err < 0) goto free_and_ret; diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 25a6403044305d..60cc0721d0a6f3 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -1535,6 +1535,13 @@ static int pcxhr_probe(struct pci_dev *pci, mgr->pci = pci; mgr->irq = -1; + /* ISR lock */ + mutex_init(&mgr->lock); + mutex_init(&mgr->msg_lock); + + /* init setup mutex*/ + mutex_init(&mgr->setup_mutex); + if (request_threaded_irq(pci->irq, pcxhr_interrupt, pcxhr_threaded_irq, IRQF_SHARED, KBUILD_MODNAME, mgr)) { @@ -1548,13 +1555,6 @@ static int pcxhr_probe(struct pci_dev *pci, "Digigram at 0x%lx & 0x%lx, 0x%lx irq %i", mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq); - /* ISR lock */ - mutex_init(&mgr->lock); - mutex_init(&mgr->msg_lock); - - /* init setup mutex*/ - mutex_init(&mgr->setup_mutex); - mgr->prmh = kmalloc(sizeof(*mgr->prmh) + sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS - PCXHR_SIZE_MAX_STATUS), diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index ae90e9f513d15a..79858fc9480651 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -843,6 +843,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Thin A15 B7VE"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Thin A15 B7UC"), + } + }, { .driver_data = &acp6x_card, .matches = { diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index a70c46dd5d76fd..27ce03b0fb91a5 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -68,23 +68,33 @@ #define ADAU1761_FIRMWARE "adau1761.bin" static const struct reg_default adau1761_reg_defaults[] = { - { ADAU1761_DEJITTER, 0x03 }, + { ADAU17X1_CLOCK_CONTROL, 0x00 }, + { ADAU17X1_PLL_CONTROL, 0x00 }, { ADAU1761_DIGMIC_JACKDETECT, 0x00 }, + { ADAU17X1_REC_POWER_MGMT, 0x00 }, { ADAU1761_REC_MIXER_LEFT0, 0x00 }, { ADAU1761_REC_MIXER_LEFT1, 0x00 }, { ADAU1761_REC_MIXER_RIGHT0, 0x00 }, { ADAU1761_REC_MIXER_RIGHT1, 0x00 }, { ADAU1761_LEFT_DIFF_INPUT_VOL, 0x00 }, + { ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 }, + { ADAU17X1_MICBIAS, 0x00 }, { ADAU1761_ALC_CTRL0, 0x00 }, { ADAU1761_ALC_CTRL1, 0x00 }, { ADAU1761_ALC_CTRL2, 0x00 }, { ADAU1761_ALC_CTRL3, 0x00 }, - { ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 }, - { ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 }, + { ADAU17X1_SERIAL_PORT0, 0x00 }, + { ADAU17X1_SERIAL_PORT1, 0x00 }, + { ADAU17X1_CONVERTER0, 0x00 }, + { ADAU17X1_CONVERTER1, 0x00 }, + { ADAU17X1_ADC_CONTROL, 0x00 }, + { ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 }, + { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 }, { ADAU1761_PLAY_MIXER_LEFT0, 0x00 }, { ADAU1761_PLAY_MIXER_LEFT1, 0x00 }, { ADAU1761_PLAY_MIXER_RIGHT0, 0x00 }, { ADAU1761_PLAY_MIXER_RIGHT1, 0x00 }, + { ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 }, { ADAU1761_PLAY_LR_MIXER_RIGHT, 0x00 }, { ADAU1761_PLAY_MIXER_MONO, 0x00 }, { ADAU1761_PLAY_HP_LEFT_VOL, 0x00 }, @@ -93,20 +103,6 @@ static const struct reg_default adau1761_reg_defaults[] = { { ADAU1761_PLAY_LINE_RIGHT_VOL, 0x00 }, { ADAU1761_PLAY_MONO_OUTPUT_VOL, 0x00 }, { ADAU1761_POP_CLICK_SUPPRESS, 0x00 }, - { ADAU1761_JACK_DETECT_PIN, 0x00 }, - { ADAU1761_CLK_ENABLE0, 0x00 }, - { ADAU1761_CLK_ENABLE1, 0x00 }, - { ADAU17X1_CLOCK_CONTROL, 0x00 }, - { ADAU17X1_PLL_CONTROL, 0x00 }, - { ADAU17X1_REC_POWER_MGMT, 0x00 }, - { ADAU17X1_MICBIAS, 0x00 }, - { ADAU17X1_SERIAL_PORT0, 0x00 }, - { ADAU17X1_SERIAL_PORT1, 0x00 }, - { ADAU17X1_CONVERTER0, 0x00 }, - { ADAU17X1_CONVERTER1, 0x00 }, - { ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 }, - { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 }, - { ADAU17X1_ADC_CONTROL, 0x00 }, { ADAU17X1_PLAY_POWER_MGMT, 0x00 }, { ADAU17X1_DAC_CONTROL0, 0x00 }, { ADAU17X1_DAC_CONTROL1, 0x00 }, @@ -114,12 +110,16 @@ static const struct reg_default adau1761_reg_defaults[] = { { ADAU17X1_SERIAL_PORT_PAD, 0xaa }, { ADAU17X1_CONTROL_PORT_PAD0, 0xaa }, { ADAU17X1_CONTROL_PORT_PAD1, 0x00 }, + { ADAU1761_JACK_DETECT_PIN, 0x00 }, + { ADAU1761_DEJITTER, 0x03 }, { ADAU17X1_DSP_SAMPLING_RATE, 0x01 }, { ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 }, { ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 }, { ADAU17X1_DSP_ENABLE, 0x00 }, { ADAU17X1_DSP_RUN, 0x00 }, { ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 }, + { ADAU1761_CLK_ENABLE0, 0x00 }, + { ADAU1761_CLK_ENABLE1, 0x00 }, }; static const DECLARE_TLV_DB_SCALE(adau1761_sing_in_tlv, -1500, 300, 1); diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index 549783d3e75efa..dbdf51188cf572 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -206,10 +206,10 @@ static int aw88261_dev_check_sysst(struct aw_device *aw_dev) return ret; check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK) - & AW88261_BIT_SYSST_CHECK; - if (check_val != AW88261_BIT_SYSST_CHECK) { + & AW88261_BIT_PLL_CHECK; + if (check_val != AW88261_BIT_PLL_CHECK) { dev_dbg(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x", - reg_val, AW88261_BIT_SYSST_CHECK); + reg_val, AW88261_BIT_PLL_CHECK); usleep_range(AW88261_2000_US, AW88261_2000_US + 10); } else { return 0; diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h index 270ccf375f367e..f16f9f8c40a2f1 100644 --- a/sound/soc/codecs/aw88261.h +++ b/sound/soc/codecs/aw88261.h @@ -194,12 +194,6 @@ AW88261_OTHS_OT_VALUE | \ AW88261_PLLS_LOCKED_VALUE)) -#define AW88261_BIT_SYSST_CHECK \ - (AW88261_BSTS_FINISHED_VALUE | \ - AW88261_SWS_SWITCHING_VALUE | \ - AW88261_CLKS_STABLE_VALUE | \ - AW88261_PLLS_LOCKED_VALUE) - #define AW88261_ULS_HMUTE_START_BIT (14) #define AW88261_ULS_HMUTE_BITS_LEN (1) #define AW88261_ULS_HMUTE_MASK \ diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c index f49edb9ea1f2ce..e217da124549a8 100644 --- a/sound/soc/codecs/cs35l33.c +++ b/sound/soc/codecs/cs35l33.c @@ -40,6 +40,7 @@ struct cs35l33_private { struct regmap *regmap; struct gpio_desc *reset_gpio; bool amp_cal; + bool irq_requested; int mclk_int; struct regulator_bulk_data core_supplies[2]; int num_core_supplies; @@ -881,6 +882,9 @@ static int cs35l33_runtime_resume(struct device *dev) goto err; } + if (cs35l33->irq_requested) + enable_irq(to_i2c_client(dev)->irq); + return 0; err: @@ -900,6 +904,10 @@ static int cs35l33_runtime_suspend(struct device *dev) /* redo the calibration in next power up */ cs35l33->amp_cal = false; + /* Drain and block the threaded IRQ before cache_only/power-off. */ + if (cs35l33->irq_requested) + disable_irq(to_i2c_client(dev)->irq); + regcache_cache_only(cs35l33->regmap, true); regcache_mark_dirty(cs35l33->regmap); regulator_bulk_disable(cs35l33->num_core_supplies, @@ -1154,10 +1162,12 @@ static int cs35l33_i2c_probe(struct i2c_client *i2c_client) } ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL, - cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, - "cs35l33", cs35l33); + cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs35l33", cs35l33); if (ret != 0) dev_warn(&i2c_client->dev, "Failed to request IRQ: %d\n", ret); + else + cs35l33->irq_requested = true; /* We could issue !RST or skip it based on AMP topology */ cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index e80984b221593a..3f265bd760f7c9 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -45,6 +45,7 @@ struct cs35l34_private { int num_core_supplies; int mclk_int; bool tdm_mode; + bool irq_requested; struct gpio_desc *reset_gpio; /* Active-low reset GPIO */ }; @@ -1032,10 +1033,12 @@ static int cs35l34_i2c_probe(struct i2c_client *i2c_client) } ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL, - cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, - "cs35l34", cs35l34); + cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs35l34", cs35l34); if (ret != 0) dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret); + else + cs35l34->irq_requested = true; cs35l34->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, "reset", GPIOD_OUT_LOW); @@ -1140,6 +1143,9 @@ static int cs35l34_runtime_resume(struct device *dev) dev_err(dev, "Failed to restore register cache\n"); goto err; } + + if (cs35l34->irq_requested) + enable_irq(to_i2c_client(dev)->irq); return 0; err: regcache_cache_only(cs35l34->regmap, true); @@ -1153,6 +1159,10 @@ static int cs35l34_runtime_suspend(struct device *dev) { struct cs35l34_private *cs35l34 = dev_get_drvdata(dev); + /* Drain and block the threaded IRQ before cache_only/power-off. */ + if (cs35l34->irq_requested) + disable_irq(to_i2c_client(dev)->irq); + regcache_cache_only(cs35l34->regmap, true); regcache_mark_dirty(cs35l34->regmap); diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 83c6cbd40804f6..ffd6a5119f5ebd 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -96,51 +96,60 @@ static const struct reg_default cx2072x_reg_defaults[] = { { CX2072X_GPIO_WAKE, 0x00000000 }, { CX2072X_GPIO_UM_ENABLE, 0x00000000 }, { CX2072X_GPIO_STICKY_MASK, 0x00000000 }, - { CX2072X_DAC1_CONVERTER_FORMAT, 0x00000031 }, - { CX2072X_DAC1_AMP_GAIN_RIGHT, 0x0000004a }, - { CX2072X_DAC1_AMP_GAIN_LEFT, 0x0000004a }, { CX2072X_DAC1_POWER_STATE, 0x00000433 }, { CX2072X_DAC1_CONVERTER_STREAM_CHANNEL, 0x00000000 }, { CX2072X_DAC1_EAPD_ENABLE, 0x00000000 }, - { CX2072X_DAC2_CONVERTER_FORMAT, 0x00000031 }, - { CX2072X_DAC2_AMP_GAIN_RIGHT, 0x0000004a }, - { CX2072X_DAC2_AMP_GAIN_LEFT, 0x0000004a }, + { CX2072X_DAC1_AMP_GAIN_RIGHT, 0x0000004a }, + { CX2072X_DAC1_AMP_GAIN_LEFT, 0x0000004a }, + { CX2072X_DAC1_CONVERTER_FORMAT, 0x00000031 }, { CX2072X_DAC2_POWER_STATE, 0x00000433 }, { CX2072X_DAC2_CONVERTER_STREAM_CHANNEL, 0x00000000 }, - { CX2072X_ADC1_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_DAC2_AMP_GAIN_RIGHT, 0x0000004a }, + { CX2072X_DAC2_AMP_GAIN_LEFT, 0x0000004a }, + { CX2072X_DAC2_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0x00000000 }, + { CX2072X_ADC1_POWER_STATE, 0x00000433 }, + { CX2072X_ADC1_CONVERTER_STREAM_CHANNEL, 0x00000000 }, { CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_0, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_1, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_2, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_RIGHT_3, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_3, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_RIGHT_4, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_4, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_RIGHT_5, 0x0000004a }, - { CX2072X_ADC1_AMP_GAIN_LEFT_5, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_RIGHT_6, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_0, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_1, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_2, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_3, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_4, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_5, 0x0000004a }, { CX2072X_ADC1_AMP_GAIN_LEFT_6, 0x0000004a }, - { CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0x00000000 }, - { CX2072X_ADC1_POWER_STATE, 0x00000433 }, - { CX2072X_ADC1_CONVERTER_STREAM_CHANNEL, 0x00000000 }, - { CX2072X_ADC2_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_ADC1_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0x00000000 }, + { CX2072X_ADC2_POWER_STATE, 0x00000433 }, + { CX2072X_ADC2_CONVERTER_STREAM_CHANNEL, 0x00000000 }, { CX2072X_ADC2_AMP_GAIN_RIGHT_0, 0x0000004a }, - { CX2072X_ADC2_AMP_GAIN_LEFT_0, 0x0000004a }, { CX2072X_ADC2_AMP_GAIN_RIGHT_1, 0x0000004a }, - { CX2072X_ADC2_AMP_GAIN_LEFT_1, 0x0000004a }, { CX2072X_ADC2_AMP_GAIN_RIGHT_2, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_LEFT_0, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_LEFT_1, 0x0000004a }, { CX2072X_ADC2_AMP_GAIN_LEFT_2, 0x0000004a }, - { CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0x00000000 }, - { CX2072X_ADC2_POWER_STATE, 0x00000433 }, - { CX2072X_ADC2_CONVERTER_STREAM_CHANNEL, 0x00000000 }, + { CX2072X_ADC2_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_MIXER_POWER_STATE, 0x00000433 }, + { CX2072X_MIXER_GAIN_RIGHT_0, 0x0000004a }, + { CX2072X_MIXER_GAIN_RIGHT_1, 0x0000004a }, + { CX2072X_MIXER_GAIN_LEFT_0, 0x0000004a }, + { CX2072X_MIXER_GAIN_LEFT_1, 0x0000004a }, { CX2072X_PORTA_CONNECTION_SELECT_CTRL, 0x00000000 }, { CX2072X_PORTA_POWER_STATE, 0x00000433 }, { CX2072X_PORTA_PIN_CTRL, 0x000000c0 }, { CX2072X_PORTA_UNSOLICITED_RESPONSE, 0x00000000 }, { CX2072X_PORTA_PIN_SENSE, 0x00000000 }, { CX2072X_PORTA_EAPD_BTL, 0x00000002 }, + { CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTG_POWER_STATE, 0x00000433 }, + { CX2072X_PORTG_PIN_CTRL, 0x00000040 }, + { CX2072X_PORTG_EAPD_BTL, 0x00000002 }, { CX2072X_PORTB_POWER_STATE, 0x00000433 }, { CX2072X_PORTB_PIN_CTRL, 0x00000000 }, { CX2072X_PORTB_UNSOLICITED_RESPONSE, 0x00000000 }, @@ -148,43 +157,16 @@ static const struct reg_default cx2072x_reg_defaults[] = { { CX2072X_PORTB_EAPD_BTL, 0x00000002 }, { CX2072X_PORTB_GAIN_RIGHT, 0x00000000 }, { CX2072X_PORTB_GAIN_LEFT, 0x00000000 }, - { CX2072X_PORTC_POWER_STATE, 0x00000433 }, - { CX2072X_PORTC_PIN_CTRL, 0x00000000 }, - { CX2072X_PORTC_GAIN_RIGHT, 0x00000000 }, - { CX2072X_PORTC_GAIN_LEFT, 0x00000000 }, { CX2072X_PORTD_POWER_STATE, 0x00000433 }, { CX2072X_PORTD_PIN_CTRL, 0x00000020 }, { CX2072X_PORTD_UNSOLICITED_RESPONSE, 0x00000000 }, { CX2072X_PORTD_PIN_SENSE, 0x00000000 }, { CX2072X_PORTD_GAIN_RIGHT, 0x00000000 }, { CX2072X_PORTD_GAIN_LEFT, 0x00000000 }, - { CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0x00000000 }, - { CX2072X_PORTE_POWER_STATE, 0x00000433 }, - { CX2072X_PORTE_PIN_CTRL, 0x00000040 }, - { CX2072X_PORTE_UNSOLICITED_RESPONSE, 0x00000000 }, - { CX2072X_PORTE_PIN_SENSE, 0x00000000 }, - { CX2072X_PORTE_EAPD_BTL, 0x00000002 }, - { CX2072X_PORTE_GAIN_RIGHT, 0x00000000 }, - { CX2072X_PORTE_GAIN_LEFT, 0x00000000 }, - { CX2072X_PORTF_POWER_STATE, 0x00000433 }, - { CX2072X_PORTF_PIN_CTRL, 0x00000000 }, - { CX2072X_PORTF_UNSOLICITED_RESPONSE, 0x00000000 }, - { CX2072X_PORTF_PIN_SENSE, 0x00000000 }, - { CX2072X_PORTF_GAIN_RIGHT, 0x00000000 }, - { CX2072X_PORTF_GAIN_LEFT, 0x00000000 }, - { CX2072X_PORTG_POWER_STATE, 0x00000433 }, - { CX2072X_PORTG_PIN_CTRL, 0x00000040 }, - { CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0x00000000 }, - { CX2072X_PORTG_EAPD_BTL, 0x00000002 }, - { CX2072X_PORTM_POWER_STATE, 0x00000433 }, - { CX2072X_PORTM_PIN_CTRL, 0x00000000 }, - { CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0x00000000 }, - { CX2072X_PORTM_EAPD_BTL, 0x00000002 }, - { CX2072X_MIXER_POWER_STATE, 0x00000433 }, - { CX2072X_MIXER_GAIN_RIGHT_0, 0x0000004a }, - { CX2072X_MIXER_GAIN_LEFT_0, 0x0000004a }, - { CX2072X_MIXER_GAIN_RIGHT_1, 0x0000004a }, - { CX2072X_MIXER_GAIN_LEFT_1, 0x0000004a }, + { CX2072X_PORTC_POWER_STATE, 0x00000433 }, + { CX2072X_PORTC_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTC_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTC_GAIN_LEFT, 0x00000000 }, { CX2072X_SPKR_DRC_ENABLE_STEP, 0x040065a4 }, { CX2072X_SPKR_DRC_CONTROL, 0x007b0024 }, { CX2072X_SPKR_DRC_TEST, 0x00000000 }, @@ -195,12 +177,15 @@ static const struct reg_default cx2072x_reg_defaults[] = { { CX2072X_I2SPCM_CONTROL3, 0x00000000 }, { CX2072X_I2SPCM_CONTROL4, 0x00000000 }, { CX2072X_I2SPCM_CONTROL5, 0x00000000 }, - { CX2072X_I2SPCM_CONTROL6, 0x00000000 }, { CX2072X_UM_INTERRUPT_CRTL_E, 0x00000000 }, + { CX2072X_I2SPCM_CONTROL6, 0x00000000 }, + { CX2072X_DIGITAL_TEST16, 0x00000021 }, + { CX2072X_DIGITAL_TEST17, 0x00000018 }, + { CX2072X_DIGITAL_TEST18, 0x00000024 }, + { CX2072X_DIGITAL_TEST19, 0x00000001 }, + { CX2072X_DIGITAL_TEST20, 0x00000002 }, { CX2072X_CODEC_TEST2, 0x00000000 }, { CX2072X_CODEC_TEST9, 0x00000004 }, - { CX2072X_CODEC_TEST20, 0x00000600 }, - { CX2072X_CODEC_TEST26, 0x00000208 }, { CX2072X_ANALOG_TEST4, 0x00000000 }, { CX2072X_ANALOG_TEST5, 0x00000000 }, { CX2072X_ANALOG_TEST6, 0x0000059a }, @@ -215,11 +200,26 @@ static const struct reg_default cx2072x_reg_defaults[] = { { CX2072X_DIGITAL_TEST11, 0x00000000 }, { CX2072X_DIGITAL_TEST12, 0x00000084 }, { CX2072X_DIGITAL_TEST15, 0x00000077 }, - { CX2072X_DIGITAL_TEST16, 0x00000021 }, - { CX2072X_DIGITAL_TEST17, 0x00000018 }, - { CX2072X_DIGITAL_TEST18, 0x00000024 }, - { CX2072X_DIGITAL_TEST19, 0x00000001 }, - { CX2072X_DIGITAL_TEST20, 0x00000002 }, + { CX2072X_CODEC_TEST20, 0x00000600 }, + { CX2072X_CODEC_TEST26, 0x00000208 }, + { CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTE_POWER_STATE, 0x00000433 }, + { CX2072X_PORTE_PIN_CTRL, 0x00000040 }, + { CX2072X_PORTE_UNSOLICITED_RESPONSE, 0x00000000 }, + { CX2072X_PORTE_PIN_SENSE, 0x00000000 }, + { CX2072X_PORTE_EAPD_BTL, 0x00000002 }, + { CX2072X_PORTE_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTE_GAIN_LEFT, 0x00000000 }, + { CX2072X_PORTF_POWER_STATE, 0x00000433 }, + { CX2072X_PORTF_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTF_UNSOLICITED_RESPONSE, 0x00000000 }, + { CX2072X_PORTF_PIN_SENSE, 0x00000000 }, + { CX2072X_PORTF_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTF_GAIN_LEFT, 0x00000000 }, + { CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTM_POWER_STATE, 0x00000433 }, + { CX2072X_PORTM_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTM_EAPD_BTL, 0x00000002 }, }; /* diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 680e341aa7f1b3..1ab5f8a26e03b5 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -642,10 +642,8 @@ static int hdac_hda_dev_probe(struct hdac_device *hdev) &hdac_hda_codec, hdac_hda_dais, ARRAY_SIZE(hdac_hda_dais)); - if (ret < 0) { + if (ret < 0) dev_err(&hdev->dev, "%s: failed to register HDA codec %d\n", __func__, ret); - return ret; - } snd_hdac_ext_bus_link_put(hdev->bus, hlink); diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c index 6e3b8d0897dd62..f42887caf64e87 100644 --- a/sound/soc/codecs/lpass-macro-common.c +++ b/sound/soc/codecs/lpass-macro-common.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2022, The Linux Foundation. All rights reserved. +#include #include #include #include @@ -14,6 +15,10 @@ static DEFINE_MUTEX(lpass_codec_mutex); static enum lpass_codec_version lpass_codec_version; +static DEFINE_MUTEX(lpass_dmic_clk_mutex); +static void *lpass_dmic_clk_priv; +static const struct lpass_macro_dmic_clk_ops *lpass_dmic_clk_ops; + struct lpass_macro *lpass_macro_pds_init(struct device *dev) { struct lpass_macro *l_pds; @@ -89,5 +94,52 @@ enum lpass_codec_version lpass_macro_get_codec_version(void) } EXPORT_SYMBOL_GPL(lpass_macro_get_codec_version); +int lpass_macro_register_dmic_clk_provider(void *priv, const struct lpass_macro_dmic_clk_ops *ops) +{ + int ret = 0; + + if (!priv || !ops || !ops->request) + return -EINVAL; + + mutex_lock(&lpass_dmic_clk_mutex); + if (lpass_dmic_clk_priv && lpass_dmic_clk_priv != priv) { + ret = -EBUSY; + } else { + lpass_dmic_clk_priv = priv; + lpass_dmic_clk_ops = ops; + } + mutex_unlock(&lpass_dmic_clk_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(lpass_macro_register_dmic_clk_provider); + +void lpass_macro_unregister_dmic_clk_provider(void *priv) +{ + mutex_lock(&lpass_dmic_clk_mutex); + if (lpass_dmic_clk_priv == priv) { + lpass_dmic_clk_ops = NULL; + lpass_dmic_clk_priv = NULL; + } + mutex_unlock(&lpass_dmic_clk_mutex); +} +EXPORT_SYMBOL_GPL(lpass_macro_unregister_dmic_clk_provider); + +int lpass_macro_dmic_clk_request(unsigned int dmic, bool enable) +{ + int ret; + + mutex_lock(&lpass_dmic_clk_mutex); + if (!lpass_dmic_clk_ops) + ret = -ENODEV; + else + ret = lpass_dmic_clk_ops->request(lpass_dmic_clk_priv, dmic, + enable); + mutex_unlock(&lpass_dmic_clk_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(lpass_macro_dmic_clk_request); + MODULE_DESCRIPTION("Common macro driver"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h index 10ad682019fa7b..e13b1e541da63b 100644 --- a/sound/soc/codecs/lpass-macro-common.h +++ b/sound/soc/codecs/lpass-macro-common.h @@ -42,6 +42,14 @@ void lpass_macro_pds_exit(struct lpass_macro *pds); void lpass_macro_set_codec_version(enum lpass_codec_version version); enum lpass_codec_version lpass_macro_get_codec_version(void); +struct lpass_macro_dmic_clk_ops { + int (*request)(void *priv, unsigned int dmic, bool enable); +}; + +int lpass_macro_register_dmic_clk_provider(void *priv, const struct lpass_macro_dmic_clk_ops *ops); +void lpass_macro_unregister_dmic_clk_provider(void *priv); +int lpass_macro_dmic_clk_request(unsigned int dmic, bool enable); + static inline void lpass_macro_pds_exit_action(void *pds) { lpass_macro_pds_exit(pds); diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 0cbf50647ff57c..fb961bbb9db805 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ #define CDC_TX_INP_MUX_ADC_MUXn_CFG0(n) (0x0100 + 0x8 * n) #define CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK GENMASK(3, 0) #define CDC_TX_MACRO_DMIC_MUX_SEL_MASK GENMASK(7, 4) +#define CDC_TX_MACRO_DEC_MUX_SEL_MASK GENMASK(1, 0) #define CDC_TX_INP_MUX_ADC_MUX0_CFG0 (0x0100) #define CDC_TX_INP_MUX_ADC_MUXn_CFG1(n) (0x0104 + 0x8 * n) #define CDC_TX_INP_MUX_ADC_MUX0_CFG1 (0x0104) @@ -285,6 +287,7 @@ struct tx_macro { int dec_mode[NUM_DECIMATORS]; struct lpass_macro *pds; bool bcs_clk_en; + bool denali_va_dmic_active; }; #define to_tx_macro(_hw) container_of(_hw, struct tx_macro, hw) @@ -906,6 +909,69 @@ static int tx_macro_tx_mixer_put(struct snd_kcontrol *kcontrol, return 1; } +static int tx_macro_denali_va_dmic_request(struct snd_soc_component *component, + bool enable) +{ + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + unsigned long expected = BIT(TX_MACRO_DEC0) | BIT(TX_MACRO_DEC1); + unsigned int dec0_src, dec1_src, dec0_dmic, dec1_dmic; + int ret, ret2; + + if (!of_machine_is_compatible("microsoft,denali")) + return 0; + + if (enable == tx->denali_va_dmic_active) + return 0; + + if (!enable) { + ret = lpass_macro_dmic_clk_request(1, false); + ret2 = lpass_macro_dmic_clk_request(0, false); + if (!ret && ret2) + ret = ret2; + if (!ret) + tx->denali_va_dmic_active = false; + + return ret; + } + + if (tx->active_ch_mask[TX_MACRO_AIF1_CAP] != expected || + tx->active_ch_mask[TX_MACRO_AIF2_CAP] || + tx->active_ch_mask[TX_MACRO_AIF3_CAP]) + return -EINVAL; + + dec0_src = snd_soc_component_read(component, + CDC_TX_INP_MUX_ADC_MUXn_CFG1(TX_MACRO_DEC0)); + dec0_src &= CDC_TX_MACRO_DEC_MUX_SEL_MASK; + dec1_src = snd_soc_component_read(component, + CDC_TX_INP_MUX_ADC_MUXn_CFG1(TX_MACRO_DEC1)); + dec1_src &= CDC_TX_MACRO_DEC_MUX_SEL_MASK; + dec0_dmic = snd_soc_component_read(component, + CDC_TX_INP_MUX_ADC_MUXn_CFG0(TX_MACRO_DEC0)); + dec0_dmic = FIELD_GET(CDC_TX_MACRO_DMIC_MUX_SEL_MASK, dec0_dmic); + dec1_dmic = snd_soc_component_read(component, + CDC_TX_INP_MUX_ADC_MUXn_CFG0(TX_MACRO_DEC1)); + dec1_dmic = FIELD_GET(CDC_TX_MACRO_DMIC_MUX_SEL_MASK, dec1_dmic); + + /* Windows EP16 uses DEC0 <- DMIC1 and DEC1 <- DMIC0. */ + if (dec0_src != MSM_DMIC || dec1_src != MSM_DMIC || + dec0_dmic != 2 || dec1_dmic != 1) + return -EINVAL; + + ret = lpass_macro_dmic_clk_request(1, true); + if (ret) + return ret; + + ret = lpass_macro_dmic_clk_request(0, true); + if (ret) { + lpass_macro_dmic_clk_request(1, false); + return ret; + } + + tx->denali_va_dmic_active = true; + + return 0; +} + static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -915,6 +981,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, u8 hpf_cut_off_freq; int hpf_delay = TX_MACRO_DMIC_HPF_DELAY_MS; int unmute_delay = TX_MACRO_DMIC_UNMUTE_DELAY_MS; + int ret; u16 adc_mux_reg, adc_reg, adc_n, dmic; u16 dmic_clk_reg; struct tx_macro *tx = snd_soc_component_get_drvdata(component); @@ -949,6 +1016,10 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, CDC_TXn_PGA_MUTE_MASK, 0x1); break; case SND_SOC_DAPM_POST_PMU: + ret = tx_macro_denali_va_dmic_request(component, true); + if (ret) + return ret; + snd_soc_component_write_field(component, tx_vol_ctl_reg, CDC_TXn_CLK_EN_MASK, 0x1); if (!is_amic_enabled(component, tx, decimator)) { @@ -1047,6 +1118,10 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, cancel_delayed_work_sync(&tx->tx_mute_dwork[decimator].dwork); break; case SND_SOC_DAPM_POST_PMD: + ret = tx_macro_denali_va_dmic_request(component, false); + if (ret) + return ret; + snd_soc_component_write_field(component, tx_vol_ctl_reg, CDC_TXn_CLK_EN_MASK, 0x0); snd_soc_component_write_field(component, dec_cfg_reg, diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index d043e79dc7f393..34fe8d9f742bc9 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -724,6 +725,16 @@ static int va_dmic_clk_enable(struct snd_soc_component *component, return 0; } +static int va_macro_shared_dmic_clk_request(void *priv, unsigned int dmic, + bool enable) +{ + return va_dmic_clk_enable(priv, dmic, enable); +} + +static const struct lpass_macro_dmic_clk_ops va_macro_dmic_clk_ops = { + .request = va_macro_shared_dmic_clk_request, +}; + static int va_macro_enable_dmic(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1331,15 +1342,31 @@ static const struct snd_kcontrol_new va_macro_snd_controls[] = { static int va_macro_component_probe(struct snd_soc_component *component) { struct va_macro *va = snd_soc_component_get_drvdata(component); + int ret; snd_soc_component_init_regmap(component, va->regmap); + if (!of_machine_is_compatible("microsoft,denali")) + return 0; + + ret = lpass_macro_register_dmic_clk_provider(component, &va_macro_dmic_clk_ops); + if (ret) + return dev_err_probe(component->dev, ret, + "failed to register shared DMIC clock provider\n"); + return 0; } +static void va_macro_component_remove(struct snd_soc_component *component) +{ + if (of_machine_is_compatible("microsoft,denali")) + lpass_macro_unregister_dmic_clk_provider(component); +} + static const struct snd_soc_component_driver va_macro_component_drv = { .name = "VA MACRO", .probe = va_macro_component_probe, + .remove = va_macro_component_remove, .controls = va_macro_snd_controls, .num_controls = ARRAY_SIZE(va_macro_snd_controls), .dapm_widgets = va_macro_dapm_widgets, @@ -1454,6 +1481,10 @@ static int va_macro_validate_dmic_sample_rate(u32 dmic_sample_rate, u32 div_factor; u32 mclk_rate = VA_MACRO_MCLK_FREQ; + /* Denali programs its divider from the 19.2 MHz VA MCLK vote. */ + if (of_machine_is_compatible("microsoft,denali")) + mclk_rate = 2 * VA_MACRO_MCLK_FREQ; + if (!dmic_sample_rate || mclk_rate % dmic_sample_rate != 0) goto undefined_rate; diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index cb457f3edf7d59..1ecef7f6d9d50b 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +57,17 @@ #define CDC_WSA_RX_MIX_TX0_SEL_MASK GENMASK(2, 0) #define CDC_WSA_RX_INP_MUX_RX_EC_CFG0 (0x0114) #define CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0 (0x0118) +#define CDC_WSA_VBAT_BCL_VBAT_PATH_CTL (0x0180) +#define CDC_WSA_VBAT_BCL_VBAT_CFG (0x0184) +#define CDC_WSA_VBAT_BCL_GAIN_UPD1 (0x01DC) +#define CDC_WSA_VBAT_BCL_GAIN_UPD2 (0x01E0) +#define CDC_WSA_VBAT_BCL_GAIN_UPD3 (0x01E4) +#define CDC_WSA_VBAT_BCL_GAIN_UPD4 (0x01E8) +#define CDC_WSA_VBAT_BCL_GAIN_UPD5 (0x01EC) +#define CDC_WSA_VBAT_BCL_GAIN_UPD6 (0x01F0) +#define CDC_WSA_VBAT_BCL_GAIN_UPD7 (0x01F4) +#define CDC_WSA_VBAT_BCL_GAIN_UPD8 (0x01F8) +#define CDC_WSA_VBAT_BCL_GAIN_UPD9 (0x01FC) #define CDC_WSA_TX0_SPKR_PROT_PATH_CTL (0x0244) #define CDC_WSA_TX_SPKR_PROT_RESET_MASK BIT(5) #define CDC_WSA_TX_SPKR_PROT_RESET BIT(5) @@ -75,6 +88,7 @@ #define CDC_WSA_TX2_SPKR_PROT_PATH_CFG0 (0x0288) #define CDC_WSA_TX3_SPKR_PROT_PATH_CTL (0x02A4) #define CDC_WSA_TX3_SPKR_PROT_PATH_CFG0 (0x02A8) +#define WSA_MACRO_PROTECTION_PA_COUNT 2 #define CDC_WSA_INTR_CTRL_CFG (0x0340) #define CDC_WSA_INTR_CTRL_CLR_COMMIT (0x0344) #define CDC_WSA_INTR_CTRL_PIN1_MASK0 (0x0360) @@ -262,6 +276,9 @@ #define CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0644) #define CDC_2_5_WSA_SOFTCLIP1_CRC (0x0660) #define CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0664) +#define CDC_2_5_WSA_CB_DECODE_CTL0 (0x0900) +#define CDC_2_5_WSA_CB_DECODE_CTL1 (0x0904) +#define CDC_2_5_WSA_CB_DECODE_CMD (0x0908) #define WSA_MACRO_RX_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ @@ -286,6 +303,7 @@ #define WSA_MACRO_RX_PATH_OFFSET 0x80 #define WSA_MACRO_RX_PATH_CFG3_OFFSET 0x10 #define WSA_MACRO_RX_PATH_DSMDEM_OFFSET 0x4C +#define WSA_MACRO_SOFTCLIP_CTRL_OFFSET 0x4 #define WSA_MACRO_FS_RATE_MASK 0x0F #define WSA_MACRO_EC_MIX_TX0_MASK 0x03 #define WSA_MACRO_EC_MIX_TX1_MASK 0x18 @@ -374,6 +392,7 @@ enum { WSA_MACRO_AIF_MIX1_PB, WSA_MACRO_AIF_VI, WSA_MACRO_AIF_ECHO, + WSA_MACRO_AIF_CPS, WSA_MACRO_MAX_DAIS, }; @@ -414,6 +433,13 @@ struct wsa_macro { u32 pcm_rate_vi; int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX]; int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX]; + bool protected_feedback; + /* Protects protection_pa_users and protection_clocks_enabled. */ + struct mutex protection_lock; + unsigned int protection_pa_users; + bool protection_clocks_enabled; + /* Runtime PM serializes this; teardown reads it after disabling PM. */ + bool runtime_clks_enabled; struct regmap *regmap; struct clk *mclk; struct clk *npl; @@ -424,6 +450,8 @@ struct wsa_macro { }; #define to_wsa_macro(_hw) container_of(_hw, struct wsa_macro, hw) +static const struct snd_soc_component_driver wsa_macro_component_drv; + static const struct wsa_reg_layout wsa_codec_v2_1 = { .rx_intx_1_mix_inp0_sel_mask = GENMASK(2, 0), .rx_intx_1_mix_inp1_sel_mask = GENMASK(5, 3), @@ -814,6 +842,33 @@ static bool wsa_is_wronly_register(struct device *dev, return false; } +static bool wsa_is_vbat_register(struct wsa_macro *wsa, unsigned int reg) +{ + if (!wsa->protected_feedback) + return false; + + switch (reg) { + case CDC_WSA_VBAT_BCL_VBAT_PATH_CTL: + case CDC_WSA_VBAT_BCL_VBAT_CFG: + case CDC_WSA_VBAT_BCL_GAIN_UPD1: + case CDC_WSA_VBAT_BCL_GAIN_UPD2: + case CDC_WSA_VBAT_BCL_GAIN_UPD3: + case CDC_WSA_VBAT_BCL_GAIN_UPD4: + case CDC_WSA_VBAT_BCL_GAIN_UPD5: + case CDC_WSA_VBAT_BCL_GAIN_UPD6: + case CDC_WSA_VBAT_BCL_GAIN_UPD7: + case CDC_WSA_VBAT_BCL_GAIN_UPD8: + case CDC_WSA_VBAT_BCL_GAIN_UPD9: + return true; + case CDC_2_5_WSA_CB_DECODE_CTL0: + case CDC_2_5_WSA_CB_DECODE_CTL1: + case CDC_2_5_WSA_CB_DECODE_CMD: + return wsa->codec_version >= LPASS_CODEC_VERSION_2_5; + default: + return false; + } +} + static bool wsa_is_rw_register_v2_1(struct device *dev, unsigned int reg) { switch (reg) { @@ -885,6 +940,9 @@ static bool wsa_is_rw_register(struct device *dev, unsigned int reg) { struct wsa_macro *wsa = dev_get_drvdata(dev); + if (wsa_is_vbat_register(wsa, reg)) + return true; + switch (reg) { case CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL: case CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL: @@ -1080,6 +1138,9 @@ static bool wsa_is_volatile_register(struct device *dev, unsigned int reg) { struct wsa_macro *wsa = dev_get_drvdata(dev); + if (wsa_is_vbat_register(wsa, reg)) + return true; + /* Update volatile list for rx/tx macros */ switch (reg) { case CDC_WSA_INTR_CTRL_PIN1_STATUS0: @@ -1278,6 +1339,15 @@ static int wsa_macro_hw_params(struct snd_pcm_substream *substream, struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); int ret; + if (wsa->protected_feedback && dai->id == WSA_MACRO_AIF_VI && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + wsa->pcm_rate_vi = params_rate(params); + return 0; + } + + if (wsa->protected_feedback && dai->id == WSA_MACRO_AIF_CPS) + return 0; + switch (substream->stream) { case SNDRV_PCM_STREAM_PLAYBACK: ret = wsa_macro_set_interpolator_rate(dai, params_rate(params)); @@ -1312,6 +1382,10 @@ static int wsa_macro_get_channel_map(const struct snd_soc_dai *dai, *tx_slot = wsa->active_ch_mask[dai->id]; *tx_num = wsa->active_ch_cnt[dai->id]; break; + case WSA_MACRO_AIF_CPS: + *tx_num = 2; + *tx_slot = GENMASK(1, 0); + break; case WSA_MACRO_AIF1_PB: case WSA_MACRO_AIF_MIX1_PB: for_each_set_bit(temp, &wsa->active_ch_mask[dai->id], @@ -1452,6 +1526,8 @@ static void wsa_macro_mclk_enable(struct wsa_macro *wsa, bool mclk_enable) static void wsa_macro_enable_disable_vi_sense(struct snd_soc_component *component, bool enable, u32 tx_reg0, u32 tx_reg1, u32 val) { + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + if (enable) { /* Enable V&I sensing */ snd_soc_component_update_bits(component, tx_reg0, @@ -1478,6 +1554,10 @@ static void wsa_macro_enable_disable_vi_sense(struct snd_soc_component *componen snd_soc_component_update_bits(component, tx_reg1, CDC_WSA_TX_SPKR_PROT_RESET_MASK, CDC_WSA_TX_SPKR_PROT_NO_RESET); + + /* Materialize the qualified feedback lane order at the producer. */ + if (wsa->protected_feedback) + snd_soc_component_write(component, CDC_WSA_TOP_TOP_CFG1, 0x03); } else { snd_soc_component_update_bits(component, tx_reg0, CDC_WSA_TX_SPKR_PROT_RESET_MASK, @@ -1494,6 +1574,63 @@ static void wsa_macro_enable_disable_vi_sense(struct snd_soc_component *componen } } +static void wsa_macro_set_protection_clocks(struct snd_soc_component *component, + bool enable) +{ + wsa_macro_enable_disable_vi_sense(component, enable, + CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0); + wsa_macro_enable_disable_vi_sense(component, enable, + CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0); +} + +bool wsa_macro_protection_pa_event(struct snd_soc_component *source, + bool enable) +{ + struct snd_soc_component *component; + struct wsa_macro *wsa; + + if (!source || !source->card) + return false; + + for_each_card_components(source->card, component) { + if (component->driver != &wsa_macro_component_drv) + continue; + + wsa = snd_soc_component_get_drvdata(component); + if (!wsa->protected_feedback) + continue; + + guard(mutex)(&wsa->protection_lock); + + if (enable) { + if (wsa->protection_pa_users < WSA_MACRO_PROTECTION_PA_COUNT) + wsa->protection_pa_users++; + + if (wsa->protection_pa_users == WSA_MACRO_PROTECTION_PA_COUNT && + !wsa->protection_clocks_enabled) { + wsa_macro_set_protection_clocks(component, true); + wsa->protection_clocks_enabled = true; + } + } else { + if (wsa->protection_pa_users == WSA_MACRO_PROTECTION_PA_COUNT && + wsa->protection_clocks_enabled) { + wsa_macro_set_protection_clocks(component, false); + wsa->protection_clocks_enabled = false; + } + + if (wsa->protection_pa_users) + wsa->protection_pa_users--; + } + + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(wsa_macro_protection_pa_event); + static void wsa_macro_enable_disable_vi_feedback(struct snd_soc_component *component, bool enable, u32 rate) { @@ -1566,9 +1703,14 @@ static int wsa_macro_enable_vi_feedback(struct snd_soc_dapm_widget *w, static void wsa_macro_hd2_control(struct snd_soc_component *component, u16 reg, int event) { + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); u16 hd2_scale_reg; u16 hd2_enable_reg; + /* The qualified Denali producer path does not enable generic HD2. */ + if (wsa->protected_feedback) + return; + if (reg == CDC_WSA_RX0_RX_PATH_CTL) { hd2_scale_reg = CDC_WSA_RX0_RX_PATH_SEC3; hd2_enable_reg = CDC_WSA_RX0_RX_PATH_CFG0; @@ -1688,6 +1830,84 @@ static void wsa_macro_enable_softclip_clk(struct snd_soc_component *component, } } +/* Preserve the qualified Denali VBAT/BCL producer sequence on both paths. */ +static int wsa_macro_enable_vbat(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + u16 vbat_path_cfg; + int softclip_path; + static const u16 bcl_regs[] = { + CDC_WSA_VBAT_BCL_GAIN_UPD1, CDC_WSA_VBAT_BCL_GAIN_UPD2, + CDC_WSA_VBAT_BCL_GAIN_UPD3, CDC_WSA_VBAT_BCL_GAIN_UPD4, + CDC_WSA_VBAT_BCL_GAIN_UPD5, CDC_WSA_VBAT_BCL_GAIN_UPD6, + CDC_WSA_VBAT_BCL_GAIN_UPD7, CDC_WSA_VBAT_BCL_GAIN_UPD8, + CDC_WSA_VBAT_BCL_GAIN_UPD9, + }; + static const u8 bcl_gain[] = { 0xff, 0x03, 0x00, 0xff, 0x03, 0x00, + 0xff, 0x03, 0x00 }; + int i; + + if (!wsa->protected_feedback) + return -ENODEV; + + if (!snd_soc_dapm_widget_name_cmp(w, "WSA_RX INT0 VBAT")) { + vbat_path_cfg = CDC_WSA_RX0_RX_PATH_CFG1; + softclip_path = WSA_MACRO_SOFTCLIP0; + } else if (!snd_soc_dapm_widget_name_cmp(w, "WSA_RX INT1 VBAT")) { + vbat_path_cfg = CDC_WSA_RX1_RX_PATH_CFG1; + softclip_path = WSA_MACRO_SOFTCLIP1; + } else { + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, CDC_WSA_VBAT_BCL_VBAT_PATH_CTL, + 0x10, 0x10); + snd_soc_component_update_bits(component, CDC_WSA_VBAT_BCL_VBAT_CFG, + 0x01, 0x01); + snd_soc_component_update_bits(component, vbat_path_cfg, 0x80, 0x80); + snd_soc_component_update_bits(component, CDC_WSA_VBAT_BCL_VBAT_CFG, + 0x02, 0x00); + wsa_macro_enable_softclip_clk(component, wsa, softclip_path, true); + snd_soc_component_update_bits(component, vbat_path_cfg, 0x02, 0x02); + for (i = 0; i < ARRAY_SIZE(bcl_regs); i++) + snd_soc_component_write(component, bcl_regs[i], bcl_gain[i]); + + if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5) { + snd_soc_component_write(component, CDC_2_5_WSA_CB_DECODE_CTL0, 0x01); + snd_soc_component_write(component, CDC_2_5_WSA_CB_DECODE_CTL1, 0x01); + snd_soc_component_write(component, CDC_2_5_WSA_CB_DECODE_CMD, 0x01); + } + dev_info(component->dev, "SP11VBAT: %s enabled\n", w->name); + break; + + case SND_SOC_DAPM_POST_PMD: + if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5) { + snd_soc_component_write(component, CDC_2_5_WSA_CB_DECODE_CMD, 0x00); + snd_soc_component_write(component, CDC_2_5_WSA_CB_DECODE_CTL1, 0x00); + snd_soc_component_write(component, CDC_2_5_WSA_CB_DECODE_CTL0, 0x00); + } + snd_soc_component_update_bits(component, vbat_path_cfg, 0x80, 0x00); + snd_soc_component_update_bits(component, CDC_WSA_VBAT_BCL_VBAT_CFG, + 0x02, 0x02); + snd_soc_component_update_bits(component, vbat_path_cfg, 0x02, 0x00); + for (i = 0; i < ARRAY_SIZE(bcl_regs); i++) + snd_soc_component_write(component, bcl_regs[i], 0x00); + wsa_macro_enable_softclip_clk(component, wsa, softclip_path, false); + snd_soc_component_update_bits(component, CDC_WSA_VBAT_BCL_VBAT_CFG, + 0x01, 0x00); + snd_soc_component_update_bits(component, CDC_WSA_VBAT_BCL_VBAT_PATH_CTL, + 0x10, 0x00); + dev_info(component->dev, "SP11VBAT: %s disabled\n", w->name); + break; + } + + return 0; +} + static int wsa_macro_config_softclip(struct snd_soc_component *component, int path, int event) { @@ -1703,7 +1923,8 @@ static int wsa_macro_config_softclip(struct snd_soc_component *component, if (!wsa->is_softclip_on[softclip_path]) return 0; - softclip_ctrl_reg = CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL + + softclip_ctrl_reg = wsa->reg_layout->softclip0_reg_base + + WSA_MACRO_SOFTCLIP_CTRL_OFFSET + (softclip_path * wsa->reg_layout->softclip1_reg_offset); if (SND_SOC_DAPM_EVENT_ON(event)) { @@ -1868,6 +2089,8 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w, snd_soc_component_update_bits(component, CDC_WSA_RX0_RX_PATH_SEC1, CDC_WSA_RX_PGA_HALF_DB_MASK, + wsa->protected_feedback ? + CDC_WSA_RX_PGA_HALF_DB_DISABLE : CDC_WSA_RX_PGA_HALF_DB_ENABLE); snd_soc_component_update_bits(component, CDC_WSA_RX0_RX_PATH_MIX_SEC0, @@ -1876,6 +2099,8 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w, snd_soc_component_update_bits(component, CDC_WSA_RX1_RX_PATH_SEC1, CDC_WSA_RX_PGA_HALF_DB_MASK, + wsa->protected_feedback ? + CDC_WSA_RX_PGA_HALF_DB_DISABLE : CDC_WSA_RX_PGA_HALF_DB_ENABLE); snd_soc_component_update_bits(component, CDC_WSA_RX1_RX_PATH_MIX_SEC0, @@ -2388,6 +2613,40 @@ static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), }; +static const struct snd_soc_dapm_widget wsa_macro_denali_widgets[] = { + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 VBAT", SND_SOC_NOPM, 0, 0, + NULL, 0, wsa_macro_enable_vbat, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 VBAT", SND_SOC_NOPM, 0, 0, + NULL, 0, wsa_macro_enable_vbat, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("WSA AIF_VI Protection", + "WSA_AIF_VI Protection", 0, + SND_SOC_NOPM, WSA_MACRO_AIF_VI, 0, + wsa_macro_enable_vi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN("WSA AIF_CPS Protection", + "WSA_AIF_CPS Protection", 0, + SND_SOC_NOPM, WSA_MACRO_AIF_CPS, 0), + SND_SOC_DAPM_INPUT("CPSINPUT_WSA"), +}; + +static const struct snd_soc_dapm_route wsa_macro_denali_routes[] = { + { "WSA_RX INT0 VBAT", NULL, "WSA_RX INT0 INTERP" }, + { "WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 VBAT" }, + { "WSA_RX INT1 VBAT", NULL, "WSA_RX INT1 INTERP" }, + { "WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 VBAT" }, + { "WSA AIF_VI Protection", NULL, "WSA_AIF_VI Mixer" }, + { "WSA AIF_VI Protection", NULL, "WSA_MCLK" }, + { "WSA AIF_CPS Protection", NULL, "CPSINPUT_WSA" }, + { "WSA AIF_CPS Protection", NULL, "WSA_MCLK" }, +}; + +static const struct snd_soc_dapm_route wsa_macro_generic_routes[] = { + { "WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 INTERP" }, + { "WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 INTERP" }, +}; + static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets_v2_1[] = { SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux_v2_1), SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux_v2_1), @@ -2482,7 +2741,6 @@ static const struct snd_soc_dapm_route wsa_audio_map[] = { {"WSA_RX INT0 INTERP", NULL, "WSA_RX INT0 SEC MIX"}, {"WSA_RX0 INT0 SIDETONE MIX", "SRC0", "WSA SRC0_INP"}, {"WSA_RX INT0 INTERP", NULL, "WSA_RX0 INT0 SIDETONE MIX"}, - {"WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 INTERP"}, {"WSA_SPK1 OUT", NULL, "WSA_RX INT0 CHAIN"}, {"WSA_SPK1 OUT", NULL, "WSA_MCLK"}, @@ -2522,7 +2780,6 @@ static const struct snd_soc_dapm_route wsa_audio_map[] = { {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX INT1 MIX"}, {"WSA_RX INT1 INTERP", NULL, "WSA_RX INT1 SEC MIX"}, - {"WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 INTERP"}, {"WSA_SPK2 OUT", NULL, "WSA_RX INT1 CHAIN"}, {"WSA_SPK2 OUT", NULL, "WSA_MCLK"}, }; @@ -2562,6 +2819,7 @@ static int wsa_macro_component_probe(struct snd_soc_component *comp) struct wsa_macro *wsa = snd_soc_component_get_drvdata(comp); const struct snd_soc_dapm_widget *widgets; unsigned int num_widgets; + int ret; snd_soc_component_init_regmap(comp, wsa->regmap); @@ -2599,7 +2857,21 @@ static int wsa_macro_component_probe(struct snd_soc_component *comp) return -EINVAL; } - return snd_soc_dapm_new_controls(dapm, widgets, num_widgets); + ret = snd_soc_dapm_new_controls(dapm, widgets, num_widgets); + if (ret) + return ret; + + if (!wsa->protected_feedback) + return snd_soc_dapm_add_routes(dapm, wsa_macro_generic_routes, + ARRAY_SIZE(wsa_macro_generic_routes)); + + ret = snd_soc_dapm_new_controls(dapm, wsa_macro_denali_widgets, + ARRAY_SIZE(wsa_macro_denali_widgets)); + if (ret) + return ret; + + return snd_soc_dapm_add_routes(dapm, wsa_macro_denali_routes, + ARRAY_SIZE(wsa_macro_denali_routes)); } static int swclk_gate_enable(struct clk_hw *hw) @@ -2676,12 +2948,172 @@ static const struct snd_soc_component_driver wsa_macro_component_drv = { .num_dapm_routes = ARRAY_SIZE(wsa_audio_map), }; +/* + * Preserve Denali's qualified cache state without changing the shared reset + * tables. These defaults are cache assumptions, not forced hardware writes. + */ +static void wsa_macro_set_denali_defaults(struct wsa_macro *wsa, + struct reg_default *defaults, int count) +{ + int i; + + if (!wsa->protected_feedback) + return; + + for (i = 0; i < count; i++) { + switch (defaults[i].reg) { + case CDC_WSA_TOP_TOP_CFG1: + defaults[i].def = 0x03; + break; + case CDC_WSA_RX0_RX_PATH_CFG1: + case CDC_WSA_RX1_RX_PATH_CFG1: + defaults[i].def = 0x6c; + break; + case CDC_WSA_COMPANDER0_CTL7: + defaults[i].def = 0x2e; + break; + } + + if (wsa->codec_version < LPASS_CODEC_VERSION_2_5) + continue; + + switch (defaults[i].reg) { + case CDC_2_5_WSA_COMPANDER1_CTL7: + defaults[i].def = 0x2e; + break; + case CDC_2_5_WSA_COMPANDER0_CTL11: + case CDC_2_5_WSA_COMPANDER1_CTL11: + defaults[i].def = 0x0c; + break; + case CDC_2_5_WSA_COMPANDER0_CTL12: + case CDC_2_5_WSA_COMPANDER0_CTL13: + case CDC_2_5_WSA_COMPANDER0_CTL14: + case CDC_2_5_WSA_COMPANDER0_CTL15: + case CDC_2_5_WSA_COMPANDER1_CTL12: + case CDC_2_5_WSA_COMPANDER1_CTL13: + case CDC_2_5_WSA_COMPANDER1_CTL14: + case CDC_2_5_WSA_COMPANDER1_CTL15: + defaults[i].def = 0x15; + break; + case CDC_2_5_WSA_COMPANDER0_CTL16: + case CDC_2_5_WSA_COMPANDER1_CTL16: + defaults[i].def = 0x0f; + break; + } + } +} + +static void wsa_macro_disable_runtime_clks(struct wsa_macro *wsa) +{ + if (!wsa->runtime_clks_enabled) + return; + + clk_disable_unprepare(wsa->fsgen); + clk_disable_unprepare(wsa->npl); + clk_disable_unprepare(wsa->mclk); + wsa->runtime_clks_enabled = false; +} + +static int wsa_macro_enable_runtime_clks(struct wsa_macro *wsa) +{ + int ret; + + ret = clk_prepare_enable(wsa->mclk); + if (ret) + return ret; + + ret = clk_prepare_enable(wsa->npl); + if (ret) + goto err_mclk; + + ret = clk_prepare_enable(wsa->fsgen); + if (ret) + goto err_npl; + + wsa->runtime_clks_enabled = true; + return 0; + +err_npl: + clk_disable_unprepare(wsa->npl); +err_mclk: + clk_disable_unprepare(wsa->mclk); + return ret; +} + +static void wsa_macro_release_denali_clks(void *data) +{ + struct wsa_macro *wsa = data; + + /* PM is disabled, and managed component teardown has already run. */ + wsa_macro_disable_runtime_clks(wsa); + clk_disable_unprepare(wsa->dcodec); + clk_disable_unprepare(wsa->macro); +} + +static int wsa_macro_init_clks(struct wsa_macro *wsa) +{ + struct device *dev = wsa->dev; + int ret; + + if (!wsa->protected_feedback) { + ret = devm_pm_clk_create(dev); + if (ret) + return ret; + + ret = of_pm_clk_add_clks(dev); + return ret < 0 ? ret : 0; + } + + /* + * Keep Denali's macro/dcodec votes across runtime suspend, as in its + * qualified clock sequence. Only mclk/npl/fsgen follow runtime PM. + */ + ret = clk_prepare_enable(wsa->macro); + if (ret) + return ret; + + ret = clk_prepare_enable(wsa->dcodec); + if (ret) { + clk_disable_unprepare(wsa->macro); + return ret; + } + + /* Register before the component so its teardown retains the clocks. */ + ret = devm_add_action_or_reset(dev, wsa_macro_release_denali_clks, wsa); + if (ret) + return ret; + + regcache_cache_only(wsa->regmap, true); + return 0; +} + +static int wsa_macro_denali_runtime_resume(struct wsa_macro *wsa) +{ + int ret; + + ret = wsa_macro_enable_runtime_clks(wsa); + if (ret) + return ret; + + regcache_cache_only(wsa->regmap, false); + ret = regcache_sync(wsa->regmap); + if (ret) { + regcache_cache_only(wsa->regmap, true); + regcache_mark_dirty(wsa->regmap); + wsa_macro_disable_runtime_clks(wsa); + } + + return ret; +} + static int wsa_macro_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct snd_soc_dai_driver *dais = wsa_macro_dai; struct wsa_macro *wsa; kernel_ulong_t flags; void __iomem *base; + int num_dais = ARRAY_SIZE(wsa_macro_dai); int ret, def_count; flags = (kernel_ulong_t)device_get_match_data(dev); @@ -2690,6 +3122,12 @@ static int wsa_macro_probe(struct platform_device *pdev) if (!wsa) return -ENOMEM; + mutex_init(&wsa->protection_lock); + wsa->protected_feedback = + of_machine_is_compatible("microsoft,denali"); + /* Regmap's access callbacks need the instance during initialization. */ + dev_set_drvdata(dev, wsa); + wsa->macro = devm_clk_get_optional(dev, "macro"); if (IS_ERR(wsa->macro)) return dev_err_probe(dev, PTR_ERR(wsa->macro), "unable to get macro clock\n"); @@ -2763,28 +3201,57 @@ static int wsa_macro_probe(struct platform_device *pdev) reg_config->reg_defaults = reg_defaults; reg_config->num_reg_defaults = def_count; + wsa_macro_set_denali_defaults(wsa, reg_defaults, def_count); + if (wsa->protected_feedback && + wsa->codec_version >= LPASS_CODEC_VERSION_2_5) + reg_config->max_register = CDC_2_5_WSA_CB_DECODE_CMD; wsa->regmap = devm_regmap_init_mmio(dev, base, reg_config); if (IS_ERR(wsa->regmap)) return PTR_ERR(wsa->regmap); - dev_set_drvdata(dev, wsa); - wsa->dev = dev; + if (wsa->protected_feedback) { + dais = devm_kcalloc(dev, num_dais + 1, sizeof(*dais), GFP_KERNEL); + if (!dais) + return -ENOMEM; + + memcpy(dais, wsa_macro_dai, sizeof(wsa_macro_dai)); + dais[WSA_MACRO_AIF_VI].playback = (struct snd_soc_pcm_stream) { + .stream_name = "WSA_AIF_VI Protection", + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_max = 8000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }; + dais[num_dais] = (struct snd_soc_dai_driver) { + .name = "wsa_macro_cps", + .id = WSA_MACRO_AIF_CPS, + .playback = { + .stream_name = "WSA_AIF_CPS Protection", + .rates = SNDRV_PCM_RATE_24000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_max = 24000, + .rate_min = 24000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }; + num_dais++; + } /* set MCLK and NPL rates */ clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); if (wsa->npl) clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); - ret = devm_pm_clk_create(dev); + ret = wsa_macro_init_clks(wsa); if (ret) return ret; - ret = of_pm_clk_add_clks(dev); - if (ret < 0) - return ret; - pm_runtime_set_autosuspend_delay(dev, 3000); pm_runtime_use_autosuspend(dev); pm_runtime_enable(dev); @@ -2807,8 +3274,7 @@ static int wsa_macro_probe(struct platform_device *pdev) CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_DISABLE); ret = devm_snd_soc_register_component(dev, &wsa_macro_component_drv, - wsa_macro_dai, - ARRAY_SIZE(wsa_macro_dai)); + dais, num_dais); if (ret) goto err_rpm_put; @@ -2839,6 +3305,11 @@ static int wsa_macro_runtime_suspend(struct device *dev) regcache_cache_only(wsa->regmap, true); regcache_mark_dirty(wsa->regmap); + if (wsa->protected_feedback) { + wsa_macro_disable_runtime_clks(wsa); + return 0; + } + return pm_clk_suspend(dev); } @@ -2847,6 +3318,9 @@ static int wsa_macro_runtime_resume(struct device *dev) struct wsa_macro *wsa = dev_get_drvdata(dev); int ret; + if (wsa->protected_feedback) + return wsa_macro_denali_runtime_resume(wsa); + regcache_cache_only(wsa->regmap, false); ret = pm_clk_resume(dev); if (ret) diff --git a/sound/soc/codecs/lpass-wsa-macro.h b/sound/soc/codecs/lpass-wsa-macro.h index d3d62b3f6500c9..d88b92350c7d0d 100644 --- a/sound/soc/codecs/lpass-wsa-macro.h +++ b/sound/soc/codecs/lpass-wsa-macro.h @@ -3,6 +3,10 @@ #ifndef __LPASS_WSA_MACRO_H__ #define __LPASS_WSA_MACRO_H__ +#include + +struct snd_soc_component; + /* * Selects compander and smart boost settings * for a given speaker mode @@ -14,4 +18,15 @@ enum { int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode); +#if IS_REACHABLE(CONFIG_SND_SOC_LPASS_WSA_MACRO) +bool wsa_macro_protection_pa_event(struct snd_soc_component *source, + bool enable); +#else +static inline bool +wsa_macro_protection_pa_event(struct snd_soc_component *source, bool enable) +{ + return false; +} +#endif + #endif /* __LPASS_WSA_MACRO_H__ */ diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index 0d7ac37850bba4..20e288e38669b1 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -48,7 +48,6 @@ static int max9860_dvddio_event(struct notifier_block *nb, } static const struct reg_default max9860_reg_defaults[] = { - { MAX9860_PWRMAN, 0x00 }, { MAX9860_INTEN, 0x00 }, { MAX9860_SYSCLK, 0x00 }, { MAX9860_AUDIOCLKHIGH, 0x00 }, @@ -62,6 +61,7 @@ static const struct reg_default max9860_reg_defaults[] = { { MAX9860_MICGAIN, 0x00 }, { MAX9860_MICADC, 0x00 }, { MAX9860_NOISEGATE, 0x00 }, + { MAX9860_PWRMAN, 0x00 }, }; static bool max9860_readable(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 8a14626d43ffde..7209b47c202913 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -224,7 +224,6 @@ static const struct reg_default ml26124_reg[] = { /* Analog Path Control Register */ {0x54, 0x00}, /* Speaker AMP Output Control */ {0x5a, 0x00}, /* Mic IF Control */ - {0xe8, 0x01}, /* Mic Select Control */ /* Audio Interface Control Register */ {0x60, 0x00}, /* SAI-Trans Control */ @@ -287,6 +286,9 @@ static const struct reg_default ml26124_reg[] = { {0xd0, 0x01}, /* VIDEO AMP Gain Control */ {0xd2, 0x01}, /* VIDEO AMP Setup 1 */ {0xd4, 0x01}, /* VIDEO AMP Control2 */ + + /* Analog Path Control Register */ + {0xe8, 0x01}, /* Mic Select Control */ }; /* Get sampling rate value of sampling rate setting register (0x0) */ diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index fe3b5011fa1678..03b1fcb1bf96ef 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -78,28 +78,10 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_POWER, 0x00 }, { PCM512x_MUTE, 0x00 }, { PCM512x_DSP, 0x00 }, - { PCM512x_PLL_REF, 0x00 }, - { PCM512x_DAC_REF, 0x00 }, - { PCM512x_DAC_ROUTING, 0x11 }, - { PCM512x_DSP_PROGRAM, 0x01 }, - { PCM512x_CLKDET, 0x00 }, - { PCM512x_AUTO_MUTE, 0x00 }, - { PCM512x_ERROR_DETECT, 0x00 }, - { PCM512x_DIGITAL_VOLUME_1, 0x00 }, - { PCM512x_DIGITAL_VOLUME_2, 0x30 }, - { PCM512x_DIGITAL_VOLUME_3, 0x30 }, - { PCM512x_DIGITAL_MUTE_1, 0x22 }, - { PCM512x_DIGITAL_MUTE_2, 0x00 }, - { PCM512x_DIGITAL_MUTE_3, 0x07 }, - { PCM512x_OUTPUT_AMPLITUDE, 0x00 }, - { PCM512x_ANALOG_GAIN_CTRL, 0x00 }, - { PCM512x_UNDERVOLTAGE_PROT, 0x00 }, - { PCM512x_ANALOG_MUTE_CTRL, 0x00 }, - { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, - { PCM512x_VCOM_CTRL_1, 0x00 }, - { PCM512x_VCOM_CTRL_2, 0x01 }, { PCM512x_BCLK_LRCLK_CFG, 0x00 }, { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_PLL_REF, 0x00 }, + { PCM512x_DAC_REF, 0x00 }, { PCM512x_GPIO_DACIN, 0x00 }, { PCM512x_GPIO_PLLIN, 0x00 }, { PCM512x_SYNCHRONIZE, 0x10 }, @@ -117,8 +99,26 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_FS_SPEED_MODE, 0x00 }, { PCM512x_IDAC_1, 0x01 }, { PCM512x_IDAC_2, 0x00 }, + { PCM512x_ERROR_DETECT, 0x00 }, { PCM512x_I2S_1, 0x02 }, { PCM512x_I2S_2, 0x00 }, + { PCM512x_DAC_ROUTING, 0x11 }, + { PCM512x_DSP_PROGRAM, 0x01 }, + { PCM512x_CLKDET, 0x00 }, + { PCM512x_AUTO_MUTE, 0x00 }, + { PCM512x_DIGITAL_VOLUME_1, 0x00 }, + { PCM512x_DIGITAL_VOLUME_2, 0x30 }, + { PCM512x_DIGITAL_VOLUME_3, 0x30 }, + { PCM512x_DIGITAL_MUTE_1, 0x22 }, + { PCM512x_DIGITAL_MUTE_2, 0x00 }, + { PCM512x_DIGITAL_MUTE_3, 0x07 }, + { PCM512x_OUTPUT_AMPLITUDE, 0x00 }, + { PCM512x_ANALOG_GAIN_CTRL, 0x00 }, + { PCM512x_UNDERVOLTAGE_PROT, 0x00 }, + { PCM512x_ANALOG_MUTE_CTRL, 0x00 }, + { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, + { PCM512x_VCOM_CTRL_1, 0x00 }, + { PCM512x_VCOM_CTRL_2, 0x01 }, }; static bool pcm512x_readable(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c index 1c612ae4a4b277..ee4cde4d8686c5 100644 --- a/sound/soc/codecs/pm4125-sdw.c +++ b/sound/soc/codecs/pm4125-sdw.c @@ -126,8 +126,8 @@ static const struct reg_default pm4125_defaults[] = { { PM4125_ANA_HPHPA_FSM_CLK, 0x12 }, { PM4125_ANA_HPHPA_L_GAIN, 0x00 }, { PM4125_ANA_HPHPA_R_GAIN, 0x00 }, - { PM4125_SWR_HPHPA_HD2, 0x1B }, { PM4125_ANA_HPHPA_SPARE_CTL, 0x02 }, + { PM4125_SWR_HPHPA_HD2, 0x1B }, { PM4125_ANA_SURGE_EN, 0x38 }, { PM4125_ANA_COMBOPA_CTL, 0x35 }, { PM4125_ANA_COMBOPA_CTL_4, 0x84 }, diff --git a/sound/soc/codecs/rt1017-sdca-sdw.h b/sound/soc/codecs/rt1017-sdca-sdw.h index 4932b5dbe3c0b5..1604e1cfd85eb7 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.h +++ b/sound/soc/codecs/rt1017-sdca-sdw.h @@ -165,19 +165,19 @@ static const struct reg_default rt1017_sdca_reg_defaults[] = { { 0xdb09, 0x0f }, { 0xdb0a, 0xff }, { 0xdb14, 0x00 }, - - { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, - RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, RT1017_SDCA_CTL_FU_MUTE, 0x01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22, RT1017_SDCA_CTL_BYPASS, 0), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, - RT1017_SDCA_CTL_FS_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22, RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, + RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, + RT1017_SDCA_CTL_FS_INDEX, 0), 0x09 }, }; #endif /* __RT1017_SDW_H__ */ diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index ca318dbd946ee1..0f6fc36e50f0f9 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -59,13 +59,13 @@ static const struct reg_default rt1316_reg_defaults[] = { { 0xd101, 0x00 }, { 0xd102, 0x30 }, { 0xd103, 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, }; static const struct reg_sequence rt1316_blind_write[] = { diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index c038ac0e3b769b..2f1a776585fcb8 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -235,10 +235,10 @@ static const struct reg_default rt1318_reg_defaults[] = { { 0xf805, 0x00 }, { 0xf806, 0x07 }, { 0xf807, 0xff }, - { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23, RT1318_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_CS21, RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, }; diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c index d13fd0e141252e..55607f1b9a1ed0 100644 --- a/sound/soc/codecs/rt1318.c +++ b/sound/soc/codecs/rt1318.c @@ -337,6 +337,8 @@ static const struct reg_default rt1318_reg[] = { { 0xdd08, 0x40 }, { 0xdd12, 0x00 }, { 0xdd35, 0x00 }, + { 0xdd93, 0x00 }, + { 0xdd94, 0x64 }, { 0xddb5, 0x00 }, { 0xddb6, 0x40 }, { 0xddb7, 0x00 }, @@ -345,8 +347,6 @@ static const struct reg_default rt1318_reg[] = { { 0xddc6, 0x00 }, { 0xddc7, 0x00 }, { 0xddc8, 0x00 }, - { 0xdd93, 0x00 }, - { 0xdd94, 0x64 }, { 0xdf00, 0x00 }, { 0xdf5f, 0x00 }, { 0xdf60, 0x00 }, diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index 63b5fc439773dd..f8c370106504c1 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -184,8 +184,10 @@ static const struct reg_default rt274_reg[] = { { 0x0023a000, 0x00000057 }, { 0x00270500, 0x00000400 }, { 0x00370500, 0x00000400 }, + { 0x00830000, 0x00000097 }, { 0x00870500, 0x00000400 }, { 0x00920000, 0x00000031 }, + { 0x00930000, 0x00000097 }, { 0x00935000, 0x00000097 }, { 0x00936000, 0x00000097 }, { 0x00970500, 0x00000400 }, @@ -195,10 +197,12 @@ static const struct reg_default rt274_reg[] = { { 0x00c37000, 0x00000400 }, { 0x00c37100, 0x00000400 }, { 0x01270500, 0x00000400 }, + { 0x01270700, 0x00000000 }, { 0x01370500, 0x00000400 }, { 0x01371f00, 0x411111f0 }, { 0x01937000, 0x00000000 }, { 0x01970500, 0x00000400 }, + { 0x01970700, 0x00000020 }, { 0x02050000, 0x0000001b }, { 0x02139000, 0x00000080 }, { 0x0213a000, 0x00000080 }, @@ -207,10 +211,6 @@ static const struct reg_default rt274_reg[] = { { 0x02170700, 0x00000000 }, { 0x02270100, 0x00000000 }, { 0x02370100, 0x00000000 }, - { 0x01970700, 0x00000020 }, - { 0x00830000, 0x00000097 }, - { 0x00930000, 0x00000097 }, - { 0x01270700, 0x00000000 }, }; static bool rt274_volatile_register(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index ded0ea3324804e..4217467904d6fb 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -77,8 +77,10 @@ static const struct reg_default rt286_reg[] = { { 0x0023a000, 0x0000007f }, { 0x00270500, 0x00000400 }, { 0x00370500, 0x00000400 }, + { 0x00830000, 0x000000c3 }, { 0x00870500, 0x00000400 }, { 0x00920000, 0x00000031 }, + { 0x00930000, 0x000000c3 }, { 0x00935000, 0x000000c3 }, { 0x00936000, 0x000000c3 }, { 0x00970500, 0x00000400 }, @@ -88,16 +90,18 @@ static const struct reg_default rt286_reg[] = { { 0x00c37000, 0x00000000 }, { 0x00c37100, 0x00000080 }, { 0x01270500, 0x00000400 }, + { 0x01270700, 0x00000000 }, { 0x01370500, 0x00000400 }, { 0x01371f00, 0x411111f0 }, { 0x01439000, 0x00000080 }, { 0x0143a000, 0x00000080 }, - { 0x01470700, 0x00000000 }, + { 0x01470100, 0x00000000 }, { 0x01470500, 0x00000400 }, + { 0x01470700, 0x00000000 }, { 0x01470c00, 0x00000000 }, - { 0x01470100, 0x00000000 }, { 0x01837000, 0x00000000 }, { 0x01870500, 0x00000400 }, + { 0x01870700, 0x00000020 }, { 0x02050000, 0x00000000 }, { 0x02139000, 0x00000080 }, { 0x0213a000, 0x00000080 }, @@ -106,10 +110,6 @@ static const struct reg_default rt286_reg[] = { { 0x02170700, 0x00000000 }, { 0x02270100, 0x00000000 }, { 0x02370100, 0x00000000 }, - { 0x01870700, 0x00000020 }, - { 0x00830000, 0x000000c3 }, - { 0x00930000, 0x000000c3 }, - { 0x01270700, 0x00000000 }, }; static bool rt286_volatile_register(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 5414a1712b5715..09aed08b5b7980 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -78,8 +78,10 @@ static const struct reg_default rt298_reg[] = { { 0x0023a000, 0x0000007f }, { 0x00270500, 0x00000400 }, { 0x00370500, 0x00000400 }, + { 0x00830000, 0x000000c3 }, { 0x00870500, 0x00000400 }, { 0x00920000, 0x00000031 }, + { 0x00930000, 0x000000c3 }, { 0x00935000, 0x000000c3 }, { 0x00936000, 0x000000c3 }, { 0x00970500, 0x00000400 }, @@ -89,16 +91,18 @@ static const struct reg_default rt298_reg[] = { { 0x00c37000, 0x00000000 }, { 0x00c37100, 0x00000080 }, { 0x01270500, 0x00000400 }, + { 0x01270700, 0x00000000 }, { 0x01370500, 0x00000400 }, { 0x01371f00, 0x411111f0 }, { 0x01439000, 0x00000080 }, { 0x0143a000, 0x00000080 }, - { 0x01470700, 0x00000000 }, + { 0x01470100, 0x00000000 }, { 0x01470500, 0x00000400 }, + { 0x01470700, 0x00000000 }, { 0x01470c00, 0x00000000 }, - { 0x01470100, 0x00000000 }, { 0x01837000, 0x00000000 }, { 0x01870500, 0x00000400 }, + { 0x01870700, 0x00000020 }, { 0x02050000, 0x00000000 }, { 0x02139000, 0x00000080 }, { 0x0213a000, 0x00000080 }, @@ -107,10 +111,6 @@ static const struct reg_default rt298_reg[] = { { 0x02170700, 0x00000000 }, { 0x02270100, 0x00000000 }, { 0x02370100, 0x00000000 }, - { 0x01870700, 0x00000020 }, - { 0x00830000, 0x000000c3 }, - { 0x00930000, 0x000000c3 }, - { 0x01270700, 0x00000000 }, }; static bool rt298_volatile_register(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/rt700-sdw.h b/sound/soc/codecs/rt700-sdw.h index 4ad0dcfd16fdb7..002f94493b01a2 100644 --- a/sound/soc/codecs/rt700-sdw.h +++ b/sound/soc/codecs/rt700-sdw.h @@ -313,17 +313,16 @@ static const struct reg_default rt700_reg_defaults[] = { { 0x3122, 0x0000 }, { 0x3123, 0x0000 }, { 0x7303, 0x0057 }, - { 0x7303, 0x0057 }, - { 0x8383, 0x0057 }, { 0x7308, 0x0097 }, - { 0x8388, 0x0097 }, { 0x7309, 0x0097 }, - { 0x8389, 0x0097 }, { 0x7312, 0x0000 }, - { 0x8392, 0x0000 }, { 0x7313, 0x0000 }, - { 0x8393, 0x0000 }, { 0x7319, 0x0000 }, + { 0x8383, 0x0057 }, + { 0x8388, 0x0097 }, + { 0x8389, 0x0097 }, + { 0x8392, 0x0000 }, + { 0x8393, 0x0000 }, { 0x8399, 0x0000 }, { 0x75201a, 0x8003 }, { 0x752045, 0x5289 }, diff --git a/sound/soc/codecs/rt711-sdca-sdw.h b/sound/soc/codecs/rt711-sdca-sdw.h index 0d774e473ab940..9aa22b52d556fc 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.h +++ b/sound/soc/codecs/rt711-sdca-sdw.h @@ -58,12 +58,12 @@ static const struct reg_default rt711_sdca_reg_defaults[] = { { 0x2f0f, 0x00 }, { 0x2f50, 0x03 }, { 0x2f5a, 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE28, RT711_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, }; @@ -86,14 +86,14 @@ static const struct reg_default rt711_sdca_mbq_defaults[] = { { 0x610003f, 0xff12 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, }; #endif /* __RT711_SDW_SDCA_H__ */ diff --git a/sound/soc/codecs/rt711-sdw.h b/sound/soc/codecs/rt711-sdw.h index 6acf9858330d78..82da0e62d9d7df 100644 --- a/sound/soc/codecs/rt711-sdw.h +++ b/sound/soc/codecs/rt711-sdw.h @@ -256,16 +256,16 @@ static const struct reg_default rt711_reg_defaults[] = { { 0x3122, 0x00 }, { 0x3123, 0x00 }, { 0x7303, 0x57 }, - { 0x8383, 0x57 }, { 0x7308, 0x97 }, - { 0x8388, 0x97 }, { 0x7309, 0x97 }, - { 0x8389, 0x97 }, { 0x7312, 0x00 }, - { 0x8392, 0x00 }, { 0x7313, 0x00 }, - { 0x8393, 0x00 }, { 0x7319, 0x00 }, + { 0x8383, 0x57 }, + { 0x8388, 0x97 }, + { 0x8389, 0x97 }, + { 0x8392, 0x00 }, + { 0x8393, 0x00 }, { 0x8399, 0x00 }, { 0x752008, 0xa807 }, { 0x752009, 0x1029 }, diff --git a/sound/soc/codecs/rt712-sdca-dmic.h b/sound/soc/codecs/rt712-sdca-dmic.h index 110154e74efed0..2f08e58ed519b9 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.h +++ b/sound/soc/codecs/rt712-sdca-dmic.h @@ -36,6 +36,7 @@ struct rt712_sdca_dmic_kctrl_priv { #define CH_03 0x03 #define CH_04 0x04 +/* must stay sorted by register address, regcache_lookup_reg() does a bsearch() */ static const struct reg_default rt712_sdca_dmic_reg_defaults[] = { { 0x201a, 0x00 }, { 0x201b, 0x00 }, @@ -72,15 +73,16 @@ static const struct reg_default rt712_sdca_dmic_reg_defaults[] = { { 0x2f59, 0x07 }, { 0x3201, 0x01 }, { 0x320c, 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, }; +/* must stay sorted by register address, regcache_lookup_reg() does a bsearch() */ static const struct reg_default rt712_sdca_dmic_mbq_defaults[] = { { 0x0590001e, 0x0020 }, { 0x06100000, 0x0010 }, diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h index 99fd2d67f04dbc..7ad25cc27f624d 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.h +++ b/sound/soc/codecs/rt712-sdca-sdw.h @@ -11,21 +11,21 @@ #include #include +/* must stay sorted by register address, regcache_lookup_reg() does a bsearch() */ static const struct reg_default rt712_sdca_reg_defaults[] = { - - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, @@ -34,6 +34,7 @@ static const struct reg_default rt712_sdca_reg_defaults[] = { { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, }; +/* must stay sorted by register address, regcache_lookup_reg() does a bsearch() */ static const struct reg_default rt712_sdca_mbq_defaults[] = { { 0x2000004, 0xaa01 }, { 0x200000e, 0x21e0 }, diff --git a/sound/soc/codecs/rt715-sdca-sdw.h b/sound/soc/codecs/rt715-sdca-sdw.h index 0cbc14844f8c25..d408f2d7d0f281 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.h +++ b/sound/soc/codecs/rt715-sdca-sdw.h @@ -76,8 +76,6 @@ static const struct reg_default rt715_reg_defaults_sdca[] = { { 0x2f52, 0x01 }, { 0x2f5a, 0x02 }, { 0x2f5b, 0x05 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, - RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, @@ -98,14 +96,12 @@ static const struct reg_default rt715_reg_defaults_sdca[] = { RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, + RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x02 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, }; static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { @@ -149,8 +145,6 @@ static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, @@ -165,6 +159,8 @@ static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, }; diff --git a/sound/soc/codecs/rt715-sdw.h b/sound/soc/codecs/rt715-sdw.h index 5d7661e335ae55..f19fafb913f00b 100644 --- a/sound/soc/codecs/rt715-sdw.h +++ b/sound/soc/codecs/rt715-sdw.h @@ -281,8 +281,8 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x371b, 0x00 }, { 0x371d, 0x00 }, { 0x3729, 0x00 }, - { 0x385e, 0x00 }, { 0x3859, 0x00 }, + { 0x385e, 0x00 }, { 0x4c12, 0x411111f0 }, { 0x4c13, 0x411111f0 }, { 0x4c1d, 0x411111f0 }, @@ -300,36 +300,36 @@ static const struct reg_default rt715_reg_defaults[] = { { 0x4f1d, 0x411111f0 }, { 0x4f29, 0x411111f0 }, { 0x7207, 0x00 }, - { 0x8287, 0x00 }, { 0x7208, 0x00 }, - { 0x8288, 0x00 }, { 0x7209, 0x00 }, - { 0x8289, 0x00 }, { 0x7227, 0x00 }, - { 0x82a7, 0x00 }, { 0x7307, 0x97 }, - { 0x8387, 0x97 }, { 0x7308, 0x97 }, - { 0x8388, 0x97 }, { 0x7309, 0x97 }, - { 0x8389, 0x97 }, { 0x7312, 0x00 }, - { 0x8392, 0x00 }, { 0x7313, 0x00 }, - { 0x8393, 0x00 }, { 0x7318, 0x00 }, - { 0x8398, 0x00 }, { 0x7319, 0x00 }, - { 0x8399, 0x00 }, { 0x731a, 0x00 }, - { 0x839a, 0x00 }, { 0x731b, 0x00 }, - { 0x839b, 0x00 }, { 0x731d, 0x00 }, - { 0x839d, 0x00 }, { 0x7327, 0x97 }, - { 0x83a7, 0x97 }, { 0x7329, 0x00 }, + { 0x8287, 0x00 }, + { 0x8288, 0x00 }, + { 0x8289, 0x00 }, + { 0x82a7, 0x00 }, + { 0x8387, 0x97 }, + { 0x8388, 0x97 }, + { 0x8389, 0x97 }, + { 0x8392, 0x00 }, + { 0x8393, 0x00 }, + { 0x8398, 0x00 }, + { 0x8399, 0x00 }, + { 0x839a, 0x00 }, + { 0x839b, 0x00 }, + { 0x839d, 0x00 }, + { 0x83a7, 0x97 }, { 0x83a9, 0x00 }, { 0x752039, 0xa500 }, }; diff --git a/sound/soc/codecs/rt721-sdca-sdw.h b/sound/soc/codecs/rt721-sdca-sdw.h index 214b31b825833e..13d6227f3392aa 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.h +++ b/sound/soc/codecs/rt721-sdca-sdw.h @@ -29,14 +29,6 @@ static const struct reg_default rt721_sdca_reg_defaults[] = { { 0x2f5b, 0x07 }, { 0x2f5c, 0x27 }, { 0x2f5d, 0x07 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01, - RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11, - RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12, - RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40, - RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, @@ -45,6 +37,14 @@ static const struct reg_default rt721_sdca_reg_defaults[] = { RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, @@ -53,30 +53,30 @@ static const struct reg_default rt721_sdca_reg_defaults[] = { RT721_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A, + RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F, RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26, RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A, - RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31, - RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23, - RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23, RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31, + RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55, RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23, + RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, }; static const struct reg_default rt721_sdca_mbq_defaults[] = { diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 59642673b4cbed..35df1a8c4c8c6d 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -56,8 +56,8 @@ static const struct reg_default sgtl5000_reg_defaults[] = { { SGTL5000_CHIP_PLL_CTRL, 0x5000 }, { SGTL5000_CHIP_CLK_TOP_CTRL, 0x0000 }, { SGTL5000_CHIP_ANA_STATUS, 0x0000 }, - { SGTL5000_CHIP_SHORT_CTRL, 0x0000 }, { SGTL5000_CHIP_ANA_TEST2, 0x0000 }, + { SGTL5000_CHIP_SHORT_CTRL, 0x0000 }, { SGTL5000_DAP_CTRL, 0x0000 }, { SGTL5000_DAP_PEQ, 0x0000 }, { SGTL5000_DAP_BASS_ENHANCE, 0x0040 }, diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c index 4ab15be69f3a43..b7eaf4795d8ba5 100644 --- a/sound/soc/codecs/sti-sas.c +++ b/sound/soc/codecs/sti-sas.c @@ -44,8 +44,8 @@ enum { }; static const struct reg_default stih407_sas_reg_defaults[] = { - { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, { STIH407_AUDIO_GLUE_CTRL, 0x00000040 }, + { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, }; struct sti_dac_audio { diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 1a7650b9b2a7bb..ea083d1cec2b42 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -31,25 +31,25 @@ static const struct reg_default tas2552_reg_defs[] = { {TAS2552_CFG_1, 0x22}, + {TAS2552_CFG_2, 0xef}, {TAS2552_CFG_3, 0x80}, {TAS2552_DOUT, 0x00}, - {TAS2552_OUTPUT_DATA, 0xc0}, - {TAS2552_PDM_CFG, 0x01}, - {TAS2552_PGA_GAIN, 0x00}, - {TAS2552_BOOST_APT_CTRL, 0x0f}, - {TAS2552_RESERVED_0D, 0xbe}, - {TAS2552_LIMIT_RATE_HYS, 0x08}, - {TAS2552_CFG_2, 0xef}, {TAS2552_SER_CTRL_1, 0x00}, {TAS2552_SER_CTRL_2, 0x00}, + {TAS2552_OUTPUT_DATA, 0xc0}, {TAS2552_PLL_CTRL_1, 0x10}, {TAS2552_PLL_CTRL_2, 0x00}, {TAS2552_PLL_CTRL_3, 0x00}, {TAS2552_BTIP, 0x8f}, {TAS2552_BTS_CTRL, 0x80}, + {TAS2552_RESERVED_0D, 0xbe}, + {TAS2552_LIMIT_RATE_HYS, 0x08}, {TAS2552_LIMIT_RELEASE, 0x04}, {TAS2552_LIMIT_INT_COUNT, 0x00}, + {TAS2552_PDM_CFG, 0x01}, + {TAS2552_PGA_GAIN, 0x00}, {TAS2552_EDGE_RATE_CTRL, 0x40}, + {TAS2552_BOOST_APT_CTRL, 0x0f}, {TAS2552_VBAT_DATA, 0x00}, }; diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index b11b998aa32a6c..f9c65c9f6d0a4e 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -897,13 +897,13 @@ static const struct reg_default tas2764_reg_defaults[] = { { TAS2764_PAGE, 0x00 }, { TAS2764_SW_RST, 0x00 }, { TAS2764_PWR_CTRL, 0x1a }, - { TAS2764_DVC, 0x00 }, { TAS2764_CHNL_0, 0x28 }, { TAS2764_TDM_CFG0, 0x09 }, { TAS2764_TDM_CFG1, 0x02 }, { TAS2764_TDM_CFG2, 0x0a }, { TAS2764_TDM_CFG3, 0x10 }, { TAS2764_TDM_CFG5, 0x42 }, + { TAS2764_DVC, 0x00 }, { TAS2764_INT_CLK_CFG, 0x19 }, }; diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c index 1ec1c076204fb6..f1fce4305fa11f 100644 --- a/sound/soc/codecs/tas2780.c +++ b/sound/soc/codecs/tas2780.c @@ -527,13 +527,13 @@ static const struct reg_default tas2780_reg_defaults[] = { { TAS2780_PAGE, 0x00 }, { TAS2780_SW_RST, 0x00 }, { TAS2780_PWR_CTRL, 0x1a }, - { TAS2780_DVC, 0x00 }, { TAS2780_CHNL_0, 0x00 }, { TAS2780_TDM_CFG0, 0x09 }, { TAS2780_TDM_CFG1, 0x02 }, { TAS2780_TDM_CFG2, 0x0a }, { TAS2780_TDM_CFG3, 0x10 }, { TAS2780_TDM_CFG5, 0x42 }, + { TAS2780_DVC, 0x00 }, }; static const struct regmap_range_cfg tas2780_regmap_ranges[] = { diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 3d0b116544cc48..e5d27266370e21 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -106,7 +106,6 @@ struct tas2783_prv { static const struct reg_default tas2783_reg_default[] = { {TAS2783_AMP_LEVEL, 0x28}, - {TASDEV_REG_SDW(0, 0, 0x03), 0x28}, {TASDEV_REG_SDW(0, 0, 0x04), 0x21}, {TASDEV_REG_SDW(0, 0, 0x05), 0x41}, {TASDEV_REG_SDW(0, 0, 0x06), 0x00}, @@ -142,6 +141,7 @@ static const struct reg_default tas2783_reg_default[] = { {TASDEV_REG_SDW(0, 0, 0x41), 0x14}, {TASDEV_REG_SDW(0, 0, 0x5c), 0x19}, {TASDEV_REG_SDW(0, 0, 0x5d), 0x80}, + {TASDEV_REG_SDW(0, 0, 0x60), 0x21}, {TASDEV_REG_SDW(0, 0, 0x63), 0x48}, {TASDEV_REG_SDW(0, 0, 0x65), 0x08}, {TASDEV_REG_SDW(0, 0, 0x66), 0xb2}, @@ -157,7 +157,6 @@ static const struct reg_default tas2783_reg_default[] = { {TASDEV_REG_SDW(0, 0, 0x73), 0x08}, {TASDEV_REG_SDW(0, 0, 0x75), 0xe0}, {TASDEV_REG_SDW(0, 0, 0x7a), 0x60}, - {TASDEV_REG_SDW(0, 0, 0x60), 0x21}, {TASDEV_REG_SDW(0, 1, 0x02), 0x00}, {TASDEV_REG_SDW(0, 1, 0x17), 0xc0}, {TASDEV_REG_SDW(0, 1, 0x19), 0x60}, @@ -176,63 +175,44 @@ static const struct reg_default tas2783_reg_default[] = { {TASDEV_REG_SDW(0, 0xfd, 0x39), 0x00}, {TASDEV_REG_SDW(0, 0xfd, 0x3e), 0x00}, {TASDEV_REG_SDW(0, 0xfd, 0x45), 0x00}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x02, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x10, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, 0x01, 1), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, 0x02, 1), 0x9c00}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 0), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 1), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x0b, 1), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 1), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 1), 0x1}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x0b, 1), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 1), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 2), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 1), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 2), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x01, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x06, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x07, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x09, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x0a, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x1, 0), 0x3}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x05, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x06, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x06, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x04, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x08, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x04, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x11, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x04, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x01, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x05, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x01, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x05, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x08, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 1), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 2), 0x0}, @@ -242,19 +222,60 @@ static const struct reg_default tas2783_reg_default[] = { {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 6), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 7), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x06, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 1), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 2), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 1), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 2), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x02, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x01, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x05, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x01, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x04, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x05, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x08, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x10, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x13, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x14, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x15, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x16, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x10, 0), 0x3}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x10, 0), 0x1}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x13, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x13, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_TG23, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x11, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x08, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x08, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x04, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x08, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x11, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 1), 0x0}, @@ -264,6 +285,14 @@ static const struct reg_default tas2783_reg_default[] = { {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 5), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 6), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 7), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x12, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x10, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x11, 0), 0x0}, + {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x12, 0), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 8), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 9), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xa), 0x0}, @@ -272,36 +301,6 @@ static const struct reg_default tas2783_reg_default[] = { {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xd), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xe), 0x0}, {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xf), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x1, 0), 0x3}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x10, 0), 0x3}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x06, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x13, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x06, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x13, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x05, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x10, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x11, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_TG23, 0x10, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x01, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x06, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x07, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x08, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x09, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x0a, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x10, 0), 0x1}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x12, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x13, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x14, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x15, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x16, 0), 0x0}, - {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23, 0x10, 0), 0x0}, }; static const struct reg_sequence tas2783_init_seq[] = { diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index 6c6b497657d0c8..420538d4fae6d2 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +29,8 @@ #include #include +#include "lpass-wsa-macro.h" + #define WSA884X_BASE 0x3000 #define WSA884X_ANA_BG_TSADC_BASE (WSA884X_BASE + 0x0001) #define WSA884X_BG_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x00) @@ -155,6 +159,7 @@ #define WSA884X_BBM_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x28) #define WSA884X_TOP_MISC1 (WSA884X_ANA_SPK_TOP_BASE + 0x29) #define WSA884X_DAC_VCM_CTRL_REG7 (WSA884X_ANA_SPK_TOP_BASE + 0x2a) +#define WSA884X_DAC_VCM_CTRL_REG7_FINAL_OVERRIDE_MASK 0x02 #define WSA884X_TOP_BIAS_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x2b) #define WSA884X_DRV_LF_MISC_CTL2 (WSA884X_ANA_SPK_TOP_BASE + 0x2c) #define WSA884X_SPK_TOP_SPARE_TUNE_2 (WSA884X_ANA_SPK_TOP_BASE + 0x2d) @@ -297,6 +302,9 @@ #define WSA884X_PA_FSM_TIMER1 (WSA884X_DIG_CTRL0_BASE + 0x34) #define WSA884X_PA_FSM_STA0 (WSA884X_DIG_CTRL0_BASE + 0x35) #define WSA884X_PA_FSM_STA1 (WSA884X_DIG_CTRL0_BASE + 0x36) +#define WSA884X_PA_FSM_RESET_MASK BIT(4) +#define WSA884X_PA_ERROR_MASK GENMASK(4, 0) +#define WSA884X_PA_RECOVERY_RETRIES 3 #define WSA884X_PA_FSM_ERR_CTL (WSA884X_DIG_CTRL0_BASE + 0x37) #define WSA884X_PA_FSM_ERR_COND0 (WSA884X_DIG_CTRL0_BASE + 0x38) #define WSA884X_PA_FSM_ERR_COND1 (WSA884X_DIG_CTRL0_BASE + 0x39) @@ -437,7 +445,9 @@ #define WSA884X_CLSH_CTL_0_INPUT_EN_SHIFT 1 #define WSA884X_CLSH_CTL_0_CLSH_EN_SHIFT 0 #define WSA884X_CLSH_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xd1) +#define WSA884X_CLSH_CTL_1_SLR_MAX_MASK GENMASK(7, 4) #define WSA884X_CLSH_V_HD_PA (WSA884X_DIG_CTRL0_BASE + 0xd2) +#define WSA884X_CLSH_V_HD_PA_MASK GENMASK(4, 0) #define WSA884X_CLSH_V_PA_MIN (WSA884X_DIG_CTRL0_BASE + 0xd3) #define WSA884X_CLSH_OVRD_VAL (WSA884X_DIG_CTRL0_BASE + 0xd4) #define WSA884X_CLSH_HARD_MAX (WSA884X_DIG_CTRL0_BASE + 0xd5) @@ -731,8 +741,11 @@ struct wsa884x_priv { struct regulator_bulk_data supplies[WSA884X_SUPPLIES_NUM]; struct sdw_slave *slave; struct sdw_stream_config sconfig; - struct sdw_stream_runtime *sruntime; + struct sdw_stream_runtime *sruntime[3]; struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS]; + u8 cps_offset1; + u8 visense_channel_mask; + bool protected_feedback; struct gpio_desc *sd_n; struct reset_control *sd_reset; bool port_prepared[WSA884X_MAX_SWR_PORTS]; @@ -747,6 +760,16 @@ struct wsa884x_priv { struct mutex sp_lock; unsigned int temperature; bool pa_on; + unsigned int supply_config; + unsigned int speaker_load_ohms; + bool protection_pa_reported; +}; + +enum wsa884x_supply_config { + WSA884X_SUPPLY_EXT_ABOVE_3S, + WSA884X_SUPPLY_1S, + WSA884X_SUPPLY_2S, + WSA884X_SUPPLY_3S, }; enum { @@ -872,6 +895,29 @@ static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = { } }; +static struct sdw_dpn_prop wsa884x_source_dpn_prop[] = { + { + .num = WSA884X_PORT_VISENSE + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, + { + .num = WSA884X_PORT_CPS + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 2, + .simple_ch_prep_sm = true, + .read_only_wordlength = false, + .simple_transport_registers = + SDW_DPN_SIMPLE_TRANSPORT_BLOCKCTRL3 | + SDW_DPN_SIMPLE_TRANSPORT_SAMPLECTRL2 | + SDW_DPN_SIMPLE_TRANSPORT_HCTRL, + }, +}; + static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = { [WSA884X_PORT_DAC] = { .num = WSA884X_PORT_DAC + 1, @@ -1457,11 +1503,26 @@ static const struct reg_sequence wsa884x_reg_init[] = { { WSA884X_OTP_REG_40, FIELD_PREP_CONST(WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK, 0x8) }, }; +static bool wsa884x_uses_2s_4ohm_profile(struct wsa884x_priv *wsa884x) +{ + return wsa884x->speaker_load_ohms == 4 && + wsa884x->supply_config == WSA884X_SUPPLY_2S; +} + static void wsa884x_set_gain_parameters(struct wsa884x_priv *wsa884x) { struct regmap *regmap = wsa884x->regmap; unsigned int min_gain, igain, vgain, comp_offset; + if (wsa884x_uses_2s_4ohm_profile(wsa884x) && + wsa884x->dev_mode == WSA884X_SPEAKER) { + comp_offset = COMP_OFFSET0; + min_gain = G_0_DB; + igain = ISENSE_6_DB; + vgain = VSENSE_M21_DB; + goto apply; + } + /* * Downstream sets gain parameters customized per boards per use-case. * Choose here some sane values matching knowon users, like QRD8550 @@ -1484,6 +1545,7 @@ static void wsa884x_set_gain_parameters(struct wsa884x_priv *wsa884x) vgain = VSENSE_M24_DB; } +apply: regmap_update_bits(regmap, WSA884X_ISENSE2, WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK, FIELD_PREP(WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK, igain)); @@ -1509,18 +1571,239 @@ static void wsa884x_set_gain_parameters(struct wsa884x_priv *wsa884x) } } +static void wsa884x_apply_2s_4ohm_profile(struct wsa884x_priv *wsa884x) +{ + static const struct reg_sequence pbr_thresholds[] = { + { WSA884X_CLSH_VTH1, WSA884X_VTH_TO_REG(808) }, + { WSA884X_CLSH_VTH2, WSA884X_VTH_TO_REG(839) }, + { WSA884X_CLSH_VTH3, WSA884X_VTH_TO_REG(894) }, + { WSA884X_CLSH_VTH4, WSA884X_VTH_TO_REG(925) }, + { WSA884X_CLSH_VTH5, WSA884X_VTH_TO_REG(973) }, + { WSA884X_CLSH_VTH6, WSA884X_VTH_TO_REG(996) }, + { WSA884X_CLSH_VTH7, WSA884X_VTH_TO_REG(1051) }, + { WSA884X_CLSH_VTH8, WSA884X_VTH_TO_REG(1114) }, + { WSA884X_CLSH_VTH9, WSA884X_VTH_TO_REG(1184) }, + { WSA884X_CLSH_VTH10, WSA884X_VTH_TO_REG(1255) }, + { WSA884X_CLSH_VTH11, WSA884X_VTH_TO_REG(1318) }, + { WSA884X_CLSH_VTH12, WSA884X_VTH_TO_REG(1467) }, + { WSA884X_CLSH_VTH13, WSA884X_VTH_TO_REG(1616) }, + { WSA884X_CLSH_VTH14, WSA884X_VTH_TO_REG(1788) }, + { WSA884X_CLSH_VTH15, WSA884X_VTH_TO_REG(2000) }, + }; + struct regmap *regmap = wsa884x->regmap; + + if (wsa884x->speaker_load_ohms != 4) + return; + + if (!wsa884x_uses_2s_4ohm_profile(wsa884x)) { + /* + * A SoundWire detach leaves the cached profile values behind. Put + * every fixed 2S override back on the upstream defaults when a + * subsequent status read does not confirm 2S. wsa884x_reg_init + * restores the PBR thresholds before this function is called. + */ + regmap_write(regmap, WSA884X_OCP_CTL, 0xc6); + regmap_update_bits(regmap, WSA884X_CLSH_CTL_1, + WSA884X_CLSH_CTL_1_SLR_MAX_MASK, + FIELD_PREP(WSA884X_CLSH_CTL_1_SLR_MAX_MASK, 0x8)); + regmap_update_bits(regmap, WSA884X_CLSH_V_HD_PA, + WSA884X_CLSH_V_HD_PA_MASK, + FIELD_PREP(WSA884X_CLSH_V_HD_PA_MASK, 0xc)); + regmap_write(regmap, WSA884X_DAC_VCM_CTRL_REG2, 0x00); + regmap_write(regmap, WSA884X_DAC_VCM_CTRL_REG3, 0x00); + regmap_write(regmap, WSA884X_DAC_VCM_CTRL_REG4, 0x00); + regmap_write(regmap, WSA884X_DAC_VCM_CTRL_REG5, 0x00); + regmap_write(regmap, WSA884X_DAC_VCM_CTRL_REG6, 0x00); + regmap_update_bits(regmap, WSA884X_DAC_VCM_CTRL_REG7, + WSA884X_DAC_VCM_CTRL_REG7_FINAL_OVERRIDE_MASK, 0); + regmap_write(regmap, WSA884X_UVLO_PROG, 0x99); + regmap_write(regmap, WSA884X_PA_FSM_TIMER0, 0x80); + regmap_write(regmap, WSA884X_UVLO_DEGLITCH_CTL, 0x05); + regmap_write(regmap, WSA884X_UVLO_PROG1, 0x70); + regmap_update_bits(regmap, WSA884X_TOP_CTRL1, + WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK, + WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK); + return; + } + + regmap_write(regmap, WSA884X_OCP_CTL, 0xf6); + regmap_multi_reg_write(regmap, pbr_thresholds, + ARRAY_SIZE(pbr_thresholds)); + regmap_update_bits(regmap, WSA884X_CLSH_CTL_1, + WSA884X_CLSH_CTL_1_SLR_MAX_MASK, + FIELD_PREP(WSA884X_CLSH_CTL_1_SLR_MAX_MASK, 0x2)); + regmap_update_bits(regmap, WSA884X_CLSH_V_HD_PA, + WSA884X_CLSH_V_HD_PA_MASK, + FIELD_PREP(WSA884X_CLSH_V_HD_PA_MASK, 0x13)); + regmap_write(regmap, WSA884X_DAC_VCM_CTRL_REG2, 0x06); + regmap_write(regmap, WSA884X_DAC_VCM_CTRL_REG3, 0x14); + regmap_write(regmap, WSA884X_DAC_VCM_CTRL_REG4, 0x19); + regmap_write(regmap, WSA884X_DAC_VCM_CTRL_REG5, 0x1b); + regmap_write(regmap, WSA884X_DAC_VCM_CTRL_REG6, 0x1c); + regmap_update_bits(regmap, WSA884X_DAC_VCM_CTRL_REG7, + WSA884X_DAC_VCM_CTRL_REG7_FINAL_OVERRIDE_MASK, + WSA884X_DAC_VCM_CTRL_REG7_FINAL_OVERRIDE_MASK); + regmap_write(regmap, WSA884X_UVLO_PROG, 0x77); + regmap_write(regmap, WSA884X_PA_FSM_TIMER0, 0xc0); + regmap_write(regmap, WSA884X_UVLO_DEGLITCH_CTL, 0x1d); + regmap_write(regmap, WSA884X_UVLO_PROG1, 0x40); + regmap_update_bits(regmap, WSA884X_TOP_CTRL1, + WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK, 0); +} + +/* + * Shipping Denali firmware programs this ordered WSA8845 transaction once + * after SoundWire attachment. Ordinary playback uses the shorter start and + * stop transactions below. The write order is part of the observed hardware + * contract and therefore must not be reduced to final-value overrides. + */ +static const struct reg_sequence denali_wsa8845_cold_init[] = { + { WSA884X_PWRSTG_DBG, 0x0c }, + { WSA884X_PDRV_HS_CTL, 0x5a }, + { WSA884X_SWR_RESET_EN, 0x07 }, + { WSA884X_INTR_MASK0, 0xff }, + { WSA884X_INTR_MASK1, 0x03 }, + { WSA884X_PA_FSM_BYP_CTL, 0x00 }, + { WSA884X_VBAT_SNS, 0x20 }, + { WSA884X_CKWD_CTL_1, 0x1b }, + { WSA884X_CDC_SPK_DSM_A2_0, 0x0a }, + { WSA884X_CDC_SPK_DSM_A2_1, 0x08 }, + { WSA884X_CDC_SPK_DSM_A3_0, 0xf3 }, + { WSA884X_CDC_SPK_DSM_A3_1, 0x07 }, + { WSA884X_CDC_SPK_DSM_A4_0, 0x79 }, + { WSA884X_CDC_SPK_DSM_A4_1, 0x02 }, + { WSA884X_CDC_SPK_DSM_A5_0, 0x0b }, + { WSA884X_CDC_SPK_DSM_A5_1, 0x02 }, + { WSA884X_CDC_SPK_DSM_A6_0, 0x8a }, + { WSA884X_CDC_SPK_DSM_A7_0, 0x9b }, + { WSA884X_CDC_SPK_DSM_C_0, 0x68 }, + { WSA884X_CDC_SPK_DSM_C_2, 0xf2 }, + { WSA884X_CDC_SPK_DSM_C_3, 0x20 }, + { WSA884X_CDC_SPK_DSM_R1, 0x83 }, + { WSA884X_CDC_SPK_DSM_R2, 0x7f }, + { WSA884X_CDC_SPK_DSM_R3, 0x9d }, + { WSA884X_CDC_SPK_DSM_R4, 0x82 }, + { WSA884X_CDC_SPK_DSM_R5, 0x8b }, + { WSA884X_CDC_SPK_DSM_R6, 0x9b }, + { WSA884X_CDC_SPK_DSM_R7, 0x3f }, + { WSA884X_DRE_IDLE_DET_CTL, 0x0f }, + { WSA884X_PDM_WD_CTL, 0x01 }, + { WSA884X_DRE_CTL_0, 0xf0 }, + { WSA884X_DRE_CTL_1, 0x00 }, + { WSA884X_BOP_DEGLITCH_CTL, 0x11 }, + { WSA884X_VBAT_CAL_CTL, 0x05 }, + { WSA884X_VBAT_THRM_FLT_CTL, 0x79 }, + { WSA884X_GAIN_RAMPING_MIN, 0x0e }, + { WSA884X_CLSH_CTL_0, 0x00 }, + { WSA884X_CLSH_V_HD_PA, 0x13 }, + { WSA884X_CLSH_SOFT_MAX, 0xff }, + { WSA884X_ANA_WO_CTL_0, 0x9d }, + { WSA884X_ANA_WO_CTL_1, 0x00 }, + { WSA884X_OTP_REG_38, 0x08 }, + { WSA884X_OTP_REG_40, 0x20 }, + { WSA884X_REF_CTRL, 0xd1 }, + { WSA884X_UVLO_PROG, 0x33 }, + { WSA884X_UVLO_PROG1, 0x60 }, + { WSA884X_BOP1_PROG, 0x22 }, + { WSA884X_BOP2_PROG, 0x44 }, + { WSA884X_ZX_CTRL1, 0xf8 }, + { WSA884X_STB_CTRL1, 0x6a }, + { WSA884X_ILIM_CTRL1, 0xe3 }, + { WSA884X_CURRENT_LIMIT, 0xd4 }, + { WSA884X_TOP_CTRL1, 0xd2 }, + { WSA884X_OCP_CTL, 0xf6 }, + { WSA884X_DAC_VCM_CTRL_REG2, 0x06 }, + { WSA884X_DAC_VCM_CTRL_REG3, 0x14 }, + { WSA884X_DAC_VCM_CTRL_REG4, 0x19 }, + { WSA884X_DAC_VCM_CTRL_REG5, 0x1b }, + { WSA884X_DAC_VCM_CTRL_REG6, 0x1c }, + { WSA884X_DAC_VCM_CTRL_REG7, 0x02 }, + { WSA884X_CKWD_CTL_1, 0x13 }, + { WSA884X_PWRSTAGE_CTRL2, 0xf1 }, + { WSA884X_CURRENT_LIMIT, 0x44 }, +}; + +static const struct reg_sequence denali_wsa8845_start[] = { + { WSA884X_ISENSE2, 0x07 }, + { WSA884X_VSENSE1, 0x67 }, + { WSA884X_INTR_MASK0, 0x90 }, + { WSA884X_INTR_MASK1, 0x00 }, + { WSA884X_CLSH_CTL_0, 0x67 }, + { WSA884X_PWRSTG_DBG, 0x08 }, + { WSA884X_PDRV_HS_CTL, 0x52 }, + { WSA884X_PA_FSM_EN, 0x01 }, + { WSA884X_PWRSTG_DBG, 0x0c }, + { WSA884X_PDRV_HS_CTL, 0x5a }, +}; + +static const struct reg_sequence denali_wsa8845_stop[] = { + { WSA884X_PA_FSM_EN, 0x00 }, + { WSA884X_INTR_MASK0, 0xff }, + { WSA884X_INTR_MASK1, 0x03 }, + { WSA884X_INTR_CLEAR0, 0xff }, + { WSA884X_INTR_CLEAR1, 0x03 }, + { WSA884X_CLSH_CTL_0, 0x00 }, +}; + +static bool wsa884x_uses_denali_protected_profile(struct wsa884x_priv *wsa884x) +{ + return of_machine_is_compatible("microsoft,denali") && + wsa884x->protected_feedback && + wsa884x_uses_2s_4ohm_profile(wsa884x); +} + +static void wsa884x_write_sequence(struct wsa884x_priv *wsa884x, + const struct reg_sequence *sequence, + size_t count) +{ + size_t i; + + for (i = 0; i < count; i++) + regmap_write(wsa884x->regmap, sequence[i].reg, sequence[i].def); +} + static void wsa884x_init(struct wsa884x_priv *wsa884x) { unsigned int wo_ctl_0; + unsigned int supply_config; unsigned int variant = 0; if (!regmap_read(wsa884x->regmap, WSA884X_OTP_REG_0, &variant)) variant = variant & WSA884X_OTP_REG_0_ID_MASK; + /* Never reuse a prior attach's supply status if this read fails. */ + wsa884x->supply_config = WSA884X_SUPPLY_1S; + if (wsa884x->speaker_load_ohms == 4) { + if (regmap_read(wsa884x->regmap, WSA884X_VPHX_SYS_EN_STATUS, + &supply_config)) { + dev_warn(wsa884x->dev, + "cannot read VPHX supply configuration; retaining defaults\n"); + } else { + wsa884x->supply_config = supply_config; + if (supply_config == WSA884X_SUPPLY_2S) + dev_dbg(wsa884x->dev, + "selected 2S 4-ohm speaker profile\n"); + else + dev_warn(wsa884x->dev, + "4-ohm profile requires 2S VPHX; retaining defaults\n"); + } + } + + if (wsa884x_uses_denali_protected_profile(wsa884x)) { + wsa884x_write_sequence(wsa884x, denali_wsa8845_cold_init, + ARRAY_SIZE(denali_wsa8845_cold_init)); + wsa884x->hw_init = true; + return; + } + regmap_multi_reg_write(wsa884x->regmap, wsa884x_reg_init, ARRAY_SIZE(wsa884x_reg_init)); + wsa884x_apply_2s_4ohm_profile(wsa884x); wo_ctl_0 = 0xc; + if (wsa884x_uses_2s_4ohm_profile(wsa884x)) + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_VPHX_SYS_EN_MASK, + WSA884X_SUPPLY_2S); wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK, WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER); /* Assume that compander is enabled by default unless it is haptics sku */ @@ -1537,12 +1820,100 @@ static void wsa884x_init(struct wsa884x_priv *wsa884x) wsa884x->hw_init = true; } +static void wsa884x_reset_pa_fsm(struct wsa884x_priv *wsa884x) +{ + regmap_update_bits(wsa884x->regmap, WSA884X_PA_FSM_CTL0, + WSA884X_PA_FSM_RESET_MASK, 0); + regmap_update_bits(wsa884x->regmap, WSA884X_PA_FSM_CTL0, + WSA884X_PA_FSM_RESET_MASK, + WSA884X_PA_FSM_RESET_MASK); + regmap_update_bits(wsa884x->regmap, WSA884X_PA_FSM_CTL0, + WSA884X_PA_FSM_RESET_MASK, 0); +} + +/* Caller serializes this sequence with sp_lock. */ +static int wsa884x_recover_pa(struct wsa884x_priv *wsa884x, bool reenable) +{ + unsigned int sta0 = 0, sta1 = 0, err0 = 0, err1 = 0; + int ret = -EIO; + int retry; + + regmap_update_bits(wsa884x->regmap, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, 0); + regmap_read(wsa884x->regmap, WSA884X_PA_FSM_STA0, &sta0); + regmap_read(wsa884x->regmap, WSA884X_PA_FSM_STA1, &sta1); + regmap_read(wsa884x->regmap, WSA884X_PA_FSM_ERR_COND0, &err0); + regmap_read(wsa884x->regmap, WSA884X_PA_FSM_ERR_COND1, &err1); + + dev_warn_ratelimited(wsa884x->dev, + "PA fault: sta0=%#02x sta1=%#02x err0=%#02x err1=%#02x; resetting FSM\n", + sta0, sta1, err0, err1); + + wsa884x_reset_pa_fsm(wsa884x); + regmap_write(wsa884x->regmap, WSA884X_INTR_CLEAR1, BIT(1)); + if (!reenable) + return 0; + + for (retry = 0; retry < WSA884X_PA_RECOVERY_RETRIES; retry++) { + regmap_update_bits(wsa884x->regmap, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK); + usleep_range(1000, 1100); + + ret = regmap_read(wsa884x->regmap, WSA884X_PA_FSM_STA1, &sta1); + if (!ret && !(sta1 & WSA884X_PA_ERROR_MASK)) { + regmap_write(wsa884x->regmap, WSA884X_INTR_CLEAR1, BIT(1)); + dev_info(wsa884x->dev, + "PA state machine recovered after %d attempt(s)\n", + retry + 1); + return 0; + } + + regmap_update_bits(wsa884x->regmap, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, 0); + wsa884x_reset_pa_fsm(wsa884x); + } + + regmap_read(wsa884x->regmap, WSA884X_PA_FSM_ERR_COND0, &err0); + regmap_read(wsa884x->regmap, WSA884X_PA_FSM_ERR_COND1, &err1); + dev_err_ratelimited(wsa884x->dev, + "PA recovery failed: sta1=%#02x err0=%#02x err1=%#02x; amplifier left disabled\n", + sta1, err0, err1); + + return ret ?: -EIO; +} + static int wsa884x_update_status(struct sdw_slave *slave, enum sdw_slave_status status) { struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev); int ret; + /* + * Denali keeps the amplifier supplies and reset asserted while its + * simple-clock-stop-capable SoundWire link transiently detaches. Retain + * the one-time cold state and sync only writes queued while cache-only. + */ + if (wsa884x_uses_denali_protected_profile(wsa884x) && + wsa884x->hw_init) { + if (status == SDW_SLAVE_UNATTACHED) { + regcache_cache_only(wsa884x->regmap, true); + return 0; + } + + if (status == SDW_SLAVE_ATTACHED) { + regcache_cache_only(wsa884x->regmap, false); + ret = regcache_sync(wsa884x->regmap); + if (ret < 0) { + dev_err(&slave->dev, + "Cannot sync retained regmap cache\n"); + return ret; + } + + return 0; + } + } + if (status == SDW_SLAVE_UNATTACHED) { wsa884x->hw_init = false; regcache_cache_only(wsa884x->regmap, true); @@ -1654,11 +2025,24 @@ static int wsa884x_codec_probe(struct snd_soc_component *comp) return 0; } +static void wsa884x_codec_remove(struct snd_soc_component *comp) +{ + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + + if (wsa884x->protection_pa_reported) { + wsa_macro_protection_pa_event(comp, false); + wsa884x->protection_pa_reported = false; + } +} + static void wsa884x_spkr_post_pmu(struct snd_soc_component *component, struct wsa884x_priv *wsa884x) { unsigned int curr_limit, curr_ovrd_en; + if (wsa884x_uses_denali_protected_profile(wsa884x)) + return; + wsa884x_set_gain_parameters(wsa884x); if (wsa884x->dev_mode == WSA884X_RECEIVER) { snd_soc_component_write_field(component, WSA884X_DRE_CTL_0, @@ -1677,7 +2061,8 @@ static void wsa884x_spkr_post_pmu(struct snd_soc_component *component, if (wsa884x->port_enable[WSA884X_PORT_PBR]) { curr_ovrd_en = 0x0; - curr_limit = 0x15; + curr_limit = wsa884x_uses_2s_4ohm_profile(wsa884x) ? + 0x11 : 0x15; } else { curr_ovrd_en = 0x1; if (wsa884x->dev_mode == WSA884X_RECEIVER) @@ -1707,15 +2092,17 @@ static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, wsa884x_spkr_post_pmu(component, wsa884x); - snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL, - WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, - 0x1); + if (!wsa884x_uses_denali_protected_profile(wsa884x)) + snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL, + WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, + 0x1); break; case SND_SOC_DAPM_PRE_PMD: - snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL, - WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, - 0x0); + if (!wsa884x_uses_denali_protected_profile(wsa884x)) + snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL, + WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, + 0x0); mutex_lock(&wsa884x->sp_lock); wsa884x->pa_on = false; @@ -1760,6 +2147,7 @@ static const struct snd_soc_dapm_route wsa884x_audio_map[] = { static const struct snd_soc_component_driver wsa884x_component_drv = { .name = "WSA884x", .probe = wsa884x_codec_probe, + .remove = wsa884x_codec_remove, .controls = wsa884x_snd_controls, .num_controls = ARRAY_SIZE(wsa884x_snd_controls), .dapm_widgets = wsa884x_dapm_widgets, @@ -1773,22 +2161,77 @@ static int wsa884x_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS]; + struct sdw_stream_config sconfig = { + .ch_count = 1, + .bps = 1, + .type = SDW_STREAM_PDM, + }; + int active_ports = 0; int i; - wsa884x->active_ports = 0; + if (dai->id && !wsa884x_uses_2s_4ohm_profile(wsa884x)) + return -ENODEV; + + if (!wsa884x->protected_feedback) { + wsa884x->active_ports = 0; + for (i = 0; i < WSA884X_MAX_SWR_PORTS; i++) { + if (!wsa884x->port_enable[i]) + continue; + + wsa884x->port_config[wsa884x->active_ports] = + wsa884x_pconfig[i]; + wsa884x->active_ports++; + } + + wsa884x->sconfig.frame_rate = params_rate(params); + + return sdw_stream_add_slave(wsa884x->slave, &wsa884x->sconfig, + wsa884x->port_config, + wsa884x->active_ports, + wsa884x->sruntime[0]); + } + for (i = 0; i < WSA884X_MAX_SWR_PORTS; i++) { if (!wsa884x->port_enable[i]) continue; - wsa884x->port_config[wsa884x->active_ports] = wsa884x_pconfig[i]; - wsa884x->active_ports++; + if (dai->id == 0) { + if (i != WSA884X_PORT_DAC && + i != WSA884X_PORT_COMP && + i != WSA884X_PORT_BOOST) + continue; + } else if (dai->id == 1) { + if (i != WSA884X_PORT_VISENSE) + continue; + } else if (dai->id == 2) { + if (i != WSA884X_PORT_CPS) + continue; + } else { + continue; + } + + port_config[active_ports] = wsa884x_pconfig[i]; + if (i == WSA884X_PORT_VISENSE) + port_config[active_ports].ch_mask = + wsa884x->visense_channel_mask; + if (i == WSA884X_PORT_CPS) { + port_config[active_ports].transport_params_override_mask = + SDW_PORT_CONFIG_OVERRIDE_OFFSET1; + port_config[active_ports].transport_params_override.offset1 = + wsa884x->cps_offset1; + } + active_ports++; } - wsa884x->sconfig.frame_rate = params_rate(params); + if (!active_ports) + return -ENODEV; - return sdw_stream_add_slave(wsa884x->slave, &wsa884x->sconfig, - wsa884x->port_config, wsa884x->active_ports, - wsa884x->sruntime); + sconfig.frame_rate = params_rate(params); + sconfig.direction = dai->id == 0 ? SDW_DATA_DIR_RX : SDW_DATA_DIR_TX; + + return sdw_stream_add_slave(wsa884x->slave, &sconfig, port_config, + active_ports, wsa884x->sruntime[dai->id]); } static int wsa884x_hw_free(struct snd_pcm_substream *substream, @@ -1796,30 +2239,132 @@ static int wsa884x_hw_free(struct snd_pcm_substream *substream, { struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); - sdw_stream_remove_slave(wsa884x->slave, wsa884x->sruntime); + sdw_stream_remove_slave(wsa884x->slave, wsa884x->sruntime[dai->id]); return 0; } +static void wsa884x_disable_pa(struct snd_soc_component *component) +{ + snd_soc_component_write_field(component, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x0); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, 0x0); +} + static int wsa884x_mute_stream(struct snd_soc_dai *dai, int mute, int stream) { struct snd_soc_component *component = dai->component; + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + unsigned int pa_status = 0; + int ret; + + if (dai->id != 0) + return 0; + + guard(mutex)(&wsa884x->sp_lock); + + if (wsa884x_uses_denali_protected_profile(wsa884x)) { + if (mute) { + if (wsa884x->protection_pa_reported) { + wsa_macro_protection_pa_event(component, false); + wsa884x->protection_pa_reported = false; + } + + wsa884x_write_sequence(wsa884x, denali_wsa8845_stop, + ARRAY_SIZE(denali_wsa8845_stop)); + return 0; + } + + /* Recovery remains a contingency outside the clean start trace. */ + ret = regmap_read(wsa884x->regmap, WSA884X_PA_FSM_STA1, + &pa_status); + if (!ret && (pa_status & WSA884X_PA_ERROR_MASK)) + wsa884x_recover_pa(wsa884x, false); + + wsa884x_write_sequence(wsa884x, denali_wsa8845_start, + ARRAY_SIZE(denali_wsa8845_start)); + usleep_range(1000, 1100); + + ret = regmap_read(wsa884x->regmap, WSA884X_PA_FSM_STA1, + &pa_status); + if (ret) { + wsa884x_disable_pa(component); + dev_err(wsa884x->dev, + "cannot read protected PA status: %pe\n", + ERR_PTR(ret)); + return ret; + } + + if (pa_status & WSA884X_PA_ERROR_MASK) { + ret = wsa884x_recover_pa(wsa884x, true); + if (ret) + return ret; + } + + if (!wsa884x->protection_pa_reported) { + if (!wsa_macro_protection_pa_event(component, true)) { + wsa884x_disable_pa(component); + return -ENODEV; + } + + wsa884x->protection_pa_reported = true; + } + + return 0; + } if (mute) { - snd_soc_component_write_field(component, WSA884X_DRE_CTL_1, - WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, - 0x0); - snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, - WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, - 0x0); + if (wsa884x->protection_pa_reported) { + wsa_macro_protection_pa_event(component, false); + wsa884x->protection_pa_reported = false; + } + + wsa884x_disable_pa(component); } else { - snd_soc_component_write_field(component, WSA884X_DRE_CTL_1, - WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, - 0x1); - snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, - WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, - 0x1); + ret = snd_soc_component_write_field(component, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + 0x1); + if (ret < 0) { + wsa884x_disable_pa(component); + return ret; + } + + ret = snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x1); + if (ret < 0) { + wsa884x_disable_pa(component); + return ret; + } + + if (!wsa884x->protection_pa_reported && + wsa884x_uses_2s_4ohm_profile(wsa884x) && + wsa884x->protected_feedback) { + usleep_range(1000, 1100); + ret = regmap_read(wsa884x->regmap, WSA884X_PA_FSM_STA1, + &pa_status); + if (ret || (pa_status & WSA884X_PA_ERROR_MASK)) { + wsa884x_disable_pa(component); + if (ret) + dev_err(wsa884x->dev, + "cannot read protected PA status: %pe\n", + ERR_PTR(ret)); + else + dev_err(wsa884x->dev, + "protected PA start failed: status %#x\n", + pa_status); + return ret ?: -EIO; + } + + if (!wsa_macro_protection_pa_event(component, true)) { + wsa884x_disable_pa(component); + return -ENODEV; + } + + wsa884x->protection_pa_reported = true; + } } return 0; @@ -1830,7 +2375,7 @@ static int wsa884x_set_stream(struct snd_soc_dai *dai, { struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); - wsa884x->sruntime = stream; + wsa884x->sruntime[dai->id] = stream; return 0; } @@ -1846,6 +2391,7 @@ static const struct snd_soc_dai_ops wsa884x_dai_ops = { static struct snd_soc_dai_driver wsa884x_dais[] = { { .name = "SPKR", + .id = 0, .playback = { .stream_name = "SPKR Playback", .rates = WSA884X_RATES | WSA884X_FRAC_RATES, @@ -1857,6 +2403,34 @@ static struct snd_soc_dai_driver wsa884x_dais[] = { }, .ops = &wsa884x_dai_ops, }, + { + .name = "SPKR_VI", + .id = 1, + .playback = { + .stream_name = "SPKR VI Protection", + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 8000, + .rate_max = 8000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &wsa884x_dai_ops, + }, + { + .name = "SPKR_CPS", + .id = 2, + .playback = { + .stream_name = "SPKR CPS Protection", + .rates = SNDRV_PCM_RATE_24000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 24000, + .rate_max = 24000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &wsa884x_dai_ops, + }, }; static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp) @@ -2040,12 +2614,45 @@ static int wsa884x_get_reset(struct device *dev, struct wsa884x_priv *wsa884x) return 0; } +static int wsa884x_init_sink_ports(struct wsa884x_priv *wsa884x) +{ + struct sdw_slave *slave = wsa884x->slave; + struct sdw_dpn_prop *ports; + + slave->prop.sink_dpn_prop = wsa884x_sink_dpn_prop; + if (!of_machine_is_compatible("microsoft,denali") || + !wsa884x->protected_feedback || wsa884x->speaker_load_ohms != 4) + return 0; + + /* + * Select transport capabilities from the board wiring. VPHX status is + * only available after attach, and remains a separate analog guard. + */ + ports = devm_kmemdup(wsa884x->dev, wsa884x_sink_dpn_prop, + sizeof(wsa884x_sink_dpn_prop), GFP_KERNEL); + if (!ports) + return -ENOMEM; + + ports[WSA884X_PORT_DAC].simple_transport_registers = + SDW_DPN_SIMPLE_TRANSPORT_BLOCKCTRL3; + ports[WSA884X_PORT_COMP].simple_transport_registers = + SDW_DPN_SIMPLE_TRANSPORT_OFFSETCTRL2; + ports[WSA884X_PORT_BOOST].simple_transport_registers = + SDW_DPN_SIMPLE_TRANSPORT_OFFSETCTRL2; + slave->prop.sink_dpn_prop = ports; + + return 0; +} + static int wsa884x_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) { struct device *dev = &pdev->dev; struct wsa884x_priv *wsa884x; unsigned int i; + u32 cps_offset1; + u32 speaker_load_ohms; + u32 visense_channel_mask; int ret; wsa884x = devm_kzalloc(dev, sizeof(*wsa884x), GFP_KERNEL); @@ -2079,10 +2686,42 @@ static int wsa884x_probe(struct sdw_slave *pdev, wsa884x->slave = pdev; wsa884x->dev = dev; wsa884x->dev_mode = WSA884X_SPEAKER; + wsa884x->speaker_load_ohms = 8; + wsa884x->supply_config = WSA884X_SUPPLY_1S; + if (!of_property_read_u32(dev->of_node, "qcom,speaker-load-ohms", + &speaker_load_ohms)) { + if (speaker_load_ohms != 4 && speaker_load_ohms != 8) + return dev_err_probe(dev, -EINVAL, + "invalid qcom,speaker-load-ohms\n"); + wsa884x->speaker_load_ohms = speaker_load_ohms; + } wsa884x->sconfig.ch_count = 1; wsa884x->sconfig.bps = 1; wsa884x->sconfig.direction = SDW_DATA_DIR_RX; wsa884x->sconfig.type = SDW_STREAM_PDM; + wsa884x->visense_channel_mask = + wsa884x_pconfig[WSA884X_PORT_VISENSE].ch_mask; + wsa884x->protected_feedback = + of_property_read_bool(dev->of_node, "qcom,enable-cps"); + if (wsa884x->protected_feedback) { + ret = of_property_read_u32(dev->of_node, + "qcom,visense-channel-mask", + &visense_channel_mask); + if (!ret) { + if (!visense_channel_mask || visense_channel_mask > U8_MAX) + return dev_err_probe(dev, -EINVAL, + "invalid qcom,visense-channel-mask\n"); + wsa884x->visense_channel_mask = visense_channel_mask; + } + + ret = of_property_read_u32(dev->of_node, "qcom,cps-offset1", + &cps_offset1); + if (ret || cps_offset1 > U8_MAX) + return dev_err_probe(dev, ret ?: -ERANGE, + "invalid qcom,cps-offset1\n"); + wsa884x->cps_offset1 = cps_offset1; + wsa884x->port_enable[WSA884X_PORT_CPS] = true; + } /* * Port map index starts with 0, however the data port for this codec @@ -2092,9 +2731,21 @@ static int wsa884x_probe(struct sdw_slave *pdev, WSA884X_MAX_SWR_PORTS)) dev_dbg(dev, "Static Port mapping not specified\n"); - pdev->prop.sink_ports = GENMASK(WSA884X_MAX_SWR_PORTS - 1, 0); + if (wsa884x->protected_feedback) { + pdev->prop.sink_ports = BIT(WSA884X_PORT_DAC + 1) | + BIT(WSA884X_PORT_COMP + 1) | + BIT(WSA884X_PORT_BOOST + 1) | + BIT(WSA884X_PORT_PBR + 1); + pdev->prop.source_ports = BIT(WSA884X_PORT_VISENSE + 1) | + BIT(WSA884X_PORT_CPS + 1); + pdev->prop.src_dpn_prop = wsa884x_source_dpn_prop; + } else { + pdev->prop.sink_ports = GENMASK(WSA884X_MAX_SWR_PORTS - 1, 0); + } pdev->prop.simple_clk_stop_capable = true; - pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop; + ret = wsa884x_init_sink_ports(wsa884x); + if (ret) + return ret; pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; wsa884x_reset_deassert(wsa884x); @@ -2131,15 +2782,17 @@ static int wsa884x_probe(struct sdw_slave *pdev, return devm_snd_soc_register_component(dev, &wsa884x_component_drv, wsa884x_dais, - ARRAY_SIZE(wsa884x_dais)); + wsa884x->protected_feedback ? + ARRAY_SIZE(wsa884x_dais) : 1); } static int wsa884x_runtime_suspend(struct device *dev) { - struct regmap *regmap = dev_get_regmap(dev, NULL); + struct wsa884x_priv *wsa884x = dev_get_drvdata(dev); - regcache_cache_only(regmap, true); - regcache_mark_dirty(regmap); + regcache_cache_only(wsa884x->regmap, true); + if (!wsa884x_uses_denali_protected_profile(wsa884x)) + regcache_mark_dirty(wsa884x->regmap); return 0; } diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 8535ef844ce031..be59c138e3ea47 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -1711,12 +1711,12 @@ static const struct reg_default fsl_easrc_reg_defaults[] = { {REG_EASRC_SFS(2), 0x00000000}, {REG_EASRC_SFS(3), 0x00000000}, {REG_EASRC_RRL(0), 0x00000000}, - {REG_EASRC_RRL(1), 0x00000000}, - {REG_EASRC_RRL(2), 0x00000000}, - {REG_EASRC_RRL(3), 0x00000000}, {REG_EASRC_RRH(0), 0x00000000}, + {REG_EASRC_RRL(1), 0x00000000}, {REG_EASRC_RRH(1), 0x00000000}, + {REG_EASRC_RRL(2), 0x00000000}, {REG_EASRC_RRH(2), 0x00000000}, + {REG_EASRC_RRL(3), 0x00000000}, {REG_EASRC_RRH(3), 0x00000000}, {REG_EASRC_RUC(0), 0x00000000}, {REG_EASRC_RUC(1), 0x00000000}, @@ -2058,7 +2058,7 @@ static int fsl_easrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buff /* right shift 12 bit to make ratio in 32bit space */ val2 = (u64)in_samples << (frac_bits - 12); val1 = val1 >> 12; - do_div(val2, val1); + val2 = div64_u64(val2, val1); out_samples = val2; out_length = out_samples * out_width * channels; diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 9ad44eeed6ad8f..7831136f4f1295 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -170,6 +170,7 @@ static int psc_i2s_of_probe(struct platform_device *op) psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai)); if (rc != 0) { pr_err("Failed to register DAI\n"); + mpc5200_audio_dma_destroy(op); return rc; } diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c index 22ae2d22f121aa..44bb11c694905f 100644 --- a/sound/soc/intel/atom/sst/sst_pci.c +++ b/sound/soc/intel/atom/sst/sst_pci.c @@ -130,13 +130,15 @@ static int intel_sst_probe(struct pci_dev *pci, sst_drv_ctx->pci = pci_dev_get(pci); ret = sst_platform_get_resources(sst_drv_ctx); if (ret < 0) - goto do_free_drv_ctx; + goto do_put_pci; pci_set_drvdata(pci, sst_drv_ctx); sst_configure_runtime_pm(sst_drv_ctx); return ret; +do_put_pci: + pci_dev_put(sst_drv_ctx->pci); do_free_drv_ctx: sst_context_cleanup(sst_drv_ctx); dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c index 7910d5d9ac4ffe..ea895fe6b5e973 100644 --- a/sound/soc/loongson/loongson_card.c +++ b/sound/soc/loongson/loongson_card.c @@ -91,7 +91,7 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) const char *codec_dai_name; struct acpi_device *adev; struct device *phy_dev; - int i; + int i, ret; /* fixup platform name based on reference node */ adev = loongson_card_acpi_find_device(card, "cpu"); @@ -108,7 +108,9 @@ static int loongson_card_parse_acpi(struct loongson_card_data *data) return -ENOENT; snprintf(codec_name, sizeof(codec_name), "i2c-%s", acpi_dev_name(adev)); - device_property_read_string(card->dev, "codec-dai-name", &codec_dai_name); + ret = device_property_read_string(card->dev, "codec-dai-name", &codec_dai_name); + if (ret) + return ret; for (i = 0; i < card->num_links; i++) { loongson_dai_links[i].platforms->name = dev_name(phy_dev); diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index e6e9eb2e85aa1f..8e7af54b09b2a9 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -12,6 +13,10 @@ #include "q6apm.h" #include "audioreach.h" +DEFINE_FREE(q6apm_graph_user, struct q6apm_graph *, q6apm_graph_user_put(_T)) + +#define AUDIOREACH_DSP_EUNSUPPORTED 3 + /* SubGraph Config */ struct apm_sub_graph_data { struct apm_sub_graph_cfg sub_graph_cfg; @@ -55,14 +60,20 @@ struct apm_container_obj { struct apm_cont_prop_id_domain domain; } __packed; +struct apm_container_extended_obj { + struct apm_container_obj common; + struct apm_prop_data parent_data; + struct apm_cont_prop_id_parent_container parent; + struct apm_prop_data heap_data; + struct apm_cont_prop_id_headp_id heap; +} __packed; + struct apm_container_params { struct apm_module_param_data param_data; uint32_t num_containers; - struct apm_container_obj cont_obj[]; + u8 cont_obj[]; } __packed; -#define APM_CONTAINER_PSIZE(p, n) ALIGN(struct_size(p, cont_obj, n), 8) - /* Module List config */ struct apm_mod_list_obj { /* Modules list cfg */ @@ -109,6 +120,15 @@ struct apm_mod_conn_list_params { #define APM_MOD_CONN_PSIZE(p, n) ALIGN(struct_size(p, conn_obj, n), 8) +struct apm_mod_ctrl_link_list_params { + struct apm_module_param_data param_data; + u32 num_ctrl_links; + u8 ctrl_link_cfg[]; +} __packed; + +#define APM_MOD_CTRL_LINK_PSIZE(p, n) \ + ALIGN(struct_size(p, ctrl_link_cfg, n), 8) + struct apm_graph_open_params { struct apm_cmd_header *cmd_header; struct apm_sub_graph_params *sg_data; @@ -116,6 +136,7 @@ struct apm_graph_open_params { struct apm_module_list_params *mod_list_data; struct apm_prop_list_params *mod_prop_data; struct apm_mod_conn_list_params *mod_conn_list_data; + struct apm_mod_ctrl_link_list_params *mod_ctrl_link_data; } __packed; struct apm_pcm_module_media_fmt_cmd { @@ -292,6 +313,32 @@ void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t token } EXPORT_SYMBOL_GPL(audioreach_alloc_apm_cmd_pkt); +static int audioreach_size_add(size_t *total, size_t value) +{ + return check_add_overflow(*total, value, total) ? -EOVERFLOW : 0; +} + +static int audioreach_flex_size(size_t base, size_t count, size_t element, + size_t *size) +{ + size_t elements; + + if (check_mul_overflow(count, element, &elements) || + check_add_overflow(base, elements, size)) + return -EOVERFLOW; + + return 0; +} + +static int audioreach_align_size(size_t *size) +{ + if (*size > SIZE_MAX - 7) + return -EOVERFLOW; + + *size = ALIGN(*size, 8); + return 0; +} + void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels) { if (num_channels == 1) { @@ -308,13 +355,21 @@ void audioreach_set_default_channel_mapping(u8 *ch_map, int num_channels) } EXPORT_SYMBOL_GPL(audioreach_set_default_channel_mapping); -static void apm_populate_container_config(struct apm_container_obj *cfg, +static size_t apm_container_obj_size(const struct audioreach_container *cont) +{ + return cont->has_extended_properties ? + sizeof(struct apm_container_extended_obj) : + sizeof(struct apm_container_obj); +} + +static void apm_populate_container_config(void *data, const struct audioreach_container *cont) { + struct apm_container_obj *cfg = data; /* Container Config */ cfg->container_cfg.container_id = cont->container_id; - cfg->container_cfg.num_prop = 4; + cfg->container_cfg.num_prop = cont->has_extended_properties ? 6 : 4; /* Capability list */ cfg->cap_data.prop_id = APM_CONTAINER_PROP_ID_CAPABILITY_LIST; @@ -336,6 +391,19 @@ static void apm_populate_container_config(struct apm_container_obj *cfg, cfg->domain_data.prop_id = APM_CONTAINER_PROP_ID_PROC_DOMAIN; cfg->domain_data.prop_size = sizeof(struct apm_cont_prop_id_domain); cfg->domain.proc_domain = cont->proc_domain; + + if (cont->has_extended_properties) { + struct apm_container_extended_obj *ext = data; + + ext->parent_data.prop_id = + APM_CONTAINER_PROP_ID_PARENT_CONTAINER_ID; + ext->parent_data.prop_size = sizeof(ext->parent); + ext->parent.parent_container_id = cont->parent_container_id; + + ext->heap_data.prop_id = APM_CONTAINER_PROP_ID_HEAP_ID; + ext->heap_data.prop_size = sizeof(ext->heap); + ext->heap.heap_id = cont->heap_id; + } } static void apm_populate_sub_graph_config(struct apm_sub_graph_data *cfg, @@ -398,23 +466,26 @@ static void audioreach_populate_graph(struct q6apm *apm, { struct apm_mod_conn_list_params *mc_data = open->mod_conn_list_data; struct apm_module_list_params *ml_data = open->mod_list_data; + struct apm_mod_ctrl_link_list_params *cl_data = open->mod_ctrl_link_data; + u8 *ctrl_link_cfg = cl_data ? cl_data->ctrl_link_cfg : NULL; struct apm_prop_list_params *mp_data = open->mod_prop_data; struct apm_container_params *c_data = open->cont_data; struct apm_sub_graph_params *sg_data = open->sg_data; - int ncontainer = 0, nmodule = 0, nconn = 0; + int nmodule = 0, nconn = 0; struct apm_mod_prop_obj *module_prop_obj; struct audioreach_container *container; struct apm_module_conn_obj *conn_obj; struct audioreach_module *module; struct audioreach_sub_graph *sg; - struct apm_container_obj *cobj; + u8 *cobj = c_data->cont_obj; struct apm_mod_list_obj *mlobj; int i = 0; mlobj = &ml_data->mod_list_obj[0]; - if (info->dst_mod_inst_id && info->src_mod_inst_id) { + if (!info->internal_vmixer_connection && + info->dst_mod_inst_id && info->src_mod_inst_id) { conn_obj = &mc_data->conn_obj[nconn]; conn_obj->src_mod_inst_id = info->src_mod_inst_id; conn_obj->src_mod_op_port_id = info->src_mod_op_port_id; @@ -429,8 +500,6 @@ static void audioreach_populate_graph(struct q6apm *apm, apm_populate_sub_graph_config(sg_cfg, sg); list_for_each_entry(container, &sg->container_list, node) { - cobj = &c_data->cont_obj[ncontainer]; - apm_populate_container_config(cobj, container); apm_populate_module_list_obj(mlobj, container, sg->sub_graph_id); @@ -440,6 +509,16 @@ static void audioreach_populate_graph(struct q6apm *apm, module_prop_obj = &mp_data->mod_prop_obj[nmodule++]; apm_populate_module_prop_obj(module_prop_obj, module); + if (module->ctrl_link_data) { + u32 size = le32_to_cpu(module->ctrl_link_data->size); + + memcpy(ctrl_link_cfg, + (u8 *)module->ctrl_link_data->data + + sizeof(u32), + size - sizeof(u32)); + ctrl_link_cfg += size - sizeof(u32); + } + if (!module->max_op_port) continue; @@ -459,8 +538,7 @@ static void audioreach_populate_graph(struct q6apm *apm, } mlobj = (void *) mlobj + APM_MOD_LIST_OBJ_PSIZE(mlobj, container->num_modules); - - ncontainer++; + cobj += apm_container_obj_size(container); } } } @@ -468,58 +546,115 @@ static void audioreach_populate_graph(struct q6apm *apm, void *audioreach_alloc_graph_pkt(struct q6apm *apm, const struct audioreach_graph_info *info) { - int payload_size, sg_sz, cont_sz, ml_sz, mp_sz, mc_sz; + size_t payload_size = 0, sg_sz, cont_sz, ml_sz = 0; + size_t mp_sz, mc_sz, cl_sz = 0, object_size; struct apm_module_param_data *param_data; - struct apm_container_params *cont_params; struct audioreach_container *container; struct apm_sub_graph_params *sg_params; struct apm_mod_conn_list_params *mcon; struct apm_graph_open_params params; struct apm_prop_list_params *mprop; + struct apm_mod_ctrl_link_list_params *mctrl; struct audioreach_module *module; struct audioreach_sub_graph *sgs; struct apm_mod_list_obj *mlobj; const struct list_head *sg_list; - int num_connections = 0; - int num_containers = 0; - int num_sub_graphs = 0; - int num_modules = 0; - int num_modules_list; + size_t num_connections = 0; + size_t num_containers = 0; + size_t num_sub_graphs = 0; + size_t num_ctrl_links = 0; + size_t ctrl_link_cfg_size = 0; + size_t num_modules = 0; + size_t num_modules_list; struct gpr_pkt *pkt; void *p; + int ret; sg_list = &info->sg_list; - ml_sz = 0; + cont_sz = sizeof(struct apm_container_params); /* add FE-BE connections */ - if (info->dst_mod_inst_id && info->src_mod_inst_id) - num_connections++; + if (!info->internal_vmixer_connection && + info->dst_mod_inst_id && info->src_mod_inst_id) + num_connections = 1; list_for_each_entry(sgs, sg_list, node) { - num_sub_graphs++; + if (check_add_overflow(num_sub_graphs, 1UL, &num_sub_graphs)) + return ERR_PTR(-EOVERFLOW); list_for_each_entry(container, &sgs->container_list, node) { - num_containers++; - num_modules += container->num_modules; - ml_sz = ml_sz + sizeof(struct apm_module_list_params) + - APM_MOD_LIST_OBJ_PSIZE(mlobj, container->num_modules); + if (check_add_overflow(num_containers, 1UL, + &num_containers) || + check_add_overflow(num_modules, + (size_t)container->num_modules, + &num_modules) || + audioreach_size_add(&cont_sz, + apm_container_obj_size(container))) + return ERR_PTR(-EOVERFLOW); + + ret = audioreach_flex_size(sizeof(*mlobj), + container->num_modules, + sizeof(mlobj->mod_cfg[0]), + &object_size); + if (ret || + audioreach_size_add(&ml_sz, + sizeof(struct apm_module_list_params)) || + audioreach_size_add(&ml_sz, object_size)) + return ERR_PTR(-EOVERFLOW); list_for_each_entry(module, &container->modules_list, node) { - num_connections += module->num_connections; + if (check_add_overflow(num_connections, + (size_t)module->num_connections, + &num_connections)) + return ERR_PTR(-EOVERFLOW); + if (module->ctrl_link_data) { + u32 size = le32_to_cpu(module->ctrl_link_data->size); + u32 links; + + links = get_unaligned_le32(module->ctrl_link_data->data); + if (check_add_overflow(num_ctrl_links, + (size_t)links, + &num_ctrl_links) || + audioreach_size_add(&ctrl_link_cfg_size, + size - sizeof(u32))) + return ERR_PTR(-EOVERFLOW); + } } } } - num_modules_list = num_containers; - sg_sz = APM_SUB_GRAPH_PSIZE(sg_params, num_sub_graphs); - cont_sz = APM_CONTAINER_PSIZE(cont_params, num_containers); - - ml_sz = ALIGN(ml_sz, 8); - - mp_sz = APM_MOD_PROP_PSIZE(mprop, num_modules); - mc_sz = APM_MOD_CONN_PSIZE(mcon, num_connections); + if (num_sub_graphs > U32_MAX || num_containers > U32_MAX || + num_modules > U32_MAX || num_connections > U32_MAX || + num_ctrl_links > U32_MAX) + return ERR_PTR(-EOVERFLOW); - payload_size = sg_sz + cont_sz + ml_sz + mp_sz + mc_sz; - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_GRAPH_OPEN, 0); + num_modules_list = num_containers; + ret = audioreach_flex_size(sizeof(*sg_params), num_sub_graphs, + sizeof(sg_params->sg_cfg[0]), &sg_sz); + ret = ret ?: audioreach_flex_size(sizeof(*mprop), num_modules, + sizeof(mprop->mod_prop_obj[0]), &mp_sz); + ret = ret ?: audioreach_flex_size(sizeof(*mcon), num_connections, + sizeof(mcon->conn_obj[0]), &mc_sz); + if (num_ctrl_links) + ret = ret ?: audioreach_flex_size(sizeof(*mctrl), + ctrl_link_cfg_size, 1, &cl_sz); + ret = ret ?: audioreach_align_size(&sg_sz); + ret = ret ?: audioreach_align_size(&cont_sz); + ret = ret ?: audioreach_align_size(&ml_sz); + ret = ret ?: audioreach_align_size(&mp_sz); + ret = ret ?: audioreach_align_size(&mc_sz); + if (num_ctrl_links) + ret = ret ?: audioreach_align_size(&cl_sz); + if (ret || audioreach_size_add(&payload_size, sg_sz) || + audioreach_size_add(&payload_size, cont_sz) || + audioreach_size_add(&payload_size, ml_sz) || + audioreach_size_add(&payload_size, mp_sz) || + audioreach_size_add(&payload_size, mc_sz) || + audioreach_size_add(&payload_size, cl_sz) || + payload_size > INT_MAX - GPR_HDR_SIZE - APM_CMD_HDR_SIZE) + return ERR_PTR(-EOVERFLOW); + + pkt = audioreach_alloc_apm_cmd_pkt((int)payload_size, + APM_CMD_GRAPH_OPEN, 0); if (IS_ERR(pkt)) return pkt; @@ -570,12 +705,647 @@ void *audioreach_alloc_graph_pkt(struct q6apm *apm, params.mod_conn_list_data->num_connections = num_connections; p += mc_sz; + if (num_ctrl_links) { + params.mod_ctrl_link_data = p; + param_data = ¶ms.mod_ctrl_link_data->param_data; + param_data->module_instance_id = APM_MODULE_INSTANCE_ID; + param_data->param_id = APM_PARAM_ID_MODULE_CTRL_LINK_CFG; + param_data->param_size = sizeof(u32) + ctrl_link_cfg_size; + params.mod_ctrl_link_data->num_ctrl_links = num_ctrl_links; + } else { + params.mod_ctrl_link_data = NULL; + } + audioreach_populate_graph(apm, info, ¶ms, sg_list, num_sub_graphs); return pkt; } EXPORT_SYMBOL_GPL(audioreach_alloc_graph_pkt); +static struct audioreach_module_priv_data * +audioreach_module_data(const struct audioreach_module *module, u32 type) +{ + switch (type) { + case SND_SOC_AR_TPLG_GRAPH_CAL_CFG_TYPE: + return module->graph_cal_data; + case SND_SOC_AR_TPLG_RENDER_EP_CFG_TYPE: + return module->render_ep_data; + case SND_SOC_AR_TPLG_SP_TAG_CFG_TYPE: + return module->sp_tag_data; + case SND_SOC_AR_TPLG_SPVI_TAG_CFG_TYPE: + return module->spvi_tag_data; + case SND_SOC_AR_TPLG_VI_EP_CFG_TYPE: + return module->vi_ep_data; + case SND_SOC_AR_TPLG_PROTECTION_DYNAMIC_CFG_TYPE: + return module->protection_dynamic_data; + case SND_SOC_AR_TPLG_VOLUME_GAIN_CFG_TYPE: + return module->volume_gain_data; + case SND_SOC_AR_TPLG_VOLUME_FILTER_CFG_TYPE: + return module->volume_filter_data; + case SND_SOC_AR_TPLG_VOLUME_MUTE_CFG_TYPE: + return module->volume_mute_data; + case SND_SOC_AR_TPLG_CHANNEL_MIXER_CFG_TYPE: + return module->channel_mixer_data; + default: + return NULL; + } +} + +const struct audioreach_module_priv_data * +audioreach_graph_find_data(const struct audioreach_graph_info *info, u32 type) +{ + const struct audioreach_module_priv_data *found = NULL; + struct audioreach_container *container; + struct audioreach_sub_graph *sg; + struct audioreach_module *module; + + list_for_each_entry(sg, &info->sg_list, node) { + list_for_each_entry(container, &sg->container_list, node) { + list_for_each_entry(module, &container->modules_list, node) { + struct audioreach_module_priv_data *candidate; + + candidate = audioreach_module_data(module, type); + if (!candidate) + continue; + if (found) + return ERR_PTR(-EEXIST); + found = candidate; + } + } + } + + return found; +} + +const struct audioreach_module * +audioreach_graph_find_module(const struct audioreach_graph_info *info, u32 mid) +{ + const struct audioreach_module *found = NULL; + struct audioreach_container *container; + struct audioreach_sub_graph *sg; + struct audioreach_module *module; + + list_for_each_entry(sg, &info->sg_list, node) { + list_for_each_entry(container, &sg->container_list, node) { + list_for_each_entry(module, &container->modules_list, node) { + if (module->module_id != mid) + continue; + if (found) + return ERR_PTR(-EEXIST); + found = module; + } + } + } + + return found; +} + +static bool +audioreach_graph_has_iid(const struct audioreach_graph_info *info, u32 iid) +{ + struct audioreach_container *container; + struct audioreach_sub_graph *sg; + struct audioreach_module *module; + + list_for_each_entry(sg, &info->sg_list, node) + list_for_each_entry(container, &sg->container_list, node) + list_for_each_entry(module, &container->modules_list, node) + if (module->instance_id == iid) + return true; + + return false; +} + +static int audioreach_validate_stage(const struct audioreach_graph_info *info, + const struct audioreach_module_priv_data *stage) +{ + const u8 *cursor = (const u8 *)stage->data; + size_t remaining = le32_to_cpu(stage->size); + + if (!remaining || !IS_ALIGNED(remaining, 8)) + return -EINVAL; + + while (remaining) { + size_t frame_size; + u32 param_size; + + if (remaining < sizeof(struct apm_module_param_data)) + return -EINVAL; + + param_size = get_unaligned_le32(cursor + 2 * sizeof(u32)); + if (check_add_overflow(sizeof(struct apm_module_param_data), + (size_t)param_size, &frame_size)) + return -EOVERFLOW; + frame_size = ALIGN(frame_size, 8); + if (frame_size > remaining) + return -EINVAL; + if (!audioreach_graph_has_iid(info, + get_unaligned_le32(cursor))) + return -ENOENT; + + cursor += frame_size; + remaining -= frame_size; + } + + return 0; +} + +int audioreach_graph_protection_profile(const struct audioreach_graph_info *info) +{ + static const u32 stage_types[] = { + SND_SOC_AR_TPLG_GRAPH_CAL_CFG_TYPE, + SND_SOC_AR_TPLG_RENDER_EP_CFG_TYPE, + SND_SOC_AR_TPLG_SP_TAG_CFG_TYPE, + SND_SOC_AR_TPLG_SPVI_TAG_CFG_TYPE, + SND_SOC_AR_TPLG_VI_EP_CFG_TYPE, + SND_SOC_AR_TPLG_PROTECTION_DYNAMIC_CFG_TYPE, + SND_SOC_AR_TPLG_VOLUME_GAIN_CFG_TYPE, + SND_SOC_AR_TPLG_VOLUME_FILTER_CFG_TYPE, + SND_SOC_AR_TPLG_VOLUME_MUTE_CFG_TYPE, + SND_SOC_AR_TPLG_CHANNEL_MIXER_CFG_TYPE, + }; + struct audioreach_container *container; + const struct audioreach_module_priv_data *data; + const struct audioreach_module *sp, *spvi; + struct audioreach_sub_graph *sg; + struct audioreach_module *module; + bool declared = false; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(stage_types); i++) { + data = audioreach_graph_find_data(info, stage_types[i]); + if (IS_ERR(data)) + return PTR_ERR(data); + if (data) + declared = true; + } + list_for_each_entry(sg, &info->sg_list, node) + list_for_each_entry(container, &sg->container_list, node) + list_for_each_entry(module, &container->modules_list, node) + if (module->speaker_protection_bypass || + module->integrated_backend_id) + declared = true; + if (!declared) + return 0; + + for (i = 0; i < ARRAY_SIZE(stage_types); i++) { + data = audioreach_graph_find_data(info, stage_types[i]); + if (IS_ERR(data)) + return PTR_ERR(data); + if (!data || !le32_to_cpu(data->size) || + !IS_ALIGNED(le32_to_cpu(data->size), 8)) + return -EINVAL; + /* The graph-calibration aggregate is opaque and may carry sentinels. */ + if (i) { + ret = audioreach_validate_stage(info, data); + if (ret) + return ret; + } + } + + sp = audioreach_graph_find_module(info, MODULE_ID_SPEAKER_PROTECTION); + spvi = audioreach_graph_find_module(info, + MODULE_ID_SPEAKER_PROTECTION_VI); + if (IS_ERR(sp) || IS_ERR(spvi)) + return -EEXIST; + if (!sp || !spvi || !sp->speaker_protection_bypass || + !spvi->speaker_protection_bypass) + return -EINVAL; + + return 1; +} + +int audioreach_graph_protection_oob_size(const struct audioreach_graph_info *info, + size_t *size) +{ + static const u32 oob_types[] = { + SND_SOC_AR_TPLG_GRAPH_CAL_CFG_TYPE, + SND_SOC_AR_TPLG_RENDER_EP_CFG_TYPE, + SND_SOC_AR_TPLG_SP_TAG_CFG_TYPE, + SND_SOC_AR_TPLG_SPVI_TAG_CFG_TYPE, + SND_SOC_AR_TPLG_VI_EP_CFG_TYPE, + SND_SOC_AR_TPLG_VOLUME_FILTER_CFG_TYPE, + SND_SOC_AR_TPLG_CHANNEL_MIXER_CFG_TYPE, + }; + const struct audioreach_module_priv_data *data; + size_t max_size = 0; + int i, ret; + + ret = audioreach_graph_protection_profile(info); + if (ret <= 0) { + *size = 0; + return ret; + } + + for (i = 0; i < ARRAY_SIZE(oob_types); i++) { + data = audioreach_graph_find_data(info, oob_types[i]); + if (IS_ERR(data)) + return PTR_ERR(data); + if (data) + max_size = max(max_size, + (size_t)le32_to_cpu(data->size)); + } + + *size = max_size; + return 0; +} + +static size_t audioreach_frame_size(const u8 *frame) +{ + return ALIGN(sizeof(struct apm_module_param_data) + + get_unaligned_le32(frame + 2 * sizeof(u32)), 8); +} + +static int audioreach_validate_calibration_frames(const u8 *data, size_t size) +{ + size_t frame_size, padding, payload_size; + + if (!size) + return -EINVAL; + + while (size) { + if (size < sizeof(struct apm_module_param_data)) + return -EINVAL; + payload_size = get_unaligned_le32(data + 2 * sizeof(u32)); + if (payload_size > size - sizeof(struct apm_module_param_data)) + return -EINVAL; + frame_size = sizeof(struct apm_module_param_data) + payload_size; + padding = -frame_size & 7; + if (padding > size - frame_size) + return -EINVAL; + frame_size += padding; + data += frame_size; + size -= frame_size; + } + + return 0; +} + +static int audioreach_replay_graph_calibration(struct audioreach_graph *graph, + const u8 *data, size_t size) +{ + size_t offset = 0, frame_size; + int ret; + + /* The entire block has been validated before sending any record. */ + while (offset < size) { + frame_size = audioreach_frame_size(data + offset); + ret = q6apm_send_oob_config(graph, data + offset, frame_size); + if (ret && ret != -EOPNOTSUPP) { + dev_err(graph->apm->dev, + "graph calibration replay failed at offset %zu (%d)\n", + offset, ret); + return ret; + } + offset += frame_size; + } + + return 0; +} + +int audioreach_send_protected_graph_calibration(struct audioreach_graph *graph) +{ + struct audioreach_graph_info *info = graph->info; + const struct audioreach_module_priv_data *graph_cal; + size_t size; + int ret; + + ret = audioreach_graph_protection_profile(graph->info); + if (ret <= 0) + return ret < 0 ? ret : 0; + + graph_cal = audioreach_graph_find_data(graph->info, + SND_SOC_AR_TPLG_GRAPH_CAL_CFG_TYPE); + size = le32_to_cpu(graph_cal->size); + /* Validate framing only: calibration may contain opaque sentinel IIDs. */ + ret = audioreach_validate_calibration_frames((const u8 *)graph_cal->data, + size); + if (ret) + return ret; + + mutex_lock(&info->calibration_lock); + /* A later graph must not silently accept a partially failed replay. */ + if (info->calibration_replayed && info->calibration_replay_result) { + ret = info->calibration_replay_result; + goto unlock; + } + ret = q6apm_send_oob_config(graph, graph_cal->data, size); + if (ret == -EOPNOTSUPP) { + /* Preserve the qualified replay, including query-only records. */ + if (!info->calibration_replayed) { + dev_warn(graph->apm->dev, + "graph calibration returned AR_EUNSUPPORTED; replaying records\n"); + info->calibration_replay_result = + audioreach_replay_graph_calibration(graph, + (const u8 *)graph_cal->data, + size); + info->calibration_replayed = true; + } + ret = info->calibration_replay_result; + } + +unlock: + mutex_unlock(&info->calibration_lock); + return ret; +} + +static int audioreach_send_inband_frame(struct q6apm_graph *graph, + const u8 *frame, size_t frame_size) +{ + struct gpr_pkt *pkt __free(kfree) = NULL; + void *payload; + + pkt = audioreach_alloc_cmd_pkt(frame_size, APM_CMD_SET_CFG, 0, graph->port->id, + get_unaligned_le32(frame)); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + payload = (u8 *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + memcpy(payload, frame, frame_size); + + return audioreach_graph_send_cmd_sync(graph, pkt, 0); +} + +static int audioreach_send_inband_stage(struct q6apm_graph *graph, + const struct audioreach_module_priv_data *stage, + size_t offset) +{ + const u8 *cursor = (const u8 *)stage->data + offset; + size_t remaining = le32_to_cpu(stage->size) - offset; + int ret; + + while (remaining) { + size_t frame_size = audioreach_frame_size(cursor); + + ret = audioreach_send_inband_frame(graph, cursor, frame_size); + if (ret) + return ret; + cursor += frame_size; + remaining -= frame_size; + } + + return 0; +} + +static int audioreach_graph_module_enable(struct q6apm_graph *graph, + const struct audioreach_module *module, + bool enable) +{ + struct gpr_pkt *pkt __free(kfree) = NULL; + struct apm_module_param_data *param; + u32 *value; + + pkt = audioreach_alloc_cmd_pkt(APM_MODULE_PARAM_DATA_SIZE + sizeof(*value), + APM_CMD_SET_CFG, 0, graph->port->id, + module->instance_id); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + param = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + param->module_instance_id = module->instance_id; + param->param_id = PARAM_ID_MODULE_ENABLE; + param->param_size = sizeof(*value); + value = (void *)param + APM_MODULE_PARAM_DATA_SIZE; + *value = enable; + + return audioreach_graph_send_cmd_sync(graph, pkt, 0); +} + +static int audioreach_protection_enable(struct q6apm_graph *graph, + bool enable) +{ + const struct audioreach_module *first, *second; + const struct audioreach_module *sp, *spvi; + int ret; + + sp = audioreach_graph_find_module(graph->info, + MODULE_ID_SPEAKER_PROTECTION); + spvi = audioreach_graph_find_module(graph->info, + MODULE_ID_SPEAKER_PROTECTION_VI); + if (IS_ERR_OR_NULL(sp) || IS_ERR_OR_NULL(spvi)) + return -ENODEV; + + /* Bring feedback up first; tear render down first. */ + first = enable ? spvi : sp; + second = enable ? sp : spvi; + ret = audioreach_graph_module_enable(graph, first, enable); + if (ret) + return ret; + + return audioreach_graph_module_enable(graph, second, enable); +} + +static int audioreach_send_oob_stage(struct q6apm_graph *graph, u32 type) +{ + const struct audioreach_module_priv_data *stage; + + stage = audioreach_graph_find_data(graph->info, type); + if (IS_ERR_OR_NULL(stage)) + return stage ? PTR_ERR(stage) : -ENODATA; + + return q6apm_send_graph_oob_config(graph, stage->data, + le32_to_cpu(stage->size)); +} + +static int audioreach_query_protection_config(struct q6apm_graph *graph, + u32 type, u32 module_id) +{ + const struct audioreach_module_priv_data *stage; + const struct audioreach_module *module; + struct gpr_pkt *pkt __free(kfree) = NULL; + struct apm_module_param_data *param; + const u8 *frame; + size_t payload_size; + u32 param_size; + + stage = audioreach_graph_find_data(graph->info, type); + module = audioreach_graph_find_module(graph->info, module_id); + if (IS_ERR_OR_NULL(stage) || IS_ERR_OR_NULL(module)) + return -ENODATA; + if (le32_to_cpu(stage->size) < sizeof(*param)) + return -EINVAL; + + /* The first tag record supplies the configuration query's shape. */ + frame = (const u8 *)stage->data; + if (get_unaligned_le32(frame) != module->instance_id) + return -EINVAL; + param_size = get_unaligned_le32(frame + 2 * sizeof(u32)); + if (!param_size || param_size > le32_to_cpu(stage->size) - sizeof(*param)) + return -EINVAL; + payload_size = ALIGN(sizeof(*param) + (size_t)param_size, 8); + /* GPR's packet-size field has 24 bits, including both headers. */ + if (payload_size > GENMASK(23, 0) - GPR_HDR_SIZE - APM_CMD_HDR_SIZE) + return -E2BIG; + + pkt = audioreach_alloc_cmd_pkt(payload_size, APM_CMD_GET_CFG, 0, + graph->port->id, module->instance_id); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + param = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + param->module_instance_id = module->instance_id; + param->param_id = get_unaligned_le32(frame + sizeof(u32)); + param->param_size = param_size; + + return audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_RSP_GET_CFG); +} + +static int audioreach_configure_protection_volume(struct q6apm_graph *graph, + const struct audioreach_module_priv_data *gain, + const struct audioreach_module_priv_data *mute) +{ + int ret; + + ret = audioreach_send_inband_stage(graph, gain, 0); + if (ret) + return ret; + ret = audioreach_send_oob_stage(graph, + SND_SOC_AR_TPLG_VOLUME_FILTER_CFG_TYPE); + if (ret) + return ret; + ret = audioreach_send_inband_stage(graph, mute, 0); + if (ret) + return ret; + + return audioreach_send_oob_stage(graph, + SND_SOC_AR_TPLG_CHANNEL_MIXER_CFG_TYPE); +} + +int audioreach_configure_protection(struct q6apm_graph *graph) +{ + const struct audioreach_module_priv_data *dynamic, *gain, *mute; + struct audioreach_graph *ar_graph = graph->ar_graph; + size_t first_size; + int bypass_ret; + int ret = 0; + + mutex_lock(&ar_graph->protection_lock); + if (ar_graph->protection_faulted) { + ret = -EIO; + goto unlock; + } + if (ar_graph->protection_configured || + ar_graph->protection_bypass_confirmed) + goto unlock; + if (!ar_graph->protection_available || + READ_ONCE(ar_graph->oob_transfer_uncertain)) { + ret = audioreach_protection_enable(graph, false); + if (ret) { + ar_graph->protection_faulted = true; + dev_err(graph->dev, + "protected graph calibration is unavailable; bypass failed (%d)\n", + ret); + } else { + ar_graph->protection_bypass_confirmed = true; + dev_warn(graph->dev, + "protected graph calibration is unavailable; using bypass\n"); + } + goto unlock; + } + + dynamic = audioreach_graph_find_data(graph->info, + SND_SOC_AR_TPLG_PROTECTION_DYNAMIC_CFG_TYPE); + gain = audioreach_graph_find_data(graph->info, + SND_SOC_AR_TPLG_VOLUME_GAIN_CFG_TYPE); + mute = audioreach_graph_find_data(graph->info, + SND_SOC_AR_TPLG_VOLUME_MUTE_CFG_TYPE); + if (IS_ERR_OR_NULL(dynamic) || IS_ERR_OR_NULL(gain) || + IS_ERR_OR_NULL(mute)) { + ret = -ENODATA; + goto bypass; + } + + if (!ar_graph->protection_vi_ready || + !ar_graph->protection_cps_ready) { + ret = audioreach_protection_enable(graph, false); + if (ret) { + ar_graph->protection_faulted = true; + dev_err(graph->dev, + "protected speaker feedback is incomplete; bypass failed (%d)\n", + ret); + } else { + /* Preserve the qualified volume tail after both disables. */ + ret = audioreach_configure_protection_volume(graph, gain, mute); + if (ret) { + ar_graph->protection_faulted = true; + dev_err(graph->dev, + "bypass volume configuration failed (%d)\n", ret); + } else { + ar_graph->protection_bypass_confirmed = true; + dev_warn(graph->dev, + "protected speaker feedback is incomplete; using bypass\n"); + } + } + goto unlock; + } + + first_size = audioreach_frame_size((const u8 *)dynamic->data); + ret = audioreach_send_inband_frame(graph, + (const u8 *)dynamic->data, + first_size); + if (ret) + goto bypass; + ret = audioreach_send_oob_stage(graph, + SND_SOC_AR_TPLG_SP_TAG_CFG_TYPE); + if (ret) + goto bypass; + /* Preserve the qualified query order before programming SPVI. */ + ret = audioreach_query_protection_config(graph, + SND_SOC_AR_TPLG_SP_TAG_CFG_TYPE, + MODULE_ID_SPEAKER_PROTECTION); + if (ret) + goto bypass; + ret = audioreach_query_protection_config(graph, + SND_SOC_AR_TPLG_SPVI_TAG_CFG_TYPE, + MODULE_ID_SPEAKER_PROTECTION_VI); + if (ret) + goto bypass; + ret = audioreach_send_inband_stage(graph, dynamic, first_size); + if (ret) + goto bypass; + ret = audioreach_send_oob_stage(graph, + SND_SOC_AR_TPLG_SPVI_TAG_CFG_TYPE); + if (ret) + goto bypass; + ret = audioreach_send_oob_stage(graph, + SND_SOC_AR_TPLG_RENDER_EP_CFG_TYPE); + if (ret) + goto bypass; + ret = audioreach_send_oob_stage(graph, + SND_SOC_AR_TPLG_VI_EP_CFG_TYPE); + if (ret) + goto bypass; + ret = audioreach_protection_enable(graph, true); + if (ret) + goto bypass; + ret = audioreach_configure_protection_volume(graph, gain, mute); + if (ret) + goto bypass; + + ar_graph->protection_configured = true; + ar_graph->protection_bypass_confirmed = false; + goto unlock; + +bypass: + bypass_ret = audioreach_protection_enable(graph, false); + if (!bypass_ret) { + ar_graph->protection_bypass_confirmed = true; + dev_err(graph->dev, + "protected speaker setup failed (%d); using bypass\n", ret); + ret = 0; + } else { + ar_graph->protection_faulted = true; + dev_err(graph->dev, + "protected speaker setup failed (%d), bypass failed (%d)\n", + ret, bypass_ret); + ret = bypass_ret; + } + +unlock: + mutex_unlock(&ar_graph->protection_lock); + return ret; +} + int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_ibasic_rsp_result_t *result, struct mutex *cmd_lock, gpr_port_t *port, wait_queue_head_t *cmd_wait, @@ -610,7 +1380,8 @@ int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, rc = -ETIMEDOUT; } else if (result->status > 0) { dev_err(dev, "DSP returned error[%x] %x\n", hdr->opcode, result->status); - rc = -EINVAL; + rc = result->status == AUDIOREACH_DSP_EUNSUPPORTED ? + -EOPNOTSUPP : -EINVAL; } else { /* DSP successfully finished the command */ rc = 0; @@ -622,12 +1393,83 @@ int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, } EXPORT_SYMBOL_GPL(audioreach_send_cmd_sync); -int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, const struct gpr_pkt *pkt, +int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt, uint32_t rsp_opcode) { + struct gpr_hdr *hdr = &pkt->hdr; + u32 result_status; + bool aborted; + int ret; + + if (!q6apm_graph_user_get(graph)) + return -ESHUTDOWN; + + mutex_lock(&graph->cmd_lock); + if (!++graph->cmd_token) + graph->cmd_token++; + hdr->token = graph->cmd_token; + + spin_lock(&graph->lifecycle_lock); + if (graph->dying || graph->detached) { + spin_unlock(&graph->lifecycle_lock); + ret = -ESHUTDOWN; + goto unlock; + } + spin_lock(&graph->result_lock); + graph->result.opcode = 0; + graph->result.status = 0; + graph->result_token = U32_MAX; + graph->pending_opcode = hdr->opcode; + graph->pending_rsp_opcode = rsp_opcode; + graph->pending_token = hdr->token; + graph->cmd_pending = true; + spin_unlock(&graph->result_lock); + spin_unlock(&graph->lifecycle_lock); + + ret = gpr_send_port_pkt(graph->port, pkt); + if (ret >= 0) + ret = wait_event_timeout(graph->cmd_wait, + !READ_ONCE(graph->cmd_pending) || + (((READ_ONCE(graph->result.opcode) == + hdr->opcode) || + (rsp_opcode && + READ_ONCE(graph->result.opcode) == + rsp_opcode)) && + READ_ONCE(graph->result_token) == + hdr->token), 5 * HZ); + + spin_lock(&graph->result_lock); + aborted = !graph->cmd_pending || READ_ONCE(graph->dying); + if (!ret && graph->result_token == hdr->token && + (graph->result.opcode == hdr->opcode || + (rsp_opcode && graph->result.opcode == rsp_opcode))) + ret = 1; + graph->cmd_pending = false; + result_status = graph->result.status; + spin_unlock(&graph->result_lock); + + if (aborted) { + ret = -ESHUTDOWN; + goto unlock; + } + if (ret < 0) + goto unlock; + if (!ret) { + dev_err(graph->dev, "CMD timeout for [%x] opcode\n", hdr->opcode); + ret = -ETIMEDOUT; + } else if (result_status > 0) { + dev_err(graph->dev, "DSP returned error[%x] %x\n", + hdr->opcode, result_status); + ret = result_status == AUDIOREACH_DSP_EUNSUPPORTED ? + -EOPNOTSUPP : -EINVAL; + } else { + ret = 0; + } - return audioreach_send_cmd_sync(graph->dev, NULL, &graph->result, &graph->lock, - graph->port, &graph->cmd_wait, pkt, rsp_opcode); +unlock: + mutex_unlock(&graph->cmd_lock); + q6apm_graph_user_put(graph); + return ret; } EXPORT_SYMBOL_GPL(audioreach_graph_send_cmd_sync); @@ -843,7 +1685,9 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, int i; void *p; - struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, graph->port->id, + module->instance_id); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -863,7 +1707,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, for (i = 0; i < num_channels; i++) media_format->channel_mapping[i] = cfg->channel_map[i]; - return q6apm_send_cmd_sync(graph->apm, pkt, 0); + return audioreach_graph_send_cmd_sync(graph, pkt, 0); } static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr, @@ -952,15 +1796,21 @@ static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr, int audioreach_compr_set_param(struct q6apm_graph *graph, const struct audioreach_module_config *mcfg) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; struct media_format *header; int rc; void *p; - int iid = graph->shm_iid; + int iid; int payload_size = sizeof(struct apm_sh_module_media_fmt_cmd); + struct gpr_pkt *pkt __free(kfree) = NULL; - struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_cmd_pkt(payload_size, - DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT, - 0, graph->port->id, iid); + if (!active) + return -ESHUTDOWN; + iid = graph->shm_iid; + pkt = audioreach_alloc_cmd_pkt(payload_size, + DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT, + 0, graph->port->id, iid); if (IS_ERR(pkt)) return -ENOMEM; @@ -1089,7 +1939,8 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, payload_size = APM_PCM_MODULE_FMT_CMD_PSIZE(num_channels); struct gpr_pkt *pkt __free(kfree) = - audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + audioreach_alloc_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, graph->port->id, + module->instance_id); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -1115,7 +1966,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph, media_cfg->bits_per_sample = mcfg->bit_width; memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels); - return q6apm_send_cmd_sync(graph->apm, pkt, 0); + return audioreach_graph_send_cmd_sync(graph, pkt, 0); } int audioreach_shmem_register_event(struct q6apm_graph *graph, int bytes, int num_levels) @@ -1400,6 +2251,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph, rc = audioreach_gapless_set_media_format(graph, module, cfg); break; case MODULE_ID_SPEAKER_PROTECTION: + if (module->speaker_protection_bypass) { + rc = 0; + break; + } rc = audioreach_speaker_protection(graph, module, PARAM_ID_SP_OP_MODE_NORMAL); if (!rc) @@ -1407,6 +2262,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph, break; case MODULE_ID_SPEAKER_PROTECTION_VI: + if (module->speaker_protection_bypass) { + rc = 0; + break; + } rc = audioreach_speaker_protection_vi(graph, module, cfg); if (!rc) rc = audioreach_module_enable(graph, module, true); @@ -1446,10 +2305,15 @@ int audioreach_setup_push_pull(struct q6apm_graph *graph, phys_addr_t bphys, struct apm_module_param_data *param_data; int payload_size; struct gpr_pkt *pkt __free(kfree) = NULL; + bool protected = q6apm_graph_is_sp11_pull(graph); void *p; payload_size = sizeof(*cfg) + APM_MODULE_PARAM_DATA_SIZE; - pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + if (protected) + pkt = audioreach_alloc_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0, + graph->port->id, graph->shm_iid); + else + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -1472,16 +2336,27 @@ int audioreach_setup_push_pull(struct q6apm_graph *graph, phys_addr_t bphys, cfg->shared_pos_buf_addr_msw = upper_32_bits(pphys); cfg->pos_buf_mem_map_handle = pos_buf_mem_map_handle; + if (protected) + return audioreach_graph_send_cmd_sync(graph, pkt, 0); + return q6apm_send_cmd_sync(graph->apm, pkt, 0); } EXPORT_SYMBOL_GPL(audioreach_setup_push_pull); int audioreach_shared_memory_send_eos(struct q6apm_graph *graph) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; struct data_cmd_wr_sh_mem_ep_eos *eos; - int iid = graph->shm_iid; - struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_cmd_pkt(sizeof(*eos), - DATA_CMD_WR_SH_MEM_EP_EOS, 0, graph->port->id, iid); + struct gpr_pkt *pkt __free(kfree) = NULL; + int iid; + + if (!active) + return -ESHUTDOWN; + iid = graph->shm_iid; + pkt = audioreach_alloc_cmd_pkt(sizeof(*eos), + DATA_CMD_WR_SH_MEM_EP_EOS, 0, + graph->port->id, iid); if (IS_ERR(pkt)) return PTR_ERR(pkt); diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 62a2fd79bbcb93..22edd8ba743c37 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -3,11 +3,13 @@ #ifndef __AUDIOREACH_H__ #define __AUDIOREACH_H__ #include +#include #include #include #include struct q6apm; struct q6apm_graph; +struct audioreach_graph; /* Module IDs */ #define MODULE_ID_WR_SHARED_MEM_EP 0x07001000 @@ -72,6 +74,8 @@ struct q6apm_graph; #define APM_CMD_CLOSE_ALL 0x01001013 #define APM_CMD_REGISTER_SHARED_CFG 0x0100100A #define EVENT_ID_SH_MEM_PULL_PUSH_MODE_WATERMARK 0x0800101C +#define EVENT_ID_SOFT_PAUSE_PAUSE_COMPLETE 0x0800103F +#define EVENT_ID_SOFT_PAUSE_RESUME_COMPLETE 0x08001043 /** * struct event_cfg_sh_mem_pull_push_mode_watermark_t - Watermark config @@ -157,6 +161,7 @@ struct apm_module_prop_cfg { } __packed; #define APM_PARAM_ID_MODULE_CONN 0x08001004 +#define APM_PARAM_ID_MODULE_CTRL_LINK_CFG 0x08001061 struct apm_param_id_module_conn { uint32_t num_connections; @@ -821,6 +826,11 @@ struct audioreach_graph_info { uint32_t src_mod_op_port_id; uint32_t dst_mod_inst_id; uint32_t dst_mod_ip_port_id; + bool internal_vmixer_connection; + /* Replay state follows the topology, not transient runtime graphs. */ + struct mutex calibration_lock; + bool calibration_replayed; + int calibration_replay_result; }; struct audioreach_sub_graph { @@ -841,6 +851,9 @@ struct audioreach_container { uint32_t graph_pos; uint32_t stack_size; uint32_t proc_domain; + u32 parent_container_id; + u32 heap_id; + bool has_extended_properties; struct list_head node; uint32_t num_modules; @@ -887,12 +900,25 @@ struct audioreach_module { uint32_t log_code; uint32_t log_tap_point_id; uint32_t log_mode; + bool speaker_protection_bypass; + u32 integrated_backend_id; /* bookkeeping */ struct list_head node; struct audioreach_container *container; struct snd_soc_dapm_widget *widget; struct audioreach_module_priv_data *data; + struct audioreach_module_priv_data *ctrl_link_data; + struct audioreach_module_priv_data *graph_cal_data; + struct audioreach_module_priv_data *render_ep_data; + struct audioreach_module_priv_data *sp_tag_data; + struct audioreach_module_priv_data *spvi_tag_data; + struct audioreach_module_priv_data *vi_ep_data; + struct audioreach_module_priv_data *protection_dynamic_data; + struct audioreach_module_priv_data *volume_gain_data; + struct audioreach_module_priv_data *volume_filter_data; + struct audioreach_module_priv_data *volume_mute_data; + struct audioreach_module_priv_data *channel_mixer_data; }; struct audioreach_module_config { @@ -925,6 +951,15 @@ void *audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t dest_port); void *audioreach_alloc_graph_pkt(struct q6apm *apm, const struct audioreach_graph_info *info); +const struct audioreach_module_priv_data * +audioreach_graph_find_data(const struct audioreach_graph_info *info, u32 type); +const struct audioreach_module * +audioreach_graph_find_module(const struct audioreach_graph_info *info, u32 mid); +int audioreach_graph_protection_profile(const struct audioreach_graph_info *info); +int audioreach_graph_protection_oob_size(const struct audioreach_graph_info *info, + size_t *size); +int audioreach_send_protected_graph_calibration(struct audioreach_graph *graph); +int audioreach_configure_protection(struct q6apm_graph *graph); /* Topology specific */ int audioreach_tplg_init(struct snd_soc_component *component); @@ -933,7 +968,7 @@ void audioreach_graph_free_buf(struct q6apm_graph *graph); int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_ibasic_rsp_result_t *result, struct mutex *cmd_lock, gpr_port_t *port, wait_queue_head_t *cmd_wait, const struct gpr_pkt *pkt, uint32_t rsp_opcode); -int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, const struct gpr_pkt *pkt, +int audioreach_graph_send_cmd_sync(struct q6apm_graph *graph, struct gpr_pkt *pkt, uint32_t rsp_opcode); int audioreach_set_media_format(struct q6apm_graph *graph, const struct audioreach_module *module, diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index bf1f872a09f45a..58776e221fd754 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -2,11 +2,15 @@ // Copyright (c) 2021, Linaro Limited #include +#include +#include #include +#include #include #include #include #include +#include #include #include #include @@ -30,6 +34,14 @@ #define CAPTURE_MIN_PERIOD_SIZE 6144 #define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE) #define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE) +#define SP11_PULL_PERIOD_BYTES 1920 +#define SP11_PULL_BUFFER_BYTES 3840 +#define SP11_SOFT_PAUSE_RAMPDOWN_MS 20 +#define SP11_SOFT_PAUSE_DOWNSTREAM_MS 25 +#define SP11_SOFT_PAUSE_MARGIN_MS 5 +#define SP11_SOFT_PAUSE_TIMEOUT_MS (SP11_SOFT_PAUSE_RAMPDOWN_MS + \ + SP11_SOFT_PAUSE_DOWNSTREAM_MS + \ + SP11_SOFT_PAUSE_MARGIN_MS) #define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) #define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) #define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) @@ -55,6 +67,8 @@ enum stream_state { Q6APM_STREAM_IDLE = 0, Q6APM_STREAM_STOPPED, Q6APM_STREAM_RUNNING, + Q6APM_STREAM_UNCERTAIN, + Q6APM_STREAM_QUARANTINED, }; struct q6apm_dai_rtd { @@ -68,6 +82,7 @@ struct q6apm_dai_rtd { phys_addr_t phys; phys_addr_t pos_phys; unsigned int pcm_size; + size_t mapped_pcm_bytes; unsigned int push_pull_size; unsigned int pcm_count; unsigned int periods; @@ -80,13 +95,90 @@ struct q6apm_dai_rtd { enum stream_state state; struct q6apm_graph *graph; spinlock_t lock; + /* Serialize callback admission against terminal DMA quarantine. */ + spinlock_t callback_lock; + atomic_t callback_users; + wait_queue_head_t callback_wait; + bool callbacks_quarantined; bool notify_on_drain; + bool soft_paused; + bool pull_started; + struct completion soft_pause_done; + struct completion soft_resume_done; }; struct q6apm_dai_data { long long sid; + DECLARE_BITMAP(protected_graphs, APM_PORT_MAX); }; +static int q6apm_dai_sp11_soft_pause(struct snd_soc_component *component, + struct q6apm_dai_rtd *prtd, + bool pause); + +static bool q6apm_denali_rtd(const struct snd_soc_pcm_runtime *rtd) +{ + return rtd && rtd->card && + rtd->card->dev->of_node && + of_device_is_compatible(rtd->card->dev->of_node, + "microsoft,denali-sndcard"); +} + +static bool q6apm_denali_card(struct snd_pcm_substream *substream) +{ + return q6apm_denali_rtd(snd_soc_substream_to_rtd(substream)); +} + +static bool q6apm_denali_protection_runtime(struct snd_pcm_substream *substream, + struct q6apm_graph *graph) +{ + return q6apm_denali_card(substream) && + q6apm_graph_has_protection(graph); +} + +static bool q6apm_denali_pull_runtime(struct snd_pcm_substream *substream, + struct q6apm_graph *graph) +{ + return substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + q6apm_denali_protection_runtime(substream, graph) && + q6apm_graph_is_sp11_pull(graph); +} + +static bool q6apm_denali_protected_graph(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd, + unsigned int graph_id) +{ + return q6apm_denali_rtd(rtd) && graph_id < APM_PORT_MAX && + q6apm_graph_id_has_protection(component->dev, graph_id); +} + +static bool q6apm_denali_sp11_pull_graph(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd, + unsigned int graph_id) +{ + return q6apm_denali_rtd(rtd) && graph_id < APM_PORT_MAX && + q6apm_graph_id_is_sp11_pull(component->dev, graph_id); +} + +static void q6apm_latch_protected_graph(struct snd_soc_component *component, + unsigned int graph_id) +{ + struct q6apm_dai_data *pdata = snd_soc_component_get_drvdata(component); + + if (pdata && graph_id < APM_PORT_MAX) + set_bit(graph_id, pdata->protected_graphs); +} + +static bool q6apm_protected_graph_latched(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd, + unsigned int graph_id) +{ + struct q6apm_dai_data *pdata = snd_soc_component_get_drvdata(component); + + return pdata && q6apm_denali_rtd(rtd) && graph_id < APM_PORT_MAX && + test_bit(graph_id, pdata->protected_graphs); +} + static const struct snd_pcm_hardware q6apm_dai_hardware_capture = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | @@ -127,14 +219,71 @@ static const struct snd_pcm_hardware q6apm_dai_hardware_playback = { .fifo_size = 0, }; +static const struct snd_pcm_hardware q6apm_dai_hardware_sp11_pull = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_REWINDS | + SNDRV_PCM_INFO_SYNC_APPLPTR | + SNDRV_PCM_INFO_BATCH), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = SP11_PULL_BUFFER_BYTES, + .period_bytes_min = SP11_PULL_PERIOD_BYTES, + .period_bytes_max = SP11_PULL_PERIOD_BYTES, + .periods_min = 2, + .periods_max = 2, + .fifo_size = 0, +}; + +static bool q6apm_dai_callback_get(struct q6apm_dai_rtd *prtd) +{ + unsigned long flags; + bool acquired = false; + + spin_lock_irqsave(&prtd->callback_lock, flags); + if (!prtd->callbacks_quarantined) { + atomic_inc(&prtd->callback_users); + acquired = true; + } + spin_unlock_irqrestore(&prtd->callback_lock, flags); + + return acquired; +} + +static void q6apm_dai_callback_put(struct q6apm_dai_rtd *prtd) +{ + if (atomic_dec_and_test(&prtd->callback_users)) + wake_up(&prtd->callback_wait); +} + static void event_handler(uint32_t opcode, uint32_t token, void *payload, void *priv) { struct q6apm_dai_rtd *prtd = priv; - struct snd_pcm_substream *substream = prtd->substream; + struct snd_pcm_substream *substream; + + if (!q6apm_dai_callback_get(prtd)) + return; + + substream = prtd->substream; switch (opcode) { case APM_CLIENT_EVENT_WATERMARK_EVENT: - snd_pcm_period_elapsed(substream); + if (prtd->state == Q6APM_STREAM_RUNNING) + snd_pcm_period_elapsed(substream); + break; + case APM_CLIENT_EVENT_SOFT_PAUSE_COMPLETE: + complete(&prtd->soft_pause_done); + break; + case APM_CLIENT_EVENT_SOFT_RESUME_COMPLETE: + complete(&prtd->soft_resume_done); break; case APM_CLIENT_EVENT_CMD_EOS_DONE: prtd->state = Q6APM_STREAM_STOPPED; @@ -152,6 +301,8 @@ static void event_handler(uint32_t opcode, uint32_t token, void *payload, void * default: break; } + + q6apm_dai_callback_put(prtd); } static void event_handler_compr(uint32_t opcode, uint32_t token, @@ -219,6 +370,8 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, struct audioreach_module_config cfg; struct device *dev = component->dev; struct q6apm_dai_data *pdata; + bool protected; + bool protected_pull; int ret; pdata = snd_soc_component_get_drvdata(component); @@ -229,6 +382,31 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, dev_err(dev, "%s: private data null or audio client freed\n", __func__); return -EINVAL; } + if (prtd->state == Q6APM_STREAM_QUARANTINED) + return -EIO; + protected = q6apm_denali_protection_runtime(substream, prtd->graph); + protected_pull = q6apm_denali_pull_runtime(substream, prtd->graph); + if (protected_pull && + (runtime->rate != 48000 || runtime->channels != 2 || + prtd->bits_per_sample != 16 || + prtd->pcm_size != SP11_PULL_BUFFER_BYTES || + snd_pcm_lib_period_bytes(substream) != SP11_PULL_PERIOD_BYTES || + prtd->periods != 2)) { + dev_err(dev, "Denali pull PCM geometry is invalid\n"); + return -EINVAL; + } + if (protected_pull && prtd->pull_started && + prtd->state != Q6APM_STREAM_UNCERTAIN) { + if (prtd->soft_paused) { + ret = q6apm_dai_sp11_soft_pause(component, prtd, false); + if (ret) + return ret; + prtd->soft_paused = false; + } + prtd->state = Q6APM_STREAM_RUNNING; + dev_dbg(dev, "reusing persistent Denali pull graph\n"); + return 0; + } cfg.direction = substream->stream; cfg.sample_rate = runtime->rate; @@ -238,7 +416,20 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, audioreach_set_default_channel_mapping(cfg.channel_map, runtime->channels); if (prtd->state) { /* clear the previous setup if any */ - q6apm_graph_stop(prtd->graph); + if (!protected || prtd->state == Q6APM_STREAM_RUNNING || + prtd->state == Q6APM_STREAM_UNCERTAIN) { + ret = q6apm_graph_stop(prtd->graph); + if (ret) { + dev_err(dev, "Failed to stop Graph %d\n", ret); + return ret; + } + prtd->state = Q6APM_STREAM_STOPPED; + if (protected_pull) { + prtd->pull_started = false; + prtd->push_pull_size = 0; + prtd->soft_paused = false; + } + } q6apm_free_fragments(prtd->graph, substream->stream); } @@ -260,6 +451,15 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, dev_err(dev, "WaterMark event config failed rc = %d\n", ret); return ret; } + if (protected_pull) { + ret = q6apm_register_sp11_soft_pause_events(prtd->graph); + if (ret < 0) { + dev_err(dev, + "soft-pause event registration failed: %d\n", + ret); + return ret; + } + } prtd->push_pull_size = prtd->pcm_size; } } else { @@ -272,6 +472,14 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, } + if (protected_pull) { + ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg); + if (ret < 0) { + dev_err(dev, "Failed to set pull media format %d\n", ret); + return ret; + } + } + ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg); if (ret < 0) { dev_err(dev, "%s: CMD Format block failed\n", __func__); @@ -279,12 +487,21 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, } /* rate and channels are sent to audio driver */ - ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg); + ret = protected_pull ? 0 : + q6apm_graph_media_format_shmem(prtd->graph, &cfg); if (ret < 0) { dev_err(dev, "Failed to set media format %d\n", ret); return ret; } + if (protected) { + ret = q6apm_graph_configure_protection(prtd->graph); + if (ret < 0) { + dev_err(dev, "Failed to configure protected graph %d\n", ret); + return ret; + } + } + ret = q6apm_graph_prepare(prtd->graph); if (ret) { dev_err(dev, "Failed to prepare Graph %d\n", ret); @@ -293,6 +510,8 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, ret = q6apm_graph_start(prtd->graph); if (ret) { + if (q6apm_graph_execution_uncertain(prtd->graph)) + prtd->state = Q6APM_STREAM_UNCERTAIN; dev_err(dev, "Failed to Start Graph %d\n", ret); return ret; } @@ -307,6 +526,8 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, /* Now that graph as been prepared and started update the internal state accordingly */ prtd->state = Q6APM_STREAM_RUNNING; + if (protected_pull) + prtd->pull_started = true; return 0; } @@ -335,26 +556,107 @@ static int q6apm_dai_ack(struct snd_soc_component *component, struct snd_pcm_sub return ret; } +static int q6apm_dai_sp11_soft_pause(struct snd_soc_component *component, + struct q6apm_dai_rtd *prtd, + bool pause) +{ + struct completion *done = pause ? &prtd->soft_pause_done : + &prtd->soft_resume_done; + unsigned long timeout; + int ret; + + if (!q6apm_graph_is_sp11_pull(prtd->graph)) + return -EOPNOTSUPP; + + /* Suppress watermark callbacks while trigger() holds the PCM lock. */ + if (pause) + prtd->state = Q6APM_STREAM_STOPPED; + + reinit_completion(done); + ret = q6apm_graph_sp11_soft_pause(prtd->graph, pause); + if (ret) { + if (pause) + prtd->state = Q6APM_STREAM_RUNNING; + return ret; + } + + prtd->soft_paused = pause; + timeout = wait_for_completion_timeout(done, msecs_to_jiffies(SP11_SOFT_PAUSE_TIMEOUT_MS)); + if (!timeout) { + prtd->state = Q6APM_STREAM_UNCERTAIN; + dev_err(component->dev, "Denali soft-%s completion timed out\n", + pause ? "pause" : "resume"); + return -ETIMEDOUT; + } + + return 0; +} + static int q6apm_dai_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; struct q6apm_dai_rtd *prtd = runtime->private_data; + bool protected; + bool protected_pull; int ret = 0; + protected = q6apm_denali_protection_runtime(substream, prtd->graph); + protected_pull = q6apm_denali_pull_runtime(substream, prtd->graph); + if (prtd->state == Q6APM_STREAM_QUARANTINED) + return -EIO; switch (cmd) { case SNDRV_PCM_TRIGGER_START: + break; case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (protected_pull && prtd->soft_paused) { + ret = q6apm_dai_sp11_soft_pause(component, prtd, false); + if (!ret) { + prtd->soft_paused = false; + prtd->state = Q6APM_STREAM_RUNNING; + } + } break; case SNDRV_PCM_TRIGGER_STOP: - /* TODO support be handled via SoftPause Module */ + if (protected_pull) { + if (prtd->state == Q6APM_STREAM_UNCERTAIN) { + ret = q6apm_graph_stop(prtd->graph); + if (ret) + break; + prtd->pull_started = false; + prtd->push_pull_size = 0; + prtd->soft_paused = false; + } else if (prtd->soft_paused) { + ret = q6apm_dai_sp11_soft_pause(component, prtd, false); + if (ret) + break; + prtd->soft_paused = false; + } + prtd->state = Q6APM_STREAM_STOPPED; + prtd->queue_ptr = 0; + prtd->last_pos_index = 0; + break; + } + if (protected && + (prtd->state == Q6APM_STREAM_RUNNING || + prtd->state == Q6APM_STREAM_UNCERTAIN)) { + ret = q6apm_graph_stop(prtd->graph); + if (ret) + break; + } prtd->state = Q6APM_STREAM_STOPPED; prtd->queue_ptr = 0; prtd->last_pos_index = 0; break; case SNDRV_PCM_TRIGGER_SUSPEND: + break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (protected_pull && !prtd->soft_paused) { + ret = q6apm_dai_sp11_soft_pause(component, prtd, true); + if (!ret) + prtd->state = Q6APM_STREAM_STOPPED; + } break; default: ret = -EINVAL; @@ -364,6 +666,11 @@ static int q6apm_dai_trigger(struct snd_soc_component *component, return ret; } +static void q6apm_dai_quarantine_buffer(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct q6apm_dai_rtd *prtd, + int reason); + static int q6apm_dai_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -373,7 +680,8 @@ static int q6apm_dai_open(struct snd_soc_component *component, struct device *dev = component->dev; struct q6apm_dai_data *pdata; struct q6apm_dai_rtd *prtd; - int graph_id, ret; + bool protected_pull; + int close_ret, graph_id, ret; graph_id = cpu_dai->driver->id; @@ -388,16 +696,25 @@ static int q6apm_dai_open(struct snd_soc_component *component, return -ENOMEM; spin_lock_init(&prtd->lock); + spin_lock_init(&prtd->callback_lock); + atomic_set(&prtd->callback_users, 0); + init_waitqueue_head(&prtd->callback_wait); + init_completion(&prtd->soft_pause_done); + init_completion(&prtd->soft_resume_done); prtd->substream = substream; - prtd->graph = q6apm_graph_open(dev, event_handler, prtd, graph_id, substream->stream); + prtd->graph = q6apm_graph_open(dev, event_handler, prtd, + graph_id, substream->stream, + q6apm_denali_card(substream)); if (IS_ERR(prtd->graph)) { dev_err(dev, "%s: Could not allocate memory\n", __func__); ret = PTR_ERR(prtd->graph); goto err; } + protected_pull = q6apm_denali_pull_runtime(substream, prtd->graph); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - runtime->hw = q6apm_dai_hardware_playback; + runtime->hw = protected_pull ? q6apm_dai_hardware_sp11_pull : + q6apm_dai_hardware_playback; else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) runtime->hw = q6apm_dai_hardware_capture; @@ -408,7 +725,7 @@ static int q6apm_dai_open(struct snd_soc_component *component, goto err; } - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !protected_pull) { ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, BUFFER_BYTES_MIN, BUFFER_BYTES_MAX); if (ret < 0) { @@ -431,45 +748,143 @@ static int q6apm_dai_open(struct snd_soc_component *component, } runtime->private_data = prtd; - runtime->dma_bytes = BUFFER_BYTES_MAX; + prtd->mapped_pcm_bytes = protected_pull ? PAGE_SIZE : BUFFER_BYTES_MAX; + runtime->dma_bytes = prtd->mapped_pcm_bytes; if (pdata->sid < 0) prtd->phys = substream->dma_buffer.addr; else prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32); - if (q6apm_is_graph_in_push_pull_mode(prtd->graph)) { + if (q6apm_is_graph_in_push_pull_mode(prtd->graph) && !protected_pull) { void *pos_buffer; - prtd->pos_phys = prtd->phys + BUFFER_BYTES_MAX; - pos_buffer = (void *)(substream->dma_buffer.area + BUFFER_BYTES_MAX); + prtd->pos_phys = prtd->phys + prtd->mapped_pcm_bytes; + pos_buffer = (void *)(substream->dma_buffer.area + + prtd->mapped_pcm_bytes); prtd->pos_buffer = (struct sh_mem_pull_push_mode_position_buffer *)(pos_buffer); } return 0; err: + if (!IS_ERR_OR_NULL(prtd->graph)) { + close_ret = q6apm_graph_close(prtd->graph); + if (close_ret) { + q6apm_dai_quarantine_buffer(component, substream, prtd, + close_ret); + return ret; + } + } kfree(prtd); return ret; } +static void q6apm_dai_quarantine_buffer(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct q6apm_dai_rtd *prtd, int reason) +{ + unsigned long flags; + + if (prtd->state == Q6APM_STREAM_QUARANTINED) + return; + + spin_lock_irqsave(&prtd->callback_lock, flags); + prtd->callbacks_quarantined = true; + spin_unlock_irqrestore(&prtd->callback_lock, flags); + wait_event(prtd->callback_wait, + !atomic_read(&prtd->callback_users)); + + q6apm_graph_quarantine_dma(prtd->graph); + snd_pcm_set_runtime_buffer(substream, NULL); + /* DSP ownership is uncertain, so ALSA must never reclaim mapped pages. */ + if (substream->dma_buffer.area && substream->dma_buffer.dev.dev) + get_device(substream->dma_buffer.dev.dev); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + substream->dma_buffer.bytes = 0; + prtd->state = Q6APM_STREAM_QUARANTINED; + dev_err(component->dev, + "retaining protected client and DMA buffer after unconfirmed stop (%d)\n", + reason); +} + +static int q6apm_dai_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct q6apm_dai_rtd *prtd = substream->runtime->private_data; + bool protected; + int ret; + + if (!prtd) + return 0; + protected = q6apm_denali_protection_runtime(substream, prtd->graph); + if (prtd->state == Q6APM_STREAM_QUARANTINED) + return -EIO; + if (q6apm_denali_pull_runtime(substream, prtd->graph) && + prtd->state != Q6APM_STREAM_UNCERTAIN) + return 0; + if (prtd->state != Q6APM_STREAM_UNCERTAIN && + (!protected || prtd->state != Q6APM_STREAM_RUNNING)) + return 0; + + ret = q6apm_graph_stop(prtd->graph); + if (ret) { + q6apm_dai_quarantine_buffer(component, substream, prtd, ret); + return ret; + } + + prtd->state = Q6APM_STREAM_STOPPED; + if (q6apm_denali_pull_runtime(substream, prtd->graph)) { + prtd->pull_started = false; + prtd->push_pull_size = 0; + prtd->soft_paused = false; + } + return 0; +} + static int q6apm_dai_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct q6apm_dai_rtd *prtd = runtime->private_data; + bool protected; + bool protected_pull; + int close_ret; + int ret = 0; - if (prtd->state) { + protected = q6apm_denali_protection_runtime(substream, prtd->graph); + protected_pull = q6apm_denali_pull_runtime(substream, prtd->graph); + if (prtd->state == Q6APM_STREAM_QUARANTINED) { + ret = -EIO; + } else if (protected_pull && prtd->pull_started) { + ret = q6apm_graph_stop(prtd->graph); + if (!ret) { + prtd->pull_started = false; + prtd->push_pull_size = 0; + } + } else if (prtd->state) { /* only stop graph that is started */ - q6apm_graph_stop(prtd->graph); - q6apm_free_fragments(prtd->graph, substream->stream); + if (!protected || prtd->state == Q6APM_STREAM_RUNNING || + prtd->state == Q6APM_STREAM_UNCERTAIN) { + ret = q6apm_graph_stop(prtd->graph); + } + if (!ret && !q6apm_is_graph_in_push_pull_mode(prtd->graph)) + q6apm_free_fragments(prtd->graph, substream->stream); + } + if (ret && q6apm_graph_execution_uncertain(prtd->graph)) + q6apm_dai_quarantine_buffer(component, substream, prtd, ret); + + close_ret = q6apm_graph_close(prtd->graph); + if (close_ret && prtd->state != Q6APM_STREAM_QUARANTINED) + q6apm_dai_quarantine_buffer(component, substream, prtd, + close_ret); + if (prtd->state != Q6APM_STREAM_QUARANTINED) { + prtd->graph = NULL; + kfree(prtd); } - - q6apm_graph_close(prtd->graph); - prtd->graph = NULL; - kfree(prtd); runtime->private_data = NULL; - return 0; + return ret ?: close_ret; } static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component, @@ -479,6 +894,9 @@ static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component, struct q6apm_dai_rtd *prtd = runtime->private_data; snd_pcm_uframes_t ptr; + if (q6apm_denali_pull_runtime(substream, prtd->graph)) + return q6apm_get_pull_hw_pointer(prtd->graph, runtime); + if (q6apm_is_graph_in_push_pull_mode(prtd->graph)) { int retries = 10; uint32_t index, fc1, fc2; @@ -531,10 +949,13 @@ static int q6apm_dai_hw_params(struct snd_soc_component *component, static int q6apm_dai_memory_map(struct snd_soc_component *component, struct snd_pcm_substream *substream, - int graph_id, bool is_push_pull) + int graph_id, bool is_push_pull, + bool sp11_pull, bool protected, + bool *retain_buffer) { struct q6apm_dai_data *pdata; struct device *dev = component->dev; + size_t map_size; phys_addr_t phys; int ret; @@ -549,19 +970,32 @@ static int q6apm_dai_memory_map(struct snd_soc_component *component, else phys = substream->dma_buffer.addr | (pdata->sid << 32); - ret = q6apm_map_memory_fixed_region(dev, graph_id, phys, BUFFER_BYTES_MAX); - if (ret < 0) - dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); + map_size = sp11_pull ? PAGE_SIZE : BUFFER_BYTES_MAX; + ret = q6apm_map_memory_fixed_region(dev, graph_id, phys, map_size); + if (ret < 0) { + *retain_buffer = protected && + (ret == -ETIMEDOUT || ret == -ESHUTDOWN); + dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); + return ret; + } - if (is_push_pull) { + if (is_push_pull && !sp11_pull) { if (pdata->sid < 0) - phys = substream->dma_buffer.addr + BUFFER_BYTES_MAX; + phys = substream->dma_buffer.addr + map_size; else - phys = (substream->dma_buffer.addr + BUFFER_BYTES_MAX) | (pdata->sid << 32); + phys = (substream->dma_buffer.addr + map_size) | + (pdata->sid << 32); ret = q6apm_map_pos_buffer(dev, graph_id, phys, POS_BUFFER_BYTES); - if (ret < 0) - dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", ret); + if (ret < 0) { + int unmap_ret; + + dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n", + ret); + unmap_ret = q6apm_unmap_memory_fixed_region(dev, graph_id); + *retain_buffer = protected && + (ret == -ETIMEDOUT || unmap_ret); + } } else { } @@ -569,6 +1003,26 @@ static int q6apm_dai_memory_map(struct snd_soc_component *component, return ret; } +static void q6apm_dai_retain_dma_buffer(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + int reason) +{ + if (!substream->dma_buffer.area) + return; + + if (substream->runtime) + snd_pcm_set_runtime_buffer(substream, NULL); + /* Retain until reboot or a future, proven DSP-reset reclamation hook. */ + if (substream->dma_buffer.dev.dev) + get_device(substream->dma_buffer.dev.dev); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + substream->dma_buffer.bytes = 0; + dev_err(component->dev, + "retaining DMA buffer after uncertain DSP mapping (%d)\n", + reason); +} + static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); @@ -578,9 +1032,12 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc * address arithmetic can overflow when the buffer is placed near the * end of the addressable range. */ - int size = BUFFER_BYTES_MAX + PAGE_SIZE; + int size; int graph_id, ret; bool is_push_pull; + bool protected; + bool sp11_pull; + bool retain_buffer = false; struct snd_pcm_substream *substream = NULL; graph_id = cpu_dai->driver->id; @@ -593,44 +1050,83 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc if (substream) { + protected = q6apm_denali_protected_graph(component, rtd, + graph_id); + if (protected) + q6apm_latch_protected_graph(component, graph_id); is_push_pull = q6apm_is_graph_in_push_pull_mode_from_id(component->dev, graph_id, substream->stream); - if (is_push_pull) + sp11_pull = q6apm_denali_sp11_pull_graph(component, rtd, graph_id) && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + size = sp11_pull ? PAGE_SIZE : BUFFER_BYTES_MAX; + size += PAGE_SIZE; + if (is_push_pull && !sp11_pull) size += POS_BUFFER_BYTES; ret = snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); if (ret) return ret; - ret = q6apm_dai_memory_map(component, substream, graph_id, is_push_pull); - if (ret) + ret = q6apm_dai_memory_map(component, substream, + graph_id, is_push_pull, + sp11_pull, protected, + &retain_buffer); + if (ret) { + if (retain_buffer) + q6apm_dai_retain_dma_buffer(component, + substream, ret); return ret; + } } return 0; } -static void q6apm_dai_memory_unmap(struct snd_soc_component *component, - struct snd_pcm_substream *substream) +static int q6apm_dai_memory_unmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_prtd; struct snd_soc_dai *cpu_dai; int graph_id; + int ret; soc_prtd = snd_soc_substream_to_rtd(substream); if (!soc_prtd) - return; + return 0; cpu_dai = snd_soc_rtd_to_cpu(soc_prtd, 0); if (!cpu_dai) - return; + return 0; graph_id = cpu_dai->driver->id; - q6apm_unmap_memory_fixed_region(component->dev, graph_id); + if (q6apm_graph_dma_quarantined(component->dev, graph_id)) + return -EBUSY; + + ret = q6apm_unmap_memory_fixed_region(component->dev, graph_id); + if (ret) + return ret; if (q6apm_is_graph_in_push_pull_mode_from_id(component->dev, graph_id, substream->stream)) - q6apm_unmap_pos_buffer(component->dev, graph_id); + return q6apm_unmap_pos_buffer(component->dev, graph_id); + + return 0; +} + +static bool q6apm_dai_protected_substream(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai; + + if (!rtd) + return false; + cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + if (!cpu_dai) + return false; + + return q6apm_protected_graph_latched(component, rtd, + cpu_dai->driver->id); } static void q6apm_dai_pcm_free(struct snd_soc_component *component, struct snd_pcm *pcm) @@ -638,12 +1134,20 @@ static void q6apm_dai_pcm_free(struct snd_soc_component *component, struct snd_p struct snd_pcm_substream *substream; substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - if (substream) - q6apm_dai_memory_unmap(component, substream); + if (substream) { + int ret = q6apm_dai_memory_unmap(component, substream); + + if (ret && q6apm_dai_protected_substream(component, substream)) + q6apm_dai_retain_dma_buffer(component, substream, ret); + } substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - if (substream) - q6apm_dai_memory_unmap(component, substream); + if (substream) { + int ret = q6apm_dai_memory_unmap(component, substream); + + if (ret && q6apm_dai_protected_substream(component, substream)) + q6apm_dai_retain_dma_buffer(component, substream, ret); + } } static int q6apm_dai_compr_open(struct snd_soc_component *component, @@ -669,7 +1173,7 @@ static int q6apm_dai_compr_open(struct snd_soc_component *component, prtd->cstream = stream; prtd->graph = q6apm_graph_open(dev, event_handler_compr, prtd, graph_id, - SNDRV_PCM_STREAM_PLAYBACK); + SNDRV_PCM_STREAM_PLAYBACK, false); if (IS_ERR(prtd->graph)) { ret = PTR_ERR(prtd->graph); kfree(prtd); @@ -680,8 +1184,14 @@ static int q6apm_dai_compr_open(struct snd_soc_component *component, runtime->dma_bytes = BUFFER_BYTES_MAX; size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &prtd->dma_buffer); - if (ret) + if (ret) { + if (!q6apm_graph_close(prtd->graph)) { + prtd->graph = NULL; + runtime->private_data = NULL; + kfree(prtd); + } return ret; + } if (pdata->sid < 0) prtd->phys = prtd->dma_buffer.addr; @@ -1009,6 +1519,7 @@ static const struct snd_soc_component_driver q6apm_fe_dai_component = { .pcm_new = q6apm_dai_pcm_new, .pcm_free = q6apm_dai_pcm_free, .hw_params = q6apm_dai_hw_params, + .hw_free = q6apm_dai_hw_free, .pointer = q6apm_dai_pointer, .trigger = q6apm_dai_trigger, .ack = q6apm_dai_ack, @@ -1017,27 +1528,62 @@ static const struct snd_soc_component_driver q6apm_fe_dai_component = { .remove_order = SND_SOC_COMP_ORDER_EARLY, }; +static void q6apm_dai_release_dma_dev(void *data) +{ + struct device *dev = data; + struct q6apm *apm = dev_get_drvdata(dev->parent); + + if (apm) { + mutex_lock(&apm->lock); + if (apm->dma_dev == dev) + apm->dma_dev = NULL; + mutex_unlock(&apm->lock); + } + put_device(dev); +} + static int q6apm_dai_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct q6apm_dai_data *pdata; + struct q6apm *apm; struct of_phandle_args args; int rc; + apm = dev_get_drvdata(dev->parent); + if (!apm) + return -EPROBE_DEFER; + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args); - if (rc < 0) + if (rc < 0) { pdata->sid = -1; - else + } else { pdata->sid = args.args[0] & SID_MASK_DEFAULT; + of_node_put(args.np); + } dev_set_drvdata(dev, pdata); + rc = 0; + mutex_lock(&apm->lock); + if (apm->dma_dev) + rc = -EBUSY; + else + apm->dma_dev = get_device(dev); + mutex_unlock(&apm->lock); + if (rc) + return rc; + + rc = devm_add_action_or_reset(dev, q6apm_dai_release_dma_dev, dev); + if (rc) + return rc; - return devm_snd_soc_register_component(dev, &q6apm_fe_dai_component, NULL, 0); + return devm_snd_soc_register_component(dev, &q6apm_fe_dai_component, + NULL, 0); } #ifdef CONFIG_OF diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 006b283484d9e9..e7c70159f5fff8 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,28 @@ struct q6apm_lpass_dai_data { struct audioreach_module_config module_config[APM_PORT_MAX]; }; +static bool q6apm_lpass_protection_runtime(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + + return rtd && rtd->card && rtd->card->dev->of_node && + of_device_is_compatible(rtd->card->dev->of_node, + "microsoft,denali-sndcard"); +} + +static int q6apm_lpass_graph_id(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + + if (!rtd || !rtd->card || !rtd->card->dev->of_node || + !of_device_is_compatible(rtd->card->dev->of_node, + "microsoft,denali-sndcard")) + return dai->id; + + return q6apm_graph_id_for_backend(dai->dev, dai->id); +} + static int q6dma_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, const unsigned int *tx_ch_mask, @@ -148,13 +171,26 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct if (dai_data->is_port_started[dai->id]) { rc = q6apm_graph_stop(dai_data->graph[dai->id]); - dai_data->is_port_started[dai->id] = false; - if (rc < 0) + if (rc < 0) { + if (rc == -ESHUTDOWN && + !q6apm_graph_close(dai_data->graph[dai->id])) { + dai_data->graph[dai->id] = NULL; + dai_data->is_port_started[dai->id] = false; + return; + } dev_err(dai->dev, "failed to stop APM port (%d)\n", rc); + return; + } + dai_data->is_port_started[dai->id] = false; } if (dai_data->graph[dai->id]) { - q6apm_graph_close(dai_data->graph[dai->id]); + rc = q6apm_graph_close(dai_data->graph[dai->id]); + if (rc) { + dev_err(dai->dev, + "retaining uncertain APM port (%d)\n", rc); + return; + } dai_data->graph[dai->id] = NULL; } } @@ -171,10 +207,25 @@ static int q6apm_lpass_dai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (!dai_data->is_port_started[dai->id]) { ret = q6apm_graph_start(dai_data->graph[dai->id]); - if (ret < 0) + if (ret < 0) { + if (q6apm_graph_execution_uncertain(dai_data->graph[dai->id])) + dai_data->is_port_started[dai->id] = true; dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); - else + } else { dai_data->is_port_started[dai->id] = true; + } + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (dai_data->is_port_started[dai->id] && + q6apm_graph_has_protection(dai_data->graph[dai->id]) && + !q6apm_graph_is_sp11_pull(dai_data->graph[dai->id])) { + ret = q6apm_graph_stop(dai_data->graph[dai->id]); + if (ret < 0) + dev_err(dai->dev, "Failed to stop APM port %d\n", + dai->id); + else + dai_data->is_port_started[dai->id] = false; } break; default: @@ -189,13 +240,21 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; struct q6apm_graph *graph; - int graph_id = dai->id; + int graph_id = q6apm_lpass_graph_id(substream, dai); int rc; + if (graph_id < 0) + return graph_id; + if (dai_data->is_port_started[dai->id]) { - q6apm_graph_stop(dai_data->graph[dai->id]); + if (q6apm_graph_is_sp11_pull(dai_data->graph[dai->id])) + return 0; + rc = q6apm_graph_stop(dai_data->graph[dai->id]); + if (rc < 0) { + dev_err(dai->dev, "failed to stop APM port (%d)\n", rc); + return rc; + } dai_data->is_port_started[dai->id] = false; - } /** @@ -203,14 +262,18 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * graph, so sequence for playback and capture will be different */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { - graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, substream->stream); + graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, + substream->stream, + q6apm_lpass_protection_runtime(substream)); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); rc = PTR_ERR(graph); return rc; } - dai_data->graph[graph_id] = graph; + dai_data->graph[dai->id] = graph; } + if (q6apm_graph_has_protection(dai_data->graph[dai->id])) + return 0; cfg->direction = substream->stream; rc = q6apm_graph_media_format_pcm(dai_data->graph[dai->id], cfg); @@ -227,8 +290,11 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s return 0; err: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - q6apm_graph_close(dai_data->graph[dai->id]); - dai_data->graph[dai->id] = NULL; + int close_ret; + + close_ret = q6apm_graph_close(dai_data->graph[dai->id]); + if (!close_ret) + dai_data->graph[dai->id] = NULL; } return rc; } @@ -237,15 +303,20 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); struct q6apm_graph *graph; - int graph_id = dai->id; + int graph_id = q6apm_lpass_graph_id(substream, dai); + + if (graph_id < 0) + return graph_id; - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE || graph_id != dai->id) { + graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, + substream->stream, + q6apm_lpass_protection_runtime(substream)); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); return PTR_ERR(graph); } - dai_data->graph[graph_id] = graph; + dai_data->graph[dai->id] = graph; } return 0; @@ -316,6 +387,8 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) cfg.q6dma_ops = &q6dma_ops; cfg.q6hdmi_ops = &q6hdmi_ops; dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais); + if (IS_ERR(dais)) + return PTR_ERR(dais); return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais); } diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 641d6d24322990..b9e91231237340 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -11,14 +12,30 @@ #include #include #include +#include #include #include #include #include #include "audioreach.h" #include "q6apm.h" +#include "q6dsp-errno.h" /* Graph Management */ +#define APM_MMAP_TOKEN_MAP_TYPE_OOB BIT(17) +#define APM_MMAP_TOKEN_MAP_TYPE_GRAPH_POS BIT(30) +#define APM_MMAP_TOKEN_OOB_SEQ_SHIFT 18 +#define APM_MMAP_TOKEN_OOB_SEQ_MASK GENMASK(31, 18) +#define Q6APM_DSP_SID_MASK GENMASK(3, 0) +#define Q6APM_POSITION_MAP_UNCACHED BIT(1) + +/* Windows DEFAULT soft-pause module in the qualified SP11 graph. */ +#define SP11_MODULE_ID_SOFT_PAUSE 0x07001019 +#define SP11_PULL_IID 0x00004660 +#define SP11_SOFT_PAUSE_IID 0x0000466b +#define SP11_PARAM_SOFT_PAUSE_PAUSE 0x0800102e +#define SP11_PARAM_SOFT_PAUSE_RESUME 0x0800102f + struct apm_graph_mgmt_cmd { struct apm_module_param_data param_data; uint32_t num_sub_graphs; @@ -28,68 +45,707 @@ struct apm_graph_mgmt_cmd { #define APM_GRAPH_MGMT_PSIZE(p, n) ALIGN(struct_size(p, sub_graph_id_list, n), 8) static struct q6apm *g_apm; +static DEFINE_MUTEX(g_apm_lock); +static atomic_t q6apm_mmap_token_seq = ATOMIC_INIT(0); + +static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, + u32 opcode); +static struct audioreach_module * +__q6apm_find_module_by_mid(struct q6apm *apm, + struct audioreach_graph_info *info, u32 mid); + +static bool q6apm_is_graph_position_token(u32 token) +{ + return (token & ~APM_MMAP_TOKEN_GID_MASK) == + APM_MMAP_TOKEN_MAP_TYPE_GRAPH_POS; +} + +bool q6apm_graph_user_get(struct q6apm_graph *graph) +{ + unsigned long flags; + bool acquired = false; + + if (!graph) + return false; + + spin_lock_irqsave(&graph->lifecycle_lock, flags); + if (!graph->dying && !graph->detached) { + graph->active_users++; + acquired = true; + } + spin_unlock_irqrestore(&graph->lifecycle_lock, flags); + + return acquired; +} + +void q6apm_graph_user_put(struct q6apm_graph *graph) +{ + unsigned long flags; + bool wake = false; + + if (!graph) + return; + + spin_lock_irqsave(&graph->lifecycle_lock, flags); + if (WARN_ON(!graph->active_users)) { + spin_unlock_irqrestore(&graph->lifecycle_lock, flags); + return; + } + wake = --graph->active_users == 0; + spin_unlock_irqrestore(&graph->lifecycle_lock, flags); -int q6apm_send_cmd_sync(struct q6apm *apm, const struct gpr_pkt *pkt, + if (wake) + wake_up(&graph->users_wait); +} + +DEFINE_FREE(q6apm_graph_user, struct q6apm_graph *, q6apm_graph_user_put(_T)) + +static void q6apm_graph_abort_cmd(struct q6apm_graph *graph) +{ + spin_lock(&graph->result_lock); + graph->cmd_pending = false; + spin_unlock(&graph->result_lock); + wake_up(&graph->cmd_wait); +} + +static void q6apm_graph_mark_dying(struct q6apm_graph *graph) +{ + unsigned long flags; + + spin_lock_irqsave(&graph->lifecycle_lock, flags); + graph->dying = true; + spin_unlock_irqrestore(&graph->lifecycle_lock, flags); + q6apm_graph_abort_cmd(graph); +} + +static void q6apm_graph_wait_users(struct q6apm_graph *graph) +{ + wait_event(graph->users_wait, !READ_ONCE(graph->active_users)); +} + +int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode) { + struct gpr_hdr *hdr = &pkt->hdr; gpr_device_t *gdev = apm->gdev; + u32 result_status; + bool aborted; + int ret; + + mutex_lock(&apm->lock); + if (READ_ONCE(apm->removing)) { + ret = -ESHUTDOWN; + goto unlock; + } + if (!hdr->token) { + do { + hdr->token = ++apm->cmd_token; + } while (!hdr->token); + } + spin_lock(&apm->result_lock); + apm->result.opcode = 0; + apm->result.status = 0; + apm->result_token = U32_MAX; + apm->pending_opcode = hdr->opcode; + apm->pending_rsp_opcode = rsp_opcode; + apm->pending_token = hdr->token; + apm->cmd_pending = true; + spin_unlock(&apm->result_lock); + + ret = gpr_send_pkt(gdev, pkt); + if (ret >= 0) + ret = wait_event_timeout(apm->wait, + !READ_ONCE(apm->cmd_pending) || + (((READ_ONCE(apm->result.opcode) == + hdr->opcode) || + (rsp_opcode && + READ_ONCE(apm->result.opcode) == + rsp_opcode)) && + READ_ONCE(apm->result_token) == + hdr->token), 5 * HZ); + + spin_lock(&apm->result_lock); + aborted = !apm->cmd_pending || apm->removing; + if (!ret && apm->result_token == hdr->token && + (apm->result.opcode == hdr->opcode || + (rsp_opcode && apm->result.opcode == rsp_opcode))) + ret = 1; + apm->cmd_pending = false; + result_status = apm->result.status; + spin_unlock(&apm->result_lock); + + if (aborted) { + ret = -ESHUTDOWN; + goto unlock; + } + if (ret < 0) + goto unlock; + if (!ret) { + dev_err(&gdev->dev, "CMD timeout for [%x] opcode\n", + hdr->opcode); + ret = -ETIMEDOUT; + } else if (result_status > 0) { + dev_err(&gdev->dev, "DSP returned error[%x] %x\n", + hdr->opcode, result_status); + ret = result_status == ADSP_EUNSUPPORTED ? + -EOPNOTSUPP : -EINVAL; + } else { + ret = 0; + } + +unlock: + mutex_unlock(&apm->lock); + return ret; +} + +static phys_addr_t q6apm_dsp_addr(struct device *dev, dma_addr_t dma_addr) +{ + struct of_phandle_args args; + phys_addr_t dsp_addr = dma_addr; + + if (!dev->of_node || + of_parse_phandle_with_fixed_args(dev->of_node, "iommus", 1, 0, + &args)) + return dsp_addr; + + dsp_addr |= (phys_addr_t)(args.args[0] & Q6APM_DSP_SID_MASK) << 32; + of_node_put(args.np); + + return dsp_addr; +} + +static u32 q6apm_next_mmap_token(u32 graph_id, u32 map_type) +{ + u32 sequence; + + do { + sequence = (u32)atomic_inc_return(&q6apm_mmap_token_seq) << + APM_MMAP_TOKEN_OOB_SEQ_SHIFT; + sequence &= APM_MMAP_TOKEN_OOB_SEQ_MASK; + } while (sequence == APM_MMAP_TOKEN_MAP_TYPE_GRAPH_POS); + + return (graph_id & APM_MMAP_TOKEN_GID_MASK) | map_type | + sequence; +} + +static int q6apm_map_position_buffer(struct audioreach_graph *graph) +{ + struct apm_shared_map_region_payload *region; + struct apm_cmd_shared_mem_map_regions *cmd; + struct gpr_pkt *pkt __free(kfree) = NULL; + u32 opcode = APM_CMD_SHARED_MEM_MAP_REGIONS; + int payload_size = sizeof(*cmd) + sizeof(*region); + bool map_uncertain; + bool mapped; + void *payload; + u32 token; + int ret; + + if (!graph->dma_dev) + return -ENODEV; + if (graph->position_virt) + return -EBUSY; + + graph->position_token = (graph->id & APM_MMAP_TOKEN_GID_MASK) | + APM_MMAP_TOKEN_MAP_TYPE_GRAPH_POS; + token = graph->position_token; + graph->position_size = PAGE_SIZE; + graph->position_virt = + dma_alloc_coherent(graph->dma_dev, graph->position_size, + &graph->position_dma, GFP_KERNEL); + if (!graph->position_virt) + return -ENOMEM; + memset(graph->position_virt, 0, graph->position_size); + graph->position_dsp_addr = q6apm_dsp_addr(graph->dma_dev, graph->position_dma); + + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, token); + if (IS_ERR(pkt)) { + ret = PTR_ERR(pkt); + goto free_buffer; + } + + payload = (u8 *)pkt + GPR_HDR_SIZE; + cmd = payload; + cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL; + cmd->num_regions = 1; + cmd->property_flag = Q6APM_POSITION_MAP_UNCACHED; + region = payload + sizeof(*cmd); + region->shm_addr_lsw = lower_32_bits(graph->position_dsp_addr); + region->shm_addr_msw = upper_32_bits(graph->position_dsp_addr); + region->mem_size_bytes = graph->position_size; + + graph->position_map_uncertain = true; + ret = q6apm_send_cmd_sync(graph->apm, pkt, + APM_CMD_RSP_SHARED_MEM_MAP_REGIONS); + spin_lock(&graph->apm->graph_lock); + mapped = graph->position_mem_map_handle; + map_uncertain = graph->position_map_uncertain; + spin_unlock(&graph->apm->graph_lock); + if (mapped) + return 0; + if (map_uncertain) { + if (!ret) + ret = -EIO; + return ret; + } + if (!ret) + ret = -EIO; + +free_buffer: + dma_free_coherent(graph->dma_dev, graph->position_size, + graph->position_virt, graph->position_dma); + graph->position_virt = NULL; + graph->position_dma = 0; + graph->position_dsp_addr = 0; + graph->position_size = 0; + return ret; +} + +static int q6apm_unmap_position_buffer(struct audioreach_graph *graph) +{ + struct apm_cmd_shared_mem_unmap_regions *cmd; + u32 opcode = APM_CMD_SHARED_MEM_UNMAP_REGIONS; + u32 mem_map_handle; + u32 token = graph->position_token; + bool map_uncertain; + int ret = 0; + + mutex_lock(&graph->position_lock); + spin_lock(&graph->apm->graph_lock); + map_uncertain = graph->position_map_uncertain; + mem_map_handle = graph->position_mem_map_handle; + spin_unlock(&graph->apm->graph_lock); + if (map_uncertain) { + ret = -EAGAIN; + goto unlock; + } + + if (mem_map_handle) { + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_apm_cmd_pkt(sizeof(*cmd), opcode, token); + + if (IS_ERR(pkt)) { + ret = PTR_ERR(pkt); + goto unlock; + } + cmd = (void *)pkt + GPR_HDR_SIZE; + cmd->mem_map_handle = mem_map_handle; + ret = q6apm_send_cmd_sync(graph->apm, pkt, opcode); + if (ret) + goto unlock; + spin_lock(&graph->apm->graph_lock); + mem_map_handle = graph->position_mem_map_handle; + spin_unlock(&graph->apm->graph_lock); + if (mem_map_handle) { + ret = -EIO; + goto unlock; + } + } + + if (graph->position_virt) { + dma_free_coherent(graph->dma_dev, graph->position_size, + graph->position_virt, graph->position_dma); + graph->position_virt = NULL; + graph->position_dma = 0; + graph->position_dsp_addr = 0; + graph->position_size = 0; + } + +unlock: + mutex_unlock(&graph->position_lock); + return ret; +} + +static int q6apm_map_oob_buffer(struct audioreach_graph *graph, size_t data_size) +{ + struct apm_shared_map_region_payload *region; + struct apm_cmd_shared_mem_map_regions *cmd; + struct gpr_pkt *pkt __free(kfree) = NULL; + u32 opcode = APM_CMD_SHARED_MEM_MAP_REGIONS; + int payload_size = sizeof(*cmd) + sizeof(*region); + bool map_uncertain; + bool mapped; + void *payload; + int ret; + + if (!graph->dma_dev) + return -ENODEV; + if (!data_size || data_size > U32_MAX - (PAGE_SIZE - 1)) + return -E2BIG; + graph->oob_token = q6apm_next_mmap_token(graph->id, + APM_MMAP_TOKEN_MAP_TYPE_OOB); + + graph->oob_size = PAGE_ALIGN(data_size); + graph->oob_virt = dma_alloc_coherent(graph->dma_dev, graph->oob_size, + &graph->oob_dma, GFP_KERNEL); + if (!graph->oob_virt) + return -ENOMEM; + graph->oob_dsp_addr = q6apm_dsp_addr(graph->dma_dev, graph->oob_dma); + + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, opcode, graph->oob_token); + if (IS_ERR(pkt)) { + ret = PTR_ERR(pkt); + goto free_buffer; + } + + payload = (u8 *)pkt + GPR_HDR_SIZE; + cmd = payload; + cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL; + cmd->num_regions = 1; + cmd->property_flag = 0; + region = payload + sizeof(*cmd); + region->shm_addr_lsw = lower_32_bits(graph->oob_dsp_addr); + region->shm_addr_msw = upper_32_bits(graph->oob_dsp_addr); + region->mem_size_bytes = graph->oob_size; + + graph->oob_map_uncertain = true; + ret = q6apm_send_cmd_sync(graph->apm, pkt, + APM_CMD_RSP_SHARED_MEM_MAP_REGIONS); + spin_lock(&graph->apm->graph_lock); + mapped = graph->oob_mem_map_handle; + map_uncertain = graph->oob_map_uncertain; + spin_unlock(&graph->apm->graph_lock); + if (mapped) + return 0; + if (!map_uncertain) { + if (!ret) + ret = -EIO; + goto free_buffer; + } + if (!ret) + ret = -EIO; + + /* + * A timeout does not prove that the DSP failed to map the buffer. Keep + * it allocated; teardown retains it unless a correlated response makes + * the mapping state certain. + */ + return ret; + +free_buffer: + dma_free_coherent(graph->dma_dev, graph->oob_size, graph->oob_virt, + graph->oob_dma); + graph->oob_virt = NULL; + graph->oob_dma = 0; + graph->oob_dsp_addr = 0; + graph->oob_size = 0; + return ret; +} + +static int q6apm_unmap_oob_buffer(struct audioreach_graph *graph) +{ + struct apm_cmd_shared_mem_unmap_regions *cmd; + u32 opcode = APM_CMD_SHARED_MEM_UNMAP_REGIONS; + u32 mem_map_handle; + bool map_uncertain; + int ret = 0; + + mutex_lock(&graph->oob_lock); + if (graph->oob_transfer_uncertain) { + ret = -EAGAIN; + goto unlock; + } + + spin_lock(&graph->apm->graph_lock); + map_uncertain = graph->oob_map_uncertain; + if (map_uncertain) + mem_map_handle = 0; + else + mem_map_handle = graph->oob_mem_map_handle; + spin_unlock(&graph->apm->graph_lock); + if (map_uncertain) { + ret = -EAGAIN; + goto unlock; + } + + if (mem_map_handle) { + struct gpr_pkt *pkt __free(kfree) = NULL; + + pkt = audioreach_alloc_apm_cmd_pkt(sizeof(*cmd), opcode, graph->oob_token); + + if (IS_ERR(pkt)) { + ret = PTR_ERR(pkt); + } else { + cmd = (void *)pkt + GPR_HDR_SIZE; + cmd->mem_map_handle = mem_map_handle; + ret = q6apm_send_cmd_sync(graph->apm, pkt, opcode); + } + if (ret) + goto unlock; + spin_lock(&graph->apm->graph_lock); + mem_map_handle = graph->oob_mem_map_handle; + spin_unlock(&graph->apm->graph_lock); + if (mem_map_handle) { + ret = -EIO; + goto unlock; + } + } + + if (graph->oob_virt) { + dma_free_coherent(graph->dma_dev, graph->oob_size, + graph->oob_virt, graph->oob_dma); + graph->oob_virt = NULL; + graph->oob_dma = 0; + graph->oob_dsp_addr = 0; + graph->oob_size = 0; + } + +unlock: + mutex_unlock(&graph->oob_lock); + return ret; +} + +static int __q6apm_send_oob_config(struct audioreach_graph *graph, + struct q6apm_graph *client, + const void *data, size_t size) +{ + struct apm_cmd_header *cmd; + struct gpr_pkt *pkt; + int ret; + + if (!data || !size || size > graph->oob_size || + !graph->oob_virt || !graph->oob_mem_map_handle) + return -EINVAL; + + mutex_lock(&graph->oob_lock); + if (graph->oob_transfer_uncertain) { + ret = -EIO; + goto unlock; + } + memset(graph->oob_virt, 0, graph->oob_size); + memcpy(graph->oob_virt, data, size); + dma_wmb(); + + if (client) + pkt = audioreach_alloc_cmd_pkt(0, APM_CMD_SET_CFG, 0, + client->port->id, + APM_MODULE_INSTANCE_ID); + else + pkt = audioreach_alloc_apm_cmd_pkt(0, APM_CMD_SET_CFG, 0); + if (IS_ERR(pkt)) { + ret = PTR_ERR(pkt); + goto unlock; + } + + cmd = (void *)pkt + GPR_HDR_SIZE; + cmd->payload_address_lsw = lower_32_bits(graph->oob_dsp_addr); + cmd->payload_address_msw = upper_32_bits(graph->oob_dsp_addr); + cmd->mem_map_handle = graph->oob_mem_map_handle; + cmd->payload_size = size; + + if (client) + ret = audioreach_graph_send_cmd_sync(client, pkt, 0); + else + ret = q6apm_send_cmd_sync(graph->apm, pkt, 0); + kfree(pkt); + if (ret == -ETIMEDOUT) + graph->oob_transfer_uncertain = true; + +unlock: + mutex_unlock(&graph->oob_lock); + return ret; +} + +int q6apm_send_oob_config(struct audioreach_graph *graph, + const void *data, size_t size) +{ + return __q6apm_send_oob_config(graph, NULL, data, size); +} + +int q6apm_send_graph_oob_config(struct q6apm_graph *graph, + const void *data, size_t size) +{ + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + + if (!active) + return -ESHUTDOWN; + if (!graph->ar_graph) + return -ENODEV; + + return __q6apm_send_oob_config(graph->ar_graph, graph, data, size); +} + +static bool q6apm_info_is_sp11_pull(struct q6apm *apm, + struct audioreach_graph_info *info) +{ + struct audioreach_module *pause; + struct audioreach_module *pull; + + if (audioreach_graph_protection_profile(info) <= 0) + return false; - return audioreach_send_cmd_sync(&gdev->dev, gdev, &apm->result, &apm->lock, - NULL, &apm->wait, pkt, rsp_opcode); + pull = __q6apm_find_module_by_mid(apm, info, MODULE_ID_SH_MEM_PULL_MODE); + pause = __q6apm_find_module_by_mid(apm, info, SP11_MODULE_ID_SOFT_PAUSE); + + return pull && pull->instance_id == SP11_PULL_IID && pause && + pause->instance_id == SP11_SOFT_PAUSE_IID; } -static struct audioreach_graph *q6apm_get_audioreach_graph(struct q6apm *apm, uint32_t graph_id) +static struct audioreach_graph * +q6apm_get_audioreach_graph(struct q6apm *apm, u32 graph_id, + bool enable_protection) { struct audioreach_graph_info *info; struct audioreach_graph *graph; - int id; + struct device *dma_dev; + bool graph_faulted; + bool mode_mismatch; + bool sp11_pull; + size_t oob_size; + int id, position_ret; mutex_lock(&apm->lock); + spin_lock(&apm->graph_lock); graph = idr_find(&apm->graph_idr, graph_id); + graph_faulted = graph && + (READ_ONCE(graph->protection_faulted) || + (enable_protection && READ_ONCE(graph->protection_malformed))); + mode_mismatch = graph && !graph->initializing && + ((enable_protection && graph->protection_profile) != + graph->protection_runtime); + if (graph && (graph->initializing || + graph_faulted || + mode_mismatch || + !kref_get_unless_zero(&graph->refcount))) { + spin_unlock(&apm->graph_lock); + mutex_unlock(&apm->lock); + return ERR_PTR(mode_mismatch ? -EBUSY : + graph_faulted ? -EIO : -EBUSY); + } + spin_unlock(&apm->graph_lock); + info = idr_find(&apm->graph_info_idr, graph_id); + dma_dev = !graph && info ? get_device(apm->dma_dev) : NULL; mutex_unlock(&apm->lock); - if (graph) { - kref_get(&graph->refcount); + if (graph) return graph; - } - - info = idr_find(&apm->graph_info_idr, graph_id); if (!info) return ERR_PTR(-ENODEV); + sp11_pull = enable_protection && q6apm_info_is_sp11_pull(apm, info); graph = kzalloc_obj(*graph); - if (!graph) + if (!graph) { + put_device(dma_dev); return ERR_PTR(-ENOMEM); + } graph->apm = apm; graph->info = info; graph->id = graph_id; + graph->dma_dev = dma_dev; + graph->initializing = true; + mutex_init(&graph->oob_lock); + mutex_init(&graph->position_lock); + mutex_init(&graph->protection_lock); graph->graph = audioreach_alloc_graph_pkt(apm, info); if (IS_ERR(graph->graph)) { void *err = graph->graph; + put_device(graph->dma_dev); kfree(graph); return ERR_CAST(err); } + kref_init(&graph->refcount); mutex_lock(&apm->lock); - id = idr_alloc(&apm->graph_idr, graph, graph_id, graph_id + 1, GFP_KERNEL); + spin_lock(&apm->graph_lock); + id = idr_alloc(&apm->graph_idr, graph, graph_id, graph_id + 1, + GFP_ATOMIC); + spin_unlock(&apm->graph_lock); if (id < 0) { dev_err(apm->dev, "Unable to allocate graph id (%d)\n", graph_id); kfree(graph->graph); + put_device(graph->dma_dev); kfree(graph); mutex_unlock(&apm->lock); return ERR_PTR(id); } mutex_unlock(&apm->lock); + if (sp11_pull) { + id = q6apm_map_position_buffer(graph); + if (id) { + if (graph->position_map_uncertain) { + dev_err(apm->dev, + "retaining graph %u after uncertain position map\n", + graph_id); + return ERR_PTR(id); + } + goto remove_graph; + } + } - kref_init(&graph->refcount); + id = q6apm_send_cmd_sync(apm, graph->graph, 0); + if (id == -ETIMEDOUT) { + dev_err(apm->dev, + "retaining graph %u after uncertain GRAPH_OPEN\n", + graph_id); + return ERR_PTR(id); + } + if (id) { + position_ret = q6apm_unmap_position_buffer(graph); + if (position_ret) { + dev_err(apm->dev, + "retaining graph %u after position unmap failure (%d)\n", + graph_id, position_ret); + return ERR_PTR(id); + } + goto remove_graph; + } - q6apm_send_cmd_sync(apm, graph->graph, 0); + id = audioreach_graph_protection_oob_size(info, &oob_size); + if (id < 0) { + graph->protection_profile = true; + graph->protection_malformed = true; + if (enable_protection) { + graph->protection_runtime = true; + graph->protection_faulted = true; + dev_warn(apm->dev, + "protected topology is incomplete (%d); refusing runtime\n", + id); + } + goto graph_ready; + } + if (oob_size) { + graph->protection_profile = true; + if (!enable_protection) + goto graph_ready; + graph->protection_runtime = true; + id = q6apm_map_oob_buffer(graph, oob_size); + if (id) { + dev_warn(apm->dev, + "protected OOB map failed (%d); using bypass\n", id); + goto graph_ready; + } + id = audioreach_send_protected_graph_calibration(graph); + if (id) { + dev_warn(apm->dev, + "protected graph calibration failed (%d); using bypass\n", + id); + q6apm_unmap_oob_buffer(graph); + goto graph_ready; + } + graph->protection_available = true; + } +graph_ready: + spin_lock(&apm->graph_lock); + graph->initializing = false; + spin_unlock(&apm->graph_lock); return graph; + +remove_graph: + mutex_lock(&apm->lock); + spin_lock(&apm->graph_lock); + idr_remove(&apm->graph_idr, graph->id); + spin_unlock(&apm->graph_lock); + mutex_unlock(&apm->lock); + kfree(graph->graph); + put_device(graph->dma_dev); + kfree(graph); + return ERR_PTR(id); } static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t opcode) @@ -121,21 +777,106 @@ static int audioreach_graph_mgmt_cmd(struct audioreach_graph *graph, uint32_t op return q6apm_send_cmd_sync(apm, pkt, 0); } +static int audioreach_graph_client_mgmt_cmd(struct q6apm_graph *graph, + u32 opcode) +{ + static const u32 sp11_sub_graph_order[] = { + 0xb0000001, 0xb000007f, 0xb000007e, + }; + struct audioreach_graph_info *info = graph->info; + struct apm_module_param_data *param_data; + struct apm_graph_mgmt_cmd *mgmt_cmd; + struct gpr_pkt *pkt __free(kfree) = NULL; + struct audioreach_sub_graph *sg; + bool sp11_pull = q6apm_graph_is_sp11_pull(graph); + int num_sub_graphs = sp11_pull ? ARRAY_SIZE(sp11_sub_graph_order) : + info->num_sub_graphs; + int payload_size; + int i = 0; + u32 dest = APM_MODULE_INSTANCE_ID; + + payload_size = APM_GRAPH_MGMT_PSIZE(mgmt_cmd, num_sub_graphs); + pkt = audioreach_alloc_cmd_pkt(payload_size, opcode, 0, graph->port->id, dest); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + mgmt_cmd = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + mgmt_cmd->num_sub_graphs = num_sub_graphs; + param_data = &mgmt_cmd->param_data; + param_data->module_instance_id = APM_MODULE_INSTANCE_ID; + param_data->param_id = APM_PARAM_ID_SUB_GRAPH_LIST; + param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE; + + if (sp11_pull) { + if (info->num_sub_graphs != ARRAY_SIZE(sp11_sub_graph_order)) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(sp11_sub_graph_order); i++) { + bool found = false; + + list_for_each_entry(sg, &info->sg_list, node) { + if (sg->sub_graph_id != sp11_sub_graph_order[i]) + continue; + mgmt_cmd->sub_graph_id_list[i] = sg->sub_graph_id; + found = true; + break; + } + if (!found) + return -ENODEV; + } + } else { + /* FullIO emits the generic graph's required run-state order. */ + list_for_each_entry(sg, &info->sg_list, node) + mgmt_cmd->sub_graph_id_list[i++] = sg->sub_graph_id; + } + + return audioreach_graph_send_cmd_sync(graph, pkt, 0); +} + static void q6apm_put_audioreach_graph(struct kref *ref) { struct audioreach_graph *graph; struct q6apm *apm; + int close_ret; + int unmap_ret = 0; + int position_unmap_ret = 0; graph = container_of(ref, struct audioreach_graph, refcount); apm = graph->apm; - audioreach_graph_mgmt_cmd(graph, APM_CMD_GRAPH_CLOSE); + /* Stop all DSP users before releasing their OOB mapping. */ + close_ret = graph->close_confirmed ? 0 : + audioreach_graph_mgmt_cmd(graph, APM_CMD_GRAPH_CLOSE); + if (close_ret && graph->protection_runtime) { + /* Keep the ID reserved until reboot or a proven reset hook. */ + spin_lock(&apm->graph_lock); + graph->initializing = true; + kref_init(&graph->refcount); + spin_unlock(&apm->graph_lock); + dev_err(apm->dev, + "retaining graph %u after uncertain GRAPH_CLOSE (%d)\n", + graph->id, close_ret); + return; + } + + mutex_lock(&graph->oob_lock); + graph->oob_transfer_uncertain = false; + mutex_unlock(&graph->oob_lock); + unmap_ret = q6apm_unmap_oob_buffer(graph); + position_unmap_ret = q6apm_unmap_position_buffer(graph); mutex_lock(&apm->lock); + spin_lock(&apm->graph_lock); graph = idr_remove(&apm->graph_idr, graph->id); + spin_unlock(&apm->graph_lock); mutex_unlock(&apm->lock); kfree(graph->graph); + if (graph->oob_virt || graph->position_virt) + dev_err(apm->dev, + "retaining protected DMA after teardown failure (close %d, OOB unmap %d, position unmap %d)\n", + close_ret, unmap_ret, position_unmap_ret); + else + put_device(graph->dma_dev); kfree(graph); } @@ -154,10 +895,14 @@ static int q6apm_get_apm_state(struct q6apm *apm) bool q6apm_is_adsp_ready(void) { + bool ready = false; + + mutex_lock(&g_apm_lock); if (g_apm) - return q6apm_get_apm_state(g_apm); + ready = q6apm_get_apm_state(g_apm); + mutex_unlock(&g_apm_lock); - return false; + return ready; } EXPORT_SYMBOL_GPL(q6apm_is_adsp_ready); @@ -184,16 +929,28 @@ static struct audioreach_module *__q6apm_find_module_by_mid(struct q6apm *apm, int q6apm_graph_media_format_shmem(struct q6apm_graph *graph, struct audioreach_module_config *cfg) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; struct audioreach_module *module; + if (!active) + return -ESHUTDOWN; if (cfg->direction == SNDRV_PCM_STREAM_CAPTURE) { - module = q6apm_find_module_by_mid(graph, MODULE_ID_SH_MEM_PUSH_MODE); + module = __q6apm_find_module_by_mid(graph->apm, + graph->info, + MODULE_ID_SH_MEM_PUSH_MODE); if (!module) - module = q6apm_find_module_by_mid(graph, MODULE_ID_RD_SHARED_MEM_EP); + module = __q6apm_find_module_by_mid(graph->apm, + graph->info, + MODULE_ID_RD_SHARED_MEM_EP); } else { - module = q6apm_find_module_by_mid(graph, MODULE_ID_SH_MEM_PULL_MODE); + module = __q6apm_find_module_by_mid(graph->apm, + graph->info, + MODULE_ID_SH_MEM_PULL_MODE); if (!module) - module = q6apm_find_module_by_mid(graph, MODULE_ID_WR_SHARED_MEM_EP); + module = __q6apm_find_module_by_mid(graph->apm, + graph->info, + MODULE_ID_WR_SHARED_MEM_EP); } if (!module) { @@ -216,9 +973,15 @@ static int __q6apm_map_memory_fixed_region(struct device *dev, unsigned int grap uint32_t buf_sz; void *p; uint32_t pos_mask = is_pos_buf ? APM_MMAP_TOKEN_MAP_TYPE_POS_BUF : 0; + u32 token = q6apm_next_mmap_token(graph_id, pos_mask); struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, - APM_CMD_SHARED_MEM_MAP_REGIONS, (graph_id | pos_mask)); + APM_CMD_SHARED_MEM_MAP_REGIONS, token); + if (!apm) + return -ENODEV; + guard(mutex)(&apm->client_lock); + if (apm->removing) + return -ESHUTDOWN; if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -271,10 +1034,14 @@ EXPORT_SYMBOL_GPL(q6apm_map_memory_fixed_region); int q6apm_alloc_fragments(struct q6apm_graph *graph, unsigned int dir, phys_addr_t phys, size_t period_sz, unsigned int periods) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; struct audioreach_graph_data *data; struct audio_buffer *buf; int cnt; + if (!active) + return -ESHUTDOWN; if (dir == SNDRV_PCM_STREAM_PLAYBACK) data = &graph->rx_data; else @@ -326,8 +1093,15 @@ static int __q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int gr struct q6apm *apm = dev_get_drvdata(dev->parent); struct audioreach_graph_info *info; uint32_t mem_map_handle; + u32 map_type = is_pos_buf ? APM_MMAP_TOKEN_MAP_TYPE_POS_BUF : 0; + u32 token = q6apm_next_mmap_token(graph_id, map_type); struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(sizeof(*cmd), - APM_CMD_SHARED_MEM_UNMAP_REGIONS, graph_id); + APM_CMD_SHARED_MEM_UNMAP_REGIONS, token); + if (!apm) + return -ENODEV; + guard(mutex)(&apm->client_lock); + if (apm->removing) + return -ESHUTDOWN; if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -366,6 +1140,11 @@ EXPORT_SYMBOL_GPL(q6apm_unmap_pos_buffer); int q6apm_free_fragments(struct q6apm_graph *graph, unsigned int dir) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + + if (!active) + return -ESHUTDOWN; audioreach_graph_free_buf(graph); return 0; @@ -374,9 +1153,14 @@ EXPORT_SYMBOL_GPL(q6apm_free_fragments); int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; struct audioreach_module *module; - module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!active) + return -ESHUTDOWN; + module = __q6apm_find_module_by_mid(graph->apm, graph->info, + MODULE_ID_PLACEHOLDER_DECODER); if (!module) return -ENODEV; @@ -386,9 +1170,14 @@ EXPORT_SYMBOL_GPL(q6apm_remove_initial_silence); int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; struct audioreach_module *module; - module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!active) + return -ESHUTDOWN; + module = __q6apm_find_module_by_mid(graph->apm, graph->info, + MODULE_ID_PLACEHOLDER_DECODER); if (!module) return -ENODEV; @@ -398,9 +1187,14 @@ EXPORT_SYMBOL_GPL(q6apm_remove_trailing_silence); int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; struct audioreach_module *module; - module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!active) + return -ESHUTDOWN; + module = __q6apm_find_module_by_mid(graph->apm, graph->info, + MODULE_ID_PLACEHOLDER_DECODER); if (!module) return -ENODEV; @@ -411,10 +1205,15 @@ EXPORT_SYMBOL_GPL(q6apm_enable_compress_module); int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, uint32_t codec_id) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; struct audioreach_module *module; uint32_t module_id; - module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!active) + return -ESHUTDOWN; + module = __q6apm_find_module_by_mid(graph->apm, graph->info, + MODULE_ID_PLACEHOLDER_DECODER); if (!module) return -ENODEV; @@ -440,14 +1239,242 @@ int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, } EXPORT_SYMBOL_GPL(q6apm_set_real_module_id); +bool q6apm_graph_has_protection(const struct q6apm_graph *graph) +{ + return graph && READ_ONCE(graph->protection_runtime); +} +EXPORT_SYMBOL_GPL(q6apm_graph_has_protection); + +int q6apm_graph_configure_protection(struct q6apm_graph *graph) +{ + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + + if (!active) + return -ESHUTDOWN; + if (!q6apm_graph_has_protection(graph)) + return 0; + + return audioreach_configure_protection(graph); +} +EXPORT_SYMBOL_GPL(q6apm_graph_configure_protection); + +static int __q6apm_graph_id_for_backend(struct q6apm *apm, int backend_id) +{ + struct audioreach_graph_info *info; + struct audioreach_container *container; + struct audioreach_sub_graph *sg; + struct audioreach_module *module; + int graph_id = -ENOENT; + int profile; + int id; + + mutex_lock(&apm->lock); + idr_for_each_entry(&apm->graph_info_idr, info, id) { + profile = audioreach_graph_protection_profile(info); + if (profile <= 0) + continue; + list_for_each_entry(sg, &info->sg_list, node) { + list_for_each_entry(container, &sg->container_list, node) { + list_for_each_entry(module, + &container->modules_list, node) { + if (!module->integrated_backend_id || + module->integrated_backend_id != backend_id) + continue; + if (graph_id >= 0 && graph_id != info->id) { + graph_id = -EEXIST; + goto unlock; + } + graph_id = info->id; + } + } + } + } + +unlock: + mutex_unlock(&apm->lock); + return graph_id == -ENOENT ? backend_id : graph_id; +} + +int q6apm_graph_id_for_backend(struct device *dev, int backend_id) +{ + struct q6apm *apm = dev_get_drvdata(dev->parent); + + if (!apm) + return -ENODEV; + guard(mutex)(&apm->client_lock); + if (apm->removing) + return -ESHUTDOWN; + + return __q6apm_graph_id_for_backend(apm, backend_id); +} +EXPORT_SYMBOL_GPL(q6apm_graph_id_for_backend); + +int q6apm_set_protection_backend_ready(struct device *dev, int backend_id, + enum q6apm_protection_backend backend, + bool ready) +{ + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct audioreach_graph *graph; + bool *backend_ready; + int graph_id; + int ret = 0; + + if (!apm) + return -ENODEV; + guard(mutex)(&apm->client_lock); + if (apm->removing) + return -ESHUTDOWN; + + graph_id = __q6apm_graph_id_for_backend(apm, backend_id); + if (graph_id < 0) + return graph_id; + + mutex_lock(&apm->lock); + spin_lock(&apm->graph_lock); + graph = idr_find(&apm->graph_idr, graph_id); + if (graph && (graph->initializing || + !kref_get_unless_zero(&graph->refcount))) + graph = NULL; + spin_unlock(&apm->graph_lock); + mutex_unlock(&apm->lock); + if (!graph) + return -ENODEV; + if (!graph->protection_profile) { + kref_put(&graph->refcount, q6apm_put_audioreach_graph); + return -ENODEV; + } + + mutex_lock(&graph->protection_lock); + switch (backend) { + case Q6APM_PROTECTION_BACKEND_VI: + backend_ready = &graph->protection_vi_ready; + break; + case Q6APM_PROTECTION_BACKEND_CPS: + backend_ready = &graph->protection_cps_ready; + break; + default: + ret = -EINVAL; + goto unlock; + } + if (*backend_ready == ready) + goto unlock; + if (graph->start_count > 0) { + ret = -EBUSY; + goto unlock; + } + if (ready && graph->protection_faulted) { + ret = -EIO; + goto unlock; + } + /* + * Keep ready=false available after a fault once execution has stopped so + * backend teardown can proceed. + */ + *backend_ready = ready; + graph->protection_configured = false; + graph->protection_bypass_confirmed = false; + +unlock: + mutex_unlock(&graph->protection_lock); + kref_put(&graph->refcount, q6apm_put_audioreach_graph); + + return ret; +} +EXPORT_SYMBOL_GPL(q6apm_set_protection_backend_ready); + +static const struct audioreach_module * +q6apm_find_module_by_iid(const struct audioreach_graph_info *info, u32 iid) +{ + struct audioreach_container *container; + struct audioreach_sub_graph *sg; + struct audioreach_module *module; + + list_for_each_entry(sg, &info->sg_list, node) + list_for_each_entry(container, &sg->container_list, node) + list_for_each_entry(module, &container->modules_list, node) + if (module->instance_id == iid) + return module; + + return NULL; +} + +static unsigned int +q6apm_graph_module_count(const struct audioreach_graph_info *info) +{ + struct audioreach_container *container; + struct audioreach_sub_graph *sg; + unsigned int count = 0; + + list_for_each_entry(sg, &info->sg_list, node) + list_for_each_entry(container, &sg->container_list, node) + count += container->num_modules; + + return count; +} + +static int q6apm_protected_media_format_pcm(struct q6apm_graph *graph, + struct audioreach_module_config *cfg) +{ + const struct audioreach_module *source; + const struct audioreach_module *module; + unsigned int limit, step; + bool pcm_configured = false; + int ret; + + if (cfg->direction != SNDRV_PCM_STREAM_PLAYBACK || + cfg->sample_rate != 48000 || cfg->bit_width != 16 || + cfg->num_channels != 2) + return -EINVAL; + + source = audioreach_graph_find_module(graph->info, + MODULE_ID_SH_MEM_PULL_MODE); + if (IS_ERR_OR_NULL(source)) + return source ? PTR_ERR(source) : -ENODEV; + + module = source; + limit = q6apm_graph_module_count(graph->info); + for (step = 0; step < limit; step++) { + if (module->module_id == MODULE_ID_PCM_CNV) { + if (pcm_configured) + return -EEXIST; + ret = audioreach_set_media_format(graph, module, cfg); + if (ret) + return ret; + pcm_configured = true; + } else if (module->module_id == MODULE_ID_MFC) { + if (!pcm_configured) + return -EINVAL; + return audioreach_set_media_format(graph, module, cfg); + } + + if (module->num_connections != 1) + return -EINVAL; + module = q6apm_find_module_by_iid(graph->info, + module->dst_mod_inst_id[0]); + if (!module) + return -ENODEV; + } + + return -ELOOP; +} + int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg) { - struct audioreach_graph_info *info = graph->info; + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + struct audioreach_graph_info *info; struct audioreach_sub_graph *sgs; struct audioreach_container *container; struct audioreach_module *module; int ret; + if (!active) + return -ESHUTDOWN; + info = graph->info; + if (q6apm_graph_has_protection(graph)) + return q6apm_protected_media_format_pcm(graph, cfg); + list_for_each_entry(sgs, &info->sg_list, node) { list_for_each_entry(container, &sgs->container_list, node) { list_for_each_entry(module, &container->modules_list, node) { @@ -472,11 +1499,18 @@ EXPORT_SYMBOL_GPL(q6apm_graph_media_format_pcm); int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer; struct audio_buffer *ab; + int ret; + + struct gpr_pkt *pkt __free(kfree) = NULL; - struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*write_buffer), - DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2, + if (!active) + return -ESHUTDOWN; + pkt = audioreach_alloc_pkt(sizeof(*write_buffer), + DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2, graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT), graph->port->id, graph->shm_iid); if (IS_ERR(pkt)) @@ -502,18 +1536,26 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, mutex_unlock(&graph->lock); - return gpr_send_port_pkt(graph->port, pkt); + ret = gpr_send_port_pkt(graph->port, pkt); + return ret; } EXPORT_SYMBOL_GPL(q6apm_write_async); int q6apm_read(struct q6apm_graph *graph) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read_buffer; struct audioreach_graph_data *port; struct audio_buffer *ab; + int ret; - struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_pkt(sizeof(*read_buffer), - DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2, + struct gpr_pkt *pkt __free(kfree) = NULL; + + if (!active) + return -ESHUTDOWN; + pkt = audioreach_alloc_pkt(sizeof(*read_buffer), + DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER_V2, graph->tx_data.dsp_buf, graph->port->id, graph->shm_iid); if (IS_ERR(pkt)) return PTR_ERR(pkt); @@ -536,14 +1578,19 @@ int q6apm_read(struct q6apm_graph *graph) mutex_unlock(&graph->lock); - return gpr_send_port_pkt(graph->port, pkt); + ret = gpr_send_port_pkt(graph->port, pkt); + return ret; } EXPORT_SYMBOL_GPL(q6apm_read); int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; struct audioreach_graph_data *data; + if (!active) + return 0; if (dir == SNDRV_PCM_STREAM_PLAYBACK) data = &graph->rx_data; else @@ -553,6 +1600,43 @@ int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir) } EXPORT_SYMBOL_GPL(q6apm_get_hw_pointer); +static bool q6apm_graph_try_complete_cmd(struct q6apm_graph *graph, + const struct gpr_hdr *hdr, u32 opcode, + u32 status, bool basic_response) +{ + bool expected; + + spin_lock(&graph->result_lock); + expected = graph->cmd_pending && graph->pending_token == hdr->token && + (graph->pending_opcode == opcode || + graph->pending_rsp_opcode == opcode); + if (expected && basic_response && !status && + graph->pending_rsp_opcode && graph->pending_rsp_opcode != opcode) + expected = false; + if (expected) { + graph->result.status = status; + graph->result_token = hdr->token; + graph->result.opcode = opcode; + } + spin_unlock(&graph->result_lock); + + if (expected) + wake_up(&graph->cmd_wait); + + return expected; +} + +static void q6apm_graph_get_cfg_response(struct q6apm_graph *graph, + const struct gpr_resp_pkt *data) +{ + /* A truncated response cannot establish a successful query. */ + if (data->payload_size < (int)sizeof(u32)) + return; + + q6apm_graph_try_complete_cmd(graph, &data->hdr, data->hdr.opcode, + get_unaligned_le32(data->payload), false); +} + static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) { struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done; @@ -561,20 +1645,44 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) const struct gpr_ibasic_rsp_result_t *result; struct q6apm_graph *graph = priv; const struct gpr_hdr *hdr = &data->hdr; - struct device *dev = graph->dev; + struct device *dev; uint32_t client_event; + bool expected; phys_addr_t phys; int token; + if (!q6apm_graph_user_get(graph)) + return 0; + + dev = graph->dev; result = data->payload; switch (hdr->opcode) { + case APM_CMD_RSP_GET_CFG: + q6apm_graph_get_cfg_response(graph, data); + break; case APM_EVENT_MODULE_TO_CLIENT: + if (data->payload_size < sizeof(*event)) + break; event = data->payload; switch (event->event_id) { case EVENT_ID_SH_MEM_PULL_PUSH_MODE_WATERMARK: client_event = APM_CLIENT_EVENT_WATERMARK_EVENT; - graph->cb(client_event, hdr->token, data->payload, graph->priv); + if (graph->cb) + graph->cb(client_event, hdr->token, data->payload, + graph->priv); + break; + case EVENT_ID_SOFT_PAUSE_PAUSE_COMPLETE: + case EVENT_ID_SOFT_PAUSE_RESUME_COMPLETE: + if (hdr->src_port != SP11_SOFT_PAUSE_IID || + !q6apm_graph_is_sp11_pull(graph) || !graph->cb) + break; + client_event = event->event_id == + EVENT_ID_SOFT_PAUSE_PAUSE_COMPLETE ? + APM_CLIENT_EVENT_SOFT_PAUSE_COMPLETE : + APM_CLIENT_EVENT_SOFT_RESUME_COMPLETE; + graph->cb(client_event, hdr->token, data->payload, + graph->priv); break; } @@ -597,8 +1705,6 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) atomic_set(&graph->rx_data.hw_ptr, token + 1); if (lower_32_bits(phys) == done->buf_addr_lsw && upper_32_bits(phys) == done->buf_addr_msw) { - graph->result.opcode = hdr->opcode; - graph->result.status = done->status; if (graph->cb) graph->cb(client_event, hdr->token, data->payload, graph->priv); } else { @@ -624,8 +1730,6 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) if (upper_32_bits(phys) == rd_done->buf_addr_msw && lower_32_bits(phys) == rd_done->buf_addr_lsw) { - graph->result.opcode = hdr->opcode; - graph->result.status = rd_done->status; if (graph->cb) graph->cb(client_event, hdr->token, data->payload, graph->priv); } else { @@ -639,17 +1743,23 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) graph->cb(client_event, hdr->token, data->payload, graph->priv); break; case GPR_BASIC_RSP_RESULT: + if (data->payload_size < (int)sizeof(*result)) + break; switch (result->opcode) { case APM_CMD_SHARED_MEM_MAP_REGIONS: case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT: case APM_CMD_REGISTER_MODULE_EVENTS: case APM_CMD_SET_CFG: - graph->result.opcode = result->opcode; - graph->result.status = result->status; - if (result->status) + case APM_CMD_GET_CFG: + case APM_CMD_GRAPH_START: + case APM_CMD_GRAPH_STOP: + case APM_CMD_GRAPH_FLUSH: + expected = q6apm_graph_try_complete_cmd(graph, hdr, + result->opcode, + result->status, true); + if (expected && result->status) dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, result->opcode); - wake_up(&graph->cmd_wait); break; default: break; @@ -658,23 +1768,97 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) default: break; } + q6apm_graph_user_put(graph); return 0; } int q6apm_register_watermark_event(struct q6apm_graph *graph, int water_mark_level_bytes, int num_levels) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + + if (!active) + return -ESHUTDOWN; return audioreach_shmem_register_event(graph, water_mark_level_bytes, num_levels); } EXPORT_SYMBOL_GPL(q6apm_register_watermark_event); +static int q6apm_register_module_event(struct q6apm_graph *graph, u32 iid, + u32 event_id) +{ + struct apm_module_register_events *event; + size_t payload_size = ALIGN(sizeof(*event), 8); + void *p; + + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_cmd_pkt(payload_size, + APM_CMD_REGISTER_MODULE_EVENTS, 0, + graph->port->id, iid); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + p = (u8 *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + memset(p, 0, payload_size); + event = p; + event->module_instance_id = iid; + event->event_id = event_id; + event->is_register = 1; + + return audioreach_graph_send_cmd_sync(graph, pkt, 0); +} + +int q6apm_register_sp11_soft_pause_events(struct q6apm_graph *graph) +{ + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + struct audioreach_module *module; + int ret; + + if (!active) + return -ESHUTDOWN; + if (!q6apm_graph_is_sp11_pull(graph)) + return -EOPNOTSUPP; + + module = q6apm_find_module_by_mid(graph, SP11_MODULE_ID_SOFT_PAUSE); + if (!module || module->instance_id != SP11_SOFT_PAUSE_IID) + return -ENODEV; + + ret = q6apm_register_module_event(graph, module->instance_id, + EVENT_ID_SOFT_PAUSE_PAUSE_COMPLETE); + if (ret) + return ret; + + return q6apm_register_module_event(graph, module->instance_id, + EVENT_ID_SOFT_PAUSE_RESUME_COMPLETE); +} +EXPORT_SYMBOL_GPL(q6apm_register_sp11_soft_pause_events); + int q6apm_push_pull_config(struct q6apm_graph *graph, phys_addr_t bphys, phys_addr_t pphys, uint32_t size) { - struct audioreach_graph_info *info = graph->info; + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + struct audioreach_graph_info *info; + phys_addr_t position_addr = pphys; + u32 position_handle; + + if (!active) + return -ESHUTDOWN; + info = graph->info; + position_handle = info->pos_buf_mem_map_handle; + if (q6apm_graph_is_sp11_pull(graph)) { + if (size != 3840 || !graph->ar_graph || + !graph->ar_graph->position_virt || + !graph->ar_graph->position_mem_map_handle) + return -EINVAL; + position_addr = graph->ar_graph->position_dsp_addr; + position_handle = graph->ar_graph->position_mem_map_handle; + } - return audioreach_setup_push_pull(graph, bphys, pphys, info->mem_map_handle, - info->pos_buf_mem_map_handle, size); + return audioreach_setup_push_pull(graph, bphys, position_addr, + info->mem_map_handle, + position_handle, size); } EXPORT_SYMBOL_GPL(q6apm_push_pull_config); @@ -684,6 +1868,11 @@ bool q6apm_is_graph_in_push_pull_mode_from_id(struct device *dev, unsigned int g struct q6apm *apm = dev_get_drvdata(dev->parent); struct audioreach_module *module; + if (!apm) + return false; + guard(mutex)(&apm->client_lock); + if (apm->removing) + return false; info = idr_find(&apm->graph_info_idr, graph_id); if (!info) return false; @@ -700,10 +1889,75 @@ EXPORT_SYMBOL_GPL(q6apm_is_graph_in_push_pull_mode_from_id); bool q6apm_is_graph_in_push_pull_mode(struct q6apm_graph *graph) { - return graph->info->is_push_pull_mode; + return graph && READ_ONCE(graph->is_push_pull_mode); } EXPORT_SYMBOL_GPL(q6apm_is_graph_in_push_pull_mode); +bool q6apm_graph_is_sp11_pull(struct q6apm_graph *graph) +{ + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + + if (!active || !graph->protection_runtime || + !graph->is_push_pull_mode) + return false; + + return q6apm_info_is_sp11_pull(graph->apm, graph->info); +} +EXPORT_SYMBOL_GPL(q6apm_graph_is_sp11_pull); + +bool q6apm_graph_id_is_sp11_pull(struct device *dev, unsigned int graph_id) +{ + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct audioreach_graph_info *info; + bool sp11_pull; + + if (!apm) + return false; + guard(mutex)(&apm->client_lock); + if (apm->removing) + return false; + mutex_lock(&apm->lock); + info = idr_find(&apm->graph_info_idr, graph_id); + sp11_pull = info && q6apm_info_is_sp11_pull(apm, info); + mutex_unlock(&apm->lock); + + return sp11_pull; +} +EXPORT_SYMBOL_GPL(q6apm_graph_id_is_sp11_pull); + +snd_pcm_uframes_t q6apm_get_pull_hw_pointer(struct q6apm_graph *graph, + struct snd_pcm_runtime *runtime) +{ + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + struct sh_mem_pull_push_mode_position_buffer *position; + u32 before, after, index; + int retries = 4; + + if (!active || !q6apm_graph_is_sp11_pull(graph) || + !graph->ar_graph || !graph->ar_graph->position_virt) + return 0; + + position = graph->ar_graph->position_virt; + do { + before = READ_ONCE(position->frame_counter); + dma_rmb(); + index = READ_ONCE(position->index); + dma_rmb(); + after = READ_ONCE(position->frame_counter); + } while ((!before || before != after) && --retries); + + if (!before || before != after || + index >= frames_to_bytes(runtime, runtime->buffer_size)) + index = atomic_read(&graph->rx_data.hw_ptr); + else + atomic_set(&graph->rx_data.hw_ptr, index); + + return bytes_to_frames(runtime, index); +} +EXPORT_SYMBOL_GPL(q6apm_get_pull_hw_pointer); + static int q6apm_graph_get_module_iid(struct q6apm_graph *graph, uint32_t mid) { struct audioreach_module *module; @@ -716,17 +1970,33 @@ static int q6apm_graph_get_module_iid(struct q6apm_graph *graph, uint32_t mid) } struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, - void *priv, int graph_id, int dir) + void *priv, int graph_id, int dir, + bool enable_protection) { struct q6apm *apm = dev_get_drvdata(dev->parent); struct audioreach_graph *ar_graph; struct q6apm_graph *graph; int ret, iid = 0; - ar_graph = q6apm_get_audioreach_graph(apm, graph_id); + if (!apm) + return ERR_PTR(-ENODEV); + + mutex_lock(&apm->client_lock); + if (apm->removing) { + ret = -ESHUTDOWN; + goto unlock_clients; + } + + ar_graph = q6apm_get_audioreach_graph(apm, graph_id, + enable_protection); if (IS_ERR(ar_graph)) { dev_err(dev, "No graph found with id %d\n", graph_id); - return ERR_CAST(ar_graph); + ret = PTR_ERR(ar_graph); + goto unlock_clients; + } + if (enable_protection && ar_graph->protection_malformed) { + ret = -EINVAL; + goto put_ar_graph; } graph = kzalloc_obj(*graph); @@ -741,7 +2011,12 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, graph->info = ar_graph->info; graph->ar_graph = ar_graph; graph->id = ar_graph->id; - graph->dev = dev; + graph->dev = get_device(dev); + graph->protection_profile = ar_graph->protection_profile; + graph->protection_runtime = ar_graph->protection_runtime; + spin_lock_init(&graph->lifecycle_lock); + init_waitqueue_head(&graph->users_wait); + init_completion(&graph->detached_done); if (dir == SNDRV_PCM_STREAM_PLAYBACK) { iid = q6apm_graph_get_module_iid(graph, MODULE_ID_SH_MEM_PULL_MODE); @@ -757,11 +2032,14 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, else graph->info->is_push_pull_mode = true; } + graph->is_push_pull_mode = graph->info->is_push_pull_mode; if (iid > 0) graph->shm_iid = iid; mutex_init(&graph->lock); + mutex_init(&graph->cmd_lock); + spin_lock_init(&graph->result_lock); init_waitqueue_head(&graph->cmd_wait); graph->port = gpr_alloc_port(apm->gdev, dev, graph_callback, graph); @@ -769,24 +2047,119 @@ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, ret = PTR_ERR(graph->port); goto free_graph; } + list_add_tail(&graph->node, &apm->graph_client_list); + mutex_unlock(&apm->client_lock); return graph; free_graph: + put_device(graph->dev); kfree(graph); put_ar_graph: kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph); + mutex_unlock(&apm->client_lock); + return ERR_PTR(ret); + +unlock_clients: + mutex_unlock(&apm->client_lock); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(q6apm_graph_open); int q6apm_graph_close(struct q6apm_graph *graph) { - struct audioreach_graph *ar_graph = graph->ar_graph; + struct audioreach_graph *ar_graph; + struct q6apm *apm; + unsigned long flags; + int active_refs = 0; + int close_ret; + bool retain_graph = false; + bool final_protected; + bool retain_dma; + bool detached; + bool dying; + +retry: + spin_lock_irqsave(&graph->lifecycle_lock, flags); + detached = graph->detached; + dying = graph->dying; + retain_dma = graph->retain_dma_on_detach; + apm = graph->apm; + spin_unlock_irqrestore(&graph->lifecycle_lock, flags); + if (detached) { + if (retain_dma) + return -EBUSY; + put_device(graph->dev); + kfree(graph); + return 0; + } + if (dying) { + wait_for_completion(&graph->detached_done); + goto retry; + } + if (!apm) + return -ESHUTDOWN; + + mutex_lock(&apm->client_lock); + spin_lock_irqsave(&graph->lifecycle_lock, flags); + if (graph->dying || graph->detached) { + spin_unlock_irqrestore(&graph->lifecycle_lock, flags); + mutex_unlock(&apm->client_lock); + goto retry; + } + spin_unlock_irqrestore(&graph->lifecycle_lock, flags); + ar_graph = graph->ar_graph; + + if (graph->protection_runtime) { + mutex_lock(&ar_graph->protection_lock); + active_refs = ar_graph->start_count; + retain_graph = ar_graph->execution_uncertain || + (ar_graph->protection_faulted && active_refs > 0); + mutex_unlock(&ar_graph->protection_lock); + } + if (retain_graph) { + graph->retained = true; + mutex_unlock(&apm->client_lock); + dev_err(graph->dev, + "retaining uncertain DSP client with %d active references\n", + active_refs); + return -EBUSY; + } + q6apm_graph_mark_dying(graph); + q6apm_graph_wait_users(graph); + final_protected = graph->protection_runtime && + kref_read(&ar_graph->refcount) == 1; + if (final_protected) { + close_ret = audioreach_graph_mgmt_cmd(ar_graph, + APM_CMD_GRAPH_CLOSE); + if (close_ret) { + mutex_lock(&ar_graph->protection_lock); + ar_graph->protection_faulted = true; + ar_graph->execution_uncertain = true; + ar_graph->dma_quarantined = true; + mutex_unlock(&ar_graph->protection_lock); + spin_lock_irqsave(&graph->lifecycle_lock, flags); + graph->dying = false; + spin_unlock_irqrestore(&graph->lifecycle_lock, flags); + graph->retained = true; + mutex_unlock(&apm->client_lock); + dev_err(graph->dev, + "retaining protected client after GRAPH_CLOSE failure (%d)\n", + close_ret); + return close_ret; + } + mutex_lock(&ar_graph->protection_lock); + ar_graph->close_confirmed = true; + mutex_unlock(&ar_graph->protection_lock); + } + list_del_init(&graph->node); + gpr_free_port(graph->port); + graph->port = NULL; graph->ar_graph = NULL; kref_put(&ar_graph->refcount, q6apm_put_audioreach_graph); - gpr_free_port(graph->port); + mutex_unlock(&apm->client_lock); + put_device(graph->dev); kfree(graph); return 0; @@ -795,19 +2168,86 @@ EXPORT_SYMBOL_GPL(q6apm_graph_close); int q6apm_graph_prepare(struct q6apm_graph *graph) { - return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_PREPARE); + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + struct audioreach_graph *ar_graph; + bool protected = q6apm_graph_has_protection(graph); + int ret = 0; + + if (!active) + return -ESHUTDOWN; + ar_graph = graph->ar_graph; + if (q6apm_graph_is_sp11_pull(graph)) { + /* The qualified FullIO pull sequence advances directly to START. */ + mutex_lock(&ar_graph->protection_lock); + if (ar_graph->protection_faulted) + ret = -EIO; + else + ar_graph->prepared = true; + ar_graph->prepare_uncertain = false; + mutex_unlock(&ar_graph->protection_lock); + return ret; + } + ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_PREPARE); + if (protected) { + mutex_lock(&ar_graph->protection_lock); + ar_graph->prepared = !ret; + ar_graph->prepare_uncertain = ret == -ETIMEDOUT; + mutex_unlock(&ar_graph->protection_lock); + } + + return ret; } EXPORT_SYMBOL_GPL(q6apm_graph_prepare); int q6apm_graph_start(struct q6apm_graph *graph) { - struct audioreach_graph *ar_graph = graph->ar_graph; + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + struct audioreach_graph *ar_graph; + bool protected = q6apm_graph_has_protection(graph); int ret = 0; - if (ar_graph->start_count == 0) - ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_START); + if (!active) + return -ESHUTDOWN; + ar_graph = graph->ar_graph; + if (protected) + mutex_lock(&ar_graph->protection_lock); + if (protected && ar_graph->protection_faulted) { + ret = -EIO; + goto unlock; + } + if (protected && !ar_graph->protection_configured && + !ar_graph->protection_bypass_confirmed) { + ret = -EAGAIN; + goto unlock; + } + if (protected && (!ar_graph->prepared || + ar_graph->prepare_uncertain)) { + ret = -EAGAIN; + goto unlock; + } + if (ar_graph->start_count == 0) { + if (protected) + ret = audioreach_graph_client_mgmt_cmd(graph, APM_CMD_GRAPH_START); + else + ret = audioreach_graph_mgmt_cmd(ar_graph, + APM_CMD_GRAPH_START); + } + + if (!ret) + ar_graph->start_count++; + else if (protected) { + ar_graph->protection_faulted = true; + if (ret == -ETIMEDOUT) { + ar_graph->start_count = 1; + ar_graph->execution_uncertain = true; + } + } - ar_graph->start_count++; +unlock: + if (protected) + mutex_unlock(&ar_graph->protection_lock); return ret; } @@ -815,21 +2255,188 @@ EXPORT_SYMBOL_GPL(q6apm_graph_start); int q6apm_graph_stop(struct q6apm_graph *graph) { - struct audioreach_graph *ar_graph = graph->ar_graph; + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + struct audioreach_graph *ar_graph; + bool protected = q6apm_graph_has_protection(graph); + int ret; - if (--ar_graph->start_count > 0) - return 0; + if (!active) + return -ESHUTDOWN; + ar_graph = graph->ar_graph; + if (protected) + mutex_lock(&ar_graph->protection_lock); + if (ar_graph->start_count <= 0) { + ret = 0; + goto unlock; + } + if (--ar_graph->start_count > 0) { + ret = 0; + goto unlock; + } + + if (protected) + ret = audioreach_graph_client_mgmt_cmd(graph, APM_CMD_GRAPH_STOP); + else + ret = audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_STOP); + if (ret) + ar_graph->start_count++; + if (ret && protected) { + ar_graph->protection_faulted = true; + ar_graph->execution_uncertain = true; + } else if (protected) { + ar_graph->execution_uncertain = false; + ar_graph->protection_configured = false; + ar_graph->protection_bypass_confirmed = false; + /* Complete backend clears deferred by DPCM teardown ordering. */ + ar_graph->protection_vi_ready = false; + ar_graph->protection_cps_ready = false; + ar_graph->prepared = false; + ar_graph->prepare_uncertain = false; + } - return audioreach_graph_mgmt_cmd(ar_graph, APM_CMD_GRAPH_STOP); +unlock: + if (protected) + mutex_unlock(&ar_graph->protection_lock); + return ret; } EXPORT_SYMBOL_GPL(q6apm_graph_stop); +bool q6apm_graph_execution_uncertain(struct q6apm_graph *graph) +{ + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + struct audioreach_graph *ar_graph; + bool uncertain; + + if (!active) + return false; + if (!q6apm_graph_has_protection(graph)) + return false; + ar_graph = graph->ar_graph; + + mutex_lock(&ar_graph->protection_lock); + uncertain = ar_graph->execution_uncertain; + mutex_unlock(&ar_graph->protection_lock); + + return uncertain; +} +EXPORT_SYMBOL_GPL(q6apm_graph_execution_uncertain); + +void q6apm_graph_quarantine_dma(struct q6apm_graph *graph) +{ + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + struct audioreach_graph *ar_graph; + + if (!active) + return; + if (!q6apm_graph_has_protection(graph)) + return; + ar_graph = graph->ar_graph; + + mutex_lock(&ar_graph->protection_lock); + ar_graph->dma_quarantined = true; + mutex_unlock(&ar_graph->protection_lock); +} +EXPORT_SYMBOL_GPL(q6apm_graph_quarantine_dma); + +bool q6apm_graph_dma_quarantined(struct device *dev, unsigned int graph_id) +{ + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct audioreach_graph *graph; + bool quarantined = false; + + if (!apm) + return false; + + guard(mutex)(&apm->client_lock); + mutex_lock(&apm->lock); + spin_lock(&apm->graph_lock); + graph = idr_find(&apm->graph_idr, graph_id); + if (graph && !kref_get_unless_zero(&graph->refcount)) + graph = NULL; + spin_unlock(&apm->graph_lock); + mutex_unlock(&apm->lock); + if (!graph) + return false; + + mutex_lock(&graph->protection_lock); + quarantined = graph->dma_quarantined; + mutex_unlock(&graph->protection_lock); + kref_put(&graph->refcount, q6apm_put_audioreach_graph); + + return quarantined; +} +EXPORT_SYMBOL_GPL(q6apm_graph_dma_quarantined); + +bool q6apm_graph_id_has_protection(struct device *dev, unsigned int graph_id) +{ + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct audioreach_graph_info *info; + int profile = 0; + + if (!apm) + return false; + guard(mutex)(&apm->client_lock); + mutex_lock(&apm->lock); + info = idr_find(&apm->graph_info_idr, graph_id); + if (info) + profile = audioreach_graph_protection_profile(info); + mutex_unlock(&apm->lock); + + /* A malformed partial declaration is protected and must fail closed. */ + return profile != 0; +} +EXPORT_SYMBOL_GPL(q6apm_graph_id_has_protection); + int q6apm_graph_flush(struct q6apm_graph *graph) { + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + + if (!active) + return -ESHUTDOWN; + if (q6apm_graph_has_protection(graph)) + return audioreach_graph_client_mgmt_cmd(graph, + APM_CMD_GRAPH_FLUSH); + return audioreach_graph_mgmt_cmd(graph->ar_graph, APM_CMD_GRAPH_FLUSH); } EXPORT_SYMBOL_GPL(q6apm_graph_flush); +int q6apm_graph_sp11_soft_pause(struct q6apm_graph *graph, bool pause) +{ + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + struct apm_module_param_data *param; + struct audioreach_module *module; + size_t payload_size = ALIGN(sizeof(*param), 8); + + if (!active) + return -ESHUTDOWN; + if (!q6apm_graph_is_sp11_pull(graph)) + return -EOPNOTSUPP; + + module = q6apm_find_module_by_mid(graph, SP11_MODULE_ID_SOFT_PAUSE); + if (!module || module->instance_id != SP11_SOFT_PAUSE_IID) + return -EOPNOTSUPP; + + struct gpr_pkt *pkt __free(kfree) = + audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + param = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + memset(param, 0, payload_size); + param->module_instance_id = module->instance_id; + param->param_id = pause ? SP11_PARAM_SOFT_PAUSE_PAUSE : + SP11_PARAM_SOFT_PAUSE_RESUME; + + return q6apm_send_cmd_sync(graph->apm, pkt, 0); +} +EXPORT_SYMBOL_GPL(q6apm_graph_sp11_soft_pause); + static int q6apm_audio_probe(struct snd_soc_component *component) { return audioreach_tplg_init(component); @@ -863,11 +2470,15 @@ static int apm_probe(gpr_device_t *gdev) dev_set_drvdata(dev, apm); mutex_init(&apm->lock); + mutex_init(&apm->client_lock); + spin_lock_init(&apm->result_lock); + spin_lock_init(&apm->graph_lock); apm->dev = dev; apm->gdev = gdev; init_waitqueue_head(&apm->wait); INIT_LIST_HEAD(&apm->widget_list); + INIT_LIST_HEAD(&apm->graph_client_list); idr_init(&apm->graph_idr); idr_init(&apm->graph_info_idr); idr_init(&apm->sub_graphs_idr); @@ -875,7 +2486,9 @@ static int apm_probe(gpr_device_t *gdev) idr_init(&apm->modules_idr); + mutex_lock(&g_apm_lock); g_apm = apm; + mutex_unlock(&g_apm_lock); q6apm_get_apm_state(apm); @@ -892,45 +2505,212 @@ static int apm_probe(gpr_device_t *gdev) return ret; } +static void q6apm_abort_global_cmd(struct q6apm *apm) +{ + spin_lock(&apm->result_lock); + apm->cmd_pending = false; + spin_unlock(&apm->result_lock); + wake_up(&apm->wait); +} + +static void q6apm_detach_graph_clients(struct q6apm *apm) +{ + struct q6apm_graph *graph, *next; + LIST_HEAD(detaching); + + mutex_lock(&apm->client_lock); + apm->removing = true; + list_for_each_entry(graph, &apm->graph_client_list, node) + q6apm_graph_mark_dying(graph); + list_splice_init(&apm->graph_client_list, &detaching); + mutex_unlock(&apm->client_lock); + q6apm_abort_global_cmd(apm); + /* Drain the command thread awakened by the abort before teardown. */ + mutex_lock(&apm->lock); + mutex_unlock(&apm->lock); + + list_for_each_entry_safe(graph, next, &detaching, node) { + struct audioreach_graph *ar_graph = graph->ar_graph; + unsigned long flags; + bool retain_dma = false; + + list_del_init(&graph->node); + q6apm_graph_wait_users(graph); + gpr_free_port(graph->port); + + if (ar_graph && graph->protection_runtime) { + mutex_lock(&ar_graph->protection_lock); + retain_dma = ar_graph->start_count > 0 || + ar_graph->execution_uncertain || + ar_graph->dma_quarantined; + if (retain_dma) + ar_graph->dma_quarantined = true; + mutex_unlock(&ar_graph->protection_lock); + } + + spin_lock_irqsave(&graph->lifecycle_lock, flags); + graph->port = NULL; + graph->ar_graph = NULL; + graph->info = NULL; + graph->apm = NULL; + graph->retain_dma_on_detach = retain_dma; + graph->detached = true; + spin_unlock_irqrestore(&graph->lifecycle_lock, flags); + complete_all(&graph->detached_done); + /* The ALSA owner releases the detached client on its close path. */ + } +} + +static void q6apm_remove_graph_state(struct q6apm *apm) +{ + struct audioreach_graph *ar_graph; + int id = 0; + + guard(mutex)(&apm->client_lock); + while ((ar_graph = idr_get_next(&apm->graph_idr, &id))) { + idr_remove(&apm->graph_idr, id); + if (ar_graph->oob_virt || ar_graph->position_virt) { + dev_err(apm->dev, + "retaining graph %u protected DMA until reboot after service removal\n", + ar_graph->id); + /* Keep the DMA descriptor and its device reference pinned. */ + } else { + kfree(ar_graph->graph); + put_device(ar_graph->dma_dev); + kfree(ar_graph); + } + id++; + } +} + static void apm_remove(gpr_device_t *gdev) { + struct q6apm *apm = dev_get_drvdata(&gdev->dev); + + mutex_lock(&g_apm_lock); + if (g_apm == apm) + g_apm = NULL; + mutex_unlock(&g_apm_lock); + q6apm_detach_graph_clients(apm); of_platform_depopulate(&gdev->dev); snd_soc_unregister_component(&gdev->dev); + q6apm_remove_graph_state(apm); } struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid) { - struct audioreach_graph_info *info = graph->info; - struct q6apm *apm = graph->apm; + struct q6apm_graph *active __free(q6apm_graph_user) = + q6apm_graph_user_get(graph) ? graph : NULL; + + if (!active) + return NULL; + + return __q6apm_find_module_by_mid(graph->apm, graph->info, mid); +} - return __q6apm_find_module_by_mid(apm, info, mid); +static bool __q6apm_cmd_response_expected(struct q6apm *apm, + const struct gpr_hdr *hdr, + u32 opcode, u32 status, + bool basic_response) +{ + bool expected; + + expected = apm->cmd_pending && apm->pending_token == hdr->token && + (apm->pending_opcode == opcode || + apm->pending_rsp_opcode == opcode); + if (expected && basic_response && !status && + apm->pending_rsp_opcode && apm->pending_rsp_opcode != opcode) + expected = false; + return expected; +} + +static bool q6apm_try_complete_cmd(struct q6apm *apm, + const struct gpr_hdr *hdr, u32 opcode, + u32 status, bool basic_response) +{ + bool expected; + + spin_lock(&apm->result_lock); + expected = __q6apm_cmd_response_expected(apm, hdr, opcode, status, + basic_response); + if (expected) { + apm->result.status = status; + apm->result_token = hdr->token; + apm->result.opcode = opcode; + } + spin_unlock(&apm->result_lock); + + if (expected) + wake_up(&apm->wait); + return expected; } static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) { gpr_device_t *gdev = priv; - struct audioreach_graph_info *info; + struct audioreach_graph *graph = NULL; + struct audioreach_graph_info *info = NULL; struct q6apm *apm = dev_get_drvdata(&gdev->dev); struct apm_cmd_rsp_shared_mem_map_regions *rsp; struct device *dev = &gdev->dev; struct gpr_ibasic_rsp_result_t *result; const struct gpr_hdr *hdr = &data->hdr; - int graph_id, is_pos_buf; + bool expected; + int graph_id, is_graph_pos, is_oob, is_pos_buf; result = data->payload; switch (hdr->opcode) { case APM_CMD_RSP_GET_SPF_STATE: - apm->result.opcode = hdr->opcode; - apm->result.status = 0; - /* First word of result it state */ - apm->state = result->opcode; - wake_up(&apm->wait); + /* First word of result is the state. */ + spin_lock(&apm->result_lock); + expected = __q6apm_cmd_response_expected(apm, hdr, hdr->opcode, + 0, false); + if (expected) { + apm->state = result->opcode; + apm->result.status = 0; + apm->result_token = hdr->token; + apm->result.opcode = hdr->opcode; + } + spin_unlock(&apm->result_lock); + if (expected) + wake_up(&apm->wait); break; case GPR_BASIC_RSP_RESULT: switch (result->opcode) { case APM_CMD_SHARED_MEM_MAP_REGIONS: + spin_lock(&apm->result_lock); + expected = __q6apm_cmd_response_expected(apm, hdr, + result->opcode, + result->status, true); + if (expected && result->status) { + graph_id = hdr->token & APM_MMAP_TOKEN_GID_MASK; + is_oob = hdr->token & APM_MMAP_TOKEN_MAP_TYPE_OOB; + is_graph_pos = q6apm_is_graph_position_token(hdr->token); + spin_lock(&apm->graph_lock); + graph = idr_find(&apm->graph_idr, graph_id); + if (graph && is_oob && graph->oob_token == hdr->token) + graph->oob_map_uncertain = false; + else if (graph && is_graph_pos && + graph->position_token == hdr->token) + graph->position_map_uncertain = false; + spin_unlock(&apm->graph_lock); + } + if (expected) { + apm->result.status = result->status; + apm->result_token = hdr->token; + apm->result.opcode = result->opcode; + } + spin_unlock(&apm->result_lock); + if (expected) { + wake_up(&apm->wait); + if (result->status) + dev_err(dev, + "Error (%d) Processing 0x%08x cmd\n", + result->status, result->opcode); + } + break; case APM_CMD_GRAPH_START: case APM_CMD_GRAPH_OPEN: case APM_CMD_GRAPH_PREPARE: @@ -938,50 +2718,130 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) case APM_CMD_GRAPH_FLUSH: case APM_CMD_GRAPH_STOP: case APM_CMD_SET_CFG: - apm->result.opcode = result->opcode; - apm->result.status = result->status; - if (result->status) + expected = q6apm_try_complete_cmd(apm, hdr, + result->opcode, + result->status, true); + if (expected && result->status) dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, result->opcode); - wake_up(&apm->wait); break; case APM_CMD_SHARED_MEM_UNMAP_REGIONS: - apm->result.opcode = hdr->opcode; - apm->result.status = 0; - rsp = data->payload; - - info = idr_find(&apm->graph_info_idr, hdr->token); - if (info) - info->mem_map_handle = 0; - else - dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, - result->opcode); + if (result->status) { + expected = q6apm_try_complete_cmd(apm, hdr, + result->opcode, + result->status, + true); + if (expected) { + dev_err(dev, + "Error (%d) Processing 0x%08x cmd\n", + result->status, result->opcode); + } + break; + } - wake_up(&apm->wait); + graph_id = hdr->token & APM_MMAP_TOKEN_GID_MASK; + is_oob = hdr->token & APM_MMAP_TOKEN_MAP_TYPE_OOB; + is_pos_buf = + hdr->token & APM_MMAP_TOKEN_MAP_TYPE_POS_BUF; + is_graph_pos = q6apm_is_graph_position_token(hdr->token); + spin_lock(&apm->result_lock); + expected = __q6apm_cmd_response_expected(apm, hdr, + result->opcode, + 0, true); + if (is_oob && expected) { + spin_lock(&apm->graph_lock); + graph = idr_find(&apm->graph_idr, graph_id); + if (graph && graph->oob_token == hdr->token) + graph->oob_mem_map_handle = 0; + else + graph = NULL; + spin_unlock(&apm->graph_lock); + } else if (is_graph_pos && expected) { + spin_lock(&apm->graph_lock); + graph = idr_find(&apm->graph_idr, graph_id); + if (graph && graph->position_token == hdr->token) + graph->position_mem_map_handle = 0; + else + graph = NULL; + spin_unlock(&apm->graph_lock); + } else if (expected) { + info = idr_find(&apm->graph_info_idr, graph_id); + if (info && is_pos_buf) + info->pos_buf_mem_map_handle = 0; + else if (info) + info->mem_map_handle = 0; + } + if (expected) { + apm->result.status = 0; + apm->result_token = hdr->token; + apm->result.opcode = result->opcode; + } + spin_unlock(&apm->result_lock); + if (expected) { + wake_up(&apm->wait); + if ((is_oob || is_graph_pos) ? !graph : !info) + dev_err(dev, + "no mapping for 0x%08x response token 0x%08x\n", + result->opcode, hdr->token); + } break; default: break; } break; case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS: - apm->result.opcode = hdr->opcode; - apm->result.status = 0; rsp = data->payload; graph_id = hdr->token & APM_MMAP_TOKEN_GID_MASK; + is_oob = hdr->token & APM_MMAP_TOKEN_MAP_TYPE_OOB; is_pos_buf = hdr->token & APM_MMAP_TOKEN_MAP_TYPE_POS_BUF; - - info = idr_find(&apm->graph_info_idr, graph_id); - if (info) { + is_graph_pos = q6apm_is_graph_position_token(hdr->token); + + spin_lock(&apm->result_lock); + expected = __q6apm_cmd_response_expected(apm, hdr, hdr->opcode, + 0, false); + if (is_oob && expected) { + spin_lock(&apm->graph_lock); + graph = idr_find(&apm->graph_idr, graph_id); + if (graph && graph->oob_token == hdr->token) { + graph->oob_mem_map_handle = rsp->mem_map_handle; + graph->oob_map_uncertain = false; + } else { + graph = NULL; + } + spin_unlock(&apm->graph_lock); + } else if (is_graph_pos && expected) { + spin_lock(&apm->graph_lock); + graph = idr_find(&apm->graph_idr, graph_id); + if (graph && graph->position_token == hdr->token) { + graph->position_mem_map_handle = rsp->mem_map_handle; + graph->position_map_uncertain = false; + } else { + graph = NULL; + } + spin_unlock(&apm->graph_lock); + } else if (expected) { + info = idr_find(&apm->graph_info_idr, graph_id); + } + if (!is_oob && !is_graph_pos && info) { if (is_pos_buf) info->pos_buf_mem_map_handle = rsp->mem_map_handle; else info->mem_map_handle = rsp->mem_map_handle; - } else { - dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status, - result->opcode); } - wake_up(&apm->wait); + if (expected) { + apm->result.status = 0; + apm->result_token = hdr->token; + apm->result.opcode = hdr->opcode; + } + spin_unlock(&apm->result_lock); + if (expected) + wake_up(&apm->wait); + if (expected && ((is_oob || is_graph_pos) ? !graph : !info)) { + dev_err(dev, + "no graph for 0x%08x response token 0x%08x\n", + hdr->opcode, hdr->token); + } break; default: break; diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 5cb51ca491dc4f..0f89bf59b568e8 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -3,6 +3,7 @@ #define __Q6APM_H__ #include #include +#include #include #include #include @@ -42,6 +43,8 @@ #define APM_CLIENT_EVENT_DATA_WRITE_DONE 0x1009 #define APM_CLIENT_EVENT_DATA_READ_DONE 0x100a #define APM_CLIENT_EVENT_WATERMARK_EVENT 0x100b +#define APM_CLIENT_EVENT_SOFT_PAUSE_COMPLETE 0x100c +#define APM_CLIENT_EVENT_SOFT_RESUME_COMPLETE 0x100d #define APM_WRITE_TOKEN_MASK GENMASK(15, 0) #define APM_WRITE_TOKEN_LEN_MASK GENMASK(31, 16) #define APM_WRITE_TOKEN_LEN_SHIFT 16 @@ -52,17 +55,32 @@ struct q6apm { struct device *dev; + struct device *dma_dev; gpr_port_t *port; gpr_device_t *gdev; /* For Graph OPEN/START/STOP/CLOSE operations */ wait_queue_head_t wait; struct gpr_ibasic_rsp_result_t result; + u32 result_token; + u32 pending_opcode; + u32 pending_rsp_opcode; + u32 pending_token; + u32 cmd_token; + bool cmd_pending; struct mutex cmd_lock; struct mutex lock; + /* Serialize graph-client publication against service removal. */ + struct mutex client_lock; + /* Protect command matching from asynchronous GPR responses. */ + spinlock_t result_lock; + /* Protect graph lifetime during asynchronous GPR responses. */ + spinlock_t graph_lock; uint32_t state; struct list_head widget_list; + struct list_head graph_client_list; + bool removing; struct idr graph_idr; struct idr graph_info_idr; struct idr sub_graphs_idr; @@ -87,6 +105,43 @@ struct audioreach_graph { uint32_t id; int state; int start_count; + bool initializing; + struct device *dma_dev; + void *oob_virt; + dma_addr_t oob_dma; + phys_addr_t oob_dsp_addr; + size_t oob_size; + u32 oob_mem_map_handle; + u32 oob_token; + /* Serialize use of the graph's shared OOB buffer. */ + struct mutex oob_lock; + /* Serialize topology-derived protection state for this graph only. */ + struct mutex protection_lock; + bool protection_profile; + bool protection_malformed; + bool protection_runtime; + bool protection_available; + bool protection_configured; + bool protection_bypass_confirmed; + bool protection_vi_ready; + bool protection_cps_ready; + bool protection_faulted; + bool prepared; + bool prepare_uncertain; + bool close_confirmed; + bool execution_uncertain; + bool dma_quarantined; + bool oob_map_uncertain; + bool oob_transfer_uncertain; + void *position_virt; + dma_addr_t position_dma; + phys_addr_t position_dsp_addr; + size_t position_size; + u32 position_mem_map_handle; + u32 position_token; + bool position_map_uncertain; + /* Serialize teardown of the DSP-owned position mapping. */ + struct mutex position_lock; /* Cached Graph data */ void *graph; struct kref refcount; @@ -102,24 +157,55 @@ struct q6apm_graph { uint32_t shm_iid; struct device *dev; struct q6apm *apm; + struct list_head node; + bool retained; + bool protection_profile; + bool protection_runtime; + bool is_push_pull_mode; + bool dying; + bool detached; + bool retain_dma_on_detach; + unsigned int active_users; + /* Protect admission and detach state for graph API users. */ + spinlock_t lifecycle_lock; + wait_queue_head_t users_wait; + struct completion detached_done; gpr_port_t *port; struct audioreach_graph_data rx_data; struct audioreach_graph_data tx_data; struct gpr_ibasic_rsp_result_t result; + u32 result_token; + u32 pending_opcode; + u32 pending_rsp_opcode; + u32 pending_token; + u32 cmd_token; + bool cmd_pending; wait_queue_head_t cmd_wait; + /* Serialize synchronous commands without blocking data callbacks. */ + struct mutex cmd_lock; struct mutex lock; + /* Protect command matching from asynchronous client responses. */ + spinlock_t result_lock; struct audioreach_graph *ar_graph; struct audioreach_graph_info *info; }; /* Graph Operations */ struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb, - void *priv, int graph_id, int dir); + void *priv, int graph_id, int dir, + bool enable_protection); +bool q6apm_graph_user_get(struct q6apm_graph *graph); +void q6apm_graph_user_put(struct q6apm_graph *graph); int q6apm_graph_close(struct q6apm_graph *graph); int q6apm_graph_prepare(struct q6apm_graph *graph); int q6apm_graph_start(struct q6apm_graph *graph); int q6apm_graph_stop(struct q6apm_graph *graph); int q6apm_graph_flush(struct q6apm_graph *graph); +int q6apm_graph_sp11_soft_pause(struct q6apm_graph *graph, bool pause); +bool q6apm_graph_execution_uncertain(struct q6apm_graph *graph); +void q6apm_graph_quarantine_dma(struct q6apm_graph *graph); +bool q6apm_graph_dma_quarantined(struct device *dev, unsigned int graph_id); +bool q6apm_graph_id_has_protection(struct device *dev, unsigned int graph_id); /* Media Format */ int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, @@ -147,8 +233,24 @@ int q6apm_alloc_fragments(struct q6apm_graph *graph, int q6apm_free_fragments(struct q6apm_graph *graph, unsigned int dir); int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id); /* Helpers */ -int q6apm_send_cmd_sync(struct q6apm *apm, const struct gpr_pkt *pkt, +int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode); +int q6apm_send_oob_config(struct audioreach_graph *graph, + const void *data, size_t size); +int q6apm_send_graph_oob_config(struct q6apm_graph *graph, + const void *data, size_t size); +int q6apm_graph_id_for_backend(struct device *dev, int backend_id); +bool q6apm_graph_has_protection(const struct q6apm_graph *graph); +int q6apm_graph_configure_protection(struct q6apm_graph *graph); + +enum q6apm_protection_backend { + Q6APM_PROTECTION_BACKEND_VI, + Q6APM_PROTECTION_BACKEND_CPS, +}; + +int q6apm_set_protection_backend_ready(struct device *dev, int backend_id, + enum q6apm_protection_backend backend, + bool ready); /* Callback for graph specific */ struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, @@ -162,8 +264,13 @@ int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, uint int q6apm_get_hw_pointer(struct q6apm_graph *graph, int dir); bool q6apm_is_graph_in_push_pull_mode(struct q6apm_graph *graph); bool q6apm_is_graph_in_push_pull_mode_from_id(struct device *dev, unsigned int graph_id, int dir); +bool q6apm_graph_is_sp11_pull(struct q6apm_graph *graph); +bool q6apm_graph_id_is_sp11_pull(struct device *dev, unsigned int graph_id); +snd_pcm_uframes_t q6apm_get_pull_hw_pointer(struct q6apm_graph *graph, + struct snd_pcm_runtime *runtime); int q6apm_push_pull_config(struct q6apm_graph *graph, phys_addr_t bphys, phys_addr_t pphys, uint32_t size); int q6apm_register_watermark_event(struct q6apm_graph *graph, int watermark_bytes, int num_levels); +int q6apm_register_sp11_soft_pause_events(struct q6apm_graph *graph); #endif /* __APM_GRAPH_ */ diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c index e5cd82f77b5520..6cdd6586ff1f8c 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2020, Linaro Limited +#include +#include #include #include #include @@ -694,6 +696,7 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, struct q6dsp_audio_port_dai_driver_config *cfg, int *num_dais) { + struct snd_soc_dai_driver *dais = q6dsp_audio_fe_dais; int i; for (i = 0; i < ARRAY_SIZE(q6dsp_audio_fe_dais); i++) { @@ -729,6 +732,43 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, } *num_dais = ARRAY_SIZE(q6dsp_audio_fe_dais); - return q6dsp_audio_fe_dais; + if (!of_machine_is_compatible("microsoft,denali") || + !of_device_is_compatible(dev->of_node, "qcom,q6apm-lpass-dais")) + return dais; + + dais = devm_kmemdup(dev, q6dsp_audio_fe_dais, sizeof(q6dsp_audio_fe_dais), GFP_KERNEL); + if (!dais) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < *num_dais; i++) { + switch (dais[i].id) { + case WSA_CODEC_DMA_TX_0: + dais[i].playback = (struct snd_soc_pcm_stream) { + .stream_name = "WSA_CODEC_DMA_TX_0 Protection", + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 8000, + }; + break; + case WSA_CODEC_DMA_TX_1: + dais[i].playback = (struct snd_soc_pcm_stream) { + .stream_name = "WSA_CODEC_DMA_TX_1 Protection", + .rates = SNDRV_PCM_RATE_24000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .rate_min = 24000, + .rate_max = 24000, + }; + break; + default: + break; + } + } + + return dais; } EXPORT_SYMBOL_GPL(q6dsp_audio_ports_set_config); diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index 1f69fba6de26d1..4aa949ad42ab80 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -2,6 +2,7 @@ // Copyright (c) 2020, Linaro Limited #include +#include #include #include #include @@ -47,6 +48,7 @@ static struct audioreach_graph_info *audioreach_tplg_alloc_graph_info(struct q6a return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&info->sg_list); + mutex_init(&info->calibration_lock); mutex_lock(&apm->lock); ret = idr_alloc_u32(&apm->graph_info_idr, info, &graph_id, graph_id, GFP_KERNEL); @@ -138,6 +140,8 @@ static struct audioreach_container *audioreach_tplg_alloc_container(struct q6apm if (!cont) return ERR_PTR(-ENOMEM); + cont->parent_container_id = U32_MAX; + cont->heap_id = APM_CONT_HEAP_DEFAULT; INIT_LIST_HEAD(&cont->modules_list); mutex_lock(&apm->lock); @@ -206,131 +210,256 @@ static struct audioreach_module *audioreach_tplg_alloc_module(struct q6apm *apm, return mod; } -static const struct snd_soc_tplg_vendor_array * -audioreach_get_sg_array(const struct snd_soc_tplg_private *private) +static bool audioreach_is_raw_private_type(u32 type) { - const struct snd_soc_tplg_vendor_array *sg_array = NULL; - bool found = false; - int sz; - - for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) { - const struct snd_soc_tplg_vendor_value_elem *sg_elem; - int tkn_count = 0; - - sg_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); - sg_elem = sg_array->value; - sz = sz + le32_to_cpu(sg_array->size); - while (!found && tkn_count <= (le32_to_cpu(sg_array->num_elems) - 1)) { - switch (le32_to_cpu(sg_elem->token)) { - case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: - found = true; - break; - default: - break; - } - tkn_count++; - sg_elem++; + return type == SND_SOC_AR_TPLG_MODULE_CFG_TYPE || + type == SND_SOC_AR_TPLG_CTRL_LINK_CFG_TYPE || + type == SND_SOC_AR_TPLG_GRAPH_CAL_CFG_TYPE || + type == SND_SOC_AR_TPLG_RENDER_EP_CFG_TYPE || + type == SND_SOC_AR_TPLG_SP_TAG_CFG_TYPE || + type == SND_SOC_AR_TPLG_SPVI_TAG_CFG_TYPE || + type == SND_SOC_AR_TPLG_VI_EP_CFG_TYPE || + type == SND_SOC_AR_TPLG_PROTECTION_DYNAMIC_CFG_TYPE || + type == SND_SOC_AR_TPLG_VOLUME_GAIN_CFG_TYPE || + type == SND_SOC_AR_TPLG_VOLUME_FILTER_CFG_TYPE || + type == SND_SOC_AR_TPLG_VOLUME_MUTE_CFG_TYPE || + type == SND_SOC_AR_TPLG_CHANNEL_MIXER_CFG_TYPE; +} + +static int +audioreach_next_private_block(const struct snd_soc_tplg_private *private, + size_t *offset, + const struct snd_soc_tplg_vendor_array **array, + bool *raw) +{ + const struct audioreach_module_priv_data *header; + size_t private_size = le32_to_cpu(private->size); + size_t block_size, elements_size, remaining; + u32 array_size, num_elems, type; + + if (*offset == private_size) + return 0; + if (*offset > private_size) + return -EINVAL; + + remaining = private_size - *offset; + if (remaining < 2 * sizeof(__le32)) + return -EINVAL; + header = (const void *)((const u8 *)private->array + *offset); + array_size = le32_to_cpu(header->size); + type = le32_to_cpu(header->type); + *raw = audioreach_is_raw_private_type(type); + *array = (const void *)header; + + if (*raw) { + if (remaining < sizeof(*header) || + check_add_overflow(sizeof(*header), (size_t)array_size, + &block_size) || + block_size > remaining) + return -EINVAL; + } else { + if (remaining < sizeof(**array) || + array_size < sizeof(**array) || array_size > remaining) + return -EINVAL; + block_size = array_size; + if (type == SND_SOC_TPLG_TUPLE_TYPE_WORD) { + num_elems = le32_to_cpu((*array)->num_elems); + if (check_mul_overflow((size_t)num_elems, + sizeof((*array)->value[0]), + &elements_size) || + elements_size > block_size - sizeof(**array)) + return -EINVAL; } } - if (found) - return sg_array; - - return NULL; + *offset += block_size; + return 1; } static const struct snd_soc_tplg_vendor_array * -audioreach_get_cont_array(const struct snd_soc_tplg_private *private) +audioreach_get_token_array(const struct snd_soc_tplg_private *private, u32 token) { - const struct snd_soc_tplg_vendor_array *cont_array = NULL; - bool found = false; - int sz; - - for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) { - const struct snd_soc_tplg_vendor_value_elem *cont_elem; - int tkn_count = 0; - - cont_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); - cont_elem = cont_array->value; - sz = sz + le32_to_cpu(cont_array->size); - while (!found && tkn_count <= (le32_to_cpu(cont_array->num_elems) - 1)) { - switch (le32_to_cpu(cont_elem->token)) { - case AR_TKN_U32_CONTAINER_INSTANCE_ID: - found = true; - break; - default: - break; - } - tkn_count++; - cont_elem++; + const struct snd_soc_tplg_vendor_array *array; + const struct snd_soc_tplg_vendor_array *found = NULL; + size_t offset = 0; + bool raw; + int ret; + + while ((ret = audioreach_next_private_block(private, &offset, + &array, &raw)) > 0) { + u32 count, i; + + if (raw || le32_to_cpu(array->type) != + SND_SOC_TPLG_TUPLE_TYPE_WORD) + continue; + count = le32_to_cpu(array->num_elems); + for (i = 0; i < count; i++) { + if (le32_to_cpu(array->value[i].token) != token) + continue; + if (found) + return ERR_PTR(-EEXIST); + found = array; } } - if (found) - return cont_array; + return ret < 0 ? ERR_PTR(ret) : found; +} - return NULL; +static const struct snd_soc_tplg_vendor_array * +audioreach_get_sg_array(const struct snd_soc_tplg_private *private) +{ + return audioreach_get_token_array(private, + AR_TKN_U32_SUB_GRAPH_INSTANCE_ID); +} + +static const struct snd_soc_tplg_vendor_array * +audioreach_get_cont_array(const struct snd_soc_tplg_private *private) +{ + return audioreach_get_token_array(private, + AR_TKN_U32_CONTAINER_INSTANCE_ID); } static const struct snd_soc_tplg_vendor_array * audioreach_get_module_array(const struct snd_soc_tplg_private *private) { - const struct snd_soc_tplg_vendor_array *mod_array = NULL; - bool found = false; - int sz = 0; - - for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) { - const struct snd_soc_tplg_vendor_value_elem *mod_elem; - int tkn_count = 0; - - mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); - mod_elem = mod_array->value; - sz = sz + le32_to_cpu(mod_array->size); - while (!found && tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) { - switch (le32_to_cpu(mod_elem->token)) { - case AR_TKN_U32_MODULE_INSTANCE_ID: - found = true; - break; - default: - break; - } - tkn_count++; - mod_elem++; + return audioreach_get_token_array(private, + AR_TKN_U32_MODULE_INSTANCE_ID); +} + +static struct audioreach_module_priv_data * +audioreach_get_module_priv_data(const struct snd_soc_tplg_private *private, + u32 requested_type) +{ + const struct snd_soc_tplg_vendor_array *array; + struct audioreach_module_priv_data *pdata = NULL; + size_t offset = 0; + bool raw; + int ret; + + while ((ret = audioreach_next_private_block(private, &offset, + &array, &raw)) > 0) { + size_t block_size, size; + + if (!raw || le32_to_cpu(array->type) != requested_type) + continue; + if (pdata) { + ret = -EEXIST; + goto error; } + + size = le32_to_cpu(array->size); + if (check_add_overflow(sizeof(*pdata), size, &block_size)) + return ERR_PTR(-EINVAL); + pdata = kmemdup(array, block_size, GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); } - if (found) - return mod_array; + if (ret < 0) + goto error; - return NULL; + return pdata; + +error: + kfree(pdata); + return ERR_PTR(ret); } -static struct audioreach_module_priv_data * -audioreach_get_module_priv_data(const struct snd_soc_tplg_private *private) +static int +audioreach_validate_ctrl_links(const struct audioreach_module_priv_data *pdata) { - int sz; + size_t size = le32_to_cpu(pdata->size); + const u8 *cursor = (const u8 *)pdata->data; + const u8 *end = cursor + size; + u32 links, link, property; - for (sz = 0; sz < le32_to_cpu(private->size); ) { - const struct snd_soc_tplg_vendor_array *mod_array; + if (size < sizeof(u32)) + return -EINVAL; - mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); - if (le32_to_cpu(mod_array->type) == SND_SOC_AR_TPLG_MODULE_CFG_TYPE) { - struct audioreach_module_priv_data *pdata; + links = get_unaligned_le32(cursor); + if (!links) + return -EINVAL; + cursor += sizeof(u32); + for (link = 0; link < links; link++) { + u32 properties; + + if (end - cursor < 5 * sizeof(u32)) + return -EINVAL; + properties = get_unaligned_le32(cursor + 4 * sizeof(u32)); + cursor += 5 * sizeof(u32); + + for (property = 0; property < properties; property++) { + u32 property_size; + + if (end - cursor < 2 * sizeof(u32)) + return -EINVAL; + property_size = get_unaligned_le32(cursor + sizeof(u32)); + cursor += 2 * sizeof(u32); + if (property_size > end - cursor) + return -EINVAL; + cursor += property_size; + } + } - pdata = kzalloc_flex(*pdata, data, - le32_to_cpu(mod_array->size)); - if (!pdata) - return ERR_PTR(-ENOMEM); + return cursor == end ? 0 : -EINVAL; +} - memcpy(pdata, ((u8 *)private->data + sz), struct_size(pdata, data, - le32_to_cpu(mod_array->size))); - return pdata; - } +static void audioreach_free_extended_data(struct audioreach_module *mod) +{ + kfree(mod->ctrl_link_data); + kfree(mod->graph_cal_data); + kfree(mod->render_ep_data); + kfree(mod->sp_tag_data); + kfree(mod->spvi_tag_data); + kfree(mod->vi_ep_data); + kfree(mod->protection_dynamic_data); + kfree(mod->volume_gain_data); + kfree(mod->volume_filter_data); + kfree(mod->volume_mute_data); + kfree(mod->channel_mixer_data); +} - sz = sz + le32_to_cpu(mod_array->size); +static int audioreach_load_extended_data(struct audioreach_module *mod, + const struct snd_soc_tplg_private *private) +{ + static const u32 types[] = { + SND_SOC_AR_TPLG_GRAPH_CAL_CFG_TYPE, + SND_SOC_AR_TPLG_RENDER_EP_CFG_TYPE, + SND_SOC_AR_TPLG_SP_TAG_CFG_TYPE, + SND_SOC_AR_TPLG_SPVI_TAG_CFG_TYPE, + SND_SOC_AR_TPLG_VI_EP_CFG_TYPE, + SND_SOC_AR_TPLG_PROTECTION_DYNAMIC_CFG_TYPE, + SND_SOC_AR_TPLG_VOLUME_GAIN_CFG_TYPE, + SND_SOC_AR_TPLG_VOLUME_FILTER_CFG_TYPE, + SND_SOC_AR_TPLG_VOLUME_MUTE_CFG_TYPE, + SND_SOC_AR_TPLG_CHANNEL_MIXER_CFG_TYPE, + }; + struct audioreach_module_priv_data **destinations[] = { + &mod->graph_cal_data, + &mod->render_ep_data, + &mod->sp_tag_data, + &mod->spvi_tag_data, + &mod->vi_ep_data, + &mod->protection_dynamic_data, + &mod->volume_gain_data, + &mod->volume_filter_data, + &mod->volume_mute_data, + &mod->channel_mixer_data, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(types); i++) { + *destinations[i] = + audioreach_get_module_priv_data(private, types[i]); + if (IS_ERR(*destinations[i])) { + int ret = PTR_ERR(*destinations[i]); + + *destinations[i] = NULL; + return ret; + } } - return NULL; + return 0; } static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm, @@ -340,13 +469,17 @@ static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm const struct snd_soc_tplg_vendor_array *sg_array; struct audioreach_graph_info *info = NULL; int graph_id, sub_graph_id, tkn_count = 0; - struct audioreach_sub_graph *sg; + struct audioreach_sub_graph *sg = NULL; bool found; sg_array = audioreach_get_sg_array(private); + if (IS_ERR(sg_array)) + return ERR_CAST(sg_array); + if (!sg_array) + return ERR_PTR(-EINVAL); sg_elem = sg_array->value; - while (tkn_count <= (le32_to_cpu(sg_array->num_elems) - 1)) { + while (tkn_count < le32_to_cpu(sg_array->num_elems)) { switch (le32_to_cpu(sg_elem->token)) { case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: sub_graph_id = le32_to_cpu(sg_elem->value); @@ -366,12 +499,18 @@ static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm return ERR_CAST(info); break; case AR_TKN_U32_SUB_GRAPH_PERF_MODE: + if (!sg) + return ERR_PTR(-EINVAL); sg->perf_mode = le32_to_cpu(sg_elem->value); break; case AR_TKN_U32_SUB_GRAPH_DIRECTION: + if (!sg) + return ERR_PTR(-EINVAL); sg->direction = le32_to_cpu(sg_elem->value); break; case AR_TKN_U32_SUB_GRAPH_SCENARIO_ID: + if (!sg) + return ERR_PTR(-EINVAL); sg->scenario_id = le32_to_cpu(sg_elem->value); break; default: @@ -382,6 +521,8 @@ static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm tkn_count++; sg_elem++; } + if (!sg) + return ERR_PTR(-EINVAL); /* Sub graph is associated with predefined graph */ if (info) @@ -396,14 +537,18 @@ static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *a { const struct snd_soc_tplg_vendor_value_elem *cont_elem; const struct snd_soc_tplg_vendor_array *cont_array; - struct audioreach_container *cont; + struct audioreach_container *cont = NULL; int container_id, tkn_count = 0; bool found = false; cont_array = audioreach_get_cont_array(private); + if (IS_ERR(cont_array)) + return ERR_CAST(cont_array); + if (!cont_array) + return ERR_PTR(-EINVAL); cont_elem = cont_array->value; - while (tkn_count <= (le32_to_cpu(cont_array->num_elems) - 1)) { + while (tkn_count < le32_to_cpu(cont_array->num_elems)) { switch (le32_to_cpu(cont_elem->token)) { case AR_TKN_U32_CONTAINER_INSTANCE_ID: container_id = le32_to_cpu(cont_elem->value); @@ -412,17 +557,37 @@ static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *a return cont; break; case AR_TKN_U32_CONTAINER_CAPABILITY_ID: + if (!cont) + return ERR_PTR(-EINVAL); cont->capability_id = le32_to_cpu(cont_elem->value); break; case AR_TKN_U32_CONTAINER_STACK_SIZE: + if (!cont) + return ERR_PTR(-EINVAL); cont->stack_size = le32_to_cpu(cont_elem->value); break; case AR_TKN_U32_CONTAINER_GRAPH_POS: + if (!cont) + return ERR_PTR(-EINVAL); cont->graph_pos = le32_to_cpu(cont_elem->value); break; case AR_TKN_U32_CONTAINER_PROC_DOMAIN: + if (!cont) + return ERR_PTR(-EINVAL); cont->proc_domain = le32_to_cpu(cont_elem->value); break; + case AR_TKN_U32_CONTAINER_PARENT_ID: + if (!cont) + return ERR_PTR(-EINVAL); + cont->parent_container_id = le32_to_cpu(cont_elem->value); + cont->has_extended_properties = true; + break; + case AR_TKN_U32_CONTAINER_HEAP_ID: + if (!cont) + return ERR_PTR(-EINVAL); + cont->heap_id = le32_to_cpu(cont_elem->value); + cont->has_extended_properties = true; + break; default: dev_err(apm->dev, "Not a valid token %d for graph\n", cont_elem->token); break; @@ -432,7 +597,7 @@ static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *a cont_elem++; } - return cont; + return cont ?: ERR_PTR(-EINVAL); } static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *apm, @@ -445,6 +610,8 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap uint32_t dst_mod_inst_id[AR_MAX_MOD_LINKS] = { 0, }; uint32_t dst_mod_ip_port_id[AR_MAX_MOD_LINKS] = { 0, }; uint32_t src_mod_inst_id = 0; + u32 integrated_backend_id = 0; + bool speaker_protection_bypass = false; int module_id = 0, instance_id = 0, tkn_count = 0; const struct snd_soc_tplg_vendor_value_elem *mod_elem; @@ -455,9 +622,13 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap int max_tokens; mod_array = audioreach_get_module_array(private); + if (IS_ERR(mod_array)) + return ERR_CAST(mod_array); + if (!mod_array) + return ERR_PTR(-EINVAL); mod_elem = mod_array->value; max_tokens = le32_to_cpu(mod_array->num_elems); - while (tkn_count <= (max_tokens - 1)) { + while (tkn_count < max_tokens) { token = le32_to_cpu(mod_elem->token); switch (token) { /* common module info */ @@ -482,6 +653,8 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap break; case AR_TKN_U32_MODULE_MAX_OP_PORTS: max_op_port = le32_to_cpu(mod_elem->value); + if (max_op_port > AR_MAX_MOD_LINKS) + return ERR_PTR(-EINVAL); break; case AR_TKN_U32_MODULE_SRC_INSTANCE_ID: src_mod_inst_id = le32_to_cpu(mod_elem->value); @@ -558,6 +731,12 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap case AR_TKN_U32_MODULE_DST_IN_PORT_ID7: dst_mod_ip_port_id[7] = le32_to_cpu(mod_elem->value); break; + case AR_TKN_U32_MODULE_SPEAKER_PROTECTION_BYPASS: + speaker_protection_bypass = !!le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_INTEGRATED_BACKEND_ID: + integrated_backend_id = le32_to_cpu(mod_elem->value); + break; default: break; @@ -573,6 +752,8 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap mod->max_ip_port = max_ip_port; mod->max_op_port = max_op_port; mod->src_mod_inst_id = src_mod_inst_id; + mod->speaker_protection_bypass = speaker_protection_bypass; + mod->integrated_backend_id = integrated_backend_id; for (pn = 0; pn < mod->max_op_port; pn++) { if (src_mod_op_port_id[pn] && dst_mod_inst_id[pn] && dst_mod_ip_port_id[pn]) { @@ -585,7 +766,7 @@ static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *ap } } - return mod; + return mod ?: ERR_PTR(-EINVAL); } static int audioreach_widget_load_module_common(struct snd_soc_component *component, @@ -597,6 +778,7 @@ static int audioreach_widget_load_module_common(struct snd_soc_component *compon struct audioreach_sub_graph *sg; struct audioreach_module *mod; struct snd_soc_dobj *dobj; + const u32 ctrl_type = SND_SOC_AR_TPLG_CTRL_LINK_CFG_TYPE; sg = audioreach_parse_sg_tokens(apm, &tplg_w->priv); if (IS_ERR(sg)) @@ -610,12 +792,34 @@ static int audioreach_widget_load_module_common(struct snd_soc_component *compon if (IS_ERR_OR_NULL(mod)) return mod ? PTR_ERR(mod) : -ENODEV; - mod->data = audioreach_get_module_priv_data(&tplg_w->priv); - dobj = &w->dobj; dobj->private = mod; - return 0; + mod->data = audioreach_get_module_priv_data(&tplg_w->priv, + SND_SOC_AR_TPLG_MODULE_CFG_TYPE); + if (IS_ERR(mod->data)) { + int ret = PTR_ERR(mod->data); + + mod->data = NULL; + return ret; + } + + mod->ctrl_link_data = + audioreach_get_module_priv_data(&tplg_w->priv, ctrl_type); + if (IS_ERR(mod->ctrl_link_data)) { + int ret = PTR_ERR(mod->ctrl_link_data); + + mod->ctrl_link_data = NULL; + return ret; + } + if (mod->ctrl_link_data) { + int ret = audioreach_validate_ctrl_links(mod->ctrl_link_data); + + if (ret) + return ret; + } + + return audioreach_load_extended_data(mod, &tplg_w->priv); } static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *component, @@ -636,9 +840,13 @@ static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *componen dobj = &w->dobj; mod = dobj->private; mod_array = audioreach_get_module_array(&tplg_w->priv); + if (IS_ERR(mod_array)) + return PTR_ERR(mod_array); + if (!mod_array) + return -EINVAL; mod_elem = mod_array->value; - while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) { + while (tkn_count < le32_to_cpu(mod_array->num_elems)) { switch (le32_to_cpu(mod_elem->token)) { case AR_TKN_U32_MODULE_FMT_INTERLEAVE: mod->interleave_type = le32_to_cpu(mod_elem->value); @@ -793,8 +1001,17 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component, mod = dobj->private; mod_array = audioreach_get_module_array(&tplg_w->priv); + if (IS_ERR(mod_array)) + return PTR_ERR(mod_array); + if (!mod_array) + return -EINVAL; switch (mod->module_id) { + case MODULE_ID_WR_SHARED_MEM_EP: + case MODULE_ID_RD_SHARED_MEM_EP: + case MODULE_ID_SH_MEM_PULL_MODE: + case MODULE_ID_SH_MEM_PUSH_MODE: + break; case MODULE_ID_CODEC_DMA_SINK: case MODULE_ID_CODEC_DMA_SOURCE: audioreach_widget_dma_module_load(mod, mod_array); @@ -810,7 +1027,8 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component, audioreach_widget_dp_module_load(mod, mod_array); break; default: - return -EINVAL; + /* Common tokens are sufficient for modules without a special loader. */ + break; } return 0; @@ -919,16 +1137,14 @@ static int audioreach_widget_ready(struct snd_soc_component *component, switch (w->id) { case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: - audioreach_widget_load_buffer(component, index, w, tplg_w); - break; + return audioreach_widget_load_buffer(component, index, w, tplg_w); case snd_soc_dapm_decoder: case snd_soc_dapm_encoder: case snd_soc_dapm_src: - audioreach_widget_load_enc_dec_cnv(component, index, w, tplg_w); - break; + return audioreach_widget_load_enc_dec_cnv(component, index, w, + tplg_w); case snd_soc_dapm_buffer: - audioreach_widget_load_buffer(component, index, w, tplg_w); - break; + return audioreach_widget_load_buffer(component, index, w, tplg_w); case snd_soc_dapm_mixer: return audioreach_widget_load_mixer(component, index, w, tplg_w); case snd_soc_dapm_pga: @@ -972,6 +1188,7 @@ static int audioreach_widget_unload(struct snd_soc_component *scomp, list_del(&mod->node); kfree(mod->data); + audioreach_free_extended_data(mod); kfree(mod); /* Graph Info has N sub-graphs, sub-graph has N containers, Container has N Modules */ if (list_empty(&cont->modules_list)) { /* if no modules in the container then remove it */ @@ -1115,12 +1332,14 @@ static void audioreach_connect_sub_graphs(struct q6apm *apm, info->src_mod_op_port_id = 1; info->dst_mod_inst_id = m2->module_instance_id; info->dst_mod_ip_port_id = 2; + info->internal_vmixer_connection = m1->graph_id == m2->graph_id; } else { info->src_mod_inst_id = 0; info->src_mod_op_port_id = 0; info->dst_mod_inst_id = 0; info->dst_mod_ip_port_id = 0; + info->internal_vmixer_connection = false; } } diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c index c92fe652f544ff..bfebdfea4d8e2d 100644 --- a/sound/soc/qcom/x1e80100.c +++ b/sound/soc/qcom/x1e80100.c @@ -2,16 +2,19 @@ // Copyright (c) 2023, Linaro Limited #include +#include #include #include #include #include +#include #include #include #include #include "common.h" #include "qdsp6/q6afe.h" +#include "qdsp6/q6apm.h" #include "qdsp6/q6dsp-common.h" #include "sdw.h" @@ -19,6 +22,7 @@ struct x1e80100_snd_cfg { const char *driver_name; const unsigned int *channels_map; int channels_num; + bool protected_speaker_feedback; }; struct x1e80100_snd_data { @@ -41,21 +45,30 @@ static int x1e80100_snd_init(struct snd_soc_pcm_runtime *rtd) switch (cpu_dai->id) { case WSA_CODEC_DMA_RX_0: case WSA_CODEC_DMA_RX_1: - /* - * Set limit of -3 dB on Digital Volume and 0 dB on PA Volume - * to reduce the risk of speaker damage until we have active - * speaker protection in place. - */ + /* Keep the -3 dB digital limit on every X1E80100 card. */ snd_soc_limit_volume(card, "WSA WSA_RX0 Digital Volume", 81); snd_soc_limit_volume(card, "WSA WSA_RX1 Digital Volume", 81); snd_soc_limit_volume(card, "WSA2 WSA_RX0 Digital Volume", 81); snd_soc_limit_volume(card, "WSA2 WSA_RX1 Digital Volume", 81); - snd_soc_limit_volume(card, "SpkrLeft PA Volume", 6); - snd_soc_limit_volume(card, "SpkrRight PA Volume", 6); - snd_soc_limit_volume(card, "WooferLeft PA Volume", 6); - snd_soc_limit_volume(card, "TweeterLeft PA Volume", 6); - snd_soc_limit_volume(card, "WooferRight PA Volume", 6); - snd_soc_limit_volume(card, "TweeterRight PA Volume", 6); + + /* + * Denali's hardware-qualified protected profile uses the full PA + * range and selects its operating point in UCM. Keep the upstream + * 0 dB damage-prevention cap on every unprotected machine. + */ + if (!data->cfg->protected_speaker_feedback) { + snd_soc_limit_volume(card, "SpkrLeft PA Volume", 6); + snd_soc_limit_volume(card, "SpkrRight PA Volume", 6); + snd_soc_limit_volume(card, "WooferLeft PA Volume", 6); + snd_soc_limit_volume(card, "TweeterLeft PA Volume", 6); + snd_soc_limit_volume(card, "WooferRight PA Volume", 6); + snd_soc_limit_volume(card, "TweeterRight PA Volume", 6); + } + break; + case WSA_CODEC_DMA_TX_0: + case WSA_CODEC_DMA_TX_1: + if (data->cfg->protected_speaker_feedback) + return 0; break; case DISPLAY_PORT_RX_0: dp_pcm_id = 0; @@ -78,11 +91,14 @@ static int x1e80100_snd_init(struct snd_soc_pcm_runtime *rtd) static int x1e80100_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { + struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *format = hw_param_mask(params, + SNDRV_PCM_HW_PARAM_FORMAT); rate->min = rate->max = 48000; switch (cpu_dai->id) { @@ -92,6 +108,26 @@ static int x1e80100_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, case TX_CODEC_DMA_TX_3: channels->min = 1; break; + case WSA_CODEC_DMA_TX_0: + if (!data->cfg->protected_speaker_feedback) + break; + rate->min = 8000; + rate->max = 8000; + channels->min = 2; + channels->max = 2; + snd_mask_none(format); + snd_mask_set_format(format, SNDRV_PCM_FORMAT_S32_LE); + break; + case WSA_CODEC_DMA_TX_1: + if (!data->cfg->protected_speaker_feedback) + break; + rate->min = 24000; + rate->max = 24000; + channels->min = 2; + channels->max = 2; + snd_mask_none(format); + snd_mask_set_format(format, SNDRV_PCM_FORMAT_S32_LE); + break; default: break; } @@ -135,13 +171,55 @@ static int x1e80100_snd_hw_map_channels(struct x1e80100_snd_data *data, return 0; } +static int x1e80100_protection_backend(unsigned int dai_id) +{ + switch (dai_id) { + case WSA_CODEC_DMA_TX_0: + return Q6APM_PROTECTION_BACKEND_VI; + case WSA_CODEC_DMA_TX_1: + return Q6APM_PROTECTION_BACKEND_CPS; + default: + return -EINVAL; + } +} + +static int x1e80100_set_protection_ready(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *cpu_dai, + int backend, bool ready) +{ + int ret; + + ret = q6apm_set_protection_backend_ready(cpu_dai->dev, cpu_dai->id, + backend, ready); + /* + * DPCM tears the feedback BEs down before the final FE graph stop. + * Defer only the ready-to-not-ready transition while that graph still + * owns a start reference; the confirmed final stop clears both flags. + */ + if (ret == -EBUSY && !ready) { + dev_dbg(rtd->dev, + "deferring protected backend %d readiness clear\n", backend); + return 0; + } + if (ret && ret != -ENODEV) + dev_warn(rtd->dev, + "failed to update protected backend readiness: %d\n", ret); + + return ret; +} + static int x1e80100_snd_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct sdw_stream_runtime *sruntime; unsigned int channels = substream->runtime->channels; unsigned int rx_slot[4]; + unsigned int tx_slot[4]; + bool ready; + int backend; + int protection_ret; int ret; switch (cpu_dai->id) { @@ -156,11 +234,67 @@ static int x1e80100_snd_prepare(struct snd_pcm_substream *substream) if (ret) return ret; break; + case WSA_CODEC_DMA_TX_0: + case WSA_CODEC_DMA_TX_1: + if (!data->cfg->protected_speaker_feedback) + break; + + ret = x1e80100_snd_hw_map_channels(data, tx_slot, channels); + if (ret) + return ret; + + ret = snd_soc_dai_set_channel_map(cpu_dai, channels, tx_slot, + 0, NULL); + if (ret) + return ret; + break; default: break; } + if (data->cfg->protected_speaker_feedback) { + backend = x1e80100_protection_backend(cpu_dai->id); + if (backend >= 0) { + sruntime = qcom_snd_sdw_get_stream(substream); + if (IS_ERR_OR_NULL(sruntime)) { + data->stream_prepared[cpu_dai->id] = false; + ret = x1e80100_set_protection_ready(rtd, cpu_dai, + backend, false); + if (ret && ret != -ENODEV) + return ret; + dev_warn(rtd->dev, + "speaker-feedback backend %d has no SoundWire runtime; using bypass\n", + cpu_dai->id); + return 0; + } + } + } + + ret = qcom_snd_sdw_prepare(substream, + &data->stream_prepared[cpu_dai->id]); + if (!data->cfg->protected_speaker_feedback) + return ret; + + backend = x1e80100_protection_backend(cpu_dai->id); + if (backend < 0) + return ret; - return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); + ready = !ret && data->stream_prepared[cpu_dai->id]; + protection_ret = x1e80100_set_protection_ready(rtd, cpu_dai, backend, + ready); + if (!ready) { + if (protection_ret) + return ret ? ret : protection_ret; + dev_warn(rtd->dev, + "speaker-feedback backend %d unavailable; using bypass\n", + cpu_dai->id); + return 0; + } + if (protection_ret && protection_ret != -ENODEV) { + qcom_snd_sdw_hw_free(substream, &data->stream_prepared[cpu_dai->id]); + return protection_ret; + } + + return 0; } static int x1e80100_snd_hw_free(struct snd_pcm_substream *substream) @@ -168,13 +302,46 @@ static int x1e80100_snd_hw_free(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + int backend; + int ret; + + if (data->cfg->protected_speaker_feedback) { + backend = x1e80100_protection_backend(cpu_dai->id); + if (backend >= 0) { + ret = x1e80100_set_protection_ready(rtd, cpu_dai, + backend, false); + if (ret && ret != -ENODEV) + return ret; + } + } return qcom_snd_sdw_hw_free(substream, &data->stream_prepared[cpu_dai->id]); } +static void x1e80100_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + int backend; + int ret; + + if (data->cfg->protected_speaker_feedback) { + backend = x1e80100_protection_backend(cpu_dai->id); + if (backend >= 0) { + ret = x1e80100_set_protection_ready(rtd, cpu_dai, + backend, false); + if (ret && ret != -ENODEV) + return; + } + } + + qcom_snd_sdw_shutdown(substream); +} + static const struct snd_soc_ops x1e80100_be_ops = { .startup = qcom_snd_sdw_startup, - .shutdown = qcom_snd_sdw_shutdown, + .shutdown = x1e80100_snd_shutdown, .hw_free = x1e80100_snd_hw_free, .prepare = x1e80100_snd_prepare, }; @@ -229,6 +396,11 @@ static const struct x1e80100_snd_cfg x1e80100_cfg = { .driver_name = "x1e80100", }; +static const struct x1e80100_snd_cfg denali_cfg = { + .driver_name = "x1e80100", + .protected_speaker_feedback = true, +}; + static const struct x1e80100_snd_cfg glymur_cfg = { .driver_name = "glymur", }; @@ -247,6 +419,7 @@ static const struct x1e80100_snd_cfg dell_xps13_9345_cfg = { }; static const struct of_device_id snd_x1e80100_dt_match[] = { + { .compatible = "microsoft,denali-sndcard", .data = &denali_cfg, }, { .compatible = "qcom,x1e80100-sndcard", .data = &x1e80100_cfg, }, { .compatible = "dell,xps13-9345-sndcard", .data = &dell_xps13_9345_cfg, }, { .compatible = "qcom,glymur-sndcard", .data = &glymur_cfg, }, diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 48ccc1d1854bc6..6db91b73f25c32 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -658,6 +658,7 @@ static int aries_audio_probe(struct platform_device *pdev) goto out; } + of_node_get(aries_dai[0].cpus->of_node); aries_dai[0].platforms->of_node = aries_dai[0].cpus->of_node; /* Set CPU of_node for BT DAI */ diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c index 7299c6bfcf1581..87e898cc35e424 100644 --- a/sound/soc/tegra/tegra210_admaif.c +++ b/sound/soc/tegra/tegra210_admaif.c @@ -26,94 +26,162 @@ #define CH_RX_REG(reg, id) CH_REG(admaif->soc_data->rx_base, reg, id) -#define REG_DEFAULTS(id, rx_ctrl, tx_ctrl, tx_base, rx_base, cif_ctrl) \ +#define RX_REG_DEFAULTS(id, rx_ctrl, rx_base, cif_ctrl) \ { CH_REG(rx_base, TEGRA_ADMAIF_RX_INT_MASK, id), 0x00000001 }, \ { CH_REG(rx_base, TEGRA_ADMAIF_CH_ACIF_RX_CTRL, id), cif_ctrl }, \ - { CH_REG(rx_base, TEGRA_ADMAIF_RX_FIFO_CTRL, id), rx_ctrl }, \ + { CH_REG(rx_base, TEGRA_ADMAIF_RX_FIFO_CTRL, id), rx_ctrl } + +#define TX_REG_DEFAULTS(id, tx_ctrl, tx_base, cif_ctrl) \ { CH_REG(tx_base, TEGRA_ADMAIF_TX_INT_MASK, id), 0x00000001 }, \ { CH_REG(tx_base, TEGRA_ADMAIF_CH_ACIF_TX_CTRL, id), cif_ctrl }, \ { CH_REG(tx_base, TEGRA_ADMAIF_TX_FIFO_CTRL, id), tx_ctrl } -#define ADMAIF_REG_DEFAULTS(id, chip) \ - REG_DEFAULTS((id) - 1, \ +#define ADMAIF_RX_REG_DEFAULTS(id, chip) \ + RX_REG_DEFAULTS((id) - 1, \ chip ## _ADMAIF_RX ## id ## _FIFO_CTRL_REG_DEFAULT, \ + chip ## _ADMAIF_RX_BASE, \ + chip ## _ADMAIF_CIF_REG_DEFAULT) + +#define ADMAIF_TX_REG_DEFAULTS(id, chip) \ + TX_REG_DEFAULTS((id) - 1, \ chip ## _ADMAIF_TX ## id ## _FIFO_CTRL_REG_DEFAULT, \ chip ## _ADMAIF_TX_BASE, \ - chip ## _ADMAIF_RX_BASE, \ chip ## _ADMAIF_CIF_REG_DEFAULT) static const struct reg_default tegra186_admaif_reg_defaults[] = { - {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA186_ADMAIF_GLOBAL_BASE), 0x00000003}, - ADMAIF_REG_DEFAULTS(1, TEGRA186), - ADMAIF_REG_DEFAULTS(2, TEGRA186), - ADMAIF_REG_DEFAULTS(3, TEGRA186), - ADMAIF_REG_DEFAULTS(4, TEGRA186), - ADMAIF_REG_DEFAULTS(5, TEGRA186), - ADMAIF_REG_DEFAULTS(6, TEGRA186), - ADMAIF_REG_DEFAULTS(7, TEGRA186), - ADMAIF_REG_DEFAULTS(8, TEGRA186), - ADMAIF_REG_DEFAULTS(9, TEGRA186), - ADMAIF_REG_DEFAULTS(10, TEGRA186), - ADMAIF_REG_DEFAULTS(11, TEGRA186), - ADMAIF_REG_DEFAULTS(12, TEGRA186), - ADMAIF_REG_DEFAULTS(13, TEGRA186), - ADMAIF_REG_DEFAULTS(14, TEGRA186), - ADMAIF_REG_DEFAULTS(15, TEGRA186), - ADMAIF_REG_DEFAULTS(16, TEGRA186), - ADMAIF_REG_DEFAULTS(17, TEGRA186), - ADMAIF_REG_DEFAULTS(18, TEGRA186), - ADMAIF_REG_DEFAULTS(19, TEGRA186), - ADMAIF_REG_DEFAULTS(20, TEGRA186) + ADMAIF_RX_REG_DEFAULTS(1, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(2, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(3, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(4, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(5, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(6, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(7, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(8, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(9, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(10, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(11, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(12, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(13, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(14, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(15, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(16, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(17, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(18, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(19, TEGRA186), + ADMAIF_RX_REG_DEFAULTS(20, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(1, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(2, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(3, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(4, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(5, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(6, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(7, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(8, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(9, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(10, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(11, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(12, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(13, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(14, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(15, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(16, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(17, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(18, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(19, TEGRA186), + ADMAIF_TX_REG_DEFAULTS(20, TEGRA186), + {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA186_ADMAIF_GLOBAL_BASE), 0x00000003} }; static const struct reg_default tegra210_admaif_reg_defaults[] = { - {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA210_ADMAIF_GLOBAL_BASE), 0x00000003}, - ADMAIF_REG_DEFAULTS(1, TEGRA210), - ADMAIF_REG_DEFAULTS(2, TEGRA210), - ADMAIF_REG_DEFAULTS(3, TEGRA210), - ADMAIF_REG_DEFAULTS(4, TEGRA210), - ADMAIF_REG_DEFAULTS(5, TEGRA210), - ADMAIF_REG_DEFAULTS(6, TEGRA210), - ADMAIF_REG_DEFAULTS(7, TEGRA210), - ADMAIF_REG_DEFAULTS(8, TEGRA210), - ADMAIF_REG_DEFAULTS(9, TEGRA210), - ADMAIF_REG_DEFAULTS(10, TEGRA210) + ADMAIF_RX_REG_DEFAULTS(1, TEGRA210), + ADMAIF_RX_REG_DEFAULTS(2, TEGRA210), + ADMAIF_RX_REG_DEFAULTS(3, TEGRA210), + ADMAIF_RX_REG_DEFAULTS(4, TEGRA210), + ADMAIF_RX_REG_DEFAULTS(5, TEGRA210), + ADMAIF_RX_REG_DEFAULTS(6, TEGRA210), + ADMAIF_RX_REG_DEFAULTS(7, TEGRA210), + ADMAIF_RX_REG_DEFAULTS(8, TEGRA210), + ADMAIF_RX_REG_DEFAULTS(9, TEGRA210), + ADMAIF_RX_REG_DEFAULTS(10, TEGRA210), + ADMAIF_TX_REG_DEFAULTS(1, TEGRA210), + ADMAIF_TX_REG_DEFAULTS(2, TEGRA210), + ADMAIF_TX_REG_DEFAULTS(3, TEGRA210), + ADMAIF_TX_REG_DEFAULTS(4, TEGRA210), + ADMAIF_TX_REG_DEFAULTS(5, TEGRA210), + ADMAIF_TX_REG_DEFAULTS(6, TEGRA210), + ADMAIF_TX_REG_DEFAULTS(7, TEGRA210), + ADMAIF_TX_REG_DEFAULTS(8, TEGRA210), + ADMAIF_TX_REG_DEFAULTS(9, TEGRA210), + ADMAIF_TX_REG_DEFAULTS(10, TEGRA210), + {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA210_ADMAIF_GLOBAL_BASE), 0x00000003} }; static const struct reg_default tegra264_admaif_reg_defaults[] = { - {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA264_ADMAIF_GLOBAL_BASE), 0x00000003}, - ADMAIF_REG_DEFAULTS(1, TEGRA264), - ADMAIF_REG_DEFAULTS(2, TEGRA264), - ADMAIF_REG_DEFAULTS(3, TEGRA264), - ADMAIF_REG_DEFAULTS(4, TEGRA264), - ADMAIF_REG_DEFAULTS(5, TEGRA264), - ADMAIF_REG_DEFAULTS(6, TEGRA264), - ADMAIF_REG_DEFAULTS(7, TEGRA264), - ADMAIF_REG_DEFAULTS(8, TEGRA264), - ADMAIF_REG_DEFAULTS(9, TEGRA264), - ADMAIF_REG_DEFAULTS(10, TEGRA264), - ADMAIF_REG_DEFAULTS(11, TEGRA264), - ADMAIF_REG_DEFAULTS(12, TEGRA264), - ADMAIF_REG_DEFAULTS(13, TEGRA264), - ADMAIF_REG_DEFAULTS(14, TEGRA264), - ADMAIF_REG_DEFAULTS(15, TEGRA264), - ADMAIF_REG_DEFAULTS(16, TEGRA264), - ADMAIF_REG_DEFAULTS(17, TEGRA264), - ADMAIF_REG_DEFAULTS(18, TEGRA264), - ADMAIF_REG_DEFAULTS(19, TEGRA264), - ADMAIF_REG_DEFAULTS(20, TEGRA264), - ADMAIF_REG_DEFAULTS(21, TEGRA264), - ADMAIF_REG_DEFAULTS(22, TEGRA264), - ADMAIF_REG_DEFAULTS(23, TEGRA264), - ADMAIF_REG_DEFAULTS(24, TEGRA264), - ADMAIF_REG_DEFAULTS(25, TEGRA264), - ADMAIF_REG_DEFAULTS(26, TEGRA264), - ADMAIF_REG_DEFAULTS(27, TEGRA264), - ADMAIF_REG_DEFAULTS(28, TEGRA264), - ADMAIF_REG_DEFAULTS(29, TEGRA264), - ADMAIF_REG_DEFAULTS(30, TEGRA264), - ADMAIF_REG_DEFAULTS(31, TEGRA264), - ADMAIF_REG_DEFAULTS(32, TEGRA264) + ADMAIF_RX_REG_DEFAULTS(1, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(2, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(3, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(4, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(5, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(6, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(7, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(8, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(9, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(10, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(11, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(12, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(13, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(14, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(15, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(16, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(17, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(18, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(19, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(20, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(21, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(22, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(23, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(24, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(25, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(26, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(27, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(28, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(29, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(30, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(31, TEGRA264), + ADMAIF_RX_REG_DEFAULTS(32, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(1, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(2, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(3, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(4, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(5, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(6, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(7, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(8, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(9, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(10, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(11, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(12, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(13, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(14, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(15, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(16, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(17, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(18, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(19, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(20, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(21, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(22, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(23, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(24, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(25, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(26, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(27, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(28, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(29, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(30, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(31, TEGRA264), + ADMAIF_TX_REG_DEFAULTS(32, TEGRA264), + {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA264_ADMAIF_GLOBAL_BASE), 0x00000003} }; static bool tegra_admaif_wr_reg(struct device *dev, unsigned int reg) diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c index ff8c72fc38c5cb..fdcc5d62b720c5 100644 --- a/sound/soc/tegra/tegra210_i2s.c +++ b/sound/soc/tegra/tegra210_i2s.c @@ -23,9 +23,9 @@ static const struct reg_default tegra210_i2s_reg_defaults[] = { { TEGRA210_I2S_RX_CIF_CTRL, 0x00007700 }, { TEGRA210_I2S_TX_INT_MASK, 0x00000003 }, { TEGRA210_I2S_TX_CIF_CTRL, 0x00007700 }, + { TEGRA210_I2S_ENABLE, 0x0 }, { TEGRA210_I2S_CG, 0x1 }, { TEGRA210_I2S_TIMING, 0x0000001f }, - { TEGRA210_I2S_ENABLE, 0x1 }, /* * Below update does not have any effect on Tegra186 and Tegra194. * On Tegra210, I2S4 has "i2s4a" and "i2s4b" pins and below update @@ -38,13 +38,13 @@ static const struct reg_default tegra210_i2s_reg_defaults[] = { static const struct reg_default tegra264_i2s_reg_defaults[] = { { TEGRA210_I2S_RX_INT_MASK, 0x00000003 }, { TEGRA210_I2S_RX_CIF_CTRL, 0x00003f00 }, + { TEGRA264_I2S_RX_FIFO_WR_ACCESS_MODE, 0x1 }, { TEGRA264_I2S_TX_INT_MASK, 0x00000003 }, { TEGRA264_I2S_TX_CIF_CTRL, 0x00003f00 }, + { TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE, 0x1 }, + { TEGRA264_I2S_ENABLE, 0x0 }, { TEGRA264_I2S_CG, 0x1 }, { TEGRA264_I2S_TIMING, 0x0000001f }, - { TEGRA264_I2S_ENABLE, 0x1 }, - { TEGRA264_I2S_RX_FIFO_WR_ACCESS_MODE, 0x1 }, - { TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE, 0x1 }, }; static void tegra210_i2s_set_slot_ctrl(struct tegra210_i2s *i2s, @@ -201,9 +201,21 @@ static int tegra210_i2s_runtime_resume(struct device *dev) } regcache_cache_only(i2s->regmap, false); - regcache_sync(i2s->regmap); + err = regcache_sync(i2s->regmap); + if (err) + goto err; + + err = regmap_write(i2s->regmap, i2s->soc_data->enable_reg, I2S_EN); + if (err) + goto err; return 0; + +err: + regcache_cache_only(i2s->regmap, true); + clk_disable_unprepare(i2s->clk_i2s); + + return err; } static void tegra210_i2s_set_data_offset(struct tegra210_i2s *i2s, @@ -1133,6 +1145,7 @@ static const struct tegra_i2s_soc_data soc_data_tegra210 = { .regmap_conf = &tegra210_regmap_conf, .i2s_cmpnt = &tegra210_i2s_cmpnt, .max_ch = TEGRA210_I2S_MAX_CHANNEL, + .enable_reg = TEGRA210_I2S_ENABLE, .tx_offset = TEGRA210_I2S_TX_OFFSET, .i2s_ctrl_offset = TEGRA210_I2S_CTRL_OFFSET, .fsync_width_mask = I2S_CTRL_FSYNC_WIDTH_MASK, @@ -1144,6 +1157,7 @@ static const struct tegra_i2s_soc_data soc_data_tegra264 = { .regmap_conf = &tegra264_regmap_conf, .i2s_cmpnt = &tegra264_i2s_cmpnt, .max_ch = TEGRA264_I2S_MAX_CHANNEL, + .enable_reg = TEGRA264_I2S_ENABLE, .tx_offset = TEGRA264_I2S_TX_OFFSET, .i2s_ctrl_offset = TEGRA264_I2S_CTRL_OFFSET, .fsync_width_mask = TEGRA264_I2S_CTRL_FSYNC_WIDTH_MASK, diff --git a/sound/soc/tegra/tegra210_i2s.h b/sound/soc/tegra/tegra210_i2s.h index 42be2137342c4d..82292f96ab3676 100644 --- a/sound/soc/tegra/tegra210_i2s.h +++ b/sound/soc/tegra/tegra210_i2s.h @@ -150,6 +150,7 @@ struct tegra_i2s_soc_data { const struct regmap_config *regmap_conf; const struct snd_soc_component_driver *i2s_cmpnt; unsigned int max_ch; + unsigned int enable_reg; unsigned int tx_offset; unsigned int i2s_ctrl_offset; unsigned int fsync_width_mask; diff --git a/sound/soc/tegra/tegra210_mbdrc.c b/sound/soc/tegra/tegra210_mbdrc.c index f5d4a93dd6f162..1d78b4c1cc885e 100644 --- a/sound/soc/tegra/tegra210_mbdrc.c +++ b/sound/soc/tegra/tegra210_mbdrc.c @@ -20,33 +20,33 @@ #define MBDRC_FILTER_REG(reg, id) \ ((reg) + ((id) * TEGRA210_MBDRC_FILTER_PARAM_STRIDE)) -#define MBDRC_FILTER_REG_DEFAULTS(id) \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_IIR_CFG, id), 0x00000005}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_IN_ATTACK, id), 0x3e48590c}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_IN_RELEASE, id), 0x08414e9f}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_FAST_ATTACK, id), 0x7fffffff}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_IN_THRESHOLD, id), 0x06145082}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_OUT_THRESHOLD, id), 0x060d379b}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_1ST, id), 0x0000a000}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_2ND, id), 0x00002000}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_3RD, id), 0x00000b33}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_4TH, id), 0x00000800}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_RATIO_5TH, id), 0x0000019a}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_MAKEUP_GAIN, id), 0x00000002}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_INIT_GAIN, id), 0x00066666}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_GAIN_ATTACK, id), 0x00d9ba0e}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_GAIN_RELEASE, id), 0x3e48590c}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_FAST_RELEASE, id), 0x7ffff26a}, \ - { MBDRC_FILTER_REG(TEGRA210_MBDRC_CFG_RAM_CTRL, id), 0x4000} +#define MBDRC_FILTER_PARAM_REG_DEFAULTS(reg, val) \ + { MBDRC_FILTER_REG(reg, 0), val}, \ + { MBDRC_FILTER_REG(reg, 1), val}, \ + { MBDRC_FILTER_REG(reg, 2), val} static const struct reg_default tegra210_mbdrc_reg_defaults[] = { { TEGRA210_MBDRC_CFG, 0x0030de51}, { TEGRA210_MBDRC_CHANNEL_MASK, 0x00000003}, { TEGRA210_MBDRC_FAST_FACTOR, 0x30000800}, - MBDRC_FILTER_REG_DEFAULTS(0), - MBDRC_FILTER_REG_DEFAULTS(1), - MBDRC_FILTER_REG_DEFAULTS(2), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_IIR_CFG, 0x00000005), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_IN_ATTACK, 0x3e48590c), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_IN_RELEASE, 0x08414e9f), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_FAST_ATTACK, 0x7fffffff), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_IN_THRESHOLD, 0x06145082), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_OUT_THRESHOLD, 0x060d379b), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_RATIO_1ST, 0x0000a000), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_RATIO_2ND, 0x00002000), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_RATIO_3RD, 0x00000b33), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_RATIO_4TH, 0x00000800), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_RATIO_5TH, 0x0000019a), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_MAKEUP_GAIN, 0x00000002), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_INIT_GAIN, 0x00066666), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_GAIN_ATTACK, 0x00d9ba0e), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_GAIN_RELEASE, 0x3e48590c), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_FAST_RELEASE, 0x7ffff26a), + MBDRC_FILTER_PARAM_REG_DEFAULTS(TEGRA210_MBDRC_CFG_RAM_CTRL, 0x4000), }; /* Default MBDRC parameters */ diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c index a69774578d69f4..96259a6cbc59b2 100644 --- a/sound/soc/tegra/tegra210_mixer.c +++ b/sound/soc/tegra/tegra210_mixer.c @@ -57,10 +57,10 @@ static const struct reg_default tegra210_mixer_reg_defaults[] = { MIXER_TX_REG_DEFAULTS(3), MIXER_TX_REG_DEFAULTS(4), + { TEGRA210_MIXER_ENABLE, 0x0 }, { TEGRA210_MIXER_CG, 0x00000001}, { TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, 0x00004000}, { TEGRA210_MIXER_PEAKM_RAM_CTRL, 0x00004000}, - { TEGRA210_MIXER_ENABLE, 0x1 }, }; /* Default gain parameters */ @@ -86,11 +86,15 @@ static int tegra210_mixer_runtime_suspend(struct device *dev) static int tegra210_mixer_runtime_resume(struct device *dev) { struct tegra210_mixer *mixer = dev_get_drvdata(dev); + int err; regcache_cache_only(mixer->regmap, false); - regcache_sync(mixer->regmap); + err = regcache_sync(mixer->regmap); + if (err) + return err; - return 0; + return regmap_write(mixer->regmap, TEGRA210_MIXER_ENABLE, + TEGRA210_MIXER_EN); } static int tegra210_mixer_write_ram(struct tegra210_mixer *mixer, diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c index 9a0b85653c618e..d3b7cab8569973 100644 --- a/sound/usb/6fire/comm.c +++ b/sound/usb/6fire/comm.c @@ -36,11 +36,14 @@ static void usb6fire_comm_receiver_handler(struct urb *urb) struct midi_runtime *midi_rt = rt->chip->midi; if (!urb->status) { - if (rt->receiver_buffer[0] == 0x10) /* midi in event */ + u8 len = rt->receiver_buffer[1]; + + if (rt->receiver_buffer[0] == 0x10 && /* midi in event */ + len <= COMM_RECEIVER_BUFSIZE - 2 && + urb->actual_length >= len + 2) if (midi_rt) midi_rt->in_received(midi_rt, - rt->receiver_buffer + 2, - rt->receiver_buffer[1]); + rt->receiver_buffer + 2, len); } if (!rt->chip->shutdown) { diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c index bebb48cb9abc8c..c5c542d17ccc15 100644 --- a/sound/usb/bcd2000/bcd2000.c +++ b/sound/usb/bcd2000/bcd2000.c @@ -134,6 +134,9 @@ static void bcd2000_midi_send(struct bcd2000 *bcd2k) if (!midi_out_substream) return; + if (!bcd2k->midi_out_urb) + return; + /* copy command prefix bytes */ memcpy(bcd2k->midi_out_buf, device_cmd_prefix, sizeof(device_cmd_prefix)); @@ -178,7 +181,7 @@ static int bcd2000_midi_output_close(struct snd_rawmidi_substream *substream) { struct bcd2000 *bcd2k = substream->rmidi->private_data; - if (bcd2k->midi_out_active) { + if (bcd2k->midi_out_active && bcd2k->midi_out_urb) { usb_kill_urb(bcd2k->midi_out_urb); bcd2k->midi_out_active = 0; } @@ -348,11 +351,13 @@ static int bcd2000_init_midi(struct bcd2000 *bcd2k) static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k, struct usb_interface *interface) { - usb_kill_urb(bcd2k->midi_out_urb); - usb_kill_urb(bcd2k->midi_in_urb); + usb_poison_urb(bcd2k->midi_out_urb); + usb_poison_urb(bcd2k->midi_in_urb); usb_free_urb(bcd2k->midi_out_urb); usb_free_urb(bcd2k->midi_in_urb); + bcd2k->midi_out_urb = NULL; + bcd2k->midi_in_urb = NULL; if (bcd2k->intf) { usb_set_intfdata(bcd2k->intf, NULL); diff --git a/sound/usb/card.c b/sound/usb/card.c index 6a3b576fb06792..8b9d2ebf1809cd 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -1276,8 +1276,11 @@ static int usb_audio_resume(struct usb_interface *intf) list_for_each_entry(as, &chip->pcm_list, list) { err = snd_usb_pcm_resume(as); - if (err < 0) - goto err_out; + if (err < 0) { + if (!chip->system_suspend) + goto err_out; + goto out; + } } /* @@ -1286,8 +1289,11 @@ static int usb_audio_resume(struct usb_interface *intf) */ list_for_each_entry(mixer, &chip->mixer_list, list) { err = snd_usb_mixer_resume(mixer); - if (err < 0) - goto err_out; + if (err < 0) { + if (!chip->system_suspend) + goto err_out; + goto out; + } } list_for_each(p, &chip->midi_list) { diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c index cee5ec9d78a03d..5fc2131b45617f 100644 --- a/sound/usb/fcp.c +++ b/sound/usb/fcp.c @@ -494,7 +494,7 @@ static int fcp_ioctl_init(struct usb_mixer_interface *mixer, buf_size = init.step0_resp_size + init.step2_resp_size; void *resp __free(kfree) = - kmalloc(buf_size, GFP_KERNEL); + kzalloc(buf_size, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -1033,6 +1033,8 @@ static int fcp_init(struct usb_mixer_interface *mixer, step0_resp, private->step0_resp_size); if (err < 0) return err; + if (err != private->step0_resp_size) + return -EIO; err = fcp_init_notify(mixer); if (err < 0) diff --git a/sound/usb/midi.c b/sound/usb/midi.c index f8996416c3be15..7e5b1b13360f02 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -875,6 +875,8 @@ static void snd_usbmidi_novation_output(struct snd_usb_midi_out_endpoint *ep, if (!ep->ports[0].active) return; + if (ep->max_transfer < 3) + return; transfer_buffer = urb->transfer_buffer; count = snd_rawmidi_transmit(ep->ports[0].substream, &transfer_buffer[2], @@ -969,6 +971,8 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep, default: count = 2; } + if (ep->max_transfer < count) + return; count = snd_rawmidi_transmit(ep->ports[0].substream, urb->transfer_buffer, count); diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 71444c2898b4b2..9a28d7c6d923a4 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2692,6 +2692,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), { QUIRK_DATA_AUDIOFORMAT(2) { .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .fmt_bits = 24, .channels = 2, .iface = 2, .altsetting = 1, @@ -2703,11 +2704,16 @@ YAMAHA_DEVICE(0x7010, "UB99"), SNDRV_PCM_RATE_48000, .rate_min = 44100, .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + }, } }, { QUIRK_DATA_AUDIOFORMAT(3) { .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .fmt_bits = 24, .channels = 2, .iface = 3, .altsetting = 1, @@ -2719,6 +2725,10 @@ YAMAHA_DEVICE(0x7010, "UB99"), SNDRV_PCM_RATE_48000, .rate_min = 44100, .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + }, } }, QUIRK_COMPOSITE_END diff --git a/tools/include/linux/compiler.h b/tools/include/linux/compiler.h index f40bd2b04c2987..f2f54b0381680b 100644 --- a/tools/include/linux/compiler.h +++ b/tools/include/linux/compiler.h @@ -119,7 +119,7 @@ #define __read_mostly #ifndef __attribute_const__ -# define __attribute_const__ +# define __attribute_const__ __attribute__ ((__const__)) #endif #ifndef __maybe_unused diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 4d4caf17474281..107da0940e6fff 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -194,6 +194,8 @@ static bool is_rust_noreturn(const struct symbol *func) */ return str_ends_with(func->name, "_4core3num20from_str_radix_panic") || str_ends_with(func->name, "_4core3num22from_ascii_radix_panic") || + str_ends_with(func->name, "_4core3num28from_ascii_bytes_radix_panic") || + str_ends_with(func->name, "_4core3str16slice_error_fail") || str_ends_with(func->name, "_4core5sliceSp15copy_from_slice17len_mismatch_fail") || str_ends_with(func->name, "_4core6option13expect_failed") || str_ends_with(func->name, "_4core6option13unwrap_failed") || diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 5660e0f1043b4b..e03a3242b3d1ba 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -27,6 +27,7 @@ CFLAGS += -fno-strict-aliasing ifeq ($(CC_NO_CLANG), 0) CLANG_TARGET_FLAGS_arm := arm-linux-gnueabi CLANG_TARGET_FLAGS_arm64 := aarch64-linux-gnu + CLANG_TARGET_FLAGS_loongarch := loongarch64-linux-gnu CLANG_TARGET_FLAGS_m68k := m68k-linux-gnu CLANG_TARGET_FLAGS_mips := mipsel-linux-gnu CLANG_TARGET_FLAGS_powerpc := powerpc64le-linux-gnu @@ -1149,6 +1150,7 @@ ifndef NO_RUST ifneq ($(CROSS_COMPILE),) RUST_TARGET_FLAGS_arm := arm-unknown-linux-gnueabi RUST_TARGET_FLAGS_arm64 := aarch64-unknown-linux-gnu + RUST_TARGET_FLAGS_loongarch := loongarch64-unknown-linux-gnu RUST_TARGET_FLAGS_m68k := m68k-unknown-linux-gnu RUST_TARGET_FLAGS_mips := mipsel-unknown-linux-gnu RUST_TARGET_FLAGS_powerpc := powerpc64le-unknown-linux-gnu diff --git a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c index 2a6e61864ee015..3bc9e28a9b8a36 100644 --- a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c +++ b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c @@ -429,15 +429,88 @@ static bool pid_filter__has(struct pids_filtered *pids, pid_t pid) return bpf_map_lookup_elem(pids, &pid) != NULL; } +u64 ZERO = 0; + +/* + * Determine what type of argument and how many bytes to read from user space, using the + * value in the beauty_map. This is the relation of parameter type and its corresponding + * value in the beauty map, and how many bytes we read eventually: + * + * string: 1 -> size of string + * struct: size of struct -> size of struct + * buffer: -1 * (index of paired len) -> value of paired len (maximum: TRACE_AUG_MAX_BUF) + */ +static inline int augment_arg(struct syscall_enter_args *args, int i, + unsigned int *beauty_map, + struct beauty_payload_enter *payload, u64 offset) +{ + int index, value_size = sizeof(struct augmented_arg) - offsetof(struct augmented_arg, value); + struct augmented_arg *payload_offset; + s64 aug_size, size; + bool augmented; + void *arg; + + arg = (void *)args->args[i]; + augmented = false; + size = beauty_map[i]; + aug_size = size; /* size of the augmented data read from user space */ + + if (size == 0 || arg == NULL) + return 0; + + /* bounds check for the verifier */ + if (offset > sizeof(payload->aug_args) - sizeof(payload->aug_args[0])) + return -1; + barrier_var(offset); + payload_offset = (struct augmented_arg *)((void *)&payload->aug_args + offset); + + if (size == 1) { /* string */ + aug_size = bpf_probe_read_user_str(payload_offset->value, value_size, arg); + /* minimum of 0 to pass the verifier */ + if (aug_size < 0) + aug_size = 0; + + augmented = true; + } else if (size > 0 && size <= value_size) { /* struct */ + if (!bpf_probe_read_user(payload_offset->value, size, arg)) + augmented = true; + } else if ((int)size < 0 && size >= -6) { /* buffer */ + index = -(size + 1); + barrier_var(index); // Prevent clang (noticed with v18) from removing the &= 7 trick. + index &= 7; // Satisfy the bounds checking with the verifier in some kernels. + aug_size = args->args[index] > TRACE_AUG_MAX_BUF ? TRACE_AUG_MAX_BUF : args->args[index]; + + if (aug_size > 0) { + if (!bpf_probe_read_user(payload_offset->value, aug_size, arg)) + augmented = true; + } + } + + /* Augmented data size is limited to sizeof(augmented_arg->unnamed union with value field) */ + if (aug_size > value_size) + aug_size = value_size; + + /* write data to payload */ + if (augmented) { + int written = offsetof(struct augmented_arg, value) + aug_size; + + if (written < 0 || written > sizeof(struct augmented_arg)) + return -1; + + payload_offset->size = aug_size; + return written; + } + + return 0; +} + static int augment_sys_enter(void *ctx, struct syscall_enter_args *args) { - bool augmented, do_output = false; - int zero = 0, index, value_size = sizeof(struct augmented_arg) - offsetof(struct augmented_arg, value); + bool do_output = false; + int i, zero = 0, written; u64 output = 0; /* has to be u64, otherwise it won't pass the verifier */ - s64 aug_size, size; unsigned int nr, *beauty_map; struct beauty_payload_enter *payload; - void *arg, *payload_offset; /* fall back to do predefined tail call */ if (args == NULL) @@ -449,7 +522,6 @@ static int augment_sys_enter(void *ctx, struct syscall_enter_args *args) /* set up payload for output */ payload = bpf_map_lookup_elem(&beauty_payload_enter_map, &zero); - payload_offset = (void *)&payload->aug_args; if (beauty_map == NULL || payload == NULL) return 1; @@ -457,61 +529,30 @@ static int augment_sys_enter(void *ctx, struct syscall_enter_args *args) /* copy the sys_enter header, which has the syscall_nr */ __builtin_memcpy(&payload->args, args, sizeof(struct syscall_enter_args)); - /* - * Determine what type of argument and how many bytes to read from user space, using the - * value in the beauty_map. This is the relation of parameter type and its corresponding - * value in the beauty map, and how many bytes we read eventually: - * - * string: 1 -> size of string - * struct: size of struct -> size of struct - * buffer: -1 * (index of paired len) -> value of paired len (maximum: TRACE_AUG_MAX_BUF) - */ - for (int i = 0; i < 6; i++) { - arg = (void *)args->args[i]; - augmented = false; - size = beauty_map[i]; - aug_size = size; /* size of the augmented data read from user space */ - - if (size == 0 || arg == NULL) - continue; - - if (size == 1) { /* string */ - aug_size = bpf_probe_read_user_str(((struct augmented_arg *)payload_offset)->value, value_size, arg); - /* minimum of 0 to pass the verifier */ - if (aug_size < 0) - aug_size = 0; - - augmented = true; - } else if (size > 0 && size <= value_size) { /* struct */ - if (!bpf_probe_read_user(((struct augmented_arg *)payload_offset)->value, size, arg)) - augmented = true; - } else if ((int)size < 0 && size >= -6) { /* buffer */ - index = -(size + 1); - barrier_var(index); // Prevent clang (noticed with v18) from removing the &= 7 trick. - index &= 7; // Satisfy the bounds checking with the verifier in some kernels. - aug_size = args->args[index] > TRACE_AUG_MAX_BUF ? TRACE_AUG_MAX_BUF : args->args[index]; - - if (aug_size > 0) { - if (!bpf_probe_read_user(((struct augmented_arg *)payload_offset)->value, aug_size, arg)) - augmented = true; + if (bpf_ksym_exists(bpf_iter_num_new)) { + bpf_for(i, 0, 6) { + written = augment_arg(args, i, beauty_map, payload, output); + if (written < 0) + return 1; + if (written > 0) { + output += written; + /* + * guide the verifier to forget range of `output`, which + * helps to prove convergence of the loop + */ + output += ZERO; + do_output = true; } } - - /* Augmented data size is limited to sizeof(augmented_arg->unnamed union with value field) */ - if (aug_size > value_size) - aug_size = value_size; - - /* write data to payload */ - if (augmented) { - int written = offsetof(struct augmented_arg, value) + aug_size; - - if (written < 0 || written > sizeof(struct augmented_arg)) + } else { + for (i = 0; i < 6; i++) { + written = augment_arg(args, i, beauty_map, payload, output); + if (written < 0) return 1; - - ((struct augmented_arg *)payload_offset)->size = aug_size; - output += written; - payload_offset += written; - do_output = true; + if (written > 0) { + output += written; + do_output = true; + } } } diff --git a/tools/perf/util/hisi-ptt-decoder/hisi-ptt-pkt-decoder.c b/tools/perf/util/hisi-ptt-decoder/hisi-ptt-pkt-decoder.c index a17c423a526ddf..5db44556fc4f58 100644 --- a/tools/perf/util/hisi-ptt-decoder/hisi-ptt-pkt-decoder.c +++ b/tools/perf/util/hisi-ptt-decoder/hisi-ptt-pkt-decoder.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include "../color.h" @@ -73,29 +75,20 @@ static const char * const hisi_ptt_4dw_pkt_field_name[] = { [HISI_PTT_4DW_HEAD3] = "Header DW3", }; -union hisi_ptt_4dw { - struct { - uint32_t format : 2; - uint32_t type : 5; - uint32_t t9 : 1; - uint32_t t8 : 1; - uint32_t th : 1; - uint32_t so : 1; - uint32_t len : 10; - uint32_t time : 11; - }; - uint32_t value; -}; - static void hisi_ptt_print_pkt(const unsigned char *buf, int pos, const char *desc) { const char *color = PERF_COLOR_BLUE; + uint8_t byte; + uint32_t dw; int i; + dw = get_unaligned_le32(buf + pos); printf("."); color_fprintf(stdout, color, " %08x: ", pos); - for (i = 0; i < HISI_PTT_FIELD_LENTH; i++) - color_fprintf(stdout, color, "%02x ", buf[pos + i]); + for (i = 0; i < HISI_PTT_FIELD_LENTH; i++) { + byte = (dw >> (24 - i * 8)) & 0xFF; + color_fprintf(stdout, color, "%02x ", byte); + } for (i = 0; i < HISI_PTT_MAX_SPACE_LEN; i++) color_fprintf(stdout, color, " "); color_fprintf(stdout, color, " %s\n", desc); @@ -122,22 +115,30 @@ static int hisi_ptt_8dw_kpt_desc(const unsigned char *buf, int pos) static void hisi_ptt_4dw_print_dw0(const unsigned char *buf, int pos) { const char *color = PERF_COLOR_BLUE; - union hisi_ptt_4dw dw0; + uint8_t byte; + uint32_t dw; int i; - dw0.value = *(uint32_t *)(buf + pos); + dw = get_unaligned_le32(buf + pos); printf("."); color_fprintf(stdout, color, " %08x: ", pos); - for (i = 0; i < HISI_PTT_FIELD_LENTH; i++) - color_fprintf(stdout, color, "%02x ", buf[pos + i]); + for (i = 0; i < HISI_PTT_FIELD_LENTH; i++) { + byte = (dw >> (24 - i * 8)) & 0xFF; + color_fprintf(stdout, color, "%02x ", byte); + } for (i = 0; i < HISI_PTT_MAX_SPACE_LEN; i++) color_fprintf(stdout, color, " "); color_fprintf(stdout, color, " %s %x %s %x %s %x %s %x %s %x %s %x %s %x %s %x\n", - "Format", dw0.format, "Type", dw0.type, "T9", dw0.t9, - "T8", dw0.t8, "TH", dw0.th, "SO", dw0.so, "Length", - dw0.len, "Time", dw0.time); + "Format", FIELD_GET(HISI_PTT_HEAD0_4DW_FORMAT, dw), + "Type", FIELD_GET(HISI_PTT_HEAD0_4DW_TYPE, dw), + "T9", FIELD_GET(HISI_PTT_HEAD0_4DW_T9, dw), + "T8", FIELD_GET(HISI_PTT_HEAD0_4DW_T8, dw), + "TH", FIELD_GET(HISI_PTT_HEAD0_4DW_TH, dw), + "SO", FIELD_GET(HISI_PTT_HEAD0_4DW_SO, dw), + "Length", FIELD_GET(HISI_PTT_HEAD0_4DW_LEN, dw), + "Time", FIELD_GET(HISI_PTT_HEAD0_4DW_TIME, dw)); } static int hisi_ptt_4dw_kpt_desc(const unsigned char *buf, int pos) diff --git a/tools/perf/util/hisi-ptt-decoder/hisi-ptt-pkt-decoder.h b/tools/perf/util/hisi-ptt-decoder/hisi-ptt-pkt-decoder.h index e78f1b5bc836e9..333eee02b19d2a 100644 --- a/tools/perf/util/hisi-ptt-decoder/hisi-ptt-pkt-decoder.h +++ b/tools/perf/util/hisi-ptt-decoder/hisi-ptt-pkt-decoder.h @@ -9,12 +9,24 @@ #include #include +#include +#include #define HISI_PTT_8DW_CHECK_MASK GENMASK(31, 11) #define HISI_PTT_IS_8DW_PKT GENMASK(31, 11) #define HISI_PTT_MAX_SPACE_LEN 10 #define HISI_PTT_FIELD_LENTH 4 +/* Header DW0 fields for 4DW format */ +#define HISI_PTT_HEAD0_4DW_TIME GENMASK_U32(10, 0) +#define HISI_PTT_HEAD0_4DW_LEN GENMASK_U32(20, 11) +#define HISI_PTT_HEAD0_4DW_SO BIT_U32(21) +#define HISI_PTT_HEAD0_4DW_TH BIT_U32(22) +#define HISI_PTT_HEAD0_4DW_T8 BIT_U32(23) +#define HISI_PTT_HEAD0_4DW_T9 BIT_U32(24) +#define HISI_PTT_HEAD0_4DW_TYPE GENMASK_U32(29, 25) +#define HISI_PTT_HEAD0_4DW_FORMAT GENMASK_U32(31, 30) + enum hisi_ptt_pkt_type { HISI_PTT_4DW_PKT, HISI_PTT_8DW_PKT, diff --git a/tools/sched_ext/include/scx/compat.bpf.h b/tools/sched_ext/include/scx/compat.bpf.h index 87f15f2962348c..bcc0b4c84fc09a 100644 --- a/tools/sched_ext/include/scx/compat.bpf.h +++ b/tools/sched_ext/include/scx/compat.bpf.h @@ -31,7 +31,7 @@ struct cgroup *scx_bpf_task_cgroup___new(struct task_struct *p) __ksym __weak; * * v7.1: scx_bpf_dsq_move_to_local___v2() to add @enq_flags. */ -bool scx_bpf_dsq_move_to_local___v2(u64 dsq_id, u64 enq_flags) __ksym __weak; +bool scx_bpf_dsq_move_to_local___v2___compat(u64 dsq_id, u64 enq_flags) __ksym __weak; bool scx_bpf_dsq_move_to_local___v1(u64 dsq_id) __ksym __weak; void scx_bpf_dsq_move_set_slice___new(struct bpf_iter_scx_dsq *it__iter, u64 slice) __ksym __weak; void scx_bpf_dsq_move_set_vtime___new(struct bpf_iter_scx_dsq *it__iter, u64 vtime) __ksym __weak; @@ -45,8 +45,8 @@ bool scx_bpf_dispatch_from_dsq___old(struct bpf_iter_scx_dsq *it__iter, struct t bool scx_bpf_dispatch_vtime_from_dsq___old(struct bpf_iter_scx_dsq *it__iter, struct task_struct *p, u64 dsq_id, u64 enq_flags) __ksym __weak; #define scx_bpf_dsq_move_to_local(dsq_id, enq_flags) \ - (bpf_ksym_exists(scx_bpf_dsq_move_to_local___v2) ? \ - scx_bpf_dsq_move_to_local___v2((dsq_id), (enq_flags)) : \ + (bpf_ksym_exists(scx_bpf_dsq_move_to_local___v2___compat) ? \ + scx_bpf_dsq_move_to_local___v2___compat((dsq_id), (enq_flags)) : \ (bpf_ksym_exists(scx_bpf_dsq_move_to_local___v1) ? \ scx_bpf_dsq_move_to_local___v1((dsq_id)) : \ scx_bpf_consume___old((dsq_id)))) @@ -414,10 +414,10 @@ static inline void scx_bpf_reenqueue_local(void) } /* - * v6.20: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This + * v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This * will eventually deprecate scx_bpf_reenqueue_local(). */ -void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags, const struct bpf_prog_aux *aux__prog) __ksym __weak; +void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak; static inline bool __COMPAT_has_generic_reenq(void) { @@ -427,7 +427,7 @@ static inline bool __COMPAT_has_generic_reenq(void) static inline void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags) { if (bpf_ksym_exists(scx_bpf_dsq_reenq___compat)) - scx_bpf_dsq_reenq___compat(dsq_id, reenq_flags, NULL); + scx_bpf_dsq_reenq___compat(dsq_id, reenq_flags); else if (dsq_id == SCX_DSQ_LOCAL && reenq_flags == 0) scx_bpf_reenqueue_local(); else diff --git a/tools/sched_ext/include/scx/enum_defs.autogen.h b/tools/sched_ext/include/scx/enum_defs.autogen.h index da4b459820fddd..8e287c88ad90f4 100644 --- a/tools/sched_ext/include/scx/enum_defs.autogen.h +++ b/tools/sched_ext/include/scx/enum_defs.autogen.h @@ -143,7 +143,6 @@ #define HAVE___SCX_REENQ_TSR_MASK #define HAVE_SCX_RQ_ONLINE #define HAVE_SCX_RQ_CAN_STOP_TICK -#define HAVE_SCX_RQ_BAL_KEEP #define HAVE_SCX_RQ_CLK_VALID #define HAVE_SCX_RQ_BAL_CB_PENDING #define HAVE_SCX_RQ_IN_WAKEUP diff --git a/tools/sched_ext/include/scx/enums.autogen.bpf.h b/tools/sched_ext/include/scx/enums.autogen.bpf.h index dafccbb6b69d2e..d02abed94e8eeb 100644 --- a/tools/sched_ext/include/scx/enums.autogen.bpf.h +++ b/tools/sched_ext/include/scx/enums.autogen.bpf.h @@ -22,9 +22,6 @@ const volatile u64 __SCX_RQ_CAN_STOP_TICK __weak; const volatile u64 __SCX_RQ_BAL_PENDING __weak; #define SCX_RQ_BAL_PENDING __SCX_RQ_BAL_PENDING -const volatile u64 __SCX_RQ_BAL_KEEP __weak; -#define SCX_RQ_BAL_KEEP __SCX_RQ_BAL_KEEP - const volatile u64 __SCX_RQ_BYPASSING __weak; #define SCX_RQ_BYPASSING __SCX_RQ_BYPASSING diff --git a/tools/sched_ext/include/scx/enums.autogen.h b/tools/sched_ext/include/scx/enums.autogen.h index bbd4901f4fce33..c26934a3d8e9c3 100644 --- a/tools/sched_ext/include/scx/enums.autogen.h +++ b/tools/sched_ext/include/scx/enums.autogen.h @@ -11,7 +11,6 @@ SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_ONLINE); \ SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_CAN_STOP_TICK); \ SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_BAL_PENDING); \ - SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_BAL_KEEP); \ SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_BYPASSING); \ SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_CLK_VALID); \ SCX_ENUM_SET(skel, scx_rq_flags, SCX_RQ_IN_WAKEUP); \ diff --git a/tools/sched_ext/scx_qmap.bpf.c b/tools/sched_ext/scx_qmap.bpf.c index fd9a82a676278b..0beac1abc58774 100644 --- a/tools/sched_ext/scx_qmap.bpf.c +++ b/tools/sched_ext/scx_qmap.bpf.c @@ -749,7 +749,7 @@ static s64 task_qdist(struct task_struct *p) bool BPF_STRUCT_OPS(qmap_core_sched_before, struct task_struct *a, struct task_struct *b) { - return task_qdist(a) > task_qdist(b); + return task_qdist(a) < task_qdist(b); } /* diff --git a/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c b/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c index 0660f29dca9555..3177bc5b733ac4 100644 --- a/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c +++ b/tools/testing/selftests/bpf/progs/sockmap_verdict_prog.c @@ -44,8 +44,18 @@ int bpf_prog2(struct __sk_buff *skb) __sink(lport); __sink(rport); - if (data + 8 > data_end) - return SK_DROP; + if (data + 8 > data_end) { + if (bpf_skb_pull_data(skb, 8)) + return SK_DROP; + + data = (void *)(long)skb->data; + data_end = (void *)(long)skb->data_end; + + if (data + 8 > data_end) + return SK_DROP; + + d = data; + } map = d[0]; sk = d[1]; diff --git a/tools/testing/selftests/mm/memfd_secret.c b/tools/testing/selftests/mm/memfd_secret.c index aac4f795c327bd..c55d84c5e613a4 100644 --- a/tools/testing/selftests/mm/memfd_secret.c +++ b/tools/testing/selftests/mm/memfd_secret.c @@ -57,33 +57,6 @@ static void test_file_apis(int fd) pass("file IO is blocked as expected\n"); } -static void test_mlock_limit(int fd) -{ - size_t len; - char *mem; - - len = mlock_limit_cur; - if (len % page_size != 0) - len = (len/page_size) * page_size; - - mem = mmap(NULL, len, prot, mode, fd, 0); - if (mem == MAP_FAILED) { - fail("unable to mmap secret memory\n"); - return; - } - munmap(mem, len); - - len = mlock_limit_max * 2; - mem = mmap(NULL, len, prot, mode, fd, 0); - if (mem != MAP_FAILED) { - fail("unexpected mlock limit violation\n"); - munmap(mem, len); - return; - } - - pass("mlock limit is respected\n"); -} - static void test_vmsplice(int fd, const char *desc) { ssize_t transferred; @@ -297,7 +270,7 @@ static void prepare(void) strerror(errno)); } -#define NUM_TESTS 6 +#define NUM_TESTS 5 int main(int argc, char *argv[]) { @@ -319,7 +292,6 @@ int main(int argc, char *argv[]) if (ftruncate(fd, page_size)) ksft_exit_fail_msg("ftruncate failed: %s\n", strerror(errno)); - test_mlock_limit(fd); test_file_apis(fd); /* * We have to run the first vmsplice test before any secretmem page was diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index 8c296dedf0474d..44cabdc54144e6 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -303,7 +303,7 @@ CATEGORY="compaction" run_test ./compaction_test if command -v sudo &> /dev/null && sudo -u nobody ls ./on-fault-limit >/dev/null; then - CATEGORY="mlock" run_test sudo -u nobody ./on-fault-limit + CATEGORY="mlock" run_test sudo -u nobody sh -c ./on-fault-limit else echo "# SKIP ./on-fault-limit" fi diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index db57c5766ab604..171a9da1b6855d 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -185,6 +185,10 @@ static void __kvm_gmem_invalidate_start(struct gmem_file *f, pgoff_t start, } flush |= kvm_mmu_unmap_gfn_range(kvm, &gfn_range); + +#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE + kvm_arch_gmem_invalidate_range(kvm, &gfn_range); +#endif } if (flush)