From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 831E94570D4; Sun, 11 Oct 2026 13:53:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791726846; cv=none; b=vAc5/r7SN7cNK4S4MKyr0zXNbagNGDGcPOwwk8YW29r2A3HoEs5h890aO0HJYm+h8vyAQlKVSXGztZVoUQxvm3TBJpkFzydi/ED4YJVe1+wdK8EQ6rWvtMYXGURp8mx4Hn/9R/gSbzCZ90v+KuPZXBoPdObEaDrwv7VrAj8YMBQ= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791726846; c=relaxed/simple; bh=dJDZdgmbwIcv5nujGFeGgt1iOOI7BneJKRmU6tRfGok=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=euvUsgp1uCVIkL0RlWgASB8Awd14RYHbctGMS0BpmEhpC16DwlTuJOvM+DsXVkvWsVOCN9dQI2HkYqotcd3rdGjutiUSzcex1t8Yur3ijuKtRpK28k1cxxMObw13+hp4PiVg4hdWk6jjqrlpfaENpnS8mOSovYOQkYZN4Nh27uo= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=KVBDfDF9; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="KVBDfDF9" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 738BA1F0089D; Sun, 11 Oct 2026 13:53:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1791726820; bh=R9RJ4BGjtbs1RvbobasdhyCCczQRVVcvJN8D/ISEt/M=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=KVBDfDF9qaT5klv37SpLBoh3qZ3A2LrgvE4/3RE9d81xH0HroXop66H3BQUUaHI89 BQSXhv+eSim9ilikKkuN9Ik8aNboS7CTUn9POtZV9a2O29KODZOX3vBkTynA/Tlzkl 5fMpOB5j34dtZfhnIz3PR/N2dIN+b926yS/Bz5JE= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org, akpm@linux-foundation.org, torvalds@linux-foundation.org, stable@vger.kernel.org Cc: lwn@lwn.net, jslaby@suse.cz, Greg Kroah-Hartman Subject: Re: Linux 7.2.10 Date: Sun, 11 Oct 2026 15:53:28 +0200 Message-ID: <2026101128-passivism-elastic-6806@gregkh> X-Mailer: git-send-email 2.56.0 In-Reply-To: <2026101128-gave-stiffen-b868@gregkh> References: <2026101128-gave-stiffen-b868@gregkh> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst index 88b4aa45a206..826dfb90c8f7 100644 --- a/Documentation/arch/arm64/silicon-errata.rst +++ b/Documentation/arch/arm64/silicon-errata.rst @@ -174,6 +174,8 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A725 | #3456106 | ARM64_ERRATUM_3194386 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-A725 | #3821522 | ARM64_ERRATUM_3821522 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-X1 | #1502854 | N/A | +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-X1 | #3324344 | ARM64_ERRATUM_3194386 | diff --git a/Documentation/filesystems/netfs_library.rst b/Documentation/filesystems/netfs_library.rst index ddd799df6ce3..a9de281db8ee 100644 --- a/Documentation/filesystems/netfs_library.rst +++ b/Documentation/filesystems/netfs_library.rst @@ -451,6 +451,32 @@ one. The inode should be marked ``NETFS_ICTX_SINGLE_NO_UPLOAD`` if this API is to be used. The writeback function requires the buffer to be of ITER_FOLIOQ type. +Clearing Stale Post-EOF Pagecache +--------------------------------- + +When a file is extended, data left in the pagecache past the old EOF by a write +through an mmap must not be exposed as file content. Netfslib clears this on +its own write paths, and exports a helper so a filesystem can do the same from +a resize path (truncate, setattr, fallocate and the like) that holds the +inode's ``i_rwsem`` exclusively across the whole resize:: + + void netfs_clear_stale_post_isize(struct inode *inode, uoff_t from, + uoff_t to); + +This zeroes any such data within the ``[from, to)`` hole to be made, where +@from is the old EOF and @to is the new one, and the caller must have already +updated ``i_size`` to @to before calling it. Only the folio straddling @from +can hold data written past the EOF through an mmap, as pages wholly beyond the +EOF can't be faulted in, so the zeroing is limited to that folio. The folio is +zeroed rather than dropped so that a concurrent extending write can't lose +data. + +This overlaps with ``pagecache_isize_extended()`` but can't reuse it: that +helper is keyed on a sub-page block size and is a no-op when the block size is +``>= PAGE_SIZE`` (as on network filesystems), and it doesn't wait for +writeback. As both address the same problem, a change to one should probably +be reflected in the other to keep them in sync. + High-Level VM API ================== diff --git a/Makefile b/Makefile index 6f8ed7329a20..be413b167b24 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 7 PATCHLEVEL = 2 -SUBLEVEL = 9 +SUBLEVEL = 10 EXTRAVERSION = NAME = Baby Opossum Posse diff --git a/arch/arc/include/asm/atomic64-arcv2.h b/arch/arc/include/asm/atomic64-arcv2.h index 73080a664369..1ca4a1047fb3 100644 --- a/arch/arc/include/asm/atomic64-arcv2.h +++ b/arch/arc/include/asm/atomic64-arcv2.h @@ -156,6 +156,12 @@ static inline u64 __arch_cmpxchg64_relaxed(volatile void *ptr, u64 old, u64 new) } #define arch_cmpxchg64_relaxed __arch_cmpxchg64_relaxed +static inline s64 arch_atomic64_cmpxchg_relaxed(atomic64_t *v, s64 old, s64 new) +{ + return arch_cmpxchg64_relaxed(&v->counter, old, new); +} +#define arch_atomic64_cmpxchg_relaxed arch_atomic64_cmpxchg_relaxed + static inline s64 arch_atomic64_xchg(atomic64_t *ptr, s64 new) { s64 prev; diff --git a/arch/arc/include/asm/cmpxchg.h b/arch/arc/include/asm/cmpxchg.h index 76f43db0890f..66b3743c9400 100644 --- a/arch/arc/include/asm/cmpxchg.h +++ b/arch/arc/include/asm/cmpxchg.h @@ -46,7 +46,7 @@ __typeof__(*(ptr)) _n_ = (new); \ __typeof__(*(ptr)) _prev_; \ \ - switch(sizeof((_p_))) { \ + switch (sizeof(*(_p_))) { \ case 1: \ _prev_ = (__typeof__(*(ptr)))cmpxchg_emu_u8((volatile u8 *__force)_p_, (uintptr_t)_o_, (uintptr_t)_n_); \ break; \ @@ -102,7 +102,7 @@ __typeof__(ptr) _p_ = (ptr); \ __typeof__(*(ptr)) _val_ = (val); \ \ - switch(sizeof(*(_p_))) { \ + switch (sizeof(*(_p_))) { \ case 4: \ _val_ = __arch_xchg(_p_, _val_); \ break; \ diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index fc57d90d92c1..89b6a7dc6d32 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1017,9 +1017,17 @@ config ARM64_ERRATUM_1902691 If unsure, say Y. +config ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT + bool + # Targeting errata affecting AMEVCNTR01, the AMU constant counter. + # On affected CPUs the counter may not increment at the expected rate, + # making it unreliable. As a result, AMU users should treat it as + # unavailable rather than relying on misleading counter values. + config ARM64_ERRATUM_2457168 bool "Cortex-A510: 2457168: workaround for AMEVCNTR01 incrementing incorrectly" depends on ARM64_AMU_EXTN + select ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT default y help This option adds the workaround for ARM Cortex-A510 erratum 2457168. @@ -1050,6 +1058,28 @@ config ARM64_ERRATUM_2645198 If unsure, say Y. +config ARM64_ERRATUM_3821522 + bool "Cortex-A725: 3821522: workaround for possible CNT_CYCLES increment error due to WFE/WFI" + depends on ARM64_AMU_EXTN + select ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT + default y + help + This option adds the workaround for ARM Cortex-A725 erratum 3821522. + + On affected A725 cores, the CNT_CYCLES event may incur a significant + increment error when entering and subsequently exiting WFx. + As a result, the CNT_CYCLES may diverge from the system counter + frequency at which it is expected to increment. + This renders the AMU counter AMEVCNTR01, that implements CNT_CYCLES, + being unreliable and unsuitable for use. + + Since there is no hardware workaround, reads of the affected CNT_CYCLES + counter return 0 in the relevant paths. This causes users of the counter + to treat it as unavailable and is functionally equivalent to firmware + disabling the affected counter. + + If unsure, say Y. + config ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD bool diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index be47908bb9d2..f67ae51dc239 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -377,6 +377,20 @@ static const struct arm64_cpu_capabilities arm64_repeat_tlbi_list[] = { }; #endif +#ifdef CONFIG_ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT +static const struct midr_range workaround_amu_constcnt_list[] = { +#ifdef CONFIG_ARM64_ERRATUM_2457168 + /* Cortex-A510 r0p0-r1p1 */ + MIDR_RANGE(MIDR_CORTEX_A510, 0, 0, 1, 1), +#endif +#ifdef CONFIG_ARM64_ERRATUM_3821522 + /* Cortex-A725 r0p0 - r0p2 */ + MIDR_RANGE(MIDR_CORTEX_A725, 0, 0, 0, 2), +#endif + {} +}; +#endif /* CONFIG_ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT */ + #ifdef CONFIG_CAVIUM_ERRATUM_23154 static const struct midr_range cavium_erratum_23154_cpus[] = { MIDR_ALL_VERSIONS(MIDR_THUNDERX), @@ -904,14 +918,12 @@ const struct arm64_cpu_capabilities arm64_errata[] = { ERRATA_MIDR_REV_RANGE(MIDR_CORTEX_A510, 0, 0, 2) }, #endif -#ifdef CONFIG_ARM64_ERRATUM_2457168 +#ifdef CONFIG_ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT { - .desc = "ARM erratum 2457168", - .capability = ARM64_WORKAROUND_2457168, + .desc = "Broken AMU AMEVCNTR01 (const counter)", + .capability = ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT, .type = ARM64_CPUCAP_WEAK_LOCAL_CPU_FEATURE, - - /* Cortex-A510 r0p0-r1p1 */ - CAP_MIDR_RANGE(MIDR_CORTEX_A510, 0, 0, 1, 1) + CAP_MIDR_RANGE_LIST(workaround_amu_constcnt_list) }, #endif #ifdef CONFIG_ARM64_ERRATUM_2038923 diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 103e70d683ca..5dbc20d6d969 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -2087,7 +2087,7 @@ static void cpu_amu_enable(struct arm64_cpu_capabilities const *cap) cpumask_set_cpu(smp_processor_id(), &amu_cpus); /* 0 reference values signal broken/disabled counters */ - if (!this_cpu_has_cap(ARM64_WORKAROUND_2457168)) + if (!this_cpu_has_cap(ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT)) update_freq_counters_refs(); } } diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c index 38e6fa204c17..6a06a4e352b1 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c @@ -433,6 +433,7 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user) unsigned int vl, vq; struct user_fpsimd_state fpsimd; u16 user_vl, flags; + bool fpsimd_only; bool sm; if (user->sve_size < sizeof(*user->sve)) @@ -443,19 +444,33 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user) if (err) return err; + fpsimd_only = (user->sve_size == sizeof(*user->sve)); sm = flags & SVE_SIG_FLAG_SM; + if (sm) { if (!system_supports_sme()) return -EINVAL; + /* + * Streaming SVE state is always preserved with an SVE payload. + * Only accept streaming state which has an SVE payload. + */ + if (fpsimd_only) + return -EINVAL; + vl = task_get_sme_vl(current); } else { /* - * A SME only system use SVE for streaming mode so can - * have a SVE formatted context with a zero VL and no - * payload data. + * Non-streaming SVE state may be preserved without an SVE + * payload, in which case all state is saved in the FPSIMD + * context. + * + * On SME-only systems, non-streaming (FPSIMD-only) state is + * always preserved without an SVE payload, and with VL==0. On + * such systems, only accept non-streaming state without an SVE + * payload. */ - if (!system_supports_sve() && !system_supports_sme()) + if (!system_supports_sve() && !fpsimd_only) return -EINVAL; vl = task_get_sve_vl(current); @@ -464,16 +479,7 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user) if (user_vl != vl) return -EINVAL; - /* - * Non-streaming SVE state may be preserved without an SVE payload, in - * which case the SVE context only has a header with VL==0, and all - * state can be restored from the FPSIMD context. - * - * Streaming SVE state is always preserved with an SVE payload. For - * consistency and robustness, reject restoring streaming SVE state - * without an SVE payload. - */ - if (!sm && user->sve_size == sizeof(*user->sve)) + if (fpsimd_only) return restore_fpsimd_context(user); vq = sve_vq_from_vl(vl); diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index b32f13358fbb..b4f6eb213aa8 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -186,7 +186,6 @@ int arch_freq_get_on_cpu(int cpu) struct amu_cntr_sample *amu_sample; unsigned int start_cpu = cpu; unsigned long last_update; - unsigned int freq = 0; u64 scale; if (!amu_fie_cpu_supported(cpu) || !arch_scale_freq_ref(cpu)) @@ -245,9 +244,8 @@ int arch_freq_get_on_cpu(int cpu) * (see amu_scale_freq_tick for details) */ scale = arch_scale_freq_capacity(cpu); - freq = scale * arch_scale_freq_ref(cpu); - freq >>= SCHED_CAPACITY_SHIFT; - return freq; + + return (scale * arch_scale_freq_ref(cpu)) >> SCHED_CAPACITY_SHIFT; } static void amu_fie_setup(const struct cpumask *cpus) @@ -387,12 +385,13 @@ static void cpu_read_corecnt(void *val) static void cpu_read_constcnt(void *val) { /* - * Return 0 if the current CPU is affected by erratum 2457168. A value - * of 0 is also returned if the current CPU does not support AMUs or if - * the counter is disabled. A return value of 0 at counter read is - * properly handled as an error case by the users of the counter. + * Return 0 if the current CPU is affected by a HW erratum. + * A value of 0 is also returned if the current CPU does not + * support AMUs or if the counter is disabled. A return + * value of 0 at counter read is properly handled as an error + * case by the users of the counter. */ - *(u64 *)val = this_cpu_has_cap(ARM64_WORKAROUND_2457168) ? + *(u64 *)val = this_cpu_has_cap(ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT) ? 0UL : read_constcnt(); } diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile index 7dec05dd33b7..f6619e1cb2ce 100644 --- a/arch/arm64/kernel/vdso/Makefile +++ b/arch/arm64/kernel/vdso/Makefile @@ -41,7 +41,7 @@ CC_FLAGS_REMOVE_VDSO := $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) \ $(CC_FLAGS_LTO) $(CC_FLAGS_CFI) \ -Wmissing-prototypes -Wmissing-declarations -CC_FLAGS_ADD_VDSO := -O2 -mcmodel=tiny -fasynchronous-unwind-tables +CC_FLAGS_ADD_VDSO := -O2 -mcmodel=tiny -fasynchronous-unwind-tables $(CONFIG_CC_OPT_INLINE_MEMSET) CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_REMOVE_VDSO) CFLAGS_REMOVE_vgetrandom.o = $(CC_FLAGS_REMOVE_VDSO) diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index b32742d9dd73..aa5d305484d1 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2378,9 +2378,6 @@ int __init populate_nv_trap_config(void) print_nv_trap_error(fgt, "FGT bit is reserved", ret); } - if (!cpus_have_final_cap(ARM64_HAS_FGT)) - continue; - prev = xa_store(&sr_forward_xa, enc, xa_mk_value(tc.val), GFP_KERNEL); diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 53532f0c4162..714e03de24f4 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -54,6 +54,15 @@ static void pkvm_vcpu_reset_hcr(struct kvm_vcpu *vcpu) else vcpu->arch.hcr_el2 |= HCR_TID2; + /* + * Without AArch32 EL1, leave RW set and let the entry fail with an + * illegal exception return: the *32_EL2 registers EL2 would otherwise + * switch are UNDEFINED there. + */ + if (vcpu_has_feature(vcpu, KVM_ARM_VCPU_EL1_32BIT) && + cpus_have_final_cap(ARM64_HAS_32BIT_EL1)) + vcpu->arch.hcr_el2 &= ~HCR_EL2_RW; + if (vcpu_has_ptrauth(vcpu)) vcpu->arch.hcr_el2 |= (HCR_API | HCR_APK); diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 2ed0836cb929..6dd5bfc329be 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -1468,32 +1468,11 @@ transparent_hugepage_adjust(struct kvm *kvm, struct kvm_memory_slot *memslot, return PAGE_SIZE; } -static int get_vma_page_shift(struct vm_area_struct *vma, unsigned long hva) +static int get_vma_page_shift(struct vm_area_struct *vma) { - unsigned long pa; - - if (is_vm_hugetlb_page(vma) && !(vma->vm_flags & VM_PFNMAP)) + if (is_vm_hugetlb_page(vma)) return huge_page_shift(hstate_vma(vma)); - if (!(vma->vm_flags & VM_PFNMAP)) - return PAGE_SHIFT; - - VM_BUG_ON(is_vm_hugetlb_page(vma)); - - pa = (vma->vm_pgoff << PAGE_SHIFT) + (hva - vma->vm_start); - -#ifndef __PAGETABLE_PMD_FOLDED - if ((hva & (PUD_SIZE - 1)) == (pa & (PUD_SIZE - 1)) && - ALIGN_DOWN(hva, PUD_SIZE) >= vma->vm_start && - ALIGN(hva, PUD_SIZE) <= vma->vm_end) - return PUD_SHIFT; -#endif - - if ((hva & (PMD_SIZE - 1)) == (pa & (PMD_SIZE - 1)) && - ALIGN_DOWN(hva, PMD_SIZE) >= vma->vm_start && - ALIGN(hva, PMD_SIZE) <= vma->vm_end) - return PMD_SHIFT; - return PAGE_SHIFT; } @@ -1794,7 +1773,7 @@ static short kvm_s2_resolve_vma_size(const struct kvm_s2_fault_desc *s2fd, vma_shift = PAGE_SHIFT; } else { s2vi->max_map_size = PUD_SIZE; - vma_shift = get_vma_page_shift(vma, s2fd->hva); + vma_shift = get_vma_page_shift(vma); } switch (vma_shift) { @@ -1952,16 +1931,6 @@ static int kvm_s2_fault_pin_pfn(const struct kvm_s2_fault_desc *s2fd, return -EFAULT; } } else { - /* - * If the page was identified as device early by looking at - * the VMA flags, vma_pagesize is already representing the - * largest quantity we can map. If instead it was mapped - * via __kvm_faultin_pfn(), vma_pagesize is set to PAGE_SIZE - * and must not be upgraded. - * - * In both cases, we don't let transparent_hugepage_adjust() - * change things at the last minute. - */ s2vi->map_non_cacheable = true; } @@ -2051,10 +2020,10 @@ static int kvm_s2_fault_map(const struct kvm_s2_fault_desc *s2fd, /* * If we are not forced to use page mapping, check if we are - * backed by a THP and thus use block mapping if possible. + * backed by a huge stage-1 mapping and thus use block mapping if + * possible. */ - if (mapping_size == PAGE_SIZE && - !(s2vi->max_map_size == PAGE_SIZE || s2vi->map_non_cacheable)) { + if (mapping_size == PAGE_SIZE && s2vi->max_map_size != PAGE_SIZE) { if (perm_fault_granule > PAGE_SIZE) { mapping_size = perm_fault_granule; } else { @@ -2132,10 +2101,6 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd) return ret; } - /* - * Let's check if we will get back a huge page backed by hugetlbfs, or - * get block mapping for device MMIO region. - */ ret = kvm_s2_fault_pin_pfn(s2fd, &s2vi); if (ret != 1) return ret; diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 7dc5ef5a6e33..441cc279e688 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -319,12 +319,16 @@ static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq, return ret; } -static int update_affinity(struct vgic_irq *irq, struct kvm_vcpu *vcpu) +static int update_affinity(struct vgic_irq *irq, + struct kvm_vcpu *from_vcpu, struct kvm_vcpu *vcpu) { struct its_vlpi_map map; int ret; guard(raw_spinlock_irqsave)(&irq->irq_lock); + if (from_vcpu && irq->target_vcpu != from_vcpu) + return 0; + irq->target_vcpu = vcpu; if (!irq->hw) @@ -362,7 +366,7 @@ static void update_affinity_ite(struct kvm *kvm, struct its_ite *ite) return; vcpu = collection_to_vcpu(kvm, ite->collection); - update_affinity(ite->irq, vcpu); + update_affinity(ite->irq, NULL, vcpu); } /* @@ -856,7 +860,7 @@ static int vgic_its_cmd_handle_movi(struct kvm *kvm, struct vgic_its *its, vgic_its_invalidate_cache(its); - return update_affinity(ite->irq, vcpu); + return update_affinity(ite->irq, NULL, vcpu); } static bool __is_visible_gfn_locked(struct vgic_its *its, gpa_t gpa) @@ -1383,7 +1387,7 @@ static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its, if (!irq) continue; - update_affinity(irq, vcpu2); + update_affinity(irq, vcpu1, vcpu2); vgic_put_irq(kvm, irq); } diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c index 7182f63fc938..70cc53ba810a 100644 --- a/arch/arm64/kvm/vgic/vgic-v2.c +++ b/arch/arm64/kvm/vgic/vgic-v2.c @@ -122,6 +122,10 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu) for (int lr = 0; lr < vgic_cpu->vgic_v2.used_lrs; lr++) vgic_v2_fold_lr(vcpu, cpuif->vgic_lr[lr]); + cpuif->used_lrs = 0; + if (!irq) + return; + /* See the GICv3 equivalent for the EOIcount handling rationale */ list_for_each_entry_continue(irq, &vgic_cpu->ap_list_head, ap_list) { u32 lr; @@ -144,8 +148,6 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu) vgic_v2_fold_lr(vcpu, lr); eoicount--; } - - cpuif->used_lrs = 0; } void vgic_v2_deactivate(struct kvm_vcpu *vcpu, u32 val) diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c index 0e1c121766ae..f9284dfb47f1 100644 --- a/arch/arm64/kvm/vgic/vgic-v3.c +++ b/arch/arm64/kvm/vgic/vgic-v3.c @@ -155,6 +155,10 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu) for (int lr = 0; lr < cpuif->used_lrs; lr++) vgic_v3_fold_lr(vcpu, cpuif->vgic_lr[lr]); + cpuif->used_lrs = 0; + if (!irq) + return; + /* * EOIMode=0: use EOIcount to emulate deactivation. We are * guaranteed to deactivate in reverse order of the activation, so @@ -188,8 +192,6 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu) vgic_v3_fold_lr(vcpu, lr); eoicount--; } - - cpuif->used_lrs = 0; } void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val) diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 74bace10a22e..d3427cd37882 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -850,6 +850,17 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu) goto retry; } + /* + * Fix the last_lr_irq refcount which was obtained while + * populating the LRs. This can also result in the LPI being + * deleted. + */ + irq = *host_data_ptr(last_lr_irq); + if (irq) { + deleted_lpis |= vgic_put_irq_norelease(vcpu->kvm, irq); + *host_data_ptr(last_lr_irq) = NULL; + } + raw_spin_unlock(&vgic_cpu->ap_list_lock); if (unlikely(deleted_lpis)) @@ -863,9 +874,6 @@ static void vgic_fold_state(struct kvm_vcpu *vcpu) return; } - if (!*host_data_ptr(last_lr_irq)) - return; - if (kvm_vgic_global_state.type == VGIC_V2) vgic_v2_fold_lr_state(vcpu); else @@ -1018,11 +1026,14 @@ static void vgic_flush_lr_state(struct kvm_vcpu *vcpu) scoped_guard(raw_spinlock, &irq->irq_lock) { if (likely(vgic_target_oracle(irq) == vcpu)) { vgic_populate_lr(vcpu, irq, count++); - *host_data_ptr(last_lr_irq) = irq; + if (count == kvm_vgic_global_state.nr_lr) { + vgic_get_irq_ref(irq); + *host_data_ptr(last_lr_irq) = irq; + } } } - if (count == kvm_vgic_global_state.nr_lr) + if (*host_data_ptr(last_lr_irq)) break; } diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index d4de88770ecf..1a5cf32a71fc 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -2245,7 +2245,7 @@ pte_t modify_prot_start_ptes(struct vm_area_struct *vma, unsigned long addr, * in cases where cpu is affected with errata #2645198. */ if (pte_accessible(vma->vm_mm, pte) && pte_user_exec(pte)) - __flush_tlb_range(vma, addr, nr * PAGE_SIZE, + __flush_tlb_range(vma, addr, addr + nr * PAGE_SIZE, PAGE_SIZE, 3, TLBF_NOWALKCACHE); } diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index 8c0c9b0fda16..4405b06c0356 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -2358,10 +2358,11 @@ bool bpf_jit_supports_subprog_tailcalls(void) return true; } -static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_node *node, - int bargs_off, int retval_off, int run_ctx_off, - bool save_ret) +static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_image *im, + struct bpf_tramp_node *node, int bargs_off, + int retval_off, int run_ctx_off, bool save_ret) { + void *skip; __le32 *branch; u64 enter_prog; u64 exit_prog; @@ -2371,6 +2372,10 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_node *node, enter_prog = (u64)bpf_trampoline_enter(p); exit_prog = (u64)bpf_trampoline_exit(p); + /* nop, patched to skip this prog when it is detached */ + skip = ctx->ro_image + ctx->idx; + emit(A64_NOP, ctx); + if (node->cookie == 0) { /* if cookie is zero, one instruction is enough to store it */ emit(A64_STR64I(A64_ZR, A64_SP, run_ctx_off + cookie_off), ctx); @@ -2423,11 +2428,13 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_node *node, emit(A64_ADD_I(1, A64_R(2), A64_SP, run_ctx_off), ctx); emit_call(exit_prog, ctx); + + bpf_tramp_image_add_skip(im, p, skip, ctx->ro_image + ctx->idx); } -static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_nodes *tn, - int bargs_off, int retval_off, int run_ctx_off, - __le32 **branches) +static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_image *im, + struct bpf_tramp_nodes *tn, int bargs_off, + int retval_off, int run_ctx_off, __le32 **branches) { int i; @@ -2436,7 +2443,7 @@ static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_nodes *tn, */ emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx); for (i = 0; i < tn->nr_nodes; i++) { - invoke_bpf_prog(ctx, tn->nodes[i], bargs_off, retval_off, + invoke_bpf_prog(ctx, im, tn->nodes[i], bargs_off, retval_off, run_ctx_off, true); /* if (*(u64 *)(sp + retval_off) != 0) * goto do_fexit; @@ -2771,7 +2778,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, store_func_meta(ctx, meta, func_meta_off); cookie_bargs_off--; } - invoke_bpf_prog(ctx, fentry->nodes[i], bargs_off, + invoke_bpf_prog(ctx, im, fentry->nodes[i], bargs_off, retval_off, run_ctx_off, flags & BPF_TRAMP_F_RET_FENTRY_RET); } @@ -2782,7 +2789,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, if (!branches) return -ENOMEM; - invoke_bpf_mod_ret(ctx, fmod_ret, bargs_off, retval_off, + invoke_bpf_mod_ret(ctx, im, fmod_ret, bargs_off, retval_off, run_ctx_off, branches); } @@ -2795,9 +2802,6 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, emit(A64_RET(A64_R(10)), ctx); /* store return value */ emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx); - /* reserve a nop for bpf_tramp_image_put */ - im->ip_after_call = ctx->ro_image + ctx->idx; - emit(A64_NOP, ctx); } /* update the branches saved in invoke_bpf_mod_ret with cbnz */ @@ -2819,12 +2823,11 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, store_func_meta(ctx, meta, func_meta_off); cookie_bargs_off--; } - invoke_bpf_prog(ctx, fexit->nodes[i], bargs_off, retval_off, + invoke_bpf_prog(ctx, im, fexit->nodes[i], bargs_off, retval_off, run_ctx_off, false); } if (flags & BPF_TRAMP_F_CALL_ORIG) { - im->ip_epilogue = ctx->ro_image + ctx->idx; /* for the first pass, assume the worst case */ if (!ctx->image) ctx->idx += 4; @@ -2883,7 +2886,7 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, .image = NULL, .idx = 0, }; - struct bpf_tramp_image im; + struct bpf_tramp_image im = {}; struct arg_aux aaux; int ret; @@ -3170,6 +3173,10 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t, * longer reachable, since bpf_tramp_image_put() function already * uses percpu_ref and task-based rcu to do the sync, no need to call * the sync version here, see bpf_tramp_image_put() for details. + * + * 3. when a detached prog is patched out of a trampoline, a CPU that + * still executes the old nop calls the prog before it went through + * a quiescent state, and the prog is freed after grace periods. */ ret = aarch64_insn_patch_text_nosync(ip, new_insn); out: diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index 9b85a84f6fd4..050175dfa324 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -103,7 +103,6 @@ WORKAROUND_1902691 WORKAROUND_2038923 WORKAROUND_2064142 WORKAROUND_2077057 -WORKAROUND_2457168 WORKAROUND_2645198 WORKAROUND_2658417 WORKAROUND_4193714 @@ -128,3 +127,4 @@ WORKAROUND_REPEAT_TLBI WORKAROUND_SPECULATIVE_AT WORKAROUND_SPECULATIVE_SSBS WORKAROUND_SPECULATIVE_UNPRIV_LOAD +WORKAROUND_BROKEN_AMU_CONSTCNT diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index 45eee761df9e..1b5ddb964255 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -1580,6 +1580,24 @@ void *bpf_arch_text_copy(void *dst, void *src, size_t len) return ret ? ERR_PTR(-EINVAL) : dst; } +int arch_bpf_trampoline_skip(void *nop, void *target) +{ + u32 old_insn = INSN_NOP; + u32 new_insn = larch_insn_gen_b((unsigned long)nop, (unsigned long)target); + int ret; + + if (memcmp(nop, &old_insn, LOONGARCH_INSN_SIZE)) + return -EFAULT; + + cpus_read_lock(); + mutex_lock(&text_mutex); + ret = larch_insn_text_copy(nop, &new_insn, LOONGARCH_INSN_SIZE); + mutex_unlock(&text_mutex); + cpus_read_unlock(); + + return ret; +} + int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t, enum bpf_text_poke_type new_t, void *old_addr, void *new_addr) @@ -1698,13 +1716,18 @@ static void restore_stk_args(struct jit_ctx *ctx, int nr_stk_args, int args_off, } } -static int invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_node *n, - int args_off, int retval_off, int run_ctx_off, bool save_ret) +static int invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_image *im, + struct bpf_tramp_node *n, int args_off, int retval_off, + int run_ctx_off, bool save_ret) { int ret; u32 *branch; struct bpf_prog *p = n->link->prog; int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie); + void *skip = ctx->ro_image + ctx->idx; + + /* nop, patched to a b over this prog when it is detached */ + emit_insn(ctx, nop); if (n->cookie) emit_store_stack_imm64(ctx, LOONGARCH_GPR_T1, @@ -1757,13 +1780,17 @@ static int invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_node *n, /* arg3: &run_ctx */ emit_insn(ctx, addid, LOONGARCH_GPR_A2, LOONGARCH_GPR_FP, -run_ctx_off); ret = emit_call(ctx, (const u64)bpf_trampoline_exit(p)); + if (ret) + return ret; - return ret; + bpf_tramp_image_add_skip(im, p, skip, ctx->ro_image + ctx->idx); + return 0; } -static int invoke_bpf(struct jit_ctx *ctx, struct bpf_tramp_nodes *tn, - int args_off, int retval_off, int run_ctx_off, - int func_meta_off, bool save_ret, u64 func_meta, int cookie_off) +static int invoke_bpf(struct jit_ctx *ctx, struct bpf_tramp_image *im, + struct bpf_tramp_nodes *tn, int args_off, int retval_off, + int run_ctx_off, int func_meta_off, bool save_ret, + u64 func_meta, int cookie_off) { int i, cur_cookie = (cookie_off - args_off) / 8; @@ -1776,7 +1803,8 @@ static int invoke_bpf(struct jit_ctx *ctx, struct bpf_tramp_nodes *tn, emit_store_stack_imm64(ctx, LOONGARCH_GPR_T1, -func_meta_off, meta); cur_cookie--; } - err = invoke_bpf_prog(ctx, tn->nodes[i], args_off, retval_off, run_ctx_off, save_ret); + err = invoke_bpf_prog(ctx, im, tn->nodes[i], args_off, retval_off, + run_ctx_off, save_ret); if (err) return err; } @@ -2019,7 +2047,7 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i } if (fentry->nr_nodes) { - ret = invoke_bpf(ctx, fentry, args_off, retval_off, run_ctx_off, func_meta_off, + ret = invoke_bpf(ctx, im, fentry, args_off, retval_off, run_ctx_off, func_meta_off, flags & BPF_TRAMP_F_RET_FENTRY_RET, func_meta, cookie_off); if (ret) return ret; @@ -2031,7 +2059,7 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i emit_insn(ctx, std, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_FP, -retval_off); for (i = 0; i < fmod_ret->nr_nodes; i++) { - ret = invoke_bpf_prog(ctx, fmod_ret->nodes[i], + ret = invoke_bpf_prog(ctx, im, fmod_ret->nodes[i], args_off, retval_off, run_ctx_off, true); if (ret) goto out; @@ -2053,10 +2081,6 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i goto out; emit_insn(ctx, std, LOONGARCH_GPR_A0, LOONGARCH_GPR_FP, -retval_off); emit_insn(ctx, std, regmap[BPF_REG_0], LOONGARCH_GPR_FP, -(retval_off - 8)); - im->ip_after_call = ctx->ro_image + ctx->idx; - /* Reserve space for the move_imm + jirl instruction */ - for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) - emit_insn(ctx, nop); } for (i = 0; ctx->image && i < fmod_ret->nr_nodes; i++) { @@ -2070,14 +2094,13 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i emit_store_stack_imm64(ctx, LOONGARCH_GPR_T1, -func_meta_off, func_meta); if (fexit->nr_nodes) { - ret = invoke_bpf(ctx, fexit, args_off, retval_off, run_ctx_off, + ret = invoke_bpf(ctx, im, fexit, args_off, retval_off, run_ctx_off, func_meta_off, false, func_meta, cookie_off); if (ret) goto out; } if (flags & BPF_TRAMP_F_CALL_ORIG) { - im->ip_epilogue = ctx->ro_image + ctx->idx; move_addr(ctx, LOONGARCH_GPR_A0, (const u64)im); ret = emit_call(ctx, (const u64)__bpf_tramp_exit); if (ret) @@ -2179,11 +2202,8 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, struct bpf_tramp_nodes *tnodes, void *func_addr) { int ret; - struct jit_ctx ctx; - struct bpf_tramp_image im; - - ctx.image = NULL; - ctx.idx = 0; + struct jit_ctx ctx = {}; + struct bpf_tramp_image im = {}; ret = __arch_prepare_bpf_trampoline(&ctx, &im, m, tnodes, func_addr, flags); diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile index 9c9181bb4071..0b84892d084b 100644 --- a/arch/loongarch/vdso/Makefile +++ b/arch/loongarch/vdso/Makefile @@ -16,6 +16,7 @@ ccflags-vdso := \ $(filter -m64,$(KBUILD_CFLAGS)) \ $(filter -march=%,$(KBUILD_CFLAGS)) \ $(filter -m%-float,$(KBUILD_CFLAGS)) \ + $(CONFIG_CC_OPT_INLINE_MEMSET) \ $(CLANG_FLAGS) \ -D__VDSO__ diff --git a/arch/powerpc/kernel/vdso/Makefile b/arch/powerpc/kernel/vdso/Makefile index 368759f81708..0d1a49529885 100644 --- a/arch/powerpc/kernel/vdso/Makefile +++ b/arch/powerpc/kernel/vdso/Makefile @@ -43,6 +43,7 @@ ccflags-y := -fno-common -fno-builtin -DBUILD_VDSO ccflags-y += $(DISABLE_LATENT_ENTROPY_PLUGIN) ccflags-y += $(call cc-option, -fno-stack-protector) ccflags-y += -DDISABLE_BRANCH_PROFILING +ccflags-y += $(CONFIG_CC_OPT_INLINE_MEMSET) ccflags-y += -ffreestanding -fasynchronous-unwind-tables ccflags-remove-y := $(CC_FLAGS_FTRACE) ldflags-y := -Wl,--hash-style=both -nostdlib -shared -z noexecstack $(CLANG_FLAGS) diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 7b07b43575f1..7a612688e7a2 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -602,14 +602,18 @@ int arch_protect_bpf_trampoline(void *image, unsigned int size) } static int invoke_bpf_prog(u32 *image, u32 *ro_image, struct codegen_context *ctx, - struct bpf_tramp_node *n, int regs_off, int retval_off, - int run_ctx_off, bool save_ret) + struct bpf_tramp_image *im, struct bpf_tramp_node *n, + int regs_off, int retval_off, int run_ctx_off, bool save_ret) { struct bpf_prog *p = n->link->prog; ppc_inst_t branch_insn; - u32 jmp_idx; + u32 jmp_idx, skip_idx; int ret = 0; + /* nop, patched to skip this prog when it is detached */ + skip_idx = ctx->idx; + EMIT(PPC_RAW_NOP()); + /* Save cookie */ if (IS_ENABLED(CONFIG_PPC64)) { PPC_LI64(_R3, n->cookie); @@ -679,13 +683,17 @@ static int invoke_bpf_prog(u32 *image, u32 *ro_image, struct codegen_context *ct EMIT(PPC_RAW_ADDI(_R5, _R1, run_ctx_off)); ret = bpf_jit_emit_func_call_rel(image, ro_image, ctx, (unsigned long)bpf_trampoline_exit(p)); + if (ret) + return ret; - return ret; + if (ro_image) /* image is NULL for dummy pass */ + bpf_tramp_image_add_skip(im, p, &ro_image[skip_idx], &ro_image[ctx->idx]); + return 0; } static int invoke_bpf_mod_ret(u32 *image, u32 *ro_image, struct codegen_context *ctx, - struct bpf_tramp_nodes *tn, int regs_off, int retval_off, - int run_ctx_off, u32 *branches) + struct bpf_tramp_image *im, struct bpf_tramp_nodes *tn, + int regs_off, int retval_off, int run_ctx_off, u32 *branches) { int i; @@ -696,8 +704,8 @@ static int invoke_bpf_mod_ret(u32 *image, u32 *ro_image, struct codegen_context EMIT(PPC_RAW_LI(_R3, 0)); EMIT(PPC_RAW_STL(_R3, _R1, retval_off)); for (i = 0; i < tn->nr_nodes; i++) { - if (invoke_bpf_prog(image, ro_image, ctx, tn->nodes[i], regs_off, retval_off, - run_ctx_off, true)) + if (invoke_bpf_prog(image, ro_image, ctx, im, tn->nodes[i], regs_off, + retval_off, run_ctx_off, true)) return -EINVAL; /* @@ -1043,8 +1051,8 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im cookie_ctx_off--; } - if (invoke_bpf_prog(image, ro_image, ctx, fentry->nodes[i], regs_off, retval_off, - run_ctx_off, flags & BPF_TRAMP_F_RET_FENTRY_RET)) + if (invoke_bpf_prog(image, ro_image, ctx, im, fentry->nodes[i], regs_off, + retval_off, run_ctx_off, flags & BPF_TRAMP_F_RET_FENTRY_RET)) return -EINVAL; } @@ -1053,7 +1061,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im if (!branches) return -ENOMEM; - if (invoke_bpf_mod_ret(image, ro_image, ctx, fmod_ret, regs_off, retval_off, + if (invoke_bpf_mod_ret(image, ro_image, ctx, im, fmod_ret, regs_off, retval_off, run_ctx_off, branches)) { ret = -EINVAL; goto cleanup; @@ -1090,11 +1098,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im /* Restore updated tail_call_cnt */ if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) bpf_trampoline_restore_tail_call_cnt(image, ctx, bpf_frame_size, r4_off); - - /* Reserve space to patch branch instruction to skip fexit progs */ - if (ro_image) /* image is NULL for dummy pass */ - im->ip_after_call = &((u32 *)ro_image)[ctx->idx]; - EMIT(PPC_RAW_NOP()); } /* Update branches saved in invoke_bpf_mod_ret with address of do_fexit */ @@ -1123,16 +1126,14 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im cookie_ctx_off--; } - if (invoke_bpf_prog(image, ro_image, ctx, fexit->nodes[i], regs_off, retval_off, - run_ctx_off, false)) { + if (invoke_bpf_prog(image, ro_image, ctx, im, fexit->nodes[i], regs_off, + retval_off, run_ctx_off, false)) { ret = -EINVAL; goto cleanup; } } if (flags & BPF_TRAMP_F_CALL_ORIG) { - if (ro_image) /* image is NULL for dummy pass */ - im->ip_epilogue = &((u32 *)ro_image)[ctx->idx]; PPC_LI_ADDR(_R3, im); ret = bpf_jit_emit_func_call_rel(image, ro_image, ctx, (unsigned long)__bpf_tramp_exit); @@ -1192,7 +1193,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, struct bpf_tramp_nodes *tnodes, void *func_addr) { - struct bpf_tramp_image im; + struct bpf_tramp_image im = {}; int ret; ret = __arch_prepare_bpf_trampoline(&im, NULL, NULL, NULL, m, flags, tnodes, func_addr); @@ -1320,7 +1321,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t, /* * If we are not poking at bpf prog entry, then we are simply patching in/out - * an unconditional branch instruction at im->ip_after_call + * an unconditional branch instruction in a trampoline image */ if (offset) { if (old_t == BPF_MOD_CALL || new_t == BPF_MOD_CALL) { diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c index 1d2db6d3e226..31be1fd5ab18 100644 --- a/arch/powerpc/perf/imc-pmu.c +++ b/arch/powerpc/perf/imc-pmu.c @@ -1275,6 +1275,8 @@ static int trace_imc_prepare_sample(struct trace_imc_data *mem, struct perf_event_header *header, struct perf_event *event) { + u16 misc = 0; + /* Sanity checks for a valid record */ if (be64_to_cpu(READ_ONCE(mem->tb1)) > *prev_tb) *prev_tb = be64_to_cpu(READ_ONCE(mem->tb1)); @@ -1289,23 +1291,19 @@ static int trace_imc_prepare_sample(struct trace_imc_data *mem, data->ip = be64_to_cpu(READ_ONCE(mem->ip)); data->period = event->hw.last_period; - header->type = PERF_RECORD_SAMPLE; - header->size = sizeof(*header) + event->header_size; - header->misc = 0; - if (cpu_has_feature(CPU_FTR_ARCH_31)) { switch (IMC_TRACE_RECORD_VAL_HVPR(be64_to_cpu(READ_ONCE(mem->val)))) { case 0:/* when MSR HV and PR not set in the trace-record */ - header->misc |= PERF_RECORD_MISC_GUEST_KERNEL; + misc |= PERF_RECORD_MISC_GUEST_KERNEL; break; case 1: /* MSR HV is 0 and PR is 1 */ - header->misc |= PERF_RECORD_MISC_GUEST_USER; + misc |= PERF_RECORD_MISC_GUEST_USER; break; case 2: /* MSR HV is 1 and PR is 0 */ - header->misc |= PERF_RECORD_MISC_KERNEL; + misc |= PERF_RECORD_MISC_KERNEL; break; case 3: /* MSR HV is 1 and PR is 1 */ - header->misc |= PERF_RECORD_MISC_USER; + misc |= PERF_RECORD_MISC_USER; break; default: pr_info("IMC: Unable to set the flag based on MSR bits\n"); @@ -1313,11 +1311,14 @@ static int trace_imc_prepare_sample(struct trace_imc_data *mem, } } else { if (is_kernel_addr(data->ip)) - header->misc |= PERF_RECORD_MISC_KERNEL; + misc |= PERF_RECORD_MISC_KERNEL; else - header->misc |= PERF_RECORD_MISC_USER; + misc |= PERF_RECORD_MISC_USER; } - perf_event_header__init_id(header, data, event); + perf_event_header__init(header, data, + PERF_RECORD_SAMPLE, misc, + sizeof(*header) + event->header_size, + event); return 0; } diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index 43ee881f6c6f..10cd97e28262 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile @@ -36,6 +36,7 @@ endif ccflags-y := -fno-stack-protector ccflags-y += -DDISABLE_BRANCH_PROFILING ccflags-y += -fno-builtin +ccflags-y += $(CONFIG_CC_OPT_INLINE_MEMSET) ccflags-y += $(KBUILD_BASE_ISA)$(CFI_MARCH) ccflags-y += $(CFI_FULL) asflags-y += $(KBUILD_BASE_ISA)$(CFI_MARCH) diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c index a3af5070ac66..0b66d2753d87 100644 --- a/arch/riscv/net/bpf_jit_comp64.c +++ b/arch/riscv/net/bpf_jit_comp64.c @@ -851,6 +851,24 @@ static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call) return emit_jump_and_link(is_call ? RV_REG_T0 : RV_REG_ZERO, rvoff, false, &ctx); } +int arch_bpf_trampoline_skip(void *nop, void *target) +{ + u32 old_insn = rv_nop(); + u32 new_insn = rv_jal(RV_REG_ZERO, (target - nop) >> 1); + int ret; + + if (memcmp(nop, &old_insn, sizeof(old_insn))) + return -EFAULT; + + cpus_read_lock(); + mutex_lock(&text_mutex); + ret = patch_text(nop, &new_insn, sizeof(new_insn)); + mutex_unlock(&text_mutex); + cpus_read_unlock(); + + return ret; +} + int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t, enum bpf_text_poke_type new_t, void *old_addr, void *new_addr) @@ -933,12 +951,17 @@ static void emit_store_stack_imm64(u8 reg, int stack_off, u64 imm64, emit_sd(RV_REG_FP, stack_off, reg, ctx); } -static int invoke_bpf_prog(struct bpf_tramp_node *node, int args_off, int retval_off, - int run_ctx_off, bool save_ret, struct rv_jit_context *ctx) +static int invoke_bpf_prog(struct bpf_tramp_image *im, struct bpf_tramp_node *node, + int args_off, int retval_off, int run_ctx_off, bool save_ret, + struct rv_jit_context *ctx) { int ret, branch_off; struct bpf_prog *p = node->link->prog; int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie); + void *skip = ctx->ro_insns + ctx->ninsns; + + /* nop, patched to a jal over this prog when it is detached */ + emit(rv_nop(), ctx); if (node->cookie) emit_store_stack_imm64(RV_REG_T1, -run_ctx_off + cookie_off, node->cookie, ctx); @@ -991,13 +1014,17 @@ static int invoke_bpf_prog(struct bpf_tramp_node *node, int args_off, int retval /* arg3: &run_ctx */ emit_addi(RV_REG_A2, RV_REG_FP, -run_ctx_off, ctx); ret = emit_call((const u64)bpf_trampoline_exit(p), true, ctx); + if (ret) + return ret; - return ret; + bpf_tramp_image_add_skip(im, p, skip, ctx->ro_insns + ctx->ninsns); + return 0; } -static int invoke_bpf(struct bpf_tramp_nodes *tn, int args_off, int retval_off, - int run_ctx_off, int func_meta_off, bool save_ret, u64 func_meta, - int cookie_off, struct rv_jit_context *ctx) +static int invoke_bpf(struct bpf_tramp_image *im, struct bpf_tramp_nodes *tn, + int args_off, int retval_off, int run_ctx_off, int func_meta_off, + bool save_ret, u64 func_meta, int cookie_off, + struct rv_jit_context *ctx) { int i, cur_cookie = (cookie_off - args_off) / 8; @@ -1010,8 +1037,8 @@ static int invoke_bpf(struct bpf_tramp_nodes *tn, int args_off, int retval_off, emit_store_stack_imm64(RV_REG_T1, -func_meta_off, meta, ctx); cur_cookie--; } - err = invoke_bpf_prog(tn->nodes[i], args_off, retval_off, run_ctx_off, - save_ret, ctx); + err = invoke_bpf_prog(im, tn->nodes[i], args_off, retval_off, + run_ctx_off, save_ret, ctx); if (err) return err; } @@ -1187,7 +1214,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, } if (fentry->nr_nodes) { - ret = invoke_bpf(fentry, args_off, retval_off, run_ctx_off, func_meta_off, + ret = invoke_bpf(im, fentry, args_off, retval_off, run_ctx_off, func_meta_off, flags & BPF_TRAMP_F_RET_FENTRY_RET, func_meta, cookie_off, ctx); if (ret) return ret; @@ -1201,7 +1228,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, /* cleanup to avoid garbage return value confusion */ emit_sd(RV_REG_FP, -retval_off, RV_REG_ZERO, ctx); for (i = 0; i < fmod_ret->nr_nodes; i++) { - ret = invoke_bpf_prog(fmod_ret->nodes[i], args_off, retval_off, + ret = invoke_bpf_prog(im, fmod_ret->nodes[i], args_off, retval_off, run_ctx_off, true, ctx); if (ret) goto out; @@ -1222,10 +1249,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, goto out; emit_sd(RV_REG_FP, -retval_off, RV_REG_A0, ctx); emit_sd(RV_REG_FP, -(retval_off - 8), regmap[BPF_REG_0], ctx); - im->ip_after_call = ctx->ro_insns + ctx->ninsns; - /* 2 nops reserved for auipc+jalr pair */ - emit(rv_nop(), ctx); - emit(rv_nop(), ctx); } /* update branches saved in invoke_bpf_mod_ret with bnez */ @@ -1241,14 +1264,13 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, emit_store_stack_imm64(RV_REG_T1, -func_meta_off, func_meta, ctx); if (fexit->nr_nodes) { - ret = invoke_bpf(fexit, args_off, retval_off, run_ctx_off, func_meta_off, + ret = invoke_bpf(im, fexit, args_off, retval_off, run_ctx_off, func_meta_off, false, func_meta, cookie_off, ctx); if (ret) goto out; } if (flags & BPF_TRAMP_F_CALL_ORIG) { - im->ip_epilogue = ctx->ro_insns + ctx->ninsns; emit_imm(RV_REG_A0, ctx->insns ? (const s64)im : RV_MAX_COUNT_IMM, ctx); ret = emit_call((const u64)__bpf_tramp_exit, true, ctx); if (ret) @@ -1306,7 +1328,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, struct bpf_tramp_nodes *tnodes, void *func_addr) { - struct bpf_tramp_image im; + struct bpf_tramp_image im = {}; struct rv_jit_context ctx; int ret; diff --git a/arch/s390/kernel/vdso/Makefile b/arch/s390/kernel/vdso/Makefile index 35c834b895ec..54bcf2984ca1 100644 --- a/arch/s390/kernel/vdso/Makefile +++ b/arch/s390/kernel/vdso/Makefile @@ -29,6 +29,7 @@ KBUILD_CFLAGS_VDSO := $(filter-out -munaligned-symbols,$(KBUILD_CFLAGS_VDSO)) KBUILD_CFLAGS_VDSO := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_VDSO)) KBUILD_CFLAGS_VDSO += -fPIC -fno-common -fno-builtin -fasynchronous-unwind-tables KBUILD_CFLAGS_VDSO += -fno-stack-protector $(DISABLE_KSTACK_ERASE) +KBUILD_CFLAGS_VDSO += $(CONFIG_CC_OPT_INLINE_MEMSET) ldflags-y := -shared -soname=linux-vdso.so.1 \ --hash-style=both --build-id=sha1 \ $(call ld-option, --eh-frame-hdr) -T diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index 408ae09be3a5..3e564e1d4e7a 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c @@ -2529,6 +2529,8 @@ struct bpf_tramp_jit { int r14_off; /* Offset of saved %r14, has to be at the * bottom */ int do_fexit; /* do_fexit: label */ + int skip[BPF_MAX_TRAMP_LINKS]; /* skip: labels after each prog */ + int nr_progs; }; static void load_imm64(struct bpf_jit *jit, int dst_reg, u64 val) @@ -2547,6 +2549,7 @@ static void emit_store_stack_imm64(struct bpf_jit *jit, int tmp_reg, int stack_o } static int invoke_bpf_prog(struct bpf_tramp_jit *tjit, + struct bpf_tramp_image *im, const struct btf_func_model *m, struct bpf_tramp_node *node, bool save_ret) { @@ -2554,8 +2557,20 @@ static int invoke_bpf_prog(struct bpf_tramp_jit *tjit, int cookie_off = tjit->run_ctx_off + offsetof(struct bpf_tramp_run_ctx, bpf_cookie); struct bpf_prog *p = node->link->prog; + void *skip = jit->prg_buf + jit->prg; + int idx = tjit->nr_progs++; int patch; + if (idx >= ARRAY_SIZE(tjit->skip)) + return -E2BIG; + + /* + * nop, patched to skip this prog when it is detached + */ + + /* brcl 0,skip */ + EMIT6_PCREL_RILC(0xc0040000, 0, tjit->skip[idx]); + /* * run_ctx.cookie = node->cookie; */ @@ -2615,10 +2630,15 @@ static int invoke_bpf_prog(struct bpf_tramp_jit *tjit, /* brasl %r14,__bpf_prog_exit */ EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14, bpf_trampoline_exit(p)); + /* skip: */ + tjit->skip[idx] = jit->prg; + bpf_tramp_image_add_skip(im, p, skip, jit->prg_buf + jit->prg); + return 0; } static int invoke_bpf(struct bpf_tramp_jit *tjit, + struct bpf_tramp_image *im, const struct btf_func_model *m, struct bpf_tramp_nodes *tn, bool save_ret, u64 func_meta, int cookie_off) @@ -2633,7 +2653,7 @@ static int invoke_bpf(struct bpf_tramp_jit *tjit, emit_store_stack_imm64(jit, REG_0, tjit->func_meta_off, meta); cur_cookie--; } - if (invoke_bpf_prog(tjit, m, tn->nodes[i], save_ret)) + if (invoke_bpf_prog(tjit, im, m, tn->nodes[i], save_ret)) return -EINVAL; } @@ -2675,6 +2695,11 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, u64 func_meta; int i, j; + /* The skip labels are taken from the previous pass. */ + tjit->nr_progs = 0; + if (im) + im->nr_skips = 0; + /* Support as many stack arguments as "mvc" instruction can handle. */ nr_reg_args = min_t(int, m->nr_args, MAX_NR_REG_ARGS); nr_stack_args = m->nr_args - nr_reg_args; @@ -2836,7 +2861,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, emit_store_stack_imm64(jit, REG_0, tjit->retval_off, 0); } - if (invoke_bpf(tjit, m, fentry, flags & BPF_TRAMP_F_RET_FENTRY_RET, + if (invoke_bpf(tjit, im, m, fentry, flags & BPF_TRAMP_F_RET_FENTRY_RET, func_meta, cookie_off)) return -EINVAL; @@ -2850,7 +2875,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, 0xf000 | tjit->retval_off); for (i = 0; i < fmod_ret->nr_nodes; i++) { - if (invoke_bpf_prog(tjit, m, fmod_ret->nodes[i], true)) + if (invoke_bpf_prog(tjit, im, m, fmod_ret->nodes[i], true)) return -EINVAL; /* @@ -2904,15 +2929,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, /* mvc tccnt_off(%r15),tail_call_cnt(4,%r15) */ _EMIT6(0xd203f000 | tjit->tccnt_off, 0xf000 | offsetof(struct prog_frame, tail_call_cnt)); - - im->ip_after_call = jit->prg_buf + jit->prg; - - /* - * The following nop will be patched by bpf_tramp_image_put(). - */ - - /* brcl 0,im->ip_epilogue */ - EMIT6_PCREL_RILC(0xc0040000, 0, (u64)im->ip_epilogue); } /* Set the "is_return" flag for fsession. */ @@ -2923,12 +2939,10 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, /* do_fexit: */ tjit->do_fexit = jit->prg; - if (invoke_bpf(tjit, m, fexit, false, func_meta, cookie_off)) + if (invoke_bpf(tjit, im, m, fexit, false, func_meta, cookie_off)) return -EINVAL; if (flags & BPF_TRAMP_F_CALL_ORIG) { - im->ip_epilogue = jit->prg_buf + jit->prg; - /* * __bpf_tramp_exit(im); */ @@ -2977,7 +2991,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, struct bpf_tramp_nodes *tnodes, void *orig_call) { - struct bpf_tramp_image im; + struct bpf_tramp_image im = {}; struct bpf_tramp_jit tjit; int ret; diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c index f522fff9231e..1903ed8bbe3b 100644 --- a/arch/x86/crypto/aesni-intel_glue.c +++ b/arch/x86/crypto/aesni-intel_glue.c @@ -1293,6 +1293,7 @@ static void gcm_process_assoc(const struct aes_gcm_key *key, u8 ghash_acc[16], unsigned int len; const u8 *src = walk.addr; + assoclen -= orig_len_this_step; if (unlikely(pos)) { len = min(len_this_step, 16 - pos); memcpy(&buf[pos], src, len); @@ -1320,7 +1321,6 @@ static void gcm_process_assoc(const struct aes_gcm_key *key, u8 ghash_acc[16], kernel_fpu_end(); kernel_fpu_begin(); } - assoclen -= orig_len_this_step; } if (unlikely(pos)) aes_gcm_aad_update(key, ghash_acc, buf, pos, flags); diff --git a/arch/x86/entry/vdso/vdso64/Makefile b/arch/x86/entry/vdso/vdso64/Makefile index 7c0790065b5e..c2353a065279 100644 --- a/arch/x86/entry/vdso/vdso64/Makefile +++ b/arch/x86/entry/vdso/vdso64/Makefile @@ -14,7 +14,7 @@ vobjs-$(CONFIG_X86_SGX) += vsgx.o vobjs-$(CONFIG_FUTEX_ROBUST_UNLOCK) += vfutex.o # Compilation flags -flags-y := -DBUILD_VDSO64 -m64 -mcmodel=small +flags-y := -DBUILD_VDSO64 -m64 -mcmodel=small $(CONFIG_CC_OPT_INLINE_MEMSET) # The location of this include matters! include $(src)/../common/Makefile.include diff --git a/arch/x86/kernel/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c index 776ae6fa7f2d..4c078827f26a 100644 --- a/arch/x86/kernel/sys_x86_64.c +++ b/arch/x86/kernel/sys_x86_64.c @@ -50,9 +50,16 @@ static unsigned long get_align_mask(struct file *filp) * value before calling vm_unmapped_area() or ORed directly to the * address. */ -static unsigned long get_align_bits(void) +static unsigned long get_align_bits(struct file *filp) { - return va_align.bits & get_align_mask(NULL); + /* + * va_align.bits is smaller than the huge page size and will + * lead to misaligned huge pages. Ignore it for huge mappings. + */ + if (is_file_hugepages(filp)) + return 0; + + return va_align.bits & get_align_mask(filp); } static int __init control_va_addr_alignment(char *str) @@ -157,7 +164,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, } if (filp) { info.align_mask = get_align_mask(filp); - info.align_offset += get_align_bits(); + info.align_offset += get_align_bits(filp); } return vm_unmapped_area(&info); @@ -222,7 +229,7 @@ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr0, if (filp) { info.align_mask = get_align_mask(filp); - info.align_offset += get_align_bits(); + info.align_offset += get_align_bits(filp); } addr = vm_unmapped_area(&info); if (!(addr & ~PAGE_MASK)) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 49a5fb103194..1af8a3c72951 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -2506,6 +2506,12 @@ static void shadow_walk_init_using_root(struct kvm_shadow_walk_iterator *iterato { iterator->addr = addr; iterator->shadow_addr = root; + + if (WARN_ON_ONCE(!VALID_PAGE(root)) || kvm_mmu_is_dummy_root(root)) { + iterator->level = 0; + return; + } + iterator->level = vcpu->arch.mmu->root_role.level; if (iterator->level >= PT64_ROOT_4LEVEL && diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index c1485c3e691c..02a0f9541800 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -2131,13 +2131,8 @@ static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu) return false; } - if (!nested_svm_merge_msrpm(vcpu)) { - vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; - vcpu->run->internal.suberror = - KVM_INTERNAL_ERROR_EMULATION; - vcpu->run->internal.ndata = 0; + if (!nested_svm_merge_msrpm(vcpu)) return false; - } if (kvm_hv_verify_vp_assist(vcpu)) return false; diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 62561da83107..3d1a8f50387f 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -488,6 +488,20 @@ static void snp_guest_req_cleanup(struct kvm *kvm) sev->guest_resp_buf = NULL; } +static int sev_alloc_have_run_cpus(struct kvm_sev_info *sev) +{ + if (!zalloc_cpumask_var(&sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) + return -ENOMEM; + + return 0; +} + +static void sev_free_have_run_cpus(struct kvm_sev_info *sev) +{ + free_cpumask_var(sev->have_run_cpus); + memset(&sev->have_run_cpus, 0, sizeof(sev->have_run_cpus)); +} + static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp, struct kvm_sev_init *data, unsigned long vm_type) @@ -545,10 +559,9 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp, if (ret) goto e_free_asid; - if (!zalloc_cpumask_var(&sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) { - ret = -ENOMEM; + ret = sev_alloc_have_run_cpus(sev); + if (ret) goto e_free_asid; - } /* This needs to happen after SEV/SNP firmware initialization. */ if (snp_active) { @@ -566,7 +579,7 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp, return 0; e_free: - free_cpumask_var(sev->have_run_cpus); + sev_free_have_run_cpus(sev); e_free_asid: argp->error = init_args.error; sev_asid_free(sev); @@ -2035,6 +2048,13 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm) struct kvm_sev_info *mirror; unsigned long i; + /* + * Do cache maintenance on the source VM *before* clearing "SEV active", + * as memory reclaim flows won't trigger cache maintenance on the VM + * once it's no longer an SEV VM. + */ + sev_writeback_caches(src_kvm); + dst->active = true; dst->asid = src->asid; dst->handle = src->handle; @@ -2048,12 +2068,6 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm) src->pages_locked = 0; src->es_active = false; - /* - * Do cache maintenance on the source VM as it is no longer an SEV VM, - * i.e. memory reclaim flows won't trigger cache maintenance on the VM. - */ - sev_writeback_caches(src_kvm); - list_cut_before(&dst->regions_list, &src->regions_list, &src->regions_list); mutex_lock(&sev_mirror_lock); @@ -2198,10 +2212,9 @@ int sev_vm_move_enc_context_from(struct kvm *kvm, unsigned int source_fd) * does not, i.e. KVM could skip flushes if memory is reclaimed from * the old VM but not the new VM. */ - if (!zalloc_cpumask_var(&dst_sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) { - ret = -ENOMEM; + ret = sev_alloc_have_run_cpus(dst_sev); + if (ret) goto out_source_vcpu; - } sev_migrate_from(kvm, source_kvm); kvm_vm_dead(source_kvm); @@ -2899,10 +2912,9 @@ int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd) } mirror_sev = to_kvm_sev_info(kvm); - if (!zalloc_cpumask_var(&mirror_sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) { - ret = -ENOMEM; + ret = sev_alloc_have_run_cpus(mirror_sev); + if (ret) goto e_unlock; - } /* * The mirror kvm holds an enc_context_owner ref so its asid can't @@ -2995,7 +3007,7 @@ void sev_vm_destroy(struct kvm *kvm) * Free the mask even if the VM is not *currently* an SEV VM, as it may * have been an SEV VM prior to intra-host migration. */ - free_cpumask_var(sev->have_run_cpus); + sev_free_have_run_cpus(sev); if (!sev_guest(kvm)) return; diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index d1423bab1a3a..975587aba12e 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -673,16 +673,7 @@ static void clr_dr_intercepts(struct vcpu_svm *svm) static bool msr_write_intercepted(struct vcpu_svm *svm, u32 msr) { - /* - * For non-nested case: - * If the L01 MSR bitmap does not intercept the MSR, then we need to - * save it. - * - * For nested case: - * If the L02 MSR bitmap does not intercept the MSR, then we need to - * save it. - */ - void *msrpm = is_guest_mode(&svm->vcpu) ? svm->nested.msrpm : svm->msrpm; + void *msrpm = __va(__sme_clr(svm->vmcb->control.msrpm_base_pa)); return svm_test_msr_bitmap_write(msrpm, msr); } @@ -1906,8 +1897,7 @@ static void new_asid(struct vcpu_svm *svm, struct svm_cpu_data *sd) if (sd->next_asid > sd->max_asid) { ++sd->asid_generation; sd->next_asid = sd->min_asid; - svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ALL_ASID; - vmcb_mark_dirty(svm->vmcb, VMCB_ASID); + sd->flush_all_asids = true; } svm->current_vmcb->asid_generation = sd->asid_generation; @@ -4527,6 +4517,11 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) svm->vmcb->control.asid = svm->asid; vmcb_mark_dirty(svm->vmcb, VMCB_ASID); } + if (this_cpu_ptr(&svm_data)->flush_all_asids) { + svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ALL_ASID; + vmcb_mark_dirty(svm->vmcb, VMCB_ASID); + } + svm->vmcb->save.cr2 = vcpu->arch.cr2; if (guest_cpu_cap_has(vcpu, X86_FEATURE_ERAPS) && @@ -4617,16 +4612,19 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) vcpu->arch.nested_run_pending = 0; } - svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING; + if (!svm_is_vmrun_failure(svm->vmcb->control.exit_code)) { + this_cpu_ptr(&svm_data)->flush_all_asids = false; + svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING; - /* - * Unconditionally mask off the CLEAR_RAP bit, the AND is just as cheap - * as the TEST+Jcc to avoid it. - */ - if (cpu_feature_enabled(X86_FEATURE_ERAPS)) - svm->vmcb->control.erap_ctl &= ~ERAP_CONTROL_CLEAR_RAP; + /* + * Unconditionally mask off the CLEAR_RAP bit, the AND is just + * as cheap as the TEST+Jcc to avoid it. + */ + if (cpu_feature_enabled(X86_FEATURE_ERAPS)) + svm->vmcb->control.erap_ctl &= ~ERAP_CONTROL_CLEAR_RAP; - vmcb_mark_all_clean(svm->vmcb); + vmcb_mark_all_clean(svm->vmcb); + } /* if exit due to PF check for async PF */ if (svm->vmcb->control.exit_code == SVM_EXIT_EXCP_BASE + PF_VECTOR) diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index cd30740e9984..f02c3d5eb089 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -376,6 +376,7 @@ struct svm_cpu_data { u32 next_asid; u32 min_asid; + bool flush_all_asids; bool bp_spec_reduce_set; struct vmcb *save_area; diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index cec7eb646a1e..e3dd5e84941f 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -1758,6 +1758,9 @@ static void copy_vmcs12_to_shadow(struct vcpu_vmx *vmx) static void copy_enlightened_to_vmcs12(struct vcpu_vmx *vmx, u32 hv_clean_fields) { #ifdef CONFIG_KVM_HYPERV + const u64 runtime_controls = HV_VMX_ENLIGHTENED_CLEAN_FIELD_CONTROL_GRP1 | + HV_VMX_ENLIGHTENED_CLEAN_FIELD_CONTROL_GRP2 | + HV_VMX_ENLIGHTENED_CLEAN_FIELD_CONTROL_PROC; struct vmcs12 *vmcs12 = vmx->nested.cached_vmcs12; struct hv_enlightened_vmcs *evmcs = nested_vmx_evmcs(vmx); struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(&vmx->vcpu); @@ -1766,6 +1769,9 @@ static void copy_enlightened_to_vmcs12(struct vcpu_vmx *vmx, u32 hv_clean_fields vmcs12->tpr_threshold = evmcs->tpr_threshold; vmcs12->guest_rip = evmcs->guest_rip; + if ((hv_clean_fields & runtime_controls) != runtime_controls) + vmx->nested.force_msr_bitmap_recalc = true; + if (unlikely(!(hv_clean_fields & HV_VMX_ENLIGHTENED_CLEAN_FIELD_ENLIGHTENMENTSCONTROL))) { hv_vcpu->nested.pa_page_gpa = evmcs->partition_assist_page; @@ -3469,10 +3475,6 @@ static bool nested_get_vmcs12_pages(struct kvm_vcpu *vcpu) } else { pr_debug_ratelimited("%s: no backing for APIC-access address in vmcs12\n", __func__); - vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; - vcpu->run->internal.suberror = - KVM_INTERNAL_ERROR_EMULATION; - vcpu->run->internal.ndata = 0; return false; } } @@ -3543,11 +3545,6 @@ static bool vmx_get_nested_state_pages(struct kvm_vcpu *vcpu) if (!nested_get_evmcs_page(vcpu)) { pr_debug_ratelimited("%s: enlightened vmptrld failed\n", __func__); - vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; - vcpu->run->internal.suberror = - KVM_INTERNAL_ERROR_EMULATION; - vcpu->run->internal.ndata = 0; - return false; } #endif @@ -3919,8 +3916,12 @@ static int nested_vmx_run(struct kvm_vcpu *vcpu, bool launch) vmentry_failed: vcpu->arch.nested_run_pending = 0; - if (status == NVMX_VMENTRY_KVM_INTERNAL_ERROR) + if (status == NVMX_VMENTRY_KVM_INTERNAL_ERROR) { + vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; + vcpu->run->internal.ndata = 0; return 0; + } if (status == NVMX_VMENTRY_VMEXIT) return 1; WARN_ON_ONCE(status != NVMX_VMENTRY_VMFAIL); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 61db05ff023c..e3636a97ae9e 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -10694,6 +10694,10 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) if (kvm_check_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu)) { if (unlikely(!kvm_nested_call(get_nested_state_pages)(vcpu))) { + vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; + vcpu->run->internal.ndata = 0; + kvm_make_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu); r = 0; goto out; } diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c index f32facdb3035..ad801175dd5e 100644 --- a/arch/x86/mm/pgtable.c +++ b/arch/x86/mm/pgtable.c @@ -705,47 +705,37 @@ int pmd_clear_huge(pmd_t *pmd) } #ifdef CONFIG_X86_64 -/** - * pud_free_pmd_page - Clear PUD entry and free PMD page - * @pud: Pointer to a PUD - * @addr: Virtual address associated with PUD - * - * Context: The PUD range has been unmapped and TLB purged. - * Return: 1 if clearing the entry succeeded. 0 otherwise. - * - * NOTE: Callers must allow a single page allocation. +/* + * Given a PUD poitner, detach and free the pointed-to + * PMD page and any PTE page children. The entire range + * under the PUD must not have any valid translations + * and the TLB must have already been flushed. */ int pud_free_pmd_page(pud_t *pud, unsigned long addr) { - pmd_t *pmd, *pmd_sv; struct ptdesc *pt; + pmd_t *pmd; int i; pmd = pud_pgtable(*pud); - pmd_sv = (pmd_t *)__get_free_page(GFP_KERNEL); - if (!pmd_sv) - return 0; - - for (i = 0; i < PTRS_PER_PMD; i++) { - pmd_sv[i] = pmd[i]; - if (!pmd_none(pmd[i])) - pmd_clear(&pmd[i]); - } + /* Detach the PMD page: */ pud_clear(pud); - /* INVLPG to clear all paging-structure caches */ + /* + * PMD and all its descendents are unreachable + * via normal page walks. Make them unreachable + * in cached mid-level walks too: + */ flush_tlb_kernel_range(addr, addr + PAGE_SIZE-1); for (i = 0; i < PTRS_PER_PMD; i++) { - if (!pmd_none(pmd_sv[i])) { - pt = page_ptdesc(pmd_page(pmd_sv[i])); + if (!pmd_none(pmd[i])) { + pt = page_ptdesc(pmd_page(pmd[i])); pagetable_dtor_free(pt); } } - free_page((unsigned long)pmd_sv); - pmd_free(&init_mm, pmd); return 1; diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index ea74ff1d166c..b06347da06fc 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -3128,16 +3128,21 @@ static void restore_regs(const struct btf_func_model *m, u8 **prog, } static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog, + struct bpf_tramp_image *im, struct bpf_tramp_node *node, int stack_size, int run_ctx_off, bool save_ret, void *image, void *rw_image) { u8 *prog = *pprog; - u8 *jmp_insn; + u8 *jmp_insn, *skip; int ctx_cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie); struct bpf_prog *p = node->link->prog; u64 cookie = node->cookie; + /* nop, patched to skip this prog when it is detached */ + skip = image + (prog - (u8 *)rw_image); + emit_nops(&prog, X86_PATCH_SIZE); + /* mov rdi, cookie */ emit_mov_imm64(&prog, BPF_REG_1, (long) cookie >> 32, (u32) (long) cookie); @@ -3212,6 +3217,8 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog, if (emit_rsb_call(&prog, bpf_trampoline_exit(p), image + (prog - (u8 *)rw_image))) return -EINVAL; + bpf_tramp_image_add_skip(im, p, skip, image + (prog - (u8 *)rw_image)); + *pprog = prog; return 0; } @@ -3243,6 +3250,7 @@ static int emit_cond_near_jump(u8 **pprog, void *func, void *ip, u8 jmp_cond) } static int invoke_bpf(const struct btf_func_model *m, u8 **pprog, + struct bpf_tramp_image *im, struct bpf_tramp_nodes *tl, int stack_size, int run_ctx_off, int func_meta_off, bool save_ret, void *image, void *rw_image, u64 func_meta, @@ -3257,7 +3265,7 @@ static int invoke_bpf(const struct btf_func_model *m, u8 **pprog, func_meta | (cur_cookie << BPF_TRAMP_COOKIE_INDEX_SHIFT)); cur_cookie--; } - if (invoke_bpf_prog(m, &prog, tl->nodes[i], stack_size, + if (invoke_bpf_prog(m, &prog, im, tl->nodes[i], stack_size, run_ctx_off, save_ret, image, rw_image)) return -EINVAL; } @@ -3266,6 +3274,7 @@ static int invoke_bpf(const struct btf_func_model *m, u8 **pprog, } static int invoke_bpf_mod_ret(const struct btf_func_model *m, u8 **pprog, + struct bpf_tramp_image *im, struct bpf_tramp_nodes *tl, int stack_size, int run_ctx_off, u8 **branches, void *image, void *rw_image) @@ -3279,7 +3288,7 @@ static int invoke_bpf_mod_ret(const struct btf_func_model *m, u8 **pprog, emit_mov_imm32(&prog, false, BPF_REG_0, 0); emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8); for (i = 0; i < tl->nr_nodes; i++) { - if (invoke_bpf_prog(m, &prog, tl->nodes[i], stack_size, run_ctx_off, true, + if (invoke_bpf_prog(m, &prog, im, tl->nodes[i], stack_size, run_ctx_off, true, image, rw_image)) return -EINVAL; @@ -3548,7 +3557,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im } if (fentry->nr_nodes) { - if (invoke_bpf(m, &prog, fentry, regs_off, run_ctx_off, func_meta_off, + if (invoke_bpf(m, &prog, im, fentry, regs_off, run_ctx_off, func_meta_off, flags & BPF_TRAMP_F_RET_FENTRY_RET, image, rw_image, func_meta, cookie_off)) return -EINVAL; @@ -3560,7 +3569,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im if (!branches) return -ENOMEM; - if (invoke_bpf_mod_ret(m, &prog, fmod_ret, regs_off, + if (invoke_bpf_mod_ret(m, &prog, im, fmod_ret, regs_off, run_ctx_off, branches, image, rw_image)) { ret = -EINVAL; goto cleanup; @@ -3590,8 +3599,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im } /* remember return value in a stack for bpf prog to access */ emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8); - im->ip_after_call = image + (prog - (u8 *)rw_image); - emit_nops(&prog, X86_PATCH_SIZE); } if (fmod_ret->nr_nodes) { @@ -3616,7 +3623,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im emit_store_stack_imm64(&prog, BPF_REG_0, -func_meta_off, func_meta); if (fexit->nr_nodes) { - if (invoke_bpf(m, &prog, fexit, regs_off, run_ctx_off, func_meta_off, + if (invoke_bpf(m, &prog, im, fexit, regs_off, run_ctx_off, func_meta_off, false, image, rw_image, func_meta, cookie_off)) { ret = -EINVAL; goto cleanup; @@ -3631,7 +3638,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im * restored to R0. */ if (flags & BPF_TRAMP_F_CALL_ORIG) { - im->ip_epilogue = image + (prog - (u8 *)rw_image); /* arg1: mov rdi, im */ emit_mov_imm64(&prog, BPF_REG_1, (long) im >> 32, (u32) (long) im); if (emit_rsb_call(&prog, __bpf_tramp_exit, image + (prog - (u8 *)rw_image))) { @@ -3719,7 +3725,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, struct bpf_tramp_nodes *tnodes, void *func_addr) { - struct bpf_tramp_image im; + struct bpf_tramp_image im = {}; void *image; int ret; diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 52080c7c1a14..1951bda17cf5 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -2078,6 +2078,7 @@ static inline struct blkcg_gq *blkg_tryget_closest(struct bio *bio, struct request_queue *q = bio->bi_bdev->bd_queue; struct blkcg *blkcg = css_to_blkcg(css); struct blkcg_gq *blkg; + unsigned long flags; rcu_read_lock(); blkg = blkg_lookup(blkcg, q); @@ -2092,11 +2093,11 @@ static inline struct blkcg_gq *blkg_tryget_closest(struct bio *bio, * Fast path failed, we're probably issuing IO in this cgroup the first * time, hold lock to create new blkg. */ - spin_lock_irq(&q->queue_lock); + spin_lock_irqsave(&q->queue_lock, flags); blkg = blkg_lookup_create(blkcg, bio->bi_bdev->bd_disk); if (blkg) blkg = blkg_lookup_tryget(blkg); - spin_unlock_irq(&q->queue_lock); + spin_unlock_irqrestore(&q->queue_lock, flags); return blkg; } diff --git a/block/blk-mq.c b/block/blk-mq.c index 2c850330a32b..3be2dd4d9001 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -447,16 +447,6 @@ static struct request *blk_mq_rq_ctx_init(struct blk_mq_alloc_data *data, WRITE_ONCE(rq->deadline, 0); req_ref_set(rq, 1); - if (rq->rq_flags & RQF_USE_SCHED) { - struct elevator_queue *e = data->q->elevator; - - INIT_HLIST_NODE(&rq->hash); - RB_CLEAR_NODE(&rq->rb_node); - - if (e->type->ops.prepare_request) - e->type->ops.prepare_request(rq); - } - return rq; } @@ -498,6 +488,12 @@ __blk_mq_alloc_requests_batch(struct blk_mq_alloc_data *data) return rq_list_pop(data->cached_rqs); } +static bool blk_op_bypass_sched(blk_opf_t opf) +{ + return (opf & REQ_OP_MASK) == REQ_OP_FLUSH || + blk_op_is_passthrough(opf); +} + static void blk_mq_limit_depth(struct blk_mq_alloc_data *data) { struct elevator_mq_ops *ops; @@ -518,12 +514,10 @@ static void blk_mq_limit_depth(struct blk_mq_alloc_data *data) * Flush/passthrough requests are special and go directly to the * dispatch list, they are not subject to the async_depth limit. */ - if ((data->cmd_flags & REQ_OP_MASK) == REQ_OP_FLUSH || - blk_op_is_passthrough(data->cmd_flags)) + if (blk_op_bypass_sched(data->cmd_flags)) return; WARN_ON_ONCE(data->flags & BLK_MQ_REQ_RESERVED); - data->rq_flags |= RQF_USE_SCHED; /* * By default, sync requests have no limit, and async requests are @@ -534,6 +528,29 @@ static void blk_mq_limit_depth(struct blk_mq_alloc_data *data) ops->limit_depth(data->cmd_flags, data); } +/* + * Finish initializing a request once it has been claimed for an operation. + * Cached requests are allocated before that operation is known. + */ +static void blk_mq_rq_late_init(struct request *rq, u64 alloc_time_ns) +{ + struct elevator_queue *e; + + blk_mq_rq_time_init(rq, alloc_time_ns); + + if (!(rq->rq_flags & RQF_SCHED_TAGS) || (rq->rq_flags & RQF_RESV) || + blk_op_bypass_sched(rq->cmd_flags)) + return; + + rq->rq_flags |= RQF_USE_SCHED; + INIT_HLIST_NODE(&rq->hash); + RB_CLEAR_NODE(&rq->rb_node); + + e = rq->q->elevator; + if (e->type->ops.prepare_request) + e->type->ops.prepare_request(rq); +} + static struct request *__blk_mq_alloc_requests(struct blk_mq_alloc_data *data) { struct request_queue *q = data->q; @@ -562,7 +579,7 @@ static struct request *__blk_mq_alloc_requests(struct blk_mq_alloc_data *data) if (data->nr_tags > 1) { rq = __blk_mq_alloc_requests_batch(data); if (rq) { - blk_mq_rq_time_init(rq, alloc_time_ns); + blk_mq_rq_late_init(rq, alloc_time_ns); return rq; } data->nr_tags = 1; @@ -590,7 +607,7 @@ static struct request *__blk_mq_alloc_requests(struct blk_mq_alloc_data *data) if (!(data->rq_flags & RQF_SCHED_TAGS)) blk_mq_inc_active_requests(data->hctx); rq = blk_mq_rq_ctx_init(data, blk_mq_tags_from_data(data), tag); - blk_mq_rq_time_init(rq, alloc_time_ns); + blk_mq_rq_late_init(rq, alloc_time_ns); return rq; } @@ -637,8 +654,6 @@ static struct request *blk_mq_alloc_cached_request(struct request_queue *q, if (plug->nr_ios == 1) return NULL; rq = blk_mq_rq_cache_fill(q, plug, opf, flags); - if (!rq) - return NULL; } else { rq = rq_list_peek(&plug->cached_rqs); if (!rq || rq->q != q) @@ -650,11 +665,11 @@ static struct request *blk_mq_alloc_cached_request(struct request_queue *q, return NULL; rq_list_pop(&plug->cached_rqs); - blk_mq_rq_time_init(rq, blk_time_get_ns()); + rq->cmd_flags = opf; + INIT_LIST_HEAD(&rq->queuelist); + blk_mq_rq_late_init(rq, blk_time_get_ns()); } - rq->cmd_flags = opf; - INIT_LIST_HEAD(&rq->queuelist); return rq; } @@ -766,7 +781,7 @@ struct request *blk_mq_alloc_request_hctx(struct request_queue *q, if (!(data.rq_flags & RQF_SCHED_TAGS)) blk_mq_inc_active_requests(data.hctx); rq = blk_mq_rq_ctx_init(&data, blk_mq_tags_from_data(&data), tag); - blk_mq_rq_time_init(rq, alloc_time_ns); + blk_mq_rq_late_init(rq, alloc_time_ns); rq->__data_len = 0; rq->phys_gap_bit = 0; rq->__sector = (sector_t) -1; @@ -3163,9 +3178,9 @@ void blk_mq_submit_bio(struct bio *bio) new_request: if (rq) { rq_qos_throttle(rq->q, bio); - blk_mq_rq_time_init(rq, blk_time_get_ns()); rq->cmd_flags = bio->bi_opf; INIT_LIST_HEAD(&rq->queuelist); + blk_mq_rq_late_init(rq, blk_time_get_ns()); } else { rq = blk_mq_get_new_requests(q, plug, bio); if (unlikely(!rq)) { diff --git a/block/fops.c b/block/fops.c index cb919dea0041..1f29aeb19157 100644 --- a/block/fops.c +++ b/block/fops.c @@ -404,6 +404,11 @@ static ssize_t blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter) if (blkdev_dio_invalid(bdev, iocb, iter)) return -EINVAL; + /* HIPRI needs private as bio; HAS_METADATA keeps it as uio_meta */ + if ((iocb->ki_flags & IOCB_HIPRI) && + (iocb->ki_flags & IOCB_HAS_METADATA)) + return -EOPNOTSUPP; + if (iov_iter_rw(iter) == WRITE) { u16 max_write_streams = bdev_max_write_streams(bdev); diff --git a/block/mq-deadline.c b/block/mq-deadline.c index 824bfc17b2c6..d8f5514cc131 100644 --- a/block/mq-deadline.c +++ b/block/mq-deadline.c @@ -688,7 +688,7 @@ static void dd_insert_requests(struct blk_mq_hw_ctx *hctx, blk_mq_free_requests(&free); } -/* Callback from inside blk_mq_rq_ctx_init(). */ +/* Callback from inside blk_mq_rq_late_init(). */ static void dd_prepare_request(struct request *rq) { rq->elv.priv[0] = NULL; diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 8f2ef1bd539f..bc8bc9ee43a5 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2930,8 +2930,9 @@ static int binder_proc_transaction(struct binder_transaction *t, t_outdated->buffer = NULL; buffer->transaction = NULL; trace_binder_transaction_update_buffer_release(buffer); - binder_release_entire_buffer(proc, NULL, buffer, false); + binder_release_entire_buffer(proc, NULL, buffer, true); binder_alloc_free_buf(&proc->alloc, buffer); + binder_free_txn_fixups(t_outdated); kfree(t_outdated); binder_stats_deleted(BINDER_STAT_TRANSACTION); } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index bc0ef8927905..7e0be3a733cc 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -659,6 +659,12 @@ pub(crate) fn push_return_work(&self, reply: u32) { self.inner.lock().push_return_work(reply); } + pub(crate) fn pop_work_even_if_deferred(&self) -> Option> { + let mut thread_inner = self.inner.lock(); + thread_inner.process_work_list = true; + thread_inner.pop_work() + } + fn translate_object( &self, obj_index: usize, @@ -1621,7 +1627,7 @@ pub(crate) fn release(self: &Arc) { self.unwind_transaction_stack(); // Cancel all pending work items. - while let Ok(Some(work)) = self.get_work_local(false) { + while let Some(work) = self.pop_work_even_if_deferred() { work.into_arc().cancel(); } } diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 361d69f756f5..1ca31b9a0583 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -189,12 +189,11 @@ static int binderfs_binder_device_create(struct inode *ref_inode, goto err; } inode->i_private = device; + binder_add_device(device); d_make_persistent(dentry, inode); fsnotify_create(root->d_inode, dentry); simple_done_creating(dentry); - binder_add_device(device); - return 0; err: diff --git a/drivers/block/drbd/drbd_nl_gen.c b/drivers/block/drbd/drbd_nl_gen.c index fb44b948cec8..e133e8415205 100644 --- a/drivers/block/drbd/drbd_nl_gen.c +++ b/drivers/block/drbd/drbd_nl_gen.c @@ -638,10 +638,6 @@ const struct genl_split_ops drbd_nl_ops[32] = { }, }; -static const struct genl_multicast_group drbd_nl_mcgrps[] = { - [DRBD_NLGRP_EVENTS] = { "events", }, -}; - static int __drbd_cfg_context_from_attrs(struct drbd_cfg_context *s, struct nlattr ***ret_nested_attribute_table, struct genl_info *info) diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 32bf3ba07a9d..c8bfbf8435ac 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -743,6 +743,7 @@ static int virtblk_read_zoned_limits(struct virtio_blk *vblk, dev_warn(&vdev->dev, "zero write granularity reported\n"); return -ENODEV; } + lim->zone_write_granularity = wg; lim->physical_block_size = wg; lim->io_min = wg; diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 96a55d5badda..aab92cf57159 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -288,7 +288,8 @@ void btintel_hw_error(struct hci_dev *hdev, u8 code) goto unlock; } - bt_dev_err(hdev, "Exception info %s", (char *)(skb->data + 1)); + bt_dev_err(hdev, "Exception info %.*s", (int)(skb->len - 1), + (char *)(skb->data + 1)); kfree_skb(skb); @@ -407,8 +408,16 @@ int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name) /* DDC file contains one or more DDC structure which has * Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2). */ - while (fw->size > fw_ptr - fw->data) { - u8 cmd_plen = fw_ptr[0] + sizeof(u8); + while (fw->size > (size_t)(fw_ptr - fw->data)) { + size_t remaining = fw->size - (fw_ptr - fw->data); + unsigned int cmd_plen = fw_ptr[0] + 1U; + + if (cmd_plen < 3 || cmd_plen > U8_MAX || cmd_plen > remaining) { + bt_dev_err(hdev, "Malformed DDC record (plen=%u, remaining=%zu)", + cmd_plen, remaining); + release_firmware(fw); + return -EINVAL; + } skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr, HCI_INIT_TIMEOUT); diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index 9381895dc3cf..fdc1fd2586b9 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -1197,7 +1197,7 @@ static int btintel_pcie_recv_frame(struct btintel_pcie_data *data, { int ret; u8 pkt_type; - u16 plen; + u32 plen; u32 pcie_pkt_type; void *pdata; struct hci_dev *hdev = data->hdev; @@ -2157,6 +2157,16 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev, if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags)) return -ENODEV; + /* Account for the 4-byte PCIe type header prepended before the + * DMA copy. Written as a subtraction to avoid wrap-around on + * attacker-controlled skb->len. + */ + if (skb->len > BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN) { + bt_dev_err(hdev, "Packet too large: %u > %u", skb->len, + BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN); + return -EMSGSIZE; + } + /* Due to the fw limitation, the type header of the packet should be * 4 bytes unlike 1 byte for UART. In UART, the firmware can read * the first byte to get the packet type and redirect the rest of data diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 87d50efcd271..bf9a93d5c9ef 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -308,6 +308,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3501), .driver_info = BTUSB_QCA_ROME | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x13d3, 0x3503), .driver_info = BTUSB_QCA_ROME | + BTUSB_WIDEBAND_SPEECH }, /* QCA WCN6855 chipset */ { USB_DEVICE(0x0489, 0xe0c7), .driver_info = BTUSB_QCA_WCN6855 | diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 52ee350da867..e12e245faeb1 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -318,7 +318,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) out = (struct tpm2_get_random_out *)&buf.data[offset]; recd = min_t(u32, be16_to_cpu(out->size), num_bytes); if (tpm_buf_length(&buf) < - TPM_HEADER_SIZE + + offset + offsetof(struct tpm2_get_random_out, buffer) + recd) { err = -EFAULT; diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index a1ae5e1829cb..3e5404a54cd8 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -985,6 +985,7 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key) /* Deduce from the name change TPM interference: */ dev_err(&chip->dev, "null key integrity check failed\n"); tpm2_flush_context(chip, tmp_null_key); + rc = -ENODEV; err: if (rc) { diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index e94a5d2687d5..81f0598286fb 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -30,14 +30,87 @@ static const struct ccu_pll_rate_tbl pll2_rate_tbl[] = { }; static const struct ccu_pll_rate_tbl pll3_rate_tbl[] = { + CCU_PLLA_RATE(1050000000UL, 0x0b2b3000, 0x00000000, 0xa0458080), + CCU_PLLA_RATE(1100000000UL, 0x0b2d3555, 0x00005500, 0xa0458181), + CCU_PLLA_RATE(1150000000UL, 0x0b2f3aaa, 0x0000ab00, 0xa0458181), + CCU_PLLA_RATE(1200000000UL, 0x0b320000, 0x00000000, 0xa0458181), + CCU_PLLA_RATE(1250000000UL, 0x0b340555, 0x00005500, 0xa0458181), + CCU_PLLA_RATE(1300000000UL, 0x0b360aaa, 0x0000ab00, 0xa0458282), + CCU_PLLA_RATE(1350000000UL, 0x0b381000, 0x00000000, 0xa0458282), + CCU_PLLA_RATE(1400000000UL, 0x0b3a1555, 0x00005500, 0xa0458282), + CCU_PLLA_RATE(1450000000UL, 0x0b3c1aaa, 0x0000ab00, 0xa0458383), + CCU_PLLA_RATE(1500000000UL, 0x0b3e2000, 0x00000000, 0xa0458383), + CCU_PLLA_RATE(1550000000UL, 0x0b402555, 0x00005500, 0xa0458383), + CCU_PLLA_RATE(1600000000UL, 0x0b422aaa, 0x0000ab00, 0xa0458484), + CCU_PLLA_RATE(1650000000UL, 0x0b443000, 0x00000000, 0xa0458484), + CCU_PLLA_RATE(1700000000UL, 0x0b463555, 0x00005500, 0xa0458484), + CCU_PLLA_RATE(1750000000UL, 0x0b483aaa, 0x0000ab00, 0xa0458585), + CCU_PLLA_RATE(1800000000UL, 0x0b4b0000, 0x00000000, 0xa0458585), + CCU_PLLA_RATE(1850000000UL, 0x0b4d0555, 0x00005500, 0xa0458585), + CCU_PLLA_RATE(1900000000UL, 0x0b4f0aaa, 0x0000ab00, 0xa0458686), + CCU_PLLA_RATE(1950000000UL, 0x0b511000, 0x00000000, 0xa0458686), + CCU_PLLA_RATE(2000000000UL, 0x0b292aaa, 0x0000ab00, 0xa0558686), + CCU_PLLA_RATE(2050000000UL, 0x0b2a2d55, 0x00005500, 0xa0558686), + CCU_PLLA_RATE(2100000000UL, 0x0b2b3000, 0x00000000, 0xa0558787), + CCU_PLLA_RATE(2150000000UL, 0x0b2c32aa, 0x0000ab00, 0xa0558787), CCU_PLLA_RATE(2200000000UL, 0x0b2d3555, 0x00005500, 0xa0558787), + CCU_PLLA_RATE(2250000000UL, 0x0b2e3800, 0x00000000, 0xa0558888), + CCU_PLLA_RATE(2300000000UL, 0x0b2f3aaa, 0x0000ab00, 0xa0558888), + CCU_PLLA_RATE(2350000000UL, 0x0b303d55, 0x00005500, 0xa0558888), + CCU_PLLA_RATE(2400000000UL, 0x0b320000, 0x00000000, 0xa0558989), }; static const struct ccu_pll_rate_tbl pll4_rate_tbl[] = { + CCU_PLLA_RATE(1050000000UL, 0x0b2b3000, 0x00000000, 0xa0458080), + CCU_PLLA_RATE(1100000000UL, 0x0b2d3555, 0x00005500, 0xa0458181), + CCU_PLLA_RATE(1150000000UL, 0x0b2f3aaa, 0x0000ab00, 0xa0458181), + CCU_PLLA_RATE(1200000000UL, 0x0b320000, 0x00000000, 0xa0458181), + CCU_PLLA_RATE(1250000000UL, 0x0b340555, 0x00005500, 0xa0458181), + CCU_PLLA_RATE(1300000000UL, 0x0b360aaa, 0x0000ab00, 0xa0458282), + CCU_PLLA_RATE(1350000000UL, 0x0b381000, 0x00000000, 0xa0458282), + CCU_PLLA_RATE(1400000000UL, 0x0b3a1555, 0x00005500, 0xa0458282), + CCU_PLLA_RATE(1450000000UL, 0x0b3c1aaa, 0x0000ab00, 0xa0458383), + CCU_PLLA_RATE(1500000000UL, 0x0b3e2000, 0x00000000, 0xa0458383), + CCU_PLLA_RATE(1550000000UL, 0x0b402555, 0x00005500, 0xa0458383), + CCU_PLLA_RATE(1600000000UL, 0x0b422aaa, 0x0000ab00, 0xa0458484), + CCU_PLLA_RATE(1650000000UL, 0x0b443000, 0x00000000, 0xa0458484), + CCU_PLLA_RATE(1700000000UL, 0x0b463555, 0x00005500, 0xa0458484), + CCU_PLLA_RATE(1750000000UL, 0x0b483aaa, 0x0000ab00, 0xa0458585), + CCU_PLLA_RATE(1800000000UL, 0x0b4b0000, 0x00000000, 0xa0458585), + CCU_PLLA_RATE(1850000000UL, 0x0b4d0555, 0x00005500, 0xa0458585), + CCU_PLLA_RATE(1900000000UL, 0x0b4f0aaa, 0x0000ab00, 0xa0458686), + CCU_PLLA_RATE(1950000000UL, 0x0b511000, 0x00000000, 0xa0458686), + CCU_PLLA_RATE(2000000000UL, 0x0b292aaa, 0x0000ab00, 0xa0558686), + CCU_PLLA_RATE(2050000000UL, 0x0b2a2d55, 0x00005500, 0xa0558686), + CCU_PLLA_RATE(2100000000UL, 0x0b2b3000, 0x00000000, 0xa0558787), + CCU_PLLA_RATE(2150000000UL, 0x0b2c32aa, 0x0000ab00, 0xa0558787), CCU_PLLA_RATE(2200000000UL, 0x0b2d3555, 0x00005500, 0xa0558787), + CCU_PLLA_RATE(2250000000UL, 0x0b2e3800, 0x00000000, 0xa0558888), + CCU_PLLA_RATE(2300000000UL, 0x0b2f3aaa, 0x0000ab00, 0xa0558888), + CCU_PLLA_RATE(2350000000UL, 0x0b303d55, 0x00005500, 0xa0558888), + CCU_PLLA_RATE(2400000000UL, 0x0b320000, 0x00000000, 0xa0558989), }; static const struct ccu_pll_rate_tbl pll5_rate_tbl[] = { + CCU_PLLA_RATE(1050000000UL, 0x0b2b3000, 0x00000000, 0xa0458080), + CCU_PLLA_RATE(1100000000UL, 0x0b2d3555, 0x00005500, 0xa0458181), + CCU_PLLA_RATE(1150000000UL, 0x0b2f3aaa, 0x0000ab00, 0xa0458181), + CCU_PLLA_RATE(1200000000UL, 0x0b320000, 0x00000000, 0xa0458181), + CCU_PLLA_RATE(1250000000UL, 0x0b340555, 0x00005500, 0xa0458181), + CCU_PLLA_RATE(1300000000UL, 0x0b360aaa, 0x0000ab00, 0xa0458282), + CCU_PLLA_RATE(1350000000UL, 0x0b381000, 0x00000000, 0xa0458282), + CCU_PLLA_RATE(1400000000UL, 0x0b3a1555, 0x00005500, 0xa0458282), + CCU_PLLA_RATE(1450000000UL, 0x0b3c1aaa, 0x0000ab00, 0xa0458383), + CCU_PLLA_RATE(1500000000UL, 0x0b3e2000, 0x00000000, 0xa0458383), + CCU_PLLA_RATE(1550000000UL, 0x0b402555, 0x00005500, 0xa0458383), + CCU_PLLA_RATE(1600000000UL, 0x0b422aaa, 0x0000ab00, 0xa0458484), + CCU_PLLA_RATE(1650000000UL, 0x0b443000, 0x00000000, 0xa0458484), + CCU_PLLA_RATE(1700000000UL, 0x0b463555, 0x00005500, 0xa0458484), + CCU_PLLA_RATE(1750000000UL, 0x0b483aaa, 0x0000ab00, 0xa0458585), + CCU_PLLA_RATE(1800000000UL, 0x0b4b0000, 0x00000000, 0xa0458585), + CCU_PLLA_RATE(1850000000UL, 0x0b4d0555, 0x00005500, 0xa0458585), + CCU_PLLA_RATE(1900000000UL, 0x0b4f0aaa, 0x0000ab00, 0xa0458686), + CCU_PLLA_RATE(1950000000UL, 0x0b511000, 0x00000000, 0xa0458686), CCU_PLLA_RATE(2000000000UL, 0x0b292aaa, 0x0000ab00, 0xa0558686), }; @@ -50,6 +123,25 @@ static const struct ccu_pll_rate_tbl pll7_rate_tbl[] = { }; static const struct ccu_pll_rate_tbl pll8_rate_tbl[] = { + CCU_PLLA_RATE(1050000000UL, 0x0b2b3000, 0x00000000, 0xa0458080), + CCU_PLLA_RATE(1100000000UL, 0x0b2d3555, 0x00005500, 0xa0458181), + CCU_PLLA_RATE(1150000000UL, 0x0b2f3aaa, 0x0000ab00, 0xa0458181), + CCU_PLLA_RATE(1200000000UL, 0x0b320000, 0x00000000, 0xa0458181), + CCU_PLLA_RATE(1250000000UL, 0x0b340555, 0x00005500, 0xa0458181), + CCU_PLLA_RATE(1300000000UL, 0x0b360aaa, 0x0000ab00, 0xa0458282), + CCU_PLLA_RATE(1350000000UL, 0x0b381000, 0x00000000, 0xa0458282), + CCU_PLLA_RATE(1400000000UL, 0x0b3a1555, 0x00005500, 0xa0458282), + CCU_PLLA_RATE(1450000000UL, 0x0b3c1aaa, 0x0000ab00, 0xa0458383), + CCU_PLLA_RATE(1500000000UL, 0x0b3e2000, 0x00000000, 0xa0458383), + CCU_PLLA_RATE(1550000000UL, 0x0b402555, 0x00005500, 0xa0458383), + CCU_PLLA_RATE(1600000000UL, 0x0b422aaa, 0x0000ab00, 0xa0458484), + CCU_PLLA_RATE(1650000000UL, 0x0b443000, 0x00000000, 0xa0458484), + CCU_PLLA_RATE(1700000000UL, 0x0b463555, 0x00005500, 0xa0458484), + CCU_PLLA_RATE(1750000000UL, 0x0b483aaa, 0x0000ab00, 0xa0458585), + CCU_PLLA_RATE(1800000000UL, 0x0b4b0000, 0x00000000, 0xa0458585), + CCU_PLLA_RATE(1850000000UL, 0x0b4d0555, 0x00005500, 0xa0458585), + CCU_PLLA_RATE(1900000000UL, 0x0b4f0aaa, 0x0000ab00, 0xa0458686), + CCU_PLLA_RATE(1950000000UL, 0x0b511000, 0x00000000, 0xa0458686), CCU_PLLA_RATE(2000000000UL, 0x0b292aaa, 0x0000ab00, 0xa0558686), }; diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c index 5914b2fd94d9..e814670da55a 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c @@ -453,15 +453,6 @@ static int altr_sdram_probe(struct platform_device *pdev) return res; } -static void altr_sdram_remove(struct platform_device *pdev) -{ - struct mem_ctl_info *mci = platform_get_drvdata(pdev); - - edac_mc_del_mc(&pdev->dev); - edac_mc_free(mci); - platform_set_drvdata(pdev, NULL); -} - /* * If you want to suspend, need to disable EDAC by removing it * from the device tree or defconfig. @@ -481,13 +472,13 @@ static const struct dev_pm_ops altr_sdram_pm_ops = { static struct platform_driver altr_sdram_edac_driver = { .probe = altr_sdram_probe, - .remove = altr_sdram_remove, .driver = { .name = "altr_sdram_edac", #ifdef CONFIG_PM .pm = &altr_sdram_pm_ops, #endif .of_match_table = altr_sdram_ctrl_of_match, + .suppress_bind_attrs = true, }, }; @@ -517,6 +508,7 @@ static struct platform_driver altr_edac_driver = { .driver = { .name = "socfpga_ecc_manager", .of_match_table = altr_edac_of_match, + .suppress_bind_attrs = true, }, }; module_platform_driver(altr_edac_driver); @@ -803,22 +795,12 @@ static int altr_edac_device_probe(struct platform_device *pdev) return res; } -static void altr_edac_device_remove(struct platform_device *pdev) -{ - struct edac_device_ctl_info *dci = platform_get_drvdata(pdev); - struct altr_edac_device_dev *drvdata = dci->pvt_info; - - debugfs_remove_recursive(drvdata->debugfs_dir); - edac_device_del_device(&pdev->dev); - edac_device_free_ctl_info(dci); -} - static struct platform_driver altr_edac_device_driver = { .probe = altr_edac_device_probe, - .remove = altr_edac_device_remove, .driver = { .name = "altr_edac_device", .of_match_table = altr_edac_device_of_match, + .suppress_bind_attrs = true, }, }; module_platform_driver(altr_edac_device_driver); @@ -944,7 +926,7 @@ static int __maybe_unused altr_init_memory_port(void __iomem *ioaddr, int port) return ret; } -static __init int __maybe_unused +static int __maybe_unused altr_init_a10_ecc_block(struct device_node *np, u32 irq_mask, u32 ecc_ctrl_en_mask, bool dual_port) { @@ -1019,7 +1001,7 @@ altr_init_a10_ecc_block(struct device_node *np, u32 irq_mask, static int validate_parent_available(struct device_node *np); static const struct of_device_id altr_edac_a10_device_of_match[]; -static int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat) +static int __maybe_unused altr_init_a10_ecc_device_type(char *compat) { int irq; struct device_node *child, *np; @@ -1348,7 +1330,7 @@ static const struct edac_device_prv_data a10_l2ecc_data = { #ifdef CONFIG_EDAC_ALTERA_ETHERNET -static int __init socfpga_init_ethernet_ecc(struct altr_edac_device_dev *dev) +static int socfpga_init_ethernet_ecc(struct altr_edac_device_dev *dev) { int ret; @@ -1378,7 +1360,7 @@ static const struct edac_device_prv_data a10_enetecc_data = { #ifdef CONFIG_EDAC_ALTERA_NAND -static int __init socfpga_init_nand_ecc(struct altr_edac_device_dev *device) +static int socfpga_init_nand_ecc(struct altr_edac_device_dev *device) { int ret; @@ -1408,7 +1390,7 @@ static const struct edac_device_prv_data a10_nandecc_data = { #ifdef CONFIG_EDAC_ALTERA_DMA -static int __init socfpga_init_dma_ecc(struct altr_edac_device_dev *device) +static int socfpga_init_dma_ecc(struct altr_edac_device_dev *device) { int ret; @@ -1438,7 +1420,7 @@ static const struct edac_device_prv_data a10_dmaecc_data = { #ifdef CONFIG_EDAC_ALTERA_USB -static int __init socfpga_init_usb_ecc(struct altr_edac_device_dev *device) +static int socfpga_init_usb_ecc(struct altr_edac_device_dev *device) { int ret; @@ -1468,7 +1450,7 @@ static const struct edac_device_prv_data a10_usbecc_data = { #ifdef CONFIG_EDAC_ALTERA_QSPI -static int __init socfpga_init_qspi_ecc(struct altr_edac_device_dev *device) +static int socfpga_init_qspi_ecc(struct altr_edac_device_dev *device) { int ret; @@ -1523,6 +1505,7 @@ static int altr_portb_setup(struct altr_edac_device_dev *device) dci = edac_device_alloc_ctl_info(sizeof(*altdev), ecc_name, 1, ecc_name, 1, 0, edac_idx); if (!dci) { + of_node_put(np); edac_printk(KERN_ERR, EDAC_DEVICE, "%s: Unable to allocate PortB EDAC device\n", ecc_name); @@ -1533,8 +1516,11 @@ static int altr_portb_setup(struct altr_edac_device_dev *device) altdev = dci->pvt_info; *altdev = *device; - if (!devres_open_group(device->edac->dev, altr_portb_setup, GFP_KERNEL)) + if (!devres_open_group(device->edac->dev, altr_portb_setup, GFP_KERNEL)) { + edac_device_free_ctl_info(dci); + of_node_put(np); return -ENOMEM; + } /* Update PortB specific values */ altdev->edac_dev_name = ecc_name; @@ -1601,6 +1587,8 @@ static int altr_portb_setup(struct altr_edac_device_dev *device) rc = -ENOMEM; goto err_release_group_1; } + of_node_put(np); + altr_create_edacdev_dbgfs(dci, prv); list_add(&altdev->next, &altdev->edac->a10_ecc_devices); @@ -1610,14 +1598,20 @@ static int altr_portb_setup(struct altr_edac_device_dev *device) return 0; err_release_group_1: - edac_device_free_ctl_info(dci); + /* + * Release the devres group first so the managed IRQs are + * unregistered before dci (which contains the IRQ handler's + * data via dci->pvt_info) is freed, avoiding a use-after-free. + */ devres_release_group(device->edac->dev, altr_portb_setup); + edac_device_free_ctl_info(dci); + of_node_put(np); edac_printk(KERN_ERR, EDAC_DEVICE, "%s:Error setting up EDAC device: %d\n", ecc_name, rc); return rc; } -static int __init socfpga_init_sdmmc_ecc(struct altr_edac_device_dev *device) +static int socfpga_init_sdmmc_ecc(struct altr_edac_device_dev *device) { int rc = -ENODEV; struct device_node *child; @@ -1639,7 +1633,7 @@ static int __init socfpga_init_sdmmc_ecc(struct altr_edac_device_dev *device) goto exit; /* Setup portB */ - return altr_portb_setup(device); + rc = altr_portb_setup(device); exit: of_node_put(child); @@ -2014,9 +2008,17 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac, return 0; err_release_group1: + /* + * Release the devres group first so the managed IRQs are + * unregistered before dci (which contains the IRQ handler's + * data via dci->pvt_info) is freed, avoiding a use-after-free. + */ + devres_release_group(edac->dev, NULL); edac_device_free_ctl_info(dci); + goto err_print; err_release_group: devres_release_group(edac->dev, NULL); +err_print: edac_printk(KERN_ERR, EDAC_DEVICE, "%s:Error setting up EDAC device: %d\n", ecc_name, rc); @@ -2217,6 +2219,7 @@ static struct platform_driver altr_edac_a10_driver = { .driver = { .name = "socfpga_a10_ecc_manager", .of_match_table = altr_edac_a10_of_match, + .suppress_bind_attrs = true, }, }; module_platform_driver(altr_edac_a10_driver); diff --git a/drivers/edac/versalnet_edac.c b/drivers/edac/versalnet_edac.c index 97ec05d68bbb..3a06b41c1d84 100644 --- a/drivers/edac/versalnet_edac.c +++ b/drivers/edac/versalnet_edac.c @@ -953,6 +953,7 @@ static void mc_remove(struct platform_device *pdev) remove_versalnet(priv); rproc_shutdown(priv->mcdi->r5_rproc); cdx_mcdi_finish(priv->mcdi); + rproc_put(priv->mcdi->r5_rproc); kfree(priv->mcdi); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index 9014678d75ab..be4d00ac96b3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c @@ -318,7 +318,7 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block) r = device_for_each_child(adev->acp.parent, &adev->acp.acp_genpd->gpd, acp_genpd_add_device); if (r) - goto failure; + goto failure_remove_mfd; break; } default: @@ -443,7 +443,7 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block) r = device_for_each_child(adev->acp.parent, &adev->acp.acp_genpd->gpd, acp_genpd_add_device); if (r) - goto failure; + goto failure_remove_mfd; } /* Assert Soft reset of ACP */ @@ -461,7 +461,7 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block) if (--count == 0) { dev_err(&adev->pdev->dev, "Failed to reset ACP\n"); r = -ETIMEDOUT; - goto failure; + goto failure_remove_mfd; } udelay(100); } @@ -479,7 +479,7 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block) if (--count == 0) { dev_err(&adev->pdev->dev, "Failed to reset ACP\n"); r = -ETIMEDOUT; - goto failure; + goto failure_remove_mfd; } udelay(100); } @@ -489,6 +489,11 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block) cgs_write_register(adev->acp.cgs_device, mmACP_SOFT_RESET, val); return 0; +failure_remove_mfd: + device_for_each_child(adev->acp.parent, NULL, + acp_genpd_remove_device); + mfd_remove_devices(adev->acp.parent); + failure: kfree(adev->acp.i2s_pdata); kfree(adev->acp.acp_res); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 47e0680ed7b1..2b2c6795f20b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -1270,6 +1270,8 @@ static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev, ip_hw_instance->kobj.kset = &ip_hw_id->hw_id_kset; res = kobject_add(&ip_hw_instance->kobj, NULL, "%d", ip_hw_instance->num_instance); + if (res) + kobject_put(&ip_hw_instance->kobj); next_ip: if (reg_base_64) ip_offset += struct_size(ip, base_address_64, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index e814701bc8fd..14b539d50efa 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2653,6 +2653,14 @@ static int amdgpu_pmops_suspend_noirq(struct device *dev) struct amdgpu_device *adev = drm_to_adev(drm_dev); int r; + /* + * A GPU parked by vga_switcheroo has no power and no PCIe link, so the + * ASIC reset below would fail and abort the whole noirq suspend phase. + * Bail out like amdgpu_device_prepare/suspend/resume() already do. + */ + if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) + return 0; + if (amdgpu_acpi_should_gpu_reset(adev)) { amdgpu_device_lock_reset_domain(adev->reset_domain); r = amdgpu_asic_reset(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index ea416749138d..7375fc8b0641 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2320,7 +2320,7 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev) void amdgpu_ttm_enable_buffer_funcs(struct amdgpu_device *adev) { struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM); - u32 num_clear_entities, num_move_entities; + u32 num_clear_entities, num_move_entities, sdma_ip_version; int r, i, j; if (!adev->mman.initialized || amdgpu_in_reset(adev) || @@ -2345,6 +2345,12 @@ void amdgpu_ttm_enable_buffer_funcs(struct amdgpu_device *adev) num_clear_entities = MIN(adev->mman.num_buffer_funcs_scheds, TTM_NUM_MOVE_FENCES); num_move_entities = MIN(adev->mman.num_buffer_funcs_scheds, TTM_NUM_MOVE_FENCES); + /* TODO: workaround for DCC corruption when moving BOs from multiple queues at + * the same time: use a single queue until the root cause is identified and fixed. + */ + sdma_ip_version = amdgpu_ip_version(adev, SDMA0_HWIP, 0); + if (sdma_ip_version == IP_VERSION(7, 0, 0) || sdma_ip_version == IP_VERSION(7, 0, 1)) + num_move_entities = 1; adev->mman.clear_entities = kcalloc(num_clear_entities, sizeof(struct amdgpu_ttm_buffer_entity), diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c index 8f3f90d5ce37..d5141bcb283b 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c @@ -322,6 +322,14 @@ static const u32 verde_rlc_save_restore_register_list[] = 0x00000000 }; +static void gfx_v6_0_free_microcode(struct amdgpu_device *adev) +{ + amdgpu_ucode_release(&adev->gfx.pfp_fw); + amdgpu_ucode_release(&adev->gfx.me_fw); + amdgpu_ucode_release(&adev->gfx.ce_fw); + amdgpu_ucode_release(&adev->gfx.rlc_fw); +} + static int gfx_v6_0_init_microcode(struct amdgpu_device *adev) { const char *chip_name; @@ -3187,6 +3195,7 @@ static int gfx_v6_0_sw_fini(struct amdgpu_ip_block *ip_block) amdgpu_ring_fini(&adev->gfx.compute_ring[i]); amdgpu_gfx_rlc_fini(adev); + gfx_v6_0_free_microcode(adev); return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c index 84c93364d220..0fc0c783b1cc 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c @@ -327,7 +327,7 @@ static void gmc_v12_0_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid, return; } - gmc_v12_0_flush_vm_hub(adev, vmid, vmhub, 0); + gmc_v12_0_flush_vm_hub(adev, vmid, vmhub, flush_type); return; } diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c index 5fe43f7eab29..5d0d316fa94e 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c @@ -389,7 +389,7 @@ static void gmc_v12_1_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid, return; } - gmc_v12_1_flush_vm_hub(adev, vmid, vmhub, 0); + gmc_v12_1_flush_vm_hub(adev, vmid, vmhub, flush_type); return; } diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c index b06001f6b536..9f438a7e4440 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c @@ -1356,10 +1356,6 @@ static int sdma_v7_1_sw_init(struct amdgpu_ip_block *ip_block) else DRM_ERROR("Failed to allocated memory for SDMA IP Dump\n"); -#ifdef CONFIG_DRM_AMDGPU_NAVI3X_USERQ - adev->userq_funcs[AMDGPU_HW_IP_DMA] = &userq_mes_funcs; -#endif - return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c index a256320b92f3..fcc93c76ad74 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi.c +++ b/drivers/gpu/drm/amd/amdgpu/vi.c @@ -1407,10 +1407,31 @@ static uint64_t vi_get_pcie_replay_count(struct amdgpu_device *adev) return (nak_r + nak_g); } +struct vi_reset_quirk { + u16 device; + u16 subsystem_vendor; + u16 subsystem_device; + u8 revision; +}; + +static const struct vi_reset_quirk vi_reset_quirks[] = { + { 0x67ef, PCI_VENDOR_ID_APPLE, 0x0190, 0xe3 }, /* Radeon Pro 555X */ + { 0x67ef, PCI_VENDOR_ID_APPLE, 0x018f, 0xc2 }, /* Radeon Pro 560X */ + { 0x67ef, PCI_VENDOR_ID_APPLE, 0x017a, 0xc7 }, /* Radeon Pro 555 */ +}; + static bool vi_need_reset_on_init(struct amdgpu_device *adev) { + unsigned int i; u32 clock_cntl, pc; + for (i = 0; i < ARRAY_SIZE(vi_reset_quirks); i++) + if (adev->pdev->device == vi_reset_quirks[i].device && + adev->pdev->subsystem_vendor == vi_reset_quirks[i].subsystem_vendor && + adev->pdev->subsystem_device == vi_reset_quirks[i].subsystem_device && + adev->pdev->revision == vi_reset_quirks[i].revision) + return true; + if (adev->flags & AMD_IS_APU) return false; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index e5945485ec59..4ee7db8a6a22 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -70,6 +70,7 @@ #include "ivsrcid/ivsrcid_vislands30.h" #include +#include #include #include #include @@ -1974,6 +1975,8 @@ static int amdgpu_dm_init_power_module(struct amdgpu_display_manager *dm) init_data[i].use_custom_backlight_caps = false; init_data[i].custom_backlight_caps_config_no = 0; init_data[i].use_linear_backlight_curve = false; + if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE) + init_data[i].use_linear_backlight_curve = true; init_data[i].def_varibright_enable = 0; init_data[i].varibright_level = 0; /* @@ -3516,9 +3519,14 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block) dc_allow_idle_optimizations(adev->dm.dc, false); dm->cached_dc_state = dc_state_create_copy(dm->dc->current_state); + if (!dm->cached_dc_state) { + drm_err(adev_to_drm(adev), + "Failed to allocate cached DC state during suspend\n"); + mutex_unlock(&dm->dc_lock); + return -ENOMEM; + } - if (dm->cached_dc_state) - dm_gpureset_toggle_interrupts(adev, dm->cached_dc_state, false); + dm_gpureset_toggle_interrupts(adev, dm->cached_dc_state, false); res = amdgpu_dm_commit_zero_streams(dm->dc); if (res != DC_OK) { @@ -5299,6 +5307,68 @@ static inline u32 scale_fw_to_input(int min, int max, u64 input) return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL); } +static u32 backlight_signal_to_luminance(const struct amdgpu_dm_backlight_caps *caps, + u32 signal) +{ + u32 lower_signal, upper_signal; + u32 lower_lum, upper_lum; + int i; + + if (!caps->data_points) + return DIV_ROUND_CLOSEST(signal * 100, AMDGPU_MAX_BL_LEVEL); + + if (signal < caps->luminance_data[0].input_signal) { + lower_signal = 0; + lower_lum = 0; + upper_signal = caps->luminance_data[0].input_signal; + upper_lum = caps->luminance_data[0].luminance; + } else if (signal >= + caps->luminance_data[caps->data_points - 1].input_signal) { + lower_signal = caps->luminance_data[caps->data_points - 1].input_signal; + lower_lum = caps->luminance_data[caps->data_points - 1].luminance; + upper_signal = caps->max_input_signal; + upper_lum = 100; + } else { + for (i = 1; + i < caps->data_points && + signal > caps->luminance_data[i].input_signal; + i++) + ; + + lower_signal = caps->luminance_data[i - 1].input_signal; + upper_signal = caps->luminance_data[i].input_signal; + lower_lum = caps->luminance_data[i - 1].luminance; + upper_lum = caps->luminance_data[i].luminance; + } + + if (upper_signal == lower_signal) + return upper_lum; + + return lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) * + (signal - lower_signal), + upper_signal - lower_signal); +} + +static u32 convert_brightness_to_millipercent(const struct amdgpu_dm_backlight_caps *caps, + u32 user_brightness) +{ + unsigned int min, max; + u32 signal, luminance; + + if (!get_brightness_range(caps, &min, &max)) + return DIV_ROUND_CLOSEST_ULL((u64)user_brightness * 100000, + MAX_BACKLIGHT_LEVEL); + + user_brightness = clamp(user_brightness, min, max); + if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE) + return DIV_ROUND_CLOSEST_ULL((u64)user_brightness * 100000, max); + + signal = scale_input_to_fw(max, user_brightness); + luminance = backlight_signal_to_luminance(caps, signal); + + return min_t(u32, luminance * 1000, 100000); +} + static void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps, unsigned int min, unsigned int max, uint32_t *user_brightness) @@ -5416,10 +5486,10 @@ static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, struct amdgpu_dm_backlight_caps *caps; struct dc_link *link; u32 brightness = 0; + u32 millipercent = 0; bool rc = false, reallow_idle = false; struct drm_connector *connector; struct dc_stream_state *stream; - unsigned int min, max; list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); @@ -5442,12 +5512,21 @@ static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, /* update scratch register */ if (bl_idx == 0) amdgpu_atombios_scratch_regs_set_backlight_level(dm->adev, dm->brightness[bl_idx]); - brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]); + link = (struct dc_link *)dm->backlight_link[bl_idx]; + stream = dm_find_stream_with_link(dm, link); + if (!stream) + return; - /* Apply brightness quirk */ - if (caps->brightness_mask) - brightness |= caps->brightness_mask; + if (caps->aux_support) { + brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]); + /* Apply brightness quirk */ + if (caps->brightness_mask) + brightness |= caps->brightness_mask; + } else { + millipercent = convert_brightness_to_millipercent(caps, + user_brightness); + } if (trace_amdgpu_dm_brightness_enabled()) { trace_amdgpu_dm_brightness(__builtin_return_address(0), @@ -5457,10 +5536,6 @@ static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, power_supply_is_system_supplied() > 0); } - stream = dm_find_stream_with_link(dm, link); - if (!stream) - return; - mutex_lock(&dm->dc_lock); if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) { dc_allow_idle_optimizations(dm->dc, false); @@ -5472,10 +5547,8 @@ static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, AUX_BL_DEFAULT_TRANSITION_TIME_MS, false, true); } else { /* power module uses millipercent */ - get_brightness_range(caps, &min, &max); - brightness = DIV_ROUND_CLOSEST(brightness * 100, (max - min)) * 1000; rc = mod_power_set_backlight_percent(dm->power_module, stream, - brightness, 0, false); + millipercent, 0, false); } /* diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index b3af7445b457..145cec552c21 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -1181,7 +1181,8 @@ static bool get_conv_frl_bw(struct amdgpu_dm_connector *aconnector, unsigned int max_sink_bw_in_kbps = 0; unsigned int dsc_max_sink_bw_in_kbps = 0; - if (aconnector->dc_link->dc->caps.dp_hdmi21_pcon_support && + if (aconnector->dc_sink && + aconnector->dc_link->dc->caps.dp_hdmi21_pcon_support && aconnector->mst_downstream_port_caps.bytes.byte0.bits.DWN_STRM_PORTX_TYPE == DOWN_STREAM_DETAILED_HDMI) { max_conv_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data( aconnector->dc_link->dc, @@ -1946,6 +1947,9 @@ enum dc_status dm_dp_mst_is_port_support_mode( struct dc_dsc_config_options dsc_options = {0}; uint32_t stream_kbps; + if (!aconnector->dc_sink) + return DC_FAIL_BANDWIDTH_VALIDATE; + /* DSC unnecessary case * Check if timing could be supported within end-to-end BW */ diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 1dc85f6b6689..79184384cdfd 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -1763,6 +1763,7 @@ struct dc_scratch_space { struct ddc_service *ddc; enum dp_panel_mode panel_mode; + bool panel_mode_initialized; bool aux_mode; /* Private to DC core */ diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c index 80a372ceaa51..20157f67be07 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c @@ -92,6 +92,7 @@ void dp_set_panel_mode(struct dc_link *link, enum dp_panel_mode panel_mode) } link->panel_mode = panel_mode; + link->panel_mode_initialized = true; DC_LOG_DETECTION_DP_CAPS("%d eDP panel mode supported: %d, enabled: %d\n", link->link_index, link->dpcd_caps.panel_mode_edp, @@ -1300,7 +1301,8 @@ void edp_set_panel_assr(struct dc_link *link, struct pipe_ctx *pipe_ctx, result = cp_psp->funcs.enable_assr(cp_psp->handle, link); - if (!result && link->panel_mode != DP_PANEL_MODE_EDP) + if (!result && link->panel_mode_initialized && + link->panel_mode != DP_PANEL_MODE_EDP) *panel_mode = DP_PANEL_MODE_DEFAULT; } } diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c index d2d56a1c4b8b..65c7dae3ff66 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c @@ -448,6 +448,12 @@ static const struct dce_abm_mask abm_mask = { ABM_MASK_SH_LIST_DCE110(_MASK) }; +static const struct dc_debug_options debug_defaults = { 0 }; + +static const struct dc_check_config config_defaults = { + .enable_legacy_fast_update = true, +}; + #define CTX ctx #define REG(reg) mm ## reg @@ -937,6 +943,8 @@ static bool dce60_construct( dc->caps.max_cursor_size = 64; dc->caps.dual_link_dvi = true; dc->caps.extended_aux_timeout_support = false; + dc->debug = debug_defaults; + dc->check_config = config_defaults; /************************************************* * Create resources * @@ -1134,6 +1142,8 @@ static bool dce61_construct( dc->caps.i2c_speed_in_khz = 40; dc->caps.max_cursor_size = 64; dc->caps.is_apu = true; + dc->debug = debug_defaults; + dc->check_config = config_defaults; /************************************************* * Create resources * @@ -1331,7 +1341,8 @@ static bool dce64_construct( dc->caps.max_downscale_ratio = 200; dc->caps.i2c_speed_in_khz = 40; dc->caps.max_cursor_size = 64; - dc->caps.is_apu = true; + dc->debug = debug_defaults; + dc->check_config = config_defaults; /************************************************* * Create resources * diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c index 26820d35478a..2f4de697ec4b 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c @@ -1148,6 +1148,8 @@ static bool dce81_construct( dc->caps.max_cursor_size = 128; dc->caps.min_horizontal_blanking_period = 80; dc->caps.is_apu = true; + dc->debug = debug_defaults; + dc->check_config = config_defaults; /************************************************* * Create resources * diff --git a/drivers/gpu/drm/amd/display/modules/power/power.c b/drivers/gpu/drm/amd/display/modules/power/power.c index 5659a38b3366..d0afb0e03e3a 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power.c +++ b/drivers/gpu/drm/amd/display/modules/power/power.c @@ -333,6 +333,7 @@ bool mod_power_add_stream(struct mod_power *mod_power, core_power->map[core_power->num_entities].psr_enabled = 0; core_power->map[core_power->num_entities].psr_events = psr_event_vsync; core_power->map[core_power->num_entities].psr_power_opt = 0; + core_power->map[core_power->num_entities].replay_events = replay_event_vsync; core_power->num_entities++; return true; } @@ -392,6 +393,7 @@ bool mod_power_remove_stream(struct mod_power *mod_power, core_power->map[i].psr_enabled = core_power->map[i + 1].psr_enabled; core_power->map[i].psr_events = core_power->map[i + 1].psr_events; core_power->map[i].psr_power_opt = core_power->map[i + 1].psr_power_opt; + core_power->map[i].replay_events = core_power->map[i + 1].replay_events; memcpy(core_power->map[i].psr_context, core_power->map[i + 1].psr_context, sizeof(struct mod_power_psr_context)); memset(core_power->map[i + 1].psr_context, 0, sizeof(struct mod_power_psr_context)); 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 012227c70600..d26be73637e3 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c @@ -5263,7 +5263,8 @@ static int si_init_smc_table(struct amdgpu_device *adev) break; } - if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC) + if ((adev->flags & AMD_IS_MOBILITY) && + (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)) table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC; if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT) { @@ -7237,6 +7238,19 @@ static void si_parse_pplib_non_clock_info(struct amdgpu_device *adev, adev->pm.dpm.uvd_ps = rps; } +static void si_update_limits(struct amdgpu_clock_and_voltage_limits *limits, + struct rv7xx_pl *pl) +{ + if (pl->sclk > limits->sclk) + limits->sclk = pl->sclk; + if (pl->mclk > limits->mclk) + limits->mclk = pl->mclk; + if (pl->vddc > limits->vddc) + limits->vddc = pl->vddc; + if (pl->vddci > limits->vddci) + limits->vddci = pl->vddci; +} + static void si_parse_pplib_clock_info(struct amdgpu_device *adev, struct amdgpu_ps *rps, int index, union pplib_clock_info *clock_info) @@ -7245,7 +7259,6 @@ static void si_parse_pplib_clock_info(struct amdgpu_device *adev, struct evergreen_power_info *eg_pi = evergreen_get_pi(adev); struct si_power_info *si_pi = si_get_pi(adev); struct si_ps *ps = si_get_ps(rps); - struct amdgpu_clock_and_voltage_limits *limits; u16 leakage_voltage; struct rv7xx_pl *pl = &ps->performance_levels[index]; int ret; @@ -7310,26 +7323,14 @@ static void si_parse_pplib_clock_info(struct amdgpu_device *adev, * VBIOS can contain conflicting values between: * - the maximum allowed clocks and voltages on AC or DC * - the clocks and voltages in power states on AC or DC + * + * Assume that the AC limits are the maximum of all power states, + * and the DC limits are the maximum of battery power states. */ + si_update_limits(&adev->pm.dpm.dyn_state.max_clock_voltage_on_ac, pl); if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == - ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) - limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac; - else if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) - limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_dc; - else - limits = NULL; - - if (limits) { - if (pl->sclk > limits->sclk) - limits->sclk = pl->sclk; - if (pl->mclk > limits->mclk) - limits->mclk = pl->mclk; - if (pl->vddc > limits->vddc) - limits->vddc = pl->vddc; - if (pl->vddci > limits->vddci) - limits->vddci = pl->vddci; - } + si_update_limits(&adev->pm.dpm.dyn_state.max_clock_voltage_on_dc, pl); } union pplib_power_state { diff --git a/drivers/gpu/drm/i915/display/intel_display_params.c b/drivers/gpu/drm/i915/display/intel_display_params.c index 2aed110c5b09..ca0ef466bb10 100644 --- a/drivers/gpu/drm/i915/display/intel_display_params.c +++ b/drivers/gpu/drm/i915/display/intel_display_params.c @@ -120,6 +120,10 @@ intel_display_param_named_unsafe(enable_psr, int, 0400, "(0=disabled, 1=enable up to PSR1, 2=enable up to PSR2) " "Default: -1 (use per-chip default)"); +intel_display_param_named_unsafe(enable_dc_balance, bool, 0400, + "Enable VRR DC balance (0=disabled, 1=enabled). " + "Default: 0 (disabled)"); + intel_display_param_named_unsafe(enable_panel_replay, int, 0400, "Enable Panel Replay (0=disabled, 1=enabled). Default: -1 (use per-chip default)"); diff --git a/drivers/gpu/drm/i915/display/intel_display_params.h b/drivers/gpu/drm/i915/display/intel_display_params.h index b95ecf728daa..16759e94c025 100644 --- a/drivers/gpu/drm/i915/display/intel_display_params.h +++ b/drivers/gpu/drm/i915/display/intel_display_params.h @@ -46,6 +46,7 @@ struct drm_printer; param(bool, enable_dp_mst, true, 0600) \ param(int, enable_fbc, -1, 0600) \ param(int, enable_psr, -1, 0600) \ + param(bool, enable_dc_balance, false, 0600) \ param(int, enable_panel_replay, -1, 0600) \ param(bool, psr_safest_params, false, 0400) \ param(bool, enable_psr2_sel_fetch, true, 0400) \ diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index aa587be908f1..69647d3f116c 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -383,10 +383,12 @@ static bool intel_vrr_dc_balance_possible(const struct intel_crtc_state *crtc_st static void intel_vrr_dc_balance_compute_config(struct intel_crtc_state *crtc_state) { + struct intel_display *display = to_intel_display(crtc_state); int guardband_usec, adjustment_usec; struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; - if (!intel_vrr_dc_balance_possible(crtc_state) || !crtc_state->vrr.enable) + if (!intel_vrr_dc_balance_possible(crtc_state) || + !crtc_state->vrr.enable || !display->params.enable_dc_balance) return; crtc_state->vrr.dc_balance.vmax = crtc_state->vrr.vmax; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index c86a3f54f35b..dc8e3e917dd7 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -638,8 +638,10 @@ static int mtk_drm_bind(struct device *dev) private->mtk_drm_bound = true; private->dev = dev; - if (!mtk_drm_get_all_drm_priv(dev)) + if (!mtk_drm_get_all_drm_priv(dev)) { + put_device(private->mutex_dev); return 0; + } drm = drm_dev_alloc(&mtk_drm_driver, dev); if (IS_ERR(drm)) { @@ -1060,7 +1062,7 @@ static int mtk_drm_probe(struct platform_device *pdev) struct mtk_mmsys_driver_data *mtk_drm_data; struct device_node *node; struct component_match *match = NULL; - struct platform_device *ovl_adaptor; + struct platform_device *ovl_adaptor = NULL; int ret; int i; @@ -1112,6 +1114,9 @@ static int mtk_drm_probe(struct platform_device *pdev) PLATFORM_DEVID_AUTO, (void *)private->mmsys_dev, sizeof(*private->mmsys_dev)); + if (IS_ERR(ovl_adaptor)) + return PTR_ERR(ovl_adaptor); + private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR].dev = &ovl_adaptor->dev; mtk_ddp_comp_init(dev, NULL, &private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR], DDP_COMPONENT_DRM_OVL_ADAPTOR); @@ -1205,6 +1210,8 @@ static int mtk_drm_probe(struct platform_device *pdev) err_pm: pm_runtime_disable(dev); err_node: + if (ovl_adaptor) + platform_device_unregister(ovl_adaptor); of_node_put(private->mutex_node); for (i = 0; i < DDP_COMPONENT_DRM_ID_MAX; i++) of_node_put(private->comp_node[i]); @@ -1214,9 +1221,15 @@ static int mtk_drm_probe(struct platform_device *pdev) static void mtk_drm_remove(struct platform_device *pdev) { struct mtk_drm_private *private = platform_get_drvdata(pdev); + struct device *ovl_adaptor_dev; int i; + ovl_adaptor_dev = + private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR].dev; + component_master_del(&pdev->dev, &mtk_drm_ops); + if (ovl_adaptor_dev) + platform_device_unregister(to_platform_device(ovl_adaptor_dev)); pm_runtime_disable(&pdev->dev); of_node_put(private->mutex_node); for (i = 0; i < DDP_COMPONENT_DRM_ID_MAX; i++) diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c index 31e81a6de6d8..1ec973d3f52c 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c @@ -376,8 +376,10 @@ static int mtk_hdmi_ddc_v2_probe(struct platform_device *pdev) pm_runtime_get_sync(dev); ret = devm_i2c_add_adapter(dev, &ddc->adap); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_noidle(dev); return dev_err_probe(dev, ret, "Cannot add DDC I2C adapter\n"); + } platform_set_drvdata(pdev, ddc); return 0; diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h b/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h index 521b35c7e14d..bf3ebf5492d1 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h @@ -173,7 +173,7 @@ #define TPI_AUDIO_LOOKUP_EN BIT(2) /* Video downsampling configuration */ -#define VID_DOWNSAMPLE_CONFIG 0x8d0 +#define VID_DOWNSAMPLE_CONFIG 0x8f0 #define C444_C422_CONFIG_ENABLE BIT(0) #define C422_C420_CONFIG_ENABLE BIT(4) #define C422_C420_CONFIG_BYPASS BIT(5) diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index 3a8c5199a0fe..ef2567f1a37a 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c @@ -49,6 +49,7 @@ static bool igp_read_bios_from_vram(struct radeon_device *rdev) { uint8_t __iomem *bios; + uint8_t val1, val2; resource_size_t vram_base; resource_size_t size = 256 * 1024; /* ??? */ @@ -63,7 +64,10 @@ static bool igp_read_bios_from_vram(struct radeon_device *rdev) return false; } - if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) { + val1 = readb(&bios[0]); + val2 = readb(&bios[1]); + + if (size == 0 || val1 != 0x55 || val2 != 0xaa) { iounmap(bios); return false; } diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c index 6fce3de27296..b376a9167cc1 100644 --- a/drivers/gpu/drm/xe/xe_i2c.c +++ b/drivers/gpu/drm/xe/xe_i2c.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -215,11 +216,40 @@ void xe_i2c_irq_postinstall(struct xe_device *xe) xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, PCI_COMMAND_INTX_DISABLE, 0); } +/* See "Disabling DW_apb_i2c" in the DesignWare DW_abp_i2c databook. */ +static void xe_i2c_disable(struct xe_i2c *i2c) +{ + int timeout = 100; + u32 status; + + xe_mmio_rmw32(i2c->mmio, I2C_REG(DW_IC_ENABLE), DW_IC_ENABLE_ENABLE, 0); + + do { + status = xe_mmio_read32(i2c->mmio, I2C_REG(DW_IC_ENABLE_STATUS)); + if (!(status & DW_IC_ENABLE_ENABLE)) + return; + /* Can't sleep here. */ + udelay(25); + } while (timeout--); + + dev_warn(i2c->drm_dev, "timeout in disabling i2c adapter\n"); +} + static int xe_i2c_read(void *context, unsigned int reg, unsigned int *val) { struct xe_i2c *i2c = context; - *val = xe_mmio_read32(i2c->mmio, XE_REG(reg + I2C_MEM_SPACE_OFFSET)); + *val = xe_mmio_read32(i2c->mmio, I2C_REG(reg)); + + switch (reg) { + case DW_IC_ENABLE: + case DW_IC_ENABLE_STATUS: + FIELD_MODIFY(DW_IC_ENABLE_ENABLE, val, + i2c->ic_enable & DW_IC_ENABLE_ENABLE); + break; + default: + break; + } return 0; } @@ -229,6 +259,23 @@ static int xe_i2c_write(void *context, unsigned int reg, unsigned int val) struct xe_i2c *i2c = context; switch (reg) { + case DW_IC_CON: + case DW_IC_TAR: + case DW_IC_SAR: + /* Disable the controller. */ + xe_i2c_disable(i2c); + + /* Write the register. */ + xe_mmio_write32(i2c->mmio, I2C_REG(reg), val); + + /* Enable the controller. */ + xe_mmio_rmw32(i2c->mmio, I2C_REG(DW_IC_ENABLE), 0, DW_IC_ENABLE_ENABLE); + return 0; + case DW_IC_ENABLE: + i2c->ic_enable = val; + /* Other fields can be updated except the enable bit. */ + val |= DW_IC_ENABLE_ENABLE; + break; case DW_IC_SMBUS_INTR_MASK: /* Make sure the Alert is never masked. */ val |= DW_IC_SMBUS_INTR_ALERT; diff --git a/drivers/gpu/drm/xe/xe_i2c.h b/drivers/gpu/drm/xe/xe_i2c.h index b200966b0048..d63adacfefe7 100644 --- a/drivers/gpu/drm/xe/xe_i2c.h +++ b/drivers/gpu/drm/xe/xe_i2c.h @@ -37,6 +37,7 @@ struct xe_i2c { struct platform_device *pdev; struct i2c_adapter *adapter; struct i2c_client *client[XE_I2C_MAX_CLIENTS]; + unsigned int ic_enable; struct notifier_block bus_notifier; struct work_struct work; diff --git a/drivers/gpu/drm/xe/xe_pci_sriov.c b/drivers/gpu/drm/xe/xe_pci_sriov.c index 35e6b53e18ce..31abcee55211 100644 --- a/drivers/gpu/drm/xe/xe_pci_sriov.c +++ b/drivers/gpu/drm/xe/xe_pci_sriov.c @@ -82,16 +82,33 @@ static void pf_engine_activity_stats(struct xe_device *xe, unsigned int num_vfs, } } -static int resize_vf_vram_bar(struct xe_device *xe, int num_vfs) +static int pf_resize_vf_vram_bar(struct xe_device *xe, int num_vfs) { struct pci_dev *pdev = to_pci_dev(xe->drm.dev); + char buf[10]; u32 sizes; + int size; + int err; + + if (!IS_DGFX(xe)) + return 0; sizes = pci_iov_vf_bar_get_sizes(pdev, VF_LMEM_BAR, num_vfs); if (!sizes) return 0; - return pci_iov_vf_bar_set_size(pdev, VF_LMEM_BAR, __fls(sizes)); + size = __fls(sizes); + string_get_size(pci_rebar_size_to_bytes(size), 1, STRING_UNITS_2, buf, sizeof(buf)); + + err = pci_iov_vf_bar_set_size(pdev, VF_LMEM_BAR, size); + if (err) { + xe_sriov_notice(xe, "Failed to resize VF BAR %u to %s (%pe)\n", + VF_LMEM_BAR, buf, ERR_PTR(err)); + return err; + } + + xe_sriov_dbg(xe, "VF LMEM BAR resized to %s\n", buf); + return 0; } static int pf_prepare_vfs_enabling(struct xe_device *xe) @@ -142,11 +159,7 @@ static int pf_enable_vfs(struct xe_device *xe, int num_vfs) if (err < 0) goto failed; - if (IS_DGFX(xe)) { - err = resize_vf_vram_bar(xe, num_vfs); - if (err) - xe_sriov_info(xe, "Failed to set VF LMEM BAR size: %d\n", err); - } + pf_resize_vf_vram_bar(xe, num_vfs); err = pci_enable_sriov(pdev, num_vfs); if (err < 0) @@ -164,6 +177,7 @@ static int pf_enable_vfs(struct xe_device *xe, int num_vfs) return num_vfs; failed: + pf_resize_vf_vram_bar(xe, xe->sriov.pf.device_total_vfs); xe_sriov_pf_unprovision_vfs(xe, num_vfs); xe_pm_runtime_put(xe); pf_finish_vfs_enabling(xe); @@ -191,6 +205,8 @@ static int pf_disable_vfs(struct xe_device *xe) pci_disable_sriov(pdev); + pf_resize_vf_vram_bar(xe, xe->sriov.pf.device_total_vfs); + xe_sriov_pf_reprovision_default(xe); pf_reset_vfs(xe, num_vfs); diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index b1de1dd0f21d..ece2c7c45ea6 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -453,7 +453,7 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz, (u64)(long)ctx, true); /* prevent infinite recursions */ - if (ret > size) + if (ret > (ssize_t)size) ret = size; if (ret > 0) memcpy(buf, dma_data, ret); diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index e9da59e4bf79..29d7e33ee812 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -2260,7 +2260,7 @@ static void mt_remove(struct hid_device *hdev) { struct mt_device *td = hid_get_drvdata(hdev); - timer_delete_sync(&td->release_timer); + timer_shutdown_sync(&td->release_timer); sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); hid_hw_stop(hdev); diff --git a/drivers/i2c/busses/i2c-at91-master.c b/drivers/i2c/busses/i2c-at91-master.c index 68238cc8aee0..e0c3de5e8203 100644 --- a/drivers/i2c/busses/i2c-at91-master.c +++ b/drivers/i2c/busses/i2c-at91-master.c @@ -921,8 +921,14 @@ int at91_twi_probe_master(struct platform_device *pdev, at91_calc_twi_clock(dev); rc = at91_init_twi_recovery_info(pdev, dev); - if (rc == -EPROBE_DEFER) + if (rc == -EPROBE_DEFER) { + /* + * The DMA channels were already set up above, release them + * before deferring the probe so they are not leaked. + */ + at91_twi_dma_release(dev); return rc; + } dev->adapter.algo = &at91_twi_algorithm; dev->adapter.quirks = &at91_twi_quirks; diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 3e7735e1dae0..76ef9c26ed57 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -73,6 +73,9 @@ enum i2c_scl_freq { * @prev_msg_tx: Previous message is Tx * @quirks: To hold platform specific bug info * @smbus_block_read: Flag to handle block read + * @smbus_actual_len: Valid byte count (length + payload + optional PEC) of a + * padded SMBus block read. msg->len is trimmed to this on completion. + * Zero when no trimming is needed. * @input_clk: Input clock to I2C controller * @i2c_clk: I2C SCL frequency * @atomic: Mode of transfer @@ -98,6 +101,7 @@ struct xiic_i2c { bool prev_msg_tx; u32 quirks; bool smbus_block_read; + unsigned int smbus_actual_len; unsigned long input_clk; unsigned int i2c_clk; bool atomic; @@ -539,30 +543,38 @@ static void xiic_smbus_block_read_setup(struct xiic_i2c *i2c) /* Check if received length is valid */ if (rxmsg_len <= I2C_SMBUS_BLOCK_MAX) { + unsigned int pec_len = i2c->rx_msg->len - 1; + /* Set Receive fifo depth */ - if (rxmsg_len > IIC_RX_FIFO_DEPTH) { + if (rxmsg_len + pec_len > IIC_RX_FIFO_DEPTH) { /* - * When Rx msg len greater than or equal to Rx fifo capacity - * Receive fifo depth should set to Rx fifo capacity minus 1 + * Trailing bytes (payload plus any PEC) exceed Rx FIFO + * capacity, so drain in chunks. This also keeps the + * else branch below from pushing rfd_set past the + * 4-bit XIIC_RFD_REG_OFFSET field. */ rfd_set = IIC_RX_FIFO_DEPTH - 1; - i2c->rx_msg->len = rxmsg_len + 1; - } else if ((rxmsg_len == 1) || - (rxmsg_len == 0)) { + i2c->rx_msg->len = rxmsg_len + 1 + pec_len; + } else if (1 + rxmsg_len + pec_len < SMBUS_BLOCK_READ_MIN_LEN) { /* - * Minimum of 3 bytes required to exit cleanly. 1 byte - * already received, Second byte is being received. Have - * to set NACK in read_rx before receiving the last byte + * The HW needs SMBUS_BLOCK_READ_MIN_LEN bytes on the + * bus to exit cleanly: by the time the ISR reads the + * length byte the second byte is already being clocked + * in, too late to NACK. Pad the drain target and record + * the real length, trimmed back on completion so the + * PEC check sees the right byte. */ rfd_set = 0; i2c->rx_msg->len = SMBUS_BLOCK_READ_MIN_LEN; + i2c->smbus_actual_len = 1 + rxmsg_len + pec_len; } else { /* - * When Rx msg len less than Rx fifo capacity - * Receive fifo depth should set to Rx msg len minus 2 + * All trailing bytes fit in the Rx FIFO. The widened + * condition above guarantees rxmsg_len + pec_len >= 2, + * so this cannot underflow. */ - rfd_set = rxmsg_len - 2; - i2c->rx_msg->len = rxmsg_len + 1; + rfd_set = rxmsg_len + pec_len - 2; + i2c->rx_msg->len = rxmsg_len + 1 + pec_len; } xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rfd_set); @@ -575,6 +587,16 @@ static void xiic_smbus_block_read_setup(struct xiic_i2c *i2c) dev_err(i2c->adap.dev.parent, "smbus_block_read Invalid msg length\n"); } +/* + * Undo the setup-time padding of a short SMBus block read before rx_msg is + * cleared, so the PEC check sees the right byte. + */ +static void xiic_smbus_trim_len(struct xiic_i2c *i2c) +{ + if (i2c->rx_msg && i2c->smbus_actual_len) + i2c->rx_msg->len = i2c->smbus_actual_len; +} + static void xiic_read_rx(struct xiic_i2c *i2c) { u8 bytes_in_fifo, cr = 0, bytes_to_read = 0; @@ -797,6 +819,8 @@ static irqreturn_t xiic_process(int irq, void *dev_id) xiic_read_rx(i2c); if (xiic_rx_space(i2c) == 0) { + xiic_smbus_trim_len(i2c); + /* this is the last part of the message */ i2c->rx_msg = NULL; @@ -865,8 +889,11 @@ static irqreturn_t xiic_process(int irq, void *dev_id) if (i2c->tx_msg && i2c->smbus_block_read) { i2c->smbus_block_read = false; - /* Set requested message len=1 to indicate STATE_DONE */ - i2c->tx_msg->len = 1; + /* + * Drive xiic_tx_space() to 0 to signal STATE_DONE + * without truncating the rx_msg length. + */ + i2c->tx_pos = i2c->tx_msg->len; } if (!i2c->tx_msg) @@ -938,6 +965,7 @@ static void xiic_recv_atomic(struct xiic_i2c *i2c) return; } + xiic_smbus_trim_len(i2c); i2c->rx_msg = NULL; xiic_irq_clr_en(i2c, XIIC_INTR_TX_ERROR_MASK); @@ -955,6 +983,9 @@ static void xiic_start_recv(struct xiic_i2c *i2c) u8 cr = 0, rfd_set = 0; struct i2c_msg *msg = i2c->rx_msg = i2c->tx_msg; + /* A stale value from an aborted block read would truncate this msg. */ + i2c->smbus_actual_len = 0; + if (!i2c->atomic) dev_dbg(i2c->adap.dev.parent, "%s entry, ISR: 0x%x, CR: 0x%x\n", __func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET), diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 39485572a76b..8caa6551d2db 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -649,7 +649,7 @@ static int kx022a_read_raw(struct iio_dev *idev, if (ret) return ret; - if ((regval & KX022A_MASK_ODR) > + if ((regval & KX022A_MASK_ODR) >= ARRAY_SIZE(kx022a_accel_samp_freq_table)) { dev_err(data->dev, "Invalid ODR\n"); return -EINVAL; @@ -980,26 +980,44 @@ static int kx022a_fifo_enable(struct kx022a_data *data) guard(mutex)(&data->mutex); ret = __kx022a_turn_on_off(data, false); if (ret) - return ret; + goto err_free_out; /* Update watermark to HW */ ret = kx022a_fifo_set_wmi(data); if (ret) - return ret; + goto err_turn_on_out; /* Enable buffer */ ret = regmap_set_bits(data->regmap, data->chip_info->buf_cntl2, KX022A_MASK_BUF_EN); if (ret) - return ret; + goto err_turn_on_out; data->state |= KX022A_STATE_FIFO; ret = regmap_set_bits(data->regmap, data->ien_reg, KX022A_MASK_WMI); if (ret) - return ret; + goto err_buf_disable_out; - return __kx022a_turn_on_off(data, true); + ret = __kx022a_turn_on_off(data, true); + if (ret) + goto err_wmi_clear_out; + + return ret; + +err_wmi_clear_out: + regmap_clear_bits(data->regmap, data->ien_reg, + KX022A_MASK_WMI); +err_buf_disable_out: + regmap_clear_bits(data->regmap, data->chip_info->buf_cntl2, + KX022A_MASK_BUF_EN); + data->state &= ~KX022A_STATE_FIFO; +err_turn_on_out: + __kx022a_turn_on_off(data, true); +err_free_out: + kfree(data->fifo_buffer); + + return ret; } static int kx022a_buffer_postenable(struct iio_dev *idev) diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 166fb786425f..8994c9e8e048 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1030,7 +1030,7 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev, struct kxcjk1013_data *data = iio_priv(indio_dev); int ret; - if (state && data->ev_enable_state) + if (state == data->ev_enable_state) return 0; mutex_lock(&data->mutex); diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 573831199bba..cced5cd82b02 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -638,7 +638,7 @@ static int sca3000_write_raw_samp_freq(struct sca3000_state *st, int val) if (val == base_freq / 2) ctrlval |= SCA3000_REG_OUT_CTRL_BUF_DIV_2; - if (val == base_freq / 4) + else if (val == base_freq / 4) ctrlval |= SCA3000_REG_OUT_CTRL_BUF_DIV_4; else if (val != base_freq) return -EINVAL; diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c index 9c5f19321e3b..f1499b4792b5 100644 --- a/drivers/iio/adc/ad4030.c +++ b/drivers/iio/adc/ad4030.c @@ -746,14 +746,21 @@ static int ad4030_set_chan_calibbias(struct iio_dev *indio_dev, static int ad4030_set_avg_frame_len(struct iio_dev *dev, int avg_val) { struct ad4030_state *st = iio_priv(dev); - unsigned int avg_log2 = ilog2(avg_val); unsigned int last_avg_idx = ARRAY_SIZE(ad4030_average_modes) - 1; + unsigned int avg_log2; int freq_hz; int ret; - if (avg_val < 0 || avg_val > ad4030_average_modes[last_avg_idx]) + /* Reject unsupported modes */ + if (avg_val > ad4030_average_modes[last_avg_idx]) + return -EINVAL; + + /* Avoid invalid values for logarithm since it's undefined */ + if (avg_val < 1) return -EINVAL; + avg_log2 = ilog2(avg_val); + if (st->offload_trigger) { /* * The sample averaging and sampling frequency configurations diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 9ee65d63c525..4173ac1bbcb0 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -775,9 +775,9 @@ static int ad7173_load_config(struct ad7173_state *st, return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2, FIELD_PREP(AD7173_FILTER_SINC3_MAP, 0) | - FIELD_PREP(AD7173_FILTER_ENHFILT_MASK, - post_filter_enable) | FIELD_PREP(AD7173_FILTER_ENHFILTEN, + post_filter_enable) | + FIELD_PREP(AD7173_FILTER_ENHFILT_MASK, post_filter_select) | FIELD_PREP(AD7173_FILTER_ORDER, 0) | FIELD_PREP(AD7173_FILTER_ODR_MASK, diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 1b410291da53..119ddc5a13e2 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -498,7 +498,6 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type; struct spi_transfer *xfer = sigma_delta->sample_xfer; unsigned int i, slot, channel; - u8 *samples_buf; int ret; if (sigma_delta->num_slots == 1) { @@ -530,7 +529,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) xfer[1].bits_per_word = scan_type->realbits; xfer[1].len = spi_bpw_to_bytes(scan_type->realbits); } else { - unsigned int samples_buf_size, scan_size; + unsigned int scan_size; if (sigma_delta->active_slots > 1) { ret = ad_sigma_delta_append_status(sigma_delta, true); @@ -538,17 +537,6 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) return ret; } - samples_buf_size = - ALIGN(slot * BITS_TO_BYTES(scan_type->storagebits), - sizeof(s64)); - samples_buf_size += sizeof(s64); - samples_buf = devm_krealloc(&sigma_delta->spi->dev, - sigma_delta->samples_buf, - samples_buf_size, GFP_KERNEL); - if (!samples_buf) - return -ENOMEM; - - sigma_delta->samples_buf = samples_buf; scan_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift); /* For 24-bit data, there is an extra byte of padding. */ xfer[1].rx_buf = &sigma_delta->rx_buf[scan_size == 3 ? 1 : 0]; @@ -855,6 +843,23 @@ int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indi indio_dev->setup_ops = &ad_sd_buffer_setup_ops; } else { + const struct iio_scan_type *scan_type = + &indio_dev->channels[0].scan_type; + unsigned int samples_buf_size; + + /* + * Worst-case size: all sequencer slots can be active, capped + * at num_slots by ad_sd_validate_scan_mask(). + */ + samples_buf_size = + ALIGN(sigma_delta->num_slots * + BITS_TO_BYTES(scan_type->storagebits), + sizeof(s64)); + samples_buf_size += sizeof(s64); + sigma_delta->samples_buf = devm_kzalloc(dev, samples_buf_size, GFP_KERNEL); + if (!sigma_delta->samples_buf) + return -ENOMEM; + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, &iio_pollfunc_store_time, &ad_sd_trigger_handler, diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index c0a9babfa519..4e96cc37fbed 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -219,7 +219,7 @@ #define ADE9000_ST1_ERROR2_BIT BIT(30) #define ADE9000_ST1_ERROR3_BIT BIT(31) #define ADE9000_ST1_CROSSING_FIRST 6 -#define ADE9000_ST1_CROSSING_DEPTH 25 +#define ADE9000_ST1_CROSSING_DEPTH 26 #define ADE9000_WFB_TRG_DIP_BIT BIT(0) #define ADE9000_WFB_TRG_SWELL_BIT BIT(1) @@ -444,7 +444,7 @@ static const struct iio_chan_spec_ext_info ade9000_ext_info[] = { BIT(IIO_CHAN_INFO_CALIBSCALE), \ .event_spec = ade9000_current_events, \ .num_event_specs = ARRAY_SIZE(ade9000_current_events), \ - .scan_index = num, \ + .scan_index = num * 2, \ .indexed = 1, \ .scan_type = { \ .sign = 's', \ @@ -464,7 +464,7 @@ static const struct iio_chan_spec_ext_info ade9000_ext_info[] = { BIT(IIO_CHAN_INFO_FREQUENCY), \ .event_spec = ade9000_voltage_events, \ .num_event_specs = ARRAY_SIZE(ade9000_voltage_events), \ - .scan_index = num + 1, /* interleave with current channels */ \ + .scan_index = num * 2 + 1, /* interleave with current channels */ \ .indexed = 1, \ .scan_type = { \ .sign = 's', \ @@ -1647,8 +1647,9 @@ static int ade9000_setup_clkout(struct device *dev, struct ade9000_state *st) return 0; /* CLKOUT passes through CLKIN with divider of 1 */ - clkout_hw = devm_clk_hw_register_divider(dev, "clkout", __clk_get_name(st->clkin), - CLK_SET_RATE_PARENT, NULL, 0, 1, 0, NULL); + clkout_hw = devm_clk_hw_register_fixed_factor(dev, "clkout", + __clk_get_name(st->clkin), + CLK_SET_RATE_PARENT, 1, 1); if (IS_ERR(clkout_hw)) return dev_err_probe(dev, PTR_ERR(clkout_hw), "Failed to register clkout"); @@ -1705,18 +1706,6 @@ static int ade9000_probe(struct spi_device *spi) if (ret) return ret; - ret = ade9000_request_irq(dev, "irq0", ade9000_irq0_thread, indio_dev); - if (ret) - return ret; - - ret = ade9000_request_irq(dev, "irq1", ade9000_irq1_thread, indio_dev); - if (ret) - return ret; - - ret = ade9000_request_irq(dev, "dready", ade9000_dready_thread, indio_dev); - if (ret) - return ret; - /* External CMOS clock input (optional - crystal can be used instead) */ st->clkin = devm_clk_get_optional_enabled(dev, NULL); if (IS_ERR(st->clkin)) @@ -1736,6 +1725,19 @@ static int ade9000_probe(struct spi_device *spi) return dev_err_probe(&spi->dev, ret, "Failed to get and enable vdd regulator\n"); + /* Request IRQs only after powering the chip; the handlers do SPI access. */ + ret = ade9000_request_irq(dev, "irq0", ade9000_irq0_thread, indio_dev); + if (ret) + return ret; + + ret = ade9000_request_irq(dev, "irq1", ade9000_irq1_thread, indio_dev); + if (ret) + return ret; + + ret = ade9000_request_irq(dev, "dready", ade9000_dready_thread, indio_dev); + if (ret) + return ret; + indio_dev->channels = ade9000_channels; indio_dev->num_channels = ARRAY_SIZE(ade9000_channels); diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 589f03618ecf..fc7cc92b35f0 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -714,6 +714,10 @@ static int adi_axi_adc_probe(struct platform_device *pdev) if (!st) return -ENOMEM; + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 6169ce28261e..452c30cf04f2 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -585,7 +585,9 @@ static int aspeed_adc_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(data->rst), "invalid or missing reset controller device tree entry"); - reset_control_deassert(data->rst); + ret = reset_control_deassert(data->rst); + if (ret) + return ret; ret = devm_add_action_or_reset(dev, aspeed_adc_reset_assert, data->rst); if (ret) diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c index c8283279c477..c98863d612ad 100644 --- a/drivers/iio/adc/axp288_adc.c +++ b/drivers/iio/adc/axp288_adc.c @@ -207,6 +207,14 @@ static const struct dmi_system_id axp288_adc_ts_bias_override[] = { }, .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA, }, + { + /* Haier HV103H */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Haier"), + DMI_MATCH(DMI_PRODUCT_NAME, "HV103H"), + }, + .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA, + }, { } }; diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 65a2d92bb112..629c53af6472 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -404,6 +404,14 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev, data = rxbuf[0]; } + + /* + * Differential channels are bipolar and the device returns the sample + * in two's complement, so sign-extend it from the resolution bit. + */ + if (chan->differential) + data = sign_extend32(data, st->chip_info->bits - 1); + *val = data; return 0; diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c index 23055405a6e0..de59dc27c440 100644 --- a/drivers/iio/adc/pac1934.c +++ b/drivers/iio/adc/pac1934.c @@ -1108,6 +1108,10 @@ static int pac1934_acpi_parse_channel_config(struct i2c_client *client, devm_kmemdup(dev, rez->package.elements[i].string.pointer, (size_t)rez->package.elements[i].string.length + 1, GFP_KERNEL); + if (!info->labels[idx]) { + ACPI_FREE(rez); + return -ENOMEM; + } info->labels[idx][rez->package.elements[i].string.length] = '\0'; info->shunts[idx] = rez->package.elements[i + 1].integer.value * 1000; info->active_channels[idx] = (info->shunts[idx] != 0); diff --git a/drivers/iio/adc/rohm-bd79124.c b/drivers/iio/adc/rohm-bd79124.c index ed5427288961..8091a138cca8 100644 --- a/drivers/iio/adc/rohm-bd79124.c +++ b/drivers/iio/adc/rohm-bd79124.c @@ -200,7 +200,7 @@ static int bd79124gpo_set_multiple(struct gpio_chip *gc, unsigned long *mask, if (ret) return ret; - if (all_gpos ^ *mask) { + if (*mask & ~all_gpos) { dev_dbg(data->dev, "Invalid mux config. Can't set value.\n"); return -EINVAL; @@ -381,6 +381,9 @@ static int bd79124_start_measurement(struct bd79124_data *data, int chan) /* See if already started */ ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, &val); + if (ret) + return ret; + if (val & BIT(chan)) return 0; @@ -420,11 +423,16 @@ static int bd79124_stop_measurement(struct bd79124_data *data, int chan) /* See if already stopped */ ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, &enabled_chans); + if (ret) + return ret; + if (!(enabled_chans & BIT(chan))) return 0; ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, BD79124_MSK_SEQ_START); + if (ret) + return ret; /* Clear the channel from the measured channels */ enabled_chans &= ~BIT(chan); @@ -524,7 +532,7 @@ static int bd79124_enable_event(struct bd79124_data *data, return ret; if (dir == IIO_EV_DIR_RISING) { - limit = &data->alarm_f_limit[channel]; + limit = &data->alarm_r_limit[channel]; reg = BD79124_GET_HIGH_LIMIT_REG(channel); } else { limit = &data->alarm_f_limit[channel]; @@ -919,13 +927,13 @@ static int bd79124_chan_init(struct bd79124_data *data, int channel) { int ret; - ret = regmap_write(data->map, BD79124_GET_HIGH_LIMIT_REG(channel), - BD79124_HIGH_LIMIT_MAX); + ret = bd79124_write_int_to_reg(data, BD79124_GET_HIGH_LIMIT_REG(channel), + BD79124_HIGH_LIMIT_MAX); if (ret) return ret; - return regmap_write(data->map, BD79124_GET_LOW_LIMIT_REG(channel), - BD79124_LOW_LIMIT_MIN); + return bd79124_write_int_to_reg(data, BD79124_GET_LOW_LIMIT_REG(channel), + BD79124_LOW_LIMIT_MIN); } static int bd79124_get_gpio_pins(const struct iio_chan_spec *cs, int num_channels) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 5c6c06b269be..90f0e257e6d1 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -1608,11 +1608,16 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev, ret = stm32_adc_single_conv(indio_dev, chan, val); else ret = -EINVAL; + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; - if (mask == IIO_CHAN_INFO_PROCESSED) + if (mask == IIO_CHAN_INFO_PROCESSED) { + if (*val == 0) + return -EINVAL; *val = STM32_ADC_VREFINT_VOLTAGE * adc->vrefint.vrefint_cal / *val; + } - iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SCALE: @@ -2263,33 +2268,37 @@ static int stm32_adc_populate_int_ch(struct iio_dev *indio_dev, const char *ch_n for (i = 0; i < STM32_ADC_INT_CH_NB; i++) { if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) { + bool na; + /* Check internal channel availability */ switch (i) { case STM32_ADC_INT_CH_VDDCORE: - if (!adc->cfg->regs->or_vddcore.reg) - dev_warn(&indio_dev->dev, - "%s channel not available\n", ch_name); + na = !adc->cfg->regs->or_vddcore.reg; break; case STM32_ADC_INT_CH_VDDCPU: - if (!adc->cfg->regs->or_vddcpu.reg) - dev_warn(&indio_dev->dev, - "%s channel not available\n", ch_name); + na = !adc->cfg->regs->or_vddcpu.reg; break; case STM32_ADC_INT_CH_VDDQ_DDR: - if (!adc->cfg->regs->or_vddq_ddr.reg) - dev_warn(&indio_dev->dev, - "%s channel not available\n", ch_name); + na = !adc->cfg->regs->or_vddq_ddr.reg; break; case STM32_ADC_INT_CH_VREFINT: - if (!adc->cfg->regs->ccr_vref.reg) - dev_warn(&indio_dev->dev, - "%s channel not available\n", ch_name); + na = !adc->cfg->regs->ccr_vref.reg; break; case STM32_ADC_INT_CH_VBAT: - if (!adc->cfg->regs->ccr_vbat.reg) - dev_warn(&indio_dev->dev, - "%s channel not available\n", ch_name); + na = !adc->cfg->regs->ccr_vbat.reg; break; + default: + return -EINVAL; + } + + if (na) { + /* + * Channel label matches an internal STM32 ADC channel. + * Warn about it, as there's normally no restriction on the + * name but that's not among available internal channels. + */ + dev_warn(&indio_dev->dev, "no %s internal channel\n", ch_name); + return 0; } if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT) { diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c index 203459ca9907..ac5ba76b979e 100644 --- a/drivers/iio/adc/sun4i-gpadc-iio.c +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -638,10 +638,10 @@ static int sun4i_gpadc_probe(struct platform_device *pdev) * thermal because no thermal DT node is found. */ if (IS_ERR(info->tzd) && PTR_ERR(info->tzd) != -ENODEV) { - dev_err(&pdev->dev, - "could not register thermal sensor: %ld\n", - PTR_ERR(info->tzd)); - return PTR_ERR(info->tzd); + ret = dev_err_probe(&pdev->dev, + PTR_ERR(info->tzd), + "could not register thermal sensor\n"); + goto err_map; } } @@ -657,7 +657,6 @@ static int sun4i_gpadc_probe(struct platform_device *pdev) if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF)) iio_map_array_unregister(indio_dev); - pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); return ret; @@ -668,7 +667,6 @@ static void sun4i_gpadc_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct sun4i_gpadc_iio *info = iio_priv(indio_dev); - pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); if (!IS_ENABLED(CONFIG_THERMAL_OF)) diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index cab66bb8cc1c..66202b3a3abb 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -1401,13 +1401,14 @@ static int xadc_probe(struct platform_device *pdev) } if (irq > 0) { - ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler, - 0, dev_name(dev), indio_dev); + /* Clear out any work queued by the interrupt */ + ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work, + &xadc->zynq_unmask_work); if (ret) return ret; - ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work, - &xadc->zynq_unmask_work); + ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler, + 0, dev_name(dev), indio_dev); if (ret) return ret; } diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 98acce909854..6bd0ce183635 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -80,10 +80,13 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, dma_dir = DMA_MEM_TO_DEV; if (block->sg_table) { - sgl = block->sg_table->sgl; - nents = sg_nents_for_len(sgl, block->bytes_used); - if (nents < 0) - return nents; + /* + * Only the first sgt->nents entries carry a valid + * sg_dma_address()/sg_dma_len() pair as mapping the table may + * have coalesced entries, in which case nents is smaller than + * orig_nents. + */ + nents = block->sg_table->nents; vecs = kmalloc_array(nents, sizeof(*vecs), GFP_ATOMIC); if (!vecs) @@ -91,7 +94,8 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, len_total = block->bytes_used; - for (i = 0; i < nents; i++) { + sgl = block->sg_table->sgl; + for (i = 0; i < nents && len_total; i++) { vecs[i].addr = sg_dma_address(sgl); vecs[i].len = min(sg_dma_len(sgl), len_total); len_total -= vecs[i].len; @@ -99,6 +103,8 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, sgl = sg_next(sgl); } + nents = i; + desc = dmaengine_prep_peripheral_dma_vec(dmaengine_buffer->chan, vecs, nents, dma_dir, DMA_PREP_INTERRUPT); diff --git a/drivers/iio/cdc/ad7150.c b/drivers/iio/cdc/ad7150.c index 2f35c6d2f9ce..b36ac4e2dae8 100644 --- a/drivers/iio/cdc/ad7150.c +++ b/drivers/iio/cdc/ad7150.c @@ -636,11 +636,13 @@ static const struct i2c_device_id ad7150_id[] = { MODULE_DEVICE_TABLE(i2c, ad7150_id); static const struct of_device_id ad7150_of_match[] = { - { "adi,ad7150" }, - { "adi,ad7151" }, - { "adi,ad7156" }, + { .compatible = "adi,ad7150" }, + { .compatible = "adi,ad7151" }, + { .compatible = "adi,ad7156" }, { } }; +MODULE_DEVICE_TABLE(of, ad7150_of_match); + static struct i2c_driver ad7150_driver = { .driver = { .name = "ad7150", diff --git a/drivers/iio/dac/rohm-bd79703.c b/drivers/iio/dac/rohm-bd79703.c index e91090e4a66d..68e66e7286d8 100644 --- a/drivers/iio/dac/rohm-bd79703.c +++ b/drivers/iio/dac/rohm-bd79703.c @@ -73,6 +73,9 @@ static int bd79703_write_raw(struct iio_dev *idev, { struct bd79703_data *data = iio_priv(idev); + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + if (val < 0 || val >= 1 << BD79703_DAC_BITS) return -EINVAL; diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c index ff6077e29c73..f024680492b8 100644 --- a/drivers/iio/frequency/adf4377.c +++ b/drivers/iio/frequency/adf4377.c @@ -997,10 +997,10 @@ static const struct clk_ops adf4377_clk_ops = { static int adf4377_clk_register(struct adf4377_state *st) { + struct clk_parent_data parent_data = { }; struct spi_device *spi = st->spi; struct device *dev = &spi->dev; - struct clk_init_data init; - struct clk_parent_data parent_data; + struct clk_init_data init = { }; int ret; if (!device_property_present(dev, "#clock-cells")) diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index b823adfb0f70..bf6f195b2253 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -195,7 +195,7 @@ static int admv1013_read_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_CALIBBIAS: - switch (chan->channel) { + switch (chan->channel2) { case IIO_MOD_I: addr = ADMV1013_REG_OFFSET_ADJUST_I; break; @@ -601,11 +601,14 @@ static int admv1013_probe(struct spi_device *spi) "failed to get the LO input clock\n"); st->nb.notifier_call = admv1013_freq_change; - ret = devm_clk_notifier_register(dev, st->clkin, &st->nb); + + ret = devm_mutex_init(dev, &st->lock); if (ret) return ret; - mutex_init(&st->lock); + ret = devm_clk_notifier_register(dev, st->clkin, &st->nb); + if (ret) + return ret; ret = admv1013_init(st, vcm_uv); if (ret) diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index 369c7428e1ef..adcf54b67a28 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -145,7 +145,7 @@ static int adis16136_debugfs_init(struct iio_dev *indio_dev) struct adis16136 *adis16136 = iio_priv(indio_dev); struct dentry *d = iio_get_debugfs_dentry(indio_dev); - debugfs_create_file_unsafe("serial_number", 0400, + debugfs_create_file("serial_number", 0400, d, adis16136, &adis16136_serial_fops); debugfs_create_file_unsafe("product_id", 0400, d, adis16136, &adis16136_product_id_fops); diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 0eeaa378b10d..b7402e2bb081 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -290,9 +290,15 @@ static irqreturn_t max30102_interrupt_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct max30102_data *data = iio_priv(indio_dev); - unsigned int measurements = bitmap_weight(indio_dev->active_scan_mask, - iio_get_masklength(indio_dev)); - int ret, cnt = 0; + unsigned int measurements; + int ret, cnt; + + cnt = max30102_fifo_count(data); + if (cnt <= 0) + return IRQ_HANDLED; + + measurements = bitmap_weight(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); mutex_lock(&data->lock); diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 36323ad149e0..7e85c1b8dc16 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -280,7 +280,7 @@ static void adis16400_debugfs_init(struct iio_dev *indio_dev) return; if (st->variant->flags & ADIS16400_HAS_SERIAL_NUMBER) - debugfs_create_file_unsafe("serial_number", 0400, + debugfs_create_file("serial_number", 0400, d, st, &adis16400_serial_number_fops); if (st->variant->flags & ADIS16400_HAS_PROD_ID) debugfs_create_file_unsafe("product_id", 0400, diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index e009f3824768..254b7ff25b66 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -309,9 +309,9 @@ static void adis16480_debugfs_init(struct iio_dev *indio_dev) if (!IS_ENABLED(CONFIG_DEBUG_FS)) return; - debugfs_create_file_unsafe("firmware_revision", 0400, + debugfs_create_file("firmware_revision", 0400, d, adis16480, &adis16480_firmware_revision_fops); - debugfs_create_file_unsafe("firmware_date", 0400, + debugfs_create_file("firmware_date", 0400, d, adis16480, &adis16480_firmware_date_fops); debugfs_create_file_unsafe("serial_number", 0400, d, adis16480, &adis16480_serial_number_fops); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 2c9ec93dff47..4294101810fd 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1377,6 +1377,10 @@ EXPORT_SYMBOL_GPL(iio_update_buffers); void iio_disable_all_buffers(struct iio_dev *indio_dev) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + + guard(mutex)(&iio_dev_opaque->mlock); + iio_disable_buffers(indio_dev); iio_buffer_deactivate_all(indio_dev); } @@ -2462,7 +2466,8 @@ int iio_push_to_buffers_with_ts_unaligned(struct iio_dev *indio_dev, bb = devm_krealloc(&indio_dev->dev, iio_dev_opaque->bounce_buffer, - indio_dev->scan_bytes, GFP_KERNEL); + indio_dev->scan_bytes, + GFP_KERNEL | __GFP_ZERO); if (!bb) return -ENOMEM; iio_dev_opaque->bounce_buffer = bb; diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 17781c12bc85..76926fee0f84 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -509,6 +509,8 @@ static void iio_trig_release(struct device *device) struct iio_trigger *trig = to_iio_trigger(device); int i; + cancel_work_sync(&trig->reenable_work); + if (trig->subirq_base) { for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) { irq_modify_status(trig->subirq_base + i, diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 63591b7ecb89..b435dc5e8706 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -1497,6 +1497,7 @@ static int gp2ap020a00f_probe(struct i2c_client *client) iio_trigger_unregister(data->trig); error_free_irq: free_irq(client->irq, indio_dev); + irq_work_sync(&data->work); error_uninit_buffer: iio_triggered_buffer_cleanup(indio_dev); error_regulator_disable: @@ -1519,6 +1520,7 @@ static void gp2ap020a00f_remove(struct i2c_client *client) iio_device_unregister(indio_dev); iio_trigger_unregister(data->trig); free_irq(client->irq, indio_dev); + irq_work_sync(&data->work); iio_triggered_buffer_cleanup(indio_dev); regulator_disable(data->vled_reg); } diff --git a/drivers/iio/light/rohm-bu27034.c b/drivers/iio/light/rohm-bu27034.c index 28d111ac8c0a..92fca33387bb 100644 --- a/drivers/iio/light/rohm-bu27034.c +++ b/drivers/iio/light/rohm-bu27034.c @@ -137,6 +137,7 @@ static const struct iio_gain_sel_pair bu27034_gains[] = { #define BU27034_MEAS_MODE_200MS 2 #define BU27034_MEAS_MODE_400MS 4 +#define BU27034_INT_TIME_US_MIN (55 * USEC_PER_MSEC) static const struct iio_itime_sel_mul bu27034_itimes[] = { GAIN_SCALE_ITIME_US(400000, BU27034_MEAS_MODE_400MS, 8), GAIN_SCALE_ITIME_US(200000, BU27034_MEAS_MODE_200MS, 4), @@ -296,7 +297,7 @@ static int bu27034_get_gain(struct bu27034_data *data, int chan, int *gain) return 0; } -static int bu27034_get_int_time(struct bu27034_data *data) +static int bu27034_get_int_time(struct bu27034_data *data, int *itime) { int ret, sel; @@ -304,24 +305,30 @@ static int bu27034_get_int_time(struct bu27034_data *data) if (ret) return ret; - return iio_gts_find_int_time_by_sel(&data->gts, - sel & BU27034_MASK_MEAS_MODE); + ret = iio_gts_find_int_time_by_sel(&data->gts, + sel & BU27034_MASK_MEAS_MODE); + if (ret < 0) + return ret; + + *itime = ret; + + return 0; } static int _bu27034_get_scale(struct bu27034_data *data, int channel, int *val, int *val2) { - int gain, ret; + int gain, itime, ret; ret = bu27034_get_gain(data, channel, &gain); if (ret) return ret; - ret = bu27034_get_int_time(data); - if (ret < 0) + ret = bu27034_get_int_time(data, &itime); + if (ret) return ret; - return iio_gts_get_scale(&data->gts, gain, ret, val, val2); + return iio_gts_get_scale(&data->gts, gain, itime, val, val2); } static int bu27034_get_scale(struct bu27034_data *data, int channel, int *val, @@ -397,12 +404,10 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) int ret, int_time_old, i; guard(mutex)(&data->mutex); - ret = bu27034_get_int_time(data); - if (ret < 0) + ret = bu27034_get_int_time(data, &int_time_old); + if (ret) return ret; - int_time_old = ret; - if (!iio_gts_valid_time(&data->gts, time_us)) { dev_err(data->dev, "Unsupported integration time %u\n", time_us); @@ -415,7 +420,7 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) for (i = 0; i < numg; i++) { ret = bu27034_get_gain(data, gains[i].chan, &gains[i].old_gain); if (ret) - return 0; + return ret; ret = iio_gts_find_new_gain_by_old_gain_time(&data->gts, gains[i].old_gain, @@ -841,7 +846,7 @@ static int bu27034_meas_set(struct bu27034_data *data, bool en) static int bu27034_get_single_result(struct bu27034_data *data, int chan, int *val) { - int ret; + int ret, itime; if (chan < BU27034_CHAN_DATA0 || chan > BU27034_CHAN_DATA1) return -EINVAL; @@ -850,11 +855,11 @@ static int bu27034_get_single_result(struct bu27034_data *data, int chan, if (ret) return ret; - ret = bu27034_get_int_time(data); - if (ret < 0) + ret = bu27034_get_int_time(data, &itime); + if (ret) return ret; - msleep(ret / 1000); + msleep(itime / 1000); return bu27034_read_result(data, chan, val); } @@ -904,12 +909,10 @@ static int bu27034_calc_mlux(struct bu27034_data *data, __le16 *res, int *val) if (ret) return ret; - ret = bu27034_get_int_time(data); - if (ret < 0) + ret = bu27034_get_int_time(data, &meastime); + if (ret) return ret; - meastime = ret; - d1_d0_ratio_scaled = (unsigned int)ch1 * (unsigned int)gain0 * 100; helper64 = (u64)ch1 * (u64)gain0 * 100LLU; @@ -970,9 +973,9 @@ static int bu27034_read_raw(struct iio_dev *idev, switch (mask) { case IIO_CHAN_INFO_INT_TIME: *val = 0; - *val2 = bu27034_get_int_time(data); - if (*val2 < 0) - return *val2; + ret = bu27034_get_int_time(data, val2); + if (ret) + return ret; return IIO_VAL_INT_PLUS_MICRO; @@ -1157,11 +1160,20 @@ static int bu27034_buffer_thread(void *arg) { struct iio_dev *idev = arg; struct bu27034_data *data; - int wait_ms; + int wait_ms, ret; data = iio_priv(idev); - wait_ms = bu27034_get_int_time(data); + /* + * If reading the integration time fails, default to the minimum so we + * don't lose samples. This may waste CPU cycles, but as a hardening + * against theoretical, once-in-a-blue-moon error, this should be Ok. + */ + wait_ms = BU27034_INT_TIME_US_MIN; + ret = bu27034_get_int_time(data, &wait_ms); + if (ret) + dev_warn(data->dev, "Failed to get integration time\n"); + wait_ms /= 1000; wait_ms -= BU27034_MEAS_WAIT_PREMATURE_MS; diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 990340a9b10c..dbe42233c81d 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -836,7 +836,7 @@ static int bmp280_write_sampling_frequency(struct bmp280_data *data, int val, int val2) { const int (*avail)[2] = data->chip_info->sampling_freq_avail; - const int n = data->chip_info->num_sampling_freq_avail; + const int n = data->chip_info->num_sampling_freq_avail / 2; int ret, prev; int i; diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index b3be9de03678..d00d7ed54cb1 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -289,7 +289,7 @@ static int bm1390_read_data(struct bm1390_data *data, if (warn) dev_warn(data->dev, "Failed to stop measurement (%d)\n", warn); - return 0; + return ret; } static int bm1390_read_raw(struct iio_dev *idev, diff --git a/drivers/iio/proximity/aw96103.c b/drivers/iio/proximity/aw96103.c index 8fbb755dcae0..8352d51e5128 100644 --- a/drivers/iio/proximity/aw96103.c +++ b/drivers/iio/proximity/aw96103.c @@ -24,6 +24,7 @@ #define AW96103_BIN_VALID_DATA_OFFSET 64 #define AW96103_BIN_DATA_LEN_OFFSET 16 #define AW96103_BIN_DATA_REG_NUM_SIZE 4 +#define AW96103_BIN_REG_SIZE 6 #define AW96103_BIN_CHIP_TYPE_SIZE 8 #define AW96103_BIN_CHIP_TYPE_OFFSET 24 @@ -229,14 +230,27 @@ static const struct aw_chip_info aw_chip_info_tbl[] = { }, }; -static void aw96103_parsing_bin_file(struct aw_bin *bin) +static int aw96103_parsing_bin_file(struct aw_bin *bin) { + u32 data_len; + + if (bin->len < AW96103_BIN_VALID_DATA_OFFSET) + return -EINVAL; + + data_len = get_unaligned_le32(bin->data + AW96103_BIN_DATA_LEN_OFFSET); + if (data_len < AW96103_BIN_DATA_REG_NUM_SIZE) + return -EINVAL; + + bin->valid_data_len = data_len - AW96103_BIN_DATA_REG_NUM_SIZE; + if (bin->valid_data_len > bin->len - AW96103_BIN_VALID_DATA_OFFSET || + bin->valid_data_len % AW96103_BIN_REG_SIZE) + return -EINVAL; + bin->valid_data_addr = AW96103_BIN_VALID_DATA_OFFSET; - bin->valid_data_len = - *(unsigned int *)(bin->data + AW96103_BIN_DATA_LEN_OFFSET) - - AW96103_BIN_DATA_REG_NUM_SIZE; memcpy(bin->chip_type, bin->data + AW96103_BIN_CHIP_TYPE_OFFSET, AW96103_BIN_CHIP_TYPE_SIZE); + + return 0; } static const struct regmap_config aw96103_regmap_confg = { @@ -500,7 +514,7 @@ static int aw96103_bin_valid_loaded(struct aw96103 *aw96103, int ret; for (i = 0; i < aw_bin_data_s->valid_data_len; - i += 6, start_addr += 6) { + i += AW96103_BIN_REG_SIZE, start_addr += AW96103_BIN_REG_SIZE) { reg_addr = get_unaligned_le16(aw_bin_data_s->data + start_addr); reg_data = get_unaligned_le32(aw_bin_data_s->data + start_addr + 2); @@ -550,6 +564,8 @@ static int aw96103_para_loaded(struct aw96103 *aw96103) static int aw96103_cfg_all_loaded(const struct firmware *cont, struct aw96103 *aw96103) { + int ret; + if (!cont) return -EINVAL; @@ -561,7 +577,9 @@ static int aw96103_cfg_all_loaded(const struct firmware *cont, aw_bin->len = cont->size; memcpy(aw_bin->data, cont->data, cont->size); release_firmware(cont); - aw96103_parsing_bin_file(aw_bin); + ret = aw96103_parsing_bin_file(aw_bin); + if (ret) + return ret; return aw96103_bin_valid_loaded(aw96103, aw_bin); } diff --git a/drivers/iio/proximity/isl29501.c b/drivers/iio/proximity/isl29501.c index 95fb7238f678..a98a9975323c 100644 --- a/drivers/iio/proximity/isl29501.c +++ b/drivers/iio/proximity/isl29501.c @@ -226,7 +226,7 @@ static int isl29501_register_read(struct isl29501_private *isl29501, return ret; } -static u32 isl29501_register_write(struct isl29501_private *isl29501, +static int isl29501_register_write(struct isl29501_private *isl29501, enum isl29501_register_name name, u32 value) { diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index 400477b4c740..8a9ee21f2bf6 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -294,12 +294,14 @@ static int lidar_probe(struct i2c_client *client) ret = pm_runtime_set_active(&client->dev); if (ret) - goto error_unreg_buffer; + goto error_unreg_dev; pm_runtime_enable(&client->dev); pm_runtime_idle(&client->dev); return 0; +error_unreg_dev: + iio_device_unregister(indio_dev); error_unreg_buffer: iio_triggered_buffer_cleanup(indio_dev); diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index 13b4ef2896e3..255a36976ac4 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -222,7 +222,7 @@ static const struct iio_chan_spec_ext_info sx9324_channel_ext_info[] = { .scan_index = idx, \ .scan_type = { \ .sign = 's', \ - .realbits = 12, \ + .realbits = 16, \ .storagebits = 16, \ .endianness = IIO_BE, \ }, \ diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c index 7f417372566a..d0b8f429999f 100644 --- a/drivers/iio/proximity/vcnl3020.c +++ b/drivers/iio/proximity/vcnl3020.c @@ -584,16 +584,26 @@ static irqreturn_t vcnl3020_handle_irq_thread(int irq, void *p) return IRQ_HANDLED; } - if (!(isr & VCNL_ICR_THRES_EN)) + if (!(isr & (VCNL_INT_TH_HI | VCNL_INT_TH_LOW))) return IRQ_NONE; - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - iio_get_time_ns(indio_dev)); + if (isr & VCNL_INT_TH_HI) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } + + if (isr & VCNL_INT_TH_LOW) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + } - rc = regmap_write(data->regmap, VCNL_ISR, isr & VCNL_ICR_THRES_EN); + rc = regmap_write(data->regmap, VCNL_ISR, isr & (VCNL_INT_TH_HI | VCNL_INT_TH_LOW)); if (rc) dev_err(data->dev, "Error (%d) writing in reg (0x%x)\n", rc, VCNL_ISR); diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index b7127c3c52d2..b38374ba6894 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -221,7 +221,10 @@ static int vl53l0x_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = vl53l0x_read_proximity(data, chan, val); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 5041443e0efe..b441585bc778 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1946,6 +1946,14 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { }, .callback = atkbd_deactivate_fixup, }, + { + /* Lenovo IdeaPad Slim 3 15IWC11 (83RR) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83RR"), + }, + .callback = atkbd_deactivate_fixup, + }, { /* Xiaomi Book Pro 14 (TM2424) */ .matches = { diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 007f90759d06..3d05cee4a58a 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -191,6 +191,7 @@ static const char * const smbus_pnp_ids[] = { "LEN2054", /* E480 */ "LEN2055", /* E580 */ "LEN2058", /* E490 */ + "LEN205b", /* T490 */ "LEN2068", /* T14 Gen 1 */ "SYN1221", /* TUXEDO InfinityBook Pro 14 v5 */ "SYN3003", /* HP EliteBook 850 G1 */ @@ -1838,8 +1839,10 @@ static int synaptics_setup_intertouch(struct psmouse *psmouse, return -ENXIO; } - /* Disable intertouch on known-broken board revisions */ - if (info->board_id == 2722) { + /* Disable intertouch on known-broken T440p board revisions */ + if (info->board_id == 2722 && + psmouse_matches_pnp_id(psmouse, + (const char * const []){"LEN0036", NULL})) { psmouse_info(psmouse, "Disabling intertouch for board id %u\n", info->board_id); diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h index 67da08af75cb..bbc0a0a6cffb 100644 --- a/drivers/input/serio/i8042-acpipnpio.h +++ b/drivers/input/serio/i8042-acpipnpio.h @@ -670,6 +670,15 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { }, .driver_data = (void *)(SERIO_QUIRK_NOAUX) }, + { + /* Fujitsu LIFEBOOK U7410 */ + /* Mux mode also disturbs the EC keyboard, causing spurious ACKs */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU CLIENT COMPUTING LIMITED"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U7410"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) + }, { /* Gigabyte M912 */ .matches = { diff --git a/drivers/input/touchscreen/s6sy761.c b/drivers/input/touchscreen/s6sy761.c index 0f24a9b73063..899c179fb2d3 100644 --- a/drivers/input/touchscreen/s6sy761.c +++ b/drivers/input/touchscreen/s6sy761.c @@ -500,10 +500,22 @@ static int s6sy761_suspend(struct device *dev) static int s6sy761_resume(struct device *dev) { struct s6sy761_data *sdata = dev_get_drvdata(dev); + int err; + + err = s6sy761_power_on(sdata); + if (err) + return err; enable_irq(sdata->client->irq); - return s6sy761_power_on(sdata); + guard(mutex)(&sdata->input->mutex); + + if (!input_device_enabled(sdata->input)) + return 0; + + err = i2c_smbus_write_byte(sdata->client, S6SY761_SENSE_ON); + + return err; } static const struct dev_pm_ops s6sy761_pm_ops = { diff --git a/drivers/md/dm-pcache/cache_gc.c b/drivers/md/dm-pcache/cache_gc.c index a167892be7ce..d551848912b4 100644 --- a/drivers/md/dm-pcache/cache_gc.c +++ b/drivers/md/dm-pcache/cache_gc.c @@ -146,6 +146,11 @@ void pcache_cache_gc_fn(struct work_struct *work) continue; } + if (get_kset_onmedia_size(kset_onmedia) > cache_seg_remain(&key_tail)) { + atomic_inc(&cache->gc_errors); + return; + } + for (i = 0; i < kset_onmedia->key_num; i++) { struct pcache_cache_key key_tmp = { 0 }; diff --git a/drivers/md/dm-pcache/cache_key.c b/drivers/md/dm-pcache/cache_key.c index a0d109f85504..40afba5f3723 100644 --- a/drivers/md/dm-pcache/cache_key.c +++ b/drivers/md/dm-pcache/cache_key.c @@ -90,10 +90,19 @@ int cache_key_decode(struct pcache_cache *cache, struct pcache_cache_key *key) { struct dm_pcache *pcache = CACHE_TO_PCACHE(cache); + u64 dev_bytes = (u64)cache->dev_size << SECTOR_SHIFT; key->off = key_onmedia->off; key->len = key_onmedia->len; + if (key_onmedia->len == 0 || + key_onmedia->len > dev_bytes || + key_onmedia->off > dev_bytes - key_onmedia->len) { + pcache_dev_err(pcache, "key off %llu + len %u exceeds device size\n", + key_onmedia->off, key_onmedia->len); + return -EIO; + } + if (!cache_seg_id_valid(cache, key_onmedia->cache_seg_id)) { pcache_dev_err(pcache, "invalid cache_seg_id %u in cache key (n_segs %u)\n", key_onmedia->cache_seg_id, cache->n_segs); @@ -822,6 +831,11 @@ int cache_replay(struct pcache_cache *cache) } /* Replay the kset and check for errors. */ + if (get_kset_onmedia_size(kset_onmedia) > cache_seg_remain(pos)) { + ret = -EIO; + goto out; + } + ret = kset_replay(cache, kset_onmedia); if (ret) goto out; diff --git a/drivers/md/dm-pcache/cache_writeback.c b/drivers/md/dm-pcache/cache_writeback.c index c34db79fcefb..c8a4c8110a58 100644 --- a/drivers/md/dm-pcache/cache_writeback.c +++ b/drivers/md/dm-pcache/cache_writeback.c @@ -261,6 +261,11 @@ void cache_writeback_fn(struct work_struct *work) goto queue_work; } + if (get_kset_onmedia_size(kset_onmedia) > cache_seg_remain(&dirty_tail)) { + atomic_inc(&cache->writeback_errors); + goto unlock; + } + ret = cache_kset_insert_tree(cache, kset_onmedia); if (ret) { atomic_inc(&cache->writeback_errors); diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 03e80b2c4f5a..349fa07be314 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -293,7 +293,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size, } size = bdev_nr_bytes(bdev); - if ((long)size % erase_size) { + if (!erase_size || (long)size % erase_size) { pr_err("erasesize must be a divisor of device size\n"); goto err_free_block2mtd; } diff --git a/drivers/mtd/devices/mtd_intel_dg.c b/drivers/mtd/devices/mtd_intel_dg.c index a566e86eb5e3..55a945c1a67a 100644 --- a/drivers/mtd/devices/mtd_intel_dg.c +++ b/drivers/mtd/devices/mtd_intel_dg.c @@ -387,7 +387,7 @@ idg_erase(struct intel_dg_nvm *nvm, u8 region, loff_t from, u64 len, u64 *fail_a void __iomem *base2 = nvm->base2; void __iomem *base = nvm->base; const u32 block = 0x10; - u32 iter = 0; + u32 iter; u32 reg; u64 i; @@ -396,6 +396,7 @@ idg_erase(struct intel_dg_nvm *nvm, u8 region, loff_t from, u64 len, u64 *fail_a iowrite32(region << 24 | block, base + NVM_ERASE_REG); if (nvm->non_posted_erase) { /* Wait for Erase Done */ + iter = 0; reg = ioread32(base2 + NVM_DEBUG_REG); while (!(reg & NVM_NON_POSTED_ERASE_DONE) && ++iter < NVM_NON_POSTED_ERASE_DONE_ITER) { diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 16629382a787..491a27d0315f 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1083,6 +1083,7 @@ static int mtd_otp_nvmem_add(struct mtd_info *mtd) err: nvmem_unregister(mtd->otp_user_nvmem); + mtd->otp_user_nvmem = NULL; /* Don't report error if OTP is not supported. */ if (err == -EOPNOTSUPP) return 0; @@ -1332,7 +1333,7 @@ int __get_mtd_device(struct mtd_info *mtd) int err; if (master->_get_device) { - err = master->_get_device(mtd); + err = master->_get_device(master); if (err) return err; } diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c index 6ccdff3fc913..1d809cbd47e6 100644 --- a/drivers/mtd/nand/ecc.c +++ b/drivers/mtd/nand/ecc.c @@ -159,6 +159,13 @@ int nand_ecc_finish_io_req(struct nand_device *nand, } EXPORT_SYMBOL(nand_ecc_finish_io_req); +bool nand_ecc_is_pipelined(const struct nand_device *nand) +{ + return nand->ecc.engine && + nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED; +} +EXPORT_SYMBOL(nand_ecc_is_pipelined); + /* Define default OOB placement schemes for large and small page devices */ static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index d53b35a8b3cb..2085632631c0 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -3143,6 +3143,9 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl) goto free_buf_desc; } + spin_lock_init(&cdns_ctrl->irq_lock); + init_completion(&cdns_ctrl->complete); + if (devm_request_irq(cdns_ctrl->dev, cdns_ctrl->irq, cadence_nand_isr, IRQF_SHARED, "cadence-nand-controller", cdns_ctrl)) { @@ -3151,9 +3154,6 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl) goto free_buf; } - spin_lock_init(&cdns_ctrl->irq_lock); - init_completion(&cdns_ctrl->complete); - ret = cadence_nand_hw_init(cdns_ctrl); if (ret) goto disable_irq; diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c index 9940681810cf..1b9b370adfab 100644 --- a/drivers/mtd/nand/raw/vf610_nfc.c +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -505,6 +505,11 @@ static int vf610_nfc_exec_op(struct nand_chip *chip, check_only); } +static unsigned int vf610_nfc_spare_size(struct mtd_info *mtd) +{ + return min_t(unsigned int, mtd->oobsize, 64); +} + static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat, uint8_t *oob, int page) { @@ -514,6 +519,7 @@ static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat, u8 ecc_status; u8 ecc_count; int flips_threshold = nfc->chip.ecc.strength / 2; + int ret; ecc_status = vf610_nfc_read(nfc, ecc_status_off) & 0xff; ecc_count = ecc_status & ECC_STATUS_ERR_COUNT; @@ -521,16 +527,22 @@ static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat, if (!(ecc_status & ECC_STATUS_MASK)) return ecc_count; + /* The failed decode leaves a bogus correction in SRAM; re-read without ECC */ nfc->data_access = true; - nand_read_oob_op(&nfc->chip, page, 0, oob, mtd->oobsize); + ret = nand_read_page_op(&nfc->chip, page, 0, dat, mtd->writesize); + if (!ret) + ret = nand_read_oob_op(&nfc->chip, page, 0, oob, + vf610_nfc_spare_size(mtd)); nfc->data_access = false; + if (ret) + return ret; /* - * On an erased page, bit count (including OOB) should be zero or - * at least less then half of the ECC strength. + * Run the erased-page check with the driver's historic threshold + * of half the ECC strength. */ - return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob, - mtd->oobsize, NULL, 0, + return nand_check_erased_ecc_chunk(dat, mtd->writesize, oob, + vf610_nfc_spare_size(mtd), NULL, 0, flips_threshold); } @@ -551,7 +563,7 @@ static int vf610_nfc_read_page(struct nand_chip *chip, uint8_t *buf, { struct vf610_nfc *nfc = chip_to_nfc(chip); struct mtd_info *mtd = nand_to_mtd(chip); - int trfr_sz = mtd->writesize + mtd->oobsize; + int trfr_sz = mtd->writesize + vf610_nfc_spare_size(mtd); u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0; int stat; @@ -577,11 +589,16 @@ static int vf610_nfc_read_page(struct nand_chip *chip, uint8_t *buf, */ vf610_nfc_rd_from_sram(buf, nfc->regs + NFC_MAIN_AREA(0), mtd->writesize, false); - if (oob_required) + if (oob_required) { + unsigned int spare = vf610_nfc_spare_size(mtd); + vf610_nfc_rd_from_sram(chip->oob_poi, nfc->regs + NFC_MAIN_AREA(0) + mtd->writesize, - mtd->oobsize, false); + spare, false); + /* Not transferred, and never written: reads back erased */ + memset(chip->oob_poi + spare, 0xff, mtd->oobsize - spare); + } stat = vf610_nfc_correct_data(chip, buf, chip->oob_poi, page); @@ -599,7 +616,7 @@ static int vf610_nfc_write_page(struct nand_chip *chip, const uint8_t *buf, { struct vf610_nfc *nfc = chip_to_nfc(chip); struct mtd_info *mtd = nand_to_mtd(chip); - int trfr_sz = mtd->writesize + mtd->oobsize; + int trfr_sz = mtd->writesize + vf610_nfc_spare_size(mtd); u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0; u8 status; int ret; @@ -740,6 +757,49 @@ static void vf610_nfc_init_controller(struct vf610_nfc *nfc) } } +/* + * With 64 byte OOB chips the core's large page layout matches what + * U-Boot uses, and on chips with more, U-Boot and older kernels clamped + * mtd->oobsize to 64. Modifying the OOB size is no longer possible, the + * actual chip geometry must be respected, so to avoid breaking those + * existing setups use our own layout: the core's large page one, + * computed over the first 64 spare bytes only. + */ +static int vf610_nfc_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int total_ecc_bytes = nand->ecc.ctx.total; + + if (section || !total_ecc_bytes) + return -ERANGE; + + oobregion->length = total_ecc_bytes; + oobregion->offset = vf610_nfc_spare_size(mtd) - oobregion->length; + + return 0; +} + +static int vf610_nfc_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int total_ecc_bytes = nand->ecc.ctx.total; + + if (section) + return -ERANGE; + + oobregion->length = vf610_nfc_spare_size(mtd) - total_ecc_bytes - 2; + oobregion->offset = 2; + + return 0; +} + +static const struct mtd_ooblayout_ops vf610_nfc_ooblayout_ops = { + .ecc = vf610_nfc_ooblayout_ecc, + .free = vf610_nfc_ooblayout_free, +}; + static int vf610_nfc_attach_chip(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); @@ -770,12 +830,7 @@ static int vf610_nfc_attach_chip(struct nand_chip *chip) return -ENXIO; } - /* Only 64 byte ECC layouts known */ - if (mtd->oobsize > 64) - mtd->oobsize = 64; - - /* Use default large page ECC layout defined in NAND core */ - mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); + mtd_set_ooblayout(mtd, &vf610_nfc_ooblayout_ops); if (chip->ecc.strength == 32) { nfc->ecc_mode = ECC_60_BYTE; chip->ecc.bytes = 60; diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 74bb5ee83b31..abfdd4b03360 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -281,8 +281,26 @@ static int spinand_init_cfg_cache(struct spinand_device *spinand) static int spinand_init_quad_enable(struct spinand_device *spinand, bool enable) { - return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE, - enable ? CFG_QUAD_ENABLE : 0); + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int target; + int ret; + + /* + * QE is a per-die setting on some devices. Program each target + * individually when enabling or disabling quad I/O mode. + */ + for (target = 0; target < nand->memorg.ntargets; target++) { + ret = spinand_select_target(spinand, target); + if (ret) + return ret; + + ret = spinand_upd_cfg(spinand, CFG_QUAD_ENABLE, + enable ? CFG_QUAD_ENABLE : 0); + if (ret) + return ret; + } + + return 0; } static int spinand_ecc_enable(struct spinand_device *spinand, @@ -508,8 +526,7 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, else rdesc->info.op_tmpl = &rdesc->info.primary_op_tmpl; - if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED && - req->mode != MTD_OPS_RAW) + if (nand_ecc_is_pipelined(nand) && req->mode != MTD_OPS_RAW) rdesc->info.op_tmpl->data.ecc = true; else rdesc->info.op_tmpl->data.ecc = false; @@ -603,8 +620,7 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand, wdesc = spinand->dirmaps[req->pos.plane].wdesc; - if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED && - req->mode != MTD_OPS_RAW) + if (nand_ecc_is_pipelined(nand) && req->mode != MTD_OPS_RAW) wdesc->info.op_tmpl->data.ecc = true; else wdesc->info.op_tmpl->data.ecc = false; @@ -1261,7 +1277,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand, struct spi_mem_dirmap_desc *desc; bool enable_ecc = false, secondary_op = false; - if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED) + if (nand_ecc_is_pipelined(nand)) enable_ecc = true; if (spinand->cont_read_possible && spinand->op_templates->cont_read_cache) @@ -1796,11 +1812,11 @@ static int spinand_configure_chip(struct spinand_device *spinand) spinand->ssdr_op_templates.write_cache->data.buswidth == 4 || spinand->ssdr_op_templates.update_cache->data.buswidth == 4) quad_enable = true; - } - ret = spinand_init_quad_enable(spinand, quad_enable); - if (ret) - return ret; + ret = spinand_init_quad_enable(spinand, quad_enable); + if (ret) + return ret; + } if (spinand->configure_chip) { ret = spinand->configure_chip(spinand, SSDR); @@ -1808,7 +1824,7 @@ static int spinand_configure_chip(struct spinand_device *spinand) return ret; } - return ret; + return 0; } static int spinand_init_flash(struct spinand_device *spinand) @@ -1979,12 +1995,17 @@ static int spinand_init(struct spinand_device *spinand) goto err_cleanup_ecc_engine; } - if (nand->ecc.engine) { - ret = mtd_ooblayout_count_freebytes(mtd); - if (ret < 0) - goto err_cleanup_ecc_engine; + if (!nand->ecc.engine) { + if (spinand->eccinfo.ooblayout) + mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout); + else + mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout); } + ret = mtd_ooblayout_count_freebytes(mtd); + if (ret < 0) + goto err_cleanup_ecc_engine; + mtd->oobavail = ret; /* Propagate ECC information to mtd_info */ diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index ccf4396cdcd0..8bc117b46e02 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1310,7 +1310,7 @@ static bool spi_nor_rww_start_exclusive(struct spi_nor *nor) { struct spi_nor_rww *rww = &nor->rww; - mutex_lock(&nor->lock); + guard(mutex)(&nor->lock); if (rww->ongoing_io || rww->ongoing_rd || rww->ongoing_pe) return false; diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 4d06c6ba6641..78909f516823 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -391,8 +391,10 @@ static void bcm_sysport_update_mib_counters(struct bcm_sysport_priv *priv) if (priv->is_lite) continue; - if (s->type != BCM_SYSPORT_STAT_MIB_RX) + if (s->type == BCM_SYSPORT_STAT_MIB_TX) offset = UMAC_MIB_STAT_OFFSET; + else if (s->type == BCM_SYSPORT_STAT_RUNT) + offset = 2 * UMAC_MIB_STAT_OFFSET; val = umac_readl(priv, UMAC_MIB_START + j + offset); break; case BCM_SYSPORT_STAT_RXCHK: @@ -482,10 +484,10 @@ static void bcm_sysport_get_stats(struct net_device *dev, s->type == BCM_SYSPORT_STAT_NETDEV64) { do { start = u64_stats_fetch_begin(syncp); - data[i] = *(u64 *)p; + data[j] = *(u64 *)p; } while (u64_stats_fetch_retry(syncp, start)); } else - data[i] = *(u32 *)p; + data[j] = *(u32 *)p; j++; } diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 21668e41b696..e8908916558b 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -57,6 +57,9 @@ */ #define GENET_RSB_PAD (sizeof(struct status_64) + 2) +/* RX buffer plus the skb_shared_info napi_build_skb() places behind it */ +#define GENET_RX_BUF_SIZE SKB_HEAD_ALIGN(RX_BUF_LENGTH) + /* Tx/Rx DMA register offset, skip 256 descriptors */ #define WORDS_PER_BD(p) (p->hw_params->words_per_bd) #define DMA_DESC_SIZE (WORDS_PER_BD(priv) * sizeof(u32)) @@ -2254,11 +2257,12 @@ static int bcmgenet_rx_refill(struct bcmgenet_rx_ring *ring, struct enet_cb *cb) { struct bcmgenet_priv *priv = ring->priv; + unsigned int size = GENET_RX_BUF_SIZE; + unsigned int offset; dma_addr_t mapping; struct page *page; - page = page_pool_alloc_pages(ring->page_pool, - GFP_ATOMIC); + page = page_pool_dev_alloc(ring->page_pool, &offset, &size); if (!page) { priv->mib.alloc_rx_buff_failed++; netif_err(priv, rx_err, priv->dev, @@ -2267,9 +2271,13 @@ static int bcmgenet_rx_refill(struct bcmgenet_rx_ring *ring, } /* page_pool handles DMA mapping via PP_FLAG_DMA_MAP */ - mapping = page_pool_get_dma_addr(page); + mapping = page_pool_get_dma_addr(page) + offset; + dma_sync_single_for_device(&priv->pdev->dev, mapping, RX_BUF_LENGTH, + DMA_FROM_DEVICE); cb->rx_page = page; + cb->rx_offset = offset; + cb->rx_size = size; dmadesc_set_addr(priv, cb->bd_addr, mapping); return 0; @@ -2323,6 +2331,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, while ((rxpktprocessed < rxpkttoprocess) && (rxpktprocessed < budget)) { + unsigned int rx_offset, rx_size; struct status_64 *status; struct page *rx_page; void *hard_start; @@ -2332,6 +2341,8 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, /* Save the received page before refilling */ rx_page = cb->rx_page; + rx_offset = cb->rx_offset; + rx_size = cb->rx_size; if (bcmgenet_rx_refill(ring, cb)) { BCMGENET_STATS64_INC(stats, dropped); @@ -2341,10 +2352,10 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, /* Sync the full buffer; the HW may have written anywhere * up to RX_BUF_LENGTH. */ - page_pool_dma_sync_for_cpu(ring->page_pool, rx_page, 0, + page_pool_dma_sync_for_cpu(ring->page_pool, rx_page, rx_offset, RX_BUF_LENGTH); - hard_start = page_address(rx_page); + hard_start = page_address(rx_page) + rx_offset; status = (struct status_64 *)hard_start; dma_length_status = status->length_status; @@ -2410,7 +2421,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, /* Build SKB from the page - data starts at hard_start, * frame begins after RSB(64) + pad(2) = 66 bytes. */ - skb = napi_build_skb(hard_start, PAGE_SIZE); + skb = napi_build_skb(hard_start, rx_size); if (unlikely(!skb)) { BCMGENET_STATS64_INC(stats, dropped); page_pool_put_full_page(ring->page_pool, rx_page, @@ -2762,14 +2773,16 @@ static void bcmgenet_init_tx_ring(struct bcmgenet_priv *priv, static int bcmgenet_rx_ring_create_pool(struct bcmgenet_priv *priv, struct bcmgenet_rx_ring *ring) { + /* Buffers share a page. bcmgenet_rx_refill() syncs each one for the + * device, PP_FLAG_DMA_SYNC_DEV would sync the whole page. + */ struct page_pool_params pp_params = { .order = 0, - .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, + .flags = PP_FLAG_DMA_MAP, .pool_size = ring->size, .nid = NUMA_NO_NODE, .dev = &priv->pdev->dev, .dma_dir = DMA_FROM_DEVICE, - .max_len = RX_BUF_LENGTH, }; int err; @@ -4138,8 +4151,11 @@ static int bcmgenet_probe(struct platform_device *pdev) netif_set_real_num_rx_queues(priv->dev, priv->hw_params->rx_queues + 1); /* Set default coalescing parameters */ - for (i = 0; i <= priv->hw_params->rx_queues; i++) + for (i = 0; i <= priv->hw_params->rx_queues; i++) { + priv->rx_rings[i].priv = priv; + priv->rx_rings[i].index = i; priv->rx_rings[i].rx_max_coalesced_frames = 1; + } /* Initialize u64 stats seq counter for 32bit machines */ for (i = 0; i <= GENET_MAX_MQ_CNT; i++) { diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 22a958ba9902..86f2aed20dbe 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -470,6 +470,8 @@ struct bcmgenet_rx_stats64 { struct enet_cb { struct sk_buff *skb; struct page *rx_page; + unsigned int rx_offset; + unsigned int rx_size; void __iomem *bd_addr; DEFINE_DMA_UNMAP_ADDR(dma_addr); DEFINE_DMA_UNMAP_LEN(dma_len); diff --git a/drivers/net/ethernet/google/gve/gve_tx_dqo.c b/drivers/net/ethernet/google/gve/gve_tx_dqo.c index 616c1921aebe..ad99cbb77e29 100644 --- a/drivers/net/ethernet/google/gve/gve_tx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_tx_dqo.c @@ -595,25 +595,25 @@ static int gve_prep_tso(struct sk_buff *skb) err = skb_cow_head(skb, 0); if (err < 0) return err; + shinfo = skb_shinfo(skb); l4_start = skb_transport_offset(skb); paylen = skb->len - l4_start; - switch (shinfo->gso_type) { - case SKB_GSO_TCPV4: - case SKB_GSO_TCPV6: + /* gso_type is a bitmask: SKB_GSO_DODGY, SKB_GSO_TCP_ECN and + * SKB_GSO_TCP_FIXEDID may be set alongside the protocol bit. + */ + if (shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) { tcp = tcp_hdr(skb); csum_replace_by_diff(&tcp->check, (__force __wsum)htonl(paylen)); header_len = skb_tcp_all_headers(skb); - break; - case SKB_GSO_UDP_L4: + } else if (shinfo->gso_type & SKB_GSO_UDP_L4) { udp = udp_hdr(skb); csum_replace_by_diff(&udp->check, (__force __wsum)htonl(paylen)); header_len = sizeof(struct udphdr) + l4_start; - break; - default: + } else { return -EINVAL; } diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 744d6585a949..ad71b5e39e33 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2340,7 +2340,7 @@ mvneta_swbm_rx_frame(struct mvneta_port *pp, /* Prefetch header */ prefetch(data); - xdp_buff_clear_frags_flag(xdp); + xdp_reinit_buff(xdp); xdp_prepare_buff(xdp, data, pp->rx_offset_correction + MVNETA_MH_SIZE, data_len, true); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c index 211c10aa5880..30c672d526a0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c @@ -314,7 +314,7 @@ int mcs_clear_all_stats(struct mcs *mcs, u16 pcifunc, int dir) } /* Clear SC stats */ - for (id = 0; id < map->secy.max; id++) { + for (id = 0; id < map->sc.max; id++) { if (map->sc2pf_map[id] != pcifunc) continue; mcs_clear_stats(mcs, MCS_SC_STATS, id, dir); @@ -738,7 +738,7 @@ int mcs_free_all_rsrc(struct mcs *mcs, int dir, u16 pcifunc) } /* free sc entries */ - for (id = 0; id < map->secy.max; id++) { + for (id = 0; id < map->sc.max; id++) { if (map->sc2pf_map[id] != pcifunc) continue; mcs_free_rsrc(&map->sc, map->sc2pf_map, id, pcifunc); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c index dbf173196608..65248bb0b61d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c @@ -242,15 +242,6 @@ int cn20k_register_pfvf_mbox_intr(struct otx2_nic *pf, int numvfs) #define RQ_BP_LVL_AURA (255 - ((85 * 256) / 100)) /* BP when 85% is full */ -static u8 cn20k_aura_bpid_idx(struct otx2_nic *pfvf, int aura_id) -{ -#ifdef CONFIG_DCB - return pfvf->queue_to_pfc_map[aura_id]; -#else - return 0; -#endif -} - static int cn20k_tc_get_entry_index(struct otx2_flow_config *flow_cfg, struct otx2_tc_flow *node) { @@ -580,7 +571,7 @@ static int cn20k_aura_aq_init(struct otx2_nic *pfvf, int aura_id, if (pfvf->nix_blkaddr == BLKADDR_NIX1) aq->aura.bp_ena = 1; - bpid_idx = cn20k_aura_bpid_idx(pfvf, aura_id); + bpid_idx = otx2_get_bpid_idx(pfvf, aura_id); aq->aura.bpid = pfvf->bpid[bpid_idx]; /* Set backpressure level for RQ's Aura */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 52980c4afb54..7e7727880625 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -394,7 +394,7 @@ int otx2_rss_init(struct otx2_nic *pfvf) struct otx2_rss_info *rss = &pfvf->hw.rss_info; int idx, ret = 0; - rss->rss_size = sizeof(*rss->ind_tbl); + rss->rss_size = ARRAY_SIZE(rss->ind_tbl); /* Init RSS key if it is not setup already */ if (!rss->enable) @@ -1056,6 +1056,7 @@ int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) struct nix_aq_enq_req *aq; struct otx2_cq_queue *cq; struct otx2_pool *pool; + u8 bpid_idx; cq = &qset->cq[qidx]; cq->cq_idx = qidx; @@ -1133,11 +1134,8 @@ int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) if (!is_otx2_lbkvf(pfvf->pdev)) { /* Enable receive CQ backpressure */ aq->cq.bp_ena = 1; -#ifdef CONFIG_DCB - aq->cq.bpid = pfvf->bpid[pfvf->queue_to_pfc_map[qidx]]; -#else - aq->cq.bpid = pfvf->bpid[0]; -#endif + bpid_idx = otx2_get_bpid_idx(pfvf, qidx); + aq->cq.bpid = pfvf->bpid[bpid_idx]; /* Set backpressure level is same as cq pass level */ aq->cq.bp = RQ_PASS_LVL_CQ(pfvf->hw.rq_skid, qset->rqe_cnt); @@ -1379,6 +1377,7 @@ int otx2_aura_aq_init(struct otx2_nic *pfvf, int aura_id, { struct npa_aq_enq_req *aq; struct otx2_pool *pool; + u8 bpid_idx; int err; pool = &pfvf->qset.pool[pool_id]; @@ -1434,11 +1433,8 @@ int otx2_aura_aq_init(struct otx2_nic *pfvf, int aura_id, */ if (pfvf->nix_blkaddr == BLKADDR_NIX1) aq->aura.bp_ena = 1; -#ifdef CONFIG_DCB - aq->aura.nix0_bpid = pfvf->bpid[pfvf->queue_to_pfc_map[aura_id]]; -#else - aq->aura.nix0_bpid = pfvf->bpid[0]; -#endif + bpid_idx = otx2_get_bpid_idx(pfvf, aura_id); + aq->aura.nix0_bpid = pfvf->bpid[bpid_idx]; /* Set backpressure level for RQ's Aura */ aq->aura.bp = RQ_BP_LVL_AURA; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index eecee612b7b2..44cd56a0c2c4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -1029,6 +1029,15 @@ static inline int otx2_tc_flower_rule_cnt(struct otx2_nic *pfvf) return pfvf->flow_cfg->nr_flows; } +static inline u8 otx2_get_bpid_idx(struct otx2_nic *pfvf, int qidx) +{ +#ifdef CONFIG_DCB + if (pfvf->queue_to_pfc_map) + return pfvf->queue_to_pfc_map[qidx]; +#endif + return 0; +} + /* MSI-X APIs */ void otx2_free_cints(struct otx2_nic *pfvf, int n); void otx2_set_cints_affinity(struct otx2_nic *pfvf); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index efc238abb67b..772666226715 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -5336,6 +5336,22 @@ static int mtk_probe(struct platform_device *pdev) } } + /* we run 2 devices on the same DMA ring so we need a dummy device + * for NAPI to work. Allocate it before registering the netdevs so a + * concurrent ndo_open (e.g. netifd bringing the first netdev up the + * instant it is registered) never observes eth->dummy_dev == NULL in + * mtk_dma_init() -> mtk_rx_alloc() -> __xdp_rxq_info_reg() (net/core/xdp.c + * "Missing net_device from driver") and fails the first open. + */ + eth->dummy_dev = alloc_netdev_dummy(0); + if (!eth->dummy_dev) { + err = -ENOMEM; + dev_err(eth->dev, "failed to allocated dummy device\n"); + goto err_unreg_netdev; + } + netif_napi_add(eth->dummy_dev, ð->tx_napi, mtk_napi_tx); + netif_napi_add(eth->dummy_dev, ð->rx_napi, mtk_napi_rx); + for (i = 0; i < MTK_MAX_DEVS; i++) { if (!eth->netdev[i]) continue; @@ -5350,18 +5366,6 @@ static int mtk_probe(struct platform_device *pdev) eth->netdev[i]->base_addr, eth->irq[MTK_FE_IRQ_SHARED]); } - /* we run 2 devices on the same DMA ring so we need a dummy device - * for NAPI to work - */ - eth->dummy_dev = alloc_netdev_dummy(0); - if (!eth->dummy_dev) { - err = -ENOMEM; - dev_err(eth->dev, "failed to allocated dummy device\n"); - goto err_unreg_netdev; - } - netif_napi_add(eth->dummy_dev, ð->tx_napi, mtk_napi_tx); - netif_napi_add(eth->dummy_dev, ð->rx_napi, mtk_napi_rx); - platform_set_drvdata(pdev, eth); schedule_delayed_work(ð->reset.monitor_work, MTK_DMA_MONITOR_TIMEOUT); @@ -5371,6 +5375,16 @@ static int mtk_probe(struct platform_device *pdev) err_unreg_netdev: mtk_unreg_dev(eth); err_deinit_ppe: + /* A netdev opened while probe was still registering can have queued + * the frame-engine reset worker, which takes rtnl and drives the + * hardware; cancel it before anything below is torn down. + */ + cancel_work_sync(ð->pending_work); + if (eth->dummy_dev) { + netif_napi_del(ð->tx_napi); + netif_napi_del(ð->rx_napi); + free_netdev(eth->dummy_dev); + } mtk_ppe_deinit(eth); mtk_mdio_cleanup(eth); err_free_dev: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c index 77ea51bfbaae..2cca20e1b9b7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c @@ -293,6 +293,9 @@ static u64 mlx5e_xsk_fill_timestamp(void *_priv) struct mlx5e_xsk_tx_complete *priv = _priv; u64 ts; + if (!priv->cqe) + return 0; + ts = get_cqe_ts(priv->cqe); if (mlx5_is_real_time_rq(priv->cq->mdev) || mlx5_is_real_time_sq(priv->cq->mdev)) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c index dd14cdc378de..3b34bec559e0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c @@ -1943,9 +1943,11 @@ static int mlx5_handle_changeupper_event(struct mlx5_lag *ldev, } } if (i < MLX5_MAX_PORTS) { - slave = bond_slave_get_rcu(ndev_tmp); - if (slave) - has_inactive |= bond_is_slave_inactive(slave); + if (netif_is_bond_master(upper)) { + slave = bond_slave_get_rcu(ndev_tmp); + if (slave) + has_inactive |= bond_is_slave_inactive(slave); + } bond_status |= (1 << idx); } @@ -2029,7 +2031,7 @@ static int mlx5_handle_changeinfodata_event(struct mlx5_lag *ldev, bool has_inactive = 0; int idx; - if (!netif_is_lag_master(ndev)) + if (!netif_is_bond_master(ndev)) return 0; rcu_read_lock(); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c index dad713e9ddd5..b530fb6d3d0c 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c @@ -898,6 +898,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev) dev_info(sparx5->dev, "MAC addr was not set, use random MAC\n"); eth_random_addr(sparx5->base_mac); sparx5->base_mac[5] = 0; + sparx5->base_mac_assign_type = NET_ADDR_RANDOM; } sparx5->fdma_irq = platform_get_irq_byname(sparx5->pdev, "fdma"); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index eb57b86fbe22..0f7d8440f614 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -392,6 +392,7 @@ struct sparx5 { struct notifier_block switchdev_blocking_nb; /* Switch state */ u8 base_mac[ETH_ALEN]; + u8 base_mac_assign_type; /* Associated bridge device (when bridged) */ struct net_device *hw_bridge_dev; /* Bridged interfaces */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c index 4dedf5d92641..b3725cf2dd44 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c @@ -294,6 +294,7 @@ struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno) ndev->ethtool_ops = &sparx5_ethtool_ops; eth_hw_addr_gen(ndev, sparx5->base_mac, portno + 1); + ndev->addr_assign_type = sparx5->base_mac_assign_type; return ndev; } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c index a16ec8136d6d..5953e04ef14e 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c @@ -673,6 +673,9 @@ void sparx5_ptp_deinit(struct sparx5 *sparx5) struct sparx5_port *port; int i; + if (!sparx5->ptp) + return; + if (sparx5->ptp_irq >= 0) { disable_irq(sparx5->ptp_irq); sparx5->ptp_irq = -ENXIO; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index ff86cde11a32..2676bc2268c8 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -3524,8 +3524,8 @@ int vcap_filter_rule_keys(struct vcap_rule *rule, if (key == keylist[idx]) { list_del(&ckf->ctrl.list); kfree(ckf); - idx++; err = 0; + break; } } } diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index ec4fc21fa21f..ee1bc9bd0447 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -5484,6 +5484,12 @@ static int r8169_mdio_register(struct rtl8169_private *tp) phy_disable_eee_mode(tp->phydev, ETHTOOL_LINK_MODE_2500baseT_Full_BIT); + /* Keep EEE off but the version stays EEE-capable so that link-up + * still clears the MAC TX-LPI bits. + */ + if (tp->mac_version == RTL_GIGA_MAC_VER_46) + phy_disable_eee(tp->phydev); + /* PHY will be woken up in rtl_open() */ phy_suspend(tp->phydev); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 29025eb41698..010fef79ec01 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1629,6 +1629,16 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv, stmmac_clear_tx_descriptors(priv, dma_conf, queue); } +static bool stmmac_rx_check_buf2_cap(struct stmmac_priv *priv) +{ + /* Only cores that can program an independent secondary RX buffer + * (used for scatter-gather overflow or split-header payload) back + * buffer2. Legacy cores have no set_sec_addr op, so buffer2 is + * never handed to the hardware there. + */ + return priv->hw->desc && priv->hw->desc->set_sec_addr; +} + /** * stmmac_init_rx_buffers - init the RX descriptor buffer. * @priv: driver private structure @@ -1659,16 +1669,13 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, buf->page_offset = stmmac_rx_offset(priv); } - if (priv->sph_active && !buf->sec_page) { + if (stmmac_rx_check_buf2_cap(priv) && !buf->sec_page) { buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp); if (!buf->sec_page) return -ENOMEM; buf->sec_addr = page_pool_get_dma_addr(buf->sec_page); stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, true); - } else { - buf->sec_page = NULL; - stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, false); } buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset; @@ -2256,13 +2263,8 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, pp_params.nid = dev_to_node(priv->device); pp_params.dev = priv->device; pp_params.dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; - pp_params.offset = stmmac_rx_offset(priv); - pp_params.max_len = dma_conf->dma_buf_sz; - - if (priv->sph_active) { - pp_params.offset = 0; - pp_params.max_len += stmmac_rx_offset(priv); - } + pp_params.offset = 0; + pp_params.max_len = dma_conf->dma_buf_sz + stmmac_rx_offset(priv); rx_q->page_pool = page_pool_create(&pp_params); if (IS_ERR(rx_q->page_pool)) { @@ -5095,7 +5097,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) break; } - if (priv->sph_active && !buf->sec_page) { + if (stmmac_rx_check_buf2_cap(priv) && !buf->sec_page) { buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp); if (!buf->sec_page) break; @@ -5106,10 +5108,8 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset; stmmac_set_desc_addr(priv, p, buf->addr); - if (priv->sph_active) - stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, true); - else - stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, false); + stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, + stmmac_rx_check_buf2_cap(priv)); stmmac_refill_desc3(priv, rx_q, p); rx_q->rx_count_frames++; @@ -5140,7 +5140,9 @@ static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv, unsigned int plen = 0, hlen = 0; int coe = priv->hw->rx_csum; - /* Not first descriptor, buffer is always zero */ + /* Not first descriptor, SPH enabled: buffer1 only carries the + * split header of the first descriptor, so it is zero here. + */ if (priv->sph_active && len) return 0; @@ -5151,14 +5153,16 @@ static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv, return hlen; } - /* First descriptor, not last descriptor and not split header */ + /* Not last descriptor and not split header: buffer1 is fully filled */ if (status & rx_not_ls) return priv->dma_conf.dma_buf_sz; plen = stmmac_get_rx_frame_len(priv, p, coe); - /* First descriptor and last descriptor and not split header */ - return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen); + /* Last descriptor and not split header: buffer1 holds the remaining + * bytes of the frame, up to dma_buf_sz + */ + return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen - len); } static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv, @@ -5168,8 +5172,7 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv, int coe = priv->hw->rx_csum; unsigned int plen = 0; - /* Not split header, buffer is not available */ - if (!priv->sph_active) + if (!stmmac_rx_check_buf2_cap(priv)) return 0; /* For GMAC4, when split header is enabled, in some rare cases, the @@ -5186,14 +5189,15 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv, * Thus 'plen - len' always gives the correct length of buf2. */ - /* Not GMAC4 and not last descriptor */ - if (priv->plat->core_type != DWMAC_CORE_GMAC4 && (status & rx_not_ls)) + /* Not GMAC4, or non-SPH and not last descriptor */ + if ((priv->plat->core_type != DWMAC_CORE_GMAC4 || !priv->sph_active) && + (status & rx_not_ls)) return priv->dma_conf.dma_buf_sz; /* GMAC4 or last descriptor */ plen = stmmac_get_rx_frame_len(priv, p, coe); - return plen - len; + return plen > len ? plen - len : 0; } static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue, @@ -5716,6 +5720,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) unsigned int desc_size; struct sk_buff *skb = NULL; struct stmmac_xdp_buff ctx; + bool fcs_stripped = false; int xdp_status = 0; int bufsz; @@ -5805,24 +5810,25 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) len += buf2_len; /* ACS is disabled; strip manually. */ - if (likely(!(status & rx_not_ls))) { - if (buf2_len) { - buf2_len -= ETH_FCS_LEN; - len -= ETH_FCS_LEN; - } else if (buf1_len) { - buf1_len -= ETH_FCS_LEN; - len -= ETH_FCS_LEN; - } - } + if (likely(!(status & rx_not_ls))) + len -= ETH_FCS_LEN; if (!skb) { unsigned int pre_len, sync_len; + /* Each frame starts here: reset the FCS handling */ + fcs_stripped = false; + dma_sync_single_for_cpu(priv->device, buf->addr, buf1_len, dma_dir); net_prefetch(page_address(buf->page) + buf->page_offset); + if (stmmac_xdp_is_enabled(priv) && !buf2_len) { + buf1_len -= ETH_FCS_LEN; + fcs_stripped = true; + } + xdp_init_buff(&ctx.xdp, bufsz, &rx_q->xdp_rxq); xdp_prepare_buff(&ctx.xdp, page_address(buf->page), buf->page_offset, buf1_len, true); @@ -5923,6 +5929,15 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) /* Got entire packet into SKB. Finish it. */ + /* Remove FCS if needed */ + if (!fcs_stripped && pskb_trim(skb, len)) { + dev_kfree_skb_any(skb); + skb = NULL; + rx_dropped++; + count++; + continue; + } + stmmac_get_rx_hwtstamp(priv, p, np, skb); if (priv->hw->hw_vlan_en) diff --git a/drivers/net/pcs/pcs-rzn1-miic.c b/drivers/net/pcs/pcs-rzn1-miic.c index cb74861e823c..3aa513467060 100644 --- a/drivers/net/pcs/pcs-rzn1-miic.c +++ b/drivers/net/pcs/pcs-rzn1-miic.c @@ -270,6 +270,9 @@ static struct miic_port *phylink_pcs_to_miic_port(struct phylink_pcs *pcs) static void miic_unlock_regs(struct miic *miic) { + /* Clear protection state */ + writel(0x0000, miic->base + MIIC_PRCMD); + /* Unprotect register writes */ writel(0x00A5, miic->base + MIIC_PRCMD); writel(0x0001, miic->base + MIIC_PRCMD); diff --git a/drivers/net/pfcp.c b/drivers/net/pfcp.c index d8e4d60f5834..8bbaf167db75 100644 --- a/drivers/net/pfcp.c +++ b/drivers/net/pfcp.c @@ -104,6 +104,9 @@ static int pfcp_encap_recv(struct sock *sk, struct sk_buff *skb) static void pfcp_del_sock(struct pfcp_dev *pfcp) { + if (!pfcp->sk) + return; + udp_tunnel_sock_release(pfcp->sk); pfcp->sk = NULL; } diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c index e95d391a6dc2..096407bebd52 100644 --- a/drivers/net/phy/aquantia/aquantia_main.c +++ b/drivers/net/phy/aquantia/aquantia_main.c @@ -544,7 +544,7 @@ static int aqr_gen1_read_status(struct phy_device *phydev) if (ret) return ret; - if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE) + if (!phydev->link) return 0; /* The status register is not immediately correct on line side link up. @@ -594,6 +594,9 @@ static int aqr_gen1_read_status(struct phy_device *phydev) phydev->interface = aqr_translate_interface(phydev, interface); + if (phydev->autoneg == AUTONEG_DISABLE) + return 0; + /* Read rate from vendor register */ return aqr_gen1_read_rate(phydev); } diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c index ba4dc07752b6..61bd4972d59f 100644 --- a/drivers/net/phy/qcom/at803x.c +++ b/drivers/net/phy/qcom/at803x.c @@ -1051,11 +1051,15 @@ static int ipq5018_config_init(struct phy_device *phydev) if (priv->set_short_cable_dac) { /* setting MDAC (Multi-level Digital-to-Analog Converter) in MMD1 */ phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC, - IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_MMD1_MDAC_VAL); + IPQ5018_PHY_DAC_MASK, + FIELD_PREP(IPQ5018_PHY_DAC_MASK, + IPQ5018_PHY_MMD1_MDAC_VAL)); /* setting EDAC (Error-detection and Correction) in debug register */ at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC, - IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_DEBUG_EDAC_VAL); + IPQ5018_PHY_DAC_MASK, + FIELD_PREP(IPQ5018_PHY_DAC_MASK, + IPQ5018_PHY_DEBUG_EDAC_VAL)); } return 0; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 8178a8758cd3..aa66a30e56cd 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1085,6 +1085,7 @@ static const struct usb_device_id products[] = { {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0122)}, /* Quectel RG650V */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)}, /* Quectel EP06/EG06/EM06 */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x030b)}, /* Quectel EM060K/EG120K-EA */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0620)}, /* Quectel EM160R-GL */ {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)}, /* Quectel RM500Q-GL */ diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index ae91b57c8422..c1fa42edd363 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -6716,6 +6716,7 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw) ar->num_started_vdevs = 0; ar->num_created_vdevs = 0; ar->num_peers = 0; + ar->num_stations = 0; ar->allocated_vdev_map = 0; /* Configure monitor status ring with default rx_filter to get rx status diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index d3491ff08e6e..7d2f292e20c5 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -14,6 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include "htc.h" @@ -328,11 +329,14 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev) tx_skb_cnt = min_t(u16, hif_dev->tx.tx_skb_cnt, MAX_TX_AGGR_NUM); for (i = 0; i < tx_skb_cnt; i++) { - nskb = __skb_dequeue(&hif_dev->tx.tx_skb_queue); + nskb = skb_peek(&hif_dev->tx.tx_skb_queue); + if (!nskb) + break; - /* Should never be NULL */ - BUG_ON(!nskb); + if (tx_buf->offset + nskb->len + 4 > MAX_TX_BUF_SIZE) + break; + nskb = __skb_dequeue(&hif_dev->tx.tx_skb_queue); hif_dev->tx.tx_skb_cnt--; buf = tx_buf->buf; @@ -342,13 +346,8 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev) *hdr++ = cpu_to_le16(ATH_USB_TX_STREAM_MODE_TAG); buf += 4; memcpy(buf, nskb->data, nskb->len); - tx_buf->len = nskb->len + 4; - - if (i < (tx_skb_cnt - 1)) - tx_buf->offset += (((tx_buf->len - 1) / 4) + 1) * 4; - - if (i == (tx_skb_cnt - 1)) - tx_buf->len += tx_buf->offset; + tx_buf->len = tx_buf->offset + nskb->len + 4; + tx_buf->offset += round_up(nskb->len + 4, 4); __skb_queue_tail(&tx_buf->skb_queue, nskb); TX_STAT_INC(hif_dev, skb_queued); diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 6c33e898b300..4d2b75d0212e 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -281,7 +281,6 @@ struct ath9k_htc_rxbuf { struct ath9k_htc_rx { struct list_head rxbuf; spinlock_t rxbuflock; - bool initialized; }; #define ATH9K_HTC_TX_CLEANUP_INTERVAL 50 /* ms */ diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 6de78ae85726..911eaa4f776a 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -910,6 +910,9 @@ static int ath9k_init_device(struct ath9k_htc_priv *priv, ath9k_init_leds(priv); ath9k_start_rfkill_poll(priv); + /* signal completion to ath9k_htc_rxep() and ath9k_wmi_event_tasklet() */ + smp_store_release(&priv->initialized, true); + return 0; err_world: @@ -966,10 +969,6 @@ int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, htc_handle->drv_priv = priv; - /* Allow ath9k_wmi_event_tasklet() to operate. */ - smp_wmb(); - priv->initialized = true; - return 0; err_init: diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index bed7ea2425a0..95f48c7dca15 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -1141,8 +1141,8 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb, struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL; unsigned long flags; - /* Check if ath9k_rx_init() completed. */ - if (!data_race(priv->rx.initialized)) + /* Check if ath9k_init_device() completed. */ + if (!smp_load_acquire(&priv->initialized)) goto err; spin_lock_irqsave(&priv->rx.rxbuflock, flags); @@ -1200,10 +1200,6 @@ int ath9k_rx_init(struct ath9k_htc_priv *priv) list_add_tail(&rxbuf->list, &priv->rx.rxbuf); } - /* Allow ath9k_htc_rxep() to operate. */ - smp_wmb(); - priv->rx.initialized = true; - return 0; err: diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c index 284e8c13b043..4a08babb6d9d 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.c +++ b/drivers/net/wireless/ath/ath9k/wmi.c @@ -146,6 +146,12 @@ void ath9k_wmi_event_tasklet(struct tasklet_struct *t) unsigned long flags; u16 cmd_id; + /* Check if ath9k_init_device() completed. */ + if (!smp_load_acquire(&priv->initialized)) { + tasklet_schedule(t); + return; + } + do { spin_lock_irqsave(&wmi->wmi_lock, flags); skb = __skb_dequeue(&wmi->wmi_event_queue); @@ -155,12 +161,6 @@ void ath9k_wmi_event_tasklet(struct tasklet_struct *t) } spin_unlock_irqrestore(&wmi->wmi_lock, flags); - /* Check if ath9k_htc_probe_device() completed. */ - if (!data_race(priv->initialized)) { - kfree_skb(skb); - continue; - } - hdr = (struct wmi_cmd_hdr *) skb->data; cmd_id = be16_to_cpu(hdr->command_id); wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr)); @@ -206,9 +206,16 @@ static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb) { skb_pull(skb, sizeof(struct wmi_cmd_hdr)); - if (wmi->cmd_rsp_buf != NULL && wmi->cmd_rsp_len != 0) + if (wmi->cmd_rsp_buf && wmi->cmd_rsp_len) { + if (skb->len < wmi->cmd_rsp_len) { + wmi->cmd_rsp_status = -EMSGSIZE; + goto complete; + } memcpy(wmi->cmd_rsp_buf, skb->data, wmi->cmd_rsp_len); + } + wmi->cmd_rsp_status = 0; +complete: complete(&wmi->cmd_wait); } @@ -300,6 +307,7 @@ static int ath9k_wmi_cmd_issue(struct wmi *wmi, /* record the rsp buffer and length */ wmi->cmd_rsp_buf = rsp_buf; wmi->cmd_rsp_len = rsp_len; + wmi->cmd_rsp_status = 0; wmi->last_seq_id = wmi->tx_seq_id; spin_unlock_irqrestore(&wmi->wmi_lock, flags); @@ -356,9 +364,13 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, return -ETIMEDOUT; } + spin_lock_irqsave(&wmi->wmi_lock, flags); + ret = wmi->cmd_rsp_status; + spin_unlock_irqrestore(&wmi->wmi_lock, flags); + mutex_unlock(&wmi->op_mutex); - return 0; + return ret; out: ath_dbg(common, WMI, "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id)); diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h index 5c3b710b8f31..0bb9d31f1a1c 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.h +++ b/drivers/net/wireless/ath/ath9k/wmi.h @@ -157,6 +157,7 @@ struct wmi { u16 tx_seq_id; u8 *cmd_rsp_buf; u32 cmd_rsp_len; + int cmd_rsp_status; bool stopped; struct list_head pending_tx_events; diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c index cbaf250626c5..f7ca182d1494 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -3807,6 +3807,7 @@ il3945_pci_remove(struct pci_dev *pdev) il_free_channel_map(il); il_free_geos(il); + il_eeprom_free(il); kfree(il->scan_cmd); dev_kfree_skb(il->beacon_skb); ieee80211_free_hw(il->hw); diff --git a/drivers/net/wireless/silabs/wfx/hif_tx.c b/drivers/net/wireless/silabs/wfx/hif_tx.c index 9f403d275cb1..9efb108bd877 100644 --- a/drivers/net/wireless/silabs/wfx/hif_tx.c +++ b/drivers/net/wireless/silabs/wfx/hif_tx.c @@ -207,9 +207,11 @@ int wfx_hif_read_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, void *val, si dev_warn(wdev->dev, "%s: confirmation mismatch request\n", __func__); ret = -EIO; } - if (ret == -ENOMEM) + if (!ret && le16_to_cpu(reply->length) > val_len) { dev_err(wdev->dev, "buffer is too small to receive %s (%zu < %d)\n", wfx_get_mib_name(mib_id), val_len, le16_to_cpu(reply->length)); + ret = -EIO; + } if (!ret) memcpy(val, &reply->mib_data, le16_to_cpu(reply->length)); else diff --git a/drivers/net/wireless/st/cw1200/bh.c b/drivers/net/wireless/st/cw1200/bh.c index b034bab4b489..9367a981f1de 100644 --- a/drivers/net/wireless/st/cw1200/bh.c +++ b/drivers/net/wireless/st/cw1200/bh.c @@ -344,6 +344,7 @@ static int cw1200_bh_tx_helper(struct cw1200_common *priv, int *tx_burst) { size_t tx_len; + size_t aligned_len; u8 *data; int ret; struct wsm_hdr *wsm; @@ -376,9 +377,14 @@ static int cw1200_bh_tx_helper(struct cw1200_common *priv, atomic_inc(&priv->bh_tx); - tx_len = priv->hwbus_ops->align_size( + aligned_len = priv->hwbus_ops->align_size( priv->hwbus_priv, tx_len); + /* Zero the bus alignment padding so no stale data is sent. */ + if (aligned_len > tx_len) + memset(data + tx_len, 0, aligned_len - tx_len); + tx_len = aligned_len; + /* Check if not exceeding CW1200 capabilities */ if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE)) pr_debug("Write aligned len: %zu\n", tx_len); diff --git a/drivers/net/wireless/st/cw1200/txrx.c b/drivers/net/wireless/st/cw1200/txrx.c index 084d52b11f5b..e13695667a00 100644 --- a/drivers/net/wireless/st/cw1200/txrx.c +++ b/drivers/net/wireless/st/cw1200/txrx.c @@ -14,6 +14,7 @@ #include "wsm.h" #include "bh.h" #include "sta.h" +#include "hwbus.h" #include "debug.h" #define CW1200_INVALID_RATE_ID (0xFF) @@ -716,6 +717,7 @@ void cw1200_tx(struct ieee80211_hw *dev, struct ieee80211_sta *sta; struct wsm_tx *wsm; bool tid_update = false; + size_t pad_len; u8 flags = 0; int ret; @@ -764,6 +766,18 @@ void cw1200_tx(struct ieee80211_hw *dev, sta = t.sta; + /* + * The bus transfer is padded up to the bus alignment and the + * padding is transferred from the frame buffer. Make sure the + * buffer has room for the padding so the device never receives + * data past the end of the frame. + */ + pad_len = priv->hwbus_ops->align_size(priv->hwbus_priv, skb->len) - + skb->len; + if (skb_tailroom(skb) < pad_len && + pskb_expand_head(skb, 0, pad_len, GFP_ATOMIC)) + goto drop; + spin_lock_bh(&priv->ps_state_lock); { tid_update = cw1200_tx_h_pm_state(priv, &t); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index ecf55e34caae..ef920a52e935 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -6811,8 +6811,10 @@ void wlcore_remove(struct platform_device *pdev) if (pdev_data->family && pdev_data->family->nvs_name) wait_for_completion(&wl->nvs_loading_complete); - if (!wl->initialized) + if (!wl->initialized) { + pm_runtime_put_noidle(wl->dev); return; + } if (wl->wakeirq >= 0) { dev_pm_clear_wake_irq(wl->dev); diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 2ed673649c48..d269457e839e 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1234,8 +1234,16 @@ static int handle_incoming_queue(struct netfront_queue *queue, while ((skb = __skb_dequeue(rxq)) != NULL) { int pull_to = NETFRONT_SKB_CB(skb)->pull_to; - if (pull_to > skb_headlen(skb)) - __pskb_pull_tail(skb, pull_to - skb_headlen(skb)); + /* pull_to comes from the first slot's length, which the + * backend controls. Make sure the head holds at least an + * Ethernet header for eth_type_trans(). + */ + if (!pskb_may_pull(skb, max(pull_to, ETH_HLEN))) { + kfree_skb(skb); + packets_dropped++; + queue->info->netdev->stats.rx_errors++; + continue; + } /* Ethernet work: Delayed to here as it peeks the header. */ skb->protocol = eth_type_trans(skb, queue->info->netdev); diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index a73cc6a43f5e..a085e47f0d03 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -691,6 +691,11 @@ static void nvme_free_ns_head(struct kref *ref) kfree(head); } +void nvme_get_ns_head(struct nvme_ns_head *head) +{ + kref_get(&head->ref); +} + bool nvme_tryget_ns_head(struct nvme_ns_head *head) { return kref_get_unless_zero(&head->ref); @@ -3174,9 +3179,16 @@ static void nvme_release_subsystem(struct device *dev) { struct nvme_subsystem *subsys = container_of(dev, struct nvme_subsystem, dev); + struct nvme_effects_log *cel; + unsigned long i; if (subsys->instance >= 0) ida_free(&nvme_instance_ida, subsys->instance); + xa_for_each(&subsys->cels, i, cel) { + xa_erase(&subsys->cels, i); + kfree(cel); + } + xa_destroy(&subsys->cels); kfree(subsys); } @@ -3288,6 +3300,7 @@ static int nvme_init_subsystem(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id) kref_init(&subsys->ref); INIT_LIST_HEAD(&subsys->ctrls); INIT_LIST_HEAD(&subsys->nsheads); + xa_init(&subsys->cels); nvme_init_subnqn(subsys, ctrl, id); memcpy(subsys->serial, id->sn, sizeof(subsys->serial)); memcpy(subsys->model, id->mn, sizeof(subsys->model)); @@ -3390,7 +3403,7 @@ int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp, u8 csi, static int nvme_get_effects_log(struct nvme_ctrl *ctrl, u8 csi, struct nvme_effects_log **log) { - struct nvme_effects_log *old, *cel = xa_load(&ctrl->cels, csi); + struct nvme_effects_log *old, *cel = xa_load(&ctrl->subsys->cels, csi); int ret; if (cel) @@ -3407,7 +3420,7 @@ static int nvme_get_effects_log(struct nvme_ctrl *ctrl, u8 csi, return ret; } - old = xa_store(&ctrl->cels, csi, cel, GFP_KERNEL); + old = xa_store(&ctrl->subsys->cels, csi, cel, GFP_KERNEL); if (xa_is_err(old)) { kfree(cel); return xa_err(old); @@ -3482,7 +3495,7 @@ static int nvme_init_effects_log(struct nvme_ctrl *ctrl, if (!effects) return -ENOMEM; - old = xa_store(&ctrl->cels, csi, effects, GFP_KERNEL); + old = xa_store(&ctrl->subsys->cels, csi, effects, GFP_KERNEL); if (xa_is_err(old)) { kfree(effects); return xa_err(old); @@ -3528,23 +3541,27 @@ static int nvme_init_effects(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id) { int ret = 0; + mutex_lock(&ctrl->subsys->lock); + ctrl->effects = xa_load(&ctrl->subsys->cels, NVME_CSI_NVM); if (ctrl->effects) - return 0; + goto out_unlock; if (id->lpa & NVME_CTRL_LPA_CMD_EFFECTS_LOG) { ret = nvme_get_effects_log(ctrl, NVME_CSI_NVM, &ctrl->effects); if (ret < 0) - return ret; + goto out_unlock; } if (!ctrl->effects) { ret = nvme_init_effects_log(ctrl, NVME_CSI_NVM, &ctrl->effects); if (ret < 0) - return ret; + goto out_unlock; } nvme_init_known_nvm_effects(ctrl); - return 0; +out_unlock: + mutex_unlock(&ctrl->subsys->lock); + return ret; } static int nvme_check_ctrl_fabric_info(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id) @@ -3895,6 +3912,11 @@ static int nvme_subsys_check_duplicate_ids(struct nvme_subsystem *subsys, static void nvme_cdev_rel(struct device *dev) { ida_free(&nvme_ns_chr_minor_ida, MINOR(dev->devt)); + if (dev->parent->class == &nvme_class) + nvme_put_ns(container_of(dev, struct nvme_ns, cdev_device)); + else + nvme_put_ns_head(container_of(dev, struct nvme_ns_head, + cdev_device)); } void nvme_cdev_del(struct cdev *cdev, struct device *cdev_device) @@ -3903,9 +3925,9 @@ void nvme_cdev_del(struct cdev *cdev, struct device *cdev_device) put_device(cdev_device); } -int nvme_cdev_add(const char *name, struct cdev *cdev, - struct device *cdev_device, - const struct file_operations *fops, struct module *owner) +int nvme_cdev_add(struct cdev *cdev, struct device *cdev_device, + const struct file_operations *fops, struct module *owner, + int ctrl, int head) { int minor, ret; @@ -3913,7 +3935,7 @@ int nvme_cdev_add(const char *name, struct cdev *cdev, if (minor < 0) return minor; - ret = dev_set_name(cdev_device, name); + ret = dev_set_name(cdev_device, "ng%dn%d", ctrl, head); if (ret) { ida_free(&nvme_ns_chr_minor_ida, minor); return ret; @@ -3954,16 +3976,15 @@ static const struct file_operations nvme_ns_chr_fops = { static void nvme_add_ns_cdev(struct nvme_ns *ns) { - char name[32]; - ns->cdev_device.parent = ns->ctrl->device; - snprintf(name, sizeof(name), "ng%dn%d", ns->ctrl->instance, - ns->head->instance); - if (nvme_cdev_add(name, &ns->cdev, &ns->cdev_device, - &nvme_ns_chr_fops, ns->ctrl->ops->module)) { - dev_err(ns->ctrl->device, "Unable to create the %s device\n", - name); + nvme_get_ns(ns); /* Undone in nvme_cdev_rel() */ + if (nvme_cdev_add(&ns->cdev, &ns->cdev_device, + &nvme_ns_chr_fops, ns->ctrl->ops->module, + ns->ctrl->instance, ns->head->instance)) { + dev_err(ns->ctrl->device, "Unable to create the ng%dn%d device\n", + ns->ctrl->instance, ns->head->instance); + nvme_put_ns(ns); return; } set_bit(NVME_NS_CDEV_LIVE, &ns->flags); @@ -5125,19 +5146,6 @@ void nvme_uninit_ctrl(struct nvme_ctrl *ctrl) } EXPORT_SYMBOL_GPL(nvme_uninit_ctrl); -static void nvme_free_cels(struct nvme_ctrl *ctrl) -{ - struct nvme_effects_log *cel; - unsigned long i; - - xa_for_each(&ctrl->cels, i, cel) { - xa_erase(&ctrl->cels, i); - kfree(cel); - } - - xa_destroy(&ctrl->cels); -} - static void nvme_free_ctrl(struct device *dev) { struct nvme_ctrl *ctrl = @@ -5150,7 +5158,6 @@ static void nvme_free_ctrl(struct device *dev) blk_put_queue(ctrl->fabrics_q); if (!subsys || ctrl->instance != subsys->instance) ida_free(&nvme_instance_ida, ctrl->instance); - nvme_free_cels(ctrl); nvme_mpath_uninit(ctrl); cleanup_srcu_struct(&ctrl->srcu); nvme_auth_stop(ctrl); @@ -5196,7 +5203,6 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, mutex_init(&ctrl->scan_lock); INIT_LIST_HEAD(&ctrl->namespaces); - xa_init(&ctrl->cels); ctrl->dev = dev; ctrl->ops = ops; ctrl->quirks = quirks; diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index 9b9a657fa330..98472f7179fd 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -611,28 +611,8 @@ const struct block_device_operations nvme_ns_head_ops = { .pr_ops = &nvme_pr_ops, }; -static inline struct nvme_ns_head *cdev_to_ns_head(struct cdev *cdev) -{ - return container_of(cdev, struct nvme_ns_head, cdev); -} - -static int nvme_ns_head_chr_open(struct inode *inode, struct file *file) -{ - if (!nvme_tryget_ns_head(cdev_to_ns_head(inode->i_cdev))) - return -ENXIO; - return 0; -} - -static int nvme_ns_head_chr_release(struct inode *inode, struct file *file) -{ - nvme_put_ns_head(cdev_to_ns_head(inode->i_cdev)); - return 0; -} - static const struct file_operations nvme_ns_head_chr_fops = { .owner = THIS_MODULE, - .open = nvme_ns_head_chr_open, - .release = nvme_ns_head_chr_release, .unlocked_ioctl = nvme_ns_head_chr_ioctl, .compat_ioctl = compat_ptr_ioctl, .uring_cmd = nvme_ns_head_chr_uring_cmd, @@ -641,16 +621,16 @@ static const struct file_operations nvme_ns_head_chr_fops = { static void nvme_add_ns_head_cdev(struct nvme_ns_head *head) { - char name[32]; - head->cdev_device.parent = &head->subsys->dev; - snprintf(name, sizeof(name), "ng%dn%d", head->subsys->instance, - head->instance); - if (nvme_cdev_add(name, &head->cdev, &head->cdev_device, - &nvme_ns_head_chr_fops, THIS_MODULE)) { + nvme_get_ns_head(head); /* Undone in nvme_cdev_rel() */ + if (nvme_cdev_add(&head->cdev, &head->cdev_device, + &nvme_ns_head_chr_fops, THIS_MODULE, + head->subsys->instance, head->instance)) { dev_err(disk_to_dev(head->disk), - "Unable to create the %s device\n", name); + "Unable to create the ng%dn%d device\n", + head->subsys->instance, head->instance); + nvme_put_ns_head(head); return; } set_bit(NVME_NSHEAD_CDEV_LIVE, &head->flags); @@ -757,8 +737,6 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head) lim.dma_alignment = 3; lim.features |= BLK_FEAT_IO_STAT | BLK_FEAT_NOWAIT | BLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES | BLK_FEAT_PCI_P2PDMA; - if (head->ids.csi == NVME_CSI_ZNS) - lim.features |= BLK_FEAT_ZONED; head->disk = blk_alloc_disk(&lim, ctrl->numa_node); if (IS_ERR(head->disk)) @@ -777,7 +755,7 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head) set_bit(GD_SUPPRESS_PART_SCAN, &head->disk->state); sprintf(head->disk->disk_name, "nvme%dn%d", ctrl->subsys->instance, head->instance); - nvme_tryget_ns_head(head); + nvme_get_ns_head(head); return 0; } @@ -837,7 +815,8 @@ static int nvme_parse_ana_log(struct nvme_ctrl *ctrl, void *data, u32 nr_nsids; size_t nsid_buf_size; - if (WARN_ON_ONCE(offset > ctrl->ana_log_size - sizeof(*desc))) + if (WARN_ON_ONCE(offset > ctrl->ana_log_size || + sizeof(*desc) > ctrl->ana_log_size - offset)) return -EINVAL; nr_nsids = le32_to_cpu(desc->nnsids); @@ -853,7 +832,7 @@ static int nvme_parse_ana_log(struct nvme_ctrl *ctrl, void *data, return -EINVAL; offset += sizeof(*desc); - if (WARN_ON_ONCE(offset > ctrl->ana_log_size - nsid_buf_size)) + if (WARN_ON_ONCE(nsid_buf_size > ctrl->ana_log_size - offset)) return -EINVAL; error = cb(ctrl, desc, data); diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index cfdcf53db5ab..47e52a2b61c8 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -413,7 +413,6 @@ struct nvme_ctrl { unsigned long quirks; struct nvme_id_power_state psd[32]; struct nvme_effects_log *effects; - struct xarray cels; struct work_struct scan_work; struct work_struct async_event_work; struct delayed_work ka_work; @@ -505,6 +504,8 @@ struct nvme_subsystem { struct list_head ctrls; struct list_head nsheads __guarded_by(&lock); + /* Command effects logs, indexed by CSI and protected by lock. */ + struct xarray cels; char subnqn[NVMF_NQN_SIZE]; char serial[20]; char model[40]; @@ -1005,11 +1006,12 @@ int nvme_delete_ctrl(struct nvme_ctrl *ctrl); void nvme_queue_scan(struct nvme_ctrl *ctrl); int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp, u8 csi, void *log, size_t size, u64 offset); +void nvme_get_ns_head(struct nvme_ns_head *head); bool nvme_tryget_ns_head(struct nvme_ns_head *head); void nvme_put_ns_head(struct nvme_ns_head *head); -int nvme_cdev_add(const char *name, struct cdev *cdev, - struct device *cdev_device, - const struct file_operations *fops, struct module *owner); +int nvme_cdev_add(struct cdev *cdev, struct device *cdev_device, + const struct file_operations *fops, struct module *owner, + int ctrl, int head); void nvme_cdev_del(struct cdev *cdev, struct device *cdev_device); int nvme_ioctl(struct block_device *bdev, blk_mode_t mode, unsigned int cmd, unsigned long arg); diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 664a36eed23c..8c9826e72bd2 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -4182,6 +4182,8 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_IGNORE_DEV_SUBNQN }, { PCI_DEVICE(0x1344, 0x6001), /* Micron Nitro NVMe */ .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1344, 0x6004), /* Micron 4100AT */ + .driver_data = NVME_QUIRK_DMAPOOL_ALIGN_512, }, { PCI_DEVICE(0x1c5c, 0x1504), /* SK Hynix PC400 */ .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, { PCI_DEVICE(0x1c5c, 0x174a), /* SK Hynix P31 SSD */ diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c index 6286e38436dd..b03b5d1c2dc3 100644 --- a/drivers/nvme/target/configfs.c +++ b/drivers/nvme/target/configfs.c @@ -534,6 +534,7 @@ static ssize_t nvmet_ns_device_path_store(struct config_item *item, struct nvmet_subsys *subsys = ns->subsys; size_t len; int ret; + char *new_path = NULL; mutex_lock(&subsys->lock); ret = -EBUSY; @@ -545,12 +546,14 @@ static ssize_t nvmet_ns_device_path_store(struct config_item *item, if (!len) goto out_unlock; - kfree(ns->device_path); ret = -ENOMEM; - ns->device_path = kmemdup_nul(page, len, GFP_KERNEL); - if (!ns->device_path) + new_path = kmemdup_nul(page, len, GFP_KERNEL); + if (!new_path) goto out_unlock; + kfree(ns->device_path); + ns->device_path = new_path; + mutex_unlock(&subsys->lock); return count; diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index 269daefb8885..2aa9ded7d551 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -1641,6 +1641,8 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args) INIT_DELAYED_WORK(&ctrl->ka_work, nvmet_keep_alive_timer); memcpy(ctrl->hostnqn, args->hostnqn, NVMF_NQN_SIZE); + if (args->hostid) + uuid_copy(&ctrl->hostid, args->hostid); kref_init(&ctrl->ref); ctrl->subsys = subsys; @@ -1699,9 +1701,6 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args) nvmet_start_keep_alive_timer(ctrl); - if (args->hostid) - uuid_copy(&ctrl->hostid, args->hostid); - dhchap_status = nvmet_setup_auth(ctrl, args->sq, false); if (dhchap_status) { pr_err("Failed to setup authentication, dhchap status %u\n", diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index 0e88e227edac..292e40d3b492 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -426,7 +426,7 @@ static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al) struct nvmet_ctrl *ctrl = req->sq->ctrl; int ret = 0; int hash_len = nvme_auth_hmac_hash_len(ctrl->shash_id); - int data_size = sizeof(*d) + hash_len; + int data_size = sizeof(*data) + hash_len; if (ctrl->dh_tfm) data_size += ctrl->dh_keysize; diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c index 803e85df50e5..c4e989480dd7 100644 --- a/drivers/nvme/target/pci-epf.c +++ b/drivers/nvme/target/pci-epf.c @@ -1001,6 +1001,9 @@ nvmet_pci_epf_get_sgl_segment(struct nvmet_pci_epf_ctrl *ctrl, int nr_descs, ret; void *buf; + if (length < sizeof(struct nvme_sgl_desc)) + return NULL; + buf = kmalloc(length, GFP_KERNEL); if (!buf) return NULL; diff --git a/drivers/of/base.c b/drivers/of/base.c index 477017ed6f49..f4673bfe3810 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -95,9 +95,17 @@ static bool __of_node_is_type(const struct device_node *np, const char *type) return !strcmp(match, type); } +static bool of_coreboot_present(void) +{ + struct device_node *np __free(device_node) = + of_find_compatible_node(NULL, NULL, "coreboot"); + + return np; +} + #define EXCLUDED_DEFAULT_CELLS_PLATFORMS ( \ IS_ENABLED(CONFIG_SPARC) || \ - of_find_compatible_node(NULL, NULL, "coreboot") \ + of_coreboot_present() \ ) int of_bus_n_addr_cells(struct device_node *np) diff --git a/drivers/of/irq.c b/drivers/of/irq.c index ec035367c950..ef1ed9743907 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -754,6 +754,8 @@ void __init of_irq_init(const struct of_device_id *matches) list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) { list_del(&desc->list); + of_node_put(desc->interrupt_parent); + of_node_put(desc->dev); kfree(desc); } err: diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 08d5351746be..83c56dd9b5d8 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -256,6 +256,9 @@ static struct property *dup_and_fixup_symbol_prop( if (!target_path) return NULL; target_path_len = strlen(target_path); + /* a root target renders as "/"; drop it to avoid "//" results */ + if (target_path_len == 1 && target_path[0] == '/' && path_tail_len) + target_path_len = 0; new_prop = kzalloc_obj(*new_prop); if (!new_prop) @@ -358,12 +361,13 @@ static int add_changeset_property(struct overlay_changeset *ovcs, return -ENOMEM; if (!prop) { - if (!target->in_livetree) { + ret = of_changeset_add_property(&ovcs->cset, target->np, + new_prop); + /* the detached node owns the property until the apply */ + if (!ret && !target->in_livetree) { new_prop->next = target->np->deadprops; target->np->deadprops = new_prop; } - ret = of_changeset_add_property(&ovcs->cset, target->np, - new_prop); } else { ret = of_changeset_update_property(&ovcs->cset, target->np, new_prop); @@ -853,6 +857,10 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs, err_out: pr_err("%s() failed, ret = %d\n", __func__, ret); + /* let free_overlay_changeset() put the fragments set up so far */ + if (ovcs->fragments) + ovcs->count = cnt; + return ret; } @@ -863,7 +871,8 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs) if (ovcs->cset.entries.next) of_changeset_destroy(&ovcs->cset); - if (ovcs->id) { + /* a failed idr_alloc() leaves its negative error in ovcs->id */ + if (ovcs->id > 0) { idr_remove(&ovcs_idr, ovcs->id); list_del(&ovcs->ovcs_list); ovcs->id = 0; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 350bae907ebf..80d2338b2a0a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3736,8 +3736,21 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask) } root = pcie_find_root_port(dev); - if (!root) + if (!root) { + + /* + * A hypervisor may expose a topology with the root port + * not visible to the guest. If the hypervisor has already + * set AtomicOp Requester Enable in the endpoint, we assume + * it has verified support in the root port, so it is safe + * for the driver to use AtomicOps. + */ + pcie_capability_read_dword(dev, PCI_EXP_DEVCTL2, &ctl2); + if (ctl2 & PCI_EXP_DEVCTL2_ATOMIC_REQ) + return 0; + return -EINVAL; + } pcie_capability_read_dword(root, PCI_EXP_DEVCAP2, &cap); if ((cap & cap_mask) != cap_mask) diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c index 8ba7c25d2565..be173c9511fa 100644 --- a/drivers/rtc/dev.c +++ b/drivers/rtc/dev.c @@ -216,7 +216,7 @@ static long rtc_dev_ioctl(struct file *file, struct rtc_device *rtc = file->private_data; const struct rtc_class_ops *ops = rtc->ops; struct rtc_time tm; - struct rtc_wkalrm alarm; + struct rtc_wkalrm alarm = { 0 }; struct rtc_param param; void __user *uarg = (void __user *)arg; diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c index bba7115ff3ad..c579c4bf5578 100644 --- a/drivers/rtc/rtc-ac100.c +++ b/drivers/rtc/rtc-ac100.c @@ -317,10 +317,12 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip) if (!chip->clk_data) return -ENOMEM; - chip->rtc_32k_clk = clk_hw_register_fixed_rate(chip->dev, - AC100_RTC_32K_NAME, - NULL, 0, - AC100_RTC_32K_RATE); + chip->clk_data->num = AC100_CLKOUT_NUM; + + chip->rtc_32k_clk = devm_clk_hw_register_fixed_rate(chip->dev, + AC100_RTC_32K_NAME, + NULL, 0, + AC100_RTC_32K_RATE); if (IS_ERR(chip->rtc_32k_clk)) { ret = PTR_ERR(chip->rtc_32k_clk); dev_err(chip->dev, "Failed to register RTC-32k clock: %d\n", @@ -354,29 +356,14 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip) if (ret) { dev_err(chip->dev, "Failed to register clk '%s': %d\n", init.name, ret); - goto err_unregister_rtc_32k; + return ret; } chip->clk_data->hws[i] = &clk->hw; } - chip->clk_data->num = i; - ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, chip->clk_data); - if (ret) - goto err_unregister_rtc_32k; - - return 0; - -err_unregister_rtc_32k: - clk_unregister_fixed_rate(chip->rtc_32k_clk->clk); - - return ret; -} - -static void ac100_rtc_unregister_clks(struct ac100_rtc_dev *chip) -{ - of_clk_del_provider(chip->dev->of_node); - clk_unregister_fixed_rate(chip->rtc_32k_clk->clk); + return devm_of_clk_add_hw_provider(chip->dev, of_clk_hw_onecell_get, + chip->clk_data); } /* @@ -614,13 +601,6 @@ static int ac100_rtc_probe(struct platform_device *pdev) return devm_rtc_register_device(chip->rtc); } -static void ac100_rtc_remove(struct platform_device *pdev) -{ - struct ac100_rtc_dev *chip = platform_get_drvdata(pdev); - - ac100_rtc_unregister_clks(chip); -} - static const struct of_device_id ac100_rtc_match[] = { { .compatible = "x-powers,ac100-rtc" }, { }, @@ -629,7 +609,6 @@ MODULE_DEVICE_TABLE(of, ac100_rtc_match); static struct platform_driver ac100_rtc_driver = { .probe = ac100_rtc_probe, - .remove = ac100_rtc_remove, .driver = { .name = "ac100-rtc", .of_match_table = of_match_ptr(ac100_rtc_match), diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c index ece6de4a6adb..60596b0ea9d8 100644 --- a/drivers/rtc/rtc-mpfs.c +++ b/drivers/rtc/rtc-mpfs.c @@ -256,8 +256,12 @@ static int mpfs_rtc_probe(struct platform_device *pdev) return ret; } + clk = devm_clk_get(&pdev->dev, "rtcref"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + /* prescaler hardware adds 1 to reg value */ - prescaler = clk_get_rate(devm_clk_get(&pdev->dev, "rtcref")) - 1; + prescaler = clk_get_rate(clk) - 1; if (prescaler > MAX_PRESCALER_COUNT) { dev_dbg(&pdev->dev, "invalid prescaler %lu\n", prescaler); return -EINVAL; diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c index 959acff8faff..d12bccb3cf5e 100644 --- a/drivers/rtc/rtc-spear.c +++ b/drivers/rtc/rtc-spear.c @@ -363,6 +363,12 @@ static int spear_rtc_probe(struct platform_device *pdev) if (irq < 0) return irq; + config->ioaddr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(config->ioaddr)) + return PTR_ERR(config->ioaddr); + + spin_lock_init(&config->lock); + status = devm_request_irq(&pdev->dev, irq, spear_rtc_irq, 0, pdev->name, config); if (status) { @@ -371,10 +377,6 @@ static int spear_rtc_probe(struct platform_device *pdev) return status; } - config->ioaddr = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(config->ioaddr)) - return PTR_ERR(config->ioaddr); - config->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(config->clk)) return PTR_ERR(config->clk); @@ -383,7 +385,6 @@ static int spear_rtc_probe(struct platform_device *pdev) if (status < 0) return status; - spin_lock_init(&config->lock); platform_set_drvdata(pdev, config); config->rtc->ops = &spear_rtc_ops; diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c index 7106a8a4f280..6772abb483dd 100644 --- a/drivers/spi/spi-cs42l43.c +++ b/drivers/spi/spi-cs42l43.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -215,12 +216,108 @@ static size_t cs42l43_spi_max_length(struct spi_device *spi) return CS42L43_SPI_MAX_LENGTH; } +/* + * Workaround needed for two speaker ID pins in one ACPI GpioIo() but + * the Linux-specific _DSD property only contains one pin. + * Create a temporary acpi_gpio_mapping pointing at both pins. + */ +static const struct acpi_gpio_params cs42l43_2bit_speaker_id_from_one_gpioio_params[] = { + [0] = { + .crs_entry_index = 0, + .line_index = 0, + }, + [1] = { + .crs_entry_index = 0, + .line_index = 1, + }, +}; + +static const struct acpi_gpio_mapping cs42l43_2bit_speaker_id_from_one_gpioio_mapping[] = { + { + .name = "spk-id-quirk-gpios", + .data = cs42l43_2bit_speaker_id_from_one_gpioio_params, + .size = ARRAY_SIZE(cs42l43_2bit_speaker_id_from_one_gpioio_params), + }, + { } +}; + +static int cs42l43_get_2bit_speaker_id_from_one_gpioio(struct cs42l43_spi *priv, int *result) +{ + struct fwnode_reference_args args; + struct acpi_device *adev; + struct gpio_desc *desc; + u32 spkid = 0; + int i, ret; + + /* Use the _DSD property to get the node containing the GpioIo() */ + ret = fwnode_property_get_reference_args(dev_fwnode(priv->dev), "spk-id-gpios", + NULL, 3, 0, &args); + if (ret) + return ret; + + struct fwnode_handle *fwnode __free(fwnode_handle) = args.fwnode; + + /* An acpi_gpio_mapping must be added to the node that contains the GpioIo() */ + adev = to_acpi_device_node(fwnode); + if (!adev) + return -EINVAL; + + ret = acpi_dev_add_driver_gpios(adev, cs42l43_2bit_speaker_id_from_one_gpioio_mapping); + if (ret) + return ret; + + /* gpiod_get_array() can't read from a mapping in a child node */ + for (i = 0; i < ARRAY_SIZE(cs42l43_2bit_speaker_id_from_one_gpioio_params); i++) { + desc = fwnode_gpiod_get_index(fwnode, "spk-id-quirk", i, GPIOD_IN, + dev_name(priv->dev)); + if (IS_ERR(desc)) { + ret = PTR_ERR(desc); + goto out; + } + + ret = gpiod_get_value_cansleep(desc); + gpiod_put(desc); + if (ret < 0) + goto out; + + spkid |= (u32)ret << i; + } + + dev_dbg(priv->dev, "spk-id-gpios = %u\n", spkid); + *result = spkid; + ret = 0; +out: + acpi_dev_remove_driver_gpios(adev); + + return ret; +} + +static const struct dmi_system_id cs42l43_spk_id_quirks[] = { + { + .ident = "Dell XPS 13 DX13260", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0E53"), + }, + .driver_data = cs42l43_get_2bit_speaker_id_from_one_gpioio, + }, + { } +}; + static int cs42l43_get_speaker_id_gpios(struct cs42l43_spi *priv, int *result) { + const struct dmi_system_id *dmi_id; struct gpio_descs *descs; u32 spkid; int i, ret; + dmi_id = dmi_first_match(cs42l43_spk_id_quirks); + if (dmi_id) { + int (*get_speaker_id)(struct cs42l43_spi *priv, int *result) = dmi_id->driver_data; + + return get_speaker_id(priv, result); + } + descs = gpiod_get_array_optional(priv->dev, "spk-id", GPIOD_IN); if (!descs) return 0; diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 7d060b4cd33d..2d95d4a30c4e 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -948,7 +948,8 @@ static int gb_tty_init(void) int retval = 0; gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV); + TTY_DRIVER_DYNAMIC_DEV | + TTY_DRIVER_RESET_SAVED_TERMIOS); if (IS_ERR(gb_tty_driver)) { pr_err("Can not allocate tty driver\n"); retval = -ENOMEM; diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index cd47b627f97b..965988b18608 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -73,7 +73,6 @@ struct tb_ctl { #define tb_ctl_dbg_once(ctl, format, arg...) \ dev_dbg_once((ctl)->nhi->dev, format, ## arg) -static DECLARE_WAIT_QUEUE_HEAD(tb_cfg_request_cancel_queue); /* Serializes access to request kref_get/put */ static DEFINE_MUTEX(tb_cfg_request_lock); @@ -133,41 +132,42 @@ void tb_cfg_request_put(struct tb_cfg_request *req) static int tb_cfg_request_enqueue(struct tb_ctl *ctl, struct tb_cfg_request *req) { + tb_cfg_request_get(req); + WARN_ON(test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags)); WARN_ON(req->ctl); - mutex_lock(&ctl->request_queue_lock); + guard(mutex)(&ctl->request_queue_lock); if (!ctl->running) { - mutex_unlock(&ctl->request_queue_lock); + tb_cfg_request_put(req); return -ENOTCONN; } req->ctl = ctl; list_add_tail(&req->list, &ctl->request_queue); set_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); - mutex_unlock(&ctl->request_queue_lock); return 0; } -static void tb_cfg_request_dequeue(struct tb_cfg_request *req) +static bool tb_cfg_request_is_active(struct tb_cfg_request *req) { - struct tb_ctl *ctl = req->ctl; - - mutex_lock(&ctl->request_queue_lock); - if (!test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags)) { - mutex_unlock(&ctl->request_queue_lock); - return; - } + return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); +} - list_del(&req->list); - clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); - if (test_bit(TB_CFG_REQUEST_CANCELED, &req->flags)) - wake_up(&tb_cfg_request_cancel_queue); - mutex_unlock(&ctl->request_queue_lock); +static bool tb_cfg_request_is_canceled(struct tb_cfg_request *req) +{ + return test_bit(TB_CFG_REQUEST_CANCELED, &req->flags); } -static bool tb_cfg_request_is_active(struct tb_cfg_request *req) +static void tb_cfg_request_dequeue(struct tb_cfg_request *req) { - return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); + struct tb_ctl *ctl = req->ctl; + + guard(mutex)(&ctl->request_queue_lock); + if (tb_cfg_request_is_active(req)) { + list_del(&req->list); + clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); + tb_cfg_request_put(req); + } } static struct tb_cfg_request * @@ -178,7 +178,7 @@ tb_cfg_request_find(struct tb_ctl *ctl, struct ctl_pkg *pkg) mutex_lock(&pkg->ctl->request_queue_lock); list_for_each_entry(iter, &pkg->ctl->request_queue, list) { tb_cfg_request_get(iter); - if (iter->match(iter, pkg)) { + if (!tb_cfg_request_is_canceled(iter) && iter->match(iter, pkg)) { req = iter; break; } @@ -512,8 +512,11 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame, trace_tb_rx(pkg->ctl->index, frame->eof, pkg->buffer, frame->size, !req); if (req) { - if (req->copy(req, pkg)) - schedule_work(&req->work); + scoped_guard(mutex, &pkg->ctl->request_queue_lock) { + if (!tb_cfg_request_is_canceled(req) && + req->copy(req, pkg)) + schedule_work(&req->work); + } tb_cfg_request_put(req); } @@ -525,11 +528,10 @@ static void tb_cfg_request_work(struct work_struct *work) { struct tb_cfg_request *req = container_of(work, typeof(*req), work); - if (!test_bit(TB_CFG_REQUEST_CANCELED, &req->flags)) + if (!tb_cfg_request_is_canceled(req)) req->callback(req->callback_data); tb_cfg_request_dequeue(req); - tb_cfg_request_put(req); } /** @@ -555,10 +557,9 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, INIT_WORK(&req->work, tb_cfg_request_work); INIT_LIST_HEAD(&req->list); - tb_cfg_request_get(req); ret = tb_cfg_request_enqueue(ctl, req); if (ret) - goto err_put; + return ret; ret = tb_ctl_tx(ctl, req->request, req->request_size, req->request_type); @@ -572,9 +573,6 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, err_dequeue: tb_cfg_request_dequeue(req); -err_put: - tb_cfg_request_put(req); - return ret; } @@ -588,9 +586,10 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, */ void tb_cfg_request_cancel(struct tb_cfg_request *req, int err) { - set_bit(TB_CFG_REQUEST_CANCELED, &req->flags); - schedule_work(&req->work); - wait_event(tb_cfg_request_cancel_queue, !tb_cfg_request_is_active(req)); + scoped_guard(mutex, &req->ctl->request_queue_lock) + set_bit(TB_CFG_REQUEST_CANCELED, &req->flags); + cancel_work_sync(&req->work); + tb_cfg_request_dequeue(req); req->result.err = err; } diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c index 9f7914ac2f48..96b7fe0386e3 100644 --- a/drivers/thunderbolt/quirks.c +++ b/drivers/thunderbolt/quirks.c @@ -99,6 +99,9 @@ static const struct tb_quirk tb_quirks[] = { quirk_usb3_maximum_bandwidth }, { 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_40G_BRIDGE, 0x0000, 0x0000, quirk_usb3_maximum_bandwidth }, + /* CLx is unstable on the Anker Prime TB5 dock (DROM 01ea:83b5) */ + { 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_80G_BRIDGE, 0x01ea, 0x83b5, + quirk_clx_disable }, /* * Block Runtime PM in DP redrive mode for Intel Barlow Ridge host * controllers. diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index a830c82bb905..217e18f8a7c7 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -765,6 +765,7 @@ static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid, { int port_max_hopid; struct ida *ida; + int ret; if (in) { port_max_hopid = port->config.max_in_hop_id; @@ -784,7 +785,11 @@ static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid, if (max_hopid < 0 || max_hopid > port_max_hopid) max_hopid = port_max_hopid; - return ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL); + ret = ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL); + if (ret >= 0) + tb_switch_get(port->sw); + + return ret; } /** @@ -823,6 +828,7 @@ int tb_port_alloc_out_hopid(struct tb_port *port, int min_hopid, int max_hopid) void tb_port_release_in_hopid(struct tb_port *port, int hopid) { ida_free(&port->in_hopids, hopid); + tb_switch_put(port->sw); } /** @@ -833,6 +839,7 @@ void tb_port_release_in_hopid(struct tb_port *port, int hopid) void tb_port_release_out_hopid(struct tb_port *port, int hopid) { ida_free(&port->out_hopids, hopid); + tb_switch_put(port->sw); } static inline bool tb_switch_is_reachable(const struct tb_switch *parent, diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index f43f2d952372..7e1f8c1747a9 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -89,6 +89,7 @@ static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port, const char *reason); static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port, int retry, unsigned long delay); +static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data); static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug) { @@ -385,7 +386,8 @@ static void tb_switch_discover_tunnels(struct tb_switch *sw, switch (port->config.type) { case TB_TYPE_DP_HDMI_IN: - tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids); + tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids, + tb_dp_tunnel_active, tb); tb_increase_tmu_accuracy(tunnel); break; @@ -1910,6 +1912,18 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data) struct tb *tb = data; mutex_lock(&tb->lock); + + /* + * If the DPRX read was canceled the tunnel is already being torn + * down by whoever canceled it. Do not touch the adapters here + * because the routers may be gone by now. + */ + if (tunnel->dprx_canceled) { + tb_tunnel_dbg(tunnel, "DPRX read canceled, not activating\n"); + mutex_unlock(&tb->lock); + return; + } + if (tb_tunnel_is_active(tunnel)) { int consumed_up, consumed_down, ret; @@ -1964,8 +1978,6 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data) tb_dp_resource_unavailable(tb, in, "DPRX negotiation failed"); } mutex_unlock(&tb->lock); - - tb_domain_put(tb); } static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, @@ -2026,8 +2038,7 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, available_up, available_down); tunnel = tb_tunnel_alloc_dp(tb, in, out, link_nr, available_up, - available_down, tb_dp_tunnel_active, - tb_domain_get(tb)); + available_down, tb_dp_tunnel_active, tb); if (!tunnel) { tb_port_dbg(out, "could not allocate DP tunnel\n"); goto err_reclaim_usb; @@ -2048,7 +2059,6 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in, tb_tunnel_put(tunnel); err_reclaim_usb: tb_reclaim_usb3_bandwidth(tb, in, out); - tb_domain_put(tb); err_detach_group: tb_detach_bandwidth_group(in); err_dealloc_dp: @@ -2948,11 +2958,12 @@ static void tb_stop(struct tb *tb) /* tunnels are only present after everything has been initialized */ list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) { /* - * DMA tunnels require the driver to be functional so we - * tear them down. Other protocol tunnels can be left - * intact. + * DMA tunnels and DP tunnels which are not yet active require + * the driver to be functional so we tear them down. + * Other protocol tunnels can be left intact. */ - if (tb_tunnel_is_dma(tunnel)) + if (tb_tunnel_is_dma(tunnel) || + (tb_tunnel_is_dp(tunnel) && !tb_tunnel_is_active(tunnel))) tb_tunnel_deactivate(tunnel); tb_tunnel_put(tunnel); } @@ -3253,11 +3264,11 @@ static void tb_remove_work(struct work_struct *work) struct tb *tb = tcm_to_tb(tcm); mutex_lock(&tb->lock); - if (tb->root_switch) + if (tb->root_switch) { tb_free_unplugged_children(tb->root_switch); + tb_free_unplugged_xdomains(tb->root_switch); + } mutex_unlock(&tb->lock); - - tb_free_unplugged_xdomains(tb->root_switch); } static int tb_runtime_resume(struct tb *tb) diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c index 05652ee82fbf..c8c648f31107 100644 --- a/drivers/thunderbolt/test.c +++ b/drivers/thunderbolt/test.c @@ -33,6 +33,18 @@ static void kunit_ida_init(struct kunit *test, struct ida *ida) kunit_alloc_resource(test, __ida_init, __ida_destroy, GFP_KERNEL, ida); } +static void tb_test_switch_release(struct device *dev) +{ + /* The memory is owned by KUnit, nothing to do here */ +} + +static void tb_test_switch_put(void *data) +{ + struct tb_switch *sw = data; + + put_device(&sw->dev); +} + static struct tb_switch *alloc_switch(struct kunit *test, u64 route, u8 upstream_port, u8 max_port_number) { @@ -44,6 +56,15 @@ static struct tb_switch *alloc_switch(struct kunit *test, u64 route, if (!sw) return NULL; + /* + * HopID allocations take a reference to their routers and those devices + * have to be initialized for that to work. + */ + sw->dev.release = tb_test_switch_release; + device_initialize(&sw->dev); + if (kunit_add_action_or_reset(test, tb_test_switch_put, sw)) + return NULL; + sw->config.upstream_port_number = upstream_port; sw->config.depth = tb_route_length(route); sw->config.route_hi = upper_32_bits(route); @@ -1386,6 +1407,10 @@ static void tb_test_tunnel_pcie(struct kunit *test) tb_tunnel_put(tunnel1); } +static void tb_test_dp_tunnel_active(struct tb_tunnel *tunnel, void *data) +{ +} + static void tb_test_tunnel_dp(struct kunit *test) { struct tb_switch *host, *dev; @@ -1406,7 +1431,8 @@ static void tb_test_tunnel_dp(struct kunit *test) in = &host->ports[5]; out = &dev->ports[13]; - tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); + tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, + tb_test_dp_tunnel_active, NULL); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in); @@ -1452,7 +1478,8 @@ static void tb_test_tunnel_dp_chain(struct kunit *test) in = &host->ports[5]; out = &dev4->ports[14]; - tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); + tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, + tb_test_dp_tunnel_active, NULL); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in); @@ -1502,7 +1529,8 @@ static void tb_test_tunnel_dp_tree(struct kunit *test) in = &dev2->ports[13]; out = &dev5->ports[13]; - tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); + tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, + tb_test_dp_tunnel_active, NULL); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in); @@ -1567,7 +1595,8 @@ static void tb_test_tunnel_dp_max_length(struct kunit *test) in = &dev6->ports[13]; out = &dev12->ports[13]; - tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); + tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, + tb_test_dp_tunnel_active, NULL); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in); @@ -1637,7 +1666,8 @@ static void tb_test_tunnel_3dp(struct kunit *test) out2 = &dev5->ports[13]; out3 = &dev4->ports[14]; - tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0, NULL, NULL); + tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0, + tb_test_dp_tunnel_active, NULL); KUNIT_ASSERT_TRUE(test, tunnel1 != NULL); KUNIT_EXPECT_EQ(test, tunnel1->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel1->src_port, in1); @@ -1645,7 +1675,8 @@ static void tb_test_tunnel_3dp(struct kunit *test) KUNIT_ASSERT_EQ(test, tunnel1->npaths, 3); KUNIT_ASSERT_EQ(test, tunnel1->paths[0]->path_length, 3); - tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0, NULL, NULL); + tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0, + tb_test_dp_tunnel_active, NULL); KUNIT_ASSERT_TRUE(test, tunnel2 != NULL); KUNIT_EXPECT_EQ(test, tunnel2->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel2->src_port, in2); @@ -1653,7 +1684,8 @@ static void tb_test_tunnel_3dp(struct kunit *test) KUNIT_ASSERT_EQ(test, tunnel2->npaths, 3); KUNIT_ASSERT_EQ(test, tunnel2->paths[0]->path_length, 4); - tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0, NULL, NULL); + tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0, + tb_test_dp_tunnel_active, NULL); KUNIT_ASSERT_TRUE(test, tunnel3 != NULL); KUNIT_EXPECT_EQ(test, tunnel3->type, TB_TUNNEL_DP); KUNIT_EXPECT_PTR_EQ(test, tunnel3->src_port, in3); @@ -1751,7 +1783,8 @@ static void tb_test_tunnel_port_on_path(struct kunit *test) in = &dev2->ports[13]; out = &dev5->ports[13]; - dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); + dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, + tb_test_dp_tunnel_active, NULL); KUNIT_ASSERT_NOT_NULL(test, dp_tunnel); KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, in)); @@ -2183,7 +2216,8 @@ static void tb_test_credit_alloc_dp(struct kunit *test) in = &host->ports[5]; out = &dev->ports[14]; - tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); + tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, + tb_test_dp_tunnel_active, NULL); KUNIT_ASSERT_NOT_NULL(test, tunnel); KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)3); @@ -2419,7 +2453,8 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL1(struct kunit *test, in = &host->ports[5]; out = &dev->ports[13]; - dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); + dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, + tb_test_dp_tunnel_active, NULL); KUNIT_ASSERT_NOT_NULL(test, dp_tunnel1); KUNIT_ASSERT_EQ(test, dp_tunnel1->npaths, (size_t)3); @@ -2456,7 +2491,8 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL2(struct kunit *test, in = &host->ports[6]; out = &dev->ports[14]; - dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL); + dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, + tb_test_dp_tunnel_active, NULL); KUNIT_ASSERT_NOT_NULL(test, dp_tunnel2); KUNIT_ASSERT_EQ(test, dp_tunnel2->npaths, (size_t)3); diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index b7f32305f14a..3785e29cf92b 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -507,6 +507,7 @@ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down, goto err_deactivate; } + tb_tunnel_set_active(tunnel, true); tb_tunnel_dbg(tunnel, "discovered\n"); return tunnel; @@ -1090,8 +1091,14 @@ static void tb_dp_dprx_work(struct work_struct *work) struct tb_tunnel *tunnel = container_of(work, typeof(*tunnel), dprx_work.work); struct tb *tb = tunnel->tb; + /* + * The DPRX read can be canceled while this work is waiting for + * tb->lock. Check the flag only once it is held: while the lock is + * held the tunnel cannot be torn down under us and the adapters are + * safe to access. + */ + mutex_lock(&tb->lock); if (!tunnel->dprx_canceled) { - mutex_lock(&tb->lock); if (tb_dp_is_usb4(tunnel->src_port->sw) && tb_dp_wait_dprx(tunnel, TB_DPRX_WAIT_TIMEOUT)) { if (ktime_before(ktime_get(), tunnel->dprx_timeout)) { @@ -1103,41 +1110,42 @@ static void tb_dp_dprx_work(struct work_struct *work) } else { tb_tunnel_set_active(tunnel, true); } - mutex_unlock(&tb->lock); } + mutex_unlock(&tb->lock); - if (tunnel->callback) - tunnel->callback(tunnel, tunnel->callback_data); + tunnel->callback(tunnel, tunnel->callback_data); tb_tunnel_put(tunnel); + tb_domain_put(tb); } static int tb_dp_dprx_start(struct tb_tunnel *tunnel) { /* - * Bump up the reference to keep the tunnel around. It will be - * dropped in tb_dp_dprx_stop() once the tunnel is deactivated. + * Bump up the references to keep the tunnel and the domain around + * until the work has run or has been canceled. */ tb_tunnel_get(tunnel); + tb_domain_get(tunnel->tb); tunnel->dprx_started = true; + tunnel->dprx_canceled = false; + tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout); + queue_delayed_work(tunnel->tb->wq, &tunnel->dprx_work, 0); - if (tunnel->callback) { - tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout); - queue_delayed_work(tunnel->tb->wq, &tunnel->dprx_work, 0); - return -EINPROGRESS; - } - - return tb_dp_is_usb4(tunnel->src_port->sw) ? - tb_dp_wait_dprx(tunnel, dprx_timeout) : 0; + return -EINPROGRESS; } static void tb_dp_dprx_stop(struct tb_tunnel *tunnel) { + struct tb *tb = tunnel->tb; + if (tunnel->dprx_started) { tunnel->dprx_started = false; tunnel->dprx_canceled = true; - if (cancel_delayed_work(&tunnel->dprx_work)) + if (cancel_delayed_work(&tunnel->dprx_work)) { tb_tunnel_put(tunnel); + tb_domain_put(tb); + } } } @@ -1579,20 +1587,28 @@ static void tb_dp_dump(struct tb_tunnel *tunnel) * @tb: Pointer to the domain structure * @in: DP in adapter * @alloc_hopid: Allocate HopIDs from visited ports + * @callback: Callback that is called when the DP tunnel is fully + * activated (or there is an error) + * @callback_data: Data for @callback * * If @in adapter is active, follows the tunnel to the DP out adapter * and back. Returns the discovered tunnel or %NULL if there was no - * tunnel. + * tunnel. See tb_tunnel_alloc_dp() for @callback. * * Return: Pointer to &struct tb_tunnel or %NULL if no tunnel found. */ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, - bool alloc_hopid) + bool alloc_hopid, + void (*callback)(struct tb_tunnel *, void *), + void *callback_data) { struct tb_tunnel *tunnel; struct tb_port *port; struct tb_path *path; + if (WARN_ON(!callback)) + return NULL; + if (!tb_dp_port_is_enabled(in)) return NULL; @@ -1608,6 +1624,9 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, tunnel->alloc_bandwidth = tb_dp_alloc_bandwidth; tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth; tunnel->src_port = in; + tunnel->callback = callback; + tunnel->callback_data = callback_data; + INIT_DELAYED_WORK(&tunnel->dprx_work, tb_dp_dprx_work); path = tb_path_discover(in, TB_DP_VIDEO_HOPID, NULL, -1, &tunnel->dst_port, "Video", alloc_hopid); @@ -1653,6 +1672,7 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, tb_dp_dump(tunnel); + tb_tunnel_set_active(tunnel, true); tb_tunnel_dbg(tunnel, "discovered\n"); return tunnel; @@ -1674,16 +1694,16 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, * %0 if no available bandwidth. * @max_down: Maximum available downstream bandwidth for the DP tunnel. * %0 if no available bandwidth. - * @callback: Optional callback that is called when the DP tunnel is - * fully activated (or there is an error) - * @callback_data: Optional data for @callback + * @callback: Callback that is called when the DP tunnel is fully + * activated (or there is an error) + * @callback_data: Data for @callback * * Allocates a tunnel between @in and @out that is capable of tunneling - * Display Port traffic. If @callback is not %NULL it will be called - * after tb_tunnel_activate() once the tunnel has been fully activated. - * It can call tb_tunnel_is_active() to check if activation was - * successful (or if it returns %false there was some sort of issue). - * The @callback is called without @tb->lock held. + * Display Port traffic. The @callback is called after tb_tunnel_activate() + * once the tunnel has been fully activated. It can call + * tb_tunnel_is_active() to check if activation was successful (or if it + * returns %false there was some sort of issue). The @callback is called + * without @tb->lock held. * * Return: Pointer to @struct tb_tunnel or %NULL in case of failure. */ @@ -1698,7 +1718,7 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, struct tb_path *path; bool pm_support; - if (WARN_ON(!in->cap_adap || !out->cap_adap)) + if (WARN_ON(!in->cap_adap || !out->cap_adap || !callback)) return NULL; tunnel = tb_tunnel_alloc(tb, 3, TB_TUNNEL_DP); @@ -2281,6 +2301,7 @@ struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down, tb_usb3_reclaim_available_bandwidth; } + tb_tunnel_set_active(tunnel, true); tb_tunnel_dbg(tunnel, "discovered\n"); return tunnel; diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h index 4878763a82b3..7d1d255ab5a7 100644 --- a/drivers/thunderbolt/tunnel.h +++ b/drivers/thunderbolt/tunnel.h @@ -66,8 +66,8 @@ enum tb_tunnel_state { * @dprx_canceled: Was DPRX capabilities read poll canceled * @dprx_timeout: If set DPRX capabilities read poll work will timeout after this passes * @dprx_work: Worker that is scheduled to poll completion of DPRX capabilities read - * @callback: Optional callback called when DP tunnel is fully activated - * @callback_data: Optional data for @callback + * @callback: Callback called when DP tunnel is fully activated + * @callback_data: Data for @callback * @paths: All paths required by the tunnel */ struct tb_tunnel { @@ -117,7 +117,9 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up, bool tb_tunnel_reserved_pci(struct tb_port *port, int *reserved_up, int *reserved_down); struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, - bool alloc_hopid); + bool alloc_hopid, + void (*callback)(struct tb_tunnel *, void *), + void *callback_data); struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, struct tb_port *out, int link_nr, int max_up, int max_down, diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 86b2f7474670..ea7b6f8e2a29 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -377,6 +377,10 @@ static int tb_xdp_properties_request(struct tb_ctl *ctl, u64 route, len += sizeof(res->hdr.xd_hdr) / 4; len -= sizeof(*res) / 4; + if (len > TB_XDP_PROPERTIES_MAX_DATA_LENGTH) { + ret = -EINVAL; + goto err; + } if (res->offset != req.offset) { ret = -EINVAL; diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 81eaca751541..33cb3b476747 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1138,11 +1138,6 @@ static int rs_ioctl(struct tty_struct *tty, local_irq_save(flags); cnow = info->icount; /* atomic copy */ local_irq_restore(flags); - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { - ret = -EIO; /* no change => error */ - break; - } if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || @@ -1150,6 +1145,10 @@ static int rs_ioctl(struct tty_struct *tty, ret = 0; break; } + if (tty_io_error(tty)) { + ret = -EIO; + break; + } schedule(); /* see if a signal did it */ if (signal_pending(current)) { diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index c13e050de83b..e55779ff0af2 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -454,14 +454,13 @@ static const u8 gsm_fcs8[256] = { static void gsm_dlci_close(struct gsm_dlci *dlci); static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len); -static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk); +static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk, bool wait); static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, u8 ctrl); static int gsm_send_packet(struct gsm_mux *gsm, struct gsm_msg *msg); static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr); static void gsmld_write_trigger(struct gsm_mux *gsm); static void gsmld_write_task(struct work_struct *work); -static int gsm_modem_send_initial_msc(struct gsm_dlci *dlci); /** * gsm_fcs_add - update FCS @@ -2174,7 +2173,7 @@ static void gsm_dlci_open(struct gsm_dlci *dlci) pr_debug("DLCI %d goes open.\n", dlci->addr); /* Send current modem state */ if (dlci->addr) { - gsm_modem_send_initial_msc(dlci); + gsm_modem_update(dlci, 0, false); } else { /* Start keep-alive control */ gsm->ka_num = 0; @@ -4136,9 +4135,10 @@ static void gsm_modem_upd_via_data(struct gsm_dlci *dlci, u8 brk) * gsm_modem_upd_via_msc - send modem bits via control frame * @dlci: channel * @brk: break signal + * @wait: wait for the MSC command */ -static int gsm_modem_upd_via_msc(struct gsm_dlci *dlci, u8 brk) +static int gsm_modem_upd_via_msc(struct gsm_dlci *dlci, u8 brk, bool wait) { u8 modembits[3]; struct gsm_control *ctrl; @@ -4155,41 +4155,22 @@ static int gsm_modem_upd_via_msc(struct gsm_dlci *dlci, u8 brk) modembits[2] = (brk << 4) | 2 | EA; /* Length, Break, EA */ len++; } + if (!wait) + return gsm_control_command(dlci->gsm, CMD_MSC, modembits, len); ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len); if (ctrl == NULL) return -ENOMEM; return gsm_control_wait(dlci->gsm, ctrl); } -/** - * gsm_modem_send_initial_msc - Send initial modem status message - * - * @dlci: channel - * - * Send an initial MSC message after DLCI open to set the initial - * modem status lines. This is only done for basic mode. - * Does not wait for a response as we cannot block the input queue - * processing. - */ -static int gsm_modem_send_initial_msc(struct gsm_dlci *dlci) -{ - u8 modembits[2]; - - if (dlci->adaption != 1 || dlci->gsm->encoding != GSM_BASIC_OPT) - return 0; - - modembits[0] = (dlci->addr << 2) | 2 | EA; /* DLCI, Valid, EA */ - modembits[1] = (gsm_encode_modem(dlci) << 1) | EA; - return gsm_control_command(dlci->gsm, CMD_MSC, (const u8 *)&modembits, 2); -} - /** * gsm_modem_update - send modem status line state * @dlci: channel * @brk: break signal + * @wait: wait for the MSC command */ -static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk) +static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk, bool wait) { if (dlci->gsm->dead) return -EL2HLT; @@ -4199,7 +4180,7 @@ static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk) return 0; } else if (dlci->gsm->encoding == GSM_BASIC_OPT) { /* Send as MSC control message. */ - return gsm_modem_upd_via_msc(dlci, brk); + return gsm_modem_upd_via_msc(dlci, brk, wait); } /* Modem status lines are not supported. */ @@ -4265,7 +4246,7 @@ static void gsm_dtr_rts(struct tty_port *port, bool active) modem_tx &= ~(TIOCM_DTR | TIOCM_RTS); if (modem_tx != dlci->modem_tx) { dlci->modem_tx = modem_tx; - gsm_modem_update(dlci, 0); + gsm_modem_update(dlci, 0, true); } } @@ -4471,7 +4452,7 @@ static int gsmtty_tiocmset(struct tty_struct *tty, if (modem_tx != dlci->modem_tx) { dlci->modem_tx = modem_tx; - return gsm_modem_update(dlci, 0); + return gsm_modem_update(dlci, 0, true); } return 0; } @@ -4553,7 +4534,7 @@ static void gsmtty_throttle(struct tty_struct *tty) dlci->modem_tx &= ~TIOCM_RTS; dlci->throttled = true; /* Send an MSC with RTS cleared */ - gsm_modem_update(dlci, 0); + gsm_modem_update(dlci, 0, true); } static void gsmtty_unthrottle(struct tty_struct *tty) @@ -4565,7 +4546,7 @@ static void gsmtty_unthrottle(struct tty_struct *tty) dlci->modem_tx |= TIOCM_RTS; dlci->throttled = false; /* Send an MSC with RTS set */ - gsm_modem_update(dlci, 0); + gsm_modem_update(dlci, 0, true); } static int gsmtty_break_ctl(struct tty_struct *tty, int state) @@ -4583,7 +4564,7 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state) if (encode > 0x0F) encode = 0x0F; /* Best effort */ } - return gsm_modem_update(dlci, encode); + return gsm_modem_update(dlci, encode, true); } static void gsmtty_cleanup(struct tty_struct *tty) diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c index 742004d63c6f..60c7d5c9100c 100644 --- a/drivers/tty/serial/8250/8250_bcm7271.c +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -1123,8 +1123,8 @@ static void brcmuart_remove(struct platform_device *pdev) struct brcmuart_priv *priv = platform_get_drvdata(pdev); debugfs_remove_recursive(priv->debugfs_dir); - hrtimer_cancel(&priv->hrt); serial8250_unregister_port(priv->line); + hrtimer_cancel(&priv->hrt); brcmuart_free_bufs(&pdev->dev, priv); if (priv->dma_enabled) brcmuart_arbitration(priv, 0); diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 3c7775df27ef..ceecb39fb82d 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -711,12 +711,6 @@ static int omap_8250_startup(struct uart_port *port) struct uart_8250_dma *dma = &priv->omap8250_dma; int ret; - if (priv->wakeirq) { - ret = dev_pm_set_dedicated_wake_irq(port->dev, priv->wakeirq); - if (ret) - return ret; - } - #ifdef CONFIG_PM up->capabilities |= UART_CAP_RPM; #endif @@ -787,7 +781,6 @@ static void omap_8250_shutdown(struct uart_port *port) } disable_irq_nosync(port->irq); - dev_pm_clear_wake_irq(port->dev); serial8250_release_dma(up); up->dma = NULL; @@ -1588,6 +1581,12 @@ static int omap8250_probe(struct platform_device *pdev) priv->wakeirq = irq_of_parse_and_map(np, 1); + if (priv->wakeirq) { + ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, priv->wakeirq); + if (ret) + goto err; + } + ret = serial8250_register_8250_port(&up); if (ret < 0) { dev_err(&pdev->dev, "unable to register 8250 port\n"); @@ -1603,6 +1602,7 @@ static int omap8250_probe(struct platform_device *pdev) return 0; err: + dev_pm_clear_wake_irq(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_sync(&pdev->dev); flush_work(&priv->qos_work); @@ -1625,6 +1625,7 @@ static void omap8250_remove(struct platform_device *pdev) omap_8250_shutdown(&up->port); serial8250_unregister_port(priv->line); priv->line = -ENODEV; + dev_pm_clear_wake_irq(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_sync(&pdev->dev); flush_work(&priv->qos_work); diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 5a955c80a853..e2c8e5ec688c 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -188,6 +188,11 @@ static void cleanup_kgdboc(void) kgdboc_unregister_kbd(); kgdb_unregister_io_module(&kgdboc_io_ops); + + if (kgdb_tty_driver) { + tty_driver_kref_put(kgdb_tty_driver); + kgdb_tty_driver = NULL; + } } static int configure_kgdboc(void) @@ -254,6 +259,10 @@ static int configure_kgdboc(void) noconfig: kgdboc_unregister_kbd(); + if (kgdb_tty_driver) { + tty_driver_kref_put(kgdb_tty_driver); + kgdb_tty_driver = NULL; + } configured = 0; return err; diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c index 920fe7ff5083..0809655f4395 100644 --- a/drivers/tty/serial/ma35d1_serial.c +++ b/drivers/tty/serial/ma35d1_serial.c @@ -728,7 +728,7 @@ static int ma35d1serial_probe(struct platform_device *pdev) ret = clk_prepare_enable(up->clk); if (ret) - goto err_iounmap; + goto err_put_clk; if (up->port.line != 0) up->port.uartclk = clk_get_rate(up->clk); @@ -755,6 +755,9 @@ static int ma35d1serial_probe(struct platform_device *pdev) err_clk_disable: clk_disable_unprepare(up->clk); +err_put_clk: + clk_put(up->clk); + err_iounmap: iounmap(up->port.membase); return ret; @@ -770,6 +773,7 @@ static void ma35d1serial_remove(struct platform_device *dev) uart_remove_one_port(&ma35d1serial_reg, port); clk_disable_unprepare(up->clk); + clk_put(up->clk); } static int ma35d1serial_suspend(struct platform_device *dev, pm_message_t state) diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 44b745fa26c6..e8027c09c619 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -535,11 +535,11 @@ static void max3100_shutdown(struct uart_port *port) timer_delete_sync(&s->timer); if (s->workqueue) { + free_irq(port->irq, s); + cancel_work_sync(&s->work); destroy_workqueue(s->workqueue); s->workqueue = NULL; } - if (port->irq) - free_irq(port->irq, s); /* set shutdown mode to save power */ max3100_sr(s, MAX3100_WC | MAX3100_SHDN, &rx); @@ -752,6 +752,15 @@ static void max3100_remove(struct spi_device *spi) if (max3100s[i] == s) { dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i); uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port); + + s->force_end_work = 1; + timer_shutdown_sync(&s->timer); + if (s->workqueue) { + free_irq(s->port.irq, s); + cancel_work_sync(&s->work); + destroy_workqueue(s->workqueue); + s->workqueue = NULL; + } kfree(max3100s[i]); max3100s[i] = NULL; break; diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 1fc869ad84bd..88f9ba092da0 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1312,9 +1312,8 @@ static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud) * Bump up BW vote on CPU and CORE path as driver supports FIFO mode * only. */ - avg_bw_core = (baud > 115200) ? Bps_to_icc(CORE_2X_50_MHZ) - : GENI_DEFAULT_BW; - port->se.icc_paths[GENI_TO_CORE].avg_bw = avg_bw_core; + avg_bw_core = baud > 115200 ? CORE_2X_50_MHZ : CORE_2X_19_2_MHZ; + port->se.icc_paths[GENI_TO_CORE].avg_bw = Bps_to_icc(avg_bw_core); port->se.icc_paths[CPU_TO_GENI].avg_bw = Bps_to_icc(baud); geni_icc_set_bw(&port->se); diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 9b152ead050f..cb40642bcfe8 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -216,6 +216,8 @@ #define SC16IS7XX_TLR_TX_TRIGGER(words) ((((words) / 4) & 0x0f) << 0) #define SC16IS7XX_TLR_RX_TRIGGER(words) ((((words) / 4) & 0x0f) << 4) +#define SC16IS7XX_TX_TRIGGER_LEVEL 8 + /* IOControl register bits (Only 75x/76x) */ #define SC16IS7XX_IOCONTROL_LATCH_BIT BIT(0) /* Enable input latching */ #define SC16IS7XX_IOCONTROL_MODEM_A_BIT BIT(1) /* Enable GPIO[7:4] as modem A pins */ @@ -647,12 +649,26 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, tty_flip_buffer_push(&port->state->port); } +static unsigned int sc16is7xx_txlvl(struct uart_port *port) +{ + unsigned int txlvl; + + txlvl = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); + if (txlvl > SC16IS7XX_FIFO_SIZE) { + dev_err_ratelimited(port->dev, + "chip reports %u free bytes in TX FIFO, but it only has %u\n", + txlvl, SC16IS7XX_FIFO_SIZE); + return 0; + } + + return txlvl; +} + static void sc16is7xx_handle_tx(struct uart_port *port) { struct tty_port *tport = &port->state->port; unsigned long flags; unsigned int txlen; - unsigned char *tail; if (unlikely(port->x_char)) { sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char); @@ -669,17 +685,28 @@ static void sc16is7xx_handle_tx(struct uart_port *port) } /* Limit to space available in TX FIFO */ - txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); - if (txlen > SC16IS7XX_FIFO_SIZE) { - dev_err_ratelimited(port->dev, - "chip reports %d free bytes in TX fifo, but it only has %d", - txlen, SC16IS7XX_FIFO_SIZE); - txlen = 0; - } + txlen = sc16is7xx_txlvl(port); + + /* Handle circular buffer wrap-around by sending multiple segments */ + while (txlen > 0 && !kfifo_is_empty(&tport->xmit_fifo)) { + unsigned char *tail; + unsigned int to_send; + + to_send = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen); + if (!to_send) + break; - txlen = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen); - sc16is7xx_fifo_write(port, tail, txlen); - uart_xmit_advance(port, txlen); + sc16is7xx_fifo_write(port, tail, to_send); + uart_xmit_advance(port, to_send); + + if (kfifo_is_empty(&tport->xmit_fifo)) + break; + + /* Refill below the trigger to enable the next THRI crossing. */ + txlen = sc16is7xx_txlvl(port); + if (txlen < SC16IS7XX_TX_TRIGGER_LEVEL) + break; + } uart_port_lock_irqsave(port, &flags); if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) @@ -1130,6 +1157,10 @@ static int sc16is7xx_startup(struct uart_port *port) SC16IS7XX_TCR_RX_RESUME(24) | SC16IS7XX_TCR_RX_HALT(48)); + /* Sync hardware and software TX trigger levels */ + sc16is7xx_port_write(port, SC16IS7XX_TLR_REG, + SC16IS7XX_TLR_TX_TRIGGER(SC16IS7XX_TX_TRIGGER_LEVEL)); + /* Disable TCR/TLR access */ sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, SC16IS7XX_MCR_TCRTLR_BIT, 0); diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 8004fc00fb9c..b0fc4ae3686f 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -306,11 +306,19 @@ static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits) { unsigned long fcr = tup->fcr_shadow; unsigned int lsr, tmout = 10000; + bool clear_tx = !!(fcr_bits & UART_FCR_CLEAR_XMIT); if (tup->rts_active) set_rts(tup, false); - if (tup->cdata->allow_txfifo_reset_fifo_mode) { + /* + * Leaving FIFO mode below is a workaround for a Tegra30 + * restriction on clearing the Tx FIFO while FIFO mode is + * enabled. It empties both FIFOs, so applying it to an + * Rx-only reset would destroy an in-flight transmission. + * Only take that path when the caller asked for CLEAR_XMIT. + */ + if (tup->cdata->allow_txfifo_reset_fifo_mode || !clear_tx) { fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); tegra_uart_write(tup, fcr, UART_FCR); } else { @@ -335,9 +343,15 @@ static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits) */ tegra_uart_wait_cycle_time(tup, 32); + /* + * Only wait for the transmitter to drain when the Tx FIFO was + * part of the reset. For an Rx-only reset it is left intact, + * and waiting for TEMT here would spin for a full frame time. + */ do { lsr = tegra_uart_read(tup, UART_LSR); - if ((lsr & UART_LSR_TEMT) && !(lsr & UART_LSR_DR)) + if ((!clear_tx || (lsr & UART_LSR_TEMT)) && + !(lsr & UART_LSR_DR)) break; udelay(1); } while (--tmout); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 234976fb2a87..b2c815e5d14c 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1212,18 +1212,19 @@ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg) uport = uart_port_ref(state); if (!uport) return -EIO; - scoped_guard(uart_port_lock_irq, uport) { - memcpy(&cprev, &uport->icount, sizeof(struct uart_icount)); - uart_enable_ms(uport); - } + uart_port_lock_irq(uport); + memcpy(&cprev, &uport->icount, sizeof(struct uart_icount)); + uart_enable_ms(uport); + uart_port_unlock_irq(uport); add_wait_queue(&port->delta_msr_wait, &wait); for (;;) { - scoped_guard(uart_port_lock_irq, uport) - memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); - set_current_state(TASK_INTERRUPTIBLE); + uart_port_lock_irq(uport); + memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); + uart_port_unlock_irq(uport); + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || @@ -3331,6 +3332,7 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port) err_unregister_port_dev: serial_base_port_device_remove(port->port_dev); + port->port_dev = NULL; err_unregister_ctrl_dev: serial_base_ctrl_device_remove(new_ctrl_dev); @@ -3345,12 +3347,24 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port) void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port) { struct device *phys_dev = port->dev; - struct serial_port_device *port_dev = port->port_dev; - struct serial_ctrl_device *ctrl_dev = serial_core_get_ctrl_dev(port_dev); + struct serial_port_device *port_dev; + struct serial_ctrl_device *ctrl_dev; int ctrl_id = port->ctrl_id; guard(mutex)(&port_mutex); + /* + * A NULL port device means there is no registered port device to + * remove: serial_core_remove_one_port() clears port_dev on + * teardown, and it is never set if registration failed before + * serial_core_port_device_add(). + */ + port_dev = port->port_dev; + if (!port_dev) + return; + + ctrl_dev = serial_core_get_ctrl_dev(port_dev); + port->flags |= UPF_DEAD; serial_core_remove_one_port(drv, port); diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 78a1c1eea11b..fc6fdc751b16 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -656,6 +656,7 @@ static int vt8500_serial_probe(struct platform_device *pdev) ret = clk_prepare_enable(vt8500_port->clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock\n"); + clk_put(vt8500_port->clk); return ret; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 4889076b975f..b7472f46d9ff 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -620,6 +620,8 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) tty_ldisc_hangup(tty, cons_filp != NULL); + wake_up_interruptible(&tty->break_wait); + spin_lock_irq(&tty->ctrl.lock); clear_bit(TTY_THROTTLED, &tty->flags); clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); @@ -2458,6 +2460,7 @@ static int tiocgetd(struct tty_struct *tty, int __user *p) * send_break - performed time break * @tty: device to break on * @duration: timeout in mS + * @file: file object * * Perform a timed break on hardware that lacks its own driver level timed * break functionality. @@ -2465,8 +2468,9 @@ static int tiocgetd(struct tty_struct *tty, int __user *p) * Locking: * @tty->atomic_write_lock serializes */ -static int send_break(struct tty_struct *tty, unsigned int duration) +static int send_break(struct file *file, struct tty_struct *tty, unsigned int duration) { + long timeout; int retval; if (tty->ops->break_ctl == NULL) @@ -2480,13 +2484,26 @@ static int send_break(struct tty_struct *tty, unsigned int duration) return -EINTR; retval = tty->ops->break_ctl(tty, -1); - if (!retval) { - msleep_interruptible(duration); - retval = tty->ops->break_ctl(tty, 0); - } else if (retval == -EOPNOTSUPP) { - /* some drivers can tell only dynamically */ - retval = 0; + if (retval) { + if (retval == -EOPNOTSUPP) { + /* some drivers can tell only dynamically */ + retval = 0; + } + goto out_unlock; + } + + timeout = msecs_to_jiffies(duration); + timeout = wait_event_interruptible_timeout(tty->break_wait, + tty_hung_up_p(file), + timeout); + /* return early on hangup only */ + if (timeout > 0) { + retval = -EIO; + goto out_unlock; } + + retval = tty->ops->break_ctl(tty, 0); +out_unlock: tty_write_unlock(tty); if (signal_pending(current)) @@ -2754,10 +2771,10 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) * This is used by the tcdrain() termios function. */ if (!arg) - return send_break(tty, 250); + return send_break(file, tty, 250); return 0; case TCSBRKP: /* support for POSIX tcsendbreak() */ - return send_break(tty, arg ? arg*100 : 250); + return send_break(file, tty, arg ? arg * 100 : 250); case TIOCMGET: return tty_tiocmget(tty, p); @@ -3117,6 +3134,7 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx) init_ldsem(&tty->ldisc_sem); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); + init_waitqueue_head(&tty->break_wait); INIT_WORK(&tty->hangup_work, do_tty_hangup); mutex_init(&tty->atomic_write_lock); spin_lock_init(&tty->ctrl.lock); @@ -3265,14 +3283,16 @@ struct device *tty_register_device_attr(struct tty_driver *driver, goto err_put; if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) { - /* - * Free any saved termios data so that the termios state is - * reset when reusing a minor number. - */ - tp = driver->termios[index]; - if (tp) { - driver->termios[index] = NULL; - kfree(tp); + if (driver->flags & TTY_DRIVER_RESET_SAVED_TERMIOS) { + /* + * Free any saved termios data so that the termios + * state is reset when reusing a minor number. + */ + tp = driver->termios[index]; + if (tp) { + driver->termios[index] = NULL; + kfree(tp); + } } retval = tty_cdev_add(driver, devt, index, 1); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 54359310e293..b22ff9eb7778 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -340,19 +340,9 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) } EXPORT_SYMBOL(tty_port_tty_set); -/** - * tty_port_shutdown - internal helper to shutdown the device - * @port: tty port to be shut down - * @tty: the associated tty - * - * It is used by tty_port_hangup() and tty_port_close(). Its task is to - * shutdown the device if it was initialized (note consoles remain - * functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes - * @port->ops->shutdown(). - */ -static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) +static void tty_port_shutdown_locked(struct tty_port *port, struct tty_struct *tty) { - guard(mutex)(&port->mutex); + lockdep_assert_held(&port->mutex); if (port->console) return; @@ -372,6 +362,23 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) port->ops->shutdown(port); } +/** + * tty_port_shutdown - internal helper to shutdown the device + * @port: tty port to be shut down + * @tty: the associated tty + * + * It is used by tty_port_hangup() and tty_port_close(). Its task is to + * shutdown the device if it was initialized (note consoles remain + * functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes + * @port->ops->shutdown(). + */ +static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) +{ + guard(mutex)(&port->mutex); + + tty_port_shutdown_locked(port, tty); +} + /** * tty_port_hangup - hangup helper * @port: tty port @@ -385,36 +392,58 @@ void tty_port_hangup(struct tty_port *port) { struct tty_struct *tty; - scoped_guard(spinlock_irqsave, &port->lock) { - port->count = 0; - tty = port->tty; - if (tty) - set_bit(TTY_IO_ERROR, &tty->flags); - port->tty = NULL; - } + scoped_guard(mutex, &port->mutex) { + scoped_guard(spinlock_irqsave, &port->lock) { + port->count = 0; + tty = port->tty; + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); + port->tty = NULL; + } - tty_port_set_active(port, false); - tty_port_shutdown(port, tty); + tty_port_set_active(port, false); + tty_port_shutdown_locked(port, tty); + } tty_kref_put(tty); wake_up_interruptible(&port->open_wait); wake_up_interruptible(&port->delta_msr_wait); } EXPORT_SYMBOL(tty_port_hangup); -void __tty_port_tty_hangup(struct tty_port *port, bool check_clocal, bool async) +/** + * tty_port_tty_hangup - helper to hang up a tty asynchronously + * @port: tty port + * @check_clocal: hang only ttys with %CLOCAL unset? + */ +void tty_port_tty_hangup(struct tty_port *port, bool check_clocal) { scoped_guard(tty_port_tty, port) { struct tty_struct *tty = scoped_tty(); - if (!check_clocal || !C_CLOCAL(tty)) { - if (async) - tty_hangup(tty); - else - tty_vhangup(tty); - } + if (!check_clocal || !C_CLOCAL(tty)) + tty_hangup(tty); + } +} +EXPORT_SYMBOL_GPL(tty_port_tty_hangup); + +/** + * tty_port_tty_vhangup - helper to hang up a tty synchronously + * @port: tty port + */ +void tty_port_tty_vhangup(struct tty_port *port) +{ + struct tty_struct *tty; + + mutex_lock(&port->mutex); + tty = tty_port_tty_get(port); + mutex_unlock(&port->mutex); + + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); } } -EXPORT_SYMBOL_GPL(__tty_port_tty_hangup); +EXPORT_SYMBOL_GPL(tty_port_tty_vhangup); /** * tty_port_tty_wakeup - helper to wake up a tty @@ -767,8 +796,10 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty, clear_bit(TTY_IO_ERROR, &tty->flags); if (port->ops->activate) { int retval = port->ops->activate(port, tty); - if (retval) + if (retval) { + set_bit(TTY_IO_ERROR, &tty->flags); return retval; + } } tty_port_set_initialized(port, true); } diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 27a55465bf5e..9adb533f7034 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -492,7 +492,7 @@ static ssize_t domain_show(struct device *dev, static int vcc_send_ctl(struct vcc_port *port, int ctl) { - struct vio_vcc pkt; + struct vio_vcc pkt = {}; int rv; pkt.tag.type = VIO_TYPE_CTRL; @@ -986,7 +986,8 @@ static void vcc_cleanup(struct tty_struct *tty) port = vcc_get(tty->index, true); if (port) { - port->tty = NULL; + scoped_guard(spinlock_irqsave, &port->lock) + port->tty = NULL; if (port->removed) { vcc_table_remove(tty->index); diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 13f4e48b4142..f1a3bc5b5fad 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -434,8 +434,8 @@ int paste_selection(struct tty_struct *tty) bps = NULL; } - count = vc_sel.buf_len - pasted; - if (count) { + if (vc_sel.buf_len > pasted) { + count = vc_sel.buf_len - pasted; pasted += tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted, NULL, count); if (vc_sel.buf_len > pasted) diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 7d40eacc21b3..93ba5fffe531 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -633,6 +633,18 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) ret = copy_from_user(con_buf, buf, this_round); console_lock(); + /* The vc might have been freed or vcs_size might have changed + * while we slept to grab the user buffer; recheck here, before + * if (ret), so every break path below passes a fresh vc to the + * post-loop vcs_scr_updated(). Return data written so far. + */ + vc = vcs_vc(inode, &viewed); + if (!vc) { + if (written) + break; + return -ENXIO; + } + if (ret) { this_round -= ret; if (!this_round) { @@ -644,17 +656,6 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) return -EFAULT; } } - - /* The vc might have been freed or vcs_size might have changed - * while we slept to grab the user buffer, so recheck. - * Return data written up to now on failure. - */ - vc = vcs_vc(inode, &viewed); - if (!vc) { - if (written) - break; - return -ENXIO; - } size = vcs_size(vc, attr, false); if (size < 0) { if (written) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8f467b22b799..c5a43fee7d30 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -1134,6 +1134,8 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ return 0; err_free: visual_deinit(vc); + if (*vc->uni_pagedict_loc) + con_free_unimap(vc); kfree(vc); vc_cons[currcons].d = NULL; return err; @@ -2860,7 +2862,8 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c) csi_J(vc, CSI_J_VISIBLE); vc->vc_video_erase_char = (vc->vc_video_erase_char & 0xff00) | ' '; - do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2); + if (con_should_update(vc)) + do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2); } return; case ESsetG0: /* ESC ( */ diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 42311c1bfada..0d272e9c556c 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -3269,6 +3269,10 @@ static void cdns3_gadget_exit(struct cdns *cdns) usb_del_gadget(&priv_dev->gadget); devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev); + /* The works can still be queued until the IRQ is freed. */ + cancel_work_sync(&priv_dev->pending_status_wq); + cancel_work_sync(&priv_dev->aligned_buf_wq); + cdns3_free_all_eps(priv_dev); while (!list_empty(&priv_dev->aligned_buf_list)) { diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c index eb5760f75b9d..fd06a3d8e638 100644 --- a/drivers/usb/cdns3/cdns3-pci-wrap.c +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c @@ -96,6 +96,11 @@ static int cdns3_pci_probe(struct pci_dev *pdev, if (pci_is_enabled(func)) { wrap = pci_get_drvdata(func); + if (!wrap) { + dev_err(&pdev->dev, + "second function not initialized, retrying\n"); + return -EPROBE_DEFER; + } } else { wrap = kzalloc_obj(*wrap); if (!wrap) diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index 372788f0f970..26225b034d3b 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -306,8 +306,10 @@ static int tegra_usb_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); err = pm_runtime_resume_and_get(&pdev->dev); - if (err) + if (err) { + pm_runtime_disable(&pdev->dev); return err; + } if (device_property_present(&pdev->dev, "nvidia,needs-double-reset")) usb->needs_double_reset = true; diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 7bc5329fa3ed..0d3f815b2f1e 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -331,7 +331,6 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) spin_lock_irqsave(&acm->read_lock, flags); acm->ctrlin = newctrl; - acm->oldcount = acm->iocount; if (difference & USB_CDC_SERIAL_STATE_DSR) acm->iocount.dsr++; @@ -788,6 +787,9 @@ static void acm_port_shutdown(struct tty_port *port) usb_autopm_put_interface_async(acm->control); } + if (acm->disconnected) + return; + acm_unpoison_urbs(acm); if (acm->quirks & ALWAYS_POLL_CTRL) { @@ -1027,11 +1029,16 @@ static int wait_serial_change(struct acm *acm, unsigned long arg) DECLARE_WAITQUEUE(wait, current); struct async_icount old, new; - do { + spin_lock_irq(&acm->read_lock); + old = acm->iocount; + spin_unlock_irq(&acm->read_lock); + + add_wait_queue(&acm->wioctl, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irq(&acm->read_lock); - old = acm->oldcount; new = acm->iocount; - acm->oldcount = new; spin_unlock_irq(&acm->read_lock); if ((arg & TIOCM_DSR) && @@ -1044,22 +1051,20 @@ static int wait_serial_change(struct acm *acm, unsigned long arg) old.rng != new.rng) break; - add_wait_queue(&acm->wioctl, &wait); - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - remove_wait_queue(&acm->wioctl, &wait); if (acm->disconnected) { - if (arg & TIOCM_CD) - break; - else - rv = -ENODEV; - } else { - if (signal_pending(current)) - rv = -ERESTARTSYS; + rv = -ENODEV; + break; } - } while (!rv); - + schedule(); + + if (signal_pending(current)) { + rv = -ERESTARTSYS; + break; + } + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&acm->wioctl, &wait); return rv; } @@ -2140,7 +2145,7 @@ static int __init acm_init(void) { int retval; acm_tty_driver = tty_alloc_driver(ACM_TTY_MINORS, TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV); + TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_RESET_SAVED_TERMIOS); if (IS_ERR(acm_tty_driver)) return PTR_ERR(acm_tty_driver); acm_tty_driver->driver_name = "acm", diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 01f448a783c0..d245e60eb9f5 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -90,7 +90,6 @@ struct acm { unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ unsigned int ctrlout; /* output control lines (DTR, RTS) */ struct async_icount iocount; /* counters for control line changes */ - struct async_icount oldcount; /* for comparison of counter */ wait_queue_head_t wioctl; /* for ioctl */ unsigned int writesize; /* max packet size for the output bulk endpoint */ unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 068c7bea3102..b119f5b6065c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -73,8 +73,11 @@ /* * Give SS hubs 200ms time after wake to train downstream links before * assuming no port activity and allowing hub to runtime suspend back. + * Root hubs have no upstream hub whose wake propagation needs to be + * accounted for, so they need less time, use 120ms for them. */ #define USB_SS_PORT_U0_WAKE_TIME 200 /* ms */ +#define USB_SS_RH_PORT_U0_WAKE_TIME 120 /* ms */ /* Protect struct usb_device->state and ->children members * Note: Both are also protected by ->dev.sem, except that ->state can @@ -1358,7 +1361,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) queue_delayed_work(system_power_efficient_wq, &hub->post_resume_work, - msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME)); + msecs_to_jiffies(hdev->parent ? + USB_SS_PORT_U0_WAKE_TIME : + USB_SS_RH_PORT_U0_WAKE_TIME)); return; } diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 75e2bfd744a9..0cd6dd2b6334 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1337,7 +1337,8 @@ static void remove_intf_ep_devs(struct usb_interface *intf) * * Disables the endpoint for URB submission and nukes all pending URBs. * If @reset_hardware is set then also deallocates hcd/hardware state - * for the endpoint. + * for the endpoint (clearing both ep_in and ep_out pointers for + * bidirectional non-ep0 control endpoints). */ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr, bool reset_hardware) @@ -1358,6 +1359,11 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr, dev->ep_in[epnum] = NULL; } if (ep) { + if (reset_hardware && epnum != 0 && + usb_endpoint_xfer_control(&ep->desc)) { + dev->ep_out[epnum] = NULL; + dev->ep_in[epnum] = NULL; + } ep->enabled = 0; usb_hcd_flush_endpoint(dev, ep); if (reset_hardware) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 2414291aa908..cd0dc876b421 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -5082,14 +5082,13 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) } cancel_work_sync(&hsotg->phy_reset_work); - - timer_delete(&hsotg->wkp_timer); } static void dwc2_hcd_release(struct dwc2_hsotg *hsotg) { /* Turn off all host-specific interrupts */ dwc2_disable_host_interrupts(hsotg); + timer_shutdown_sync(&hsotg->wkp_timer); dwc2_hcd_free(hsotg); } diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e0dee9d28740..bb58bf290d5a 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -49,6 +49,7 @@ #define DWC3_ENDPOINTS_NUM 32 #define DWC3_XHCI_RESOURCES_NUM 2 #define DWC3_ISOC_MAX_RETRIES 5 +#define DWC3_ERR_RECOVERY_MAX 3 #define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */ #define DWC3_EVENT_BUFFERS_SIZE 4096 @@ -842,6 +843,12 @@ enum dwc3_link_state { DWC3_LINK_STATE_MASK = 0x0f, }; +enum dwc3_err_state { + DWC3_ERR_NONE = 0, + DWC3_ERR_RECOVERY, + DWC3_ERR_UNRECOVERABLE, +}; + /* TRB Length, PCM and Status */ #define DWC3_TRB_SIZE_MASK (0x00ffffff) #define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK) @@ -1005,6 +1012,7 @@ struct dwc3_glue_ops { /** * struct dwc3 - representation of our controller * @drd_work: workqueue used for role swapping + * @err_recovery_work: workqueue used for controller error recovery * @ep0_trb: trb which is used for the ctrl_req * @bounce: address of bounce buffer * @setup_buf: used while precessing STD USB requests @@ -1014,6 +1022,7 @@ struct dwc3_glue_ops { * @ep0_in_setup: one control transfer is completed and enter setup phase * @lock: for synchronizing * @mutex: for mode switching + * @connect_mutex: for the pull-up and err_recovery_work * @dev: pointer to our struct device * @sysdev: pointer to the DMA-capable device * @xhci: pointer to our xHCI child @@ -1078,6 +1087,9 @@ struct dwc3_glue_ops { * @ep0_next_event: hold the next expected event * @ep0state: state of endpoint zero * @link_state: link state + * @err_state: current error recovery state. + * @err_recovery_count: number of consecutive error recovery attempts until + * confirmed healthy and reset to 0 on reset event. * @speed: device speed (super, high, full, low) * @hwparams: copy of hwparams registers * @regset: debugfs pointer to regdump file @@ -1188,6 +1200,7 @@ struct dwc3_glue_ops { */ struct dwc3 { struct work_struct drd_work; + struct work_struct err_recovery_work; struct dwc3_trb *ep0_trb; void *bounce; u8 *setup_buf; @@ -1202,6 +1215,9 @@ struct dwc3 { /* mode switching lock */ struct mutex mutex; + /* serializes pull-up run/stop vs error recovery */ + struct mutex connect_mutex; + struct device *dev; struct device *sysdev; @@ -1327,6 +1343,9 @@ struct dwc3 { enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; enum dwc3_link_state link_state; + enum dwc3_err_state err_state; + + u32 err_recovery_count; u16 u2sel; u16 u2pel; diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c index e11d7643f966..2397e63a95dd 100644 --- a/drivers/usb/dwc3/dwc3-am62.c +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -297,6 +297,7 @@ static int dwc3_ti_probe(struct platform_device *pdev) err_pm_disable: clk_disable_unprepare(am62->usb2_refclk); + pm_runtime_put_noidle(dev); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); return ret; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 310b5ffb236a..46a34c4a046b 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -200,6 +200,11 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, int ret; spin_lock_irqsave(&dwc->lock, flags); + if (dwc->err_state != DWC3_ERR_NONE) { + ret = -ESHUTDOWN; + goto out; + } + if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) { dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n", dep->name); @@ -271,6 +276,10 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value) int ret; spin_lock_irqsave(&dwc->lock, flags); + if (dwc->err_state != DWC3_ERR_NONE) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -ESHUTDOWN; + } ret = __dwc3_gadget_ep0_set_halt(ep, value); spin_unlock_irqrestore(&dwc->lock, flags); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e46db7032089..a2b69358468a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1149,6 +1149,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, return 0; spin_lock_irqsave(&dwc->lock, flags); + if (dwc->err_state != DWC3_ERR_NONE) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -ESHUTDOWN; + } ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT); spin_unlock_irqrestore(&dwc->lock, flags); @@ -2060,6 +2064,10 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, int ret; spin_lock_irqsave(&dwc->lock, flags); + if (dwc->err_state != DWC3_ERR_NONE) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -ESHUTDOWN; + } ret = __dwc3_gadget_ep_queue(dep, req); spin_unlock_irqrestore(&dwc->lock, flags); @@ -2292,6 +2300,10 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) int ret; spin_lock_irqsave(&dwc->lock, flags); + if (dwc->err_state != DWC3_ERR_NONE) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -ESHUTDOWN; + } ret = __dwc3_gadget_ep_set_halt(dep, value, false); spin_unlock_irqrestore(&dwc->lock, flags); @@ -2306,6 +2318,10 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) int ret; spin_lock_irqsave(&dwc->lock, flags); + if (dwc->err_state != DWC3_ERR_NONE) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -ESHUTDOWN; + } dep->flags |= DWC3_EP_WEDGE; if (dep->number == 0 || dep->number == 1) @@ -2437,6 +2453,10 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) } spin_lock_irqsave(&dwc->lock, flags); + if (dwc->err_state != DWC3_ERR_NONE) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -ESHUTDOWN; + } if (!dwc->gadget->wakeup_armed) { dev_err(dwc->dev, "not armed for remote wakeup\n"); spin_unlock_irqrestore(&dwc->lock, flags); @@ -2464,6 +2484,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id) } spin_lock_irqsave(&dwc->lock, flags); + if (dwc->err_state != DWC3_ERR_NONE) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -ESHUTDOWN; + } /* * If the link is in U3, signal for remote wakeup and wait for the * link to transition to U0 before sending device notification. @@ -2833,10 +2857,13 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) synchronize_irq(dwc->irq_gadget); + /* Serialize against dwc3_err_recovery_work() */ + mutex_lock(&dwc->connect_mutex); if (!is_on) ret = dwc3_gadget_soft_disconnect(dwc); else ret = dwc3_gadget_soft_connect(dwc); + mutex_unlock(&dwc->connect_mutex); pm_runtime_put(dwc->dev); @@ -4142,6 +4169,10 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dwc->suspended = false; + /* The controller is recovered. */ + if (dwc->err_state == DWC3_ERR_NONE) + dwc->err_recovery_count = 0; + /* * Ideally, dwc3_reset_gadget() would trigger the function * drivers to stop any active transfers through ep disable. @@ -4629,6 +4660,12 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt) return ret; } +static void dwc3_schedule_err_recovery(struct dwc3 *dwc) +{ + dwc->err_state = DWC3_ERR_RECOVERY; + schedule_work(&dwc->err_recovery_work); +} + static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) { struct dwc3 *dwc = evt->dwc; @@ -4662,9 +4699,21 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) return IRQ_NONE; if (count > evt->length) { - dev_err_ratelimited(dwc->dev, "invalid count(%u) > evt->length(%u)\n", + dev_err(dwc->dev, "invalid count(%u) > evt->length(%u)\n", count, evt->length); - return IRQ_NONE; + /* + * This is a fatal error - the driver and controller are out of + * sync on which event has been consumed. Reinitializing the + * controller is required to recover. Write the bogus count back + * to GEVNTCOUNT to clear the IRQ source, consistent with the + * stale event clearing in dwc3_event_buffers_setup(), then + * schedule error recovery. + */ + spin_lock(&dwc->lock); + dwc3_schedule_err_recovery(dwc); + spin_unlock(&dwc->lock); + dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), count); + return IRQ_HANDLED; } evt->count = count; @@ -4724,6 +4773,57 @@ static void dwc_gadget_release(struct device *dev) kfree(gadget); } +static void dwc3_err_recovery_work(struct work_struct *work) +{ + struct dwc3 *dwc = container_of(work, struct dwc3, err_recovery_work); + unsigned long flags; + int ret; + + /* serializes against dwc3_gadget_pullup() */ + mutex_lock(&dwc->connect_mutex); + + ret = dwc3_gadget_soft_disconnect(dwc); + + dwc3_disconnect_gadget_sleepable(dwc); + + if (ret) + goto err_unrecoverable; + + if (dwc->softconnect) { + u32 count; + /* + * Wait irq to finish before soft_connect resets evt->lpos and + * the event buffer registers to avoid racing with + * dwc3_process_event_buf(). + */ + synchronize_irq(dwc->irq_gadget); + + spin_lock_irqsave(&dwc->lock, flags); + count = ++dwc->err_recovery_count; + spin_unlock_irqrestore(&dwc->lock, flags); + + if (count > DWC3_ERR_RECOVERY_MAX) + goto err_unrecoverable; + + ret = dwc3_gadget_soft_connect(dwc); + if (ret) + goto err_unrecoverable; + } + mutex_unlock(&dwc->connect_mutex); + + spin_lock_irqsave(&dwc->lock, flags); + dwc->err_state = DWC3_ERR_NONE; + spin_unlock_irqrestore(&dwc->lock, flags); + return; + +err_unrecoverable: + dev_err(dwc->dev, "Unable to recover the controller\n"); + mutex_unlock(&dwc->connect_mutex); + spin_lock_irqsave(&dwc->lock, flags); + dwc->err_state = DWC3_ERR_UNRECOVERABLE; + spin_unlock_irqrestore(&dwc->lock, flags); +} + /** * dwc3_gadget_init - initializes gadget related registers * @dwc: pointer to our controller context structure @@ -4767,6 +4867,8 @@ int dwc3_gadget_init(struct dwc3 *dwc) } init_completion(&dwc->ep0_in_setup); + INIT_WORK(&dwc->err_recovery_work, dwc3_err_recovery_work); + mutex_init(&dwc->connect_mutex); dwc->gadget = kzalloc_obj(struct usb_gadget); if (!dwc->gadget) { ret = -ENOMEM; @@ -4863,6 +4965,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc) if (!dwc->gadget) return; + cancel_work_sync(&dwc->err_recovery_work); + mutex_destroy(&dwc->connect_mutex); dwc3_enable_susphy(dwc, true); usb_del_gadget(dwc->gadget); dwc3_gadget_free_endpoints(dwc); diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 563390f05f66..64eb5c99299f 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1844,8 +1844,9 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, break; case FUNCTIONFS_ENDPOINT_DESC: { + const struct usb_endpoint_descriptor *desc; + struct usb_endpoint_descriptor desc1; int desc_idx; - struct usb_endpoint_descriptor desc1, *desc; switch (epfile->ffs->gadget->speed) { case USB_SPEED_SUPER: @@ -1859,7 +1860,17 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, desc_idx = 0; } - desc = epfile->ep->descs[desc_idx]; + do { + desc = epfile->ep->descs[desc_idx]; + } while (!desc && --desc_idx >= 0); + + if (!desc) + desc = epfile->ep->ep->desc; + if (!desc) { + ret = -EINVAL; + break; + } + memcpy(&desc1, desc, desc->bLength); spin_unlock_irq(&epfile->ffs->eps_lock); diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index 5b8b18281989..d693de427641 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -1633,8 +1633,8 @@ struct f_midi2_usb_config { /* MIDI 1.0 jacks */ unsigned char jack_in, jack_out, jack_id; - struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES]; - struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES]; + struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES * 2]; + struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES * 2]; }; static int append_config(struct f_midi2_usb_config *config, void *d) diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c index 3f52099a4fdd..7c8d60c7e87d 100644 --- a/drivers/usb/gadget/function/f_uac1_legacy.c +++ b/drivers/usb/gadget/function/f_uac1_legacy.c @@ -797,6 +797,9 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) { + if (cmd >= ARRAY_SIZE(con->data)) + return -EINVAL; + con->data[cmd] = value; return 0; @@ -804,6 +807,9 @@ static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) { + if (cmd >= ARRAY_SIZE(con->data)) + return -EINVAL; + return con->data[cmd]; } diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index 4a8b9ff8368f..069673f0d359 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -267,6 +267,9 @@ static void ast_vhub_remove(struct platform_device *pdev) for (i = 0; i < vhub->max_ports; i++) ast_vhub_del_dev(&vhub->ports[i].dev); + /* Final drain; the worker takes vhub->lock, so stay outside of it */ + cancel_work_sync(&vhub->wake_work); + spin_lock_irqsave(&vhub->lock, flags); /* Mask & ack all interrupts */ @@ -328,6 +331,7 @@ static int ast_vhub_probe(struct platform_device *pdev) return -ENOMEM; spin_lock_init(&vhub->lock); + INIT_WORK(&vhub->wake_work, ast_vhub_wake_work); vhub->pdev = pdev; vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1, VHUB_IRQ_DEV1_BIT); diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index 8b9449d16324..4b389de0d935 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -280,7 +280,7 @@ static int ast_vhub_udc_wakeup(struct usb_gadget* gadget) int rc = -EINVAL; spin_lock_irqsave(&d->vhub->lock, flags); - if (!d->wakeup_en) + if (!d->wakeup_en || !d->registered) goto err; DDBG(d, "Device initiated wakeup\n"); diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c index 02fe1a08d575..d0345f31037a 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c @@ -558,7 +558,7 @@ void ast_vhub_device_connect(struct ast_vhub *vhub, ast_vhub_send_host_wakeup(vhub); } -static void ast_vhub_wake_work(struct work_struct *work) +void ast_vhub_wake_work(struct work_struct *work) { struct ast_vhub *vhub = container_of(work, struct ast_vhub, @@ -588,6 +588,8 @@ static void ast_vhub_wake_work(struct work_struct *work) void ast_vhub_hub_wake_all(struct ast_vhub *vhub) { + lockdep_assert_held(&vhub->lock); + /* * A device is trying to wake the world, because this * can recurse into the device, we break the call chain @@ -1076,7 +1078,6 @@ static int ast_vhub_init_desc(struct ast_vhub *vhub) int ast_vhub_init_hub(struct ast_vhub *vhub) { vhub->speed = USB_SPEED_UNKNOWN; - INIT_WORK(&vhub->wake_work, ast_vhub_wake_work); return ast_vhub_init_desc(vhub); } diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h index aca2050e2db0..96f1c3709042 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h +++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h @@ -547,6 +547,7 @@ void ast_vhub_hub_suspend(struct ast_vhub *vhub); void ast_vhub_hub_resume(struct ast_vhub *vhub); void ast_vhub_hub_reset(struct ast_vhub *vhub); void ast_vhub_hub_wake_all(struct ast_vhub *vhub); +void ast_vhub_wake_work(struct work_struct *work); /* dev.c */ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx); diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index c0e40fa6dde5..0d30dd67b9ca 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -343,9 +343,11 @@ static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep, { bool fifo = req == &dum->fifo_req; + ++dum->callback_usage; spin_unlock(&dum->lock); usb_gadget_giveback_request(_ep, &req->req); spin_lock(&dum->lock); + --dum->callback_usage; if (fifo) dum->fifo_req_busy = 0; } @@ -759,11 +761,13 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req, req->req.complete = fifo_complete; list_add_tail(&req->queue, &ep->queue); + ++dum->callback_usage; spin_unlock(&dum->lock); _req->actual = _req->length; _req->status = 0; usb_gadget_giveback_request(_ep, _req); spin_lock(&dum->lock); + --dum->callback_usage; } else list_add_tail(&req->queue, &ep->queue); spin_unlock_irqrestore(&dum->lock, flags); diff --git a/drivers/usb/host/octeon-hcd.c b/drivers/usb/host/octeon-hcd.c index 34ce771fa0f5..7d121909f514 100644 --- a/drivers/usb/host/octeon-hcd.c +++ b/drivers/usb/host/octeon-hcd.c @@ -578,7 +578,7 @@ static int cvmx_wait_tx_rx(struct octeon_hcd *usb, int fflsh_type) int result; u64 address = CVMX_USBCX_GRSTCTL(usb->index); u64 done = cvmx_get_cycle() + 100 * - (u64)octeon_get_clock_rate / 1000000; + (u64)octeon_get_clock_rate() / 1000000; union cvmx_usbcx_grstctl c; while (1) { diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 0938c0e7a8b6..a7cd5fb4f119 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -447,6 +447,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev) err_remove_hcd: usb_remove_hcd(hcd); + device_wakeup_disable(hcd->self.controller); err: usb_put_hcd(hcd); return error; @@ -457,6 +458,7 @@ static void ohci_da8xx_remove(struct platform_device *pdev) struct usb_hcd *hcd = platform_get_drvdata(pdev); usb_remove_hcd(hcd); + device_wakeup_disable(hcd->self.controller); usb_put_hcd(hcd); } diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index e623e24d3f8e..7c0e35be58f1 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -335,6 +335,7 @@ ohci_hcd_s3c2410_remove(struct platform_device *dev) struct usb_hcd *hcd = platform_get_drvdata(dev); usb_remove_hcd(hcd); + device_wakeup_disable(hcd->self.controller); s3c2410_stop_hc(dev); usb_put_hcd(hcd); } diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 6843d7cb3f9f..5aeabb051265 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -103,6 +103,7 @@ static void spear_ohci_hcd_drv_remove(struct platform_device *pdev) struct spear_ohci *sohci_p = to_spear_ohci(hcd); usb_remove_hcd(hcd); + device_wakeup_disable(hcd->self.controller); clk_disable_unprepare(sohci_p->clk); usb_put_hcd(hcd); diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c index d1656fce5400..53010a277b67 100644 --- a/drivers/usb/host/ohci-st.c +++ b/drivers/usb/host/ohci-st.c @@ -235,6 +235,7 @@ static void st_ohci_platform_remove(struct platform_device *dev) int clk; usb_remove_hcd(hcd); + device_wakeup_disable(hcd->self.controller); if (pdata->power_off) pdata->power_off(dev); diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 9e2a18c0b218..ea1fe5d1e449 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -165,7 +165,11 @@ int usb_serial_bus_register(struct usb_serial_driver *driver) void usb_serial_bus_deregister(struct usb_serial_driver *driver) { - free_dynids(driver); driver_unregister(&driver->driver); + free_dynids(driver); } +void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver) +{ + driver_remove_file(&driver->driver, &driver_attr_new_id); +} diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 333b8b463704..c3f9add98333 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -223,6 +223,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */ { USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */ { USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */ + { USB_DEVICE(0x1B1C, 0x1C02) }, /* Corsair AX1500i Power Supply */ { USB_DEVICE(0x1B93, 0x1013) }, /* Phoenix Contact UPS Device */ { USB_DEVICE(0x1BA4, 0x0002) }, /* Silicon Labs 358x factory default */ { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */ diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 6eaf74930aa3..17272701fab0 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -266,6 +266,7 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer); void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; unsigned int bps; unsigned long period; unsigned long expire; @@ -285,7 +286,14 @@ void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) __func__, jiffies_to_msecs(timeout), jiffies_to_msecs(period)); expire = jiffies + timeout; - while (!port->serial->type->tx_empty(port)) { + for (;;) { + mutex_lock(&tport->mutex); + if (tty_io_error(tty) || port->serial->type->tx_empty(port)) { + mutex_unlock(&tport->mutex); + break; + } + mutex_unlock(&tport->mutex); + schedule_timeout_interruptible(period); if (signal_pending(current)) break; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index e4ad14375d6a..828b1be3fee2 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -260,6 +260,7 @@ static void option_instat_callback(struct urb *urb); #define QUECTEL_PRODUCT_EM060K_12a 0x012a #define QUECTEL_PRODUCT_EM060K_12b 0x012b #define QUECTEL_PRODUCT_EM060K_12c 0x012c +#define QUECTEL_PRODUCT_RG660QB 0x013d #define QUECTEL_PRODUCT_EG91 0x0191 #define QUECTEL_PRODUCT_EG95 0x0195 #define QUECTEL_PRODUCT_BG96 0x0296 @@ -280,6 +281,7 @@ static void option_instat_callback(struct urb *urb); #define QUECTEL_PRODUCT_EC200U 0x0901 #define QUECTEL_PRODUCT_EG912Y 0x6001 #define QUECTEL_PRODUCT_EC200S_CN 0x6002 +#define QUECTEL_PRODUCT_EG060W 0x6004 #define QUECTEL_PRODUCT_EC200A 0x6005 #define QUECTEL_PRODUCT_EG916Q 0x6007 #define QUECTEL_PRODUCT_EM061K_LWW 0x6008 @@ -1268,12 +1270,15 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200A, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200U, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200S_CN, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG060W, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200T, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG912Y, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG916Q, 0xff, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500K, 0xff, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0xff, 0x30) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x30) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x40) }, @@ -2168,6 +2173,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) }, /* Simcom SIM7500/SIM7600 MBIM mode */ { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9011, 0xff), /* Simcom SIM7500/SIM7600 RNDIS mode */ .driver_info = RSVD(7) }, + { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x902b, 0xff), + .driver_info = RSVD(4) }, { USB_DEVICE(0x1e0e, 0x9071), /* Simcom SIM8230 RMNET mode */ .driver_info = RSVD(3) | RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9078, 0xff), /* Simcom SIM8230 ECM mode */ @@ -2429,6 +2436,13 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(5) }, { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe167, 0xff), /* Foxconn T99W640 MBIM */ .driver_info = RSVD(3) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x01) }, /* Compal EXC-T1 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x02) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x03) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x04) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x30) }, /* Compal EXM-G1x */ + { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x60) }, { USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */ .driver_info = RSVD(4) | RSVD(5) | RSVD(6) }, { USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */ diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index dc2b39810a26..3b4df3ac7c73 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -52,6 +52,7 @@ #define QT2_XMIT_FLUSH 0x05 /* no following info */ #define QT2_CONTROL_ESCAPE 0xff /* pass through previous 2 control bytes */ +#define MIN_BAUD_RATE 50 #define MAX_BAUD_RATE 921600 #define DEFAULT_BAUD_RATE 9600 @@ -156,7 +157,7 @@ static inline int calc_baud_divisor(int baudrate) static inline int qt2_set_port_config(struct usb_device *dev, unsigned char port_number, - u16 baudrate, u16 lcr) + speed_t baudrate, u16 lcr) { int divisor = calc_baud_divisor(baudrate); u16 index = ((u16) (lcr << 8) | (u16) (port_number)); @@ -257,9 +258,9 @@ static void qt2_set_termios(struct tty_struct *tty, struct usb_device *dev = port->serial->dev; struct qt2_port_private *port_priv; struct ktermios *termios = &tty->termios; - u16 baud; unsigned int cflag = termios->c_cflag; u16 new_lcr = 0; + speed_t baud; int status; port_priv = usb_get_serial_port_data(port); @@ -277,6 +278,18 @@ static void qt2_set_termios(struct tty_struct *tty, if (!baud) baud = 9600; + if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) { + if (old_termios) + baud = tty_termios_baud_rate(old_termios); + else + baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE); + + tty_encode_baud_rate(tty, baud, baud); + + if (!baud) + baud = 9600; + } + status = qt2_set_port_config(dev, port_priv->device_port, baud, new_lcr); if (status < 0) @@ -373,7 +386,7 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port) port_priv->device_port = (u8) device_port; if (tty) - qt2_set_termios(tty, port, &tty->termios); + qt2_set_termios(tty, port, NULL); return 0; diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index b0d51558b73c..8c5b9ef708b3 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -32,6 +32,7 @@ #define SERIAL_EVEN_PARITY (UART_LCR_PARITY | UART_LCR_EPAR) +#define MIN_BAUD_RATE 50 #define MAX_BAUD_RATE 460800 #define ATC_DISABLED 0x00 @@ -217,9 +218,10 @@ static void ssu100_set_termios(struct tty_struct *tty, { struct usb_device *dev = port->serial->dev; struct ktermios *termios = &tty->termios; - u16 baud, divisor, remainder; + speed_t baud, remainder; unsigned int cflag = termios->c_cflag; u16 urb_value = 0; /* will hold the new flags */ + u16 divisor; int result; if (cflag & PARENB) { @@ -235,6 +237,18 @@ static void ssu100_set_termios(struct tty_struct *tty, if (!baud) baud = 9600; + if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) { + if (old_termios) + baud = tty_termios_baud_rate(old_termios); + else + baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE); + + tty_encode_baud_rate(tty, baud, baud); + + if (!baud) + baud = 9600; + } + dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud); @@ -310,7 +324,7 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port) dev_dbg(&port->dev, "%s - set uart failed\n", __func__); if (tty) - ssu100_set_termios(tty, port, &tty->termios); + ssu100_set_termios(tty, port, NULL); return usb_serial_generic_open(tty, port); } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 17edc057a311..9cc9ae286b69 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -400,17 +400,13 @@ static unsigned int serial_chars_in_buffer(struct tty_struct *tty) static void serial_wait_until_sent(struct tty_struct *tty, int timeout) { struct usb_serial_port *port = tty->driver_data; - struct usb_serial *serial = port->serial; dev_dbg(&port->dev, "%s\n", __func__); if (!port->serial->type->wait_until_sent) return; - mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) - port->serial->type->wait_until_sent(tty, timeout); - mutex_unlock(&serial->disc_mutex); + port->serial->type->wait_until_sent(tty, timeout); } static void serial_throttle(struct tty_struct *tty) @@ -438,8 +434,13 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss) struct usb_serial_port *port = tty->driver_data; struct tty_port *tport = &port->port; unsigned int close_delay, closing_wait; + int ret = 0; mutex_lock(&tport->mutex); + if (tty_io_error(tty)) { + ret = -EIO; + goto out_unlock; + } close_delay = jiffies_to_msecs(tport->close_delay) / 10; closing_wait = tport->closing_wait; @@ -452,10 +453,10 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss) if (port->serial->type->get_serial) port->serial->type->get_serial(tty, ss); - +out_unlock: mutex_unlock(&tport->mutex); - return 0; + return ret; } static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss) @@ -471,6 +472,10 @@ static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss) closing_wait = msecs_to_jiffies(closing_wait * 10); mutex_lock(&tport->mutex); + if (tty_io_error(tty)) { + ret = -EIO; + goto out_unlock; + } if (!capable(CAP_SYS_ADMIN)) { if (close_delay != tport->close_delay || @@ -498,6 +503,7 @@ static int serial_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; int retval = -ENOIOCTLCMD; dev_dbg(&port->dev, "%s - cmd 0x%04x\n", __func__, cmd); @@ -508,8 +514,21 @@ static int serial_ioctl(struct tty_struct *tty, retval = port->serial->type->tiocmiwait(tty, arg); break; default: - if (port->serial->type->ioctl) + if (!port->serial->type->ioctl) + break; + + if (cmd == TIOCSRS485) + down_write(&tty->termios_rwsem); + + mutex_lock(&tport->mutex); + if (tty_io_error(tty)) + retval = -EIO; + else retval = port->serial->type->ioctl(tty, cmd, arg); + mutex_unlock(&tport->mutex); + + if (cmd == TIOCSRS485) + up_write(&tty->termios_rwsem); } return retval; @@ -519,25 +538,40 @@ static void serial_set_termios(struct tty_struct *tty, const struct ktermios *old) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; dev_dbg(&port->dev, "%s\n", __func__); - if (port->serial->type->set_termios) - port->serial->type->set_termios(tty, port, old); - else + if (!port->serial->type->set_termios) { tty_termios_copy_hw(&tty->termios, old); + return; + } + + mutex_lock(&tport->mutex); + if (!tty_io_error(tty)) + port->serial->type->set_termios(tty, port, old); + mutex_unlock(&tport->mutex); } static int serial_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; + int ret; dev_dbg(&port->dev, "%s\n", __func__); - if (port->serial->type->break_ctl) - return port->serial->type->break_ctl(tty, break_state); + if (!port->serial->type->break_ctl) + return -ENOTTY; - return -ENOTTY; + mutex_lock(&tport->mutex); + if (tty_io_error(tty)) + ret = -EIO; + else + ret = port->serial->type->break_ctl(tty, break_state); + mutex_unlock(&tport->mutex); + + return ret; } static int serial_proc_show(struct seq_file *m, void *v) @@ -578,24 +612,44 @@ static int serial_proc_show(struct seq_file *m, void *v) static int serial_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; + int ret; dev_dbg(&port->dev, "%s\n", __func__); - if (port->serial->type->tiocmget) - return port->serial->type->tiocmget(tty); - return -ENOTTY; + if (!port->serial->type->tiocmget) + return -ENOTTY; + + mutex_lock(&tport->mutex); + if (tty_io_error(tty)) + ret = -EIO; + else + ret = port->serial->type->tiocmget(tty); + mutex_unlock(&tport->mutex); + + return ret; } static int serial_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; + int ret; dev_dbg(&port->dev, "%s\n", __func__); - if (port->serial->type->tiocmset) - return port->serial->type->tiocmset(tty, set, clear); - return -ENOTTY; + if (!port->serial->type->tiocmset) + return -ENOTTY; + + mutex_lock(&tport->mutex); + if (tty_io_error(tty)) + ret = -EIO; + else + ret = port->serial->type->tiocmset(tty, set, clear); + mutex_unlock(&tport->mutex); + + return ret; } static int serial_get_icount(struct tty_struct *tty, @@ -1191,12 +1245,17 @@ static void usb_serial_disconnect(struct usb_interface *interface) usb_serial_port_poison_urbs(port); wake_up_interruptible(&port->port.delta_msr_wait); cancel_work_sync(&port->work); - if (device_is_registered(&port->dev)) - device_del(&port->dev); } + if (serial->type->disconnect) serial->type->disconnect(serial); + for (i = 0; i < serial->num_ports; ++i) { + port = serial->port[i]; + if (device_is_registered(&port->dev)) + device_del(&port->dev); + } + release_sibling(serial, interface); /* let the last holder of this object cause it to be cleaned up */ @@ -1313,7 +1372,8 @@ static int __init usb_serial_init(void) int result; usb_serial_tty_driver = tty_alloc_driver(USB_SERIAL_TTY_MINORS, - TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV); + TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | + TTY_DRIVER_RESET_SAVED_TERMIOS); if (IS_ERR(usb_serial_tty_driver)) return PTR_ERR(usb_serial_tty_driver); @@ -1464,7 +1524,7 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers { int rc; struct usb_driver *udriver; - struct usb_serial_driver * const *sd; + struct usb_serial_driver * const *sd, * const *s; /* * udriver must be registered before any of the serial drivers, @@ -1517,9 +1577,11 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers return 0; err_deregister_drivers: + for (s = serial_drivers; s < sd; ++s) + usb_serial_bus_remove_new_id(*s); + usb_deregister(udriver); while (sd-- > serial_drivers) usb_serial_deregister(*sd); - usb_deregister(udriver); err_free_driver: kfree(udriver); return rc; @@ -1537,10 +1599,23 @@ EXPORT_SYMBOL_GPL(__usb_serial_register_drivers); void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_drivers[]) { struct usb_driver *udriver = (*serial_drivers)->usb_driver; + struct usb_serial_driver * const *sd; + + /* + * udriver must be deregistered before the serial drivers so that + * I/O is stopped before unbinding the ports. + * + * Remove the new_id attributes to prevent ids from being added and + * triggering a probe of udriver after it has been deregistered. + */ + for (sd = serial_drivers; *sd; ++sd) + usb_serial_bus_remove_new_id(*sd); + + usb_deregister(udriver); for (; *serial_drivers; ++serial_drivers) usb_serial_deregister(*serial_drivers); - usb_deregister(udriver); + kfree(udriver); } EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers); diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c index 352c765d8803..c08f4aa14a3d 100644 --- a/drivers/usb/serial/xr_serial.c +++ b/drivers/usb/serial/xr_serial.c @@ -850,12 +850,9 @@ static int xr_get_rs485_config(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; struct xr_data *data = usb_get_serial_port_data(port); - down_read(&tty->termios_rwsem); - if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) { - up_read(&tty->termios_rwsem); + /* core holds port mutex */ + if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) return -EFAULT; - } - up_read(&tty->termios_rwsem); return 0; } @@ -871,10 +868,9 @@ static int xr_set_rs485_config(struct tty_struct *tty, return -EFAULT; xr_sanitize_serial_rs485(&rs485); - down_write(&tty->termios_rwsem); + /* core holds termios rwsem and port mutex */ data->rs485 = rs485; xr_set_flow_mode(tty, port, NULL); - up_write(&tty->termios_rwsem); if (copy_to_user(argp, &rs485, sizeof(rs485))) return -EFAULT; diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c index c5e3eeeb2144..992685ca6f1a 100644 --- a/drivers/usb/storage/sierra_ms.c +++ b/drivers/usb/storage/sierra_ms.c @@ -77,6 +77,13 @@ static int sierra_get_swoc_info(struct usb_device *udev, sizeof(struct swoc_info), /* __u16 size */ USB_CTRL_SET_TIMEOUT); /* int timeout */ + /* + * A short IN transfer leaves the tail of swocInfo uninitialized; + * only a full transfer is valid. + */ + if (result != sizeof(struct swoc_info)) + return -EIO; + swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU); swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer); return result; diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index 41df115912b9..c3d36da692e4 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -1566,8 +1566,11 @@ static void anx7411_i2c_remove(struct i2c_client *client) anx7411_partner_unregister_altmode(plat); anx7411_unregister_partner(plat); - if (plat->workqueue) + if (plat->workqueue) { + disable_irq(plat->intp_irq); + cancel_work_sync(&plat->work); destroy_workqueue(plat->workqueue); + } i2c_unregister_device(plat->spi_client); diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c index cdbb7c11d714..3a97b084c3bd 100644 --- a/drivers/usb/typec/port-mapper.c +++ b/drivers/usb/typec/port-mapper.c @@ -42,6 +42,23 @@ static int usb4_port_compare(struct device *dev, void *fwnode) return usb4_usb3_port_match(dev, fwnode); } +static bool typec_has_usb4_host_interface(const struct fwnode_handle *fwnode) +{ + if (!IS_REACHABLE(CONFIG_USB4)) + return false; + + struct fwnode_handle *nhi_fwnode __free(fwnode_handle) = + fwnode_find_reference(fwnode, "usb4-host-interface", 0); + + /* + * The USB4 port can only appear if the host interface is enabled in + * the firmware and has been enumerated as a device. The latter is + * the same check as in usb_acpi_add_usb4_devlink(). + */ + return !IS_ERR(nhi_fwnode) && fwnode_device_is_available(nhi_fwnode) && + nhi_fwnode->dev; +} + static int typec_port_compare(struct device *dev, void *fwnode) { return device_match_fwnode(dev, fwnode); @@ -64,11 +81,11 @@ static int typec_port_match(struct device *dev, void *data) adev_fwnode); /* - * If dev is USB 3.x port, it may have reference to the + * If dev is USB 3.x port, it may have reference to an available * USB4 host interface in which case we can also link the * Type-C port with the USB4 port. */ - if (fwnode_property_present(adev_fwnode, "usb4-host-interface")) + if (typec_has_usb4_host_interface(adev_fwnode)) component_match_add(&arg->port->dev, &arg->match, usb4_port_compare, adev_fwnode); } diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index e49ab6d54a5d..0cc2d3ab468f 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -6915,7 +6915,7 @@ static void tcpm_pd_event_handler(struct kthread_work *work) port->upcoming_state = FR_SWAP_SEND; ret = tcpm_ams_start(port, FAST_ROLE_SWAP); if (ret == -EAGAIN) - port->upcoming_state = INVALID_STATE; + tcpm_set_state(port, ERROR_RECOVERY, 0); } else { tcpm_log(port, "Discarding FRS_SIGNAL! Not in sink ready"); } diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 159d1a2f9a1f..ba152d0b7ed8 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -340,7 +340,7 @@ static void tps6598x_set_data_role(struct tps6598x *tps, static int tps6598x_connect(struct tps6598x *tps, u32 status) { - struct typec_partner_desc desc; + struct typec_partner_desc desc = { }; enum typec_pwr_opmode mode; int ret; @@ -351,7 +351,6 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status) desc.usb_pd = mode == TYPEC_PWR_MODE_PD; desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */ - desc.identity = NULL; if (desc.usb_pd) { ret = tps6598x_read_partner_identity(tps); @@ -819,11 +818,10 @@ static void cd321x_update_work(struct work_struct *work) /* Set up partner if we were previously disconnected (or changed). */ if (!tps->partner) { - struct typec_partner_desc desc; + struct typec_partner_desc desc = { }; desc.usb_pd = is_pd; desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */ - desc.identity = NULL; if (desc.usb_pd) desc.identity = &st.partner_identity; @@ -1752,6 +1750,7 @@ static int tps6598x_probe(struct i2c_client *client) const struct tipd_data *data; struct tps6598x *tps; struct fwnode_handle *fwnode; + bool patch_loaded = false; u32 status; u32 vid; int ret; @@ -1804,6 +1803,7 @@ static int tps6598x_probe(struct i2c_client *client) return ret; if (ret == TPS_MODE_PTCH) { + patch_loaded = true; ret = tps->data->init(tps); if (ret) return ret; @@ -1892,7 +1892,8 @@ static int tps6598x_probe(struct i2c_client *client) tps6598x_write64(tps, TPS_REG_INT_MASK1, 0); err_reset_controller: /* Reset PD controller to remove any applied patch */ - tps->data->reset(tps); + if (patch_loaded) + tps->data->reset(tps); return ret; } diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 8d2032d0762c..572da7bbd29c 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -71,11 +71,14 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) if (ret < 0) { if (ucsi->version > 0x0100) goto err_unlock; - cur = 0xff; } - if (cur < UCSI_MAX_ALTMODES) { - ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY; + if (cur != 0xff) { + if (cur < UCSI_MAX_ALTMODES) + ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY; + else + ret = -EINVAL; + goto err_unlock; } diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index b3a7712a6450..af71c128821e 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1801,10 +1801,21 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) { struct fwnode_handle *fwnode; int i = 1; + int ret; + u32 port; - device_for_each_child_node(con->ucsi->dev, fwnode) - if (i++ == con->num) - return fwnode; + device_for_each_child_node(con->ucsi->dev, fwnode) { + ret = fwnode_property_read_u32(fwnode, "reg", &port); + if (ret < 0) { + if (i == con->num) + return fwnode; + } else { + if (port == con->num - 1) + return fwnode; + } + + i++; + } return NULL; } diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 18286d3e9cc5..5697ccc24ec3 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -121,6 +121,33 @@ static const struct ucsi_operations ucsi_acpi_ops = { .async_control = ucsi_acpi_async_control }; +static int ucsi_acer_read_version(struct ucsi *ucsi, u16 *version) +{ + struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); + int ret; + + ret = ucsi_acpi_read_version(ucsi, version); + if (ret) + return ret; + + if (!*version) { + dev_warn(ua->dev, "UCSI version is zero, assuming 1.2\n"); + *version = UCSI_VERSION_1_2; + } + + return 0; +} + +static const struct ucsi_operations ucsi_acer_ops = { + .read_version = ucsi_acer_read_version, + .read_cci = ucsi_acpi_read_cci, + .poll_cci = ucsi_acpi_poll_cci, + .read_message_in = ucsi_acpi_read_message_in, + .write_message_out = ucsi_acpi_write_message_out, + .sync_control = ucsi_sync_control_common, + .async_control = ucsi_acpi_async_control +}; + static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, void *val, size_t len, void *msg_out, size_t msg_out_size) @@ -162,6 +189,13 @@ static const struct ucsi_operations ucsi_gram_ops = { }; static const struct dmi_system_id ucsi_acpi_quirks[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"), + }, + .driver_data = (void *)&ucsi_acer_ops, + }, { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 12e07b9fe622..1db0c88d817e 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -362,6 +362,10 @@ static void pmic_glink_ucsi_destroy(void *data) { struct pmic_glink_ucsi *ucsi = data; + /* Drain the work items before ucsi_destroy() frees the ucsi instance */ + cancel_work_sync(&ucsi->notify_work); + cancel_work_sync(&ucsi->register_work); + /* Protect to make sure we're not in a middle of a transaction from a glink callback */ mutex_lock(&ucsi->lock); ucsi_destroy(ucsi->ucsi); @@ -467,8 +471,15 @@ static void pmic_glink_ucsi_remove(struct auxiliary_device *adev) { struct pmic_glink_ucsi *ucsi = dev_get_drvdata(&adev->dev); - /* Unregister first to stop having read & writes */ - ucsi_unregister(ucsi->ucsi); + /* Callbacks can queue work until devres releases the client */ + disable_work_sync(&ucsi->notify_work); + disable_work_sync(&ucsi->register_work); + + /* register_work may have unregistered the instance already */ + if (ucsi->ucsi_registered) { + ucsi->ucsi_registered = false; + ucsi_unregister(ucsi->ucsi); + } } static const struct auxiliary_device_id pmic_glink_ucsi_id_table[] = { diff --git a/drivers/virt/nitro_enclaves/ne_misc_dev.c b/drivers/virt/nitro_enclaves/ne_misc_dev.c index c91300a73f50..0d7bdad924b7 100644 --- a/drivers/virt/nitro_enclaves/ne_misc_dev.c +++ b/drivers/virt/nitro_enclaves/ne_misc_dev.c @@ -1706,6 +1706,7 @@ static int ne_create_vm_ioctl(struct ne_pci_dev *ne_pci_dev, u64 __user *slot_ui return enclave_fd; put_file: + enclave_file->private_data = NULL; fput(enclave_file); put_fd: put_unused_fd(enclave_fd); diff --git a/drivers/virt/vmgenid.c b/drivers/virt/vmgenid.c index 66135eac3abf..0d269edf283d 100644 --- a/drivers/virt/vmgenid.c +++ b/drivers/virt/vmgenid.c @@ -75,13 +75,16 @@ static int vmgenid_add_acpi(struct device *dev, struct vmgenid_state *state) phys_addr = (obj->package.elements[0].integer.value << 0) | (obj->package.elements[1].integer.value << 32); - virt_addr = devm_memremap(&device->dev, phys_addr, VMGENID_SIZE, MEMREMAP_WB); + virt_addr = devm_memremap(&device->dev, phys_addr, VMGENID_SIZE, + MEMREMAP_WB | MEMREMAP_DEC); if (IS_ERR(virt_addr)) { ret = PTR_ERR(virt_addr); goto out; } setup_vmgenid_state(state, virt_addr); + dev->driver_data = state; + status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, vmgenid_acpi_handler, dev); if (ACPI_FAILURE(status)) { @@ -89,7 +92,6 @@ static int vmgenid_add_acpi(struct device *dev, struct vmgenid_state *state) goto out; } - dev->driver_data = state; out: ACPI_FREE(parsed.pointer); return ret; @@ -123,12 +125,13 @@ static int vmgenid_add_of(struct platform_device *pdev, if (ret < 0) return ret; + pdev->dev.driver_data = state; + ret = devm_request_irq(&pdev->dev, ret, vmgenid_of_irq_handler, IRQF_SHARED, "vmgenid", &pdev->dev); if (ret < 0) return ret; - pdev->dev.driver_data = state; return 0; } diff --git a/fs/exec.c b/fs/exec.c index 834d622777d6..a598d48b5ffc 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -882,6 +882,7 @@ static int exec_mmap(struct linux_binprm *bprm) active_mm = tsk->active_mm; tsk->active_mm = mm; tsk->mm = mm; + sched_cache_exec_mmap(tsk, mm); mm_init_cid(mm, tsk); exec_state = task_exec_state_replace(tsk, exec_state); /* diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index a33629b397b0..32f79f48a9dd 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5287,7 +5287,8 @@ void ext4_set_inode_mapping_order(struct inode *inode) if (!min_order && !S_ISREG(inode->i_mode)) return; - if (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA)) + if (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) || + ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT)) max_order = min_order; mapping_set_folio_order_range(inode->i_mapping, min_order, max_order); diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 105194de6e13..e6506942aeda 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -541,6 +541,13 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) ret = netfs_wait_for_read(rreq); if (ret >= 0) { + if (ret < flen) { + struct iov_iter iter; + + iov_iter_bvec(&iter, ITER_DEST, bvec, i, flen); + iov_iter_advance(&iter, ret); + iov_iter_zero(flen - ret, &iter); + } if (group) folio_change_private(folio, group); else diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 2cdb68e6b16f..ecf119b4f916 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -469,6 +469,7 @@ ssize_t netfs_buffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *fr struct netfs_group *netfs_group) { struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); ssize_t ret; trace_netfs_write_iter(iocb, from); @@ -481,6 +482,14 @@ ssize_t netfs_buffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *fr if (ret) return ret; + if (iocb->ki_pos > i_size_read(inode)) { + ret = netfs_clear_stale_pre_isize(inode, i_size_read(inode), + iocb->ki_pos, + iocb->ki_flags & IOCB_NOWAIT); + if (ret) + return ret; + } + return netfs_perform_write(iocb, from, netfs_group); } EXPORT_SYMBOL(netfs_buffered_write_iter_locked); diff --git a/fs/netfs/direct_write.c b/fs/netfs/direct_write.c index 2361277416c7..8be74706d85e 100644 --- a/fs/netfs/direct_write.c +++ b/fs/netfs/direct_write.c @@ -359,6 +359,15 @@ ssize_t netfs_unbuffered_write_iter(struct kiocb *iocb, struct iov_iter *from) ret = file_update_time(file); if (ret < 0) goto out; + + if (iocb->ki_pos > i_size_read(inode)) { + ret = netfs_clear_stale_pre_isize(inode, i_size_read(inode), + iocb->ki_pos, + iocb->ki_flags & IOCB_NOWAIT); + if (ret < 0) + goto out; + } + if (iocb->ki_flags & IOCB_NOWAIT) { /* We could block if there are any pages in the range. */ ret = -EAGAIN; diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index c79c8e69d60c..786af76da98c 100644 --- a/fs/netfs/internal.h +++ b/fs/netfs/internal.h @@ -80,6 +80,8 @@ ssize_t netfs_wait_for_write(struct netfs_io_request *rreq); void netfs_wait_for_paused_read(struct netfs_io_request *rreq); void netfs_wait_for_paused_write(struct netfs_io_request *rreq); void netfs_wait_for_put_ra_refs(struct netfs_io_request *rreq); +int netfs_clear_stale_pre_isize(struct inode *inode, uoff_t from, + uoff_t to, bool nowait); /* * objects.c diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c index f5c1c463f4ff..523057390d6f 100644 --- a/fs/netfs/misc.c +++ b/fs/netfs/misc.c @@ -6,6 +6,7 @@ */ #include +#include #include "internal.h" /** @@ -582,3 +583,101 @@ void netfs_wait_for_put_ra_refs(struct netfs_io_request *rreq) trace_netfs_rreq(rreq, netfs_rreq_trace_waited_put_ra_refs); finish_wait(&rreq->waitq, &myself); } + +/** + * netfs_clear_stale_isize - Clear stale pagecache in a to-be-created hole + * @inode: The inode to act upon. + * @from: The base of the hole to be made. + * @to: The top of the hole to be made. + * @nowait: True to return -EAGAIN rather than block. + * @exclusive: True if the caller holds i_rwsem exclusively for the resize. + * + * Zero any data left in the pagecache within the [@from, @to) hole by a + * write through an mmap so that it isn't exposed as file content once the + * file is extended. Only the uptodate folio straddling @from can hold such + * data as pages wholly beyond the EOF can't be faulted in, so the zeroing + * is limited to that folio. The folio is zeroed rather than dropped so + * that a concurrent extending write can't lose data. + * + * If @exclusive is false, @from is re-read from i_size and used to clamp + * the zeroed range, for callers that may race with another writer also + * extending the file (eg. multiple buffered writes extending the same file + * under a shared i_rwsem). If @exclusive is true, for callers that hold + * i_rwsem exclusively across the whole resize and have already updated + * i_size to @to, staleness is decided from the folio's dirty state instead: + * since no genuine concurrent buffered writer can be racing, a lockless + * stat() adopting a server-confirmed size mid-resize has no data behind it + * and never dirties the folio, so it can't fool this check into skipping + * the zeroing the way it could fool the @exclusive false clamp. + * + * pagecache_isize_extended() can't be reused here: it is keyed on a + * sub-page block size (a no-op when the block size is >= PAGE_SIZE, as on + * network filesystems), runs after i_size is updated, can't honour + * @nowait, and doesn't wait for writeback. Keep the two in sync if either + * is changed. + * + * Return: 0 on success, or -EAGAIN if @nowait is set and the folio is + * mapped or under writeback and so can't be cleaned without blocking. + */ +static int netfs_clear_stale_isize(struct inode *inode, uoff_t from, + uoff_t to, bool nowait, bool exclusive) +{ + struct address_space *mapping = inode->i_mapping; + fgf_t fgp = FGP_LOCK; + struct folio *folio; + int ret; + + if (from >= to) + return 0; + + if (nowait) + fgp |= FGP_NOWAIT; + + folio = __filemap_get_folio(mapping, from >> PAGE_SHIFT, fgp, 0); + if (IS_ERR(folio)) + return PTR_ERR(folio) == -EAGAIN ? -EAGAIN : 0; + + ret = 0; + if (nowait && (folio_mapped(folio) || folio_test_writeback(folio))) { + ret = -EAGAIN; + goto out; + } + + folio_wait_writeback(folio); + + if (folio_mkclean(folio)) + folio_mark_dirty(folio); + + if (folio_test_uptodate(folio) && + (!exclusive || folio_test_dirty(folio))) { + uoff_t fpos = folio_pos(folio); + + if (!exclusive) + from = umax(from, i_size_read(inode)); + if (from < to && from < fpos + folio_size(folio)) { + size_t end = umin(to - fpos, folio_size(folio)); + size_t offset = from - fpos; + + folio_zero_segment(folio, offset, end); + } + } +out: + folio_unlock(folio); + folio_put(folio); + return ret; +} + +/* Clear stale pagecache before an extending buffered/DIO write. */ +int netfs_clear_stale_pre_isize(struct inode *inode, uoff_t from, + uoff_t to, bool nowait) +{ + return netfs_clear_stale_isize(inode, from, to, nowait, false); +} + +/* Clear stale pagecache when extending a file under an exclusive resize. */ +void netfs_clear_stale_post_isize(struct inode *inode, uoff_t from, + uoff_t to) +{ + netfs_clear_stale_isize(inode, from, to, false, true); +} +EXPORT_SYMBOL(netfs_clear_stale_post_isize); diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c index a94197ef0181..c5bfaf2c6f25 100644 --- a/fs/netfs/read_collect.c +++ b/fs/netfs/read_collect.c @@ -33,6 +33,22 @@ static void netfs_clear_unread(struct netfs_io_subrequest *subreq) __set_bit(NETFS_SREQ_HIT_EOF, &subreq->flags); } +static void netfs_clear_unread_dio(struct netfs_io_subrequest *subreq) +{ + uoff_t pos = subreq->start + subreq->transferred; + struct netfs_io_request *rreq = subreq->rreq; + size_t fill; + + if (pos >= rreq->i_size) + return; + + fill = min_t(uoff_t, rreq->i_size - pos, + subreq->len - subreq->transferred); + + netfs_reset_iter(subreq); + subreq->transferred += iov_iter_zero(fill, &subreq->io_iter); +} + /* * Cancel the copy-to-cache mark on a folio. */ @@ -311,6 +327,14 @@ static void netfs_collect_read_results(struct netfs_io_request *rreq) test_bit(NETFS_SREQ_HIT_EOF, &front->flags)) netfs_read_unlock_folios(rreq, ¬es); } else { + if (!(notes & HIT_PENDING) && + front->error == 0 && + transferred < front->len && + test_bit(NETFS_SREQ_CLEAR_TAIL, &front->flags)) { + netfs_clear_unread_dio(front); + transferred = front->transferred; + trace_netfs_sreq(front, netfs_sreq_trace_clear); + } stream->collected_to = front->start + transferred; rreq->collected_to = stream->collected_to; } diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index cb3a397b109c..6159b44049ec 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -1412,6 +1412,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, * server could even support copy of range where source = target */ lock_two_nondirectories(target_inode, src_inode); + filemap_invalidate_lock(target_inode->i_mapping); if (len == 0) { loff_t src_size = i_size_read(src_inode); @@ -1467,9 +1468,15 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, i_size = target_inode->i_size; spin_unlock(&target_inode->i_lock); - /* Discard all the folios that overlap the destination region. */ + /* + * Discard all the folios that overlap the destination region. Start at + * the old EOF when extending so the folio straddling it, which may hold + * data written past EOF through an mmap, is dropped too. + */ cifs_dbg(FYI, "about to discard pages %llx-%llx\n", fstart, fend); - truncate_inode_pages_range(&target_inode->i_data, fstart, fend); + truncate_inode_pages_range(&target_inode->i_data, + min(fstart, i_size), fend); + netfs_wait_for_outstanding_io(target_inode); fscache_invalidate(cifs_inode_cookie(target_inode), NULL, i_size, 0); @@ -1512,6 +1519,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, if (rc) CIFS_I(target_inode)->time = 0; unlock: + filemap_invalidate_unlock(target_inode->i_mapping); /* although unlocking in the reverse order from locking is not strictly necessary here it is a little cleaner to be consistent */ unlock_two_nondirectories(src_inode, target_inode); @@ -1535,6 +1543,9 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, struct cifs_tcon *target_tcon; ssize_t rc; + if (len == 0) + return 0; + cifs_dbg(FYI, "copychunk range\n"); if (!src_file->private_data || !dst_file->private_data) { @@ -1564,6 +1575,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, * server could even support copy of range where source = target */ lock_two_nondirectories(target_inode, src_inode); + filemap_invalidate_lock(target_inode->i_mapping); cifs_dbg(FYI, "about to flush pages\n"); @@ -1585,10 +1597,28 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, /* Flush and invalidate all the folios in the destination region. If * the copy was successful, then some of the flush is extra overhead, * but we need to allow for the copy failing in some way (eg. ENOSPC). + * + * Start at the old EOF when extending so the folio straddling it, which + * may hold data written past EOF through an mmap, is dropped too. */ - rc = filemap_invalidate_inode(target_inode, true, destoff, destoff + len - 1); - if (rc) - goto unlock; + if (target_inode->i_mapping->nrpages) { + loff_t fstart = min(destoff, i_size_read(target_inode)); + loff_t fend = destoff + len - 1; + + unmap_mapping_pages(target_inode->i_mapping, + fstart >> PAGE_SHIFT, + (fend >> PAGE_SHIFT) - + (fstart >> PAGE_SHIFT) + 1, + false); + rc = filemap_write_and_wait_range(target_inode->i_mapping, + fstart, fend); + if (rc) + goto unlock; + invalidate_inode_pages2_range(target_inode->i_mapping, + fstart >> PAGE_SHIFT, + fend >> PAGE_SHIFT); + } + netfs_wait_for_outstanding_io(target_inode); fscache_invalidate(cifs_inode_cookie(target_inode), NULL, i_size_read(target_inode), 0); @@ -1620,6 +1650,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, CIFS_I(target_inode)->time = 0; unlock: + filemap_invalidate_unlock(target_inode->i_mapping); /* although unlocking in the reverse order from locking is not * strictly necessary here it is a little cleaner to be consistent */ diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 79e4e84f8985..5c5b76a9e9fe 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -1450,11 +1450,11 @@ struct cifsFileInfo { struct dentry *dentry; struct tcon_link *tlink; unsigned int f_flags; - bool invalidHandle:1; /* file closed via session abend */ - bool swapfile:1; - bool oplock_break_cancelled:1; - bool status_file_deleted:1; /* file has been deleted */ - bool offload:1; /* offload final part of _put to a wq */ + bool invalidHandle; /* file closed via session abend */ + bool swapfile; + bool oplock_break_cancelled; + bool status_file_deleted; /* file has been deleted */ + bool offload; /* offload final part of _put to a wq */ __u16 oplock_epoch; /* epoch from the lease break */ __u32 oplock_level; /* oplock/lease level from the lease break */ int count; diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index db797a562156..7e94ada3f17b 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -88,6 +88,8 @@ static void cifs_set_ops(struct inode *inode) else inode->i_data.a_ops = &cifs_addr_ops; mapping_set_large_folios(inode->i_mapping); + if (tcon->ses->server->sign) + mapping_set_stable_writes(inode->i_mapping); break; case S_IFDIR: if (IS_AUTOMOUNT(inode)) { diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 46507f8bc68b..9f02dfe86b3a 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3517,6 +3517,21 @@ static long smb3_zero_data(struct file *file, struct cifs_tcon *tcon, 0, NULL, NULL); } +static long query_server_eof(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, + unsigned long long *eof) +{ + struct smb2_file_all_info file_inf = {}; + long rc; + + rc = SMB2_query_info(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, &file_inf); + if (!rc) + *eof = le64_to_cpu(file_inf.EndOfFile); + return rc; +} + static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, unsigned long long offset, unsigned long long len, bool keep_size) @@ -3544,25 +3559,32 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, filemap_invalidate_lock(inode->i_mapping); netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point); - if (offset + len >= remote_i_size && offset < i_size) { - unsigned long long top = umin(offset + len, i_size); - rc = filemap_write_and_wait_range(inode->i_mapping, offset, top - 1); - if (rc < 0) - goto zero_range_exit; - } + rc = filemap_write_and_wait_range(inode->i_mapping, offset, + offset + len - 1); + if (rc < 0) + goto zero_range_exit; /* * We zero the range through ioctl, so we need remove the page caches * first, otherwise the data may be inconsistent with the server. + * + * Start at the old EOF when extending so the folio straddling it, which + * may hold data written past EOF through an mmap, is dropped too. */ - truncate_pagecache_range(inode, offset, offset + len - 1); + truncate_pagecache_range(inode, min(offset, i_size), offset + len - 1); netfs_wait_for_outstanding_io(inode); - /* if file not oplocked can't be sure whether asking to extend size */ - rc = -EOPNOTSUPP; - if (keep_size == false && !CIFS_CACHE_READ(cifsi)) - goto zero_range_exit; + if (!keep_size && !CIFS_CACHE_READ(cifsi)) { + rc = query_server_eof(xid, tcon, cfile, &remote_i_size); + if (rc) + goto zero_range_exit; + i_size = max(i_size, remote_i_size); + if (i_size < new_size) { + rc = -EOPNOTSUPP; + goto zero_range_exit; + } + } fscache_invalidate(cifs_inode_cookie(inode), NULL, i_size_read(inode), 0); @@ -3574,7 +3596,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, /* * do we also need to change the size of the file? */ - if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) { + if (!keep_size && umax(i_size, i_size_read(inode)) < new_size) { rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, cfile->pid, new_size); if (rc >= 0) { @@ -3720,7 +3742,8 @@ static int smb3_simple_fallocate_write_range(unsigned int xid, static int smb3_simple_fallocate_range(unsigned int xid, struct cifs_tcon *tcon, struct cifsFileInfo *cfile, - loff_t off, loff_t len) + loff_t off, loff_t len, + loff_t old_eof) { struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data; struct inode *inode = d_inode(cfile->dentry); @@ -3736,12 +3759,30 @@ static int smb3_simple_fallocate_range(unsigned int xid, goto out; } - if (off >= i_size_read(inode)) { + if (off >= old_eof) { rc = smb3_simple_fallocate_write_range(xid, tcon, cfile, off, len, buf); goto out; } + filemap_invalidate_lock(inode->i_mapping); + + /* + * Flush and commit the data to the server, otherwise + * FSCTL_QUERY_ALLOCATED_RANGES might report recently written data as + * unallocated holes on Windows Servers, and the loop below would + * then zero-fill them and corrupt the file. + */ + rc = filemap_write_and_wait_range(inode->i_mapping, off, + off + len - 1); + if (rc) + goto out_unlock; + netfs_wait_for_outstanding_io(inode); + rc = SMB2_flush(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid); + if (rc) + goto out_unlock; + in_data.file_offset = cpu_to_le64(off); in_data.length = cpu_to_le64(len); rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, @@ -3751,7 +3792,7 @@ static int smb3_simple_fallocate_range(unsigned int xid, 1024 * sizeof(struct file_allocated_range_buffer), (char **)&out_data, &out_data_len); if (rc) - goto out; + goto out_unlock; tmp_data = out_data; while (len) { @@ -3761,12 +3802,12 @@ static int smb3_simple_fallocate_range(unsigned int xid, if (out_data_len == 0) { rc = smb3_simple_fallocate_write_range(xid, tcon, cfile, off, len, buf); - goto out; + goto out_unlock; } if (out_data_len < sizeof(struct file_allocated_range_buffer)) { rc = -EINVAL; - goto out; + goto out_unlock; } range_start = le64_to_cpu(tmp_data->file_offset); @@ -3774,7 +3815,7 @@ static int smb3_simple_fallocate_range(unsigned int xid, if (check_add_overflow(range_start, range_len, &range_end) || range_end > S64_MAX) { rc = -EINVAL; - goto out; + goto out_unlock; } if (off < range_start) { @@ -3789,11 +3830,11 @@ static int smb3_simple_fallocate_range(unsigned int xid, rc = smb3_simple_fallocate_write_range(xid, tcon, cfile, off, l, buf); if (rc) - goto out; + goto out_unlock; off = off + l; len = len - l; if (len == 0) - goto out; + goto out_unlock; } /* * We are at a section of allocated data, just skip forward @@ -3812,6 +3853,8 @@ static int smb3_simple_fallocate_range(unsigned int xid, out_data_len -= sizeof(struct file_allocated_range_buffer); } + out_unlock: + filemap_invalidate_unlock(inode->i_mapping); out: kfree(out_data); kvfree(buf); @@ -3840,14 +3883,31 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, off, len); - /* if file not oplocked can't be sure whether asking to extend size */ - if (!CIFS_CACHE_READ(cifsi)) - if (!keep_size) { + + if (!keep_size && !CIFS_CACHE_READ(cifsi)) { + unsigned long long server_eof; + + rc = filemap_write_and_wait(inode->i_mapping); + if (rc) { + trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, + tcon->tid, tcon->ses->Suid, off, len, rc); + free_xid(xid); + return rc; + } + netfs_wait_for_outstanding_io(inode); + + rc = query_server_eof(xid, tcon, cfile, &server_eof); + if (rc) { trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, off, len, rc); free_xid(xid); return rc; } + /* + * Only use the larger EOF to decide whether we're extending. + */ + old_eof = max_t(loff_t, old_eof, server_eof); + } /* * Extending the file @@ -3871,7 +3931,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, } rc = smb3_simple_fallocate_range(xid, tcon, cfile, - off, len); + off, len, old_eof); if (rc) { spin_lock(&inode->i_lock); cifsi->time = 0; @@ -3973,7 +4033,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, } } - if ((keep_size == true) || (i_size_read(inode) >= off + len)) { + if (keep_size || old_eof >= off + len) { /* * At this point, we are trying to fallocate an internal * regions of a sparse file. Since smb2 does not have a @@ -3990,7 +4050,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, */ if (len <= 1024 * 1024) { rc = smb3_simple_fallocate_range(xid, tcon, cfile, - off, len); + off, len, old_eof); goto out; } @@ -4002,7 +4062,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, * ie potentially making a few extra pages at the beginning * or end of the file non-sparse via set_sparse is harmless. */ - if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) { + if (off > 8192 || off + len + 8192 < old_eof) { rc = -EOPNOTSUPP; goto out; } diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 3d7ead36d1a0..cfe3c4b4abe7 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4796,14 +4796,20 @@ smb2_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) rdata->got_bytes); if (rdata->result == -ENODATA) { - __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); rdata->result = 0; + if (rdata->subreq.start + rdata->subreq.transferred >= i_size_read(inode)) + __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); + else + __set_bit(NETFS_SREQ_CLEAR_TAIL, &rdata->subreq.flags); } else { size_t trans = rdata->subreq.transferred + rdata->got_bytes; if (trans < rdata->subreq.len && rdata->subreq.start + trans >= netfs_read_remote_i_size(inode)) { - __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); rdata->result = 0; + if (rdata->subreq.start + trans >= i_size_read(inode)) + __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); + else + __set_bit(NETFS_SREQ_CLEAR_TAIL, &rdata->subreq.flags); } if (rdata->got_bytes) __set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags); diff --git a/include/linux/bpf.h b/include/linux/bpf.h index c2c327dc0a72..3ad3c2038c40 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1255,11 +1255,15 @@ struct btf_func_model { #define BPF_TRAMP_F_INDIRECT BIT(8) /* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50 - * bytes on x86. + * bytes on x86. The trampoline image has to fit in PAGE_SIZE. */ enum { -#if defined(__s390x__) +#if defined(__s390x__) || defined(__powerpc64__) BPF_MAX_TRAMP_LINKS = 27, +#elif defined(__x86_64__) + BPF_MAX_TRAMP_LINKS = 36, +#elif defined(__aarch64__) + BPF_MAX_TRAMP_LINKS = 37, #else BPF_MAX_TRAMP_LINKS = 38, #endif @@ -1303,6 +1307,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i void *arch_alloc_bpf_trampoline(unsigned int size); void arch_free_bpf_trampoline(void *image, unsigned int size); int __must_check arch_protect_bpf_trampoline(void *image, unsigned int size); +int arch_bpf_trampoline_skip(void *nop, void *target); int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, struct bpf_tramp_nodes *tnodes, void *func_addr); @@ -1351,19 +1356,48 @@ enum bpf_tramp_prog_type { BPF_TRAMP_FSESSION, }; +/* + * Each prog call in a trampoline image is preceded by a nop. When the prog is + * detached, the nop is patched to a jump to target, right after the call, so + * that tasks still running in the image skip the prog. + */ +struct bpf_tramp_skip { + struct bpf_prog *prog; + void *nop; + void *target; +}; + struct bpf_tramp_image { void *image; int size; struct bpf_ksym ksym; struct percpu_ref pcref; - void *ip_after_call; - void *ip_epilogue; + bool call_orig; + /* entry in tr->images, the image holds a reference on tr */ + struct bpf_trampoline *tr; + struct list_head list; + int nr_skips; + struct bpf_tramp_skip *skips; union { struct rcu_head rcu; struct work_struct work; }; }; +static inline void bpf_tramp_image_add_skip(struct bpf_tramp_image *im, struct bpf_prog *prog, + void *nop, void *target) +{ + struct bpf_tramp_skip *skip; + + /* struct_ops trampolines and arch_bpf_trampoline_size() have no image */ + if (!im || !im->skips) + return; + skip = &im->skips[im->nr_skips++]; + skip->prog = prog; + skip->nop = nop; + skip->target = target; +} + struct bpf_trampoline { /* hlist for trampoline_key_table */ struct hlist_node hlist_key; @@ -1390,6 +1424,8 @@ struct bpf_trampoline { int progs_cnt[BPF_TRAMP_MAX]; /* Executable image of trampoline */ struct bpf_tramp_image *cur_image; + /* Images not freed yet, cur_image and older ones still in use */ + struct list_head images; /* Used as temporary old image storage for multi_attach */ struct { struct bpf_tramp_image *old_image; @@ -1758,6 +1794,7 @@ struct bpf_prog_aux { bool offload_requested; /* Program is bound and offloaded to the netdev. */ bool attach_btf_trace; /* true if attaching to BTF-enabled raw tp */ bool attach_tracing_prog; /* true if tracing another tracing program */ + bool tramp_linked; /* true if it was ever linked to a trampoline */ bool func_proto_unreliable; bool tail_call_reachable; bool xdp_has_frags; diff --git a/include/linux/hrtimer_rearm.h b/include/linux/hrtimer_rearm.h index a6f2e5d5e1c7..666ced5485f1 100644 --- a/include/linux/hrtimer_rearm.h +++ b/include/linux/hrtimer_rearm.h @@ -40,7 +40,7 @@ hrtimer_rearm_deferred_user_irq(unsigned long *tif_work, const unsigned long tif clear_thread_flag(TIF_HRTIMER_REARM); __hrtimer_rearm_deferred(); /* Don't go into the loop if HRTIMER_REARM was the only flag */ - *tif_work &= ~TIF_HRTIMER_REARM; + *tif_work &= ~_TIF_HRTIMER_REARM; return !*tif_work; } return false; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 09c8c93e4dba..6936180b6ea5 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -305,6 +305,8 @@ int nand_ecc_prepare_io_req(struct nand_device *nand, struct nand_page_io_req *req); int nand_ecc_finish_io_req(struct nand_device *nand, struct nand_page_io_req *req); +bool nand_ecc_is_pipelined(const struct nand_device *nand); + bool nand_ecc_is_strong_enough(struct nand_device *nand); #if IS_REACHABLE(CONFIG_MTD_NAND_CORE) diff --git a/include/linux/netfilter_netdev.h b/include/linux/netfilter_netdev.h index 3175073a66ba..2a854a0bbd4b 100644 --- a/include/linux/netfilter_netdev.h +++ b/include/linux/netfilter_netdev.h @@ -133,7 +133,7 @@ static inline struct sk_buff *nf_hook_egress(struct sk_buff *skb, int *rc, static inline void nf_skip_egress(struct sk_buff *skb, bool skip) { -#ifdef CONFIG_NETFILTER_SKIP_EGRESS +#ifdef CONFIG_NET_EGRESS skb->nf_skip_egress = skip; #endif } diff --git a/include/linux/netfs.h b/include/linux/netfs.h index 67c2d9ef7c62..afde9e0f3ec2 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -406,6 +406,8 @@ ssize_t netfs_unbuffered_write_iter(struct kiocb *iocb, struct iov_iter *from); ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *iter, struct netfs_group *netfs_group); ssize_t netfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from); +void netfs_clear_stale_post_isize(struct inode *inode, uoff_t from, + uoff_t to); /* Single, monolithic object read/write API. */ void netfs_single_mark_inode_dirty(struct inode *inode); diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 310681cccb50..858c59b03145 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1506,9 +1506,10 @@ is_default_overflow_handler(struct perf_event *event) } extern void -perf_event_header__init_id(struct perf_event_header *header, - struct perf_sample_data *data, - struct perf_event *event); +perf_event_header__init(struct perf_event_header *header, + struct perf_sample_data *data, + u32 type, u16 misc, u16 size, + struct perf_event *event); extern void perf_event__output_id_sample(struct perf_event *event, struct perf_output_handle *handle, diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index ea39dd23a197..077d2b486de0 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -178,7 +178,12 @@ void net_dec_ingress_queue(void); #ifdef CONFIG_NET_EGRESS void net_inc_egress_queue(void); void net_dec_egress_queue(void); -void netdev_xmit_skip_txqueue(bool skip); +bool netdev_xmit_skip_txqueue(bool skip); +#else +static inline bool netdev_xmit_skip_txqueue(bool skip) +{ + return false; +} #endif void rtnetlink_init(void); diff --git a/include/linux/sched.h b/include/linux/sched.h index 1713fb6b33ed..5b36a3364ca0 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1421,6 +1421,7 @@ struct task_struct { #ifdef CONFIG_SCHED_CACHE struct callback_head cache_work; + struct sched_cache_group __rcu *sched_cache_grp; int preferred_llc; /* 1: task was enqueued to its preferred LLC, 0 otherwise */ int pref_llc_queued; @@ -2410,10 +2411,23 @@ struct sched_cache_group { struct rcu_head rcu; } ____cacheline_aligned_in_smp; +struct sched_cache_group *sched_cache_group_get(struct sched_cache_group *grp); +struct sched_cache_group *task_cache_group_get(struct task_struct *p); + +void sched_cache_fork(struct task_struct *p); +void sched_cache_fork_cleanup(struct task_struct *p); +void sched_cache_exec_mmap(struct task_struct *p, struct mm_struct *mm); +void sched_cache_exit_mm(struct task_struct *p); + #else struct sched_cache_group { }; +static inline void sched_cache_fork(struct task_struct *p) { } +static inline void sched_cache_fork_cleanup(struct task_struct *p) { } +static inline void sched_cache_exec_mmap(struct task_struct *p, struct mm_struct *mm) { } +static inline void sched_cache_exit_mm(struct task_struct *p) { } + #endif #ifndef MODULE diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index ce125cd24443..a9f4d2cba64f 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1020,7 +1020,7 @@ struct sk_buff { #ifdef CONFIG_NET_REDIRECT __u8 from_ingress:1; #endif -#ifdef CONFIG_NETFILTER_SKIP_EGRESS +#ifdef CONFIG_NET_EGRESS __u8 nf_skip_egress:1; #endif #ifdef CONFIG_SKB_DECRYPTED diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h index 0991ef7ec092..6fc08751d9c4 100644 --- a/include/linux/soc/qcom/geni-se.h +++ b/include/linux/soc/qcom/geni-se.h @@ -309,17 +309,12 @@ struct geni_se { #define QUP_SE_VERSION_2_5 0x20050000 /* - * Define bandwidth thresholds that cause the underlying Core 2X interconnect - * clock to run at the named frequency. These baseline values are recommended - * by the hardware team, and are not dynamically scaled with GENI bandwidth - * beyond basic on/off. + * QUP Core 2X clock votes used by GENI clients through the "qup-core" ICC + * path. Values are in Bps and must be converted with Bps_to_icc() before + * setting avg_bw. */ -#define CORE_2X_19_2_MHZ 960 -#define CORE_2X_50_MHZ 2500 -#define CORE_2X_100_MHZ 5000 -#define CORE_2X_150_MHZ 7500 -#define CORE_2X_200_MHZ 10000 -#define CORE_2X_236_MHZ 16383 +#define CORE_2X_19_2_MHZ 9600000 +#define CORE_2X_50_MHZ 25000000 #define GENI_DEFAULT_BW Bps_to_icc(1000) diff --git a/include/linux/tty.h b/include/linux/tty.h index 0a46e4054dec..ad4e4a835f90 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -230,6 +230,7 @@ struct tty_struct { struct fasync_struct *fasync; wait_queue_head_t write_wait; wait_queue_head_t read_wait; + wait_queue_head_t break_wait; struct work_struct hangup_work; void *disc_data; void *driver_data; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 1f2896e56e77..62e459c5d3ae 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -73,6 +73,11 @@ struct serial_struct; * @TTY_DRIVER_NO_WORKQUEUE: * Do not create workqueue when tty_register_driver(). Whenever set, flip * buffer workqueue can be set by tty_port_link_wq() for every port. + * + * @TTY_DRIVER_RESET_SAVED_TERMIOS + * Reset any saved termios settings on device registration when reusing a + * minor number. Must only be set by drivers that guarantee that the minor + * number is no longer in use. */ enum tty_driver_flag { TTY_DRIVER_INSTALLED = BIT(0), @@ -84,6 +89,7 @@ enum tty_driver_flag { TTY_DRIVER_DYNAMIC_ALLOC = BIT(6), TTY_DRIVER_UNNUMBERED_NODE = BIT(7), TTY_DRIVER_NO_WORKQUEUE = BIT(8), + TTY_DRIVER_RESET_SAVED_TERMIOS = BIT(9), }; enum tty_driver_type { diff --git a/include/linux/tty_port.h b/include/linux/tty_port.h index 23cad403bb8f..8d22c59c6f15 100644 --- a/include/linux/tty_port.h +++ b/include/linux/tty_port.h @@ -245,7 +245,8 @@ bool tty_port_carrier_raised(struct tty_port *port); void tty_port_raise_dtr_rts(struct tty_port *port); void tty_port_lower_dtr_rts(struct tty_port *port); void tty_port_hangup(struct tty_port *port); -void __tty_port_tty_hangup(struct tty_port *port, bool check_clocal, bool async); +void tty_port_tty_hangup(struct tty_port *port, bool check_clocal); +void tty_port_tty_vhangup(struct tty_port *port); void tty_port_tty_wakeup(struct tty_port *port); int tty_port_block_til_ready(struct tty_port *port, struct tty_struct *tty, struct file *filp); @@ -264,25 +265,6 @@ static inline int tty_port_users(struct tty_port *port) return port->count + port->blocked_open; } -/** - * tty_port_tty_hangup - helper to hang up a tty asynchronously - * @port: tty port - * @check_clocal: hang only ttys with %CLOCAL unset? - */ -static inline void tty_port_tty_hangup(struct tty_port *port, bool check_clocal) -{ - __tty_port_tty_hangup(port, check_clocal, true); -} - -/** - * tty_port_tty_vhangup - helper to hang up a tty synchronously - * @port: tty port - */ -static inline void tty_port_tty_vhangup(struct tty_port *port) -{ - __tty_port_tty_hangup(port, false, false); -} - #ifdef CONFIG_TTY void tty_kref_put(struct tty_struct *tty); __DEFINE_CLASS_IS_CONDITIONAL(tty_port_tty, true); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 534e6650e2aa..ab0a32a90629 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -381,6 +381,7 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port, int usb_serial_bus_register(struct usb_serial_driver *device); void usb_serial_bus_deregister(struct usb_serial_driver *device); +void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver); extern const struct bus_type usb_serial_bus_type; extern struct tty_driver *usb_serial_tty_driver; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8115435b5d2a..9084fcd7125a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -62,7 +62,7 @@ struct inquiry_entry { NAME_PENDING, NAME_KNOWN, } name_state; - __u32 timestamp; + unsigned long timestamp; struct inquiry_data data; }; @@ -78,7 +78,7 @@ struct discovery_state { struct list_head all; /* All devices found during inquiry */ struct list_head unknown; /* Name state not known */ struct list_head resolve; /* Name needs to be resolved */ - __u32 timestamp; + unsigned long timestamp; bdaddr_t last_adv_addr; u8 last_adv_addr_type; s8 last_adv_rssi; @@ -561,6 +561,7 @@ struct hci_dev { struct list_head link_keys; struct list_head long_term_keys; struct list_head identity_resolving_keys; + struct mutex remote_oob_lock; struct list_head remote_oob_data; struct list_head le_accept_list; struct list_head le_resolv_list; diff --git a/include/net/gso.h b/include/net/gso.h index 29975440cad5..0749230d414e 100644 --- a/include/net/gso.h +++ b/include/net/gso.h @@ -19,10 +19,18 @@ struct skb_gso_cb { int encap_level; __wsum csum; __u16 csum_start; + /* Number of IPv4/IPv6 GSO handler entries for this packet. */ + u8 recursion_counter; }; #define SKB_GSO_CB_OFFSET 32 #define SKB_GSO_CB(skb) ((struct skb_gso_cb *)((skb)->cb + SKB_GSO_CB_OFFSET)) +static inline bool gso_recursion_inc_test(struct sk_buff *skb, + unsigned int limit) +{ + return ++SKB_GSO_CB(skb)->recursion_counter > limit; +} + static inline int skb_tnl_header_len(const struct sk_buff *inner_skb) { return (skb_mac_header(inner_skb) - inner_skb->head) - diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index ce414118962f..d69cadd53bc3 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -183,10 +183,10 @@ enum nf_flow_flags { NF_FLOW_DNAT, NF_FLOW_CLOSING, NF_FLOW_TEARDOWN, + NF_FLOW_PENDING, NF_FLOW_HW, NF_FLOW_HW_DYING, NF_FLOW_HW_DEAD, - NF_FLOW_HW_PENDING, NF_FLOW_HW_BIDIRECTIONAL, NF_FLOW_HW_ESTABLISHED, }; diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index f35bd06a6bad..1acaadb3e2cf 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -810,6 +810,15 @@ static inline bool skb_at_tc_ingress(const struct sk_buff *skb) #endif } +static inline bool skb_at_tc_egress(const struct sk_buff *skb) +{ +#ifdef CONFIG_NET_EGRESS + return skb->nf_skip_egress && !skb_at_tc_ingress(skb); +#else + return false; +#endif +} + static inline bool skb_skip_tc_classify(struct sk_buff *skb) { #ifdef CONFIG_NET_CLS_ACT diff --git a/include/net/xdp.h b/include/net/xdp.h index aa742f413c35..07231adfb5f8 100644 --- a/include/net/xdp.h +++ b/include/net/xdp.h @@ -106,6 +106,11 @@ struct xdp_buff { }; }; +static __always_inline void xdp_reinit_buff(struct xdp_buff *xdp) +{ + xdp->flags = 0; +} + static __always_inline bool xdp_buff_has_frags(const struct xdp_buff *xdp) { return !!(xdp->flags & XDP_FLAGS_HAS_FRAGS); diff --git a/include/sound/core.h b/include/sound/core.h index 404785b7d885..373a91ac3dfd 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -340,6 +340,15 @@ static inline void snd_card_unref(struct snd_card *card) DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T)) +/* + * For automatic error handling at probe time, assign the card like: + * struct snd_card *card __free(snd_card_free) = NULL; + * then create and process as usual. + * But, don't forget to clear to NULL at successful return for avoiding + * the unexpected cleanup! + */ +DEFINE_FREE(snd_card_free, struct snd_card *, if (_T) snd_card_free(_T)) + #define snd_card_set_dev(card, devptr) ((card)->dev = (devptr)) /* device.c */ diff --git a/include/uapi/linux/random.h b/include/uapi/linux/random.h index 1dd047ec98a1..ef6614d9c704 100644 --- a/include/uapi/linux/random.h +++ b/include/uapi/linux/random.h @@ -58,7 +58,7 @@ struct rand_pool_info { /** * struct vgetrandom_opaque_params - arguments for allocating memory for vgetrandom * - * @size_per_opaque_state: Size of each state that is to be passed to vgetrandom(). + * @size_of_opaque_state: Size of each state passed to vgetrandom(). * @mmap_prot: Value of the prot argument in mmap(2). * @mmap_flags: Value of the flags argument in mmap(2). * @reserved: Reserved for future use. diff --git a/init/Kconfig b/init/Kconfig index 10f2013b5321..570cc72aad36 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -173,6 +173,11 @@ config CC_HAS_ALLOC_TOKEN config CC_HAS_MULTIDIMENSIONAL_NONSTRING def_bool $(success,echo 'char tag[][4] __attribute__((__nonstring__)) = { };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror) +config CC_OPT_INLINE_MEMSET + string + default "-finline-stringops=memset" if $(cc-option,-finline-stringops=memset) + default "-mllvm -max-store-memset=4294967294" if $(cc-option,-mllvm -max-store-memset=4294967294) + config LD_CAN_USE_KEEP_IN_OVERLAY # ld.lld prior to 21.0.0 did not support KEEP within an overlay description # https://github.com/llvm/llvm-project/pull/130661 diff --git a/io_uring/cmd_net.c b/io_uring/cmd_net.c index 7cd411fc4f33..90d4ec7cc761 100644 --- a/io_uring/cmd_net.c +++ b/io_uring/cmd_net.c @@ -69,8 +69,8 @@ static inline int io_uring_cmd_setsockopt(struct socket *sock, optlen); } -static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk, - struct sk_buff *skb, unsigned issue_flags) +static int io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk, + struct sk_buff *skb, unsigned int issue_flags) { struct sock_exterr_skb *serr = SKB_EXT_ERR(skb); struct io_uring_cqe cqe[2]; @@ -83,7 +83,7 @@ static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk, ret = skb_get_tx_timestamp(skb, sk, &ts); if (ret < 0) - return false; + return ret; tskey = serr->ee.ee_data; tstype = serr->ee.ee_info; @@ -98,7 +98,9 @@ static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk, iots = (struct io_timespec *)&cqe[1]; iots->tv_sec = ts.tv_sec; iots->tv_nsec = ts.tv_nsec; - return io_uring_cmd_post_mshot_cqe32(cmd, issue_flags, cqe); + if (!io_uring_cmd_post_mshot_cqe32(cmd, issue_flags, cqe)) + return -ENOBUFS; + return 0; } static int io_uring_cmd_timestamp(struct socket *sock, @@ -135,7 +137,8 @@ static int io_uring_cmd_timestamp(struct socket *sock, skb = skb_peek(&list); if (!skb) break; - if (!io_process_timestamp_skb(cmd, sk, skb, issue_flags)) + ret = io_process_timestamp_skb(cmd, sk, skb, issue_flags); + if (ret) break; __skb_dequeue(&list); consume_skb(skb); @@ -145,6 +148,12 @@ static int io_uring_cmd_timestamp(struct socket *sock, scoped_guard(spinlock_irqsave, &q->lock) skb_queue_splice(&list, q); } + /* + * Aux CQEs cannot overflow and the poll is edge triggered, so nothing + * re-runs the command once the CQ drains. End the multishot instead. + */ + if (ret == -ENOBUFS) + return -ENOBUFS; return -EAGAIN; } diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index 6b7db9ab681b..c5404fbe3f7e 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -2412,8 +2412,8 @@ static __cold void io_ring_exit_work(struct work_struct *work) spin_lock(&ctx->completion_lock); spin_unlock(&ctx->completion_lock); - /* pairs with RCU read section in io_req_local_work_add() */ - if (ctx->flags & IORING_SETUP_DEFER_TASKRUN) + /* pairs with the RCU read sections in the task_work add paths */ + if (ctx->flags & (IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SQPOLL)) synchronize_rcu(); io_ring_ctx_free(ctx); diff --git a/io_uring/tw.c b/io_uring/tw.c index f573bcc3af6a..f9affd68dd26 100644 --- a/io_uring/tw.c +++ b/io_uring/tw.c @@ -210,6 +210,9 @@ void io_req_normal_work_add(struct io_kiocb *req) struct io_uring_task *tctx = req->tctx; struct io_ring_ctx *ctx = req->ctx; + /* SQPOLL can retire the request on push, see io_ring_exit_work() */ + guard(rcu)(); + /* tw run already pending, nothing else to do */ if (!mpscq_push(&tctx->task_list, &req->io_task_work.node)) return; diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index f1464ea8ca64..1b6832f6e10f 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -340,6 +340,7 @@ struct io_zcrx_args { struct io_kiocb *req; struct io_zcrx_ifq *ifq; unsigned nr_skbs; + bool stopped_early; }; static const struct memory_provider_ops io_uring_pp_zc_ops; @@ -1727,6 +1728,9 @@ io_zcrx_recv_skb(read_descriptor_t *desc, struct sk_buff *skb, } out: + /* Bytes left in len mean an error stopped the walk early. */ + if (len) + args->stopped_early = true; if (offset == start_off) return ret; desc->count -= (offset - start_off); @@ -1763,8 +1767,9 @@ static int io_zcrx_tcp_recvmsg(struct io_kiocb *req, struct io_zcrx_ifq *ifq, ret = -ENOTCONN; else ret = -EAGAIN; - } else if (unlikely(args.nr_skbs > IO_SKBS_PER_CALL_LIMIT) && - (issue_flags & IO_URING_F_MULTISHOT)) { + } else if ((issue_flags & IO_URING_F_MULTISHOT) && + (unlikely(args.nr_skbs > IO_SKBS_PER_CALL_LIMIT) || + args.stopped_early)) { ret = IOU_REQUEUE; } else if (sock_flag(sk, SOCK_DONE)) { /* Make it to retry until it finally gets 0. */ diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 1ed19b775912..f2e81be8265e 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -545,22 +545,38 @@ static void kill_rules(struct audit_context *context, struct audit_tree *tree) { struct audit_krule *rule, *next; struct audit_entry *entry; + bool need_sync = false; list_for_each_entry_safe(rule, next, &tree->rules, rlist) { entry = container_of(rule, struct audit_entry, rule); - list_del_init(&rule->rlist); if (rule->tree) { /* not a half-baked one */ audit_tree_log_remove_rule(context, rule); - if (entry->rule.exe) - audit_remove_mark(entry->rule.exe); rule->tree = NULL; list_del_rcu(&entry->list); list_del(&entry->rule.list); - call_rcu(&entry->rcu, audit_free_rule_rcu); + if (entry->rule.exe) + need_sync = true; + } else { + list_del_init(&rule->rlist); } } + + if (list_empty(&tree->rules)) + return; + + if (need_sync) + synchronize_rcu(); + + list_for_each_entry_safe(rule, next, &tree->rules, rlist) { + entry = container_of(rule, struct audit_entry, rule); + + list_del_init(&rule->rlist); + if (entry->rule.exe) + audit_remove_mark(entry->rule.exe); + call_rcu(&entry->rcu, audit_free_rule_rcu); + } } /* diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 71ee96a77ae2..5ec94c370872 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1355,7 +1355,7 @@ static int bpf_jit_blind_insn(const struct bpf_insn *from, { struct bpf_insn *to = to_buff; u32 imm_rnd = get_random_u32(); - s16 off; + int off; BUILD_BUG_ON(BPF_REG_PARAMS + 2 != MAX_BPF_JIT_REG); BUILD_BUG_ON(BPF_REG_AX + 1 != MAX_BPF_JIT_REG); @@ -1431,6 +1431,8 @@ static int bpf_jit_blind_insn(const struct bpf_insn *from, off = from->off; if (off < 0) off -= 2; + if (off < S16_MIN) + return -ERANGE; *to++ = BPF_ALU64_IMM(BPF_MOV, BPF_REG_AX, imm_rnd ^ from->imm); *to++ = BPF_ALU64_IMM(BPF_XOR, BPF_REG_AX, imm_rnd); *to++ = BPF_JMP_REG(from->code, from->dst_reg, BPF_REG_AX, off); @@ -1451,6 +1453,8 @@ static int bpf_jit_blind_insn(const struct bpf_insn *from, off = from->off; if (off < 0) off -= 2; + if (off < S16_MIN) + return -ERANGE; *to++ = BPF_ALU32_IMM(BPF_MOV, BPF_REG_AX, imm_rnd ^ from->imm); *to++ = BPF_ALU32_IMM(BPF_XOR, BPF_REG_AX, imm_rnd); *to++ = BPF_JMP32_REG(from->code, from->dst_reg, BPF_REG_AX, @@ -1599,7 +1603,9 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bp if (!rewritten) continue; - if (env) + if (rewritten < 0) + tmp = ERR_PTR(rewritten); + else if (env) tmp = bpf_patch_insn_data(env, i, insn_buff, rewritten); else tmp = bpf_patch_insn_single(clone, i, insn_buff, rewritten); diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 61ef6191f79a..84cfeb9270d1 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -128,6 +128,7 @@ struct htab_elem { struct htab_btf_record { struct btf_record *record; + struct btf *btf; u32 key_size; }; @@ -497,8 +498,13 @@ static void htab_dtor_ctx_free(void *ctx) { struct htab_btf_record *hrec = ctx; + /* + * The duplicated record still points into the map BTF, so free it + * before dropping the reference that keeps that BTF alive. + */ btf_record_free(hrec->record); - kfree(ctx); + btf_put(hrec->btf); + kfree(hrec); } static int bpf_ma_set_dtor(struct bpf_map *map, struct bpf_mem_alloc *ma, @@ -521,6 +527,15 @@ static int bpf_ma_set_dtor(struct bpf_map *map, struct bpf_mem_alloc *ma, kfree(hrec); return err; } + /* + * btf_record_dup() only acquires kernel and module BTF. Fields whose + * types live in the map BTF keep pointing into it: kptrs to local + * types refer to map->btf, and graph roots carry a value record owned + * by its struct meta table. The context can outlive the map when the + * allocator defers its teardown, so hold a reference of our own. + */ + hrec->btf = map->btf; + btf_get(hrec->btf); bpf_mem_alloc_set_dtor(ma, dtor, htab_dtor_ctx_free, hrec); return 0; } @@ -3361,8 +3376,10 @@ static int __rhtab_map_lookup_and_delete_batch(struct bpf_map *map, } if (do_delete) { + migrate_disable(); for (i = 0; i < total; i++) rhtab_delete_elem(rhtab, del_elems[i], NULL, 0); + migrate_enable(); } rcu_read_unlock(); diff --git a/kernel/bpf/memalloc.c b/kernel/bpf/memalloc.c index 8a8f088e83e6..15684d0fc883 100644 --- a/kernel/bpf/memalloc.c +++ b/kernel/bpf/memalloc.c @@ -118,6 +118,11 @@ struct bpf_mem_cache { struct llist_head free_by_rcu_ttrace; struct llist_head waiting_for_gp_ttrace; struct rcu_head rcu_ttrace; + /* + * 0 - idle + * 1 - __free_rcu() is queued + * 2 - __free_rcu() is queued and free_by_rcu_ttrace got more objects since + */ atomic_t call_rcu_ttrace_in_progress; raw_spinlock_t lock; }; @@ -276,6 +281,8 @@ static int free_all(struct bpf_mem_cache *c, struct llist_node *llnode, bool per return cnt; } +static void __do_call_rcu_ttrace(struct bpf_mem_cache *c); + static void __free_rcu(struct rcu_head *head) { struct bpf_mem_cache *c = container_of(head, struct bpf_mem_cache, rcu_ttrace); @@ -285,7 +292,19 @@ static void __free_rcu(struct rcu_head *head) llnode = llist_del_all(&c->waiting_for_gp_ttrace); free_all(c, llnode, !!c->percpu_size); - atomic_set(&c->call_rcu_ttrace_in_progress, 0); + + /* + * do_call_rcu_ttrace() that ran while GP was in flight left its objects + * in free_by_rcu_ttrace. This cache may never free or alloc in bulk + * again, so start the next GP from here. + * 'c' can be freed as soon as call_rcu_ttrace_in_progress is zero. + */ + if (atomic_cmpxchg(&c->call_rcu_ttrace_in_progress, 1, 0) == 1) + return; + + /* Pairs with synchronize_rcu() in free_mem_alloc() */ + guard(rcu)(); + __do_call_rcu_ttrace(c); } static void enque_to_free(struct bpf_mem_cache *c, void *obj) @@ -298,18 +317,15 @@ static void enque_to_free(struct bpf_mem_cache *c, void *obj) llist_add(llnode, &c->free_by_rcu_ttrace); } -static void do_call_rcu_ttrace(struct bpf_mem_cache *c) +static void __do_call_rcu_ttrace(struct bpf_mem_cache *c) { struct llist_node *llnode, *t; - if (atomic_xchg(&c->call_rcu_ttrace_in_progress, 1)) { - if (unlikely(READ_ONCE(c->draining))) { - scoped_guard(raw_spinlock_irqsave, &c->lock) - llnode = llist_del_all(&c->free_by_rcu_ttrace); - free_all(c, llnode, !!c->percpu_size); - } - return; - } + /* + * Must be done before llist_del_all(). Objects that it misses were + * added by do_call_rcu_ttrace() that will set 2 after this store. + */ + atomic_set(&c->call_rcu_ttrace_in_progress, 1); WARN_ON_ONCE(!llist_empty(&c->waiting_for_gp_ttrace)); llist_for_each_safe(llnode, t, llist_del_all(&c->free_by_rcu_ttrace)) @@ -328,6 +344,22 @@ static void do_call_rcu_ttrace(struct bpf_mem_cache *c) call_rcu_tasks_trace(&c->rcu_ttrace, __free_rcu); } +static void do_call_rcu_ttrace(struct bpf_mem_cache *c) +{ + struct llist_node *llnode; + + if (atomic_xchg(&c->call_rcu_ttrace_in_progress, 2)) { + if (unlikely(READ_ONCE(c->draining))) { + scoped_guard(raw_spinlock_irqsave, &c->lock) + llnode = llist_del_all(&c->free_by_rcu_ttrace); + free_all(c, llnode, !!c->percpu_size); + } + return; + } + + __do_call_rcu_ttrace(c); +} + static void free_bulk(struct bpf_mem_cache *c) { struct bpf_mem_cache *tgt = c->tgt; @@ -700,7 +732,12 @@ static void free_mem_alloc(struct bpf_mem_alloc *ma) * to wait for the pending __free_by_rcu(), and __free_rcu(). RCU Tasks * Trace grace period implies RCU grace period, so all __free_rcu don't * need extra call_rcu() (and thus extra rcu_barrier() here). + * + * __free_rcu() queues itself again unless it sees 'draining'. After + * synchronize_rcu() it either did that already or will not do it, so + * rcu_barrier_tasks_trace() cannot miss it. */ + synchronize_rcu(); rcu_barrier(); /* wait for __free_by_rcu */ rcu_barrier_tasks_trace(); /* wait for __free_rcu */ free_mem_alloc_no_barrier(ma); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 77b3252270f3..e92e251e67a9 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2445,6 +2445,21 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu) bpf_prog_free(aux->prog); } +/* + * Progs called from a trampoline can also be reached by a task that was + * preempted in the trampoline before the prog's enter helper took its RCU + * read lock, wait for those first. + */ +static void __bpf_prog_put_rcu_tasks(struct rcu_head *rcu) +{ + struct bpf_prog *prog = container_of(rcu, struct bpf_prog_aux, rcu)->prog; + + if (prog->sleepable) + call_rcu_tasks_trace(rcu, __bpf_prog_put_rcu); + else + call_rcu(rcu, __bpf_prog_put_rcu); +} + static void __bpf_prog_put_noref(struct bpf_prog *prog, bool deferred) { bpf_prog_kallsyms_del_all(prog); @@ -2458,7 +2473,9 @@ static void __bpf_prog_put_noref(struct bpf_prog *prog, bool deferred) btf_put(prog->aux->attach_btf); if (deferred) { - if (prog->sleepable) + if (IS_ENABLED(CONFIG_TASKS_RCU) && prog->aux->tramp_linked) + call_rcu_tasks(&prog->aux->rcu, __bpf_prog_put_rcu_tasks); + else if (prog->sleepable) call_rcu_tasks_trace(&prog->aux->rcu, __bpf_prog_put_rcu); else call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu); diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 02c208bd2b63..b8812dbb3174 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -401,6 +401,7 @@ static struct bpf_trampoline *bpf_trampoline_lookup(u64 key, unsigned long ip) head = &trampoline_ip_table[hash_64(tr->ip, TRAMPOLINE_HASH_BITS)]; hlist_add_head(&tr->hlist_ip, head); refcount_set(&tr->refcnt, 1); + INIT_LIST_HEAD(&tr->images); for (i = 0; i < BPF_TRAMP_MAX; i++) INIT_HLIST_HEAD(&tr->progs_hlist[i]); out: @@ -535,15 +536,22 @@ static void bpf_tramp_image_free(struct bpf_tramp_image *im) arch_free_bpf_trampoline(im->image, im->size); bpf_jit_uncharge_modmem(im->size); percpu_ref_exit(&im->pcref); + kfree(im->skips); kfree_rcu(im, rcu); } static void __bpf_tramp_image_put_deferred(struct work_struct *work) { struct bpf_tramp_image *im; + struct bpf_trampoline *tr; im = container_of(work, struct bpf_tramp_image, work); + tr = im->tr; + trampoline_lock(tr); + list_del(&im->list); + trampoline_unlock(tr); bpf_tramp_image_free(im); + bpf_trampoline_put(tr); } /* callback, fexit step 3 or fentry step 2 */ @@ -571,7 +579,7 @@ static void __bpf_tramp_image_put_rcu_tasks(struct rcu_head *rcu) struct bpf_tramp_image *im; im = container_of(rcu, struct bpf_tramp_image, rcu); - if (im->ip_after_call) + if (im->call_orig) /* the case of fmod_ret/fexit trampoline and CONFIG_PREEMPTION=y */ percpu_ref_kill(&im->pcref); else @@ -591,9 +599,9 @@ static void bpf_tramp_image_put(struct bpf_tramp_image *im) * * The trampoline is unreachable before bpf_tramp_image_put(). * - * First, patch the trampoline to avoid calling into fexit progs. - * The progs will be freed even if the original function is still - * executing or sleeping. + * Progs are patched out of the image when they are detached, see + * bpf_trampoline_skip_prog(), so they can be freed even if a task is + * still in the image. * In case of CONFIG_PREEMPT=y use call_rcu_tasks() to wait on * first few asm instructions to execute and call into * __bpf_tramp_enter->percpu_ref_get. @@ -607,11 +615,7 @@ static void bpf_tramp_image_put(struct bpf_tramp_image *im) * percpu_ref_kill will be waiting for. Hence the first * call_rcu_tasks() is not necessary. */ - if (im->ip_after_call) { - int err = bpf_arch_text_poke(im->ip_after_call, BPF_MOD_NOP, - BPF_MOD_JUMP, NULL, - im->ip_epilogue); - WARN_ON(err); + if (im->call_orig) { if (IS_ENABLED(CONFIG_TASKS_RCU)) call_rcu_tasks(&im->rcu, __bpf_tramp_image_put_rcu_tasks); else @@ -628,7 +632,7 @@ static void bpf_tramp_image_put(struct bpf_tramp_image *im) call_rcu_tasks_trace(&im->rcu, __bpf_tramp_image_put_rcu_tasks); } -static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, int size) +static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, int size, int nr_progs) { struct bpf_tramp_image *im; struct bpf_ksym *ksym; @@ -639,6 +643,10 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, int size) if (!im) goto out; + im->skips = kzalloc_objs(*im->skips, nr_progs); + if (!im->skips) + goto out_free_im; + err = bpf_jit_charge_modmem(size); if (err) goto out_free_im; @@ -665,6 +673,7 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, int size) out_uncharge: bpf_jit_uncharge_modmem(size); out_free_im: + kfree(im->skips); kfree(im); out: return ERR_PTR(err); @@ -741,11 +750,12 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut goto out; } - im = bpf_tramp_image_alloc(tr->key, size); + im = bpf_tramp_image_alloc(tr->key, size, total); if (IS_ERR(im)) { err = PTR_ERR(im); goto out; } + im->call_orig = tr->flags & BPF_TRAMP_F_CALL_ORIG; err = arch_prepare_bpf_trampoline(im, im->image, im->image + size, &tr->func.model, tr->flags, tnodes, @@ -776,8 +786,14 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut #endif out_free: - if (err) + if (err) { bpf_tramp_image_free(im); + } else { + /* track the image until it is freed, for bpf_trampoline_skip_prog() */ + refcount_inc(&tr->refcnt); + im->tr = tr; + list_add(&im->list, &tr->images); + } out: /* If any error happens, restore previous flags */ if (err) @@ -877,6 +893,7 @@ static int bpf_trampoline_add_prog(struct bpf_trampoline *tr, } hlist_add_head(&node->tramp_hlist, prog_list); + node->link->prog->aux->tramp_linked = true; if (kind == BPF_TRAMP_FSESSION) { tr->progs_cnt[BPF_TRAMP_FENTRY]++; fexit = fsession_exit(node); @@ -890,6 +907,41 @@ static int bpf_trampoline_add_prog(struct bpf_trampoline *tr, return 0; } +/* + * Patch the nop in front of a prog call to a jump over it. A task can be + * preempted anywhere in the image, so archs that need several instructions for + * a jump of any range patch a single near branch here instead. + */ +int __weak arch_bpf_trampoline_skip(void *nop, void *target) +{ + return bpf_arch_text_poke(nop, BPF_MOD_NOP, BPF_MOD_JUMP, NULL, target); +} + +/* + * prog was detached and can be freed, but tasks may still be running in images + * that call it, sleeping in an earlier prog for example. They can be in any + * image that is not freed yet, not only in cur_image, so patch all of them to + * jump over prog. + */ +static void bpf_trampoline_skip_prog(struct bpf_trampoline *tr, struct bpf_prog *prog) +{ + struct bpf_tramp_image *im; + int i, err; + + list_for_each_entry(im, &tr->images, list) { + for (i = 0; i < im->nr_skips; i++) { + struct bpf_tramp_skip *skip = &im->skips[i]; + + if (skip->prog != prog) + continue; + err = arch_bpf_trampoline_skip(skip->nop, skip->target); + WARN_ON_ONCE(err); + /* not a nop anymore, and prog's address can be reused */ + skip->prog = NULL; + } + } +} + static void bpf_trampoline_remove_prog(struct bpf_trampoline *tr, struct bpf_tramp_node *node) { @@ -907,6 +959,7 @@ static void bpf_trampoline_remove_prog(struct bpf_trampoline *tr, } hlist_del_init(&node->tramp_hlist); tr->progs_cnt[kind]--; + bpf_trampoline_skip_prog(tr, node->link->prog); } static int __bpf_trampoline_link_prog(struct bpf_tramp_node *node, @@ -1205,11 +1258,9 @@ void bpf_trampoline_put(struct bpf_trampoline *tr) if (WARN_ON_ONCE(!hlist_empty(&tr->progs_hlist[i]))) goto out; - /* This code will be executed even when the last bpf_tramp_image - * is alive. All progs are detached from the trampoline and the - * trampoline image is patched with jmp into epilogue to skip - * fexit progs. The fentry-only trampoline will be freed via - * multiple rcu callbacks. + /* + * All progs are detached and the last image has been freed, images + * hold a reference on the trampoline until then. */ hlist_del(&tr->hlist_key); hlist_del(&tr->hlist_ip); diff --git a/kernel/events/core.c b/kernel/events/core.c index d8f9efaca989..4959b287f589 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7624,9 +7624,11 @@ static void perf_sigtrap(struct perf_event *event) { /* * Both perf_pending_task() and perf_pending_irq() can race with the - * task exiting. + * task exiting or exec-ing. We can determine if such a race has + * occurred by checking if perf_event_exit_task(), which will set + * ctx->task to TASK_TOMBSTONE, has already been called. */ - if (current->flags & PF_EXITING) + if (event->ctx->task == TASK_TOMBSTONE) return; /* @@ -8105,10 +8107,15 @@ static void __perf_event_header__init_id(struct perf_sample_data *data, } } -void perf_event_header__init_id(struct perf_event_header *header, - struct perf_sample_data *data, - struct perf_event *event) +void perf_event_header__init(struct perf_event_header *header, + struct perf_sample_data *data, + u32 type, u16 misc, u16 size, + struct perf_event *event) { + header->type = type; + header->misc = misc; + header->size = size; + if (event->attr.sample_id_all) { header->size += event->id_header_size; __perf_event_header__init_id(data, event, event->attr.sample_type); @@ -8944,17 +8951,16 @@ perf_event_read_event(struct perf_event *event, struct perf_output_handle handle; struct perf_sample_data sample; struct perf_read_event read_event = { - .header = { - .type = PERF_RECORD_READ, - .misc = 0, - .size = sizeof(read_event) + event->read_size, - }, .pid = perf_event_pid(event, task), .tid = perf_event_tid(event, task), }; int ret; - perf_event_header__init_id(&read_event.header, &sample, event); + perf_event_header__init(&read_event.header, &sample, + PERF_RECORD_READ, + /* misc= */ 0, + sizeof(read_event) + event->read_size, + event); ret = perf_output_begin(&handle, &sample, event, read_event.header.size); if (ret) return; @@ -9195,6 +9201,7 @@ struct perf_task_event { u32 ptid; u64 time; } event_id; + int new; }; static int perf_event_task_match(struct perf_event *event) @@ -9211,17 +9218,21 @@ static void perf_event_task_output(struct perf_event *event, struct perf_output_handle handle; struct perf_sample_data sample; struct task_struct *task = task_event->task; - int ret, size = task_event->event_id.header.size; + int ret; if (!perf_event_task_match(event)) return; - perf_event_header__init_id(&task_event->event_id.header, &sample, event); + perf_event_header__init(&task_event->event_id.header, &sample, + task_event->new ? PERF_RECORD_FORK : PERF_RECORD_EXIT, + /* misc= */ 0, + sizeof(task_event->event_id), + event); ret = perf_output_begin(&handle, &sample, event, task_event->event_id.header.size); if (ret) - goto out; + return; task_event->event_id.pid = perf_event_pid(event, task); task_event->event_id.tid = perf_event_tid(event, task); @@ -9243,8 +9254,6 @@ static void perf_event_task_output(struct perf_event *event, perf_event__output_id_sample(event, &handle, &sample); perf_output_end(&handle); -out: - task_event->event_id.header.size = size; } static void perf_event_task(struct task_struct *task, @@ -9261,18 +9270,7 @@ static void perf_event_task(struct task_struct *task, task_event = (struct perf_task_event){ .task = task, .task_ctx = task_ctx, - .event_id = { - .header = { - .type = new ? PERF_RECORD_FORK : PERF_RECORD_EXIT, - .misc = 0, - .size = sizeof(task_event.event_id), - }, - /* .pid */ - /* .ppid */ - /* .tid */ - /* .ptid */ - /* .time */ - }, + .new = new, }; perf_iterate_sb(perf_event_task_output, @@ -9349,6 +9347,7 @@ struct perf_comm_event { u32 pid; u32 tid; } event_id; + bool exec; }; static int perf_event_comm_match(struct perf_event *event) @@ -9362,18 +9361,21 @@ static void perf_event_comm_output(struct perf_event *event, struct perf_comm_event *comm_event = data; struct perf_output_handle handle; struct perf_sample_data sample; - int size = comm_event->event_id.header.size; int ret; if (!perf_event_comm_match(event)) return; - perf_event_header__init_id(&comm_event->event_id.header, &sample, event); + perf_event_header__init(&comm_event->event_id.header, &sample, + PERF_RECORD_COMM, + comm_event->exec ? PERF_RECORD_MISC_COMM_EXEC : 0, + sizeof(comm_event->event_id) + comm_event->comm_size, + event); ret = perf_output_begin(&handle, &sample, event, comm_event->event_id.header.size); if (ret) - goto out; + return; comm_event->event_id.pid = perf_event_pid(event, comm_event->task); comm_event->event_id.tid = perf_event_tid(event, comm_event->task); @@ -9385,8 +9387,6 @@ static void perf_event_comm_output(struct perf_event *event, perf_event__output_id_sample(event, &handle, &sample); perf_output_end(&handle); -out: - comm_event->event_id.header.size = size; } static void perf_event_comm_event(struct perf_comm_event *comm_event) @@ -9401,8 +9401,6 @@ static void perf_event_comm_event(struct perf_comm_event *comm_event) comm_event->comm = comm; comm_event->comm_size = size; - comm_event->event_id.header.size = sizeof(comm_event->event_id) + size; - perf_iterate_sb(perf_event_comm_output, comm_event, NULL); @@ -9419,15 +9417,8 @@ void perf_event_comm(struct task_struct *task, bool exec) .task = task, /* .comm */ /* .comm_size */ - .event_id = { - .header = { - .type = PERF_RECORD_COMM, - .misc = exec ? PERF_RECORD_MISC_COMM_EXEC : 0, - /* .size */ - }, - /* .pid */ - /* .tid */ - }, + /* .event_id */ + .exec = exec, }; perf_event_comm_event(&comm_event); @@ -9461,18 +9452,20 @@ static void perf_event_namespaces_output(struct perf_event *event, struct perf_namespaces_event *namespaces_event = data; struct perf_output_handle handle; struct perf_sample_data sample; - u16 header_size = namespaces_event->event_id.header.size; int ret; if (!perf_event_namespaces_match(event)) return; - perf_event_header__init_id(&namespaces_event->event_id.header, - &sample, event); + perf_event_header__init(&namespaces_event->event_id.header, &sample, + PERF_RECORD_NAMESPACES, + /* misc= */ 0, + sizeof(namespaces_event->event_id), + event); ret = perf_output_begin(&handle, &sample, event, namespaces_event->event_id.header.size); if (ret) - goto out; + return; namespaces_event->event_id.pid = perf_event_pid(event, namespaces_event->task); @@ -9484,8 +9477,6 @@ static void perf_event_namespaces_output(struct perf_event *event, perf_event__output_id_sample(event, &handle, &sample); perf_output_end(&handle); -out: - namespaces_event->event_id.header.size = header_size; } static void perf_fill_ns_link_info(struct perf_ns_link_info *ns_link_info, @@ -9516,11 +9507,7 @@ void perf_event_namespaces(struct task_struct *task) namespaces_event = (struct perf_namespaces_event){ .task = task, .event_id = { - .header = { - .type = PERF_RECORD_NAMESPACES, - .misc = 0, - .size = sizeof(namespaces_event.event_id), - }, + /* .header */ /* .pid */ /* .tid */ .nr_namespaces = NR_NAMESPACES, @@ -9588,18 +9575,19 @@ static void perf_event_cgroup_output(struct perf_event *event, void *data) struct perf_cgroup_event *cgroup_event = data; struct perf_output_handle handle; struct perf_sample_data sample; - u16 header_size = cgroup_event->event_id.header.size; int ret; + u16 size = sizeof(cgroup_event->event_id) + cgroup_event->path_size; if (!perf_event_cgroup_match(event)) return; - perf_event_header__init_id(&cgroup_event->event_id.header, - &sample, event); + perf_event_header__init(&cgroup_event->event_id.header, &sample, + PERF_RECORD_CGROUP, /* misc= */ 0, size, + event); ret = perf_output_begin(&handle, &sample, event, cgroup_event->event_id.header.size); if (ret) - goto out; + return; perf_output_put(&handle, cgroup_event->event_id); __output_copy(&handle, cgroup_event->path, cgroup_event->path_size); @@ -9607,8 +9595,6 @@ static void perf_event_cgroup_output(struct perf_event *event, void *data) perf_event__output_id_sample(event, &handle, &sample); perf_output_end(&handle); -out: - cgroup_event->event_id.header.size = header_size; } static void perf_event_cgroup(struct cgroup *cgrp) @@ -9623,11 +9609,6 @@ static void perf_event_cgroup(struct cgroup *cgrp) cgroup_event = (struct perf_cgroup_event){ .event_id = { - .header = { - .type = PERF_RECORD_CGROUP, - .misc = 0, - .size = sizeof(cgroup_event.event_id), - }, .id = cgroup_id(cgrp), }, }; @@ -9650,7 +9631,6 @@ static void perf_event_cgroup(struct cgroup *cgrp) while (!IS_ALIGNED(size, sizeof(u64))) cgroup_event.path[size++] = '\0'; - cgroup_event.event_id.header.size += size; cgroup_event.path_size = size; perf_iterate_sb(perf_event_cgroup_output, @@ -9706,38 +9686,40 @@ static void perf_event_mmap_output(struct perf_event *event, struct perf_mmap_event *mmap_event = data; struct perf_output_handle handle; struct perf_sample_data sample; - int size = mmap_event->event_id.header.size; - u32 type = mmap_event->event_id.header.type; - bool use_build_id; + int size = sizeof(mmap_event->event_id) + mmap_event->file_size; + u32 type = PERF_RECORD_MMAP; + u16 misc = PERF_RECORD_MISC_USER; + bool use_build_id = false; int ret; if (!perf_event_mmap_match(event, data)) return; if (event->attr.mmap2) { - mmap_event->event_id.header.type = PERF_RECORD_MMAP2; - mmap_event->event_id.header.size += sizeof(mmap_event->maj); - mmap_event->event_id.header.size += sizeof(mmap_event->min); - mmap_event->event_id.header.size += sizeof(mmap_event->ino); - mmap_event->event_id.header.size += sizeof(mmap_event->ino_generation); - mmap_event->event_id.header.size += sizeof(mmap_event->prot); - mmap_event->event_id.header.size += sizeof(mmap_event->flags); - } - - perf_event_header__init_id(&mmap_event->event_id.header, &sample, event); + type = PERF_RECORD_MMAP2; + size += sizeof(mmap_event->maj); + size += sizeof(mmap_event->min); + size += sizeof(mmap_event->ino); + size += sizeof(mmap_event->ino_generation); + size += sizeof(mmap_event->prot); + size += sizeof(mmap_event->flags); + use_build_id = event->attr.build_id && mmap_event->build_id_size; + if (use_build_id) + misc |= PERF_RECORD_MISC_MMAP_BUILD_ID; + } + if (!(mmap_event->vma->vm_flags & VM_EXEC)) + misc |= PERF_RECORD_MISC_MMAP_DATA; + + perf_event_header__init(&mmap_event->event_id.header, &sample, + type, misc, size, event); ret = perf_output_begin(&handle, &sample, event, mmap_event->event_id.header.size); if (ret) - goto out; + return; mmap_event->event_id.pid = perf_event_pid(event, current); mmap_event->event_id.tid = perf_event_tid(event, current); - use_build_id = event->attr.build_id && mmap_event->build_id_size; - - if (event->attr.mmap2 && use_build_id) - mmap_event->event_id.header.misc |= PERF_RECORD_MISC_MMAP_BUILD_ID; - perf_output_put(&handle, mmap_event->event_id); if (event->attr.mmap2) { @@ -9762,9 +9744,6 @@ static void perf_event_mmap_output(struct perf_event *event, perf_event__output_id_sample(event, &handle, &sample); perf_output_end(&handle); -out: - mmap_event->event_id.header.size = size; - mmap_event->event_id.header.type = type; } static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) @@ -9860,11 +9839,6 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) mmap_event->prot = prot; mmap_event->flags = flags; - if (!(vma->vm_flags & VM_EXEC)) - mmap_event->event_id.header.misc |= PERF_RECORD_MISC_MMAP_DATA; - - mmap_event->event_id.header.size = sizeof(mmap_event->event_id) + size; - if (atomic_read(&nr_build_id_events)) build_id_parse_nofault(vma, mmap_event->build_id, &mmap_event->build_id_size); @@ -9984,11 +9958,7 @@ void perf_event_mmap(struct vm_area_struct *vma) /* .file_name */ /* .file_size */ .event_id = { - .header = { - .type = PERF_RECORD_MMAP, - .misc = PERF_RECORD_MISC_USER, - /* .size */ - }, + /* .header */ /* .pid */ /* .tid */ .start = vma->vm_start, @@ -10018,18 +9988,15 @@ void perf_event_aux_event(struct perf_event *event, unsigned long head, u64 size; u64 flags; } rec = { - .header = { - .type = PERF_RECORD_AUX, - .misc = 0, - .size = sizeof(rec), - }, .offset = head, .size = size, .flags = flags, }; int ret; - perf_event_header__init_id(&rec.header, &sample, event); + perf_event_header__init(&rec.header, &sample, + PERF_RECORD_AUX, /* misc= */ 0, sizeof(rec), + event); ret = perf_output_begin(&handle, &sample, event, rec.header.size); if (ret) @@ -10054,15 +10021,14 @@ void perf_log_lost_samples(struct perf_event *event, u64 lost) struct perf_event_header header; u64 lost; } lost_samples_event = { - .header = { - .type = PERF_RECORD_LOST_SAMPLES, - .misc = 0, - .size = sizeof(lost_samples_event), - }, .lost = lost, }; - perf_event_header__init_id(&lost_samples_event.header, &sample, event); + perf_event_header__init(&lost_samples_event.header, &sample, + PERF_RECORD_LOST_SAMPLES, + /* misc= */ 0, + sizeof(lost_samples_event), + event); ret = perf_output_begin(&handle, &sample, event, lost_samples_event.header.size); @@ -10087,6 +10053,8 @@ struct perf_switch_event { u32 next_prev_pid; u32 next_prev_tid; } event_id; + bool sched_in; + bool preempt; }; static int perf_event_switch_match(struct perf_event *event) @@ -10099,6 +10067,9 @@ static void perf_event_switch_output(struct perf_event *event, void *data) struct perf_switch_event *se = data; struct perf_output_handle handle; struct perf_sample_data sample; + u32 type; + u16 misc; + u16 size; int ret; if (!perf_event_switch_match(event)) @@ -10106,18 +10077,22 @@ static void perf_event_switch_output(struct perf_event *event, void *data) /* Only CPU-wide events are allowed to see next/prev pid/tid */ if (event->ctx->task) { - se->event_id.header.type = PERF_RECORD_SWITCH; - se->event_id.header.size = sizeof(se->event_id.header); + type = PERF_RECORD_SWITCH; + size = sizeof(se->event_id.header); } else { - se->event_id.header.type = PERF_RECORD_SWITCH_CPU_WIDE; - se->event_id.header.size = sizeof(se->event_id); + type = PERF_RECORD_SWITCH_CPU_WIDE; + size = sizeof(se->event_id); se->event_id.next_prev_pid = perf_event_pid(event, se->next_prev); se->event_id.next_prev_tid = perf_event_tid(event, se->next_prev); } + misc = se->sched_in ? 0 : PERF_RECORD_MISC_SWITCH_OUT; + if (se->preempt) + misc |= PERF_RECORD_MISC_SWITCH_OUT_PREEMPT; - perf_event_header__init_id(&se->event_id.header, &sample, event); + perf_event_header__init(&se->event_id.header, &sample, + type, misc, size, event); ret = perf_output_begin(&handle, &sample, event, se->event_id.header.size); if (ret) @@ -10143,22 +10118,11 @@ static void perf_event_switch(struct task_struct *task, switch_event = (struct perf_switch_event){ .task = task, .next_prev = next_prev, - .event_id = { - .header = { - /* .type */ - .misc = sched_in ? 0 : PERF_RECORD_MISC_SWITCH_OUT, - /* .size */ - }, - /* .next_prev_pid */ - /* .next_prev_tid */ - }, + /* .event_id */ + .sched_in = sched_in, + .preempt = !sched_in && task_is_runnable(task), }; - if (!sched_in && task_is_runnable(task)) { - switch_event.event_id.header.misc |= - PERF_RECORD_MISC_SWITCH_OUT_PREEMPT; - } - perf_iterate_sb(perf_event_switch_output, &switch_event, NULL); } @@ -10178,20 +10142,17 @@ static void perf_log_throttle(struct perf_event *event, int enable) u64 id; u64 stream_id; } throttle_event = { - .header = { - .type = PERF_RECORD_THROTTLE, - .misc = 0, - .size = sizeof(throttle_event), - }, .time = perf_event_clock(event), .id = primary_event_id(event), .stream_id = event->id, }; - if (enable) - throttle_event.header.type = PERF_RECORD_UNTHROTTLE; - - perf_event_header__init_id(&throttle_event.header, &sample, event); + perf_event_header__init(&throttle_event.header, &sample, + enable ? PERF_RECORD_UNTHROTTLE + : PERF_RECORD_THROTTLE, + /* misc= */ 0, + sizeof(throttle_event), + event); ret = perf_output_begin(&handle, &sample, event, throttle_event.header.size); @@ -10230,12 +10191,14 @@ static void perf_event_ksymbol_output(struct perf_event *event, void *data) struct perf_output_handle handle; struct perf_sample_data sample; int ret; + u16 size = sizeof(ksymbol_event->event_id) + ksymbol_event->name_len; if (!perf_event_ksymbol_match(event)) return; - perf_event_header__init_id(&ksymbol_event->event_id.header, - &sample, event); + perf_event_header__init(&ksymbol_event->event_id.header, &sample, + PERF_RECORD_KSYMBOL, /* misc= */ 0, size, + event); ret = perf_output_begin(&handle, &sample, event, ksymbol_event->event_id.header.size); if (ret) @@ -10276,11 +10239,6 @@ void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, bool unregister, .name = name, .name_len = name_len, .event_id = { - .header = { - .type = PERF_RECORD_KSYMBOL, - .size = sizeof(ksymbol_event.event_id) + - name_len, - }, .addr = addr, .len = len, .ksym_type = ksym_type, @@ -10324,8 +10282,11 @@ static void perf_event_bpf_output(struct perf_event *event, void *data) if (!perf_event_bpf_match(event)) return; - perf_event_header__init_id(&bpf_event->event_id.header, - &sample, event); + perf_event_header__init(&bpf_event->event_id.header, &sample, + PERF_RECORD_BPF_EVENT, + /* misc= */ 0, + sizeof(bpf_event->event_id), + event); ret = perf_output_begin(&handle, &sample, event, bpf_event->event_id.header.size); if (ret) @@ -10381,10 +10342,6 @@ void perf_event_bpf_event(struct bpf_prog *prog, bpf_event = (struct perf_bpf_event){ .prog = prog, .event_id = { - .header = { - .type = PERF_RECORD_BPF_EVENT, - .size = sizeof(bpf_event.event_id), - }, .type = type, .flags = flags, .id = prog->aux->id, @@ -10412,18 +10369,23 @@ static void perf_callchain_deferred_output(struct perf_event *event, void *data) struct perf_callchain_deferred_event *deferred_event = data; struct perf_output_handle handle; struct perf_sample_data sample; - int ret, size = deferred_event->event.header.size; + int ret; + u16 size = sizeof(deferred_event->event) + (deferred_event->trace->nr * sizeof(u64)); if (!event->attr.defer_output) return; /* XXX do we really need sample_id_all for this ??? */ - perf_event_header__init_id(&deferred_event->event.header, &sample, event); + perf_event_header__init(&deferred_event->event.header, &sample, + PERF_RECORD_CALLCHAIN_DEFERRED, + PERF_RECORD_MISC_USER, + size, + event); ret = perf_output_begin(&handle, &sample, event, deferred_event->event.header.size); if (ret) - goto out; + return; perf_output_put(&handle, deferred_event->event); for (int i = 0; i < deferred_event->trace->nr; i++) { @@ -10433,8 +10395,6 @@ static void perf_callchain_deferred_output(struct perf_event *event, void *data) perf_event__output_id_sample(event, &handle, &sample); perf_output_end(&handle); -out: - deferred_event->event.header.size = size; } static void perf_unwind_deferred_callback(struct unwind_work *work, @@ -10443,12 +10403,6 @@ static void perf_unwind_deferred_callback(struct unwind_work *work, struct perf_callchain_deferred_event deferred_event = { .trace = trace, .event = { - .header = { - .type = PERF_RECORD_CALLCHAIN_DEFERRED, - .misc = PERF_RECORD_MISC_USER, - .size = sizeof(deferred_event.event) + - (trace->nr * sizeof(u64)), - }, .cookie = cookie, .nr = trace->nr, }, @@ -10460,7 +10414,8 @@ static void perf_unwind_deferred_callback(struct unwind_work *work, struct perf_text_poke_event { const void *old_bytes; const void *new_bytes; - size_t pad; + u16 tot; + u16 pad; u16 old_len; u16 new_len; @@ -10481,13 +10436,18 @@ static void perf_event_text_poke_output(struct perf_event *event, void *data) struct perf_text_poke_event *text_poke_event = data; struct perf_output_handle handle; struct perf_sample_data sample; + u16 size = sizeof(text_poke_event->event_id) + text_poke_event->tot + text_poke_event->pad; u64 padding = 0; int ret; if (!perf_event_text_poke_match(event)) return; - perf_event_header__init_id(&text_poke_event->event_id.header, &sample, event); + perf_event_header__init(&text_poke_event->event_id.header, &sample, + PERF_RECORD_TEXT_POKE, + PERF_RECORD_MISC_KERNEL, + size, + event); ret = perf_output_begin(&handle, &sample, event, text_poke_event->event_id.header.size); @@ -10525,15 +10485,11 @@ void perf_event_text_poke(const void *addr, const void *old_bytes, text_poke_event = (struct perf_text_poke_event){ .old_bytes = old_bytes, .new_bytes = new_bytes, + .tot = tot, .pad = pad, .old_len = old_len, .new_len = new_len, .event_id = { - .header = { - .type = PERF_RECORD_TEXT_POKE, - .misc = PERF_RECORD_MISC_KERNEL, - .size = sizeof(text_poke_event.event_id) + tot + pad, - }, .addr = (unsigned long)addr, }, }; @@ -10564,13 +10520,12 @@ static void perf_log_itrace_start(struct perf_event *event) event->attach_state & PERF_ATTACH_ITRACE) return; - rec.header.type = PERF_RECORD_ITRACE_START; - rec.header.misc = 0; - rec.header.size = sizeof(rec); rec.pid = perf_event_pid(event, current); rec.tid = perf_event_tid(event, current); - perf_event_header__init_id(&rec.header, &sample, event); + perf_event_header__init(&rec.header, &sample, + PERF_RECORD_ITRACE_START, /* misc= */ 0, sizeof(rec), + event); ret = perf_output_begin(&handle, &sample, event, rec.header.size); if (ret) @@ -10595,12 +10550,10 @@ void perf_report_aux_output_id(struct perf_event *event, u64 hw_id) if (event->parent) event = event->parent; - rec.header.type = PERF_RECORD_AUX_OUTPUT_HW_ID; - rec.header.misc = 0; - rec.header.size = sizeof(rec); - rec.hw_id = hw_id; - - perf_event_header__init_id(&rec.header, &sample, event); + rec.hw_id = hw_id; + perf_event_header__init(&rec.header, &sample, + PERF_RECORD_AUX_OUTPUT_HW_ID, /* misc= */ 0, + sizeof(rec), event); ret = perf_output_begin(&handle, &sample, event, rec.header.size); if (ret) @@ -13929,7 +13882,9 @@ SYSCALL_DEFINE5(perf_event_open, if (err) return err; - if (!attr.exclude_kernel) { + if (!attr.exclude_kernel || attr.text_poke || + ((attr.sample_type & PERF_SAMPLE_CALLCHAIN) && + !attr.exclude_callchain_kernel)) { err = perf_allow_kernel(); if (err) return err; diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 1b1ffe0533e5..13bd42e5493d 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -246,14 +246,13 @@ __perf_output_begin(struct perf_output_handle *handle, handle->size = (1UL << page_shift) - offset; if (unlikely(have_lost)) { - lost_event.header.size = sizeof(lost_event); - lost_event.header.type = PERF_RECORD_LOST; - lost_event.header.misc = 0; lost_event.id = event->id; lost_event.lost = local_xchg(&rb->lost, 0); /* XXX mostly redundant; @data is already fully initializes */ - perf_event_header__init_id(&lost_event.header, data, event); + perf_event_header__init(&lost_event.header, data, + PERF_RECORD_LOST, /* misc= */ 0, + sizeof(lost_event), event); perf_output_put(handle, lost_event); perf_event__output_id_sample(event, handle, data); } diff --git a/kernel/exit.c b/kernel/exit.c index 200a936f601a..27e5cf81e78d 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -552,37 +552,6 @@ void mm_update_next_owner(struct mm_struct *mm) } #endif /* CONFIG_MEMCG */ -#if defined(CONFIG_SCHED_CACHE) && defined(CONFIG_NUMA_BALANCING) -/* - * Subtract the memory footprint of the current task from - * mm. - */ -static void exit_mm_sched_cache(struct mm_struct *mm) -{ - struct sched_cache_group *grp; - unsigned long fp, sub; - - if (!current->total_numa_faults) - return; - /* - * No lock protection due to performance considerations. - * Make sure the group footprint does not become - * negative. - */ - grp = READ_ONCE(mm->sched_cache_grp); - if (!grp) - return; - - fp = READ_ONCE(grp->footprint); - sub = min(fp, current->total_numa_faults); - WRITE_ONCE(grp->footprint, fp - sub); -} -#else -static inline void exit_mm_sched_cache(struct mm_struct *mm) -{ -} -#endif /* CONFIG_SCHED_CACHE CONFIG_NUMA_BALANCING */ - /* * Turn us into a lazy TLB process if we * aren't already.. @@ -595,7 +564,7 @@ static void exit_mm(void) if (!mm) return; - exit_mm_sched_cache(mm); + sched_cache_exit_mm(current); mmap_read_lock(mm); mmgrab_lazy_tlb(mm); diff --git a/kernel/fork.c b/kernel/fork.c index 2c88568b0463..b22b5ba9a543 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1598,6 +1598,7 @@ static int copy_mm(u64 clone_flags, struct task_struct *tsk) tsk->mm = mm; tsk->active_mm = mm; + sched_cache_fork(tsk); return 0; } @@ -2583,6 +2584,7 @@ __latent_entropy struct task_struct *copy_process( bad_fork_cleanup_namespaces: exit_nsproxy_namespaces(p); bad_fork_cleanup_mm: + sched_cache_fork_cleanup(p); if (p->mm) { mm_clear_owner(p->mm, p); mmput(p->mm); diff --git a/kernel/futex/core.c b/kernel/futex/core.c index be4aeacd4b54..6a1971f9588e 100644 --- a/kernel/futex/core.c +++ b/kernel/futex/core.c @@ -216,10 +216,12 @@ static bool __futex_pivot_hash(struct mm_struct *mm, struct futex_private_hash * futex_rehash_private(fph, new); } new->state = FR_PERCPU; - scoped_guard(rcu) { - mmph->batches = get_state_synchronize_rcu(); - rcu_assign_pointer(mmph->hash, new); - } + rcu_assign_pointer(mmph->hash, new); + /* + * mmph->batches must reference a grace period which started after + * mmph->hash was assigned. See futex_ref_drop(). + */ + mmph->batches = get_state_synchronize_rcu(); kvfree_rcu(fph, rcu); return true; } diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 4edd8ca5c657..e787e4948c8b 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -496,14 +496,16 @@ static bool kprobe_queued(struct kprobe *p) static struct kprobe *get_optimized_kprobe(kprobe_opcode_t *addr) { int i; - struct kprobe *p = NULL; + struct kprobe *p; struct optimized_kprobe *op; /* Don't check i == 0, since that is a breakpoint case. */ - for (i = 1; !p && i < MAX_OPTIMIZED_LENGTH / sizeof(kprobe_opcode_t); i++) + for (i = 1; i < MAX_OPTIMIZED_LENGTH / sizeof(kprobe_opcode_t); i++) { p = get_kprobe(addr - i); + /* A disabled probe can have prepared, but inactive, optinsns. */ + if (!p || !kprobe_optready(p) || kprobe_disarmed(p)) + continue; - if (p && kprobe_optready(p)) { op = container_of(p, struct optimized_kprobe, kp); if (arch_within_optimized_kprobe(op, addr)) return p; diff --git a/kernel/module/main.c b/kernel/module/main.c index 7cbc8f0e28c6..d35cb5e362c6 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -3580,8 +3580,9 @@ static int load_module(struct load_info *info, const char __user *uargs, goto sysfs_cleanup; } - if (codetag_load_module(mod)) - goto sysfs_cleanup; + err = codetag_load_module(mod); + if (err) + goto livepatch_cleanup; /* Get rid of temporary copy. */ free_copy(info, flags); @@ -3591,6 +3592,9 @@ static int load_module(struct load_info *info, const char __user *uargs, return do_init_module(mod); + livepatch_cleanup: + if (is_livepatch_module(mod)) + free_module_elf(mod); sysfs_cleanup: mod_sysfs_teardown(mod); coming_cleanup: diff --git a/kernel/sched/ext/cid.c b/kernel/sched/ext/cid.c index af83084ec740..dfbc8d04f4cb 100644 --- a/kernel/sched/ext/cid.c +++ b/kernel/sched/ext/cid.c @@ -13,14 +13,15 @@ /* * cid tables. * - * Pointers are published once on first enable and never revoked. The default - * mapping is populated before ops.init() runs; scx_bpf_cid_override() commits - * before it returns. As long as the BPF scheduler only uses the tables from - * those points onward, it sees a consistent view. + * The cpu<->cid arrays are allocated once and populated before ops.init(). + * The topology table is built privately and published with RCU, including + * when ops.init() overrides the mapping. Published topology tables are never + * modified and are retired after an RCU grace period. Root disable revokes + * the topology table, so readers must check for NULL. */ s16 *scx_cid_to_cpu_tbl; s16 *scx_cpu_to_cid_tbl; -struct scx_cid_topo *scx_cid_topo; +struct scx_cid_topo __rcu *scx_cid_topo; #define SCX_CID_TOPO_NEG (struct scx_cid_topo) { \ .core_cid = -1, .core_idx = -1, .llc_cid = -1, .llc_idx = -1, \ @@ -43,30 +44,26 @@ static const struct cpumask *cpu_llc_mask(int cpu, struct cpumask *fallbacks) return &ci->info_list[ci->num_leaves - 1].shared_cpu_map; } -/* Allocate the cid tables once on first enable; never freed. */ +/* Allocate the cpu<->cid arrays once on first enable; never freed. */ static s32 scx_cid_arrays_alloc(void) { u32 npossible = num_possible_cpus(); s16 *cid_to_cpu, *cpu_to_cid; - struct scx_cid_topo *cid_topo; if (scx_cid_to_cpu_tbl) return 0; cid_to_cpu = kzalloc_objs(*scx_cid_to_cpu_tbl, npossible, GFP_KERNEL); cpu_to_cid = kzalloc_objs(*scx_cpu_to_cid_tbl, nr_cpu_ids, GFP_KERNEL); - cid_topo = kmalloc_objs(*scx_cid_topo, npossible, GFP_KERNEL); - if (!cid_to_cpu || !cpu_to_cid || !cid_topo) { + if (!cid_to_cpu || !cpu_to_cid) { kfree(cid_to_cpu); kfree(cpu_to_cid); - kfree(cid_topo); return -ENOMEM; } WRITE_ONCE(scx_cid_to_cpu_tbl, cid_to_cpu); WRITE_ONCE(scx_cpu_to_cid_tbl, cpu_to_cid); - WRITE_ONCE(scx_cid_topo, cid_topo); return 0; } @@ -88,6 +85,8 @@ s32 scx_cid_init(struct scx_sched *sch) cpumask_var_t core_scratch __free(free_cpumask_var) = CPUMASK_VAR_NULL; cpumask_var_t llc_fallback __free(free_cpumask_var) = CPUMASK_VAR_NULL; cpumask_var_t online_no_topo __free(free_cpumask_var) = CPUMASK_VAR_NULL; + struct scx_cid_topo *topo __free(kfree) = NULL; + struct scx_cid_topo *old_topo; u32 next_cid = 0; s32 next_node_idx = 0, next_llc_idx = 0, next_core_idx = 0; s32 cpu, ret; @@ -101,6 +100,10 @@ s32 scx_cid_init(struct scx_sched *sch) if (ret) return ret; + topo = kmalloc_objs(*topo, num_possible_cpus(), GFP_KERNEL); + if (!topo) + return -ENOMEM; + if (!zalloc_cpumask_var(&to_walk, GFP_KERNEL) || !zalloc_cpumask_var(&node_scratch, GFP_KERNEL) || !zalloc_cpumask_var(&llc_scratch, GFP_KERNEL) || @@ -165,7 +168,7 @@ s32 scx_cid_init(struct scx_sched *sch) scx_cid_to_cpu_tbl[cid] = ccpu; scx_cpu_to_cid_tbl[ccpu] = cid; - scx_cid_topo[cid] = (struct scx_cid_topo){ + topo[cid] = (struct scx_cid_topo){ .core_cid = core_cid, .core_idx = core_idx, .llc_cid = llc_cid, @@ -198,7 +201,7 @@ s32 scx_cid_init(struct scx_sched *sch) cid = next_cid++; scx_cid_to_cpu_tbl[cid] = cpu; scx_cpu_to_cid_tbl[cpu] = cid; - scx_cid_topo[cid] = SCX_CID_TOPO_NEG; + topo[cid] = SCX_CID_TOPO_NEG; } if (!cpumask_empty(llc_fallback)) @@ -208,6 +211,13 @@ s32 scx_cid_init(struct scx_sched *sch) pr_warn("scx_cid: online cpus with no usable topology: %*pbl\n", cpumask_pr_args(online_no_topo)); + /* Root enable and ops.init() serialize topology table updates. */ + old_topo = rcu_replace_pointer(scx_cid_topo, no_free_ptr(topo), true); + if (old_topo) { + synchronize_rcu(); + kfree(old_topo); + } + return 0; } @@ -291,51 +301,60 @@ __bpf_kfunc void scx_bpf_cid_override(const s32 *cpu_to_cid, u32 cpu_to_cid__sz, const struct bpf_prog_aux *aux) { cpumask_var_t seen __free(free_cpumask_var) = CPUMASK_VAR_NULL; + struct scx_cid_topo *topo __free(kfree) = NULL; + struct scx_cid_topo *old_topo; struct scx_sched *sch; bool alloced; s32 cpu, cid; /* GFP_KERNEL alloc must happen before the rcu read section */ alloced = zalloc_cpumask_var(&seen, GFP_KERNEL); + topo = kmalloc_objs(*topo, num_possible_cpus(), GFP_KERNEL); - guard(rcu)(); - - sch = scx_prog_sched(aux); - if (unlikely(!sch)) - return; + scoped_guard(rcu) { + sch = scx_prog_sched(aux); + if (unlikely(!sch)) + return; - if (!alloced) { - scx_error(sch, "scx_bpf_cid_override: failed to allocate cpumask"); - return; - } + if (!alloced || !topo) { + scx_error(sch, "scx_bpf_cid_override: failed to allocate tables"); + return; + } - if (scx_parent(sch)) { - scx_error(sch, "scx_bpf_cid_override() only allowed from root sched"); - return; - } + if (scx_parent(sch)) { + scx_error(sch, "scx_bpf_cid_override() only allowed from root sched"); + return; + } - if (cpu_to_cid__sz != nr_cpu_ids * sizeof(s32)) { - scx_error(sch, "scx_bpf_cid_override: expected %zu bytes, got %u", - nr_cpu_ids * sizeof(s32), cpu_to_cid__sz); - return; - } + if (cpu_to_cid__sz != nr_cpu_ids * sizeof(s32)) { + scx_error(sch, "scx_bpf_cid_override: expected %zu bytes, got %u", + nr_cpu_ids * sizeof(s32), cpu_to_cid__sz); + return; + } - for_each_possible_cpu(cpu) { - s32 c = cpu_to_cid[cpu]; + for_each_possible_cpu(cpu) { + s32 c = cpu_to_cid[cpu]; - if (!cid_valid(sch, c)) - return; - if (cpumask_test_and_set_cpu(c, seen)) { - scx_error(sch, "cid %d assigned to multiple cpus", c); - return; + if (!cid_valid(sch, c)) + return; + if (cpumask_test_and_set_cpu(c, seen)) { + scx_error(sch, "cid %d assigned to multiple cpus", c); + return; + } + scx_cpu_to_cid_tbl[cpu] = c; + scx_cid_to_cpu_tbl[c] = cpu; } - scx_cpu_to_cid_tbl[cpu] = c; - scx_cid_to_cpu_tbl[c] = cpu; + + /* The override carries no topology. Publish a complete replacement. */ + for (cid = 0; cid < num_possible_cpus(); cid++) + topo[cid] = SCX_CID_TOPO_NEG; + + old_topo = rcu_replace_pointer(scx_cid_topo, no_free_ptr(topo), true); } - /* Invalidate stale topo info - the override carries no topology. */ - for (cid = 0; cid < num_possible_cpus(); cid++) - scx_cid_topo[cid] = SCX_CID_TOPO_NEG; + /* This sleepable kfunc must leave its RCU read section before waiting. */ + synchronize_rcu(); + kfree(old_topo); } /** @@ -655,27 +674,36 @@ bool scx_cmask_empty(const struct scx_cmask *m) /** * scx_bpf_cid_topo - Copy out per-cid topology info * @cid: cid to look up - * @out__uninit: where to copy the topology info; fully written by this call + * @out: where to copy the topology info + * @out__sz: size of @out, the program's sizeof(struct scx_cid_topo) * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs * - * Fill @out__uninit with the topology info for @cid. Trigger scx_error() if - * @cid is out of range. If @cid is valid but in the no-topo section, all fields - * are set to -1. + * Fill @out with the topology info for @cid. Trigger scx_error() if @cid is out + * of range. If @cid is valid but in the no-topo section, all fields are set to + * -1. All fields are also set to -1 when no cid tables have been published yet, + * which a program may observe while racing the root enable. + * + * The program's struct may be older or newer than the kernel's. The smaller of + * @out__sz and the kernel's size is copied and the rest of @out is set to -1. */ -__bpf_kfunc void scx_bpf_cid_topo(s32 cid, struct scx_cid_topo *out__uninit, +__bpf_kfunc void scx_bpf_cid_topo(s32 cid, struct scx_cid_topo *out, size_t out__sz, const struct bpf_prog_aux *aux) { + size_t len = min(out__sz, sizeof(*out)); + struct scx_cid_topo *topo; struct scx_sched *sch; + /* the error cases and fields the kernel lacks read as -1 */ + memset(out, 0xff, out__sz); + guard(rcu)(); sch = scx_prog_sched(aux); - if (unlikely(!sch) || !cid_valid(sch, cid)) { - *out__uninit = SCX_CID_TOPO_NEG; + topo = rcu_dereference(scx_cid_topo); + if (unlikely(!sch) || !cid_valid(sch, cid) || unlikely(!topo)) return; - } - *out__uninit = READ_ONCE(scx_cid_topo)[cid]; + memcpy(out, &topo[cid], len); } __bpf_kfunc_end_defs(); diff --git a/kernel/sched/ext/cid.h b/kernel/sched/ext/cid.h index 9c4f4b907f12..55c8ab30156e 100644 --- a/kernel/sched/ext/cid.h +++ b/kernel/sched/ext/cid.h @@ -50,7 +50,7 @@ struct scx_sched; */ extern s16 *scx_cid_to_cpu_tbl; extern s16 *scx_cpu_to_cid_tbl; -extern struct scx_cid_topo *scx_cid_topo; +extern struct scx_cid_topo __rcu *scx_cid_topo; extern struct btf_id_set8 scx_kfunc_ids_init; void scx_cmask_clear(struct scx_cmask *m); diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 2d0ab78b0efa..cd7cbd1c99a2 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -1450,20 +1450,14 @@ static inline bool task_scx_migrating(struct task_struct *p) return p->scx.sticky_cpu >= 0; } -/* - * Call ops.dequeue() if the task is in BPF custody and not migrating. - * Clears %SCX_TASK_IN_CUSTODY when the callback is invoked. - */ -static void call_task_dequeue(struct scx_sched *sch, struct rq *rq, - struct task_struct *p, u64 deq_flags) +/* Must be called under the lock serializing @p's custody transfers. */ +static bool task_leave_custody(struct task_struct *p) { if (!(p->scx.flags & SCX_TASK_IN_CUSTODY) || task_scx_migrating(p)) - return; - - if (SCX_HAS_OP(sch, dequeue)) - SCX_CALL_OP_TASK(sch, dequeue, rq, p, deq_flags); + return false; p->scx.flags &= ~SCX_TASK_IN_CUSTODY; + return true; } static void local_dsq_post_enq(struct scx_sched *sch, struct scx_dispatch_q *dsq, @@ -1471,7 +1465,8 @@ static void local_dsq_post_enq(struct scx_sched *sch, struct scx_dispatch_q *dsq { struct rq *rq = container_of(dsq, struct rq, scx.local_dsq); - call_task_dequeue(sch, rq, p, 0); + if (task_leave_custody(p) && SCX_HAS_OP(sch, dequeue)) + SCX_CALL_OP_TASK(sch, dequeue, rq, p, 0); /* * Note that @rq's lock may be dropped between this enqueue and @p @@ -1634,16 +1629,28 @@ static void dispatch_enqueue(struct scx_sched *sch, struct rq *rq, if (is_local) { local_dsq_post_enq(sch, dsq, p, enq_flags); } else { + bool call_dequeue = false; + /* - * Task on global/bypass DSQ: leave custody, task on - * non-terminal DSQ: enter custody. + * Global and bypass DSQs are terminal - the task leaves the + * scheduler's custody, so ops.dequeue() fires. It can run + * without @p's rq lock (finish_dispatch() passes the dispatch + * rq); that's safe because dequeue_task_scx() waits on + * SCX_OPSS_DISPATCHING (see the ops_state note above) and so + * can't race it. A non-terminal DSQ keeps the task in custody. + * The custody transfer happens under @dsq->lock so that later + * consumers see the flag clear; the callback runs after + * @dsq->lock is dropped because it may lock a DSQ itself. */ if (dsq->id == SCX_DSQ_GLOBAL || dsq->id == SCX_DSQ_BYPASS) - call_task_dequeue(sch, rq, p, 0); + call_dequeue = task_leave_custody(p); else p->scx.flags |= SCX_TASK_IN_CUSTODY; raw_spin_unlock(&dsq->lock); + + if (call_dequeue && SCX_HAS_OP(sch, dequeue)) + SCX_CALL_OP_TASK(sch, dequeue, rq, p, 0); } /* @@ -2111,7 +2118,7 @@ static void ops_dequeue(struct rq *rq, struct task_struct *p, u64 deq_flags) /* * A queued task must always be in BPF scheduler's custody. If * SCX_TASK_IN_CUSTODY is clear, finish_dispatch() on another - * CPU has already passed call_task_dequeue() (which clears the + * CPU has already passed task_leave_custody() (which clears the * flag), but has not yet written SCX_OPSS_NONE. That final * store does not require this rq's lock, so retrying with * cpu_relax() is bounded: we will observe NONE (or DISPATCHING, @@ -2159,7 +2166,8 @@ static void ops_dequeue(struct rq *rq, struct task_struct *p, u64 deq_flags) * NONE but the task may still have %SCX_TASK_IN_CUSTODY set until * it is enqueued on the destination. */ - call_task_dequeue(sch, rq, p, deq_flags); + if (task_leave_custody(p) && SCX_HAS_OP(sch, dequeue)) + SCX_CALL_OP_TASK(sch, dequeue, rq, p, deq_flags); } static bool dequeue_task_scx(struct rq *rq, struct task_struct *p, int core_deq_flags) @@ -2263,13 +2271,10 @@ static void wakeup_preempt_scx(struct rq *rq, struct task_struct *p, int wake_fl static void move_local_task_to_local_dsq(struct scx_sched *sch, struct task_struct *p, u64 enq_flags, - struct scx_dispatch_q *src_dsq, struct rq *dst_rq) { struct scx_dispatch_q *dst_dsq = &dst_rq->scx.local_dsq; - /* @dsq is locked and @p is on @dst_rq */ - lockdep_assert_held(&src_dsq->lock); lockdep_assert_rq_held(dst_rq); WARN_ON_ONCE(p->scx.holding_cpu >= 0); @@ -2513,9 +2518,8 @@ static struct rq *move_task_between_dsqs(struct scx_sched *sch, /* @p is going from a non-local DSQ to a local DSQ */ if (src_rq == dst_rq) { task_unlink_from_dsq(p, src_dsq); - move_local_task_to_local_dsq(sch, p, enq_flags, - src_dsq, dst_rq); raw_spin_unlock(&src_dsq->lock); + move_local_task_to_local_dsq(sch, p, enq_flags, dst_rq); } else { raw_spin_unlock(&src_dsq->lock); move_remote_task_to_local_dsq(p, enq_flags, @@ -2566,8 +2570,8 @@ static bool consume_dispatch_q(struct scx_sched *sch, struct rq *rq, if (rq == task_rq) { task_unlink_from_dsq(p, dsq); - move_local_task_to_local_dsq(sch, p, enq_flags, dsq, rq); raw_spin_unlock(&dsq->lock); + move_local_task_to_local_dsq(sch, p, enq_flags, rq); return true; } @@ -4290,6 +4294,14 @@ static u32 reenq_local(struct scx_sched *sch, struct rq *rq, u64 reenq_flags) if (!local_task_should_reenq(p, &reenq_flags, &reason)) continue; + /* + * The dispatcher stores the final ops_state after dropping the DSQ + * lock, so @p can be found on a DSQ while still + * %SCX_OPSS_DISPATCHING. Reenqueueing @p before that store lands + * would have it clobber the new %SCX_OPSS_QUEUED. + */ + if (unlikely(atomic_long_read_acquire(&p->scx.ops_state) == SCX_OPSS_DISPATCHING)) + wait_ops_state(p, SCX_OPSS_DISPATCHING); dispatch_dequeue(rq, p); if (WARN_ON_ONCE(p->scx.flags & SCX_TASK_REENQ_REASON_MASK)) @@ -4412,6 +4424,8 @@ static void reenq_user(struct rq *rq, struct scx_dispatch_q *dsq, u64 reenq_flag } /* @p is on @dsq, its rq and @dsq are locked */ + if (unlikely(atomic_long_read_acquire(&p->scx.ops_state) == SCX_OPSS_DISPATCHING)) + wait_ops_state(p, SCX_OPSS_DISPATCHING); dispatch_dequeue_locked(p, dsq); raw_spin_unlock(&dsq->lock); @@ -6221,6 +6235,7 @@ static inline void scx_sub_disable(struct scx_sched *sch) { } static void scx_root_disable(struct scx_sched *sch) { + struct scx_cid_topo *topo; struct scx_task_iter sti; struct task_struct *p; bool was_switched_all; @@ -6350,8 +6365,15 @@ static void scx_root_disable(struct scx_sched *sch) */ cpus_read_lock(); RCU_INIT_POINTER(scx_root, NULL); + topo = rcu_replace_pointer(scx_cid_topo, NULL, + lockdep_is_held(&scx_enable_mutex)); cpus_read_unlock(); + if (topo) { + synchronize_rcu(); + kfree(topo); + } + /* * Delete the kobject from the hierarchy synchronously. Otherwise, sysfs * could observe an object of the same name still in the hierarchy when @@ -9625,10 +9647,10 @@ __bpf_kfunc void scx_bpf_kick_cpu(s32 cpu, u64 flags, const struct bpf_prog_aux * @flags: %SCX_KICK_* flags * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs * - * cid-addressed equivalent of scx_bpf_kick_cpu(). Return 0 on success, - * -errno otherwise. + * cid-addressed equivalent of scx_bpf_kick_cpu(). An invalid @cid aborts the + * scheduler via scx_cid_to_cpu(). */ -__bpf_kfunc s32 scx_bpf_kick_cid(s32 cid, u64 flags, const struct bpf_prog_aux *aux) +__bpf_kfunc void scx_bpf_kick_cid(s32 cid, u64 flags, const struct bpf_prog_aux *aux) { struct scx_sched *sch; s32 cpu; @@ -9636,12 +9658,11 @@ __bpf_kfunc s32 scx_bpf_kick_cid(s32 cid, u64 flags, const struct bpf_prog_aux * guard(rcu)(); sch = scx_prog_sched(aux); if (unlikely(!sch)) - return -ENODEV; + return; cpu = scx_cid_to_cpu(sch, cid); if (cpu < 0) - return cpu; + return; scx_kick_cpu(sch, cpu, flags); - return 0; } /** diff --git a/kernel/sched/ext/types.h b/kernel/sched/ext/types.h index bc74eafd43f1..0b80e40436e1 100644 --- a/kernel/sched/ext/types.h +++ b/kernel/sched/ext/types.h @@ -53,6 +53,10 @@ enum scx_consts { * over [0, nr_cores_at_init) with no gaps. No-topo cids have all fields set * to -1. * + * New fields are appended, never inserted: scx_bpf_cid_topo() copies this + * struct out sized by the program's own layout, and an older program's copy + * must stay a prefix of the kernel's. + * * @core_cid: first cid of this cid's core (smt-sibling group) * @core_idx: global index of that core, in [0, nr_cores_at_init) * @llc_cid: first cid of this cid's LLC diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index db34eb74804d..b6cace1d0e32 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1439,7 +1439,7 @@ static inline int get_sched_cache_scale(int mul) return (1 + (tol - 1) * mul); } -static bool exceed_llc_capacity(struct mm_struct *mm, int cpu) +static bool exceed_llc_capacity(struct sched_cache_group *grp, int cpu) { #ifdef CONFIG_NUMA_BALANCING unsigned long llc, footprint; @@ -1453,11 +1453,6 @@ static bool exceed_llc_capacity(struct mm_struct *mm, int cpu) return true; if (static_branch_likely(&sched_numa_balancing)) { - struct sched_cache_group *grp = READ_ONCE(mm->sched_cache_grp); - - if (!grp) - return true; - /* * TBD: RDT exclusive LLC ways reserved should be * excluded. @@ -1492,10 +1487,9 @@ static bool exceed_llc_capacity(struct mm_struct *mm, int cpu) return false; } -static bool invalid_llc_nr(struct mm_struct *mm, struct task_struct *p, +static bool invalid_llc_nr(struct sched_cache_group *grp, struct task_struct *p, int cpu) { - struct sched_cache_group *grp; int scale; if (get_nr_threads(p) <= 1) @@ -1509,14 +1503,32 @@ static bool invalid_llc_nr(struct mm_struct *mm, struct task_struct *p, if (scale == INT_MAX) return false; - grp = READ_ONCE(mm->sched_cache_grp); - if (!grp) - return true; - return !fits_capacity((READ_ONCE(grp->nr_running_avg) * cpu_smt_num_threads), (scale * per_cpu(sd_llc_size, cpu))); } +/* + * A task counts in nr_pref_llc_running while it is queued on its preferred + * LLC (pref_llc_queued) and runnable (!sched_delayed), keeping the counter in + * the runnable domain so alb_break_llc() can compare it with h_nr_runnable. + */ +static bool task_pref_llc_runnable(struct task_struct *p) +{ + return p->pref_llc_queued && !p->se.sched_delayed; +} + +static void pref_llc_running_inc(struct rq *rq, struct task_struct *p) +{ + if (task_pref_llc_runnable(p)) + rq->nr_pref_llc_running++; +} + +static void pref_llc_running_dec(struct rq *rq, struct task_struct *p) +{ + if (task_pref_llc_runnable(p)) + rq->nr_pref_llc_running--; +} + static void account_llc_enqueue(struct rq *rq, struct task_struct *p) { int pref_llc, pref_llc_queued; @@ -1528,7 +1540,6 @@ static void account_llc_enqueue(struct rq *rq, struct task_struct *p) pref_llc_queued = (pref_llc == task_llc(p)); rq->nr_llc_running++; - rq->nr_pref_llc_running += pref_llc_queued; /* * Record whether p is enqueued on its preferred @@ -1546,6 +1557,9 @@ static void account_llc_enqueue(struct rq *rq, struct task_struct *p) */ p->pref_llc_queued = pref_llc_queued; + /* Skipped while delayed; clear_delayed() adds it back on wake. */ + pref_llc_running_inc(rq, p); + sd = rcu_dereference_all(rq->sd); if (sd && (unsigned int)pref_llc < sd->llc_max) sd->llc_counts[pref_llc]++; @@ -1562,7 +1576,12 @@ static void account_llc_dequeue(struct rq *rq, struct task_struct *p) rq->nr_llc_running--; if (p->pref_llc_queued) { - rq->nr_pref_llc_running--; + /* + * Skipped if still delayed (set_delayed() already removed it); + * clearing pref_llc_queued below also stops clear_delayed() + * from re-adding it. + */ + pref_llc_running_dec(rq, p); /* * Update the status in case * other logic might query @@ -1655,6 +1674,96 @@ static void sched_cache_group_put(struct sched_cache_group *grp) call_rcu(&grp->rcu, sched_cache_group_free_rcu); } +DEFINE_FREE(sched_cache_group_put, struct sched_cache_group *, + sched_cache_group_put(_T)); + +#define rcu_deref_sched_cache_grp(tsk) \ + rcu_dereference_check((tsk)->sched_cache_grp, (tsk) == current) + +static struct sched_cache_group *sched_cache_replace_grp(struct task_struct *p, + struct sched_cache_group *new) +{ + struct sched_cache_group *old; + + old = rcu_deref_sched_cache_grp(p); + rcu_assign_pointer(p->sched_cache_grp, new); + + return old; +} + +struct sched_cache_group *sched_cache_group_get(struct sched_cache_group *grp) +{ + /* + * refcount_inc_not_zero() is the acquire primitive for lockless + * (RCU) lookups; plain refcount_inc() would scribble the count if + * it already reached zero. Return NULL in that case. + */ + if (grp && !refcount_inc_not_zero(&grp->refcnt)) + grp = NULL; + + return grp; +} + +struct sched_cache_group *task_cache_group_get(struct task_struct *p) +{ + guard(rcu)(); + return sched_cache_group_get(rcu_dereference(p->sched_cache_grp)); +} + +void sched_cache_fork(struct task_struct *p) +{ + /* + * The child takes its own reference on the mm's cache group, separate + * from the reference held by the mm. @p is not yet visible to readers, + * so a plain initializing store is enough. + */ + RCU_INIT_POINTER(p->sched_cache_grp, + sched_cache_group_get(p->mm->sched_cache_grp)); +} + +void sched_cache_fork_cleanup(struct task_struct *p) +{ + /* + * A fork that fails after sched_cache_fork() never reaches exit_mm(), + * so drop the reference here. @p never became visible, so there are no + * concurrent readers and the reference we hold keeps the group alive. + */ + sched_cache_group_put(rcu_access_pointer(p->sched_cache_grp)); + RCU_INIT_POINTER(p->sched_cache_grp, NULL); +} + +void sched_cache_exec_mmap(struct task_struct *p, struct mm_struct *mm) +{ + struct sched_cache_group *old; + + /* + * Acquire the new reference before publishing the pointer, then drop + * the old one. @p is current and the only writer of its own pointer. + */ + old = sched_cache_replace_grp(p, sched_cache_group_get(mm->sched_cache_grp)); + sched_cache_group_put(old); +} + +void sched_cache_exit_mm(struct task_struct *p) +{ + struct sched_cache_group *grp = sched_cache_replace_grp(p, NULL); + +#ifdef CONFIG_NUMA_BALANCING + /* + * Subtract this task's footprint from the group before dropping the + * reference, so the group footprint converges as its threads exit. + * Unlocked for performance; clamp to avoid underflow. + */ + if (grp && p->total_numa_faults) { + unsigned long fp = READ_ONCE(grp->footprint); + unsigned long sub = min(fp, p->total_numa_faults); + + WRITE_ONCE(grp->footprint, fp - sub); + } +#endif + sched_cache_group_put(grp); +} + void mm_destroy_sched(struct mm_struct *mm) { sched_cache_group_put(mm->sched_cache_grp); @@ -1709,15 +1818,10 @@ static unsigned long fraction_mm_sched(struct rq *rq, return div64_u64(NICE_0_LOAD * pcpu_sched->runtime, rq->cpu_runtime + 1); } -static int get_pref_llc(struct task_struct *p, struct mm_struct *mm) +static int get_pref_llc(struct task_struct *p, struct sched_cache_group *grp) { int mm_sched_llc = -1, mm_sched_cpu; - struct sched_cache_group *grp; - - if (!mm) - return -1; - grp = READ_ONCE(mm->sched_cache_grp); if (!grp) return -1; @@ -1751,9 +1855,8 @@ static unsigned int task_running_on_cpu(int cpu, struct task_struct *p); static inline void account_mm_sched(struct rq *rq, struct task_struct *p, s64 delta_exec) { + struct sched_cache_group *grp = rcu_dereference_all(p->sched_cache_grp); struct sched_cache_time *pcpu_sched; - struct sched_cache_group *grp; - struct mm_struct *mm = p->mm; int mm_sched_llc = -1; unsigned long epoch; @@ -1764,13 +1867,15 @@ void account_mm_sched(struct rq *rq, struct task_struct *p, s64 delta_exec) return; /* * init_task, kthreads and user thread created - * by user_mode_thread() don't have mm. + * by user_mode_thread() don't have a cache group. + * In theory a kernel thread does not have any valid + * cache group, because sched_cache_fork() is not + * invoked for a kernel thread - !grp should gate the + * kernel thread. Use the PF_KTHREAD check explicitly + * here for safety reasons, to guard against future + * modifications and to pair with task_tick_cache(). */ - if (!mm) - return; - - grp = READ_ONCE(mm->sched_cache_grp); - if (!grp || !grp->pcpu_sched) + if (p->flags & PF_KTHREAD || !grp || !grp->pcpu_sched) return; pcpu_sched = per_cpu_ptr(grp->pcpu_sched, cpu_of(rq)); @@ -1787,13 +1892,13 @@ void account_mm_sched(struct rq *rq, struct task_struct *p, s64 delta_exec) * its preferred state. */ if ((long)(epoch - READ_ONCE(grp->epoch)) > llc_epoch_affinity_timeout || - invalid_llc_nr(mm, p, cpu_of(rq)) || - exceed_llc_capacity(mm, cpu_of(rq))) { + invalid_llc_nr(grp, p, cpu_of(rq)) || + exceed_llc_capacity(grp, cpu_of(rq))) { if (READ_ONCE(grp->cpu) != -1) WRITE_ONCE(grp->cpu, -1); } - mm_sched_llc = get_pref_llc(p, mm); + mm_sched_llc = get_pref_llc(p, grp); /* task not on rq accounted later in account_entity_enqueue() */ if (task_running_on_cpu(rq->cpu, p) && @@ -1806,19 +1911,15 @@ void account_mm_sched(struct rq *rq, struct task_struct *p, s64 delta_exec) static void task_tick_cache(struct rq *rq, struct task_struct *p) { + struct sched_cache_group *grp = rcu_dereference_all(p->sched_cache_grp); struct callback_head *work = &p->cache_work; - struct sched_cache_group *grp; - struct mm_struct *mm = p->mm; unsigned long epoch; if (!sched_cache_enabled()) return; - if (!mm || p->flags & PF_KTHREAD) - return; - - grp = READ_ONCE(mm->sched_cache_grp); - if (!grp || !grp->pcpu_sched) + if (!grp || p->flags & PF_KTHREAD || + !grp->pcpu_sched) return; epoch = rq->cpu_epoch; @@ -1900,14 +2001,13 @@ static inline void update_avg_scale(u64 *avg, u64 sample) static void task_cache_work(struct callback_head *work) { + struct sched_cache_group *grp __free(sched_cache_group_put) = NULL; + cpumask_var_t cpus __free(free_cpumask_var) = CPUMASK_VAR_NULL; int cpu, m_a_cpu = -1, nr_running = 0, curr_cpu; unsigned long next_scan, now = jiffies; struct task_struct *p = current, *cur; unsigned long curr_m_a_occ = 0; - struct sched_cache_group *grp; - struct mm_struct *mm = p->mm; unsigned long m_a_occ = 0; - cpumask_var_t cpus; WARN_ON_ONCE(work != &p->cache_work); @@ -1916,7 +2016,12 @@ static void task_cache_work(struct callback_head *work) if (p->flags & PF_EXITING) return; - grp = READ_ONCE(mm->sched_cache_grp); + /* + * A reference makes sure grp is not released by others. The rcu + * lock can not be held till after zalloc_cpumask_var() below, + * because the latter might sleep. + */ + grp = task_cache_group_get(p); if (!grp) return; @@ -1931,8 +2036,8 @@ static void task_cache_work(struct callback_head *work) return; curr_cpu = task_cpu(p); - if (invalid_llc_nr(mm, p, curr_cpu) || - exceed_llc_capacity(mm, curr_cpu)) { + if (invalid_llc_nr(grp, p, curr_cpu) || + exceed_llc_capacity(grp, curr_cpu)) { if (READ_ONCE(grp->cpu) != -1) WRITE_ONCE(grp->cpu, -1); @@ -1965,9 +2070,13 @@ static void task_cache_work(struct callback_head *work) m_cpu = i; } + /* + * rcu_access_pointer() is used because the + * pointer is only compared, never dereferenced. + */ cur = rcu_dereference_all(cpu_rq(i)->curr); if (cur && !(cur->flags & (PF_EXITING | PF_KTHREAD)) && - cur->mm == mm) + rcu_access_pointer(cur->sched_cache_grp) == grp) nr_running++; } @@ -2013,7 +2122,6 @@ static void task_cache_work(struct callback_head *work) } update_avg_scale(&grp->nr_running_avg, nr_running); - free_cpumask_var(cpus); } void init_sched_mm(struct task_struct *p) @@ -2022,11 +2130,19 @@ void init_sched_mm(struct task_struct *p) init_task_work(work, task_cache_work); work->next = work; + /* + * dup_task_struct() copies the parent's task_struct, including its + * sched_cache_grp, for which the child holds no reference. Clear it + * here - before copy_mm() runs - so the child never carries a + * borrowed pointer that the fork error path would put. + */ + RCU_INIT_POINTER(p->sched_cache_grp, NULL); /* * Reset new task's preference to avoid * polluting account_llc_enqueue(). */ p->preferred_llc = -1; + p->pref_llc_queued = 0; } #else /* CONFIG_SCHED_CACHE */ @@ -2048,6 +2164,10 @@ static void account_llc_enqueue(struct rq *rq, struct task_struct *p) {} static void account_llc_dequeue(struct rq *rq, struct task_struct *p) {} +static void pref_llc_running_inc(struct rq *rq, struct task_struct *p) {} + +static void pref_llc_running_dec(struct rq *rq, struct task_struct *p) {} + #endif /* CONFIG_SCHED_CACHE */ /* @@ -3811,10 +3931,9 @@ static void task_numa_placement(struct task_struct *p) * heuristic and occasional lost updates are tolerable. * * If a task exits, its corresponding footprint must - * be subtracted from the mm->sched_cache_grp->footprint, - * otherwise the mm->sched_cache_grp->footprint will not - * converge: the exiting thread's footprint remains - * unchanged/undecayed in mm->sched_cache_grp->footprint. + * be subtracted from p->sched_cache_grp->footprint, + * otherwise the footprint will not converge: the + * exiting thread's footprint remains unchanged/undecayed. * See exit_mm(). * * Lost updates and unsynchronized subtraction @@ -3822,12 +3941,14 @@ static void task_numa_placement(struct task_struct *p) * go negative. Clamp to zero to prevent the * unsigned footprint from wrapping. */ - grp = READ_ONCE(p->mm->sched_cache_grp); - if (!grp) - continue; + scoped_guard(rcu) { + grp = rcu_dereference(p->sched_cache_grp); - new_fp = (long)READ_ONCE(grp->footprint) + diff; - WRITE_ONCE(grp->footprint, max(new_fp, 0L)); + if (grp) { + new_fp = (long)READ_ONCE(grp->footprint) + diff; + WRITE_ONCE(grp->footprint, max(new_fp, 0L)); + } + } #endif } @@ -6278,15 +6399,27 @@ static __always_inline void return_cfs_rq_runtime(struct cfs_rq *cfs_rq); static void set_delayed(struct sched_entity *se) { - se->sched_delayed = 1; - /* * Delayed se of cfs_rq have no tasks queued on them. * Do not adjust h_nr_runnable since dequeue_entities() * will account it for blocked tasks. + * + * This check can be removed because when flat pick + * patches get merged as only task can get delayed, + * same for clear_delayed(). */ - if (!entity_is_task(se)) + if (!entity_is_task(se)) { + se->sched_delayed = 1; return; + } + + /* + * Drop a task leaving the runnable set. + * Needs to be called before sched_delayed is set. + * clear_delayed() mirrors this after clearing the flag. + */ + pref_llc_running_dec(rq_of(cfs_rq_of(se)), task_of(se)); + se->sched_delayed = 1; for_each_sched_entity(se) { struct cfs_rq *cfs_rq = cfs_rq_of(se); @@ -6308,6 +6441,13 @@ static void clear_delayed(struct sched_entity *se) if (!entity_is_task(se)) return; + /* + * Re-add on wake, after sched_delayed is cleared. On a final delayed + * dequeue account_llc_dequeue() already cleared pref_llc_queued, so + * this does nothing. + */ + pref_llc_running_inc(rq_of(cfs_rq_of(se)), task_of(se)); + for_each_sched_entity(se) { struct cfs_rq *cfs_rq = cfs_rq_of(se); @@ -10342,6 +10482,7 @@ enum migration_type { #define LBF_SOME_PINNED 0x08 #define LBF_ACTIVE_LB 0x10 #define LBF_LLC_PINNED 0x20 +#define LBF_ACTIVE_LB_LLC 0x40 struct lb_env { struct sched_domain *sd; @@ -10651,23 +10792,41 @@ static enum llc_mig can_migrate_llc(int src_cpu, int dst_cpu, return mig_llc; } +static inline bool task_misfits_asym_cpu(struct lb_env *env, struct task_struct *p) +{ + /* + * On asymmetric CPU capacity domains, do not let cache-aware + * balancing pull the task onto a destination CPU that cannot + * accommodate it. Doing so would turn the task into a misfit on + * the destination, trading a cache-locality gain for a capacity + * loss. If the task already does not fit its source CPU, the move + * cannot make things worse, so let the LLC preference decide. + */ + if ((env->sd->flags & SD_ASYM_CPUCAPACITY) && p && + !task_fits_cpu(p, env->dst_cpu) && + task_fits_cpu(p, env->src_cpu)) + return true; + + return false; +} + /* * Check if task p can migrate from source LLC to * destination LLC in terms of cache aware load balance. */ -static enum llc_mig can_migrate_llc_task(int src_cpu, int dst_cpu, +static enum llc_mig can_migrate_llc_task(struct lb_env *env, struct task_struct *p) { struct sched_cache_group *grp; - struct mm_struct *mm; bool to_pref; - int cpu; + int cpu, src_cpu, dst_cpu; - mm = p->mm; - if (!mm) - return mig_unrestricted; + if (task_misfits_asym_cpu(env, p)) + return mig_forbid; - grp = READ_ONCE(mm->sched_cache_grp); + src_cpu = env->src_cpu; + dst_cpu = env->dst_cpu; + grp = rcu_dereference_all(p->sched_cache_grp); if (!grp) return mig_unrestricted; @@ -10676,8 +10835,8 @@ static enum llc_mig can_migrate_llc_task(int src_cpu, int dst_cpu, return mig_unrestricted; /* skip cache aware load balance for too many threads */ - if (invalid_llc_nr(mm, p, dst_cpu) || - exceed_llc_capacity(mm, dst_cpu)) { + if (invalid_llc_nr(grp, p, dst_cpu) || + exceed_llc_capacity(grp, dst_cpu)) { if (READ_ONCE(grp->cpu) != -1) WRITE_ONCE(grp->cpu, -1); return mig_unrestricted; @@ -10723,6 +10882,14 @@ alb_break_llc(struct lb_env *env) unsigned long util = 0; struct task_struct *cur; + /* + * Migrating misfit tasks from current CPU + * to CPU with a better fit. + * Prioritize that over LLC preference. + */ + if (env->migration_type == migrate_misfit) + return false; + if (env->src_rq->nr_running <= 1) return true; @@ -10730,7 +10897,8 @@ alb_break_llc(struct lb_env *env) if (cur && cur->sched_class == &fair_sched_class) util = task_util(cur); - if (can_migrate_llc(env->src_cpu, env->dst_cpu, + if (task_misfits_asym_cpu(env, cur) || + can_migrate_llc(env->src_cpu, env->dst_cpu, util, false) == mig_forbid) return true; } @@ -10738,6 +10906,21 @@ alb_break_llc(struct lb_env *env) return false; } +/* + * Returns true if p's preferred LLC does not match the destination CPU + * under migrate_llc_task semantics. Passive LB passes migrate_llc_task + * in env->migration_type, while active LB carries LBF_ACTIVE_LB_LLC in + * env->flags to avoid overwriting env->migration_type. + */ +static inline bool +migrate_llc_task_wrong_dst(struct task_struct *p, struct lb_env *env) +{ + return sched_cache_enabled() && + (env->migration_type == migrate_llc_task || + env->flags & LBF_ACTIVE_LB_LLC) && + READ_ONCE(p->preferred_llc) != llc_id(env->dst_cpu); +} + /* * Check if migrating task p from env->src_cpu to * env->dst_cpu breaks LLC localiy. @@ -10766,12 +10949,10 @@ static bool migrate_degrades_llc(struct task_struct *p, struct lb_env *env) * run on env->dst_cpu, skip the tasks do not prefer * env->dst_cpu, and find the one that prefers. */ - if (env->migration_type == migrate_llc_task && - READ_ONCE(p->preferred_llc) != llc_id(env->dst_cpu)) + if (migrate_llc_task_wrong_dst(p, env)) return true; - if (can_migrate_llc_task(env->src_cpu, - env->dst_cpu, p) != mig_forbid) + if (can_migrate_llc_task(env, p) != mig_forbid) return false; return true; @@ -10790,6 +10971,12 @@ alb_break_llc(struct lb_env *env) return false; } +static inline bool +migrate_llc_task_wrong_dst(struct task_struct *p, struct lb_env *env) +{ + return false; +} + static inline bool migrate_degrades_llc(struct task_struct *p, struct lb_env *env) { @@ -10889,7 +11076,7 @@ int can_migrate_task(struct task_struct *p, struct lb_env *env) * 4) too many balance attempts have failed. */ if (env->flags & LBF_ACTIVE_LB) - return 1; + return !migrate_llc_task_wrong_dst(p, env); degrades = migrate_degrades_locality(p, env); if (!degrades) { @@ -11834,6 +12021,15 @@ static inline bool llc_balance(struct lb_env *env, struct sg_lb_stats *sgs, if (env->sd->flags & SD_SHARE_LLC) return false; + /* + * On asymmetric domains, group_misfit_task_load + * should be prioritized to move tasks to CPU that fit them + * over aggregating tasks to their preferred LLC. + */ + if ((env->sd->flags & SD_ASYM_CPUCAPACITY) && + sgs->group_misfit_task_load) + return false; + /* * Skip cache aware tagging if nr_balanced_failed is sufficiently high. * Threshold of cache_nice_tries is set to 1 higher than nr_balance_failed @@ -13251,6 +13447,20 @@ static int need_active_balance(struct lb_env *env) } static int active_load_balance_cpu_stop(void *data); +static int active_load_balance_llc_cpu_stop(void *data); + +/* + * migration_type is checked elsewhere to decide migration policy, so + * it shouldn't be repurposed just to flag an LLC-directed active + * balance across the stopper. Pick the callback here instead. + */ +static inline cpu_stop_fn_t alb_stop_fn(struct lb_env *env) +{ + if (env->migration_type == migrate_llc_task) + return active_load_balance_llc_cpu_stop; + + return active_load_balance_cpu_stop; +} static int should_we_balance(struct lb_env *env) { @@ -13587,7 +13797,7 @@ static int sched_balance_rq(int this_cpu, struct rq *this_rq, raw_spin_rq_unlock_irqrestore(busiest, flags); if (active_balance) { stop_one_cpu_nowait(cpu_of(busiest), - active_load_balance_cpu_stop, busiest, + alb_stop_fn(&env), busiest, &busiest->active_balance_work); } preempt_enable(); @@ -13698,7 +13908,7 @@ update_next_balance(struct sched_domain *sd, unsigned long *next_balance) * least 1 task to be running on each physical CPU where possible, and * avoids physical / logical imbalances. */ -static int active_load_balance_cpu_stop(void *data) +static int __active_load_balance_cpu_stop(void *data, unsigned int lb_flags) { struct rq *busiest_rq = data; int busiest_cpu = cpu_of(busiest_rq); @@ -13748,7 +13958,7 @@ static int active_load_balance_cpu_stop(void *data) .src_cpu = busiest_rq->cpu, .src_rq = busiest_rq, .idle = CPU_IDLE, - .flags = LBF_ACTIVE_LB, + .flags = LBF_ACTIVE_LB | lb_flags, }; schedstat_inc(sd->alb_count); @@ -13776,6 +13986,16 @@ static int active_load_balance_cpu_stop(void *data) return 0; } +static int active_load_balance_cpu_stop(void *data) +{ + return __active_load_balance_cpu_stop(data, 0); +} + +static int active_load_balance_llc_cpu_stop(void *data) +{ + return __active_load_balance_cpu_stop(data, LBF_ACTIVE_LB_LLC); +} + /* * Scale the max sched_balance_rq interval with the number of CPUs in the system. * This trades load-balance latency on larger machines for less cross talk. diff --git a/kernel/sysctl.c b/kernel/sysctl.c index c9efb17cc255..0392f15e98c2 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -478,7 +478,7 @@ int proc_int_k2u_conv_kop(ulong *u_ptr, const int *k_ptr, bool *negp, if (val < 0) { *negp = true; - *u_ptr = k_ptr_op ? -k_ptr_op((ulong)val) : -(ulong)val; + *u_ptr = k_ptr_op ? k_ptr_op(-(ulong)val) : -(ulong)val; } else { *negp = false; *u_ptr = k_ptr_op ? k_ptr_op((ulong)val) : (ulong) val; diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c index f4c43b9716f8..61dd7df6e3a4 100644 --- a/kernel/time/jiffies.c +++ b/kernel/time/jiffies.c @@ -101,6 +101,8 @@ void __init register_refined_jiffies(long cycles_per_second) #ifdef CONFIG_PROC_SYSCTL static ulong mult_hz(const ulong val) { + if (val >= ULONG_MAX / HZ) + return ULONG_MAX; return val * HZ; } diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index 2a1d65754b04..16d66fc06ea4 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -47,6 +47,10 @@ static struct rhltable fprobe_ip_table; static DEFINE_MUTEX(fprobe_mutex); static struct fgraph_ops fprobe_graph_ops; +DEFINE_LOCK_GUARD_0(rcu_sched_notrace, + rcu_read_lock_sched_notrace(), + rcu_read_unlock_sched_notrace()) + static u32 fprobe_node_hashfn(const void *data, u32 len, u32 seed) { return hash_ptr(*(unsigned long **)data, 32); @@ -329,16 +333,14 @@ static void fprobe_ftrace_entry(unsigned long ip, unsigned long parent_ip, struct fprobe *fp; int bit; + if (!rcu_is_watching()) + return; + bit = ftrace_test_recursion_trylock(ip, parent_ip); if (bit < 0) return; - /* - * ftrace_test_recursion_trylock() disables preemption, but - * rhltable_lookup() checks whether rcu_read_lcok is held. - * So we take rcu_read_lock() here. - */ - rcu_read_lock(); + guard(rcu_sched_notrace)(); head = rhltable_lookup(&fprobe_ip_table, &ip, fprobe_rht_params); rhl_for_each_entry_rcu(node, pos, head, hlist) { @@ -353,7 +355,6 @@ static void fprobe_ftrace_entry(unsigned long ip, unsigned long parent_ip, else __fprobe_handler(ip, parent_ip, fp, fregs, NULL); } - rcu_read_unlock(); ftrace_test_recursion_unlock(bit); } NOKPROBE_SYMBOL(fprobe_ftrace_entry); @@ -574,10 +575,13 @@ static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops struct fprobe *fp; int used, ret; + if (!rcu_is_watching()) + return 0; + if (WARN_ON_ONCE(!fregs)) return 0; - guard(rcu)(); + guard(rcu_sched_notrace)(); head = rhltable_lookup(&fprobe_ip_table, &func, fprobe_rht_params); reserved_words = 0; rhl_for_each_entry_rcu(node, pos, head, hlist) { @@ -672,13 +676,16 @@ static void fprobe_return(struct ftrace_graph_ret *trace, int size, curr; int size_words; + if (!rcu_is_watching()) + return; + fgraph_data = (unsigned long *)fgraph_retrieve_data(gops->idx, &size); if (WARN_ON_ONCE(!fgraph_data)) return; size_words = SIZE_IN_LONG(size); ret_ip = ftrace_regs_get_instruction_pointer(fregs); - preempt_disable_notrace(); + guard(rcu_sched_notrace)(); curr = 0; while (size_words > curr) { @@ -694,7 +701,6 @@ static void fprobe_return(struct ftrace_graph_ret *trace, } curr += size; } - preempt_enable_notrace(); } NOKPROBE_SYMBOL(fprobe_return); diff --git a/mm/damon/core.c b/mm/damon/core.c index c66fd59eb8ff..40714c69d1b1 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -2949,7 +2949,7 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s) unsigned long cumulated_sz, cached_esz; unsigned int score, max_score = 0; - if (!quota->ms && !quota->sz && list_empty("a->goals)) + if (!damos_quota_is_set(quota)) return; /* First charge window */ diff --git a/mm/kasan/common.c b/mm/kasan/common.c index b7d05c2a6d93..7d21c4db68d2 100644 --- a/mm/kasan/common.c +++ b/mm/kasan/common.c @@ -114,12 +114,23 @@ void kasan_unpoison_task_stack(struct task_struct *task) /* Unpoison the stack for the current task beyond a watermark sp value. */ asmlinkage void kasan_unpoison_task_stack_below(const void *watermark) { + void *base; + + /* + * Only the generic mode needs this. In the tag-based mode the + * compiler fully initializes the shadow of stack variables on + * function entry, so stale tags left below the watermark are + * harmless. + */ + if (!IS_ENABLED(CONFIG_KASAN_GENERIC)) + return; + /* * Calculate the task stack base address. Avoid using 'current' * because this function is called by early resume code which hasn't * yet set up the percpu register (%gs). */ - void *base = (void *)((unsigned long)watermark & ~(THREAD_SIZE - 1)); + base = (void *)((unsigned long)watermark & ~(THREAD_SIZE - 1)); kasan_unpoison(base, watermark - base, false); } diff --git a/mm/shmem.c b/mm/shmem.c index 9001aaf3b7b9..cfb546785295 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1831,8 +1831,11 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode, global_orders = shmem_huge_global_enabled(inode, index, write_end, shmem_huge_force, vma, vm_flags); - /* Tmpfs huge pages allocation */ - if (!vma || !vma_is_anon_shmem(vma)) + /* + * Tmpfs huge pages allocation or forced collapse ignores + * sysfs configs. + */ + if (!vma || !vma_is_anon_shmem(vma) || shmem_huge_force) return global_orders; /* diff --git a/mm/slab.h b/mm/slab.h index f5e336b6b6b0..ba08da09da36 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -24,11 +24,27 @@ #define SLAB_ALLOC_NO_RECURSE 0x04 /* prevent kmalloc() recursion */ #define SLAB_ALLOC_NO_OBJ_EXT 0x08 /* prevent obj_exts array allocation */ +#define SLAB_FREE_DEFAULT 0x00 /* no flags */ +#define SLAB_FREE_NOLOCK 0x01 /* spinning not allowed */ + +static inline unsigned int to_alloc_flags(unsigned int free_flags) +{ + if (free_flags & SLAB_FREE_NOLOCK) + return SLAB_ALLOC_NOLOCK; + else + return SLAB_ALLOC_DEFAULT; +} + static inline bool alloc_flags_allow_spinning(const unsigned int alloc_flags) { return !(alloc_flags & SLAB_ALLOC_NOLOCK); } +static inline bool free_flags_allow_spinning(const unsigned int free_flags) +{ + return !(free_flags & SLAB_FREE_NOLOCK); +} + void *__kmalloc_flags_noprof(DECL_TOKEN_PARAMS(size, token), gfp_t flags, unsigned int alloc_flags, int node) __assume_kmalloc_alignment __alloc_size(1); @@ -436,7 +452,7 @@ static inline bool is_kmalloc_normal(struct kmem_cache *s) return !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT|SLAB_NO_OBJ_EXT)); } -bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj); +bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj, unsigned int free_flags); void flush_all_rcu_sheaves(void); void flush_rcu_sheaves_on_cache(struct kmem_cache *s); diff --git a/mm/slab_common.c b/mm/slab_common.c index 03ecac12cd86..2475fc0e5e41 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -1622,7 +1622,7 @@ static bool kfree_rcu_sheaf(void *obj) s = slab->slab_cache; if (likely(!IS_ENABLED(CONFIG_NUMA) || slab_nid(slab) == numa_mem_id())) - return __kfree_rcu_sheaf(s, obj); + return __kfree_rcu_sheaf(s, obj, SLAB_FREE_DEFAULT); return false; } diff --git a/mm/slub.c b/mm/slub.c index 3791d69ba6f9..130b6fe3fe03 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2777,7 +2777,8 @@ static inline struct slab_sheaf *alloc_empty_sheaf(struct kmem_cache *s, return __alloc_empty_sheaf(s, gfp, alloc_flags, s->sheaf_capacity); } -static void free_empty_sheaf(struct kmem_cache *s, struct slab_sheaf *sheaf) +static void __free_empty_sheaf(struct kmem_cache *s, struct slab_sheaf *sheaf, + unsigned int free_flags) { /* * If the sheaf was created with SLAB_ALLOC_NO_RECURSE flag then its @@ -2789,11 +2790,20 @@ static void free_empty_sheaf(struct kmem_cache *s, struct slab_sheaf *sheaf) mark_obj_codetag_empty(sheaf); VM_WARN_ON_ONCE(sheaf->size > 0); - kfree(sheaf); + + if (unlikely(free_flags & SLAB_FREE_NOLOCK)) + kfree_nolock(sheaf); + else + kfree(sheaf); stat(s, SHEAF_FREE); } +static void free_empty_sheaf(struct kmem_cache *s, struct slab_sheaf *sheaf) +{ + __free_empty_sheaf(s, sheaf, SLAB_FREE_DEFAULT); +} + static unsigned int refill_objects(struct kmem_cache *s, void **p, gfp_t gfp, unsigned int min, unsigned int max); @@ -5901,7 +5911,8 @@ __pcs_replace_full_main(struct kmem_cache *s, struct slub_percpu_sheaves *pcs, if (!allow_spin) return NULL; - empty = alloc_empty_sheaf(s, GFP_NOWAIT, SLAB_ALLOC_DEFAULT); + /* Don't wake up kswapd, it will cause deadlock under pi_lock */ + empty = alloc_empty_sheaf(s, __GFP_NOWARN, SLAB_ALLOC_DEFAULT); if (empty) goto got_empty; @@ -6038,10 +6049,11 @@ static void rcu_free_sheaf(struct rcu_head *head) */ static DEFINE_WAIT_OVERRIDE_MAP(kfree_rcu_sheaf_map, LD_WAIT_CONFIG); -bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj) +bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj, unsigned int free_flags) { struct slub_percpu_sheaves *pcs; struct slab_sheaf *rcu_sheaf; + bool allow_spin = free_flags_allow_spinning(free_flags); if (WARN_ON_ONCE(IS_ENABLED(CONFIG_PREEMPT_RT))) return false; @@ -6054,9 +6066,9 @@ bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj) pcs = this_cpu_ptr(s->cpu_sheaves); if (unlikely(!pcs->rcu_free)) { - struct slab_sheaf *empty; struct node_barn *barn; + unsigned int alloc_flags = to_alloc_flags(free_flags); /* Bootstrap or debug cache, fall back */ if (unlikely(!cache_has_sheaves(s))) { @@ -6076,7 +6088,7 @@ bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj) goto fail; } - empty = barn_get_empty_sheaf(barn, true); + empty = barn_get_empty_sheaf(barn, allow_spin); if (empty) { pcs->rcu_free = empty; @@ -6085,20 +6097,21 @@ bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj) local_unlock(&s->cpu_sheaves->lock); - empty = alloc_empty_sheaf(s, GFP_NOWAIT, SLAB_ALLOC_DEFAULT); + /* Don't wake up kswapd, it will cause deadlock under pi_lock */ + empty = alloc_empty_sheaf(s, __GFP_NOWARN, alloc_flags); if (!empty) goto fail; if (!local_trylock(&s->cpu_sheaves->lock)) { - barn_put_empty_sheaf(barn, empty); + __free_empty_sheaf(s, empty, free_flags); goto fail; } pcs = this_cpu_ptr(s->cpu_sheaves); if (unlikely(pcs->rcu_free)) - barn_put_empty_sheaf(barn, empty); + __free_empty_sheaf(s, empty, free_flags); else pcs->rcu_free = empty; } @@ -6116,6 +6129,12 @@ bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj) if (likely(rcu_sheaf->size < s->sheaf_capacity)) { rcu_sheaf = NULL; } else { + if (unlikely(!allow_spin)) { + /* call_rcu() cannot be called in an unknown context */ + rcu_sheaf->size--; + local_unlock(&s->cpu_sheaves->lock); + goto fail; + } pcs->rcu_free = NULL; rcu_sheaf->node = numa_node_id(); } diff --git a/mm/vma.c b/mm/vma.c index 638ec4793798..8f7f77a4c7ce 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -2745,7 +2745,7 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr, { struct mm_struct *mm = current->mm; struct vm_area_struct *vma = NULL; - bool have_mmap_prepare = file && file->f_op->mmap_prepare; + const bool have_mmap_prepare = file && file->f_op->mmap_prepare; VMA_ITERATOR(vmi, mm, addr); MMAP_STATE(map, mm, &vmi, addr, len, pgoff, vma_flags, file); struct vm_area_desc desc = { @@ -2787,7 +2787,7 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr, allocated_new = true; } - if (have_mmap_prepare) + if (have_mmap_prepare && allocated_new) set_vma_user_defined_fields(vma, &map); __mmap_complete(&map, vma); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 96195d2fd10f..cf44452e0766 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -302,11 +302,13 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) cp.tx_bandwidth = cpu_to_le32(0x00001f40); cp.rx_bandwidth = cpu_to_le32(0x00001f40); + hci_dev_lock(hdev); + switch (conn->codec.id) { case BT_CODEC_MSBC: if (!find_next_esco_param(conn, esco_param_msbc, ARRAY_SIZE(esco_param_msbc))) - return -EINVAL; + goto unlock; param = &esco_param_msbc[conn->attempt - 1]; cp.tx_coding_format.id = 0x05; @@ -332,7 +334,7 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) case BT_CODEC_TRANSPARENT: if (!find_next_esco_param(conn, esco_param_msbc, ARRAY_SIZE(esco_param_msbc))) - return -EINVAL; + goto unlock; param = &esco_param_msbc[conn->attempt - 1]; cp.tx_coding_format.id = 0x03; @@ -359,11 +361,11 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) if (conn->parent && lmp_esco_capable(conn->parent)) { if (!find_next_esco_param(conn, esco_param_cvsd, ARRAY_SIZE(esco_param_cvsd))) - return -EINVAL; + goto unlock; param = &esco_param_cvsd[conn->attempt - 1]; } else { if (conn->attempt > ARRAY_SIZE(sco_param_cvsd)) - return -EINVAL; + goto unlock; param = &sco_param_cvsd[conn->attempt - 1]; } cp.tx_coding_format.id = 2; @@ -386,9 +388,11 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) cp.out_transport_unit_size = 16; break; default: - return -EINVAL; + goto unlock; } + hci_dev_unlock(hdev); + cp.retrans_effort = param->retrans_effort; cp.pkt_type = __cpu_to_le16(param->pkt_type); cp.max_latency = __cpu_to_le16(param->max_latency); @@ -397,6 +401,10 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data) return -EIO; return 0; + +unlock: + hci_dev_unlock(hdev); + return -EINVAL; } static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3ea5ea7267d9..aea017ee5b0a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1486,8 +1486,10 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, BT_DBG("%s removing %pMR (%u)", hdev->name, bdaddr, bdaddr_type); + mutex_lock(&hdev->remote_oob_lock); list_del(&data->list); kfree(data); + mutex_unlock(&hdev->remote_oob_lock); return 0; } @@ -1496,10 +1498,12 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev) { struct oob_data *data, *n; + mutex_lock(&hdev->remote_oob_lock); list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) { list_del(&data->list); kfree(data); } + mutex_unlock(&hdev->remote_oob_lock); } int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, @@ -1508,11 +1512,14 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, { struct oob_data *data; + mutex_lock(&hdev->remote_oob_lock); data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type); if (!data) { data = kmalloc_obj(*data); - if (!data) + if (!data) { + mutex_unlock(&hdev->remote_oob_lock); return -ENOMEM; + } bacpy(&data->bdaddr, bdaddr); data->bdaddr_type = bdaddr_type; @@ -1545,6 +1552,8 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, BT_DBG("%s for %pMR", hdev->name, bdaddr); + mutex_unlock(&hdev->remote_oob_lock); + return 0; } @@ -2482,6 +2491,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv) mutex_init(&hdev->lock); mutex_init(&hdev->req_lock); mutex_init(&hdev->mgmt_pending_lock); + mutex_init(&hdev->remote_oob_lock); ida_init(&hdev->unset_handle_ida); @@ -2554,8 +2564,10 @@ int hci_register_dev(struct hci_dev *hdev) return id; error = dev_set_name(&hdev->dev, "hci%u", id); - if (error) + if (error) { + ida_free(&hci_index_ida, id); return error; + } hdev->name = dev_name(&hdev->dev); hdev->id = id; @@ -3323,6 +3335,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue, skb_shinfo(skb)->frag_list = NULL; + spin_lock_bh(&queue->lock); + __skb_queue_tail(queue, skb); do { @@ -3337,6 +3351,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue, __skb_queue_tail(queue, skb); } while (list); + + spin_unlock_bh(&queue->lock); } bt_dev_dbg(hdev, "hcon %p queued %d", conn, skb_queue_len(queue)); @@ -3400,9 +3416,6 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, struct hci_conn *conn = NULL, *c; unsigned int num = 0, min = ~0; - /* We don't have to lock device here. Connections are always - * added and removed with TX task disabled. */ - rcu_read_lock(); list_for_each_entry_rcu(c, &h->list, list) { @@ -3607,13 +3620,15 @@ static void __check_timeout(struct hci_dev *hdev, unsigned int cnt, u8 type) } /* Schedule SCO */ -static void hci_sched_sco(struct hci_dev *hdev, __u8 type) +static void __hci_sched_sco(struct hci_dev *hdev, __u8 type) { struct hci_conn *conn; struct sk_buff *skb; int quote, *cnt; unsigned int pkts = hdev->sco_pkts; + lockdep_assert_held(&hdev->lock); + bt_dev_dbg(hdev, "type %u", type); if (!hci_conn_num(hdev, type) || !pkts) @@ -3648,6 +3663,13 @@ static void hci_sched_sco(struct hci_dev *hdev, __u8 type) queue_work(hdev->workqueue, &hdev->tx_work); } +static void hci_sched_sco(struct hci_dev *hdev, __u8 type) +{ + hci_dev_lock(hdev); + __hci_sched_sco(hdev, type); + hci_dev_unlock(hdev); +} + static void hci_sched_acl_pkt(struct hci_dev *hdev) { unsigned int cnt = hdev->acl_cnt; @@ -3657,6 +3679,8 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev) __check_timeout(hdev, cnt, ACL_LINK); + hci_dev_lock(hdev); + while (hdev->acl_cnt && (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { u32 priority = (skb_peek(&chan->data_q))->priority; @@ -3681,13 +3705,15 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev) chan->conn->sent++; /* Send pending SCO packets right away */ - hci_sched_sco(hdev, SCO_LINK); - hci_sched_sco(hdev, ESCO_LINK); + __hci_sched_sco(hdev, SCO_LINK); + __hci_sched_sco(hdev, ESCO_LINK); } } if (cnt != hdev->acl_cnt) hci_prio_recalculate(hdev, ACL_LINK); + + hci_dev_unlock(hdev); } static void hci_sched_acl(struct hci_dev *hdev) @@ -3716,6 +3742,8 @@ static void hci_sched_le(struct hci_dev *hdev) __check_timeout(hdev, *cnt, LE_LINK); + hci_dev_lock(hdev); + tmp = *cnt; while (*cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) { u32 priority = (skb_peek(&chan->data_q))->priority; @@ -3737,13 +3765,15 @@ static void hci_sched_le(struct hci_dev *hdev) chan->conn->sent++; /* Send pending SCO packets right away */ - hci_sched_sco(hdev, SCO_LINK); - hci_sched_sco(hdev, ESCO_LINK); + __hci_sched_sco(hdev, SCO_LINK); + __hci_sched_sco(hdev, ESCO_LINK); } } if (*cnt != tmp) hci_prio_recalculate(hdev, LE_LINK); + + hci_dev_unlock(hdev); } /* Schedule iso */ @@ -3762,6 +3792,8 @@ static void hci_sched_iso(struct hci_dev *hdev, __u8 type) __check_timeout(hdev, *cnt, type); + hci_dev_lock(hdev); + while (*cnt && (conn = hci_low_sent(hdev, type, "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); @@ -3775,6 +3807,8 @@ static void hci_sched_iso(struct hci_dev *hdev, __u8 type) (*cnt)--; } } + + hci_dev_unlock(hdev); } static void hci_tx_work(struct work_struct *work) diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index 0635e4641db4..0d96575868f8 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -364,7 +364,7 @@ static int inquiry_cache_show(struct seq_file *f, void *p) list_for_each_entry(e, &cache->all, all) { struct inquiry_data *data = &e->data; - seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", + seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %lu\n", &data->bdaddr, data->pscan_rep_mode, data->pscan_period_mode, data->pscan_mode, data->dev_class[2], diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 9d9e270492f9..17882feeeeee 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -312,6 +312,10 @@ static void hci_cmd_sync_work(struct work_struct *work) while (1) { struct hci_cmd_sync_work_entry *entry; + /* Leave the backlog to hci_cmd_sync_clear() */ + if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) + break; + mutex_lock(&hdev->cmd_sync_work_lock); entry = list_first_entry_or_null(&hdev->cmd_sync_work_list, struct hci_cmd_sync_work_entry, @@ -658,6 +662,8 @@ void hci_cmd_sync_clear(struct hci_dev *hdev) { struct hci_cmd_sync_work_entry *entry, *tmp; + /* cmd_work is disabled, the pending request can only time out */ + hci_cmd_sync_cancel_sync(hdev, ENODEV); cancel_work_sync(&hdev->cmd_sync_work); cancel_work_sync(&hdev->reenable_adv_work); @@ -5678,6 +5684,7 @@ int hci_stop_discovery_sync(struct hci_dev *hdev) { struct discovery_state *d = &hdev->discovery; struct inquiry_entry *e; + bdaddr_t addr; int err; bt_dev_dbg(hdev, "state %u", hdev->discovery.state); @@ -5713,15 +5720,21 @@ int hci_stop_discovery_sync(struct hci_dev *hdev) return 0; if (d->state == DISCOVERY_RESOLVING || d->state == DISCOVERY_STOPPING) { + hci_dev_lock(hdev); e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING); - if (!e) + if (!e) { + hci_dev_unlock(hdev); return 0; + } + + bacpy(&addr, &e->data.bdaddr); + hci_dev_unlock(hdev); /* Ignore cancel errors since it should interfere with stopping * of the discovery. */ - hci_remote_name_cancel_sync(hdev, &e->data.bdaddr); + hci_remote_name_cancel_sync(hdev, &addr); } return 0; @@ -7151,6 +7164,7 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) bacpy(&cp.bdaddr, &conn->dst); cp.pscan_rep_mode = 0x02; + hci_dev_lock(hdev); ie = hci_inquiry_cache_lookup(hdev, &conn->dst); if (ie) { if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) { @@ -7162,6 +7176,7 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) memcpy(conn->dev_class, ie->data.dev_class, 3); } + hci_dev_unlock(hdev); cp.pkt_type = cpu_to_le16(conn->pkt_type); if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER)) diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index b2c1060394e6..dc3cdf614def 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -462,7 +462,7 @@ static int __rfcomm_release_dev(void __user *arg) /* Shut down TTY synchronously before freeing rfcomm_dev */ tty_port_tty_vhangup(&dev->port); - if (!test_bit(RFCOMM_TTY_OWNED, &dev->status)) + if (!test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status)) tty_port_put(&dev->port); tty_port_put(&dev->port); @@ -724,10 +724,9 @@ static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty) * when the last process closes the tty. The behaviour is expected by * userspace. */ - if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { - set_bit(RFCOMM_TTY_OWNED, &dev->status); + if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) && + !test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status)) tty_port_put(&dev->port); - } return 0; } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 14bb2c82a562..076bb5e82ea0 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -661,6 +661,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, else bdaddr_type = BDADDR_LE_RANDOM; + mutex_lock(&hdev->remote_oob_lock); oob_data = hci_find_remote_oob_data(hdev, &hcon->dst, bdaddr_type); if (oob_data && oob_data->present) { @@ -671,6 +672,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, SMP_DBG("OOB Remote Confirmation: %16phN", smp->pcnf); SMP_DBG("OOB Remote Random: %16phN", smp->rr); } + mutex_unlock(&hdev->remote_oob_lock); } else { authreq &= ~SMP_AUTH_SC; diff --git a/net/core/dev.c b/net/core/dev.c index 55e1f4045d2c..6f75d1d1af32 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3812,20 +3812,36 @@ static netdev_features_t dflt_features_check(struct sk_buff *skb, return vlan_features_check(skb, features); } -static bool skb_gso_has_extension_hdr(const struct sk_buff *skb) -{ - if (!skb->encapsulation) - return ((skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6 || - (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && - vlan_get_protocol(skb) == htons(ETH_P_IPV6))) && - skb_transport_header_was_set(skb) && - skb_network_header_len(skb) != sizeof(struct ipv6hdr)); - else - return (!skb_inner_network_header_was_set(skb) || - ((skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6 || - (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && - inner_ip_hdr(skb)->version == 6)) && - skb_inner_network_header_len(skb) != sizeof(struct ipv6hdr))); +static bool __skb_has_ipv6_ext_hdr(const struct sk_buff *skb, int nhoff) +{ + const struct ipv6hdr *ip6h; + struct ipv6hdr _ip6h; + + ip6h = skb_header_pointer(skb, nhoff, sizeof(_ip6h), &_ip6h); + return ip6h && ip6h->version == 6 && ipv6_ext_hdr(ip6h->nexthdr); +} + +static bool skb_has_ipv6_extension_hdr(const struct sk_buff *skb) +{ + if (vlan_get_protocol(skb) == htons(ETH_P_IPV6)) { + if (__skb_has_ipv6_ext_hdr(skb, skb_network_offset(skb))) + return true; + + /* Catch tunnels without skb->encapsulation (e.g., virtio). */ + if (!skb->encapsulation && + skb_transport_header_was_set(skb) && + skb_network_header_len(skb) != sizeof(struct ipv6hdr)) + return true; + } + + /* Tunnels without an inner network header, such as SCTP-in-UDP or + * PSP, have no inner IP header and thus no inner extension header. + */ + if (skb->encapsulation && skb_inner_network_header_was_set(skb) && + __skb_has_ipv6_ext_hdr(skb, skb_inner_network_offset(skb))) + return true; + + return false; } static netdev_features_t gso_features_check(const struct sk_buff *skb, @@ -3880,7 +3896,7 @@ static netdev_features_t gso_features_check(const struct sk_buff *skb, * so neither does TSO that depends on it. */ if (features & NETIF_F_IPV6_CSUM && - skb_gso_has_extension_hdr(skb)) + skb_has_ipv6_extension_hdr(skb)) features &= ~(NETIF_F_IPV6_CSUM | NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4); return features; @@ -3982,8 +3998,7 @@ int skb_csum_hwoffload_help(struct sk_buff *skb, return 0; if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) { - if (vlan_get_protocol(skb) == htons(ETH_P_IPV6) && - skb_network_header_len(skb) != sizeof(struct ipv6hdr)) + if (skb_has_ipv6_extension_hdr(skb)) goto sw_checksum; switch (skb->csum_offset) { @@ -4398,9 +4413,14 @@ EXPORT_SYMBOL(dev_loopback_xmit); static struct netdev_queue * netdev_tx_queue_mapping(struct net_device *dev, struct sk_buff *skb) { - int qm = skb_get_queue_mapping(skb); + int queue = skb_get_queue_mapping(skb); + int capped; + + capped = netdev_cap_txqueue(dev, queue); + if (unlikely(capped != queue)) + skb_set_queue_mapping(skb, capped); - return netdev_get_tx_queue(dev, netdev_cap_txqueue(dev, qm)); + return netdev_get_tx_queue(dev, capped); } #ifndef CONFIG_PREEMPT_RT @@ -4409,9 +4429,13 @@ static bool netdev_xmit_txqueue_skipped(void) return __this_cpu_read(softnet_data.xmit.skip_txqueue); } -void netdev_xmit_skip_txqueue(bool skip) +bool netdev_xmit_skip_txqueue(bool skip) { + bool prev = netdev_xmit_txqueue_skipped(); + __this_cpu_write(softnet_data.xmit.skip_txqueue, skip); + + return prev; } EXPORT_SYMBOL_GPL(netdev_xmit_skip_txqueue); @@ -4421,9 +4445,13 @@ static bool netdev_xmit_txqueue_skipped(void) return current->net_xmit.skip_txqueue; } -void netdev_xmit_skip_txqueue(bool skip) +bool netdev_xmit_skip_txqueue(bool skip) { + bool prev = netdev_xmit_txqueue_skipped(); + current->net_xmit.skip_txqueue = skip; + + return prev; } EXPORT_SYMBOL_GPL(netdev_xmit_skip_txqueue); #endif @@ -4842,21 +4870,25 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) tcx_set_ingress(skb, false); #ifdef CONFIG_NET_EGRESS if (static_branch_unlikely(&egress_needed_key)) { + bool skip_txq; + if (nf_hook_egress_active()) { skb = nf_hook_egress(skb, &rc, dev); if (!skb) goto out; } - netdev_xmit_skip_txqueue(false); + skip_txq = netdev_xmit_skip_txqueue(false); nf_skip_egress(skb, true); skb = sch_handle_egress(skb, &rc, dev); - if (!skb) + if (!skb) { + netdev_xmit_skip_txqueue(skip_txq); goto out; + } nf_skip_egress(skb, false); - if (netdev_xmit_txqueue_skipped()) + if (netdev_xmit_skip_txqueue(skip_txq)) txq = netdev_tx_queue_mapping(dev, skb); } #endif diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index 164643140a52..f6029f60c1dc 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -267,7 +267,8 @@ int dev_get_hwtstamp_phylib(struct net_device *dev, hwprov->phydev) return phy_hwtstamp_get(hwprov->phydev, cfg); - if (hwprov->source == HWTSTAMP_SOURCE_NETDEV) + if (hwprov->source == HWTSTAMP_SOURCE_NETDEV && + dev->netdev_ops->ndo_hwtstamp_get) return dev->netdev_ops->ndo_hwtstamp_get(dev, cfg); return -EOPNOTSUPP; diff --git a/net/core/gso.c b/net/core/gso.c index bcd156372f4d..e96ef6350064 100644 --- a/net/core/gso.c +++ b/net/core/gso.c @@ -117,6 +117,7 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb, SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb); SKB_GSO_CB(skb)->encap_level = 0; + SKB_GSO_CB(skb)->recursion_counter = 0; skb_reset_mac_header(skb); skb_reset_mac_len(skb); diff --git a/net/core/page_pool.c b/net/core/page_pool.c index 8f8956fb061b..34ce99514d72 100644 --- a/net/core/page_pool.c +++ b/net/core/page_pool.c @@ -951,8 +951,8 @@ static void page_pool_recycle_ring_bulk(struct page_pool *pool, } } - page_pool_producer_unlock(pool, in_softirq); recycle_stat_add(pool, ring, i); + page_pool_producer_unlock(pool, in_softirq); /* Hopefully all pages were returned into ptr_ring */ if (likely(i == bulk_len)) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 39102b366003..88febbc5803e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -7397,7 +7397,8 @@ nodefer: kfree_skb_napi_cache(skb); } static void skb_splice_csum_page(struct sk_buff *skb, struct page *page, - size_t offset, size_t len) + size_t offset, size_t len, + unsigned int csum_offset) { const char *kaddr; __wsum csum; @@ -7405,7 +7406,7 @@ static void skb_splice_csum_page(struct sk_buff *skb, struct page *page, kaddr = kmap_local_page(page); csum = csum_partial(kaddr + offset, len, 0); kunmap_local(kaddr); - skb->csum = csum_block_add(skb->csum, csum, skb->len); + skb->csum = csum_block_add(skb->csum, csum, csum_offset); } /** @@ -7465,7 +7466,8 @@ ssize_t skb_splice_from_iter(struct sk_buff *skb, struct iov_iter *iter, } if (skb->ip_summed == CHECKSUM_NONE) - skb_splice_csum_page(skb, page, off, part); + skb_splice_csum_page(skb, page, off, part, + skb->len + spliced); off = 0; spliced += part; diff --git a/net/core/sock.c b/net/core/sock.c index 4395509ab9dc..e4503ea6fd0c 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1034,7 +1034,7 @@ static int sock_reserve_memory(struct sock *sk, int bytes) bool charged; int pages; - if (!mem_cgroup_sk_enabled(sk) || !sk_has_account(sk)) + if (!mem_cgroup_sk_enabled(sk) || !sk_is_tcp(sk)) return -EOPNOTSUPP; if (!bytes) @@ -1661,7 +1661,7 @@ int sk_setsockopt(struct sock *sk, int level, int optname, { int delta; - if (val < 0) { + if (val < 0 || val > SZ_1G) { ret = -EINVAL; break; } diff --git a/net/ethtool/tsconfig.c b/net/ethtool/tsconfig.c index 24b64862011f..a2848ef0ddc4 100644 --- a/net/ethtool/tsconfig.c +++ b/net/ethtool/tsconfig.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include #include @@ -42,7 +43,9 @@ static int tsconfig_prepare_data(const struct ethnl_req_info *req_base, struct kernel_hwtstamp_config cfg = {}; int ret; - if (!dev->netdev_ops->ndo_hwtstamp_get) + if (!dev->netdev_ops->ndo_hwtstamp_get && + !phy_is_default_hwtstamp(dev->phydev) && + !netdev_ops_lock_dereference(dev->hwprov, dev)) return -EOPNOTSUPP; ret = ethnl_ops_begin(dev); @@ -248,17 +251,6 @@ static int tsconfig_send_reply(struct net_device *dev, struct genl_info *info) return ret; } -static int ethnl_set_tsconfig_validate(struct ethnl_req_info *req_base, - struct genl_info *info) -{ - const struct net_device_ops *ops = req_base->dev->netdev_ops; - - if (!ops->ndo_hwtstamp_set || !ops->ndo_hwtstamp_get) - return -EOPNOTSUPP; - - return 1; -} - static struct hwtstamp_provider * tsconfig_set_hwprov_from_desc(struct net_device *dev, struct genl_info *info, @@ -272,7 +264,7 @@ tsconfig_set_hwprov_from_desc(struct net_device *dev, int ret; ret = ethtool_net_get_ts_info_by_phc(dev, &ts_info, hwprov_desc); - if (!ret) { + if (!ret && dev->netdev_ops->ndo_hwtstamp_set) { /* Found */ source = HWTSTAMP_SOURCE_NETDEV; } else { @@ -313,6 +305,11 @@ static int ethnl_set_tsconfig(struct ethnl_req_info *req_base, if (!netif_device_present(dev)) return -ENODEV; + if (!dev->netdev_ops->ndo_hwtstamp_set && + !phy_is_default_hwtstamp(dev->phydev) && + !netdev_ops_lock_dereference(dev->hwprov, dev)) + return -EOPNOTSUPP; + if (tb[ETHTOOL_A_TSCONFIG_HWTSTAMP_PROVIDER]) { struct hwtstamp_provider_desc __hwprov_desc = {.index = -1}; struct hwtstamp_provider *__hwprov; @@ -455,6 +452,5 @@ const struct ethnl_request_ops ethnl_tsconfig_request_ops = { .reply_size = tsconfig_reply_size, .fill_reply = tsconfig_fill_reply, - .set_validate = ethnl_set_tsconfig_validate, .set = ethnl_set_tsconfig, }; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 32d006c1a8ee..14ce01092fda 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -161,7 +161,7 @@ void inet_sock_destruct(struct sock *sk) WARN_ON_ONCE(sk->sk_wmem_queued); WARN_ON_ONCE(sk->sk_forward_alloc); - kfree(rcu_dereference_protected(inet->inet_opt, 1)); + kfree_rcu(rcu_dereference_protected(inet->inet_opt, 1), rcu); dst_release(rcu_dereference_protected(sk->sk_dst_cache, 1)); dst_release(rcu_dereference_protected(sk->sk_rx_dst, 1)); psp_sk_assoc_free(sk); @@ -1375,6 +1375,9 @@ struct sk_buff *inet_gso_segment(struct sk_buff *skb, int id; skb_reset_network_header(skb); + if (unlikely(gso_recursion_inc_test(skb, + IP_TUNNEL_RECURSION_LIMIT))) + goto out; nhoff = skb_network_header(skb) - skb_mac_header(skb); if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) goto out; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 92bc60716f33..99baf14afdfd 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4465,6 +4465,12 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) return 1; old_ack: + /* An old ACK can carry new data. Update TS.Recent before SACK + * processing can trigger a retransmission. + */ + if (flag & FLAG_UPDATE_TS_RECENT) + tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq); + /* If data was SACKed, tag it and see if we should send more data. * If data was DSACKed, see if we can undo a cwnd reduction. */ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 497b1a0370cd..f542d88ccfe3 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1209,7 +1209,7 @@ static int tcp_v4_send_synack(const struct sock *sk, struct dst_entry *dst, */ static void tcp_v4_reqsk_destructor(struct request_sock *req) { - kfree(rcu_dereference_protected(inet_rsk(req)->ireq_opt, 1)); + kfree_rcu(rcu_dereference_protected(inet_rsk(req)->ireq_opt, 1), rcu); } #ifdef CONFIG_TCP_MD5SIG diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index cbe681de3818..e482389376dc 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -4873,7 +4873,7 @@ static int modify_prefix_route(struct net *net, struct inet6_ifaddr *ifp, fib6_clean_expires(f6i); fib6_may_remove_gc_list(net, f6i); } else { - fib6_set_expires(f6i, expires); + fib6_set_expires(f6i, jiffies + clock_t_to_jiffies(expires)); fib6_add_gc_list(f6i); } diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 78f50c93c536..700f1861d8eb 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "ip6_offload.h" #include "tcpv6_offload.c" @@ -104,6 +105,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, bool gso_partial; skb_reset_network_header(skb); + if (unlikely(gso_recursion_inc_test(skb, + IP_TUNNEL_RECURSION_LIMIT))) + goto out; nhoff = skb_network_header(skb) - skb_mac_header(skb); if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) goto out; diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 4c45c0a77d75..73422a273080 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -127,7 +127,7 @@ static __be32 seg6_make_flowlabel(struct net *net, struct sk_buff *skb, u32 hash; if (do_flowlabel > 0) { - hash = skb_get_hash(skb); + hash = skb_get_hash_net(net, skb); hash = rol32(hash, 16); flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK; } else if (!do_flowlabel && skb->protocol == htons(ETH_P_IPV6)) { diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index d1070aec7b72..53f7b2452575 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -278,13 +278,43 @@ static bool decap_and_validate(struct sk_buff *skb, int proto) return true; } -static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr) +/* advance the SRH to the next segment and set the IPv6 DA accordingly. + * skb pointers may change: after this call, the caller must evaluate again + * any pointer into the packet data. + * + * This function returns: + * - the SRH on success; + * - NULL when the skb cannot be made writable. In this case, the function + * sets the drop reason to SKB_DROP_REASON_NOMEM. + */ +static struct ipv6_sr_hdr *advance_nextseg(struct sk_buff *skb, + struct ipv6_sr_hdr *srh, + enum skb_drop_reason *reason) { struct in6_addr *addr; + int srhoff; + int wlen; + + srhoff = (unsigned char *)srh - skb->data; + /* we write only the Segment Left field and the IPv6 DA. The segment + * list is only read, and seg6_get_srh() already pulled it into the + * linear area. + */ + wlen = srhoff + sizeof(*srh); + + if (unlikely(skb_ensure_writable(skb, wlen))) { + *reason = SKB_DROP_REASON_NOMEM; + return NULL; + } + + /* skb_ensure_writable() may change skb pointers; evaluate srh again */ + srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); srh->segments_left--; addr = srh->segments + srh->segments_left; - *daddr = *addr; + ipv6_hdr(skb)->daddr = *addr; + + return srh; } static int @@ -382,12 +412,26 @@ static bool seg6_next_csid_is_arg_zero(const struct in6_addr *addr, return true; } -/* assume that DA.Argument length > 0 */ -static void seg6_next_csid_advance_arg(struct in6_addr *addr, - const struct seg6_flavors_info *finfo) +/* assume that DA.Argument length > 0. + * skb pointers may change: after this call, the caller must evaluate again + * any pointer into the packet data. + * + * This function returns: + * - SKB_NOT_DROPPED_YET on success; + * - SKB_DROP_REASON_NOMEM when the skb cannot be made writable. + */ +static enum skb_drop_reason +seg6_next_csid_advance_arg(struct sk_buff *skb, + const struct seg6_flavors_info *finfo) { __u8 fnc_octects = seg6_flv_lcnode_func_octects(finfo); __u8 blk_octects = seg6_flv_lcblock_octects(finfo); + struct in6_addr *addr; + + if (unlikely(skb_ensure_writable(skb, sizeof(struct ipv6hdr)))) + return SKB_DROP_REASON_NOMEM; + + addr = &ipv6_hdr(skb)->daddr; /* advance DA.Argument */ memmove(&addr->s6_addr[blk_octects], @@ -395,6 +439,8 @@ static void seg6_next_csid_advance_arg(struct in6_addr *addr, 16 - blk_octects - fnc_octects); memset(&addr->s6_addr[16 - fnc_octects], 0x00, fnc_octects); + + return SKB_NOT_DROPPED_YET; } static int input_action_end_finish(struct sk_buff *skb, @@ -408,31 +454,42 @@ static int input_action_end_finish(struct sk_buff *skb, static int input_action_end_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) { + enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED; struct ipv6_sr_hdr *srh; + int err = -EINVAL; srh = get_and_validate_srh(skb); if (!srh) goto drop; - advance_nextseg(srh, &ipv6_hdr(skb)->daddr); + srh = advance_nextseg(skb, srh, &reason); + if (!srh) { + err = -ENOMEM; + goto drop; + } return input_action_end_finish(skb, slwt); drop: - kfree_skb(skb); - return -EINVAL; + kfree_skb_reason(skb, reason); + return err; } static int end_next_csid_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) { const struct seg6_flavors_info *finfo = &slwt->flv_info; struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; + enum skb_drop_reason reason; if (seg6_next_csid_is_arg_zero(daddr, finfo)) return input_action_end_core(skb, slwt); /* update DA */ - seg6_next_csid_advance_arg(daddr, finfo); + reason = seg6_next_csid_advance_arg(skb, finfo); + if (reason) { + kfree_skb_reason(skb, reason); + return -ENOMEM; + } return input_action_end_finish(skb, slwt); } @@ -448,19 +505,25 @@ static int input_action_end_x_finish(struct sk_buff *skb, static int input_action_end_x_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) { + enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED; struct ipv6_sr_hdr *srh; + int err = -EINVAL; srh = get_and_validate_srh(skb); if (!srh) goto drop; - advance_nextseg(srh, &ipv6_hdr(skb)->daddr); + srh = advance_nextseg(skb, srh, &reason); + if (!srh) { + err = -ENOMEM; + goto drop; + } return input_action_end_x_finish(skb, slwt); drop: - kfree_skb(skb); - return -EINVAL; + kfree_skb_reason(skb, reason); + return err; } static int end_x_next_csid_core(struct sk_buff *skb, @@ -468,12 +531,17 @@ static int end_x_next_csid_core(struct sk_buff *skb, { const struct seg6_flavors_info *finfo = &slwt->flv_info; struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; + enum skb_drop_reason reason; if (seg6_next_csid_is_arg_zero(daddr, finfo)) return input_action_end_x_core(skb, slwt); /* update DA */ - seg6_next_csid_advance_arg(daddr, finfo); + reason = seg6_next_csid_advance_arg(skb, finfo); + if (reason) { + kfree_skb_reason(skb, reason); + return -ENOMEM; + } return input_action_end_x_finish(skb, slwt); } @@ -760,10 +828,12 @@ static bool seg6_pop_srh(struct sk_buff *skb, int srhoff) */ static int end_flv8986_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) { + enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED; const struct seg6_flavors_info *finfo = &slwt->flv_info; enum seg6_local_flv_action action; enum seg6_local_pktinfo pinfo; struct ipv6_sr_hdr *srh; + int err = -EINVAL; __u32 flvmask; int srhoff; @@ -787,10 +857,18 @@ static int end_flv8986_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) switch (action) { case SEG6_LOCAL_FLV_ACT_END: /* process the packet as the "standard" End behavior */ - advance_nextseg(srh, &ipv6_hdr(skb)->daddr); + srh = advance_nextseg(skb, srh, &reason); + if (!srh) { + err = -ENOMEM; + goto drop; + } break; case SEG6_LOCAL_FLV_ACT_PSP: - advance_nextseg(srh, &ipv6_hdr(skb)->daddr); + srh = advance_nextseg(skb, srh, &reason); + if (!srh) { + err = -ENOMEM; + goto drop; + } if (unlikely(!seg6_pop_srh(skb, srhoff))) goto drop; @@ -807,8 +885,8 @@ static int end_flv8986_core(struct sk_buff *skb, struct seg6_local_lwt *slwt) return input_action_end_finish(skb, slwt); drop: - kfree_skb(skb); - return -EINVAL; + kfree_skb_reason(skb, reason); + return err; } /* regular endpoint function */ @@ -847,21 +925,27 @@ static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) { + enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED; struct ipv6_sr_hdr *srh; + int err = -EINVAL; srh = get_and_validate_srh(skb); if (!srh) goto drop; - advance_nextseg(srh, &ipv6_hdr(skb)->daddr); + srh = advance_nextseg(skb, srh, &reason); + if (!srh) { + err = -ENOMEM; + goto drop; + } seg6_lookup_nexthop(skb, NULL, slwt->table); return dst_input(skb); drop: - kfree_skb(skb); - return -EINVAL; + kfree_skb_reason(skb, reason); + return err; } /* decapsulate and forward inner L2 frame on specified interface */ @@ -1375,6 +1459,7 @@ static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) static int input_action_end_b6_encap(struct sk_buff *skb, struct seg6_local_lwt *slwt) { + enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED; struct ipv6_sr_hdr *srh; int err = -EINVAL; @@ -1382,7 +1467,11 @@ static int input_action_end_b6_encap(struct sk_buff *skb, if (!srh) goto drop; - advance_nextseg(srh, &ipv6_hdr(skb)->daddr); + srh = advance_nextseg(skb, srh, &reason); + if (!srh) { + err = -ENOMEM; + goto drop; + } skb_reset_inner_headers(skb); skb->encapsulation = 1; @@ -1398,7 +1487,7 @@ static int input_action_end_b6_encap(struct sk_buff *skb, return dst_input(skb); drop: - kfree_skb(skb); + kfree_skb_reason(skb, reason); return err; } @@ -1434,6 +1523,7 @@ static int input_action_end_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt) { struct seg6_bpf_srh_state *srh_state; + enum skb_drop_reason reason; struct ipv6_sr_hdr *srh; int ret; @@ -1442,7 +1532,11 @@ static int input_action_end_bpf(struct sk_buff *skb, kfree_skb(skb); return -EINVAL; } - advance_nextseg(srh, &ipv6_hdr(skb)->daddr); + srh = advance_nextseg(skb, srh, &reason); + if (!srh) { + kfree_skb_reason(skb, reason); + return -ENOMEM; + } /* The access to the per-CPU buffer srh_state is protected by running * always in softirq context (with disabled BH). On PREEMPT_RT the diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 9629e00069a1..01d6703767c6 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -102,7 +102,7 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, if (!tid_rx) return; - timer_delete_sync(&tid_rx->session_timer); + timer_shutdown_sync(&tid_rx->session_timer); /* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */ spin_lock_bh(&tid_rx->reorder_lock); diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 5152b84a3357..f6d3966e53bc 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -231,6 +231,13 @@ int ieee80211_chanctx_refcount(struct ieee80211_local *local, return num; } +static bool +ieee80211_chanctx_has_replace_partner(struct ieee80211_chanctx *ctx) +{ + return ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED && + ctx->replace_ctx; +} + static int ieee80211_num_chanctx(struct ieee80211_local *local, int radio_idx) { struct ieee80211_chanctx *ctx; @@ -1403,6 +1410,7 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link) { struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_chanctx *ctx = link->reserved_chanctx; + struct ieee80211_chanctx *old_ctx = NULL; lockdep_assert_wiphy(sdata->local->hw.wiphy); @@ -1416,6 +1424,7 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link) if (WARN_ON(!ctx->replace_ctx)) return; + old_ctx = ctx->replace_ctx; WARN_ON(ctx->replace_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED); WARN_ON(ctx->replace_ctx->replace_ctx != ctx); @@ -1426,6 +1435,11 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link) list_del_rcu(&ctx->list); kfree_rcu(ctx, rcu_head); + + if (ieee80211_chanctx_refcount(sdata->local, + old_ctx) == 0) + ieee80211_free_chanctx(sdata->local, old_ctx, + false); } else { ieee80211_free_chanctx(sdata->local, ctx, false); } @@ -1705,7 +1719,8 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link) ieee80211_check_fast_xmit_iface(sdata); - if (ieee80211_chanctx_refcount(local, old_ctx) == 0) + if (ieee80211_chanctx_refcount(local, old_ctx) == 0 && + !ieee80211_chanctx_has_replace_partner(old_ctx)) ieee80211_free_chanctx(local, old_ctx, false); ieee80211_recalc_chanctx_min_def(local, new_ctx); @@ -1770,7 +1785,8 @@ ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link) } static bool -ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link) +ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link, + struct ieee80211_chanctx *ctx) { struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_chanctx *old_ctx, *new_ctx; @@ -1780,6 +1796,9 @@ ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link) new_ctx = link->reserved_chanctx; old_ctx = ieee80211_link_get_chanctx(link); + if (new_ctx != ctx) + return false; + if (!old_ctx) return false; @@ -1792,6 +1811,12 @@ ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link) if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) return false; + if (new_ctx->replace_ctx != old_ctx) + return false; + + if (old_ctx->replace_ctx != new_ctx) + return false; + return true; } @@ -1821,7 +1846,8 @@ static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, } for_each_chanctx_user_reserved(local, ctx, &iter) { - if (!ieee80211_link_has_in_place_reservation(iter.link)) + if (!ieee80211_link_has_in_place_reservation(iter.link, + ctx)) continue; old_ctx = ieee80211_link_get_chanctx(iter.link); @@ -1923,7 +1949,9 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) for_each_chanctx_user_assigned(local, ctx->replace_ctx, &iter) { n_assigned++; - if (iter.link && iter.link->reserved_chanctx) { + if (iter.link && + ieee80211_link_has_in_place_reservation(iter.link, + ctx)) { n_reserved++; if (iter.link->reserved_ready) n_ready++; @@ -1948,7 +1976,8 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) use_reserved: ctx->conf.radar_enabled = false; for_each_chanctx_user_reserved(local, ctx, &iter) { - if (ieee80211_link_has_in_place_reservation(iter.link) && + if (ieee80211_link_has_in_place_reservation(iter.link, + ctx) && !iter.link->reserved_ready) return -EAGAIN; @@ -1989,7 +2018,8 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) } for_each_chanctx_user_reserved(local, ctx, &iter) { - if (!ieee80211_link_has_in_place_reservation(iter.link)) + if (!ieee80211_link_has_in_place_reservation(iter.link, + ctx)) continue; ieee80211_chan_bw_change(local, @@ -2038,7 +2068,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) struct ieee80211_bss_conf *link_conf = link->conf; u64 changed = 0; - if (!ieee80211_link_has_in_place_reservation(link)) + if (!ieee80211_link_has_in_place_reservation(link, ctx)) continue; rcu_assign_pointer(link_conf->chanctx_conf, @@ -2089,7 +2119,8 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) for_each_chanctx_user_reserved(local, ctx, &iter) { struct ieee80211_link_data *link = iter.link; - if (WARN_ON(ieee80211_link_has_in_place_reservation(link))) + if (WARN_ON(ieee80211_link_has_in_place_reservation(link, + ctx))) continue; if (!link->reserved_ready) @@ -2174,7 +2205,8 @@ void __ieee80211_link_release_channel(struct ieee80211_link_data *link, } ieee80211_assign_link_chanctx(link, NULL, false); - if (ieee80211_chanctx_refcount(local, ctx) == 0) + if (ieee80211_chanctx_refcount(local, ctx) == 0 && + !ieee80211_chanctx_has_replace_partner(ctx)) ieee80211_free_chanctx(local, ctx, skip_idle_recalc); link->radar_required = false; diff --git a/net/mac80211/drop.h b/net/mac80211/drop.h index f06a8aa905c5..f433ee17cce5 100644 --- a/net/mac80211/drop.h +++ b/net/mac80211/drop.h @@ -108,6 +108,7 @@ typedef unsigned int __bitwise ieee80211_rx_result; R(RX_DROP_U_UNHANDLED_PREQ) \ R(RX_DROP_U_UNHANDLED_MGMT_STYPE) \ R(RX_DROP_U_NO_LINK) \ + R(RX_DROP_U_DEFRAG_OVERFLOW) \ /* this line for the trailing \ - add before this */ /* having two enums allows for checking ieee80211_rx_result use with sparse */ diff --git a/net/mac80211/fils_aead.c b/net/mac80211/fils_aead.c index d2f4a17eab99..2bc1b9c54d73 100644 --- a/net/mac80211/fils_aead.c +++ b/net/mac80211/fils_aead.c @@ -90,6 +90,14 @@ static int aes_siv_encrypt(const u8 *key, size_t key_len, if (res) return res; + /* With an empty plaintext there is no ciphertext to produce and + * the S2V result is the complete output. + */ + if (!plain_len) { + memcpy(out, v, AES_BLOCK_SIZE); + return 0; + } + /* Use a temporary buffer of the plaintext to handle need for * overwriting this during AES-CTR. */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0564370419fb..011ec7fbb711 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2478,7 +2478,7 @@ static inline bool ieee80211_require_encrypted_assoc(__le16 fc, /* sta_out needs to be checked for ERR_PTR() before using */ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct sta_info **sta_out); + struct sta_info **sta_out, bool bss); static inline void ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 889c32fd8de1..88942317fec8 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -998,8 +998,8 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev, len_rthdr = ieee80211_get_radiotap_len(skb->data); hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr); - if (skb->len < len_rthdr + 2 || - skb->len < len_rthdr + ieee80211_hdrlen(hdr->frame_control)) + if (skb_headlen(skb) < len_rthdr + 2 || + skb_headlen(skb) < len_rthdr + ieee80211_hdrlen(hdr->frame_control)) return 0; /* doesn't matter, frame will be dropped */ return ieee80211_select_queue_80211(sdata, skb, hdr); diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 03171cf00855..07a21dfa8674 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -577,6 +577,12 @@ void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata, goto unlock_sta; spin_lock(&cache->walk_lock); + if ((READ_ONCE(mpath->flags) & MESH_PATH_DELETED) || + (mppath && (READ_ONCE(mppath->flags) & MESH_PATH_DELETED))) { + kfree(entry); + goto unlock_cache; + } + prev = rhashtable_lookup_get_insert_fast(&cache->rht, &entry->rhash, fast_tx_rht_params); @@ -694,8 +700,10 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata, return ERR_PTR(-ENOSPC); new_mpath = mesh_path_new(sdata, dst, GFP_ATOMIC); - if (!new_mpath) + if (!new_mpath) { + atomic_dec(&sdata->u.mesh.mpaths); return ERR_PTR(-ENOMEM); + } tbl = &sdata->u.mesh.mesh_paths; spin_lock_bh(&tbl->walk_lock); @@ -708,6 +716,7 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata, if (mpath) { kfree(new_mpath); + atomic_dec(&sdata->u.mesh.mpaths); if (IS_ERR(mpath)) return mpath; @@ -733,10 +742,15 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata, if (is_multicast_ether_addr(dst)) return -EOPNOTSUPP; + if (!atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS)) + return -ENOSPC; + new_mpath = mesh_path_new(sdata, dst, GFP_ATOMIC); - if (!new_mpath) + if (!new_mpath) { + atomic_dec(&sdata->u.mesh.mpaths); return -ENOMEM; + } memcpy(new_mpath->mpp, mpp, ETH_ALEN); tbl = &sdata->u.mesh.mpp_paths; @@ -749,10 +763,12 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata, hlist_add_head_rcu(&new_mpath->walk_list, &tbl->walk_head); spin_unlock_bh(&tbl->walk_lock); - if (ret) + if (ret) { kfree(new_mpath); - else + atomic_dec(&sdata->u.mesh.mpaths); + } else { mesh_fast_tx_flush_addr(sdata, dst); + } sdata->u.mesh.mpp_paths_generation++; return ret; @@ -798,7 +814,8 @@ static void mesh_path_free_rcu(struct mesh_table *tbl, struct ieee80211_sub_if_data *sdata = mpath->sdata; spin_lock_bh(&mpath->state_lock); - mpath->flags |= MESH_PATH_RESOLVING | MESH_PATH_DELETED; + WRITE_ONCE(mpath->flags, + mpath->flags | MESH_PATH_RESOLVING | MESH_PATH_DELETED); mesh_gate_del(tbl, mpath); spin_unlock_bh(&mpath->state_lock); timer_shutdown_sync(&mpath->timer); @@ -812,6 +829,9 @@ static void __mesh_path_del(struct mesh_table *tbl, struct mesh_path *mpath) { hlist_del_rcu(&mpath->walk_list); rhashtable_remove_fast(&tbl->rhead, &mpath->rhash, mesh_rht_params); + spin_lock_bh(&mpath->state_lock); + WRITE_ONCE(mpath->flags, mpath->flags | MESH_PATH_DELETED); + spin_unlock_bh(&mpath->state_lock); if (tbl == &mpath->sdata->u.mesh.mpp_paths) mesh_fast_tx_flush_addr(mpath->sdata, mpath->dst); else diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1b9d272e7f8e..598056776ff9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -5871,6 +5871,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ; const struct cfg80211_bss_ies *bss_ies = NULL; + struct ieee802_11_elems *bss_elems = NULL; struct ieee80211_supported_band *sband; struct ieee802_11_elems *elems; const __le16 prof_bss_param_ch_present = @@ -5954,7 +5955,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, (is_5ghz && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_VHT && (!elems->vht_cap_elem || !elems->vht_operation)))) { const struct cfg80211_bss_ies *ies; - struct ieee802_11_elems *bss_elems; rcu_read_lock(); ies = rcu_dereference(cbss->ies); @@ -6016,7 +6016,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, "AP bug: VHT operation missing from AssocResp\n"); } } - kfree(bss_elems); } /* @@ -6289,6 +6288,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, ret = true; out: kfree(elems); + kfree(bss_elems); kfree(bss_ies); return ret; } diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index b73ef3adfcc5..45e7a7ea7690 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -1193,6 +1194,54 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) mi->sample_time = jiffies; } +/* + * Check whether an HT/VHT rate from a TX status entry maps to an entry + * in the minstrel_ht rate tables. Values that cannot be represented + * there must not be used for indexing mi->groups[] and the MCS groups. + */ +static bool +minstrel_ht_txstat_rate_valid(struct ieee80211_tx_rate *rate) +{ + unsigned int bw; + + if (!(rate->flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))) + return true; + + if (rate->flags & IEEE80211_TX_RC_MCS) { + /* minstrel_ht supports up to MINSTREL_MAX_STREAMS streams */ + return rate->idx < MINSTREL_MAX_STREAMS * 8; + } + + /* minstrel_ht has no VHT groups for 160 MHz and wider */ + bw = !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + + 2 * !!(rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH); + if ((rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) || bw > BW_80) + return false; + + return ieee80211_rate_get_vht_nss(rate) <= MINSTREL_MAX_STREAMS && + ieee80211_rate_get_vht_mcs(rate) < MCS_GROUP_RATES; +} + +static bool +minstrel_ht_ri_txstat_rate_valid(struct rate_info *rate) +{ + if (!(rate->flags & (RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_VHT_MCS))) + return true; + + if (rate->flags & RATE_INFO_FLAGS_MCS) { + /* minstrel_ht supports up to MINSTREL_MAX_STREAMS streams */ + return rate->mcs < MINSTREL_MAX_STREAMS * 8; + } + + /* minstrel_ht has VHT groups only for 20/40/80 MHz */ + if (rate->bw != RATE_INFO_BW_20 && rate->bw != RATE_INFO_BW_40 && + rate->bw != RATE_INFO_BW_80) + return false; + + return rate->nss <= MINSTREL_MAX_STREAMS && + rate->mcs < MCS_GROUP_RATES; +} + static bool minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, struct ieee80211_tx_rate *rate) @@ -1205,9 +1254,8 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, if (!rate->count) return false; - if (rate->flags & IEEE80211_TX_RC_MCS || - rate->flags & IEEE80211_TX_RC_VHT_MCS) - return true; + if (rate->flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS)) + return minstrel_ht_txstat_rate_valid(rate); for (i = 0; i < ARRAY_SIZE(mp->cck_rates); i++) if (rate->idx == mp->cck_rates[i]) @@ -1235,9 +1283,9 @@ minstrel_ht_ri_txstat_valid(struct minstrel_priv *mp, if (!rate_status->try_count) return false; - if (rate_status->rate_idx.flags & RATE_INFO_FLAGS_MCS || - rate_status->rate_idx.flags & RATE_INFO_FLAGS_VHT_MCS) - return true; + if (rate_status->rate_idx.flags & + (RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_VHT_MCS)) + return minstrel_ht_ri_txstat_rate_valid(&rate_status->rate_idx); for (i = 0; i < ARRAY_SIZE(mp->cck_rates); i++) { if (rate_status->rate_idx.legacy == @@ -1947,14 +1995,39 @@ minstrel_ht_alloc(struct ieee80211_hw *hw) } #ifdef CONFIG_MAC80211_DEBUGFS +static int minstrel_ht_fixed_rate_idx_get(void *data, u64 *val) +{ + *val = *(u32 *)data; + return 0; +} + +static int minstrel_ht_fixed_rate_idx_set(void *data, u64 val) +{ + u32 idx = val; + + /* U32_MAX is the default and keeps fixed rate processing disabled */ + if (val != U32_MAX && + (val > U16_MAX || + MI_RATE_GROUP(idx) >= ARRAY_SIZE(minstrel_mcs_groups) || + MI_RATE_IDX(idx) >= MCS_GROUP_RATES)) + return -EINVAL; + + *(u32 *)data = idx; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(minstrel_ht_fixed_rate_idx_fops, + minstrel_ht_fixed_rate_idx_get, + minstrel_ht_fixed_rate_idx_set, "%llu\n"); + static void minstrel_ht_add_debugfs(struct ieee80211_hw *hw, void *priv, struct dentry *debugfsdir) { struct minstrel_priv *mp = priv; mp->fixed_rate_idx = (u32) -1; - debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir, - &mp->fixed_rate_idx); + debugfs_create_file("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir, + &mp->fixed_rate_idx, &minstrel_ht_fixed_rate_idx_fops); } #endif diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 5e26be8e27d8..7ead509304eb 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2499,6 +2499,12 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) } skb_pull(rx->skb, ieee80211_hdrlen(fc)); + if (unlikely((u32)entry->extra_len + rx->skb->len > U16_MAX)) { + I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); + __skb_queue_purge(&entry->skb_list); + return RX_DROP_U_DEFRAG_OVERFLOW; + } + __skb_queue_tail(&entry->skb_list, rx->skb); entry->last_frag = frag; entry->extra_len += rx->skb->len; diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 880f4625775d..f5e4970869b6 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -111,8 +111,8 @@ validate_chandef_by_ht_vht_oper(struct ieee80211_sub_if_data *sdata, switch (chan_width) { case NL80211_CHAN_WIDTH_320: - WARN_ON(1); - break; + chandef->chan = NULL; + return; case NL80211_CHAN_WIDTH_160: vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; vht_oper.center_freq_seg1_idx = vht_oper.center_freq_seg0_idx; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 22eba0e6e54c..01d70f71c43e 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -137,6 +137,8 @@ static void __cleanup_single_sta(struct sta_info *sta) struct ieee80211_local *local = sdata->local; struct ps_data *ps; + cancel_work_sync(&sta->drv_deliver_wk); + if (test_sta_flag(sta, WLAN_STA_PS_STA) || test_sta_flag(sta, WLAN_STA_PS_DRIVER) || test_sta_flag(sta, WLAN_STA_PS_DELIVER)) { @@ -166,8 +168,6 @@ static void __cleanup_single_sta(struct sta_info *sta) if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_sta_cleanup(sta); - cancel_work_sync(&sta->drv_deliver_wk); - /* * Destroy aggregation state here. It would be nice to wait for the * driver to finish aggregation stop and then clean up, but for now @@ -1582,6 +1582,8 @@ static void __sta_info_destroy_part2(struct sta_info *sta, bool recalc) sta->dead = true; + cancel_work_sync(&sta->drv_deliver_wk); + local->num_sta--; local->sta_generation++; diff --git a/net/mac80211/status.c b/net/mac80211/status.c index dd1dbba06838..e10e5bcc279b 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -1161,6 +1161,73 @@ void ieee80211_tx_status_skb(struct ieee80211_hw *hw, struct sk_buff *skb) } EXPORT_SYMBOL(ieee80211_tx_status_skb); +/* + * Check whether the HT/VHT rate information in a TX status entry is + * valid for its encoding. This is not specific to any rate control + * algorithm; all status consumers rely on the values being sane. + */ +static bool ieee80211_tx_status_rate_info_valid(const struct rate_info *rate) +{ + if (rate->flags & RATE_INFO_FLAGS_MCS) + return rate->mcs < 32; + + if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) + return rate->nss >= 1 && rate->nss <= 8 && rate->mcs <= 11; + + return true; +} + +static bool +ieee80211_tx_status_tx_rate_valid(const struct ieee80211_tx_rate *rate) +{ + if (rate->flags & IEEE80211_TX_RC_MCS) + return rate->idx < 32; + + if (rate->flags & IEEE80211_TX_RC_VHT_MCS) + return ieee80211_rate_get_vht_mcs(rate) <= 11; + + return true; +} + +/* + * Drop TX status rate entries that don't describe a valid rate. The + * status information is used by rate control and by other mac80211 + * code, and a malformed entry must not be able to corrupt state beyond + * the driver that reported it. + */ +static void +ieee80211_tx_status_drop_invalid_rates(struct ieee80211_tx_status *status) +{ + int i; + + for (i = 0; i < status->n_rates; i++) { + struct ieee80211_rate_status *rs = &status->rates[i]; + + if (ieee80211_tx_status_rate_info_valid(&rs->rate_idx)) + continue; + + rs->try_count = 0; + memset(&rs->rate_idx, 0, sizeof(rs->rate_idx)); + } + + if (!status->info) + return; + + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + struct ieee80211_tx_rate *rate; + + rate = &status->info->status.rates[i]; + if (rate->idx < 0) + break; + + if (ieee80211_tx_status_tx_rate_valid(rate)) + continue; + + rate->idx = -1; + rate->count = 0; + } +} + void ieee80211_tx_status_ext(struct ieee80211_hw *hw, struct ieee80211_tx_status *status) { @@ -1173,6 +1240,8 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, bool acked, noack_success, ack_signal_valid; u16 tx_time_est; + ieee80211_tx_status_drop_invalid_rates(status); + if (pubsta) { sta = container_of(pubsta, struct sta_info, sta); @@ -1296,6 +1365,7 @@ void ieee80211_tx_rate_update(struct ieee80211_hw *hw, .sta = pubsta, }; + ieee80211_tx_status_drop_invalid_rates(&status); rate_control_tx_status(local, &status); if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e26412120873..65c5a6519b89 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2082,7 +2082,7 @@ static bool ieee80211_validate_radiotap_len(struct sk_buff *skb) (struct ieee80211_radiotap_header *)skb->data; /* check for not even having the fixed radiotap header part */ - if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) + if (unlikely(skb_headlen(skb) < sizeof(struct ieee80211_radiotap_header))) return false; /* too short to be possibly valid */ /* is it a header version we can trust to find length from? */ @@ -2090,7 +2090,7 @@ static bool ieee80211_validate_radiotap_len(struct sk_buff *skb) return false; /* only version 0 is supported */ /* does the skb contain enough to deliver on the alleged length? */ - if (unlikely(skb->len < ieee80211_get_radiotap_len(skb->data))) + if (unlikely(skb_headlen(skb) < ieee80211_get_radiotap_len(skb->data))) return false; /* skb too short for claimed rt header extent */ return true; @@ -2379,13 +2379,13 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, skb_set_network_header(skb, len_rthdr); skb_set_transport_header(skb, len_rthdr); - if (skb->len < len_rthdr + 2) + if (skb_headlen(skb) < len_rthdr + 2) goto fail; hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr); hdrlen = ieee80211_hdrlen(hdr->frame_control); - if (skb->len < len_rthdr + hdrlen) + if (skb_headlen(skb) < len_rthdr + hdrlen) goto fail; /* @@ -2393,7 +2393,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, * carrying a rfc1042 header */ if (ieee80211_is_data(hdr->frame_control) && - skb->len >= len_rthdr + hdrlen + sizeof(rfc1042_header) + 2) { + skb_headlen(skb) >= len_rthdr + hdrlen + sizeof(rfc1042_header) + 2) { u8 *payload = (u8 *)hdr + hdrlen; if (ether_addr_equal(payload, rfc1042_header)) @@ -2523,7 +2523,7 @@ static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb) int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - struct sta_info **sta_out) + struct sta_info **sta_out, bool bss) { struct sta_info *sta; @@ -2544,7 +2544,10 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, *sta_out = ERR_PTR(-ENOENT); return 0; } - sta = sta_info_get_bss(sdata, skb->data); + if (bss) + sta = sta_info_get_bss(sdata, skb->data); + else + sta = sta_info_get(sdata, skb->data); break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: @@ -4390,7 +4393,8 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, ieee80211_mesh_xmit_fast(sdata, skb, ctrl_flags)) goto out; - if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) + if (ieee80211_lookup_ra_sta(sdata, skb, &sta, + skb->protocol == sdata->control_port_protocol)) goto out_free; if (IS_ERR(sta)) @@ -4703,6 +4707,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, { struct ieee80211_tx_info *info; struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; struct tid_ampdu_tx *tid_tx = NULL; struct sk_buff *seg, *next; unsigned int skbs = 0, len = 0; @@ -4752,6 +4757,16 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); + /* MLD transmissions must not rely on the band */ + if (!ieee80211_vif_is_mld(&sdata->vif)) { + chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf); + if (unlikely(!chanctx_conf)) { + kfree_skb_list(skb); + return; + } + info->band = chanctx_conf->def.chan->band; + } + info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP; info->control.vif = &sdata->vif; @@ -4820,7 +4835,10 @@ static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb, rcu_read_lock(); - if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) { + if (unlikely(sdata->control_port_protocol == ehdr->h_proto)) + goto skip_offload; + + if (ieee80211_lookup_ra_sta(sdata, skb, &sta, false)) { kfree_skb(skb); goto out; } @@ -4841,8 +4859,7 @@ static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb, link = &sdata->deflink; key = rcu_dereference(link->default_multicast_key); } else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded || - !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || - sdata->control_port_protocol == ehdr->h_proto)) { + !test_sta_flag(sta, WLAN_STA_AUTHORIZED))) { goto skip_offload; } else { key = rcu_dereference(sta->ptk[sta->ptk_idx]); @@ -4903,7 +4920,7 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); - if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) { + if (ieee80211_lookup_ra_sta(sdata, skb, &sta, false)) { kfree_skb(skb); skb = ERR_PTR(-EINVAL); goto out; @@ -4974,7 +4991,7 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, } result = ieee80211_tx(sdata, NULL, skb, true); } else if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) { - if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) { + if (ieee80211_lookup_ra_sta(sdata, skb, &sta, true)) { dev_kfree_skb(skb); return true; } @@ -6611,7 +6628,7 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, * AF_PACKET */ rcu_read_lock(); - err = ieee80211_lookup_ra_sta(sdata, skb, &sta); + err = ieee80211_lookup_ra_sta(sdata, skb, &sta, true); if (err) { dev_kfree_skb(skb); rcu_read_unlock(); diff --git a/net/mctp/device.c b/net/mctp/device.c index 822120e860c8..9f76ff092559 100644 --- a/net/mctp/device.c +++ b/net/mctp/device.c @@ -344,13 +344,13 @@ static struct mctp_dev *mctp_add_dev(struct net_device *dev) mdev->net = mctp_default_net(dev_net(dev)); - /* associate to net_device */ refcount_set(&mdev->refs, 1); - rcu_assign_pointer(dev->mctp_ptr, mdev); - dev_hold(dev); mdev->dev = dev; + /* Associate to net_device, publish the mdev. */ + rcu_assign_pointer(dev->mctp_ptr, mdev); + return mdev; } diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index de6f289a6e14..79ec52f64dc5 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3475,7 +3475,7 @@ static int mptcp_disconnect(struct sock *sk, int flags) /* The later subflow close can not kick again the tout timer, * as the msk is already in closed status. */ - msk->timer_ival = icsk->icsk_rto_min; + msk->timer_ival = TCP_RTO_MIN; sk_stop_timer_sync(sk, &sk->mptcp_retransmit_timer); icsk->icsk_mtup.probe_timestamp = 0; sk_stop_timer_sync(sk, &icsk->mptcp_tout_timer); diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index d6a7e6604542..ae376fa3e7a3 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -159,7 +159,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (SET_WITH_COUNTER(set)) ip_set_init_counter(ext_counter(x, set), ext); - if (SET_WITH_COMMENT(set)) + if (SET_WITH_COMMENT(set) && !ext->target) ip_set_init_comment(set, ext_comment(x, set), ext); if (SET_WITH_SKBINFO(set)) ip_set_init_skbinfo(ext_skbinfo(x, set), ext); diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 6fa3e1dc534c..cb009208826f 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -1102,6 +1102,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) if (cp->protocol != IPPROTO_UDP) conn_flags &= ~IP_VS_CONN_F_ONE_PACKET; flags = cp->flags; + /* Only visible templates can control multiple connections */ + if (flags & IP_VS_CONN_F_TEMPLATE) + conn_flags &= ~IP_VS_CONN_F_ONE_PACKET; /* Bind with the destination and its corresponding transmitter */ if (flags & IP_VS_CONN_F_SYNC) { /* Synced conns are hashed, so they can not get this flag */ diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 693bcc82ccb7..55af77a56929 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -203,7 +203,11 @@ ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr, if (en->dest == dest) return en; ip_vs_lblc_del(en); + atomic_dec(&tbl->entries); } + if (atomic_read(&tbl->entries) >= tbl->max_size * 3 / 2) + return NULL; + en = kmalloc_obj(*en, GFP_ATOMIC); if (!en) return NULL; diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index f53f05ceea36..858393b1d2d1 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -363,6 +363,9 @@ ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, const union nf_inet_addr *daddr, en = ip_vs_lblcr_get(af, tbl, daddr); if (!en) { + if (atomic_read(&tbl->entries) >= tbl->max_size * 3 / 2) + return NULL; + en = kmalloc_obj(*en, GFP_ATOMIC); if (!en) return NULL; diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 5383aeafb0ae..69dc28153ec1 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -949,6 +949,21 @@ static void ip_vs_proc_conn(struct netns_ipvs *ipvs, struct ip_vs_conn_param *pa ip_vs_conn_put(cp); } +/* Check for incompatible flags */ +static bool ip_vs_sync_validate_flags(u32 flags) +{ + /* We do not expect NO_CPORT, especially to allow lookups + * to hit templates + */ + if (flags & IP_VS_CONN_F_NO_CPORT) { + if (flags & IP_VS_CONN_F_TEMPLATE) + return false; + } + if (flags & IP_VS_CONN_F_ONE_PACKET) + return false; + return true; +} + /* * Process received multicast message for Version 0 */ @@ -972,8 +987,7 @@ static void ip_vs_process_message_v0(struct netns_ipvs *ipvs, const char *buffer return; } s = (struct ip_vs_sync_conn_v0 *) p; - flags = ntohs(s->flags) | IP_VS_CONN_F_SYNC; - flags &= ~IP_VS_CONN_F_HASHED; + flags = ntohs(s->flags); if (flags & IP_VS_CONN_F_SEQ_MASK) { opt = (struct ip_vs_sync_conn_options *)&s[1]; p += FULL_CONN_SIZE; @@ -986,6 +1000,13 @@ static void ip_vs_process_message_v0(struct netns_ipvs *ipvs, const char *buffer p += SIMPLE_CONN_SIZE; } + if (!ip_vs_sync_validate_flags(flags)) { + IP_VS_DBG(2, "BACKUP v0, Invalid flags 0x%X\n", flags); + continue; + } + flags &= IP_VS_CONN_F_BACKUP_MASK; + flags |= IP_VS_CONN_F_SYNC; + state = ntohs(s->state); if (!(flags & IP_VS_CONN_F_TEMPLATE)) { pp = ip_vs_proto_get(s->protocol); @@ -1141,7 +1162,13 @@ static inline int ip_vs_proc_sync_conn(struct netns_ipvs *ipvs, __u8 *p, __u8 *m } /* Get flags and Mask off unsupported */ - flags = ntohl(s->v4.flags) & IP_VS_CONN_F_BACKUP_MASK; + flags = ntohl(s->v4.flags); + if (!ip_vs_sync_validate_flags(flags)) { + IP_VS_DBG(3, "BACKUP, Invalid flags 0x%X\n", flags); + retc = 25; + goto out; + } + flags &= IP_VS_CONN_F_BACKUP_MASK; flags |= IP_VS_CONN_F_SYNC; state = ntohs(s->v4.state); diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 254063fca078..7f51b18f9fb4 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -575,7 +575,12 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct) static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table, struct flow_offload *flow, void *data) { - bool teardown = test_bit(NF_FLOW_TEARDOWN, &flow->flags); + bool teardown; + + if (test_bit(NF_FLOW_PENDING, &flow->flags)) + return; + + teardown = test_bit(NF_FLOW_TEARDOWN, &flow->flags); if (nf_flow_has_expired(flow) || nf_ct_is_dying(flow->ct) || diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index 6757fd89c1f1..4365859220e6 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -995,6 +995,7 @@ static void flow_offload_work_del(struct flow_offload_work *offload) flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_ORIGINAL); if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags)) flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY); + set_bit(NF_FLOW_HW_DEAD, &offload->flow->flags); } static void flow_offload_tuple_stats(struct flow_offload_work *offload, @@ -1056,13 +1057,8 @@ static void flow_offload_work_handler(struct work_struct *work) default: WARN_ON_ONCE(1); } - - clear_bit(NF_FLOW_HW_PENDING, &offload->flow->flags); - if (offload->cmd == FLOW_CLS_DESTROY) { - /* Publish after the worker's last flow access. */ - smp_mb__before_atomic(); - set_bit(NF_FLOW_HW_DEAD, &offload->flow->flags); - } + smp_mb__before_atomic(); + clear_bit(NF_FLOW_PENDING, &offload->flow->flags); kfree(offload); } @@ -1089,12 +1085,12 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable, { struct flow_offload_work *offload; - if (test_and_set_bit(NF_FLOW_HW_PENDING, &flow->flags)) + if (test_and_set_bit(NF_FLOW_PENDING, &flow->flags)) return NULL; offload = kmalloc_obj(struct flow_offload_work, GFP_ATOMIC); if (!offload) { - clear_bit(NF_FLOW_HW_PENDING, &flow->flags); + clear_bit(NF_FLOW_PENDING, &flow->flags); return NULL; } diff --git a/net/netfilter/nf_nat_bpf.c b/net/netfilter/nf_nat_bpf.c index f9dd85ccea01..7572b58c448b 100644 --- a/net/netfilter/nf_nat_bpf.c +++ b/net/netfilter/nf_nat_bpf.c @@ -39,6 +39,9 @@ __bpf_kfunc int bpf_ct_set_nat_info(struct nf_conn___init *nfct, if (proto != NFPROTO_IPV4 && proto != NFPROTO_IPV6) return -EINVAL; + if (manip != NF_NAT_MANIP_SRC && manip != NF_NAT_MANIP_DST) + return -EINVAL; + memset(&range, 0, sizeof(struct nf_nat_range2)); range.flags = NF_NAT_RANGE_MAP_IPS; range.min_addr = *addr; diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index be021d8ecc41..eee3d5427146 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -773,8 +773,9 @@ nf_nat_setup_info(struct nf_conn *ct, if (nf_ct_is_confirmed(ct)) return NF_ACCEPT; - WARN_ON(maniptype != NF_NAT_MANIP_SRC && - maniptype != NF_NAT_MANIP_DST); + if (WARN_ON(maniptype != NF_NAT_MANIP_SRC && + maniptype != NF_NAT_MANIP_DST)) + return NF_DROP; if (WARN_ON(nf_nat_initialized(ct, maniptype))) return NF_DROP; diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c index 32b4281038dd..d3c5651dd699 100644 --- a/net/netfilter/nft_flow_offload.c +++ b/net/netfilter/nft_flow_offload.c @@ -160,6 +160,7 @@ static int nft_flow_offload_init(const struct nft_ctx *ctx, struct nft_flow_offload *priv = nft_expr_priv(expr); u8 genmask = nft_genmask_next(ctx->net); struct nft_flowtable *flowtable; + int err; if (!tb[NFTA_FLOW_TABLE_NAME]) return -EINVAL; @@ -174,7 +175,11 @@ static int nft_flow_offload_init(const struct nft_ctx *ctx, priv->flowtable = flowtable; - return nf_ct_netns_get(ctx->net, ctx->family); + err = nf_ct_netns_get(ctx->net, ctx->family); + if (err < 0) + nft_use_dec(&flowtable->use); + + return err; } static void nft_flow_offload_deactivate(const struct nft_ctx *ctx, diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 7c83e01526ed..8865c1a42667 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2874,6 +2874,9 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) vnet_hdr.hdr_len); has_vnet_hdr = true; } + /* Prevent the byte count from wrapping into a negative errno */ + if (unlikely(tp_len > INT_MAX - len_sum)) + break; copylen = max_t(int, copylen, hard_header_len); skb = sock_alloc_send_skb(&po->sk, hlen + tlen + sizeof(struct sockaddr_ll) + diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 1d62bb654149..1cc6eb625061 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -289,7 +289,7 @@ static bool tcf_ct_flow_is_outdated(const struct flow_offload *flow) { return test_bit(IPS_SEEN_REPLY_BIT, &flow->ct->status) && test_bit(IPS_HW_OFFLOAD_BIT, &flow->ct->status) && - !test_bit(NF_FLOW_HW_PENDING, &flow->flags) && + !test_bit(NF_FLOW_PENDING, &flow->flags) && !test_bit(NF_FLOW_HW_ESTABLISHED, &flow->flags); } diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index bfec6b668410..26258b353cde 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -72,9 +72,8 @@ TC_INDIRECT_SCOPE int tcf_skbedit_act(struct sk_buff *skb, } if (params->flags & SKBEDIT_F_QUEUE_MAPPING && skb->dev->real_num_tx_queues > params->queue_mapping) { -#ifdef CONFIG_NET_EGRESS - netdev_xmit_skip_txqueue(true); -#endif + if (skb_at_tc_egress(skb)) + netdev_xmit_skip_txqueue(true); skb_set_queue_mapping(skb, tcf_skbedit_hash(params, skb)); } if (params->flags & SKBEDIT_F_MARK) { diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index c47d2ee13641..a9f54988561f 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -2463,7 +2463,20 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n, } errout: - if (err && tp_state == TP_CREATED) + if (err && !IS_ERR_OR_NULL(tp) && + (tp_state == TP_CREATED || tp->ops->delete_empty)) + /* + * The request is dropping its reference to tp. If it was + * the last user (the idr is empty now), reclaim the proto. + * A tp this request created is reclaimed unconditionally: + * it is the only owner, so marking it for deletion is + * safe. Otherwise only classifiers with a delete_empty + * callback are reclaimed -- the callback admits an empty + * proto only, so a live shared proto is never unlinked. + * Classifiers without one (deleting is set + * unconditionally) are rtnl-serialized, so the raced + * window this guard closes cannot arise for them. + */ tcf_chain_tp_delete_empty(chain, tp, rtnl_held, NULL); errout_tp: if (chain) { diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c index 6aa5829d6961..a1ba2e355f2d 100644 --- a/net/sched/sch_codel.c +++ b/net/sched/sch_codel.c @@ -205,7 +205,7 @@ static int codel_init(struct Qdisc *sch, struct nlattr *opt, codel_params_init(&q->params); codel_vars_init(&q->vars); codel_stats_init(&q->stats); - q->params.mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 256, 1 << 20); + q->params.mtu = min_t(u32, psched_mtu(qdisc_dev(sch)), 1 << 20); if (opt) { int err = codel_change(sch, opt, extack); diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index 969b2510b0b8..e6c87a32950f 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -517,8 +517,8 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt, q->flows_cnt = 1024; q->memory_limit = 32 << 20; /* 32 MBytes */ q->drop_batch_size = 64; - mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 256, FQ_CODEL_QUANTUM_MAX); - q->quantum = mtu; + mtu = min_t(u32, psched_mtu(qdisc_dev(sch)), FQ_CODEL_QUANTUM_MAX); + q->quantum = clamp_t(u32, mtu, 256, FQ_CODEL_QUANTUM_MAX); INIT_LIST_HEAD(&q->new_flows); INIT_LIST_HEAD(&q->old_flows); codel_params_init(&q->cparams); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index c7b9e325ec1c..4652fd90d9a6 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1844,7 +1844,7 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc, if (asoc->ep->intl_enable) { timeo = sock_sndtimeo(sk, 0); err = sctp_wait_for_connect(asoc, &timeo); - if (err) { + if (err || sk->sk_shutdown & RCV_SHUTDOWN) { err = -ESRCH; goto err; } diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c index 16f1ed1f6b1b..4409bdb70fe9 100644 --- a/net/tipc/crypto.c +++ b/net/tipc/crypto.c @@ -144,7 +144,7 @@ struct tipc_tfm { * @rcu: struct rcu_head * @key: the aead key * @gen: the key's generation - * @seqno: the key seqno (cluster scope) + * @seqno: the per-key TX nonce counter * @refcnt: the key reference counter */ struct tipc_aead { @@ -190,7 +190,6 @@ struct tipc_crypto_stats { * @rekeying_intv: rekeying interval (in minutes) * @stats: the crypto statistics * @name: the crypto name - * @sndnxt: the per-peer sndnxt (TX) * @timer1: general timer 1 (jiffies) * @timer2: general timer 2 (jiffies) * @working: the crypto is working or not @@ -219,7 +218,6 @@ struct tipc_crypto { struct tipc_crypto_stats __percpu *stats; char name[48]; - atomic64_t sndnxt ____cacheline_aligned; unsigned long timer1; unsigned long timer2; union { @@ -1051,14 +1049,11 @@ static int tipc_ehdr_build(struct net *net, struct tipc_aead *aead, WARN_ON(skb_headroom(skb) < ehsz); ehdr = (struct tipc_ehdr *)skb_push(skb, ehsz); - /* Obtain a seqno first: - * Use the key seqno (= cluster wise) if dest is unknown or we're in - * cluster key mode, otherwise it's better for a per-peer seqno! + /* + * Keep the nonce unique for the lifetime of the TX key, + * including key state changes and peer reconnection. */ - if (!__rx || aead->mode == CLUSTER_KEY) - seqno = atomic64_inc_return(&aead->seqno); - else - seqno = atomic64_inc_return(&__rx->sndnxt); + seqno = atomic64_inc_return(&aead->seqno); /* Revoke the key if seqno is wrapped around */ if (unlikely(!seqno)) @@ -1237,7 +1232,6 @@ void tipc_crypto_key_flush(struct tipc_crypto *c) tipc_crypto_key_set_state(c, 0, 0, 0); for (k = KEY_MIN; k <= KEY_MAX; k++) tipc_crypto_key_detach(c->aead[k], &c->lock); - atomic64_set(&c->sndnxt, 0); spin_unlock_bh(&c->lock); } @@ -1384,8 +1378,6 @@ static struct tipc_aead *tipc_crypto_key_pick_tx(struct tipc_crypto *tx, * It also considers if peer has no key, then we need to make own master key * (if any) taking over i.e. starting grace period and also trigger key * distributing process. - * - * The "per-peer" sndnxt is also reset when the peer key has switched. */ static void tipc_crypto_key_synch(struct tipc_crypto *rx, struct sk_buff *skb) { @@ -1436,7 +1428,6 @@ static void tipc_crypto_key_synch(struct tipc_crypto *rx, struct sk_buff *skb) if (cur) tipc_aead_users_dec(tx->aead[cur], 0); - atomic64_set(&rx->sndnxt, 0); /* Mark the point TX key users changed */ tx->timer1 = jiffies; @@ -1501,7 +1492,6 @@ int tipc_crypto_start(struct tipc_crypto **crypto, struct net *net, tipc_crypto_key_set_state(c, 0, 0, 0); atomic_set(&c->key_distr, 0); atomic_set(&c->peer_rx_active, 0); - atomic64_set(&c->sndnxt, 0); c->timer1 = jiffies; c->timer2 = jiffies; c->rekeying_intv = TIPC_REKEYING_INTV_DEF; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 755f8fe711fb..688050dbeb39 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4562,12 +4562,13 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) rdev->wiphy.retry_long = retry_long; if (changed & WIPHY_PARAM_FRAG_THRESHOLD) rdev->wiphy.frag_threshold = frag_threshold; - if ((changed & WIPHY_PARAM_RTS_THRESHOLD) && - old_radio_rts_threshold) { + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { rdev->wiphy.rts_threshold = rts_threshold; - for (i = 0 ; i < rdev->wiphy.n_radio; i++) - rdev->wiphy.radio_cfg[i].rts_threshold = - rdev->wiphy.rts_threshold; + if (old_radio_rts_threshold) { + for (i = 0; i < rdev->wiphy.n_radio; i++) + rdev->wiphy.radio_cfg[i].rts_threshold = + rdev->wiphy.rts_threshold; + } } if (changed & WIPHY_PARAM_COVERAGE_CLASS) rdev->wiphy.coverage_class = coverage_class; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 4a1ec7e3547d..9af995894bd5 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1964,20 +1964,22 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev, return false; } - old = rcu_access_pointer(known->pub.beacon_ies); + if (!new->pub.hidden_beacon_bss) { + old = rcu_access_pointer(known->pub.beacon_ies); - rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies); + rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies); - /* Override IEs if they were from a beacon before */ - if (old == rcu_access_pointer(known->pub.ies)) - rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies); + /* Override IEs if they were from a beacon before */ + if (old == rcu_access_pointer(known->pub.ies)) + rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies); - cfg80211_update_hidden_bsses(known, - rcu_access_pointer(new->pub.beacon_ies), - old); + cfg80211_update_hidden_bsses(known, + rcu_access_pointer(new->pub.beacon_ies), + old); - if (old) - kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); + if (old) + kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); + } } known->pub.beacon_interval = new->pub.beacon_interval; diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs index f425fe12f7c8..d84a32ff0f7c 100644 --- a/rust/kernel/irq/request.rs +++ b/rust/kernel/irq/request.rs @@ -14,7 +14,7 @@ use crate::irq::flags::Flags; use crate::prelude::*; use crate::str::CStr; -use crate::sync::Arc; +use crate::sync::{aref::ARef, Arc}; /// The value that can be returned from a [`Handler`] or a [`ThreadedHandler`]. #[repr(u32)] @@ -54,13 +54,18 @@ fn handle(&self, device: &Device) -> IrqReturn { /// # Invariants /// /// - `self.irq` is the same as the one passed to `request_{threaded}_irq`. -/// - `cookie` was passed to `request_{threaded}_irq` as the cookie. It is guaranteed to be unique +/// - `&self` was passed to `request_{threaded}_irq` as the cookie. It is guaranteed to be unique /// by the type system, since each call to `new` will return a different instance of /// `Registration`. +/// - `self.handler` points to a valid instance of the handler `T` that lives at least until +/// `Self::drop` completes. #[pin_data(PinnedDrop)] struct RegistrationInner { irq: u32, - cookie: *mut c_void, + dev: ARef, + handler: *const c_void, + #[pin] + _pin: PhantomPinned, } impl RegistrationInner { @@ -77,18 +82,22 @@ fn drop(self: Pin<&mut Self>) { // // Safe as per the invariants of `RegistrationInner` and: // - // - The containing struct is `!Unpin` and was initialized using + // - `RegistrationInner` is `!Unpin` and was initialized using // pin-init, so it occupied the same memory location for the entirety of // its lifetime. // // Notice that this will block until all handlers finish executing, // i.e.: at no point will &self be invalid while the handler is running. - unsafe { bindings::free_irq(self.irq, self.cookie) }; + unsafe { + bindings::free_irq( + self.irq, + core::ptr::from_mut::(self.get_unchecked_mut()).cast::(), + ) + }; } } -// SAFETY: We only use `inner` on drop, which called at most once with no -// concurrent access. +// SAFETY: `RegistrationInner` has no interior mutability and `handler` points to a `Sync` handler. unsafe impl Sync for RegistrationInner {} // SAFETY: It is safe to send `RegistrationInner` across threads. @@ -179,7 +188,7 @@ pub fn irq(&self) -> u32 { /// /// # Invariants /// -/// * We own an irq handler whose cookie is a pointer to `Self`. +/// * We own an irq handler whose cookie is a pointer to `Self::inner`. #[pin_data] pub struct Registration { #[pin] @@ -206,10 +215,13 @@ pub fn new<'a>( handler <- handler, inner <- Devres::new( request.dev, - try_pin_init!(RegistrationInner { - // INVARIANT: `this` is a valid pointer to the `Registration` instance - cookie: this.as_ptr().cast::(), - irq: { + try_pin_init!(&inner_this in RegistrationInner { + irq: request.irq, + dev: request.dev.into(), + // SAFETY: `this` is a valid pointer to the `Registration` instance. + handler: unsafe { &raw const (*this.as_ptr()).handler }.cast(), + _pin: PhantomPinned, + _: { // SAFETY: // - The callbacks are valid for use with request_irq. // - If this succeeds, the slot is guaranteed to be valid until the @@ -224,11 +236,10 @@ pub fn new<'a>( Some(handle_irq_callback::), flags.into_inner(), name.as_char_ptr(), - this.as_ptr().cast::(), + inner_this.as_ptr().cast::(), ) })?; - request.irq - } + }, }) ), _pin: PhantomPinned, @@ -261,13 +272,15 @@ pub fn synchronize(&self, dev: &Device) -> Result { /// /// This function should be only used as the callback in `request_irq`. unsafe extern "C" fn handle_irq_callback(_irq: i32, ptr: *mut c_void) -> c_uint { - // SAFETY: `ptr` is a pointer to `Registration` set in `Registration::new` - let registration = unsafe { &*(ptr as *const Registration) }; + // SAFETY: `ptr` is a pointer to `RegistrationInner` set in `Registration::new` + let inner = unsafe { &*(ptr as *const RegistrationInner) }; + // SAFETY: `inner.handler` is a pointer to `T` set in `Registration::new` + let handler = unsafe { &*inner.handler.cast::() }; // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq // callback is running implies that the device has not yet been unbound. - let device = unsafe { registration.inner.device().as_bound() }; + let device = unsafe { inner.dev.as_bound() }; - T::handle(®istration.handler, device) as c_uint + T::handle(handler, device) as c_uint } /// The value that can be returned from [`ThreadedHandler::handle`]. @@ -396,7 +409,7 @@ fn handle_threaded(&self, device: &Device) -> IrqReturn { /// /// # Invariants /// -/// * We own an irq handler whose cookie is a pointer to `Self`. +/// * We own an irq handler whose cookie is a pointer to `Self::inner`. #[pin_data] pub struct ThreadedRegistration { #[pin] @@ -423,10 +436,13 @@ pub fn new<'a>( handler <- handler, inner <- Devres::new( request.dev, - try_pin_init!(RegistrationInner { - // INVARIANT: `this` is a valid pointer to the `ThreadedRegistration` instance. - cookie: this.as_ptr().cast::(), - irq: { + try_pin_init!(&inner_this in RegistrationInner { + irq: request.irq, + dev: request.dev.into(), + // SAFETY: `this` is a valid pointer to the `ThreadedRegistration` instance. + handler: unsafe { &raw const (*this.as_ptr()).handler }.cast(), + _pin: PhantomPinned, + _: { // SAFETY: // - The callbacks are valid for use with request_threaded_irq. // - If this succeeds, the slot is guaranteed to be valid until the @@ -442,11 +458,10 @@ pub fn new<'a>( Some(thread_fn_callback::), flags.into_inner(), name.as_char_ptr(), - this.as_ptr().cast::(), + inner_this.as_ptr().cast::(), ) })?; - request.irq - } + }, }) ), _pin: PhantomPinned, @@ -482,24 +497,28 @@ pub fn synchronize(&self, dev: &Device) -> Result { _irq: i32, ptr: *mut c_void, ) -> c_uint { - // SAFETY: `ptr` is a pointer to `ThreadedRegistration` set in `ThreadedRegistration::new` - let registration = unsafe { &*(ptr as *const ThreadedRegistration) }; + // SAFETY: `ptr` is a pointer to `RegistrationInner` set in `ThreadedRegistration::new` + let inner = unsafe { &*(ptr as *const RegistrationInner) }; + // SAFETY: `inner.handler` is a pointer to `T` set in `ThreadedRegistration::new` + let handler = unsafe { &*inner.handler.cast::() }; // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq // callback is running implies that the device has not yet been unbound. - let device = unsafe { registration.inner.device().as_bound() }; + let device = unsafe { inner.dev.as_bound() }; - T::handle(®istration.handler, device) as c_uint + T::handle(handler, device) as c_uint } /// # Safety /// /// This function should be only used as the callback in `request_threaded_irq`. unsafe extern "C" fn thread_fn_callback(_irq: i32, ptr: *mut c_void) -> c_uint { - // SAFETY: `ptr` is a pointer to `ThreadedRegistration` set in `ThreadedRegistration::new` - let registration = unsafe { &*(ptr as *const ThreadedRegistration) }; + // SAFETY: `ptr` is a pointer to `RegistrationInner` set in `ThreadedRegistration::new` + let inner = unsafe { &*(ptr as *const RegistrationInner) }; + // SAFETY: `inner.handler` is a pointer to `T` set in `ThreadedRegistration::new` + let handler = unsafe { &*inner.handler.cast::() }; // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq // callback is running implies that the device has not yet been unbound. - let device = unsafe { registration.inner.device().as_bound() }; + let device = unsafe { inner.dev.as_bound() }; - T::handle_threaded(®istration.handler, device) as c_uint + T::handle_threaded(handler, device) as c_uint } diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c index 22679dca9aae..80110501da6f 100644 --- a/sound/core/seq/seq_compat.c +++ b/sound/core/seq/seq_compat.c @@ -44,7 +44,9 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned return -EFAULT; data->kernel = NULL; - err = snd_seq_kernel_client_ctl(client->number, cmd, data); + scoped_guard(mutex, &client->ioctl_mutex) { + err = snd_seq_kernel_client_ctl(client->number, cmd, data); + } if (err < 0) return err; diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 1e0612c39c89..e98cc42e1a78 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6891,6 +6891,8 @@ static const struct hda_fixup alc269_fixups[] = { [ALC236_FIXUP_DELL_HP_POP_NOISE] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_invalidate_dacs, + .chained = true, + .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE }, [ALC274_FIXUP_HP_89E9_GPIO] = { .type = HDA_FIXUP_FUNC, @@ -6973,6 +6975,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x173a, "Acer Swift SFG14-73", ALC245_FIXUP_ACER_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x173b, "Acer Swift SFG16-72", ALC245_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x1758, "Acer Nitro ANV15-41", ALC245_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), @@ -7680,7 +7683,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C_ASUS), SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2), @@ -7703,6 +7706,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN), SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE), + SND_PCI_QUIRK(0x10ec, 0x111e, "Higole F9B", ALC290_FIXUP_MONO_SPEAKERS), SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE), SND_PCI_QUIRK(0x10ec, 0x119e, "Positivo SU C1400", ALC269_FIXUP_ASPIRE_HEADSET_MIC), SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), diff --git a/sound/hda/controllers/tegra.c b/sound/hda/controllers/tegra.c index 31c14c4bbe68..674cc0f79c3c 100644 --- a/sound/hda/controllers/tegra.c +++ b/sound/hda/controllers/tegra.c @@ -374,7 +374,8 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) * in powers of 2, next available ratio is 16 which can be * used as a limiting factor here. */ - if (of_device_is_compatible(np, "nvidia,tegra30-hda")) + if (of_device_is_compatible(np, "nvidia,tegra30-hda") || + of_device_is_compatible(np, "nvidia,tegra194-hda")) chip->bus.core.sdo_limit = 16; /* codec detection */ diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 36066ffe8784..26a6fa69f5f5 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -1895,6 +1895,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) if (hw->model == CTSB1270) { /* Set up the PCM4220 ADC on Titanium HD */ + data = hw_read_20kx(hw, GPIO_DATA); data &= ~0x0C; if (1 == info->msr) data |= 0x00; /* Single Speed Mode 32-50kHz */ @@ -1903,6 +1904,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) else data |= 0x04; /* Quad Speed Mode 108kHz-216kHz */ hw_write_20kx(hw, GPIO_DATA, data); + usleep_range(10000, 11000); } hw_adc_start(hw); diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index d6bfd8fb20e6..1fad8c418132 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2523,7 +2523,7 @@ static int snd_ice1712_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { static int dev; - struct snd_card *card __free(snd_card_unref) = NULL; + struct snd_card *card __free(snd_card_free) = NULL; struct snd_ice1712 *ice; int pcm_dev = 0, err; const struct snd_ice1712_card_info * const *tbl, *c; diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index af521e0988db..0bb733353fbb 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -1840,6 +1840,7 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int gain; u16 gain_reg; u16 reg; struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); @@ -1881,6 +1882,8 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w, CDC_WSA_RX_PGA_HALF_DB_MASK, CDC_WSA_RX_PGA_HALF_DB_ENABLE); } + gain = snd_soc_component_read(component, gain_reg); + snd_soc_component_write(component, gain_reg, gain); wsa_macro_config_ear_spkr_gain(component, wsa, event, gain_reg); break; diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index 099dc5bf6195..9f9d49e7a1c7 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -195,7 +195,7 @@ static int max98363_sdw_dai_hw_params(struct snd_pcm_substream *substream, struct max98363_priv *max98363 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; + struct sdw_stream_config stream_config = {0}; struct sdw_port_config port_config; enum sdw_data_direction direction; struct sdw_stream_runtime *stream; diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index 95405cea8143..f9602a247487 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -573,11 +574,10 @@ static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_component *component = dai->component; struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; + struct sdw_stream_config stream_config = {0}; struct sdw_port_config port_config; - enum sdw_data_direction direction; struct sdw_stream_runtime *sdw_stream; - int retval, port, num_channels, ch_mask; + int retval, port; unsigned int sampling_rate; dev_dbg(dai->dev, "%s %s", __func__, dai->name); @@ -591,28 +591,17 @@ static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream, /* SoundWire specific configuration */ /* port 1 for playback */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - direction = SDW_DATA_DIR_RX; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) port = 1; - } else { - direction = SDW_DATA_DIR_TX; + else port = 2; - } - - num_channels = params_channels(params); - ch_mask = (1 << num_channels) - 1; - - stream_config.frame_rate = params_rate(params); - stream_config.ch_count = num_channels; - stream_config.bps = snd_pcm_format_width(params_format(params)); - stream_config.direction = direction; - port_config.ch_mask = ch_mask; + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); port_config.num = port; dev_dbg(dai->dev, "frame_rate %d, ch_count %d, bps %d, direction %d, ch_mask %d, port: %d\n", - params_rate(params), num_channels, snd_pcm_format_width(params_format(params)), - direction, ch_mask, port); + stream_config.frame_rate, stream_config.ch_count, stream_config.bps, + stream_config.direction, port_config.ch_mask, port); retval = sdw_stream_add_slave(rt1017->sdw_slave, &stream_config, &port_config, 1, sdw_stream); diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index f8c370106504..3c47f1ae8db9 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -619,9 +619,11 @@ static int rt274_hw_params(struct snd_pcm_substream *substream, unsigned int val = 0; int d_len_code = 0, c_len_code = 0; + /* bit 14 Sample Rate Base, 0: 48kHz 1: 44.1kHz */ switch (params_rate(params)) { - /* bit 14 0:48K 1:44.1K */ case 44100: + val |= BIT(14); + break; case 48000: break; default: diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 4217467904d6..bcd9f5abcda8 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -953,7 +953,7 @@ static int rt286_probe(struct snd_soc_component *component) if (rt286->i2c->irq) schedule_delayed_work(&rt286->jack_detect_work, - msecs_to_jiffies(50)); + msecs_to_jiffies(1250)); return 0; } diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 320d7737699d..585f0185c0d3 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -677,7 +677,7 @@ SOC_ENUM("DRC Decay Rate", drc_decay), SOC_ENUM("DRC FF Delay", drc_ff_delay), SOC_SINGLE("DRC Anticlip Switch", WM8903_DRC_0, 1, 1, 0), SOC_SINGLE("DRC QR Switch", WM8903_DRC_0, 2, 1, 0), -SOC_SINGLE_TLV("DRC QR Threshold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max), +SOC_SINGLE_TLV("DRC QR Threshold Volume", WM8903_DRC_1, 6, 3, 0, drc_tlv_max), SOC_ENUM("DRC QR Decay Rate", drc_qr_decay), SOC_SINGLE("DRC Smoothing Switch", WM8903_DRC_0, 3, 1, 0), SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8903_DRC_0, 0, 1, 0), diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 242bc16da36d..079177a6d81e 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -1234,8 +1234,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) } if (drvdata->mi2s_playback_sd_mode[dai_id] == LPAIF_I2SCTL_MODE_QUAD01) { - variant->dai_driver[dai_id].playback.channels_min = 4; - variant->dai_driver[dai_id].playback.channels_max = 4; + variant->dai_driver[i].playback.channels_min = 4; + variant->dai_driver[i].playback.channels_max = 4; } } diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 2fb2b043c979..60eecf34c778 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -388,20 +388,27 @@ static int class_function_probe(struct auxiliary_device *auxdev, ret = devm_pm_runtime_enable(dev); if (ret) - return ret; + goto err_pm; ret = class_function_boot(drv); if (ret) - return ret; + goto err_pm; ret = devm_snd_soc_register_component(dev, cmp_drv, dais, num_dais); - if (ret) - return dev_err_probe(dev, ret, "failed to register component\n"); + if (ret) { + dev_err_probe(dev, ret, "failed to register component\n"); + goto err_pm; + } pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; + +err_pm: + pm_runtime_put_sync(dev); + + return ret; } static void class_function_remove(struct auxiliary_device *auxdev) @@ -490,8 +497,6 @@ static int class_function_suspend(struct device *dev) struct class_function_drv *drv = auxiliary_get_drvdata(auxdev); int ret; - drv->suspended = true; - /* Ensure runtime resume runs on resume */ ret = pm_runtime_resume_and_get(dev); if (ret) { @@ -499,6 +504,8 @@ static int class_function_suspend(struct device *dev) return ret; } + drv->suspended = true; + sdca_irq_disable(drv->function, drv->core->irq_info); ret = pm_runtime_force_suspend(dev); diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index befcad08135e..5266720e58b3 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -1510,7 +1510,7 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai_link_ch_map *ch_maps; int ch = params_channels(params); - unsigned int ch_mask; + unsigned int cpu_ch_mask, codec_ch_mask; int num_codecs; int step; int i; @@ -1520,8 +1520,9 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, /* Identical data will be sent to all codecs in playback */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ch_mask = GENMASK(ch - 1, 0); + cpu_ch_mask = GENMASK(ch - 1, 0); step = 0; + codec_ch_mask = 0; } else { num_codecs = rtd->dai_link->num_codecs; @@ -1531,17 +1532,24 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - ch_mask = GENMASK(ch / num_codecs - 1, 0); - step = hweight_long(ch_mask); + cpu_ch_mask = GENMASK(ch / num_codecs - 1, 0); + step = hweight_long(cpu_ch_mask); + codec_ch_mask = cpu_ch_mask; } /* * The captured data will be combined from each cpu DAI if the dai * link has more than one codec DAIs. Set codec channel mask and * ASoC will set the corresponding channel numbers for each cpu dai. + * + * sdw_stream_add_slave() assigns different payload offsets to each + * codec in a capture stream, so that the same channels on each + * codec map to different channels on the CPU. */ - for_each_link_ch_maps(rtd->dai_link, i, ch_maps) - ch_maps->cpu_ch_mask = ch_mask << (i * step); + for_each_link_ch_maps(rtd->dai_link, i, ch_maps) { + ch_maps->cpu_ch_mask = cpu_ch_mask << (i * step); + ch_maps->codec_ch_mask = codec_ch_mask; + } return 0; } diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c index 7f360dfaf8b1..d8ae5d997615 100644 --- a/sound/soc/tegra/tegra186_asrc.c +++ b/sound/soc/tegra/tegra186_asrc.c @@ -828,7 +828,7 @@ static const struct snd_kcontrol_new tegra186_asrc_controls[] = { tegra186_asrc_put_input_threshold), SOC_SINGLE_EXT("Stream6 Input Threshold", - ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 4), 0, 3, 0, + ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 5), 0, 3, 0, tegra186_asrc_get_input_threshold, tegra186_asrc_put_input_threshold), diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 3d821fde4582..f604846ec6e6 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -562,6 +562,9 @@ static int snd_probe(struct usb_interface *intf, ret = init_card(caiaqdev(card)); if (ret < 0) { dev_err(&usb_dev->dev, "unable to init card! (ret=%d)\n", ret); +#ifdef CONFIG_SND_USB_CAIAQ_INPUT + snd_usb_caiaq_input_disconnect(caiaqdev(card)); +#endif snd_card_free(card); return ret; } diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c index 7ebaf125f969..aa6ddf8746a0 100644 --- a/sound/usb/line6/playback.c +++ b/sound/usb/line6/playback.c @@ -182,6 +182,13 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) fsize *= bytes_per_frame; + if (fsize > line6pcm->max_packet_size_out) { + dev_err(line6pcm->line6->ifcdev, + "playback packet too large: %d > %d\n", + fsize, line6pcm->max_packet_size_out); + return -EMSGSIZE; + } + fout->offset = urb_size; fout->length = fsize; urb_size += fsize; diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index b9a62e94e06c..2f0f4233380c 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -1002,6 +1002,15 @@ static int detect_usb_format(struct ua101 *ua) } ua->playback.usb_pipe = usb_sndisocpipe(ua->dev, usb_endpoint_num(epd)); ua->playback.max_packet_bytes = usb_endpoint_maxp(epd); + + if (ua->capture.max_packet_bytes / ua->capture.frame_bytes * + ua->playback.frame_bytes > ua->playback.max_packet_bytes) { + dev_err(&ua->dev->dev, + "playback packet size %u too small for %u capture frames\n", + ua->playback.max_packet_bytes, + ua->capture.max_packet_bytes / ua->capture.frame_bytes); + return -ENXIO; + } return 0; } diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 4d2957e62fbd..9ab79172fd4a 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -849,6 +849,27 @@ static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev) return 0; } +/* + * A post configuration device descriptor read is needed to make the CM1A + * operational after reenumeration. + */ +static int snd_usb_cm1a_boot_quirk(struct usb_device *dev) +{ + struct usb_device_descriptor *desc __free(kfree) = kmalloc_obj(*desc); + int err; + + if (!desc) + return -ENOMEM; + + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, sizeof(*desc)); + if (err < 0) { + dev_err(&dev->dev, "failed to read device descriptor: %d\n", err); + return err; + } + + return 0; +} + /* * Some sound cards from Native Instruments are in fact compliant to the USB * audio standard of version 2 and other approved USB standards, even though @@ -1683,6 +1704,10 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev, return snd_usb_motu_m_series_boot_quirk(dev); } + /* Behringer devices may need explicit device descriptor read at boot */ + if (USB_ID_VENDOR(id) == 0x1397) + return snd_usb_cm1a_boot_quirk(dev); + return 0; } @@ -2478,8 +2503,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x262a, 0x9302, /* ddHiFi TC44C */ QUIRK_FLAG_DSD_RAW), - DEVICE_FLG(0x2708, 0x0002, /* Audient iD14 */ - QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x2772, 0x0502, /* Musical Fidelity M6s DAC */ 0), /* for avoiding QUIRK_FLAG_DSD_RAW with vendor match */ DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */ @@ -2576,6 +2599,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_DSD_RAW), VENDOR_FLG(0x2622, /* IAG Limited devices */ QUIRK_FLAG_DSD_RAW), + VENDOR_FLG(0x2708, /* Audient devices */ + QUIRK_FLAG_IGNORE_CTL_ERROR), VENDOR_FLG(0x2772, /* Musical Fidelity devices */ QUIRK_FLAG_DSD_RAW), VENDOR_FLG(0x278b, /* Rotel? */ diff --git a/tools/sched_ext/include/scx/common.bpf.h b/tools/sched_ext/include/scx/common.bpf.h index 9591a6e778ce..7b19e76ebb52 100644 --- a/tools/sched_ext/include/scx/common.bpf.h +++ b/tools/sched_ext/include/scx/common.bpf.h @@ -104,8 +104,8 @@ u64 scx_bpf_now(void) __ksym __weak; void scx_bpf_events(struct scx_event_stats *events, size_t events__sz) __ksym __weak; s32 scx_bpf_cpu_to_cid(s32 cpu) __ksym __weak; s32 scx_bpf_cid_to_cpu(s32 cid) __ksym __weak; -void scx_bpf_cid_topo(s32 cid, struct scx_cid_topo *out) __ksym __weak; -s32 scx_bpf_kick_cid(s32 cid, u64 flags) __ksym __weak; +void scx_bpf_cid_topo(s32 cid, struct scx_cid_topo *out, size_t out__sz) __ksym __weak; +void scx_bpf_kick_cid(s32 cid, u64 flags) __ksym __weak; s32 scx_bpf_task_cid(const struct task_struct *p) __ksym __weak; s32 scx_bpf_this_cid(void) __ksym __weak; struct task_struct *scx_bpf_cid_curr(s32 cid) __ksym __weak; diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index 6e7b89315bbf..eb0933ace4ac 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -20,6 +20,7 @@ static int page_size; #define PATH_ZSWAP "/sys/module/zswap" #define PATH_ZSWAP_ENABLED "/sys/module/zswap/parameters/enabled" +#define PATH_ZSWAP_SHRINKER_ENABLED "/sys/module/zswap/parameters/shrinker_enabled" static int read_int(const char *path, size_t *value) { @@ -443,6 +444,16 @@ static int test_zswap_writeback_disabled(const char *root) return test_zswap_writeback(root, false); } +static bool zswap_shrinker_enabled(void) +{ + char value[2]; + + if (read_text(PATH_ZSWAP_SHRINKER_ENABLED, value, sizeof(value)) <= 0) + return 0; + + return value[0] == 'Y'; +} + /* * When trying to store a memcg page in zswap, if the memcg hits its memory * limit in zswap, writeback should affect only the zswapped pages of that @@ -452,6 +463,7 @@ static int test_no_invasive_cgroup_shrink(const char *root) { int ret = KSFT_FAIL; unsigned int off; + long zswpwb_before, zswpwb_after, zswpwb_target; size_t allocation_size = page_size * 1024; unsigned int nr_pages = allocation_size / page_size; char zswap_max_buf[32], mem_max_buf[32]; @@ -487,6 +499,14 @@ static int test_no_invasive_cgroup_shrink(const char *root) if (cg_read_key_long(zw_group, "memory.stat", "zswapped") < 1) goto out; + /* If the shrinker is enabled, try to let the writebacks finish first */ + if (zswap_shrinker_enabled()) + sleep(5); + + zswpwb_before = get_cg_wb_count(zw_group); + if (zswpwb_before < 0) + goto out; + /* Push wb_group memory into zswap with hard-to-compress data to trigger wb */ if (cg_enter_current(wb_group)) goto out; @@ -499,9 +519,13 @@ static int test_no_invasive_cgroup_shrink(const char *root) getrandom(&wb_allocation[off], page_size/4, 0); } - /* Verify that only zswapped memory from gwb_group has been written back */ - if (wait_for_writeback(wb_group, 5000) > 0 && get_cg_wb_count(zw_group) == 0) + /* Verify that only zswapped memory from wb_group has been written back */ + zswpwb_target = wait_for_writeback(wb_group, 5000); + zswpwb_after = get_cg_wb_count(zw_group); + + if (zswpwb_target > 0 && zswpwb_before == zswpwb_after) ret = KSFT_PASS; + out: cg_enter_current(root); if (zw_group) {