From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: linux-kernel@vger.kernel.org, akpm@linux-foundation.org,
torvalds@linux-foundation.org, stable@vger.kernel.org
Cc: lwn@lwn.net, jslaby@suse.cz,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Subject: Re: Linux 7.2.5
Date: Fri, 11 Sep 2026 13:28:22 +0200 [thread overview]
Message-ID: <2026091122-throng-stimulus-e9b6@gregkh> (raw)
In-Reply-To: <2026091122-audience-emotion-e092@gregkh>
diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-damon b/Documentation/ABI/testing/sysfs-kernel-mm-damon
index 4fdec63a47d4..59010885618c 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/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/access_bp
+What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intervals_goal/access_bp
Date: Feb 2025
Contact: SJ Park <sj@kernel.org>
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/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/aggrs
+What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intervals_goal/aggrs
Date: Feb 2025
Contact: SJ Park <sj@kernel.org>
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/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/min_sample_us
+What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intervals_goal/min_sample_us
Date: Feb 2025
Contact: SJ Park <sj@kernel.org>
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/<K>/contexts/<C>/monitoring_attrs/intervals/intrvals_goal/max_sample_us
+What: /sys/kernel/mm/damon/admin/kdamonds/<K>/contexts/<C>/monitoring_attrs/intervals/intervals_goal/max_sample_us
Date: Feb 2025
Contact: SJ Park <sj@kernel.org>
Description: Writing a value to this file sets the maximum value of
diff --git a/Documentation/admin-guide/cgroup-v1/memory.rst b/Documentation/admin-guide/cgroup-v1/memory.rst
index 7db63c002922..7d2a44af52c9 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/media/nxp,imx8-isi.yaml b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
index b59c4ce30b8b..279c9cd3034a 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/trace/hisi-ptt.rst b/Documentation/trace/hisi-ptt.rst
index 6eef28ebb0c7..f6a2655f99e5 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 38a4136d7220..0a7da2f16850 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 91d6e74db4dc..408e8512b713 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 7
PATCHLEVEL = 2
-SUBLEVEL = 4
+SUBLEVEL = 5
EXTRAVERSION =
NAME = Baby Opossum Posse
diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h
index 71493b760b83..3c008821219c 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 a57870fa96db..f6a7200700cc 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 bae2c4f92ef5..ac16f96c878d 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 012d711034d1..1ed708335809 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/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 1995e1198648..b0937e3f6fb4 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 9a22df0c5120..103e70d683ca 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 d50e2a9b066b..45c63f3d75c5 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/kvm/at.c b/arch/arm64/kvm/at.c
index 640f2dc00a8b..0926426b8798 100644
--- a/arch/arm64/kvm/at.c
+++ b/arch/arm64/kvm/at.c
@@ -11,8 +11,6 @@
#include <asm/kvm_mmu.h>
#include <asm/lsui.h>
-#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 bbe9cebd3d9d..c09b1d411c58 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 dfb96edbdc43..550c9bd3dbe7 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 5d5c579d4579..797e888bf939 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 907057881b26..fdac6e24762d 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 36ab3e492915..ed281fbf008b 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-v3-nested.c b/arch/arm64/kvm/vgic/vgic-v3-nested.c
index 5c69fa615823..e3e84d7f5ad5 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 7aa417440f6a..0e1c121766ae 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 0b52557652be..75c3e463df2e 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -16,6 +16,7 @@
#include <linux/mm.h>
#include <linux/hardirq.h>
#include <linux/init.h>
+#include <linux/irqflags.h>
#include <linux/kasan.h>
#include <linux/kprobes.h>
#include <linux/uaccess.h>
@@ -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 d8d252325017..14d31e9a3d70 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 5a71b9ccbe78..7c0158764d84 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 8b7a2fa3f7f8..9633fbfc066d 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 23cfbecebbd7..3538a199aac6 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 060163dfb4a3..d1d72d4bb8d1 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 e74b3b742634..887b0431fd20 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 223528c04d73..a2191044435d 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 873e90990771..cb454ee92b20 100644
--- a/arch/loongarch/kernel/acpi.c
+++ b/arch/loongarch/kernel/acpi.c
@@ -13,6 +13,7 @@
#include <linux/export.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
+#include <linux/kvm_host.h>
#include <linux/memblock.h>
#include <linux/of_fdt.h>
#include <linux/serial_core.h>
@@ -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 1985ed30dd16..ddfefea17472 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 2e009fbea53f..160189444684 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 8572b63478bb..482ba17bcd3d 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 de25735ce039..f5d3cdf291c3 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 2b14485d14a7..5fd9d4c9238e 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 4fa0897d7bdb..1e613741d7b8 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 175a630aceb4..40a4aa3000d6 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 3e1005526f4b..236523d2449d 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 e104897aa532..4ca7c6ea8f08 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 20c207d80e31..ae6a6e30ab82 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 1317c718f896..86ba7d54e81f 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 3407030f3e7a..4b46c5d30708 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 29c281bef28e..45eee761df9e 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/parisc/kernel/head.S b/arch/parisc/kernel/head.S
index 9188c8d87437..e84d7d6e1eb9 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 000000000000..bf876fc2caae
--- /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 3449dd2b577d..b9ab9df1e2bc 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/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c
index 8c72e12ea44e..6075b1c88511 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 867135560e5c..eb45e89502ca 100644
--- a/arch/powerpc/kexec/ranges.c
+++ b/arch/powerpc/kexec/ranges.c
@@ -21,6 +21,7 @@
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/memblock.h>
+#include <linux/minmax.h>
#include <linux/crash_core.h>
#include <asm/sections.h>
#include <asm/kexec_ranges.h>
@@ -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 61dbeea317f3..f9380ef65750 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 00302399fc37..b6b83fe3233f 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 10aced261cff..cf692b2b5f7b 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/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 1223dc961242..f29e7547c995 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -77,6 +77,7 @@
#include <asm/dtl.h>
#include <asm/hvconsole.h>
#include <asm/setup.h>
+#include <asm/papr-watchdog.h>
#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/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
index f9d5347ba966..0942116e59bc 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 6454c1531854..7919b2b9ac9c 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/kernel/nmi.c b/arch/s390/kernel/nmi.c
index e17a59d4d5a4..17297a8b63d9 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/kvm/guestdbg.c b/arch/s390/kvm/guestdbg.c
index 69835e1d4f20..9a6149e310bb 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 8f24bcd1a6d3..d33dd58f4a4f 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 518a69c55e85..17aa38710170 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/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 2b35483e2b70..465c414f145d 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 cae2e02fe6cc..52d14927a191 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 eae24bb35dc1..5902a297daa1 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/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index 83dc5086138b..ccf23b3f0e1c 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 6db5b5f79df9..2dfd6199a10c 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/hyperv.c b/arch/x86/kvm/hyperv.c
index d8736b5dfcd3..5f43ae345f87 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 a61750f8e1e3..51ba46aa6b7e 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 364c5da6c499..f898d8d0d93c 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 c1cbae65d239..d95af13d922a 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 8b9af7d4c7f6..1ed78f770340 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -4021,19 +4021,25 @@ static int snp_begin_psc(struct vcpu_svm *svm)
return snp_do_psc(svm);
}
-static void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
+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;
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;
- svm->sev_es.snp_guest_vmsa_gpa = INVALID_PAGE;
+ WRITE_ONCE(svm->sev_es.snp_guest_vmsa_gpa, INVALID_PAGE);
/*
* When replacing the VMSA during SEV-SNP AP creation,
@@ -4048,6 +4054,9 @@ static void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
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.
@@ -4066,15 +4075,20 @@ static void sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
*/
svm->sev_es.snp_has_guest_vmsa = true;
- /* Use the new VMSA */
- svm->sev_es.snp_guest_vmsa_gpa = gpa;
- svm->vmcb->control.vmsa_pa = pfn_to_hpa(pfn);
-
+ read_lock(&kvm->mmu_lock);
/*
- * 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.
+ * 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);
}
@@ -4100,7 +4114,7 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
gpa = svm->sev_es.snp_pending_vmsa_gpa;
svm->sev_es.snp_pending_vmsa_gpa = INVALID_PAGE;
- sev_snp_reload_vmsa(vcpu, gpa);
+ __sev_snp_reload_vmsa(vcpu, gpa);
/*
* Mark the vCPU as runnable for CREATE requests, indicated by a valid
@@ -4112,6 +4126,15 @@ static void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
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)
{
struct kvm_sev_info *sev = to_kvm_sev_info(svm->vcpu.kvm);
@@ -5241,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 1158f3e28200..03fb9d7231c8 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5463,12 +5463,14 @@ 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,
@@ -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 344312bd1fd4..cd30740e9984 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -997,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);
@@ -1011,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);
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index ddf6df7bee93..ed28cf6b6efc 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 47cb9eba113b..cb94b87df989 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 9de577ef9c97..ad9d2aacb87d 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/block/genhd.c b/block/genhd.c
index f1990c7cdfb9..10ca8b4d6eea 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
diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c
index 1c63eff0a4a8..2a16de96e6a4 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 ed9c748a54ad..b2901eb8a7a0 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 1e2465279aae..1e4b65f62933 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/acpi/battery.c b/drivers/acpi/battery.c
index f5e0eb299610..f8fa0d9a1f4c 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -15,6 +15,7 @@
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
@@ -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/ata/ahci.c b/drivers/ata/ahci.c
index 58f512f8952a..9b8c0935001c 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/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 2a22f9dc1f2f..037e08cb97ce 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/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index f173f4c54b59..6cb44e229f1e 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)
@@ -1989,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)));
@@ -2006,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;
}
diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h
index 08d1774c15db..996bb99ea8bb 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 <asm/byteorder.h>
#include <linux/rwsem.h>
#include <linux/zsmalloc.h>
@@ -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/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index 2c09fd729bab..fd8440122ec2 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 39af7b1868e3..7a7b104182ff 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 ee58304913ef..7f044775aea8 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 07f1b78d737a..4b749c99586b 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 9c7c6b23ce32..e91406e6f527 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 45193b3d714b..ffd7f14fcbaf 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 86953f9ffee3..86a6870cc2ee 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 4a0136349f71..dbef983eb425 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/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c
index dcf20ea5ef5e..b250d0dde760 100644
--- a/drivers/cpuidle/cpuidle-psci.c
+++ b/drivers/cpuidle/cpuidle-psci.c
@@ -16,7 +16,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/device/faux.h>
+#include <linux/platform_device.h>
#include <linux/psci.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
@@ -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 d292975cc468..ed41a90eeeb7 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/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index d504c636dc29..4c9add51f9ef 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 a76bf3f8b071..43c32fb28313 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 89a4c498a17b..18ec188c9911 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 632abb8b481c..2beec876b184 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 d3541301a247..45d964a3726d 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/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index f0f516a79424..194e53880edd 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 45dfe1c8a11b..a4d79484f6d9 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 e84b75c03c6f..ddfc51a92d79 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 1aed121f4ddb..e814701bc8fd 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 7139cb3b2db5..5c5c0fc76aeb 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 de3dbc95e376..5ca302e62af7 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 1baad7624f1f..816800f596cd 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 2a241a5b12c4..ac3f71d77140 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 e63d05c477a0..adbfdee01e80 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 3b12eb27a253..047f5f891b8a 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 9d7a75670bee..e418390353b1 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 70ba81e6b4d4..180ed00bb959 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 81bba3ec2a93..00d8f35846f2 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 894780669f9c..a12652dc3a38 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 e1689b3d2add..6738cd1d4f15 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 67137679a901..8b522fede60a 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 7cd236c1ff75..f0dc0c00de82 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 503176f00204..236089aef75a 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 9320e49e4428..f059bed728f0 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;
}
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 86086d10c543..46a74f895430 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/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 11e295253f72..51725ca16a28 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 166f10b8862f..c82886323a51 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 b6262e43ca02..4c27052b62f7 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 dd854d992692..f43fc4b78a8d 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 17d5caedb32e..9eb6cf8a321f 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 e997917819e8..7b951af5ab32 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 675675480da4..e890052061f3 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 892b325fa99b..05eb7254028f 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 ea0cca58b7c8..8141c3a1e30a 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 3066dc9ebc64..2e6e189bec1a 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 52347668f27d..18ef7e64821b 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 164b7d61c9a3..4369c8eeaac2 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 82415af47d54..2f03f95945f1 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 982abaaac00d..5e1b313f04c0 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -670,12 +670,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 7903d7470d19..01145db32c53 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 598513f60449..f142f6310596 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 9442ec6e1f6c..ad4570c50be7 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_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
index fa3b4ebf38a8..2bd0376193ae 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 f5e4756b4de4..fc125fd44a9b 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 ea62dc97f118..96c8a5b29999 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 e1aecd3fe96c..98d6ca5ac311 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 ab0a85c92430..820834b5ee9b 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 000000000000..d0360610f9fa
--- /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 <subdev/timer.h>
+
+/* 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 dbd984da7501..0608266188d3 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 856252bf559a..5976498da909 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 3ba04bead2f9..5d682a774f2d 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 a3fd7cb7c488..fde321dbd7c8 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 <engine/disp.h>
#include <core/enum.h>
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 dcb9f8ba374c..f6c163072ff6 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 <nvif/class.h>
+/* 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 1155f079b0c3..f5f22173fc2c 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 a96e31c2d80b..8a23837f356e 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 <rm/rm.h>
#include <engine/disp.h>
+#include <engine/disp/ior.h>
#include <engine/disp/outp.h>
#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 996941c668ba..1488771c63fc 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 06e972835d77..742b25a2a12d 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 a9af94adf9ef..fcd0221dcea1 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 aa27d6cd932e..ac815729aa83 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 fa88b2f9da76..10b7e12c1f1e 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 cae92a3ae8a4..784f7000fad2 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 03e46a2af646..adf5b6e09ca6 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 819aed466727..9d60db45139c 100644
--- a/drivers/gpu/drm/sysfb/ofdrm.c
+++ b/drivers/gpu/drm/sysfb/ofdrm.c
@@ -2,6 +2,7 @@
#include <linux/aperture.h>
#include <linux/of_address.h>
+#include <linux/overflow.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
@@ -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 fc168920f2c6..ca8a8d86094e 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 e7648ad3e615..6142f90d43e2 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 23eb7edbdd57..56cff1e44530 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 702c22fae136..049df0647761 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-rmi.c b/drivers/hid/hid-rmi.c
index d4af17fdba46..d55a0388895f 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-sony.c b/drivers/hid/hid-sony.c
index 77ab30303080..af1ad2c23370 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 <linux/cleanup.h>
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
@@ -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;
}
@@ -2431,13 +2428,7 @@ static void sony_remove(struct hid_device *hdev)
}
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/wacom_wac.c b/drivers/hid/wacom_wac.c
index da1f0ea85625..af76e49fd0f2 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/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 6d6e81242f74..447af5523c2e 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-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
index bdeda3979c48..25b6e4e9e3fa 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 d2f5055b0b10..9a02e57c5a4b 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 3ec04787a737..17cf3d225268 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 681a201c239b..68a4c34b5987 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 f2a1f4744978..2403c0bf7c43 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/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 3755a81c1efd..c6d3cf68b1f7 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 0797d64aeaec..967144eee3eb 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 26b9c75bd4d8..589f03618ecf 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 68bdd2f30bad..f0bab94462f4 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 af87f5f19a0f..da82a6947b2b 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 1e8adbe1790d..1d4a9919df1a 100644
--- a/drivers/iio/chemical/atlas-sensor.c
+++ b/drivers/iio/chemical/atlas-sensor.c
@@ -13,7 +13,6 @@
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/irq.h>
-#include <linux/irq_work.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
@@ -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 f10bbebc29e4..379c1c4af8d8 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 657c68e75542..14a246729d2b 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 02a124ac4855..f865843aa439 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 9797fc3e57a9..286695e240c3 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 7e80c0eb5cc1..60bb672b70e2 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 d84e04e4b431..07c0f9011c63 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 2e72c7ba94dd..5321033daebc 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 531fc4ccc15d..2c9ec93dff47 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 5ca4c87524fe..d582bda4d847 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 2590fc8fd154..b32a94028f09 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 a8db514cca5e..05773e24931b 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 3f34ddc911b4..0a8851342039 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 dd152d921b48..aa3d87995b7c 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 c391ad3271c6..d248db1c42e8 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 f45af72a0554..45bdb8c7670f 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 16e112b796ba..2929ea8e4e01 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 7be50bdebfcb..e03f3f0dee04 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 54a00253de11..58d4c0273932 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/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c
index 6f69f4e5dbac..12919836dadb 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 bf3a2def69ca..a19e91fbd010 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/md/dm.c b/drivers/md/dm.c
index d413bfaf3527..eb9e32995b79 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/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index a90cb84a4b4d..de0c4fcd8dfe 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 0fcd3b5e60c8..90a98f322f5e 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 41f5b8669cb0..2c914f0008b5 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/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 3381d86096a1..3c6ce6f3d93e 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 c564485e3bbb..0330e7f0881a 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 dd991c2ee700..f51f9b987759 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 27a5c212a527..ac59908f57aa 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 0b424c17e880..c3b22b238ea0 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 143dcfe10445..0150e4d296af 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 c2e02f191816..c09ad663f9de 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 5b6f897a74fc..5abe51d21f89 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 78d18c028154..6c0c821babc7 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 6b50fb422a61..4811a0489524 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 7bb7f13c70c0..9ed547cd40af 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 154918984534..723509fa1e06 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 88581a4c081d..ad3ddfb9ccb1 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -298,9 +298,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 c9cdeb7054d7..24db2763de54 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 6bcde506adf5..db65506ea885 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 f707bdc1fb0f..b8285df64473 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 0b3d58b9f2f7..2ba4ae20f829 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 94a389b3f833..f28e9f4354ac 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 de0e9d898db7..33e2232ec8f5 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 f098effde7b4..4cd13b702d9f 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 c4f46651dcee..6706939b4a90 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 8d28ba0b59a3..cc7627e9a51a 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 bb2ba9204a83..6564cf3ec739 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 <linux/delay.h>
#include <linux/pm_runtime.h>
#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 e6c94b6f2671..f9fcdf4c224b 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 4ebd48d5550e..e04f2dbf3b65 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 8f4da4cf55d2..f412aadb5996 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 c580c831972e..01374b7af843 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 fe4adfa3a1f0..056603055c81 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/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c
index 10e33b8a73f6..391e1fc5f6e2 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 ca1545d28b53..eff9216bb501 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 c9256f2323dc..ff48333ad089 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 d14472414750..e991f34916ec 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 14d63dc76c9b..1c052ca235be 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 ab41da1f47c8..e4847c107709 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 b1657443f23f..d07845f37c55 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 5185a547461d..91eda5034248 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 ca1c268303dd..c4a71306278b 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 f89602075121..9c4a6093af32 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 1d1e9bfef8e9..061281f903f3 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 e56a95f53ea9..ccb688f4d7af 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 cb4c56bf0752..28e840a7e5b8 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/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c
index e0c6bc9f8f20..ac60c9b5418e 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 7bb945ba0989..bd7ed50fb6e8 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 57edb42463e8..358a66ab8e48 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 2cd4e333bc4b..70aa99fead27 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 fbfb74eab475..a6881f7f8e1b 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 5bbb082dbed9..3d6b196b8483 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 e4b9f37be77b..01d547ad678d 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 0b8182edf8e4..58658dd3e457 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 333bd305a4f9..ae0a14e5ed2a 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 888a2e213b08..0aa4265a6782 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 ba047d7d8601..5b8a594fb9e2 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-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 62a3a452f788..570df3c415b0 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/misc/fastrpc.c b/drivers/misc/fastrpc.c
index eb6c2a78d3c7..d7a9e12f5575 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/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
index 39df7ce8f55f..1da7ef06a7a9 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 7d003fd72027..76d75e2107df 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 cd3ad373883e..b5e304c35738 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 06f8f1e14b9c..7d43506b1654 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 02647565c8ba..ca74c9c333e7 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 26116694c821..7ab3d50f565e 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/nvdimm/btt.c b/drivers/nvdimm/btt.c
index 7e1112960d7f..380e352dc3cc 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/nvme/host/core.c b/drivers/nvme/host/core.c
index ee993dfa1e41..618620e7080e 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -2447,9 +2447,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;
@@ -4295,6 +4312,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);
diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index ac3d4f400601..736570b5fb34 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -1028,6 +1028,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;
}
@@ -1042,6 +1043,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 cd76a1be2038..3082a17320b8 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -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/tcp.c b/drivers/nvme/host/tcp.c
index 06d46f72df5c..ddbb34a22f68 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -682,6 +682,13 @@ static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue,
return -ENOENT;
}
+ 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,
diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
index edb9627d97b0..a55319bcdbd1 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 4477c4d6b1ee..7e565a84d341 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 aaba745e3c21..1645f4c818af 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 e4f603b2ace7..e59810175262 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/parisc/eisa.c b/drivers/parisc/eisa.c
index 9eab974e6baf..b5b5413bfd1e 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/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c
index ff92a5c301b8..37299d59f906 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 67362e5b9971..92eabf5f61b9 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/pci.c b/drivers/pci/pci.c
index 77b17b13ee61..350bae907ebf 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/slot.c b/drivers/pci/slot.c
index 6d5cd37bfb1e..897223f01f6a 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/<name>
* @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/pmdomain/mediatek/Kconfig b/drivers/pmdomain/mediatek/Kconfig
index 8923e6516441..a2eb704a773c 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 ffc46de8d07a..63120e703923 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 eb5c1ae68e44..cfcc05dfe28d 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/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
index 808e9609988d..7ab896952822 100644
--- a/drivers/remoteproc/qcom_q6v5_pas.c
+++ b/drivers/remoteproc/qcom_q6v5_pas.c
@@ -240,17 +240,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/rtc/rtc-rzn1.c b/drivers/rtc/rtc-rzn1.c
index 305f10a8a85b..919ed0fb1e23 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/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 44b3a1dcc1b3..940c0ff668be 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/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index ecd365d78ae3..d83abded2039 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 e93ea76b565e..54b35893261a 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 6a05ce195aa0..14c3efd0e210 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 5e910b5ca670..5b15fc28ddd7 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 a7e3ec9bba47..c6b8e6d798c6 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 43970caca7b3..cb4278b88e1d 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 eccedb38a515..6caae48510b3 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 880cd73feaca..ed46217ce26e 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 e746c9274cde..d38bf74ea7a5 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 47fbd830ff16..abf0db7f72d5 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 33776330956c..612e00ca1ada 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 44e310f1a370..f604aab8b161 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 2531e71c39dc..b84fe71ea340 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 72b1c28e4dae..d853b4006f4e 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 e80dec53174e..5eec248a77a6 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/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c
index 9b0bba64e91e..73a77763210d 100644
--- a/drivers/soc/fsl/qe/qe_ports_ic.c
+++ b/drivers/soc/fsl/qe/qe_ports_ic.c
@@ -6,6 +6,7 @@
*/
#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/platform_device.h>
@@ -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/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
index 15636a8dc907..107fa2057654 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/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
index 58012e1b5ae7..fd7b9d5d9686 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 43d7b54e3ae8..74c06934cd2d 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 09c1472ae4fa..d4491058b456 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 d9e6b4b87c89..12b3a3b29a89 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/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index a039d925c0fe..c18bf352b199 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 bbd186b8035b..96acd52e380c 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/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
index e60ed3359866..b6e7de235204 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 1196ec011455..7bdc5fe6dc8a 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 aafc94bcb635..c282b6a70296 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/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
index 5aaacbc53478..b0e88b6a9352 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 35439ec5f8bc..297724e81593 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/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c
index 7a516e509198..e84405352caa 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 c44bca348a41..c3ae5040f9cc 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 83f3384b735d..419309c8439e 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/dwc3/dwc3-google.c b/drivers/usb/dwc3/dwc3-google.c
index 60ee4cc99b28..a01ca23cb6a8 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 bfe616194dfa..310b5ffb236a 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 1afe533b3ebd..e46db7032089 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;
}
@@ -3715,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 826bf41260c2..4ec37c1fdd22 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -873,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);
@@ -1270,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;
}
@@ -1314,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 b7b06cb79ff5..8a6ae944d436 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 fba8cf787d6c..63fb6ee70a3d 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 a4b72a6fad8a..5b8b18281989 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, \
@@ -2590,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,
diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c
index 203361a64212..70e31c40e267 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 d87a8ab51510..12819e8c265d 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/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 567d7c37a7da..8908f5c27e52 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)
{
@@ -3688,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/image/mdc800.c b/drivers/usb/image/mdc800.c
index ca287b770e8c..f7caa1c5cbb7 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/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index ed49a3bc859c..895f90c7a3fa 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 af038b897c6b..c4b28744693b 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/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c
index d0de5a2488f9..4eec90c82bae 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 ? "enable" : "disable", ret);
+ return;
+ }
+
+ hd3ss3220->vbus_enabled = on;
}
static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
index 9b908c46bd7d..afa6fc181397 100644
--- a/drivers/usb/typec/mux.c
+++ b/drivers/usb/typec/mux.c
@@ -57,6 +57,8 @@ static void *typec_switch_match(const struct fwnode_handle *fwnode,
*/
dev = class_find_device(&typec_mux_class, NULL, fwnode,
switch_fwnode_match);
+ if (!dev)
+ return ERR_PTR(-EPROBE_DEFER);
/* Skip duplicates */
for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++)
@@ -65,7 +67,7 @@ static void *typec_switch_match(const struct fwnode_handle *fwnode,
return NULL;
}
- return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER);
+ return to_typec_switch_dev(dev);
}
/**
@@ -275,7 +277,9 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode)
static void *typec_mux_match(const struct fwnode_handle *fwnode,
const char *id, void *data)
{
+ struct typec_mux_dev **mux_devs = data;
struct device *dev;
+ int i;
/*
* Device graph (OF graph) does not give any means to identify the
@@ -290,8 +294,18 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode,
dev = class_find_device(&typec_mux_class, NULL, fwnode,
mux_fwnode_match);
+ if (!dev)
+ return ERR_PTR(-EPROBE_DEFER);
- return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER);
+ /* Skip duplicates */
+ for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++)
+ if (to_typec_mux_dev(dev) == mux_devs[i]) {
+ put_device(dev);
+ return NULL;
+ }
+
+
+ return to_typec_mux_dev(dev);
}
/**
@@ -316,7 +330,8 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode)
return ERR_PTR(-ENOMEM);
count = fwnode_connection_find_matches(fwnode, "mode-switch",
- NULL, typec_mux_match,
+ (void **)mux_devs,
+ typec_mux_match,
(void **)mux_devs,
ARRAY_SIZE(mux_devs));
if (count <= 0) {
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 e6b28648f440..926fa017ebaf 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 bf985efe1cd6..d3523435f3e0 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/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 89eec20a2064..e49ab6d54a5d 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 d5ee0af9058b..159d1a2f9a1f 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 03edbb77bbd6..d172c84ada74 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 7067f2561b84..8d2032d0762c 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/watchdog/pseries-wdt.c b/drivers/watchdog/pseries-wdt.c
index 48d67f7c972a..e97b943e1d3c 100644
--- a/drivers/watchdog/pseries-wdt.c
+++ b/drivers/watchdog/pseries-wdt.c
@@ -12,61 +12,10 @@
#include <linux/platform_device.h>
#include <linux/time64.h>
#include <linux/watchdog.h>
+#include <asm/papr-watchdog.h>
#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/fs/ceph/crypto.c b/fs/ceph/crypto.c
index 64d240759277..1cc11f77413c 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/mds_client.c b/fs/ceph/mds_client.c
index ec886ca9b526..0b6ff5b704f6 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
@@ -6589,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) {
@@ -6615,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;
}
@@ -6629,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 3c62e3c3530b..e7a262c9c2ab 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -604,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/f2fs/data.c b/fs/f2fs/data.c
index a765fda71536..631269c6f94d 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 af88db8fdb71..ff379aff4472 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 a9563f7fcd88..fd0e2cd31a81 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 8f3e632f315c..8011bbdf2c68 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 4b52c56d71f0..67fcea7408b1 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -36,23 +36,69 @@
#include <trace/events/f2fs.h>
#include <uapi/linux/f2fs.h>
-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 ffaa7ba76a1b..1eda81861974 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 6c4d4567571e..b015742fb455 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 e2f7bedf1552..aec06fb4fd76 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 ae265e3e9b2c..12d4e18a6a50 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 cac03b8e91a1..a8db3928c8b5 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 38917e4a7319..1671535fbb10 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 89af8407b667..3fecfdbd5958 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[] = "<encrypted>";
+
+ 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 = "<encrypted>";
- 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 = "<encrypted>";
- 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 d71ddb3ee918..091bfb170d61 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 9760e4efeffe..f7e2dcb58b4d 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 665687244c93..aa1621419932 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 ed33e5110f2a..6728d1488cad 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/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index e3c3249e63be..939749709e41 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1597,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)
{
@@ -1606,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];
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 386ca748522b..6180f6dc8995 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)
{
@@ -1944,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);
@@ -1985,9 +2028,7 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
*/
nn->nfs40_last_revoke =
ktime_get_boottime_seconds();
- if (atomic_dec_and_test(&clp->cl_rpc_users) &&
- is_client_expired(clp))
- wake_up_all(&expiry_wq);
+ put_client_no_renew_locked(clp);
goto retry;
}
}
@@ -2030,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);
@@ -2044,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);
@@ -2055,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;
}
}
@@ -7186,9 +7238,7 @@ static void nfs40_clean_admin_revoked(struct nfsd_net *nn,
nfsd4_drop_revoked_stid(stid);
nfs4_put_stid(stid);
spin_lock(&nn->client_lock);
- if (atomic_dec_and_test(&clp->cl_rpc_users) &&
- is_client_expired(clp))
- wake_up_all(&expiry_wq);
+ put_client_no_renew_locked(clp);
goto retry;
}
spin_unlock(&clp->cl_lock);
@@ -7261,15 +7311,7 @@ nfs4_laundromat(struct nfsd_net *nn)
clp = dp->dl_stid.sc_client;
list_del_init(&dp->dl_recall_lru);
revoke_delegation(dp);
- /*
- * Unpin without renewing: put_client_renew() would
- * renew the reaped client's lease.
- */
- if (atomic_dec_and_lock(&clp->cl_rpc_users, &nn->client_lock)) {
- if (is_client_expired(clp))
- wake_up_all(&expiry_wq);
- spin_unlock(&nn->client_lock);
- }
+ put_client_no_renew(clp);
}
spin_lock(&nn->client_lock);
@@ -7279,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);
@@ -7299,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 */
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index caf59421f8f4..bc16fc7ca24f 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -299,7 +299,7 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
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 {
@@ -2424,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 {
@@ -2471,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
diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
index a751661cdca9..2c18cc1b4fb7 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);
diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
index 79787099afdc..32f2f8b219ba 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 76f63f9adc72..e02e254645e2 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/include/linux/dma-buf.h b/include/linux/dma-buf.h
index d1203da56fc5..d15b2b31d3c9 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/ftrace.h b/include/linux/ftrace.h
index 02bc5027523a..bd76a16a63af 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/kprobes.h b/include/linux/kprobes.h
index 8c4f3bb24429..e6de7ae55bda 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 ab8cfaec82d3..c00fc1740ce5 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/pci.h b/include/linux/pci.h
index 64b308b6e61c..43f80d6189a7 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 4e3338103654..922935cc3383 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 4cc52698e214..8d7e5521f7cd 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/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h
index c5e6ab85df09..0991ef7ec092 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/trace/events/f2fs.h b/include/trace/events/f2fs.h
index 270c1a2c24c4..1dd9fc5afc46 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/kernel/bpf/percpu_freelist.c b/kernel/bpf/percpu_freelist.c
index 632762b57299..06ce588d13a3 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 914798b74967..980cf2884fd2 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 443d2c3bd45d..9b086ecbf159 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;
}
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 4391b797d4db..b4cb2c03e5d7 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 1a73ba0747df..ed3b62b0f948 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7029,7 +7029,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)
@@ -7060,17 +7059,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 88788e584ec8..98f1b962e59a 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 79823ad13683..b3f4a4bccb12 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 bfc89083daa9..6337da5cab9e 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/locking/rtmutex_api.c b/kernel/locking/rtmutex_api.c
index 5d48d64725b1..eb18b094473c 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/sched/core.c b/kernel/sched/core.c
index 145eea2d99a1..87caa1290fad 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7633,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);
@@ -7645,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
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 7c50f8ae5a0c..a4ecba1518de 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 e3136a5ed4ff..eb01f1d65066 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -7182,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))
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 525062941ee3..a6d5d1786469 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,
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index bf77331f56a4..191a64c885da 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 00172f301f25..d3ba356d5503 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 28167b229419..640529409021 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
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
index cd37f2013758..c879d43a5fbb 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 0aa2514a6593..e7f4e523587d 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/once.c b/lib/once.c
index d801bfa945e6..0a0a919156e0 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/mm/damon/core.c b/mm/damon/core.c
index cff932b3317d..a86812d457c1 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 0bcad6b1e5b9..6a969b1d2987 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 f7613ce279a2..b3507b1f6249 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 3cbeccd436e4..e2c8716be6c9 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 a9e187158067..26cdad603ad0 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 fcf7c7fadb5f..fbcc882dccc9 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 61f844336ffb..6a95441d193a 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 2c1c1952c008..404785128ffd 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/huge_memory.c b/mm/huge_memory.c
index 2860fb43ff57..0dbed3ba5c8a 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -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);
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 5c9c6a842c41..ca080806de17 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 {
@@ -7243,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 e0083de1ca82..ecb6e0b7819a 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 39344d6c78d8..3e22161686d2 100644
--- a/mm/hugetlb_cma.c
+++ b/mm/hugetlb_cma.c
@@ -3,6 +3,7 @@
#include <linux/mm.h>
#include <linux/cma.h>
#include <linux/compiler.h>
+#include <linux/cpuset.h>
#include <linux/mm_inline.h>
#include <asm/page.h>
@@ -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/khugepaged.c b/mm/khugepaged.c
index 617bca76db49..2d0aee0312f2 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/madvise.c b/mm/madvise.c
index 31ee9ca2b5c1..65e40fc8699d 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);
diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c
index a1776cfacd68..f1a64d0604a5 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
@@ -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;
}
@@ -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 02f5b414f531..405faa4b8472 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();
/*
@@ -5133,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));
@@ -5332,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.
@@ -5360,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();
@@ -5386,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);
@@ -5414,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 11cf9705975a..9d7cb63d3273 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2676,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_device.c b/mm/migrate_device.c
index 162d29b2807a..52ba68f2c915 100644
--- a/mm/migrate_device.c
+++ b/mm/migrate_device.c
@@ -1413,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++)
@@ -1447,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/page_alloc.c b/mm/page_alloc.c
index f92055827ae9..73645d2eda6f 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -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;
}
diff --git a/mm/secretmem.c b/mm/secretmem.c
index 4877c262cb1f..5411324fec1e 100644
--- a/mm/secretmem.c
+++ b/mm/secretmem.c
@@ -18,6 +18,8 @@
#include <linux/secretmem.h>
#include <linux/set_memory.h>
#include <linux/sched/signal.h>
+#include <linux/sched/user.h>
+#include <linux/cred.h>
#include <uapi/linux/magic.h>
@@ -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/userfaultfd.c b/mm/userfaultfd.c
index c3adedaaf7d5..ec5d511bd179 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/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index 5c73f6ba16cf..7a31bc21bf87 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 a6fe4820f65b..6ca946da9275 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 67bed3ee77e7..7918886e6f94 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 fbfd99268de4..91913271be3c 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 1404a3b7adfb..c99579dc1ca9 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/core/skbuff.c b/net/core/skbuff.c
index 71bb4a3b5776..04776a112334 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3910,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/sunrpc/xprtrdma/ib_client.c b/net/sunrpc/xprtrdma/ib_client.c
index 69166d5d9987..188f7a13397f 100644
--- a/net/sunrpc/xprtrdma/ib_client.c
+++ b/net/sunrpc/xprtrdma/ib_client.c
@@ -52,8 +52,8 @@ static struct rpcrdma_device *rpcrdma_get_client_data(struct ib_device *device)
* is unregistered first.
*
* On failure, a negative errno is returned. rn->rn_done is left
- * NULL on every failure path (it is assigned only after xa_alloc
- * and kref_get have both succeeded), so the @rn may safely be
+ * 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.
*/
@@ -66,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;
}
@@ -102,8 +113,9 @@ void rpcrdma_rn_unregister(struct ib_device *device,
/*
* rn_done is the registration sentinel: rpcrdma_rn_register
- * assigns it last, after xa_alloc and kref_get have both
- * succeeded. A NULL rn_done means this notification was
+ * 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
diff --git a/rust/kernel/drm/ioctl.rs b/rust/kernel/drm/ioctl.rs
index cf328101dde4..ccf4150d83b6 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/samples/damon/mtier.c b/samples/damon/mtier.c
index dae6929f827c..1c7c9c2b47a0 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 b7c50f2656ce..edeae145c4a8 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 799ad4443943..ff5e8a890f44 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/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index ced2e131b061..b280488e15fc 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/sound/core/pcm_native.c b/sound/core/pcm_native.c
index c9d0c2bb55b2..fa6723a6e827 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 4dfd9d53e6d3..62addab22efe 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/parisc/harmony.c b/sound/parisc/harmony.c
index fb40476c6c91..a9625aedf2a4 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/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index a70c46dd5d76..27ce03b0fb91 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/cs35l33.c b/sound/soc/codecs/cs35l33.c
index f49edb9ea1f2..e217da124549 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 e80984b22159..3f265bd760f7 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 83c6cbd40804..ffd6a5119f5e 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 680e341aa7f1..1ab5f8a26e03 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/max9860.c b/sound/soc/codecs/max9860.c
index 0d7ac37850bb..20e288e38669 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 8a14626d43ff..7209b47c2029 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 fe3b5011fa16..03b1fcb1bf96 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 1c612ae4a4b2..ee4cde4d8686 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 4932b5dbe3c0..1604e1cfd85e 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 ca318dbd946e..0f6fc36e50f0 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 c038ac0e3b76..2f1a776585fc 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 d13fd0e14125..55607f1b9a1e 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 63b5fc439773..f8c370106504 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 ded0ea332480..4217467904d6 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 5414a1712b57..09aed08b5b79 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 4ad0dcfd16fd..002f94493b01 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 0d774e473ab9..9aa22b52d556 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 6acf9858330d..82da0e62d9d7 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 110154e74efe..2f08e58ed519 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 99fd2d67f04d..7ad25cc27f62 100644
--- a/sound/soc/codecs/rt712-sdca-sdw.h
+++ b/sound/soc/codecs/rt712-sdca-sdw.h
@@ -11,21 +11,21 @@
#include <linux/regmap.h>
#include <linux/soundwire/sdw_registers.h>
+/* 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 0cbc14844f8c..d408f2d7d0f2 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 5d7661e335ae..f19fafb913f0 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 214b31b82583..13d6227f3392 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 59642673b4cb..35df1a8c4c8c 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 4ab15be69f3a..b7eaf4795d8b 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 1a7650b9b2a7..ea083d1cec2b 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 b11b998aa32a..f9c65c9f6d0a 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 1ec1c076204f..f1fce4305fa1 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 3d0b116544cc..e5d27266370e 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/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c
index 8535ef844ce0..be59c138e3ea 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 9ad44eeed6ad..7831136f4f12 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 22ae2d22f121..44bb11c69490 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 7910d5d9ac4f..ea895fe6b5e9 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/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c
index 48ccc1d1854b..6db91b73f25c 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/usb/midi.c b/sound/usb/midi.c
index 8a9bc37f0b6e..7e5b1b13360f 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -971,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/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 0ba307e78fe1..4d5993da9f94 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
@@ -1142,6 +1143,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 2a6e61864ee0..3bc9e28a9b8a 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 a17c423a526d..5db44556fc4f 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 <endian.h>
#include <byteswap.h>
#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/unaligned.h>
#include <stdarg.h>
#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 e78f1b5bc836..333eee02b19d 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 <stddef.h>
#include <stdint.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
#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/testing/selftests/mm/memfd_secret.c b/tools/testing/selftests/mm/memfd_secret.c
index aac4f795c327..c55d84c5e613 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/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index db57c5766ab6..171a9da1b685 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)
prev parent reply other threads:[~2026-09-11 11:28 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-11 11:28 Linux 7.2.5 Greg Kroah-Hartman
2026-09-11 11:28 ` Greg Kroah-Hartman [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2026091122-throng-stimulus-e9b6@gregkh \
--to=gregkh@linuxfoundation.org \
--cc=akpm@linux-foundation.org \
--cc=jslaby@suse.cz \
--cc=linux-kernel@vger.kernel.org \
--cc=lwn@lwn.net \
--cc=stable@vger.kernel.org \
--cc=torvalds@linux-foundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.