Re: Linux 6.12.93

From: Greg Kroah-Hartman

Date: Tue Jun 09 2026 - 07:03:25 EST


diff --git a/Documentation/netlink/specs/handshake.yaml b/Documentation/netlink/specs/handshake.yaml
index b934cc513e3d..090fc11da460 100644
--- a/Documentation/netlink/specs/handshake.yaml
+++ b/Documentation/netlink/specs/handshake.yaml
@@ -12,6 +12,12 @@ protocol: genetlink
doc: Netlink protocol to request a transport layer security handshake.

definitions:
+ -
+ type: const
+ name: max-errno
+ value: 4095
+ header: linux/err.h
+ scope: kernel
-
type: enum
name: handler-class
@@ -77,6 +83,8 @@ attribute-sets:
-
name: status
type: u32
+ checks:
+ max: max-errno
-
name: sockfd
type: s32
diff --git a/Makefile b/Makefile
index feb1d9b8b910..692ed9db7c79 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 6
PATCHLEVEL = 12
-SUBLEVEL = 92
+SUBLEVEL = 93
EXTRAVERSION =
NAME = Baby Opossum Posse

diff --git a/arch/alpha/include/asm/Kbuild b/arch/alpha/include/asm/Kbuild
index 396caece6d6d..80a12a603553 100644
--- a/arch/alpha/include/asm/Kbuild
+++ b/arch/alpha/include/asm/Kbuild
@@ -5,3 +5,4 @@ generic-y += agp.h
generic-y += asm-offsets.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild
index 49285a3ce239..ccc466802b5c 100644
--- a/arch/arc/include/asm/Kbuild
+++ b/arch/arc/include/asm/Kbuild
@@ -5,4 +5,5 @@ generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += user.h
diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild
index 03657ff8fbe3..decad5f2c826 100644
--- a/arch/arm/include/asm/Kbuild
+++ b/arch/arm/include/asm/Kbuild
@@ -3,6 +3,7 @@ generic-y += early_ioremap.h
generic-y += extable.h
generic-y += flat.h
generic-y += parport.h
+generic-y += ring_buffer.h

generated-y += mach-types.h
generated-y += unistd-nr.h
diff --git a/arch/arm64/include/asm/debug-monitors.h b/arch/arm64/include/asm/debug-monitors.h
index 13d437bcbf58..4f3901884c5d 100644
--- a/arch/arm64/include/asm/debug-monitors.h
+++ b/arch/arm64/include/asm/debug-monitors.h
@@ -62,30 +62,6 @@ struct task_struct;
#define DBG_HOOK_HANDLED 0
#define DBG_HOOK_ERROR 1

-struct step_hook {
- struct list_head node;
- int (*fn)(struct pt_regs *regs, unsigned long esr);
-};
-
-void register_user_step_hook(struct step_hook *hook);
-void unregister_user_step_hook(struct step_hook *hook);
-
-void register_kernel_step_hook(struct step_hook *hook);
-void unregister_kernel_step_hook(struct step_hook *hook);
-
-struct break_hook {
- struct list_head node;
- int (*fn)(struct pt_regs *regs, unsigned long esr);
- u16 imm;
- u16 mask; /* These bits are ignored when comparing with imm */
-};
-
-void register_user_break_hook(struct break_hook *hook);
-void unregister_user_break_hook(struct break_hook *hook);
-
-void register_kernel_break_hook(struct break_hook *hook);
-void unregister_kernel_break_hook(struct break_hook *hook);
-
u8 debug_monitors_arch(void);

enum dbg_active_el {
@@ -107,17 +83,15 @@ int kernel_active_single_step(void);
void kernel_rewind_single_step(struct pt_regs *regs);

#ifdef CONFIG_HAVE_HW_BREAKPOINT
-int reinstall_suspended_bps(struct pt_regs *regs);
+bool try_step_suspended_breakpoints(struct pt_regs *regs);
#else
-static inline int reinstall_suspended_bps(struct pt_regs *regs)
+static inline bool try_step_suspended_breakpoints(struct pt_regs *regs)
{
- return -ENODEV;
+ return false;
}
#endif

-int aarch32_break_handler(struct pt_regs *regs);
-
-void debug_traps_init(void);
+bool try_handle_aarch32_break(struct pt_regs *regs);

#endif /* __ASSEMBLY */
#endif /* __ASM_DEBUG_MONITORS_H */
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 5f4dc6364dbb..b0520b18192c 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -409,6 +409,11 @@ static inline bool esr_is_cfi_brk(unsigned long esr)
(esr_brk_comment(esr) & ~CFI_BRK_IMM_MASK) == CFI_BRK_IMM_BASE;
}

+static inline bool esr_is_ubsan_brk(unsigned long esr)
+{
+ return (esr_brk_comment(esr) & ~UBSAN_BRK_MASK) == UBSAN_BRK_IMM;
+}
+
static inline bool esr_fsc_is_translation_fault(unsigned long esr)
{
esr = esr & ESR_ELx_FSC;
diff --git a/arch/arm64/include/asm/exception.h b/arch/arm64/include/asm/exception.h
index f296662590c7..50c5329ff2ed 100644
--- a/arch/arm64/include/asm/exception.h
+++ b/arch/arm64/include/asm/exception.h
@@ -57,8 +57,20 @@ void do_el0_undef(struct pt_regs *regs, unsigned long esr);
void do_el1_undef(struct pt_regs *regs, unsigned long esr);
void do_el0_bti(struct pt_regs *regs);
void do_el1_bti(struct pt_regs *regs, unsigned long esr);
-void do_debug_exception(unsigned long addr_if_watchpoint, unsigned long esr,
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+void do_breakpoint(unsigned long esr, struct pt_regs *regs);
+void do_watchpoint(unsigned long addr, unsigned long esr,
struct pt_regs *regs);
+#else
+static inline void do_breakpoint(unsigned long esr, struct pt_regs *regs) {}
+static inline void do_watchpoint(unsigned long addr, unsigned long esr,
+ struct pt_regs *regs) {}
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
+void do_el0_softstep(unsigned long esr, struct pt_regs *regs);
+void do_el1_softstep(unsigned long esr, struct pt_regs *regs);
+void do_el0_brk64(unsigned long esr, struct pt_regs *regs);
+void do_el1_brk64(unsigned long esr, struct pt_regs *regs);
+void do_bkpt32(unsigned long esr, struct pt_regs *regs);
void do_fpsimd_acc(unsigned long esr, struct pt_regs *regs);
void do_sve_acc(unsigned long esr, struct pt_regs *regs);
void do_sme_acc(unsigned long esr, struct pt_regs *regs);
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index 1ada23a6ec19..46bd37707e08 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -274,15 +274,29 @@ __iowrite64_copy(void __iomem *to, const void *from, size_t count)
typedef int (*ioremap_prot_hook_t)(phys_addr_t phys_addr, size_t size,
pgprot_t *prot);
int arm64_ioremap_prot_hook_register(const ioremap_prot_hook_t hook);
+void __iomem *__ioremap_prot(phys_addr_t phys, size_t size, pgprot_t prot);

-#define ioremap_prot ioremap_prot
+static inline void __iomem *ioremap_prot(phys_addr_t phys, size_t size,
+ unsigned long user_prot)
+{
+ pgprot_t prot;
+ pteval_t user_prot_val = pgprot_val(__pgprot(user_prot));
+
+ if (WARN_ON_ONCE(!(user_prot_val & PTE_USER)))
+ return NULL;

-#define _PAGE_IOREMAP PROT_DEVICE_nGnRE
+ prot = __pgprot_modify(PAGE_KERNEL, PTE_ATTRINDX_MASK,
+ user_prot_val & PTE_ATTRINDX_MASK);
+ return __ioremap_prot(phys, size, prot);
+}
+#define ioremap_prot ioremap_prot

+#define ioremap(addr, size) \
+ __ioremap_prot((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
#define ioremap_wc(addr, size) \
- ioremap_prot((addr), (size), PROT_NORMAL_NC)
+ __ioremap_prot((addr), (size), __pgprot(PROT_NORMAL_NC))
#define ioremap_np(addr, size) \
- ioremap_prot((addr), (size), PROT_DEVICE_nGnRnE)
+ __ioremap_prot((addr), (size), __pgprot(PROT_DEVICE_nGnRnE))

/*
* io{read,write}{16,32,64}be() macros
@@ -303,7 +317,7 @@ static inline void __iomem *ioremap_cache(phys_addr_t addr, size_t size)
if (pfn_is_map_memory(__phys_to_pfn(addr)))
return (void __iomem *)__phys_to_virt(addr);

- return ioremap_prot(addr, size, PROT_NORMAL);
+ return __ioremap_prot(addr, size, __pgprot(PROT_NORMAL));
}

/*
diff --git a/arch/arm64/include/asm/kgdb.h b/arch/arm64/include/asm/kgdb.h
index 21fc85e9d2be..3184f5d1e3ae 100644
--- a/arch/arm64/include/asm/kgdb.h
+++ b/arch/arm64/include/asm/kgdb.h
@@ -24,6 +24,18 @@ static inline void arch_kgdb_breakpoint(void)
extern void kgdb_handle_bus_error(void);
extern int kgdb_fault_expected;

+int kgdb_brk_handler(struct pt_regs *regs, unsigned long esr);
+int kgdb_compiled_brk_handler(struct pt_regs *regs, unsigned long esr);
+#ifdef CONFIG_KGDB
+int kgdb_single_step_handler(struct pt_regs *regs, unsigned long esr);
+#else
+static inline int kgdb_single_step_handler(struct pt_regs *regs,
+ unsigned long esr)
+{
+ return DBG_HOOK_ERROR;
+}
+#endif
+
#endif /* !__ASSEMBLY__ */

/*
diff --git a/arch/arm64/include/asm/kprobes.h b/arch/arm64/include/asm/kprobes.h
index be7a3680dadf..f2782560647b 100644
--- a/arch/arm64/include/asm/kprobes.h
+++ b/arch/arm64/include/asm/kprobes.h
@@ -41,4 +41,12 @@ void __kretprobe_trampoline(void);
void __kprobes *trampoline_probe_handler(struct pt_regs *regs);

#endif /* CONFIG_KPROBES */
+
+int __kprobes kprobe_brk_handler(struct pt_regs *regs,
+ unsigned long esr);
+int __kprobes kprobe_ss_brk_handler(struct pt_regs *regs,
+ unsigned long esr);
+int __kprobes kretprobe_brk_handler(struct pt_regs *regs,
+ unsigned long esr);
+
#endif /* _ARM_KPROBES_H */
diff --git a/arch/arm64/include/asm/ring_buffer.h b/arch/arm64/include/asm/ring_buffer.h
new file mode 100644
index 000000000000..62316c406888
--- /dev/null
+++ b/arch/arm64/include/asm/ring_buffer.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _ASM_ARM64_RING_BUFFER_H
+#define _ASM_ARM64_RING_BUFFER_H
+
+#include <asm/cacheflush.h>
+
+/* Flush D-cache on persistent ring buffer */
+#define arch_ring_buffer_flush_range(start, end) dcache_clean_pop(start, end)
+
+#endif /* _ASM_ARM64_RING_BUFFER_H */
diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
index c34344256762..344b1c1a4bbb 100644
--- a/arch/arm64/include/asm/system_misc.h
+++ b/arch/arm64/include/asm/system_misc.h
@@ -25,10 +25,6 @@ void arm64_notify_die(const char *str, struct pt_regs *regs,
int signo, int sicode, unsigned long far,
unsigned long err);

-void hook_debug_fault_code(int nr, int (*fn)(unsigned long, unsigned long,
- struct pt_regs *),
- int sig, int code, const char *name);
-
struct mm_struct;
extern void __show_regs(struct pt_regs *);

diff --git a/arch/arm64/include/asm/tlb.h b/arch/arm64/include/asm/tlb.h
index a947c6e784ed..d29a3b842b10 100644
--- a/arch/arm64/include/asm/tlb.h
+++ b/arch/arm64/include/asm/tlb.h
@@ -58,7 +58,7 @@ static inline int tlb_get_level(struct mmu_gather *tlb)
static inline void tlb_flush(struct mmu_gather *tlb)
{
struct vm_area_struct vma = TLB_FLUSH_VMA(tlb->mm, 0);
- bool last_level = !tlb->freed_tables;
+ bool last_level = !(tlb->freed_tables || tlb->unshared_tables);
unsigned long stride = tlb_get_unmap_size(tlb);
int tlb_level = tlb_get_level(tlb);

diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h
index 82cf1f879c61..e3e8944a71c3 100644
--- a/arch/arm64/include/asm/traps.h
+++ b/arch/arm64/include/asm/traps.h
@@ -29,6 +29,12 @@ void arm64_force_sig_fault_pkey(unsigned long far, const char *str, int pkey);
void arm64_force_sig_mceerr(int code, unsigned long far, short lsb, const char *str);
void arm64_force_sig_ptrace_errno_trap(int errno, unsigned long far, const char *str);

+int bug_brk_handler(struct pt_regs *regs, unsigned long esr);
+int cfi_brk_handler(struct pt_regs *regs, unsigned long esr);
+int reserved_fault_brk_handler(struct pt_regs *regs, unsigned long esr);
+int kasan_brk_handler(struct pt_regs *regs, unsigned long esr);
+int ubsan_brk_handler(struct pt_regs *regs, unsigned long esr);
+
int early_brk64(unsigned long addr, unsigned long esr, struct pt_regs *regs);

/*
diff --git a/arch/arm64/include/asm/uprobes.h b/arch/arm64/include/asm/uprobes.h
index 014b02897f8e..89bfb0213a50 100644
--- a/arch/arm64/include/asm/uprobes.h
+++ b/arch/arm64/include/asm/uprobes.h
@@ -28,4 +28,15 @@ struct arch_uprobe {
bool simulate;
};

+int uprobe_brk_handler(struct pt_regs *regs, unsigned long esr);
+#ifdef CONFIG_UPROBES
+int uprobe_single_step_handler(struct pt_regs *regs, unsigned long esr);
+#else
+static inline int uprobe_single_step_handler(struct pt_regs *regs,
+ unsigned long esr)
+{
+ return DBG_HOOK_ERROR;
+}
+#endif
+
#endif
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
index e6f66491fbe9..a99476819e6b 100644
--- a/arch/arm64/kernel/acpi.c
+++ b/arch/arm64/kernel/acpi.c
@@ -379,7 +379,7 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
prot = __acpi_get_writethrough_mem_attribute();
}
}
- return ioremap_prot(phys, size, pgprot_val(prot));
+ return __ioremap_prot(phys, size, prot);
}

/*
diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
index 024a7b245056..16390fd4ba5e 100644
--- a/arch/arm64/kernel/debug-monitors.c
+++ b/arch/arm64/kernel/debug-monitors.c
@@ -21,8 +21,12 @@
#include <asm/cputype.h>
#include <asm/daifflags.h>
#include <asm/debug-monitors.h>
+#include <asm/exception.h>
+#include <asm/kgdb.h>
+#include <asm/kprobes.h>
#include <asm/system_misc.h>
#include <asm/traps.h>
+#include <asm/uprobes.h>

/* Determine debug architecture. */
u8 debug_monitors_arch(void)
@@ -156,188 +160,124 @@ NOKPROBE_SYMBOL(clear_user_regs_spsr_ss);
#define set_regs_spsr_ss(r) set_user_regs_spsr_ss(&(r)->user_regs)
#define clear_regs_spsr_ss(r) clear_user_regs_spsr_ss(&(r)->user_regs)

-static DEFINE_SPINLOCK(debug_hook_lock);
-static LIST_HEAD(user_step_hook);
-static LIST_HEAD(kernel_step_hook);
-
-static void register_debug_hook(struct list_head *node, struct list_head *list)
+static void send_user_sigtrap(int si_code)
{
- spin_lock(&debug_hook_lock);
- list_add_rcu(node, list);
- spin_unlock(&debug_hook_lock);
-
-}
+ struct pt_regs *regs = current_pt_regs();

-static void unregister_debug_hook(struct list_head *node)
-{
- spin_lock(&debug_hook_lock);
- list_del_rcu(node);
- spin_unlock(&debug_hook_lock);
- synchronize_rcu();
-}
+ if (WARN_ON(!user_mode(regs)))
+ return;

-void register_user_step_hook(struct step_hook *hook)
-{
- register_debug_hook(&hook->node, &user_step_hook);
-}
+ if (interrupts_enabled(regs))
+ local_irq_enable();

-void unregister_user_step_hook(struct step_hook *hook)
-{
- unregister_debug_hook(&hook->node);
+ arm64_force_sig_fault(SIGTRAP, si_code, instruction_pointer(regs),
+ "User debug trap");
}

-void register_kernel_step_hook(struct step_hook *hook)
+/*
+ * We have already unmasked interrupts and enabled preemption
+ * when calling do_el0_softstep() from entry-common.c.
+ */
+void do_el0_softstep(unsigned long esr, struct pt_regs *regs)
{
- register_debug_hook(&hook->node, &kernel_step_hook);
-}
+ if (uprobe_single_step_handler(regs, esr) == DBG_HOOK_HANDLED)
+ return;

-void unregister_kernel_step_hook(struct step_hook *hook)
-{
- unregister_debug_hook(&hook->node);
+ send_user_sigtrap(TRAP_TRACE);
+ /*
+ * ptrace will disable single step unless explicitly
+ * asked to re-enable it. For other clients, it makes
+ * sense to leave it enabled (i.e. rewind the controls
+ * to the active-not-pending state).
+ */
+ user_rewind_single_step(current);
}

-/*
- * Call registered single step handlers
- * There is no Syndrome info to check for determining the handler.
- * So we call all the registered handlers, until the right handler is
- * found which returns zero.
- */
-static int call_step_hook(struct pt_regs *regs, unsigned long esr)
+void do_el1_softstep(unsigned long esr, struct pt_regs *regs)
{
- struct step_hook *hook;
- struct list_head *list;
- int retval = DBG_HOOK_ERROR;
-
- list = user_mode(regs) ? &user_step_hook : &kernel_step_hook;
+ if (kgdb_single_step_handler(regs, esr) == DBG_HOOK_HANDLED)
+ return;

+ pr_warn("Unexpected kernel single-step exception at EL1\n");
/*
- * Since single-step exception disables interrupt, this function is
- * entirely not preemptible, and we can use rcu list safely here.
+ * Re-enable stepping since we know that we will be
+ * returning to regs.
*/
- list_for_each_entry_rcu(hook, list, node) {
- retval = hook->fn(regs, esr);
- if (retval == DBG_HOOK_HANDLED)
- break;
- }
-
- return retval;
+ set_regs_spsr_ss(regs);
}
-NOKPROBE_SYMBOL(call_step_hook);
+NOKPROBE_SYMBOL(do_el1_softstep);

-static void send_user_sigtrap(int si_code)
+static int call_el1_break_hook(struct pt_regs *regs, unsigned long esr)
{
- struct pt_regs *regs = current_pt_regs();
+ if (esr_brk_comment(esr) == BUG_BRK_IMM)
+ return bug_brk_handler(regs, esr);

- if (WARN_ON(!user_mode(regs)))
- return;
+ if (IS_ENABLED(CONFIG_CFI_CLANG) && esr_is_cfi_brk(esr))
+ return cfi_brk_handler(regs, esr);

- if (interrupts_enabled(regs))
- local_irq_enable();
+ if (esr_brk_comment(esr) == FAULT_BRK_IMM)
+ return reserved_fault_brk_handler(regs, esr);

- arm64_force_sig_fault(SIGTRAP, si_code, instruction_pointer(regs),
- "User debug trap");
-}
+ if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) &&
+ (esr_brk_comment(esr) & ~KASAN_BRK_MASK) == KASAN_BRK_IMM)
+ return kasan_brk_handler(regs, esr);

-static int single_step_handler(unsigned long unused, unsigned long esr,
- struct pt_regs *regs)
-{
- bool handler_found = false;
+ if (IS_ENABLED(CONFIG_UBSAN_TRAP) && esr_is_ubsan_brk(esr))
+ return ubsan_brk_handler(regs, esr);

- /*
- * If we are stepping a pending breakpoint, call the hw_breakpoint
- * handler first.
- */
- if (!reinstall_suspended_bps(regs))
- return 0;
-
- if (!handler_found && call_step_hook(regs, esr) == DBG_HOOK_HANDLED)
- handler_found = true;
-
- if (!handler_found && user_mode(regs)) {
- send_user_sigtrap(TRAP_TRACE);
-
- /*
- * ptrace will disable single step unless explicitly
- * asked to re-enable it. For other clients, it makes
- * sense to leave it enabled (i.e. rewind the controls
- * to the active-not-pending state).
- */
- user_rewind_single_step(current);
- } else if (!handler_found) {
- pr_warn("Unexpected kernel single-step exception at EL1\n");
- /*
- * Re-enable stepping since we know that we will be
- * returning to regs.
- */
- set_regs_spsr_ss(regs);
+ if (IS_ENABLED(CONFIG_KGDB)) {
+ if (esr_brk_comment(esr) == KGDB_DYN_DBG_BRK_IMM)
+ return kgdb_brk_handler(regs, esr);
+ if (esr_brk_comment(esr) == KGDB_COMPILED_DBG_BRK_IMM)
+ return kgdb_compiled_brk_handler(regs, esr);
}

- return 0;
-}
-NOKPROBE_SYMBOL(single_step_handler);
-
-static LIST_HEAD(user_break_hook);
-static LIST_HEAD(kernel_break_hook);
+ if (IS_ENABLED(CONFIG_KPROBES)) {
+ if (esr_brk_comment(esr) == KPROBES_BRK_IMM)
+ return kprobe_brk_handler(regs, esr);
+ if (esr_brk_comment(esr) == KPROBES_BRK_SS_IMM)
+ return kprobe_ss_brk_handler(regs, esr);
+ }

-void register_user_break_hook(struct break_hook *hook)
-{
- register_debug_hook(&hook->node, &user_break_hook);
-}
+ if (IS_ENABLED(CONFIG_KRETPROBES) &&
+ esr_brk_comment(esr) == KRETPROBES_BRK_IMM)
+ return kretprobe_brk_handler(regs, esr);

-void unregister_user_break_hook(struct break_hook *hook)
-{
- unregister_debug_hook(&hook->node);
+ return DBG_HOOK_ERROR;
}
+NOKPROBE_SYMBOL(call_el1_break_hook);

-void register_kernel_break_hook(struct break_hook *hook)
+/*
+ * We have already unmasked interrupts and enabled preemption
+ * when calling do_el0_brk64() from entry-common.c.
+ */
+void do_el0_brk64(unsigned long esr, struct pt_regs *regs)
{
- register_debug_hook(&hook->node, &kernel_break_hook);
-}
+ if (IS_ENABLED(CONFIG_UPROBES) &&
+ esr_brk_comment(esr) == UPROBES_BRK_IMM &&
+ uprobe_brk_handler(regs, esr) == DBG_HOOK_HANDLED)
+ return;

-void unregister_kernel_break_hook(struct break_hook *hook)
-{
- unregister_debug_hook(&hook->node);
+ send_user_sigtrap(TRAP_BRKPT);
}

-static int call_break_hook(struct pt_regs *regs, unsigned long esr)
+void do_el1_brk64(unsigned long esr, struct pt_regs *regs)
{
- struct break_hook *hook;
- struct list_head *list;
- int (*fn)(struct pt_regs *regs, unsigned long esr) = NULL;
-
- list = user_mode(regs) ? &user_break_hook : &kernel_break_hook;
-
- /*
- * Since brk exception disables interrupt, this function is
- * entirely not preemptible, and we can use rcu list safely here.
- */
- list_for_each_entry_rcu(hook, list, node) {
- if ((esr_brk_comment(esr) & ~hook->mask) == hook->imm)
- fn = hook->fn;
- }
+ if (call_el1_break_hook(regs, esr) == DBG_HOOK_HANDLED)
+ return;

- return fn ? fn(regs, esr) : DBG_HOOK_ERROR;
+ die("Oops - BRK", regs, esr);
}
-NOKPROBE_SYMBOL(call_break_hook);
+NOKPROBE_SYMBOL(do_el1_brk64);

-static int brk_handler(unsigned long unused, unsigned long esr,
- struct pt_regs *regs)
+#ifdef CONFIG_COMPAT
+void do_bkpt32(unsigned long esr, struct pt_regs *regs)
{
- if (call_break_hook(regs, esr) == DBG_HOOK_HANDLED)
- return 0;
-
- if (user_mode(regs)) {
- send_user_sigtrap(TRAP_BRKPT);
- } else {
- pr_warn("Unexpected kernel BRK exception at EL1\n");
- return -EFAULT;
- }
-
- return 0;
+ arm64_notify_die("aarch32 BKPT", regs, SIGTRAP, TRAP_BRKPT, regs->pc, esr);
}
-NOKPROBE_SYMBOL(brk_handler);
+#endif /* CONFIG_COMPAT */

-int aarch32_break_handler(struct pt_regs *regs)
+bool try_handle_aarch32_break(struct pt_regs *regs)
{
u32 arm_instr;
u16 thumb_instr;
@@ -345,7 +285,7 @@ int aarch32_break_handler(struct pt_regs *regs)
void __user *pc = (void __user *)instruction_pointer(regs);

if (!compat_user_mode(regs))
- return -EFAULT;
+ return false;

if (compat_thumb_mode(regs)) {
/* get 16-bit Thumb instruction */
@@ -369,20 +309,12 @@ int aarch32_break_handler(struct pt_regs *regs)
}

if (!bp)
- return -EFAULT;
+ return false;

send_user_sigtrap(TRAP_BRKPT);
- return 0;
-}
-NOKPROBE_SYMBOL(aarch32_break_handler);
-
-void __init debug_traps_init(void)
-{
- hook_debug_fault_code(DBG_ESR_EVT_HWSS, single_step_handler, SIGTRAP,
- TRAP_TRACE, "single-step handler");
- hook_debug_fault_code(DBG_ESR_EVT_BRK, brk_handler, SIGTRAP,
- TRAP_BRKPT, "BRK handler");
+ return true;
}
+NOKPROBE_SYMBOL(try_handle_aarch32_break);

/* Re-enable single step for syscall restarting. */
void user_rewind_single_step(struct task_struct *task)
diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
index d23315ef7b67..ea3876d99c2e 100644
--- a/arch/arm64/kernel/entry-common.c
+++ b/arch/arm64/kernel/entry-common.c
@@ -441,6 +441,28 @@ static __always_inline void fpsimd_syscall_exit(void)
__this_cpu_write(fpsimd_last_state.to_save, FP_STATE_CURRENT);
}

+/*
+ * In debug exception context, we explicitly disable preemption despite
+ * having interrupts disabled.
+ * This serves two purposes: it makes it much less likely that we would
+ * accidentally schedule in exception context and it will force a warning
+ * if we somehow manage to schedule by accident.
+ */
+static void debug_exception_enter(struct pt_regs *regs)
+{
+ preempt_disable();
+
+ /* This code is a bit fragile. Test it. */
+ RCU_LOCKDEP_WARN(!rcu_is_watching(), "exception_enter didn't work");
+}
+NOKPROBE_SYMBOL(debug_exception_enter);
+
+static void debug_exception_exit(struct pt_regs *regs)
+{
+ preempt_enable_no_resched();
+}
+NOKPROBE_SYMBOL(debug_exception_exit);
+
UNHANDLED(el1t, 64, sync)
UNHANDLED(el1t, 64, irq)
UNHANDLED(el1t, 64, fiq)
@@ -486,13 +508,51 @@ static void noinstr el1_bti(struct pt_regs *regs, unsigned long esr)
exit_to_kernel_mode(regs);
}

-static void noinstr el1_dbg(struct pt_regs *regs, unsigned long esr)
+static void noinstr el1_breakpt(struct pt_regs *regs, unsigned long esr)
{
+ arm64_enter_el1_dbg(regs);
+ debug_exception_enter(regs);
+ do_breakpoint(esr, regs);
+ debug_exception_exit(regs);
+ arm64_exit_el1_dbg(regs);
+}
+
+static void noinstr el1_softstp(struct pt_regs *regs, unsigned long esr)
+{
+ arm64_enter_el1_dbg(regs);
+ if (!cortex_a76_erratum_1463225_debug_handler(regs)) {
+ debug_exception_enter(regs);
+ /*
+ * After handling a breakpoint, we suspend the breakpoint
+ * and use single-step to move to the next instruction.
+ * If we are stepping a suspended breakpoint there's nothing more to do:
+ * the single-step is complete.
+ */
+ if (!try_step_suspended_breakpoints(regs))
+ do_el1_softstep(esr, regs);
+ debug_exception_exit(regs);
+ }
+ arm64_exit_el1_dbg(regs);
+}
+
+static void noinstr el1_watchpt(struct pt_regs *regs, unsigned long esr)
+{
+ /* Watchpoints are the only debug exception to write FAR_EL1 */
unsigned long far = read_sysreg(far_el1);

arm64_enter_el1_dbg(regs);
- if (!cortex_a76_erratum_1463225_debug_handler(regs))
- do_debug_exception(far, esr, regs);
+ debug_exception_enter(regs);
+ do_watchpoint(far, esr, regs);
+ debug_exception_exit(regs);
+ arm64_exit_el1_dbg(regs);
+}
+
+static void noinstr el1_brk64(struct pt_regs *regs, unsigned long esr)
+{
+ arm64_enter_el1_dbg(regs);
+ debug_exception_enter(regs);
+ do_el1_brk64(esr, regs);
+ debug_exception_exit(regs);
arm64_exit_el1_dbg(regs);
}

@@ -529,10 +589,16 @@ asmlinkage void noinstr el1h_64_sync_handler(struct pt_regs *regs)
el1_bti(regs, esr);
break;
case ESR_ELx_EC_BREAKPT_CUR:
+ el1_breakpt(regs, esr);
+ break;
case ESR_ELx_EC_SOFTSTP_CUR:
+ el1_softstp(regs, esr);
+ break;
case ESR_ELx_EC_WATCHPT_CUR:
+ el1_watchpt(regs, esr);
+ break;
case ESR_ELx_EC_BRK64:
- el1_dbg(regs, esr);
+ el1_brk64(regs, esr);
break;
case ESR_ELx_EC_FPAC:
el1_fpac(regs, esr);
@@ -715,14 +781,58 @@ static void noinstr el0_inv(struct pt_regs *regs, unsigned long esr)
exit_to_user_mode(regs);
}

-static void noinstr el0_dbg(struct pt_regs *regs, unsigned long esr)
+static void noinstr el0_breakpt(struct pt_regs *regs, unsigned long esr)
{
- /* Only watchpoints write FAR_EL1, otherwise its UNKNOWN */
+ if (!is_ttbr0_addr(regs->pc))
+ arm64_apply_bp_hardening();
+
+ enter_from_user_mode(regs);
+ debug_exception_enter(regs);
+ do_breakpoint(esr, regs);
+ debug_exception_exit(regs);
+ local_daif_restore(DAIF_PROCCTX);
+ exit_to_user_mode(regs);
+}
+
+static void noinstr el0_softstp(struct pt_regs *regs, unsigned long esr)
+{
+ bool step_done;
+
+ if (!is_ttbr0_addr(regs->pc))
+ arm64_apply_bp_hardening();
+
+ enter_from_user_mode(regs);
+ /*
+ * After handling a breakpoint, we suspend the breakpoint
+ * and use single-step to move to the next instruction.
+ * If we are stepping a suspended breakpoint there's nothing more to do:
+ * the single-step is complete.
+ */
+ step_done = try_step_suspended_breakpoints(regs);
+ local_daif_restore(DAIF_PROCCTX);
+ if (!step_done)
+ do_el0_softstep(esr, regs);
+ exit_to_user_mode(regs);
+}
+
+static void noinstr el0_watchpt(struct pt_regs *regs, unsigned long esr)
+{
+ /* Watchpoints are the only debug exception to write FAR_EL1 */
unsigned long far = read_sysreg(far_el1);

enter_from_user_mode(regs);
- do_debug_exception(far, esr, regs);
+ debug_exception_enter(regs);
+ do_watchpoint(far, esr, regs);
+ debug_exception_exit(regs);
+ local_daif_restore(DAIF_PROCCTX);
+ exit_to_user_mode(regs);
+}
+
+static void noinstr el0_brk64(struct pt_regs *regs, unsigned long esr)
+{
+ enter_from_user_mode(regs);
local_daif_restore(DAIF_PROCCTX);
+ do_el0_brk64(esr, regs);
exit_to_user_mode(regs);
}

@@ -791,10 +901,16 @@ asmlinkage void noinstr el0t_64_sync_handler(struct pt_regs *regs)
el0_mops(regs, esr);
break;
case ESR_ELx_EC_BREAKPT_LOW:
+ el0_breakpt(regs, esr);
+ break;
case ESR_ELx_EC_SOFTSTP_LOW:
+ el0_softstp(regs, esr);
+ break;
case ESR_ELx_EC_WATCHPT_LOW:
+ el0_watchpt(regs, esr);
+ break;
case ESR_ELx_EC_BRK64:
- el0_dbg(regs, esr);
+ el0_brk64(regs, esr);
break;
case ESR_ELx_EC_FPAC:
el0_fpac(regs, esr);
@@ -877,6 +993,14 @@ static void noinstr el0_svc_compat(struct pt_regs *regs)
exit_to_user_mode(regs);
}

+static void noinstr el0_bkpt32(struct pt_regs *regs, unsigned long esr)
+{
+ enter_from_user_mode(regs);
+ local_daif_restore(DAIF_PROCCTX);
+ do_bkpt32(esr, regs);
+ exit_to_user_mode(regs);
+}
+
asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs)
{
unsigned long esr = read_sysreg(esr_el1);
@@ -911,10 +1035,16 @@ asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs)
el0_cp15(regs, esr);
break;
case ESR_ELx_EC_BREAKPT_LOW:
+ el0_breakpt(regs, esr);
+ break;
case ESR_ELx_EC_SOFTSTP_LOW:
+ el0_softstp(regs, esr);
+ break;
case ESR_ELx_EC_WATCHPT_LOW:
+ el0_watchpt(regs, esr);
+ break;
case ESR_ELx_EC_BKPT32:
- el0_dbg(regs, esr);
+ el0_bkpt32(regs, esr);
break;
default:
el0_inv(regs, esr);
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index 722ac45f9f7b..ab76b36dce82 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -22,6 +22,7 @@
#include <asm/current.h>
#include <asm/debug-monitors.h>
#include <asm/esr.h>
+#include <asm/exception.h>
#include <asm/hw_breakpoint.h>
#include <asm/traps.h>
#include <asm/cputype.h>
@@ -618,8 +619,7 @@ NOKPROBE_SYMBOL(toggle_bp_registers);
/*
* Debug exception handlers.
*/
-static int breakpoint_handler(unsigned long unused, unsigned long esr,
- struct pt_regs *regs)
+void do_breakpoint(unsigned long esr, struct pt_regs *regs)
{
int i, step = 0, *kernel_step;
u32 ctrl_reg;
@@ -662,7 +662,7 @@ static int breakpoint_handler(unsigned long unused, unsigned long esr,
}

if (!step)
- return 0;
+ return;

if (user_mode(regs)) {
debug_info->bps_disabled = 1;
@@ -670,7 +670,7 @@ static int breakpoint_handler(unsigned long unused, unsigned long esr,

/* If we're already stepping a watchpoint, just return. */
if (debug_info->wps_disabled)
- return 0;
+ return;

if (test_thread_flag(TIF_SINGLESTEP))
debug_info->suspended_step = 1;
@@ -681,7 +681,7 @@ static int breakpoint_handler(unsigned long unused, unsigned long esr,
kernel_step = this_cpu_ptr(&stepping_kernel_bp);

if (*kernel_step != ARM_KERNEL_STEP_NONE)
- return 0;
+ return;

if (kernel_active_single_step()) {
*kernel_step = ARM_KERNEL_STEP_SUSPEND;
@@ -690,10 +690,8 @@ static int breakpoint_handler(unsigned long unused, unsigned long esr,
kernel_enable_single_step(regs);
}
}
-
- return 0;
}
-NOKPROBE_SYMBOL(breakpoint_handler);
+NOKPROBE_SYMBOL(do_breakpoint);

/*
* Arm64 hardware does not always report a watchpoint hit address that matches
@@ -752,8 +750,7 @@ static int watchpoint_report(struct perf_event *wp, unsigned long addr,
return step;
}

-static int watchpoint_handler(unsigned long addr, unsigned long esr,
- struct pt_regs *regs)
+void do_watchpoint(unsigned long addr, unsigned long esr, struct pt_regs *regs)
{
int i, step = 0, *kernel_step, access, closest_match = 0;
u64 min_dist = -1, dist;
@@ -808,7 +805,7 @@ static int watchpoint_handler(unsigned long addr, unsigned long esr,
rcu_read_unlock();

if (!step)
- return 0;
+ return;

/*
* We always disable EL0 watchpoints because the kernel can
@@ -821,7 +818,7 @@ static int watchpoint_handler(unsigned long addr, unsigned long esr,

/* If we're already stepping a breakpoint, just return. */
if (debug_info->bps_disabled)
- return 0;
+ return;

if (test_thread_flag(TIF_SINGLESTEP))
debug_info->suspended_step = 1;
@@ -832,7 +829,7 @@ static int watchpoint_handler(unsigned long addr, unsigned long esr,
kernel_step = this_cpu_ptr(&stepping_kernel_bp);

if (*kernel_step != ARM_KERNEL_STEP_NONE)
- return 0;
+ return;

if (kernel_active_single_step()) {
*kernel_step = ARM_KERNEL_STEP_SUSPEND;
@@ -841,44 +838,41 @@ static int watchpoint_handler(unsigned long addr, unsigned long esr,
kernel_enable_single_step(regs);
}
}
-
- return 0;
}
-NOKPROBE_SYMBOL(watchpoint_handler);
+NOKPROBE_SYMBOL(do_watchpoint);

/*
* Handle single-step exception.
*/
-int reinstall_suspended_bps(struct pt_regs *regs)
+bool try_step_suspended_breakpoints(struct pt_regs *regs)
{
struct debug_info *debug_info = &current->thread.debug;
- int handled_exception = 0, *kernel_step;
-
- kernel_step = this_cpu_ptr(&stepping_kernel_bp);
+ int *kernel_step = this_cpu_ptr(&stepping_kernel_bp);
+ bool handled_exception = false;

/*
- * Called from single-step exception handler.
- * Return 0 if execution can resume, 1 if a SIGTRAP should be
- * reported.
+ * Called from single-step exception entry.
+ * Return true if we stepped a breakpoint and can resume execution,
+ * false if we need to handle a single-step.
*/
if (user_mode(regs)) {
if (debug_info->bps_disabled) {
debug_info->bps_disabled = 0;
toggle_bp_registers(AARCH64_DBG_REG_BCR, DBG_ACTIVE_EL0, 1);
- handled_exception = 1;
+ handled_exception = true;
}

if (debug_info->wps_disabled) {
debug_info->wps_disabled = 0;
toggle_bp_registers(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL0, 1);
- handled_exception = 1;
+ handled_exception = true;
}

if (handled_exception) {
if (debug_info->suspended_step) {
debug_info->suspended_step = 0;
/* Allow exception handling to fall-through. */
- handled_exception = 0;
+ handled_exception = false;
} else {
user_disable_single_step(current);
}
@@ -892,17 +886,17 @@ int reinstall_suspended_bps(struct pt_regs *regs)

if (*kernel_step != ARM_KERNEL_STEP_SUSPEND) {
kernel_disable_single_step();
- handled_exception = 1;
+ handled_exception = true;
} else {
- handled_exception = 0;
+ handled_exception = false;
}

*kernel_step = ARM_KERNEL_STEP_NONE;
}

- return !handled_exception;
+ return handled_exception;
}
-NOKPROBE_SYMBOL(reinstall_suspended_bps);
+NOKPROBE_SYMBOL(try_step_suspended_breakpoints);

/*
* Context-switcher for restoring suspended breakpoints.
@@ -987,12 +981,6 @@ static int __init arch_hw_breakpoint_init(void)
pr_info("found %d breakpoint and %d watchpoint registers.\n",
core_num_brps, core_num_wrps);

- /* Register debug fault handlers. */
- hook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP,
- TRAP_HWBKPT, "hw-breakpoint handler");
- hook_debug_fault_code(DBG_ESR_EVT_HWWP, watchpoint_handler, SIGTRAP,
- TRAP_HWBKPT, "hw-watchpoint handler");
-
/*
* Reset the breakpoint resources. We assume that a halting
* debugger will leave the world in a nice state for us.
diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c
index 4e1f983df3d1..f8eaf6084c3d 100644
--- a/arch/arm64/kernel/kgdb.c
+++ b/arch/arm64/kernel/kgdb.c
@@ -234,23 +234,23 @@ int kgdb_arch_handle_exception(int exception_vector, int signo,
return err;
}

-static int kgdb_brk_fn(struct pt_regs *regs, unsigned long esr)
+int kgdb_brk_handler(struct pt_regs *regs, unsigned long esr)
{
kgdb_handle_exception(1, SIGTRAP, 0, regs);
return DBG_HOOK_HANDLED;
}
-NOKPROBE_SYMBOL(kgdb_brk_fn)
+NOKPROBE_SYMBOL(kgdb_brk_handler)

-static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned long esr)
+int kgdb_compiled_brk_handler(struct pt_regs *regs, unsigned long esr)
{
compiled_break = 1;
kgdb_handle_exception(1, SIGTRAP, 0, regs);

return DBG_HOOK_HANDLED;
}
-NOKPROBE_SYMBOL(kgdb_compiled_brk_fn);
+NOKPROBE_SYMBOL(kgdb_compiled_brk_handler);

-static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned long esr)
+int kgdb_single_step_handler(struct pt_regs *regs, unsigned long esr)
{
if (!kgdb_single_step)
return DBG_HOOK_ERROR;
@@ -258,21 +258,7 @@ static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned long esr)
kgdb_handle_exception(0, SIGTRAP, 0, regs);
return DBG_HOOK_HANDLED;
}
-NOKPROBE_SYMBOL(kgdb_step_brk_fn);
-
-static struct break_hook kgdb_brkpt_hook = {
- .fn = kgdb_brk_fn,
- .imm = KGDB_DYN_DBG_BRK_IMM,
-};
-
-static struct break_hook kgdb_compiled_brkpt_hook = {
- .fn = kgdb_compiled_brk_fn,
- .imm = KGDB_COMPILED_DBG_BRK_IMM,
-};
-
-static struct step_hook kgdb_step_hook = {
- .fn = kgdb_step_brk_fn
-};
+NOKPROBE_SYMBOL(kgdb_single_step_handler);

static int __kgdb_notify(struct die_args *args, unsigned long cmd)
{
@@ -311,15 +297,7 @@ static struct notifier_block kgdb_notifier = {
*/
int kgdb_arch_init(void)
{
- int ret = register_die_notifier(&kgdb_notifier);
-
- if (ret != 0)
- return ret;
-
- register_kernel_break_hook(&kgdb_brkpt_hook);
- register_kernel_break_hook(&kgdb_compiled_brkpt_hook);
- register_kernel_step_hook(&kgdb_step_hook);
- return 0;
+ return register_die_notifier(&kgdb_notifier);
}

/*
@@ -329,9 +307,6 @@ int kgdb_arch_init(void)
*/
void kgdb_arch_exit(void)
{
- unregister_kernel_break_hook(&kgdb_brkpt_hook);
- unregister_kernel_break_hook(&kgdb_compiled_brkpt_hook);
- unregister_kernel_step_hook(&kgdb_step_hook);
unregister_die_notifier(&kgdb_notifier);
}

diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
index b0e0f0aed748..8661cd406473 100644
--- a/arch/arm64/kernel/probes/kprobes.c
+++ b/arch/arm64/kernel/probes/kprobes.c
@@ -306,8 +306,8 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
return 0;
}

-static int __kprobes
-kprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr)
+int __kprobes
+kprobe_brk_handler(struct pt_regs *regs, unsigned long esr)
{
struct kprobe *p, *cur_kprobe;
struct kprobe_ctlblk *kcb;
@@ -350,13 +350,8 @@ kprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr)
return DBG_HOOK_HANDLED;
}

-static struct break_hook kprobes_break_hook = {
- .imm = KPROBES_BRK_IMM,
- .fn = kprobe_breakpoint_handler,
-};
-
-static int __kprobes
-kprobe_breakpoint_ss_handler(struct pt_regs *regs, unsigned long esr)
+int __kprobes
+kprobe_ss_brk_handler(struct pt_regs *regs, unsigned long esr)
{
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
unsigned long addr = instruction_pointer(regs);
@@ -374,13 +369,8 @@ kprobe_breakpoint_ss_handler(struct pt_regs *regs, unsigned long esr)
return DBG_HOOK_ERROR;
}

-static struct break_hook kprobes_break_ss_hook = {
- .imm = KPROBES_BRK_SS_IMM,
- .fn = kprobe_breakpoint_ss_handler,
-};
-
-static int __kprobes
-kretprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr)
+int __kprobes
+kretprobe_brk_handler(struct pt_regs *regs, unsigned long esr)
{
if (regs->pc != (unsigned long)__kretprobe_trampoline)
return DBG_HOOK_ERROR;
@@ -389,11 +379,6 @@ kretprobe_breakpoint_handler(struct pt_regs *regs, unsigned long esr)
return DBG_HOOK_HANDLED;
}

-static struct break_hook kretprobes_break_hook = {
- .imm = KRETPROBES_BRK_IMM,
- .fn = kretprobe_breakpoint_handler,
-};
-
/*
* Provide a blacklist of symbols identifying ranges which cannot be kprobed.
* This blacklist is exposed to userspace via debugfs (kprobes/blacklist).
@@ -436,9 +421,5 @@ int __kprobes arch_trampoline_kprobe(struct kprobe *p)

int __init arch_init_kprobes(void)
{
- register_kernel_break_hook(&kprobes_break_hook);
- register_kernel_break_hook(&kprobes_break_ss_hook);
- register_kernel_break_hook(&kretprobes_break_hook);
-
return 0;
}
diff --git a/arch/arm64/kernel/probes/kprobes_trampoline.S b/arch/arm64/kernel/probes/kprobes_trampoline.S
index a362f3dbb3d1..b60739d3983f 100644
--- a/arch/arm64/kernel/probes/kprobes_trampoline.S
+++ b/arch/arm64/kernel/probes/kprobes_trampoline.S
@@ -12,7 +12,7 @@
SYM_CODE_START(__kretprobe_trampoline)
/*
* Trigger a breakpoint exception. The PC will be adjusted by
- * kretprobe_breakpoint_handler(), and no subsequent instructions will
+ * kretprobe_brk_handler(), and no subsequent instructions will
* be executed from the trampoline.
*/
brk #KRETPROBES_BRK_IMM
diff --git a/arch/arm64/kernel/probes/uprobes.c b/arch/arm64/kernel/probes/uprobes.c
index a2f137a595fc..6ae4396577d4 100644
--- a/arch/arm64/kernel/probes/uprobes.c
+++ b/arch/arm64/kernel/probes/uprobes.c
@@ -165,7 +165,7 @@ int arch_uprobe_exception_notify(struct notifier_block *self,
return NOTIFY_DONE;
}

-static int uprobe_breakpoint_handler(struct pt_regs *regs,
+int uprobe_brk_handler(struct pt_regs *regs,
unsigned long esr)
{
if (uprobe_pre_sstep_notifier(regs))
@@ -174,7 +174,7 @@ static int uprobe_breakpoint_handler(struct pt_regs *regs,
return DBG_HOOK_ERROR;
}

-static int uprobe_single_step_handler(struct pt_regs *regs,
+int uprobe_single_step_handler(struct pt_regs *regs,
unsigned long esr)
{
struct uprobe_task *utask = current->utask;
@@ -186,23 +186,3 @@ static int uprobe_single_step_handler(struct pt_regs *regs,
return DBG_HOOK_ERROR;
}

-/* uprobe breakpoint handler hook */
-static struct break_hook uprobes_break_hook = {
- .imm = UPROBES_BRK_IMM,
- .fn = uprobe_breakpoint_handler,
-};
-
-/* uprobe single step handler hook */
-static struct step_hook uprobes_step_hook = {
- .fn = uprobe_single_step_handler,
-};
-
-static int __init arch_init_uprobes(void)
-{
- register_user_break_hook(&uprobes_break_hook);
- register_user_step_hook(&uprobes_step_hook);
-
- return 0;
-}
-
-device_initcall(arch_init_uprobes);
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index e2e8ffa65aa5..e6e815ef03c7 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -462,7 +462,7 @@ void do_el0_undef(struct pt_regs *regs, unsigned long esr)
u32 insn;

/* check for AArch32 breakpoint instructions */
- if (!aarch32_break_handler(regs))
+ if (try_handle_aarch32_break(regs))
return;

if (user_insn_read(regs, &insn))
@@ -978,7 +978,7 @@ void do_serror(struct pt_regs *regs, unsigned long esr)
int is_valid_bugaddr(unsigned long addr)
{
/*
- * bug_handler() only called for BRK #BUG_BRK_IMM.
+ * bug_brk_handler() only called for BRK #BUG_BRK_IMM.
* So the answer is trivial -- any spurious instances with no
* bug table entry will be rejected by report_bug() and passed
* back to the debug-monitors code and handled as a fatal
@@ -988,7 +988,7 @@ int is_valid_bugaddr(unsigned long addr)
}
#endif

-static int bug_handler(struct pt_regs *regs, unsigned long esr)
+int bug_brk_handler(struct pt_regs *regs, unsigned long esr)
{
switch (report_bug(regs->pc, regs)) {
case BUG_TRAP_TYPE_BUG:
@@ -1008,13 +1008,8 @@ static int bug_handler(struct pt_regs *regs, unsigned long esr)
return DBG_HOOK_HANDLED;
}

-static struct break_hook bug_break_hook = {
- .fn = bug_handler,
- .imm = BUG_BRK_IMM,
-};
-
#ifdef CONFIG_CFI_CLANG
-static int cfi_handler(struct pt_regs *regs, unsigned long esr)
+int cfi_brk_handler(struct pt_regs *regs, unsigned long esr)
{
unsigned long target;
u32 type;
@@ -1037,15 +1032,9 @@ static int cfi_handler(struct pt_regs *regs, unsigned long esr)
arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
return DBG_HOOK_HANDLED;
}
-
-static struct break_hook cfi_break_hook = {
- .fn = cfi_handler,
- .imm = CFI_BRK_IMM_BASE,
- .mask = CFI_BRK_IMM_MASK,
-};
#endif /* CONFIG_CFI_CLANG */

-static int reserved_fault_handler(struct pt_regs *regs, unsigned long esr)
+int reserved_fault_brk_handler(struct pt_regs *regs, unsigned long esr)
{
pr_err("%s generated an invalid instruction at %pS!\n",
"Kernel text patching",
@@ -1055,11 +1044,6 @@ static int reserved_fault_handler(struct pt_regs *regs, unsigned long esr)
return DBG_HOOK_ERROR;
}

-static struct break_hook fault_break_hook = {
- .fn = reserved_fault_handler,
- .imm = FAULT_BRK_IMM,
-};
-
#ifdef CONFIG_KASAN_SW_TAGS

#define KASAN_ESR_RECOVER 0x20
@@ -1067,7 +1051,7 @@ static struct break_hook fault_break_hook = {
#define KASAN_ESR_SIZE_MASK 0x0f
#define KASAN_ESR_SIZE(esr) (1 << ((esr) & KASAN_ESR_SIZE_MASK))

-static int kasan_handler(struct pt_regs *regs, unsigned long esr)
+int kasan_brk_handler(struct pt_regs *regs, unsigned long esr)
{
bool recover = esr & KASAN_ESR_RECOVER;
bool write = esr & KASAN_ESR_WRITE;
@@ -1098,62 +1082,12 @@ static int kasan_handler(struct pt_regs *regs, unsigned long esr)
arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
return DBG_HOOK_HANDLED;
}
-
-static struct break_hook kasan_break_hook = {
- .fn = kasan_handler,
- .imm = KASAN_BRK_IMM,
- .mask = KASAN_BRK_MASK,
-};
#endif

#ifdef CONFIG_UBSAN_TRAP
-static int ubsan_handler(struct pt_regs *regs, unsigned long esr)
+int ubsan_brk_handler(struct pt_regs *regs, unsigned long esr)
{
die(report_ubsan_failure(regs, esr & UBSAN_BRK_MASK), regs, esr);
return DBG_HOOK_HANDLED;
}
-
-static struct break_hook ubsan_break_hook = {
- .fn = ubsan_handler,
- .imm = UBSAN_BRK_IMM,
- .mask = UBSAN_BRK_MASK,
-};
-#endif
-
-/*
- * Initial handler for AArch64 BRK exceptions
- * This handler only used until debug_traps_init().
- */
-int __init early_brk64(unsigned long addr, unsigned long esr,
- struct pt_regs *regs)
-{
-#ifdef CONFIG_CFI_CLANG
- if (esr_is_cfi_brk(esr))
- return cfi_handler(regs, esr) != DBG_HOOK_HANDLED;
-#endif
-#ifdef CONFIG_KASAN_SW_TAGS
- if ((esr_brk_comment(esr) & ~KASAN_BRK_MASK) == KASAN_BRK_IMM)
- return kasan_handler(regs, esr) != DBG_HOOK_HANDLED;
-#endif
-#ifdef CONFIG_UBSAN_TRAP
- if ((esr_brk_comment(esr) & ~UBSAN_BRK_MASK) == UBSAN_BRK_IMM)
- return ubsan_handler(regs, esr) != DBG_HOOK_HANDLED;
-#endif
- return bug_handler(regs, esr) != DBG_HOOK_HANDLED;
-}
-
-void __init trap_init(void)
-{
- register_kernel_break_hook(&bug_break_hook);
-#ifdef CONFIG_CFI_CLANG
- register_kernel_break_hook(&cfi_break_hook);
-#endif
- register_kernel_break_hook(&fault_break_hook);
-#ifdef CONFIG_KASAN_SW_TAGS
- register_kernel_break_hook(&kasan_break_hook);
#endif
-#ifdef CONFIG_UBSAN_TRAP
- register_kernel_break_hook(&ubsan_break_hook);
-#endif
- debug_traps_init();
-}
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index 3940fe893783..aa5e8bcb2615 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -163,8 +163,8 @@ static void kvm_pmu_set_pmc_value(struct kvm_pmc *pmc, u64 val, bool force)
* action is to use PMCR.P, which will reset them to
* 0 (the only use of the 'force' parameter).
*/
- val = __vcpu_sys_reg(vcpu, reg) & GENMASK(63, 32);
- val |= lower_32_bits(val);
+ val = (__vcpu_sys_reg(vcpu, reg) & GENMASK(63, 32)) |
+ lower_32_bits(val);
}

__vcpu_sys_reg(vcpu, reg) = val;
diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
index 5f6583b9abe3..dcd6b23ad2e1 100644
--- a/arch/arm64/kvm/vgic/vgic-its.c
+++ b/arch/arm64/kvm/vgic/vgic-its.c
@@ -590,8 +590,10 @@ static void vgic_its_invalidate_cache(struct vgic_its *its)
unsigned long idx;

xa_for_each(&its->translation_cache, idx, irq) {
- xa_erase(&its->translation_cache, idx);
- vgic_put_irq(kvm, irq);
+ /* Only the context that erases the entry drops its cache ref. */
+ irq = xa_erase(&its->translation_cache, idx);
+ if (irq)
+ vgic_put_irq(kvm, irq);
}
}

diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 2d1ebc0c3437..9ee5a2d2b321 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -53,18 +53,12 @@ struct fault_info {
};

static const struct fault_info fault_info[];
-static struct fault_info debug_fault_info[];

static inline const struct fault_info *esr_to_fault_info(unsigned long esr)
{
return fault_info + (esr & ESR_ELx_FSC);
}

-static inline const struct fault_info *esr_to_debug_fault_info(unsigned long esr)
-{
- return debug_fault_info + DBG_ESR_EVT(esr);
-}
-
static void data_abort_decode(unsigned long esr)
{
unsigned long iss2 = ESR_ELx_ISS2(esr);
@@ -911,75 +905,6 @@ void do_sp_pc_abort(unsigned long addr, unsigned long esr, struct pt_regs *regs)
}
NOKPROBE_SYMBOL(do_sp_pc_abort);

-/*
- * __refdata because early_brk64 is __init, but the reference to it is
- * clobbered at arch_initcall time.
- * See traps.c and debug-monitors.c:debug_traps_init().
- */
-static struct fault_info __refdata debug_fault_info[] = {
- { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware breakpoint" },
- { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware single-step" },
- { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware watchpoint" },
- { do_bad, SIGKILL, SI_KERNEL, "unknown 3" },
- { do_bad, SIGTRAP, TRAP_BRKPT, "aarch32 BKPT" },
- { do_bad, SIGKILL, SI_KERNEL, "aarch32 vector catch" },
- { early_brk64, SIGTRAP, TRAP_BRKPT, "aarch64 BRK" },
- { do_bad, SIGKILL, SI_KERNEL, "unknown 7" },
-};
-
-void __init hook_debug_fault_code(int nr,
- int (*fn)(unsigned long, unsigned long, struct pt_regs *),
- int sig, int code, const char *name)
-{
- BUG_ON(nr < 0 || nr >= ARRAY_SIZE(debug_fault_info));
-
- debug_fault_info[nr].fn = fn;
- debug_fault_info[nr].sig = sig;
- debug_fault_info[nr].code = code;
- debug_fault_info[nr].name = name;
-}
-
-/*
- * In debug exception context, we explicitly disable preemption despite
- * having interrupts disabled.
- * This serves two purposes: it makes it much less likely that we would
- * accidentally schedule in exception context and it will force a warning
- * if we somehow manage to schedule by accident.
- */
-static void debug_exception_enter(struct pt_regs *regs)
-{
- preempt_disable();
-
- /* This code is a bit fragile. Test it. */
- RCU_LOCKDEP_WARN(!rcu_is_watching(), "exception_enter didn't work");
-}
-NOKPROBE_SYMBOL(debug_exception_enter);
-
-static void debug_exception_exit(struct pt_regs *regs)
-{
- preempt_enable_no_resched();
-}
-NOKPROBE_SYMBOL(debug_exception_exit);
-
-void do_debug_exception(unsigned long addr_if_watchpoint, unsigned long esr,
- struct pt_regs *regs)
-{
- const struct fault_info *inf = esr_to_debug_fault_info(esr);
- unsigned long pc = instruction_pointer(regs);
-
- debug_exception_enter(regs);
-
- if (user_mode(regs) && !is_ttbr0_addr(pc))
- arm64_apply_bp_hardening();
-
- if (inf->fn(addr_if_watchpoint, esr, regs)) {
- arm64_notify_die(inf->name, regs, inf->sig, inf->code, pc, esr);
- }
-
- debug_exception_exit(regs);
-}
-NOKPROBE_SYMBOL(do_debug_exception);
-
/*
* Used during anonymous page fault handling.
*/
diff --git a/arch/arm64/mm/ioremap.c b/arch/arm64/mm/ioremap.c
index 6cc0b7e7eb03..1e4794a2af7d 100644
--- a/arch/arm64/mm/ioremap.c
+++ b/arch/arm64/mm/ioremap.c
@@ -14,11 +14,10 @@ int arm64_ioremap_prot_hook_register(ioremap_prot_hook_t hook)
return 0;
}

-void __iomem *ioremap_prot(phys_addr_t phys_addr, size_t size,
- unsigned long prot)
+void __iomem *__ioremap_prot(phys_addr_t phys_addr, size_t size,
+ pgprot_t pgprot)
{
unsigned long last_addr = phys_addr + size - 1;
- pgprot_t pgprot = __pgprot(prot);

/* Don't allow outside PHYS_MASK */
if (last_addr & ~PHYS_MASK)
@@ -39,7 +38,7 @@ void __iomem *ioremap_prot(phys_addr_t phys_addr, size_t size,

return generic_ioremap_prot(phys_addr, size, pgprot);
}
-EXPORT_SYMBOL(ioremap_prot);
+EXPORT_SYMBOL(__ioremap_prot);

/*
* Must be called after early_fixmap_init
diff --git a/arch/csky/include/asm/Kbuild b/arch/csky/include/asm/Kbuild
index 9a9bc65b57a9..c98d8be2330a 100644
--- a/arch/csky/include/asm/Kbuild
+++ b/arch/csky/include/asm/Kbuild
@@ -9,5 +9,6 @@ generic-y += qrwlock.h
generic-y += qrwlock_types.h
generic-y += qspinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += vmlinux.lds.h
diff --git a/arch/hexagon/include/asm/Kbuild b/arch/hexagon/include/asm/Kbuild
index 8c1a78c8f527..10a1426d95e8 100644
--- a/arch/hexagon/include/asm/Kbuild
+++ b/arch/hexagon/include/asm/Kbuild
@@ -5,3 +5,4 @@ generic-y += extable.h
generic-y += iomap.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
diff --git a/arch/loongarch/include/asm/Kbuild b/arch/loongarch/include/asm/Kbuild
index 5b5a6c90e6e2..7850eedda3b3 100644
--- a/arch/loongarch/include/asm/Kbuild
+++ b/arch/loongarch/include/asm/Kbuild
@@ -9,5 +9,6 @@ generic-y += qrwlock.h
generic-y += user.h
generic-y += ioctl.h
generic-y += mmzone.h
+generic-y += ring_buffer.h
generic-y += statfs.h
generic-y += param.h
diff --git a/arch/m68k/include/asm/Kbuild b/arch/m68k/include/asm/Kbuild
index 0dbf9c5c6fae..8ea462cd10e7 100644
--- a/arch/m68k/include/asm/Kbuild
+++ b/arch/m68k/include/asm/Kbuild
@@ -3,4 +3,5 @@ generated-y += syscall_table.h
generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += spinlock.h
diff --git a/arch/microblaze/include/asm/Kbuild b/arch/microblaze/include/asm/Kbuild
index a055f5dbe00a..0ed312ae61ef 100644
--- a/arch/microblaze/include/asm/Kbuild
+++ b/arch/microblaze/include/asm/Kbuild
@@ -5,6 +5,7 @@ generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += syscalls.h
generic-y += tlb.h
generic-y += user.h
diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c
index c4fcb8c58e01..723ce16cbfc0 100644
--- a/arch/mips/dec/platform.c
+++ b/arch/mips/dec/platform.c
@@ -10,6 +10,14 @@
#include <linux/mc146818rtc.h>
#include <linux/platform_device.h>

+#include <asm/bootinfo.h>
+
+#include <asm/dec/interrupts.h>
+#include <asm/dec/ioasic_addrs.h>
+#include <asm/dec/kn01.h>
+#include <asm/dec/kn02.h>
+#include <asm/dec/system.h>
+
static struct resource dec_rtc_resources[] = {
{
.name = "rtc",
@@ -30,11 +38,110 @@ static struct platform_device dec_rtc_device = {
.num_resources = ARRAY_SIZE(dec_rtc_resources),
};

+static struct resource dec_dz_resources[] = {
+ { .name = "dz", .flags = IORESOURCE_MEM, },
+ { .name = "dz", .flags = IORESOURCE_IRQ, },
+};
+
+static struct platform_device dec_dz_device = {
+ .name = "dz",
+ .id = PLATFORM_DEVID_NONE,
+ .resource = dec_dz_resources,
+ .num_resources = ARRAY_SIZE(dec_dz_resources),
+};
+
+static struct platform_device *dec_dz_devices[] __initdata = {
+ &dec_dz_device,
+};
+
+static struct resource dec_zs_resources[][2] = {
+ {
+ { .name = "scc0", .flags = IORESOURCE_MEM, },
+ { .name = "scc0", .flags = IORESOURCE_IRQ, },
+ },
+ {
+ { .name = "scc1", .flags = IORESOURCE_MEM, },
+ { .name = "scc1", .flags = IORESOURCE_IRQ, },
+ },
+};
+
+static struct platform_device dec_zs_device[] = {
+ {
+ .name = "zs",
+ .id = 0,
+ .resource = dec_zs_resources[0],
+ .num_resources = ARRAY_SIZE(dec_zs_resources[0]),
+ },
+ {
+ .name = "zs",
+ .id = 1,
+ .resource = dec_zs_resources[1],
+ .num_resources = ARRAY_SIZE(dec_zs_resources[1]),
+ },
+};
+
static int __init dec_add_devices(void)
{
+ struct platform_device *dec_zs_devices[ARRAY_SIZE(dec_zs_device)];
+ int ret1, ret2, ret3;
+ int num_dz, num_zs;
+ int irq, i;
+
dec_rtc_resources[0].start = RTC_PORT(0);
dec_rtc_resources[0].end = RTC_PORT(0) + dec_kn_slot_size - 1;
- return platform_device_register(&dec_rtc_device);
+
+ i = 0;
+ irq = dec_interrupt[DEC_IRQ_DZ11];
+ if (IS_ENABLED(CONFIG_32BIT) && irq >= 0) {
+ resource_size_t base;
+
+ switch (mips_machtype) {
+ case MACH_DS23100:
+ case MACH_DS5100:
+ base = dec_kn_slot_base + KN01_DZ11;
+ break;
+ default:
+ base = dec_kn_slot_base + KN02_DZ11;
+ break;
+ }
+ dec_dz_device.resource[0].start = base;
+ dec_dz_device.resource[0].end = base + dec_kn_slot_size - 1;
+ dec_dz_device.resource[1].start = irq;
+ dec_dz_device.resource[1].end = irq;
+ i++;
+ }
+ num_dz = i;
+
+ i = 0;
+ irq = dec_interrupt[DEC_IRQ_SCC0];
+ if (irq >= 0) {
+ resource_size_t base = dec_kn_slot_base + IOASIC_SCC0;
+
+ dec_zs_device[i].resource[0].start = base;
+ dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1;
+ dec_zs_device[i].resource[1].start = irq;
+ dec_zs_device[i].resource[1].end = irq;
+ dec_zs_devices[i] = &dec_zs_device[i];
+ i++;
+ }
+ irq = dec_interrupt[DEC_IRQ_SCC1];
+ if (irq >= 0) {
+ resource_size_t base = dec_kn_slot_base + IOASIC_SCC1;
+
+ dec_zs_device[i].resource[0].start = base;
+ dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1;
+ dec_zs_device[i].resource[1].start = irq;
+ dec_zs_device[i].resource[1].end = irq;
+ dec_zs_devices[i] = &dec_zs_device[i];
+ i++;
+ }
+ num_zs = i;
+
+ ret1 = platform_device_register(&dec_rtc_device);
+ ret2 = IS_ENABLED(CONFIG_32BIT) ?
+ platform_add_devices(dec_dz_devices, num_dz) : 0;
+ ret3 = platform_add_devices(dec_zs_devices, num_zs);
+ return ret1 ? ret1 : ret2 ? ret2 : ret3;
}

device_initcall(dec_add_devices);
diff --git a/arch/mips/include/asm/Kbuild b/arch/mips/include/asm/Kbuild
index 7ba67a0d6c97..a6bb06820e7c 100644
--- a/arch/mips/include/asm/Kbuild
+++ b/arch/mips/include/asm/Kbuild
@@ -12,4 +12,5 @@ generic-y += mcs_spinlock.h
generic-y += parport.h
generic-y += qrwlock.h
generic-y += qspinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
diff --git a/arch/nios2/include/asm/Kbuild b/arch/nios2/include/asm/Kbuild
index 0d09829ed144..378ddebc1db3 100644
--- a/arch/nios2/include/asm/Kbuild
+++ b/arch/nios2/include/asm/Kbuild
@@ -5,5 +5,6 @@ generic-y += cmpxchg.h
generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += spinlock.h
generic-y += user.h
diff --git a/arch/openrisc/include/asm/Kbuild b/arch/openrisc/include/asm/Kbuild
index cef49d60d74c..8aa34621702d 100644
--- a/arch/openrisc/include/asm/Kbuild
+++ b/arch/openrisc/include/asm/Kbuild
@@ -8,4 +8,5 @@ generic-y += spinlock_types.h
generic-y += spinlock.h
generic-y += qrwlock_types.h
generic-y += qrwlock.h
+generic-y += ring_buffer.h
generic-y += user.h
diff --git a/arch/parisc/include/asm/Kbuild b/arch/parisc/include/asm/Kbuild
index 4fb596d94c89..d48d158f7241 100644
--- a/arch/parisc/include/asm/Kbuild
+++ b/arch/parisc/include/asm/Kbuild
@@ -4,4 +4,5 @@ generated-y += syscall_table_64.h
generic-y += agp.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
diff --git a/arch/powerpc/include/asm/Kbuild b/arch/powerpc/include/asm/Kbuild
index e5fdc336c9b2..12537f780e2c 100644
--- a/arch/powerpc/include/asm/Kbuild
+++ b/arch/powerpc/include/asm/Kbuild
@@ -6,4 +6,5 @@ generic-y += agp.h
generic-y += kvm_types.h
generic-y += mcs_spinlock.h
generic-y += qrwlock.h
+generic-y += ring_buffer.h
generic-y += early_ioremap.h
diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild
index 1461af12da6e..ad6978567e5f 100644
--- a/arch/riscv/include/asm/Kbuild
+++ b/arch/riscv/include/asm/Kbuild
@@ -11,5 +11,6 @@ generic-y += spinlock.h
generic-y += spinlock_types.h
generic-y += qrwlock.h
generic-y += qrwlock_types.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += vmlinux.lds.h
diff --git a/arch/riscv/include/asm/syscall_wrapper.h b/arch/riscv/include/asm/syscall_wrapper.h
index ac80216549ff..226289c3b5c8 100644
--- a/arch/riscv/include/asm/syscall_wrapper.h
+++ b/arch/riscv/include/asm/syscall_wrapper.h
@@ -32,6 +32,10 @@ asmlinkage long __riscv_sys_ni_syscall(const struct pt_regs *);
__diag_push(); \
__diag_ignore(GCC, 8, "-Wattribute-alias", \
"Type aliasing is used to sanitize syscall arguments"); \
+ __diag_ignore(clang, 23, "-Wunknown-warning-option", \
+ "Avoid breaking versions without -Wattribute-alias"); \
+ __diag_ignore(clang, 23, "-Wattribute-alias", \
+ "Type aliasing is used to sanitize syscall arguments"); \
static long __se_##prefix##name(ulong, ulong, ulong, ulong, ulong, ulong, \
ulong) \
__attribute__((alias(__stringify(___se_##prefix##name)))); \
diff --git a/arch/s390/include/asm/Kbuild b/arch/s390/include/asm/Kbuild
index 297bf7157968..2b367fa4de8e 100644
--- a/arch/s390/include/asm/Kbuild
+++ b/arch/s390/include/asm/Kbuild
@@ -8,3 +8,4 @@ generic-y += asm-offsets.h
generic-y += kvm_types.h
generic-y += mcs_spinlock.h
generic-y += mmzone.h
+generic-y += ring_buffer.h
diff --git a/arch/sh/include/asm/Kbuild b/arch/sh/include/asm/Kbuild
index fc44d9c88b41..edbf46a7ea4c 100644
--- a/arch/sh/include/asm/Kbuild
+++ b/arch/sh/include/asm/Kbuild
@@ -3,3 +3,4 @@ generated-y += syscall_table.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild
index 43b0ae4c2c21..9aa7a7e1242d 100644
--- a/arch/sparc/include/asm/Kbuild
+++ b/arch/sparc/include/asm/Kbuild
@@ -4,3 +4,4 @@ generated-y += syscall_table_64.h
generic-y += agp.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild
index 18f902da8e99..abe7a9fe13c8 100644
--- a/arch/um/include/asm/Kbuild
+++ b/arch/um/include/asm/Kbuild
@@ -19,6 +19,7 @@ generic-y += param.h
generic-y += parport.h
generic-y += percpu.h
generic-y += preempt.h
+generic-y += ring_buffer.h
generic-y += runtime-const.h
generic-y += softirq_stack.h
generic-y += switch_to.h
diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild
index 6c23d1661b17..269b1ed3f12e 100644
--- a/arch/x86/include/asm/Kbuild
+++ b/arch/x86/include/asm/Kbuild
@@ -12,3 +12,4 @@ generated-y += xen-hypercalls.h
generic-y += early_ioremap.h
generic-y += mcs_spinlock.h
generic-y += mmzone.h
+generic-y += ring_buffer.h
diff --git a/arch/x86/include/asm/text-patching.h b/arch/x86/include/asm/text-patching.h
index 6259f1937fe7..bb3fd7f2c2d4 100644
--- a/arch/x86/include/asm/text-patching.h
+++ b/arch/x86/include/asm/text-patching.h
@@ -15,7 +15,7 @@

extern void text_poke_early(void *addr, const void *opcode, size_t len);

-extern void apply_relocation(u8 *buf, const u8 * const instr, size_t instrlen, u8 *repl, size_t repl_len);
+extern void text_poke_apply_relocation(u8 *buf, const u8 * const instr, size_t instrlen, u8 *repl, size_t repl_len);

/*
* Clear and restore the kernel write-protection flag on the local CPU.
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index f7918980667a..5f58b5a38cac 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -44,6 +44,22 @@ KCOV_INSTRUMENT_unwind_orc.o := n
KCOV_INSTRUMENT_unwind_frame.o := n
KCOV_INSTRUMENT_unwind_guess.o := n

+# Disable KCOV to prevent crashes during kexec: load_segments() invalidates
+# the GS base, which KCOV relies on for per-CPU data.
+#
+# As KCOV and KEXEC compatibility should be preserved (e.g. syzkaller is
+# using it to collect crash dumps during kernel fuzzing), disabling
+# KCOV for KEXEC kernels is not an option. Selectively disabling KCOV
+# instrumentation for individual affected functions can be fragile, while
+# adding more checks to KCOV would slow it down.
+#
+# As a compromise solution, disable KCOV instrumentation for the whole
+# source code file. If its coverage is ever needed, other approaches
+# should be considered.
+KCOV_INSTRUMENT_machine_kexec_64.o := n
+
+CFLAGS_head32.o := -fno-stack-protector
+CFLAGS_head64.o := -fno-stack-protector
CFLAGS_irq.o := -I $(src)/../include/asm/trace

obj-y += head_$(BITS).o
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 6ab96bc764cf..a0550398313d 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -502,7 +502,7 @@ static void __apply_relocation(u8 *buf, const u8 * const instr, size_t instrlen,
}
}

-void apply_relocation(u8 *buf, const u8 * const instr, size_t instrlen, u8 *repl, size_t repl_len)
+void text_poke_apply_relocation(u8 *buf, const u8 * const instr, size_t instrlen, u8 *repl, size_t repl_len)
{
__apply_relocation(buf, instr, instrlen, repl, repl_len);
optimize_nops(instr, buf, instrlen);
@@ -658,7 +658,7 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,
for (; insn_buff_sz < a->instrlen; insn_buff_sz++)
insn_buff[insn_buff_sz] = 0x90;

- apply_relocation(insn_buff, instr, a->instrlen, replacement, a->replacementlen);
+ text_poke_apply_relocation(insn_buff, instr, a->instrlen, replacement, a->replacementlen);

DUMP_BYTES(ALT, instr, a->instrlen, "%px: old_insn: ", instr);
DUMP_BYTES(ALT, replacement, a->replacementlen, "%px: rpl_insn: ", replacement);
@@ -1865,7 +1865,7 @@ __visible noinline void __init __alt_reloc_selftest(void *arg)
static noinline void __init alt_reloc_selftest(void)
{
/*
- * Tests apply_relocation().
+ * Tests text_poke_apply_relocation().
*
* This has a relative immediate (CALL) in a place other than the first
* instruction and additionally on x86_64 we get a RIP-relative LEA:
diff --git a/arch/x86/kernel/callthunks.c b/arch/x86/kernel/callthunks.c
index f17d16607882..dd602d2ed126 100644
--- a/arch/x86/kernel/callthunks.c
+++ b/arch/x86/kernel/callthunks.c
@@ -180,7 +180,7 @@ static void *patch_dest(void *dest, bool direct)
u8 *pad = dest - tsize;

memcpy(insn_buff, skl_call_thunk_template, tsize);
- apply_relocation(insn_buff, pad, tsize, skl_call_thunk_template, tsize);
+ text_poke_apply_relocation(insn_buff, pad, tsize, skl_call_thunk_template, tsize);

/* Already patched? */
if (!bcmp(pad, insn_buff, tsize))
@@ -302,7 +302,7 @@ static bool is_callthunk(void *addr)
pad = (void *)(dest - tmpl_size);

memcpy(insn_buff, skl_call_thunk_template, tmpl_size);
- apply_relocation(insn_buff, pad, tmpl_size, skl_call_thunk_template, tmpl_size);
+ text_poke_apply_relocation(insn_buff, pad, tmpl_size, skl_call_thunk_template, tmpl_size);

return !bcmp(pad, insn_buff, tmpl_size);
}
@@ -320,7 +320,7 @@ int x86_call_depth_emit_accounting(u8 **pprog, void *func, void *ip)
return 0;

memcpy(insn_buff, skl_call_thunk_template, tmpl_size);
- apply_relocation(insn_buff, ip, tmpl_size, skl_call_thunk_template, tmpl_size);
+ text_poke_apply_relocation(insn_buff, ip, tmpl_size, skl_call_thunk_template, tmpl_size);

memcpy(*pprog, insn_buff, tmpl_size);
*pprog += tmpl_size;
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index d3b14a9ad2ed..6bcc080f361e 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -370,6 +370,13 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
goto fail;
}

+ /*
+ * Generated trampoline may contain rIP-relative addressing which
+ * displacement needs to be fixed.
+ */
+ text_poke_apply_relocation(trampoline, trampoline, size,
+ (void *)start_offset, size);
+
/*
* The address of the ftrace_ops that is used for this trampoline
* is stored at the end of the trampoline. This will be used to
diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index d539e95a2f8d..9e1fccb39eab 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -110,6 +110,35 @@ static void avic_activate_vmcb(struct vcpu_svm *svm)

svm_clr_intercept(svm, INTERCEPT_CR8_WRITE);

+ /*
+ * Flush the TLB when enabling (x2)AVIC and when transitioning between
+ * xAVIC and x2AVIC, as the CPU may have inserted a TLB entry for the
+ * "wrong" mapping.
+ *
+ * KVM uses a per-VM "scratch" page to back the APIC memslot, because
+ * KVM also uses per-VM page tables *and* maintains the page table (NPT
+ * or shadow page) mappings for said memslot even if one or more vCPUs
+ * have their local APIC hardware-disabled or are in x2APIC mode, i.e.
+ * even if one or more vCPUs' APIC MMIO BAR is effectively disabled.
+ *
+ * If xAVIC is fully enabled, hardware ignores the physical address in
+ * KVM's page tables, i.e. in the leaf SPTE for the APIC memslot, and
+ * instead redirects the access to the AVIC backing page, i.e. to the
+ * vCPU's virtual APIC page. If xAVIC is not enabled (APIC is either
+ * hardware-disabled or in x2APIC mode), then guest accesses will use
+ * the page table mapping verbatim, i.e. will access the per-VM scratch
+ * page, as normal memory.
+ *
+ * In both cases, the CPU is allowed to cache TLB entries for the APIC
+ * base GPA. So, KVM needs to flush the TLB when enabling xAVIC, as
+ * accesses need to be redirected to the virtual APIC page, but the TLB
+ * may contain entries pointing at the scratch page. KVM also needs to
+ * flush the TLB when enabling x2AVIC, as accesses need to go to the
+ * scratch page, but the TLB may contain entries tagged as xAVIC, i.e.
+ * entries pointing to the vCPU's virtual APIC page.
+ */
+ kvm_make_request(KVM_REQ_TLB_FLUSH_CURRENT, &svm->vcpu);
+
/*
* Note: KVM supports hybrid-AVIC mode, where KVM emulates x2APIC MSR
* accesses, while interrupt injection to a running vCPU can be
@@ -123,12 +152,6 @@ static void avic_activate_vmcb(struct vcpu_svm *svm)
/* Disabling MSR intercept for x2APIC registers */
svm_set_x2apic_msr_interception(svm, false);
} else {
- /*
- * Flush the TLB, the guest may have inserted a non-APIC
- * mapping into the TLB while AVIC was disabled.
- */
- kvm_make_request(KVM_REQ_TLB_FLUSH_CURRENT, &svm->vcpu);
-
/* Enabling MSR intercept for x2APIC registers */
svm_set_x2apic_msr_interception(svm, true);
}
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index ad7590ec4065..881e07d08375 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -3502,23 +3502,26 @@ void pre_sev_run(struct vcpu_svm *svm, int cpu)
}

#define GHCB_SCRATCH_AREA_LIMIT (16ULL * PAGE_SIZE)
-static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
+static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 min_len)
{
struct vmcb_control_area *control = &svm->vmcb->control;
u64 ghcb_scratch_beg, ghcb_scratch_end;
u64 scratch_gpa_beg, scratch_gpa_end;
void *scratch_va;

+ if (WARN_ON_ONCE(!min_len))
+ goto e_scratch;
+
scratch_gpa_beg = svm->sev_es.sw_scratch;
if (!scratch_gpa_beg) {
pr_err("vmgexit: scratch gpa not provided\n");
goto e_scratch;
}

- scratch_gpa_end = scratch_gpa_beg + len;
+ scratch_gpa_end = scratch_gpa_beg + min_len;
if (scratch_gpa_end < scratch_gpa_beg) {
pr_err("vmgexit: scratch length (%#llx) not valid for scratch address (%#llx)\n",
- len, scratch_gpa_beg);
+ min_len, scratch_gpa_beg);
goto e_scratch;
}

@@ -3542,21 +3545,27 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)

scratch_va = (void *)svm->sev_es.ghcb;
scratch_va += (scratch_gpa_beg - control->ghcb_gpa);
+
+ svm->sev_es.ghcb_sa_len = ghcb_scratch_end - scratch_gpa_beg;
} else {
+ /* GHCB v2 requires the scratch area to be within the GHCB. */
+ if (to_kvm_sev_info(svm->vcpu.kvm)->ghcb_version >= 2)
+ goto e_scratch;
+
/*
* The guest memory must be read into a kernel buffer, so
* limit the size
*/
- if (len > GHCB_SCRATCH_AREA_LIMIT) {
+ if (min_len > GHCB_SCRATCH_AREA_LIMIT) {
pr_err("vmgexit: scratch area exceeds KVM limits (%#llx requested, %#llx limit)\n",
- len, GHCB_SCRATCH_AREA_LIMIT);
+ min_len, GHCB_SCRATCH_AREA_LIMIT);
goto e_scratch;
}
- scratch_va = kvzalloc(len, GFP_KERNEL_ACCOUNT);
+ scratch_va = kvzalloc(min_len, GFP_KERNEL_ACCOUNT);
if (!scratch_va)
return -ENOMEM;

- if (kvm_read_guest(svm->vcpu.kvm, scratch_gpa_beg, scratch_va, len)) {
+ if (kvm_read_guest(svm->vcpu.kvm, scratch_gpa_beg, scratch_va, min_len)) {
/* Unable to copy scratch area from guest */
pr_err("vmgexit: kvm_read_guest for scratch area failed\n");

@@ -3572,11 +3581,10 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
*/
svm->sev_es.ghcb_sa_sync = sync;
svm->sev_es.ghcb_sa_free = true;
+ svm->sev_es.ghcb_sa_len = min_len;
}

svm->sev_es.ghcb_sa = scratch_va;
- svm->sev_es.ghcb_sa_len = len;
-
return 0;

e_scratch:
@@ -3667,7 +3675,7 @@ struct psc_buffer {
struct psc_entry entries[];
} __packed;

-static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc);
+static int snp_begin_psc(struct vcpu_svm *svm);

static void snp_complete_psc(struct vcpu_svm *svm, u64 psc_ret)
{
@@ -3691,9 +3699,9 @@ static void __snp_complete_one_psc(struct vcpu_svm *svm)
*/
for (idx = svm->sev_es.psc_idx; svm->sev_es.psc_inflight;
svm->sev_es.psc_inflight--, idx++) {
- struct psc_entry *entry = &entries[idx];
+ struct psc_entry entry = READ_ONCE(entries[idx]);

- entry->cur_page = entry->pagesize ? 512 : 1;
+ entries[idx].cur_page = entry.pagesize ? 512 : 1;
}

hdr->cur_entry = idx;
@@ -3702,7 +3710,6 @@ static void __snp_complete_one_psc(struct vcpu_svm *svm)
static int snp_complete_one_psc(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
- struct psc_buffer *psc = svm->sev_es.ghcb_sa;

if (vcpu->run->hypercall.ret) {
snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC);
@@ -3712,16 +3719,18 @@ static int snp_complete_one_psc(struct kvm_vcpu *vcpu)
__snp_complete_one_psc(svm);

/* Handle the next range (if any). */
- return snp_begin_psc(svm, psc);
+ return snp_begin_psc(svm);
}

-static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc)
+static int snp_begin_psc(struct vcpu_svm *svm)
{
+ struct vcpu_sev_es_state *sev_es = &svm->sev_es;
+ struct psc_buffer *psc = sev_es->ghcb_sa;
struct psc_entry *entries = psc->entries;
struct kvm_vcpu *vcpu = &svm->vcpu;
struct psc_hdr *hdr = &psc->hdr;
struct psc_entry entry_start;
- u16 idx, idx_start, idx_end;
+ u16 idx, idx_start, idx_end, max_nr_entries;
int npages;
bool huge;
u64 gfn;
@@ -3731,6 +3740,19 @@ static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc)
return 1;
}

+ /*
+ * GHCB v2 requires the scratch area to reside within the GHCB itself,
+ * and PSC requests are only supported for GHCB v2+. Thus it should be
+ * impossible to exceed the max PSC entry count (which is derived from
+ * the size of the shared GHCB buffer).
+ */
+ max_nr_entries = (sev_es->ghcb_sa_len - sizeof(struct psc_hdr)) /
+ sizeof(struct psc_entry);
+ if (WARN_ON_ONCE(max_nr_entries > VMGEXIT_PSC_MAX_COUNT)) {
+ snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC);
+ return 1;
+ }
+
next_range:
/* There should be no other PSCs in-flight at this point. */
if (WARN_ON_ONCE(svm->sev_es.psc_inflight)) {
@@ -3743,17 +3765,17 @@ static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc)
* validation, so take care to only use validated copies of values used
* for things like array indexing.
*/
- idx_start = hdr->cur_entry;
- idx_end = hdr->end_entry;
+ idx_start = READ_ONCE(hdr->cur_entry);
+ idx_end = READ_ONCE(hdr->end_entry);

- if (idx_end >= VMGEXIT_PSC_MAX_COUNT) {
+ if (idx_end >= max_nr_entries) {
snp_complete_psc(svm, VMGEXIT_PSC_ERROR_INVALID_HDR);
return 1;
}

/* Find the start of the next range which needs processing. */
for (idx = idx_start; idx <= idx_end; idx++, hdr->cur_entry++) {
- entry_start = entries[idx];
+ entry_start = READ_ONCE(entries[idx]);

gfn = entry_start.gfn;
huge = entry_start.pagesize;
@@ -3797,7 +3819,7 @@ static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc)
* KVM_HC_MAP_GPA_RANGE exit.
*/
while (++idx <= idx_end) {
- struct psc_entry entry = entries[idx];
+ struct psc_entry entry = READ_ONCE(entries[idx]);

if (entry.operation != entry_start.operation ||
entry.gfn != entry_start.gfn + npages ||
@@ -4389,11 +4411,11 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
vcpu->run->system_event.data[0] = control->ghcb_gpa;
break;
case SVM_VMGEXIT_PSC:
- ret = setup_vmgexit_scratch(svm, true, control->exit_info_2);
+ ret = setup_vmgexit_scratch(svm, true, sizeof(struct psc_hdr));
if (ret)
break;

- ret = snp_begin_psc(svm, svm->sev_es.ghcb_sa);
+ ret = snp_begin_psc(svm);
break;
case SVM_VMGEXIT_AP_CREATION:
ret = sev_snp_ap_creation(svm);
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile
index 690fbf48e853..60a53baa0427 100644
--- a/arch/x86/mm/Makefile
+++ b/arch/x86/mm/Makefile
@@ -5,6 +5,8 @@ KCOV_INSTRUMENT_mem_encrypt.o := n
KCOV_INSTRUMENT_mem_encrypt_amd.o := n
KCOV_INSTRUMENT_mem_encrypt_identity.o := n
KCOV_INSTRUMENT_pgprot.o := n
+# See the "Disable KCOV" comment in arch/x86/kernel/Makefile.
+KCOV_INSTRUMENT_physaddr.o := n

KASAN_SANITIZE_mem_encrypt.o := n
KASAN_SANITIZE_mem_encrypt_amd.o := n
diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild
index fa07c686cbcc..a5875956392a 100644
--- a/arch/xtensa/include/asm/Kbuild
+++ b/arch/xtensa/include/asm/Kbuild
@@ -7,4 +7,5 @@ generic-y += param.h
generic-y += parport.h
generic-y += qrwlock.h
generic-y += qspinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
diff --git a/drivers/accel/ivpu/ivpu_debugfs.c b/drivers/accel/ivpu/ivpu_debugfs.c
index df89c1c0da6d..1da4ce6a99cd 100644
--- a/drivers/accel/ivpu/ivpu_debugfs.c
+++ b/drivers/accel/ivpu/ivpu_debugfs.c
@@ -447,7 +447,7 @@ priority_bands_fops_write(struct file *file, const char __user *user_buf, size_t
u32 band;
int ret;

- if (size >= sizeof(buf))
+ if (*pos != 0 || size >= sizeof(buf))
return -EINVAL;

ret = simple_write_to_buffer(buf, sizeof(buf) - 1, pos, user_buf, size);
diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c
index 731ffdfafc4e..f86681ff2229 100644
--- a/drivers/auxdisplay/line-display.c
+++ b/drivers/auxdisplay/line-display.c
@@ -90,7 +90,7 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg,
count = strlen(msg);

/* if the string ends with a newline, trim it */
- if (msg[count - 1] == '\n')
+ if (count && msg[count - 1] == '\n')
count--;

if (!count) {
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index b8bf4dfea11c..aaabc5f37eb0 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -3430,7 +3430,13 @@ static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev,
"firmware rome 0x%x build 0x%x",
rver_rom, rver_patch, ver_rom, ver_patch);

- if (rver_rom != ver_rom || rver_patch <= ver_patch) {
+ /* Allow rampatch when the patch version equals the firmware version.
+ * A firmware download may be aborted by a transient USB error (e.g.
+ * disconnect) after the controller updates version info but before
+ * completion.
+ * Allowing equal versions enables re-flashing during recovery.
+ */
+ if (rver_rom != ver_rom || rver_patch < ver_patch) {
bt_dev_err(hdev, "rampatch file version did not match with firmware");
err = -EINVAL;
goto done;
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 586a2ff72a2a..7897307a7fe2 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -47,13 +47,12 @@
#define HCI_MAX_IBS_SIZE 10

#define IBS_WAKE_RETRANS_TIMEOUT_MS 100
-#define IBS_BTSOC_TX_IDLE_TIMEOUT_MS 200
+#define IBS_BTSOC_TX_IDLE_TIMEOUT msecs_to_jiffies(200)
#define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000
-#define CMD_TRANS_TIMEOUT_MS 100
-#define MEMDUMP_TIMEOUT_MS 8000
-#define IBS_DISABLE_SSR_TIMEOUT_MS \
- (MEMDUMP_TIMEOUT_MS + FW_DOWNLOAD_TIMEOUT_MS)
-#define FW_DOWNLOAD_TIMEOUT_MS 3000
+#define CMD_TRANS_TIMEOUT msecs_to_jiffies(100)
+#define MEMDUMP_TIMEOUT msecs_to_jiffies(8000)
+#define FW_DOWNLOAD_TIMEOUT msecs_to_jiffies(3000)
+#define IBS_DISABLE_SSR_TIMEOUT (MEMDUMP_TIMEOUT + FW_DOWNLOAD_TIMEOUT)

/* susclk rate */
#define SUSCLK_RATE_32KHZ 32768
@@ -1078,7 +1077,7 @@ static void qca_controller_memdump(struct work_struct *work)

queue_delayed_work(qca->workqueue,
&qca->ctrl_memdump_timeout,
- msecs_to_jiffies(MEMDUMP_TIMEOUT_MS));
+ MEMDUMP_TIMEOUT);
skb_pull(skb, sizeof(qca_memdump->ram_dump_size));
qca_memdump->current_seq_no = 0;
qca_memdump->received_dump = 0;
@@ -1350,7 +1349,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)

if (hu->serdev)
serdev_device_wait_until_sent(hu->serdev,
- msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS));
+ CMD_TRANS_TIMEOUT);

/* Give the controller time to process the request */
switch (qca_soc_type(hu)) {
@@ -1381,8 +1380,8 @@ static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed)

static int qca_send_power_pulse(struct hci_uart *hu, bool on)
{
+ int timeout = CMD_TRANS_TIMEOUT;
int ret;
- int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS);
u8 cmd = on ? QCA_WCN3990_POWERON_PULSE : QCA_WCN3990_POWEROFF_PULSE;

/* These power pulses are single byte command which are sent
@@ -1584,7 +1583,7 @@ static void qca_wait_for_dump_collection(struct hci_dev *hdev)
struct qca_data *qca = hu->priv;

wait_on_bit_timeout(&qca->flags, QCA_MEMDUMP_COLLECTION,
- TASK_UNINTERRUPTIBLE, MEMDUMP_TIMEOUT_MS);
+ TASK_UNINTERRUPTIBLE, MEMDUMP_TIMEOUT);

clear_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
}
@@ -2516,11 +2515,10 @@ static void qca_serdev_remove(struct serdev_device *serdev)
hci_uart_unregister_device(&qcadev->serdev_hu);
}

-static void qca_serdev_shutdown(struct device *dev)
+static void qca_serdev_shutdown(struct serdev_device *serdev)
{
int ret;
- int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS);
- struct serdev_device *serdev = to_serdev_device(dev);
+ int timeout = CMD_TRANS_TIMEOUT;
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
struct hci_uart *hu = &qcadev->serdev_hu;
struct hci_dev *hdev = hu->hdev;
@@ -2577,7 +2575,7 @@ static int __maybe_unused qca_suspend(struct device *dev)
bool tx_pending = false;
int ret = 0;
u8 cmd;
- u32 wait_timeout = 0;
+ unsigned long wait_timeout = 0;

set_bit(QCA_SUSPENDING, &qca->flags);

@@ -2598,15 +2596,15 @@ static int __maybe_unused qca_suspend(struct device *dev)
if (test_bit(QCA_IBS_DISABLED, &qca->flags) ||
test_bit(QCA_SSR_TRIGGERED, &qca->flags)) {
wait_timeout = test_bit(QCA_SSR_TRIGGERED, &qca->flags) ?
- IBS_DISABLE_SSR_TIMEOUT_MS :
- FW_DOWNLOAD_TIMEOUT_MS;
+ IBS_DISABLE_SSR_TIMEOUT :
+ FW_DOWNLOAD_TIMEOUT;

/* QCA_IBS_DISABLED flag is set to true, During FW download
* and during memory dump collection. It is reset to false,
* After FW download complete.
*/
wait_on_bit_timeout(&qca->flags, QCA_IBS_DISABLED,
- TASK_UNINTERRUPTIBLE, msecs_to_jiffies(wait_timeout));
+ TASK_UNINTERRUPTIBLE, wait_timeout);

if (test_bit(QCA_IBS_DISABLED, &qca->flags)) {
bt_dev_err(hu->hdev, "SSR or FW download time out");
@@ -2658,7 +2656,7 @@ static int __maybe_unused qca_suspend(struct device *dev)

if (tx_pending) {
serdev_device_wait_until_sent(hu->serdev,
- msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS));
+ CMD_TRANS_TIMEOUT);
serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
}

@@ -2667,7 +2665,7 @@ static int __maybe_unused qca_suspend(struct device *dev)
*/
ret = wait_event_interruptible_timeout(qca->suspend_wait_q,
qca->rx_ibs_state == HCI_IBS_RX_ASLEEP,
- msecs_to_jiffies(IBS_BTSOC_TX_IDLE_TIMEOUT_MS));
+ IBS_BTSOC_TX_IDLE_TIMEOUT);
if (ret == 0) {
ret = -ETIMEDOUT;
goto error;
@@ -2741,11 +2739,11 @@ static void hciqca_coredump(struct device *dev)
static struct serdev_device_driver qca_serdev_driver = {
.probe = qca_serdev_probe,
.remove = qca_serdev_remove,
+ .shutdown = qca_serdev_shutdown,
.driver = {
.name = "hci_uart_qca",
.of_match_table = of_match_ptr(qca_bluetooth_of_match),
.acpi_match_table = ACPI_PTR(qca_bluetooth_acpi_match),
- .shutdown = qca_serdev_shutdown,
.pm = &qca_pm_ops,
#ifdef CONFIG_DEV_COREDUMP
.coredump = hciqca_coredump,
diff --git a/drivers/comedi/drivers/comedi_test.c b/drivers/comedi/drivers/comedi_test.c
index e713ef611434..a0eb262616ea 100644
--- a/drivers/comedi/drivers/comedi_test.c
+++ b/drivers/comedi/drivers/comedi_test.c
@@ -273,6 +273,7 @@ static int waveform_ai_cmdtest(struct comedi_device *dev,
/* Step 2a : make sure trigger sources are unique */

err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
err |= comedi_check_trigger_is_unique(cmd->stop_src);

/* Step 2b : and mutually compatible */
@@ -323,10 +324,10 @@ static int waveform_ai_cmdtest(struct comedi_device *dev,
arg = min(arg,
rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC));
arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC);
- if (cmd->scan_begin_arg == TRIG_TIMER) {
+ if (cmd->scan_begin_src == TRIG_TIMER) {
/* limit convert_arg to keep scan_begin_arg in range */
limit = UINT_MAX / cmd->scan_end_arg;
- limit = rounddown(limit, (unsigned int)NSEC_PER_SEC);
+ limit = rounddown(limit, (unsigned int)NSEC_PER_USEC);
arg = min(arg, limit);
}
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c
index 893b4f0726d2..6bd775f7db07 100644
--- a/drivers/counter/counter-core.c
+++ b/drivers/counter/counter-core.c
@@ -124,7 +124,8 @@ struct counter_device *counter_alloc(size_t sizeof_priv)

err_dev_set_name:

- counter_chrdev_remove(counter);
+ put_device(dev);
+ return NULL;
err_chrdev_add:

ida_free(&counter_ida, dev->id);
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index 3cdc2b218a86..a8ab78ae7fa3 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -473,7 +473,7 @@ static int mxc_gpio_probe(struct platform_device *pdev)
* the handler is needed only once, but doing it for every port
* is more robust and easier.
*/
- port->irq_high = -1;
+ port->irq_high = 0;
port->mx_irq_handler = mx2_gpio_irq_handler;
} else
port->mx_irq_handler = mx3_gpio_irq_handler;
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
index 4e2132c80be3..052713bd8d07 100644
--- a/drivers/gpio/gpio-rockchip.c
+++ b/drivers/gpio/gpio-rockchip.c
@@ -647,11 +647,10 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
if (!bank->irq)
return -EINVAL;

- bank->clk = of_clk_get(bank->of_node, 0);
+ bank->clk = devm_clk_get_enabled(bank->dev, NULL);
if (IS_ERR(bank->clk))
return PTR_ERR(bank->clk);

- clk_prepare_enable(bank->clk);
id = readl(bank->reg_base + gpio_regs_v2.version_id);

/* If not gpio v2, that is default to v1. */
@@ -661,7 +660,6 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
bank->db_clk = of_clk_get(bank->of_node, 1);
if (IS_ERR(bank->db_clk)) {
dev_err(bank->dev, "cannot find debounce clk\n");
- clk_disable_unprepare(bank->clk);
return -EINVAL;
}
} else {
@@ -735,7 +733,6 @@ static int rockchip_gpio_probe(struct platform_device *pdev)

ret = rockchip_gpiolib_register(bank);
if (ret) {
- clk_disable_unprepare(bank->clk);
mutex_unlock(&bank->deferred_lock);
return ret;
}
@@ -776,7 +773,6 @@ static void rockchip_gpio_remove(struct platform_device *pdev)
{
struct rockchip_pin_bank *bank = platform_get_drvdata(pdev);

- clk_disable_unprepare(bank->clk);
gpiochip_remove(&bank->gpio_chip);
}

diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c
index 8a313dd624c2..ff1977b26991 100644
--- a/drivers/gpio/gpio-virtuser.c
+++ b/drivers/gpio/gpio-virtuser.c
@@ -400,7 +400,7 @@ static ssize_t gpio_virtuser_direction_do_write(struct file *file,
char buf[32], *trimmed;
int ret, dir, val = 0;

- if (count >= sizeof(buf))
+ if (*ppos != 0 || count >= sizeof(buf))
return -EINVAL;

ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
@@ -627,7 +627,7 @@ static ssize_t gpio_virtuser_consumer_write(struct file *file,
char buf[GPIO_VIRTUSER_NAME_BUF_LEN + 2];
int ret;

- if (count >= sizeof(buf))
+ if (*ppos != 0 || count >= sizeof(buf))
return -EINVAL;

ret = simple_write_to_buffer(buf, GPIO_VIRTUSER_NAME_BUF_LEN, ppos,
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index aa723ad8ba98..6bca87b99f7c 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -2260,6 +2260,11 @@ static int criu_restore_devices(struct kfd_process *p,
ret = -EINVAL;
goto exit;
}
+
+ if (pdd->drm_file) {
+ ret = -EINVAL;
+ goto exit;
+ }
pdd->user_gpu_id = device_buckets[i].user_gpu_id;

drm_file = fget(device_buckets[i].drm_fd);
@@ -2270,11 +2275,6 @@ static int criu_restore_devices(struct kfd_process *p,
goto exit;
}

- if (pdd->drm_file) {
- ret = -EINVAL;
- goto exit;
- }
-
/* create the vm using render nodes for kfd pdd */
if (kfd_process_device_init_vm(pdd, drm_file)) {
pr_err("could not init vm for given pdd\n");
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index e841e3a51007..bd443133734e 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -3194,12 +3194,14 @@ static void copy_context_work_handler (struct work_struct *work)

static uint32_t *get_queue_ids(uint32_t num_queues, uint32_t *usr_queue_id_array)
{
- size_t array_size = num_queues * sizeof(uint32_t);
-
if (!usr_queue_id_array)
return NULL;

- return memdup_user(usr_queue_id_array, array_size);
+ if (num_queues > KFD_MAX_NUM_OF_QUEUES_PER_PROCESS)
+ return ERR_PTR(-EINVAL);
+
+ return memdup_user(usr_queue_id_array,
+ array_size(num_queues, sizeof(uint32_t)));
}

int resume_queues(struct kfd_process *p,
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
index 54ab7adeb444..31b6903e6b74 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
@@ -3668,6 +3668,9 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm,

svms = &p->svms;

+ if (!process_info)
+ return -EINVAL;
+
mutex_lock(&process_info->lock);

svm_range_list_lock_and_flush_work(svms, mm);
diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
index b5ecef3f75bb..4020ebcdff72 100644
--- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
+++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
@@ -3062,6 +3062,10 @@ static bool si_dpm_vblank_too_short(void *handle)
/* we never hit the non-gddr5 limit so disable it */
u32 switch_limit = adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 0;

+ /* Disregard vblank time when there are no displays connected */
+ if (!adev->pm.pm_display_cfg.num_display)
+ return false;
+
/* Consider zero vblank time too short and disable MCLK switching.
* Note that the vblank time is set to maximum when no displays are attached,
* so we'll still enable MCLK switching in that case.
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
index 26b8d137bce0..2baeb1c5301e 100644
--- a/drivers/gpu/drm/bridge/sil-sii8620.c
+++ b/drivers/gpu/drm/bridge/sil-sii8620.c
@@ -2220,6 +2220,7 @@ static void sii8620_detach(struct drm_bridge *bridge)
return;

rc_unregister_device(ctx->rc_dev);
+ rc_free_device(ctx->rc_dev);
}

static int sii8620_is_packing_required(struct sii8620 *ctx,
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
index 013a7829182d..5ef76b926a32 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
@@ -396,8 +396,11 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev)
return -ETIMEDOUT;
}

- if (msg->resolution_resp.resolution_count == 0) {
- drm_err(dev, "No supported resolutions\n");
+ if (msg->resolution_resp.resolution_count == 0 ||
+ msg->resolution_resp.resolution_count >
+ SYNTHVID_MAX_RESOLUTION_COUNT) {
+ drm_err(dev, "Invalid resolution count: %d\n",
+ msg->resolution_resp.resolution_count);
return -ENODEV;
}

@@ -422,30 +425,92 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev)
return 0;
}

-static void hyperv_receive_sub(struct hv_device *hdev)
+static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd)
{
struct hyperv_drm_device *hv = hv_get_drvdata(hdev);
struct synthvid_msg *msg;
+ size_t hdr_size;
+ size_t need;

if (!hv)
return;

- msg = (struct synthvid_msg *)hv->recv_buf;
-
- /* Complete the wait event */
- if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE ||
- msg->vid_hdr.type == SYNTHVID_RESOLUTION_RESPONSE ||
- msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) {
- memcpy(hv->init_buf, msg, VMBUS_MAX_PACKET_SIZE);
- complete(&hv->wait);
+ hdr_size = sizeof(struct pipe_msg_hdr) +
+ sizeof(struct synthvid_msg_hdr);
+ if (bytes_recvd < hdr_size) {
+ drm_err_ratelimited(&hv->dev,
+ "synthvid packet too small for header: %u\n",
+ bytes_recvd);
return;
}

- if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE) {
+ msg = (struct synthvid_msg *)hv->recv_buf;
+ need = hdr_size;
+
+ switch (msg->vid_hdr.type) {
+ case SYNTHVID_VERSION_RESPONSE:
+ need += sizeof(struct synthvid_version_resp);
+ break;
+ case SYNTHVID_RESOLUTION_RESPONSE:
+ /*
+ * The resolution response is variable length: the host
+ * fills resolution_count entries, not the full
+ * SYNTHVID_MAX_RESOLUTION_COUNT array. Require the fixed
+ * prefix first so resolution_count can be read, then
+ * demand exactly the count-sized array.
+ */
+ need += offsetof(struct synthvid_supported_resolution_resp,
+ supported_resolution);
+ if (bytes_recvd < need)
+ break;
+ if (msg->resolution_resp.resolution_count >
+ SYNTHVID_MAX_RESOLUTION_COUNT) {
+ drm_err_ratelimited(&hv->dev,
+ "synthvid resolution count too large: %u\n",
+ msg->resolution_resp.resolution_count);
+ return;
+ }
+ need += msg->resolution_resp.resolution_count *
+ sizeof(struct hvd_screen_info);
+ break;
+ case SYNTHVID_VRAM_LOCATION_ACK:
+ need += sizeof(struct synthvid_vram_location_ack);
+ break;
+ case SYNTHVID_FEATURE_CHANGE:
+ /*
+ * Not a completion-driving message: validate its own payload
+ * and consume it here rather than falling through to the
+ * memcpy/complete shared by the wait-event responses.
+ */
+ if (bytes_recvd < need +
+ sizeof(struct synthvid_feature_change)) {
+ drm_err_ratelimited(&hv->dev,
+ "synthvid feature change packet too small: %u\n",
+ bytes_recvd);
+ return;
+ }
hv->dirt_needed = msg->feature_chg.is_dirt_needed;
if (hv->dirt_needed)
hyperv_hide_hw_ptr(hv->hdev);
+ return;
+ default:
+ return;
+ }
+
+ /*
+ * Shared completion path for the wait-event responses
+ * (VERSION_RESPONSE, RESOLUTION_RESPONSE, VRAM_LOCATION_ACK):
+ * require the type-specific payload before handing the buffer to
+ * the waiter.
+ */
+ if (bytes_recvd < need) {
+ drm_err_ratelimited(&hv->dev,
+ "synthvid packet too small for type %u: %u < %zu\n",
+ msg->vid_hdr.type, bytes_recvd, need);
+ return;
}
+ memcpy(hv->init_buf, msg, bytes_recvd);
+ complete(&hv->wait);
}

static void hyperv_receive(void *ctx)
@@ -466,9 +531,21 @@ static void hyperv_receive(void *ctx)
ret = vmbus_recvpacket(hdev->channel, recv_buf,
VMBUS_MAX_PACKET_SIZE,
&bytes_recvd, &req_id);
- if (bytes_recvd > 0 &&
- recv_buf->pipe_hdr.type == PIPE_MSG_DATA)
- hyperv_receive_sub(hdev);
+ if (ret) {
+ /*
+ * A nonzero return (e.g. -ENOBUFS for an oversized
+ * packet) is itself a malformed message: bytes_recvd
+ * then reports the required length rather than a copied
+ * payload, so it must not be forwarded to the
+ * sub-handler. Channel recovery is not attempted.
+ */
+ drm_err_ratelimited(&hv->dev,
+ "vmbus_recvpacket failed: %d (need %u)\n",
+ ret, bytes_recvd);
+ } else if (bytes_recvd > 0 &&
+ recv_buf->pipe_hdr.type == PIPE_MSG_DATA) {
+ hyperv_receive_sub(hdev, bytes_recvd);
+ }
} while (bytes_recvd > 0 && ret == 0);
}

@@ -513,9 +590,13 @@ int hyperv_connect_vsp(struct hv_device *hdev)
ret = hyperv_get_supported_resolution(hdev);
if (ret)
drm_err(dev, "Failed to get supported resolution from host, use default\n");
- } else {
+ }
+
+ if (!hv->screen_width_max) {
hv->screen_width_max = SYNTHVID_WIDTH_WIN8;
hv->screen_height_max = SYNTHVID_HEIGHT_WIN8;
+ hv->preferred_width = SYNTHVID_WIDTH_WIN8;
+ hv->preferred_height = SYNTHVID_HEIGHT_WIN8;
}

hv->mmio_megabytes = hdev->channel->offermsg.offer.mmio_megabytes;
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index 2039c17a9ee7..992945b37190 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -1773,6 +1773,7 @@ struct intel_dp {
u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE];
u8 lttpr_phy_caps[DP_MAX_LTTPR_COUNT][DP_LTTPR_PHY_CAP_SIZE];
u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE];
+ u8 intel_wa_dpcd;
/* source rates */
int num_source_rates;
const int *source_rates;
diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h
new file mode 100644
index 000000000000..4aea5326f2ed
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_dpcd.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef __INTEL_DPCD_H__
+#define __INTEL_DPCD_H__
+
+#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS 0x3f0
+# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK REG_GENMASK(1, 0)
+# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1 0
+# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE 1
+# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE 2
+
+#endif /* __INTEL_DPCD_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
index 2a7f379c59fe..5173f5759ce8 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -35,6 +35,7 @@
#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_dp.h"
+#include "intel_dpcd.h"
#include "intel_dp_aux.h"
#include "intel_frontbuffer.h"
#include "intel_hdmi.h"
@@ -665,6 +666,12 @@ static void _psr_init_dpcd(struct intel_dp *intel_dp)
drm_dbg_kms(display->drm, "PSR2 %ssupported\n",
intel_dp->psr.sink_psr2_support ? "" : "not ");
}
+
+ if (intel_dp->psr.sink_psr2_support)
+ drm_dp_dpcd_read(&intel_dp->aux,
+ INTEL_DPCD_INTEL_WA_REGISTER_CAPS,
+ &intel_dp->intel_wa_dpcd,
+ sizeof(intel_dp->intel_wa_dpcd));
}

void intel_psr_init_dpcd(struct intel_dp *intel_dp)
@@ -1296,6 +1303,30 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp,
return true;
}

+static bool apply_scanline_indication_wa(struct intel_dp *intel_dp,
+ struct intel_crtc_state *crtc_state)
+{
+ u8 early_scanline_support = intel_dp->intel_wa_dpcd &
+ INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK;
+
+ if (intel_dp->edp_dpcd[0] >= DP_EDP_15)
+ return true;
+
+ switch (early_scanline_support) {
+ case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1:
+ crtc_state->req_psr2_sdp_prior_scanline = false;
+ return false;
+ case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE:
+ return true;
+ case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE:
+ crtc_state->req_psr2_sdp_prior_scanline = false;
+ return true;
+ default:
+ MISSING_CASE(early_scanline_support);
+ return false;
+ }
+}
+
static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_dp,
struct intel_crtc_state *crtc_state)
{
@@ -1317,7 +1348,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d
return false;

crtc_state->req_psr2_sdp_prior_scanline = true;
- return true;
+
+ return apply_scanline_indication_wa(intel_dp, crtc_state);
}

static int intel_psr_entry_setup_frames(struct intel_dp *intel_dp,
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
index b22e2019768f..ed610931f151 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -416,8 +416,6 @@ void i915_ttm_free_cached_io_rsgt(struct drm_i915_gem_object *obj)
int i915_ttm_purge(struct drm_i915_gem_object *obj)
{
struct ttm_buffer_object *bo = i915_gem_to_ttm(obj);
- struct i915_ttm_tt *i915_tt =
- container_of(bo->ttm, typeof(*i915_tt), ttm);
struct ttm_operation_ctx ctx = {
.interruptible = true,
.no_wait_gpu = false,
@@ -432,16 +430,22 @@ int i915_ttm_purge(struct drm_i915_gem_object *obj)
if (ret)
return ret;

- if (bo->ttm && i915_tt->filp) {
- /*
- * The below fput(which eventually calls shmem_truncate) might
- * be delayed by worker, so when directly called to purge the
- * pages(like by the shrinker) we should try to be more
- * aggressive and release the pages immediately.
- */
- shmem_truncate_range(file_inode(i915_tt->filp),
- 0, (loff_t)-1);
- fput(fetch_and_zero(&i915_tt->filp));
+ if (bo->ttm) {
+ struct i915_ttm_tt *i915_tt =
+ container_of(bo->ttm, typeof(*i915_tt), ttm);
+
+ if (i915_tt->filp) {
+ /*
+ * The below fput(which eventually calls shmem_truncate)
+ * might be delayed by worker, so when directly called
+ * to purge the pages(like by the shrinker) we should
+ * try to be more aggressive and release the pages
+ * immediately.
+ */
+ shmem_truncate_range(file_inode(i915_tt->filp),
+ 0, (loff_t)-1);
+ fput(fetch_and_zero(&i915_tt->filp));
+ }
}

obj->write_domain = 0;
diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c
index c9c88d3ad669..90eef062766c 100644
--- a/drivers/gpu/drm/v3d/v3d_sched.c
+++ b/drivers/gpu/drm/v3d/v3d_sched.c
@@ -103,20 +103,6 @@ v3d_performance_query_info_free(struct v3d_performance_query_info *query_info,
}
}

-static void
-v3d_cpu_job_free(struct drm_sched_job *sched_job)
-{
- struct v3d_cpu_job *job = to_cpu_job(sched_job);
-
- v3d_timestamp_query_info_free(&job->timestamp_query,
- job->timestamp_query.count);
-
- v3d_performance_query_info_free(&job->performance_query,
- job->performance_query.count);
-
- v3d_job_cleanup(&job->base);
-}
-
static void
v3d_switch_perfmon(struct v3d_dev *v3d, struct v3d_job *job)
{
@@ -846,7 +832,7 @@ static const struct drm_sched_backend_ops v3d_cache_clean_sched_ops = {
static const struct drm_sched_backend_ops v3d_cpu_sched_ops = {
.run_job = v3d_cpu_job_run,
.timedout_job = v3d_generic_job_timedout,
- .free_job = v3d_cpu_job_free
+ .free_job = v3d_sched_job_free
};

int
diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c
index ddc20191a1ce..23472c7af41a 100644
--- a/drivers/gpu/drm/v3d/v3d_submit.c
+++ b/drivers/gpu/drm/v3d/v3d_submit.c
@@ -118,6 +118,24 @@ v3d_render_job_free(struct kref *ref)
v3d_job_free(ref);
}

+static void
+v3d_cpu_job_free(struct kref *ref)
+{
+ struct v3d_cpu_job *job = container_of(ref, struct v3d_cpu_job,
+ base.refcount);
+
+ v3d_timestamp_query_info_free(&job->timestamp_query,
+ job->timestamp_query.count);
+
+ v3d_performance_query_info_free(&job->performance_query,
+ job->performance_query.count);
+
+ if (job->indirect_csd.indirect)
+ drm_gem_object_put(job->indirect_csd.indirect);
+
+ v3d_job_free(ref);
+}
+
void v3d_job_cleanup(struct v3d_job *job)
{
if (!job)
@@ -1310,7 +1328,7 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data,
trace_v3d_submit_cpu_ioctl(&v3d->drm, cpu_job->job_type);

ret = v3d_job_init(v3d, file_priv, &cpu_job->base,
- v3d_job_free, 0, &se, V3D_CPU);
+ v3d_cpu_job_free, 0, &se, V3D_CPU);
if (ret) {
v3d_job_deallocate((void *)&cpu_job);
goto fail;
@@ -1393,8 +1411,6 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data,
v3d_job_cleanup((void *)csd_job);
v3d_job_cleanup(clean_job);
v3d_put_multisync_post_deps(&se);
- kvfree(cpu_job->timestamp_query.queries);
- kvfree(cpu_job->performance_query.queries);

return ret;
}
diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
index 284861c166d9..b711d83dfde1 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.c
+++ b/drivers/hid/bpf/hid_bpf_dispatch.c
@@ -24,7 +24,8 @@ EXPORT_SYMBOL(hid_ops);

u8 *
dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data,
- u32 *size, int interrupt, u64 source, bool from_bpf)
+ size_t *buf_size, u32 *size, int interrupt, u64 source,
+ bool from_bpf)
{
struct hid_bpf_ctx_kern ctx_kern = {
.ctx = {
@@ -74,6 +75,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type
*size = ret;
}

+ *buf_size = ctx_kern.ctx.allocated_size;
return ctx_kern.data;
}
EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event);
@@ -514,7 +516,7 @@ __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *b
if (ret)
return ret;

- return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (u64)(long)ctx, true,
+ return hid_ops->hid_input_report(ctx->hid, type, buf, size, size, 0, (u64)(long)ctx, true,
lock_already_taken);
}

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 294a25330ed0..87d990ada868 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1998,24 +1998,32 @@ int __hid_request(struct hid_device *hid, struct hid_report *report,
}
EXPORT_SYMBOL_GPL(__hid_request);

-int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
- int interrupt)
+int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
+ size_t bufsize, u32 size, int interrupt)
{
struct hid_report_enum *report_enum = hid->report_enum + type;
struct hid_report *report;
struct hid_driver *hdrv;
int max_buffer_size = HID_MAX_BUFFER_SIZE;
u32 rsize, csize = size;
+ size_t bsize = bufsize;
u8 *cdata = data;
int ret = 0;

report = hid_get_report(report_enum, data);
if (!report)
- goto out;
+ return 0;
+
+ if (unlikely(bsize < csize)) {
+ hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %zu)\n",
+ report->id, csize, bsize);
+ return -EINVAL;
+ }

if (report_enum->numbered) {
cdata++;
csize--;
+ bsize--;
}

rsize = hid_compute_report_size(report);
@@ -2028,9 +2036,15 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
else if (rsize > max_buffer_size)
rsize = max_buffer_size;

+ if (bsize < rsize) {
+ hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %zu)\n",
+ report->id, rsize, bsize);
+ return -EINVAL;
+ }
+
if (csize < rsize) {
dbg_hid("report %d is too short, (%d < %d)\n", report->id,
- csize, rsize);
+ csize, rsize);
memset(cdata + csize, 0, rsize - csize);
}

@@ -2039,7 +2053,7 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
if (hid->claimed & HID_CLAIMED_HIDRAW) {
ret = hidraw_report_event(hid, data, size);
if (ret)
- goto out;
+ return ret;
}

if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
@@ -2051,15 +2065,15 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *

if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_report_event(hid, report);
-out:
+
return ret;
}
EXPORT_SYMBOL_GPL(hid_report_raw_event);


static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
- u8 *data, u32 size, int interrupt, u64 source, bool from_bpf,
- bool lock_already_taken)
+ u8 *data, size_t bufsize, u32 size, int interrupt, u64 source,
+ bool from_bpf, bool lock_already_taken)
{
struct hid_report_enum *report_enum;
struct hid_driver *hdrv;
@@ -2084,7 +2098,8 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
report_enum = hid->report_enum + type;
hdrv = hid->driver;

- data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf);
+ data = dispatch_hid_bpf_device_event(hid, type, data, &bufsize, &size, interrupt,
+ source, from_bpf);
if (IS_ERR(data)) {
ret = PTR_ERR(data);
goto unlock;
@@ -2113,7 +2128,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
goto unlock;
}

- ret = hid_report_raw_event(hid, type, data, size, interrupt);
+ ret = hid_report_raw_event(hid, type, data, bufsize, size, interrupt);

unlock:
if (!lock_already_taken)
@@ -2131,16 +2146,41 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
* @interrupt: distinguish between interrupt and control transfers
*
* This is data entry for lower layers.
+ * Legacy, please use hid_safe_input_report() instead.
*/
int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
int interrupt)
{
- return __hid_input_report(hid, type, data, size, interrupt, 0,
+ return __hid_input_report(hid, type, data, size, size, interrupt, 0,
false, /* from_bpf */
false /* lock_already_taken */);
}
EXPORT_SYMBOL_GPL(hid_input_report);

+/**
+ * hid_safe_input_report - report data from lower layer (usb, bt...)
+ *
+ * @hid: hid device
+ * @type: HID report type (HID_*_REPORT)
+ * @data: report contents
+ * @bufsize: allocated size of the data buffer
+ * @size: useful size of data parameter
+ * @interrupt: distinguish between interrupt and control transfers
+ *
+ * This is data entry for lower layers.
+ * Please use this function instead of the non safe version because we provide
+ * here the size of the buffer, allowing hid-core to make smarter decisions
+ * regarding the incoming buffer.
+ */
+int hid_safe_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data,
+ size_t bufsize, u32 size, int interrupt)
+{
+ return __hid_input_report(hid, type, data, bufsize, size, interrupt, 0,
+ false, /* from_bpf */
+ false /* lock_already_taken */);
+}
+EXPORT_SYMBOL_GPL(hid_safe_input_report);
+
bool hid_match_one_id(const struct hid_device *hdev,
const struct hid_device_id *id)
{
diff --git a/drivers/hid/hid-gfrm.c b/drivers/hid/hid-gfrm.c
index 699186ff2349..d2a56bf92b41 100644
--- a/drivers/hid/hid-gfrm.c
+++ b/drivers/hid/hid-gfrm.c
@@ -66,7 +66,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,
switch (data[1]) {
case GFRM100_SEARCH_KEY_DOWN:
ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_dn,
- sizeof(search_key_dn), 1);
+ sizeof(search_key_dn), sizeof(search_key_dn), 1);
break;

case GFRM100_SEARCH_KEY_AUDIO_DATA:
@@ -74,7 +74,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,

case GFRM100_SEARCH_KEY_UP:
ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_up,
- sizeof(search_key_up), 1);
+ sizeof(search_key_up), sizeof(search_key_up), 1);
break;

default:
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 475e6eb4702a..3453b5dbf589 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -1230,6 +1230,7 @@

#define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f
#define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002
+#define USB_DEVICE_ID_SIGMA_MICRO_USB_MOUSE 0x0034
#define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD2 0x0059

#define USB_VENDOR_ID_SIGMATEL 0x066F
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index d60cd4379e86..858ac2ab46bd 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -3691,7 +3691,7 @@ static int hidpp10_consumer_keys_raw_event(struct hidpp_device *hidpp,
memcpy(&consumer_report[1], &data[3], 4);
/* We are called from atomic context */
hid_report_raw_event(hidpp->hid_dev, HID_INPUT_REPORT,
- consumer_report, 5, 1);
+ consumer_report, sizeof(consumer_report), 5, 1);

return 1;
}
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index fcf9a806f109..760f9db44c9e 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -500,7 +500,7 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
}

ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf,
- size, 0);
+ size, size, 0);
if (ret)
dev_warn(&hdev->dev, "failed to report feature\n");
}
diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
index d6faa0e00f95..6d4c636e1c9f 100644
--- a/drivers/hid/hid-picolcd_cir.c
+++ b/drivers/hid/hid-picolcd_cir.c
@@ -134,5 +134,6 @@ void picolcd_exit_cir(struct picolcd_data *data)

data->rc_dev = NULL;
rc_unregister_device(rdev);
+ rc_free_device(rdev);
}

diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c
index e44d79dff8de..8db054280afb 100644
--- a/drivers/hid/hid-primax.c
+++ b/drivers/hid/hid-primax.c
@@ -44,7 +44,7 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report,
data[0] |= (1 << (data[idx] - 0xE0));
data[idx] = 0;
}
- hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, 0);
+ hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, size, 0);
return 1;

default: /* unknown report */
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 9d396d2e534d..39d81777cb7e 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -186,6 +186,7 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD), HID_QUIRK_NO_INIT_REPORTS },
{ HID_USB_DEVICE(USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB), HID_QUIRK_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD), HID_QUIRK_NO_INIT_REPORTS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_USB_MOUSE), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_SIGMATEL, USB_DEVICE_ID_SIGMATEL_STMP3780), HID_QUIRK_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS1030_TOUCH), HID_QUIRK_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS817_TOUCH), HID_QUIRK_NOGET },
diff --git a/drivers/hid/hid-vivaldi-common.c b/drivers/hid/hid-vivaldi-common.c
index bf734055d4b6..b12bb5cc091a 100644
--- a/drivers/hid/hid-vivaldi-common.c
+++ b/drivers/hid/hid-vivaldi-common.c
@@ -85,7 +85,7 @@ void vivaldi_feature_mapping(struct hid_device *hdev,
}

ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
- report_len, 0);
+ report_len, report_len, 0);
if (ret) {
dev_warn(&hdev->dev, "failed to report feature %d\n",
field->report->id);
diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
index cf8ae0df0cda..8ce0535fc42d 100644
--- a/drivers/hid/i2c-hid/i2c-hid-core.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -568,9 +568,10 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
if (ihid->hid->group != HID_GROUP_RMI)
pm_wakeup_event(&ihid->client->dev, 0);

- hid_input_report(ihid->hid, HID_INPUT_REPORT,
- ihid->inbuf + sizeof(__le16),
- ret_size - sizeof(__le16), 1);
+ hid_safe_input_report(ihid->hid, HID_INPUT_REPORT,
+ ihid->inbuf + sizeof(__le16),
+ ihid->bufsize - sizeof(__le16),
+ ret_size - sizeof(__le16), 1);
}

return;
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index f14b46ce00cb..336ad7cf3d48 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -283,9 +283,9 @@ static void hid_irq_in(struct urb *urb)
break;
usbhid_mark_busy(usbhid);
if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
- hid_input_report(urb->context, HID_INPUT_REPORT,
- urb->transfer_buffer,
- urb->actual_length, 1);
+ hid_safe_input_report(urb->context, HID_INPUT_REPORT,
+ urb->transfer_buffer, urb->transfer_buffer_length,
+ urb->actual_length, 1);
/*
* autosuspend refused while keys are pressed
* because most keyboards don't wake up when
@@ -482,9 +482,10 @@ static void hid_ctrl(struct urb *urb)
switch (status) {
case 0: /* success */
if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
- hid_input_report(urb->context,
+ hid_safe_input_report(urb->context,
usbhid->ctrl[usbhid->ctrltail].report->type,
- urb->transfer_buffer, urb->actual_length, 0);
+ urb->transfer_buffer, urb->transfer_buffer_length,
+ urb->actual_length, 0);
break;
case -ESHUTDOWN: /* unplug */
unplug = 1;
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 1b1112772777..2b679b99bd5a 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -74,7 +74,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev,
int err;

size = kfifo_out(fifo, buf, sizeof(buf));
- err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, false);
+ err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, size, false);
if (err) {
hid_warn(hdev, "%s: unable to flush event due to error %d\n",
__func__, err);
@@ -319,7 +319,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,
data, n, WAC_CMD_RETRIES);
if (ret == n && features->type == HID_GENERIC) {
ret = hid_report_raw_event(hdev,
- HID_FEATURE_REPORT, data, n, 0);
+ HID_FEATURE_REPORT, data, n, n, 0);
} else if (ret == 2 && features->type != HID_GENERIC) {
features->touch_max = data[1];
} else {
@@ -341,6 +341,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,

hid_data->inputmode = field->report->id;
hid_data->inputmode_index = usage->usage_index;
+ hid_data->inputmode_field_index = field->index;
break;

case HID_UP_DIGITIZER:
@@ -380,7 +381,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,
data, n, WAC_CMD_RETRIES);
if (ret == n) {
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT,
- data, n, 0);
+ data, n, n, 0);
} else {
hid_warn(hdev, "%s: could not retrieve sensor offsets\n",
__func__);
@@ -556,9 +557,14 @@ static int wacom_hid_set_device_mode(struct hid_device *hdev)

re = &(hdev->report_enum[HID_FEATURE_REPORT]);
r = re->report_id_hash[hid_data->inputmode];
- if (r) {
- r->field[0]->value[hid_data->inputmode_index] = 2;
- hid_hw_request(hdev, r, HID_REQ_SET_REPORT);
+ if (r && hid_data->inputmode_field_index >= 0 &&
+ hid_data->inputmode_field_index < r->maxfield) {
+ struct hid_field *field = r->field[hid_data->inputmode_field_index];
+
+ if (field && hid_data->inputmode_index < field->report_count) {
+ field->value[hid_data->inputmode_index] = 2;
+ hid_hw_request(hdev, r, HID_REQ_SET_REPORT);
+ }
}
return 0;
}
@@ -2819,6 +2825,7 @@ static int wacom_probe(struct hid_device *hdev,
return error;

wacom_wac->hid_data.inputmode = -1;
+ wacom_wac->hid_data.inputmode_field_index = -1;
wacom_wac->mode_report = -1;

if (hid_is_usb(hdev)) {
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index c8803d5c6a71..b2e74d7ab3c4 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -298,6 +298,7 @@ struct wacom_shared {
struct hid_data {
__s16 inputmode; /* InputMode HID feature, -1 if non-existent */
__s16 inputmode_index; /* InputMode HID feature index in the report */
+ __s16 inputmode_field_index; /* InputMode HID feature field index in the report */
bool sense_state;
bool inrange_state;
bool invert_state;
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 432a7088e22b..e303517d74f9 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -173,7 +173,12 @@ static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset)
else
pmbus_cmd = ADM1266_PDIO_STATUS;

+ ret = pmbus_lock_interruptible(data->client);
+ if (ret)
+ return ret;
+
ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, read_buf);
+ pmbus_unlock(data->client);
if (ret < 0)
return ret;
if (ret < 2)
@@ -195,11 +200,19 @@ static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask
unsigned int gpio_nr;
int ret;

+ ret = pmbus_lock_interruptible(data->client);
+ if (ret)
+ return ret;
+
ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, read_buf);
- if (ret < 0)
+ if (ret < 0) {
+ pmbus_unlock(data->client);
return ret;
- if (ret < 2)
+ }
+ if (ret < 2) {
+ pmbus_unlock(data->client);
return -EIO;
+ }

status = read_buf[0] + (read_buf[1] << 8);

@@ -210,10 +223,14 @@ static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask
}

ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, read_buf);
- if (ret < 0)
+ if (ret < 0) {
+ pmbus_unlock(data->client);
return ret;
- if (ret < 2)
+ }
+ if (ret < 2) {
+ pmbus_unlock(data->client);
return -EIO;
+ }

status = read_buf[0] + (read_buf[1] << 8);

@@ -222,6 +239,8 @@ static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask
set_bit(gpio_nr, bits);
}

+ pmbus_unlock(data->client);
+
return 0;
}

@@ -236,11 +255,16 @@ static void adm1266_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
int ret;
int i;

+ if (pmbus_lock_interruptible(data->client))
+ return;
+
for (i = 0; i < ADM1266_GPIO_NR; i++) {
write_cmd = adm1266_gpio_mapping[i][1];
ret = adm1266_pmbus_block_xfer(data, ADM1266_GPIO_CONFIG, 1, &write_cmd, read_buf);
- if (ret != 2)
+ if (ret != 2) {
+ pmbus_unlock(data->client);
return;
+ }

gpio_config = read_buf[0];
seq_puts(s, adm1266_names[i]);
@@ -262,8 +286,10 @@ static void adm1266_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)

write_cmd = 0xFF;
ret = adm1266_pmbus_block_xfer(data, ADM1266_PDIO_CONFIG, 1, &write_cmd, read_buf);
- if (ret != 32)
+ if (ret != 32) {
+ pmbus_unlock(data->client);
return;
+ }

for (i = 0; i < ADM1266_PDIO_NR; i++) {
seq_puts(s, adm1266_names[ADM1266_GPIO_NR + i]);
@@ -286,6 +312,8 @@ static void adm1266_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)

seq_puts(s, ")\n");
}
+
+ pmbus_unlock(data->client);
}

static int adm1266_config_gpio(struct adm1266_data *data)
@@ -328,7 +356,12 @@ static int adm1266_state_read(struct seq_file *s, void *pdata)
struct i2c_client *client = to_i2c_client(dev);
int ret;

+ ret = pmbus_lock_interruptible(client);
+ if (ret)
+ return ret;
+
ret = i2c_smbus_read_word_data(client, ADM1266_READ_STATE);
+ pmbus_unlock(client);
if (ret < 0)
return ret;

@@ -393,18 +426,25 @@ static int adm1266_nvmem_read(void *priv, unsigned int offset, void *val, size_t
if (offset + bytes > data->nvmem_config.size)
return -EINVAL;

+ ret = pmbus_lock_interruptible(data->client);
+ if (ret)
+ return ret;
+
if (offset == 0) {
memset(data->dev_mem, 0, data->nvmem_config.size);

ret = adm1266_nvmem_read_blackbox(data, data->dev_mem);
if (ret) {
dev_err(&data->client->dev, "Could not read blackbox!");
+ pmbus_unlock(data->client);
return ret;
}
}

memcpy(val, data->dev_mem + offset, bytes);

+ pmbus_unlock(data->client);
+
return 0;
}

diff --git a/drivers/iio/adc/mt6359-auxadc.c b/drivers/iio/adc/mt6359-auxadc.c
index a4970cfb49a5..51693e46fdc4 100644
--- a/drivers/iio/adc/mt6359-auxadc.c
+++ b/drivers/iio/adc/mt6359-auxadc.c
@@ -347,6 +347,7 @@ static int mt6358_read_imp(struct mt6359_auxadc *adc_dev, int *vbat, int *ibat)
return ret;

/* Read the params before stopping */
+ val_v = 0;
regmap_read(regmap, reg_adc0 + (cinfo->imp_adc_num << 1), &val_v);

mt6358_stop_imp_conv(adc_dev);
diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c
index 3a55465951e7..84f306b76c57 100644
--- a/drivers/iio/adc/npcm_adc.c
+++ b/drivers/iio/adc/npcm_adc.c
@@ -231,7 +231,7 @@ static int npcm_adc_probe(struct platform_device *pdev)
if (IS_ERR(info->reset))
return PTR_ERR(info->reset);

- info->adc_clk = devm_clk_get(&pdev->dev, NULL);
+ info->adc_clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(info->adc_clk)) {
dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n");
return PTR_ERR(info->adc_clk);
@@ -244,17 +244,13 @@ static int npcm_adc_probe(struct platform_device *pdev)
info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);

irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- ret = irq;
- goto err_disable_clk;
- }
+ if (irq < 0)
+ return irq;

ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0,
"NPCM_ADC", indio_dev);
- if (ret < 0) {
- dev_err(dev, "failed requesting interrupt\n");
- goto err_disable_clk;
- }
+ if (ret < 0)
+ return ret;

reg_con = ioread32(info->regs + NPCM_ADCCON);
info->vref = devm_regulator_get_optional(&pdev->dev, "vref");
@@ -262,7 +258,7 @@ static int npcm_adc_probe(struct platform_device *pdev)
ret = regulator_enable(info->vref);
if (ret) {
dev_err(&pdev->dev, "Can't enable ADC reference voltage\n");
- goto err_disable_clk;
+ return ret;
}

iowrite32(reg_con & ~NPCM_ADCCON_REFSEL,
@@ -272,10 +268,8 @@ static int npcm_adc_probe(struct platform_device *pdev)
* Any error which is not ENODEV indicates the regulator
* has been specified and so is a failure case.
*/
- if (PTR_ERR(info->vref) != -ENODEV) {
- ret = PTR_ERR(info->vref);
- goto err_disable_clk;
- }
+ if (PTR_ERR(info->vref) != -ENODEV)
+ return PTR_ERR(info->vref);

/* Use internal reference */
iowrite32(reg_con | NPCM_ADCCON_REFSEL,
@@ -314,8 +308,6 @@ static int npcm_adc_probe(struct platform_device *pdev)
iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
if (!IS_ERR(info->vref))
regulator_disable(info->vref);
-err_disable_clk:
- clk_disable_unprepare(info->adc_clk);

return ret;
}
@@ -332,7 +324,6 @@ static void npcm_adc_remove(struct platform_device *pdev)
iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
if (!IS_ERR(info->vref))
regulator_disable(info->vref);
- clk_disable_unprepare(info->adc_clk);
}

static struct platform_driver npcm_adc_driver = {
diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c
index 1028b101cf56..8723b21c0230 100644
--- a/drivers/iio/adc/viperboard_adc.c
+++ b/drivers/iio/adc/viperboard_adc.c
@@ -70,8 +70,10 @@ static int vprbrd_iio_read_raw(struct iio_dev *iio_dev,
VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg,
sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
if (ret != sizeof(struct vprbrd_adc_msg)) {
- dev_err(&iio_dev->dev, "usb send error on adc read\n");
+ mutex_unlock(&vb->lock);
error = -EREMOTEIO;
+ dev_err(&iio_dev->dev, "usb send error on adc read\n");
+ goto error;
}

ret = usb_control_msg(vb->usb_dev,
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
index cfbfcaefec0f..a17e25d31420 100644
--- a/drivers/iio/adc/xilinx-xadc-core.c
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -817,6 +817,7 @@ static int xadc_postdisable(struct iio_dev *indio_dev)
{
struct xadc *xadc = iio_priv(indio_dev);
unsigned long scan_mask;
+ int seq_mode;
int ret;
int i;

@@ -824,6 +825,12 @@ static int xadc_postdisable(struct iio_dev *indio_dev)
for (i = 0; i < indio_dev->num_channels; i++)
scan_mask |= BIT(indio_dev->channels[i].scan_index);

+ /*
+ * Use the correct sequencer mode for the idle state: simultaneous
+ * mode for dual external mux configurations, continuous otherwise.
+ */
+ seq_mode = xadc_get_seq_mode(xadc, scan_mask);
+
/* Enable all channels and calibration */
ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff);
if (ret)
@@ -834,11 +841,11 @@ static int xadc_postdisable(struct iio_dev *indio_dev)
return ret;

ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK,
- XADC_CONF1_SEQ_CONTINUOUS);
+ seq_mode);
if (ret)
return ret;

- return xadc_power_adc_b(xadc, XADC_CONF1_SEQ_CONTINUOUS);
+ return xadc_power_adc_b(xadc, seq_mode);
}

static int xadc_preenable(struct iio_dev *indio_dev)
diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c
index 526b2a8d725d..534252db48cf 100644
--- a/drivers/iio/buffer/industrialio-hw-consumer.c
+++ b/drivers/iio/buffer/industrialio-hw-consumer.c
@@ -82,7 +82,7 @@ static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
*/
struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
{
- struct hw_consumer_buffer *buf;
+ struct hw_consumer_buffer *buf, *tmp;
struct iio_hw_consumer *hwc;
struct iio_channel *chan;
int ret;
@@ -113,7 +113,7 @@ struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
return hwc;

err_put_buffers:
- list_for_each_entry(buf, &hwc->buffers, head)
+ list_for_each_entry_safe(buf, tmp, &hwc->buffers, head)
iio_buffer_put(&buf->buffer);
iio_channel_release_all(hwc->channels);
err_free_hwc:
diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c
index 7be5a45cf71a..6ecdc7a07199 100644
--- a/drivers/iio/chemical/scd30_core.c
+++ b/drivers/iio/chemical/scd30_core.c
@@ -5,6 +5,7 @@
* Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@xxxxxxxxxxx>
*/
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -198,112 +199,104 @@ static int scd30_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const
int *val, int *val2, long mask)
{
struct scd30_state *state = iio_priv(indio_dev);
- int ret = -EINVAL;
+ int ret;
u16 tmp;

- mutex_lock(&state->lock);
+ guard(mutex)(&state->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
case IIO_CHAN_INFO_PROCESSED:
if (chan->output) {
*val = state->pressure_comp;
- ret = IIO_VAL_INT;
- break;
+ return IIO_VAL_INT;
}

ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
- break;
+ return ret;

ret = scd30_read(state);
if (ret) {
iio_device_release_direct_mode(indio_dev);
- break;
+ return ret;
}

*val = state->meas[chan->address];
iio_device_release_direct_mode(indio_dev);
- ret = IIO_VAL_INT;
- break;
+ return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = 1;
- ret = IIO_VAL_INT_PLUS_MICRO;
- break;
+ return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = scd30_command_read(state, CMD_MEAS_INTERVAL, &tmp);
if (ret)
- break;
+ return ret;

*val = 0;
*val2 = 1000000000 / tmp;
- ret = IIO_VAL_INT_PLUS_NANO;
- break;
+ return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_CALIBBIAS:
ret = scd30_command_read(state, CMD_TEMP_OFFSET, &tmp);
if (ret)
- break;
+ return ret;

*val = tmp;
- ret = IIO_VAL_INT;
- break;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
}
- mutex_unlock(&state->lock);
-
- return ret;
}

static int scd30_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct scd30_state *state = iio_priv(indio_dev);
- int ret = -EINVAL;
+ int ret;

- mutex_lock(&state->lock);
+ guard(mutex)(&state->lock);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
- if (val)
- break;
+ if (val || !val2)
+ return -EINVAL;

val = 1000000000 / val2;
if (val < SCD30_MEAS_INTERVAL_MIN_S || val > SCD30_MEAS_INTERVAL_MAX_S)
- break;
+ return -EINVAL;

ret = scd30_command_write(state, CMD_MEAS_INTERVAL, val);
if (ret)
- break;
+ return ret;

state->meas_interval = val;
- break;
+ return 0;
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_PRESSURE:
if (val < SCD30_PRESSURE_COMP_MIN_MBAR ||
val > SCD30_PRESSURE_COMP_MAX_MBAR)
- break;
+ return -EINVAL;

ret = scd30_command_write(state, CMD_START_MEAS, val);
if (ret)
- break;
+ return ret;

state->pressure_comp = val;
- break;
+ return 0;
default:
- break;
+ return -EINVAL;
}
- break;
case IIO_CHAN_INFO_CALIBBIAS:
if (val < 0 || val > SCD30_TEMP_OFFSET_MAX)
- break;
+ return -EINVAL;
/*
* Manufacturer does not explicitly specify min/max sensible
* values hence check is omitted for simplicity.
*/
- ret = scd30_command_write(state, CMD_TEMP_OFFSET / 10, val);
+ return scd30_command_write(state, CMD_TEMP_OFFSET / 10, val);
+ default:
+ return -EINVAL;
}
- mutex_unlock(&state->lock);
-
- return ret;
}

static int scd30_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c
index 0c6629da2112..e44d47222867 100644
--- a/drivers/iio/common/ssp_sensors/ssp_dev.c
+++ b/drivers/iio/common/ssp_sensors/ssp_dev.c
@@ -590,6 +590,7 @@ static void ssp_remove(struct spi_device *spi)
ssp_clean_pending_list(data);

free_irq(data->spi->irq, data);
+ cancel_delayed_work_sync(&data->work_refresh);

del_timer_sync(&data->wdt_timer);
cancel_work_sync(&data->work_wdt);
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 4c9f7ade52b3..0f9f4b7d6ba3 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -30,6 +30,8 @@ static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev,
{
struct ad5686_state *st = iio_priv(indio_dev);

+ guard(mutex)(&st->lock);
+
return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1;
}

@@ -39,6 +41,8 @@ static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev,
{
struct ad5686_state *st = iio_priv(indio_dev);

+ guard(mutex)(&st->lock);
+
st->pwr_down_mode &= ~(0x3 << (chan->channel * 2));
st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2));

@@ -57,6 +61,8 @@ static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev,
{
struct ad5686_state *st = iio_priv(indio_dev);

+ guard(mutex)(&st->lock);
+
return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask &
(0x3 << (chan->channel * 2))));
}
@@ -77,6 +83,8 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
if (ret)
return ret;

+ guard(mutex)(&st->lock);
+
if (readin)
st->pwr_down_mask |= (0x3 << (chan->channel * 2));
else
@@ -154,7 +162,7 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,

switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (val > (1 << chan->scan_type.realbits) || val < 0)
+ if (val >= (1 << chan->scan_type.realbits) || val < 0)
return -EINVAL;

mutex_lock(&st->lock);
@@ -520,7 +528,7 @@ int ad5686_probe(struct device *dev,
break;
case AD5686_REGMAP:
cmd = AD5686_CMD_INTERNAL_REFER_SETUP;
- ref_bit_msk = 0;
+ ref_bit_msk = AD5686_REF_BIT_MSK;
break;
case AD5693_REGMAP:
cmd = AD5686_CMD_CONTROL_REG;
@@ -532,9 +540,9 @@ int ad5686_probe(struct device *dev,
goto error_disable_reg;
}

- val = (voltage_uv | ref_bit_msk);
+ val = voltage_uv ? ref_bit_msk : 0;

- ret = st->write(st, cmd, 0, !!val);
+ ret = st->write(st, cmd, 0, val);
if (ret)
goto error_disable_reg;

diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h
index 760f852911df..182d951912d8 100644
--- a/drivers/iio/dac/ad5686.h
+++ b/drivers/iio/dac/ad5686.h
@@ -46,6 +46,7 @@

#define AD5310_REF_BIT_MSK BIT(8)
#define AD5683_REF_BIT_MSK BIT(12)
+#define AD5686_REF_BIT_MSK BIT(0)
#define AD5693_REF_BIT_MSK BIT(12)

/**
diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c
index 18ba3eaaad75..77700dc11e60 100644
--- a/drivers/iio/dac/max5821.c
+++ b/drivers/iio/dac/max5821.c
@@ -91,6 +91,7 @@ static int max5821_sync_powerdown_mode(struct max5821_data *data,
const struct iio_chan_spec *chan)
{
u8 outbuf[2];
+ int ret;

outbuf[0] = MAX5821_EXTENDED_COMMAND_MODE;

@@ -104,7 +105,13 @@ static int max5821_sync_powerdown_mode(struct max5821_data *data,
else
outbuf[1] |= MAX5821_EXTENDED_POWER_UP;

- return i2c_master_send(data->client, outbuf, 2);
+ ret = i2c_master_send(data->client, outbuf, sizeof(outbuf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(outbuf))
+ return -EIO;
+
+ return 0;
}

static ssize_t max5821_write_dac_powerdown(struct iio_dev *indio_dev,
diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c
index 495b64a27061..9a17fad0e4e4 100644
--- a/drivers/iio/gyro/adis16260.c
+++ b/drivers/iio/gyro/adis16260.c
@@ -287,6 +287,9 @@ static int adis16260_write_raw(struct iio_dev *indio_dev,
addr = adis16260_addresses[chan->scan_index][1];
return adis_write_reg_16(adis, addr, val);
case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val <= 0)
+ return -EINVAL;
+
if (spi_get_device_id(adis->spi)->driver_data)
t = 256 / val;
else
diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c
index d1c125a77308..5391a5bd0cc7 100644
--- a/drivers/iio/gyro/itg3200_buffer.c
+++ b/drivers/iio/gyro/itg3200_buffer.c
@@ -34,7 +34,7 @@ static int itg3200_read_all_channels(struct i2c_client *i2c, __be16 *buf)
.addr = i2c->addr,
.flags = i2c->flags | I2C_M_RD,
.len = ITG3200_SCAN_ELEMENTS * sizeof(s16),
- .buf = (char *)&buf,
+ .buf = (char *)buf,
},
};

diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
index 07b81e523e63..7cd12de50bf5 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
@@ -608,7 +608,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
* must be passed a buffer that is aligned to 8 bytes so
* as to allow insertion of a naturally aligned timestamp.
*/
- u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE] __aligned(8);
+ u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE] __aligned(8) = { };
u8 tag;
bool reset_ts = false;
int i, err, read_len;
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 989b70ec923a..1fb3abe75edc 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1911,6 +1911,7 @@ static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib,

dma_resv_add_fence(dmabuf->resv, &fence->base,
dma_to_ram ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);
+ dma_fence_put(&fence->base);
dma_resv_unlock(dmabuf->resv);

cookie = dma_fence_begin_signalling();
diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c
index 79a64e2ff812..b55cb78ec571 100644
--- a/drivers/iio/light/cm3323.c
+++ b/drivers/iio/light/cm3323.c
@@ -89,15 +89,14 @@ static int cm3323_init(struct iio_dev *indio_dev)

/* enable sensor and set auto force mode */
ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT);
+ data->reg_conf = ret;

- ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, ret);
+ ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, data->reg_conf);
if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_conf\n");
return ret;
}

- data->reg_conf = ret;
-
return 0;
}

diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
index 6cc0dfd31821..9bb58c355fe2 100644
--- a/drivers/iio/magnetometer/st_magn_core.c
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -506,6 +506,11 @@ static const struct st_sensors_platform_data default_magn_pdata = {
.drdy_int_pin = 2,
};

+/* LIS2MDL only supports DRDY on INT1 */
+static const struct st_sensors_platform_data alt_magn_pdata = {
+ .drdy_int_pin = 1,
+};
+
static int st_magn_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *ch, int *val,
int *val2, long mask)
@@ -628,8 +633,12 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
mdata->current_fullscale = &mdata->sensor_settings->fs.fs_avl[0];
mdata->odr = mdata->sensor_settings->odr.odr_avl[0].hz;

- if (!pdata)
- pdata = (struct st_sensors_platform_data *)&default_magn_pdata;
+ if (!pdata) {
+ if (mdata->sensor_settings->drdy_irq.int2.mask)
+ pdata = (struct st_sensors_platform_data *)&default_magn_pdata;
+ else
+ pdata = (struct st_sensors_platform_data *)&alt_magn_pdata;
+ }

err = st_sensors_init_sensor(indio_dev, pdata);
if (err < 0)
diff --git a/drivers/iio/temperature/tsys01.c b/drivers/iio/temperature/tsys01.c
index 9213761c5d18..e85f8c24f3ce 100644
--- a/drivers/iio/temperature/tsys01.c
+++ b/drivers/iio/temperature/tsys01.c
@@ -119,7 +119,7 @@ static bool tsys01_crc_valid(u16 *n_prom)
u8 sum = 0;

for (cnt = 0; cnt < TSYS01_PROM_WORDS_NB; cnt++)
- sum += ((n_prom[0] >> 8) + (n_prom[0] & 0xFF));
+ sum += ((n_prom[cnt] >> 8) + (n_prom[cnt] & 0xFF));

return (sum == 0);
}
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index e66e1ea2af2d..4d35f730b585 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -215,6 +215,10 @@ static const struct xpad_device {
{ 0x07ff, 0xffff, "Mad Catz GamePad", 0, XTYPE_XBOX360 },
{ 0x0b05, 0x1a38, "ASUS ROG RAIKIRI", MAP_SHARE_BUTTON, XTYPE_XBOXONE },
{ 0x0b05, 0x1abb, "ASUS ROG RAIKIRI PRO", 0, XTYPE_XBOXONE },
+ { 0x0b05, 0x1c91, "ASUS ROG RAIKIRI II", 0, XTYPE_XBOX360 },
+ { 0x0b05, 0x1c92, "ASUS ROG RAIKIRI II WIRELESS", 0, XTYPE_XBOX360 },
+ { 0x0b05, 0x1c96, "ASUS ROG RAIKIRI II XBOX", MAP_SHARE_BUTTON, XTYPE_XBOXONE },
+ { 0x0b05, 0x1d04, "ASUS ROG RAIKIRI II XBOX WIRELESS", MAP_SHARE_BUTTON, XTYPE_XBOXONE },
{ 0x0c12, 0x0005, "Intec wireless", 0, XTYPE_XBOX },
{ 0x0c12, 0x8801, "Nyko Xbox Controller", 0, XTYPE_XBOX },
{ 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX },
@@ -415,6 +419,7 @@ static const struct xpad_device {
{ 0x3285, 0x0662, "Nacon Revolution5 Pro", 0, XTYPE_XBOX360 },
{ 0x3285, 0x0663, "Nacon Evol-X", 0, XTYPE_XBOXONE },
{ 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 },
{ 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX },
{ 0x413d, 0x2104, "Black Shark Green Ghost Gamepad", 0, XTYPE_XBOX360 },
@@ -527,6 +532,7 @@ static const struct usb_device_id xpad_table[] = {
{ USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */
XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */
XPAD_XBOX360_VENDOR(0x07ff), /* Mad Catz Gamepad */
+ XPAD_XBOX360_VENDOR(0x0b05), /* ASUS controllers */
XPAD_XBOXONE_VENDOR(0x0b05), /* ASUS controllers */
XPAD_XBOX360_VENDOR(0x0c12), /* Zeroplus X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x0db0), /* Micro Star International X-Box 360 controllers */
@@ -1081,10 +1087,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char
input_report_key(dev, BTN_START, data[4] & BIT(2));
input_report_key(dev, BTN_SELECT, data[4] & BIT(3));
if (xpad->mapping & MAP_SHARE_BUTTON) {
- if (xpad->mapping & MAP_SHARE_OFFSET)
- input_report_key(dev, KEY_RECORD, data[len - 26] & BIT(0));
- else
- input_report_key(dev, KEY_RECORD, data[len - 18] & BIT(0));
+ u32 offset = (xpad->mapping & MAP_SHARE_OFFSET) ? 26 : 18;
+
+ if (len >= offset)
+ input_report_key(dev, KEY_RECORD, data[len - offset] & BIT(0));
}

/* buttons A,B,X,Y */
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index fc22cbb854a3..bcbaefe03e47 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -1604,7 +1604,7 @@ static void ims_pcu_buffers_free(struct ims_pcu *pcu)
usb_kill_urb(pcu->urb_in);
usb_free_urb(pcu->urb_in);

- usb_free_coherent(pcu->udev, pcu->max_out_size,
+ usb_free_coherent(pcu->udev, pcu->max_in_size,
pcu->urb_in_buf, pcu->read_dma);

kfree(pcu->urb_out_buf);
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index 7521981274bd..500de9659cf6 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -645,6 +645,11 @@ static ssize_t elan_sysfs_update_fw(struct device *dev,
return error;
}

+ if (fw->size < data->fw_signature_address + sizeof(signature)) {
+ dev_err(dev, "firmware file too small\n");
+ return -EBADF;
+ }
+
/* Firmware file must match signature data */
fw_signature = &fw->data[data->fw_signature_address];
if (memcmp(fw_signature, signature, sizeof(signature)) != 0) {
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 2b8895368437..edd4bf4670ad 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -189,6 +189,7 @@ static const char * const smbus_pnp_ids[] = {
"LEN2044", /* L470 */
"LEN2054", /* E480 */
"LEN2055", /* E580 */
+ "LEN2058", /* E490 */
"LEN2068", /* T14 Gen 1 */
"SYN1221", /* TUXEDO InfinityBook Pro 14 v5 */
"SYN3003", /* HP EliteBook 850 G1 */
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index d7496d47eabe..c832e41e4a02 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1476,7 +1476,7 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg)
}
cfg->raw_pos += offset;

- if (i > mxt_obj_size(object))
+ if (i >= mxt_obj_size(object))
continue;

byte_offset = reg + i - cfg->start_ofs;
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index 7567efabe014..cc4a0d3b8a80 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -1070,6 +1070,11 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)
if (x_len > 0xff)
x_len -= 0x80;

+ if (data_len > usbtouch->data_size - sizeof(*packet))
+ data_len = usbtouch->data_size - sizeof(*packet);
+ if (x_len > data_len)
+ x_len = data_len;
+
/* send ACK */
ret = usb_submit_urb(priv->ack, GFP_ATOMIC);
if (ret)
diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c
index 06ffc683b28f..3364efa94741 100644
--- a/drivers/iommu/io-pgtable-arm-v7s.c
+++ b/drivers/iommu/io-pgtable-arm-v7s.c
@@ -894,21 +894,27 @@ struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns = {

static struct io_pgtable_cfg *cfg_cookie __initdata;

-static void __init dummy_tlb_flush_all(void *cookie)
+/*
+ * __noipa prevents gcc from turning indirect iommu_flush_ops calls
+ * into direct calls from a specialized __arm_v7s_unmap() that triggers
+ * a build time section mismatch assertion.
+ */
+static __noipa void __init dummy_tlb_flush_all(void *cookie)
{
WARN_ON(cookie != cfg_cookie);
}

-static void __init dummy_tlb_flush(unsigned long iova, size_t size,
- size_t granule, void *cookie)
+static __noipa void __init dummy_tlb_flush(unsigned long iova, size_t size,
+ size_t granule, void *cookie)
{
WARN_ON(cookie != cfg_cookie);
WARN_ON(!(size & cfg_cookie->pgsize_bitmap));
}

-static void __init dummy_tlb_add_page(struct iommu_iotlb_gather *gather,
- unsigned long iova, size_t granule,
- void *cookie)
+static __noipa void __init dummy_tlb_add_page(struct iommu_iotlb_gather *gather,
+ unsigned long iova,
+ size_t granule,
+ void *cookie)
{
dummy_tlb_flush(iova, granule, granule, cookie);
}
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 0ad55649e2d0..62e1d6372503 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -3341,9 +3341,11 @@ static int __iommu_set_group_pasid(struct iommu_domain *domain,
int ret;

for_each_group_device(group, device) {
- ret = domain->ops->set_dev_pasid(domain, device->dev, pasid);
- if (ret)
- goto err_revert;
+ if (device->dev->iommu->max_pasids > 0) {
+ ret = domain->ops->set_dev_pasid(domain, device->dev, pasid);
+ if (ret)
+ goto err_revert;
+ }
}

return 0;
@@ -3355,7 +3357,8 @@ static int __iommu_set_group_pasid(struct iommu_domain *domain,

if (device == last_gdev)
break;
- ops->remove_dev_pasid(device->dev, pasid, domain);
+ if (device->dev->iommu->max_pasids > 0)
+ ops->remove_dev_pasid(device->dev, pasid, domain);
}
return ret;
}
@@ -3368,8 +3371,10 @@ static void __iommu_remove_group_pasid(struct iommu_group *group,
const struct iommu_ops *ops;

for_each_group_device(group, device) {
- ops = dev_iommu_ops(device->dev);
- ops->remove_dev_pasid(device->dev, pasid, domain);
+ if (device->dev->iommu->max_pasids > 0) {
+ ops = dev_iommu_ops(device->dev);
+ ops->remove_dev_pasid(device->dev, pasid, domain);
+ }
}
}

@@ -3403,7 +3408,13 @@ int iommu_attach_device_pasid(struct iommu_domain *domain,

mutex_lock(&group->mutex);
for_each_group_device(group, device) {
- if (pasid >= device->dev->iommu->max_pasids) {
+ /*
+ * Skip PASID validation for devices without PASID support
+ * (max_pasids = 0). These devices cannot issue transactions
+ * with PASID, so they don't affect group's PASID usage.
+ */
+ if ((device->dev->iommu->max_pasids > 0) &&
+ (pasid >= device->dev->iommu->max_pasids)) {
ret = -EINVAL;
goto out_unlock;
}
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 6e0ac0958c10..f969ea434925 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -1378,7 +1378,8 @@ static CLOSURE_CALLBACK(cached_dev_free)
* The sb_bio is embedded in struct cached_dev, so we must
* ensure no I/O is in progress.
*/
- closure_sync(&dc->sb_write);
+ down(&dc->sb_write_mutex);
+ up(&dc->sb_write_mutex);

if (dc->sb_disk)
put_page(virt_to_page(dc->sb_disk));
diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
index 865d86f34add..b3c0710eb5b1 100644
--- a/drivers/media/cec/core/cec-core.c
+++ b/drivers/media/cec/core/cec-core.c
@@ -337,8 +337,8 @@ int cec_register_adapter(struct cec_adapter *adap,
res = cec_devnode_register(&adap->devnode, adap->owner);
if (res) {
#ifdef CONFIG_MEDIA_CEC_RC
- /* Note: rc_unregister also calls rc_free */
rc_unregister_device(adap->rc);
+ rc_free_device(adap->rc);
adap->rc = NULL;
#endif
return res;
diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
index d85c78c104b9..5f4c0aa7a0d7 100644
--- a/drivers/media/common/siano/smsir.c
+++ b/drivers/media/common/siano/smsir.c
@@ -92,6 +92,7 @@ int sms_ir_init(struct smscore_device_t *coredev)
void sms_ir_exit(struct smscore_device_t *coredev)
{
rc_unregister_device(coredev->ir.dev);
+ rc_free_device(coredev->ir.dev);

pr_debug("\n");
}
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 5588cdd7ec20..604745317004 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -355,6 +355,7 @@ static void ir_work(struct work_struct *work)
mutex_unlock(&ir->lock);
if (rc == -ENODEV) {
rc_unregister_device(ir->rc);
+ rc_free_device(ir->rc);
ir->rc = NULL;
return;
}
@@ -972,6 +973,7 @@ static void ir_remove(struct i2c_client *client)
i2c_unregister_device(ir->tx_c);

rc_unregister_device(ir->rc);
+ rc_free_device(ir->rc);
}

static const struct i2c_device_id ir_kbd_id[] = {
diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c
index 41226f1d0e5b..d70e6282c48b 100644
--- a/drivers/media/pci/bt8xx/bttv-input.c
+++ b/drivers/media/pci/bt8xx/bttv-input.c
@@ -572,8 +572,9 @@ void bttv_input_fini(struct bttv *btv)
if (btv->remote == NULL)
return;

- bttv_ir_stop(btv);
rc_unregister_device(btv->remote->dev);
+ bttv_ir_stop(btv);
+ rc_free_device(btv->remote->dev);
kfree(btv->remote);
btv->remote = NULL;
}
diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
index d2e84c6457e0..722329ef3fd2 100644
--- a/drivers/media/pci/cx23885/cx23885-input.c
+++ b/drivers/media/pci/cx23885/cx23885-input.c
@@ -402,6 +402,7 @@ void cx23885_input_fini(struct cx23885_dev *dev)
if (dev->kernel_ir == NULL)
return;
rc_unregister_device(dev->kernel_ir->rc);
+ rc_free_device(dev->kernel_ir->rc);
kfree(dev->kernel_ir->phys);
kfree(dev->kernel_ir->name);
kfree(dev->kernel_ir);
diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
index a04a1d33fadb..74a8769dd6c7 100644
--- a/drivers/media/pci/cx88/cx88-input.c
+++ b/drivers/media/pci/cx88/cx88-input.c
@@ -510,8 +510,9 @@ int cx88_ir_fini(struct cx88_core *core)
if (!ir)
return 0;

- cx88_ir_stop(core);
rc_unregister_device(ir->dev);
+ cx88_ir_stop(core);
+ rc_free_device(ir->dev);
kfree(ir);

/* done */
diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
index 9e9c7c071acc..e1185aa669f4 100644
--- a/drivers/media/pci/dm1105/dm1105.c
+++ b/drivers/media/pci/dm1105/dm1105.c
@@ -763,6 +763,7 @@ static int dm1105_ir_init(struct dm1105_dev *dm1105)
static void dm1105_ir_exit(struct dm1105_dev *dm1105)
{
rc_unregister_device(dm1105->ir.dev);
+ rc_free_device(dm1105->ir.dev);
}

static int dm1105_hw_init(struct dm1105_dev *dev)
diff --git a/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c
index 34c0d979240f..edb4cacf55d2 100644
--- a/drivers/media/pci/mantis/mantis_input.c
+++ b/drivers/media/pci/mantis/mantis_input.c
@@ -72,5 +72,6 @@ EXPORT_SYMBOL_GPL(mantis_input_init);
void mantis_input_exit(struct mantis_pci *mantis)
{
rc_unregister_device(mantis->rc);
+ rc_free_device(mantis->rc);
}
EXPORT_SYMBOL_GPL(mantis_input_exit);
diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c
index 8610eb473b39..8a0f26d94d1d 100644
--- a/drivers/media/pci/saa7134/saa7134-input.c
+++ b/drivers/media/pci/saa7134/saa7134-input.c
@@ -834,6 +834,7 @@ void saa7134_input_fini(struct saa7134_dev *dev)
return;

rc_unregister_device(dev->remote->dev);
+ rc_free_device(dev->remote->dev);
kfree(dev->remote);
dev->remote = NULL;
}
diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c
index c0604d9c7011..0bbe4fa2d5a8 100644
--- a/drivers/media/pci/smipcie/smipcie-ir.c
+++ b/drivers/media/pci/smipcie/smipcie-ir.c
@@ -181,5 +181,6 @@ void smi_ir_exit(struct smi_dev *dev)

rc_unregister_device(rc_dev);
smi_ir_stop(ir);
+ rc_free_device(rc_dev);
ir->rc_dev = NULL;
}
diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c
index 33f08adf4feb..16973ac8e6a9 100644
--- a/drivers/media/pci/ttpci/budget-ci.c
+++ b/drivers/media/pci/ttpci/budget-ci.c
@@ -249,6 +249,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci)
cancel_work_sync(&budget_ci->ir.msp430_irq_bh_work);

rc_unregister_device(budget_ci->ir.dev);
+ rc_free_device(budget_ci->ir.dev);
}

static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
index d7721e60776e..46d1844f5c98 100644
--- a/drivers/media/rc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -921,7 +921,6 @@ static int ati_remote_probe(struct usb_interface *interface,
input_free_device(input_dev);
exit_unregister_device:
rc_unregister_device(rc_dev);
- rc_dev = NULL;
exit_kill_urbs:
usb_kill_urb(ati_remote->irq_urb);
usb_kill_urb(ati_remote->out_urb);
@@ -941,18 +940,19 @@ static void ati_remote_disconnect(struct usb_interface *interface)
struct ati_remote *ati_remote;

ati_remote = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
if (!ati_remote) {
dev_warn(&interface->dev, "%s - null device?\n", __func__);
return;
}

+ rc_unregister_device(ati_remote->rdev);
+ usb_set_intfdata(interface, NULL);
usb_kill_urb(ati_remote->irq_urb);
usb_kill_urb(ati_remote->out_urb);
if (ati_remote->idev)
input_unregister_device(ati_remote->idev);
- rc_unregister_device(ati_remote->rdev);
ati_remote_free_buffers(ati_remote);
+ rc_free_device(ati_remote->rdev);
kfree(ati_remote);
}

diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index 67722e2e47ff..3fd51a41c3b2 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1090,7 +1090,6 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
release_region(dev->hw_io, ENE_IO_SIZE);
exit_unregister_device:
rc_unregister_device(rdev);
- rdev = NULL;
exit_free_dev_rdev:
rc_free_device(rdev);
kfree(dev);
@@ -1110,6 +1109,7 @@ static void ene_remove(struct pnp_dev *pnp_dev)
ene_rx_restore_hw_buffer(dev);
spin_unlock_irqrestore(&dev->hw_lock, flags);

+ rc_free_device(dev->rdev);
free_irq(dev->irq, dev);
release_region(dev->hw_io, ENE_IO_SIZE);
kfree(dev);
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index 3fb0968efd57..9b789097cdd4 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -568,6 +568,7 @@ static void fintek_remove(struct pnp_dev *pdev)
struct fintek_dev *fintek = pnp_get_drvdata(pdev);
unsigned long flags;

+ rc_unregister_device(fintek->rdev);
spin_lock_irqsave(&fintek->fintek_lock, flags);
/* disable CIR */
fintek_disable_cir(fintek);
@@ -580,7 +581,7 @@ static void fintek_remove(struct pnp_dev *pdev)
free_irq(fintek->cir_irq, fintek);
release_region(fintek->cir_addr, fintek->cir_port_len);

- rc_unregister_device(fintek->rdev);
+ rc_free_device(fintek->rdev);

kfree(fintek);
}
diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c
index f3616607d4f5..c97dd5ed6eda 100644
--- a/drivers/media/rc/igorplugusb.c
+++ b/drivers/media/rc/igorplugusb.c
@@ -184,7 +184,7 @@ static int igorplugusb_probe(struct usb_interface *intf,
if (!ir->buf_in)
goto fail;
usb_fill_control_urb(ir->urb, udev,
- usb_rcvctrlpipe(udev, 0), (uint8_t *)&ir->request,
+ usb_rcvctrlpipe(udev, 0), (uint8_t *)ir->request,
ir->buf_in, MAX_PACKET, igorplugusb_callback, ir);

usb_make_path(udev, ir->phys, sizeof(ir->phys));
@@ -247,6 +247,7 @@ static void igorplugusb_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
usb_unpoison_urb(ir->urb);
usb_free_urb(ir->urb);
+ rc_free_device(ir->rc);
kfree(ir->buf_in);
kfree(ir->request);
}
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index 8af94246e591..7bd6dd725415 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -500,6 +500,7 @@ static void iguanair_disconnect(struct usb_interface *intf)
usb_set_intfdata(intf, NULL);
usb_kill_urb(ir->urb_in);
usb_kill_urb(ir->urb_out);
+ rc_free_device(ir->rc);
usb_free_urb(ir->urb_in);
usb_free_urb(ir->urb_out);
usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c
index 5da7479c1793..07f41372976e 100644
--- a/drivers/media/rc/img-ir/img-ir-hw.c
+++ b/drivers/media/rc/img-ir/img-ir-hw.c
@@ -1117,9 +1117,10 @@ void img_ir_remove_hw(struct img_ir_priv *priv)
struct rc_dev *rdev = hw->rdev;
if (!rdev)
return;
+ rc_unregister_device(rdev);
img_ir_set_decoder(priv, NULL, 0);
hw->rdev = NULL;
- rc_unregister_device(rdev);
+ rc_free_device(rdev);
#ifdef CONFIG_COMMON_CLK
if (!IS_ERR(priv->clk))
clk_notifier_unregister(priv->clk, &hw->clk_nb);
diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c
index 8b0bdd9603b3..533d40dae542 100644
--- a/drivers/media/rc/img-ir/img-ir-raw.c
+++ b/drivers/media/rc/img-ir/img-ir-raw.c
@@ -136,6 +136,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv)
if (!rdev)
return;

+ rc_unregister_device(rdev);
/* switch off and disable raw (edge) interrupts */
spin_lock_irq(&priv->lock);
raw->rdev = NULL;
@@ -145,7 +146,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv)
img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
spin_unlock_irq(&priv->lock);

- rc_unregister_device(rdev);
+ rc_free_device(rdev);

del_timer_sync(&raw->timer);
}
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index ddb1304cb77b..cb9bd5a6ff54 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -2546,9 +2546,10 @@ static void imon_disconnect(struct usb_interface *interface)

if (ifnum == 0) {
ictx->dev_present_intf0 = false;
+ rc_unregister_device(ictx->rdev);
usb_kill_urb(ictx->rx_urb_intf0);
input_unregister_device(ictx->idev);
- rc_unregister_device(ictx->rdev);
+ rc_free_device(ictx->rdev);
if (ictx->display_supported) {
if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
usb_deregister_dev(interface, &imon_lcd_class);
diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
index de5bb9a08ea4..1604679fa2c8 100644
--- a/drivers/media/rc/ir-hix5hd2.c
+++ b/drivers/media/rc/ir-hix5hd2.c
@@ -331,7 +331,6 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)

regerr:
rc_unregister_device(rdev);
- rdev = NULL;
clkerr:
clk_disable_unprepare(priv->clock);
err:
@@ -346,6 +345,7 @@ static void hix5hd2_ir_remove(struct platform_device *pdev)

clk_disable_unprepare(priv->clock);
rc_unregister_device(priv->rdev);
+ rc_free_device(priv->rdev);
}

#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c
index 533faa117517..e79de56997a4 100644
--- a/drivers/media/rc/ir_toy.c
+++ b/drivers/media/rc/ir_toy.c
@@ -536,6 +536,7 @@ static void irtoy_disconnect(struct usb_interface *intf)
usb_free_urb(ir->urb_out);
usb_kill_urb(ir->urb_in);
usb_free_urb(ir->urb_in);
+ rc_free_device(ir->rc);
kfree(ir->in);
kfree(ir->out);
kfree(ir);
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 2bacecb02262..23afbafb5574 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1414,7 +1414,6 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
release_region(itdev->cir_addr, itdev->params->io_region_size);
exit_unregister_device:
rc_unregister_device(rdev);
- rdev = NULL;
exit_free_dev_rdev:
rc_free_device(rdev);
kfree(itdev);
@@ -1439,6 +1438,7 @@ static void ite_remove(struct pnp_dev *pdev)
release_region(dev->cir_addr, dev->params->io_region_size);

rc_unregister_device(dev->rdev);
+ rc_free_device(dev->rdev);

kfree(dev);
}
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index cd7af4d88b7f..bf93b94d337f 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -1851,6 +1851,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf)
usb_free_urb(ir->urb_in);
usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in);
usb_put_dev(dev);
+ rc_free_device(ir->rc);

kfree(ir);
}
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index 16e33d7eaaa2..dd70f8ad5266 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -647,9 +647,6 @@ int ir_raw_event_register(struct rc_dev *dev)

void ir_raw_event_free(struct rc_dev *dev)
{
- if (!dev)
- return;
-
kfree(dev->raw);
dev->raw = NULL;
}
@@ -673,8 +670,6 @@ void ir_raw_event_unregister(struct rc_dev *dev)

lirc_bpf_free(dev);

- ir_raw_event_free(dev);
-
/*
* A user can be calling bpf(BPF_PROG_{QUERY|ATTACH|DETACH}), so
* ensure that the raw member is null on unlock; this is how
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index 8288366f891f..a108b057b5fd 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -263,6 +263,7 @@ static int __init loop_init(void)
static void __exit loop_exit(void)
{
rc_unregister_device(loopdev.dev);
+ rc_free_device(loopdev.dev);
}

module_init(loop_init);
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index a4c539b17cf3..a4c0ec06ee6a 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -1611,6 +1611,7 @@ static void rc_dev_release(struct device *device)
{
struct rc_dev *dev = to_rc_dev(device);

+ ir_raw_event_free(dev);
kfree(dev);
}

@@ -1773,7 +1774,6 @@ struct rc_dev *devm_rc_allocate_device(struct device *dev,
}

rc->dev.parent = dev;
- rc->managed_alloc = true;
*dr = rc;
devres_add(dev, dr);

@@ -2042,11 +2042,7 @@ void rc_unregister_device(struct rc_dev *dev)
device_del(&dev->dev);

ida_free(&rc_ida, dev->minor);
-
- if (!dev->managed_alloc)
- rc_free_device(dev);
}
-
EXPORT_SYMBOL_GPL(rc_unregister_device);

/*
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index a49173f54a4d..b8289327f6a2 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -1133,11 +1133,13 @@ static void redrat3_dev_disconnect(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct redrat3_dev *rr3 = usb_get_intfdata(intf);
+ struct rc_dev *rc = rr3->rc;

usb_set_intfdata(intf, NULL);
- rc_unregister_device(rr3->rc);
+ rc_unregister_device(rc);
led_classdev_unregister(&rr3->led);
redrat3_delete(rr3, udev);
+ rc_free_device(rc);
}

static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message)
diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
index fd2f056f287b..79aad3d7f69f 100644
--- a/drivers/media/rc/st_rc.c
+++ b/drivers/media/rc/st_rc.c
@@ -203,6 +203,7 @@ static void st_rc_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, false);
clk_disable_unprepare(rc_dev->sys_clock);
rc_unregister_device(rc_dev->rdev);
+ rc_free_device(rc_dev->rdev);
}

static int st_rc_open(struct rc_dev *rdev)
@@ -334,7 +335,6 @@ static int st_rc_probe(struct platform_device *pdev)
return ret;
rcerr:
rc_unregister_device(rdev);
- rdev = NULL;
clkerr:
clk_disable_unprepare(rc_dev->sys_clock);
err:
diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
index 8e9b156e4300..8c85b9f30a3a 100644
--- a/drivers/media/rc/streamzap.c
+++ b/drivers/media/rc/streamzap.c
@@ -392,15 +392,16 @@ static void streamzap_disconnect(struct usb_interface *interface)
struct streamzap_ir *sz = usb_get_intfdata(interface);
struct usb_device *usbdev = interface_to_usbdev(interface);

- usb_set_intfdata(interface, NULL);
-
if (!sz)
return;

- usb_kill_urb(sz->urb_in);
rc_unregister_device(sz->rdev);
+ usb_set_intfdata(interface, NULL);
+
+ usb_kill_urb(sz->urb_in);
usb_free_urb(sz->urb_in);
usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+ rc_free_device(sz->rdev);

kfree(sz);
}
diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index b49df8355e6b..448d453cfda9 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -371,6 +371,7 @@ 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);
}

diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c
index dde446a95eaa..3452b5aefd28 100644
--- a/drivers/media/rc/ttusbir.c
+++ b/drivers/media/rc/ttusbir.c
@@ -191,7 +191,7 @@ static int ttusbir_probe(struct usb_interface *intf,
tt = kzalloc(sizeof(*tt), GFP_KERNEL);
buffer = kzalloc(5, GFP_KERNEL);
rc = rc_allocate_device(RC_DRIVER_IR_RAW);
- if (!tt || !rc || buffer) {
+ if (!tt || !rc || !buffer) {
ret = -ENOMEM;
goto out;
}
@@ -336,7 +336,6 @@ static int ttusbir_probe(struct usb_interface *intf,
return 0;
out3:
rc_unregister_device(rc);
- rc = NULL;
out2:
led_classdev_unregister(&tt->led);
out:
@@ -378,6 +377,7 @@ static void ttusbir_disconnect(struct usb_interface *intf)
usb_kill_urb(tt->bulk_urb);
usb_free_urb(tt->bulk_urb);
kfree(tt->bulk_buffer);
+ rc_free_device(tt->rc);
usb_set_intfdata(intf, NULL);
kfree(tt);
}
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 25884a79985c..14d8b58e2839 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -1132,7 +1132,6 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
release_region(data->wbase, WAKEUP_IOMEM_LEN);
exit_unregister_device:
rc_unregister_device(data->dev);
- data->dev = NULL;
exit_free_rc:
rc_free_device(data->dev);
exit_unregister_led:
@@ -1163,6 +1162,7 @@ wbcir_remove(struct pnp_dev *device)
wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);

rc_unregister_device(data->dev);
+ rc_free_device(data->dev);

led_classdev_unregister(&data->led);

diff --git a/drivers/media/rc/xbox_remote.c b/drivers/media/rc/xbox_remote.c
index 0c9c855ced72..80b7c247932a 100644
--- a/drivers/media/rc/xbox_remote.c
+++ b/drivers/media/rc/xbox_remote.c
@@ -283,14 +283,15 @@ static void xbox_remote_disconnect(struct usb_interface *interface)
struct xbox_remote *xbox_remote;

xbox_remote = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
if (!xbox_remote) {
dev_warn(&interface->dev, "%s - null device?\n", __func__);
return;
}

- usb_kill_urb(xbox_remote->irq_urb);
rc_unregister_device(xbox_remote->rdev);
+ usb_set_intfdata(interface, NULL);
+ usb_kill_urb(xbox_remote->irq_urb);
+ rc_free_device(xbox_remote->rdev);
usb_free_urb(xbox_remote->irq_urb);
kfree(xbox_remote->inbuf);
kfree(xbox_remote);
diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c
index 3d3368202cd0..283ad2c6288c 100644
--- a/drivers/media/usb/au0828/au0828-input.c
+++ b/drivers/media/usb/au0828/au0828-input.c
@@ -357,6 +357,7 @@ void au0828_rc_unregister(struct au0828_dev *dev)
return;

rc_unregister_device(ir->rc);
+ rc_free_device(ir->rc);

/* done */
kfree(ir);
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
index f1c79f351ec8..17e8961179d1 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
@@ -187,6 +187,7 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d)
if (d->rc_dev) {
cancel_delayed_work_sync(&d->rc_query_work);
rc_unregister_device(d->rc_dev);
+ rc_free_device(d->rc_dev);
d->rc_dev = NULL;
}

diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
index 65e2c9e2cdc9..6dc11718dfb9 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
@@ -347,10 +347,12 @@ int dvb_usb_remote_exit(struct dvb_usb_device *d)
{
if (d->state & DVB_USB_STATE_REMOTE) {
cancel_delayed_work_sync(&d->rc_query_work);
- if (d->props.rc.mode == DVB_RC_LEGACY)
+ if (d->props.rc.mode == DVB_RC_LEGACY) {
input_unregister_device(d->input_dev);
- else
+ } else {
rc_unregister_device(d->rc_dev);
+ rc_free_device(d->rc_dev);
+ }
}
d->state &= ~DVB_USB_STATE_REMOTE;
return 0;
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
index 5f3b00869bdb..26f333b5be73 100644
--- a/drivers/media/usb/em28xx/em28xx-input.c
+++ b/drivers/media/usb/em28xx/em28xx-input.c
@@ -853,6 +853,7 @@ static int em28xx_ir_fini(struct em28xx *dev)
goto ref_put;

rc_unregister_device(ir->rc);
+ rc_free_device(ir->rc);

kfree(ir->i2c_client);

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 1b2cd7f87035..c6b114946d9a 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1927,6 +1927,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
int link_reporting;
int res = 0, i;

+ if (slave_dev->type == ARPHRD_CAN) {
+ BOND_NL_ERR(bond_dev, extack,
+ "CAN devices cannot be enslaved");
+ return -EPERM;
+ }
+
if (slave_dev->flags & IFF_MASTER &&
!netif_is_bond_master(slave_dev)) {
BOND_NL_ERR(bond_dev, extack,
diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.c b/drivers/net/ethernet/intel/ice/ice_virtchnl.c
index 471d64d202b7..e98acbc41a81 100644
--- a/drivers/net/ethernet/intel/ice/ice_virtchnl.c
+++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.c
@@ -1746,7 +1746,7 @@ static int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg)

if (qpi->rxq.databuffer_size != 0 &&
(qpi->rxq.databuffer_size > ((16 * 1024) - 128) ||
- qpi->rxq.databuffer_size < 1024))
+ qpi->rxq.databuffer_size < 128))
goto error_param;
ring->rx_buf_len = qpi->rxq.databuffer_size;
if (qpi->rxq.max_pkt_size > max_frame_size ||
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
index f75afcf5f5ae..0d5571bc8b4a 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
@@ -1386,11 +1386,13 @@ int otx2_pool_init(struct otx2_nic *pfvf, u16 pool_id,
err = otx2_sync_mbox_msg(&pfvf->mbox);
if (err) {
qmem_free(pfvf->dev, pool->stack);
+ pool->stack = NULL;
return err;
}
aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox);
if (!aq) {
qmem_free(pfvf->dev, pool->stack);
+ pool->stack = NULL;
return -ENOMEM;
}
}
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index e527139936de..0e4b0ac4acf8 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -1304,6 +1304,9 @@ static void mana_fence_rqs(struct mana_port_context *apc)
struct mana_rxq *rxq;
int err;

+ if (!apc->rxqs)
+ return;
+
for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) {
rxq = apc->rxqs[rxq_idx];
err = mana_fence_rq(apc, rxq);
@@ -2324,13 +2327,16 @@ static void mana_destroy_vport(struct mana_port_context *apc)
struct mana_rxq *rxq;
u32 rxq_idx;

- for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) {
- rxq = apc->rxqs[rxq_idx];
- if (!rxq)
- continue;
+ if (apc->rxqs) {

- mana_destroy_rxq(apc, rxq, true);
- apc->rxqs[rxq_idx] = NULL;
+ for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) {
+ rxq = apc->rxqs[rxq_idx];
+ if (!rxq)
+ continue;
+
+ mana_destroy_rxq(apc, rxq, true);
+ apc->rxqs[rxq_idx] = NULL;
+ }
}

mana_destroy_txq(apc);
@@ -2633,7 +2639,8 @@ static int mana_dealloc_queues(struct net_device *ndev)
if (apc->port_is_up)
return -EINVAL;

- mana_chn_setxdp(apc, NULL);
+ if (apc->rxqs)
+ mana_chn_setxdp(apc, NULL);

if (gd->gdma_context->is_pf)
mana_pf_deregister_filter(apc);
@@ -2651,33 +2658,38 @@ static int mana_dealloc_queues(struct net_device *ndev)
* number of queues.
*/

- for (i = 0; i < apc->num_queues; i++) {
- txq = &apc->tx_qp[i].txq;
- tsleep = 1000;
- while (atomic_read(&txq->pending_sends) > 0 &&
- time_before(jiffies, timeout)) {
- usleep_range(tsleep, tsleep + 1000);
- tsleep <<= 1;
- }
- if (atomic_read(&txq->pending_sends)) {
- err = pcie_flr(to_pci_dev(gd->gdma_context->dev));
- if (err) {
- netdev_err(ndev, "flr failed %d with %d pkts pending in txq %u\n",
- err, atomic_read(&txq->pending_sends),
- txq->gdma_txq_id);
+ if (apc->tx_qp) {
+ for (i = 0; i < apc->num_queues; i++) {
+ txq = &apc->tx_qp[i].txq;
+ tsleep = 1000;
+ while (atomic_read(&txq->pending_sends) > 0 &&
+ time_before(jiffies, timeout)) {
+ usleep_range(tsleep, tsleep + 1000);
+ tsleep <<= 1;
+ }
+ if (atomic_read(&txq->pending_sends)) {
+ err =
+ pcie_flr(to_pci_dev(gd->gdma_context->dev));
+ if (err) {
+ netdev_err(ndev, "flr failed %d with %d pkts pending in txq %u\n",
+ err,
+ atomic_read(&txq->pending_sends),
+ txq->gdma_txq_id);
+ }
+ break;
}
- break;
}
- }

- for (i = 0; i < apc->num_queues; i++) {
- txq = &apc->tx_qp[i].txq;
- while ((skb = skb_dequeue(&txq->pending_skbs))) {
- mana_unmap_skb(skb, apc);
- dev_kfree_skb_any(skb);
+ for (i = 0; i < apc->num_queues; i++) {
+ txq = &apc->tx_qp[i].txq;
+ while ((skb = skb_dequeue(&txq->pending_skbs))) {
+ mana_unmap_skb(skb, apc);
+ dev_kfree_skb_any(skb);
+ }
+ atomic_set(&txq->pending_sends, 0);
}
- atomic_set(&txq->pending_sends, 0);
}
+
/* We're 100% sure the queues can no longer be woken up, because
* we're sure now mana_poll_tx_cq() can't be running.
*/
diff --git a/drivers/net/ethernet/ti/cpsw_new.c b/drivers/net/ethernet/ti/cpsw_new.c
index a74caaca94d1..fa161a109604 100644
--- a/drivers/net/ethernet/ti/cpsw_new.c
+++ b/drivers/net/ethernet/ti/cpsw_new.c
@@ -1443,7 +1443,8 @@ static void cpsw_unregister_ports(struct cpsw_common *cpsw)
int i = 0;

for (i = 0; i < cpsw->data.slaves; i++) {
- if (!cpsw->slaves[i].ndev)
+ if (!cpsw->slaves[i].ndev ||
+ cpsw->slaves[i].ndev->reg_state != NETREG_REGISTERED)
continue;

unregister_netdev(cpsw->slaves[i].ndev);
@@ -1463,7 +1464,6 @@ static int cpsw_register_ports(struct cpsw_common *cpsw)
if (ret) {
dev_err(cpsw->dev,
"cpsw: err registering net device%d\n", i);
- cpsw->slaves[i].ndev = NULL;
break;
}
}
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index 8b10112c30dc..95c655341a61 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -803,7 +803,8 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u
if (pn + 1 > rx_sa->next_pn_halves.lower) {
rx_sa->next_pn_halves.lower = pn + 1;
} else if (secy->xpn &&
- !pn_same_half(pn, rx_sa->next_pn_halves.lower)) {
+ (pn + 1 == 0 ||
+ !pn_same_half(pn, rx_sa->next_pn_halves.lower))) {
rx_sa->next_pn_halves.upper++;
rx_sa->next_pn_halves.lower = pn + 1;
}
diff --git a/drivers/net/phy/mscc/mscc.h b/drivers/net/phy/mscc/mscc.h
index 2bfe314ef881..105191c43a2c 100644
--- a/drivers/net/phy/mscc/mscc.h
+++ b/drivers/net/phy/mscc/mscc.h
@@ -286,12 +286,12 @@ enum rgmii_clock_delay {
#define PHY_ID_VSC8540 0x00070760
#define PHY_ID_VSC8541 0x00070770
#define PHY_ID_VSC8552 0x000704e0
-#define PHY_ID_VSC856X 0x000707e0
+#define PHY_ID_VSC856X 0x000707e1
#define PHY_ID_VSC8572 0x000704d0
#define PHY_ID_VSC8574 0x000704a0
-#define PHY_ID_VSC8575 0x000707d0
-#define PHY_ID_VSC8582 0x000707b0
-#define PHY_ID_VSC8584 0x000707c0
+#define PHY_ID_VSC8575 0x000707d1
+#define PHY_ID_VSC8582 0x000707b1
+#define PHY_ID_VSC8584 0x000707c1
#define PHY_VENDOR_MSCC 0x00070400

#define MSCC_VDDMAC_1500 1500
diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c
index a8e587dd96c5..7297dea16027 100644
--- a/drivers/net/phy/mscc/mscc_main.c
+++ b/drivers/net/phy/mscc/mscc_main.c
@@ -1724,12 +1724,6 @@ static int vsc8584_config_init(struct phy_device *phydev)
* in this pre-init function.
*/
if (phy_package_init_once(phydev)) {
- /* The following switch statement assumes that the lowest
- * nibble of the phy_id_mask is always 0. This works because
- * the lowest nibble of the PHY_ID's below are also 0.
- */
- WARN_ON(phydev->drv->phy_id_mask & 0xf);
-
switch (phydev->phy_id & phydev->drv->phy_id_mask) {
case PHY_ID_VSC8504:
case PHY_ID_VSC8552:
@@ -2268,11 +2262,6 @@ static int vsc8584_probe(struct phy_device *phydev)
VSC8531_DUPLEX_COLLISION};
int ret;

- if ((phydev->phy_id & MSCC_DEV_REV_MASK) != VSC8584_REVB) {
- dev_err(&phydev->mdio.dev, "Only VSC8584 revB is supported.\n");
- return -ENOTSUPP;
- }
-
vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL);
if (!vsc8531)
return -ENOMEM;
@@ -2559,9 +2548,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_stats = &vsc85xx_get_stats,
},
{
- .phy_id = PHY_ID_VSC856X,
+ PHY_ID_MATCH_EXACT(PHY_ID_VSC856X),
.name = "Microsemi GE VSC856X SyncE",
- .phy_id_mask = 0xfffffff0,
/* PHY_GBIT_FEATURES */
.soft_reset = &genphy_soft_reset,
.config_init = &vsc8584_config_init,
@@ -2633,9 +2621,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_stats = &vsc85xx_get_stats,
},
{
- .phy_id = PHY_ID_VSC8575,
+ PHY_ID_MATCH_EXACT(PHY_ID_VSC8575),
.name = "Microsemi GE VSC8575 SyncE",
- .phy_id_mask = 0xfffffff0,
/* PHY_GBIT_FEATURES */
.soft_reset = &genphy_soft_reset,
.config_init = &vsc8584_config_init,
@@ -2657,9 +2644,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_stats = &vsc85xx_get_stats,
},
{
- .phy_id = PHY_ID_VSC8582,
+ PHY_ID_MATCH_EXACT(PHY_ID_VSC8582),
.name = "Microsemi GE VSC8582 SyncE",
- .phy_id_mask = 0xfffffff0,
/* PHY_GBIT_FEATURES */
.soft_reset = &genphy_soft_reset,
.config_init = &vsc8584_config_init,
@@ -2681,9 +2667,8 @@ static struct phy_driver vsc85xx_driver[] = {
.get_stats = &vsc85xx_get_stats,
},
{
- .phy_id = PHY_ID_VSC8584,
+ PHY_ID_MATCH_EXACT(PHY_ID_VSC8584),
.name = "Microsemi GE VSC8584 SyncE",
- .phy_id_mask = 0xfffffff0,
/* PHY_GBIT_FEATURES */
.soft_reset = &genphy_soft_reset,
.config_init = &vsc8584_config_init,
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index fb9d425eff8c..d53e60823bf1 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -2459,8 +2459,10 @@ static int tun_xdp_one(struct tun_struct *tun,
bool skb_xdp = false;
struct page *page;

- if (unlikely(datasize < ETH_HLEN))
+ if (unlikely(datasize < ETH_HLEN)) {
+ put_page(virt_to_head_page(xdp->data));
return -EINVAL;
+ }

xdp_prog = rcu_dereference(tun->xdp_prog);
if (xdp_prog) {
@@ -2503,6 +2505,7 @@ static int tun_xdp_one(struct tun_struct *tun,
build:
skb = build_skb(xdp->data_hard_start, buflen);
if (!skb) {
+ put_page(virt_to_head_page(xdp->data));
ret = -ENOMEM;
goto out;
}
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index ed428293b0e5..765d25eee2fe 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -2541,7 +2541,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
goto out_unlock;
}

- tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
+ tos = ip_tunnel_ecn_encap(tos, ip_hdr(skb), skb);
ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
err = vxlan_build_skb(skb, ndst, sizeof(struct iphdr),
vni, md, flags, udp_sum);
@@ -2601,7 +2601,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
goto out_unlock;
}

- tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
+ tos = ip_tunnel_ecn_encap(tos, ip_hdr(skb), skb);
ttl = ttl ? : ip6_dst_hoplimit(ndst);
skb_scrub_packet(skb, xnet);
err = vxlan_build_skb(skb, ndst, sizeof(struct ipv6hdr),
diff --git a/drivers/net/wireguard/send.c b/drivers/net/wireguard/send.c
index 26e09c30d596..67d01478eb76 100644
--- a/drivers/net/wireguard/send.c
+++ b/drivers/net/wireguard/send.c
@@ -177,16 +177,6 @@ static bool encrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair)
trailer_len = padding_len + noise_encrypted_len(0);
plaintext_len = skb->len + padding_len;

- /* Expand data section to have room for padding and auth tag. */
- num_frags = skb_cow_data(skb, trailer_len, &trailer);
- if (unlikely(num_frags < 0 || num_frags > ARRAY_SIZE(sg)))
- return false;
-
- /* Set the padding to zeros, and make sure it and the auth tag are part
- * of the skb.
- */
- memset(skb_tail_pointer(trailer), 0, padding_len);
-
/* Expand head section to have room for our header and the network
* stack's headers.
*/
@@ -198,6 +188,16 @@ static bool encrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair)
skb_checksum_help(skb)))
return false;

+ /* Expand data section to have room for padding and auth tag. */
+ num_frags = skb_cow_data(skb, trailer_len, &trailer);
+ if (unlikely(num_frags < 0 || num_frags > ARRAY_SIZE(sg)))
+ return false;
+
+ /* Set the padding to zeros, and make sure it and the auth tag are part
+ * of the skb.
+ */
+ memset(skb_tail_pointer(trailer), 0, padding_len);
+
/* Only after checksumming can we safely add on the padding at the end
* and the header.
*/
diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c
index b3d34433bd14..a6c08175d9dd 100644
--- a/drivers/nfc/nxp-nci/i2c.c
+++ b/drivers/nfc/nxp-nci/i2c.c
@@ -16,6 +16,7 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/module.h>
#include <linux/nfc.h>
#include <linux/gpio/consumer.h>
@@ -267,6 +268,7 @@ static int nxp_nci_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct nxp_nci_i2c_phy *phy;
+ unsigned long irqflags;
int r;

if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
@@ -303,9 +305,26 @@ static int nxp_nci_i2c_probe(struct i2c_client *client)
if (r < 0)
return r;

+ /*
+ * ACPI platforms may report incorrect IRQ trigger types
+ * (e.g. level-high), which can lead to interrupt storms.
+ *
+ * Use the historically stable rising-edge trigger for ACPI devices.
+ *
+ * On non-ACPI systems (e.g. Device Tree), prefer the firmware-
+ * provided trigger type, falling back to rising-edge if not set.
+ */
+ if (ACPI_COMPANION(dev)) {
+ irqflags = IRQF_TRIGGER_RISING;
+ } else {
+ irqflags = irq_get_trigger_type(client->irq);
+ if (!irqflags)
+ irqflags = IRQF_TRIGGER_RISING;
+ }
+
r = request_threaded_irq(client->irq, NULL,
nxp_nci_i2c_irq_thread_fn,
- IRQF_ONESHOT,
+ irqflags | IRQF_ONESHOT,
NXP_NCI_I2C_DRIVER_NAME, phy);
if (r < 0)
nfc_err(&client->dev, "Unable to register IRQ handler\n");
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index 77df3432dfb7..31406438e3ff 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -1719,7 +1719,7 @@ static void nvme_tcp_tls_done(void *data, int status, key_serial_t pskid)
qid, pskid, status);

if (status) {
- queue->tls_err = -status;
+ queue->tls_err = status;
goto out_complete;
}

diff --git a/drivers/parport/share.c b/drivers/parport/share.c
index 427abdf3c4c4..80bdf55eeb9a 100644
--- a/drivers/parport/share.c
+++ b/drivers/parport/share.c
@@ -214,10 +214,14 @@ static void get_lowlevel_driver(void)
static int port_check(struct device *dev, void *dev_drv)
{
struct parport_driver *drv = dev_drv;
+ struct parport *port;

/* only send ports, do not send other devices connected to bus */
- if (is_parport(dev))
- drv->match_port(to_parport_dev(dev));
+ if (is_parport(dev)) {
+ port = to_parport_dev(dev);
+ if (test_bit(PARPORT_ANNOUNCED, &port->devflags))
+ drv->match_port(port);
+ }
return 0;
}

@@ -532,6 +536,7 @@ void parport_announce_port(struct parport *port)
if (slave)
attach_driver_chain(slave);
}
+ set_bit(PARPORT_ANNOUNCED, &port->devflags);
mutex_unlock(&registration_lock);
}
EXPORT_SYMBOL(parport_announce_port);
@@ -561,6 +566,8 @@ void parport_remove_port(struct parport *port)

mutex_lock(&registration_lock);

+ clear_bit(PARPORT_ANNOUNCED, &port->devflags);
+
/* Spread the word. */
detach_driver_chain(port);

diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c
index 55dd2286f3f3..06e9888df400 100644
--- a/drivers/platform/x86/intel/vsec.c
+++ b/drivers/platform/x86/intel/vsec.c
@@ -348,20 +348,10 @@ void intel_vsec_register(struct pci_dev *pdev,
}
EXPORT_SYMBOL_NS_GPL(intel_vsec_register, INTEL_VSEC);

-static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int intel_vsec_pci_init(struct pci_dev *pdev,
+ struct intel_vsec_platform_info *info)
{
- struct intel_vsec_platform_info *info;
bool have_devices = false;
- int ret;
-
- ret = pcim_enable_device(pdev);
- if (ret)
- return ret;
-
- pci_save_state(pdev);
- info = (struct intel_vsec_platform_info *)id->driver_data;
- if (!info)
- return -EINVAL;

if (intel_vsec_walk_dvsec(pdev, info))
have_devices = true;
@@ -379,6 +369,23 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id
return 0;
}

+static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct intel_vsec_platform_info *info;
+ int ret;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ pci_save_state(pdev);
+ info = (struct intel_vsec_platform_info *)id->driver_data;
+ if (!info)
+ return -EINVAL;
+
+ return intel_vsec_pci_init(pdev, info);
+}
+
/* DG1 info */
static struct intel_vsec_header dg1_header = {
.length = 0x10,
@@ -467,6 +474,7 @@ static pci_ers_result_t intel_vsec_pci_error_detected(struct pci_dev *pdev,
static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev)
{
struct intel_vsec_device *intel_vsec_dev;
+ struct intel_vsec_platform_info *info;
pci_ers_result_t status = PCI_ERS_RESULT_DISCONNECT;
const struct pci_device_id *pci_dev_id;
unsigned long index;
@@ -489,10 +497,10 @@ static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev)
devm_release_action(&pdev->dev, intel_vsec_remove_aux,
&intel_vsec_dev->auxdev);
}
- pci_disable_device(pdev);
pci_restore_state(pdev);
pci_dev_id = pci_match_id(intel_vsec_pci_ids, pdev);
- intel_vsec_pci_probe(pdev, pci_dev_id);
+ info = (struct intel_vsec_platform_info *)pci_dev_id->driver_data;
+ intel_vsec_pci_init(pdev, info);

out:
return status;
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index dcc1e1c34ca2..8fe6658dcfe1 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -1153,8 +1153,8 @@ int __init chsc_init(void)
{
int ret;

- sei_page = (void *)get_zeroed_page(GFP_KERNEL);
- chsc_page = (void *)get_zeroed_page(GFP_KERNEL);
+ sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sei_page || !chsc_page) {
ret = -ENOMEM;
goto out_err;
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index 1e58ee3cc87d..9131ce3af1b8 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -293,7 +293,7 @@ static int chsc_ioctl_start(void __user *user_area)
if (!css_general_characteristics.dynio)
/* It makes no sense to try. */
return -EOPNOTSUPP;
- chsc_area = (void *)get_zeroed_page(GFP_KERNEL);
+ chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
if (!chsc_area)
return -ENOMEM;
request = kzalloc(sizeof(*request), GFP_KERNEL);
@@ -341,7 +341,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area)
ret = -ENOMEM;
goto out_unlock;
}
- on_close_chsc_area = (void *)get_zeroed_page(GFP_KERNEL);
+ on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
if (!on_close_chsc_area) {
ret = -ENOMEM;
goto out_free_request;
@@ -393,7 +393,7 @@ static int chsc_ioctl_start_sync(void __user *user_area)
struct chsc_sync_area *chsc_area;
int ret, ccode;

- chsc_area = (void *)get_zeroed_page(GFP_KERNEL);
+ chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!chsc_area)
return -ENOMEM;
if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
@@ -439,7 +439,7 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *scpcd_area;

- scpcd_area = (void *)get_zeroed_page(GFP_KERNEL);
+ scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scpcd_area)
return -ENOMEM;
cd = kzalloc(sizeof(*cd), GFP_KERNEL);
@@ -501,7 +501,7 @@ static int chsc_ioctl_info_cu(void __user *user_cd)
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *scucd_area;

- scucd_area = (void *)get_zeroed_page(GFP_KERNEL);
+ scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scucd_area)
return -ENOMEM;
cd = kzalloc(sizeof(*cd), GFP_KERNEL);
@@ -564,7 +564,7 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud)
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *sscud_area;

- sscud_area = (void *)get_zeroed_page(GFP_KERNEL);
+ sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sscud_area)
return -ENOMEM;
cud = kzalloc(sizeof(*cud), GFP_KERNEL);
@@ -626,7 +626,7 @@ static int chsc_ioctl_conf_info(void __user *user_ci)
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *sci_area;

- sci_area = (void *)get_zeroed_page(GFP_KERNEL);
+ sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sci_area)
return -ENOMEM;
ci = kzalloc(sizeof(*ci), GFP_KERNEL);
@@ -697,7 +697,7 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
u32 res;
} __attribute__ ((packed)) *cssids_parm;

- sccl_area = (void *)get_zeroed_page(GFP_KERNEL);
+ sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sccl_area)
return -ENOMEM;
ccl = kzalloc(sizeof(*ccl), GFP_KERNEL);
@@ -757,7 +757,7 @@ static int chsc_ioctl_chpd(void __user *user_chpd)
int ret;

chpd = kzalloc(sizeof(*chpd), GFP_KERNEL);
- scpd_area = (void *)get_zeroed_page(GFP_KERNEL);
+ scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scpd_area || !chpd) {
ret = -ENOMEM;
goto out_free;
@@ -797,7 +797,7 @@ static int chsc_ioctl_dcal(void __user *user_dcal)
u8 data[PAGE_SIZE - 36];
} __attribute__ ((packed)) *sdcal_area;

- sdcal_area = (void *)get_zeroed_page(GFP_KERNEL);
+ sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sdcal_area)
return -ENOMEM;
dcal = kzalloc(sizeof(*dcal), GFP_KERNEL);
diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c
index c7894d61306d..375cbfa31b53 100644
--- a/drivers/s390/cio/scm.c
+++ b/drivers/s390/cio/scm.c
@@ -228,7 +228,7 @@ int scm_update_information(void)
size_t num;
int ret;

- scm_info = (void *)__get_free_page(GFP_KERNEL);
+ scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
if (!scm_info)
return -ENOMEM;

diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c
index 5c8d1ba3f8f3..8d6e0fdd1ce4 100644
--- a/drivers/scsi/fcoe/fcoe_ctlr.c
+++ b/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -1386,7 +1386,7 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,

while (rlen >= sizeof(*desc)) {
dlen = desc->fip_dlen * FIP_BPW;
- if (dlen > rlen)
+ if (dlen < sizeof(*desc) || dlen > rlen)
goto err;
/* Drop CVL if there are duplicate critical descriptors */
if ((desc->fip_dtype < 32) &&
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 55717fd3234b..d63d10d53a2a 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -569,10 +569,33 @@ void scsi_requeue_run_queue(struct work_struct *work)

void scsi_run_host_queues(struct Scsi_Host *shost)
{
- struct scsi_device *sdev;
+ struct scsi_device *sdev, *prev = NULL;
+ unsigned long flags;

- shost_for_each_device(sdev, shost)
+ spin_lock_irqsave(shost->host_lock, flags);
+ __shost_for_each_device(sdev, shost) {
+ /*
+ * Only skip devices so deep into removal they will never need
+ * another kick to their queues. Thus scsi_device_get() cannot
+ * be used as it would skip devices in SDEV_CANCEL state which
+ * may need a queue kick.
+ */
+ if (sdev->sdev_state == SDEV_DEL ||
+ !get_device(&sdev->sdev_gendev))
+ continue;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ if (prev)
+ put_device(&prev->sdev_gendev);
scsi_run_queue(sdev->request_queue);
+
+ prev = sdev;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ if (prev)
+ put_device(&prev->sdev_gendev);
}

static void scsi_uninit_cmd(struct scsi_cmnd *cmd)
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 082f76e76721..33632952ff95 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -742,6 +742,37 @@ fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats)
}
}

+static void
+fc_fpin_pname_stats_update(struct Scsi_Host *shost,
+ struct fc_rport *attach_rport, u16 event_type,
+ u32 desc_len, u32 fixed_len, u32 pname_count,
+ __be64 *pname_list,
+ void (*stats_update)(u16 event_type,
+ struct fc_fpin_stats *stats))
+{
+ u32 i;
+ struct fc_rport *rport;
+ u64 wwpn;
+
+ if (desc_len < fixed_len)
+ pname_count = 0;
+ else
+ pname_count = min(pname_count, (desc_len - fixed_len) /
+ sizeof(pname_list[0]));
+
+ for (i = 0; i < pname_count; i++) {
+ wwpn = be64_to_cpu(pname_list[i]);
+ rport = fc_find_rport_by_wwpn(shost, wwpn);
+ if (rport &&
+ (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
+ rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
+ if (rport == attach_rport)
+ continue;
+ stats_update(event_type, &rport->fpin_stats);
+ }
+ }
+}
+
/*
* fc_fpin_li_stats_update - routine to update Link Integrity
* event statistics.
@@ -752,13 +783,11 @@ fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats)
static void
fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv)
{
- u8 i;
struct fc_rport *rport = NULL;
struct fc_rport *attach_rport = NULL;
struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv;
u16 event_type = be16_to_cpu(li_desc->event_type);
- u64 wwpn;

rport = fc_find_rport_by_wwpn(shost,
be64_to_cpu(li_desc->attached_wwpn));
@@ -769,22 +798,11 @@ fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv)
fc_li_stats_update(event_type, &attach_rport->fpin_stats);
}

- if (be32_to_cpu(li_desc->pname_count) > 0) {
- for (i = 0;
- i < be32_to_cpu(li_desc->pname_count);
- i++) {
- wwpn = be64_to_cpu(li_desc->pname_list[i]);
- rport = fc_find_rport_by_wwpn(shost, wwpn);
- if (rport &&
- (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
- rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
- if (rport == attach_rport)
- continue;
- fc_li_stats_update(event_type,
- &rport->fpin_stats);
- }
- }
- }
+ fc_fpin_pname_stats_update(shost, attach_rport, event_type,
+ be32_to_cpu(li_desc->desc_len),
+ FC_TLV_DESC_LENGTH_FROM_SZ(*li_desc),
+ be32_to_cpu(li_desc->pname_count),
+ li_desc->pname_list, fc_li_stats_update);

if (fc_host->port_name == be64_to_cpu(li_desc->attached_wwpn))
fc_li_stats_update(event_type, &fc_host->fpin_stats);
@@ -832,13 +850,11 @@ static void
fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost,
struct fc_tlv_desc *tlv)
{
- u8 i;
struct fc_rport *rport = NULL;
struct fc_rport *attach_rport = NULL;
struct fc_fn_peer_congn_desc *pc_desc =
(struct fc_fn_peer_congn_desc *)tlv;
u16 event_type = be16_to_cpu(pc_desc->event_type);
- u64 wwpn;

rport = fc_find_rport_by_wwpn(shost,
be64_to_cpu(pc_desc->attached_wwpn));
@@ -849,22 +865,11 @@ fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost,
fc_cn_stats_update(event_type, &attach_rport->fpin_stats);
}

- if (be32_to_cpu(pc_desc->pname_count) > 0) {
- for (i = 0;
- i < be32_to_cpu(pc_desc->pname_count);
- i++) {
- wwpn = be64_to_cpu(pc_desc->pname_list[i]);
- rport = fc_find_rport_by_wwpn(shost, wwpn);
- if (rport &&
- (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
- rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
- if (rport == attach_rport)
- continue;
- fc_cn_stats_update(event_type,
- &rport->fpin_stats);
- }
- }
- }
+ fc_fpin_pname_stats_update(shost, attach_rport, event_type,
+ be32_to_cpu(pc_desc->desc_len),
+ FC_TLV_DESC_LENGTH_FROM_SZ(*pc_desc),
+ be32_to_cpu(pc_desc->pname_count),
+ pc_desc->pname_list, fc_cn_stats_update);
}

/*
diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c
index 63c77a3df591..afa78c96ede8 100644
--- a/drivers/staging/greybus/hid.c
+++ b/drivers/staging/greybus/hid.c
@@ -201,7 +201,7 @@ static void gb_hid_init_report(struct gb_hid *ghid, struct hid_report *report)
* we just need to setup the input fields, so using
* hid_report_raw_event is safe.
*/
- hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, size, 1);
+ hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, ghid->bufsize, size, 1);
}

static void gb_hid_init_reports(struct gb_hid *ghid)
diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c
index 68b3979ba5f2..fdae467fd7ab 100644
--- a/drivers/staging/media/av7110/av7110_ir.c
+++ b/drivers/staging/media/av7110/av7110_ir.c
@@ -151,6 +151,7 @@ int av7110_ir_init(struct av7110 *av7110)
void av7110_ir_exit(struct av7110 *av7110)
{
rc_unregister_device(av7110->ir.rcdev);
+ rc_free_device(av7110->ir.rcdev);
}

//MODULE_AUTHOR("Holger Waechtler <holger@xxxxxxxxxxxxxx>, Oliver Endriss <o.endriss@xxxxxx>");
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 68bbdf3ee101..8a7d308da991 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -2329,8 +2329,9 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,

if (conn->conn_ops->DataDigest) {
iscsit_do_crypto_hash_buf(conn->conn_rx_hash,
- text_in, rx_size, 0, NULL,
- &data_crc);
+ text_in,
+ ALIGN(payload_length, 4),
+ 0, NULL, &data_crc);

if (checksum != data_crc) {
pr_err("Text data CRC32C DataDigest"
@@ -2350,6 +2351,7 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
" Command CmdSN: 0x%08x due to"
" DataCRC error.\n", hdr->cmdsn);
kfree(text_in);
+ cmd->text_in_ptr = NULL;
return 0;
}
} else {
diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c
index c8a248bd11be..02a4c9aff98d 100644
--- a/drivers/target/iscsi/iscsi_target_auth.c
+++ b/drivers/target/iscsi/iscsi_target_auth.c
@@ -339,13 +339,22 @@ static int chap_server_compute_hash(
goto out;
}
break;
- case BASE64:
+ case BASE64: {
+ size_t r_len = strlen(chap_r);
+
+ while (r_len > 0 && chap_r[r_len - 1] == '=')
+ r_len--;
+ if (r_len > DIV_ROUND_UP(chap->digest_size * 4, 3)) {
+ pr_err("Malformed CHAP_R: base64 payload too long\n");
+ goto out;
+ }
if (chap_base64_decode(client_digest, chap_r, strlen(chap_r)) !=
chap->digest_size) {
pr_err("Malformed CHAP_R: invalid BASE64\n");
goto out;
}
break;
+ }
default:
pr_err("Could not find CHAP_R\n");
goto out;
@@ -472,6 +481,14 @@ static int chap_server_compute_hash(
}
break;
case BASE64:
+ /*
+ * No overflow check needed: initiatorchg_binhex is
+ * CHAP_CHALLENGE_STR_LEN bytes and extract_param() caps
+ * initiatorchg at CHAP_CHALLENGE_STR_LEN characters, so
+ * the decoded output is at most DIV_ROUND_UP(
+ * (CHAP_CHALLENGE_STR_LEN - 1) * 3, 4) bytes, which is
+ * less than CHAP_CHALLENGE_STR_LEN.
+ */
initiatorchg_len = chap_base64_decode(initiatorchg_binhex,
initiatorchg,
strlen(initiatorchg));
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index fa3fb5f4e6bc..f98734524693 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -899,10 +899,14 @@ static int iscsi_target_handle_csg_zero(
SENDER_TARGET,
login->rsp_buf,
&login->rsp_length,
+ MAX_KEY_VALUE_PAIRS,
conn->param_list,
conn->tpg->tpg_attrib.login_keys_workaround);
- if (ret < 0)
+ if (ret < 0) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_INIT_ERR);
return -1;
+ }

if (!iscsi_check_negotiated_keys(conn->param_list)) {
bool auth_required = iscsi_conn_auth_required(conn);
@@ -986,6 +990,7 @@ static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_lo
SENDER_TARGET,
login->rsp_buf,
&login->rsp_length,
+ MAX_KEY_VALUE_PAIRS,
conn->param_list,
conn->tpg->tpg_attrib.login_keys_workaround);
if (ret < 0) {
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c
index 5b90c22ee3dc..5e15c2ea7d65 100644
--- a/drivers/target/iscsi/iscsi_target_parameters.c
+++ b/drivers/target/iscsi/iscsi_target_parameters.c
@@ -1419,19 +1419,42 @@ int iscsi_decode_text_input(
return -1;
}

+/*
+ * Append "key=value" plus a trailing NUL into @textbuf at *@length.
+ * Returns 0 on success and advances *@length, or -EMSGSIZE if the
+ * record (including the NUL) would not fit in the remaining buffer.
+ */
+static int iscsi_encode_text_record(char *textbuf, u32 *length,
+ u32 textbuf_size,
+ const char *key, const char *value)
+{
+ int n;
+ u32 avail;
+
+ if (*length >= textbuf_size)
+ return -EMSGSIZE;
+
+ avail = textbuf_size - *length;
+ n = snprintf(textbuf + *length, avail, "%s=%s", key, value);
+ if (n < 0 || (u32)n + 1 > avail)
+ return -EMSGSIZE;
+
+ *length += n + 1;
+ return 0;
+}
+
int iscsi_encode_text_output(
u8 phase,
u8 sender,
char *textbuf,
u32 *length,
+ u32 textbuf_size,
struct iscsi_param_list *param_list,
bool keys_workaround)
{
- char *output_buf = NULL;
struct iscsi_extra_response *er;
struct iscsi_param *param;
-
- output_buf = textbuf + *length;
+ int ret;

if (iscsi_enforce_integrity_rules(phase, param_list) < 0)
return -1;
@@ -1443,10 +1466,12 @@ int iscsi_encode_text_output(
!IS_PSTATE_RESPONSE_SENT(param) &&
!IS_PSTATE_REPLY_OPTIONAL(param) &&
(param->phase & phase)) {
- *length += sprintf(output_buf, "%s=%s",
- param->name, param->value);
- *length += 1;
- output_buf = textbuf + *length;
+ ret = iscsi_encode_text_record(textbuf, length,
+ textbuf_size,
+ param->name,
+ param->value);
+ if (ret < 0)
+ goto err_overflow;
SET_PSTATE_RESPONSE_SENT(param);
pr_debug("Sending key: %s=%s\n",
param->name, param->value);
@@ -1456,10 +1481,12 @@ int iscsi_encode_text_output(
!IS_PSTATE_ACCEPTOR(param) &&
!IS_PSTATE_PROPOSER(param) &&
(param->phase & phase)) {
- *length += sprintf(output_buf, "%s=%s",
- param->name, param->value);
- *length += 1;
- output_buf = textbuf + *length;
+ ret = iscsi_encode_text_record(textbuf, length,
+ textbuf_size,
+ param->name,
+ param->value);
+ if (ret < 0)
+ goto err_overflow;
SET_PSTATE_PROPOSER(param);
iscsi_check_proposer_for_optional_reply(param,
keys_workaround);
@@ -1469,14 +1496,21 @@ int iscsi_encode_text_output(
}

list_for_each_entry(er, &param_list->extra_response_list, er_list) {
- *length += sprintf(output_buf, "%s=%s", er->key, er->value);
- *length += 1;
- output_buf = textbuf + *length;
+ ret = iscsi_encode_text_record(textbuf, length, textbuf_size,
+ er->key, er->value);
+ if (ret < 0)
+ goto err_overflow;
pr_debug("Sending key: %s=%s\n", er->key, er->value);
}
iscsi_release_extra_responses(param_list);

return 0;
+
+err_overflow:
+ pr_err("iSCSI login response buffer (%u bytes) exhausted, dropping login.\n",
+ textbuf_size);
+ iscsi_release_extra_responses(param_list);
+ return -1;
}

int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list)
diff --git a/drivers/target/iscsi/iscsi_target_parameters.h b/drivers/target/iscsi/iscsi_target_parameters.h
index 00fbbebb8c75..d6cbe5dd4b00 100644
--- a/drivers/target/iscsi/iscsi_target_parameters.h
+++ b/drivers/target/iscsi/iscsi_target_parameters.h
@@ -46,7 +46,7 @@ extern struct iscsi_param *iscsi_find_param_from_key(char *, struct iscsi_param_
extern int iscsi_extract_key_value(char *, char **, char **);
extern int iscsi_update_param_value(struct iscsi_param *, char *);
extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsit_conn *);
-extern int iscsi_encode_text_output(u8, u8, char *, u32 *,
+extern int iscsi_encode_text_output(u8, u8, char *, u32 *, u32,
struct iscsi_param_list *, bool);
extern int iscsi_check_negotiated_keys(struct iscsi_param_list *);
extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *,
diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c
index dc555cda98e6..e6c0330a9e50 100644
--- a/drivers/thunderbolt/property.c
+++ b/drivers/thunderbolt/property.c
@@ -8,6 +8,7 @@
*/

#include <linux/err.h>
+#include <linux/overflow.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/uuid.h>
@@ -34,10 +35,11 @@ struct tb_property_dir_entry {
};

#define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401
+#define TB_PROPERTY_MAX_DEPTH 8

static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
size_t block_len, unsigned int dir_offset, size_t dir_len,
- bool is_root);
+ bool is_root, unsigned int depth);

static inline void parse_dwdata(void *dst, const void *src, size_t dwords)
{
@@ -52,13 +54,16 @@ static inline void format_dwdata(void *dst, const void *src, size_t dwords)
static bool tb_property_entry_valid(const struct tb_property_entry *entry,
size_t block_len)
{
+ u32 end;
+
switch (entry->type) {
case TB_PROPERTY_TYPE_DIRECTORY:
case TB_PROPERTY_TYPE_DATA:
case TB_PROPERTY_TYPE_TEXT:
if (entry->length > block_len)
return false;
- if (entry->value + entry->length > block_len)
+ if (check_add_overflow(entry->value, entry->length, &end) ||
+ end > block_len)
return false;
break;

@@ -93,7 +98,8 @@ tb_property_alloc(const char *key, enum tb_property_type type)
}

static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
- const struct tb_property_entry *entry)
+ const struct tb_property_entry *entry,
+ unsigned int depth)
{
char key[TB_PROPERTY_KEY_SIZE + 1];
struct tb_property *property;
@@ -114,7 +120,7 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
switch (property->type) {
case TB_PROPERTY_TYPE_DIRECTORY:
dir = __tb_property_parse_dir(block, block_len, entry->value,
- entry->length, false);
+ entry->length, false, depth + 1);
if (!dir) {
kfree(property);
return NULL;
@@ -159,21 +165,31 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
}

static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
- size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root)
+ size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root,
+ unsigned int depth)
{
const struct tb_property_entry *entries;
size_t i, content_len, nentries;
unsigned int content_offset;
struct tb_property_dir *dir;

+ if (depth > TB_PROPERTY_MAX_DEPTH)
+ return NULL;
+
dir = kzalloc(sizeof(*dir), GFP_KERNEL);
if (!dir)
return NULL;

+ INIT_LIST_HEAD(&dir->properties);
+
if (is_root) {
content_offset = dir_offset + 2;
content_len = dir_len;
} else {
+ if (dir_len < 4) {
+ tb_property_free_dir(dir);
+ return NULL;
+ }
dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid),
GFP_KERNEL);
if (!dir->uuid) {
@@ -187,12 +203,10 @@ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
entries = (const struct tb_property_entry *)&block[content_offset];
nentries = content_len / (sizeof(*entries) / 4);

- INIT_LIST_HEAD(&dir->properties);
-
for (i = 0; i < nentries; i++) {
struct tb_property *property;

- property = tb_property_parse(block, block_len, &entries[i]);
+ property = tb_property_parse(block, block_len, &entries[i], depth);
if (!property) {
tb_property_free_dir(dir);
return NULL;
@@ -229,7 +243,7 @@ struct tb_property_dir *tb_property_parse_dir(const u32 *block,
return NULL;

return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
- true);
+ true, 0);
}

/**
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
index ebf0bbc2cff2..7bd3571b7d3e 100644
--- a/drivers/tty/serdev/core.c
+++ b/drivers/tty/serdev/core.c
@@ -431,11 +431,21 @@ static void serdev_drv_remove(struct device *dev)
dev_pm_domain_detach(dev, true);
}

+static void serdev_drv_shutdown(struct device *dev)
+{
+ const struct serdev_device_driver *sdrv =
+ to_serdev_device_driver(dev->driver);
+
+ if (dev->driver && sdrv->shutdown)
+ sdrv->shutdown(to_serdev_device(dev));
+}
+
static const struct bus_type serdev_bus_type = {
.name = "serial",
.match = serdev_device_match,
.probe = serdev_drv_probe,
.remove = serdev_drv_remove,
+ .shutdown = serdev_drv_shutdown,
};

/**
@@ -832,6 +842,14 @@ void serdev_controller_remove(struct serdev_controller *ctrl)
}
EXPORT_SYMBOL_GPL(serdev_controller_remove);

+static void serdev_legacy_shutdown(struct serdev_device *serdev)
+{
+ struct device *dev = &serdev->dev;
+ struct device_driver *driver = dev->driver;
+
+ driver->shutdown(dev);
+}
+
/**
* __serdev_device_driver_register() - Register client driver with serdev core
* @sdrv: client driver to be associated with client-device.
@@ -848,6 +866,9 @@ int __serdev_device_driver_register(struct serdev_device_driver *sdrv, struct mo
/* force drivers to async probe so I/O is possible in probe */
sdrv->driver.probe_type = PROBE_PREFER_ASYNCHRONOUS;

+ if (!sdrv->shutdown && sdrv->driver.shutdown)
+ sdrv->shutdown = serdev_legacy_shutdown;
+
return driver_register(&sdrv->driver);
}
EXPORT_SYMBOL_GPL(__serdev_device_driver_register);
diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c
index effcba71ea77..232dc4b64f9a 100644
--- a/drivers/tty/serial/altera_jtaguart.c
+++ b/drivers/tty/serial/altera_jtaguart.c
@@ -381,6 +381,7 @@ static int altera_jtaguart_probe(struct platform_device *pdev)
struct resource *res_mem;
int i = pdev->id;
int irq;
+ int ret;

/* -1 emphasizes that the platform must have one port, no .N suffix */
if (i == -1)
@@ -420,7 +421,11 @@ static int altera_jtaguart_probe(struct platform_device *pdev)
port->flags = UPF_BOOT_AUTOCONF;
port->dev = &pdev->dev;

- uart_add_one_port(&altera_jtaguart_driver, port);
+ ret = uart_add_one_port(&altera_jtaguart_driver, port);
+ if (ret) {
+ iounmap(port->membase);
+ return ret;
+ }

return 0;
}
diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c
index eba91daedef8..67b12d7a647d 100644
--- a/drivers/tty/serial/dz.c
+++ b/drivers/tty/serial/dz.c
@@ -40,6 +40,7 @@
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/sysrq.h>
@@ -48,14 +49,6 @@

#include <linux/atomic.h>
#include <linux/io.h>
-#include <asm/bootinfo.h>
-
-#include <asm/dec/interrupts.h>
-#include <asm/dec/kn01.h>
-#include <asm/dec/kn02.h>
-#include <asm/dec/machtype.h>
-#include <asm/dec/prom.h>
-#include <asm/dec/system.h>

#include "dz.h"

@@ -65,7 +58,9 @@ MODULE_LICENSE("GPL");


static char dz_name[] __initdata = "DECstation DZ serial driver version ";
-static char dz_version[] __initdata = "1.04";
+static char dz_version[] __initdata = "1.05";
+
+#define DZ_IO_SIZE 0x20 /* IOMEM space size. */

struct dz_port {
struct dz_mux *mux;
@@ -81,6 +76,7 @@ struct dz_mux {
};

static struct dz_mux dz_mux;
+static struct uart_driver dz_reg;

static inline struct dz_port *to_dport(struct uart_port *uport)
{
@@ -542,14 +538,47 @@ static int dz_encode_baud_rate(unsigned int baud)
static void dz_reset(struct dz_port *dport)
{
struct dz_mux *mux = dport->mux;
+ unsigned short tcr;
+ int loops = 10000;

if (mux->initialised)
return;

+ tcr = dz_in(dport, DZ_TCR);
+
+ /* Do not disturb any ongoing transmissions. */
+ if (dz_in(dport, DZ_CSR) & DZ_MSE) {
+ unsigned short csr, mask;
+
+ mask = tcr;
+ while ((mask & DZ_LNENB) && loops--) {
+ csr = dz_in(dport, DZ_CSR);
+ if (!(csr & DZ_TRDY))
+ continue;
+ mask &= ~(1 << ((csr & DZ_TLINE) >> 8));
+ dz_out(dport, DZ_TCR, mask);
+ iob();
+ udelay(2); /* 1.4us TRDY recovery. */
+ }
+ fsleep(1200); /* Transmitter drain. */
+ }
+
dz_out(dport, DZ_CSR, DZ_CLR);
while (dz_in(dport, DZ_CSR) & DZ_CLR);
iob();

+ /*
+ * Set parameters across all lines such as not to interfere
+ * with the initial PROM-based console. Otherwise any output
+ * produced before the console handover would cause the system
+ * firmware to produce rubbish.
+ */
+ for (int line = 0; line < DZ_NB_PORT; line++)
+ dz_out(dport, DZ_LPR, DZ_B9600 | DZ_CS8 | line);
+
+ /* Re-enable transmission for the initial PROM-based console. */
+ dz_out(dport, DZ_TCR, tcr);
+
/* Enable scanning. */
dz_out(dport, DZ_CSR, DZ_MSE);

@@ -633,26 +662,6 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios,
uart_port_unlock_irqrestore(&dport->port, flags);
}

-/*
- * Hack alert!
- * Required solely so that the initial PROM-based console
- * works undisturbed in parallel with this one.
- */
-static void dz_pm(struct uart_port *uport, unsigned int state,
- unsigned int oldstate)
-{
- struct dz_port *dport = to_dport(uport);
- unsigned long flags;
-
- uart_port_lock_irqsave(&dport->port, &flags);
- if (state < 3)
- dz_start_tx(&dport->port);
- else
- dz_stop_tx(&dport->port);
- uart_port_unlock_irqrestore(&dport->port, flags);
-}
-
-
static const char *dz_type(struct uart_port *uport)
{
return "DZ";
@@ -668,14 +677,13 @@ static void dz_release_port(struct uart_port *uport)

map_guard = atomic_add_return(-1, &mux->map_guard);
if (!map_guard)
- release_mem_region(uport->mapbase, dec_kn_slot_size);
+ release_mem_region(uport->mapbase, DZ_IO_SIZE);
}

static int dz_map_port(struct uart_port *uport)
{
if (!uport->membase)
- uport->membase = ioremap(uport->mapbase,
- dec_kn_slot_size);
+ uport->membase = ioremap(uport->mapbase, DZ_IO_SIZE);
if (!uport->membase) {
printk(KERN_ERR "dz: Cannot map MMIO\n");
return -ENOMEM;
@@ -691,8 +699,7 @@ static int dz_request_port(struct uart_port *uport)

map_guard = atomic_add_return(1, &mux->map_guard);
if (map_guard == 1) {
- if (!request_mem_region(uport->mapbase, dec_kn_slot_size,
- "dz")) {
+ if (!request_mem_region(uport->mapbase, DZ_IO_SIZE, "dz")) {
atomic_add(-1, &mux->map_guard);
printk(KERN_ERR
"dz: Unable to reserve MMIO resource\n");
@@ -703,7 +710,7 @@ static int dz_request_port(struct uart_port *uport)
if (ret) {
map_guard = atomic_add_return(-1, &mux->map_guard);
if (!map_guard)
- release_mem_region(uport->mapbase, dec_kn_slot_size);
+ release_mem_region(uport->mapbase, DZ_IO_SIZE);
return ret;
}
return 0;
@@ -748,7 +755,6 @@ static const struct uart_ops dz_ops = {
.startup = dz_startup,
.shutdown = dz_shutdown,
.set_termios = dz_set_termios,
- .pm = dz_pm,
.type = dz_type,
.release_port = dz_release_port,
.request_port = dz_request_port,
@@ -756,20 +762,15 @@ static const struct uart_ops dz_ops = {
.verify_port = dz_verify_port,
};

-static void __init dz_init_ports(void)
+static int __init dz_probe(struct platform_device *pdev)
{
- static int first = 1;
- unsigned long base;
+ struct resource *mem_resource, *irq_resource;
int line;

- if (!first)
- return;
- first = 0;
-
- if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100)
- base = dec_kn_slot_base + KN01_DZ11;
- else
- base = dec_kn_slot_base + KN02_DZ11;
+ mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!mem_resource || !irq_resource)
+ return -ENODEV;

for (line = 0; line < DZ_NB_PORT; line++) {
struct dz_port *dport = &dz_mux.dport[line];
@@ -777,14 +778,33 @@ static void __init dz_init_ports(void)

dport->mux = &dz_mux;

- uport->irq = dec_interrupt[DEC_IRQ_DZ11];
+ uport->dev = &pdev->dev;
+ uport->irq = irq_resource->start;
uport->fifosize = 1;
uport->iotype = UPIO_MEM;
uport->flags = UPF_BOOT_AUTOCONF;
uport->ops = &dz_ops;
uport->line = line;
- uport->mapbase = base;
+ uport->mapbase = mem_resource->start;
uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_DZ_CONSOLE);
+
+ if (uart_add_one_port(&dz_reg, uport))
+ uport->dev = NULL;
+ }
+
+ return 0;
+}
+
+static void __exit dz_remove(struct platform_device *pdev)
+{
+ int line;
+
+ for (line = DZ_NB_PORT - 1; line >= 0; line--) {
+ struct dz_port *dport = &dz_mux.dport[line];
+ struct uart_port *uport = &dport->port;
+
+ if (uport->dev)
+ uart_remove_one_port(&dz_reg, uport);
}
}

@@ -867,24 +887,14 @@ static int __init dz_console_setup(struct console *co, char *options)
int bits = 8;
int parity = 'n';
int flow = 'n';
- int ret;
-
- ret = dz_map_port(uport);
- if (ret)
- return ret;
-
- spin_lock_init(&dport->port.lock); /* For dz_pm(). */
-
- dz_reset(dport);
- dz_pm(uport, 0, -1);

+ if (!dport->mux)
+ return -ENODEV;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
-
- return uart_set_options(&dport->port, co, baud, parity, bits, flow);
+ return uart_set_options(uport, co, baud, parity, bits, flow);
}

-static struct uart_driver dz_reg;
static struct console dz_console = {
.name = "ttyS",
.write = dz_console_print,
@@ -895,18 +905,6 @@ static struct console dz_console = {
.data = &dz_reg,
};

-static int __init dz_serial_console_init(void)
-{
- if (!IOASIC) {
- dz_init_ports();
- register_console(&dz_console);
- return 0;
- } else
- return -ENXIO;
-}
-
-console_initcall(dz_serial_console_init);
-
#define SERIAL_DZ_CONSOLE &dz_console
#else
#define SERIAL_DZ_CONSOLE NULL
@@ -922,25 +920,32 @@ static struct uart_driver dz_reg = {
.cons = SERIAL_DZ_CONSOLE,
};

+static struct platform_driver dz_driver = {
+ .remove = __exit_p(dz_remove),
+ .driver = { .name = "dz" },
+};
+
static int __init dz_init(void)
{
- int ret, i;
-
- if (IOASIC)
- return -ENXIO;
+ int ret;

printk("%s%s\n", dz_name, dz_version);

- dz_init_ports();
-
ret = uart_register_driver(&dz_reg);
if (ret)
return ret;
+ ret = platform_driver_probe(&dz_driver, dz_probe);
+ if (ret)
+ uart_unregister_driver(&dz_reg);

- for (i = 0; i < DZ_NB_PORT; i++)
- uart_add_one_port(&dz_reg, &dz_mux.dport[i].port);
+ return ret;
+}

- return 0;
+static void __exit dz_exit(void)
+{
+ platform_driver_unregister(&dz_driver);
+ uart_unregister_driver(&dz_reg);
}

module_init(dz_init);
+module_exit(dz_exit);
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 951c3cdac3b9..f63191e4608b 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -1375,7 +1375,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)

if (!nent) {
dev_err(sport->port.dev, "DMA Rx mapping error\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_free_buf;
}

dma_rx_sconfig.src_addr = lpuart_dma_datareg_addr(sport);
@@ -1387,7 +1388,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
if (ret < 0) {
dev_err(sport->port.dev,
"DMA Rx slave config failed, err = %d\n", ret);
- return ret;
+ goto err_unmap_sg;
}

sport->dma_rx_desc = dmaengine_prep_dma_cyclic(chan,
@@ -1398,7 +1399,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
DMA_PREP_INTERRUPT);
if (!sport->dma_rx_desc) {
dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n");
- return -EFAULT;
+ ret = -ENOMEM;
+ goto err_unmap_sg;
}

sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
@@ -1422,6 +1424,13 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
}

return 0;
+
+err_unmap_sg:
+ dma_unmap_sg(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
+err_free_buf:
+ kfree(ring->buf);
+ ring->buf = NULL;
+ return ret;
}

static void lpuart_dma_rx_free(struct uart_port *port)
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 70676e3247ab..5d99f050aff0 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -689,8 +689,7 @@ static void pch_request_dma(struct uart_port *port)
if (!chan) {
dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Tx)\n",
__func__);
- pci_dev_put(dma_dev);
- return;
+ goto err_pci_get;
}
priv->chan_tx = chan;

@@ -704,18 +703,26 @@ static void pch_request_dma(struct uart_port *port)
if (!chan) {
dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Rx)\n",
__func__);
- dma_release_channel(priv->chan_tx);
- priv->chan_tx = NULL;
- pci_dev_put(dma_dev);
- return;
+ goto err_req_tx;
}

/* Get Consistent memory for DMA */
priv->rx_buf_virt = dma_alloc_coherent(port->dev, port->fifosize,
&priv->rx_buf_dma, GFP_KERNEL);
+ if (!priv->rx_buf_virt)
+ goto err_req_rx;
priv->chan_rx = chan;

pci_dev_put(dma_dev);
+ return;
+
+err_req_rx:
+ dma_release_channel(chan);
+err_req_tx:
+ dma_release_channel(priv->chan_tx);
+ priv->chan_tx = NULL;
+err_pci_get:
+ pci_dev_put(dma_dev);
}

static void pch_dma_rx_complete(void *arg)
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 5dfe4e599ad6..c09526e2d423 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -46,7 +46,7 @@
#define TX_STOP_BIT_LEN_2 2

/* SE_UART_RX_TRANS_CFG */
-#define UART_RX_PAR_EN BIT(3)
+#define UART_RX_PAR_EN BIT(4)

/* SE_UART_RX_WORD_LEN */
#define RX_WORD_LEN_MASK GENMASK(9, 0)
@@ -993,8 +993,20 @@ static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
struct tty_port *tport = &uport->state->port;
+ unsigned int fifo_len = kfifo_len(&tport->xmit_fifo);
+
+ /*
+ * Only advance the kfifo if it still contains the bytes that were
+ * transferred. uart_flush_buffer() may have run before this IRQ
+ * fired: it calls kfifo_reset() under the port lock, making
+ * fifo_len = 0 while tx_remaining remains non-zero. Calling
+ * uart_xmit_advance() in that case would underflow kfifo->out past
+ * kfifo->in, making kfifo_len() wrap to UART_XMIT_SIZE - tx_remaining
+ * and triggering a spurious large DMA transfer of stale data.
+ */
+ if (fifo_len >= port->tx_remaining)
+ uart_xmit_advance(uport, port->tx_remaining);

- uart_xmit_advance(uport, port->tx_remaining);
geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining);
port->tx_dma_addr = 0;
port->tx_remaining = 0;
diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c
index 0d184ee2f9ce..e7c0746ffc7b 100644
--- a/drivers/tty/serial/samsung_tty.c
+++ b/drivers/tty/serial/samsung_tty.c
@@ -243,12 +243,9 @@ static bool s3c24xx_serial_txempty_nofifo(const struct uart_port *port)
static void s3c24xx_serial_rx_enable(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
- unsigned long flags;
int count = 10000;
u32 ucon, ufcon;

- uart_port_lock_irqsave(port, &flags);
-
while (--count && !s3c24xx_serial_txempty_nofifo(port))
udelay(100);

@@ -261,23 +258,18 @@ static void s3c24xx_serial_rx_enable(struct uart_port *port)
wr_regl(port, S3C2410_UCON, ucon);

ourport->rx_enabled = 1;
- uart_port_unlock_irqrestore(port, flags);
}

static void s3c24xx_serial_rx_disable(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
- unsigned long flags;
u32 ucon;

- uart_port_lock_irqsave(port, &flags);
-
ucon = rd_regl(port, S3C2410_UCON);
ucon &= ~S3C2410_UCON_RXIRQMODE;
wr_regl(port, S3C2410_UCON, ucon);

ourport->rx_enabled = 0;
- uart_port_unlock_irqrestore(port, flags);
}

static void s3c24xx_serial_stop_tx(struct uart_port *port)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 22c958a0308f..1bbd59b3ab49 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -2849,7 +2849,7 @@ static int sci_request_port(struct uart_port *port)

ret = sci_remap_port(port);
if (unlikely(ret != 0)) {
- release_resource(res);
+ release_mem_region(port->mapbase, sport->reg_size);
return ret;
}

diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c
index 79ea7108a0f3..8cafb79912cf 100644
--- a/drivers/tty/serial/zs.c
+++ b/drivers/tty/serial/zs.c
@@ -56,6 +56,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/major.h>
+#include <linux/platform_device.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/spinlock.h>
@@ -66,10 +67,6 @@

#include <linux/atomic.h>

-#include <asm/dec/interrupts.h>
-#include <asm/dec/ioasic_addrs.h>
-#include <asm/dec/system.h>
-
#include "zs.h"


@@ -79,7 +76,7 @@ MODULE_LICENSE("GPL");


static char zs_name[] __initdata = "DECstation Z85C30 serial driver version ";
-static char zs_version[] __initdata = "0.10";
+static char zs_version[] __initdata = "0.11";

/*
* It would be nice to dynamically allocate everything that
@@ -98,25 +95,27 @@ static char zs_version[] __initdata = "0.10";

#define to_zport(uport) container_of(uport, struct zs_port, port)

-struct zs_parms {
- resource_size_t scc[ZS_NUM_SCCS];
- int irq[ZS_NUM_SCCS];
-};
-
static struct zs_scc zs_sccs[ZS_NUM_SCCS];
+static struct uart_driver zs_reg;

+/*
+ * Set parameters in WR5, WR12, WR13 such as not to interfere
+ * with the initial PROM-based console. Otherwise any output
+ * produced before the console handover would cause the system
+ * firmware to hang (TxENAB) or produce rubbish (Tx8, B9600).
+ */
static u8 zs_init_regs[ZS_NUM_REGS] __initdata = {
0, /* write 0 */
PAR_SPEC, /* write 1 */
0, /* write 2 */
0, /* write 3 */
X16CLK | SB1, /* write 4 */
- 0, /* write 5 */
+ Tx8 | TxENAB, /* write 5 */
0, 0, 0, /* write 6, 7, 8 */
MIE | DLC | NV, /* write 9 */
NRZ, /* write 10 */
TCBR | RCBR, /* write 11 */
- 0, 0, /* BRG time constant, write 12 + 13 */
+ 0x16, 0x00, /* BRG time constant, write 12 + 13 */
BRSRC | BRENABL, /* write 14 */
0, /* write 15 */
};
@@ -680,9 +679,9 @@ static void zs_status_handle(struct zs_port *zport, struct zs_port *zport_a)
uart_handle_dcd_change(uport,
zport->mctrl & TIOCM_CAR);
if (delta & TIOCM_RNG)
- uport->icount.dsr++;
- if (delta & TIOCM_DSR)
uport->icount.rng++;
+ if (delta & TIOCM_DSR)
+ uport->icount.dsr++;

if (delta)
wake_up_interruptible(&uport->state->port.delta_msr_wait);
@@ -826,22 +825,22 @@ static void zs_shutdown(struct uart_port *uport)

static void zs_reset(struct zs_port *zport)
{
+ struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A];
struct zs_scc *scc = zport->scc;
int irq;
unsigned long flags;

spin_lock_irqsave(&scc->zlock, flags);
irq = !irqs_disabled_flags(flags);
- if (!scc->initialised) {
- /* Reset the pointer first, just in case... */
- read_zsreg(zport, R0);
- /* And let the current transmission finish. */
- zs_line_drain(zport, irq);
- write_zsreg(zport, R9, FHWRES);
- udelay(10);
- write_zsreg(zport, R9, 0);
- scc->initialised = 1;
- }
+
+ /* Reset the pointer first, just in case... */
+ read_zsreg(zport, R0);
+ /* And let the current transmission finish. */
+ zs_line_drain(zport, irq);
+ write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB);
+ udelay(10);
+ write_zsreg(zport, R9, 0);
+
load_zsregs(zport, zport->regs, irq);
spin_unlock_irqrestore(&scc->zlock, flags);
}
@@ -956,23 +955,6 @@ static void zs_set_termios(struct uart_port *uport, struct ktermios *termios,
spin_unlock_irqrestore(&scc->zlock, flags);
}

-/*
- * Hack alert!
- * Required solely so that the initial PROM-based console
- * works undisturbed in parallel with this one.
- */
-static void zs_pm(struct uart_port *uport, unsigned int state,
- unsigned int oldstate)
-{
- struct zs_port *zport = to_zport(uport);
-
- if (state < 3)
- zport->regs[5] |= TxENAB;
- else
- zport->regs[5] &= ~TxENAB;
- write_zsreg(zport, R5, zport->regs[5]);
-}
-

static const char *zs_type(struct uart_port *uport)
{
@@ -1055,7 +1037,6 @@ static const struct uart_ops zs_ops = {
.startup = zs_startup,
.shutdown = zs_shutdown,
.set_termios = zs_set_termios,
- .pm = zs_pm,
.type = zs_type,
.release_port = zs_release_port,
.request_port = zs_request_port,
@@ -1066,63 +1047,62 @@ static const struct uart_ops zs_ops = {
/*
* Initialize Z85C30 port structures.
*/
-static int __init zs_probe_sccs(void)
+static int __init zs_probe(struct platform_device *pdev)
{
- static int probed;
- struct zs_parms zs_parms;
- int chip, side, irq;
- int n_chips = 0;
+ struct resource *mem_resource, *irq_resource;
+ int chip, side;
int i;

- if (probed)
- return 0;
+ mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!mem_resource || !irq_resource)
+ return -ENODEV;

- irq = dec_interrupt[DEC_IRQ_SCC0];
- if (irq >= 0) {
- zs_parms.scc[n_chips] = IOASIC_SCC0;
- zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC0];
- n_chips++;
- }
- irq = dec_interrupt[DEC_IRQ_SCC1];
- if (irq >= 0) {
- zs_parms.scc[n_chips] = IOASIC_SCC1;
- zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC1];
- n_chips++;
- }
- if (!n_chips)
- return -ENXIO;
-
- probed = 1;
-
- for (chip = 0; chip < n_chips; chip++) {
- spin_lock_init(&zs_sccs[chip].zlock);
- for (side = 0; side < ZS_NUM_CHAN; side++) {
- struct zs_port *zport = &zs_sccs[chip].zport[side];
- struct uart_port *uport = &zport->port;
-
- zport->scc = &zs_sccs[chip];
- zport->clk_mode = 16;
-
- uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE);
- uport->irq = zs_parms.irq[chip];
- uport->uartclk = ZS_CLOCK;
- uport->fifosize = 1;
- uport->iotype = UPIO_MEM;
- uport->flags = UPF_BOOT_AUTOCONF;
- uport->ops = &zs_ops;
- uport->line = chip * ZS_NUM_CHAN + side;
- uport->mapbase = dec_kn_slot_base +
- zs_parms.scc[chip] +
- (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE;
-
- for (i = 0; i < ZS_NUM_REGS; i++)
- zport->regs[i] = zs_init_regs[i];
- }
+ chip = pdev->id;
+ spin_lock_init(&zs_sccs[chip].zlock);
+ for (side = 0; side < ZS_NUM_CHAN; side++) {
+ struct zs_port *zport = &zs_sccs[chip].zport[side];
+ struct uart_port *uport = &zport->port;
+
+ zport->scc = &zs_sccs[chip];
+ zport->clk_mode = 16;
+
+ uport->dev = &pdev->dev;
+ uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE);
+ uport->irq = irq_resource->start;
+ uport->uartclk = ZS_CLOCK;
+ uport->fifosize = 1;
+ uport->iotype = UPIO_MEM;
+ uport->flags = UPF_BOOT_AUTOCONF;
+ uport->ops = &zs_ops;
+ uport->line = chip * ZS_NUM_CHAN + side;
+ uport->mapbase = mem_resource->start +
+ (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE;
+
+ for (i = 0; i < ZS_NUM_REGS; i++)
+ zport->regs[i] = zs_init_regs[i];
+
+ if (uart_add_one_port(&zs_reg, uport))
+ uport->dev = NULL;
}

return 0;
}

+static void __exit zs_remove(struct platform_device *pdev)
+{
+ int chip, side;
+
+ chip = pdev->id;
+ for (side = ZS_NUM_CHAN - 1; side >= 0; side--) {
+ struct zs_port *zport = &zs_sccs[chip].zport[side];
+ struct uart_port *uport = &zport->port;
+
+ if (uport->dev)
+ uart_remove_one_port(&zs_reg, uport);
+ }
+}
+

#ifdef CONFIG_SERIAL_ZS_CONSOLE
static void zs_console_putchar(struct uart_port *uport, unsigned char ch)
@@ -1203,21 +1183,14 @@ static int __init zs_console_setup(struct console *co, char *options)
int bits = 8;
int parity = 'n';
int flow = 'n';
- int ret;
-
- ret = zs_map_port(uport);
- if (ret)
- return ret;
-
- zs_reset(zport);
- zs_pm(uport, 0, -1);

+ if (!zport->scc)
+ return -ENODEV;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
return uart_set_options(uport, co, baud, parity, bits, flow);
}

-static struct uart_driver zs_reg;
static struct console zs_console = {
.name = "ttyS",
.write = zs_console_write,
@@ -1228,23 +1201,6 @@ static struct console zs_console = {
.data = &zs_reg,
};

-/*
- * Register console.
- */
-static int __init zs_serial_console_init(void)
-{
- int ret;
-
- ret = zs_probe_sccs();
- if (ret)
- return ret;
- register_console(&zs_console);
-
- return 0;
-}
-
-console_initcall(zs_serial_console_init);
-
#define SERIAL_ZS_CONSOLE &zs_console
#else
#define SERIAL_ZS_CONSOLE NULL
@@ -1260,47 +1216,31 @@ static struct uart_driver zs_reg = {
.cons = SERIAL_ZS_CONSOLE,
};

+static struct platform_driver zs_driver = {
+ .remove = __exit_p(zs_remove),
+ .driver = { .name = "zs" },
+};
+
/* zs_init inits the driver. */
static int __init zs_init(void)
{
- int i, ret;
+ int ret;

pr_info("%s%s\n", zs_name, zs_version);

- /* Find out how many Z85C30 SCCs we have. */
- ret = zs_probe_sccs();
- if (ret)
- return ret;
-
ret = uart_register_driver(&zs_reg);
if (ret)
return ret;
+ ret = platform_driver_probe(&zs_driver, zs_probe);
+ if (ret)
+ uart_unregister_driver(&zs_reg);

- for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) {
- struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN];
- struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN];
- struct uart_port *uport = &zport->port;
-
- if (zport->scc)
- uart_add_one_port(&zs_reg, uport);
- }
-
- return 0;
+ return ret;
}

static void __exit zs_exit(void)
{
- int i;
-
- for (i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1; i >= 0; i--) {
- struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN];
- struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN];
- struct uart_port *uport = &zport->port;
-
- if (zport->scc)
- uart_remove_one_port(&zs_reg, uport);
- }
-
+ platform_driver_unregister(&zs_driver);
uart_unregister_driver(&zs_reg);
}

diff --git a/drivers/tty/serial/zs.h b/drivers/tty/serial/zs.h
index 26ef8eafa1c1..e0d3c189b33f 100644
--- a/drivers/tty/serial/zs.h
+++ b/drivers/tty/serial/zs.h
@@ -41,7 +41,6 @@ struct zs_scc {
struct zs_port zport[2];
spinlock_t zlock;
atomic_t irq_guard;
- int initialised;
};

#endif /* __KERNEL__ */
diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c
index fdab2a5d62ba..ac0aeb135d6f 100644
--- a/drivers/usb/cdns3/cdns3-gadget.c
+++ b/drivers/usb/cdns3/cdns3-gadget.c
@@ -2817,9 +2817,19 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep)
priv_ep->flags &= ~(EP_STALLED | EP_STALL_PENDING);

if (request) {
- if (trb)
+ if (trb) {
*trb = trb_tmp;

+ /*
+ * Per datasheet, EPRST causes DMA to reposition to the next TD.
+ * Manually reset EP_TRADDR to the current TRB to prevent
+ * the hardware from skipping the interrupted request.
+ */
+ writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma +
+ priv_req->start_trb * TRB_SIZE),
+ &priv_dev->regs->ep_traddr);
+ }
+
cdns3_rearm_transfer(priv_ep, 1);
}

diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c
index 3ef8e3c872a3..6278e88ba5da 100644
--- a/drivers/usb/cdns3/cdns3-plat.c
+++ b/drivers/usb/cdns3/cdns3-plat.c
@@ -126,15 +126,15 @@ static int cdns3_plat_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy),
"Failed to get cdn3,usb2-phy\n");

- ret = phy_init(cdns->usb2_phy);
- if (ret)
- return ret;
-
cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy");
if (IS_ERR(cdns->usb3_phy))
return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy),
"Failed to get cdn3,usb3-phy\n");

+ ret = phy_init(cdns->usb2_phy);
+ if (ret)
+ return ret;
+
ret = phy_init(cdns->usb3_phy);
if (ret)
goto err_phy3_init;
@@ -188,6 +188,9 @@ static void cdns3_plat_remove(struct platform_device *pdev)
struct device *dev = cdns->dev;

pm_runtime_get_sync(dev);
+ if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)))
+ pm_runtime_allow(dev);
+
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
cdns_remove(cdns);
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index c60390a1d591..ac7c58b69e9d 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -669,12 +669,6 @@ static enum ci_role ci_get_role(struct ci_hdrc *ci)
return role;
}

-static struct usb_role_switch_desc ci_role_switch = {
- .set = ci_usb_role_switch_set,
- .get = ci_usb_role_switch_get,
- .allow_userspace_control = true,
-};
-
static int ci_get_platdata(struct device *dev,
struct ci_hdrc_platform_data *platdata)
{
@@ -801,9 +795,6 @@ static int ci_get_platdata(struct device *dev,
cable->connected = false;
}

- if (device_property_read_bool(dev, "usb-role-switch"))
- ci_role_switch.fwnode = dev->fwnode;
-
platdata->pctl = devm_pinctrl_get(dev);
if (!IS_ERR(platdata->pctl)) {
struct pinctrl_state *p;
@@ -1045,6 +1036,7 @@ ATTRIBUTE_GROUPS(ci);

static int ci_hdrc_probe(struct platform_device *pdev)
{
+ struct usb_role_switch_desc ci_role_switch = {};
struct device *dev = &pdev->dev;
struct ci_hdrc *ci;
struct resource *res;
@@ -1191,7 +1183,11 @@ static int ci_hdrc_probe(struct platform_device *pdev)
}
}

- if (ci_role_switch.fwnode) {
+ if (device_property_read_bool(dev, "usb-role-switch")) {
+ ci_role_switch.set = ci_usb_role_switch_set;
+ ci_role_switch.get = ci_usb_role_switch_get;
+ ci_role_switch.allow_userspace_control = true;
+ ci_role_switch.fwnode = dev_fwnode(dev);
ci_role_switch.driver_data = ci;
ci->role_switch = usb_role_switch_register(dev,
&ci_role_switch);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index d05b8806124a..730ea34cb744 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -114,8 +114,6 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value,
int retval;

retval = usb_autopm_get_interface(acm->control);
-#define VENDOR_CLASS_DATA_IFACE BIT(9) /* data interface uses vendor-specific class */
-#define ALWAYS_POLL_CTRL BIT(10) /* keep ctrl URB active even without an open TTY */
if (retval)
return retval;

diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 25fd5329a878..01f448a783c0 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -115,3 +115,5 @@ struct acm {
#define DISABLE_ECHO BIT(7)
#define MISSING_CAP_BRK BIT(8)
#define NO_UNION_12 BIT(9)
+#define VENDOR_CLASS_DATA_IFACE BIT(10) /* data interface uses vendor-specific class */
+#define ALWAYS_POLL_CTRL BIT(11) /* keep ctrl URB active even without an open TTY */
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 49459e3d34d6..fe88d555142a 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -2310,6 +2310,14 @@ static void usbtmc_interrupt(struct urb *urb)

switch (status) {
case 0: /* SUCCESS */
+ /* ensure at least two bytes of headers were transferred */
+ if (urb->actual_length < 2) {
+ dev_warn(dev,
+ "actual length %d not sufficient for interrupt headers\n",
+ urb->actual_length);
+ goto exit;
+ }
+
/* check for valid STB notification */
if (data->iin_buffer[0] > 0x81) {
data->bNotify1 = data->iin_buffer[0];
@@ -2436,6 +2444,12 @@ static int usbtmc_probe(struct usb_interface *intf,
data->iin_ep = int_in->bEndpointAddress;
data->iin_wMaxPacketSize = usb_endpoint_maxp(int_in);
data->iin_interval = int_in->bInterval;
+ /* wMaxPacketSize should be 0x02 or more as per USB488 Table 22 */
+ if (iface_desc->desc.bInterfaceProtocol == 1 &&
+ data->iin_wMaxPacketSize < 2) {
+ retcode = -EINVAL;
+ goto err_put;
+ }
dev_dbg(&intf->dev, "Found Int in endpoint at %u\n",
data->iin_ep);
}
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 0baecdd342c7..43f1419d6617 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -165,7 +165,14 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
(desc->bMaxBurst + 1);
else
max_tx = 999999;
- if (le16_to_cpu(desc->wBytesPerInterval) > max_tx) {
+ /*
+ * wBytesPerInterval > max_tx is bogus, but USB3 spec doesn't forbid the opposite.
+ * Experience shows that wBytesPerInterval < wMaxPacketSize on common interrupt IN
+ * endpoints is usually bogus too, and recent HCs enforce interrupt BW limits.
+ */
+ if (le16_to_cpu(desc->wBytesPerInterval) > max_tx ||
+ (le16_to_cpu(desc->wBytesPerInterval) < usb_endpoint_maxp(&ep->desc) &&
+ usb_endpoint_is_int_in(&ep->desc))) {
dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in "
"config %d interface %d altsetting %d ep %d: "
"setting to %d\n",
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index bc795257696e..01d0362b9abf 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -332,9 +332,7 @@ static const u8 ss_rh_config_descriptor[] = {
USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
- /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
- * see hub.c:hub_configure() for details. */
- (USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
+ 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 2 bytes per USB3 10.15.1 */
0x0c, /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */

/* one SuperSpeed endpoint companion descriptor */
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 7442ac03f5ff..a273cdcef0c5 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -511,6 +511,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Lenovo ThinkPad USB-C Dock Gen2 Ethernet (RTL8153 GigE) */
{ USB_DEVICE(0x17ef, 0xa387), .driver_info = USB_QUIRK_NO_LPM },

+ /* Lenovo ThinkPad USB-C Dock Gen2 USB 3.1 and USB 2.0 hub controllers */
+ { USB_DEVICE(0x17ef, 0xa391), .driver_info = USB_QUIRK_NO_LPM },
+ { USB_DEVICE(0x17ef, 0xa392), .driver_info = USB_QUIRK_NO_LPM },
+
/* BUILDWIN Photo Frame */
{ USB_DEVICE(0x1908, 0x1315), .driver_info =
USB_QUIRK_HONOR_BNUMINTERFACES },
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 8c3941ecaaf5..8e295f41fc13 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -4804,6 +4804,7 @@ static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
int rc;
unsigned long flags;
+ int urb_status;

dev_dbg(hsotg->dev, "DWC OTG HCD URB Dequeue\n");
dwc2_dump_urb_info(hcd, urb, "urb_dequeue");
@@ -4828,11 +4829,12 @@ static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,

/* Higher layer software sets URB status */
spin_unlock(&hsotg->lock);
+ urb_status = urb->status;
usb_hcd_giveback_urb(hcd, urb, status);
spin_lock(&hsotg->lock);

dev_dbg(hsotg->dev, "Called usb_hcd_giveback_urb()\n");
- dev_dbg(hsotg->dev, " urb->status = %d\n", urb->status);
+ dev_dbg(hsotg->dev, " urb->status = %d\n", urb_status);
out:
spin_unlock_irqrestore(&hsotg->lock, flags);

diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
index 47e891c92337..939783e4a98b 100644
--- a/drivers/usb/dwc3/dwc3-xilinx.c
+++ b/drivers/usb/dwc3/dwc3-xilinx.c
@@ -170,15 +170,13 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
}

ret = phy_init(priv_data->usb3_phy);
- if (ret < 0) {
- phy_exit(priv_data->usb3_phy);
+ if (ret < 0)
goto err;
- }

ret = reset_control_deassert(apbrst);
if (ret < 0) {
dev_err(dev, "Failed to release APB reset\n");
- goto err;
+ goto err_phy_exit;
}

/* Set PIPE Power Present signal in FPD Power Present Register*/
@@ -190,27 +188,25 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
ret = reset_control_deassert(crst);
if (ret < 0) {
dev_err(dev, "Failed to release core reset\n");
- goto err;
+ goto err_phy_exit;
}

ret = reset_control_deassert(hibrst);
if (ret < 0) {
dev_err(dev, "Failed to release hibernation reset\n");
- goto err;
+ goto err_phy_exit;
}

ret = phy_power_on(priv_data->usb3_phy);
- if (ret < 0) {
- phy_exit(priv_data->usb3_phy);
- goto err;
- }
+ if (ret < 0)
+ goto err_phy_exit;

skip_usb3_phy:
/* ulpi reset via gpio-modepin or gpio-framework driver */
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset_gpio)) {
- return dev_err_probe(dev, PTR_ERR(reset_gpio),
- "Failed to request reset GPIO\n");
+ ret = PTR_ERR(reset_gpio);
+ goto err_phy_power_off;
}

if (reset_gpio) {
@@ -230,6 +226,12 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
writel(reg, priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG);
}

+ return 0;
+
+err_phy_power_off:
+ phy_power_off(priv_data->usb3_phy);
+err_phy_exit:
+ phy_exit(priv_data->usb3_phy);
err:
return ret;
}
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 460a102c1419..fbf270505cbf 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -2196,7 +2196,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
sizeof(url_descriptor->URL)
- WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset);

- if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length)
+ if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH)
+ landing_page_length = landing_page_offset;
+ else if (w_length <
+ WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length)
landing_page_length = w_length
- WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset;

diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index e03ac64361cc..1b9f3b2953cc 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -151,6 +151,8 @@ struct ffs_dma_fence {
struct dma_fence base;
struct ffs_dmabuf_priv *priv;
struct work_struct work;
+ struct usb_ep *ep;
+ struct usb_request *req;
};

struct ffs_epfile {
@@ -622,7 +624,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,

/* unlocks spinlock */
ret = __ffs_ep0_queue_wait(ffs, data, len);
- if ((ret > 0) && (copy_to_user(buf, data, len)))
+ if ((ret > 0) && (copy_to_user(buf, data, ret)))
ret = -EFAULT;
goto done_mutex;

@@ -1375,6 +1377,21 @@ static void ffs_dmabuf_cleanup(struct work_struct *work)
struct ffs_dmabuf_priv *priv = dma_fence->priv;
struct dma_buf_attachment *attach = priv->attach;
struct dma_fence *fence = &dma_fence->base;
+ struct usb_request *req = dma_fence->req;
+ struct usb_ep *ep = dma_fence->ep;
+
+ /*
+ * eps_lock pairs with the cancel paths so they cannot pass a freed
+ * req to usb_ep_dequeue(). Only clear if priv->req still names ours;
+ * a re-queue on the same attachment may have taken that slot.
+ */
+ spin_lock_irq(&priv->ffs->eps_lock);
+ if (priv->req == req)
+ priv->req = NULL;
+ spin_unlock_irq(&priv->ffs->eps_lock);
+
+ if (ep && req)
+ usb_ep_free_request(ep, req);

ffs_dmabuf_put(attach);
dma_fence_put(fence);
@@ -1404,8 +1421,8 @@ static void ffs_epfile_dmabuf_io_complete(struct usb_ep *ep,
struct usb_request *req)
{
pr_vdebug("FFS: DMABUF transfer complete, status=%d\n", req->status);
+ /* req is freed by ffs_dmabuf_cleanup() under eps_lock. */
ffs_dmabuf_signal_done(req->context, req->status);
- usb_ep_free_request(ep, req);
}

static const char *ffs_dmabuf_get_driver_name(struct dma_fence *fence)
@@ -1689,6 +1706,10 @@ static int ffs_dmabuf_transfer(struct file *file,
usb_req->context = fence;
usb_req->complete = ffs_epfile_dmabuf_io_complete;

+ /* ffs_dmabuf_cleanup() frees usb_req via these two fields. */
+ fence->req = usb_req;
+ fence->ep = ep->ep;
+
cookie = dma_fence_begin_signalling();
ret = usb_ep_queue(ep->ep, usb_req, GFP_ATOMIC);
dma_fence_end_signalling(cookie);
@@ -1698,7 +1719,6 @@ static int ffs_dmabuf_transfer(struct file *file,
} else {
pr_warn("FFS: Failed to queue DMABUF: %d\n", ret);
ffs_dmabuf_signal_done(fence, ret);
- usb_ep_free_request(ep->ep, usb_req);
}

spin_unlock_irq(&epfile->ffs->eps_lock);
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index ba6267196a68..2f4a5d22aca4 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -1562,7 +1562,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
hidg->dev.devt = MKDEV(major, opts->minor);
ret = dev_set_name(&hidg->dev, "hidg%d", opts->minor);
if (ret)
- goto err_unlock;
+ goto err_put_device;

hidg->bInterfaceSubClass = opts->subclass;
hidg->bInterfaceProtocol = opts->protocol;
@@ -1597,7 +1597,6 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)

err_put_device:
put_device(&hidg->dev);
-err_unlock:
mutex_unlock(&opts->lock);
return ERR_PTR(ret);
}
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 6007bd6cf86f..2aa5bc09df87 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -764,6 +764,16 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;

+ /*
+ * Hold opts->lock across both the XU string-descriptor fixup below and
+ * the descriptor-copy block further down. Without this, configfs
+ * uvcg_extension_drop() (which takes opts->lock) can race with the
+ * list_for_each_entry() walks here and inside uvc_copy_descriptors(),
+ * leading to a UAF on a freed struct uvcg_extension. See
+ * drivers/usb/gadget/function/uvc_configfs.c::uvcg_extension_drop().
+ */
+ mutex_lock(&opts->lock);
+
/*
* XUs can have an arbitrary string descriptor describing them. If they
* have one pick up the ID.
@@ -781,7 +791,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
ARRAY_SIZE(uvc_en_us_strings));
if (IS_ERR(us)) {
ret = PTR_ERR(us);
- goto error;
+ goto error_unlock;
}

uvc_iad.iFunction = opts->iad_index ? cdev->usb_strings[opts->iad_index].id :
@@ -795,14 +805,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)

/* Allocate interface IDs. */
if ((ret = usb_interface_id(c, f)) < 0)
- goto error;
+ goto error_unlock;
uvc_iad.bFirstInterface = ret;
uvc_control_intf.bInterfaceNumber = ret;
uvc->control_intf = ret;
opts->control_interface = ret;

if ((ret = usb_interface_id(c, f)) < 0)
- goto error;
+ goto error_unlock;
uvc_streaming_intf_alt0.bInterfaceNumber = ret;
uvc_streaming_intf_alt1.bInterfaceNumber = ret;
uvc->streaming_intf = ret;
@@ -813,30 +823,32 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
if (IS_ERR(f->fs_descriptors)) {
ret = PTR_ERR(f->fs_descriptors);
f->fs_descriptors = NULL;
- goto error;
+ goto error_unlock;
}

f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
if (IS_ERR(f->hs_descriptors)) {
ret = PTR_ERR(f->hs_descriptors);
f->hs_descriptors = NULL;
- goto error;
+ goto error_unlock;
}

f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
if (IS_ERR(f->ss_descriptors)) {
ret = PTR_ERR(f->ss_descriptors);
f->ss_descriptors = NULL;
- goto error;
+ goto error_unlock;
}

f->ssp_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER_PLUS);
if (IS_ERR(f->ssp_descriptors)) {
ret = PTR_ERR(f->ssp_descriptors);
f->ssp_descriptors = NULL;
- goto error;
+ goto error_unlock;
}

+ mutex_unlock(&opts->lock);
+
/* Preallocate control endpoint request. */
uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL);
@@ -868,6 +880,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)

return 0;

+error_unlock:
+ mutex_unlock(&opts->lock);
v4l2_error:
v4l2_device_unregister(&uvc->v4l2_dev);
error:
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 8d6f705c5819..1c204ef5f544 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -2132,6 +2132,8 @@ static int dummy_hub_control(
case ClearHubFeature:
break;
case ClearPortFeature:
+ if (wIndex != 1)
+ goto error;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
if (hcd->speed == HCD_USB3) {
@@ -2246,6 +2248,8 @@ static int dummy_hub_control(
retval = -EPIPE;
break;
case SetPortFeature:
+ if (wIndex != 1)
+ goto error;
switch (wValue) {
case USB_PORT_FEAT_LINK_STATE:
if (hcd->speed != HCD_USB3) {
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index b2903e4bbf54..b3391524a744 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -3790,10 +3790,8 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return 0;

done:
- if (dev) {
+ if (dev)
net2280_remove(pdev);
- kfree(dev);
- }
return retval;
}

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 89b3079194d7..2eb1aa25be1d 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -243,6 +243,7 @@ struct tegra_xusb_soc {
bool has_ipfs;
bool lpm_support;
bool otg_reset_sspi;
+ bool otg_set_port_power;

bool has_bar2;
};
@@ -1346,14 +1347,17 @@ static void tegra_xhci_id_work(struct work_struct *work)
struct tegra_xusb_mbox_msg msg;
struct phy *phy = tegra_xusb_get_phy(tegra, "usb2",
tegra->otg_usb2_port);
+ bool host_mode;
u32 status;
int ret;

- dev_dbg(tegra->dev, "host mode %s\n", tegra->host_mode ? "on" : "off");
-
mutex_lock(&tegra->lock);

- if (tegra->host_mode)
+ host_mode = tegra->host_mode;
+
+ dev_dbg(tegra->dev, "host mode %s\n", host_mode ? "on" : "off");
+
+ if (host_mode)
phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST);
else
phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
@@ -1364,42 +1368,44 @@ static void tegra_xhci_id_work(struct work_struct *work)
tegra->otg_usb2_port);

pm_runtime_get_sync(tegra->dev);
- if (tegra->host_mode) {
- /* switch to host mode */
- if (tegra->otg_usb3_port >= 0) {
- if (tegra->soc->otg_reset_sspi) {
- /* set PP=0 */
- tegra_xhci_hc_driver.hub_control(
- xhci->shared_hcd, GetPortStatus,
- 0, tegra->otg_usb3_port+1,
- (char *) &status, sizeof(status));
- if (status & USB_SS_PORT_STAT_POWER)
- tegra_xhci_set_port_power(tegra, false,
- false);
-
- /* reset OTG port SSPI */
- msg.cmd = MBOX_CMD_RESET_SSPI;
- msg.data = tegra->otg_usb3_port+1;
-
- ret = tegra_xusb_mbox_send(tegra, &msg);
- if (ret < 0) {
- dev_info(tegra->dev,
- "failed to RESET_SSPI %d\n",
- ret);
+ if (tegra->soc->otg_set_port_power) {
+ if (host_mode) {
+ /* switch to host mode */
+ if (tegra->otg_usb3_port >= 0) {
+ if (tegra->soc->otg_reset_sspi) {
+ /* set PP=0 */
+ tegra_xhci_hc_driver.hub_control(
+ xhci->shared_hcd, GetPortStatus,
+ 0, tegra->otg_usb3_port+1,
+ (char *) &status, sizeof(status));
+ if (status & USB_SS_PORT_STAT_POWER)
+ tegra_xhci_set_port_power(tegra, false,
+ false);
+
+ /* reset OTG port SSPI */
+ msg.cmd = MBOX_CMD_RESET_SSPI;
+ msg.data = tegra->otg_usb3_port+1;
+
+ ret = tegra_xusb_mbox_send(tegra, &msg);
+ if (ret < 0) {
+ dev_info(tegra->dev,
+ "failed to RESET_SSPI %d\n",
+ ret);
+ }
}
- }

- tegra_xhci_set_port_power(tegra, false, true);
- }
+ tegra_xhci_set_port_power(tegra, false, true);
+ }

- tegra_xhci_set_port_power(tegra, true, true);
- pm_runtime_mark_last_busy(tegra->dev);
+ tegra_xhci_set_port_power(tegra, true, true);
+ pm_runtime_mark_last_busy(tegra->dev);

- } else {
- if (tegra->otg_usb3_port >= 0)
- tegra_xhci_set_port_power(tegra, false, false);
+ } else {
+ if (tegra->otg_usb3_port >= 0)
+ tegra_xhci_set_port_power(tegra, false, false);

- tegra_xhci_set_port_power(tegra, true, false);
+ tegra_xhci_set_port_power(tegra, true, false);
+ }
}
pm_runtime_put_autosuspend(tegra->dev);
}
@@ -2497,6 +2503,7 @@ static const struct tegra_xusb_soc tegra124_soc = {
.scale_ss_clock = true,
.has_ipfs = true,
.otg_reset_sspi = false,
+ .otg_set_port_power = true,
.ops = &tegra124_ops,
.mbox = {
.cmd = 0xe4,
@@ -2535,6 +2542,7 @@ static const struct tegra_xusb_soc tegra210_soc = {
.scale_ss_clock = false,
.has_ipfs = true,
.otg_reset_sspi = true,
+ .otg_set_port_power = true,
.ops = &tegra124_ops,
.mbox = {
.cmd = 0xe4,
@@ -2578,6 +2586,7 @@ static const struct tegra_xusb_soc tegra186_soc = {
.scale_ss_clock = false,
.has_ipfs = false,
.otg_reset_sspi = false,
+ .otg_set_port_power = true,
.ops = &tegra124_ops,
.mbox = {
.cmd = 0xe4,
@@ -2611,6 +2620,7 @@ static const struct tegra_xusb_soc tegra194_soc = {
.scale_ss_clock = false,
.has_ipfs = false,
.otg_reset_sspi = false,
+ .otg_set_port_power = false,
.ops = &tegra124_ops,
.mbox = {
.cmd = 0x68,
@@ -2643,6 +2653,7 @@ static const struct tegra_xusb_soc tegra234_soc = {
.scale_ss_clock = false,
.has_ipfs = false,
.otg_reset_sspi = false,
+ .otg_set_port_power = false,
.ops = &tegra234_ops,
.mbox = {
.cmd = XUSB_BAR2_ARU_MBOX_CMD,
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index a4668c6d575d..22d91f9f138e 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -340,7 +340,6 @@ static int omap2430_probe(struct platform_device *pdev)
} else {
device_set_of_node_from_dev(&musb->dev, &pdev->dev);
}
- of_node_put(np);

glue->dev = &pdev->dev;
glue->musb = musb;
@@ -458,6 +457,7 @@ static int omap2430_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to register musb device\n");
goto err3;
}
+ of_node_put(np);

return 0;

@@ -467,6 +467,7 @@ static int omap2430_probe(struct platform_device *pdev)
if (!IS_ERR(glue->control_otghs))
put_device(glue->control_otghs);
err2:
+ of_node_put(np);
platform_device_put(musb);

err0:
diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
index aa6b4c4ad5ec..62c853ab18a6 100644
--- a/drivers/usb/serial/belkin_sa.c
+++ b/drivers/usb/serial/belkin_sa.c
@@ -194,6 +194,9 @@ static void belkin_sa_read_int_callback(struct urb *urb)

usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);

+ if (urb->actual_length < BELKIN_SA_MSR_INDEX + 1)
+ goto exit;
+
/* Handle known interrupt data */
/* ignore data[0] and data[1] */

diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index e29569d65991..905f6a560e04 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -445,6 +445,14 @@ static int cypress_generic_port_probe(struct usb_serial_port *port)
return -ENODEV;
}

+ /*
+ * The buffer must be large enough for the one or two-byte header (and
+ * following data), but assume anything smaller than eight bytes is
+ * broken.
+ */
+ if (port->interrupt_out_size < 8)
+ return -EINVAL;
+
priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -1017,8 +1025,8 @@ static void cypress_read_int_callback(struct urb *urb)
char tty_flag = TTY_NORMAL;
int bytes = 0;
int result;
- int i = 0;
int status = urb->status;
+ int i;

switch (status) {
case 0: /* success */
@@ -1056,22 +1064,32 @@ static void cypress_read_int_callback(struct urb *urb)

spin_lock_irqsave(&priv->lock, flags);
result = urb->actual_length;
+ i = 0;
switch (priv->pkt_fmt) {
default:
case packet_format_1:
/* This is for the CY7C64013... */
+ if (result < 2)
+ break;
priv->current_status = data[0] & 0xF8;
bytes = data[1] + 2;
i = 2;
break;
case packet_format_2:
/* This is for the CY7C63743... */
+ if (result < 1)
+ break;
priv->current_status = data[0] & 0xF8;
bytes = (data[0] & 0x07) + 1;
i = 1;
break;
}
spin_unlock_irqrestore(&priv->lock, flags);
+ if (i == 0) {
+ dev_dbg(dev, "%s - short packet received: %d bytes\n",
+ __func__, result);
+ goto continue_read;
+ }
if (result < bytes) {
dev_dbg(dev,
"%s - wrong packet size - received %d bytes but packet said %d bytes\n",
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index a06485965412..a876d6629b65 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -1229,15 +1229,34 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
static int digi_startup(struct usb_serial *serial)
{
struct digi_serial *serial_priv;
+ int oob_port_num;
int ret;
+ int i;
+
+ /*
+ * The port bulk-out buffers must be large enough for header and
+ * buffered data.
+ */
+ for (i = 0; i < serial->type->num_ports; i++) {
+ if (serial->port[i]->bulk_out_size < DIGI_OUT_BUF_SIZE + 2)
+ return -EINVAL;
+ }
+
+ /*
+ * The OOB port bulk-out buffer must be large enough for the two
+ * commands in digi_set_modem_signals().
+ */
+ oob_port_num = serial->type->num_ports;
+ if (serial->port[oob_port_num]->bulk_out_size < 8)
+ return -EINVAL;

serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL);
if (!serial_priv)
return -ENOMEM;

spin_lock_init(&serial_priv->ds_serial_lock);
- serial_priv->ds_oob_port_num = serial->type->num_ports;
- serial_priv->ds_oob_port = serial->port[serial_priv->ds_oob_port_num];
+ serial_priv->ds_oob_port_num = oob_port_num;
+ serial_priv->ds_oob_port = serial->port[oob_port_num];

ret = digi_port_init(serial_priv->ds_oob_port,
serial_priv->ds_oob_port_num);
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 9129e0282c24..baae11b2fa7b 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -1187,6 +1187,10 @@ static void usa49wg_indat_callback(struct urb *urb)
len = 0;

while (i < urb->actual_length) {
+ if (urb->actual_length - i < 3) {
+ dev_warn_ratelimited(&urb->dev->dev, "malformed indat packet\n");
+ break;
+ }

/* Check port number from message */
if (data[i] >= serial->num_ports) {
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 2bce8cc03aca..d225d7c1455f 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -543,6 +543,11 @@ static void mct_u232_read_int_callback(struct urb *urb)
goto exit;
}

+ if (urb->actual_length < 2) {
+ dev_warn_ratelimited(&port->dev, "short interrupt-in packet\n");
+ goto exit;
+ }
+
/*
* The interrupt-in pipe signals exceptional conditions (modem line
* signal changes and errors). data[0] holds MSR, data[1] holds LSR.
diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c
index ad5fdf55a02e..c9b9928c473a 100644
--- a/drivers/usb/serial/mxuport.c
+++ b/drivers/usb/serial/mxuport.c
@@ -962,6 +962,14 @@ static int mxuport_calc_num_ports(struct usb_serial *serial,
*/
BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < 16);

+ /*
+ * The bulk-out buffers must be large enough for the four-byte header
+ * (and following data), but assume anything smaller than eight bytes
+ * is broken.
+ */
+ if (usb_endpoint_maxp(epds->bulk_out[0]) < 8)
+ return -EINVAL;
+
for (i = 1; i < num_ports; ++i)
epds->bulk_out[i] = epds->bulk_out[0];

diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 397ebd5a3e74..91cefff72246 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -30,6 +30,10 @@
/* This one seems to be a re-branded ZyXEL device */
#define BT_IGNITIONPRO_ID 0x2000

+#define OMNINET_HEADERLEN 4
+#define OMNINET_BULKOUTSIZE 64
+#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN)
+
/* function prototypes */
static void omninet_process_read_urb(struct urb *urb);
static int omninet_prepare_write_buffer(struct usb_serial_port *port,
@@ -54,6 +58,7 @@ static struct usb_serial_driver zyxel_omninet_device = {
.description = "ZyXEL - omni.net usb",
.id_table = id_table,
.num_bulk_out = 2,
+ .bulk_out_size = OMNINET_BULKOUTSIZE,
.calc_num_ports = omninet_calc_num_ports,
.port_probe = omninet_port_probe,
.port_remove = omninet_port_remove,
@@ -130,10 +135,6 @@ static void omninet_port_remove(struct usb_serial_port *port)
kfree(od);
}

-#define OMNINET_HEADERLEN 4
-#define OMNINET_BULKOUTSIZE 64
-#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN)
-
static void omninet_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 8add3a5477f6..c8f0d2bbfc1b 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -2450,6 +2450,12 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM825WN (Diag) */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825WN (AT) */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825WN (NMEA) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d63, 0xff, 0xff, 0x30) }, /* MeiG SRM813Q (Diag) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d63, 0xff, 0xff, 0x40) }, /* MeiG SRM813Q (AT) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x30) }, /* MeiG SRM813Q (Diag) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x40) }, /* MeiG SRM813Q (AT) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x60) }, /* MeiG SRM813Q (NMEA) */
+
{ USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */
{ USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */
{ USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */
@@ -2470,7 +2476,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0302, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0802, 0xff), /* Rolling RW350-GL (laptop MBIM) */
.driver_info = RSVD(5) },
- { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff) }, /* Rolling RW135R-GL (laptop MBIM) */
+ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff), /* Rolling RW135R-GL (laptop MBIM) */
+ .driver_info = RSVD(5) },
{ 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) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) },
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
index 238b54993446..d267a31dcccf 100644
--- a/drivers/usb/serial/safe_serial.c
+++ b/drivers/usb/serial/safe_serial.c
@@ -259,6 +259,7 @@ static int safe_prepare_write_buffer(struct usb_serial_port *port,
static int safe_startup(struct usb_serial *serial)
{
struct usb_interface_descriptor *desc;
+ int bulk_out_size;

if (serial->dev->descriptor.bDeviceClass != CDC_DEVICE_CLASS)
return -ENODEV;
@@ -279,6 +280,16 @@ static int safe_startup(struct usb_serial *serial)
default:
return -EINVAL;
}
+
+ /*
+ * The bulk-out buffer needs to be large enough for the two-byte
+ * trailer in safe mode, but assume anything smaller than eight bytes
+ * is broken.
+ */
+ bulk_out_size = serial->port[0]->bulk_out_size;
+ if (bulk_out_size > 0 && bulk_out_size < 8)
+ return -EINVAL;
+
return 0;
}

diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index 939a98c2d3f7..d6f86d5db3bf 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -132,6 +132,13 @@ UNUSUAL_DEV(0x152d, 0x0583, 0x0000, 0x9999,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_REPORT_OPCODES),

+/* Reported-by: Sam Burkels <sam@xxxxxxx> */
+UNUSUAL_DEV(0x154b, 0xf009, 0x0000, 0x9999,
+ "PNY",
+ "PNY ELITE PSSD",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES),
+
/* Reported-by: Thinh Nguyen <thinhn@xxxxxxxxxxxx> */
UNUSUAL_DEV(0x154b, 0xf00b, 0x0000, 0x9999,
"PNY",
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 5439e760a563..1d2eb93caaa2 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -386,6 +386,8 @@ static int dp_altmode_vdm(struct typec_altmode *alt,
dp->state = DP_STATE_EXIT_PRIME;
break;
case DP_CMD_STATUS_UPDATE:
+ if (count < 2)
+ break;
dp->data.status = *vdo;
ret = dp_altmode_status_update(dp);
break;
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
index eeaf79e97261..2f4942cb4f10 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c
+++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
@@ -186,6 +186,15 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status)
rx_buf_ptr = rx_buf + TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET;
msg.header = cpu_to_le16(*(u16 *)rx_buf_ptr);
rx_buf_ptr = rx_buf_ptr + sizeof(msg.header);
+
+ if (count < TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET + sizeof(msg.header) +
+ pd_header_cnt_le(msg.header) * sizeof(msg.payload[0])) {
+ max_tcpci_write16(chip, TCPC_ALERT, TCPC_ALERT_RX_STATUS);
+ dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d for header cnt %d\n",
+ count, pd_header_cnt_le(msg.header));
+ return;
+ }
+
for (payload_index = 0; payload_index < pd_header_cnt_le(msg.header); payload_index++,
rx_buf_ptr += sizeof(msg.payload[0]))
msg.payload[payload_index] = cpu_to_le32(*(u32 *)rx_buf_ptr);
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index c0306b00256b..41d0c17542c0 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -1639,6 +1639,9 @@ static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int cnt)
u32 vdo = p[VDO_INDEX_IDH];
u32 product = p[VDO_INDEX_PRODUCT];

+ if (cnt <= VDO_INDEX_PRODUCT)
+ return;
+
memset(&port->mode_data, 0, sizeof(port->mode_data));

port->partner_ident.id_header = vdo;
@@ -1659,6 +1662,9 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p
u32 product = p[VDO_INDEX_PRODUCT];
int svdm_version;

+ if (cnt <= VDO_INDEX_CABLE_1)
+ return;
+
/*
* Attempt to consume identity only if cable currently is not set
*/
@@ -1682,7 +1688,7 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p
switch (port->negotiated_rev_prime) {
case PD_REV30:
port->cable_desc.pd_revision = 0x0300;
- if (port->cable_desc.active)
+ if (port->cable_desc.active && cnt > VDO_INDEX_CABLE_2)
port->cable_ident.vdo[1] = p[VDO_INDEX_CABLE_2];
break;
case PD_REV20:
@@ -1770,23 +1776,19 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt,
switch (rx_sop_type) {
case TCPC_TX_SOP_PRIME:
pmdata = &port->mode_data_prime;
- if (pmdata->altmodes >= ARRAY_SIZE(port->plug_prime_altmode)) {
- /* Already logged in svdm_consume_svids() */
- return;
- }
break;
case TCPC_TX_SOP:
pmdata = &port->mode_data;
- if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) {
- /* Already logged in svdm_consume_svids() */
- return;
- }
break;
default:
return;
}

for (i = 1; i < cnt; i++) {
+ if (pmdata->altmodes >= ALTMODE_DISCOVERY_MAX) {
+ /* Already logged in svdm_consume_svids() */
+ return;
+ }
paltmode = &pmdata->altmode_desc[pmdata->altmodes];
memset(paltmode, 0, sizeof(*paltmode));

@@ -1931,6 +1933,55 @@ static bool tcpm_cable_vdm_supported(struct tcpm_port *port)
tcpm_can_communicate_sop_prime(port);
}

+static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response,
+ enum tcpm_transmit_type rx_sop_type,
+ enum tcpm_transmit_type *response_tx_sop_type)
+{
+ struct typec_port *typec = port->typec_port;
+ struct pd_mode_data *modep;
+
+ if (rx_sop_type == TCPC_TX_SOP) {
+ modep = &port->mode_data;
+ modep->svid_index++;
+
+ if (modep->svid_index < modep->nsvids) {
+ u16 svid = modep->svids[modep->svid_index];
+ *response_tx_sop_type = TCPC_TX_SOP;
+ response[0] = VDO(svid, 1,
+ typec_get_negotiated_svdm_version(typec),
+ CMD_DISCOVER_MODES);
+ return 1;
+ }
+
+ if (tcpm_cable_vdm_supported(port)) {
+ *response_tx_sop_type = TCPC_TX_SOP_PRIME;
+ response[0] = VDO(USB_SID_PD, 1,
+ typec_get_cable_svdm_version(typec),
+ CMD_DISCOVER_SVID);
+ return 1;
+ }
+
+ tcpm_register_partner_altmodes(port);
+ } else if (rx_sop_type == TCPC_TX_SOP_PRIME) {
+ modep = &port->mode_data_prime;
+ modep->svid_index++;
+
+ if (modep->svid_index < modep->nsvids) {
+ u16 svid = modep->svids[modep->svid_index];
+ *response_tx_sop_type = TCPC_TX_SOP_PRIME;
+ response[0] = VDO(svid, 1,
+ typec_get_cable_svdm_version(typec),
+ CMD_DISCOVER_MODES);
+ return 1;
+ }
+
+ tcpm_register_plug_altmodes(port);
+ tcpm_register_partner_altmodes(port);
+ }
+
+ return 0;
+}
+
static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
const u32 *p, int cnt, u32 *response,
enum adev_actions *adev_action,
@@ -2188,41 +2239,11 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
}
break;
case CMD_DISCOVER_MODES:
- if (rx_sop_type == TCPC_TX_SOP) {
- /* 6.4.4.3.3 */
- svdm_consume_modes(port, p, cnt, rx_sop_type);
- modep->svid_index++;
- if (modep->svid_index < modep->nsvids) {
- u16 svid = modep->svids[modep->svid_index];
- *response_tx_sop_type = TCPC_TX_SOP;
- response[0] = VDO(svid, 1, svdm_version,
- CMD_DISCOVER_MODES);
- rlen = 1;
- } else if (tcpm_cable_vdm_supported(port)) {
- *response_tx_sop_type = TCPC_TX_SOP_PRIME;
- response[0] = VDO(USB_SID_PD, 1,
- typec_get_cable_svdm_version(typec),
- CMD_DISCOVER_SVID);
- rlen = 1;
- } else {
- tcpm_register_partner_altmodes(port);
- }
- } else if (rx_sop_type == TCPC_TX_SOP_PRIME) {
- /* 6.4.4.3.3 */
- svdm_consume_modes(port, p, cnt, rx_sop_type);
- modep_prime->svid_index++;
- if (modep_prime->svid_index < modep_prime->nsvids) {
- u16 svid = modep_prime->svids[modep_prime->svid_index];
- *response_tx_sop_type = TCPC_TX_SOP_PRIME;
- response[0] = VDO(svid, 1,
- typec_get_cable_svdm_version(typec),
- CMD_DISCOVER_MODES);
- rlen = 1;
- } else {
- tcpm_register_plug_altmodes(port);
- tcpm_register_partner_altmodes(port);
- }
- }
+ /* 6.4.4.3.3 */
+ svdm_consume_modes(port, p, cnt, rx_sop_type);
+ rlen = tcpm_handle_discover_mode(port, response,
+ rx_sop_type,
+ response_tx_sop_type);
break;
case CMD_ENTER_MODE:
*response_tx_sop_type = rx_sop_type;
@@ -2265,9 +2286,15 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
switch (cmd) {
case CMD_DISCOVER_IDENT:
case CMD_DISCOVER_SVID:
- case CMD_DISCOVER_MODES:
case VDO_CMD_VENDOR(0) ... VDO_CMD_VENDOR(15):
break;
+ case CMD_DISCOVER_MODES:
+ tcpm_log(port, "Skip SVID 0x%04x (failed to discover mode)",
+ PD_VDO_SVID_SVID0(p[0]));
+ rlen = tcpm_handle_discover_mode(port, response,
+ rx_sop_type,
+ response_tx_sop_type);
+ break;
case CMD_ENTER_MODE:
/* Back to USB Operation */
*adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM;
diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c
index 60b2766a69bf..8b870812a27f 100644
--- a/drivers/usb/typec/tcpm/wcove.c
+++ b/drivers/usb/typec/tcpm/wcove.c
@@ -444,9 +444,11 @@ static int wcove_start_toggling(struct tcpc_dev *tcpc,
return regmap_write(wcove->regmap, USBC_CONTROL1, usbc_ctrl);
}

-static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg)
+static int wcove_read_rx_buffer(struct wcove_typec *wcove,
+ struct pd_message *msg)
{
- unsigned int info;
+ unsigned int info, val, len;
+ u8 *buf = (u8 *)msg;
int ret;
int i;

@@ -454,12 +456,13 @@ static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg)
if (ret)
return ret;

- /* FIXME: Check that USBC_RXINFO_RXBYTES(info) matches the header */
+ len = min(USBC_RXINFO_RXBYTES(info), sizeof(*msg));

- for (i = 0; i < USBC_RXINFO_RXBYTES(info); i++) {
- ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, msg + i);
+ for (i = 0; i < len; i++) {
+ ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, &val);
if (ret)
return ret;
+ buf[i] = val;
}

return regmap_write(wcove->regmap, USBC_RXSTATUS,
diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c
index 8aae80b457d7..67a0991a7b76 100644
--- a/drivers/usb/typec/ucsi/displayport.c
+++ b/drivers/usb/typec/ucsi/displayport.c
@@ -240,6 +240,10 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt,
dp->header |= VDO_CMDT(CMDT_RSP_ACK);
break;
case DP_CMD_CONFIGURE:
+ if (count < 2) {
+ dp->header |= VDO_CMDT(CMDT_RSP_NAK);
+ break;
+ }
dp->data.conf = *data;
if (ucsi_displayport_configure(dp)) {
dp->header |= VDO_CMDT(CMDT_RSP_NAK);
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index efe45ce94374..acc9856f88dc 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -1201,7 +1201,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
struct ucsi_connector *con = container_of(work, struct ucsi_connector,
work);
struct ucsi *ucsi = con->ucsi;
- enum typec_role role;
+ enum typec_role role, prev_role;
u64 command;
int ret;

@@ -1211,6 +1211,8 @@ static void ucsi_handle_connector_change(struct work_struct *work)
dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n",
__func__);

+ prev_role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR);
+
command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);

ret = ucsi_send_command_common(ucsi, command, &con->status,
@@ -1229,9 +1231,14 @@ static void ucsi_handle_connector_change(struct work_struct *work)

role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR);

- if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) {
+ if ((con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) && role != prev_role) {
typec_set_pwr_role(con->port, role);
- ucsi_port_psy_changed(con);
+
+ /* Some power_supply properties vary depending on the power direction when
+ * connected
+ */
+ if (con->status.flags & UCSI_CONSTAT_CONNECTED)
+ ucsi_port_psy_changed(con);

/* Complete pending power role swap */
if (!completion_done(&con->complete))
@@ -1290,13 +1297,22 @@ static void ucsi_handle_connector_change(struct work_struct *work)
*/
void ucsi_connector_change(struct ucsi *ucsi, u8 num)
{
- struct ucsi_connector *con = &ucsi->connector[num - 1];
+ struct ucsi_connector *con;

if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) {
dev_dbg(ucsi->dev, "Early connector change event\n");
return;
}

+ if (!num || num > ucsi->cap.num_connectors) {
+ dev_warn_ratelimited(ucsi->dev,
+ "Bogus connector change on %u (max %u)\n",
+ num, ucsi->cap.num_connectors);
+ return;
+ }
+
+ con = &ucsi->connector[num - 1];
+
if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
schedule_work(&con->work);
}
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index 511dd1b224ae..66864dd5874a 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -1241,6 +1241,11 @@ static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode)
*****************************************************************/

p = strnchr(fw->data, fw->size, ':');
+ if (!p) {
+ dev_err(dev, "Bad FW format: no ':' record header found\n");
+ err = -EINVAL;
+ goto release_mem;
+ }
while (p < eof) {
s = strnchr(p + 1, eof - p - 1, ':');

diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c
index f11535020e35..a5c100107186 100644
--- a/drivers/usb/usbip/vudc_dev.c
+++ b/drivers/usb/usbip/vudc_dev.c
@@ -632,6 +632,7 @@ void vudc_remove(struct platform_device *pdev)
{
struct vudc *udc = platform_get_drvdata(pdev);

+ v_stop_timer(udc);
usb_del_gadget_udc(&udc->gadget);
cleanup_vudc_hw(udc);
kfree(udc);
diff --git a/drivers/usb/usbip/vudc_transfer.c b/drivers/usb/usbip/vudc_transfer.c
index 7e801fee33bf..94b9549c14cb 100644
--- a/drivers/usb/usbip/vudc_transfer.c
+++ b/drivers/usb/usbip/vudc_transfer.c
@@ -490,7 +490,8 @@ void v_stop_timer(struct vudc *udc)
{
struct transfer_timer *t = &udc->tr_timer;

- /* timer itself will take care of stopping */
+ /* Delete the timer synchronously before teardown frees udc. */
dev_dbg(&udc->pdev->dev, "timer stop");
+ timer_delete_sync(&t->timer);
t->state = VUDC_TR_STOPPED;
}
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 3d89de31066a..a7947a615db6 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -12,7 +12,6 @@
#include <linux/fs.h>
#include <linux/filelock.h>
#include <linux/file.h>
-#include <linux/fdtable.h>
#include <linux/capability.h>
#include <linux/dnotify.h>
#include <linux/slab.h>
diff --git a/fs/file_table.c b/fs/file_table.c
index f7661a708746..2a08bc93b0b9 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -9,7 +9,6 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/file.h>
-#include <linux/fdtable.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
diff --git a/fs/hpfs/alloc.c b/fs/hpfs/alloc.c
index 66617b1557c6..f5150372618e 100644
--- a/fs/hpfs/alloc.c
+++ b/fs/hpfs/alloc.c
@@ -372,8 +372,8 @@ int hpfs_check_free_dnodes(struct super_block *s, int n)
return 0;
}
}
+ hpfs_brelse4(&qbh);
}
- hpfs_brelse4(&qbh);
i = 0;
if (hpfs_sb(s)->sb_c_bitmap != -1) {
bmp = hpfs_map_bitmap(s, b, &qbh, "chkdn1");
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 4aa9a1428dd5..b0c3b4399a79 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -96,7 +96,6 @@ static const struct fs_parameter_spec hugetlb_fs_parameters[] = {
static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
{
struct inode *inode = file_inode(file);
- struct hugetlbfs_inode_info *info = HUGETLBFS_I(inode);
loff_t len, vma_len;
int ret;
struct hstate *h = hstate_file(file);
@@ -113,10 +112,6 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
vm_flags_set(vma, VM_HUGETLB | VM_DONTEXPAND);
vma->vm_ops = &hugetlb_vm_ops;

- ret = seal_check_write(info->seals, vma);
- if (ret)
- return ret;
-
/*
* page based offset in vm_pgoff could be sufficiently large to
* overflow a loff_t when converted to byte offset. This can
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index bb00e1e16838..4d86a05258b9 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/fanotify.h>
-#include <linux/fdtable.h>
#include <linux/fsnotify_backend.h>
#include <linux/init.h>
#include <linux/jiffies.h>
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 93c1619cdad6..b89ad128bf09 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/fanotify.h>
#include <linux/fcntl.h>
-#include <linux/fdtable.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/anon_inodes.h>
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 57f635d050eb..75e804bc152c 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -16,7 +16,6 @@
#include <linux/sched/signal.h>
#include <linux/cred.h>
#include <linux/namei.h>
-#include <linux/fdtable.h>
#include <linux/ratelimit.h>
#include <linux/exportfs.h>
#include "overlayfs.h"
diff --git a/fs/proc/base.c b/fs/proc/base.c
index d060af34a6e8..704cf6a0612e 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -58,7 +58,6 @@
#include <linux/init.h>
#include <linux/capability.h>
#include <linux/file.h>
-#include <linux/fdtable.h>
#include <linux/generic-radix-tree.h>
#include <linux/string.h>
#include <linux/seq_file.h>
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index 6c4f9c8c7f13..e3c512675c63 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -1446,8 +1446,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
aces_size = acl_size - sizeof(struct smb_acl);
for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
- if (offsetof(struct smb_ace, sid) +
- aces_size < CIFS_SID_BASE_SIZE)
+ if (aces_size < offsetof(struct smb_ace, sid) +
+ CIFS_SID_BASE_SIZE)
break;
ace_size = le16_to_cpu(ace->size);
if (ace_size > aces_size ||
@@ -1470,8 +1470,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
aces_size = acl_size - sizeof(struct smb_acl);
for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
- if (offsetof(struct smb_ace, sid) +
- aces_size < CIFS_SID_BASE_SIZE)
+ if (aces_size < offsetof(struct smb_ace, sid) +
+ CIFS_SID_BASE_SIZE)
break;
ace_size = le16_to_cpu(ace->size);
if (ace_size > aces_size ||
diff --git a/include/asm-generic/ring_buffer.h b/include/asm-generic/ring_buffer.h
new file mode 100644
index 000000000000..201d2aee1005
--- /dev/null
+++ b/include/asm-generic/ring_buffer.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Generic arch dependent ring_buffer macros.
+ */
+#ifndef __ASM_GENERIC_RING_BUFFER_H__
+#define __ASM_GENERIC_RING_BUFFER_H__
+
+#include <linux/cacheflush.h>
+
+/* Flush cache on ring buffer range if needed. Do nothing by default. */
+#define arch_ring_buffer_flush_range(start, end) do { } while (0)
+
+#endif /* __ASM_GENERIC_RING_BUFFER_H__ */
diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h
index 3bd9f482f0c3..dd218400a613 100644
--- a/include/drm/display/drm_dp.h
+++ b/include/drm/display/drm_dp.h
@@ -997,6 +997,7 @@
# define DP_EDP_14 0x03
# define DP_EDP_14a 0x04 /* eDP 1.4a */
# define DP_EDP_14b 0x05 /* eDP 1.4b */
+# define DP_EDP_15 0x06 /* eDP 1.5 */

#define DP_EDP_GENERAL_CAP_1 0x701
# define DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP (1 << 0)
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 34b71e42fb10..6132faa314fc 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -547,6 +547,7 @@ unsigned long kunit_vm_mmap(struct kunit *test, struct file *file,
unsigned long offset);

void kunit_cleanup(struct kunit *test);
+void kunit_free_boot_suites(void);

void __printf(2, 3) kunit_log_append(struct string_stream *log, const char *fmt, ...);

diff --git a/include/linux/compat.h b/include/linux/compat.h
index 56cebaff0c91..8da0a15c95f4 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -72,6 +72,10 @@
__diag_push(); \
__diag_ignore(GCC, 8, "-Wattribute-alias", \
"Type aliasing is used to sanitize syscall arguments");\
+ __diag_ignore(clang, 23, "-Wunknown-warning-option", \
+ "Avoid breaking versions without -Wattribute-alias"); \
+ __diag_ignore(clang, 23, "-Wattribute-alias", \
+ "Type aliasing is used to sanitize syscall arguments"); \
asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \
__attribute__((alias(__stringify(__se_compat_sys##name)))); \
ALLOW_ERROR_INJECTION(compat_sys##name, ERRNO); \
diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h
index c4e705b794c5..9b4db392a5cc 100644
--- a/include/linux/compiler-clang.h
+++ b/include/linux/compiler-clang.h
@@ -133,6 +133,12 @@
#define __diag_str(s) __diag_str1(s)
#define __diag(s) _Pragma(__diag_str(clang diagnostic s))

+#if CONFIG_CLANG_VERSION >= 230000
+#define __diag_clang_23(s) __diag(s)
+#else
+#define __diag_clang_23(s)
+#endif
+
#define __diag_clang_13(s) __diag(s)

#define __diag_ignore_all(option, comment) \
diff --git a/include/linux/compiler_attributes.h b/include/linux/compiler_attributes.h
index c16d4199bf92..836a50f5917a 100644
--- a/include/linux/compiler_attributes.h
+++ b/include/linux/compiler_attributes.h
@@ -396,6 +396,17 @@
# define __disable_sanitizer_instrumentation
#endif

+/*
+ * Optional: not supported by clang
+ *
+ * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-noipa
+ */
+#if __has_attribute(noipa)
+# define __noipa __attribute__((noipa))
+#else
+# define __noipa
+#endif
+
/*
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-weak-function-attribute
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-weak-variable-attribute
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index bf775396d384..b624f3e8716b 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -569,6 +569,10 @@ struct ftrace_likely_data {
#define __diag_GCC(version, severity, string)
#endif

+#ifndef __diag_clang
+#define __diag_clang(version, severity, string)
+#endif
+
#define __diag_push() __diag(push)
#define __diag_pop() __diag(pop)

diff --git a/include/linux/hid.h b/include/linux/hid.h
index 7d8d09318fa9..7d05b1edacd8 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -949,6 +949,8 @@ struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_ty
int hid_set_field(struct hid_field *, unsigned, __s32);
int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
int interrupt);
+int hid_safe_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data,
+ size_t bufsize, u32 size, int interrupt);
struct hid_field *hidinput_get_led_field(struct hid_device *hid);
unsigned int hidinput_count_leds(struct hid_device *hid);
__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code);
@@ -1213,8 +1215,8 @@ static inline u32 hid_report_len(struct hid_report *report)
return DIV_ROUND_UP(report->size, 8) + (report->id > 0);
}

-int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
- int interrupt);
+int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
+ size_t bufsize, u32 size, int interrupt);

/* HID quirks API */
unsigned long hid_lookup_quirk(const struct hid_device *hdev);
@@ -1245,4 +1247,15 @@ void hid_quirks_exit(__u16 bus);
#define hid_dbg_once(hid, fmt, ...) \
dev_dbg_once(&(hid)->dev, fmt, ##__VA_ARGS__)

+#define hid_err_ratelimited(hid, fmt, ...) \
+ dev_err_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_notice_ratelimited(hid, fmt, ...) \
+ dev_notice_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_warn_ratelimited(hid, fmt, ...) \
+ dev_warn_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_info_ratelimited(hid, fmt, ...) \
+ dev_info_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_dbg_ratelimited(hid, fmt, ...) \
+ dev_dbg_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+
#endif
diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h
index 6a47223e6460..aa87513acbcd 100644
--- a/include/linux/hid_bpf.h
+++ b/include/linux/hid_bpf.h
@@ -72,8 +72,8 @@ struct hid_ops {
int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len,
u64 source, bool from_bpf);
int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type,
- u8 *data, u32 size, int interrupt, u64 source, bool from_bpf,
- bool lock_already_taken);
+ u8 *data, size_t bufsize, u32 size, int interrupt, u64 source,
+ bool from_bpf, bool lock_already_taken);
struct module *owner;
const struct bus_type *bus_type;
};
@@ -200,7 +200,8 @@ struct hid_bpf {

#ifdef CONFIG_HID_BPF
u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
- u32 *size, int interrupt, u64 source, bool from_bpf);
+ size_t *buf_size, u32 *size, int interrupt, u64 source,
+ bool from_bpf);
int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
unsigned char reportnum, __u8 *buf,
u32 size, enum hid_report_type rtype,
@@ -215,8 +216,11 @@ int hid_bpf_device_init(struct hid_device *hid);
u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size);
#else /* CONFIG_HID_BPF */
static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
- u8 *data, u32 *size, int interrupt,
- u64 source, bool from_bpf) { return data; }
+ u8 *data, size_t *buf_size, u32 *size,
+ int interrupt, u64 source, bool from_bpf)
+{
+ return data;
+}
static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev,
unsigned char reportnum, u8 *buf,
u32 size, enum hid_report_type rtype,
diff --git a/include/linux/memfd.h b/include/linux/memfd.h
index d437e3070850..246daadbfde8 100644
--- a/include/linux/memfd.h
+++ b/include/linux/memfd.h
@@ -7,7 +7,14 @@
#ifdef CONFIG_MEMFD_CREATE
extern long memfd_fcntl(struct file *file, unsigned int cmd, unsigned int arg);
struct folio *memfd_alloc_folio(struct file *memfd, pgoff_t idx);
-unsigned int *memfd_file_seals_ptr(struct file *file);
+/*
+ * Check for any existing seals on mmap, return an error if access is denied due
+ * to sealing, or 0 otherwise.
+ *
+ * We also update VMA flags if appropriate by manipulating the VMA flags pointed
+ * to by vm_flags_ptr.
+ */
+int memfd_check_seals_mmap(struct file *file, unsigned long *vm_flags_ptr);
#else
static inline long memfd_fcntl(struct file *f, unsigned int c, unsigned int a)
{
@@ -17,19 +24,11 @@ static inline struct folio *memfd_alloc_folio(struct file *memfd, pgoff_t idx)
{
return ERR_PTR(-EINVAL);
}
-
-static inline unsigned int *memfd_file_seals_ptr(struct file *file)
+static inline int memfd_check_seals_mmap(struct file *file,
+ unsigned long *vm_flags_ptr)
{
- return NULL;
+ return 0;
}
#endif

-/* Retrieve memfd seals associated with the file, if any. */
-static inline unsigned int memfd_file_seals(struct file *file)
-{
- unsigned int *sealsp = memfd_file_seals_ptr(file);
-
- return sealsp ? *sealsp : 0;
-}
-
#endif /* __LINUX_MEMFD_H */
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 01d53e7fdcce..544ee79faf37 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -4140,61 +4140,6 @@ void mem_dump_obj(void *object);
static inline void mem_dump_obj(void *object) {}
#endif

-static inline bool is_write_sealed(int seals)
-{
- return seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE);
-}
-
-/**
- * is_readonly_sealed - Checks whether write-sealed but mapped read-only,
- * in which case writes should be disallowing moving
- * forwards.
- * @seals: the seals to check
- * @vm_flags: the VMA flags to check
- *
- * Returns whether readonly sealed, in which case writess should be disallowed
- * going forward.
- */
-static inline bool is_readonly_sealed(int seals, vm_flags_t vm_flags)
-{
- /*
- * Since an F_SEAL_[FUTURE_]WRITE sealed memfd can be mapped as
- * MAP_SHARED and read-only, take care to not allow mprotect to
- * revert protections on such mappings. Do this only for shared
- * mappings. For private mappings, don't need to mask
- * VM_MAYWRITE as we still want them to be COW-writable.
- */
- if (is_write_sealed(seals) &&
- ((vm_flags & (VM_SHARED | VM_WRITE)) == VM_SHARED))
- return true;
-
- return false;
-}
-
-/**
- * seal_check_write - Check for F_SEAL_WRITE or F_SEAL_FUTURE_WRITE flags and
- * handle them.
- * @seals: the seals to check
- * @vma: the vma to operate on
- *
- * Check whether F_SEAL_WRITE or F_SEAL_FUTURE_WRITE are set; if so, do proper
- * check/handling on the vma flags. Return 0 if check pass, or <0 for errors.
- */
-static inline int seal_check_write(int seals, struct vm_area_struct *vma)
-{
- if (!is_write_sealed(seals))
- return 0;
-
- /*
- * New PROT_WRITE and MAP_SHARED mmaps are not allowed when
- * write seals are active.
- */
- if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_WRITE))
- return -EPERM;
-
- return 0;
-}
-
#ifdef CONFIG_ANON_VMA_NAME
int madvise_set_anon_name(struct mm_struct *mm, unsigned long start,
unsigned long len_in,
diff --git a/include/linux/netdevice_xmit.h b/include/linux/netdevice_xmit.h
index 38325e070296..59726e6cd2cc 100644
--- a/include/linux/netdevice_xmit.h
+++ b/include/linux/netdevice_xmit.h
@@ -2,12 +2,22 @@
#ifndef _LINUX_NETDEVICE_XMIT_H
#define _LINUX_NETDEVICE_XMIT_H

+#if IS_ENABLED(CONFIG_NET_ACT_MIRRED)
+#define MIRRED_NEST_LIMIT 4
+#endif
+
+struct net_device;
+
struct netdev_xmit {
u16 recursion;
u8 more;
#ifdef CONFIG_NET_EGRESS
u8 skip_txqueue;
#endif
+#if IS_ENABLED(CONFIG_NET_ACT_MIRRED)
+ u8 sched_mirred_nest;
+ struct net_device *sched_mirred_dev[MIRRED_NEST_LIMIT];
+#endif
};

#endif
diff --git a/include/linux/parport.h b/include/linux/parport.h
index 464c2ad28039..f64cb0676e3b 100644
--- a/include/linux/parport.h
+++ b/include/linux/parport.h
@@ -240,6 +240,7 @@ struct parport {

unsigned long devflags;
#define PARPORT_DEVPROC_REGISTERED 0
+#define PARPORT_ANNOUNCED 1
struct pardevice *proc_device; /* Currently register proc device */

struct list_head full_list;
diff --git a/include/linux/serdev.h b/include/linux/serdev.h
index ff78efc1f60d..0e2758282522 100644
--- a/include/linux/serdev.h
+++ b/include/linux/serdev.h
@@ -65,6 +65,7 @@ struct serdev_device_driver {
struct device_driver driver;
int (*probe)(struct serdev_device *);
void (*remove)(struct serdev_device *);
+ void (*shutdown)(struct serdev_device *);
};

static inline struct serdev_device_driver *to_serdev_device_driver(struct device_driver *d)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 4344724a9782..107a8c3ff07f 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -802,6 +802,7 @@ enum skb_tstamp_type {
* @_sk_redir: socket redirection information for skmsg
* @_nfct: Associated connection, if any (with nfctinfo bits)
* @skb_iif: ifindex of device we arrived on
+ * @tc_depth: counter for packet duplication
* @tc_index: Traffic control index
* @hash: the packet hash
* @queue_mapping: Queue mapping for multiqueue devices
@@ -1011,6 +1012,7 @@ struct sk_buff {
__u8 csum_not_inet:1;
#endif
__u8 unreadable:1;
+ __u8 tc_depth:2;
#if defined(CONFIG_NET_SCHED) || defined(CONFIG_NET_XGRESS)
__u16 tc_index; /* traffic control index */
#endif
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 5758104921e6..d300a009f1f1 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -245,6 +245,10 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event)
__diag_push(); \
__diag_ignore(GCC, 8, "-Wattribute-alias", \
"Type aliasing is used to sanitize syscall arguments");\
+ __diag_ignore(clang, 23, "-Wunknown-warning-option", \
+ "Avoid breaking versions without -Wattribute-alias");\
+ __diag_ignore(clang, 23, "-Wattribute-alias", \
+ "Type aliasing is used to sanitize syscall arguments");\
asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \
__attribute__((alias(__stringify(__se_sys##name)))); \
ALLOW_ERROR_INJECTION(sys##name, ERRNO); \
diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index d095908073ef..7a8511d0d4b4 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -81,7 +81,6 @@ struct lirc_fh {
/**
* struct rc_dev - represents a remote control device
* @dev: driver model's view of this device
- * @managed_alloc: devm_rc_allocate_device was used to create rc_dev
* @sysfs_groups: sysfs attribute groups
* @device_name: name of the rc child device
* @input_phys: physical path to the input child device
@@ -156,7 +155,6 @@ struct lirc_fh {
*/
struct rc_dev {
struct device dev;
- bool managed_alloc;
const struct attribute_group *sysfs_groups[5];
const char *device_name;
const char *input_phys;
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
index 5af6eb14c5db..fcabb34fff35 100644
--- a/include/net/inet_frag.h
+++ b/include/net/inet_frag.h
@@ -123,27 +123,15 @@ void inet_frags_fini(struct inet_frags *);

int fqdir_init(struct fqdir **fqdirp, struct inet_frags *f, struct net *net);

-static inline void fqdir_pre_exit(struct fqdir *fqdir)
-{
- /* Prevent creation of new frags.
- * Pairs with READ_ONCE() in inet_frag_find().
- */
- WRITE_ONCE(fqdir->high_thresh, 0);
-
- /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire()
- * and ip6frag_expire_frag_queue().
- */
- WRITE_ONCE(fqdir->dead, true);
-}
+void fqdir_pre_exit(struct fqdir *fqdir);
void fqdir_exit(struct fqdir *fqdir);

void inet_frag_kill(struct inet_frag_queue *q);
void inet_frag_destroy(struct inet_frag_queue *q);
struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key);

-/* Free all skbs in the queue; return the sum of their truesizes. */
-unsigned int inet_frag_rbtree_purge(struct rb_root *root,
- enum skb_drop_reason reason);
+void inet_frag_queue_flush(struct inet_frag_queue *q,
+ enum skb_drop_reason reason);

static inline void inet_frag_put(struct inet_frag_queue *q)
{
diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h
index 7321ffe3a108..df61b98b5215 100644
--- a/include/net/ipv6_frag.h
+++ b/include/net/ipv6_frag.h
@@ -68,9 +68,6 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq)
struct sk_buff *head;

rcu_read_lock();
- /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */
- if (READ_ONCE(fq->q.fqdir->dead))
- goto out_rcu_unlock;
spin_lock(&fq->q.lock);

if (fq->q.flags & INET_FRAG_COMPLETE)
@@ -79,6 +76,12 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq)
fq->q.flags |= INET_FRAG_DROP;
inet_frag_kill(&fq->q);

+ /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */
+ if (READ_ONCE(fq->q.fqdir->dead)) {
+ inet_frag_queue_flush(&fq->q, 0);
+ goto out;
+ }
+
dev = dev_get_by_index_rcu(net, fq->iif);
if (!dev)
goto out;
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index b6fff506bf30..b51d65cd965e 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -650,6 +650,7 @@ struct xfrm_mgr {
const struct xfrm_migrate *m,
int num_bundles,
const struct xfrm_kmaddress *k,
+ struct net *net,
const struct xfrm_encap_tmpl *encap);
bool (*is_alive)(const struct km_event *c);
};
@@ -1818,7 +1819,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol);
#ifdef CONFIG_XFRM_MIGRATE
int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
const struct xfrm_migrate *m, int num_bundles,
- const struct xfrm_kmaddress *k,
+ const struct xfrm_kmaddress *k, struct net *net,
const struct xfrm_encap_tmpl *encap);
struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net,
u32 if_id);
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index eef59b9eccfa..e515aeafa878 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -51,7 +51,6 @@
#include <linux/sched/signal.h>
#include <linux/fs.h>
#include <linux/file.h>
-#include <linux/fdtable.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/percpu.h>
diff --git a/ipc/util.c b/ipc/util.c
index 05cb9de66735..14dec7e9c887 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -253,7 +253,7 @@ static inline int ipc_idr_alloc(struct ipc_ids *ids, struct kern_ipc_perm *new)
} else {
new->seq = ipcid_to_seqx(next_id);
idx = idr_alloc(&ids->ipcs_idr, new, ipcid_to_idx(next_id),
- 0, GFP_NOWAIT);
+ ipc_mni, GFP_NOWAIT);
}
if (idx >= 0)
new->id = (new->seq << ipcmni_seq_shift()) + idx;
diff --git a/kernel/bpf/bpf_inode_storage.c b/kernel/bpf/bpf_inode_storage.c
index 29da6d3838f6..e16e79f8cd6d 100644
--- a/kernel/bpf/bpf_inode_storage.c
+++ b/kernel/bpf/bpf_inode_storage.c
@@ -16,7 +16,6 @@
#include <uapi/linux/btf.h>
#include <linux/bpf_lsm.h>
#include <linux/btf_ids.h>
-#include <linux/fdtable.h>
#include <linux/rcupdate_trace.h>

DEFINE_BPF_STORAGE_CACHE(inode_cache);
diff --git a/kernel/bpf/bpf_task_storage.c b/kernel/bpf/bpf_task_storage.c
index adf6dfe0ba68..1eb9852a9f8e 100644
--- a/kernel/bpf/bpf_task_storage.c
+++ b/kernel/bpf/bpf_task_storage.c
@@ -16,7 +16,6 @@
#include <linux/filter.h>
#include <uapi/linux/btf.h>
#include <linux/btf_ids.h>
-#include <linux/fdtable.h>
#include <linux/rcupdate_trace.h>

DEFINE_BPF_STORAGE_CACHE(task_cache);
diff --git a/kernel/bpf/token.c b/kernel/bpf/token.c
index dcbec1a0dfb3..26057aa13503 100644
--- a/kernel/bpf/token.c
+++ b/kernel/bpf/token.c
@@ -1,6 +1,5 @@
#include <linux/bpf.h>
#include <linux/vmalloc.h>
-#include <linux/fdtable.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/kernel.h>
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 6fce2bac6dae..9099c0cc933b 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -2096,18 +2096,6 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx)
if (event->group_leader == event)
del_event_from_groups(event, ctx);

- /*
- * If event was in error state, then keep it
- * that way, otherwise bogus counts will be
- * returned on read(). The only way to get out
- * of error state is by explicit re-enabling
- * of the event
- */
- if (event->state > PERF_EVENT_STATE_OFF) {
- perf_cgroup_event_disable(event, ctx);
- perf_event_set_state(event, PERF_EVENT_STATE_OFF);
- }
-
ctx->generation++;
event->pmu_ctx->nr_events--;
}
@@ -2457,6 +2445,10 @@ __perf_remove_from_context(struct perf_event *event,
state = PERF_EVENT_STATE_DEAD;
}
event_sched_out(event, ctx);
+
+ if (event->state > PERF_EVENT_STATE_OFF)
+ perf_cgroup_event_disable(event, ctx);
+
perf_event_set_state(event, min(event->state, state));
if (flags & DETACH_GROUP)
perf_group_detach(event);
diff --git a/kernel/exit.c b/kernel/exit.c
index b91124b2d334..e798078f958c 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -25,7 +25,6 @@
#include <linux/acct.h>
#include <linux/tsacct_kern.h>
#include <linux/file.h>
-#include <linux/fdtable.h>
#include <linux/freezer.h>
#include <linux/binfmts.h>
#include <linux/nsproxy.h>
diff --git a/kernel/module/dups.c b/kernel/module/dups.c
index 9a92f2f8c9d3..bd2149fbe117 100644
--- a/kernel/module/dups.c
+++ b/kernel/module/dups.c
@@ -18,7 +18,6 @@
#include <linux/completion.h>
#include <linux/cred.h>
#include <linux/file.h>
-#include <linux/fdtable.h>
#include <linux/workqueue.h>
#include <linux/security.h>
#include <linux/mount.h>
diff --git a/kernel/module/kmod.c b/kernel/module/kmod.c
index 0800d9891692..25f253812512 100644
--- a/kernel/module/kmod.c
+++ b/kernel/module/kmod.c
@@ -15,7 +15,6 @@
#include <linux/completion.h>
#include <linux/cred.h>
#include <linux/file.h>
-#include <linux/fdtable.h>
#include <linux/workqueue.h>
#include <linux/security.h>
#include <linux/mount.h>
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index cf2044b4a2ea..4deeb613c556 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -5,6 +5,7 @@
* Copyright (C) 2008 Steven Rostedt <srostedt@xxxxxxxxxx>
*/
#include <linux/trace_recursion.h>
+#include <linux/panic_notifier.h>
#include <linux/trace_events.h>
#include <linux/ring_buffer.h>
#include <linux/trace_clock.h>
@@ -29,6 +30,7 @@
#include <linux/oom.h>
#include <linux/mm.h>

+#include <asm/ring_buffer.h>
#include <asm/local64.h>
#include <asm/local.h>

@@ -549,6 +551,7 @@ struct trace_buffer {

unsigned long range_addr_start;
unsigned long range_addr_end;
+ struct notifier_block flush_nb;

long last_text_delta;
long last_data_delta;
@@ -2316,6 +2319,16 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
kfree(cpu_buffer);
}

+/* Stop recording on a persistent buffer and flush cache if needed. */
+static int rb_flush_buffer_cb(struct notifier_block *nb, unsigned long event, void *data)
+{
+ struct trace_buffer *buffer = container_of(nb, struct trace_buffer, flush_nb);
+
+ ring_buffer_record_off(buffer);
+ arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end);
+ return NOTIFY_DONE;
+}
+
static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
int order, unsigned long start,
unsigned long end,
@@ -2421,6 +2434,12 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,

mutex_init(&buffer->mutex);

+ /* Persistent ring buffer needs to flush cache before reboot. */
+ if (start && end) {
+ buffer->flush_nb.notifier_call = rb_flush_buffer_cb;
+ atomic_notifier_chain_register(&panic_notifier_list, &buffer->flush_nb);
+ }
+
return buffer;

fail_free_buffers:
@@ -2512,6 +2531,9 @@ ring_buffer_free(struct trace_buffer *buffer)
{
int cpu;

+ if (buffer->range_addr_start && buffer->range_addr_end)
+ atomic_notifier_chain_unregister(&panic_notifier_list, &buffer->flush_nb);
+
cpuhp_state_remove_instance(CPUHP_TRACE_RB_PREPARE, &buffer->node);

irq_work_sync(&buffer->irq_work.work);
diff --git a/kernel/umh.c b/kernel/umh.c
index ff1f13a27d29..be9234270777 100644
--- a/kernel/umh.c
+++ b/kernel/umh.c
@@ -13,7 +13,6 @@
#include <linux/completion.h>
#include <linux/cred.h>
#include <linux/file.h>
-#include <linux/fdtable.h>
#include <linux/fs_struct.h>
#include <linux/workqueue.h>
#include <linux/security.h>
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 5ce473ad499b..932e2d8dbd9b 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -1075,7 +1075,7 @@ struct self_test {

static __initconst const struct debug_obj_descr descr_type_test;

-static bool __init is_static_object(void *addr)
+static __noipa bool __init is_static_object(void *addr)
{
struct self_test *obj = addr;

diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c
index 34b7b6833df3..7cd1c87eb2ed 100644
--- a/lib/kunit/executor.c
+++ b/lib/kunit/executor.c
@@ -15,6 +15,16 @@ extern struct kunit_suite * const __kunit_suites_end[];
extern struct kunit_suite * const __kunit_init_suites_start[];
extern struct kunit_suite * const __kunit_init_suites_end[];

+static struct kunit_suite_set kunit_boot_suites;
+
+void kunit_free_boot_suites(void)
+{
+ if (kunit_boot_suites.start) {
+ kunit_free_suite_set(kunit_boot_suites);
+ kunit_boot_suites = (struct kunit_suite_set){ NULL, NULL };
+ }
+}
+
static char *action_param;

module_param_named(action, action_param, charp, 0400);
@@ -392,9 +402,12 @@ int kunit_run_all_tests(void)
pr_err("kunit executor: unknown action '%s'\n", action_param);

free_out:
- if (filter_glob_param || filter_param)
- kunit_free_suite_set(suite_set);
- else if (init_num_suites > 0)
+ if (filter_glob_param || filter_param) {
+ if (err)
+ kunit_free_suite_set(suite_set);
+ else
+ kunit_boot_suites = suite_set;
+ } else if (init_num_suites > 0)
/* Don't use kunit_free_suite_set because suites aren't individually allocated */
kfree(suite_set.start);

diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index 089c832e3cdb..b808826e6de2 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -954,6 +954,7 @@ static void __exit kunit_exit(void)
kunit_bus_shutdown();

kunit_debugfs_cleanup();
+ kunit_free_boot_suites();
}
module_exit(kunit_exit);

diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c
index b550d1a17507..a84a633e7924 100644
--- a/mm/damon/sysfs-schemes.c
+++ b/mm/damon/sysfs-schemes.c
@@ -79,7 +79,6 @@ static void damon_sysfs_scheme_region_release(struct kobject *kobj)
struct damon_sysfs_scheme_region *region = container_of(kobj,
struct damon_sysfs_scheme_region, kobj);

- list_del(&region->list);
kfree(region);
}

@@ -197,7 +196,7 @@ static void damon_sysfs_scheme_regions_rm_dirs(
struct damon_sysfs_scheme_region *r, *next;

list_for_each_entry_safe(r, next, &regions->regions_list, list) {
- /* release function deletes it from the list */
+ list_del(&r->list);
kobject_put(&r->kobj);
regions->nr_regions--;
}
@@ -2186,14 +2185,15 @@ static int damon_sysfs_before_damos_apply(struct damon_ctx *ctx,
region = damon_sysfs_scheme_region_alloc(r);
if (!region)
return 0;
- list_add_tail(&region->list, &sysfs_regions->regions_list);
- sysfs_regions->nr_regions++;
if (kobject_init_and_add(&region->kobj,
&damon_sysfs_scheme_region_ktype,
&sysfs_regions->kobj, "%d",
damon_sysfs_schemes_region_idx++)) {
kobject_put(&region->kobj);
+ return 0;
}
+ list_add_tail(&region->list, &sysfs_regions->regions_list);
+ sysfs_regions->nr_regions++;
return 0;
}

diff --git a/mm/memfd.c b/mm/memfd.c
index 119467307bff..3e5a014fdacc 100644
--- a/mm/memfd.c
+++ b/mm/memfd.c
@@ -197,7 +197,7 @@ static int memfd_wait_for_pins(struct address_space *mapping)
return error;
}

-unsigned int *memfd_file_seals_ptr(struct file *file)
+static unsigned int *memfd_file_seals_ptr(struct file *file)
{
if (shmem_file(file))
return &SHMEM_I(file_inode(file))->seals;
@@ -273,6 +273,12 @@ static int memfd_add_seals(struct file *file, unsigned int seals)
goto unlock;
}

+ /*
+ * SEAL_EXEC implies SEAL_WRITE, making W^X from the start.
+ */
+ if (seals & F_SEAL_EXEC && inode->i_mode & 0111)
+ seals |= F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_FUTURE_WRITE;
+
if ((seals & F_SEAL_WRITE) && !(*file_seals & F_SEAL_WRITE)) {
error = mapping_deny_writable(file->f_mapping);
if (error)
@@ -285,12 +291,6 @@ static int memfd_add_seals(struct file *file, unsigned int seals)
}
}

- /*
- * SEAL_EXEC implys SEAL_WRITE, making W^X from the start.
- */
- if (seals & F_SEAL_EXEC && inode->i_mode & 0111)
- seals |= F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_FUTURE_WRITE;
-
*file_seals |= seals;
error = 0;

@@ -354,6 +354,48 @@ static int check_sysctl_memfd_noexec(unsigned int *flags)
return 0;
}

+static inline bool is_write_sealed(unsigned int seals)
+{
+ return seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE);
+}
+
+static int check_write_seal(unsigned long *vm_flags_ptr)
+{
+ unsigned long vm_flags = *vm_flags_ptr;
+ unsigned long mask = vm_flags & (VM_SHARED | VM_WRITE);
+
+ /* If a private mapping then writability is irrelevant. */
+ if (!(mask & VM_SHARED))
+ return 0;
+
+ /*
+ * New PROT_WRITE and MAP_SHARED mmaps are not allowed when
+ * write seals are active.
+ */
+ if (mask & VM_WRITE)
+ return -EPERM;
+
+ /*
+ * This is a read-only mapping, disallow mprotect() from making a
+ * write-sealed mapping writable in future.
+ */
+ *vm_flags_ptr &= ~VM_MAYWRITE;
+
+ return 0;
+}
+
+int memfd_check_seals_mmap(struct file *file, unsigned long *vm_flags_ptr)
+{
+ int err = 0;
+ unsigned int *seals_ptr = memfd_file_seals_ptr(file);
+ unsigned int seals = seals_ptr ? *seals_ptr : 0;
+
+ if (is_write_sealed(seals))
+ err = check_write_seal(vm_flags_ptr);
+
+ return err;
+}
+
SYSCALL_DEFINE2(memfd_create,
const char __user *, uname,
unsigned int, flags)
diff --git a/mm/memory.c b/mm/memory.c
index 49ee03c4392e..359de59c39b1 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1639,7 +1639,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
* consider uffd-wp bit when zap. For more information,
* see zap_install_uffd_wp_if_needed().
*/
- WARN_ON_ONCE(!vma_is_anonymous(vma));
+ WARN_ON_ONCE(!folio_test_anon(folio));
rss[mm_counter(folio)]--;
if (is_device_private_entry(entry))
folio_remove_rmap_pte(folio, page, vma);
diff --git a/mm/mmap.c b/mm/mmap.c
index d361b1058da1..e5ddc9c2af49 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -369,8 +369,8 @@ unsigned long do_mmap(struct file *file, unsigned long addr,

if (file) {
struct inode *inode = file_inode(file);
- unsigned int seals = memfd_file_seals(file);
unsigned long flags_mask;
+ int err;

if (!file_mmap_ok(file, inode, pgoff, len))
return -EOVERFLOW;
@@ -410,8 +410,6 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
vm_flags |= VM_SHARED | VM_MAYSHARE;
if (!(file->f_mode & FMODE_WRITE))
vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
- else if (is_readonly_sealed(seals, vm_flags))
- vm_flags &= ~VM_MAYWRITE;
fallthrough;
case MAP_PRIVATE:
if (!(file->f_mode & FMODE_READ))
@@ -431,6 +429,14 @@ unsigned long do_mmap(struct file *file, unsigned long addr,
default:
return -EINVAL;
}
+
+ /*
+ * Check to see if we are violating any seals and update VMA
+ * flags if necessary to avoid future seal violations.
+ */
+ err = memfd_check_seals_mmap(file, &vm_flags);
+ if (err)
+ return (unsigned long)err;
} else {
switch (flags & MAP_TYPE) {
case MAP_SHARED:
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index b1a8abe5005e..259249a37faf 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1158,6 +1158,7 @@ __always_inline bool free_pages_prepare(struct page *page,

page_cpupid_reset_last(page);
page->flags &= ~PAGE_FLAGS_CHECK_AT_PREP;
+ page->private = 0;
reset_page_owner(page, order);
page_table_check_free(page, order);
pgalloc_tag_sub(page, 1 << order);
diff --git a/mm/shmem.c b/mm/shmem.c
index c92af39eebdd..51a0f94e6d9f 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2820,12 +2820,6 @@ int shmem_lock(struct file *file, int lock, struct ucounts *ucounts)
static int shmem_mmap(struct file *file, struct vm_area_struct *vma)
{
struct inode *inode = file_inode(file);
- struct shmem_inode_info *info = SHMEM_I(inode);
- int ret;
-
- ret = seal_check_write(info->seals, vma);
- if (ret)
- return ret;

file_accessed(file);
/* This is anonymous shared memory if it is unlinked at the time of mmap */
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index 748188d3b878..b37c9fb178ae 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -223,6 +223,8 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
hard_iface->bat_iv.ogm_buff = NULL;

mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
+
+ cancel_delayed_work_sync(&hard_iface->bat_iv.reschedule_work);
}

static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
@@ -527,8 +529,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
* @if_incoming: interface where the packet was received
* @if_outgoing: interface for which the retransmission should be considered
* @own_packet: true if it is a self-generated ogm
+ *
+ * Return: whether forward packet was scheduled
*/
-static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
+static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
int packet_len, unsigned long send_time,
bool direct_link,
struct batadv_hard_iface *if_incoming,
@@ -552,13 +556,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,

skb = netdev_alloc_skb_ip_align(NULL, skb_size);
if (!skb)
- return;
+ return false;

forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing,
queue_left, bat_priv, skb);
if (!forw_packet_aggr) {
kfree_skb(skb);
- return;
+ return false;
}

forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
@@ -580,6 +584,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
batadv_iv_send_outstanding_bat_ogm_packet);

batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time);
+
+ return true;
}

/* aggregate a new packet into the existing ogm packet */
@@ -609,8 +615,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr,
* @if_outgoing: interface for which the retransmission should be considered
* @own_packet: true if it is a self-generated ogm
* @send_time: timestamp (jiffies) when the packet is to be sent
+ *
+ * Return: whether forward packet was scheduled
*/
-static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
+static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
unsigned char *packet_buff,
int packet_len,
struct batadv_hard_iface *if_incoming,
@@ -662,14 +670,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
send_time += max_aggregation_jiffies;

- batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
- send_time, direct_link,
- if_incoming, if_outgoing,
- own_packet);
+ return batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
+ send_time, direct_link,
+ if_incoming, if_outgoing,
+ own_packet);
} else {
batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
packet_len, direct_link);
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
+
+ return true;
}
}

@@ -781,6 +791,9 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
u32 seqno;
u16 tvlv_len = 0;
unsigned long send_time;
+ bool reschedule = false;
+ bool scheduled;
+ int ret;

lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);

@@ -804,9 +817,15 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
* appended as it may alter the tt tvlv container
*/
batadv_tt_local_commit_changes(bat_priv);
- tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
- ogm_buff_len,
- BATADV_OGM_HLEN);
+ ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
+ ogm_buff_len,
+ BATADV_OGM_HLEN);
+ if (ret < 0) {
+ reschedule = true;
+ goto out;
+ }
+
+ tvlv_len = ret;
}

batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
@@ -825,8 +844,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
/* OGMs from secondary interfaces are only scheduled on their
* respective interfaces.
*/
- batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
- hard_iface, hard_iface, 1, send_time);
+ scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
+ hard_iface, hard_iface, 1, send_time);
+ if (!scheduled)
+ reschedule = true;
+
goto out;
}

@@ -841,15 +863,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
if (!kref_get_unless_zero(&tmp_hard_iface->refcount))
continue;

- batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
- *ogm_buff_len, hard_iface,
- tmp_hard_iface, 1, send_time);
-
+ scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
+ *ogm_buff_len, hard_iface,
+ tmp_hard_iface, 1, send_time);
batadv_hardif_put(tmp_hard_iface);
+
+ if (!scheduled && tmp_hard_iface == hard_iface)
+ reschedule = true;
}
rcu_read_unlock();

out:
+ if (reschedule) {
+ /* there was a failure scheduling the own forward packet.
+ * as result, the batadv_iv_send_outstanding_bat_ogm_packet()
+ * work item is no longer scheduled. it is therefore necessary
+ * to reschedule it manually
+ */
+ queue_delayed_work(batadv_event_workqueue,
+ &hard_iface->bat_iv.reschedule_work,
+ msecs_to_jiffies(atomic_read(&bat_priv->orig_interval)));
+ }
+
batadv_hardif_put(primary_if);
}

@@ -864,6 +899,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
}

+static void batadv_iv_ogm_reschedule(struct work_struct *work)
+{
+ struct delayed_work *delayed_work = to_delayed_work(work);
+ struct batadv_hard_iface *hard_iface;
+
+ hard_iface = container_of(delayed_work,
+ struct batadv_hard_iface,
+ bat_iv.reschedule_work);
+ batadv_iv_ogm_schedule(hard_iface);
+}
+
/**
* batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface
* @orig_node: originator which reproadcasted the OGMs directly
@@ -2267,6 +2313,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,

static void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface)
{
+ INIT_DELAYED_WORK(&hard_iface->bat_iv.reschedule_work, batadv_iv_ogm_reschedule);
+
/* begin scheduling originator messages on that interface */
batadv_iv_ogm_schedule(hard_iface);
}
diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
index 8f89ffe6020c..8cfc3944dcfd 100644
--- a/net/batman-adv/bat_v_ogm.c
+++ b/net/batman-adv/bat_v_ogm.c
@@ -115,14 +115,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)

/**
* batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
+ * @bat_priv: the bat priv with all the mesh interface information
* @skb: the OGM to send
* @hard_iface: the interface to use to send the OGM
*/
-static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
+static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
- struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
-
if (hard_iface->if_status != BATADV_IF_ACTIVE) {
kfree_skb(skb);
return;
@@ -189,6 +189,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)

/**
* batadv_v_ogm_aggr_send() - flush & send aggregation queue
+ * @bat_priv: the bat priv with all the mesh interface information
* @hard_iface: the interface with the aggregation queue to flush
*
* Aggregates all OGMv2 packets currently in the aggregation queue into a
@@ -198,7 +199,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
*
* Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
*/
-static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
+static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
+ struct batadv_hard_iface *hard_iface)
{
unsigned int aggr_len = hard_iface->bat_v.aggr_len;
struct sk_buff *skb_aggr;
@@ -228,27 +230,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
consume_skb(skb);
}

- batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
+ batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
}

/**
* batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
+ * @bat_priv: the bat priv with all the mesh interface information
* @skb: the OGM to queue
* @hard_iface: the interface to queue the OGM on
*/
-static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
+static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
- struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+ if (hard_iface->soft_iface != bat_priv->soft_iface) {
+ kfree_skb(skb);
+ return;
+ }

if (!atomic_read(&bat_priv->aggregated_ogms)) {
- batadv_v_ogm_send_to_if(skb, hard_iface);
+ batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
return;
}

spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
if (!batadv_v_ogm_queue_left(skb, hard_iface))
- batadv_v_ogm_aggr_send(hard_iface);
+ batadv_v_ogm_aggr_send(bat_priv, hard_iface);

hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
__skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
@@ -264,9 +271,9 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
struct batadv_hard_iface *hard_iface;
struct batadv_ogm2_packet *ogm_packet;
struct sk_buff *skb, *skb_tmp;
- unsigned char *ogm_buff;
- int ogm_buff_len;
- u16 tvlv_len = 0;
+ unsigned char **ogm_buff;
+ int *ogm_buff_len;
+ u16 tvlv_len;
int ret;

lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex);
@@ -274,25 +281,27 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
goto out;

- ogm_buff = bat_priv->bat_v.ogm_buff;
- ogm_buff_len = bat_priv->bat_v.ogm_buff_len;
+ ogm_buff = &bat_priv->bat_v.ogm_buff;
+ ogm_buff_len = &bat_priv->bat_v.ogm_buff_len;
+
/* tt changes have to be committed before the tvlv data is
* appended as it may alter the tt tvlv container
*/
batadv_tt_local_commit_changes(bat_priv);
- tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff,
- &ogm_buff_len,
- BATADV_OGM2_HLEN);
+ ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
+ ogm_buff_len,
+ BATADV_OGM2_HLEN);
+ if (ret < 0)
+ goto reschedule;

- bat_priv->bat_v.ogm_buff = ogm_buff;
- bat_priv->bat_v.ogm_buff_len = ogm_buff_len;
+ tvlv_len = ret;

- skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len);
+ skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + *ogm_buff_len);
if (!skb)
goto reschedule;

skb_reserve(skb, ETH_HLEN);
- skb_put_data(skb, ogm_buff, ogm_buff_len);
+ skb_put_data(skb, *ogm_buff, *ogm_buff_len);

ogm_packet = (struct batadv_ogm2_packet *)skb->data;
ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno));
@@ -347,7 +356,7 @@ static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
break;
}

- batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
+ batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
batadv_hardif_put(hard_iface);
}
rcu_read_unlock();
@@ -387,12 +396,14 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)
{
struct batadv_hard_iface_bat_v *batv;
struct batadv_hard_iface *hard_iface;
+ struct batadv_priv *bat_priv;

batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
+ bat_priv = netdev_priv(hard_iface->soft_iface);

spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
- batadv_v_ogm_aggr_send(hard_iface);
+ batadv_v_ogm_aggr_send(bat_priv, hard_iface);
spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);

batadv_v_ogm_start_queue_timer(hard_iface);
@@ -582,7 +593,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
ogm_forward->ttl, if_incoming->net_dev->name);

- batadv_v_ogm_queue_on_if(skb, if_outgoing);
+ batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);

out:
batadv_orig_ifinfo_put(orig_ifinfo);
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index e77f3ef3d733..15aeb07285e6 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -356,12 +356,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
sizeof(local_claim_dest));
local_claim_dest.type = claimtype;

- soft_iface = primary_if->soft_iface;
+ soft_iface = READ_ONCE(primary_if->soft_iface);
+ if (!soft_iface)
+ goto out;

skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
/* IP DST: 0.0.0.0 */
zeroip,
- primary_if->soft_iface,
+ soft_iface,
/* IP SRC: 0.0.0.0 */
zeroip,
/* Ethernet DST: Broadcast */
@@ -514,8 +516,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
entry->crc = BATADV_BLA_CRC_INIT;
entry->bat_priv = bat_priv;
spin_lock_init(&entry->crc_lock);
- atomic_set(&entry->request_sent, 0);
- atomic_set(&entry->wait_periods, 0);
+ entry->state = BATADV_BLA_BACKBONE_GW_SYNCED;
+ entry->wait_periods = 0;
ether_addr_copy(entry->orig, orig);
INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report);
kref_init(&entry->refcount);
@@ -544,9 +546,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
batadv_bla_send_announce(bat_priv, entry);

/* this will be decreased in the worker thread */
- atomic_inc(&entry->request_sent);
- atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS);
- atomic_inc(&bat_priv->bla.num_requests);
+ spin_lock_bh(&bat_priv->bla.num_requests_lock);
+ if (entry->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
+ entry->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
+ entry->wait_periods = BATADV_BLA_WAIT_PERIODS;
+ atomic_inc(&bat_priv->bla.num_requests);
+ }
+ spin_unlock_bh(&bat_priv->bla.num_requests_lock);
}

return entry;
@@ -649,10 +655,12 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw)
backbone_gw->vid, BATADV_CLAIM_TYPE_REQUEST);

/* no local broadcasts should be sent or received, for now. */
- if (!atomic_read(&backbone_gw->request_sent)) {
+ spin_lock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
+ if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
+ backbone_gw->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
atomic_inc(&backbone_gw->bat_priv->bla.num_requests);
- atomic_set(&backbone_gw->request_sent, 1);
}
+ spin_unlock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
}

/**
@@ -873,10 +881,12 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
/* if we have sent a request and the crc was OK,
* we can allow traffic again.
*/
- if (atomic_read(&backbone_gw->request_sent)) {
+ spin_lock_bh(&bat_priv->bla.num_requests_lock);
+ if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED) {
+ backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
- atomic_set(&backbone_gw->request_sent, 0);
}
+ spin_unlock_bh(&bat_priv->bla.num_requests_lock);
}

batadv_backbone_gw_put(backbone_gw);
@@ -1255,9 +1265,13 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
purged = true;

/* don't wait for the pending request anymore */
- if (atomic_read(&backbone_gw->request_sent))
+ spin_lock_bh(&bat_priv->bla.num_requests_lock);
+ if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED)
atomic_dec(&bat_priv->bla.num_requests);

+ backbone_gw->state = BATADV_BLA_BACKBONE_GW_STOPPED;
+ spin_unlock_bh(&bat_priv->bla.num_requests_lock);
+
batadv_bla_del_backbone_claims(backbone_gw);

hlist_del_rcu(&backbone_gw->hash_entry);
@@ -1508,7 +1522,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
batadv_bla_send_loopdetect(bat_priv,
backbone_gw);

- /* request_sent is only set after creation to avoid
+ /* state is only set to unsynced after creation to avoid
* problems when we are not yet known as backbone gw
* in the backbone.
*
@@ -1517,14 +1531,21 @@ static void batadv_bla_periodic_work(struct work_struct *work)
* some grace time.
*/

- if (atomic_read(&backbone_gw->request_sent) == 0)
- continue;
+ spin_lock_bh(&bat_priv->bla.num_requests_lock);
+ if (backbone_gw->state != BATADV_BLA_BACKBONE_GW_UNSYNCED)
+ goto unlock_next;

- if (!atomic_dec_and_test(&backbone_gw->wait_periods))
- continue;
+ if (backbone_gw->wait_periods > 0)
+ backbone_gw->wait_periods--;
+
+ if (backbone_gw->wait_periods > 0)
+ goto unlock_next;

+ backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
- atomic_set(&backbone_gw->request_sent, 0);
+
+unlock_next:
+ spin_unlock_bh(&bat_priv->bla.num_requests_lock);
}
rcu_read_unlock();
}
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 2758aba47a2f..f46064333f33 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -787,6 +787,7 @@ static int batadv_softif_init_late(struct net_device *dev)
atomic_set(&bat_priv->tt.ogm_append_cnt, 0);
#ifdef CONFIG_BATMAN_ADV_BLA
atomic_set(&bat_priv->bla.num_requests, 0);
+ spin_lock_init(&bat_priv->bla.num_requests_lock);
#endif
atomic_set(&bat_priv->tp_num, 0);

diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index 04a83d6be45b..dfc337454992 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -255,6 +255,7 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason,
* batadv_tp_list_find() - find a tp_vars object in the global list
* @bat_priv: the bat priv with all the soft interface information
* @dst: the other endpoint MAC address to look for
+ * @role: role of the session
*
* Look for a tp_vars object matching dst as end_point and return it after
* having increment the refcounter. Return NULL is not found
@@ -262,7 +263,8 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason,
* Return: matching tp_vars or NULL when no tp_vars with @dst was found
*/
static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
- const u8 *dst)
+ const u8 *dst,
+ enum batadv_tp_meter_role role)
{
struct batadv_tp_vars *pos, *tp_vars = NULL;

@@ -271,6 +273,9 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
if (!batadv_compare_eth(pos->other_end, dst))
continue;

+ if (pos->role != role)
+ continue;
+
/* most of the time this function is invoked during the normal
* process..it makes sens to pay more when the session is
* finished and to speed the process up during the measurement
@@ -286,12 +291,33 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
return tp_vars;
}

+/**
+ * batadv_tp_list_active() - check if session from/to destination is ongoing
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @dst: the other endpoint MAC address to look for
+ *
+ * Return: if matching session with @dst was found
+ */
+static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst)
+ __must_hold(&bat_priv->tp_list_lock)
+{
+ struct batadv_tp_vars *tp_vars;
+
+ hlist_for_each_entry_rcu(tp_vars, &bat_priv->tp_list, list) {
+ if (batadv_compare_eth(tp_vars->other_end, dst))
+ return true;
+ }
+
+ return false;
+}
+
/**
* batadv_tp_list_find_session() - find tp_vars session object in the global
* list
* @bat_priv: the bat priv with all the soft interface information
* @dst: the other endpoint MAC address to look for
* @session: session identifier
+ * @role: role of the session
*
* Look for a tp_vars object matching dst as end_point, session as tp meter
* session and return it after having increment the refcounter. Return NULL
@@ -301,7 +327,7 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
*/
static struct batadv_tp_vars *
batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
- const u8 *session)
+ const u8 *session, enum batadv_tp_meter_role role)
{
struct batadv_tp_vars *pos, *tp_vars = NULL;

@@ -313,6 +339,9 @@ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
if (memcmp(pos->session, session, sizeof(pos->session)) != 0)
continue;

+ if (pos->role != role)
+ continue;
+
/* most of the time this function is invoked during the normal
* process..it makes sense to pay more when the session is
* finished and to speed the process up during the measurement
@@ -401,13 +430,7 @@ static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
batadv_tp_list_detach(tp_vars);

/* kill the timer and remove its reference */
- del_timer_sync(&tp_vars->timer);
- /* the worker might have rearmed itself therefore we kill it again. Note
- * that if the worker should run again before invoking the following
- * del_timer(), it would not re-arm itself once again because the status
- * is OFF now
- */
- del_timer(&tp_vars->timer);
+ timer_shutdown_sync(&tp_vars->timer);
batadv_tp_vars_put(tp_vars);
}

@@ -671,13 +694,10 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,

/* find the tp_vars */
tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
- icmp->session);
+ icmp->session, BATADV_TP_SENDER);
if (unlikely(!tp_vars))
return;

- if (unlikely(tp_vars->role != BATADV_TP_SENDER))
- goto out;
-
if (unlikely(batadv_tp_sender_stopped(tp_vars)))
goto out;

@@ -986,10 +1006,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
return;
}

- tp_vars = batadv_tp_list_find(bat_priv, dst);
- if (tp_vars) {
+ if (batadv_tp_list_active(bat_priv, dst)) {
spin_unlock_bh(&bat_priv->tp_list_lock);
- batadv_tp_vars_put(tp_vars);
batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Meter: test to or from the same node already ongoing, aborting\n");
batadv_tp_batctl_error_notify(BATADV_TP_REASON_ALREADY_ONGOING,
@@ -1110,18 +1128,14 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
if (!orig_node)
return;

- tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig);
+ tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig, BATADV_TP_SENDER);
if (!tp_vars) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Meter: trying to interrupt an already over connection\n");
goto out_put_orig_node;
}

- if (unlikely(tp_vars->role != BATADV_TP_SENDER))
- goto out_put_tp_vars;
-
batadv_tp_sender_shutdown(tp_vars, return_value);
-out_put_tp_vars:
batadv_tp_vars_put(tp_vars);
out_put_orig_node:
batadv_orig_node_put(orig_node);
@@ -1377,7 +1391,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
goto out_unlock;

tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
- icmp->session);
+ icmp->session, BATADV_TP_RECEIVER);
if (tp_vars)
goto out_unlock;

@@ -1448,7 +1462,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
}
} else {
tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
- icmp->session);
+ icmp->session, BATADV_TP_RECEIVER);
if (!tp_vars) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Unexpected packet from %pM!\n",
@@ -1457,13 +1471,6 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
}
}

- if (unlikely(tp_vars->role != BATADV_TP_RECEIVER)) {
- batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
- "Meter: dropping packet: not expected (role=%u)\n",
- tp_vars->role);
- goto out;
- }
-
tp_vars->last_recv_time = jiffies;

/* if the packet is a duplicate, it may be the case that an ACK has been
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index d830ccf01669..7041cd69e200 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -843,17 +843,26 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
s32 *tt_len)
{
u16 num_vlan = 0;
- u16 num_entries = 0;
u16 tvlv_len = 0;
unsigned int change_offset;
struct batadv_tvlv_tt_vlan_data *tt_vlan;
struct batadv_orig_node_vlan *vlan;
+ u16 total_entries = 0;
u8 *tt_change_ptr;
+ int vlan_entries;
+ u16 sum_entries;

spin_lock_bh(&orig_node->vlan_list_lock);
hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
+ vlan_entries = atomic_read(&vlan->tt.num_entries);
+
+ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) {
+ *tt_len = 0;
+ goto out;
+ }
+
+ total_entries = sum_entries;
num_vlan++;
- num_entries += atomic_read(&vlan->tt.num_entries);
}

change_offset = sizeof(**tt_data);
@@ -861,7 +870,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,

/* if tt_len is negative, allocate the space needed by the full table */
if (*tt_len < 0)
- *tt_len = batadv_tt_len(num_entries);
+ *tt_len = batadv_tt_len(total_entries);

if (change_offset > U16_MAX || *tt_len > U16_MAX - change_offset) {
*tt_len = 0;
@@ -882,14 +891,27 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
(*tt_data)->num_vlan = htons(num_vlan);

tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
+ num_vlan = 0;
hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
+ vlan_entries = atomic_read(&vlan->tt.num_entries);
+ if (vlan_entries < 1)
+ continue;
+
tt_vlan->vid = htons(vlan->vid);
tt_vlan->crc = htonl(vlan->tt.crc);
tt_vlan->reserved = 0;

tt_vlan++;
+ num_vlan++;
}

+ /* recalculate in case number of VLANs reduced */
+ change_offset = sizeof(**tt_data);
+ change_offset += num_vlan * sizeof(*tt_vlan);
+ tvlv_len = *tt_len + change_offset;
+
+ (*tt_data)->num_vlan = htons(num_vlan);
+
tt_change_ptr = (u8 *)*tt_data + change_offset;
*tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;

@@ -924,21 +946,25 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
{
struct batadv_tvlv_tt_vlan_data *tt_vlan;
struct batadv_softif_vlan *vlan;
+ size_t change_offset;
u16 num_vlan = 0;
- u16 vlan_entries = 0;
u16 total_entries = 0;
u16 tvlv_len;
u8 *tt_change_ptr;
- int change_offset;
+ int vlan_entries;
+ u16 sum_entries;

spin_lock_bh(&bat_priv->softif_vlan_list_lock);
hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
vlan_entries = atomic_read(&vlan->tt.num_entries);
- if (vlan_entries < 1)
- continue;

+ if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) {
+ tvlv_len = 0;
+ goto out;
+ }
+
+ total_entries = sum_entries;
num_vlan++;
- total_entries += vlan_entries;
}

change_offset = sizeof(**tt_data);
@@ -948,8 +974,10 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
if (*tt_len < 0)
*tt_len = batadv_tt_len(total_entries);

- tvlv_len = *tt_len;
- tvlv_len += change_offset;
+ if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) {
+ tvlv_len = 0;
+ goto out;
+ }

*tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
if (!*tt_data) {
@@ -962,6 +990,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
(*tt_data)->num_vlan = htons(num_vlan);

tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
+ num_vlan = 0;
hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
vlan_entries = atomic_read(&vlan->tt.num_entries);
if (vlan_entries < 1)
@@ -972,8 +1001,16 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
tt_vlan->reserved = 0;

tt_vlan++;
+ num_vlan++;
}

+ /* recalculate in case number of VLANs reduced */
+ change_offset = sizeof(**tt_data);
+ change_offset += num_vlan * sizeof(*tt_vlan);
+ tvlv_len = *tt_len + change_offset;
+
+ (*tt_data)->num_vlan = htons(num_vlan);
+
tt_change_ptr = (u8 *)*tt_data + change_offset;
*tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;

diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
index 2a583215d439..8d6b017c433c 100644
--- a/net/batman-adv/tvlv.c
+++ b/net/batman-adv/tvlv.c
@@ -8,10 +8,12 @@

#include <linux/byteorder/generic.h>
#include <linux/container_of.h>
+#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/gfp.h>
#include <linux/if_ether.h>
#include <linux/kref.h>
+#include <linux/limits.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/netdevice.h>
@@ -159,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version)
*
* Return: size of all currently registered tvlv containers in bytes.
*/
-static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
+static size_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
{
struct batadv_tvlv_container *tvlv;
- u16 tvlv_len = 0;
+ size_t tvlv_len = 0;

lockdep_assert_held(&bat_priv->tvlv.container_list_lock);

@@ -306,26 +308,35 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
* The ogm packet might be enlarged or shrunk depending on the current size
* and the size of the to-be-appended tvlv containers.
*
- * Return: size of all appended tvlv containers in bytes.
+ * Return: size of all appended tvlv containers in bytes (max U16_MAX), negative
+ * if operation failed
*/
-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
unsigned char **packet_buff,
int *packet_buff_len, int packet_min_len)
{
struct batadv_tvlv_container *tvlv;
struct batadv_tvlv_hdr *tvlv_hdr;
- u16 tvlv_value_len;
+ size_t tvlv_value_len;
void *tvlv_value;
+ int tvlv_len_ret;
bool ret;

spin_lock_bh(&bat_priv->tvlv.container_list_lock);
tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
+ if (tvlv_value_len > U16_MAX) {
+ tvlv_len_ret = -E2BIG;
+ goto end;
+ }

ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
packet_min_len, tvlv_value_len);
-
- if (!ret)
+ if (!ret) {
+ tvlv_len_ret = -ENOMEM;
goto end;
+ }
+
+ tvlv_len_ret = tvlv_value_len;

if (!tvlv_value_len)
goto end;
@@ -344,7 +355,8 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,

end:
spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
- return tvlv_value_len;
+
+ return tvlv_len_ret;
}

/**
diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h
index e5697230d991..f96f6b3f44a0 100644
--- a/net/batman-adv/tvlv.h
+++ b/net/batman-adv/tvlv.h
@@ -16,7 +16,7 @@
void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
u8 type, u8 version,
void *tvlv_value, u16 tvlv_value_len);
-u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
unsigned char **packet_buff,
int *packet_buff_len, int packet_min_len);
void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index fe774ec8b80b..f703d266780d 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -83,6 +83,9 @@ struct batadv_hard_iface_bat_iv {
/** @ogm_seqno: OGM sequence number - used to identify each OGM */
atomic_t ogm_seqno;

+ /** @reschedule_work: recover OGM schedule after schedule error */
+ struct delayed_work reschedule_work;
+
/** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */
struct mutex ogm_buff_mutex;
};
@@ -1088,6 +1091,12 @@ struct batadv_priv_bla {
/** @num_requests: number of bla requests in flight */
atomic_t num_requests;

+ /**
+ * @num_requests_lock: locks update num_requests +
+ * batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update
+ */
+ spinlock_t num_requests_lock;
+
/**
* @claim_hash: hash table containing mesh nodes this host has claimed
*/
@@ -1822,6 +1831,27 @@ struct batadv_priv {

#ifdef CONFIG_BATMAN_ADV_BLA

+enum batadv_bla_backbone_gw_state {
+ /**
+ * @BATADV_BLA_BACKBONE_GW_STOPPED: backbone gw is being removed
+ * and it must not longer work on requests
+ */
+ BATADV_BLA_BACKBONE_GW_STOPPED,
+
+ /**
+ * @BATADV_BLA_BACKBONE_GW_UNSYNCED: backbone was detected out
+ * of sync and a request was send. No traffic is forwarded until the
+ * situation is resolved
+ */
+ BATADV_BLA_BACKBONE_GW_UNSYNCED,
+
+ /**
+ * @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is consider to be in
+ * sync. traffic can be forwarded
+ */
+ BATADV_BLA_BACKBONE_GW_SYNCED,
+};
+
/**
* struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN
*/
@@ -1847,16 +1877,12 @@ struct batadv_bla_backbone_gw {
/**
* @wait_periods: grace time for bridge forward delays and bla group
* forming at bootup phase - no bcast traffic is formwared until it has
- * elapsed
+ * elapsed. Must only be access with num_requests_lock.
*/
- atomic_t wait_periods;
+ u8 wait_periods;

- /**
- * @request_sent: if this bool is set to true we are out of sync with
- * this backbone gateway - no bcast traffic is formwared until the
- * situation was resolved
- */
- atomic_t request_sent;
+ /** @state: sync state. Must only be access with num_requests_lock. */
+ enum batadv_bla_backbone_gw_state state;

/** @crc: crc16 checksum over all claims */
u16 crc;
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index e5186a438290..03f0b5d27b60 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -485,6 +485,8 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
int ret;

local_skb = skb_clone(skb, GFP_ATOMIC);
+ if (!local_skb)
+ continue;

BT_DBG("xmit %s to %pMR type %u IP %pI6c chan %p",
netdev->name,
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index fbcb3bbfef4f..535fd7de9b1a 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -5223,6 +5223,12 @@ int hci_dev_close_sync(struct hci_dev *hdev)

bt_dev_dbg(hdev, "");

+ /* Set HCI_DRAIN_WORKQUEUE flag to prevent queuing work during
+ * reset/close. See hci_cmd_work() and handle_cmd_cnt_and_timer().
+ */
+ hci_dev_set_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);
+ synchronize_rcu();
+
if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
disable_delayed_work(&hdev->power_off);
disable_delayed_work(&hdev->ncmd_timer);
@@ -5246,6 +5252,7 @@ int hci_dev_close_sync(struct hci_dev *hdev)

if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
cancel_delayed_work_sync(&hdev->cmd_timer);
+ hci_dev_clear_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);
return err;
}

@@ -5345,6 +5352,7 @@ int hci_dev_close_sync(struct hci_dev *hdev)
/* Clear flags */
hdev->flags &= BIT(HCI_RAW);
hci_dev_clear_volatile_flags(hdev);
+ hci_dev_clear_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);

memset(hdev->eir, 0, sizeof(hdev->eir));
memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
@@ -6618,6 +6626,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)
DEFINE_FLEX(struct hci_cp_le_create_cis, cmd, cis, num_cis, 0x1f);
size_t aux_num_cis = 0;
struct hci_conn *conn;
+ u16 timeout = 0;
u8 cig = BT_ISO_QOS_CIG_UNSET;

/* The spec allows only one pending LE Create CIS command at a time. If
@@ -6688,6 +6697,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)
set_bit(HCI_CONN_CREATE_CIS, &conn->flags);
cis->acl_handle = cpu_to_le16(conn->parent->handle);
cis->cis_handle = cpu_to_le16(conn->handle);
+ timeout = conn->conn_timeout;
aux_num_cis++;

if (aux_num_cis >= cmd->num_cis)
@@ -6707,7 +6717,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)
return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_CREATE_CIS,
struct_size(cmd, cis, cmd->num_cis),
cmd, HCI_EVT_LE_CIS_ESTABLISHED,
- conn->conn_timeout, NULL);
+ timeout, NULL);
}

int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle)
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 40a6f1e20bab..c0c4df8cfbc9 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -179,12 +179,21 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
{
struct input_dev *dev = session->input;
unsigned char *keys = session->keys;
- unsigned char *udata = skb->data + 1;
- signed char *sdata = skb->data + 1;
- int i, size = skb->len - 1;
+ unsigned char *udata;
+ signed char *sdata;
+ u8 *hdr;
+ int i;
+
+ hdr = skb_pull_data(skb, 1);
+ if (!hdr)
+ return;

- switch (skb->data[0]) {
+ switch (*hdr) {
case 0x01: /* Keyboard report */
+ udata = skb_pull_data(skb, 8);
+ if (!udata)
+ break;
+
for (i = 0; i < 8; i++)
input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);

@@ -213,6 +222,10 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
break;

case 0x02: /* Mouse report */
+ sdata = skb_pull_data(skb, 3);
+ if (!sdata)
+ break;
+
input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
@@ -222,7 +235,7 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
input_report_rel(dev, REL_X, sdata[1]);
input_report_rel(dev, REL_Y, sdata[2]);

- if (size > 3)
+ if (skb->len > 0)
input_report_rel(dev, REL_WHEEL, sdata[3]);
break;
}
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index d00cd1bf45a8..f262c32da4f2 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -553,7 +553,7 @@ static void iso_recv_frame(struct iso_conn *conn, struct sk_buff *skb)
struct sock *sk;

iso_conn_lock(conn);
- sk = conn->sk;
+ sk = iso_sock_hold(conn);
iso_conn_unlock(conn);

if (!sk)
@@ -562,11 +562,15 @@ static void iso_recv_frame(struct iso_conn *conn, struct sk_buff *skb)
BT_DBG("sk %p len %d", sk, skb->len);

if (sk->sk_state != BT_CONNECTED)
- goto drop;
+ goto drop_put;

- if (!sock_queue_rcv_skb(sk, skb))
+ if (!sock_queue_rcv_skb(sk, skb)) {
+ sock_put(sk);
return;
+ }

+drop_put:
+ sock_put(sk);
drop:
kfree_skb(skb);
}
@@ -831,8 +835,8 @@ static void __iso_sock_close(struct sock *sk)
/* Must be called on unlocked socket. */
static void iso_sock_close(struct sock *sk)
{
- iso_sock_clear_timer(sk);
lock_sock(sk);
+ iso_sock_clear_timer(sk);
__iso_sock_close(sk);
release_sock(sk);
iso_sock_kill(sk);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index b24e4d8130dd..17d69d721c72 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -411,8 +411,10 @@ static void l2cap_chan_timeout(struct work_struct *work)

BT_DBG("chan %p state %s", chan, state_to_string(chan->state));

- if (!conn)
+ if (!conn) {
+ l2cap_chan_put(chan);
return;
+ }

mutex_lock(&conn->lock);
/* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling
@@ -5194,6 +5196,7 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
cmd_len -= sizeof(*rsp);

list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
+ struct l2cap_chan *orig;
u16 dcid;

if (chan->ident != cmd->ident ||
@@ -5215,8 +5218,10 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,

BT_DBG("dcid[%d] 0x%4.4x", i, dcid);

+ orig = __l2cap_get_chan_by_dcid(conn, dcid);
+
/* Check if dcid is already in use */
- if (dcid && __l2cap_get_chan_by_dcid(conn, dcid)) {
+ if (dcid && orig) {
/* If a device receives a
* L2CAP_CREDIT_BASED_CONNECTION_RSP packet with an
* already-assigned Destination CID, then both the
@@ -5225,10 +5230,24 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
*/
l2cap_chan_del(chan, ECONNREFUSED);
l2cap_chan_unlock(chan);
- chan = __l2cap_get_chan_by_dcid(conn, dcid);
- l2cap_chan_lock(chan);
- l2cap_chan_del(chan, ECONNRESET);
- l2cap_chan_unlock(chan);
+
+ /* Check that the dcid channel mode is
+ * L2CAP_MODE_EXT_FLOWCTL since this procedure is only
+ * valid for that mode and shouldn't disconnect a dcid
+ * in other modes.
+ */
+ if (orig->mode == L2CAP_MODE_EXT_FLOWCTL) {
+ l2cap_chan_lock(orig);
+ /* Disconnect the original channel as it may be
+ * considered connected since dcid has already
+ * been assigned; don't call l2cap_chan_close
+ * directly since that could lead to
+ * l2cap_chan_del and then removing the channel
+ * from the list while we're iterating over it.
+ */
+ __set_chan_timer(orig, 0);
+ l2cap_chan_unlock(orig);
+ }
continue;
}

@@ -5392,14 +5411,20 @@ static inline int l2cap_ecred_reconf_rsp(struct l2cap_conn *conn,

BT_DBG("result 0x%4.4x", result);

- if (!result)
+ if (!result) {
+ list_for_each_entry(chan, &conn->chan_l, list) {
+ if (chan->ident == cmd->ident)
+ chan->ident = 0;
+ }
return 0;
+ }

list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
if (chan->ident != cmd->ident)
continue;

- l2cap_chan_hold(chan);
+ if (!l2cap_chan_hold_unless_zero(chan))
+ continue;
l2cap_chan_lock(chan);

l2cap_chan_del(chan, ECONNRESET);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 5ff9e544d9e1..87d4e3998407 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -1468,6 +1468,10 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
* pin it (hold_unless_zero() additionally skips a chan already past
* its last reference). We then drop the sk lock before taking
* chan->lock, so sk and chan locks are never held together.
+ *
+ * Since we cannot call l2cap_chan_close() without conn->lock,
+ * schedule l2cap_chan_timeout to close the channel; it already
+ * acquires conn->lock -> chan->lock in the correct order.
*/
while ((sk = bt_accept_dequeue(parent, NULL))) {
struct l2cap_chan *chan;
@@ -1485,14 +1489,12 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
state_to_string(chan->state));

l2cap_chan_lock(chan);
- __clear_chan_timer(chan);
- l2cap_chan_close(chan, ECONNRESET);
- /* l2cap_conn_del() may already have killed this socket
- * (it sets SOCK_DEAD); skip the duplicate to avoid a
- * double sock_put()/l2cap_chan_put().
+ /* Since we cannot call l2cap_chan_close() without
+ * conn->lock, schedule its timer to trigger the close
+ * and cleanup of this channel.
*/
- if (!sock_flag(sk, SOCK_DEAD))
- l2cap_sock_kill(sk);
+ if (chan->conn)
+ __set_chan_timer(chan, 0);
l2cap_chan_unlock(chan);

l2cap_chan_put(chan);
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index a461c59ad285..1bc7b5d8f76d 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -1952,6 +1952,25 @@ enum compat_mwt {
EBT_COMPAT_TARGET,
};

+static bool match_size_ok(const struct xt_match *match, unsigned int match_size)
+{
+ u16 csize;
+
+ if (match->matchsize == -1) /* cannot validate ebt_among */
+ return true;
+
+ csize = match->compatsize ? : match->matchsize;
+
+ return match_size >= csize;
+}
+
+static bool tgt_size_ok(const struct xt_target *tgt, unsigned int tgt_size)
+{
+ u16 csize = tgt->compatsize ? : tgt->targetsize;
+
+ return tgt_size >= csize;
+}
+
static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
enum compat_mwt compat_mwt,
struct ebt_entries_buf_state *state,
@@ -1977,6 +1996,11 @@ static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
if (IS_ERR(match))
return PTR_ERR(match);

+ if (!match_size_ok(match, match_size)) {
+ module_put(match->me);
+ return -EINVAL;
+ }
+
off = ebt_compat_match_offset(match, match_size);
if (dst) {
if (match->compat_from_user)
@@ -1996,6 +2020,12 @@ static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
mwt->u.revision);
if (IS_ERR(wt))
return PTR_ERR(wt);
+
+ if (!tgt_size_ok(wt, match_size)) {
+ module_put(wt->me);
+ return -EINVAL;
+ }
+
off = xt_compat_target_offset(wt);

if (dst) {
diff --git a/net/core/filter.c b/net/core/filter.c
index 1d7467601a32..193ecaa7425e 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2867,7 +2867,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start,

psge->length = start - offset;
rsge.length -= psge->length;
- rsge.offset += start;
+ rsge.offset += start - offset;

sk_msg_iter_var_next(i);
sg_unmark_end(psge);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index aa9e91488473..fba5f06b94d9 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2765,6 +2765,8 @@ int ___pskb_trim(struct sk_buff *skb, unsigned int len)
skb->data_len = 0;
skb_set_tail_pointer(skb, len);
}
+ if (!skb_shinfo(skb)->nr_frags && !skb_has_frag_list(skb))
+ skb->unreadable = 0;

if (!skb->sk || skb->destructor == sock_edemux)
skb_condense(skb);
@@ -2772,16 +2774,37 @@ int ___pskb_trim(struct sk_buff *skb, unsigned int len)
}
EXPORT_SYMBOL(___pskb_trim);

+static int pskb_trim_rcsum_complete(struct sk_buff *skb, unsigned int len)
+{
+ int delta = skb->len - len;
+
+ if (skb_frags_readable(skb)) {
+ skb->csum = csum_block_sub(skb->csum,
+ skb_checksum(skb, len, delta, 0),
+ len);
+ return 0;
+ }
+
+ if (len > skb_headlen(skb))
+ return -EFAULT;
+
+ /* The trimmed bytes are unreadable, but the remaining packet can be
+ * checksummed by software after trimming.
+ */
+ skb->ip_summed = CHECKSUM_NONE;
+ return 0;
+}
+
/* Note : use pskb_trim_rcsum() instead of calling this directly
*/
int pskb_trim_rcsum_slow(struct sk_buff *skb, unsigned int len)
{
if (skb->ip_summed == CHECKSUM_COMPLETE) {
- int delta = skb->len - len;
+ int err;

- skb->csum = csum_block_sub(skb->csum,
- skb_checksum(skb, len, delta, 0),
- len);
+ err = pskb_trim_rcsum_complete(skb, len);
+ if (err)
+ return err;
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
int hdlen = (len > skb_headlen(skb)) ? skb_headlen(skb) : len;
int offset = skb_checksum_start_offset(skb) + skb->csum_offset;
@@ -6666,6 +6689,11 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off,
skb_copy_from_linear_data_offset(skb, off, data, new_hlen);
skb->len -= off;

+ /* Remove SKBFL_MANAGED_FRAG_REFS instead of trying to honour it
+ * while refcounting frags below.
+ */
+ skb_zcopy_downgrade_managed(skb);
+
memcpy((struct skb_shared_info *)(data + size),
skb_shinfo(skb),
offsetof(struct skb_shared_info,
@@ -6676,6 +6704,8 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off,
skb_kfree_head(data, size);
return -ENOMEM;
}
+ if (skb_zcopy(skb))
+ net_zcopy_get(skb_zcopy(skb));
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
skb_frag_ref(skb, i);
if (skb_has_frag_list(skb))
@@ -6778,6 +6808,11 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off,
return -ENOMEM;
size = SKB_WITH_OVERHEAD(size);

+ /* Remove SKBFL_MANAGED_FRAG_REFS instead of trying to honour it
+ * while refcounting frags below.
+ */
+ skb_zcopy_downgrade_managed(skb);
+
memcpy((struct skb_shared_info *)(data + size),
skb_shinfo(skb), offsetof(struct skb_shared_info, frags[0]));
if (skb_orphan_frags(skb, gfp_mask)) {
@@ -6820,6 +6855,8 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off,
skb_kfree_head(data, size);
return -ENOMEM;
}
+ if (skb_zcopy(skb))
+ net_zcopy_get(skb_zcopy(skb));
skb_release_data(skb, SKB_CONSUMED);

skb->head = data;
diff --git a/net/ethtool/cmis.h b/net/ethtool/cmis.h
index 3e7c293af78c..778783a0f23c 100644
--- a/net/ethtool/cmis.h
+++ b/net/ethtool/cmis.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */

#define ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH 120
+#define ETHTOOL_CMIS_CDB_EPL_MAX_PL_LENGTH 2048
#define ETHTOOL_CMIS_CDB_CMD_PAGE 0x9F
#define ETHTOOL_CMIS_CDB_PAGE_I2C_ADDR 0x50

@@ -23,6 +24,7 @@ enum ethtool_cmis_cdb_cmd_id {
ETHTOOL_CMIS_CDB_CMD_FW_MANAGMENT_FEATURES = 0x0041,
ETHTOOL_CMIS_CDB_CMD_START_FW_DOWNLOAD = 0x0101,
ETHTOOL_CMIS_CDB_CMD_WRITE_FW_BLOCK_LPL = 0x0103,
+ ETHTOOL_CMIS_CDB_CMD_WRITE_FW_BLOCK_EPL = 0x0104,
ETHTOOL_CMIS_CDB_CMD_COMPLETE_FW_DOWNLOAD = 0x0107,
ETHTOOL_CMIS_CDB_CMD_RUN_FW_IMAGE = 0x0109,
ETHTOOL_CMIS_CDB_CMD_COMMIT_FW_IMAGE = 0x010A,
@@ -38,6 +40,7 @@ enum ethtool_cmis_cdb_cmd_id {
* @resv1: Added to match the CMIS standard request continuity.
* @resv2: Added to match the CMIS standard request continuity.
* @payload: Payload for the CDB commands.
+ * @epl: Extended payload for the CDB commands.
*/
struct ethtool_cmis_cdb_request {
__be16 id;
@@ -49,6 +52,7 @@ struct ethtool_cmis_cdb_request {
u8 resv2;
u8 payload[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH];
);
+ u8 *epl; /* Everything above this field checksummed. */
};

#define CDB_F_COMPLETION_VALID BIT(0)
@@ -59,9 +63,9 @@ struct ethtool_cmis_cdb_request {
* struct ethtool_cmis_cdb_cmd_args - CDB commands execution arguments
* @req: CDB command fields as described in the CMIS standard.
* @max_duration: Maximum duration time for command completion in msec.
+ * @msleep_pre_rpl: Waiting time before checking reply in msec.
* @read_write_len_ext: Allowable additional number of byte octets to the LPL
* in a READ or a WRITE commands.
- * @msleep_pre_rpl: Waiting time before checking reply in msec.
* @rpl_exp_len: Expected reply length in bytes.
* @flags: Validation flags for CDB commands.
* @err_msg: Error message to be sent to user space.
@@ -69,8 +73,8 @@ struct ethtool_cmis_cdb_request {
struct ethtool_cmis_cdb_cmd_args {
struct ethtool_cmis_cdb_request req;
u16 max_duration;
+ u16 msleep_pre_rpl;
u8 read_write_len_ext;
- u8 msleep_pre_rpl;
u8 rpl_exp_len;
u8 flags;
char *err_msg;
@@ -96,13 +100,14 @@ struct ethtool_cmis_cdb_rpl {
u8 payload[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH];
};

-u32 ethtool_cmis_get_max_payload_size(u8 num_of_byte_octs);
+u32 ethtool_cmis_get_max_lpl_size(u8 num_of_byte_octs);

void ethtool_cmis_cdb_compose_args(struct ethtool_cmis_cdb_cmd_args *args,
- enum ethtool_cmis_cdb_cmd_id cmd, u8 *pl,
- u8 lpl_len, u16 max_duration,
- u8 read_write_len_ext, u16 msleep_pre_rpl,
- u8 rpl_exp_len, u8 flags);
+ enum ethtool_cmis_cdb_cmd_id cmd, u8 *lpl,
+ u8 lpl_len, u8 *epl, u16 epl_len,
+ u16 max_duration, u8 read_write_len_ext,
+ u16 msleep_pre_rpl, u8 rpl_exp_len,
+ u8 flags);

void ethtool_cmis_cdb_check_completion_flag(u8 cmis_rev, u8 *flags);

diff --git a/net/ethtool/cmis_cdb.c b/net/ethtool/cmis_cdb.c
index 8bf99295bfbe..fe156991d0be 100644
--- a/net/ethtool/cmis_cdb.c
+++ b/net/ethtool/cmis_cdb.c
@@ -11,25 +11,29 @@
* min(i, 15) byte octets where i specifies the allowable additional number of
* byte octets in a READ or a WRITE.
*/
-u32 ethtool_cmis_get_max_payload_size(u8 num_of_byte_octs)
+u32 ethtool_cmis_get_max_lpl_size(u8 num_of_byte_octs)
{
return 8 * (1 + min_t(u8, num_of_byte_octs, 15));
}

void ethtool_cmis_cdb_compose_args(struct ethtool_cmis_cdb_cmd_args *args,
- enum ethtool_cmis_cdb_cmd_id cmd, u8 *pl,
- u8 lpl_len, u16 max_duration,
- u8 read_write_len_ext, u16 msleep_pre_rpl,
- u8 rpl_exp_len, u8 flags)
+ enum ethtool_cmis_cdb_cmd_id cmd, u8 *lpl,
+ u8 lpl_len, u8 *epl, u16 epl_len,
+ u16 max_duration, u8 read_write_len_ext,
+ u16 msleep_pre_rpl, u8 rpl_exp_len, u8 flags)
{
args->req.id = cpu_to_be16(cmd);
args->req.lpl_len = lpl_len;
- if (pl)
- memcpy(args->req.payload, pl, args->req.lpl_len);
+ if (lpl)
+ memcpy(args->req.payload, lpl, args->req.lpl_len);
+ if (epl) {
+ args->req.epl_len = cpu_to_be16(epl_len);
+ args->req.epl = epl;
+ }

args->max_duration = max_duration;
args->read_write_len_ext =
- ethtool_cmis_get_max_payload_size(read_write_len_ext);
+ ethtool_cmis_get_max_lpl_size(read_write_len_ext);
args->msleep_pre_rpl = msleep_pre_rpl;
args->rpl_exp_len = rpl_exp_len;
args->flags = flags;
@@ -183,7 +187,7 @@ cmis_cdb_validate_password(struct ethtool_cmis_cdb *cdb,
}

ethtool_cmis_cdb_compose_args(&args, ETHTOOL_CMIS_CDB_CMD_QUERY_STATUS,
- (u8 *)&qs_pl, sizeof(qs_pl), 0,
+ (u8 *)&qs_pl, sizeof(qs_pl), NULL, 0, 0,
cdb->read_write_len_ext, 1000,
sizeof(*rpl),
CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
@@ -245,8 +249,9 @@ static int cmis_cdb_module_features_get(struct ethtool_cmis_cdb *cdb,
ethtool_cmis_cdb_check_completion_flag(cdb->cmis_rev, &flags);
ethtool_cmis_cdb_compose_args(&args,
ETHTOOL_CMIS_CDB_CMD_MODULE_FEATURES,
- NULL, 0, 0, cdb->read_write_len_ext,
- 1000, sizeof(*rpl), flags);
+ NULL, 0, NULL, 0, 0,
+ cdb->read_write_len_ext, 1000,
+ sizeof(*rpl), flags);

err = ethtool_cmis_cdb_execute_cmd(dev, &args);
if (err < 0) {
@@ -508,8 +513,13 @@ static int cmis_cdb_process_reply(struct net_device *dev,
}

rpl = (struct ethtool_cmis_cdb_rpl *)page_data->data;
- if ((args->rpl_exp_len > rpl->hdr.rpl_len + rpl_hdr_len) ||
- !rpl->hdr.rpl_chk_code) {
+ if (rpl->hdr.rpl_len != args->rpl_exp_len) {
+ netdev_warn(dev, "CDB reply length mismatch, expected %u got %u\n",
+ args->rpl_exp_len, rpl->hdr.rpl_len);
+ err = -EIO;
+ goto out;
+ }
+ if (!rpl->hdr.rpl_chk_code) {
err = -EIO;
goto out;
}
@@ -546,6 +556,49 @@ __ethtool_cmis_cdb_execute_cmd(struct net_device *dev,
return err;
}

+#define CMIS_CDB_EPL_PAGE_START 0xA0
+#define CMIS_CDB_EPL_PAGE_END 0xAF
+#define CMIS_CDB_EPL_FW_BLOCK_OFFSET_START 128
+#define CMIS_CDB_EPL_FW_BLOCK_OFFSET_END 255
+
+static int
+ethtool_cmis_cdb_execute_epl_cmd(struct net_device *dev,
+ struct ethtool_cmis_cdb_cmd_args *args,
+ struct ethtool_module_eeprom *page_data)
+{
+ u16 epl_len = be16_to_cpu(args->req.epl_len);
+ u32 bytes_written = 0;
+ u8 page;
+ int err;
+
+ for (page = CMIS_CDB_EPL_PAGE_START;
+ page <= CMIS_CDB_EPL_PAGE_END && bytes_written < epl_len; page++) {
+ u16 offset = CMIS_CDB_EPL_FW_BLOCK_OFFSET_START;
+
+ while (offset <= CMIS_CDB_EPL_FW_BLOCK_OFFSET_END &&
+ bytes_written < epl_len) {
+ u32 bytes_left = epl_len - bytes_written;
+ u16 space_left, bytes_to_write;
+
+ space_left = CMIS_CDB_EPL_FW_BLOCK_OFFSET_END - offset + 1;
+ bytes_to_write = min_t(u16, bytes_left,
+ min_t(u16, space_left,
+ args->read_write_len_ext));
+
+ err = __ethtool_cmis_cdb_execute_cmd(dev, page_data,
+ page, offset,
+ bytes_to_write,
+ args->req.epl + bytes_written);
+ if (err < 0)
+ return err;
+
+ offset += bytes_to_write;
+ bytes_written += bytes_to_write;
+ }
+ }
+ return 0;
+}
+
static u8 cmis_cdb_calc_checksum(const void *data, size_t size)
{
const u8 *bytes = (const u8 *)data;
@@ -567,7 +620,9 @@ int ethtool_cmis_cdb_execute_cmd(struct net_device *dev,
int err;

args->req.chk_code =
- cmis_cdb_calc_checksum(&args->req, sizeof(args->req));
+ cmis_cdb_calc_checksum(&args->req,
+ offsetof(struct ethtool_cmis_cdb_request,
+ epl));

if (args->req.lpl_len > args->read_write_len_ext) {
args->err_msg = "LPL length is longer than CDB read write length extension allows";
@@ -589,6 +644,12 @@ int ethtool_cmis_cdb_execute_cmd(struct net_device *dev,
if (err < 0)
return err;

+ if (args->req.epl_len) {
+ err = ethtool_cmis_cdb_execute_epl_cmd(dev, args, &page_data);
+ if (err < 0)
+ return err;
+ }
+
offset = CMIS_CDB_CMD_ID_OFFSET +
offsetof(struct ethtool_cmis_cdb_request, id);
err = __ethtool_cmis_cdb_execute_cmd(dev, &page_data,
diff --git a/net/ethtool/cmis_fw_update.c b/net/ethtool/cmis_fw_update.c
index 655ff5224ffa..9c6d9571cf24 100644
--- a/net/ethtool/cmis_fw_update.c
+++ b/net/ethtool/cmis_fw_update.c
@@ -9,6 +9,7 @@

struct cmis_fw_update_fw_mng_features {
u8 start_cmd_payload_size;
+ u8 write_mechanism;
u16 max_duration_start;
u16 max_duration_write;
u16 max_duration_complete;
@@ -36,10 +37,26 @@ struct cmis_cdb_fw_mng_features_rpl {
};

enum cmis_cdb_fw_write_mechanism {
+ CMIS_CDB_FW_WRITE_MECHANISM_NONE = 0x00,
CMIS_CDB_FW_WRITE_MECHANISM_LPL = 0x01,
+ CMIS_CDB_FW_WRITE_MECHANISM_EPL = 0x10,
CMIS_CDB_FW_WRITE_MECHANISM_BOTH = 0x11,
};

+/* See section 9.7.2 "CMD 0101h: Start Firmware Download" in CMIS standard
+ * revision 5.2.
+ * struct cmis_cdb_start_fw_download_pl is a structured layout of the
+ * flat array, ethtool_cmis_cdb_request::payload.
+ */
+struct cmis_cdb_start_fw_download_pl {
+ __struct_group(cmis_cdb_start_fw_download_pl_h, head, /* no attrs */,
+ __be32 image_size;
+ __be32 resv1;
+ );
+ u8 vendor_data[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH -
+ sizeof(struct cmis_cdb_start_fw_download_pl_h)];
+};
+
static int
cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb,
struct net_device *dev,
@@ -54,7 +71,8 @@ cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb,
ethtool_cmis_cdb_check_completion_flag(cdb->cmis_rev, &flags);
ethtool_cmis_cdb_compose_args(&args,
ETHTOOL_CMIS_CDB_CMD_FW_MANAGMENT_FEATURES,
- NULL, 0, cdb->max_completion_time,
+ NULL, 0, NULL, 0,
+ cdb->max_completion_time,
cdb->read_write_len_ext, 1000,
sizeof(*rpl), flags);

@@ -67,10 +85,9 @@ cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb,
}

rpl = (struct cmis_cdb_fw_mng_features_rpl *)args.req.payload;
- if (!(rpl->write_mechanism == CMIS_CDB_FW_WRITE_MECHANISM_LPL ||
- rpl->write_mechanism == CMIS_CDB_FW_WRITE_MECHANISM_BOTH)) {
+ if (rpl->write_mechanism == CMIS_CDB_FW_WRITE_MECHANISM_NONE) {
ethnl_module_fw_flash_ntf_err(dev, ntf_params,
- "Write LPL is not supported",
+ "CDB write mechanism is not supported",
NULL);
return -EOPNOTSUPP;
}
@@ -82,6 +99,18 @@ cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb,
*/
cdb->read_write_len_ext = rpl->read_write_len_ext;
fw_mng->start_cmd_payload_size = rpl->start_cmd_payload_size;
+ if (fw_mng->start_cmd_payload_size >
+ sizeof_field(struct cmis_cdb_start_fw_download_pl, vendor_data)) {
+ ethnl_module_fw_flash_ntf_err(dev, ntf_params,
+ "Start cmd payload size exceeds max LPL payload",
+ NULL);
+ return -EINVAL;
+ }
+
+ fw_mng->write_mechanism =
+ rpl->write_mechanism == CMIS_CDB_FW_WRITE_MECHANISM_LPL ?
+ CMIS_CDB_FW_WRITE_MECHANISM_LPL :
+ CMIS_CDB_FW_WRITE_MECHANISM_EPL;
fw_mng->max_duration_start = be16_to_cpu(rpl->max_duration_start);
fw_mng->max_duration_write = be16_to_cpu(rpl->max_duration_write);
fw_mng->max_duration_complete = be16_to_cpu(rpl->max_duration_complete);
@@ -89,20 +118,6 @@ cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb,
return 0;
}

-/* See section 9.7.2 "CMD 0101h: Start Firmware Download" in CMIS standard
- * revision 5.2.
- * struct cmis_cdb_start_fw_download_pl is a structured layout of the
- * flat array, ethtool_cmis_cdb_request::payload.
- */
-struct cmis_cdb_start_fw_download_pl {
- __struct_group(cmis_cdb_start_fw_download_pl_h, head, /* no attrs */,
- __be32 image_size;
- __be32 resv1;
- );
- u8 vendor_data[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH -
- sizeof(struct cmis_cdb_start_fw_download_pl_h)];
-};
-
static int
cmis_fw_update_start_download(struct ethtool_cmis_cdb *cdb,
struct ethtool_cmis_fw_update_params *fw_update,
@@ -114,6 +129,14 @@ cmis_fw_update_start_download(struct ethtool_cmis_cdb *cdb,
u8 lpl_len;
int err;

+ if (fw_update->fw->size < vendor_data_size) {
+ ethnl_module_fw_flash_ntf_err(fw_update->dev,
+ &fw_update->ntf_params,
+ "Firmware image too small for module's start payload",
+ NULL);
+ return -EINVAL;
+ }
+
pl.image_size = cpu_to_be32(fw_update->fw->size);
memcpy(pl.vendor_data, fw_update->fw->data, vendor_data_size);

@@ -122,7 +145,7 @@ cmis_fw_update_start_download(struct ethtool_cmis_cdb *cdb,

ethtool_cmis_cdb_compose_args(&args,
ETHTOOL_CMIS_CDB_CMD_START_FW_DOWNLOAD,
- (u8 *)&pl, lpl_len,
+ (u8 *)&pl, lpl_len, NULL, 0,
fw_mng->max_duration_start,
cdb->read_write_len_ext, 1000, 0,
CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
@@ -148,9 +171,9 @@ struct cmis_cdb_write_fw_block_lpl_pl {
};

static int
-cmis_fw_update_write_image(struct ethtool_cmis_cdb *cdb,
- struct ethtool_cmis_fw_update_params *fw_update,
- struct cmis_fw_update_fw_mng_features *fw_mng)
+cmis_fw_update_write_image_lpl(struct ethtool_cmis_cdb *cdb,
+ struct ethtool_cmis_fw_update_params *fw_update,
+ struct cmis_fw_update_fw_mng_features *fw_mng)
{
u8 start = fw_mng->start_cmd_payload_size;
u32 offset, max_block_size, max_lpl_len;
@@ -158,7 +181,7 @@ cmis_fw_update_write_image(struct ethtool_cmis_cdb *cdb,
int err;

max_lpl_len = min_t(u32,
- ethtool_cmis_get_max_payload_size(cdb->read_write_len_ext),
+ ethtool_cmis_get_max_lpl_size(cdb->read_write_len_ext),
ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH);
max_block_size =
max_lpl_len - sizeof_field(struct cmis_cdb_write_fw_block_lpl_pl,
@@ -183,7 +206,7 @@ cmis_fw_update_write_image(struct ethtool_cmis_cdb *cdb,

ethtool_cmis_cdb_compose_args(&args,
ETHTOOL_CMIS_CDB_CMD_WRITE_FW_BLOCK_LPL,
- (u8 *)&pl, lpl_len,
+ (u8 *)&pl, lpl_len, NULL, 0,
fw_mng->max_duration_write,
cdb->read_write_len_ext, 1, 0,
CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
@@ -201,6 +224,67 @@ cmis_fw_update_write_image(struct ethtool_cmis_cdb *cdb,
return 0;
}

+struct cmis_cdb_write_fw_block_epl_pl {
+ u8 fw_block[ETHTOOL_CMIS_CDB_EPL_MAX_PL_LENGTH];
+};
+
+static int
+cmis_fw_update_write_image_epl(struct ethtool_cmis_cdb *cdb,
+ struct ethtool_cmis_fw_update_params *fw_update,
+ struct cmis_fw_update_fw_mng_features *fw_mng)
+{
+ u8 start = fw_mng->start_cmd_payload_size;
+ u32 image_size = fw_update->fw->size;
+ u32 offset, lpl_len;
+ int err;
+
+ lpl_len = sizeof_field(struct cmis_cdb_write_fw_block_lpl_pl,
+ block_address);
+
+ for (offset = start; offset < image_size;
+ offset += ETHTOOL_CMIS_CDB_EPL_MAX_PL_LENGTH) {
+ struct cmis_cdb_write_fw_block_lpl_pl lpl = {
+ .block_address = cpu_to_be32(offset - start),
+ };
+ struct cmis_cdb_write_fw_block_epl_pl *epl;
+ struct ethtool_cmis_cdb_cmd_args args = {};
+ u32 epl_len;
+
+ ethnl_module_fw_flash_ntf_in_progress(fw_update->dev,
+ &fw_update->ntf_params,
+ offset - start,
+ image_size);
+
+ epl_len = min_t(u32, ETHTOOL_CMIS_CDB_EPL_MAX_PL_LENGTH,
+ image_size - offset);
+ epl = kmalloc_array(epl_len, sizeof(u8), GFP_KERNEL);
+ if (!epl)
+ return -ENOMEM;
+
+ memcpy(epl->fw_block, &fw_update->fw->data[offset], epl_len);
+
+ ethtool_cmis_cdb_compose_args(&args,
+ ETHTOOL_CMIS_CDB_CMD_WRITE_FW_BLOCK_EPL,
+ (u8 *)&lpl, lpl_len, (u8 *)epl,
+ epl_len,
+ fw_mng->max_duration_write,
+ cdb->read_write_len_ext, 1, 0,
+ CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
+
+ err = ethtool_cmis_cdb_execute_cmd(fw_update->dev, &args);
+ kfree(epl);
+ if (err < 0) {
+ ethnl_module_fw_flash_ntf_err(fw_update->dev,
+ &fw_update->ntf_params,
+ "Write FW block EPL command failed",
+ args.err_msg);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
static int
cmis_fw_update_complete_download(struct ethtool_cmis_cdb *cdb,
struct net_device *dev,
@@ -212,7 +296,8 @@ cmis_fw_update_complete_download(struct ethtool_cmis_cdb *cdb,

ethtool_cmis_cdb_compose_args(&args,
ETHTOOL_CMIS_CDB_CMD_COMPLETE_FW_DOWNLOAD,
- NULL, 0, fw_mng->max_duration_complete,
+ NULL, 0, NULL, 0,
+ fw_mng->max_duration_complete,
cdb->read_write_len_ext, 1000, 0,
CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);

@@ -236,9 +321,15 @@ cmis_fw_update_download_image(struct ethtool_cmis_cdb *cdb,
if (err < 0)
return err;

- err = cmis_fw_update_write_image(cdb, fw_update, fw_mng);
- if (err < 0)
- return err;
+ if (fw_mng->write_mechanism == CMIS_CDB_FW_WRITE_MECHANISM_LPL) {
+ err = cmis_fw_update_write_image_lpl(cdb, fw_update, fw_mng);
+ if (err < 0)
+ return err;
+ } else {
+ err = cmis_fw_update_write_image_epl(cdb, fw_update, fw_mng);
+ if (err < 0)
+ return err;
+ }

err = cmis_fw_update_complete_download(cdb, fw_update->dev, fw_mng,
&fw_update->ntf_params);
@@ -294,7 +385,7 @@ cmis_fw_update_run_image(struct ethtool_cmis_cdb *cdb, struct net_device *dev,
int err;

ethtool_cmis_cdb_compose_args(&args, ETHTOOL_CMIS_CDB_CMD_RUN_FW_IMAGE,
- (u8 *)&pl, sizeof(pl),
+ (u8 *)&pl, sizeof(pl), NULL, 0,
cdb->max_completion_time,
cdb->read_write_len_ext, 1000, 0,
CDB_F_MODULE_STATE_VALID);
@@ -326,7 +417,8 @@ cmis_fw_update_commit_image(struct ethtool_cmis_cdb *cdb,

ethtool_cmis_cdb_compose_args(&args,
ETHTOOL_CMIS_CDB_CMD_COMMIT_FW_IMAGE,
- NULL, 0, cdb->max_completion_time,
+ NULL, 0, NULL, 0,
+ cdb->max_completion_time,
cdb->read_write_len_ext, 1000, 0,
CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);

diff --git a/net/ethtool/coalesce.c b/net/ethtool/coalesce.c
index 3e18ca1ccc5e..cace02d964cb 100644
--- a/net/ethtool/coalesce.c
+++ b/net/ethtool/coalesce.c
@@ -463,6 +463,12 @@ static int ethnl_update_profile(struct net_device *dev,

nla_for_each_nested_type(nest, ETHTOOL_A_PROFILE_IRQ_MODERATION,
nests, rem) {
+ if (i >= NET_DIM_PARAMS_NUM_PROFILES) {
+ NL_SET_BAD_ATTR(extack, nest);
+ ret = -E2BIG;
+ goto err_out;
+ }
+
ret = nla_parse_nested(tb, len_irq_moder - 1, nest,
coalesce_irq_moderation_policy,
extack);
diff --git a/net/ethtool/eeprom.c b/net/ethtool/eeprom.c
index 3b8209e930fd..80af38a6c76a 100644
--- a/net/ethtool/eeprom.c
+++ b/net/ethtool/eeprom.c
@@ -43,6 +43,9 @@ static int fallback_set_params(struct eeprom_req_info *request,
if (offset >= modinfo->eeprom_len)
return -EINVAL;

+ if (length > modinfo->eeprom_len - offset)
+ return -EINVAL;
+
eeprom->cmd = ETHTOOL_GMODULEEEPROM;
eeprom->len = length;
eeprom->offset = offset;
@@ -68,7 +71,7 @@ static int eeprom_fallback(struct eeprom_req_info *request,
if (err < 0)
return err;

- data = kmalloc(eeprom.len, GFP_KERNEL);
+ data = kzalloc(eeprom.len, GFP_KERNEL);
if (!data)
return -ENOMEM;
err = ethtool_get_module_eeprom_call(dev, &eeprom, data);
@@ -140,12 +143,11 @@ static int eeprom_prepare_data(const struct ethnl_req_info *req_base,
return 0;

err_ops:
+ if (ret == -EOPNOTSUPP)
+ ret = eeprom_fallback(request, reply);
ethnl_ops_complete(dev);
err_free:
kfree(page_data.data);
-
- if (ret == -EOPNOTSUPP)
- return eeprom_fallback(request, reply);
return ret;
}

diff --git a/net/ethtool/linkstate.c b/net/ethtool/linkstate.c
index 05a5f72c99fa..3dc52a39d345 100644
--- a/net/ethtool/linkstate.c
+++ b/net/ethtool/linkstate.c
@@ -105,10 +105,8 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base,

phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_LINKSTATE_HEADER,
info->extack);
- if (IS_ERR(phydev)) {
- ret = PTR_ERR(phydev);
- goto out;
- }
+ if (IS_ERR(phydev))
+ return PTR_ERR(phydev);

ret = ethnl_ops_begin(dev);
if (ret < 0)
diff --git a/net/ethtool/module.c b/net/ethtool/module.c
index 6988e07bdcd6..5a08c320b466 100644
--- a/net/ethtool/module.c
+++ b/net/ethtool/module.c
@@ -119,12 +119,6 @@ ethnl_set_module_validate(struct ethnl_req_info *req_info,
if (!tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY])
return 0;

- if (req_info->dev->ethtool->module_fw_flash_in_progress) {
- NL_SET_ERR_MSG(info->extack,
- "Module firmware flashing is in progress");
- return -EBUSY;
- }
-
if (!ops->get_module_power_mode || !ops->set_module_power_mode) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY],
@@ -147,6 +141,12 @@ ethnl_set_module(struct ethnl_req_info *req_info, struct genl_info *info)

ops = dev->ethtool_ops;

+ if (dev->ethtool->module_fw_flash_in_progress) {
+ NL_SET_ERR_MSG(info->extack,
+ "Module firmware flashing is in progress");
+ return -EBUSY;
+ }
+
power_new.policy = nla_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]);
ret = ops->get_module_power_mode(dev, &power, info->extack);
if (ret < 0)
@@ -282,11 +282,9 @@ void ethnl_module_fw_flash_sock_destroy(struct ethnl_sock_priv *sk_priv)

spin_lock(&module_fw_flash_work_list_lock);
list_for_each_entry(work, &module_fw_flash_work_list, list) {
- if (work->fw_update.dev == sk_priv->dev &&
- work->fw_update.ntf_params.portid == sk_priv->portid) {
+ if (work->fw_update.ntf_params.portid == sk_priv->portid &&
+ dev_net(work->fw_update.dev) == sk_priv->net)
work->fw_update.ntf_params.closed_sock = true;
- break;
- }
}
spin_unlock(&module_fw_flash_work_list_lock);
}
@@ -318,14 +316,13 @@ module_flash_fw_schedule(struct net_device *dev, const char *file_name,
if (err < 0)
goto err_release_firmware;

- dev->ethtool->module_fw_flash_in_progress = true;
- netdev_hold(dev, &module_fw->dev_tracker, GFP_KERNEL);
fw_update->dev = dev;
fw_update->ntf_params.portid = info->snd_portid;
fw_update->ntf_params.seq = info->snd_seq;
fw_update->ntf_params.closed_sock = false;

- err = ethnl_sock_priv_set(skb, dev, fw_update->ntf_params.portid,
+ err = ethnl_sock_priv_set(skb, dev_net(dev),
+ fw_update->ntf_params.portid,
ETHTOOL_SOCK_TYPE_MODULE_FW_FLASH);
if (err < 0)
goto err_release_firmware;
@@ -334,6 +331,9 @@ module_flash_fw_schedule(struct net_device *dev, const char *file_name,
if (err < 0)
goto err_release_firmware;

+ dev->ethtool->module_fw_flash_in_progress = true;
+ netdev_hold(dev, &module_fw->dev_tracker, GFP_KERNEL);
+
schedule_work(&module_fw->work);

return 0;
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index a52be67139d0..409b4109940b 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -50,7 +50,7 @@ const struct nla_policy ethnl_header_policy_phy_stats[] = {
[ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1),
};

-int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid,
+int ethnl_sock_priv_set(struct sk_buff *skb, struct net *net, u32 portid,
enum ethnl_sock_type type)
{
struct ethnl_sock_priv *sk_priv;
@@ -59,7 +59,7 @@ int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid,
if (IS_ERR(sk_priv))
return PTR_ERR(sk_priv);

- sk_priv->dev = dev;
+ sk_priv->net = net;
sk_priv->portid = portid;
sk_priv->type = type;

diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 5e176938d6d2..11843bd10bca 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -315,12 +315,12 @@ enum ethnl_sock_type {
};

struct ethnl_sock_priv {
- struct net_device *dev;
+ struct net *net;
u32 portid;
enum ethnl_sock_type type;
};

-int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid,
+int ethnl_sock_priv_set(struct sk_buff *skb, struct net *net, u32 portid,
enum ethnl_sock_type type);

/**
diff --git a/net/ethtool/pse-pd.c b/net/ethtool/pse-pd.c
index 71843de832cc..01517c53113d 100644
--- a/net/ethtool/pse-pd.c
+++ b/net/ethtool/pse-pd.c
@@ -60,14 +60,14 @@ static int pse_prepare_data(const struct ethnl_req_info *req_base,
struct phy_device *phydev;
int ret;

- ret = ethnl_ops_begin(dev);
- if (ret < 0)
- return ret;
-
phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_PSE_HEADER,
info->extack);
if (IS_ERR(phydev))
- return -ENODEV;
+ return PTR_ERR(phydev);
+
+ ret = ethnl_ops_begin(dev);
+ if (ret < 0)
+ return ret;

ret = pse_get_pse_attributes(phydev, info->extack, data);

diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c
index 8aa45f3fdfdf..3570d58c5cca 100644
--- a/net/ethtool/rss.c
+++ b/net/ethtool/rss.c
@@ -78,8 +78,7 @@ rss_prepare_get(const struct rss_req_info *request, struct net_device *dev,
goto out_ops;
}

- if (data->indir_size)
- data->indir_table = (u32 *)rss_config;
+ data->indir_table = (u32 *)rss_config;
if (data->hkey_size)
data->hkey = rss_config + indir_bytes;

diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c
index b9400d18f01d..73597f0bc923 100644
--- a/net/ethtool/strset.c
+++ b/net/ethtool/strset.c
@@ -299,7 +299,7 @@ static int strset_prepare_data(const struct ethnl_req_info *req_base,
return 0;
}

- phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_HEADER_FLAGS,
+ phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_STRSET_HEADER,
info->extack);

/* phydev can be NULL, check for errors only */
diff --git a/net/handshake/genl.c b/net/handshake/genl.c
index f55d14d7b726..a5fa8b27f224 100644
--- a/net/handshake/genl.c
+++ b/net/handshake/genl.c
@@ -9,6 +9,7 @@
#include "genl.h"

#include <uapi/linux/handshake.h>
+#include <linux/err.h>

/* HANDSHAKE_CMD_ACCEPT - do */
static const struct nla_policy handshake_accept_nl_policy[HANDSHAKE_A_ACCEPT_HANDLER_CLASS + 1] = {
@@ -17,7 +18,7 @@ static const struct nla_policy handshake_accept_nl_policy[HANDSHAKE_A_ACCEPT_HAN

/* HANDSHAKE_CMD_DONE - do */
static const struct nla_policy handshake_done_nl_policy[HANDSHAKE_A_DONE_REMOTE_AUTH + 1] = {
- [HANDSHAKE_A_DONE_STATUS] = { .type = NLA_U32, },
+ [HANDSHAKE_A_DONE_STATUS] = NLA_POLICY_MAX(NLA_U32, MAX_ERRNO),
[HANDSHAKE_A_DONE_SOCKFD] = { .type = NLA_S32, },
[HANDSHAKE_A_DONE_REMOTE_AUTH] = { .type = NLA_U32, },
};
diff --git a/net/handshake/genl.h b/net/handshake/genl.h
index ae72a596f6cc..684e5fd68448 100644
--- a/net/handshake/genl.h
+++ b/net/handshake/genl.h
@@ -10,6 +10,7 @@
#include <net/genetlink.h>

#include <uapi/linux/handshake.h>
+#include <linux/err.h>

int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info);
int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info);
diff --git a/net/handshake/handshake-test.c b/net/handshake/handshake-test.c
index 34fd1d9b2db8..a331b308aaa2 100644
--- a/net/handshake/handshake-test.c
+++ b/net/handshake/handshake-test.c
@@ -25,7 +25,7 @@ static int test_accept_func(struct handshake_req *req, struct genl_info *info,
return 0;
}

-static void test_done_func(struct handshake_req *req, unsigned int status,
+static void test_done_func(struct handshake_req *req, int status,
struct genl_info *info)
{
}
diff --git a/net/handshake/handshake.h b/net/handshake/handshake.h
index a48163765a7a..da61cadd1ad3 100644
--- a/net/handshake/handshake.h
+++ b/net/handshake/handshake.h
@@ -24,6 +24,7 @@ enum hn_flags_bits {
HANDSHAKE_F_NET_DRAINING,
};

+struct file;
struct handshake_proto;

/* One handshake request */
@@ -32,6 +33,7 @@ struct handshake_req {
struct rhash_head hr_rhash;
unsigned long hr_flags;
const struct handshake_proto *hr_proto;
+ struct file *hr_file;
struct sock *hr_sk;
void (*hr_odestruct)(struct sock *sk);

@@ -57,7 +59,7 @@ struct handshake_proto {
int (*hp_accept)(struct handshake_req *req,
struct genl_info *info, int fd);
void (*hp_done)(struct handshake_req *req,
- unsigned int status,
+ int status,
struct genl_info *info);
void (*hp_destroy)(struct handshake_req *req);
};
@@ -86,7 +88,7 @@ struct handshake_req *handshake_req_hash_lookup(struct sock *sk);
struct handshake_req *handshake_req_next(struct handshake_net *hn, int class);
int handshake_req_submit(struct socket *sock, struct handshake_req *req,
gfp_t flags);
-void handshake_complete(struct handshake_req *req, unsigned int status,
+void handshake_complete(struct handshake_req *req, int status,
struct genl_info *info);
bool handshake_req_cancel(struct sock *sk);

diff --git a/net/handshake/netlink.c b/net/handshake/netlink.c
index 7e46d130dce2..e49041cc0f9d 100644
--- a/net/handshake/netlink.c
+++ b/net/handshake/netlink.c
@@ -161,7 +161,7 @@ int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info)

status = -EIO;
if (info->attrs[HANDSHAKE_A_DONE_STATUS])
- status = nla_get_u32(info->attrs[HANDSHAKE_A_DONE_STATUS]);
+ status = -(int)nla_get_u32(info->attrs[HANDSHAKE_A_DONE_STATUS]);

handshake_complete(req, status, info);
sockfd_put(sock);
@@ -203,21 +203,21 @@ static void __net_exit handshake_net_exit(struct net *net)
* accepted and are in progress will be destroyed when
* the socket is closed.
*/
- spin_lock(&hn->hn_lock);
+ spin_lock_bh(&hn->hn_lock);
set_bit(HANDSHAKE_F_NET_DRAINING, &hn->hn_flags);
- list_splice_init(&requests, &hn->hn_requests);
- spin_unlock(&hn->hn_lock);
+ list_splice_init(&hn->hn_requests, &requests);
+ list_for_each_entry(req, &requests, hr_list)
+ get_file(req->hr_file);
+ spin_unlock_bh(&hn->hn_lock);

while (!list_empty(&requests)) {
- req = list_first_entry(&requests, struct handshake_req, hr_list);
- list_del(&req->hr_list);
-
- /*
- * Requests on this list have not yet been
- * accepted, so they do not have an fd to put.
- */
+ struct file *file;

+ req = list_first_entry(&requests, struct handshake_req, hr_list);
+ file = req->hr_file;
+ list_del_init(&req->hr_list);
handshake_complete(req, -ETIMEDOUT, NULL);
+ fput(file);
}
}

diff --git a/net/handshake/request.c b/net/handshake/request.c
index 5df102534a59..96f80e0df67b 100644
--- a/net/handshake/request.c
+++ b/net/handshake/request.c
@@ -13,7 +13,7 @@
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/inet.h>
-#include <linux/fdtable.h>
+#include <linux/file.h>
#include <linux/rhashtable.h>

#include <net/sock.h>
@@ -163,17 +163,20 @@ static void __remove_pending_locked(struct handshake_net *hn,
* otherwise %false.
*
* If @req was on a pending list, it has not yet been accepted.
+ * Returns %false when the net namespace is draining; the drain
+ * loop has taken ownership of the pending list.
*/
static bool remove_pending(struct handshake_net *hn, struct handshake_req *req)
{
bool ret = false;

- spin_lock(&hn->hn_lock);
- if (!list_empty(&req->hr_list)) {
+ spin_lock_bh(&hn->hn_lock);
+ if (!test_bit(HANDSHAKE_F_NET_DRAINING, &hn->hn_flags) &&
+ !list_empty(&req->hr_list)) {
__remove_pending_locked(hn, req);
ret = true;
}
- spin_unlock(&hn->hn_lock);
+ spin_unlock_bh(&hn->hn_lock);

return ret;
}
@@ -183,7 +186,7 @@ struct handshake_req *handshake_req_next(struct handshake_net *hn, int class)
struct handshake_req *req, *pos;

req = NULL;
- spin_lock(&hn->hn_lock);
+ spin_lock_bh(&hn->hn_lock);
list_for_each_entry(pos, &hn->hn_requests, hr_list) {
if (pos->hr_proto->hp_handler_class != class)
continue;
@@ -191,7 +194,7 @@ struct handshake_req *handshake_req_next(struct handshake_net *hn, int class)
req = pos;
break;
}
- spin_unlock(&hn->hn_lock);
+ spin_unlock_bh(&hn->hn_lock);

return req;
}
@@ -216,9 +219,16 @@ EXPORT_SYMBOL_IF_KUNIT(handshake_req_next);
* A zero return value from handshake_req_submit() means that
* exactly one subsequent completion callback is guaranteed.
*
- * A negative return value from handshake_req_submit() means that
- * no completion callback will be done and that @req has been
- * destroyed.
+ * A negative return value from handshake_req_submit() guarantees that
+ * no completion callback will occur and that @req is no longer owned by
+ * the caller. If cancellation wins the completion race after the request
+ * has been published, final destruction is deferred until socket teardown.
+ *
+ * The caller must hold a reference on @sock->file for the duration
+ * of this call. Once the request is published to the accept side, a
+ * concurrent completion or cancellation may release the request's pin on
+ * @sock->file; the caller's reference is what keeps @sock->sk valid until
+ * handshake_req_submit() returns.
*/
int handshake_req_submit(struct socket *sock, struct handshake_req *req,
gfp_t flags)
@@ -237,6 +247,14 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req,
kfree(req);
return -EINVAL;
}
+
+ /*
+ * Pin sock->file for the lifetime of the request so the
+ * accept side does not race a consumer that releases the
+ * socket while a handshake is pending.
+ */
+ req->hr_file = get_file(sock->file);
+
req->hr_odestruct = req->hr_sk->sk_destruct;
req->hr_sk->sk_destruct = handshake_sk_destruct;

@@ -250,7 +268,7 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req,
if (READ_ONCE(hn->hn_pending) >= hn->hn_pending_max)
goto out_err;

- spin_lock(&hn->hn_lock);
+ spin_lock_bh(&hn->hn_lock);
ret = -EOPNOTSUPP;
if (test_bit(HANDSHAKE_F_NET_DRAINING, &hn->hn_flags))
goto out_unlock;
@@ -259,7 +277,7 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req,
goto out_unlock;
if (!__add_pending_locked(hn, req))
goto out_unlock;
- spin_unlock(&hn->hn_lock);
+ spin_unlock_bh(&hn->hn_lock);

ret = handshake_genl_notify(net, req->hr_proto, flags);
if (ret) {
@@ -268,35 +286,46 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req,
goto out_err;
}

- /* Prevent socket release while a handshake request is pending */
+ /*
+ * Pin struct sock so sk_destruct does not run until the
+ * handshake completion path releases it; struct socket is
+ * held separately via hr_file above.
+ */
sock_hold(req->hr_sk);

trace_handshake_submit(net, req, req->hr_sk);
return 0;

out_unlock:
- spin_unlock(&hn->hn_lock);
+ spin_unlock_bh(&hn->hn_lock);
out_err:
- /* Restore original destructor so socket teardown still runs on failure */
- req->hr_sk->sk_destruct = req->hr_odestruct;
trace_handshake_submit_err(net, req, req->hr_sk, ret);
- handshake_req_destroy(req);
+ if (!test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED, &req->hr_flags)) {
+ /* Restore original destructor so socket teardown still runs. */
+ req->hr_sk->sk_destruct = req->hr_odestruct;
+ fput(req->hr_file);
+ handshake_req_destroy(req);
+ }
return ret;
}
EXPORT_SYMBOL(handshake_req_submit);

-void handshake_complete(struct handshake_req *req, unsigned int status,
+void handshake_complete(struct handshake_req *req, int status,
struct genl_info *info)
{
struct sock *sk = req->hr_sk;
struct net *net = sock_net(sk);

if (!test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED, &req->hr_flags)) {
+ struct file *file = req->hr_file;
+
trace_handshake_complete(net, req, sk, status);
req->hr_proto->hp_done(req, status, info);

/* Handshake request is no longer pending */
sock_put(sk);
+
+ fput(file);
}
}
EXPORT_SYMBOL_IF_KUNIT(handshake_complete);
@@ -345,6 +374,7 @@ bool handshake_req_cancel(struct sock *sk)

/* Handshake request is no longer pending */
sock_put(sk);
+ fput(req->hr_file);
return true;
}
EXPORT_SYMBOL(handshake_req_cancel);
diff --git a/net/handshake/tlshd.c b/net/handshake/tlshd.c
index 822507b87447..5464e57c347b 100644
--- a/net/handshake/tlshd.c
+++ b/net/handshake/tlshd.c
@@ -93,7 +93,7 @@ static void tls_handshake_remote_peerids(struct tls_handshake_req *treq,
*
*/
static void tls_handshake_done(struct handshake_req *req,
- unsigned int status, struct genl_info *info)
+ int status, struct genl_info *info)
{
struct tls_handshake_req *treq = handshake_req_private(req);

@@ -104,7 +104,7 @@ static void tls_handshake_done(struct handshake_req *req,
if (!status)
set_bit(HANDSHAKE_F_REQ_SESSION, &req->hr_flags);

- treq->th_consumer_done(treq->th_consumer_data, -status,
+ treq->th_consumer_done(treq->th_consumer_data, status,
treq->th_peerid[0]);
}

@@ -419,6 +419,8 @@ EXPORT_SYMBOL(tls_server_hello_psk);
* Request cancellation races with request completion. To determine
* who won, callers examine the return value from this function.
*
+ * Context: May be called from process or softirq context.
+ *
* Return values:
* %true - Uncompleted handshake request was canceled
* %false - Handshake request already completed or not found
diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index fa97405c517c..e3037741a748 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -84,7 +84,7 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)

/* Get next tlv */
total_length += hsr_sup_tag->tlv.HSR_TLV_length;
- if (!pskb_may_pull(skb, total_length))
+ if (!pskb_may_pull(skb, total_length + sizeof(struct hsr_sup_tlv)))
return false;
skb_pull(skb, total_length);
hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data;
@@ -100,7 +100,7 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)

/* make sure another tlv follows */
total_length += sizeof(struct hsr_sup_tlv) + hsr_sup_tlv->HSR_TLV_length;
- if (!pskb_may_pull(skb, total_length))
+ if (!pskb_may_pull(skb, total_length + sizeof(struct hsr_sup_tlv)))
return false;

/* get next tlv */
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
index 85991fab7db5..47faa8b4aaa9 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -131,8 +131,10 @@ void hsr_del_nodes(struct list_head *node_db)
struct hsr_node *node;
struct hsr_node *tmp;

- list_for_each_entry_safe(node, tmp, node_db, mac_list)
- kfree(node);
+ list_for_each_entry_safe(node, tmp, node_db, mac_list) {
+ list_del_rcu(&node->mac_list);
+ kfree_rcu(node, rcu_head);
+ }
}

void prp_handle_san_frame(bool san, enum hsr_port_type port,
diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c
index 8b0f15abbb38..7976f5106af0 100644
--- a/net/ipv4/ah4.c
+++ b/net/ipv4/ah4.c
@@ -143,7 +143,7 @@ static void ah_output_done(void *data, int err)
}

kfree(AH_SKB_CB(skb)->tmp);
- xfrm_output_resume(skb->sk, skb, err);
+ xfrm_output_resume(skb_to_full_sk(skb), skb, err);
}

static int ah_output(struct xfrm_state *x, struct sk_buff *skb)
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index 2f548900e238..6c8c789ded0e 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -419,8 +419,8 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *
return err;
}

- if (ALIGN(tailen, L1_CACHE_BYTES) > PAGE_SIZE ||
- ALIGN(skb->data_len, L1_CACHE_BYTES) > PAGE_SIZE)
+ if (ALIGN(skb->data_len + tailen, L1_CACHE_BYTES) >
+ PAGE_SIZE)
goto cow;

if (!skb_cloned(skb)) {
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
index d179a2c84222..f9cf20b21a07 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -219,6 +219,41 @@ static int __init inet_frag_wq_init(void)

pure_initcall(inet_frag_wq_init);

+void fqdir_pre_exit(struct fqdir *fqdir)
+{
+ struct inet_frag_queue *fq;
+ struct rhashtable_iter hti;
+
+ /* Prevent creation of new frags.
+ * Pairs with READ_ONCE() in inet_frag_find().
+ */
+ WRITE_ONCE(fqdir->high_thresh, 0);
+
+ /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire()
+ * and ip6frag_expire_frag_queue().
+ */
+ WRITE_ONCE(fqdir->dead, true);
+
+ rhashtable_walk_enter(&fqdir->rhashtable, &hti);
+ rhashtable_walk_start(&hti);
+
+ while ((fq = rhashtable_walk_next(&hti))) {
+ if (IS_ERR(fq)) {
+ if (PTR_ERR(fq) != -EAGAIN)
+ break;
+ continue;
+ }
+ spin_lock_bh(&fq->lock);
+ if (!(fq->flags & INET_FRAG_COMPLETE))
+ inet_frag_queue_flush(fq, 0);
+ spin_unlock_bh(&fq->lock);
+ }
+
+ rhashtable_walk_stop(&hti);
+ rhashtable_walk_exit(&hti);
+}
+EXPORT_SYMBOL(fqdir_pre_exit);
+
void fqdir_exit(struct fqdir *fqdir)
{
INIT_WORK(&fqdir->destroy_work, fqdir_work_fn);
@@ -264,8 +299,8 @@ static void inet_frag_destroy_rcu(struct rcu_head *head)
kmem_cache_free(f->frags_cachep, q);
}

-unsigned int inet_frag_rbtree_purge(struct rb_root *root,
- enum skb_drop_reason reason)
+static unsigned int
+inet_frag_rbtree_purge(struct rb_root *root, enum skb_drop_reason reason)
{
struct rb_node *p = rb_first(root);
unsigned int sum = 0;
@@ -285,7 +320,17 @@ unsigned int inet_frag_rbtree_purge(struct rb_root *root,
}
return sum;
}
-EXPORT_SYMBOL(inet_frag_rbtree_purge);
+
+void inet_frag_queue_flush(struct inet_frag_queue *q,
+ enum skb_drop_reason reason)
+{
+ unsigned int sum;
+
+ reason = reason ?: SKB_DROP_REASON_FRAG_REASM_TIMEOUT;
+ sum = inet_frag_rbtree_purge(&q->rb_fragments, reason);
+ sub_frag_mem_limit(q->fqdir, sum);
+}
+EXPORT_SYMBOL(inet_frag_queue_flush);

void inet_frag_destroy(struct inet_frag_queue *q)
{
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 183856b0b740..124c0d64d420 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -148,11 +148,6 @@ static void ip_expire(struct timer_list *t)
net = qp->q.fqdir->net;

rcu_read_lock();
-
- /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */
- if (READ_ONCE(qp->q.fqdir->dead))
- goto out_rcu_unlock;
-
spin_lock(&qp->q.lock);

if (qp->q.flags & INET_FRAG_COMPLETE)
@@ -160,6 +155,13 @@ static void ip_expire(struct timer_list *t)

qp->q.flags |= INET_FRAG_DROP;
ipq_kill(qp);
+
+ /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */
+ if (READ_ONCE(qp->q.fqdir->dead)) {
+ inet_frag_queue_flush(&qp->q, 0);
+ goto out;
+ }
+
__IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS);
__IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT);

@@ -253,16 +255,12 @@ static int ip_frag_too_far(struct ipq *qp)

static int ip_frag_reinit(struct ipq *qp)
{
- unsigned int sum_truesize = 0;
-
if (!mod_timer(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) {
refcount_inc(&qp->q.refcnt);
return -ETIMEDOUT;
}

- sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments,
- SKB_DROP_REASON_FRAG_TOO_FAR);
- sub_frag_mem_limit(qp->q.fqdir, sum_truesize);
+ inet_frag_queue_flush(&qp->q, SKB_DROP_REASON_FRAG_TOO_FAR);

qp->q.flags = 0;
qp->q.len = 0;
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index 507f2f9ec400..d0ceb86e1687 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -210,7 +210,7 @@ EXPORT_SYMBOL_GPL(iptunnel_handle_offloads);
*/
static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu)
{
- const struct iphdr *iph = ip_hdr(skb);
+ const struct iphdr *iph;
struct icmphdr *icmph;
struct iphdr *niph;
struct ethhdr eh;
@@ -224,7 +224,6 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu)

skb_copy_bits(skb, skb_mac_offset(skb), &eh, ETH_HLEN);
pskb_pull(skb, ETH_HLEN);
- skb_reset_network_header(skb);

err = pskb_trim(skb, 576 - sizeof(*niph) - sizeof(*icmph));
if (err)
@@ -234,7 +233,7 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu)
err = skb_cow(skb, sizeof(*niph) + sizeof(*icmph) + ETH_HLEN);
if (err)
return err;
-
+ iph = ip_hdr(skb);
icmph = skb_push(skb, sizeof(*icmph));
*icmph = (struct icmphdr) {
.type = ICMP_DEST_UNREACH,
@@ -279,7 +278,6 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu)
*/
static int iptunnel_pmtud_check_icmp(struct sk_buff *skb, int mtu)
{
- const struct icmphdr *icmph = icmp_hdr(skb);
const struct iphdr *iph = ip_hdr(skb);

if (mtu < 576 || iph->frag_off != htons(IP_DF))
@@ -290,9 +288,17 @@ static int iptunnel_pmtud_check_icmp(struct sk_buff *skb, int mtu)
ipv4_is_lbcast(iph->saddr) || ipv4_is_multicast(iph->saddr))
return 0;

- if (iph->protocol == IPPROTO_ICMP && icmp_is_err(icmph->type))
- return 0;
+ if (iph->protocol == IPPROTO_ICMP) {
+ const struct icmphdr *icmph;

+ if (!pskb_network_may_pull(skb, iph->ihl * 4 +
+ offsetofend(struct icmphdr, type)))
+ return 0;
+ iph = ip_hdr(skb);
+ icmph = (void *)iph + iph->ihl * 4;
+ if (icmp_is_err(icmph->type))
+ return 0;
+ }
return iptunnel_pmtud_build_icmp(skb, mtu);
}

@@ -306,7 +312,7 @@ static int iptunnel_pmtud_check_icmp(struct sk_buff *skb, int mtu)
*/
static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu)
{
- const struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ const struct ipv6hdr *ip6h;
struct icmp6hdr *icmp6h;
struct ipv6hdr *nip6h;
struct ethhdr eh;
@@ -321,7 +327,6 @@ static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu)

skb_copy_bits(skb, skb_mac_offset(skb), &eh, ETH_HLEN);
pskb_pull(skb, ETH_HLEN);
- skb_reset_network_header(skb);

err = pskb_trim(skb, IPV6_MIN_MTU - sizeof(*nip6h) - sizeof(*icmp6h));
if (err)
@@ -332,6 +337,7 @@ static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu)
if (err)
return err;

+ ip6h = ipv6_hdr(skb);
icmp6h = skb_push(skb, sizeof(*icmp6h));
*icmp6h = (struct icmp6hdr) {
.icmp6_type = ICMPV6_PKT_TOOBIG,
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 8d411cce0aed..35a6e7d8f52f 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -1630,10 +1630,10 @@ static __net_exit void ipv4_sysctl_exit_net(struct net *net)
{
const struct ctl_table *table;

- kfree(net->ipv4.sysctl_local_reserved_ports);
table = net->ipv4.ipv4_hdr->ctl_table_arg;
unregister_net_sysctl_table(net->ipv4.ipv4_hdr);
kfree(table);
+ kfree(net->ipv4.sysctl_local_reserved_ports);
}

static __net_initdata struct pernet_operations ipv4_sysctl_ops = {
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index bd9ec8000f0b..bf4e11614af2 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -337,7 +337,7 @@ static void ah6_output_done(void *data, int err)
ah6_restore_hdrs(top_iph, iph_ext, extlen);

kfree(AH_SKB_CB(skb)->tmp);
- xfrm_output_resume(skb->sk, skb, err);
+ xfrm_output_resume(skb_to_full_sk(skb), skb, err);
}

static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 9a83f658cd89..9bcec0828fe8 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -617,6 +617,18 @@ void ip6_datagram_recv_common_ctl(struct sock *sk, struct msghdr *msg,
}
}

+static u16 ipv6_get_exthdr_len(const struct sk_buff *skb, const u8 *ptr)
+{
+ u16 len;
+
+ if (ptr + 2 > skb_tail_pointer(skb))
+ return 0;
+
+ len = (ptr[1] + 1) << 3;
+
+ return (len <= skb_tail_pointer(skb) - ptr) ? len : 0;
+}
+
void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
struct sk_buff *skb)
{
@@ -643,7 +655,10 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
/* HbH is allowed only once */
if (np->rxopt.bits.hopopts && (opt->flags & IP6SKB_HOPBYHOP)) {
u8 *ptr = nh + sizeof(struct ipv6hdr);
- put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
+ u16 len = ipv6_get_exthdr_len(skb, ptr);
+
+ if (len)
+ put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, len, ptr);
}

if (opt->lastopt &&
@@ -664,26 +679,37 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
unsigned int len;
u8 *ptr = nh + off;

+ if (ptr + 2 > skb_tail_pointer(skb))
+ return;
+
switch (nexthdr) {
case IPPROTO_DSTOPTS:
nexthdr = ptr[0];
- len = (ptr[1] + 1) << 3;
+ len = ipv6_get_exthdr_len(skb, ptr);
+ if (!len)
+ return;
if (np->rxopt.bits.dstopts)
put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, len, ptr);
break;
case IPPROTO_ROUTING:
nexthdr = ptr[0];
- len = (ptr[1] + 1) << 3;
+ len = ipv6_get_exthdr_len(skb, ptr);
+ if (!len)
+ return;
if (np->rxopt.bits.srcrt)
put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, len, ptr);
break;
case IPPROTO_AH:
nexthdr = ptr[0];
len = (ptr[1] + 2) << 2;
+ if (ptr + len > skb_tail_pointer(skb))
+ return;
break;
default:
nexthdr = ptr[0];
- len = (ptr[1] + 1) << 3;
+ len = ipv6_get_exthdr_len(skb, ptr);
+ if (!len)
+ return;
break;
}

@@ -705,19 +731,31 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
}
if (np->rxopt.bits.ohopopts && (opt->flags & IP6SKB_HOPBYHOP)) {
u8 *ptr = nh + sizeof(struct ipv6hdr);
- put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr);
+ u16 len = ipv6_get_exthdr_len(skb, ptr);
+
+ if (len)
+ put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, len, ptr);
}
if (np->rxopt.bits.odstopts && opt->dst0) {
u8 *ptr = nh + opt->dst0;
- put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
+ u16 len = ipv6_get_exthdr_len(skb, ptr);
+
+ if (len)
+ put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, len, ptr);
}
if (np->rxopt.bits.osrcrt && opt->srcrt) {
struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(nh + opt->srcrt);
- put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
+ u16 len = ipv6_get_exthdr_len(skb, (u8 *)rthdr);
+
+ if (len)
+ put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, len, rthdr);
}
if (np->rxopt.bits.odstopts && opt->dst1) {
u8 *ptr = nh + opt->dst1;
- put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
+ u16 len = ipv6_get_exthdr_len(skb, ptr);
+
+ if (len)
+ put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, len, ptr);
}
if (np->rxopt.bits.rxorigdstaddr) {
struct sockaddr_in6 sin6;
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index a797d5740d9b..80981596236a 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -448,8 +448,8 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info
return err;
}

- if (ALIGN(tailen, L1_CACHE_BYTES) > PAGE_SIZE ||
- ALIGN(skb->data_len, L1_CACHE_BYTES) > PAGE_SIZE)
+ if (ALIGN(skb->data_len + tailen, L1_CACHE_BYTES) >
+ PAGE_SIZE)
goto cow;

if (!skb_cloned(skb)) {
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 5ef6fbc66beb..e91afe5ec0b5 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -184,6 +184,8 @@ static bool ip6_parse_tlv(bool hopbyhop,
case IPV6_TLV_JUMBO:
if (!ipv6_hop_jumbo(skb, off))
return false;
+
+ nh = skb_network_header(skb);
break;
case IPV6_TLV_CALIPSO:
if (!ipv6_hop_calipso(skb, off))
@@ -201,6 +203,8 @@ static bool ip6_parse_tlv(bool hopbyhop,
case IPV6_TLV_HAO:
if (!ipv6_dest_hao(skb, off))
return false;
+
+ nh = skb_network_header(skb);
break;
#endif
default:
@@ -546,7 +550,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
* unsigned char which is segments_left field. Should not be
* higher than that.
*/
- if (r || (n + 1) > 255) {
+ if (r || (n + 1) > 127) {
kfree_skb(skb);
return -1;
}
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index fd6f76e36e80..2ac88593a954 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -722,10 +722,11 @@ vti6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p,
static int vti6_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p,
bool keep_mtu)
{
- struct net *net = dev_net(t->dev);
- struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+ struct net *net = t->net;
+ struct vti6_net *ip6n;
int err;

+ ip6n = net_generic(net, vti6_net_id);
vti6_tnl_unlink(ip6n, t);
synchronize_net();
err = vti6_tnl_change(t, p, keep_mtu);
@@ -834,17 +835,24 @@ vti6_siocdevprivate(struct net_device *dev, struct ifreq *ifr, void __user *data
if (p.proto != IPPROTO_IPV6 && p.proto != 0)
break;
vti6_parm_from_user(&p1, &p);
- t = vti6_locate(net, &p1, cmd == SIOCADDTUNNEL);
if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) {
+ struct ip6_tnl *self = netdev_priv(dev);
+
+ err = -EPERM;
+ if (!ns_capable(self->net->user_ns, CAP_NET_ADMIN))
+ break;
+ t = vti6_locate(self->net, &p1, false);
if (t) {
if (t->dev != dev) {
err = -EEXIST;
break;
}
} else
- t = netdev_priv(dev);
+ t = self;

err = vti6_update(t, &p1, false);
+ } else {
+ t = vti6_locate(net, &p1, cmd == SIOCADDTUNNEL);
}
if (t) {
err = 0;
@@ -1029,11 +1037,12 @@ static int vti6_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[],
struct netlink_ext_ack *extack)
{
- struct ip6_tnl *t;
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct net *net = t->net;
struct __ip6_tnl_parm p;
- struct net *net = dev_net(dev);
- struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+ struct vti6_net *ip6n;

+ ip6n = net_generic(net, vti6_net_id);
if (dev == ip6n->fb_tnl_dev)
return -EINVAL;

diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 31c9e3b73f2d..9e7470e81544 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -482,6 +482,9 @@ void fib6_select_path(const struct net *net, struct fib6_result *res,
const struct fib6_nh *nh = sibling->fib6_nh;
int nh_upper_bound;

+ if (!READ_ONCE(first->fib6_nsiblings))
+ break;
+
nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound);
if (hash > nh_upper_bound)
continue;
@@ -5812,6 +5815,8 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,

goto nla_put_failure;
}
+ if (!READ_ONCE(rt->fib6_nsiblings))
+ break;
}

rcu_read_unlock();
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index 7929df08d4e0..1a0b41fcea81 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -1537,7 +1537,7 @@ static int iucv_sock_getsockopt(struct socket *sock, int level, int optname,
struct sock *sk = sock->sk;
struct iucv_sock *iucv = iucv_sk(sk);
unsigned int val;
- int len;
+ int len, rc;

if (level != SOL_IUCV)
return -ENOPROTOOPT;
@@ -1550,26 +1550,34 @@ static int iucv_sock_getsockopt(struct socket *sock, int level, int optname,

len = min_t(unsigned int, len, sizeof(int));

+ rc = 0;
+
+ lock_sock(sk);
switch (optname) {
case SO_IPRMDATA_MSG:
val = (iucv->flags & IUCV_IPRMDATA) ? 1 : 0;
break;
case SO_MSGLIMIT:
- lock_sock(sk);
val = (iucv->path != NULL) ? iucv->path->msglim /* connected */
: iucv->msglimit; /* default */
- release_sock(sk);
break;
case SO_MSGSIZE:
- if (sk->sk_state == IUCV_OPEN)
- return -EBADFD;
+ if (sk->sk_state == IUCV_OPEN) {
+ rc = -EBADFD;
+ break;
+ }
val = (iucv->hs_dev) ? iucv->hs_dev->mtu -
sizeof(struct af_iucv_trans_hdr) - ETH_HLEN :
0x7fffffff;
break;
default:
- return -ENOPROTOOPT;
+ rc = -ENOPROTOOPT;
+ break;
}
+ release_sock(sk);
+
+ if (rc)
+ return rc;

if (put_user(len, optlen))
return -EFAULT;
diff --git a/net/key/af_key.c b/net/key/af_key.c
index f4ad0239b720..a176bcb3d89a 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -3564,7 +3564,7 @@ static int set_ipsecrequest(struct sk_buff *skb,
#ifdef CONFIG_NET_KEY_MIGRATE
static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
const struct xfrm_migrate *m, int num_bundles,
- const struct xfrm_kmaddress *k,
+ const struct xfrm_kmaddress *k, struct net *net,
const struct xfrm_encap_tmpl *encap)
{
int i;
@@ -3669,7 +3669,7 @@ static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
}

/* broadcast migrate message to sockets */
- pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, &init_net);
+ pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, net);

return 0;

@@ -3680,7 +3680,7 @@ static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
#else
static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
const struct xfrm_migrate *m, int num_bundles,
- const struct xfrm_kmaddress *k,
+ const struct xfrm_kmaddress *k, struct net *net,
const struct xfrm_encap_tmpl *encap)
{
return -ENOPROTOOPT;
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 458570f388b1..7cdfab3a7809 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -441,12 +441,13 @@ struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net,
idr_for_each_entry_ul(&pn->l2tp_tunnel_idr, tunnel, tmp, tunnel_id) {
if (tunnel) {
list_for_each_entry_rcu(session, &tunnel->session_list, list) {
- if (!strcmp(session->ifname, ifname)) {
- refcount_inc(&session->ref_count);
- rcu_read_unlock_bh();
+ if (strcmp(session->ifname, ifname))
+ continue;
+ if (!refcount_inc_not_zero(&session->ref_count))
+ continue;
+ rcu_read_unlock_bh();

- return session;
- }
+ return session;
}
}
}
diff --git a/net/mctp/device.c b/net/mctp/device.c
index 8d1386601bbe..67576cb2728e 100644
--- a/net/mctp/device.c
+++ b/net/mctp/device.c
@@ -70,6 +70,7 @@ static int mctp_fill_addrinfo(struct sk_buff *skb,
return -EMSGSIZE;

hdr = nlmsg_data(nlh);
+ memset(hdr, 0, sizeof(*hdr));
hdr->ifa_family = AF_MCTP;
hdr->ifa_prefixlen = 0;
hdr->ifa_flags = 0;
diff --git a/net/mctp/neigh.c b/net/mctp/neigh.c
index 590f642413e4..c0151a69d2b7 100644
--- a/net/mctp/neigh.c
+++ b/net/mctp/neigh.c
@@ -218,6 +218,7 @@ static int mctp_fill_neigh(struct sk_buff *skb, u32 portid, u32 seq, int event,
return -EMSGSIZE;

hdr = nlmsg_data(nlh);
+ memset(hdr, 0, sizeof(*hdr));
hdr->ndm_family = AF_MCTP;
hdr->ndm_ifindex = dev->ifindex;
hdr->ndm_state = 0; // TODO other state bits?
diff --git a/net/mctp/route.c b/net/mctp/route.c
index ccba2abbbbfb..35a0681123a3 100644
--- a/net/mctp/route.c
+++ b/net/mctp/route.c
@@ -1405,6 +1405,7 @@ static int mctp_fill_rtinfo(struct sk_buff *skb, struct mctp_route *rt,
return -EMSGSIZE;

hdr = nlmsg_data(nlh);
+ memset(hdr, 0, sizeof(*hdr));
hdr->rtm_family = AF_MCTP;

/* we use the _len fields as a number of EIDs, rather than
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 8d2c27c43ee0..b601dab95a42 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -334,6 +334,7 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, const struct sk_buff *skb,
struct mptcp_addr_info *addr, bool *echo,
bool *drop_other_suboptions)
{
+ bool skip_add_addr = false;
int ret = false;
u8 add_addr;
u8 family;
@@ -355,24 +356,49 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, const struct sk_buff *skb,
}

*echo = mptcp_pm_should_add_signal_echo(msk);
- port = !!(*echo ? msk->pm.remote.port : msk->pm.local.port);
-
- family = *echo ? msk->pm.remote.family : msk->pm.local.family;
- if (remaining < mptcp_add_addr_len(family, *echo, port))
- goto out_unlock;
-
if (*echo) {
*addr = msk->pm.remote;
add_addr = msk->pm.addr_signal & ~BIT(MPTCP_ADD_ADDR_ECHO);
+ port = !!msk->pm.remote.port;
+ family = msk->pm.remote.family;
} else {
*addr = msk->pm.local;
add_addr = msk->pm.addr_signal & ~BIT(MPTCP_ADD_ADDR_SIGNAL);
+ port = !!msk->pm.local.port;
+ family = msk->pm.local.family;
}
- WRITE_ONCE(msk->pm.addr_signal, add_addr);
+
+ if (remaining < mptcp_add_addr_len(family, *echo, port)) {
+ struct net *net = sock_net((struct sock *)msk);
+
+ if (!*drop_other_suboptions)
+ goto out_unlock;
+
+ if (*echo) {
+ MPTCP_INC_STATS(net, MPTCP_MIB_ECHOADDTXDROP);
+ } else {
+ skip_add_addr = true;
+ MPTCP_INC_STATS(net, MPTCP_MIB_ADDADDRTXDROP);
+ }
+ goto drop_signal_mark;
+ }
+
ret = true;

+drop_signal_mark:
+ WRITE_ONCE(msk->pm.addr_signal, add_addr);
+
out_unlock:
spin_unlock_bh(&msk->pm.lock);
+
+ /* On pure-ACK option-space exhaustion, stop retrying this ADD_ADDR:
+ * clear the signal bit, cancel the matching retransmission timer, and
+ * let the PM state machine progress.
+ */
+ if (skip_add_addr) {
+ mptcp_pm_del_add_timer(msk, addr, true);
+ mptcp_pm_subflow_established(msk);
+ }
return ret;
}

diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
index 4ff6721ad5c7..8159ffb8466b 100644
--- a/net/mptcp/pm_netlink.c
+++ b/net/mptcp/pm_netlink.c
@@ -320,7 +320,13 @@ static void mptcp_pm_add_timer(struct timer_list *timer)

spin_lock_bh(&msk->pm.lock);

- if (!mptcp_pm_should_add_signal_addr(msk)) {
+ /* The cancel path (mptcp_pm_del_add_timer()) can race with this
+ * callback. Once cancel updates retrans_times to MAX, suppress further
+ * retransmissions here. If this callback acquires pm.lock first, one
+ * final transmit attempt is still possible.
+ */
+ if (entry->retrans_times < ADD_ADDR_RETRANS_MAX &&
+ !mptcp_pm_should_add_signal_addr(msk)) {
pr_debug("retransmit ADD_ADDR id=%d\n", entry->addr.id);
mptcp_pm_announce_addr(msk, &entry->addr, false);
mptcp_pm_add_addr_send_ack(msk);
@@ -368,8 +374,12 @@ mptcp_pm_del_add_timer(struct mptcp_sock *msk,
/* Note: entry might have been removed by another thread.
* We hold rcu_read_lock() to ensure it is not freed under us.
*/
- if (stop_timer)
- sk_stop_timer_sync(sk, &entry->add_timer);
+ if (stop_timer) {
+ if (check_id)
+ sk_stop_timer(sk, &entry->add_timer);
+ else
+ sk_stop_timer_sync(sk, &entry->add_timer);
+ }

rcu_read_unlock();
return entry;
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index c1b1fb0fe8bc..38550c44a201 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -321,7 +321,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
mptcp_set_owner_r(skb, sk);
}

-static bool mptcp_rmem_schedule(struct sock *sk, struct sock *ssk, int size)
+static bool mptcp_rmem_schedule(struct sock *sk, int size)
{
struct mptcp_sock *msk = mptcp_sk(sk);
int amt, amount;
@@ -339,27 +339,11 @@ static bool mptcp_rmem_schedule(struct sock *sk, struct sock *ssk, int size)
return true;
}

-static bool __mptcp_move_skb(struct mptcp_sock *msk, struct sock *ssk,
- struct sk_buff *skb, unsigned int offset,
- size_t copy_len)
+static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset,
+ int copy_len)
{
- struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
- struct sock *sk = (struct sock *)msk;
- struct sk_buff *tail;
- bool has_rxtstamp;
-
- __skb_unlink(skb, &ssk->sk_receive_queue);
-
- skb_ext_reset(skb);
- skb_orphan(skb);
-
- /* try to fetch required memory from subflow */
- if (!mptcp_rmem_schedule(sk, ssk, skb->truesize)) {
- MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED);
- goto drop;
- }
-
- has_rxtstamp = TCP_SKB_CB(skb)->has_rxtstamp;
+ const struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
+ bool has_rxtstamp = TCP_SKB_CB(skb)->has_rxtstamp;

/* the skb map_seq accounts for the skb offset:
* mptcp_subflow_get_mapped_dsn() is based on the current tp->copied_seq
@@ -371,6 +355,25 @@ static bool __mptcp_move_skb(struct mptcp_sock *msk, struct sock *ssk,
MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
MPTCP_SKB_CB(skb)->cant_coalesce = 0;

+ __skb_unlink(skb, &ssk->sk_receive_queue);
+
+ skb_ext_reset(skb);
+ skb_dst_drop(skb);
+}
+
+static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
+{
+ u64 copy_len = MPTCP_SKB_CB(skb)->end_seq - MPTCP_SKB_CB(skb)->map_seq;
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct sk_buff *tail;
+
+ /* try to fetch required memory from subflow */
+ if (!mptcp_rmem_schedule(sk, skb->truesize)) {
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED);
+ mptcp_drop(sk, skb);
+ return false;
+ }
+
if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) {
/* in sequence */
msk->bytes_received += copy_len;
@@ -387,13 +390,26 @@ static bool __mptcp_move_skb(struct mptcp_sock *msk, struct sock *ssk,
return false;
}

- /* old data, keep it simple and drop the whole pkt, sender
- * will retransmit as needed, if needed.
+ /* Completely old data? */
+ if (!after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
+ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
+ mptcp_drop(sk, skb);
+ return false;
+ }
+
+ /* Partial packet: map_seq < ack_seq < end_seq.
+ * Skip the already-acked bytes and enqueue the new data.
*/
- MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
-drop:
- mptcp_drop(sk, skb);
- return false;
+ copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq;
+ MPTCP_SKB_CB(skb)->offset += msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq;
+ MPTCP_SKB_CB(skb)->map_seq += msk->ack_seq -
+ MPTCP_SKB_CB(skb)->map_seq;
+ msk->bytes_received += copy_len;
+ WRITE_ONCE(msk->ack_seq, msk->ack_seq + copy_len);
+
+ skb_set_owner_r(skb, sk);
+ __skb_queue_tail(&sk->sk_receive_queue, skb);
+ return true;
}

static void mptcp_stop_rtx_timer(struct sock *sk)
@@ -720,7 +736,9 @@ static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk,
if (tp->urg_data)
done = true;

- if (__mptcp_move_skb(msk, ssk, skb, offset, len))
+ mptcp_init_skb(ssk, skb, offset, len);
+ skb_orphan(skb);
+ if (__mptcp_move_skb(sk, skb))
moved += len;
seq += len;

@@ -873,10 +891,10 @@ void mptcp_data_ready(struct sock *sk, struct sock *ssk)
int sk_rbuf, ssk_rbuf;

/* The peer can send data while we are shutting down this
- * subflow at msk destruction time, but we must avoid enqueuing
+ * subflow at subflow destruction time, but we must avoid enqueuing
* more data to the msk receive queue
*/
- if (unlikely(subflow->disposable))
+ if (unlikely(subflow->closing))
return;

ssk_rbuf = READ_ONCE(ssk->sk_rcvbuf);
@@ -2510,6 +2528,13 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
struct mptcp_sock *msk = mptcp_sk(sk);
bool dispose_it, need_push = false;

+ /* Do not pass RX data to the msk, even if the subflow socket is not
+ * going to be freed (i.e. even for the first subflow on graceful
+ * subflow close.
+ */
+ lock_sock_nested(ssk, SINGLE_DEPTH_NESTING);
+ subflow->closing = 1;
+
/* If the first subflow moved to a close state before accept, e.g. due
* to an incoming reset or listener shutdown, the subflow socket is
* already deleted by inet_child_forget() and the mptcp socket can't
@@ -2520,7 +2545,6 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
/* ensure later check in mptcp_worker() will dispose the msk */
sock_set_flag(sk, SOCK_DEAD);
mptcp_set_close_tout(sk, tcp_jiffies32 - (mptcp_close_timeout(sk) + 1));
- lock_sock_nested(ssk, SINGLE_DEPTH_NESTING);
mptcp_subflow_drop_ctx(ssk);
goto out_release;
}
@@ -2529,8 +2553,6 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
if (dispose_it)
list_del(&subflow->node);

- lock_sock_nested(ssk, SINGLE_DEPTH_NESTING);
-
if (subflow->send_fastclose && ssk->sk_state != TCP_CLOSE)
tcp_set_state(ssk, TCP_CLOSE);

@@ -3369,6 +3391,10 @@ static int mptcp_disconnect(struct sock *sk, int flags)
msk->rcvspace_init = 0;
msk->fastclosing = 0;

+ /* for fallback's sake */
+ WRITE_ONCE(msk->ack_seq, 0);
+ atomic64_set(&msk->rcv_wnd_sent, 0);
+
WRITE_ONCE(sk->sk_shutdown, 0);
sk_error_report(sk);
return 0;
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 9ed9cb36e9bb..8ba3b0244bad 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -537,12 +537,13 @@ struct mptcp_subflow_context {
send_infinite_map : 1,
remote_key_valid : 1, /* received the peer key from */
disposable : 1, /* ctx can be free at ulp release time */
+ closing : 1, /* must not pass rx data to msk anymore */
stale : 1, /* unable to snd/rcv data, do not use for xmit */
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 */
- __unused : 9;
+ __unused : 8;
bool data_avail;
bool scheduled;
bool pm_listener; /* a listener managed by the kernel PM? */
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 10e945f5fa0f..26ea58691f79 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -490,6 +490,9 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
mptcp_crypto_key_sha(subflow->remote_key, NULL, &subflow->iasn);
subflow->iasn++;

+ /* for fallback's sake */
+ subflow->map_seq = subflow->iasn;
+
WRITE_ONCE(msk->remote_key, subflow->remote_key);
WRITE_ONCE(msk->ack_seq, subflow->iasn);
WRITE_ONCE(msk->can_ack, true);
@@ -1415,9 +1418,12 @@ static bool subflow_check_data_avail(struct sock *ssk)

skb = skb_peek(&ssk->sk_receive_queue);
subflow->map_valid = 1;
- subflow->map_seq = READ_ONCE(msk->ack_seq);
subflow->map_data_len = skb->len;
subflow->map_subflow_seq = tcp_sk(ssk)->copied_seq - subflow->ssn_offset;
+ subflow->map_seq = __mptcp_expand_seq(subflow->map_seq,
+ subflow->iasn +
+ TCP_SKB_CB(skb)->seq -
+ subflow->ssn_offset - 1);
WRITE_ONCE(subflow->data_avail, true);
return true;
}
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index b67426c2189b..e99ab1e88e9f 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -1221,7 +1221,8 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
new_state = old_state;
}
if (((test_bit(IPS_SEEN_REPLY_BIT, &ct->status)
- && ct->proto.tcp.last_index == TCP_SYN_SET)
+ && ct->proto.tcp.last_index == TCP_SYN_SET
+ && ct->proto.tcp.last_dir != dir)
|| (!test_bit(IPS_ASSURED_BIT, &ct->status)
&& ct->proto.tcp.last_index == TCP_ACK_SET))
&& ntohl(th->ack_seq) == ct->proto.tcp.last_end) {
diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c
index 3fa3f5dfb264..6a851ac4dd04 100644
--- a/net/netfilter/nf_synproxy_core.c
+++ b/net/netfilter/nf_synproxy_core.c
@@ -199,6 +199,8 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff,
if (skb_ensure_writable(skb, optend))
return 0;

+ th = (struct tcphdr *)(skb->data + protoff);
+
while (optoff < optend) {
unsigned char *op = skb->data + optoff;

diff --git a/net/netfilter/xt_cpu.c b/net/netfilter/xt_cpu.c
index 3bdc302a0f91..9cb259902a58 100644
--- a/net/netfilter/xt_cpu.c
+++ b/net/netfilter/xt_cpu.c
@@ -34,7 +34,7 @@ static bool cpu_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_cpu_info *info = par->matchinfo;

- return (info->cpu == smp_processor_id()) ^ info->invert;
+ return (info->cpu == raw_smp_processor_id()) ^ info->invert;
}

static struct xt_match cpu_mt_reg __read_mostly = {
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 8b060465a2be..e250d4a3d030 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1477,9 +1477,14 @@ static void do_one_broadcast(struct sock *sk,
p->skb2 = NULL;
goto out;
}
- NETLINK_CB(p->skb2).nsid = peernet2id(sock_net(sk), p->net);
- if (NETLINK_CB(p->skb2).nsid != NETNSA_NSID_NOT_ASSIGNED)
- NETLINK_CB(p->skb2).nsid_is_set = true;
+
+ NETLINK_CB(p->skb2).nsid_is_set = false;
+ if (!net_eq(sock_net(sk), p->net)) {
+ NETLINK_CB(p->skb2).nsid = peernet2id(sock_net(sk), p->net);
+ if (NETLINK_CB(p->skb2).nsid != NETNSA_NSID_NOT_ASSIGNED)
+ NETLINK_CB(p->skb2).nsid_is_set = true;
+ }
+
val = netlink_broadcast_deliver(sk, p->skb2);
if (val < 0) {
netlink_overrun(sk);
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index ceb87db57cdb..7fc8f20e0d54 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -861,6 +861,11 @@ static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
struct sk_buff *frag_skb;
int msg_len;

+ if (!pskb_may_pull(skb, NFC_HCI_HCP_PACKET_HEADER_LEN)) {
+ kfree_skb(skb);
+ return;
+ }
+
packet = (struct hcp_packet *)skb->data;
if ((packet->header & ~NFC_HCI_FRAGMENT) == 0) {
skb_queue_tail(&hdev->rx_hcp_frags, skb);
@@ -904,6 +909,11 @@ static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
* unblock waiting cmd context. Otherwise, enqueue to dispatch
* in separate context where handler can also execute command.
*/
+ if (!pskb_may_pull(hcp_skb, NFC_HCI_HCP_HEADER_LEN)) {
+ kfree_skb(hcp_skb);
+ return;
+ }
+
packet = (struct hcp_packet *)hcp_skb->data;
type = HCP_MSG_GET_TYPE(packet->message.header);
if (type == NFC_HCI_HCP_RESPONSE) {
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index d9562840fa18..62b0f2d6686e 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -1216,6 +1216,15 @@ static void nfc_llcp_recv_cc(struct nfc_llcp_local *local,

sk = &llcp_sock->sk;

+ lock_sock(sk);
+
+ /* Check if socket was destroyed whilst waiting for the lock */
+ if (!sk_hashed(sk)) {
+ release_sock(sk);
+ nfc_llcp_sock_put(llcp_sock);
+ return;
+ }
+
/* Unlink from connecting and link to the client array */
nfc_llcp_sock_unlink(&local->connecting_sockets, sk);
nfc_llcp_sock_link(&local->sockets, sk);
@@ -1227,6 +1236,8 @@ static void nfc_llcp_recv_cc(struct nfc_llcp_local *local,
sk->sk_state = LLCP_CONNECTED;
sk->sk_state_change(sk);

+ release_sock(sk);
+
nfc_llcp_sock_put(llcp_sock);
}

diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index 57a2f97004e1..915929cd724f 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -633,6 +633,8 @@ static int llcp_sock_release(struct socket *sock)

if (sock->type == SOCK_RAW)
nfc_llcp_sock_unlink(&local->raw_sockets, sk);
+ else if (sk->sk_state == LLCP_CONNECTING)
+ nfc_llcp_sock_unlink(&local->connecting_sockets, sk);
else
nfc_llcp_sock_unlink(&local->sockets, sk);

diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c
index 082ab66f120b..7f3b4fffb3d6 100644
--- a/net/nfc/nci/hci.c
+++ b/net/nfc/nci/hci.c
@@ -439,6 +439,11 @@ void nci_hci_data_received_cb(void *context,
return;
}

+ if (!pskb_may_pull(skb, NCI_HCI_HCP_PACKET_HEADER_LEN)) {
+ kfree_skb(skb);
+ return;
+ }
+
packet = (struct nci_hcp_packet *)skb->data;
if ((packet->header & ~NCI_HCI_FRAGMENT) == 0) {
skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb);
@@ -482,6 +487,11 @@ void nci_hci_data_received_cb(void *context,
* unblock waiting cmd context. Otherwise, enqueue to dispatch
* in separate context where handler can also execute command.
*/
+ if (!pskb_may_pull(hcp_skb, NCI_HCI_HCP_HEADER_LEN)) {
+ kfree_skb(hcp_skb);
+ return;
+ }
+
packet = (struct nci_hcp_packet *)hcp_skb->data;
type = NCI_HCP_MSG_GET_TYPE(packet->message.header);
if (type == NCI_HCI_HCP_RESPONSE) {
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 63cd5217b4ee..61dd1298124f 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -203,8 +203,6 @@ struct rxrpc_skb_priv {
struct {
u16 offset; /* Offset of data */
u16 len; /* Length of data */
- u8 flags;
-#define RXRPC_RX_VERIFIED 0x01
};
struct {
rxrpc_seq_t first_ack; /* First packet in acks table */
@@ -272,8 +270,9 @@ struct rxrpc_security {
struct sk_buff *);

/* verify a response */
- int (*verify_response)(struct rxrpc_connection *,
- struct sk_buff *);
+ int (*verify_response)(struct rxrpc_connection *conn,
+ struct sk_buff *response_skb,
+ void *response, unsigned int len);

/* clear connection security */
void (*clear)(struct rxrpc_connection *);
@@ -686,6 +685,11 @@ struct rxrpc_call {
/* Received data tracking */
struct sk_buff_head recvmsg_queue; /* Queue of packets ready for recvmsg() */
struct sk_buff_head rx_oos_queue; /* Queue of out of sequence packets */
+ void *rx_dec_buffer; /* Decryption buffer */
+ unsigned short rx_dec_bsize; /* rx_dec_buffer size */
+ unsigned short rx_dec_offset; /* Decrypted packet data offset */
+ unsigned short rx_dec_len; /* Decrypted packet data len */
+ rxrpc_seq_t rx_dec_seq; /* Packet in decryption buffer */

rxrpc_seq_t rx_highest_seq; /* Higest sequence number received */
rxrpc_seq_t rx_consumed; /* Highest packet consumed */
diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
index fda16b39e8e7..7bbb68504766 100644
--- a/net/rxrpc/call_event.c
+++ b/net/rxrpc/call_event.c
@@ -342,31 +342,8 @@ bool rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
if (skb && skb->mark == RXRPC_SKB_MARK_ERROR)
goto out;

- if (skb) {
- struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
-
- if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA &&
- sp->hdr.securityIndex != 0 &&
- (skb_cloned(skb) ||
- skb_has_frag_list(skb) ||
- skb_has_shared_frag(skb))) {
- /* Unshare the packet so that it can be modified for
- * in-place decryption.
- */
- struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
-
- if (nskb) {
- rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
- rxrpc_input_call_packet(call, nskb);
- rxrpc_free_skb(nskb, rxrpc_skb_put_input);
- } else {
- /* OOM - Drop the packet. */
- rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
- }
- } else {
- rxrpc_input_call_packet(call, skb);
- }
- }
+ if (skb)
+ rxrpc_input_call_packet(call, skb);

/* If we see our async-event poke, check for timeout trippage. */
now = ktime_get_real();
diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c
index 09c54b2e825c..bafffd470c82 100644
--- a/net/rxrpc/call_object.c
+++ b/net/rxrpc/call_object.c
@@ -154,6 +154,7 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp,
spin_lock_init(&call->tx_lock);
refcount_set(&call->ref, 1);
call->debug_id = debug_id;
+ call->rx_pkt_offset = USHRT_MAX;
call->tx_total_len = -1;
call->next_rx_timo = 20 * HZ;
call->next_req_timo = 1 * HZ;
@@ -535,6 +536,7 @@ static void rxrpc_cleanup_ring(struct rxrpc_call *call)
{
rxrpc_purge_queue(&call->recvmsg_queue);
rxrpc_purge_queue(&call->rx_oos_queue);
+ kfree(call->rx_dec_buffer);
}

/*
diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c
index 3a58fb921038..ab66903e4d72 100644
--- a/net/rxrpc/conn_event.c
+++ b/net/rxrpc/conn_event.c
@@ -229,28 +229,22 @@ static void rxrpc_call_is_secure(struct rxrpc_call *call)
static int rxrpc_verify_response(struct rxrpc_connection *conn,
struct sk_buff *skb)
{
+ unsigned int len = skb->len - sizeof(struct rxrpc_wire_header);
+ void *buffer;
int ret;

- if (skb_cloned(skb) || skb_has_frag_list(skb) ||
- skb_has_shared_frag(skb)) {
- /* Copy the packet if shared so that we can do in-place
- * decryption.
- */
- struct sk_buff *nskb = skb_copy(skb, GFP_NOFS);
-
- if (nskb) {
- rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
- ret = conn->security->verify_response(conn, nskb);
- rxrpc_free_skb(nskb, rxrpc_skb_put_response_copy);
- } else {
- /* OOM - Drop the packet. */
- rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
- ret = -ENOMEM;
- }
- } else {
- ret = conn->security->verify_response(conn, skb);
- }
+ buffer = kmalloc(len, GFP_NOFS);
+ if (!buffer)
+ return -ENOMEM;
+
+ ret = skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), buffer, len);
+ if (ret < 0)
+ goto out;
+
+ ret = conn->security->verify_response(conn, skb, buffer, len);

+out:
+ kfree(buffer);
return ret;
}

diff --git a/net/rxrpc/insecure.c b/net/rxrpc/insecure.c
index 6716c021a532..4a3b96aed933 100644
--- a/net/rxrpc/insecure.c
+++ b/net/rxrpc/insecure.c
@@ -29,9 +29,6 @@ static int none_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb)

static int none_verify_packet(struct rxrpc_call *call, struct sk_buff *skb)
{
- struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
-
- sp->flags |= RXRPC_RX_VERIFIED;
return 0;
}

@@ -47,9 +44,10 @@ static int none_respond_to_challenge(struct rxrpc_connection *conn,
}

static int none_verify_response(struct rxrpc_connection *conn,
- struct sk_buff *skb)
+ struct sk_buff *response_skb,
+ void *response, unsigned int len)
{
- return rxrpc_abort_conn(conn, skb, RX_PROTOCOL_ERROR, -EPROTO,
+ return rxrpc_abort_conn(conn, response_skb, RX_PROTOCOL_ERROR, -EPROTO,
rxrpc_eproto_rxnull_response);
}

diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c
index b6e524c065f0..99efe14eb0c9 100644
--- a/net/rxrpc/recvmsg.c
+++ b/net/rxrpc/recvmsg.c
@@ -143,15 +143,52 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call)
}

/*
- * Decrypt and verify a DATA packet.
+ * Decrypt and verify a DATA packet. The content of the packet is pulled out
+ * into a flat buffer rather than decrypting in place in the skbuff. This also
+ * has the advantage of aligning the buffer correctly for the crypto routines.
+ *
+ * We keep track of the sequence number of the packet currently decrypted into
+ * the buffer in ->rx_dec_seq. If MSG_PEEK is used and steps onto a new
+ * packet, subsequent recvmsg() calls will have to go back and re-decrypt the
+ * current packet.
*/
static int rxrpc_verify_data(struct rxrpc_call *call, struct sk_buff *skb)
{
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+ int ret;
+
+ if (sp->len > call->rx_dec_bsize) {
+ /* Make sure we can hold a 1412-byte jumbo subpacket and make
+ * sure that the buffer size is aligned to a crypto blocksize.
+ */
+ size_t size = clamp(round_up(sp->len, 32), 2048, 65535);
+ void *buffer = krealloc(call->rx_dec_buffer, size, GFP_NOFS);
+
+ if (!buffer)
+ return -ENOMEM;
+ call->rx_dec_buffer = buffer;
+ call->rx_dec_bsize = size;
+ }
+
+ ret = -EFAULT;
+ if (skb_copy_bits(skb, sp->offset, call->rx_dec_buffer, sp->len) < 0)
+ goto err;

- if (sp->flags & RXRPC_RX_VERIFIED)
- return 0;
- return call->security->verify_packet(call, skb);
+ call->rx_dec_offset = 0;
+ call->rx_dec_len = sp->len;
+ call->rx_dec_seq = sp->hdr.seq;
+ ret = call->security->verify_packet(call, skb);
+ if (ret < 0)
+ goto err;
+ return 0;
+
+err:
+ kfree(call->rx_dec_buffer);
+ call->rx_dec_buffer = NULL;
+ call->rx_dec_bsize = 0;
+ call->rx_dec_offset = 0;
+ call->rx_dec_len = 0;
+ return ret;
}

/*
@@ -202,17 +239,22 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call,
if (msg)
sock_recv_timestamp(msg, sock->sk, skb);

- if (rx_pkt_offset == 0) {
+ if (call->rx_dec_seq != sp->hdr.seq ||
+ !call->rx_dec_buffer) {
ret2 = rxrpc_verify_data(call, skb);
trace_rxrpc_recvdata(call, rxrpc_recvmsg_next, seq,
- sp->offset, sp->len, ret2);
+ call->rx_dec_offset,
+ call->rx_dec_len, ret2);
if (ret2 < 0) {
kdebug("verify = %d", ret2);
ret = ret2;
goto out;
}
- rx_pkt_offset = sp->offset;
- rx_pkt_len = sp->len;
+ }
+
+ if (rx_pkt_offset == USHRT_MAX) {
+ rx_pkt_offset = call->rx_dec_offset;
+ rx_pkt_len = call->rx_dec_len;
} else {
trace_rxrpc_recvdata(call, rxrpc_recvmsg_cont, seq,
rx_pkt_offset, rx_pkt_len, 0);
@@ -224,10 +266,10 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call,
if (copy > remain)
copy = remain;
if (copy > 0) {
- ret2 = skb_copy_datagram_iter(skb, rx_pkt_offset, iter,
- copy);
- if (ret2 < 0) {
- ret = ret2;
+ ret2 = copy_to_iter(call->rx_dec_buffer + rx_pkt_offset,
+ copy, iter);
+ if (ret2 != copy) {
+ ret = -EFAULT;
goto out;
}

@@ -248,7 +290,7 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call,
/* The whole packet has been transferred. */
if (sp->hdr.flags & RXRPC_LAST_PACKET)
ret = 1;
- rx_pkt_offset = 0;
+ rx_pkt_offset = USHRT_MAX;
rx_pkt_len = 0;

skb = skb_peek_next(skb, &call->recvmsg_queue);
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
index 73bbe8cd391b..c4d946ffc7de 100644
--- a/net/rxrpc/rxkad.c
+++ b/net/rxrpc/rxkad.c
@@ -414,27 +414,25 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb,
rxrpc_seq_t seq,
struct skcipher_request *req)
{
- struct rxkad_level1_hdr sechdr;
+ struct rxkad_level1_hdr *sechdr;
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
struct rxrpc_crypt iv;
- struct scatterlist sg[16];
- u32 data_size, buf;
+ struct scatterlist sg[1];
+ void *data = call->rx_dec_buffer;
+ u32 len = sp->len, data_size, buf;
u16 check;
int ret;

_enter("");

- if (sp->len < 8)
+ if (len < 8)
return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
rxkad_abort_1_short_header);

/* Decrypt the skbuff in-place. TODO: We really want to decrypt
* directly into the target buffer.
*/
- sg_init_table(sg, ARRAY_SIZE(sg));
- ret = skb_to_sgvec(skb, sg, sp->offset, 8);
- if (unlikely(ret < 0))
- return ret;
+ sg_init_one(sg, data, len);

/* start the decryption afresh */
memset(&iv, 0, sizeof(iv));
@@ -448,13 +446,11 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb,
return ret;

/* Extract the decrypted packet length */
- if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0)
- return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
- rxkad_abort_1_short_encdata);
- sp->offset += sizeof(sechdr);
- sp->len -= sizeof(sechdr);
+ sechdr = data;
+ call->rx_dec_offset = sizeof(*sechdr);
+ len -= sizeof(*sechdr);

- buf = ntohl(sechdr.data_size);
+ buf = ntohl(sechdr->data_size);
data_size = buf & 0xffff;

check = buf >> 16;
@@ -463,10 +459,10 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb,
if (check != 0)
return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
rxkad_abort_1_short_check);
- if (data_size > sp->len)
+ if (data_size > len)
return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
rxkad_abort_1_short_data);
- sp->len = data_size;
+ call->rx_dec_len = data_size;

_leave(" = 0 [dlen=%x]", data_size);
return 0;
@@ -480,43 +476,28 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,
struct skcipher_request *req)
{
const struct rxrpc_key_token *token;
- struct rxkad_level2_hdr sechdr;
+ struct rxkad_level2_hdr *sechdr;
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
struct rxrpc_crypt iv;
- struct scatterlist _sg[4], *sg;
- u32 data_size, buf;
+ struct scatterlist sg[1];
+ void *data = call->rx_dec_buffer;
+ u32 len = sp->len, data_size, buf;
u16 check;
- int nsg, ret;
+ int ret;

- _enter(",{%d}", sp->len);
+ _enter(",{%d}", len);

- if (sp->len < 8)
+ if (len < 8)
return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
rxkad_abort_2_short_header);

/* Don't let the crypto algo see a misaligned length. */
- sp->len = round_down(sp->len, 8);
+ len = round_down(len, 8);

- /* Decrypt the skbuff in-place. TODO: We really want to decrypt
- * directly into the target buffer.
+ /* Decrypt in place in the call's decryption buffer. TODO: We really
+ * want to decrypt directly into the target buffer.
*/
- sg = _sg;
- nsg = skb_shinfo(skb)->nr_frags + 1;
- if (nsg <= 4) {
- nsg = 4;
- } else {
- sg = kmalloc_array(nsg, sizeof(*sg), GFP_NOIO);
- if (!sg)
- return -ENOMEM;
- }
-
- sg_init_table(sg, nsg);
- ret = skb_to_sgvec(skb, sg, sp->offset, sp->len);
- if (unlikely(ret < 0)) {
- if (sg != _sg)
- kfree(sg);
- return ret;
- }
+ sg_init_one(sg, data, len);

/* decrypt from the session key */
token = call->conn->key->payload.data[0];
@@ -524,11 +505,9 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,

skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher);
skcipher_request_set_callback(req, 0, NULL, NULL);
- skcipher_request_set_crypt(req, sg, sg, sp->len, iv.x);
+ skcipher_request_set_crypt(req, sg, sg, len, iv.x);
ret = crypto_skcipher_decrypt(req);
skcipher_request_zero(req);
- if (sg != _sg)
- kfree(sg);
if (ret < 0) {
if (ret == -ENOMEM)
return ret;
@@ -537,13 +516,11 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,
}

/* Extract the decrypted packet length */
- if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0)
- return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
- rxkad_abort_2_short_len);
- sp->offset += sizeof(sechdr);
- sp->len -= sizeof(sechdr);
+ sechdr = data;
+ call->rx_dec_offset = sizeof(*sechdr);
+ len -= sizeof(*sechdr);

- buf = ntohl(sechdr.data_size);
+ buf = ntohl(sechdr->data_size);
data_size = buf & 0xffff;

check = buf >> 16;
@@ -553,17 +530,18 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,
return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
rxkad_abort_2_short_check);

- if (data_size > sp->len)
+ if (data_size > len)
return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
rxkad_abort_2_short_data);

- sp->len = data_size;
+ call->rx_dec_len = data_size;
_leave(" = 0 [dlen=%x]", data_size);
return 0;
}

/*
- * Verify the security on a received packet and the subpackets therein.
+ * Verify the security on a received (sub)packet. If the packet needs
+ * modifying (e.g. decrypting), it must be copied.
*/
static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb)
{
@@ -897,7 +875,6 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
*_expiry = 0;

ASSERT(server_key->payload.data[0] != NULL);
- ASSERTCMP((unsigned long) ticket & 7UL, ==, 0);

memcpy(&iv, &server_key->payload.data[2], sizeof(iv));

@@ -1046,14 +1023,15 @@ static int rxkad_decrypt_response(struct rxrpc_connection *conn,
* verify a response
*/
static int rxkad_verify_response(struct rxrpc_connection *conn,
- struct sk_buff *skb)
+ struct sk_buff *skb,
+ void *buffer, unsigned int len)
{
struct rxkad_response *response;
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
struct rxrpc_crypt session_key;
struct key *server_key;
time64_t expiry;
- void *ticket = NULL;
+ void *ticket;
u32 version, kvno, ticket_len, level;
__be32 csum;
int ret, i;
@@ -1076,13 +1054,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
}
}

- ret = -ENOMEM;
- response = kzalloc(sizeof(struct rxkad_response), GFP_NOFS);
- if (!response)
- goto error;
-
- if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
- response, sizeof(*response)) < 0) {
+ response = buffer;
+ if (len < sizeof(*response)) {
ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
rxkad_abort_resp_short);
goto error;
@@ -1094,6 +1067,9 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,

trace_rxrpc_rx_response(conn, sp->hdr.serial, version, kvno, ticket_len);

+ buffer += sizeof(*response);
+ len -= sizeof(*response);
+
if (version != RXKAD_VERSION) {
ret = rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO,
rxkad_abort_resp_version);
@@ -1113,13 +1089,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
}

/* extract the kerberos ticket and decrypt and decode it */
- ret = -ENOMEM;
- ticket = kmalloc(ticket_len, GFP_NOFS);
- if (!ticket)
- goto error;
-
- if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response),
- ticket, ticket_len) < 0) {
+ ticket = buffer;
+ if (ticket_len > len) {
ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
rxkad_abort_resp_short_tkt);
goto error;
@@ -1199,8 +1170,6 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno);

error:
- kfree(ticket);
- kfree(response);
key_put(server_key);
_leave(" = %d", ret);
return ret;
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index b1b0049d7a0e..5b5485849c17 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -26,12 +26,13 @@
#include <net/tc_act/tc_mirred.h>
#include <net/tc_wrapper.h>

+#define MIRRED_DEFER_LIMIT 3
+_Static_assert(MIRRED_DEFER_LIMIT <= 3,
+ "MIRRED_DEFER_LIMIT exceeds tc_depth bitfield width");
+
static LIST_HEAD(mirred_list);
static DEFINE_SPINLOCK(mirred_list_lock);

-#define MIRRED_NEST_LIMIT 4
-static DEFINE_PER_CPU(unsigned int, mirred_nest_level);
-
static bool tcf_mirred_is_act_redirect(int action)
{
return action == TCA_EGRESS_REDIR || action == TCA_INGRESS_REDIR;
@@ -237,12 +238,15 @@ tcf_mirred_forward(bool at_ingress, bool want_ingress, struct sk_buff *skb)
{
int err;

- if (!want_ingress)
+ if (!want_ingress) {
err = tcf_dev_queue_xmit(skb, dev_queue_xmit);
- else if (!at_ingress)
- err = netif_rx(skb);
- else
- err = netif_receive_skb(skb);
+ } else {
+ skb->tc_depth++;
+ if (!at_ingress)
+ err = netif_rx(skb);
+ else
+ err = netif_receive_skb(skb);
+ }

return err;
}
@@ -359,7 +363,8 @@ static int tcf_blockcast_redir(struct sk_buff *skb, struct tcf_mirred *m,
dev_is_mac_header_xmit(dev_prev),
m_eaction, retval);

- return retval;
+ /* If the packet wasn't redirected, we have to register as a drop */
+ return TC_ACT_SHOT;
}

static int tcf_blockcast_mirror(struct sk_buff *skb, struct tcf_mirred *m,
@@ -383,14 +388,12 @@ static int tcf_blockcast_mirror(struct sk_buff *skb, struct tcf_mirred *m,

static int tcf_blockcast(struct sk_buff *skb, struct tcf_mirred *m,
const u32 blockid, struct tcf_result *res,
- int retval)
+ int m_eaction, int retval)
{
const u32 exception_ifindex = skb->dev->ifindex;
struct tcf_block *block;
bool is_redirect;
- int m_eaction;

- m_eaction = READ_ONCE(m->tcfm_eaction);
is_redirect = tcf_mirred_is_act_redirect(m_eaction);

/* we are already under rcu protection, so can call block lookup
@@ -399,7 +402,7 @@ static int tcf_blockcast(struct sk_buff *skb, struct tcf_mirred *m,
block = tcf_block_lookup(dev_net(skb->dev), blockid);
if (!block || xa_empty(&block->ports)) {
tcf_action_inc_overlimit_qstats(&m->common);
- return retval;
+ return is_redirect ? TC_ACT_SHOT : retval;
}

if (is_redirect)
@@ -417,45 +420,73 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
{
struct tcf_mirred *m = to_mirred(a);
int retval = READ_ONCE(m->tcf_action);
- unsigned int nest_level;
- bool m_mac_header_xmit;
+ bool m_mac_header_xmit, is_redirect;
+ struct netdev_xmit *xmit;
struct net_device *dev;
- int m_eaction;
+ bool want_ingress;
+ int i, m_eaction;
u32 blockid;

- nest_level = __this_cpu_inc_return(mirred_nest_level);
- if (unlikely(nest_level > MIRRED_NEST_LIMIT)) {
+#ifdef CONFIG_PREEMPT_RT
+ xmit = &current->net_xmit;
+#else
+ xmit = this_cpu_ptr(&softnet_data.xmit);
+#endif
+ if (unlikely(xmit->sched_mirred_nest >= MIRRED_NEST_LIMIT ||
+ skb->tc_depth >= MIRRED_DEFER_LIMIT)) {
net_warn_ratelimited("Packet exceeded mirred recursion limit on dev %s\n",
netdev_name(skb->dev));
- retval = TC_ACT_SHOT;
- goto dec_nest_level;
+ return TC_ACT_SHOT;
}

tcf_lastuse_update(&m->tcf_tm);
tcf_action_update_bstats(&m->common, skb);

blockid = READ_ONCE(m->tcfm_blockid);
+ m_eaction = READ_ONCE(m->tcfm_eaction);
+ want_ingress = tcf_mirred_act_wants_ingress(m_eaction);
if (blockid) {
- retval = tcf_blockcast(skb, m, blockid, res, retval);
- goto dec_nest_level;
+ if (!want_ingress)
+ xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = NULL;
+ retval = tcf_blockcast(skb, m, blockid, res, m_eaction, retval);
+ if (!want_ingress)
+ xmit->sched_mirred_nest--;
+ return retval;
}

+ is_redirect = tcf_mirred_is_act_redirect(m_eaction);
+
dev = rcu_dereference_bh(m->tcfm_dev);
if (unlikely(!dev)) {
pr_notice_once("tc mirred: target device is gone\n");
tcf_action_inc_overlimit_qstats(&m->common);
- goto dec_nest_level;
+ goto err_out;
+ }
+
+ if (!want_ingress) {
+ for (i = 0; i < xmit->sched_mirred_nest; i++) {
+ if (xmit->sched_mirred_dev[i] != dev)
+ continue;
+ pr_notice_once("tc mirred: loop on device %s\n",
+ netdev_name(dev));
+ tcf_action_inc_overlimit_qstats(&m->common);
+ goto err_out;
+ }
+ xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = dev;
}

m_mac_header_xmit = READ_ONCE(m->tcfm_mac_header_xmit);
- m_eaction = READ_ONCE(m->tcfm_eaction);

retval = tcf_mirred_to_dev(skb, m, dev, m_mac_header_xmit, m_eaction,
retval);
+ if (!want_ingress)
+ xmit->sched_mirred_nest--;

-dec_nest_level:
- __this_cpu_dec(mirred_nest_level);
+ return retval;

+err_out:
+ if (is_redirect)
+ retval = TC_ACT_SHOT;
return retval;
}

diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index 83a7372ea15c..fd9c6c2815a1 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -74,9 +74,13 @@ TC_INDIRECT_SCOPE int fw_classify(struct sk_buff *skb,
}
}
} else {
- struct Qdisc *q = tcf_block_q(tp->chain->block);
+ struct Qdisc *q;

/* Old method: classify the packet using its skb mark. */
+ if (tcf_block_shared(tp->chain->block))
+ return -1;
+
+ q = tcf_block_q(tp->chain->block);
if (id && (TC_H_MAJ(id) == 0 ||
!(TC_H_MAJ(id ^ q->handle)))) {
res->classid = id;
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 498c18d7d9c3..136b7d81296e 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -459,7 +459,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
skb->prev = NULL;

/* Random duplication */
- if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor, &q->prng))
+ if (q->duplicate && skb->tc_depth == 0 &&
+ q->duplicate >= get_crandom(&q->dup_cor, &q->prng))
++count;

/* Drop packet? */
@@ -538,11 +539,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
*/
if (skb2) {
struct Qdisc *rootq = qdisc_root_bh(sch);
- u32 dupsave = q->duplicate; /* prevent duplicating a dup... */

- q->duplicate = 0;
+ skb2->tc_depth++; /* prevent duplicating a dup... */
rootq->enqueue(skb2, rootq, to_free);
- q->duplicate = dupsave;
skb2 = NULL;
}

@@ -1005,41 +1004,6 @@ static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla,
return 0;
}

-static const struct Qdisc_class_ops netem_class_ops;
-
-static int check_netem_in_tree(struct Qdisc *sch, bool duplicates,
- struct netlink_ext_ack *extack)
-{
- struct Qdisc *root, *q;
- unsigned int i;
-
- root = qdisc_root_sleeping(sch);
-
- if (sch != root && root->ops->cl_ops == &netem_class_ops) {
- if (duplicates ||
- ((struct netem_sched_data *)qdisc_priv(root))->duplicate)
- goto err;
- }
-
- if (!qdisc_dev(root))
- return 0;
-
- hash_for_each(qdisc_dev(root)->qdisc_hash, i, q, hash) {
- if (sch != q && q->ops->cl_ops == &netem_class_ops) {
- if (duplicates ||
- ((struct netem_sched_data *)qdisc_priv(q))->duplicate)
- goto err;
- }
- }
-
- return 0;
-
-err:
- NL_SET_ERR_MSG(extack,
- "netem: cannot mix duplicating netems with other netems in tree");
- return -EINVAL;
-}
-
/* Parse netlink message to set options */
static int netem_change(struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
@@ -1116,11 +1080,6 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt,
q->gap = qopt->gap;
q->counter = 0;
q->loss = qopt->loss;
-
- ret = check_netem_in_tree(sch, qopt->duplicate, extack);
- if (ret)
- goto unlock;
-
q->duplicate = qopt->duplicate;

/* for compatibility with earlier versions.
diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c
index c36725f0870d..9a2edaf8352a 100644
--- a/net/sched/sch_sfb.c
+++ b/net/sched/sch_sfb.c
@@ -439,7 +439,7 @@ static struct sk_buff *sfb_dequeue(struct Qdisc *sch)
struct Qdisc *child = q->qdisc;
struct sk_buff *skb;

- skb = child->dequeue(q->qdisc);
+ skb = qdisc_dequeue_peeked(child);

if (skb) {
qdisc_bstats_update(sch, skb);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 6b562dd1aae1..3e80cf4e63ff 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -9377,6 +9377,8 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
release_sock(sk);
current_timeo = schedule_timeout(current_timeo);
lock_sock(sk);
+ if (sk != asoc->base.sk)
+ goto do_error;

*timeo_p = current_timeo;
}
diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c
index c96abb1386be..6f3469ad54a1 100644
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -187,10 +187,12 @@ static bool smc_hs_congested(const struct sock *sk)

struct smc_hashinfo smc_v4_hashinfo = {
.lock = __RW_LOCK_UNLOCKED(smc_v4_hashinfo.lock),
+ .ht = HLIST_HEAD_INIT,
};

struct smc_hashinfo smc_v6_hashinfo = {
.lock = __RW_LOCK_UNLOCKED(smc_v6_hashinfo.lock),
+ .ht = HLIST_HEAD_INIT,
};

int smc_hash_sk(struct sock *sk)
@@ -3594,8 +3596,6 @@ static int __init smc_init(void)
pr_err("%s: sock_register fails with %d\n", __func__, rc);
goto out_proto6;
}
- INIT_HLIST_HEAD(&smc_v4_hashinfo.ht);
- INIT_HLIST_HEAD(&smc_v6_hashinfo.ht);

rc = smc_ib_register_client();
if (rc) {
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index 1db7a1f8e55f..f03e00cae028 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -523,7 +523,7 @@ int vsock_assign_transport(struct vsock_sock *vsk, struct vsock_sock *psk)
*/
sock_reset_flag(sk, SOCK_DONE);
sk->sk_state = TCP_CLOSE;
- vsk->peer_shutdown = 0;
+ WRITE_ONCE(vsk->peer_shutdown, 0);
}

if (sk->sk_type == SOCK_SEQPACKET) {
@@ -814,7 +814,7 @@ static struct sock *__vsock_create(struct net *net,
vsk->rejected = false;
vsk->sent_request = false;
vsk->ignore_connecting_rst = false;
- vsk->peer_shutdown = 0;
+ WRITE_ONCE(vsk->peer_shutdown, 0);
INIT_DELAYED_WORK(&vsk->connect_work, vsock_connect_timeout);
INIT_DELAYED_WORK(&vsk->pending_work, vsock_pending_work);

@@ -1099,6 +1099,25 @@ static int vsock_shutdown(struct socket *sock, int mode)
return err;
}

+static __poll_t vsock_poll_shutdown(struct sock *sk, u32 peer_shutdown)
+{
+ __poll_t mask = 0;
+
+ /* INET sockets treat local write shutdown and peer write shutdown as a
+ * case of EPOLLHUP set.
+ */
+ if (sk->sk_shutdown == SHUTDOWN_MASK ||
+ ((sk->sk_shutdown & SEND_SHUTDOWN) &&
+ (peer_shutdown & SEND_SHUTDOWN)))
+ mask |= EPOLLHUP;
+
+ if (sk->sk_shutdown & RCV_SHUTDOWN ||
+ peer_shutdown & SEND_SHUTDOWN)
+ mask |= EPOLLRDHUP;
+
+ return mask;
+}
+
static __poll_t vsock_poll(struct file *file, struct socket *sock,
poll_table *wait)
{
@@ -1116,24 +1135,17 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock,
/* Signify that there has been an error on this socket. */
mask |= EPOLLERR;

- /* INET sockets treat local write shutdown and peer write shutdown as a
- * case of EPOLLHUP set.
- */
- if ((sk->sk_shutdown == SHUTDOWN_MASK) ||
- ((sk->sk_shutdown & SEND_SHUTDOWN) &&
- (vsk->peer_shutdown & SEND_SHUTDOWN))) {
- mask |= EPOLLHUP;
- }
-
- if (sk->sk_shutdown & RCV_SHUTDOWN ||
- vsk->peer_shutdown & SEND_SHUTDOWN) {
- mask |= EPOLLRDHUP;
- }
-
if (sk_is_readable(sk))
mask |= EPOLLIN | EPOLLRDNORM;

if (sock->type == SOCK_DGRAM) {
+ u32 peer_shutdown = READ_ONCE(vsk->peer_shutdown);
+
+ /* DGRAM sockets do not take lock_sock() in poll(), so use one
+ * lockless snapshot for all shutdown-derived mask bits.
+ */
+ mask |= vsock_poll_shutdown(sk, peer_shutdown);
+
/* For datagram sockets we can read if there is something in
* the queue and write as long as the socket isn't shutdown for
* sending.
@@ -1148,6 +1160,7 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock,

} else if (sock_type_connectible(sk->sk_type)) {
const struct vsock_transport *transport;
+ u32 peer_shutdown;

lock_sock(sk);

@@ -1180,8 +1193,10 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock,
* terminated should also be considered read, and we check the
* shutdown flag for that.
*/
+ peer_shutdown = READ_ONCE(vsk->peer_shutdown);
+ mask |= vsock_poll_shutdown(sk, peer_shutdown);
if (sk->sk_shutdown & RCV_SHUTDOWN ||
- vsk->peer_shutdown & SEND_SHUTDOWN) {
+ peer_shutdown & SEND_SHUTDOWN) {
mask |= EPOLLIN | EPOLLRDNORM;
}

diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c
index 34871ed1a099..865e004ee286 100644
--- a/net/vmw_vsock/hyperv_transport.c
+++ b/net/vmw_vsock/hyperv_transport.c
@@ -264,7 +264,7 @@ static void hvs_do_close_lock_held(struct vsock_sock *vsk,
struct sock *sk = sk_vsock(vsk);

sock_set_flag(sk, SOCK_DONE);
- vsk->peer_shutdown = SHUTDOWN_MASK;
+ WRITE_ONCE(vsk->peer_shutdown, SHUTDOWN_MASK);
if (vsock_stream_has_data(vsk) <= 0)
sk->sk_state = TCP_CLOSING;
sk->sk_state_change(sk);
@@ -593,7 +593,9 @@ static int hvs_update_recv_data(struct hvsock *hvs)
return -EIO;

if (payload_len == 0)
- hvs->vsk->peer_shutdown |= SEND_SHUTDOWN;
+ WRITE_ONCE(hvs->vsk->peer_shutdown,
+ READ_ONCE(hvs->vsk->peer_shutdown) |
+ SEND_SHUTDOWN);

hvs->recv_data_len = payload_len;
hvs->recv_data_off = 0;
@@ -704,7 +706,8 @@ static s64 hvs_stream_has_data(struct vsock_sock *vsk)
ret = 1;
break;
case 0:
- vsk->peer_shutdown |= SEND_SHUTDOWN;
+ WRITE_ONCE(vsk->peer_shutdown,
+ READ_ONCE(vsk->peer_shutdown) | SEND_SHUTDOWN);
ret = 0;
break;
default: /* -1 */
diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index c182886136b4..b588ccd133ea 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -1234,7 +1234,7 @@ static void virtio_transport_do_close(struct vsock_sock *vsk,
struct sock *sk = sk_vsock(vsk);

sock_set_flag(sk, SOCK_DONE);
- vsk->peer_shutdown = SHUTDOWN_MASK;
+ WRITE_ONCE(vsk->peer_shutdown, SHUTDOWN_MASK);
if (vsock_stream_has_data(vsk) <= 0)
sk->sk_state = TCP_CLOSING;
sk->sk_state_change(sk);
@@ -1437,12 +1437,15 @@ virtio_transport_recv_connected(struct sock *sk,
case VIRTIO_VSOCK_OP_CREDIT_UPDATE:
sk->sk_write_space(sk);
break;
- case VIRTIO_VSOCK_OP_SHUTDOWN:
+ case VIRTIO_VSOCK_OP_SHUTDOWN: {
+ u32 peer_shutdown = READ_ONCE(vsk->peer_shutdown);
+
if (le32_to_cpu(hdr->flags) & VIRTIO_VSOCK_SHUTDOWN_RCV)
- vsk->peer_shutdown |= RCV_SHUTDOWN;
+ peer_shutdown |= RCV_SHUTDOWN;
if (le32_to_cpu(hdr->flags) & VIRTIO_VSOCK_SHUTDOWN_SEND)
- vsk->peer_shutdown |= SEND_SHUTDOWN;
- if (vsk->peer_shutdown == SHUTDOWN_MASK) {
+ peer_shutdown |= SEND_SHUTDOWN;
+ WRITE_ONCE(vsk->peer_shutdown, peer_shutdown);
+ if (peer_shutdown == SHUTDOWN_MASK) {
if (vsock_stream_has_data(vsk) <= 0 && !sock_flag(sk, SOCK_DONE)) {
(void)virtio_transport_reset(vsk, NULL);
virtio_transport_do_close(vsk, true);
@@ -1457,6 +1460,7 @@ virtio_transport_recv_connected(struct sock *sk,
if (le32_to_cpu(virtio_vsock_hdr(skb)->flags))
sk->sk_state_change(sk);
break;
+ }
case VIRTIO_VSOCK_OP_RST:
virtio_transport_do_close(vsk, true);
break;
diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c
index 4cd11f355e9d..443125e48f24 100644
--- a/net/vmw_vsock/vmci_transport.c
+++ b/net/vmw_vsock/vmci_transport.c
@@ -811,7 +811,7 @@ static void vmci_transport_handle_detach(struct sock *sk)
/* On a detach the peer will not be sending or receiving
* anymore.
*/
- vsk->peer_shutdown = SHUTDOWN_MASK;
+ WRITE_ONCE(vsk->peer_shutdown, SHUTDOWN_MASK);

/* We should not be sending anymore since the peer won't be
* there to receive, but we can still receive if there is data
@@ -1534,7 +1534,9 @@ static int vmci_transport_recv_connected(struct sock *sk,
if (pkt->u.mode) {
vsk = vsock_sk(sk);

- vsk->peer_shutdown |= pkt->u.mode;
+ WRITE_ONCE(vsk->peer_shutdown,
+ READ_ONCE(vsk->peer_shutdown) |
+ pkt->u.mode);
sk->sk_state_change(sk);
}
break;
@@ -1551,7 +1553,7 @@ static int vmci_transport_recv_connected(struct sock *sk,
* a clean shutdown.
*/
sock_set_flag(sk, SOCK_DONE);
- vsk->peer_shutdown = SHUTDOWN_MASK;
+ WRITE_ONCE(vsk->peer_shutdown, SHUTDOWN_MASK);
if (vsock_stream_has_data(vsk) <= 0)
sk->sk_state = TCP_CLOSING;

diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 841a60a6fbfe..8edcb32735e5 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -769,9 +769,12 @@ static void xfrm_trans_reinject(struct work_struct *work)
spin_unlock_bh(&trans->queue_lock);

local_bh_disable();
- while ((skb = __skb_dequeue(&queue)))
- XFRM_TRANS_SKB_CB(skb)->finish(XFRM_TRANS_SKB_CB(skb)->net,
- NULL, skb);
+ while ((skb = __skb_dequeue(&queue))) {
+ struct net *net = XFRM_TRANS_SKB_CB(skb)->net;
+
+ XFRM_TRANS_SKB_CB(skb)->finish(net, NULL, skb);
+ put_net(net);
+ }
local_bh_enable();
}

@@ -780,6 +783,7 @@ int xfrm_trans_queue_net(struct net *net, struct sk_buff *skb,
struct sk_buff *))
{
struct xfrm_trans_tasklet *trans;
+ struct net *hold_net;

trans = this_cpu_ptr(&xfrm_trans_tasklet);

@@ -788,8 +792,12 @@ int xfrm_trans_queue_net(struct net *net, struct sk_buff *skb,

BUILD_BUG_ON(sizeof(struct xfrm_trans_cb) > sizeof(skb->cb));

+ hold_net = maybe_get_net(net);
+ if (!hold_net)
+ return -ENODEV;
+
XFRM_TRANS_SKB_CB(skb)->finish = finish;
- XFRM_TRANS_SKB_CB(skb)->net = net;
+ XFRM_TRANS_SKB_CB(skb)->net = hold_net;
spin_lock_bh(&trans->queue_lock);
__skb_queue_tail(&trans->queue, skb);
spin_unlock_bh(&trans->queue_lock);
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index fca07f8e6074..dab782dcc829 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -4264,21 +4264,21 @@ static int __net_init xfrm_policy_init(struct net *net)
return -ENOMEM;
}

-static void xfrm_policy_fini(struct net *net)
+static void __net_exit xfrm_net_pre_exit(struct net *net)
{
- struct xfrm_pol_inexact_bin *b, *t;
- unsigned int sz;
- int dir;
-
disable_work_sync(&net->xfrm.policy_hthresh.work);
-
flush_work(&net->xfrm.policy_hash_work);
#ifdef CONFIG_XFRM_SUB_POLICY
xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, false);
#endif
xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, false);
+}

- synchronize_rcu();
+static void xfrm_policy_fini(struct net *net)
+{
+ struct xfrm_pol_inexact_bin *b, *t;
+ unsigned int sz;
+ int dir;

WARN_ON(!list_empty(&net->xfrm.policy_all));

@@ -4356,6 +4356,7 @@ static void __net_exit xfrm_net_exit(struct net *net)

static struct pernet_operations __net_initdata xfrm_net_ops = {
.init = xfrm_net_init,
+ .pre_exit = xfrm_net_pre_exit,
.exit = xfrm_net_exit,
};

@@ -4689,7 +4690,7 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
}

/* Stage 5 - announce */
- km_migrate(sel, dir, type, m, num_migrate, k, encap);
+ km_migrate(sel, dir, type, m, num_migrate, k, net, encap);

xfrm_pol_put(pol);

diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 6a92d88f9e03..04cb20163802 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -2745,7 +2745,7 @@ EXPORT_SYMBOL(km_policy_expired);
#ifdef CONFIG_XFRM_MIGRATE
int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
const struct xfrm_migrate *m, int num_migrate,
- const struct xfrm_kmaddress *k,
+ const struct xfrm_kmaddress *k, struct net *net,
const struct xfrm_encap_tmpl *encap)
{
int err = -EINVAL;
@@ -2756,7 +2756,7 @@ int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
list_for_each_entry_rcu(km, &xfrm_km_list, list) {
if (km->migrate) {
ret = km->migrate(sel, dir, type, m, num_migrate, k,
- encap);
+ net, encap);
if (!ret)
err = ret;
}
@@ -3022,10 +3022,14 @@ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
const struct xfrm_type *type = READ_ONCE(x->type);
struct crypto_aead *aead;
u32 blksize, net_adj = 0;
+ u32 overhead, payload_mtu;

if (x->km.state != XFRM_STATE_VALID ||
- !type || type->proto != IPPROTO_ESP)
+ !type || type->proto != IPPROTO_ESP) {
+ if (mtu <= x->props.header_len)
+ return 1;
return mtu - x->props.header_len;
+ }

aead = x->data;
blksize = ALIGN(crypto_aead_blocksize(aead), 4);
@@ -3045,8 +3049,17 @@ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
break;
}

- return ((mtu - x->props.header_len - crypto_aead_authsize(aead) -
- net_adj) & ~(blksize - 1)) + net_adj - 2;
+ overhead = x->props.header_len + crypto_aead_authsize(aead) + net_adj;
+ if (mtu <= overhead)
+ return 1;
+
+ payload_mtu = mtu - overhead;
+ payload_mtu &= ~(blksize - 1);
+ if (payload_mtu <= 2)
+ return 1;
+
+ return payload_mtu + net_adj - 2;
+
}
EXPORT_SYMBOL_GPL(xfrm_state_mtu);

diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 3182dc066011..50d916654b52 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -3183,10 +3183,9 @@ static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m,

static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
const struct xfrm_migrate *m, int num_migrate,
- const struct xfrm_kmaddress *k,
+ const struct xfrm_kmaddress *k, struct net *net,
const struct xfrm_encap_tmpl *encap)
{
- struct net *net = &init_net;
struct sk_buff *skb;
int err;

@@ -3204,7 +3203,7 @@ static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
#else
static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
const struct xfrm_migrate *m, int num_migrate,
- const struct xfrm_kmaddress *k,
+ const struct xfrm_kmaddress *k, struct net *net,
const struct xfrm_encap_tmpl *encap)
{
return -ENOPROTOOPT;
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index cccd61cca509..fbfb1d48dc88 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -9,7 +9,6 @@
*/

#include <linux/errno.h>
-#include <linux/fdtable.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index daa7cda98ae6..a65a3b8d04b8 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -2966,8 +2966,10 @@ static void snd_pcm_oss_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_pcm_str *pstr = entry->private_data;
- struct snd_pcm_oss_setup *setup = pstr->oss.setup_list;
+ struct snd_pcm_oss_setup *setup;
+
guard(mutex)(&pstr->oss.setup_mutex);
+ setup = pstr->oss.setup_list;
while (setup) {
snd_iprintf(buffer, "%s %u %u%s%s%s%s%s%s\n",
setup->task_name,
@@ -3052,6 +3054,13 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
buffer->error = -ENOMEM;
return;
}
+ template.task_name = kstrdup(task_name, GFP_KERNEL);
+ if (!template.task_name) {
+ kfree(setup);
+ buffer->error = -ENOMEM;
+ return;
+ }
+ *setup = template;
if (pstr->oss.setup_list == NULL)
pstr->oss.setup_list = setup;
else {
@@ -3059,12 +3068,7 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
setup1->next; setup1 = setup1->next);
setup1->next = setup;
}
- template.task_name = kstrdup(task_name, GFP_KERNEL);
- if (! template.task_name) {
- kfree(setup);
- buffer->error = -ENOMEM;
- return;
- }
+ continue;
}
*setup = template;
}
diff --git a/sound/firewire/motu/motu-register-dsp-message-parser.c b/sound/firewire/motu/motu-register-dsp-message-parser.c
index ef3b0b0f0dab..abafae59f365 100644
--- a/sound/firewire/motu/motu-register-dsp-message-parser.c
+++ b/sound/firewire/motu/motu-register-dsp-message-parser.c
@@ -393,6 +393,8 @@ unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *m
{
struct msg_parser *parser = motu->message_parser;

+ guard(spinlock_irqsave)(&parser->lock);
+
if (parser->pull_pos > parser->push_pos)
return EVENT_QUEUE_SIZE - parser->pull_pos + parser->push_pos;
else
@@ -402,14 +404,14 @@ unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *m
bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event)
{
struct msg_parser *parser = motu->message_parser;
- unsigned int pos = parser->pull_pos;
- unsigned long flags;
+ unsigned int pos;

- if (pos == parser->push_pos)
- return false;
+ guard(spinlock_irqsave)(&parser->lock);

- spin_lock_irqsave(&parser->lock, flags);
+ if (parser->pull_pos == parser->push_pos)
+ return false;

+ pos = parser->pull_pos;
*event = parser->event_queue[pos];

++pos;
@@ -417,7 +419,5 @@ bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32
pos = 0;
parser->pull_pos = pos;

- spin_unlock_irqrestore(&parser->lock, flags);
-
return true;
}
diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c
index 240af0563283..4c94087a246e 100644
--- a/sound/soc/codecs/simple-mux.c
+++ b/sound/soc/codecs/simple-mux.c
@@ -49,7 +49,7 @@ static int simple_mux_control_put(struct snd_kcontrol *kcontrol,
struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);
struct simple_mux *priv = snd_soc_component_get_drvdata(c);

- if (ucontrol->value.enumerated.item[0] > e->items)
+ if (ucontrol->value.enumerated.item[0] >= e->items)
return -EINVAL;

if (priv->mux == ucontrol->value.enumerated.item[0])
diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c
index 7975dc0ceb35..676c08247cfc 100644
--- a/sound/soc/intel/boards/bytcht_es8316.c
+++ b/sound/soc/intel/boards/bytcht_es8316.c
@@ -40,6 +40,7 @@ struct byt_cht_es8316_private {
struct gpio_desc *speaker_en_gpio;
struct device *codec_dev;
bool speaker_en;
+ bool mclk_enabled;
};

enum {
@@ -170,6 +171,15 @@ static struct snd_soc_jack_pin byt_cht_es8316_jack_pins[] = {
},
};

+static void byt_cht_es8316_disable_mclk(struct byt_cht_es8316_private *priv)
+{
+ if (!priv->mclk_enabled)
+ return;
+
+ clk_disable_unprepare(priv->mclk);
+ priv->mclk_enabled = false;
+}
+
static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component;
@@ -226,12 +236,14 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
ret = clk_prepare_enable(priv->mclk);
if (ret)
dev_err(card->dev, "unable to enable MCLK\n");
+ else
+ priv->mclk_enabled = true;

ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(runtime, 0), 0, 19200000,
SND_SOC_CLOCK_IN);
if (ret < 0) {
dev_err(card->dev, "can't set codec clock %d\n", ret);
- return ret;
+ goto err_disable_mclk;
}

ret = snd_soc_card_jack_new_pins(card, "Headset",
@@ -240,13 +252,25 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime)
ARRAY_SIZE(byt_cht_es8316_jack_pins));
if (ret) {
dev_err(card->dev, "jack creation failed %d\n", ret);
- return ret;
+ goto err_disable_mclk;
}

snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_soc_component_set_jack(codec, &priv->jack, NULL);

return 0;
+
+err_disable_mclk:
+ byt_cht_es8316_disable_mclk(priv);
+ return ret;
+}
+
+static void byt_cht_es8316_exit(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card);
+
+ byt_cht_es8316_disable_mclk(priv);
}

static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd,
@@ -356,6 +380,7 @@ static struct snd_soc_dai_link byt_cht_es8316_dais[] = {
.dpcm_playback = 1,
.dpcm_capture = 1,
.init = byt_cht_es8316_init,
+ .exit = byt_cht_es8316_exit,
SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),
},
};
diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c
index 9f1c5e2676dd..eaf4e77892ff 100644
--- a/sound/soc/qcom/qdsp6/q6asm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6asm-dai.c
@@ -186,7 +186,6 @@ static void event_handler(uint32_t opcode, uint32_t token,
prtd->pcm_count, 0, 0, 0);
break;
case ASM_CLIENT_EVENT_CMD_EOS_DONE:
- prtd->state = Q6ASM_STREAM_STOPPED;
break;
case ASM_CLIENT_EVENT_DATA_WRITE_DONE: {
prtd->pcm_irq_pos += prtd->pcm_count;
@@ -234,9 +233,19 @@ static int q6asm_dai_prepare(struct snd_soc_component *component,
/* rate and channels are sent to audio driver */
if (prtd->state == Q6ASM_STREAM_RUNNING) {
/* clear the previous setup if any */
- q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE);
- q6asm_unmap_memory_regions(substream->stream,
- prtd->audio_client);
+ ret = q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE);
+ if (ret < 0) {
+ dev_err(dev, "Failed to close q6asm stream %d\n", prtd->stream_id);
+ return ret;
+ }
+
+ ret = q6asm_unmap_memory_regions(substream->stream, prtd->audio_client);
+ if (ret < 0) {
+ dev_err(dev, "Failed to unmap memory regions for q6asm stream %d\n",
+ prtd->stream_id);
+ return ret;
+ }
+
q6routing_stream_close(soc_prtd->dai_link->id,
substream->stream);
prtd->state = Q6ASM_STREAM_STOPPED;
@@ -304,8 +313,6 @@ static int q6asm_dai_prepare(struct snd_soc_component *component,
q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE);
open_err:
q6asm_unmap_memory_regions(substream->stream, prtd->audio_client);
- q6asm_audio_client_free(prtd->audio_client);
- prtd->audio_client = NULL;

return ret;
}
@@ -325,7 +332,6 @@ static int q6asm_dai_trigger(struct snd_soc_component *component,
0, 0, 0);
break;
case SNDRV_PCM_TRIGGER_STOP:
- prtd->state = Q6ASM_STREAM_STOPPED;
ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,
CMD_EOS);
break;
@@ -438,12 +444,12 @@ static int q6asm_dai_close(struct snd_soc_component *component,
struct q6asm_dai_rtd *prtd = runtime->private_data;

if (prtd->audio_client) {
- if (prtd->state)
+ if (prtd->state == Q6ASM_STREAM_RUNNING) {
q6asm_cmd(prtd->audio_client, prtd->stream_id,
CMD_CLOSE);
-
- q6asm_unmap_memory_regions(substream->stream,
+ q6asm_unmap_memory_regions(substream->stream,
prtd->audio_client);
+ }
q6asm_audio_client_free(prtd->audio_client);
prtd->audio_client = NULL;
}
@@ -536,8 +542,6 @@ static void compress_event_handler(uint32_t opcode, uint32_t token,
snd_compr_drain_notify(prtd->cstream);
prtd->notify_on_drain = false;

- } else {
- prtd->state = Q6ASM_STREAM_STOPPED;
}
spin_unlock_irqrestore(&prtd->lock, flags);
break;
@@ -660,7 +664,7 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd = stream->private_data;

if (prtd->audio_client) {
- if (prtd->state) {
+ if (prtd->state == Q6ASM_STREAM_RUNNING) {
q6asm_cmd(prtd->audio_client, prtd->stream_id,
CMD_CLOSE);
if (prtd->next_track_stream_id) {
@@ -668,11 +672,11 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component,
prtd->next_track_stream_id,
CMD_CLOSE);
}
- }

- snd_dma_free_pages(&prtd->dma_buffer);
- q6asm_unmap_memory_regions(stream->direction,
+ q6asm_unmap_memory_regions(stream->direction,
prtd->audio_client);
+ }
+ snd_dma_free_pages(&prtd->dma_buffer);
q6asm_audio_client_free(prtd->audio_client);
prtd->audio_client = NULL;
}
@@ -902,7 +906,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
prtd->session_id, dir);
if (ret) {
dev_err(dev, "Stream reg failed ret:%d\n", ret);
- goto q6_err;
+ goto routing_err;
}

ret = __q6asm_dai_compr_set_codec_params(component, stream,
@@ -928,11 +932,11 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component,
return 0;

q6_err:
+ q6routing_stream_close(rtd->dai_link->id, dir);
+routing_err:
q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE);

open_err:
- q6asm_audio_client_free(prtd->audio_client);
- prtd->audio_client = NULL;
return ret;
}

@@ -1000,7 +1004,6 @@ static int q6asm_dai_compr_trigger(struct snd_soc_component *component,
0, 0, 0);
break;
case SNDRV_PCM_TRIGGER_STOP:
- prtd->state = Q6ASM_STREAM_STOPPED;
ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id,
CMD_EOS);
break;
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index d767a89e452d..c46c4d41ca0f 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -2467,6 +2467,27 @@ static int scarlett2_has_config_item(
return !!private->config_set->items[config_item_num].offset;
}

+/* Return the configuration item's offset, applying any per-firmware
+ * overrides.
+ *
+ * Firmware 2417 for the 2i2 Gen 4 moved DIRECT_MONITOR_GAIN by 4
+ * bytes. Apply that shift here so that the rest of the driver can
+ * keep using the single config set. This override can be removed
+ * once the multi-config-set framework lands.
+ */
+static int scarlett2_config_item_offset(
+ struct scarlett2_data *private, int config_item_num)
+{
+ int offset = private->config_set->items[config_item_num].offset;
+
+ if (config_item_num == SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN &&
+ private->info == &s2i2_gen4_info &&
+ private->firmware_version >= 2417)
+ offset = 0x2a4;
+
+ return offset;
+}
+
/* Send a USB message to get configuration parameters; result placed in *buf */
static int scarlett2_usb_get_config(
struct usb_mixer_interface *mixer,
@@ -2476,6 +2497,7 @@ static int scarlett2_usb_get_config(
const struct scarlett2_config *config_item =
&private->config_set->items[config_item_num];
int size, err, i;
+ int item_offset;
u8 *buf_8;
u8 value;

@@ -2485,13 +2507,15 @@ static int scarlett2_usb_get_config(
if (!config_item->offset)
return -EFAULT;

+ item_offset = scarlett2_config_item_offset(private, config_item_num);
+
/* Writes to the parameter buffer are always 1 byte */
size = config_item->size ? config_item->size : 8;

/* For byte-sized parameters, retrieve directly into buf */
if (size >= 8) {
size = size / 8 * count;
- err = scarlett2_usb_get(mixer, config_item->offset, buf, size);
+ err = scarlett2_usb_get(mixer, item_offset, buf, size);
if (err < 0)
return err;
if (config_item->size == 16) {
@@ -2509,7 +2533,7 @@ static int scarlett2_usb_get_config(
}

/* For bit-sized parameters, retrieve into value */
- err = scarlett2_usb_get(mixer, config_item->offset, &value, 1);
+ err = scarlett2_usb_get(mixer, item_offset, &value, 1);
if (err < 0)
return err;

@@ -2659,7 +2683,8 @@ static int scarlett2_usb_set_config(
*/
if (config_item->size >= 8) {
size = config_item->size / 8;
- offset = config_item->offset + index * size;
+ offset = scarlett2_config_item_offset(private, config_item_num) +
+ index * size;

/* If updating a bit, retrieve the old value, set/clear the
* bit as needed, and update value
@@ -2668,7 +2693,7 @@ static int scarlett2_usb_set_config(
u8 tmp;

size = 1;
- offset = config_item->offset;
+ offset = scarlett2_config_item_offset(private, config_item_num);

err = scarlett2_usb_get(mixer, offset, &tmp, 1);
if (err < 0)
@@ -9524,12 +9549,15 @@ static long scarlett2_hwdep_write(struct snd_hwdep *hw,
flash_size = private->flash_segment_blocks[segment_id] *
SCARLETT2_FLASH_BLOCK_SIZE;

- if (count < 0 || *offset < 0 || *offset + count >= flash_size)
+ if (count < 0 || *offset < 0)
return -EINVAL;

if (!count)
return 0;

+ if (*offset >= flash_size || count > flash_size - *offset)
+ return -ENOSPC;
+
/* Limit the *req size to SCARLETT2_FLASH_RW_MAX */
if (count > max_data_size)
count = max_data_size;
diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
index 050725afa45d..0d0c434426e7 100644
--- a/tools/testing/cxl/test/cxl.c
+++ b/tools/testing/cxl/test/cxl.c
@@ -1058,6 +1058,23 @@ static void mock_companion(struct acpi_device *adev, struct device *dev)
#define SZ_64G (SZ_32G * 2)
#endif

+static int cxl_mock_platform_device_add(struct platform_device *pdev,
+ struct platform_device **ppdev)
+{
+ int rc;
+
+ if (ppdev)
+ *ppdev = pdev;
+ rc = platform_device_add(pdev);
+ if (rc) {
+ platform_device_put(pdev);
+ if (ppdev)
+ *ppdev = NULL;
+ }
+
+ return rc;
+}
+
static __init int cxl_rch_topo_init(void)
{
int rc, i;
@@ -1072,13 +1089,10 @@ static __init int cxl_rch_topo_init(void)
goto err_bridge;

mock_companion(adev, &pdev->dev);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_rch[i]);
+ if (rc)
goto err_bridge;
- }

- cxl_rch[i] = pdev;
mock_pci_bus[idx].bridge = &pdev->dev;
rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
"firmware_node");
@@ -1130,13 +1144,10 @@ static __init int cxl_single_topo_init(void)
goto err_bridge;

mock_companion(adev, &pdev->dev);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_hb_single[i]);
+ if (rc)
goto err_bridge;
- }

- cxl_hb_single[i] = pdev;
mock_pci_bus[i + NR_CXL_HOST_BRIDGES].bridge = &pdev->dev;
rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
"physical_node");
@@ -1155,12 +1166,9 @@ static __init int cxl_single_topo_init(void)
goto err_port;
pdev->dev.parent = &bridge->dev;

- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_root_single[i]);
+ if (rc)
goto err_port;
- }
- cxl_root_single[i] = pdev;
}

for (i = 0; i < ARRAY_SIZE(cxl_swu_single); i++) {
@@ -1173,12 +1181,9 @@ static __init int cxl_single_topo_init(void)
goto err_uport;
pdev->dev.parent = &root_port->dev;

- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_swu_single[i]);
+ if (rc)
goto err_uport;
- }
- cxl_swu_single[i] = pdev;
}

for (i = 0; i < ARRAY_SIZE(cxl_swd_single); i++) {
@@ -1192,12 +1197,9 @@ static __init int cxl_single_topo_init(void)
goto err_dport;
pdev->dev.parent = &uport->dev;

- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_swd_single[i]);
+ if (rc)
goto err_dport;
- }
- cxl_swd_single[i] = pdev;
}

return 0;
@@ -1270,12 +1272,9 @@ static int cxl_mem_init(void)
pdev->dev.parent = &dport->dev;
set_dev_node(&pdev->dev, i % 2);

- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mem[i]);
+ if (rc)
goto err_mem;
- }
- cxl_mem[i] = pdev;
}

for (i = 0; i < ARRAY_SIZE(cxl_mem_single); i++) {
@@ -1288,12 +1287,9 @@ static int cxl_mem_init(void)
pdev->dev.parent = &dport->dev;
set_dev_node(&pdev->dev, i % 2);

- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_mem_single[i]);
+ if (rc)
goto err_single;
- }
- cxl_mem_single[i] = pdev;
}

for (i = 0; i < ARRAY_SIZE(cxl_rcd); i++) {
@@ -1307,12 +1303,9 @@ static int cxl_mem_init(void)
pdev->dev.parent = &rch->dev;
set_dev_node(&pdev->dev, i % 2);

- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_rcd[i]);
+ if (rc)
goto err_rcd;
- }
- cxl_rcd[i] = pdev;
}

return 0;
@@ -1373,13 +1366,10 @@ static __init int cxl_test_init(void)
goto err_bridge;

mock_companion(adev, &pdev->dev);
- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_host_bridge[i]);
+ if (rc)
goto err_bridge;
- }

- cxl_host_bridge[i] = pdev;
mock_pci_bus[i].bridge = &pdev->dev;
rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj,
"physical_node");
@@ -1397,12 +1387,9 @@ static __init int cxl_test_init(void)
goto err_port;
pdev->dev.parent = &bridge->dev;

- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_root_port[i]);
+ if (rc)
goto err_port;
- }
- cxl_root_port[i] = pdev;
}

BUILD_BUG_ON(ARRAY_SIZE(cxl_switch_uport) != ARRAY_SIZE(cxl_root_port));
@@ -1415,12 +1402,9 @@ static __init int cxl_test_init(void)
goto err_uport;
pdev->dev.parent = &root_port->dev;

- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_switch_uport[i]);
+ if (rc)
goto err_uport;
- }
- cxl_switch_uport[i] = pdev;
}

for (i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++) {
@@ -1433,12 +1417,9 @@ static __init int cxl_test_init(void)
goto err_dport;
pdev->dev.parent = &uport->dev;

- rc = platform_device_add(pdev);
- if (rc) {
- platform_device_put(pdev);
+ rc = cxl_mock_platform_device_add(pdev, &cxl_switch_dport[i]);
+ if (rc)
goto err_dport;
- }
- cxl_switch_dport[i] = pdev;
}

rc = cxl_single_topo_init();
@@ -1456,9 +1437,9 @@ static __init int cxl_test_init(void)
mock_companion(&acpi0017_mock, &cxl_acpi->dev);
acpi0017_mock.dev.bus = &platform_bus_type;

- rc = platform_device_add(cxl_acpi);
+ rc = cxl_mock_platform_device_add(cxl_acpi, NULL);
if (rc)
- goto err_root;
+ goto err_rch;

rc = cxl_mem_init();
if (rc)
diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
index 141bf63cbe05..a545b5e50b19 100644
--- a/tools/testing/selftests/mm/hmm-tests.c
+++ b/tools/testing/selftests/mm/hmm-tests.c
@@ -998,6 +998,56 @@ TEST_F(hmm, migrate)
hmm_buffer_free(buffer);
}

+/*
+ * Migrate private file memory to device private memory.
+ */
+TEST_F(hmm, migrate_file_private)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+ int fd;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ fd = hmm_create_file(size);
+ ASSERT_GE(fd, 0);
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = fd;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Initialize buffer in system memory. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Migrate memory to device. */
+ ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ hmm_buffer_free(buffer);
+}
+
/*
* Migrate anonymous memory to device private memory and fault some of it back
* to system memory, then try migrating the resulting mix of system and device
diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh
index 6c2deef673e5..c3d96e46304b 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh
@@ -412,7 +412,7 @@ do_transfer()
mptcp_lib_wait_local_port_listen "${listener_ns}" "${port}"

local start
- start=$(date +%s%3N)
+ start=$(date +%s%N)
timeout ${timeout_test} \
ip netns exec ${connector_ns} \
./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \
@@ -425,7 +425,7 @@ do_transfer()
local rets=$?

local stop
- stop=$(date +%s%3N)
+ stop=$(date +%s%N)

if $capture; then
sleep 1
@@ -441,7 +441,7 @@ do_transfer()
fi

local duration
- duration=$((stop-start))
+ duration=$(((stop-start) / 1000000))
printf "(duration %05sms) " "${duration}"
if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then
mptcp_lib_pr_fail "client exit code $retc, server $rets"
diff --git a/tools/testing/selftests/net/mptcp/mptcp_lib.sh b/tools/testing/selftests/net/mptcp/mptcp_lib.sh
index 7e26b5a7db7f..1fa926036cee 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_lib.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_lib.sh
@@ -29,7 +29,7 @@ declare -rx MPTCP_LIB_AF_INET6=10
MPTCP_LIB_SUBTESTS=()
MPTCP_LIB_SUBTESTS_DUPLICATED=0
MPTCP_LIB_SUBTEST_FLAKY=0
-MPTCP_LIB_SUBTESTS_LAST_TS_MS=
+MPTCP_LIB_SUBTESTS_LAST_TS_NS=
MPTCP_LIB_TEST_COUNTER=0
MPTCP_LIB_TEST_FORMAT="%02u %-50s"
MPTCP_LIB_IP_MPTCP=0
@@ -207,7 +207,7 @@ mptcp_lib_kversion_ge() {
}

mptcp_lib_subtests_last_ts_reset() {
- MPTCP_LIB_SUBTESTS_LAST_TS_MS="$(date +%s%3N)"
+ MPTCP_LIB_SUBTESTS_LAST_TS_NS="$(date +%s%N)"
}
mptcp_lib_subtests_last_ts_reset

@@ -226,7 +226,7 @@ __mptcp_lib_result_check_duplicated() {
__mptcp_lib_result_add() {
local result="${1}"
local time="time="
- local ts_prev_ms
+ local ts_prev_ns
shift

local id=$((${#MPTCP_LIB_SUBTESTS[@]} + 1))
@@ -236,9 +236,9 @@ __mptcp_lib_result_add() {
# not to add two '#'
[[ "${*}" != *"#"* ]] && time="# ${time}"

- ts_prev_ms="${MPTCP_LIB_SUBTESTS_LAST_TS_MS}"
+ ts_prev_ns="${MPTCP_LIB_SUBTESTS_LAST_TS_NS}"
mptcp_lib_subtests_last_ts_reset
- time+="$((MPTCP_LIB_SUBTESTS_LAST_TS_MS - ts_prev_ms))ms"
+ time+="$(((MPTCP_LIB_SUBTESTS_LAST_TS_NS - ts_prev_ns) / 1000000))ms"

MPTCP_LIB_SUBTESTS+=("${result} ${id} - ${KSFT_TEST}: ${*} ${time}")
}