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 6.18.63
Date: Tue, 10 Nov 2026 13:28:36 -0500 [thread overview]
Message-ID: <2026111036-cringe-stylishly-9aca@gregkh> (raw)
In-Reply-To: <2026111036-racoon-landing-be00@gregkh>
diff --git a/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml b/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
index 8f0c43683729..9649feda8d7a 100644
--- a/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
+++ b/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
@@ -29,7 +29,18 @@ properties:
const: 1
clocks:
- maxItems: 1
+ minItems: 1
+ maxItems: 2
+ description:
+ The controller takes 2 crystal oscillator inputs from the board.
+ HXT is a 24 MHz crystal feeding the internal PLLs. LXT is a 32.768
+ kHz crystal, for RTC and low power operations.
+
+ clock-names:
+ minItems: 1
+ items:
+ - const: hxt
+ - const: lxt
nuvoton,pll-mode:
description:
@@ -58,6 +69,7 @@ examples:
compatible = "nuvoton,ma35d1-clk";
reg = <0x40460200 0x100>;
#clock-cells = <1>;
- clocks = <&clk_hxt>;
+ clocks = <&clk_hxt>, <&clk_lxt>;
+ clock-names = "hxt", "lxt";
};
...
diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst
index 27a861053b2f..9b8a1980bb54 100644
--- a/Documentation/networking/ip-sysctl.rst
+++ b/Documentation/networking/ip-sysctl.rst
@@ -1088,6 +1088,8 @@ tcp_tso_rtt_log - INTEGER
If you want to use the old autosizing, set this sysctl to 0.
+ Possible values are [0, 31], inclusive.
+
Default: 9 (2^9 = 512 usec)
tcp_pacing_ss_ratio - INTEGER
@@ -3729,5 +3731,6 @@ l3mdev_accept - BOOLEAN
max_dgram_qlen - INTEGER
The maximum length of dgram socket receive queue
- Default: 10
+ Possible values: 0 - INT_MAX
+ Default: 10
diff --git a/Documentation/userspace-api/check_exec.rst b/Documentation/userspace-api/check_exec.rst
index 05dfe3b56f71..0904810e1d8d 100644
--- a/Documentation/userspace-api/check_exec.rst
+++ b/Documentation/userspace-api/check_exec.rst
@@ -99,6 +99,10 @@ This secure bit may be set by user session managers, service managers,
container runtimes, sandboxer tools... Except for test environments, the
related ``SECBIT_EXEC_DENY_INTERACTIVE_LOCKED`` bit should also be set.
+Both secure bits and their locks are inherited across :manpage:`fork(2)` and
+:manpage:`execve(2)`, and they are kept when a task creates or joins a user
+namespace.
+
Here is the expected behavior for a script interpreter according to combination
of any exec securebits:
diff --git a/Documentation/virt/hyperv/vmbus.rst b/Documentation/virt/hyperv/vmbus.rst
index 654bb4849972..c546e821ce6b 100644
--- a/Documentation/virt/hyperv/vmbus.rst
+++ b/Documentation/virt/hyperv/vmbus.rst
@@ -141,14 +141,15 @@ Three functions exist to send VMBus channel messages:
1. vmbus_sendpacket(): Control-only messages and messages with
embedded data -- no GPAs
-2. vmbus_sendpacket_pagebuffer(): Message with list of GPAs
- identifying data to transfer. An offset and length is
- associated with each GPA so that multiple discontinuous areas
- of guest memory can be targeted.
-3. vmbus_sendpacket_mpb_desc(): Message with list of GPAs
- identifying data to transfer. A single offset and length is
- associated with a list of GPAs. The GPAs must describe a
- single logical area of guest memory to be targeted.
+2. vmbus_sendpacket_getid(): Similar to vmbus_sendpacket() but returns
+ a transaction ID that is matched against a reply from the Hyper-V
+ host. Useful when the struct completion (or other rendezvous data)
+ must reside on the caller's stack. By invalidating the transaction
+ ID before exiting the function due to a timeout or other error, a
+ late arriving interrupt is assured to not trash the stack.
+3. vmbus_sendpacket_mpb_desc(): Message with one or more data ranges
+ to target. Each range consists of a single offset and length plus
+ a list of GPAs that describe a logical area of guest memory.
Historically, Linux guests have trusted Hyper-V to send well-formed
and valid messages, and Linux drivers for synthetic devices did not
diff --git a/Makefile b/Makefile
index ae234882e7e5..124ecd981272 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 6
PATCHLEVEL = 18
-SUBLEVEL = 62
+SUBLEVEL = 63
EXTRAVERSION =
NAME = Baby Opossum Posse
diff --git a/arch/alpha/include/asm/mmu_context.h b/arch/alpha/include/asm/mmu_context.h
index eee8fe836a59..09f356934b90 100644
--- a/arch/alpha/include/asm/mmu_context.h
+++ b/arch/alpha/include/asm/mmu_context.h
@@ -130,6 +130,8 @@ __get_new_mm_context(struct mm_struct *mm, long cpu)
return next;
}
+extern void __load_new_mm_context(struct mm_struct *);
+
__EXTERN_INLINE void
ev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm,
struct task_struct *next)
@@ -139,6 +141,16 @@ ev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm,
unsigned long mmc;
long cpu = smp_processor_id();
+ /*
+ * kthread_use_mm(), sched_force_init_mm() and do_shoot_lazy_tlb()
+ * switch current's mm without alpha_switch_to(), which loads the
+ * context. The latter two require options Alpha does not select.
+ */
+ if (next == current) {
+ __load_new_mm_context(next_mm);
+ return;
+ }
+
#ifdef CONFIG_SMP
cpu_data[cpu].asn_lock = 1;
barrier();
@@ -147,11 +159,16 @@ ev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm,
mmc = next_mm->context[cpu];
if ((mmc ^ asn) & ~HARDWARE_ASN_MASK) {
mmc = __get_new_mm_context(next_mm, cpu);
- next_mm->context[cpu] = mmc;
+ /*
+ * Ordered before any user access through the mm by the
+ * mb() in arch_spin_unlock(), when finish_lock_switch()
+ * drops the rq lock.
+ */
+ WRITE_ONCE(next_mm->context[cpu], mmc);
}
#ifdef CONFIG_SMP
- else
- cpu_data[cpu].need_new_asn = 1;
+ /* A deferred shootdown can also invalidate a newly allocated ASN. */
+ cpu_data[cpu].need_new_asn = 1;
#endif
/* Always update the PCB ASN. Another thread may have allocated
@@ -160,7 +177,6 @@ ev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm,
task_thread_info(next)->pcb.asn = mmc & HARDWARE_ASN_MASK;
}
-extern void __load_new_mm_context(struct mm_struct *);
asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr,
long cause, struct pt_regs *regs);
@@ -181,6 +197,17 @@ do { \
#define check_mmu_context() do { } while(0)
#endif
+/*
+ * Also called from kthread_use_mm() with preemption enabled. Alpha has
+ * no kernel preemption, so the task is still on the CPU that ran
+ * switch_mm().
+ */
+#define finish_arch_post_lock_switch finish_arch_post_lock_switch
+static inline void finish_arch_post_lock_switch(void)
+{
+ check_mmu_context();
+}
+
__EXTERN_INLINE void
ev5_activate_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm)
{
diff --git a/arch/alpha/include/asm/sfp-machine.h b/arch/alpha/include/asm/sfp-machine.h
index 5fe63afbd474..bff1ad963c68 100644
--- a/arch/alpha/include/asm/sfp-machine.h
+++ b/arch/alpha/include/asm/sfp-machine.h
@@ -59,6 +59,10 @@
R##_c = FP_CLS_NAN; \
} while (0)
+/* Alpha determines tininess after rounding, so the emulation must do the
+ same as the hardware does for the results it produces itself. */
+#define _FP_TININESS_AFTER_ROUNDING 1
+
/* Obtain the current rounding mode. */
#define FP_ROUNDMODE mode
#define FP_RND_NEAREST (FPCR_DYN_NORMAL >> FPCR_DYN_SHIFT)
diff --git a/arch/alpha/include/asm/switch_to.h b/arch/alpha/include/asm/switch_to.h
index 762b7f975310..35c4b2c9d992 100644
--- a/arch/alpha/include/asm/switch_to.h
+++ b/arch/alpha/include/asm/switch_to.h
@@ -9,7 +9,6 @@ extern struct task_struct *alpha_switch_to(unsigned long, struct task_struct *);
#define switch_to(P,N,L) \
do { \
(L) = alpha_switch_to(virt_to_phys(&task_thread_info(N)->pcb), (P)); \
- check_mmu_context(); \
} while (0)
#endif /* __ALPHA_SWITCH_TO_H */
diff --git a/arch/alpha/include/asm/tlbflush.h b/arch/alpha/include/asm/tlbflush.h
index 0c8529997f54..9ae1902faf8e 100644
--- a/arch/alpha/include/asm/tlbflush.h
+++ b/arch/alpha/include/asm/tlbflush.h
@@ -87,7 +87,8 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
{
struct mm_struct *mm = vma->vm_mm;
- if (mm == current->active_mm)
+ /* A targeted tbi() needs a thread of MM to be current. */
+ if (mm == current->mm)
flush_tlb_current_page(mm, vma, addr);
else
flush_tlb_other(mm);
diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c
index ed06367ece57..11768aa292a4 100644
--- a/arch/alpha/kernel/smp.c
+++ b/arch/alpha/kernel/smp.c
@@ -625,12 +625,30 @@ static void
ipi_flush_tlb_mm(void *x)
{
struct mm_struct *mm = x;
- if (mm == current->active_mm && !asn_locked())
+ if (mm == current->mm && !asn_locked())
flush_tlb_current(mm);
else
flush_tlb_other(mm);
}
+/* True if a CPU other than this one holds an ASN for MM. */
+static bool
+mm_context_elsewhere(struct mm_struct *mm)
+{
+ int cpu, this_cpu = smp_processor_id();
+
+ /* Pairs with the barrier the publishing CPU issues before using MM. */
+ smp_mb();
+
+ for_each_online_cpu(cpu) {
+ if (cpu == this_cpu)
+ continue;
+ if (READ_ONCE(mm->context[cpu]))
+ return true;
+ }
+ return false;
+}
+
void
flush_tlb_mm(struct mm_struct *mm)
{
@@ -638,17 +656,14 @@ flush_tlb_mm(struct mm_struct *mm)
if (mm == current->active_mm) {
flush_tlb_current(mm);
- if (atomic_read(&mm->mm_users) <= 1) {
- int cpu, this_cpu = smp_processor_id();
- for (cpu = 0; cpu < NR_CPUS; cpu++) {
- if (!cpu_online(cpu) || cpu == this_cpu)
- continue;
- if (mm->context[cpu])
- mm->context[cpu] = 0;
- }
+ if (atomic_read(&mm->mm_users) <= 1 &&
+ !mm_context_elsewhere(mm)) {
preempt_enable();
return;
}
+ } else {
+ /* smp_call_function() does not call back into this CPU. */
+ flush_tlb_other(mm);
}
smp_call_function(ipi_flush_tlb_mm, mm, 1);
@@ -669,7 +684,8 @@ ipi_flush_tlb_page(void *x)
struct flush_tlb_page_struct *data = x;
struct mm_struct * mm = data->mm;
- if (mm == current->active_mm && !asn_locked())
+ /* A targeted tbi() needs a thread of MM to be current. */
+ if (mm == current->mm && !asn_locked())
flush_tlb_current_page(mm, data->vma, data->addr);
else
flush_tlb_other(mm);
@@ -683,19 +699,17 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
preempt_disable();
- if (mm == current->active_mm) {
+ /* As in ipi_flush_tlb_page(): a targeted tbi() needs MM current. */
+ if (mm == current->mm) {
flush_tlb_current_page(mm, vma, addr);
- if (atomic_read(&mm->mm_users) <= 1) {
- int cpu, this_cpu = smp_processor_id();
- for (cpu = 0; cpu < NR_CPUS; cpu++) {
- if (!cpu_online(cpu) || cpu == this_cpu)
- continue;
- if (mm->context[cpu])
- mm->context[cpu] = 0;
- }
+ if (atomic_read(&mm->mm_users) <= 1 &&
+ !mm_context_elsewhere(mm)) {
preempt_enable();
return;
}
+ } else {
+ /* smp_call_function() does not call back into this CPU. */
+ flush_tlb_other(mm);
}
data.vma = vma;
@@ -720,7 +734,7 @@ static void
ipi_flush_icache_page(void *x)
{
struct mm_struct *mm = (struct mm_struct *) x;
- if (mm == current->active_mm && !asn_locked())
+ if (mm == current->mm && !asn_locked())
__load_new_mm_context(mm);
else
flush_tlb_other(mm);
@@ -739,17 +753,14 @@ flush_icache_user_page(struct vm_area_struct *vma, struct page *page,
if (mm == current->active_mm) {
__load_new_mm_context(mm);
- if (atomic_read(&mm->mm_users) <= 1) {
- int cpu, this_cpu = smp_processor_id();
- for (cpu = 0; cpu < NR_CPUS; cpu++) {
- if (!cpu_online(cpu) || cpu == this_cpu)
- continue;
- if (mm->context[cpu])
- mm->context[cpu] = 0;
- }
+ if (atomic_read(&mm->mm_users) <= 1 &&
+ !mm_context_elsewhere(mm)) {
preempt_enable();
return;
}
+ } else {
+ /* smp_call_function() does not call back into this CPU. */
+ flush_tlb_other(mm);
}
smp_call_function(ipi_flush_icache_page, mm, 1);
diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c
index a9816bbc9f34..53e170e96591 100644
--- a/arch/alpha/mm/fault.c
+++ b/arch/alpha/mm/fault.c
@@ -8,6 +8,7 @@
#include <linux/sched/signal.h>
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <linux/hugetlb.h>
#include <asm/io.h>
#define __EXTERN_INLINE inline
@@ -45,7 +46,9 @@ __load_new_mm_context(struct mm_struct *next_mm)
struct pcb_struct *pcb;
mmc = __get_new_mm_context(next_mm, smp_processor_id());
- next_mm->context[smp_processor_id()] = mmc;
+ WRITE_ONCE(next_mm->context[smp_processor_id()], mmc);
+ /* Publish the context before this CPU can access the mm. */
+ smp_mb();
pcb = ¤t_thread_info()->pcb;
pcb->asn = mmc & HARDWARE_ASN_MASK;
@@ -89,6 +92,7 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
struct mm_struct *mm = current->mm;
const struct exception_table_entry *fixup;
int si_code = SEGV_MAPERR;
+ unsigned int lsb;
vm_fault_t fault;
unsigned int flags = FAULT_FLAG_DEFAULT;
@@ -161,6 +165,8 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
goto bad_area;
else if (fault & VM_FAULT_SIGBUS)
goto do_sigbus;
+ else if (fault & (VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE))
+ goto do_sigbus_mceerr;
BUG();
}
@@ -222,6 +228,21 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
goto no_context;
return;
+ do_sigbus_mceerr:
+ mmap_read_unlock(mm);
+ if (!user_mode(regs))
+ goto no_context;
+ /*
+ * Report the size of the poisoned area, which for a hugetlb fault
+ * is the size of the huge page that could not be mapped.
+ */
+ lsb = PAGE_SHIFT;
+ if (fault & VM_FAULT_HWPOISON_LARGE)
+ lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault));
+ show_signal_msg(regs, address, SIGBUS, "hardware memory error");
+ force_sig_mceerr(BUS_MCEERR_AR, (void __user *) address, lsb);
+ return;
+
do_sigsegv:
force_sig_fault(SIGSEGV, si_code, (void __user *) address);
return;
diff --git a/arch/alpha/mm/tlbflush.c b/arch/alpha/mm/tlbflush.c
index ccbc317b9a34..a2aef90e6234 100644
--- a/arch/alpha/mm/tlbflush.c
+++ b/arch/alpha/mm/tlbflush.c
@@ -66,7 +66,7 @@ static void ipi_flush_mm_and_page(void *x)
struct tlb_mm_and_addr *d = x;
/* Part 1: mm context side (Alpha uses ASN/context as a key mechanism). */
- if (d->mm == current->active_mm && !asn_locked())
+ if (d->mm == current->mm && !asn_locked())
__load_new_mm_context(d->mm);
else
flush_tlb_other(d->mm);
@@ -90,22 +90,6 @@ void migrate_flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
*/
preempt_disable();
on_each_cpu(ipi_flush_mm_and_page, &d, 1);
-
- /*
- * mimic flush_tlb_mm()'s mm_users<=1 optimization.
- */
- if (atomic_read(&mm->mm_users) <= 1) {
-
- int cpu, this_cpu;
- this_cpu = smp_processor_id();
-
- for (cpu = 0; cpu < NR_CPUS; cpu++) {
- if (!cpu_online(cpu) || cpu == this_cpu)
- continue;
- if (READ_ONCE(mm->context[cpu]))
- WRITE_ONCE(mm->context[cpu], 0);
- }
- }
preempt_enable();
}
diff --git a/arch/arm/include/asm/linkage.h b/arch/arm/include/asm/linkage.h
index c4670694ada7..bba992f5d7a8 100644
--- a/arch/arm/include/asm/linkage.h
+++ b/arch/arm/include/asm/linkage.h
@@ -9,4 +9,33 @@
.type name, %function; \
END(name)
+#ifdef __ASSEMBLY__
+
+/*
+ * Variants of SYM_TYPED_START/SYM_TYPED_FUNC_START that align the
+ * function entry itself instead of the kCFI type hash preceding it,
+ * for functions whose entry point must meet an alignment requirement,
+ * such as the 8-byte alignment fncpy() demands of its source.
+ */
+#ifdef CONFIG_CFI
+
+#define SYM_TYPED_START_ALIGNED(name, linkage, align) \
+ linkage(name) ASM_NL \
+ .balign align ASM_NL \
+ .fill (align) - 4, 1, 0 ASM_NL \
+ __CFI_TYPE(name) ASM_NL \
+ name:
+
+#else /* CONFIG_CFI */
+
+#define SYM_TYPED_START_ALIGNED(name, linkage, align) \
+ SYM_START(name, linkage, .balign align)
+
+#endif /* CONFIG_CFI */
+
+#define SYM_TYPED_FUNC_START_ALIGNED(name, align) \
+ SYM_TYPED_START_ALIGNED(name, SYM_L_GLOBAL, align)
+
+#endif /* __ASSEMBLY__ */
+
#endif
diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h
index 8c05a7f374d8..84aa6a663e81 100644
--- a/arch/arm/include/asm/smp.h
+++ b/arch/arm/include/asm/smp.h
@@ -43,7 +43,7 @@ extern void set_smp_ipi_range(int ipi_base, int nr_ipi);
* Called from platform specific assembly code, this is the
* secondary CPU entry point.
*/
-asmlinkage void secondary_start_kernel(struct task_struct *task);
+asmlinkage void secondary_start_kernel(void);
/*
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
index e1469b641780..993c8a134786 100644
--- a/arch/arm/kernel/cacheinfo.c
+++ b/arch/arm/kernel/cacheinfo.c
@@ -80,19 +80,21 @@ static void ci_leaf_init(struct cacheinfo *this_leaf,
this_leaf->type = type;
}
-static int detect_cache_level(unsigned int *level_p, unsigned int *leaves_p)
+static bool clidr_present(void)
{
- unsigned int ctype, level, leaves;
- u32 ctr, format;
-
/* CLIDR is not present before ARMv7/v7m */
if (cpu_architecture() < CPU_ARCH_ARMv7)
- return -EOPNOTSUPP;
+ return false;
/* Don't try reading CLIDR if CTR declares old format */
- ctr = read_cpuid_cachetype();
- format = FIELD_GET(CTR_FORMAT_MASK, ctr);
- if (format != CTR_FORMAT_ARMV7)
+ return FIELD_GET(CTR_FORMAT_MASK, read_cpuid_cachetype()) == CTR_FORMAT_ARMV7;
+}
+
+static int detect_cache_level(unsigned int *level_p, unsigned int *leaves_p)
+{
+ unsigned int ctype, level, leaves;
+
+ if (!clidr_present())
return -EOPNOTSUPP;
for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) {
@@ -111,13 +113,6 @@ static int detect_cache_level(unsigned int *level_p, unsigned int *leaves_p)
return 0;
}
-int early_cache_level(unsigned int cpu)
-{
- struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
-
- return detect_cache_level(&this_cpu_ci->num_levels, &this_cpu_ci->num_leaves);
-}
-
int init_cache_level(unsigned int cpu)
{
unsigned int level, leaves;
@@ -146,26 +141,32 @@ int init_cache_level(unsigned int cpu)
return 0;
}
+int early_cache_level(unsigned int cpu)
+{
+ return init_cache_level(cpu);
+}
+
int populate_cache_leaves(unsigned int cpu)
{
unsigned int level, idx;
enum cache_type type;
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
- struct cacheinfo *this_leaf = this_cpu_ci->info_list;
- unsigned int arch = cpu_architecture();
+ struct cacheinfo *infos = this_cpu_ci->info_list;
- /* CLIDR is not present before ARMv7/v7m */
- if (arch < CPU_ARCH_ARMv7)
- return -EOPNOTSUPP;
+ /* The device tree can describe caches CLIDR cannot fill in. */
+ if (!clidr_present())
+ return -ENOENT;
for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
- idx < this_cpu_ci->num_leaves; idx++, level++) {
+ idx < this_cpu_ci->num_leaves; level++) {
type = get_cache_type(level);
if (type == CACHE_TYPE_SEPARATE) {
- ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
- ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level);
+ if (idx + 1 >= this_cpu_ci->num_leaves)
+ break;
+ ci_leaf_init(&infos[idx++], CACHE_TYPE_DATA, level);
+ ci_leaf_init(&infos[idx++], CACHE_TYPE_INST, level);
} else {
- ci_leaf_init(this_leaf++, type, level);
+ ci_leaf_init(&infos[idx++], type, level);
}
}
diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S
index b9d6818f1ee1..f80bbfbd3a6f 100644
--- a/arch/arm/kernel/head-nommu.S
+++ b/arch/arm/kernel/head-nommu.S
@@ -116,6 +116,7 @@ ENTRY(secondary_startup)
ldr r7, __secondary_data @ reload r7
ldr sp, [r7, #12] @ set up the stack pointer
ldr r0, [r7, #16] @ set up task pointer
+ set_current r0, r1
mov fp, #0
b secondary_start_kernel
ENDPROC(secondary_startup)
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index f22c50d4bd41..a22403a629ef 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -442,6 +442,7 @@ ENTRY(__secondary_switched)
adr_l r7, secondary_data + 12 @ get secondary_data.stack
ldr sp, [r7]
ldr r0, [r7, #4] @ get secondary_data.task
+ set_current r0, r1
mov fp, #0
b secondary_start_kernel
ENDPROC(__secondary_switched)
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 50999886a8b5..e30206365877 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -371,12 +371,9 @@ void __noreturn arch_cpu_idle_dead(void)
*/
__asm__("mov sp, %0\n"
" mov fp, #0\n"
- " mov r0, %1\n"
" b secondary_start_kernel"
:
- : "r" (task_stack_page(current) + THREAD_SIZE - 8),
- "r" (current)
- : "r0");
+ : "r" (task_stack_page(current) + THREAD_SIZE - 8));
unreachable();
}
@@ -397,23 +394,15 @@ static void smp_store_cpu_info(unsigned int cpuid)
check_cpu_icache_size(cpuid);
}
-static void set_current(struct task_struct *cur)
-{
- /* Set TPIDRURO */
- asm("mcr p15, 0, %0, c13, c0, 3" :: "r"(cur) : "memory");
-}
-
/*
* This is the secondary CPU boot entry. We're using this CPUs
* idle thread stack, but a set of temporary page tables.
*/
-asmlinkage void secondary_start_kernel(struct task_struct *task)
+asmlinkage void secondary_start_kernel(void)
{
struct mm_struct *mm = &init_mm;
unsigned int cpu;
- set_current(task);
-
secondary_biglittle_init();
/*
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 2336ee2aa44a..e6b50cd43ce5 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -12,6 +12,7 @@
*/
#include <linux/arch_topology.h>
+#include <linux/cacheinfo.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
@@ -238,8 +239,20 @@ void store_cpu_topology(unsigned int cpuid)
*/
void __init init_cpu_topology(void)
{
+ int cpu, ret;
+
reset_cpu_topology();
smp_wmb();
parse_dt_topology();
+
+ for_each_possible_cpu(cpu) {
+ ret = fetch_cache_info(cpu);
+ if (!ret)
+ continue;
+ /* CPUs without a usable CLIDR return -EOPNOTSUPP. */
+ if (ret != -ENOENT && ret != -EOPNOTSUPP)
+ pr_err("Early cacheinfo failed, ret = %d\n", ret);
+ return;
+ }
}
diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c
index a671ca498f88..3c73e2cdb8ce 100644
--- a/arch/arm/mach-imx/pm-imx6.c
+++ b/arch/arm/mach-imx/pm-imx6.c
@@ -4,6 +4,8 @@
* Copyright 2011 Linaro Ltd.
*/
+#include <linux/build_bug.h>
+#include <linux/cfi.h>
#include <linux/clk/imx.h>
#include <linux/delay.h>
#include <linux/init.h>
@@ -18,6 +20,7 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/stddef.h>
#include <linux/suspend.h>
#include <asm/cacheflush.h>
#include <asm/fncpy.h>
@@ -61,9 +64,9 @@
#define MX6Q_SUSPEND_OCRAM_SIZE 0x1000
#define MX6_MAX_MMDC_IO_NUM 33
-static void __iomem *ccm_base;
-static void __iomem *suspend_ocram_base;
-static void (*imx6_suspend_in_ocram_fn)(void __iomem *ocram_vbase);
+static void __iomem *ccm_base __ro_after_init;
+static void __iomem *suspend_ocram_base __ro_after_init;
+static void (*imx6_suspend_in_ocram_fn)(void __iomem *ocram_vbase) __ro_after_init;
/*
* suspend ocram space layout:
@@ -229,8 +232,18 @@ struct imx6_cpu_pm_info {
struct imx6_pm_base l2_base;
u32 mmdc_io_num; /* Number of MMDC IOs which need saved/restored. */
u32 mmdc_io_val[MX6_MAX_MMDC_IO_NUM][2]; /* To save offset and value */
+ u32 cfi_type; /* kCFI type hash of imx6_suspend() */
} __aligned(8);
+/*
+ * The ocram copy of imx6_suspend() starts right after struct imx6_cpu_pm_info,
+ * and the CFI check on the indirect call reads the kCFI type hash from the
+ * four bytes preceding the function entry, so cfi_type must occupy the last
+ * four bytes of the struct, i.e. fit into its tail padding.
+ */
+static_assert(offsetofend(struct imx6_cpu_pm_info, cfi_type) ==
+ sizeof(struct imx6_cpu_pm_info));
+
void imx6_set_int_mem_clk_lpm(bool enable)
{
u32 val = readl_relaxed(ccm_base + CGPR);
@@ -568,6 +581,13 @@ static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
mmdc_offset_array[i]);
}
+ /*
+ * Mask out the Thumb bit, as cfi_get_func_hash() expects the
+ * function's actual start address. Returns 0 if CONFIG_CFI=n.
+ */
+ pm_info->cfi_type =
+ cfi_get_func_hash((void *)((uintptr_t)&imx6_suspend & ~1UL));
+
imx6_suspend_in_ocram_fn = fncpy(
suspend_ocram_base + sizeof(*pm_info),
&imx6_suspend,
diff --git a/arch/arm/mach-imx/suspend-imx6.S b/arch/arm/mach-imx/suspend-imx6.S
index 63ccc2d0e920..95d5f723ea0e 100644
--- a/arch/arm/mach-imx/suspend-imx6.S
+++ b/arch/arm/mach-imx/suspend-imx6.S
@@ -3,6 +3,7 @@
* Copyright 2014 Freescale Semiconductor, Inc.
*/
+#include <linux/cfi_types.h>
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/asm-offsets.h>
@@ -148,7 +149,8 @@
.endm
-ENTRY(imx6_suspend)
+/* fncpy() requires an 8-byte-aligned entry; see arch/arm/include/asm/fncpy.h */
+SYM_TYPED_FUNC_START_ALIGNED(imx6_suspend, 8)
ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
@@ -329,4 +331,4 @@ resume:
resume_mmdc
ret lr
-ENDPROC(imx6_suspend)
+SYM_FUNC_END(imx6_suspend)
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index 812380f30ae3..2b470a419dda 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -341,6 +341,8 @@ static int
do_alignment_ldrhstrh(unsigned long addr, u32 instr, struct pt_regs *regs)
{
unsigned int rd = RD_BITS(instr);
+ unsigned int ua_flags;
+ bool ua_saved = false;
ai_half += 1;
@@ -364,10 +366,11 @@ do_alignment_ldrhstrh(unsigned long addr, u32 instr, struct pt_regs *regs)
user:
if (LDST_L_BIT(instr)) {
unsigned long val;
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
get16t_unaligned_check(val, addr);
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
/* signed half-word? */
if (instr & 0x40)
@@ -375,14 +378,17 @@ do_alignment_ldrhstrh(unsigned long addr, u32 instr, struct pt_regs *regs)
regs->uregs[rd] = val;
} else {
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
put16t_unaligned_check(regs->uregs[rd], addr);
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
}
return TYPE_LDST;
fault:
+ if (ua_saved)
+ uaccess_restore(ua_flags);
return TYPE_FAULT;
}
@@ -391,6 +397,8 @@ do_alignment_ldrdstrd(unsigned long addr, u32 instr, struct pt_regs *regs)
{
unsigned int rd = RD_BITS(instr);
unsigned int rd2;
+ unsigned int ua_flags;
+ bool ua_saved = false;
int load;
if ((instr & 0xfe000000) == 0xe8000000) {
@@ -425,26 +433,30 @@ do_alignment_ldrdstrd(unsigned long addr, u32 instr, struct pt_regs *regs)
user:
if (load) {
unsigned long val, val2;
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
get32t_unaligned_check(val, addr);
get32t_unaligned_check(val2, addr + 4);
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
regs->uregs[rd] = val;
regs->uregs[rd2] = val2;
} else {
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
put32t_unaligned_check(regs->uregs[rd], addr);
put32t_unaligned_check(regs->uregs[rd2], addr + 4);
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
}
return TYPE_LDST;
bad:
return TYPE_ERROR;
fault:
+ if (ua_saved)
+ uaccess_restore(ua_flags);
return TYPE_FAULT;
}
@@ -452,6 +464,8 @@ static int
do_alignment_ldrstr(unsigned long addr, u32 instr, struct pt_regs *regs)
{
unsigned int rd = RD_BITS(instr);
+ unsigned int ua_flags;
+ bool ua_saved = false;
ai_word += 1;
@@ -469,18 +483,22 @@ do_alignment_ldrstr(unsigned long addr, u32 instr, struct pt_regs *regs)
trans:
if (LDST_L_BIT(instr)) {
unsigned int val;
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
get32t_unaligned_check(val, addr);
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
regs->uregs[rd] = val;
} else {
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
put32t_unaligned_check(regs->uregs[rd], addr);
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
}
return TYPE_LDST;
fault:
+ if (ua_saved)
+ uaccess_restore(ua_flags);
return TYPE_FAULT;
}
@@ -502,6 +520,8 @@ do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
{
unsigned int rd, rn, correction, nr_regs, regbits;
unsigned long eaddr, newaddr;
+ unsigned int ua_flags;
+ bool ua_saved = false;
if (LDM_S_BIT(instr))
goto bad;
@@ -547,7 +567,8 @@ do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
#endif
if (user_mode(regs)) {
- unsigned int __ua_flags = uaccess_save_and_enable();
+ ua_flags = uaccess_save_and_enable();
+ ua_saved = true;
for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
regbits >>= 1, rd += 1)
if (regbits & 1) {
@@ -559,7 +580,7 @@ do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
put32t_unaligned_check(regs->uregs[rd], eaddr);
eaddr += 4;
}
- uaccess_restore(__ua_flags);
+ uaccess_restore(ua_flags);
} else {
for (regbits = REGMASK_BITS(instr), rd = 0; regbits;
regbits >>= 1, rd += 1)
@@ -581,6 +602,8 @@ do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
return TYPE_DONE;
fault:
+ if (ua_saved)
+ uaccess_restore(ua_flags);
regs->ARM_pc -= correction;
return TYPE_FAULT;
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-verdin-ivy.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-verdin-ivy.dtsi
index 29075ff5eda6..b64751aa31d5 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-verdin-ivy.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mm-verdin-ivy.dtsi
@@ -270,8 +270,8 @@ &gpio3 {
"",
"",
"",
- "DIG_1", /* SODIMM 56 */
- "DIG_2", /* SODIMM 58 */
+ "DIGI_1", /* SODIMM 56 */
+ "DIGI_2", /* SODIMM 58 */
"REL1", /* SODIMM 60 */
"REL2", /* SODIMM 62 */
"", /* 10 */
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-toradex-smarc.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-toradex-smarc.dtsi
index bebe19eb360f..576da7875601 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-toradex-smarc.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mp-toradex-smarc.dtsi
@@ -323,7 +323,7 @@ &gpio3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lvds_dsi_sel>;
gpio-line-names = "ETH_0_INT#", /* 0 */
- "SLEEP#",
+ "SMARC_SLEEP#",
"",
"",
"",
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-verdin-ivy.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-verdin-ivy.dtsi
index db1b4ee7728c..bf021e85cfc9 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-verdin-ivy.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mp-verdin-ivy.dtsi
@@ -333,8 +333,8 @@ &gpio3 {
"",
"",
"",
- "DIG_1", /* SODIMM 56 */
- "DIG_2", /* SODIMM 58 */
+ "DIGI_1", /* SODIMM 56 */
+ "DIGI_2", /* SODIMM 58 */
"REL1", /* SODIMM 60 */
"REL2", /* SODIMM 62 */
"", /* 10 */
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-verdin-yavia.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-verdin-yavia.dtsi
index cc389cda2af2..20d99012d4f3 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-verdin-yavia.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mp-verdin-yavia.dtsi
@@ -117,6 +117,11 @@ &flexcan1 {
status = "okay";
};
+&gpio3 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_gpios_ext_yavia>;
+};
+
&gpio4 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ctrl_sleep_moci>;
@@ -262,7 +267,7 @@ MX8MP_IOMUXC_NAND_DATA03__GPIO3_IO09 0x106 /* SODIMM 62 */
pinctrl_gpios_ext_yavia: gpiosextyaviagrp {
fsl,pins = <
- MX8MP_IOMUXC_NAND_CE1_B__GPIO3_IO02 0x106 /* SODIMM 64 */
+ MX8MP_IOMUXC_NAND_READY_B__GPIO3_IO16 0x106 /* SODIMM 64 */
MX8MP_IOMUXC_NAND_DQS__GPIO3_IO14 0x106 /* SODIMM 66 */
>;
};
diff --git a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi
index 6786e5b590e8..df5f7d831d81 100644
--- a/arch/arm64/boot/dts/mediatek/mt7986a.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt7986a.dtsi
@@ -653,6 +653,7 @@ cpu_trip_active_low: active-low {
timer {
compatible = "arm,armv8-timer";
interrupt-parent = <&gic>;
+ clock-frequency = <12986200>;
interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
diff --git a/arch/arm64/boot/dts/qcom/ipq9574.dtsi b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
index 86c9cb9fffc9..d3895259f25c 100644
--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
@@ -432,7 +432,7 @@ gcc: clock-controller@1800000 {
<&pcie1_phy>,
<&pcie2_phy>,
<&pcie3_phy>,
- <0>;
+ <&usb_0_qmpphy>;
#clock-cells = <1>;
#reset-cells = <1>;
#interconnect-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi
index dbd5f08b5cdd..3db935a7442e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium-common.dtsi
@@ -27,6 +27,7 @@
/delete-node/ &slpi_mem;
/delete-node/ &spss_mem;
/delete-node/ &rmtfs_mem;
+/delete-node/ &xbl_mem;
/ {
chassis-type = "handset";
@@ -55,6 +56,11 @@ key-vol-up {
/* Reserved memory changes from downstream */
reserved-memory {
+ xbl-mem@85d00000 {
+ reg = <0 0x85d00000 0 0x200000>;
+ no-map;
+ };
+
tz_mem: memory@86200000 {
reg = <0 0x86200000 0 0x4900000>;
no-map;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts
index 1c50a0563bc4..8fc7cbc087ed 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts
@@ -32,6 +32,7 @@
/delete-node/ &mba_region;
/delete-node/ &slpi_mem;
/delete-node/ &spss_mem;
+/delete-node/ &xbl_mem;
/ {
model = "Xiaomi Mi MIX 2S";
@@ -63,6 +64,11 @@ key-vol-up {
};
reserved-memory {
+ xbl-mem@85d00000 {
+ reg = <0 0x85d00000 0 0x200000>;
+ no-map;
+ };
+
adsp_mem: memory@8c500000 {
reg = <0 0x8c500000 0 0x1e00000>;
no-map;
diff --git a/arch/arm64/boot/dts/ti/k3-am62-lp-sk.dts b/arch/arm64/boot/dts/ti/k3-am62-lp-sk.dts
index 5ed757c19db6..fc79641d7bb5 100644
--- a/arch/arm64/boot/dts/ti/k3-am62-lp-sk.dts
+++ b/arch/arm64/boot/dts/ti/k3-am62-lp-sk.dts
@@ -86,7 +86,7 @@ main_mmc0_pins_default: main-mmc0-default-pins {
bootph-all;
pinctrl-single,pins = <
AM62X_IOPAD(0x220, PIN_INPUT, 0) /* (V3) MMC0_CMD */
- AM62X_IOPAD(0x218, PIN_INPUT, 0) /* (Y1) MMC0_CLK */
+ AM62X_IOPAD(0x218, PIN_OUTPUT, 0) /* (Y1) MMC0_CLK */
AM62X_IOPAD(0x214, PIN_INPUT, 0) /* (V2) MMC0_DAT0 */
AM62X_IOPAD(0x210, PIN_INPUT_PULLUP, 0) /* (V1) MMC0_DAT1 */
AM62X_IOPAD(0x20c, PIN_INPUT_PULLUP, 0) /* (W2) MMC0_DAT2 */
diff --git a/arch/arm64/boot/dts/ti/k3-am62-phycore-som.dtsi b/arch/arm64/boot/dts/ti/k3-am62-phycore-som.dtsi
index eeca643fedbe..19ac2dbe66cb 100644
--- a/arch/arm64/boot/dts/ti/k3-am62-phycore-som.dtsi
+++ b/arch/arm64/boot/dts/ti/k3-am62-phycore-som.dtsi
@@ -146,7 +146,7 @@ AM62X_IOPAD(0x15c, PIN_INPUT, 0) /* (AB22) MDIO0_MDIO */
main_mmc0_pins_default: main-mmc0-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x220, PIN_INPUT_PULLUP, 0) /* (Y3) MMC0_CMD */
- AM62X_IOPAD(0x218, PIN_INPUT_PULLDOWN, 0) /* (AB1) MMC0_CLK */
+ AM62X_IOPAD(0x218, PIN_OUTPUT_PULLDOWN, 0) /* (AB1) MMC0_CLK */
AM62X_IOPAD(0x214, PIN_INPUT_PULLUP, 0) /* (AA2) MMC0_DAT0 */
AM62X_IOPAD(0x210, PIN_INPUT_PULLUP, 0) /* (AA1) MMC0_DAT1 */
AM62X_IOPAD(0x20c, PIN_INPUT_PULLUP, 0) /* (AA3) MMC0_DAT2 */
diff --git a/arch/arm64/boot/dts/ti/k3-am62-pocketbeagle2.dts b/arch/arm64/boot/dts/ti/k3-am62-pocketbeagle2.dts
index 7a4cffc27bda..1c691629d4ad 100644
--- a/arch/arm64/boot/dts/ti/k3-am62-pocketbeagle2.dts
+++ b/arch/arm64/boot/dts/ti/k3-am62-pocketbeagle2.dts
@@ -235,7 +235,7 @@ AM62X_IOPAD(0x0020, PIN_OUTPUT, 3) /* (J25) OSPI0_D5.UART6_TXD */
main_mmc1_pins_default: main-mmc1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x23c, PIN_INPUT, 0) /* (A21/C18) MMC1_CMD */
- AM62X_IOPAD(0x234, PIN_INPUT, 0) /* (B22/A20) MMC1_CLK */
+ AM62X_IOPAD(0x234, PIN_OUTPUT, 0) /* (B22/A20) MMC1_CLK */
AM62X_IOPAD(0x230, PIN_INPUT, 0) /* (A22/A19) MMC1_DAT0 */
AM62X_IOPAD(0x22c, PIN_INPUT, 0) /* (B21/B19) MMC1_DAT1 */
AM62X_IOPAD(0x228, PIN_INPUT, 0) /* (C21/B20) MMC1_DAT2 */
diff --git a/arch/arm64/boot/dts/ti/k3-am62-verdin.dtsi b/arch/arm64/boot/dts/ti/k3-am62-verdin.dtsi
index 059ec492a973..5e8830fffb94 100644
--- a/arch/arm64/boot/dts/ti/k3-am62-verdin.dtsi
+++ b/arch/arm64/boot/dts/ti/k3-am62-verdin.dtsi
@@ -573,7 +573,7 @@ AM62X_IOPAD(0x15c, PIN_INPUT, 0) /* (AB22) MDIO0_MDIO */ /* ETH_1_MDIO, SODIMM
pinctrl_sdhci0: main-mmc0-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x220, PIN_INPUT, 0) /* (Y3) MMC0_CMD */
- AM62X_IOPAD(0x218, PIN_INPUT, 0) /* (AB1) MMC0_CLK */
+ AM62X_IOPAD(0x218, PIN_OUTPUT, 0) /* (AB1) MMC0_CLK */
AM62X_IOPAD(0x214, PIN_INPUT, 0) /* (AA2) MMC0_DAT0 */
AM62X_IOPAD(0x210, PIN_INPUT_PULLUP, 0) /* (AA1) MMC0_DAT1 */
AM62X_IOPAD(0x20c, PIN_INPUT_PULLUP, 0) /* (AA3) MMC0_DAT2 */
@@ -589,7 +589,7 @@ AM62X_IOPAD(0x1f8, PIN_INPUT_PULLUP, 0) /* (AC2) MMC0_DAT7 */
pinctrl_sdhci1: main-mmc1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x23c, PIN_INPUT, 0) /* (A21) MMC1_CMD */ /* SODIMM 74 */
- AM62X_IOPAD(0x234, PIN_INPUT, 0) /* (B22) MMC1_CLK */ /* SODIMM 78 */
+ AM62X_IOPAD(0x234, PIN_OUTPUT, 0) /* (B22) MMC1_CLK */ /* SODIMM 78 */
AM62X_IOPAD(0x230, PIN_INPUT, 0) /* (A22) MMC1_DAT0 */ /* SODIMM 80 */
AM62X_IOPAD(0x22c, PIN_INPUT, 0) /* (B21) MMC1_DAT1 */ /* SODIMM 82 */
AM62X_IOPAD(0x228, PIN_INPUT, 0) /* (C21) MMC1_DAT2 */ /* SODIMM 70 */
@@ -601,7 +601,7 @@ AM62X_IOPAD(0x224, PIN_INPUT, 0) /* (D22) MMC1_DAT3 */ /* SODIMM 72 */
pinctrl_sdhci2: main-mmc2-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x120, PIN_INPUT, 0) /* (C24) MMC2_CMD */ /* WiFi_SDIO_CMD */
- AM62X_IOPAD(0x118, PIN_INPUT, 0) /* (D25) MMC2_CLK */ /* WiFi_SDIO_CLK */
+ AM62X_IOPAD(0x118, PIN_OUTPUT,0) /* (D25) MMC2_CLK */ /* WiFi_SDIO_CLK */
AM62X_IOPAD(0x114, PIN_INPUT, 0) /* (B24) MMC2_DAT0 */ /* WiFi_SDIO_DATA0 */
AM62X_IOPAD(0x110, PIN_INPUT, 0) /* (C25) MMC2_DAT1 */ /* WiFi_SDIO_DATA1 */
AM62X_IOPAD(0x10c, PIN_INPUT, 0) /* (E23) MMC2_DAT2 */ /* WiFi_SDIO_DATA2 */
diff --git a/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts b/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts
index 7028d9835c4a..5d05f4b9a6d9 100644
--- a/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts
+++ b/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts
@@ -323,7 +323,7 @@ emmc_pins_default: emmc-default-pins {
bootph-all;
pinctrl-single,pins = <
AM62X_IOPAD(0x0220, PIN_INPUT, 0) /* (Y3) MMC0_CMD */
- AM62X_IOPAD(0x0218, PIN_INPUT, 0) /* (AB1) MMC0_CLK */
+ AM62X_IOPAD(0x0218, PIN_OUTPUT, 0) /* (AB1) MMC0_CLK */
AM62X_IOPAD(0x0214, PIN_INPUT, 0) /* (AA2) MMC0_DAT0 */
AM62X_IOPAD(0x0210, PIN_INPUT, 0) /* (AA1) MMC0_DAT1 */
AM62X_IOPAD(0x020c, PIN_INPUT, 0) /* (AA3) MMC0_DAT2 */
@@ -346,7 +346,7 @@ sd_pins_default: sd-default-pins {
bootph-all;
pinctrl-single,pins = <
AM62X_IOPAD(0x023c, PIN_INPUT, 0) /* (A21) MMC1_CMD */
- AM62X_IOPAD(0x0234, PIN_INPUT, 0) /* (B22) MMC1_CLK */
+ AM62X_IOPAD(0x0234, PIN_OUTPUT, 0) /* (B22) MMC1_CLK */
AM62X_IOPAD(0x0230, PIN_INPUT, 0) /* (A22) MMC1_DAT0 */
AM62X_IOPAD(0x022c, PIN_INPUT, 0) /* (B21) MMC1_DAT1 */
AM62X_IOPAD(0x0228, PIN_INPUT, 0) /* (C21) MMC1_DAT2 */
@@ -358,7 +358,7 @@ AM62X_IOPAD(0x0240, PIN_INPUT, 7) /* (D17) MMC1_SDCD.GPIO1_48 */
wifi_pins_default: wifi-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x0120, PIN_INPUT, 0) /* (C24) MMC2_CMD */
- AM62X_IOPAD(0x0118, PIN_INPUT, 0) /* (D25) MMC2_CLK */
+ AM62X_IOPAD(0x0118, PIN_OUTPUT, 0) /* (D25) MMC2_CLK */
AM62X_IOPAD(0x0114, PIN_INPUT, 0) /* (B24) MMC2_DAT0 */
AM62X_IOPAD(0x0110, PIN_INPUT, 0) /* (C25) MMC2_DAT1 */
AM62X_IOPAD(0x010c, PIN_INPUT, 0) /* (E23) MMC2_DAT2 */
diff --git a/arch/arm64/boot/dts/ti/k3-am625-sk-common.dtsi b/arch/arm64/boot/dts/ti/k3-am625-sk-common.dtsi
index fe0b98e1d105..d492ff34c5b1 100644
--- a/arch/arm64/boot/dts/ti/k3-am625-sk-common.dtsi
+++ b/arch/arm64/boot/dts/ti/k3-am625-sk-common.dtsi
@@ -98,7 +98,7 @@ main_mmc0_pins_default: main-mmc0-default-pins {
bootph-all;
pinctrl-single,pins = <
AM62X_IOPAD(0x220, PIN_INPUT, 0) /* (Y3) MMC0_CMD */
- AM62X_IOPAD(0x218, PIN_INPUT, 0) /* (AB1) MMC0_CLK */
+ AM62X_IOPAD(0x218, PIN_OUTPUT, 0) /* (AB1) MMC0_CLK */
AM62X_IOPAD(0x214, PIN_INPUT, 0) /* (AA2) MMC0_DAT0 */
AM62X_IOPAD(0x210, PIN_INPUT_PULLUP, 0) /* (AA1) MMC0_DAT1 */
AM62X_IOPAD(0x20c, PIN_INPUT_PULLUP, 0) /* (AA3) MMC0_DAT2 */
diff --git a/arch/arm64/boot/dts/ti/k3-am62a-phycore-som.dtsi b/arch/arm64/boot/dts/ti/k3-am62a-phycore-som.dtsi
index b3d012a5a26a..9da48a54855e 100644
--- a/arch/arm64/boot/dts/ti/k3-am62a-phycore-som.dtsi
+++ b/arch/arm64/boot/dts/ti/k3-am62a-phycore-som.dtsi
@@ -120,7 +120,7 @@ AM62AX_IOPAD(0x15c, PIN_INPUT, 0) /* (V13) MDIO0_MDIO */
main_mmc0_pins_default: main-mmc0-default-pins {
pinctrl-single,pins = <
AM62AX_IOPAD(0x220, PIN_INPUT_PULLUP, 0) /* (Y6) MMC0_CMD */
- AM62AX_IOPAD(0x218, PIN_INPUT_PULLDOWN, 0) /* (AB7) MMC0_CLK */
+ AM62AX_IOPAD(0x218, PIN_OUTPUT_PULLDOWN, 0) /* (AB7) MMC0_CLK */
AM62AX_IOPAD(0x214, PIN_INPUT_PULLUP, 0) /* (AA6) MMC0_DAT0 */
AM62AX_IOPAD(0x210, PIN_INPUT_PULLUP, 0) /* (AB6) MMC0_DAT1 */
AM62AX_IOPAD(0x20c, PIN_INPUT_PULLUP, 0) /* (Y7) MMC0_DAT2 */
diff --git a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
index a902b8806209..17ae6f0ceecf 100644
--- a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
+++ b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
@@ -313,8 +313,7 @@ AM62AX_IOPAD(0x0b4, PIN_INPUT_PULLUP, 1) /* (K24) GPMC0_CSn3.I2C2_SDA */
main_mmc0_pins_default: main-mmc0-default-pins {
pinctrl-single,pins = <
AM62AX_IOPAD(0x220, PIN_INPUT, 0) /* (Y3) MMC0_CMD */
- AM62AX_IOPAD(0x218, PIN_INPUT, 0) /* (AB1) MMC0_CLKLB */
- AM62AX_IOPAD(0x21c, PIN_INPUT, 0) /* (AB1) MMC0_CLK */
+ AM62AX_IOPAD(0x218, PIN_OUTPUT, 0) /* (AB1) MMC0_CLK */
AM62AX_IOPAD(0x214, PIN_INPUT, 0) /* (AA2) MMC0_DAT0 */
AM62AX_IOPAD(0x210, PIN_INPUT_PULLUP, 0) /* (AA1) MMC0_DAT1 */
AM62AX_IOPAD(0x20c, PIN_INPUT_PULLUP, 0) /* (AA3) MMC0_DAT2 */
@@ -330,7 +329,7 @@ AM62AX_IOPAD(0x1f8, PIN_INPUT_PULLUP, 0) /* (AC2) MMC0_DAT7 */
main_mmc1_pins_default: main-mmc1-default-pins {
pinctrl-single,pins = <
AM62AX_IOPAD(0x23c, PIN_INPUT, 0) /* (A21) MMC1_CMD */
- AM62AX_IOPAD(0x234, PIN_INPUT, 0) /* (B22) MMC1_CLK */
+ AM62AX_IOPAD(0x234, PIN_OUTPUT, 0) /* (B22) MMC1_CLK */
AM62AX_IOPAD(0x230, PIN_INPUT, 0) /* (A22) MMC1_DAT0 */
AM62AX_IOPAD(0x22c, PIN_INPUT, 0) /* (B21) MMC1_DAT1 */
AM62AX_IOPAD(0x228, PIN_INPUT, 0) /* (C21) MMC1_DAT2 */
diff --git a/arch/arm64/boot/dts/ti/k3-am62x-phyboard-lyra.dtsi b/arch/arm64/boot/dts/ti/k3-am62x-phyboard-lyra.dtsi
index aab74d6019b0..5a09b4c1b659 100644
--- a/arch/arm64/boot/dts/ti/k3-am62x-phyboard-lyra.dtsi
+++ b/arch/arm64/boot/dts/ti/k3-am62x-phyboard-lyra.dtsi
@@ -227,7 +227,7 @@ AM62X_IOPAD(0x068, PIN_INPUT, 3) /* (R21) GPMC0_AD11.MCASP2_AXR3 */
main_mmc1_pins_default: main-mmc1-default-pins {
pinctrl-single,pins = <
AM62X_IOPAD(0x23c, PIN_INPUT_PULLUP, 0) /* (A21) MMC1_CMD */
- AM62X_IOPAD(0x234, PIN_INPUT_PULLDOWN, 0) /* (B22) MMC1_CLK */
+ AM62X_IOPAD(0x234, PIN_OUTPUT_PULLDOWN, 0) /* (B22) MMC1_CLK */
AM62X_IOPAD(0x230, PIN_INPUT_PULLUP, 0) /* (A22) MMC1_DAT0 */
AM62X_IOPAD(0x22c, PIN_INPUT_PULLUP, 0) /* (B21) MMC1_DAT1 */
AM62X_IOPAD(0x228, PIN_INPUT_PULLUP, 0) /* (C21) MMC1_DAT2 */
diff --git a/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi b/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi
index 58f78c0de292..3c5ef8fc9cdd 100644
--- a/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi
+++ b/arch/arm64/boot/dts/ti/k3-am62x-sk-common.dtsi
@@ -187,7 +187,7 @@ main_mmc1_pins_default: main-mmc1-default-pins {
bootph-all;
pinctrl-single,pins = <
AM62X_IOPAD(0x23c, PIN_INPUT, 0) /* (A21/C18) MMC1_CMD */
- AM62X_IOPAD(0x234, PIN_INPUT, 0) /* (B22/A20) MMC1_CLK */
+ AM62X_IOPAD(0x234, PIN_OUTPUT, 0) /* (B22/A20) MMC1_CLK */
AM62X_IOPAD(0x230, PIN_INPUT, 0) /* (A22/A19) MMC1_DAT0 */
AM62X_IOPAD(0x22c, PIN_INPUT, 0) /* (B21/B19) MMC1_DAT1 */
AM62X_IOPAD(0x228, PIN_INPUT, 0) /* (C21/B20) MMC1_DAT2 */
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 1da290aeedce..641276868a65 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -98,7 +98,7 @@
* TID1: Trap REVIDR_EL1, AIDR_EL1, and SMIDR_EL1
*/
#define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWE | HCR_TWI | HCR_VM | \
- HCR_BSU_IS | HCR_FB | HCR_TACR | \
+ SYS_FIELD_PREP_ENUM(HCR_EL2, BSU, IS) | HCR_EL2_FB | HCR_EL2_TACR | \
HCR_AMO | HCR_SWIO | HCR_TIDCP | HCR_RW | HCR_TLOR | \
HCR_FMO | HCR_IMO | HCR_PTW | HCR_TID3 | HCR_TID1)
#define HCR_HOST_NVHE_FLAGS (HCR_RW | HCR_API | HCR_APK | HCR_ATA)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 419e37112a6f..b7334f98deaa 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -833,11 +833,10 @@ struct kvm_vcpu_arch {
/*
* Don't run the guest (internal implementation need).
*
- * Contrary to the flags above, this is set/cleared outside of
- * a vcpu context, and thus cannot be mixed with the flags
- * themselves (or the flag accesses need to be made atomic).
+ * Contrary to the flags above, this is updated outside of
+ * a vcpu context, and thus cannot be mixed with the flags.
*/
- bool pause;
+ atomic_t pause;
/*
* We maintain more than a single set of debug registers to support
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 1765e484fb9f..2ce69d64e60c 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -1675,6 +1675,9 @@ extern void modify_prot_commit_ptes(struct vm_area_struct *vma, unsigned long ad
* The contpte APIs are used to transparently manage the contiguous bit in ptes
* where it is possible and makes sense to do so. The PTE_CONT bit is considered
* a private implementation detail of the public ptep API (see below).
+ * Single-PTE transparent contpte helpers use pte_valid_cont() to identify
+ * grouped PTEs; explicit contiguous-encoding users such as hugetlb manage
+ * their entries separately.
*/
extern void __contpte_try_fold(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pte);
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index 6c371b158b99..a3c0d5c2e9f7 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -116,8 +116,14 @@ static int __init set_permissions(pte_t *ptep, unsigned long addr, void *data)
const efi_memory_desc_t *md = spd->md;
pte_t pte = __ptep_get(ptep);
+ /*
+ * Clear PTE_WRITE as well: it doubles as the DBM bit, and leaving it
+ * set would permit the hardware to make the mapping writable again
+ * when TCR_EL1.HD is set.
+ */
if (md->attribute & EFI_MEMORY_RO)
- pte = set_pte_bit(pte, __pgprot(PTE_RDONLY));
+ pte = set_pte_bit(clear_pte_bit(pte, __pgprot(PTE_WRITE)),
+ __pgprot(PTE_RDONLY));
if (md->attribute & EFI_MEMORY_XP)
pte = set_pte_bit(pte, __pgprot(PTE_PXN));
else if (system_supports_bti_kernel() && spd->has_bti)
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index b52c162cb380..4e0db01a528a 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -500,6 +500,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
kvm_arm_pvtime_vcpu_init(&vcpu->arch);
vcpu->arch.hw_mmu = &vcpu->kvm->arch.mmu;
+ atomic_set(&vcpu->arch.pause, 0);
/*
* This vCPU may have been created after mpidr_data was initialized.
@@ -765,6 +766,11 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
return ret;
}
+static bool vcpu_can_run(struct kvm_vcpu *vcpu)
+{
+ return !kvm_arm_vcpu_stopped(vcpu) && !atomic_read(&vcpu->arch.pause);
+}
+
/**
* kvm_arch_vcpu_runnable - determine if the vcpu can be scheduled
* @v: The VCPU pointer
@@ -780,8 +786,7 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
(kvm_timer_should_notify_user(v) ||
kvm_pmu_should_notify_user(v)));
- return ((irq_lines || kvm_vgic_vcpu_pending_irq(v))
- && !kvm_arm_vcpu_stopped(v) && !v->arch.pause);
+ return ((irq_lines || kvm_vgic_vcpu_pending_irq(v)) && vcpu_can_run(v));
}
bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu)
@@ -937,7 +942,7 @@ void kvm_arm_halt_guest(struct kvm *kvm)
struct kvm_vcpu *vcpu;
kvm_for_each_vcpu(i, vcpu, kvm)
- vcpu->arch.pause = true;
+ atomic_inc(&vcpu->arch.pause);
kvm_make_all_cpus_request(kvm, KVM_REQ_SLEEP);
}
@@ -947,8 +952,8 @@ void kvm_arm_resume_guest(struct kvm *kvm)
struct kvm_vcpu *vcpu;
kvm_for_each_vcpu(i, vcpu, kvm) {
- vcpu->arch.pause = false;
- __kvm_vcpu_wake_up(vcpu);
+ if (atomic_dec_and_test(&vcpu->arch.pause))
+ __kvm_vcpu_wake_up(vcpu);
}
}
@@ -956,11 +961,9 @@ static void kvm_vcpu_sleep(struct kvm_vcpu *vcpu)
{
struct rcuwait *wait = kvm_arch_vcpu_get_wait(vcpu);
- rcuwait_wait_event(wait,
- (!kvm_arm_vcpu_stopped(vcpu)) && (!vcpu->arch.pause),
- TASK_INTERRUPTIBLE);
+ rcuwait_wait_event(wait, vcpu_can_run(vcpu), TASK_INTERRUPTIBLE);
- if (kvm_arm_vcpu_stopped(vcpu) || vcpu->arch.pause) {
+ if (!vcpu_can_run(vcpu)) {
/* Awaken to handle a signal, request we sleep again later. */
kvm_make_request(KVM_REQ_SLEEP, vcpu);
}
@@ -1229,31 +1232,33 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
local_irq_disable();
- kvm_vgic_flush_hwstate(vcpu);
-
- kvm_pmu_update_vcpu_events(vcpu);
-
/*
* Ensure we set mode to IN_GUEST_MODE after we disable
* interrupts and before the final VCPU requests check.
* See the comment in kvm_vcpu_exiting_guest_mode() and
* Documentation/virt/kvm/vcpu-requests.rst
+ *
+ * Requests to self must be posted before this point, or
+ * won't be taken into account until the next exit. No state
+ * loading other than what happens at vcpu_load() time can
+ * be done before we have checked for pending requests.
*/
smp_store_mb(vcpu->mode, IN_GUEST_MODE);
if (ret <= 0 || kvm_vcpu_exit_request(vcpu, &ret)) {
- vcpu->mode = OUTSIDE_GUEST_MODE;
- isb(); /* Ensure work in x_flush_hwstate is committed */
- if (kvm_vcpu_has_pmu(vcpu))
- kvm_pmu_sync_hwstate(vcpu);
if (unlikely(!irqchip_in_kernel(vcpu->kvm)))
kvm_timer_sync_user(vcpu);
- kvm_vgic_sync_hwstate(vcpu);
+ smp_store_release(&vcpu->mode, OUTSIDE_GUEST_MODE);
local_irq_enable();
preempt_enable();
continue;
}
+ /* From here onwards, we are commited to entering the guest */
+ kvm_vgic_flush_hwstate(vcpu);
+
+ kvm_pmu_update_vcpu_events(vcpu);
+
kvm_arch_vcpu_ctxflush_fp(vcpu);
/**************************************************************
@@ -1264,7 +1269,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
ret = kvm_arm_vcpu_enter_exit(vcpu);
- vcpu->mode = OUTSIDE_GUEST_MODE;
vcpu->stat.exits++;
/*
* Back from guest
@@ -1298,6 +1302,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
kvm_arch_vcpu_ctxsync_fp(vcpu);
+ /*
+ * The state that was pushed into the CPU on entering
+ * the guest has been synchronised back, let's
+ * advertise we're outside of the guest.
+ */
+ smp_store_release(&vcpu->mode, OUTSIDE_GUEST_MODE);
+
/*
* We must ensure that any pending interrupts are taken before
* we exit guest timing so that timer ticks are accounted as
diff --git a/arch/arm64/kvm/hyp/exception.c b/arch/arm64/kvm/hyp/exception.c
index bef40ddb16db..c041142896e3 100644
--- a/arch/arm64/kvm/hyp/exception.c
+++ b/arch/arm64/kvm/hyp/exception.c
@@ -86,6 +86,7 @@ static void enter_exception64(struct kvm_vcpu *vcpu, unsigned long target_mode,
enum exception_type type)
{
unsigned long sctlr, vbar, old, new, mode;
+ struct kvm *kvm;
u64 exc_offset;
mode = *vcpu_cpsr(vcpu) & (PSR_MODE_MASK | PSR_MODE32_BIT);
@@ -125,8 +126,10 @@ static void enter_exception64(struct kvm_vcpu *vcpu, unsigned long target_mode,
new |= (old & PSR_C_BIT);
new |= (old & PSR_V_BIT);
- if (kvm_has_mte(kern_hyp_va(vcpu->kvm)))
+ kvm = vcpu_get_kvm(vcpu);
+ if (kvm && kvm_has_mte(kvm))
new |= PSR_TCO_BIT;
+ vcpu_put_kvm(vcpu, kvm);
new |= (old & PSR_DIT_BIT);
diff --git a/arch/arm64/kvm/hyp/include/hyp/adjust_pc.h b/arch/arm64/kvm/hyp/include/hyp/adjust_pc.h
index 4fdfeabefeb4..f55950ee2a7e 100644
--- a/arch/arm64/kvm/hyp/include/hyp/adjust_pc.h
+++ b/arch/arm64/kvm/hyp/include/hyp/adjust_pc.h
@@ -13,6 +13,34 @@
#include <asm/kvm_emulate.h>
#include <asm/kvm_host.h>
+#ifdef __KVM_NVHE_HYPERVISOR__
+#include <nvhe/pkvm.h>
+
+/* Under pKVM a host vCPU's ->kvm is host-writable. */
+static inline struct kvm *vcpu_get_kvm(struct kvm_vcpu *vcpu)
+{
+ if (is_protected_kvm_enabled())
+ return pkvm_vcpu_get_kvm(vcpu);
+
+ return kern_hyp_va(vcpu->kvm);
+}
+
+static inline void vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm)
+{
+ if (is_protected_kvm_enabled())
+ pkvm_vcpu_put_kvm(vcpu, kvm);
+}
+#else
+static inline struct kvm *vcpu_get_kvm(struct kvm_vcpu *vcpu)
+{
+ return vcpu->kvm;
+}
+
+static inline void vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm)
+{
+}
+#endif
+
static inline void kvm_skip_instr(struct kvm_vcpu *vcpu)
{
if (vcpu_mode_is_32bit(vcpu)) {
diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
index 184ad7a39950..25b2f477bb5b 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
@@ -79,6 +79,8 @@ struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
unsigned int vcpu_idx);
void pkvm_put_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu);
struct pkvm_hyp_vcpu *pkvm_get_loaded_hyp_vcpu(void);
+struct kvm *pkvm_vcpu_get_kvm(struct kvm_vcpu *vcpu);
+void pkvm_vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm);
struct pkvm_hyp_vm *get_pkvm_hyp_vm(pkvm_handle_t handle);
struct pkvm_hyp_vm *get_np_pkvm_hyp_vm(pkvm_handle_t handle);
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index bf0250806a78..136c4008b936 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -306,6 +306,43 @@ struct pkvm_hyp_vcpu *pkvm_get_loaded_hyp_vcpu(void)
}
+static struct pkvm_hyp_vm *pkvm_get_loaded_hyp_vm(struct kvm_vcpu *vcpu)
+{
+ struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
+
+ if (hyp_vcpu &&
+ (vcpu == &hyp_vcpu->vcpu || vcpu == hyp_vcpu->host_vcpu))
+ return pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
+
+ return NULL;
+}
+
+/*
+ * A loaded vCPU's VM is the hyp VM. An unloaded host vCPU's is mapped at
+ * EL2 only while pinned, so it's read once and pinned; the pin fails for
+ * memory the host isn't sharing.
+ */
+struct kvm *pkvm_vcpu_get_kvm(struct kvm_vcpu *vcpu)
+{
+ struct pkvm_hyp_vm *hyp_vm = pkvm_get_loaded_hyp_vm(vcpu);
+ struct kvm *kvm;
+
+ if (hyp_vm)
+ return &hyp_vm->kvm;
+
+ kvm = kern_hyp_va(READ_ONCE(vcpu->kvm));
+ if (hyp_pin_shared_mem(kvm, kvm + 1))
+ return NULL;
+
+ return kvm;
+}
+
+void pkvm_vcpu_put_kvm(struct kvm_vcpu *vcpu, struct kvm *kvm)
+{
+ if (kvm && !pkvm_get_loaded_hyp_vm(vcpu))
+ hyp_unpin_shared_mem(kvm, kvm + 1);
+}
+
struct pkvm_hyp_vm *get_pkvm_hyp_vm(pkvm_handle_t handle)
{
struct pkvm_hyp_vm *hyp_vm;
diff --git a/arch/arm64/kvm/hyp/nvhe/sys_regs.c b/arch/arm64/kvm/hyp/nvhe/sys_regs.c
index 3108b5185c20..168d473e084b 100644
--- a/arch/arm64/kvm/hyp/nvhe/sys_regs.c
+++ b/arch/arm64/kvm/hyp/nvhe/sys_regs.c
@@ -115,6 +115,9 @@ static const struct pvm_ftr_bits pvmid_aa64mmfr0[] = {
MAX_FEAT(ID_AA64MMFR0_EL1, SNSMEM, IMP),
MAX_FEAT(ID_AA64MMFR0_EL1, BIGENDEL0, IMP),
MAX_FEAT(ID_AA64MMFR0_EL1, EXS, IMP),
+ MAX_FEAT(ID_AA64MMFR0_EL1, TGRAN4, IMP),
+ MAX_FEAT(ID_AA64MMFR0_EL1, TGRAN16, IMP),
+ MAX_FEAT(ID_AA64MMFR0_EL1, TGRAN64, IMP),
FEAT_END
};
diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c
index 051d9bf73879..79e018cec0f9 100644
--- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c
@@ -818,16 +818,21 @@ int vgic_register_redist_iodev(struct kvm_vcpu *vcpu)
rd_dev->nr_regions = ARRAY_SIZE(vgic_v3_rd_registers);
rd_dev->redist_vcpu = vcpu;
+ /* Protected by slots_lock */
+ rdreg->free_index++;
+
mutex_unlock(&kvm->arch.config_lock);
ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, rd_base,
2 * SZ_64K, &rd_dev->dev);
- if (ret)
- return ret;
+ if (ret) {
+ guard(mutex)(&vcpu->kvm->arch.config_lock);
+ vgic_cpu->rdreg->free_index--;
+ vgic_cpu->rdreg = NULL;
+ vgic_cpu->rd_iodev.base_addr = VGIC_ADDR_UNDEF;
+ }
- /* Protected by slots_lock */
- rdreg->free_index++;
- return 0;
+ return ret;
out_unlock:
mutex_unlock(&kvm->arch.config_lock);
@@ -841,7 +846,8 @@ void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu)
kvm_io_bus_unregister_dev(vcpu->kvm, KVM_MMIO_BUS, &rd_dev->dev);
}
-static int vgic_register_all_redist_iodevs(struct kvm *kvm)
+static int vgic_register_all_redist_iodevs(struct kvm *kvm,
+ struct vgic_redist_region *rdreg)
{
struct kvm_vcpu *vcpu;
unsigned long c;
@@ -861,7 +867,14 @@ static int vgic_register_all_redist_iodevs(struct kvm *kvm)
for (i = 0; i < c; i++) {
vcpu = kvm_get_vcpu(kvm, i);
+ if (vcpu->arch.vgic_cpu.rdreg != rdreg)
+ continue;
+
vgic_unregister_redist_iodev(vcpu);
+
+ guard(mutex)(&vcpu->kvm->arch.config_lock);
+ vcpu->arch.vgic_cpu.rdreg = NULL;
+ vcpu->arch.vgic_cpu.rd_iodev.base_addr = VGIC_ADDR_UNDEF;
}
}
@@ -970,10 +983,13 @@ void vgic_v3_free_redist_region(struct kvm *kvm, struct vgic_redist_region *rdre
int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count)
{
+ struct vgic_redist_region *rdreg;
int ret;
mutex_lock(&kvm->arch.config_lock);
ret = vgic_v3_alloc_redist_region(kvm, index, addr, count);
+ if (!ret)
+ rdreg = vgic_v3_rdist_region_from_index(kvm, index);
mutex_unlock(&kvm->arch.config_lock);
if (ret)
return ret;
@@ -982,12 +998,9 @@ int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count)
* Register iodevs for each existing VCPU. Adding more VCPUs
* afterwards will register the iodevs when needed.
*/
- ret = vgic_register_all_redist_iodevs(kvm);
+ ret = vgic_register_all_redist_iodevs(kvm, rdreg);
if (ret) {
- struct vgic_redist_region *rdreg;
-
mutex_lock(&kvm->arch.config_lock);
- rdreg = vgic_v3_rdist_region_from_index(kvm, index);
vgic_v3_free_redist_region(kvm, rdreg);
mutex_unlock(&kvm->arch.config_lock);
return ret;
diff --git a/arch/arm64/mm/contpte.c b/arch/arm64/mm/contpte.c
index 29024e20e876..c39a290c6b5a 100644
--- a/arch/arm64/mm/contpte.c
+++ b/arch/arm64/mm/contpte.c
@@ -444,8 +444,10 @@ void contpte_set_ptes(struct mm_struct *mm, unsigned long addr,
*/
VM_WARN_ON(nr == 1);
- if (!mm_is_user(mm))
+ if (!mm_is_user(mm) || !pte_valid(pte)) {
+ VM_WARN_ON_ONCE(!pte_valid(pte) && pte_cont(pte));
return __set_ptes(mm, addr, ptep, pte, nr);
+ }
end = addr + (nr << PAGE_SHIFT);
pfn = pte_pfn(pte);
diff --git a/arch/loongarch/include/asm/barrier.h b/arch/loongarch/include/asm/barrier.h
index 4b663f197706..84c6d739119d 100644
--- a/arch/loongarch/include/asm/barrier.h
+++ b/arch/loongarch/include/asm/barrier.h
@@ -57,9 +57,6 @@
#define __WEAK_LLSC_MB " \n"
#endif
-#define __smp_mb__before_atomic() barrier()
-#define __smp_mb__after_atomic() barrier()
-
/**
* array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise
* @index: array element index
diff --git a/arch/loongarch/include/asm/hw_breakpoint.h b/arch/loongarch/include/asm/hw_breakpoint.h
index 5faa97a87a9e..d202052df8a1 100644
--- a/arch/loongarch/include/asm/hw_breakpoint.h
+++ b/arch/loongarch/include/asm/hw_breakpoint.h
@@ -116,8 +116,8 @@ extern void arch_uninstall_hw_breakpoint(struct perf_event *bp);
extern int hw_breakpoint_slots(int type);
extern void hw_breakpoint_pmu_read(struct perf_event *bp);
-void breakpoint_handler(struct pt_regs *regs);
-void watchpoint_handler(struct pt_regs *regs);
+bool breakpoint_handler(struct pt_regs *regs);
+bool watchpoint_handler(struct pt_regs *regs);
#ifdef CONFIG_HAVE_HW_BREAKPOINT
extern void ptrace_hw_copy_thread(struct task_struct *task);
diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
index 58a4a3b6b035..642dc9adddce 100644
--- a/arch/loongarch/include/asm/loongarch.h
+++ b/arch/loongarch/include/asm/loongarch.h
@@ -1090,8 +1090,8 @@
#define LOONGARCH_CSR_DERA 0x501 /* debug era */
#define LOONGARCH_CSR_DESAVE 0x502 /* debug save */
-#define CSR_FWPC_SKIP_SHIFT 16
-#define CSR_FWPC_SKIP (_ULCAST_(1) << CSR_FWPC_SKIP_SHIFT)
+#define CSR_FWPS_SKIP_SHIFT 16
+#define CSR_FWPS_SKIP (_ULCAST_(1) << CSR_FWPS_SKIP_SHIFT)
/*
* CSR_ECFG IM
diff --git a/arch/loongarch/include/asm/numa.h b/arch/loongarch/include/asm/numa.h
index bbf9f70bd25f..c9654ff900a3 100644
--- a/arch/loongarch/include/asm/numa.h
+++ b/arch/loongarch/include/asm/numa.h
@@ -14,7 +14,7 @@
#define NODE_ADDRSPACE_SHIFT 44
#define pa_to_nid(addr) (((addr) & 0xf00000000000) >> NODE_ADDRSPACE_SHIFT)
-#define nid_to_addrbase(nid) (_ULCAST_(nid) << NODE_ADDRSPACE_SHIFT)
+#define nid_to_addrbase(nid) (_U64CAST_(nid) << NODE_ADDRSPACE_SHIFT)
#ifdef CONFIG_NUMA
diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
index 4ac9c9599167..9f0f9fdac011 100644
--- a/arch/loongarch/kernel/cpu-probe.c
+++ b/arch/loongarch/kernel/cpu-probe.c
@@ -93,7 +93,7 @@ static int __init cpu_final_simd(void)
return 0;
}
-arch_initcall(cpu_final_simd);
+pure_initcall(cpu_final_simd);
static inline void set_elf_platform(int cpu, const char *plat)
{
diff --git a/arch/loongarch/kernel/hw_breakpoint.c b/arch/loongarch/kernel/hw_breakpoint.c
index c35f9bf38033..1a05350159f7 100644
--- a/arch/loongarch/kernel/hw_breakpoint.c
+++ b/arch/loongarch/kernel/hw_breakpoint.c
@@ -233,7 +233,7 @@ static int hw_breakpoint_control(struct perf_event *bp,
}
enable = csr_read64(LOONGARCH_CSR_CRMD);
csr_write64(CSR_CRMD_WE | enable, LOONGARCH_CSR_CRMD);
- if (bp->hw.target && test_tsk_thread_flag(bp->hw.target, TIF_LOAD_WATCH))
+ if (bp->hw.target)
regs->csr_prmd |= CSR_PRMD_PWE;
break;
case HW_BREAKPOINT_UNINSTALL:
@@ -453,7 +453,6 @@ static void update_bp_registers(struct pt_regs *regs, int enable, int type)
if (enable) {
if ((info->ctrl.type == LOONGARCH_BREAKPOINT_EXECUTE) && (type == 0)) {
write_wb_reg(CSR_CFG_CTRL, i, 0, CTRL_PLV_ENABLE);
- write_wb_reg(CSR_CFG_CTRL, i, 0, CTRL_PLV_ENABLE);
} else {
ctrl = read_wb_reg(CSR_CFG_CTRL, i, 1);
if (info->ctrl.type == LOONGARCH_BREAKPOINT_LOAD)
@@ -469,9 +468,9 @@ static void update_bp_registers(struct pt_regs *regs, int enable, int type)
} else {
ctrl = read_wb_reg(CSR_CFG_CTRL, i, 1);
if (info->ctrl.type == LOONGARCH_BREAKPOINT_LOAD)
- ctrl &= ~0x1 << MWPnCFG3_LoadEn;
+ ctrl &= ~(0x1 << MWPnCFG3_LoadEn);
if (info->ctrl.type == LOONGARCH_BREAKPOINT_STORE)
- ctrl &= ~0x1 << MWPnCFG3_StoreEn;
+ ctrl &= ~(0x1 << MWPnCFG3_StoreEn);
write_wb_reg(CSR_CFG_CTRL, i, 1, ctrl);
}
regs->csr_prmd &= ~CSR_PRMD_PWE;
@@ -483,9 +482,10 @@ NOKPROBE_SYMBOL(update_bp_registers);
/*
* Debug exception handlers.
*/
-void breakpoint_handler(struct pt_regs *regs)
+bool breakpoint_handler(struct pt_regs *regs)
{
int i;
+ bool need_sigtrap = false;
struct perf_event *bp, **slots;
slots = this_cpu_ptr(bp_on_reg);
@@ -495,17 +495,24 @@ void breakpoint_handler(struct pt_regs *regs)
bp = slots[i];
if (bp == NULL)
continue;
+
perf_bp_event(bp, regs);
+ if (bp->attr.sigtrap)
+ need_sigtrap = true;
+
csr_write32(0x1 << i, LOONGARCH_CSR_FWPS);
update_bp_registers(regs, 0, 0);
}
}
+
+ return need_sigtrap;
}
NOKPROBE_SYMBOL(breakpoint_handler);
-void watchpoint_handler(struct pt_regs *regs)
+bool watchpoint_handler(struct pt_regs *regs)
{
int i;
+ bool need_sigtrap = false;
struct perf_event *wp, **slots;
slots = this_cpu_ptr(wp_on_reg);
@@ -515,11 +522,17 @@ void watchpoint_handler(struct pt_regs *regs)
wp = slots[i];
if (wp == NULL)
continue;
+
perf_bp_event(wp, regs);
+ if (wp->attr.sigtrap)
+ need_sigtrap = true;
+
csr_write32(0x1 << i, LOONGARCH_CSR_MWPS);
update_bp_registers(regs, 0, 1);
}
}
+
+ return need_sigtrap;
}
NOKPROBE_SYMBOL(watchpoint_handler);
@@ -551,7 +564,7 @@ void hw_breakpoint_thread_switch(struct task_struct *next)
addr = read_wb_reg(CSR_CFG_ADDR, 0, 0);
mask = read_wb_reg(CSR_CFG_MASK, 0, 0);
if (!((regs->csr_era ^ addr) & ~mask))
- csr_write32(CSR_FWPC_SKIP, LOONGARCH_CSR_FWPS);
+ csr_write32(CSR_FWPS_SKIP, LOONGARCH_CSR_FWPS);
regs->csr_prmd |= CSR_PRMD_PWE;
} else {
/* Update breakpoints */
diff --git a/arch/loongarch/kernel/irq.c b/arch/loongarch/kernel/irq.c
index 80946cafaec1..130954ab9ee2 100644
--- a/arch/loongarch/kernel/irq.c
+++ b/arch/loongarch/kernel/irq.c
@@ -19,6 +19,7 @@
#include <asm/irq.h>
#include <asm/loongson.h>
+#include <asm/numa.h>
#include <asm/setup.h>
DEFINE_PER_CPU(unsigned long, irq_stack);
@@ -68,7 +69,7 @@ static int __init early_pci_mcfg_parse(struct acpi_table_header *header)
for (i = 0; i < n; i++, mptr++) {
msi_group[i].pci_segment = mptr->pci_segment;
- pch_group[i].node = msi_group[i].node = (mptr->address >> 44) & 0xf;
+ pch_group[i].node = msi_group[i].node = pa_to_nid(mptr->address);
}
return 0;
diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c
index 8e51ce004572..9ce12327d64e 100644
--- a/arch/loongarch/kernel/traps.c
+++ b/arch/loongarch/kernel/traps.c
@@ -807,6 +807,8 @@ asmlinkage void noinstr do_watch(struct pt_regs *regs)
#ifndef CONFIG_HAVE_HW_BREAKPOINT
pr_warn("Hardware watch point handler not implemented!\n");
#else
+ bool need_sigtrap = !!current->ptrace;
+
if (kgdb_breakpoint_handler(regs))
goto out;
@@ -821,7 +823,7 @@ asmlinkage void noinstr do_watch(struct pt_regs *regs)
* instruction. So don't clear llbit and reset CSR.FWPS.Skip until
* the llsc execution is completed.
*/
- csr_write32(CSR_FWPC_SKIP, LOONGARCH_CSR_FWPS);
+ csr_write32(CSR_FWPS_SKIP, LOONGARCH_CSR_FWPS);
csr_write32(CSR_LLBCTL_KLO, LOONGARCH_CSR_LLBCTL);
goto out;
}
@@ -838,16 +840,17 @@ asmlinkage void noinstr do_watch(struct pt_regs *regs)
* current pc, If yes, then we should not set the CSR.FWPS.SKIP
* bit to break the original instruction stream.
*/
- csr_write32(CSR_FWPC_SKIP, LOONGARCH_CSR_FWPS);
+ csr_write32(CSR_FWPS_SKIP, LOONGARCH_CSR_FWPS);
goto out;
}
}
} else {
- breakpoint_handler(regs);
- watchpoint_handler(regs);
+ need_sigtrap |= breakpoint_handler(regs);
+ need_sigtrap |= watchpoint_handler(regs);
}
- force_sig(SIGTRAP);
+ if (need_sigtrap)
+ force_sig(SIGTRAP);
out:
#endif
irqentry_exit(regs, state);
diff --git a/arch/loongarch/pci/pci.c b/arch/loongarch/pci/pci.c
index f33c7ea1443d..0b99b32110a5 100644
--- a/arch/loongarch/pci/pci.c
+++ b/arch/loongarch/pci/pci.c
@@ -12,6 +12,7 @@
#include <linux/io-64-nonatomic-lo-hi.h>
#include <asm/cacheflush.h>
#include <asm/loongson.h>
+#include <asm/numa.h>
#define PCI_DEVICE_ID_LOONGSON_HOST 0x7a00
#define PCI_DEVICE_ID_LOONGSON_DC1 0x7a06
@@ -43,7 +44,7 @@ int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
phys_addr_t mcfg_addr_init(int node)
{
- return (((u64)node << 44) | MCFG_EXT_PCICFG_BASE);
+ return (nid_to_addrbase(node) | MCFG_EXT_PCICFG_BASE);
}
static int __init pcibios_init(void)
diff --git a/arch/microblaze/include/asm/entry.h b/arch/microblaze/include/asm/entry.h
index 9efadf12397c..05acf4d7bc4b 100644
--- a/arch/microblaze/include/asm/entry.h
+++ b/arch/microblaze/include/asm/entry.h
@@ -21,6 +21,19 @@
#define PER_CPU(var) var
+/*
+ * The MicroBlaze ABI has the caller reserve an argument home area:
+ * REG_PARM_STACK_SPACE is 24 and OUTGOING_REG_PARM_STACK_SPACE is 1, so a
+ * callee may write to [caller_sp + 4, caller_sp + 28). The kernel calls C
+ * with r1 at the frame base, so reserve that area below pt_regs and reach
+ * the saved registers through PTO. This restores what 6e83557c38b4
+ * ("microblaze: Remove r0_ram pointer and PTO alignment") removed; the old
+ * value of 24 was one word short and left the last argument slot
+ * overlapping pt_regs' r0.
+ */
+#define PTO 28
+#define STATE_SAVE_SIZE (PT_SIZE + PTO)
+
# ifndef __ASSEMBLER__
DECLARE_PER_CPU(unsigned int, KSP); /* Saved kernel stack pointer */
DECLARE_PER_CPU(unsigned int, KM); /* Kernel/user mode */
diff --git a/arch/microblaze/include/asm/processor.h b/arch/microblaze/include/asm/processor.h
index d59bdfffca7c..595c24db2728 100644
--- a/arch/microblaze/include/asm/processor.h
+++ b/arch/microblaze/include/asm/processor.h
@@ -73,7 +73,7 @@ unsigned long __get_wchan(struct task_struct *p);
# define task_regs(task) ((struct pt_regs *)task_tos(task) - 1)
# define task_pt_regs_plus_args(tsk) \
- ((void *)task_pt_regs(tsk))
+ (((void *)task_pt_regs(tsk)) - PTO)
# define task_sp(task) (task_regs(task)->r1)
# define task_pc(task) (task_regs(task)->pc)
diff --git a/arch/microblaze/kernel/entry.S b/arch/microblaze/kernel/entry.S
index 582d7256d815..ebc8bcb98c52 100644
--- a/arch/microblaze/kernel/entry.S
+++ b/arch/microblaze/kernel/entry.S
@@ -177,78 +177,78 @@ syscall_debug_table:
1:
#define SAVE_REGS \
- swi r2, r1, PT_R2; /* Save SDA */ \
- swi r3, r1, PT_R3; \
- swi r4, r1, PT_R4; \
- swi r5, r1, PT_R5; \
- swi r6, r1, PT_R6; \
- swi r7, r1, PT_R7; \
- swi r8, r1, PT_R8; \
- swi r9, r1, PT_R9; \
- swi r10, r1, PT_R10; \
- swi r11, r1, PT_R11; /* save clobbered regs after rval */\
- swi r12, r1, PT_R12; \
- swi r13, r1, PT_R13; /* Save SDA2 */ \
- swi r14, r1, PT_PC; /* PC, before IRQ/trap */ \
- swi r15, r1, PT_R15; /* Save LP */ \
- swi r16, r1, PT_R16; \
- swi r17, r1, PT_R17; \
- swi r18, r1, PT_R18; /* Save asm scratch reg */ \
- swi r19, r1, PT_R19; \
- swi r20, r1, PT_R20; \
- swi r21, r1, PT_R21; \
- swi r22, r1, PT_R22; \
- swi r23, r1, PT_R23; \
- swi r24, r1, PT_R24; \
- swi r25, r1, PT_R25; \
- swi r26, r1, PT_R26; \
- swi r27, r1, PT_R27; \
- swi r28, r1, PT_R28; \
- swi r29, r1, PT_R29; \
- swi r30, r1, PT_R30; \
- swi r31, r1, PT_R31; /* Save current task reg */ \
+ swi r2, r1, PTO+PT_R2; /* Save SDA */ \
+ swi r3, r1, PTO+PT_R3; \
+ swi r4, r1, PTO+PT_R4; \
+ swi r5, r1, PTO+PT_R5; \
+ swi r6, r1, PTO+PT_R6; \
+ swi r7, r1, PTO+PT_R7; \
+ swi r8, r1, PTO+PT_R8; \
+ swi r9, r1, PTO+PT_R9; \
+ swi r10, r1, PTO+PT_R10; \
+ swi r11, r1, PTO+PT_R11; /* save clobbered regs after rval */\
+ swi r12, r1, PTO+PT_R12; \
+ swi r13, r1, PTO+PT_R13; /* Save SDA2 */ \
+ swi r14, r1, PTO+PT_PC; /* PC, before IRQ/trap */ \
+ swi r15, r1, PTO+PT_R15; /* Save LP */ \
+ swi r16, r1, PTO+PT_R16; \
+ swi r17, r1, PTO+PT_R17; \
+ swi r18, r1, PTO+PT_R18; /* Save asm scratch reg */ \
+ swi r19, r1, PTO+PT_R19; \
+ swi r20, r1, PTO+PT_R20; \
+ swi r21, r1, PTO+PT_R21; \
+ swi r22, r1, PTO+PT_R22; \
+ swi r23, r1, PTO+PT_R23; \
+ swi r24, r1, PTO+PT_R24; \
+ swi r25, r1, PTO+PT_R25; \
+ swi r26, r1, PTO+PT_R26; \
+ swi r27, r1, PTO+PT_R27; \
+ swi r28, r1, PTO+PT_R28; \
+ swi r29, r1, PTO+PT_R29; \
+ swi r30, r1, PTO+PT_R30; \
+ swi r31, r1, PTO+PT_R31; /* Save current task reg */ \
mfs r11, rmsr; /* save MSR */ \
- swi r11, r1, PT_MSR;
+ swi r11, r1, PTO+PT_MSR;
#define RESTORE_REGS_GP \
- lwi r2, r1, PT_R2; /* restore SDA */ \
- lwi r3, r1, PT_R3; \
- lwi r4, r1, PT_R4; \
- lwi r5, r1, PT_R5; \
- lwi r6, r1, PT_R6; \
- lwi r7, r1, PT_R7; \
- lwi r8, r1, PT_R8; \
- lwi r9, r1, PT_R9; \
- lwi r10, r1, PT_R10; \
- lwi r11, r1, PT_R11; /* restore clobbered regs after rval */\
- lwi r12, r1, PT_R12; \
- lwi r13, r1, PT_R13; /* restore SDA2 */ \
- lwi r14, r1, PT_PC; /* RESTORE_LINK PC, before IRQ/trap */\
- lwi r15, r1, PT_R15; /* restore LP */ \
- lwi r16, r1, PT_R16; \
- lwi r17, r1, PT_R17; \
- lwi r18, r1, PT_R18; /* restore asm scratch reg */ \
- lwi r19, r1, PT_R19; \
- lwi r20, r1, PT_R20; \
- lwi r21, r1, PT_R21; \
- lwi r22, r1, PT_R22; \
- lwi r23, r1, PT_R23; \
- lwi r24, r1, PT_R24; \
- lwi r25, r1, PT_R25; \
- lwi r26, r1, PT_R26; \
- lwi r27, r1, PT_R27; \
- lwi r28, r1, PT_R28; \
- lwi r29, r1, PT_R29; \
- lwi r30, r1, PT_R30; \
- lwi r31, r1, PT_R31; /* Restore cur task reg */
+ lwi r2, r1, PTO+PT_R2; /* restore SDA */ \
+ lwi r3, r1, PTO+PT_R3; \
+ lwi r4, r1, PTO+PT_R4; \
+ lwi r5, r1, PTO+PT_R5; \
+ lwi r6, r1, PTO+PT_R6; \
+ lwi r7, r1, PTO+PT_R7; \
+ lwi r8, r1, PTO+PT_R8; \
+ lwi r9, r1, PTO+PT_R9; \
+ lwi r10, r1, PTO+PT_R10; \
+ lwi r11, r1, PTO+PT_R11; /* restore clobbered regs after rval */\
+ lwi r12, r1, PTO+PT_R12; \
+ lwi r13, r1, PTO+PT_R13; /* restore SDA2 */ \
+ lwi r14, r1, PTO+PT_PC; /* RESTORE_LINK PC, before IRQ/trap */\
+ lwi r15, r1, PTO+PT_R15; /* restore LP */ \
+ lwi r16, r1, PTO+PT_R16; \
+ lwi r17, r1, PTO+PT_R17; \
+ lwi r18, r1, PTO+PT_R18; /* restore asm scratch reg */ \
+ lwi r19, r1, PTO+PT_R19; \
+ lwi r20, r1, PTO+PT_R20; \
+ lwi r21, r1, PTO+PT_R21; \
+ lwi r22, r1, PTO+PT_R22; \
+ lwi r23, r1, PTO+PT_R23; \
+ lwi r24, r1, PTO+PT_R24; \
+ lwi r25, r1, PTO+PT_R25; \
+ lwi r26, r1, PTO+PT_R26; \
+ lwi r27, r1, PTO+PT_R27; \
+ lwi r28, r1, PTO+PT_R28; \
+ lwi r29, r1, PTO+PT_R29; \
+ lwi r30, r1, PTO+PT_R30; \
+ lwi r31, r1, PTO+PT_R31; /* Restore cur task reg */
#define RESTORE_REGS \
- lwi r11, r1, PT_MSR; \
+ lwi r11, r1, PTO+PT_MSR; \
mts rmsr , r11; \
RESTORE_REGS_GP
#define RESTORE_REGS_RTBD \
- lwi r11, r1, PT_MSR; \
+ lwi r11, r1, PTO+PT_MSR; \
andni r11, r11, MSR_EIP; /* clear EIP */ \
ori r11, r11, MSR_EE | MSR_BIP; /* set EE and BIP */ \
mts rmsr , r11; \
@@ -265,11 +265,11 @@ syscall_debug_table:
lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); \
/* FIXME: I can add these two lines to one */ \
/* tophys(r1,r1); */ \
- /* addik r1, r1, -PT_SIZE; */ \
- addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \
+ /* addik r1, r1, -STATE_SAVE_SIZE; */ \
+ addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE; \
SAVE_REGS \
brid 2f; \
- swi r1, r1, PT_MODE; \
+ swi r1, r1, PTO+PT_MODE; \
1: /* User-mode state save. */ \
lwi r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\
tophys(r1,r1); \
@@ -277,12 +277,13 @@ syscall_debug_table:
/* MS these three instructions can be added to one */ \
/* addik r1, r1, THREAD_SIZE; */ \
/* tophys(r1,r1); */ \
- /* addik r1, r1, -PT_SIZE; */ \
- addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \
+ /* addik r1, r1, -STATE_SAVE_SIZE; */ \
+ addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR \
+ - CONFIG_KERNEL_START - STATE_SAVE_SIZE; \
SAVE_REGS \
lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP)); \
- swi r11, r1, PT_R1; /* Store user SP. */ \
- swi r0, r1, PT_MODE; /* Was in user-mode. */ \
+ swi r11, r1, PTO+PT_R1; /* Store user SP. */ \
+ swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */ \
/* MS: I am clearing UMS even in case when I come from kernel space */ \
clear_ums; \
2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
@@ -292,7 +293,7 @@ syscall_debug_table:
.extern cpuinfo
C_ENTRY(mb_flush_dcache):
- addik r1, r1, -PT_SIZE
+ addik r1, r1, -STATE_SAVE_SIZE
SAVE_REGS
addik r3, r0, cpuinfo
@@ -305,12 +306,12 @@ C_ENTRY(mb_flush_dcache):
addk r9, r9, r8
RESTORE_REGS
- addik r1, r1, PT_SIZE
+ addik r1, r1, STATE_SAVE_SIZE
rtsd r15, 8
nop
C_ENTRY(mb_invalidate_icache):
- addik r1, r1, -PT_SIZE
+ addik r1, r1, -STATE_SAVE_SIZE
SAVE_REGS
addik r3, r0, cpuinfo
@@ -323,7 +324,7 @@ C_ENTRY(mb_invalidate_icache):
addk r9, r9, r8
RESTORE_REGS
- addik r1, r1, PT_SIZE
+ addik r1, r1, STATE_SAVE_SIZE
rtsd r15, 8
nop
@@ -350,18 +351,18 @@ C_ENTRY(_user_exception):
addik r1, r1, THREAD_SIZE;
tophys(r1,r1);
- addik r1, r1, -PT_SIZE; /* Make room on the stack. */
+ addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */
SAVE_REGS
- swi r0, r1, PT_R3
- swi r0, r1, PT_R4
+ swi r0, r1, PTO+PT_R3
+ swi r0, r1, PTO+PT_R4
- swi r0, r1, PT_MODE; /* Was in user-mode. */
+ swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */
lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
- swi r11, r1, PT_R1; /* Store user SP. */
+ swi r11, r1, PTO+PT_R1; /* Store user SP. */
clear_ums;
2: lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
/* Save away the syscall number. */
- swi r12, r1, PT_R0;
+ swi r12, r1, PTO+PT_R0;
tovirt(r1,r1)
/* where the trap should return need -8 to adjust for rtsd r15, 8*/
@@ -380,18 +381,18 @@ C_ENTRY(_user_exception):
beqi r11, 4f
addik r3, r0, -ENOSYS
- swi r3, r1, PT_R3
+ swi r3, r1, PTO+PT_R3
brlid r15, do_syscall_trace_enter
- addik r5, r1, PT_R0
+ addik r5, r1, PTO+PT_R0
# do_syscall_trace_enter returns the new syscall nr.
addk r12, r0, r3
- lwi r5, r1, PT_R5;
- lwi r6, r1, PT_R6;
- lwi r7, r1, PT_R7;
- lwi r8, r1, PT_R8;
- lwi r9, r1, PT_R9;
- lwi r10, r1, PT_R10;
+ lwi r5, r1, PTO+PT_R5;
+ lwi r6, r1, PTO+PT_R6;
+ lwi r7, r1, PTO+PT_R7;
+ lwi r8, r1, PTO+PT_R8;
+ lwi r9, r1, PTO+PT_R9;
+ lwi r10, r1, PTO+PT_R10;
4:
/* Jump to the appropriate function for the system call number in r12
* (r12 is not preserved), or return an error if r12 is not valid.
@@ -432,10 +433,16 @@ C_ENTRY(_user_exception):
/* Entry point used to return from a syscall/trap */
/* We re-enable BIP bit before state restore */
C_ENTRY(ret_from_trap):
- swi r3, r1, PT_R3
- swi r4, r1, PT_R4
-
- lwi r11, r1, PT_MODE;
+ swi r3, r1, PTO+PT_R3
+ swi r4, r1, PTO+PT_R4
+/*
+ * Entry point for returns that must not store r3/r4 back into pt_regs,
+ * i.e. rt_sigreturn, which has already restored them from the signal
+ * context. Reached as "rtsd r15, 8" with r15 set to this label minus 8,
+ * so it stays correct if the number of stores above ever changes.
+ */
+ret_from_trap_no_rval:
+ lwi r11, r1, PTO+PT_MODE;
/* See if returning to kernel mode, if so, skip resched &c. */
bnei r11, 2f;
/* We're returning to user mode, so check for various conditions that
@@ -447,7 +454,7 @@ C_ENTRY(ret_from_trap):
beqi r11, 1f
brlid r15, do_syscall_trace_leave
- addik r5, r1, PT_R0
+ addik r5, r1, PTO+PT_R0
1:
/* We're returning to user mode, so check for various conditions that
* trigger rescheduling. */
@@ -466,7 +473,7 @@ C_ENTRY(ret_from_trap):
andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;
beqi r11, 4f; /* Signals to handle, handle them */
- addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */
+ addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */
bralid r15, do_notify_resume; /* Handle any signals */
add r6, r30, r0; /* Arg 2: int in_syscall */
add r30, r0, r0 /* no more restarts */
@@ -478,7 +485,11 @@ C_ENTRY(ret_from_trap):
VM_OFF;
tophys(r1,r1);
RESTORE_REGS_RTBD;
- addik r1, r1, PT_SIZE /* Clean up stack space. */
+ addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */
+ /*
+ * Deliberately PT_SIZE, not STATE_SAVE_SIZE: r1 is back at the stack
+ * top, so PTO cancels and the saved SP sits at PT_R1 - PT_SIZE.
+ */
lwi r1, r1, PT_R1 - PT_SIZE;/* Restore user stack pointer. */
bri 6f;
@@ -487,7 +498,7 @@ C_ENTRY(ret_from_trap):
VM_OFF;
tophys(r1,r1);
RESTORE_REGS_RTBD;
- addik r1, r1, PT_SIZE /* Clean up stack space. */
+ addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */
tovirt(r1,r1);
6:
TRAP_return: /* Make global symbol for debugging */
@@ -518,8 +529,17 @@ C_ENTRY(ret_from_kernel_thread):
C_ENTRY(sys_rt_sigreturn_wrapper):
addik r30, r0, 0 /* no restarts */
+ /*
+ * rt_sigreturn restores the full register set from the signal
+ * context, so it must skip the r3/r4 syscall-return stores at the
+ * head of ret_from_trap which would otherwise overwrite the
+ * just-restored r3/r4. Every C function returns with "rtsd r15, 8"
+ * -- the ABI return, where the 8 skips the caller's branch and its
+ * delay slot -- so bias r15 by -8 to land on ret_from_trap_no_rval.
+ */
+ addik r15, r0, ret_from_trap_no_rval - 8
brid sys_rt_sigreturn /* Do real work */
- addik r5, r1, 0; /* add user context as 1st arg */
+ addik r5, r1, PTO; /* add user context as 1st arg */
/*
* HW EXCEPTION rutine start
@@ -530,7 +550,7 @@ C_ENTRY(full_exception_trap):
addik r17, r17, -4
SAVE_STATE /* Save registers */
/* PC, before IRQ/trap - this is one instruction above */
- swi r17, r1, PT_PC;
+ swi r17, r1, PTO+PT_PC;
tovirt(r1,r1)
/* FIXME this can be store directly in PT_ESR reg.
* I tested it but there is a fault */
@@ -540,7 +560,7 @@ C_ENTRY(full_exception_trap):
mfs r7, rfsr; /* save FSR */
mts rfsr, r0; /* Clear sticky fsr */
rted r0, full_exception
- addik r5, r1, 0 /* parameter struct pt_regs * regs */
+ addik r5, r1, PTO /* parameter struct pt_regs * regs */
/*
* Unaligned data trap.
@@ -566,14 +586,14 @@ C_ENTRY(unaligned_data_trap):
lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
SAVE_STATE /* Save registers.*/
/* PC, before IRQ/trap - this is one instruction above */
- swi r17, r1, PT_PC;
+ swi r17, r1, PTO+PT_PC;
tovirt(r1,r1)
/* where the trap should return need -8 to adjust for rtsd r15, 8 */
addik r15, r0, ret_from_exc-8
mfs r3, resr /* ESR */
mfs r4, rear /* EAR */
rtbd r0, _unaligned_data_exception
- addik r7, r1, 0 /* parameter struct pt_regs * regs */
+ addik r7, r1, PTO /* parameter struct pt_regs * regs */
/*
* Page fault traps.
@@ -596,30 +616,30 @@ C_ENTRY(unaligned_data_trap):
C_ENTRY(page_fault_data_trap):
SAVE_STATE /* Save registers.*/
/* PC, before IRQ/trap - this is one instruction above */
- swi r17, r1, PT_PC;
+ swi r17, r1, PTO+PT_PC;
tovirt(r1,r1)
/* where the trap should return need -8 to adjust for rtsd r15, 8 */
addik r15, r0, ret_from_exc-8
mfs r6, rear /* parameter unsigned long address */
mfs r7, resr /* parameter unsigned long error_code */
rted r0, do_page_fault
- addik r5, r1, 0 /* parameter struct pt_regs * regs */
+ addik r5, r1, PTO /* parameter struct pt_regs * regs */
C_ENTRY(page_fault_instr_trap):
SAVE_STATE /* Save registers.*/
/* PC, before IRQ/trap - this is one instruction above */
- swi r17, r1, PT_PC;
+ swi r17, r1, PTO+PT_PC;
tovirt(r1,r1)
/* where the trap should return need -8 to adjust for rtsd r15, 8 */
addik r15, r0, ret_from_exc-8
mfs r6, rear /* parameter unsigned long address */
ori r7, r0, 0 /* parameter unsigned long error_code */
rted r0, do_page_fault
- addik r5, r1, 0 /* parameter struct pt_regs * regs */
+ addik r5, r1, PTO /* parameter struct pt_regs * regs */
/* Entry point used to return from an exception. */
C_ENTRY(ret_from_exc):
- lwi r11, r1, PT_MODE;
+ lwi r11, r1, PTO+PT_MODE;
bnei r11, 2f; /* See if returning to kernel mode, */
/* ... if so, skip resched &c. */
@@ -651,7 +671,7 @@ C_ENTRY(ret_from_exc):
* complete register state. Here we save anything not saved by
* the normal entry sequence, so that it may be safely restored
* (in a possibly modified form) after do_notify_resume returns. */
- addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */
+ addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */
bralid r15, do_notify_resume; /* Handle any signals */
addi r6, r0, 0; /* Arg 2: int in_syscall */
bri 1b
@@ -663,7 +683,7 @@ C_ENTRY(ret_from_exc):
tophys(r1,r1);
RESTORE_REGS_RTBD;
- addik r1, r1, PT_SIZE /* Clean up stack space. */
+ addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */
lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer. */
bri 6f;
@@ -672,7 +692,7 @@ C_ENTRY(ret_from_exc):
VM_OFF;
tophys(r1,r1);
RESTORE_REGS_RTBD;
- addik r1, r1, PT_SIZE /* Clean up stack space. */
+ addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space. */
tovirt(r1,r1);
6:
@@ -705,10 +725,10 @@ C_ENTRY(_interrupt):
tophys(r1,r1); /* MS: I have in r1 physical address where stack is */
/* save registers */
/* MS: Make room on the stack -> activation record */
- addik r1, r1, -PT_SIZE;
+ addik r1, r1, -STATE_SAVE_SIZE;
SAVE_REGS
brid 2f;
- swi r1, r1, PT_MODE; /* 0 - user mode, 1 - kernel mode */
+ swi r1, r1, PTO+PT_MODE; /* 0 - user mode, 1 - kernel mode */
1:
/* User-mode state save. */
/* MS: get the saved current */
@@ -718,23 +738,23 @@ C_ENTRY(_interrupt):
addik r1, r1, THREAD_SIZE;
tophys(r1,r1);
/* save registers */
- addik r1, r1, -PT_SIZE;
+ addik r1, r1, -STATE_SAVE_SIZE;
SAVE_REGS
/* calculate mode */
- swi r0, r1, PT_MODE;
+ swi r0, r1, PTO+PT_MODE;
lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
- swi r11, r1, PT_R1;
+ swi r11, r1, PTO+PT_R1;
clear_ums;
2:
lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
tovirt(r1,r1)
addik r15, r0, irq_call;
irq_call:rtbd r0, do_IRQ;
- addik r5, r1, 0;
+ addik r5, r1, PTO;
/* MS: we are in virtual mode */
ret_from_irq:
- lwi r11, r1, PT_MODE;
+ lwi r11, r1, PTO+PT_MODE;
bnei r11, 2f;
1:
@@ -750,7 +770,7 @@ ret_from_irq:
5: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;
beqid r11, no_intr_resched
/* Handle a signal return; Pending signals should be in r18. */
- addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */
+ addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */
bralid r15, do_notify_resume; /* Handle any signals */
addi r6, r0, 0; /* Arg 2: int in_syscall */
bri 1b
@@ -763,7 +783,7 @@ no_intr_resched:
VM_OFF;
tophys(r1,r1);
RESTORE_REGS
- addik r1, r1, PT_SIZE /* MS: Clean up stack space. */
+ addik r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */
lwi r1, r1, PT_R1 - PT_SIZE;
bri 6f;
/* MS: Return to kernel state. */
@@ -786,7 +806,7 @@ restore:
VM_OFF /* MS: turn off MMU */
tophys(r1,r1)
RESTORE_REGS
- addik r1, r1, PT_SIZE /* MS: Clean up stack space. */
+ addik r1, r1, STATE_SAVE_SIZE /* MS: Clean up stack space. */
tovirt(r1,r1);
6:
IRQ_return: /* MS: Make global symbol for debugging */
@@ -805,29 +825,29 @@ C_ENTRY(_xtmr_manager_reset):
lwi r1, r0, xmb_manager_stackpointer
/* Restore MSR */
- lwi r2, r1, PT_MSR
+ lwi r2, r1, PTO+PT_MSR
mts rmsr, r2
bri 4
/* restore Special purpose registers */
- lwi r2, r1, PT_PID
+ lwi r2, r1, PTO+PT_PID
mts rpid, r2
- lwi r2, r1, PT_TLBI
+ lwi r2, r1, PTO+PT_TLBI
mts rtlbx, r2
- lwi r2, r1, PT_ZPR
+ lwi r2, r1, PTO+PT_ZPR
mts rzpr, r2
#if CONFIG_XILINX_MICROBLAZE0_USE_FPU
- lwi r2, r1, PT_FSR
+ lwi r2, r1, PTO+PT_FSR
mts rfsr, r2
#endif
/* restore all the tlb's */
addik r3, r0, TOPHYS(tlb_skip)
- addik r6, r0, PT_TLBL0
- addik r7, r0, PT_TLBH0
+ addik r6, r0, PTO+PT_TLBL0
+ addik r7, r0, PTO+PT_TLBH0
restore_tlb:
add r6, r6, r1
add r7, r7, r1
@@ -853,9 +873,9 @@ ret_from_reset:
VM_OFF
/* MS: Restore all regs */
RESTORE_REGS
- lwi r14, r1, PT_R14
- lwi r16, r1, PT_PC
- addik r1, r1, PT_SIZE + 36
+ lwi r14, r1, PTO+PT_R14
+ lwi r16, r1, PTO+PT_PC
+ addik r1, r1, STATE_SAVE_SIZE + 36
rtbd r16, 0
nop
@@ -870,11 +890,11 @@ C_ENTRY(_xmb_manager_break):
* Reserve memory in the stack for context store/restore
* (which includes memory for storing tlbs (max two tlbs))
*/
- addik r1, r1, -PT_SIZE - 36
+ addik r1, r1, -STATE_SAVE_SIZE - 36
swi r1, r0, xmb_manager_stackpointer
SAVE_REGS
- swi r14, r1, PT_R14 /* rewrite saved R14 value */
- swi r16, r1, PT_PC; /* PC and r16 are the same */
+ swi r14, r1, PTO+PT_R14 /* rewrite saved R14 value */
+ swi r16, r1, PTO+PT_PC; /* PC and r16 are the same */
lwi r6, r0, TOPHYS(xmb_manager_baseaddr)
lwi r7, r0, TOPHYS(xmb_manager_crval)
@@ -888,25 +908,25 @@ C_ENTRY(_xmb_manager_break):
/* Save the special purpose registers */
mfs r2, rpid
- swi r2, r1, PT_PID
+ swi r2, r1, PTO+PT_PID
mfs r2, rtlbx
- swi r2, r1, PT_TLBI
+ swi r2, r1, PTO+PT_TLBI
mfs r2, rzpr
- swi r2, r1, PT_ZPR
+ swi r2, r1, PTO+PT_ZPR
#if CONFIG_XILINX_MICROBLAZE0_USE_FPU
mfs r2, rfsr
- swi r2, r1, PT_FSR
+ swi r2, r1, PTO+PT_FSR
#endif
mfs r2, rmsr
- swi r2, r1, PT_MSR
+ swi r2, r1, PTO+PT_MSR
/* Save all the tlb's */
addik r3, r0, TOPHYS(tlb_skip)
- addik r6, r0, PT_TLBL0
- addik r7, r0, PT_TLBH0
+ addik r6, r0, PTO+PT_TLBL0
+ addik r7, r0, PTO+PT_TLBH0
save_tlb:
add r6, r6, r1
add r7, r7, r1
@@ -943,7 +963,7 @@ ret_from_break:
mbar 1
mbar 2
bri 4
- suspend
+ mbar 24 /* suspend */
nop
#endif
@@ -963,28 +983,28 @@ C_ENTRY(_debug_exception):
lwi r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* Reload kernel stack-ptr*/
/* BIP bit is set on entry, no interrupts can occur */
- addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE;
+ addik r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - STATE_SAVE_SIZE;
SAVE_REGS;
/* save all regs to pt_reg structure */
- swi r0, r1, PT_R0; /* R0 must be saved too */
- swi r14, r1, PT_R14 /* rewrite saved R14 value */
- swi r16, r1, PT_PC; /* PC and r16 are the same */
+ swi r0, r1, PTO+PT_R0; /* R0 must be saved too */
+ swi r14, r1, PTO+PT_R14 /* rewrite saved R14 value */
+ swi r16, r1, PTO+PT_PC; /* PC and r16 are the same */
/* save special purpose registers to pt_regs */
mfs r11, rear;
- swi r11, r1, PT_EAR;
+ swi r11, r1, PTO+PT_EAR;
mfs r11, resr;
- swi r11, r1, PT_ESR;
+ swi r11, r1, PTO+PT_ESR;
mfs r11, rfsr;
- swi r11, r1, PT_FSR;
+ swi r11, r1, PTO+PT_FSR;
/* stack pointer is in physical address at it is decrease
- * by PT_SIZE but we need to get correct R1 value */
- addik r11, r1, CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR + PT_SIZE;
- swi r11, r1, PT_R1
+ * by STATE_SAVE_SIZE but we need to get correct R1 value */
+ addik r11, r1, CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR + STATE_SAVE_SIZE;
+ swi r11, r1, PTO+PT_R1
/* MS: r31 - current pointer isn't changed */
tovirt(r1,r1)
#ifdef CONFIG_KGDB
- addi r5, r1, 0 /* pass pt_reg address as the first arg */
+ addi r5, r1, PTO /* pass pt_reg address as the first arg */
addik r15, r0, dbtrap_call; /* return address */
rtbd r0, microblaze_kgdb_break
nop;
@@ -1000,16 +1020,16 @@ C_ENTRY(_debug_exception):
addik r1, r1, THREAD_SIZE; /* calculate kernel stack pointer */
tophys(r1,r1);
- addik r1, r1, -PT_SIZE; /* Make room on the stack. */
+ addik r1, r1, -STATE_SAVE_SIZE; /* Make room on the stack. */
SAVE_REGS;
- swi r16, r1, PT_PC; /* Save LP */
- swi r0, r1, PT_MODE; /* Was in user-mode. */
+ swi r16, r1, PTO+PT_PC; /* Save LP */
+ swi r0, r1, PTO+PT_MODE; /* Was in user-mode. */
lwi r11, r0, TOPHYS(PER_CPU(ENTRY_SP));
- swi r11, r1, PT_R1; /* Store user SP. */
+ swi r11, r1, PTO+PT_R1; /* Store user SP. */
lwi CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
tovirt(r1,r1)
set_vms;
- addik r5, r1, 0;
+ addik r5, r1, PTO;
addik r15, r0, dbtrap_call;
dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */
rtbd r0, sw_exception
@@ -1017,7 +1037,7 @@ dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */
/* MS: The first instruction for the second part of the gdb/kgdb */
set_bip; /* Ints masked for state restore */
- lwi r11, r1, PT_MODE;
+ lwi r11, r1, PTO+PT_MODE;
bnei r11, 2f;
/* MS: Return to user space - gdb */
1:
@@ -1036,7 +1056,7 @@ dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */
5: andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;
beqi r11, 4f; /* Signals to handle, handle them */
- addik r5, r1, 0; /* Arg 1: struct pt_regs *regs */
+ addik r5, r1, PTO; /* Arg 1: struct pt_regs *regs */
bralid r15, do_notify_resume; /* Handle any signals */
addi r6, r0, 0; /* Arg 2: int in_syscall */
bri 1b
@@ -1047,7 +1067,7 @@ dbtrap_call: /* Return point for kernel/user entry + 8 because of rtsd r15, 8 */
tophys(r1,r1);
/* MS: Restore all regs */
RESTORE_REGS_RTBD
- addik r1, r1, PT_SIZE /* Clean up stack space */
+ addik r1, r1, STATE_SAVE_SIZE /* Clean up stack space */
lwi r1, r1, PT_R1 - PT_SIZE; /* Restore user stack pointer */
DBTRAP_return_user: /* MS: Make global symbol for debugging */
rtbd r16, 0; /* MS: Instructions to return from a debug trap */
@@ -1058,9 +1078,9 @@ DBTRAP_return_user: /* MS: Make global symbol for debugging */
tophys(r1,r1);
/* MS: Restore all regs */
RESTORE_REGS_RTBD
- lwi r14, r1, PT_R14;
- lwi r16, r1, PT_PC;
- addik r1, r1, PT_SIZE; /* MS: Clean up stack space */
+ lwi r14, r1, PTO+PT_R14;
+ lwi r16, r1, PTO+PT_PC;
+ addik r1, r1, STATE_SAVE_SIZE; /* MS: Clean up stack space */
tovirt(r1,r1);
DBTRAP_return_kernel: /* MS: Make global symbol for debugging */
rtbd r16, 0; /* MS: Instructions to return from a debug trap */
@@ -1158,7 +1178,7 @@ ENTRY(_switch_to)
.ent xmb_inject_err
.type xmb_inject_err, @function
xmb_inject_err:
- addik r1, r1, -PT_SIZE
+ addik r1, r1, -STATE_SAVE_SIZE
SAVE_REGS
/* Switch to real mode */
@@ -1182,7 +1202,7 @@ xmb_inject_err:
nop;
1:
RESTORE_REGS
- addik r1, r1, PT_SIZE
+ addik r1, r1, STATE_SAVE_SIZE
rtsd r15, 8;
nop;
.end xmb_inject_err
diff --git a/arch/microblaze/kernel/hw_exception_handler.S b/arch/microblaze/kernel/hw_exception_handler.S
index 07ea23965f81..4693916be526 100644
--- a/arch/microblaze/kernel/hw_exception_handler.S
+++ b/arch/microblaze/kernel/hw_exception_handler.S
@@ -313,6 +313,11 @@ _MB_HW_ExceptionVectorTable:
.align 4
.ent _hw_exception_handler
_hw_exception_handler:
+ /*
+ * This handler saves into the static pt_pool_space buffer, not into a
+ * stack frame, so the plain PT_* offsets are right here -- do not add
+ * the PTO of entry.S's frames.
+ */
swi r1, r0, TOPHYS(pt_pool_space + PT_R1); /* GET_SP */
/* Save date to kernel memory. Here is the problem
* when you came from user space */
diff --git a/arch/microblaze/kernel/process.c b/arch/microblaze/kernel/process.c
index 6cbf642d7b80..090e1697ea7a 100644
--- a/arch/microblaze/kernel/process.c
+++ b/arch/microblaze/kernel/process.c
@@ -19,6 +19,7 @@
#include <linux/bitops.h>
#include <linux/ptrace.h>
#include <asm/cacheflush.h>
+#include <asm/entry.h>
void show_regs(struct pt_regs *regs)
{
@@ -65,7 +66,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
* the registers. That's OK for a brand new thread.*/
memset(childregs, 0, sizeof(struct pt_regs));
memset(&ti->cpu_context, 0, sizeof(struct cpu_context));
- ti->cpu_context.r1 = (unsigned long)childregs;
+ ti->cpu_context.r1 = (unsigned long)childregs - PTO;
ti->cpu_context.r20 = (unsigned long)args->fn;
ti->cpu_context.r19 = (unsigned long)args->fn_arg;
childregs->pt_mode = 1;
@@ -79,7 +80,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
childregs->r1 = usp;
memset(&ti->cpu_context, 0, sizeof(struct cpu_context));
- ti->cpu_context.r1 = (unsigned long)childregs;
+ ti->cpu_context.r1 = (unsigned long)childregs - PTO;
childregs->msr |= MSR_UMS;
/* we should consider the fact that childregs is a copy of the parent
diff --git a/arch/microblaze/kernel/signal.c b/arch/microblaze/kernel/signal.c
index c78a0ff48066..6bbc16f98d5e 100644
--- a/arch/microblaze/kernel/signal.c
+++ b/arch/microblaze/kernel/signal.c
@@ -33,6 +33,7 @@
#include <linux/linkage.h>
#include <linux/resume_user_mode.h>
#include <asm/entry.h>
+#include <asm/registers.h>
#include <asm/ucontext.h>
#include <linux/uaccess.h>
#include <linux/syscalls.h>
@@ -49,6 +50,14 @@ struct sigframe {
};
struct rt_sigframe {
+ /*
+ * Home area for the handler's register arguments: the MicroBlaze
+ * ABI reserves [r1+0] for the return address and lets the callee
+ * store r5..r10 at [r1+4]..[r1+24], and r1 points at this frame
+ * when the handler is entered. Without the gap those stores
+ * corrupt frame->info.
+ */
+ unsigned long abi_gap[7];
struct siginfo info;
struct ucontext uc;
unsigned long tramp[2]; /* signal trampoline */
@@ -73,6 +82,22 @@ static int restore_sigcontext(struct pt_regs *regs,
COPY(pc); COPY(ear); COPY(esr); COPY(fsr);
#undef COPY
+ /*
+ * The frame is user-writable, so restore only the user-writable
+ * status flags (carry) and keep the kernel-controlled MSR bits
+ * (UMS/VMS/IE/EE/...) from regs->msr: rtbd derives the resumed mode
+ * from UMS/VMS, so a verbatim restore would hand userspace the
+ * privileged return state. Same idea as x86 masking the restored
+ * EFLAGS to FIX_EFLAGS.
+ */
+ {
+ unsigned long msr;
+
+ err |= __get_user(msr, &sc->regs.msr);
+ regs->msr = (regs->msr & ~(MSR_C | MSR_CC)) |
+ (msr & (MSR_C | MSR_CC));
+ }
+
*rval_p = regs->r3;
return err;
@@ -132,6 +157,7 @@ setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
COPY(r26); COPY(r27); COPY(r28); COPY(r29);
COPY(r30); COPY(r31);
COPY(pc); COPY(ear); COPY(esr); COPY(fsr);
+ COPY(msr); /* restore_sigcontext() accepts only carry state */
#undef COPY
err |= __put_user(mask, &sc->oldmask);
diff --git a/arch/mips/mm/mmap.c b/arch/mips/mm/mmap.c
index 5d2a1225785b..119edaa928f3 100644
--- a/arch/mips/mm/mmap.c
+++ b/arch/mips/mm/mmap.c
@@ -9,6 +9,7 @@
#include <linux/compiler.h>
#include <linux/elf-randomize.h>
#include <linux/errno.h>
+#include <linux/hugetlb.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/export.h>
@@ -72,8 +73,11 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp,
}
info.length = len;
- info.align_mask = do_color_align ? (PAGE_MASK & shm_align_mask) : 0;
info.align_offset = pgoff << PAGE_SHIFT;
+ if (filp && is_file_hugepages(filp))
+ info.align_mask = huge_page_mask_align(filp);
+ else
+ info.align_mask = do_color_align ? (PAGE_MASK & shm_align_mask) : 0;
if (dir == DOWN) {
info.flags = VM_UNMAPPED_AREA_TOPDOWN;
diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c
index 24fe85fa169d..78bafa317fa6 100644
--- a/arch/mips/mm/tlb-r4k.c
+++ b/arch/mips/mm/tlb-r4k.c
@@ -332,6 +332,19 @@ void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
/* this could be a huge page */
if (pmd_leaf(*pmdp)) {
unsigned long lo;
+
+ /*
+ * The probe above only covers the 8K pair at @address, and
+ * a huge mapping is installed with the huge-aligned address
+ * while the refill that started the fault left a 4K entry
+ * for the faulting page elsewhere in the range. Writing the
+ * huge entry to a random index would leave two entries
+ * matching the faulting address; leave it to the refill and
+ * TLBL/TLBS handlers, which probe the faulting address.
+ */
+ if (idx < 0)
+ goto out;
+
write_c0_pagemask(PM_HUGE_MASK);
ptep = (pte_t *)pmdp;
lo = pte_to_entrylo(pte_val(*ptep));
@@ -339,10 +352,7 @@ void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
write_c0_entrylo1(lo + (HPAGE_SIZE >> 7));
mtc0_tlbw_hazard();
- if (idx < 0)
- tlb_write_random();
- else
- tlb_write_indexed();
+ tlb_write_indexed();
tlbw_use_hazard();
write_c0_pagemask(PM_DEFAULT_MASK);
} else
@@ -380,6 +390,9 @@ void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
tlb_write_indexed();
}
tlbw_use_hazard();
+#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
+out:
+#endif
htw_start();
flush_micro_tlb_vm(vma);
diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c
index b1667ed05f98..719f7e513c5a 100644
--- a/arch/powerpc/platforms/pseries/plpks.c
+++ b/arch/powerpc/platforms/pseries/plpks.c
@@ -434,6 +434,9 @@ int plpks_signed_update_var(struct plpks_var *var, u64 flags)
u64 continuetoken = 0;
u64 timeout = 0;
+ if (!var)
+ return -EINVAL;
+
if (!var->data || var->datalen <= 0 || var->namelen > PLPKS_MAX_NAME_SIZE)
return -EINVAL;
@@ -476,7 +479,7 @@ int plpks_signed_update_var(struct plpks_var *var, u64 flags)
kfree(label);
out:
- kfree(auth);
+ kfree_sensitive(auth);
return rc;
}
@@ -515,7 +518,7 @@ int plpks_write_var(struct plpks_var var)
rc = pseries_status_to_err(rc);
kfree(label);
out:
- kfree(auth);
+ kfree_sensitive(auth);
return rc;
}
@@ -549,7 +552,7 @@ int plpks_remove_var(char *component, u8 varos, struct plpks_var_name vname)
rc = pseries_status_to_err(rc);
kfree(label);
out:
- kfree(auth);
+ kfree_sensitive(auth);
return rc;
}
@@ -562,6 +565,9 @@ static int plpks_read_var(u8 consumer, struct plpks_var *var)
u8 *output;
int rc;
+ if (!var)
+ return -EINVAL;
+
if (var->namelen > PLPKS_MAX_NAME_SIZE)
return -EINVAL;
@@ -593,28 +599,27 @@ static int plpks_read_var(u8 consumer, struct plpks_var *var)
virt_to_phys(var->name), var->namelen, virt_to_phys(output),
maxobjsize);
-
if (rc != H_SUCCESS) {
rc = pseries_status_to_err(rc);
- goto out_free_output;
+ if (rc != -EPERM || !retbuf[1])
+ goto out_free_output;
+ goto out_copy_policy;
}
if (!var->data || var->datalen > retbuf[0])
var->datalen = retbuf[0];
- var->policy = retbuf[1];
-
if (var->data)
memcpy(var->data, output, var->datalen);
- rc = 0;
-
+out_copy_policy:
+ var->policy = retbuf[1];
out_free_output:
- kfree(output);
+ kfree_sensitive(output);
out_free_label:
kfree(label);
out_free_auth:
- kfree(auth);
+ kfree_sensitive(auth);
return rc;
}
diff --git a/arch/powerpc/platforms/pseries/plpks_sed_ops.c b/arch/powerpc/platforms/pseries/plpks_sed_ops.c
index 7c873c9589ef..ac3ec2ca7fbe 100644
--- a/arch/powerpc/platforms/pseries/plpks_sed_ops.c
+++ b/arch/powerpc/platforms/pseries/plpks_sed_ops.c
@@ -84,14 +84,16 @@ int sed_read_key(char *keyname, char *key, u_int *keylen)
ret = plpks_read_os_var(&var);
if (ret != 0)
- return ret;
+ goto out;
len = min_t(u16, be32_to_cpu(data.key_len), var.datalen);
memcpy(key, data.key, len);
key[len] = '\0';
*keylen = len;
- return 0;
+out:
+ memzero_explicit(&data, sizeof(data));
+ return ret;
}
/*
@@ -102,6 +104,7 @@ int sed_write_key(char *keyname, char *key, u_int keylen)
struct plpks_var var;
struct plpks_sed_object_data data;
struct plpks_var_name vname;
+ int ret;
plpks_init_var(&var, keyname);
@@ -127,5 +130,8 @@ int sed_write_key(char *keyname, char *key, u_int keylen)
vname.name = var.name;
plpks_remove_var(var.component, var.os, vname);
- return plpks_write_var(var);
+ ret = plpks_write_var(var);
+ memzero_explicit(&data, sizeof(data));
+
+ return ret;
}
diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
index 9b2e967c9fe0..420d402336d0 100644
--- a/arch/riscv/include/asm/pgtable.h
+++ b/arch/riscv/include/asm/pgtable.h
@@ -833,7 +833,14 @@ static inline bool pte_user_accessible_page(pte_t pte)
static inline bool pmd_user_accessible_page(pmd_t pmd)
{
- return pmd_leaf(pmd) && pmd_user(pmd);
+ /*
+ * page_table_check() must ignore THP split invalidation entries created by
+ * pmd_mkinvalid(). These retain _PAGE_LEAF so pmd_present()/pmd_leaf() stay
+ * true during the split, but they no longer describe a user-accessible
+ * mapping once both _PAGE_PRESENT and _PAGE_PROT_NONE are cleared.
+ */
+ return (pmd_val(pmd) & (_PAGE_PRESENT | _PAGE_PROT_NONE)) &&
+ (pmd_val(pmd) & _PAGE_LEAF) && pmd_user(pmd);
}
static inline bool pud_user_accessible_page(pud_t pud)
diff --git a/arch/riscv/kvm/vcpu_sbi_replace.c b/arch/riscv/kvm/vcpu_sbi_replace.c
index b490ed1428a6..df3818fec0bf 100644
--- a/arch/riscv/kvm/vcpu_sbi_replace.c
+++ b/arch/riscv/kvm/vcpu_sbi_replace.c
@@ -64,8 +64,17 @@ static int kvm_sbi_ext_ipi_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
if (tmp->vcpu_id < hbase)
continue;
hart_bit = tmp->vcpu_id - hbase;
+ /*
+ * kvm_for_each_vcpu() walks kvm->vcpus[] by
+ * vcpu_idx, i.e. the creation order, which has
+ * nothing to do with the vcpu_id (hart id) space
+ * that the SBI IPI operates on. vcpu_ids need not
+ * increase along the iteration, so harts outside
+ * the hart_mask window must be skipped instead of
+ * aborting the loop.
+ */
if (hart_bit >= __riscv_xlen)
- goto done;
+ continue;
if (!(hmask & (1UL << hart_bit)))
continue;
}
@@ -76,7 +85,6 @@ static int kvm_sbi_ext_ipi_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
kvm_riscv_vcpu_pmu_incr_fw(tmp, SBI_PMU_FW_IPI_RCVD);
}
-done:
if (hbase != -1UL && (hmask ^ sentmask))
retdata->err_val = SBI_ERR_INVALID_PARAM;
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index b60b4a82ea20..de7da218835e 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -502,6 +502,7 @@
#define X86_FEATURE_IBPB_EXIT_TO_USER (21*32+14) /* Use IBPB on exit-to-userspace, see VMSCAPE bug */
#define X86_FEATURE_ABMC (21*32+15) /* Assignable Bandwidth Monitoring Counters */
#define X86_FEATURE_MSR_IMM (21*32+16) /* MSR immediate form instructions */
+#define X86_FEATURE_TSA_FILL_NO (21*32+17) /* AMD CPU not vulnerable to TSA-FILL */
/*
* BUG word(s)
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index c85c50019523..a9daddd84ad6 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -20,6 +20,14 @@
#include <asm/trapnr.h>
#include <asm/vmxfeatures.h>
+/*
+ * UNDEFINED is a KVM-defined value used to indicate that the basic exit reason
+ * is undefined/invalid, e.g. on VM-Fail or if TDX never attempted VM-Enter.
+ * The value itself is arbitrary, it just needs to not collide with a real exit
+ * reason.
+ */
+#define EXIT_REASON_UNDEFINED 0xdead
+
#define VMCS_CONTROL_BIT(x) BIT(VMX_FEATURE_##x & 0x1f)
/*
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index ca4265a1f4bb..bd274c2b4c6a 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -413,9 +413,14 @@ static void tsa_init(struct cpuinfo_x86 *c)
else
pr_debug("%s: current revision: 0x%x\n", __func__, c->microcode);
} else {
- setup_force_cpu_cap(X86_FEATURE_TSA_SQ_NO);
- setup_force_cpu_cap(X86_FEATURE_TSA_L1_NO);
+ if (!cpu_has(c, X86_FEATURE_ZEN5)) {
+ setup_force_cpu_cap(X86_FEATURE_TSA_SQ_NO);
+ setup_force_cpu_cap(X86_FEATURE_TSA_L1_NO);
+ }
}
+
+ if (!cpu_has(c, X86_FEATURE_ZEN5))
+ setup_force_cpu_cap(X86_FEATURE_TSA_FILL_NO);
}
static void bsp_init_amd(struct cpuinfo_x86 *c)
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index a39d97eee8bd..90d0488d54c5 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -2103,7 +2103,7 @@ early_param("spectre_v2", spectre_v2_parse_cmdline);
static enum spectre_v2_mitigation __init spectre_v2_select_retpoline(void)
{
if (!IS_ENABLED(CONFIG_MITIGATION_RETPOLINE)) {
- pr_err("Kernel not compiled with retpoline; no mitigation available!");
+ pr_err("Kernel not compiled with retpoline");
return SPECTRE_V2_NONE;
}
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 3ffeadad0244..b0de3aabcd79 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -1299,7 +1299,7 @@ static const struct x86_cpu_id cpu_vuln_blacklist[] __initconst = {
VULNBL_AMD(0x17, RETBLEED | SMT_RSB | SRSO | VMSCAPE),
VULNBL_HYGON(0x18, RETBLEED | SMT_RSB | SRSO | VMSCAPE),
VULNBL_AMD(0x19, SRSO | TSA | VMSCAPE),
- VULNBL_AMD(0x1a, SRSO | VMSCAPE),
+ VULNBL_AMD(0x1a, SRSO | TSA | VMSCAPE),
{}
};
@@ -1564,7 +1564,8 @@ static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c)
if (c->x86_vendor == X86_VENDOR_AMD) {
if (!cpu_has(c, X86_FEATURE_TSA_SQ_NO) ||
- !cpu_has(c, X86_FEATURE_TSA_L1_NO)) {
+ !cpu_has(c, X86_FEATURE_TSA_L1_NO) ||
+ !cpu_has(c, X86_FEATURE_TSA_FILL_NO)) {
if (cpu_matches(cpu_vuln_blacklist, TSA) ||
/* Enable bug on Zen guests to allow for live migration. */
(cpu_has(c, X86_FEATURE_HYPERVISOR) && cpu_has(c, X86_FEATURE_ZEN)))
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index c4bc3c259f3f..083403886da2 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -55,6 +55,7 @@ static const struct cpuid_bit cpuid_bits[] = {
{ X86_FEATURE_ABMC, CPUID_EBX, 5, 0x80000020, 0 },
{ X86_FEATURE_TSA_SQ_NO, CPUID_ECX, 1, 0x80000021, 0 },
{ X86_FEATURE_TSA_L1_NO, CPUID_ECX, 2, 0x80000021, 0 },
+ { X86_FEATURE_TSA_FILL_NO, CPUID_ECX, 7, 0x80000021, 0 },
{ X86_FEATURE_AMD_WORKLOAD_CLASS, CPUID_EAX, 22, 0x80000021, 0 },
{ X86_FEATURE_BTB_CTX_ISOLATION, CPUID_ECX, 8, 0x80000021, 0 },
{ X86_FEATURE_PERFMON_V2, CPUID_EAX, 0, 0x80000022, 0 },
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 913ffb995279..a35ca056fe3e 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1231,6 +1231,7 @@ void kvm_set_cpu_caps(void)
kvm_cpu_cap_init(CPUID_8000_0021_ECX,
SYNTHESIZED_F(TSA_SQ_NO),
SYNTHESIZED_F(TSA_L1_NO),
+ SYNTHESIZED_F(TSA_FILL_NO),
);
kvm_cpu_cap_init(CPUID_8000_0022_EAX,
diff --git a/arch/x86/kvm/reverse_cpuid.h b/arch/x86/kvm/reverse_cpuid.h
index 743ab25ba787..dcd8ed7c5ed4 100644
--- a/arch/x86/kvm/reverse_cpuid.h
+++ b/arch/x86/kvm/reverse_cpuid.h
@@ -58,6 +58,7 @@
/* CPUID level 0x80000021 (ECX) */
#define KVM_X86_FEATURE_TSA_SQ_NO KVM_X86_FEATURE(CPUID_8000_0021_ECX, 1)
#define KVM_X86_FEATURE_TSA_L1_NO KVM_X86_FEATURE(CPUID_8000_0021_ECX, 2)
+#define KVM_X86_FEATURE_TSA_FILL_NO KVM_X86_FEATURE(CPUID_8000_0021_ECX, 7)
struct cpuid_reg {
u32 function;
@@ -133,6 +134,7 @@ static __always_inline u32 __feature_translate(int x86_feature)
KVM_X86_TRANSLATE_FEATURE(TSA_SQ_NO);
KVM_X86_TRANSLATE_FEATURE(TSA_L1_NO);
KVM_X86_TRANSLATE_FEATURE(MSR_IMM);
+ KVM_X86_TRANSLATE_FEATURE(TSA_FILL_NO);
default:
return x86_feature;
}
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 8da96a6c6177..ced20415a14f 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -6151,6 +6151,8 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = {
static const int kvm_vmx_max_exit_handlers =
ARRAY_SIZE(kvm_vmx_exit_handlers);
+static_assert(EXIT_REASON_UNDEFINED >= ARRAY_SIZE(kvm_vmx_exit_handlers));
+
void vmx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason,
u64 *info1, u64 *info2, u32 *intr_info, u32 *error_code)
{
@@ -7333,7 +7335,7 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu,
vmx_enable_fb_clear(vmx);
if (unlikely(vmx->fail)) {
- vmx->vt.exit_reason.full = 0xdead;
+ vmx->vt.exit_reason.full = EXIT_REASON_UNDEFINED;
goto out;
}
diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c
index 12b7de5f0957..9fc18a091311 100644
--- a/arch/x86/mm/pat/set_memory.c
+++ b/arch/x86/mm/pat/set_memory.c
@@ -718,6 +718,21 @@ static inline pgprot_t verify_rwx(pgprot_t old, pgprot_t new, unsigned long star
return new;
end = start + npg * PAGE_SIZE - 1;
+
+ /*
+ * If the kernel text is still RWX, gently complain, this could be a
+ * false positive.
+ * Once debug_checkwx() becomes mandatory for CONFIG_STRICT_KERNEL_RWX,
+ * the warning can be removed completely.
+ */
+ if (!kernel_set_to_readonly) {
+ pr_warn_once("CPA detected W^X violation: %016llx -> %016llx range: 0x%016lx - 0x%016lx PFN %lx\n",
+ (unsigned long long)pgprot_val(old),
+ (unsigned long long)pgprot_val(new),
+ start, end, pfn);
+ return new;
+ }
+
WARN_ONCE(1, "CPA detected W^X violation: %016llx -> %016llx range: 0x%016lx - 0x%016lx PFN %lx\n",
(unsigned long long)pgprot_val(old),
(unsigned long long)pgprot_val(new),
diff --git a/block/blk-iocost.c b/block/blk-iocost.c
index d0b13580c6b6..5c7cdb493768 100644
--- a/block/blk-iocost.c
+++ b/block/blk-iocost.c
@@ -1965,6 +1965,7 @@ static void transfer_surpluses(struct list_head *surpluses, struct ioc_now *now)
pr_cont(": active=%u donating=%u after=%u\n",
iocg->hweight_active, iocg->hweight_donating,
iocg->hweight_after_donation);
+ goto out;
}
}
@@ -2063,6 +2064,7 @@ static void transfer_surpluses(struct list_head *surpluses, struct ioc_now *now)
__propagate_weights(iocg, iocg->active, inuse, true, now);
}
+out:
/* walk list should be dissolved after use */
list_for_each_entry_safe(iocg, tiocg, &inner_walk, walk_list)
list_del_init(&iocg->walk_list);
diff --git a/block/blk-mq.c b/block/blk-mq.c
index ba0c48d58e13..cced68a0e3c5 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1428,6 +1428,7 @@ static void blk_add_rq_to_plug(struct blk_plug *plug, struct request *rq)
(!blk_queue_nomerges(rq->q) &&
blk_rq_bytes(last) >= BLK_PLUG_FLUSH_SIZE)) {
blk_mq_flush_plug_list(plug, false);
+ blk_plug_invalidate_ts();
last = NULL;
trace_block_plug(rq->q);
}
@@ -4015,6 +4016,7 @@ static int blk_mq_init_hctx(struct request_queue *q,
goto fail;
hctx->queue_num = hctx_idx;
+ clear_bit(BLK_MQ_S_INACTIVE, &hctx->state);
hctx->tags = set->tags[hctx_idx];
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index 35ebc3e0201b..b245d37db76b 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -116,12 +116,16 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg,
* full block size buffers.
*/
if (ctx->more || len < ctx->used) {
- if (len < bs) {
+ size_t excess = len % bs;
+
+ len -= excess;
+ iov_iter_revert(&msg->msg_iter, excess);
+
+ if (!len) {
err = -EINVAL;
goto free;
}
- len -= len % bs;
cflags |= CRYPTO_SKCIPHER_REQ_NOTFINAL;
}
diff --git a/crypto/ecc.c b/crypto/ecc.c
index f97ff234086f..a289d47d5651 100644
--- a/crypto/ecc.c
+++ b/crypto/ecc.c
@@ -1410,9 +1410,22 @@ static void ecc_point_add(const struct ecc_point *result,
vli_set(result->x, q->x, ndigits);
vli_set(result->y, q->y, ndigits);
vli_mod_sub(z, result->x, p->x, curve->p, ndigits);
+ if (vli_is_zero(z, ndigits)) {
+ if (vli_cmp(p->y, q->y, ndigits)) {
+ /* P + (-P) is the point at infinity. */
+ vli_clear(result->x, ndigits);
+ vli_clear(result->y, ndigits);
+ return;
+ }
+ /* The co-Z addition formula does not handle P == Q. */
+ z[0] = 1;
+ ecc_point_double_jacobian(result->x, result->y, z, curve);
+ goto out;
+ }
vli_set(px, p->x, ndigits);
vli_set(py, p->y, ndigits);
xycz_add(px, py, result->x, result->y, curve);
+out:
vli_mod_inv(z, z, curve->p, ndigits);
apply_z(result->x, result->y, z, curve);
}
@@ -1452,22 +1465,38 @@ void ecc_point_mult_shamir(const struct ecc_point *result,
vli_set(rx, point->x, ndigits);
vli_set(ry, point->y, ndigits);
vli_clear(z + 1, ndigits - 1);
- z[0] = 1;
+ z[0] = !ecc_point_is_zero(point);
for (--i; i >= 0; i--) {
ecc_point_double_jacobian(rx, ry, z, curve);
idx = !!vli_test_bit(u1, i);
idx |= (!!vli_test_bit(u2, i)) << 1;
point = points[idx];
- if (point) {
+ if (point && !ecc_point_is_zero(point)) {
u64 tx[ECC_MAX_DIGITS];
u64 ty[ECC_MAX_DIGITS];
u64 tz[ECC_MAX_DIGITS];
+ if (vli_is_zero(z, ndigits)) {
+ /* Adding to infinity starts a new accumulator. */
+ vli_set(rx, point->x, ndigits);
+ vli_set(ry, point->y, ndigits);
+ z[0] = 1;
+ continue;
+ }
vli_set(tx, point->x, ndigits);
vli_set(ty, point->y, ndigits);
apply_z(tx, ty, z, curve);
vli_mod_sub(tz, rx, tx, curve->p, ndigits);
+ if (vli_is_zero(tz, ndigits)) {
+ if (!vli_cmp(ry, ty, ndigits))
+ ecc_point_double_jacobian(rx, ry, z,
+ curve);
+ else
+ /* Adding opposite points yields infinity. */
+ vli_clear(z, ndigits);
+ continue;
+ }
xycz_add(tx, ty, rx, ry, curve);
vli_mod_mult_fast(z, z, tz, curve);
}
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 6a490aaa71b9..8543e21a4259 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -4061,16 +4061,17 @@ static int test_sig_one(struct crypto_sig *tfm, const struct sig_testvec *vecs)
*/
err = crypto_sig_verify(tfm, vecs->c, vecs->c_size,
vecs->m, vecs->m_size);
- if (err) {
- pr_err("alg: sig: verify test failed: err %d\n", err);
- return err;
+ if (err != vecs->verify_error) {
+ pr_err("alg: sig: verify test failed: expected %d, got %d\n",
+ vecs->verify_error, err);
+ return err ?: -EINVAL;
}
/*
* Don't invoke sign test (which requires a private key)
- * for vectors with only a public key.
+ * for vectors with only a public key or an invalid signature.
*/
- if (vecs->public_key_vec)
+ if (vecs->public_key_vec || vecs->verify_error)
return 0;
sig_size = crypto_sig_maxsize(tfm);
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index 268231227282..210dafb684fd 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -165,6 +165,7 @@ struct sig_testvec {
unsigned int param_len;
unsigned int m_size;
unsigned int c_size;
+ int verify_error;
bool public_key_vec;
enum OID algo;
};
@@ -1447,6 +1448,140 @@ static const struct sig_testvec x962_ecdsa_nist_p192_tv_template[] = {
};
static const struct sig_testvec x962_ecdsa_nist_p256_tv_template[] = {
+ /* Invalid signature: Q = G, r = s, e = 3r mod n. */
+ {
+ .key =
+ "\x04\x6b\x17\xd1\xf2\xe1\x2c\x42\x47\xf8\xbc\xe6\xe5\x63\xa4\x40"
+ "\xf2\x77\x03\x7d\x81\x2d\xeb\x33\xa0\xf4\xa1\x39\x45\xd8\x98\xc2"
+ "\x96\x4f\xe3\x42\xe2\xfe\x1a\x7f\x9b\x8e\xe7\xeb\x4a\x7c\x0f\x9e"
+ "\x16\x2b\xce\x33\x57\x6b\x31\x5e\xce\xcb\xb6\x40\x68\x37\xbf\x51"
+ "\xf5",
+ .key_len = 65,
+ .m =
+ "\xb2\xc3\x09\x72\x03\x17\x9d\xaa\x5c\x4a\xd8\xff\x02\xfe\xcb\x15"
+ "\x21\x53\x81\x77\x45\xec\x45\x24\x3d\x68\x10\xb9\xf2\xa5\x88\x3a",
+ .m_size = 32,
+ .c =
+ "\x30\x46\x02\x21\x00\x90\xeb\xad\xd0\x56\x5d\x34\x8e\x74\x18\xf2"
+ "\xff\xab\xaa\x43\xb1\x9f\x68\xd4\x0c\x4f\x01\x4b\xe3\x10\x60\x9e"
+ "\x7e\xfa\x58\x39\xd9\x02\x21\x00\x90\xeb\xad\xd0\x56\x5d\x34\x8e"
+ "\x74\x18\xf2\xff\xab\xaa\x43\xb1\x9f\x68\xd4\x0c\x4f\x01\x4b\xe3"
+ "\x10\x60\x9e\x7e\xfa\x58\x39\xd9",
+ .c_size = 72,
+ .verify_error = -EKEYREJECTED,
+ .public_key_vec = true,
+ },
+ /* Invalid signature: Q = -G, r = s, e = 3r mod n. */
+ {
+ .key =
+ "\x04\x6b\x17\xd1\xf2\xe1\x2c\x42\x47\xf8\xbc\xe6\xe5\x63\xa4\x40"
+ "\xf2\x77\x03\x7d\x81\x2d\xeb\x33\xa0\xf4\xa1\x39\x45\xd8\x98\xc2"
+ "\x96\xb0\x1c\xbd\x1c\x01\xe5\x80\x65\x71\x18\x14\xb5\x83\xf0\x61"
+ "\xe9\xd4\x31\xcc\xa9\x94\xce\xa1\x31\x34\x49\xbf\x97\xc8\x40\xae"
+ "\x0a",
+ .key_len = 65,
+ .m =
+ "\xb2\xc3\x09\x72\x03\x17\x9d\xaa\x5c\x4a\xd8\xff\x02\xfe\xcb\x15"
+ "\x21\x53\x81\x77\x45\xec\x45\x24\x3d\x68\x10\xb9\xf2\xa5\x88\x3a",
+ .m_size = 32,
+ .c =
+ "\x30\x46\x02\x21\x00\x90\xeb\xad\xd0\x56\x5d\x34\x8e\x74\x18\xf2"
+ "\xff\xab\xaa\x43\xb1\x9f\x68\xd4\x0c\x4f\x01\x4b\xe3\x10\x60\x9e"
+ "\x7e\xfa\x58\x39\xd9\x02\x21\x00\x90\xeb\xad\xd0\x56\x5d\x34\x8e"
+ "\x74\x18\xf2\xff\xab\xaa\x43\xb1\x9f\x68\xd4\x0c\x4f\x01\x4b\xe3"
+ "\x10\x60\x9e\x7e\xfa\x58\x39\xd9",
+ .c_size = 72,
+ .verify_error = -EKEYREJECTED,
+ .public_key_vec = true,
+ },
+ /* Wycheproof secp256r1/SHA-256 tcId 60. */
+ {
+ .key =
+ "\x04\x29\x27\xb1\x05\x12\xba\xe3\xed\xdc\xfe\x46\x78\x28\x12\x8b"
+ "\xad\x29\x03\x26\x99\x19\xf7\x08\x60\x69\xc8\xc4\xdf\x6c\x73\x28"
+ "\x38\xc7\x78\x79\x64\xea\xac\x00\xe5\x92\x1f\xb1\x49\x8a\x60\xf4"
+ "\x60\x67\x66\xb3\xd9\x68\x50\x01\x55\x8d\x1a\x97\x4e\x73\x41\x51"
+ "\x3e",
+ .key_len = 65,
+ .m =
+ "\x70\x23\x9d\xd8\x77\xf7\xc9\x44\xc4\x22\xf4\x4d\xea\x4e\xd1\xa5"
+ "\x2f\x26\x27\x41\x6f\xaf\x2f\x07\x2f\xa5\x0c\x77\x2e\xd6\xf8\x07",
+ .m_size = 32,
+ .c =
+ "\x30\x44\x02\x20\x64\xa1\xaa\xb5\x00\x0d\x0e\x80\x4f\x3e\x2f\xc0"
+ "\x2b\xde\xe9\xbe\x8f\xf3\x12\x33\x4e\x2b\xa1\x6d\x11\x54\x7c\x97"
+ "\x71\x1c\x89\x8e\x02\x20\x6a\xf0\x15\x97\x1c\xc3\x0b\xe6\xd1\xa2"
+ "\x06\xd4\xe0\x13\xe0\x99\x77\x72\xa2\xf9\x1d\x73\x28\x6f\xfd\x68"
+ "\x3b\x9b\xb2\xcf\x4f\x1b",
+ .c_size = 70,
+ .public_key_vec = true,
+ },
+ /* Wycheproof secp256r1/SHA-256 tcId 210. */
+ {
+ .key =
+ "\x04\xc6\xa7\x71\x52\x70\x24\x22\x77\x92\x17\x0a\x6f\x8e\xee\x73"
+ "\x5b\xf3\x2b\x7f\x98\xaf\x66\x9e\xad\x29\x98\x02\xe3\x2d\x7c\x31"
+ "\x07\xbc\x3b\x4b\x5e\x65\xab\x88\x7b\xbd\x34\x35\x72\xb3\xe5\x61"
+ "\x92\x61\xfe\x3a\x07\x3e\x2f\xfd\x78\x41\x2f\x72\x68\x67\xdb\x58"
+ "\x9e",
+ .key_len = 65,
+ .m =
+ "\xbb\x5a\x52\xf4\x2f\x9c\x92\x61\xed\x43\x61\xf5\x94\x22\xa1\xe3"
+ "\x00\x36\xe7\xc3\x2b\x27\x0c\x88\x07\xa4\x19\xfe\xca\x60\x50\x23",
+ .m_size = 32,
+ .c =
+ "\x30\x45\x02\x20\x7c\xf2\x7b\x18\x8d\x03\x4f\x7e\x8a\x52\x38\x03"
+ "\x04\xb5\x1a\xc3\xc0\x89\x69\xe2\x77\xf2\x1b\x35\xa6\x0b\x48\xfc"
+ "\x47\x66\x99\x78\x02\x21\x00\xb6\xdb\x6d\xb6\x24\x92\x49\x25\x49"
+ "\x24\x92\x49\x24\x92\x49\x24\x62\x5b\xd7\xa0\x9b\xec\x4c\xa8\x1b"
+ "\xcd\xd9\xf8\xfd\x6b\x63\xcc",
+ .c_size = 71,
+ .public_key_vec = true,
+ },
+ /* Q = G, d = 1, k = 2. */
+ {
+ .key =
+ "\x04\x6b\x17\xd1\xf2\xe1\x2c\x42\x47\xf8\xbc\xe6\xe5\x63\xa4\x40"
+ "\xf2\x77\x03\x7d\x81\x2d\xeb\x33\xa0\xf4\xa1\x39\x45\xd8\x98\xc2"
+ "\x96\x4f\xe3\x42\xe2\xfe\x1a\x7f\x9b\x8e\xe7\xeb\x4a\x7c\x0f\x9e"
+ "\x16\x2b\xce\x33\x57\x6b\x31\x5e\xce\xcb\xb6\x40\x68\x37\xbf\x51"
+ "\xf5",
+ .key_len = 65,
+ .m =
+ "\x35\x9d\x17\x29\xb9\x1a\x52\xe9\x8b\xb5\x95\xc3\x86\x28\x3c\x10"
+ "\x30\x44\x95\x1a\xa1\x08\x20\xf0\xa0\x22\xc4\x18\xc2\x71\xa0\xcc",
+ .m_size = 32,
+ .c =
+ "\x30\x44\x02\x20\x7c\xf2\x7b\x18\x8d\x03\x4f\x7e\x8a\x52\x38\x03"
+ "\x04\xb5\x1a\xc3\xc0\x89\x69\xe2\x77\xf2\x1b\x35\xa6\x0b\x48\xfc"
+ "\x47\x66\x99\x78\x02\x20\x59\x47\xc9\x21\x23\x0e\xd1\x34\x0b\x03"
+ "\xe6\xe3\x45\x6e\xab\x69\xf8\x66\xff\x7e\x8c\x7d\x1e\x13\x23\x17"
+ "\x06\x8a\x84\xec\x1d\x22",
+ .c_size = 70,
+ .public_key_vec = true,
+ },
+ /* Q = -G, d = n - 1, k = 2. */
+ {
+ .key =
+ "\x04\x6b\x17\xd1\xf2\xe1\x2c\x42\x47\xf8\xbc\xe6\xe5\x63\xa4\x40"
+ "\xf2\x77\x03\x7d\x81\x2d\xeb\x33\xa0\xf4\xa1\x39\x45\xd8\x98\xc2"
+ "\x96\xb0\x1c\xbd\x1c\x01\xe5\x80\x65\x71\x18\x14\xb5\x83\xf0\x61"
+ "\xe9\xd4\x31\xcc\xa9\x94\xce\xa1\x31\x34\x49\xbf\x97\xc8\x40\xae"
+ "\x0a",
+ .key_len = 65,
+ .m =
+ "\x35\x9d\x17\x29\xb9\x1a\x52\xe9\x8b\xb5\x95\xc3\x86\x28\x3c\x10"
+ "\x30\x44\x95\x1a\xa1\x08\x20\xf0\xa0\x22\xc4\x18\xc2\x71\xa0\xcc",
+ .m_size = 32,
+ .c =
+ "\x30\x45\x02\x20\x7c\xf2\x7b\x18\x8d\x03\x4f\x7e\x8a\x52\x38\x03"
+ "\x04\xb5\x1a\xc3\xc0\x89\x69\xe2\x77\xf2\x1b\x35\xa6\x0b\x48\xfc"
+ "\x47\x66\x99\x78\x02\x21\x00\xdc\x55\x4e\x07\x96\x0b\x81\xb6\x80"
+ "\xb1\xae\xe0\x40\xb9\x90\xa5\xf4\xc4\x90\x49\xbb\xa2\xa1\x62\x70"
+ "\xc5\x88\x51\x39\xe8\xa8\xfb",
+ .c_size = 71,
+ .public_key_vec = true,
+ },
{
.key = /* secp256r1(sha1) */
"\x04\xb9\x7b\xbb\xd7\x17\x64\xd2\x7e\xfc\x81\x5d\x87\x06\x83\x41"
@@ -1831,7 +1966,8 @@ static const struct sig_testvec p1363_ecdsa_nist_p256_tv_template[] = {
};
/*
- * EC-RDSA test vectors are generated by gost-engine.
+ * EC-RDSA test vectors generated by gost-engine, followed by constructed
+ * exceptional-point regression vectors.
*/
static const struct sig_testvec ecrdsa_tv_template[] = {
{
@@ -1977,6 +2113,195 @@ static const struct sig_testvec ecrdsa_tv_template[] = {
.m_size = 64,
.public_key_vec = true,
},
+ /*
+ * Constructed EC-RDSA exceptional-point vectors.
+ * With Q = dG, choose k = z1 + d*z2 (mod n), r = x(kG) mod n,
+ * e = -r/z2 (mod n), and s = r*d + k*e (mod n).
+ * The input digest is e, encoded little-endian.
+ */
+ /* cp256a: Q=G, z1 = 3, z2 = 1. */
+ {
+ .key =
+ "\x04\x40\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x14\x1e\x9f\x9e\x9c\xc9\xac\x22\xb1\xe3\x23\xdf\x2d\x4f"
+ "\x29\x35\x76\x2b\x3f\x45\x5a\x50\xdf\x27\xda\x9c\x98\xe0\x71\xe4"
+ "\x91\x8d",
+ .key_len = 66,
+ .params = /* OID_gostCPSignA */
+ "\x30\x13\x06\x07\x2a\x85\x03\x02\x02\x23\x01\x06\x08\x2a\x85\x03"
+ "\x07\x01\x01\x02\x02",
+ .param_len = 21,
+ .c =
+ "\x1a\xed\x44\xae\xd4\x4a\xed\x44\xae\xd4\x4a\xed\x44\xae\xd4\x49"
+ "\x32\x67\xe0\x26\x16\xfd\xb7\xaf\xa4\xd7\x3e\x61\xd4\xf9\x7b\x94"
+ "\xf7\x06\x3e\x70\x63\xe7\x06\x3e\x70\x63\xe7\x06\x3e\x70\x63\xe7"
+ "\x06\x3e\x70\x63\xe7\x06\x3e\x70\x63\xe7\x06\x3e\x70\x63\xe4\xb7",
+ .c_size = 64,
+ .m =
+ "\xdc\xd3\xfd\x46\xcb\x14\x9d\xe1\x8f\x92\x54\xb2\x0c\xa0\x22\x66"
+ "\x18\x9c\x8f\xc1\xf9\x18\x9c\x8f\xc1\xf9\x18\x9c\x8f\xc1\xf9\x08",
+ .m_size = 32,
+ .algo = OID_gost2012PKey256,
+ .public_key_vec = true,
+ },
+ /* cp256a: Q=-G, z1 = 7, z2 = 6. */
+ {
+ .key =
+ "\x04\x40\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x83\xdf\x60\x61\x63\x36\x53\xdd\x4e\x1c\xdc\x20\xd2\xb0"
+ "\xd6\xca\x89\xd4\xc0\xba\xa5\xaf\x20\xd8\x25\x63\x67\x1f\x8e\x1b"
+ "\x6e\x72",
+ .key_len = 66,
+ .params = /* OID_gostCPSignA */
+ "\x30\x13\x06\x07\x2a\x85\x03\x02\x02\x23\x01\x06\x08\x2a\x85\x03"
+ "\x07\x01\x01\x02\x02",
+ .param_len = 21,
+ .c =
+ "\x2a\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\x92\x10\x2d\x68\x19\x8f\x22\xd5\x60\xeb\x59\xd6\xf3\xe5\x9e\xc2"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+ .c_size = 64,
+ .m =
+ "\xc3\x9e\xe5\xf3\xd6\x59\xeb\x60\xd5\x22\x8f\x19\x68\x2d\x10\x92"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\x2a",
+ .m_size = 32,
+ .algo = OID_gost2012PKey256,
+ .public_key_vec = true,
+ },
+ /* cp256a: Q=-2G, z1 = 5, z2 = 2. */
+ {
+ .key =
+ "\x04\x40\x95\xfd\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\x14\x1e\x9f\x9e\x9c\xc9\xac\x22\xb1\xe3\x23\xdf\x2d\x4f"
+ "\x29\x35\x76\x2b\x3f\x45\x5a\x50\xdf\x27\xda\x9c\x98\xe0\x71\xe4"
+ "\x91\x8d",
+ .key_len = 66,
+ .params = /* OID_gostCPSignA */
+ "\x30\x13\x06\x07\x2a\x85\x03\x02\x02\x23\x01\x06\x08\x2a\x85\x03"
+ "\x07\x01\x01\x02\x02",
+ .param_len = 21,
+ .c =
+ "\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xb6\x30\x88\x38\x4c\xad\x68\x80\x22\xc2\x0d\x84\xdb\xb0\xdc\x47"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+ .c_size = 64,
+ .m =
+ "\x49\xdc\xb0\xdb\x84\x0d\xc2\x22\x80\x68\xad\x4c\x38\x88\x30\xb6"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7f",
+ .m_size = 32,
+ .algo = OID_gost2012PKey256,
+ .public_key_vec = true,
+ },
+ /* tc512a: Q=G, z1 = 3, z2 = 1. */
+ {
+ .key =
+ "\x04\x81\x80\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\xa4\xf2\x15\x52\xcb\x89\xa5\x89\xb8\xf5\x35\xc2\x5f"
+ "\xfe\x28\x80\xe9\x41\x3a\x0e\xa5\xe6\x75\x3d\xe9\x36\xd0\x4f\xbe"
+ "\x26\x16\xdf\x21\xa9\xef\xcb\xfd\x64\x80\x77\xc1\xab\xf1\xac\x93"
+ "\x1c\x5e\xce\xe6\x50\x54\xe2\x16\x88\x1b\xa6\xe3\x6a\x83\x7a\xe8"
+ "\xcf\x03\x75",
+ .key_len = 131,
+ .params = /* OID_gostTC26Sign512A */
+ "\x30\x0b\x06\x09\x2a\x85\x03\x07\x01\x02\x01\x02\x01",
+ .param_len = 13,
+ .c =
+ "\xd8\xc0\xd7\xe3\xfb\xa6\xca\xff\x6a\xd1\xb4\xb4\x82\x1d\x15\x6f"
+ "\xa7\xc7\xbd\x63\x95\xb8\x29\x0d\xd4\xfa\xb1\x1c\x36\xbd\x32\x8d"
+ "\x1a\xba\xf1\x2e\xbc\x7b\xf2\x02\x96\xf8\xef\x2a\x5e\x72\x64\x86"
+ "\x67\x62\xfb\x62\x8e\x83\x53\x63\xb7\xe6\x78\x3c\x42\xd7\x0d\x1d"
+ "\xb7\xbf\xb8\x09\x56\xc8\x67\x00\x31\xba\x19\x19\x29\xf6\x4e\x30"
+ "\x1d\x68\x16\x34\x23\x6d\x47\xa6\x0e\x57\x1a\x4b\xed\xc0\xef\x25"
+ "\x74\x52\xef\x78\xb5\xb9\x8d\xbb\x3d\x9f\x31\x29\xd9\x34\x94\x33"
+ "\xce\x2a\x3a\x35\xcb\x51\x9c\x91\xe2\xd6\x33\xd7\xb3\x73\xae\x16",
+ .c_size = 128,
+ .m =
+ "\x5f\x04\x9d\x6b\x69\x7d\xf7\xe7\xcb\x1b\x81\x2f\x76\xfe\x20\xcd"
+ "\x2c\xcc\xd0\x74\x63\xfa\x52\x32\x56\xfb\xd3\x3e\xba\xa5\x93\xb3"
+ "\xd9\x10\x3f\x12\xb4\xe5\xa8\xf1\x59\xb8\x92\xdc\xcb\xe9\x97\xe2"
+ "\xcf\xb1\x09\xd6\xe6\xe6\x45\xce\xff\x98\x37\xa9\xf6\x47\x40\x48",
+ .m_size = 64,
+ .algo = OID_gost2012PKey512,
+ .public_key_vec = true,
+ },
+ /* tc512a: Q=-G, z1 = 7, z2 = 6. */
+ {
+ .key =
+ "\x04\x81\x80\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x23\x0b\xea\xad\x34\x76\x5a\x76\x47\x0a\xca\x3d\xa0"
+ "\x01\xd7\x7f\x16\xbe\xc5\xf1\x5a\x19\x8a\xc2\x16\xc9\x2f\xb0\x41"
+ "\xd9\xe9\x20\xde\x56\x10\x34\x02\x9b\x7f\x88\x3e\x54\x0e\x53\x6c"
+ "\xe3\xa1\x31\x19\xaf\xab\x1d\xe9\x77\xe4\x59\x1c\x95\x7c\x85\x17"
+ "\x30\xfc\x8a",
+ .key_len = 131,
+ .params = /* OID_gostTC26Sign512A */
+ "\x30\x0b\x06\x09\x2a\x85\x03\x07\x01\x02\x01\x02\x01",
+ .param_len = 13,
+ .c =
+ "\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\x93\xf3\x4a\x99\x7a\x46\xc4\x88\xb7\xf9\x15\xc6\xa7\x02\xb0\x30"
+ "\x4d\xa5\x9c\x55\xfd\x69\x5c\x2e\xe5\x66\xd8\xa0\x8f\x88\x59\x37"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03",
+ .c_size = 128,
+ .m =
+ "\x3a\x59\x88\x8f\xa0\xd8\x66\xe5\x2e\x5c\x69\xfd\x55\x9c\xa5\x4d"
+ "\x30\xb0\x02\xa7\xc6\x15\xf9\xb7\x88\xc4\x46\x7a\x99\x4a\xf3\x93"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7f",
+ .m_size = 64,
+ .algo = OID_gost2012PKey512,
+ .public_key_vec = true,
+ },
+ /* tc512a: Q=-2G, z1 = 5, z2 = 2. */
+ {
+ .key =
+ "\x04\x81\x80\x7c\x13\xea\xd2\xd9\xaf\x82\xb9\x1f\xb2\xfd\xe1\x8d"
+ "\xf4\x5b\x37\xe4\xf2\x04\x6a\x2a\x1d\x15\x16\x95\x2a\x5d\x40\xcd"
+ "\xbb\x34\x44\x2f\x2b\x13\x0a\x47\xb0\xf6\xe5\xa5\x58\x3a\xf4\x54"
+ "\x09\xb0\x1d\xf5\x5c\xf1\xbf\x9d\x86\xa5\x97\xab\x96\x29\x62\xfc"
+ "\xdc\x89\x3b\x76\x3d\x50\x6e\x8a\xcf\x02\x4c\x74\x0e\x6e\xe0\x1f"
+ "\x28\xab\x27\x9b\x20\x02\xc8\xc4\x12\xcb\x8a\xe3\xa4\x27\xce\x39"
+ "\x62\xdf\x35\x6d\xc1\x65\x7f\x7b\x48\x90\x11\x8f\x19\x7c\x1e\x67"
+ "\x91\x97\xeb\x90\x85\x25\xfc\x86\xb3\x88\x6e\x5c\x57\x1f\x1d\x1d"
+ "\x3b\xec\x37",
+ .key_len = 131,
+ .params = /* OID_gostTC26Sign512A */
+ "\x30\x0b\x06\x09\x2a\x85\x03\x07\x01\x02\x01\x02\x01",
+ .param_len = 13,
+ .c =
+ "\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\x93\xf3\x4a\x99\x7a\x46\xc4\x88\xb7\xf9\x15\xc6\xa7\x02\xb0\x30"
+ "\x4d\xa5\x9c\x55\xfd\x69\x5c\x2e\xe5\x66\xd8\xa0\x8f\x88\x59\x33"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03",
+ .c_size = 128,
+ .m =
+ "\x39\x59\x88\x8f\xa0\xd8\x66\xe5\x2e\x5c\x69\xfd\x55\x9c\xa5\x4d"
+ "\x30\xb0\x02\xa7\xc6\x15\xf9\xb7\x88\xc4\x46\x7a\x99\x4a\xf3\x93"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7f",
+ .m_size = 64,
+ .algo = OID_gost2012PKey512,
+ .public_key_vec = true,
+ },
};
/*
diff --git a/drivers/accessibility/speakup/selection.c b/drivers/accessibility/speakup/selection.c
index 7df7afad5ab4..463a7ef19315 100644
--- a/drivers/accessibility/speakup/selection.c
+++ b/drivers/accessibility/speakup/selection.c
@@ -61,6 +61,9 @@ static struct speakup_selection_work speakup_sel_work = {
int speakup_set_selection(struct tty_struct *tty)
{
+ if (!tty)
+ return -ENODEV;
+
/* we get kref here first in order to avoid a subtle race when
* cancelling selection work. getting kref first establishes the
* invariant that if speakup_sel_work.tty is not NULL when
@@ -120,6 +123,9 @@ static struct speakup_selection_work speakup_paste_work = {
int speakup_paste_selection(struct tty_struct *tty)
{
+ if (!tty)
+ return -ENODEV;
+
tty_kref_get(tty);
if (cmpxchg(&speakup_paste_work.tty, NULL, tty)) {
tty_kref_put(tty);
diff --git a/drivers/acpi/pfr_update.c b/drivers/acpi/pfr_update.c
index 9afd2c52fdbd..98ace679601b 100644
--- a/drivers/acpi/pfr_update.c
+++ b/drivers/acpi/pfr_update.c
@@ -422,7 +422,7 @@ static int start_update(int action, struct pfru_device *pfru_dev)
static long pfru_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct pfru_update_cap_info cap_hdr;
+ struct pfru_update_cap_info cap_hdr = {};
struct pfru_device *pfru_dev = to_pfru_dev(file);
void __user *p = (void __user *)arg;
u32 rev;
diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c
index 4336c8a6e208..eea75961038c 100644
--- a/drivers/ata/ahci_st.c
+++ b/drivers/ata/ahci_st.c
@@ -83,18 +83,27 @@ static int st_ahci_deassert_resets(struct ahci_host_priv *hpriv,
return 0;
}
-static void st_ahci_host_stop(struct ata_host *host)
+static int st_ahci_assert_pwrdwn(struct ahci_host_priv *hpriv,
+ struct device *dev)
{
- struct ahci_host_priv *hpriv = host->private_data;
struct st_ahci_drv_data *drv_data = hpriv->plat_data;
- struct device *dev = host->dev;
int err;
- if (drv_data->pwr) {
- err = reset_control_assert(drv_data->pwr);
- if (err)
- dev_err(dev, "unable to pwrdwn\n");
- }
+ if (!drv_data->pwr)
+ return 0;
+
+ err = reset_control_assert(drv_data->pwr);
+ if (err)
+ dev_err(dev, "unable to pwrdwn\n");
+
+ return err;
+}
+
+static void st_ahci_host_stop(struct ata_host *host)
+{
+ struct ahci_host_priv *hpriv = host->private_data;
+
+ st_ahci_assert_pwrdwn(hpriv, host->dev);
ahci_platform_disable_resources(hpriv);
}
@@ -104,23 +113,20 @@ static int st_ahci_probe_resets(struct ahci_host_priv *hpriv,
{
struct st_ahci_drv_data *drv_data = hpriv->plat_data;
- drv_data->pwr = devm_reset_control_get(dev, "pwr-dwn");
- if (IS_ERR(drv_data->pwr)) {
- dev_info(dev, "power reset control not defined\n");
- drv_data->pwr = NULL;
- }
+ drv_data->pwr = devm_reset_control_get_optional(dev, "pwr-dwn");
+ if (IS_ERR(drv_data->pwr))
+ return dev_err_probe(dev, PTR_ERR(drv_data->pwr),
+ "failed to get pwr-dwn reset\n");
- drv_data->sw_rst = devm_reset_control_get(dev, "sw-rst");
- if (IS_ERR(drv_data->sw_rst)) {
- dev_info(dev, "soft reset control not defined\n");
- drv_data->sw_rst = NULL;
- }
+ drv_data->sw_rst = devm_reset_control_get_optional(dev, "sw-rst");
+ if (IS_ERR(drv_data->sw_rst))
+ return dev_err_probe(dev, PTR_ERR(drv_data->sw_rst),
+ "failed to get sw-rst reset\n");
- drv_data->pwr_rst = devm_reset_control_get(dev, "pwr-rst");
- if (IS_ERR(drv_data->pwr_rst)) {
- dev_dbg(dev, "power soft reset control not defined\n");
- drv_data->pwr_rst = NULL;
- }
+ drv_data->pwr_rst = devm_reset_control_get_optional(dev, "pwr-rst");
+ if (IS_ERR(drv_data->pwr_rst))
+ return dev_err_probe(dev, PTR_ERR(drv_data->pwr_rst),
+ "failed to get pwr-rst reset\n");
return st_ahci_deassert_resets(hpriv, dev);
}
@@ -162,38 +168,38 @@ static int st_ahci_probe(struct platform_device *pdev)
err = ahci_platform_enable_resources(hpriv);
if (err)
- return err;
+ goto assert_pwrdwn;
st_ahci_configure_oob(hpriv->mmio);
err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info,
&ahci_platform_sht);
- if (err) {
- ahci_platform_disable_resources(hpriv);
- return err;
- }
+ if (err)
+ goto disable_resources;
return 0;
+
+disable_resources:
+ ahci_platform_disable_resources(hpriv);
+assert_pwrdwn:
+ st_ahci_assert_pwrdwn(hpriv, &pdev->dev);
+
+ return err;
}
static int st_ahci_suspend(struct device *dev)
{
struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data;
- struct st_ahci_drv_data *drv_data = hpriv->plat_data;
int err;
err = ahci_platform_suspend_host(dev);
if (err)
return err;
- if (drv_data->pwr) {
- err = reset_control_assert(drv_data->pwr);
- if (err) {
- dev_err(dev, "unable to pwrdwn");
- return err;
- }
- }
+ err = st_ahci_assert_pwrdwn(hpriv, dev);
+ if (err)
+ return err;
ahci_platform_disable_resources(hpriv);
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index ecb56bb50fd7..50b2c997b6ba 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -2739,11 +2739,35 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host,
0, pp->irq_desc, host->ports[i]);
if (rc)
- return rc;
+ goto free_irqs;
ata_port_desc_misc(host->ports[i], irq);
}
- return ata_host_register(host, sht);
+ rc = ata_host_register(host, sht);
+ if (rc)
+ goto free_irqs;
+
+ return 0;
+
+free_irqs:
+ /*
+ * Free the IRQs which have been requested, so that the handlers can no
+ * longer access the MMIO of the host once we return, e.g. after the
+ * caller has disabled the clocks of the host.
+ */
+ while (--i >= 0) {
+ struct ahci_port_priv *pp = host->ports[i]->private_data;
+
+ if (!pp)
+ continue;
+
+ devm_free_irq(host->dev, hpriv->get_irq_vector(host, i),
+ host->ports[i]);
+ }
+
+ ata_host_undo_start(host);
+
+ return rc;
}
/**
diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c
index 7308d48cf748..7703c22ef532 100644
--- a/drivers/ata/libahci_platform.c
+++ b/drivers/ata/libahci_platform.c
@@ -692,6 +692,10 @@ EXPORT_SYMBOL_GPL(ahci_platform_get_resources);
* ahci-platform host, note any necessary resources (ie clks, phys, etc.)
* must be initialized / enabled before calling this.
*
+ * On failure, ->host_stop() is not called, so the caller has to release the
+ * resources it enabled (clocks, regulators, resets, PHYs) in its probe()
+ * error path.
+ *
* RETURNS:
* 0 on success otherwise a negative error code
*/
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index cd9c96560ddd..cb74c6a5d9aa 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -5976,6 +5976,46 @@ int ata_host_start(struct ata_host *host)
}
EXPORT_SYMBOL_GPL(ata_host_start);
+/**
+ * ata_host_undo_start - undo ata_host_start()
+ * @host: ATA host to undo_start
+ *
+ * Stop the ports of @host and drop the devres action registered by
+ * ata_host_start(), without calling ->host_stop(). Nothing is done if
+ * @host has not been started.
+ *
+ * This gives the release of the host resources back to the caller, which
+ * is what a driver whose probe() error path releases those resources
+ * itself needs when starting or activating the host fails.
+ *
+ * LOCKING:
+ * Inherited from calling layer (may sleep).
+ */
+void ata_host_undo_start(struct ata_host *host)
+{
+ int i;
+
+ if (!(host->flags & ATA_HOST_STARTED))
+ return;
+
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+
+ if (ap->ops->port_stop)
+ ap->ops->port_stop(ap);
+ }
+
+ /*
+ * Drop the action added by ata_host_start() without calling it.
+ * It does not exist if neither ->port_stop() nor ->host_stop() is
+ * implemented, in which case there is nothing to drop.
+ */
+ devres_destroy(host->dev, ata_host_stop, NULL, NULL);
+
+ host->flags &= ~ATA_HOST_STARTED;
+}
+EXPORT_SYMBOL_GPL(ata_host_undo_start);
+
/**
* ata_host_init - Initialize a host struct for sas (ipr, libsas)
* @host: host to initialize
@@ -6048,6 +6088,12 @@ static void async_port_probe(void *data, async_cookie_t cookie)
* starts ports, registers @host with ATA and SCSI layers and
* probe registered devices.
*
+ * On failure, the host remains started, i.e. the devres action
+ * registered by ata_host_start() is kept, so ->host_stop() is called by
+ * the driver core when probe() fails. A caller which releases the host
+ * resources in its own probe() error path must therefore call
+ * ata_host_undo_start().
+ *
* LOCKING:
* Inherited from calling layer (may sleep).
*
@@ -6141,6 +6187,10 @@ EXPORT_SYMBOL_GPL(ata_host_register);
* have set polling mode on the port. In this case, @irq_handler
* should be NULL.
*
+ * On failure, the ports are stopped again and the devres action
+ * registered by ata_host_start() is dropped without calling
+ * ->host_stop(), so releasing the host resources is left to the caller.
+ *
* LOCKING:
* Inherited from calling layer (may sleep).
*
@@ -6161,27 +6211,40 @@ int ata_host_activate(struct ata_host *host, int irq,
/* Special case for polling mode */
if (!irq) {
WARN_ON(irq_handler);
- return ata_host_register(host, sht);
+ rc = ata_host_register(host, sht);
+ if (rc)
+ goto undo_start;
+
+ return 0;
}
irq_desc = devm_kasprintf(host->dev, GFP_KERNEL, "%s[%s]",
dev_driver_string(host->dev),
dev_name(host->dev));
- if (!irq_desc)
- return -ENOMEM;
+ if (!irq_desc) {
+ rc = -ENOMEM;
+ goto undo_start;
+ }
rc = devm_request_irq(host->dev, irq, irq_handler, irq_flags,
irq_desc, host);
if (rc)
- return rc;
+ goto undo_start;
for (i = 0; i < host->n_ports; i++)
ata_port_desc_misc(host->ports[i], irq);
rc = ata_host_register(host, sht);
- /* if failed, just free the IRQ and leave ports alone */
- if (rc)
+ if (rc) {
+ /* Free the IRQ, so that the handler can no longer run */
devm_free_irq(host->dev, irq, host);
+ goto undo_start;
+ }
+
+ return 0;
+
+undo_start:
+ ata_host_undo_start(host);
return rc;
}
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index c780a7f15738..8b9530c50531 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3474,6 +3474,11 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
if (is_multi_taskfile(tf)) {
unsigned int multi_count = 1 << (cdb[1] >> 5);
+ if (!dev->multi_count) {
+ ata_scsi_set_sense(dev, scmd, ABORTED_COMMAND, 0, 0);
+ return 1;
+ }
+
/* compare the passed through multi_count
* with the cached multi_count of libata
*/
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index 1e2a2c33cdc8..142347b5428d 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -2256,6 +2256,11 @@ EXPORT_SYMBOL_GPL(ata_pci_sff_prepare_host);
* hosts. This separate helper is necessary because SFF hosts
* use two separate interrupts in legacy mode.
*
+ * Note that, unlike ata_host_activate(), the devres action registered
+ * by ata_host_start() is kept on failure, i.e. ->host_stop() is called
+ * by the driver core when probe() fails. All callers of this function
+ * rely on that, none of them releases the host resources itself.
+ *
* LOCKING:
* Inherited from calling layer (may sleep).
*
diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index 040e5c3f635a..a04e38cd4170 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -1489,8 +1489,10 @@ static int sata_fsl_probe(struct platform_device *ofdev)
* device discovery process, invoking our port_start() handler &
* error_handler() to execute a dummy Softreset EH session
*/
- ata_host_activate(host, irq, sata_fsl_interrupt, SATA_FSL_IRQ_FLAG,
- &sata_fsl_sht);
+ retval = ata_host_activate(host, irq, sata_fsl_interrupt,
+ SATA_FSL_IRQ_FLAG, &sata_fsl_sht);
+ if (retval)
+ goto error_exit_with_cleanup;
host_priv->intr_coalescing.show = fsl_sata_intr_coalescing_show;
host_priv->intr_coalescing.store = fsl_sata_intr_coalescing_store;
diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c
index cfb9b5b61cd7..b2be1ce87878 100644
--- a/drivers/ata/sata_qstor.c
+++ b/drivers/ata/sata_qstor.c
@@ -584,8 +584,12 @@ static int qs_ata_init_one(struct pci_dev *pdev,
qs_host_init(host, board_idx);
pci_set_master(pdev);
- return ata_host_activate(host, pdev->irq, qs_intr, IRQF_SHARED,
- &qs_ata_sht);
+ rc = ata_host_activate(host, pdev->irq, qs_intr, IRQF_SHARED,
+ &qs_ata_sht);
+ if (rc)
+ qs_host_stop(host);
+
+ return rc;
}
module_pci_driver(qs_ata_pci_driver);
diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c
index 2932b6653b6f..a477ada6b1ef 100644
--- a/drivers/block/amiflop.c
+++ b/drivers/block/amiflop.c
@@ -265,6 +265,8 @@ static struct fd_data_type data_types[] = {
static struct amiga_floppy_struct unit[FD_MAX_UNITS];
static struct timer_list flush_track_timer[FD_MAX_UNITS];
+static DEFINE_SPINLOCK(flush_track_timer_lock);
+static bool flush_track_timer_stopped[FD_MAX_UNITS];
static struct timer_list post_write_timer;
static unsigned long post_write_timer_drive;
static struct timer_list motor_on_timer;
@@ -1370,11 +1372,22 @@ static void flush_track_callback(struct timer_list *timer)
sizeof(flush_track_timer[0]);
nr&=3;
+ spin_lock(&flush_track_timer_lock);
+ if (flush_track_timer_stopped[nr]) {
+ spin_unlock(&flush_track_timer_lock);
+ return;
+ }
+ spin_unlock(&flush_track_timer_lock);
+
writefromint = 1;
if (!try_fdc(nr)) {
/* we might block in an interrupt, so try again later */
- flush_track_timer[nr].expires = jiffies + 1;
- add_timer(flush_track_timer + nr);
+ spin_lock(&flush_track_timer_lock);
+ if (!flush_track_timer_stopped[nr]) {
+ flush_track_timer[nr].expires = jiffies + 1;
+ add_timer(flush_track_timer + nr);
+ }
+ spin_unlock(&flush_track_timer_lock);
return;
}
get_fdc(nr);
@@ -1421,6 +1434,35 @@ static int non_int_flush_track (unsigned long nr)
return 1;
}
+static void flush_track_timer_stop(unsigned int drive)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&flush_track_timer_lock, flags);
+ flush_track_timer_stopped[drive] = true;
+ spin_unlock_irqrestore(&flush_track_timer_lock, flags);
+
+ timer_delete_sync(flush_track_timer + drive);
+}
+
+static void flush_track_timer_start(unsigned int drive)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&flush_track_timer_lock, flags);
+ flush_track_timer_stopped[drive] = false;
+ if (unit[drive].dirty)
+ mod_timer(flush_track_timer + drive, jiffies + 1);
+ spin_unlock_irqrestore(&flush_track_timer_lock, flags);
+}
+
+static void flush_track_timer_sync(unsigned int drive)
+{
+ flush_track_timer_stop(drive);
+ non_int_flush_track(drive);
+ flush_track_timer_start(drive);
+}
+
static int get_track(int drive, int track)
{
int error, errcnt;
@@ -1435,8 +1477,7 @@ static int get_track(int drive, int track)
}
if (unit[drive].dirty == 1) {
- timer_delete(flush_track_timer + drive);
- non_int_flush_track (drive);
+ flush_track_timer_sync(drive);
}
errcnt = 0;
while (errcnt < MAX_ERRORS) {
@@ -1562,6 +1603,12 @@ static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
case FDFMTTRK:
if (param < p->type->tracks * p->type->heads)
{
+ flush_track_timer_stop(drive);
+ if (unit[drive].dirty == 1 &&
+ !non_int_flush_track(drive)) {
+ flush_track_timer_start(drive);
+ return -EIO;
+ }
get_fdc(drive);
if (fd_seek(drive,param) != 0){
memset(p->trackbuf, FD_FILL_BYTE,
@@ -1570,6 +1617,7 @@ static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
}
floppy_off(drive);
rel_fdc();
+ flush_track_timer_start(drive);
}
else
return -EINVAL;
@@ -1591,8 +1639,7 @@ static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
case FDDEFPRM:
return -EINVAL;
case FDFLUSH: /* unconditionally, even if not needed */
- timer_delete(flush_track_timer + drive);
- non_int_flush_track(drive);
+ flush_track_timer_sync(drive);
break;
#ifdef RAW_IOCTL
case IOCTL_RAW_TRACK:
@@ -1611,9 +1658,15 @@ static int fd_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long param)
{
int ret;
+ unsigned int memflags = 0;
+ bool freeze_queue = cmd == FDFMTTRK || cmd == FDFLUSH;
mutex_lock(&amiflop_mutex);
+ if (freeze_queue)
+ memflags = blk_mq_freeze_queue(bdev->bd_disk->queue);
ret = fd_locked_ioctl(bdev, mode, cmd, param);
+ if (freeze_queue)
+ blk_mq_unfreeze_queue(bdev->bd_disk->queue, memflags);
mutex_unlock(&amiflop_mutex);
return ret;
@@ -1711,11 +1764,12 @@ static void floppy_release(struct gendisk *disk)
{
struct amiga_floppy_struct *p = disk->private_data;
int drive = p - unit;
+ unsigned int memflags;
mutex_lock(&amiflop_mutex);
+ memflags = blk_mq_freeze_queue(disk->queue);
if (unit[drive].dirty == 1) {
- timer_delete(flush_track_timer + drive);
- non_int_flush_track (drive);
+ flush_track_timer_sync(drive);
}
if (!fd_ref[drive]--) {
@@ -1725,6 +1779,7 @@ static void floppy_release(struct gendisk *disk)
#ifdef MODULE
floppy_off (drive);
#endif
+ blk_mq_unfreeze_queue(disk->queue, memflags);
mutex_unlock(&amiflop_mutex);
}
diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c
index 66e617664c14..cd7c735bb43b 100644
--- a/drivers/block/aoe/aoenet.c
+++ b/drivers/block/aoe/aoenet.c
@@ -131,9 +131,7 @@ static int
aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, struct net_device *orig_dev)
{
struct aoe_hdr *h;
- struct aoe_atahdr *ah;
u32 n;
- int sn;
if (dev_net(ifp) != &init_net)
goto exit;
@@ -144,12 +142,8 @@ aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt,
if (!is_aoe_netif(ifp))
goto exit;
skb_push(skb, ETH_HLEN); /* (1) */
- sn = sizeof(*h) + sizeof(*ah);
- if (skb->len >= sn) {
- sn -= skb_headlen(skb);
- if (sn > 0 && !__pskb_pull_tail(skb, sn))
- goto exit;
- }
+ if (!pskb_may_pull(skb, sizeof(*h)))
+ goto exit;
h = (struct aoe_hdr *) skb->data;
n = get_unaligned_be32(&h->tag);
if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31))
@@ -171,10 +165,14 @@ aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt,
switch (h->cmd) {
case AOECMD_ATA:
+ if (!pskb_may_pull(skb, sizeof(*h) + sizeof(struct aoe_atahdr)))
+ goto exit;
/* ata_rsp may keep skb for later processing or give it back */
skb = aoecmd_ata_rsp(skb);
break;
case AOECMD_CFG:
+ if (!pskb_may_pull(skb, sizeof(*h) + sizeof(struct aoe_cfghdr)))
+ goto exit;
aoecmd_cfg_rsp(skb);
break;
default:
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index c28786e0fe1c..eed7de727e54 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -2278,11 +2278,10 @@ static void request_done(int uptodate)
{
struct request *req = current_req;
int block;
- char msg[sizeof("request done ") + sizeof(int) * 3];
probing = 0;
- snprintf(msg, sizeof(msg), "request done %d", uptodate);
- reschedule_timeout(MAXTIMEOUT, msg);
+ reschedule_timeout(MAXTIMEOUT,
+ uptodate ? "request done 1" : "request done 0");
if (!req) {
pr_info("floppy.c: no request in request_done\n");
@@ -3603,7 +3602,7 @@ struct compat_floppy_drive_params {
};
struct compat_floppy_drive_struct {
- signed char flags;
+ compat_ulong_t flags;
compat_ulong_t spinup_date;
compat_ulong_t select_date;
compat_ulong_t first_read_date;
@@ -3787,6 +3786,7 @@ static int compat_getdrvstat(int drive, bool poll,
goto Eintr;
process_fd_request();
}
+ v.flags = drive_state[drive].flags;
v.spinup_date = drive_state[drive].spinup_date;
v.select_date = drive_state[drive].select_date;
v.first_read_date = drive_state[drive].first_read_date;
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 13ce229d450c..57d3babbd63b 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1686,8 +1686,10 @@ static int lo_compat_ioctl(struct block_device *bdev, blk_mode_t mode,
switch(cmd) {
case LOOP_SET_STATUS:
- err = loop_set_status_compat(lo,
- (const struct compat_loop_info __user *)arg);
+ err = -EPERM;
+ if ((mode & BLK_OPEN_WRITE) || capable(CAP_SYS_ADMIN))
+ err = loop_set_status_compat(lo,
+ (const struct compat_loop_info __user *)arg);
break;
case LOOP_GET_STATUS:
err = loop_get_status_compat(lo,
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index f44512ed1217..8c33a5f86c3d 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -1761,12 +1761,15 @@ static void ublk_ch_release_work_fn(struct work_struct *work)
}
/*
- * disk isn't attached yet, either device isn't live, or it has
- * been removed already, so we needn't to do anything
+ * No disk: the device isn't live, or it has been removed already.
+ * There are no requests to abort, but the round still has to be
+ * reset, so that a new server can fetch and start the device.
*/
disk = ublk_get_disk(ub);
- if (!disk)
- goto out;
+ if (!disk) {
+ mutex_lock(&ub->mutex);
+ goto reset;
+ }
/*
* All uring_cmd are done now, so abort any request outstanding to
@@ -1798,7 +1801,7 @@ static void ublk_ch_release_work_fn(struct work_struct *work)
/* double check after grabbing lock */
if (!ub->ub_disk)
- goto unlock;
+ goto reset;
/*
* Transition the device to the nosrv state. What exactly this
@@ -1824,17 +1827,19 @@ static void ublk_ch_release_work_fn(struct work_struct *work)
WRITE_ONCE(ublk_get_queue(ub, i)->fail_io, true);
}
}
-unlock:
+reset:
+ /*
+ * All uring_cmd has been done now, reset device & ubq. Under
+ * ub->mutex, so START_DEV sees the round either ready or reset.
+ */
+ ublk_reset_ch_dev(ub);
mutex_unlock(&ub->mutex);
ublk_put_disk(disk);
-
- /* all uring_cmd has been done now, reset device & ubq */
- ublk_reset_ch_dev(ub);
-out:
clear_bit(UB_STATE_OPEN, &ub->state);
/* put the reference grabbed in ublk_ch_release() */
ublk_put_device(ub);
+ module_put(THIS_MODULE);
}
static int ublk_ch_release(struct inode *inode, struct file *filp)
@@ -1847,6 +1852,8 @@ static int ublk_ch_release(struct inode *inode, struct file *filp)
*/
ublk_get_device(ub);
+ /* Keep the worker's callback and final device release code resident. */
+ __module_get(THIS_MODULE);
INIT_DELAYED_WORK(&ub->exit_work, ublk_ch_release_work_fn);
schedule_delayed_work(&ub->exit_work, 0);
return 0;
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index f37290fd3c5d..79fe0b107887 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -2133,10 +2133,8 @@ static void blkfront_closing(struct blkfront_info *info)
return;
/* No more blkif_request(). */
- if (info->rq && info->gd) {
- blk_mq_stop_hw_queues(info->rq);
+ if (info->gd)
blk_mark_disk_dead(info->gd);
- }
for_each_rinfo(info, rinfo, i) {
/* No more gnttab callback work. */
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 0b8c2ecf0a64..5b8b71efffac 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -843,6 +843,8 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x37ad, 0x0600), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x14b0, 0x047a), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Additional Realtek 8821AE Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
diff --git a/drivers/char/hw_random/amd-rng.c b/drivers/char/hw_random/amd-rng.c
index 9a24d19236dc..cc65d40a5598 100644
--- a/drivers/char/hw_random/amd-rng.c
+++ b/drivers/char/hw_random/amd-rng.c
@@ -80,7 +80,7 @@ static int amd_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
if (timeout-- == 0)
return read;
} else {
- return 0;
+ return read;
}
} else {
*data = ioread32(priv->iobase + RNGDATA);
diff --git a/drivers/char/hw_random/ba431-rng.c b/drivers/char/hw_random/ba431-rng.c
index 9de7466e6896..c33fbc25b7cd 100644
--- a/drivers/char/hw_random/ba431-rng.c
+++ b/drivers/char/hw_random/ba431-rng.c
@@ -148,7 +148,7 @@ static int ba431_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
}
}
- n *= sizeof(data);
+ n *= sizeof(*data);
return (n || !wait) ? n : -EIO;
}
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 054ffbc9e422..6412bfa792a2 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -381,6 +381,7 @@ static ssize_t rng_current_store(struct device *dev,
cur_rng_set_by_user = 1;
drop_current_rng();
} else {
+ err = -ENODEV;
list_for_each_entry(rng, &rng_list, list) {
if (sysfs_streq(rng->name, buf)) {
err = set_current_rng(rng);
diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
index f2a5e09257dd..a856b4ddbdc2 100644
--- a/drivers/char/tpm/tpm-dev-common.c
+++ b/drivers/char/tpm/tpm-dev-common.c
@@ -134,6 +134,9 @@ ssize_t tpm_common_read(struct file *file, char __user *buf,
ssize_t ret_size = 0;
int rc;
+ if (!size)
+ return 0;
+
mutex_lock(&priv->buffer_mutex);
if (priv->response_length) {
diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
index 879ac88f5783..bcfa2a1a208a 100644
--- a/drivers/char/tpm/tpm_nsc.c
+++ b/drivers/char/tpm/tpm_nsc.c
@@ -259,7 +259,12 @@ static struct platform_device *pdev = NULL;
static void tpm_nsc_remove(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
- struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
+ struct tpm_nsc_priv *priv;
+
+ if (!chip)
+ return;
+
+ priv = dev_get_drvdata(&chip->dev);
tpm_chip_unregister(chip);
release_region(priv->base, 2);
@@ -322,7 +327,6 @@ static int __init init_nsc(void)
pdev->num_resources = 0;
pdev->dev.driver = &nsc_drv.driver;
- pdev->dev.release = tpm_nsc_remove;
if ((rc = platform_device_add(pdev)) < 0)
goto err_put_dev;
diff --git a/drivers/clk/imx/clk.c b/drivers/clk/imx/clk.c
index df83bd939492..153e7405eae4 100644
--- a/drivers/clk/imx/clk.c
+++ b/drivers/clk/imx/clk.c
@@ -95,11 +95,16 @@ struct clk_hw *imx_obtain_fixed_clock_hw(
const char *name, unsigned long rate)
{
struct clk *clk;
+ struct clk_hw *hw;
clk = imx_obtain_fixed_clock_from_dt(name);
if (IS_ERR(clk))
- clk = imx_clk_fixed(name, rate);
- return __clk_get_hw(clk);
+ return __clk_get_hw(imx_clk_fixed(name, rate));
+
+ hw = __clk_get_hw(clk);
+ clk_put(clk);
+
+ return hw;
}
struct clk_hw *imx_obtain_fixed_of_clock(struct device_node *np,
@@ -119,12 +124,16 @@ struct clk_hw *imx_obtain_fixed_of_clock(struct device_node *np,
struct clk_hw *imx_get_clk_hw_by_name(struct device_node *np, const char *name)
{
struct clk *clk;
+ struct clk_hw *hw;
clk = of_clk_get_by_name(np, name);
if (IS_ERR(clk))
return ERR_PTR(-ENOENT);
- return __clk_get_hw(clk);
+ hw = __clk_get_hw(clk);
+ clk_put(clk);
+
+ return hw;
}
EXPORT_SYMBOL_GPL(imx_get_clk_hw_by_name);
diff --git a/drivers/clk/mstar/clk-msc313-mpll.c b/drivers/clk/mstar/clk-msc313-mpll.c
index 61beb4e87525..bd45aa0aec01 100644
--- a/drivers/clk/mstar/clk-msc313-mpll.c
+++ b/drivers/clk/mstar/clk-msc313-mpll.c
@@ -105,7 +105,7 @@ static int msc313_mpll_probe(struct platform_device *pdev)
return PTR_ERR(mpll->loop_div_second);
mpll->clk_data = devm_kzalloc(dev, struct_size(mpll->clk_data, hws,
- ARRAY_SIZE(output_dividers)), GFP_KERNEL);
+ NUMOUTPUTS), GFP_KERNEL);
if (!mpll->clk_data)
return -ENOMEM;
diff --git a/drivers/clk/mvebu/ap-cpu-clk.c b/drivers/clk/mvebu/ap-cpu-clk.c
index 1e44ace7d951..ed3ca59e0ee9 100644
--- a/drivers/clk/mvebu/ap-cpu-clk.c
+++ b/drivers/clk/mvebu/ap-cpu-clk.c
@@ -284,6 +284,8 @@ static int ap_cpu_clock_probe(struct platform_device *pdev)
if (!ap_cpu_data)
return -ENOMEM;
+ ap_cpu_data->num = nclusters;
+
for_each_of_cpu_node(dn) {
char *clk_name = "cpu-cluster-0";
struct clk_init_data init;
@@ -333,8 +335,6 @@ static int ap_cpu_clock_probe(struct platform_device *pdev)
ap_cpu_data->hws[cluster_index] = &ap_cpu_clk[cluster_index].hw;
}
- ap_cpu_data->num = cluster_index + 1;
-
ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, ap_cpu_data);
if (ret)
dev_err(dev, "failed to register OF clock provider\n");
diff --git a/drivers/clk/qcom/gcc-ipq5018.c b/drivers/clk/qcom/gcc-ipq5018.c
index 64792cda0620..78ccd70199fc 100644
--- a/drivers/clk/qcom/gcc-ipq5018.c
+++ b/drivers/clk/qcom/gcc-ipq5018.c
@@ -69,6 +69,15 @@ static struct clk_alpha_pll gpll0_main = {
.parent_data = gcc_xo_data,
.num_parents = ARRAY_SIZE(gcc_xo_data),
.ops = &clk_alpha_pll_stromer_ops,
+ /*
+ * During system boot, this PLL feeds the CPUs.
+ * Mark it as critical to ensure that CCF does
+ * not disable it, even if there are no active
+ * consumers. This is needed to avoid a system
+ * hang caused by turning off the clock driving
+ * the CPUs.
+ */
+ .flags = CLK_IS_CRITICAL,
},
},
};
@@ -1261,6 +1270,7 @@ static struct clk_rcg2 usb0_master_clk_src = {
};
static const struct freq_tbl ftbl_usb0_mock_utmi_clk_src[] = {
+ F(24000000, P_XO, 1, 0, 0),
F(60000000, P_GPLL4, 10, 1, 2),
{ }
};
diff --git a/drivers/clk/qcom/gcc-ipq5332.c b/drivers/clk/qcom/gcc-ipq5332.c
index 9246e97d785a..1d02c35c6722 100644
--- a/drivers/clk/qcom/gcc-ipq5332.c
+++ b/drivers/clk/qcom/gcc-ipq5332.c
@@ -1039,6 +1039,7 @@ static struct clk_rcg2 gcc_usb0_master_clk_src = {
};
static const struct freq_tbl ftbl_gcc_usb0_mock_utmi_clk_src[] = {
+ F(24000000, P_XO, 1, 0, 0),
F(60000000, P_GPLL4_OUT_AUX, 10, 1, 2),
{ }
};
diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c
index 369798d1ce42..e01c8ccf1731 100644
--- a/drivers/clk/qcom/ipq-cmn-pll.c
+++ b/drivers/clk/qcom/ipq-cmn-pll.c
@@ -351,6 +351,8 @@ static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
if (!hw_data)
return -ENOMEM;
+ hw_data->num = num_clks + 1;
+
/*
* Register the CMN PLL clock, which is the parent clock of
* the fixed rate output clocks.
@@ -377,7 +379,6 @@ static int ipq_cmn_pll_register_clks(struct platform_device *pdev)
* is configured to 12 GHZ by DT property assigned-clock-rates-u64.
*/
hw_data->hws[CMN_PLL_CLK] = cmn_pll_hw;
- hw_data->num = num_clks + 1;
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data);
if (ret)
diff --git a/drivers/clk/starfive/clk-starfive-jh7110-isp.c b/drivers/clk/starfive/clk-starfive-jh7110-isp.c
index f3fa069db193..c02c8b29a123 100644
--- a/drivers/clk/starfive/clk-starfive-jh7110-isp.c
+++ b/drivers/clk/starfive/clk-starfive-jh7110-isp.c
@@ -130,7 +130,7 @@ static int jh7110_ispcrg_probe(struct platform_device *pdev)
/* enable power domain and clocks */
pm_runtime_enable(priv->dev);
- ret = pm_runtime_get_sync(priv->dev);
+ ret = pm_runtime_resume_and_get(priv->dev);
if (ret < 0)
return dev_err_probe(priv->dev, ret, "failed to turn on power\n");
diff --git a/drivers/clocksource/timer-digicolor.c b/drivers/clocksource/timer-digicolor.c
index 559aa96089c3..46e0c329957a 100644
--- a/drivers/clocksource/timer-digicolor.c
+++ b/drivers/clocksource/timer-digicolor.c
@@ -190,6 +190,8 @@ static int __init digicolor_timer_init(struct device_node *node)
&dc_timer_dev.ce);
if (ret) {
pr_warn("request of timer irq %d failed (%d)\n", irq, ret);
+ clk_disable_unprepare(clk);
+ clk_put(clk);
return ret;
}
diff --git a/drivers/clocksource/timer-fttmr010.c b/drivers/clocksource/timer-fttmr010.c
index 126fb1f259b2..0003d49768f6 100644
--- a/drivers/clocksource/timer-fttmr010.c
+++ b/drivers/clocksource/timer-fttmr010.c
@@ -292,6 +292,7 @@ static int __init fttmr010_common_init(struct device_node *np,
ret = clk_prepare_enable(clk);
if (ret) {
pr_err("failed to enable PCLK\n");
+ clk_put(clk);
return ret;
}
@@ -432,6 +433,7 @@ static int __init fttmr010_common_init(struct device_node *np,
kfree(fttmr010);
out_disable_clock:
clk_disable_unprepare(clk);
+ clk_put(clk);
return ret;
}
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index 78702a08364f..73c1b62c1b37 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -289,6 +289,7 @@ endif
if LOONGARCH
config LOONGSON3_CPUFREQ
tristate "Loongson3 CPUFreq Driver"
+ depends on MACH_LOONGSON64
help
This option adds a CPUFreq driver for Loongson processors which
support software configurable cpu frequency.
diff --git a/drivers/cpufreq/airoha-cpufreq.c b/drivers/cpufreq/airoha-cpufreq.c
index b6b1cdc4d11d..ec9b2fc00f5e 100644
--- a/drivers/cpufreq/airoha-cpufreq.c
+++ b/drivers/cpufreq/airoha-cpufreq.c
@@ -64,7 +64,7 @@ static int airoha_cpufreq_probe(struct platform_device *pdev)
/* Attach PM for OPP */
ret = dev_pm_domain_attach_list(cpu_dev, &attach_data,
&priv->pd_list);
- if (ret)
+ if (ret < 0)
goto clear_opp_config;
cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index b1a200531628..0b06bbd7215f 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -469,8 +469,10 @@ static int shmem_init_perf(struct amd_cpudata *cpudata)
return 0;
}
- ret = cppc_set_auto_sel(cpudata->cpu,
- (cppc_state == AMD_PSTATE_PASSIVE) ? 0 : 1);
+ if ((cppc_state != AMD_PSTATE_PASSIVE) == auto_sel)
+ return 0;
+
+ ret = cppc_set_auto_sel(cpudata->cpu, !auto_sel);
if (ret)
pr_warn("failed to set auto_sel, ret: %d\n", ret);
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 60bab2b71b1b..4ee6990ab2ba 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -85,10 +85,12 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy)
freq_step = get_freq_step(cs_tuners, policy);
/*
- * Decrease requested_freq one freq_step for each idle period that
- * we didn't update the frequency.
+ * Apply deferred down steps only when the policy sample load is
+ * below down_threshold. Otherwise, multiple deferred down steps may
+ * cause a net frequency decrease outside the downscaling region.
*/
- if (policy_dbs->idle_periods < UINT_MAX) {
+ if (policy_dbs->max_sample_load < cs_tuners->down_threshold &&
+ policy_dbs->idle_periods < UINT_MAX) {
unsigned int freq_steps = policy_dbs->idle_periods * freq_step;
if (requested_freq > policy->min + freq_steps)
diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c
index 00a26fa6292c..a10cf44600ff 100644
--- a/drivers/cpufreq/cpufreq_governor.c
+++ b/drivers/cpufreq/cpufreq_governor.c
@@ -116,7 +116,8 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
struct policy_dbs_info *policy_dbs = policy->governor_data;
struct dbs_data *dbs_data = policy_dbs->dbs_data;
unsigned int ignore_nice = dbs_data->ignore_nice_load;
- unsigned int max_load = 0, idle_periods = UINT_MAX;
+ unsigned int max_load = 0, max_sample_load = 0;
+ unsigned int idle_periods = UINT_MAX;
unsigned int sampling_rate, io_busy, j;
/*
@@ -138,7 +139,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j);
u64 update_time, cur_idle_time;
unsigned int idle_time, time_elapsed;
- unsigned int load;
+ unsigned int load, sample_load;
cur_idle_time = get_cpu_idle_time(j, &update_time, io_busy);
@@ -171,6 +172,20 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
j_cdbs->prev_cpu_nice = cur_nice;
}
+ /*
+ * Compute the sample load separately from the prev_load value
+ * that may be reused after a long idle interval. The conservative
+ * governor uses it to decide whether to apply deferred down steps.
+ * If no time has elapsed, retain the existing behavior and use
+ * prev_load.
+ */
+ if (unlikely(!time_elapsed))
+ sample_load = j_cdbs->prev_load;
+ else if (time_elapsed > idle_time)
+ sample_load = 100 * (time_elapsed - idle_time) / time_elapsed;
+ else
+ sample_load = 0;
+
if (unlikely(!time_elapsed)) {
/*
* That can only happen when this function is called
@@ -205,11 +220,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
load = j_cdbs->prev_load;
j_cdbs->prev_load = 0;
} else {
- if (time_elapsed > idle_time)
- load = 100 * (time_elapsed - idle_time) / time_elapsed;
- else
- load = 0;
-
+ load = sample_load;
j_cdbs->prev_load = load;
}
@@ -222,9 +233,13 @@ unsigned int dbs_update(struct cpufreq_policy *policy)
if (load > max_load)
max_load = load;
+
+ if (sample_load > max_sample_load)
+ max_sample_load = sample_load;
}
policy_dbs->idle_periods = idle_periods;
+ policy_dbs->max_sample_load = max_sample_load;
return max_load;
}
diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h
index 1462d59277bd..ceb33336b2b3 100644
--- a/drivers/cpufreq/cpufreq_governor.h
+++ b/drivers/cpufreq/cpufreq_governor.h
@@ -94,6 +94,8 @@ struct policy_dbs_info {
/* Multiplier for increasing sample delay temporarily. */
unsigned int rate_mult;
unsigned int idle_periods; /* For conservative */
+ /* Maximum load from the current policy sample. */
+ unsigned int max_sample_load;
/* Status indicators */
bool is_shared; /* This object is used by multiple CPUs */
bool work_in_progress; /* Work is being queued up or in progress */
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index 40a9ff18da06..d457848b3eab 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -52,6 +52,10 @@ static void cpufreq_stats_reset_table(struct cpufreq_stats *stats)
* reset_pending accesses in cpufreq_stats_record_transition().
*/
smp_rmb();
+
+ if (stats->last_index == -1)
+ return;
+
cpufreq_stats_update(stats, READ_ONCE(stats->reset_time));
}
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 1de12358c0b9..a498d66883b5 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -3458,6 +3458,8 @@ static void intel_pstate_driver_cleanup(void)
if (intel_pstate_driver == &intel_pstate)
intel_pstate_clear_update_util_hook(cpu);
+ intel_pstate_disable_hwp_interrupt(all_cpu_data[cpu]);
+
kfree(all_cpu_data[cpu]);
WRITE_ONCE(all_cpu_data[cpu], NULL);
}
diff --git a/drivers/cpufreq/loongson3_cpufreq.c b/drivers/cpufreq/loongson3_cpufreq.c
index 1e8715ea1b77..aea7c31e155a 100644
--- a/drivers/cpufreq/loongson3_cpufreq.c
+++ b/drivers/cpufreq/loongson3_cpufreq.c
@@ -16,14 +16,16 @@
#include <asm/idle.h>
#include <asm/loongarch.h>
#include <asm/loongson.h>
+#include <asm/smp.h>
+#include <asm/numa.h>
/* Message */
union smc_message {
u32 value;
struct {
- u32 id : 4;
+ u32 id : 8;
u32 info : 4;
- u32 val : 16;
+ u32 val : 12;
u32 cmd : 6;
u32 extra : 1;
u32 complete : 1;
@@ -164,27 +166,47 @@ union smc_message {
#define FREQ_MAX_LEVEL 16
+#define MMIO_SMCMBX(node) \
+ ((void __iomem *)(nid_to_addrbase(node) | IO_BASE | LOONGSON_REG_BASE | LOONGARCH_IOCSR_SMCMBX))
+
+#define MMIO_MISC_FUNC(node) \
+ ((void __iomem *)(nid_to_addrbase(node) | IO_BASE | LOONGSON_REG_BASE | LOONGARCH_IOCSR_MISC_FUNC))
+
struct loongson3_freq_data {
unsigned int def_freq_level;
struct cpufreq_frequency_table table[];
};
-static struct mutex cpufreq_mutex[MAX_PACKAGES];
+static struct mutex cpufreq_mutex[MAX_NUMNODES];
static struct cpufreq_driver loongson3_cpufreq_driver;
static DEFINE_PER_CPU(struct loongson3_freq_data *, freq_data);
static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 extra)
{
int retries;
- unsigned int cpu = raw_smp_processor_id();
- unsigned int package = cpu_data[cpu].package;
+ unsigned int cpu = 0, nid;
union smc_message msg, last;
- mutex_lock(&cpufreq_mutex[package]);
+#ifdef CONFIG_SMP
+ switch (cmd) {
+ case CMD_GET_FREQ_INFO:
+ case CMD_SET_FREQ_INFO:
+ case CMD_GET_FREQ_LEVEL_NUM:
+ case CMD_GET_FREQ_LEVEL_INFO:
+ case CMD_GET_FREQ_BOOST_LEVEL:
+ cpu = cpu_number_map(id);
+ break;
+ default:
+ cpu = raw_smp_processor_id();
+ }
+#endif
+ nid = cpu_to_node(cpu);
- last.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);
+ mutex_lock(&cpufreq_mutex[nid]);
+
+ last.value = readl(MMIO_SMCMBX(nid));
if (!last.complete) {
- mutex_unlock(&cpufreq_mutex[package]);
+ mutex_unlock(&cpufreq_mutex[nid]);
return -EPERM;
}
@@ -195,12 +217,11 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext
msg.extra = extra;
msg.complete = 0;
- iocsr_write32(msg.value, LOONGARCH_IOCSR_SMCMBX);
- iocsr_write32(iocsr_read32(LOONGARCH_IOCSR_MISC_FUNC) | IOCSR_MISC_FUNC_SOFT_INT,
- LOONGARCH_IOCSR_MISC_FUNC);
+ writel(msg.value, MMIO_SMCMBX(nid));
+ writel(readl(MMIO_MISC_FUNC(nid)) | IOCSR_MISC_FUNC_SOFT_INT, MMIO_MISC_FUNC(nid));
for (retries = 0; retries < 10000; retries++) {
- msg.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);
+ msg.value = readl(MMIO_SMCMBX(nid));
if (msg.complete)
break;
@@ -208,49 +229,51 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext
}
if (!msg.complete || msg.cmd != CMD_OK) {
- mutex_unlock(&cpufreq_mutex[package]);
+ mutex_unlock(&cpufreq_mutex[nid]);
return -EPERM;
}
- mutex_unlock(&cpufreq_mutex[package]);
+ mutex_unlock(&cpufreq_mutex[nid]);
return msg.val;
}
static unsigned int loongson3_cpufreq_get(unsigned int cpu)
{
- int ret;
+ int ret, core = cpu_logical_map(cpu);
- ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0);
+ ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0);
- return ret * KILO;
+ return (ret < 0) ? 0 : ret * KILO;
}
static int loongson3_cpufreq_target(struct cpufreq_policy *policy, unsigned int index)
{
- int ret;
+ int ret, core = cpu_logical_map(policy->cpu);
- ret = do_service_request(cpu_data[policy->cpu].core,
- FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, index, 0);
+ ret = do_service_request(core, FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO,
+ index, 0);
return (ret >= 0) ? 0 : ret;
}
static int configure_freq_table(int cpu)
{
- int i, ret, boost_level, max_level, freq_level;
+ int i, ret, core, boost_level, max_level, freq_level;
struct platform_device *pdev = cpufreq_get_driver_data();
struct loongson3_freq_data *data;
if (per_cpu(freq_data, cpu))
return 0;
- ret = do_service_request(cpu, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0);
+ core = cpu_logical_map(cpu);
+
+ ret = do_service_request(core, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0);
if (ret < 0)
return ret;
max_level = ret;
- ret = do_service_request(cpu, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0);
+ ret = do_service_request(core, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0);
if (ret < 0)
return ret;
boost_level = ret;
@@ -263,7 +286,7 @@ static int configure_freq_table(int cpu)
data->def_freq_level = boost_level - 1;
for (i = 0; i < freq_level; i++) {
- ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0);
+ ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0);
if (ret < 0) {
devm_kfree(&pdev->dev, data);
return ret;
@@ -337,7 +360,7 @@ static int loongson3_cpufreq_probe(struct platform_device *pdev)
{
int i, ret;
- for (i = 0; i < MAX_PACKAGES; i++) {
+ for (i = 0; i < MAX_NUMNODES; i++) {
ret = devm_mutex_init(&pdev->dev, &cpufreq_mutex[i]);
if (ret)
return ret;
diff --git a/drivers/crypto/amlogic/amlogic-gxl-core.c b/drivers/crypto/amlogic/amlogic-gxl-core.c
index 6cb33949915f..7194117518b9 100644
--- a/drivers/crypto/amlogic/amlogic-gxl-core.c
+++ b/drivers/crypto/amlogic/amlogic-gxl-core.c
@@ -37,12 +37,13 @@ static irqreturn_t meson_irq_handler(int irq, void *data)
complete(&mc->chanlist[flow].complete);
return IRQ_HANDLED;
}
- dev_err(mc->dev, "%s %d Got irq for flow %d but ctrl is empty\n", __func__, irq, flow);
+ dev_err_ratelimited(mc->dev, "%s %d Got irq for flow %d but ctrl is empty\n", __func__, irq, flow);
+ return IRQ_NONE;
}
}
- dev_err(mc->dev, "%s %d from unknown irq\n", __func__, irq);
- return IRQ_HANDLED;
+ dev_err_ratelimited(mc->dev, "%s %d from unknown irq\n", __func__, irq);
+ return IRQ_NONE;
}
static struct meson_alg_template mc_algs[] = {
diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
index b393689400b4..e057be7fc713 100644
--- a/drivers/crypto/atmel-aes.c
+++ b/drivers/crypto/atmel-aes.c
@@ -1272,7 +1272,7 @@ static int atmel_aes_gcm_ghash_init(struct atmel_aes_dev *dd)
struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx);
/* Set the data length. */
- atmel_aes_write(dd, AES_AADLENR, dd->total);
+ atmel_aes_write(dd, AES_AADLENR, dd->datalen);
atmel_aes_write(dd, AES_CLENR, 0);
/* If needed, overwrite the GCM Intermediate Hash Word Registers */
diff --git a/drivers/crypto/atmel-tdes.c b/drivers/crypto/atmel-tdes.c
index d380f6741a2c..ac0be3c4cd01 100644
--- a/drivers/crypto/atmel-tdes.c
+++ b/drivers/crypto/atmel-tdes.c
@@ -373,6 +373,8 @@ static int atmel_tdes_crypt_pdc(struct atmel_tdes_dev *dd,
if (!(dd->flags & TDES_FLAGS_FAST)) {
dma_sync_single_for_device(dd->dev, dma_addr_in, length,
DMA_TO_DEVICE);
+ dma_sync_single_for_device(dd->dev, dma_addr_out, length,
+ DMA_FROM_DEVICE);
}
len32 = DIV_ROUND_UP(length, sizeof(u32));
@@ -405,6 +407,8 @@ static int atmel_tdes_crypt_dma(struct atmel_tdes_dev *dd,
if (!(dd->flags & TDES_FLAGS_FAST)) {
dma_sync_single_for_device(dd->dev, dma_addr_in, length,
DMA_TO_DEVICE);
+ dma_sync_single_for_device(dd->dev, dma_addr_out, length,
+ DMA_FROM_DEVICE);
}
addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
@@ -945,7 +949,7 @@ static int atmel_tdes_probe(struct platform_device *pdev)
struct resource *tdes_res;
int err;
- tdes_dd = devm_kmalloc(&pdev->dev, sizeof(*tdes_dd), GFP_KERNEL);
+ tdes_dd = devm_kzalloc(&pdev->dev, sizeof(*tdes_dd), GFP_KERNEL);
if (!tdes_dd)
return -ENOMEM;
diff --git a/drivers/crypto/caam/caampkc.c b/drivers/crypto/caam/caampkc.c
index cb001aa1de66..ac515ed8c575 100644
--- a/drivers/crypto/caam/caampkc.c
+++ b/drivers/crypto/caam/caampkc.c
@@ -1033,16 +1033,22 @@ static int caam_rsa_set_priv_key_form(struct caam_rsa_ctx *ctx,
free_dq:
kfree_sensitive(rsa_key->dq);
+ rsa_key->dq = NULL;
free_dp:
kfree_sensitive(rsa_key->dp);
+ rsa_key->dp = NULL;
free_tmp2:
kfree_sensitive(rsa_key->tmp2);
+ rsa_key->tmp2 = NULL;
free_tmp1:
kfree_sensitive(rsa_key->tmp1);
+ rsa_key->tmp1 = NULL;
free_q:
kfree_sensitive(rsa_key->q);
+ rsa_key->q = NULL;
free_p:
kfree_sensitive(rsa_key->p);
+ rsa_key->p = NULL;
return -ENOMEM;
}
diff --git a/drivers/crypto/ccp/dbc.c b/drivers/crypto/ccp/dbc.c
index 410084a9039c..4db49a14550c 100644
--- a/drivers/crypto/ccp/dbc.c
+++ b/drivers/crypto/ccp/dbc.c
@@ -236,6 +236,8 @@ int dbc_dev_init(struct psp_device *psp)
goto cleanup_mbox;
}
+ mutex_init(&dbc_dev->ioctl_mutex);
+
dbc_dev->char_dev.minor = MISC_DYNAMIC_MINOR;
dbc_dev->char_dev.name = "dbc";
dbc_dev->char_dev.fops = &dbc_fops;
@@ -244,8 +246,6 @@ int dbc_dev_init(struct psp_device *psp)
if (ret)
goto cleanup_mbox;
- mutex_init(&dbc_dev->ioctl_mutex);
-
return 0;
cleanup_mbox:
diff --git a/drivers/crypto/hisilicon/hpre/hpre_crypto.c b/drivers/crypto/hisilicon/hpre/hpre_crypto.c
index 839c1f677143..af5d10add106 100644
--- a/drivers/crypto/hisilicon/hpre/hpre_crypto.c
+++ b/drivers/crypto/hisilicon/hpre/hpre_crypto.c
@@ -138,6 +138,7 @@ struct hpre_asym_request {
int err;
hpre_cb cb;
struct timespec64 req_time;
+ u32 flags;
};
static inline unsigned int hpre_align_sz(void)
@@ -186,6 +187,7 @@ static int hpre_prepare_dma_buf(struct hpre_asym_request *hpre_req,
struct scatterlist *data, unsigned int len,
int is_src, dma_addr_t *tmp)
{
+ gfp_t gfp = hpre_req->flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL : GFP_ATOMIC;
struct hpre_ctx *ctx = hpre_req->ctx;
struct device *dev = ctx->dev;
void *ptr;
@@ -195,7 +197,7 @@ static int hpre_prepare_dma_buf(struct hpre_asym_request *hpre_req,
if (unlikely(shift < 0))
return -EINVAL;
- ptr = dma_alloc_coherent(dev, ctx->key_sz, tmp, GFP_ATOMIC);
+ ptr = dma_alloc_coherent(dev, ctx->key_sz, tmp, gfp);
if (unlikely(!ptr))
return -ENOMEM;
@@ -419,6 +421,7 @@ static int hpre_msg_request_set(struct hpre_ctx *ctx, void *req, bool is_rsa)
h_req = PTR_ALIGN(tmp, hpre_align_sz());
h_req->cb = hpre_rsa_cb;
h_req->areq.rsa = akreq;
+ h_req->flags = akreq->base.flags;
msg = &h_req->req;
memset(msg, 0, sizeof(*msg));
} else {
@@ -433,6 +436,7 @@ static int hpre_msg_request_set(struct hpre_ctx *ctx, void *req, bool is_rsa)
h_req = PTR_ALIGN(tmp, hpre_align_sz());
h_req->cb = hpre_dh_cb;
h_req->areq.dh = kreq;
+ h_req->flags = kreq->base.flags;
msg = &h_req->req;
memset(msg, 0, sizeof(*msg));
msg->key = cpu_to_le64(ctx->dh.dma_xa_p);
@@ -720,7 +724,7 @@ static void hpre_dh_exit_tfm(struct crypto_kpp *tfm)
static void hpre_rsa_drop_leading_zeros(const char **ptr, size_t *len)
{
- while (!**ptr && *len) {
+ while (*len && !**ptr) {
(*ptr)++;
(*len)--;
}
@@ -1460,13 +1464,14 @@ static int hpre_ecdh_msg_request_set(struct hpre_ctx *ctx,
if (req->dst_len < ctx->key_sz << 1) {
req->dst_len = ctx->key_sz << 1;
- return -EINVAL;
+ return -EOVERFLOW;
}
tmp = kpp_request_ctx(req);
h_req = PTR_ALIGN(tmp, hpre_align_sz());
h_req->cb = hpre_ecdh_cb;
h_req->areq.ecdh = req;
+ h_req->flags = req->base.flags;
msg = &h_req->req;
memset(msg, 0, sizeof(*msg));
msg->in = cpu_to_le64(DMA_MAPPING_ERROR);
@@ -1485,6 +1490,7 @@ static int hpre_ecdh_msg_request_set(struct hpre_ctx *ctx,
static int hpre_ecdh_src_data_init(struct hpre_asym_request *hpre_req,
struct scatterlist *data, unsigned int len)
{
+ gfp_t gfp = hpre_req->flags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL : GFP_ATOMIC;
struct hpre_sqe *msg = &hpre_req->req;
struct hpre_ctx *ctx = hpre_req->ctx;
struct device *dev = ctx->dev;
@@ -1498,7 +1504,7 @@ static int hpre_ecdh_src_data_init(struct hpre_asym_request *hpre_req,
if (unlikely(shift < 0))
return -EINVAL;
- ptr = dma_alloc_coherent(dev, ctx->key_sz << 2, &dma, GFP_KERNEL);
+ ptr = dma_alloc_coherent(dev, ctx->key_sz << 2, &dma, gfp);
if (unlikely(!ptr))
return -ENOMEM;
@@ -1520,13 +1526,13 @@ static int hpre_ecdh_dst_data_init(struct hpre_asym_request *hpre_req,
struct device *dev = ctx->dev;
dma_addr_t dma;
- if (unlikely(!data || !sg_is_last(data) || len != ctx->key_sz << 1)) {
- dev_err(dev, "data or data length is illegal!\n");
+ if (unlikely(!data || !sg_is_last(data))) {
+ dev_err(dev, "data is illegal!\n");
return -EINVAL;
}
hpre_req->dst = NULL;
- dma = dma_map_single(dev, sg_virt(data), len, DMA_FROM_DEVICE);
+ dma = dma_map_single(dev, sg_virt(data), ctx->key_sz << 1, DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(dev, dma))) {
dev_err(dev, "dma map data err!\n");
return -ENOMEM;
diff --git a/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c b/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c
index 81dd22d2083f..833d3c8e4650 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_dev_mgr.c
@@ -137,6 +137,13 @@ int adf_devmgr_add_dev(struct adf_accel_dev *accel_dev,
mutex_lock(&table_lock);
atomic_set(&accel_dev->ref_count, 0);
+ mutex_init(&accel_dev->state_lock);
+ /*
+ * The VF2PF lock is used to send messages to the PF outside of the
+ * device up/down cycle, so it must live as long as the accel device.
+ */
+ if (accel_dev->is_vf)
+ mutex_init(&accel_dev->vf.vf2pf_lock);
/* PF on host or VF on guest - optimized to remove redundant is_vf */
if (!accel_dev->is_vf || !pf) {
@@ -210,7 +217,6 @@ int adf_devmgr_add_dev(struct adf_accel_dev *accel_dev,
map->attached = true;
list_add_tail(&map->list, &vfs_table);
}
- mutex_init(&accel_dev->state_lock);
unlock:
mutex_unlock(&table_lock);
return ret;
@@ -257,6 +263,8 @@ void adf_devmgr_rm_dev(struct adf_accel_dev *accel_dev,
}
}
unlock:
+ if (accel_dev->is_vf)
+ mutex_destroy(&accel_dev->vf.vf2pf_lock);
mutex_destroy(&accel_dev->state_lock);
list_del(&accel_dev->list);
mutex_unlock(&table_lock);
diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen4_vf_mig.c b/drivers/crypto/intel/qat/qat_common/adf_gen4_vf_mig.c
index adb21656a3ba..91eff18a505b 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_gen4_vf_mig.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_gen4_vf_mig.c
@@ -837,6 +837,9 @@ static int adf_gen4_vfmig_save_state(struct qat_mig_dev *mdev)
return ret;
}
+ memset(mdev->state + mdev->setup_size, 0,
+ mdev->state_size - mdev->setup_size);
+
adf_mstate_mgr_init(vfmig->mstate_mgr, mdev->state + mdev->setup_size,
mdev->state_size - mdev->setup_size);
if (!adf_mstate_preamble_add(vfmig->mstate_mgr))
@@ -934,6 +937,8 @@ static int adf_gen4_vfmig_save_setup(struct qat_mig_dev *mdev)
if (mdev->setup_size)
return 0;
+ memset(mdev->state, 0, mdev->state_size);
+
adf_mstate_mgr_init(vfmig->mstate_mgr, mdev->state, mdev->state_size);
if (!adf_mstate_preamble_add(vfmig->mstate_mgr))
return -EINVAL;
diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen6_ras.c b/drivers/crypto/intel/qat/qat_common/adf_gen6_ras.c
index 967253082a98..902c4b1a23e3 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_gen6_ras.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_gen6_ras.c
@@ -323,14 +323,26 @@ static void adf_handle_ti_cd_par_sts(struct adf_accel_dev *accel_dev, void __iom
u32 ti_cd_par_sts;
ti_cd_par_sts = ADF_CSR_RD(csr, ADF_GEN6_TI_CD_PAR_STS);
- ti_cd_par_sts &= ADF_GEN6_TI_CD_PAR_STS_MASK;
+ ti_cd_par_sts &= ADF_GEN6_TI_CD_PAR_STS_TI_INT_ERR_DEVHALT_MASK;
if (ti_cd_par_sts) {
dev_err(&GET_DEV(accel_dev), "TI CD parity error: %#x\n", ti_cd_par_sts);
- ADF_RAS_ERR_CTR_INC(accel_dev->ras_errors, ADF_RAS_UNCORR);
ADF_CSR_WR(csr, ADF_GEN6_TI_CD_PAR_STS, ti_cd_par_sts);
}
}
+static void adf_handle_ti_misc_cd_par_sts(struct adf_accel_dev *accel_dev, void __iomem *csr)
+{
+ u32 ti_misc_cd_par_sts;
+
+ ti_misc_cd_par_sts = ADF_CSR_RD(csr, ADF_GEN6_TI_CD_PAR_STS);
+ ti_misc_cd_par_sts &= ADF_GEN6_TI_CD_PAR_STS_TI_MISC_MASK;
+ if (ti_misc_cd_par_sts) {
+ dev_err(&GET_DEV(accel_dev), "TI MISC CD parity error: %#x\n", ti_misc_cd_par_sts);
+ ADF_RAS_ERR_CTR_INC(accel_dev->ras_errors, ADF_RAS_UNCORR);
+ ADF_CSR_WR(csr, ADF_GEN6_TI_CD_PAR_STS, ti_misc_cd_par_sts);
+ }
+}
+
static void adf_handle_ti_trnsb_par_sts(struct adf_accel_dev *accel_dev, void __iomem *csr)
{
u32 ti_trnsb_par_sts;
@@ -367,7 +379,6 @@ static void adf_handle_ti_err(struct adf_accel_dev *accel_dev, void __iomem *csr
adf_handle_ti_ci_par_sts(accel_dev, csr);
adf_handle_ti_pullfub_par_sts(accel_dev, csr);
adf_handle_ti_pushfub_par_sts(accel_dev, csr);
- adf_handle_ti_cd_par_sts(accel_dev, csr);
adf_handle_ti_trnsb_par_sts(accel_dev, csr);
adf_handle_iosfp_cmd_parerr(accel_dev, csr);
}
@@ -591,6 +602,8 @@ static void adf_handle_timiscsts(struct adf_accel_dev *accel_dev, void __iomem *
if (!(errsou & ADF_GEN6_ERRSOU3_TIMISCSTS_BIT))
return;
+ adf_handle_ti_misc_cd_par_sts(accel_dev, csr);
+
timiscsts = ADF_CSR_RD(csr, ADF_GEN6_TIMISCSTS);
if (timiscsts) {
dev_err(&GET_DEV(accel_dev), "Fatal error in transmit interface: %#x\n",
@@ -730,6 +743,7 @@ static void adf_handle_ti_int_err_devhalt(struct adf_accel_dev *accel_dev, void
if (!(errsou & ADF_GEN6_ERRSOU3_TI_INT_ERR_DEVHALT_BIT))
return;
+ adf_handle_ti_cd_par_sts(accel_dev, csr);
dev_err(&GET_DEV(accel_dev), "DEVHALT due to a TI internal memory error\n");
ADF_RAS_ERR_CTR_INC(accel_dev->ras_errors, ADF_RAS_FATAL);
}
diff --git a/drivers/crypto/intel/qat/qat_common/adf_gen6_ras.h b/drivers/crypto/intel/qat/qat_common/adf_gen6_ras.h
index 66ced271d173..11f7b2986f56 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_gen6_ras.h
+++ b/drivers/crypto/intel/qat/qat_common/adf_gen6_ras.h
@@ -170,23 +170,25 @@ struct adf_ras_ops;
#define ADF_GEN6_TI_CD_PAR_ERR_MASK 0x500638
/*
- * TI CD parity status mask
+ * TI CD parity status mask - TI_INT_ERR_DEVHALT bits
* BIT(0) - BIT(15) - CtxMdRam[0:15]_sts parity status
* BIT(16) - Leaf2ClusterRam_sts parity status
* BIT(17) - BIT(18) - Ring2LeafRam[0:1]_sts parity status
- * BIT(19) - VirtualQ_sts parity status
- * BIT(20) - DtRdQ_sts parity status
- * BIT(21) - DtWrQ_sts parity status
- * BIT(22) - RiCmdQ_sts parity status
- * BIT(23) - BypassQ_sts parity status
- * BIT(24) - DtRdQ_sc_sts parity status
- * BIT(25) - DtWrQ_sc_sts parity status
*/
-#define ADF_GEN6_TI_CD_PAR_STS_MASK \
+#define ADF_GEN6_TI_CD_PAR_STS_TI_INT_ERR_DEVHALT_MASK \
(BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | \
BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12) | BIT(13) | \
- BIT(14) | BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(20) | \
- BIT(21) | BIT(22) | BIT(23) | BIT(24) | BIT(25))
+ BIT(14) | BIT(15) | BIT(16) | BIT(17) | BIT(18))
+
+/*
+ * TI CD parity status mask - TIMISCSTS bit
+ * BIT(19) - VirtualQ_sts parity status
+ */
+#define ADF_GEN6_TI_CD_PAR_STS_TI_MISC_MASK BIT(19)
+
+#define ADF_GEN6_TI_CD_PAR_STS_MASK \
+ (ADF_GEN6_TI_CD_PAR_STS_TI_INT_ERR_DEVHALT_MASK | \
+ ADF_GEN6_TI_CD_PAR_STS_TI_MISC_MASK)
/* TI TRNSB parity status */
#define ADF_GEN6_TI_TRNSB_PAR_STS 0x500648
diff --git a/drivers/crypto/intel/qat/qat_common/adf_vf_isr.c b/drivers/crypto/intel/qat/qat_common/adf_vf_isr.c
index a4636ec9f9ca..9ff6f6648301 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_vf_isr.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_vf_isr.c
@@ -118,7 +118,6 @@ static int adf_setup_pf2vf_bh(struct adf_accel_dev *accel_dev)
tasklet_init(&accel_dev->vf.pf2vf_bh_tasklet,
(void *)adf_pf2vf_bh_handler, (unsigned long)accel_dev);
- mutex_init(&accel_dev->vf.vf2pf_lock);
return 0;
}
@@ -126,7 +125,6 @@ static void adf_cleanup_pf2vf_bh(struct adf_accel_dev *accel_dev)
{
tasklet_disable(&accel_dev->vf.pf2vf_bh_tasklet);
tasklet_kill(&accel_dev->vf.pf2vf_bh_tasklet);
- mutex_destroy(&accel_dev->vf.vf2pf_lock);
}
static irqreturn_t adf_isr(int irq, void *privdata)
diff --git a/drivers/crypto/nx/nx-842.c b/drivers/crypto/nx/nx-842.c
index 8b1dc05df8c8..ade5361571f1 100644
--- a/drivers/crypto/nx/nx-842.c
+++ b/drivers/crypto/nx/nx-842.c
@@ -421,6 +421,8 @@ static int decompress(struct nx842_crypto_ctx *ctx,
slen -= spadding;
+ if (ignore > dlen)
+ return -EINVAL;
dlen -= ignore;
if (ignore)
pr_debug("ignoring last %x bytes\n", ignore);
diff --git a/drivers/crypto/omap-des.c b/drivers/crypto/omap-des.c
index 16d5c617d5ee..96b399c7eca6 100644
--- a/drivers/crypto/omap-des.c
+++ b/drivers/crypto/omap-des.c
@@ -1084,6 +1084,8 @@ static void omap_des_remove(struct platform_device *pdev)
omap_des_unregister_algs(dd->pdata);
+ crypto_engine_exit(dd->engine);
+
cancel_work_sync(&dd->done_task);
omap_des_dma_cleanup(dd);
pm_runtime_disable(dd->dev);
diff --git a/drivers/crypto/xilinx/zynqmp-sha.c b/drivers/crypto/xilinx/zynqmp-sha.c
index 5813017b6b79..cd051e1f4275 100644
--- a/drivers/crypto/xilinx/zynqmp-sha.c
+++ b/drivers/crypto/xilinx/zynqmp-sha.c
@@ -59,7 +59,7 @@ static int zynqmp_sha_init_tfm(struct crypto_shash *hash)
return PTR_ERR(fallback_tfm);
if (crypto_shash_descsize(hash) <
- crypto_shash_statesize(tfm_ctx->fbk_tfm)) {
+ crypto_shash_statesize(fallback_tfm)) {
crypto_free_shash(fallback_tfm);
return -EINVAL;
}
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index bd2e282ca93a..eeb6921950fc 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -205,9 +205,10 @@ cxl_acpi_evaluate_qtg_dsm(acpi_handle handle, struct access_coordinate *coord,
int entries, int *qos_class)
{
union acpi_object *out_obj, *out_buf, *obj;
+ /* QTG _DSM expects latencies in picoseconds. */
union acpi_object in_array[4] = {
- [0].integer = { ACPI_TYPE_INTEGER, coord->read_latency },
- [1].integer = { ACPI_TYPE_INTEGER, coord->write_latency },
+ [0].integer = { ACPI_TYPE_INTEGER, coord->read_latency * 1000ULL },
+ [1].integer = { ACPI_TYPE_INTEGER, coord->write_latency * 1000ULL },
[2].integer = { ACPI_TYPE_INTEGER, coord->read_bandwidth },
[3].integer = { ACPI_TYPE_INTEGER, coord->write_bandwidth },
};
@@ -225,6 +226,11 @@ cxl_acpi_evaluate_qtg_dsm(acpi_handle handle, struct access_coordinate *coord,
if (!entries)
return -EINVAL;
+ /* Latency inputs must fit in the DWORD fields defined by the spec. */
+ if (coord->read_latency > U32_MAX / 1000 ||
+ coord->write_latency > U32_MAX / 1000)
+ return -ERANGE;
+
out_obj = acpi_evaluate_dsm(handle, &acpi_cxl_qtg_id_guid, 1, 1, &in_obj);
if (!out_obj)
return -ENXIO;
@@ -892,13 +898,17 @@ static int pair_cxl_resource(struct device *dev, void *data)
static int cxl_acpi_probe(struct platform_device *pdev)
{
- int rc;
+ struct cxl_cfmws_context ctx;
+ struct acpi_device *adev;
struct resource *cxl_res;
struct cxl_root *cxl_root;
struct cxl_port *root_port;
struct device *host = &pdev->dev;
- struct acpi_device *adev = ACPI_COMPANION(host);
- struct cxl_cfmws_context ctx;
+ int rc;
+
+ adev = ACPI_COMPANION(host);
+ if (!adev)
+ return -ENODEV;
device_lock_set_class(&pdev->dev, &cxl_root_key);
rc = devm_add_action_or_reset(&pdev->dev, cxl_acpi_lock_reset_class,
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 68859eba30db..48c1f3854b79 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -738,8 +738,8 @@ long cxl_pci_get_latency(struct pci_dev *pdev)
{
long bw;
- bw = pcie_link_speed_mbps(pdev);
- if (bw < 0)
+ bw = pcie_link_bandwidth_mbps(pdev);
+ if (bw <= 0)
return 0;
bw /= BITS_PER_BYTE;
@@ -777,18 +777,12 @@ EXPORT_SYMBOL_NS_GPL(cxl_endpoint_decoder_reset_detected, "CXL");
int cxl_pci_get_bandwidth(struct pci_dev *pdev, struct access_coordinate *c)
{
- int speed, bw;
- u16 lnksta;
- u32 width;
-
- speed = pcie_link_speed_mbps(pdev);
- if (speed < 0)
- return speed;
- speed /= BITS_PER_BYTE;
+ int bw;
- pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta);
- width = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);
- bw = speed * width;
+ bw = pcie_link_bandwidth_mbps(pdev);
+ if (bw < 0)
+ return bw;
+ bw /= BITS_PER_BYTE;
for (int i = 0; i < ACCESS_COORDINATE_MAX; i++) {
c[i].read_bandwidth = bw;
@@ -860,7 +854,7 @@ static int update_gpf_port_dvsec(struct pci_dev *pdev, int dvsec, int phase)
rc = pci_read_config_word(pdev, dvsec + offset, &ctrl);
if (rc)
- return rc;
+ return pcibios_err_to_errno(rc);
if (FIELD_GET(base, ctrl) == GPF_TIMEOUT_BASE_MAX &&
FIELD_GET(scale, ctrl) == GPF_TIMEOUT_SCALE_MAX)
@@ -870,11 +864,17 @@ static int update_gpf_port_dvsec(struct pci_dev *pdev, int dvsec, int phase)
ctrl |= FIELD_PREP(scale, GPF_TIMEOUT_SCALE_MAX);
rc = pci_write_config_word(pdev, dvsec + offset, ctrl);
- if (!rc)
- pci_dbg(pdev, "Port GPF phase %d timeout: %d0 secs\n",
- phase, GPF_TIMEOUT_BASE_MAX);
+ if (rc) {
+ rc = pcibios_err_to_errno(rc);
+ pci_warn(pdev, "Port GPF phase %d timeout write failed: %d\n",
+ phase, rc);
+ return rc;
+ }
- return rc;
+ pci_dbg(pdev, "Port GPF phase %d timeout: %d0 secs\n",
+ phase, GPF_TIMEOUT_BASE_MAX);
+
+ return 0;
}
int cxl_gpf_port_setup(struct cxl_dport *dport)
@@ -884,16 +884,22 @@ int cxl_gpf_port_setup(struct cxl_dport *dport)
if (!dport->gpf_dvsec) {
struct pci_dev *pdev;
- int dvsec;
+ int dvsec, rc;
dvsec = cxl_gpf_get_dvsec(dport->dport_dev);
if (!dvsec)
return -EINVAL;
- dport->gpf_dvsec = dvsec;
pdev = to_pci_dev(dport->dport_dev);
- update_gpf_port_dvsec(pdev, dport->gpf_dvsec, 1);
- update_gpf_port_dvsec(pdev, dport->gpf_dvsec, 2);
+ rc = update_gpf_port_dvsec(pdev, dvsec, 1);
+ if (rc)
+ return rc;
+
+ rc = update_gpf_port_dvsec(pdev, dvsec, 2);
+ if (rc)
+ return rc;
+
+ dport->gpf_dvsec = dvsec;
}
return 0;
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 8d2dc4b74178..599aa9d9eddc 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -2286,6 +2286,9 @@ EXPORT_SYMBOL_NS_GPL(schedule_cxl_memdev_detach, "CXL");
static void add_latency(struct access_coordinate *c, long latency)
{
+ /* Convert link latency from picoseconds to nanoseconds. */
+ latency = DIV_ROUND_UP(latency, 1000);
+
for (int i = 0; i < ACCESS_COORDINATE_MAX; i++) {
c[i].write_latency += latency;
c[i].read_latency += latency;
diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index efa5f6186019..9b46166fd214 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -601,7 +601,7 @@ static int __init dax_core_init(void)
unregister_chrdev_region(dax_devt, MINORMASK+1);
err_chrdev:
dax_fs_exit();
- return 0;
+ return rc;
}
static void __exit dax_core_exit(void)
diff --git a/drivers/dma/altera-msgdma.c b/drivers/dma/altera-msgdma.c
index e1811a9be0bb..2d4bca495b3d 100644
--- a/drivers/dma/altera-msgdma.c
+++ b/drivers/dma/altera-msgdma.c
@@ -849,11 +849,6 @@ static int msgdma_probe(struct platform_device *pdev)
if (mdev->irq < 0)
return -ENXIO;
- ret = devm_request_irq(&pdev->dev, mdev->irq, msgdma_irq_handler,
- 0, dev_name(&pdev->dev), mdev);
- if (ret)
- return ret;
-
tasklet_setup(&mdev->irq_tasklet, msgdma_tasklet);
dma_cookie_init(&mdev->dmachan);
@@ -865,6 +860,11 @@ static int msgdma_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&mdev->done_list);
INIT_LIST_HEAD(&mdev->free_list);
+ ret = devm_request_irq(&pdev->dev, mdev->irq, msgdma_irq_handler,
+ 0, dev_name(&pdev->dev), mdev);
+ if (ret)
+ return ret;
+
dma_dev = &mdev->dmadev;
/* Set DMA capabilities */
diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c
index dfd49412f319..59d359f3f3fb 100644
--- a/drivers/edac/i10nm_base.c
+++ b/drivers/edac/i10nm_base.c
@@ -834,6 +834,7 @@ static int i10nm_get_ddr_munits(void)
mbase = ioremap(base + off, size);
if (!mbase) {
+ pci_dev_put(mdev);
i10nm_printk(KERN_ERR, "Failed to ioremap 0x%llx\n",
base + off);
return -ENODEV;
diff --git a/drivers/firewire/ohci-serdes-test.c b/drivers/firewire/ohci-serdes-test.c
index 258f668619ef..87b136e041de 100644
--- a/drivers/firewire/ohci-serdes-test.c
+++ b/drivers/firewire/ohci-serdes-test.c
@@ -43,7 +43,7 @@ static void test_self_id_receive_buffer_deserialization(struct kunit *test)
static void test_at_data_serdes(struct kunit *test)
{
static const __le32 expected[] = {
- cpu_to_le32(0x00020e80),
+ cpu_to_le32(0x00820e80),
cpu_to_le32(0xffc2ffff),
cpu_to_le32(0xe0000000),
};
@@ -56,7 +56,7 @@ static void test_at_data_serdes(struct kunit *test)
unsigned int destination_id = ohci1394_at_data_get_destination_id(expected);
u64 destination_offset = ohci1394_at_data_get_destination_offset(expected);
- KUNIT_EXPECT_FALSE(test, has_src_bus_id);
+ KUNIT_EXPECT_TRUE(test, has_src_bus_id);
KUNIT_EXPECT_EQ(test, 0x02, speed);
KUNIT_EXPECT_EQ(test, 0x03, tlabel);
KUNIT_EXPECT_EQ(test, 0x02, retry);
diff --git a/drivers/firewire/ohci.h b/drivers/firewire/ohci.h
index 218666cfe14a..63ccf2f44504 100644
--- a/drivers/firewire/ohci.h
+++ b/drivers/firewire/ohci.h
@@ -178,7 +178,7 @@
static inline bool ohci1394_at_data_get_src_bus_id(const __le32 *data)
{
- return !!((data[0] & OHCI1394_AT_DATA_Q0_srcBusID_MASK) >> OHCI1394_AT_DATA_Q0_srcBusID_SHIFT);
+ return !!((le32_to_cpu(data[0]) & OHCI1394_AT_DATA_Q0_srcBusID_MASK) >> OHCI1394_AT_DATA_Q0_srcBusID_SHIFT);
}
static inline void ohci1394_at_data_set_src_bus_id(__le32 *data, bool src_bus_id)
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 94b05e4451dd..c532ba550352 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -22,7 +22,7 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -std=gnu11 \
# arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly
# disable the stackleak plugin
-cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_KSTACK_ERASE) \
+cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_KSTACK_ERASE) -ffixed-x18 \
-fno-unwind-tables -fno-asynchronous-unwind-tables
cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \
-DEFI_HAVE_MEMCHR -DEFI_HAVE_STRRCHR \
diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
index 874f63b4a383..adc5d121a33c 100644
--- a/drivers/firmware/efi/libstub/efi-stub.c
+++ b/drivers/firmware/efi/libstub/efi-stub.c
@@ -73,6 +73,14 @@ static void install_memreserve_table(void)
efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID;
efi_status_t status;
+ /*
+ * If an earlier boot stage installed a memreserve table already, e.g.,
+ * a kexec loader passing on the persistent reservations made by the
+ * previous kernel, use it as is: installing a new one would replace it.
+ */
+ if (get_efi_config_table(memreserve_table_guid))
+ return;
+
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv),
(void **)&rsv);
if (status != EFI_SUCCESS) {
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index 0f4035b089a2..f6856aabf27d 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -744,7 +744,7 @@ EXPORT_SYMBOL_GPL(fpga_mgr_put);
* @mgr: fpga manager
*
* Given a pointer to FPGA Manager (from fpga_mgr_get() or
- * of_fpga_mgr_put()) attempt to get the mutex. The user should call
+ * of_fpga_mgr_get()) attempt to get the mutex. The user should call
* fpga_mgr_lock() and verify that it returns 0 before attempting to
* program the FPGA. Likewise, the user should call fpga_mgr_unlock
* when done programming the FPGA.
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index 5de42f1143da..80b3b36cf703 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -593,10 +593,10 @@ static void davinci_gpio_save_context(struct davinci_gpio_controller *chips,
context->set_data = readl_relaxed(&g->set_data);
context->set_rising = readl_relaxed(&g->set_rising);
context->set_falling = readl_relaxed(&g->set_falling);
- }
- /* Clear all interrupt status registers */
- writel_relaxed(GENMASK(31, 0), &g->intstat);
+ /* Clear stale interrupt status for this bank. */
+ writel_relaxed(GENMASK(31, 0), &g->intstat);
+ }
}
static void davinci_gpio_restore_context(struct davinci_gpio_controller *chips,
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 4eeac12ad0ed..7ebc4f9662c9 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -863,6 +863,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
DECLARE_BITMAP(irq_mask, MAX_LINE);
DECLARE_BITMAP(reg_direction, MAX_LINE);
int level;
+ int ret;
guard(mutex)(&chip->i2c_lock);
@@ -877,7 +878,9 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
}
/* Switch direction to input if needed */
- pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ ret = pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ if (ret)
+ goto out;
bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio);
bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_high, gc->ngpio);
@@ -889,6 +892,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
for_each_set_bit(level, irq_mask, gc->ngpio)
pca953x_gpio_direction_input_unlocked(&chip->gpio_chip, level);
+out:
mutex_unlock(&chip->irq_lock);
}
@@ -983,7 +987,9 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin
}
/* Remove output pins from the equation */
- pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ ret = pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ if (ret)
+ return false;
bitmap_copy(old_stat, chip->irq_stat, gc->ngpio);
@@ -1084,7 +1090,9 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base)
* interrupt. We have to rely on the previous read for
* this purpose.
*/
- pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ ret = pca953x_read_regs(chip, chip->regs->direction, reg_direction);
+ if (ret)
+ return ret;
bitmap_and(chip->irq_stat, irq_stat, reg_direction, gc->ngpio);
mutex_init(&chip->irq_lock);
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index d67d361cc456..1afda8ea5769 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -1253,11 +1253,6 @@ static void gpio_v2_line_config_flags_to_desc_flags(u64 lflags,
assign_bit(GPIOD_FLAG_ACTIVE_LOW, &flags,
lflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW);
- if (lflags & GPIO_V2_LINE_FLAG_OUTPUT)
- set_bit(GPIOD_FLAG_IS_OUT, &flags);
- else if (lflags & GPIO_V2_LINE_FLAG_INPUT)
- clear_bit(GPIOD_FLAG_IS_OUT, &flags);
-
assign_bit(GPIOD_FLAG_EDGE_RISING, &flags,
lflags & GPIO_V2_LINE_FLAG_EDGE_RISING);
assign_bit(GPIOD_FLAG_EDGE_FALLING, &flags,
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index d0efcb96d276..c23ba5e94cd6 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2464,10 +2464,13 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
ret = desc_set_label(desc, label ? : "?");
if (ret)
- goto out_clear_bit;
+ goto out_free_gpio;
return 0;
+out_free_gpio:
+ if (guard.gc->free)
+ guard.gc->free(guard.gc, offset);
out_clear_bit:
clear_bit(GPIOD_FLAG_REQUESTED, &desc->flags);
return ret;
@@ -2553,6 +2556,7 @@ void gpiod_free(struct gpio_desc *desc)
char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_desc *desc;
+ const char *name;
char *label;
desc = gpiochip_get_desc(gc, offset);
@@ -2564,7 +2568,11 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset)
guard(srcu)(&desc->gdev->desc_srcu);
- label = kstrdup(gpiod_get_label(desc), GFP_KERNEL);
+ name = gpiod_get_label(desc);
+ if (!name)
+ return NULL;
+
+ label = kstrdup(name, GFP_KERNEL);
if (!label)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 803b6fc360a0..034f2d03e367 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -1195,8 +1195,17 @@ struct amdgpu_device {
struct mutex mn_lock;
DECLARE_HASHTABLE(mn_hash, 7);
- /* tracking pinned memory */
+ /* tracking pinned memory
+ *
+ * vram_pin_size = sum(vram_pin_size_xcp[]) + vram_pin_size_common
+ *
+ * vram_pin_size total pin size in device
+ * vram_pin_size_xcp[i] pins owned by partition i (bo->xcp_id = i, i >= 0)
+ * vram_pin_size_common pins not owned by any partition, bo->xcp_id = -1
+ */
atomic64_t vram_pin_size;
+ atomic64_t vram_pin_size_xcp[MAX_XCP];
+ atomic64_t vram_pin_size_common;
atomic64_t visible_pin_size;
atomic64_t gart_pin_size;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
index c59b5d9cd36b..21dbc8675f4e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
@@ -40,6 +40,12 @@
extern uint64_t amdgpu_amdkfd_total_mem_size;
+/*
+ * Align VRAM availability to 2MB to avoid fragmentation caused by 4K
+ * allocations in the tail 2MB BO chunk.
+ */
+#define VRAM_AVAILABLITY_ALIGN (1 << 21)
+
enum TLB_FLUSH_TYPE {
TLB_FLUSH_LEGACY = 0,
TLB_FLUSH_LIGHTWEIGHT,
@@ -104,6 +110,7 @@ struct amdgpu_kfd_dev {
struct kfd_dev *dev;
int64_t vram_used[MAX_XCP];
uint64_t vram_used_aligned[MAX_XCP];
+ atomic64_t vram_pinned[MAX_XCP];
bool init_complete;
struct work_struct reset_work;
@@ -374,6 +381,8 @@ void amdgpu_amdkfd_gpuvm_destroy_cb(struct amdgpu_device *adev,
* Allows KFD to release its resources associated with the GEM object.
*/
void amdgpu_amdkfd_release_notify(struct amdgpu_bo *bo);
+void amdgpu_amdkfd_account_vram_pin(struct amdgpu_bo *bo, int xcp_id);
+void amdgpu_amdkfd_account_vram_unpin(struct amdgpu_bo *bo, int xcp_id);
void amdgpu_amdkfd_reserve_system_mem(uint64_t size);
#else
static inline
@@ -391,6 +400,16 @@ static inline
void amdgpu_amdkfd_release_notify(struct amdgpu_bo *bo)
{
}
+
+static inline
+void amdgpu_amdkfd_account_vram_pin(struct amdgpu_bo *bo, int xcp_id)
+{
+}
+
+static inline
+void amdgpu_amdkfd_account_vram_unpin(struct amdgpu_bo *bo, int xcp_id)
+{
+}
#endif
#if IS_ENABLED(CONFIG_HSA_AMD_SVM)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index d56728b257f4..6de7dfa30ce5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
@@ -46,12 +46,6 @@
#define AMDGPU_USERPTR_RESTORE_DELAY_MS 1
#define AMDGPU_RESERVE_MEM_LIMIT (3UL << 29)
-/*
- * Align VRAM availability to 2MB to avoid fragmentation caused by 4K allocations in the tail 2MB
- * BO chunk
- */
-#define VRAM_AVAILABLITY_ALIGN (1 << 21)
-
/* Impose limit on how much memory KFD can use */
static struct {
uint64_t max_system_mem_limit;
@@ -73,6 +67,8 @@ static const char * const domain_bit_to_string[] = {
#define domain_string(domain) domain_bit_to_string[ffs(domain)-1]
static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work);
+static size_t __get_available_memory(struct amdgpu_device *adev,
+ uint8_t xcp_id, int64_t vram_used);
static bool kfd_mem_is_attached(struct amdgpu_vm *avm,
struct kgd_mem *mem)
@@ -169,13 +165,8 @@ void amdgpu_amdkfd_reserve_system_mem(uint64_t size)
int amdgpu_amdkfd_reserve_mem_limit(struct amdgpu_device *adev,
uint64_t size, u32 alloc_flag, int8_t xcp_id)
{
- uint64_t reserved_for_pt =
- ESTIMATE_PT_SIZE(amdgpu_amdkfd_total_mem_size);
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- uint64_t reserved_for_ras = (con ? con->reserved_pages_in_bytes : 0);
size_t system_mem_needed, ttm_mem_needed, vram_needed;
int ret = 0;
- uint64_t vram_size = 0;
system_mem_needed = 0;
ttm_mem_needed = 0;
@@ -196,7 +187,6 @@ int amdgpu_amdkfd_reserve_mem_limit(struct amdgpu_device *adev,
if (WARN_ONCE(xcp_id < 0, "invalid XCP ID %d", xcp_id))
return -EINVAL;
- vram_size = KFD_XCP_MEMORY_SIZE(adev, xcp_id);
if (adev->apu_prefer_gtt) {
system_mem_needed = size;
ttm_mem_needed = size;
@@ -232,14 +222,11 @@ int amdgpu_amdkfd_reserve_mem_limit(struct amdgpu_device *adev,
* VRAM check since ttm_mem_limit check already cover this allocation
*/
- if (adev && xcp_id >= 0 && (!adev->apu_prefer_gtt || adev->gmc.is_app_apu)) {
- uint64_t vram_available =
- vram_size - reserved_for_pt - reserved_for_ras -
- atomic64_read(&adev->vram_pin_size);
- if (adev->kfd.vram_used[xcp_id] + vram_needed > vram_available) {
- ret = -ENOMEM;
- goto release;
- }
+ if (adev && xcp_id >= 0 && (!adev->apu_prefer_gtt || adev->gmc.is_app_apu) &&
+ vram_needed > __get_available_memory(adev, xcp_id,
+ adev->kfd.vram_used[xcp_id])) {
+ ret = -ENOMEM;
+ goto release;
}
/* Update memory accounting by decreasing available system
@@ -318,6 +305,30 @@ void amdgpu_amdkfd_release_notify(struct amdgpu_bo *bo)
kfree(bo->kfd_bo);
}
+void amdgpu_amdkfd_account_vram_pin(struct amdgpu_bo *bo, int xcp_id)
+{
+ if (!bo->kfd_bo)
+ return;
+
+ if (WARN_ONCE(xcp_id < 0, "KFD BO pinned in VRAM with no partition"))
+ return;
+
+ atomic64_add(amdgpu_bo_size(bo),
+ &amdgpu_ttm_adev(bo->tbo.bdev)->kfd.vram_pinned[xcp_id]);
+}
+
+void amdgpu_amdkfd_account_vram_unpin(struct amdgpu_bo *bo, int xcp_id)
+{
+ if (!bo->kfd_bo)
+ return;
+
+ if (WARN_ONCE(xcp_id < 0, "KFD BO unpinned in VRAM with no partition"))
+ return;
+
+ atomic64_sub(amdgpu_bo_size(bo),
+ &amdgpu_ttm_adev(bo->tbo.bdev)->kfd.vram_pinned[xcp_id]);
+}
+
/**
* create_dmamap_sg_bo() - Creates a amdgpu_bo object to reflect information
* about USERPTR or DOOREBELL or MMIO BO.
@@ -1518,6 +1529,7 @@ static int amdgpu_amdkfd_gpuvm_pin_bo(struct amdgpu_bo *bo, u32 domain)
pr_err("Error in Pinning BO to domain: %d\n", domain);
amdgpu_bo_sync_wait(bo, AMDGPU_FENCE_OWNER_KFD, false);
+
out:
amdgpu_bo_unreserve(bo);
return ret;
@@ -1540,6 +1552,7 @@ static void amdgpu_amdkfd_gpuvm_unpin_bo(struct amdgpu_bo *bo)
return;
amdgpu_bo_unpin(bo);
+
amdgpu_bo_unreserve(bo);
}
@@ -1643,8 +1656,9 @@ int amdgpu_amdkfd_criu_resume(void *p)
return ret;
}
-size_t amdgpu_amdkfd_get_available_memory(struct amdgpu_device *adev,
- uint8_t xcp_id)
+/* Caller must hold kfd_mem_limit.mem_limit_lock. */
+static size_t __get_available_memory(struct amdgpu_device *adev,
+ uint8_t xcp_id, int64_t vram_used)
{
uint64_t reserved_for_pt =
ESTIMATE_PT_SIZE(amdgpu_amdkfd_total_mem_size);
@@ -1653,14 +1667,17 @@ size_t amdgpu_amdkfd_get_available_memory(struct amdgpu_device *adev,
ssize_t available;
uint64_t vram_available, system_mem_available, ttm_mem_available;
- spin_lock(&kfd_mem_limit.mem_limit_lock);
+ lockdep_assert_held(&kfd_mem_limit.mem_limit_lock);
+
if (adev->apu_prefer_gtt && !adev->gmc.is_app_apu)
vram_available = KFD_XCP_MEMORY_SIZE(adev, xcp_id)
- - adev->kfd.vram_used_aligned[xcp_id];
+ - vram_used;
else
vram_available = KFD_XCP_MEMORY_SIZE(adev, xcp_id)
- - adev->kfd.vram_used_aligned[xcp_id]
- - atomic64_read(&adev->vram_pin_size)
+ - vram_used
+ - atomic64_read(&adev->vram_pin_size_xcp[xcp_id])
+ - atomic64_read(&adev->vram_pin_size_common)
+ + atomic64_read(&adev->kfd.vram_pinned[xcp_id])
- reserved_for_pt
- reserved_for_ras;
@@ -1675,19 +1692,29 @@ size_t amdgpu_amdkfd_get_available_memory(struct amdgpu_device *adev,
available = min3(system_mem_available, ttm_mem_available,
vram_available);
- available = ALIGN_DOWN(available, PAGE_SIZE);
} else {
- available = ALIGN_DOWN(vram_available, VRAM_AVAILABLITY_ALIGN);
+ available = vram_available;
}
- spin_unlock(&kfd_mem_limit.mem_limit_lock);
-
if (available < 0)
available = 0;
return available;
}
+size_t amdgpu_amdkfd_get_available_memory(struct amdgpu_device *adev,
+ uint8_t xcp_id)
+{
+ size_t available;
+
+ spin_lock(&kfd_mem_limit.mem_limit_lock);
+ available = __get_available_memory(adev, xcp_id,
+ adev->kfd.vram_used_aligned[xcp_id]);
+ spin_unlock(&kfd_mem_limit.mem_limit_lock);
+
+ return available;
+}
+
int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu(
struct amdgpu_device *adev, uint64_t va, uint64_t size,
void *drm_priv, struct kgd_mem **mem,
@@ -2797,6 +2824,7 @@ static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work)
struct task_struct *usertask;
struct mm_struct *mm;
uint32_t evicted_bos;
+ int ret;
mutex_lock(&process_info->notifier_lock);
evicted_bos = process_info->evicted_bos;
@@ -2816,14 +2844,16 @@ static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work)
mutex_lock(&process_info->lock);
- if (update_invalid_user_pages(process_info, mm))
+ ret = update_invalid_user_pages(process_info, mm);
+ if (ret)
goto unlock_out;
/* userptr_inval_list can be empty if all evicted userptr BOs
* have been freed. In that case there is nothing to validate
* and we can just restart the queues.
*/
if (!list_empty(&process_info->userptr_inval_list)) {
- if (validate_invalid_user_pages(process_info))
+ ret = validate_invalid_user_pages(process_info);
+ if (ret)
goto unlock_out;
}
/* Final check for concurrent evicton and atomic update. If
@@ -2832,11 +2862,14 @@ static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work)
* reference counting inside KFD will handle this case.
*/
mutex_lock(&process_info->notifier_lock);
- if (process_info->evicted_bos != evicted_bos)
+ if (process_info->evicted_bos != evicted_bos) {
+ ret = -EAGAIN;
goto unlock_notifier_out;
+ }
if (confirm_valid_user_pages_locked(process_info)) {
WARN(1, "User pages unexpectedly invalid");
+ ret = -EINVAL;
goto unlock_notifier_out;
}
@@ -2854,8 +2887,10 @@ static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work)
unlock_out:
mutex_unlock(&process_info->lock);
- /* If validation failed, reschedule another attempt */
- if (evicted_bos) {
+ /* If validation failed, reschedule another attempt, unless the
+ * device is gone for good
+ */
+ if (evicted_bos && ret != -ENODEV) {
queue_delayed_work(system_freezable_wq,
&process_info->restore_userptr_work,
msecs_to_jiffies(AMDGPU_USERPTR_RESTORE_DELAY_MS));
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index f8a2c9e41ca3..efcd2322afa6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -235,6 +235,12 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p,
size = p->chunks[i].length_dw;
+ if (size > INT_MAX / sizeof(uint32_t)) {
+ ret = -EINVAL;
+ i--;
+ goto free_partial_kdata;
+ }
+
p->chunks[i].kdata = vmemdup_array_user(u64_to_user_ptr(user_chunk.chunk_data),
size,
sizeof(uint32_t));
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 88dda7dd2cd1..2e0998de67ed 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -442,10 +442,8 @@ bool amdgpu_device_supports_boco(struct amdgpu_device *adev)
if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
return false;
- if (adev->has_pr3 ||
- ((adev->flags & AMD_IS_PX) && amdgpu_is_atpx_hybrid()))
- return true;
- return false;
+ /* BOCO needs a platform power resource (_PR3) to cut the power */
+ return adev->has_pr3;
}
/**
@@ -3591,6 +3589,12 @@ static int amdgpu_device_ip_late_init(struct amdgpu_device *adev)
amdgpu_device_set_cg_state(adev, AMD_CG_STATE_GATE);
amdgpu_device_set_pg_state(adev, AMD_PG_STATE_GATE);
+ r = amdgpu_dpm_reapply_umd_profile(adev);
+ if (r) {
+ dev_err(adev->dev, "Failed to restore UMD profile state: %d\n", r);
+ return r;
+ }
+
amdgpu_device_fill_reset_magic(adev);
r = amdgpu_device_enable_mgpu_fan_boost();
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 845ebe041563..fc2823771cc4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -2904,19 +2904,6 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev)
return 0;
}
-static void amdgpu_restore_umd_profile_pstate_after_runpm(struct amdgpu_device *adev)
-{
- enum amd_dpm_forced_level level;
- uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD |
- AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK |
- AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK |
- AMD_DPM_FORCED_LEVEL_PROFILE_PEAK;
-
- level = amdgpu_dpm_get_performance_level(adev);
- if (level & profile_mode_mask)
- amdgpu_asic_update_umd_stable_pstate(adev, true);
-}
-
static int amdgpu_pmops_runtime_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -2962,7 +2949,6 @@ static int amdgpu_pmops_runtime_resume(struct device *dev)
if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX)
drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
- amdgpu_restore_umd_profile_pstate_after_runpm(adev);
adev->in_runpm = false;
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c
index db2bb5cb671d..fa3676c5babc 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c
@@ -173,7 +173,6 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier,
const u64 max_bytes = SZ_2G;
struct hmm_range *hmm_range;
- unsigned long timeout;
unsigned long *pfns;
unsigned long end;
int r;
@@ -204,15 +203,9 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier,
pr_debug("hmm range: start = 0x%lx, end = 0x%lx",
hmm_range->start, hmm_range->end);
- timeout = jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
-
-retry:
r = hmm_range_fault(hmm_range);
- if (unlikely(r)) {
- if (r == -EBUSY && !time_after(jiffies, timeout))
- goto retry;
+ if (unlikely(r))
goto out_free_pfns;
- }
if (hmm_range->end == end)
break;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index cdab2c098cfa..c2f13584a5a3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -917,6 +917,19 @@ void amdgpu_bo_unref(struct amdgpu_bo **bo)
*bo = NULL;
}
+/* Map a BO onto vram_pin_size_xcp[]. bo->xcp_id < 0 means "any partition":
+ * such pins are not owned by a single partition and go to
+ * vram_pin_size_common, which every partition subtracts.
+ */
+static int amdgpu_bo_get_xcp(struct amdgpu_bo *bo)
+{
+ s8 xcp = bo->xcp_id;
+
+ if (xcp >= 0 && xcp < MAX_XCP)
+ return xcp;
+ return -1;
+}
+
/**
* amdgpu_bo_pin - pin an &amdgpu_bo buffer object
* @bo: &amdgpu_bo buffer object to be pinned
@@ -997,7 +1010,17 @@ int amdgpu_bo_pin(struct amdgpu_bo *bo, u32 domain)
ttm_bo_pin(&bo->tbo);
if (bo->tbo.resource->mem_type == TTM_PL_VRAM) {
+ int xcp = amdgpu_bo_get_xcp(bo);
+
atomic64_add(amdgpu_bo_size(bo), &adev->vram_pin_size);
+ if (xcp >= 0) {
+ atomic64_add(amdgpu_bo_size(bo),
+ &adev->vram_pin_size_xcp[xcp]);
+ } else {
+ atomic64_add(amdgpu_bo_size(bo),
+ &adev->vram_pin_size_common);
+ }
+ amdgpu_amdkfd_account_vram_pin(bo, xcp);
atomic64_add(amdgpu_vram_mgr_bo_visible_size(bo),
&adev->visible_pin_size);
} else if (bo->tbo.resource->mem_type == TTM_PL_TT) {
@@ -1030,7 +1053,17 @@ void amdgpu_bo_unpin(struct amdgpu_bo *bo)
dma_buf_unpin(bo->tbo.base.import_attach);
if (bo->tbo.resource->mem_type == TTM_PL_VRAM) {
+ int xcp = amdgpu_bo_get_xcp(bo);
+
atomic64_sub(amdgpu_bo_size(bo), &adev->vram_pin_size);
+ if (xcp >= 0) {
+ atomic64_sub(amdgpu_bo_size(bo),
+ &adev->vram_pin_size_xcp[xcp]);
+ } else {
+ atomic64_sub(amdgpu_bo_size(bo),
+ &adev->vram_pin_size_common);
+ }
+ amdgpu_amdkfd_account_vram_unpin(bo, xcp);
atomic64_sub(amdgpu_vram_mgr_bo_visible_size(bo),
&adev->visible_pin_size);
} else if (bo->tbo.resource->mem_type == TTM_PL_TT) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 08c31d78b3fa..918f38965cf2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -766,7 +766,8 @@ int amdgpu_ttm_tt_get_user_pages(struct amdgpu_bo *bo,
goto out_unlock;
}
if (unlikely((gtt->userflags & AMDGPU_GEM_USERPTR_ANONONLY) &&
- vma->vm_file)) {
+ (vma->vm_file ||
+ vma->vm_end < start + (u64)ttm->num_pages * PAGE_SIZE))) {
r = -EPERM;
goto out_unlock;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index f78ac6777ee5..c9614a99b40d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -2720,10 +2720,7 @@ int amdgpu_vm_make_compute(struct amdgpu_device *adev, struct amdgpu_vm *vm)
if (vm->use_cpu_for_update) {
/* Sync with last SDMA update/clear before switching to CPU */
- r = amdgpu_bo_sync_wait(vm->root.bo,
- AMDGPU_FENCE_OWNER_UNDEFINED, true);
- if (r)
- goto unreserve_bo;
+ dma_fence_wait(vm->last_update, false);
vm->update_funcs = &amdgpu_vm_cpu_funcs;
r = amdgpu_vm_pt_map_tables(adev, vm);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c
index f794fb1cc06e..a05f34259d57 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c
@@ -421,7 +421,7 @@ int amdgpu_vm_pt_clear(struct amdgpu_device *adev, struct amdgpu_vm *vm,
if (r)
goto exit;
- r = vm->update_funcs->commit(¶ms, NULL);
+ r = vm->update_funcs->commit(¶ms, &vm->last_update);
exit:
drm_dev_exit(idx);
return r;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c
index 36805dcfa159..902d0a538e41 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c
@@ -254,6 +254,11 @@ static int amdgpu_vm_sdma_update(struct amdgpu_vm_update_params *p,
AMDGPU_KERNEL_JOB_ID_VM_UPDATE);
if (r)
return r;
+
+ /* The estimate above describes the job which was just
+ * submitted, take the budget of the newly allocated one.
+ */
+ ndw = p->num_dw_left;
}
if (!p->pages_addr) {
diff --git a/drivers/gpu/drm/amd/amdgpu/cik.c b/drivers/gpu/drm/amd/amdgpu/cik.c
index 9cd63b4177bf..4bc35b5f7c47 100644
--- a/drivers/gpu/drm/amd/amdgpu/cik.c
+++ b/drivers/gpu/drm/amd/amdgpu/cik.c
@@ -1932,6 +1932,16 @@ static bool cik_need_reset_on_init(struct amdgpu_device *adev)
{
u32 clock_cntl, pc;
+ /*
+ * MacBookPros with switchable graphics put VRAM at 0 when the iGPU
+ * is enabled. Any later post (S3 resume, GPU reset) moves it back to
+ * the vbios default, so reset and post on load to start with the
+ * vbios layout.
+ */
+ if (adev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE &&
+ !(RREG32(mmMC_VM_FB_LOCATION) & MC_VM_FB_LOCATION__FB_BASE_MASK))
+ return true;
+
if (adev->flags & AMD_IS_APU)
return false;
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
index 0f0d6cd52d91..230a346db3b1 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
@@ -2186,10 +2186,22 @@ static int gmc_v9_0_hw_init(struct amdgpu_ip_block *ip_block)
* (type 2), which flushes both. Due to a race condition with
* concurrent memory accesses using the same TLB cache line, we still
* need a second TLB flush after this.
+ *
+ * GC 9.4.3, 9.4.4 and 9.5.0 hit a similar race.
*/
- adev->gmc.flush_tlb_needs_extra_type_2 =
- amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 0) &&
- adev->gmc.xgmi.num_physical_nodes;
+ switch (amdgpu_ip_version(adev, GC_HWIP, 0)) {
+ case IP_VERSION(9, 4, 0):
+ adev->gmc.flush_tlb_needs_extra_type_2 =
+ adev->gmc.xgmi.num_physical_nodes;
+ break;
+ case IP_VERSION(9, 4, 3):
+ case IP_VERSION(9, 4, 4):
+ case IP_VERSION(9, 5, 0):
+ adev->gmc.flush_tlb_needs_extra_type_2 = true;
+ break;
+ default:
+ break;
+ }
/* The sequence of these two function calls matters.*/
gmc_v9_0_init_golden_registers(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/si.c b/drivers/gpu/drm/amd/amdgpu/si.c
index e0f139de7991..0c96a40fe4b4 100644
--- a/drivers/gpu/drm/amd/amdgpu/si.c
+++ b/drivers/gpu/drm/amd/amdgpu/si.c
@@ -1524,6 +1524,16 @@ static bool si_need_full_reset(struct amdgpu_device *adev)
static bool si_need_reset_on_init(struct amdgpu_device *adev)
{
+ /*
+ * MacBookPros with switchable graphics put VRAM at 0 when the iGPU
+ * is enabled. Any later post (S3 resume, GPU reset) moves it back to
+ * the vbios default, so reset and post on load to start with the
+ * vbios layout.
+ */
+ if (adev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE &&
+ !(RREG32(mmMC_VM_FB_LOCATION) & MC_VM_FB_LOCATION__FB_BASE_MASK))
+ return true;
+
return false;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c
index ce54e2938107..bb8125131908 100644
--- a/drivers/gpu/drm/amd/amdgpu/vi.c
+++ b/drivers/gpu/drm/amd/amdgpu/vi.c
@@ -1425,6 +1425,16 @@ static bool vi_need_reset_on_init(struct amdgpu_device *adev)
unsigned int i;
u32 clock_cntl, pc;
+ /*
+ * MacBookPros with switchable graphics put VRAM at 0 when the iGPU
+ * is enabled. Any later post (S3 resume, GPU reset) moves it back to
+ * the vbios default, so reset and post on load to start with the
+ * vbios layout.
+ */
+ if (adev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE &&
+ !(RREG32(mmMC_VM_FB_LOCATION) & MC_VM_FB_LOCATION__FB_BASE_MASK))
+ return true;
+
for (i = 0; i < ARRAY_SIZE(vi_reset_quirks); i++)
if (adev->pdev->device == vi_reset_quirks[i].device &&
adev->pdev->subsystem_vendor == vi_reset_quirks[i].subsystem_vendor &&
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index 04c2d412fa7d..e4a6100d036a 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -352,12 +352,10 @@ static int kfd_ioctl_create_queue(struct file *filep, struct kfd_process *p,
}
}
- if (!pdd->qpd.proc_doorbells) {
- err = kfd_alloc_process_doorbells(dev->kfd, pdd);
- if (err) {
- pr_debug("failed to allocate process doorbells\n");
- goto err_bind_process;
- }
+ err = kfd_alloc_process_doorbells(dev->kfd, pdd);
+ if (err) {
+ pr_debug("failed to allocate process doorbells\n");
+ goto err_bind_process;
}
err = kfd_queue_acquire_buffers(pdd, &q_properties);
@@ -1037,11 +1035,19 @@ static int kfd_ioctl_get_available_memory(struct file *filep,
{
struct kfd_ioctl_get_available_memory_args *args = data;
struct kfd_process_device *pdd = kfd_lock_pdd_by_id(p, args->gpu_id);
+ struct amdgpu_device *adev;
+ size_t available;
if (!pdd)
return -EINVAL;
- args->available = amdgpu_amdkfd_get_available_memory(pdd->dev->adev,
- pdd->dev->node_id);
+
+ adev = pdd->dev->adev;
+ available = amdgpu_amdkfd_get_available_memory(adev, pdd->dev->node_id);
+ if (adev->apu_prefer_gtt)
+ args->available = ALIGN_DOWN(available, PAGE_SIZE);
+ else
+ args->available = ALIGN_DOWN(available, VRAM_AVAILABLITY_ALIGN);
+
kfd_unlock_pdd(pdd);
return 0;
}
@@ -1224,6 +1230,9 @@ static int kfd_ioctl_free_memory_of_gpu(struct file *filep,
goto err_unlock;
}
+ /* Queues a reset could not stop may still access the buffer */
+ kfd_process_wait_gpu_reset_complete(p);
+
ret = amdgpu_amdkfd_gpuvm_free_memory_of_gpu(pdd->dev->adev,
(struct kgd_mem *)mem, pdd->drm_priv, &size);
@@ -2282,11 +2291,9 @@ static int criu_restore_devices(struct kfd_process *p,
goto exit;
}
- if (!pdd->qpd.proc_doorbells) {
- ret = kfd_alloc_process_doorbells(dev->kfd, pdd);
- if (ret)
- goto exit;
- }
+ ret = kfd_alloc_process_doorbells(dev->kfd, pdd);
+ if (ret)
+ goto exit;
}
/*
@@ -2696,6 +2703,7 @@ static int criu_process_info(struct file *filep,
struct kfd_process *p,
struct kfd_ioctl_criu_args *args)
{
+ bool paused_here = false;
int ret = 0;
mutex_lock(&p->mutex);
@@ -2706,11 +2714,15 @@ static int criu_process_info(struct file *filep,
goto err_unlock;
}
- ret = kfd_process_evict_queues(p, KFD_QUEUE_EVICTION_CRIU_CHECKPOINT);
- if (ret)
- goto err_unlock;
+ /* UNPAUSE restores the queues once, so evict them only once */
+ if (!p->queues_paused) {
+ ret = kfd_process_evict_queues(p, KFD_QUEUE_EVICTION_CRIU_CHECKPOINT);
+ if (ret)
+ goto err_unlock;
- p->queues_paused = true;
+ p->queues_paused = true;
+ paused_here = true;
+ }
args->pid = task_pid_nr_ns(p->lead_thread,
task_active_pid_ns(p->lead_thread));
@@ -2725,7 +2737,7 @@ static int criu_process_info(struct file *filep,
args->priv_data_size);
err_unlock:
- if (ret) {
+ if (ret && paused_here) {
kfd_process_restore_queues(p);
p->queues_paused = false;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c
index b0e87bdc2911..ee6c17e88976 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c
@@ -2051,27 +2051,33 @@ static void kfd_find_numa_node_in_srat(struct kfd_node *kdev)
return;
}
+ if (table_header->length < sizeof(struct acpi_table_srat))
+ goto invalid_srat;
+
table_end = (unsigned long)table_header + table_header->length;
/* Parse all entries looking for a match. */
sub_header = (struct acpi_subtable_header *)
((unsigned long)table_header +
sizeof(struct acpi_table_srat));
- subtable_len = sub_header->length;
- while (((unsigned long)sub_header) + subtable_len < table_end) {
- /*
- * If length is 0, break from this loop to avoid
- * infinite loop.
- */
- if (subtable_len == 0) {
- pr_err("SRAT invalid zero length\n");
- break;
- }
+ while ((unsigned long)sub_header < table_end) {
+ unsigned long start = (unsigned long)sub_header;
+
+ if (table_end - start < sizeof(*sub_header))
+ goto invalid_srat;
+
+ subtable_len = sub_header->length;
+ if (subtable_len < sizeof(*sub_header) ||
+ subtable_len > table_end - start)
+ goto invalid_srat;
switch (sub_header->type) {
case ACPI_SRAT_TYPE_CPU_AFFINITY:
cpu = (struct acpi_srat_cpu_affinity *)sub_header;
+ if (subtable_len < sizeof(*cpu))
+ goto invalid_srat;
+
pxm = *((u32 *)cpu->proximity_domain_hi) << 8 |
cpu->proximity_domain_lo;
if (pxm > max_pxm)
@@ -2079,6 +2085,9 @@ static void kfd_find_numa_node_in_srat(struct kfd_node *kdev)
break;
case ACPI_SRAT_TYPE_GENERIC_AFFINITY:
gpu = (struct acpi_srat_generic_affinity *)sub_header;
+ if (subtable_len < sizeof(*gpu))
+ goto invalid_srat;
+
bdf = *((u16 *)(&gpu->device_handle[0])) << 16 |
*((u16 *)(&gpu->device_handle[2]));
if (bdf == pci_id) {
@@ -2095,9 +2104,13 @@ static void kfd_find_numa_node_in_srat(struct kfd_node *kdev)
sub_header = (struct acpi_subtable_header *)
((unsigned long)sub_header + subtable_len);
- subtable_len = sub_header->length;
}
+ goto out;
+
+invalid_srat:
+ pr_err("SRAT contains an invalid subtable\n");
+out:
acpi_put_table(table_header);
/* Workaround bad cpu-gpu binding case */
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c
index fdcf7f2d1b5b..6af268a031f2 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c
@@ -239,11 +239,9 @@ phys_addr_t kfd_get_process_doorbells(struct kfd_process_device *pdd)
struct amdgpu_device *adev = pdd->dev->adev;
uint32_t first_db_index;
- if (!pdd->qpd.proc_doorbells) {
- if (kfd_alloc_process_doorbells(pdd->dev->kfd, pdd))
- /* phys_addr_t 0 is error */
- return 0;
- }
+ if (kfd_alloc_process_doorbells(pdd->dev->kfd, pdd))
+ /* phys_addr_t 0 is error */
+ return 0;
first_db_index = amdgpu_doorbell_index_on_bar(adev,
pdd->qpd.proc_doorbells,
@@ -257,12 +255,21 @@ int kfd_alloc_process_doorbells(struct kfd_dev *kfd, struct kfd_process_device *
int r;
struct qcm_process_device *qpd = &pdd->qpd;
+ mutex_lock(&pdd->doorbell_mutex);
+
+ /* Another thread may have won the check-and-allocate race */
+ if (qpd->proc_doorbells) {
+ mutex_unlock(&pdd->doorbell_mutex);
+ return 0;
+ }
+
/* Allocate bitmap for dynamic doorbell allocation */
qpd->doorbell_bitmap = bitmap_zalloc(KFD_MAX_NUM_OF_QUEUES_PER_PROCESS,
GFP_KERNEL);
if (!qpd->doorbell_bitmap) {
DRM_ERROR("Failed to allocate process doorbell bitmap\n");
- return -ENOMEM;
+ r = -ENOMEM;
+ goto unlock;
}
r = init_doorbell_bitmap(&pdd->qpd, kfd);
@@ -284,11 +291,15 @@ int kfd_alloc_process_doorbells(struct kfd_dev *kfd, struct kfd_process_device *
DRM_ERROR("Failed to allocate process doorbells\n");
goto err;
}
+
+ mutex_unlock(&pdd->doorbell_mutex);
return 0;
err:
bitmap_free(qpd->doorbell_bitmap);
qpd->doorbell_bitmap = NULL;
+unlock:
+ mutex_unlock(&pdd->doorbell_mutex);
return r;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
index f2cda82cbed4..1f5f52818898 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
@@ -769,6 +769,9 @@ struct kfd_process_device {
/* per-process-per device QCM data structure */
struct qcm_process_device qpd;
+ /* Serializes process doorbell allocation for this device */
+ struct mutex doorbell_mutex;
+
/*Apertures*/
uint64_t lds_base;
uint64_t lds_limit;
@@ -1081,6 +1084,7 @@ void kfd_unref_process(struct kfd_process *p);
int kfd_process_evict_queues(struct kfd_process *p, uint32_t trigger);
int kfd_process_restore_queues(struct kfd_process *p);
void kfd_suspend_all_processes(void);
+void kfd_process_wait_gpu_reset_complete(struct kfd_process *p);
int kfd_resume_all_processes(void);
struct kfd_process_device *kfd_process_device_data_by_id(struct kfd_process *process,
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
index 8ed513a77d38..1c591953cc80 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
@@ -1089,6 +1089,7 @@ static void kfd_process_destroy_pdds(struct kfd_process *p)
}
atomic_dec(&pdd->dev->kfd->kfd_processes_count);
+ mutex_destroy(&pdd->doorbell_mutex);
kfree(pdd);
p->pdds[i] = NULL;
@@ -1143,7 +1144,7 @@ static void kfd_process_remove_sysfs(struct kfd_process *p)
/*
* If any GPU is ongoing reset, wait for reset complete.
*/
-static void kfd_process_wait_gpu_reset_complete(struct kfd_process *p)
+void kfd_process_wait_gpu_reset_complete(struct kfd_process *p)
{
int i;
@@ -1628,6 +1629,7 @@ struct kfd_process_device *kfd_create_process_device_data(struct kfd_node *dev,
pdd->dev = dev;
INIT_LIST_HEAD(&pdd->qpd.queues_list);
INIT_LIST_HEAD(&pdd->qpd.priv_queue_list);
+ mutex_init(&pdd->doorbell_mutex);
pdd->qpd.dqm = dev->dqm;
pdd->qpd.pqm = &p->pqm;
pdd->qpd.evicted = 0;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
index 6f06516da32a..eaa284e55182 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
@@ -231,6 +231,15 @@ void kfd_queue_buffer_put(struct amdgpu_bo **bo)
amdgpu_bo_unref(bo);
}
+static void kfd_queue_put_bos(struct queue_properties *properties)
+{
+ kfd_queue_buffer_put(&properties->wptr_bo);
+ kfd_queue_buffer_put(&properties->rptr_bo);
+ kfd_queue_buffer_put(&properties->ring_bo);
+ kfd_queue_buffer_put(&properties->eop_buf_bo);
+ kfd_queue_buffer_put(&properties->cwsr_bo);
+}
+
int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_properties *properties)
{
struct kfd_topology_device *topo_dev;
@@ -341,7 +350,13 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope
* dropping the VM reservation.
*/
kfd_queue_unref_bo_vas(pdd, properties);
- kfd_queue_release_buffers(pdd, properties);
+ /*
+ * The SVM CWSR range is the last thing acquired and a failed
+ * kfd_queue_buffer_svm_get() takes nothing, so only the BOs are held
+ * here. Dropping the SVM reference would drop the one of another queue
+ * using the same CWSR address.
+ */
+ kfd_queue_put_bos(properties);
return err;
}
@@ -350,11 +365,7 @@ int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_prope
struct kfd_topology_device *topo_dev;
u64 total_cwsr_size;
- kfd_queue_buffer_put(&properties->wptr_bo);
- kfd_queue_buffer_put(&properties->rptr_bo);
- kfd_queue_buffer_put(&properties->ring_bo);
- kfd_queue_buffer_put(&properties->eop_buf_bo);
- kfd_queue_buffer_put(&properties->cwsr_bo);
+ kfd_queue_put_bos(properties);
topo_dev = kfd_topology_device_by_id(pdd->dev->id);
if (!topo_dev)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
index 1b29dee7948d..cd2cfb9a3bae 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
@@ -987,9 +987,12 @@ svm_range_split_pages(struct svm_range *new, struct svm_range *old,
uint64_t start, uint64_t last)
{
uint64_t npages = last - start + 1;
+ bool has_dma_addr = false;
int i, r;
for (i = 0; i < MAX_GPU_INSTANCE; i++) {
+ if (old->dma_addr[i])
+ has_dma_addr = true;
r = svm_range_split_array(&new->dma_addr[i], &old->dma_addr[i],
sizeof(*old->dma_addr[i]), old->start,
npages, new->start, new->npages,
@@ -997,8 +1000,16 @@ svm_range_split_pages(struct svm_range *new, struct svm_range *old,
if (r)
return r;
}
- if (old->actual_loc)
+ if (old->actual_loc) {
+ /*
+ * Pages migrated by prefetch have no dma_addr until they are
+ * mapped on a GPU. A fully resident range still splits exactly,
+ * old->npages isn't adjusted yet here.
+ */
+ if (!has_dma_addr && old->vram_pages == old->npages)
+ new->vram_pages = new->npages;
old->vram_pages -= new->vram_pages;
+ }
return 0;
}
@@ -1853,7 +1864,7 @@ static void svm_range_restore_work(struct work_struct *work)
struct mm_struct *mm;
int evicted_ranges;
int invalid;
- int r;
+ int r = 0;
svms = container_of(dwork, struct svm_range_list, restore_work);
evicted_ranges = atomic_read(&svms->evicted_ranges);
@@ -1927,8 +1938,10 @@ static void svm_range_restore_work(struct work_struct *work)
mmap_write_unlock(mm);
mutex_unlock(&process_info->lock);
- /* If validation failed, reschedule another attempt */
- if (evicted_ranges) {
+ /* If validation failed, reschedule another attempt, unless the
+ * device is gone for good
+ */
+ if (evicted_ranges && r != -ENODEV) {
pr_debug("reschedule to restore svm range\n");
queue_delayed_work(system_freezable_wq, &svms->restore_work,
msecs_to_jiffies(AMDGPU_SVM_RANGE_RESTORE_DELAY_MS));
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 bb1fae39cd39..a77b55ff9321 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -10550,8 +10550,6 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_state *state,
if (dm_old_crtc_state->stream)
remove_stream(adev, acrtc, dm_old_crtc_state->stream);
- pm_runtime_get_noresume(dev->dev);
-
acrtc->enabled = true;
acrtc->hw_mode = new_crtc_state->mode;
crtc->hwmode = new_crtc_state->mode;
@@ -11024,8 +11022,13 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
#ifdef CONFIG_DEBUG_FS
enum amdgpu_dm_pipe_crc_source cur_crc_src;
#endif
- /* Count number of newly disabled CRTCs for dropping PM refs later. */
- if (old_crtc_state->active && !new_crtc_state->active)
+ /*
+ * One runtime PM reference per active CRTC: take it when the
+ * CRTC turns on, count it when it turns off for dropping later.
+ */
+ if (!old_crtc_state->active && new_crtc_state->active)
+ pm_runtime_get_noresume(dev->dev);
+ else if (old_crtc_state->active && !new_crtc_state->active)
crtc_disable_count++;
dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
index 7633af242919..e8f4e7b7af57 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
@@ -225,7 +225,7 @@ fill_dc_mst_payload_table_from_drm(struct dc_link *link,
break;
}
}
- ASSERT(i != copy_of_link_table.stream_count);
+ /* Payload absent from HW table when never allocated: nothing to remove. */
}
/* Fill payload info*/
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dsc/rc_calc_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dsc/rc_calc_fpu.c
index ef75eb7d5adc..940de620e330 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dsc/rc_calc_fpu.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dsc/rc_calc_fpu.c
@@ -46,7 +46,7 @@ static int median3(int a, int b, int c)
if (b > c)
swap(b, c);
if (a > b)
- swap(b, c);
+ swap(a, b);
return b;
}
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
index d2a237fcf5b0..ccc7ce0db2e1 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
@@ -10921,6 +10921,8 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex
calculate_tdlut_setting_params->cursor_buffer_size = mode_lib->ip.cursor_buffer_size;
calculate_tdlut_setting_params->gpuvm_enable = display_cfg->gpuvm_enable;
calculate_tdlut_setting_params->gpuvm_page_size_kbytes = display_cfg->plane_descriptors[k].overrides.gpuvm_min_page_size_kbytes;
+ calculate_tdlut_setting_params->tdlut_mpc_width_flag = display_cfg->plane_descriptors[k].tdlut.tdlut_mpc_width_flag;
+ calculate_tdlut_setting_params->is_gfx11 = dml_get_gfx_version(display_cfg->plane_descriptors[k].surface.tiling);
// output
calculate_tdlut_setting_params->tdlut_pte_bytes_per_frame = &s->tdlut_pte_bytes_per_frame[k];
diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c
index a71df052cf25..7103d8cfb479 100644
--- a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c
+++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c
@@ -1694,7 +1694,8 @@ bool mod_color_calculate_degamma_params(struct dc_color_caps *dc_caps,
return true;
if (dc_caps != NULL &&
- dc_caps->dpp.dcn_arch == 1) {
+ dc_caps->dpp.dcn_arch == 1 &&
+ !map_user_ramp) {
if (input_tf->tf == TRANSFER_FUNCTION_PQ &&
dc_caps->dpp.dgam_rom_caps.pq == 1)
diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
index 2b0cdb2a2775..834ce6d1a40e 100644
--- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h
+++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
@@ -426,6 +426,8 @@ struct amd_pm_funcs {
int (*print_clock_levels)(void *handle, enum pp_clock_type type, char *buf);
int (*emit_clock_levels)(void *handle, enum pp_clock_type type, char *buf, int *offset);
int (*force_performance_level)(void *handle, enum amd_dpm_forced_level level);
+ int (*reapply_performance_level)(void *handle,
+ enum amd_dpm_forced_level level);
int (*get_sclk_od)(void *handle);
int (*set_sclk_od)(void *handle, uint32_t value);
int (*get_mclk_od)(void *handle);
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index d1c4d7f4accc..402354846c56 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -1085,6 +1085,91 @@ static void amdgpu_dpm_exit_umd_state(struct amdgpu_device *adev)
AMD_PG_STATE_GATE);
}
+/**
+ * amdgpu_dpm_reapply_umd_profile - Restore the active UMD profile
+ * @adev: amdgpu device
+ *
+ * Resume and reset retain the selected DPM level in software, but the
+ * underlying GFX and PMFW state is lost. Reapply a retained UMD profile
+ * after the common resume path has restored its default CG and PG state.
+ */
+int amdgpu_dpm_reapply_umd_profile(struct amdgpu_device *adev)
+{
+ const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
+ enum amd_dpm_forced_level level;
+ int ret, fallback_ret;
+
+ if (!pp_funcs || !pp_funcs->reapply_performance_level ||
+ !pp_funcs->force_performance_level)
+ return 0;
+
+ /* Serialize the retained level with the GFX and PMFW reprogramming. */
+ mutex_lock(&adev->pm.mutex);
+ if (adev->pm.dpm.thermal_active) {
+ dev_warn(adev->dev,
+ "Cannot restore UMD profile while thermal control is active\n");
+ ret = 0;
+ goto unlock;
+ }
+
+ if (pp_funcs->get_performance_level)
+ level = pp_funcs->get_performance_level(adev->powerplay.pp_handle);
+ else
+ level = adev->pm.dpm.forced_level;
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ break;
+ default:
+ ret = 0;
+ goto unlock;
+ }
+
+ /* The active profile already owns the GFX PG reference. */
+ ret = amdgpu_device_ip_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_GFX,
+ AMD_CG_STATE_UNGATE);
+ if (ret)
+ goto fallback_auto;
+
+ ret = amdgpu_asic_update_umd_stable_pstate(adev, true);
+ if (ret)
+ goto fallback_auto;
+
+ ret = pp_funcs->reapply_performance_level(adev->powerplay.pp_handle,
+ level);
+ if (ret)
+ goto fallback_auto;
+
+unlock:
+ mutex_unlock(&adev->pm.mutex);
+
+ return ret;
+
+fallback_auto:
+ dev_warn(adev->dev, "Failed to restore UMD profile state: %d\n", ret);
+
+ /* Restore normal CG before releasing the profile's PG reference. */
+ amdgpu_asic_update_umd_stable_pstate(adev, false);
+ amdgpu_device_ip_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_GFX,
+ AMD_CG_STATE_GATE);
+ mutex_unlock(&adev->pm.mutex);
+
+ mutex_lock(&adev->pm.stable_pstate_ctx_lock);
+ fallback_ret = amdgpu_dpm_force_performance_level(adev,
+ AMD_DPM_FORCED_LEVEL_AUTO);
+ if (fallback_ret)
+ dev_warn(adev->dev, "Failed to fall back to AUTO performance level: %d\n",
+ fallback_ret);
+ else
+ adev->pm.stable_pstate_ctx = NULL;
+ mutex_unlock(&adev->pm.stable_pstate_ctx_lock);
+
+ return 0;
+}
+
int amdgpu_dpm_force_performance_level(struct amdgpu_device *adev,
enum amd_dpm_forced_level level)
{
diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
index 13a834575ed7..db78f44da050 100644
--- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
+++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
@@ -482,6 +482,7 @@ void amdgpu_dpm_set_power_state(struct amdgpu_device *adev,
enum amd_dpm_forced_level amdgpu_dpm_get_performance_level(struct amdgpu_device *adev);
int amdgpu_dpm_force_performance_level(struct amdgpu_device *adev,
enum amd_dpm_forced_level level);
+int amdgpu_dpm_reapply_umd_profile(struct amdgpu_device *adev);
int amdgpu_dpm_get_pp_num_states(struct amdgpu_device *adev,
struct pp_states_info *states);
int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev,
diff --git a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
index 05c5e7d19ca1..4edca695e73a 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
@@ -367,24 +367,46 @@ static void pp_dpm_en_umd_pstate(struct pp_hwmgr *hwmgr,
}
}
-static int pp_dpm_force_performance_level(void *handle,
- enum amd_dpm_forced_level level)
+static int pp_dpm_apply_performance_level(struct pp_hwmgr *hwmgr,
+ enum amd_dpm_forced_level level,
+ bool reapply)
{
- struct pp_hwmgr *hwmgr = handle;
+ int ret;
if (!hwmgr || !hwmgr->pm_en)
return -EINVAL;
- if (level == hwmgr->dpm_level)
+ if (!reapply && level == hwmgr->dpm_level)
return 0;
pp_dpm_en_umd_pstate(hwmgr, &level);
hwmgr->request_dpm_level = level;
- hwmgr_handle_task(hwmgr, AMD_PP_TASK_READJUST_POWER_STATE, NULL);
+ ret = hwmgr_handle_task(hwmgr, AMD_PP_TASK_READJUST_POWER_STATE, NULL);
+ if (ret)
+ return ret;
+
+ ret = phm_force_dpm_levels(hwmgr, level);
+ if (ret)
+ return ret;
+
+ hwmgr->dpm_level = level;
return 0;
}
+static int pp_dpm_force_performance_level(void *handle,
+ enum amd_dpm_forced_level level)
+{
+ return pp_dpm_apply_performance_level(handle, level, false);
+}
+
+static int
+pp_dpm_reapply_performance_level(void *handle,
+ enum amd_dpm_forced_level level)
+{
+ return pp_dpm_apply_performance_level(handle, level, true);
+}
+
static enum amd_dpm_forced_level pp_dpm_get_performance_level(
void *handle)
{
@@ -1569,6 +1591,7 @@ static const struct amd_pm_funcs pp_dpm_funcs = {
.load_firmware = pp_dpm_load_fw,
.wait_for_fw_loading_complete = pp_dpm_fw_loading_complete,
.force_performance_level = pp_dpm_force_performance_level,
+ .reapply_performance_level = pp_dpm_reapply_performance_level,
.get_performance_level = pp_dpm_get_performance_level,
.get_current_power_state = pp_dpm_get_current_power_state,
.dispatch_tasks = pp_dpm_dispatch_tasks,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index e0e208a0d9f9..0df4b36ec3a8 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -2314,6 +2314,13 @@ static int smu_set_powergating_state(struct amdgpu_ip_block *ip_block,
return 0;
}
+static void smu_enter_umd_pstate(struct smu_context *smu)
+{
+ smu_gpo_control(smu, false);
+ smu_gfx_ulv_control(smu, false);
+ smu_deep_sleep_control(smu, false);
+}
+
static int smu_enable_umd_pstate(void *handle,
enum amd_dpm_forced_level *level)
{
@@ -2332,9 +2339,7 @@ static int smu_enable_umd_pstate(void *handle,
/* enter umd pstate, save current level, disable gfx cg*/
if (*level & profile_mode_mask) {
smu_dpm_ctx->saved_dpm_level = smu_dpm_ctx->dpm_level;
- smu_gpo_control(smu, false);
- smu_gfx_ulv_control(smu, false);
- smu_deep_sleep_control(smu, false);
+ smu_enter_umd_pstate(smu);
amdgpu_asic_update_umd_stable_pstate(smu->adev, true);
}
} else {
@@ -2393,7 +2398,8 @@ static void smu_power_profile_mode_put(struct smu_context *smu,
static int smu_adjust_power_state_dynamic(struct smu_context *smu,
enum amd_dpm_forced_level level,
- bool skip_display_settings)
+ bool skip_display_settings,
+ bool force_performance_level)
{
int ret = 0;
struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm);
@@ -2420,7 +2426,7 @@ static int smu_adjust_power_state_dynamic(struct smu_context *smu,
}
}
- if (smu_dpm_ctx->dpm_level != level) {
+ if (force_performance_level || smu_dpm_ctx->dpm_level != level) {
ret = smu_asic_set_performance_level(smu, level);
if (ret) {
if (ret == -EOPNOTSUPP)
@@ -2443,6 +2449,39 @@ static int smu_adjust_power_state_dynamic(struct smu_context *smu,
return ret;
}
+static int
+smu_reapply_power_state_dynamic(struct smu_context *smu,
+ enum amd_dpm_forced_level level)
+{
+ struct smu_dpm_context *smu_dpm_ctx = &smu->smu_dpm;
+ int ret;
+
+ ret = smu_apply_clocks_adjust_rules(smu);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to apply clocks adjust rules!");
+ return ret;
+ }
+
+ ret = smu_asic_set_performance_level(smu, level);
+ if (ret) {
+ if (ret == -EOPNOTSUPP)
+ dev_info(smu->adev->dev, "set performance level %d not supported",
+ level);
+ else
+ dev_err(smu->adev->dev, "Failed to set performance level %d",
+ level);
+ return ret;
+ }
+
+ smu_dpm_ctx->dpm_level = level;
+
+ if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_MANUAL ||
+ smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM)
+ return 0;
+
+ return smu_bump_power_profile_mode(smu, NULL, 0);
+}
+
static int smu_handle_task(struct smu_context *smu,
enum amd_dpm_forced_level level,
enum amd_pp_task task_id)
@@ -2457,13 +2496,13 @@ static int smu_handle_task(struct smu_context *smu,
ret = smu_pre_display_config_changed(smu);
if (ret)
return ret;
- ret = smu_adjust_power_state_dynamic(smu, level, false);
+ ret = smu_adjust_power_state_dynamic(smu, level, false, false);
break;
case AMD_PP_TASK_COMPLETE_INIT:
- ret = smu_adjust_power_state_dynamic(smu, level, true);
+ ret = smu_adjust_power_state_dynamic(smu, level, true, false);
break;
case AMD_PP_TASK_READJUST_POWER_STATE:
- ret = smu_adjust_power_state_dynamic(smu, level, true);
+ ret = smu_adjust_power_state_dynamic(smu, level, true, false);
break;
default:
break;
@@ -2592,6 +2631,37 @@ static int smu_force_performance_level(void *handle,
return ret;
}
+static int
+smu_reapply_performance_level(void *handle,
+ enum amd_dpm_forced_level level)
+{
+ struct smu_context *smu = handle;
+ struct smu_dpm_context *smu_dpm_ctx = &smu->smu_dpm;
+
+ if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled)
+ return -EOPNOTSUPP;
+
+ if (!smu->is_apu && !smu_dpm_ctx->dpm_context)
+ return -EINVAL;
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (smu_dpm_ctx->dpm_level != level)
+ return -EINVAL;
+
+ smu_enter_umd_pstate(smu);
+
+ return smu_reapply_power_state_dynamic(smu, level);
+}
+
static int smu_set_display_count(void *handle, uint32_t count)
{
struct smu_context *smu = handle;
@@ -3932,6 +4002,7 @@ static const struct amd_pm_funcs swsmu_pm_funcs = {
.print_clock_levels = smu_print_ppclk_levels,
.emit_clock_levels = smu_emit_ppclk_levels,
.force_performance_level = smu_force_performance_level,
+ .reapply_performance_level = smu_reapply_performance_level,
.read_sensor = smu_read_sensor,
.get_apu_thermal_limit = smu_get_apu_thermal_limit,
.set_apu_thermal_limit = smu_set_apu_thermal_limit,
diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c
index d72c22dcf685..591f71577937 100644
--- a/drivers/gpu/drm/arm/malidp_crtc.c
+++ b/drivers/gpu/drm/arm/malidp_crtc.c
@@ -56,6 +56,7 @@ static void malidp_crtc_atomic_enable(struct drm_crtc *crtc,
if (err < 0) {
DRM_DEBUG_DRIVER("Failed to enable runtime power management: %d\n", err);
+ pm_runtime_put_noidle(crtc->dev->dev);
return;
}
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index efe534977d12..44761f7161f6 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -1389,6 +1389,7 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
{
struct device_node *dp_node = dp->dev->of_node;
struct video_info *video_info = &dp->video_info;
+ u32 val;
switch (dp->plat_data->dev_type) {
case RK3288_DP:
@@ -1409,10 +1410,14 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
* NOTE: those property parseing code is used for
* providing backward compatibility for samsung platform.
*/
- of_property_read_u32(dp_node, "samsung,link-rate",
- &video_info->max_link_rate);
- of_property_read_u32(dp_node, "samsung,lane-count",
- &video_info->max_lane_count);
+ if (of_property_read_u32(dp_node, "samsung,link-rate", &val))
+ return dev_err_probe(dp->dev, -EINVAL,
+ "Failed to get samsung,link-rate\n");
+ video_info->max_link_rate = val;
+ if (of_property_read_u32(dp_node, "samsung,lane-count", &val))
+ return dev_err_probe(dp->dev, -EINVAL,
+ "Failed to get samsung,lane-count\n");
+ video_info->max_lane_count = val;
break;
}
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index d4b6ea42db0f..9c4ac2febc93 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -184,6 +184,8 @@ static void drm_panic_write_pixel24_xpage(void *vaddr, struct page *next_page,
u8 *p = vaddr + offset;
vaddr2 = kmap_local_page_try_from_panic(next_page);
+ if (!vaddr2)
+ return;
*p++ = color & 0xff;
color >>= 8;
@@ -662,7 +664,8 @@ static int drm_panic_get_qr_code_url(u8 **qr_image)
char *kmsg;
int max_qr_data_size, url_len;
- url_len = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&z=",
+ url_len = snprintf(url, sizeof(url), "%s?a=%s&v=%s&z=",
+ CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL,
utsname()->machine, utsname()->release);
max_qr_data_size = drm_panic_qr_max_data_size(panic_qr_version, url_len);
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index 4e93f8dd39d7..1827af24a3fa 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -1254,6 +1254,8 @@ int dsi_tx_buf_alloc_6g(struct msm_dsi_host *msm_host, int size)
if (IS_ERR(data)) {
msm_host->tx_gem_obj = NULL;
+ drm_gpuvm_put(msm_host->vm);
+ msm_host->vm = NULL;
return PTR_ERR(data);
}
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
index 93a491a103e0..75dea34c43a8 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
@@ -13,14 +13,16 @@
#include "msm_kms.h"
#include "hdmi.h"
-static void msm_hdmi_power_on(struct drm_bridge *bridge)
+static int msm_hdmi_power_on(struct drm_bridge *bridge)
{
struct drm_device *dev = bridge->dev;
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
int ret;
- pm_runtime_resume_and_get(&hdmi->pdev->dev);
+ ret = pm_runtime_resume_and_get(&hdmi->pdev->dev);
+ if (ret)
+ return ret;
if (hdmi->extp_clk) {
DBG("pixclock: %lu", hdmi->pixclock);
@@ -29,9 +31,14 @@ static void msm_hdmi_power_on(struct drm_bridge *bridge)
DRM_DEV_ERROR(dev->dev, "failed to set extp clk rate: %d\n", ret);
ret = clk_prepare_enable(hdmi->extp_clk);
- if (ret)
+ if (ret) {
DRM_DEV_ERROR(dev->dev, "failed to enable extp clk: %d\n", ret);
+ pm_runtime_put(&hdmi->pdev->dev);
+ return ret;
+ }
}
+
+ return 0;
}
static void power_off(struct drm_bridge *bridge)
@@ -302,13 +309,18 @@ static void msm_hdmi_bridge_atomic_pre_enable(struct drm_bridge *bridge,
msm_hdmi_set_timings(hdmi, &crtc_state->adjusted_mode);
- mutex_lock(&hdmi->state_mutex);
- if (!hdmi->power_on) {
- msm_hdmi_phy_resource_enable(phy);
- msm_hdmi_power_on(bridge);
- hdmi->power_on = true;
+ scoped_guard(mutex, &hdmi->state_mutex) {
+ if (!hdmi->power_on) {
+ if (msm_hdmi_phy_resource_enable(phy))
+ return;
+
+ if (msm_hdmi_power_on(bridge)) {
+ msm_hdmi_phy_resource_disable(phy);
+ return;
+ }
+ hdmi->power_on = true;
+ }
}
- mutex_unlock(&hdmi->state_mutex);
if (connector->display_info.is_hdmi)
msm_hdmi_audio_update(hdmi);
@@ -342,10 +354,10 @@ static void msm_hdmi_bridge_atomic_post_disable(struct drm_bridge *bridge,
msm_hdmi_phy_powerdown(phy);
if (hdmi->power_on) {
- power_off(bridge);
hdmi->power_on = false;
if (hdmi->connector->display_info.is_hdmi)
msm_hdmi_audio_update(hdmi);
+ power_off(bridge);
msm_hdmi_phy_resource_disable(phy);
}
mutex_unlock(&hdmi->state_mutex);
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c
index 6777cba9255e..bc603a79c085 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c
@@ -67,16 +67,28 @@ int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy)
ret = regulator_bulk_enable(cfg->num_regs, phy->regs);
if (ret) {
DRM_DEV_ERROR(dev, "failed to enable regulators: (%d)\n", ret);
- return ret;
+ goto err_pm_put;
}
for (i = 0; i < cfg->num_clks; i++) {
ret = clk_prepare_enable(phy->clks[i]);
- if (ret)
+ if (ret) {
DRM_DEV_ERROR(dev, "failed to enable clock: %s (%d)\n",
cfg->clk_names[i], ret);
+ goto err_clk_unprepare;
+ }
}
+ return 0;
+
+err_clk_unprepare:
+ for (; i > 0; i--)
+ clk_disable_unprepare(phy->clks[i - 1]);
+ regulator_bulk_disable(cfg->num_regs, phy->regs);
+
+err_pm_put:
+ pm_runtime_put_sync(dev);
+
return ret;
}
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h
index 112b674ed9c8..0172e0d200f8 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h
@@ -46,6 +46,7 @@ int nv4c_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct n
int g84_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
int g92_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
int g94_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
+int mcp79_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
int gf100_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
int gf106_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
int gk104_pci_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_pci **);
diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c
index a3ba07fc48a0..a067c7244e7e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_abi16.c
+++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c
@@ -631,6 +631,10 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
if (unlikely(device->info.family >= NV_DEVICE_INFO_V0_FERMI))
return nouveau_abi16_put(abi16, -EINVAL);
+ /* zero size yields a zero-length node, underflowing args.limit */
+ if (unlikely(!info->size))
+ return nouveau_abi16_put(abi16, -EINVAL);
+
chan = nouveau_abi16_chan(abi16, info->channel);
if (!chan)
return nouveau_abi16_put(abi16, -ENOENT);
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 7c6d91ee2722..f9662e067c56 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -397,6 +397,7 @@ nouveau_connector_destroy(struct drm_connector *connector)
struct nouveau_connector *nv_connector = nouveau_connector(connector);
nvif_event_dtor(&nv_connector->irq);
nvif_event_dtor(&nv_connector->hpd);
+ cancel_work_sync(&nv_connector->irq_work);
kfree(nv_connector->edid);
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 55691ec44aba..738802358d85 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -486,7 +486,7 @@ nouveau_dp_irq(struct work_struct *work)
container_of(work, typeof(*nv_connector), irq_work);
struct drm_connector *connector = &nv_connector->base;
struct nouveau_encoder *outp = find_encoder(connector, DCB_OUTPUT_DP);
- struct nouveau_drm *drm = nouveau_drm(outp->base.base.dev);
+ struct nouveau_drm *drm;
struct nv50_mstm *mstm;
u64 hpd = 0;
int ret;
@@ -494,6 +494,8 @@ nouveau_dp_irq(struct work_struct *work)
if (!outp)
return;
+ drm = nouveau_drm(outp->base.base.dev);
+
mstm = outp->dp.mstm;
NV_DEBUG(drm, "service %s\n", connector->name);
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index 4a193b7d6d9e..7acf241c4daf 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -95,7 +95,7 @@ nouveau_fence_context_kill(struct nouveau_fence_chan *fctx, int error)
void
nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
{
- cancel_work_sync(&fctx->uevent_work);
+ disable_work_sync(&fctx->uevent_work);
nouveau_fence_context_kill(fctx, 0);
nvif_event_dtor(&fctx->event);
fctx->dead = 1;
diff --git a/drivers/gpu/drm/nouveau/nouveau_svm.c b/drivers/gpu/drm/nouveau/nouveau_svm.c
index 6fa387da0637..dc6c79f57a56 100644
--- a/drivers/gpu/drm/nouveau/nouveau_svm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_svm.c
@@ -304,6 +304,19 @@ nouveau_svmm_fini(struct nouveau_svmm **psvmm)
{
struct nouveau_svmm *svmm = *psvmm;
if (svmm) {
+ struct nouveau_svm *svm = svmm->vmm->cli->drm->svm;
+
+ /* The fault handler caches svmm pointers under svm->mutex and
+ * dereferences them after dropping it, across blocking faults.
+ * The instance is already unlinked (nouveau_svmm_part), so drain
+ * the handler before the free to release any in-flight reference.
+ *
+ * On device teardown nouveau_svm_fini() ran first and freed
+ * drm->svm (NULL) after draining the buffer; nothing to flush.
+ */
+ if (svm)
+ flush_work(&svm->buffer[0].work);
+
mutex_lock(&svmm->mutex);
svmm->vmm = NULL;
mutex_unlock(&svmm->mutex);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
index 3738c4664fb6..305b16d9bcc7 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
@@ -1237,7 +1237,7 @@ nvac_chipset = {
.mc = { 0x00000001, g98_mc_new },
.mmu = { 0x00000001, mcp77_mmu_new },
.mxm = { 0x00000001, nv50_mxm_new },
- .pci = { 0x00000001, g94_pci_new },
+ .pci = { 0x00000001, mcp79_pci_new },
.therm = { 0x00000001, g84_therm_new },
.timer = { 0x00000001, nv41_timer_new },
.volt = { 0x00000001, nv40_volt_new },
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild
index a14ea0f7b1c8..90f03baeb22a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild
@@ -9,6 +9,7 @@ nvkm-y += nvkm/subdev/pci/nv4c.o
nvkm-y += nvkm/subdev/pci/g84.o
nvkm-y += nvkm/subdev/pci/g92.o
nvkm-y += nvkm/subdev/pci/g94.o
+nvkm-y += nvkm/subdev/pci/mcp79.o
nvkm-y += nvkm/subdev/pci/gf100.o
nvkm-y += nvkm/subdev/pci/gf106.o
nvkm-y += nvkm/subdev/pci/gk104.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/mcp79.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/mcp79.c
new file mode 100644
index 000000000000..e2ae242f1aa7
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/mcp79.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2026 Marek Czernohous
+ *
+ * MCP79/MCP7A (NVAC): like g94, but MSI re-arm goes through real PCI
+ * config space. The MMIO-mirror re-arm is unreliable on this IGP and a
+ * missed re-arm kills the interrupt line (see the nv46 comment; g92
+ * already re-arms through config space for the same reason).
+ */
+#include "priv.h"
+
+static const struct nvkm_pci_func
+mcp79_pci_func = {
+ .cfg = { .addr = 0x088000, .size = 0x1000 },
+
+ .init = g84_pci_init,
+ .msi_rearm = nv46_pci_msi_rearm,
+
+ .pcie.init = g84_pcie_init,
+ .pcie.set_link = g84_pcie_set_link,
+
+ .pcie.max_speed = g84_pcie_max_speed,
+ .pcie.cur_speed = g84_pcie_cur_speed,
+
+ .pcie.set_version = g84_pcie_set_version,
+ .pcie.version = g84_pcie_version,
+ .pcie.version_supported = g92_pcie_version_supported,
+};
+
+int
+mcp79_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
+ struct nvkm_pci **ppci)
+{
+ return nvkm_pci_new_(&mcp79_pci_func, device, type, inst, ppci);
+}
diff --git a/drivers/gpu/drm/panel/panel-ebbg-ft8719.c b/drivers/gpu/drm/panel/panel-ebbg-ft8719.c
index fb9f9f42be4f..1ab09c50a630 100644
--- a/drivers/gpu/drm/panel/panel-ebbg-ft8719.c
+++ b/drivers/gpu/drm/panel/panel-ebbg-ft8719.c
@@ -198,6 +198,8 @@ static int ebbg_ft8719_probe(struct mipi_dsi_device *dsi)
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_CLOCK_NON_CONTINUOUS;
+ ctx->panel.prepare_prev_first = true;
+
ret = drm_panel_of_backlight(&ctx->panel);
if (ret)
return dev_err_probe(dev, ret, "Failed to get backlight\n");
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 7aa35899e33c..7c1edf8c5c2d 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -359,6 +359,10 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo,
if (ret)
return ret;
+ /* Imported sg tables only come with DMA addresses, no pages to map. */
+ if (!ttm->pages)
+ return -EINVAL;
+
if (num_pages == 1 && ttm->caching == ttm_cached &&
!(man->use_tt && (ttm->page_flags & TTM_TT_FLAG_DECRYPTED))) {
/*
@@ -422,7 +426,7 @@ EXPORT_SYMBOL(ttm_bo_kmap_try_from_panic);
*
* Returns
* -ENOMEM: Out of memory.
- * -EINVAL: Invalid range.
+ * -EINVAL: Invalid range or the BO has no pages to map.
*/
int ttm_bo_kmap(struct ttm_buffer_object *bo,
unsigned long start_page, unsigned long num_pages,
@@ -496,7 +500,7 @@ EXPORT_SYMBOL(ttm_bo_kunmap);
*
* Returns
* -ENOMEM: Out of memory.
- * -EINVAL: Invalid range.
+ * -EINVAL: Invalid range or the BO has no pages to map.
*/
int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map)
{
@@ -543,6 +547,9 @@ int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map)
if (ret)
return ret;
+ if (!ttm->pages)
+ return -EINVAL;
+
/*
* We need to use vmap to get the desired page protection
* or to make the buffer object look contiguous.
diff --git a/drivers/hte/hte-tegra194-test.c b/drivers/hte/hte-tegra194-test.c
index 94e931f45305..4951049e00b0 100644
--- a/drivers/hte/hte-tegra194-test.c
+++ b/drivers/hte/hte-tegra194-test.c
@@ -218,10 +218,10 @@ static void tegra_hte_test_remove(struct platform_device *pdev)
{
(void)pdev;
+ timer_shutdown_sync(&hte.timer);
free_irq(hte.gpio_in_irq, &hte);
gpiod_put(hte.gpio_in);
gpiod_put(hte.gpio_out);
- timer_delete_sync(&hte.timer);
}
static struct platform_driver tegra_hte_test_driver = {
diff --git a/drivers/hte/hte-tegra194.c b/drivers/hte/hte-tegra194.c
index 690eb9be30fb..464467737002 100644
--- a/drivers/hte/hte-tegra194.c
+++ b/drivers/hte/hte-tegra194.c
@@ -787,17 +787,17 @@ static int tegra_hte_probe(struct platform_device *pdev)
hte_dev->chip = gc;
+ for (i = 0; i < slices; i++) {
+ hte_dev->sl[i].flags = 0;
+ spin_lock_init(&hte_dev->sl[i].s_lock);
+ }
+
ret = devm_hte_register_chip(hte_dev->chip);
if (ret) {
dev_err(gc->dev, "hte chip register failed");
return ret;
}
- for (i = 0; i < slices; i++) {
- hte_dev->sl[i].flags = 0;
- spin_lock_init(&hte_dev->sl[i].s_lock);
- }
-
val = HTE_TECTRL_ENABLE_ENABLE |
(HTE_TECTRL_INTR_ENABLE << HTE_TECTRL_INTR_SHIFT) |
(hte_dev->itr_thrshld << HTE_TECTRL_OCCU_SHIFT);
diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c
index de408df0c4d7..667dffde3301 100644
--- a/drivers/hwmon/acpi_power_meter.c
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -10,6 +10,7 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/jiffies.h>
+#include <linux/limits.h>
#include <linux/mutex.h>
#include <linux/dmi.h>
#include <linux/slab.h>
@@ -31,7 +32,12 @@
#define POWER_METER_CAN_NOTIFY (1 << 3)
#define POWER_METER_IS_BATTERY (1 << 8)
#define UNKNOWN_HYSTERESIS 0xFFFFFFFF
-#define UNKNOWN_POWER 0xFFFFFFFF
+/*
+ * ACPI integers can either be 32-bit or 64-bit wide, so
+ * we have to check for both values.
+ */
+#define UNKNOWN_POWER_32 U32_MAX
+#define UNKNOWN_POWER_64 U64_MAX
#define METER_NOTIFY_CONFIG 0x80
#define METER_NOTIFY_TRIP 0x81
@@ -454,7 +460,7 @@ static int power_meter_read(struct device *dev, enum hwmon_sensor_types type,
ret = update_meter(res);
if (ret)
return ret;
- if (res->power == UNKNOWN_POWER)
+ if (res->power == UNKNOWN_POWER_32 || res->power == UNKNOWN_POWER_64)
return -ENODATA;
*val = res->power * 1000;
break;
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index a84a32ada8bd..c9b7e435eda8 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -511,6 +511,11 @@ init_temp_data(struct platform_data *pdata, unsigned int cpu, int pkg_flag)
tdata->cpu = cpu;
tdata->cpu_core_id = topology_core_id(cpu);
tdata->attr_size = MAX_CORE_ATTRS;
+ /*
+ * A zero timestamp does not look stale on 32-bit, where jiffies
+ * starts just short of wrapping. Backdate it instead.
+ */
+ tdata->last_updated = jiffies - HZ - 1;
mutex_init(&tdata->update_lock);
return tdata;
}
@@ -554,9 +559,6 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu,
u32 eax, edx;
int err;
- if (!housekeeping_cpu(cpu, HK_TYPE_MISC))
- return 0;
-
tdata = init_temp_data(pdata, cpu, pkg_flag);
if (!tdata)
return -ENOMEM;
@@ -676,6 +678,14 @@ static int coretemp_cpu_online(unsigned int cpu)
if (!cpu_has(c, X86_FEATURE_DTHERM))
return -ENODEV;
+ /*
+ * Keep isolated CPUs out of pdata->cpumask. Besides tracking the CPUs
+ * available to this driver, the mask is used to select the CPU on which
+ * to read the temperature MSRs.
+ */
+ if (!housekeeping_cpu(cpu, HK_TYPE_MISC))
+ return 0;
+
pdata = platform_get_drvdata(pdev);
if (!pdata->hwmon_dev) {
struct device *hwmon;
@@ -705,8 +715,8 @@ static int coretemp_cpu_online(unsigned int cpu)
}
/*
- * Check whether a thread sibling is already online. If not add the
- * interface for this CPU core.
+ * Check whether a housekeeping thread sibling is already online. If
+ * not, add the interface for this CPU core.
*/
if (!cpumask_intersects(&pdata->cpumask, topology_sibling_cpumask(cpu)))
coretemp_add_core(pdev, cpu, 0);
@@ -736,9 +746,9 @@ static int coretemp_cpu_offline(unsigned int cpu)
cpumask_clear_cpu(cpu, &pd->cpumask);
/*
- * If this is the last thread sibling, remove the CPU core
- * interface, If there is still a sibling online, transfer the
- * target cpu of that core interface to it.
+ * If this is the last housekeeping thread sibling, remove the CPU core
+ * interface. If there is still a housekeeping sibling online, transfer
+ * the target CPU of that core interface to it.
*/
target = cpumask_any_and(&pd->cpumask, topology_sibling_cpumask(cpu));
if (target >= nr_cpu_ids) {
@@ -750,7 +760,8 @@ static int coretemp_cpu_offline(unsigned int cpu)
}
/*
- * If all cores in this pkg are offline, remove the interface.
+ * If all housekeeping cores in this pkg are offline, remove the
+ * interface.
*/
tdata = get_temp_data(pd, -1);
if (cpumask_empty(&pd->cpumask)) {
@@ -763,7 +774,7 @@ static int coretemp_cpu_offline(unsigned int cpu)
/*
* Check whether this core is the target for the package
- * interface. We need to assign it to some other cpu.
+ * interface. We need to assign it to some other housekeeping CPU.
*/
if (tdata && tdata->cpu == cpu) {
target = cpumask_first(&pd->cpumask);
diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c
index bf7775a731fb..17bb080eb9bb 100644
--- a/drivers/hwmon/corsair-cpro.c
+++ b/drivers/hwmon/corsair-cpro.c
@@ -135,9 +135,9 @@ static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2,
* the raw event parsing and marked the ccp->wait_input_report
* completion as done.
*/
- spin_lock_bh(&ccp->wait_input_report_lock);
+ spin_lock_irq(&ccp->wait_input_report_lock);
reinit_completion(&ccp->wait_input_report);
- spin_unlock_bh(&ccp->wait_input_report_lock);
+ spin_unlock_irq(&ccp->wait_input_report_lock);
ret = hid_hw_output_report(ccp->hdev, ccp->cmd_buffer, OUT_BUFFER_SIZE);
if (ret < 0)
@@ -156,15 +156,16 @@ static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2,
static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
{
struct ccp_device *ccp = hid_get_drvdata(hdev);
+ unsigned long flags;
/* only copy buffer when requested */
- spin_lock(&ccp->wait_input_report_lock);
+ spin_lock_irqsave(&ccp->wait_input_report_lock, flags);
if (!completion_done(&ccp->wait_input_report)) {
memcpy(ccp->buffer, data, min(IN_BUFFER_SIZE, size));
ccp->buffer_recv_size = size;
complete_all(&ccp->wait_input_report);
}
- spin_unlock(&ccp->wait_input_report_lock);
+ spin_unlock_irqrestore(&ccp->wait_input_report_lock, flags);
return 0;
}
diff --git a/drivers/hwmon/gigabyte_waterforce.c b/drivers/hwmon/gigabyte_waterforce.c
index 4eea05f8b569..f4d0376dd68e 100644
--- a/drivers/hwmon/gigabyte_waterforce.c
+++ b/drivers/hwmon/gigabyte_waterforce.c
@@ -257,6 +257,9 @@ static int waterforce_raw_event(struct hid_device *hdev, struct hid_report *repo
{
struct waterforce_data *priv = hid_get_drvdata(hdev);
+ if (size <= FIRMWARE_VER_START_OFFSET_2)
+ return 0;
+
if (data[0] == get_firmware_ver_cmd[0] && data[1] == get_firmware_ver_cmd[1]) {
/* Received a firmware version report */
priv->firmware_version =
@@ -269,6 +272,8 @@ static int waterforce_raw_event(struct hid_device *hdev, struct hid_report *repo
if (data[0] != get_status_cmd[0] || data[1] != get_status_cmd[1])
return 0;
+ if (size <= WATERFORCE_TEMP_SENSOR)
+ return 0;
priv->temp_input[0] = data[WATERFORCE_TEMP_SENSOR] * 1000;
priv->speed_input[0] = get_unaligned_le16(data + WATERFORCE_FAN_SPEED);
diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c
index 3bdc30530847..9c977d2820f9 100644
--- a/drivers/hwmon/lm95245.c
+++ b/drivers/hwmon/lm95245.c
@@ -98,9 +98,9 @@ static int temp_from_reg_unsigned(u8 val_h, u8 val_l)
static int temp_from_reg_signed(u8 val_h, u8 val_l)
{
- if (val_h & 0x80)
- return (val_h - 0x100) * 1000;
- return temp_from_reg_unsigned(val_h, val_l);
+ s16 val_hl = (val_h << 8) | val_l;
+
+ return val_hl * 1000 / 256;
}
static int lm95245_read_conversion_rate(struct lm95245_data *data)
diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c
index 5d04013efb52..c3e68bde709d 100644
--- a/drivers/hwmon/sht3x.c
+++ b/drivers/hwmon/sht3x.c
@@ -543,7 +543,10 @@ static ssize_t heater_enable_store(struct device *dev,
mutex_unlock(&data->i2c_lock);
- return ret;
+ if (ret != SHT3X_CMD_LENGTH)
+ return ret < 0 ? ret : -EIO;
+
+ return count;
}
static int update_interval_read(struct device *dev)
diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c
index 5a76361bb135..83dc3ca67046 100644
--- a/drivers/hwmon/sht4x.c
+++ b/drivers/hwmon/sht4x.c
@@ -67,7 +67,7 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table);
*/
struct sht4x_data {
struct i2c_client *client;
- unsigned long heating_complete; /* in jiffies */
+ u64 heating_complete; /* in jiffies */
bool data_pending;
u32 heater_power; /* in milli-watts */
u32 heater_time; /* in milli-seconds */
@@ -92,14 +92,14 @@ static int sht4x_read_values(struct sht4x_data *data)
u8 crc;
u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM};
u8 raw_data[SHT4X_RESPONSE_LENGTH];
- unsigned long curr_jiffies;
+ u64 curr_jiffies;
- curr_jiffies = jiffies;
- if (time_before(curr_jiffies, data->heating_complete))
+ curr_jiffies = get_jiffies_64();
+ if (time_before64(curr_jiffies, data->heating_complete))
msleep(jiffies_to_msecs(data->heating_complete - curr_jiffies));
if (data->data_pending &&
- time_before(jiffies, data->heating_complete + data->update_interval)) {
+ time_before64(get_jiffies_64(), data->heating_complete + data->update_interval)) {
data->data_pending = false;
} else {
next_update = data->last_updated +
@@ -237,7 +237,9 @@ static ssize_t heater_enable_show(struct device *dev,
{
struct sht4x_data *data = dev_get_drvdata(dev);
- return sysfs_emit(buf, "%u\n", time_before(jiffies, data->heating_complete));
+ guard(hwmon_lock)(dev);
+
+ return sysfs_emit(buf, "%u\n", time_before64(get_jiffies_64(), data->heating_complete));
}
static ssize_t heater_enable_store(struct device *dev,
@@ -279,14 +281,16 @@ static ssize_t heater_enable_store(struct device *dev,
guard(hwmon_lock)(dev);
- if (time_before(jiffies, data->heating_complete))
+ if (time_before64(get_jiffies_64(), data->heating_complete))
return -EBUSY;
ret = i2c_master_send(data->client, &cmd, SHT4X_CMD_LEN);
if (ret < 0)
return ret;
+ if (ret != SHT4X_CMD_LEN)
+ return -EIO;
- data->heating_complete = jiffies + msecs_to_jiffies(heating_time_bound);
+ data->heating_complete = get_jiffies_64() + msecs_to_jiffies(heating_time_bound);
data->data_pending = true;
return count;
}
@@ -410,7 +414,7 @@ static int sht4x_probe(struct i2c_client *client)
data->client = client;
data->heater_power = 200;
data->heater_time = 1000;
- data->heating_complete = jiffies;
+ data->heating_complete = get_jiffies_64();
crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL);
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index c4bab532eed1..593ddec3c40e 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -515,6 +515,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
if (dev->rx_outstanding >= dev->rx_fifo_depth)
break;
+ /*
+ * For block reads, queue only the length
+ * byte until i2c_dw_recv_len() runs.
+ */
+ if ((flags & I2C_M_RECV_LEN) &&
+ buf_len < msgs[dev->msg_write_idx].len)
+ break;
+
regmap_write(dev->map, DW_IC_DATA_CMD,
cmd | 0x100);
rx_limit--;
diff --git a/drivers/i2c/busses/i2c-mt7621.c b/drivers/i2c/busses/i2c-mt7621.c
index 0a288c998419..9ea5e3f9a763 100644
--- a/drivers/i2c/busses/i2c-mt7621.c
+++ b/drivers/i2c/busses/i2c-mt7621.c
@@ -144,7 +144,6 @@ static int mtk_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
u16 addr;
int i, j, ret, len, page_len;
u32 cmd;
- u32 data[2];
i2c = i2c_get_adapdata(adap);
@@ -186,6 +185,8 @@ static int mtk_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
/* transfer data */
for (len = pmsg->len, j = 0; len > 0; len -= 8, j += 8) {
+ u32 data[2] = { };
+
page_len = (len >= 8) ? 8 : len;
if (pmsg->flags & I2C_M_RD) {
@@ -194,7 +195,8 @@ static int mtk_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
} else {
memcpy(data, &pmsg->buf[j], page_len);
iowrite32(data[0], i2c->base + REG_SM0D0_REG);
- iowrite32(data[1], i2c->base + REG_SM0D1_REG);
+ if (page_len > 4)
+ iowrite32(data[1], i2c->base + REG_SM0D1_REG);
cmd = SM0CTL1_WRITE;
}
@@ -204,7 +206,8 @@ static int mtk_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
if (pmsg->flags & I2C_M_RD) {
data[0] = ioread32(i2c->base + REG_SM0D0_REG);
- data[1] = ioread32(i2c->base + REG_SM0D1_REG);
+ if (page_len > 4)
+ data[1] = ioread32(i2c->base + REG_SM0D1_REG);
memcpy(&pmsg->buf[j], data, page_len);
} else {
if (!(pmsg->flags & I2C_M_IGNORE_NAK)) {
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
index 98ebbc973f21..8805fc454a4e 100644
--- a/drivers/i2c/i2c-core-acpi.c
+++ b/drivers/i2c/i2c-core-acpi.c
@@ -545,6 +545,7 @@ struct i2c_client *i2c_acpi_new_device_by_fwnode(struct fwnode_handle *fwnode,
{
struct i2c_acpi_lookup lookup;
struct i2c_adapter *adapter;
+ struct i2c_client *client;
struct acpi_device *adev;
LIST_HEAD(resource_list);
int ret;
@@ -572,7 +573,10 @@ struct i2c_client *i2c_acpi_new_device_by_fwnode(struct fwnode_handle *fwnode,
if (!adapter)
return ERR_PTR(-EPROBE_DEFER);
- return i2c_new_client_device(adapter, info);
+ client = i2c_new_client_device(adapter, info);
+ put_device(&adapter->dev);
+
+ return client;
}
EXPORT_SYMBOL_GPL(i2c_acpi_new_device_by_fwnode);
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 038f7b2ec8c4..0dbcfb1a5204 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -1955,12 +1955,11 @@ struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *buf,
if (force_bounce) {
dma_xfer->map_len = ALIGN(len, cache_line_size());
- if (dir == DMA_FROM_DEVICE)
- bounce = kzalloc(dma_xfer->map_len, GFP_KERNEL);
- else
- bounce = kmemdup(buf, dma_xfer->map_len, GFP_KERNEL);
+ bounce = kzalloc(dma_xfer->map_len, GFP_KERNEL);
if (!bounce)
return NULL;
+ if (dir != DMA_FROM_DEVICE)
+ memcpy(bounce, buf, len);
dma_buf = bounce;
}
diff --git a/drivers/i3c/master/adi-i3c-master.c b/drivers/i3c/master/adi-i3c-master.c
index 178029769bab..d6278d03012d 100644
--- a/drivers/i3c/master/adi-i3c-master.c
+++ b/drivers/i3c/master/adi-i3c-master.c
@@ -115,6 +115,7 @@ struct adi_i3c_master {
spinlock_t lock; /* Protect transfer */
} xferqueue;
void __iomem *regs;
+ int irq;
struct clk *clk;
unsigned long i3c_scl_lim;
struct {
@@ -957,6 +958,7 @@ static int adi_i3c_master_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
+ master->irq = irq;
version = readl(master->regs + ADI_AXI_REG_VERSION);
if (ADI_AXI_PCORE_VER_MAJOR(version) != 1)
@@ -1001,6 +1003,8 @@ static void adi_i3c_master_remove(struct platform_device *pdev)
writel(0x00, master->regs + REG_IRQ_MASK);
writel(0x01, master->regs + REG_ENABLE);
+ devm_free_irq(&pdev->dev, master->irq, master);
+
i3c_master_unregister(&master->base);
}
diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
index 37717e530a25..15a34429197b 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dma.c
+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
@@ -376,7 +376,7 @@ static void hci_dma_unmap_xfer(struct i3c_hci *hci,
static struct i3c_dma *hci_dma_map_xfer(struct device *dev, struct hci_xfer *xfer)
{
enum dma_data_direction dir = xfer->rnw ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- bool need_bounce = device_iommu_mapped(dev) && xfer->rnw && (xfer->data_len & 3);
+ bool need_bounce = xfer->rnw && (xfer->data_len & 3);
return i3c_master_dma_map_single(dev, xfer->data, xfer->data_len, need_bounce, dir);
}
diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c
index 5127e58eebc7..f0cf3783bcd9 100644
--- a/drivers/iio/accel/adis16201.c
+++ b/drivers/iio/accel/adis16201.c
@@ -202,19 +202,19 @@ static int adis16201_write_raw(struct iio_dev *indio_dev,
}
static const struct iio_chan_spec adis16201_channels[] = {
- ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT_REG, ADIS16201_SCAN_SUPPLY, 0,
- 12),
- ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT_REG, ADIS16201_SCAN_TEMP, 0, 12),
ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT_REG, ADIS16201_SCAN_ACC_X,
BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT_REG, ADIS16201_SCAN_ACC_Y,
BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
- ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC_REG, ADIS16201_SCAN_AUX_ADC, 0, 12),
ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT_REG, ADIS16201_SCAN_INCLI_X,
BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 12),
ADIS_INCLI_CHAN(Y, ADIS16201_YINCL_OUT_REG, ADIS16201_SCAN_INCLI_Y,
BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 12),
- IIO_CHAN_SOFT_TIMESTAMP(7)
+ ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT_REG, ADIS16201_SCAN_SUPPLY, 0,
+ 12),
+ ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC_REG, ADIS16201_SCAN_AUX_ADC, 0, 12),
+ ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT_REG, ADIS16201_SCAN_TEMP, 0, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(7),
};
static const struct iio_info adis16201_info = {
diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c
index dedb0885c277..b2287f4f4442 100644
--- a/drivers/iio/accel/adxl313_spi.c
+++ b/drivers/iio/accel/adxl313_spi.c
@@ -79,6 +79,8 @@ static int adxl313_spi_probe(struct spi_device *spi)
return ret;
chip_data = spi_get_device_match_data(spi);
+ if (!chip_data)
+ return -ENODATA;
regmap = devm_regmap_init_spi(spi,
&adxl31x_spi_regmap_config[chip_data->type]);
diff --git a/drivers/iio/accel/adxl380_spi.c b/drivers/iio/accel/adxl380_spi.c
index 6edd0d211ffa..b51edc021b26 100644
--- a/drivers/iio/accel/adxl380_spi.c
+++ b/drivers/iio/accel/adxl380_spi.c
@@ -26,6 +26,8 @@ static int adxl380_spi_probe(struct spi_device *spi)
struct regmap *regmap;
chip_data = spi_get_device_match_data(spi);
+ if (!chip_data)
+ return -ENODATA;
regmap = devm_regmap_init_spi(spi, &adxl380_spi_regmap_config);
if (IS_ERR(regmap))
diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
index 297853cd39e5..a8652bdd011a 100644
--- a/drivers/iio/accel/kxcjk-1013.c
+++ b/drivers/iio/accel/kxcjk-1013.c
@@ -1548,6 +1548,7 @@ static void kxcjk1013_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
+ pm_runtime_dont_use_autosuspend(&client->dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c
index 02195deada49..28342a757c6c 100644
--- a/drivers/iio/accel/mma9551.c
+++ b/drivers/iio/accel/mma9551.c
@@ -503,6 +503,7 @@ static void mma9551_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
+ pm_runtime_dont_use_autosuspend(&client->dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c
index f85384a7908f..65533bc275ec 100644
--- a/drivers/iio/accel/mma9553.c
+++ b/drivers/iio/accel/mma9553.c
@@ -1140,6 +1140,7 @@ static void mma9553_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
+ pm_runtime_dont_use_autosuspend(&client->dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
index f28e4ca37707..bec3f656406b 100644
--- a/drivers/iio/adc/ad7606_spi.c
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -456,7 +456,11 @@ static const struct ad7606_bus_info ad7616_bus_info = {
static int ad7606_spi_probe(struct spi_device *spi)
{
- const struct ad7606_bus_info *bus_info = spi_get_device_match_data(spi);
+ const struct ad7606_bus_info *bus_info;
+
+ bus_info = spi_get_device_match_data(spi);
+ if (!bus_info)
+ return -ENODATA;
return ad7606_probe(&spi->dev, spi->irq, NULL,
bus_info->chip_info, bus_info->bops);
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
index aa4ba3f5a506..8c16c5938601 100644
--- a/drivers/iio/adc/at91-sama5d2_adc.c
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -2487,6 +2487,7 @@ static void at91_adc_remove(struct platform_device *pdev)
pm_runtime_disable(st->dev);
pm_runtime_set_suspended(st->dev);
+ pm_runtime_dont_use_autosuspend(st->dev);
clk_disable_unprepare(st->per_clk);
regulator_disable(st->vref);
diff --git a/drivers/iio/adc/max11205.c b/drivers/iio/adc/max11205.c
index 6c803df220b6..e8404973b01a 100644
--- a/drivers/iio/adc/max11205.c
+++ b/drivers/iio/adc/max11205.c
@@ -117,6 +117,8 @@ static int max11205_probe(struct spi_device *spi)
ad_sd_init(&st->sd, indio_dev, spi, &max11205_sigma_delta_info);
st->chip_info = spi_get_device_match_data(spi);
+ if (!st->chip_info)
+ return -ENODATA;
indio_dev->name = st->chip_info->name;
indio_dev->modes = INDIO_DIRECT_MODE;
diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c
index a6f21791c685..5bdfe5e26718 100644
--- a/drivers/iio/adc/mcp3911.c
+++ b/drivers/iio/adc/mcp3911.c
@@ -722,6 +722,8 @@ static int mcp3911_probe(struct spi_device *spi)
adc = iio_priv(indio_dev);
adc->spi = spi;
adc->chip = spi_get_device_match_data(spi);
+ if (!adc->chip)
+ return -ENODATA;
ret = devm_regulator_get_enable_read_voltage(dev, "vref");
if (ret < 0 && ret != -ENODEV)
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index dae35ab929b6..ab4433210ad0 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -2743,7 +2743,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = {
.set_ovs = stm32h7_adc_set_ovs,
};
-static const unsigned int stm32_adc_min_ts_mp1[] = { 100, 100, 100, 4300, 9800 };
+static const unsigned int stm32_adc_min_ts_mp1[] = { 100, 0, 0, 4300, 9800 };
static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp1) == STM32_ADC_INT_CH_NB);
static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
@@ -2765,7 +2765,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
.set_ovs = stm32h7_adc_set_ovs,
};
-static const unsigned int stm32_adc_min_ts_mp13[] = { 100, 0, 0, 4300, 9800 };
+static const unsigned int stm32_adc_min_ts_mp13[] = { 1000, 1000, 1000, 4300, 9800 };
static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp13) == STM32_ADC_INT_CH_NB);
static const struct stm32_adc_cfg stm32mp13_adc_cfg = {
diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c
index 4ae65793ad9b..19c30d60c504 100644
--- a/drivers/iio/adc/ti-adc128s052.c
+++ b/drivers/iio/adc/ti-adc128s052.c
@@ -196,12 +196,14 @@ static int adc128_probe(struct spi_device *spi)
adc = iio_priv(indio_dev);
adc->spi = spi;
- indio_dev->name = spi_get_device_id(spi)->name;
+ config = spi_get_device_match_data(spi);
+ if (!config)
+ return -ENODATA;
+
+ indio_dev->name = spi->modalias;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &adc128_info;
- config = spi_get_device_match_data(spi);
-
indio_dev->channels = config->channels;
indio_dev->num_channels = config->num_channels;
diff --git a/drivers/iio/adc/ti-ads1100.c b/drivers/iio/adc/ti-ads1100.c
index aa8946063c7d..5c08bed48018 100644
--- a/drivers/iio/adc/ti-ads1100.c
+++ b/drivers/iio/adc/ti-ads1100.c
@@ -15,10 +15,12 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/iopoll.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/time.h>
#include <linux/units.h>
#include <linux/iio/iio.h>
@@ -43,6 +45,9 @@
static const int ads1100_data_rate[] = { 128, 32, 16, 8 };
static const int ads1100_data_rate_bits[] = { 12, 14, 15, 16 };
+/* Timeout based on the minimum sample rate of 8 SPS (7500ms) */
+#define ADS1100_MAX_DRDY_TIMEOUT_US (7500 * USEC_PER_MSEC)
+
struct ads1100_data {
struct i2c_client *client;
struct regulator *reg_vdd;
@@ -123,10 +128,86 @@ static int ads1100_get_adc_result(struct ads1100_data *data, int chan, int *val)
return 0;
}
+static int ads1100_conversion_busy(struct ads1100_data *data)
+{
+ u8 buffer[3];
+ int ret;
+
+ ret = i2c_master_recv(data->client, (char *)&buffer, sizeof(buffer));
+ if (ret < 0) {
+ dev_err(&data->client->dev, "I2C read fail: %d\n", ret);
+ return ret;
+ }
+
+ return FIELD_GET(ADS1100_CFG_ST_BSY, buffer[2]);
+}
+
+static int ads1100_wait_single_conversion(struct ads1100_data *data)
+{
+ int data_rate_index = FIELD_GET(ADS1100_DR_MASK, data->config);
+ int data_rate_Hz = ads1100_data_rate[data_rate_index];
+ unsigned long poll_us = DIV_ROUND_CLOSEST(USEC_PER_SEC, data_rate_Hz) / 4;
+ int busy;
+ int ret;
+
+ ret = readx_poll_timeout(ads1100_conversion_busy, data,
+ busy, busy <= 0,
+ poll_us, ADS1100_MAX_DRDY_TIMEOUT_US);
+ if (busy < 0)
+ return busy;
+
+ return ret;
+}
+
+static int ads1100_start_single_conversion(struct ads1100_data *data)
+{
+ u8 config = data->config | ADS1100_CFG_SC;
+ int ret;
+
+ ret = i2c_master_send(data->client, &config, sizeof(config));
+ if (ret < 0) {
+ dev_err(&data->client->dev, "I2C write fail: %d\n", ret);
+ return ret;
+ }
+ /* Need to wait because of change from continuous to single mode */
+ ret = ads1100_wait_single_conversion(data);
+ if (ret)
+ return ret;
+
+ config |= ADS1100_CFG_ST_BSY;
+
+ ret = i2c_master_send(data->client, &config, sizeof(config));
+ if (ret < 0) {
+ dev_err(&data->client->dev, "I2C write fail: %d\n", ret);
+ return ret;
+ }
+
+ /* No need to cache it, it's status bit */
+ data->config = config & ~ADS1100_CFG_ST_BSY;
+
+ return 0;
+}
+
+static int ads1100_poll_data_ready(struct ads1100_data *data)
+{
+ int ret;
+
+ ret = ads1100_start_single_conversion(data);
+ if (ret)
+ return ret;
+
+ ret = ads1100_wait_single_conversion(data);
+ if (ret)
+ return ret;
+
+ return ads1100_set_config_bits(data, ADS1100_CFG_SC, ADS1100_CONTINUOUS);
+}
+
static int ads1100_set_scale(struct ads1100_data *data, int val, int val2)
{
int microvolts;
int gain;
+ int ret;
/* With Vdd between 2.7 and 5V, the scale is always below 1 */
if (val)
@@ -135,6 +216,11 @@ static int ads1100_set_scale(struct ads1100_data *data, int val, int val2)
if (!val2)
return -EINVAL;
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&data->client->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
microvolts = regulator_get_voltage(data->reg_vdd);
/*
* val2 is in 'micro' units, n = val2 / 1000000
@@ -149,22 +235,35 @@ static int ads1100_set_scale(struct ads1100_data *data, int val, int val2)
ads1100_set_config_bits(data, ADS1100_PGA_MASK, ffs(gain) - 1);
- return 0;
+ return ads1100_poll_data_ready(data);
}
static int ads1100_set_data_rate(struct ads1100_data *data, int chan, int rate)
{
unsigned int i;
unsigned int size;
+ int ret;
size = data->supports_data_rate ? ARRAY_SIZE(ads1100_data_rate) : 1;
for (i = 0; i < size; i++) {
if (ads1100_data_rate[i] == rate)
- return ads1100_set_config_bits(data, ADS1100_DR_MASK,
- FIELD_PREP(ADS1100_DR_MASK, i));
+ break;
}
- return -EINVAL;
+ if (i == size)
+ return -EINVAL;
+
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&data->client->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = ads1100_set_config_bits(data, ADS1100_DR_MASK,
+ FIELD_PREP(ADS1100_DR_MASK, i));
+ if (ret)
+ return ret;
+
+ return ads1100_poll_data_ready(data);
}
static int ads1100_get_vdd_millivolts(struct ads1100_data *data)
diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c
index d0e5f35462b1..ce188c5a9ad1 100644
--- a/drivers/iio/dac/ad5755.c
+++ b/drivers/iio/dac/ad5755.c
@@ -819,6 +819,9 @@ static int ad5755_probe(struct spi_device *spi)
spi_set_drvdata(spi, indio_dev);
st->chip_info = spi_get_device_match_data(spi);
+ if (!st->chip_info)
+ return -ENODATA;
+
st->spi = spi;
st->pwr_down = 0xf;
diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c
index 48aa4b015a14..0548f626bb45 100644
--- a/drivers/iio/frequency/adf4377.c
+++ b/drivers/iio/frequency/adf4377.c
@@ -964,6 +964,9 @@ static int adf4377_probe(struct spi_device *spi)
st->regmap = regmap;
st->spi = spi;
st->chip_info = spi_get_device_match_data(spi);
+ if (!st->chip_info)
+ return -ENODATA;
+
mutex_init(&st->lock);
ret = adf4377_properties_parse(st);
diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c
index 7e85c1b8dc16..9f8721c196a9 100644
--- a/drivers/iio/imu/adis16400.c
+++ b/drivers/iio/imu/adis16400.c
@@ -1150,6 +1150,9 @@ static int adis16400_probe(struct spi_device *spi)
/* setup the industrialio driver allocated elements */
st->variant = spi_get_device_match_data(spi);
+ if (!st->variant)
+ return -ENODATA;
+
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->channels = st->variant->channels;
indio_dev->num_channels = st->variant->num_channels;
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
index c8b48a5c5ed0..a44e4ea746a6 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
@@ -177,6 +177,7 @@ struct inv_icm42600_state {
struct {
s64 gyro;
s64 accel;
+ bool accel_force_odr;
} timestamp;
struct inv_icm42600_apex apex;
struct inv_icm42600_fifo fifo;
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
index 96a6213770d4..d858acad3bfb 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
@@ -1225,14 +1225,16 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev)
if (size <= 0)
return size;
- /* skip packet if no accel data or data is invalid */
- if (accel == NULL || !inv_icm42600_fifo_is_data_valid(accel))
- continue;
-
/* update odr */
- if (odr & INV_ICM42600_SENSOR_ACCEL)
+ if (odr & INV_ICM42600_SENSOR_ACCEL) {
inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
st->fifo.nb.total, no);
+ st->timestamp.accel_force_odr = false;
+ }
+
+ /* skip packet if no accel data or data is invalid */
+ if (accel == NULL || !inv_icm42600_fifo_is_data_valid(accel))
+ continue;
memcpy(&buffer.accel, accel, sizeof(buffer.accel));
/* convert 8 bits FIFO temperature in high resolution format */
@@ -1241,5 +1243,10 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev)
iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
}
+ if (st->timestamp.accel_force_odr) {
+ inv_sensors_timestamp_apply_odr(ts, 0, 0, 0);
+ st->timestamp.accel_force_odr = false;
+ }
+
return 0;
}
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
index 5c3840acf085..2c3e41a24b33 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
@@ -396,6 +396,8 @@ static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev)
mutex_lock(&st->lock);
inv_sensors_timestamp_apply_odr(ts, 0, 0, 0);
+ if (sensor == INV_ICM42600_SENSOR_ACCEL)
+ st->timestamp.accel_force_odr = false;
ret = inv_icm42600_buffer_set_fifo_en(st, st->fifo.en & ~sensor);
if (ret)
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
index 76eb22488e5f..e37b395dbd90 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
@@ -324,6 +324,15 @@ int inv_icm42600_set_accel_conf(struct inv_icm42600_state *st,
break;
}
+ /*
+ * If we change mode from low-power to low-noise because of odr,
+ * we need to force odr change.
+ */
+ if (oldconf->mode == INV_ICM42600_SENSOR_MODE_LOW_POWER &&
+ conf->mode == INV_ICM42600_SENSOR_MODE_LOW_NOISE &&
+ conf->odr != oldconf->odr)
+ st->timestamp.accel_force_odr = true;
+
/* set ACCEL_CONFIG0 register (accel fullscale & odr) */
if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
val = INV_ICM42600_ACCEL_CONFIG0_FS(conf->fs) |
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
index a18dcac93929..194838d88284 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
@@ -806,15 +806,15 @@ int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev)
if (size <= 0)
return size;
- /* skip packet if no gyro data or data is invalid */
- if (gyro == NULL || !inv_icm42600_fifo_is_data_valid(gyro))
- continue;
-
/* update odr */
if (odr & INV_ICM42600_SENSOR_GYRO)
inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
st->fifo.nb.total, no);
+ /* skip packet if no gyro data or data is invalid */
+ if (gyro == NULL || !inv_icm42600_fifo_is_data_valid(gyro))
+ continue;
+
memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
/* convert 8 bits FIFO temperature in high resolution format */
buffer.temp = temp ? (*temp * 64) : 0;
diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c
index 5ce01037ae55..fc07518faec7 100644
--- a/drivers/iio/light/apds9306.c
+++ b/drivers/iio/light/apds9306.c
@@ -61,7 +61,7 @@
#define APDS9306_NUM_REPEAT_RATES 7
#define APDS9306_INT_SRC_CLEAR 0
#define APDS9306_INT_SRC_ALS 1
-#define APDS9306_SAMP_FREQ_10HZ 0
+#define APDS9306_SAMP_FREQ_10HZ 2
/**
* struct part_id_gts_multiplier - Part no. and corresponding gts multiplier
diff --git a/drivers/iio/potentiometer/x9250.c b/drivers/iio/potentiometer/x9250.c
index 735348492699..1c9e22459058 100644
--- a/drivers/iio/potentiometer/x9250.c
+++ b/drivers/iio/potentiometer/x9250.c
@@ -155,10 +155,15 @@ static const char *const x9250_regulator_names[] = {
static int x9250_probe(struct spi_device *spi)
{
+ const struct x9250_cfg *cfg;
struct iio_dev *indio_dev;
struct x9250 *x9250;
int ret;
+ cfg = spi_get_device_match_data(spi);
+ if (!cfg)
+ return -ENODATA;
+
ret = devm_regulator_bulk_get_enable(&spi->dev, ARRAY_SIZE(x9250_regulator_names),
x9250_regulator_names);
if (ret)
@@ -176,7 +181,7 @@ static int x9250_probe(struct spi_device *spi)
x9250 = iio_priv(indio_dev);
x9250->spi = spi;
- x9250->cfg = spi_get_device_match_data(spi);
+ x9250->cfg = cfg;
x9250->wp_gpio = devm_gpiod_get_optional(&spi->dev, "wp", GPIOD_OUT_LOW);
if (IS_ERR(x9250->wp_gpio))
return dev_err_probe(&spi->dev, PTR_ERR(x9250->wp_gpio),
diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c
index 3b90384f17d7..e303923df675 100644
--- a/drivers/iio/pressure/bmp280-spi.c
+++ b/drivers/iio/pressure/bmp280-spi.c
@@ -83,6 +83,8 @@ static int bmp280_spi_probe(struct spi_device *spi)
struct regmap *regmap;
chip_info = spi_get_device_match_data(spi);
+ if (!chip_info)
+ return -ENODATA;
if (chip_info->spi_read_extra_byte)
bmp_regmap_bus = &bmp380_regmap_bus;
diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c
index 57c70384d210..648b31f37aa8 100644
--- a/drivers/infiniband/hw/hfi1/chip.c
+++ b/drivers/infiniband/hw/hfi1/chip.c
@@ -5576,7 +5576,7 @@ static int init_rcverr(struct hfi1_devdata *dd)
static void free_rcverr(struct hfi1_devdata *dd)
{
if (dd->rcverr_timer.function)
- timer_delete_sync(&dd->rcverr_timer);
+ timer_shutdown_sync(&dd->rcverr_timer);
}
static void handle_rxe_err(struct hfi1_devdata *dd, u32 unused, u64 reg)
@@ -12308,7 +12308,7 @@ static void free_cntrs(struct hfi1_devdata *dd)
int i;
if (dd->synth_stats_timer.function)
- timer_delete_sync(&dd->synth_stats_timer);
+ timer_shutdown_sync(&dd->synth_stats_timer);
cancel_work_sync(&dd->update_cntr_work);
ppd = (struct hfi1_pportdata *)(dd + 1);
for (i = 0; i < dd->num_pports; i++, ppd++) {
diff --git a/drivers/infiniband/hw/hfi1/fault.c b/drivers/infiniband/hw/hfi1/fault.c
index a45cbffd52c7..b6acc0edb116 100644
--- a/drivers/infiniband/hw/hfi1/fault.c
+++ b/drivers/infiniband/hw/hfi1/fault.c
@@ -216,7 +216,6 @@ int hfi1_fault_init_debugfs(struct hfi1_ibdev *ibd)
ibd->fault->attr.interval = 1;
ibd->fault->attr.require_end = ULONG_MAX;
ibd->fault->attr.stacktrace_depth = 32;
- ibd->fault->attr.dname = NULL;
ibd->fault->attr.verbose = 0;
ibd->fault->enable = false;
ibd->fault->opcode = false;
diff --git a/drivers/input/input.c b/drivers/input/input.c
index a500e1e276c2..9907dd9ec880 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -391,7 +391,7 @@ void input_handle_event(struct input_dev *dev,
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
- if (is_event_supported(type, dev->evbit, EV_MAX)) {
+ if (type == EV_SYN || is_event_supported(type, dev->evbit, EV_MAX)) {
guard(spinlock_irqsave)(&dev->event_lock);
input_handle_event(dev, type, code, value);
}
@@ -415,7 +415,7 @@ void input_inject_event(struct input_handle *handle,
struct input_dev *dev = handle->dev;
struct input_handle *grab;
- if (is_event_supported(type, dev->evbit, EV_MAX)) {
+ if (type == EV_SYN || is_event_supported(type, dev->evbit, EV_MAX)) {
guard(spinlock_irqsave)(&dev->event_lock);
guard(rcu)();
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 126f99cd0b29..716ab1c608f0 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -442,6 +442,7 @@ static const struct xpad_device {
{ 0x3537, 0x1004, "GameSir T4 Kaleid", 0, XTYPE_XBOX360 },
{ 0x3537, 0x100f, "GameSir Nova 2 Lite", 0, XTYPE_XBOX360 },
{ 0x3537, 0x1010, "GameSir G7 SE", 0, XTYPE_XBOXONE },
+ { 0x3537, 0x1040, "GameSir Nova Lite", 0, XTYPE_XBOX360 },
{ 0x3651, 0x1000, "CRKD SG", 0, XTYPE_XBOX360 },
{ 0x366c, 0x0005, "ByoWave Proteus Controller", MAP_SHARE_BUTTON, XTYPE_XBOXONE, FLAG_DELAY_INIT },
{ 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX },
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index e50a6fea9a60..4247772892ca 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -135,9 +135,6 @@ static void matrix_keypad_scan(struct work_struct *work)
memset(new_state, 0, sizeof(new_state));
- for (row = 0; row < keypad->num_row_gpios; row++)
- gpiod_direction_input(keypad->row_gpios[row]);
-
/* assert each column and read the row status out */
for (col = 0; col < keypad->num_col_gpios; col++) {
diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index c07c3a01b978..16c26d75f46f 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -409,7 +409,13 @@ static void iommu_set_device_table(struct amd_iommu *iommu)
BUG_ON(iommu->mmio_base == NULL);
- if (is_kdump_kernel())
+ /*
+ * A kdump kernel that reuses the previous kernel's device table must
+ * leave the base register alone. It already points at that table, and
+ * with SNP enabled the register is locked. If reuse failed, program
+ * the freshly allocated table like a normal boot.
+ */
+ if (iommu->pci_seg->old_dev_tbl_cpy)
return;
entry = iommu_virt_to_phys(dev_table);
@@ -642,8 +648,19 @@ static int __init find_last_devid_acpi(struct acpi_table_header *table, u16 pci_
/* Allocate per PCI segment device table */
static inline int __init alloc_dev_table(struct amd_iommu_pci_seg *pci_seg)
{
- pci_seg->dev_table = iommu_alloc_pages_sz(GFP_KERNEL | GFP_DMA32,
- pci_seg->dev_table_size);
+ gfp_t gfp = GFP_KERNEL;
+
+ /*
+ * Keep normal kernel device tables below 4G because a later kdump
+ * kernel only trusts and reuses old tables below that limit. A kdump
+ * kernel's new table is either discarded when reuse succeeds or
+ * programmed after the IOMMU is disabled, so the table does not
+ * need DMA32 memory (which the crashkernel may not have).
+ */
+ if (!is_kdump_kernel())
+ gfp |= GFP_DMA32;
+
+ pci_seg->dev_table = iommu_alloc_pages_sz(gfp, pci_seg->dev_table_size);
if (!pci_seg->dev_table)
return -ENOMEM;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 9da773ba550b..aa9ebf2bc96e 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1453,6 +1453,11 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
sg_dma_len(s) = s_length;
s->offset -= s_iova_off;
s_length = iova_align(iovad, s_length + s_iova_off);
+ if (overflows_type(s_length, s->length)) {
+ ret = -EOVERFLOW;
+
+ goto out_restore_sg;
+ }
s->length = s_length;
/*
@@ -1469,7 +1474,20 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
* time through here (i.e. before it has a meaningful value).
*/
if (pad_len && pad_len < s_length - 1) {
- prev->length += pad_len;
+ unsigned int new_prev_len;
+
+ /*
+ * For large mappings spanning multiple GBs we
+ * may not be able to fit all needed padding into
+ * sg->length.
+ */
+ if (check_add_overflow(prev->length, pad_len, &new_prev_len)) {
+ ret = -EOVERFLOW;
+
+ goto out_restore_sg;
+ }
+
+ prev->length = new_prev_len;
iova_len += pad_len;
}
diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
index e696e019e969..33c9e06df63f 100644
--- a/drivers/iommu/intel/iommu.h
+++ b/drivers/iommu/intel/iommu.h
@@ -1183,12 +1183,20 @@ static inline void qi_desc_dev_iotlb_pasid(u16 sid, u16 pfsid, u32 pasid,
unsigned int size_order,
struct qi_desc *desc)
{
- unsigned long mask = 1UL << (VTD_PAGE_SHIFT + size_order - 1);
-
desc->qw0 = QI_DEV_EIOTLB_PASID(pasid) | QI_DEV_EIOTLB_SID(sid) |
QI_DEV_EIOTLB_QDEP(qdep) | QI_DEIOTLB_TYPE |
QI_DEV_IOTLB_PFSID(pfsid);
+ /*
+ * The widest range the descriptor can express is a full flush, encoded
+ * by making bit 63 the least significant zero bit of ADDR, that is
+ * @size_order == 64 - VTD_PAGE_SHIFT. Bound @size_order there so that
+ * a caller passing something larger cannot produce an out-of-range
+ * shift below.
+ */
+ if (size_order > 64 - VTD_PAGE_SHIFT)
+ size_order = 64 - VTD_PAGE_SHIFT;
+
/*
* If S bit is 0, we only flush a single page. If S bit is set,
* The least significant zero bit indicates the invalidation address
@@ -1198,7 +1206,8 @@ static inline void qi_desc_dev_iotlb_pasid(u16 sid, u16 pfsid, u32 pasid,
* Max Invs Pending (MIP) is set to 0 for now until we have DIT in
* ECAP.
*/
- if (!IS_ALIGNED(addr, VTD_PAGE_SIZE << size_order))
+ if (size_order < 64 - VTD_PAGE_SHIFT &&
+ !IS_ALIGNED(addr, BIT_ULL(VTD_PAGE_SHIFT + size_order)))
pr_warn_ratelimited("Invalidate non-aligned address %llx, order %d\n",
addr, size_order);
@@ -1214,7 +1223,7 @@ static inline void qi_desc_dev_iotlb_pasid(u16 sid, u16 pfsid, u32 pasid,
desc->qw1 |= GENMASK_ULL(size_order + VTD_PAGE_SHIFT - 1,
VTD_PAGE_SHIFT);
/* Clear size_order bit to indicate size */
- desc->qw1 &= ~mask;
+ desc->qw1 &= ~BIT_ULL(VTD_PAGE_SHIFT + size_order - 1);
/* Set the S bit to indicate flushing more than 1 page */
desc->qw1 |= QI_DEV_EIOTLB_SIZE;
}
diff --git a/drivers/iommu/iommufd/io_pagetable.c b/drivers/iommu/iommufd/io_pagetable.c
index a4d7a9d43560..77891d4d65fb 100644
--- a/drivers/iommu/iommufd/io_pagetable.c
+++ b/drivers/iommu/iommufd/io_pagetable.c
@@ -568,6 +568,9 @@ int iommufd_check_iova_range(struct io_pagetable *iopt,
size_t iommu_pgsize = iopt->iova_alignment;
u64 last_iova;
+ if (!bitmap->length)
+ return -EINVAL;
+
if (check_add_overflow(bitmap->iova, bitmap->length - 1, &last_iova))
return -EOVERFLOW;
diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c
index 1dcc52760eca..4a7935e75e4d 100644
--- a/drivers/irqchip/irq-atmel-aic.c
+++ b/drivers/irqchip/irq-atmel-aic.c
@@ -123,7 +123,7 @@ static void aic_pm_shutdown(struct irq_data *d)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
- guard(raw_spinlock)(&gc->lock);
+ guard(raw_spinlock_irqsave)(&gc->lock);
irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR);
irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR);
}
diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c
index 1f14b401f71d..07a317ed06ef 100644
--- a/drivers/irqchip/irq-atmel-aic5.c
+++ b/drivers/irqchip/irq-atmel-aic5.c
@@ -217,7 +217,7 @@ static void aic5_pm_shutdown(struct irq_data *d)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
int i;
- guard(raw_spinlock)(&bgc->lock);
+ guard(raw_spinlock_irqsave)(&bgc->lock);
for (i = 0; i < dgc->irqs_per_chip; i++) {
irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR);
irq_reg_writel(bgc, 1, AT91_AIC5_IDCR);
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 8607ed0d9884..4443bf46e390 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -3488,7 +3488,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
* Also honor the ITS's own EID limit.
*/
id_bits = FIELD_GET(GITS_TYPER_IDBITS, its->typer) + 1;
- nvecs = min_t(unsigned int, nvecs, BIT(id_bits));
+ nvecs = min_t(u64, nvecs, BIT_ULL(id_bits));
nr_ites = max(2, nvecs);
sz = nr_ites * (FIELD_GET(GITS_TYPER_ITT_ENTRY_SIZE, its->typer) + 1);
sz = max(sz, ITS_ITT_ALIGN);
diff --git a/drivers/irqchip/irq-imx-intmux.c b/drivers/irqchip/irq-imx-intmux.c
index 5f9b204d350b..18e214e5b1b5 100644
--- a/drivers/irqchip/irq-imx-intmux.c
+++ b/drivers/irqchip/irq-imx-intmux.c
@@ -289,12 +289,16 @@ static int imx_intmux_probe(struct platform_device *pdev)
static void imx_intmux_remove(struct platform_device *pdev)
{
struct intmux_data *data = platform_get_drvdata(pdev);
- int i;
+ int i, ret;
+
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "failed to resume device: %d\n", ret);
for (i = 0; i < data->channum; i++) {
/* disable all interrupt sources of this channel */
- writel_relaxed(0, data->regs + CHANIER(i));
-
+ if (ret >= 0)
+ writel_relaxed(0, data->regs + CHANIER(i));
irq_set_chained_handler_and_data(data->irqchip_data[i].irq,
NULL, NULL);
@@ -302,6 +306,11 @@ static void imx_intmux_remove(struct platform_device *pdev)
}
pm_runtime_disable(&pdev->dev);
+ if (ret >= 0) {
+ pm_runtime_put_noidle(&pdev->dev);
+ clk_disable_unprepare(data->ipg_clk);
+ pm_runtime_set_suspended(&pdev->dev);
+ }
}
#ifdef CONFIG_PM
diff --git a/drivers/irqchip/irq-mchp-eic.c b/drivers/irqchip/irq-mchp-eic.c
index 31093a8ab67c..82cb0513bbb8 100644
--- a/drivers/irqchip/irq-mchp-eic.c
+++ b/drivers/irqchip/irq-mchp-eic.c
@@ -233,7 +233,7 @@ static int mchp_eic_probe(struct platform_device *pdev, struct device_node *pare
ret = clk_prepare_enable(eic->clk);
if (ret)
- goto unmap;
+ goto clk_put;
for (i = 0; i < MCHP_EIC_NIRQ; i++) {
struct of_phandle_args irq;
@@ -270,6 +270,8 @@ static int mchp_eic_probe(struct platform_device *pdev, struct device_node *pare
clk_unprepare:
clk_disable_unprepare(eic->clk);
+clk_put:
+ clk_put(eic->clk);
unmap:
iounmap(eic->base);
free:
diff --git a/drivers/irqchip/irq-msi-lib.c b/drivers/irqchip/irq-msi-lib.c
index d5eefc3d7215..8b8a64f63a71 100644
--- a/drivers/irqchip/irq-msi-lib.c
+++ b/drivers/irqchip/irq-msi-lib.c
@@ -72,6 +72,7 @@ bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
info->flags = MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | MSI_FLAG_FREE_MSI_DESCS;
fallthrough;
case DOMAIN_BUS_WIRED_TO_MSI:
+ info->flags |= MSI_FLAG_FREE_MSI_DESCS;
/* Remove PCI specific flags */
required_flags &= ~MSI_FLAG_PCI_MSI_MASK_PARENT;
break;
diff --git a/drivers/irqchip/irq-riscv-imsic-platform.c b/drivers/irqchip/irq-riscv-imsic-platform.c
index 643c8e459611..52673ff9edf4 100644
--- a/drivers/irqchip/irq-riscv-imsic-platform.c
+++ b/drivers/irqchip/irq-riscv-imsic-platform.c
@@ -61,7 +61,7 @@ static int imsic_irq_retrigger(struct irq_data *d)
local = per_cpu_ptr(imsic->global.local, vec->cpu);
writel_relaxed(vec->local_id, local->msi_va);
- return 0;
+ return 1;
}
static void imsic_irq_ack(struct irq_data *d)
diff --git a/drivers/leds/flash/leds-ktd2692.c b/drivers/leds/flash/leds-ktd2692.c
index 0f16eefcfe4c..04e2efdac3e6 100644
--- a/drivers/leds/flash/leds-ktd2692.c
+++ b/drivers/leds/flash/leds-ktd2692.c
@@ -69,6 +69,8 @@ const struct expresswire_timing ktd2692_timing = {
};
struct ktd2692_context {
+ struct device *dev;
+
/* Common ExpressWire properties (ctrl GPIO and timing) */
struct expresswire_common_props props;
@@ -196,13 +198,12 @@ static void ktd2692_setup(struct ktd2692_context *led)
static void regulator_disable_action(void *_data)
{
- struct device *dev = _data;
- struct ktd2692_context *led = dev_get_drvdata(dev);
+ struct ktd2692_context *led = _data;
int ret;
ret = regulator_disable(led->regulator);
if (ret)
- dev_err(dev, "Failed to disable supply: %d\n", ret);
+ dev_err(led->dev, "Failed to disable supply: %d\n", ret);
}
static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
@@ -233,7 +234,7 @@ static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
dev_err(dev, "Failed to enable supply: %d\n", ret);
} else {
ret = devm_add_action_or_reset(dev,
- regulator_disable_action, dev);
+ regulator_disable_action, led);
if (ret)
return ret;
}
@@ -290,6 +291,7 @@ static int ktd2692_probe(struct platform_device *pdev)
if (!led)
return -ENOMEM;
+ led->dev = &pdev->dev;
fled_cdev = &led->fled_cdev;
led_cdev = &fled_cdev->led_cdev;
led->props.timing = ktd2692_timing;
diff --git a/drivers/mailbox/bcm74110-mailbox.c b/drivers/mailbox/bcm74110-mailbox.c
index 2e7e86f3e6a4..0ae6c56b8786 100644
--- a/drivers/mailbox/bcm74110-mailbox.c
+++ b/drivers/mailbox/bcm74110-mailbox.c
@@ -97,6 +97,8 @@ struct bcm74110_mbox_chan {
int type;
};
+#define BCM74110_INIT_MSG_MAX 16
+
struct bcm74110_mbox {
struct platform_device *pdev;
void __iomem *base;
@@ -105,6 +107,7 @@ struct bcm74110_mbox {
int rx_chan;
int rx_irq;
struct list_head rx_svc_init_list;
+ unsigned int rx_svc_init_count;
spinlock_t rx_svc_list_lock;
struct mbox_controller controller;
@@ -148,7 +151,15 @@ static void bcm74110_rx_push_init_msg(struct bcm74110_mbox *mbox, u32 val)
msg->msg = val;
spin_lock(&mbox->rx_svc_list_lock);
+ if (mbox->rx_svc_init_count >= BCM74110_INIT_MSG_MAX) {
+ spin_unlock(&mbox->rx_svc_list_lock);
+ kfree(msg);
+ dev_warn_ratelimited(&mbox->pdev->dev,
+ "PMC INIT msg list full, dropping message\n");
+ return;
+ }
list_add_tail(&msg->list_entry, &mbox->rx_svc_init_list);
+ mbox->rx_svc_init_count++;
spin_unlock(&mbox->rx_svc_list_lock);
}
@@ -225,6 +236,7 @@ static int bcm74110_rx_pop_init_msg(struct bcm74110_mbox *mbox, u32 func_type,
list_entry) {
if (BCM_MSG_GET_FIELD(msg->msg, FUNC) == func_type) {
list_del(&msg->list_entry);
+ mbox->rx_svc_init_count--;
found = true;
break;
}
@@ -248,6 +260,7 @@ static void bcm74110_rx_flush_msg(struct bcm74110_mbox *mbox)
spin_lock_irqsave(&mbox->rx_svc_list_lock, flags);
list_splice_init(&mbox->rx_svc_init_list, &list_temp);
+ mbox->rx_svc_init_count = 0;
spin_unlock_irqrestore(&mbox->rx_svc_list_lock, flags);
list_for_each_entry_safe(msg, msg_tmp, &list_temp, list_entry) {
diff --git a/drivers/mailbox/mailbox-altera.c b/drivers/mailbox/mailbox-altera.c
index 17278c2571d3..7cb91413ef7f 100644
--- a/drivers/mailbox/mailbox-altera.c
+++ b/drivers/mailbox/mailbox-altera.c
@@ -270,7 +270,7 @@ static void altera_mbox_shutdown(struct mbox_chan *chan)
writel_relaxed(~0, mbox->mbox_base + MAILBOX_INTMASK_REG);
free_irq(mbox->irq, chan);
} else if (!mbox->is_sender) {
- timer_delete_sync(&mbox->rxpoll_timer);
+ timer_shutdown_sync(&mbox->rxpoll_timer);
}
}
diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c
index a1360b70b83f..6e915ec5deaa 100644
--- a/drivers/mailbox/mtk-cmdq-mailbox.c
+++ b/drivers/mailbox/mtk-cmdq-mailbox.c
@@ -506,8 +506,10 @@ static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
int ret;
ret = pm_runtime_get_sync(cmdq->mbox.dev);
- if (ret < 0)
+ if (ret < 0) {
+ pm_runtime_put_noidle(cmdq->mbox.dev);
return ret;
+ }
spin_lock_irqsave(&thread->chan->lock, flags);
if (list_empty(&thread->task_busy_list))
@@ -544,6 +546,9 @@ static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
dev_err(cmdq->mbox.dev, "Fail to wait GCE thread 0x%x done\n",
(u32)(thread->base - cmdq->base));
+ pm_runtime_mark_last_busy(cmdq->mbox.dev);
+ pm_runtime_put_autosuspend(cmdq->mbox.dev);
+
return -EFAULT;
}
pm_runtime_mark_last_busy(cmdq->mbox.dev);
diff --git a/drivers/mailbox/qcom-ipcc.c b/drivers/mailbox/qcom-ipcc.c
index dead907aa7a2..b0b4e40d9202 100644
--- a/drivers/mailbox/qcom-ipcc.c
+++ b/drivers/mailbox/qcom-ipcc.c
@@ -10,6 +10,7 @@
#include <linux/mailbox_controller.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/spinlock.h>
#include <dt-bindings/mailbox/qcom-ipcc.h>
@@ -151,6 +152,9 @@ static int qcom_ipcc_mbox_send_data(struct mbox_chan *chan, void *data)
struct qcom_ipcc_chan_info *mchan = chan->con_priv;
u32 hwirq;
+ if (!mchan)
+ return -EINVAL;
+
hwirq = qcom_ipcc_get_hwirq(mchan->client_id, mchan->signal_id);
writel(hwirq, ipcc->base + IPCC_REG_SEND_ID);
@@ -159,6 +163,8 @@ static int qcom_ipcc_mbox_send_data(struct mbox_chan *chan, void *data)
static void qcom_ipcc_mbox_shutdown(struct mbox_chan *chan)
{
+ guard(spinlock_irqsave)(&chan->lock);
+
chan->con_priv = NULL;
}
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 59e5d8aa42b7..07e2f0fccefe 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -2402,8 +2402,10 @@ static void __scan(struct dm_bufio_client *c)
for (l = 0; l < LIST_SIZE; l++) {
while (true) {
- if (count - freed <= retain_target)
+ if (count - freed <= retain_target) {
atomic_long_set(&c->need_shrink, 0);
+ break;
+ }
if (!atomic_long_read(&c->need_shrink))
break;
@@ -2521,6 +2523,13 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign
init_waitqueue_head(&c->free_buffer_wait);
c->async_write_error = 0;
+ /*
+ * A client with no buffers yet is not holding the oldest buffer in
+ * the system; leaving this zeroed would claim exactly that and keep
+ * the client pinned at the front of dm_bufio_all_clients.
+ */
+ c->oldest_buffer = jiffies;
+
c->dm_io = dm_io_client_create();
if (IS_ERR(c->dm_io)) {
r = PTR_ERR(c->dm_io);
@@ -2755,9 +2764,23 @@ static unsigned long __evict_a_few(unsigned long nr_buffers)
dm_bufio_unlock(c);
- if (count)
+ if (count) {
c->oldest_buffer = oldest_buffer;
- __insert_client(c);
+ __insert_client(c);
+ } else {
+ /*
+ * We evicted nothing, so 'oldest_buffer' tells us nothing new
+ * and the client keeps its old, possibly very stale, value.
+ * Requeue it at the back rather than by that value, otherwise
+ * __insert_client() would put it straight back at the front
+ * and every subsequent call would pop this same client again,
+ * never reaching any of the others.
+ *
+ * This leaves the list slightly out of order, which is fine -
+ * it is only ever an approximation.
+ */
+ list_add_tail(&c->client_list, &dm_bufio_all_clients);
+ }
return count;
}
@@ -2782,11 +2805,23 @@ static void evict_old(void)
{
unsigned long threshold = dm_bufio_cache_size -
dm_bufio_cache_size / DM_BUFIO_LOW_WATERMARK_RATIO;
+ int unproductive = 0;
mutex_lock(&dm_bufio_clients_lock);
while (dm_bufio_current_allocated > threshold) {
- if (!__evict_a_few(64))
- break;
+ if (__evict_a_few(64)) {
+ unproductive = 0;
+ } else {
+ /*
+ * This client had nothing to give. Try the others
+ * before giving up: one idle client, or one whose
+ * buffers are all held, must not stop us evicting
+ * from everybody else. Stop once a whole sweep of
+ * the client list has freed nothing.
+ */
+ if (++unproductive >= dm_bufio_client_count)
+ break;
+ }
cond_resched();
}
mutex_unlock(&dm_bufio_clients_lock);
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index 0dc429eb2692..989f958c229b 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -325,8 +325,11 @@ static int read_header(struct pstore *ps, int *new_snapshot)
}
ps->io_client = dm_io_client_create();
- if (IS_ERR(ps->io_client))
- return PTR_ERR(ps->io_client);
+ if (IS_ERR(ps->io_client)) {
+ r = PTR_ERR(ps->io_client);
+ ps->io_client = NULL;
+ return r;
+ }
r = alloc_area(ps);
if (r)
diff --git a/drivers/md/dm-vdo/indexer/chapter-index.c b/drivers/md/dm-vdo/indexer/chapter-index.c
index fb1db41c794b..7a744b2da28f 100644
--- a/drivers/md/dm-vdo/indexer/chapter-index.c
+++ b/drivers/md/dm-vdo/indexer/chapter-index.c
@@ -284,10 +284,17 @@ int uds_search_chapter_index_page(struct delta_index_page *index_page,
if (result != UDS_SUCCESS)
return result;
- if (was_entry_found(&entry, address))
- *record_page_ptr = uds_get_delta_entry_value(&entry);
- else
+ if (!was_entry_found(&entry, address)) {
*record_page_ptr = NO_CHAPTER_INDEX_ENTRY;
+ return UDS_SUCCESS;
+ }
- return UDS_SUCCESS;
+ *record_page_ptr = uds_get_delta_entry_value(&entry);
+ result = VDO_ASSERT(*record_page_ptr < geometry->record_pages_per_chapter,
+ "0 <= %d < %u", *record_page_ptr,
+ geometry->record_pages_per_chapter);
+ if (result != VDO_SUCCESS)
+ *record_page_ptr = NO_CHAPTER_INDEX_ENTRY;
+
+ return result;
}
diff --git a/drivers/md/dm-vdo/indexer/delta-index.c b/drivers/md/dm-vdo/indexer/delta-index.c
index 0ac2443f0df3..a42de1af4b69 100644
--- a/drivers/md/dm-vdo/indexer/delta-index.c
+++ b/drivers/md/dm-vdo/indexer/delta-index.c
@@ -803,7 +803,7 @@ static void compute_new_list_offsets(struct delta_zone *delta_zone, u32 growing_
(delta_zone->size * BITS_PER_BYTE - delta_lists[tail_guard_index].size);
}
-static void rebalance_lists(struct delta_zone *delta_zone)
+static int rebalance_lists(struct delta_zone *delta_zone)
{
struct delta_list *delta_lists;
u32 i;
@@ -814,9 +814,17 @@ static void rebalance_lists(struct delta_zone *delta_zone)
for (i = 0; i <= delta_zone->list_count + 1; i++)
used_space += get_delta_list_byte_size(&delta_lists[i]);
+ if (delta_zone->size < used_space) {
+ return vdo_log_warning_strerror(UDS_CORRUPT_DATA,
+ "delta zone lists overflow zone size %zu",
+ delta_zone->size);
+ }
+
compute_new_list_offsets(delta_zone, 0, 0, used_space);
for (i = 1; i <= delta_zone->list_count + 1; i++)
delta_lists[i].start = delta_zone->new_offsets[i];
+
+ return UDS_SUCCESS;
}
/* Start restoring a delta index from multiple input streams. */
@@ -944,8 +952,11 @@ int uds_start_restoring_delta_index(struct delta_index *delta_index,
}
/* Prepare each zone to start receiving the delta list data. */
- for (z = 0; z < delta_index->zone_count; z++)
- rebalance_lists(&delta_index->delta_zones[z]);
+ for (z = 0; z < delta_index->zone_count; z++) {
+ result = rebalance_lists(&delta_index->delta_zones[z]);
+ if (result != UDS_SUCCESS)
+ return result;
+ }
return UDS_SUCCESS;
}
diff --git a/drivers/md/dm-vdo/indexer/volume.c b/drivers/md/dm-vdo/indexer/volume.c
index 425b3a74f4db..f7de78e0d0db 100644
--- a/drivers/md/dm-vdo/indexer/volume.c
+++ b/drivers/md/dm-vdo/indexer/volume.c
@@ -891,12 +891,6 @@ int uds_search_cached_record_page(struct volume *volume, struct uds_request *req
if (record_page_number == NO_CHAPTER_INDEX_ENTRY)
return UDS_SUCCESS;
- result = VDO_ASSERT(record_page_number < geometry->record_pages_per_chapter,
- "0 <= %d < %u", record_page_number,
- geometry->record_pages_per_chapter);
- if (result != VDO_SUCCESS)
- return result;
-
page_number = geometry->index_pages_per_chapter + record_page_number;
physical_page = map_to_physical_page(volume->geometry, chapter, page_number);
diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c
index 34148aa4df79..aa758d5ef601 100644
--- a/drivers/md/dm-writecache.c
+++ b/drivers/md/dm-writecache.c
@@ -194,6 +194,7 @@ struct dm_writecache {
struct task_struct *endio_thread;
struct task_struct *flush_thread;
+ spinlock_t flush_list_lock;
struct bio_list flush_list;
struct dm_kcopyd_client *dm_kcopyd;
@@ -899,8 +900,6 @@ static void writecache_discard(struct dm_writecache *wc, sector_t start, sector_
}
discarded_something = true;
}
- if (!writecache_entry_is_committed(wc, e))
- wc->uncommitted_blocks--;
writecache_free_entry(wc, e);
}
@@ -1290,9 +1289,11 @@ static int writecache_flush_thread(void *data)
struct bio *bio;
wc_lock(wc);
+ spin_lock_irq(&wc->flush_list_lock);
bio = bio_list_pop(&wc->flush_list);
if (!bio) {
set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&wc->flush_list_lock);
wc_unlock(wc);
if (unlikely(kthread_should_stop())) {
@@ -1303,6 +1304,7 @@ static int writecache_flush_thread(void *data)
schedule();
continue;
}
+ spin_unlock_irq(&wc->flush_list_lock);
if (bio_op(bio) == REQ_OP_DISCARD) {
writecache_discard(wc, bio->bi_iter.bi_sector,
@@ -1324,9 +1326,12 @@ static int writecache_flush_thread(void *data)
static void writecache_offload_bio(struct dm_writecache *wc, struct bio *bio)
{
+ unsigned long flags;
+ spin_lock_irqsave(&wc->flush_list_lock, flags);
if (bio_list_empty(&wc->flush_list))
wake_up_process(wc->flush_thread);
bio_list_add(&wc->flush_list, bio);
+ spin_unlock_irqrestore(&wc->flush_list_lock, flags);
}
enum wc_map_op {
@@ -1424,7 +1429,8 @@ static void writecache_bio_copy_ssd(struct dm_writecache *wc, struct bio *bio,
dm_accept_partial_bio(bio, bio_size >> SECTOR_SHIFT);
wc->stats.writes += bio->bi_iter.bi_size >> wc->block_size_bits;
- wc->stats.writes_allocate += (bio->bi_iter.bi_size - wc->block_size) >> wc->block_size_bits;
+ if (!search_used)
+ wc->stats.writes_allocate += (bio->bi_iter.bi_size - wc->block_size) >> wc->block_size_bits;
if (unlikely(wc->uncommitted_blocks >= wc->autocommit_blocks)) {
wc->uncommitted_blocks = 0;
@@ -1437,6 +1443,8 @@ static void writecache_bio_copy_ssd(struct dm_writecache *wc, struct bio *bio,
static enum wc_map_op writecache_map_write(struct dm_writecache *wc, struct bio *bio)
{
struct wc_entry *e;
+ enum wc_map_op ret = WC_MAP_SUBMIT;
+ bool need_flush = false;
do {
bool found_entry = false;
@@ -1473,7 +1481,8 @@ static enum wc_map_op writecache_map_write(struct dm_writecache *wc, struct bio
writecache_map_remap_origin(wc, bio, e);
wc->stats.writes_around += bio->bi_iter.bi_size >> wc->block_size_bits;
wc->stats.writes += bio->bi_iter.bi_size >> wc->block_size_bits;
- return WC_MAP_REMAP_ORIGIN;
+ ret = WC_MAP_REMAP_ORIGIN;
+ goto flush_pmem_and_ret;
}
wc->stats.writes_blocked_on_freelist++;
writecache_wait_on_freelist(wc);
@@ -1487,18 +1496,25 @@ static enum wc_map_op writecache_map_write(struct dm_writecache *wc, struct bio
if (WC_MODE_PMEM(wc)) {
bio_copy_block(wc, bio, memory_data(wc, e));
wc->stats.writes++;
+ need_flush = true;
} else {
writecache_bio_copy_ssd(wc, bio, e, search_used);
return WC_MAP_REMAP;
}
} while (bio->bi_iter.bi_size);
- if (unlikely(bio->bi_opf & REQ_FUA || wc->uncommitted_blocks >= wc->autocommit_blocks))
- writecache_flush(wc);
- else
- writecache_schedule_autocommit(wc);
+flush_pmem_and_ret:
+ if (need_flush) {
+ if (unlikely(bio->bi_opf & REQ_FUA || wc->uncommitted_blocks >= wc->autocommit_blocks)) {
+ writecache_flush(wc);
+ if (writecache_has_error(wc))
+ ret = WC_MAP_ERROR;
+ } else {
+ writecache_schedule_autocommit(wc);
+ }
+ }
- return WC_MAP_SUBMIT;
+ return ret;
}
static enum wc_map_op writecache_map_flush(struct dm_writecache *wc, struct bio *bio)
@@ -1588,7 +1604,10 @@ static int writecache_map(struct dm_target *ti, struct bio *bio)
case WC_MAP_REMAP:
/* make sure that writecache_end_io decrements bio_in_progress: */
- bio->bi_private = (void *)1;
+ if (unlikely(bio->bi_opf & REQ_FUA) && bio_op(bio) == REQ_OP_WRITE)
+ bio->bi_private = (void *)3;
+ else
+ bio->bi_private = (void *)1;
atomic_inc(&wc->bio_in_progress[bio_data_dir(bio)]);
wc_unlock(wc);
return DM_MAPIO_REMAPPED;
@@ -1618,12 +1637,19 @@ static int writecache_end_io(struct dm_target *ti, struct bio *bio, blk_status_t
{
struct dm_writecache *wc = ti->private;
- if (bio->bi_private == (void *)1) {
+ if (bio->bi_private == (void *)1 || bio->bi_private == (void *)3) {
int dir = bio_data_dir(bio);
+ bool fua = bio->bi_private == (void *)3;
if (atomic_dec_and_test(&wc->bio_in_progress[dir]))
if (unlikely(waitqueue_active(&wc->bio_in_progress_wait[dir])))
wake_up(&wc->bio_in_progress_wait[dir]);
+
+ if (fua && likely(*status == BLK_STS_OK)) {
+ bio->bi_private = NULL;
+ writecache_offload_bio(wc, bio);
+ return DM_ENDIO_INCOMPLETE;
+ }
} else if (bio->bi_private == (void *)2) {
dm_iot_io_end(&wc->iot, 1);
}
@@ -2507,6 +2533,11 @@ static int writecache_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
if (WC_MODE_PMEM(wc)) {
+ if (!wc->ssd_dev->dax_dev) {
+ r = -EOPNOTSUPP;
+ ti->error = "Cache device is not a DAX device";
+ goto bad;
+ }
if (!dax_synchronous(wc->ssd_dev->dax_dev)) {
r = -EOPNOTSUPP;
ti->error = "Asynchronous persistent memory not supported as pmem cache";
@@ -2524,6 +2555,7 @@ static int writecache_ctr(struct dm_target *ti, unsigned int argc, char **argv)
wc->memory_map_size -= (uint64_t)wc->start_sector << SECTOR_SHIFT;
+ spin_lock_init(&wc->flush_list_lock);
bio_list_init(&wc->flush_list);
wc->flush_thread = kthread_run(writecache_flush_thread, wc, "dm_writecache_flush");
if (IS_ERR(wc->flush_thread)) {
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index f587b7229ce8..077bf8b16a0b 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1349,7 +1349,7 @@ void dm_accept_partial_bio(struct bio *bio, unsigned int n_sectors)
* for accounting but it is being reduced so update accordingly.
*/
dm_io_set_flag(io, DM_IO_WAS_SPLIT);
- io->sectors = n_sectors;
+ io->sectors = *tio->len_ptr;
io->sector_offset = bio_sectors(io->orig_bio);
}
EXPORT_SYMBOL_GPL(dm_accept_partial_bio);
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 44f44219ed7f..937a2f4da84b 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -1930,8 +1930,11 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_
if (!badblocks_set(&rdev->badblocks, sector, count, 1))
return -EINVAL;
}
- } else if (sb->bblog_offset != 0)
+ } else if (sb->bblog_offset != 0) {
+ if (le16_to_cpu(sb->bblog_size) > (PAGE_SIZE / 512))
+ return -EINVAL;
rdev->badblocks.shift = 0;
+ }
if ((le32_to_cpu(sb->feature_map) &
(MD_FEATURE_PPL | MD_FEATURE_MULTIPLE_PPLS))) {
@@ -6255,12 +6258,22 @@ struct mddev *md_alloc(dev_t dev, char *name)
disk->events |= DISK_EVENT_MEDIA_CHANGE;
mddev->gendisk = disk;
+
+ /*
+ * add_disk() makes the array openable before mddev->kobj exists.
+ * Hold reconfig_mutex until kobject_add() is done so that ioctls
+ * issued in between wait instead of using an uninitialised kobj.
+ */
+ mddev_lock_nointr(mddev);
error = add_disk(disk);
- if (error)
+ if (error) {
+ mddev_unlock(mddev);
goto out_put_disk;
+ }
kobject_init(&mddev->kobj, &md_ktype);
error = kobject_add(&mddev->kobj, &disk_to_dev(disk)->kobj, "%s", "md");
+ mddev_unlock(mddev);
if (error) {
/*
* The disk is already live at this point. Clear the hold flag
diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
index 2518781cbdf6..1f7bf680234d 100644
--- a/drivers/md/raid5-cache.c
+++ b/drivers/md/raid5-cache.c
@@ -2001,27 +2001,36 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
if (!page)
return -ENOMEM;
- while (mb_offset < le32_to_cpu(mb->meta_size)) {
+ while (mb_offset + sizeof(struct r5l_payload_header) <= le32_to_cpu(mb->meta_size)) {
+ u32 payload_size;
sector_t payload_len;
payload = (void *)mb + mb_offset;
payload_flush = (void *)mb + mb_offset;
if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_DATA) {
+ if (mb_offset + sizeof(struct r5l_payload_data_parity)
+ > le32_to_cpu(mb->meta_size))
+ goto mismatch;
+ payload_size = le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9);
payload_len = sizeof(struct r5l_payload_data_parity) +
- (sector_t)sizeof(__le32) *
- (le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
- if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
+ (sector_t)sizeof(__le32) * payload_size;
+ if (mb_offset + payload_len > le32_to_cpu(mb->meta_size) ||
+ payload_size < 1)
goto mismatch;
if (r5l_recovery_verify_data_checksum(
log, ctx, page, log_offset,
payload->checksum[0]) < 0)
goto mismatch;
} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_PARITY) {
+ if (mb_offset + sizeof(struct r5l_payload_data_parity)
+ > le32_to_cpu(mb->meta_size))
+ goto mismatch;
+ payload_size = le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9);
payload_len = sizeof(struct r5l_payload_data_parity) +
- (sector_t)sizeof(__le32) *
- (le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
- if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
+ (sector_t)sizeof(__le32) * payload_size;
+ if (mb_offset + payload_len > le32_to_cpu(mb->meta_size) ||
+ payload_size < conf->max_degraded)
goto mismatch;
if (r5l_recovery_verify_data_checksum(
log, ctx, page, log_offset,
@@ -2035,6 +2044,9 @@ r5l_recovery_verify_data_checksum_for_mb(struct r5l_log *log,
payload->checksum[1]) < 0)
goto mismatch;
} else if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
+ if (mb_offset + sizeof(struct r5l_payload_flush)
+ > le32_to_cpu(mb->meta_size))
+ goto mismatch;
payload_len = sizeof(struct r5l_payload_flush) +
(sector_t)le32_to_cpu(payload_flush->size);
if (mb_offset + payload_len > le32_to_cpu(mb->meta_size))
@@ -2096,7 +2108,7 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
mb_offset = sizeof(struct r5l_meta_block);
log_offset = r5l_ring_add(log, ctx->pos, BLOCK_SECTORS);
- while (mb_offset < le32_to_cpu(mb->meta_size)) {
+ while (mb_offset + sizeof(struct r5l_payload_header) < le32_to_cpu(mb->meta_size)) {
sector_t payload_len;
int dd;
@@ -2106,6 +2118,9 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
if (le16_to_cpu(payload->header.type) == R5LOG_PAYLOAD_FLUSH) {
int i, count;
+ if (mb_offset + sizeof(struct r5l_payload_flush) >
+ le32_to_cpu(mb->meta_size))
+ return -EINVAL;
payload_len = sizeof(struct r5l_payload_flush) +
(sector_t)le32_to_cpu(payload_flush->size);
if (mb_offset + payload_len >
@@ -2130,6 +2145,9 @@ r5c_recovery_analyze_meta_block(struct r5l_log *log,
}
/* DATA or PARITY payload */
+ if (mb_offset + sizeof(struct r5l_payload_data_parity) >
+ le32_to_cpu(mb->meta_size))
+ return -EINVAL;
payload_len = sizeof(struct r5l_payload_data_parity) +
(sector_t)sizeof(__le32) *
(le32_to_cpu(payload->size) >> (PAGE_SHIFT - 9));
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 56d7e05e94ce..6dfefbacfe3c 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -2627,6 +2627,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
} else {
kfree(conf->disks);
conf->disks = ndisks;
+ conf->disks_cnt = newsize;
}
} else
err = -ENOMEM;
@@ -7391,7 +7392,8 @@ static void raid5_free_percpu(struct r5conf *conf)
if (!conf->percpu)
return;
- cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
+ if (!hlist_unhashed_lockless(&conf->node))
+ cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
free_percpu(conf->percpu);
}
@@ -7405,7 +7407,7 @@ static void free_conf(struct r5conf *conf)
free_thread_groups(conf);
shrink_stripes(conf);
raid5_free_percpu(conf);
- for (i = 0; i < conf->pool_size; i++)
+ for (i = 0; i < conf->disks_cnt; i++)
if (conf->disks[i].extra_page)
put_page(conf->disks[i].extra_page);
kfree(conf->disks);
@@ -7588,7 +7590,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
if (!conf->disks)
goto abort;
-
+ conf->disks_cnt = max_disks;
for (i = 0; i < max_disks; i++) {
conf->disks[i].extra_page = alloc_page(GFP_KERNEL);
if (!conf->disks[i].extra_page)
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index eafc6e9ed6ee..fa9647a862ad 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -668,6 +668,7 @@ struct r5conf {
unsigned long cache_state;
struct shrinker *shrinker;
int pool_size; /* number of disks in stripeheads in pool */
+ int disks_cnt; /* number of entries in disks[] array */
spinlock_t device_lock;
struct disk_info *disks;
struct bio_set bio_split;
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 48410ce33490..638def4cb569 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
@@ -846,7 +846,9 @@ static irqreturn_t extron_interrupt(struct serio *serio, unsigned char data,
return IRQ_HANDLED;
memcpy(extron->data, extron->buf, extron->idx);
extron->len = extron->idx;
- extron->data[extron->len] = 0;
+ /* Keep fixed-offset response tests from using stale tail bytes. */
+ memset(extron->data + extron->len, 0,
+ min_t(size_t, 10, sizeof(extron->data) - extron->len));
if (debug)
dev_info(extron->dev, "received %s\n", extron->data);
extron->idx = 0;
diff --git a/drivers/media/common/saa7146/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c
index 27c53eed8fe3..e908af0693d2 100644
--- a/drivers/media/common/saa7146/saa7146_core.c
+++ b/drivers/media/common/saa7146/saa7146_core.c
@@ -340,6 +340,8 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
goto out;
}
+ spin_lock_init(&dev->int_slock);
+
/* create a nice device name */
sprintf(dev->name, "saa7146 (%d)", saa7146_num);
@@ -425,7 +427,6 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
dev->ext = ext;
mutex_init(&dev->v4l2_lock);
- spin_lock_init(&dev->int_slock);
spin_lock_init(&dev->slock);
mutex_init(&dev->i2c_lock);
diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c
index e7b9b9b11d7d..ce228f2ef330 100644
--- a/drivers/media/dvb-frontends/stv0900_core.c
+++ b/drivers/media/dvb-frontends/stv0900_core.c
@@ -1775,6 +1775,8 @@ static int stv0900_recv_slave_reply(struct dvb_frontend *fe,
if (stv0900_get_bits(intp, RX_END)) {
reply->msg_len = stv0900_get_bits(intp, FIFO_BYTENBR);
+ if (reply->msg_len > sizeof(reply->msg))
+ reply->msg_len = sizeof(reply->msg);
for (i = 0; i < reply->msg_len; i++)
reply->msg[i] = stv0900_read_reg(intp, DISRXDATA);
diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c
index f273efa330cf..b95b6b4a69c7 100644
--- a/drivers/media/dvb-frontends/stv090x.c
+++ b/drivers/media/dvb-frontends/stv090x.c
@@ -3902,6 +3902,8 @@ static int stv090x_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_s
if (rx_end) {
reply->msg_len = STV090x_GETFIELD_Px(reg, FIFO_BYTENBR_FIELD);
+ if (reply->msg_len > sizeof(reply->msg))
+ reply->msg_len = sizeof(reply->msg);
for (i = 0; i < reply->msg_len; i++)
reply->msg[i] = STV090x_READ_DEMOD(state, DISRXDATA);
}
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index 5cbc973df684..6223911e324a 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -1425,6 +1425,7 @@ static int adv7180_probe(struct i2c_client *client)
if (state == NULL)
return -ENOMEM;
+ mutex_init(&state->mutex);
state->client = client;
state->field = V4L2_FIELD_ALTERNATE;
state->chip_info = i2c_get_match_data(client);
@@ -1466,7 +1467,6 @@ static int adv7180_probe(struct i2c_client *client)
}
state->irq = client->irq;
- mutex_init(&state->mutex);
state->curr_norm = V4L2_STD_NTSC;
state->input = 0;
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 516553fb17e9..cfecfff4c3aa 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -2641,8 +2641,9 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
"(16-235)" : "(0-255)",
(reg_io_0x02 & 0x08) ? "enabled" : "disabled");
}
+ ret = cp_read(sd, info->cp_csc) >> 4;
v4l2_info(sd, "Color space conversion: %s\n",
- csc_coeff_sel_rb[cp_read(sd, info->cp_csc) >> 4]);
+ ret < 0 ? "" : csc_coeff_sel_rb[ret]);
if (!is_digital_input(sd))
return 0;
diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c
index 3d399af01322..d08d01098a9a 100644
--- a/drivers/media/i2c/alvium-csi2.c
+++ b/drivers/media/i2c/alvium-csi2.c
@@ -2523,6 +2523,7 @@ static void alvium_remove(struct i2c_client *client)
* make sure to turn power off manually.
*/
pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
if (!pm_runtime_status_suspended(dev))
alvium_set_power(alvium, false);
pm_runtime_set_suspended(dev);
diff --git a/drivers/media/i2c/ov2735.c b/drivers/media/i2c/ov2735.c
index b96600204141..f248fdb352af 100644
--- a/drivers/media/i2c/ov2735.c
+++ b/drivers/media/i2c/ov2735.c
@@ -1081,6 +1081,7 @@ static void ov2735_remove(struct i2c_client *client)
v4l2_subdev_cleanup(&ov2735->sd);
media_entity_cleanup(&sd->entity);
v4l2_ctrl_handler_free(ov2735->sd.ctrl_handler);
+ ov2735_power_off(ov2735->dev);
}
static DEFINE_RUNTIME_DEV_PM_OPS(ov2735_pm_ops,
diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c
index dd01e1d515ff..27d7ca17d4d6 100644
--- a/drivers/media/i2c/ov8856.c
+++ b/drivers/media/i2c/ov8856.c
@@ -2071,9 +2071,6 @@ static int ov8856_power_on(struct device *dev)
struct ov8856 *ov8856 = to_ov8856(sd);
int ret;
- if (is_acpi_node(dev_fwnode(dev)))
- return 0;
-
ret = clk_prepare_enable(ov8856->xvclk);
if (ret < 0) {
dev_err(dev, "failed to enable xvclk\n");
@@ -2109,9 +2106,6 @@ static int ov8856_power_off(struct device *dev)
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov8856 *ov8856 = to_ov8856(sd);
- if (is_acpi_node(dev_fwnode(dev)))
- return 0;
-
gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(ov8856_supply_names),
ov8856->supplies);
@@ -2282,21 +2276,18 @@ static int ov8856_get_hwcfg(struct ov8856 *ov8856)
dev_warn(dev, "external clock rate %u is unsupported",
xvclk_rate);
- if (!is_acpi_node(fwnode)) {
- ov8856->reset_gpio = devm_gpiod_get_optional(dev, "reset",
- GPIOD_OUT_LOW);
- if (IS_ERR(ov8856->reset_gpio))
- return PTR_ERR(ov8856->reset_gpio);
+ ov8856->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ov8856->reset_gpio))
+ return PTR_ERR(ov8856->reset_gpio);
- for (i = 0; i < ARRAY_SIZE(ov8856_supply_names); i++)
- ov8856->supplies[i].supply = ov8856_supply_names[i];
+ for (i = 0; i < ARRAY_SIZE(ov8856_supply_names); i++)
+ ov8856->supplies[i].supply = ov8856_supply_names[i];
- ret = devm_regulator_bulk_get(dev,
- ARRAY_SIZE(ov8856_supply_names),
- ov8856->supplies);
- if (ret)
- return ret;
- }
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov8856_supply_names),
+ ov8856->supplies);
+ if (ret)
+ return ret;
ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
if (!ep)
@@ -2316,7 +2307,8 @@ static int ov8856_get_hwcfg(struct ov8856 *ov8856)
goto check_hwcfg_error;
}
- dev_dbg(dev, "Using %u data lanes\n", ov8856->cur_mode->data_lanes);
+ dev_dbg(dev, "Using %u data lanes\n",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
if (bus_cfg.bus.mipi_csi2.num_data_lanes == 2)
ov8856->priv_lane = &lane_cfg_2;
diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c
index a0ca19359c43..aefc442b5737 100644
--- a/drivers/media/i2c/tc358743.c
+++ b/drivers/media/i2c/tc358743.c
@@ -1373,7 +1373,8 @@ static int tc358743_log_status(struct v4l2_subdev *sd)
static const char * const input_color_space[] = {
"RGB", "YCbCr 601", "opRGB", "YCbCr 709", "NA (4)",
"xvYCC 601", "NA(6)", "xvYCC 709", "NA(8)", "sYCC601",
- "NA(10)", "NA(11)", "NA(12)", "opYCC 601"};
+ "NA(10)", "NA(11)", "NA(12)", "opYCC 601", "NA(14)",
+ "NA(15)"};
v4l2_info(sd, "-----Chip status-----\n");
v4l2_info(sd, "Chip ID: 0x%02x\n",
diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c
index 74c59a94b2b0..d3bd31e38efa 100644
--- a/drivers/media/pci/cx18/cx18-driver.c
+++ b/drivers/media/pci/cx18/cx18-driver.c
@@ -806,12 +806,12 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
}
if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) {
CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
- return -EIO;
+ goto err_disable_device;
}
if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
CX18_ERR("Cannot request encoder memory region, card %d\n",
cx->instance);
- return -EIO;
+ goto err_disable_device;
}
/* Enable bus mastering and memory mapped IO for the CX23418 */
@@ -835,6 +835,10 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
cx->pci_dev->irq, pci_latency, (u64)cx->base_addr);
return 0;
+
+err_disable_device:
+ pci_disable_device(pci_dev);
+ return -EIO;
}
static void cx18_init_subdevs(struct cx18 *cx)
@@ -1121,6 +1125,7 @@ static int cx18_probe(struct pci_dev *pci_dev,
cx18_iounmap(cx);
free_mem:
release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+ pci_disable_device(pci_dev);
free_workqueues:
destroy_workqueue(cx->in_work_queue);
err:
diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c
index 08b7ce1043aa..27ce670197cc 100644
--- a/drivers/media/pci/tw68/tw68-core.c
+++ b/drivers/media/pci/tw68/tw68-core.c
@@ -228,7 +228,7 @@ static int tw68_initdev(struct pci_dev *pci_dev,
/* pci init */
dev->pci = pci_dev;
- if (pci_enable_device(pci_dev)) {
+ if (pcim_enable_device(pci_dev)) {
err = -EIO;
goto fail1;
}
diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c
index f39e0e34deb6..6dc8af345295 100644
--- a/drivers/media/pci/tw686x/tw686x-core.c
+++ b/drivers/media/pci/tw686x/tw686x-core.c
@@ -241,6 +241,7 @@ static int tw686x_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
struct tw686x_dev *dev;
+ unsigned long flags;
int err;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@@ -352,6 +353,15 @@ static int tw686x_probe(struct pci_dev *pci_dev,
pci_release_regions(pci_dev);
disable_pci:
pci_disable_device(pci_dev);
+ if (dev->v4l2_dev.dev) {
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->pci_dev = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ v4l2_device_put(&dev->v4l2_dev);
+ return err;
+ }
+
free_audio:
kfree(dev->audio_channels);
free_video:
diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c
index da00f5fc0e5d..538ef56823b4 100644
--- a/drivers/media/platform/amphion/vpu_core.c
+++ b/drivers/media/platform/amphion/vpu_core.c
@@ -302,22 +302,23 @@ static void vpu_core_put_vpu(struct vpu_core *core)
core->vpu->put_vpu(core->vpu);
}
+static void vpu_core_cancel_work(struct vpu_core *core);
+
static int vpu_core_unregister(struct device *dev, struct vpu_core *core)
{
list_del_init(&core->list);
- vpu_core_put_vpu(core);
- core->vpu = NULL;
- vfree(core->msg_buffer);
- core->msg_buffer = NULL;
-
if (core->workqueue) {
- cancel_work_sync(&core->msg_work);
- cancel_delayed_work_sync(&core->msg_delayed_work);
+ vpu_core_cancel_work(core);
destroy_workqueue(core->workqueue);
core->workqueue = NULL;
}
+ vpu_core_put_vpu(core);
+ core->vpu = NULL;
+ vfree(core->msg_buffer);
+ core->msg_buffer = NULL;
+
return 0;
}
@@ -689,7 +690,6 @@ static int vpu_core_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
ret = pm_runtime_resume_and_get(dev);
if (ret) {
- pm_runtime_put_noidle(dev);
pm_runtime_set_suspended(dev);
goto err_runtime_disable;
}
@@ -718,6 +718,8 @@ static void vpu_core_remove(struct platform_device *pdev)
struct vpu_core *core = platform_get_drvdata(pdev);
int ret;
+ WARN_ON(!list_empty(&core->instances));
+
vpu_core_remove_dbgfs_file(core);
ret = pm_runtime_resume_and_get(dev);
WARN_ON(ret < 0);
@@ -868,6 +870,7 @@ static struct platform_driver amphion_vpu_core_driver = {
.remove = vpu_core_remove,
.driver = {
.name = "amphion-vpu-core",
+ .suppress_bind_attrs = true,
.of_match_table = vpu_core_dt_match,
.pm = &vpu_core_pm_ops,
},
diff --git a/drivers/media/platform/amphion/vpu_drv.c b/drivers/media/platform/amphion/vpu_drv.c
index efbfd2652721..67d48c338fd1 100644
--- a/drivers/media/platform/amphion/vpu_drv.c
+++ b/drivers/media/platform/amphion/vpu_drv.c
@@ -230,6 +230,7 @@ static struct platform_driver amphion_vpu_driver = {
.remove = vpu_remove,
.driver = {
.name = "amphion-vpu",
+ .suppress_bind_attrs = true,
.of_match_table = vpu_dt_match,
.pm = &vpu_pm_ops,
},
diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c
index a05a744cbb75..6a7e240c5b18 100644
--- a/drivers/media/platform/atmel/atmel-isi.c
+++ b/drivers/media/platform/atmel/atmel-isi.c
@@ -1121,10 +1121,13 @@ static void isi_graph_notify_unbind(struct v4l2_async_notifier *notifier,
{
struct atmel_isi *isi = notifier_to_isi(notifier);
+ if (!video_is_registered(isi->vdev))
+ return;
+
dev_dbg(isi->dev, "Removing %s\n", video_device_node_name(isi->vdev));
- /* Checks internally if vdev have been init or not */
video_unregister_device(isi->vdev);
+ isi->vdev = NULL;
}
static int isi_graph_notify_bound(struct v4l2_async_notifier *notifier,
@@ -1323,6 +1326,8 @@ static void atmel_isi_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
v4l2_async_nf_unregister(&isi->notifier);
v4l2_async_nf_cleanup(&isi->notifier);
+ if (isi->vdev)
+ video_device_release(isi->vdev);
v4l2_device_unregister(&isi->v4l2_dev);
}
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 e1ad685a42e5..6f93fdf28ab3 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
@@ -1261,8 +1261,14 @@ static void wave5_vpu_dec_buf_queue_dst(struct vb2_buffer *vb)
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue);
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(inst->dev->dev);
+ if (ret < 0) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ return;
+ }
- pm_runtime_resume_and_get(inst->dev->dev);
vbuf->sequence = inst->queued_dst_buf_num++;
if (inst->state == VPU_INST_STATE_PIC_RUN) {
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
index 3216b4997644..6fa2ff1cbf12 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
@@ -340,6 +340,13 @@ static int wave5_vpu_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get irq resource, falling back to polling\n");
sema_init(&dev->irq_sem, 1);
dev->irq_thread = kthread_run(irq_thread, dev, "irq thread");
+ if (IS_ERR(dev->irq_thread)) {
+ ret = PTR_ERR(dev->irq_thread);
+ dev_err(&pdev->dev, "failed to create irq thread\n");
+ dev->irq_thread = NULL;
+ goto err_vdi_release;
+ }
+
hrtimer_setup(&dev->hrtimer, &wave5_vpu_timer_callback, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_PINNED);
dev->worker = kthread_run_worker(0, "vpu_irq_thread");
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
index e7ea2f6a99d5..c76d657cf863 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
@@ -729,7 +729,7 @@ int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res)
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
if (ret) {
- pm_runtime_resume_and_get(inst->dev->dev);
+ pm_runtime_put_sync(inst->dev->dev);
return ret;
}
@@ -737,16 +737,13 @@ int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res)
ret = wave5_vpu_enc_finish_seq(inst, fail_res);
if (ret < 0 && *fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) {
dev_warn(inst->dev->dev, "enc_finish_seq timed out\n");
- pm_runtime_resume_and_get(inst->dev->dev);
- mutex_unlock(&vpu_dev->hw_lock);
- return ret;
+ goto unlock_and_return;
}
if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING &&
retry++ >= MAX_FIRMWARE_CALL_RETRY) {
- pm_runtime_resume_and_get(inst->dev->dev);
- mutex_unlock(&vpu_dev->hw_lock);
- return -ETIMEDOUT;
+ ret = -ETIMEDOUT;
+ goto unlock_and_return;
}
} while (ret != 0);
@@ -762,10 +759,12 @@ int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res)
}
wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_task);
+
+unlock_and_return:
mutex_unlock(&vpu_dev->hw_lock);
pm_runtime_put_sync(inst->dev->dev);
- return 0;
+ return ret;
}
int wave5_vpu_enc_register_frame_buffer(struct vpu_instance *inst, unsigned int num,
diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.c b/drivers/media/platform/imagination/e5010-jpeg-enc.c
index c4e0097cb8b7..a36a2acc896c 100644
--- a/drivers/media/platform/imagination/e5010-jpeg-enc.c
+++ b/drivers/media/platform/imagination/e5010-jpeg-enc.c
@@ -175,12 +175,12 @@ static void calculate_qp_tables(struct e5010_context *ctx)
long long delta = v4l2_jpeg_ref_table_chroma_qt[i] * contrast + luminosity;
int val = (int)(v4l2_jpeg_ref_table_chroma_qt[i] + delta);
- clamp(val, 1, 255);
+ val = clamp(val, 1, 255);
ctx->chroma_qp[i] = quality == -50 ? 1 : val;
delta = v4l2_jpeg_ref_table_luma_qt[i] * contrast + luminosity;
val = (int)(v4l2_jpeg_ref_table_luma_qt[i] + delta);
- clamp(val, 1, 255);
+ val = clamp(val, 1, 255);
ctx->luma_qp[i] = quality == -50 ? 1 : val;
}
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index e07e57d4206b..83d6c54f015d 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -829,7 +829,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
q_data[V4L2_M2M_DST].width = 640;
q_data[V4L2_M2M_DST].height = 480;
q_data[V4L2_M2M_DST].sizeimage = (640 * 480 * 3) / 2;
- q_data[V4L2_M2M_SRC].field = V4L2_FIELD_INTERLACED_TB;
+ q_data[V4L2_M2M_DST].field = V4L2_FIELD_INTERLACED_TB;
return vb2_queue_init(dst_vq);
}
diff --git a/drivers/media/platform/marvell/Kconfig b/drivers/media/platform/marvell/Kconfig
index d31f4730f2a3..ab58175e433c 100644
--- a/drivers/media/platform/marvell/Kconfig
+++ b/drivers/media/platform/marvell/Kconfig
@@ -22,6 +22,7 @@ config VIDEO_MMP_CAMERA
depends on V4L_PLATFORM_DRIVERS
depends on I2C && VIDEO_DEV
depends on ARCH_MMP || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
depends on COMMON_CLK
select VIDEO_OV7670 if MEDIA_SUBDRV_AUTOSELECT && VIDEO_CAMERA_SENSOR
select I2C_GPIO
diff --git a/drivers/media/platform/marvell/mmp-driver.c b/drivers/media/platform/marvell/mmp-driver.c
index d3da7ebb4a2b..4c31b62751a1 100644
--- a/drivers/media/platform/marvell/mmp-driver.c
+++ b/drivers/media/platform/marvell/mmp-driver.c
@@ -25,6 +25,7 @@
#include <linux/list.h>
#include <linux/pm.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include "mcam-core.h"
@@ -269,8 +270,8 @@ static int mmpcam_probe(struct platform_device *pdev)
/*
* Add OF clock provider.
*/
- ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
- mcam->mclk);
+ ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get,
+ &mcam->mclk_hw);
if (ret) {
dev_err(&pdev->dev, "can't add DT clock provider\n");
goto out;
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
index 5a9ba04d3d48..8e52b40e2423 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
@@ -1630,7 +1630,7 @@ static void mtk_jpegenc_worker(struct work_struct *work)
v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true);
mtk_jpegenc_set_hw_param(ctx, hw_id, src_buf, dst_buf);
- ret = pm_runtime_get_sync(comp_jpeg[hw_id]->dev);
+ ret = pm_runtime_resume_and_get(comp_jpeg[hw_id]->dev);
if (ret < 0) {
dev_err(jpeg->dev, "%s : %d, pm_runtime_get_sync fail !!!\n",
__func__, __LINE__);
@@ -1641,6 +1641,7 @@ static void mtk_jpegenc_worker(struct work_struct *work)
if (ret) {
dev_err(jpeg->dev, "%s : %d, jpegenc clk_prepare_enable fail\n",
__func__, __LINE__);
+ pm_runtime_put(comp_jpeg[hw_id]->dev);
goto enc_end;
}
@@ -1704,9 +1705,15 @@ static void mtk_jpegdec_worker(struct work_struct *work)
ret = wait_event_interruptible_timeout(jpeg->hw_wq,
atomic_read(&jpeg->hw_rdy) > 0,
MTK_JPEG_HW_TIMEOUT_MSEC);
- if (ret != 0 || (i++ > MTK_JPEG_MAX_RETRY_TIME)) {
- dev_err(jpeg->dev, "%s : %d, all HW are busy\n",
- __func__, __LINE__);
+ if (ret < 0) {
+ dev_err(jpeg->dev, "decoder HW wait interrupted: %d\n",
+ ret);
+ v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
+ return;
+ }
+
+ if (!ret && i++ > MTK_JPEG_MAX_RETRY_TIME) {
+ dev_err(jpeg->dev, "all decoder HW are busy\n");
v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
return;
}
@@ -1740,7 +1747,7 @@ static void mtk_jpegdec_worker(struct work_struct *work)
jpeg_dst_buf->frame_num = ctx->total_frame_num;
mtk_jpegdec_set_hw_param(ctx, hw_id, src_buf, dst_buf);
- ret = pm_runtime_get_sync(comp_jpeg[hw_id]->dev);
+ ret = pm_runtime_resume_and_get(comp_jpeg[hw_id]->dev);
if (ret < 0) {
dev_err(jpeg->dev, "%s : %d, pm_runtime_get_sync fail !!!\n",
__func__, __LINE__);
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c
index 7fcb2fbdd64e..acb2758a7b42 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c
@@ -1730,6 +1730,7 @@ static int mdp_comp_init(struct mdp_dev *mdp, struct device_node *node,
struct device *dev = &mdp->pdev->dev;
struct platform_device *pdev_c;
int clk_ofst;
+ int ret;
int i;
s32 event;
@@ -1756,8 +1757,10 @@ static int mdp_comp_init(struct mdp_dev *mdp, struct device_node *node,
comp->clk_num = mdp->mdp_data->comp_data[id].info.clk_num;
comp->clks = devm_kzalloc(dev, sizeof(struct clk *) * comp->clk_num,
GFP_KERNEL);
- if (!comp->clks)
- return -ENOMEM;
+ if (!comp->clks) {
+ ret = -ENOMEM;
+ goto err_put_device;
+ }
clk_ofst = mdp->mdp_data->comp_data[id].info.clk_ofst;
@@ -1782,7 +1785,8 @@ static int mdp_comp_init(struct mdp_dev *mdp, struct device_node *node,
if (of_property_read_u32_index(node, "mediatek,gce-events",
MDP_GCE_EVENT_EOF, &event)) {
dev_err(dev, "Component id %d has no EOF\n", id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_put_device;
}
} else {
event = MDP_GCE_NO_EVENT;
@@ -1791,6 +1795,10 @@ static int mdp_comp_init(struct mdp_dev *mdp, struct device_node *node,
comp->gce_event[MDP_GCE_EVENT_EOF] = event;
return 0;
+
+err_put_device:
+ put_device(&pdev_c->dev);
+ return ret;
}
static void mdp_comp_deinit(struct mdp_comp *comp)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
index 3b81fae9f913..7fec6b21684f 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
@@ -570,6 +570,8 @@ static void mtk_vcodec_dec_remove(struct platform_device *pdev)
struct mtk_vcodec_dec_dev *dev = platform_get_drvdata(pdev);
destroy_workqueue(dev->decode_workqueue);
+ if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch))
+ destroy_workqueue(dev->core_workqueue);
if (media_devnode_is_registered(dev->mdev_dec.devnode)) {
media_device_unregister(&dev->mdev_dec);
diff --git a/drivers/media/platform/mediatek/vpu/mtk_vpu.c b/drivers/media/platform/mediatek/vpu/mtk_vpu.c
index 8d8319f0cd22..9dff1d971d37 100644
--- a/drivers/media/platform/mediatek/vpu/mtk_vpu.c
+++ b/drivers/media/platform/mediatek/vpu/mtk_vpu.c
@@ -962,10 +962,12 @@ static void mtk_vpu_remove(struct platform_device *pdev)
#ifdef CONFIG_DEBUG_FS
debugfs_remove(vpu_debugfs);
#endif
+ devm_free_irq(&pdev->dev, vpu->reg.irq, vpu);
if (vpu->wdt.wq)
destroy_workqueue(vpu->wdt.wq);
vpu_free_ext_mem(vpu, P_FW);
vpu_free_ext_mem(vpu, D_FW);
+ of_reserved_mem_device_release(&pdev->dev);
mutex_destroy(&vpu->vpu_mutex);
clk_unprepare(vpu->clk);
}
diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c
index 32bdeae04ad0..21d108eb93c7 100644
--- a/drivers/media/platform/microchip/microchip-isc-base.c
+++ b/drivers/media/platform/microchip/microchip-isc-base.c
@@ -289,6 +289,7 @@ static int isc_configure(struct isc_device *isc)
struct regmap *regmap = isc->regmap;
u32 pfe_cfg0, dcfg, mask, pipeline;
struct isc_subdev_entity *subdev = isc->current_subdev;
+ int ret;
pfe_cfg0 = isc->config.sd_format->pfe_cfg0_bps;
pipeline = isc->config.bits_pipeline;
@@ -321,7 +322,15 @@ static int isc_configure(struct isc_device *isc)
isc_set_histogram(isc, false);
/* Update profile */
- return isc_update_profile(isc);
+ ret = isc_update_profile(isc);
+ if (ret) {
+ /* flush the histogram work before the clocks are gated */
+ isc_set_histogram(isc, false);
+ synchronize_irq(isc->irq);
+ cancel_work_sync(&isc->awb_work);
+ }
+
+ return ret;
}
static int isc_prepare_streaming(struct vb2_queue *vq)
@@ -425,6 +434,13 @@ static void isc_stop_streaming(struct vb2_queue *vq)
/* Disable DMA interrupt */
regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE);
+ isc_set_histogram(isc, false);
+
+ /* let a running IRQ handler finish before the clock is disabled */
+ synchronize_irq(isc->irq);
+
+ cancel_work_sync(&isc->awb_work);
+
pm_runtime_put_sync(isc->dev);
/* Disable stream on the sub device */
@@ -1416,7 +1432,7 @@ static void isc_awb_work(struct work_struct *w)
/* streaming is not active anymore */
if (isc->stop) {
mutex_unlock(&isc->awb_mutex);
- return;
+ goto out_pm_put;
}
isc_update_profile(isc);
@@ -1427,6 +1443,7 @@ static void isc_awb_work(struct work_struct *w)
if (ctrls->awb)
regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
+out_pm_put:
pm_runtime_put_sync(isc->dev);
}
@@ -1495,20 +1512,24 @@ static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl)
if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new)
ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val;
- isc_update_awb_ctrls(isc);
-
mutex_lock(&isc->awb_mutex);
- if (vb2_is_streaming(&isc->vb2_vidq)) {
+ if (vb2_is_streaming(&isc->vb2_vidq) && !isc->stop) {
+ unsigned long flags;
+
/*
- * If we are streaming, we can update profile to
- * have the new settings in place.
+ * awb_lock keeps the DMA done IRQ from latching a
+ * partially written WB pipeline.
*/
+ spin_lock_irqsave(&isc->awb_lock, flags);
+ isc_update_awb_ctrls(isc);
+ spin_unlock_irqrestore(&isc->awb_lock, flags);
+
isc_update_profile(isc);
} else {
/*
- * The auto cluster will activate automatically this
- * control. This has to be deactivated when not
- * streaming.
+ * Not streaming: keep the cached values for the next
+ * stream start and deactivate the cluster-activated
+ * do_white_balance button.
*/
v4l2_ctrl_activate(isc->do_wb_ctrl, false);
}
@@ -1703,7 +1724,6 @@ static void isc_async_unbind(struct v4l2_async_notifier *notifier,
{
struct isc_device *isc = container_of(notifier->v4l2_dev,
struct isc_device, v4l2_dev);
- mutex_destroy(&isc->awb_mutex);
cancel_work_sync(&isc->awb_work);
video_unregister_device(&isc->video_dev);
v4l2_ctrl_handler_free(&isc->ctrls.handler);
@@ -1767,8 +1787,6 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
isc->current_subdev = container_of(notifier,
struct isc_subdev_entity, notifier);
- mutex_init(&isc->lock);
- mutex_init(&isc->awb_mutex);
init_completion(&isc->comp);
@@ -1787,7 +1805,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
ret = vb2_queue_init(q);
if (ret < 0) {
dev_err(isc->dev, "vb2_queue_init() failed: %d\n", ret);
- goto isc_async_complete_err;
+ return ret;
}
/* Init video dma queues */
@@ -1798,13 +1816,13 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
ret = isc_set_default_fmt(isc);
if (ret) {
dev_err(isc->dev, "Could not set default format\n");
- goto isc_async_complete_err;
+ return ret;
}
ret = isc_ctrl_init(isc);
if (ret) {
dev_err(isc->dev, "Init isc ctrols failed: %d\n", ret);
- goto isc_async_complete_err;
+ return ret;
}
/* Register video device */
@@ -1824,7 +1842,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
dev_err(isc->dev, "video_register_device failed: %d\n", ret);
- goto isc_async_complete_err;
+ return ret;
}
ret = isc_scaler_link(isc);
@@ -1839,10 +1857,6 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
isc_async_complete_unregister_device:
video_unregister_device(vdev);
-
-isc_async_complete_err:
- mutex_destroy(&isc->awb_mutex);
- mutex_destroy(&isc->lock);
return ret;
}
diff --git a/drivers/media/platform/microchip/microchip-isc-clk.c b/drivers/media/platform/microchip/microchip-isc-clk.c
index 24358d804e75..66dc522a6190 100644
--- a/drivers/media/platform/microchip/microchip-isc-clk.c
+++ b/drivers/media/platform/microchip/microchip-isc-clk.c
@@ -98,15 +98,14 @@ static int isc_clk_is_enabled(struct clk_hw *hw)
{
struct isc_clk *isc_clk = to_isc_clk(hw);
u32 status;
- int ret;
- ret = pm_runtime_resume_and_get(isc_clk->dev);
- if (ret < 0)
+ /* Runs in atomic context, so must not sleep to resume the ISC. */
+ if (pm_runtime_get_if_active(isc_clk->dev) <= 0)
return 0;
regmap_read(isc_clk->regmap, ISC_CLKSR, &status);
- pm_runtime_put_sync(isc_clk->dev);
+ pm_runtime_put(isc_clk->dev);
return status & ISC_CLK(isc_clk->id) ? 1 : 0;
}
diff --git a/drivers/media/platform/microchip/microchip-isc.h b/drivers/media/platform/microchip/microchip-isc.h
index ad4e98a1dd8f..f5e322c2e36b 100644
--- a/drivers/media/platform/microchip/microchip-isc.h
+++ b/drivers/media/platform/microchip/microchip-isc.h
@@ -287,6 +287,7 @@ struct isc_device {
u32 dcfg;
struct device *dev;
+ int irq;
struct v4l2_device v4l2_dev;
struct video_device video_dev;
diff --git a/drivers/media/platform/microchip/microchip-sama5d2-isc.c b/drivers/media/platform/microchip/microchip-sama5d2-isc.c
index 88e6d8e1c6c9..5bd109e93a8f 100644
--- a/drivers/media/platform/microchip/microchip-sama5d2-isc.c
+++ b/drivers/media/platform/microchip/microchip-sama5d2-isc.c
@@ -30,6 +30,7 @@
#include <linux/interrupt.h>
#include <linux/math64.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
@@ -424,6 +425,14 @@ static int microchip_isc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, isc);
isc->dev = dev;
+ ret = devm_mutex_init(dev, &isc->lock);
+ if (ret)
+ return ret;
+
+ ret = devm_mutex_init(dev, &isc->awb_mutex);
+ if (ret)
+ return ret;
+
io_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(io_base))
return PTR_ERR(io_base);
@@ -439,6 +448,8 @@ static int microchip_isc_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ isc->irq = irq;
+
ret = devm_request_irq(dev, irq, microchip_isc_interrupt, 0,
"microchip-sama5d2-isc", isc);
if (ret < 0) {
diff --git a/drivers/media/platform/microchip/microchip-sama7g5-isc.c b/drivers/media/platform/microchip/microchip-sama7g5-isc.c
index 77c6c183451b..d978ce448a4d 100644
--- a/drivers/media/platform/microchip/microchip-sama7g5-isc.c
+++ b/drivers/media/platform/microchip/microchip-sama7g5-isc.c
@@ -33,6 +33,7 @@
#include <linux/interrupt.h>
#include <linux/math64.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
@@ -413,6 +414,14 @@ static int microchip_xisc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, isc);
isc->dev = dev;
+ ret = devm_mutex_init(dev, &isc->lock);
+ if (ret)
+ return ret;
+
+ ret = devm_mutex_init(dev, &isc->awb_mutex);
+ if (ret)
+ return ret;
+
io_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(io_base))
return PTR_ERR(io_base);
@@ -428,6 +437,8 @@ static int microchip_xisc_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ isc->irq = irq;
+
ret = devm_request_irq(dev, irq, microchip_isc_interrupt, 0,
"microchip-sama7g5-xisc", isc);
if (ret < 0) {
diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c
index c4c5c318f786..d10bda3757ef 100644
--- a/drivers/media/platform/nuvoton/npcm-video.c
+++ b/drivers/media/platform/nuvoton/npcm-video.c
@@ -120,6 +120,7 @@ struct npcm_video {
struct list_head buffers;
struct mutex buffer_lock; /* buffer list lock */
+ int irq;
unsigned long flags;
unsigned int sequence;
@@ -1486,6 +1487,7 @@ static int npcm_video_start_streaming(struct vb2_queue *q, unsigned int count)
}
set_bit(VIDEO_STREAMING, &video->flags);
+ enable_irq(video->irq);
return 0;
}
@@ -1494,6 +1496,7 @@ static void npcm_video_stop_streaming(struct vb2_queue *q)
struct npcm_video *video = vb2_get_drv_priv(q);
struct regmap *vcd = video->vcd_regmap;
+ disable_irq(video->irq);
clear_bit(VIDEO_STREAMING, &video->flags);
regmap_write(vcd, VCD_INTE, 0);
regmap_write(vcd, VCD_STAT, VCD_STAT_CLEAR);
@@ -1707,9 +1710,10 @@ static int npcm_video_init(struct npcm_video *video)
dev_err(dev, "Failed to find VCD IRQ\n");
return -ENODEV;
}
+ video->irq = irq;
rc = devm_request_threaded_irq(dev, irq, NULL, npcm_video_irq,
- IRQF_ONESHOT, DEVICE_NAME, video);
+ IRQF_ONESHOT | IRQF_NO_AUTOEN, DEVICE_NAME, video);
if (rc < 0) {
dev_err(dev, "Failed to request IRQ %d\n", irq);
return rc;
@@ -1807,8 +1811,7 @@ static void npcm_video_remove(struct platform_device *pdev)
struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
struct npcm_video *video = to_npcm_video(v4l2_dev);
- video_unregister_device(&video->vdev);
- vb2_queue_release(&video->queue);
+ vb2_video_unregister_device(&video->vdev);
v4l2_ctrl_handler_free(&video->ctrl_handler);
v4l2_device_unregister(v4l2_dev);
if (video->ece.enable)
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
index 4ac4f576fdd1..9f0794dd5c54 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
@@ -1525,15 +1525,15 @@ static void mxc_jpeg_device_run(void *priv)
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
if (!src_buf || !dst_buf) {
dev_err(dev, "Null src or dst buf\n");
- goto end;
+ goto job_finish;
}
q_data_cap = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
if (!q_data_cap)
- goto end;
+ goto buf_finish;
q_data_out = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
if (!q_data_out)
- goto end;
+ goto buf_finish;
src_buf->sequence = q_data_out->sequence++;
dst_buf->sequence = q_data_cap->sequence++;
@@ -1571,11 +1571,11 @@ static void mxc_jpeg_device_run(void *priv)
ctx->slot = mxc_get_free_slot(&jpeg->slot_data);
if (ctx->slot < 0) {
dev_err(dev, "No more free slots\n");
- goto end;
+ goto buf_finish;
}
if (!mxc_jpeg_alloc_slot_data(jpeg)) {
dev_err(dev, "Cannot allocate slot data\n");
- goto end;
+ goto buf_finish;
}
mxc_jpeg_enable_slot(reg, ctx->slot);
@@ -1597,8 +1597,17 @@ static void mxc_jpeg_device_run(void *priv)
mxc_jpeg_dec_mode_go(dev, reg);
}
schedule_delayed_work(&ctx->task_timer, msecs_to_jiffies(hw_timeout));
-end:
+
spin_unlock_irqrestore(&ctx->mxc_jpeg->hw_lock, flags);
+ return;
+buf_finish:
+ v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
+job_finish:
+ spin_unlock_irqrestore(&ctx->mxc_jpeg->hw_lock, flags);
+ v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
}
static int mxc_jpeg_decoder_cmd(struct file *file, void *priv,
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-17x.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
index e5ee7e717b3b..62c2405359a0 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-17x.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
@@ -364,9 +364,8 @@ static irqreturn_t vfe_isr(int irq, void *dev)
vfe->isr_ops.comp_done(vfe, i);
for (wm = 0; wm < MSM_VFE_IMAGE_MASTERS_NUM; wm++)
- if (status0 & BIT(9))
- if (vfe_bus_status[1] & STATUS1_WM_CLIENT_BUF_DONE(wm))
- vfe->isr_ops.wm_done(vfe, wm);
+ if (vfe_bus_status[1] & STATUS1_WM_CLIENT_BUF_DONE(wm))
+ vfe->isr_ops.wm_done(vfe, wm);
return IRQ_HANDLED;
}
@@ -383,45 +382,6 @@ static int vfe_halt(struct vfe_device *vfe)
return 0;
}
-static int vfe_get_output(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output;
- unsigned long flags;
- int wm_idx;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- output = &line->output;
- if (output->state > VFE_OUTPUT_RESERVED) {
- dev_err(vfe->camss->dev, "Output is running\n");
- goto error;
- }
-
- output->wm_num = 1;
-
- wm_idx = vfe_reserve_wm(vfe, line->id);
- if (wm_idx < 0) {
- dev_err(vfe->camss->dev, "Can not reserve wm\n");
- goto error_get_wm;
- }
- output->wm_idx[0] = wm_idx;
-
- output->drop_update_idx = 0;
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-
-error_get_wm:
- vfe_release_wm(vfe, output->wm_idx[0]);
- output->state = VFE_OUTPUT_OFF;
-error:
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return -EINVAL;
-}
-
/*
* vfe_enable - Enable streaming on VFE line
* @line: VFE line
@@ -442,7 +402,7 @@ static int vfe_enable(struct vfe_line *line)
mutex_unlock(&vfe->stream_lock);
- ret = vfe_get_output(line);
+ ret = vfe_get_output_v2(line);
if (ret < 0)
goto error_get_output;
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index 20e0d6604fa9..bf76ac3fd4c5 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -804,7 +804,7 @@ int vfe_put_output(struct vfe_line *line)
return 0;
}
-static int vfe_disable_output(struct vfe_line *line)
+static void vfe_disable_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output = &line->output;
@@ -816,8 +816,6 @@ static int vfe_disable_output(struct vfe_line *line)
vfe->res->hw_ops->vfe_wm_stop(vfe, output->wm_idx[i]);
output->gen2.active_num = 0;
spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return vfe_reset(vfe);
}
/*
@@ -829,21 +827,19 @@ static int vfe_disable_output(struct vfe_line *line)
int vfe_disable(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
- int ret;
+ int ret = 0;
- ret = vfe_disable_output(line);
- if (ret)
- goto error;
+ vfe_disable_output(line);
vfe_put_output(line);
mutex_lock(&vfe->stream_lock);
- vfe->stream_count--;
+ if (--vfe->stream_count == 0)
+ ret = vfe_reset(vfe);
mutex_unlock(&vfe->stream_lock);
-error:
return ret;
}
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index c1148f6ad6b2..0fb43cb6348d 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -2926,10 +2926,11 @@ static const struct camss_subdev_resources vfe_res_8775p[] = {
/* VFE2 (lite) */
{
.regulators = {},
- .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb",
- "vfe_lite_csid", "vfe_lite_cphy_rx",
+ .clock = { "cpas_ahb", "gcc_axi_hf", "cpas_vfe_lite",
+ "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx",
"vfe_lite", "camnoc_axi"},
.clock_rate = {
+ { 0 },
{ 0 },
{ 0 },
{ 300000000, 400000000, 400000000, 400000000 },
@@ -2951,10 +2952,11 @@ static const struct camss_subdev_resources vfe_res_8775p[] = {
/* VFE3 (lite) */
{
.regulators = {},
- .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb",
- "vfe_lite_csid", "vfe_lite_cphy_rx",
+ .clock = { "cpas_ahb", "gcc_axi_hf", "cpas_vfe_lite",
+ "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx",
"vfe_lite", "camnoc_axi"},
.clock_rate = {
+ { 0 },
{ 0 },
{ 0 },
{ 300000000, 400000000, 400000000, 400000000 },
@@ -2976,10 +2978,11 @@ static const struct camss_subdev_resources vfe_res_8775p[] = {
/* VFE4 (lite) */
{
.regulators = {},
- .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb",
- "vfe_lite_csid", "vfe_lite_cphy_rx",
+ .clock = { "cpas_ahb", "gcc_axi_hf", "cpas_vfe_lite",
+ "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx",
"vfe_lite", "camnoc_axi"},
.clock_rate = {
+ { 0 },
{ 0 },
{ 0 },
{ 300000000, 400000000, 400000000, 400000000 },
@@ -3001,10 +3004,11 @@ static const struct camss_subdev_resources vfe_res_8775p[] = {
/* VFE5 (lite) */
{
.regulators = {},
- .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb",
- "vfe_lite_csid", "vfe_lite_cphy_rx",
+ .clock = { "cpas_ahb", "gcc_axi_hf", "cpas_vfe_lite",
+ "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx",
"vfe_lite", "camnoc_axi"},
.clock_rate = {
+ { 0 },
{ 0 },
{ 0 },
{ 300000000, 400000000, 400000000, 400000000 },
@@ -3026,10 +3030,11 @@ static const struct camss_subdev_resources vfe_res_8775p[] = {
/* VFE6 (lite) */
{
.regulators = {},
- .clock = { "cpas_ahb", "cpas_vfe_lite", "vfe_lite_ahb",
- "vfe_lite_csid", "vfe_lite_cphy_rx",
+ .clock = { "cpas_ahb", "gcc_axi_hf", "cpas_vfe_lite",
+ "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx",
"vfe_lite", "camnoc_axi"},
.clock_rate = {
+ { 0 },
{ 0 },
{ 0 },
{ 300000000, 400000000, 400000000, 400000000 },
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c
index 83b2b0e59f03..b916e8c8a645 100644
--- a/drivers/media/platform/qcom/iris/iris_vidc.c
+++ b/drivers/media/platform/qcom/iris/iris_vidc.c
@@ -434,14 +434,14 @@ static int iris_enum_frameintervals(struct file *filp, void *fh,
mbpf = NUM_MBS_PER_FRAME(fival->height, fival->width);
fps = DIV_ROUND_UP(core->iris_platform_data->max_core_mbps, mbpf);
- fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
+ fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
fival->stepwise.min.numerator = 1;
fival->stepwise.min.denominator =
min_t(u32, fps, MAXIMUM_FPS);
fival->stepwise.max.numerator = 1;
fival->stepwise.max.denominator = 1;
fival->stepwise.step.numerator = 1;
- fival->stepwise.step.denominator = MAXIMUM_FPS;
+ fival->stepwise.step.denominator = 1;
return 0;
}
@@ -462,7 +462,7 @@ static int iris_querycap(struct file *filp, void *fh, struct v4l2_capability *ca
info = "enc";
}
snprintf(cap->bus_info, sizeof(cap->bus_info),
- "plat:%s:%s", dev_name(core->dev), info);
+ "platform:%s:%s", dev_name(core->dev), info);
return 0;
}
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index 66a750ed1609..236934a0022a 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -374,6 +374,57 @@ static int venus_add_dynamic_nodes(struct venus_core *core)
static void venus_remove_dynamic_nodes(struct venus_core *core) {}
#endif
+static void venus_unreserve_iova_region(struct device *dev, struct dma_iova_state *state)
+{
+ unsigned int i;
+
+ for (i = 0; dma_iova_size(&state[i]); i++)
+ dma_iova_free(dev, &state[i]);
+}
+
+static int venus_reserve_iova_region(struct device *dev, struct dma_iova_state **iova_state,
+ unsigned long start, unsigned long size)
+{
+ unsigned long dma_limit = dev->bus_dma_limit;
+ unsigned long end, rem, chunk;
+ struct dma_iova_state *state;
+ unsigned int count = 0;
+ int ret = -ENOMEM;
+
+ state = devm_kcalloc(dev, BITS_PER_TYPE(dma_addr_t) + 1, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return ret;
+
+ end = start + size;
+ rem = end - max(start, PAGE_SIZE);
+ dev->bus_dma_limit = end - 1;
+
+ while (rem) {
+ chunk = min(end & -end, (u64)1 << (fls64(rem) - 1));
+
+ if (!dma_iova_try_alloc(dev, &state[count], 0, chunk))
+ goto err_free_iova;
+
+ if (state[count].addr != end - chunk || state[count].__size != chunk)
+ goto err_free_iova;
+
+ rem -= chunk;
+ end -= chunk;
+ count++;
+ }
+
+ *iova_state = state;
+ dev->bus_dma_limit = dma_limit;
+
+ return 0;
+
+err_free_iova:
+ venus_unreserve_iova_region(dev, state);
+ dev->bus_dma_limit = dma_limit;
+
+ return ret;
+}
+
static int venus_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -418,10 +469,15 @@ static int venus_probe(struct platform_device *pdev)
return ret;
}
- ret = dma_set_mask_and_coherent(dev, core->res->dma_mask);
+ ret = venus_reserve_iova_region(dev, &core->iova_state, VENUS_NP_RESERVE_IOVA_START,
+ VENUS_NP_RESERVE_IOVA_SIZE);
if (ret)
goto err_core_put;
+ ret = dma_set_mask_and_coherent(dev, core->res->dma_mask);
+ if (ret)
+ goto err_unresv_iova_region;
+
dma_set_max_seg_size(dev, UINT_MAX);
INIT_LIST_HEAD(&core->instances);
@@ -431,13 +487,13 @@ static int venus_probe(struct platform_device *pdev)
ret = hfi_create(core, &venus_core_ops);
if (ret)
- goto err_core_put;
+ goto err_unresv_iova_region;
ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, venus_isr_thread,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"venus", core);
if (ret)
- goto err_core_put;
+ goto err_unresv_iova_region;
venus_assign_register_offsets(core);
@@ -522,6 +578,8 @@ static int venus_probe(struct platform_device *pdev)
v4l2_device_unregister(&core->v4l2_dev);
err_hfi_destroy:
hfi_destroy(core);
+err_unresv_iova_region:
+ venus_unreserve_iova_region(dev, core->iova_state);
err_core_put:
if (core->pm_ops->core_put)
core->pm_ops->core_put(core);
@@ -559,6 +617,8 @@ static void venus_remove(struct platform_device *pdev)
hfi_destroy(core);
+ venus_unreserve_iova_region(dev, core->iova_state);
+
mutex_destroy(&core->pm_lock);
mutex_destroy(&core->lock);
venus_dbgfs_deinit(core);
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index 70e7b40affa9..497e8f50290f 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -8,6 +8,7 @@
#define __VENUS_CORE_H_
#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
#include <linux/list.h>
#include <media/videobuf2-v4l2.h>
#include <media/v4l2-ctrls.h>
@@ -30,6 +31,9 @@
#define VENUS_MAX_FPS 240
+#define VENUS_NP_RESERVE_IOVA_START 0x0
+#define VENUS_NP_RESERVE_IOVA_SIZE 0x25800000
+
extern int venus_fw_debug;
struct freq_tbl {
@@ -183,6 +187,7 @@ struct venus_format {
* @dump_core: a flag indicating that a core dump is required
* @ocs: OF changeset pointer
* @hwmode_dev: a flag indicating that HW_CTRL_TRIGGER is used in clock driver
+ * @iova_state: a pointer to an array of dma_iova_state entries reserved for restricted IOVA region
*/
struct venus_core {
void __iomem *base;
@@ -243,6 +248,7 @@ struct venus_core {
unsigned long dump_core;
struct of_changeset *ocs;
bool hwmode_dev;
+ struct dma_iova_state *iova_state;
};
struct vdec_controls {
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
index 0ce293b0718b..0862fd14d41e 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
@@ -1209,9 +1209,8 @@ static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on)
spin_unlock_irqrestore(&fimc->slock, flags);
}
} else {
- set_bit(ST_FLITE_OFF, &fimc->state);
-
spin_lock_irqsave(&fimc->slock, flags);
+ set_bit(ST_FLITE_OFF, &fimc->state);
flite_hw_capture_stop(fimc);
spin_unlock_irqrestore(&fimc->slock, flags);
diff --git a/drivers/media/platform/st/sti/delta/delta-mjpeg-hdr.c b/drivers/media/platform/st/sti/delta/delta-mjpeg-hdr.c
index 90e5b2f72c82..849a5e118aec 100644
--- a/drivers/media/platform/st/sti/delta/delta-mjpeg-hdr.c
+++ b/drivers/media/platform/st/sti/delta/delta-mjpeg-hdr.c
@@ -4,6 +4,7 @@
* Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
*/
+#include <linux/unaligned.h>
#include "delta.h"
#include "delta-mjpeg.h"
@@ -48,13 +49,13 @@ static int delta_mjpeg_read_sof(struct delta_ctx *pctx,
goto err_no_more;
memset(header, 0, sizeof(*header));
- header->length = be16_to_cpu(*(__be16 *)(data + offset));
+ header->length = get_unaligned_be16(data + offset);
offset += sizeof(u16);
header->sample_precision = *(u8 *)(data + offset);
offset += sizeof(u8);
- header->frame_height = be16_to_cpu(*(__be16 *)(data + offset));
+ header->frame_height = get_unaligned_be16(data + offset);
offset += sizeof(u16);
- header->frame_width = be16_to_cpu(*(__be16 *)(data + offset));
+ header->frame_width = get_unaligned_be16(data + offset);
offset += sizeof(u16);
header->nb_of_components = *(u8 *)(data + offset);
offset += sizeof(u8);
diff --git a/drivers/media/platform/st/stm32/stm32-csi.c b/drivers/media/platform/st/stm32/stm32-csi.c
index fd2b6dfbd44c..78c68cf90ffc 100644
--- a/drivers/media/platform/st/stm32/stm32-csi.c
+++ b/drivers/media/platform/st/stm32/stm32-csi.c
@@ -825,7 +825,7 @@ static int stm32_csi_async_bound(struct v4l2_async_notifier *notifier,
int remote_pad;
remote_pad = media_entity_get_fwnode_pad(&s_subdev->entity,
- s_subdev->fwnode,
+ asd->match.fwnode,
MEDIA_PAD_FL_SOURCE);
if (remote_pad < 0) {
dev_err(csidev->dev, "Couldn't find output pad for subdev %s\n",
@@ -1059,6 +1059,7 @@ static int stm32_csi_probe(struct platform_device *pdev)
return 0;
err_cleanup:
+ v4l2_async_nf_unregister(&csidev->notifier);
v4l2_async_nf_cleanup(&csidev->notifier);
return ret;
}
@@ -1067,6 +1068,8 @@ static void stm32_csi_remove(struct platform_device *pdev)
{
struct stm32_csi_dev *csidev = platform_get_drvdata(pdev);
+ v4l2_async_nf_unregister(&csidev->notifier);
+ v4l2_async_nf_cleanup(&csidev->notifier);
v4l2_async_unregister_subdev(&csidev->sd);
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
index 8e6f452f76c7..22a5e137f5b6 100644
--- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
@@ -154,7 +154,6 @@ struct snps_hdmirx_dev {
int num_clks;
u32 edid_blocks_written;
u32 cur_fmt_fourcc;
- u32 color_depth;
spinlock_t rst_lock; /* to lock register access */
u8 edid[EDID_NUM_BLOCKS_MAX * EDID_BLOCK_SIZE];
};
@@ -378,6 +377,38 @@ static void hdmirx_toggle_polarity(struct snps_hdmirx_dev *hdmirx_dev)
VPROC_HSYNC_POL_OVR_EN, 0);
}
+static u32 hdmirx_get_colordepth(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 val, color_depth_reg, color_depth;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
+ color_depth_reg = (val & HDMIRX_COLOR_DEPTH_MASK) >> 3;
+
+ switch (color_depth_reg) {
+ case 0x4:
+ color_depth = 24;
+ break;
+ case 0x5:
+ color_depth = 30;
+ break;
+ case 0x6:
+ color_depth = 36;
+ break;
+ case 0x7:
+ color_depth = 48;
+ break;
+ default:
+ color_depth = 24;
+ break;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: color_depth: %d, reg_val:%d\n",
+ __func__, color_depth, color_depth_reg);
+
+ return color_depth;
+}
+
/*
* When querying DV timings during preview, if the DMA's timing is stable,
* we retrieve the timings directly from the DMA. However, if the current
@@ -391,7 +422,7 @@ static int hdmirx_get_detected_timings(struct snps_hdmirx_dev *hdmirx_dev,
struct v4l2_bt_timings *bt = &timings->bt;
u32 val, tmdsqpclk_freq, pix_clk;
unsigned int num_retries = 0;
- u32 field_type, deframer_st;
+ u32 field_type, deframer_st, color_depth;
u64 tmp_data, tmds_clk;
bool is_dvi_mode;
int ret;
@@ -412,10 +443,11 @@ static int hdmirx_get_detected_timings(struct snps_hdmirx_dev *hdmirx_dev,
deframer_st = hdmirx_readl(hdmirx_dev, DEFRAMER_STATUS);
is_dvi_mode = !(deframer_st & OPMODE_STS_MASK);
+ color_depth = hdmirx_get_colordepth(hdmirx_dev);
tmdsqpclk_freq = hdmirx_readl(hdmirx_dev, CMU_TMDSQPCLK_FREQ);
tmds_clk = tmdsqpclk_freq * 4 * 1000;
tmp_data = tmds_clk * 24;
- do_div(tmp_data, hdmirx_dev->color_depth);
+ do_div(tmp_data, color_depth);
pix_clk = tmp_data;
bt->pixelclock = pix_clk;
@@ -427,7 +459,7 @@ static int hdmirx_get_detected_timings(struct snps_hdmirx_dev *hdmirx_dev,
v4l2_dbg(2, debug, v4l2_dev, "tmds_clk:%llu, pix_clk:%d\n", tmds_clk, pix_clk);
v4l2_dbg(1, debug, v4l2_dev, "interlace:%d, fmt:%d, color:%d, mode:%s\n",
bt->interlaced, hdmirx_dev->pix_fmt,
- hdmirx_dev->color_depth,
+ color_depth,
is_dvi_mode ? "dvi" : "hdmi");
v4l2_dbg(2, debug, v4l2_dev, "deframer_st:%#x\n", deframer_st);
@@ -947,36 +979,6 @@ static void hdmirx_controller_init(struct snps_hdmirx_dev *hdmirx_dev)
VS_REMAPFILTER_EN_QST | VS_FILTER_ORDER_QST(0x3));
}
-static void hdmirx_get_colordepth(struct snps_hdmirx_dev *hdmirx_dev)
-{
- struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
- u32 val, color_depth_reg;
-
- val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
- color_depth_reg = (val & HDMIRX_COLOR_DEPTH_MASK) >> 3;
-
- switch (color_depth_reg) {
- case 0x4:
- hdmirx_dev->color_depth = 24;
- break;
- case 0x5:
- hdmirx_dev->color_depth = 30;
- break;
- case 0x6:
- hdmirx_dev->color_depth = 36;
- break;
- case 0x7:
- hdmirx_dev->color_depth = 48;
- break;
- default:
- hdmirx_dev->color_depth = 24;
- break;
- }
-
- v4l2_dbg(1, debug, v4l2_dev, "%s: color_depth: %d, reg_val:%d\n",
- __func__, hdmirx_dev->color_depth, color_depth_reg);
-}
-
static void hdmirx_get_pix_fmt(struct snps_hdmirx_dev *hdmirx_dev)
{
struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
@@ -1087,7 +1089,6 @@ static void hdmirx_format_change(struct snps_hdmirx_dev *hdmirx_dev)
};
hdmirx_get_pix_fmt(hdmirx_dev);
- hdmirx_get_colordepth(hdmirx_dev);
hdmirx_get_avi_infoframe(hdmirx_dev);
v4l2_dbg(1, debug, v4l2_dev, "%s: queue res_chg_event\n", __func__);
@@ -2532,10 +2533,11 @@ static int hdmirx_setup_irq(struct snps_hdmirx_dev *hdmirx_dev,
irq_set_status_flags(irq, IRQ_NOAUTOEN);
hdmirx_dev->det_irq = irq;
- ret = devm_request_irq(dev, irq, hdmirx_5v_det_irq_handler,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- "rk_hdmirx-5v", hdmirx_dev);
- if (ret) {
+ ret = devm_request_any_context_irq(dev, irq, hdmirx_5v_det_irq_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING,
+ "rk_hdmirx-5v", hdmirx_dev);
+ if (ret < 0) {
dev_err_probe(dev, ret, "failed to request hdmirx-5v irq\n");
return ret;
}
diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c
index 9fbef2402574..5158417fe603 100644
--- a/drivers/media/platform/ti/davinci/vpif_capture.c
+++ b/drivers/media/platform/ti/davinci/vpif_capture.c
@@ -1603,7 +1603,7 @@ vpif_capture_get_pdata(struct platform_device *pdev,
static int vpif_probe(struct platform_device *pdev)
{
struct vpif_subdev_info *subdevdata;
- struct i2c_adapter *i2c_adap;
+ struct i2c_adapter *i2c_adap = NULL;
int subdev_count;
int res_idx = 0;
int i, err;
@@ -1693,9 +1693,12 @@ static int vpif_probe(struct platform_device *pdev)
}
}
+ i2c_put_adapter(i2c_adap);
+
return 0;
probe_subdev_out:
+ i2c_put_adapter(i2c_adap);
v4l2_async_nf_cleanup(&vpif_obj.notifier);
/* free sub devices memory */
kfree(vpif_obj.sd);
diff --git a/drivers/media/platform/ti/davinci/vpif_display.c b/drivers/media/platform/ti/davinci/vpif_display.c
index 1e7815e9f8e0..ba16370b70f9 100644
--- a/drivers/media/platform/ti/davinci/vpif_display.c
+++ b/drivers/media/platform/ti/davinci/vpif_display.c
@@ -1289,9 +1289,12 @@ static int vpif_probe(struct platform_device *pdev)
if (err)
goto probe_subdev_out;
+ i2c_put_adapter(i2c_adap);
+
return 0;
probe_subdev_out:
+ i2c_put_adapter(i2c_adap);
kfree(vpif_obj.sd);
vpif_unregister:
v4l2_device_unregister(&vpif_obj.v4l2_dev);
diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c
index 418cfe3a1414..a35f6392c6fa 100644
--- a/drivers/media/platform/verisilicon/hantro_drv.c
+++ b/drivers/media/platform/verisilicon/hantro_drv.c
@@ -1212,7 +1212,7 @@ static int hantro_probe(struct platform_device *pdev)
ret = clk_bulk_prepare(vpu->variant->num_clocks, vpu->clocks);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare clocks\n");
- goto err_rst_assert;
+ goto err_pm_disable_assert_reset;
}
ret = v4l2_device_register(&pdev->dev, &vpu->v4l2_dev);
@@ -1266,8 +1266,11 @@ static int hantro_probe(struct platform_device *pdev)
v4l2_device_unregister(&vpu->v4l2_dev);
err_clk_unprepare:
clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks);
-err_rst_assert:
+err_pm_disable_assert_reset:
+ pm_runtime_dont_use_autosuspend(vpu->dev);
+ pm_runtime_disable(vpu->dev);
reset_control_assert(vpu->resets);
+ return ret;
err_pm_disable:
pm_runtime_dont_use_autosuspend(vpu->dev);
pm_runtime_disable(vpu->dev);
@@ -1287,9 +1290,9 @@ static void hantro_remove(struct platform_device *pdev)
v4l2_m2m_put(vpu->m2m_dev);
v4l2_device_unregister(&vpu->v4l2_dev);
clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks);
- reset_control_assert(vpu->resets);
pm_runtime_dont_use_autosuspend(vpu->dev);
pm_runtime_disable(vpu->dev);
+ reset_control_assert(vpu->resets);
}
#ifdef CONFIG_PM
diff --git a/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c b/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c
index e0d6bd0a6e44..6a942f4b8a2d 100644
--- a/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c
+++ b/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c
@@ -161,8 +161,13 @@ int hantro_g1_mpeg2_dec_run(struct hantro_ctx *ctx)
seq = hantro_get_ctrl(ctx,
V4L2_CID_STATELESS_MPEG2_SEQUENCE);
+ if (WARN_ON(!seq))
+ return -EINVAL;
+
pic = hantro_get_ctrl(ctx,
V4L2_CID_STATELESS_MPEG2_PICTURE);
+ if (WARN_ON(!pic))
+ return -EINVAL;
reg = G1_REG_DEC_AXI_RD_ID(0) |
G1_REG_DEC_TIMEOUT_E(1) |
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
index 00aa566a4ccd..2a145433e6d1 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
@@ -369,6 +369,7 @@ void rockchip_vpu981_av1_dec_exit(struct hantro_ctx *ctx)
int rockchip_vpu981_av1_dec_init(struct hantro_ctx *ctx)
{
+ int ret = 0;
struct hantro_dev *vpu = ctx->dev;
struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
@@ -377,39 +378,54 @@ int rockchip_vpu981_av1_dec_init(struct hantro_ctx *ctx)
av1_dec->global_model.cpu = dma_alloc_coherent(vpu->dev, GLOBAL_MODEL_SIZE,
&av1_dec->global_model.dma,
GFP_KERNEL);
- if (!av1_dec->global_model.cpu)
- return -ENOMEM;
+ if (!av1_dec->global_model.cpu) {
+ ret = -ENOMEM;
+ goto global_model_cpu_err;
+ }
+
av1_dec->global_model.size = GLOBAL_MODEL_SIZE;
av1_dec->tile_info.cpu = dma_alloc_coherent(vpu->dev, AV1_TILE_INFO_SIZE,
&av1_dec->tile_info.dma,
GFP_KERNEL);
- if (!av1_dec->tile_info.cpu)
- return -ENOMEM;
+ if (!av1_dec->tile_info.cpu) {
+ ret = -ENOMEM;
+ goto tile_info_cpu_err;
+ }
+
av1_dec->tile_info.size = AV1_TILE_INFO_SIZE;
av1_dec->film_grain.cpu = dma_alloc_coherent(vpu->dev,
ALIGN(sizeof(struct rockchip_av1_film_grain), 2048),
&av1_dec->film_grain.dma,
GFP_KERNEL);
- if (!av1_dec->film_grain.cpu)
- return -ENOMEM;
+ if (!av1_dec->film_grain.cpu) {
+ ret = -ENOMEM;
+ goto film_grain_cpu_err;
+ }
+
av1_dec->film_grain.size = ALIGN(sizeof(struct rockchip_av1_film_grain), 2048);
av1_dec->prob_tbl.cpu = dma_alloc_coherent(vpu->dev,
ALIGN(sizeof(struct av1cdfs), 2048),
&av1_dec->prob_tbl.dma,
GFP_KERNEL);
- if (!av1_dec->prob_tbl.cpu)
- return -ENOMEM;
+ if (!av1_dec->prob_tbl.cpu) {
+ ret = -ENOMEM;
+ goto prob_tbl_cpu_err;
+ }
+
av1_dec->prob_tbl.size = ALIGN(sizeof(struct av1cdfs), 2048);
av1_dec->prob_tbl_out.cpu = dma_alloc_coherent(vpu->dev,
ALIGN(sizeof(struct av1cdfs), 2048),
&av1_dec->prob_tbl_out.dma,
GFP_KERNEL);
- if (!av1_dec->prob_tbl_out.cpu)
- return -ENOMEM;
+ if (!av1_dec->prob_tbl_out.cpu) {
+ ret = -ENOMEM;
+ goto prob_tbl_out_cpu_err;
+ }
+
av1_dec->prob_tbl_out.size = ALIGN(sizeof(struct av1cdfs), 2048);
av1_dec->cdfs = &av1_dec->default_cdfs;
av1_dec->cdfs_ndvc = &av1_dec->default_cdfs_ndvc;
@@ -420,11 +436,37 @@ int rockchip_vpu981_av1_dec_init(struct hantro_ctx *ctx)
AV1_TILE_SIZE,
&av1_dec->tile_buf.dma,
GFP_KERNEL);
- if (!av1_dec->tile_buf.cpu)
- return -ENOMEM;
+ if (!av1_dec->tile_buf.cpu) {
+ ret = -ENOMEM;
+ goto tile_buf_cpu_err;
+ }
+
av1_dec->tile_buf.size = AV1_TILE_SIZE;
- return 0;
+ return ret;
+
+tile_buf_cpu_err:
+ dma_free_coherent(vpu->dev, av1_dec->prob_tbl_out.size,
+ av1_dec->prob_tbl_out.cpu,
+ av1_dec->prob_tbl_out.dma);
+prob_tbl_out_cpu_err:
+ dma_free_coherent(vpu->dev, av1_dec->prob_tbl.size,
+ av1_dec->prob_tbl.cpu,
+ av1_dec->prob_tbl.dma);
+prob_tbl_cpu_err:
+ dma_free_coherent(vpu->dev, av1_dec->film_grain.size,
+ av1_dec->film_grain.cpu,
+ av1_dec->film_grain.dma);
+film_grain_cpu_err:
+ dma_free_coherent(vpu->dev, av1_dec->tile_info.size,
+ av1_dec->tile_info.cpu,
+ av1_dec->tile_info.dma);
+tile_info_cpu_err:
+ dma_free_coherent(vpu->dev, av1_dec->global_model.size,
+ av1_dec->global_model.cpu,
+ av1_dec->global_model.dma);
+global_model_cpu_err:
+ return ret;
}
static int rockchip_vpu981_av1_dec_prepare_run(struct hantro_ctx *ctx)
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index 136fc4192265..7598b623e4e8 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1103,15 +1103,15 @@ static void ene_remove(struct pnp_dev *pnp_dev)
unsigned long flags;
rc_unregister_device(dev->rdev);
- timer_delete_sync(&dev->tx_sim_timer);
spin_lock_irqsave(&dev->hw_lock, flags);
ene_rx_disable(dev);
ene_rx_restore_hw_buffer(dev);
spin_unlock_irqrestore(&dev->hw_lock, flags);
- rc_free_device(dev->rdev);
free_irq(dev->irq, dev);
+ timer_delete_sync(&dev->tx_sim_timer);
release_region(dev->hw_io, ENE_IO_SIZE);
+ rc_free_device(dev->rdev);
kfree(dev);
}
diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
index bb0f95833df5..04982ffde68f 100644
--- a/drivers/media/rc/ir-hix5hd2.c
+++ b/drivers/media/rc/ir-hix5hd2.c
@@ -316,6 +316,9 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)
if (ret < 0)
goto clkerr;
+ priv->rdev = rdev;
+ priv->dev = dev;
+
if (devm_request_irq(dev, priv->irq, hix5hd2_ir_rx_interrupt,
0, pdev->name, priv) < 0) {
dev_err(dev, "IRQ %d register failed\n", priv->irq);
@@ -323,8 +326,6 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)
goto regerr;
}
- priv->rdev = rdev;
- priv->dev = dev;
platform_set_drvdata(pdev, priv);
return ret;
diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c
index e79de56997a4..ba6c04212707 100644
--- a/drivers/media/rc/ir_toy.c
+++ b/drivers/media/rc/ir_toy.c
@@ -373,7 +373,7 @@ static int irtoy_tx_carrier(struct rc_dev *rc, uint32_t carrier)
u8 buf[3];
int err;
- if (carrier < 11800)
+ if (carrier < 11800 || carrier > 3000000)
return -EINVAL;
buf[0] = 0x06;
diff --git a/drivers/media/rc/meson-ir-tx.c b/drivers/media/rc/meson-ir-tx.c
index fded2c256f2a..a0d07da09f67 100644
--- a/drivers/media/rc/meson-ir-tx.c
+++ b/drivers/media/rc/meson-ir-tx.c
@@ -219,6 +219,9 @@ static int meson_irtx_set_carrier(struct rc_dev *rc, u32 carrier)
if (carrier == 0)
return -EINVAL;
+ if (!DIV_ROUND_CLOSEST(USEC_PER_SEC, carrier))
+ return -EINVAL;
+
ir->carrier = carrier;
meson_irtx_set_mod(ir);
@@ -288,11 +291,12 @@ static int meson_irtx_mod_clock_probe(struct meson_irtx *ir,
if (!np)
return -ENODEV;
- clock = devm_clk_get(ir->dev, "xtal");
- if (IS_ERR(clock) || clk_prepare_enable(clock))
- return -ENODEV;
-
*clk_nr = IRB_MOD_XTAL3_CLK;
+
+ clock = devm_clk_get_enabled(ir->dev, "xtal");
+ if (IS_ERR(clock))
+ return PTR_ERR(clock);
+
ir->clk_rate = clk_get_rate(clock) / 3;
if (ir->clk_rate < IRB_MOD_1US_CLK_RATE) {
@@ -324,7 +328,7 @@ static int meson_irtx_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0)
- return -ENODEV;
+ return irq;
ir->dev = dev;
ir->carrier = MIRTX_DEFAULT_CARRIER;
@@ -345,7 +349,7 @@ static int meson_irtx_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "irq request failed\n");
- rc = rc_allocate_device(RC_DRIVER_IR_RAW_TX);
+ rc = devm_rc_allocate_device(dev, RC_DRIVER_IR_RAW_TX);
if (!rc)
return -ENOMEM;
@@ -358,10 +362,8 @@ static int meson_irtx_probe(struct platform_device *pdev)
rc->s_tx_duty_cycle = meson_irtx_set_duty_cycle;
ret = devm_rc_register_device(dev, rc);
- if (ret < 0) {
- rc_free_device(rc);
+ if (ret < 0)
return dev_err_probe(dev, ret, "rc_dev registration failed\n");
- }
return 0;
}
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index 3170bd72a8e6..c9a5c030f772 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -358,8 +358,24 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3)
/* process each rr3 encoded byte into an int */
sig_size = be16_to_cpu(rr3->irdata.sig_size);
+
+ /*
+ * Note we are not checking if we are reading beyond the end of the
+ * packet which was sent, and reading stale data. If the device
+ * sends a packet which is short then we get garbage IR, but no
+ * out of bounds read.
+ */
+ if (sig_size > RR3_MAX_SIG_SIZE) {
+ dev_err(dev, "length %u is incorrect\n", sig_size);
+ return;
+ }
+
for (i = 0; i < sig_size; i++) {
offset = rr3->irdata.sigdata[i];
+ if (offset >= RR3_DRIVER_MAXLENS) {
+ dev_err(dev, "offset %u is incorrect\n", offset);
+ return;
+ }
val = get_unaligned_be16(&rr3->irdata.lens[offset]);
/* we should always get pulse/space/pulse/space samples */
@@ -1154,6 +1170,7 @@ static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message)
usb_kill_urb(rr3->narrow_urb);
usb_kill_urb(rr3->wide_urb);
usb_kill_urb(rr3->flash_urb);
+ usb_kill_urb(rr3->learn_urb);
return 0;
}
diff --git a/drivers/media/rc/serial_ir.c b/drivers/media/rc/serial_ir.c
index 992fff82b524..f6702b3df9a0 100644
--- a/drivers/media/rc/serial_ir.c
+++ b/drivers/media/rc/serial_ir.c
@@ -798,7 +798,7 @@ static int __init serial_ir_init_module(void)
static void __exit serial_ir_exit_module(void)
{
- timer_delete_sync(&serial_ir.timeout_timer);
+ timer_shutdown_sync(&serial_ir.timeout_timer);
serial_ir_exit();
}
diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
index 8c85b9f30a3a..e91cf2ffbc87 100644
--- a/drivers/media/rc/streamzap.c
+++ b/drivers/media/rc/streamzap.c
@@ -365,6 +365,7 @@ static int streamzap_probe(struct usb_interface *intf,
return 0;
rc_submit_fail:
+ rc_unregister_device(sz->rdev);
rc_free_device(sz->rdev);
usb_set_intfdata(intf, NULL);
rc_dev_fail:
diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 28e840a7e5b8..af1ee08ffdbe 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -374,8 +374,8 @@ static void sunxi_ir_remove(struct platform_device *pdev)
struct sunxi_ir *ir = platform_get_drvdata(pdev);
rc_unregister_device(ir->rc);
- rc_free_device(ir->rc);
sunxi_ir_hw_exit(&pdev->dev);
+ rc_free_device(ir->rc);
}
static void sunxi_ir_shutdown(struct platform_device *pdev)
diff --git a/drivers/media/test-drivers/vicodec/codec-fwht.h b/drivers/media/test-drivers/vicodec/codec-fwht.h
index 0eab24020e9e..4b4d39031089 100644
--- a/drivers/media/test-drivers/vicodec/codec-fwht.h
+++ b/drivers/media/test-drivers/vicodec/codec-fwht.h
@@ -61,7 +61,7 @@
* both luma and chroma components resolutions are rounded up to
* a multiple of 8
*/
-#define vic_round_dim(dim, div) (round_up((dim) / (div), 8) * (div))
+#define vic_round_dim(dim, div) round_up(dim, 8 * (div))
struct fwht_cframe_hdr {
u32 magic1;
diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c
index 654da27b1140..3052d248a728 100644
--- a/drivers/media/test-drivers/vicodec/vicodec-core.c
+++ b/drivers/media/test-drivers/vicodec/vicodec-core.c
@@ -652,7 +652,7 @@ static int job_ready(void *priv)
* if the header is invalid the device_run will just drop the frame
* with an error
*/
- if (!is_header_valid(&ctx->state.header) && ctx->comp_has_frame)
+ if (!is_header_valid(&ctx->state.header))
return 1;
flags = ntohl(ctx->state.header.flags);
hdr_width_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2;
@@ -893,6 +893,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vicodec_ctx *ctx = file2ctx(file);
+ struct vicodec_q_data *q_data_out =
+ get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
struct v4l2_pix_format_mplane *pix_mp;
struct v4l2_pix_format *pix;
@@ -903,6 +905,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
pix = &f->fmt.pix;
pix->pixelformat = ctx->is_enc ? V4L2_PIX_FMT_FWHT :
find_fmt(f->fmt.pix.pixelformat)->id;
+ if (ctx->is_enc) {
+ pix->width = q_data_out->coded_width;
+ pix->height = q_data_out->coded_height;
+ }
pix->colorspace = ctx->state.colorspace;
pix->xfer_func = ctx->state.xfer_func;
pix->ycbcr_enc = ctx->state.ycbcr_enc;
@@ -914,6 +920,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
pix_mp = &f->fmt.pix_mp;
pix_mp->pixelformat = ctx->is_enc ? V4L2_PIX_FMT_FWHT :
find_fmt(pix_mp->pixelformat)->id;
+ if (ctx->is_enc) {
+ pix_mp->width = q_data_out->coded_width;
+ pix_mp->height = q_data_out->coded_height;
+ }
pix_mp->colorspace = ctx->state.colorspace;
pix_mp->xfer_func = ctx->state.xfer_func;
pix_mp->ycbcr_enc = ctx->state.ycbcr_enc;
@@ -1596,9 +1606,9 @@ static int vicodec_start_streaming(struct vb2_queue *q,
}
state->ref_stride = q_data->coded_width * info->luma_alpha_step;
- state->ref_frame.buf = kvmalloc(total_planes_size, GFP_KERNEL);
+ state->ref_frame.buf = kvzalloc(total_planes_size, GFP_KERNEL);
state->ref_frame.luma = state->ref_frame.buf;
- new_comp_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL);
+ new_comp_frame = kvzalloc(ctx->comp_max_size, GFP_KERNEL);
if (!state->ref_frame.luma || !new_comp_frame) {
kvfree(state->ref_frame.luma);
diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c
index 8a8e097d2ac9..60310ece3530 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c
@@ -474,6 +474,7 @@ static int vidtv_bridge_dvb_init(struct vidtv_dvb *dvb)
fail_demod_probe:
for (i = i - 1; i >= 0; --i) {
dvb_unregister_frontend(dvb->fe[i]);
+ dvb_frontend_detach(dvb->fe[i]);
fail_fe:
dvb_module_release(dvb->i2c_client_tuner[i]);
fail_tuner_probe:
@@ -550,8 +551,11 @@ static void vidtv_bridge_remove(struct platform_device *pdev)
mutex_destroy(&dvb->feed_lock);
+ vidtv_stop_streaming(dvb);
+
for (i = 0; i < NUM_FE; ++i) {
dvb_unregister_frontend(dvb->fe[i]);
+ dvb_frontend_detach(dvb->fe[i]);
dvb_module_release(dvb->i2c_client_tuner[i]);
dvb_module_release(dvb->i2c_client_demod[i]);
}
diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.c b/drivers/media/test-drivers/vidtv/vidtv_demod.c
index 505f96fccbf3..36bfeb33476b 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_demod.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_demod.c
@@ -343,13 +343,6 @@ static int vidtv_diseqc_send_burst(struct dvb_frontend *fe,
return 0;
}
-static void vidtv_demod_release(struct dvb_frontend *fe)
-{
- struct vidtv_demod_state *state = fe->demodulator_priv;
-
- kfree(state);
-}
-
static const struct dvb_frontend_ops vidtv_demod_ops = {
.delsys = {
SYS_DVBT,
@@ -390,8 +383,6 @@ static const struct dvb_frontend_ops vidtv_demod_ops = {
FE_CAN_HIERARCHY_AUTO,
},
- .release = vidtv_demod_release,
-
.set_frontend = vidtv_demod_set_frontend,
.get_frontend = vidtv_demod_get_frontend,
diff --git a/drivers/media/test-drivers/vim2m.c b/drivers/media/test-drivers/vim2m.c
index 86c32699111a..5db440bf728e 100644
--- a/drivers/media/test-drivers/vim2m.c
+++ b/drivers/media/test-drivers/vim2m.c
@@ -205,6 +205,7 @@ struct vim2m_ctx {
struct vim2m_dev *dev;
struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *trans_num_bufs_ctrl;
/* Processed buffers in this transaction */
u8 num_processed;
@@ -1270,9 +1271,27 @@ static int vim2m_start_streaming(struct vb2_queue *q, unsigned int count)
ctx->aborting = 0;
q_data->sequence = 0;
+ v4l2_ctrl_grab(ctx->trans_num_bufs_ctrl, true);
+
return 0;
}
+static bool vim2m_other_queue_is_streaming(struct vim2m_ctx *ctx,
+ struct vb2_queue *q)
+{
+ struct vb2_queue *other_vq;
+
+ if (!ctx->fh.m2m_ctx)
+ return false;
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ other_vq = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx);
+ else
+ other_vq = v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx);
+
+ return vb2_is_streaming(other_vq);
+}
+
static void vim2m_stop_streaming(struct vb2_queue *q)
{
struct vim2m_ctx *ctx = vb2_get_drv_priv(q);
@@ -1286,11 +1305,14 @@ static void vim2m_stop_streaming(struct vb2_queue *q)
else
vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
if (!vbuf)
- return;
+ break;
v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
&ctx->hdl);
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
}
+
+ if (!vim2m_other_queue_is_streaming(ctx, q))
+ v4l2_ctrl_grab(ctx->trans_num_bufs_ctrl, false);
}
static void vim2m_buf_request_complete(struct vb2_buffer *vb)
@@ -1392,7 +1414,8 @@ static int vim2m_open(struct file *file)
vim2m_ctrl_trans_time_msec.def = default_transtime;
v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_time_msec, NULL);
- v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_num_bufs, NULL);
+ ctx->trans_num_bufs_ctrl =
+ v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_num_bufs, NULL);
if (hdl->error) {
rc = hdl->error;
v4l2_ctrl_handler_free(hdl);
@@ -1447,10 +1470,10 @@ static int vim2m_release(struct file *file)
v4l2_fh_del(&ctx->fh, file);
v4l2_fh_exit(&ctx->fh);
- v4l2_ctrl_handler_free(&ctx->hdl);
mutex_lock(&dev->dev_mutex);
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
mutex_unlock(&dev->dev_mutex);
+ v4l2_ctrl_handler_free(&ctx->hdl);
kfree(ctx);
atomic_dec(&dev->num_inst);
diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c
index f44930502bda..64a15a24a9c3 100644
--- a/drivers/media/test-drivers/vivid/vivid-core.c
+++ b/drivers/media/test-drivers/vivid/vivid-core.c
@@ -854,6 +854,7 @@ static void vivid_dev_release(struct v4l2_device *v4l2_dev)
struct vivid_dev *dev = container_of(v4l2_dev, struct vivid_dev, v4l2_dev);
cancel_work_sync(&dev->update_hdmi_ctrl_work);
+ cancel_work_sync(&dev->update_svid_ctrl_work);
vivid_free_controls(dev);
v4l2_device_unregister(&dev->v4l2_dev);
#ifdef CONFIG_MEDIA_CONTROLLER
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
index d1c09bdb2f3b..cc2b704717f3 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
@@ -568,6 +568,7 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv,
const struct vivid_fmt *fmt;
unsigned bytesperline, max_bpl;
unsigned factor = 1;
+ unsigned int vdiv = 1;
unsigned w, h;
unsigned p;
bool user_set_csc = !!(mp->flags & V4L2_PIX_FMT_FLAG_SET_CSC);
@@ -620,6 +621,18 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv,
mp->height = r.height / factor;
}
+ /*
+ * The chroma planes of vertically subsampled formats hold
+ * height / vdownsampling lines. If the height is not a multiple of
+ * the subsampling factor, then the buffer size calculations round
+ * that number down while the test pattern generator rounds it up,
+ * so the generator writes one line past the end of the buffer.
+ * Round the height down to keep both in sync.
+ */
+ for (p = 0; p < fmt->planes; p++)
+ vdiv = max(vdiv, fmt->vdownsampling[p]);
+ mp->height = rounddown(mp->height, vdiv);
+
/* This driver supports custom bytesperline values */
mp->num_planes = fmt->buffers;
diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
index 1e246b47766d..c7ff52044bd2 100644
--- a/drivers/media/usb/au0828/au0828-core.c
+++ b/drivers/media/usb/au0828/au0828-core.c
@@ -697,6 +697,10 @@ static int au0828_usb_probe(struct usb_interface *interface,
retval = au0828_v4l2_device_register(interface, dev);
if (retval) {
au0828_usb_v4l2_media_release(dev);
+#ifdef CONFIG_MEDIA_CONTROLLER
+ media_device_delete(dev->media_dev, KBUILD_MODNAME,
+ THIS_MODULE);
+#endif
mutex_unlock(&dev->lock);
kfree(dev);
return retval;
diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c
index 51752a922a74..4dc637746685 100644
--- a/drivers/media/usb/au0828/au0828-dvb.c
+++ b/drivers/media/usb/au0828/au0828-dvb.c
@@ -163,9 +163,6 @@ static int stop_urb_transfer(struct au0828_dev *dev)
dprintk(2, "%s()\n", __func__);
- if (!dev->urb_streaming)
- return 0;
-
if (dev->bulk_timeout_running == 1) {
dev->bulk_timeout_running = 0;
timer_delete(&dev->bulk_timeout);
@@ -179,6 +176,7 @@ static int stop_urb_transfer(struct au0828_dev *dev)
kfree(dev->urbs[i]->transfer_buffer);
usb_free_urb(dev->urbs[i]);
+ dev->urbs[i] = NULL;
}
}
@@ -200,8 +198,10 @@ static int start_urb_transfer(struct au0828_dev *dev)
for (i = 0; i < URB_COUNT; i++) {
dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->urbs[i])
- return -ENOMEM;
+ if (!dev->urbs[i]) {
+ ret = -ENOMEM;
+ goto err;
+ }
purb = dev->urbs[i];
@@ -217,7 +217,7 @@ static int start_urb_transfer(struct au0828_dev *dev)
ret = -ENOMEM;
pr_err("%s: failed big buffer allocation, err = %d\n",
__func__, ret);
- return ret;
+ goto err;
}
purb->status = -EINPROGRESS;
@@ -235,10 +235,9 @@ static int start_urb_transfer(struct au0828_dev *dev)
for (i = 0; i < URB_COUNT; i++) {
ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC);
if (ret != 0) {
- stop_urb_transfer(dev);
pr_err("%s: failed urb submission, err = %d\n",
__func__, ret);
- return ret;
+ goto err;
}
}
@@ -249,6 +248,10 @@ static int start_urb_transfer(struct au0828_dev *dev)
dev->bulk_timeout_running = 1;
return 0;
+
+err:
+ stop_urb_transfer(dev);
+ return ret;
}
static void au0828_start_transport(struct au0828_dev *dev)
diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c
index 9c71b32552df..1179cb0334f4 100644
--- a/drivers/media/usb/cx231xx/cx231xx-audio.c
+++ b/drivers/media/usb/cx231xx/cx231xx-audio.c
@@ -576,12 +576,19 @@ static int cx231xx_audio_init(struct cx231xx *dev)
dev_dbg(dev->dev,
"probing for cx231xx non standard usbaudio\n");
+ /*
+ * Extension init errors are ignored by the cx231xx core, so fini()
+ * must be safe even if initialization fails part way through.
+ */
+ spin_lock_init(&adev->slock);
+ INIT_WORK(&dev->wq_trigger, audio_trigger);
+ atomic_set(&dev->stream_started, 0);
+
err = snd_card_new(dev->dev, index[devnr], "Cx231xx Audio",
THIS_MODULE, 0, &card);
if (err < 0)
return err;
- spin_lock_init(&adev->slock);
err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
if (err < 0)
goto err_free_card;
@@ -596,8 +603,6 @@ static int cx231xx_audio_init(struct cx231xx *dev)
strscpy(card->shortname, "Cx231xx Audio", sizeof(card->shortname));
strscpy(card->longname, "Conexant cx231xx Audio", sizeof(card->longname));
- INIT_WORK(&dev->wq_trigger, audio_trigger);
-
err = snd_card_register(card);
if (err < 0)
goto err_free_card;
@@ -651,8 +656,10 @@ static int cx231xx_audio_init(struct cx231xx *dev)
err_free_pkt_size:
kfree(adev->alt_max_pkt_size);
+ adev->alt_max_pkt_size = NULL;
err_free_card:
snd_card_free(card);
+ adev->sndcard = NULL;
return err;
}
@@ -669,6 +676,8 @@ static int cx231xx_audio_fini(struct cx231xx *dev)
return 0;
}
+ disable_work_sync(&dev->wq_trigger);
+
if (dev->adev.sndcard) {
snd_card_free_when_closed(dev->adev.sndcard);
kfree(dev->adev.alt_max_pkt_size);
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index d96b9ab276b7..1824e36e541f 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -1828,7 +1828,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
retval = cx231xx_init_v4l2(dev, udev, interface, isoc_pipe);
if (retval)
- goto err_init;
+ goto err_video_alt;
if (dev->current_pcb_config.ts1_source != 0xff) {
/* compute alternate max packet sizes for TS1 */
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index 19f5036a78d7..f85d054fbb5c 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
@@ -463,7 +463,7 @@ struct cx231xx_i2c_xfer_data {
u8 direction; /* 1 - IN, 0 - OUT */
u8 saddr_len; /* sub address len */
u16 saddr_dat; /* sub addr data */
- u8 buf_size; /* buffer size */
+ u16 buf_size; /* buffer size */
u8 *p_buffer; /* pointer to the buffer */
};
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 487c6ab784ab..d2957e4bf017 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -1391,8 +1391,16 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
"rtl2832_sdr",
PLATFORM_DEVID_AUTO,
&pdata, sizeof(pdata));
- if (IS_ERR(pdev) || pdev->dev.driver == NULL)
+ if (IS_ERR(pdev))
break;
+ if (!pdev->dev.driver) {
+ platform_device_unregister(pdev);
+ break;
+ }
+ if (!try_module_get(pdev->dev.driver->owner)) {
+ platform_device_unregister(pdev);
+ break;
+ }
dev->platform_device_sdr = pdev;
break;
default:
@@ -1426,8 +1434,10 @@ static int rtl28xxu_tuner_detach(struct dvb_usb_adapter *adap)
/* remove platform SDR */
pdev = dev->platform_device_sdr;
- if (pdev)
+ if (pdev) {
+ module_put(pdev->dev.driver->owner);
platform_device_unregister(pdev);
+ }
/* remove I2C tuner */
client = dev->i2c_client_tuner;
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index 1012bfb18646..555281387513 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -949,9 +949,6 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk,
usb_bufs->buf[i] = kzalloc(sb_size, GFP_KERNEL);
if (!usb_bufs->buf[i]) {
- for (i--; i >= 0; i--)
- kfree(usb_bufs->buf[i]);
-
em28xx_uninit_usb_xfer(dev, mode);
return -ENOMEM;
}
diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c
index 334cdde81a5c..ad9cc44660dd 100644
--- a/drivers/media/usb/go7007/go7007-usb.c
+++ b/drivers/media/usb/go7007/go7007-usb.c
@@ -1029,10 +1029,16 @@ static const struct i2c_algorithm go7007_usb_algo = {
.functionality = go7007_usb_functionality,
};
+static const struct i2c_adapter_quirks go7007_usb_quirks = {
+ .max_write_len = 12,
+ .max_read_len = 15,
+};
+
static struct i2c_adapter go7007_usb_adap_templ = {
.owner = THIS_MODULE,
.name = "WIS GO7007SB EZ-USB",
.algo = &go7007_usb_algo,
+ .quirks = &go7007_usb_quirks,
};
/********************* USB add/remove functions *********************/
@@ -1320,6 +1326,8 @@ static int go7007_usb_probe(struct usb_interface *intf,
return 0;
allocfail:
+ if (go->i2c_adapter_online)
+ i2c_del_adapter(&go->i2c_adapter);
go7007_usb_release(go);
kfree(go);
return -ENOMEM;
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c
index 3077399901aa..dd6fcc6245af 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c
@@ -633,6 +633,7 @@ static void class_dev_create(struct pvr2_sysfs *sfp)
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"device_register failed");
+ put_device(class_dev->parent);
put_device(class_dev);
return;
}
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 2738ef74c737..7fd2e03f92e6 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -2010,7 +2010,7 @@ bool uvc_ctrl_status_event_async(struct urb *urb, struct uvc_video_chain *chain,
struct uvc_device *dev = chain->dev;
struct uvc_ctrl_work *w = &dev->async_ctrl;
- if (list_empty(&ctrl->info.mappings))
+ if (!ctrl->initialized || list_empty(&ctrl->info.mappings))
return false;
w->data = data;
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 524c11e92aec..0ca98df9e60b 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -296,7 +296,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_NV20, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_NV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_NV42, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_P010, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_P010, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
{ .format = V4L2_PIX_FMT_P012, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
{ .format = V4L2_PIX_FMT_YUV410, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 4, .vdiv = 4 },
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 02e2ae93d176..579787f0be35 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -2431,7 +2431,10 @@ void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
int i;
/* The first control is the master control and it must not be NULL */
- if (WARN_ON(ncontrols == 0 || controls[0] == NULL))
+ if (WARN_ON(ncontrols == 0))
+ return;
+
+ if (!controls[0])
return;
for (i = 0; i < ncontrols; i++) {
@@ -2453,8 +2456,13 @@ void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
u32 flag = 0;
int i;
+ if (WARN_ON(ncontrols <= 1))
+ return;
+
+ if (!master)
+ return;
+
v4l2_ctrl_cluster(ncontrols, controls);
- WARN_ON(ncontrols <= 1);
WARN_ON(manual_val < master->minimum || manual_val > master->maximum);
WARN_ON(set_volatile && !has_op(master, g_volatile_ctrl));
master->is_auto = true;
diff --git a/drivers/memory/brcmstb_dpfe.c b/drivers/memory/brcmstb_dpfe.c
index 08d9e05b1b33..7b02c36c7fca 100644
--- a/drivers/memory/brcmstb_dpfe.c
+++ b/drivers/memory/brcmstb_dpfe.c
@@ -182,6 +182,8 @@ struct brcmstb_dpfe_priv {
void __iomem *regs;
void __iomem *dmem;
void __iomem *imem;
+ resource_size_t regs_size;
+ resource_size_t dmem_size;
struct device *dev;
const struct dpfe_api *dpfe_api;
struct mutex lock;
@@ -401,9 +403,14 @@ static void __iomem *get_msg_ptr(struct brcmstb_dpfe_priv *priv, u32 response,
*/
switch (msg_type) {
case 1:
+ if (DCPU_MSG_RAM_START + offset + DRAM_VENDOR_ERROR +
+ sizeof(u32) > priv->regs_size)
+ goto bad_offset;
ptr = priv->regs + DCPU_MSG_RAM_START + offset;
break;
case 0:
+ if (offset + DRAM_VENDOR_ERROR + sizeof(u32) > priv->dmem_size)
+ goto bad_offset;
ptr = priv->dmem + offset;
break;
default:
@@ -415,6 +422,12 @@ static void __iomem *get_msg_ptr(struct brcmstb_dpfe_priv *priv, u32 response,
}
return ptr;
+
+bad_offset:
+ dev_err(priv->dev, "DCPU returned out-of-range offset %#x\n", offset);
+ if (buf && size)
+ *size = sprintf(buf, "ERROR: DCPU offset out of range\n");
+ return NULL;
}
static void __finalize_command(struct brcmstb_dpfe_priv *priv)
@@ -490,6 +503,8 @@ static int __send_command(struct brcmstb_dpfe_priv *priv, unsigned int cmd,
for (i = 0; i < MSG_FIELD_MAX; i++)
result[i] = readl_relaxed(regs + DCPU_MSG_RAM(i));
chksum_idx = result[MSG_ARG_COUNT] + MSG_ARG_COUNT + 1;
+ if (chksum_idx >= MSG_FIELD_MAX)
+ ret = -EINVAL;
}
/* Tell DCPU we are done */
@@ -858,6 +873,7 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct brcmstb_dpfe_priv *priv;
+ struct resource *res;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -869,17 +885,21 @@ static int brcmstb_dpfe_probe(struct platform_device *pdev)
mutex_init(&priv->lock);
platform_set_drvdata(pdev, priv);
- priv->regs = devm_platform_ioremap_resource_byname(pdev, "dpfe-cpu");
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-cpu");
+ priv->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->regs)) {
dev_err(dev, "couldn't map DCPU registers\n");
return -ENODEV;
}
+ priv->regs_size = resource_size(res);
- priv->dmem = devm_platform_ioremap_resource_byname(pdev, "dpfe-dmem");
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-dmem");
+ priv->dmem = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->dmem)) {
dev_err(dev, "Couldn't map DCPU data memory\n");
return -ENOENT;
}
+ priv->dmem_size = resource_size(res);
priv->imem = devm_platform_ioremap_resource_byname(pdev, "dpfe-imem");
if (IS_ERR(priv->imem)) {
diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c
index 4a417b693080..7f75052b6ae3 100644
--- a/drivers/memory/renesas-rpc-if.c
+++ b/drivers/memory/renesas-rpc-if.c
@@ -34,6 +34,7 @@ static const struct regmap_access_table rpcif_volatile_table = {
static const struct regmap_range xspi_volatile_ranges[] = {
regmap_reg_range(XSPI_CDD0BUF0, XSPI_CDD0BUF0),
+ regmap_reg_range(XSPI_CDD1BUF0, XSPI_CDD1BUF0),
};
static const struct regmap_access_table xspi_volatile_table = {
diff --git a/drivers/memory/stm32_omm.c b/drivers/memory/stm32_omm.c
index 2a1af229d244..faef90334249 100644
--- a/drivers/memory/stm32_omm.c
+++ b/drivers/memory/stm32_omm.c
@@ -275,6 +275,8 @@ static int stm32_omm_configure(struct device *dev)
writel_relaxed(omm->cr, omm->io_base + OMM_CR);
ret = stm32_omm_set_amcr(dev, true);
+ if (ret && (mux & CR_MUXEN))
+ stm32_omm_toggle_child_clock(dev, false);
error:
pm_runtime_put_sync_suspend(dev);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 55a9fea95195..ae39cbf31710 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -989,6 +989,8 @@ config MFD_MAX77705
depends on I2C
select MFD_CORE
select MFD_SIMPLE_MFD_I2C
+ select REGMAP_I2C
+ select REGMAP_IRQ
help
Say yes here to add support for Maxim Integrated MAX77705 PMIC.
This is a Power Management IC with Charger, safe LDOs, Flash, Haptic
diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c
index e86b39de3303..e4ad48e1f139 100644
--- a/drivers/mfd/da903x.c
+++ b/drivers/mfd/da903x.c
@@ -421,6 +421,14 @@ static irqreturn_t da903x_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
+static void da903x_cancel_irq_work(void *data)
+{
+ struct da903x_chip *chip = data;
+
+ disable_irq(chip->client->irq);
+ cancel_work_sync(&chip->irq_work);
+}
+
static const struct da903x_chip_ops da903x_ops[] = {
[0] = {
.init_chip = da9030_init_chip,
@@ -456,6 +464,13 @@ static int da903x_remove_subdevs(struct da903x_chip *chip)
return device_for_each_child(chip->dev, NULL, __remove_subdev);
}
+static void da903x_remove_subdevs_action(void *data)
+{
+ struct da903x_chip *chip = data;
+
+ da903x_remove_subdevs(chip);
+}
+
static int da903x_add_subdevs(struct da903x_chip *chip,
struct da903x_platform_data *pdata)
{
@@ -484,7 +499,6 @@ static int da903x_add_subdevs(struct da903x_chip *chip,
return 0;
failed:
- da903x_remove_subdevs(chip);
return ret;
}
@@ -520,6 +534,11 @@ static int da903x_probe(struct i2c_client *client)
chip->ops->mask_events(chip, chip->events_mask);
chip->ops->read_events(chip, &tmp);
+ ret = devm_add_action_or_reset(&client->dev,
+ da903x_remove_subdevs_action, chip);
+ if (ret)
+ return ret;
+
ret = devm_request_irq(&client->dev, client->irq, da903x_irq_handler,
IRQF_TRIGGER_FALLING,
"da903x", chip);
@@ -529,14 +548,12 @@ static int da903x_probe(struct i2c_client *client)
return ret;
}
- return da903x_add_subdevs(chip, pdata);
-}
-
-static void da903x_remove(struct i2c_client *client)
-{
- struct da903x_chip *chip = i2c_get_clientdata(client);
+ ret = devm_add_action_or_reset(&client->dev, da903x_cancel_irq_work,
+ chip);
+ if (ret)
+ return ret;
- da903x_remove_subdevs(chip);
+ return da903x_add_subdevs(chip, pdata);
}
static struct i2c_driver da903x_driver = {
@@ -544,7 +561,6 @@ static struct i2c_driver da903x_driver = {
.name = "da903x",
},
.probe = da903x_probe,
- .remove = da903x_remove,
.id_table = da903x_id_table,
};
diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c
index c240fac0ede7..569c28c8b20f 100644
--- a/drivers/mfd/tps65217.c
+++ b/drivers/mfd/tps65217.c
@@ -146,6 +146,31 @@ static const struct irq_domain_ops tps65217_irq_domain_ops = {
.map = tps65217_irq_map,
};
+static void tps65217_irq_cleanup(void *data)
+{
+ struct tps65217 *tps = data;
+ unsigned int virq;
+
+ if (!tps->irq_domain)
+ return;
+
+ for (int i = 0; i < TPS65217_NUM_IRQ; i++) {
+ virq = irq_find_mapping(tps->irq_domain, i);
+ if (virq)
+ irq_dispose_mapping(virq);
+ }
+
+ irq_domain_remove(tps->irq_domain);
+ tps->irq_domain = NULL;
+}
+
+static void tps65217_irq_wake_disable(void *data)
+{
+ unsigned long irq = (unsigned long)data;
+
+ disable_irq_wake(irq);
+}
+
static int tps65217_irq_init(struct tps65217 *tps, int irq)
{
int ret;
@@ -155,8 +180,12 @@ static int tps65217_irq_init(struct tps65217 *tps, int irq)
/* Mask all interrupt sources */
tps->irq_mask = TPS65217_INT_MASK;
- tps65217_set_bits(tps, TPS65217_REG_INT, TPS65217_INT_MASK,
- TPS65217_INT_MASK, TPS65217_PROTECT_NONE);
+ ret = tps65217_set_bits(tps, TPS65217_REG_INT, TPS65217_INT_MASK,
+ TPS65217_INT_MASK, TPS65217_PROTECT_NONE);
+ if (ret) {
+ dev_err(tps->dev, "Failed to mask interrupt sources: %d\n", ret);
+ return ret;
+ }
tps->irq_domain = irq_domain_create_linear(dev_fwnode(tps->dev), TPS65217_NUM_IRQ,
&tps65217_irq_domain_ops, tps);
@@ -165,6 +194,10 @@ static int tps65217_irq_init(struct tps65217 *tps, int irq)
return -ENOMEM;
}
+ ret = devm_add_action_or_reset(tps->dev, tps65217_irq_cleanup, tps);
+ if (ret)
+ return ret;
+
ret = devm_request_threaded_irq(tps->dev, irq, NULL,
tps65217_irq_thread, IRQF_ONESHOT,
"tps65217-irq", tps);
@@ -174,7 +207,16 @@ static int tps65217_irq_init(struct tps65217 *tps, int irq)
return ret;
}
- enable_irq_wake(irq);
+ ret = enable_irq_wake(irq);
+ if (ret) {
+ dev_warn(tps->dev, "failed to enable IRQ wake: %d\n", ret);
+ return 0;
+ }
+
+ ret = devm_add_action_or_reset(tps->dev, tps65217_irq_wake_disable,
+ (void *)(unsigned long)irq);
+ if (ret)
+ return ret;
return 0;
}
@@ -333,7 +375,9 @@ static int tps65217_probe(struct i2c_client *client)
}
if (client->irq) {
- tps65217_irq_init(tps, client->irq);
+ ret = tps65217_irq_init(tps, client->irq);
+ if (ret)
+ return ret;
} else {
int i;
@@ -373,22 +417,6 @@ static int tps65217_probe(struct i2c_client *client)
return 0;
}
-static void tps65217_remove(struct i2c_client *client)
-{
- struct tps65217 *tps = i2c_get_clientdata(client);
- unsigned int virq;
- int i;
-
- for (i = 0; i < TPS65217_NUM_IRQ; i++) {
- virq = irq_find_mapping(tps->irq_domain, i);
- if (virq)
- irq_dispose_mapping(virq);
- }
-
- irq_domain_remove(tps->irq_domain);
- tps->irq_domain = NULL;
-}
-
static const struct i2c_device_id tps65217_id_table[] = {
{"tps65217", TPS65217},
{ /* sentinel */ }
@@ -402,7 +430,6 @@ static struct i2c_driver tps65217_driver = {
},
.id_table = tps65217_id_table,
.probe = tps65217_probe,
- .remove = tps65217_remove,
};
static int __init tps65217_init(void)
diff --git a/drivers/mfd/wcd934x.c b/drivers/mfd/wcd934x.c
index 3c3080e8c8cf..d57796933d87 100644
--- a/drivers/mfd/wcd934x.c
+++ b/drivers/mfd/wcd934x.c
@@ -2,6 +2,7 @@
// Copyright (c) 2019, Linaro Limited
#include <linux/clk.h>
+#include <linux/dma-mapping.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -217,13 +218,18 @@ static int wcd934x_slim_probe(struct slim_device *sdev)
struct gpio_desc *reset_gpio;
int ret;
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to set DMA mask\n");
+
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
ddata->irq = of_irq_get(np, 0);
if (ddata->irq < 0)
- return dev_err_probe(ddata->dev, ddata->irq,
+ return dev_err_probe(dev, ddata->irq,
"Failed to get IRQ\n");
ddata->extclk = devm_clk_get(dev, "extclk");
diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c
index 5ecf99883996..e70c07aa0d60 100644
--- a/drivers/misc/mei/vsc-tp.c
+++ b/drivers/misc/mei/vsc-tp.c
@@ -293,7 +293,7 @@ int vsc_tp_xfer(struct vsc_tp *tp, u8 cmd, const void *obuf, size_t olen,
pkt->hdr.seq = cpu_to_le32(++tp->seq);
memcpy(pkt->buf, obuf, olen);
- crc = ~crc32(~0, (u8 *)pkt, sizeof(pkt) + olen);
+ crc = ~crc32(~0, (u8 *)pkt, sizeof(pkt->hdr) + olen);
memcpy(pkt->buf + olen, &crc, sizeof(crc));
ret = vsc_tp_wakeup_request(tp);
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index 7c91429a670b..c8a82b1592c9 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -283,6 +283,21 @@ static void fixup_use_write_buffers(struct mtd_info *mtd)
pr_debug("Using buffer write method\n");
mtd->_write = cfi_amdstd_write_buffers;
}
+
+ /*
+ * The word count of the Write to Buffer command is a single bus
+ * word per device, so an x8 device can be told to program at most
+ * 256 bytes however large a buffer it advertises - including when
+ * several of them are interleaved on a wider bus, where CMD()
+ * replicates the count into each device's lane and it is truncated
+ * there.
+ */
+ if (cfi->device_type == CFI_DEVICETYPE_X8 &&
+ cfi->cfiq->MaxBufWriteSize > 8) {
+ cfi->cfiq->MaxBufWriteSize = 8;
+ mtd->writebufsize = cfi_interleave(cfi) <<
+ cfi->cfiq->MaxBufWriteSize;
+ }
}
#endif /* !FORCE_WORD_WRITE */
diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c
index 281fcbaa74e7..43411ec02ed5 100644
--- a/drivers/mtd/maps/amd76xrom.c
+++ b/drivers/mtd/maps/amd76xrom.c
@@ -152,6 +152,8 @@ static int amd76xrom_init_one(struct pci_dev *pdev,
printk(KERN_ERR MOD_NAME
" %s(): Unable to register resource %pR - kernel bug?\n",
__func__, &window->rsrc);
+ pci_dev_put(window->pdev);
+ window->pdev = NULL;
return -EBUSY;
}
diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c
index 832b880d1aaf..1df5ea5fafca 100644
--- a/drivers/mtd/maps/l440gx.c
+++ b/drivers/mtd/maps/l440gx.c
@@ -110,8 +110,6 @@ static int __init init_l440gx(void)
/* Allocate the resource region */
if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) {
- pci_dev_put(dev);
- pci_dev_put(pm_dev);
printk(KERN_WARNING "Could not allocate pm iobase resource\n");
iounmap(l440gx_map.virt);
return -ENXIO;
diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c
index ca00d211e73e..c6bbce6aed41 100644
--- a/drivers/mtd/maps/pci.c
+++ b/drivers/mtd/maps/pci.c
@@ -256,7 +256,7 @@ static int mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
struct mtd_info *mtd = NULL;
int err;
- err = pci_enable_device(dev);
+ err = pcim_enable_device(dev);
if (err)
goto out;
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 51c8ed489348..f84e4213b678 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -562,7 +562,7 @@ int mtd_pairing_info_to_wunit(struct mtd_info *mtd,
return -EINVAL;
if (master->pairing && master->pairing->get_wunit)
- return mtd->pairing->get_wunit(master, info);
+ return master->pairing->get_wunit(master, info);
return info->pair;
}
diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c
index b7e3763c47f0..22ddeaa7d522 100644
--- a/drivers/mtd/mtdsuper.c
+++ b/drivers/mtd/mtdsuper.c
@@ -31,8 +31,10 @@ static int mtd_get_sb(struct fs_context *fc,
int ret;
sb = sget_dev(fc, MKDEV(MTD_BLOCK_MAJOR, mtd->index));
- if (IS_ERR(sb))
+ if (IS_ERR(sb)) {
+ put_mtd_device(mtd);
return PTR_ERR(sb);
+ }
if (sb->s_root) {
/* new mountpoint for an already mounted superblock */
diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
index 83ba4ebd02d4..b954e70b4c6b 100644
--- a/drivers/mtd/nand/raw/atmel/nand-controller.c
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -2086,7 +2086,7 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
if (!np) {
dev_err(dev, "Missing or invalid atmel,smc property\n");
ret = -EINVAL;
- goto out_release_dma;
+ goto out_put_mck;
}
nc->smc = syscon_node_to_regmap(np);
@@ -2094,11 +2094,13 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
if (IS_ERR(nc->smc)) {
ret = PTR_ERR(nc->smc);
dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret);
- goto out_release_dma;
+ goto out_put_mck;
}
return 0;
+out_put_mck:
+ clk_put(nc->mck);
out_release_dma:
if (nc->dmac)
dma_release_channel(nc->dmac);
diff --git a/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c b/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c
index 1a285cd8fad6..f659d3a6b9ef 100644
--- a/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c
+++ b/drivers/mtd/nand/raw/nuvoton-ma35d1-nand-controller.c
@@ -993,7 +993,6 @@ static int ma35_nand_probe(struct platform_device *pdev)
ret = ma35_nand_chips_init(&pdev->dev, nand);
if (ret) {
dev_err(&pdev->dev, "failed to init NAND chips\n");
- clk_disable(nand->clk);
return ret;
}
diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c
index 4dd6f1a4e797..b469c01aacf9 100644
--- a/drivers/mtd/nand/raw/qcom_nandc.c
+++ b/drivers/mtd/nand/raw/qcom_nandc.c
@@ -328,14 +328,16 @@ config_nand_cw_read(struct nand_chip *chip, bool use_ecc, int cw)
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc;
-
__le32 *reg = &nandc->regs->read_location0;
+ int reg_base = NAND_READ_LOCATION_0;
- if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw))
+ if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) {
reg = &nandc->regs->read_location_last0;
+ reg_base = NAND_READ_LOCATION_LAST_CW_0;
+ }
if (nandc->props->supports_bam)
- qcom_write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, reg, reg_base, 4, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index 75db1713e617..f9b72671c525 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -1631,6 +1631,9 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) {
int bytes;
+ if (mtd->writesize < 1024)
+ return -EINVAL;
+
ecc->size = 1024;
nsectors = mtd->writesize / ecc->size;
diff --git a/drivers/mtd/parsers/qcomsmempart.c b/drivers/mtd/parsers/qcomsmempart.c
index 4311b89d8df0..b12d87c3b023 100644
--- a/drivers/mtd/parsers/qcomsmempart.c
+++ b/drivers/mtd/parsers/qcomsmempart.c
@@ -143,9 +143,9 @@ static int parse_qcomsmem_part(struct mtd_info *mtd,
*c = tolower(*c);
parts[j].name = name;
- parts[j].offset = le32_to_cpu(pentry->offset) * mtd->erasesize;
+ parts[j].offset = (u64)le32_to_cpu(pentry->offset) * mtd->erasesize;
parts[j].mask_flags = pentry->attr;
- parts[j].size = le32_to_cpu(pentry->length) * mtd->erasesize;
+ parts[j].size = (u64)le32_to_cpu(pentry->length) * mtd->erasesize;
pr_debug("%d: %s offs=0x%08x size=0x%08x attr:0x%08x\n",
i, pentry->name, le32_to_cpu(pentry->offset),
le32_to_cpu(pentry->length), pentry->attr);
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 037407fc2e11..512d0e4e3b1f 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1281,6 +1281,9 @@ int spi_nor_prep_and_lock(struct spi_nor *nor)
ret = wait_event_killable(nor->rww.wait,
spi_nor_rww_start_exclusive(nor));
+ if (ret)
+ spi_nor_unprep(nor);
+
return ret;
}
@@ -1352,6 +1355,9 @@ static int spi_nor_prep_and_lock_pe(struct spi_nor *nor, loff_t start, size_t le
ret = wait_event_killable(nor->rww.wait,
spi_nor_rww_start_pe(nor, start, len));
+ if (ret)
+ spi_nor_unprep(nor);
+
return ret;
}
@@ -1425,6 +1431,9 @@ static int spi_nor_prep_and_lock_rd(struct spi_nor *nor, loff_t start, size_t le
ret = wait_event_killable(nor->rww.wait,
spi_nor_rww_start_rd(nor, start, len));
+ if (ret)
+ spi_nor_unprep(nor);
+
return ret;
}
diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index a8324c2da0ac..a9668c83ac5e 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -1175,6 +1175,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
#define PROFILE1_DWORD5_DUMMY_166MHZ GENMASK(31, 27)
#define PROFILE1_DWORD5_DUMMY_133MHZ GENMASK(21, 17)
#define PROFILE1_DWORD5_DUMMY_100MHZ GENMASK(11, 7)
+#define SFDP_PROFILE1_DWORD_MIN 5
/**
* spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table
@@ -1192,6 +1193,9 @@ static int spi_nor_parse_profile1(struct spi_nor *nor,
int ret;
u8 dummy, opcode;
+ if (profile1_header->length < SFDP_PROFILE1_DWORD_MIN)
+ return -EINVAL;
+
len = profile1_header->length * sizeof(*dwords);
dwords = kmalloc(len, GFP_KERNEL);
if (!dwords)
@@ -1262,6 +1266,7 @@ static int spi_nor_parse_profile1(struct spi_nor *nor,
}
#define SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE BIT(31)
+#define SFDP_SCCR_DWORD_MIN 22
/**
* spi_nor_parse_sccr() - Parse the Status, Control and Configuration Register
@@ -1280,6 +1285,9 @@ static int spi_nor_parse_sccr(struct spi_nor *nor,
size_t len;
int ret;
+ if (sccr_header->length < SFDP_SCCR_DWORD_MIN)
+ return -EINVAL;
+
len = sccr_header->length * sizeof(*dwords);
dwords = kmalloc(len, GFP_KERNEL);
if (!dwords)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index baafc13603d3..2e9e1b4d74da 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -6270,7 +6270,7 @@ static int __init bond_check_params(struct bond_params *params)
}
for (arp_ip_count = 0, i = 0;
- (arp_ip_count < BOND_MAX_ARP_TARGETS) && arp_ip_target[i]; i++) {
+ (i < BOND_MAX_ARP_TARGETS) && arp_ip_target[i]; i++) {
__be32 ip;
/* not a complete check, but good enough to catch mistakes */
diff --git a/drivers/net/ethernet/google/gve/gve_flow_rule.c b/drivers/net/ethernet/google/gve/gve_flow_rule.c
index 0bb8cd1876a3..426a264af8b2 100644
--- a/drivers/net/ethernet/google/gve/gve_flow_rule.c
+++ b/drivers/net/ethernet/google/gve/gve_flow_rule.c
@@ -206,6 +206,7 @@ int gve_get_flow_rule_entry(struct gve_priv *priv, struct ethtool_rxnfc *cmd)
return -EOPNOTSUPP;
if (!priv->flow_rules_cache.rules_cache_synced ||
+ !*cache_num ||
fsp->location < be32_to_cpu(rules_cache[0].location) ||
fsp->location > be32_to_cpu(rules_cache[*cache_num - 1].location)) {
err = gve_adminq_query_flow_rules(priv, GVE_FLOW_RULE_QUERY_RULES, fsp->location);
diff --git a/drivers/net/ethernet/hisilicon/hisi_femac.c b/drivers/net/ethernet/hisilicon/hisi_femac.c
index d244a40df430..fba68035c969 100644
--- a/drivers/net/ethernet/hisilicon/hisi_femac.c
+++ b/drivers/net/ethernet/hisilicon/hisi_femac.c
@@ -491,6 +491,7 @@ static int hisi_femac_net_close(struct net_device *dev)
netif_stop_queue(dev);
napi_disable(&priv->napi);
+ hisi_femac_port_reset(priv);
hisi_femac_free_skb_rings(priv);
return 0;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index f950570909ab..75ba78a84c86 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -9426,23 +9426,6 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
}
protocol = vlan_get_protocol(skb);
- if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
- adapter->ptp_clock) {
- if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON &&
- !test_and_set_bit_lock(__IXGBE_PTP_TX_IN_PROGRESS,
- &adapter->state)) {
- skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
- tx_flags |= IXGBE_TX_FLAGS_TSTAMP;
-
- /* schedule check for Tx timestamp */
- adapter->ptp_tx_skb = skb_get(skb);
- adapter->ptp_tx_start = jiffies;
- schedule_work(&adapter->ptp_tx_work);
- } else {
- adapter->tx_hwtstamp_skipped++;
- }
- }
-
#ifdef CONFIG_PCI_IOV
/*
* Use the l2switch_enable flag - would be false if the DMA
@@ -9507,6 +9490,29 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
#ifdef IXGBE_FCOE
xmit_fcoe:
#endif /* IXGBE_FCOE */
+ /* Reserve the Tx timestamp only after all skb preparation:
+ * skb_get() makes the skb shared, and pskb_expand_head()
+ * (reached from skb_cow_head() or skb_checksum_help())
+ * refuses shared skbs.
+ */
+ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+ adapter->ptp_clock) {
+ if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON &&
+ !test_and_set_bit_lock(__IXGBE_PTP_TX_IN_PROGRESS,
+ &adapter->state)) {
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ tx_flags |= IXGBE_TX_FLAGS_TSTAMP;
+ first->tx_flags |= IXGBE_TX_FLAGS_TSTAMP;
+
+ /* schedule check for Tx timestamp */
+ adapter->ptp_tx_skb = skb_get(skb);
+ adapter->ptp_tx_start = jiffies;
+ schedule_work(&adapter->ptp_tx_work);
+ } else {
+ adapter->tx_hwtstamp_skipped++;
+ }
+ }
+
if (ixgbe_tx_map(tx_ring, first, hdr_len))
goto cleanup_tx_timestamp;
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 7151ae883136..c38989009c76 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -1630,7 +1630,8 @@ static int macvlan_changelink(struct net_device *dev,
if (data && data[IFLA_MACVLAN_FLAGS]) {
__u16 flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]);
bool promisc = (flags ^ vlan->flags) & MACVLAN_FLAG_NOPROMISC;
- if (macvlan_passthru(vlan->port) && promisc) {
+ if (macvlan_passthru(vlan->port) && promisc &&
+ netif_running(dev)) {
int err;
if (flags & MACVLAN_FLAG_NOPROMISC)
diff --git a/drivers/net/netdevsim/bpf.c b/drivers/net/netdevsim/bpf.c
index 5f17f68f3c08..319f75e42f7c 100644
--- a/drivers/net/netdevsim/bpf.c
+++ b/drivers/net/netdevsim/bpf.c
@@ -150,6 +150,17 @@ int nsim_bpf_setup_tc_block_cb(enum tc_setup_type type,
return -EOPNOTSUPP;
}
+ /* A dev-bound program can be replayed here when a netdev joins
+ * a shared block, after the core attach-time check has run;
+ * enforce the device match as nfp does.
+ */
+ if (prog && bpf_prog_is_dev_bound(prog->aux) &&
+ !bpf_offload_dev_match(prog, ns->netdev)) {
+ NSIM_EA(cls_bpf->common.extack,
+ "program bound to a different device");
+ return -EINVAL;
+ }
+
if (cls_bpf->command != TC_CLSBPF_OFFLOAD)
return -EOPNOTSUPP;
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 14fa87a8e083..61c240d846ee 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -3204,6 +3204,7 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
len = CCP_LENGTH(dp);
if (!pskb_may_pull(skb, len + 2))
return; /* too short */
+ dp = skb->data + 2;
dp += CCP_HDRLEN;
len -= CCP_HDRLEN;
if (len < CCP_OPT_MINLEN || len < CCP_OPT_LENGTH(dp))
diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c
index f5a26a0bc679..69445a94ee6f 100644
--- a/drivers/net/usb/pegasus.c
+++ b/drivers/net/usb/pegasus.c
@@ -523,11 +523,17 @@ static void read_bulk_callback(struct urb *urb)
}
/*
- * If the packet is unreasonably long, quietly drop it rather than
- * kernel panicing by calling skb_put.
+ * Drop frames shorter than an Ethernet header, because
+ * eth_type_trans() reads the header whatever the length, and frames
+ * longer than what was actually received. Passing either up would
+ * expose stale buffer contents, and skb_put() could panic on the
+ * latter. count is at most PEGASUS_MTU.
*/
- if (pkt_len > PEGASUS_MTU - rx_offset)
+ if (pkt_len < ETH_HLEN || pkt_len + rx_offset > count) {
+ net->stats.rx_errors++;
+ net->stats.rx_length_errors++;
goto goon;
+ }
/*
* at this point we are sure pegasus->rx_skb != NULL
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 8cf4e81f8f88..7b9923b6480e 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -6785,11 +6785,8 @@ static void set_carrier(struct r8152 *tp)
{
struct net_device *netdev = tp->netdev;
struct napi_struct *napi = &tp->napi;
- u16 speed;
-
- speed = rtl8152_get_speed(tp);
- if (speed & LINK_STATUS) {
+ if (r8152_mdio_read(tp, MII_BMSR) & BMSR_LSTATUS) {
if (!netif_carrier_ok(netdev)) {
tp->rtl_ops.enable(tp);
netif_stop_queue(netdev);
@@ -6815,6 +6812,11 @@ static void set_carrier(struct r8152 *tp)
tasklet_enable(&tp->tx_tl);
netif_info(tp, link, netdev, "carrier off\n");
}
+
+ if (rtl8152_get_speed(tp) & LINK_STATUS) {
+ set_bit(RTL8152_LINK_CHG, &tp->flags);
+ schedule_delayed_work(&tp->schedule, 0);
+ }
}
}
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 148114ab85ac..25cdbba5dfa9 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -1621,10 +1621,9 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog,
}
}
- if (!old_prog) {
+ if (!old_prog)
peer->hw_features &= ~NETIF_F_GSO_SOFTWARE;
- peer->max_mtu = max_mtu;
- }
+ peer->max_mtu = max_mtu;
xdp_features_set_redirect_target(peer, true);
}
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index b7ab57f4e277..34390befdf8a 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -458,9 +458,6 @@ struct virtnet_info {
/* Work struct for setting rx mode */
struct work_struct rx_mode_work;
- /* OK to queue work setting RX mode? */
- bool rx_mode_work_enabled;
-
/* Does the affinity hint is set for virtqueues? */
bool affinity_hint_set;
@@ -740,16 +737,13 @@ static void disable_delayed_refill(struct virtnet_info *vi)
static void enable_rx_mode_work(struct virtnet_info *vi)
{
- rtnl_lock();
- vi->rx_mode_work_enabled = true;
- rtnl_unlock();
+ if (enable_work(&vi->rx_mode_work) && netif_running(vi->dev))
+ schedule_work(&vi->rx_mode_work);
}
static void disable_rx_mode_work(struct virtnet_info *vi)
{
- rtnl_lock();
- vi->rx_mode_work_enabled = false;
- rtnl_unlock();
+ disable_work_sync(&vi->rx_mode_work);
}
static void virtqueue_napi_schedule(struct napi_struct *napi,
@@ -3996,8 +3990,7 @@ static void virtnet_set_rx_mode(struct net_device *dev)
{
struct virtnet_info *vi = netdev_priv(dev);
- if (vi->rx_mode_work_enabled)
- schedule_work(&vi->rx_mode_work);
+ schedule_work(&vi->rx_mode_work);
}
static int virtnet_vlan_rx_add_vid(struct net_device *dev,
@@ -5813,7 +5806,6 @@ static void virtnet_freeze_down(struct virtio_device *vdev)
/* Make sure no work handler is accessing the device */
flush_work(&vi->config_work);
disable_rx_mode_work(vi);
- flush_work(&vi->rx_mode_work);
if (netif_running(vi->dev)) {
rtnl_lock();
@@ -5840,7 +5832,6 @@ static int virtnet_restore_up(struct virtio_device *vdev)
virtio_device_ready(vdev);
enable_delayed_refill(vi);
- enable_rx_mode_work(vi);
if (netif_running(vi->dev)) {
rtnl_lock();
@@ -5853,6 +5844,8 @@ static int virtnet_restore_up(struct virtio_device *vdev)
netif_tx_lock_bh(vi->dev);
netif_device_attach(vi->dev);
netif_tx_unlock_bh(vi->dev);
+
+ enable_rx_mode_work(vi);
return err;
}
@@ -6952,6 +6945,7 @@ static int virtnet_probe(struct virtio_device *vdev)
INIT_WORK(&vi->config_work, virtnet_config_changed_work);
INIT_WORK(&vi->rx_mode_work, virtnet_rx_mode_work);
+ disable_work(&vi->rx_mode_work);
spin_lock_init(&vi->refill_lock);
if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) {
@@ -7103,8 +7097,6 @@ static int virtnet_probe(struct virtio_device *vdev)
if (vi->has_rss || vi->has_rss_hash_report)
virtnet_init_default_rss(vi);
- enable_rx_mode_work(vi);
-
/* serialize netdev register + virtio_device_ready() with ndo_open() */
rtnl_lock();
@@ -7204,6 +7196,8 @@ static int virtnet_probe(struct virtio_device *vdev)
goto free_unregister_netdev;
}
+ enable_rx_mode_work(vi);
+
pr_debug("virtnet: registered device %s with %d RX and TX vq's\n",
dev->name, max_queue_pairs);
@@ -7255,7 +7249,6 @@ static void virtnet_remove(struct virtio_device *vdev)
/* Make sure no work handler is accessing the device. */
flush_work(&vi->config_work);
disable_rx_mode_work(vi);
- flush_work(&vi->rx_mode_work);
virtnet_free_irq_moder(vi);
diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c
index 68c7ef190780..5e67f088962e 100644
--- a/drivers/net/wireless/ath/ath11k/dp.c
+++ b/drivers/net/wireless/ath/ath11k/dp.c
@@ -316,6 +316,7 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
case HAL_RXDMA_DIR_BUF:
break;
default:
+ ath11k_dp_srng_cleanup(ab, ring);
ath11k_warn(ab, "Not a valid ring type in dp :%d\n", type);
return -EINVAL;
}
@@ -327,6 +328,7 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
ret = ath11k_hal_srng_setup(ab, type, ring_num, mac_id, ¶ms);
if (ret < 0) {
+ ath11k_dp_srng_cleanup(ab, ring);
ath11k_warn(ab, "failed to setup srng: %d ring_id %d\n",
ret, ring_num);
return ret;
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 91f293ab7013..67c9174a30aa 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -4814,6 +4814,7 @@ static int ath12k_wmi_svc_rdy_ext_parse(struct ath12k_base *ab,
u16 tag, u16 len,
const void *ptr, void *data)
{
+ struct ath12k_svc_ext_info *svc_ext_info = &ab->wmi_ab.svc_ext_info;
struct ath12k_wmi_pdev *wmi_handle = &ab->wmi_ab.wmi[0];
struct ath12k_wmi_svc_rdy_ext_parse *svc_rdy_ext = data;
int ret;
@@ -4858,6 +4859,22 @@ static int ath12k_wmi_svc_rdy_ext_parse(struct ath12k_base *ab,
return ret;
}
+ if (svc_rdy_ext->tot_phy_id >
+ ARRAY_SIZE(svc_ext_info->mac_phy_info)) {
+ ath12k_warn(ab, "too many PHY entries %u (max %zu)\n",
+ svc_rdy_ext->tot_phy_id,
+ ARRAY_SIZE(svc_ext_info->mac_phy_info));
+ return -EINVAL;
+ }
+
+ if (svc_rdy_ext->n_mac_phy_caps < svc_rdy_ext->tot_phy_id) {
+ ath12k_warn(ab,
+ "not enough MAC/PHY caps %u, expected %u\n",
+ svc_rdy_ext->n_mac_phy_caps,
+ svc_rdy_ext->tot_phy_id);
+ return -EINVAL;
+ }
+
ath12k_wmi_save_all_mac_phy_info(ab, svc_rdy_ext);
svc_rdy_ext->mac_phy_done = true;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c
index b03d5a1f1a93..7fd3e018affe 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c
@@ -221,7 +221,7 @@ static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_5GHz[] = {
};
static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_2GHz[] = {
- {0, 0, 0, 0, 0, 128},
+ {0, 0, 0, 0, 0, -128},
{0, 0, 0, 0, 0, 70},
{0, 0, 0, 0, 0, 20},
{0, 0, 0, 3, 0, 20},
diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c
index 6ce8e4af18fe..281b2c3e9e7c 100644
--- a/drivers/net/wireless/mediatek/mt76/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
@@ -224,12 +224,14 @@ mt76_find_power_limits_node(struct mt76_dev *dev)
struct property *regd = of_find_property(cur, "regdomain", NULL);
if (!country && !regd) {
- fallback = cur;
+ of_node_put(fallback);
+ fallback = of_node_get(cur);
continue;
}
if (mt76_string_prop_find(country, dev->alpha2) ||
mt76_string_prop_find(regd, region_name)) {
+ of_node_put(fallback);
of_node_put(np);
return cur;
}
@@ -356,7 +358,7 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
s8 target_power)
{
struct mt76_dev *dev = phy->dev;
- struct device_node *np;
+ struct device_node *np, *band_np;
const s8 *val;
char name[16];
u32 mcs_rates = dev->drv->mcs_rates;
@@ -389,15 +391,18 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
band = '6';
break;
default:
+ of_node_put(np);
return target_power;
}
snprintf(name, sizeof(name), "txpower-%cg", band);
- np = of_get_child_by_name(np, name);
- if (!np)
+ band_np = of_get_child_by_name(np, name);
+ of_node_put(np);
+ if (!band_np)
return target_power;
- np = mt76_find_channel_node(np, chan);
+ np = mt76_find_channel_node(band_np, chan);
+ of_node_put(band_np);
if (!np)
return target_power;
@@ -422,6 +427,8 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
ARRAY_SIZE(dest->ru), val, len,
target_power, txs_delta, &max_power);
+ of_node_put(np);
+
return max_power;
}
EXPORT_SYMBOL_GPL(mt76_get_rate_power_limits);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c
index c4f5f6ee916a..4b41462413bc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c
@@ -541,8 +541,10 @@ int mt7615_register_ext_phy(struct mt7615_dev *dev)
mt7615_cap_dbdc_enable(dev);
mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7615_ops, MT_BAND1);
- if (!mphy)
- return -ENOMEM;
+ if (!mphy) {
+ ret = -ENOMEM;
+ goto error;
+ }
phy = mphy->priv;
phy->dev = dev;
@@ -572,7 +574,7 @@ int mt7615_register_ext_phy(struct mt7615_dev *dev)
ret = mt76_eeprom_override(mphy);
if (ret)
- return ret;
+ goto error_free_hw;
/* second phy can only handle 5 GHz */
mphy->cap.has_5ghz = true;
@@ -590,7 +592,14 @@ int mt7615_register_ext_phy(struct mt7615_dev *dev)
ret = mt76_register_phy(mphy, true, mt76_rates,
ARRAY_SIZE(mt76_rates));
if (ret)
- ieee80211_free_hw(mphy->hw);
+ goto error_free_hw;
+
+ return 0;
+
+error_free_hw:
+ ieee80211_free_hw(mphy->hw);
+error:
+ mt7615_cap_dbdc_disable(dev);
return ret;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
index 25936fb309fb..a6667e3a66d0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
@@ -254,8 +254,10 @@ static void mt7615_remove_interface(struct ieee80211_hw *hw,
mt7615_mutex_acquire(dev);
- mt7615_mcu_add_bss_info(phy, vif, NULL, false);
- mt7615_mcu_sta_add(phy, vif, NULL, false);
+ if (vif->type != NL80211_IFTYPE_MONITOR) {
+ mt7615_mcu_add_bss_info(phy, vif, NULL, false);
+ mt7615_mcu_sta_add(phy, vif, NULL, false);
+ }
mt76_testmode_reset(phy->mt76, true);
if (vif == phy->monitor_vif)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c
index f607eee3fb47..5e09f43926e0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c
@@ -116,6 +116,7 @@ void mt7615_unregister_device(struct mt7615_dev *dev)
mcu_running = mt7615_wait_for_mcu_init(dev);
mt7615_unregister_ext_phy(dev);
+ cancel_work_sync(&dev->reset_work);
mt76_unregister_device(&dev->mt76);
if (mcu_running)
mt7615_mcu_exit(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
index 17c978291531..eef131c05f76 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
@@ -302,10 +302,14 @@ mt76x02_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate,
txrate->flags |= IEEE80211_TX_RC_GREEN_FIELD;
fallthrough;
case MT_PHY_TYPE_HT:
+ if (idx > 31)
+ return -EINVAL;
txrate->flags |= IEEE80211_TX_RC_MCS;
txrate->idx = idx;
break;
case MT_PHY_TYPE_VHT:
+ if ((idx & 0xf) > 9)
+ return -EINVAL;
txrate->flags |= IEEE80211_TX_RC_VHT_MCS;
txrate->idx = idx;
break;
@@ -493,18 +497,21 @@ mt76x02_mac_fill_tx_status(struct mt76x02_dev *dev, struct mt76x02_sta *msta,
first_rate = st->rate & ~MT_PKTID_RATE;
first_rate |= st->pktid & MT_PKTID_RATE;
- mt76x02_mac_process_tx_rate(&rate[0], first_rate,
- dev->mphy.chandef.chan->band);
+ if (mt76x02_mac_process_tx_rate(&rate[0], first_rate,
+ dev->mphy.chandef.chan->band))
+ return;
} else if (rate[0].idx < 0) {
if (!msta)
return;
- mt76x02_mac_process_tx_rate(&rate[0], msta->wcid.tx_info,
- dev->mphy.chandef.chan->band);
+ if (mt76x02_mac_process_tx_rate(&rate[0], msta->wcid.tx_info,
+ dev->mphy.chandef.chan->band))
+ return;
}
- mt76x02_mac_process_tx_rate(&last_rate, st->rate,
- dev->mphy.chandef.chan->band);
+ if (mt76x02_mac_process_tx_rate(&last_rate, st->rate,
+ dev->mphy.chandef.chan->band))
+ return;
for (i = 0; i < ARRAY_SIZE(info->status.rates); i++) {
retry--;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c
index b2cc44914294..d06cc5ee07b2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c
@@ -90,7 +90,7 @@ __mt76x02u_mcu_send_msg(struct mt76_dev *dev, struct sk_buff *skb,
MT_MCU_MSG_TYPE_CMD;
ret = mt76x02u_skb_dma_info(skb, CPU_TX_PORT, info);
if (ret)
- return ret;
+ goto out;
ret = mt76u_bulk_msg(dev, skb->data, skb->len, NULL, 500,
MT_EP_OUT_INBAND_CMD);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
index 95047ee48a42..0ad86526dddc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
@@ -132,6 +132,15 @@ static int mt7925u_mac_reset(struct mt792x_dev *dev)
return err;
}
+static void mt7925u_stop(struct ieee80211_hw *hw, bool suspend)
+{
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+
+ cancel_delayed_work_sync(&dev->mlo_pm_work);
+
+ mt792xu_stop(hw, suspend);
+}
+
static int mt7925u_probe(struct usb_interface *usb_intf,
const struct usb_device_id *id)
{
@@ -178,7 +187,7 @@ static int mt7925u_probe(struct usb_interface *usb_intf,
if (!ops)
return -ENOMEM;
- ops->stop = mt792xu_stop;
+ ops->stop = mt7925u_stop;
mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops);
if (!mdev)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
index 3f49bbbba3b9..1d9102b5e556 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
@@ -63,12 +63,18 @@ static void mt7996_put_hif2(struct mt7996_hif *hif)
static struct mt7996_hif *mt7996_pci_init_hif2(struct pci_dev *pdev)
{
+ struct pci_dev *tmp_pdev;
+
hif_idx++;
- if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID_2, NULL) &&
- !pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID_2, NULL) &&
- !pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID_2, NULL))
+ tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID_2, NULL);
+ if (!tmp_pdev)
+ tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID_2, NULL);
+ if (!tmp_pdev)
+ tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID_2, NULL);
+ if (!tmp_pdev)
return NULL;
+ pci_dev_put(tmp_pdev);
writel(hif_idx | MT_PCIE_RECOG_ID_SEM,
pcim_iomap_table(pdev)[0] + MT_PCIE_RECOG_ID);
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 93275bb692fa..3fef2ad45dee 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -885,7 +885,7 @@ int mt76_skb_adjust_pad(struct sk_buff *skb, int pad)
}
}
- if (skb_pad(last, pad))
+ if (__skb_pad(last, pad, false))
return -ENOMEM;
__skb_put(last, pad);
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index f9e67b8c3b3c..b39dbf22dfc1 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -854,6 +854,9 @@ mt76u_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
enum mt76_txq_id qid, struct sk_buff *skb,
struct mt76_wcid *wcid, struct ieee80211_sta *sta)
{
+ struct ieee80211_tx_status status = {
+ .sta = sta,
+ };
struct mt76_tx_info tx_info = {
.skb = skb,
};
@@ -861,17 +864,27 @@ mt76u_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
u16 idx = q->head;
int err;
- if (q->queued == q->ndesc)
- return -ENOSPC;
+ if (q->queued == q->ndesc) {
+ err = -ENOSPC;
+ goto err_free_skb;
+ }
skb->prev = skb->next = NULL;
err = dev->drv->tx_prepare_skb(dev, NULL, qid, wcid, sta, &tx_info);
if (err < 0)
- return err;
+ goto err_free_skb;
err = mt76u_tx_setup_buffers(dev, tx_info.skb, q->entry[idx].urb);
- if (err < 0)
- return err;
+ if (err < 0) {
+ /*
+ * mt76_tx_status_skb_get() walks the idr and dereferences
+ * a freed skb. This SKB is not counted in non-AQL counter
+ * due to error return, so using 0xffff as wcid to keep
+ * balanced.
+ */
+ mt76_tx_complete_skb(dev, 0xffff, tx_info.skb);
+ goto err_ret;
+ }
mt76u_fill_bulk_urb(dev, USB_DIR_OUT, q->ep, q->entry[idx].urb,
mt76u_complete_tx, &q->entry[idx]);
@@ -882,6 +895,14 @@ mt76u_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
q->queued++;
return idx;
+
+err_free_skb:
+ status.skb = tx_info.skb;
+ spin_lock_bh(&dev->rx_lock);
+ ieee80211_tx_status_ext(dev->hw, &status);
+ spin_unlock_bh(&dev->rx_lock);
+err_ret:
+ return err;
}
static void mt76u_tx_kick(struct mt76_dev *dev, struct mt76_queue *q)
diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c
index 766f22d31079..3b6302f14de0 100644
--- a/drivers/net/wireless/realtek/rtw88/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
@@ -597,7 +597,7 @@ static int rtw_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
hw_key_idx = key->keyidx;
}
- if (hw_key_idx > sec->total_cam_num) {
+ if (hw_key_idx >= sec->total_cam_num) {
ret = -ENOSPC;
goto out;
}
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 299526220380..c4fb9c226fae 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -156,8 +156,24 @@ static int rtw89_fw_hdr_parser_v0(struct rtw89_dev *rtwdev, const u8 *fw, u32 le
if (!info)
return -EINVAL;
+ if (len < sizeof(*fw_hdr)) {
+ rtw89_err(rtwdev, "[ERR]fw header truncated\n");
+ return -EINVAL;
+ }
+
info->section_num = le32_get_bits(fw_hdr->w6, FW_HDR_W6_SEC_NUM);
+ if (!info->section_num || info->section_num > FWDL_SECTION_MAX_NUM) {
+ rtw89_err(rtwdev, "[ERR]invalid fw section num %u\n",
+ info->section_num);
+ return -EINVAL;
+ }
+
base_hdr_len = struct_size(fw_hdr, sections, info->section_num);
+ if (base_hdr_len > len) {
+ rtw89_err(rtwdev, "[ERR]fw header truncated\n");
+ return -EINVAL;
+ }
+
info->dynamic_hdr_en = le32_get_bits(fw_hdr->w7, FW_HDR_W7_DYN_HDR);
info->idmem_share_mode = le32_get_bits(fw_hdr->w7, FW_HDR_W7_IDMEM_SHARE_MODE);
@@ -449,9 +465,25 @@ static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 le
int ret;
u32 i;
+ if (len < sizeof(*fw_hdr)) {
+ rtw89_err(rtwdev, "[ERR]fw header truncated\n");
+ return -EINVAL;
+ }
+
info->section_num = le32_get_bits(fw_hdr->w6, FW_HDR_V1_W6_SEC_NUM);
+ if (!info->section_num || info->section_num > FWDL_SECTION_MAX_NUM) {
+ rtw89_err(rtwdev, "[ERR]invalid fw section num %u\n",
+ info->section_num);
+ return -EINVAL;
+ }
+
info->dsp_checksum = le32_get_bits(fw_hdr->w6, FW_HDR_V1_W6_DSP_CHKSUM);
base_hdr_len = struct_size(fw_hdr, sections, info->section_num);
+ if (base_hdr_len > len) {
+ rtw89_err(rtwdev, "[ERR]fw header truncated\n");
+ return -EINVAL;
+ }
+
info->dynamic_hdr_en = le32_get_bits(fw_hdr->w7, FW_HDR_V1_W7_DYN_HDR);
info->idmem_share_mode = le32_get_bits(fw_hdr->w7, FW_HDR_V1_W7_IDMEM_SHARE_MODE);
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c
index 84c46d2f4d85..6d2382cb5cd8 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c
@@ -1895,8 +1895,8 @@ static void _dpk_information(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
u8 kidx = dpk->cur_idx[path];
dpk->bp[path][kidx].band = chan->band_type;
- dpk->bp[path][kidx].ch = chan->band_width;
- dpk->bp[path][kidx].bw = chan->channel;
+ dpk->bp[path][kidx].ch = chan->channel;
+ dpk->bp[path][kidx].bw = chan->band_width;
rtw89_debug(rtwdev, RTW89_DBG_RFK,
"[DPK] S%d[%d] (PHY%d): TSSI %s/ DBCC %s/ %s/ CH%d/ %s\n",
diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c
index 90046d6f620e..144c4c2801bd 100644
--- a/drivers/net/wireless/realtek/rtw89/wow.c
+++ b/drivers/net/wireless/realtek/rtw89/wow.c
@@ -16,10 +16,14 @@ void __rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb)
{
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
struct rtw89_wow_param *rtw_wow = &rtwdev->wow;
+ size_t hdr_len = offsetof(struct ieee80211_mgmt, u.assoc_req.variable);
const u8 *rsn, *ies = mgmt->u.assoc_req.variable;
struct rtw89_rsn_ie *rsn_ie;
- rsn = cfg80211_find_ie(WLAN_EID_RSN, ies, skb->len);
+ if (skb->len < hdr_len)
+ return;
+
+ rsn = cfg80211_find_ie(WLAN_EID_RSN, ies, skb->len - hdr_len);
if (!rsn)
return;
diff --git a/drivers/net/wwan/iosm/iosm_ipc_protocol_ops.c b/drivers/net/wwan/iosm/iosm_ipc_protocol_ops.c
index 4627847c6daa..455b3b6f0991 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_protocol_ops.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_protocol_ops.c
@@ -14,6 +14,11 @@ ipc_protocol_free_msg_get(struct iosm_protocol *ipc_protocol, int *index)
u32 new_head = (head + 1) % IPC_MEM_MSG_ENTRIES;
union ipc_mem_msg_entry *msg;
+ if (head >= IPC_MEM_MSG_ENTRIES) {
+ dev_err(ipc_protocol->dev, "invalid message head %u", head);
+ return NULL;
+ }
+
if (new_head == le32_to_cpu(ipc_protocol->p_ap_shm->msg_tail)) {
dev_err(ipc_protocol->dev, "message ring is full");
return NULL;
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index c759ebc56457..e3f126897cfc 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -1530,12 +1530,17 @@ static inline bool tx_dealloc_work_todo(struct xenvif_queue *queue)
void xenvif_unmap_frontend_data_rings(struct xenvif_queue *queue)
{
- if (queue->tx.sring)
+ if (queue->tx.sring) {
xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(queue->vif),
queue->tx.sring);
- if (queue->rx.sring)
+ queue->tx.sring = NULL;
+ }
+
+ if (queue->rx.sring) {
xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(queue->vif),
queue->rx.sring);
+ queue->rx.sring = NULL;
+ }
}
int xenvif_map_frontend_data_rings(struct xenvif_queue *queue,
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
index aca25db2503b..e4405a6c6e70 100644
--- a/drivers/net/xen-netback/xenbus.c
+++ b/drivers/net/xen-netback/xenbus.c
@@ -731,6 +731,7 @@ static void connect(struct backend_info *be)
unsigned int queue_index;
unsigned int requested_num_queues;
struct xenvif_queue *queue;
+ unsigned int num_queues;
/* Check whether the frontend requested multiple queues
* and read the number requested.
@@ -837,14 +838,15 @@ static void connect(struct backend_info *be)
return;
err:
- if (be->vif->num_queues > 0)
+ num_queues = be->vif->num_queues;
+ if (num_queues > 0)
xenvif_disconnect_data(be->vif); /* Clean up existing queues */
+ be->vif->num_queues = 0;
synchronize_net();
- for (queue_index = 0; queue_index < be->vif->num_queues; ++queue_index)
+ for (queue_index = 0; queue_index < num_queues; ++queue_index)
xenvif_deinit_queue(&be->vif->queues[queue_index]);
vfree(be->vif->queues);
be->vif->queues = NULL;
- be->vif->num_queues = 0;
xenvif_disconnect_ctrl(be->vif);
return;
}
diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
index 9d73713d6f82..38506314248f 100644
--- a/drivers/nvme/host/auth.c
+++ b/drivers/nvme/host/auth.c
@@ -378,8 +378,21 @@ static int nvme_auth_process_dhchap_success1(struct nvme_ctrl *ctrl,
nvme_auth_hmac_name(chap->hash_id),
nvme_auth_dhgroup_name(chap->dhgroup_id));
- if (!data->rvalid)
+ if (!data->rvalid) {
+ /*
+ * The controller did not return a response. If we asked it to
+ * authenticate itself the session must not be used, otherwise
+ * mutual authentication would silently degrade to one-way.
+ */
+ if (chap->bi_directional) {
+ dev_warn(ctrl->device,
+ "qid %d: controller did not authenticate\n",
+ chap->qid);
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
+ return -ECONNREFUSED;
+ }
return 0;
+ }
/* Validate controller response */
if (crypto_memneq(chap->response, data->rval, data->hl)) {
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index ad725e346c94..99a8e2953b02 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -2210,13 +2210,18 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl,
}
size = le32_to_cpu(hdr.sze);
+ if (size < sizeof(*h) + sizeof(*desc)) {
+ dev_warn(ctrl->device, "FDP config size too small:%zu\n",
+ size);
+ return 0;
+ }
if (size > PAGE_SIZE * MAX_ORDER_NR_PAGES) {
dev_warn(ctrl->device, "FDP config size too large:%zu\n",
size);
return 0;
}
- h = kvmalloc(size, GFP_KERNEL);
+ h = kvzalloc(size, GFP_KERNEL);
if (!h)
return -ENOMEM;
@@ -2242,9 +2247,9 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl,
desc = log;
end = log + size - sizeof(*h);
for (i = 0; i < fdp_idx; i++) {
- u16 dsze = le16_to_cpu(desc->dsze);
+ u16 dsze = get_unaligned_le16(&desc->dsze);
- if (!dsze || log + dsze > end) {
+ if (dsze < sizeof(*desc) || log + dsze + sizeof(*desc) > end) {
dev_warn(ctrl->device,
"FDP invalid config descriptor at index %d\n", i);
ret = 0;
@@ -2254,13 +2259,13 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl,
desc = log;
}
- if (le32_to_cpu(desc->nrg) > 1) {
+ if (get_unaligned_le32(&desc->nrg) > 1) {
dev_warn(ctrl->device, "FDP NRG > 1 not supported\n");
ret = 0;
goto out;
}
- info->runs = le64_to_cpu(desc->runs);
+ info->runs = get_unaligned_le64(&desc->runs);
out:
kvfree(h);
return ret;
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 19a9dd7caed8..dbaf503b7de9 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -1713,6 +1713,7 @@ static void nvme_rdma_process_nvme_rsp(struct nvme_rdma_queue *queue,
"Bogus remote invalidation for rkey %#x\n",
req->mr ? req->mr->rkey : 0);
nvme_rdma_error_recovery(queue->ctrl);
+ return;
}
} else if (req->mr) {
int ret;
diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c
index 12478e029735..2b6085359a95 100644
--- a/drivers/nvme/host/sysfs.c
+++ b/drivers/nvme/host/sysfs.c
@@ -626,7 +626,7 @@ static ssize_t quirks_show(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_RO(quirks);
#ifdef CONFIG_NVME_HOST_AUTH
-static ssize_t nvme_ctrl_dhchap_secret_show(struct device *dev,
+static ssize_t dhchap_secret_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
@@ -637,7 +637,7 @@ static ssize_t nvme_ctrl_dhchap_secret_show(struct device *dev,
return sysfs_emit(buf, "%s\n", opts->dhchap_secret);
}
-static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev,
+static ssize_t dhchap_secret_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
@@ -681,10 +681,9 @@ static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(dhchap_secret, S_IRUGO | S_IWUSR,
- nvme_ctrl_dhchap_secret_show, nvme_ctrl_dhchap_secret_store);
+static DEVICE_ATTR_ADMIN_RW(dhchap_secret);
-static ssize_t nvme_ctrl_dhchap_ctrl_secret_show(struct device *dev,
+static ssize_t dhchap_ctrl_secret_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
@@ -695,7 +694,7 @@ static ssize_t nvme_ctrl_dhchap_ctrl_secret_show(struct device *dev,
return sysfs_emit(buf, "%s\n", opts->dhchap_ctrl_secret);
}
-static ssize_t nvme_ctrl_dhchap_ctrl_secret_store(struct device *dev,
+static ssize_t dhchap_ctrl_secret_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
@@ -739,8 +738,7 @@ static ssize_t nvme_ctrl_dhchap_ctrl_secret_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(dhchap_ctrl_secret, S_IRUGO | S_IWUSR,
- nvme_ctrl_dhchap_ctrl_secret_show, nvme_ctrl_dhchap_ctrl_secret_store);
+static DEVICE_ATTR_ADMIN_RW(dhchap_ctrl_secret);
#endif
static struct attribute *nvme_dev_attrs[] = {
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index b7e4624f1dcd..94495b022871 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -1425,6 +1425,19 @@ static int nvme_tcp_try_recv(struct nvme_tcp_queue *queue)
lock_sock(sk);
queue->nr_cqe = 0;
consumed = sock->ops->read_sock(sk, &rd_desc, nvme_tcp_recv_skb);
+ /*
+ * nvme_tcp_recv_skb() handles its own errors, so a queue that is
+ * still enabled failed inside read_sock(). Handle it before
+ * release_sock() processes the backlog.
+ */
+ if (unlikely(consumed < 0 && consumed != -EAGAIN &&
+ queue->rd_enabled)) {
+ dev_err(queue->ctrl->ctrl.device,
+ "queue %d: receive failed: %d\n",
+ nvme_tcp_queue_id(queue), consumed);
+ queue->rd_enabled = false;
+ nvme_tcp_error_recovery(&queue->ctrl->ctrl);
+ }
release_sock(sk);
return consumed == -EAGAIN ? 0 : consumed;
}
@@ -1437,9 +1450,11 @@ static void nvme_tcp_io_work(struct work_struct *w)
do {
bool pending = false;
+ bool got_send_mutex = false;
int result;
if (mutex_trylock(&queue->send_mutex)) {
+ got_send_mutex = true;
result = nvme_tcp_try_send(queue);
mutex_unlock(&queue->send_mutex);
if (result > 0)
@@ -1454,8 +1469,13 @@ static void nvme_tcp_io_work(struct work_struct *w)
else if (unlikely(result < 0))
return;
- /* did we get some space after spending time in recv? */
- if (nvme_tcp_queue_has_pending(queue) &&
+ /*
+ * did we get some space after spending time in recv?
+ * Only check this if we could take send_mutex. Otherwise
+ * the task holding it sends the requests and queues io_work
+ * again after it releases the mutex.
+ */
+ if (got_send_mutex && nvme_tcp_queue_has_pending(queue) &&
sk_stream_is_writeable(queue->sock->sk))
pending = true;
diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index fa9920787a8f..623b5303de2a 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -1606,7 +1606,7 @@ void nvmet_execute_keep_alive(struct nvmet_req *req)
pr_debug("ctrl %d update keep-alive timer for %d secs\n",
ctrl->cntlid, ctrl->kato);
- mod_delayed_work(system_wq, &ctrl->ka_work, ctrl->kato * HZ);
+ mod_delayed_work(system_percpu_wq, &ctrl->ka_work, ctrl->kato * HZ);
out:
nvmet_req_complete(req, status);
}
diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
index 8230a0563616..eb2b6c7a8083 100644
--- a/drivers/nvme/target/auth.c
+++ b/drivers/nvme/target/auth.c
@@ -238,6 +238,7 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, bool reset)
void nvmet_auth_sq_free(struct nvmet_sq *sq)
{
+ sq->auth_expiry_active = false;
cancel_delayed_work(&sq->auth_expired_work);
#ifdef CONFIG_NVME_TARGET_TCP_TLS
sq->tls_key = NULL;
@@ -248,6 +249,7 @@ void nvmet_auth_sq_free(struct nvmet_sq *sq)
sq->dhchap_c2 = NULL;
kfree(sq->dhchap_skey);
sq->dhchap_skey = NULL;
+ sq->dhchap_skey_len = 0;
}
void nvmet_auth_sq_destroy(struct nvmet_sq *sq)
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index f6ac42eaac0e..e2032b58191c 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -1662,8 +1662,10 @@ static ssize_t nvmet_subsys_attr_qid_max_store(struct config_item *item,
subsys->max_qid = qid_max;
/* Force reconnect */
+ mutex_lock(&subsys->lock);
list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry)
ctrl->ops->delete_ctrl(ctrl);
+ mutex_unlock(&subsys->lock);
up_write(&nvmet_config_sem);
return cnt;
diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
index 5b010318286e..61a36fb54e87 100644
--- a/drivers/nvme/target/fabrics-cmd-auth.c
+++ b/drivers/nvme/target/fabrics-cmd-auth.c
@@ -6,6 +6,7 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/blkdev.h>
+#include <linux/jiffies.h>
#include <linux/random.h>
#include <linux/nvme-auth.h>
#include <crypto/hash.h>
@@ -18,16 +19,27 @@ static void nvmet_auth_expired_work(struct work_struct *work)
struct nvmet_sq *sq = container_of(to_delayed_work(work),
struct nvmet_sq, auth_expired_work);
+ mutex_lock(&sq->auth_lock);
+ /* A command may have cancelled or rearmed this timeout. */
+ if (!sq->auth_expiry_active ||
+ delayed_work_pending(&sq->auth_expired_work))
+ goto out_unlock;
+ sq->auth_expiry_active = false;
+
pr_debug("%s: ctrl %d qid %d transaction %u expired, resetting\n",
__func__, sq->ctrl->cntlid, sq->qid, sq->dhchap_tid);
sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
sq->dhchap_tid = -1;
+out_unlock:
+ mutex_unlock(&sq->auth_lock);
}
void nvmet_auth_sq_init(struct nvmet_sq *sq)
{
/* Initialize in-band authentication */
+ mutex_init(&sq->auth_lock);
INIT_DELAYED_WORK(&sq->auth_expired_work, nvmet_auth_expired_work);
+ sq->auth_expiry_active = false;
sq->authenticated = false;
sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
}
@@ -258,6 +270,8 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
u16 status = 0;
u8 dhchap_status;
+ mutex_lock(&req->sq->auth_lock);
+
if (req->cmd->auth_send.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
req->error_loc =
@@ -283,8 +297,10 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
offsetof(struct nvmf_auth_send_command, tl);
goto done;
}
- if (!nvmet_check_transfer_len(req, tl)) {
+ if (unlikely(tl != req->transfer_len)) {
pr_debug("%s: transfer length mismatch (%u)\n", __func__, tl);
+ mutex_unlock(&req->sq->auth_lock);
+ nvmet_check_transfer_len(req, tl);
return;
}
@@ -310,6 +326,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
/* Restart negotiation */
pr_debug("%s: ctrl %d qid %d reset negotiation\n",
__func__, ctrl->cntlid, req->sq->qid);
+ nvmet_auth_sq_free(req->sq);
if (!req->sq->qid) {
dhchap_status = nvmet_setup_auth(ctrl, req->sq,
true);
@@ -408,10 +425,12 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
status, req->error_loc);
if (req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 &&
req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) {
- unsigned long auth_expire_secs = ctrl->kato ? ctrl->kato : 120;
+ unsigned long auth_expire = min_t(u64, MAX_JIFFY_OFFSET,
+ (u64)(ctrl->kato ? ctrl->kato : 120) * HZ);
- mod_delayed_work(system_wq, &req->sq->auth_expired_work,
- auth_expire_secs * HZ);
+ req->sq->auth_expiry_active = true;
+ mod_delayed_work(system_percpu_wq, &req->sq->auth_expired_work,
+ auth_expire);
goto complete;
}
/* Final states, clear up variables */
@@ -420,6 +439,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
nvmet_ctrl_fatal_error(ctrl);
complete:
+ mutex_unlock(&req->sq->auth_lock);
nvmet_req_complete(req, status);
}
@@ -538,6 +558,8 @@ void nvmet_execute_auth_receive(struct nvmet_req *req)
u32 al;
u16 status = 0;
+ mutex_lock(&req->sq->auth_lock);
+
if (req->cmd->auth_receive.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
req->error_loc =
@@ -563,8 +585,10 @@ void nvmet_execute_auth_receive(struct nvmet_req *req)
offsetof(struct nvmf_auth_receive_command, al);
goto done;
}
- if (!nvmet_check_transfer_len(req, al)) {
+ if (unlikely(al != req->transfer_len)) {
pr_debug("%s: transfer length mismatch (%u)\n", __func__, al);
+ mutex_unlock(&req->sq->auth_lock);
+ nvmet_check_transfer_len(req, al);
return;
}
@@ -578,10 +602,11 @@ void nvmet_execute_auth_receive(struct nvmet_req *req)
switch (req->sq->dhchap_step) {
case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE:
if (nvmet_auth_challenge(req, d, al) < 0) {
- pr_warn("ctrl %d qid %d: challenge error (%d)\n",
- ctrl->cntlid, req->sq->qid, status);
+ pr_warn("ctrl %d qid %d: challenge error\n",
+ ctrl->cntlid, req->sq->qid);
status = NVME_SC_INTERNAL;
- break;
+ kfree(d);
+ goto done;
}
req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
break;
@@ -623,5 +648,6 @@ void nvmet_execute_auth_receive(struct nvmet_req *req)
nvmet_auth_sq_free(req->sq);
nvmet_ctrl_fatal_error(ctrl);
}
+ mutex_unlock(&req->sq->auth_lock);
nvmet_req_complete(req, status);
}
diff --git a/drivers/nvme/target/fabrics-cmd.c b/drivers/nvme/target/fabrics-cmd.c
index bc837fbfada1..9aedab2603bd 100644
--- a/drivers/nvme/target/fabrics-cmd.c
+++ b/drivers/nvme/target/fabrics-cmd.c
@@ -14,6 +14,10 @@ static void nvmet_execute_prop_set(struct nvmet_req *req)
if (!nvmet_check_transfer_len(req, 0))
return;
+ if (!nvmet_check_auth_status(req)) {
+ status = NVME_SC_AUTH_REQUIRED | NVME_STATUS_DNR;
+ goto out;
+ }
if (req->cmd->prop_set.attrib & 1) {
req->error_loc =
diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c
index 1880fc780432..89edffeb1062 100644
--- a/drivers/nvme/target/fc.c
+++ b/drivers/nvme/target/fc.c
@@ -1710,7 +1710,7 @@ nvmet_fc_ls_create_association(struct nvmet_fc_tgtport *tgtport,
be16_to_cpu(rqst->assoc_cmd.sqsize));
if (!queue) {
ret = VERR_QUEUE_ALLOC_FAIL;
- nvmet_fc_tgt_a_put(iod->assoc);
+ nvmet_fc_schedule_delete_assoc(iod->assoc);
}
}
}
@@ -2493,11 +2493,11 @@ __nvmet_fc_fcp_nvme_cmd_done(struct nvmet_fc_tgtport *tgtport,
} else {
/*
- * try to push the data even if the SQE status is non-zero.
- * There may be a status where data still was intended to
- * be moved
+ * A failed command may not have filled its read buffer, so
+ * only push data for success, as the tcp and rdma targets do.
*/
- if ((fod->io_dir == NVMET_FCP_READ) && (fod->data_sg_cnt)) {
+ if (fod->io_dir == NVMET_FCP_READ && fod->data_sg_cnt &&
+ !cqe->status) {
/* push the data over before sending rsp */
nvmet_fc_transfer_fcp_data(tgtport, fod,
NVMET_FCOP_READDATA);
diff --git a/drivers/nvme/target/io-cmd-bdev.c b/drivers/nvme/target/io-cmd-bdev.c
index 0103815542d4..160364cc8c98 100644
--- a/drivers/nvme/target/io-cmd-bdev.c
+++ b/drivers/nvme/target/io-cmd-bdev.c
@@ -305,7 +305,7 @@ static void nvmet_bdev_execute_rw(struct nvmet_req *req)
&prot_miter);
if (unlikely(rc)) {
bio_io_error(bio);
- return;
+ goto out_plug;
}
}
@@ -325,11 +325,12 @@ static void nvmet_bdev_execute_rw(struct nvmet_req *req)
rc = nvmet_bdev_alloc_bip(req, bio, &prot_miter);
if (unlikely(rc)) {
bio_io_error(bio);
- return;
+ goto out_plug;
}
}
submit_bio(bio);
+out_plug:
blk_finish_plug(&plug);
}
diff --git a/drivers/nvme/target/io-cmd-file.c b/drivers/nvme/target/io-cmd-file.c
index 2d068439b129..6e1470016472 100644
--- a/drivers/nvme/target/io-cmd-file.c
+++ b/drivers/nvme/target/io-cmd-file.c
@@ -133,8 +133,8 @@ static bool nvmet_file_execute_io(struct nvmet_req *req, int ki_flags)
pos = le64_to_cpu(req->cmd->rw.slba) << req->ns->blksize_shift;
if (unlikely(pos + req->transfer_len > req->ns->size)) {
- nvmet_req_complete(req, errno_to_nvme_status(req, -ENOSPC));
- return true;
+ ret = -ENOSPC;
+ goto complete;
}
memset(&req->f.iocb, 0, sizeof(struct kiocb));
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 87e2fdbd1865..45fc188412d2 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -156,8 +156,10 @@ struct nvmet_sq {
u32 sqhd;
bool sqhd_disabled;
#ifdef CONFIG_NVME_TARGET_AUTH
+ struct mutex auth_lock;
bool authenticated;
struct delayed_work auth_expired_work;
+ bool auth_expiry_active;
u16 dhchap_tid;
u8 sc_c;
u8 dhchap_status;
diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
index e3815be98ee6..dcc6cae6f3c5 100644
--- a/drivers/nvme/target/pci-epf.c
+++ b/drivers/nvme/target/pci-epf.c
@@ -1095,10 +1095,22 @@ static int nvmet_pci_epf_iod_parse_sgl_segments(struct nvmet_pci_epf_ctrl *ctrl,
return 0;
}
+static u64 nvmet_pci_epf_iod_data_segs_len(struct nvmet_pci_epf_iod *iod)
+{
+ u64 len = 0;
+ int i;
+
+ for (i = 0; i < iod->nr_data_segs; i++)
+ len += iod->data_segs[i].length;
+
+ return len;
+}
+
static int nvmet_pci_epf_iod_parse_sgls(struct nvmet_pci_epf_iod *iod)
{
struct nvmet_pci_epf_ctrl *ctrl = iod->ctrl;
struct nvme_sgl_desc *sgl = &iod->cmd.common.dptr.sgl;
+ int ret;
if (sgl->type == (NVME_SGL_FMT_DATA_DESC << 4)) {
/* Single data descriptor case. */
@@ -1106,10 +1118,23 @@ static int nvmet_pci_epf_iod_parse_sgls(struct nvmet_pci_epf_iod *iod)
iod->data_segs = &iod->data_seg;
iod->data_seg.pci_addr = le64_to_cpu(sgl->addr);
iod->data_seg.length = le32_to_cpu(sgl->length);
- return 0;
+ } else {
+ ret = nvmet_pci_epf_iod_parse_sgl_segments(ctrl, iod);
+ if (ret)
+ return ret;
}
- return nvmet_pci_epf_iod_parse_sgl_segments(ctrl, iod);
+ /*
+ * The command executes on data_len bytes, but each segment is
+ * allocated and transferred in full, so they must describe exactly
+ * data_len bytes.
+ */
+ if (nvmet_pci_epf_iod_data_segs_len(iod) != iod->data_len) {
+ iod->status = NVME_SC_SGL_INVALID_DATA | NVME_STATUS_DNR;
+ return -EINVAL;
+ }
+
+ return 0;
}
static int nvmet_pci_epf_alloc_iod_data_buf(struct nvmet_pci_epf_iod *iod)
@@ -1174,7 +1199,6 @@ static void nvmet_pci_epf_complete_iod(struct nvmet_pci_epf_iod *iod)
unsigned long flags;
/* Print an error message for failed commands, except AENs. */
- iod->status = le16_to_cpu(iod->cqe.status) >> 1;
if (iod->status && iod->cmd.common.opcode != nvme_admin_async_event)
dev_err(iod->ctrl->dev,
"CQ[%d]: Command %s (0x%x) status 0x%0x\n",
diff --git a/drivers/nvme/target/pr.c b/drivers/nvme/target/pr.c
index 9ab50f821ad6..0f696840b168 100644
--- a/drivers/nvme/target/pr.c
+++ b/drivers/nvme/target/pr.c
@@ -589,7 +589,7 @@ static u16 nvmet_pr_preempt(struct nvmet_req *req,
&ctrl->hostid, abort);
}
- if (holder == reg) {
+ if (holder == reg && prkey == holder->rkey) {
status = nvmet_pr_update_reg_attr(pr, holder,
nvmet_pr_update_holder_rtype, &rtype);
if (!status && original_rtype != rtype)
diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c
index 2b03bce27a88..a0ba6641c7ff 100644
--- a/drivers/nvme/target/rdma.c
+++ b/drivers/nvme/target/rdma.c
@@ -1410,7 +1410,7 @@ nvmet_rdma_parse_cm_connect_req(struct rdma_conn_param *conn,
struct nvme_rdma_cm_req *req;
req = (struct nvme_rdma_cm_req *)conn->private_data;
- if (!req || conn->private_data_len == 0)
+ if (!req || conn->private_data_len < sizeof(*req))
return NVME_RDMA_CM_INVALID_LEN;
if (le16_to_cpu(req->recfmt) != NVME_RDMA_CM_FMT_1_0)
diff --git a/drivers/nvme/target/zns.c b/drivers/nvme/target/zns.c
index 95af4b67108c..00f694ac3986 100644
--- a/drivers/nvme/target/zns.c
+++ b/drivers/nvme/target/zns.c
@@ -286,6 +286,11 @@ static void nvmet_bdev_zone_zmgmt_recv_work(struct work_struct *w)
if (status)
goto out;
+ /* Only the zone count and matching descriptors are written below. */
+ status = nvmet_zero_sgl(req, 0, out_bufsize);
+ if (status)
+ goto out;
+
if (!req_slba_nr_zones) {
status = NVME_SC_SUCCESS;
goto out;
@@ -314,6 +319,11 @@ static void nvmet_bdev_zone_zmgmt_recv_work(struct work_struct *w)
void nvmet_bdev_execute_zone_mgmt_recv(struct nvmet_req *req)
{
+ u32 out_bufsize = (le32_to_cpu(req->cmd->zmr.numd) + 1) << 2;
+
+ if (!nvmet_check_transfer_len(req, out_bufsize))
+ return;
+
INIT_WORK(&req->z.zmgmt_work, nvmet_bdev_zone_zmgmt_recv_work);
queue_work(zbd_wq, &req->z.zmgmt_work);
}
diff --git a/drivers/parisc/eisa_enumerator.c b/drivers/parisc/eisa_enumerator.c
index f0cb31198a8f..aa41d3abc6c2 100644
--- a/drivers/parisc/eisa_enumerator.c
+++ b/drivers/parisc/eisa_enumerator.c
@@ -311,6 +311,7 @@ static int configure_function(const unsigned char *buf, int *more)
static int parse_slot_config(int slot,
const unsigned char *buf,
+ unsigned int buflen,
struct eeprom_eisa_slot_info *es,
struct resource *io_parent,
struct resource *mem_parent)
@@ -333,8 +334,8 @@ static int parse_slot_config(int slot,
printk(KERN_INFO "EISA slot %d: %s %s ",
slot, board, es->flags&HPEE_FLAG_BOARD_IS_ISA ? "ISA" : "EISA");
- maxlen = es->config_data_length < HPEE_MAX_LENGTH ?
- es->config_data_length : HPEE_MAX_LENGTH;
+ /* never parse beyond the data that is actually in the buffer */
+ maxlen = min_t(unsigned int, es->config_data_length, buflen);
while ((pos < maxlen) && (num_func <= es->num_functions)) {
pos+=configure_function(buf+pos, &function_len);
@@ -491,6 +492,13 @@ int eisa_enumerator(unsigned long eeprom_addr,
printk(KERN_INFO "Enumerating EISA bus\n");
eh = (struct eeprom_header*)(eeprom_buf);
+ /*
+ * The EEPROM contents are not to be trusted: clamp the number of
+ * slots so that all slot records fit into eeprom_buf.
+ */
+ eh->num_slots = min_t(u8, eh->num_slots,
+ (HPEE_MAX_LENGTH - sizeof(*eh)) /
+ sizeof(struct eeprom_eisa_slot_info));
for (i=0;i<eh->num_slots;i++) {
struct eeprom_eisa_slot_info *es;
@@ -503,7 +511,8 @@ int eisa_enumerator(unsigned long eeprom_addr,
if (es->config_data_offset < HPEE_MAX_LENGTH) {
if (parse_slot_config(i+1, &eeprom_buf[es->config_data_offset],
- es, io_parent, mem_parent)) {
+ HPEE_MAX_LENGTH - es->config_data_offset,
+ es, io_parent, mem_parent)) {
return -1;
}
} else {
diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
index f1bc0ac81a92..72bb2a2dd238 100644
--- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
+++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
@@ -920,6 +920,8 @@ static void qcom_pcie_ep_remove(struct platform_device *pdev)
if (pcie_ep->link_status == QCOM_PCIE_EP_LINK_DISABLED)
return;
+ pci_epc_deinit_notify(pcie_ep->pci.ep.epc);
+ dw_pcie_ep_deinit(&pcie_ep->pci.ep);
qcom_pcie_disable_resources(pcie_ep);
}
diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c
index 799461335762..9ebc227a1ef8 100644
--- a/drivers/pci/controller/pcie-rockchip-ep.c
+++ b/drivers/pci/controller/pcie-rockchip-ep.c
@@ -553,19 +553,6 @@ static void rockchip_pcie_ep_link_training(struct work_struct *work)
if (ret)
goto again;
- /*
- * Check the current speed: if gen2 speed was requested and we are not
- * at gen2 speed yet, retrain again for gen2.
- */
- val = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL);
- if (!PCIE_LINK_IS_GEN2(val) && rockchip->link_gen == 2) {
- /* Enable retrain for gen2 */
- rockchip_pcie_ep_retrain_link(rockchip);
- readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL,
- val, PCIE_LINK_IS_GEN2(val), 50,
- LINK_TRAIN_TIMEOUT);
- }
-
/* Check again that the link is up */
if (!rockchip_pcie_ep_link_up(rockchip))
goto again;
diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c
index d203c4876d30..0b52f2e370f4 100644
--- a/drivers/pci/controller/pcie-rockchip-host.c
+++ b/drivers/pci/controller/pcie-rockchip-host.c
@@ -328,26 +328,6 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip)
goto err_power_off_phy;
}
- if (rockchip->link_gen == 2) {
- /*
- * Enable retrain for gen2. This should be configured only after
- * gen1 finished.
- */
- status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL2);
- status &= ~PCI_EXP_LNKCTL2_TLS;
- status |= PCI_EXP_LNKCTL2_TLS_5_0GT;
- rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL2);
- status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL);
- status |= PCI_EXP_LNKCTL_RL;
- rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL);
-
- err = readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL,
- status, PCIE_LINK_IS_GEN2(status), 20,
- 500 * USEC_PER_MSEC);
- if (err)
- dev_dbg(dev, "PCIe link training gen2 timeout, fall back to gen1!\n");
- }
-
/* Check the final link width from negotiated lane counter from MGMT */
status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL);
status = 0x1 << ((status & PCIE_CORE_PL_CONF_LANE_MASK) >>
diff --git a/drivers/pci/controller/pcie-rockchip.c b/drivers/pci/controller/pcie-rockchip.c
index 0f88da378805..456dcfd676ed 100644
--- a/drivers/pci/controller/pcie-rockchip.c
+++ b/drivers/pci/controller/pcie-rockchip.c
@@ -66,8 +66,10 @@ int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip)
}
rockchip->link_gen = of_pci_get_max_link_speed(node);
- if (rockchip->link_gen < 0 || rockchip->link_gen > 2)
- rockchip->link_gen = 2;
+ if (rockchip->link_gen < 0 || rockchip->link_gen >= 2) {
+ rockchip->link_gen = 1;
+ dev_warn(dev, "invalid max-link-speed, limited to 2.5 GT/s\n");
+ }
for (i = 0; i < ROCKCHIP_NUM_PM_RSTS; i++)
rockchip->pm_rsts[i].id = rockchip_pci_pm_rsts[i];
@@ -147,12 +149,8 @@ int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
goto err_exit_phy;
}
- if (rockchip->link_gen == 2)
- rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_2,
- PCIE_CLIENT_CONFIG);
- else
- rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_1,
- PCIE_CLIENT_CONFIG);
+ rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_1,
+ PCIE_CLIENT_CONFIG);
regs = PCIE_CLIENT_ARI_ENABLE |
PCIE_CLIENT_CONF_LANE_NUM(rockchip->lanes);
diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h
index 3e82a69b9c00..b5da15601b58 100644
--- a/drivers/pci/controller/pcie-rockchip.h
+++ b/drivers/pci/controller/pcie-rockchip.h
@@ -42,7 +42,6 @@
#define PCIE_CLIENT_MODE_RC HWORD_SET_BIT(0x0040)
#define PCIE_CLIENT_MODE_EP HWORD_CLR_BIT(0x0040)
#define PCIE_CLIENT_GEN_SEL_1 HWORD_CLR_BIT(0x0080)
-#define PCIE_CLIENT_GEN_SEL_2 HWORD_SET_BIT(0x0080)
#define PCIE_CLIENT_LEGACY_INT_CTRL (PCIE_CLIENT_BASE + 0x0c)
#define PCIE_CLIENT_INT_IN_ASSERT HWORD_SET_BIT(0x0002)
#define PCIE_CLIENT_INT_IN_DEASSERT HWORD_CLR_BIT(0x0002)
@@ -197,8 +196,6 @@
(((x) & PCIE_CORE_PL_CONF_LS_MASK) == PCIE_CORE_PL_CONF_LS_READY)
#define PCIE_LINK_UP(x) \
(((x) & PCIE_CLIENT_LINK_STATUS_MASK) == PCIE_CLIENT_LINK_STATUS_UP)
-#define PCIE_LINK_IS_GEN2(x) \
- (((x) & PCIE_CORE_PL_CONF_SPEED_MASK) == PCIE_CORE_PL_CONF_SPEED_5G)
#define RC_REGION_0_ADDR_TRANS_H 0x00000000
#define RC_REGION_0_ADDR_TRANS_L 0x00000000
diff --git a/drivers/pci/controller/pcie-xilinx-dma-pl.c b/drivers/pci/controller/pcie-xilinx-dma-pl.c
index b037c8f315e4..e18eeecdc8a9 100644
--- a/drivers/pci/controller/pcie-xilinx-dma-pl.c
+++ b/drivers/pci/controller/pcie-xilinx-dma-pl.c
@@ -581,7 +581,7 @@ static int xilinx_pl_dma_pcie_init_irq_domain(struct pl_dma_pcie *port)
port->pldma_domain = irq_domain_create_linear(of_fwnode_handle(pcie_intc_node), 32,
&event_domain_ops, port);
if (!port->pldma_domain)
- return -ENOMEM;
+ goto err_put_node;
irq_domain_update_bus_token(port->pldma_domain, DOMAIN_BUS_NEXUS);
@@ -589,7 +589,7 @@ static int xilinx_pl_dma_pcie_init_irq_domain(struct pl_dma_pcie *port)
&intx_domain_ops, port);
if (!port->intx_domain) {
dev_err(dev, "Failed to get a INTx IRQ domain\n");
- return -ENOMEM;
+ goto err_put_node;
}
irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
@@ -597,13 +597,17 @@ static int xilinx_pl_dma_pcie_init_irq_domain(struct pl_dma_pcie *port)
ret = xilinx_pl_dma_pcie_init_msi_irq_domain(port);
if (ret != 0) {
irq_domain_remove(port->intx_domain);
- return -ENOMEM;
+ goto err_put_node;
}
of_node_put(pcie_intc_node);
raw_spin_lock_init(&port->lock);
return 0;
+
+err_put_node:
+ of_node_put(pcie_intc_node);
+ return -ENOMEM;
}
static int xilinx_pl_dma_pcie_setup_irq(struct pl_dma_pcie *port)
diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c
index aae9a8a00406..3b0cc6db4402 100644
--- a/drivers/pci/doe.c
+++ b/drivers/pci/doe.c
@@ -210,8 +210,9 @@ static int pci_doe_sysfs_feature_populate(struct pci_dev *pdev,
pci_warn(pdev, "Failed adding %s to sysfs group\n",
attrs[i].attr.name);
goto fail;
- } else
- kfree(attrs[i].attr.name);
+ }
+ kfree(attrs[i].attr.name);
+ attrs[i].attr.name = NULL;
}
}
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index f59baa912970..785b7d5e7c09 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -38,7 +38,7 @@ int pciehp_poll_time;
* not really modular, but the easiest way to keep compat with existing
* bootargs behaviour is to continue using module_param here.
*/
-module_param(pciehp_poll_mode, bool, 0644);
+module_param(pciehp_poll_mode, bool, 0444);
module_param(pciehp_poll_time, int, 0644);
MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c
index 0c6446519640..c224619b9a26 100644
--- a/drivers/pci/pci-label.c
+++ b/drivers/pci/pci-label.c
@@ -144,7 +144,7 @@ static int dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf)
int len;
len = utf16s_to_utf8s((const wchar_t *)obj->buffer.pointer,
- obj->buffer.length,
+ obj->buffer.length / sizeof(wchar_t),
UTF16_LITTLE_ENDIAN,
buf, PAGE_SIZE - 1);
buf[len++] = '\n';
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index bab3213bafbd..25720f28518a 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3242,7 +3242,15 @@ void pci_pm_init(struct pci_dev *dev)
}
poweron:
- pci_pm_power_up_and_verify_state(dev);
+ pci_update_current_state(dev, PCI_UNKNOWN);
+ if (dev->current_state == PCI_D0) {
+ /* No need for full power-up, just sync-up platform state */
+ platform_pci_set_power_state(dev, PCI_D0);
+ } else {
+ /* Power up the device and restore BARs if necessary */
+ pci_set_full_power_state(dev, false);
+ }
+ pci_update_current_state(dev, PCI_D0);
pm_runtime_forbid(&dev->dev);
/*
@@ -5892,18 +5900,31 @@ static enum pci_bus_speed to_pcie_link_speed(u16 lnksta)
return pcie_link_speed[FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta)];
}
-int pcie_link_speed_mbps(struct pci_dev *pdev)
+/**
+ * pcie_link_bandwidth_mbps - get the current bandwidth of a single PCIe link
+ * @pdev: PCI device to query
+ *
+ * The bandwidth includes all negotiated lanes and does not account for
+ * encoding overhead. A negotiated width of zero gives zero bandwidth.
+ *
+ * Return: Bandwidth in Mb/s, or a negative errno on failure.
+ */
+int pcie_link_bandwidth_mbps(struct pci_dev *pdev)
{
u16 lnksta;
- int err;
+ int speed, err;
err = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta);
if (err)
- return err;
+ return pcibios_err_to_errno(err);
+
+ speed = pcie_dev_speed_mbps(to_pcie_link_speed(lnksta));
+ if (speed < 0)
+ return speed;
- return pcie_dev_speed_mbps(to_pcie_link_speed(lnksta));
+ return speed * FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);
}
-EXPORT_SYMBOL(pcie_link_speed_mbps);
+EXPORT_SYMBOL(pcie_link_bandwidth_mbps);
/**
* pcie_bandwidth_available - determine minimum link settings of a PCIe
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index 9f90abe3e88e..ad3fea4b56f8 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -1379,11 +1379,14 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
};
if (kfifo_in_spinlocked(&aer_recover_ring, &entry, 1,
- &aer_recover_ring_lock))
+ &aer_recover_ring_lock)) {
schedule_work(&aer_recover_work);
- else
+ } else {
pr_err("buffer overflow in recovery for %04x:%02x:%02x.%x\n",
domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+ ghes_estatus_pool_region_free((unsigned long)aer_regs,
+ sizeof(struct aer_capability_regs));
+ }
}
EXPORT_SYMBOL_GPL(aer_recover_queue);
#endif
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 4e4e38e62691..082c51ce8893 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1661,6 +1661,8 @@ void set_pcie_port_type(struct pci_dev *pdev)
pdev->aspm_l1_support = 1;
#endif
+ pdev->supported_speeds = pcie_get_supported_speeds(pdev);
+
parent = pci_upstream_bridge(pdev);
if (!parent)
return;
@@ -2046,9 +2048,6 @@ int pci_setup_device(struct pci_dev *dev)
set_pcie_untrusted(dev);
- if (pci_is_pcie(dev))
- dev->supported_speeds = pcie_get_supported_speeds(dev);
-
/* "Unknown power state" */
dev->current_state = PCI_UNKNOWN;
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index e03a3831bcc0..812b01d24fc4 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -103,7 +103,11 @@ int pcie_failed_link_retrain(struct pci_dev *dev)
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2);
- if (!(lnksta & PCI_EXP_LNKSTA_DLLLA) && pcie_lbms_seen(dev, lnksta)) {
+ if (lnksta & PCI_EXP_LNKSTA_DLLLA) {
+ ;
+ } else if (PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2) == PCIE_SPEED_2_5GT) {
+ return ret;
+ } else if (pcie_lbms_seen(dev, lnksta)) {
pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n");
ret = pcie_set_target_speed(dev, PCIE_SPEED_2_5GT, false);
if (ret)
@@ -1940,6 +1944,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4388, quirk_no_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4389, quirk_no_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x438a, quirk_no_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x438b, quirk_no_msi);
+/* ULi M1575 EHCI (10b9:5239): MSI stalls the async schedule */
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, 0x5239, quirk_no_msi);
static void quirk_pcie_mch(struct pci_dev *pdev)
{
@@ -2380,6 +2386,24 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
DECLARE_PCI_FIXUP_HEADER(0x1402, 0x2000, quirk_plx_pci9050);
DECLARE_PCI_FIXUP_HEADER(0x1402, 0x2600, quirk_plx_pci9050);
+/*
+ * An idle downstream port of a PLX/Broadcom PEX 8532/8533/8548 switch
+ * autosuspends into D3hot, and that takes the whole switch down with
+ * it -- any device behind another port of the same switch then sees a
+ * fatal, non-recoverable bus fault on its next access. Keep these
+ * ports out of D3 entirely.
+ */
+static void quirk_plx_pex_no_d3(struct pci_dev *dev)
+{
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
+ dev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
+ pci_info(dev, "avoiding D3 on this downstream port\n");
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_PLX, 0x8532, quirk_plx_pex_no_d3);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_PLX, 0x8533, quirk_plx_pex_no_d3);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_PLX, 0x8548, quirk_plx_pex_no_d3);
+
static void quirk_netmos(struct pci_dev *dev)
{
unsigned int num_parallel = (dev->subsystem_device & 0xf0) >> 4;
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index 11636634ae51..451476ce10ae 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -599,11 +599,16 @@ static pci_ers_result_t pcifront_common_process(int cmd,
case XEN_PCI_OP_aer_detected:
return pdrv->err_handler->error_detected(pcidev, state);
case XEN_PCI_OP_aer_mmio:
- return pdrv->err_handler->mmio_enabled(pcidev);
+ if (pdrv->err_handler->mmio_enabled)
+ return pdrv->err_handler->mmio_enabled(pcidev);
+ return PCI_ERS_RESULT_RECOVERED;
case XEN_PCI_OP_aer_slotreset:
- return pdrv->err_handler->slot_reset(pcidev);
+ if (pdrv->err_handler->slot_reset)
+ return pdrv->err_handler->slot_reset(pcidev);
+ return PCI_ERS_RESULT_RECOVERED;
case XEN_PCI_OP_aer_resume:
- pdrv->err_handler->resume(pcidev);
+ if (pdrv->err_handler->resume)
+ pdrv->err_handler->resume(pcidev);
return PCI_ERS_RESULT_NONE;
default:
dev_err(&pdev->xdev->dev,
diff --git a/drivers/peci/controller/peci-aspeed.c b/drivers/peci/controller/peci-aspeed.c
index ad3a7d71ed4c..57e1a7138607 100644
--- a/drivers/peci/controller/peci-aspeed.c
+++ b/drivers/peci/controller/peci-aspeed.c
@@ -527,14 +527,14 @@ static int aspeed_peci_probe(struct platform_device *pdev)
if (priv->irq < 0)
return priv->irq;
+ init_completion(&priv->xfer_complete);
+ spin_lock_init(&priv->lock);
+
ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler,
0, "peci-aspeed", priv);
if (ret)
return ret;
- init_completion(&priv->xfer_complete);
- spin_lock_init(&priv->lock);
-
priv->rst = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(priv->rst))
return dev_err_probe(priv->dev, PTR_ERR(priv->rst),
diff --git a/drivers/peci/controller/peci-npcm.c b/drivers/peci/controller/peci-npcm.c
index 931868991241..3cc4e959aff3 100644
--- a/drivers/peci/controller/peci-npcm.c
+++ b/drivers/peci/controller/peci-npcm.c
@@ -253,14 +253,14 @@ static int npcm_peci_probe(struct platform_device *pdev)
if (priv->irq < 0)
return priv->irq;
+ init_completion(&priv->xfer_complete);
+ spin_lock_init(&priv->lock);
+
ret = devm_request_irq(&pdev->dev, priv->irq, npcm_peci_irq_handler,
0, "peci-npcm-irq", priv);
if (ret)
return ret;
- init_completion(&priv->xfer_complete);
- spin_lock_init(&priv->lock);
-
ret = npcm_peci_init_ctrl(priv);
if (ret)
return ret;
diff --git a/drivers/perf/xgene_pmu.c b/drivers/perf/xgene_pmu.c
index 33b5497bdc06..ecbcd2d39f9e 100644
--- a/drivers/perf/xgene_pmu.c
+++ b/drivers/perf/xgene_pmu.c
@@ -1873,6 +1873,8 @@ static int xgene_pmu_probe(struct platform_device *pdev)
if (irq < 0)
return -EINVAL;
+ raw_spin_lock_init(&xgene_pmu->lock);
+
rc = devm_request_irq(&pdev->dev, irq, xgene_pmu_isr,
IRQF_NOBALANCING | IRQF_NO_THREAD,
dev_name(&pdev->dev), xgene_pmu);
@@ -1883,8 +1885,6 @@ static int xgene_pmu_probe(struct platform_device *pdev)
xgene_pmu->irq = irq;
- raw_spin_lock_init(&xgene_pmu->lock);
-
/* Check for active MCBs and MCUs */
rc = xgene_pmu_probe_active_mcb_mcu_l3c(xgene_pmu, pdev);
if (rc) {
diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c
index e73feb8ac90d..f64dbb29a99d 100644
--- a/drivers/pinctrl/stm32/pinctrl-stm32.c
+++ b/drivers/pinctrl/stm32/pinctrl-stm32.c
@@ -1452,8 +1452,12 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl, struct fwnode
/* get the last defined gpio line (offset + nb of pins) */
npins = args.args[0] + args.args[2];
- while (!fwnode_property_get_reference_args(fwnode, "gpio-ranges", NULL, 3, ++i, &args))
+ fwnode_handle_put(args.fwnode);
+
+ while (!fwnode_property_get_reference_args(fwnode, "gpio-ranges", NULL, 3, ++i, &args)) {
npins = max(npins, (int)(args.args[0] + args.args[2]));
+ fwnode_handle_put(args.fwnode);
+ }
} else {
bank_nr = pctl->nbanks;
bank->gpio_chip.base = bank_nr * STM32_GPIO_PINS_PER_BANK;
diff --git a/drivers/platform/x86/amd/hfi/hfi.c b/drivers/platform/x86/amd/hfi/hfi.c
index ac6ef0b5b95b..1d12a3b8f32f 100644
--- a/drivers/platform/x86/amd/hfi/hfi.c
+++ b/drivers/platform/x86/amd/hfi/hfi.c
@@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/mailbox_client.h>
#include <linux/mutex.h>
+#include <linux/overflow.h>
#include <linux/percpu-defs.h>
#include <linux/platform_device.h>
#include <linux/smp.h>
@@ -169,6 +170,21 @@ static int amd_hfi_fill_metadata(struct amd_hfi_data *amd_hfi_data)
return -EINVAL;
}
+ /*
+ * The bitmaps enumerating the APIC IDs occupy the first n_bitmaps
+ * words of table_data. Reject a firmware-provided count that would
+ * push those reads past the end of the shared memory region.
+ */
+ if (struct_size(amd_hfi_data->shmem, table_data, amd_hfi_data->shmem->n_bitmaps) >
+ pcct_ext->length) {
+ dev_err(amd_hfi_data->dev, "invalid number of bitmaps: %u\n",
+ amd_hfi_data->shmem->n_bitmaps);
+ return -EINVAL;
+ }
+
+ /* The ranking data for each processor follows the bitmaps */
+ u32 *table = amd_hfi_data->shmem->table_data + amd_hfi_data->shmem->n_bitmaps;
+
for (unsigned int i = 0; i < amd_hfi_data->shmem->n_bitmaps; i++) {
u32 bitmap = amd_hfi_data->shmem->table_data[i];
@@ -193,10 +209,6 @@ static int amd_hfi_fill_metadata(struct amd_hfi_data *amd_hfi_data)
info = per_cpu_ptr(&amd_hfi_cpuinfo, cpu_index);
apic_index = apic_start * info->nr_class * 2;
for (unsigned int k = 0; k < info->nr_class; k++) {
- u32 *table = amd_hfi_data->shmem->table_data +
- amd_hfi_data->shmem->n_bitmaps +
- i * info->nr_class;
-
info->amd_hfi_classes[k].eff = table[apic_index + 2 * k];
info->amd_hfi_classes[k].perf = table[apic_index + 2 * k + 1];
}
diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c
index 000f876e27eb..8abb80e1803f 100644
--- a/drivers/platform/x86/amd/pmc/pmc.c
+++ b/drivers/platform/x86/amd/pmc/pmc.c
@@ -14,6 +14,7 @@
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/dmi.h>
@@ -554,7 +555,6 @@ static int amd_pmc_wa_irq1(struct amd_pmc_dev *pdev)
static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
{
- struct rtc_device *rtc_device;
time64_t then, now, duration;
struct rtc_wkalrm alarm;
struct rtc_time tm;
@@ -570,7 +570,8 @@ static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
if (pdev->major < 64 || (pdev->major == 64 && pdev->minor < 53))
return 0;
- rtc_device = rtc_class_open("rtc0");
+ struct rtc_device *rtc_device __free(rtc_class_close) =
+ rtc_class_open("rtc0");
if (!rtc_device)
return 0;
rc = rtc_read_alarm(rtc_device, &alarm);
@@ -604,19 +605,6 @@ static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
return rc;
}
-static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev)
-{
- /*
- * Starting a new HW sleep cycle right after waking from one
- * can cause electrical problems triggering the over voltage protection.
- * That is avoided by delaying the next suspend a bit, see also
- * https://lore.kernel.org/all/20250414162446.3853194-1-superm1@kernel.org/
- */
- struct smu_metrics table;
-
- return get_metrics_table(pdev, &table) == 0 && table.s0i3_last_entry_status;
-}
-
static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev)
{
/*
@@ -709,7 +697,7 @@ static void amd_pmc_s2idle_check(void)
struct amd_pmc_dev *pdev = &pmc;
int rc;
- if (amd_pmc_intermediate_wakeup_need_delay(pdev) ||
+ if (!pdev->is_first_check_after_suspend ||
amd_pmc_want_suspend_delay(pdev))
msleep(2500);
diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c
index 85192c7536b8..cbe4eef5b8bb 100644
--- a/drivers/platform/x86/amd/pmf/spc.c
+++ b/drivers/platform/x86/amd/pmf/spc.c
@@ -244,13 +244,11 @@ static int amd_pmf_get_battery_prop(enum power_supply_property prop)
continue;
ret = power_supply_get_property(psy, prop, &value);
- if (ret) {
- power_supply_put(psy);
- return ret;
- }
+ power_supply_put(psy);
+ return ret ? ret : value.intval;
}
- return value.intval;
+ return -ENODEV;
}
static int amd_pmf_get_battery_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c
index c28f3c5744c2..bf101e527b11 100644
--- a/drivers/platform/x86/amd/pmf/sps.c
+++ b/drivers/platform/x86/amd/pmf/sps.c
@@ -248,20 +248,24 @@ static int amd_pmf_update_sps_power_limits_v2(struct amd_pmf_dev *pdev, int pwr_
switch (pwr_mode) {
case POWER_MODE_PERFORMANCE:
index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BEST_PERFORMANCE];
- amd_pmf_update_slider_v2(pdev, index);
break;
case POWER_MODE_BALANCED_POWER:
index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BALANCED];
- amd_pmf_update_slider_v2(pdev, index);
break;
case POWER_MODE_POWER_SAVER:
index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BEST_POWER_EFFICIENCY];
- amd_pmf_update_slider_v2(pdev, index);
break;
default:
return -EINVAL;
}
+ if (index >= APTS_MAX_STATES) {
+ dev_err(pdev->dev, "Invalid APTS index: %d\n", index);
+ return -EINVAL;
+ }
+
+ amd_pmf_update_slider_v2(pdev, index);
+
return 0;
}
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 9f0f79030b5b..c8cc47df9294 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -998,10 +998,10 @@ static ssize_t kbd_rgb_state_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- u32 flags, cmd, boot, awake, sleep, keyboard;
+ u32 flags, cmd, boot, awake, sleep, shutdown;
int err;
- if (sscanf(buf, "%d %d %d %d %d", &cmd, &boot, &awake, &sleep, &keyboard) != 5)
+ if (sscanf(buf, "%d %d %d %d %d", &cmd, &boot, &awake, &sleep, &shutdown) != 5)
return -EINVAL;
if (cmd)
@@ -1014,7 +1014,7 @@ static ssize_t kbd_rgb_state_store(struct device *dev,
flags |= BIT(3);
if (sleep)
flags |= BIT(5);
- if (keyboard)
+ if (shutdown)
flags |= BIT(7);
/* 0xbd is the required default arg0 for the method. Nothing happens otherwise */
@@ -1028,7 +1028,7 @@ static ssize_t kbd_rgb_state_store(struct device *dev,
static DEVICE_ATTR_WO(kbd_rgb_state);
static DEVICE_STRING_ATTR_RO(kbd_rgb_state_index, 0444,
- "cmd boot awake sleep keyboard");
+ "cmd boot awake sleep shutdown");
static struct attribute *kbd_rgb_state_attrs[] = {
&dev_attr_kbd_rgb_state.attr,
@@ -4122,13 +4122,20 @@ static int is_display_toggle(int code)
static int read_screenpad_backlight_power(struct asus_wmi *asus)
{
- int ret;
+ int ret, retval;
- ret = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_SCREENPAD_POWER);
+ ret = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_POWER, &retval);
if (ret < 0)
return ret;
- /* 1 == powered */
- return ret ? BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF;
+
+ /*
+ * The firmware reports the panel power in the low byte of the
+ * devstate as a raw EC status byte that is non-zero when the
+ * panel is powered; other models report it through
+ * ASUS_WMI_DSTS_STATUS_BIT, which lies inside the same mask.
+ */
+ return (retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK) ?
+ BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF;
}
static int read_screenpad_brightness(struct backlight_device *bd)
@@ -4156,7 +4163,8 @@ static int update_screenpad_bl_status(struct backlight_device *bd)
u32 ctrl_param = bd->props.brightness;
int err = 0;
- if (bd->props.power) {
+ switch (bd->props.power) {
+ case BACKLIGHT_POWER_ON:
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 1, NULL);
if (err < 0)
return err;
@@ -4164,12 +4172,17 @@ static int update_screenpad_bl_status(struct backlight_device *bd)
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, ctrl_param, NULL);
if (err < 0)
return err;
- }
+ break;
- if (!bd->props.power) {
+ case BACKLIGHT_POWER_OFF:
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 0, NULL);
if (err < 0)
return err;
+ break;
+
+ default:
+ pr_warn("Invalid screenpad backlight power state: %d\n", bd->props.power);
+ return -EINVAL;
}
return err;
@@ -4186,16 +4199,17 @@ static int asus_screenpad_init(struct asus_wmi *asus)
struct backlight_device *bd;
struct backlight_properties props;
int err, power;
- int brightness = 0;
+ u32 brightness = 0;
- power = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_SCREENPAD_POWER);
+ power = read_screenpad_backlight_power(asus);
if (power < 0)
return power;
- if (power) {
+ if (power == BACKLIGHT_POWER_ON) {
err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &brightness);
if (err < 0)
return err;
+ brightness &= ASUS_WMI_DSTS_BRIGHTNESS_MASK;
}
memset(&props, 0, sizeof(struct backlight_properties));
diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
index 274bdecce685..0dac3a853dfe 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
@@ -11,7 +11,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/printk.h>
-#include <linux/string.h>
+#include <linux/string_helpers.h>
#include <linux/wmi.h>
#include "bioscfg.h"
#include "../../firmware_attributes_class.h"
@@ -53,74 +53,44 @@ int hp_get_integer_from_buffer(u8 **buffer, u32 *buffer_size, u32 *integer)
int hp_get_string_from_buffer(u8 **buffer, u32 *buffer_size, char *dst, u32 dst_size)
{
+ char utf8_buf[MAX_BUFF_SIZE];
u16 *src = (u16 *)*buffer;
+ int escaped_len;
+ u16 orig_size;
u16 src_size;
-
- u16 size;
- int i;
- int conv_dst_size;
+ int utf8_len;
if (*buffer_size < sizeof(u16))
return -EINVAL;
- src_size = *(src++);
- /* size value in u16 chars */
- size = src_size / sizeof(u16);
+ src_size = *src;
- /* Ensure there is enough space remaining to read and convert
- * the string
+ /*
+ * Ensure there is enough space remaining for the length prefix
+ * just read plus the string data it describes.
*/
- if (*buffer_size < src_size)
+ if (*buffer_size < sizeof(u16) + src_size)
return -EINVAL;
- for (i = 0; i < size; i++)
- if (src[i] == '\\' ||
- src[i] == '\r' ||
- src[i] == '\n' ||
- src[i] == '\t')
- size++;
-
- /*
- * Conversion is limited to destination string max number of
- * bytes.
- */
- conv_dst_size = size;
- if (size >= dst_size)
- conv_dst_size = dst_size - 1;
+ src++;
+ /* size value in u16 chars */
+ orig_size = src_size / sizeof(u16);
- /*
- * convert from UTF-16 unicode to ASCII
- */
- utf16s_to_utf8s(src, src_size, UTF16_HOST_ENDIAN, dst, conv_dst_size);
- dst[conv_dst_size] = 0;
-
- for (i = 0; i < conv_dst_size; i++) {
- if (*src == '\\' ||
- *src == '\r' ||
- *src == '\n' ||
- *src == '\t') {
- dst[i++] = '\\';
- if (i == conv_dst_size)
- break;
- }
+ utf8_len = utf16s_to_utf8s(src, orig_size, UTF16_HOST_ENDIAN,
+ utf8_buf, sizeof(utf8_buf));
- if (*src == '\r')
- dst[i] = 'r';
- else if (*src == '\n')
- dst[i] = 'n';
- else if (*src == '\t')
- dst[i] = 't';
- else if (*src == '"')
- dst[i] = '\'';
- else
- dst[i] = *src;
- src++;
- }
+ escaped_len = string_escape_mem(utf8_buf, utf8_len, dst, dst_size - 1,
+ ESCAPE_SPACE | ESCAPE_SPECIAL,
+ "\\\r\n\t");
+ if (escaped_len > dst_size - 1)
+ escaped_len = dst_size - 1;
+ dst[escaped_len] = '\0';
+ strreplace(dst, '"', '\'');
- *buffer = (u8 *)src;
- *buffer_size -= size * sizeof(u16);
+ *buffer += sizeof(u16) + src_size;
+ *buffer_size -= sizeof(u16) + src_size;
- return size;
+ return escaped_len;
}
int hp_get_common_data_from_buffer(u8 **buffer_ptr, u32 *buffer_size,
@@ -442,7 +412,7 @@ int hp_convert_hexstr_to_str(const char *input, u32 input_len, char **str, int *
*len = 0;
*str = NULL;
- new_str = kmalloc(input_len, GFP_KERNEL);
+ new_str = kmalloc(2 * DIV_ROUND_UP(input_len, 5) + 1, GFP_KERNEL);
if (!new_str)
return -ENOMEM;
diff --git a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c
index 6b94f90bea07..97990191b71f 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c
@@ -60,7 +60,8 @@ int hp_get_password_instance_for_type(const char *name)
return -EINVAL;
}
-static int validate_password_input(int instance_id, const char *buf)
+static int validate_password_input(int instance_id, const char *buf,
+ bool is_current)
{
int length;
struct password_data *password_data = &bioscfg_drv.password_data[instance_id];
@@ -69,13 +70,18 @@ static int validate_password_input(int instance_id, const char *buf)
if (length > 0 && buf[length - 1] == '\n')
length--;
+ /* An empty current_password clears the session password */
+ if (is_current && !length)
+ return 0;
+
if (length > MAX_PASSWD_SIZE)
- return INVALID_BIOS_AUTH;
+ return -E2BIG;
if (password_data->min_password_length > length ||
password_data->max_password_length < length)
- return INVALID_BIOS_AUTH;
- return SUCCESS;
+ return -E2BIG;
+
+ return 0;
}
ATTRIBUTE_N_PROPERTY_SHOW(is_enabled, password);
@@ -96,7 +102,7 @@ static int store_password_instance(struct kobject *kobj, const char *buf,
id = get_password_instance_id(kobj);
if (id >= 0)
- ret = validate_password_input(id, buf_cp);
+ ret = validate_password_input(id, buf_cp, is_current);
}
if (!ret) {
diff --git a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
index 4d94e48c1a4c..040964ed50ce 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c
@@ -47,9 +47,6 @@ size_t hp_calculate_security_buffer(const char *authentication)
return sizeof(u16) * 2;
authlen = strlen(authentication);
- if (!authlen)
- return sizeof(u16) * 2;
-
size = sizeof(u16) + authlen * sizeof(u16);
if (!strstarts(authentication, BEAM_PREFIX))
size += strlen(UTF_PREFIX) * sizeof(u16);
@@ -310,7 +307,7 @@ static ssize_t auth_token_store(struct kobject *kobj,
length--;
/* allocate space and copy current auth token */
- bioscfg_drv.spm_data.auth_token = kmemdup(buf, length, GFP_KERNEL);
+ bioscfg_drv.spm_data.auth_token = kstrndup(buf, length, GFP_KERNEL);
if (!bioscfg_drv.spm_data.auth_token) {
ret = -ENOMEM;
goto exit_token;
diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
index 5eb2aade44cc..f92bb3dca2e3 100644
--- a/drivers/platform/x86/hp/hp-wmi.c
+++ b/drivers/platform/x86/hp/hp-wmi.c
@@ -491,8 +491,9 @@ static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
goto out_free;
}
- if (obj->type != ACPI_TYPE_BUFFER) {
- pr_warn("query 0x%x returned an invalid object 0x%x\n", query, ret);
+ if (obj->type != ACPI_TYPE_BUFFER ||
+ obj->buffer.length < sizeof(*bios_return)) {
+ pr_warn("query 0x%x returned wrong type or too small buffer\n", query);
ret = -EINVAL;
goto out_free;
}
@@ -511,7 +512,7 @@ static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
if (!outsize)
goto out_free;
- actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return)));
+ actual_outsize = min_t(u32, outsize, obj->buffer.length - sizeof(*bios_return));
memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize);
memset(buffer + actual_outsize, 0, outsize - actual_outsize);
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
index 1505fc3ef7a8..96c33ba80002 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -280,7 +280,6 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
unsigned long gpio_flags;
union acpi_object *obj;
struct gpio_desc *gpio;
- const char *err_msg;
const char *con_id;
int ret;
@@ -326,7 +325,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
case INT3472_GPIO_TYPE_HOTPLUG_DETECT:
ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, con_id, gpio_flags);
if (ret)
- err_msg = "Failed to map GPIO pin to sensor\n";
+ dev_err_probe(int3472->dev, ret,
+ "Failed to map GPIO pin to sensor\n");
break;
case INT3472_GPIO_TYPE_CLK_ENABLE:
@@ -336,7 +336,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, con_id, gpio_flags);
if (IS_ERR(gpio)) {
ret = PTR_ERR(gpio);
- err_msg = "Failed to get GPIO\n";
+ dev_err_probe(int3472->dev, ret, "Failed to get GPIO\n");
break;
}
@@ -344,13 +344,15 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
case INT3472_GPIO_TYPE_CLK_ENABLE:
ret = skl_int3472_register_gpio_clock(int3472, gpio);
if (ret)
- err_msg = "Failed to register clock\n";
+ dev_err_probe(int3472->dev, ret,
+ "Failed to register clock\n");
break;
case INT3472_GPIO_TYPE_PRIVACY_LED:
ret = skl_int3472_register_pled(int3472, gpio);
if (ret)
- err_msg = "Failed to register LED\n";
+ dev_err_probe(int3472->dev, ret,
+ "Failed to register LED\n");
break;
case INT3472_GPIO_TYPE_POWER_ENABLE:
@@ -360,7 +362,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
ret = skl_int3472_register_regulator(int3472, gpio, enable_time_us,
con_id, second_sensor);
if (ret)
- err_msg = "Failed to register regulator\n";
+ dev_err_probe(int3472->dev, ret,
+ "Failed to register regulator\n");
break;
default: /* Never reached */
@@ -383,11 +386,11 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
int3472->ngpios++;
ACPI_FREE(obj);
- if (ret < 0)
- return dev_err_probe(int3472->dev, ret, err_msg);
-
- /* Tell acpi_dev_get_resources() to not make a copy of the resource */
- return 1;
+ /*
+ * Either return an error or tell acpi_dev_get_resources() to not make a
+ * copy of the resource.
+ */
+ return ret < 0 ? ret : 1;
}
int int3472_discrete_parse_crs(struct int3472_discrete_device *int3472)
diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c
index 8077274726b5..9ec843617e3c 100644
--- a/drivers/platform/x86/intel/pmt/class.c
+++ b/drivers/platform/x86/intel/pmt/class.c
@@ -339,7 +339,14 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
sysfs_bin_attr_init(&entry->pmt_bin_attr);
entry->pmt_bin_attr.attr.name = ns->name;
entry->pmt_bin_attr.attr.mode = 0440;
- entry->pmt_bin_attr.mmap = intel_pmt_mmap;
+
+ /*
+ * The read_telem callback controls access to the memory area. Block
+ * usage if the read_telem callback is present.
+ */
+ if (!(entry->cb && entry->cb->read_telem))
+ entry->pmt_bin_attr.mmap = intel_pmt_mmap;
+
entry->pmt_bin_attr.read = intel_pmt_read;
entry->pmt_bin_attr.size = entry->size;
diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
index 0dfc552b2802..3836d76943f5 100644
--- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
@@ -211,7 +211,7 @@ static int uncore_pm_notify(struct notifier_block *nb, unsigned long mode,
struct uncore_data *data = &uncore_instances[i];
if (!data || !data->valid || !data->stored_uncore_data)
- return 0;
+ continue;
wrmsrq_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT,
data->stored_uncore_data);
diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c
index 1e292eaeb209..814e418bea82 100644
--- a/drivers/platform/x86/intel/vsec.c
+++ b/drivers/platform/x86/intel/vsec.c
@@ -617,7 +617,7 @@ static bool intel_vsec_get_features(struct pci_dev *pdev,
found = true;
if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) &&
- intel_vsec_walk_header(&pdev->dev, info))
+ !intel_vsec_walk_header(&pdev->dev, info))
found = true;
return found;
diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c
index a30845ba3796..4fc931029634 100644
--- a/drivers/platform/x86/oxpec.c
+++ b/drivers/platform/x86/oxpec.c
@@ -60,6 +60,7 @@ enum oxp_board {
oxp_x1,
oxp_g1_i,
oxp_g1_a,
+ oxp_apex,
};
static enum oxp_board board;
@@ -103,11 +104,14 @@ static struct device *oxp_dev;
#define OXP_X1_CHARGE_LIMIT_REG 0xA3 /* X1 charge limit (%) */
#define OXP_X1_CHARGE_INHIBIT_REG 0xA4 /* X1 bypass charging */
-#define OXP_X1_CHARGE_INHIBIT_MASK_AWAKE 0x01
+#define OXP_APEX_CHARGE_LIMIT_REG 0xE5 /* Apex charge limit (%) */
+#define OXP_APEX_CHARGE_INHIBIT_REG 0xE6 /* Apex bypass charging */
+
+#define OXP_CHARGE_INHIBIT_MASK_AWAKE 0x01
/* X1 Mask is 0x0A, F1Pro is 0x02 but the extra bit on the X1 does nothing. */
-#define OXP_X1_CHARGE_INHIBIT_MASK_OFF 0x02
-#define OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS (OXP_X1_CHARGE_INHIBIT_MASK_AWAKE | \
- OXP_X1_CHARGE_INHIBIT_MASK_OFF)
+#define OXP_CHARGE_INHIBIT_MASK_OFF 0x02
+#define OXP_CHARGE_INHIBIT_MASK_ALWAYS (OXP_CHARGE_INHIBIT_MASK_AWAKE | \
+ OXP_CHARGE_INHIBIT_MASK_OFF)
static const struct dmi_system_id dmi_table[] = {
{
@@ -220,7 +224,7 @@ static const struct dmi_system_id dmi_table[] = {
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER APEX"),
},
- .driver_data = (void *)oxp_fly,
+ .driver_data = (void *)oxp_apex,
},
{
.matches = {
@@ -404,6 +408,7 @@ static umode_t tt_toggle_is_visible(struct kobject *kobj,
case oxp_x1:
case oxp_g1_i:
case oxp_g1_a:
+ case oxp_apex:
return attr->mode;
default:
break;
@@ -433,6 +438,7 @@ static ssize_t tt_toggle_store(struct device *dev,
case oxp_fly:
case oxp_mini_amd_pro:
case oxp_g1_a:
+ case oxp_apex:
reg = OXP_TURBO_SWITCH_REG;
mask = OXP_TURBO_TAKE_VAL;
break;
@@ -479,6 +485,7 @@ static ssize_t tt_toggle_show(struct device *dev,
case oxp_fly:
case oxp_mini_amd_pro:
case oxp_g1_a:
+ case oxp_apex:
reg = OXP_TURBO_SWITCH_REG;
mask = OXP_TURBO_TAKE_VAL;
break;
@@ -576,6 +583,7 @@ static bool oxp_psy_ext_supported(void)
case oxp_g1_i:
case oxp_g1_a:
case oxp_fly:
+ case oxp_apex:
return true;
default:
break;
@@ -589,12 +597,24 @@ static int oxp_psy_ext_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
+ u8 limit_reg, inhibit_reg;
long raw_val;
int ret;
+ switch (board) {
+ case oxp_apex:
+ limit_reg = OXP_APEX_CHARGE_LIMIT_REG;
+ inhibit_reg = OXP_APEX_CHARGE_INHIBIT_REG;
+ break;
+ default:
+ limit_reg = OXP_X1_CHARGE_LIMIT_REG;
+ inhibit_reg = OXP_X1_CHARGE_INHIBIT_REG;
+ break;
+ }
+
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
- ret = read_from_ec(OXP_X1_CHARGE_LIMIT_REG, 1, &raw_val);
+ ret = read_from_ec(limit_reg, 1, &raw_val);
if (ret)
return ret;
if (raw_val < 0 || raw_val > 100)
@@ -602,14 +622,14 @@ static int oxp_psy_ext_get_prop(struct power_supply *psy,
val->intval = raw_val;
return 0;
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
- ret = read_from_ec(OXP_X1_CHARGE_INHIBIT_REG, 1, &raw_val);
+ ret = read_from_ec(inhibit_reg, 1, &raw_val);
if (ret)
return ret;
- if ((raw_val & OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS) ==
- OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS)
+ if ((raw_val & OXP_CHARGE_INHIBIT_MASK_ALWAYS) ==
+ OXP_CHARGE_INHIBIT_MASK_ALWAYS)
val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
- else if ((raw_val & OXP_X1_CHARGE_INHIBIT_MASK_AWAKE) ==
- OXP_X1_CHARGE_INHIBIT_MASK_AWAKE)
+ else if ((raw_val & OXP_CHARGE_INHIBIT_MASK_AWAKE) ==
+ OXP_CHARGE_INHIBIT_MASK_AWAKE)
val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE;
else
val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
@@ -625,29 +645,41 @@ static int oxp_psy_ext_set_prop(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
+ u8 limit_reg, inhibit_reg;
long raw_val;
+ switch (board) {
+ case oxp_apex:
+ limit_reg = OXP_APEX_CHARGE_LIMIT_REG;
+ inhibit_reg = OXP_APEX_CHARGE_INHIBIT_REG;
+ break;
+ default:
+ limit_reg = OXP_X1_CHARGE_LIMIT_REG;
+ inhibit_reg = OXP_X1_CHARGE_INHIBIT_REG;
+ break;
+ }
+
switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
if (val->intval < 0 || val->intval > 100)
return -EINVAL;
- return write_to_ec(OXP_X1_CHARGE_LIMIT_REG, val->intval);
+ return write_to_ec(limit_reg, val->intval);
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
switch (val->intval) {
case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
raw_val = 0;
break;
case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE:
- raw_val = OXP_X1_CHARGE_INHIBIT_MASK_AWAKE;
+ raw_val = OXP_CHARGE_INHIBIT_MASK_AWAKE;
break;
case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
- raw_val = OXP_X1_CHARGE_INHIBIT_MASK_ALWAYS;
+ raw_val = OXP_CHARGE_INHIBIT_MASK_ALWAYS;
break;
default:
return -EINVAL;
}
- return write_to_ec(OXP_X1_CHARGE_INHIBIT_REG, raw_val);
+ return write_to_ec(inhibit_reg, raw_val);
default:
return -EINVAL;
}
@@ -715,6 +747,7 @@ static int oxp_pwm_enable(void)
case oxp_x1:
case oxp_g1_i:
case oxp_g1_a:
+ case oxp_apex:
return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
default:
return -EINVAL;
@@ -743,6 +776,7 @@ static int oxp_pwm_disable(void)
case oxp_x1:
case oxp_g1_i:
case oxp_g1_a:
+ case oxp_apex:
return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
default:
return -EINVAL;
@@ -771,6 +805,7 @@ static int oxp_pwm_read(long *val)
case oxp_x1:
case oxp_g1_i:
case oxp_g1_a:
+ case oxp_apex:
return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
default:
return -EOPNOTSUPP;
@@ -815,6 +850,7 @@ static int oxp_pwm_fan_speed(long *val)
case oxp_mini_amd_a07:
case oxp_mini_amd_pro:
case oxp_g1_a:
+ case oxp_apex:
return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
default:
return -EOPNOTSUPP;
@@ -855,6 +891,7 @@ static int oxp_pwm_input_write(long val)
case oxp_fly:
case oxp_mini_amd_pro:
case oxp_g1_a:
+ case oxp_apex:
return write_to_ec(OXP_SENSOR_PWM_REG, val);
default:
return -EOPNOTSUPP;
@@ -902,6 +939,7 @@ static int oxp_pwm_input_read(long *val)
case oxp_fly:
case oxp_mini_amd_pro:
case oxp_g1_a:
+ case oxp_apex:
default:
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
if (ret)
diff --git a/drivers/platform/x86/siemens/simatic-ipc.c b/drivers/platform/x86/siemens/simatic-ipc.c
index 7039874d8f11..4a4509fc9263 100644
--- a/drivers/platform/x86/siemens/simatic-ipc.c
+++ b/drivers/platform/x86/siemens/simatic-ipc.c
@@ -92,6 +92,7 @@ static int register_platform_devices(u32 station_id)
u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
u8 battmode = SIMATIC_IPC_DEVICE_NONE;
char *pdevname;
+ int ret;
int i;
for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
@@ -139,8 +140,10 @@ static int register_platform_devices(u32 station_id)
pdevname, PLATFORM_DEVID_NONE,
&platform_data,
sizeof(struct simatic_ipc_platform));
- if (IS_ERR(ipc_led_platform_device))
- return PTR_ERR(ipc_led_platform_device);
+ if (IS_ERR(ipc_led_platform_device)) {
+ ret = PTR_ERR(ipc_led_platform_device);
+ goto err_unregister_batt;
+ }
pr_debug("device=%s created\n",
ipc_led_platform_device->name);
@@ -153,8 +156,10 @@ static int register_platform_devices(u32 station_id)
KBUILD_MODNAME "_wdt", PLATFORM_DEVID_NONE,
&platform_data,
sizeof(struct simatic_ipc_platform));
- if (IS_ERR(ipc_wdt_platform_device))
- return PTR_ERR(ipc_wdt_platform_device);
+ if (IS_ERR(ipc_wdt_platform_device)) {
+ ret = PTR_ERR(ipc_wdt_platform_device);
+ goto err_unregister_led;
+ }
pr_debug("device=%s created\n",
ipc_wdt_platform_device->name);
@@ -169,6 +174,14 @@ static int register_platform_devices(u32 station_id)
}
return 0;
+
+err_unregister_led:
+ if (ledmode != SIMATIC_IPC_DEVICE_NONE)
+ platform_device_unregister(ipc_led_platform_device);
+err_unregister_batt:
+ if (battmode != SIMATIC_IPC_DEVICE_NONE)
+ platform_device_unregister(ipc_batt_platform_device);
+ return ret;
}
static void request_additional_modules(u32 station_id)
diff --git a/drivers/platform/x86/tuxedo/nb04/wmi_ab.c b/drivers/platform/x86/tuxedo/nb04/wmi_ab.c
index 32d7756022c2..38a719381fdc 100644
--- a/drivers/platform/x86/tuxedo/nb04/wmi_ab.c
+++ b/drivers/platform/x86/tuxedo/nb04/wmi_ab.c
@@ -232,8 +232,8 @@ static const struct tux_kbl_map_entry_t sirius_16_ansii_kbl_map[] = {
{ 0x2e, { 246000, 67500, 5250 } },
{ 0x2a, { 269500, 67500, 5250 } },
{ 0x53, { 294500, 67500, 5250 } },
- { 0x55, { 311200, 67500, 5250 } },
- { 0x54, { 327900, 67500, 5250 } },
+ { 0x54, { 311200, 67500, 5250 } },
+ { 0x55, { 327900, 67500, 5250 } },
{ 0x56, { 344600, 67500, 5250 } },
{ 0x2b, { 31000, 85500, 5500 } },
{ 0x14, { 51500, 85500, 5500 } },
@@ -337,8 +337,8 @@ static const struct tux_kbl_map_entry_t sirius_16_iso_kbl_map[] = {
{ 0x2e, { 246000, 67500, 5250 } },
{ 0x2a, { 269500, 67500, 5250 } },
{ 0x53, { 294500, 67500, 5250 } },
- { 0x55, { 311200, 67500, 5250 } },
- { 0x54, { 327900, 67500, 5250 } },
+ { 0x54, { 311200, 67500, 5250 } },
+ { 0x55, { 327900, 67500, 5250 } },
{ 0x56, { 344600, 67500, 5250 } },
{ 0x2b, { 31000, 85500, 5500 } },
{ 0x14, { 51500, 85500, 5500 } },
diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c
index 969a75f4c016..3b0a5bdd352b 100644
--- a/drivers/power/supply/ab8500_fg.c
+++ b/drivers/power/supply/ab8500_fg.c
@@ -1387,6 +1387,13 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
}
}
+ /* Keep the charge properties current between percentage changes. */
+ if (di->bat_cap.prev_percent == percent &&
+ di->bat_cap.prev_mah != di->bat_cap.mah) {
+ di->bat_cap.prev_mah = di->bat_cap.mah;
+ changed = true;
+ }
+
if (changed) {
if (di->bm->capacity_scaling) {
di->bat_cap.cap_scale.scaled_cap =
@@ -2205,42 +2212,36 @@ static int ab8500_fg_get_ext_psy_data(struct power_supply *ext, void *data)
switch (prop) {
case POWER_SUPPLY_PROP_STATUS:
- switch (ext->desc->type) {
- case POWER_SUPPLY_TYPE_BATTERY:
- switch (ret.intval) {
- case POWER_SUPPLY_STATUS_UNKNOWN:
- case POWER_SUPPLY_STATUS_DISCHARGING:
- case POWER_SUPPLY_STATUS_NOT_CHARGING:
- if (!di->flags.charging)
- break;
- di->flags.charging = false;
- di->flags.fully_charged = false;
- if (di->bm->capacity_scaling)
- ab8500_fg_update_cap_scalers(di);
- queue_work(di->fg_wq, &di->fg_work);
+ switch (ret.intval) {
+ case POWER_SUPPLY_STATUS_UNKNOWN:
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ case POWER_SUPPLY_STATUS_NOT_CHARGING:
+ if (!di->flags.charging)
break;
- case POWER_SUPPLY_STATUS_FULL:
- if (di->flags.fully_charged)
- break;
- di->flags.fully_charged = true;
- di->flags.force_full = true;
- /* Save current capacity as maximum */
- di->bat_cap.max_mah = di->bat_cap.mah;
- queue_work(di->fg_wq, &di->fg_work);
- break;
- case POWER_SUPPLY_STATUS_CHARGING:
- if (di->flags.charging &&
- !di->flags.fully_charged)
- break;
- di->flags.charging = true;
- di->flags.fully_charged = false;
- if (di->bm->capacity_scaling)
- ab8500_fg_update_cap_scalers(di);
- queue_work(di->fg_wq, &di->fg_work);
+ di->flags.charging = false;
+ di->flags.fully_charged = false;
+ if (di->bm->capacity_scaling)
+ ab8500_fg_update_cap_scalers(di);
+ queue_work(di->fg_wq, &di->fg_work);
+ break;
+ case POWER_SUPPLY_STATUS_FULL:
+ if (di->flags.fully_charged)
break;
- }
+ di->flags.fully_charged = true;
+ di->flags.force_full = true;
+ /* Save current capacity as maximum */
+ di->bat_cap.max_mah = di->bat_cap.mah;
+ queue_work(di->fg_wq, &di->fg_work);
break;
- default:
+ case POWER_SUPPLY_STATUS_CHARGING:
+ if (di->flags.charging &&
+ !di->flags.fully_charged)
+ break;
+ di->flags.charging = true;
+ di->flags.fully_charged = false;
+ if (di->bm->capacity_scaling)
+ ab8500_fg_update_cap_scalers(di);
+ queue_work(di->fg_wq, &di->fg_work);
break;
}
break;
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 01c9ec1116fa..cd30993b8a89 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -43,6 +43,7 @@
*/
#include <linux/device.h>
+#include <linux/devm-helpers.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/param.h>
@@ -2234,7 +2235,6 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
};
int ret;
- INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
ret = devm_mutex_init(di->dev, &di->lock);
if (ret)
return ret;
@@ -2255,6 +2255,12 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
psy_desc->get_property = bq27xxx_battery_get_property;
psy_desc->external_power_changed = bq27xxx_external_power_changed;
+ /* Cancel the poll work after the power_supply is unregistered. */
+ ret = devm_delayed_work_autocancel(di->dev, &di->work,
+ bq27xxx_battery_poll);
+ if (ret)
+ return ret;
+
di->bat = devm_power_supply_register(di->dev, psy_desc, &psy_cfg);
if (IS_ERR(di->bat))
return dev_err_probe(di->dev, PTR_ERR(di->bat),
@@ -2281,8 +2287,6 @@ void bq27xxx_battery_teardown(struct bq27xxx_device_info *di)
mutex_lock(&di->lock);
di->removed = true;
mutex_unlock(&di->lock);
-
- cancel_delayed_work_sync(&di->work);
}
EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown);
@@ -2291,7 +2295,7 @@ static int bq27xxx_battery_suspend(struct device *dev)
{
struct bq27xxx_device_info *di = dev_get_drvdata(dev);
- cancel_delayed_work(&di->work);
+ cancel_delayed_work_sync(&di->work);
return 0;
}
diff --git a/drivers/power/supply/cros_charge-control.c b/drivers/power/supply/cros_charge-control.c
index 53e6a77e03fc..6a6022f377b7 100644
--- a/drivers/power/supply/cros_charge-control.c
+++ b/drivers/power/supply/cros_charge-control.c
@@ -14,6 +14,7 @@
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/types.h>
#define EC_CHARGE_CONTROL_BEHAVIOURS (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | \
@@ -294,6 +295,7 @@ static int cros_chctl_probe(struct platform_device *pdev)
priv->dev = dev;
priv->cros_ec = cros_ec;
+ dev_set_drvdata(dev, priv);
if (priv->cmd_version == 1)
priv->psy_ext = &cros_chctl_psy_ext_v1;
@@ -319,13 +321,38 @@ static int cros_chctl_probe(struct platform_device *pdev)
return devm_battery_hook_register(dev, &priv->battery_hook);
}
+static int cros_chctl_resume(struct device *dev)
+{
+ struct cros_chctl_priv *priv = dev_get_drvdata(dev);
+ int ret;
+
+ /*
+ * The EC resets its charge control state when the AP powers off, so the
+ * configuration is lost across hibernation, while the values exposed to
+ * userspace still describe it. The cached state is the only remaining
+ * record of that configuration: reprogram the EC from it rather than
+ * re-reading the EC, which would adopt the reset defaults instead.
+ */
+ guard(mutex)(&priv->lock);
+ ret = cros_chctl_configure_ec(priv);
+ if (ret)
+ dev_err(dev, "failed to restore charge control state: %d\n", ret);
+
+ return ret;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(cros_chctl_pm_ops, NULL, cros_chctl_resume);
+
static const struct platform_device_id cros_chctl_id[] = {
{ "cros-charge-control", 0 },
{}
};
static struct platform_driver cros_chctl_driver = {
- .driver.name = "cros-charge-control",
+ .driver = {
+ .name = "cros-charge-control",
+ .pm = pm_sleep_ptr(&cros_chctl_pm_ops),
+ },
.probe = cros_chctl_probe,
.id_table = cros_chctl_id,
};
diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c
index c69c9b9fa0eb..df5f91bc1d0f 100644
--- a/drivers/power/supply/generic-adc-battery.c
+++ b/drivers/power/supply/generic-adc-battery.c
@@ -135,7 +135,7 @@ static void gab_work(struct work_struct *work)
adc_bat = container_of(delayed_work, struct gab, bat_work);
status = adc_bat->status;
- if (!power_supply_am_i_supplied(adc_bat->psy))
+ if (power_supply_am_i_supplied(adc_bat->psy) <= 0)
adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
else if (gab_charge_finished(adc_bat))
adc_bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index 615dc27089a3..7edca0694eb7 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -190,7 +190,35 @@ static void power_supply_deferred_register_work(struct work_struct *work)
device_unlock(psy->dev.parent);
}
-#ifdef CONFIG_OF
+static int power_supply_check_supplies_by_name(struct power_supply *psy)
+{
+ struct device *parent = psy->dev.parent;
+ int nval, ret;
+
+ if (!parent)
+ return 0;
+
+ nval = device_property_string_array_count(parent, "supplied-from");
+ if (nval <= 0)
+ return 0;
+
+ psy->supplied_from = devm_kmalloc_array(&psy->dev, nval,
+ sizeof(*psy->supplied_from),
+ GFP_KERNEL);
+ if (!psy->supplied_from)
+ return -ENOMEM;
+
+ ret = device_property_read_string_array(parent, "supplied-from",
+ (const char **)psy->supplied_from,
+ nval);
+ if (ret < 0)
+ return ret;
+
+ psy->num_supplies = nval;
+
+ return 0;
+}
+
static int __power_supply_populate_supplied_from(struct power_supply *epsy,
void *data)
{
@@ -262,22 +290,21 @@ static int power_supply_find_supply_from_fwnode(struct fwnode_handle *supply_nod
static int power_supply_check_supplies(struct power_supply *psy)
{
struct fwnode_handle *np;
- int cnt = 0;
+ int cnt, ret;
/* If there is already a list honor it */
if (psy->supplied_from && psy->num_supplies > 0)
return 0;
- /* No device node found, nothing to do */
- if (!psy->dev.fwnode)
- return 0;
-
- do {
- int ret;
+ for (cnt = 0; psy->dev.fwnode; cnt++) {
+ np = fwnode_find_reference(psy->dev.fwnode, "power-supplies", cnt);
+ if (IS_ERR(np)) {
+ ret = PTR_ERR(np);
+ if (ret != -ENOENT)
+ return ret;
- np = fwnode_find_reference(psy->dev.fwnode, "power-supplies", cnt++);
- if (IS_ERR(np))
break;
+ }
ret = power_supply_find_supply_from_fwnode(np);
fwnode_handle_put(np);
@@ -286,48 +313,21 @@ static int power_supply_check_supplies(struct power_supply *psy)
dev_dbg(&psy->dev, "Failed to find supply!\n");
return ret;
}
- } while (!IS_ERR(np));
+ }
- /* Missing valid "power-supplies" entries */
- if (cnt == 1)
- return 0;
+ /* Fall back to the name-based property if no references were specified. */
+ if (!cnt)
+ return power_supply_check_supplies_by_name(psy);
/* All supplies found, allocate char * array for filling */
psy->supplied_from = devm_kcalloc(&psy->dev,
- cnt - 1, sizeof(*psy->supplied_from),
+ cnt, sizeof(*psy->supplied_from),
GFP_KERNEL);
if (!psy->supplied_from)
return -ENOMEM;
return power_supply_populate_supplied_from(psy);
}
-#else
-static int power_supply_check_supplies(struct power_supply *psy)
-{
- int nval, ret;
-
- if (!psy->dev.parent)
- return 0;
-
- nval = device_property_string_array_count(psy->dev.parent, "supplied-from");
- if (nval <= 0)
- return 0;
-
- psy->supplied_from = devm_kmalloc_array(&psy->dev, nval,
- sizeof(char *), GFP_KERNEL);
- if (!psy->supplied_from)
- return -ENOMEM;
-
- ret = device_property_read_string_array(psy->dev.parent,
- "supplied-from", (const char **)psy->supplied_from, nval);
- if (ret < 0)
- return ret;
-
- psy->num_supplies = nval;
-
- return 0;
-}
-#endif
struct psy_am_i_supplied_data {
struct power_supply *psy;
diff --git a/drivers/power/supply/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c
index 7454ec0e9963..03ffaad3b53d 100644
--- a/drivers/power/supply/qcom_battmgr.c
+++ b/drivers/power/supply/qcom_battmgr.c
@@ -1258,6 +1258,23 @@ static unsigned int qcom_battmgr_sc8280xp_convert_temp(unsigned int temperature)
return DIV_ROUND_CLOSEST(temperature, 10);
}
+/*
+ * The charger supplies' online state comes from the battery status response,
+ * and the firmware sends no notification of its own when it changes, so
+ * announce the change here. A supply that is not registered yet announces its
+ * state when it is.
+ */
+static void qcom_battmgr_sc8280xp_set_online(struct power_supply *psy,
+ bool *online, bool value)
+{
+ if (*online == value)
+ return;
+
+ *online = value;
+ if (psy)
+ power_supply_changed(psy);
+}
+
static void qcom_battmgr_sc8280xp_callback(struct qcom_battmgr *battmgr,
const struct qcom_battmgr_message *resp,
size_t len)
@@ -1327,9 +1344,12 @@ static void qcom_battmgr_sc8280xp_callback(struct qcom_battmgr *battmgr,
battmgr->status.temperature = qcom_battmgr_sc8280xp_convert_temp(le32_to_cpu(resp->status.temperature));
source = le32_to_cpu(resp->status.charging_source);
- battmgr->ac.online = source == BATTMGR_CHARGING_SOURCE_AC;
- battmgr->usb.online = source == BATTMGR_CHARGING_SOURCE_USB;
- battmgr->wireless.online = source == BATTMGR_CHARGING_SOURCE_WIRELESS;
+ qcom_battmgr_sc8280xp_set_online(battmgr->ac_psy, &battmgr->ac.online,
+ source == BATTMGR_CHARGING_SOURCE_AC);
+ qcom_battmgr_sc8280xp_set_online(battmgr->usb_psy, &battmgr->usb.online,
+ source == BATTMGR_CHARGING_SOURCE_USB);
+ qcom_battmgr_sc8280xp_set_online(battmgr->wls_psy, &battmgr->wireless.online,
+ source == BATTMGR_CHARGING_SOURCE_WIRELESS);
if (battmgr->info.last_full_capacity != 0) {
/*
* 100 * battmgr->status.capacity can overflow a 32bit
diff --git a/drivers/power/supply/qcom_smbx.c b/drivers/power/supply/qcom_smbx.c
index b1cb925581ec..ad2cf516e2de 100644
--- a/drivers/power/supply/qcom_smbx.c
+++ b/drivers/power/supply/qcom_smbx.c
@@ -349,6 +349,9 @@
/* pmi8998 registers represent current in increments of 1/40th of an amp */
#define CURRENT_SCALE_FACTOR 25000
+#define SMB2_FLOAT_VOLTAGE_MIN_UV 3487500
+#define SMB2_FLOAT_VOLTAGE_MAX_UV 4920000
+#define SMB2_FLOAT_VOLTAGE_STEP_UV 7500
/* clang-format on */
enum charger_status {
@@ -375,6 +378,7 @@ struct smb_init_register {
* @base: Base address for smb registers
* @regmap: Register map
* @batt_info: Battery data from DT
+ * @initial_charge_enable: Charging enable state before hardware setup
* @status_change_work: Worker to handle plug/unplug events
* @cable_irq: USB plugin IRQ
* @wakeup_enabled: If the cable IRQ will cause a wakeup
@@ -388,6 +392,7 @@ struct smb_chip {
unsigned int base;
struct regmap *regmap;
struct power_supply_battery_info *batt_info;
+ u8 initial_charge_enable;
struct delayed_work status_change_work;
int cable_irq;
@@ -629,26 +634,20 @@ static int smb_get_prop_health(struct smb_chip *chip, int *val)
return rc;
}
- switch (stat) {
- case CHARGER_ERROR_STATUS_BAT_OV_BIT:
+ if (stat & CHARGER_ERROR_STATUS_BAT_OV_BIT)
*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
- return 0;
- case BAT_TEMP_STATUS_TOO_COLD_BIT:
+ else if (stat & BAT_TEMP_STATUS_TOO_COLD_BIT)
*val = POWER_SUPPLY_HEALTH_COLD;
- return 0;
- case BAT_TEMP_STATUS_TOO_HOT_BIT:
+ else if (stat & BAT_TEMP_STATUS_TOO_HOT_BIT)
*val = POWER_SUPPLY_HEALTH_OVERHEAT;
- return 0;
- case BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT:
+ else if (stat & BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT)
*val = POWER_SUPPLY_HEALTH_COOL;
- return 0;
- case BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT:
+ else if (stat & BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT)
*val = POWER_SUPPLY_HEALTH_WARM;
- return 0;
- default:
+ else
*val = POWER_SUPPLY_HEALTH_GOOD;
- return 0;
- }
+
+ return 0;
}
static int smb_get_property(struct power_supply *psy,
@@ -716,16 +715,22 @@ static irqreturn_t smb_handle_batt_overvoltage(int irq, void *data)
{
struct smb_chip *chip = data;
unsigned int status;
+ int rc;
- regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_2,
- &status);
+ rc = regmap_read(chip->regmap,
+ chip->base + BATTERY_CHARGER_STATUS_2, &status);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read charger status: %d\n", rc);
+ return IRQ_HANDLED;
+ }
if (status & CHARGER_ERROR_STATUS_BAT_OV_BIT) {
/* The hardware stops charging automatically */
dev_err(chip->dev, "battery overvoltage detected\n");
- power_supply_changed(chip->chg_psy);
}
+ power_supply_changed(chip->chg_psy);
+
return IRQ_HANDLED;
}
@@ -757,7 +762,7 @@ static irqreturn_t smb_handle_wdog_bark(int irq, void *data)
power_supply_changed(chip->chg_psy);
- rc = regmap_write(chip->regmap, BARK_BITE_WDOG_PET,
+ rc = regmap_write(chip->regmap, chip->base + BARK_BITE_WDOG_PET,
BARK_BITE_WDOG_PET_BIT);
if (rc < 0)
dev_err(chip->dev, "Couldn't pet the dog rc=%d\n", rc);
@@ -807,11 +812,7 @@ static const struct smb_init_register smb_init_seq[] = {
.mask = SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT |
VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT,
.val = VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT },
- /* Enable charging */
{ .addr = USBIN_OPTIONS_1_CFG, .mask = HVDCP_EN_BIT, .val = 0 },
- { .addr = CHARGING_ENABLE_CMD,
- .mask = CHARGING_ENABLE_CMD_BIT,
- .val = CHARGING_ENABLE_CMD_BIT },
/*
* Match downstream defaults
* CHG_EN_SRC_BIT - charger enable is controlled by software
@@ -832,6 +833,13 @@ static const struct smb_init_register smb_init_seq[] = {
AUTO_RECHG_BIT | EN_ANALOG_DROP_IN_VBATT_BIT |
CHARGER_INHIBIT_BIT,
.val = CHARGER_INHIBIT_BIT },
+ /*
+ * Set the enable command only once CHGR_CFG2 has selected software
+ * control, or a charger its watchdog has disabled stays disabled.
+ */
+ { .addr = CHARGING_ENABLE_CMD,
+ .mask = CHARGING_ENABLE_CMD_BIT,
+ .val = CHARGING_ENABLE_CMD_BIT },
/* STAT pin software override, match downstream. Parallel charging? */
{ .addr = STAT_CFG,
.mask = STAT_SW_OVERRIDE_CFG_BIT,
@@ -902,6 +910,19 @@ static int smb_init_hw(struct smb_chip *chip)
return 0;
}
+static void smb_restore_charge_enable(void *data)
+{
+ struct smb_chip *chip = data;
+ int rc;
+
+ rc = regmap_update_bits(chip->regmap,
+ chip->base + CHARGING_ENABLE_CMD,
+ CHARGING_ENABLE_CMD_BIT,
+ chip->initial_charge_enable);
+ if (rc < 0)
+ dev_err(chip->dev, "Couldn't restore charging state: %d\n", rc);
+}
+
static int smb_init_irq(struct smb_chip *chip, int *irq, const char *name,
irqreturn_t (*handler)(int irq, void *data))
{
@@ -929,6 +950,9 @@ static int smb_probe(struct platform_device *pdev)
struct power_supply_config supply_config = {};
struct power_supply_desc *desc;
struct smb_chip *chip;
+ unsigned int charge_enable;
+ unsigned int float_voltage_sel;
+ int float_voltage_uv;
int rc, irq;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
@@ -959,6 +983,18 @@ static int smb_probe(struct platform_device *pdev)
"Couldn't get usbin_i IIO channel\n");
}
+ rc = regmap_read(chip->regmap, chip->base + CHARGING_ENABLE_CMD,
+ &charge_enable);
+ if (rc < 0)
+ return dev_err_probe(chip->dev, rc,
+ "Couldn't read charging state\n");
+
+ chip->initial_charge_enable = charge_enable & CHARGING_ENABLE_CMD_BIT;
+ rc = devm_add_action_or_reset(chip->dev, smb_restore_charge_enable, chip);
+ if (rc)
+ return dev_err_probe(chip->dev, rc,
+ "Couldn't register charging state rollback\n");
+
rc = smb_init_hw(chip);
if (rc < 0)
return rc;
@@ -993,12 +1029,35 @@ static int smb_probe(struct platform_device *pdev)
return dev_err_probe(chip->dev, rc,
"Failed to init status change work\n");
- rc = (chip->batt_info->voltage_max_design_uv - 3487500) / 7500 + 1;
+ if (power_supply_battery_info_has_prop(chip->batt_info,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX)) {
+ float_voltage_uv =
+ chip->batt_info->constant_charge_voltage_max_uv;
+ } else if (power_supply_battery_info_has_prop(chip->batt_info,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN)) {
+ float_voltage_uv = chip->batt_info->voltage_max_design_uv;
+ } else {
+ dev_warn(chip->dev, "No battery float voltage; preserving hardware setting\n");
+ goto skip_float_voltage;
+ }
+
+ if (float_voltage_uv < SMB2_FLOAT_VOLTAGE_MIN_UV ||
+ float_voltage_uv > SMB2_FLOAT_VOLTAGE_MAX_UV)
+ return dev_err_probe(chip->dev, -EINVAL,
+ "float voltage %d uV outside %d-%d uV\n",
+ float_voltage_uv,
+ SMB2_FLOAT_VOLTAGE_MIN_UV,
+ SMB2_FLOAT_VOLTAGE_MAX_UV);
+
+ float_voltage_sel =
+ (float_voltage_uv - SMB2_FLOAT_VOLTAGE_MIN_UV) /
+ SMB2_FLOAT_VOLTAGE_STEP_UV;
rc = regmap_update_bits(chip->regmap, chip->base + FLOAT_VOLTAGE_CFG,
- FLOAT_VOLTAGE_SETTING_MASK, rc);
+ FLOAT_VOLTAGE_SETTING_MASK, float_voltage_sel);
if (rc < 0)
return dev_err_probe(chip->dev, rc, "Couldn't set vbat max\n");
+skip_float_voltage:
rc = smb_init_irq(chip, &irq, "bat-ov", smb_handle_batt_overvoltage);
if (rc < 0)
return rc;
@@ -1022,6 +1081,8 @@ static int smb_probe(struct platform_device *pdev)
if (rc < 0)
return dev_err_probe(chip->dev, rc, "Couldn't set wake irq\n");
+ devm_remove_action(chip->dev, smb_restore_charge_enable, chip);
+
platform_set_drvdata(pdev, chip);
/* Initialise charger state */
diff --git a/drivers/pps/generators/pps_gen.c b/drivers/pps/generators/pps_gen.c
index 69a470fa164d..eda7fd933bec 100644
--- a/drivers/pps/generators/pps_gen.c
+++ b/drivers/pps/generators/pps_gen.c
@@ -69,17 +69,28 @@ static long pps_gen_cdev_ioctl(struct file *file,
if (ret)
return -EFAULT;
- ret = pps_gen->info->enable(pps_gen, status);
- if (ret)
- return ret;
- pps_gen->enabled = status;
+ scoped_guard(mutex, &pps_gen->info_lock) {
+ if (!pps_gen->info)
+ return -ENODEV;
+
+ ret = pps_gen->info->enable(pps_gen, status);
+ if (ret)
+ return ret;
+ pps_gen->enabled = status;
+ }
break;
case PPS_GEN_USESYSTEMCLOCK:
dev_dbg(&pps_gen->dev, "PPS_GEN_USESYSTEMCLOCK\n");
- ret = put_user(pps_gen->info->use_system_clock, uiuarg);
+ scoped_guard(mutex, &pps_gen->info_lock) {
+ if (!pps_gen->info)
+ return -ENODEV;
+ status = pps_gen->info->use_system_clock;
+ }
+
+ ret = put_user(status, uiuarg);
if (ret)
return -EFAULT;
@@ -212,6 +223,24 @@ static void pps_gen_unregister_cdev(struct pps_gen_device *pps_gen)
{
pr_debug("unregistering pps-gen%d\n", pps_gen->id);
cdev_device_del(&pps_gen->cdev, &pps_gen->dev);
+
+ /*
+ * An open file keeps pps_gen around, but the driver may free info as
+ * soon as we return. The sysfs files are gone now, so wait for the
+ * ioctls using info and make later ones fail.
+ *
+ * The driver may have stopped the generator before calling us, but
+ * userspace could have enabled it again since. Nothing can enable it
+ * after this point, so stop it here for good.
+ */
+ scoped_guard(mutex, &pps_gen->info_lock) {
+ if (pps_gen->enabled) {
+ pps_gen->info->enable(pps_gen, false);
+ pps_gen->enabled = false;
+ }
+ pps_gen->info = NULL;
+ }
+
put_device(&pps_gen->dev);
}
@@ -243,6 +272,7 @@ struct pps_gen_device *pps_gen_register_source(const struct pps_gen_source_info
pps_gen->info = info;
pps_gen->enabled = false;
+ mutex_init(&pps_gen->info_lock);
init_waitqueue_head(&pps_gen->queue);
spin_lock_init(&pps_gen->lock);
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
index 2533c95b0ba9..c02f69de44dc 100644
--- a/drivers/pwm/pwm-tiehrpwm.c
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -358,6 +358,9 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
/* Disable clock on PWM disable */
pm_runtime_put_sync(pwmchip_parent(chip));
+
+ /* A disabled channel must not constrain the sibling's period */
+ pc->period_cycles[pwm->hwpwm] = 0;
}
static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index eae756c82c26..bb0b7268b2b5 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2110,6 +2110,7 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
if (r == rdev) {
dev_err(dev, "Supply for %s (%s) resolved to itself\n",
rdev->desc->name, rdev->supply_name);
+ put_device(&rdev->dev);
if (!have_full_constraints()) {
ret = -EINVAL;
goto out;
@@ -5659,6 +5660,7 @@ static void regulator_resolve_coupling(struct regulator_dev *rdev)
if (c_rdev->coupling_desc.coupler != coupler) {
rdev_err(rdev, "coupler mismatch with %s\n",
rdev_get_name(c_rdev));
+ put_device(&c_rdev->dev);
return;
}
@@ -5705,6 +5707,8 @@ static void regulator_remove_coupling(struct regulator_dev *rdev)
c_desc->coupled_rdevs[i] = NULL;
c_desc->n_resolved--;
+
+ put_device(&c_rdev->dev);
}
if (coupler && coupler->detach_regulator) {
diff --git a/drivers/regulator/rt6190-regulator.c b/drivers/regulator/rt6190-regulator.c
index 3883440295ed..bf99e18f6d5b 100644
--- a/drivers/regulator/rt6190-regulator.c
+++ b/drivers/regulator/rt6190-regulator.c
@@ -109,19 +109,27 @@ static int rt6190_out_enable(struct regulator_dev *rdev)
ret = regmap_raw_read(regmap, RT6190_REG_OUTV, out_cfg,
sizeof(out_cfg));
if (ret)
- return ret;
+ goto err_pm_put;
ret = regulator_enable_regmap(rdev);
if (ret)
- return ret;
+ goto err_pm_put;
ret = regmap_raw_write(regmap, RT6190_REG_OUTV, out_cfg,
sizeof(out_cfg));
if (ret)
- return ret;
+ goto err_pm_put;
+
+ ret = regmap_update_bits(regmap, RT6190_REG_SET5, RT6190_ENGCP_MASK,
+ RT6190_ENGCP_MASK);
+ if (ret)
+ goto err_pm_put;
+
+ return 0;
- return regmap_update_bits(regmap, RT6190_REG_SET5, RT6190_ENGCP_MASK,
- RT6190_ENGCP_MASK);
+err_pm_put:
+ pm_runtime_put(data->dev);
+ return ret;
}
static int rt6190_out_disable(struct regulator_dev *rdev)
diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c
index 94177e416047..da3cddbe7d4c 100644
--- a/drivers/remoteproc/remoteproc_elf_loader.c
+++ b/drivers/remoteproc/remoteproc_elf_loader.c
@@ -46,8 +46,9 @@ int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw)
struct elf32_hdr *ehdr;
u32 elf_shdr_get_size;
u64 phoff, shoff;
+ size_t shend;
char class;
- u16 phnum;
+ u16 phnum, shnum, shstrndx;
if (!fw) {
dev_err(dev, "failed to load %s\n", name);
@@ -90,9 +91,27 @@ int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw)
phoff = elf_hdr_get_e_phoff(class, fw->data);
shoff = elf_hdr_get_e_shoff(class, fw->data);
phnum = elf_hdr_get_e_phnum(class, fw->data);
+ shnum = elf_hdr_get_e_shnum(class, fw->data);
+ shstrndx = elf_hdr_get_e_shstrndx(class, fw->data);
elf_shdr_get_size = elf_size_of_shdr(class);
- if (fw->size < shoff + elf_shdr_get_size) {
+ /* keeps shoff in size_t range for the two bounds below */
+ if (shoff > fw->size) {
+ dev_err(dev, "Section header table is out of bounds\n");
+ return -EINVAL;
+ }
+
+ if (shnum) {
+ shend = size_add(size_mul(elf_shdr_get_size, shnum), shoff);
+ if (shend > fw->size) {
+ dev_err(dev, "Section headers are out of bounds\n");
+ return -EINVAL;
+ }
+ }
+
+ /* find_table() reads the header at shstrndx even with no sections */
+ shend = size_add(size_mul(elf_shdr_get_size, (size_t)shstrndx + 1), shoff);
+ if (shend > fw->size) {
dev_err(dev, "Image is too small\n");
return -EINVAL;
}
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index b537f40654b7..9dfa1f4ef7ff 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -753,7 +753,9 @@ static u32 rtc_handler(void *context)
unsigned char rtc_intr;
unsigned long flags;
-
+ /* Quiesce this event before rtc->irqwork can arm the next one. */
+ acpi_clear_event(ACPI_EVENT_RTC);
+ acpi_disable_event(ACPI_EVENT_RTC, 0);
/*
* Always update rtc irq when ACPI is used as RTC Alarm.
* Or else, ACPI SCI is enabled during suspend/resume only,
@@ -776,8 +778,6 @@ static u32 rtc_handler(void *context)
}
pm_wakeup_hard_event(dev);
- acpi_clear_event(ACPI_EVENT_RTC);
- acpi_disable_event(ACPI_EVENT_RTC, 0);
return ACPI_INTERRUPT_HANDLED;
}
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 7083d927820f..c23f04bdd95f 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -531,7 +531,7 @@ static int pcf8563_probe(struct i2c_client *client)
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pcf8563_irq,
IRQF_SHARED | IRQF_ONESHOT | irqflags,
- pcf8563_driver.driver.name, client);
+ pcf8563_driver.driver.name, pcf8563);
if (err) {
dev_err(&client->dev, "unable to request IRQ %d\n",
client->irq);
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index d35f93451ee9..3c9e736d45e7 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -601,7 +601,8 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
data_length, session->max_burst);
data_offset = be32_to_cpu(rhdr->data_offset);
- if (data_offset + data_length > task->sc->sdb.length) {
+ if (data_offset > task->sc->sdb.length ||
+ data_length > task->sc->sdb.length - data_offset) {
iscsi_conn_printk(KERN_ERR, conn,
"invalid R2T with data len %u at offset %u "
"and total length %d\n", data_length,
diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c
index 3dde703b7766..137a7a94d9cb 100644
--- a/drivers/sh/intc/core.c
+++ b/drivers/sh/intc/core.c
@@ -277,9 +277,6 @@ int __init register_intc_controller(struct intc_desc *desc)
k += save_reg(d, k, hw->prio_regs[i].set_reg, smp);
k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp);
}
-
- sort(d->prio, hw->nr_prio_regs, sizeof(*d->prio),
- intc_handle_int_cmp, NULL);
}
if (hw->sense_regs) {
@@ -290,9 +287,6 @@ int __init register_intc_controller(struct intc_desc *desc)
for (i = 0; i < hw->nr_sense_regs; i++)
k += save_reg(d, k, hw->sense_regs[i].reg, 0);
-
- sort(d->sense, hw->nr_sense_regs, sizeof(*d->sense),
- intc_handle_int_cmp, NULL);
}
if (hw->subgroups)
@@ -360,6 +354,11 @@ int __init register_intc_controller(struct intc_desc *desc)
}
}
+ sort(d->prio, d->nr_prio, sizeof(*d->prio),
+ intc_handle_int_cmp, NULL);
+ sort(d->sense, d->nr_sense, sizeof(*d->sense),
+ intc_handle_int_cmp, NULL);
+
intc_subgroup_init(desc, d);
/* enable bits matching force_enable after registering irqs */
diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c
index 6dc0549f7900..3d3302c900ca 100644
--- a/drivers/sh/maple/maple.c
+++ b/drivers/sh/maple/maple.c
@@ -863,10 +863,10 @@ static int __init maple_bus_init(void)
kmem_cache_destroy(maple_queue_cache);
cleanup_bothirqs:
- free_irq(HW_EVENT_VSYNC, 0);
+ free_irq(HW_EVENT_VSYNC, &maple_unsupported_device);
cleanup_irq:
- free_irq(HW_EVENT_MAPLE_DMA, 0);
+ free_irq(HW_EVENT_MAPLE_DMA, &maple_unsupported_device);
cleanup_dma:
free_pages((unsigned long) maple_sendbuf, MAPLE_DMA_PAGES);
diff --git a/drivers/slimbus/messaging.c b/drivers/slimbus/messaging.c
index e2dbe4a66b70..ee127ef42046 100644
--- a/drivers/slimbus/messaging.c
+++ b/drivers/slimbus/messaging.c
@@ -139,7 +139,7 @@ int slim_do_transfer(struct slim_controller *ctrl, struct slim_msg_txn *txn)
if (need_tid) {
ret = slim_alloc_txn_tid(ctrl, txn);
if (ret)
- return ret;
+ goto slim_xfer_err;
if (!txn->msg->comp)
txn->comp = &done;
diff --git a/drivers/soc/fsl/dpio/dpio-driver.c b/drivers/soc/fsl/dpio/dpio-driver.c
index 9e3fddd8f5a9..2962e66e2e41 100644
--- a/drivers/soc/fsl/dpio/dpio-driver.c
+++ b/drivers/soc/fsl/dpio/dpio-driver.c
@@ -88,6 +88,7 @@ static void unregister_dpio_irq_handlers(struct fsl_mc_device *dpio_dev)
/* clear the affinity hint */
irq_set_affinity_hint(irq->virq, NULL);
+ devm_free_irq(&dpio_dev->dev, irq->virq, &dpio_dev->dev);
}
static int register_dpio_irq_handlers(struct fsl_mc_device *dpio_dev, int cpu)
@@ -248,9 +249,9 @@ static int dpaa2_dpio_probe(struct fsl_mc_device *dpio_dev)
return 0;
-err_dpaa2_io_create:
- unregister_dpio_irq_handlers(dpio_dev);
err_register_dpio_irq:
+ dpaa2_io_down(priv->io);
+err_dpaa2_io_create:
fsl_mc_free_irqs(dpio_dev);
err_allocate_irqs:
dpio_disable(dpio_dev->mc_io, 0, dpio_dev->mc_handle);
@@ -280,10 +281,10 @@ static void dpaa2_dpio_remove(struct fsl_mc_device *dpio_dev)
priv = dev_get_drvdata(dev);
cpu = dpaa2_io_get_cpu(priv->io);
- dpaa2_io_down(priv->io);
-
dpio_teardown_irqs(dpio_dev);
+ dpaa2_io_down(priv->io);
+
cpumask_set_cpu(cpu, cpus_unused_mask);
err = dpio_open(dpio_dev->mc_io, 0, dpio_dev->obj_desc.id,
diff --git a/drivers/soc/fsl/qe/tsa.c b/drivers/soc/fsl/qe/tsa.c
index 4a88e54d25b9..502e2ffe8689 100644
--- a/drivers/soc/fsl/qe/tsa.c
+++ b/drivers/soc/fsl/qe/tsa.c
@@ -865,12 +865,12 @@ static int tsa_of_parse_tdms(struct tsa *tsa, struct device_node *np)
clk_put(tsa->tdm[i].l1rclk_clk);
}
if (tsa->tdm[i].l1tsync_clk) {
- clk_disable_unprepare(tsa->tdm[i].l1rsync_clk);
- clk_put(tsa->tdm[i].l1rsync_clk);
+ clk_disable_unprepare(tsa->tdm[i].l1tsync_clk);
+ clk_put(tsa->tdm[i].l1tsync_clk);
}
if (tsa->tdm[i].l1tclk_clk) {
- clk_disable_unprepare(tsa->tdm[i].l1rclk_clk);
- clk_put(tsa->tdm[i].l1rclk_clk);
+ clk_disable_unprepare(tsa->tdm[i].l1tclk_clk);
+ clk_put(tsa->tdm[i].l1tclk_clk);
}
}
return ret;
@@ -1039,12 +1039,12 @@ static void tsa_remove(struct platform_device *pdev)
clk_put(tsa->tdm[i].l1rclk_clk);
}
if (tsa->tdm[i].l1tsync_clk) {
- clk_disable_unprepare(tsa->tdm[i].l1rsync_clk);
- clk_put(tsa->tdm[i].l1rsync_clk);
+ clk_disable_unprepare(tsa->tdm[i].l1tsync_clk);
+ clk_put(tsa->tdm[i].l1tsync_clk);
}
if (tsa->tdm[i].l1tclk_clk) {
- clk_disable_unprepare(tsa->tdm[i].l1rclk_clk);
- clk_put(tsa->tdm[i].l1rclk_clk);
+ clk_disable_unprepare(tsa->tdm[i].l1tclk_clk);
+ clk_put(tsa->tdm[i].l1tclk_clk);
}
}
}
diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
index 2fa6a45904e9..f85cac34c726 100644
--- a/drivers/soc/qcom/qcom-geni-se.c
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -991,25 +991,28 @@ EXPORT_SYMBOL_GPL(geni_icc_disable);
* @se: Pointer to the serial engine structure.
* @fw: Pointer to the firmware image.
* @protocol: Expected serial engine protocol type.
+ * @fw_size_out: Non-NULL output parameter; receives the rounded, validated
+ * firmware word count on success.
*
* Identifies the appropriate firmware image or configuration required for a
* 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.
+ * Return: pointer to a valid 'const struct se_fw_hdr' if found, or NULL otherwise.
*/
-static struct se_fw_hdr *geni_find_protocol_fw(struct geni_se *se, const struct firmware *fw,
- enum geni_se_protocol_type protocol)
+static const struct se_fw_hdr *geni_find_protocol_fw(struct geni_se *se, const struct firmware *fw,
+ enum geni_se_protocol_type protocol,
+ u32 *fw_size_out)
{
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;
+ const struct se_fw_hdr *sefw;
u32 fw_end, cfg_idx_end, cfg_val_end;
u32 prog_ram_depth;
- u16 fw_size;
+ u32 fw_size;
int i;
if (!fw || fw->size < sizeof(struct elf32_hdr))
@@ -1048,24 +1051,23 @@ static struct se_fw_hdr *geni_find_protocol_fw(struct geni_se *se, const struct
if (phdr->p_filesz < sizeof(struct se_fw_hdr))
continue;
- sefw = (struct se_fw_hdr *)(fw->data + phdr->p_offset);
+ sefw = (const struct se_fw_hdr *)(fw->data + phdr->p_offset);
fw_size = le16_to_cpu(sefw->fw_size_in_items);
- fw_end = le16_to_cpu(sefw->fw_offset) + fw_size * sizeof(u32);
- cfg_idx_end = le16_to_cpu(sefw->cfg_idx_offset) +
- le16_to_cpu(sefw->cfg_size_in_items) * sizeof(u8);
- cfg_val_end = le16_to_cpu(sefw->cfg_val_offset) +
- le16_to_cpu(sefw->cfg_size_in_items) * sizeof(u32);
if (le32_to_cpu(sefw->magic) != SE_MAGIC_NUM || le32_to_cpu(sefw->version) != 1)
continue;
- if (le32_to_cpu(sefw->serial_protocol) != protocol)
+ if (le16_to_cpu(sefw->serial_protocol) != protocol)
continue;
- if (fw_size % 2 != 0) {
+ if (fw_size % 2 != 0)
fw_size++;
- sefw->fw_size_in_items = cpu_to_le16(fw_size);
- }
+
+ fw_end = le16_to_cpu(sefw->fw_offset) + fw_size * sizeof(u32);
+ cfg_idx_end = le16_to_cpu(sefw->cfg_idx_offset) +
+ le16_to_cpu(sefw->cfg_size_in_items) * sizeof(u8);
+ cfg_val_end = le16_to_cpu(sefw->cfg_val_offset) +
+ le16_to_cpu(sefw->cfg_size_in_items) * sizeof(u32);
prog_ram_depth = FIELD_GET(PROG_RAM_DEPTH_MSK,
readl_relaxed(se->base + SE_HW_PARAM_2));
@@ -1081,6 +1083,7 @@ static struct se_fw_hdr *geni_find_protocol_fw(struct geni_se *se, const struct
continue;
}
+ *fw_size_out = fw_size;
return sefw;
}
@@ -1191,17 +1194,17 @@ static int geni_load_se_fw(struct geni_se *se, const struct firmware *fw,
{
const u32 *fw_data, *cfg_val_arr;
const u8 *cfg_idx_arr;
- u32 i, reg_value;
+ u32 i, reg_value, fw_size_in_items;
int ret;
- struct se_fw_hdr *hdr;
+ const struct se_fw_hdr *hdr;
- hdr = geni_find_protocol_fw(se, fw, protocol);
+ hdr = geni_find_protocol_fw(se, fw, protocol, &fw_size_in_items);
if (!hdr)
return -EINVAL;
- fw_data = (const u32 *)((u8 *)hdr + le16_to_cpu(hdr->fw_offset));
+ fw_data = (const u32 *)((const u8 *)hdr + le16_to_cpu(hdr->fw_offset));
cfg_idx_arr = (const u8 *)hdr + le16_to_cpu(hdr->cfg_idx_offset);
- cfg_val_arr = (const u32 *)((u8 *)hdr + le16_to_cpu(hdr->cfg_val_offset));
+ cfg_val_arr = (const u32 *)((const u8 *)hdr + le16_to_cpu(hdr->cfg_val_offset));
ret = geni_icc_set_bw(se);
if (ret)
@@ -1272,7 +1275,7 @@ static int geni_load_se_fw(struct geni_se *se, const struct firmware *fw,
/* Program RAM address space. */
memcpy_toio(se->base + SE_GENI_CFG_RAMN, fw_data,
- le16_to_cpu(hdr->fw_size_in_items) * sizeof(u32));
+ fw_size_in_items * sizeof(u32));
/* Put default values on GENI's output pads. */
writel_relaxed(0x1, se->base + GENI_FORCE_DEFAULT_REG);
diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
index 0fcbc08633f6..748be7ff2aff 100644
--- a/drivers/soc/samsung/exynos-pmu.c
+++ b/drivers/soc/samsung/exynos-pmu.c
@@ -47,6 +47,8 @@ struct exynos_pmu_context {
unsigned long *in_cpuhp;
bool sys_insuspend;
bool sys_inreboot;
+ int cpuhp_prepare_state;
+ int cpuhp_online_state;
};
void __iomem *pmu_base_addr;
@@ -589,16 +591,55 @@ static int setup_cpuhp_and_cpuidle(struct device *dev)
gs101_cpuhp_pmu_online(cpu);
/* register CPU hotplug callbacks */
- cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "soc/exynos-pmu:prepare",
- gs101_cpuhp_pmu_online, NULL);
+ pmu_context->cpuhp_prepare_state = CPUHP_INVALID;
+ pmu_context->cpuhp_online_state = CPUHP_INVALID;
- cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/exynos-pmu:online",
- NULL, gs101_cpuhp_pmu_offline);
+ ret = cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "soc/exynos-pmu:prepare",
+ gs101_cpuhp_pmu_online, NULL);
+ if (ret < 0)
+ return ret;
+
+ pmu_context->cpuhp_prepare_state = ret;
+
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/exynos-pmu:online",
+ NULL, gs101_cpuhp_pmu_offline);
+ if (ret < 0)
+ goto clean_cpuhp_state_prepare;
+
+ pmu_context->cpuhp_online_state = ret;
/* register CPU PM notifiers for cpuidle */
- cpu_pm_register_notifier(&gs101_cpu_pm_notifier);
- register_reboot_notifier(&exynos_cpupm_reboot_nb);
- return 0;
+ ret = cpu_pm_register_notifier(&gs101_cpu_pm_notifier);
+ if (ret)
+ goto clean_cpuhp_states;
+
+ ret = register_reboot_notifier(&exynos_cpupm_reboot_nb);
+ if (!ret)
+ /* Success */
+ return ret;
+
+ cpu_pm_unregister_notifier(&gs101_cpu_pm_notifier);
+
+clean_cpuhp_states:
+ cpuhp_remove_state(pmu_context->cpuhp_online_state);
+ pmu_context->cpuhp_online_state = CPUHP_INVALID;
+
+clean_cpuhp_state_prepare:
+ cpuhp_remove_state(pmu_context->cpuhp_prepare_state);
+ pmu_context->cpuhp_prepare_state = CPUHP_INVALID;
+
+ return ret;
+}
+
+static void destroy_cpuhp_and_cpuidle(void)
+{
+ cpu_pm_unregister_notifier(&gs101_cpu_pm_notifier);
+ unregister_reboot_notifier(&exynos_cpupm_reboot_nb);
+
+ if (pmu_context->cpuhp_prepare_state != CPUHP_INVALID)
+ cpuhp_remove_state(pmu_context->cpuhp_prepare_state);
+ if (pmu_context->cpuhp_online_state != CPUHP_INVALID)
+ cpuhp_remove_state(pmu_context->cpuhp_online_state);
}
static int exynos_pmu_probe(struct platform_device *pdev)
@@ -669,8 +710,12 @@ static int exynos_pmu_probe(struct platform_device *pdev)
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, exynos_pmu_devs,
ARRAY_SIZE(exynos_pmu_devs), NULL, 0, NULL);
- if (ret)
+ if (ret) {
+ if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_cpuhp)
+ destroy_cpuhp_and_cpuidle();
+
return ret;
+ }
if (devm_of_platform_populate(dev))
dev_err(dev, "Error populating children, reboot and poweroff might not work properly\n");
diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c
index 71a6e5c475b0..d3953de255be 100644
--- a/drivers/spi/spi-amd.c
+++ b/drivers/spi/spi-amd.c
@@ -322,6 +322,10 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
list_for_each_entry(xfer, &message->transfers,
transfer_list) {
+ /* Skip zero-length transfers to avoid len underflow below */
+ if (!xfer->len)
+ continue;
+
if (xfer->speed_hz)
amd_set_spi_freq(amd_spi, xfer->speed_hz);
else
@@ -335,6 +339,10 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
xfer->len--;
}
tx_len += xfer->len;
+ if (tx_len + rx_len > AMD_SPI_FIFO_SIZE) {
+ message->status = -EMSGSIZE;
+ goto fin_msg;
+ }
/* Write data into the FIFO. */
for (i = 0; i < xfer->len; i++)
@@ -344,8 +352,13 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
}
/* Store no. of bytes to be received from FIFO */
- if (xfer->rx_buf)
+ if (xfer->rx_buf) {
rx_len += xfer->len;
+ if (tx_len + rx_len > AMD_SPI_FIFO_SIZE) {
+ message->status = -EMSGSIZE;
+ goto fin_msg;
+ }
+ }
}
if (!buf) {
diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c
index ae5545be0a45..de333a4a3200 100644
--- a/drivers/spi/spi-bcm-qspi.c
+++ b/drivers/spi/spi-bcm-qspi.c
@@ -1197,8 +1197,9 @@ static int bcm_qspi_exec_mem_op(struct spi_mem *mem,
u32 addr = 0, len;
u_char *buf;
+ /* cmd[] holds 1 opcode + 4 address + 1 dummy byte at most */
if (!op->data.nbytes || !op->addr.nbytes || op->addr.nbytes > 4 ||
- op->data.dir != SPI_MEM_DATA_IN)
+ op->dummy.nbytes > 1 || op->data.dir != SPI_MEM_DATA_IN)
return -EOPNOTSUPP;
buf = op->data.buf.in;
diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c
index 192cc5ef65fb..61f70da3959e 100644
--- a/drivers/spi/spi-bcm2835.c
+++ b/drivers/spi/spi-bcm2835.c
@@ -1297,7 +1297,9 @@ static int bcm2835_spi_setup(struct spi_device *spi)
}
for (i = 0; i < ARRAY_SIZE(pinctrl_compats); i++) {
- if (of_find_compatible_node(NULL, NULL, pinctrl_compats[i]))
+ struct device_node *np __free(device_node) =
+ of_find_compatible_node(NULL, NULL, pinctrl_compats[i]);
+ if (np)
break;
}
diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 6dcba0e0ddaa..c3676b21e816 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -268,6 +268,7 @@
#define MRVL_XFER_FUNC_START BIT(0)
#define MRVL_XFER_QWORD_COUNT 32
#define MRVL_XFER_QWORD_BYTECOUNT 8
+#define MRVL_XFER_MAX_LEN (MRVL_XFER_QWORD_COUNT * MRVL_XFER_QWORD_BYTECOUNT)
#define MRVL_XSPI_POLL_TIMEOUT_US 1000
#define MRVL_XSPI_POLL_DELAY_US 10
@@ -1047,7 +1048,7 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
struct spi_device *spi = m->spi;
struct spi_transfer *t = NULL;
- const unsigned int max_len = MRVL_XFER_QWORD_BYTECOUNT * MRVL_XFER_QWORD_COUNT;
+ const unsigned int max_len = MRVL_XFER_MAX_LEN;
int current_transfer_len;
int cs = spi_get_chipselect(spi, 0);
int cs_change = 0;
@@ -1070,13 +1071,16 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
list_for_each_entry(t, &m->transfers, transfer_list) {
u8 *txd = (u8 *) t->tx_buf;
u8 *rxd = (u8 *) t->rx_buf;
- u8 data[10];
+ u8 data[MRVL_XFER_MAX_LEN] = {0};
u32 cmd_regs[6];
if (!txd)
txd = data;
- cdns_xspi->in_buffer = txd + 1;
+ if (rxd)
+ cdns_xspi->in_buffer = rxd;
+ else
+ cdns_xspi->in_buffer = data;
cdns_xspi->out_buffer = txd + 1;
while (t->len) {
@@ -1101,8 +1105,10 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
if (!cdns_xspi_is_stig_ready(cdns_xspi, true))
return -EIO;
- cdns_xspi->in_buffer += current_transfer_len;
- cdns_xspi->out_buffer += current_transfer_len;
+ if (rxd)
+ cdns_xspi->in_buffer += current_transfer_len;
+ if (t->tx_buf)
+ cdns_xspi->out_buffer += current_transfer_len;
}
if (rxd) {
diff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c
index 3eaa8f176f63..170240a0dde8 100644
--- a/drivers/spi/spi-ch341.c
+++ b/drivers/spi/spi-ch341.c
@@ -189,6 +189,12 @@ static int ch341_probe(struct usb_interface *intf,
ctrl->transfer_one = ch341_transfer_one;
ctrl->set_cs = ch341_set_cs;
ctrl->auto_runtime_pm = false;
+ /*
+ * The CH341A SPI_STREAM command clocks data in both directions on
+ * every transfer, so the driver always needs a TX and an RX buffer;
+ * let the SPI core supply dummy buffers for half-duplex transfers.
+ */
+ ctrl->flags = SPI_CONTROLLER_MUST_TX | SPI_CONTROLLER_MUST_RX;
usb_set_intfdata(intf, ch341);
diff --git a/drivers/spi/spi-jcore.c b/drivers/spi/spi-jcore.c
index e37ca22e04ba..2bde899eb2b4 100644
--- a/drivers/spi/spi-jcore.c
+++ b/drivers/spi/spi-jcore.c
@@ -84,6 +84,8 @@ static void jcore_spi_baudrate(struct jcore_spi *hw, int speed)
{
if (speed == hw->speed_hz)
return;
+ if (speed <= 0)
+ speed = 1;
hw->speed_hz = speed;
if (speed >= hw->clock_freq / 2)
hw->speed_reg = 0;
diff --git a/drivers/spi/spi-ljca.c b/drivers/spi/spi-ljca.c
index 3f412cf8f1cd..0bc858993311 100644
--- a/drivers/spi/spi-ljca.c
+++ b/drivers/spi/spi-ljca.c
@@ -105,7 +105,13 @@ static int ljca_spi_read_write(struct ljca_spi_dev *ljca_spi, const u8 *w_data,
(u8 *)r_packet, LJCA_SPI_BUF_SIZE);
if (ret < 0)
return ret;
- else if (ret < sizeof(*r_packet) || r_packet->len <= 0)
+
+ if (ret < sizeof(*r_packet))
+ return -EIO;
+
+ /* write acks carry no payload */
+ if (!r_packet->len || r_packet->len > len ||
+ (r_data && ret < sizeof(*r_packet) + r_packet->len))
return -EIO;
if (r_data)
diff --git a/drivers/spi/spi-npcm-fiu.c b/drivers/spi/spi-npcm-fiu.c
index cccd17f24775..79328ae89fbb 100644
--- a/drivers/spi/spi-npcm-fiu.c
+++ b/drivers/spi/spi-npcm-fiu.c
@@ -343,6 +343,10 @@ static int npcm_fiu_uma_read(struct spi_mem *mem,
u32 val;
u32 i;
+ /* data_reg holds at most 16 bytes */
+ if (data_size > sizeof(data_reg))
+ return -EOPNOTSUPP;
+
regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS,
NPCM_FIU_UMA_CTS_DEV_NUM,
(spi_get_chipselect(mem->spi, 0) <<
@@ -398,6 +402,10 @@ static int npcm_fiu_uma_write(struct spi_mem *mem,
u32 val;
u32 i;
+ /* data_reg holds at most 16 bytes */
+ if (data_size > sizeof(data_reg))
+ return -EOPNOTSUPP;
+
regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS,
NPCM_FIU_UMA_CTS_DEV_NUM,
(spi_get_chipselect(mem->spi, 0) <<
diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c
index 919e847c061d..567485785ec9 100644
--- a/drivers/spi/spi-qpic-snand.c
+++ b/drivers/spi/spi-qpic-snand.c
@@ -391,6 +391,8 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
dev_dbg(snandc->dev, "ECC strength: %u bits per %u bytes\n",
ecc_cfg->strength, ecc_cfg->step_size);
+ snandc->qspi->ecc = ecc_cfg;
+
return 0;
err_free_ecc_cfg:
@@ -407,6 +409,7 @@ static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand)
kfree(snandc->qspi->oob_buf);
snandc->qspi->oob_buf = NULL;
+ snandc->qspi->ecc = NULL;
kfree(ecc_cfg);
}
diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c
index 7a6ee93be9bd..e5d483a9d38b 100644
--- a/drivers/spi/spi-stm32.c
+++ b/drivers/spi/spi-stm32.c
@@ -2454,6 +2454,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
gen_pool_free(spi->sram_pool,
(unsigned long)spi->sram_rx_buf,
spi->sram_rx_buf_size);
+ spi->sram_rx_buf = NULL;
dev_warn(&pdev->dev,
"failed to request rx mdma channel, DMA only\n");
}
@@ -2491,7 +2492,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
if (spi->mdma_rx)
dma_release_channel(spi->mdma_rx);
err_pool_free:
- if (spi->sram_pool)
+ if (spi->sram_rx_buf)
gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf,
spi->sram_rx_buf_size);
err_dma_release:
diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c
index 406fd9d5337e..c9588fd1d123 100644
--- a/drivers/spi/spi-zynq-qspi.c
+++ b/drivers/spi/spi-zynq-qspi.c
@@ -229,11 +229,10 @@ static bool zynq_qspi_supports_op(struct spi_mem *mem,
return false;
/*
- * The number of address bytes should be equal to or less than 3 bytes.
+ * The number of address bytes should be equal to or less than 4 bytes.
*/
- if (op->addr.nbytes > 3)
+ if (op->addr.nbytes > 4)
return false;
-
return true;
}
@@ -526,6 +525,7 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
struct zynq_qspi *xqspi = spi_controller_get_devdata(mem->spi->controller);
+ u8 opaddr[4] __aligned(4);
int err = 0, i;
u8 *tmpbuf;
@@ -547,12 +547,13 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
}
if (op->addr.nbytes) {
- for (i = 0; i < op->addr.nbytes; i++) {
- xqspi->txbuf[i] = op->addr.val >>
+ for (i = 0; i < op->addr.nbytes; i++)
+ opaddr[i] = op->addr.val >>
(8 * (op->addr.nbytes - i - 1));
- }
reinit_completion(&xqspi->data_completion);
+ xqspi->txbuf = opaddr;
+
xqspi->rxbuf = NULL;
xqspi->tx_bytes = op->addr.nbytes;
xqspi->rx_bytes = op->addr.nbytes;
diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c
index 78bf8c4031e9..7d2844d9bcd9 100644
--- a/drivers/staging/greybus/uart.c
+++ b/drivers/staging/greybus/uart.c
@@ -53,7 +53,6 @@ struct gb_tty {
spinlock_t read_lock;
spinlock_t write_lock;
struct async_icount iocount;
- struct async_icount oldcount;
wait_queue_head_t wioctl;
struct mutex mutex;
u8 ctrlin; /* input control lines */
@@ -643,11 +642,16 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg)
if (!(arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD)))
return -EINVAL;
- do {
+ spin_lock_irq(&gb_tty->read_lock);
+ old = gb_tty->iocount;
+ spin_unlock_irq(&gb_tty->read_lock);
+
+ add_wait_queue(&gb_tty->wioctl, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
spin_lock_irq(&gb_tty->read_lock);
- old = gb_tty->oldcount;
new = gb_tty->iocount;
- gb_tty->oldcount = new;
spin_unlock_irq(&gb_tty->read_lock);
if ((arg & TIOCM_DSR) && (old.dsr != new.dsr))
@@ -657,18 +661,20 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg)
if ((arg & TIOCM_RI) && (old.rng != new.rng))
break;
- add_wait_queue(&gb_tty->wioctl, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- remove_wait_queue(&gb_tty->wioctl, &wait);
if (gb_tty->disconnected) {
- if (arg & TIOCM_CD)
- break;
retval = -ENODEV;
- } else if (signal_pending(current)) {
+ break;
+ }
+
+ schedule();
+
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
+ break;
}
- } while (!retval);
+ }
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&gb_tty->wioctl, &wait);
return retval;
}
diff --git a/drivers/staging/media/av7110/sp8870.c b/drivers/staging/media/av7110/sp8870.c
index 0c813860f5b2..b5b49f1ef5e7 100644
--- a/drivers/staging/media/av7110/sp8870.c
+++ b/drivers/staging/media/av7110/sp8870.c
@@ -315,7 +315,6 @@ static int sp8870_init(struct dvb_frontend *fe)
sp8870_wake_up(state);
if (state->initialised)
return 0;
- state->initialised = 1;
dprintk("initialising frontend...\n");
@@ -353,6 +352,8 @@ static int sp8870_init(struct dvb_frontend *fe)
sp8870_writereg(state, 0x0D00, 0x010);
sp8870_writereg(state, 0x0D01, 0x000);
+ state->initialised = 1;
+
return 0;
}
@@ -401,7 +402,7 @@ static int sp8870_read_ber(struct dvb_frontend *fe, u32 *ber)
if (ret < 0)
return -EIO;
- tmp = ret << 6;
+ tmp |= ret << 6;
if (tmp >= 0x3FFF0)
tmp = ~0;
diff --git a/drivers/staging/media/imx/imx-media-csc-scaler.c b/drivers/staging/media/imx/imx-media-csc-scaler.c
index 1869c5792ecb..085f4f8eb8bd 100644
--- a/drivers/staging/media/imx/imx-media-csc-scaler.c
+++ b/drivers/staging/media/imx/imx-media-csc-scaler.c
@@ -147,6 +147,7 @@ static void device_run(void *_ctx)
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
v4l2_m2m_job_finish(priv->m2m_dev, ctx->fh.m2m_ctx);
+ kfree(run);
}
/*
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index ccb71de22b0a..49df5c6398b5 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -34,7 +34,7 @@ struct dummy_buf {
static u32 get_output_size(u32 width, u32 height)
{
- return ALIGN(width * height, SZ_64K);
+ return ALIGN(ALIGN(width, 32) * ALIGN(height, 32), SZ_64K);
}
u32 amvdec_get_output_size(struct amvdec_session *sess)
diff --git a/drivers/staging/rtl8723bs/core/rtw_recv.c b/drivers/staging/rtl8723bs/core/rtw_recv.c
index 4727f94df435..f8ade0bea1da 100644
--- a/drivers/staging/rtl8723bs/core/rtw_recv.c
+++ b/drivers/staging/rtl8723bs/core/rtw_recv.c
@@ -316,7 +316,7 @@ static void rtw_handle_tkip_mic_err(struct adapter *padapter, u8 bgroup)
} else {
cur_time = jiffies;
- if (cur_time - psecuritypriv->last_mic_err_time < 60*HZ) {
+ if (cur_time - psecuritypriv->last_mic_err_time < 60 * HZ) {
psecuritypriv->btkip_countermeasure = true;
psecuritypriv->last_mic_err_time = 0;
psecuritypriv->btkip_countermeasure_time = cur_time;
@@ -384,13 +384,13 @@ static signed int recvframe_chkmic(struct adapter *adapter, union recv_frame *p
mickey = &stainfo->dot11tkiprxmickey.skey[0];
}
- datalen = precvframe->u.hdr.len-prxattrib->hdrlen-prxattrib->iv_len-prxattrib->icv_len-8;/* icv_len included the mic code */
+ datalen = precvframe->u.hdr.len - prxattrib->hdrlen - prxattrib->iv_len - prxattrib->icv_len - 8;/* icv_len included the mic code */
pframe = precvframe->u.hdr.rx_data;
- payload = pframe+prxattrib->hdrlen+prxattrib->iv_len;
+ payload = pframe + prxattrib->hdrlen + prxattrib->iv_len;
rtw_seccalctkipmic(mickey, pframe, payload, datalen, &miccode[0], (unsigned char)prxattrib->priority); /* care the length of the data */
- pframemic = payload+datalen;
+ pframemic = payload + datalen;
bmic_err = false;
@@ -437,9 +437,22 @@ static union recv_frame *decryptor(struct adapter *padapter, union recv_frame *p
u32 res = _SUCCESS;
if (prxattrib->encrypt > 0) {
- u8 *iv = precv_frame->u.hdr.rx_data+prxattrib->hdrlen;
+ u8 *iv;
+ u32 min_len = prxattrib->hdrlen + prxattrib->iv_len + prxattrib->icv_len;
- prxattrib->key_index = (((iv[3])>>6)&0x3);
+ /* TKIP appends an 8-byte Michael MIC that icv_len doesn't account for */
+ if (prxattrib->encrypt == _TKIP_)
+ min_len += 8;
+
+ /* a protected frame must be long enough to hold the IV and ICV/MIC */
+ if (precv_frame->u.hdr.len < min_len) {
+ rtw_free_recvframe(precv_frame,
+ &padapter->recvpriv.free_recv_queue);
+ return NULL;
+ }
+
+ iv = precv_frame->u.hdr.rx_data + prxattrib->hdrlen;
+ prxattrib->key_index = (((iv[3]) >> 6) & 0x3);
if (prxattrib->key_index > WEP_KEYS) {
switch (prxattrib->encrypt) {
@@ -561,7 +574,7 @@ static signed int recv_decache(union recv_frame *precv_frame, u8 bretry, struct
{
signed int tid = precv_frame->u.hdr.attrib.priority;
- u16 seq_ctrl = ((precv_frame->u.hdr.attrib.seq_num&0xffff) << 4) |
+ u16 seq_ctrl = ((precv_frame->u.hdr.attrib.seq_num & 0xffff) << 4) |
(precv_frame->u.hdr.attrib.frag_num & 0xf);
if (tid > 15)
@@ -625,29 +638,29 @@ static void process_wmmps_data(struct adapter *padapter, union recv_frame *precv
if (!psta->qos_option)
return;
- if (!(psta->qos_info&0xf))
+ if (!(psta->qos_info & 0xf))
return;
- if (psta->state&WIFI_SLEEP_STATE) {
+ if (psta->state & WIFI_SLEEP_STATE) {
u8 wmmps_ac = 0;
switch (pattrib->priority) {
case 1:
case 2:
- wmmps_ac = psta->uapsd_bk&BIT(1);
+ wmmps_ac = psta->uapsd_bk & BIT(1);
break;
case 4:
case 5:
- wmmps_ac = psta->uapsd_vi&BIT(1);
+ wmmps_ac = psta->uapsd_vi & BIT(1);
break;
case 6:
case 7:
- wmmps_ac = psta->uapsd_vo&BIT(1);
+ wmmps_ac = psta->uapsd_vo & BIT(1);
break;
case 0:
case 3:
default:
- wmmps_ac = psta->uapsd_be&BIT(1);
+ wmmps_ac = psta->uapsd_be & BIT(1);
break;
}
@@ -967,20 +980,20 @@ static signed int validate_recv_ctrl_frame(struct adapter *padapter, union recv_
switch (pattrib->priority) {
case 1:
case 2:
- wmmps_ac = psta->uapsd_bk&BIT(0);
+ wmmps_ac = psta->uapsd_bk & BIT(0);
break;
case 4:
case 5:
- wmmps_ac = psta->uapsd_vi&BIT(0);
+ wmmps_ac = psta->uapsd_vi & BIT(0);
break;
case 6:
case 7:
- wmmps_ac = psta->uapsd_vo&BIT(0);
+ wmmps_ac = psta->uapsd_vo & BIT(0);
break;
case 0:
case 3:
default:
- wmmps_ac = psta->uapsd_be&BIT(0);
+ wmmps_ac = psta->uapsd_be & BIT(0);
break;
}
@@ -992,7 +1005,7 @@ static signed int validate_recv_ctrl_frame(struct adapter *padapter, union recv_
psta->state ^= WIFI_STA_ALIVE_CHK_STATE;
}
- if ((psta->state&WIFI_SLEEP_STATE) && (pstapriv->sta_dz_bitmap&BIT(psta->aid))) {
+ if ((psta->state & WIFI_SLEEP_STATE) && (pstapriv->sta_dz_bitmap & BIT(psta->aid))) {
struct list_head *xmitframe_plist, *xmitframe_phead;
struct xmit_frame *pxmitframe = NULL;
struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
@@ -1036,7 +1049,7 @@ static signed int validate_recv_ctrl_frame(struct adapter *padapter, union recv_
/* spin_unlock_bh(&psta->sleep_q.lock); */
spin_unlock_bh(&pxmitpriv->lock);
- if (pstapriv->tim_bitmap&BIT(psta->aid)) {
+ if (pstapriv->tim_bitmap & BIT(psta->aid)) {
if (psta->sleepq_len == 0) {
/* issue nulldata with More data bit = 0 to indicate we have no buffered packets */
issue_nulldata_in_interrupt(padapter, psta->hwaddr);
@@ -1411,15 +1424,17 @@ static signed int validate_80211w_mgmt(struct adapter *adapter, union recv_frame
if (!mgmt_DATA)
goto validate_80211w_fail;
precv_frame = decryptor(adapter, precv_frame);
+ if (!precv_frame) {
+ kfree(mgmt_DATA);
+ goto validate_80211w_fail;
+ }
/* save actual management data frame body */
- memcpy(mgmt_DATA, ptr+pattrib->hdrlen+pattrib->iv_len, data_len);
+ memcpy(mgmt_DATA, ptr + pattrib->hdrlen + pattrib->iv_len, data_len);
/* overwrite the iv field */
- memcpy(ptr+pattrib->hdrlen, mgmt_DATA, data_len);
+ memcpy(ptr + pattrib->hdrlen, mgmt_DATA, data_len);
/* remove the iv and icv length */
pattrib->pkt_len = pattrib->pkt_len - pattrib->iv_len - pattrib->icv_len;
kfree(mgmt_DATA);
- if (!precv_frame)
- goto validate_80211w_fail;
} else if (is_multicast_ether_addr(GetAddr1Ptr(ptr)) &&
(subtype == WIFI_DEAUTH || subtype == WIFI_DISASSOC)) {
signed int BIP_ret = _SUCCESS;
@@ -1470,7 +1485,7 @@ static signed int validate_recv_frame(struct adapter *adapter, union recv_frame
struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib;
u8 *ptr = precv_frame->u.hdr.rx_data;
- u8 ver = (unsigned char) (*ptr)&0x3;
+ u8 ver = (unsigned char) (*ptr) & 0x3;
/* add version chk */
if (ver != 0) {
@@ -1508,7 +1523,7 @@ static signed int validate_recv_frame(struct adapter *adapter, union recv_frame
retval = _FAIL; /* only data frame return _SUCCESS */
break;
case WIFI_DATA_TYPE: /* data */
- pattrib->qos = (subtype & BIT(7)) ? 1:0;
+ pattrib->qos = (subtype & BIT(7)) ? 1 : 0;
retval = validate_recv_data_frame(adapter, precv_frame);
if (retval == _FAIL) {
struct recv_priv *precvpriv = &adapter->recvpriv;
@@ -1553,8 +1568,8 @@ static signed int wlanhdr_to_ethhdr(union recv_frame *precvframe)
if (pattrib->encrypt)
recvframe_pull_tail(precvframe, pattrib->icv_len);
- psnap = (struct ieee80211_snap_hdr *)(ptr+pattrib->hdrlen + pattrib->iv_len);
- psnap_type = ptr+pattrib->hdrlen + pattrib->iv_len+SNAP_SIZE;
+ psnap = (struct ieee80211_snap_hdr *)(ptr + pattrib->hdrlen + pattrib->iv_len);
+ psnap_type = ptr + pattrib->hdrlen + pattrib->iv_len + SNAP_SIZE;
/* convert hdr + possible LLC headers into Ethernet header */
/* eth_type = (psnap_type[0] << 8) | psnap_type[1]; */
if ((!memcmp(psnap, rfc1042_header, SNAP_SIZE) &&
@@ -1568,37 +1583,37 @@ static signed int wlanhdr_to_ethhdr(union recv_frame *precvframe)
/* Leave Ethernet header part of hdr and full payload */
bsnaphdr = false;
- rmv_len = pattrib->hdrlen + pattrib->iv_len + (bsnaphdr?SNAP_SIZE:0);
+ rmv_len = pattrib->hdrlen + pattrib->iv_len + (bsnaphdr ? SNAP_SIZE : 0);
len = precvframe->u.hdr.len - rmv_len;
- memcpy(&be_tmp, ptr+rmv_len, 2);
+ memcpy(&be_tmp, ptr + rmv_len, 2);
eth_type = ntohs(be_tmp); /* pattrib->ether_type */
pattrib->eth_type = eth_type;
if ((check_fwstate(pmlmepriv, WIFI_MP_STATE) == true)) {
ptr += rmv_len;
*ptr = 0x87;
- *(ptr+1) = 0x12;
+ *(ptr + 1) = 0x12;
eth_type = 0x8712;
/* append rx status for mp test packets */
- ptr = recvframe_pull(precvframe, (rmv_len-sizeof(struct ethhdr)+2)-24);
+ ptr = recvframe_pull(precvframe, (rmv_len - sizeof(struct ethhdr) + 2) - 24);
if (!ptr)
return _FAIL;
memcpy(ptr, get_rxmem(precvframe), 24);
ptr += 24;
} else {
- ptr = recvframe_pull(precvframe, (rmv_len-sizeof(struct ethhdr) + (bsnaphdr?2:0)));
+ ptr = recvframe_pull(precvframe, (rmv_len - sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0)));
if (!ptr)
return _FAIL;
}
memcpy(ptr, pattrib->dst, ETH_ALEN);
- memcpy(ptr+ETH_ALEN, pattrib->src, ETH_ALEN);
+ memcpy(ptr + ETH_ALEN, pattrib->src, ETH_ALEN);
if (!bsnaphdr) {
be_tmp = htons(len);
- memcpy(ptr+12, &be_tmp, 2);
+ memcpy(ptr + 12, &be_tmp, 2);
}
return _SUCCESS;
@@ -1736,7 +1751,7 @@ static int amsdu_to_msdu(struct adapter *padapter, union recv_frame *prframe)
pdata += nSubframe_Length;
a_len -= nSubframe_Length;
if (a_len != 0) {
- padding_len = 4 - ((nSubframe_Length + ETH_HLEN) & (4-1));
+ padding_len = 4 - ((nSubframe_Length + ETH_HLEN) & (4 - 1));
if (padding_len == 4)
padding_len = 0;
@@ -2002,7 +2017,7 @@ static int recv_indicatepkt_reorder(struct adapter *padapter, union recv_frame *
rtw_recv_indicatepkt(padapter, prframe);
- preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + 1)%4096;
+ preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + 1) % 4096;
return _SUCCESS;
}
@@ -2012,7 +2027,7 @@ static int recv_indicatepkt_reorder(struct adapter *padapter, union recv_frame *
retval = amsdu_to_msdu(padapter, prframe);
- preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + 1)%4096;
+ preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + 1) % 4096;
if (retval != _SUCCESS) {
}
@@ -2201,7 +2216,7 @@ static int recv_func(struct adapter *padapter, union recv_frame *rframe)
!psecuritypriv->busetkipkey) {
rtw_enqueue_recvframe(rframe, &padapter->recvpriv.uc_swdec_pending_queue);
- if (recvpriv->free_recvframe_cnt < NR_RECVFRAME/4) {
+ if (recvpriv->free_recvframe_cnt < NR_RECVFRAME / 4) {
/* to prevent from recvframe starvation, get recvframe from uc_swdec_pending_queue to free_recvframe_cnt */
rframe = rtw_alloc_recvframe(&padapter->recvpriv.uc_swdec_pending_queue);
if (rframe)
@@ -2289,19 +2304,19 @@ static void rtw_signal_stat_timer_hdl(struct timer_list *t)
}
/* update value of signal_strength, rssi, signal_qual */
- tmp_s = (avg_signal_strength+(_alpha-1)*recvpriv->signal_strength);
+ tmp_s = (avg_signal_strength + (_alpha - 1) * recvpriv->signal_strength);
if (tmp_s % _alpha)
- tmp_s = tmp_s/_alpha + 1;
+ tmp_s = tmp_s / _alpha + 1;
else
- tmp_s = tmp_s/_alpha;
+ tmp_s = tmp_s / _alpha;
if (tmp_s > 100)
tmp_s = 100;
- tmp_q = (avg_signal_qual+(_alpha-1)*recvpriv->signal_qual);
+ tmp_q = (avg_signal_qual + (_alpha - 1) * recvpriv->signal_qual);
if (tmp_q % _alpha)
- tmp_q = tmp_q/_alpha + 1;
+ tmp_q = tmp_q / _alpha + 1;
else
- tmp_q = tmp_q/_alpha;
+ tmp_q = tmp_q / _alpha;
if (tmp_q > 100)
tmp_q = 100;
diff --git a/drivers/staging/rtl8723bs/hal/sdio_ops.c b/drivers/staging/rtl8723bs/hal/sdio_ops.c
index 54228c41579e..811720775eb8 100644
--- a/drivers/staging/rtl8723bs/hal/sdio_ops.c
+++ b/drivers/staging/rtl8723bs/hal/sdio_ops.c
@@ -791,6 +791,13 @@ static struct recv_buf *sd_recv_rxfifo(struct adapter *adapter, u32 size)
struct recv_priv *recv_priv;
struct recv_buf *recvbuf;
+ /* RX0_REQ_LEN is device-reported; the skb buffer is only
+ * MAX_RECVBUF_SZ bytes, reject bogus lengths instead of
+ * overflowing past it.
+ */
+ if (size > MAX_RECVBUF_SZ)
+ return NULL;
+
/* Patch for some SDIO Host 4 bytes issue */
/* ex. RK3188 */
readsize = round_up(size, 4);
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
index 48e7849d4816..dc9c82e8650e 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
@@ -178,17 +178,21 @@ static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
{
int cpu;
int curr_temp, ret;
-
- *temp = 0;
+ bool temp_valid = false;
for_each_online_cpu(cpu) {
ret = intel_tcc_get_temp(cpu, &curr_temp, false);
if (ret < 0)
return ret;
- if (!*temp || curr_temp > *temp)
+ if (!temp_valid || curr_temp > *temp) {
*temp = curr_temp;
+ temp_valid = true;
+ }
}
+ if (!temp_valid)
+ return -ENODATA;
+
*temp *= 1000;
return 0;
diff --git a/drivers/thermal/intel/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c
index 9a4cec000910..61665f945b4c 100644
--- a/drivers/thermal/intel/intel_powerclamp.c
+++ b/drivers/thermal/intel/intel_powerclamp.c
@@ -290,9 +290,10 @@ static int window_size_set(const char *arg, const struct kernel_param *kp)
pr_err("Out of recommended window size %lu, between 2-10\n",
new_window_size);
ret = -EINVAL;
+ goto exit_win;
}
- window_size = clamp(new_window_size, 2ul, 10ul);
+ window_size = new_window_size;
smp_mb();
exit_win:
diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
index b9cc0b786ca6..f9f9c10246e1 100644
--- a/drivers/tty/serial/8250/8250_exar.c
+++ b/drivers/tty/serial/8250/8250_exar.c
@@ -231,8 +231,9 @@ struct exar8250_platform {
int (*rs485_config)(struct uart_port *port, struct ktermios *termios,
struct serial_rs485 *rs485);
const struct serial_rs485 *rs485_supported;
- int (*register_gpio)(struct pci_dev *pcidev, struct uart_8250_port *port);
- void (*unregister_gpio)(struct uart_8250_port *port);
+ int (*register_gpio)(struct exar8250 *priv, struct pci_dev *pcidev,
+ struct uart_8250_port *port);
+ void (*unregister_gpio)(struct exar8250 *priv);
};
/**
@@ -256,6 +257,7 @@ struct exar8250 {
struct exar8250_board *board;
struct eeprom_93cx6 eeprom;
void __iomem *virt;
+ struct platform_device *gpio;
int line[];
};
@@ -1174,22 +1176,21 @@ static const struct software_node exar_gpio_node = {
.properties = exar_gpio_properties,
};
-static int xr17v35x_register_gpio(struct pci_dev *pcidev, struct uart_8250_port *port)
+static int xr17v35x_register_gpio(struct exar8250 *priv, struct pci_dev *pcidev,
+ struct uart_8250_port *port)
{
if (pcidev->vendor == PCI_VENDOR_ID_EXAR)
- port->port.private_data =
- __xr17v35x_register_gpio(pcidev, &exar_gpio_node);
+ priv->gpio = __xr17v35x_register_gpio(pcidev, &exar_gpio_node);
return 0;
}
-static void xr17v35x_unregister_gpio(struct uart_8250_port *port)
+static void xr17v35x_unregister_gpio(struct exar8250 *priv)
{
- if (!port->port.private_data)
- return;
-
- __xr17v35x_unregister_gpio(port->port.private_data);
- port->port.private_data = NULL;
+ if (priv->gpio) {
+ __xr17v35x_unregister_gpio(priv->gpio);
+ priv->gpio = NULL;
+ }
}
static int sealevel_rs485_config(struct uart_port *port, struct ktermios *termios,
@@ -1286,8 +1287,9 @@ static const struct software_node iot2040_gpio_node = {
.properties = iot2040_gpio_properties,
};
-static int iot2040_register_gpio(struct pci_dev *pcidev,
- struct uart_8250_port *port)
+static int iot2040_register_gpio(struct exar8250 *priv,
+ struct pci_dev *pcidev,
+ struct uart_8250_port *port)
{
u8 __iomem *p = port->port.membase;
@@ -1296,8 +1298,7 @@ static int iot2040_register_gpio(struct pci_dev *pcidev,
writeb(IOT2040_UARTS_ENABLE, p + UART_EXAR_MPIOLVL_15_8);
writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8);
- port->port.private_data =
- __xr17v35x_register_gpio(pcidev, &iot2040_gpio_node);
+ priv->gpio = __xr17v35x_register_gpio(pcidev, &iot2040_gpio_node);
return 0;
}
@@ -1374,7 +1375,7 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
/* Setup Multipurpose Input/Output pins. */
setup_gpio(pcidev, p);
- ret = platform->register_gpio(pcidev, port);
+ ret = platform->register_gpio(priv, pcidev, port);
}
return ret;
@@ -1384,9 +1385,8 @@ static void pci_xr17v35x_exit(struct pci_dev *pcidev)
{
const struct exar8250_platform *platform = exar_get_platform();
struct exar8250 *priv = pci_get_drvdata(pcidev);
- struct uart_8250_port *port = serial8250_get_port(priv->line[0]);
- platform->unregister_gpio(port);
+ platform->unregister_gpio(priv);
}
static inline void exar_misc_clear(struct exar8250 *priv)
diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c
index 776ec1ef29d6..325e587ae2c4 100644
--- a/drivers/tty/serial/8250/8250_lpss.c
+++ b/drivers/tty/serial/8250/8250_lpss.c
@@ -183,9 +183,8 @@ static int ehl_serial_setup(struct lpss8250 *lpss, struct uart_port *port)
static void ehl_serial_exit(struct lpss8250 *lpss)
{
- struct uart_8250_port *up = serial8250_get_port(lpss->data.line);
-
- up->dma = NULL;
+ if (lpss->data.line >= 0)
+ serial8250_get_port(lpss->data.line)->dma = NULL;
}
#ifdef CONFIG_SERIAL_8250_DMA
@@ -329,6 +328,8 @@ static int lpss8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return ret;
lpss->board = (struct lpss8250_board *)id->driver_data;
+ /* for tests in lpss->board->exit() */
+ lpss->data.line = -1;
memset(&uart, 0, sizeof(struct uart_8250_port));
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index c0ffee251614..8c48e0a26b3c 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -727,6 +727,9 @@ static int omap_8250_startup(struct uart_port *port)
struct uart_8250_dma *dma = &priv->omap8250_dma;
int ret;
+ /* ISR and runtime PM need the line; probe sets it only after registration */
+ priv->line = port->line;
+
#ifdef CONFIG_PM
up->capabilities |= UART_CAP_RPM;
#endif
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 4b213d0c6133..20d2a8612cbc 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -1355,10 +1355,6 @@ static void pl011_rs485_tx_start(struct uart_amba_port *uap)
struct uart_port *port = &uap->port;
u32 cr;
- if (uap->rs485_tx_state == WAIT_AFTER_RTS) {
- uap->rs485_tx_state = SEND;
- return;
- }
if (uap->rs485_tx_state == WAIT_AFTER_SEND ||
uap->rs485_tx_state == WAIT_AFTER_SEND_DELAY) {
hrtimer_try_to_cancel(&uap->trigger_stop_tx);
@@ -1397,6 +1393,10 @@ static void pl011_start_tx(struct uart_port *port)
if ((uap->port.rs485.flags & SER_RS485_ENABLED) &&
uap->rs485_tx_state != SEND) {
+ /* trigger_start_tx starts TX once delay_rts_before_send passed */
+ if (uap->rs485_tx_state == WAIT_AFTER_RTS)
+ return;
+
pl011_rs485_tx_start(uap);
if (uap->rs485_tx_state == WAIT_AFTER_RTS)
return;
@@ -1413,8 +1413,10 @@ static enum hrtimer_restart pl011_trigger_start_tx(struct hrtimer *t)
unsigned long flags;
uart_port_lock_irqsave(&uap->port, &flags);
- if (uap->rs485_tx_state == WAIT_AFTER_RTS)
+ if (uap->rs485_tx_state == WAIT_AFTER_RTS) {
+ uap->rs485_tx_state = SEND;
pl011_start_tx(&uap->port);
+ }
uart_port_unlock_irqrestore(&uap->port, flags);
return HRTIMER_NORESTART;
@@ -2306,8 +2308,31 @@ static int pl011_rs485_config(struct uart_port *port, struct ktermios *termios,
struct uart_amba_port *uap =
container_of(port, struct uart_amba_port, port);
- if (port->rs485.flags & SER_RS485_ENABLED)
+ if ((port->rs485.flags & SER_RS485_ENABLED) &&
+ !(rs485->flags & SER_RS485_ENABLED)) {
+ bool tx_pending = uap->rs485_tx_state == WAIT_AFTER_RTS;
+ u32 cr = pl011_read(uap, REG_CR);
+
+ /*
+ * Leaving RS485 mode, which only enables the transmitter while
+ * sending. Don't stop it, as that stalls a DMA transfer still
+ * in flight. Drop pending deferred starts and stops and keep
+ * it enabled.
+ */
+ hrtimer_try_to_cancel(&uap->trigger_start_tx);
+ hrtimer_try_to_cancel(&uap->trigger_stop_tx);
+ uap->rs485_tx_state = OFF;
+
+ cr |= UART011_CR_TXE | UART011_CR_RXE;
+ pl011_write(cr, uap, REG_CR);
+
+ /* Start TX that was still waiting for delay_rts_before_send */
+ if (tx_pending && !uart_tx_stopped(port) &&
+ !pl011_dma_tx_start(uap))
+ pl011_start_tx_pio(uap);
+ } else if (port->rs485.flags & SER_RS485_ENABLED) {
pl011_rs485_tx_stop(uap);
+ }
/* Make sure auto RTS is disabled */
if (rs485->flags & SER_RS485_ENABLED) {
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index e3a919328695..f49d6052d11b 100644
--- a/drivers/tty/serial/pmac_zilog.c
+++ b/drivers/tty/serial/pmac_zilog.c
@@ -444,7 +444,7 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id)
skip_a:
uart_port_unlock(&uap_a->port);
if (push)
- tty_flip_buffer_push(&uap->port.state->port);
+ tty_flip_buffer_push(&uap_a->port.state->port);
if (!uap_b)
goto out;
@@ -469,7 +469,7 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id)
skip_b:
uart_port_unlock(&uap_b->port);
if (push)
- tty_flip_buffer_push(&uap->port.state->port);
+ tty_flip_buffer_push(&uap_b->port.state->port);
out:
return rc;
diff --git a/drivers/ufs/core/ufs_bsg.c b/drivers/ufs/core/ufs_bsg.c
index bb6ff8ca5c7a..0e0b08a3821c 100644
--- a/drivers/ufs/core/ufs_bsg.c
+++ b/drivers/ufs/core/ufs_bsg.c
@@ -142,7 +142,7 @@ static int ufs_bsg_request(struct bsg_job *job)
int ret;
bool rpmb = false;
- bsg_reply->reply_payload_rcv_len = 0;
+ memset(job->reply, 0, job->reply_len);
ufshcd_rpm_get_sync(hba);
@@ -194,14 +194,12 @@ static int ufs_bsg_request(struct bsg_job *job)
ufshcd_rpm_put_sync(hba);
kfree(buff);
bsg_reply->result = ret;
- /* complete the job here only if no error */
- if (ret == 0) {
+ if (ret == 0)
job->reply_len = rpmb ? sizeof(struct ufs_rpmb_reply) :
sizeof(struct ufs_bsg_reply);
- bsg_job_done(job, ret, bsg_reply->reply_payload_rcv_len);
- }
+ bsg_job_done(job, ret, bsg_reply->reply_payload_rcv_len);
- return ret;
+ return 0;
}
/**
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 52df1df6333a..97f86b6905b5 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -4219,9 +4219,12 @@ int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
do {
/* for peer attributes we retry upon failure */
ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
- if (ret)
+ if (ret) {
dev_dbg(hba->dev, "%s: attr-id 0x%x val 0x%x error code %d\n",
set, UIC_GET_ATTR_ID(attr_sel), mib_val, ret);
+ uic_cmd.argument2 &= ~MASK_UIC_COMMAND_RESULT;
+ uic_cmd.argument3 = mib_val;
+ }
} while (ret && peer && --retries);
if (ret)
@@ -4285,9 +4288,11 @@ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
do {
/* for peer attributes we retry upon failure */
ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
- if (ret)
+ if (ret) {
dev_dbg(hba->dev, "%s: attr-id 0x%x error code %d\n",
get, UIC_GET_ATTR_ID(attr_sel), ret);
+ uic_cmd.argument2 &= ~MASK_UIC_COMMAND_RESULT;
+ }
} while (ret && peer && --retries);
if (ret)
@@ -5709,9 +5714,6 @@ static int ufshcd_poll(struct Scsi_Host *shost, unsigned int queue_num)
spin_lock_irqsave(&hba->outstanding_lock, flags);
tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
completed_reqs = ~tr_doorbell & hba->outstanding_reqs;
- WARN_ONCE(completed_reqs & ~hba->outstanding_reqs,
- "completed: %#lx; outstanding: %#lx\n", completed_reqs,
- hba->outstanding_reqs);
if (queue_num == UFSHCD_POLL_FROM_INTERRUPT_CONTEXT) {
/* Do not complete polled requests from interrupt context. */
ufshcd_clear_polled(hba, &completed_reqs);
@@ -7775,15 +7777,6 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
}
hba->req_abort_count++;
- if (!hba->mcq_enabled && !(reg & (1 << tag))) {
- /* only execute this code in single doorbell mode */
- dev_err(hba->dev,
- "%s: cmd was completed, but without a notifying intr, tag = %d",
- __func__, tag);
- __ufshcd_transfer_req_compl(hba, 1UL << tag);
- goto release;
- }
-
/*
* Task abort to the device W-LUN is illegal. When this command
* will fail, due to spec violation, scsi err handling next step
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index b980cb08d675..990482982a28 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -782,6 +782,7 @@ static void acm_port_shutdown(struct tty_port *port)
urb = usb_get_from_anchor(&acm->delayed);
if (!urb)
break;
+ usb_put_urb(urb);
wb = urb->context;
wb->use = false;
usb_autopm_put_interface_async(acm->control);
@@ -1710,7 +1711,7 @@ static int acm_resume(struct usb_interface *intf)
urb = usb_get_from_anchor(&acm->delayed);
if (!urb)
break;
-
+ usb_put_urb(urb);
acm_start_wb(acm, urb->context);
}
@@ -1818,6 +1819,9 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x11ca, 0x0201), /* VeriFone Mx870 Gadget Serial */
.driver_info = SINGLE_RX_URB,
},
+ { USB_DEVICE(0x11ca, 0x0219), /* VeriFone Vx820 Gadget Serial */
+ .driver_info = SINGLE_RX_URB,
+ },
{ USB_DEVICE(0x1901, 0x0006), /* GE Healthcare Patient Monitor UI Controller */
.driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */
},
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 0f804a52ab76..a8e5016fee98 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -613,6 +613,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* ezcap401 - BOS descriptor fetch hangs at SuperSpeed Plus */
{ USB_DEVICE(0x32ed, 0x0401), .driver_info = USB_QUIRK_NO_BOS },
+ /* GameSir G7 SE */
+ { USB_DEVICE(0x3537, 0x10a0), .driver_info = USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE },
+
/* DELL USB GEN2 */
{ USB_DEVICE(0x413c, 0xb062), .driver_info = USB_QUIRK_NO_LPM | USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/fotg210/fotg210-udc.c b/drivers/usb/fotg210/fotg210-udc.c
index 0bae12e34f9a..26f29dba58f2 100644
--- a/drivers/usb/fotg210/fotg210-udc.c
+++ b/drivers/usb/fotg210/fotg210-udc.c
@@ -661,7 +661,7 @@ static void fotg210_set_feature(struct fotg210_udc *fotg210,
case USB_RECIP_ENDPOINT: {
u8 epnum;
epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
- if (epnum)
+ if (epnum && epnum < FOTG210_MAX_NUM_EP)
fotg210_set_epnstall(fotg210->ep[epnum]);
else
fotg210_set_cxstall(fotg210);
@@ -677,8 +677,8 @@ static void fotg210_set_feature(struct fotg210_udc *fotg210,
static void fotg210_clear_feature(struct fotg210_udc *fotg210,
struct usb_ctrlrequest *ctrl)
{
- struct fotg210_ep *ep =
- fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK];
+ u8 epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+ struct fotg210_ep *ep;
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
@@ -688,7 +688,12 @@ static void fotg210_clear_feature(struct fotg210_udc *fotg210,
fotg210_set_cxdone(fotg210);
break;
case USB_RECIP_ENDPOINT:
- if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) {
+ if (epnum >= FOTG210_MAX_NUM_EP) {
+ fotg210_request_error(fotg210);
+ break;
+ }
+ if (epnum) {
+ ep = fotg210->ep[epnum];
if (ep->wedged) {
fotg210_set_cxdone(fotg210);
break;
@@ -744,8 +749,8 @@ static void fotg210_get_status(struct fotg210_udc *fotg210,
fotg210->ep0_data = cpu_to_le16(0);
break;
case USB_RECIP_ENDPOINT:
- epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
- if (epnum)
+ epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+ if (epnum && epnum < FOTG210_MAX_NUM_EP)
fotg210->ep0_data =
cpu_to_le16(fotg210_is_epnstall(fotg210->ep[epnum])
<< USB_ENDPOINT_HALT);
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 677cd236f079..c14e8e250037 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -615,7 +615,7 @@ ep_read_iter(struct kiocb *iocb, struct iov_iter *to)
return -EBADMSG;
}
- buf = kmalloc(len, GFP_KERNEL);
+ buf = kmalloc(len, GFP_KERNEL | __GFP_NOWARN);
if (unlikely(!buf)) {
mutex_unlock(&epdata->lock);
return -ENOMEM;
@@ -677,7 +677,7 @@ ep_write_iter(struct kiocb *iocb, struct iov_iter *from)
return -EBADMSG;
}
- buf = kmalloc(len, GFP_KERNEL);
+ buf = kmalloc(len, GFP_KERNEL | __GFP_NOWARN);
if (unlikely(!buf)) {
mutex_unlock(&epdata->lock);
return -ENOMEM;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index dc18da8385a2..071a0310d610 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3600,15 +3600,13 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred,
static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len,
- u32 *trb_buff_len, struct xhci_segment *seg)
+ u32 *trb_buff_len, struct xhci_segment *seg, u32 max_pkt)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
unsigned int unalign;
- unsigned int max_pkt;
u32 new_buff_len;
size_t len;
- max_pkt = xhci_usb_endpoint_maxp(urb->dev, urb->ep);
unalign = (enqd_len + *trb_buff_len) % max_pkt;
/* we got lucky, last normal TRB data on segment is packet aligned */
@@ -3753,9 +3751,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
if (enqd_len + trb_buff_len < full_len) {
field |= TRB_CHAIN;
if (trb_is_link(ring->enqueue + 1)) {
- if (xhci_align_td(xhci, urb, enqd_len,
- &trb_buff_len,
- ring->enq_seg)) {
+ if (xhci_align_td(xhci, urb, enqd_len, &trb_buff_len,
+ ring->enq_seg, ring->bounce_buf_len)) {
send_addr = ring->enq_seg->bounce_dma;
/* TD bounced at least, and last on this seg */
td->bounce_seg = ring->enq_seg;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index a4fab82da76b..b894ba988a3d 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4435,10 +4435,13 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
dev_warn(&udev->dev, "Device not responding to setup %s.\n", act);
mutex_unlock(&xhci->mutex);
- ret = xhci_disable_and_free_slot(xhci, udev->slot_id);
- if (!ret) {
- if (xhci_alloc_dev(hcd, udev) == 1)
- xhci_setup_addressable_virt_dev(xhci, udev);
+
+ if (!virt_dev->sideband) {
+ ret = xhci_disable_and_free_slot(xhci, udev->slot_id);
+ if (!ret) {
+ if (xhci_alloc_dev(hcd, udev) == 1)
+ xhci_setup_addressable_virt_dev(xhci, udev);
+ }
}
kfree(command->completion);
kfree(command);
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 0f9a7a4c1f66..2f83e7b866f6 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -2444,6 +2444,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x30) }, /* Compal EXM-G1x */
{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x40) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x60) },
+ { USB_DEVICE_INTERFACE_CLASS(0x1286, 0x4e3c, 0xff) }, /* ASR modem */
{ USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */
.driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
{ USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */
@@ -2511,6 +2512,10 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(5) },
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff), /* Rolling RW135R-GL (laptop MBIM) */
.driver_info = RSVD(5) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x33f8, 0x9300, 0xff, 0xff, 0x30) }, /* Rolling RN947R */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x33f8, 0x9300, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x33f8, 0x9300, 0xff, 0xff, 0x10),
+ .driver_info = ZLP },
{ USB_DEVICE_INTERFACE_CLASS(0x3466, 0x3301, 0xff) }, /* TDTECH MT5710-CN */
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) },
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index 741e68e46139..bac926cfee28 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -972,12 +972,13 @@ static int sierra_submit_delayed_urbs(struct usb_serial_port *port)
intfdata->in_flight--;
usb_unanchor_urb(urb);
kfree(urb->transfer_buffer);
- usb_free_urb(urb);
spin_lock(&portdata->lock);
portdata->outstanding_urbs--;
spin_unlock(&portdata->lock);
}
+
+ usb_put_urb(urb);
}
if (ec)
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index 0017f6e969e1..67e1820bbbaf 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -402,6 +402,7 @@ void usb_wwan_close(struct usb_serial_port *port)
urb = usb_get_from_anchor(&portdata->delayed);
if (!urb)
break;
+ usb_put_urb(urb);
unbusy_queued_urb(urb, portdata);
usb_autopm_put_interface_async(serial->interface);
}
@@ -579,6 +580,8 @@ static int usb_wwan_submit_delayed_urbs(struct usb_serial_port *port)
if (!urb)
break;
+ usb_put_urb(urb);
+
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
dev_err(&port->dev, "%s: submit urb failed: %d\n",
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index 6c1f62c78ac2..55dbe7508ea7 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -134,6 +134,8 @@ struct version_format {
#define NVIDIA_FTB_DP_OFFSET (2)
#define NVIDIA_FTB_DBG_OFFSET (3)
+#define CCG_READ_MAX_RETRIES 4
+
struct version_info {
struct version_format base;
struct version_format app;
@@ -254,6 +256,7 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
};
u32 rlen, rem_len = len, max_read_len = len;
int status;
+ int retry_count;
/* check any max_read_len limitation on i2c adapter */
if (quirks && quirks->max_read_len)
@@ -265,7 +268,17 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
rlen = min_t(u16, rem_len, max_read_len);
msgs[1].len = rlen;
put_unaligned_le16(rab, buf);
- status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+
+ for (retry_count = 0; retry_count < CCG_READ_MAX_RETRIES; retry_count++) {
+ status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+
+ /* Only retry on -EREMOTEIO, and not after the last attempt */
+ if (status != -EREMOTEIO || retry_count == CCG_READ_MAX_RETRIES - 1)
+ break;
+
+ fsleep(1000);
+ }
+
if (status < 0) {
dev_err(uc->dev, "i2c_transfer failed %d\n", status);
pm_runtime_put_sync(uc->dev);
@@ -383,14 +396,28 @@ static int ucsi_ccg_init(struct ucsi_ccg *uc)
return -ETIMEDOUT;
}
-static void ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg *uc, u8 *data)
+static int ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg *uc, u8 *data)
{
u8 cam, new_cam;
cam = data[0];
+ if (cam >= UCSI_MAX_ALTMODES) {
+ dev_err(uc->dev, "PPM returned invalid alternate mode index %u\n",
+ cam);
+ return -EPROTO;
+ }
+
new_cam = uc->orig[cam].linked_idx;
+ if (new_cam >= UCSI_MAX_ALTMODES) {
+ dev_err(uc->dev, "invalid linked alternate mode index %u for %u\n",
+ new_cam, cam);
+ return -EPROTO;
+ }
+
uc->updated[new_cam].active_idx = cam;
data[0] = new_cam;
+
+ return 0;
}
static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi,
@@ -633,8 +660,10 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
switch (UCSI_COMMAND(command)) {
case UCSI_GET_CURRENT_CAM:
- if (uc->has_multiple_dp)
- ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)data);
+ if (!ret && uc->has_multiple_dp &&
+ (*cci & UCSI_CCI_COMMAND_COMPLETE) &&
+ !(*cci & UCSI_CCI_ERROR))
+ ret = ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)data);
break;
case UCSI_GET_ALTERNATE_MODES:
if (UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_SOP) {
diff --git a/drivers/vfio/pci/mlx5/cmd.c b/drivers/vfio/pci/mlx5/cmd.c
index a92b095b90f6..a870ea1c0e9d 100644
--- a/drivers/vfio/pci/mlx5/cmd.c
+++ b/drivers/vfio/pci/mlx5/cmd.c
@@ -1627,7 +1627,7 @@ set_report_output(u32 size, int index, struct mlx5_vhca_qp *qp,
int i;
buf_offset = index * qp->max_msg_size;
- if (WARN_ON(buf_offset + size >= qp->recv_buf.npages * PAGE_SIZE ||
+ if (WARN_ON(buf_offset + size > qp->recv_buf.npages * PAGE_SIZE ||
(nent > qp->max_msg_size / entry_size)))
return;
diff --git a/drivers/video/fbdev/atmel_lcdfb.c b/drivers/video/fbdev/atmel_lcdfb.c
index 35862e83f7af..2b602ad004dd 100644
--- a/drivers/video/fbdev/atmel_lcdfb.c
+++ b/drivers/video/fbdev/atmel_lcdfb.c
@@ -1009,7 +1009,9 @@ static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo)
if (ret < 0)
goto put_display_node;
- fb_add_videomode(&fb_vm, &info->modelist);
+ ret = fb_add_videomode(&fb_vm, &info->modelist);
+ if (ret < 0)
+ goto put_display_node;
put_display_node:
of_node_put(display_np);
@@ -1077,8 +1079,13 @@ static int atmel_lcdfb_probe(struct platform_device *pdev)
}
atmel_lcdfb_start_clock(sinfo);
- modelist = list_first_entry(&info->modelist,
- struct fb_modelist, list);
+ modelist = list_first_entry_or_null(&info->modelist,
+ struct fb_modelist, list);
+ if (!modelist) {
+ dev_err(dev, "no video mode available\n");
+ ret = -EINVAL;
+ goto stop_clk;
+ }
fb_videomode_to_var(&info->var, &modelist->mode);
atmel_lcdfb_check_var(&info->var, info);
diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c
index 8f6a2d1736d3..86ae7a7252d2 100644
--- a/drivers/video/fbdev/core/bitblit.c
+++ b/drivers/video/fbdev/core/bitblit.c
@@ -18,6 +18,7 @@
#include <linux/console.h>
#include <asm/types.h>
#include "fbcon.h"
+#include "fbcon_rotate.h"
/*
* Accelerated handlers.
@@ -151,7 +152,9 @@ static void bit_putcs(struct vc_data *vc, struct fb_info *info,
const unsigned short *s, int count, int yy, int xx,
int fg, int bg)
{
+ struct fbcon_par *par = info->fbcon_par;
struct fb_image image;
+ u32 vyres = GETVYRES(par->p, info);
u32 width = DIV_ROUND_UP(vc->vc_font.width, 8);
u32 cellsize = width * vc->vc_font.height;
u32 maxcnt = info->pixmap.size/cellsize;
@@ -168,10 +171,10 @@ static void bit_putcs(struct vc_data *vc, struct fb_info *info,
image.height = vc->vc_font.height;
image.depth = 1;
- if (image.dy >= info->var.yres)
+ if (image.dy >= vyres)
return;
- image.height = min(image.height, info->var.yres - image.dy);
+ image.height = min(image.height, vyres - image.dy);
if (attribute) {
buf = kmalloc(cellsize, GFP_ATOMIC);
diff --git a/drivers/video/fbdev/kyro/fbdev.c b/drivers/video/fbdev/kyro/fbdev.c
index c44d6809859e..fc350edb41a8 100644
--- a/drivers/video/fbdev/kyro/fbdev.c
+++ b/drivers/video/fbdev/kyro/fbdev.c
@@ -766,7 +766,7 @@ static int kyrofb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
fb_memset_io(info->screen_base, 0, size);
if (register_framebuffer(info) < 0)
- goto out_free_fb;
+ goto out_free_wc;
fb_info(info, "%s frame buffer device, at %dx%d@%d using %ldk/%ldk of VRAM\n",
info->fix.id,
@@ -777,6 +777,8 @@ static int kyrofb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
+out_free_wc:
+ arch_phys_wc_del(currentpar->wc_cookie);
out_free_fb:
framebuffer_release(info);
diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c
index 6038f217ab42..c6da5a62f8c5 100644
--- a/drivers/video/fbdev/sh_mobile_lcdcfb.c
+++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c
@@ -1042,7 +1042,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
ch->defio.delay = msecs_to_jiffies(tmp);
ch->info->fbdefio = &ch->defio;
- fb_deferred_io_init(ch->info);
+ ret = fb_deferred_io_init(ch->info);
+ if (ret) {
+ ch->info->fbdefio = NULL;
+ return ret;
+ }
}
sh_mobile_lcdc_display_on(ch);
diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c
index 891ce7b76d63..30a94d85fc8c 100644
--- a/drivers/video/fbdev/smscufx.c
+++ b/drivers/video/fbdev/smscufx.c
@@ -1041,10 +1041,13 @@ static int ufx_ops_open(struct fb_info *info, int user)
if (fbdefio) {
fbdefio->delay = UFX_DEFIO_WRITE_DELAY;
fbdefio->deferred_io = ufx_dpy_deferred_io;
- }
- info->fbdefio = fbdefio;
- fb_deferred_io_init(info);
+ info->fbdefio = fbdefio;
+ if (fb_deferred_io_init(info)) {
+ kfree(fbdefio);
+ info->fbdefio = NULL;
+ }
+ }
}
pr_debug("open /dev/fb%d user=%d fb_info=%p count=%d",
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index 0df3ac694f4e..e4559312a926 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -763,7 +763,11 @@ static int ssd1307fb_probe(struct i2c_client *client)
info->fix.smem_start = __pa(vmem);
info->fix.smem_len = vmem_size;
- fb_deferred_io_init(info);
+ ret = fb_deferred_io_init(info);
+ if (ret) {
+ dev_err(dev, "failed to init fb deferred io: %d\n", ret);
+ goto fb_defio_error;
+ }
i2c_set_clientdata(client, info);
diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
index f6da680c29ea..abaac2ed22a9 100644
--- a/drivers/video/fbdev/udlfb.c
+++ b/drivers/video/fbdev/udlfb.c
@@ -947,10 +947,13 @@ static int dlfb_ops_open(struct fb_info *info, int user)
fbdefio->delay = DL_DEFIO_WRITE_DELAY;
fbdefio->sort_pagereflist = true;
fbdefio->deferred_io = dlfb_dpy_deferred_io;
- }
- info->fbdefio = fbdefio;
- fb_deferred_io_init(info);
+ info->fbdefio = fbdefio;
+ if (fb_deferred_io_init(info)) {
+ kfree(fbdefio);
+ info->fbdefio = NULL;
+ }
+ }
}
dev_dbg(info->dev, "open, user=%d fb_info=%p count=%d\n",
diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c
index d8f3bfb2dd6c..22aaafd2c11f 100644
--- a/drivers/video/fbdev/xen-fbfront.c
+++ b/drivers/video/fbdev/xen-fbfront.c
@@ -443,7 +443,11 @@ static int xenfb_probe(struct xenbus_device *dev,
}
fb_info->fbdefio = &xenfb_defio;
- fb_deferred_io_init(fb_info);
+ ret = fb_deferred_io_init(fb_info);
+ if (ret < 0) {
+ xenbus_dev_fatal(dev, ret, "fb_deferred_io_init");
+ goto error_cmap;
+ }
xenfb_init_shared_page(info, fb_info);
@@ -465,6 +469,7 @@ static int xenfb_probe(struct xenbus_device *dev,
error_fb:
fb_deferred_io_cleanup(fb_info);
+error_cmap:
fb_dealloc_cmap(&fb_info->cmap);
framebuffer_release(fb_info);
error_nomem:
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 1e21b75114e5..02ebbdba8610 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -1072,6 +1072,13 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma)
if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED))
return -EINVAL;
+ /*
+ * Private mappings of foreign grant pages must never become
+ * writable: they would take the COW path on granted pages.
+ */
+ if (!(vma->vm_flags & VM_SHARED))
+ vm_flags_clear(vma, VM_MAYWRITE);
+
pr_debug("map %d+%d at %lx (pgoff %lx)\n",
index, count, vma->vm_start, vma->vm_pgoff);
diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c
index f37d8d212c06..fc21473672d4 100644
--- a/drivers/xen/privcmd.c
+++ b/drivers/xen/privcmd.c
@@ -1168,51 +1168,40 @@ static irqreturn_t ioeventfd_interrupt(int irq, void *dev_id)
struct privcmd_kernel_ioreq *kioreq = port->kioreq;
struct ioreq *ioreq = &kioreq->ioreq[port->vcpu];
struct privcmd_kernel_ioeventfd *kioeventfd;
- unsigned int state = STATE_IOREQ_READY;
+ bool matched = false;
- if (ioreq->state != STATE_IOREQ_READY ||
- ioreq->type != IOREQ_TYPE_COPY || ioreq->dir != IOREQ_WRITE)
+ if (ioreq->state != STATE_IOREQ_READY)
return IRQ_NONE;
- /*
- * We need a barrier, smp_mb(), here to ensure reads are finished before
- * `state` is updated. Since the lock implementation ensures that
- * appropriate barrier will be added anyway, we can avoid adding
- * explicit barrier here.
- *
- * Ideally we don't need to update `state` within the locks, but we do
- * that here to avoid adding explicit barrier.
- */
+ /* Xen publishes the request before changing its state to READY. */
+ virt_rmb();
- spin_lock(&kioreq->lock);
- ioreq->state = STATE_IOREQ_INPROCESS;
+ if (ioreq->type != IOREQ_TYPE_COPY || ioreq->dir != IOREQ_WRITE)
+ return IRQ_NONE;
+ spin_lock(&kioreq->lock);
list_for_each_entry(kioeventfd, &kioreq->ioeventfds, list) {
if (ioreq->addr == kioeventfd->addr + VIRTIO_MMIO_QUEUE_NOTIFY &&
ioreq->size == kioeventfd->addr_len &&
(ioreq->data & QUEUE_NOTIFY_VQ_MASK) == kioeventfd->vq) {
+ /* Finish reading the request before claiming it. */
+ virt_mb();
+ ioreq->state = STATE_IOREQ_INPROCESS;
eventfd_signal(kioeventfd->eventfd);
- state = STATE_IORESP_READY;
+ matched = true;
break;
}
}
spin_unlock(&kioreq->lock);
- /*
- * We need a barrier, smp_mb(), here to ensure writes are finished
- * before `state` is updated. Since the lock implementation ensures that
- * appropriate barrier will be added anyway, we can avoid adding
- * explicit barrier here.
- */
-
- ioreq->state = state;
-
- if (state == STATE_IORESP_READY) {
- notify_remote_via_evtchn(port->port);
- return IRQ_HANDLED;
- }
+ if (!matched)
+ return IRQ_NONE;
- return IRQ_NONE;
+ /* Publish the response only after signaling the eventfd. */
+ virt_wmb();
+ ioreq->state = STATE_IORESP_READY;
+ notify_remote_via_evtchn(port->port);
+ return IRQ_HANDLED;
}
static void ioreq_free(struct privcmd_kernel_ioreq *kioreq)
diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c
index 7de2fa67743c..b128ceeee21b 100644
--- a/drivers/xen/xen-pciback/pci_stub.c
+++ b/drivers/xen/xen-pciback/pci_stub.c
@@ -234,13 +234,16 @@ static int pcistub_get_gsi_from_sbdf(unsigned int sbdf)
int bus = PCI_BUS_NUM(sbdf);
int slot = PCI_SLOT(sbdf);
int func = PCI_FUNC(sbdf);
+ int gsi;
psdev = pcistub_device_find(domain, bus, slot, func);
-
if (!psdev)
return -ENODEV;
- return psdev->gsi;
+ gsi = psdev->gsi;
+ pcistub_device_put(psdev);
+
+ return gsi;
}
#endif
diff --git a/fs/affs/bitmap.c b/fs/affs/bitmap.c
index 5ba9ef2742f6..f400b28eea28 100644
--- a/fs/affs/bitmap.c
+++ b/fs/affs/bitmap.c
@@ -46,7 +46,7 @@ affs_free_block(struct super_block *sb, u32 block)
pr_debug("%s(%u)\n", __func__, block);
- if (block > sbi->s_partition_size)
+ if (!affs_validblock(sb, block))
goto err_range;
blk = block - sbi->s_reserved;
diff --git a/fs/aio.c b/fs/aio.c
index 5bc133386407..e06fa57b05ca 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -474,7 +474,18 @@ static int aio_migrate_folio(struct address_space *mapping, struct folio *dst,
#define aio_migrate_folio NULL
#endif
+/*
+ * filemap_fault() reads a folio that it did not find in the page cache. All
+ * folios of a live ring are there, so the ring was truncated under the fault.
+ */
+static int aio_read_folio(struct file *file, struct folio *folio)
+{
+ folio_unlock(folio);
+ return -EIO;
+}
+
static const struct address_space_operations aio_ctx_aops = {
+ .read_folio = aio_read_folio,
.dirty_folio = noop_dirty_folio,
.migrate_folio = aio_migrate_folio,
};
@@ -1816,7 +1827,12 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync,
list_del_init(&req->wait.entry);
list_del(&iocb->ki_list);
iocb->ki_res.res = mangle_poll(mask);
- if (iocb->ki_eventfd && !eventfd_signal_allowed()) {
+ /*
+ * We hold an arbitrary provider waitqueue lock here. Signaling a
+ * result eventfd can feed back through epoll and try to take the same
+ * lock again. Defer all eventfd-backed poll completions.
+ */
+ if (iocb->ki_eventfd) {
iocb = NULL;
INIT_WORK(&req->work, aio_poll_put_work);
schedule_work(&req->work);
diff --git a/fs/backing-file.c b/fs/backing-file.c
index 6f16e23f7046..bf526fa2e29d 100644
--- a/fs/backing-file.c
+++ b/fs/backing-file.c
@@ -35,13 +35,36 @@ struct file *backing_file_open(const struct file *user_file, int flags,
const struct cred *cred)
{
const struct path *user_path = file_user_path(user_file);
+ struct inode *real_inode = d_inode(real_path->dentry);
struct file *f;
int error;
+ /*
+ * may_open() checked the inode of the user file, which need not carry
+ * S_APPEND and S_IMMUTABLE of the backing inode.
+ */
+ if ((flags & O_ACCMODE) != O_RDONLY) {
+ if (IS_IMMUTABLE(real_inode))
+ return ERR_PTR(-EPERM);
+ if (IS_APPEND(real_inode) && !(flags & O_APPEND))
+ return ERR_PTR(-EPERM);
+ }
+
f = alloc_empty_backing_file(flags, cred, user_file);
if (IS_ERR(f))
return f;
+ /*
+ * A user file that must not generate fsnotify events (e.g. the fd
+ * that fanotify creates for a permission event with
+ * dentry_open_nonotify()) must not generate them through its backing
+ * file either. Otherwise a listener watching the backing filesystem
+ * gets a nested permission event from its own read(2), which only
+ * another reader of the same group can answer.
+ */
+ if (FMODE_FSNOTIFY_NONE(user_file->f_mode))
+ file_set_fsnotify_mode(f, FMODE_NONOTIFY);
+
path_get(user_path);
backing_file_set_user_path(f, user_path);
error = vfs_open(real_path, f);
@@ -222,6 +245,14 @@ ssize_t backing_file_write_iter(struct file *file, struct iov_iter *iter,
if (!iov_iter_count(iter))
return 0;
+ /*
+ * F_SETFL and RWF_NOAPPEND can take append mode away from the user
+ * file. They checked its inode, not the backing one.
+ */
+ if ((file->f_flags & O_APPEND) && !(flags & IOCB_APPEND) &&
+ IS_APPEND(file_inode(file)))
+ return -EPERM;
+
ret = file_remove_privs(iocb->ki_filp);
if (ret)
return ret;
@@ -309,6 +340,10 @@ ssize_t backing_file_splice_write(struct pipe_inode_info *pipe,
if (!out->f_op->splice_write)
return -EINVAL;
+ /* splice refuses a user file in append mode, so F_SETFL cleared it */
+ if ((out->f_flags & O_APPEND) && IS_APPEND(file_inode(out)))
+ return -EPERM;
+
ret = file_remove_privs(iocb->ki_filp);
if (ret)
return ret;
@@ -339,6 +374,10 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma,
if (!can_mmap_file(file))
return -ENODEV;
+ /* do_mmap() checked the inode of the user file */
+ if ((vma->vm_flags & VM_SHARED) && IS_APPEND(file_inode(file)))
+ return -EACCES;
+
vma_set_file(vma, file);
old_cred = override_creds(ctx->cred);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 62858d8d66bd..6287e15bce11 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -8143,7 +8143,6 @@ static int btrfs_getattr(struct mnt_idmap *idmap,
struct inode *inode = d_inode(path->dentry);
u32 blocksize = btrfs_sb(inode->i_sb)->sectorsize;
u32 bi_flags = BTRFS_I(inode)->flags;
- u32 bi_ro_flags = BTRFS_I(inode)->ro_flags;
stat->result_mask |= STATX_BTIME;
stat->btime.tv_sec = BTRFS_I(inode)->i_otime_sec;
@@ -8156,8 +8155,6 @@ static int btrfs_getattr(struct mnt_idmap *idmap,
stat->attributes |= STATX_ATTR_IMMUTABLE;
if (bi_flags & BTRFS_INODE_NODUMP)
stat->attributes |= STATX_ATTR_NODUMP;
- if (bi_ro_flags & BTRFS_INODE_RO_VERITY)
- stat->attributes |= STATX_ATTR_VERITY;
stat->attributes_mask |= (STATX_ATTR_APPEND |
STATX_ATTR_COMPRESSED |
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 6cc4fe2ccd8a..61a0575fd162 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -2284,6 +2284,9 @@ static long btrfs_control_ioctl(struct file *file, unsigned int cmd,
case BTRFS_IOC_GET_SUPPORTED_FEATURES:
ret = btrfs_ioctl_get_supported_features((void __user*)arg);
break;
+ default:
+ ret = -ENOTTY;
+ break;
}
out:
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 200b4e4a0799..a07b5a4d6e4d 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -8302,6 +8302,7 @@ static int relocating_repair_kthread(void *data)
bool btrfs_repair_one_zone(struct btrfs_fs_info *fs_info, u64 logical)
{
struct btrfs_block_group *cache;
+ struct task_struct *task;
if (!btrfs_is_zoned(fs_info))
return false;
@@ -8319,8 +8320,9 @@ bool btrfs_repair_one_zone(struct btrfs_fs_info *fs_info, u64 logical)
return true;
}
- kthread_run(relocating_repair_kthread, cache,
- "btrfs-relocating-repair");
+ task = kthread_run(relocating_repair_kthread, cache, "btrfs-relocating-repair");
+ if (IS_ERR(task))
+ btrfs_put_block_group(cache);
return true;
}
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index cfbd88412f04..51aeae341f56 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -314,6 +314,12 @@ static bool ceph_netfs_issue_op_inline(struct netfs_io_subrequest *subreq)
return false;
}
+ if (subreq->start >= iinfo->inline_len) {
+ ceph_mdsc_put_request(req);
+ err = -ENODATA;
+ goto out;
+ }
+
len = min_t(size_t, iinfo->inline_len - subreq->start, subreq->len);
err = copy_to_iter(iinfo->inline_data + subreq->start, len, &subreq->io_iter);
if (err == 0) {
@@ -356,7 +362,7 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
struct ceph_client *cl = fsc->client;
struct ceph_osd_request *req = NULL;
struct ceph_vino vino = ceph_vino(inode);
- int err;
+ int err = 0;
u64 len;
bool sparse = IS_ENCRYPTED(inode) || ceph_test_mount_opt(fsc, SPARSEREAD);
u64 off = subreq->start;
@@ -1883,7 +1889,12 @@ static int ceph_netfs_check_write_begin(struct file *file, loff_t pos, unsigned
r = wait_event_killable(ci->i_cap_wq,
context_is_writeable_or_written(inode, snapc));
ceph_put_snap_context(snapc);
- return r == 0 ? -EAGAIN : r;
+ /*
+ * *foliop is cleared, so a return of 0 makes
+ * netfs_write_begin() grab the folio again and call us again
+ * to recheck it. Any error (fatal signal) fails the write.
+ */
+ return r;
}
return 0;
}
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index 91cb305815cb..3dfd230412cc 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -1561,6 +1561,8 @@ static void __send_cap(struct cap_msg_args *arg, struct ceph_inode_info *ci)
spin_lock(&ci->i_ceph_lock);
__cap_delay_requeue(arg->session->s_mdsc, ci);
spin_unlock(&ci->i_ceph_lock);
+ ceph_buffer_put(arg->old_xattr_buf);
+ ceph_buffer_put(arg->xattr_buf);
return;
}
@@ -2878,6 +2880,10 @@ static int try_get_cap_refs(struct inode *inode, int need, int want,
}
/* finish pending truncate */
+ if (ci->i_truncate_pending && (flags & NON_BLOCKING)) {
+ ret = -EAGAIN;
+ goto out_unlock;
+ }
while (ci->i_truncate_pending) {
spin_unlock(&ci->i_ceph_lock);
if (snap_rwsem_locked) {
diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c
index a236035f3a7a..f23ba5c14878 100644
--- a/fs/ceph/crypto.c
+++ b/fs/ceph/crypto.c
@@ -83,6 +83,9 @@ static int ceph_crypt_get_context(struct inode *inode, void *ctx, size_t len)
return -ENOBUFS;
ctxlen = le32_to_cpu(cfa->cfa_blob_len);
+ if (ctxlen > ci->fscrypt_auth_len -
+ offsetof(struct ceph_fscrypt_auth, cfa_blob))
+ return -ENOBUFS;
if (len < ctxlen)
return -ERANGE;
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 342d8778b69c..0220daac0fba 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -14,6 +14,7 @@
#include <linux/ktime.h>
#include <linux/bitmap.h>
#include <linux/mnt_idmapping.h>
+#include <linux/overflow.h>
#include "super.h"
#include "mds_client.h"
@@ -104,6 +105,7 @@ static int parse_reply_info_in(void **p, void *end,
u64 features)
{
int err = 0;
+ size_t splits_len;
u8 struct_v = 0;
if (features == (u64)-1) {
@@ -122,9 +124,11 @@ static int parse_reply_info_in(void **p, void *end,
ceph_decode_need(p, end, sizeof(struct ceph_mds_reply_inode), bad);
info->in = *p;
- *p += sizeof(struct ceph_mds_reply_inode) +
- sizeof(*info->in->fragtree.splits) *
- le32_to_cpu(info->in->fragtree.nsplits);
+ splits_len = size_mul(sizeof(*info->in->fragtree.splits),
+ le32_to_cpu(info->in->fragtree.nsplits));
+ *p += sizeof(struct ceph_mds_reply_inode);
+ ceph_decode_need(p, end, splits_len, bad);
+ *p += splits_len;
ceph_decode_32_safe(p, end, info->symlink_len, bad);
ceph_decode_need(p, end, info->symlink_len, bad);
@@ -338,6 +342,8 @@ static int parse_reply_info_lease(void **p, void *end,
lend = *p + struct_len;
ceph_decode_need(p, end, struct_len, bad);
+ if (struct_len < sizeof(**lease))
+ goto bad;
*lease = *p;
*p += sizeof(**lease);
@@ -434,10 +440,8 @@ static int parse_reply_info_readdir(void **p, void *end,
goto done;
BUG_ON(!info->dir_entries);
- if ((unsigned long)(info->dir_entries + num) >
- (unsigned long)info->dir_entries + info->dir_buf_size) {
+ if (num > info->dir_buf_size / sizeof(*info->dir_entries)) {
pr_err_client(cl, "dir contents are larger than expected\n");
- WARN_ON(1);
goto bad;
}
@@ -1756,8 +1760,10 @@ __open_export_target_session(struct ceph_mds_client *mdsc, int target)
if (session->s_state == CEPH_MDS_SESSION_NEW ||
session->s_state == CEPH_MDS_SESSION_CLOSING) {
ret = __open_session(mdsc, session);
- if (ret)
+ if (ret) {
+ ceph_put_mds_session(session);
return ERR_PTR(ret);
+ }
}
return session;
@@ -3812,6 +3818,14 @@ int ceph_mdsc_wait_request(struct ceph_mds_client *mdsc,
/* only abort if we didn't race with a real reply */
if (test_bit(CEPH_MDS_R_GOT_RESULT, &req->r_req_flags)) {
err = le32_to_cpu(req->r_reply_info.head->result);
+ if (err > 0 || err < -MAX_ERRNO) {
+ /*
+ * The result is server-controlled. Values outside
+ * the errno range are turned into bogus pointers by
+ * the ERR_PTR() users among the callers.
+ */
+ err = -EIO;
+ }
} else if (err < 0) {
doutc(cl, "aborted request %lld with %d\n", req->r_tid, err);
diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c
index c91c0e2e118b..d377d13f854d 100644
--- a/fs/ceph/mdsmap.c
+++ b/fs/ceph/mdsmap.c
@@ -170,6 +170,10 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
* and the mds rank >= m_num_active_mds.
*/
m->possible_max_rank = max(m->m_num_active_mds, m->m_max_mds);
+ if (m->possible_max_rank > CEPH_MAX_MDS) {
+ err = -EIO;
+ goto corrupt;
+ }
m->m_info = kcalloc(m->possible_max_rank, sizeof(*m->m_info), GFP_NOFS);
if (!m->m_info)
@@ -205,7 +209,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
ceph_decode_need(p, end, sizeof(u64) + sizeof(u32), bad);
*p += sizeof(u64);
namelen = ceph_decode_32(p); /* skip mds name */
- *p += namelen;
+ ceph_decode_skip_n(p, end, namelen, bad);
ceph_decode_32_safe(p, end, mds, bad);
ceph_decode_32_safe(p, end, inc, bad);
@@ -223,7 +227,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
laggy = laggy_since.tv_sec != 0 || laggy_since.tv_nsec != 0;
*p += sizeof(u32);
ceph_decode_32_safe(p, end, namelen, bad);
- *p += namelen;
+ ceph_decode_skip_n(p, end, namelen, bad);
if (info_v >= 2) {
ceph_decode_32_safe(p, end, num_export_targets, bad);
export_targets_len = size_mul(num_export_targets,
@@ -316,6 +320,10 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
{
int num_laggy = 0;
ceph_decode_32_safe(p, end, n, bad_ext);
+ if (n > CEPH_MAX_MDS) {
+ err = -EIO;
+ goto corrupt;
+ }
ceph_decode_need(p, end, sizeof(u32) * n, bad_ext);
for (i = 0; i < n; i++) {
diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c
index c65f2b202b2b..00cfcef5fbb2 100644
--- a/fs/ceph/snap.c
+++ b/fs/ceph/snap.c
@@ -788,6 +788,7 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc,
struct ceph_snap_realm *first_realm = NULL;
struct ceph_snap_realm *realm_to_rebuild = NULL;
struct ceph_client *client = mdsc->fsc->client;
+ size_t snaps_len;
int rebuild_snapcs;
int err = -ENOMEM;
int ret;
@@ -802,8 +803,10 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc,
ceph_decode_need(&p, e, sizeof(*ri), bad);
ri = p;
p += sizeof(*ri);
- ceph_decode_need(&p, e, sizeof(u64)*(le32_to_cpu(ri->num_snaps) +
- le32_to_cpu(ri->num_prior_parent_snaps)), bad);
+ snaps_len = size_mul(size_add(le32_to_cpu(ri->num_snaps),
+ le32_to_cpu(ri->num_prior_parent_snaps)),
+ sizeof(u64));
+ ceph_decode_need(&p, e, snaps_len, bad);
snaps = p;
p += sizeof(u64) * le32_to_cpu(ri->num_snaps);
prior_parent_snaps = p;
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index cc73b0874ce4..90a9e7de8fea 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -11,6 +11,7 @@
#include <linux/security.h>
#include <linux/posix_acl_xattr.h>
#include <linux/slab.h>
+#include <linux/overflow.h>
#define XATTR_CEPH_PREFIX "ceph."
#define XATTR_CEPH_PREFIX_LEN (sizeof (XATTR_CEPH_PREFIX) - 1)
@@ -845,14 +846,23 @@ static int __build_xattrs(struct inode *inode)
}
err = -EIO;
while (numattr--) {
+ int names_size, vals_size;
+
ceph_decode_32_safe(&p, end, len, bad);
namelen = len;
name = p;
+ ceph_decode_need(&p, end, namelen, bad);
p += len;
ceph_decode_32_safe(&p, end, len, bad);
ceph_decode_need(&p, end, len, bad);
val = p;
p += len;
+ if (namelen > INT_MAX || len > INT_MAX ||
+ check_add_overflow(ci->i_xattrs.names_size,
+ (int)namelen, &names_size) ||
+ check_add_overflow(ci->i_xattrs.vals_size,
+ (int)len, &vals_size))
+ goto bad;
err = __set_xattr(ci, name, namelen, val, len,
0, 0, &xattrs[numattr]);
diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c
index f27997ef9a08..707552228ed6 100644
--- a/fs/crypto/keysetup.c
+++ b/fs/crypto/keysetup.c
@@ -207,8 +207,14 @@ fscrypt_find_mode_key(struct fscrypt_master_key *mk, u8 hkdf_context,
if (node->hkdf_context == hkdf_context &&
node->mode_num == mode_num &&
node->data_unit_bits == ci->ci_data_unit_bits &&
- fscrypt_is_key_prepared(&node->key, ci))
+ fscrypt_is_key_prepared(&node->key, ci)) {
+ /*
+ * Upgrade the RCU dereference to a full ACQUIRE, so
+ * that blk-crypto global state is seen as initialized.
+ */
+ smp_acquire__after_ctrl_dep();
return &node->key;
+ }
}
return NULL;
}
diff --git a/fs/dax.c b/fs/dax.c
index 69ac2562cec3..30d0c7e4b30c 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -2168,6 +2168,7 @@ static int dax_range_compare_iter(struct iomap_iter *it_src,
loff_t pos1 = it_src->pos, pos2 = it_dest->pos;
u64 dest_len;
void *saddr, *daddr;
+ size_t size1, size2;
int id, ret;
len = min(len, min(smap->length, dmap->length));
@@ -2183,17 +2184,18 @@ static int dax_range_compare_iter(struct iomap_iter *it_src,
}
id = dax_read_lock();
- ret = dax_iomap_direct_access(smap, pos1, ALIGN(pos1 + len, PAGE_SIZE),
- &saddr, NULL);
+ size1 = ALIGN(len + offset_in_page(pos1), PAGE_SIZE);
+ ret = dax_iomap_direct_access(smap, pos1, size1, &saddr, NULL);
if (ret < 0)
goto out_unlock;
- ret = dax_iomap_direct_access(dmap, pos2, ALIGN(pos2 + len, PAGE_SIZE),
- &daddr, NULL);
+ size2 = ALIGN(len + offset_in_page(pos2), PAGE_SIZE);
+ ret = dax_iomap_direct_access(dmap, pos2, size2, &daddr, NULL);
if (ret < 0)
goto out_unlock;
- *same = !memcmp(saddr, daddr, len);
+ *same = !memcmp(saddr + offset_in_page(pos1),
+ daddr + offset_in_page(pos2), len);
if (!*same)
len = 0;
dax_read_unlock(id);
@@ -2233,7 +2235,7 @@ int dax_dedupe_file_range_compare(struct inode *src, loff_t srcoff,
status = dax_range_compare_iter(&src_iter, &dst_iter,
min(src_iter.len, dst_iter.len), same);
if (status < 0)
- return ret;
+ return status;
src_iter.status = dst_iter.status = status;
}
return ret;
diff --git a/fs/dcache.c b/fs/dcache.c
index 5ee1c3d592c8..3ad74a3abf9a 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -442,6 +442,16 @@ static void dentry_free(struct dentry *dentry)
call_rcu(&dentry->d_u.d_rcu, __d_free);
}
+/*
+ * When inode is unlinked and doesn't have any aliases (i.e., all fds pointing
+ * to it are closed) we report FS_DELETE_SELF to fsnotify and cleanup all inode
+ * marks.
+ */
+static bool inode_notify_dead(struct inode *inode)
+{
+ return !inode->i_nlink && hlist_empty(&inode->i_dentry);
+}
+
/*
* Release the dentry's inode, using the filesystem
* d_iput() operation if defined.
@@ -451,14 +461,16 @@ static void dentry_unlink_inode(struct dentry * dentry)
__releases(dentry->d_inode->i_lock)
{
struct inode *inode = dentry->d_inode;
+ bool notify_dead;
raw_write_seqcount_begin(&dentry->d_seq);
__d_clear_type_and_inode(dentry);
hlist_del_init(&dentry->d_u.d_alias);
raw_write_seqcount_end(&dentry->d_seq);
+ notify_dead = inode_notify_dead(inode);
spin_unlock(&dentry->d_lock);
spin_unlock(&inode->i_lock);
- if (!inode->i_nlink)
+ if (notify_dead)
fsnotify_inoderemove(inode);
if (dentry->d_op && dentry->d_op->d_iput)
dentry->d_op->d_iput(dentry, inode);
@@ -673,7 +685,7 @@ static struct dentry *__dentry_kill(struct dentry *dentry)
if (dentry->d_op && dentry->d_op->d_release)
dentry->d_op->d_release(dentry);
- cond_resched();
+ cond_resched_tasks_rcu_qs();
/* now that it's negative, ->d_parent is stable */
if (!IS_ROOT(dentry)) {
parent = dentry->d_parent;
diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c
index 9a0b6c2b6b01..dd68198f34dd 100644
--- a/fs/dlm/lowcomms.c
+++ b/fs/dlm/lowcomms.c
@@ -520,10 +520,8 @@ static void lowcomms_write_space(struct sock *sk)
clear_bit(SOCK_NOSPACE, &con->sock->flags);
spin_lock_bh(&con->writequeue_lock);
- if (test_and_clear_bit(CF_APP_LIMITED, &con->flags)) {
+ if (test_and_clear_bit(CF_APP_LIMITED, &con->flags))
con->sock->sk->sk_write_pending--;
- clear_bit(SOCKWQ_ASYNC_NOSPACE, &con->sock->flags);
- }
lowcomms_queue_swork(con);
spin_unlock_bh(&con->writequeue_lock);
@@ -1389,7 +1387,7 @@ static int send_to_sock(struct connection *con)
if (ret == -EAGAIN || ret == 0) {
lock_sock(con->sock->sk);
spin_lock_bh(&con->writequeue_lock);
- if (test_bit(SOCKWQ_ASYNC_NOSPACE, &con->sock->flags) &&
+ if (test_bit(SOCK_NOSPACE, &con->sock->flags) &&
!test_and_set_bit(CF_APP_LIMITED, &con->flags)) {
/* Notify TCP that we're limited by the
* application window size.
diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c
index 48a0c84cec75..858fe65ba84d 100644
--- a/fs/erofs/decompressor.c
+++ b/fs/erofs/decompressor.c
@@ -23,7 +23,7 @@ static int z_erofs_load_lz4_config(struct super_block *sb,
if (!sbi->lz4.max_pclusterblks) {
sbi->lz4.max_pclusterblks = 1; /* reserved case */
} else if (sbi->lz4.max_pclusterblks >
- erofs_blknr(sb, Z_EROFS_PCLUSTER_MAX_SIZE)) {
+ Z_EROFS_PCLUSTER_MAX_SIZE >> sbi->blkszbits) {
erofs_err(sb, "too large lz4 pclusterblks %u",
sbi->lz4.max_pclusterblks);
return -EINVAL;
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
index d3e55de4a2a2..ab470783e798 100644
--- a/fs/exportfs/expfs.c
+++ b/fs/exportfs/expfs.c
@@ -336,7 +336,11 @@ static int get_name(const struct path *path, char *name, struct dentry *child)
}
out_close:
- fput(file);
+ /*
+ * @file is the read-only directory opened above, so __fput()
+ * cannot block on anything the caller holds.
+ */
+ __fput_sync(file);
out:
return error;
}
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index 08556dd45760..b7d59eec7b3f 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -329,7 +329,7 @@ struct ext4_group_info *ext4_get_group_info(struct super_block *sb,
struct ext4_group_info **grp_info;
long indexv, indexh;
- if (unlikely(group >= EXT4_SB(sb)->s_groups_count))
+ if (unlikely(group >= ext4_get_groups_count(sb)))
return NULL;
if (unlikely(!EXT4_SB(sb)->s_group_info))
return NULL;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index ed546c52ebc3..9c67a2c28faf 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -3809,6 +3809,9 @@ extern void ext4_ext_tree_init(handle_t *handle, struct inode *inode);
extern int ext4_ext_index_trans_blocks(struct inode *inode, int extents);
extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
struct ext4_map_blocks *map, int flags);
+ext4_lblk_t ext4_determine_insert_hole(struct inode *inode, ext4_lblk_t lblk,
+ ext4_lblk_t hole_start,
+ ext4_lblk_t hole_len);
extern int ext4_ext_truncate(handle_t *, struct inode *);
extern int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
ext4_lblk_t end);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 21fba12520a8..80d41be51564 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4160,21 +4160,19 @@ static int get_implied_cluster_alloc(struct super_block *sb,
}
/*
- * Determine hole length around the given logical block, first try to
- * locate and expand the hole from the given @path, and then adjust it
- * if it's partially or completely converted to delayed extents, insert
- * it into the extent cache tree if it's indeed a hole, finally return
- * the length of the determined extent.
+ * Adjust the hole [@hole_start, @hole_start + @hole_len) the caller found
+ * around the queried block @lblk if it's partially or completely converted
+ * to delayed extents, insert it into the extent cache tree if it's indeed a
+ * hole, finally return the length of the determined extent starting at
+ * @lblk. @hole_start must not be behind @lblk.
*/
-static ext4_lblk_t ext4_ext_determine_insert_hole(struct inode *inode,
- struct ext4_ext_path *path,
- ext4_lblk_t lblk)
+ext4_lblk_t ext4_determine_insert_hole(struct inode *inode, ext4_lblk_t lblk,
+ ext4_lblk_t hole_start,
+ ext4_lblk_t hole_len)
{
- ext4_lblk_t hole_start, len;
+ ext4_lblk_t len = hole_len;
struct extent_status es;
- hole_start = lblk;
- len = ext4_ext_find_hole(inode, path, &hole_start);
again:
ext4_es_find_extent_range(inode, &ext4_es_is_delayed, hole_start,
hole_start + len - 1, &es);
@@ -4342,9 +4340,11 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
* we couldn't try to create block if flags doesn't contain EXT4_GET_BLOCKS_CREATE
*/
if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) {
- ext4_lblk_t len;
+ ext4_lblk_t hole_start = map->m_lblk, len;
- len = ext4_ext_determine_insert_hole(inode, path, map->m_lblk);
+ len = ext4_ext_find_hole(inode, path, &hole_start);
+ len = ext4_determine_insert_hole(inode, map->m_lblk,
+ hole_start, len);
map->m_pblk = 0;
map->m_len = min_t(unsigned int, map->m_len, len);
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
index da76353b3a57..50c567123b42 100644
--- a/fs/ext4/indirect.c
+++ b/fs/ext4/indirect.c
@@ -586,6 +586,23 @@ int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
for (i = partial - chain + 1; i < depth; i++)
count = count * epb + (epb - offsets[i] - 1);
count++;
+
+ /*
+ * The count knows nothing about delayed allocation, so let
+ * the common helper reconcile it with the extent status
+ * tree. Only when the lookup really did find a hole:
+ * ext4_get_branch() also returns a non-NULL partial when it
+ * failed, and caching a hole for a range it could not read
+ * would answer every later read from that cache instead of
+ * retrying. Clamp the count first, a subtree can be bigger
+ * than the logical block space when the block size is large.
+ */
+ if (!err) {
+ count = umin(count, EXT_MAX_BLOCKS - map->m_lblk);
+ count = ext4_determine_insert_hole(inode, map->m_lblk,
+ map->m_lblk, count);
+ }
+
/* Fill in size of a hole we found */
map->m_pblk = 0;
map->m_len = umin(map->m_len, count);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index c3fb5f16f6e3..47ce682ae17f 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -6216,15 +6216,12 @@ int ext4_getattr(struct mnt_idmap *idmap, const struct path *path,
stat->attributes |= STATX_ATTR_IMMUTABLE;
if (flags & EXT4_NODUMP_FL)
stat->attributes |= STATX_ATTR_NODUMP;
- if (flags & EXT4_VERITY_FL)
- stat->attributes |= STATX_ATTR_VERITY;
stat->attributes_mask |= (STATX_ATTR_APPEND |
STATX_ATTR_COMPRESSED |
STATX_ATTR_ENCRYPTED |
STATX_ATTR_IMMUTABLE |
- STATX_ATTR_NODUMP |
- STATX_ATTR_VERITY);
+ STATX_ATTR_NODUMP);
generic_fillattr(idmap, request_mask, inode, stat);
return 0;
@@ -6482,8 +6479,14 @@ static int ext4_try_to_expand_extra_isize(struct inode *inode,
* When !SB_ACTIVE, iput triggers write_inode_now() which acquires
* s_writepages_rwsem, causing a deadlock with the caller's active
* jbd2 handle (lock order: s_writepages_rwsem -> jbd2_handle).
+ *
+ * Skip it while unmounting as well. shrink_dcache_for_umount()
+ * clears s_root before generic_shutdown_super() clears SB_ACTIVE, and
+ * the last iput() of a lazytime inode in that window redirties it and
+ * lands here. Moving xattrs out to a block then needs a new EA inode,
+ * which ext4_xattr_inode_create() refuses without s_root.
*/
- if (unlikely(!(inode->i_sb->s_flags & SB_ACTIVE)))
+ if (unlikely(!(inode->i_sb->s_flags & SB_ACTIVE) || !inode->i_sb->s_root))
return -EBUSY;
/*
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 9aec4ee780a7..cc85786be270 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -1360,7 +1360,8 @@ static void ext4_sb_setparams(struct ext4_sb_info *sbi,
es->s_encoding = cpu_to_le16(params->encoding);
if (params->set_flags & EXT4_TUNE_FL_ENCODING_FLAGS)
es->s_encoding_flags = cpu_to_le16(params->encoding_flags);
- strscpy_pad(es->s_mount_opts, params->mount_opts);
+ if (params->set_flags & EXT4_TUNE_FL_MOUNT_OPTS)
+ strscpy_pad(es->s_mount_opts, params->mount_opts);
if (params->set_flags & EXT4_TUNE_FL_EDIT_FEATURES) {
es->s_feature_compat |=
cpu_to_le32(params->set_feature_compat_mask);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 2c661155158f..49bd70aab225 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3204,19 +3204,20 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
ext4_fc_track_unlink(handle, dentry);
retval = ext4_mark_inode_dirty(handle, dir);
+end_rmdir:
+ brelse(bh);
+ if (handle)
+ ext4_journal_stop(handle);
+
/* VFS negative dentries are incompatible with Encoding and
* Case-insensitiveness. Eventually we'll want avoid
* invalidating the dentries here, alongside with returning the
* negative dentries at ext4_lookup(), when it is better
* supported by the VFS for the CI case.
*/
- if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
+ if (!retval && IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
d_invalidate(dentry);
-end_rmdir:
- brelse(bh);
- if (handle)
- ext4_journal_stop(handle);
return retval;
}
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index b4ff7e692986..613b04b64b14 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -2783,6 +2783,14 @@ static int ext4_check_opt_consistency(struct fs_context *fc,
}
if (is_remount) {
+ if (test_opt(sb, DELALLOC) &&
+ (ctx->mask_s_mount_opt & EXT4_MOUNT_DELALLOC) &&
+ !ctx_test_mount_opt(ctx, EXT4_MOUNT_DELALLOC)) {
+ ext4_msg(sb, KERN_ERR,
+ "can't disable delalloc during remount");
+ return -EINVAL;
+ }
+
if (!sbi->s_journal &&
ctx_test_mount_opt(ctx, EXT4_MOUNT_DATA_ERR_ABORT)) {
ext4_msg(NULL, KERN_WARNING,
@@ -6016,6 +6024,7 @@ static int ext4_load_journal(struct super_block *sb,
int err = 0;
int really_read_only;
int journal_dev_ro;
+ bool enable_write = false;
if (WARN_ON_ONCE(!ext4_has_feature_journal(sb)))
return -EFSCORRUPTED;
@@ -6072,6 +6081,7 @@ static int ext4_load_journal(struct super_block *sb,
}
ext4_msg(sb, KERN_INFO, "write access will "
"be enabled during recovery");
+ enable_write = true;
}
}
@@ -6088,7 +6098,19 @@ static int ext4_load_journal(struct super_block *sb,
if (save)
memcpy(save, ((char *) es) +
EXT4_S_ERR_START, EXT4_S_ERR_LEN);
+ /*
+ * Fast commit replay performs regular ext4 metadata updates
+ * (see ext4_fc_replay()) which refuse to run on a read-only
+ * superblock. We promised write access above, so make that
+ * true for the duration of the recovery, the same way
+ * ext4_orphan_cleanup() does. The superblock is not published
+ * yet, so nothing can observe the transient state.
+ */
+ if (enable_write)
+ sb->s_flags &= ~SB_RDONLY;
err = jbd2_journal_load(journal);
+ if (enable_write)
+ sb->s_flags |= SB_RDONLY;
if (save && memcmp(((char *) es) + EXT4_S_ERR_START,
save, EXT4_S_ERR_LEN)) {
memcpy(((char *) es) + EXT4_S_ERR_START,
@@ -6581,13 +6603,6 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb)
goto restore_opts;
}
- if ((old_opts.s_mount_opt & EXT4_MOUNT_DELALLOC) &&
- !test_opt(sb, DELALLOC)) {
- ext4_msg(sb, KERN_ERR, "can't disable delalloc during remount");
- err = -EINVAL;
- goto restore_opts;
- }
-
sb->s_flags = (sb->s_flags & ~SB_POSIXACL) |
(test_opt(sb, POSIX_ACL) ? SB_POSIXACL : 0);
diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index 0399db98fccf..8fea48dc50ba 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -1219,6 +1219,7 @@ int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock)
int i;
int err;
+repeat:
err = f2fs_is_compressed_cluster(inode, start_idx);
if (err < 0)
return err;
@@ -1230,12 +1231,11 @@ int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock)
/* truncate compressed cluster */
err = f2fs_prepare_compress_overwrite(inode, &pagep,
start_idx, &fsdata);
-
- /* should not be a normal cluster */
- f2fs_bug_on(F2FS_I_SB(inode), err == 0);
-
- if (err <= 0)
+ if (err < 0)
return err;
+ else if (err == 0)
+ /* the cluster became non-compressed one due to race case */
+ goto repeat;
rpages = fsdata;
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 3455565b8245..eba1e025b9d6 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2186,7 +2186,6 @@ static int f2fs_read_single_page(struct inode *inode, struct folio *folio,
got_it:
if ((map->m_flags & F2FS_MAP_MAPPED)) {
block_nr = map->m_pblk + block_in_file - map->m_lblk;
- folio_set_mappedtodisk(folio);
if (!f2fs_is_valid_blkaddr(F2FS_I_SB(inode), block_nr,
DATA_GENERIC_ENHANCE_READ)) {
@@ -2675,10 +2674,12 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)
return true;
if (f2fs_used_in_atomic_write(inode))
return true;
- /* rewrite low ratio compress data w/ OPU mode to avoid fragmentation */
- if (f2fs_compressed_file(inode) &&
- F2FS_OPTION(sbi).compress_mode == COMPR_MODE_USER &&
- is_inode_flag_set(inode, FI_ENABLE_COMPRESS))
+ /*
+ * rewrite low ratio compress data w/ OPU mode to avoid fragmentation.
+ * If IO comes from compressed write path and fallback to raw write,
+ * force out‑place to prevent metadata‑data inconsistency.
+ */
+ if (fio && f2fs_compressed_file(inode))
return true;
/* swap file is migrating in aligned write mode */
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 791f1cf198c1..8211ee75fb8f 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -123,6 +123,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct dnode_of_data dn;
bool need_alloc = !f2fs_is_pinned_file(inode);
+ loff_t isize;
int err = 0;
vm_fault_t ret;
@@ -176,9 +177,26 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
file_update_time(vmf->vma->vm_file);
filemap_invalidate_lock_shared(inode->i_mapping);
+ /*
+ * mmap stores bypass the COW inode and would break atomicity. Check
+ * under invalidate_lock: f2fs_ioc_start_atomic_write() holds it while
+ * flushing dirty folios and setting FI_ATOMIC_FILE, so either it sees
+ * our dirty folio and writes it back, or we see the flag.
+ */
+ if (unlikely(f2fs_is_atomic_file(inode))) {
+ err = -EOPNOTSUPP;
+ goto out_sem;
+ }
+
folio_lock(folio);
+ /*
+ * A concurrent buffered write can extend isize, so we store isize in a
+ * local variable to let the zeroing offset match the EOF check below.
+ */
+ isize = i_size_read(inode);
+
if (unlikely(folio->mapping != inode->i_mapping ||
- folio_pos(folio) > i_size_read(inode) ||
+ folio_pos(folio) > isize ||
!folio_test_uptodate(folio))) {
folio_unlock(folio);
err = -EFAULT;
@@ -207,18 +225,11 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
/* wait for GCed page writeback via META_MAPPING */
f2fs_wait_on_block_writeback(inode, dn.data_blkaddr);
- /*
- * check to see if the page is mapped already (no holes)
- */
- if (folio_test_mappedtodisk(folio))
- goto out_sem;
-
/* page is wholly or partially inside EOF */
- if (((loff_t)(folio->index + 1) << PAGE_SHIFT) >
- i_size_read(inode)) {
+ if (((loff_t)(folio->index + 1) << PAGE_SHIFT) > isize) {
loff_t offset;
- offset = i_size_read(inode) & ~PAGE_MASK;
+ offset = isize & ~PAGE_MASK;
folio_zero_segment(folio, offset, folio_size(folio));
}
folio_mark_dirty(folio);
@@ -1058,15 +1069,12 @@ int f2fs_getattr(struct mnt_idmap *idmap, const struct path *path,
stat->attributes |= STATX_ATTR_IMMUTABLE;
if (flags & F2FS_NODUMP_FL)
stat->attributes |= STATX_ATTR_NODUMP;
- if (IS_VERITY(inode))
- stat->attributes |= STATX_ATTR_VERITY;
stat->attributes_mask |= (STATX_ATTR_COMPRESSED |
STATX_ATTR_APPEND |
STATX_ATTR_ENCRYPTED |
STATX_ATTR_IMMUTABLE |
- STATX_ATTR_NODUMP |
- STATX_ATTR_VERITY);
+ STATX_ATTR_NODUMP);
generic_fillattr(idmap, request_mask, inode, stat);
@@ -2084,9 +2092,11 @@ static long f2fs_fallocate(struct file *file, int mode,
/*
* Pinned file should not support partial truncation since the block
- * can be used by applications.
+ * can be used by applications. Atomic files should not either, since
+ * these modify the original inode directly and break atomicity.
*/
- if ((f2fs_compressed_file(inode) || f2fs_is_pinned_file(inode)) &&
+ if ((f2fs_compressed_file(inode) || f2fs_is_pinned_file(inode) ||
+ f2fs_is_atomic_file(inode)) &&
(mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE |
FALLOC_FL_ZERO_RANGE | FALLOC_FL_INSERT_RANGE))) {
ret = -EOPNOTSUPP;
@@ -2371,6 +2381,8 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
f2fs_down_write(&fi->i_gc_rwsem[WRITE]);
f2fs_down_write(&fi->i_gc_rwsem[READ]);
+ /* serialize with f2fs_vm_page_mkwrite(), see the comment there */
+ filemap_invalidate_lock(inode->i_mapping);
/*
* Should wait end_io to count F2FS_WB_CP_DATA correctly by
@@ -2426,6 +2438,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
f2fs_i_size_write(fi->cow_inode, isize);
out_unlock:
+ filemap_invalidate_unlock(inode->i_mapping);
f2fs_up_write(&fi->i_gc_rwsem[READ]);
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
if (ret)
@@ -2991,8 +3004,8 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
/* writeback all dirty pages in the range */
err = filemap_write_and_wait_range(inode->i_mapping,
- pg_start << PAGE_SHIFT,
- (pg_end << PAGE_SHIFT) - 1);
+ (loff_t)pg_start << PAGE_SHIFT,
+ ((loff_t)pg_end << PAGE_SHIFT) - 1);
if (err)
goto out;
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 510d073f8ec5..46f14f891843 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -4087,6 +4087,7 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio)
else
err = f2fs_submit_page_bio(fio);
if (!err) {
+ fio->submitted = 1;
f2fs_update_device_state(fio->sbi, fio->ino,
fio->new_blkaddr, 1);
f2fs_update_iostat(fio->sbi, fio_inode(fio),
@@ -4687,7 +4688,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
down_write(&sit_i->sentry_lock);
- if (!sit_i->dirty_sentries)
+ if (!sit_i->dirty_sentries && to_journal)
goto out;
/*
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 74b12c1ef821..69d36e1a301b 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -3171,15 +3171,42 @@ static qsize_t *f2fs_get_reserved_space(struct inode *inode)
return &F2FS_I(inode)->i_reserved_quota;
}
+/*
+ * Quota files only grow and there are at most MAXQUOTAS of them, so there is
+ * no point in keeping them inline. Writeback of quota files does not handle
+ * inline data and would drop what was written to them.
+ */
+static int f2fs_quota_convert_inline(struct inode *inode)
+{
+ int err;
+
+ if (!f2fs_has_inline_data(inode))
+ return 0;
+
+ inode_lock(inode);
+ err = f2fs_convert_inline_inode(inode);
+ inode_unlock(inode);
+ return err;
+}
+
static int f2fs_quota_on_mount(struct f2fs_sb_info *sbi, int type)
{
+ int err;
+
if (is_set_ckpt_flags(sbi, CP_QUOTA_NEED_FSCK_FLAG)) {
f2fs_err(sbi, "quota sysfile may be corrupted, skip loading it");
return 0;
}
- return dquot_quota_on_mount(sbi->sb, F2FS_OPTION(sbi).s_qf_names[type],
+ err = dquot_quota_on_mount(sbi->sb, F2FS_OPTION(sbi).s_qf_names[type],
F2FS_OPTION(sbi).s_jquota_fmt, type);
+ if (err)
+ return err;
+
+ err = f2fs_quota_convert_inline(sb_dqopt(sbi->sb)->files[type]);
+ if (err)
+ dquot_quota_off(sbi->sb, type);
+ return err;
}
int f2fs_enable_quota_files(struct f2fs_sb_info *sbi, bool rdonly)
@@ -3391,6 +3418,10 @@ static int f2fs_quota_on(struct super_block *sb, int type, int format_id,
inode = d_inode(path->dentry);
+ err = f2fs_quota_convert_inline(inode);
+ if (err)
+ goto out;
+
err = filemap_fdatawrite(inode->i_mapping);
if (err)
goto out;
@@ -3889,6 +3920,12 @@ static inline bool sanity_check_area_boundary(struct f2fs_sb_info *sbi,
/* fix in-memory information all the time */
raw_super->segment_count = cpu_to_le32((main_end_blkaddr -
segment0_blkaddr) >> log_blocks_per_seg);
+ if (__F2FS_HAS_FEATURE(raw_super, F2FS_FEATURE_SB_CHKSUM)) {
+ __u32 crc = f2fs_crc32(raw_super,
+ offsetof(struct f2fs_super_block, crc));
+
+ raw_super->crc = cpu_to_le32(crc);
+ }
if (f2fs_readonly(sb) || f2fs_hw_is_readonly(sbi)) {
set_sbi_flag(sbi, SBI_NEED_SB_WRITE);
@@ -4149,7 +4186,7 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi)
return 1;
if (f2fs_sb_has_readonly(sbi))
- goto check_data;
+ continue;
for (j = i + 1; j < NR_CURSEG_NODE_TYPE; j++) {
if (le32_to_cpu(ckpt->cur_node_segno[i]) ==
@@ -4161,14 +4198,14 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi)
}
}
}
-check_data:
+
for (i = 0; i < NR_CURSEG_DATA_TYPE; i++) {
if (le32_to_cpu(ckpt->cur_data_segno[i]) >= main_segs ||
le16_to_cpu(ckpt->cur_data_blkoff[i]) >= blocks_per_seg)
return 1;
if (f2fs_sb_has_readonly(sbi))
- goto skip_cross;
+ continue;
for (j = i + 1; j < NR_CURSEG_DATA_TYPE; j++) {
if (le32_to_cpu(ckpt->cur_data_segno[i]) ==
@@ -4180,6 +4217,9 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi)
}
}
}
+ if (f2fs_sb_has_readonly(sbi))
+ goto skip_cross;
+
for (i = 0; i < NR_CURSEG_NODE_TYPE; i++) {
for (j = 0; j < NR_CURSEG_DATA_TYPE; j++) {
if (le32_to_cpu(ckpt->cur_node_segno[i]) ==
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index d3e426de5f01..6748dea6cd21 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -389,7 +389,7 @@ extern void fat_ent_access_init(struct super_block *sb);
extern int fat_ent_read(struct inode *inode, struct fat_entry *fatent,
int entry);
extern int fat_ent_write(struct inode *inode, struct fat_entry *fatent,
- int new, int wait);
+ int new, int old, int wait);
extern int fat_alloc_clusters(struct inode *inode, int *cluster,
int nr_cluster);
extern int fat_free_clusters(struct inode *inode, int cluster);
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
index a7061c2ad8e4..515bc0cfc64f 100644
--- a/fs/fat/fatent.c
+++ b/fs/fat/fatent.c
@@ -408,7 +408,7 @@ static int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs,
}
int fat_ent_write(struct inode *inode, struct fat_entry *fatent,
- int new, int wait)
+ int new, int old, int wait)
{
struct super_block *sb = inode->i_sb;
const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
@@ -417,10 +417,25 @@ int fat_ent_write(struct inode *inode, struct fat_entry *fatent,
ops->ent_put(fatent, new);
if (wait) {
err = fat_sync_bhs(fatent->bhs, fatent->nr_bhs);
- if (err)
+ if (err) {
+ /*
+ * bhs are not uptodate after I/O error. So we
+ * can't simply re-dirty to revert. And it
+ * would not have value to write again on I/O
+ * error.
+ */
return err;
+ }
}
- return fat_mirror_bhs(sb, fatent->bhs, fatent->nr_bhs);
+
+ err = fat_mirror_bhs(sb, fatent->bhs, fatent->nr_bhs);
+ if (err) {
+ /* Try to revert if got the error on mirror FAT */
+ ops->ent_put(fatent, old);
+ if (wait)
+ fat_sync_bhs(fatent->bhs, fatent->nr_bhs);
+ }
+ return err;
}
static inline int fat_ent_next(struct msdos_sb_info *sbi,
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 4fc49a614fb8..ca2f9db34da5 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -360,7 +360,8 @@ static int fat_free(struct inode *inode, int skip)
__func__, MSDOS_I(inode)->i_pos);
ret = -EIO;
} else if (ret > 0) {
- err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, wait);
+ err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, ret,
+ wait);
if (err)
ret = err;
}
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index 5c45603bf77e..c0bc4356eded 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -133,11 +133,9 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
ret = fat_ent_read(inode, &fatent, last);
if (ret >= 0) {
int wait = inode_needs_sync(inode);
- int old = ret;
- ret = fat_ent_write(inode, &fatent, new_dclus, wait);
- if (ret < 0)
- fat_ent_write(inode, &fatent, old, wait);
+ ret = fat_ent_write(inode, &fatent, new_dclus, ret,
+ wait);
fatent_brelse(&fatent);
}
if (ret < 0)
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 094d2872881d..3025fc7ddfe8 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -454,12 +454,12 @@ static bool inode_do_switch_wbs(struct inode *inode,
if (inode->i_state & I_DIRTY_ALL) {
/*
* We need to keep b_dirty list sorted by
- * dirtied_time_when. However properly sorting the
+ * dirtied_when. However properly sorting the
* inode in the list gets too expensive when switching
* many inodes. So just attach inode at the end of the
- * dirty list and clobber the dirtied_time_when.
+ * dirty list and clobber the dirtied_when.
*/
- inode->dirtied_time_when = jiffies;
+ inode->dirtied_when = jiffies;
inode_io_list_move_locked(inode, new_wb,
&new_wb->b_dirty);
} else {
@@ -534,6 +534,7 @@ static void process_inode_switch_wbs(struct bdi_writeback *new_wb,
}
}
+ wb_io_lists_depopulated(old_wb);
spin_unlock(&new_wb->list_lock);
spin_unlock(&old_wb->list_lock);
diff --git a/fs/fs_pin.c b/fs/fs_pin.c
index 47ef3c71ce90..1a508f2167e0 100644
--- a/fs/fs_pin.c
+++ b/fs/fs_pin.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/fs.h>
+#include <linux/rculist.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include "internal.h"
@@ -22,8 +23,8 @@ void pin_remove(struct fs_pin *pin)
void pin_insert(struct fs_pin *pin, struct vfsmount *m)
{
spin_lock(&pin_lock);
- hlist_add_head(&pin->s_list, &m->mnt_sb->s_pins);
- hlist_add_head(&pin->m_list, &real_mount(m)->mnt_pins);
+ hlist_add_head_rcu(&pin->s_list, &m->mnt_sb->s_pins);
+ hlist_add_head_rcu(&pin->m_list, &real_mount(m)->mnt_pins);
spin_unlock(&pin_lock);
}
@@ -73,7 +74,7 @@ void mnt_pin_kill(struct mount *m)
while (1) {
struct hlist_node *p;
rcu_read_lock();
- p = READ_ONCE(m->mnt_pins.first);
+ p = rcu_dereference(hlist_first_rcu(&m->mnt_pins));
if (!p) {
rcu_read_unlock();
break;
@@ -87,7 +88,7 @@ void group_pin_kill(struct hlist_head *p)
while (1) {
struct hlist_node *q;
rcu_read_lock();
- q = READ_ONCE(p->first);
+ q = rcu_dereference(hlist_first_rcu(p));
if (!q) {
rcu_read_unlock();
break;
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index a7a3ac5b34f4..f72d74e3c5f0 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -2271,6 +2271,9 @@ static int fuse_launder_folio(struct folio *folio)
.wb_ctx = &data,
};
+ if (fuse_is_bad(wpc.inode))
+ return -EIO;
+
if (folio_clear_dirty_for_io(folio)) {
err = iomap_writeback_folio(&wpc, folio);
err = fuse_iomap_writeback_submit(&wpc, err);
@@ -3075,7 +3078,7 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
}
err = fuse_simple_request(fm, &args);
if (err == -ENOSYS) {
- if (fc->no_copy_file_range_64) {
+ if (args.opcode == FUSE_COPY_FILE_RANGE) {
fc->no_copy_file_range = 1;
err = -EOPNOTSUPP;
} else {
@@ -3086,7 +3089,7 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
if (err)
goto out;
- bytes_copied = fc->no_copy_file_range_64 ?
+ bytes_copied = args.opcode == FUSE_COPY_FILE_RANGE ?
outarg.size : outarg_64.bytes_copied;
if (bytes_copied > len) {
@@ -3094,9 +3097,11 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
goto out;
}
- truncate_inode_pages_range(inode_out->i_mapping,
- ALIGN_DOWN(pos_out, PAGE_SIZE),
- ALIGN(pos_out + bytes_copied, PAGE_SIZE) - 1);
+ if (bytes_copied)
+ truncate_inode_pages_range(inode_out->i_mapping,
+ ALIGN_DOWN(pos_out, PAGE_SIZE),
+ ALIGN(pos_out + bytes_copied,
+ PAGE_SIZE) - 1);
file_update_time(file_out);
fuse_write_update_attr(inode_out, pos_out + bytes_copied, bytes_copied);
diff --git a/fs/fuse/virtio_fs.c b/fs/fuse/virtio_fs.c
index 2b3daa93e299..e475f7f9e83d 100644
--- a/fs/fuse/virtio_fs.c
+++ b/fs/fuse/virtio_fs.c
@@ -454,26 +454,23 @@ static int virtio_fs_add_instance(struct virtio_device *vdev,
return ret;
}
-/* Return the virtio_fs with a given tag, or NULL */
+/* Return the virtio_fs with a given tag, or NULL.
+ * Callers hold virtio_fs_mutex, which also keeps the virtqueues alive.
+ */
static struct virtio_fs *virtio_fs_find_instance(const char *tag)
{
struct virtio_fs *fs;
- mutex_lock(&virtio_fs_mutex);
+ lockdep_assert_held(&virtio_fs_mutex);
list_for_each_entry(fs, &virtio_fs_instances, list) {
if (strcmp(fs->tag, tag) == 0) {
kobject_get(&fs->kobj);
- goto found;
+ return fs;
}
}
- fs = NULL; /* not found */
-
-found:
- mutex_unlock(&virtio_fs_mutex);
-
- return fs;
+ return NULL; /* not found */
}
static void virtio_fs_free_devs(struct virtio_fs *fs)
@@ -1675,13 +1672,17 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
* in fc->iq->priv. Once fuse_conn is going away, it calls ->put()
* to drop the reference to this object.
*/
+ mutex_lock(&virtio_fs_mutex);
fs = virtio_fs_find_instance(fsc->source);
+ if (fs)
+ virtqueue_size = virtqueue_get_vring_size(fs->vqs[VQ_REQUEST].vq);
+ mutex_unlock(&virtio_fs_mutex);
+
if (!fs) {
pr_info("virtio-fs: tag <%s> not found\n", fsc->source);
return -EINVAL;
}
- virtqueue_size = virtqueue_get_vring_size(fs->vqs[VQ_REQUEST].vq);
if (WARN_ON(virtqueue_size <= FUSE_HEADER_OVERHEAD))
goto out_err;
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 0ed81a685fcd..59e3d295a2a1 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -690,7 +690,9 @@ static void gfs2_before_commit(struct gfs2_sbd *sdp, unsigned int limit,
page = mempool_alloc(gfs2_page_pool, GFP_NOIO);
p = page_address(page);
- memcpy_from_page(p, page, bh_offset(bd2->bd_bh), bd2->bd_bh->b_size);
+ memcpy_from_folio(p, bd2->bd_bh->b_folio,
+ bh_offset(bd2->bd_bh),
+ bd2->bd_bh->b_size);
*(__be32 *)p = 0;
clear_buffer_escaped(bd2->bd_bh);
unlock_buffer(bd2->bd_bh);
diff --git a/fs/iomap/fiemap.c b/fs/iomap/fiemap.c
index d11dadff8286..54b824b7edb5 100644
--- a/fs/iomap/fiemap.c
+++ b/fs/iomap/fiemap.c
@@ -76,15 +76,15 @@ int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fi,
while ((ret = iomap_iter(&iter, ops)) > 0)
iter.status = iomap_fiemap_iter(&iter, fi, &prev);
+ /* inode with no (attribute) mapping will give ENOENT */
+ if (ret < 0 && ret != -ENOENT)
+ return ret;
+
if (prev.type != IOMAP_HOLE) {
ret = iomap_to_fiemap(fi, &prev, FIEMAP_EXTENT_LAST);
if (ret < 0)
return ret;
}
-
- /* inode with no (attribute) mapping will give ENOENT */
- if (ret < 0 && ret != -ENOENT)
- return ret;
return 0;
}
EXPORT_SYMBOL_GPL(iomap_fiemap);
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 3433fa14cd01..8a0da54f391d 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -821,6 +821,8 @@ static int isofs_fill_super(struct super_block *s, struct fs_context *fc)
if (!sb_set_blocksize(s, orig_zonesize))
goto out_freesbi;
+ sbi->s_session_start = (sector_t)vol_desc_start <<
+ (ISOFS_BLOCK_BITS - s->s_blocksize_bits);
sbi->s_nls_iocharset = NULL;
#ifdef CONFIG_JOLIET
diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h
index 506555837533..a36a63479743 100644
--- a/fs/isofs/isofs.h
+++ b/fs/isofs/isofs.h
@@ -35,6 +35,8 @@ struct isofs_sb_info {
unsigned long s_firstdatazone;
unsigned long s_log_zone_size;
unsigned long s_max_size;
+ /* Session start in filesystem block units. */
+ sector_t s_session_start;
int s_rock_offset; /* offset of SUSP fields within SU area */
s32 s_sbsector;
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index 869b331eff4f..a4a337301b6f 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -9,6 +9,7 @@
#include <linux/slab.h>
#include <linux/pagemap.h>
+#include <linux/blkdev.h>
#include "isofs.h"
#include "rock.h"
@@ -84,6 +85,10 @@ static void init_rock_state(struct rock_state *rs, struct inode *inode)
*/
static int rock_continue(struct rock_state *rs)
{
+ struct super_block *sb = rs->inode->i_sb;
+ struct isofs_sb_info *sbi = ISOFS_SB(sb);
+ sector_t session_end = sbi->s_session_start +
+ ((sector_t)sbi->s_nzones << (ISOFS_BLOCK_BITS - sb->s_blocksize_bits));
int ret = 1;
int blocksize = 1 << rs->inode->i_blkbits;
const int min_de_size = offsetof(struct rock_ridge, u);
@@ -101,11 +106,13 @@ static int rock_continue(struct rock_state *rs)
goto out;
}
- if ((unsigned)rs->cont_extent >= ISOFS_SB(rs->inode->i_sb)->s_nzones) {
+ if (rs->cont_extent &&
+ (rs->cont_extent < sbi->s_session_start ||
+ rs->cont_extent >= session_end)) {
printk(KERN_NOTICE "rock: corrupted directory entry. "
"extent=%u out of volume (nzones=%lu)\n",
(unsigned)rs->cont_extent,
- ISOFS_SB(rs->inode->i_sb)->s_nzones);
+ sbi->s_nzones);
ret = -EIO;
goto out;
}
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index fa0fb738db0c..1184dbad43e5 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -844,7 +844,6 @@ int jbd2_fc_get_buf(journal_t *journal, struct buffer_head **bh_out)
fc_off = journal->j_fc_off;
blocknr = journal->j_fc_first + fc_off;
- journal->j_fc_off++;
ret = jbd2_journal_bmap(journal, blocknr, &pblock);
if (ret)
return ret;
@@ -854,6 +853,7 @@ int jbd2_fc_get_buf(journal_t *journal, struct buffer_head **bh_out)
return -ENOMEM;
journal->j_fc_wbuf[fc_off] = bh;
+ journal->j_fc_off++;
*bh_out = bh;
diff --git a/fs/namespace.c b/fs/namespace.c
index ab050a939cb5..34e0d14c454b 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -5817,7 +5817,8 @@ static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
ks->bufsize = bufsize;
if (ks->mask & STATMOUNT_STRING_REQ) {
- if (bufsize == sizeof(ks->sm))
+ /* Even the reserved empty string needs room past the fixed part. */
+ if (bufsize <= sizeof(ks->sm))
return -EOVERFLOW;
ks->seq.buf = kvmalloc(seq_size, GFP_KERNEL_ACCOUNT);
@@ -5997,6 +5998,8 @@ static ssize_t do_listmount(struct klistmount *kls, bool reverse)
} else {
if (reverse)
first = mnt_find_id_at_reverse(ns, last_mnt_id - 1);
+ else if (last_mnt_id == U64_MAX)
+ first = NULL; /* nothing comes after the largest id */
else
first = mnt_find_id_at(ns, last_mnt_id + 1);
}
@@ -6373,7 +6376,7 @@ static int mntns_install(struct nsset *nsset, struct ns_common *ns)
if (is_anon_ns(mnt_ns))
return -EINVAL;
- if (fs->users != 1)
+ if (current->fs->users != 1)
return -EINVAL;
get_mnt_ns(mnt_ns);
diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c
index 6ca3d74be1e1..3e137c9ca138 100644
--- a/fs/nilfs2/dir.c
+++ b/fs/nilfs2/dir.c
@@ -231,11 +231,27 @@ static struct nilfs_dir_entry *nilfs_next_entry(struct nilfs_dir_entry *p)
nilfs_rec_len_from_disk(p->rec_len));
}
+static inline unsigned int
+nilfs_validate_entry(char *base, unsigned int offset, unsigned int mask)
+{
+ struct nilfs_dir_entry *de = (struct nilfs_dir_entry *)(base + offset);
+ struct nilfs_dir_entry *p =
+ (struct nilfs_dir_entry *)(base + (offset & mask));
+
+ while ((char *)p < (char *)de) {
+ if (p->rec_len == 0)
+ break;
+ p = nilfs_next_entry(p);
+ }
+ return (char *)p - base;
+}
+
static int nilfs_readdir(struct file *file, struct dir_context *ctx)
{
loff_t pos = ctx->pos;
struct inode *inode = file_inode(file);
struct super_block *sb = inode->i_sb;
+ unsigned int chunk_size = nilfs_chunk_size(inode);
unsigned int offset = pos & ~PAGE_MASK;
unsigned long n = pos >> PAGE_SHIFT;
unsigned long npages = dir_pages(inode);
@@ -254,6 +270,24 @@ static int nilfs_readdir(struct file *file, struct dir_context *ctx)
ctx->pos += PAGE_SIZE - offset;
return -EIO;
}
+ /*
+ * ctx->pos comes from userspace via llseek and may point
+ * into the middle of an entry -- or at unvalidated bytes
+ * of a crafted image. offset is only nonzero here on the
+ * very first iteration (subsequent pages always start at
+ * offset 0, which -- like any chunk boundary -- is always
+ * a legitimate entry start, since no rec_len chain crosses
+ * a chunk boundary). Snap a non-chunk-aligned offset onto
+ * a real entry boundary by walking the chain from the
+ * enclosing chunk's start, as ext2 does; otherwise
+ * dir_emit() copies name_len bytes from a fake entry,
+ * reading past the end of the folio.
+ */
+ if (offset & (chunk_size - 1)) {
+ offset = nilfs_validate_entry(kaddr, offset,
+ ~(chunk_size - 1));
+ ctx->pos = ((loff_t)n << PAGE_SHIFT) + offset;
+ }
de = (struct nilfs_dir_entry *)(kaddr + offset);
limit = kaddr + nilfs_last_byte(inode, n) -
NILFS_DIR_REC_LEN(1);
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index 87ddde159f0c..16e3745ce6e9 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -163,7 +163,7 @@ static int nilfs_writepages(struct address_space *mapping,
int err = 0;
if (sb_rdonly(inode->i_sb)) {
- nilfs_clear_dirty_pages(mapping);
+ nilfs_clear_dirty_pages(mapping, false);
return -EROFS;
}
@@ -897,13 +897,7 @@ void nilfs_evict_inode(struct inode *inode)
nilfs_clear_inode(inode);
- if (IS_SYNC(inode))
- nilfs_set_transaction_flag(NILFS_TI_SYNC);
nilfs_transaction_commit(sb);
- /*
- * May construct a logical segment and may fail in sync mode.
- * But delete_inode has no return value.
- */
}
int nilfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 913e1a472c8f..bcb68f92f05e 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -972,10 +972,10 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
* Return: 0 on success, or one of the following negative error codes on
* failure:
* * %-EFAULT - Failure during execution of requested operation.
+ * * %-EINTR - Interrupted.
* * %-EIO - I/O error.
* * %-ENOMEM - Insufficient memory available.
* * %-ENOSPC - No space left on device (only in a panic state).
- * * %-ERESTARTSYS - Interrupted.
* * %-EROFS - Read only filesystem.
*/
static int nilfs_ioctl_sync(struct inode *inode, struct file *filp,
diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c
index 946b0d3534a5..79e662e2bf33 100644
--- a/fs/nilfs2/mdt.c
+++ b/fs/nilfs2/mdt.c
@@ -407,7 +407,7 @@ static int nilfs_mdt_write_folio(struct folio *folio,
* have dirty folios that try to be flushed in background.
* So, here we simply discard this dirty folio.
*/
- nilfs_clear_folio_dirty(folio);
+ nilfs_clear_folio_dirty(folio, false);
folio_unlock(folio);
return -EROFS;
}
@@ -650,10 +650,10 @@ void nilfs_mdt_restore_from_shadow_map(struct inode *inode)
if (mi->mi_palloc_cache)
nilfs_palloc_clear_cache(inode);
- nilfs_clear_dirty_pages(inode->i_mapping);
+ nilfs_clear_dirty_pages(inode->i_mapping, true);
nilfs_copy_back_pages(inode->i_mapping, shadow->inode->i_mapping);
- nilfs_clear_dirty_pages(ii->i_assoc_inode->i_mapping);
+ nilfs_clear_dirty_pages(ii->i_assoc_inode->i_mapping, true);
nilfs_copy_back_pages(ii->i_assoc_inode->i_mapping,
NILFS_I(shadow->inode)->i_assoc_inode->i_mapping);
diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c
index 89180e2f59fe..969e96ce0b84 100644
--- a/fs/nilfs2/page.c
+++ b/fs/nilfs2/page.c
@@ -370,8 +370,9 @@ void nilfs_copy_back_pages(struct address_space *dmap,
/**
* nilfs_clear_dirty_pages - discard dirty pages in address space
* @mapping: address space with dirty pages for discarding
+ * @force: whether to clear the dirty state regardless of busy buffer heads
*/
-void nilfs_clear_dirty_pages(struct address_space *mapping)
+void nilfs_clear_dirty_pages(struct address_space *mapping, bool force)
{
struct folio_batch fbatch;
unsigned int i;
@@ -392,7 +393,7 @@ void nilfs_clear_dirty_pages(struct address_space *mapping)
* was acquired. Skip processing in that case.
*/
if (likely(folio->mapping == mapping))
- nilfs_clear_folio_dirty(folio);
+ nilfs_clear_folio_dirty(folio, force);
folio_unlock(folio);
}
@@ -404,13 +405,16 @@ void nilfs_clear_dirty_pages(struct address_space *mapping)
/**
* nilfs_clear_folio_dirty - discard dirty folio
* @folio: dirty folio that will be discarded
+ * @force: whether to clear the states regardless of busy buffer heads
*
* nilfs_clear_folio_dirty() clears working states including dirty state for
- * the folio and its buffers. If the folio has buffers, clear only if it is
- * confirmed that none of the buffer heads are busy (none have valid
- * references and none are locked).
+ * the folio and its buffers. If the folio has buffers and force is false,
+ * clear only if it is confirmed that none of the buffer heads are busy (none
+ * have valid references and none are locked). If force is true, the states
+ * are cleared unconditionally, the caller should guarantee that the folio is
+ * not being modified concurrently.
*/
-void nilfs_clear_folio_dirty(struct folio *folio)
+void nilfs_clear_folio_dirty(struct folio *folio, bool force)
{
struct buffer_head *bh, *head;
@@ -423,24 +427,28 @@ void nilfs_clear_folio_dirty(struct folio *folio)
BIT(BH_Async_Write) | BIT(BH_NILFS_Volatile) |
BIT(BH_NILFS_Checked) | BIT(BH_NILFS_Redirected) |
BIT(BH_Delay));
- bool busy, invalidated = false;
+
+ if (!force) {
+ bool busy, invalidated = false;
recheck_buffers:
- busy = false;
- bh = head;
- do {
- if (atomic_read(&bh->b_count) | buffer_locked(bh)) {
- busy = true;
- break;
+ busy = false;
+ bh = head;
+ do {
+ if (atomic_read(&bh->b_count) |
+ buffer_locked(bh)) {
+ busy = true;
+ break;
+ }
+ } while (bh = bh->b_this_page, bh != head);
+
+ if (busy) {
+ if (invalidated)
+ return;
+ invalidate_bh_lrus();
+ invalidated = true;
+ goto recheck_buffers;
}
- } while (bh = bh->b_this_page, bh != head);
-
- if (busy) {
- if (invalidated)
- return;
- invalidate_bh_lrus();
- invalidated = true;
- goto recheck_buffers;
}
bh = head;
diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h
index 136cd1c143c9..c3ba3468af5c 100644
--- a/fs/nilfs2/page.h
+++ b/fs/nilfs2/page.h
@@ -41,8 +41,8 @@ void nilfs_folio_bug(struct folio *);
int nilfs_copy_dirty_pages(struct address_space *, struct address_space *);
void nilfs_copy_back_pages(struct address_space *, struct address_space *);
-void nilfs_clear_folio_dirty(struct folio *folio);
-void nilfs_clear_dirty_pages(struct address_space *mapping);
+void nilfs_clear_folio_dirty(struct folio *folio, bool force);
+void nilfs_clear_dirty_pages(struct address_space *mapping, bool force);
unsigned int nilfs_page_count_clean_buffers(struct folio *folio,
unsigned int from, unsigned int to);
unsigned long nilfs_find_uncommitted_extent(struct inode *inode,
diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c
index a9c61d0492cb..dfff3bc84bcd 100644
--- a/fs/nilfs2/recovery.c
+++ b/fs/nilfs2/recovery.c
@@ -785,11 +785,11 @@ static void nilfs_abort_roll_forward(struct the_nilfs *nilfs)
*
* Return: 0 on success, or one of the following negative error codes on
* failure:
+ * * %-EINTR - Interrupted.
* * %-EINVAL - Inconsistent filesystem state.
* * %-EIO - I/O error.
* * %-ENOMEM - Insufficient memory available.
* * %-ENOSPC - No space left on device (only in a panic state).
- * * %-ERESTARTSYS - Interrupted.
*/
int nilfs_salvage_orphan_logs(struct the_nilfs *nilfs,
struct super_block *sb,
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index c20340f4e0dc..e967d16b2f13 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -1825,6 +1825,7 @@ static void nilfs_abort_logs(struct list_head *logs, int err)
list_for_each_entry(segbuf, logs, sb_list) {
list_for_each_entry(bh, &segbuf->sb_segsum_buffers,
b_assoc_buffers) {
+ clear_buffer_dirty(bh);
clear_buffer_uptodate(bh);
if (bh->b_folio != bd_folio) {
if (bd_folio)
@@ -1836,6 +1837,7 @@ static void nilfs_abort_logs(struct list_head *logs, int err)
list_for_each_entry(bh, &segbuf->sb_payload_buffers,
b_assoc_buffers) {
if (bh == segbuf->sb_super_root) {
+ clear_buffer_dirty(bh);
clear_buffer_uptodate(bh);
if (bh->b_folio != bd_folio) {
folio_end_writeback(bd_folio);
@@ -2253,7 +2255,7 @@ static int nilfs_segctor_sync(struct nilfs_sc_info *sci)
wake_up(&sci->sc_wait_daemon);
for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
+ set_current_state(TASK_KILLABLE);
/*
* Synchronize only while the log writer thread is alive.
@@ -2267,11 +2269,11 @@ static int nilfs_segctor_sync(struct nilfs_sc_info *sci)
err = wait_req.err;
break;
}
- if (!signal_pending(current)) {
+ if (!fatal_signal_pending(current)) {
schedule();
continue;
}
- err = -ERESTARTSYS;
+ err = -EINTR;
break;
}
finish_wait(&sci->sc_wait_request, &wait_req.wq);
@@ -2305,10 +2307,10 @@ static void nilfs_segctor_wakeup(struct nilfs_sc_info *sci, int err, bool force)
*
* Return: 0 on success, or one of the following negative error codes on
* failure:
+ * * %-EINTR - Interrupted.
* * %-EIO - I/O error (including metadata corruption).
* * %-ENOMEM - Insufficient memory available.
* * %-ENOSPC - No space left on device (only in a panic state).
- * * %-ERESTARTSYS - Interrupted.
* * %-EROFS - Read only filesystem.
*/
int nilfs_construct_segment(struct super_block *sb)
@@ -2335,10 +2337,10 @@ int nilfs_construct_segment(struct super_block *sb)
*
* Return: 0 on success, or one of the following negative error codes on
* failure:
+ * * %-EINTR - Interrupted.
* * %-EIO - I/O error (including metadata corruption).
* * %-ENOMEM - Insufficient memory available.
* * %-ENOSPC - No space left on device (only in a panic state).
- * * %-ERESTARTSYS - Interrupted.
* * %-EROFS - Read only filesystem.
*/
int nilfs_construct_dsync_segment(struct super_block *sb, struct inode *inode,
diff --git a/fs/nilfs2/sysfs.c b/fs/nilfs2/sysfs.c
index bc52afbfc5c7..5bb74a45ad1e 100644
--- a/fs/nilfs2/sysfs.c
+++ b/fs/nilfs2/sysfs.c
@@ -195,8 +195,10 @@ int nilfs_sysfs_create_snapshot_group(struct nilfs_root *root)
"%llu", root->cno);
}
- if (err)
+ if (err) {
kobject_put(&root->snapshot_kobj);
+ wait_for_completion(&root->snapshot_kobj_unregister);
+ }
return err;
}
@@ -204,6 +206,7 @@ int nilfs_sysfs_create_snapshot_group(struct nilfs_root *root)
void nilfs_sysfs_delete_snapshot_group(struct nilfs_root *root)
{
kobject_put(&root->snapshot_kobj);
+ wait_for_completion(&root->snapshot_kobj_unregister);
}
/************************************************************************
diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c
index 9911a484398a..b453229db3af 100644
--- a/fs/nilfs2/the_nilfs.c
+++ b/fs/nilfs2/the_nilfs.c
@@ -887,6 +887,26 @@ nilfs_find_or_create_root(struct the_nilfs *nilfs, __u64 cno)
if (!new)
return NULL;
+ new->cno = cno;
+ new->ifile = NULL;
+ new->nilfs = nilfs;
+ refcount_set(&new->count, 1);
+ atomic64_set(&new->inodes_count, 0);
+ atomic64_set(&new->blocks_count, 0);
+
+ /*
+ * Register the sysfs group before publishing the root in the
+ * checkpoint tree. nilfs_sysfs_create_snapshot_group() can sleep,
+ * so it must run outside ns_cptree_lock; creating it first also
+ * ensures a concurrent nilfs_lookup_root() can never observe a root
+ * whose sysfs registration later fails and gets freed.
+ */
+ err = nilfs_sysfs_create_snapshot_group(new);
+ if (err) {
+ kfree(new);
+ return NULL;
+ }
+
spin_lock(&nilfs->ns_cptree_lock);
p = &nilfs->ns_cptree.rb_node;
@@ -903,29 +923,17 @@ nilfs_find_or_create_root(struct the_nilfs *nilfs, __u64 cno)
} else {
refcount_inc(&root->count);
spin_unlock(&nilfs->ns_cptree_lock);
+ nilfs_sysfs_delete_snapshot_group(new);
kfree(new);
return root;
}
}
- new->cno = cno;
- new->ifile = NULL;
- new->nilfs = nilfs;
- refcount_set(&new->count, 1);
- atomic64_set(&new->inodes_count, 0);
- atomic64_set(&new->blocks_count, 0);
-
rb_link_node(&new->rb_node, parent, p);
rb_insert_color(&new->rb_node, &nilfs->ns_cptree);
spin_unlock(&nilfs->ns_cptree_lock);
- err = nilfs_sysfs_create_snapshot_group(new);
- if (err) {
- kfree(new);
- new = NULL;
- }
-
return new;
}
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index cdb69c23e2e6..bb12baebd0c9 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -650,6 +650,9 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *dir,
unsigned long name2_len = name2 ? name2->len : 0;
unsigned int len, size;
+ if (WARN_ON_ONCE(name_len >= PATH_MAX || name2_len >= PATH_MAX))
+ return NULL;
+
/* Reserve terminating null byte even for empty name */
size = sizeof(*fne) + name_len + name2_len + 2;
if (dir_fh_len)
@@ -658,7 +661,7 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *dir,
size += FANOTIFY_FH_HDR_LEN + dir2_fh_len;
if (child_fh_len)
size += FANOTIFY_FH_HDR_LEN + child_fh_len;
- fne = kmalloc(size, gfp);
+ fne = kvmalloc(size, gfp);
if (!fne)
return NULL;
@@ -1048,7 +1051,7 @@ static void fanotify_free_fid_event(struct fanotify_event *event)
static void fanotify_free_name_event(struct fanotify_event *event)
{
- kfree(FANOTIFY_NE(event));
+ kvfree(FANOTIFY_NE(event));
}
static void fanotify_free_error_event(struct fsnotify_group *group,
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index eb2ba71d5c81..775f55080dd4 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -42,9 +42,16 @@ struct fanotify_info {
u8 dir_fh_totlen;
u8 dir2_fh_totlen;
u8 file_fh_totlen;
- u8 name_len;
- u8 name2_len;
- u8 pad[3];
+ u8 pad;
+ /*
+ * Deliberately not limited to NAME_MAX: some filesystems return longer
+ * names (vfat up to 255 UTF-16 characters, as Windows allows, which is
+ * up to 765 bytes of UTF-8; FUSE up to PATH_MAX - 1), and readdir() and
+ * inotify report them in full. Only names of PATH_MAX bytes or more,
+ * which no path can refer to, are dropped.
+ */
+ u16 name_len;
+ u16 name2_len;
unsigned char buf[];
/*
* (struct fanotify_fh) dir_fh starts at buf[0]
@@ -167,7 +174,7 @@ static inline char *fanotify_info_name2(struct fanotify_info *info)
static inline void fanotify_info_init(struct fanotify_info *info)
{
BUILD_BUG_ON(FANOTIFY_FH_HDR_LEN + MAX_HANDLE_SZ > U8_MAX);
- BUILD_BUG_ON(NAME_MAX > U8_MAX);
+ BUILD_BUG_ON(PATH_MAX > U16_MAX);
info->dir_fh_totlen = 0;
info->dir2_fh_totlen = 0;
@@ -213,8 +220,7 @@ static inline void fanotify_info_set_file_fh(struct fanotify_info *info,
static inline void fanotify_info_copy_name(struct fanotify_info *info,
const struct qstr *name)
{
- if (WARN_ON_ONCE(name->len > NAME_MAX) ||
- WARN_ON_ONCE(info->name2_len > 0))
+ if (WARN_ON_ONCE(info->name2_len > 0))
return;
info->name_len = name->len;
@@ -224,9 +230,6 @@ static inline void fanotify_info_copy_name(struct fanotify_info *info,
static inline void fanotify_info_copy_name2(struct fanotify_info *info,
const struct qstr *name)
{
- if (WARN_ON_ONCE(name->len > NAME_MAX))
- return;
-
info->name2_len = name->len;
strcpy(fanotify_info_name2(info), name->name);
}
diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c
index ae15c2fc4864..4753fcab4d81 100644
--- a/fs/ntfs3/fslog.c
+++ b/fs/ntfs3/fslog.c
@@ -7,6 +7,7 @@
#include <linux/blkdev.h>
#include <linux/fs.h>
+#include <linux/overflow.h>
#include <linux/random.h>
#include <linux/slab.h>
@@ -2455,7 +2456,8 @@ static int find_log_rec(struct ntfs_log *log, u64 lsn, struct lcb *lcb)
* Check that the length field isn't greater than the total
* available space the log file.
*/
- rec_len = len + log->record_header_len;
+ if (check_add_overflow(len, log->record_header_len, &rec_len))
+ return -EINVAL;
if (rec_len >= log->total_avail)
return -EINVAL;
@@ -3300,10 +3302,15 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
asize = le32_to_cpu(attr2->size);
used = le32_to_cpu(rec->used);
+ /*
+ * attr2->size is taken from the log record and is bounded only
+ * by the log record length, not by the MFT record. Refuse an
+ * attribute that does not fit, as mi_insert_attr() does.
+ */
if (!check_if_attr(rec, lrh) || dlen < SIZEOF_RESIDENT ||
!IS_ALIGNED(asize, 8) ||
Add2Ptr(attr2, asize) > Add2Ptr(lrh, rec_len) ||
- dlen > record_size - used) {
+ dlen > record_size - used || asize > record_size - used) {
goto dirty_vol;
}
@@ -3333,10 +3340,21 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
break;
case DeleteAttribute:
- asize = le32_to_cpu(attr->size);
used = le32_to_cpu(rec->used);
- if (!check_if_attr(rec, lrh))
+ /*
+ * check_if_attr() accepts a record_off that points at the
+ * ATTR_END marker, which is how CreateAttribute appends. The
+ * bytes there are not an attribute and check_attr() never
+ * validated them, so refuse an offset that cannot hold a
+ * resident header before reading attr->size, and then bound it
+ * as mi_remove_attr() does.
+ */
+ if (!check_if_attr(rec, lrh) || roff + SIZEOF_RESIDENT > used)
+ goto dirty_vol;
+
+ asize = le32_to_cpu(attr->size);
+ if (asize > used - roff)
goto dirty_vol;
rec->used = cpu_to_le32(used - asize);
@@ -3418,7 +3436,8 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
if (!check_if_attr(rec, lrh) || !attr->non_res ||
aoff < le16_to_cpu(attr->nres.run_off) || aoff > asize ||
- (nsize > asize && nsize - asize > record_size - used)) {
+ (nsize > asize &&
+ ALIGN(nsize, 8) - asize > record_size - used)) {
goto dirty_vol;
}
diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c
index 03ae5a817c1b..97cbe176a83e 100644
--- a/fs/ntfs3/index.c
+++ b/fs/ntfs3/index.c
@@ -596,6 +596,8 @@ static const struct NTFS_DE *hdr_insert_head(struct INDEX_HDR *hdr,
if (!e)
return NULL;
+ if (size_add(used, ins_bytes) > le32_to_cpu(hdr->total))
+ return NULL;
/* Now we just make room for the inserted entries and jam it in. */
to_move = used - le32_to_cpu(hdr->de_off);
@@ -1800,7 +1802,10 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni,
}
/* Copy root entries into new buffer. */
- hdr_insert_head(hdr, re, to_move);
+ if (!hdr_insert_head(hdr, re, to_move)) {
+ err = -EINVAL;
+ goto out_put_n;
+ }
/* Update bitmap attribute. */
indx_mark_used(indx, ni, new_vbn >> indx->idx2vbn_bits);
@@ -1952,7 +1957,11 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni,
/* Copy all the entries <= sp into the new buffer. */
de_t = hdr_first_de(hdr1);
to_copy = PtrOffset(de_t, sp);
- hdr_insert_head(hdr2, de_t, to_copy);
+ if (!hdr_insert_head(hdr2, de_t, to_copy)) {
+ err = -EINVAL;
+ put_indx_node(n2);
+ goto out;
+ }
/* Remove all entries (sp including) from hdr1. */
used = used1 - to_copy - sp_size;
diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c
index 61e5eb0090d3..0888c44f677c 100644
--- a/fs/ocfs2/cluster/heartbeat.c
+++ b/fs/ocfs2/cluster/heartbeat.c
@@ -2124,6 +2124,18 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g
}
reg->hr_node_num = O2NM_MAX_NODES;
+ INIT_LIST_HEAD(®->hr_all_item);
+ mutex_init(®->hr_arming_mutex);
+ reg->hr_stopping = true;
+ INIT_DELAYED_WORK(®->hr_write_timeout_work, o2hb_write_timeout);
+ INIT_DELAYED_WORK(®->hr_nego_timeout_work, o2hb_nego_timeout);
+ INIT_LIST_HEAD(®->hr_handler_list);
+ config_item_init_type_name(®->hr_item, name, &o2hb_region_type);
+ if (!config_item_name(®->hr_item)) {
+ ret = -ENOMEM;
+ goto put_item;
+ }
+
spin_lock(&o2hb_live_lock);
reg->hr_region_num = 0;
if (o2hb_global_heartbeat_active()) {
@@ -2132,26 +2144,19 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g
if (reg->hr_region_num >= O2NM_MAX_REGIONS) {
spin_unlock(&o2hb_live_lock);
ret = -EFBIG;
- goto free;
+ goto put_item;
}
set_bit(reg->hr_region_num, o2hb_region_bitmap);
}
list_add_tail(®->hr_all_item, &o2hb_all_regions);
spin_unlock(&o2hb_live_lock);
- config_item_init_type_name(®->hr_item, name, &o2hb_region_type);
-
/* this is the same way to generate msg key as dlm, for local heartbeat,
* name is also the same, so make initial crc value different to avoid
* message key conflict.
*/
reg->hr_key = crc32_le(reg->hr_region_num + O2NM_MAX_REGIONS,
name, strlen(name));
- mutex_init(®->hr_arming_mutex);
- reg->hr_stopping = true;
- INIT_DELAYED_WORK(®->hr_write_timeout_work, o2hb_write_timeout);
- INIT_DELAYED_WORK(®->hr_nego_timeout_work, o2hb_nego_timeout);
- INIT_LIST_HEAD(®->hr_handler_list);
ret = o2net_register_handler(O2HB_NEGO_TIMEOUT_MSG, reg->hr_key,
sizeof(struct o2hb_nego_msg),
o2hb_nego_timeout_handler,
@@ -2174,10 +2179,13 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g
o2net_unregister_and_flush_handler_list(®->hr_handler_list);
remove_item:
spin_lock(&o2hb_live_lock);
- list_del(®->hr_all_item);
+ list_del_init(®->hr_all_item);
if (o2hb_global_heartbeat_active())
clear_bit(reg->hr_region_num, o2hb_region_bitmap);
spin_unlock(&o2hb_live_lock);
+put_item:
+ config_item_put(®->hr_item);
+ return ERR_PTR(ret);
free:
kfree(reg);
return ERR_PTR(ret);
diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c
index 87aeb1a7a90a..bfc7c0fb2be0 100644
--- a/fs/ocfs2/cluster/nodemanager.c
+++ b/fs/ocfs2/cluster/nodemanager.c
@@ -626,8 +626,11 @@ static void o2nm_node_group_drop_item(struct config_group *group,
write_lock(&cluster->cl_nodes_lock);
- /* XXX sloppy */
- if (node->nd_ipv4_address)
+ /*
+ * 0.0.0.0 is a valid configured address, so nd_ipv4_address does
+ * not tell us whether nd_ip_node was published in the IP tree.
+ */
+ if (test_bit(O2NM_NODE_ATTR_ADDRESS, &node->nd_set_attributes))
rb_erase(&node->nd_ip_node, &cluster->cl_node_ip_tree);
/* nd_num might be 0 if the node number hasn't been set.. */
diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
index fbfce8cd131a..1dee6b8eaf2d 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -221,7 +221,8 @@ static void TEA_transform(__u32 buf[4], __u32 const in[])
buf[1] += b1;
}
-static void str2hashbuf(const char *msg, int len, __u32 *buf, int num)
+static void str2hashbuf(const char *msg, int len, __u32 *buf, int num,
+ bool legacy_signed)
{
__u32 pad, val;
int i;
@@ -235,7 +236,10 @@ static void str2hashbuf(const char *msg, int len, __u32 *buf, int num)
for (i = 0; i < len; i++) {
if ((i % 4) == 0)
val = pad;
- val = msg[i] + (val << 8);
+ if (legacy_signed)
+ val = (signed char)msg[i] + (val << 8);
+ else
+ val = (unsigned char)msg[i] + (val << 8);
if ((i % 4) == 3) {
*buf++ = val;
val = pad;
@@ -248,8 +252,9 @@ static void str2hashbuf(const char *msg, int len, __u32 *buf, int num)
*buf++ = pad;
}
-static void ocfs2_dx_dir_name_hash(struct inode *dir, const char *name, int len,
- struct ocfs2_dx_hinfo *hinfo)
+static void __ocfs2_dx_dir_name_hash(struct inode *dir, const char *name,
+ int len, struct ocfs2_dx_hinfo *hinfo,
+ bool legacy_signed)
{
struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
const char *p;
@@ -279,7 +284,7 @@ static void ocfs2_dx_dir_name_hash(struct inode *dir, const char *name, int len,
p = name;
while (len > 0) {
- str2hashbuf(p, len, in, 4);
+ str2hashbuf(p, len, in, 4, legacy_signed);
TEA_transform(buf, in);
len -= 16;
p += 16;
@@ -290,6 +295,19 @@ static void ocfs2_dx_dir_name_hash(struct inode *dir, const char *name, int len,
hinfo->minor_hash = buf[1];
}
+static void ocfs2_dx_dir_name_hash(struct inode *dir, const char *name, int len,
+ struct ocfs2_dx_hinfo *hinfo)
+{
+ __ocfs2_dx_dir_name_hash(dir, name, len, hinfo, false);
+}
+
+static void ocfs2_dx_dir_name_hash_signed(struct inode *dir, const char *name,
+ int len,
+ struct ocfs2_dx_hinfo *hinfo)
+{
+ __ocfs2_dx_dir_name_hash(dir, name, len, hinfo, true);
+}
+
/*
* bh passed here can be an inode block or a dir data block, depending
* on the inode inline data flag.
@@ -961,10 +979,10 @@ static int ocfs2_dx_dir_lookup(struct inode *inode,
return ret;
}
-static int ocfs2_dx_dir_search(const char *name, int namelen,
- struct inode *dir,
- struct ocfs2_dx_root_block *dx_root,
- struct ocfs2_dir_lookup_result *res)
+static int __ocfs2_dx_dir_search(const char *name, int namelen,
+ struct inode *dir,
+ struct ocfs2_dx_root_block *dx_root,
+ struct ocfs2_dir_lookup_result *res)
{
int ret, i, found;
u64 phys;
@@ -977,8 +995,6 @@ static int ocfs2_dx_dir_search(const char *name, int namelen,
struct ocfs2_extent_list *dr_el;
struct ocfs2_dx_entry_list *entry_list;
- ocfs2_dx_dir_name_hash(dir, name, namelen, &res->dl_hinfo);
-
if (ocfs2_dx_root_inline(dx_root)) {
entry_list = &dx_root->dr_entries;
goto search;
@@ -1075,6 +1091,44 @@ static int ocfs2_dx_dir_search(const char *name, int namelen,
return ret;
}
+static int ocfs2_dx_dir_search(const char *name, int namelen,
+ struct inode *dir,
+ struct ocfs2_dx_root_block *dx_root,
+ struct ocfs2_dir_lookup_result *res)
+{
+ struct ocfs2_dx_hinfo legacy;
+ int ret;
+
+ ocfs2_dx_dir_name_hash(dir, name, namelen, &res->dl_hinfo);
+
+ ret = __ocfs2_dx_dir_search(name, namelen, dir, dx_root, res);
+ if (ret != -ENOENT)
+ return ret;
+
+ /*
+ * Nothing under the current hash. The entry may have been indexed by
+ * an older kernel, which sign-extended the name bytes when hashing.
+ * New entries are always indexed under the unsigned hash, so only fall
+ * back to the legacy signed one when it can actually differ: an ASCII
+ * name hashes the same either way, and a genuine miss on one should
+ * not have to walk the index twice.
+ */
+ ocfs2_dx_dir_name_hash_signed(dir, name, namelen, &legacy);
+ if (legacy.major_hash == res->dl_hinfo.major_hash &&
+ legacy.minor_hash == res->dl_hinfo.minor_hash)
+ return ret;
+
+ res->dl_hinfo = legacy;
+
+ ret = __ocfs2_dx_dir_search(name, namelen, dir, dx_root, res);
+ if (ret)
+ return ret;
+
+ pr_warn_once("ocfs2: directory index with signed name hash\n");
+
+ return 0;
+}
+
static int ocfs2_find_entry_dx(const char *name, int namelen,
struct inode *dir,
struct ocfs2_dir_lookup_result *lookup)
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index b4055ced93d9..90d23dd7d4c8 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -3527,6 +3527,10 @@ void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb,
struct ocfs2_mask_waiter mw;
unsigned long flags, flags2;
+ /* We didn't get anywhere near actually using this lockres. */
+ if (!(lockres->l_flags & OCFS2_LOCK_INITIALIZED))
+ return;
+
ocfs2_init_mask_waiter(&mw);
spin_lock_irqsave(&lockres->l_lock, flags);
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index a0df92a23498..f4c36ce1678d 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -140,18 +140,26 @@ static int ocfs2_file_open(struct inode *inode, struct file *file)
static int ocfs2_file_release(struct inode *inode, struct file *file)
{
struct ocfs2_inode_info *oi = OCFS2_I(inode);
+ struct dentry *dentry = file->f_path.dentry;
+ struct name_snapshot name;
+ bool trace_enabled = trace_ocfs2_file_release_enabled();
+
+ if (trace_enabled)
+ take_dentry_name_snapshot(&name, dentry);
spin_lock(&oi->ip_lock);
if (!--oi->ip_open_count)
oi->ip_flags &= ~OCFS2_INODE_OPEN_DIRECT;
- trace_ocfs2_file_release(inode, file, file->f_path.dentry,
- oi->ip_blkno,
- file->f_path.dentry->d_name.len,
- file->f_path.dentry->d_name.name,
- oi->ip_open_count);
+ if (trace_enabled)
+ trace_ocfs2_file_release(inode, file, dentry,
+ oi->ip_blkno, name.name.len,
+ name.name.name, oi->ip_open_count);
spin_unlock(&oi->ip_lock);
+ if (trace_enabled)
+ release_dentry_name_snapshot(&name);
+
ocfs2_free_file_private(inode, file);
return 0;
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index 835c00b90b7c..c1ac673ca456 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -637,14 +637,18 @@ static int ocfs2_read_locked_inode(struct inode *inode,
fe = (struct ocfs2_dinode *) bh->b_data;
/*
- * This is a code bug. Right now the caller needs to
- * understand whether it is asking for a system file inode or
- * not so the proper lock names can be built.
+ * The caller must know whether it is asking for a system file inode
+ * or not so the proper lock names can be built. Since i_flags comes
+ * from disk, a mismatch is filesystem corruption instead of a code
+ * bug, so handle it with ocfs2_error() rather than BUG_ON().
*/
- mlog_bug_on_msg(!!(fe->i_flags & cpu_to_le32(OCFS2_SYSTEM_FL)) !=
- !!(args->fi_flags & OCFS2_FI_FLAG_SYSFILE),
- "Inode %llu: system file state is ambiguous\n",
- (unsigned long long)args->fi_blkno);
+ if (!!(fe->i_flags & cpu_to_le32(OCFS2_SYSTEM_FL)) !=
+ !!(args->fi_flags & OCFS2_FI_FLAG_SYSFILE)) {
+ status = ocfs2_error(osb->sb,
+ "Inode %llu: system file state is ambiguous\n",
+ (unsigned long long)args->fi_blkno);
+ goto bail;
+ }
if (S_ISCHR(le16_to_cpu(fe->i_mode)) ||
S_ISBLK(le16_to_cpu(fe->i_mode)))
@@ -1530,8 +1534,23 @@ int ocfs2_validate_inode_block(struct super_block *sb,
goto bail;
}
- if (le16_to_cpu(di->i_suballoc_slot) != (u16)OCFS2_INVALID_SLOT &&
- (u32)le16_to_cpu(di->i_suballoc_slot) > OCFS2_SB(sb)->max_slots - 1) {
+ /*
+ * Only system inodes created by mkfs.ocfs2 are allocated from the
+ * global allocator and thus legitimately carry OCFS2_INVALID_SLOT.
+ * Regular inodes are always allocated from a per-slot suballocator.
+ * If a regular inode with OCFS2_INVALID_SLOT was accepted here,
+ * deleting it would pass the slot to get_local_system_inode() via
+ * ocfs2_remove_inode() and trigger BUG_ON(slot == OCFS2_INVALID_SLOT).
+ */
+ if (le16_to_cpu(di->i_suballoc_slot) == (u16)OCFS2_INVALID_SLOT) {
+ if (!(le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL)) {
+ rc = ocfs2_error(sb,
+ "Invalid dinode %llu: suballoc slot %u for non-system inode\n",
+ (unsigned long long)bh->b_blocknr,
+ le16_to_cpu(di->i_suballoc_slot));
+ goto bail;
+ }
+ } else if ((u32)le16_to_cpu(di->i_suballoc_slot) > OCFS2_SB(sb)->max_slots - 1) {
rc = ocfs2_error(sb, "Invalid dinode %llu: suballoc slot %u\n",
(unsigned long long)bh->b_blocknr,
le16_to_cpu(di->i_suballoc_slot));
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index 8c07610692d0..2b8b57e914f0 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -158,7 +158,7 @@ static void ocfs2_queue_replay_slots(struct ocfs2_super *osb,
replay_map->rm_state = REPLAY_DONE;
}
-void ocfs2_free_replay_slots(struct ocfs2_super *osb)
+static void ocfs2_free_replay_slots(struct ocfs2_super *osb)
{
struct ocfs2_replay_map *replay_map = osb->replay_map;
@@ -246,6 +246,7 @@ void ocfs2_recovery_exit(struct ocfs2_super *osb)
/* XXX: Should we bug if there are dirty entries? */
kfree(rm);
+ ocfs2_free_replay_slots(osb);
}
static int __ocfs2_recovery_map_test(struct ocfs2_super *osb,
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h
index f8b3b2a3d630..19fc920d26b1 100644
--- a/fs/ocfs2/journal.h
+++ b/fs/ocfs2/journal.h
@@ -151,7 +151,6 @@ void ocfs2_recovery_exit(struct ocfs2_super *osb);
void ocfs2_recovery_disable_quota(struct ocfs2_super *osb);
int ocfs2_compute_replay_slots(struct ocfs2_super *osb);
-void ocfs2_free_replay_slots(struct ocfs2_super *osb);
/*
* Journal Control:
* Initialize, Load, Shutdown, Wipe a journal.
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index dc252b53c7dc..fe4ab1659e0d 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -1163,15 +1163,23 @@ static int ocfs2_fill_super(struct super_block *sb, struct fs_context *fc)
out_dismount:
atomic_set(&osb->vol_state, VOLUME_DISABLED);
wake_up(&osb->osb_mount_event);
- ocfs2_free_replay_slots(osb);
ocfs2_dismount_volume(sb, 1);
goto out;
out_debugfs:
debugfs_remove_recursive(osb->osb_debug_root);
out_super:
+ /*
+ * A recovery thread launched by a node failure event may still be
+ * waiting for the volume to be mounted. Set VOLUME_DISABLED and
+ * wake it up, then wait for it to exit before osb is freed,
+ * otherwise the kthread would leak and stay blocked on the wait
+ * queue embedded in the freed osb.
+ */
+ atomic_set(&osb->vol_state, VOLUME_DISABLED);
+ wake_up(&osb->osb_mount_event);
ocfs2_release_system_inodes(osb);
- kfree(osb->recovery_map);
+ ocfs2_recovery_exit(osb);
ocfs2_delete_osb(osb);
kfree(osb);
out:
@@ -1769,14 +1777,12 @@ static int ocfs2_mount_volume(struct super_block *sb)
status = ocfs2_truncate_log_init(osb);
if (status < 0) {
mlog_errno(status);
- goto out_check_volume;
+ goto out_system_inodes;
}
ocfs2_super_unlock(osb, 1);
return 0;
-out_check_volume:
- ocfs2_free_replay_slots(osb);
out_system_inodes:
if (osb->local_alloc_state == OCFS2_LA_ENABLED)
ocfs2_shutdown_local_alloc(osb);
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index b5029c4e3784..734801de8e89 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -533,9 +533,10 @@ static inline const char *ocfs2_xattr_prefix(int name_index)
return handler ? xattr_prefix(handler) : NULL;
}
-static u32 ocfs2_xattr_name_hash(struct inode *inode,
- const char *name,
- int name_len)
+static u32 __ocfs2_xattr_name_hash(struct inode *inode,
+ const char *name,
+ int name_len,
+ bool legacy_signed)
{
/* Get hash value of uuid from super block */
u32 hash = OCFS2_SB(inode->i_sb)->uuid_hash;
@@ -544,13 +545,30 @@ static u32 ocfs2_xattr_name_hash(struct inode *inode,
/* hash extended attribute name */
for (i = 0; i < name_len; i++) {
hash = (hash << OCFS2_HASH_SHIFT) ^
- (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)) ^
- *name++;
+ (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT));
+ if (legacy_signed)
+ hash ^= (signed char)name[i];
+ else
+ hash ^= (unsigned char)name[i];
}
return hash;
}
+static u32 ocfs2_xattr_name_hash(struct inode *inode,
+ const char *name,
+ int name_len)
+{
+ return __ocfs2_xattr_name_hash(inode, name, name_len, false);
+}
+
+static u32 ocfs2_xattr_name_hash_signed(struct inode *inode,
+ const char *name,
+ int name_len)
+{
+ return __ocfs2_xattr_name_hash(inode, name, name_len, true);
+}
+
static int ocfs2_xattr_entry_real_size(int name_len, size_t value_len)
{
return namevalue_size(name_len, value_len) +
@@ -3143,6 +3161,14 @@ static int ocfs2_calc_xattr_set_need(struct inode *inode,
credits += ocfs2_calc_extend_credits(
inode->i_sb,
&def_xv.xv.xr_list);
+ /*
+ * A value too big to stay inline is only charged as
+ * OCFS2_XATTR_ROOT_SIZE above, so it can still need a
+ * value tree of its own. No xattr block is allocated
+ * on this path, so the tree is all we reserve for.
+ */
+ if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE)
+ meta_add += ocfs2_extend_meta_needed(&def_xv.xv.xr_list);
goto out;
}
}
@@ -3243,7 +3269,13 @@ static int ocfs2_calc_xattr_set_need(struct inode *inode,
credits += OCFS2_XATTR_BLOCK_CREATE_CREDITS;
if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE) {
struct ocfs2_extent_list *el = &def_xv.xv.xr_list;
- meta_add += ocfs2_extend_meta_needed(el);
+ /*
+ * One block for the xattr block we may have to
+ * allocate, the rest for growing the value tree it
+ * would hold. If ocfs2_xattr_ibody_set() succeeds
+ * instead, that first block goes unused.
+ */
+ meta_add += 1 + ocfs2_extend_meta_needed(el);
credits += ocfs2_calc_extend_credits(inode->i_sb,
el);
} else {
@@ -3935,11 +3967,12 @@ static int ocfs2_xattr_bucket_find(struct inode *inode,
return ret;
}
-static int ocfs2_xattr_index_block_find(struct inode *inode,
- struct buffer_head *root_bh,
- int name_index,
- const char *name,
- struct ocfs2_xattr_search *xs)
+static int __ocfs2_xattr_index_block_find(struct inode *inode,
+ struct buffer_head *root_bh,
+ int name_index,
+ const char *name,
+ u32 name_hash,
+ struct ocfs2_xattr_search *xs)
{
int ret;
struct ocfs2_xattr_block *xb =
@@ -3948,7 +3981,6 @@ static int ocfs2_xattr_index_block_find(struct inode *inode,
struct ocfs2_extent_list *el = &xb_root->xt_list;
u64 p_blkno = 0;
u32 first_hash, num_clusters = 0;
- u32 name_hash = ocfs2_xattr_name_hash(inode, name, strlen(name));
if (le16_to_cpu(el->l_next_free_rec) == 0)
return -ENODATA;
@@ -3979,6 +4011,59 @@ static int ocfs2_xattr_index_block_find(struct inode *inode,
return ret;
}
+static int ocfs2_xattr_index_block_find(struct inode *inode,
+ struct buffer_head *root_bh,
+ int name_index,
+ const char *name,
+ struct ocfs2_xattr_search *xs)
+{
+ u32 name_hash, legacy_hash;
+ int name_len = strlen(name);
+ int ret;
+
+ name_hash = ocfs2_xattr_name_hash(inode, name, name_len);
+
+ ret = __ocfs2_xattr_index_block_find(inode, root_bh, name_index, name,
+ name_hash, xs);
+ if (ret != -ENODATA)
+ return ret;
+
+ /*
+ * Nothing under the current hash. The entry may have been stored by
+ * an older kernel, which sign-extended the name bytes when hashing.
+ * Skip the retry when the two hashes are equal, so that a name made
+ * only of ASCII does not have to walk the tree twice.
+ */
+ legacy_hash = ocfs2_xattr_name_hash_signed(inode, name, name_len);
+ if (legacy_hash == name_hash)
+ return ret;
+
+ /*
+ * A miss still leaves xs->bucket holding the bucket a new entry would
+ * be inserted into, so drop it before searching again.
+ */
+ ocfs2_xattr_bucket_relse(xs->bucket);
+
+ ret = __ocfs2_xattr_index_block_find(inode, root_bh, name_index, name,
+ legacy_hash, xs);
+ if (!ret) {
+ pr_warn_once("ocfs2: xattr tree with signed name hash\n");
+ return ret;
+ }
+ if (ret != -ENODATA)
+ return ret;
+
+ /*
+ * Not under either hash. Restore the unsigned placement, since that
+ * is where a new entry is stored: leaving the bucket where the legacy
+ * hash put it would break the ordering the search relies on.
+ */
+ ocfs2_xattr_bucket_relse(xs->bucket);
+
+ return __ocfs2_xattr_index_block_find(inode, root_bh, name_index, name,
+ name_hash, xs);
+}
+
static int ocfs2_iterate_xattr_buckets(struct inode *inode,
u64 blkno,
u32 clusters,
@@ -5517,16 +5602,14 @@ static int ocfs2_rm_xattr_cluster(struct inode *inode,
/*
* check whether the xattr bucket is filled up with the same hash value.
- * If we want to insert the xattr with the same hash, return -ENOSPC.
- * If we want to insert a xattr with different hash value, go ahead
- * and ocfs2_divide_xattr_bucket will handle this.
+ * If the entry being set carries that same hash, return -ENOSPC, since
+ * ocfs2_divide_xattr_bucket() has no divide position to work with.
+ * Otherwise go ahead and ocfs2_divide_xattr_bucket() will handle this.
*/
-static int ocfs2_check_xattr_bucket_collision(struct inode *inode,
- struct ocfs2_xattr_bucket *bucket,
- const char *name)
+static int ocfs2_check_xattr_bucket_collision(struct ocfs2_xattr_bucket *bucket,
+ u32 name_hash)
{
struct ocfs2_xattr_header *xh = bucket_xh(bucket);
- u32 name_hash = ocfs2_xattr_name_hash(inode, name, strlen(name));
if (name_hash != le32_to_cpu(xh->xh_entries[0].xe_name_hash))
return 0;
@@ -5595,10 +5678,27 @@ static int ocfs2_xattr_set_entry_index_block(struct inode *inode,
struct ocfs2_xattr_search *xs,
struct ocfs2_xattr_set_ctxt *ctxt)
{
+ u32 name_hash;
int ret;
trace_ocfs2_xattr_set_entry_index_block(xi->xi_name);
+ /*
+ * An existing entry keeps the hash it was stored under, and that is
+ * the hash a split has to work with. A new entry is stored under the
+ * unsigned one, which is what ocfs2_xa_add_entry() will write.
+ *
+ * Read it before the set below. Filling the bucket may defragment it,
+ * and ocfs2_defrag_xattr_bucket() re-sorts the entry array, so
+ * afterwards xs->here only denotes whichever entry the sort left in
+ * that slot.
+ */
+ if (xs->not_found)
+ name_hash = ocfs2_xattr_name_hash(inode, xi->xi_name,
+ xi->xi_name_len);
+ else
+ name_hash = le32_to_cpu(xs->here->xe_name_hash);
+
ret = ocfs2_xattr_set_entry_bucket(inode, xi, xs, ctxt);
if (!ret)
goto out;
@@ -5615,9 +5715,7 @@ static int ocfs2_xattr_set_entry_index_block(struct inode *inode,
* one bucket's worth, so check it here whether we need to
* add a new bucket for the insert.
*/
- ret = ocfs2_check_xattr_bucket_collision(inode,
- xs->bucket,
- xi->xi_name);
+ ret = ocfs2_check_xattr_bucket_collision(xs->bucket, name_hash);
if (ret) {
mlog_errno(ret);
goto out;
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 12b817f4ffcf..b63f612d5ebf 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -256,9 +256,10 @@ struct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry,
inode = proc_get_inode(dir->i_sb, de);
if (!inode)
return ERR_PTR(-ENOMEM);
+ /* never reused and so never a mountpoint for long */
if (de->flags & PROC_ENTRY_FORCE_LOOKUP)
- return d_splice_alias_ops(inode, dentry,
- &proc_net_dentry_ops);
+ return proc_splice_unmountable(inode, dentry,
+ &proc_net_dentry_ops);
return d_splice_alias_ops(inode, dentry,
&proc_misc_dentry_ops);
}
diff --git a/fs/read_write.c b/fs/read_write.c
index 833bae068770..011fe55c94cd 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -1530,9 +1530,11 @@ static int generic_copy_file_checks(struct file *file_in, loff_t pos_in,
else
count = min(count, size_in - (uint64_t)pos_in);
- ret = generic_write_check_limits(file_out, pos_out, &count);
- if (ret)
- return ret;
+ if (count) {
+ ret = generic_write_check_limits(file_out, pos_out, &count);
+ if (ret)
+ return ret;
+ }
/* Don't allow overlapped copying within the same file. */
if (inode_in == inode_out &&
diff --git a/fs/signalfd.c b/fs/signalfd.c
index d469782f97f4..0aa1657bbedf 100644
--- a/fs/signalfd.c
+++ b/fs/signalfd.c
@@ -48,17 +48,30 @@ static int signalfd_release(struct inode *inode, struct file *file)
return 0;
}
+static void refine_sigmask(struct signalfd_ctx *ctx, sigset_t *sigmask)
+{
+ struct k_sigaction *k = current->sighand->action;
+ int n;
+
+ *sigmask = ctx->sigmask;
+ for (n = 1; n <= _NSIG; ++n, ++k) {
+ if (k->sa.sa_flags & SA_IMMUTABLE)
+ sigaddset(sigmask, n);
+ }
+}
+
static __poll_t signalfd_poll(struct file *file, poll_table *wait)
{
struct signalfd_ctx *ctx = file->private_data;
__poll_t events = 0;
+ sigset_t sigmask;
poll_wait(file, ¤t->sighand->signalfd_wqh, wait);
spin_lock_irq(¤t->sighand->siglock);
- if (next_signal(¤t->pending, &ctx->sigmask) ||
- next_signal(¤t->signal->shared_pending,
- &ctx->sigmask))
+ refine_sigmask(ctx, &sigmask);
+ if (next_signal(¤t->pending, &sigmask) ||
+ next_signal(¤t->signal->shared_pending, &sigmask))
events |= EPOLLIN;
spin_unlock_irq(¤t->sighand->siglock);
@@ -155,11 +168,13 @@ static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, kernel_siginfo_t *info
int nonblock)
{
enum pid_type type;
- ssize_t ret;
DECLARE_WAITQUEUE(wait, current);
+ sigset_t sigmask;
+ ssize_t ret;
spin_lock_irq(¤t->sighand->siglock);
- ret = dequeue_signal(&ctx->sigmask, info, &type);
+ refine_sigmask(ctx, &sigmask);
+ ret = dequeue_signal(&sigmask, info, &type);
switch (ret) {
case 0:
if (!nonblock)
@@ -174,7 +189,7 @@ static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, kernel_siginfo_t *info
add_wait_queue(¤t->sighand->signalfd_wqh, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
- ret = dequeue_signal(&ctx->sigmask, info, &type);
+ ret = dequeue_signal(&sigmask, info, &type);
if (ret != 0)
break;
if (signal_pending(current)) {
@@ -184,6 +199,7 @@ static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, kernel_siginfo_t *info
spin_unlock_irq(¤t->sighand->siglock);
schedule();
spin_lock_irq(¤t->sighand->siglock);
+ refine_sigmask(ctx, &sigmask);
}
spin_unlock_irq(¤t->sighand->siglock);
diff --git a/fs/splice.c b/fs/splice.c
index f5094b6d00a0..8ac45441e219 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -1351,10 +1351,10 @@ ssize_t do_splice(struct file *in, loff_t *off_in, struct file *out,
ret = do_splice_from(ipipe, out, &offset, len, flags);
file_end_write(out);
- if (!off_out)
- out->f_pos = offset;
- else
+ if (off_out)
*off_out = offset;
+ else if (ret >= 0)
+ out->f_pos = offset;
} else if (opipe) {
if (off_out)
return -ESPIPE;
@@ -1375,10 +1375,10 @@ ssize_t do_splice(struct file *in, loff_t *off_in, struct file *out,
ret = splice_file_to_pipe(in, opipe, &offset, len, flags);
- if (!off_in)
- in->f_pos = offset;
- else
+ if (off_in)
*off_in = offset;
+ else if (ret >= 0)
+ in->f_pos = offset;
} else {
ret = -EINVAL;
}
diff --git a/fs/stat.c b/fs/stat.c
index 8c110555b3d7..3bc049f0bc3d 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -203,8 +203,12 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat,
if (IS_DAX(inode))
stat->attributes |= STATX_ATTR_DAX;
+ if (IS_VERITY(inode))
+ stat->attributes |= STATX_ATTR_VERITY;
+
stat->attributes_mask |= (STATX_ATTR_AUTOMOUNT |
- STATX_ATTR_DAX);
+ STATX_ATTR_DAX |
+ STATX_ATTR_VERITY);
idmap = mnt_idmap(path->mnt);
if (inode->i_op->getattr) {
diff --git a/fs/udf/super.c b/fs/udf/super.c
index f91b231b7d7b..556540f2797a 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -1611,7 +1611,10 @@ static bool udf_lvid_valid(struct super_block *sb,
parts = le32_to_cpu(lvid->numOfPartitions);
impuselen = le32_to_cpu(lvid->lengthOfImpUse);
- if (parts >= sb->s_blocksize || impuselen >= sb->s_blocksize ||
+ if (sizeof(struct logicalVolIntegrityDescImpUse) > impuselen ||
+ impuselen >= sb->s_blocksize)
+ return false;
+ if (parts >= sb->s_blocksize ||
sizeof(struct logicalVolIntegrityDesc) + impuselen +
2 * parts * sizeof(u32) > sb->s_blocksize)
return false;
diff --git a/fs/verity/open.c b/fs/verity/open.c
index 2aa5eae5a540..02e2c14ab3ff 100644
--- a/fs/verity/open.c
+++ b/fs/verity/open.c
@@ -273,12 +273,6 @@ static bool validate_fsverity_descriptor(struct inode *inode,
const struct fsverity_descriptor *desc,
size_t desc_size)
{
- if (desc_size < sizeof(*desc)) {
- fsverity_err(inode, "Unrecognized descriptor size: %zu bytes",
- desc_size);
- return false;
- }
-
if (desc->version != 1) {
fsverity_err(inode, "Unrecognized descriptor version: %u",
desc->version);
@@ -317,31 +311,37 @@ static bool validate_fsverity_descriptor(struct inode *inode,
int fsverity_get_descriptor(struct inode *inode,
struct fsverity_descriptor **desc_ret)
{
- int res;
+ int size, res;
struct fsverity_descriptor *desc;
- res = inode->i_sb->s_vop->get_verity_descriptor(inode, NULL, 0);
- if (res < 0) {
- fsverity_err(inode,
- "Error %d getting verity descriptor size", res);
- return res;
+ size = inode->i_sb->s_vop->get_verity_descriptor(inode, NULL, 0);
+ if (size < 0) {
+ fsverity_err(inode, "Error %d getting verity descriptor size",
+ size);
+ return size;
}
- if (res > FS_VERITY_MAX_DESCRIPTOR_SIZE) {
- fsverity_err(inode, "Verity descriptor is too large (%d bytes)",
- res);
+ if (size < sizeof(*desc) || size > FS_VERITY_MAX_DESCRIPTOR_SIZE) {
+ fsverity_err(inode,
+ "Verity descriptor has invalid size (%d bytes)",
+ size);
return -EMSGSIZE;
}
- desc = kmalloc(res, GFP_KERNEL);
+ desc = kmalloc(size, GFP_KERNEL);
if (!desc)
return -ENOMEM;
- res = inode->i_sb->s_vop->get_verity_descriptor(inode, desc, res);
+ res = inode->i_sb->s_vop->get_verity_descriptor(inode, desc, size);
if (res < 0) {
fsverity_err(inode, "Error %d reading verity descriptor", res);
kfree(desc);
return res;
}
-
- if (!validate_fsverity_descriptor(inode, desc, res)) {
+ if (res != size) {
+ fsverity_err(inode, "Verity descriptor size changed (%d => %d)",
+ size, res);
+ kfree(desc);
+ return -EINVAL;
+ }
+ if (!validate_fsverity_descriptor(inode, desc, size)) {
kfree(desc);
return -EINVAL;
}
diff --git a/include/linux/fault-inject.h b/include/linux/fault-inject.h
index 8c829d28dcf3..f55b51f33346 100644
--- a/include/linux/fault-inject.h
+++ b/include/linux/fault-inject.h
@@ -14,6 +14,13 @@ struct kmem_cache;
#include <linux/configfs.h>
#include <linux/ratelimit.h>
+/*
+ * Length of the debugfs directory name embedded in struct fault_attr.
+ * Chosen to accommodate every in-tree caller of fault_create_debugfs_attr()
+ * (the longest is "fail_dma_array_full", 19 chars) with generous headroom.
+ */
+#define FAULT_ATTR_DNAME_LEN 64
+
/*
* For explanation of the elements of this struct, see
* Documentation/fault-injection/fault-injection.rst
@@ -33,7 +40,7 @@ struct fault_attr {
unsigned long count;
struct ratelimit_state ratelimit_state;
- struct dentry *dname;
+ char dname[FAULT_ATTR_DNAME_LEN];
};
enum fault_flags {
@@ -47,7 +54,6 @@ enum fault_flags {
.stacktrace_depth = 32, \
.ratelimit_state = RATELIMIT_STATE_INIT_DISABLED, \
.verbose = 2, \
- .dname = NULL, \
}
#define DECLARE_FAULT_ATTR(name) struct fault_attr name = FAULT_ATTR_INITIALIZER
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 55ecd9d26b4f..349b7fc57db3 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -847,7 +847,9 @@ struct inode {
/* 32-bit hole */
struct rw_semaphore i_rwsem;
- unsigned long dirtied_when; /* jiffies of first dirtying */
+ /* Timestamp for writeback list ordering and age checks (jiffies). */
+ unsigned long dirtied_when;
+ /* Start time for deciding when lazytime updates need writeback (jiffies). */
unsigned long dirtied_time_when;
struct hlist_node i_hash;
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 868af0a08f52..a92908f07e22 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -1136,6 +1136,7 @@ extern struct ata_host *ata_host_alloc_pinfo(struct device *dev,
extern void ata_host_get(struct ata_host *host);
extern void ata_host_put(struct ata_host *host);
extern int ata_host_start(struct ata_host *host);
+void ata_host_undo_start(struct ata_host *host);
extern int ata_host_register(struct ata_host *host,
const struct scsi_host_template *sht);
extern int ata_host_activate(struct ata_host *host, int irq,
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 95c77e2a1d1f..72d1ca12aaa8 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1413,7 +1413,7 @@ int pcie_set_mps(struct pci_dev *dev, int mps);
u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev,
enum pci_bus_speed *speed,
enum pcie_link_width *width);
-int pcie_link_speed_mbps(struct pci_dev *pdev);
+int pcie_link_bandwidth_mbps(struct pci_dev *pdev);
void pcie_print_link_status(struct pci_dev *dev);
int pcie_reset_flr(struct pci_dev *dev, bool probe);
int pcie_flr(struct pci_dev *dev);
diff --git a/include/linux/pps_gen_kernel.h b/include/linux/pps_gen_kernel.h
index f26f6aac000d..c4d22b1af118 100644
--- a/include/linux/pps_gen_kernel.h
+++ b/include/linux/pps_gen_kernel.h
@@ -11,6 +11,7 @@
#include <linux/pps_gen.h>
#include <linux/cdev.h>
#include <linux/device.h>
+#include <linux/mutex.h>
/*
* Global defines
@@ -44,6 +45,7 @@ struct pps_gen_source_info {
/* The main struct */
struct pps_gen_device {
const struct pps_gen_source_info *info; /* PSS generator info */
+ struct mutex info_lock; /* protects info */
bool enabled; /* PSS generator status */
unsigned int event;
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index 95da051fb155..116ac3cd5d54 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -13,6 +13,7 @@
#define _LINUX_RTC_H_
+#include <linux/cleanup.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/nvmem-provider.h>
@@ -199,6 +200,7 @@ extern void rtc_update_irq(struct rtc_device *rtc,
extern struct rtc_device *rtc_class_open(const char *name);
extern void rtc_class_close(struct rtc_device *rtc);
+DEFINE_FREE(rtc_class_close, struct rtc_device *, if (_T) rtc_class_close(_T));
extern int rtc_irq_set_state(struct rtc_device *rtc, int enabled);
extern int rtc_irq_set_freq(struct rtc_device *rtc, int freq);
diff --git a/include/math-emu/op-common.h b/include/math-emu/op-common.h
index 8ce066c035cf..1d1ce5c08efc 100644
--- a/include/math-emu/op-common.h
+++ b/include/math-emu/op-common.h
@@ -135,6 +135,24 @@ do { \
else \
{ \
/* we've got a denormalized number */ \
+ int _FP_PACK_CANONICAL_is_tiny = 1; \
+ if (_FP_TININESS_AFTER_ROUNDING && X##_e == 0) \
+ { \
+ /* Architectures that detect tininess after rounding \
+ only signal underflow if the result is still \
+ subnormal once rounded as if the exponent range \
+ were unbounded. Round a copy to find out. */ \
+ FP_DECL_##fs(_FP_PACK_CANONICAL_T); \
+ /* The class field is not used by the rounding below, \
+ and is unused entirely where this block is dead. */ \
+ (void)_FP_PACK_CANONICAL_T##_c; \
+ _FP_FRAC_COPY_##wc(_FP_PACK_CANONICAL_T, X); \
+ _FP_PACK_CANONICAL_T##_s = X##_s; \
+ _FP_PACK_CANONICAL_T##_e = X##_e; \
+ _FP_ROUND(wc, _FP_PACK_CANONICAL_T); \
+ if (_FP_FRAC_OVERP_##wc(fs, _FP_PACK_CANONICAL_T)) \
+ _FP_PACK_CANONICAL_is_tiny = 0; \
+ } \
X##_e = -X##_e + 1; \
if (X##_e <= _FP_WFRACBITS_##fs) \
{ \
@@ -161,8 +179,9 @@ do { \
_FP_FRAC_SRL_##wc(X, _FP_WORKBITS); \
} \
} \
- if ((FP_CUR_EXCEPTIONS & FP_EX_INEXACT) || \
- (FP_TRAPPING_EXCEPTIONS & FP_EX_UNDERFLOW)) \
+ if (_FP_PACK_CANONICAL_is_tiny \
+ && ((FP_CUR_EXCEPTIONS & FP_EX_INEXACT) || \
+ (FP_TRAPPING_EXCEPTIONS & FP_EX_UNDERFLOW))) \
FP_SET_EXCEPTION(FP_EX_UNDERFLOW); \
} \
else \
diff --git a/include/math-emu/soft-fp.h b/include/math-emu/soft-fp.h
index 5650c1628383..02ada0a9fca1 100644
--- a/include/math-emu/soft-fp.h
+++ b/include/math-emu/soft-fp.h
@@ -31,6 +31,14 @@
#include <endian.h>
#endif
+/* Whether the architecture determines tininess of a floating-point
+ result after rounding rather than before it. IEEE 754 permits either
+ but requires the same choice for every operation, so this has to agree
+ with what the hardware does for the results it produces itself. */
+#ifndef _FP_TININESS_AFTER_ROUNDING
+#define _FP_TININESS_AFTER_ROUNDING 0
+#endif
+
#define _FP_WORKBITS 3
#define _FP_WORK_LSB ((_FP_W_TYPE)1 << 3)
#define _FP_WORK_ROUND ((_FP_W_TYPE)1 << 2)
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 31fc1bee3797..b900734ee9c1 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -834,6 +834,9 @@ bool v4l2_ctrl_radio_filter(const struct v4l2_ctrl *ctrl);
*
* @ncontrols: The number of controls in this cluster.
* @controls: The cluster control array of size @ncontrols.
+ *
+ * If controls[0] is NULL, then this function does nothing and just
+ * returns.
*/
void v4l2_ctrl_cluster(unsigned int ncontrols, struct v4l2_ctrl **controls);
@@ -845,6 +848,9 @@ void v4l2_ctrl_cluster(unsigned int ncontrols, struct v4l2_ctrl **controls);
* @ncontrols: The number of controls in this cluster.
* @controls: The cluster control array of size @ncontrols. The first control
* must be the 'auto' control (e.g. autogain, autoexposure, etc.)
+ *
+ * If controls[0] is NULL, then this function does nothing and just
+ * returns.
* @manual_val: The value for the first control in the cluster that equals the
* manual setting.
* @set_volatile: If true, then all controls except the first auto control will
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index b353dea9aedc..03d6543a5189 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -80,6 +80,9 @@ struct ifa6_config {
u8 ifa_proto;
const struct in6_addr *peer_pfx;
+ struct inet6_ifaddr *ifpub;
+ unsigned long cstamp;
+ unsigned long tstamp;
u32 rt_priority;
u32 ifa_flags;
diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h
index d50e0afc3193..39ccb785dd4c 100644
--- a/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -297,14 +297,6 @@ static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk)
bool inet_csk_reqsk_queue_drop(struct sock *sk, struct request_sock *req);
void inet_csk_reqsk_queue_drop_and_put(struct sock *sk, struct request_sock *req);
-static inline unsigned long
-reqsk_timeout(struct request_sock *req, unsigned long max_timeout)
-{
- u64 timeout = (u64)req->timeout << req->num_timeout;
-
- return (unsigned long)min_t(u64, timeout, max_timeout);
-}
-
void inet_csk_destroy_sock(struct sock *sk);
void inet_csk_prepare_for_destroy_sock(struct sock *sk);
void inet_csk_prepare_forced_close(struct sock *sk);
diff --git a/include/net/ip.h b/include/net/ip.h
index 5041202ccce2..86e555fb6897 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -513,31 +513,6 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
return res;
}
-/* Configured/administrative MTU of a route, for advertising the TCP MSS.
- *
- * Unlike ip_dst_mtu_maybe_forward(), this deliberately ignores the
- * ICMP-learned path MTU (rt->rt_pmtu). The advertised MSS bounds what the
- * peer may send to us and must reflect our receive capability (the device or
- * route-configured MTU), not a path MTU learned on the reverse (send)
- * direction, which may not apply to the peer->us path and outlives the fnhe
- * for the whole connection. See RFC 2923 section 2.3 and the comment above
- * tcp_advertise_mss().
- */
-static inline unsigned int ip_dst_mtu_configured(const struct dst_entry *dst)
-{
- unsigned int mtu, res;
-
- rcu_read_lock();
- mtu = dst_metric_raw(dst, RTAX_MTU);
- if (!mtu)
- mtu = READ_ONCE(dst_dev_rcu(dst)->mtu);
- mtu = min_t(unsigned int, mtu, IP_MAX_MTU);
- res = mtu - lwtunnel_headroom(dst->lwtstate, mtu);
- rcu_read_unlock();
-
- return res;
-}
-
static inline unsigned int ip_skb_dst_mtu(struct sock *sk,
const struct sk_buff *skb)
{
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 4137bdf05baa..956ebbebd53c 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -354,43 +354,6 @@ static inline unsigned int ip6_dst_mtu_maybe_forward(const struct dst_entry *dst
return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
}
-/* Configured/administrative MTU of a route, for advertising the TCP MSS.
- *
- * Unlike ip6_dst_mtu_maybe_forward(), this ignores any ICMPv6-learned path
- * MTU (which is kept on the RTF_CACHE exception route) and returns the MTU of
- * the underlying route (fib6_pmtu) or the egress device. The advertised MSS
- * bounds what the peer may send to us and must reflect our receive
- * capability, not a path MTU learned on the reverse (send) direction. See
- * RFC 2923 section 2.3 and the comment above tcp_advertise_mss().
- */
-static inline unsigned int ip6_dst_mtu_configured(const struct dst_entry *dst)
-{
- const struct rt6_info *rt = dst_rt6_info(dst);
- const struct fib6_info *from;
- struct inet6_dev *idev;
- unsigned int mtu = 0;
-
- rcu_read_lock();
- /* IPv6 keeps the learned PMTU and the configured MTU in the same
- * RTAX_MTU slot: the learned value sits on this (possibly RTF_CACHE)
- * dst, the configured one on the underlying route. Reach the latter
- * via ->from (fib6_pmtu), populated by ip6_route_info_create().
- */
- from = rcu_dereference(rt->from);
- if (from)
- mtu = from->fib6_pmtu;
- if (!mtu) {
- mtu = IPV6_MIN_MTU;
- idev = __in6_dev_get(dst_dev_rcu(dst));
- if (idev)
- mtu = max_t(unsigned int, mtu, READ_ONCE(idev->cnf.mtu6));
- }
- rcu_read_unlock();
-
- mtu = min_t(unsigned int, mtu, IP6_MAX_MTU);
- return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
-}
-
u32 ip6_mtu_from_fib6(const struct fib6_result *res,
const struct in6_addr *daddr,
const struct in6_addr *saddr);
diff --git a/include/net/mptcp.h b/include/net/mptcp.h
index f7263fe2a2e4..a296f711e92f 100644
--- a/include/net/mptcp.h
+++ b/include/net/mptcp.h
@@ -219,6 +219,9 @@ int mptcp_subflow_init_cookie_req(struct request_sock *req,
struct request_sock *mptcp_subflow_reqsk_alloc(const struct request_sock_ops *ops,
struct sock *sk_listener,
bool attach_listener);
+/* Caller must hold an RCU read-side lock. */
+void mptcp_subflow_reqsk_clone(struct request_sock *req,
+ struct request_sock *new_req);
__be32 mptcp_get_reset_option(const struct sk_buff *skb);
@@ -314,6 +317,12 @@ static inline struct request_sock *mptcp_subflow_reqsk_alloc(const struct reques
return NULL;
}
+/* Caller must hold an RCU read-side lock. */
+static inline void mptcp_subflow_reqsk_clone(struct request_sock *req,
+ struct request_sock *new_req)
+{
+}
+
static inline __be32 mptcp_reset_option(const struct sk_buff *skb) { return htonl(0u); }
static inline void mptcp_active_detect_blackhole(struct sock *sk, bool expired) { }
diff --git a/include/net/tcp.h b/include/net/tcp.h
index e40bad560648..f9bcfa8773a3 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -838,6 +838,13 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp)
return usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us);
}
+static inline unsigned long tcp_reqsk_timeout(struct request_sock *req)
+{
+ u64 timeout = (u64)req->timeout << req->num_timeout;
+
+ return (unsigned long)min_t(u64, timeout, TCP_RTO_MAX);
+}
+
u32 tcp_delack_max(const struct sock *sk);
/* Compute the actual rto_min value */
diff --git a/include/trace/events/huge_memory.h b/include/trace/events/huge_memory.h
index 2276614691df..1464d45c90ab 100644
--- a/include/trace/events/huge_memory.h
+++ b/include/trace/events/huge_memory.h
@@ -167,10 +167,10 @@ TRACE_EVENT(mm_collapse_huge_page_swapin,
TRACE_EVENT(mm_khugepaged_scan_file,
- TP_PROTO(struct mm_struct *mm, struct folio *folio, struct file *file,
+ TP_PROTO(struct mm_struct *mm, unsigned long pfn, struct file *file,
int present, int swap, int result),
- TP_ARGS(mm, folio, file, present, swap, result),
+ TP_ARGS(mm, pfn, file, present, swap, result),
TP_STRUCT__entry(
__field(struct mm_struct *, mm)
@@ -183,7 +183,7 @@ TRACE_EVENT(mm_khugepaged_scan_file,
TP_fast_assign(
__entry->mm = mm;
- __entry->pfn = folio ? folio_pfn(folio) : -1;
+ __entry->pfn = pfn;
__assign_str(filename);
__entry->present = present;
__entry->swap = swap;
diff --git a/init/main.c b/init/main.c
index 269b30038b9f..2097f4bbc0d8 100644
--- a/init/main.c
+++ b/init/main.c
@@ -910,12 +910,12 @@ void start_kernel(void)
__stop___param - __start___param,
-1, -1, NULL, &unknown_bootoption);
print_unknown_bootoptions();
- if (!IS_ERR_OR_NULL(after_dashes))
- parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
- NULL, set_init_arg);
if (extra_init_args)
parse_args("Setting extra init args", extra_init_args,
NULL, 0, -1, -1, NULL, set_init_arg);
+ if (!IS_ERR_OR_NULL(after_dashes))
+ parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
+ NULL, set_init_arg);
/* Architectural and non-timekeeping rng init, before allocator init */
random_init_early(command_line);
diff --git a/init/version.c b/init/version.c
index 94c96f6fbfe6..0bd5c45aabc4 100644
--- a/init/version.c
+++ b/init/version.c
@@ -23,6 +23,9 @@ static int __init early_hostname(char *arg)
size_t maxlen = bufsize - 1;
ssize_t arglen;
+ if (!arg)
+ return -EINVAL;
+
arglen = strscpy(init_uts_ns.name.nodename, arg, bufsize);
if (arglen < 0) {
pr_warn("hostname parameter exceeds %zd characters and will be truncated",
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 093551fe66a7..11eaf5df3645 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -529,6 +529,13 @@ static void mqueue_evict_inode(struct inode *inode)
list_add_tail(&msg->m_list, &tmp_msg);
kfree(info->node_cache);
spin_unlock(&info->lock);
+ /*
+ * A shared file table can let the notification owner exit without
+ * running ->flush(). No users of the inode remain during eviction, so
+ * tear down any stale notification after dropping info->lock because
+ * netlink_sendskb() may release the final socket reference.
+ */
+ remove_notification(info);
list_for_each_entry_safe(msg, nmsg, &tmp_msg, m_list) {
list_del(&msg->m_list);
diff --git a/ipc/msgutil.c b/ipc/msgutil.c
index 7a03f6d03de3..2b93abb808e7 100644
--- a/ipc/msgutil.c
+++ b/ipc/msgutil.c
@@ -63,7 +63,8 @@ static struct msg_msg *alloc_msg(size_t len)
size_t alen;
alen = min(len, DATALEN_MSG);
- msg = kmem_buckets_alloc(msg_buckets, sizeof(*msg) + alen, GFP_KERNEL);
+ msg = kmem_buckets_alloc(msg_buckets, sizeof(*msg) + alen,
+ GFP_KERNEL_ACCOUNT);
if (msg == NULL)
return NULL;
diff --git a/ipc/shm.c b/ipc/shm.c
index c77a6c8683ce..2f5cee0c78c7 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -936,26 +936,11 @@ static inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminf
static void shm_add_rss_swap(struct shmid_kernel *shp,
unsigned long *rss_add, unsigned long *swp_add)
{
- struct inode *inode;
+ struct inode *inode = file_inode(shp->shm_file);
- inode = file_inode(shp->shm_file);
-
- if (is_file_hugepages(shp->shm_file)) {
- struct address_space *mapping = inode->i_mapping;
- struct hstate *h = hstate_file(shp->shm_file);
- *rss_add += pages_per_huge_page(h) * mapping->nrpages;
- } else {
-#ifdef CONFIG_SHMEM
- struct shmem_inode_info *info = SHMEM_I(inode);
-
- spin_lock_irq(&info->lock);
- *rss_add += inode->i_mapping->nrpages;
- *swp_add += info->swapped;
- spin_unlock_irq(&info->lock);
-#else
- *rss_add += inode->i_mapping->nrpages;
-#endif
- }
+ *rss_add += READ_ONCE(inode->i_mapping->nrpages);
+ if (IS_ENABLED(CONFIG_SHMEM) && !is_file_hugepages(shp->shm_file))
+ *swp_add += READ_ONCE(SHMEM_I(inode)->swapped);
}
/*
diff --git a/kernel/bpf/queue_stack_maps.c b/kernel/bpf/queue_stack_maps.c
index c1c9dee4dcdd..b5628e414981 100644
--- a/kernel/bpf/queue_stack_maps.c
+++ b/kernel/bpf/queue_stack_maps.c
@@ -59,6 +59,9 @@ static int queue_stack_map_alloc_check(union bpf_attr *attr)
*/
return -E2BIG;
+ if (attr->max_entries >= U32_MAX / attr->value_size)
+ return -E2BIG;
+
return 0;
}
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 07e1e2922819..aa629b7d76e5 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -4219,7 +4219,7 @@ static void __cgroup_kill(struct cgroup *cgrp)
cgrp->kill_seq++;
spin_unlock_irq(&css_set_lock);
- css_task_iter_start(&cgrp->self, CSS_TASK_ITER_PROCS | CSS_TASK_ITER_THREADED, &it);
+ css_task_iter_start(&cgrp->self, CSS_TASK_ITER_THREADED, &it);
while ((task = css_task_iter_next(&it))) {
/* Ignore kernel threads here. */
if (task->flags & PF_KTHREAD)
diff --git a/kernel/cpu_pm.c b/kernel/cpu_pm.c
index 7481fbb947d3..a2a598ad6e6d 100644
--- a/kernel/cpu_pm.c
+++ b/kernel/cpu_pm.c
@@ -10,6 +10,7 @@
#include <linux/cpu_pm.h>
#include <linux/module.h>
#include <linux/notifier.h>
+#include <linux/rcupdate.h>
#include <linux/spinlock.h>
#include <linux/syscore_ops.h>
@@ -76,7 +77,8 @@ EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
*
* Remove a driver from the CPU PM notifier list.
*
- * This function has the same return conditions as raw_notifier_chain_unregister.
+ * This function may sleep, and has the same return conditions as
+ * raw_notifier_chain_unregister.
*/
int cpu_pm_unregister_notifier(struct notifier_block *nb)
{
@@ -86,6 +88,11 @@ int cpu_pm_unregister_notifier(struct notifier_block *nb)
raw_spin_lock_irqsave(&cpu_pm_notifier.lock, flags);
ret = raw_notifier_chain_unregister(&cpu_pm_notifier.chain, nb);
raw_spin_unlock_irqrestore(&cpu_pm_notifier.lock, flags);
+
+ /* Wait for the rcu_read_lock() walkers in cpu_pm_notify(). */
+ if (!ret)
+ synchronize_rcu();
+
return ret;
}
EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c
index 22fe969c5d2e..1a917c5bdd1e 100644
--- a/kernel/debug/gdbstub.c
+++ b/kernel/debug/gdbstub.c
@@ -42,6 +42,8 @@
/* Our I/O buffers. */
static char remcom_in_buffer[BUFMAX];
static char remcom_out_buffer[BUFMAX];
+/* Payload bytes get_packet() stored in remcom_in_buffer, excluding the NUL. */
+static int remcom_in_len;
static int gdbstub_use_prev_in_buf;
static int gdbstub_prev_in_buf_pos;
@@ -86,7 +88,7 @@ static int gdbstub_read_wait(void)
}
#endif
/* scan for the sequence $<data>#<checksum> */
-static void get_packet(char *buffer)
+static int get_packet(char *buffer)
{
unsigned char checksum;
unsigned char xmitcsum;
@@ -134,6 +136,8 @@ static void get_packet(char *buffer)
}
buffer[count] = 0;
} while (checksum != xmitcsum);
+
+ return count;
}
/*
@@ -320,16 +324,26 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val)
* Copy the binary array pointed to by buf into mem. Fix $, #, and
* 0x7d escaped with 0x7d. Return -EFAULT on failure or 0 on success.
* The input buf is overwritten with the result to write to mem.
+ *
+ * buf_end points one past the last byte received for this packet. Each
+ * 0x7d escape consumes a second input byte, so decoding count bytes can
+ * read up to 2 * count input bytes; stop at buf_end rather than running
+ * off the end of remcom_in_buffer.
*/
-static int kgdb_ebin2mem(char *buf, char *mem, int count)
+static int kgdb_ebin2mem(char *buf, char *mem, int count, char *buf_end)
{
int size = 0;
char *c = buf;
while (count-- > 0) {
+ if (buf >= buf_end)
+ return -EINVAL;
c[size] = *buf++;
- if (c[size] == 0x7d)
+ if (c[size] == 0x7d) {
+ if (buf >= buf_end)
+ return -EINVAL;
c[size] = *buf++ ^ 0x20;
+ }
size++;
}
@@ -366,16 +380,33 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
static int write_mem_msg(int binary)
{
char *ptr = &remcom_in_buffer[1];
+ char *buf_end = &remcom_in_buffer[remcom_in_len];
unsigned long addr;
unsigned long length;
int err;
if (kgdb_hex2long(&ptr, &addr) > 0 && *(ptr++) == ',' &&
kgdb_hex2long(&ptr, &length) > 0 && *(ptr++) == ':') {
- if (binary)
- err = kgdb_ebin2mem(ptr, (char *)addr, length);
- else
+ /*
+ * Trust only the bytes actually received, not length: the
+ * client can claim more payload than it sent. 'M' decodes two
+ * hex chars per byte in place, so it needs 2 * length bytes at
+ * ptr; 'X' decodes one byte per byte, and kgdb_ebin2mem() bounds
+ * its extra 0x7d-escape reads against buf_end itself.
+ */
+ if (ptr >= buf_end)
+ return -EINVAL;
+
+ if (binary) {
+ if (length > (unsigned long)(buf_end - ptr))
+ return -EINVAL;
+ err = kgdb_ebin2mem(ptr, (char *)addr, length, buf_end);
+ } else {
+ if (length > (unsigned long)(buf_end - ptr) / 2)
+ return -EINVAL;
err = kgdb_hex2mem(ptr, (char *)addr, length);
+ }
+
if (err)
return err;
if (CACHE_FLUSH_IS_SAFE)
@@ -562,6 +593,14 @@ static void gdb_cmd_memread(struct kgdb_state *ks)
if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' &&
kgdb_hex2long(&ptr, &length) > 0) {
+ /*
+ * The hex-encoded reply needs 2 * length + 1 bytes
+ * in remcom_out_buffer[BUFMAX].
+ */
+ if (length > (BUFMAX - 1) / 2) {
+ error_packet(remcom_out_buffer, -EINVAL);
+ return;
+ }
err = kgdb_mem2hex((char *)addr, remcom_out_buffer, length);
if (!err)
error_packet(remcom_out_buffer, -EINVAL);
@@ -777,9 +816,9 @@ static void gdb_cmd_query(struct kgdb_state *ks)
strscpy(remcom_out_buffer, "E01");
break;
}
+ len = len / 2;
kgdb_hex2mem(remcom_in_buffer + 6,
remcom_out_buffer, len);
- len = len / 2;
remcom_out_buffer[len++] = 0;
kdb_common_init_state(ks);
@@ -984,7 +1023,7 @@ int gdb_serial_stub(struct kgdb_state *ks)
/* Clear the out buffer. */
memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer));
- get_packet(remcom_in_buffer);
+ remcom_in_len = get_packet(remcom_in_buffer);
switch (remcom_in_buffer[0]) {
case '?': /* gdbserial status */
diff --git a/kernel/fork.c b/kernel/fork.c
index b7543a93f269..bc6078b910a6 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -810,7 +810,8 @@ void __init fork_init(void)
#ifndef ARCH_MIN_TASKALIGN
#define ARCH_MIN_TASKALIGN 0
#endif
- int align = max_t(int, L1_CACHE_BYTES, ARCH_MIN_TASKALIGN);
+ int align = max3(L1_CACHE_BYTES, ARCH_MIN_TASKALIGN,
+ __alignof__(struct task_struct));
unsigned long useroffset, usersize;
/* create a slab on which task_structs can be allocated */
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index d197854a93c8..97e50ade3bb9 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -78,6 +78,7 @@ enum {
#include "settings.h"
extern int __irq_set_trigger(struct irq_desc *desc, unsigned long flags);
+extern int __irq_set_irqchip_state(struct irq_desc *desc, enum irqchip_irq_state which, bool val);
extern void __disable_irq(struct irq_desc *desc);
extern void __enable_irq(struct irq_desc *desc);
diff --git a/kernel/irq/kexec.c b/kernel/irq/kexec.c
index 1a3deffe6b5b..f245b9876422 100644
--- a/kernel/irq/kexec.c
+++ b/kernel/irq/kexec.c
@@ -25,7 +25,7 @@ void machine_kexec_mask_interrupts(void)
* First try to remove the active state from an interrupt which is forwarded
* to a VM. If the interrupt is not forwarded, try to EOI the interrupt.
*/
- check_eoi = irq_set_irqchip_state(i, IRQCHIP_STATE_ACTIVE, false);
+ check_eoi = __irq_set_irqchip_state(desc, IRQCHIP_STATE_ACTIVE, false);
}
if (check_eoi && chip->irq_eoi && irqd_irq_inprogress(&desc->irq_data))
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index a0b79bcc0e08..19eda7aa9feb 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -2702,6 +2702,28 @@ int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which, bool *
}
EXPORT_SYMBOL_GPL(irq_get_irqchip_state);
+int __irq_set_irqchip_state(struct irq_desc *desc, enum irqchip_irq_state which, bool val)
+{
+ struct irq_data *data = irq_desc_get_irq_data(desc);
+ struct irq_chip *chip;
+
+ do {
+ chip = irq_data_get_irq_chip(data);
+
+ if (WARN_ON_ONCE(!chip))
+ return -ENODEV;
+
+ if (chip->irq_set_irqchip_state)
+ break;
+
+ data = irqd_get_parent_data(data);
+ } while (data);
+
+ if (data)
+ return chip->irq_set_irqchip_state(data, which, val);
+ return -EINVAL;
+}
+
/**
* irq_set_irqchip_state - set the state of a forwarded interrupt.
* @irq: Interrupt line that is forwarded to a VM
@@ -2716,25 +2738,8 @@ EXPORT_SYMBOL_GPL(irq_get_irqchip_state);
*/
int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which, bool val)
{
- scoped_irqdesc_get_and_buslock(irq, 0) {
- struct irq_data *data = irq_desc_get_irq_data(scoped_irqdesc);
- struct irq_chip *chip;
-
- do {
- chip = irq_data_get_irq_chip(data);
-
- if (WARN_ON_ONCE(!chip))
- return -ENODEV;
-
- if (chip->irq_set_irqchip_state)
- break;
-
- data = irqd_get_parent_data(data);
- } while (data);
-
- if (data)
- return chip->irq_set_irqchip_state(data, which, val);
- }
+ scoped_irqdesc_get_and_buslock(irq, 0)
+ return __irq_set_irqchip_state(scoped_irqdesc, which, val);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(irq_set_irqchip_state);
diff --git a/kernel/panic.c b/kernel/panic.c
index b13f040680ac..7f8725f2154b 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -364,7 +364,8 @@ void nmi_panic(struct pt_regs *regs, const char *msg)
{
if (panic_try_start())
panic("%s", msg);
- else if (panic_on_other_cpu())
+
+ if (panic_on_other_cpu())
nmi_panic_self_stop(regs);
}
EXPORT_SYMBOL(nmi_panic);
diff --git a/kernel/resource.c b/kernel/resource.c
index fe1327753a13..139fb5e19cbe 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -1300,8 +1300,8 @@ static int __request_region_locked(struct resource *res, struct resource *parent
}
if (conflict->flags & flags & IORESOURCE_MUXED) {
add_wait_queue(&muxed_resource_wait, &wait);
- write_unlock(&resource_lock);
set_current_state(TASK_UNINTERRUPTIBLE);
+ write_unlock(&resource_lock);
schedule();
remove_wait_queue(&muxed_resource_wait, &wait);
write_lock(&resource_lock);
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index 22a1fbccb929..9b5db92744ae 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -72,6 +72,18 @@ static void sugov_update_rate_limit_us(struct sugov_policy *sg_policy)
(s64)sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
}
+static void sugov_update_rate_limit_us_sync(struct sugov_policy *sg_policy)
+{
+ /*
+ * Serialize with rate_limit_us_store() to prevent this path from using
+ * a stale rate limit tunable value to set the limit for the given
+ * policy.
+ */
+ guard(mutex)(&sg_policy->tunables->attr_set.update_lock);
+
+ sugov_update_rate_limit_us(sg_policy);
+}
+
static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)
{
s64 delta_ns;
@@ -859,7 +871,7 @@ static int sugov_start(struct cpufreq_policy *policy)
void (*uu)(struct update_util_data *data, u64 time, unsigned int flags);
unsigned int cpu;
- sugov_update_rate_limit_us(sg_policy);
+ sugov_update_rate_limit_us_sync(sg_policy);
sg_policy->last_freq_update_time = 0;
sg_policy->next_freq = 0;
sg_policy->work_in_progress = false;
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 67e8c2065d82..fbc277c6963c 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -2794,7 +2794,7 @@ static void update_task_scan_period(struct task_struct *p,
p->mm->numa_next_scan = jiffies +
msecs_to_jiffies(p->numa_scan_period);
- return;
+ goto out;
}
/*
@@ -2838,7 +2838,10 @@ static void update_task_scan_period(struct task_struct *p,
p->numa_scan_period = clamp(p->numa_scan_period + diff,
task_scan_min(p), task_scan_max(p));
- memset(p->numa_faults_locality, 0, sizeof(p->numa_faults_locality));
+
+out:
+ memset(p->numa_faults_locality, 0,
+ sizeof(p->numa_faults_locality));
}
/*
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 25f62867a16d..7fc3b3b13875 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -1810,10 +1810,13 @@ static long seccomp_notify_addfd(struct seccomp_filter *filter,
* We need to check again if the addfd request has been handled,
* and if not, we will remove it from the queue.
*/
- if (list_empty(&kaddfd.list))
+ if (list_empty(&kaddfd.list)) {
ret = kaddfd.ret;
- else
+ } else {
list_del(&kaddfd.list);
+ if (addfd.flags & SECCOMP_ADDFD_FLAG_SEND)
+ knotif->state = SECCOMP_NOTIFY_SENT;
+ }
out_unlock:
mutex_unlock(&filter->notify_lock);
diff --git a/kernel/sys.c b/kernel/sys.c
index c8376d7a0741..bc89bd133111 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1077,6 +1077,9 @@ SYSCALL_DEFINE1(times, struct tms __user *, tbuf)
#ifdef CONFIG_COMPAT
static compat_clock_t clock_t_to_compat_clock_t(clock_t x)
{
+ if (USER_HZ == COMPAT_USER_HZ)
+ return x;
+
return compat_jiffies_to_clock_t(clock_t_to_jiffies(x));
}
diff --git a/kernel/time/itimer.c b/kernel/time/itimer.c
index 03a32dffc56c..f7de39769095 100644
--- a/kernel/time/itimer.c
+++ b/kernel/time/itimer.c
@@ -60,7 +60,7 @@ static void get_cpu_itimer(struct task_struct *tsk, unsigned int clock_id,
thread_group_sample_cputime(tsk, samples);
t = samples[clock_id];
- if (val < t)
+ if (val <= t)
/* about to fire */
val = TICK_NSEC;
else
diff --git a/kernel/time/namespace.c b/kernel/time/namespace.c
index 99dcdb3315db..632f33157284 100644
--- a/kernel/time/namespace.c
+++ b/kernel/time/namespace.c
@@ -51,7 +51,7 @@ ktime_t do_timens_ktime_to_host(clockid_t clockid, ktime_t tim,
tim = 0;
} else {
tim = ktime_sub(tim, offset);
- if (unlikely(tim > KTIME_MAX))
+ if (unlikely(tim < 0))
tim = KTIME_MAX;
}
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index b3d4bae9ee3e..fcb98cbb1219 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -1597,8 +1597,8 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
*/
expires = cpu_timer_getexpires(&timer.it.cpu);
error = posix_cpu_timer_set(&timer, 0, &zero_it, &it);
- if (!error) {
- /* Timer is now unarmed, deletion can not fail. */
+ if (error != TIMER_RETRY) {
+ /* Unarmed or target gone (-ESRCH), deletion can not fail. */
posix_cpu_timer_del(&timer);
} else {
while (error == TIMER_RETRY) {
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 377e29ca5a52..32f59de2ba4c 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2569,6 +2569,7 @@ ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq, ktime_t *offs_real,
ktime_t *offs_boot, ktime_t *offs_tai)
{
struct timekeeper *tk = &tk_core.timekeeper;
+ u32 cwsseq_orig = *cwsseq;
unsigned int seq;
ktime_t base;
u64 nsecs;
@@ -2580,7 +2581,7 @@ ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq, ktime_t *offs_real,
nsecs = timekeeping_get_ns(&tk->tkr_mono);
base = ktime_add_ns(base, nsecs);
- if (*cwsseq != tk->clock_was_set_seq) {
+ if (cwsseq_orig != tk->clock_was_set_seq) {
*cwsseq = tk->clock_was_set_seq;
*offs_real = tk->offs_real;
*offs_boot = tk->offs_boot;
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index f74acc78866d..dcb762664ce7 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -7004,6 +7004,8 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
if (psize > RB_WRITE_MASK + 1)
return -EINVAL;
+ guard(cpus_read_lock)();
+
/* prevent another thread from changing buffer sizes */
guard(mutex)(&buffer->mutex);
diff --git a/kernel/ucount.c b/kernel/ucount.c
index fc4a8f2d3096..5cbf1553658e 100644
--- a/kernel/ucount.c
+++ b/kernel/ucount.c
@@ -302,23 +302,26 @@ long inc_rlimit_get_ucounts(struct ucounts *ucounts, enum rlimit_type type,
for (iter = ucounts; iter; iter = iter->ns->ucounts) {
long new = atomic_long_add_return(1, &iter->rlimit[type]);
- if (new < 0 || new > max)
+ /*
+ * Take the extra reference on the 0 -> 1 transition before the
+ * limit check: a rejected walk must not leave a concurrent one
+ * holding the surviving increment with no reference.
+ */
+ if (new == 1 && !get_ucounts(iter))
goto dec_unwind;
+ if (new < 0 || new > max) {
+ /* undo iter too, releasing the reference taken above */
+ do_dec_rlimit_put_ucounts(ucounts, iter->ns->ucounts, type);
+ return 0;
+ }
if (iter == ucounts)
ret = new;
if (!override_rlimit)
max = get_userns_rlimit_max(iter->ns, type);
- /*
- * Grab an extra ucount reference for the caller when
- * the rlimit count was previously 0.
- */
- if (new != 1)
- continue;
- if (!get_ucounts(iter))
- goto dec_unwind;
}
return ret;
dec_unwind:
+ /* get_ucounts() failed for iter: undo iter without a put */
dec = atomic_long_sub_return(1, &iter->rlimit[type]);
WARN_ON_ONCE(dec < 0);
do_dec_rlimit_put_ucounts(ucounts, iter, type);
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 03cb63883d04..702f934e69a9 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -45,8 +45,11 @@ static void set_cred_user_ns(struct cred *cred, struct user_namespace *user_ns)
{
/* Start with the same capabilities as init but useless for doing
* anything as the capabilities are bound to the new user namespace.
+ * The exec securebits grant nothing, they are restrictions the task
+ * placed on itself. Keep them and their locks.
*/
- cred->securebits = SECUREBITS_DEFAULT;
+ cred->securebits = SECUREBITS_DEFAULT |
+ (cred->securebits & (SECURE_ALL_UNPRIVILEGED | SECURE_ALL_UNPRIVILEGED << 1));
cred->cap_inheritable = CAP_EMPTY_SET;
cred->cap_permitted = CAP_FULL_SET;
cred->cap_effective = CAP_FULL_SET;
diff --git a/lib/842/842_decompress.c b/lib/842/842_decompress.c
index 582085ef8b49..45a9815abd63 100644
--- a/lib/842/842_decompress.c
+++ b/lib/842/842_decompress.c
@@ -165,6 +165,9 @@ static int __do_index(struct sw842_param *p, u8 size, u8 bits, u64 fsize)
u64 index, offset, total = round_down(p->out - p->ostart, 8);
int ret;
+ if (size > p->olen)
+ return -ENOSPC;
+
ret = next_bits(p, &index, bits);
if (ret)
return ret;
@@ -306,7 +309,7 @@ int sw842_decompress(const u8 *in, unsigned int ilen,
if (ret)
return ret;
- if (p.out == out) /* no previous bytes */
+ if (p.out - p.ostart < 8) /* no complete previous block */
return -EINVAL;
/* copy rep + 1 */
@@ -344,6 +347,8 @@ int sw842_decompress(const u8 *in, unsigned int ilen,
if (!bytes || bytes > SHORT_DATA_BITS_MAX)
return -EINVAL;
+ if (bytes > p.olen)
+ return -ENOSPC;
while (bytes-- > 0) {
ret = next_bits(&p, &tmp, 8);
diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c
index 7c6d3a086fdc..fbe9e6c99521 100644
--- a/lib/alloc_tag.c
+++ b/lib/alloc_tag.c
@@ -276,7 +276,7 @@ void __init alloc_tag_sec_init(void)
kernel_tags.count = last_codetag - kernel_tags.first_tag;
/* Check if kernel tags fit into page flags */
- if (kernel_tags.count > (1UL << NR_UNUSED_PAGEFLAG_BITS)) {
+ if (CODETAG_ID_FIRST + kernel_tags.count > (1UL << NR_UNUSED_PAGEFLAG_BITS)) {
shutdown_mem_profiling(false); /* allocinfo file does not exist yet */
pr_err("%lu allocation tags cannot be references using %d available page flag bits. Memory allocation profiling is disabled!\n",
kernel_tags.count, NR_UNUSED_PAGEFLAG_BITS);
diff --git a/lib/buildid.c b/lib/buildid.c
index ef112a7084ef..015a1f9d2b48 100644
--- a/lib/buildid.c
+++ b/lib/buildid.c
@@ -66,6 +66,10 @@ static int freader_get_folio(struct freader *r, loff_t file_off)
freader_put_folio(r);
+ /* the page index must fit in pgoff_t */
+ if (file_off < 0 || file_off > MAX_LFS_FILESIZE)
+ return -EFAULT;
+
/* only use page cache lookup - fail if not already cached */
r->folio = filemap_get_folio(r->file->f_mapping, file_off >> PAGE_SHIFT);
diff --git a/lib/decompress_bunzip2.c b/lib/decompress_bunzip2.c
index ca736166f100..b8fb40ed06d2 100644
--- a/lib/decompress_bunzip2.c
+++ b/lib/decompress_bunzip2.c
@@ -428,6 +428,10 @@ static int INIT get_next_block(struct bunzip_data *bd)
t += (runPos << nextSym);
/* +runPos if RUNA; +2*runPos if RUNB */
+ /* Bound the run so t and runPos cannot overflow. */
+ if (t >= dbufSize)
+ return RETVAL_DATA_ERROR;
+
runPos <<= 1;
continue;
}
@@ -439,7 +443,7 @@ static int INIT get_next_block(struct bunzip_data *bd)
array.) */
if (runPos) {
runPos = 0;
- if (dbufCount+t >= dbufSize)
+ if (dbufCount+t > dbufSize)
return RETVAL_DATA_ERROR;
uc = symToByte[mtfSymbol[0]];
diff --git a/lib/fault-inject.c b/lib/fault-inject.c
index 999053fa133e..02916ef2761c 100644
--- a/lib/fault-inject.c
+++ b/lib/fault-inject.c
@@ -5,6 +5,7 @@
#include <linux/debugfs.h>
#include <linux/sched.h>
#include <linux/stat.h>
+#include <linux/string.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/export.h>
@@ -64,7 +65,7 @@ static void fail_dump(struct fault_attr *attr)
{
if (attr->verbose > 0 && __ratelimit(&attr->ratelimit_state)) {
printk(KERN_NOTICE "FAULT_INJECTION: forcing a failure.\n"
- "name %pd, interval %lu, probability %lu, "
+ "name %s, interval %lu, probability %lu, "
"space %d, times %d\n", attr->dname,
attr->interval, attr->probability,
atomic_read(&attr->space),
@@ -261,7 +262,9 @@ struct dentry *fault_create_debugfs_attr(const char *name,
debugfs_create_xul("reject-end", mode, dir, &attr->reject_end);
#endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
- attr->dname = dget(dir);
+ if (strscpy(attr->dname, name, sizeof(attr->dname)) == -E2BIG)
+ pr_warn("FAULT_INJECTION: name '%s' truncated to '%s'\n",
+ name, attr->dname);
return dir;
}
EXPORT_SYMBOL_GPL(fault_create_debugfs_attr);
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index 4bc7c1933755..b2eb629497a9 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -836,6 +836,10 @@ unsigned long iov_iter_alignment(const struct iov_iter *i)
return 0;
}
+ /* An empty iterator may have no segment array at all. */
+ if (!i->count)
+ return 0;
+
/* iovec and kvec have identical layouts */
if (likely(iter_is_iovec(i) || iov_iter_is_kvec(i)))
return iov_iter_alignment_iovec(i);
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
index 5a2480467ab9..bdc2720b7aac 100644
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -83,7 +83,7 @@ static int kobject_action_type(const char *buf, size_t count,
for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
if (strncmp(kobject_actions[action], buf, count_first) != 0)
continue;
- if (kobject_actions[action][count_first] != '\0')
+ if (strlen(kobject_actions[action]) != count_first)
continue;
if (args)
*args = args_start;
diff --git a/lib/plist.c b/lib/plist.c
index 330febb4bd7d..1e9cb0e35af7 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -174,8 +174,15 @@ void plist_requeue(struct plist_node *node, struct plist_head *head)
/*
* After plist_del(), iter is the replacement of the node. If the node
* was on prio_list, take shortcut to find node_next instead of looping.
+ *
+ * prio_list is a headless ring, so from the LAST bucket ->next wraps
+ * round to the first one; in that case node_next is the list head.
*/
if (!list_empty(&iter->prio_list)) {
+ struct plist_node *first = plist_first(head);
+
+ if (iter->prio_list.next == &first->prio_list)
+ goto queue;
iter = list_entry(iter->prio_list.next, struct plist_node,
prio_list);
node_next = &iter->node_list;
diff --git a/lib/stackdepot.c b/lib/stackdepot.c
index de0b0025af2b..b20f4db5c83f 100644
--- a/lib/stackdepot.c
+++ b/lib/stackdepot.c
@@ -322,7 +322,7 @@ static bool depot_init_pool(void **prealloc)
* NULL; do not reset to NULL if we have reached the maximum number of
* pools.
*/
- if (pools_num < stack_max_pools)
+ if (pools_num + 1 < stack_max_pools)
WRITE_ONCE(new_pool, NULL);
else
WRITE_ONCE(new_pool, STACK_DEPOT_POISON);
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 8fa788b0d709..56f2343ffefa 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -2004,6 +2004,7 @@ static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t,
}
if (damos_filter_out(c, t, r, s))
return;
+ sz = damon_sz_region(r);
ktime_get_coarse_ts64(&begin);
trace_damos_before_apply(cidx, sidx, tidx, r,
damon_nr_regions(t), do_trace);
@@ -2145,6 +2146,13 @@ static __kernel_ulong_t damos_get_node_mem_bp(
}
si_meminfo_node(&i, goal->nid);
+ if (!i.totalram || i.totalram < i.freeram) {
+ if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP)
+ return 10000;
+ else /* DAMOS_QUOTA_NODE_MEM_FREE_BP */
+ return 0;
+ }
+
if (goal->metric == DAMOS_QUOTA_NODE_MEM_USED_BP)
numerator = i.totalram - i.freeram;
else /* DAMOS_QUOTA_NODE_MEM_FREE_BP */
diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c
index 1854b4bb6bd2..ddd26a78a9bc 100644
--- a/mm/damon/paddr.c
+++ b/mm/damon/paddr.c
@@ -183,7 +183,7 @@ static unsigned long damon_pa_pageout(struct damon_region *r,
else
list_add(&folio->lru, &folio_list);
put_folio:
- addr += folio_size(folio);
+ addr = PFN_PHYS(folio_pfn(folio)) + folio_size(folio);
folio_put(folio);
}
if (install_young_filter)
@@ -221,7 +221,7 @@ static inline unsigned long damon_pa_mark_accessed_or_deactivate(
folio_deactivate(folio);
applied += folio_nr_pages(folio);
put_folio:
- addr += folio_size(folio);
+ addr = PFN_PHYS(folio_pfn(folio)) + folio_size(folio);
folio_put(folio);
}
s->last_applied = folio;
@@ -271,7 +271,7 @@ static unsigned long damon_pa_migrate(struct damon_region *r,
folio_is_file_lru(folio));
list_add(&folio->lru, &folio_list);
put_folio:
- addr += folio_size(folio);
+ addr = PFN_PHYS(folio_pfn(folio)) + folio_size(folio);
folio_put(folio);
}
applied = damon_migrate_pages(&folio_list, s->target_nid);
@@ -300,7 +300,7 @@ static unsigned long damon_pa_stat(struct damon_region *r,
if (!damos_pa_filter_out(s, folio))
*sz_filter_passed += folio_size(folio) / addr_unit;
- addr += folio_size(folio);
+ addr = PFN_PHYS(folio_pfn(folio)) + folio_size(folio);
folio_put(folio);
}
s->last_applied = folio;
diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c
index 37e0a8bef4b1..e95cddca0a10 100644
--- a/mm/damon/sysfs-schemes.c
+++ b/mm/damon/sysfs-schemes.c
@@ -2484,12 +2484,12 @@ static int damon_sysfs_add_scheme_filters(struct damos *scheme,
return err;
}
} else if (filter->type == DAMOS_FILTER_TYPE_ADDR) {
- if (sysfs_filter->addr_range.end <
- sysfs_filter->addr_range.start) {
+ filter->addr_range = sysfs_filter->addr_range;
+ if (filter->addr_range.end <
+ filter->addr_range.start) {
damos_destroy_filter(filter);
return -EINVAL;
}
- filter->addr_range = sysfs_filter->addr_range;
} else if (filter->type == DAMOS_FILTER_TYPE_TARGET) {
filter->target_idx = sysfs_filter->target_idx;
} else if (filter->type == DAMOS_FILTER_TYPE_HUGEPAGE_SIZE) {
diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
index 3a79d8c89b5b..1ae026d099a2 100644
--- a/mm/damon/sysfs.c
+++ b/mm/damon/sysfs.c
@@ -1199,6 +1199,7 @@ struct damon_sysfs_kdamond {
struct damon_sysfs_contexts *contexts;
struct damon_ctx *damon_ctx;
unsigned int refresh_ms;
+ unsigned long next_refresh_jiffies;
};
static struct damon_sysfs_kdamond *damon_sysfs_kdamond_alloc(void)
@@ -1567,17 +1568,15 @@ static struct damon_ctx *damon_sysfs_build_ctx(
return ctx;
}
-static unsigned long damon_sysfs_next_update_jiffies;
-
static int damon_sysfs_repeat_call_fn(void *data)
{
struct damon_sysfs_kdamond *sysfs_kdamond = data;
if (!sysfs_kdamond->refresh_ms)
return 0;
- if (time_before(jiffies, damon_sysfs_next_update_jiffies))
+ if (time_before(jiffies, sysfs_kdamond->next_refresh_jiffies))
return 0;
- damon_sysfs_next_update_jiffies = jiffies +
+ sysfs_kdamond->next_refresh_jiffies = jiffies +
msecs_to_jiffies(sysfs_kdamond->refresh_ms);
if (!mutex_trylock(&damon_sysfs_lock))
@@ -1645,8 +1644,8 @@ static int damon_sysfs_turn_damon_on(struct damon_sysfs_kdamond *kdamond)
}
kdamond->damon_ctx = ctx;
- damon_sysfs_next_update_jiffies =
- jiffies + msecs_to_jiffies(kdamond->refresh_ms);
+ kdamond->next_refresh_jiffies = jiffies +
+ msecs_to_jiffies(kdamond->refresh_ms);
repeat_call_control->fn = damon_sysfs_repeat_call_fn;
repeat_call_control->data = kdamond;
diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c
index cfa7849041ad..7d4d7565e8ee 100644
--- a/mm/damon/vaddr.c
+++ b/mm/damon/vaddr.c
@@ -925,6 +925,8 @@ static int damos_va_stat_pmd_entry(pmd_t *pmd, unsigned long addr,
return 0;
for (; addr < next; pte += nr, addr += nr * PAGE_SIZE) {
+ unsigned long page_idx;
+
nr = 1;
ptent = ptep_get(pte);
@@ -938,7 +940,8 @@ static int damos_va_stat_pmd_entry(pmd_t *pmd, unsigned long addr,
if (!damos_va_filter_out(s, folio, vma, addr, pte, NULL))
*sz_filter_passed += folio_size(folio);
- nr = folio_nr_pages(folio);
+ page_idx = folio_page_idx(folio, pte_page(ptent));
+ nr = folio_nr_pages(folio) - page_idx;
s->last_applied = folio;
}
pte_unmap_unlock(start_pte, ptl);
diff --git a/mm/execmem.c b/mm/execmem.c
index 084a207e4278..9560441e57ab 100644
--- a/mm/execmem.c
+++ b/mm/execmem.c
@@ -164,9 +164,11 @@ static void execmem_cache_clean(struct work_struct *work)
mutex_lock(mutex);
mas_for_each(&mas, area, ULONG_MAX) {
+ struct vm_struct *vm = find_vm_area(area);
size_t size = mas_range_len(&mas);
- if (IS_ALIGNED(size, PMD_SIZE) &&
+ if (vm && get_vm_area_size(vm) == size &&
+ IS_ALIGNED(size, PMD_SIZE) &&
IS_ALIGNED(mas.index, PMD_SIZE)) {
struct vm_struct *vm = find_vm_area(area);
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index ebc78406335b..42aace6e3ca1 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -2353,6 +2353,11 @@ struct folio *alloc_hugetlb_folio_reserve(struct hstate *h, int preferred_nid,
h->resv_huge_pages--;
spin_unlock_irq(&hugetlb_lock);
+
+ if (folio)
+ lruvec_stat_mod_folio(folio, NR_HUGETLB,
+ folio_nr_pages(folio));
+
return folio;
}
@@ -2360,24 +2365,30 @@ struct folio *alloc_hugetlb_folio_reserve(struct hstate *h, int preferred_nid,
struct folio *alloc_hugetlb_folio_nodemask(struct hstate *h, int preferred_nid,
nodemask_t *nmask, gfp_t gfp_mask, bool allow_alloc_fallback)
{
- spin_lock_irq(&hugetlb_lock);
- if (available_huge_pages(h)) {
- struct folio *folio;
+ struct folio *folio = NULL;
+ spin_lock_irq(&hugetlb_lock);
+ if (available_huge_pages(h))
folio = dequeue_hugetlb_folio_nodemask(h, gfp_mask,
preferred_nid, nmask);
- if (folio) {
- spin_unlock_irq(&hugetlb_lock);
- return folio;
- }
- }
spin_unlock_irq(&hugetlb_lock);
- /* We cannot fallback to other nodes, as we could break the per-node pool. */
- if (!allow_alloc_fallback)
- gfp_mask |= __GFP_THISNODE;
+ if (!folio) {
+ /*
+ * We cannot fallback to other nodes, as we could break the
+ * per-node pool.
+ */
+ if (!allow_alloc_fallback)
+ gfp_mask |= __GFP_THISNODE;
- return alloc_migrate_hugetlb_folio(h, gfp_mask, preferred_nid, nmask);
+ folio = alloc_migrate_hugetlb_folio(h, gfp_mask, preferred_nid,
+ nmask);
+ }
+
+ if (folio)
+ lruvec_stat_mod_folio(folio, NR_HUGETLB, folio_nr_pages(folio));
+
+ return folio;
}
static nodemask_t *policy_mbind_nodemask(gfp_t gfp)
@@ -7427,15 +7438,6 @@ long hugetlb_reserve_pages(struct inode *inode,
out_put_pages:
spool_resv = chg - gbl_reserve;
- if (spool_resv) {
- /* put sub pool's reservation back, chg - gbl_reserve */
- gbl_resv = hugepage_subpool_put_pages(spool, spool_resv);
- /*
- * subpool's reserved pages can not be put back due to race,
- * return to hstate.
- */
- hugetlb_acct_memory(h, -gbl_resv);
- }
/* Restore used_hpages for pages that failed global reservation */
if (gbl_reserve && spool) {
unsigned long flags;
@@ -7445,6 +7447,15 @@ long hugetlb_reserve_pages(struct inode *inode,
spool->used_hpages -= gbl_reserve;
unlock_or_release_subpool(spool, flags);
}
+ if (spool_resv) {
+ /* put sub pool's reservation back, chg - gbl_reserve */
+ gbl_resv = hugepage_subpool_put_pages(spool, spool_resv);
+ /*
+ * subpool's reserved pages can not be put back due to race,
+ * return to hstate.
+ */
+ hugetlb_acct_memory(h, -gbl_resv);
+ }
out_uncharge_cgroup:
hugetlb_cgroup_uncharge_cgroup_rsvd(hstate_index(h),
chg * pages_per_huge_page(h), h_cg);
@@ -8054,6 +8065,15 @@ void hugetlb_unshare_all_pmds(struct vm_area_struct *vma)
*/
void fixup_hugetlb_reservations(struct vm_area_struct *vma)
{
- if (is_vm_hugetlb_page(vma))
- clear_vma_resv_huge_pages(vma);
+ if (!is_vm_hugetlb_page(vma))
+ return;
+
+ /*
+ * Rmap walks might still find this VMA and take the lock
+ * inside the reservation map for __vma_private_lock(). Don't clear
+ * the reservation map while that is possible.
+ */
+ i_mmap_lock_write(vma->vm_file->f_mapping);
+ clear_vma_resv_huge_pages(vma);
+ i_mmap_unlock_write(vma->vm_file->f_mapping);
}
diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c
index b6537a3e4f62..7c59c20d6857 100644
--- a/mm/hugetlb_cgroup.c
+++ b/mm/hugetlb_cgroup.c
@@ -187,6 +187,34 @@ static void hugetlb_cgroup_css_free(struct cgroup_subsys_state *css)
hugetlb_cgroup_free(hugetlb_cgroup_from_css(css));
}
+static void hugetlb_cgroup_move_usage(struct hugetlb_cgroup *from,
+ struct hugetlb_cgroup *to,
+ struct folio *from_folio,
+ struct folio *to_folio)
+{
+ int idx = hstate_index(folio_hstate(from_folio));
+ unsigned long nr_pages = folio_nr_pages(from_folio);
+ int from_nid = folio_nid(from_folio);
+ int to_nid = folio_nid(to_folio);
+ unsigned long usage;
+
+ lockdep_assert_held(&hugetlb_lock);
+
+ if (!from || !to)
+ return;
+
+ if (from == to && from_nid == to_nid)
+ return;
+
+ usage = from->nodeinfo[from_nid]->usage[idx];
+ if (WARN_ON_ONCE(usage < nr_pages))
+ return;
+ WRITE_ONCE(from->nodeinfo[from_nid]->usage[idx], usage - nr_pages);
+
+ usage = to->nodeinfo[to_nid]->usage[idx];
+ WRITE_ONCE(to->nodeinfo[to_nid]->usage[idx], usage + nr_pages);
+}
+
/*
* Should be called with hugetlb_lock held.
* Since we are holding hugetlb_lock, pages cannot get moved from
@@ -221,6 +249,8 @@ static void hugetlb_cgroup_move_parent(int idx, struct hugetlb_cgroup *h_cg,
/* Take the pages off the local counter */
page_counter_cancel(counter, nr_pages);
+ hugetlb_cgroup_move_usage(h_cg, parent, folio, folio);
+
set_hugetlb_cgroup(folio, parent);
out:
return;
@@ -914,6 +944,9 @@ void hugetlb_cgroup_migrate(struct folio *old_folio, struct folio *new_folio)
/* move the h_cg details to new cgroup */
set_hugetlb_cgroup(new_folio, h_cg);
set_hugetlb_cgroup_rsvd(new_folio, h_cg_rsvd);
+
+ hugetlb_cgroup_move_usage(h_cg, h_cg, old_folio, new_folio);
+
list_move(&new_folio->lru, &h->hugepage_activelist);
spin_unlock_irq(&hugetlb_lock);
}
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index e2ac26c05064..cc3a09a0c259 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -2308,6 +2308,7 @@ static int hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr,
int present, swap;
int node = NUMA_NO_NODE;
int result = SCAN_SUCCEED;
+ unsigned long failed_pfn = -1;
present = 0;
swap = 0;
@@ -2344,6 +2345,7 @@ static int hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr,
folio->index == start) {
/* Maybe PMD-mapped */
result = SCAN_PTE_MAPPED_HUGEPAGE;
+ failed_pfn = folio_pfn(folio);
/*
* For SCAN_PTE_MAPPED_HUGEPAGE, further processing
* by the caller won't touch the page cache, and so
@@ -2357,6 +2359,7 @@ static int hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr,
node = folio_nid(folio);
if (hpage_collapse_scan_abort(node, cc)) {
result = SCAN_SCAN_ABORT;
+ failed_pfn = folio_pfn(folio);
folio_put(folio);
break;
}
@@ -2364,12 +2367,14 @@ static int hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr,
if (!folio_test_lru(folio)) {
result = SCAN_PAGE_LRU;
+ failed_pfn = folio_pfn(folio);
folio_put(folio);
break;
}
if (folio_expected_ref_count(folio) + 1 != folio_ref_count(folio)) {
result = SCAN_PAGE_COUNT;
+ failed_pfn = folio_pfn(folio);
folio_put(folio);
break;
}
@@ -2401,7 +2406,7 @@ static int hpage_collapse_scan_file(struct mm_struct *mm, unsigned long addr,
}
}
- trace_mm_khugepaged_scan_file(mm, folio, file, present, swap, result);
+ trace_mm_khugepaged_scan_file(mm, failed_pfn, file, present, swap, result);
return result;
}
diff --git a/mm/ksm.c b/mm/ksm.c
index 4f672f4f2140..80274ace8155 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -1065,7 +1065,8 @@ static inline void folio_set_stable_node(struct folio *folio,
struct ksm_stable_node *stable_node)
{
VM_WARN_ON_FOLIO(folio_test_anon(folio) && PageAnonExclusive(&folio->page), folio);
- folio->mapping = (void *)((unsigned long)stable_node | FOLIO_MAPPING_KSM);
+ WRITE_ONCE(folio->mapping,
+ (void *)((unsigned long)stable_node | FOLIO_MAPPING_KSM));
}
#ifdef CONFIG_SYSFS
@@ -3248,7 +3249,7 @@ void folio_migrate_ksm(struct folio *newfolio, struct folio *folio)
stable_node = folio_stable_node(folio);
if (stable_node) {
VM_BUG_ON_FOLIO(stable_node->kpfn != folio_pfn(folio), folio);
- stable_node->kpfn = folio_pfn(newfolio);
+ WRITE_ONCE(stable_node->kpfn, folio_pfn(newfolio));
/*
* newfolio->mapping was set in advance; now we need smp_wmb()
* to make sure that the new stable_node->kpfn is visible
diff --git a/mm/madvise.c b/mm/madvise.c
index b8c25fea4fab..729d8f2f07fd 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -415,9 +415,10 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,
if (next - addr != HPAGE_PMD_SIZE) {
int err;
+ if (!folio_trylock(folio))
+ goto huge_unlock;
folio_get(folio);
spin_unlock(ptl);
- folio_lock(folio);
err = split_folio(folio);
folio_unlock(folio);
folio_put(folio);
@@ -427,10 +428,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,
}
if (!pageout && pmd_young(orig_pmd)) {
- pmdp_invalidate(vma, addr, pmd);
- orig_pmd = pmd_mkold(orig_pmd);
-
- set_pmd_at(mm, addr, pmd, orig_pmd);
+ pmdp_test_and_clear_young(vma, addr, pmd);
tlb_remove_pmd_tlb_entry(tlb, pmd, addr);
}
@@ -451,6 +449,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,
spin_unlock(ptl);
if (pageout)
reclaim_pages(&folio_list);
+ cond_resched();
return 0;
}
@@ -460,7 +459,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,
restart:
start_pte = pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
if (!start_pte)
- return 0;
+ goto out;
flush_tlb_batched_pending(mm);
arch_enter_lazy_mmu_mode();
for (; addr < end; pte += nr, addr += nr * PAGE_SIZE) {
@@ -564,6 +563,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,
folio_deactivate(folio);
}
+out:
if (start_pte) {
arch_leave_lazy_mmu_mode();
pte_unmap_unlock(start_pte, ptl);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 82ad23d689c7..2aa741316b49 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2311,10 +2311,11 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
bool raised_max_event = false;
unsigned long pflags;
bool allow_spinning = gfpflags_allow_spinning(gfp_mask);
+ int ret = 0;
retry:
if (consume_stock(memcg, nr_pages))
- return 0;
+ return ret;
if (!allow_spinning)
/* Avoid the refill and flush of the older stock */
@@ -2424,16 +2425,11 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
* put the burden of reclaim on regular allocation requests
* and let these go through as privileged allocations.
*/
- if (!(gfp_mask & (__GFP_NOFAIL | __GFP_HIGH)))
- return -ENOMEM;
+ if (!(gfp_mask & (__GFP_NOFAIL | __GFP_HIGH))) {
+ ret = -ENOMEM;
+ goto out;
+ }
force:
- /*
- * If the allocation has to be enforced, don't forget to raise
- * a MEMCG_MAX event.
- */
- if (!raised_max_event)
- __memcg_memory_event(mem_over_limit, MEMCG_MAX, allow_spinning);
-
/*
* The allocation either can't fail or will lead to more memory
* being freed very soon. Allow memory usage go over the limit
@@ -2443,7 +2439,15 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
if (do_memsw_account())
page_counter_charge(&memcg->memsw, nr_pages);
- return 0;
+out:
+ /*
+ * Don't forget to raise a MEMCG_MAX event for forced or rejected
+ * requests.
+ */
+ if (!raised_max_event)
+ __memcg_memory_event(mem_over_limit, MEMCG_MAX, allow_spinning);
+
+ return ret;
done_restock:
if (batch > nr_pages)
@@ -2502,7 +2506,7 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
!(current->flags & PF_MEMALLOC) &&
gfpflags_allow_blocking(gfp_mask))
__mem_cgroup_handle_over_high(gfp_mask);
- return 0;
+ return ret;
}
static inline int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask,
diff --git a/mm/memory.c b/mm/memory.c
index 72793ec63107..01d14ca0e74d 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -6808,14 +6808,34 @@ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
resource_size_t phys_addr;
pgprot_t prot = __pgprot(0);
void __iomem *maddr;
+ struct page *page;
int offset = offset_in_page(addr);
int ret = -EINVAL;
bool writable;
struct follow_pfnmap_args args = { .vma = vma, .address = addr };
+ /*
+ * Limit access to one page at a time, as that's what follow_pfnmap_start()
+ * guarantees; expect the caller to retry to read larger ranges.
+ */
+ len = min_t(int, len, PAGE_SIZE - offset);
+
retry:
if (follow_pfnmap_start(&args))
return -EINVAL;
+
+ /*
+ * Reject anything that looks like ordinary RAM, including anonymous
+ * folios in CoW mappings or allocated kernel memory. Note that we
+ * cannot distinguish memblock allocations from non-RAM reliably, so
+ * we cannot easily reject them here.
+ */
+ page = pfn_to_online_page(args.pfn);
+ if (page && !PageReserved(page)) {
+ follow_pfnmap_end(&args);
+ return -EINVAL;
+ }
+
prot = args.pgprot;
phys_addr = (resource_size_t)args.pfn << PAGE_SHIFT;
writable = args.writable;
@@ -6824,7 +6844,7 @@ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
if ((write & FOLL_WRITE) && !writable)
return -EINVAL;
- maddr = ioremap_prot(phys_addr, PAGE_ALIGN(len + offset), prot);
+ maddr = ioremap_prot(phys_addr, PAGE_SIZE, prot);
if (!maddr)
return -ENOMEM;
@@ -6832,7 +6852,7 @@ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
goto out_unmap;
if ((pgprot_val(prot) != pgprot_val(args.pgprot)) ||
- (phys_addr != (args.pfn << PAGE_SHIFT)) ||
+ (phys_addr != ((resource_size_t)args.pfn << PAGE_SHIFT)) ||
(writable != args.writable)) {
follow_pfnmap_end(&args);
iounmap(maddr);
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 2a84cecf5435..919a505cabae 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2206,7 +2206,7 @@ static nodemask_t *policy_nodemask(gfp_t gfp, struct mempolicy *pol,
break;
case MPOL_BIND:
/* Restrict to nodemask (but not on lower zones) */
- if (apply_policy_zone(pol, gfp_zone(gfp)) &&
+ if (apply_policy_zone(pol, gfp_zone(current_gfp_context(gfp))) &&
cpuset_nodemask_valid_mems_allowed(&pol->nodes))
nodemask = &pol->nodes;
if (pol->home_node != NUMA_NO_NODE)
@@ -2519,6 +2519,7 @@ static unsigned long alloc_pages_bulk_interleave(gfp_t gfp,
struct mempolicy *pol, unsigned long nr_pages,
struct page **page_array)
{
+ unsigned int cpuset_mems_cookie;
int nodes;
unsigned long nr_pages_per_node;
int delta;
@@ -2526,7 +2527,16 @@ static unsigned long alloc_pages_bulk_interleave(gfp_t gfp,
unsigned long nr_allocated;
unsigned long total_allocated = 0;
- nodes = nodes_weight(pol->nodes);
+ /* count the nodes, retry if a rebind happened during the read */
+ do {
+ cpuset_mems_cookie = read_mems_allowed_begin();
+ nodes = nodes_weight(pol->nodes);
+ } while (read_mems_allowed_retry(cpuset_mems_cookie));
+
+ /* if the nodemask has become invalid, we cannot do anything */
+ if (!nodes)
+ return 0;
+
nr_pages_per_node = nr_pages / nodes;
delta = nr_pages - nodes * nr_pages_per_node;
diff --git a/mm/mlock.c b/mm/mlock.c
index 31045f17766a..20061dd28150 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -468,7 +468,7 @@ static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_struct *vma,
unsigned long end, vm_flags_t newflags)
{
struct mm_struct *mm = vma->vm_mm;
- int nr_pages;
+ long nr_pages;
int ret = 0;
vm_flags_t oldflags = vma->vm_flags;
diff --git a/mm/mremap.c b/mm/mremap.c
index f0e63d3b3c14..8ed7c50e499b 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -874,12 +874,6 @@ unsigned long move_page_tables(struct pagetable_move_control *pmc)
return pmc_progress(pmc);
}
-/* Set vrm->delta to the difference in VMA size specified by user. */
-static void vrm_set_delta(struct vma_remap_struct *vrm)
-{
- vrm->delta = abs_diff(vrm->old_len, vrm->new_len);
-}
-
/* Determine what kind of remap this is - shrink, expand or no resize at all. */
static enum mremap_type vrm_remap_type(struct vma_remap_struct *vrm)
{
@@ -892,6 +886,13 @@ static enum mremap_type vrm_remap_type(struct vma_remap_struct *vrm)
return MREMAP_EXPAND;
}
+/* Populate calculated fields. */
+static void vrm_populate_calculated(struct vma_remap_struct *vrm)
+{
+ vrm->delta = abs_diff(vrm->old_len, vrm->new_len);
+ vrm->remap_type = vrm_remap_type(vrm);
+}
+
/*
* When moving a VMA to vrm->new_adr, does this result in the new and old VMAs
* overlapping?
@@ -1238,6 +1239,20 @@ static int copy_vma_and_data(struct vma_remap_struct *vrm,
pmc_revert.need_rmap_locks = true;
move_page_tables(&pmc_revert);
+ /*
+ * When copy_vma_and_data() returns, the new VMA will be
+ * unmapped rather than the old one.
+ *
+ * When an accountable move is being performed, this unmapping
+ * will not uncharge any expansion, so do that now.
+ *
+ * A move is accountable if the VMA is accountable and
+ * MREMAP_DONTUNMAP is not specified, so only do so if the
+ * latter is true.
+ */
+ if (!(vrm->flags & MREMAP_DONTUNMAP))
+ vrm_uncharge(vrm);
+
vrm->vma = new_vma;
vrm->old_len = vrm->new_len;
vrm->addr = vrm->new_addr;
@@ -1245,7 +1260,7 @@ static int copy_vma_and_data(struct vma_remap_struct *vrm,
mremap_userfaultfd_prep(new_vma, vrm->uf);
}
- fixup_hugetlb_reservations(vma);
+ fixup_hugetlb_reservations(vrm->vma);
*new_vma_ptr = new_vma;
return err;
@@ -1422,6 +1437,7 @@ static unsigned long mremap_to(struct vma_remap_struct *vrm)
/* Set up for the move now shrink has been executed. */
vrm->old_len = vrm->new_len;
+ vrm_populate_calculated(vrm);
}
/* MREMAP_DONTUNMAP expands by old_len since old_len == new_len */
@@ -1694,8 +1710,7 @@ static int check_prep_vma(struct vma_remap_struct *vrm)
if (is_vm_hugetlb_page(vma) && !align_hugetlb(vrm))
return -EINVAL;
- vrm_set_delta(vrm);
- vrm->remap_type = vrm_remap_type(vrm);
+ vrm_populate_calculated(vrm);
/* For convenience, we set new_addr even if VMA won't move. */
if (!vrm_implies_new_addr(vrm))
vrm->new_addr = addr;
diff --git a/mm/nommu.c b/mm/nommu.c
index c3a23b082adb..d609ecdcf022 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1644,6 +1644,9 @@ static int __access_remote_vm(struct mm_struct *mm, unsigned long addr,
struct vm_area_struct *vma;
int write = gup_flags & FOLL_WRITE;
+ if (addr + len < addr)
+ return 0;
+
if (mmap_read_lock_killable(mm))
return 0;
@@ -1697,9 +1700,6 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in
{
struct mm_struct *mm;
- if (addr + len < addr)
- return 0;
-
mm = get_task_mm(tsk);
if (!mm)
return 0;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index ff863a87f2b8..8bdc35efad26 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -38,6 +38,7 @@
#include <linux/vmstat.h>
#include <linux/fault-inject.h>
#include <linux/compaction.h>
+#include <linux/crash_dump.h>
#include <trace/events/kmem.h>
#include <trace/events/oom.h>
#include <linux/prefetch.h>
@@ -1026,10 +1027,12 @@ static inline void __free_one_page(struct page *page,
* Our buddy is free or it is CONFIG_DEBUG_PAGEALLOC guard page,
* merge with it and move up one order.
*/
- if (page_is_guard(buddy))
+ if (page_is_guard(buddy)) {
clear_page_guard(zone, buddy, order);
- else
+ account_freepages(zone, 1 << order, migratetype);
+ } else {
__del_page_from_free_list(buddy, zone, order, buddy_mt);
+ }
if (unlikely(buddy_mt != migratetype)) {
/*
@@ -2206,10 +2209,17 @@ static inline bool boost_watermark(struct zone *zone)
if (!watermark_boost_factor)
return false;
+
+ /*
+ * A kdump capture kernel exits before a boost can pay off, while
+ * the raised watermark can exceed the memory left for the dump.
+ */
+ if (is_kdump_kernel())
+ return false;
+
/*
* Don't bother in zones that are unlikely to produce results.
- * On small machines, including kdump capture kernels running
- * in a small area, boosting the watermark can cause an out of
+ * On small machines, boosting the watermark can cause an out of
* memory situation immediately.
*/
if ((pageblock_nr_pages * 4) > zone_managed_pages(zone))
diff --git a/mm/page_isolation.c b/mm/page_isolation.c
index f72b6cd38b95..2e366e7bd5e9 100644
--- a/mm/page_isolation.c
+++ b/mm/page_isolation.c
@@ -380,13 +380,19 @@ static int isolate_single_pageblock(unsigned long boundary_pfn,
}
if (PageBuddy(page)) {
- int order = buddy_order(page);
+ unsigned int order = buddy_order_unsafe(page);
- /* pageblock_isolate_and_move_free_pages() handled this */
- VM_WARN_ON_ONCE(pfn + (1 << order) > boundary_pfn);
-
- pfn += 1UL << order;
- continue;
+ /* buddy_order_unsafe() is racy. Validate the order before shifting. */
+ if (order <= MAX_PAGE_ORDER &&
+ /*
+ * pageblock_isolate_and_move_free_pages() splits
+ * cross-boundary PageBuddy, verify it.
+ */
+ pfn + (1UL << order) <= boundary_pfn) {
+ pfn += 1UL << order;
+ continue;
+ }
+ goto failed;
}
/*
@@ -405,10 +411,28 @@ static int isolate_single_pageblock(unsigned long boundary_pfn,
if (PageCompound(page)) {
struct page *head = compound_head(page);
unsigned long head_pfn = page_to_pfn(head);
- unsigned long nr_pages = compound_nr(head);
+ unsigned int order = compound_order(head);
+ unsigned long nr_pages;
+
+ /* compound_order() is racy. Cap it at MAX_FOLIO_ORDER. */
+ if (order > MAX_FOLIO_ORDER)
+ goto failed;
+
+ nr_pages = 1UL << order;
+
+ /*
+ * compound_head() is also racy, so the derived head_pfn
+ * needs additional checks to make sure it is valid.
+ * Otherwise, just fail the check. pfn comes from
+ * __first_valid_page() as a legitimate PFN, so use it to
+ * check head_pfn.
+ */
+ if (head_pfn > pfn || !IS_ALIGNED(head_pfn, nr_pages) ||
+ pfn - head_pfn >= nr_pages)
+ goto failed;
if (head_pfn + nr_pages <= boundary_pfn ||
- PageHuge(page)) {
+ PageHuge(head)) {
pfn = head_pfn + nr_pages;
continue;
}
diff --git a/mm/page_table_check.c b/mm/page_table_check.c
index 91c4dacc41fb..fa2c12e76a9e 100644
--- a/mm/page_table_check.c
+++ b/mm/page_table_check.c
@@ -67,7 +67,7 @@ static void page_table_check_clear(unsigned long pfn, unsigned long pgcnt)
struct page *page;
bool anon;
- if (!pfn_valid(pfn))
+ if (!pfn_valid(pfn) || is_zero_pfn(pfn) || is_huge_zero_pfn(pfn))
return;
page = pfn_to_page(pfn);
@@ -102,7 +102,7 @@ static void page_table_check_set(unsigned long pfn, unsigned long pgcnt,
struct page *page;
bool anon;
- if (!pfn_valid(pfn))
+ if (!pfn_valid(pfn) || is_zero_pfn(pfn) || is_huge_zero_pfn(pfn))
return;
page = pfn_to_page(pfn);
diff --git a/mm/percpu.c b/mm/percpu.c
index 81462ce5866e..32519523845b 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -1692,14 +1692,15 @@ static void pcpu_alloc_tag_alloc_hook(struct pcpu_chunk *chunk, int off,
{
if (mem_alloc_profiling_enabled() && likely(chunk->obj_exts)) {
alloc_tag_add(&chunk->obj_exts[off >> PCPU_MIN_ALLOC_SHIFT].tag,
- current->alloc_tag, size);
+ current->alloc_tag, pcpu_obj_full_size(size));
}
}
static void pcpu_alloc_tag_free_hook(struct pcpu_chunk *chunk, int off, size_t size)
{
if (mem_alloc_profiling_enabled() && likely(chunk->obj_exts))
- alloc_tag_sub(&chunk->obj_exts[off >> PCPU_MIN_ALLOC_SHIFT].tag, size);
+ alloc_tag_sub(&chunk->obj_exts[off >> PCPU_MIN_ALLOC_SHIFT].tag,
+ pcpu_obj_full_size(size));
}
#else
static void pcpu_alloc_tag_alloc_hook(struct pcpu_chunk *chunk, int off,
diff --git a/mm/shmem.c b/mm/shmem.c
index 0100110f77b8..68a3a0734e3d 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1129,7 +1129,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
}
folio_batch_remove_exceptionals(&fbatch);
folio_batch_release(&fbatch);
- cond_resched();
+ cond_resched_tasks_rcu_qs();
}
/*
@@ -1170,7 +1170,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
index = start;
while (index < end) {
- cond_resched();
+ cond_resched_tasks_rcu_qs();
if (!find_get_entries(mapping, &index, end - 1, &fbatch,
indices)) {
@@ -4595,6 +4595,9 @@ static int shmem_parse_opt_casefold(struct fs_context *fc, struct fs_parameter *
struct unicode_map *encoding;
char *version_str = param->string + 5;
+ if (ctx->encoding)
+ return invalfc(fc, "casefold parameter cannot be specified twice\n");
+
if (!latest_version) {
if (strncmp(param->string, "utf8-", 5))
return invalfc(fc, "Only UTF-8 encodings are supported "
@@ -5081,6 +5084,7 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
if (ctx->encoding) {
sb->s_encoding = ctx->encoding;
+ ctx->encoding = NULL;
set_default_d_op(sb, &shmem_ci_dentry_ops);
if (ctx->strict_encoding)
sb->s_encoding_flags = SB_ENC_STRICT_MODE_FL;
@@ -5177,6 +5181,9 @@ static void shmem_free_fc(struct fs_context *fc)
struct shmem_options *ctx = fc->fs_private;
if (ctx) {
+#if IS_ENABLED(CONFIG_UNICODE)
+ utf8_unload(ctx->encoding);
+#endif
mpol_put(ctx->mpol);
kfree(ctx);
}
diff --git a/mm/swap_state.c b/mm/swap_state.c
index b8d7744b4436..57cae6f6eac1 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -86,17 +86,27 @@ void show_swap_cache_info(void)
*/
struct folio *swap_cache_get_folio(swp_entry_t entry)
{
+ struct swap_cluster_info *ci = __swap_entry_to_cluster(entry);
+ unsigned int ci_off = swp_cluster_offset(entry);
unsigned long swp_tb;
struct folio *folio;
for (;;) {
- swp_tb = swap_table_get(__swap_entry_to_cluster(entry),
- swp_cluster_offset(entry));
+ swp_tb = swap_table_get(ci, ci_off);
if (!swp_tb_is_folio(swp_tb))
return NULL;
folio = swp_tb_to_folio(swp_tb);
- if (likely(folio_try_get(folio)))
+ if (!folio_try_get(folio))
+ continue;
+ /*
+ * The folio may have been freed and its memory reused before
+ * we got the reference. Make sure it is still in the slot.
+ */
+ swp_tb = swap_table_get(ci, ci_off);
+ if (likely(swp_tb_is_folio(swp_tb) &&
+ swp_tb_to_folio(swp_tb) == folio))
return folio;
+ folio_put(folio);
}
return NULL;
diff --git a/mm/truncate.c b/mm/truncate.c
index 4bf64fd610c9..3534bc320a9c 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -259,30 +259,32 @@ bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end)
* for shmem truncate
*/
struct folio *folio2;
+ pgoff_t end_idx;
if (offset + length == size)
goto no_split;
- split_at2 = folio_page(folio,
- PAGE_ALIGN_DOWN(offset + length) / PAGE_SIZE);
- folio2 = page_folio(split_at2);
-
- if (!folio_try_get(folio2))
+ /*
+ * After the first split at the start edge, the folio at the
+ * end edge may be freed and reused concurrently.
+ * __filemap_get_folio() looks up the straddler at end_idx
+ * and returns it locked and ref'd with the mapping
+ * validated.
+ */
+ end_idx = (pos + offset + length) >> PAGE_SHIFT;
+ folio2 = __filemap_get_folio(folio->mapping, end_idx,
+ FGP_LOCK | FGP_NOWAIT, 0);
+ if (IS_ERR(folio2))
goto no_split;
+ /* make sure folio2 is large */
if (!folio_test_large(folio2))
goto out;
- if (!folio_trylock(folio2))
- goto out;
-
- /* make sure folio2 is large and does not change its mapping */
- if (folio_test_large(folio2) &&
- folio2->mapping == folio->mapping)
- try_folio_split_or_unmap(folio2, split_at2, min_order);
-
- folio_unlock(folio2);
+ split_at2 = folio_page(folio2, (end_idx - folio2->index));
+ try_folio_split_or_unmap(folio2, split_at2, min_order);
out:
+ folio_unlock(folio2);
folio_put(folio2);
no_split:
return true;
@@ -878,8 +880,7 @@ void pagecache_isize_extended(struct inode *inode, loff_t from, loff_t to)
unsigned int offset, end;
offset = from - folio_pos(folio);
- end = min_t(unsigned int, to - folio_pos(folio),
- folio_size(folio));
+ end = min_t(loff_t, to - folio_pos(folio), folio_size(folio));
folio_zero_segment(folio, offset, end);
}
diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
index 34798ce783e6..579445bddf56 100644
--- a/mm/userfaultfd.c
+++ b/mm/userfaultfd.c
@@ -1414,8 +1414,12 @@ static long move_pages_ptes(struct mm_struct *mm, pmd_t *dst_pmd, pmd_t *src_pmd
pte_unmap(dst_pte);
src_pte = dst_pte = NULL;
ret = split_folio(src_folio);
- if (ret)
+ if (ret) {
+ /* do not retry, extra refs may be held forever */
+ if (ret == -EAGAIN)
+ ret = -EBUSY;
goto out;
+ }
/* have to reacquire the folio after it got split */
folio_unlock(src_folio);
folio_put(src_folio);
diff --git a/mm/vmscan.c b/mm/vmscan.c
index c983815bde5b..e72d50bab730 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -5082,7 +5082,7 @@ static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *
sc->nr_reclaimed = 0;
if (mem_cgroup_disabled())
- shrink_one(&pgdat->__lruvec, sc);
+ shrink_one(mem_cgroup_lruvec(NULL, pgdat), sc);
else
shrink_many(pgdat, sc);
@@ -7642,7 +7642,8 @@ static unsigned long __node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask,
sc->gfp_mask);
cond_resched();
- psi_memstall_enter(&pflags);
+ if (!sc->proactive)
+ psi_memstall_enter(&pflags);
delayacct_freepages_start();
fs_reclaim_acquire(sc->gfp_mask);
/*
@@ -7666,7 +7667,8 @@ static unsigned long __node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask,
memalloc_noreclaim_restore(noreclaim_flag);
fs_reclaim_release(sc->gfp_mask);
delayacct_freepages_end();
- psi_memstall_leave(&pflags);
+ if (!sc->proactive)
+ psi_memstall_leave(&pflags);
trace_mm_vmscan_node_reclaim_end(sc->nr_reclaimed);
@@ -7823,7 +7825,7 @@ int user_proactive_reclaim(char *buf,
} else {
struct scan_control sc = {
.gfp_mask = current_gfp_context(gfp_mask),
- .reclaim_idx = gfp_zone(gfp_mask),
+ .reclaim_idx = MAX_NR_ZONES - 1,
.proactive_swappiness = swappiness == -1 ? NULL : &swappiness,
.priority = DEF_PRIORITY,
.may_writepage = !laptop_mode,
diff --git a/mm/zswap.c b/mm/zswap.c
index 18249dd96ef8..d2e5f0cff71d 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -288,6 +288,8 @@ static struct zswap_pool *zswap_pool_create(char *compressor)
zswap_pool_debug("created", pool);
+ static_branch_enable(&zswap_ever_enabled);
+
return pool;
ref_fail:
@@ -1818,9 +1820,8 @@ static int zswap_setup(void)
pool = __zswap_pool_create_fallback();
if (pool) {
pr_info("loaded using pool %s\n", pool->tfm_name);
- list_add(&pool->list, &zswap_pools);
+ list_add_rcu(&pool->list, &zswap_pools);
zswap_has_pool = true;
- static_branch_enable(&zswap_ever_enabled);
} else {
pr_err("pool creation failed\n");
zswap_enabled = false;
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 3733f408d2e4..0660d306cf4f 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -1207,6 +1207,7 @@ static int lowpan_control_open(struct inode *inode, struct file *file)
}
static const struct file_operations lowpan_control_fops = {
+ .owner = THIS_MODULE,
.open = lowpan_control_open,
.read = seq_read,
.write = lowpan_control_write,
diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c
index 9ae6d9224c3b..807c9e0e212c 100644
--- a/net/bluetooth/hci_debugfs.c
+++ b/net/bluetooth/hci_debugfs.c
@@ -68,6 +68,7 @@ static ssize_t __name ## _write(struct file *file, \
} \
\
static const struct file_operations __name ## _fops = { \
+ .owner = THIS_MODULE, \
.open = simple_open, \
.read = __name ## _read, \
.write = __name ## _write, \
@@ -286,6 +287,7 @@ static ssize_t use_debug_keys_read(struct file *file, char __user *user_buf,
}
static const struct file_operations use_debug_keys_fops = {
+ .owner = THIS_MODULE,
.open = simple_open,
.read = use_debug_keys_read,
.llseek = default_llseek,
@@ -304,6 +306,7 @@ static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
}
static const struct file_operations sc_only_mode_fops = {
+ .owner = THIS_MODULE,
.open = simple_open,
.read = sc_only_mode_read,
.llseek = default_llseek,
@@ -440,6 +443,7 @@ static ssize_t ssp_debug_mode_read(struct file *file, char __user *user_buf,
}
static const struct file_operations ssp_debug_mode_fops = {
+ .owner = THIS_MODULE,
.open = simple_open,
.read = ssp_debug_mode_read,
.llseek = default_llseek,
@@ -532,6 +536,7 @@ static ssize_t force_bredr_smp_write(struct file *file,
}
static const struct file_operations force_bredr_smp_fops = {
+ .owner = THIS_MODULE,
.open = simple_open,
.read = force_bredr_smp_read,
.write = force_bredr_smp_write,
@@ -781,6 +786,7 @@ static ssize_t force_static_address_write(struct file *file,
}
static const struct file_operations force_static_address_fops = {
+ .owner = THIS_MODULE,
.open = simple_open,
.read = force_static_address_read,
.write = force_static_address_write,
@@ -1184,6 +1190,7 @@ static ssize_t force_no_mitm_write(struct file *file,
}
static const struct file_operations force_no_mitm_fops = {
+ .owner = THIS_MODULE,
.open = simple_open,
.read = force_no_mitm_read,
.write = force_no_mitm_write,
@@ -1322,6 +1329,7 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
}
static const struct file_operations dut_mode_fops = {
+ .owner = THIS_MODULE,
.open = simple_open,
.read = dut_mode_read,
.write = dut_mode_write,
@@ -1378,6 +1386,7 @@ static ssize_t vendor_diag_write(struct file *file, const char __user *user_buf,
}
static const struct file_operations vendor_diag_fops = {
+ .owner = THIS_MODULE,
.open = simple_open,
.read = vendor_diag_read,
.write = vendor_diag_write,
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 462667a6e6ac..0ba7b1ea435c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -323,6 +323,10 @@ static int l2cap_seq_list_init(struct l2cap_seq_list *seq_list, u16 size)
{
size_t alloc_size, i;
+ /* roundup_pow_of_two(0) is undefined */
+ if (!size)
+ return -EINVAL;
+
/* Allocated size is a power of 2 to map sequence numbers
* (which may be up to 14 bits) in to a smaller array that is
* sized for the negotiated ERTM transmit windows.
@@ -3648,18 +3652,33 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
break;
case L2CAP_MODE_ERTM:
- if (!test_bit(CONF_EWS_RECV, &chan->conf_state))
- chan->remote_tx_win = rfc.txwin_size;
- else
- rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
-
- chan->remote_max_tx = rfc.max_transmit;
-
size = min_t(u16, le16_to_cpu(rfc.max_pdu_size),
chan->conn->mtu - L2CAP_EXT_HDR_SIZE -
L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE);
rfc.max_pdu_size = cpu_to_le16(size);
- chan->remote_mps = size;
+
+ /* The TxWindow must be within 1 to 63, otherwise
+ * propose the default one and keep the parameters of
+ * an earlier request. This overrides a PENDING result
+ * from the EFS check above, so the request is no
+ * longer pending, nor done if an earlier one was.
+ */
+ if (!rfc.txwin_size ||
+ rfc.txwin_size > L2CAP_DEFAULT_TX_WINDOW) {
+ result = L2CAP_CONF_UNACCEPT;
+ clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
+ clear_bit(CONF_OUTPUT_DONE, &chan->conf_state);
+ rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
+ remote_efs = 0;
+ } else {
+ if (!test_bit(CONF_EWS_RECV, &chan->conf_state))
+ chan->remote_tx_win = rfc.txwin_size;
+ else
+ rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
+
+ chan->remote_max_tx = rfc.max_transmit;
+ chan->remote_mps = size;
+ }
__l2cap_set_ertm_timeouts(chan, &rfc);
@@ -3801,7 +3820,11 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
}
}
- if (chan->mode == L2CAP_MODE_BASIC && chan->mode != rfc.mode)
+ /* A connected channel cannot change its mode, as the new mode would
+ * not be initialized.
+ */
+ if (chan->mode != rfc.mode &&
+ (chan->mode == L2CAP_MODE_BASIC || chan->state == BT_CONNECTED))
return -ECONNREFUSED;
chan->mode = rfc.mode;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index bb383ee188bb..b85c6a11334e 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -805,6 +805,10 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
break;
}
+ /* A zero TxWindow is invalid, use the default instead */
+ if (!opts.txwin_size)
+ opts.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
+
if (!l2cap_valid_mtu(chan, opts.imtu)) {
err = -EINVAL;
break;
diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c
index 614d2bba9fd6..a406fff75ba0 100644
--- a/net/ceph/mon_client.c
+++ b/net/ceph/mon_client.c
@@ -1141,6 +1141,9 @@ static int build_initial_monmap(struct ceph_mon_client *monc)
int num_mon = opt->num_mon;
int i;
+ if (num_mon < 1 || num_mon > CEPH_MAX_MON)
+ return -EINVAL;
+
/* build initial monmap */
monc->monmap = kzalloc(struct_size(monc->monmap, mon_inst, num_mon),
GFP_KERNEL);
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 46b2c2ae7778..0b37fb3c6264 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -1108,7 +1108,9 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc,
req->r_base_oloc.pool = layout->pool_id;
req->r_base_oloc.pool_ns = ceph_try_get_string(layout->pool_ns);
ceph_oid_printf(&req->r_base_oid, "%llx.%08llx", vino.ino, objnum);
- req->r_flags = flags | osdc->client->options->read_from_replica;
+ req->r_flags = flags;
+ if (flags & CEPH_OSD_FLAG_READ)
+ req->r_flags |= osdc->client->options->read_from_replica;
req->r_snapid = vino.snap;
if (flags & CEPH_OSD_FLAG_WRITE)
@@ -3568,6 +3570,7 @@ static int ceph_oloc_decode(void **p, void *end,
ceph_decode_need(p, end, len, e_inval);
struct_end = *p + len;
+ ceph_decode_need(p, end, 8 + 4 + 4, e_inval);
oloc->pool = ceph_decode_64(p);
*p += 4; /* skip preferred */
@@ -3580,7 +3583,7 @@ static int ceph_oloc_decode(void **p, void *end,
if (struct_v >= 5) {
bool changed = false;
- len = ceph_decode_32(p);
+ ceph_decode_32_safe(p, end, len, e_inval);
if (len > 0) {
ceph_decode_need(p, end, len, e_inval);
if (!oloc->pool_ns ||
@@ -3599,7 +3602,9 @@ static int ceph_oloc_decode(void **p, void *end,
}
if (struct_v >= 6) {
- s64 hash = ceph_decode_64(p);
+ s64 hash;
+
+ ceph_decode_64_safe(p, end, hash, e_inval);
if (hash != -1) {
pr_warn("ceph_object_locator::hash is set\n");
goto e_inval;
@@ -3640,7 +3645,7 @@ static int ceph_redirect_decode(void **p, void *end,
if (ret)
goto out;
- len = ceph_decode_32(p);
+ ceph_decode_32_safe(p, end, len, e_inval);
if (len > 0) {
pr_warn("ceph_request_redirect::object_name is set\n");
goto e_inval;
diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index 68bd45852fc3..803514a1bc7c 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -378,21 +378,33 @@ static int decode_choose_args(void **p, void *end, struct crush_map *c)
ceph_decode_32_safe(p, end, num_buckets, e_inval);
while (num_buckets--) {
struct crush_choose_arg *arg;
+ struct crush_bucket *bucket;
u32 bucket_index;
+ u32 i;
ceph_decode_32_safe(p, end, bucket_index, e_inval);
if (bucket_index >= arg_map->size)
goto e_inval;
arg = &arg_map->args[bucket_index];
+ if (arg->ids || arg->weight_set)
+ goto e_inval;
ret = decode_choose_arg(p, end, arg);
if (ret)
goto fail;
+ bucket = c->buckets[bucket_index];
if (arg->ids_size &&
- (!c->buckets[bucket_index] ||
- arg->ids_size != c->buckets[bucket_index]->size))
+ (!bucket || arg->ids_size != bucket->size))
+ goto e_inval;
+
+ if (arg->weight_set_size && !bucket)
goto e_inval;
+
+ for (i = 0; i < arg->weight_set_size; i++) {
+ if (arg->weight_set[i].size != bucket->size)
+ goto e_inval;
+ }
}
if (!__insert_choose_arg_map(&c->choose_args, arg_map)) {
diff --git a/net/core/dev.c b/net/core/dev.c
index 0bfd5e9940df..673223065b95 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -6074,6 +6074,8 @@ static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,
}
if (unlikely(skb_vlan_tag_present(skb)) && !netdev_uses_dsa(skb->dev)) {
+ unsigned int priority_tag_depth = 1;
+
check_vlan_id:
if (skb_vlan_tag_get_id(skb)) {
/* Vlan id is non 0 and vlan_do_receive() above couldn't
@@ -6085,6 +6087,12 @@ static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,
* 802.1Q or 802.1AD and vlan_do_receive() above could
* not find vlan dev for vlan id 0.
*/
+ if (unlikely(priority_tag_depth >= VLAN_MAX_DEPTH)) {
+ drop_reason = SKB_DROP_REASON_RECURSION_LIMIT;
+ goto drop;
+ }
+
+ priority_tag_depth++;
__vlan_hwaccel_clear_tag(skb);
skb = skb_vlan_untag(skb);
if (unlikely(!skb))
diff --git a/net/core/gro.c b/net/core/gro.c
index 252d7851d1ae..f96e5024758e 100644
--- a/net/core/gro.c
+++ b/net/core/gro.c
@@ -192,6 +192,10 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb)
}
merge:
+ /* skb_segment() cannot split the linear part of a frag_list member. */
+ if (unlikely(headlen > offset + skb_shinfo(p)->gso_size))
+ return -E2BIG;
+
/* sk ownership - if any - completely transferred to the aggregated packet */
skb->destructor = NULL;
skb->sk = NULL;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 1808bed195af..227c889fba57 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3899,7 +3899,18 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
else
csstart = skb_headlen(skb);
- BUG_ON(csstart > skb_headlen(skb));
+ if (unlikely(csstart < 0 || csstart > skb_headlen(skb) ||
+ (skb->ip_summed == CHECKSUM_PARTIAL &&
+ csstart + skb->csum_offset + sizeof(__sum16) >
+ skb->len))) {
+ DEBUG_NET_WARN_ON_ONCE(1);
+ /* Send the frame without the checksum filled in, or send
+ * zeroes if we can't even copy it.
+ */
+ if (skb_copy_bits(skb, 0, to, skb->len))
+ memset(to, 0, skb->len);
+ return;
+ }
skb_copy_from_linear_data(skb, to, csstart);
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 6b36652d6bb9..dd14944ad3ed 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -20,6 +20,8 @@
#include <net/tcp_states.h>
#include <net/xfrm.h>
#include <net/tcp.h>
+#include <net/tcp_ecn.h>
+#include <net/mptcp.h>
#include <net/sock_reuseport.h>
#include <net/addrconf.h>
@@ -982,6 +984,9 @@ static struct request_sock *inet_reqsk_clone(struct request_sock *req,
rcu_assign_pointer(tcp_sk(nreq->sk)->fastopen_rsk, nreq);
}
+ if (rsk_is_mptcp(req))
+ mptcp_subflow_reqsk_clone(req, nreq);
+
return nreq;
}
@@ -1126,9 +1131,11 @@ static void reqsk_timer_handler(struct timer_list *t)
(!resend ||
!tcp_rtx_synack(sk_listener, req) ||
inet_rsk(req)->acked)) {
+ if (req->num_retrans > 1 && tcp_rsk(req)->accecn_ok)
+ tcp_rsk(req)->accecn_fail_mode |= TCP_ACCECN_ACE_FAIL_SEND;
if (req->num_timeout++ == 0)
atomic_dec(&queue->young);
- mod_timer(&req->rsk_timer, jiffies + reqsk_timeout(req, TCP_RTO_MAX));
+ mod_timer(&req->rsk_timer, jiffies + tcp_reqsk_timeout(req));
if (!nreq)
return;
@@ -1154,10 +1161,14 @@ static void reqsk_timer_handler(struct timer_list *t)
*/
if (nreq) {
__NET_INC_STATS(net, LINUX_MIB_TCPMIGRATEREQFAILURE);
+ /* The timer was not armed, drop its reference here. */
+ reqsk_put(nreq);
no_ownership:
reqsk_migrate_reset(nreq);
reqsk_queue_removed(queue, nreq);
- __reqsk_free(nreq);
+ /* Drop the temporary reference and the one reserved for ehash. */
+ reqsk_put(nreq);
+ reqsk_put(nreq);
}
drop:
@@ -1460,7 +1471,7 @@ struct sock *inet_csk_complete_hashdance(struct sock *sk, struct sock *child,
__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMIGRATEREQFAILURE);
reqsk_migrate_reset(nreq);
- __reqsk_free(nreq);
+ reqsk_put(nreq);
} else if (inet_csk_reqsk_queue_add(sk, req, child)) {
return child;
}
@@ -1515,7 +1526,7 @@ void inet_csk_listen_stop(struct sock *sk)
__NET_INC_STATS(sock_net(nsk),
LINUX_MIB_TCPMIGRATEREQFAILURE);
reqsk_migrate_reset(nreq);
- __reqsk_free(nreq);
+ reqsk_put(nreq);
}
rcu_read_unlock();
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index a57e33ff92d7..49c70d752a89 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -549,8 +549,9 @@ struct sock *__inet_lookup_established(const struct net *net,
if (likely(inet_match(net, sk, acookie, ports, dif, sdif))) {
if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
goto out;
- if (unlikely(!inet_match(net, sk, acookie,
- ports, dif, sdif))) {
+ if (unlikely(hlist_nulls_unhashed_lockless(&sk->sk_nulls_node) ||
+ !inet_match(net, sk, acookie, ports, dif,
+ sdif))) {
sock_gen_put(sk);
goto begin;
}
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index b1282c01a77e..66a7f25f236b 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -2474,6 +2474,7 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb,
skb_push(skb2, sizeof(struct iphdr));
skb_reset_network_header(skb2);
iph = ip_hdr(skb2);
+ memset(iph, 0, sizeof(*iph));
iph->ihl = sizeof(struct iphdr) >> 2;
iph->saddr = saddr;
iph->daddr = daddr;
diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c
index 59ec465a9df9..7cb33af671e2 100644
--- a/net/ipv4/netfilter/nf_reject_ipv4.c
+++ b/net/ipv4/netfilter/nf_reject_ipv4.c
@@ -357,6 +357,11 @@ void nf_send_unreach(struct sk_buff *skb_in, int code, int hook)
if (!skb_valid_dst(skb_in) && nf_reject_fill_skb_dst(skb_in) < 0)
return;
+ if (hook == NF_INET_INGRESS) {
+ memset(IPCB(skb_in), 0, sizeof(*IPCB(skb_in)));
+ IPCB(skb_in)->iif = skb_in->skb_iif;
+ }
+
if (skb_csum_unnecessary(skb_in) ||
!nf_reject_verify_csum(skb_in, dataoff, proto)) {
icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 6e51d8f5365d..e6276896f3ad 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1362,8 +1362,8 @@ static unsigned int ipv4_default_advmss(const struct dst_entry *dst)
rcu_read_lock();
net = dst_dev_net_rcu(dst);
- advmss = max_t(unsigned int, ip_dst_mtu_configured(dst) - header_size,
- net->ipv4.ip_rt_min_advmss);
+ advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size,
+ net->ipv4.ip_rt_min_advmss);
rcu_read_unlock();
return min(advmss, IPV4_MAX_PMTU - header_size);
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index b5f0a65c6786..8b3d4e8ee455 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -218,6 +218,8 @@ struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
bh_unlock_sock(child);
sock_put(child);
+ reqsk_put(req);
+ return NULL;
}
__reqsk_free(req);
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 34a8a932402d..bc3e5d6b8662 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -26,6 +26,7 @@ static int ip_local_port_range_max[] = { 65535, 65535 };
static int tcp_adv_win_scale_min = -31;
static int tcp_adv_win_scale_max = 31;
static int tcp_app_win_max = 31;
+static int tcp_tso_rtt_log_max = 31;
static int tcp_min_snd_mss_min = TCP_MIN_SND_MSS;
static int tcp_min_snd_mss_max = 65535;
static int tcp_rto_max_max = TCP_RTO_MAX_SEC * MSEC_PER_SEC;
@@ -1379,6 +1380,7 @@ static struct ctl_table ipv4_net_table[] = {
.maxlen = sizeof(u8),
.mode = 0644,
.proc_handler = proc_dou8vec_minmax,
+ .extra2 = &tcp_tso_rtt_log_max,
},
{
.procname = "tcp_min_rtt_wlen",
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index 8e83ef81fc18..e3f462f161dd 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -288,12 +288,12 @@ int tcp_set_default_congestion_control(struct net *net, const char *name)
ca = tcp_ca_find_autoload(name);
if (!ca) {
ret = -ENOENT;
- } else if (!bpf_try_module_get(ca, ca->owner)) {
- ret = -EBUSY;
} else if (!net_eq(net, &init_net) &&
!(ca->flags & TCP_CONG_NON_RESTRICTED)) {
/* Only init netns can set default to a restricted algorithm */
ret = -EPERM;
+ } else if (!bpf_try_module_get(ca, ca->owner)) {
+ ret = -EBUSY;
} else {
prev = xchg(&net->ipv4.tcp_congestion_control, ca);
if (prev)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index e1785140bf9b..28db1353445b 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -6236,6 +6236,8 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
if (th->syn) {
if (tcp_ecn_mode_accecn(tp)) {
accecn_reflector = true;
+ tp->syn_ect_rcv = TCP_SKB_CB(skb)->ip_dsfield &
+ INET_ECN_MASK;
if (tp->rx_opt.accecn &&
tp->saw_accecn_opt < TCP_ACCECN_OPT_COUNTER_SEEN) {
u8 saw_opt = tcp_accecn_option_init(skb, tp->rx_opt.accecn);
@@ -7561,7 +7563,9 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
if (!inet_csk_reqsk_queue_add(sk, req, fastopen_sk)) {
bh_unlock_sock(fastopen_sk);
sock_put(fastopen_sk);
- goto drop_and_free;
+ /* tcp_disconnect() already dropped one reqsk ref. */
+ reqsk_put(req);
+ goto drop;
}
READ_ONCE(sk->sk_data_ready)(sk);
bh_unlock_sock(fastopen_sk);
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index d05009f985af..ebcd78ad13ef 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -484,6 +484,10 @@ static void tcp_ecn_openreq_child(struct sock *sk,
tp->syn_ect_snt = treq->syn_ect_snt;
tcp_accecn_third_ack(sk, skb, treq->syn_ect_snt);
tp->saw_accecn_opt = treq->saw_accecn_opt;
+ if (treq->accecn_fail_mode & TCP_ACCECN_ACE_FAIL_SEND)
+ tcp_accecn_fail_mode_set(tp, TCP_ACCECN_ACE_FAIL_SEND);
+ if (treq->accecn_fail_mode & TCP_ACCECN_ACE_FAIL_RECV)
+ tcp_accecn_fail_mode_set(tp, TCP_ACCECN_ACE_FAIL_RECV);
tp->prev_ecnfield = treq->syn_ect_rcv;
tp->accecn_opt_demand = 1;
tcp_ecn_received_counters_payload(sk, skb);
@@ -717,7 +721,8 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
* it can be estimated (approximately)
* from another data.
*/
- tmp_opt.ts_recent_stamp = ktime_get_seconds() - reqsk_timeout(req, TCP_RTO_MAX) / HZ;
+ tmp_opt.ts_recent_stamp = ktime_get_seconds() -
+ tcp_reqsk_timeout(req) / HZ;
paws_reject = tcp_paws_reject(&tmp_opt, th->rst);
}
}
@@ -751,16 +756,28 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
*/
if (!tcp_oow_rate_limited(sock_net(sk), skb,
LINUX_MIB_TCPACKSKIPPEDSYNRECV,
- &tcp_rsk(req)->last_oow_ack_time) &&
-
- !tcp_rtx_synack(sk, req)) {
- unsigned long expires = jiffies;
-
- expires += reqsk_timeout(req, TCP_RTO_MAX);
- if (!fastopen)
- mod_timer_pending(&req->rsk_timer, expires);
- else
- req->rsk_timer.expires = expires;
+ &tcp_rsk(req)->last_oow_ack_time)) {
+ if (tcp_rsk(req)->accecn_ok) {
+ u8 ect_rcv = TCP_SKB_CB(skb)->ip_dsfield &
+ INET_ECN_MASK;
+
+ tcp_rsk(req)->syn_ect_rcv = ect_rcv;
+ if (tcp_accecn_ace(tcp_hdr(skb)) == 0x0)
+ tcp_rsk(req)->accecn_fail_mode |= TCP_ACCECN_ACE_FAIL_RECV;
+ }
+ if (!tcp_rtx_synack(sk, req)) {
+ unsigned long expires = jiffies;
+
+ if (req->num_retrans > 1 && tcp_rsk(req)->accecn_ok)
+ tcp_rsk(req)->accecn_fail_mode |= TCP_ACCECN_ACE_FAIL_SEND;
+
+ expires += tcp_reqsk_timeout(req);
+ if (!fastopen)
+ mod_timer_pending(&req->rsk_timer,
+ expires);
+ else
+ req->rsk_timer.expires = expires;
+ }
}
return NULL;
}
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index ddcc7e53f2e3..b39ab081e3a6 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -333,8 +333,11 @@ static void tcp_ecn_send(struct sock *sk, struct sk_buff *skb,
return;
if (tcp_ecn_mode_accecn(tp)) {
- if (!tcp_accecn_ace_fail_recv(tp))
+ if (!tcp_accecn_ace_fail_recv(tp) &&
+ !tcp_accecn_ace_fail_send(tp))
INET_ECN_xmit(sk);
+ else
+ INET_ECN_dontxmit(sk);
tcp_accecn_set_ace(tp, skb, th);
skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ACCECN;
} else {
@@ -2628,7 +2631,7 @@ static int tcp_mtu_probe(struct sock *sk)
mss_now = tcp_current_mss(sk);
probe_size = tcp_mtu_to_mss(sk, (icsk->icsk_mtup.search_high +
icsk->icsk_mtup.search_low) >> 1);
- size_needed = probe_size + (tp->reordering + 1) * (u64)tp->mss_cache;
+ size_needed = probe_size + ((u64)tp->reordering + 1) * tp->mss_cache;
interval = icsk->icsk_mtup.search_high - icsk->icsk_mtup.search_low;
/* When misfortune happens, we are reprobing actively,
* and then reprobe timer has expired. We stick with current
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index eb549fea8fdd..017a52022a1a 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/gfp.h>
#include <net/tcp.h>
+#include <net/tcp_ecn.h>
#include <net/rstreason.h>
static u32 tcp_clamp_rto_to_user_timeout(const struct sock *sk)
@@ -482,6 +483,8 @@ static void tcp_fastopen_synack_timer(struct sock *sk, struct request_sock *req)
* it's not good to give up too easily.
*/
tcp_rtx_synack(sk, req);
+ if (req->num_retrans > 1 && tcp_rsk(req)->accecn_ok)
+ tcp_rsk(req)->accecn_fail_mode |= TCP_ACCECN_ACE_FAIL_SEND;
req->num_timeout++;
tcp_update_rto_stats(sk);
if (!tp->retrans_stamp)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 81f5bda2a89f..527ae9fc1c41 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1155,10 +1155,19 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg,
ifa->flags |= IFA_F_TENTATIVE;
ifa->valid_lft = cfg->valid_lft;
ifa->prefered_lft = cfg->preferred_lft;
- ifa->cstamp = ifa->tstamp = jiffies;
ifa->tokenized = false;
ifa->rt = f6i;
+ ifa->ifpub = cfg->ifpub;
+ if (cfg->ifpub) {
+ ifa->cstamp = cfg->cstamp;
+ ifa->tstamp = cfg->tstamp;
+ } else {
+ unsigned long now = jiffies;
+
+ ifa->cstamp = now;
+ ifa->tstamp = now;
+ }
ifa->idev = idev;
in6_dev_hold(idev);
@@ -1168,14 +1177,18 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg,
rcu_read_lock();
- err = ipv6_add_addr_hash(idev->dev, ifa);
+ write_lock_bh(&idev->lock);
+
+ if (idev->dead)
+ err = -ENODEV;
+ else
+ err = ipv6_add_addr_hash(idev->dev, ifa);
if (err < 0) {
+ write_unlock_bh(&idev->lock);
rcu_read_unlock();
goto out;
}
- write_lock_bh(&idev->lock);
-
/* Add to inet6_dev unicast addr list. */
ipv6_link_dev_addr(idev, ifa);
@@ -1487,6 +1500,9 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
cfg.pfx = &addr;
cfg.scope = ipv6_addr_scope(cfg.pfx);
+ cfg.ifpub = ifp;
+ cfg.cstamp = now;
+ cfg.tstamp = tmp_tstamp;
ift = ipv6_add_addr(idev, &cfg, block, NULL);
if (IS_ERR(ift)) {
@@ -1497,12 +1513,6 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
goto retry;
}
- spin_lock_bh(&ift->lock);
- ift->ifpub = ifp;
- ift->cstamp = now;
- ift->tstamp = tmp_tstamp;
- spin_unlock_bh(&ift->lock);
-
addrconf_dad_start(ift);
in6_ifa_put(ift);
in6_dev_put(idev);
@@ -2188,6 +2198,7 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source);
if (ifp->flags & IFA_F_STABLE_PRIVACY) {
+ bool start_dad = false;
struct in6_addr new_addr;
struct inet6_ifaddr *ifp2;
int retries = ifp->stable_privacy_retry + 1;
@@ -2226,11 +2237,14 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
goto lock_errdad;
spin_lock_bh(&ifp2->lock);
- ifp2->stable_privacy_retry = retries;
- ifp2->state = INET6_IFADDR_STATE_PREDAD;
+ if (ifp2->state != INET6_IFADDR_STATE_DEAD) {
+ ifp2->stable_privacy_retry = retries;
+ start_dad = true;
+ }
spin_unlock_bh(&ifp2->lock);
- addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay);
+ if (start_dad)
+ addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay);
in6_ifa_put(ifp2);
lock_errdad:
spin_lock_bh(&ifp->lock);
@@ -3884,7 +3898,9 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister)
* Do not dev_put!
*/
if (unregister) {
+ write_lock_bh(&idev->lock);
WRITE_ONCE(idev->dead, 1);
+ write_unlock_bh(&idev->lock);
/* protected by rtnl_lock */
RCU_INIT_POINTER(dev->ip6_ptr, NULL);
@@ -4006,6 +4022,10 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister)
}
if (!keep) {
+ spin_lock_bh(&net->ipv6.addrconf_hash_lock);
+ hlist_del_init_rcu(&ifa->addr_lst);
+ spin_unlock_bh(&net->ipv6.addrconf_hash_lock);
+
write_lock_bh(&idev->lock);
list_del_rcu(&ifa->if_list);
write_unlock_bh(&idev->lock);
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index 182d38e6d6d8..f4f39a20e951 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -73,7 +73,9 @@ struct sock *__inet6_lookup_established(const struct net *net,
if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
goto out;
- if (unlikely(!inet6_match(net, sk, saddr, daddr, ports, dif, sdif))) {
+ if (unlikely(hlist_nulls_unhashed_lockless(&sk->sk_nulls_node) ||
+ !inet6_match(net, sk, saddr, daddr, ports, dif,
+ sdif))) {
sock_gen_put(sk);
goto begin;
}
diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c
index 07cdaa10da0d..5e08d8529cc8 100644
--- a/net/ipv6/netfilter/nf_reject_ipv6.c
+++ b/net/ipv6/netfilter/nf_reject_ipv6.c
@@ -444,6 +444,11 @@ void nf_send_unreach6(struct net *net, struct sk_buff *skb_in,
if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL)
skb_in->dev = net->loopback_dev;
+ if (hooknum == NF_INET_INGRESS) {
+ memset(IP6CB(skb_in), 0, sizeof(*IP6CB(skb_in)));
+ IP6CB(skb_in)->iif = skb_in->skb_iif;
+ }
+
if (!skb_valid_dst(skb_in) && nf_reject6_fill_skb_dst(skb_in) < 0)
return;
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 25ec8001898d..c10a8a9d607e 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -227,6 +227,7 @@ static int ip6_frag_queue(struct net *net,
}
skb_dst_drop(skb);
+ skb_orphan(skb);
return -EINPROGRESS;
insert_error:
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index ce60fe53b822..c3a837bdd0cb 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -3264,7 +3264,7 @@ EXPORT_SYMBOL_GPL(ip6_sk_redirect);
static unsigned int ip6_default_advmss(const struct dst_entry *dst)
{
- unsigned int mtu = ip6_dst_mtu_configured(dst);
+ unsigned int mtu = dst6_mtu(dst);
struct net *net;
mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index e14a49180eef..df09dcfd15ce 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -3921,9 +3921,16 @@ static int mptcp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
* subflow_finish_connect()
*/
if (unlikely(err)) {
- /* avoid leaving a dangling token in an unconnected socket */
mptcp_token_destroy(msk);
mptcp_set_state(sk, TCP_CLOSE);
+
+ spin_lock_bh(&msk->fallback_lock);
+ msk->allow_subflows = true;
+ msk->allow_infinite_fallback = true;
+ clear_bit(MPTCP_FALLBACK_DONE, &msk->flags);
+ spin_unlock_bh(&msk->fallback_lock);
+
+ mptcp_subflow_ctx_reset(mptcp_subflow_ctx(ssk));
return err;
}
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index dc8920a6a90c..184831b5b624 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -543,14 +543,16 @@ struct mptcp_subflow_context {
closing : 1, /* must not pass rx data to msk anymore */
valid_csum_seen : 1, /* at least one csum validated */
is_mptfo : 1, /* subflow is doing TFO */
- close_event_done : 1, /* has done the post-closed part */
mpc_drop : 1, /* the MPC option has been dropped in a rtx */
resetting : 1, /* subflow is resetting */
- __unused : 8;
+ __unused : 9;
bool data_avail;
bool scheduled;
bool pm_listener; /* a listener managed by the kernel PM? */
bool fully_established; /* path validated */
+ bool close_event_done; /* has done the post-closed part,
+ * protected by msk socket lock
+ */
u32 lent_mem_frag;
u32 remote_nonce;
u64 thmac;
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 1932fcf9f925..e020960f093e 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -47,6 +47,39 @@ static void subflow_req_destructor(struct request_sock *req)
mptcp_token_destroy_request(req);
}
+void mptcp_subflow_reqsk_clone(struct request_sock *req,
+ struct request_sock *new_req)
+{
+ struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req);
+ struct mptcp_subflow_request_sock *new_subflow_req;
+ struct mptcp_sock *msk;
+
+ new_subflow_req = mptcp_subflow_rsk(new_req);
+ RCU_LOCKDEP_WARN(!rcu_read_lock_any_held(),
+ "MPTCP reqsk clone called without RCU");
+
+ /* A non-NULL ->msk means the request owns one reference. The clone
+ * copied only the pointer, while the original request can concurrently
+ * transfer its reference to the child. Acquire a reference for the
+ * clone, then verify that the original request still owns the same msk.
+ * MPTCP sockets use SLAB_TYPESAFE_BY_RCU and all clone callers run in
+ * an RCU read-side critical section, keeping the memory stable here.
+ */
+ msk = READ_ONCE(subflow_req->msk);
+ if (msk) {
+ struct sock *sk = (struct sock *)msk;
+
+ if (!refcount_inc_not_zero_acquire(&sk->sk_refcnt)) {
+ msk = NULL;
+ } else if (READ_ONCE(subflow_req->msk) != msk) {
+ sock_put(sk);
+ msk = NULL;
+ }
+ }
+
+ new_subflow_req->msk = msk;
+}
+
static void subflow_generate_hmac(u64 key1, u64 key2, u32 nonce1, u32 nonce2,
void *hmac)
{
@@ -925,7 +958,7 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
}
/* move the msk reference ownership to the subflow */
- subflow_req->msk = NULL;
+ WRITE_ONCE(subflow_req->msk, NULL);
ctx->conn = (struct sock *)owner;
if (subflow_use_different_sport(owner, sk)) {
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 6ccb23a640d8..0ff9da922c39 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1098,13 +1098,13 @@ static int ctnetlink_filter_match_tuple(struct nf_conntrack_tuple *filter_tuple,
break;
case NFPROTO_IPV6:
if ((flags & CTA_FILTER_FLAG(CTA_IP_SRC)) &&
- !ipv6_addr_cmp(&filter_tuple->src.u3.in6,
- &ct_tuple->src.u3.in6))
+ !ipv6_addr_equal(&filter_tuple->src.u3.in6,
+ &ct_tuple->src.u3.in6))
return 0;
if ((flags & CTA_FILTER_FLAG(CTA_IP_DST)) &&
- !ipv6_addr_cmp(&filter_tuple->dst.u3.in6,
- &ct_tuple->dst.u3.in6))
+ !ipv6_addr_equal(&filter_tuple->dst.u3.in6,
+ &ct_tuple->dst.u3.in6))
return 0;
break;
}
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 4516e5452522..7b8ba1690d54 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2646,6 +2646,12 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
}
offset = offset_in_page(data);
+ if (unlikely(to_write > MAX_SKB_FRAGS * PAGE_SIZE - offset)) {
+ net_warn_ratelimited("Packet exceed the number of skb frags(%u)\n",
+ (unsigned int)MAX_SKB_FRAGS);
+ return -EFAULT;
+ }
+
len_max = PAGE_SIZE - offset;
len = ((to_write > len_max) ? len_max : to_write);
@@ -2657,12 +2663,6 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
while (likely(to_write)) {
nr_frags = skb_shinfo(skb)->nr_frags;
- if (unlikely(nr_frags >= MAX_SKB_FRAGS)) {
- pr_err("Packet exceed the number of skb frags(%u)\n",
- (unsigned int)MAX_SKB_FRAGS);
- return -EFAULT;
- }
-
page = pgv_to_page(data);
data += len;
flush_dcache_page(page);
diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
index 1c6e3b3f39d0..c8771aa7df53 100644
--- a/net/rds/ib_cm.c
+++ b/net/rds/ib_cm.c
@@ -1271,6 +1271,7 @@ void rds_ib_conn_free(void *arg)
{
struct rds_ib_connection *ic = arg;
spinlock_t *lock_ptr;
+ unsigned long flags;
rdsdebug("ic %p\n", ic);
@@ -1281,9 +1282,10 @@ void rds_ib_conn_free(void *arg)
*/
lock_ptr = ic->rds_ibdev ? &ic->rds_ibdev->spinlock : &ib_nodev_conns_lock;
- spin_lock_irq(lock_ptr);
+ /* May be called with IRQs already disabled (see __rds_conn_create()) */
+ spin_lock_irqsave(lock_ptr, flags);
list_del(&ic->ib_node);
- spin_unlock_irq(lock_ptr);
+ spin_unlock_irqrestore(lock_ptr, flags);
rds_ib_recv_free_caches(ic);
diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c
index 0b52e060988d..ad4ff441bc98 100644
--- a/net/rds/ib_recv.c
+++ b/net/rds/ib_recv.c
@@ -949,6 +949,12 @@ static void rds_ib_process_recv(struct rds_connection *conn,
}
}
+ if (data_len < min_t(u32, ic->i_recv_data_rem, RDS_FRAG_SIZE)) {
+ rds_ib_conn_error(conn,
+ "fragment shorter than header-declared length; forcing reconnect\n");
+ goto done;
+ }
+
list_add_tail(&recv->r_frag->f_item, &ibinc->ii_frags);
recv->r_frag = NULL;
@@ -1004,7 +1010,12 @@ void rds_ib_recv_cqe_handler(struct rds_ib_connection *ic,
* event is processed.
*/
if (wc->status == IB_WC_SUCCESS) {
- rds_ib_process_recv(conn, recv, wc->byte_len, state);
+ if (wc->opcode != IB_WC_RECV)
+ rds_ib_conn_error(conn,
+ "recv completion had unexpected opcode %u, disconnecting and reconnecting\n",
+ wc->opcode);
+ else
+ rds_ib_process_recv(conn, recv, wc->byte_len, state);
} else {
/* We expect errors as the qp is drained during shutdown */
if (rds_conn_up(conn) || rds_conn_connecting(conn))
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 03588ac5c382..8c9970352952 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -75,7 +75,8 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc,
struct sctp_transport *transport,
long *timeo_p, size_t msg_len);
static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);
-static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
+static int sctp_wait_for_connect(struct sctp_association *,
+ struct sctp_transport *, long *timeo_p);
static int sctp_wait_for_accept(struct sock *sk, long timeo);
static void sctp_wait_for_close(struct sock *sk, long timeo);
static void sctp_destruct_sock(struct sock *sk);
@@ -1244,7 +1245,7 @@ static int __sctp_connect(struct sock *sk, struct sockaddr *kaddrs,
*assoc_id = asoc->assoc_id;
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
- return sctp_wait_for_connect(asoc, &timeo);
+ return sctp_wait_for_connect(asoc, NULL, &timeo);
out_free:
pr_debug("%s: took out_free path with asoc:%p kaddrs:%p err:%d\n",
@@ -1846,7 +1847,7 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
if (asoc->ep->intl_enable) {
timeo = sock_sndtimeo(sk, 0);
- err = sctp_wait_for_connect(asoc, &timeo);
+ err = sctp_wait_for_connect(asoc, transport, &timeo);
if (err || sk->sk_shutdown & RCV_SHUTDOWN) {
err = -ESRCH;
goto err;
@@ -1889,7 +1890,7 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
if (unlikely(wait_connect)) {
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
- sctp_wait_for_connect(asoc, &timeo);
+ sctp_wait_for_connect(asoc, NULL, &timeo);
}
err = msg_len;
@@ -9337,7 +9338,9 @@ static bool sctp_writeable(const struct sock *sk)
/* Wait for an association to go into ESTABLISHED state. If timeout is 0,
* returns immediately with EINPROGRESS.
*/
-static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
+static int sctp_wait_for_connect(struct sctp_association *asoc,
+ struct sctp_transport *transport,
+ long *timeo_p)
{
struct sock *sk = asoc->base.sk;
int err = 0;
@@ -9346,12 +9349,16 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
pr_debug("%s: asoc:%p, timeo:%ld\n", __func__, asoc, *timeo_p);
- /* Increment the association's refcnt. */
+ /* Hold references while the socket lock is dropped. */
+ if (transport)
+ sctp_transport_hold(transport);
sctp_association_hold(asoc);
for (;;) {
prepare_to_wait_exclusive(&asoc->wait, &wait,
TASK_INTERRUPTIBLE);
+ if (transport && transport->dead)
+ goto do_transport_dead;
if (!*timeo_p)
goto do_nonblock;
if (sk->sk_shutdown & RCV_SHUTDOWN)
@@ -9380,7 +9387,8 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
out:
finish_wait(&asoc->wait, &wait);
- /* Release the association's refcnt. */
+ if (transport)
+ sctp_transport_put(transport);
sctp_association_put(asoc);
return err;
@@ -9399,6 +9407,10 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
do_nonblock:
err = -EINPROGRESS;
goto out;
+
+do_transport_dead:
+ err = -EAGAIN;
+ goto out;
}
static int sctp_wait_for_accept(struct sock *sk, long timeo)
diff --git a/net/smc/smc_tx.c b/net/smc/smc_tx.c
index 3144b4b1fe29..495fb1354728 100644
--- a/net/smc/smc_tx.c
+++ b/net/smc/smc_tx.c
@@ -249,6 +249,13 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len)
rc = memcpy_from_msg(sndbuf_base + chunk_off,
msg, chunk_len);
if (rc) {
+ if (chunk) {
+ /* send_done counts the first chunk:
+ * queue it and retry the rest
+ */
+ copylen -= chunk_len;
+ break;
+ }
smc_sndbuf_sync_sg_for_device(conn);
if (send_done)
return send_done;
diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
index e2694fb03ba1..091b8cb415ec 100644
--- a/net/sunrpc/xprtrdma/rpc_rdma.c
+++ b/net/sunrpc/xprtrdma/rpc_rdma.c
@@ -1033,12 +1033,16 @@ void rpcrdma_reset_cwnd(struct rpcrdma_xprt *r_xprt)
* @rqst: controlling RPC request
* @srcp: points to RPC message payload in receive buffer
* @copy_len: remaining length of receive buffer content
- * @pad: Write chunk pad bytes needed (zero for pure inline)
+ * @writelist: bytes conveyed by the Write chunk (zero for pure inline)
*
* The upper layer has set the maximum number of bytes it can
* receive in each component of rq_rcv_buf. These values are set in
* the head.iov_len, page_len, tail.iov_len, and buflen fields.
*
+ * For READ replies with a Write chunk, the payload is already in
+ * the page list. An overestimated head length and buflen can be
+ * reduced without moving reply data.
+ *
* Unlike the TCP equivalent (xdr_partial_copy_from_skb), in
* many cases this function simply updates iov_base pointers in
* rq_rcv_buf to point directly to the received reply data, to
@@ -1047,13 +1051,16 @@ void rpcrdma_reset_cwnd(struct rpcrdma_xprt *r_xprt)
* Returns the count of bytes which had to be memcopied.
*/
static unsigned long
-rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len, int pad)
+rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len,
+ u32 writelist)
{
unsigned long fixup_copy_count;
int i, npages, curlen;
char *destp;
struct page **ppages;
int page_base;
+ int pad = writelist & 3;
+ unsigned int delta;
/* The head iovec is redirected to the RPC reply message
* in the receive buffer, to avoid a memcopy.
@@ -1065,8 +1072,20 @@ rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len, int pad)
* head.iov_len bytes are copied into the page list.
*/
curlen = rqst->rq_rcv_buf.head[0].iov_len;
- if (curlen > copy_len)
+ if (curlen > copy_len) {
+ /* These READ replies have no inline fields after the payload.
+ * Trim the head without moving the directly-placed data.
+ */
+ if (writelist) {
+ delta = curlen - copy_len;
+
+ rqst->rq_rcv_buf.head[0].iov_len = copy_len;
+ rqst->rq_private_buf.head[0].iov_len = copy_len;
+ rqst->rq_rcv_buf.buflen -= delta;
+ rqst->rq_private_buf.buflen -= delta;
+ }
curlen = copy_len;
+ }
srcp += curlen;
copy_len -= curlen;
@@ -1294,7 +1313,7 @@ rpcrdma_decode_msg(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep,
base = (char *)xdr_inline_decode(xdr, 0);
rpclen = xdr_stream_remaining(xdr);
r_xprt->rx_stats.fixup_copy_count +=
- rpcrdma_inline_fixup(rqst, base, rpclen, writelist & 3);
+ rpcrdma_inline_fixup(rqst, base, rpclen, writelist);
r_xprt->rx_stats.total_rdma_reply += writelist;
return rpclen + xdr_align_size(writelist);
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index 4afa662430ca..d9bc86fa09bc 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -469,7 +469,6 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
newxprt->sc_max_requests = svcrdma_max_requests;
newxprt->sc_max_bc_requests = svcrdma_max_bc_requests;
newxprt->sc_recv_batch = RPCRDMA_MAX_RECV_BATCH;
- newxprt->sc_fc_credits = cpu_to_be32(newxprt->sc_max_requests);
/* Qualify the transport's resource defaults with the
* capabilities of this particular device.
@@ -490,6 +489,8 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
newxprt->sc_max_bc_requests = 2;
}
+ newxprt->sc_fc_credits = cpu_to_be32(newxprt->sc_max_requests);
+
/* Estimate the needed number of rdma_rw contexts. The maximum
* Read and Write chunks have one segment each. Each request
* can involve one Read chunk and either a Write chunk or Reply
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
index 8449107b2a09..111c415f1aed 100644
--- a/net/sunrpc/xprtrdma/verbs.c
+++ b/net/sunrpc/xprtrdma/verbs.c
@@ -114,9 +114,9 @@ static void rpcrdma_xprt_drain(struct rpcrdma_xprt *r_xprt)
rpcrdma_ep_put(ep);
}
-/* Ensure xprt_force_disconnect() is invoked exactly once when a
- * connection is closed or lost. (The important thing is it needs
- * to be invoked "at least" once).
+/* Ensure xprt_force_disconnect() is invoked at most once for an
+ * unexpected endpoint failure. Local endpoint teardown closes this
+ * gate before it can generate CM events and flushed completions.
*/
void rpcrdma_force_disconnect(struct rpcrdma_ep *ep)
{
@@ -587,6 +587,9 @@ void rpcrdma_xprt_disconnect(struct rpcrdma_xprt *r_xprt)
if (!ep)
return;
+ /* Prevent teardown-generated events from requesting another close. */
+ atomic_xchg(&ep->re_force_disconnect, 1);
+
id = ep->re_id;
rc = rdma_disconnect(id);
trace_xprtrdma_disconnect(r_xprt, rc);
diff --git a/net/tipc/group.c b/net/tipc/group.c
index 0da7b2713394..a9ce1c7018b6 100644
--- a/net/tipc/group.c
+++ b/net/tipc/group.c
@@ -74,7 +74,7 @@ struct tipc_member {
u16 window;
u16 bc_rcv_nxt;
u16 bc_syncpt;
- u16 bc_acked;
+ bool bc_pending;
};
struct tipc_group {
@@ -316,7 +316,6 @@ static struct tipc_member *tipc_group_create_member(struct tipc_group *grp,
m->node = node;
m->port = port;
m->instance = instance;
- m->bc_acked = grp->bc_snd_nxt - 1;
ret = tipc_group_add_to_tree(grp, m);
if (ret < 0) {
kfree(m);
@@ -340,9 +339,12 @@ static void tipc_group_delete_member(struct tipc_group *grp,
rb_erase(&m->tree_node, &grp->members);
grp->member_cnt--;
- /* Check if we were waiting for replicast ack from this member */
- if (grp->bc_ackers && less(m->bc_acked, grp->bc_snd_nxt - 1))
- grp->bc_ackers--;
+ /* If this member owed an ack for the current replicast and it
+ * was the last one, restore the open state so blocked broadcast
+ * senders can proceed.
+ */
+ if (grp->bc_ackers && m->bc_pending && !--grp->bc_ackers)
+ *grp->open = true;
list_del_init(&m->list);
list_del_init(&m->small_win);
@@ -394,7 +396,6 @@ void tipc_group_update_member(struct tipc_member *m, int len)
void tipc_group_update_bc_members(struct tipc_group *grp, int len, bool ack)
{
- u16 prev = grp->bc_snd_nxt - 1;
struct tipc_member *m;
struct rb_node *n;
u16 ackers = 0;
@@ -403,8 +404,8 @@ void tipc_group_update_bc_members(struct tipc_group *grp, int len, bool ack)
m = container_of(n, struct tipc_member, tree_node);
if (tipc_group_is_receiver(m)) {
tipc_group_update_member(m, len);
- m->bc_acked = prev;
ackers++;
+ m->bc_pending = ack;
}
}
@@ -800,9 +801,9 @@ void tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup,
if (!m || !grp->bc_ackers)
return;
acked = msg_grp_bc_acked(hdr);
- if (acked != grp->bc_snd_nxt || m->bc_acked == acked)
+ if (acked != grp->bc_snd_nxt || !m->bc_pending)
return;
- m->bc_acked = acked;
+ m->bc_pending = false;
if (--grp->bc_ackers)
return;
list_del_init(&m->small_win);
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index af14b50933f5..4780264a8a49 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -2355,8 +2355,18 @@ static void tipc_sk_filter_rcv(struct sock *sk, struct sk_buff *skb,
if (unlikely(!msg_isdata(hdr)))
tipc_sk_proto_rcv(sk, &inputq, xmitq);
- if (unlikely(grp))
+ if (unlikely(grp)) {
+ bool was_open = tsk->group_is_open;
+
tipc_group_filter_msg(grp, &inputq, xmitq);
+ /* A departing member may have held the last outstanding
+ * replicast ack, which can restore the open state inside
+ * tipc_group_filter_msg(); wake up senders blocked in
+ * poll()/write() if the group just became writable.
+ */
+ if (!was_open && tsk->group_is_open)
+ sk->sk_write_space(sk);
+ }
if (unlikely(!grp) && mtyp == TIPC_MCAST_MSG)
tipc_mcast_filter_msg(net, &tsk->mc_method.deferredq, &inputq);
diff --git a/net/unix/sysctl_net_unix.c b/net/unix/sysctl_net_unix.c
index e02ed6e3955c..bb93cf3aa6fe 100644
--- a/net/unix/sysctl_net_unix.c
+++ b/net/unix/sysctl_net_unix.c
@@ -19,7 +19,9 @@ static struct ctl_table unix_table[] = {
.data = &init_net.unx.sysctl_max_dgram_qlen,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = SYSCTL_ZERO,
+ .extra2 = SYSCTL_INT_MAX,
},
};
diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c
index 5ee0edaf075c..9dc1ff31867a 100644
--- a/net/xdp/xsk_buff_pool.c
+++ b/net/xdp/xsk_buff_pool.c
@@ -175,6 +175,7 @@ int xp_assign_dev(struct xsk_buff_pool *pool,
{
u32 needed = netdev->mtu + ETH_PAD_LEN;
u32 segs = netdev->xdp_zc_max_segs;
+ u8 umem_flags = pool->umem->flags;
bool mbuf = flags & XDP_USE_SG;
bool force_zc, force_copy;
struct netdev_bpf bpf;
@@ -270,6 +271,7 @@ int xp_assign_dev(struct xsk_buff_pool *pool,
if (!force_zc)
err = 0; /* fallback to copy mode */
if (err) {
+ pool->umem->flags = umem_flags;
xsk_clear_pool_at_qid(netdev, queue_id);
dev_put(netdev);
}
diff --git a/rust/Makefile b/rust/Makefile
index d4618f646b05..d6429e68949f 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -367,7 +367,10 @@ quiet_cmd_bindgen = BINDGEN $@
$(obj)/bindings/bindings_generated.rs: private bindgen_target_flags = \
$(shell grep -Ev '^#|^$$' $(src)/bindgen_parameters)
$(obj)/bindings/bindings_generated.rs: private bindgen_target_extra = ; \
- sed -Ei 's/pub const RUST_CONST_HELPER_([a-zA-Z0-9_]*)/pub const \1/g' $@
+ sed -Ei 's/pub const RUST_CONST_HELPER_([a-zA-Z0-9_]*)/pub const \1/g' $@; \
+ sed -Ei 's/^pub struct dma_data_direction\(/#[cfi_encoding="18dma_data_direction"]\n&/' $@; \
+ sed -Ei 's/^pub struct hrtimer_restart\(/#[cfi_encoding="15hrtimer_restart"]\n&/' $@; \
+ sed -Ei 's/^pub struct irqreturn\(/#[cfi_encoding="9irqreturn"]\n&/' $@
$(obj)/bindings/bindings_generated.rs: $(src)/bindings/bindings_helper.h \
$(src)/bindgen_parameters FORCE
$(call if_changed_dep,bindgen)
diff --git a/rust/bindgen_parameters b/rust/bindgen_parameters
index e13c6f9dd17b..a5d547dc8e8e 100644
--- a/rust/bindgen_parameters
+++ b/rust/bindgen_parameters
@@ -23,6 +23,11 @@
# warning. We don't need to peek into it anyway.
--opaque-type spinlock
+# enums that appear in indirect function calls should specify a cfi type
+--newtype-enum dma_data_direction
+--newtype-enum hrtimer_restart
+--newtype-enum irqreturn
+
# `seccomp`'s comment gets understood as a doctest
--no-doc-comments
diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs
index 8305adbdf8cb..37f584727eba 100644
--- a/rust/bindings/lib.rs
+++ b/rust/bindings/lib.rs
@@ -23,6 +23,7 @@
unreachable_pub,
unsafe_op_in_unsafe_fn
)]
+#![feature(cfi_encoding)]
#[allow(dead_code)]
#[allow(clippy::as_underscore)]
diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs
index 0e5e825aefb6..a00988bc8191 100644
--- a/rust/kernel/bitmap.rs
+++ b/rust/kernel/bitmap.rs
@@ -19,7 +19,7 @@
/// Must reference a `[c_ulong]` long enough to fit `data.len()` bits.
/// Must not be longer than [`i32::MAX`] bits, so offsets and lengths used with
/// [`Bitmap`] functions fit in the `int` and `unsigned int` arguments of the C bitmap API.
-/// This also matches [`BitmapVec::MAX_LEN`].
+/// This also matches the maximum length accepted by [`BitmapVec::new`].
#[cfg_attr(CONFIG_64BIT, repr(align(8)))]
#[cfg_attr(not(CONFIG_64BIT), repr(align(4)))]
pub struct Bitmap {
diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
index 4ce9e5d7f3dd..b6c73db358ce 100644
--- a/rust/kernel/dma.rs
+++ b/rust/kernel/dma.rs
@@ -267,25 +267,25 @@ pub enum DataDirection {
///
/// This is used when the buffer can be both read from and written to by the device.
/// The cache for the corresponding memory region is both flushed and invalidated.
- Bidirectional = Self::const_cast(bindings::dma_data_direction_DMA_BIDIRECTIONAL),
+ Bidirectional = Self::const_cast(bindings::dma_data_direction::DMA_BIDIRECTIONAL),
/// The DMA mapping is for data transfer from memory to the device (write).
///
/// The CPU has prepared data in the buffer, and the device will read it.
/// The cache for the corresponding memory region is flushed before device access.
- ToDevice = Self::const_cast(bindings::dma_data_direction_DMA_TO_DEVICE),
+ ToDevice = Self::const_cast(bindings::dma_data_direction::DMA_TO_DEVICE),
/// The DMA mapping is for data transfer from the device to memory (read).
///
/// The device will write data into the buffer for the CPU to read.
/// The cache for the corresponding memory region is invalidated before CPU access.
- FromDevice = Self::const_cast(bindings::dma_data_direction_DMA_FROM_DEVICE),
+ FromDevice = Self::const_cast(bindings::dma_data_direction::DMA_FROM_DEVICE),
/// The DMA mapping is not for data transfer.
///
/// This is primarily for debugging purposes. With this direction, the DMA mapping API
/// will not perform any cache coherency operations.
- None = Self::const_cast(bindings::dma_data_direction_DMA_NONE),
+ None = Self::const_cast(bindings::dma_data_direction::DMA_NONE),
}
impl DataDirection {
@@ -297,7 +297,7 @@ const fn const_cast(val: bindings::dma_data_direction) -> u32 {
// CAST: The C standard allows compilers to choose different integer types for enums.
// To safely check the value, we cast it to a wide signed integer type (`i128`)
// which can hold any standard C integer enum type without truncation.
- let wide_val = val as i128;
+ let wide_val = val.0 as i128;
// Check if the value is outside the valid range for the target type `u32`.
// CAST: `u32::MAX` is cast to `i128` to match the type of `wide_val` for the comparison.
@@ -315,11 +315,12 @@ const fn const_cast(val: bindings::dma_data_direction) -> u32 {
impl From<DataDirection> for bindings::dma_data_direction {
/// Returns the raw representation of [`enum dma_data_direction`].
fn from(direction: DataDirection) -> Self {
- // CAST: `direction as u32` gets the underlying representation of our `#[repr(u32)]` enum.
- // The subsequent cast to `Self` (the bindgen type) assumes the C enum is compatible
- // with the enum variants of `DataDirection`, which is a valid assumption given our
- // compile-time checks.
- direction as u32 as Self
+ match direction {
+ DataDirection::Bidirectional => Self::DMA_BIDIRECTIONAL,
+ DataDirection::ToDevice => Self::DMA_TO_DEVICE,
+ DataDirection::FromDevice => Self::DMA_FROM_DEVICE,
+ DataDirection::None => Self::DMA_NONE,
+ }
}
}
diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs
index 70700f43ddd3..4f2b315856b9 100644
--- a/rust/kernel/irq/request.rs
+++ b/rust/kernel/irq/request.rs
@@ -20,10 +20,10 @@
#[repr(u32)]
pub enum IrqReturn {
/// The interrupt was not from this device or was not handled.
- None = bindings::irqreturn_IRQ_NONE,
+ None = bindings::irqreturn::IRQ_NONE.0,
/// The interrupt was handled by this device.
- Handled = bindings::irqreturn_IRQ_HANDLED,
+ Handled = bindings::irqreturn::IRQ_HANDLED.0,
}
/// Callbacks for an IRQ handler.
@@ -275,7 +275,7 @@ pub fn synchronize(&self, dev: &Device<Bound>) -> Result {
unsafe extern "C" fn handle_irq_callback<T: Handler + 'static>(
_irq: i32,
ptr: *mut c_void,
-) -> c_uint {
+) -> bindings::irqreturn_t {
// SAFETY: `ptr` is a pointer to `RegistrationInner` set in `Registration::new`
let inner = unsafe { &*(ptr as *const RegistrationInner) };
// SAFETY: `inner.handler` is a pointer to `T` set in `Registration::new`
@@ -284,20 +284,20 @@ pub fn synchronize(&self, dev: &Device<Bound>) -> Result {
// callback is running implies that the device has not yet been unbound.
let device = unsafe { inner.dev.as_bound() };
- T::handle(handler, device) as c_uint
+ bindings::irqreturn_t(T::handle(handler, device) as c_uint)
}
/// The value that can be returned from [`ThreadedHandler::handle`].
#[repr(u32)]
pub enum ThreadedIrqReturn {
/// The interrupt was not from this device or was not handled.
- None = bindings::irqreturn_IRQ_NONE,
+ None = bindings::irqreturn::IRQ_NONE.0,
/// The interrupt was handled by this device.
- Handled = bindings::irqreturn_IRQ_HANDLED,
+ Handled = bindings::irqreturn::IRQ_HANDLED.0,
/// The handler wants the handler thread to wake up.
- WakeThread = bindings::irqreturn_IRQ_WAKE_THREAD,
+ WakeThread = bindings::irqreturn::IRQ_WAKE_THREAD.0,
}
/// Callbacks for a threaded IRQ handler.
@@ -501,7 +501,7 @@ pub fn synchronize(&self, dev: &Device<Bound>) -> Result {
unsafe extern "C" fn handle_threaded_irq_callback<T: ThreadedHandler + 'static>(
_irq: i32,
ptr: *mut c_void,
-) -> c_uint {
+) -> bindings::irqreturn_t {
// SAFETY: `ptr` is a pointer to `RegistrationInner` set in `ThreadedRegistration::new`
let inner = unsafe { &*(ptr as *const RegistrationInner) };
// SAFETY: `inner.handler` is a pointer to `T` set in `ThreadedRegistration::new`
@@ -510,7 +510,7 @@ pub fn synchronize(&self, dev: &Device<Bound>) -> Result {
// callback is running implies that the device has not yet been unbound.
let device = unsafe { inner.dev.as_bound() };
- T::handle(handler, device) as c_uint
+ bindings::irqreturn_t(T::handle(handler, device) as c_uint)
}
/// # Safety
@@ -519,7 +519,7 @@ pub fn synchronize(&self, dev: &Device<Bound>) -> Result {
unsafe extern "C" fn thread_fn_callback<T: ThreadedHandler + 'static>(
_irq: i32,
ptr: *mut c_void,
-) -> c_uint {
+) -> bindings::irqreturn_t {
// SAFETY: `ptr` is a pointer to `RegistrationInner` set in `ThreadedRegistration::new`
let inner = unsafe { &*(ptr as *const RegistrationInner) };
// SAFETY: `inner.handler` is a pointer to `T` set in `ThreadedRegistration::new`
@@ -528,5 +528,5 @@ pub fn synchronize(&self, dev: &Device<Bound>) -> Result {
// callback is running implies that the device has not yet been unbound.
let device = unsafe { inner.dev.as_bound() };
- T::handle_threaded(handler, device) as c_uint
+ bindings::irqreturn_t(T::handle_threaded(handler, device) as c_uint)
}
diff --git a/rust/kernel/time/hrtimer.rs b/rust/kernel/time/hrtimer.rs
index 856d2d929a00..35ff2ef7d944 100644
--- a/rust/kernel/time/hrtimer.rs
+++ b/rust/kernel/time/hrtimer.rs
@@ -498,14 +498,14 @@ unsafe fn start(this: *const Self, expires: <Self::TimerMode as HrTimerMode>::Ex
#[repr(u32)]
pub enum HrTimerRestart {
/// Timer should not be restarted.
- NoRestart = bindings::hrtimer_restart_HRTIMER_NORESTART,
+ NoRestart = bindings::hrtimer_restart::HRTIMER_NORESTART.0,
/// Timer should be restarted.
- Restart = bindings::hrtimer_restart_HRTIMER_RESTART,
+ Restart = bindings::hrtimer_restart::HRTIMER_RESTART.0,
}
impl HrTimerRestart {
fn into_c(self) -> bindings::hrtimer_restart {
- self as bindings::hrtimer_restart
+ bindings::hrtimer_restart(self as c_uint)
}
}
diff --git a/scripts/Makefile.autofdo b/scripts/Makefile.autofdo
index 1caf2457e585..0b14a05726ad 100644
--- a/scripts/Makefile.autofdo
+++ b/scripts/Makefile.autofdo
@@ -18,7 +18,7 @@ ifdef CONFIG_LTO_CLANG_THIN
KBUILD_LDFLAGS += --lto-sample-profile=$(CLANG_AUTOFDO_PROFILE)
endif
KBUILD_LDFLAGS += --mllvm=-enable-fs-discriminator=true --mllvm=-improved-fs-discriminator=true -plugin-opt=thinlto
- KBUILD_LDFLAGS += -plugin-opt=-split-machine-functions
+ KBUILD_LDFLAGS += $(call ld-option,-plugin-opt=-function-splitting=all,-plugin-opt=-split-machine-functions)
endif
export CFLAGS_AUTOFDO_CLANG
diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c
index 834a3b1c2f0a..aa2b31d5598c 100644
--- a/security/apparmor/af_unix.c
+++ b/security/apparmor/af_unix.c
@@ -676,7 +676,7 @@ static void update_sk_ctx(struct sock *sk, struct aa_label *label,
if (old == plabel) {
rcu_assign_pointer(ctx->peer_lastupdate,
aa_get_label(plabel));
- } else if (aa_label_is_subset(plabel, old)) {
+ } else if (!old || aa_label_is_subset(plabel, old)) {
rcu_assign_pointer(ctx->peer_lastupdate,
aa_get_label(plabel));
rcu_assign_pointer(ctx->peer, aa_get_label(plabel));
@@ -694,13 +694,17 @@ static void update_peer_ctx(struct sock *sk, struct aa_sk_ctx *ctx,
spin_lock(&unix_sk(sk)->lock);
old = rcu_dereference_protected(ctx->peer,
lockdep_is_held(&unix_sk(sk)->lock));
- l = aa_label_merge(old, label, GFP_ATOMIC);
- if (l) {
- if (l != old) {
- rcu_assign_pointer(ctx->peer, l);
- aa_put_label(old);
- } else
- aa_put_label(l);
+ if (!old) {
+ rcu_assign_pointer(ctx->peer, aa_get_label(label));
+ } else {
+ l = aa_label_merge(old, label, GFP_ATOMIC);
+ if (l) {
+ if (l != old) {
+ rcu_assign_pointer(ctx->peer, l);
+ aa_put_label(old);
+ } else
+ aa_put_label(l);
+ }
}
spin_unlock(&unix_sk(sk)->lock);
}
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index 534a93520c56..f8f9320e266a 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -691,49 +691,50 @@ static void revalidate_tty(const struct cred *subj_cred, struct aa_label *label)
no_tty();
}
-struct cred_label {
- const struct cred *cred;
- struct aa_label *label;
-};
-
-static int match_file(const void *p, struct file *file, unsigned int fd)
+/*
+ * iterate_fd() callback, called with files->file_lock held: grab a ref
+ * on the next file so it can be revalidated without the lock held.
+ */
+static int get_next_file(const void *p, struct file *file, unsigned int fd)
{
- struct cred_label *cl = (struct cred_label *)p;
+ *(struct file **)p = get_file(file);
- if (aa_file_perm(OP_INHERIT, cl->cred, cl->label, file,
- aa_map_file_to_perms(file), IN_ATOMIC))
- return fd + 1;
- return 0;
+ return fd + 1;
}
-
/* based on selinux's flush_unauthorized_files */
void aa_inherit_files(const struct cred *cred, struct files_struct *files)
{
struct aa_label *label = aa_get_newest_cred_label(cred);
- struct cred_label cl = {
- .cred = cred,
- .label = label,
- };
- struct file *devnull = NULL;
- unsigned int n;
+ struct file *devnull = NULL, *file = NULL;
+ bool devnull_tried = false;
+ unsigned int n = 0;
revalidate_tty(cred, label);
- /* Revalidate access to inherited open files. */
- n = iterate_fd(files, 0, match_file, &cl);
- if (!n) /* none found? */
- goto out;
-
- devnull = dentry_open(&aa_null, O_RDWR, cred);
- if (IS_ERR(devnull))
- devnull = NULL;
- /* replace all the matching ones with this */
- do {
+ /*
+ * Revalidate access to inherited open files. Each file is
+ * revalidated holding only a reference to it, with no locks
+ * held, as revalidation may sleep (e.g. unix socket mediation
+ * dropping path refs, or allocating path buffers).
+ */
+ while ((n = iterate_fd(files, n, get_next_file, &file)) != 0) {
+ int error = aa_file_perm(OP_INHERIT, cred, label, file,
+ aa_map_file_to_perms(file), false);
+
+ fput(file);
+ if (!error)
+ continue;
+ if (!devnull_tried) {
+ devnull_tried = true;
+ devnull = dentry_open(&aa_null, O_RDWR, cred);
+ if (IS_ERR(devnull))
+ devnull = NULL;
+ }
+ /* replace the denied fd (n - 1) with devnull, or close it */
replace_fd(n - 1, devnull, 0);
- } while ((n = iterate_fd(files, n, match_file, &cl)) != 0);
+ }
if (devnull)
fput(devnull);
-out:
aa_put_label(label);
}
diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h
index 574be681f83e..75c49e713a74 100644
--- a/security/apparmor/include/label.h
+++ b/security/apparmor/include/label.h
@@ -162,7 +162,11 @@ do { \
#define labels_ns(X) (vec_ns(&((X)->vec[0]), (X)->size))
#define labels_set(X) (&labels_ns(X)->labels)
#define labels_view(X) labels_ns(X)
-#define labels_profile(X) ((X)->vec[(X)->size - 1])
+
+static inline struct aa_profile *labels_profile(const struct aa_label *label)
+{
+ return label->vec[label->size - 1];
+}
int aa_label_next_confined(struct aa_label *l, int i);
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
index 5c42cd01ea47..667f20363ebf 100644
--- a/security/apparmor/label.c
+++ b/security/apparmor/label.c
@@ -386,7 +386,6 @@ void aa_label_kref(struct kref *kref)
label_free_switch(label);
return;
}
- /* TODO: update labels_profile macro so it works here */
AA_BUG(label_isprofile(label) &&
on_list_rcu(&label->vec[0]->base.profiles));
AA_BUG(label_isprofile(label) &&
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index b2a4bee4e393..e4ef1057e2c0 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -397,7 +397,6 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
/* check for duplicate table entry */
if (dfa->tables[table->td_id])
goto fail;
- dfa->tables[table->td_id] = table;
data += table_size(table->td_lolen, table->td_flags);
size -= table_size(table->td_lolen, table->td_flags);
@@ -414,9 +413,9 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
if (!table)
goto fail;
}
- dfa->tables[table->td_id] = table;
break;
}
+ dfa->tables[table->td_id] = table;
table = NULL;
}
error = verify_table_headers(dfa->tables, flags);
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 70d6315a059a..230d8428b4d3 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -1163,6 +1163,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
struct aa_loaddata *rawdata_ent;
const char *op;
ssize_t count, error;
+ bool rawdata_created = false;
LIST_HEAD(lh);
op = mask & AA_MAY_REPLACE_POLICY ? OP_PROF_REPL : OP_PROF_LOAD;
@@ -1302,6 +1303,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
ent = NULL;
goto fail_lock;
}
+ rawdata_created = true;
}
list_for_each_entry(ent, &lh, list) {
if (!ent->old) {
@@ -1397,6 +1399,20 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
return udata_sz;
fail_lock:
+ /*
+ * Remove fs entries created by this load. Nothing else references
+ * them yet, and leaving them behind would leak the new profiles
+ * (their dents pin the proxy) and make the next load at the same,
+ * unbumped ns revision fail with -EEXIST on its raw_data dir.
+ * ensure children (which are at the end of the list are removed
+ * before their parent
+ */
+ list_for_each_entry_reverse(tmp, &lh, list) {
+ if (!tmp->old)
+ __aafs_profile_rmdir(tmp->new);
+ }
+ if (rawdata_created)
+ __aa_fs_remove_rawdata(udata);
mutex_unlock(&ns->lock);
/* audit cause of failure */
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 1e1792d3ff5a..391665c27ede 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -1244,6 +1244,15 @@ static bool verify_dfa_accept_index(struct aa_dfa *dfa, int table_size)
for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) {
if (ACCEPT_TABLE(dfa)[i] >= table_size)
return false;
+ /*
+ * Accept indexes for owner-conditional permissions come in
+ * pairs, so the non-owner entry at index + 1 must also be
+ * in bounds.
+ */
+ if (dfa->tables[YYTD_ID_ACCEPT2] &&
+ (ACCEPT_TABLE2(dfa)[i] & ACCEPT_FLAG_OWNER) &&
+ ACCEPT_TABLE(dfa)[i] + 1 >= table_size)
+ return false;
}
return true;
}
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index 065cdfaef1ea..e1b2da84f778 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -656,13 +656,30 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
if (retval)
return retval;
- retval = snd_compr_allocate_buffer(stream, params);
- if (retval)
- return -ENOMEM;
+ if (stream->next_track) {
+ /*
+ * total_bytes_available/transferred still refer to the
+ * current buffer; its geometry must not change.
+ */
+ if (params->buffer.fragment_size != stream->runtime->fragment_size ||
+ params->buffer.fragments != stream->runtime->fragments)
+ return -EINVAL;
+ } else {
+ retval = snd_compr_allocate_buffer(stream, params);
+ if (retval)
+ return retval;
+ }
retval = stream->ops->set_params(stream, params);
- if (retval)
+ if (retval) {
+ /* the stream stays OPEN: drop the buffer of the rejected setup */
+ if (stream->runtime->state == SNDRV_PCM_STATE_OPEN &&
+ !stream->runtime->dma_buffer_p) {
+ kfree(stream->runtime->buffer);
+ stream->runtime->buffer = NULL;
+ }
return retval;
+ }
if (stream->next_track)
return retval;
@@ -731,6 +748,9 @@ snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
* we should allow parameter change only when stream has been
* opened not in other cases
*/
+ if (stream->runtime->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+
if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
return -EFAULT;
@@ -1122,7 +1142,9 @@ static int snd_compr_task_start_prepare(struct snd_compr_task_runtime *task,
{
if (task == NULL)
return -EINVAL;
- if (task->state >= SND_COMPRESS_TASK_STATE_FINISHED)
+ /* only an unqueued or already finished task may be (re)started */
+ if (task->state != SND_COMPRESS_TASK_STATE_IDLE &&
+ task->state != SND_COMPRESS_TASK_STATE_FINISHED)
return -EBUSY;
if (utask->input_size > task->input->size)
return -EINVAL;
diff --git a/sound/core/init.c b/sound/core/init.c
index 3a45fa6fc515..2bf98de846f6 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -1141,12 +1141,12 @@ EXPORT_SYMBOL(snd_card_file_remove);
int snd_power_ref_and_wait(struct snd_card *card)
{
snd_power_ref(card);
- if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0)
- return 0;
- wait_event_cmd(card->power_sleep,
- card->shutdown ||
- snd_power_get_state(card) == SNDRV_CTL_POWER_D0,
- snd_power_unref(card), snd_power_ref(card));
+ if (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) {
+ wait_event_cmd(card->power_sleep,
+ card->shutdown ||
+ snd_power_get_state(card) == SNDRV_CTL_POWER_D0,
+ snd_power_unref(card), snd_power_ref(card));
+ }
if (card->shutdown) {
snd_power_unref(card);
return -ENODEV;
diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h
index 935cf3df0b30..571186db4fa5 100644
--- a/sound/core/seq/oss/seq_oss_device.h
+++ b/sound/core/seq/oss/seq_oss_device.h
@@ -72,6 +72,7 @@ struct seq_oss_devinfo {
int cseq; /* sequencer client number */
int port; /* sequencer port number */
int queue; /* sequencer queue number */
+ bool initialized;
struct snd_seq_addr addr; /* address of this device */
diff --git a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c
index 122735862044..20f4c5ce763c 100644
--- a/sound/core/seq/oss/seq_oss_event.c
+++ b/sound/core/seq/oss/seq_oss_event.c
@@ -425,6 +425,9 @@ snd_seq_oss_event_input(struct snd_seq_event *ev, int direct, void *private_data
struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data;
union evrec *rec;
+ if (!dp || !smp_load_acquire(&dp->initialized))
+ return 0; /* ignored */
+
if (ev->type != SNDRV_SEQ_EVENT_ECHO)
return snd_seq_oss_midi_input(ev, direct, private_data);
diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
index 973f057eb731..102c9358ce48 100644
--- a/sound/core/seq/oss/seq_oss_init.c
+++ b/sound/core/seq/oss/seq_oss_init.c
@@ -258,6 +258,7 @@ snd_seq_oss_open(struct file *file, int level)
client_table[dp->index] = dp;
num_clients++;
+ smp_store_release(&dp->initialized, true);
return 0;
@@ -403,6 +404,7 @@ snd_seq_oss_release(struct seq_oss_devinfo *dp)
client_table[dp->index] = NULL;
num_clients--;
+ smp_store_release(&dp->initialized, false);
snd_seq_oss_reset(dp);
diff --git a/sound/core/ump_convert.c b/sound/core/ump_convert.c
index 85cc67de6330..0a7972d91ec5 100644
--- a/sound/core/ump_convert.c
+++ b/sound/core/ump_convert.c
@@ -323,7 +323,7 @@ static int fill_rpn(struct ump_cvt_to_ump_bank *cc,
cc->cc_data_lsb);
reset_rpn(cc);
- return 1;
+ return 8;
}
/* convert to a MIDI 1.0 Channel Voice message */
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index d08027681c37..fd8d6b474e46 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -365,11 +365,14 @@ static int loopback_check_format(struct loopback_cable *cable, int stream)
runtime->channels != cruntime->channels ||
is_access_interleaved(runtime->access) !=
is_access_interleaved(cruntime->access);
- if (!check)
- return 0;
- if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ /*
+ * Matching params also go on to the update below, so that
+ * the setup controls follow a playback that joins an open
+ * capture.
+ */
+ if (check && stream == SNDRV_PCM_STREAM_CAPTURE)
return -EIO;
- else if (cruntime->state == SNDRV_PCM_STATE_RUNNING) {
+ else if (check && cruntime->state == SNDRV_PCM_STATE_RUNNING) {
/* close must not free the peer runtime below */
atomic_inc(&cable->stop_count);
stop_capture = true;
@@ -988,6 +991,18 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream)
return !substream->stream;
}
+/*
+ * In notify mode the playback side may change format, rate, channels and,
+ * with a sound timer, the period size. A running capture that no longer
+ * matches is stopped by loopback_check_format().
+ * call in loopback->cable_lock
+ */
+static bool loopback_playback_unconstrained(struct loopback_pcm *dpcm)
+{
+ return dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ get_notify(dpcm);
+}
+
static int rule_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
@@ -997,6 +1012,8 @@ static int rule_format(struct snd_pcm_hw_params *params,
snd_mask_none(&m);
scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ if (loopback_playback_unconstrained(dpcm))
+ return 0;
m.bits[0] = (u_int32_t)cable->hw.formats;
m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
}
@@ -1011,6 +1028,8 @@ static int rule_rate(struct snd_pcm_hw_params *params,
struct snd_interval t;
scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ if (loopback_playback_unconstrained(dpcm))
+ return 0;
t.min = cable->hw.rate_min;
t.max = cable->hw.rate_max;
}
@@ -1027,6 +1046,8 @@ static int rule_channels(struct snd_pcm_hw_params *params,
struct snd_interval t;
scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ if (loopback_playback_unconstrained(dpcm))
+ return 0;
t.min = cable->hw.channels_min;
t.max = cable->hw.channels_max;
}
@@ -1043,6 +1064,8 @@ static int rule_period_bytes(struct snd_pcm_hw_params *params,
struct snd_interval t;
scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ if (loopback_playback_unconstrained(dpcm))
+ return 0;
t.min = cable->hw.period_bytes_min;
t.max = cable->hw.period_bytes_max;
}
diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
index 4af89822bf32..94213c1ff170 100644
--- a/sound/drivers/mpu401/mpu401_uart.c
+++ b/sound/drivers/mpu401/mpu401_uart.c
@@ -155,20 +155,20 @@ static void snd_mpu401_uart_timer(struct timer_list *t)
scoped_guard(spinlock_irqsave, &mpu->timer_lock) {
/*mpu->mode |= MPU401_MODE_TIMER;*/
- mod_timer(&mpu->timer, 1 + jiffies);
+ if (mpu->timer_invoked)
+ mod_timer(&mpu->timer, 1 + jiffies);
}
if (mpu->rmidi)
_snd_mpu401_uart_interrupt(mpu);
}
/*
- * initialize the timer callback if not programmed yet
+ * arm the timer if it is not already active
*/
static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input)
{
guard(spinlock_irqsave)(&mpu->timer_lock);
if (mpu->timer_invoked == 0) {
- timer_setup(&mpu->timer, snd_mpu401_uart_timer, 0);
mod_timer(&mpu->timer, 1 + jiffies);
}
mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER :
@@ -474,6 +474,7 @@ static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
struct snd_mpu401 *mpu = rmidi->private_data;
if (mpu->irq >= 0)
free_irq(mpu->irq, (void *) mpu);
+ timer_shutdown_sync(&mpu->timer);
release_and_free_resource(mpu->res);
kfree(mpu);
}
@@ -528,6 +529,7 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
spin_lock_init(&mpu->input_lock);
spin_lock_init(&mpu->output_lock);
spin_lock_init(&mpu->timer_lock);
+ timer_setup(&mpu->timer, snd_mpu401_uart_timer, 0);
mpu->hardware = hardware;
mpu->irq = -1;
mpu->rmidi = rmidi;
diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c
index 1ed6d4f0cb62..288a873a27e6 100644
--- a/sound/drivers/mtpav.c
+++ b/sound/drivers/mtpav.c
@@ -443,7 +443,7 @@ static void snd_mtpav_output_trigger(struct snd_rawmidi_substream *substream, in
scoped_guard(spinlock_irqsave, &mtp_card->spinlock) {
if (up) {
- if ((portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) {
+ if (!(portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) {
if (mtp_card->istimer++ == 0)
snd_mtpav_add_output_timer(mtp_card);
portp->mode |= MTPAV_MODE_OUTPUT_TRIGGERED;
diff --git a/sound/hda/codecs/hdmi/eld.c b/sound/hda/codecs/hdmi/eld.c
index 1464fd1c675b..fc6fe1b0873c 100644
--- a/sound/hda/codecs/hdmi/eld.c
+++ b/sound/hda/codecs/hdmi/eld.c
@@ -155,9 +155,11 @@ void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
e->aud_synch_delay = val;
else if (!strcmp(name, "speakers"))
e->spk_alloc = val;
- else if (!strcmp(name, "sad_count"))
+ else if (!strcmp(name, "sad_count")) {
+ if (val > ELD_MAX_SAD)
+ continue;
e->sad_count = val;
- else if (!strncmp(name, "sad", 3)) {
+ } else if (!strncmp(name, "sad", 3)) {
sname = name + 4;
n = name[3] - '0';
if (name[4] >= '0' && name[4] <= '9') {
diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c
index 480d1c5f129f..a30072139f35 100644
--- a/sound/hda/codecs/hdmi/hdmi.c
+++ b/sound/hda/codecs/hdmi/hdmi.c
@@ -983,7 +983,8 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
snd_hda_set_dev_select(codec, pin_nid, dev_id);
if (spec->intel_hsw_fixup) {
- conns = spec->num_cvts;
+ /* spec->cvt_nids[] only has ARRAY_SIZE(spec->cvt_nids) slots */
+ conns = min_t(int, spec->num_cvts, ARRAY_SIZE(spec->cvt_nids));
memcpy(per_pin->mux_nids, spec->cvt_nids,
sizeof(hda_nid_t) * conns);
} else {
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index 03265550c0ee..404a93f552cb 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -7222,7 +7222,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x1043, 0x1664, "ASUS ExpertBook PM3406CHA", ALC256_FIXUP_ASUS_PM3406CHA),
SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC294_FIXUP_ASUS_SPI_HEADSET_MIC),
SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401),
SND_PCI_QUIRK(0x1043, 0x16d3, "ASUS UX5304VA", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
@@ -7723,6 +7723,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX),
SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX),
SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME),
+ SND_PCI_QUIRK(0x2782, 0x1718, "Acer Aspire AL16-31P", ALC269VC_FIXUP_INFINIX_Y4_MAX),
SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK),
SND_PCI_QUIRK(0x2782, 0xa128, "Positivo N15RPE-S", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14),
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
index ebfc08b5fb28..6eb9e4b8ba2e 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
@@ -375,6 +375,11 @@ static int tas2563_save_calibration(struct tas2781_hda *h)
i, j, status);
return -EINVAL;
}
+ if (*(uint32_t *)&data[offset] == 0) {
+ dev_warn(p->dev,
+ "Dev %d: Caldat[%d] is zero\n", i, j);
+ return -EINVAL;
+ }
bedata = cpu_to_be32(*(uint32_t *)&data[offset]);
memcpy(&data[offset], &bedata, sizeof(bedata));
offset += TAS2563_CAL_DATA_SIZE;
diff --git a/sound/hda/codecs/via.c b/sound/hda/codecs/via.c
index 6becea9bb810..b35eb727cf2a 100644
--- a/sound/hda/codecs/via.c
+++ b/sound/hda/codecs/via.c
@@ -933,6 +933,7 @@ static const struct hda_fixup via_fixups[] = {
static const struct hda_quirk vt2002p_fixups[] = {
SND_PCI_QUIRK(0x1043, 0x13f7, "Asus B23E", VIA_FIXUP_POWER_SAVE),
SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
+ SND_PCI_QUIRK(0x1043, 0x1c33, "Asus X55A", VIA_FIXUP_POWER_SAVE),
SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", VIA_FIXUP_POWER_SAVE),
{}
diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c
index 7869f673894f..e6548e03a32c 100644
--- a/sound/hda/common/codec.c
+++ b/sound/hda/common/codec.c
@@ -1806,6 +1806,12 @@ int snd_hda_codec_reset(struct hda_codec *codec)
{
struct hda_bus *bus = codec->bus;
+ /* ASoC: driver unbind frees the card, incl. the hwdep issuing the reset */
+ if (bus->core.ext_ops) {
+ codec_err(codec, "Codec reset is not supported with ASoC\n");
+ return -EOPNOTSUPP;
+ }
+
if (snd_hda_lock_devices(bus) < 0)
return -EBUSY;
diff --git a/sound/hda/common/sysfs.c b/sound/hda/common/sysfs.c
index f8c8483fd5e5..121a54ea3150 100644
--- a/sound/hda/common/sysfs.c
+++ b/sound/hda/common/sysfs.c
@@ -117,6 +117,8 @@ static int clear_codec(struct hda_codec *codec)
int err;
err = snd_hda_codec_reset(codec);
+ if (err == -EOPNOTSUPP)
+ return err;
if (err < 0) {
codec_err(codec, "The codec is being used, can't free.\n");
return err;
@@ -132,6 +134,8 @@ static int reconfig_codec(struct hda_codec *codec)
CLASS(snd_hda_power, pm)(codec);
codec_info(codec, "hda-codec: reconfiguring\n");
err = snd_hda_codec_reset(codec);
+ if (err == -EOPNOTSUPP)
+ return err;
if (err < 0) {
codec_err(codec,
"The codec is being used, can't reconfigure.\n");
diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c
index 077fdf2181c1..e3480f23f7f3 100644
--- a/sound/mips/sgio2audio.c
+++ b/sound/mips/sgio2audio.c
@@ -742,7 +742,8 @@ static struct {
/* ALSA driver */
-static int snd_sgio2audio_free(struct snd_sgio2audio *chip)
+static int snd_sgio2audio_free(struct snd_sgio2audio *chip,
+ unsigned int irq_count)
{
int i;
@@ -752,7 +753,7 @@ static int snd_sgio2audio_free(struct snd_sgio2audio *chip)
writeq(0, &mace->perif.audio.control);
/* release IRQ's */
- for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++)
+ for (i = 0; i < irq_count; i++)
free_irq(snd_sgio2_isr_table[i].irq,
&chip->channel[snd_sgio2_isr_table[i].idx]);
@@ -768,7 +769,7 @@ static int snd_sgio2audio_dev_free(struct snd_device *device)
{
struct snd_sgio2audio *chip = device->device_data;
- return snd_sgio2audio_free(chip);
+ return snd_sgio2audio_free(chip, ARRAY_SIZE(snd_sgio2_isr_table));
}
static const struct snd_device_ops ops = {
@@ -819,7 +820,7 @@ static int snd_sgio2audio_create(struct snd_card *card,
0,
snd_sgio2_isr_table[i].desc,
&chip->channel[snd_sgio2_isr_table[i].idx])) {
- snd_sgio2audio_free(chip);
+ snd_sgio2audio_free(chip, i);
printk(KERN_ERR "sgio2audio: cannot allocate irq %d\n",
snd_sgio2_isr_table[i].irq);
return -EBUSY;
@@ -843,13 +844,13 @@ static int snd_sgio2audio_create(struct snd_card *card,
/* initialize the AD1843 codec */
err = ad1843_init(&chip->ad1843);
if (err < 0) {
- snd_sgio2audio_free(chip);
+ snd_sgio2audio_free(chip, ARRAY_SIZE(snd_sgio2_isr_table));
return err;
}
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
- snd_sgio2audio_free(chip);
+ snd_sgio2audio_free(chip, ARRAY_SIZE(snd_sgio2_isr_table));
return err;
}
*rchip = chip;
diff --git a/sound/oss/dmasound/dmasound_q40.c b/sound/oss/dmasound/dmasound_q40.c
index e25a78dd1bf2..339311e5d807 100644
--- a/sound/oss/dmasound/dmasound_q40.c
+++ b/sound/oss/dmasound/dmasound_q40.c
@@ -49,9 +49,8 @@ static int Q40SetFormat(int format);
static int Q40SetVolume(int volume);
static void Q40PlayNextFrame(int index);
static void Q40Play(void);
-static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
-static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
-static void Q40Interrupt(void);
+static irqreturn_t q40_audio_interrupt(int irq, void *dummy);
+static void q40_frame_done(void);
/*** Mid level stuff *********************************************************/
@@ -372,8 +371,8 @@ static void Q40Free(void *ptr, unsigned int size)
static int __init Q40IrqInit(void)
{
/* Register interrupt handler. */
- if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
- "DMA sound", Q40Interrupt))
+ if (request_irq(Q40_IRQ_SAMPLE, q40_audio_interrupt, 0,
+ "DMA sound", q40_frame_done))
return 0;
return(1);
@@ -384,7 +383,7 @@ static int __init Q40IrqInit(void)
static void Q40IrqCleanUp(void)
{
master_outb(0,SAMPLE_ENABLE_REG);
- free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
+ free_irq(Q40_IRQ_SAMPLE, q40_frame_done);
}
#endif /* MODULE */
@@ -403,7 +402,6 @@ static void Q40PlayNextFrame(int index)
u_char *start;
u_long size;
u_char speed;
- int error;
/* used by Q40Play() if all doubts whether there really is something
* to be played are already wiped out.
@@ -420,15 +418,6 @@ static void Q40PlayNextFrame(int index)
speed=(dmasound.hard.speed==10000 ? 0 : 1);
master_outb( 0,SAMPLE_ENABLE_REG);
- free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
- if (dmasound.soft.stereo)
- error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
- "Q40 sound", Q40Interrupt);
- else
- error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
- "Q40 sound", Q40Interrupt);
- if (error && printk_ratelimit())
- pr_err("Couldn't register sound interrupt\n");
master_outb( speed, SAMPLE_RATE_REG);
master_outb( 1,SAMPLE_CLEAR_REG);
@@ -456,31 +445,35 @@ static void Q40Play(void)
spin_unlock_irqrestore(&dmasound.lock, flags);
}
-static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
-{
- spin_lock(&dmasound.lock);
- if (q40_sc>1){
- *DAC_LEFT=*q40_pp++;
- *DAC_RIGHT=*q40_pp++;
- q40_sc -=2;
- master_outb(1,SAMPLE_CLEAR_REG);
- }else Q40Interrupt();
- spin_unlock(&dmasound.lock);
- return IRQ_HANDLED;
-}
-static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
+static irqreturn_t q40_audio_interrupt(int irq, void *dummy)
{
+ bool frame_done = false;
+
spin_lock(&dmasound.lock);
- if (q40_sc>0){
- *DAC_LEFT=*q40_pp;
- *DAC_RIGHT=*q40_pp++;
- q40_sc --;
- master_outb(1,SAMPLE_CLEAR_REG);
- }else Q40Interrupt();
+ if (dmasound.hard.stereo) {
+ if (q40_sc > 1) {
+ *DAC_LEFT = *q40_pp++;
+ *DAC_RIGHT = *q40_pp++;
+ q40_sc -= 2;
+ master_outb(1, SAMPLE_CLEAR_REG);
+ } else {
+ frame_done = true;
+ }
+ } else if (q40_sc > 0) {
+ *DAC_LEFT = *q40_pp;
+ *DAC_RIGHT = *q40_pp++;
+ q40_sc--;
+ master_outb(1, SAMPLE_CLEAR_REG);
+ } else {
+ frame_done = true;
+ }
spin_unlock(&dmasound.lock);
+ if (frame_done)
+ q40_frame_done();
return IRQ_HANDLED;
}
-static void Q40Interrupt(void)
+
+static void q40_frame_done(void)
{
if (!write_sq.active) {
/* playing was interrupted and sq_reset() has already cleared
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
index 3d34575a0e8f..ba290f3a50e4 100644
--- a/sound/pci/cs46xx/dsp_spos.c
+++ b/sound/pci/cs46xx/dsp_spos.c
@@ -395,7 +395,7 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m
dev_dbg(chip->card->dev,
"dsp_spos: module got no code segment\n");
} else {
- if (ins->code.offset + code->size > DSP_CODE_BYTE_SIZE) {
+ if ((u64)ins->code.offset + code->size > DSP_CODE_BYTE_SIZE / 4) {
dev_err(chip->card->dev,
"dsp_spos: no space available in DSP\n");
return -ENOMEM;
diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c
index 6b55610ad535..b17c4539f4e4 100644
--- a/sound/soc/codecs/arizona-jack.c
+++ b/sound/soc/codecs/arizona-jack.c
@@ -713,6 +713,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_priv *info)
snd_soc_jack_report(info->jack, SND_JACK_HEADPHONE,
SND_JACK_LINEOUT | SND_JACK_HEADPHONE);
+ pm_runtime_put_autosuspend(arizona->dev);
info->hpdet_active = false;
}
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
index b4d93b9404bb..ac28b3cadb69 100644
--- a/sound/soc/codecs/hdac_hda.c
+++ b/sound/soc/codecs/hdac_hda.c
@@ -517,6 +517,9 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
snd_hdac_display_power(hdev->bus,
HDA_CODEC_IDX_CONTROLLER, false);
+ /* The codec is in D0, the status is RPM_SUSPENDED after a previous unbind */
+ pm_runtime_set_active(&hdev->dev);
+
/* match for forbid call in snd_hda_codec_device_new() */
pm_runtime_allow(&hdev->dev);
@@ -557,6 +560,13 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
return;
}
+ /*
+ * Resume the codec to have its link reference (dropped below) and
+ * codec_powered bit in the same state as after the probe.
+ */
+ pm_runtime_get_sync(&hdev->dev);
+
+ /* prevent runtime PM callbacks while the link and the codec are torn down */
pm_runtime_disable(&hdev->dev);
snd_hdac_ext_bus_link_put(hdev->bus, hlink);
@@ -564,6 +574,12 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
driver->ops->remove(codec);
snd_hda_codec_cleanup_for_unbind(codec);
+
+ /* Balances the disable above, cleanup_for_unbind keeps runtime PM disabled */
+ pm_runtime_enable(&hdev->dev);
+ pm_runtime_put_noidle(&hdev->dev);
+ /* The codec is cleaned up, system suspend must skip it from now on */
+ pm_runtime_set_suspended(&hdev->dev);
}
static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = {
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index 8350b02b575f..f094225ba40a 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -1129,27 +1129,35 @@ static const struct regmap_config wsa_regmap_config = {
int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode)
{
struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ unsigned int comp1_ctl3, comp1_ctl7;
+ u8 comp_val, boost_val;
wsa->spkr_mode = mode;
- switch (mode) {
- case WSA_MACRO_SPKR_MODE_1:
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x00);
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x00);
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x00);
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x00);
- snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x44);
- snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x44);
- break;
- default:
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x80);
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x80);
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x01);
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x01);
- snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x58);
- snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x58);
- break;
+ /* Compander1 CTL3/CTL7 moved on v2.5+ silicon. */
+ if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5) {
+ comp1_ctl3 = CDC_2_5_WSA_COMPANDER1_CTL3;
+ comp1_ctl7 = CDC_2_5_WSA_COMPANDER1_CTL7;
+ } else {
+ comp1_ctl3 = CDC_WSA_COMPANDER1_CTL3;
+ comp1_ctl7 = CDC_WSA_COMPANDER1_CTL7;
}
+
+ if (mode == WSA_MACRO_SPKR_MODE_1) {
+ comp_val = 0x00;
+ boost_val = 0x44;
+ } else {
+ comp_val = 0x80;
+ boost_val = 0x58;
+ }
+
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, comp_val);
+ snd_soc_component_update_bits(component, comp1_ctl3, 0x80, comp_val);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, comp_val ? 0x01 : 0x00);
+ snd_soc_component_update_bits(component, comp1_ctl7, 0x01, comp_val ? 0x01 : 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, boost_val);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, boost_val);
+
return 0;
}
EXPORT_SYMBOL(wsa_macro_set_spkr_mode);
diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c
index 08cc52b374a9..0275fb420ef2 100644
--- a/sound/soc/codecs/pcm6240.c
+++ b/sound/soc/codecs/pcm6240.c
@@ -2034,8 +2034,6 @@ static const struct regmap_config pcmdevice_i2c_regmap = {
static void pcmdevice_remove(struct pcmdevice_priv *pcm_dev)
{
- if (pcm_dev->irq)
- free_irq(pcm_dev->irq, pcm_dev);
mutex_destroy(&pcm_dev->codec_lock);
}
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
index 1d80a4b862e2..1147b332c83a 100644
--- a/sound/soc/codecs/rt5682s.c
+++ b/sound/soc/codecs/rt5682s.c
@@ -3300,8 +3300,8 @@ static void rt5682s_i2c_shutdown(struct i2c_client *client)
struct rt5682s_priv *rt5682s = i2c_get_clientdata(client);
disable_irq(client->irq);
- cancel_delayed_work_sync(&rt5682s->jack_detect_work);
- cancel_delayed_work_sync(&rt5682s->jd_check_work);
+ disable_delayed_work_sync(&rt5682s->jack_detect_work);
+ disable_delayed_work_sync(&rt5682s->jd_check_work);
rt5682s_reset(rt5682s);
}
diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c
index bc5b872f1e42..bbbea9af73cd 100644
--- a/sound/soc/codecs/rt712-sdca-dmic.c
+++ b/sound/soc/codecs/rt712-sdca-dmic.c
@@ -247,7 +247,7 @@ static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
struct rt712_sdca_dmic_kctrl_priv *p =
(struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
unsigned int regvalue, ctl, i;
@@ -278,7 +278,7 @@ static int rt712_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt712_sdca_dmic_kctrl_priv *p =
(struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
- struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
unsigned int gain_val[4];
unsigned int i, adc_vol_flag = 0, changed = 0;
unsigned int regvalue[4];
diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c
index 5c0a76a4a106..1a815de90909 100644
--- a/sound/soc/codecs/tlv320aic32x4-clk.c
+++ b/sound/soc/codecs/tlv320aic32x4-clk.c
@@ -320,6 +320,9 @@ static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate,
struct clk_aic32x4 *div = to_clk_aic32x4(hw);
u8 divisor;
+ if (!rate)
+ return -EINVAL;
+
divisor = DIV_ROUND_UP(parent_rate, rate);
if (divisor > AIC32X4_DIV_MAX)
return -EINVAL;
@@ -333,8 +336,11 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw,
{
unsigned long divisor;
+ if (!req->rate)
+ return -EINVAL;
+
divisor = DIV_ROUND_UP(req->best_parent_rate, req->rate);
- if (divisor > AIC32X4_DIV_MAX)
+ if (!divisor || divisor > AIC32X4_DIV_MAX)
return -EINVAL;
req->rate = DIV_ROUND_UP(req->best_parent_rate, divisor);
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 1c050b8c19de..d607400b3976 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -5092,6 +5092,13 @@ static int wcd9335_slim_probe(struct slim_device *slim)
return 0;
}
+static void wcd9335_put_device_action(void *data)
+{
+ struct device *dev = data;
+
+ put_device(dev);
+}
+
static int wcd9335_slim_status(struct slim_device *sdev,
enum slim_device_status status)
{
@@ -5116,28 +5123,40 @@ static int wcd9335_slim_status(struct slim_device *sdev,
return -EINVAL;
}
+ ret = devm_add_action_or_reset(dev, wcd9335_put_device_action,
+ &wcd->slim_ifc_dev->dev);
+ if (ret)
+ return ret;
+
slim_get_logical_addr(wcd->slim_ifc_dev);
wcd->regmap = regmap_init_slimbus(sdev, &wcd9335_regmap_config);
- if (IS_ERR(wcd->regmap))
+ if (IS_ERR(wcd->regmap)) {
+ put_device(&wcd->slim_ifc_dev->dev);
return dev_err_probe(dev, PTR_ERR(wcd->regmap),
"Failed to allocate slim register map\n");
+ }
wcd->if_regmap = regmap_init_slimbus(wcd->slim_ifc_dev,
&wcd9335_ifc_regmap_config);
- if (IS_ERR(wcd->if_regmap))
+ if (IS_ERR(wcd->if_regmap)) {
+ put_device(&wcd->slim_ifc_dev->dev);
return dev_err_probe(dev, PTR_ERR(wcd->if_regmap),
"Failed to allocate ifc register map\n");
+ }
ret = wcd9335_bring_up(wcd);
if (ret) {
dev_err(dev, "Failed to bringup WCD9335\n");
+ put_device(&wcd->slim_ifc_dev->dev);
return ret;
}
ret = wcd9335_irq_init(wcd);
- if (ret)
+ if (ret) {
+ put_device(&wcd->slim_ifc_dev->dev);
return ret;
+ }
wcd9335_probe(wcd);
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 128c3a59beac..80b1bb7e1774 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -3533,6 +3533,7 @@ int wm8994_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *
break;
default:
dev_warn(component->dev, "Invalid MICBIAS %d\n", micbias);
+ pm_runtime_put(component->dev);
return -EINVAL;
}
@@ -4352,6 +4353,7 @@ static int wm8994_component_probe(struct snd_soc_component *component)
ret = regmap_read(control->regmap, WM8994_GPIO_1, ®);
if (ret < 0) {
dev_err(component->dev, "Failed to read GPIO1 state: %d\n", ret);
+ pm_runtime_put(component->dev);
goto err_irq;
}
if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) {
@@ -4364,6 +4366,7 @@ static int wm8994_component_probe(struct snd_soc_component *component)
ret = regmap_read(control->regmap, WM8994_GPIO_6, ®);
if (ret < 0) {
dev_err(component->dev, "Failed to read GPIO6 state: %d\n", ret);
+ pm_runtime_put(component->dev);
goto err_irq;
}
if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) {
diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c
index 5f2ced650dcc..6fb50b6e5e18 100644
--- a/sound/soc/codecs/wsa883x.c
+++ b/sound/soc/codecs/wsa883x.c
@@ -455,10 +455,11 @@
/*
* Device will report senseless data in many cases, so discard any measurements
- * outside of valid range.
+ * outside of the two-point trim range. Do not narrow this to ambient: a
+ * speaker that has been playing easily runs well above 45 degC.
*/
-#define WSA883X_LOW_TEMP_THRESHOLD 5
-#define WSA883X_HIGH_TEMP_THRESHOLD 45
+#define WSA883X_LOW_TEMP_THRESHOLD WSA883X_T1_TEMP
+#define WSA883X_HIGH_TEMP_THRESHOLD WSA883X_T2_TEMP
struct wsa883x_priv {
struct regmap *regmap;
@@ -1430,7 +1431,7 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp)
unsigned int dmeas_msb = 0, dmeas_lsb = 0;
int d1, d2, dmeas;
unsigned int mask;
- int ret, range;
+ int ret;
long val;
guard(mutex)(&wsa883x->sp_lock);
@@ -1493,8 +1494,8 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp)
}
val = WSA883X_T1_TEMP + (((dmeas - d1) * (WSA883X_T2_TEMP - WSA883X_T1_TEMP)) / (d2 - d1));
- range = WSA883X_HIGH_TEMP_THRESHOLD - WSA883X_LOW_TEMP_THRESHOLD;
- if (in_range(val, WSA883X_LOW_TEMP_THRESHOLD, range)) {
+ if (val >= WSA883X_LOW_TEMP_THRESHOLD &&
+ val <= WSA883X_HIGH_TEMP_THRESHOLD) {
wsa883x->temperature = val;
*temp = val * 1000;
ret = 0;
diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c
index 5ecd7962d77a..d941e9826ba0 100644
--- a/sound/soc/codecs/wsa884x.c
+++ b/sound/soc/codecs/wsa884x.c
@@ -720,10 +720,11 @@
/*
* Device will report senseless data in many cases, so discard any measurements
- * outside of valid range.
+ * outside of the two-point trim range. Do not narrow this to ambient: a
+ * speaker that has been playing easily runs well above 45 degC.
*/
-#define WSA884X_LOW_TEMP_THRESHOLD 5
-#define WSA884X_HIGH_TEMP_THRESHOLD 45
+#define WSA884X_LOW_TEMP_THRESHOLD WSA884X_T1_TEMP
+#define WSA884X_HIGH_TEMP_THRESHOLD WSA884X_T2_TEMP
struct wsa884x_priv {
struct regmap *regmap;
@@ -1931,8 +1932,8 @@ static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp)
dev_dbg(wsa884x->dev, "Measured temp %ld (dmeas=%d, d1=%d, d2=%d)\n",
val, dmeas, d1, d2);
- if ((val > WSA884X_LOW_TEMP_THRESHOLD) &&
- (val < WSA884X_HIGH_TEMP_THRESHOLD)) {
+ if (val >= WSA884X_LOW_TEMP_THRESHOLD &&
+ val <= WSA884X_HIGH_TEMP_THRESHOLD) {
wsa884x->temperature = val;
*temp = val * 1000;
ret = 0;
diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c
index d7a823384c08..08683f989cee 100644
--- a/sound/soc/fsl/fsl_xcvr.c
+++ b/sound/soc/fsl/fsl_xcvr.c
@@ -3,6 +3,7 @@
#include <linux/bitrev.h>
#include <linux/clk.h>
+#include <linux/devm-helpers.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -57,6 +58,7 @@ struct fsl_xcvr {
struct snd_aes_iec958 tx_iec958;
u8 cap_ds[FSL_XCVR_CAPDS_SIZE];
struct work_struct work_rst;
+ int irq;
spinlock_t lock; /* Protect hw_reset and trigger */
struct snd_pcm_hw_constraint_list spdif_constr_rates;
u32 spdif_constr_rates_list[SPDIF_NUM_RATES];
@@ -1555,7 +1557,7 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
struct fsl_xcvr *xcvr;
struct resource *rx_res, *tx_res;
void __iomem *regs;
- int ret, irq;
+ int ret;
xcvr = devm_kzalloc(dev, sizeof(*xcvr), GFP_KERNEL);
if (!xcvr)
@@ -1643,12 +1645,22 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(xcvr->reset),
"failed to get XCVR reset control\n");
+ /*
+ * irq0_isr() schedules work_rst. Prepare the work and its lock
+ * before the IRQ, and register the cancel action first so a failed
+ * probe frees the IRQ and then cancels any queued work.
+ */
+ spin_lock_init(&xcvr->lock);
+ ret = devm_work_autocancel(dev, &xcvr->work_rst, reset_rx_work);
+ if (ret)
+ return ret;
+
/* get IRQs */
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
+ xcvr->irq = platform_get_irq(pdev, 0);
+ if (xcvr->irq < 0)
+ return xcvr->irq;
- ret = devm_request_irq(dev, irq, irq0_isr, 0, pdev->name, xcvr);
+ ret = devm_request_irq(dev, xcvr->irq, irq0_isr, 0, pdev->name, xcvr);
if (ret)
return dev_err_probe(dev, ret, "failed to claim IRQ0\n");
@@ -1689,8 +1701,6 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
fsl_xcvr_comp.name);
}
- INIT_WORK(&xcvr->work_rst, reset_rx_work);
- spin_lock_init(&xcvr->lock);
return ret;
}
@@ -1698,6 +1708,8 @@ static void fsl_xcvr_remove(struct platform_device *pdev)
{
struct fsl_xcvr *xcvr = dev_get_drvdata(&pdev->dev);
+ /* Free the IRQ first so irq0_isr() cannot requeue work_rst. */
+ devm_free_irq(&pdev->dev, xcvr->irq, xcvr);
cancel_work_sync(&xcvr->work_rst);
pm_runtime_disable(&pdev->dev);
}
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 257180630475..92a68dbd841e 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -253,6 +253,11 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
return 0;
}
+static void sst_unregister_platform_device(void *data)
+{
+ platform_device_unregister(data);
+}
+
static int sst_acpi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -341,7 +346,10 @@ static int sst_acpi_probe(struct platform_device *pdev)
pdata->platform);
return PTR_ERR(plat_dev);
}
-
+ ret = devm_add_action_or_reset(dev, sst_unregister_platform_device,
+ plat_dev);
+ if (ret)
+ return ret;
/*
* Create platform device for sst machine driver,
* pass machine info as pdata
@@ -354,6 +362,10 @@ static int sst_acpi_probe(struct platform_device *pdev)
return PTR_ERR(mdev);
}
+ ret = devm_add_action_or_reset(dev, sst_unregister_platform_device,
+ mdev);
+ if (ret)
+ return ret;
/* Fill sst platform data */
ctx->pdata = pdata;
strcpy(ctx->firmware_name, mach->fw_filename);
diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c
index 7b59d514b432..d2cb0ccd3456 100644
--- a/sound/soc/qcom/qdsp6/q6afe.c
+++ b/sound/soc/qcom/qdsp6/q6afe.c
@@ -377,6 +377,7 @@ struct q6afe {
struct q6core_svc_api_info ainfo;
struct mutex lock;
struct aprv2_ibasic_rsp_result_t result;
+ uint32_t vote_result;
wait_queue_head_t wait;
struct list_head port_list;
spinlock_t port_list_lock;
@@ -966,13 +967,14 @@ static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data)
struct aprv2_ibasic_rsp_result_t *res;
struct apr_hdr *hdr = &data->hdr;
struct q6afe_port *port;
+ uint32_t *vote_res;
if (!data->payload_size)
return 0;
- res = data->payload;
switch (hdr->opcode) {
case APR_BASIC_RSP_RESULT: {
+ res = data->payload;
if (res->status) {
dev_err(afe->dev, "cmd = 0x%x returned error = 0x%x\n",
res->opcode, res->status);
@@ -999,8 +1001,10 @@ static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data)
}
break;
case AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST:
+ vote_res = data->payload;
afe->result.opcode = hdr->opcode;
- afe->result.status = res->status;
+ afe->result.status = 0;
+ afe->vote_result = *vote_res;
wake_up(&afe->wait);
break;
default:
@@ -1910,6 +1914,8 @@ int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST);
if (ret)
dev_err(afe->dev, "AFE failed to vote (%d)\n", hw_block_id);
+ else
+ *client_handle = afe->vote_result;
kfree(pkt);
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index 9122876e00a2..50d5d2143daa 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -223,6 +223,7 @@ int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_a
mutex_lock(&graph->lock);
data->dsp_buf = 0;
+ atomic_set(&data->hw_ptr, 0);
if (data->buf) {
mutex_unlock(&graph->lock);
diff --git a/sound/soc/sdw_utils/soc_sdw_rt711.c b/sound/soc/sdw_utils/soc_sdw_rt711.c
index 4aa93fdefef6..1d020d44d474 100644
--- a/sound/soc/sdw_utils/soc_sdw_rt711.c
+++ b/sound/soc/sdw_utils/soc_sdw_rt711.c
@@ -123,6 +123,7 @@ int asoc_sdw_rt711_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_
device_remove_software_node(ctx->headset_codec_dev);
put_device(ctx->headset_codec_dev);
+ ctx->headset_codec_dev = NULL;
return 0;
}
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index 621a9d5f9377..23a68ac3a3e3 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -647,6 +647,7 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id,
break;
default:
+ pm_runtime_put(mcasp->dev);
return -EINVAL;
}
diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c
index 01444fc960d0..863d35aff196 100644
--- a/sound/synth/emux/emux.c
+++ b/sound/synth/emux/emux.c
@@ -71,7 +71,12 @@ static int sf_sample_free(void *private_data, struct snd_sf_sample *sp,
static void sf_sample_reset(void *private_data)
{
struct snd_emux *emu = private_data;
- emu->ops.sample_reset(emu);
+
+ /* stop all voices that might refer to zones and samples to be freed */
+ if (emu->ops.sample_reset)
+ emu->ops.sample_reset(emu);
+ else
+ snd_emux_terminate_all(emu);
}
int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, char *name)
@@ -96,8 +101,7 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch
sf_cb.private_data = emu;
sf_cb.sample_new = sf_sample_new;
sf_cb.sample_free = sf_sample_free;
- if (emu->ops.sample_reset)
- sf_cb.sample_reset = sf_sample_reset;
+ sf_cb.sample_reset = sf_sample_reset;
emu->sflist = snd_sf_new(&sf_cb, emu->memhdr);
if (emu->sflist == NULL)
return -ENOMEM;
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
index 30793a131746..2f8ba39d4a6f 100644
--- a/sound/synth/emux/soundfont.c
+++ b/sound/synth/emux/soundfont.c
@@ -443,6 +443,15 @@ remove_info(struct snd_sf_list *sflist, struct snd_soundfont *sf,
struct snd_sf_zone *prev, *next, *p;
int removed = 0;
+ /* stop voices before freeing the zones they might refer to */
+ for (p = sf->zones; p; p = p->next) {
+ if (!p->mapped && p->bank == bank && p->instr == instr) {
+ if (sflist->callback.sample_reset)
+ sflist->callback.sample_reset(sflist->callback.private_data);
+ break;
+ }
+ }
+
prev = NULL;
for (p = sf->zones; p; p = next) {
next = p->next;
diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 9ef4faa006a0..46acd2f44c44 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -125,24 +125,21 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
line6pcm->in.pos_done -= runtime->buffer_size;
}
-void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
+/* update the capture position and check whether we need period_elapsed call */
+bool line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
{
- struct snd_pcm_substream *substream =
- get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
-
line6pcm->in.bytes += length;
if (line6pcm->in.bytes >= line6pcm->in.period) {
line6pcm->in.bytes %= line6pcm->in.period;
- spin_unlock(&line6pcm->in.lock);
- snd_pcm_period_elapsed(substream);
- spin_lock(&line6pcm->in.lock);
+ return true;
}
+ return false;
}
/*
* Callback for completed capture URB.
*/
-static void audio_in_callback(struct urb *urb)
+static bool handle_audio_in(struct urb *urb)
{
int i, index, length = 0, shutdown = 0;
struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
@@ -207,8 +204,18 @@ static void audio_in_callback(struct urb *urb)
if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
test_bit(LINE6_STREAM_PCM, &line6pcm->in.running))
- line6_capture_check_period(line6pcm, length);
+ return line6_capture_check_period(line6pcm, length);
}
+
+ return false;
+}
+
+static void audio_in_callback(struct urb *urb)
+{
+ struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
+
+ if (handle_audio_in(urb))
+ snd_pcm_period_elapsed(get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE));
}
/* open capture callback */
diff --git a/sound/usb/line6/capture.h b/sound/usb/line6/capture.h
index 90572dae134e..628315389efc 100644
--- a/sound/usb/line6/capture.h
+++ b/sound/usb/line6/capture.h
@@ -17,7 +17,7 @@ extern const struct snd_pcm_ops snd_line6_capture_ops;
extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf,
int fsize);
-extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm,
+extern bool line6_capture_check_period(struct snd_line6_pcm *line6pcm,
int length);
extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm);
extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm);
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 2e70ca5456c5..495a983a0c51 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -136,6 +136,8 @@ static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,
/*
Find a free URB, prepare audio data, and submit URB.
must be called in line6pcm->out.lock context
+ Returns 1 if snd_pcm_period_elapsed() needs to be called for the
+ capture stream after releasing the lock, 0 if not, or a negative error
*/
static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
{
@@ -151,6 +153,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
line6pcm->properties->rates.rats[0].den *
(line6pcm->line6->intervals_per_second / LINE6_ISO_INTERVAL);
struct urb *urb_out;
+ bool capture_period_elapsed = false;
index = find_first_zero_bit(&line6pcm->out.active_urbs,
line6pcm->line6->iso_buffers);
@@ -260,8 +263,9 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
urb_out->transfer_buffer,
urb_out->
transfer_buffer_length);
- line6_capture_check_period(line6pcm,
- urb_out->transfer_buffer_length);
+ capture_period_elapsed =
+ line6_capture_check_period(line6pcm,
+ urb_out->transfer_buffer_length);
}
} else {
if (!(line6pcm->line6->properties->capabilities & LINE6_CAP_HWMON)
@@ -283,24 +287,26 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
dev_err(line6pcm->line6->ifcdev,
"URB out #%d submission failed (%d)\n", index, ret);
- return 0;
+ return capture_period_elapsed;
}
/*
Submit all currently available playback URBs.
must be called in line6pcm->out.lock context
+ The capture period notification is skipped here since we can't call
+ snd_pcm_period_elapsed() in the lock; it'll be caught up at the next one.
*/
int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
{
- int ret = 0, i;
+ int ret, i;
for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
ret = submit_audio_out_urb(line6pcm);
if (ret < 0)
- break;
+ return ret;
}
- return ret;
+ return 0;
}
/*
@@ -316,6 +322,8 @@ static void audio_out_callback(struct urb *urb)
const int bytes_per_frame =
line6pcm->properties->bytes_per_channel *
line6pcm->properties->playback_hw.channels_max;
+ bool capture_period_elapsed = false;
+ bool playback_period_elapsed = false;
#if USE_CLEAR_BUFFER_WORKAROUND
memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
@@ -358,19 +366,22 @@ static void audio_out_callback(struct urb *urb)
shutdown = 1;
if (!shutdown) {
- submit_audio_out_urb(line6pcm);
+ capture_period_elapsed = submit_audio_out_urb(line6pcm) > 0;
if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) {
line6pcm->out.bytes += length;
if (line6pcm->out.bytes >= line6pcm->out.period) {
line6pcm->out.bytes %= line6pcm->out.period;
- spin_unlock(&line6pcm->out.lock);
- snd_pcm_period_elapsed(substream);
- spin_lock(&line6pcm->out.lock);
+ playback_period_elapsed = true;
}
}
}
spin_unlock_irqrestore(&line6pcm->out.lock, flags);
+
+ if (capture_period_elapsed)
+ snd_pcm_period_elapsed(get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE));
+ if (playback_period_elapsed)
+ snd_pcm_period_elapsed(substream);
}
/* open playback callback */
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 682b6c1fe76b..c2f7b08607ac 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -1343,6 +1343,18 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
bytes -= adj;
subs->stream_offset_adj -= adj;
}
+ /*
+ * A packet can be as large as the endpoint's maxpacksize, which
+ * the device chooses, while the ring buffer can be as small as
+ * two 64-byte periods. The hwptr_done wrap below handles one
+ * wrap only, so never take more than one buffer from a packet.
+ */
+ if (unlikely(bytes > subs->buffer_bytes)) {
+ dev_warn_ratelimited(&subs->dev->dev,
+ "%u-byte packet exceeds %u-byte buffer, truncating\n",
+ bytes, subs->buffer_bytes);
+ bytes = subs->buffer_bytes;
+ }
frames = bytes / stride;
if (!subs->txfr_quirk)
bytes = frames * stride;
@@ -1553,8 +1565,14 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
for (i = 0; i < ctx->packets; i++) {
counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail);
+ /*
+ * Bound by the ring buffer as well as by the URB:
+ * copy_to_urb() wraps hwptr_done once, so one URB must
+ * never carry more than the whole buffer.
+ */
if (counts < 0 ||
- (frames + counts) * stride > ctx->buffer_size)
+ (frames + counts) * stride > ctx->buffer_size ||
+ (frames + counts) * stride > subs->buffer_bytes)
break;
/* set up descriptor */
urb->iso_frame_desc[i].offset = frames * stride;
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index be80d9d14e0a..01a5564eda7a 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2161,6 +2161,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_DISABLE_AUTOSUSPEND),
DEVICE_FLG(0x045e, 0x070f, /* MS LifeChat LX-3000 Headset */
QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
+ DEVICE_FLG(0x046d, 0x0802, /* Logitech Webcam C200 */
+ QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384),
DEVICE_FLG(0x046d, 0x0807, /* Logitech Webcam C500 */
QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIC_RES_384),
DEVICE_FLG(0x046d, 0x0808, /* Logitech Webcam C600 */
diff --git a/tools/perf/arch/loongarch/util/header.c b/tools/perf/arch/loongarch/util/header.c
index 0c6d823334a2..46f53a74f71e 100644
--- a/tools/perf/arch/loongarch/util/header.c
+++ b/tools/perf/arch/loongarch/util/header.c
@@ -26,7 +26,7 @@
static char *_get_field(const char *line)
{
- char *line2, *nl;
+ const char *line2, *nl;
line2 = strrchr(line, ' ');
if (!line2)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index cb138d1bdd7b..8452d776d994 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2743,7 +2743,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
if (chain)
chain_nr = chain->nr;
- if (evsel__has_branch_callstack(evsel)) {
+ if (chain && evsel__has_branch_callstack(evsel)) {
struct perf_env *env = evsel__env(evsel);
err = resolve_lbr_callchain_sample(thread, cursor, sample, parent,
diff --git a/tools/perf/util/powerpc-vpadtl.c b/tools/perf/util/powerpc-vpadtl.c
index 55b9b6e07074..eacc845ebda2 100644
--- a/tools/perf/util/powerpc-vpadtl.c
+++ b/tools/perf/util/powerpc-vpadtl.c
@@ -681,7 +681,7 @@ int powerpc_vpadtl_process_auxtrace_info(union perf_event *event,
struct perf_session *session)
{
struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
- size_t min_sz = sizeof(u64) * POWERPC_VPADTL_TYPE;
+ size_t min_sz = sizeof(u64) * (POWERPC_VPADTL_TYPE + 1);
struct powerpc_vpadtl *vpa;
int err;
diff --git a/tools/testing/selftests/core/unshare_test.c b/tools/testing/selftests/core/unshare_test.c
index 7fec9dfb1b0e..6716e0f74d7d 100644
--- a/tools/testing/selftests/core/unshare_test.c
+++ b/tools/testing/selftests/core/unshare_test.c
@@ -40,6 +40,14 @@ TEST(unshare_EMFILE)
ASSERT_EQ(sscanf(buf, "%d", &nr_open), 1);
+ /*
+ * Cap nr_open for the duration of the test to avoid ENOMEM from a
+ * huge fd table allocation; buf/n keep the real original value so
+ * fs.nr_open can be restored to it once the test is done.
+ */
+ if (nr_open > 1024 * 1024)
+ nr_open = 1024 * 1024;
+
ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlimit));
/* bump fs.nr_open */
@@ -73,10 +81,13 @@ TEST(unshare_EMFILE)
if (pid == 0) {
int err;
+ char buf3[32];
+ ssize_t n3;
- /* restore fs.nr_open */
+ /* restore fs.nr_open to the (possibly capped) test baseline */
+ n3 = sprintf(buf3, "%d\n", nr_open);
lseek(fd, 0, SEEK_SET);
- write(fd, buf, n);
+ write(fd, buf3, n3);
/* ... and now unshare(CLONE_FILES) must fail with EMFILE */
err = unshare(CLONE_FILES);
EXPECT_EQ(err, -1)
@@ -89,6 +100,10 @@ TEST(unshare_EMFILE)
EXPECT_EQ(waitpid(pid, &status, 0), pid);
EXPECT_EQ(true, WIFEXITED(status));
EXPECT_EQ(0, WEXITSTATUS(status));
+
+ /* restore the real fs.nr_open value */
+ lseek(fd, 0, SEEK_SET);
+ write(fd, buf, n);
}
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index 19c9afa0be71..a0c22cd7a9c0 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -4362,6 +4362,185 @@ TEST(user_notification_addfd_rlimit)
close(memfd);
}
+/* Just take a signal and do nothing else. */
+static void empty_handler(int signo)
+{
+}
+
+/*
+ * Verify that when SECCOMP_IOCTL_NOTIF_ADDFD with SECCOMP_ADDFD_FLAG_SEND is
+ * interrupted by a signal before the tracee dequeues the addfd request,
+ * knotif->state is restored from SECCOMP_NOTIFY_REPLIED back to
+ * SECCOMP_NOTIFY_SENT so that:
+ * 1. The woken tracee sees knotif->state == SECCOMP_NOTIFY_SENT in
+ * do_user_notif() and goes back to sleep instead of prematurely
+ * returning 0 from the trapped syscall without the FD installed.
+ * 2. The supervisor can retry SECCOMP_IOCTL_NOTIF_ADDFD (or
+ * SECCOMP_IOCTL_NOTIF_SEND) instead of failing with -EINPROGRESS.
+ *
+ * To deterministically hit the race window where the supervisor sleeps in
+ * wait_for_completion_interruptible(&kaddfd.completion) after waking the
+ * tracee (complete(&knotif->ready)) but before the tracee runs
+ * seccomp_handle_addfd(), pin all processes to a single CPU and enforce a
+ * strict 3-tier scheduling priority hierarchy on that CPU:
+ * - Supervisor: SCHED_FIFO priority 99 (highest)
+ * - Signal helper (sig_pid): SCHED_FIFO priority 50 (middle)
+ * - Tracee (pid): SCHED_IDLE (lowest)
+ */
+TEST(user_notification_addfd_send_interrupted)
+{
+ /*
+ * Save parent_pid before user_notif_syscall(__NR_getppid, ...) installs
+ * the seccomp filter on the calling process; children inherit that
+ * filter, so sig_pid must not call getppid().
+ */
+ pid_t tracee_pid, sig_pid, parent_pid = getpid();
+ long ret;
+ int status, listener, memfd, err;
+ struct seccomp_notif_addfd addfd = {};
+ struct seccomp_notif req = {};
+ struct sigaction sa = {};
+ struct sched_param sp_zero = { .sched_priority = 0 };
+ struct sched_param sp_fifo_high = { .sched_priority = 99 };
+ struct sched_param sp_fifo_mid = { .sched_priority = 50 };
+ struct timespec delay = { .tv_nsec = 15000000 };
+ cpu_set_t cpuset;
+ int cpu;
+
+ /* Pin the supervisor (and its future child processes) to one CPU. */
+ cpu = sched_getcpu();
+ if (cpu >= 0) {
+ CPU_ZERO(&cpuset);
+ CPU_SET(cpu, &cpuset);
+ sched_setaffinity(0, sizeof(cpuset), &cpuset);
+ }
+
+ sa.sa_handler = empty_handler;
+ ASSERT_EQ(sigaction(SIGUSR1, &sa, NULL), 0);
+
+ memfd = memfd_create("test", 0);
+ ASSERT_GE(memfd, 0);
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ /*
+ * Follow the convention of other user_notification_* tests in this
+ * file by trapping __NR_getppid: because the filter is installed on
+ * the supervisor before fork(), the trapped syscall must be a
+ * side-effect-free syscall that the supervisor itself never invokes.
+ * Even though getppid() does not normally return an FD,
+ * SECCOMP_ADDFD_FLAG_SEND replaces the trapped syscall's return value
+ * with the newly installed FD number (42).
+ */
+ listener = user_notif_syscall(__NR_getppid,
+ SECCOMP_FILTER_FLAG_NEW_LISTENER);
+ ASSERT_GE(listener, 0);
+
+ tracee_pid = fork();
+ ASSERT_GE(tracee_pid, 0);
+
+ if (tracee_pid == 0) {
+ /*
+ * Tracee: invoke __NR_getppid as a dummy trigger syscall to
+ * trap into do_user_notif(). Verify that the syscall returns
+ * the injected FD number (42) and that FD 42 is open.
+ */
+ ret = syscall(__NR_getppid);
+ exit(ret != 42 || fcntl(42, F_GETFD) < 0);
+ }
+
+ /* Wait for the tracee to trap in do_user_notif(). */
+ ASSERT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req), 0);
+
+ /*
+ * Demote the tracee to SCHED_IDLE and promote the supervisor to
+ * SCHED_FIFO(99) on the same CPU.
+ */
+ ASSERT_EQ(sched_setscheduler(tracee_pid, SCHED_IDLE, &sp_zero), 0);
+ if (sched_setscheduler(0, SCHED_FIFO, &sp_fifo_high) != 0) {
+ kill(tracee_pid, SIGKILL);
+ waitpid(tracee_pid, NULL, 0);
+ SKIP(return, "SCHED_FIFO requires CAP_SYS_NICE");
+ }
+
+ addfd.srcfd = memfd;
+ addfd.newfd_flags = O_CLOEXEC;
+ addfd.newfd = 42;
+ addfd.id = req.id;
+ addfd.flags = SECCOMP_ADDFD_FLAG_SETFD | SECCOMP_ADDFD_FLAG_SEND;
+
+ /*
+ * Fork a signal helper on the same CPU and set it to SCHED_FIFO(50).
+ * Because the supervisor is currently running at SCHED_FIFO(99) on
+ * this CPU, sig_pid is queued on the runqueue but cannot run until the
+ * supervisor blocks inside the kernel.
+ *
+ * When the supervisor invokes ioctl(SECCOMP_IOCTL_NOTIF_ADDFD) below:
+ * 1. seccomp_notify_addfd() sets knotif->state = SECCOMP_NOTIFY_REPLIED,
+ * wakes the tracee (SCHED_IDLE), and blocks in
+ * wait_for_completion_interruptible(&kaddfd.completion).
+ * 2. The CPU scheduler immediately runs sig_pid (SCHED_FIFO 50)
+ * ahead of the woken tracee (SCHED_IDLE).
+ * 3. sig_pid sends SIGUSR1 to parent_pid, waking the supervisor
+ * (SCHED_FIFO 99), which immediately preempts sig_pid, aborts the
+ * wait with -ERESTARTSYS (-EINTR), removes kaddfd from
+ * knotif->addfd, and restores knotif->state = SECCOMP_NOTIFY_SENT
+ * before the tracee has executed a single instruction.
+ */
+ sig_pid = fork();
+ if (sig_pid < 0) {
+ sched_setscheduler(0, SCHED_OTHER, &sp_zero);
+ kill(tracee_pid, SIGKILL);
+ waitpid(tracee_pid, NULL, 0);
+ }
+ ASSERT_GE(sig_pid, 0);
+ if (sig_pid == 0) {
+ if (sched_setscheduler(0, SCHED_FIFO, &sp_fifo_mid) != 0)
+ _exit(1);
+ kill(parent_pid, SIGUSR1);
+ _exit(0);
+ }
+
+ ret = ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd);
+ err = errno;
+
+ /*
+ * Restore normal scheduling ASAP so the supervisor does not remain
+ * SCHED_FIFO on a single-CPU machine/VM/CI, then sleep briefly so the
+ * woken tracee runs in do_user_notif(). With knotif->state restored to
+ * SECCOMP_NOTIFY_SENT, the tracee must loop back to sleep waiting for
+ * the notification reply rather than returning 0 from __NR_getppid.
+ */
+ ASSERT_EQ(sched_setscheduler(0, SCHED_OTHER, &sp_zero), 0);
+ ASSERT_EQ(sched_setscheduler(tracee_pid, SCHED_OTHER, &sp_zero), 0);
+
+ EXPECT_EQ(ret, -1);
+ EXPECT_EQ(err, EINTR);
+ EXPECT_EQ(waitpid(sig_pid, &status, 0), sig_pid);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status));
+
+ nanosleep(&delay, NULL);
+
+ /*
+ * Retry SECCOMP_IOCTL_NOTIF_ADDFD. Because knotif->state is
+ * SECCOMP_NOTIFY_SENT, the retry succeeds (returns 42) instead of
+ * failing with -EINPROGRESS, installs FD 42 into the tracee, and wakes
+ * the tracee to complete the syscall with return value 42.
+ */
+ EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd), 42);
+
+ EXPECT_EQ(waitpid(tracee_pid, &status, 0), tracee_pid);
+ EXPECT_EQ(true, WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status));
+
+ close(listener);
+ close(memfd);
+}
+
#ifndef SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP
#define SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP (1UL << 0)
#define SECCOMP_IOCTL_NOTIF_SET_FLAGS SECCOMP_IOW(4, __u64)
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index a59a4446b846..2ab7bb46a371 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -576,6 +576,9 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
BUILD_BUG_ON(sizeof(gpa_t) != sizeof(offset));
BUILD_BUG_ON(sizeof(gfn_t) != sizeof(slot->gmem.pgoff));
+ if (WARN_ON_ONCE(slot->flags & KVM_MEMSLOT_GMEM_ONLY))
+ return -EINVAL;
+
file = fget(fd);
if (!file)
return -EBADF;
@@ -613,7 +616,13 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
if (kvm_gmem_supports_mmap(inode))
slot->flags |= KVM_MEMSLOT_GMEM_ONLY;
- xa_store_range(&gmem->bindings, start, end - 1, slot, GFP_KERNEL);
+ r = xa_err(xa_store_range(&gmem->bindings, start, end - 1, slot, GFP_KERNEL));
+ if (r) {
+ xa_store_range(&gmem->bindings, start, end - 1, NULL, GFP_KERNEL);
+ slot->gmem.file = NULL;
+ slot->gmem.pgoff = 0;
+ slot->flags &= ~KVM_MEMSLOT_GMEM_ONLY;
+ }
filemap_invalidate_unlock(inode->i_mapping);
/*
@@ -621,7 +630,6 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
* not the other way 'round. Active bindings are invalidated if the
* file is closed before memslots are destroyed.
*/
- r = 0;
err:
fput(file);
return r;
prev parent reply other threads:[~2026-11-10 18:28 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 18:28 Greg Kroah-Hartman
2026-11-10 18: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=2026111036-cringe-stylishly-9aca@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®