From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: linux-kernel@vger.kernel.org, akpm@linux-foundation.org,
torvalds@linux-foundation.org, stable@vger.kernel.org
Cc: lwn@lwn.net, jslaby@suse.cz,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Subject: Re: Linux 6.19.12
Date: Sat, 11 Apr 2026 14:54:00 +0200 [thread overview]
Message-ID: <2026041100-wackiness-broadly-b54d@gregkh> (raw)
In-Reply-To: <2026041100-stick-reshape-6754@gregkh>
diff --git a/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml b/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml
index b90eec2077b4..fe1272e86467 100644
--- a/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml
+++ b/Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml
@@ -66,7 +66,7 @@ then:
required:
- refresh-rate-hz
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml
index 11e40d225b9f..d97b29e49bf5 100644
--- a/Documentation/devicetree/bindings/connector/usb-connector.yaml
+++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml
@@ -301,6 +301,7 @@ properties:
maxItems: 4
dependencies:
+ pd-disable: [typec-power-opmode]
sink-vdos-v1: [ sink-vdos ]
sink-vdos: [ sink-vdos-v1 ]
diff --git a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml
index 184432d24ea1..f42c54653d52 100644
--- a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml
+++ b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml
@@ -37,7 +37,7 @@ properties:
const: 2
"#interrupt-cells":
- const: 1
+ const: 2
ngpios:
description:
@@ -86,7 +86,7 @@ examples:
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
interrupts = <53>, <53>, <53>, <53>,
<53>, <53>, <53>, <53>,
<53>, <53>, <53>, <53>,
diff --git a/Makefile b/Makefile
index 4043ecae1408..05978903b747 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 6
PATCHLEVEL = 19
-SUBLEVEL = 11
+SUBLEVEL = 12
EXTRAVERSION =
NAME = Baby Opossum Posse
diff --git a/arch/arm64/kernel/pi/patch-scs.c b/arch/arm64/kernel/pi/patch-scs.c
index bbe7d30ed12b..dac568e4a54f 100644
--- a/arch/arm64/kernel/pi/patch-scs.c
+++ b/arch/arm64/kernel/pi/patch-scs.c
@@ -192,6 +192,14 @@ static int scs_handle_fde_frame(const struct eh_frame *frame,
size -= 2;
break;
+ case DW_CFA_advance_loc4:
+ loc += *opcode++ * code_alignment_factor;
+ loc += (*opcode++ << 8) * code_alignment_factor;
+ loc += (*opcode++ << 16) * code_alignment_factor;
+ loc += (*opcode++ << 24) * code_alignment_factor;
+ size -= 4;
+ break;
+
case DW_CFA_def_cfa:
case DW_CFA_offset_extended:
size = skip_xleb128(&opcode, size);
diff --git a/arch/mips/lib/multi3.c b/arch/mips/lib/multi3.c
index 4c2483f410c2..92b3778bb56f 100644
--- a/arch/mips/lib/multi3.c
+++ b/arch/mips/lib/multi3.c
@@ -4,12 +4,12 @@
#include "libgcc.h"
/*
- * GCC 7 & older can suboptimally generate __multi3 calls for mips64r6, so for
+ * GCC 9 & older can suboptimally generate __multi3 calls for mips64r6, so for
* that specific case only we implement that intrinsic here.
*
* See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82981
*/
-#if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) && (__GNUC__ < 8)
+#if defined(CONFIG_64BIT) && defined(CONFIG_CPU_MIPSR6) && (__GNUC__ < 10)
/* multiply 64-bit values, low 64-bits returned */
static inline long long notrace dmulu(long long a, long long b)
@@ -51,4 +51,4 @@ ti_type notrace __multi3(ti_type a, ti_type b)
}
EXPORT_SYMBOL(__multi3);
-#endif /* 64BIT && CPU_MIPSR6 && GCC7 */
+#endif /* 64BIT && CPU_MIPSR6 && GCC9 */
diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
index e3b4224c9a40..ad9b0430a28e 100644
--- a/arch/mips/mm/cache.c
+++ b/arch/mips/mm/cache.c
@@ -207,7 +207,8 @@ void cpu_cache_init(void)
{
if (IS_ENABLED(CONFIG_CPU_R3000) && cpu_has_3k_cache)
r3k_cache_init();
- if (IS_ENABLED(CONFIG_CPU_R4K_CACHE_TLB) && cpu_has_4k_cache)
+ if ((IS_ENABLED(CONFIG_CPU_R4K_CACHE_TLB) ||
+ IS_ENABLED(CONFIG_CPU_SB1)) && cpu_has_4k_cache)
r4k_cache_init();
if (IS_ENABLED(CONFIG_CPU_CAVIUM_OCTEON) && cpu_has_octeon_cache)
diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c
index 44a662536148..645f77e09d5b 100644
--- a/arch/mips/mm/tlb-r4k.c
+++ b/arch/mips/mm/tlb-r4k.c
@@ -538,7 +538,7 @@ static void __ref r4k_tlb_uniquify(void)
tlb_vpn_size = tlbsize * sizeof(*tlb_vpns);
tlb_vpns = (use_slab ?
- kmalloc(tlb_vpn_size, GFP_KERNEL) :
+ kmalloc(tlb_vpn_size, GFP_ATOMIC) :
memblock_alloc_raw(tlb_vpn_size, sizeof(*tlb_vpns)));
if (WARN_ON(!tlb_vpns))
return; /* Pray local_flush_tlb_all() is good enough. */
diff --git a/arch/mips/ralink/clk.c b/arch/mips/ralink/clk.c
index 9db73fcac522..5c1eb46ef5d0 100644
--- a/arch/mips/ralink/clk.c
+++ b/arch/mips/ralink/clk.c
@@ -21,16 +21,16 @@ static const char *clk_cpu(int *idx)
{
switch (ralink_soc) {
case RT2880_SOC:
- *idx = 0;
+ *idx = 1;
return "ralink,rt2880-sysc";
case RT3883_SOC:
- *idx = 0;
+ *idx = 1;
return "ralink,rt3883-sysc";
case RT305X_SOC_RT3050:
- *idx = 0;
+ *idx = 1;
return "ralink,rt3050-sysc";
case RT305X_SOC_RT3052:
- *idx = 0;
+ *idx = 1;
return "ralink,rt3052-sysc";
case RT305X_SOC_RT3350:
*idx = 1;
diff --git a/arch/riscv/kernel/kgdb.c b/arch/riscv/kernel/kgdb.c
index 15fec5d1e6de..0bf629204c76 100644
--- a/arch/riscv/kernel/kgdb.c
+++ b/arch/riscv/kernel/kgdb.c
@@ -175,7 +175,7 @@ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
{DBG_REG_T1, GDB_SIZEOF_REG, offsetof(struct pt_regs, t1)},
{DBG_REG_T2, GDB_SIZEOF_REG, offsetof(struct pt_regs, t2)},
{DBG_REG_FP, GDB_SIZEOF_REG, offsetof(struct pt_regs, s0)},
- {DBG_REG_S1, GDB_SIZEOF_REG, offsetof(struct pt_regs, a1)},
+ {DBG_REG_S1, GDB_SIZEOF_REG, offsetof(struct pt_regs, s1)},
{DBG_REG_A0, GDB_SIZEOF_REG, offsetof(struct pt_regs, a0)},
{DBG_REG_A1, GDB_SIZEOF_REG, offsetof(struct pt_regs, a1)},
{DBG_REG_A2, GDB_SIZEOF_REG, offsetof(struct pt_regs, a2)},
@@ -244,8 +244,9 @@ sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task)
gdb_regs[DBG_REG_S6_OFF] = task->thread.s[6];
gdb_regs[DBG_REG_S7_OFF] = task->thread.s[7];
gdb_regs[DBG_REG_S8_OFF] = task->thread.s[8];
- gdb_regs[DBG_REG_S9_OFF] = task->thread.s[10];
- gdb_regs[DBG_REG_S10_OFF] = task->thread.s[11];
+ gdb_regs[DBG_REG_S9_OFF] = task->thread.s[9];
+ gdb_regs[DBG_REG_S10_OFF] = task->thread.s[10];
+ gdb_regs[DBG_REG_S11_OFF] = task->thread.s[11];
gdb_regs[DBG_REG_EPC_OFF] = task->thread.ra;
}
diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c
index 31a392993cb4..b5188dc74727 100644
--- a/arch/riscv/kernel/process.c
+++ b/arch/riscv/kernel/process.c
@@ -324,8 +324,10 @@ long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg)
if (arg & PR_TAGGED_ADDR_ENABLE && (tagged_addr_disabled || !pmlen))
return -EINVAL;
- if (!(arg & PR_TAGGED_ADDR_ENABLE))
+ if (!(arg & PR_TAGGED_ADDR_ENABLE)) {
pmlen = PMLEN_0;
+ pmm = ENVCFG_PMM_PMLEN_0;
+ }
if (mmap_write_lock_killable(mm))
return -EINTR;
diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c
index e8bd19ac82c7..26926cb077ae 100644
--- a/arch/s390/kernel/perf_cpum_sf.c
+++ b/arch/s390/kernel/perf_cpum_sf.c
@@ -1168,6 +1168,7 @@ static void hw_collect_samples(struct perf_event *event, unsigned long *sdbt,
static void hw_perf_event_update(struct perf_event *event, int flush_all)
{
unsigned long long event_overflow, sampl_overflow, num_sdb;
+ struct cpu_hw_sf *cpuhw = this_cpu_ptr(&cpu_hw_sf);
struct hw_perf_event *hwc = &event->hw;
union hws_trailer_header prev, new;
struct hws_trailer_entry *te;
@@ -1247,8 +1248,11 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all)
* are dropped.
* Slightly increase the interval to avoid hitting this limit.
*/
- if (event_overflow)
+ if (event_overflow) {
SAMPL_RATE(hwc) += DIV_ROUND_UP(SAMPL_RATE(hwc), 10);
+ if (SAMPL_RATE(hwc) > cpuhw->qsi.max_sampl_rate)
+ SAMPL_RATE(hwc) = cpuhw->qsi.max_sampl_rate;
+ }
}
static inline unsigned long aux_sdb_index(struct aux_buffer *aux,
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 20f078ceb51d..bebaac1dbaeb 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -4594,8 +4594,10 @@ static int intel_pmu_hw_config(struct perf_event *event)
intel_pmu_set_acr_caused_constr(leader, idx++, cause_mask);
if (leader->nr_siblings) {
- for_each_sibling_event(sibling, leader)
- intel_pmu_set_acr_caused_constr(sibling, idx++, cause_mask);
+ for_each_sibling_event(sibling, leader) {
+ if (is_x86_event(sibling))
+ intel_pmu_set_acr_caused_constr(sibling, idx++, cause_mask);
+ }
}
if (leader != event)
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index bc184dd38d99..558b96d53e00 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -44,6 +44,20 @@ KCOV_INSTRUMENT_unwind_orc.o := n
KCOV_INSTRUMENT_unwind_frame.o := n
KCOV_INSTRUMENT_unwind_guess.o := n
+# Disable KCOV to prevent crashes during kexec: load_segments() invalidates
+# the GS base, which KCOV relies on for per-CPU data.
+#
+# As KCOV and KEXEC compatibility should be preserved (e.g. syzkaller is
+# using it to collect crash dumps during kernel fuzzing), disabling
+# KCOV for KEXEC kernels is not an option. Selectively disabling KCOV
+# instrumentation for individual affected functions can be fragile, while
+# adding more checks to KCOV would slow it down.
+#
+# As a compromise solution, disable KCOV instrumentation for the whole
+# source code file. If its coverage is ever needed, other approaches
+# should be considered.
+KCOV_INSTRUMENT_machine_kexec_64.o := n
+
CFLAGS_head32.o := -fno-stack-protector
CFLAGS_head64.o := -fno-stack-protector
CFLAGS_irq.o := -I $(src)/../include/asm/trace
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile
index 5b9908f13dcf..3a5364853eab 100644
--- a/arch/x86/mm/Makefile
+++ b/arch/x86/mm/Makefile
@@ -4,6 +4,8 @@ KCOV_INSTRUMENT_tlb.o := n
KCOV_INSTRUMENT_mem_encrypt.o := n
KCOV_INSTRUMENT_mem_encrypt_amd.o := n
KCOV_INSTRUMENT_pgprot.o := n
+# See the "Disable KCOV" comment in arch/x86/kernel/Makefile.
+KCOV_INSTRUMENT_physaddr.o := n
KASAN_SANITIZE_mem_encrypt.o := n
KASAN_SANITIZE_mem_encrypt_amd.o := n
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
index e468714f539d..bc78c915eabc 100644
--- a/crypto/af_alg.c
+++ b/crypto/af_alg.c
@@ -623,8 +623,10 @@ static int af_alg_alloc_tsgl(struct sock *sk)
sg_init_table(sgl->sg, MAX_SGL_ENTS + 1);
sgl->cur = 0;
- if (sg)
+ if (sg) {
+ sg_unmark_end(sg + MAX_SGL_ENTS - 1);
sg_chain(sg, MAX_SGL_ENTS + 1, sgl->sg);
+ }
list_add_tail(&sgl->list, &ctx->tsgl_list);
}
@@ -635,15 +637,13 @@ static int af_alg_alloc_tsgl(struct sock *sk)
/**
* af_alg_count_tsgl - Count number of TX SG entries
*
- * The counting starts from the beginning of the SGL to @bytes. If
- * an @offset is provided, the counting of the SG entries starts at the @offset.
+ * The counting starts from the beginning of the SGL to @bytes.
*
* @sk: socket of connection to user space
* @bytes: Count the number of SG entries holding given number of bytes.
- * @offset: Start the counting of SG entries from the given offset.
* Return: Number of TX SG entries found given the constraints
*/
-unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes, size_t offset)
+unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes)
{
const struct alg_sock *ask = alg_sk(sk);
const struct af_alg_ctx *ctx = ask->private;
@@ -658,25 +658,11 @@ unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes, size_t offset)
const struct scatterlist *sg = sgl->sg;
for (i = 0; i < sgl->cur; i++) {
- size_t bytes_count;
-
- /* Skip offset */
- if (offset >= sg[i].length) {
- offset -= sg[i].length;
- bytes -= sg[i].length;
- continue;
- }
-
- bytes_count = sg[i].length - offset;
-
- offset = 0;
sgl_count++;
-
- /* If we have seen requested number of bytes, stop */
- if (bytes_count >= bytes)
+ if (sg[i].length >= bytes)
return sgl_count;
- bytes -= bytes_count;
+ bytes -= sg[i].length;
}
}
@@ -688,19 +674,14 @@ EXPORT_SYMBOL_GPL(af_alg_count_tsgl);
* af_alg_pull_tsgl - Release the specified buffers from TX SGL
*
* If @dst is non-null, reassign the pages to @dst. The caller must release
- * the pages. If @dst_offset is given only reassign the pages to @dst starting
- * at the @dst_offset (byte). The caller must ensure that @dst is large
- * enough (e.g. by using af_alg_count_tsgl with the same offset).
+ * the pages.
*
* @sk: socket of connection to user space
* @used: Number of bytes to pull from TX SGL
* @dst: If non-NULL, buffer is reassigned to dst SGL instead of releasing. The
* caller must release the buffers in dst.
- * @dst_offset: Reassign the TX SGL from given offset. All buffers before
- * reaching the offset is released.
*/
-void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst,
- size_t dst_offset)
+void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst)
{
struct alg_sock *ask = alg_sk(sk);
struct af_alg_ctx *ctx = ask->private;
@@ -725,18 +706,10 @@ void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst,
* SG entries in dst.
*/
if (dst) {
- if (dst_offset >= plen) {
- /* discard page before offset */
- dst_offset -= plen;
- } else {
- /* reassign page to dst after offset */
- get_page(page);
- sg_set_page(dst + j, page,
- plen - dst_offset,
- sg[i].offset + dst_offset);
- dst_offset = 0;
- j++;
- }
+ /* reassign page to dst after offset */
+ get_page(page);
+ sg_set_page(dst + j, page, plen, sg[i].offset);
+ j++;
}
sg[i].length -= plen;
diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c
index 79b016a899a1..dda15bb05e89 100644
--- a/crypto/algif_aead.c
+++ b/crypto/algif_aead.c
@@ -26,7 +26,6 @@
#include <crypto/internal/aead.h>
#include <crypto/scatterwalk.h>
#include <crypto/if_alg.h>
-#include <crypto/skcipher.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/kernel.h>
@@ -72,9 +71,8 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
struct alg_sock *pask = alg_sk(psk);
struct af_alg_ctx *ctx = ask->private;
struct crypto_aead *tfm = pask->private;
- unsigned int i, as = crypto_aead_authsize(tfm);
+ unsigned int as = crypto_aead_authsize(tfm);
struct af_alg_async_req *areq;
- struct af_alg_tsgl *tsgl, *tmp;
struct scatterlist *rsgl_src, *tsgl_src = NULL;
int err = 0;
size_t used = 0; /* [in] TX bufs to be en/decrypted */
@@ -154,23 +152,24 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
outlen -= less;
}
+ /*
+ * Create a per request TX SGL for this request which tracks the
+ * SG entries from the global TX SGL.
+ */
processed = used + ctx->aead_assoclen;
- list_for_each_entry_safe(tsgl, tmp, &ctx->tsgl_list, list) {
- for (i = 0; i < tsgl->cur; i++) {
- struct scatterlist *process_sg = tsgl->sg + i;
-
- if (!(process_sg->length) || !sg_page(process_sg))
- continue;
- tsgl_src = process_sg;
- break;
- }
- if (tsgl_src)
- break;
- }
- if (processed && !tsgl_src) {
- err = -EFAULT;
+ areq->tsgl_entries = af_alg_count_tsgl(sk, processed);
+ if (!areq->tsgl_entries)
+ areq->tsgl_entries = 1;
+ areq->tsgl = sock_kmalloc(sk, array_size(sizeof(*areq->tsgl),
+ areq->tsgl_entries),
+ GFP_KERNEL);
+ if (!areq->tsgl) {
+ err = -ENOMEM;
goto free;
}
+ sg_init_table(areq->tsgl, areq->tsgl_entries);
+ af_alg_pull_tsgl(sk, processed, areq->tsgl);
+ tsgl_src = areq->tsgl;
/*
* Copy of AAD from source to destination
@@ -179,76 +178,15 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg,
* when user space uses an in-place cipher operation, the kernel
* will copy the data as it does not see whether such in-place operation
* is initiated.
- *
- * To ensure efficiency, the following implementation ensure that the
- * ciphers are invoked to perform a crypto operation in-place. This
- * is achieved by memory management specified as follows.
*/
/* Use the RX SGL as source (and destination) for crypto op. */
rsgl_src = areq->first_rsgl.sgl.sgt.sgl;
- if (ctx->enc) {
- /*
- * Encryption operation - The in-place cipher operation is
- * achieved by the following operation:
- *
- * TX SGL: AAD || PT
- * | |
- * | copy |
- * v v
- * RX SGL: AAD || PT || Tag
- */
- memcpy_sglist(areq->first_rsgl.sgl.sgt.sgl, tsgl_src,
- processed);
- af_alg_pull_tsgl(sk, processed, NULL, 0);
- } else {
- /*
- * Decryption operation - To achieve an in-place cipher
- * operation, the following SGL structure is used:
- *
- * TX SGL: AAD || CT || Tag
- * | | ^
- * | copy | | Create SGL link.
- * v v |
- * RX SGL: AAD || CT ----+
- */
-
- /* Copy AAD || CT to RX SGL buffer for in-place operation. */
- memcpy_sglist(areq->first_rsgl.sgl.sgt.sgl, tsgl_src, outlen);
-
- /* Create TX SGL for tag and chain it to RX SGL. */
- areq->tsgl_entries = af_alg_count_tsgl(sk, processed,
- processed - as);
- if (!areq->tsgl_entries)
- areq->tsgl_entries = 1;
- areq->tsgl = sock_kmalloc(sk, array_size(sizeof(*areq->tsgl),
- areq->tsgl_entries),
- GFP_KERNEL);
- if (!areq->tsgl) {
- err = -ENOMEM;
- goto free;
- }
- sg_init_table(areq->tsgl, areq->tsgl_entries);
-
- /* Release TX SGL, except for tag data and reassign tag data. */
- af_alg_pull_tsgl(sk, processed, areq->tsgl, processed - as);
-
- /* chain the areq TX SGL holding the tag with RX SGL */
- if (usedpages) {
- /* RX SGL present */
- struct af_alg_sgl *sgl_prev = &areq->last_rsgl->sgl;
- struct scatterlist *sg = sgl_prev->sgt.sgl;
-
- sg_unmark_end(sg + sgl_prev->sgt.nents - 1);
- sg_chain(sg, sgl_prev->sgt.nents + 1, areq->tsgl);
- } else
- /* no RX SGL present (e.g. authentication only) */
- rsgl_src = areq->tsgl;
- }
+ memcpy_sglist(rsgl_src, tsgl_src, ctx->aead_assoclen);
/* Initialize the crypto operation */
- aead_request_set_crypt(&areq->cra_u.aead_req, rsgl_src,
+ aead_request_set_crypt(&areq->cra_u.aead_req, tsgl_src,
areq->first_rsgl.sgl.sgt.sgl, used, ctx->iv);
aead_request_set_ad(&areq->cra_u.aead_req, ctx->aead_assoclen);
aead_request_set_tfm(&areq->cra_u.aead_req, tfm);
@@ -450,7 +388,7 @@ static void aead_sock_destruct(struct sock *sk)
struct crypto_aead *tfm = pask->private;
unsigned int ivlen = crypto_aead_ivsize(tfm);
- af_alg_pull_tsgl(sk, ctx->used, NULL, 0);
+ af_alg_pull_tsgl(sk, ctx->used, NULL);
sock_kzfree_s(sk, ctx->iv, ivlen);
sock_kfree_s(sk, ctx, ctx->len);
af_alg_release_parent(sk);
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index 125d395c5e00..82735e51be10 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -138,7 +138,7 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg,
* Create a per request TX SGL for this request which tracks the
* SG entries from the global TX SGL.
*/
- areq->tsgl_entries = af_alg_count_tsgl(sk, len, 0);
+ areq->tsgl_entries = af_alg_count_tsgl(sk, len);
if (!areq->tsgl_entries)
areq->tsgl_entries = 1;
areq->tsgl = sock_kmalloc(sk, array_size(sizeof(*areq->tsgl),
@@ -149,7 +149,7 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg,
goto free;
}
sg_init_table(areq->tsgl, areq->tsgl_entries);
- af_alg_pull_tsgl(sk, len, areq->tsgl, 0);
+ af_alg_pull_tsgl(sk, len, areq->tsgl);
/* Initialize the crypto operation */
skcipher_request_set_tfm(&areq->cra_u.skcipher_req, tfm);
@@ -363,7 +363,7 @@ static void skcipher_sock_destruct(struct sock *sk)
struct alg_sock *pask = alg_sk(psk);
struct crypto_skcipher *tfm = pask->private;
- af_alg_pull_tsgl(sk, ctx->used, NULL, 0);
+ af_alg_pull_tsgl(sk, ctx->used, NULL);
sock_kzfree_s(sk, ctx->iv, crypto_skcipher_ivsize(tfm));
if (ctx->state)
sock_kzfree_s(sk, ctx->state, crypto_skcipher_statesize(tfm));
diff --git a/crypto/authencesn.c b/crypto/authencesn.c
index 542a978663b9..c0a01d738d9b 100644
--- a/crypto/authencesn.c
+++ b/crypto/authencesn.c
@@ -207,6 +207,7 @@ static int crypto_authenc_esn_decrypt_tail(struct aead_request *req,
u8 *ohash = areq_ctx->tail;
unsigned int cryptlen = req->cryptlen - authsize;
unsigned int assoclen = req->assoclen;
+ struct scatterlist *src = req->src;
struct scatterlist *dst = req->dst;
u8 *ihash = ohash + crypto_ahash_digestsize(auth);
u32 tmp[2];
@@ -214,23 +215,27 @@ static int crypto_authenc_esn_decrypt_tail(struct aead_request *req,
if (!authsize)
goto decrypt;
- /* Move high-order bits of sequence number back. */
- scatterwalk_map_and_copy(tmp, dst, 4, 4, 0);
- scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 0);
- scatterwalk_map_and_copy(tmp, dst, 0, 8, 1);
+ if (src == dst) {
+ /* Move high-order bits of sequence number back. */
+ scatterwalk_map_and_copy(tmp, dst, 4, 4, 0);
+ scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 0);
+ scatterwalk_map_and_copy(tmp, dst, 0, 8, 1);
+ } else
+ memcpy_sglist(dst, src, assoclen);
if (crypto_memneq(ihash, ohash, authsize))
return -EBADMSG;
decrypt:
- sg_init_table(areq_ctx->dst, 2);
+ if (src != dst)
+ src = scatterwalk_ffwd(areq_ctx->src, src, assoclen);
dst = scatterwalk_ffwd(areq_ctx->dst, dst, assoclen);
skcipher_request_set_tfm(skreq, ctx->enc);
skcipher_request_set_callback(skreq, flags,
req->base.complete, req->base.data);
- skcipher_request_set_crypt(skreq, dst, dst, cryptlen, req->iv);
+ skcipher_request_set_crypt(skreq, src, dst, cryptlen, req->iv);
return crypto_skcipher_decrypt(skreq);
}
@@ -255,6 +260,7 @@ static int crypto_authenc_esn_decrypt(struct aead_request *req)
unsigned int assoclen = req->assoclen;
unsigned int cryptlen = req->cryptlen;
u8 *ihash = ohash + crypto_ahash_digestsize(auth);
+ struct scatterlist *src = req->src;
struct scatterlist *dst = req->dst;
u32 tmp[2];
int err;
@@ -262,24 +268,28 @@ static int crypto_authenc_esn_decrypt(struct aead_request *req)
if (assoclen < 8)
return -EINVAL;
- cryptlen -= authsize;
-
- if (req->src != dst)
- memcpy_sglist(dst, req->src, assoclen + cryptlen);
+ if (!authsize)
+ goto tail;
+ cryptlen -= authsize;
scatterwalk_map_and_copy(ihash, req->src, assoclen + cryptlen,
authsize, 0);
- if (!authsize)
- goto tail;
-
/* Move high-order bits of sequence number to the end. */
- scatterwalk_map_and_copy(tmp, dst, 0, 8, 0);
- scatterwalk_map_and_copy(tmp, dst, 4, 4, 1);
- scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);
-
- sg_init_table(areq_ctx->dst, 2);
- dst = scatterwalk_ffwd(areq_ctx->dst, dst, 4);
+ scatterwalk_map_and_copy(tmp, src, 0, 8, 0);
+ if (src == dst) {
+ scatterwalk_map_and_copy(tmp, dst, 4, 4, 1);
+ scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);
+ dst = scatterwalk_ffwd(areq_ctx->dst, dst, 4);
+ } else {
+ scatterwalk_map_and_copy(tmp, dst, 0, 4, 1);
+ scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen - 4, 4, 1);
+
+ src = scatterwalk_ffwd(areq_ctx->src, src, 8);
+ dst = scatterwalk_ffwd(areq_ctx->dst, dst, 4);
+ memcpy_sglist(dst, src, assoclen + cryptlen - 8);
+ dst = req->dst;
+ }
ahash_request_set_tfm(ahreq, auth);
ahash_request_set_crypt(ahreq, dst, ohash, assoclen + cryptlen);
diff --git a/crypto/deflate.c b/crypto/deflate.c
index a3e1fff55661..8df17e7880c9 100644
--- a/crypto/deflate.c
+++ b/crypto/deflate.c
@@ -164,18 +164,21 @@ static int deflate_decompress_one(struct acomp_req *req,
do {
unsigned int dcur;
+ unsigned long avail_in;
dcur = acomp_walk_next_dst(&walk);
- if (!dcur) {
- out_of_space = true;
- break;
- }
stream->avail_out = dcur;
stream->next_out = walk.dst.virt.addr;
+ avail_in = stream->avail_in;
ret = zlib_inflate(stream, Z_NO_FLUSH);
+ if (!dcur && avail_in == stream->avail_in) {
+ out_of_space = true;
+ break;
+ }
+
dcur -= stream->avail_out;
acomp_walk_done_dst(&walk, dcur);
} while (ret == Z_OK && stream->avail_in);
diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c
index 428d8f65bff3..3842e59291b9 100644
--- a/drivers/accel/qaic/qaic_control.c
+++ b/drivers/accel/qaic/qaic_control.c
@@ -913,7 +913,7 @@ static int decode_deactivate(struct qaic_device *qdev, void *trans, u32 *msg_len
*/
return -ENODEV;
- if (status) {
+ if (usr && status) {
/*
* Releasing resources failed on the device side, which puts
* us in a bind since they may still be in use, so enable the
@@ -1108,6 +1108,9 @@ static void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u
mutex_lock(&qdev->cntl_mutex);
if (!list_empty(&elem.list))
list_del(&elem.list);
+ /* resp_worker() processed the response but the wait was interrupted */
+ else if (ret == -ERESTARTSYS)
+ ret = 0;
if (!ret && !elem.buf)
ret = -ETIMEDOUT;
else if (ret > 0 && !elem.buf)
@@ -1418,9 +1421,49 @@ static void resp_worker(struct work_struct *work)
}
mutex_unlock(&qdev->cntl_mutex);
- if (!found)
+ if (!found) {
+ /*
+ * The user might have gone away at this point without waiting
+ * for QAIC_TRANS_DEACTIVATE_FROM_DEV transaction coming from
+ * the device. If this is not handled correctly, the host will
+ * not know that the DBC[n] has been freed on the device.
+ * Due to this failure in synchronization between the device and
+ * the host, if another user requests to activate a network, and
+ * the device assigns DBC[n] again, save_dbc_buf() will hang,
+ * waiting for dbc[n]->in_use to be set to false, which will not
+ * happen unless the qaic_dev_reset_clean_local_state() gets
+ * called by resetting the device (or re-inserting the module).
+ *
+ * As a solution, we look for QAIC_TRANS_DEACTIVATE_FROM_DEV
+ * transactions in the message before disposing of it, then
+ * handle releasing the DBC resources.
+ *
+ * Since the user has gone away, if the device could not
+ * deactivate the network (status != 0), there is no way to
+ * enable and reassign the DBC to the user. We can put trust in
+ * the device that it will release all the active DBCs in
+ * response to the QAIC_TRANS_TERMINATE_TO_DEV transaction,
+ * otherwise, the user can issue an soc_reset to the device.
+ */
+ u32 msg_count = le32_to_cpu(msg->hdr.count);
+ u32 msg_len = le32_to_cpu(msg->hdr.len);
+ u32 len = 0;
+ int j;
+
+ for (j = 0; j < msg_count && len < msg_len; ++j) {
+ struct wire_trans_hdr *trans_hdr;
+
+ trans_hdr = (struct wire_trans_hdr *)(msg->data + len);
+ if (le32_to_cpu(trans_hdr->type) == QAIC_TRANS_DEACTIVATE_FROM_DEV) {
+ if (decode_deactivate(qdev, trans_hdr, &len, NULL))
+ len += le32_to_cpu(trans_hdr->len);
+ } else {
+ len += le32_to_cpu(trans_hdr->len);
+ }
+ }
/* request must have timed out, drop packet */
kfree(msg);
+ }
kfree(resp);
}
diff --git a/drivers/acpi/riscv/rimt.c b/drivers/acpi/riscv/rimt.c
index 7f423405e5ef..8eaa8731bddd 100644
--- a/drivers/acpi/riscv/rimt.c
+++ b/drivers/acpi/riscv/rimt.c
@@ -263,6 +263,13 @@ static int rimt_iommu_xlate(struct device *dev, struct acpi_rimt_node *node, u32
if (!rimt_fwnode)
return -EPROBE_DEFER;
+ /*
+ * EPROBE_DEFER ensures IOMMU is probed before the devices that
+ * depend on them. During shutdown, however, the IOMMU may be removed
+ * first, leading to issues. To avoid this, a device link is added
+ * which enforces the correct removal order.
+ */
+ device_link_add(dev, rimt_fwnode->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
return acpi_iommu_fwspec_init(dev, deviceid, rimt_fwnode);
}
diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
index 67aae783e8b8..3d5bfaeda4a3 100644
--- a/drivers/android/binder/page_range.rs
+++ b/drivers/android/binder/page_range.rs
@@ -13,6 +13,8 @@
//
// The shrinker will use trylock methods because it locks them in a different order.
+use crate::AssertSync;
+
use core::{
marker::PhantomPinned,
mem::{size_of, size_of_val, MaybeUninit},
@@ -143,14 +145,14 @@ pub(crate) struct ShrinkablePageRange {
}
// We do not define any ops. For now, used only to check identity of vmas.
-static BINDER_VM_OPS: bindings::vm_operations_struct = pin_init::zeroed();
+static BINDER_VM_OPS: AssertSync<bindings::vm_operations_struct> = AssertSync(pin_init::zeroed());
// To ensure that we do not accidentally install pages into or zap pages from the wrong vma, we
// check its vm_ops and private data before using it.
fn check_vma(vma: &virt::VmaRef, owner: *const ShrinkablePageRange) -> Option<&virt::VmaMixedMap> {
// SAFETY: Just reading the vm_ops pointer of any active vma is safe.
let vm_ops = unsafe { (*vma.as_ptr()).vm_ops };
- if !ptr::eq(vm_ops, &BINDER_VM_OPS) {
+ if !ptr::eq(vm_ops, &BINDER_VM_OPS.0) {
return None;
}
@@ -342,7 +344,7 @@ pub(crate) fn register_with_vma(&self, vma: &virt::VmaNew) -> Result<usize> {
// SAFETY: We own the vma, and we don't use any methods on VmaNew that rely on
// `vm_ops`.
- unsafe { (*vma.as_ptr()).vm_ops = &BINDER_VM_OPS };
+ unsafe { (*vma.as_ptr()).vm_ops = &BINDER_VM_OPS.0 };
Ok(num_pages)
}
diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs
index 9a527268f5b4..1488d82184ef 100644
--- a/drivers/android/binder/rust_binder_main.rs
+++ b/drivers/android/binder/rust_binder_main.rs
@@ -300,7 +300,7 @@ fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
/// Makes the inner type Sync.
#[repr(transparent)]
pub struct AssertSync<T>(T);
-// SAFETY: Used only to insert `file_operations` into a global, which is safe.
+// SAFETY: Used only to insert C bindings types into globals, which is safe.
unsafe impl<T> Sync for AssertSync<T> {}
/// File operations that rust_binderfs.c can use.
diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c
index 4e22373fcc1a..e80e94262830 100644
--- a/drivers/auxdisplay/line-display.c
+++ b/drivers/auxdisplay/line-display.c
@@ -365,7 +365,7 @@ static DEFINE_IDA(linedisp_id);
static void linedisp_release(struct device *dev)
{
- struct linedisp *linedisp = to_linedisp(dev);
+ struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
kfree(linedisp->map);
kfree(linedisp->message);
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
index ec017df8572c..1e9e2cad9ddf 100644
--- a/drivers/bluetooth/hci_h4.c
+++ b/drivers/bluetooth/hci_h4.c
@@ -109,9 +109,6 @@ static int h4_recv(struct hci_uart *hu, const void *data, int count)
{
struct h4_struct *h4 = hu->priv;
- if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
- return -EUNATCH;
-
h4->rx_skb = h4_recv_buf(hu, h4->rx_skb, data, count,
h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts));
if (IS_ERR(h4->rx_skb)) {
diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c
index 2c3eb9e89571..67bd71efcfa8 100644
--- a/drivers/comedi/comedi_fops.c
+++ b/drivers/comedi/comedi_fops.c
@@ -793,13 +793,15 @@ static void do_become_nonbusy(struct comedi_device *dev,
__comedi_clear_subdevice_runflags(s, COMEDI_SRF_RUNNING |
COMEDI_SRF_BUSY);
spin_unlock_irqrestore(&s->spin_lock, flags);
- if (comedi_is_runflags_busy(runflags)) {
+ if (async) {
/*
* "Run active" counter was set to 1 when setting up the
* command. Decrement it and wait for it to become 0.
*/
- comedi_put_is_subdevice_running(s);
- wait_for_completion(&async->run_complete);
+ if (comedi_is_runflags_busy(runflags)) {
+ comedi_put_is_subdevice_running(s);
+ wait_for_completion(&async->run_complete);
+ }
comedi_buf_reset(s);
async->inttrig = NULL;
kfree(async->cmd.chanlist);
diff --git a/drivers/comedi/drivers.c b/drivers/comedi/drivers.c
index 69cd2a253c66..7453f778f2c3 100644
--- a/drivers/comedi/drivers.c
+++ b/drivers/comedi/drivers.c
@@ -1063,6 +1063,14 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
ret = -EIO;
goto out;
}
+ if (IS_ENABLED(CONFIG_LOCKDEP)) {
+ /*
+ * dev->spinlock is for private use by the attached low-level
+ * driver. Reinitialize it to stop lock-dependency tracking
+ * between attachments to different low-level drivers.
+ */
+ spin_lock_init(&dev->spinlock);
+ }
dev->driver = driv;
dev->board_name = dev->board_ptr ? *(const char **)dev->board_ptr
: dev->driver->driver_name;
diff --git a/drivers/comedi/drivers/dt2815.c b/drivers/comedi/drivers/dt2815.c
index 03ba2fd18a21..d066dc303520 100644
--- a/drivers/comedi/drivers/dt2815.c
+++ b/drivers/comedi/drivers/dt2815.c
@@ -175,6 +175,18 @@ static int dt2815_attach(struct comedi_device *dev, struct comedi_devconfig *it)
? current_range_type : voltage_range_type;
}
+ /*
+ * Check if hardware is present before attempting any I/O operations.
+ * Reading 0xff from status register typically indicates no hardware
+ * on the bus (floating bus reads as all 1s).
+ */
+ if (inb(dev->iobase + DT2815_STATUS) == 0xff) {
+ dev_err(dev->class_dev,
+ "No hardware detected at I/O base 0x%lx\n",
+ dev->iobase);
+ return -ENODEV;
+ }
+
/* Init the 2815 */
outb(0x00, dev->iobase + DT2815_STATUS);
for (i = 0; i < 100; i++) {
diff --git a/drivers/comedi/drivers/me4000.c b/drivers/comedi/drivers/me4000.c
index 7dd3a0071863..effe9fdbbafe 100644
--- a/drivers/comedi/drivers/me4000.c
+++ b/drivers/comedi/drivers/me4000.c
@@ -315,6 +315,18 @@ static int me4000_xilinx_download(struct comedi_device *dev,
unsigned int val;
unsigned int i;
+ /* Get data stream length from header. */
+ if (size >= 4) {
+ file_length = (((unsigned int)data[0] & 0xff) << 24) +
+ (((unsigned int)data[1] & 0xff) << 16) +
+ (((unsigned int)data[2] & 0xff) << 8) +
+ ((unsigned int)data[3] & 0xff);
+ }
+ if (size < 16 || file_length > size - 16) {
+ dev_err(dev->class_dev, "Firmware length inconsistency\n");
+ return -EINVAL;
+ }
+
if (!xilinx_iobase)
return -ENODEV;
@@ -346,10 +358,6 @@ static int me4000_xilinx_download(struct comedi_device *dev,
outl(val, devpriv->plx_regbase + PLX9052_CNTRL);
/* Download Xilinx firmware */
- file_length = (((unsigned int)data[0] & 0xff) << 24) +
- (((unsigned int)data[1] & 0xff) << 16) +
- (((unsigned int)data[2] & 0xff) << 8) +
- ((unsigned int)data[3] & 0xff);
usleep_range(10, 1000);
for (i = 0; i < file_length; i++) {
diff --git a/drivers/comedi/drivers/me_daq.c b/drivers/comedi/drivers/me_daq.c
index 076b15097afd..2f2ea029cffc 100644
--- a/drivers/comedi/drivers/me_daq.c
+++ b/drivers/comedi/drivers/me_daq.c
@@ -344,6 +344,25 @@ static int me2600_xilinx_download(struct comedi_device *dev,
unsigned int file_length;
unsigned int i;
+ /*
+ * Format of the firmware
+ * Build longs from the byte-wise coded header
+ * Byte 1-3: length of the array
+ * Byte 4-7: version
+ * Byte 8-11: date
+ * Byte 12-15: reserved
+ */
+ if (size >= 4) {
+ file_length = (((unsigned int)data[0] & 0xff) << 24) +
+ (((unsigned int)data[1] & 0xff) << 16) +
+ (((unsigned int)data[2] & 0xff) << 8) +
+ ((unsigned int)data[3] & 0xff);
+ }
+ if (size < 16 || file_length > size - 16) {
+ dev_err(dev->class_dev, "Firmware length inconsistency\n");
+ return -EINVAL;
+ }
+
/* disable irq's on PLX */
writel(0x00, devpriv->plx_regbase + PLX9052_INTCSR);
@@ -357,22 +376,6 @@ static int me2600_xilinx_download(struct comedi_device *dev,
writeb(0x00, dev->mmio + 0x0);
sleep(1);
- /*
- * Format of the firmware
- * Build longs from the byte-wise coded header
- * Byte 1-3: length of the array
- * Byte 4-7: version
- * Byte 8-11: date
- * Byte 12-15: reserved
- */
- if (size < 16)
- return -EINVAL;
-
- file_length = (((unsigned int)data[0] & 0xff) << 24) +
- (((unsigned int)data[1] & 0xff) << 16) +
- (((unsigned int)data[2] & 0xff) << 8) +
- ((unsigned int)data[3] & 0xff);
-
/*
* Loop for writing firmware byte by byte to xilinx
* Firmware data start at offset 16
diff --git a/drivers/comedi/drivers/ni_atmio16d.c b/drivers/comedi/drivers/ni_atmio16d.c
index e5e7cc423c87..b057b3b3582e 100644
--- a/drivers/comedi/drivers/ni_atmio16d.c
+++ b/drivers/comedi/drivers/ni_atmio16d.c
@@ -698,7 +698,8 @@ static int atmio16d_attach(struct comedi_device *dev,
static void atmio16d_detach(struct comedi_device *dev)
{
- reset_atmio16d(dev);
+ if (dev->private)
+ reset_atmio16d(dev);
comedi_legacy_detach(dev);
}
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index e755d54dfece..7bfb6979193c 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -107,9 +107,9 @@ static bool rz_mtu3_is_counter_invalid(struct counter_device *counter, int id)
struct rz_mtu3_cnt *const priv = counter_priv(counter);
unsigned long tmdr;
- pm_runtime_get_sync(priv->ch->dev);
+ pm_runtime_get_sync(counter->parent);
tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
- pm_runtime_put(priv->ch->dev);
+ pm_runtime_put(counter->parent);
if (id == RZ_MTU3_32_BIT_CH && test_bit(RZ_MTU3_TMDR3_LWA, &tmdr))
return false;
@@ -165,12 +165,12 @@ static int rz_mtu3_count_read(struct counter_device *counter,
if (ret)
return ret;
- pm_runtime_get_sync(ch->dev);
+ pm_runtime_get_sync(counter->parent);
if (count->id == RZ_MTU3_32_BIT_CH)
*val = rz_mtu3_32bit_ch_read(ch, RZ_MTU3_TCNTLW);
else
*val = rz_mtu3_16bit_ch_read(ch, RZ_MTU3_TCNT);
- pm_runtime_put(ch->dev);
+ pm_runtime_put(counter->parent);
mutex_unlock(&priv->lock);
return 0;
@@ -187,26 +187,26 @@ static int rz_mtu3_count_write(struct counter_device *counter,
if (ret)
return ret;
- pm_runtime_get_sync(ch->dev);
+ pm_runtime_get_sync(counter->parent);
if (count->id == RZ_MTU3_32_BIT_CH)
rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TCNTLW, val);
else
rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TCNT, val);
- pm_runtime_put(ch->dev);
+ pm_runtime_put(counter->parent);
mutex_unlock(&priv->lock);
return 0;
}
static int rz_mtu3_count_function_read_helper(struct rz_mtu3_channel *const ch,
- struct rz_mtu3_cnt *const priv,
+ struct counter_device *const counter,
enum counter_function *function)
{
u8 timer_mode;
- pm_runtime_get_sync(ch->dev);
+ pm_runtime_get_sync(counter->parent);
timer_mode = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TMDR1);
- pm_runtime_put(ch->dev);
+ pm_runtime_put(counter->parent);
switch (timer_mode & RZ_MTU3_TMDR1_PH_CNT_MODE_MASK) {
case RZ_MTU3_TMDR1_PH_CNT_MODE_1:
@@ -240,7 +240,7 @@ static int rz_mtu3_count_function_read(struct counter_device *counter,
if (ret)
return ret;
- ret = rz_mtu3_count_function_read_helper(ch, priv, function);
+ ret = rz_mtu3_count_function_read_helper(ch, counter, function);
mutex_unlock(&priv->lock);
return ret;
@@ -279,9 +279,9 @@ static int rz_mtu3_count_function_write(struct counter_device *counter,
return -EINVAL;
}
- pm_runtime_get_sync(ch->dev);
+ pm_runtime_get_sync(counter->parent);
rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, timer_mode);
- pm_runtime_put(ch->dev);
+ pm_runtime_put(counter->parent);
mutex_unlock(&priv->lock);
return 0;
@@ -300,9 +300,9 @@ static int rz_mtu3_count_direction_read(struct counter_device *counter,
if (ret)
return ret;
- pm_runtime_get_sync(ch->dev);
+ pm_runtime_get_sync(counter->parent);
tsr = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TSR);
- pm_runtime_put(ch->dev);
+ pm_runtime_put(counter->parent);
*direction = (tsr & RZ_MTU3_TSR_TCFD) ?
COUNTER_COUNT_DIRECTION_FORWARD : COUNTER_COUNT_DIRECTION_BACKWARD;
@@ -377,14 +377,14 @@ static int rz_mtu3_count_ceiling_write(struct counter_device *counter,
return -EINVAL;
}
- pm_runtime_get_sync(ch->dev);
+ pm_runtime_get_sync(counter->parent);
if (count->id == RZ_MTU3_32_BIT_CH)
rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TGRALW, ceiling);
else
rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TGRA, ceiling);
rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA);
- pm_runtime_put(ch->dev);
+ pm_runtime_put(counter->parent);
mutex_unlock(&priv->lock);
return 0;
@@ -495,25 +495,28 @@ static int rz_mtu3_count_enable_read(struct counter_device *counter,
static int rz_mtu3_count_enable_write(struct counter_device *counter,
struct counter_count *count, u8 enable)
{
- struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
int ret = 0;
+ mutex_lock(&priv->lock);
+
+ if (priv->count_is_enabled[count->id] == enable)
+ goto exit;
+
if (enable) {
- mutex_lock(&priv->lock);
- pm_runtime_get_sync(ch->dev);
+ pm_runtime_get_sync(counter->parent);
ret = rz_mtu3_initialize_counter(counter, count->id);
if (ret == 0)
priv->count_is_enabled[count->id] = true;
- mutex_unlock(&priv->lock);
} else {
- mutex_lock(&priv->lock);
rz_mtu3_terminate_counter(counter, count->id);
priv->count_is_enabled[count->id] = false;
- pm_runtime_put(ch->dev);
- mutex_unlock(&priv->lock);
+ pm_runtime_put(counter->parent);
}
+exit:
+ mutex_unlock(&priv->lock);
+
return ret;
}
@@ -540,9 +543,9 @@ static int rz_mtu3_cascade_counts_enable_get(struct counter_device *counter,
if (ret)
return ret;
- pm_runtime_get_sync(priv->ch->dev);
+ pm_runtime_get_sync(counter->parent);
tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
- pm_runtime_put(priv->ch->dev);
+ pm_runtime_put(counter->parent);
*cascade_enable = test_bit(RZ_MTU3_TMDR3_LWA, &tmdr);
mutex_unlock(&priv->lock);
@@ -559,10 +562,10 @@ static int rz_mtu3_cascade_counts_enable_set(struct counter_device *counter,
if (ret)
return ret;
- pm_runtime_get_sync(priv->ch->dev);
+ pm_runtime_get_sync(counter->parent);
rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3,
RZ_MTU3_TMDR3_LWA, cascade_enable);
- pm_runtime_put(priv->ch->dev);
+ pm_runtime_put(counter->parent);
mutex_unlock(&priv->lock);
return 0;
@@ -579,9 +582,9 @@ static int rz_mtu3_ext_input_phase_clock_select_get(struct counter_device *count
if (ret)
return ret;
- pm_runtime_get_sync(priv->ch->dev);
+ pm_runtime_get_sync(counter->parent);
tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
- pm_runtime_put(priv->ch->dev);
+ pm_runtime_put(counter->parent);
*ext_input_phase_clock_select = test_bit(RZ_MTU3_TMDR3_PHCKSEL, &tmdr);
mutex_unlock(&priv->lock);
@@ -598,11 +601,11 @@ static int rz_mtu3_ext_input_phase_clock_select_set(struct counter_device *count
if (ret)
return ret;
- pm_runtime_get_sync(priv->ch->dev);
+ pm_runtime_get_sync(counter->parent);
rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3,
RZ_MTU3_TMDR3_PHCKSEL,
ext_input_phase_clock_select);
- pm_runtime_put(priv->ch->dev);
+ pm_runtime_put(counter->parent);
mutex_unlock(&priv->lock);
return 0;
@@ -640,7 +643,7 @@ static int rz_mtu3_action_read(struct counter_device *counter,
if (ret)
return ret;
- ret = rz_mtu3_count_function_read_helper(ch, priv, &function);
+ ret = rz_mtu3_count_function_read_helper(ch, counter, &function);
if (ret) {
mutex_unlock(&priv->lock);
return ret;
diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c
index 8f5474612b31..00a26fa6292c 100644
--- a/drivers/cpufreq/cpufreq_governor.c
+++ b/drivers/cpufreq/cpufreq_governor.c
@@ -468,13 +468,13 @@ int cpufreq_dbs_governor_init(struct cpufreq_policy *policy)
/* Failure, so roll back. */
pr_err("initialization failed (dbs_data kobject init error %d)\n", ret);
- kobject_put(&dbs_data->attr_set.kobj);
-
policy->governor_data = NULL;
if (!have_governor_per_policy())
gov->gdbs_data = NULL;
- gov->exit(dbs_data);
+
+ kobject_put(&dbs_data->attr_set.kobj);
+ goto free_policy_dbs_info;
free_dbs_data:
kfree(dbs_data);
diff --git a/drivers/crypto/caam/caamalg_qi2.c b/drivers/crypto/caam/caamalg_qi2.c
index c6117c23eb25..07665494c875 100644
--- a/drivers/crypto/caam/caamalg_qi2.c
+++ b/drivers/crypto/caam/caamalg_qi2.c
@@ -3326,9 +3326,10 @@ static int ahash_setkey(struct crypto_ahash *ahash, const u8 *key,
if (aligned_len < keylen)
return -EOVERFLOW;
- hashed_key = kmemdup(key, aligned_len, GFP_KERNEL);
+ hashed_key = kmalloc(aligned_len, GFP_KERNEL);
if (!hashed_key)
return -ENOMEM;
+ memcpy(hashed_key, key, keylen);
ret = hash_digest_key(ctx, &keylen, hashed_key, digestsize);
if (ret)
goto bad_free_key;
diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c
index 25c02e267258..053af748be86 100644
--- a/drivers/crypto/caam/caamhash.c
+++ b/drivers/crypto/caam/caamhash.c
@@ -441,9 +441,10 @@ static int ahash_setkey(struct crypto_ahash *ahash,
if (aligned_len < keylen)
return -EOVERFLOW;
- hashed_key = kmemdup(key, keylen, GFP_KERNEL);
+ hashed_key = kmalloc(aligned_len, GFP_KERNEL);
if (!hashed_key)
return -ENOMEM;
+ memcpy(hashed_key, key, keylen);
ret = hash_digest_key(ctx, &keylen, hashed_key, digestsize);
if (ret)
goto bad_free_key;
diff --git a/drivers/crypto/tegra/tegra-se-aes.c b/drivers/crypto/tegra/tegra-se-aes.c
index 0e07d0523291..9210cceb4b7b 100644
--- a/drivers/crypto/tegra/tegra-se-aes.c
+++ b/drivers/crypto/tegra/tegra-se-aes.c
@@ -529,7 +529,7 @@ static struct tegra_se_alg tegra_aes_algs[] = {
.cra_name = "cbc(aes)",
.cra_driver_name = "cbc-aes-tegra",
.cra_priority = 500,
- .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | CRYPTO_ALG_ASYNC,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_aes_ctx),
.cra_alignmask = 0xf,
@@ -550,7 +550,7 @@ static struct tegra_se_alg tegra_aes_algs[] = {
.cra_name = "ecb(aes)",
.cra_driver_name = "ecb-aes-tegra",
.cra_priority = 500,
- .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | CRYPTO_ALG_ASYNC,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_aes_ctx),
.cra_alignmask = 0xf,
@@ -572,7 +572,7 @@ static struct tegra_se_alg tegra_aes_algs[] = {
.cra_name = "ctr(aes)",
.cra_driver_name = "ctr-aes-tegra",
.cra_priority = 500,
- .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | CRYPTO_ALG_ASYNC,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = 1,
.cra_ctxsize = sizeof(struct tegra_aes_ctx),
.cra_alignmask = 0xf,
@@ -594,6 +594,7 @@ static struct tegra_se_alg tegra_aes_algs[] = {
.cra_name = "xts(aes)",
.cra_driver_name = "xts-aes-tegra",
.cra_priority = 500,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_aes_ctx),
.cra_alignmask = (__alignof__(u64) - 1),
@@ -1922,6 +1923,7 @@ static struct tegra_se_alg tegra_aead_algs[] = {
.cra_name = "gcm(aes)",
.cra_driver_name = "gcm-aes-tegra",
.cra_priority = 500,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = 1,
.cra_ctxsize = sizeof(struct tegra_aead_ctx),
.cra_alignmask = 0xf,
@@ -1944,6 +1946,7 @@ static struct tegra_se_alg tegra_aead_algs[] = {
.cra_name = "ccm(aes)",
.cra_driver_name = "ccm-aes-tegra",
.cra_priority = 500,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = 1,
.cra_ctxsize = sizeof(struct tegra_aead_ctx),
.cra_alignmask = 0xf,
@@ -1971,7 +1974,7 @@ static struct tegra_se_alg tegra_cmac_algs[] = {
.cra_name = "cmac(aes)",
.cra_driver_name = "tegra-se-cmac",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_cmac_ctx),
.cra_alignmask = 0,
diff --git a/drivers/crypto/tegra/tegra-se-hash.c b/drivers/crypto/tegra/tegra-se-hash.c
index 4a298ace6e9f..06bb5bf0fa33 100644
--- a/drivers/crypto/tegra/tegra-se-hash.c
+++ b/drivers/crypto/tegra/tegra-se-hash.c
@@ -761,7 +761,7 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "sha1",
.cra_driver_name = "tegra-se-sha1",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA1_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
@@ -786,7 +786,7 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "sha224",
.cra_driver_name = "tegra-se-sha224",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA224_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
@@ -811,7 +811,7 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "sha256",
.cra_driver_name = "tegra-se-sha256",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA256_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
@@ -836,7 +836,7 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "sha384",
.cra_driver_name = "tegra-se-sha384",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA384_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
@@ -861,7 +861,7 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "sha512",
.cra_driver_name = "tegra-se-sha512",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA512_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
@@ -886,7 +886,7 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "sha3-224",
.cra_driver_name = "tegra-se-sha3-224",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA3_224_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
@@ -911,7 +911,7 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "sha3-256",
.cra_driver_name = "tegra-se-sha3-256",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA3_256_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
@@ -936,7 +936,7 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "sha3-384",
.cra_driver_name = "tegra-se-sha3-384",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA3_384_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
@@ -961,7 +961,7 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "sha3-512",
.cra_driver_name = "tegra-se-sha3-512",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH,
+ .cra_flags = CRYPTO_ALG_ASYNC,
.cra_blocksize = SHA3_512_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
@@ -988,7 +988,8 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "hmac(sha224)",
.cra_driver_name = "tegra-se-hmac-sha224",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_NEED_FALLBACK,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = SHA224_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
@@ -1015,7 +1016,8 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "hmac(sha256)",
.cra_driver_name = "tegra-se-hmac-sha256",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_NEED_FALLBACK,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = SHA256_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
@@ -1042,7 +1044,8 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "hmac(sha384)",
.cra_driver_name = "tegra-se-hmac-sha384",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_NEED_FALLBACK,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = SHA384_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
@@ -1069,7 +1072,8 @@ static struct tegra_se_alg tegra_hash_algs[] = {
.cra_name = "hmac(sha512)",
.cra_driver_name = "tegra-se-hmac-sha512",
.cra_priority = 300,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_NEED_FALLBACK,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = SHA512_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_sha_ctx),
.cra_alignmask = 0,
diff --git a/drivers/firmware/microchip/mpfs-auto-update.c b/drivers/firmware/microchip/mpfs-auto-update.c
index e194f7acb2a9..8fc3749d4a70 100644
--- a/drivers/firmware/microchip/mpfs-auto-update.c
+++ b/drivers/firmware/microchip/mpfs-auto-update.c
@@ -113,10 +113,6 @@ static enum fw_upload_err mpfs_auto_update_prepare(struct fw_upload *fw_uploader
* be added here.
*/
- priv->flash = mpfs_sys_controller_get_flash(priv->sys_controller);
- if (!priv->flash)
- return FW_UPLOAD_ERR_HW_ERROR;
-
erase_size = round_up(erase_size, (u64)priv->flash->erasesize);
/*
@@ -427,6 +423,12 @@ static int mpfs_auto_update_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(priv->sys_controller),
"Could not register as a sub device of the system controller\n");
+ priv->flash = mpfs_sys_controller_get_flash(priv->sys_controller);
+ if (IS_ERR_OR_NULL(priv->flash)) {
+ dev_dbg(dev, "No flash connected to the system controller, auto-update not supported\n");
+ return -ENODEV;
+ }
+
priv->dev = dev;
platform_set_drvdata(pdev, priv);
diff --git a/drivers/gpib/Kconfig b/drivers/gpib/Kconfig
index eeb50956ce85..d43a28c62ed7 100644
--- a/drivers/gpib/Kconfig
+++ b/drivers/gpib/Kconfig
@@ -122,6 +122,7 @@ config GPIB_FLUKE
depends on OF
select GPIB_COMMON
select GPIB_NEC7210
+ depends on HAS_IOMEM
help
GPIB driver for Fluke based cda devices.
diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c
index 9dbbac8b8436..baa2fea5ebf7 100644
--- a/drivers/gpib/common/gpib_os.c
+++ b/drivers/gpib/common/gpib_os.c
@@ -888,10 +888,6 @@ static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo
if (read_cmd.completed_transfer_count > read_cmd.requested_transfer_count)
return -EINVAL;
- desc = handle_to_descriptor(file_priv, read_cmd.handle);
- if (!desc)
- return -EINVAL;
-
if (WARN_ON_ONCE(sizeof(userbuf) > sizeof(read_cmd.buffer_ptr)))
return -EFAULT;
@@ -904,6 +900,17 @@ static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo
if (!access_ok(userbuf, remain))
return -EFAULT;
+ /* Lock descriptors to prevent concurrent close from freeing descriptor */
+ if (mutex_lock_interruptible(&file_priv->descriptors_mutex))
+ return -ERESTARTSYS;
+ desc = handle_to_descriptor(file_priv, read_cmd.handle);
+ if (!desc) {
+ mutex_unlock(&file_priv->descriptors_mutex);
+ return -EINVAL;
+ }
+ atomic_inc(&desc->descriptor_busy);
+ mutex_unlock(&file_priv->descriptors_mutex);
+
atomic_set(&desc->io_in_progress, 1);
/* Read buffer loads till we fill the user supplied buffer */
@@ -937,6 +944,7 @@ static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo
retval = copy_to_user((void __user *)arg, &read_cmd, sizeof(read_cmd));
atomic_set(&desc->io_in_progress, 0);
+ atomic_dec(&desc->descriptor_busy);
wake_up_interruptible(&board->wait);
if (retval)
@@ -964,10 +972,6 @@ static int command_ioctl(struct gpib_file_private *file_priv,
if (cmd.completed_transfer_count > cmd.requested_transfer_count)
return -EINVAL;
- desc = handle_to_descriptor(file_priv, cmd.handle);
- if (!desc)
- return -EINVAL;
-
userbuf = (u8 __user *)(unsigned long)cmd.buffer_ptr;
userbuf += cmd.completed_transfer_count;
@@ -980,6 +984,17 @@ static int command_ioctl(struct gpib_file_private *file_priv,
if (!access_ok(userbuf, remain))
return -EFAULT;
+ /* Lock descriptors to prevent concurrent close from freeing descriptor */
+ if (mutex_lock_interruptible(&file_priv->descriptors_mutex))
+ return -ERESTARTSYS;
+ desc = handle_to_descriptor(file_priv, cmd.handle);
+ if (!desc) {
+ mutex_unlock(&file_priv->descriptors_mutex);
+ return -EINVAL;
+ }
+ atomic_inc(&desc->descriptor_busy);
+ mutex_unlock(&file_priv->descriptors_mutex);
+
/*
* Write buffer loads till we empty the user supplied buffer.
* Call drivers at least once, even if remain is zero, in
@@ -1003,6 +1018,7 @@ static int command_ioctl(struct gpib_file_private *file_priv,
userbuf += bytes_written;
if (retval < 0) {
atomic_set(&desc->io_in_progress, 0);
+ atomic_dec(&desc->descriptor_busy);
wake_up_interruptible(&board->wait);
break;
@@ -1022,6 +1038,7 @@ static int command_ioctl(struct gpib_file_private *file_priv,
*/
if (!no_clear_io_in_prog || fault)
atomic_set(&desc->io_in_progress, 0);
+ atomic_dec(&desc->descriptor_busy);
wake_up_interruptible(&board->wait);
if (fault)
@@ -1047,10 +1064,6 @@ static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *b
if (write_cmd.completed_transfer_count > write_cmd.requested_transfer_count)
return -EINVAL;
- desc = handle_to_descriptor(file_priv, write_cmd.handle);
- if (!desc)
- return -EINVAL;
-
userbuf = (u8 __user *)(unsigned long)write_cmd.buffer_ptr;
userbuf += write_cmd.completed_transfer_count;
@@ -1060,6 +1073,17 @@ static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *b
if (!access_ok(userbuf, remain))
return -EFAULT;
+ /* Lock descriptors to prevent concurrent close from freeing descriptor */
+ if (mutex_lock_interruptible(&file_priv->descriptors_mutex))
+ return -ERESTARTSYS;
+ desc = handle_to_descriptor(file_priv, write_cmd.handle);
+ if (!desc) {
+ mutex_unlock(&file_priv->descriptors_mutex);
+ return -EINVAL;
+ }
+ atomic_inc(&desc->descriptor_busy);
+ mutex_unlock(&file_priv->descriptors_mutex);
+
atomic_set(&desc->io_in_progress, 1);
/* Write buffer loads till we empty the user supplied buffer */
@@ -1094,6 +1118,7 @@ static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *b
fault = copy_to_user((void __user *)arg, &write_cmd, sizeof(write_cmd));
atomic_set(&desc->io_in_progress, 0);
+ atomic_dec(&desc->descriptor_busy);
wake_up_interruptible(&board->wait);
if (fault)
@@ -1276,6 +1301,9 @@ static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigne
{
struct gpib_close_dev_ioctl cmd;
struct gpib_file_private *file_priv = filep->private_data;
+ struct gpib_descriptor *desc;
+ unsigned int pad;
+ int sad;
int retval;
retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd));
@@ -1284,19 +1312,27 @@ static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigne
if (cmd.handle >= GPIB_MAX_NUM_DESCRIPTORS)
return -EINVAL;
- if (!file_priv->descriptors[cmd.handle])
- return -EINVAL;
- retval = decrement_open_device_count(board, &board->device_list,
- file_priv->descriptors[cmd.handle]->pad,
- file_priv->descriptors[cmd.handle]->sad);
- if (retval < 0)
- return retval;
-
- kfree(file_priv->descriptors[cmd.handle]);
+ mutex_lock(&file_priv->descriptors_mutex);
+ desc = file_priv->descriptors[cmd.handle];
+ if (!desc) {
+ mutex_unlock(&file_priv->descriptors_mutex);
+ return -EINVAL;
+ }
+ if (atomic_read(&desc->descriptor_busy)) {
+ mutex_unlock(&file_priv->descriptors_mutex);
+ return -EBUSY;
+ }
+ /* Remove from table while holding lock to prevent new IO from starting */
file_priv->descriptors[cmd.handle] = NULL;
+ pad = desc->pad;
+ sad = desc->sad;
+ mutex_unlock(&file_priv->descriptors_mutex);
- return 0;
+ retval = decrement_open_device_count(board, &board->device_list, pad, sad);
+
+ kfree(desc);
+ return retval;
}
static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg)
@@ -1331,12 +1367,25 @@ static int wait_ioctl(struct gpib_file_private *file_priv, struct gpib_board *bo
if (retval)
return -EFAULT;
+ /*
+ * Lock descriptors to prevent concurrent close from freeing
+ * descriptor. ibwait() releases big_gpib_mutex when wait_mask
+ * is non-zero, so desc must be pinned with descriptor_busy.
+ */
+ mutex_lock(&file_priv->descriptors_mutex);
desc = handle_to_descriptor(file_priv, wait_cmd.handle);
- if (!desc)
+ if (!desc) {
+ mutex_unlock(&file_priv->descriptors_mutex);
return -EINVAL;
+ }
+ atomic_inc(&desc->descriptor_busy);
+ mutex_unlock(&file_priv->descriptors_mutex);
retval = ibwait(board, wait_cmd.wait_mask, wait_cmd.clear_mask,
wait_cmd.set_mask, &wait_cmd.ibsta, wait_cmd.usec_timeout, desc);
+
+ atomic_dec(&desc->descriptor_busy);
+
if (retval < 0)
return retval;
@@ -2035,6 +2084,7 @@ void init_gpib_descriptor(struct gpib_descriptor *desc)
desc->is_board = 0;
desc->autopoll_enabled = 0;
atomic_set(&desc->io_in_progress, 0);
+ atomic_set(&desc->descriptor_busy, 0);
}
int gpib_register_driver(struct gpib_interface *interface, struct module *provider_module)
diff --git a/drivers/gpib/include/gpib_types.h b/drivers/gpib/include/gpib_types.h
index 5a0978ae27e7..28b73157ffb7 100644
--- a/drivers/gpib/include/gpib_types.h
+++ b/drivers/gpib/include/gpib_types.h
@@ -364,6 +364,14 @@ struct gpib_descriptor {
unsigned int pad; /* primary gpib address */
int sad; /* secondary gpib address (negative means disabled) */
atomic_t io_in_progress;
+ /*
+ * Kernel-only reference count to prevent descriptor from being
+ * freed while IO handlers hold a pointer to it. Incremented
+ * before each IO operation, decremented when done. Unlike
+ * io_in_progress, this cannot be modified from userspace via
+ * general_ibstatus().
+ */
+ atomic_t descriptor_busy;
unsigned is_board : 1;
unsigned autopoll_enabled : 1;
};
diff --git a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c
index 6dc22306be8a..482369f3f502 100644
--- a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c
+++ b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c
@@ -406,7 +406,7 @@ static int usb_gpib_attach(struct gpib_board *board, const struct gpib_board_con
for (j = 0 ; j < MAX_DEV ; j++) {
if ((assigned_usb_minors & 1 << j) == 0)
continue;
- udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j]));
+ udev = interface_to_usbdev(lpvo_usb_interfaces[j]);
device_path = kobject_get_path(&udev->dev.kobj, GFP_KERNEL);
match = gpib_match_device_path(&lpvo_usb_interfaces[j]->dev,
config->device_path);
@@ -421,7 +421,7 @@ static int usb_gpib_attach(struct gpib_board *board, const struct gpib_board_con
for (j = 0 ; j < MAX_DEV ; j++) {
if ((assigned_usb_minors & 1 << j) == 0)
continue;
- udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j]));
+ udev = interface_to_usbdev(lpvo_usb_interfaces[j]);
DIA_LOG(1, "dev. %d: bus %d -> %d dev: %d -> %d\n", j,
udev->bus->busnum, config->pci_bus, udev->devnum, config->pci_slot);
if (config->pci_bus == udev->bus->busnum &&
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index d7666fe9dbf8..647b6f4861b7 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -584,12 +584,13 @@ static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
unsigned long config;
bool ret = false;
int i, type;
+ bool is_imx8qm = of_device_is_compatible(port->dev->of_node, "fsl,imx8qm-gpio");
static const u32 pad_type_map[] = {
IMX_SCU_WAKEUP_OFF, /* 0 */
IMX_SCU_WAKEUP_RISE_EDGE, /* IRQ_TYPE_EDGE_RISING */
IMX_SCU_WAKEUP_FALL_EDGE, /* IRQ_TYPE_EDGE_FALLING */
- IMX_SCU_WAKEUP_FALL_EDGE, /* IRQ_TYPE_EDGE_BOTH */
+ IMX_SCU_WAKEUP_RISE_EDGE, /* IRQ_TYPE_EDGE_BOTH */
IMX_SCU_WAKEUP_HIGH_LVL, /* IRQ_TYPE_LEVEL_HIGH */
IMX_SCU_WAKEUP_OFF, /* 5 */
IMX_SCU_WAKEUP_OFF, /* 6 */
@@ -604,6 +605,13 @@ static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
config = pad_type_map[type];
else
config = IMX_SCU_WAKEUP_OFF;
+
+ if (is_imx8qm && config == IMX_SCU_WAKEUP_FALL_EDGE) {
+ dev_warn_once(port->dev,
+ "No falling-edge support for wakeup on i.MX8QM\n");
+ config = IMX_SCU_WAKEUP_OFF;
+ }
+
ret |= mxc_gpio_generic_config(port, i, config);
}
}
diff --git a/drivers/gpio/gpio-qixis-fpga.c b/drivers/gpio/gpio-qixis-fpga.c
index 6e67f43ac0bd..3ced47db1521 100644
--- a/drivers/gpio/gpio-qixis-fpga.c
+++ b/drivers/gpio/gpio-qixis-fpga.c
@@ -60,8 +60,8 @@ static int qixis_cpld_gpio_probe(struct platform_device *pdev)
return PTR_ERR(reg);
regmap = devm_regmap_init_mmio(&pdev->dev, reg, ®map_config_8r_8v);
- if (!regmap)
- return -ENODEV;
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
/* In this case, the offset of our register is 0 inside the
* regmap area that we just created.
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index e16f467b72e7..692f568ffe7a 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -443,8 +443,8 @@ static bool gpio_shared_dev_is_reset_gpio(struct device *consumer,
}
#endif /* CONFIG_RESET_GPIO */
-int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id,
- unsigned long lflags)
+int gpio_shared_add_proxy_lookup(struct device *consumer, struct fwnode_handle *fwnode,
+ const char *con_id, unsigned long lflags)
{
const char *dev_id = dev_name(consumer);
struct gpiod_lookup_table *lookup;
@@ -463,7 +463,7 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id,
if (!ref->fwnode && strstarts(dev_name(consumer), "reset.gpio.")) {
if (!gpio_shared_dev_is_reset_gpio(consumer, entry, ref))
continue;
- } else if (!device_match_fwnode(consumer, ref->fwnode)) {
+ } else if (fwnode != ref->fwnode) {
continue;
}
@@ -511,8 +511,9 @@ static void gpio_shared_remove_adev(struct auxiliary_device *adev)
auxiliary_device_uninit(adev);
}
-int gpio_device_setup_shared(struct gpio_device *gdev)
+int gpiochip_setup_shared(struct gpio_chip *gc)
{
+ struct gpio_device *gdev = gc->gpiodev;
struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref;
struct gpio_desc *desc;
@@ -543,20 +544,42 @@ int gpio_device_setup_shared(struct gpio_device *gdev)
if (list_count_nodes(&entry->refs) <= 1)
continue;
- desc = &gdev->descs[entry->offset];
+ scoped_guard(mutex, &entry->lock) {
+#if IS_ENABLED(CONFIG_OF)
+ if (is_of_node(entry->fwnode) && gc->of_xlate) {
+ /*
+ * This is the earliest that we can tranlate the
+ * devicetree offset to the chip offset.
+ */
+ struct of_phandle_args gpiospec = { };
- __set_bit(GPIOD_FLAG_SHARED, &desc->flags);
- /*
- * Shared GPIOs are not requested via the normal path. Make
- * them inaccessible to anyone even before we register the
- * chip.
- */
- ret = gpiod_request_commit(desc, "shared");
- if (ret)
- return ret;
+ gpiospec.np = to_of_node(entry->fwnode);
+ gpiospec.args_count = 2;
+ gpiospec.args[0] = entry->offset;
+
+ ret = gc->of_xlate(gc, &gpiospec, NULL);
+ if (ret < 0)
+ return ret;
+
+ entry->offset = ret;
+ }
+#endif /* CONFIG_OF */
+
+ desc = &gdev->descs[entry->offset];
+
+ __set_bit(GPIOD_FLAG_SHARED, &desc->flags);
+ /*
+ * Shared GPIOs are not requested via the normal path. Make
+ * them inaccessible to anyone even before we register the
+ * chip.
+ */
+ ret = gpiod_request_commit(desc, "shared");
+ if (ret)
+ return ret;
- pr_debug("GPIO %u owned by %s is shared by multiple consumers\n",
- entry->offset, gpio_device_get_label(gdev));
+ pr_debug("GPIO %u owned by %s is shared by multiple consumers\n",
+ entry->offset, gpio_device_get_label(gdev));
+ }
list_for_each_entry(ref, &entry->refs, list) {
pr_debug("Setting up a shared GPIO entry for %s (con_id: '%s')\n",
@@ -580,6 +603,8 @@ void gpio_device_teardown_shared(struct gpio_device *gdev)
struct gpio_shared_ref *ref;
list_for_each_entry(entry, &gpio_shared_list, list) {
+ guard(mutex)(&entry->lock);
+
if (!device_match_fwnode(&gdev->dev, entry->fwnode))
continue;
diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h
index 40568ef7364c..15e72a8dcdb1 100644
--- a/drivers/gpio/gpiolib-shared.h
+++ b/drivers/gpio/gpiolib-shared.h
@@ -11,17 +11,19 @@
struct gpio_device;
struct gpio_desc;
struct device;
+struct fwnode_handle;
#if IS_ENABLED(CONFIG_GPIO_SHARED)
-int gpio_device_setup_shared(struct gpio_device *gdev);
+int gpiochip_setup_shared(struct gpio_chip *gc);
void gpio_device_teardown_shared(struct gpio_device *gdev);
-int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id,
- unsigned long lflags);
+int gpio_shared_add_proxy_lookup(struct device *consumer,
+ struct fwnode_handle *fwnode,
+ const char *con_id, unsigned long lflags);
#else
-static inline int gpio_device_setup_shared(struct gpio_device *gdev)
+static inline int gpiochip_setup_shared(struct gpio_chip *gc)
{
return 0;
}
@@ -29,6 +31,7 @@ static inline int gpio_device_setup_shared(struct gpio_device *gdev)
static inline void gpio_device_teardown_shared(struct gpio_device *gdev) { }
static inline int gpio_shared_add_proxy_lookup(struct device *consumer,
+ struct fwnode_handle *fwnode,
const char *con_id,
unsigned long lflags)
{
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 2e33afbbfda4..ef3d66857ad6 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -893,13 +893,15 @@ static const struct device_type gpio_dev_type = {
#define gcdev_unregister(gdev) device_del(&(gdev)->dev)
#endif
+/*
+ * An initial reference count has been held in gpiochip_add_data_with_key().
+ * The caller should drop the reference via gpio_device_put() on errors.
+ */
static int gpiochip_setup_dev(struct gpio_device *gdev)
{
struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
int ret;
- device_initialize(&gdev->dev);
-
/*
* If fwnode doesn't belong to another device, it's safe to clear its
* initialized flag.
@@ -965,9 +967,11 @@ static void gpiochip_setup_devs(void)
list_for_each_entry_srcu(gdev, &gpio_devices, list,
srcu_read_lock_held(&gpio_devices_srcu)) {
ret = gpiochip_setup_dev(gdev);
- if (ret)
+ if (ret) {
+ gpio_device_put(gdev);
dev_err(&gdev->dev,
"Failed to initialize gpio device (%d)\n", ret);
+ }
}
}
@@ -1048,71 +1052,72 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
int base = 0;
int ret;
- /*
- * First: allocate and populate the internal stat container, and
- * set up the struct device.
- */
gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
if (!gdev)
return -ENOMEM;
-
- gdev->dev.type = &gpio_dev_type;
- gdev->dev.bus = &gpio_bus_type;
- gdev->dev.parent = gc->parent;
- rcu_assign_pointer(gdev->chip, gc);
-
gc->gpiodev = gdev;
gpiochip_set_data(gc, data);
- device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc));
-
ret = ida_alloc(&gpio_ida, GFP_KERNEL);
if (ret < 0)
goto err_free_gdev;
gdev->id = ret;
- ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
+ ret = init_srcu_struct(&gdev->srcu);
if (ret)
goto err_free_ida;
+ rcu_assign_pointer(gdev->chip, gc);
- if (gc->parent && gc->parent->driver)
- gdev->owner = gc->parent->driver->owner;
- else if (gc->owner)
- /* TODO: remove chip->owner */
- gdev->owner = gc->owner;
- else
- gdev->owner = THIS_MODULE;
+ ret = init_srcu_struct(&gdev->desc_srcu);
+ if (ret)
+ goto err_cleanup_gdev_srcu;
+
+ ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
+ if (ret)
+ goto err_cleanup_desc_srcu;
+
+ device_initialize(&gdev->dev);
+ /*
+ * After this point any allocated resources to `gdev` will be
+ * free():ed by gpiodev_release(). If you add new resources
+ * then make sure they get free():ed there.
+ */
+ gdev->dev.type = &gpio_dev_type;
+ gdev->dev.bus = &gpio_bus_type;
+ gdev->dev.parent = gc->parent;
+ device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc));
ret = gpiochip_get_ngpios(gc, &gdev->dev);
if (ret)
- goto err_free_dev_name;
+ goto err_put_device;
+ gdev->ngpio = gc->ngpio;
gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL);
if (!gdev->descs) {
ret = -ENOMEM;
- goto err_free_dev_name;
+ goto err_put_device;
}
gdev->label = kstrdup_const(gc->label ?: "unknown", GFP_KERNEL);
if (!gdev->label) {
ret = -ENOMEM;
- goto err_free_descs;
+ goto err_put_device;
}
- gdev->ngpio = gc->ngpio;
gdev->can_sleep = gc->can_sleep;
-
rwlock_init(&gdev->line_state_lock);
RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
-
- ret = init_srcu_struct(&gdev->srcu);
- if (ret)
- goto err_free_label;
-
- ret = init_srcu_struct(&gdev->desc_srcu);
- if (ret)
- goto err_cleanup_gdev_srcu;
+#ifdef CONFIG_PINCTRL
+ INIT_LIST_HEAD(&gdev->pin_ranges);
+#endif
+ if (gc->parent && gc->parent->driver)
+ gdev->owner = gc->parent->driver->owner;
+ else if (gc->owner)
+ /* TODO: remove chip->owner */
+ gdev->owner = gc->owner;
+ else
+ gdev->owner = THIS_MODULE;
scoped_guard(mutex, &gpio_devices_lock) {
/*
@@ -1128,7 +1133,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (base < 0) {
ret = base;
base = 0;
- goto err_cleanup_desc_srcu;
+ goto err_put_device;
}
/*
@@ -1148,14 +1153,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
ret = gpiodev_add_to_list_unlocked(gdev);
if (ret) {
gpiochip_err(gc, "GPIO integer space overlap, cannot add chip\n");
- goto err_cleanup_desc_srcu;
+ goto err_put_device;
}
}
-#ifdef CONFIG_PINCTRL
- INIT_LIST_HEAD(&gdev->pin_ranges);
-#endif
-
if (gc->names)
gpiochip_set_desc_names(gc);
@@ -1211,7 +1212,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (ret)
goto err_remove_irqchip_mask;
- ret = gpio_device_setup_shared(gdev);
+ ret = gpiochip_setup_shared(gc);
if (ret)
goto err_remove_irqchip;
@@ -1249,25 +1250,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
scoped_guard(mutex, &gpio_devices_lock)
list_del_rcu(&gdev->list);
synchronize_srcu(&gpio_devices_srcu);
- if (gdev->dev.release) {
- /* release() has been registered by gpiochip_setup_dev() */
- gpio_device_put(gdev);
- goto err_print_message;
- }
+err_put_device:
+ gpio_device_put(gdev);
+ goto err_print_message;
+
err_cleanup_desc_srcu:
cleanup_srcu_struct(&gdev->desc_srcu);
err_cleanup_gdev_srcu:
cleanup_srcu_struct(&gdev->srcu);
-err_free_label:
- kfree_const(gdev->label);
-err_free_descs:
- kfree(gdev->descs);
-err_free_dev_name:
- kfree(dev_name(&gdev->dev));
err_free_ida:
ida_free(&gpio_ida, gdev->id);
err_free_gdev:
kfree(gdev);
+
err_print_message:
/* failures here can mean systems won't boot... */
if (ret != -EPROBE_DEFER) {
@@ -2466,8 +2461,10 @@ int gpiod_request_commit(struct gpio_desc *desc, const char *label)
return -EBUSY;
offset = gpiod_hwgpio(desc);
- if (!gpiochip_line_is_valid(guard.gc, offset))
- return -EINVAL;
+ if (!gpiochip_line_is_valid(guard.gc, offset)) {
+ ret = -EINVAL;
+ goto out_clear_bit;
+ }
/* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
@@ -4718,8 +4715,8 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
* lookup table for the proxy device as previously
* we only knew the consumer's fwnode.
*/
- ret = gpio_shared_add_proxy_lookup(consumer, con_id,
- lookupflags);
+ ret = gpio_shared_add_proxy_lookup(consumer, fwnode,
+ con_id, lookupflags);
if (ret)
return ERR_PTR(ret);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 83567ade8429..b56b6759e1c3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -2703,8 +2703,12 @@ static int amdgpu_pmops_freeze(struct device *dev)
if (r)
return r;
- if (amdgpu_acpi_should_gpu_reset(adev))
- return amdgpu_asic_reset(adev);
+ if (amdgpu_acpi_should_gpu_reset(adev)) {
+ amdgpu_device_lock_reset_domain(adev->reset_domain);
+ r = amdgpu_asic_reset(adev);
+ amdgpu_device_unlock_reset_domain(adev->reset_domain);
+ return r;
+ }
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
index 644e413a4e69..e495a8fa13fd 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
@@ -68,8 +68,11 @@ int amdgpu_pasid_alloc(unsigned int bits)
return -EINVAL;
spin_lock(&amdgpu_pasid_idr_lock);
+ /* TODO: Need to replace the idr with an xarry, and then
+ * handle the internal locking with ATOMIC safe paths.
+ */
pasid = idr_alloc_cyclic(&amdgpu_pasid_idr, NULL, 1,
- 1U << bits, GFP_KERNEL);
+ 1U << bits, GFP_ATOMIC);
spin_unlock(&amdgpu_pasid_idr_lock);
if (pasid >= 0)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index ab934723579c..0d41f6f5cc0b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -550,6 +550,13 @@ amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr,
goto unpin_bo;
}
+ /* Validate doorbell_offset is within the doorbell BO */
+ if ((u64)db_info->doorbell_offset * db_size + db_size >
+ amdgpu_bo_size(db_obj->obj)) {
+ r = -EINVAL;
+ goto unpin_bo;
+ }
+
index = amdgpu_doorbell_index_on_bar(uq_mgr->adev, db_obj->obj,
db_info->doorbell_offset, db_size);
drm_dbg_driver(adev_to_drm(uq_mgr->adev),
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h
index d3edc92c7a12..42b4f246cddc 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h
@@ -172,7 +172,7 @@ struct amdgpu_bo_vm;
#define AMDGPU_VA_RESERVED_SEQ64_SIZE (2ULL << 20)
#define AMDGPU_VA_RESERVED_SEQ64_START(adev) (AMDGPU_VA_RESERVED_CSA_START(adev) \
- AMDGPU_VA_RESERVED_SEQ64_SIZE)
-#define AMDGPU_VA_RESERVED_TRAP_SIZE (2ULL << 12)
+#define AMDGPU_VA_RESERVED_TRAP_SIZE (1ULL << 16)
#define AMDGPU_VA_RESERVED_TRAP_START(adev) (AMDGPU_VA_RESERVED_SEQ64_START(adev) \
- AMDGPU_VA_RESERVED_TRAP_SIZE)
#define AMDGPU_VA_RESERVED_BOTTOM (1ULL << 16)
diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c
index a9be7a505026..27d883fda5fa 100644
--- a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c
@@ -170,7 +170,8 @@ static int psp_v11_0_wait_for_bootloader(struct psp_context *psp)
int retry_loop;
/* For a reset done at the end of S3, only wait for TOS to be unloaded */
- if (adev->in_s3 && !(adev->flags & AMD_IS_APU) && amdgpu_in_reset(adev))
+ if ((adev->in_s4 || adev->in_s3) && !(adev->flags & AMD_IS_APU) &&
+ amdgpu_in_reset(adev))
return psp_v11_wait_for_tos_unload(psp);
for (retry_loop = 0; retry_loop < 20; retry_loop++) {
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
index 70ef051511bb..4f4eb0791138 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
@@ -102,8 +102,8 @@
* The first chunk is the TBA used for the CWSR ISA code. The second
* chunk is used as TMA for user-mode trap handler setup in daisy-chain mode.
*/
-#define KFD_CWSR_TBA_TMA_SIZE (PAGE_SIZE * 2)
-#define KFD_CWSR_TMA_OFFSET (PAGE_SIZE + 2048)
+#define KFD_CWSR_TBA_TMA_SIZE (AMDGPU_GPU_PAGE_SIZE * 2)
+#define KFD_CWSR_TMA_OFFSET (AMDGPU_GPU_PAGE_SIZE + 2048)
#define KFD_MAX_NUM_OF_QUEUES_PER_DEVICE \
(KFD_MAX_NUM_OF_PROCESSES * \
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
index cc124ab6aa7f..212c13b745d0 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
@@ -37,19 +37,19 @@ const u64 amdgpu_dm_supported_degam_tfs =
BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) |
- BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV);
+ BIT(DRM_COLOROP_1D_CURVE_GAMMA22);
const u64 amdgpu_dm_supported_shaper_tfs =
BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF) |
- BIT(DRM_COLOROP_1D_CURVE_GAMMA22);
+ BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV);
const u64 amdgpu_dm_supported_blnd_tfs =
BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) |
- BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV);
+ BIT(DRM_COLOROP_1D_CURVE_GAMMA22);
#define MAX_COLOR_PIPELINE_OPS 10
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c
index 62887ce5fe20..d0ed620a26a9 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c
@@ -146,6 +146,7 @@ void dcn401_init_hw(struct dc *dc)
int edp_num;
uint32_t backlight = MAX_BACKLIGHT_LEVEL;
uint32_t user_level = MAX_BACKLIGHT_LEVEL;
+ bool dchub_ref_freq_changed;
int current_dchub_ref_freq = 0;
if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->init_clocks) {
@@ -359,14 +360,18 @@ void dcn401_init_hw(struct dc *dc)
dc->caps.dmub_caps.psr = dc->ctx->dmub_srv->dmub->feature_caps.psr;
dc->caps.dmub_caps.mclk_sw = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver > 0;
dc->caps.dmub_caps.fams_ver = dc->ctx->dmub_srv->dmub->feature_caps.fw_assisted_mclk_switch_ver;
+
+ /* sw and fw FAMS versions must match for support */
dc->debug.fams2_config.bits.enable &=
- dc->caps.dmub_caps.fams_ver == dc->debug.fams_version.ver; // sw & fw fams versions must match for support
- if ((!dc->debug.fams2_config.bits.enable && dc->res_pool->funcs->update_bw_bounding_box)
- || res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq) {
+ dc->caps.dmub_caps.fams_ver == dc->debug.fams_version.ver;
+ dchub_ref_freq_changed =
+ res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000 != current_dchub_ref_freq;
+ if ((!dc->debug.fams2_config.bits.enable || dchub_ref_freq_changed) &&
+ dc->res_pool->funcs->update_bw_bounding_box &&
+ dc->clk_mgr && dc->clk_mgr->bw_params) {
/* update bounding box if FAMS2 disabled, or if dchub clk has changed */
- if (dc->clk_mgr)
- dc->res_pool->funcs->update_bw_bounding_box(dc,
- dc->clk_mgr->bw_params);
+ dc->res_pool->funcs->update_bw_bounding_box(dc,
+ dc->clk_mgr->bw_params);
}
}
}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
index b0d6487171d7..48bf000f12e5 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
@@ -262,7 +262,6 @@ int smu_v11_0_check_fw_version(struct smu_context *smu)
"smu fw program = %d, version = 0x%08x (%d.%d.%d)\n",
smu->smc_driver_if_version, if_version,
smu_program, smu_version, smu_major, smu_minor, smu_debug);
- dev_info(smu->adev->dev, "SMU driver if version not matched\n");
}
return ret;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c
index 3d3cd546f0ad..63098ed9fcfe 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c
@@ -101,7 +101,6 @@ int smu_v12_0_check_fw_version(struct smu_context *smu)
"smu fw program = %d, smu fw version = 0x%08x (%d.%d.%d)\n",
smu->smc_driver_if_version, if_version,
smu_program, smu_version, smu_major, smu_minor, smu_debug);
- dev_info(smu->adev->dev, "SMU driver if version not matched\n");
}
return ret;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
index f4ce4dd02780..cd7db433795f 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
@@ -59,6 +59,10 @@
#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
+static void smu_v13_0_0_get_od_setting_limits(struct smu_context *smu,
+ int od_feature_bit,
+ int32_t *min, int32_t *max);
+
#define FEATURE_MASK(feature) (1ULL << feature)
#define SMC_DPM_FEATURE ( \
FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT) | \
@@ -1061,8 +1065,35 @@ static bool smu_v13_0_0_is_od_feature_supported(struct smu_context *smu,
PPTable_t *pptable = smu->smu_table.driver_pptable;
const OverDriveLimits_t * const overdrive_upperlimits =
&pptable->SkuTable.OverDriveLimitsBasicMax;
+ int32_t min_value, max_value;
+ bool feature_enabled;
- return overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit);
+ switch (od_feature_bit) {
+ case PP_OD_FEATURE_FAN_CURVE_BIT:
+ feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit));
+ if (feature_enabled) {
+ smu_v13_0_0_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_TEMP,
+ &min_value, &max_value);
+ if (!min_value && !max_value) {
+ feature_enabled = false;
+ goto out;
+ }
+
+ smu_v13_0_0_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_PWM,
+ &min_value, &max_value);
+ if (!min_value && !max_value) {
+ feature_enabled = false;
+ goto out;
+ }
+ }
+ break;
+ default:
+ feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit));
+ break;
+ }
+
+out:
+ return feature_enabled;
}
static void smu_v13_0_0_get_od_setting_limits(struct smu_context *smu,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
index e7b2e823812c..da31a6504ac0 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
@@ -59,6 +59,10 @@
#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
+static void smu_v13_0_7_get_od_setting_limits(struct smu_context *smu,
+ int od_feature_bit,
+ int32_t *min, int32_t *max);
+
#define FEATURE_MASK(feature) (1ULL << feature)
#define SMC_DPM_FEATURE ( \
FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT) | \
@@ -1050,8 +1054,35 @@ static bool smu_v13_0_7_is_od_feature_supported(struct smu_context *smu,
PPTable_t *pptable = smu->smu_table.driver_pptable;
const OverDriveLimits_t * const overdrive_upperlimits =
&pptable->SkuTable.OverDriveLimitsBasicMax;
+ int32_t min_value, max_value;
+ bool feature_enabled;
- return overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit);
+ switch (od_feature_bit) {
+ case PP_OD_FEATURE_FAN_CURVE_BIT:
+ feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit));
+ if (feature_enabled) {
+ smu_v13_0_7_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_TEMP,
+ &min_value, &max_value);
+ if (!min_value && !max_value) {
+ feature_enabled = false;
+ goto out;
+ }
+
+ smu_v13_0_7_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_PWM,
+ &min_value, &max_value);
+ if (!min_value && !max_value) {
+ feature_enabled = false;
+ goto out;
+ }
+ }
+ break;
+ default:
+ feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit));
+ break;
+ }
+
+out:
+ return feature_enabled;
}
static void smu_v13_0_7_get_od_setting_limits(struct smu_context *smu,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
index 06a81533759c..802b3fbbeb8e 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
@@ -284,7 +284,6 @@ int smu_v14_0_check_fw_version(struct smu_context *smu)
"smu fw program = %d, smu fw version = 0x%08x (%d.%d.%d)\n",
smu->smc_driver_if_version, if_version,
smu_program, smu_version, smu_major, smu_minor, smu_debug);
- dev_info(adev->dev, "SMU driver if version not matched\n");
}
return ret;
diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c
index 9e19d8c17730..677c52c0d99a 100644
--- a/drivers/gpu/drm/ast/ast_dp501.c
+++ b/drivers/gpu/drm/ast/ast_dp501.c
@@ -436,7 +436,7 @@ static void ast_init_analog(struct ast_device *ast)
/* Finally, clear bits [17:16] of SCU2c */
data = ast_read32(ast, 0x1202c);
data &= 0xfffcffff;
- ast_write32(ast, 0, data);
+ ast_write32(ast, 0x1202c, data);
/* Disable DVO */
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x00);
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 8f355df883d8..250bf8fa5167 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -1465,11 +1465,17 @@ EXPORT_SYMBOL(devm_drm_put_bridge);
static void drm_bridge_debugfs_show_bridge(struct drm_printer *p,
struct drm_bridge *bridge,
unsigned int idx,
- bool lingering)
+ bool lingering,
+ bool scoped)
{
+ unsigned int refcount = kref_read(&bridge->refcount);
+
+ if (scoped)
+ refcount--;
+
drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs);
- drm_printf(p, "\trefcount: %u%s\n", kref_read(&bridge->refcount),
+ drm_printf(p, "\trefcount: %u%s\n", refcount,
lingering ? " [lingering]" : "");
drm_printf(p, "\ttype: [%d] %s\n",
@@ -1503,10 +1509,10 @@ static int allbridges_show(struct seq_file *m, void *data)
mutex_lock(&bridge_lock);
list_for_each_entry(bridge, &bridge_list, list)
- drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false);
+ drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false, false);
list_for_each_entry(bridge, &bridge_lingering_list, list)
- drm_bridge_debugfs_show_bridge(&p, bridge, idx++, true);
+ drm_bridge_debugfs_show_bridge(&p, bridge, idx++, true, false);
mutex_unlock(&bridge_lock);
@@ -1521,7 +1527,7 @@ static int encoder_bridges_show(struct seq_file *m, void *data)
unsigned int idx = 0;
drm_for_each_bridge_in_chain_scoped(encoder, bridge)
- drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false);
+ drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false, true);
return 0;
}
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c
index 6ee9b3df8735..be5e617ceb9f 100644
--- a/drivers/gpu/drm/drm_file.c
+++ b/drivers/gpu/drm/drm_file.c
@@ -233,7 +233,6 @@ static void drm_events_release(struct drm_file *file_priv)
void drm_file_free(struct drm_file *file)
{
struct drm_device *dev;
- int idx;
if (!file)
return;
@@ -250,11 +249,9 @@ void drm_file_free(struct drm_file *file)
drm_events_release(file);
- if (drm_core_check_feature(dev, DRIVER_MODESET) &&
- drm_dev_enter(dev, &idx)) {
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
drm_fb_release(file);
drm_property_destroy_user_blobs(dev, file);
- drm_dev_exit(idx);
}
if (drm_core_check_feature(dev, DRIVER_SYNCOBJ))
diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c
index e6b5b06de148..f3e40d1e6098 100644
--- a/drivers/gpu/drm/drm_ioc32.c
+++ b/drivers/gpu/drm/drm_ioc32.c
@@ -28,6 +28,7 @@
* IN THE SOFTWARE.
*/
#include <linux/compat.h>
+#include <linux/nospec.h>
#include <linux/ratelimit.h>
#include <linux/export.h>
@@ -374,6 +375,7 @@ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (nr >= ARRAY_SIZE(drm_compat_ioctls))
return drm_ioctl(filp, cmd, arg);
+ nr = array_index_nospec(nr, ARRAY_SIZE(drm_compat_ioctls));
fn = drm_compat_ioctls[nr].fn;
if (!fn)
return drm_ioctl(filp, cmd, arg);
diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c
index 802bc4608abf..d12db9b0bab8 100644
--- a/drivers/gpu/drm/drm_mode_config.c
+++ b/drivers/gpu/drm/drm_mode_config.c
@@ -577,13 +577,10 @@ void drm_mode_config_cleanup(struct drm_device *dev)
*/
WARN_ON(!list_empty(&dev->mode_config.fb_list));
list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
- if (list_empty(&fb->filp_head) || drm_framebuffer_read_refcount(fb) > 1) {
- struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]");
+ struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]");
- drm_printf(&p, "framebuffer[%u]:\n", fb->base.id);
- drm_framebuffer_print_info(&p, 1, fb);
- }
- list_del_init(&fb->filp_head);
+ drm_printf(&p, "framebuffer[%u]:\n", fb->base.id);
+ drm_framebuffer_print_info(&p, 1, fb);
drm_framebuffer_free(&fb->base.refcount);
}
diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c
index a3ff21b2f69f..4ad36ed18724 100644
--- a/drivers/gpu/drm/i915/display/g4x_dp.c
+++ b/drivers/gpu/drm/i915/display/g4x_dp.c
@@ -137,7 +137,7 @@ static void intel_dp_prepare(struct intel_encoder *encoder,
intel_dp->DP |= DP_SYNC_VS_HIGH;
intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT;
- if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
+ if (pipe_config->enhanced_framing)
intel_dp->DP |= DP_ENHANCED_FRAMING;
intel_dp->DP |= DP_PIPE_SEL_IVB(crtc->pipe);
diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c
index 9230792960f2..bc53449cdd89 100644
--- a/drivers/gpu/drm/i915/display/icl_dsi.c
+++ b/drivers/gpu/drm/i915/display/icl_dsi.c
@@ -889,7 +889,7 @@ gen11_dsi_set_transcoder_timings(struct intel_encoder *encoder,
* non-compressed link speeds, and simplifies down to the ratio between
* compressed and non-compressed bpp.
*/
- if (crtc_state->dsc.compression_enable) {
+ if (is_vid_mode(intel_dsi) && crtc_state->dsc.compression_enable) {
mul = fxp_q4_to_int(crtc_state->dsc.compressed_bpp_x16);
div = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format);
}
@@ -1503,7 +1503,7 @@ static void gen11_dsi_get_timings(struct intel_encoder *encoder,
struct drm_display_mode *adjusted_mode =
&pipe_config->hw.adjusted_mode;
- if (pipe_config->dsc.compressed_bpp_x16) {
+ if (is_vid_mode(intel_dsi) && pipe_config->dsc.compressed_bpp_x16) {
int div = fxp_q4_to_int(pipe_config->dsc.compressed_bpp_x16);
int mul = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format);
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c
index 37801c744b05..13e4dde3daa9 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.c
@@ -2930,6 +2930,53 @@ static int intel_cdclk_update_crtc_min_cdclk(struct intel_atomic_state *state,
return 0;
}
+static int intel_cdclk_update_crtc_min_voltage_level(struct intel_atomic_state *state,
+ struct intel_crtc *crtc,
+ u8 old_min_voltage_level,
+ u8 new_min_voltage_level,
+ bool *need_cdclk_calc)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_cdclk_state *cdclk_state;
+ bool allow_voltage_level_decrease = intel_any_crtc_needs_modeset(state);
+ int ret;
+
+ if (new_min_voltage_level == old_min_voltage_level)
+ return 0;
+
+ if (!allow_voltage_level_decrease &&
+ new_min_voltage_level < old_min_voltage_level)
+ return 0;
+
+ cdclk_state = intel_atomic_get_cdclk_state(state);
+ if (IS_ERR(cdclk_state))
+ return PTR_ERR(cdclk_state);
+
+ old_min_voltage_level = cdclk_state->min_voltage_level[crtc->pipe];
+
+ if (new_min_voltage_level == old_min_voltage_level)
+ return 0;
+
+ if (!allow_voltage_level_decrease &&
+ new_min_voltage_level < old_min_voltage_level)
+ return 0;
+
+ cdclk_state->min_voltage_level[crtc->pipe] = new_min_voltage_level;
+
+ ret = intel_atomic_lock_global_state(&cdclk_state->base);
+ if (ret)
+ return ret;
+
+ *need_cdclk_calc = true;
+
+ drm_dbg_kms(display->drm,
+ "[CRTC:%d:%s] min voltage level: %d -> %d\n",
+ crtc->base.base.id, crtc->base.name,
+ old_min_voltage_level, new_min_voltage_level);
+
+ return 0;
+}
+
int intel_cdclk_update_dbuf_bw_min_cdclk(struct intel_atomic_state *state,
int old_min_cdclk, int new_min_cdclk,
bool *need_cdclk_calc)
@@ -3345,6 +3392,13 @@ static int intel_crtcs_calc_min_cdclk(struct intel_atomic_state *state,
need_cdclk_calc);
if (ret)
return ret;
+
+ ret = intel_cdclk_update_crtc_min_voltage_level(state, crtc,
+ old_crtc_state->min_voltage_level,
+ new_crtc_state->min_voltage_level,
+ need_cdclk_calc);
+ if (ret)
+ return ret;
}
return 0;
diff --git a/drivers/gpu/drm/sysfb/efidrm.c b/drivers/gpu/drm/sysfb/efidrm.c
index 1b683d55d6ea..ac48bfa47e08 100644
--- a/drivers/gpu/drm/sysfb/efidrm.c
+++ b/drivers/gpu/drm/sysfb/efidrm.c
@@ -151,7 +151,6 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv,
struct drm_sysfb_device *sysfb;
struct drm_device *dev;
struct resource *mem = NULL;
- void __iomem *screen_base = NULL;
struct drm_plane *primary_plane;
struct drm_crtc *crtc;
struct drm_encoder *encoder;
@@ -236,21 +235,38 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv,
mem_flags = efidrm_get_mem_flags(dev, res->start, vsize);
- if (mem_flags & EFI_MEMORY_WC)
- screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem));
- else if (mem_flags & EFI_MEMORY_UC)
- screen_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
- else if (mem_flags & EFI_MEMORY_WT)
- screen_base = devm_memremap(&pdev->dev, mem->start, resource_size(mem),
- MEMREMAP_WT);
- else if (mem_flags & EFI_MEMORY_WB)
- screen_base = devm_memremap(&pdev->dev, mem->start, resource_size(mem),
- MEMREMAP_WB);
- else
+ if (mem_flags & EFI_MEMORY_WC) {
+ void __iomem *screen_base = devm_ioremap_wc(&pdev->dev, mem->start,
+ resource_size(mem));
+
+ if (!screen_base)
+ return ERR_PTR(-ENXIO);
+ iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base);
+ } else if (mem_flags & EFI_MEMORY_UC) {
+ void __iomem *screen_base = devm_ioremap(&pdev->dev, mem->start,
+ resource_size(mem));
+
+ if (!screen_base)
+ return ERR_PTR(-ENXIO);
+ iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base);
+ } else if (mem_flags & EFI_MEMORY_WT) {
+ void *screen_base = devm_memremap(&pdev->dev, mem->start,
+ resource_size(mem), MEMREMAP_WT);
+
+ if (IS_ERR(screen_base))
+ return ERR_CAST(screen_base);
+ iosys_map_set_vaddr(&sysfb->fb_addr, screen_base);
+ } else if (mem_flags & EFI_MEMORY_WB) {
+ void *screen_base = devm_memremap(&pdev->dev, mem->start,
+ resource_size(mem), MEMREMAP_WB);
+
+ if (IS_ERR(screen_base))
+ return ERR_CAST(screen_base);
+ iosys_map_set_vaddr(&sysfb->fb_addr, screen_base);
+ } else {
drm_err(dev, "invalid mem_flags: 0x%llx\n", mem_flags);
- if (!screen_base)
- return ERR_PTR(-ENOMEM);
- iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base);
+ return ERR_PTR(-EINVAL);
+ }
/*
* Modesetting
diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
index afb06598b6e1..0b625a52a598 100644
--- a/drivers/gpu/drm/xe/xe_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_pagefault.c
@@ -187,6 +187,12 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
goto unlock_vm;
}
+ if (xe_vma_read_only(vma) &&
+ pf->consumer.access_type != XE_PAGEFAULT_ACCESS_TYPE_READ) {
+ err = -EPERM;
+ goto unlock_vm;
+ }
+
atomic = xe_pagefault_access_is_atomic(pf->consumer.access_type);
if (xe_vma_is_cpu_addr_mirror(vma))
diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c
index bdbdbbf6a678..9261a8412b64 100644
--- a/drivers/gpu/drm/xe/xe_pxp.c
+++ b/drivers/gpu/drm/xe/xe_pxp.c
@@ -532,7 +532,7 @@ static int __exec_queue_add(struct xe_pxp *pxp, struct xe_exec_queue *q)
static int pxp_start(struct xe_pxp *pxp, u8 type)
{
int ret = 0;
- bool restart = false;
+ bool restart;
if (!xe_pxp_is_enabled(pxp))
return -ENODEV;
@@ -561,6 +561,8 @@ static int pxp_start(struct xe_pxp *pxp, u8 type)
msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS)))
return -ETIMEDOUT;
+ restart = false;
+
mutex_lock(&pxp->mutex);
/* If PXP is not already active, turn it on */
@@ -603,6 +605,7 @@ static int pxp_start(struct xe_pxp *pxp, u8 type)
drm_err(&pxp->xe->drm, "PXP termination failed before start\n");
mutex_lock(&pxp->mutex);
pxp->status = XE_PXP_ERROR;
+ complete_all(&pxp->termination);
goto out_unlock;
}
@@ -890,11 +893,6 @@ int xe_pxp_pm_suspend(struct xe_pxp *pxp)
pxp->key_instance++;
needs_queue_inval = true;
break;
- default:
- drm_err(&pxp->xe->drm, "unexpected state during PXP suspend: %u",
- pxp->status);
- ret = -EIO;
- goto out;
}
/*
@@ -919,7 +917,6 @@ int xe_pxp_pm_suspend(struct xe_pxp *pxp)
pxp->last_suspend_key_instance = pxp->key_instance;
-out:
return ret;
}
diff --git a/drivers/hid/hid-appletb-kbd.c b/drivers/hid/hid-appletb-kbd.c
index b00687e67ce8..0b10cff465e1 100644
--- a/drivers/hid/hid-appletb-kbd.c
+++ b/drivers/hid/hid-appletb-kbd.c
@@ -477,7 +477,7 @@ static int appletb_kbd_suspend(struct hid_device *hdev, pm_message_t msg)
return 0;
}
-static int appletb_kbd_reset_resume(struct hid_device *hdev)
+static int appletb_kbd_resume(struct hid_device *hdev)
{
struct appletb_kbd *kbd = hid_get_drvdata(hdev);
@@ -503,7 +503,8 @@ static struct hid_driver appletb_kbd_hid_driver = {
.input_configured = appletb_kbd_input_configured,
#ifdef CONFIG_PM
.suspend = appletb_kbd_suspend,
- .reset_resume = appletb_kbd_reset_resume,
+ .resume = appletb_kbd_resume,
+ .reset_resume = appletb_kbd_resume,
#endif
.driver.dev_groups = appletb_kbd_groups,
};
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index a5b3a8ca2fcb..f5587b786f87 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2057,9 +2057,10 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
rsize = max_buffer_size;
if (csize < rsize) {
- dbg_hid("report %d is too short, (%d < %d)\n", report->id,
- csize, rsize);
- memset(cdata + csize, 0, rsize - csize);
+ hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %d)\n",
+ report->id, rsize, csize);
+ ret = -EINVAL;
+ goto out;
}
if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 02d83c3bd73d..65bfad405ac5 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -4487,10 +4487,12 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (!ret)
ret = hidpp_ff_init(hidpp, &data);
- if (ret)
+ if (ret) {
hid_warn(hidpp->hid_dev,
"Unable to initialize force feedback support, errno %d\n",
ret);
+ ret = 0;
+ }
}
/*
@@ -4668,6 +4670,8 @@ static const struct hid_device_id hidpp_devices[] = {
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb038) },
{ /* Slim Solar+ K980 Keyboard over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb391) },
+ { /* MX Master 4 mouse over Bluetooth */
+ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb042) },
{}
};
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index b8a748bbf0fd..e82a3c4e5b44 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -526,12 +526,19 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
dev_warn(&hdev->dev, "failed to fetch feature %d\n",
report->id);
} else {
+ /* The report ID in the request and the response should match */
+ if (report->id != buf[0]) {
+ hid_err(hdev, "Returned feature report did not match the request\n");
+ goto free;
+ }
+
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf,
size, 0);
if (ret)
dev_warn(&hdev->dev, "failed to report feature\n");
}
+free:
kfree(buf);
}
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 9b2c710f8da1..da1f0ea85625 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -1208,10 +1208,20 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
switch (data[0]) {
case 0x04:
+ if (len < 32) {
+ dev_warn(wacom->pen_input->dev.parent,
+ "Report 0x04 too short: %zu bytes\n", len);
+ break;
+ }
wacom_intuos_bt_process_data(wacom, data + i);
i += 10;
fallthrough;
case 0x03:
+ if (i == 1 && len < 22) {
+ dev_warn(wacom->pen_input->dev.parent,
+ "Report 0x03 too short: %zu bytes\n", len);
+ break;
+ }
wacom_intuos_bt_process_data(wacom, data + i);
i += 10;
wacom_intuos_bt_process_data(wacom, data + i);
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index adba3564d9f1..baa864cac375 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -314,15 +314,17 @@ int mshv_region_pin(struct mshv_mem_region *region)
ret = pin_user_pages_fast(userspace_addr, nr_pages,
FOLL_WRITE | FOLL_LONGTERM,
pages);
- if (ret < 0)
+ if (ret != nr_pages)
goto release_pages;
}
return 0;
release_pages:
+ if (ret > 0)
+ done_count += ret;
mshv_region_invalidate_pages(region, 0, done_count);
- return ret;
+ return ret < 0 ? ret : -ENOMEM;
}
static int mshv_region_chunk_unmap(struct mshv_mem_region *region,
diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c
index a1445799e23d..b685d9954df4 100644
--- a/drivers/hwmon/asus-ec-sensors.c
+++ b/drivers/hwmon/asus-ec-sensors.c
@@ -111,6 +111,8 @@ enum ec_sensors {
ec_sensor_temp_mb,
/* "T_Sensor" temperature sensor reading [℃] */
ec_sensor_temp_t_sensor,
+ /* like ec_sensor_temp_t_sensor, but at an alternate address [℃] */
+ ec_sensor_temp_t_sensor_alt1,
/* VRM temperature [℃] */
ec_sensor_temp_vrm,
/* VRM east (right) temperature [℃] */
@@ -160,6 +162,7 @@ enum ec_sensors {
#define SENSOR_TEMP_CPU_PACKAGE BIT(ec_sensor_temp_cpu_package)
#define SENSOR_TEMP_MB BIT(ec_sensor_temp_mb)
#define SENSOR_TEMP_T_SENSOR BIT(ec_sensor_temp_t_sensor)
+#define SENSOR_TEMP_T_SENSOR_ALT1 BIT(ec_sensor_temp_t_sensor_alt1)
#define SENSOR_TEMP_VRM BIT(ec_sensor_temp_vrm)
#define SENSOR_TEMP_VRME BIT(ec_sensor_temp_vrme)
#define SENSOR_TEMP_VRMW BIT(ec_sensor_temp_vrmw)
@@ -279,6 +282,8 @@ static const struct ec_sensor_info sensors_family_amd_600[] = {
EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33),
[ec_sensor_temp_t_sensor] =
EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x36),
+ [ec_sensor_temp_t_sensor_alt1] =
+ EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x37),
[ec_sensor_fan_cpu_opt] =
EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
[ec_sensor_temp_water_in] =
@@ -509,7 +514,7 @@ static const struct ec_board_info board_info_prime_x570_pro = {
static const struct ec_board_info board_info_prime_x670e_pro_wifi = {
.sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
SENSOR_TEMP_MB | SENSOR_TEMP_VRM |
- SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT,
+ SENSOR_TEMP_T_SENSOR_ALT1 | SENSOR_FAN_CPU_OPT,
.mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
.family = family_amd_600_series,
};
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 89928d38831b..42cc6068bb08 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -420,6 +420,12 @@ static ssize_t occ_show_freq_2(struct device *dev,
return sysfs_emit(buf, "%u\n", val);
}
+static u64 occ_get_powr_avg(u64 accum, u32 samples)
+{
+ return (samples == 0) ? 0 :
+ mul_u64_u32_div(accum, 1000000UL, samples);
+}
+
static ssize_t occ_show_power_1(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -441,9 +447,8 @@ static ssize_t occ_show_power_1(struct device *dev,
val = get_unaligned_be16(&power->sensor_id);
break;
case 1:
- val = get_unaligned_be32(&power->accumulator) /
- get_unaligned_be32(&power->update_tag);
- val *= 1000000ULL;
+ val = occ_get_powr_avg(get_unaligned_be32(&power->accumulator),
+ get_unaligned_be32(&power->update_tag));
break;
case 2:
val = (u64)get_unaligned_be32(&power->update_tag) *
@@ -459,12 +464,6 @@ static ssize_t occ_show_power_1(struct device *dev,
return sysfs_emit(buf, "%llu\n", val);
}
-static u64 occ_get_powr_avg(u64 accum, u32 samples)
-{
- return (samples == 0) ? 0 :
- mul_u64_u32_div(accum, 1000000UL, samples);
-}
-
static ssize_t occ_show_power_2(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -725,7 +724,7 @@ static ssize_t occ_show_extended(struct device *dev,
switch (sattr->nr) {
case 0:
if (extn->flags & EXTN_FLAG_SENSOR_ID) {
- rc = sysfs_emit(buf, "%u",
+ rc = sysfs_emit(buf, "%u\n",
get_unaligned_be32(&extn->sensor_id));
} else {
rc = sysfs_emit(buf, "%4phN\n", extn->name);
diff --git a/drivers/hwmon/pmbus/ltc4286.c b/drivers/hwmon/pmbus/ltc4286.c
index aabd0bcdfeee..8715d380784a 100644
--- a/drivers/hwmon/pmbus/ltc4286.c
+++ b/drivers/hwmon/pmbus/ltc4286.c
@@ -173,3 +173,4 @@ module_i2c_driver(ltc4286_driver);
MODULE_AUTHOR("Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>");
MODULE_DESCRIPTION("PMBUS driver for LTC4286 and compatibles");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c
index 6a4a978eca7e..24c1f961c766 100644
--- a/drivers/hwmon/pmbus/pxe1610.c
+++ b/drivers/hwmon/pmbus/pxe1610.c
@@ -104,7 +104,10 @@ static int pxe1610_probe(struct i2c_client *client)
* By default this device doesn't boot to page 0, so set page 0
* to access all pmbus registers.
*/
- i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+ if (ret < 0)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to set page 0\n");
/* Read Manufacturer id */
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c
index ca2bfa25eb04..249974c13aa3 100644
--- a/drivers/hwmon/pmbus/tps53679.c
+++ b/drivers/hwmon/pmbus/tps53679.c
@@ -103,10 +103,10 @@ static int tps53679_identify_chip(struct i2c_client *client,
}
ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
- if (ret < 0)
- return ret;
+ if (ret <= 0)
+ return ret < 0 ? ret : -EIO;
- /* Adjust length if null terminator if present */
+ /* Adjust length if null terminator is present */
buf_len = (buf[ret - 1] != '\x00' ? ret : ret - 1);
id_len = strlen(id);
@@ -175,8 +175,8 @@ static int tps53676_identify(struct i2c_client *client,
ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
if (ret < 0)
return ret;
- if (strncmp("TI\x53\x67\x60", buf, 5)) {
- dev_err(&client->dev, "Unexpected device ID: %s\n", buf);
+ if (ret != 6 || memcmp(buf, "TI\x53\x67\x60\x00", 6)) {
+ dev_err(&client->dev, "Unexpected device ID: %*ph\n", ret, buf);
return -ENODEV;
}
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 09ba55bae1fa..7d0afdc7d886 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1220,6 +1220,8 @@ config I2C_TEGRA
tristate "NVIDIA Tegra internal I2C controller"
depends on ARCH_TEGRA || (COMPILE_TEST && (ARC || ARM || ARM64 || M68K || RISCV || SUPERH || SPARC))
# COMPILE_TEST needs architectures with readsX()/writesX() primitives
+ depends on PINCTRL
+ # ARCH_TEGRA implies PINCTRL, but the COMPILE_TEST side doesn't.
help
If you say yes to this option, support will be included for the
I2C controller embedded in NVIDIA Tegra SOCs
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index e533460bccc3..a9aed411e319 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -1837,8 +1837,11 @@ static int tegra_i2c_probe(struct platform_device *pdev)
*
* VI I2C device shouldn't be marked as IRQ-safe because VI I2C won't
* be used for atomic transfers. ACPI device is not IRQ safe also.
+ *
+ * Devices with pinctrl states cannot be marked IRQ-safe as the pinctrl
+ * state transitions during runtime PM require mutexes.
*/
- if (!IS_VI(i2c_dev) && !has_acpi_companion(i2c_dev->dev))
+ if (!IS_VI(i2c_dev) && !has_acpi_companion(i2c_dev->dev) && !i2c_dev->dev->pins)
pm_runtime_irq_safe(i2c_dev->dev);
pm_runtime_enable(i2c_dev->dev);
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 9f5d4d2cb325..83dcac17a042 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -998,6 +998,8 @@ static int adxl313_buffer_predisable(struct iio_dev *indio_dev)
ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL,
FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK, ADXL313_FIFO_BYPASS));
+ if (ret)
+ return ret;
ret = regmap_write(data->regmap, ADXL313_REG_INT_ENABLE, 0);
if (ret)
diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c
index 5fc7f814b907..5aadc0477102 100644
--- a/drivers/iio/accel/adxl355_core.c
+++ b/drivers/iio/accel/adxl355_core.c
@@ -745,7 +745,7 @@ static const struct iio_chan_spec adxl355_channels[] = {
BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 3,
.scan_type = {
- .sign = 's',
+ .sign = 'u',
.realbits = 12,
.storagebits = 16,
.endianness = IIO_BE,
diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c
index 9f6c0e02575a..f716bd2154bc 100644
--- a/drivers/iio/accel/adxl380.c
+++ b/drivers/iio/accel/adxl380.c
@@ -860,7 +860,7 @@ static int adxl380_set_fifo_samples(struct adxl380_state *st)
ret = regmap_update_bits(st->regmap, ADXL380_FIFO_CONFIG_0_REG,
ADXL380_FIFO_SAMPLES_8_MSK,
FIELD_PREP(ADXL380_FIFO_SAMPLES_8_MSK,
- (fifo_samples & BIT(8))));
+ !!(fifo_samples & BIT(8))));
if (ret)
return ret;
diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c
index 2de8a718d62a..3c2a93f11460 100644
--- a/drivers/iio/adc/ade9000.c
+++ b/drivers/iio/adc/ade9000.c
@@ -787,7 +787,7 @@ static int ade9000_iio_push_streaming(struct iio_dev *indio_dev)
ADE9000_MIDDLE_PAGE_BIT);
if (ret) {
dev_err_ratelimited(dev, "IRQ0 WFB write fail");
- return IRQ_HANDLED;
+ return ret;
}
ade9000_configure_scan(indio_dev, ADE9000_REG_WF_BUFF);
@@ -1123,7 +1123,7 @@ static int ade9000_write_raw(struct iio_dev *indio_dev,
tmp &= ~ADE9000_PHASE_C_POS_BIT;
switch (tmp) {
- case ADE9000_REG_AWATTOS:
+ case ADE9000_REG_AWATT:
return regmap_write(st->regmap,
ADE9000_ADDR_ADJUST(ADE9000_REG_AWATTOS,
chan->channel), val);
@@ -1706,19 +1706,19 @@ static int ade9000_probe(struct spi_device *spi)
init_completion(&st->reset_completion);
- ret = ade9000_request_irq(dev, "irq0", ade9000_irq0_thread, indio_dev);
+ ret = devm_mutex_init(dev, &st->lock);
if (ret)
return ret;
- ret = ade9000_request_irq(dev, "irq1", ade9000_irq1_thread, indio_dev);
+ ret = ade9000_request_irq(dev, "irq0", ade9000_irq0_thread, indio_dev);
if (ret)
return ret;
- ret = ade9000_request_irq(dev, "dready", ade9000_dready_thread, indio_dev);
+ ret = ade9000_request_irq(dev, "irq1", ade9000_irq1_thread, indio_dev);
if (ret)
return ret;
- ret = devm_mutex_init(dev, &st->lock);
+ ret = ade9000_request_irq(dev, "dready", ade9000_dready_thread, indio_dev);
if (ret)
return ret;
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index bf2bfd6bdc41..9df6e7f68f19 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -415,6 +415,7 @@ static int aspeed_adc_vref_config(struct iio_dev *indio_dev)
}
adc_engine_control_reg_val =
readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ adc_engine_control_reg_val &= ~ASPEED_ADC_REF_VOLTAGE;
ret = devm_regulator_get_enable_read_voltage(data->dev, "vref");
if (ret < 0 && ret != -ENODEV)
diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c
index 28aa6b80160c..be1cc2e77862 100644
--- a/drivers/iio/adc/ti-adc161s626.c
+++ b/drivers/iio/adc/ti-adc161s626.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/spi/spi.h>
+#include <linux/unaligned.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/buffer.h>
@@ -70,8 +71,7 @@ struct ti_adc_data {
u8 read_size;
u8 shift;
-
- u8 buffer[16] __aligned(IIO_DMA_MINALIGN);
+ u8 buf[3] __aligned(IIO_DMA_MINALIGN);
};
static int ti_adc_read_measurement(struct ti_adc_data *data,
@@ -80,26 +80,20 @@ static int ti_adc_read_measurement(struct ti_adc_data *data,
int ret;
switch (data->read_size) {
- case 2: {
- __be16 buf;
-
- ret = spi_read(data->spi, (void *) &buf, 2);
+ case 2:
+ ret = spi_read(data->spi, data->buf, 2);
if (ret)
return ret;
- *val = be16_to_cpu(buf);
+ *val = get_unaligned_be16(data->buf);
break;
- }
- case 3: {
- __be32 buf;
-
- ret = spi_read(data->spi, (void *) &buf, 3);
+ case 3:
+ ret = spi_read(data->spi, data->buf, 3);
if (ret)
return ret;
- *val = be32_to_cpu(buf) >> 8;
+ *val = get_unaligned_be24(data->buf);
break;
- }
default:
return -EINVAL;
}
@@ -114,15 +108,20 @@ static irqreturn_t ti_adc_trigger_handler(int irq, void *private)
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct ti_adc_data *data = iio_priv(indio_dev);
- int ret;
+ struct {
+ s16 data;
+ aligned_s64 timestamp;
+ } scan = { };
+ int ret, val;
+
+ ret = ti_adc_read_measurement(data, &indio_dev->channels[0], &val);
+ if (ret)
+ goto exit_notify_done;
- ret = ti_adc_read_measurement(data, &indio_dev->channels[0],
- (int *) &data->buffer);
- if (!ret)
- iio_push_to_buffers_with_timestamp(indio_dev,
- data->buffer,
- iio_get_time_ns(indio_dev));
+ scan.data = val;
+ iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev));
+ exit_notify_done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c
index c9cedc59cdcd..79be71b4de96 100644
--- a/drivers/iio/adc/ti-ads1119.c
+++ b/drivers/iio/adc/ti-ads1119.c
@@ -274,12 +274,15 @@ static int ads1119_single_conversion(struct ads1119_state *st,
ret = pm_runtime_resume_and_get(dev);
if (ret)
- goto pdown;
+ return ret;
ret = ads1119_configure_channel(st, mux, gain, datarate);
if (ret)
goto pdown;
+ if (st->client->irq)
+ reinit_completion(&st->completion);
+
ret = i2c_smbus_write_byte(st->client, ADS1119_CMD_START_SYNC);
if (ret)
goto pdown;
@@ -735,10 +738,8 @@ static int ads1119_probe(struct i2c_client *client)
return dev_err_probe(dev, ret, "Failed to setup IIO buffer\n");
if (client->irq > 0) {
- ret = devm_request_threaded_irq(dev, client->irq,
- ads1119_irq_handler,
- NULL, IRQF_ONESHOT,
- "ads1119", indio_dev);
+ ret = devm_request_irq(dev, client->irq, ads1119_irq_handler,
+ IRQF_NO_THREAD, "ads1119", indio_dev);
if (ret)
return dev_err_probe(dev, ret,
"Failed to allocate irq\n");
diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c
index bbe1ce577789..cdc624889559 100644
--- a/drivers/iio/adc/ti-ads7950.c
+++ b/drivers/iio/adc/ti-ads7950.c
@@ -427,13 +427,15 @@ static int ti_ads7950_set(struct gpio_chip *chip, unsigned int offset,
static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset)
{
struct ti_ads7950_state *st = gpiochip_get_data(chip);
+ bool state;
int ret;
mutex_lock(&st->slock);
/* If set as output, return the output */
if (st->gpio_cmd_settings_bitmask & BIT(offset)) {
- ret = st->cmd_settings_bitmask & BIT(offset);
+ state = st->cmd_settings_bitmask & BIT(offset);
+ ret = 0;
goto out;
}
@@ -444,7 +446,7 @@ static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset)
if (ret)
goto out;
- ret = ((st->single_rx >> 12) & BIT(offset)) ? 1 : 0;
+ state = (st->single_rx >> 12) & BIT(offset);
/* Revert back to original settings */
st->cmd_settings_bitmask &= ~TI_ADS7950_CR_GPIO_DATA;
@@ -456,7 +458,7 @@ static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset)
out:
mutex_unlock(&st->slock);
- return ret;
+ return ret ?: state;
}
static int ti_ads7950_get_direction(struct gpio_chip *chip,
diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c
index cd47cb1c685c..6027e8d88b27 100644
--- a/drivers/iio/dac/ad5770r.c
+++ b/drivers/iio/dac/ad5770r.c
@@ -322,7 +322,7 @@ static int ad5770r_read_raw(struct iio_dev *indio_dev,
chan->address,
st->transf_buf, 2);
if (ret)
- return 0;
+ return ret;
buf16 = get_unaligned_le16(st->transf_buf);
*val = buf16 >> 2;
diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c
index 317e7b217ec6..d84e04e4b431 100644
--- a/drivers/iio/gyro/mpu3050-core.c
+++ b/drivers/iio/gyro/mpu3050-core.c
@@ -1129,11 +1129,16 @@ static int mpu3050_trigger_probe(struct iio_dev *indio_dev, int irq)
ret = iio_trigger_register(mpu3050->trig);
if (ret)
- return ret;
+ goto err_iio_trigger;
indio_dev->trig = iio_trigger_get(mpu3050->trig);
return 0;
+
+err_iio_trigger:
+ free_irq(mpu3050->irq, mpu3050->trig);
+
+ return ret;
}
int mpu3050_common_probe(struct device *dev,
@@ -1221,12 +1226,6 @@ int mpu3050_common_probe(struct device *dev,
goto err_power_down;
}
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(dev, "device register failed\n");
- goto err_cleanup_buffer;
- }
-
dev_set_drvdata(dev, indio_dev);
/* Check if we have an assigned IRQ to use as trigger */
@@ -1249,9 +1248,20 @@ int mpu3050_common_probe(struct device *dev,
pm_runtime_use_autosuspend(dev);
pm_runtime_put(dev);
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "device register failed\n");
+ goto err_iio_device_register;
+ }
+
return 0;
-err_cleanup_buffer:
+err_iio_device_register:
+ pm_runtime_get_sync(dev);
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
+ if (irq)
+ free_irq(mpu3050->irq, mpu3050->trig);
iio_triggered_buffer_cleanup(indio_dev);
err_power_down:
mpu3050_power_down(mpu3050);
@@ -1264,13 +1274,13 @@ void mpu3050_common_remove(struct device *dev)
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+ iio_device_unregister(indio_dev);
pm_runtime_get_sync(dev);
pm_runtime_put_noidle(dev);
pm_runtime_disable(dev);
- iio_triggered_buffer_cleanup(indio_dev);
if (mpu3050->irq)
- free_irq(mpu3050->irq, mpu3050);
- iio_device_unregister(indio_dev);
+ free_irq(mpu3050->irq, mpu3050->trig);
+ iio_triggered_buffer_cleanup(indio_dev);
mpu3050_power_down(mpu3050);
}
diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c
index 28f0dbd0226c..1f2af506f4bd 100644
--- a/drivers/iio/imu/adis16550.c
+++ b/drivers/iio/imu/adis16550.c
@@ -643,12 +643,12 @@ static int adis16550_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
switch (chan->type) {
case IIO_ANGL_VEL:
- ret = adis16550_get_accl_filter_freq(st, val);
+ ret = adis16550_get_gyro_filter_freq(st, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_ACCEL:
- ret = adis16550_get_gyro_filter_freq(st, val);
+ ret = adis16550_get_accl_filter_freq(st, val);
if (ret)
return ret;
return IIO_VAL_INT;
@@ -681,9 +681,9 @@ static int adis16550_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
switch (chan->type) {
case IIO_ANGL_VEL:
- return adis16550_set_accl_filter_freq(st, val);
- case IIO_ACCEL:
return adis16550_set_gyro_filter_freq(st, val);
+ case IIO_ACCEL:
+ return adis16550_set_accl_filter_freq(st, val);
default:
return -EINVAL;
}
diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
index 5f47708b4c5d..4abb83b75e2e 100644
--- a/drivers/iio/imu/bmi160/bmi160_core.c
+++ b/drivers/iio/imu/bmi160/bmi160_core.c
@@ -573,12 +573,16 @@ static int bmi160_config_pin(struct regmap *regmap, enum bmi160_int_pin pin,
int_out_ctrl_shift = BMI160_INT1_OUT_CTRL_SHIFT;
int_latch_mask = BMI160_INT1_LATCH_MASK;
int_map_mask = BMI160_INT1_MAP_DRDY_EN;
+ pin_name = "INT1";
break;
case BMI160_PIN_INT2:
int_out_ctrl_shift = BMI160_INT2_OUT_CTRL_SHIFT;
int_latch_mask = BMI160_INT2_LATCH_MASK;
int_map_mask = BMI160_INT2_MAP_DRDY_EN;
+ pin_name = "INT2";
break;
+ default:
+ return -EINVAL;
}
int_out_ctrl_mask = BMI160_INT_OUT_CTRL_MASK << int_out_ctrl_shift;
@@ -612,17 +616,8 @@ static int bmi160_config_pin(struct regmap *regmap, enum bmi160_int_pin pin,
ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_MAP,
int_map_mask, int_map_mask,
write_usleep);
- if (ret) {
- switch (pin) {
- case BMI160_PIN_INT1:
- pin_name = "INT1";
- break;
- case BMI160_PIN_INT2:
- pin_name = "INT2";
- break;
- }
+ if (ret)
dev_err(dev, "Failed to configure %s IRQ pin", pin_name);
- }
return ret;
}
diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c
index 303bc308f80a..c96fec2ebb3e 100644
--- a/drivers/iio/imu/bno055/bno055.c
+++ b/drivers/iio/imu/bno055/bno055.c
@@ -64,7 +64,7 @@
#define BNO055_GRAVITY_DATA_X_LSB_REG 0x2E
#define BNO055_GRAVITY_DATA_Y_LSB_REG 0x30
#define BNO055_GRAVITY_DATA_Z_LSB_REG 0x32
-#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2)
+#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2 + 1)
#define BNO055_TEMP_REG 0x34
#define BNO055_CALIB_STAT_REG 0x35
#define BNO055_CALIB_STAT_MAGN_SHIFT 0
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
index 55d877745575..5b28a3ffcc3d 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
@@ -225,6 +225,10 @@ static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor,
const struct st_lsm6dsx_reg *batch_reg;
u8 data;
+ /* Only internal sensors have a FIFO ODR configuration register. */
+ if (sensor->id >= ARRAY_SIZE(hw->settings->batch))
+ return 0;
+
batch_reg = &hw->settings->batch[sensor->id];
if (batch_reg->addr) {
int val;
@@ -858,12 +862,21 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
int i, ret;
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+ const struct iio_dev_attr **attrs;
+
if (!hw->iio_devs[i])
continue;
+ /*
+ * For the accelerometer, allow setting FIFO sampling frequency
+ * values different from the sensor sampling frequency, which
+ * may be needed to keep FIFO data rate low while sampling
+ * acceleration data at high rates for accurate event detection.
+ */
+ attrs = i == ST_LSM6DSX_ID_ACC ? st_lsm6dsx_buffer_attrs : NULL;
ret = devm_iio_kfifo_buffer_setup_ext(hw->dev, hw->iio_devs[i],
&st_lsm6dsx_buffer_ops,
- st_lsm6dsx_buffer_attrs);
+ attrs);
if (ret)
return ret;
}
diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c
index 963747927425..16aeb17067bc 100644
--- a/drivers/iio/light/vcnl4035.c
+++ b/drivers/iio/light/vcnl4035.c
@@ -103,17 +103,23 @@ static irqreturn_t vcnl4035_trigger_consumer_handler(int irq, void *p)
struct iio_dev *indio_dev = pf->indio_dev;
struct vcnl4035_data *data = iio_priv(indio_dev);
/* Ensure naturally aligned timestamp */
- u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)] __aligned(8) = { };
+ struct {
+ u16 als_data;
+ aligned_s64 timestamp;
+ } buffer = { };
+ unsigned int val;
int ret;
- ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, (int *)buffer);
+ ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, &val);
if (ret < 0) {
dev_err(&data->client->dev,
"Trigger consumer can't read from sensor.\n");
goto fail_read;
}
- iio_push_to_buffers_with_timestamp(indio_dev, buffer,
- iio_get_time_ns(indio_dev));
+
+ buffer.als_data = val;
+ iio_push_to_buffers_with_timestamp(indio_dev, &buffer,
+ iio_get_time_ns(indio_dev));
fail_read:
iio_trigger_notify_done(indio_dev->trig);
@@ -381,7 +387,7 @@ static const struct iio_chan_spec vcnl4035_channels[] = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
- .endianness = IIO_LE,
+ .endianness = IIO_CPU,
},
},
{
@@ -395,7 +401,7 @@ static const struct iio_chan_spec vcnl4035_channels[] = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
- .endianness = IIO_LE,
+ .endianness = IIO_CPU,
},
},
};
diff --git a/drivers/iio/light/veml6070.c b/drivers/iio/light/veml6070.c
index 6d4483c85f30..74d7246e5225 100644
--- a/drivers/iio/light/veml6070.c
+++ b/drivers/iio/light/veml6070.c
@@ -134,9 +134,7 @@ static int veml6070_read(struct veml6070_data *data)
if (ret < 0)
return ret;
- ret = (msb << 8) | lsb;
-
- return 0;
+ return (msb << 8) | lsb;
}
static const struct iio_chan_spec veml6070_channels[] = {
diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index e759f91a710a..5a5e6e4fbe34 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -19,8 +19,13 @@ struct dev_rot_state {
struct hid_sensor_common common_attributes;
struct hid_sensor_hub_attribute_info quaternion;
struct {
- s32 sampled_vals[4];
- aligned_s64 timestamp;
+ IIO_DECLARE_QUATERNION(s32, sampled_vals);
+ /*
+ * ABI regression avoidance: There are two copies of the same
+ * timestamp in case of userspace depending on broken alignment
+ * from older kernels.
+ */
+ aligned_s64 timestamp[2];
} scan;
int scale_pre_decml;
int scale_post_decml;
@@ -154,8 +159,19 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
if (!rot_state->timestamp)
rot_state->timestamp = iio_get_time_ns(indio_dev);
- iio_push_to_buffers_with_timestamp(indio_dev, &rot_state->scan,
- rot_state->timestamp);
+ /*
+ * ABI regression avoidance: IIO previously had an incorrect
+ * implementation of iio_push_to_buffers_with_timestamp() that
+ * put the timestamp in the last 8 bytes of the buffer, which
+ * was incorrect according to the IIO ABI. To avoid breaking
+ * userspace that may be depending on this broken behavior, we
+ * put the timestamp in both the correct place [0] and the old
+ * incorrect place [1].
+ */
+ rot_state->scan.timestamp[0] = rot_state->timestamp;
+ rot_state->scan.timestamp[1] = rot_state->timestamp;
+
+ iio_push_to_buffers(indio_dev, &rot_state->scan);
rot_state->timestamp = 0;
}
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 363d50949386..627e8950e451 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -313,6 +313,8 @@ static const struct xpad_device {
{ 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
{ 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE },
{ 0x1532, 0x0a29, "Razer Wolverine V2", 0, XTYPE_XBOXONE },
+ { 0x1532, 0x0a57, "Razer Wolverine V3 Pro (Wired)", 0, XTYPE_XBOX360 },
+ { 0x1532, 0x0a59, "Razer Wolverine V3 Pro (2.4 GHz Dongle)", 0, XTYPE_XBOX360 },
{ 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 },
{ 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
{ 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 },
@@ -360,6 +362,8 @@ static const struct xpad_device {
{ 0x1bad, 0xfd00, "Razer Onza TE", 0, XTYPE_XBOX360 },
{ 0x1bad, 0xfd01, "Razer Onza", 0, XTYPE_XBOX360 },
{ 0x1ee9, 0x1590, "ZOTAC Gaming Zone", 0, XTYPE_XBOX360 },
+ { 0x20bc, 0x5134, "BETOP BTP-KP50B Xinput Dongle", 0, XTYPE_XBOX360 },
+ { 0x20bc, 0x514a, "BETOP BTP-KP50C Xinput Dongle", 0, XTYPE_XBOX360 },
{ 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE },
{ 0x20d6, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE },
{ 0x20d6, 0x2064, "PowerA Wired Controller for Xbox", MAP_SHARE_BUTTON, XTYPE_XBOXONE },
@@ -562,6 +566,7 @@ static const struct usb_device_id xpad_table[] = {
XPAD_XBOX360_VENDOR(0x1a86), /* Nanjing Qinheng Microelectronics (WCH) */
XPAD_XBOX360_VENDOR(0x1bad), /* Harmonix Rock Band guitar and drums */
XPAD_XBOX360_VENDOR(0x1ee9), /* ZOTAC Technology Limited */
+ XPAD_XBOX360_VENDOR(0x20bc), /* BETOP wireless dongles */
XPAD_XBOX360_VENDOR(0x20d6), /* PowerA controllers */
XPAD_XBOXONE_VENDOR(0x20d6), /* PowerA controllers */
XPAD_XBOX360_VENDOR(0x2345), /* Machenike Controllers */
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
index dfdfb59cc8b5..fe52f15c0c10 100644
--- a/drivers/input/mouse/bcm5974.c
+++ b/drivers/input/mouse/bcm5974.c
@@ -286,6 +286,8 @@ struct bcm5974 {
const struct tp_finger *index[MAX_FINGERS]; /* finger index data */
struct input_mt_pos pos[MAX_FINGERS]; /* position array */
int slots[MAX_FINGERS]; /* slot assignments */
+ struct work_struct mode_reset_work;
+ unsigned long last_mode_reset;
};
/* trackpad finger block data, le16-aligned */
@@ -696,6 +698,32 @@ static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on)
return retval;
}
+/*
+ * Mode switches sent before the control response are ignored.
+ * Fixing this state requires switching to normal mode and waiting
+ * about 1ms before switching back to wellspring mode.
+ */
+static void bcm5974_mode_reset_work(struct work_struct *work)
+{
+ struct bcm5974 *dev = container_of(work, struct bcm5974, mode_reset_work);
+ int error;
+
+ guard(mutex)(&dev->pm_mutex);
+ dev->last_mode_reset = jiffies;
+
+ error = bcm5974_wellspring_mode(dev, false);
+ if (error) {
+ dev_err(&dev->intf->dev, "reset to normal mode failed\n");
+ return;
+ }
+
+ fsleep(1000);
+
+ error = bcm5974_wellspring_mode(dev, true);
+ if (error)
+ dev_err(&dev->intf->dev, "mode switch after reset failed\n");
+}
+
static void bcm5974_irq_button(struct urb *urb)
{
struct bcm5974 *dev = urb->context;
@@ -752,10 +780,20 @@ static void bcm5974_irq_trackpad(struct urb *urb)
if (dev->tp_urb->actual_length == 2)
goto exit;
- if (report_tp_state(dev, dev->tp_urb->actual_length))
+ if (report_tp_state(dev, dev->tp_urb->actual_length)) {
dprintk(1, "bcm5974: bad trackpad package, length: %d\n",
dev->tp_urb->actual_length);
+ /*
+ * Receiving a HID packet means we aren't in wellspring mode.
+ * If we haven't tried a reset in the last second, try now.
+ */
+ if (dev->tp_urb->actual_length == 8 &&
+ time_after(jiffies, dev->last_mode_reset + msecs_to_jiffies(1000))) {
+ schedule_work(&dev->mode_reset_work);
+ }
+ }
+
exit:
error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
if (error)
@@ -906,6 +944,7 @@ static int bcm5974_probe(struct usb_interface *iface,
dev->intf = iface;
dev->input = input_dev;
dev->cfg = *cfg;
+ INIT_WORK(&dev->mode_reset_work, bcm5974_mode_reset_work);
mutex_init(&dev->pm_mutex);
/* setup urbs */
@@ -998,6 +1037,7 @@ static void bcm5974_disconnect(struct usb_interface *iface)
{
struct bcm5974 *dev = usb_get_intfdata(iface);
+ disable_work_sync(&dev->mode_reset_work);
usb_set_intfdata(iface, NULL);
input_unregister_device(dev->input);
diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c
index ac4041a69fcd..61909e1a39e2 100644
--- a/drivers/input/rmi4/rmi_f54.c
+++ b/drivers/input/rmi4/rmi_f54.c
@@ -538,6 +538,8 @@ static void rmi_f54_work(struct work_struct *work)
int error;
int i;
+ mutex_lock(&f54->data_mutex);
+
report_size = rmi_f54_get_report_size(f54);
if (report_size == 0) {
dev_err(&fn->dev, "Bad report size, report type=%d\n",
@@ -546,8 +548,6 @@ static void rmi_f54_work(struct work_struct *work)
goto error; /* retry won't help */
}
- mutex_lock(&f54->data_mutex);
-
/*
* Need to check if command has completed.
* If not try again later.
diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h
index d2cf940b105a..8ebdf4fb9030 100644
--- a/drivers/input/serio/i8042-acpipnpio.h
+++ b/drivers/input/serio/i8042-acpipnpio.h
@@ -1187,6 +1187,13 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = {
.driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "X6KK45xU_X6SP45xU"),
+ },
+ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
+ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
{
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "WUJIE Series-X5SP4NAG"),
diff --git a/drivers/interconnect/qcom/sm8450.c b/drivers/interconnect/qcom/sm8450.c
index 669a638bf3ef..c88327d200ac 100644
--- a/drivers/interconnect/qcom/sm8450.c
+++ b/drivers/interconnect/qcom/sm8450.c
@@ -800,7 +800,7 @@ static struct qcom_icc_node qhs_compute_cfg = {
.channels = 1,
.buswidth = 4,
.num_links = 1,
- .link_nodes = { MASTER_CDSP_NOC_CFG },
+ .link_nodes = { &qhm_nsp_noc_config },
};
static struct qcom_icc_node qhs_cpr_cx = {
@@ -874,7 +874,7 @@ static struct qcom_icc_node qhs_lpass_cfg = {
.channels = 1,
.buswidth = 4,
.num_links = 1,
- .link_nodes = { MASTER_CNOC_LPASS_AG_NOC },
+ .link_nodes = { &qhm_config_noc },
};
static struct qcom_icc_node qhs_mss_cfg = {
diff --git a/drivers/iommu/generic_pt/fmt/amdv1.h b/drivers/iommu/generic_pt/fmt/amdv1.h
index 3b2c41d9654d..8d11b08291d7 100644
--- a/drivers/iommu/generic_pt/fmt/amdv1.h
+++ b/drivers/iommu/generic_pt/fmt/amdv1.h
@@ -191,7 +191,7 @@ static inline enum pt_entry_type amdv1pt_load_entry_raw(struct pt_state *pts)
}
#define pt_load_entry_raw amdv1pt_load_entry_raw
-static inline void
+static __always_inline void
amdv1pt_install_leaf_entry(struct pt_state *pts, pt_oaddr_t oa,
unsigned int oasz_lg2,
const struct pt_write_attrs *attrs)
diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h
index 3e33fe64feab..7e7a6e7abdee 100644
--- a/drivers/iommu/generic_pt/iommu_pt.h
+++ b/drivers/iommu/generic_pt/iommu_pt.h
@@ -1057,7 +1057,7 @@ size_t DOMAIN_NS(unmap_pages)(struct iommu_domain *domain, unsigned long iova,
pt_walk_range(&range, __unmap_range, &unmap);
- gather_range_pages(iotlb_gather, iommu_table, iova, len,
+ gather_range_pages(iotlb_gather, iommu_table, iova, unmap.unmapped,
&unmap.free_list);
return unmap.unmapped;
diff --git a/drivers/irqchip/irq-riscv-aplic-main.c b/drivers/irqchip/irq-riscv-aplic-main.c
index 9f53979b6962..d9afb6ae98cf 100644
--- a/drivers/irqchip/irq-riscv-aplic-main.c
+++ b/drivers/irqchip/irq-riscv-aplic-main.c
@@ -150,7 +150,7 @@ static void aplic_pm_remove(void *data)
struct device *dev = priv->dev;
list_del(&priv->head);
- if (dev->pm_domain)
+ if (dev->pm_domain && dev->of_node)
dev_pm_genpd_remove_notifier(dev);
}
@@ -165,7 +165,7 @@ static int aplic_pm_add(struct device *dev, struct aplic_priv *priv)
priv->saved_hw_regs.srcs = srcs;
list_add(&priv->head, &aplics);
- if (dev->pm_domain) {
+ if (dev->pm_domain && dev->of_node) {
priv->genpd_nb.notifier_call = aplic_pm_notifier;
ret = dev_pm_genpd_add_notifier(dev, &priv->genpd_nb);
if (ret)
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index ee652ef01534..83b0ddfbd5c9 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -1370,6 +1370,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
}
err_map:
fastrpc_buf_free(fl->cctx->remote_heap);
+ fl->cctx->remote_heap = NULL;
err_name:
kfree(name);
err:
@@ -2337,8 +2338,10 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
if (!err) {
src_perms = BIT(QCOM_SCM_VMID_HLOS);
- qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms,
+ err = qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms,
data->vmperms, data->vmcount);
+ if (err)
+ goto err_free_data;
}
}
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index d4612c659784..1e4a41ac428f 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -1337,19 +1337,13 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
/* check if we need to start the dev */
if (!mei_host_is_ready(dev)) {
if (mei_hw_is_ready(dev)) {
- /* synchronized by dev mutex */
- if (waitqueue_active(&dev->wait_hw_ready)) {
- dev_dbg(&dev->dev, "we need to start the dev.\n");
- dev->recvd_hw_ready = true;
- wake_up(&dev->wait_hw_ready);
- } else if (dev->dev_state != MEI_DEV_UNINITIALIZED &&
- dev->dev_state != MEI_DEV_POWERING_DOWN &&
- dev->dev_state != MEI_DEV_POWER_DOWN) {
+ if (dev->dev_state == MEI_DEV_ENABLED) {
dev_dbg(&dev->dev, "Force link reset.\n");
schedule_work(&dev->reset_work);
} else {
- dev_dbg(&dev->dev, "Ignore this interrupt in state = %d\n",
- dev->dev_state);
+ dev_dbg(&dev->dev, "we need to start the dev.\n");
+ dev->recvd_hw_ready = true;
+ wake_up(&dev->wait_hw_ready);
}
} else {
dev_dbg(&dev->dev, "Spurious Interrupt\n");
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 106cfe732a15..1d84e348f2cc 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -5300,7 +5300,7 @@ static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,
if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP))
continue;
- if (bond_is_last_slave(bond, slave)) {
+ if (i + 1 == slaves_count) {
skb2 = skb;
skb_used = true;
} else {
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index c37a1b86180f..454d7dcf198d 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -794,18 +794,34 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q)
{
- struct airoha_eth *eth = q->qdma->eth;
+ struct airoha_qdma *qdma = q->qdma;
+ struct airoha_eth *eth = qdma->eth;
+ int qid = q - &qdma->q_rx[0];
while (q->queued) {
struct airoha_queue_entry *e = &q->entry[q->tail];
+ struct airoha_qdma_desc *desc = &q->desc[q->tail];
struct page *page = virt_to_head_page(e->buf);
dma_sync_single_for_cpu(eth->dev, e->dma_addr, e->dma_len,
page_pool_get_dma_dir(q->page_pool));
page_pool_put_full_page(q->page_pool, page, false);
+ /* Reset DMA descriptor */
+ WRITE_ONCE(desc->ctrl, 0);
+ WRITE_ONCE(desc->addr, 0);
+ WRITE_ONCE(desc->data, 0);
+ WRITE_ONCE(desc->msg0, 0);
+ WRITE_ONCE(desc->msg1, 0);
+ WRITE_ONCE(desc->msg2, 0);
+ WRITE_ONCE(desc->msg3, 0);
+
q->tail = (q->tail + 1) % q->ndesc;
q->queued--;
}
+
+ q->head = q->tail;
+ airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK,
+ FIELD_PREP(RX_RING_DMA_IDX_MASK, q->tail));
}
static int airoha_qdma_init_rx(struct airoha_qdma *qdma)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 2dadc7c66858..d8c42349ded1 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -8002,6 +8002,8 @@ static int __bnxt_reserve_rings(struct bnxt *bp)
ulp_msix = bnxt_get_avail_msix(bp, bp->ulp_num_msix_want);
if (!ulp_msix)
bnxt_set_ulp_stat_ctxs(bp, 0);
+ else
+ bnxt_set_dflt_ulp_stat_ctxs(bp);
if (ulp_msix > bp->ulp_num_msix_want)
ulp_msix = bp->ulp_num_msix_want;
@@ -8623,7 +8625,7 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp)
struct hwrm_func_backing_store_qcaps_v2_output *resp;
struct hwrm_func_backing_store_qcaps_v2_input *req;
struct bnxt_ctx_mem_info *ctx = bp->ctx;
- u16 type;
+ u16 type, next_type = 0;
int rc;
rc = hwrm_req_init(bp, req, HWRM_FUNC_BACKING_STORE_QCAPS_V2);
@@ -8639,7 +8641,7 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp)
resp = hwrm_req_hold(bp, req);
- for (type = 0; type < BNXT_CTX_V2_MAX; ) {
+ for (type = 0; type < BNXT_CTX_V2_MAX; type = next_type) {
struct bnxt_ctx_mem_type *ctxm = &ctx->ctx_arr[type];
u8 init_val, init_off, i;
u32 max_entries;
@@ -8652,7 +8654,7 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp)
if (rc)
goto ctx_done;
flags = le32_to_cpu(resp->flags);
- type = le16_to_cpu(resp->next_valid_type);
+ next_type = le16_to_cpu(resp->next_valid_type);
if (!(flags & BNXT_CTX_MEM_TYPE_VALID)) {
bnxt_free_one_ctx_mem(bp, ctxm, true);
continue;
@@ -8667,7 +8669,7 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp)
else
continue;
}
- ctxm->type = le16_to_cpu(resp->type);
+ ctxm->type = type;
ctxm->entry_size = entry_size;
ctxm->flags = flags;
ctxm->instance_bmap = le32_to_cpu(resp->instance_bit_map);
@@ -12917,6 +12919,21 @@ static int bnxt_tx_nr_rings_per_tc(struct bnxt *bp)
return bp->num_tc ? bp->tx_nr_rings / bp->num_tc : bp->tx_nr_rings;
}
+static void bnxt_set_xdp_tx_rings(struct bnxt *bp)
+{
+ bp->tx_nr_rings_xdp = bp->tx_nr_rings_per_tc;
+ bp->tx_nr_rings += bp->tx_nr_rings_xdp;
+}
+
+static void bnxt_adj_tx_rings(struct bnxt *bp)
+{
+ /* Make adjustments if reserved TX rings are less than requested */
+ bp->tx_nr_rings -= bp->tx_nr_rings_xdp;
+ bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp);
+ if (bp->tx_nr_rings_xdp)
+ bnxt_set_xdp_tx_rings(bp);
+}
+
static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
{
int rc = 0;
@@ -12934,13 +12951,7 @@ static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
if (rc)
return rc;
- /* Make adjustments if reserved TX rings are less than requested */
- bp->tx_nr_rings -= bp->tx_nr_rings_xdp;
- bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp);
- if (bp->tx_nr_rings_xdp) {
- bp->tx_nr_rings_xdp = bp->tx_nr_rings_per_tc;
- bp->tx_nr_rings += bp->tx_nr_rings_xdp;
- }
+ bnxt_adj_tx_rings(bp);
rc = bnxt_alloc_mem(bp, irq_re_init);
if (rc) {
netdev_err(bp->dev, "bnxt_alloc_mem err: %x\n", rc);
@@ -15377,11 +15388,19 @@ static int bnxt_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+void bnxt_set_cp_rings(struct bnxt *bp, bool sh)
+{
+ int tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings);
+
+ bp->cp_nr_rings = sh ? max_t(int, tx_cp, bp->rx_nr_rings) :
+ tx_cp + bp->rx_nr_rings;
+}
+
int bnxt_setup_mq_tc(struct net_device *dev, u8 tc)
{
struct bnxt *bp = netdev_priv(dev);
bool sh = false;
- int rc, tx_cp;
+ int rc;
if (tc > bp->max_tc) {
netdev_err(dev, "Too many traffic classes requested: %d. Max supported is %d.\n",
@@ -15414,9 +15433,7 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc)
bp->num_tc = 0;
}
bp->tx_nr_rings += bp->tx_nr_rings_xdp;
- tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings);
- bp->cp_nr_rings = sh ? max_t(int, tx_cp, bp->rx_nr_rings) :
- tx_cp + bp->rx_nr_rings;
+ bnxt_set_cp_rings(bp, sh);
if (netif_running(bp->dev))
return bnxt_open_nic(bp, true, false);
@@ -16421,6 +16438,19 @@ static void bnxt_trim_dflt_sh_rings(struct bnxt *bp)
bp->tx_nr_rings = bnxt_tx_nr_rings(bp);
}
+static void bnxt_adj_dflt_rings(struct bnxt *bp, bool sh)
+{
+ if (sh)
+ bnxt_trim_dflt_sh_rings(bp);
+ else
+ bp->cp_nr_rings = bp->tx_nr_rings_per_tc + bp->rx_nr_rings;
+ bp->tx_nr_rings = bnxt_tx_nr_rings(bp);
+ if (sh && READ_ONCE(bp->xdp_prog)) {
+ bnxt_set_xdp_tx_rings(bp);
+ bnxt_set_cp_rings(bp, true);
+ }
+}
+
static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh)
{
int dflt_rings, max_rx_rings, max_tx_rings, rc;
@@ -16446,11 +16476,8 @@ static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh)
return rc;
bp->rx_nr_rings = min_t(int, dflt_rings, max_rx_rings);
bp->tx_nr_rings_per_tc = min_t(int, dflt_rings, max_tx_rings);
- if (sh)
- bnxt_trim_dflt_sh_rings(bp);
- else
- bp->cp_nr_rings = bp->tx_nr_rings_per_tc + bp->rx_nr_rings;
- bp->tx_nr_rings = bnxt_tx_nr_rings(bp);
+
+ bnxt_adj_dflt_rings(bp, sh);
avail_msix = bnxt_get_max_func_irqs(bp) - bp->cp_nr_rings;
if (avail_msix >= BNXT_MIN_ROCE_CP_RINGS) {
@@ -16463,16 +16490,17 @@ static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh)
rc = __bnxt_reserve_rings(bp);
if (rc && rc != -ENODEV)
netdev_warn(bp->dev, "Unable to reserve tx rings\n");
- bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp);
+
+ bnxt_adj_tx_rings(bp);
if (sh)
- bnxt_trim_dflt_sh_rings(bp);
+ bnxt_adj_dflt_rings(bp, true);
/* Rings may have been trimmed, re-reserve the trimmed rings. */
if (bnxt_need_reserve_rings(bp)) {
rc = __bnxt_reserve_rings(bp);
if (rc && rc != -ENODEV)
netdev_warn(bp->dev, "2nd rings reservation failed.\n");
- bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp);
+ bnxt_adj_tx_rings(bp);
}
if (BNXT_CHIP_TYPE_NITRO_A0(bp)) {
bp->rx_nr_rings++;
@@ -16506,7 +16534,7 @@ static int bnxt_init_dflt_ring_mode(struct bnxt *bp)
if (rc)
goto init_dflt_ring_err;
- bp->tx_nr_rings_per_tc = bnxt_tx_nr_rings_per_tc(bp);
+ bnxt_adj_tx_rings(bp);
bnxt_set_dflt_rfs(bp);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 4d94bacf9f01..9413818788c4 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -2971,6 +2971,7 @@ int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs,
int tx_xdp);
int bnxt_fw_init_one(struct bnxt *bp);
bool bnxt_hwrm_reset_permitted(struct bnxt *bp);
+void bnxt_set_cp_rings(struct bnxt *bp, bool sh);
int bnxt_setup_mq_tc(struct net_device *dev, u8 tc);
struct bnxt_ntuple_filter *bnxt_lookup_ntp_filter_from_idx(struct bnxt *bp,
struct bnxt_ntuple_filter *fltr, u32 idx);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index fa452d6272e0..34d9264d5195 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -945,7 +945,6 @@ static int bnxt_set_channels(struct net_device *dev,
bool sh = false;
int tx_xdp = 0;
int rc = 0;
- int tx_cp;
if (channel->other_count)
return -EINVAL;
@@ -1013,9 +1012,7 @@ static int bnxt_set_channels(struct net_device *dev,
if (tcs > 1)
bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tcs + tx_xdp;
- tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings);
- bp->cp_nr_rings = sh ? max_t(int, tx_cp, bp->rx_nr_rings) :
- tx_cp + bp->rx_nr_rings;
+ bnxt_set_cp_rings(bp, sh);
/* After changing number of rx channels, update NTUPLE feature. */
netdev_update_features(dev);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
index c94a391b1ba5..06f35a61c177 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
@@ -384,7 +384,7 @@ int bnxt_xdp_xmit(struct net_device *dev, int num_frames,
static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog)
{
struct net_device *dev = bp->dev;
- int tx_xdp = 0, tx_cp, rc, tc;
+ int tx_xdp = 0, rc, tc;
struct bpf_prog *old;
netdev_assert_locked(dev);
@@ -431,8 +431,7 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog)
}
bp->tx_nr_rings_xdp = tx_xdp;
bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tc + tx_xdp;
- tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings);
- bp->cp_nr_rings = max_t(int, tx_cp, bp->rx_nr_rings);
+ bnxt_set_cp_rings(bp, true);
bnxt_set_tpa_flags(bp);
bnxt_set_ring_params(bp);
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 75f66587983d..1a59a2e53d86 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -12300,7 +12300,7 @@ static int tg3_get_link_ksettings(struct net_device *dev,
ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
advertising);
- if (netif_running(dev) && tp->link_up) {
+ if (netif_running(dev) && netif_carrier_ok(dev)) {
cmd->base.speed = tp->link_config.active_speed;
cmd->base.duplex = tp->link_config.active_duplex;
ethtool_convert_legacy_u32_to_link_mode(
@@ -17030,6 +17030,13 @@ static int tg3_get_invariants(struct tg3 *tp, const struct pci_device_id *ent)
return err;
}
+static int tg3_is_default_mac_address(u8 *addr)
+{
+ static const u8 default_mac_address[ETH_ALEN] = { 0x00, 0x10, 0x18, 0x00, 0x00, 0x00 };
+
+ return ether_addr_equal(default_mac_address, addr);
+}
+
static int tg3_get_device_address(struct tg3 *tp, u8 *addr)
{
u32 hi, lo, mac_offset;
@@ -17103,6 +17110,10 @@ static int tg3_get_device_address(struct tg3 *tp, u8 *addr)
if (!is_valid_ether_addr(addr))
return -EINVAL;
+
+ if (tg3_is_default_mac_address(addr))
+ return device_get_mac_address(&tp->pdev->dev, addr);
+
return 0;
}
diff --git a/drivers/net/ethernet/cadence/macb_pci.c b/drivers/net/ethernet/cadence/macb_pci.c
index fc4f5aee6ab3..b79dec17e6b0 100644
--- a/drivers/net/ethernet/cadence/macb_pci.c
+++ b/drivers/net/ethernet/cadence/macb_pci.c
@@ -96,10 +96,10 @@ static int macb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return 0;
err_plat_dev_register:
- clk_unregister(plat_data.hclk);
+ clk_unregister_fixed_rate(plat_data.hclk);
err_hclk_register:
- clk_unregister(plat_data.pclk);
+ clk_unregister_fixed_rate(plat_data.pclk);
err_pclk_register:
return err;
@@ -109,10 +109,12 @@ static void macb_remove(struct pci_dev *pdev)
{
struct platform_device *plat_dev = pci_get_drvdata(pdev);
struct macb_platform_data *plat_data = dev_get_platdata(&plat_dev->dev);
+ struct clk *pclk = plat_data->pclk;
+ struct clk *hclk = plat_data->hclk;
- clk_unregister(plat_data->pclk);
- clk_unregister(plat_data->hclk);
platform_device_unregister(plat_dev);
+ clk_unregister_fixed_rate(pclk);
+ clk_unregister_fixed_rate(hclk);
}
static const struct pci_device_id dev_id_table[] = {
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index a863f7841210..06ee7af7fdb9 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -964,19 +964,19 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv)
priv->tx_skbs = kcalloc(MAX_TX_QUEUE_ENTRIES, sizeof(void *),
GFP_KERNEL);
if (!priv->tx_skbs)
- return -ENOMEM;
+ goto err_free_rx_skbs;
/* Allocate descriptors */
priv->rxdes = dma_alloc_coherent(priv->dev,
MAX_RX_QUEUE_ENTRIES * sizeof(struct ftgmac100_rxdes),
&priv->rxdes_dma, GFP_KERNEL);
if (!priv->rxdes)
- return -ENOMEM;
+ goto err_free_tx_skbs;
priv->txdes = dma_alloc_coherent(priv->dev,
MAX_TX_QUEUE_ENTRIES * sizeof(struct ftgmac100_txdes),
&priv->txdes_dma, GFP_KERNEL);
if (!priv->txdes)
- return -ENOMEM;
+ goto err_free_rxdes;
/* Allocate scratch packet buffer */
priv->rx_scratch = dma_alloc_coherent(priv->dev,
@@ -984,9 +984,29 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv)
&priv->rx_scratch_dma,
GFP_KERNEL);
if (!priv->rx_scratch)
- return -ENOMEM;
+ goto err_free_txdes;
return 0;
+
+err_free_txdes:
+ dma_free_coherent(priv->dev,
+ MAX_TX_QUEUE_ENTRIES *
+ sizeof(struct ftgmac100_txdes),
+ priv->txdes, priv->txdes_dma);
+ priv->txdes = NULL;
+err_free_rxdes:
+ dma_free_coherent(priv->dev,
+ MAX_RX_QUEUE_ENTRIES *
+ sizeof(struct ftgmac100_rxdes),
+ priv->rxdes, priv->rxdes_dma);
+ priv->rxdes = NULL;
+err_free_tx_skbs:
+ kfree(priv->tx_skbs);
+ priv->tx_skbs = NULL;
+err_free_rx_skbs:
+ kfree(priv->rx_skbs);
+ priv->rx_skbs = NULL;
+ return -ENOMEM;
}
static void ftgmac100_init_rings(struct ftgmac100 *priv)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index 9fdd448e602f..8ec96f39e126 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -2579,6 +2579,7 @@ EXPORT_SYMBOL_GPL(enetc_free_si_resources);
static void enetc_setup_txbdr(struct enetc_hw *hw, struct enetc_bdr *tx_ring)
{
+ struct enetc_si *si = container_of(hw, struct enetc_si, hw);
int idx = tx_ring->index;
u32 tbmr;
@@ -2592,10 +2593,20 @@ static void enetc_setup_txbdr(struct enetc_hw *hw, struct enetc_bdr *tx_ring)
enetc_txbdr_wr(hw, idx, ENETC_TBLENR,
ENETC_RTBLENR_LEN(tx_ring->bd_count));
- /* clearing PI/CI registers for Tx not supported, adjust sw indexes */
+ /* For ENETC v1, clearing PI/CI registers for Tx not supported,
+ * adjust sw indexes
+ */
tx_ring->next_to_use = enetc_txbdr_rd(hw, idx, ENETC_TBPIR);
tx_ring->next_to_clean = enetc_txbdr_rd(hw, idx, ENETC_TBCIR);
+ if (tx_ring->next_to_use != tx_ring->next_to_clean &&
+ !is_enetc_rev1(si)) {
+ tx_ring->next_to_use = 0;
+ tx_ring->next_to_clean = 0;
+ enetc_txbdr_wr(hw, idx, ENETC_TBPIR, 0);
+ enetc_txbdr_wr(hw, idx, ENETC_TBCIR, 0);
+ }
+
/* enable Tx ints by setting pkt thr to 1 */
enetc_txbdr_wr(hw, idx, ENETC_TBICR0, ENETC_TBICR0_ICEN | 0x1);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
index 3ed0f7a02767..719c88ceb801 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
@@ -134,6 +134,12 @@
/* Port operational register */
#define ENETC4_POR 0x4100
+#define POR_TXDIS BIT(0)
+#define POR_RXDIS BIT(1)
+
+/* Port status register */
+#define ENETC4_PSR 0x4104
+#define PSR_RX_BUSY BIT(1)
/* Port traffic class a transmit maximum SDU register */
#define ENETC4_PTCTMSDUR(a) ((a) * 0x20 + 0x4208)
@@ -173,6 +179,11 @@
/* Port internal MDIO base address, use to access PCS */
#define ENETC4_PM_IMDIO_BASE 0x5030
+/* Port MAC 0/1 Interrupt Event Register */
+#define ENETC4_PM_IEVENT(mac) (0x5040 + (mac) * 0x400)
+#define PM_IEVENT_TX_EMPTY BIT(5)
+#define PM_IEVENT_RX_EMPTY BIT(6)
+
/* Port MAC 0/1 Pause Quanta Register */
#define ENETC4_PM_PAUSE_QUANTA(mac) (0x5054 + (mac) * 0x400)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 5850540634b0..993c27e34226 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -444,20 +444,11 @@ static void enetc4_set_trx_frame_size(struct enetc_pf *pf)
enetc4_pf_reset_tc_msdu(&si->hw);
}
-static void enetc4_enable_trx(struct enetc_pf *pf)
-{
- struct enetc_hw *hw = &pf->si->hw;
-
- /* Enable port transmit/receive */
- enetc_port_wr(hw, ENETC4_POR, 0);
-}
-
static void enetc4_configure_port(struct enetc_pf *pf)
{
enetc4_configure_port_si(pf);
enetc4_set_trx_frame_size(pf);
enetc_set_default_rss_key(pf);
- enetc4_enable_trx(pf);
}
static int enetc4_init_ntmp_user(struct enetc_si *si)
@@ -801,15 +792,112 @@ static void enetc4_set_tx_pause(struct enetc_pf *pf, int num_rxbdr, bool tx_paus
enetc_port_wr(hw, ENETC4_PPAUOFFTR, pause_off_thresh);
}
-static void enetc4_enable_mac(struct enetc_pf *pf, bool en)
+static void enetc4_mac_wait_tx_empty(struct enetc_si *si, int mac)
+{
+ u32 val;
+
+ if (read_poll_timeout(enetc_port_rd, val,
+ val & PM_IEVENT_TX_EMPTY,
+ 100, 10000, false, &si->hw,
+ ENETC4_PM_IEVENT(mac)))
+ dev_warn(&si->pdev->dev,
+ "MAC %d TX is not empty\n", mac);
+}
+
+static void enetc4_mac_tx_graceful_stop(struct enetc_pf *pf)
+{
+ struct enetc_hw *hw = &pf->si->hw;
+ struct enetc_si *si = pf->si;
+ u32 val;
+
+ val = enetc_port_rd(hw, ENETC4_POR);
+ val |= POR_TXDIS;
+ enetc_port_wr(hw, ENETC4_POR, val);
+
+ if (enetc_is_pseudo_mac(si))
+ return;
+
+ enetc4_mac_wait_tx_empty(si, 0);
+ if (si->hw_features & ENETC_SI_F_QBU)
+ enetc4_mac_wait_tx_empty(si, 1);
+
+ val = enetc_port_mac_rd(si, ENETC4_PM_CMD_CFG(0));
+ val &= ~PM_CMD_CFG_TX_EN;
+ enetc_port_mac_wr(si, ENETC4_PM_CMD_CFG(0), val);
+}
+
+static void enetc4_mac_tx_enable(struct enetc_pf *pf)
{
+ struct enetc_hw *hw = &pf->si->hw;
struct enetc_si *si = pf->si;
u32 val;
val = enetc_port_mac_rd(si, ENETC4_PM_CMD_CFG(0));
- val &= ~(PM_CMD_CFG_TX_EN | PM_CMD_CFG_RX_EN);
- val |= en ? (PM_CMD_CFG_TX_EN | PM_CMD_CFG_RX_EN) : 0;
+ val |= PM_CMD_CFG_TX_EN;
+ enetc_port_mac_wr(si, ENETC4_PM_CMD_CFG(0), val);
+ val = enetc_port_rd(hw, ENETC4_POR);
+ val &= ~POR_TXDIS;
+ enetc_port_wr(hw, ENETC4_POR, val);
+}
+
+static void enetc4_mac_wait_rx_empty(struct enetc_si *si, int mac)
+{
+ u32 val;
+
+ if (read_poll_timeout(enetc_port_rd, val,
+ val & PM_IEVENT_RX_EMPTY,
+ 100, 10000, false, &si->hw,
+ ENETC4_PM_IEVENT(mac)))
+ dev_warn(&si->pdev->dev,
+ "MAC %d RX is not empty\n", mac);
+}
+
+static void enetc4_mac_rx_graceful_stop(struct enetc_pf *pf)
+{
+ struct enetc_hw *hw = &pf->si->hw;
+ struct enetc_si *si = pf->si;
+ u32 val;
+
+ if (enetc_is_pseudo_mac(si))
+ goto check_rx_busy;
+
+ if (si->hw_features & ENETC_SI_F_QBU) {
+ val = enetc_port_rd(hw, ENETC4_PM_CMD_CFG(1));
+ val &= ~PM_CMD_CFG_RX_EN;
+ enetc_port_wr(hw, ENETC4_PM_CMD_CFG(1), val);
+ enetc4_mac_wait_rx_empty(si, 1);
+ }
+
+ val = enetc_port_rd(hw, ENETC4_PM_CMD_CFG(0));
+ val &= ~PM_CMD_CFG_RX_EN;
+ enetc_port_wr(hw, ENETC4_PM_CMD_CFG(0), val);
+ enetc4_mac_wait_rx_empty(si, 0);
+
+check_rx_busy:
+ if (read_poll_timeout(enetc_port_rd, val,
+ !(val & PSR_RX_BUSY),
+ 100, 10000, false, hw,
+ ENETC4_PSR))
+ dev_warn(&si->pdev->dev, "Port RX busy\n");
+
+ val = enetc_port_rd(hw, ENETC4_POR);
+ val |= POR_RXDIS;
+ enetc_port_wr(hw, ENETC4_POR, val);
+}
+
+static void enetc4_mac_rx_enable(struct enetc_pf *pf)
+{
+ struct enetc_hw *hw = &pf->si->hw;
+ struct enetc_si *si = pf->si;
+ u32 val;
+
+ val = enetc_port_rd(hw, ENETC4_POR);
+ val &= ~POR_RXDIS;
+ enetc_port_wr(hw, ENETC4_POR, val);
+
+ val = enetc_port_mac_rd(si, ENETC4_PM_CMD_CFG(0));
+ val |= PM_CMD_CFG_RX_EN;
enetc_port_mac_wr(si, ENETC4_PM_CMD_CFG(0), val);
}
@@ -853,7 +941,8 @@ static void enetc4_pl_mac_link_up(struct phylink_config *config,
enetc4_set_hd_flow_control(pf, hd_fc);
enetc4_set_tx_pause(pf, priv->num_rx_rings, tx_pause);
enetc4_set_rx_pause(pf, rx_pause);
- enetc4_enable_mac(pf, true);
+ enetc4_mac_tx_enable(pf);
+ enetc4_mac_rx_enable(pf);
}
static void enetc4_pl_mac_link_down(struct phylink_config *config,
@@ -862,7 +951,8 @@ static void enetc4_pl_mac_link_down(struct phylink_config *config,
{
struct enetc_pf *pf = phylink_to_enetc_pf(config);
- enetc4_enable_mac(pf, false);
+ enetc4_mac_rx_graceful_stop(pf);
+ enetc4_mac_tx_graceful_stop(pf);
}
static const struct phylink_mac_ops enetc_pl_mac_ops = {
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
index 2fe140ddebb2..7c17acaf7a38 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
@@ -795,9 +795,17 @@ static int enetc_set_rxfh(struct net_device *ndev,
struct enetc_si *si = priv->si;
int err = 0;
+ if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE &&
+ rxfh->hfunc != ETH_RSS_HASH_TOP)
+ return -EOPNOTSUPP;
+
/* set hash key, if PF */
- if (rxfh->key && enetc_si_is_pf(si))
+ if (rxfh->key) {
+ if (!enetc_si_is_pf(si))
+ return -EOPNOTSUPP;
+
enetc_set_rss_key(si, rxfh->key);
+ }
/* set RSS table */
if (rxfh->indir)
diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c
index 4b7bad9a485d..56801c2009d5 100644
--- a/drivers/net/ethernet/freescale/fec_ptp.c
+++ b/drivers/net/ethernet/freescale/fec_ptp.c
@@ -545,9 +545,6 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp,
if (rq->perout.flags)
return -EOPNOTSUPP;
- if (rq->perout.index != fep->pps_channel)
- return -EOPNOTSUPP;
-
period.tv_sec = rq->perout.period.sec;
period.tv_nsec = rq->perout.period.nsec;
period_ns = timespec64_to_ns(&period);
diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
index e9bd32741983..4894d4f187f7 100644
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -244,6 +244,25 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
return 0;
}
+static bool
+mtk_flow_is_valid_idev(const struct mtk_eth *eth, const struct net_device *idev)
+{
+ size_t i;
+
+ if (!idev)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(eth->netdev); i++) {
+ if (!eth->netdev[i])
+ continue;
+
+ if (idev->netdev_ops == eth->netdev[i]->netdev_ops)
+ return true;
+ }
+
+ return false;
+}
+
static int
mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f,
int ppe_index)
@@ -270,7 +289,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f,
flow_rule_match_meta(rule, &match);
if (mtk_is_netsys_v2_or_greater(eth)) {
idev = __dev_get_by_index(&init_net, match.key->ingress_ifindex);
- if (idev && idev->netdev_ops == eth->netdev[0]->netdev_ops) {
+ if (mtk_flow_is_valid_idev(eth, idev)) {
struct mtk_mac *mac = netdev_priv(idev);
if (WARN_ON(mac->ppe_idx >= eth->soc->ppe_num))
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
index ea77fbd98396..055ee020c56f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
@@ -107,9 +107,7 @@ mlx5_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
if (err)
return err;
- err = mlx5_fw_version_query(dev, &running_fw, &stored_fw);
- if (err)
- return err;
+ mlx5_fw_version_query(dev, &running_fw, &stored_fw);
snprintf(version_str, sizeof(version_str), "%d.%d.%04d",
mlx5_fw_ver_major(running_fw), mlx5_fw_ver_minor(running_fw),
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index 166a88988904..31e4eb6bd685 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -3761,6 +3761,8 @@ int esw_offloads_enable(struct mlx5_eswitch *esw)
return 0;
err_vports:
+ /* rollback to legacy, indicates don't unregister the uplink netdev */
+ esw->dev->priv.flags |= MLX5_PRIV_FLAGS_SWITCH_LEGACY;
mlx5_esw_offloads_rep_unload(esw, MLX5_VPORT_UPLINK);
err_uplink:
esw_offloads_steering_cleanup(esw);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c
index eeb4437975f2..c1f220e5fe18 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c
@@ -822,48 +822,63 @@ mlx5_fw_image_pending(struct mlx5_core_dev *dev,
return 0;
}
-int mlx5_fw_version_query(struct mlx5_core_dev *dev,
- u32 *running_ver, u32 *pending_ver)
+void mlx5_fw_version_query(struct mlx5_core_dev *dev,
+ u32 *running_ver, u32 *pending_ver)
{
u32 reg_mcqi_version[MLX5_ST_SZ_DW(mcqi_version)] = {};
bool pending_version_exists;
int component_index;
int err;
+ *running_ver = 0;
+ *pending_ver = 0;
+
if (!MLX5_CAP_GEN(dev, mcam_reg) || !MLX5_CAP_MCAM_REG(dev, mcqi) ||
!MLX5_CAP_MCAM_REG(dev, mcqs)) {
mlx5_core_warn(dev, "fw query isn't supported by the FW\n");
- return -EOPNOTSUPP;
+ return;
}
component_index = mlx5_get_boot_img_component_index(dev);
- if (component_index < 0)
- return component_index;
+ if (component_index < 0) {
+ mlx5_core_warn(dev, "fw query failed to find boot img component index, err %d\n",
+ component_index);
+ return;
+ }
+ *running_ver = U32_MAX; /* indicate failure */
err = mlx5_reg_mcqi_version_query(dev, component_index,
MCQI_FW_RUNNING_VERSION,
reg_mcqi_version);
- if (err)
- return err;
-
- *running_ver = MLX5_GET(mcqi_version, reg_mcqi_version, version);
-
+ if (!err)
+ *running_ver = MLX5_GET(mcqi_version, reg_mcqi_version,
+ version);
+ else
+ mlx5_core_warn(dev, "failed to query running version, err %d\n",
+ err);
+
+ *pending_ver = U32_MAX; /* indicate failure */
err = mlx5_fw_image_pending(dev, component_index, &pending_version_exists);
- if (err)
- return err;
+ if (err) {
+ mlx5_core_warn(dev, "failed to query pending image, err %d\n",
+ err);
+ return;
+ }
if (!pending_version_exists) {
*pending_ver = 0;
- return 0;
+ return;
}
err = mlx5_reg_mcqi_version_query(dev, component_index,
MCQI_FW_STORED_VERSION,
reg_mcqi_version);
- if (err)
- return err;
-
- *pending_ver = MLX5_GET(mcqi_version, reg_mcqi_version, version);
-
- return 0;
+ if (!err)
+ *pending_ver = MLX5_GET(mcqi_version, reg_mcqi_version,
+ version);
+ else
+ mlx5_core_warn(dev, "failed to query pending version, err %d\n",
+ err);
+
+ return;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
index 62b6faa4276a..b8d5f6a44d26 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/debugfs.c
@@ -160,8 +160,11 @@ DEFINE_SHOW_ATTRIBUTE(members);
void mlx5_ldev_add_debugfs(struct mlx5_core_dev *dev)
{
+ struct mlx5_lag *ldev = mlx5_lag_dev(dev);
struct dentry *dbg;
+ if (!ldev)
+ return;
dbg = debugfs_create_dir("lag", mlx5_debugfs_get_dev_root(dev));
dev->priv.dbg.lag_debugfs = dbg;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
index f2d74382fb85..c048839f07d6 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
@@ -392,8 +392,8 @@ int mlx5_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed);
int mlx5_firmware_flash(struct mlx5_core_dev *dev, const struct firmware *fw,
struct netlink_ext_ack *extack);
-int mlx5_fw_version_query(struct mlx5_core_dev *dev,
- u32 *running_ver, u32 *stored_ver);
+void mlx5_fw_version_query(struct mlx5_core_dev *dev, u32 *running_ver,
+ u32 *stored_ver);
#ifdef CONFIG_MLX5_CORE_EN
int mlx5e_init(void);
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c
index e119526fce14..cb0be88427f8 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c
@@ -927,7 +927,7 @@ static void fbnic_fill_bdq(struct fbnic_ring *bdq)
/* Force DMA writes to flush before writing to tail */
dma_wmb();
- writel(i, bdq->doorbell);
+ writel(i * FBNIC_BD_FRAG_COUNT, bdq->doorbell);
}
}
@@ -2548,7 +2548,7 @@ static void fbnic_enable_bdq(struct fbnic_ring *hpq, struct fbnic_ring *ppq)
hpq->tail = 0;
hpq->head = 0;
- log_size = fls(hpq->size_mask);
+ log_size = fls(hpq->size_mask) + ilog2(FBNIC_BD_FRAG_COUNT);
/* Store descriptor ring address and size */
fbnic_ring_wr32(hpq, FBNIC_QUEUE_BDQ_HPQ_BAL, lower_32_bits(hpq->dma));
@@ -2560,7 +2560,7 @@ static void fbnic_enable_bdq(struct fbnic_ring *hpq, struct fbnic_ring *ppq)
if (!ppq->size_mask)
goto write_ctl;
- log_size = fls(ppq->size_mask);
+ log_size = fls(ppq->size_mask) + ilog2(FBNIC_BD_FRAG_COUNT);
/* Add enabling of PPQ to BDQ control */
bdq_ctl |= FBNIC_QUEUE_BDQ_CTL_PPQ_ENABLE;
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h
index 51a98f27d5d9..f2ee2cbf3486 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h
@@ -38,7 +38,7 @@ struct fbnic_net;
#define FBNIC_MAX_XDPQS 128u
/* These apply to TWQs, TCQ, RCQ */
-#define FBNIC_QUEUE_SIZE_MIN 16u
+#define FBNIC_QUEUE_SIZE_MIN 64u
#define FBNIC_QUEUE_SIZE_MAX SZ_64K
#define FBNIC_TXQ_SIZE_DEFAULT 1024
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 4a616a97d89d..215ce3443ade 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -737,6 +737,13 @@ static void mana_get_rxbuf_cfg(struct mana_port_context *apc,
}
*frag_count = 1;
+
+ /* In the single-buffer path, napi_build_skb() must see the
+ * actual backing allocation size so skb->truesize reflects
+ * the full page (or higher-order page), not just the usable
+ * packet area.
+ */
+ *alloc_size = PAGE_SIZE << get_order(*alloc_size);
return;
}
@@ -3376,6 +3383,7 @@ static int add_adev(struct gdma_dev *gd, const char *name)
struct auxiliary_device *adev;
struct mana_adev *madev;
int ret;
+ int id;
madev = kzalloc(sizeof(*madev), GFP_KERNEL);
if (!madev)
@@ -3385,7 +3393,8 @@ static int add_adev(struct gdma_dev *gd, const char *name)
ret = mana_adev_idx_alloc();
if (ret < 0)
goto idx_fail;
- adev->id = ret;
+ id = ret;
+ adev->id = id;
adev->name = name;
adev->dev.parent = gd->gdma_context->dev;
@@ -3411,7 +3420,7 @@ static int add_adev(struct gdma_dev *gd, const char *name)
auxiliary_device_uninit(adev);
init_fail:
- mana_adev_idx_free(adev->id);
+ mana_adev_idx_free(id);
idx_fail:
kfree(madev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 01ede5148163..cfe47a8e2c6d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -140,7 +140,7 @@ static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue);
static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue);
static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
u32 rxmode, u32 chan);
-static int stmmac_vlan_restore(struct stmmac_priv *priv);
+static void stmmac_vlan_restore(struct stmmac_priv *priv);
#ifdef CONFIG_DEBUG_FS
static const struct net_device_ops stmmac_netdev_ops;
@@ -6811,21 +6811,15 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi
return ret;
}
-static int stmmac_vlan_restore(struct stmmac_priv *priv)
+static void stmmac_vlan_restore(struct stmmac_priv *priv)
{
- int ret;
-
if (!(priv->dev->features & NETIF_F_VLAN_FEATURES))
- return 0;
+ return;
if (priv->hw->num_vlan)
stmmac_restore_hw_vlan_rx_fltr(priv, priv->dev, priv->hw);
- ret = stmmac_vlan_update(priv, priv->num_double_vlans);
- if (ret)
- netdev_err(priv->dev, "Failed to restore VLANs\n");
-
- return ret;
+ stmmac_vlan_update(priv, priv->num_double_vlans);
}
static int stmmac_bpf(struct net_device *dev, struct netdev_bpf *bpf)
diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
index 11d5b23a61ba..a74a41ad59c8 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_common.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
@@ -902,6 +902,7 @@ static void emac_dispatch_skb_zc(struct prueth_emac *emac, struct xdp_buff *xdp,
skb_reserve(skb, headroom);
skb_put(skb, pkt_len);
+ skb_copy_to_linear_data(skb, xdp->data, pkt_len);
skb->dev = ndev;
/* RX HW timestamp */
@@ -912,7 +913,6 @@ static void emac_dispatch_skb_zc(struct prueth_emac *emac, struct xdp_buff *xdp,
skb->offload_fwd_mark = emac->offload_fwd_mark;
skb->protocol = eth_type_trans(skb, ndev);
- skb_mark_for_recycle(skb);
napi_gro_receive(&emac->napi_rx, skb);
ndev->stats.rx_bytes += pkt_len;
ndev->stats.rx_packets++;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index 5ff742103beb..fcd3aaef27fc 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -105,7 +105,7 @@
#define XAXIDMA_BD_HAS_DRE_MASK 0xF00 /* Whether has DRE mask */
#define XAXIDMA_BD_WORDLEN_MASK 0xFF /* Whether has DRE mask */
-#define XAXIDMA_BD_CTRL_LENGTH_MASK 0x007FFFFF /* Requested len */
+#define XAXIDMA_BD_CTRL_LENGTH_MASK GENMASK(25, 0) /* Requested len */
#define XAXIDMA_BD_CTRL_TXSOF_MASK 0x08000000 /* First tx packet */
#define XAXIDMA_BD_CTRL_TXEOF_MASK 0x04000000 /* Last tx packet */
#define XAXIDMA_BD_CTRL_ALL_MASK 0x0C000000 /* All control bits */
@@ -130,7 +130,7 @@
#define XAXIDMA_BD_CTRL_TXEOF_MASK 0x04000000 /* Last tx packet */
#define XAXIDMA_BD_CTRL_ALL_MASK 0x0C000000 /* All control bits */
-#define XAXIDMA_BD_STS_ACTUAL_LEN_MASK 0x007FFFFF /* Actual len */
+#define XAXIDMA_BD_STS_ACTUAL_LEN_MASK GENMASK(25, 0) /* Actual len */
#define XAXIDMA_BD_STS_COMPLETE_MASK 0x80000000 /* Completed */
#define XAXIDMA_BD_STS_DEC_ERR_MASK 0x40000000 /* Decode error */
#define XAXIDMA_BD_STS_SLV_ERR_MASK 0x20000000 /* Slave error */
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 284031fb2e2c..eefe54ce6685 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -770,8 +770,8 @@ static int axienet_device_reset(struct net_device *ndev)
* @first_bd: Index of first descriptor to clean up
* @nr_bds: Max number of descriptors to clean up
* @force: Whether to clean descriptors even if not complete
- * @sizep: Pointer to a u32 filled with the total sum of all bytes
- * in all cleaned-up descriptors. Ignored if NULL.
+ * @sizep: Pointer to a u32 accumulating the total byte count of
+ * completed packets (using skb->len). Ignored if NULL.
* @budget: NAPI budget (use 0 when not called from NAPI poll)
*
* Would either be called after a successful transmit operation, or after
@@ -805,6 +805,8 @@ static int axienet_free_tx_chain(struct axienet_local *lp, u32 first_bd,
DMA_TO_DEVICE);
if (cur_p->skb && (status & XAXIDMA_BD_STS_COMPLETE_MASK)) {
+ if (sizep)
+ *sizep += cur_p->skb->len;
napi_consume_skb(cur_p->skb, budget);
packets++;
}
@@ -818,9 +820,6 @@ static int axienet_free_tx_chain(struct axienet_local *lp, u32 first_bd,
wmb();
cur_p->cntrl = 0;
cur_p->status = 0;
-
- if (sizep)
- *sizep += status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK;
}
if (!force) {
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index ca0992533572..7a85b758fb1e 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -480,11 +480,16 @@ static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
{
/* Ubiquiti U-Fiber Instant module claims that support all transceiver
* types including 10G Ethernet which is not truth. So clear all claimed
- * modes and set only one mode which module supports: 1000baseX_Full.
+ * modes and set only one mode which module supports: 1000baseX_Full,
+ * along with the Autoneg and pause bits.
*/
linkmode_zero(caps->link_modes);
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
caps->link_modes);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, caps->link_modes);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, caps->link_modes);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, caps->link_modes);
+
phy_interface_zero(caps->interfaces);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, caps->interfaces);
}
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 60a0ff998818..dd068a4d256d 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -381,8 +381,6 @@ struct receive_queue {
struct xdp_buff **xsk_buffs;
};
-#define VIRTIO_NET_RSS_MAX_KEY_SIZE 40
-
/* Control VQ buffers: protected by the rtnl lock */
struct control_buf {
struct virtio_net_ctrl_hdr hdr;
@@ -486,7 +484,7 @@ struct virtnet_info {
/* Must be last as it ends in a flexible-array member. */
TRAILING_OVERLAP(struct virtio_net_rss_config_trailer, rss_trailer, hash_key_data,
- u8 rss_hash_key_data[VIRTIO_NET_RSS_MAX_KEY_SIZE];
+ u8 rss_hash_key_data[NETDEV_RSS_KEY_LEN];
);
};
static_assert(offsetof(struct virtnet_info, rss_trailer.hash_key_data) ==
@@ -6708,6 +6706,7 @@ static int virtnet_probe(struct virtio_device *vdev)
struct virtnet_info *vi;
u16 max_queue_pairs;
int mtu = 0;
+ u16 key_sz;
/* Find if host supports multiqueue/rss virtio_net device */
max_queue_pairs = 1;
@@ -6842,14 +6841,13 @@ static int virtnet_probe(struct virtio_device *vdev)
}
if (vi->has_rss || vi->has_rss_hash_report) {
- vi->rss_key_size =
- virtio_cread8(vdev, offsetof(struct virtio_net_config, rss_max_key_size));
- if (vi->rss_key_size > VIRTIO_NET_RSS_MAX_KEY_SIZE) {
- dev_err(&vdev->dev, "rss_max_key_size=%u exceeds the limit %u.\n",
- vi->rss_key_size, VIRTIO_NET_RSS_MAX_KEY_SIZE);
- err = -EINVAL;
- goto free;
- }
+ key_sz = virtio_cread8(vdev, offsetof(struct virtio_net_config, rss_max_key_size));
+
+ vi->rss_key_size = min_t(u16, key_sz, NETDEV_RSS_KEY_LEN);
+ if (key_sz > vi->rss_key_size)
+ dev_warn(&vdev->dev,
+ "rss_max_key_size=%u exceeds driver limit %u, clamping\n",
+ key_sz, vi->rss_key_size);
vi->rss_hash_types_supported =
virtio_cread32(vdev, offsetof(struct virtio_net_config, supported_hash_types));
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 2a140be86baf..d2d0e0bd4371 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -1965,12 +1965,14 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,
ns_olen = request->len - skb_network_offset(request) -
sizeof(struct ipv6hdr) - sizeof(*ns);
for (i = 0; i < ns_olen-1; i += (ns->opt[i+1]<<3)) {
- if (!ns->opt[i + 1]) {
+ if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) {
kfree_skb(reply);
return NULL;
}
if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
- daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
+ if ((ns->opt[i + 1] << 3) >=
+ sizeof(struct nd_opt_hdr) + ETH_ALEN)
+ daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
break;
}
}
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index b9e976ddcbbf..44eea682c297 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/ieee80211.h>
@@ -1110,9 +1110,8 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
struct ath11k_base *ab = ar->ab;
struct ath11k_peer *peer;
struct ath11k_sta *arsta = ath11k_sta_to_arsta(params->sta);
+ struct dp_rx_tid *rx_tid;
int vdev_id = arsta->arvif->vdev_id;
- dma_addr_t paddr;
- bool active;
int ret;
spin_lock_bh(&ab->base_lock);
@@ -1124,15 +1123,14 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
return -ENOENT;
}
- paddr = peer->rx_tid[params->tid].paddr;
- active = peer->rx_tid[params->tid].active;
+ rx_tid = &peer->rx_tid[params->tid];
- if (!active) {
+ if (!rx_tid->active) {
spin_unlock_bh(&ab->base_lock);
return 0;
}
- ret = ath11k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false);
+ ret = ath11k_peer_rx_tid_reo_update(ar, peer, rx_tid, 1, 0, false);
spin_unlock_bh(&ab->base_lock);
if (ret) {
ath11k_warn(ab, "failed to update reo for rx tid %d: %d\n",
@@ -1141,7 +1139,8 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
}
ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id,
- params->sta->addr, paddr,
+ params->sta->addr,
+ rx_tid->paddr,
params->tid, 1, 1);
if (ret)
ath11k_warn(ab, "failed to send wmi to delete rx tid %d\n",
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
index 8d64a271bb94..36159a769916 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
@@ -296,6 +296,11 @@ enum iwl_legacy_cmds {
*/
SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
+ /**
+ * @SCAN_START_NOTIFICATION_UMAC: uses &struct iwl_umac_scan_start
+ */
+ SCAN_START_NOTIFICATION_UMAC = 0xb2,
+
/**
* @MATCH_FOUND_NOTIFICATION: scan match found
*/
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
index 60f0a4924ddf..46fcc32608e3 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
@@ -1156,6 +1156,16 @@ enum iwl_umac_scan_abort_status {
IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND,
};
+/**
+ * struct iwl_umac_scan_start - scan start notification
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @reserved: for future use
+ */
+struct iwl_umac_scan_start {
+ __le32 uid;
+ __le32 reserved;
+} __packed; /* SCAN_START_UMAC_API_S_VER_1 */
+
/**
* struct iwl_umac_scan_complete - scan complete notification
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index f15d1f5d1bf5..a770ee5e0e73 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -111,14 +111,75 @@ static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld,
IEEE80211_HE_MAC_CAP2_ACK_EN);
}
-static void iwl_mld_set_he_support(struct iwl_mld *mld,
- struct ieee80211_vif *vif,
- struct iwl_mac_config_cmd *cmd)
+struct iwl_mld_mac_wifi_gen_sta_iter_data {
+ struct ieee80211_vif *vif;
+ struct iwl_mac_wifi_gen_support *support;
+};
+
+static void iwl_mld_mac_wifi_gen_sta_iter(void *_data,
+ struct ieee80211_sta *sta)
{
- if (vif->type == NL80211_IFTYPE_AP)
- cmd->wifi_gen.he_ap_support = 1;
- else
- cmd->wifi_gen.he_support = 1;
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_mac_wifi_gen_sta_iter_data *data = _data;
+ struct ieee80211_link_sta *link_sta;
+ unsigned int link_id;
+
+ if (mld_sta->vif != data->vif)
+ return;
+
+ for_each_sta_active_link(data->vif, sta, link_sta, link_id) {
+ if (link_sta->he_cap.has_he)
+ data->support->he_support = 1;
+ if (link_sta->eht_cap.has_eht)
+ data->support->eht_support = 1;
+ }
+}
+
+static void iwl_mld_set_wifi_gen(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_wifi_gen_support *support)
+{
+ struct iwl_mld_mac_wifi_gen_sta_iter_data sta_iter_data = {
+ .vif = vif,
+ .support = support,
+ };
+ struct ieee80211_bss_conf *link_conf;
+ unsigned int link_id;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_MONITOR:
+ /* for sniffer, set to HW capabilities */
+ support->he_support = 1;
+ support->eht_support = mld->trans->cfg->eht_supported;
+ break;
+ case NL80211_IFTYPE_AP:
+ /* for AP set according to the link configs */
+ for_each_vif_active_link(vif, link_conf, link_id) {
+ support->he_ap_support |= link_conf->he_support;
+ support->eht_support |= link_conf->eht_support;
+ }
+ break;
+ default:
+ /*
+ * If we have MLO enabled, then the firmware needs to enable
+ * address translation for the station(s) we add. That depends
+ * on having EHT enabled in firmware, which in turn depends on
+ * mac80211 in the iteration below.
+ * However, mac80211 doesn't enable capabilities on the AP STA
+ * until it has parsed the association response successfully,
+ * so set EHT (and HE as a pre-requisite for EHT) when the vif
+ * is an MLD.
+ */
+ if (ieee80211_vif_is_mld(vif)) {
+ support->he_support = 1;
+ support->eht_support = 1;
+ }
+
+ ieee80211_iterate_stations_mtx(mld->hw,
+ iwl_mld_mac_wifi_gen_sta_iter,
+ &sta_iter_data);
+ break;
+ }
}
/* fill the common part for all interface types */
@@ -128,8 +189,6 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
u32 action)
{
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
- struct ieee80211_bss_conf *link_conf;
- unsigned int link_id;
lockdep_assert_wiphy(mld->wiphy);
@@ -147,29 +206,7 @@ static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld,
cmd->nic_not_ack_enabled =
cpu_to_le32(!iwl_mld_is_nic_ack_enabled(mld, vif));
- /* If we have MLO enabled, then the firmware needs to enable
- * address translation for the station(s) we add. That depends
- * on having EHT enabled in firmware, which in turn depends on
- * mac80211 in the code below.
- * However, mac80211 doesn't enable HE/EHT until it has parsed
- * the association response successfully, so just skip all that
- * and enable both when we have MLO.
- */
- if (ieee80211_vif_is_mld(vif)) {
- iwl_mld_set_he_support(mld, vif, cmd);
- cmd->wifi_gen.eht_support = 1;
- return;
- }
-
- for_each_vif_active_link(vif, link_conf, link_id) {
- if (!link_conf->he_support)
- continue;
-
- iwl_mld_set_he_support(mld, vif, cmd);
-
- /* EHT, if supported, was already set above */
- break;
- }
+ iwl_mld_set_wifi_gen(mld, vif, &cmd->wifi_gen);
}
static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 3a1b5bfb9ed6..77793da147b7 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -1690,6 +1690,16 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
if (vif->type == NL80211_IFTYPE_STATION)
iwl_mld_link_set_2mhz_block(mld, vif, sta);
+
+ if (sta->tdls) {
+ /*
+ * update MAC since wifi generation flags may change,
+ * we also update MAC on association to the AP via the
+ * vif assoc change
+ */
+ iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY);
+ }
+
/* Now the link_sta's capabilities are set, update the FW */
iwl_mld_config_tlc(mld, vif, sta);
@@ -1799,6 +1809,15 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld,
/* just removed last TDLS STA, so enable PM */
iwl_mld_update_mac_power(mld, vif, false);
}
+
+ if (sta->tdls) {
+ /*
+ * update MAC since wifi generation flags may change,
+ * we also update MAC on disassociation to the AP via
+ * the vif assoc change
+ */
+ iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY);
+ }
} else {
return -EINVAL;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
index 8a4c96385640..18691871bdac 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
@@ -171,6 +171,7 @@ static const struct iwl_hcmd_names iwl_mld_legacy_names[] = {
HCMD_NAME(MISSED_BEACONS_NOTIFICATION),
HCMD_NAME(MAC_PM_POWER_TABLE),
HCMD_NAME(MFUART_LOAD_NOTIFICATION),
+ HCMD_NAME(SCAN_START_NOTIFICATION_UMAC),
HCMD_NAME(RSS_CONFIG_CMD),
HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC),
HCMD_NAME(REPLY_RX_MPDU_CMD),
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
index 1efefc737248..47d36f0149ef 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
@@ -725,7 +725,7 @@ iwl_mld_set_link_sel_data(struct iwl_mld *mld,
/* Ignore any BSS that was not seen in the last MLO scan */
if (ktime_before(link_conf->bss->ts_boottime,
- mld->scan.last_mlo_scan_time))
+ mld->scan.last_mlo_scan_start_time))
continue;
data[n_data].link_id = link_id;
@@ -931,7 +931,7 @@ static void _iwl_mld_select_links(struct iwl_mld *mld,
if (!mld_vif->authorized || hweight16(usable_links) <= 1)
return;
- if (WARN(ktime_before(mld->scan.last_mlo_scan_time,
+ if (WARN(ktime_before(mld->scan.last_mlo_scan_start_time,
ktime_sub_ns(ktime_get_boottime_ns(),
5ULL * NSEC_PER_SEC)),
"Last MLO scan was too long ago, can't select links\n"))
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
index 4cf3920b005f..ca90a2f90526 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
@@ -284,6 +284,8 @@ static void iwl_mld_handle_beacon_notification(struct iwl_mld *mld,
* at least enough bytes to cover the structure listed in the CMD_VER_ENTRY.
*/
+CMD_VERSIONS(scan_start_notif,
+ CMD_VER_ENTRY(1, iwl_umac_scan_start))
CMD_VERSIONS(scan_complete_notif,
CMD_VER_ENTRY(1, iwl_umac_scan_complete))
CMD_VERSIONS(scan_iter_complete_notif,
@@ -355,6 +357,7 @@ DEFINE_SIMPLE_CANCELLATION(datapath_monitor, iwl_datapath_monitor_notif,
link_id)
DEFINE_SIMPLE_CANCELLATION(roc, iwl_roc_notif, activity)
DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid)
+DEFINE_SIMPLE_CANCELLATION(scan_start, iwl_umac_scan_start, uid)
DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif,
mac_id)
DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif,
@@ -397,6 +400,8 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = {
RX_HANDLER_SYNC)
RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BA_NOTIF, compressed_ba_notif,
RX_HANDLER_SYNC)
+ RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_START_NOTIFICATION_UMAC,
+ scan_start_notif)
RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_COMPLETE_UMAC,
scan_complete_notif)
RX_HANDLER_NO_OBJECT(LEGACY_GROUP, SCAN_ITERATION_COMPLETE_UMAC,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
index fd1022ddc912..76ac6fd5f9ff 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
@@ -473,6 +473,9 @@ iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld,
params->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ)
flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_TRIGGER_UHB_SCAN;
+ if (scan_status == IWL_MLD_SCAN_INT_MLO)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTF_START;
+
if (params->enable_6ghz_passive)
flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_6GHZ_PASSIVE_SCAN;
@@ -1817,9 +1820,6 @@ static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld,
ret = _iwl_mld_single_scan_start(mld, vif, req, &ies,
IWL_MLD_SCAN_INT_MLO);
- if (!ret)
- mld->scan.last_mlo_scan_time = ktime_get_boottime_ns();
-
IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret);
}
@@ -1904,6 +1904,30 @@ void iwl_mld_handle_match_found_notif(struct iwl_mld *mld,
ieee80211_sched_scan_results(mld->hw);
}
+void iwl_mld_handle_scan_start_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_umac_scan_complete *notif = (void *)pkt->data;
+ u32 uid = le32_to_cpu(notif->uid);
+
+ if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status),
+ "FW reports out-of-range scan UID %d\n", uid))
+ return;
+
+ if (IWL_FW_CHECK(mld, !(mld->scan.uid_status[uid] & mld->scan.status),
+ "FW reports scan UID %d we didn't trigger\n", uid))
+ return;
+
+ IWL_DEBUG_SCAN(mld, "Scan started: uid=%u type=%u\n", uid,
+ mld->scan.uid_status[uid]);
+ if (IWL_FW_CHECK(mld, mld->scan.uid_status[uid] != IWL_MLD_SCAN_INT_MLO,
+ "FW reports scan start notification %d we didn't trigger\n",
+ mld->scan.uid_status[uid]))
+ return;
+
+ mld->scan.last_mlo_scan_start_time = ktime_get_boottime_ns();
+}
+
void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.h b/drivers/net/wireless/intel/iwlwifi/mld/scan.h
index 69110f0cfc8e..de5620e7f463 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h
@@ -27,6 +27,9 @@ int iwl_mld_sched_scan_start(struct iwl_mld *mld,
void iwl_mld_handle_match_found_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt);
+void iwl_mld_handle_scan_start_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt);
+
void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt);
@@ -114,8 +117,8 @@ enum iwl_mld_traffic_load {
* in jiffies.
* @last_start_time_jiffies: stores the last start time in jiffies
* (interface up/reset/resume).
- * @last_mlo_scan_time: start time of the last MLO scan in nanoseconds since
- * boot.
+ * @last_mlo_scan_start_time: start time of the last MLO scan in nanoseconds
+ * since boot.
*/
struct iwl_mld_scan {
/* Add here fields that need clean up on restart */
@@ -136,7 +139,7 @@ struct iwl_mld_scan {
void *cmd;
unsigned long last_6ghz_passive_jiffies;
unsigned long last_start_time_jiffies;
- u64 last_mlo_scan_time;
+ u64 last_mlo_scan_start_time;
};
/**
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index af1a45845999..11afe373961f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -2834,7 +2834,7 @@ static void iwl_mvm_nd_match_info_handler(struct iwl_mvm *mvm,
if (IS_ERR_OR_NULL(vif))
return;
- if (len < sizeof(struct iwl_scan_offload_match_info)) {
+ if (len < sizeof(struct iwl_scan_offload_match_info) + matches_len) {
IWL_ERR(mvm, "Invalid scan match info notification\n");
return;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index edae13755ee6..b9c9ee30272e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -470,7 +470,8 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
};
- if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
+ if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210 ||
+ !mvm->trans->cfg->uhb_supported) {
IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n");
return;
}
diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c
index a229c6cab332..d795ca05729a 100644
--- a/drivers/net/wireless/microchip/wilc1000/hif.c
+++ b/drivers/net/wireless/microchip/wilc1000/hif.c
@@ -163,7 +163,7 @@ int wilc_scan(struct wilc_vif *vif, u8 scan_source,
u32 index = 0;
u32 i, scan_timeout;
u8 *buffer;
- u8 valuesize = 0;
+ u32 valuesize = 0;
u8 *search_ssid_vals = NULL;
const u8 ch_list_len = request->n_channels;
struct host_if_drv *hif_drv = vif->hif_drv;
diff --git a/drivers/net/wireless/virtual/virt_wifi.c b/drivers/net/wireless/virtual/virt_wifi.c
index 4eae89376feb..cd6b66242bff 100644
--- a/drivers/net/wireless/virtual/virt_wifi.c
+++ b/drivers/net/wireless/virtual/virt_wifi.c
@@ -557,7 +557,6 @@ static int virt_wifi_newlink(struct net_device *dev,
eth_hw_addr_inherit(dev, priv->lowerdev);
netif_stacked_transfer_operstate(priv->lowerdev, dev);
- SET_NETDEV_DEV(dev, &priv->lowerdev->dev);
dev->ieee80211_ptr = kzalloc(sizeof(*dev->ieee80211_ptr), GFP_KERNEL);
if (!dev->ieee80211_ptr) {
diff --git a/drivers/nfc/pn533/uart.c b/drivers/nfc/pn533/uart.c
index a081bce61c29..49c399a57175 100644
--- a/drivers/nfc/pn533/uart.c
+++ b/drivers/nfc/pn533/uart.c
@@ -211,6 +211,9 @@ static size_t pn532_receive_buf(struct serdev_device *serdev,
timer_delete(&dev->cmd_timeout);
for (i = 0; i < count; i++) {
+ if (unlikely(!skb_tailroom(dev->recv_skb)))
+ skb_trim(dev->recv_skb, 0);
+
skb_put_u8(dev->recv_skb, *data++);
if (!pn532_uart_rx_is_frame(dev->recv_skb))
continue;
diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c
index 7cf7e809a8f5..a0d2985c6d03 100644
--- a/drivers/nvmem/imx-ocotp-ele.c
+++ b/drivers/nvmem/imx-ocotp-ele.c
@@ -131,6 +131,7 @@ static int imx_ocotp_cell_pp(void *context, const char *id, int index,
static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem,
struct nvmem_cell_info *cell)
{
+ cell->raw_len = round_up(cell->bytes, 4);
cell->read_post_process = imx_ocotp_cell_pp;
}
diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c
index 7bf7656d4f96..108d78d7f6cb 100644
--- a/drivers/nvmem/imx-ocotp.c
+++ b/drivers/nvmem/imx-ocotp.c
@@ -589,6 +589,7 @@ MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem,
struct nvmem_cell_info *cell)
{
+ cell->raw_len = round_up(cell->bytes, 4);
cell->read_post_process = imx_ocotp_cell_pp;
}
diff --git a/drivers/nvmem/zynqmp_nvmem.c b/drivers/nvmem/zynqmp_nvmem.c
index 7da717d6c7fa..d297ff150dc0 100644
--- a/drivers/nvmem/zynqmp_nvmem.c
+++ b/drivers/nvmem/zynqmp_nvmem.c
@@ -66,7 +66,7 @@ static int zynqmp_efuse_access(void *context, unsigned int offset,
dma_addr_t dma_buf;
size_t words = bytes / WORD_INBYTES;
int ret;
- int value;
+ unsigned int value;
char *data;
if (bytes % WORD_INBYTES != 0) {
@@ -80,7 +80,7 @@ static int zynqmp_efuse_access(void *context, unsigned int offset,
}
if (pufflag == 1 && flag == EFUSE_WRITE) {
- memcpy(&value, val, bytes);
+ memcpy(&value, val, sizeof(value));
if ((offset == EFUSE_PUF_START_OFFSET ||
offset == EFUSE_PUF_MID_OFFSET) &&
value & P_USER_0_64_UPPER_MASK) {
@@ -100,7 +100,7 @@ static int zynqmp_efuse_access(void *context, unsigned int offset,
if (!efuse)
return -ENOMEM;
- data = dma_alloc_coherent(dev, sizeof(bytes),
+ data = dma_alloc_coherent(dev, bytes,
&dma_buf, GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
@@ -134,7 +134,7 @@ static int zynqmp_efuse_access(void *context, unsigned int offset,
if (flag == EFUSE_READ)
memcpy(val, data, bytes);
efuse_access_err:
- dma_free_coherent(dev, sizeof(bytes),
+ dma_free_coherent(dev, bytes,
data, dma_buf);
efuse_data_fail:
dma_free_coherent(dev, sizeof(struct xilinx_efuse),
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index 0135dd0ae204..58ecde760b6e 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -856,7 +856,6 @@ static int reset_add_gpio_aux_device(struct device *parent,
ret = __auxiliary_device_add(adev, "reset");
if (ret) {
auxiliary_device_uninit(adev);
- kfree(adev);
return ret;
}
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
index a0dcab5dc4f2..23a32221e41a 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -953,6 +953,10 @@ static atomic_t zcrypt_step = ATOMIC_INIT(0);
/*
* The request distributor calls this function if it picked the CEXxC
* device to handle a modexpo request.
+ * This function assumes that ap_msg has been initialized with
+ * ap_init_apmsg() and thus a valid buffer with the size of
+ * ap_msg->bufsize is available within ap_msg. Also the caller has
+ * to make sure ap_release_apmsg() is always called even on failure.
* @zq: pointer to zcrypt_queue structure that identifies the
* CEXxC device to the request distributor
* @mex: pointer to the modexpo request buffer
@@ -964,21 +968,17 @@ static long zcrypt_msgtype6_modexpo(struct zcrypt_queue *zq,
struct ap_response_type *resp_type = &ap_msg->response;
int rc;
- ap_msg->msg = (void *)get_zeroed_page(GFP_KERNEL);
- if (!ap_msg->msg)
- return -ENOMEM;
- ap_msg->bufsize = PAGE_SIZE;
ap_msg->receive = zcrypt_msgtype6_receive;
ap_msg->psmid = (((unsigned long)current->pid) << 32) +
atomic_inc_return(&zcrypt_step);
rc = icamex_msg_to_type6mex_msgx(zq, ap_msg, mex);
if (rc)
- goto out_free;
+ goto out;
resp_type->type = CEXXC_RESPONSE_TYPE_ICA;
init_completion(&resp_type->work);
rc = ap_queue_message(zq->queue, ap_msg);
if (rc)
- goto out_free;
+ goto out;
rc = wait_for_completion_interruptible(&resp_type->work);
if (rc == 0) {
rc = ap_msg->rc;
@@ -991,15 +991,17 @@ static long zcrypt_msgtype6_modexpo(struct zcrypt_queue *zq,
ap_cancel_message(zq->queue, ap_msg);
}
-out_free:
- free_page((unsigned long)ap_msg->msg);
- ap_msg->msg = NULL;
+out:
return rc;
}
/*
* The request distributor calls this function if it picked the CEXxC
* device to handle a modexpo_crt request.
+ * This function assumes that ap_msg has been initialized with
+ * ap_init_apmsg() and thus a valid buffer with the size of
+ * ap_msg->bufsize is available within ap_msg. Also the caller has
+ * to make sure ap_release_apmsg() is always called even on failure.
* @zq: pointer to zcrypt_queue structure that identifies the
* CEXxC device to the request distributor
* @crt: pointer to the modexpoc_crt request buffer
@@ -1011,21 +1013,17 @@ static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_queue *zq,
struct ap_response_type *resp_type = &ap_msg->response;
int rc;
- ap_msg->msg = (void *)get_zeroed_page(GFP_KERNEL);
- if (!ap_msg->msg)
- return -ENOMEM;
- ap_msg->bufsize = PAGE_SIZE;
ap_msg->receive = zcrypt_msgtype6_receive;
ap_msg->psmid = (((unsigned long)current->pid) << 32) +
atomic_inc_return(&zcrypt_step);
rc = icacrt_msg_to_type6crt_msgx(zq, ap_msg, crt);
if (rc)
- goto out_free;
+ goto out;
resp_type->type = CEXXC_RESPONSE_TYPE_ICA;
init_completion(&resp_type->work);
rc = ap_queue_message(zq->queue, ap_msg);
if (rc)
- goto out_free;
+ goto out;
rc = wait_for_completion_interruptible(&resp_type->work);
if (rc == 0) {
rc = ap_msg->rc;
@@ -1038,9 +1036,7 @@ static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_queue *zq,
ap_cancel_message(zq->queue, ap_msg);
}
-out_free:
- free_page((unsigned long)ap_msg->msg);
- ap_msg->msg = NULL;
+out:
return rc;
}
diff --git a/drivers/spi/spi-amlogic-spifc-a4.c b/drivers/spi/spi-amlogic-spifc-a4.c
index b2589fe2425c..3393e1f30570 100644
--- a/drivers/spi/spi-amlogic-spifc-a4.c
+++ b/drivers/spi/spi-amlogic-spifc-a4.c
@@ -1066,6 +1066,13 @@ static const struct nand_ecc_engine_ops aml_sfc_ecc_engine_ops = {
.finish_io_req = aml_sfc_ecc_finish_io_req,
};
+static void aml_sfc_unregister_ecc_engine(void *data)
+{
+ struct nand_ecc_engine *eng = data;
+
+ nand_ecc_unregister_on_host_hw_engine(eng);
+}
+
static int aml_sfc_clk_init(struct aml_sfc *sfc)
{
sfc->gate_clk = devm_clk_get_enabled(sfc->dev, "gate");
@@ -1149,6 +1156,11 @@ static int aml_sfc_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(&pdev->dev, ret, "failed to register Aml host ecc engine.\n");
+ ret = devm_add_action_or_reset(dev, aml_sfc_unregister_ecc_engine,
+ &sfc->ecc_eng);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add ECC unregister action\n");
+
ret = of_property_read_u32(np, "amlogic,rx-adj", &val);
if (!ret)
sfc->rx_adj = val;
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index 51ed666a0fdd..3f8d451297fd 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -1478,14 +1478,6 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
if (refcount_read(&cqspi->inflight_ops) == 0)
return -ENODEV;
- if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) {
- ret = pm_runtime_resume_and_get(dev);
- if (ret) {
- dev_err(&mem->spi->dev, "resume failed with %d\n", ret);
- return ret;
- }
- }
-
if (!refcount_read(&cqspi->refcount))
return -EBUSY;
@@ -1497,6 +1489,14 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
return -EBUSY;
}
+ if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) {
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ dev_err(&mem->spi->dev, "resume failed with %d\n", ret);
+ goto dec_inflight_refcount;
+ }
+ }
+
ret = cqspi_mem_process(mem, op);
if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM)))
@@ -1505,6 +1505,7 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
if (ret)
dev_err(&mem->spi->dev, "operation failed with %d\n", ret);
+dec_inflight_refcount:
if (refcount_read(&cqspi->inflight_ops) > 1)
refcount_dec(&cqspi->inflight_ops);
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index acfcf870efd8..736120107184 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -958,10 +958,13 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
struct spi_controller *spi = data;
struct spi_geni_master *mas = spi_controller_get_devdata(spi);
struct geni_se *se = &mas->se;
- u32 m_irq;
+ u32 m_irq, dma_tx_status, dma_rx_status;
m_irq = readl(se->base + SE_GENI_M_IRQ_STATUS);
- if (!m_irq)
+ dma_tx_status = readl_relaxed(se->base + SE_DMA_TX_IRQ_STAT);
+ dma_rx_status = readl_relaxed(se->base + SE_DMA_RX_IRQ_STAT);
+
+ if (!m_irq && !dma_tx_status && !dma_rx_status)
return IRQ_NONE;
if (m_irq & (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |
@@ -1009,8 +1012,6 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
}
} else if (mas->cur_xfer_mode == GENI_SE_DMA) {
const struct spi_transfer *xfer = mas->cur_xfer;
- u32 dma_tx_status = readl_relaxed(se->base + SE_DMA_TX_IRQ_STAT);
- u32 dma_rx_status = readl_relaxed(se->base + SE_DMA_RX_IRQ_STAT);
if (dma_tx_status)
writel(dma_tx_status, se->base + SE_DMA_TX_IRQ_CLR);
diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c
index f36fd36da269..2988ff288ff0 100644
--- a/drivers/spi/spi-stm32-ospi.c
+++ b/drivers/spi/spi-stm32-ospi.c
@@ -939,13 +939,15 @@ static int stm32_ospi_probe(struct platform_device *pdev)
if (ret) {
/* Disable ospi */
writel_relaxed(0, ospi->regs_base + OSPI_CR);
- goto err_pm_resume;
+ goto err_reset_control;
}
pm_runtime_put_autosuspend(ospi->dev);
return 0;
+err_reset_control:
+ reset_control_release(ospi->rstc);
err_pm_resume:
pm_runtime_put_sync_suspend(ospi->dev);
@@ -963,11 +965,8 @@ static int stm32_ospi_probe(struct platform_device *pdev)
static void stm32_ospi_remove(struct platform_device *pdev)
{
struct stm32_ospi *ospi = platform_get_drvdata(pdev);
- int ret;
- ret = pm_runtime_resume_and_get(ospi->dev);
- if (ret < 0)
- return;
+ pm_runtime_resume_and_get(ospi->dev);
spi_unregister_controller(ospi->ctrl);
/* Disable ospi */
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c
index 01a8e349dc4d..156f934049f1 100644
--- a/drivers/target/loopback/tcm_loop.c
+++ b/drivers/target/loopback/tcm_loop.c
@@ -26,6 +26,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/configfs.h>
+#include <linux/blk-mq.h>
#include <scsi/scsi.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_host.h>
@@ -268,15 +269,27 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)
return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED;
}
+static bool tcm_loop_flush_work_iter(struct request *rq, void *data)
+{
+ struct scsi_cmnd *sc = blk_mq_rq_to_pdu(rq);
+ struct tcm_loop_cmd *tl_cmd = scsi_cmd_priv(sc);
+ struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd;
+
+ flush_work(&se_cmd->work);
+ return true;
+}
+
static int tcm_loop_target_reset(struct scsi_cmnd *sc)
{
struct tcm_loop_hba *tl_hba;
struct tcm_loop_tpg *tl_tpg;
+ struct Scsi_Host *sh = sc->device->host;
+ int ret;
/*
* Locate the tcm_loop_hba_t pointer
*/
- tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
+ tl_hba = *(struct tcm_loop_hba **)shost_priv(sh);
if (!tl_hba) {
pr_err("Unable to perform device reset without active I_T Nexus\n");
return FAILED;
@@ -285,11 +298,38 @@ static int tcm_loop_target_reset(struct scsi_cmnd *sc)
* Locate the tl_tpg pointer from TargetID in sc->device->id
*/
tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
- if (tl_tpg) {
- tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE;
- return SUCCESS;
- }
- return FAILED;
+ if (!tl_tpg)
+ return FAILED;
+
+ /*
+ * Issue a LUN_RESET to drain all commands that the target core
+ * knows about. This handles commands not yet marked CMD_T_COMPLETE.
+ */
+ ret = tcm_loop_issue_tmr(tl_tpg, sc->device->lun, 0, TMR_LUN_RESET);
+ if (ret != TMR_FUNCTION_COMPLETE)
+ return FAILED;
+
+ /*
+ * Flush any deferred target core completion work that may still be
+ * queued. Commands that already had CMD_T_COMPLETE set before the TMR
+ * are skipped by the TMR drain, but their async completion work
+ * (transport_lun_remove_cmd → percpu_ref_put, release_cmd → scsi_done)
+ * may still be pending in target_completion_wq.
+ *
+ * The SCSI EH will reuse in-flight scsi_cmnd structures for recovery
+ * commands (e.g. TUR) immediately after this handler returns SUCCESS —
+ * if deferred work is still pending, the memset in queuecommand would
+ * zero the se_cmd while the work accesses it, leaking the LUN
+ * percpu_ref and hanging configfs unlink forever.
+ *
+ * Use blk_mq_tagset_busy_iter() to find all started requests and
+ * flush_work() on each — the same pattern used by mpi3mr, scsi_debug,
+ * and other SCSI drivers to drain outstanding commands during reset.
+ */
+ blk_mq_tagset_busy_iter(&sh->tag_set, tcm_loop_flush_work_iter, NULL);
+
+ tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE;
+ return SUCCESS;
}
static const struct scsi_host_template tcm_loop_driver_template = {
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index b2610073e8cc..26d52f1f36df 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -276,7 +276,7 @@ fd_execute_rw_aio(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents,
ssize_t len = 0;
int ret = 0, i;
- aio_cmd = kmalloc(struct_size(aio_cmd, bvecs, sgl_nents), GFP_KERNEL);
+ aio_cmd = kzalloc(struct_size(aio_cmd, bvecs, sgl_nents), GFP_KERNEL);
if (!aio_cmd)
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 89758c9934ec..3a19ae8fb5a0 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -41,6 +41,8 @@ static struct thermal_governor *def_governor;
static bool thermal_pm_suspended;
+static struct workqueue_struct *thermal_wq __ro_after_init;
+
/*
* Governor section: set of functions to handle thermal governors
*
@@ -313,7 +315,7 @@ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
if (delay > HZ)
delay = round_jiffies_relative(delay);
- mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, delay);
+ mod_delayed_work(thermal_wq, &tz->poll_queue, delay);
}
static void thermal_zone_recheck(struct thermal_zone_device *tz, int error)
@@ -1636,6 +1638,7 @@ thermal_zone_device_register_with_trips(const char *type,
device_del(&tz->device);
release_device:
put_device(&tz->device);
+ wait_for_completion(&tz->removal);
remove_id:
ida_free(&thermal_tz_ida, id);
free_tzp:
@@ -1781,6 +1784,10 @@ static void thermal_zone_device_resume(struct work_struct *work)
guard(thermal_zone)(tz);
+ /* If the thermal zone is going away, there's nothing to do. */
+ if (tz->state & TZ_STATE_FLAG_EXIT)
+ return;
+
tz->state &= ~(TZ_STATE_FLAG_SUSPENDED | TZ_STATE_FLAG_RESUMING);
thermal_debug_tz_resume(tz);
@@ -1807,6 +1814,9 @@ static void thermal_zone_pm_prepare(struct thermal_zone_device *tz)
}
tz->state |= TZ_STATE_FLAG_SUSPENDED;
+
+ /* Prevent new work from getting to the workqueue subsequently. */
+ cancel_delayed_work(&tz->poll_queue);
}
static void thermal_pm_notify_prepare(void)
@@ -1825,8 +1835,6 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz)
{
guard(thermal_zone)(tz);
- cancel_delayed_work(&tz->poll_queue);
-
reinit_completion(&tz->resume);
tz->state |= TZ_STATE_FLAG_RESUMING;
@@ -1836,7 +1844,7 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz)
*/
INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_resume);
/* Queue up the work without a delay. */
- mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, 0);
+ mod_delayed_work(thermal_wq, &tz->poll_queue, 0);
}
static void thermal_pm_notify_complete(void)
@@ -1859,6 +1867,11 @@ static int thermal_pm_notify(struct notifier_block *nb,
case PM_RESTORE_PREPARE:
case PM_SUSPEND_PREPARE:
thermal_pm_notify_prepare();
+ /*
+ * Allow any leftover thermal work items already on the
+ * worqueue to complete so they don't get in the way later.
+ */
+ flush_workqueue(thermal_wq);
break;
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
@@ -1891,9 +1904,16 @@ static int __init thermal_init(void)
if (result)
goto error;
+ thermal_wq = alloc_workqueue("thermal_events",
+ WQ_FREEZABLE | WQ_POWER_EFFICIENT | WQ_PERCPU, 0);
+ if (!thermal_wq) {
+ result = -ENOMEM;
+ goto unregister_netlink;
+ }
+
result = thermal_register_governors();
if (result)
- goto unregister_netlink;
+ goto destroy_workqueue;
thermal_class = kzalloc(sizeof(*thermal_class), GFP_KERNEL);
if (!thermal_class) {
@@ -1920,6 +1940,8 @@ static int __init thermal_init(void)
unregister_governors:
thermal_unregister_governors();
+destroy_workqueue:
+ destroy_workqueue(thermal_wq);
unregister_netlink:
thermal_netlink_exit();
error:
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index 6d0c9d37c55d..b03792390c6a 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -1020,7 +1020,7 @@ static bool nhi_wake_supported(struct pci_dev *pdev)
* If power rails are sustainable for wakeup from S4 this
* property is set by the BIOS.
*/
- if (device_property_read_u8(&pdev->dev, "WAKE_SUPPORTED", &val))
+ if (!device_property_read_u8(&pdev->dev, "WAKE_SUPPORTED", &val))
return !!val;
return true;
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 83a285577708..738fac2718e6 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1915,6 +1915,24 @@ static void leave_alt_screen(struct vc_data *vc)
dest = ((u16 *)vc->vc_origin) + r * vc->vc_cols;
memcpy(dest, src, 2 * cols);
}
+ /*
+ * If the console was resized while in the alternate screen,
+ * resize the saved unicode buffer to the current dimensions.
+ * On allocation failure new_uniscr is NULL, causing the old
+ * buffer to be freed and vc_uni_lines to be lazily rebuilt
+ * via vc_uniscr_check() when next needed.
+ */
+ if (vc->vc_saved_uni_lines &&
+ (vc->vc_saved_rows != vc->vc_rows ||
+ vc->vc_saved_cols != vc->vc_cols)) {
+ u32 **new_uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
+
+ if (new_uniscr)
+ vc_uniscr_copy_area(new_uniscr, vc->vc_cols, vc->vc_rows,
+ vc->vc_saved_uni_lines, cols, 0, rows);
+ vc_uniscr_free(vc->vc_saved_uni_lines);
+ vc->vc_saved_uni_lines = new_uniscr;
+ }
vc_uniscr_set(vc, vc->vc_saved_uni_lines);
vc->vc_saved_uni_lines = NULL;
restore_cur(vc);
diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c
index 168707213ed9..610ad2adca88 100644
--- a/drivers/usb/cdns3/cdns3-gadget.c
+++ b/drivers/usb/cdns3/cdns3-gadget.c
@@ -2589,6 +2589,9 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
struct cdns3_request *priv_req;
int ret = 0;
+ if (!ep->desc)
+ return -ESHUTDOWN;
+
request->actual = 0;
request->status = -EINPROGRESS;
priv_req = to_cdns3_request(request);
@@ -3428,6 +3431,7 @@ static int __cdns3_gadget_init(struct cdns *cdns)
ret = cdns3_gadget_start(cdns);
if (ret) {
pm_runtime_put_sync(cdns->dev);
+ cdns_drd_gadget_off(cdns);
return ret;
}
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 8b2446463a50..76ce2e6c9864 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1225,6 +1225,12 @@ static int acm_probe(struct usb_interface *intf,
if (!data_interface || !control_interface)
return -ENODEV;
goto skip_normal_probe;
+ } else if (quirks == NO_UNION_12) {
+ data_interface = usb_ifnum_to_if(usb_dev, 2);
+ control_interface = usb_ifnum_to_if(usb_dev, 1);
+ if (!data_interface || !control_interface)
+ return -ENODEV;
+ goto skip_normal_probe;
}
/* normal probing*/
@@ -1748,6 +1754,9 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x045b, 0x024D), /* Renesas R-Car E3 USB Download mode */
.driver_info = DISABLE_ECHO, /* Don't echo banner */
},
+ { USB_DEVICE(0x04b8, 0x0d12), /* EPSON HMD Com&Sens */
+ .driver_info = NO_UNION_12, /* union descriptor is garbage */
+ },
{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 76f73853a60b..25fd5329a878 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -114,3 +114,4 @@ struct acm {
#define SEND_ZERO_PACKET BIT(6)
#define DISABLE_ECHO BIT(7)
#define MISSING_CAP_BRK BIT(8)
+#define NO_UNION_12 BIT(9)
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 4cee3fa74e0d..2aa964dbb353 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -254,6 +254,9 @@ static int usbtmc_release(struct inode *inode, struct file *file)
list_del(&file_data->file_elem);
spin_unlock_irq(&file_data->data->dev_lock);
+
+ /* flush anchored URBs */
+ usbtmc_draw_down(file_data);
mutex_unlock(&file_data->data->io_mutex);
kref_put(&file_data->data->kref, usbtmc_delete);
diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c
index 4a2ee447b213..d895cf6532a2 100644
--- a/drivers/usb/common/ulpi.c
+++ b/drivers/usb/common/ulpi.c
@@ -331,10 +331,9 @@ struct ulpi *ulpi_register_interface(struct device *dev,
ulpi->ops = ops;
ret = ulpi_register(dev, ulpi);
- if (ret) {
- kfree(ulpi);
+ if (ret)
return ERR_PTR(ret);
- }
+
return ulpi;
}
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index d29edc7c616a..74b8bdc27dbf 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1415,14 +1415,16 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
int status = 0;
int i = 0, n = 0;
struct usb_interface *intf;
+ bool offload_active = false;
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED)
goto done;
+ usb_offload_set_pm_locked(udev, true);
if (msg.event == PM_EVENT_SUSPEND && usb_offload_check(udev)) {
dev_dbg(&udev->dev, "device offloaded, skip suspend.\n");
- udev->offload_at_suspend = 1;
+ offload_active = true;
}
/* Suspend all the interfaces and then udev itself */
@@ -1436,8 +1438,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
* interrupt urbs, allowing interrupt events to be
* handled during system suspend.
*/
- if (udev->offload_at_suspend &&
- intf->needs_remote_wakeup) {
+ if (offload_active && intf->needs_remote_wakeup) {
dev_dbg(&intf->dev,
"device offloaded, skip suspend.\n");
continue;
@@ -1452,7 +1453,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
}
}
if (status == 0) {
- if (!udev->offload_at_suspend)
+ if (!offload_active)
status = usb_suspend_device(udev, msg);
/*
@@ -1498,7 +1499,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
*/
} else {
udev->can_submit = 0;
- if (!udev->offload_at_suspend) {
+ if (!offload_active) {
for (i = 0; i < 16; ++i) {
usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
usb_hcd_flush_endpoint(udev, udev->ep_in[i]);
@@ -1507,6 +1508,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
}
done:
+ if (status != 0)
+ usb_offload_set_pm_locked(udev, false);
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
return status;
}
@@ -1536,16 +1539,19 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
int status = 0;
int i;
struct usb_interface *intf;
+ bool offload_active = false;
if (udev->state == USB_STATE_NOTATTACHED) {
status = -ENODEV;
goto done;
}
udev->can_submit = 1;
+ if (msg.event == PM_EVENT_RESUME)
+ offload_active = usb_offload_check(udev);
/* Resume the device */
if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume) {
- if (!udev->offload_at_suspend)
+ if (!offload_active)
status = usb_resume_device(udev, msg);
else
dev_dbg(&udev->dev,
@@ -1562,8 +1568,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
* pending interrupt urbs, allowing interrupt events
* to be handled during system suspend.
*/
- if (udev->offload_at_suspend &&
- intf->needs_remote_wakeup) {
+ if (offload_active && intf->needs_remote_wakeup) {
dev_dbg(&intf->dev,
"device offloaded, skip resume.\n");
continue;
@@ -1572,11 +1577,11 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
udev->reset_resume);
}
}
- udev->offload_at_suspend = 0;
usb_mark_last_busy(udev);
done:
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
+ usb_offload_set_pm_locked(udev, false);
if (!status)
udev->reset_resume = 0;
return status;
diff --git a/drivers/usb/core/offload.c b/drivers/usb/core/offload.c
index 7c699f1b8d2b..9db3cfedd29c 100644
--- a/drivers/usb/core/offload.c
+++ b/drivers/usb/core/offload.c
@@ -25,33 +25,30 @@
*/
int usb_offload_get(struct usb_device *udev)
{
- int ret;
+ int ret = 0;
- usb_lock_device(udev);
- if (udev->state == USB_STATE_NOTATTACHED) {
- usb_unlock_device(udev);
+ if (!usb_get_dev(udev))
return -ENODEV;
- }
- if (udev->state == USB_STATE_SUSPENDED ||
- udev->offload_at_suspend) {
- usb_unlock_device(udev);
- return -EBUSY;
+ if (pm_runtime_get_if_active(&udev->dev) != 1) {
+ ret = -EBUSY;
+ goto err_rpm;
}
- /*
- * offload_usage could only be modified when the device is active, since
- * it will alter the suspend flow of the device.
- */
- ret = usb_autoresume_device(udev);
- if (ret < 0) {
- usb_unlock_device(udev);
- return ret;
+ spin_lock(&udev->offload_lock);
+
+ if (udev->offload_pm_locked) {
+ ret = -EAGAIN;
+ goto err;
}
udev->offload_usage++;
- usb_autosuspend_device(udev);
- usb_unlock_device(udev);
+
+err:
+ spin_unlock(&udev->offload_lock);
+ pm_runtime_put_autosuspend(&udev->dev);
+err_rpm:
+ usb_put_dev(udev);
return ret;
}
@@ -69,35 +66,32 @@ EXPORT_SYMBOL_GPL(usb_offload_get);
*/
int usb_offload_put(struct usb_device *udev)
{
- int ret;
+ int ret = 0;
- usb_lock_device(udev);
- if (udev->state == USB_STATE_NOTATTACHED) {
- usb_unlock_device(udev);
+ if (!usb_get_dev(udev))
return -ENODEV;
- }
- if (udev->state == USB_STATE_SUSPENDED ||
- udev->offload_at_suspend) {
- usb_unlock_device(udev);
- return -EBUSY;
+ if (pm_runtime_get_if_active(&udev->dev) != 1) {
+ ret = -EBUSY;
+ goto err_rpm;
}
- /*
- * offload_usage could only be modified when the device is active, since
- * it will alter the suspend flow of the device.
- */
- ret = usb_autoresume_device(udev);
- if (ret < 0) {
- usb_unlock_device(udev);
- return ret;
+ spin_lock(&udev->offload_lock);
+
+ if (udev->offload_pm_locked) {
+ ret = -EAGAIN;
+ goto err;
}
/* Drop the count when it wasn't 0, ignore the operation otherwise. */
if (udev->offload_usage)
udev->offload_usage--;
- usb_autosuspend_device(udev);
- usb_unlock_device(udev);
+
+err:
+ spin_unlock(&udev->offload_lock);
+ pm_runtime_put_autosuspend(&udev->dev);
+err_rpm:
+ usb_put_dev(udev);
return ret;
}
@@ -112,25 +106,47 @@ EXPORT_SYMBOL_GPL(usb_offload_put);
* management.
*
* The caller must hold @udev's device lock. In addition, the caller should
- * ensure downstream usb devices are all either suspended or marked as
- * "offload_at_suspend" to ensure the correctness of the return value.
+ * ensure the device itself and the downstream usb devices are all marked as
+ * "offload_pm_locked" to ensure the correctness of the return value.
*
* Returns true on any offload activity, false otherwise.
*/
bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex)
{
struct usb_device *child;
- bool active;
+ bool active = false;
int port1;
+ if (udev->offload_usage)
+ return true;
+
usb_hub_for_each_child(udev, port1, child) {
usb_lock_device(child);
active = usb_offload_check(child);
usb_unlock_device(child);
+
if (active)
- return true;
+ break;
}
- return !!udev->offload_usage;
+ return active;
}
EXPORT_SYMBOL_GPL(usb_offload_check);
+
+/**
+ * usb_offload_set_pm_locked - set the PM lock state of a USB device
+ * @udev: the USB device to modify
+ * @locked: the new lock state
+ *
+ * Setting @locked to true prevents offload_usage from being modified. This
+ * ensures that offload activities cannot be started or stopped during critical
+ * power management transitions, maintaining a stable state for the duration
+ * of the transition.
+ */
+void usb_offload_set_pm_locked(struct usb_device *udev, bool locked)
+{
+ spin_lock(&udev->offload_lock);
+ udev->offload_pm_locked = locked;
+ spin_unlock(&udev->offload_lock);
+}
+EXPORT_SYMBOL_GPL(usb_offload_set_pm_locked);
diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c
index 4bba1c275740..4d966cc9cdc9 100644
--- a/drivers/usb/core/phy.c
+++ b/drivers/usb/core/phy.c
@@ -114,7 +114,7 @@ EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc);
struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev)
{
struct usb_phy_roothub *phy_roothub;
- int num_phys;
+ int num_phys, usb2_phy_index;
if (!IS_ENABLED(CONFIG_GENERIC_PHY))
return NULL;
@@ -124,6 +124,16 @@ struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev)
if (num_phys <= 0)
return NULL;
+ /*
+ * If 'usb2-phy' is not present, usb_phy_roothub_alloc() added
+ * all PHYs to the primary HCD's phy_roothub already, so skip
+ * adding 'usb3-phy' here to avoid double use of that.
+ */
+ usb2_phy_index = of_property_match_string(dev->of_node, "phy-names",
+ "usb2-phy");
+ if (usb2_phy_index < 0)
+ return NULL;
+
phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL);
if (!phy_roothub)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 65168eb89295..34b1f7df3529 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -402,6 +402,7 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Silicon Motion Flash Drive */
{ USB_DEVICE(0x090c, 0x1000), .driver_info = USB_QUIRK_DELAY_INIT },
+ { USB_DEVICE(0x090c, 0x2000), .driver_info = USB_QUIRK_DELAY_INIT },
/* Sound Devices USBPre2 */
{ USB_DEVICE(0x0926, 0x0202), .driver_info =
@@ -493,6 +494,8 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Razer - Razer Blade Keyboard */
{ USB_DEVICE(0x1532, 0x0116), .driver_info =
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+ /* Razer - Razer Kiyo Pro Webcam */
+ { USB_DEVICE(0x1532, 0x0e05), .driver_info = USB_QUIRK_NO_LPM },
/* Lenovo ThinkPad OneLink+ Dock twin hub controllers (VIA Labs VL812) */
{ USB_DEVICE(0x17ef, 0x1018), .driver_info = USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index e740f7852bcd..8f7ca084010f 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -671,6 +671,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
set_dev_node(&dev->dev, dev_to_node(bus->sysdev));
dev->state = USB_STATE_ATTACHED;
dev->lpm_disable_count = 1;
+ spin_lock_init(&dev->offload_lock);
dev->offload_usage = 0;
atomic_set(&dev->urbnum, 0);
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 0637bfbc054e..a6c2b8de3908 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -4607,7 +4607,9 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget)
/* Exit clock gating when driver is stopped. */
if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE &&
hsotg->bus_suspended && !hsotg->params.no_clock_gating) {
+ spin_lock_irqsave(&hsotg->lock, flags);
dwc2_gadget_exit_clock_gating(hsotg, 0);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
}
/* all endpoints should be shutdown */
diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index 675d2bc538a4..e315f18b7f9f 100644
--- a/drivers/usb/gadget/function/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
@@ -681,6 +681,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_ep *ep;
struct f_ecm_opts *ecm_opts;
+ struct net_device *net __free(detach_gadget) = NULL;
struct usb_request *request __free(free_usb_request) = NULL;
if (!can_support_ecm(cdev->gadget))
@@ -688,18 +689,18 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst);
- mutex_lock(&ecm_opts->lock);
-
- gether_set_gadget(ecm_opts->net, cdev->gadget);
-
- if (!ecm_opts->bound) {
- status = gether_register_netdev(ecm_opts->net);
- ecm_opts->bound = true;
- }
-
- mutex_unlock(&ecm_opts->lock);
- if (status)
- return status;
+ scoped_guard(mutex, &ecm_opts->lock)
+ if (ecm_opts->bind_count == 0 && !ecm_opts->bound) {
+ if (!device_is_registered(&ecm_opts->net->dev)) {
+ gether_set_gadget(ecm_opts->net, cdev->gadget);
+ status = gether_register_netdev(ecm_opts->net);
+ } else
+ status = gether_attach_gadget(ecm_opts->net, cdev->gadget);
+
+ if (status)
+ return status;
+ net = ecm_opts->net;
+ }
ecm_string_defs[1].s = ecm->ethaddr;
@@ -790,6 +791,9 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
ecm->notify_req = no_free_ptr(request);
+ ecm_opts->bind_count++;
+ retain_and_null_ptr(net);
+
DBG(cdev, "CDC Ethernet: IN/%s OUT/%s NOTIFY/%s\n",
ecm->port.in_ep->name, ecm->port.out_ep->name,
ecm->notify->name);
@@ -836,7 +840,7 @@ static void ecm_free_inst(struct usb_function_instance *f)
struct f_ecm_opts *opts;
opts = container_of(f, struct f_ecm_opts, func_inst);
- if (opts->bound)
+ if (device_is_registered(&opts->net->dev))
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
@@ -906,9 +910,12 @@ static void ecm_free(struct usb_function *f)
static void ecm_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_ecm *ecm = func_to_ecm(f);
+ struct f_ecm_opts *ecm_opts;
DBG(c->cdev, "ecm unbind\n");
+ ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst);
+
usb_free_all_descriptors(f);
if (atomic_read(&ecm->notify_count)) {
@@ -918,6 +925,10 @@ static void ecm_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(ecm->notify_req->buf);
usb_ep_free_request(ecm->notify, ecm->notify_req);
+
+ ecm_opts->bind_count--;
+ if (ecm_opts->bind_count == 0 && !ecm_opts->bound)
+ gether_detach_gadget(ecm_opts->net);
}
static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index edbbadad6138..36f4295e6d63 100644
--- a/drivers/usb/gadget/function/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -7,6 +7,7 @@
* Copyright (C) 2009 EF Johnson Technologies
*/
+#include <linux/cleanup.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
@@ -251,24 +252,22 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_ep *ep;
struct f_eem_opts *eem_opts;
+ struct net_device *net __free(detach_gadget) = NULL;
eem_opts = container_of(f->fi, struct f_eem_opts, func_inst);
- /*
- * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
- * configurations are bound in sequence with list_for_each_entry,
- * in each configuration its functions are bound in sequence
- * with list_for_each_entry, so we assume no race condition
- * with regard to eem_opts->bound access
- */
- if (!eem_opts->bound) {
- mutex_lock(&eem_opts->lock);
- gether_set_gadget(eem_opts->net, cdev->gadget);
- status = gether_register_netdev(eem_opts->net);
- mutex_unlock(&eem_opts->lock);
- if (status)
- return status;
- eem_opts->bound = true;
- }
+
+ scoped_guard(mutex, &eem_opts->lock)
+ if (eem_opts->bind_count == 0 && !eem_opts->bound) {
+ if (!device_is_registered(&eem_opts->net->dev)) {
+ gether_set_gadget(eem_opts->net, cdev->gadget);
+ status = gether_register_netdev(eem_opts->net);
+ } else
+ status = gether_attach_gadget(eem_opts->net, cdev->gadget);
+
+ if (status)
+ return status;
+ net = eem_opts->net;
+ }
us = usb_gstrings_attach(cdev, eem_strings,
ARRAY_SIZE(eem_string_defs));
@@ -279,21 +278,19 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
eem->ctrl_id = status;
eem_intf.bInterfaceNumber = status;
- status = -ENODEV;
-
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
eem->port.in_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
eem->port.out_ep = ep;
/* support all relevant hardware speeds... we expect that when
@@ -309,16 +306,14 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function,
eem_ss_function, eem_ss_function);
if (status)
- goto fail;
+ return status;
+
+ eem_opts->bind_count++;
+ retain_and_null_ptr(net);
DBG(cdev, "CDC Ethernet (EEM): IN/%s OUT/%s\n",
eem->port.in_ep->name, eem->port.out_ep->name);
return 0;
-
-fail:
- ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
- return status;
}
static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
@@ -597,7 +592,7 @@ static void eem_free_inst(struct usb_function_instance *f)
struct f_eem_opts *opts;
opts = container_of(f, struct f_eem_opts, func_inst);
- if (opts->bound)
+ if (device_is_registered(&opts->net->dev))
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
@@ -640,9 +635,17 @@ static void eem_free(struct usb_function *f)
static void eem_unbind(struct usb_configuration *c, struct usb_function *f)
{
+ struct f_eem_opts *opts;
+
DBG(c->cdev, "eem unbind\n");
+ opts = container_of(f->fi, struct f_eem_opts, func_inst);
+
usb_free_all_descriptors(f);
+
+ opts->bind_count--;
+ if (opts->bind_count == 0 && !opts->bound)
+ gether_detach_gadget(opts->net);
}
static struct usb_function *eem_alloc(struct usb_function_instance *fi)
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index f1f8e7c1d528..491bd6ee14e0 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -1262,17 +1262,8 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
if (status)
goto fail;
- spin_lock_init(&hidg->write_spinlock);
hidg->write_pending = 1;
hidg->req = NULL;
- spin_lock_init(&hidg->read_spinlock);
- spin_lock_init(&hidg->get_report_spinlock);
- init_waitqueue_head(&hidg->write_queue);
- init_waitqueue_head(&hidg->read_queue);
- init_waitqueue_head(&hidg->get_queue);
- init_waitqueue_head(&hidg->get_id_queue);
- INIT_LIST_HEAD(&hidg->completed_out_req);
- INIT_LIST_HEAD(&hidg->report_list);
INIT_WORK(&hidg->work, get_report_workqueue_handler);
hidg->workqueue = alloc_workqueue("report_work",
@@ -1608,6 +1599,16 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
mutex_lock(&opts->lock);
+ spin_lock_init(&hidg->write_spinlock);
+ spin_lock_init(&hidg->read_spinlock);
+ spin_lock_init(&hidg->get_report_spinlock);
+ init_waitqueue_head(&hidg->write_queue);
+ init_waitqueue_head(&hidg->read_queue);
+ init_waitqueue_head(&hidg->get_queue);
+ init_waitqueue_head(&hidg->get_id_queue);
+ INIT_LIST_HEAD(&hidg->completed_out_req);
+ INIT_LIST_HEAD(&hidg->report_list);
+
device_initialize(&hidg->dev);
hidg->dev.release = hidg_release;
hidg->dev.class = &hidg_class;
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index 7451e7cb7a85..ec397291e40b 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -11,6 +11,7 @@
/* #define VERBOSE_DEBUG */
+#include <linux/cleanup.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -665,6 +666,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
struct f_rndis_opts *rndis_opts;
struct usb_os_desc_table *os_desc_table __free(kfree) = NULL;
+ struct net_device *net __free(detach_gadget) = NULL;
struct usb_request *request __free(free_usb_request) = NULL;
if (!can_support_rndis(c))
@@ -678,23 +680,22 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
return -ENOMEM;
}
- rndis_iad_descriptor.bFunctionClass = rndis_opts->class;
- rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass;
- rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol;
-
- /*
- * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
- * configurations are bound in sequence with list_for_each_entry,
- * in each configuration its functions are bound in sequence
- * with list_for_each_entry, so we assume no race condition
- * with regard to rndis_opts->bound access
- */
- if (!rndis_opts->bound) {
- gether_set_gadget(rndis_opts->net, cdev->gadget);
- status = gether_register_netdev(rndis_opts->net);
- if (status)
- return status;
- rndis_opts->bound = true;
+ scoped_guard(mutex, &rndis_opts->lock) {
+ rndis_iad_descriptor.bFunctionClass = rndis_opts->class;
+ rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass;
+ rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol;
+
+ if (rndis_opts->bind_count == 0 && !rndis_opts->borrowed_net) {
+ if (!device_is_registered(&rndis_opts->net->dev)) {
+ gether_set_gadget(rndis_opts->net, cdev->gadget);
+ status = gether_register_netdev(rndis_opts->net);
+ } else
+ status = gether_attach_gadget(rndis_opts->net, cdev->gadget);
+
+ if (status)
+ return status;
+ net = rndis_opts->net;
+ }
}
us = usb_gstrings_attach(cdev, rndis_strings,
@@ -793,6 +794,9 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
}
rndis->notify_req = no_free_ptr(request);
+ rndis_opts->bind_count++;
+ retain_and_null_ptr(net);
+
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
* until we're activated via set_alt().
@@ -809,11 +813,11 @@ void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
struct f_rndis_opts *opts;
opts = container_of(f, struct f_rndis_opts, func_inst);
- if (opts->bound)
+ if (device_is_registered(&opts->net->dev))
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
- opts->borrowed_net = opts->bound = true;
+ opts->borrowed_net = true;
opts->net = net;
}
EXPORT_SYMBOL_GPL(rndis_borrow_net);
@@ -871,7 +875,7 @@ static void rndis_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_rndis_opts, func_inst);
if (!opts->borrowed_net) {
- if (opts->bound)
+ if (device_is_registered(&opts->net->dev))
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
@@ -940,6 +944,9 @@ static void rndis_free(struct usb_function *f)
static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_rndis *rndis = func_to_rndis(f);
+ struct f_rndis_opts *rndis_opts;
+
+ rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);
kfree(f->os_desc_table);
f->os_desc_n = 0;
@@ -947,6 +954,10 @@ static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(rndis->notify_req->buf);
usb_ep_free_request(rndis->notify, rndis->notify_req);
+
+ rndis_opts->bind_count--;
+ if (rndis_opts->bind_count == 0 && !rndis_opts->borrowed_net)
+ gether_detach_gadget(rndis_opts->net);
}
static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c
index ea3fdd842462..638e3138f8a0 100644
--- a/drivers/usb/gadget/function/f_subset.c
+++ b/drivers/usb/gadget/function/f_subset.c
@@ -6,6 +6,7 @@
* Copyright (C) 2008 Nokia Corporation
*/
+#include <linux/cleanup.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -298,25 +299,22 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_ep *ep;
struct f_gether_opts *gether_opts;
+ struct net_device *net __free(detach_gadget) = NULL;
gether_opts = container_of(f->fi, struct f_gether_opts, func_inst);
- /*
- * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
- * configurations are bound in sequence with list_for_each_entry,
- * in each configuration its functions are bound in sequence
- * with list_for_each_entry, so we assume no race condition
- * with regard to gether_opts->bound access
- */
- if (!gether_opts->bound) {
- mutex_lock(&gether_opts->lock);
- gether_set_gadget(gether_opts->net, cdev->gadget);
- status = gether_register_netdev(gether_opts->net);
- mutex_unlock(&gether_opts->lock);
- if (status)
- return status;
- gether_opts->bound = true;
- }
+ scoped_guard(mutex, &gether_opts->lock)
+ if (gether_opts->bind_count == 0 && !gether_opts->bound) {
+ if (!device_is_registered(&gether_opts->net->dev)) {
+ gether_set_gadget(gether_opts->net, cdev->gadget);
+ status = gether_register_netdev(gether_opts->net);
+ } else
+ status = gether_attach_gadget(gether_opts->net, cdev->gadget);
+
+ if (status)
+ return status;
+ net = gether_opts->net;
+ }
us = usb_gstrings_attach(cdev, geth_strings,
ARRAY_SIZE(geth_string_defs));
@@ -329,20 +327,18 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ return status;
subset_data_intf.bInterfaceNumber = status;
- status = -ENODEV;
-
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
geth->port.in_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc);
if (!ep)
- goto fail;
+ return -ENODEV;
geth->port.out_ep = ep;
/* support all relevant hardware speeds... we expect that when
@@ -360,21 +356,19 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function,
ss_eth_function, ss_eth_function);
if (status)
- goto fail;
+ return status;
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
* until we're activated via set_alt().
*/
+ gether_opts->bind_count++;
+ retain_and_null_ptr(net);
+
DBG(cdev, "CDC Subset: IN/%s OUT/%s\n",
geth->port.in_ep->name, geth->port.out_ep->name);
return 0;
-
-fail:
- ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
-
- return status;
}
static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item)
@@ -417,7 +411,7 @@ static void geth_free_inst(struct usb_function_instance *f)
struct f_gether_opts *opts;
opts = container_of(f, struct f_gether_opts, func_inst);
- if (opts->bound)
+ if (device_is_registered(&opts->net->dev))
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
@@ -449,15 +443,28 @@ static struct usb_function_instance *geth_alloc_inst(void)
static void geth_free(struct usb_function *f)
{
struct f_gether *eth;
+ struct f_gether_opts *opts;
+
+ opts = container_of(f->fi, struct f_gether_opts, func_inst);
eth = func_to_geth(f);
+ scoped_guard(mutex, &opts->lock)
+ opts->refcnt--;
kfree(eth);
}
static void geth_unbind(struct usb_configuration *c, struct usb_function *f)
{
+ struct f_gether_opts *opts;
+
+ opts = container_of(f->fi, struct f_gether_opts, func_inst);
+
geth_string_defs[0].id = 0;
usb_free_all_descriptors(f);
+
+ opts->bind_count--;
+ if (opts->bind_count == 0 && !opts->bound)
+ gether_detach_gadget(opts->net);
}
static struct usb_function *geth_alloc(struct usb_function_instance *fi)
diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c
index 49cf5aae90ca..4981af8337ab 100644
--- a/drivers/usb/gadget/function/f_uac1_legacy.c
+++ b/drivers/usb/gadget/function/f_uac1_legacy.c
@@ -360,19 +360,46 @@ static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_audio *audio = req->context;
- int status = req->status;
- u32 data = 0;
struct usb_ep *out_ep = audio->out_ep;
- switch (status) {
-
- case 0: /* normal completion? */
- if (ep == out_ep)
+ switch (req->status) {
+ case 0:
+ if (ep == out_ep) {
f_audio_out_ep_complete(ep, req);
- else if (audio->set_con) {
- memcpy(&data, req->buf, req->length);
- audio->set_con->set(audio->set_con, audio->set_cmd,
- le16_to_cpu(data));
+ } else if (audio->set_con) {
+ struct usb_audio_control *con = audio->set_con;
+ u8 type = con->type;
+ u32 data;
+ bool valid_request = false;
+
+ switch (type) {
+ case UAC_FU_MUTE: {
+ u8 value;
+
+ if (req->actual == sizeof(value)) {
+ memcpy(&value, req->buf, sizeof(value));
+ data = value;
+ valid_request = true;
+ }
+ break;
+ }
+ case UAC_FU_VOLUME: {
+ __le16 value;
+
+ if (req->actual == sizeof(value)) {
+ memcpy(&value, req->buf, sizeof(value));
+ data = le16_to_cpu(value);
+ valid_request = true;
+ }
+ break;
+ }
+ }
+
+ if (valid_request)
+ con->set(con, audio->set_cmd, data);
+ else
+ usb_ep_set_halt(ep);
+
audio->set_con = NULL;
}
break;
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index a96476507d2f..842187a09cc0 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -413,6 +413,12 @@ uvc_function_disconnect(struct uvc_device *uvc)
{
int ret;
+ guard(mutex)(&uvc->lock);
+ if (uvc->func_unbound) {
+ dev_dbg(&uvc->vdev.dev, "skipping function deactivate (unbound)\n");
+ return;
+ }
+
if ((ret = usb_function_deactivate(&uvc->func)) < 0)
uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret);
}
@@ -431,6 +437,15 @@ static ssize_t function_name_show(struct device *dev,
static DEVICE_ATTR_RO(function_name);
+static void uvc_vdev_release(struct video_device *vdev)
+{
+ struct uvc_device *uvc = video_get_drvdata(vdev);
+
+ /* Signal uvc_function_unbind() that the video device has been released */
+ if (uvc->vdev_release_done)
+ complete(uvc->vdev_release_done);
+}
+
static int
uvc_register_video(struct uvc_device *uvc)
{
@@ -443,7 +458,7 @@ uvc_register_video(struct uvc_device *uvc)
uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev;
uvc->vdev.fops = &uvc_v4l2_fops;
uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops;
- uvc->vdev.release = video_device_release_empty;
+ uvc->vdev.release = uvc_vdev_release;
uvc->vdev.vfl_dir = VFL_DIR_TX;
uvc->vdev.lock = &uvc->video.mutex;
uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
@@ -659,6 +674,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
int ret = -EINVAL;
uvcg_info(f, "%s()\n", __func__);
+ scoped_guard(mutex, &uvc->lock)
+ uvc->func_unbound = false;
opts = fi_to_f_uvc_opts(f->fi);
/* Sanity check the streaming endpoint module parameters. */
@@ -988,12 +1005,19 @@ static void uvc_free(struct usb_function *f)
static void uvc_function_unbind(struct usb_configuration *c,
struct usb_function *f)
{
+ DECLARE_COMPLETION_ONSTACK(vdev_release_done);
struct usb_composite_dev *cdev = c->cdev;
struct uvc_device *uvc = to_uvc(f);
struct uvc_video *video = &uvc->video;
long wait_ret = 1;
+ bool connected;
uvcg_info(f, "%s()\n", __func__);
+ scoped_guard(mutex, &uvc->lock) {
+ uvc->func_unbound = true;
+ uvc->vdev_release_done = &vdev_release_done;
+ connected = uvc->func_connected;
+ }
kthread_cancel_work_sync(&video->hw_submit);
@@ -1006,7 +1030,7 @@ static void uvc_function_unbind(struct usb_configuration *c,
* though the video device removal uevent. Allow some time for the
* application to close out before things get deleted.
*/
- if (uvc->func_connected) {
+ if (connected) {
uvcg_dbg(f, "waiting for clean disconnect\n");
wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue,
uvc->func_connected == false, msecs_to_jiffies(500));
@@ -1017,7 +1041,10 @@ static void uvc_function_unbind(struct usb_configuration *c,
video_unregister_device(&uvc->vdev);
v4l2_device_unregister(&uvc->v4l2_dev);
- if (uvc->func_connected) {
+ scoped_guard(mutex, &uvc->lock)
+ connected = uvc->func_connected;
+
+ if (connected) {
/*
* Wait for the release to occur to ensure there are no longer any
* pending operations that may cause panics when resources are cleaned
@@ -1029,6 +1056,10 @@ static void uvc_function_unbind(struct usb_configuration *c,
uvcg_dbg(f, "done waiting for release with ret: %ld\n", wait_ret);
}
+ /* Wait for the video device to be released */
+ wait_for_completion(&vdev_release_done);
+ uvc->vdev_release_done = NULL;
+
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
kfree(uvc->control_buf);
@@ -1047,6 +1078,8 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOMEM);
mutex_init(&uvc->video.mutex);
+ mutex_init(&uvc->lock);
+ uvc->func_unbound = true;
uvc->state = UVC_STATE_DISCONNECTED;
init_waitqueue_head(&uvc->func_connected_queue);
opts = fi_to_f_uvc_opts(fi);
diff --git a/drivers/usb/gadget/function/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h
index 77cfb89932be..7f666b9dea02 100644
--- a/drivers/usb/gadget/function/u_ecm.h
+++ b/drivers/usb/gadget/function/u_ecm.h
@@ -15,17 +15,26 @@
#include <linux/usb/composite.h>
+/**
+ * struct f_ecm_opts - ECM function options
+ * @func_inst: USB function instance.
+ * @net: The net_device associated with the ECM function.
+ * @bound: True if the net_device is shared and pre-registered during the
+ * legacy composite driver's bind phase (e.g., multi.c). If false,
+ * the ECM function will register the net_device during its own
+ * bind phase.
+ * @bind_count: Tracks the number of configurations the ECM function is
+ * bound to, preventing double-registration of the @net device.
+ * @lock: Protects the data from concurrent access by configfs read/write
+ * and create symlink/remove symlink operations.
+ * @refcnt: Reference counter for the function instance.
+ */
struct f_ecm_opts {
struct usb_function_instance func_inst;
struct net_device *net;
bool bound;
+ int bind_count;
- /*
- * Read/write access to configfs attributes is handled by configfs.
- *
- * This is to protect the data from concurrent access by read/write
- * and create symlink/remove symlink.
- */
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/u_eem.h b/drivers/usb/gadget/function/u_eem.h
index 3bd85dfcd71c..78ef55815219 100644
--- a/drivers/usb/gadget/function/u_eem.h
+++ b/drivers/usb/gadget/function/u_eem.h
@@ -15,17 +15,26 @@
#include <linux/usb/composite.h>
+/**
+ * struct f_eem_opts - EEM function options
+ * @func_inst: USB function instance.
+ * @net: The net_device associated with the EEM function.
+ * @bound: True if the net_device is shared and pre-registered during the
+ * legacy composite driver's bind phase (e.g., multi.c). If false,
+ * the EEM function will register the net_device during its own
+ * bind phase.
+ * @bind_count: Tracks the number of configurations the EEM function is
+ * bound to, preventing double-registration of the @net device.
+ * @lock: Protects the data from concurrent access by configfs read/write
+ * and create symlink/remove symlink operations.
+ * @refcnt: Reference counter for the function instance.
+ */
struct f_eem_opts {
struct usb_function_instance func_inst;
struct net_device *net;
bool bound;
+ int bind_count;
- /*
- * Read/write access to configfs attributes is handled by configfs.
- *
- * This is to protect the data from concurrent access by read/write
- * and create symlink/remove symlink.
- */
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index dabaa6669251..2b824db4d31b 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -112,8 +112,10 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
strscpy(p->driver, "g_ether", sizeof(p->driver));
strscpy(p->version, UETH__VERSION, sizeof(p->version));
- strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
- strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
+ if (dev->gadget) {
+ strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version));
+ strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info));
+ }
}
/* REVISIT can also support:
@@ -1222,6 +1224,11 @@ void gether_disconnect(struct gether *link)
DBG(dev, "%s\n", __func__);
+ spin_lock(&dev->lock);
+ dev->port_usb = NULL;
+ link->is_suspend = false;
+ spin_unlock(&dev->lock);
+
netif_stop_queue(dev->net);
netif_carrier_off(dev->net);
@@ -1259,11 +1266,6 @@ void gether_disconnect(struct gether *link)
dev->header_len = 0;
dev->unwrap = NULL;
dev->wrap = NULL;
-
- spin_lock(&dev->lock);
- dev->port_usb = NULL;
- link->is_suspend = false;
- spin_unlock(&dev->lock);
}
EXPORT_SYMBOL_GPL(gether_disconnect);
diff --git a/drivers/usb/gadget/function/u_gether.h b/drivers/usb/gadget/function/u_gether.h
index 2f7a373ed449..e7b6b51f69c1 100644
--- a/drivers/usb/gadget/function/u_gether.h
+++ b/drivers/usb/gadget/function/u_gether.h
@@ -15,17 +15,25 @@
#include <linux/usb/composite.h>
+/**
+ * struct f_gether_opts - subset function options
+ * @func_inst: USB function instance.
+ * @net: The net_device associated with the subset function.
+ * @bound: True if the net_device is shared and pre-registered during the
+ * legacy composite driver's bind phase (e.g., multi.c). If false,
+ * the subset function will register the net_device during its own
+ * bind phase.
+ * @bind_count: Tracks the number of configurations the subset function is
+ * bound to, preventing double-registration of the @net device.
+ * @lock: Protects the data from concurrent access by configfs read/write
+ * and create symlink/remove symlink operations.
+ * @refcnt: Reference counter for the function instance.
+ */
struct f_gether_opts {
struct usb_function_instance func_inst;
struct net_device *net;
bool bound;
-
- /*
- * Read/write access to configfs attributes is handled by configfs.
- *
- * This is to protect the data from concurrent access by read/write
- * and create symlink/remove symlink.
- */
+ int bind_count;
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h
index a8c409b2f52f..4e64619714dc 100644
--- a/drivers/usb/gadget/function/u_rndis.h
+++ b/drivers/usb/gadget/function/u_rndis.h
@@ -15,12 +15,34 @@
#include <linux/usb/composite.h>
+/**
+ * struct f_rndis_opts - RNDIS function options
+ * @func_inst: USB function instance.
+ * @vendor_id: Vendor ID.
+ * @manufacturer: Manufacturer string.
+ * @net: The net_device associated with the RNDIS function.
+ * @bind_count: Tracks the number of configurations the RNDIS function is
+ * bound to, preventing double-registration of the @net device.
+ * @borrowed_net: True if the net_device is shared and pre-registered during
+ * the legacy composite driver's bind phase (e.g., multi.c).
+ * If false, the RNDIS function will register the net_device
+ * during its own bind phase.
+ * @rndis_interf_group: ConfigFS group for RNDIS interface.
+ * @rndis_os_desc: USB OS descriptor for RNDIS.
+ * @rndis_ext_compat_id: Extended compatibility ID.
+ * @class: USB class.
+ * @subclass: USB subclass.
+ * @protocol: USB protocol.
+ * @lock: Protects the data from concurrent access by configfs read/write
+ * and create symlink/remove symlink operations.
+ * @refcnt: Reference counter for the function instance.
+ */
struct f_rndis_opts {
struct usb_function_instance func_inst;
u32 vendor_id;
const char *manufacturer;
struct net_device *net;
- bool bound;
+ int bind_count;
bool borrowed_net;
struct config_group *rndis_interf_group;
@@ -30,13 +52,6 @@ struct f_rndis_opts {
u8 class;
u8 subclass;
u8 protocol;
-
- /*
- * Read/write access to configfs attributes is handled by configfs.
- *
- * This is to protect the data from concurrent access by read/write
- * and create symlink/remove symlink.
- */
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 676419a04976..7abfdd5e1eef 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -155,6 +155,9 @@ struct uvc_device {
enum uvc_state state;
struct usb_function func;
struct uvc_video video;
+ struct completion *vdev_release_done;
+ struct mutex lock; /* protects func_unbound and func_connected */
+ bool func_unbound;
bool func_connected;
wait_queue_head_t func_connected_queue;
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index fd4b998ccd16..23bafb07133f 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -574,6 +574,8 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
return -EINVAL;
+ guard(mutex)(&uvc->lock);
+
if (sub->type == UVC_EVENT_SETUP && uvc->func_connected)
return -EBUSY;
@@ -595,7 +597,8 @@ static void uvc_v4l2_disable(struct uvc_device *uvc)
uvc_function_disconnect(uvc);
uvcg_video_disable(&uvc->video);
uvcg_free_buffers(&uvc->video.queue);
- uvc->func_connected = false;
+ scoped_guard(mutex, &uvc->lock)
+ uvc->func_connected = false;
wake_up_interruptible(&uvc->func_connected_queue);
}
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 1cefca660773..da271308d753 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -462,8 +462,13 @@ static void set_link_state(struct dummy_hcd *dum_hcd)
/* Report reset and disconnect events to the driver */
if (dum->ints_enabled && (disconnect || reset)) {
- stop_activity(dum);
++dum->callback_usage;
+ /*
+ * stop_activity() can drop dum->lock, so it must
+ * not come between the dum->ints_enabled test
+ * and the ++dum->callback_usage.
+ */
+ stop_activity(dum);
spin_unlock(&dum->lock);
if (reset)
usb_gadget_udc_reset(&dum->gadget, dum->driver);
@@ -908,21 +913,6 @@ static int dummy_pullup(struct usb_gadget *_gadget, int value)
spin_lock_irqsave(&dum->lock, flags);
dum->pullup = (value != 0);
set_link_state(dum_hcd);
- if (value == 0) {
- /*
- * Emulate synchronize_irq(): wait for callbacks to finish.
- * This seems to be the best place to emulate the call to
- * synchronize_irq() that's in usb_gadget_remove_driver().
- * Doing it in dummy_udc_stop() would be too late since it
- * is called after the unbind callback and unbind shouldn't
- * be invoked until all the other callbacks are finished.
- */
- while (dum->callback_usage > 0) {
- spin_unlock_irqrestore(&dum->lock, flags);
- usleep_range(1000, 2000);
- spin_lock_irqsave(&dum->lock, flags);
- }
- }
spin_unlock_irqrestore(&dum->lock, flags);
usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd));
@@ -945,6 +935,20 @@ static void dummy_udc_async_callbacks(struct usb_gadget *_gadget, bool enable)
spin_lock_irq(&dum->lock);
dum->ints_enabled = enable;
+ if (!enable) {
+ /*
+ * Emulate synchronize_irq(): wait for callbacks to finish.
+ * This has to happen after emulated interrupts are disabled
+ * (dum->ints_enabled is clear) and before the unbind callback,
+ * just like the call to synchronize_irq() in
+ * gadget/udc/core:gadget_unbind_driver().
+ */
+ while (dum->callback_usage > 0) {
+ spin_unlock_irq(&dum->lock);
+ usleep_range(1000, 2000);
+ spin_lock_irq(&dum->lock);
+ }
+ }
spin_unlock_irq(&dum->lock);
}
@@ -1534,6 +1538,12 @@ static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb,
/* rescan to continue with any other queued i/o */
if (rescan)
goto top;
+
+ /* request not fully transferred; stop iterating to
+ * preserve data ordering across queued requests.
+ */
+ if (req->req.actual < req->req.length)
+ break;
}
return sent;
}
diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c
index 888e8f6670d2..5e3156f94cc6 100644
--- a/drivers/usb/host/ehci-brcm.c
+++ b/drivers/usb/host/ehci-brcm.c
@@ -31,8 +31,8 @@ static inline void ehci_brcm_wait_for_sof(struct ehci_hcd *ehci, u32 delay)
int res;
/* Wait for next microframe (every 125 usecs) */
- res = readl_relaxed_poll_timeout(&ehci->regs->frame_index, val,
- val != frame_idx, 1, 130);
+ res = readl_relaxed_poll_timeout_atomic(&ehci->regs->frame_index,
+ val, val != frame_idx, 1, 130);
if (res)
ehci_err(ehci, "Error waiting for SOF\n");
udelay(delay);
diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
index 2bd77255032b..651973606137 100644
--- a/drivers/usb/host/xhci-sideband.c
+++ b/drivers/usb/host/xhci-sideband.c
@@ -93,8 +93,6 @@ __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *e
static void
__xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
{
- struct usb_device *udev;
-
lockdep_assert_held(&sb->mutex);
if (!sb->ir)
@@ -102,10 +100,6 @@ __xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
sb->ir = NULL;
- udev = sb->vdev->udev;
-
- if (udev->state != USB_STATE_NOTATTACHED)
- usb_offload_put(udev);
}
/* sideband api functions */
@@ -291,8 +285,8 @@ EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer);
* Allow other drivers, such as usb controller driver, to check if there are
* any sideband activity on the host controller. This information could be used
* for power management or other forms of resource management. The caller should
- * ensure downstream usb devices are all either suspended or marked as
- * "offload_at_suspend" to ensure the correctness of the return value.
+ * ensure downstream usb devices are all marked as "offload_pm_locked" to ensure
+ * the correctness of the return value.
*
* Returns true on any active sideband existence, false otherwise.
*/
@@ -328,9 +322,6 @@ int
xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
bool ip_autoclear, u32 imod_interval, int intr_num)
{
- int ret = 0;
- struct usb_device *udev;
-
if (!sb || !sb->xhci)
return -ENODEV;
@@ -348,12 +339,9 @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
if (!sb->ir)
return -ENOMEM;
- udev = sb->vdev->udev;
- ret = usb_offload_get(udev);
-
sb->ir->ip_autoclear = ip_autoclear;
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c
index 37644dddf157..64815f8410ac 100644
--- a/drivers/usb/misc/usbio.c
+++ b/drivers/usb/misc/usbio.c
@@ -614,8 +614,10 @@ static int usbio_probe(struct usb_interface *intf, const struct usb_device_id *i
usb_fill_bulk_urb(usbio->urb, udev, usbio->rx_pipe, usbio->rxbuf,
usbio->rxbuf_len, usbio_bulk_recv, usbio);
ret = usb_submit_urb(usbio->urb, GFP_KERNEL);
- if (ret)
- return dev_err_probe(dev, ret, "Submitting usb urb\n");
+ if (ret) {
+ dev_err_probe(dev, ret, "Submitting usb urb\n");
+ goto err_free_urb;
+ }
mutex_lock(&usbio->ctrl_mutex);
@@ -663,6 +665,7 @@ static int usbio_probe(struct usb_interface *intf, const struct usb_device_id *i
err_unlock:
mutex_unlock(&usbio->ctrl_mutex);
usb_kill_urb(usbio->urb);
+err_free_urb:
usb_free_urb(usbio->urb);
return ret;
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 1fffda7647f9..ad73040b30c8 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -73,6 +73,7 @@ static const struct usb_device_id edgeport_4port_id_table[] = {
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BLACKBOX_IC135A) },
{ }
};
@@ -121,6 +122,7 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
+ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BLACKBOX_IC135A) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
@@ -470,6 +472,7 @@ static void get_product_info(struct edgeport_serial *edge_serial)
case ION_DEVICE_ID_EDGEPORT_2_DIN:
case ION_DEVICE_ID_EDGEPORT_4_DIN:
case ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU:
+ case ION_DEVICE_ID_BLACKBOX_IC135A:
product_info->IsRS232 = 1;
break;
diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h
index 9a6f742ad3ab..c82a275e8e76 100644
--- a/drivers/usb/serial/io_usbvend.h
+++ b/drivers/usb/serial/io_usbvend.h
@@ -211,6 +211,7 @@
//
// Definitions for other product IDs
+#define ION_DEVICE_ID_BLACKBOX_IC135A 0x0801 // OEM device (rebranded Edgeport/4)
#define ION_DEVICE_ID_MT4X56USB 0x1403 // OEM device
#define ION_DEVICE_ID_E5805A 0x1A01 // OEM device (rebranded Edgeport/4)
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index d4505a426446..3eaab7645494 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -2441,6 +2441,9 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM815 and SRM825L */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825L */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825L */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM825WN (Diag) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825WN (AT) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825WN (NMEA) */
{ USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */
{ USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */
{ USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */
@@ -2461,6 +2464,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0302, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0802, 0xff), /* Rolling RW350-GL (laptop MBIM) */
.driver_info = RSVD(5) },
+ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff) }, /* Rolling RW135R-GL (laptop MBIM) */
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) },
diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
index 6eadf7835f8f..d09dd09cf1c3 100644
--- a/drivers/usb/typec/altmodes/thunderbolt.c
+++ b/drivers/usb/typec/altmodes/thunderbolt.c
@@ -39,28 +39,7 @@ static bool tbt_ready(struct typec_altmode *alt);
static int tbt_enter_mode(struct tbt_altmode *tbt)
{
- struct typec_altmode *plug = tbt->plug[TYPEC_PLUG_SOP_P];
- u32 vdo;
-
- vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1);
- vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0;
- vdo |= TBT_MODE;
-
- if (plug) {
- if (typec_cable_is_active(tbt->cable))
- vdo |= TBT_ENTER_MODE_ACTIVE_CABLE;
-
- vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_SPEED(plug->vdo));
- vdo |= plug->vdo & TBT_CABLE_ROUNDED;
- vdo |= plug->vdo & TBT_CABLE_OPTICAL;
- vdo |= plug->vdo & TBT_CABLE_RETIMER;
- vdo |= plug->vdo & TBT_CABLE_LINK_TRAINING;
- } else {
- vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_USB3_PASSIVE);
- }
-
- tbt->enter_vdo = vdo;
- return typec_altmode_enter(tbt->alt, &vdo);
+ return typec_altmode_enter(tbt->alt, &tbt->enter_vdo);
}
static void tbt_altmode_work(struct work_struct *work)
@@ -337,6 +316,7 @@ static bool tbt_ready(struct typec_altmode *alt)
{
struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
struct typec_altmode *plug;
+ u32 vdo;
if (tbt->cable)
return true;
@@ -364,6 +344,26 @@ static bool tbt_ready(struct typec_altmode *alt)
tbt->plug[i] = plug;
}
+ vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1);
+ vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0;
+ vdo |= TBT_MODE;
+ plug = tbt->plug[TYPEC_PLUG_SOP_P];
+
+ if (plug) {
+ if (typec_cable_is_active(tbt->cable))
+ vdo |= TBT_ENTER_MODE_ACTIVE_CABLE;
+
+ vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_SPEED(plug->vdo));
+ vdo |= plug->vdo & TBT_CABLE_ROUNDED;
+ vdo |= plug->vdo & TBT_CABLE_OPTICAL;
+ vdo |= plug->vdo & TBT_CABLE_RETIMER;
+ vdo |= plug->vdo & TBT_CABLE_LINK_TRAINING;
+ } else {
+ vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_USB3_PASSIVE);
+ }
+
+ tbt->enter_vdo = vdo;
+
return true;
}
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index a7b388dc7fa0..b77910152399 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -42,8 +42,13 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci)
if (cci & UCSI_CCI_BUSY)
return;
- if (UCSI_CCI_CONNECTOR(cci))
- ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci));
+ if (UCSI_CCI_CONNECTOR(cci)) {
+ if (UCSI_CCI_CONNECTOR(cci) <= ucsi->cap.num_connectors)
+ ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci));
+ else
+ dev_err(ucsi->dev, "bogus connector number in CCI: %lu\n",
+ UCSI_CCI_CONNECTOR(cci));
+ }
if (cci & UCSI_CCI_ACK_COMPLETE &&
test_and_clear_bit(ACK_PENDING, &ucsi->flags))
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index b78998815ce7..16c9b242e917 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3697,7 +3697,8 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg)
}
}
- trans = btrfs_join_transaction(root);
+ /* 2 BTRFS_QGROUP_RELATION_KEY items. */
+ trans = btrfs_start_transaction(root, 2);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
goto out;
@@ -3769,7 +3770,11 @@ static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg)
goto out;
}
- trans = btrfs_join_transaction(root);
+ /*
+ * 1 BTRFS_QGROUP_INFO_KEY item.
+ * 1 BTRFS_QGROUP_LIMIT_KEY item.
+ */
+ trans = btrfs_start_transaction(root, 2);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
goto out;
@@ -3818,7 +3823,8 @@ static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg)
goto drop_write;
}
- trans = btrfs_join_transaction(root);
+ /* 1 BTRFS_QGROUP_LIMIT_KEY item. */
+ trans = btrfs_start_transaction(root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
goto out;
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index 59794d726fd2..1c8f61cbbbe5 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -1260,6 +1260,23 @@ static int check_root_item(struct extent_buffer *leaf, struct btrfs_key *key,
btrfs_root_drop_level(&ri), BTRFS_MAX_LEVEL - 1);
return -EUCLEAN;
}
+ /*
+ * If drop_progress.objectid is non-zero, a btrfs_drop_snapshot() was
+ * interrupted and the resume point was recorded in drop_progress and
+ * drop_level. In that case drop_level must be >= 1: level 0 is the
+ * leaf level and drop_snapshot never saves a checkpoint there (it
+ * only records checkpoints at internal node levels in DROP_REFERENCE
+ * stage). A zero drop_level combined with a non-zero drop_progress
+ * objectid indicates on-disk corruption and would cause a BUG_ON in
+ * merge_reloc_root() and btrfs_drop_snapshot() at mount time.
+ */
+ if (unlikely(btrfs_disk_key_objectid(&ri.drop_progress) != 0 &&
+ btrfs_root_drop_level(&ri) == 0)) {
+ generic_err(leaf, slot,
+ "invalid root drop_level 0 with non-zero drop_progress objectid %llu",
+ btrfs_disk_key_objectid(&ri.drop_progress));
+ return -EUCLEAN;
+ }
/* Flags check */
if (unlikely(btrfs_root_flags(&ri) & ~valid_root_flags)) {
diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
index 392e6ad874cc..ab7cc3021270 100644
--- a/fs/btrfs/zoned.c
+++ b/fs/btrfs/zoned.c
@@ -337,7 +337,10 @@ int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info)
if (!btrfs_fs_incompat(fs_info, ZONED))
return 0;
- mutex_lock(&fs_devices->device_list_mutex);
+ /*
+ * No need to take the device_list mutex here, we're still in the mount
+ * path and devices cannot be added to or removed from the list yet.
+ */
list_for_each_entry(device, &fs_devices->devices, dev_list) {
/* We can skip reading of zone info for missing devices */
if (!device->bdev)
@@ -347,7 +350,6 @@ int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info)
if (ret)
break;
}
- mutex_unlock(&fs_devices->device_list_mutex);
return ret;
}
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 0c3d2bbef938..474d7b2aa2ef 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -2324,4 +2324,10 @@ static inline int cifs_open_create_options(unsigned int oflags, int opts)
return opts;
}
+/*
+ * The number of blocks is not related to (i_size / i_blksize), but instead
+ * 512 byte (2**9) size is required for calculating num blocks.
+ */
+#define CIFS_INO_BLOCKS(size) DIV_ROUND_UP_ULL((u64)(size), 512)
+
#endif /* _CIFS_GLOB_H */
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index c27a38843aa6..9d703a247450 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -994,7 +994,6 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
if (!rc) {
netfs_resize_file(&cinode->netfs, 0, true);
cifs_setsize(inode, 0);
- inode->i_blocks = 0;
}
}
if (cfile)
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index f9ee95953fa4..c5d89ddc87c0 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -219,13 +219,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr,
*/
if (is_size_safe_to_change(cifs_i, fattr->cf_eof, from_readdir)) {
i_size_write(inode, fattr->cf_eof);
-
- /*
- * i_blocks is not related to (i_size / i_blksize),
- * but instead 512 byte (2**9) size is required for
- * calculating num blocks.
- */
- inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9;
+ inode->i_blocks = CIFS_INO_BLOCKS(fattr->cf_bytes);
}
if (S_ISLNK(fattr->cf_mode) && fattr->cf_symlink_target) {
@@ -3009,6 +3003,11 @@ void cifs_setsize(struct inode *inode, loff_t offset)
{
spin_lock(&inode->i_lock);
i_size_write(inode, offset);
+ /*
+ * Until we can query the server for actual allocation size,
+ * this is best estimate we have for blocks allocated for a file.
+ */
+ inode->i_blocks = CIFS_INO_BLOCKS(offset);
spin_unlock(&inode->i_lock);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
truncate_pagecache(inode, offset);
@@ -3081,14 +3080,6 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
if (rc == 0) {
netfs_resize_file(&cifsInode->netfs, size, true);
cifs_setsize(inode, size);
- /*
- * i_blocks is not related to (i_size / i_blksize), but instead
- * 512 byte (2**9) size is required for calculating num blocks.
- * Until we can query the server for actual allocation size,
- * this is best estimate we have for blocks allocated for a file
- * Number of blocks must be rounded up so size 1 is not 0 blocks
- */
- inode->i_blocks = (512 - 1 + size) >> 9;
}
return rc;
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 9bfd3711030b..067e31328329 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1493,6 +1493,7 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
{
struct smb2_file_network_open_info file_inf;
struct inode *inode;
+ u64 asize;
int rc;
rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid,
@@ -1516,14 +1517,9 @@ smb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon,
inode_set_atime_to_ts(inode,
cifs_NTtimeToUnix(file_inf.LastAccessTime));
- /*
- * i_blocks is not related to (i_size / i_blksize),
- * but instead 512 byte (2**9) size is required for
- * calculating num blocks.
- */
- if (le64_to_cpu(file_inf.AllocationSize) > 4096)
- inode->i_blocks =
- (512 - 1 + le64_to_cpu(file_inf.AllocationSize)) >> 9;
+ asize = le64_to_cpu(file_inf.AllocationSize);
+ if (asize > 4096)
+ inode->i_blocks = CIFS_INO_BLOCKS(asize);
/* End of file and Attributes should not have to be updated on close */
spin_unlock(&inode->i_lock);
@@ -2197,14 +2193,6 @@ smb2_duplicate_extents(const unsigned int xid,
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
if (rc)
goto duplicate_extents_out;
-
- /*
- * Although also could set plausible allocation size (i_blocks)
- * here in addition to setting the file size, in reflink
- * it is likely that the target file is sparse. Its allocation
- * size will be queried on next revalidate, but it is important
- * to make sure that file's cached size is updated immediately
- */
netfs_resize_file(netfs_inode(inode), dest_off + len, true);
cifs_setsize(inode, dest_off + len);
}
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 6bc857c00f36..276dcb9b83f4 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -3401,20 +3401,24 @@ int smb2_open(struct ksmbd_work *work)
KSMBD_SHARE_FLAG_ACL_XATTR)) {
struct smb_fattr fattr;
struct smb_ntsd *pntsd;
- int pntsd_size, ace_num = 0;
+ int pntsd_size;
+ size_t scratch_len;
ksmbd_acls_fattr(&fattr, idmap, inode);
- if (fattr.cf_acls)
- ace_num = fattr.cf_acls->a_count;
- if (fattr.cf_dacls)
- ace_num += fattr.cf_dacls->a_count;
-
- pntsd = kmalloc(sizeof(struct smb_ntsd) +
- sizeof(struct smb_sid) * 3 +
- sizeof(struct smb_acl) +
- sizeof(struct smb_ace) * ace_num * 2,
- KSMBD_DEFAULT_GFP);
+ scratch_len = smb_acl_sec_desc_scratch_len(&fattr,
+ NULL, 0,
+ OWNER_SECINFO | GROUP_SECINFO |
+ DACL_SECINFO);
+ if (!scratch_len || scratch_len == SIZE_MAX) {
+ rc = -EFBIG;
+ posix_acl_release(fattr.cf_acls);
+ posix_acl_release(fattr.cf_dacls);
+ goto err_out;
+ }
+
+ pntsd = kvzalloc(scratch_len, KSMBD_DEFAULT_GFP);
if (!pntsd) {
+ rc = -ENOMEM;
posix_acl_release(fattr.cf_acls);
posix_acl_release(fattr.cf_dacls);
goto err_out;
@@ -3429,7 +3433,7 @@ int smb2_open(struct ksmbd_work *work)
posix_acl_release(fattr.cf_acls);
posix_acl_release(fattr.cf_dacls);
if (rc) {
- kfree(pntsd);
+ kvfree(pntsd);
goto err_out;
}
@@ -3439,7 +3443,7 @@ int smb2_open(struct ksmbd_work *work)
pntsd,
pntsd_size,
false);
- kfree(pntsd);
+ kvfree(pntsd);
if (rc)
pr_err("failed to store ntacl in xattr : %d\n",
rc);
@@ -5371,8 +5375,9 @@ static int smb2_get_info_file(struct ksmbd_work *work,
if (test_share_config_flag(work->tcon->share_conf,
KSMBD_SHARE_FLAG_PIPE)) {
/* smb2 info file called for pipe */
- return smb2_get_info_file_pipe(work->sess, req, rsp,
+ rc = smb2_get_info_file_pipe(work->sess, req, rsp,
work->response_buf);
+ goto iov_pin_out;
}
if (work->next_smb2_rcv_hdr_off) {
@@ -5472,6 +5477,12 @@ static int smb2_get_info_file(struct ksmbd_work *work,
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
rsp, work->response_buf);
ksmbd_fd_put(work, fp);
+
+iov_pin_out:
+ if (!rc)
+ rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
+ offsetof(struct smb2_query_info_rsp, Buffer) +
+ le32_to_cpu(rsp->OutputBufferLength));
return rc;
}
@@ -5698,6 +5709,11 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
rsp, work->response_buf);
path_put(&path);
+
+ if (!rc)
+ rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
+ offsetof(struct smb2_query_info_rsp, Buffer) +
+ le32_to_cpu(rsp->OutputBufferLength));
return rc;
}
@@ -5707,13 +5723,14 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
{
struct ksmbd_file *fp;
struct mnt_idmap *idmap;
- struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL;
+ struct smb_ntsd *pntsd = NULL, *ppntsd = NULL;
struct smb_fattr fattr = {{0}};
struct inode *inode;
__u32 secdesclen = 0;
unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
int addition_info = le32_to_cpu(req->AdditionalInformation);
- int rc = 0, ppntsd_size = 0;
+ int rc = 0, ppntsd_size = 0, max_len;
+ size_t scratch_len = 0;
if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO |
PROTECTED_DACL_SECINFO |
@@ -5721,6 +5738,11 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n",
addition_info);
+ pntsd = kzalloc(ALIGN(sizeof(struct smb_ntsd), 8),
+ KSMBD_DEFAULT_GFP);
+ if (!pntsd)
+ return -ENOMEM;
+
pntsd->revision = cpu_to_le16(1);
pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED);
pntsd->osidoffset = 0;
@@ -5729,9 +5751,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
pntsd->dacloffset = 0;
secdesclen = sizeof(struct smb_ntsd);
- rsp->OutputBufferLength = cpu_to_le32(secdesclen);
-
- return 0;
+ goto iov_pin;
}
if (work->next_smb2_rcv_hdr_off) {
@@ -5763,18 +5783,58 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
&ppntsd);
/* Check if sd buffer size exceeds response buffer size */
- if (smb2_resp_buf_len(work, 8) > ppntsd_size)
- rc = build_sec_desc(idmap, pntsd, ppntsd, ppntsd_size,
- addition_info, &secdesclen, &fattr);
+ max_len = smb2_calc_max_out_buf_len(work,
+ offsetof(struct smb2_query_info_rsp, Buffer),
+ le32_to_cpu(req->OutputBufferLength));
+ if (max_len < 0) {
+ rc = -EINVAL;
+ goto release_acl;
+ }
+
+ scratch_len = smb_acl_sec_desc_scratch_len(&fattr, ppntsd,
+ ppntsd_size, addition_info);
+ if (!scratch_len || scratch_len == SIZE_MAX) {
+ rc = -EFBIG;
+ goto release_acl;
+ }
+
+ pntsd = kvzalloc(scratch_len, KSMBD_DEFAULT_GFP);
+ if (!pntsd) {
+ rc = -ENOMEM;
+ goto release_acl;
+ }
+
+ rc = build_sec_desc(idmap, pntsd, ppntsd, ppntsd_size,
+ addition_info, &secdesclen, &fattr);
+
+release_acl:
posix_acl_release(fattr.cf_acls);
posix_acl_release(fattr.cf_dacls);
kfree(ppntsd);
ksmbd_fd_put(work, fp);
+
+ if (!rc && ALIGN(secdesclen, 8) > scratch_len)
+ rc = -EFBIG;
if (rc)
- return rc;
+ goto err_out;
+iov_pin:
rsp->OutputBufferLength = cpu_to_le32(secdesclen);
- return 0;
+ rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
+ rsp, work->response_buf);
+ if (rc)
+ goto err_out;
+
+ rc = ksmbd_iov_pin_rsp_read(work, (void *)rsp,
+ offsetof(struct smb2_query_info_rsp, Buffer),
+ pntsd, secdesclen);
+err_out:
+ if (rc) {
+ rsp->OutputBufferLength = 0;
+ kvfree(pntsd);
+ }
+
+ return rc;
}
/**
@@ -5798,6 +5858,9 @@ int smb2_query_info(struct ksmbd_work *work)
goto err_out;
}
+ rsp->StructureSize = cpu_to_le16(9);
+ rsp->OutputBufferOffset = cpu_to_le16(72);
+
switch (req->InfoType) {
case SMB2_O_INFO_FILE:
ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n");
@@ -5818,14 +5881,6 @@ int smb2_query_info(struct ksmbd_work *work)
}
ksmbd_revert_fsids(work);
- if (!rc) {
- rsp->StructureSize = cpu_to_le16(9);
- rsp->OutputBufferOffset = cpu_to_le16(72);
- rc = ksmbd_iov_pin_rsp(work, (void *)rsp,
- offsetof(struct smb2_query_info_rsp, Buffer) +
- le32_to_cpu(rsp->OutputBufferLength));
- }
-
err_out:
if (rc < 0) {
if (rc == -EACCES)
@@ -5836,6 +5891,8 @@ int smb2_query_info(struct ksmbd_work *work)
rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR;
else if (rc == -ENOMEM)
rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
+ else if (rc == -EINVAL && rsp->hdr.Status == 0)
+ rsp->hdr.Status = STATUS_INVALID_PARAMETER;
else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0)
rsp->hdr.Status = STATUS_INVALID_INFO_CLASS;
smb2_set_err_rsp(work);
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index 05598d994a68..441c4ed02aba 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -915,6 +915,49 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
return 0;
}
+size_t smb_acl_sec_desc_scratch_len(struct smb_fattr *fattr,
+ struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info)
+{
+ size_t len = sizeof(struct smb_ntsd);
+ size_t tmp;
+
+ if (addition_info & OWNER_SECINFO)
+ len += sizeof(struct smb_sid);
+ if (addition_info & GROUP_SECINFO)
+ len += sizeof(struct smb_sid);
+ if (!(addition_info & DACL_SECINFO))
+ return len;
+
+ len += sizeof(struct smb_acl);
+ if (ppntsd && ppntsd_size > 0) {
+ unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset);
+
+ if (dacl_offset < ppntsd_size &&
+ check_add_overflow(len, ppntsd_size - dacl_offset, &len))
+ return 0;
+ }
+
+ if (fattr->cf_acls) {
+ if (check_mul_overflow((size_t)fattr->cf_acls->a_count,
+ 2 * sizeof(struct smb_ace), &tmp) ||
+ check_add_overflow(len, tmp, &len))
+ return 0;
+ } else {
+ /* default/minimum DACL */
+ if (check_add_overflow(len, 5 * sizeof(struct smb_ace), &len))
+ return 0;
+ }
+
+ if (fattr->cf_dacls) {
+ if (check_mul_overflow((size_t)fattr->cf_dacls->a_count,
+ sizeof(struct smb_ace), &tmp) ||
+ check_add_overflow(len, tmp, &len))
+ return 0;
+ }
+
+ return len;
+}
+
/* Convert permission bits from mode to equivalent CIFS ACL */
int build_sec_desc(struct mnt_idmap *idmap,
struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd,
diff --git a/fs/smb/server/smbacl.h b/fs/smb/server/smbacl.h
index 355adaee39b8..ab21ba2cd4df 100644
--- a/fs/smb/server/smbacl.h
+++ b/fs/smb/server/smbacl.h
@@ -101,6 +101,8 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
bool type_check, bool get_write);
void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid);
void ksmbd_init_domain(u32 *sub_auth);
+size_t smb_acl_sec_desc_scratch_len(struct smb_fattr *fattr,
+ struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info);
static inline uid_t posix_acl_uid_translate(struct mnt_idmap *idmap,
struct posix_acl_entry *pace)
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index b858e3c2ad50..7a9fe22c2b69 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -1316,6 +1316,28 @@ xfs_attr3_leaf_create(
return 0;
}
+/*
+ * Reinitialize an existing attr fork block as an empty leaf, and attach
+ * the buffer to tp.
+ */
+int
+xfs_attr3_leaf_init(
+ struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ xfs_dablk_t blkno)
+{
+ struct xfs_buf *bp = NULL;
+ struct xfs_da_args args = {
+ .trans = tp,
+ .dp = dp,
+ .owner = dp->i_ino,
+ .geo = dp->i_mount->m_attr_geo,
+ };
+
+ ASSERT(tp != NULL);
+
+ return xfs_attr3_leaf_create(&args, blkno, &bp);
+}
/*
* Split the leaf node, rebalance, then add the new entry.
*
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.h b/fs/xfs/libxfs/xfs_attr_leaf.h
index 589f810eedc0..deb62b544ac5 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.h
+++ b/fs/xfs/libxfs/xfs_attr_leaf.h
@@ -86,6 +86,9 @@ int xfs_attr3_leaf_list_int(struct xfs_buf *bp,
/*
* Routines used for shrinking the Btree.
*/
+
+int xfs_attr3_leaf_init(struct xfs_trans *tp, struct xfs_inode *dp,
+ xfs_dablk_t blkno);
int xfs_attr3_leaf_toosmall(struct xfs_da_state *state, int *retval);
void xfs_attr3_leaf_unbalance(struct xfs_da_state *state,
struct xfs_da_state_blk *drop_blk,
diff --git a/fs/xfs/libxfs/xfs_da_btree.c b/fs/xfs/libxfs/xfs_da_btree.c
index 90f7fc219fcc..d85bca22d685 100644
--- a/fs/xfs/libxfs/xfs_da_btree.c
+++ b/fs/xfs/libxfs/xfs_da_btree.c
@@ -1506,21 +1506,20 @@ xfs_da3_fixhashpath(
}
/*
- * Remove an entry from an intermediate node.
+ * Internal implementation to remove an entry from an intermediate node.
*/
STATIC void
-xfs_da3_node_remove(
- struct xfs_da_state *state,
- struct xfs_da_state_blk *drop_blk)
+__xfs_da3_node_remove(
+ struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ struct xfs_da_geometry *geo,
+ struct xfs_da_state_blk *drop_blk)
{
struct xfs_da_intnode *node;
struct xfs_da3_icnode_hdr nodehdr;
struct xfs_da_node_entry *btree;
int index;
int tmp;
- struct xfs_inode *dp = state->args->dp;
-
- trace_xfs_da_node_remove(state->args);
node = drop_blk->bp->b_addr;
xfs_da3_node_hdr_from_disk(dp->i_mount, &nodehdr, node);
@@ -1536,17 +1535,17 @@ xfs_da3_node_remove(
tmp = nodehdr.count - index - 1;
tmp *= (uint)sizeof(xfs_da_node_entry_t);
memmove(&btree[index], &btree[index + 1], tmp);
- xfs_trans_log_buf(state->args->trans, drop_blk->bp,
+ xfs_trans_log_buf(tp, drop_blk->bp,
XFS_DA_LOGRANGE(node, &btree[index], tmp));
index = nodehdr.count - 1;
}
memset(&btree[index], 0, sizeof(xfs_da_node_entry_t));
- xfs_trans_log_buf(state->args->trans, drop_blk->bp,
+ xfs_trans_log_buf(tp, drop_blk->bp,
XFS_DA_LOGRANGE(node, &btree[index], sizeof(btree[index])));
nodehdr.count -= 1;
xfs_da3_node_hdr_to_disk(dp->i_mount, node, &nodehdr);
- xfs_trans_log_buf(state->args->trans, drop_blk->bp,
- XFS_DA_LOGRANGE(node, &node->hdr, state->args->geo->node_hdr_size));
+ xfs_trans_log_buf(tp, drop_blk->bp,
+ XFS_DA_LOGRANGE(node, &node->hdr, geo->node_hdr_size));
/*
* Copy the last hash value from the block to propagate upwards.
@@ -1554,6 +1553,38 @@ xfs_da3_node_remove(
drop_blk->hashval = be32_to_cpu(btree[index - 1].hashval);
}
+/*
+ * Remove an entry from an intermediate node.
+ */
+STATIC void
+xfs_da3_node_remove(
+ struct xfs_da_state *state,
+ struct xfs_da_state_blk *drop_blk)
+{
+ trace_xfs_da_node_remove(state->args);
+
+ __xfs_da3_node_remove(state->args->trans, state->args->dp,
+ state->args->geo, drop_blk);
+}
+
+/*
+ * Remove an entry from an intermediate attr node at the specified index.
+ */
+void
+xfs_attr3_node_entry_remove(
+ struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ struct xfs_buf *bp,
+ int index)
+{
+ struct xfs_da_state_blk blk = {
+ .index = index,
+ .bp = bp,
+ };
+
+ __xfs_da3_node_remove(tp, dp, dp->i_mount->m_attr_geo, &blk);
+}
+
/*
* Unbalance the elements between two intermediate nodes,
* move all Btree elements from one node into another.
diff --git a/fs/xfs/libxfs/xfs_da_btree.h b/fs/xfs/libxfs/xfs_da_btree.h
index 354d5d65043e..afcf2d3c7a21 100644
--- a/fs/xfs/libxfs/xfs_da_btree.h
+++ b/fs/xfs/libxfs/xfs_da_btree.h
@@ -184,6 +184,8 @@ int xfs_da3_split(xfs_da_state_t *state);
int xfs_da3_join(xfs_da_state_t *state);
void xfs_da3_fixhashpath(struct xfs_da_state *state,
struct xfs_da_state_path *path_to_to_fix);
+void xfs_attr3_node_entry_remove(struct xfs_trans *tp, struct xfs_inode *dp,
+ struct xfs_buf *bp, int index);
/*
* Routines used for finding things in the Btree.
diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c
index 319004bf089f..2305ddad8a13 100644
--- a/fs/xfs/xfs_attr_inactive.c
+++ b/fs/xfs/xfs_attr_inactive.c
@@ -140,7 +140,7 @@ xfs_attr3_node_inactive(
xfs_daddr_t parent_blkno, child_blkno;
struct xfs_buf *child_bp;
struct xfs_da3_icnode_hdr ichdr;
- int error, i;
+ int error;
/*
* Since this code is recursive (gasp!) we must protect ourselves.
@@ -152,7 +152,7 @@ xfs_attr3_node_inactive(
return -EFSCORRUPTED;
}
- xfs_da3_node_hdr_from_disk(dp->i_mount, &ichdr, bp->b_addr);
+ xfs_da3_node_hdr_from_disk(mp, &ichdr, bp->b_addr);
parent_blkno = xfs_buf_daddr(bp);
if (!ichdr.count) {
xfs_trans_brelse(*trans, bp);
@@ -167,7 +167,7 @@ xfs_attr3_node_inactive(
* over the leaves removing all of them. If this is higher up
* in the tree, recurse downward.
*/
- for (i = 0; i < ichdr.count; i++) {
+ while (ichdr.count > 0) {
/*
* Read the subsidiary block to see what we have to work with.
* Don't do this in a transaction. This is a depth-first
@@ -218,29 +218,32 @@ xfs_attr3_node_inactive(
xfs_trans_binval(*trans, child_bp);
child_bp = NULL;
+ error = xfs_da3_node_read_mapped(*trans, dp,
+ parent_blkno, &bp, XFS_ATTR_FORK);
+ if (error)
+ return error;
+
/*
- * If we're not done, re-read the parent to get the next
- * child block number.
+ * Remove entry from parent node, prevents being indexed to.
*/
- if (i + 1 < ichdr.count) {
- struct xfs_da3_icnode_hdr phdr;
+ xfs_attr3_node_entry_remove(*trans, dp, bp, 0);
+
+ xfs_da3_node_hdr_from_disk(mp, &ichdr, bp->b_addr);
+ bp = NULL;
- error = xfs_da3_node_read_mapped(*trans, dp,
- parent_blkno, &bp, XFS_ATTR_FORK);
+ if (ichdr.count > 0) {
+ /*
+ * If we're not done, get the next child block number.
+ */
+ child_fsb = be32_to_cpu(ichdr.btree[0].before);
+
+ /*
+ * Atomically commit the whole invalidate stuff.
+ */
+ error = xfs_trans_roll_inode(trans, dp);
if (error)
return error;
- xfs_da3_node_hdr_from_disk(dp->i_mount, &phdr,
- bp->b_addr);
- child_fsb = be32_to_cpu(phdr.btree[i + 1].before);
- xfs_trans_brelse(*trans, bp);
- bp = NULL;
}
- /*
- * Atomically commit the whole invalidate stuff.
- */
- error = xfs_trans_roll_inode(trans, dp);
- if (error)
- return error;
}
return 0;
@@ -257,10 +260,8 @@ xfs_attr3_root_inactive(
struct xfs_trans **trans,
struct xfs_inode *dp)
{
- struct xfs_mount *mp = dp->i_mount;
struct xfs_da_blkinfo *info;
struct xfs_buf *bp;
- xfs_daddr_t blkno;
int error;
/*
@@ -272,7 +273,6 @@ xfs_attr3_root_inactive(
error = xfs_da3_node_read(*trans, dp, 0, &bp, XFS_ATTR_FORK);
if (error)
return error;
- blkno = xfs_buf_daddr(bp);
/*
* Invalidate the tree, even if the "tree" is only a single leaf block.
@@ -283,10 +283,26 @@ xfs_attr3_root_inactive(
case cpu_to_be16(XFS_DA_NODE_MAGIC):
case cpu_to_be16(XFS_DA3_NODE_MAGIC):
error = xfs_attr3_node_inactive(trans, dp, bp, 1);
+ /*
+ * Empty root node block are not allowed, convert it to leaf.
+ */
+ if (!error)
+ error = xfs_attr3_leaf_init(*trans, dp, 0);
+ if (!error)
+ error = xfs_trans_roll_inode(trans, dp);
break;
case cpu_to_be16(XFS_ATTR_LEAF_MAGIC):
case cpu_to_be16(XFS_ATTR3_LEAF_MAGIC):
error = xfs_attr3_leaf_inactive(trans, dp, bp);
+ /*
+ * Reinit the leaf before truncating extents so that a crash
+ * mid-truncation leaves an empty leaf rather than one with
+ * entries that may reference freed remote value blocks.
+ */
+ if (!error)
+ error = xfs_attr3_leaf_init(*trans, dp, 0);
+ if (!error)
+ error = xfs_trans_roll_inode(trans, dp);
break;
default:
xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK);
@@ -295,21 +311,6 @@ xfs_attr3_root_inactive(
xfs_trans_brelse(*trans, bp);
break;
}
- if (error)
- return error;
-
- /*
- * Invalidate the incore copy of the root block.
- */
- error = xfs_trans_get_buf(*trans, mp->m_ddev_targp, blkno,
- XFS_FSB_TO_BB(mp, mp->m_attr_geo->fsbcount), 0, &bp);
- if (error)
- return error;
- xfs_trans_binval(*trans, bp); /* remove from cache */
- /*
- * Commit the invalidate and start the next transaction.
- */
- error = xfs_trans_roll_inode(trans, dp);
return error;
}
@@ -328,6 +329,7 @@ xfs_attr_inactive(
{
struct xfs_trans *trans;
struct xfs_mount *mp;
+ struct xfs_buf *bp;
int lock_mode = XFS_ILOCK_SHARED;
int error = 0;
@@ -363,10 +365,27 @@ xfs_attr_inactive(
* removal below.
*/
if (dp->i_af.if_nextents > 0) {
+ /*
+ * Invalidate and truncate all blocks but leave the root block.
+ */
error = xfs_attr3_root_inactive(&trans, dp);
if (error)
goto out_cancel;
+ error = xfs_itruncate_extents(&trans, dp, XFS_ATTR_FORK,
+ XFS_FSB_TO_B(mp, mp->m_attr_geo->fsbcount));
+ if (error)
+ goto out_cancel;
+
+ /*
+ * Invalidate and truncate the root block and ensure that the
+ * operation is completed within a single transaction.
+ */
+ error = xfs_da_get_buf(trans, dp, 0, &bp, XFS_ATTR_FORK);
+ if (error)
+ goto out_cancel;
+
+ xfs_trans_binval(trans, bp);
error = xfs_itruncate_extents(&trans, dp, XFS_ATTR_FORK, 0);
if (error)
goto out_cancel;
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index f1f88e48fe22..c98af0ffc315 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1048,7 +1048,8 @@ xfs_itruncate_extents_flags(
xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
if (icount_read(VFS_I(ip)))
xfs_assert_ilocked(ip, XFS_IOLOCK_EXCL);
- ASSERT(new_size <= XFS_ISIZE(ip));
+ if (whichfork == XFS_DATA_FORK)
+ ASSERT(new_size <= XFS_ISIZE(ip));
ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
ASSERT(ip->i_itemp != NULL);
ASSERT(ip->i_itemp->ili_lock_flags == 0);
diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h
index 107b797c33ec..0cc8fa749f68 100644
--- a/include/crypto/if_alg.h
+++ b/include/crypto/if_alg.h
@@ -230,9 +230,8 @@ static inline bool af_alg_readable(struct sock *sk)
return PAGE_SIZE <= af_alg_rcvbuf(sk);
}
-unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes, size_t offset);
-void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst,
- size_t dst_offset);
+unsigned int af_alg_count_tsgl(struct sock *sk, size_t bytes);
+void af_alg_pull_tsgl(struct sock *sk, size_t used, struct scatterlist *dst);
void af_alg_wmem_wakeup(struct sock *sk);
int af_alg_wait_for_data(struct sock *sk, unsigned flags, unsigned min);
int af_alg_sendmsg(struct socket *sock, struct msghdr *msg, size_t size,
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index f7cc60de0058..2bff3e2be0d3 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -609,6 +609,9 @@ struct cgroup {
/* used to wait for offlining of csses */
wait_queue_head_t offline_waitq;
+ /* used by cgroup_rmdir() to wait for dying tasks to leave */
+ wait_queue_head_t dying_populated_waitq;
+
/* used to schedule release agent */
struct work_struct release_agent_work;
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 872ebdf0dd77..82ab8d9a8bf5 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -816,6 +816,18 @@ static inline void *iio_device_get_drvdata(const struct iio_dev *indio_dev)
#define IIO_DECLARE_DMA_BUFFER_WITH_TS(type, name, count) \
__IIO_DECLARE_BUFFER_WITH_TS(type, name, count) __aligned(IIO_DMA_MINALIGN)
+/**
+ * IIO_DECLARE_QUATERNION() - Declare a quaternion element
+ * @type: element type of the individual vectors
+ * @name: identifier name
+ *
+ * Quaternions are a vector composed of 4 elements (W, X, Y, Z). Use this macro
+ * to declare a quaternion element in a struct to ensure proper alignment in
+ * an IIO buffer.
+ */
+#define IIO_DECLARE_QUATERNION(type, name) \
+ type name[4] __aligned(sizeof(type) * 4)
+
struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv);
/* The information at the returned address is guaranteed to be cacheline aligned */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 1216f050f069..846afec74703 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1833,6 +1833,8 @@ enum netdev_reg_state {
*
* @mpls_features: Mask of features inheritable by MPLS
* @gso_partial_features: value(s) from NETIF_F_GSO\*
+ * @mangleid_features: Mask of features requiring MANGLEID, will be
+ * disabled together with the latter.
*
* @ifindex: interface index
* @group: The group the device belongs to
@@ -2222,6 +2224,7 @@ struct net_device {
netdev_features_t vlan_features;
netdev_features_t hw_enc_features;
netdev_features_t mpls_features;
+ netdev_features_t mangleid_features;
unsigned int min_mtu;
unsigned int max_mtu;
diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
index e9f4f845d760..b98331572ad2 100644
--- a/include/linux/netfilter/ipset/ip_set.h
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -309,7 +309,7 @@ enum {
/* register and unregister set references */
extern ip_set_id_t ip_set_get_byname(struct net *net,
- const char *name, struct ip_set **set);
+ const struct nlattr *name, struct ip_set **set);
extern void ip_set_put_byindex(struct net *net, ip_set_id_t index);
extern void ip_set_name_byindex(struct net *net, ip_set_id_t index, char *name);
extern ip_set_id_t ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index);
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 112e48970338..13c6eca3bbc6 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -5093,6 +5093,7 @@ static inline bool skb_has_extensions(struct sk_buff *skb)
return unlikely(skb->active_extensions);
}
#else
+static inline void __skb_ext_put(struct skb_ext *ext) {}
static inline void skb_ext_put(struct sk_buff *skb) {}
static inline void skb_ext_reset(struct sk_buff *skb) {}
static inline void skb_ext_del(struct sk_buff *skb, int unused) {}
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 32e17626dfdc..2511f1e5b114 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -21,6 +21,7 @@
#include <linux/completion.h> /* for struct completion */
#include <linux/sched.h> /* for current && schedule_timeout */
#include <linux/mutex.h> /* for struct mutex */
+#include <linux/spinlock.h> /* for spinlock_t */
#include <linux/pm_runtime.h> /* for runtime PM */
struct usb_device;
@@ -636,8 +637,9 @@ struct usb3_lpm_parameters {
* @do_remote_wakeup: remote wakeup should be enabled
* @reset_resume: needs reset instead of resume
* @port_is_suspended: the upstream port is suspended (L2 or U3)
- * @offload_at_suspend: offload activities during suspend is enabled.
+ * @offload_pm_locked: prevents offload_usage changes during PM transitions.
* @offload_usage: number of offload activities happening on this usb device.
+ * @offload_lock: protects offload_usage and offload_pm_locked
* @slot_id: Slot ID assigned by xHCI
* @l1_params: best effor service latency for USB2 L1 LPM state, and L1 timeout.
* @u1_params: exit latencies for USB3 U1 LPM state, and hub-initiated timeout.
@@ -726,8 +728,9 @@ struct usb_device {
unsigned do_remote_wakeup:1;
unsigned reset_resume:1;
unsigned port_is_suspended:1;
- unsigned offload_at_suspend:1;
+ unsigned offload_pm_locked:1;
int offload_usage;
+ spinlock_t offload_lock;
enum usb_link_tunnel_mode tunnel_mode;
struct device_link *usb4_link;
@@ -849,6 +852,7 @@ static inline void usb_mark_last_busy(struct usb_device *udev)
int usb_offload_get(struct usb_device *udev);
int usb_offload_put(struct usb_device *udev);
bool usb_offload_check(struct usb_device *udev);
+void usb_offload_set_pm_locked(struct usb_device *udev, bool locked);
#else
static inline int usb_offload_get(struct usb_device *udev)
@@ -857,6 +861,8 @@ static inline int usb_offload_put(struct usb_device *udev)
{ return 0; }
static inline bool usb_offload_check(struct usb_device *udev)
{ return false; }
+static inline void usb_offload_set_pm_locked(struct usb_device *udev, bool locked)
+{ }
#endif
extern int usb_disable_lpm(struct usb_device *udev);
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index d46ed9011ee5..89a60919050b 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -130,21 +130,30 @@ struct bt_voice {
#define BT_RCVMTU 13
#define BT_PHY 14
-#define BT_PHY_BR_1M_1SLOT 0x00000001
-#define BT_PHY_BR_1M_3SLOT 0x00000002
-#define BT_PHY_BR_1M_5SLOT 0x00000004
-#define BT_PHY_EDR_2M_1SLOT 0x00000008
-#define BT_PHY_EDR_2M_3SLOT 0x00000010
-#define BT_PHY_EDR_2M_5SLOT 0x00000020
-#define BT_PHY_EDR_3M_1SLOT 0x00000040
-#define BT_PHY_EDR_3M_3SLOT 0x00000080
-#define BT_PHY_EDR_3M_5SLOT 0x00000100
-#define BT_PHY_LE_1M_TX 0x00000200
-#define BT_PHY_LE_1M_RX 0x00000400
-#define BT_PHY_LE_2M_TX 0x00000800
-#define BT_PHY_LE_2M_RX 0x00001000
-#define BT_PHY_LE_CODED_TX 0x00002000
-#define BT_PHY_LE_CODED_RX 0x00004000
+#define BT_PHY_BR_1M_1SLOT BIT(0)
+#define BT_PHY_BR_1M_3SLOT BIT(1)
+#define BT_PHY_BR_1M_5SLOT BIT(2)
+#define BT_PHY_EDR_2M_1SLOT BIT(3)
+#define BT_PHY_EDR_2M_3SLOT BIT(4)
+#define BT_PHY_EDR_2M_5SLOT BIT(5)
+#define BT_PHY_EDR_3M_1SLOT BIT(6)
+#define BT_PHY_EDR_3M_3SLOT BIT(7)
+#define BT_PHY_EDR_3M_5SLOT BIT(8)
+#define BT_PHY_LE_1M_TX BIT(9)
+#define BT_PHY_LE_1M_RX BIT(10)
+#define BT_PHY_LE_2M_TX BIT(11)
+#define BT_PHY_LE_2M_RX BIT(12)
+#define BT_PHY_LE_CODED_TX BIT(13)
+#define BT_PHY_LE_CODED_RX BIT(14)
+
+#define BT_PHY_BREDR_MASK (BT_PHY_BR_1M_1SLOT | BT_PHY_BR_1M_3SLOT | \
+ BT_PHY_BR_1M_5SLOT | BT_PHY_EDR_2M_1SLOT | \
+ BT_PHY_EDR_2M_3SLOT | BT_PHY_EDR_2M_5SLOT | \
+ BT_PHY_EDR_3M_1SLOT | BT_PHY_EDR_3M_3SLOT | \
+ BT_PHY_EDR_3M_5SLOT)
+#define BT_PHY_LE_MASK (BT_PHY_LE_1M_TX | BT_PHY_LE_1M_RX | \
+ BT_PHY_LE_2M_TX | BT_PHY_LE_2M_RX | \
+ BT_PHY_LE_CODED_TX | BT_PHY_LE_CODED_RX)
#define BT_MODE 15
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index a27cd3626b87..a2beda3b0071 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -1883,6 +1883,15 @@ struct hci_cp_le_set_default_phy {
#define HCI_LE_SET_PHY_2M 0x02
#define HCI_LE_SET_PHY_CODED 0x04
+#define HCI_OP_LE_SET_PHY 0x2032
+struct hci_cp_le_set_phy {
+ __le16 handle;
+ __u8 all_phys;
+ __u8 tx_phys;
+ __u8 rx_phys;
+ __le16 phy_opts;
+} __packed;
+
#define HCI_OP_LE_SET_EXT_SCAN_PARAMS 0x2041
struct hci_cp_le_set_ext_scan_params {
__u8 own_addr_type;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 8aadf4cdead2..71bbaa7dc790 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -2336,6 +2336,7 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
void *hci_recv_event_data(struct hci_dev *hdev, __u8 event);
u32 hci_conn_get_phy(struct hci_conn *conn);
+int hci_conn_set_phy(struct hci_conn *conn, u32 phys);
/* ----- HCI Sockets ----- */
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h
index 56076bbc981d..73e494b2591d 100644
--- a/include/net/bluetooth/hci_sync.h
+++ b/include/net/bluetooth/hci_sync.h
@@ -191,3 +191,6 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn);
int hci_past_sync(struct hci_conn *conn, struct hci_conn *le);
int hci_le_read_remote_features(struct hci_conn *conn);
+
+int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type);
+int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys);
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
index 165e7a03b8e9..e9a8350e7ccf 100644
--- a/include/net/netfilter/nf_conntrack_expect.h
+++ b/include/net/netfilter/nf_conntrack_expect.h
@@ -22,10 +22,16 @@ struct nf_conntrack_expect {
/* Hash member */
struct hlist_node hnode;
+ /* Network namespace */
+ possible_net_t net;
+
/* We expect this tuple, with the following mask */
struct nf_conntrack_tuple tuple;
struct nf_conntrack_tuple_mask mask;
+#ifdef CONFIG_NF_CONNTRACK_ZONES
+ struct nf_conntrack_zone zone;
+#endif
/* Usage count. */
refcount_t use;
@@ -40,7 +46,7 @@ struct nf_conntrack_expect {
struct nf_conntrack_expect *this);
/* Helper to assign to new connection */
- struct nf_conntrack_helper *helper;
+ struct nf_conntrack_helper __rcu *helper;
/* The conntrack of the master connection */
struct nf_conn *master;
@@ -62,7 +68,17 @@ struct nf_conntrack_expect {
static inline struct net *nf_ct_exp_net(struct nf_conntrack_expect *exp)
{
- return nf_ct_net(exp->master);
+ return read_pnet(&exp->net);
+}
+
+static inline bool nf_ct_exp_zone_equal_any(const struct nf_conntrack_expect *a,
+ const struct nf_conntrack_zone *b)
+{
+#ifdef CONFIG_NF_CONNTRACK_ZONES
+ return a->zone.id == b->id;
+#else
+ return true;
+#endif
}
#define NF_CT_EXP_POLICY_NAME_LEN 16
diff --git a/include/net/netns/mpls.h b/include/net/netns/mpls.h
index 6682e51513ef..2073cbac2afb 100644
--- a/include/net/netns/mpls.h
+++ b/include/net/netns/mpls.h
@@ -17,6 +17,7 @@ struct netns_mpls {
size_t platform_labels;
struct mpls_route __rcu * __rcu *platform_label;
struct mutex platform_mutex;
+ seqcount_mutex_t platform_label_seq;
struct ctl_table_header *ctl;
};
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index ac1a5cf10287..84fb1f7b0d81 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -189,12 +189,15 @@ static void io_poison_req(struct io_kiocb *req)
static inline unsigned int __io_cqring_events(struct io_ring_ctx *ctx)
{
- return ctx->cached_cq_tail - READ_ONCE(ctx->rings->cq.head);
+ struct io_rings *rings = io_get_rings(ctx);
+ return ctx->cached_cq_tail - READ_ONCE(rings->cq.head);
}
static inline unsigned int __io_cqring_events_user(struct io_ring_ctx *ctx)
{
- return READ_ONCE(ctx->rings->cq.tail) - READ_ONCE(ctx->rings->cq.head);
+ struct io_rings *rings = io_get_rings(ctx);
+
+ return READ_ONCE(rings->cq.tail) - READ_ONCE(rings->cq.head);
}
static inline void req_fail_link_node(struct io_kiocb *req, int res)
@@ -2536,12 +2539,15 @@ static enum hrtimer_restart io_cqring_min_timer_wakeup(struct hrtimer *timer)
if (io_has_work(ctx))
goto out_wake;
/* got events since we started waiting, min timeout is done */
- if (iowq->cq_min_tail != READ_ONCE(ctx->rings->cq.tail))
- goto out_wake;
- /* if we have any events and min timeout expired, we're done */
- if (io_cqring_events(ctx))
- goto out_wake;
+ scoped_guard(rcu) {
+ struct io_rings *rings = io_get_rings(ctx);
+ if (iowq->cq_min_tail != READ_ONCE(rings->cq.tail))
+ goto out_wake;
+ /* if we have any events and min timeout expired, we're done */
+ if (io_cqring_events(ctx))
+ goto out_wake;
+ }
/*
* If using deferred task_work running and application is waiting on
* more than one request, ensure we reset it now where we are switching
@@ -2652,9 +2658,9 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags,
struct ext_arg *ext_arg)
{
struct io_wait_queue iowq;
- struct io_rings *rings = ctx->rings;
+ struct io_rings *rings;
ktime_t start_time;
- int ret;
+ int ret, nr_wait;
min_events = min_t(int, min_events, ctx->cq_entries);
@@ -2667,15 +2673,23 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags,
if (unlikely(test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq)))
io_cqring_do_overflow_flush(ctx);
- if (__io_cqring_events_user(ctx) >= min_events)
+
+ rcu_read_lock();
+ rings = io_get_rings(ctx);
+ if (__io_cqring_events_user(ctx) >= min_events) {
+ rcu_read_unlock();
return 0;
+ }
init_waitqueue_func_entry(&iowq.wq, io_wake_function);
iowq.wq.private = current;
INIT_LIST_HEAD(&iowq.wq.entry);
iowq.ctx = ctx;
- iowq.cq_tail = READ_ONCE(ctx->rings->cq.head) + min_events;
- iowq.cq_min_tail = READ_ONCE(ctx->rings->cq.tail);
+ iowq.cq_tail = READ_ONCE(rings->cq.head) + min_events;
+ iowq.cq_min_tail = READ_ONCE(rings->cq.tail);
+ nr_wait = (int) iowq.cq_tail - READ_ONCE(rings->cq.tail);
+ rcu_read_unlock();
+ rings = NULL;
iowq.nr_timeouts = atomic_read(&ctx->cq_timeouts);
iowq.hit_timeout = 0;
iowq.min_timeout = ext_arg->min_time;
@@ -2706,14 +2720,6 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags,
trace_io_uring_cqring_wait(ctx, min_events);
do {
unsigned long check_cq;
- int nr_wait;
-
- /* if min timeout has been hit, don't reset wait count */
- if (!iowq.hit_timeout)
- nr_wait = (int) iowq.cq_tail -
- READ_ONCE(ctx->rings->cq.tail);
- else
- nr_wait = 1;
if (ctx->flags & IORING_SETUP_DEFER_TASKRUN) {
atomic_set(&ctx->cq_wait_nr, nr_wait);
@@ -2764,13 +2770,22 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, u32 flags,
break;
}
cond_resched();
+
+ /* if min timeout has been hit, don't reset wait count */
+ if (!iowq.hit_timeout)
+ scoped_guard(rcu)
+ nr_wait = (int) iowq.cq_tail -
+ READ_ONCE(io_get_rings(ctx)->cq.tail);
+ else
+ nr_wait = 1;
} while (1);
if (!(ctx->flags & IORING_SETUP_DEFER_TASKRUN))
finish_wait(&ctx->cq_wait, &iowq.wq);
restore_saved_sigmask_unless(ret == -EINTR);
- return READ_ONCE(rings->cq.head) == READ_ONCE(rings->cq.tail) ? ret : 0;
+ guard(rcu)();
+ return READ_ONCE(io_get_rings(ctx)->cq.head) == READ_ONCE(io_get_rings(ctx)->cq.tail) ? ret : 0;
}
static void io_rings_free(struct io_ring_ctx *ctx)
@@ -2954,7 +2969,9 @@ static __poll_t io_uring_poll(struct file *file, poll_table *wait)
*/
poll_wait(file, &ctx->poll_wq, wait);
- if (!io_sqring_full(ctx))
+ rcu_read_lock();
+
+ if (!__io_sqring_full(ctx))
mask |= EPOLLOUT | EPOLLWRNORM;
/*
@@ -2974,6 +2991,7 @@ static __poll_t io_uring_poll(struct file *file, poll_table *wait)
if (__io_cqring_events_user(ctx) || io_has_work(ctx))
mask |= EPOLLIN | EPOLLRDNORM;
+ rcu_read_unlock();
return mask;
}
diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h
index 0f096f44d34b..6ee49991cec8 100644
--- a/io_uring/io_uring.h
+++ b/io_uring/io_uring.h
@@ -132,16 +132,28 @@ struct io_wait_queue {
#endif
};
+static inline struct io_rings *io_get_rings(struct io_ring_ctx *ctx)
+{
+ return rcu_dereference_check(ctx->rings_rcu,
+ lockdep_is_held(&ctx->uring_lock) ||
+ lockdep_is_held(&ctx->completion_lock));
+}
+
static inline bool io_should_wake(struct io_wait_queue *iowq)
{
struct io_ring_ctx *ctx = iowq->ctx;
- int dist = READ_ONCE(ctx->rings->cq.tail) - (int) iowq->cq_tail;
+ struct io_rings *rings;
+ int dist;
+
+ guard(rcu)();
+ rings = io_get_rings(ctx);
/*
* Wake up if we have enough events, or if a timeout occurred since we
* started waiting. For timeouts, we always want to return to userspace,
* regardless of event count.
*/
+ dist = READ_ONCE(rings->cq.tail) - (int) iowq->cq_tail;
return dist >= 0 || atomic_read(&ctx->cq_timeouts) != iowq->nr_timeouts;
}
@@ -432,9 +444,9 @@ static inline void io_cqring_wake(struct io_ring_ctx *ctx)
__io_wq_wake(&ctx->cq_wait);
}
-static inline bool io_sqring_full(struct io_ring_ctx *ctx)
+static inline bool __io_sqring_full(struct io_ring_ctx *ctx)
{
- struct io_rings *r = ctx->rings;
+ struct io_rings *r = io_get_rings(ctx);
/*
* SQPOLL must use the actual sqring head, as using the cached_sq_head
@@ -446,9 +458,15 @@ static inline bool io_sqring_full(struct io_ring_ctx *ctx)
return READ_ONCE(r->sq.tail) - READ_ONCE(r->sq.head) == ctx->sq_entries;
}
-static inline unsigned int io_sqring_entries(struct io_ring_ctx *ctx)
+static inline bool io_sqring_full(struct io_ring_ctx *ctx)
{
- struct io_rings *rings = ctx->rings;
+ guard(rcu)();
+ return __io_sqring_full(ctx);
+}
+
+static inline unsigned int __io_sqring_entries(struct io_ring_ctx *ctx)
+{
+ struct io_rings *rings = io_get_rings(ctx);
unsigned int entries;
/* make sure SQ entry isn't read before tail */
@@ -509,6 +527,12 @@ static inline void io_tw_lock(struct io_ring_ctx *ctx, io_tw_token_t tw)
lockdep_assert_held(&ctx->uring_lock);
}
+static inline unsigned int io_sqring_entries(struct io_ring_ctx *ctx)
+{
+ guard(rcu)();
+ return __io_sqring_entries(ctx);
+}
+
/*
* Don't complete immediately but use deferred completion infrastructure.
* Protected by ->uring_lock and can only be used either with
diff --git a/io_uring/net.c b/io_uring/net.c
index cfcd49f0cecf..014ebdae2c0d 100644
--- a/io_uring/net.c
+++ b/io_uring/net.c
@@ -421,6 +421,8 @@ int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
sr->done_io = 0;
sr->len = READ_ONCE(sqe->len);
+ if (unlikely(sr->len < 0))
+ return -EINVAL;
sr->flags = READ_ONCE(sqe->ioprio);
if (sr->flags & ~SENDMSG_FLAGS)
return -EINVAL;
@@ -791,6 +793,8 @@ int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
sr->umsg = u64_to_user_ptr(READ_ONCE(sqe->addr));
sr->len = READ_ONCE(sqe->len);
+ if (unlikely(sr->len < 0))
+ return -EINVAL;
sr->flags = READ_ONCE(sqe->ioprio);
if (sr->flags & ~RECVMSG_FLAGS)
return -EINVAL;
diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c
index 41c89f5c616d..81446f9649ae 100644
--- a/io_uring/rsrc.c
+++ b/io_uring/rsrc.c
@@ -1084,6 +1084,10 @@ static int io_import_fixed(int ddir, struct iov_iter *iter,
return ret;
if (!(imu->dir & (1 << ddir)))
return -EFAULT;
+ if (unlikely(!len)) {
+ iov_iter_bvec(iter, ddir, NULL, 0, 0);
+ return 0;
+ }
offset = buf_addr - imu->ubuf;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 9032c6d4dbbc..d1394e16d108 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -608,6 +608,13 @@ static bool is_atomic_load_insn(const struct bpf_insn *insn)
insn->imm == BPF_LOAD_ACQ;
}
+static bool is_atomic_fetch_insn(const struct bpf_insn *insn)
+{
+ return BPF_CLASS(insn->code) == BPF_STX &&
+ BPF_MODE(insn->code) == BPF_ATOMIC &&
+ (insn->imm & BPF_FETCH);
+}
+
static int __get_spi(s32 off)
{
return (-off - 1) / BPF_REG_SIZE;
@@ -4356,10 +4363,24 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
* dreg still needs precision before this insn
*/
}
- } else if (class == BPF_LDX || is_atomic_load_insn(insn)) {
- if (!bt_is_reg_set(bt, dreg))
+ } else if (class == BPF_LDX ||
+ is_atomic_load_insn(insn) ||
+ is_atomic_fetch_insn(insn)) {
+ u32 load_reg = dreg;
+
+ /*
+ * Atomic fetch operation writes the old value into
+ * a register (sreg or r0) and if it was tracked for
+ * precision, propagate to the stack slot like we do
+ * in regular ldx.
+ */
+ if (is_atomic_fetch_insn(insn))
+ load_reg = insn->imm == BPF_CMPXCHG ?
+ BPF_REG_0 : sreg;
+
+ if (!bt_is_reg_set(bt, load_reg))
return 0;
- bt_clear_reg(bt, dreg);
+ bt_clear_reg(bt, load_reg);
/* scalars can only be spilled into stack w/o losing precision.
* Load from any other memory can be zero extended.
@@ -7806,7 +7827,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
} else if (reg->type == CONST_PTR_TO_MAP) {
err = check_ptr_to_map_access(env, regs, regno, off, size, t,
value_regno);
- } else if (base_type(reg->type) == PTR_TO_BUF) {
+ } else if (base_type(reg->type) == PTR_TO_BUF &&
+ !type_may_be_null(reg->type)) {
bool rdonly_mem = type_is_rdonly_mem(reg->type);
u32 *max_access;
@@ -19343,8 +19365,13 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
* since someone could have accessed through (ptr - k), or
* even done ptr -= k in a register, to get a safe access.
*/
- if (rold->range > rcur->range)
+ if (rold->range < 0 || rcur->range < 0) {
+ /* special case for [BEYOND|AT]_PKT_END */
+ if (rold->range != rcur->range)
+ return false;
+ } else if (rold->range > rcur->range) {
return false;
+ }
/* If the offsets don't match, we can't trust our alignment;
* nor can we be sure that we won't fall out of range.
*/
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 3f9e4bcd7198..937010076490 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -2126,6 +2126,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
#endif
init_waitqueue_head(&cgrp->offline_waitq);
+ init_waitqueue_head(&cgrp->dying_populated_waitq);
INIT_WORK(&cgrp->release_agent_work, cgroup1_release_agent);
}
@@ -6224,6 +6225,78 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
return 0;
};
+/**
+ * cgroup_drain_dying - wait for dying tasks to leave before rmdir
+ * @cgrp: the cgroup being removed
+ *
+ * cgroup.procs and cgroup.threads use css_task_iter which filters out
+ * PF_EXITING tasks so that userspace doesn't see tasks that have already been
+ * reaped via waitpid(). However, cgroup_has_tasks() - which tests whether the
+ * cgroup has non-empty css_sets - is only updated when dying tasks pass through
+ * cgroup_task_dead() in finish_task_switch(). This creates a window where
+ * cgroup.procs reads empty but cgroup_has_tasks() is still true, making rmdir
+ * fail with -EBUSY from cgroup_destroy_locked() even though userspace sees no
+ * tasks.
+ *
+ * This function aligns cgroup_has_tasks() with what userspace can observe. If
+ * cgroup_has_tasks() but the task iterator sees nothing (all remaining tasks are
+ * PF_EXITING), we wait for cgroup_task_dead() to finish processing them. As the
+ * window between PF_EXITING and cgroup_task_dead() is short, the wait is brief.
+ *
+ * This function only concerns itself with this cgroup's own dying tasks.
+ * Whether the cgroup has children is cgroup_destroy_locked()'s problem.
+ *
+ * Each cgroup_task_dead() kicks the waitqueue via cset->cgrp_links, and we
+ * retry the full check from scratch.
+ *
+ * Must be called with cgroup_mutex held.
+ */
+static int cgroup_drain_dying(struct cgroup *cgrp)
+ __releases(&cgroup_mutex) __acquires(&cgroup_mutex)
+{
+ struct css_task_iter it;
+ struct task_struct *task;
+ DEFINE_WAIT(wait);
+
+ lockdep_assert_held(&cgroup_mutex);
+retry:
+ if (!cgroup_has_tasks(cgrp))
+ return 0;
+
+ /* Same iterator as cgroup.threads - if any task is visible, it's busy */
+ css_task_iter_start(&cgrp->self, 0, &it);
+ task = css_task_iter_next(&it);
+ css_task_iter_end(&it);
+
+ if (task)
+ return -EBUSY;
+
+ /*
+ * All remaining tasks are PF_EXITING and will pass through
+ * cgroup_task_dead() shortly. Wait for a kick and retry.
+ *
+ * cgroup_has_tasks() can't transition from false to true while we're
+ * holding cgroup_mutex, but the true to false transition happens
+ * under css_set_lock (via cgroup_task_dead()). We must retest and
+ * prepare_to_wait() under css_set_lock. Otherwise, the transition
+ * can happen between our first test and prepare_to_wait(), and we
+ * sleep with no one to wake us.
+ */
+ spin_lock_irq(&css_set_lock);
+ if (!cgroup_has_tasks(cgrp)) {
+ spin_unlock_irq(&css_set_lock);
+ return 0;
+ }
+ prepare_to_wait(&cgrp->dying_populated_waitq, &wait,
+ TASK_UNINTERRUPTIBLE);
+ spin_unlock_irq(&css_set_lock);
+ mutex_unlock(&cgroup_mutex);
+ schedule();
+ finish_wait(&cgrp->dying_populated_waitq, &wait);
+ mutex_lock(&cgroup_mutex);
+ goto retry;
+}
+
int cgroup_rmdir(struct kernfs_node *kn)
{
struct cgroup *cgrp;
@@ -6233,9 +6306,12 @@ int cgroup_rmdir(struct kernfs_node *kn)
if (!cgrp)
return 0;
- ret = cgroup_destroy_locked(cgrp);
- if (!ret)
- TRACE_CGROUP_PATH(rmdir, cgrp);
+ ret = cgroup_drain_dying(cgrp);
+ if (!ret) {
+ ret = cgroup_destroy_locked(cgrp);
+ if (!ret)
+ TRACE_CGROUP_PATH(rmdir, cgrp);
+ }
cgroup_kn_unlock(kn);
return ret;
@@ -6995,6 +7071,7 @@ void cgroup_task_exit(struct task_struct *tsk)
static void do_cgroup_task_dead(struct task_struct *tsk)
{
+ struct cgrp_cset_link *link;
struct css_set *cset;
unsigned long flags;
@@ -7008,6 +7085,11 @@ static void do_cgroup_task_dead(struct task_struct *tsk)
if (thread_group_leader(tsk) && atomic_read(&tsk->signal->live))
list_add_tail(&tsk->cg_list, &cset->dying_tasks);
+ /* kick cgroup_drain_dying() waiters, see cgroup_rmdir() */
+ list_for_each_entry(link, &cset->cgrp_links, cgrp_link)
+ if (waitqueue_active(&link->cgrp->dying_populated_waitq))
+ wake_up(&link->cgrp->dying_populated_waitq);
+
if (dl_task(tsk))
dec_dl_tasks_cs(tsk);
diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index cdd6e025935d..e0813ca9469a 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -355,8 +355,21 @@ static int kallsyms_lookup_buildid(unsigned long addr,
{
int ret;
- namebuf[KSYM_NAME_LEN - 1] = 0;
+ /*
+ * kallsyms_lookus() returns pointer to namebuf on success and
+ * NULL on error. But some callers ignore the return value.
+ * Instead they expect @namebuf filled either with valid
+ * or empty string.
+ */
namebuf[0] = 0;
+ /*
+ * Initialize the module-related return values. They are not set
+ * when the symbol is in vmlinux or it is a bpf address.
+ */
+ if (modname)
+ *modname = NULL;
+ if (modbuildid)
+ *modbuildid = NULL;
if (is_ksym_addr(addr)) {
unsigned long pos;
@@ -365,10 +378,6 @@ static int kallsyms_lookup_buildid(unsigned long addr,
/* Grab name */
kallsyms_expand_symbol(get_symbol_offset(pos),
namebuf, KSYM_NAME_LEN);
- if (modname)
- *modname = NULL;
- if (modbuildid)
- *modbuildid = NULL;
return strlen(namebuf);
}
@@ -425,6 +434,37 @@ int lookup_symbol_name(unsigned long addr, char *symname)
return lookup_module_symbol_name(addr, symname);
}
+#ifdef CONFIG_STACKTRACE_BUILD_ID
+
+static int append_buildid(char *buffer, const char *modname,
+ const unsigned char *buildid)
+{
+ if (!modname)
+ return 0;
+
+ if (!buildid) {
+ pr_warn_once("Undefined buildid for the module %s\n", modname);
+ return 0;
+ }
+
+ /* build ID should match length of sprintf */
+#ifdef CONFIG_MODULES
+ static_assert(sizeof(typeof_member(struct module, build_id)) == 20);
+#endif
+
+ return sprintf(buffer, " %20phN", buildid);
+}
+
+#else /* CONFIG_STACKTRACE_BUILD_ID */
+
+static int append_buildid(char *buffer, const char *modname,
+ const unsigned char *buildid)
+{
+ return 0;
+}
+
+#endif /* CONFIG_STACKTRACE_BUILD_ID */
+
/* Look up a kernel symbol and return it in a text buffer. */
static int __sprint_symbol(char *buffer, unsigned long address,
int symbol_offset, int add_offset, int add_buildid)
@@ -434,6 +474,9 @@ static int __sprint_symbol(char *buffer, unsigned long address,
unsigned long offset, size;
int len;
+ /* Prevent module removal until modname and modbuildid are printed */
+ guard(rcu)();
+
address += symbol_offset;
len = kallsyms_lookup_buildid(address, &size, &offset, &modname, &buildid,
buffer);
@@ -447,15 +490,8 @@ static int __sprint_symbol(char *buffer, unsigned long address,
if (modname) {
len += sprintf(buffer + len, " [%s", modname);
-#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID)
- if (add_buildid && buildid) {
- /* build ID should match length of sprintf */
-#if IS_ENABLED(CONFIG_MODULES)
- static_assert(sizeof(typeof_member(struct module, build_id)) == 20);
-#endif
- len += sprintf(buffer + len, " %20phN", buildid);
- }
-#endif
+ if (add_buildid)
+ len += append_buildid(buffer + len, modname, buildid);
len += sprintf(buffer + len, "]");
}
diff --git a/kernel/power/em_netlink.c b/kernel/power/em_netlink.c
index 5a611d3950fd..4d4fd29bd2be 100644
--- a/kernel/power/em_netlink.c
+++ b/kernel/power/em_netlink.c
@@ -109,6 +109,8 @@ int dev_energymodel_nl_get_perf_domains_doit(struct sk_buff *skb,
id = nla_get_u32(info->attrs[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID]);
pd = em_perf_domain_get_by_id(id);
+ if (!pd)
+ return -EINVAL;
__em_nl_get_pd_size(pd, &msg_sz);
msg = genlmsg_new(msg_sz, GFP_KERNEL);
diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index 93f009e1076d..3504ec9bd730 100644
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -798,6 +798,7 @@ static void print_rq(struct seq_file *m, struct rq *rq, int rq_cpu)
void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
{
s64 left_vruntime = -1, zero_vruntime, right_vruntime = -1, left_deadline = -1, spread;
+ u64 avruntime;
struct sched_entity *last, *first, *root;
struct rq *rq = cpu_rq(cpu);
unsigned long flags;
@@ -821,6 +822,7 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
if (last)
right_vruntime = last->vruntime;
zero_vruntime = cfs_rq->zero_vruntime;
+ avruntime = avg_vruntime(cfs_rq);
raw_spin_rq_unlock_irqrestore(rq, flags);
SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "left_deadline",
@@ -830,7 +832,7 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "zero_vruntime",
SPLIT_NS(zero_vruntime));
SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "avg_vruntime",
- SPLIT_NS(avg_vruntime(cfs_rq)));
+ SPLIT_NS(avruntime));
SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "right_vruntime",
SPLIT_NS(right_vruntime));
spread = right_vruntime - left_vruntime;
diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
index 2c32e12af435..35fa2970abac 100644
--- a/kernel/sched/ext.c
+++ b/kernel/sched/ext.c
@@ -1103,15 +1103,6 @@ static void dispatch_enqueue(struct scx_sched *sch, struct scx_dispatch_q *dsq,
dsq_mod_nr(dsq, 1);
p->scx.dsq = dsq;
- /*
- * scx.ddsp_dsq_id and scx.ddsp_enq_flags are only relevant on the
- * direct dispatch path, but we clear them here because the direct
- * dispatch verdict may be overridden on the enqueue path during e.g.
- * bypass.
- */
- p->scx.ddsp_dsq_id = SCX_DSQ_INVALID;
- p->scx.ddsp_enq_flags = 0;
-
/*
* We're transitioning out of QUEUEING or DISPATCHING. store_release to
* match waiters' load_acquire.
@@ -1277,12 +1268,34 @@ static void mark_direct_dispatch(struct scx_sched *sch,
p->scx.ddsp_enq_flags = enq_flags;
}
+/*
+ * Clear @p direct dispatch state when leaving the scheduler.
+ *
+ * Direct dispatch state must be cleared in the following cases:
+ * - direct_dispatch(): cleared on the synchronous enqueue path, deferred
+ * dispatch keeps the state until consumed
+ * - process_ddsp_deferred_locals(): cleared after consuming deferred state,
+ * - do_enqueue_task(): cleared on enqueue fallbacks where the dispatch
+ * verdict is ignored (local/global/bypass)
+ * - dequeue_task_scx(): cleared after dispatch_dequeue(), covering deferred
+ * cancellation and holding_cpu races
+ * - scx_disable_task(): cleared for queued wakeup tasks, which are excluded by
+ * the scx_bypass() loop, so that stale state is not reused by a subsequent
+ * scheduler instance
+ */
+static inline void clear_direct_dispatch(struct task_struct *p)
+{
+ p->scx.ddsp_dsq_id = SCX_DSQ_INVALID;
+ p->scx.ddsp_enq_flags = 0;
+}
+
static void direct_dispatch(struct scx_sched *sch, struct task_struct *p,
u64 enq_flags)
{
struct rq *rq = task_rq(p);
struct scx_dispatch_q *dsq =
find_dsq_for_dispatch(sch, rq, p->scx.ddsp_dsq_id, p);
+ u64 ddsp_enq_flags;
touch_core_sched_dispatch(rq, p);
@@ -1323,8 +1336,10 @@ static void direct_dispatch(struct scx_sched *sch, struct task_struct *p,
return;
}
- dispatch_enqueue(sch, dsq, p,
- p->scx.ddsp_enq_flags | SCX_ENQ_CLEAR_OPSS);
+ ddsp_enq_flags = p->scx.ddsp_enq_flags;
+ clear_direct_dispatch(p);
+
+ dispatch_enqueue(sch, dsq, p, ddsp_enq_flags | SCX_ENQ_CLEAR_OPSS);
}
static bool scx_rq_online(struct rq *rq)
@@ -1433,6 +1448,7 @@ static void do_enqueue_task(struct rq *rq, struct task_struct *p, u64 enq_flags,
*/
touch_core_sched(rq, p);
refill_task_slice_dfl(sch, p);
+ clear_direct_dispatch(p);
dispatch_enqueue(sch, dsq, p, enq_flags);
}
@@ -1600,6 +1616,7 @@ static bool dequeue_task_scx(struct rq *rq, struct task_struct *p, int deq_flags
sub_nr_running(rq, 1);
dispatch_dequeue(rq, p);
+ clear_direct_dispatch(p);
return true;
}
@@ -2283,13 +2300,15 @@ static void process_ddsp_deferred_locals(struct rq *rq)
struct task_struct, scx.dsq_list.node))) {
struct scx_sched *sch = scx_root;
struct scx_dispatch_q *dsq;
+ u64 dsq_id = p->scx.ddsp_dsq_id;
+ u64 enq_flags = p->scx.ddsp_enq_flags;
list_del_init(&p->scx.dsq_list.node);
+ clear_direct_dispatch(p);
- dsq = find_dsq_for_dispatch(sch, rq, p->scx.ddsp_dsq_id, p);
+ dsq = find_dsq_for_dispatch(sch, rq, dsq_id, p);
if (!WARN_ON_ONCE(dsq->id != SCX_DSQ_LOCAL))
- dispatch_to_local_dsq(sch, rq, dsq, p,
- p->scx.ddsp_enq_flags);
+ dispatch_to_local_dsq(sch, rq, dsq, p, enq_flags);
}
}
@@ -2394,7 +2413,7 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
{
struct scx_sched *sch = scx_root;
- /* see kick_cpus_irq_workfn() */
+ /* see kick_sync_wait_bal_cb() */
smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1);
update_curr_scx(rq);
@@ -2437,6 +2456,48 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
switch_class(rq, next);
}
+static void kick_sync_wait_bal_cb(struct rq *rq)
+{
+ struct scx_kick_syncs __rcu *ks = __this_cpu_read(scx_kick_syncs);
+ unsigned long *ksyncs = rcu_dereference_sched(ks)->syncs;
+ bool waited;
+ s32 cpu;
+
+ /*
+ * Drop rq lock and enable IRQs while waiting. IRQs must be enabled
+ * — a target CPU may be waiting for us to process an IPI (e.g. TLB
+ * flush) while we wait for its kick_sync to advance.
+ *
+ * Also, keep advancing our own kick_sync so that new kick_sync waits
+ * targeting us, which can start after we drop the lock, cannot form
+ * cyclic dependencies.
+ */
+retry:
+ waited = false;
+ for_each_cpu(cpu, rq->scx.cpus_to_sync) {
+ /*
+ * smp_load_acquire() pairs with smp_store_release() on
+ * kick_sync updates on the target CPUs.
+ */
+ if (cpu == cpu_of(rq) ||
+ smp_load_acquire(&cpu_rq(cpu)->scx.kick_sync) != ksyncs[cpu]) {
+ cpumask_clear_cpu(cpu, rq->scx.cpus_to_sync);
+ continue;
+ }
+
+ raw_spin_rq_unlock_irq(rq);
+ while (READ_ONCE(cpu_rq(cpu)->scx.kick_sync) == ksyncs[cpu]) {
+ smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1);
+ cpu_relax();
+ }
+ raw_spin_rq_lock_irq(rq);
+ waited = true;
+ }
+
+ if (waited)
+ goto retry;
+}
+
static struct task_struct *first_local_task(struct rq *rq)
{
return list_first_entry_or_null(&rq->scx.local_dsq.list,
@@ -2450,7 +2511,7 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
bool keep_prev;
struct task_struct *p;
- /* see kick_cpus_irq_workfn() */
+ /* see kick_sync_wait_bal_cb() */
smp_store_release(&rq->scx.kick_sync, rq->scx.kick_sync + 1);
rq_modified_clear(rq);
@@ -2460,6 +2521,17 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
rq_repin_lock(rq, rf);
maybe_queue_balance_callback(rq);
+ /*
+ * Defer to a balance callback which can drop rq lock and enable
+ * IRQs. Waiting directly in the pick path would deadlock against
+ * CPUs sending us IPIs (e.g. TLB flushes) while we wait for them.
+ */
+ if (unlikely(rq->scx.kick_sync_pending)) {
+ rq->scx.kick_sync_pending = false;
+ queue_balance_callback(rq, &rq->scx.kick_sync_bal_cb,
+ kick_sync_wait_bal_cb);
+ }
+
/*
* If any higher-priority sched class enqueued a runnable task on
* this rq during balance_one(), abort and return RETRY_TASK, so
@@ -2925,6 +2997,8 @@ static void scx_disable_task(struct task_struct *p)
lockdep_assert_rq_held(rq);
WARN_ON_ONCE(scx_get_task_state(p) != SCX_TASK_ENABLED);
+ clear_direct_dispatch(p);
+
if (SCX_HAS_OP(sch, disable))
SCX_CALL_OP_TASK(sch, SCX_KF_REST, disable, rq, p);
scx_set_task_state(p, SCX_TASK_READY);
@@ -4673,6 +4747,9 @@ static void scx_dump_state(struct scx_exit_info *ei, size_t dump_len)
if (!cpumask_empty(rq->scx.cpus_to_wait))
dump_line(&ns, " cpus_to_wait : %*pb",
cpumask_pr_args(rq->scx.cpus_to_wait));
+ if (!cpumask_empty(rq->scx.cpus_to_sync))
+ dump_line(&ns, " cpus_to_sync : %*pb",
+ cpumask_pr_args(rq->scx.cpus_to_sync));
used = seq_buf_used(&ns);
if (SCX_HAS_OP(sch, dump_cpu)) {
@@ -5571,11 +5648,11 @@ static bool kick_one_cpu(s32 cpu, struct rq *this_rq, unsigned long *ksyncs)
if (cpumask_test_cpu(cpu, this_scx->cpus_to_wait)) {
if (cur_class == &ext_sched_class) {
+ cpumask_set_cpu(cpu, this_scx->cpus_to_sync);
ksyncs[cpu] = rq->scx.kick_sync;
should_wait = true;
- } else {
- cpumask_clear_cpu(cpu, this_scx->cpus_to_wait);
}
+ cpumask_clear_cpu(cpu, this_scx->cpus_to_wait);
}
resched_curr(rq);
@@ -5630,27 +5707,15 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work)
cpumask_clear_cpu(cpu, this_scx->cpus_to_kick_if_idle);
}
- if (!should_wait)
- return;
-
- for_each_cpu(cpu, this_scx->cpus_to_wait) {
- unsigned long *wait_kick_sync = &cpu_rq(cpu)->scx.kick_sync;
-
- /*
- * Busy-wait until the task running at the time of kicking is no
- * longer running. This can be used to implement e.g. core
- * scheduling.
- *
- * smp_cond_load_acquire() pairs with store_releases in
- * pick_task_scx() and put_prev_task_scx(). The former breaks
- * the wait if SCX's scheduling path is entered even if the same
- * task is picked subsequently. The latter is necessary to break
- * the wait when $cpu is taken by a higher sched class.
- */
- if (cpu != cpu_of(this_rq))
- smp_cond_load_acquire(wait_kick_sync, VAL != ksyncs[cpu]);
-
- cpumask_clear_cpu(cpu, this_scx->cpus_to_wait);
+ /*
+ * Can't wait in hardirq — kick_sync can't advance, deadlocking if
+ * CPUs wait for each other. Defer to kick_sync_wait_bal_cb().
+ */
+ if (should_wait) {
+ raw_spin_rq_lock(this_rq);
+ this_scx->kick_sync_pending = true;
+ resched_curr(this_rq);
+ raw_spin_rq_unlock(this_rq);
}
}
@@ -5755,6 +5820,7 @@ void __init init_sched_ext_class(void)
BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_kick_if_idle, GFP_KERNEL, n));
BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_preempt, GFP_KERNEL, n));
BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_wait, GFP_KERNEL, n));
+ BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_sync, GFP_KERNEL, n));
rq->scx.deferred_irq_work = IRQ_WORK_INIT_HARD(deferred_irq_workfn);
rq->scx.kick_cpus_irq_work = IRQ_WORK_INIT_HARD(kick_cpus_irq_workfn);
diff --git a/kernel/sched/ext_idle.c b/kernel/sched/ext_idle.c
index 3d9d404d5cd2..06f33f120a68 100644
--- a/kernel/sched/ext_idle.c
+++ b/kernel/sched/ext_idle.c
@@ -543,7 +543,7 @@ s32 scx_select_cpu_dfl(struct task_struct *p, s32 prev_cpu, u64 wake_flags,
* piled up on it even if there is an idle core elsewhere on
* the system.
*/
- waker_node = cpu_to_node(cpu);
+ waker_node = scx_cpu_node_if_enabled(cpu);
if (!(current->flags & PF_EXITING) &&
cpu_rq(cpu)->scx.local_dsq.nr == 0 &&
(!(flags & SCX_PICK_IDLE_IN_NODE) || (waker_node == node)) &&
@@ -861,25 +861,32 @@ static bool check_builtin_idle_enabled(struct scx_sched *sch)
* code.
*
* We can't simply check whether @p->migration_disabled is set in a
- * sched_ext callback, because migration is always disabled for the current
- * task while running BPF code.
+ * sched_ext callback, because the BPF prolog (__bpf_prog_enter) may disable
+ * migration for the current task while running BPF code.
*
- * The prolog (__bpf_prog_enter) and epilog (__bpf_prog_exit) respectively
- * disable and re-enable migration. For this reason, the current task
- * inside a sched_ext callback is always a migration-disabled task.
+ * Since the BPF prolog calls migrate_disable() only when CONFIG_PREEMPT_RCU
+ * is enabled (via rcu_read_lock_dont_migrate()), migration_disabled == 1 for
+ * the current task is ambiguous only in that case: it could be from the BPF
+ * prolog rather than a real migrate_disable() call.
*
- * Therefore, when @p->migration_disabled == 1, check whether @p is the
- * current task or not: if it is, then migration was not disabled before
- * entering the callback, otherwise migration was disabled.
+ * Without CONFIG_PREEMPT_RCU, the BPF prolog never calls migrate_disable(),
+ * so migration_disabled == 1 always means the task is truly
+ * migration-disabled.
+ *
+ * Therefore, when migration_disabled == 1 and CONFIG_PREEMPT_RCU is enabled,
+ * check whether @p is the current task or not: if it is, then migration was
+ * not disabled before entering the callback, otherwise migration was disabled.
*
* Returns true if @p is migration-disabled, false otherwise.
*/
static bool is_bpf_migration_disabled(const struct task_struct *p)
{
- if (p->migration_disabled == 1)
- return p != current;
- else
- return p->migration_disabled;
+ if (p->migration_disabled == 1) {
+ if (IS_ENABLED(CONFIG_PREEMPT_RCU))
+ return p != current;
+ return true;
+ }
+ return p->migration_disabled;
}
static s32 select_cpu_from_kfunc(struct scx_sched *sch, struct task_struct *p,
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index a8e766eaca1f..2625a78c0300 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -707,7 +707,7 @@ void update_zero_vruntime(struct cfs_rq *cfs_rq, s64 delta)
* Called in:
* - place_entity() -- before enqueue
* - update_entity_lag() -- before dequeue
- * - entity_tick()
+ * - update_deadline() -- slice expiration
*
* This means it is one entry 'behind' but that puts it close enough to where
* the bound on entity_key() is at most two lag bounds.
@@ -1131,6 +1131,7 @@ static bool update_deadline(struct cfs_rq *cfs_rq, struct sched_entity *se)
* EEVDF: vd_i = ve_i + r_i / w_i
*/
se->deadline = se->vruntime + calc_delta_fair(se->slice, se);
+ avg_vruntime(cfs_rq);
/*
* The task has consumed its request, reschedule.
@@ -5636,11 +5637,6 @@ entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
update_load_avg(cfs_rq, curr, UPDATE_TG);
update_cfs_group(curr);
- /*
- * Pulls along cfs_rq::zero_vruntime.
- */
- avg_vruntime(cfs_rq);
-
#ifdef CONFIG_SCHED_HRTICK
/*
* queued ticks are scheduled to match the slice, so don't bother
@@ -9166,7 +9162,7 @@ static void yield_task_fair(struct rq *rq)
*/
if (entity_eligible(cfs_rq, se)) {
se->vruntime = se->deadline;
- se->deadline += calc_delta_fair(se->slice, se);
+ update_deadline(cfs_rq, se);
}
}
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index d3485d48be28..8ae171de9e6c 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -803,9 +803,12 @@ struct scx_rq {
cpumask_var_t cpus_to_kick_if_idle;
cpumask_var_t cpus_to_preempt;
cpumask_var_t cpus_to_wait;
+ cpumask_var_t cpus_to_sync;
+ bool kick_sync_pending;
unsigned long kick_sync;
local_t reenq_local_deferred;
struct balance_callback deferred_bal_cb;
+ struct balance_callback kick_sync_bal_cb;
struct irq_work deferred_irq_work;
struct irq_work kick_cpus_irq_work;
struct scx_dispatch_q bypass_dsq;
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index e448a2553f7c..42734975a06b 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -2739,6 +2739,10 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
if (!is_kprobe_multi(prog))
return -EINVAL;
+ /* kprobe_multi is not allowed to be sleepable. */
+ if (prog->sleepable)
+ return -EINVAL;
+
/* Writing to context is not allowed for kprobes. */
if (prog->aux->kprobe_write_ctx)
return -EINVAL;
diff --git a/lib/crypto/chacha-block-generic.c b/lib/crypto/chacha-block-generic.c
index 77f68de71066..4a6d627580cb 100644
--- a/lib/crypto/chacha-block-generic.c
+++ b/lib/crypto/chacha-block-generic.c
@@ -87,6 +87,8 @@ void chacha_block_generic(struct chacha_state *state,
&out[i * sizeof(u32)]);
state->x[12]++;
+
+ chacha_zeroize_state(&permuted_state);
}
EXPORT_SYMBOL(chacha_block_generic);
@@ -110,5 +112,7 @@ void hchacha_block_generic(const struct chacha_state *state,
memcpy(&out[0], &permuted_state.x[0], 16);
memcpy(&out[4], &permuted_state.x[12], 16);
+
+ chacha_zeroize_state(&permuted_state);
}
EXPORT_SYMBOL(hchacha_block_generic);
diff --git a/net/atm/lec.c b/net/atm/lec.c
index c39dc5d36797..b6f764e524f7 100644
--- a/net/atm/lec.c
+++ b/net/atm/lec.c
@@ -154,10 +154,19 @@ static void lec_handle_bridge(struct sk_buff *skb, struct net_device *dev)
/* 0x01 is topology change */
priv = netdev_priv(dev);
- atm_force_charge(priv->lecd, skb2->truesize);
- sk = sk_atm(priv->lecd);
- skb_queue_tail(&sk->sk_receive_queue, skb2);
- sk->sk_data_ready(sk);
+ struct atm_vcc *vcc;
+
+ rcu_read_lock();
+ vcc = rcu_dereference(priv->lecd);
+ if (vcc) {
+ atm_force_charge(vcc, skb2->truesize);
+ sk = sk_atm(vcc);
+ skb_queue_tail(&sk->sk_receive_queue, skb2);
+ sk->sk_data_ready(sk);
+ } else {
+ dev_kfree_skb(skb2);
+ }
+ rcu_read_unlock();
}
}
#endif /* IS_ENABLED(CONFIG_BRIDGE) */
@@ -216,7 +225,7 @@ static netdev_tx_t lec_start_xmit(struct sk_buff *skb,
int is_rdesc;
pr_debug("called\n");
- if (!priv->lecd) {
+ if (!rcu_access_pointer(priv->lecd)) {
pr_info("%s:No lecd attached\n", dev->name);
dev->stats.tx_errors++;
netif_stop_queue(dev);
@@ -449,10 +458,19 @@ static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
break;
skb2->len = sizeof(struct atmlec_msg);
skb_copy_to_linear_data(skb2, mesg, sizeof(*mesg));
- atm_force_charge(priv->lecd, skb2->truesize);
- sk = sk_atm(priv->lecd);
- skb_queue_tail(&sk->sk_receive_queue, skb2);
- sk->sk_data_ready(sk);
+ struct atm_vcc *vcc;
+
+ rcu_read_lock();
+ vcc = rcu_dereference(priv->lecd);
+ if (vcc) {
+ atm_force_charge(vcc, skb2->truesize);
+ sk = sk_atm(vcc);
+ skb_queue_tail(&sk->sk_receive_queue, skb2);
+ sk->sk_data_ready(sk);
+ } else {
+ dev_kfree_skb(skb2);
+ }
+ rcu_read_unlock();
}
}
#endif /* IS_ENABLED(CONFIG_BRIDGE) */
@@ -468,23 +486,16 @@ static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
static void lec_atm_close(struct atm_vcc *vcc)
{
- struct sk_buff *skb;
struct net_device *dev = (struct net_device *)vcc->proto_data;
struct lec_priv *priv = netdev_priv(dev);
- priv->lecd = NULL;
+ rcu_assign_pointer(priv->lecd, NULL);
+ synchronize_rcu();
/* Do something needful? */
netif_stop_queue(dev);
lec_arp_destroy(priv);
- if (skb_peek(&sk_atm(vcc)->sk_receive_queue))
- pr_info("%s closing with messages pending\n", dev->name);
- while ((skb = skb_dequeue(&sk_atm(vcc)->sk_receive_queue))) {
- atm_return(vcc, skb->truesize);
- dev_kfree_skb(skb);
- }
-
pr_info("%s: Shut down!\n", dev->name);
module_put(THIS_MODULE);
}
@@ -510,12 +521,14 @@ send_to_lecd(struct lec_priv *priv, atmlec_msg_type type,
const unsigned char *mac_addr, const unsigned char *atm_addr,
struct sk_buff *data)
{
+ struct atm_vcc *vcc;
struct sock *sk;
struct sk_buff *skb;
struct atmlec_msg *mesg;
- if (!priv || !priv->lecd)
+ if (!priv || !rcu_access_pointer(priv->lecd))
return -1;
+
skb = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC);
if (!skb)
return -1;
@@ -532,18 +545,27 @@ send_to_lecd(struct lec_priv *priv, atmlec_msg_type type,
if (atm_addr)
memcpy(&mesg->content.normal.atm_addr, atm_addr, ATM_ESA_LEN);
- atm_force_charge(priv->lecd, skb->truesize);
- sk = sk_atm(priv->lecd);
+ rcu_read_lock();
+ vcc = rcu_dereference(priv->lecd);
+ if (!vcc) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return -1;
+ }
+
+ atm_force_charge(vcc, skb->truesize);
+ sk = sk_atm(vcc);
skb_queue_tail(&sk->sk_receive_queue, skb);
sk->sk_data_ready(sk);
if (data != NULL) {
pr_debug("about to send %d bytes of data\n", data->len);
- atm_force_charge(priv->lecd, data->truesize);
+ atm_force_charge(vcc, data->truesize);
skb_queue_tail(&sk->sk_receive_queue, data);
sk->sk_data_ready(sk);
}
+ rcu_read_unlock();
return 0;
}
@@ -618,7 +640,7 @@ static void lec_push(struct atm_vcc *vcc, struct sk_buff *skb)
atm_return(vcc, skb->truesize);
if (*(__be16 *) skb->data == htons(priv->lecid) ||
- !priv->lecd || !(dev->flags & IFF_UP)) {
+ !rcu_access_pointer(priv->lecd) || !(dev->flags & IFF_UP)) {
/*
* Probably looping back, or if lecd is missing,
* lecd has gone down
@@ -753,12 +775,12 @@ static int lecd_attach(struct atm_vcc *vcc, int arg)
priv = netdev_priv(dev_lec[i]);
} else {
priv = netdev_priv(dev_lec[i]);
- if (priv->lecd)
+ if (rcu_access_pointer(priv->lecd))
return -EADDRINUSE;
}
lec_arp_init(priv);
priv->itfnum = i; /* LANE2 addition */
- priv->lecd = vcc;
+ rcu_assign_pointer(priv->lecd, vcc);
vcc->dev = &lecatm_dev;
vcc_insert_socket(sk_atm(vcc));
diff --git a/net/atm/lec.h b/net/atm/lec.h
index be0e2667bd8c..ec85709bf818 100644
--- a/net/atm/lec.h
+++ b/net/atm/lec.h
@@ -91,7 +91,7 @@ struct lec_priv {
*/
spinlock_t lec_arp_lock;
struct atm_vcc *mcast_vcc; /* Default Multicast Send VCC */
- struct atm_vcc *lecd;
+ struct atm_vcc __rcu *lecd;
struct delayed_work lec_arp_work; /* C10 */
unsigned int maximum_unknown_frame_count;
/*
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 0f512c2c2fd3..a966d36d0e79 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1843,9 +1843,13 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data)
u8 aux_num_cis = 0;
u8 cis_id;
+ hci_dev_lock(hdev);
+
conn = hci_conn_hash_lookup_cig(hdev, cig_id);
- if (!conn)
+ if (!conn) {
+ hci_dev_unlock(hdev);
return 0;
+ }
qos = &conn->iso_qos;
pdu->cig_id = cig_id;
@@ -1884,6 +1888,8 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data)
}
pdu->num_cis = aux_num_cis;
+ hci_dev_unlock(hdev);
+
if (!pdu->num_cis)
return 0;
@@ -2958,6 +2964,111 @@ u32 hci_conn_get_phy(struct hci_conn *conn)
return phys;
}
+static u16 bt_phy_pkt_type(struct hci_conn *conn, u32 phys)
+{
+ u16 pkt_type = conn->pkt_type;
+
+ if (phys & BT_PHY_BR_1M_3SLOT)
+ pkt_type |= HCI_DM3 | HCI_DH3;
+ else
+ pkt_type &= ~(HCI_DM3 | HCI_DH3);
+
+ if (phys & BT_PHY_BR_1M_5SLOT)
+ pkt_type |= HCI_DM5 | HCI_DH5;
+ else
+ pkt_type &= ~(HCI_DM5 | HCI_DH5);
+
+ if (phys & BT_PHY_EDR_2M_1SLOT)
+ pkt_type &= ~HCI_2DH1;
+ else
+ pkt_type |= HCI_2DH1;
+
+ if (phys & BT_PHY_EDR_2M_3SLOT)
+ pkt_type &= ~HCI_2DH3;
+ else
+ pkt_type |= HCI_2DH3;
+
+ if (phys & BT_PHY_EDR_2M_5SLOT)
+ pkt_type &= ~HCI_2DH5;
+ else
+ pkt_type |= HCI_2DH5;
+
+ if (phys & BT_PHY_EDR_3M_1SLOT)
+ pkt_type &= ~HCI_3DH1;
+ else
+ pkt_type |= HCI_3DH1;
+
+ if (phys & BT_PHY_EDR_3M_3SLOT)
+ pkt_type &= ~HCI_3DH3;
+ else
+ pkt_type |= HCI_3DH3;
+
+ if (phys & BT_PHY_EDR_3M_5SLOT)
+ pkt_type &= ~HCI_3DH5;
+ else
+ pkt_type |= HCI_3DH5;
+
+ return pkt_type;
+}
+
+static int bt_phy_le_phy(u32 phys, u8 *tx_phys, u8 *rx_phys)
+{
+ if (!tx_phys || !rx_phys)
+ return -EINVAL;
+
+ *tx_phys = 0;
+ *rx_phys = 0;
+
+ if (phys & BT_PHY_LE_1M_TX)
+ *tx_phys |= HCI_LE_SET_PHY_1M;
+
+ if (phys & BT_PHY_LE_1M_RX)
+ *rx_phys |= HCI_LE_SET_PHY_1M;
+
+ if (phys & BT_PHY_LE_2M_TX)
+ *tx_phys |= HCI_LE_SET_PHY_2M;
+
+ if (phys & BT_PHY_LE_2M_RX)
+ *rx_phys |= HCI_LE_SET_PHY_2M;
+
+ if (phys & BT_PHY_LE_CODED_TX)
+ *tx_phys |= HCI_LE_SET_PHY_CODED;
+
+ if (phys & BT_PHY_LE_CODED_RX)
+ *rx_phys |= HCI_LE_SET_PHY_CODED;
+
+ return 0;
+}
+
+int hci_conn_set_phy(struct hci_conn *conn, u32 phys)
+{
+ u8 tx_phys, rx_phys;
+
+ switch (conn->type) {
+ case SCO_LINK:
+ case ESCO_LINK:
+ return -EINVAL;
+ case ACL_LINK:
+ /* Only allow setting BR/EDR PHYs if link type is ACL */
+ if (phys & ~BT_PHY_BREDR_MASK)
+ return -EINVAL;
+
+ return hci_acl_change_pkt_type(conn,
+ bt_phy_pkt_type(conn, phys));
+ case LE_LINK:
+ /* Only allow setting LE PHYs if link type is LE */
+ if (phys & ~BT_PHY_LE_MASK)
+ return -EINVAL;
+
+ if (bt_phy_le_phy(phys, &tx_phys, &rx_phys))
+ return -EINVAL;
+
+ return hci_le_set_phy(conn, tx_phys, rx_phys);
+ default:
+ return -EINVAL;
+ }
+}
+
static int abort_conn_sync(struct hci_dev *hdev, void *data)
{
struct hci_conn *conn = data;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 58075bf72055..90c5b67eeff1 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -80,6 +80,10 @@ static void *hci_le_ev_skb_pull(struct hci_dev *hdev, struct sk_buff *skb,
return data;
}
+static void hci_store_wake_reason(struct hci_dev *hdev,
+ const bdaddr_t *bdaddr, u8 addr_type)
+ __must_hold(&hdev->lock);
+
static u8 hci_cc_inquiry_cancel(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
@@ -2869,6 +2873,31 @@ static void hci_cs_le_ext_create_conn(struct hci_dev *hdev, u8 status)
hci_dev_unlock(hdev);
}
+static void hci_cs_le_set_phy(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_le_set_phy *cp;
+ struct hci_conn *conn;
+
+ bt_dev_dbg(hdev, "status 0x%2.2x", status);
+
+ if (status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PHY);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (conn) {
+ conn->le_tx_def_phys = cp->tx_phys;
+ conn->le_rx_def_phys = cp->rx_phys;
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status)
{
struct hci_cp_le_read_remote_features *cp;
@@ -3086,6 +3115,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
bt_dev_dbg(hdev, "status 0x%2.2x", status);
hci_dev_lock(hdev);
+ hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR);
/* Check for existing connection:
*
@@ -3249,6 +3279,10 @@ static void hci_conn_request_evt(struct hci_dev *hdev, void *data,
bt_dev_dbg(hdev, "bdaddr %pMR type 0x%x", &ev->bdaddr, ev->link_type);
+ hci_dev_lock(hdev);
+ hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR);
+ hci_dev_unlock(hdev);
+
/* Reject incoming connection from device with same BD ADDR against
* CVE-2020-26555
*/
@@ -4359,6 +4393,7 @@ static const struct hci_cs {
HCI_CS(HCI_OP_LE_CREATE_CONN, hci_cs_le_create_conn),
HCI_CS(HCI_OP_LE_READ_REMOTE_FEATURES, hci_cs_le_read_remote_features),
HCI_CS(HCI_OP_LE_START_ENC, hci_cs_le_start_enc),
+ HCI_CS(HCI_OP_LE_SET_PHY, hci_cs_le_set_phy),
HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn),
HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis),
HCI_CS(HCI_OP_LE_CREATE_BIG, hci_cs_le_create_big),
@@ -4995,6 +5030,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
bt_dev_dbg(hdev, "status 0x%2.2x", status);
hci_dev_lock(hdev);
+ hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR);
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
if (!conn) {
@@ -5687,6 +5723,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
int err;
hci_dev_lock(hdev);
+ hci_store_wake_reason(hdev, bdaddr, bdaddr_type);
/* All controllers implicitly stop advertising in the event of a
* connection, so ensure that the state bit is cleared.
@@ -5979,6 +6016,7 @@ static void hci_le_past_received_evt(struct hci_dev *hdev, void *data,
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
hci_dev_lock(hdev);
+ hci_store_wake_reason(hdev, &ev->bdaddr, ev->bdaddr_type);
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
@@ -6377,6 +6415,8 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, void *data,
info->length + 1))
break;
+ hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type);
+
if (info->length <= max_adv_len(hdev)) {
rssi = info->data[info->length];
process_adv_report(hdev, info->type, &info->bdaddr,
@@ -6465,6 +6505,8 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data,
info->length))
break;
+ hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type);
+
evt_type = __le16_to_cpu(info->type) & LE_EXT_ADV_EVT_TYPE_MASK;
legacy_evt_type = ext_evt_type_to_legacy(hdev, evt_type);
@@ -6510,6 +6552,7 @@ static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
hci_dev_lock(hdev);
+ hci_store_wake_reason(hdev, &ev->bdaddr, ev->bdaddr_type);
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
@@ -6741,25 +6784,31 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data,
latency = le16_to_cpu(ev->latency);
timeout = le16_to_cpu(ev->timeout);
+ hci_dev_lock(hdev);
+
hcon = hci_conn_hash_lookup_handle(hdev, handle);
- if (!hcon || hcon->state != BT_CONNECTED)
- return send_conn_param_neg_reply(hdev, handle,
- HCI_ERROR_UNKNOWN_CONN_ID);
+ if (!hcon || hcon->state != BT_CONNECTED) {
+ send_conn_param_neg_reply(hdev, handle,
+ HCI_ERROR_UNKNOWN_CONN_ID);
+ goto unlock;
+ }
- if (max > hcon->le_conn_max_interval)
- return send_conn_param_neg_reply(hdev, handle,
- HCI_ERROR_INVALID_LL_PARAMS);
+ if (max > hcon->le_conn_max_interval) {
+ send_conn_param_neg_reply(hdev, handle,
+ HCI_ERROR_INVALID_LL_PARAMS);
+ goto unlock;
+ }
- if (hci_check_conn_params(min, max, latency, timeout))
- return send_conn_param_neg_reply(hdev, handle,
- HCI_ERROR_INVALID_LL_PARAMS);
+ if (hci_check_conn_params(min, max, latency, timeout)) {
+ send_conn_param_neg_reply(hdev, handle,
+ HCI_ERROR_INVALID_LL_PARAMS);
+ goto unlock;
+ }
if (hcon->role == HCI_ROLE_MASTER) {
struct hci_conn_params *params;
u8 store_hint;
- hci_dev_lock(hdev);
-
params = hci_conn_params_lookup(hdev, &hcon->dst,
hcon->dst_type);
if (params) {
@@ -6772,8 +6821,6 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data,
store_hint = 0x00;
}
- hci_dev_unlock(hdev);
-
mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type,
store_hint, min, max, latency, timeout);
}
@@ -6787,6 +6834,9 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data,
cp.max_ce_len = 0;
hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(cp), &cp);
+
+unlock:
+ hci_dev_unlock(hdev);
}
static void hci_le_direct_adv_report_evt(struct hci_dev *hdev, void *data,
@@ -6808,6 +6858,8 @@ static void hci_le_direct_adv_report_evt(struct hci_dev *hdev, void *data,
for (i = 0; i < ev->num; i++) {
struct hci_ev_le_direct_adv_info *info = &ev->info[i];
+ hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type);
+
process_adv_report(hdev, info->type, &info->bdaddr,
info->bdaddr_type, &info->direct_addr,
info->direct_addr_type, HCI_ADV_PHY_1M, 0,
@@ -7476,73 +7528,29 @@ static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
return true;
}
-static void hci_store_wake_reason(struct hci_dev *hdev, u8 event,
- struct sk_buff *skb)
+static void hci_store_wake_reason(struct hci_dev *hdev,
+ const bdaddr_t *bdaddr, u8 addr_type)
+ __must_hold(&hdev->lock)
{
- struct hci_ev_le_advertising_info *adv;
- struct hci_ev_le_direct_adv_info *direct_adv;
- struct hci_ev_le_ext_adv_info *ext_adv;
- const struct hci_ev_conn_complete *conn_complete = (void *)skb->data;
- const struct hci_ev_conn_request *conn_request = (void *)skb->data;
-
- hci_dev_lock(hdev);
+ lockdep_assert_held(&hdev->lock);
/* If we are currently suspended and this is the first BT event seen,
* save the wake reason associated with the event.
*/
if (!hdev->suspended || hdev->wake_reason)
- goto unlock;
+ return;
+
+ if (!bdaddr) {
+ hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED;
+ return;
+ }
/* Default to remote wake. Values for wake_reason are documented in the
* Bluez mgmt api docs.
*/
hdev->wake_reason = MGMT_WAKE_REASON_REMOTE_WAKE;
-
- /* Once configured for remote wakeup, we should only wake up for
- * reconnections. It's useful to see which device is waking us up so
- * keep track of the bdaddr of the connection event that woke us up.
- */
- if (event == HCI_EV_CONN_REQUEST) {
- bacpy(&hdev->wake_addr, &conn_request->bdaddr);
- hdev->wake_addr_type = BDADDR_BREDR;
- } else if (event == HCI_EV_CONN_COMPLETE) {
- bacpy(&hdev->wake_addr, &conn_complete->bdaddr);
- hdev->wake_addr_type = BDADDR_BREDR;
- } else if (event == HCI_EV_LE_META) {
- struct hci_ev_le_meta *le_ev = (void *)skb->data;
- u8 subevent = le_ev->subevent;
- u8 *ptr = &skb->data[sizeof(*le_ev)];
- u8 num_reports = *ptr;
-
- if ((subevent == HCI_EV_LE_ADVERTISING_REPORT ||
- subevent == HCI_EV_LE_DIRECT_ADV_REPORT ||
- subevent == HCI_EV_LE_EXT_ADV_REPORT) &&
- num_reports) {
- adv = (void *)(ptr + 1);
- direct_adv = (void *)(ptr + 1);
- ext_adv = (void *)(ptr + 1);
-
- switch (subevent) {
- case HCI_EV_LE_ADVERTISING_REPORT:
- bacpy(&hdev->wake_addr, &adv->bdaddr);
- hdev->wake_addr_type = adv->bdaddr_type;
- break;
- case HCI_EV_LE_DIRECT_ADV_REPORT:
- bacpy(&hdev->wake_addr, &direct_adv->bdaddr);
- hdev->wake_addr_type = direct_adv->bdaddr_type;
- break;
- case HCI_EV_LE_EXT_ADV_REPORT:
- bacpy(&hdev->wake_addr, &ext_adv->bdaddr);
- hdev->wake_addr_type = ext_adv->bdaddr_type;
- break;
- }
- }
- } else {
- hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED;
- }
-
-unlock:
- hci_dev_unlock(hdev);
+ bacpy(&hdev->wake_addr, bdaddr);
+ hdev->wake_addr_type = addr_type;
}
#define HCI_EV_VL(_op, _func, _min_len, _max_len) \
@@ -7789,14 +7797,15 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
skb_pull(skb, HCI_EVENT_HDR_SIZE);
- /* Store wake reason if we're suspended */
- hci_store_wake_reason(hdev, event, skb);
-
bt_dev_dbg(hdev, "event 0x%2.2x", event);
hci_event_func(hdev, event, skb, &opcode, &status, &req_complete,
&req_complete_skb);
+ hci_dev_lock(hdev);
+ hci_store_wake_reason(hdev, NULL, 0);
+ hci_dev_unlock(hdev);
+
if (req_complete) {
req_complete(hdev, status, opcode);
} else if (req_complete_skb) {
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 43b36581e336..d638e62f3002 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -780,7 +780,7 @@ int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy)
{
if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
- return 0;
+ return -EEXIST;
return hci_cmd_sync_queue(hdev, func, data, destroy);
}
@@ -801,8 +801,15 @@ int hci_cmd_sync_run(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
return -ENETDOWN;
/* If on cmd_sync_work then run immediately otherwise queue */
- if (current_work() == &hdev->cmd_sync_work)
- return func(hdev, data);
+ if (current_work() == &hdev->cmd_sync_work) {
+ int err;
+
+ err = func(hdev, data);
+ if (destroy)
+ destroy(hdev, data, err);
+
+ return 0;
+ }
return hci_cmd_sync_submit(hdev, func, data, destroy);
}
@@ -3255,6 +3262,8 @@ static int update_passive_scan_sync(struct hci_dev *hdev, void *data)
int hci_update_passive_scan(struct hci_dev *hdev)
{
+ int err;
+
/* Only queue if it would have any effect */
if (!test_bit(HCI_UP, &hdev->flags) ||
test_bit(HCI_INIT, &hdev->flags) ||
@@ -3264,8 +3273,9 @@ int hci_update_passive_scan(struct hci_dev *hdev)
hci_dev_test_flag(hdev, HCI_UNREGISTER))
return 0;
- return hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL,
- NULL);
+ err = hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL,
+ NULL);
+ return (err == -EEXIST) ? 0 : err;
}
int hci_write_sc_support_sync(struct hci_dev *hdev, u8 val)
@@ -6927,8 +6937,11 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
- return hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
- NULL);
+ int err;
+
+ err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
+ NULL);
+ return (err == -EEXIST) ? 0 : err;
}
static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
@@ -6964,8 +6977,11 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
- return hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
- create_le_conn_complete);
+ int err;
+
+ err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
+ create_le_conn_complete);
+ return (err == -EEXIST) ? 0 : err;
}
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
@@ -7172,8 +7188,11 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
- return hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
- create_pa_complete);
+ int err;
+
+ err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
+ create_pa_complete);
+ return (err == -EEXIST) ? 0 : err;
}
static void create_big_complete(struct hci_dev *hdev, void *data, int err)
@@ -7191,7 +7210,8 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
{
- DEFINE_FLEX(struct hci_cp_le_big_create_sync, cp, bis, num_bis, 0x11);
+ DEFINE_FLEX(struct hci_cp_le_big_create_sync, cp, bis, num_bis,
+ HCI_MAX_ISO_BIS);
struct hci_conn *conn = data;
struct bt_iso_qos *qos = &conn->iso_qos;
int err;
@@ -7235,8 +7255,11 @@ static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
- return hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
- create_big_complete);
+ int err;
+
+ err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
+ create_big_complete);
+ return (err == -EEXIST) ? 0 : err;
}
struct past_data {
@@ -7328,7 +7351,7 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
if (err)
kfree(data);
- return err;
+ return (err == -EEXIST) ? 0 : err;
}
static void le_read_features_complete(struct hci_dev *hdev, void *data, int err)
@@ -7337,10 +7360,8 @@ static void le_read_features_complete(struct hci_dev *hdev, void *data, int err)
bt_dev_dbg(hdev, "err %d", err);
- if (err == -ECANCELED)
- return;
-
hci_conn_drop(conn);
+ hci_conn_put(conn);
}
static int hci_le_read_all_remote_features_sync(struct hci_dev *hdev,
@@ -7407,13 +7428,100 @@ int hci_le_read_remote_features(struct hci_conn *conn)
* role is possible. Otherwise just transition into the
* connected state without requesting the remote features.
*/
- if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES))
+ if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) {
err = hci_cmd_sync_queue_once(hdev,
hci_le_read_remote_features_sync,
- hci_conn_hold(conn),
+ hci_conn_hold(hci_conn_get(conn)),
le_read_features_complete);
- else
+ if (err) {
+ hci_conn_drop(conn);
+ hci_conn_put(conn);
+ }
+ } else {
err = -EOPNOTSUPP;
+ }
- return err;
+ return (err == -EEXIST) ? 0 : err;
+}
+
+static void pkt_type_changed(struct hci_dev *hdev, void *data, int err)
+{
+ struct hci_cp_change_conn_ptype *cp = data;
+
+ bt_dev_dbg(hdev, "err %d", err);
+
+ kfree(cp);
+}
+
+static int hci_change_conn_ptype_sync(struct hci_dev *hdev, void *data)
+{
+ struct hci_cp_change_conn_ptype *cp = data;
+
+ return __hci_cmd_sync_status_sk(hdev, HCI_OP_CHANGE_CONN_PTYPE,
+ sizeof(*cp), cp,
+ HCI_EV_PKT_TYPE_CHANGE,
+ HCI_CMD_TIMEOUT, NULL);
+}
+
+int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_change_conn_ptype *cp;
+ int err;
+
+ cp = kmalloc(sizeof(*cp), GFP_KERNEL);
+ if (!cp)
+ return -ENOMEM;
+
+ cp->handle = cpu_to_le16(conn->handle);
+ cp->pkt_type = cpu_to_le16(pkt_type);
+
+ err = hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp,
+ pkt_type_changed);
+ if (err)
+ kfree(cp);
+
+ return (err == -EEXIST) ? 0 : err;
+}
+
+static void le_phy_update_complete(struct hci_dev *hdev, void *data, int err)
+{
+ struct hci_cp_le_set_phy *cp = data;
+
+ bt_dev_dbg(hdev, "err %d", err);
+
+ kfree(cp);
+}
+
+static int hci_le_set_phy_sync(struct hci_dev *hdev, void *data)
+{
+ struct hci_cp_le_set_phy *cp = data;
+
+ return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_SET_PHY,
+ sizeof(*cp), cp,
+ HCI_EV_LE_PHY_UPDATE_COMPLETE,
+ HCI_CMD_TIMEOUT, NULL);
+}
+
+int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_set_phy *cp;
+ int err;
+
+ cp = kmalloc(sizeof(*cp), GFP_KERNEL);
+ if (!cp)
+ return -ENOMEM;
+
+ memset(cp, 0, sizeof(*cp));
+ cp->handle = cpu_to_le16(conn->handle);
+ cp->tx_phys = tx_phys;
+ cp->rx_phys = rx_phys;
+
+ err = hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp,
+ le_phy_update_complete);
+ if (err)
+ kfree(cp);
+
+ return (err == -EEXIST) ? 0 : err;
}
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index f1131e4415c9..e8106d09f2a4 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -885,7 +885,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
struct bt_power pwr;
struct l2cap_conn *conn;
int err = 0;
- u32 opt;
+ u32 opt, phys;
u16 mtu;
u8 mode;
@@ -1066,6 +1066,24 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
+ case BT_PHY:
+ if (sk->sk_state != BT_CONNECTED) {
+ err = -ENOTCONN;
+ break;
+ }
+
+ err = copy_safe_from_sockptr(&phys, sizeof(phys), optval,
+ optlen);
+ if (err)
+ break;
+
+ if (!chan->conn)
+ break;
+
+ conn = chan->conn;
+ err = hci_conn_set_phy(conn->hcon, phys);
+ break;
+
case BT_MODE:
if (!enable_ecred) {
err = -ENOPROTOOPT;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index f3da1bc38a55..86fd2009de0d 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2478,6 +2478,7 @@ static int mesh_send(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
struct mgmt_mesh_tx *mesh_tx;
struct mgmt_cp_mesh_send *send = data;
struct mgmt_rp_mesh_read_features rp;
+ u16 expected_len;
bool sending;
int err = 0;
@@ -2485,12 +2486,19 @@ static int mesh_send(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
!hci_dev_test_flag(hdev, HCI_MESH_EXPERIMENTAL))
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
MGMT_STATUS_NOT_SUPPORTED);
- if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) ||
- len <= MGMT_MESH_SEND_SIZE ||
- len > (MGMT_MESH_SEND_SIZE + 31))
+ if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
MGMT_STATUS_REJECTED);
+ if (!send->adv_data_len || send->adv_data_len > 31)
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
+ MGMT_STATUS_REJECTED);
+
+ expected_len = struct_size(send, adv_data, send->adv_data_len);
+ if (expected_len != len)
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
+ MGMT_STATUS_INVALID_PARAMS);
+
hci_dev_lock(hdev);
memset(&rp, 0, sizeof(rp));
@@ -7248,6 +7256,9 @@ static bool ltk_is_valid(struct mgmt_ltk_info *key)
if (key->initiator != 0x00 && key->initiator != 0x01)
return false;
+ if (key->enc_size > sizeof(key->val))
+ return false;
+
switch (key->addr.type) {
case BDADDR_LE_PUBLIC:
return true;
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 6741b067d28b..a446844354a1 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -298,7 +298,7 @@ static int sco_chan_add(struct sco_conn *conn, struct sock *sk,
int err = 0;
sco_conn_lock(conn);
- if (conn->sk)
+ if (conn->sk || sco_pi(sk)->conn)
err = -EBUSY;
else
__sco_chan_add(conn, sk, parent);
@@ -353,9 +353,20 @@ static int sco_connect(struct sock *sk)
lock_sock(sk);
+ /* Recheck state after reacquiring the socket lock, as another
+ * thread may have changed it (e.g., closed the socket).
+ */
+ if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) {
+ release_sock(sk);
+ hci_conn_drop(hcon);
+ err = -EBADFD;
+ goto unlock;
+ }
+
err = sco_chan_add(conn, sk, NULL);
if (err) {
release_sock(sk);
+ hci_conn_drop(hcon);
goto unlock;
}
@@ -656,13 +667,18 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr_unsized *addr,
addr->sa_family != AF_BLUETOOTH)
return -EINVAL;
- if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND)
+ lock_sock(sk);
+
+ if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) {
+ release_sock(sk);
return -EBADFD;
+ }
- if (sk->sk_type != SOCK_SEQPACKET)
- err = -EINVAL;
+ if (sk->sk_type != SOCK_SEQPACKET) {
+ release_sock(sk);
+ return -EINVAL;
+ }
- lock_sock(sk);
/* Set destination address and psm */
bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr);
release_sock(sk);
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 9d9604074589..abf3ab7479ff 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -1018,10 +1018,7 @@ static u8 smp_random(struct smp_chan *smp)
smp_s1(smp->tk, smp->prnd, smp->rrnd, stk);
- if (hcon->pending_sec_level == BT_SECURITY_HIGH)
- auth = 1;
- else
- auth = 0;
+ auth = test_bit(SMP_FLAG_MITM_AUTH, &smp->flags) ? 1 : 0;
/* Even though there's no _RESPONDER suffix this is the
* responder STK we're adding for later lookup (the initiator
@@ -1826,7 +1823,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (sec_level > conn->hcon->pending_sec_level)
conn->hcon->pending_sec_level = sec_level;
- /* If we need MITM check that it can be achieved */
+ /* If we need MITM check that it can be achieved. */
if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
u8 method;
@@ -1834,6 +1831,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
req->io_capability);
if (method == JUST_WORKS || method == JUST_CFM)
return SMP_AUTH_REQUIREMENTS;
+
+ /* Force MITM bit if it isn't set by the initiator. */
+ auth |= SMP_AUTH_MITM;
+ rsp.auth_req |= SMP_AUTH_MITM;
}
key_size = min(req->max_key_size, rsp.max_key_size);
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
index 1e2b51769eec..6b5595868a39 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -251,12 +251,12 @@ struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *msg)
static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
struct sk_buff *request, struct neighbour *n,
- __be16 vlan_proto, u16 vlan_tci, struct nd_msg *ns)
+ __be16 vlan_proto, u16 vlan_tci)
{
struct net_device *dev = request->dev;
struct net_bridge_vlan_group *vg;
+ struct nd_msg *na, *ns;
struct sk_buff *reply;
- struct nd_msg *na;
struct ipv6hdr *pip6;
int na_olen = 8; /* opt hdr + ETH_ALEN for target */
int ns_olen;
@@ -264,7 +264,7 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
u8 *daddr;
u16 pvid;
- if (!dev)
+ if (!dev || skb_linearize(request))
return;
len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) +
@@ -281,17 +281,21 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
skb_set_mac_header(reply, 0);
daddr = eth_hdr(request)->h_source;
+ ns = (struct nd_msg *)(skb_network_header(request) +
+ sizeof(struct ipv6hdr));
/* Do we need option processing ? */
ns_olen = request->len - (skb_network_offset(request) +
sizeof(struct ipv6hdr)) - sizeof(*ns);
for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) {
- if (!ns->opt[i + 1]) {
+ if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) {
kfree_skb(reply);
return;
}
if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
- daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
+ if ((ns->opt[i + 1] << 3) >=
+ sizeof(struct nd_opt_hdr) + ETH_ALEN)
+ daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
break;
}
}
@@ -472,9 +476,9 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
if (vid != 0)
br_nd_send(br, p, skb, n,
skb->vlan_proto,
- skb_vlan_tag_get(skb), msg);
+ skb_vlan_tag_get(skb));
else
- br_nd_send(br, p, skb, n, 0, 0, msg);
+ br_nd_send(br, p, skb, n, 0, 0);
replied = true;
}
diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c
index ce6f63c77cc0..86f0e75d6e34 100644
--- a/net/bridge/br_mrp_netlink.c
+++ b/net/bridge/br_mrp_netlink.c
@@ -196,7 +196,7 @@ static const struct nla_policy
br_mrp_start_test_policy[IFLA_BRIDGE_MRP_START_TEST_MAX + 1] = {
[IFLA_BRIDGE_MRP_START_TEST_UNSPEC] = { .type = NLA_REJECT },
[IFLA_BRIDGE_MRP_START_TEST_RING_ID] = { .type = NLA_U32 },
- [IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = NLA_POLICY_MIN(NLA_U32, 1),
[IFLA_BRIDGE_MRP_START_TEST_MAX_MISS] = { .type = NLA_U32 },
[IFLA_BRIDGE_MRP_START_TEST_PERIOD] = { .type = NLA_U32 },
[IFLA_BRIDGE_MRP_START_TEST_MONITOR] = { .type = NLA_U32 },
@@ -316,7 +316,7 @@ static const struct nla_policy
br_mrp_start_in_test_policy[IFLA_BRIDGE_MRP_START_IN_TEST_MAX + 1] = {
[IFLA_BRIDGE_MRP_START_IN_TEST_UNSPEC] = { .type = NLA_REJECT },
[IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID] = { .type = NLA_U32 },
- [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = { .type = NLA_U32 },
+ [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = NLA_POLICY_MIN(NLA_U32, 1),
[IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS] = { .type = NLA_U32 },
[IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD] = { .type = NLA_U32 },
};
diff --git a/net/core/dev.c b/net/core/dev.c
index 9fa068095124..384250c3a519 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3763,6 +3763,22 @@ 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 netdev_features_t gso_features_check(const struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features)
@@ -3799,22 +3815,23 @@ static netdev_features_t gso_features_check(const struct sk_buff *skb,
* segmentation-offloads.rst).
*/
if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) {
- struct iphdr *iph = skb->encapsulation ?
- inner_ip_hdr(skb) : ip_hdr(skb);
+ const struct iphdr *iph;
+ struct iphdr _iph;
+ int nhoff = skb->encapsulation ?
+ skb_inner_network_offset(skb) :
+ skb_network_offset(skb);
- if (!(iph->frag_off & htons(IP_DF)))
- features &= ~NETIF_F_TSO_MANGLEID;
+ iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
+
+ if (!iph || !(iph->frag_off & htons(IP_DF)))
+ features &= ~dev->mangleid_features;
}
/* NETIF_F_IPV6_CSUM does not support IPv6 extension headers,
* so neither does TSO that depends on it.
*/
if (features & NETIF_F_IPV6_CSUM &&
- (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) &&
+ skb_gso_has_extension_hdr(skb) &&
!ipv6_has_hopopt_jumbo(skb))
features &= ~(NETIF_F_IPV6_CSUM | NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4);
@@ -11399,6 +11416,9 @@ int register_netdevice(struct net_device *dev)
if (dev->hw_enc_features & NETIF_F_TSO)
dev->hw_enc_features |= NETIF_F_TSO_MANGLEID;
+ /* TSO_MANGLEID belongs in mangleid_features by definition */
+ dev->mangleid_features |= NETIF_F_TSO_MANGLEID;
+
/* Make NETIF_F_HIGHDMA inheritable to VLAN devices.
*/
dev->vlan_features |= NETIF_F_HIGHDMA;
diff --git a/net/core/skmsg.c b/net/core/skmsg.c
index 12fbb0545c71..35a6acbf9a57 100644
--- a/net/core/skmsg.c
+++ b/net/core/skmsg.c
@@ -1267,17 +1267,20 @@ static int sk_psock_verdict_recv(struct sock *sk, struct sk_buff *skb)
static void sk_psock_verdict_data_ready(struct sock *sk)
{
- struct socket *sock = sk->sk_socket;
- const struct proto_ops *ops;
+ const struct proto_ops *ops = NULL;
+ struct socket *sock;
int copied;
trace_sk_data_ready(sk);
- if (unlikely(!sock))
- return;
- ops = READ_ONCE(sock->ops);
+ rcu_read_lock();
+ sock = READ_ONCE(sk->sk_socket);
+ if (likely(sock))
+ ops = READ_ONCE(sock->ops);
+ rcu_read_unlock();
if (!ops || !ops->read_skb)
return;
+
copied = ops->read_skb(sk, sk_psock_verdict_recv);
if (copied >= 0) {
struct sk_psock *psock;
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
index d1bfc49b5f01..fd2fea25eff0 100644
--- a/net/hsr/hsr_device.c
+++ b/net/hsr/hsr_device.c
@@ -532,8 +532,8 @@ static void hsr_change_rx_flags(struct net_device *dev, int change)
static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev,
__be16 proto, u16 vid)
{
- bool is_slave_a_added = false;
- bool is_slave_b_added = false;
+ struct net_device *slave_a_dev = NULL;
+ struct net_device *slave_b_dev = NULL;
struct hsr_port *port;
struct hsr_priv *hsr;
int ret = 0;
@@ -549,33 +549,35 @@ static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev,
switch (port->type) {
case HSR_PT_SLAVE_A:
if (ret) {
- /* clean up Slave-B */
netdev_err(dev, "add vid failed for Slave-A\n");
- if (is_slave_b_added)
- vlan_vid_del(port->dev, proto, vid);
- return ret;
+ goto unwind;
}
-
- is_slave_a_added = true;
+ slave_a_dev = port->dev;
break;
-
case HSR_PT_SLAVE_B:
if (ret) {
- /* clean up Slave-A */
netdev_err(dev, "add vid failed for Slave-B\n");
- if (is_slave_a_added)
- vlan_vid_del(port->dev, proto, vid);
- return ret;
+ goto unwind;
}
-
- is_slave_b_added = true;
+ slave_b_dev = port->dev;
break;
default:
+ if (ret)
+ goto unwind;
break;
}
}
return 0;
+
+unwind:
+ if (slave_a_dev)
+ vlan_vid_del(slave_a_dev, proto, vid);
+
+ if (slave_b_dev)
+ vlan_vid_del(slave_b_dev, proto, vid);
+
+ return ret;
}
static int hsr_ndo_vlan_rx_kill_vid(struct net_device *dev,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 3dcfa4b3094a..272dd1a0acd0 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -3621,12 +3621,12 @@ static void addrconf_permanent_addr(struct net *net, struct net_device *dev)
if ((ifp->flags & IFA_F_PERMANENT) &&
fixup_permanent_addr(net, idev, ifp) < 0) {
write_unlock_bh(&idev->lock);
- in6_ifa_hold(ifp);
- ipv6_del_addr(ifp);
- write_lock_bh(&idev->lock);
net_info_ratelimited("%s: Failed to add prefix route for address %pI6c; dropping\n",
idev->dev->name, &ifp->addr);
+ in6_ifa_hold(ifp);
+ ipv6_del_addr(ifp);
+ write_lock_bh(&idev->lock);
}
}
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 83e03176819c..022069c7d6ed 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -762,6 +762,7 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
{
struct in6_pktinfo *src_info;
struct cmsghdr *cmsg;
+ struct ipv6_rt_hdr *orthdr;
struct ipv6_rt_hdr *rthdr;
struct ipv6_opt_hdr *hdr;
struct ipv6_txoptions *opt = ipc6->opt;
@@ -923,9 +924,13 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
goto exit_f;
}
if (cmsg->cmsg_type == IPV6_DSTOPTS) {
+ if (opt->dst1opt)
+ opt->opt_flen -= ipv6_optlen(opt->dst1opt);
opt->opt_flen += len;
opt->dst1opt = hdr;
} else {
+ if (opt->dst0opt)
+ opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
opt->opt_nflen += len;
opt->dst0opt = hdr;
}
@@ -968,12 +973,17 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
goto exit_f;
}
+ orthdr = opt->srcrt;
+ if (orthdr)
+ opt->opt_nflen -= ((orthdr->hdrlen + 1) << 3);
opt->opt_nflen += len;
opt->srcrt = rthdr;
if (cmsg->cmsg_type == IPV6_2292RTHDR && opt->dst1opt) {
int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3);
+ if (opt->dst0opt)
+ opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
opt->opt_nflen += dsthdrlen;
opt->dst0opt = opt->dst1opt;
opt->dst1opt = NULL;
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 0f41ca6f3d83..984475825271 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -871,6 +871,9 @@ int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type,
if (!skb2)
return 1;
+ /* Remove debris left by IPv4 stack. */
+ memset(IP6CB(skb2), 0, sizeof(*IP6CB(skb2)));
+
skb_dst_drop(skb2);
skb_pull(skb2, nhs);
skb_reset_network_header(skb2);
diff --git a/net/ipv6/ioam6.c b/net/ipv6/ioam6.c
index 08b7ac8c99b7..8db7f965696a 100644
--- a/net/ipv6/ioam6.c
+++ b/net/ipv6/ioam6.c
@@ -708,7 +708,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,
struct ioam6_namespace *ns,
struct ioam6_trace_hdr *trace,
struct ioam6_schema *sc,
- u8 sclen, bool is_input)
+ unsigned int sclen, bool is_input)
{
struct net_device *dev = skb_dst_dev(skb);
struct timespec64 ts;
@@ -939,7 +939,7 @@ void ioam6_fill_trace_data(struct sk_buff *skb,
bool is_input)
{
struct ioam6_schema *sc;
- u8 sclen = 0;
+ unsigned int sclen = 0;
/* Skip if Overflow flag is set
*/
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index 60d0be47a9f3..8aa29b3d3dac 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -133,11 +133,6 @@ static void fl_release(struct ip6_flowlabel *fl)
if (time_after(ttd, fl->expires))
fl->expires = ttd;
ttd = fl->expires;
- if (fl->opt && fl->share == IPV6_FL_S_EXCL) {
- struct ipv6_txoptions *opt = fl->opt;
- fl->opt = NULL;
- kfree(opt);
- }
if (!timer_pending(&ip6_fl_gc_timer) ||
time_after(ip6_fl_gc_timer.expires, ttd))
mod_timer(&ip6_fl_gc_timer, ttd);
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index c1f39735a236..9e2449db0bdf 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -601,11 +601,16 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (!skb2)
return 0;
+ /* Remove debris left by IPv6 stack. */
+ memset(IPCB(skb2), 0, sizeof(*IPCB(skb2)));
+
skb_dst_drop(skb2);
skb_pull(skb2, offset);
skb_reset_network_header(skb2);
eiph = ip_hdr(skb2);
+ if (eiph->version != 4 || eiph->ihl < 5)
+ goto out;
/* Try to guess incoming interface */
rt = ip_route_output_ports(dev_net(skb->dev), &fl4, NULL, eiph->saddr,
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index f6a5d8c73af9..186e60c79214 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1209,6 +1209,9 @@ static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
+ ndmsg->nduseropt_pad1 = 0;
+ ndmsg->nduseropt_pad2 = 0;
+ ndmsg->nduseropt_pad3 = 0;
memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index dbbfe2d6842f..1dca2fae05a5 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -1449,7 +1449,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
}
sta = sta_info_get(sdata, peer);
- if (!sta)
+ if (!sta || !sta->sta.tdls)
return -ENOLINK;
iee80211_tdls_recalc_chanctx(sdata, sta);
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index c57f10e2ef26..d77bbe496988 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -83,14 +83,30 @@ static struct mpls_route *mpls_route_input(struct net *net, unsigned int index)
return mpls_dereference(net, platform_label[index]);
}
+static struct mpls_route __rcu **mpls_platform_label_rcu(struct net *net, size_t *platform_labels)
+{
+ struct mpls_route __rcu **platform_label;
+ unsigned int sequence;
+
+ do {
+ sequence = read_seqcount_begin(&net->mpls.platform_label_seq);
+ platform_label = rcu_dereference(net->mpls.platform_label);
+ *platform_labels = net->mpls.platform_labels;
+ } while (read_seqcount_retry(&net->mpls.platform_label_seq, sequence));
+
+ return platform_label;
+}
+
static struct mpls_route *mpls_route_input_rcu(struct net *net, unsigned int index)
{
struct mpls_route __rcu **platform_label;
+ size_t platform_labels;
+
+ platform_label = mpls_platform_label_rcu(net, &platform_labels);
- if (index >= net->mpls.platform_labels)
+ if (index >= platform_labels)
return NULL;
- platform_label = rcu_dereference(net->mpls.platform_label);
return rcu_dereference(platform_label[index]);
}
@@ -2240,8 +2256,7 @@ static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb)
if (index < MPLS_LABEL_FIRST_UNRESERVED)
index = MPLS_LABEL_FIRST_UNRESERVED;
- platform_label = rcu_dereference(net->mpls.platform_label);
- platform_labels = net->mpls.platform_labels;
+ platform_label = mpls_platform_label_rcu(net, &platform_labels);
if (filter.filter_set)
flags |= NLM_F_DUMP_FILTERED;
@@ -2645,8 +2660,12 @@ static int resize_platform_label_table(struct net *net, size_t limit)
}
/* Update the global pointers */
+ local_bh_disable();
+ write_seqcount_begin(&net->mpls.platform_label_seq);
net->mpls.platform_labels = limit;
rcu_assign_pointer(net->mpls.platform_label, labels);
+ write_seqcount_end(&net->mpls.platform_label_seq);
+ local_bh_enable();
mutex_unlock(&net->mpls.platform_mutex);
@@ -2728,6 +2747,8 @@ static __net_init int mpls_net_init(struct net *net)
int i;
mutex_init(&net->mpls.platform_mutex);
+ seqcount_mutex_init(&net->mpls.platform_label_seq, &net->mpls.platform_mutex);
+
net->mpls.platform_labels = 0;
net->mpls.platform_label = NULL;
net->mpls.ip_ttl_propagate = 1;
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index bad9fc0f27d9..f1fa35cb8c00 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -1989,10 +1989,21 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
static void mptcp_rcv_space_adjust(struct mptcp_sock *msk, int copied);
+static void mptcp_eat_recv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ /* avoid the indirect call, we know the destructor is sock_rfree */
+ skb->destructor = NULL;
+ skb->sk = NULL;
+ atomic_sub(skb->truesize, &sk->sk_rmem_alloc);
+ sk_mem_uncharge(sk, skb->truesize);
+ __skb_unlink(skb, &sk->sk_receive_queue);
+ skb_attempt_defer_free(skb);
+}
+
static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
size_t len, int flags, int copied_total,
struct scm_timestamping_internal *tss,
- int *cmsg_flags)
+ int *cmsg_flags, struct sk_buff **last)
{
struct mptcp_sock *msk = mptcp_sk(sk);
struct sk_buff *skb, *tmp;
@@ -2009,6 +2020,7 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
/* skip already peeked skbs */
if (total_data_len + data_len <= copied_total) {
total_data_len += data_len;
+ *last = skb;
continue;
}
@@ -2043,13 +2055,9 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
break;
}
- /* avoid the indirect call, we know the destructor is sock_rfree */
- skb->destructor = NULL;
- skb->sk = NULL;
- atomic_sub(skb->truesize, &sk->sk_rmem_alloc);
- sk_mem_uncharge(sk, skb->truesize);
- __skb_unlink(skb, &sk->sk_receive_queue);
- skb_attempt_defer_free(skb);
+ mptcp_eat_recv_skb(sk, skb);
+ } else {
+ *last = skb;
}
if (copied >= len)
@@ -2264,10 +2272,12 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
cmsg_flags = MPTCP_CMSG_INQ;
while (copied < len) {
+ struct sk_buff *last = NULL;
int err, bytes_read;
bytes_read = __mptcp_recvmsg_mskq(sk, msg, len - copied, flags,
- copied, &tss, &cmsg_flags);
+ copied, &tss, &cmsg_flags,
+ &last);
if (unlikely(bytes_read < 0)) {
if (!copied)
copied = bytes_read;
@@ -2319,7 +2329,7 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
pr_debug("block timeout %ld\n", timeo);
mptcp_cleanup_rbuf(msk, copied);
- err = sk_wait_data(sk, &timeo, NULL);
+ err = sk_wait_data(sk, &timeo, last);
if (err < 0) {
err = copied ? : err;
goto out_err;
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index cc20e6d56807..a4e1d7951b2c 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -821,7 +821,7 @@ EXPORT_SYMBOL_GPL(ip_set_del);
*
*/
ip_set_id_t
-ip_set_get_byname(struct net *net, const char *name, struct ip_set **set)
+ip_set_get_byname(struct net *net, const struct nlattr *name, struct ip_set **set)
{
ip_set_id_t i, index = IPSET_INVALID_ID;
struct ip_set *s;
@@ -830,7 +830,7 @@ ip_set_get_byname(struct net *net, const char *name, struct ip_set **set)
rcu_read_lock();
for (i = 0; i < inst->ip_set_max; i++) {
s = rcu_dereference(inst->ip_set_list)[i];
- if (s && STRNCMP(s->name, name)) {
+ if (s && nla_strcmp(name, s->name) == 0) {
__ip_set_get(s);
index = i;
*set = s;
diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h
index 5e4453e9ef8e..4e56269efef2 100644
--- a/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/net/netfilter/ipset/ip_set_hash_gen.h
@@ -1099,7 +1099,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
if (!test_bit(i, n->used))
k++;
}
- if (n->pos == 0 && k == 0) {
+ if (k == n->pos) {
t->hregion[r].ext_size -= ext_size(n->size, dsize);
rcu_assign_pointer(hbucket(t, key), NULL);
kfree_rcu(n, rcu);
diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c
index 13c7a08aa868..34bb84d7b174 100644
--- a/net/netfilter/ipset/ip_set_list_set.c
+++ b/net/netfilter/ipset/ip_set_list_set.c
@@ -367,7 +367,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
ret = ip_set_get_extensions(set, tb, &ext);
if (ret)
return ret;
- e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s);
+ e.id = ip_set_get_byname(map->net, tb[IPSET_ATTR_NAME], &s);
if (e.id == IPSET_INVALID_ID)
return -IPSET_ERR_NAME;
/* "Loop detection" */
@@ -389,7 +389,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
if (tb[IPSET_ATTR_NAMEREF]) {
e.refid = ip_set_get_byname(map->net,
- nla_data(tb[IPSET_ATTR_NAMEREF]),
+ tb[IPSET_ATTR_NAMEREF],
&s);
if (e.refid == IPSET_INVALID_ID) {
ret = -IPSET_ERR_NAMEREF;
diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c
index a7552a46d6ac..4f39bf7c843f 100644
--- a/net/netfilter/nf_conntrack_broadcast.c
+++ b/net/netfilter/nf_conntrack_broadcast.c
@@ -21,6 +21,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
unsigned int timeout)
{
const struct nf_conntrack_helper *helper;
+ struct net *net = read_pnet(&ct->ct_net);
struct nf_conntrack_expect *exp;
struct iphdr *iph = ip_hdr(skb);
struct rtable *rt = skb_rtable(skb);
@@ -70,8 +71,11 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
exp->expectfn = NULL;
exp->flags = NF_CT_EXPECT_PERMANENT;
exp->class = NF_CT_EXPECT_CLASS_DEFAULT;
- exp->helper = NULL;
-
+ rcu_assign_pointer(exp->helper, helper);
+ write_pnet(&exp->net, net);
+#ifdef CONFIG_NF_CONNTRACK_ZONES
+ exp->zone = ct->zone;
+#endif
nf_ct_expect_related(exp, 0);
nf_ct_expect_put(exp);
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index 227fb5dc39e2..2234c444a320 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -112,8 +112,8 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple,
const struct net *net)
{
return nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
- net_eq(net, nf_ct_net(i->master)) &&
- nf_ct_zone_equal_any(i->master, zone);
+ net_eq(net, read_pnet(&i->net)) &&
+ nf_ct_exp_zone_equal_any(i, zone);
}
bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
@@ -309,12 +309,20 @@ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
}
EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
+/* This function can only be used from packet path, where accessing
+ * master's helper is safe, because the packet holds a reference on
+ * the conntrack object. Never use it from control plane.
+ */
void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
u_int8_t family,
const union nf_inet_addr *saddr,
const union nf_inet_addr *daddr,
u_int8_t proto, const __be16 *src, const __be16 *dst)
{
+ struct nf_conntrack_helper *helper = NULL;
+ struct nf_conn *ct = exp->master;
+ struct net *net = read_pnet(&ct->ct_net);
+ struct nf_conn_help *help;
int len;
if (family == AF_INET)
@@ -325,7 +333,16 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
exp->flags = 0;
exp->class = class;
exp->expectfn = NULL;
- exp->helper = NULL;
+
+ help = nfct_help(ct);
+ if (help)
+ helper = rcu_dereference(help->helper);
+
+ rcu_assign_pointer(exp->helper, helper);
+ write_pnet(&exp->net, net);
+#ifdef CONFIG_NF_CONNTRACK_ZONES
+ exp->zone = ct->zone;
+#endif
exp->tuple.src.l3num = family;
exp->tuple.dst.protonum = proto;
@@ -658,7 +675,7 @@ static int exp_seq_show(struct seq_file *s, void *v)
if (expect->flags & NF_CT_EXPECT_USERSPACE)
seq_printf(s, "%sUSERSPACE", delim);
- helper = rcu_dereference(nfct_help(expect->master)->helper);
+ helper = rcu_dereference(expect->helper);
if (helper) {
seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
if (helper->expect_policy[expect->class].name[0])
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
index e35814d68ce3..bd7e9e13e4f6 100644
--- a/net/netfilter/nf_conntrack_h323_main.c
+++ b/net/netfilter/nf_conntrack_h323_main.c
@@ -642,7 +642,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3,
&ct->tuplehash[!dir].tuple.dst.u3,
IPPROTO_TCP, NULL, &port);
- exp->helper = &nf_conntrack_helper_h245;
+ rcu_assign_pointer(exp->helper, &nf_conntrack_helper_h245);
nathook = rcu_dereference(nfct_h323_nat_hook);
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@@ -766,7 +766,7 @@ static int expect_callforwarding(struct sk_buff *skb,
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
- exp->helper = nf_conntrack_helper_q931;
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
nathook = rcu_dereference(nfct_h323_nat_hook);
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@@ -1233,7 +1233,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3 : NULL,
&ct->tuplehash[!dir].tuple.dst.u3,
IPPROTO_TCP, NULL, &port);
- exp->helper = nf_conntrack_helper_q931;
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple calls */
nathook = rcu_dereference(nfct_h323_nat_hook);
@@ -1305,7 +1305,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct,
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_UDP, NULL, &port);
- exp->helper = nf_conntrack_helper_ras;
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_ras);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect RAS ");
@@ -1522,7 +1522,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
exp->flags = NF_CT_EXPECT_PERMANENT;
- exp->helper = nf_conntrack_helper_q931;
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect Q.931 ");
@@ -1576,7 +1576,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
exp->flags = NF_CT_EXPECT_PERMANENT;
- exp->helper = nf_conntrack_helper_q931;
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect Q.931 ");
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index ceb48c3ca0a4..a715304a53d8 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -395,14 +395,10 @@ EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
{
- struct nf_conn_help *help = nfct_help(exp->master);
const struct nf_conntrack_helper *me = data;
const struct nf_conntrack_helper *this;
- if (exp->helper == me)
- return true;
-
- this = rcu_dereference_protected(help->helper,
+ this = rcu_dereference_protected(exp->helper,
lockdep_is_held(&nf_conntrack_expect_lock));
return this == me;
}
@@ -419,8 +415,13 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
*/
synchronize_rcu();
- nf_ct_expect_iterate_destroy(expect_iter_me, NULL);
+ nf_ct_expect_iterate_destroy(expect_iter_me, me);
nf_ct_iterate_destroy(unhelp, me);
+
+ /* nf_ct_iterate_destroy() does an unconditional synchronize_rcu() as
+ * last step, this ensures rcu readers of exp->helper are done.
+ * No need for another synchronize_rcu() here.
+ */
}
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index fea750653e96..2bb9eb2d25fb 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -2635,7 +2635,6 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
static struct nf_conntrack_expect *
ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct,
- struct nf_conntrack_helper *helper,
struct nf_conntrack_tuple *tuple,
struct nf_conntrack_tuple *mask);
@@ -2864,7 +2863,6 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
{
struct nlattr *cda[CTA_EXPECT_MAX+1];
struct nf_conntrack_tuple tuple, mask;
- struct nf_conntrack_helper *helper = NULL;
struct nf_conntrack_expect *exp;
int err;
@@ -2878,17 +2876,8 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct,
if (err < 0)
return err;
- if (cda[CTA_EXPECT_HELP_NAME]) {
- const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]);
-
- helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
- nf_ct_protonum(ct));
- if (helper == NULL)
- return -EOPNOTSUPP;
- }
-
exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct,
- helper, &tuple, &mask);
+ &tuple, &mask);
if (IS_ERR(exp))
return PTR_ERR(exp);
@@ -3005,7 +2994,7 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
{
struct nf_conn *master = exp->master;
long timeout = ((long)exp->timeout.expires - (long)jiffies) / HZ;
- struct nf_conn_help *help;
+ struct nf_conntrack_helper *helper;
#if IS_ENABLED(CONFIG_NF_NAT)
struct nlattr *nest_parms;
struct nf_conntrack_tuple nat_tuple = {};
@@ -3050,15 +3039,12 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
nla_put_be32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags)) ||
nla_put_be32(skb, CTA_EXPECT_CLASS, htonl(exp->class)))
goto nla_put_failure;
- help = nfct_help(master);
- if (help) {
- struct nf_conntrack_helper *helper;
- helper = rcu_dereference(help->helper);
- if (helper &&
- nla_put_string(skb, CTA_EXPECT_HELP_NAME, helper->name))
- goto nla_put_failure;
- }
+ helper = rcu_dereference(exp->helper);
+ if (helper &&
+ nla_put_string(skb, CTA_EXPECT_HELP_NAME, helper->name))
+ goto nla_put_failure;
+
expfn = nf_ct_helper_expectfn_find_by_symbol(exp->expectfn);
if (expfn != NULL &&
nla_put_string(skb, CTA_EXPECT_FN, expfn->name))
@@ -3387,12 +3373,9 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
static bool expect_iter_name(struct nf_conntrack_expect *exp, void *data)
{
struct nf_conntrack_helper *helper;
- const struct nf_conn_help *m_help;
const char *name = data;
- m_help = nfct_help(exp->master);
-
- helper = rcu_dereference(m_help->helper);
+ helper = rcu_dereference(exp->helper);
if (!helper)
return false;
@@ -3523,20 +3506,25 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr,
static struct nf_conntrack_expect *
ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
- struct nf_conntrack_helper *helper,
struct nf_conntrack_tuple *tuple,
struct nf_conntrack_tuple *mask)
{
- u_int32_t class = 0;
+ struct net *net = read_pnet(&ct->ct_net);
+ struct nf_conntrack_helper *helper;
struct nf_conntrack_expect *exp;
struct nf_conn_help *help;
+ u32 class = 0;
int err;
help = nfct_help(ct);
if (!help)
return ERR_PTR(-EOPNOTSUPP);
- if (cda[CTA_EXPECT_CLASS] && helper) {
+ helper = rcu_dereference(help->helper);
+ if (!helper)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (cda[CTA_EXPECT_CLASS]) {
class = ntohl(nla_get_be32(cda[CTA_EXPECT_CLASS]));
if (class > helper->expect_class_max)
return ERR_PTR(-EINVAL);
@@ -3566,7 +3554,11 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
exp->class = class;
exp->master = ct;
- exp->helper = helper;
+ write_pnet(&exp->net, net);
+#ifdef CONFIG_NF_CONNTRACK_ZONES
+ exp->zone = ct->zone;
+#endif
+ rcu_assign_pointer(exp->helper, helper);
exp->tuple = *tuple;
exp->mask.src.u3 = mask->src.u3;
exp->mask.src.u.all = mask->src.u.all;
@@ -3576,6 +3568,12 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
exp, nf_ct_l3num(ct));
if (err < 0)
goto err_out;
+#if IS_ENABLED(CONFIG_NF_NAT)
+ } else {
+ memset(&exp->saved_addr, 0, sizeof(exp->saved_addr));
+ memset(&exp->saved_proto, 0, sizeof(exp->saved_proto));
+ exp->dir = 0;
+#endif
}
return exp;
err_out:
@@ -3591,7 +3589,6 @@ ctnetlink_create_expect(struct net *net,
{
struct nf_conntrack_tuple tuple, mask, master_tuple;
struct nf_conntrack_tuple_hash *h = NULL;
- struct nf_conntrack_helper *helper = NULL;
struct nf_conntrack_expect *exp;
struct nf_conn *ct;
int err;
@@ -3617,33 +3614,7 @@ ctnetlink_create_expect(struct net *net,
ct = nf_ct_tuplehash_to_ctrack(h);
rcu_read_lock();
- if (cda[CTA_EXPECT_HELP_NAME]) {
- const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]);
-
- helper = __nf_conntrack_helper_find(helpname, u3,
- nf_ct_protonum(ct));
- if (helper == NULL) {
- rcu_read_unlock();
-#ifdef CONFIG_MODULES
- if (request_module("nfct-helper-%s", helpname) < 0) {
- err = -EOPNOTSUPP;
- goto err_ct;
- }
- rcu_read_lock();
- helper = __nf_conntrack_helper_find(helpname, u3,
- nf_ct_protonum(ct));
- if (helper) {
- err = -EAGAIN;
- goto err_rcu;
- }
- rcu_read_unlock();
-#endif
- err = -EOPNOTSUPP;
- goto err_ct;
- }
- }
-
- exp = ctnetlink_alloc_expect(cda, ct, helper, &tuple, &mask);
+ exp = ctnetlink_alloc_expect(cda, ct, &tuple, &mask);
if (IS_ERR(exp)) {
err = PTR_ERR(exp);
goto err_rcu;
@@ -3653,8 +3624,8 @@ ctnetlink_create_expect(struct net *net,
nf_ct_expect_put(exp);
err_rcu:
rcu_read_unlock();
-err_ct:
nf_ct_put(ct);
+
return err;
}
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 17af0ff4ea7a..939502ff7c87 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -924,7 +924,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
exp = __nf_ct_expect_find(net, nf_ct_zone(ct), &tuple);
if (!exp || exp->master == ct ||
- nfct_help(exp->master)->helper != nfct_help(ct)->helper ||
+ exp->helper != nfct_help(ct)->helper ||
exp->class != class)
break;
#if IS_ENABLED(CONFIG_NF_NAT)
@@ -1303,7 +1303,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct),
saddr, &daddr, proto, NULL, &port);
exp->timeout.expires = sip_timeout * HZ;
- exp->helper = helper;
+ rcu_assign_pointer(exp->helper, helper);
exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
hooks = rcu_dereference(nf_nat_sip_hooks);
diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
index d8f7bfd60ac6..77e46eae2025 100644
--- a/net/netfilter/nf_flow_table_offload.c
+++ b/net/netfilter/nf_flow_table_offload.c
@@ -13,6 +13,8 @@
#include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack_tuple.h>
+#define NF_FLOW_RULE_ACTION_MAX 24
+
static struct workqueue_struct *nf_flow_offload_add_wq;
static struct workqueue_struct *nf_flow_offload_del_wq;
static struct workqueue_struct *nf_flow_offload_stats_wq;
@@ -215,7 +217,12 @@ static void flow_offload_mangle(struct flow_action_entry *entry,
static inline struct flow_action_entry *
flow_action_entry_next(struct nf_flow_rule *flow_rule)
{
- int i = flow_rule->rule->action.num_entries++;
+ int i;
+
+ if (unlikely(flow_rule->rule->action.num_entries >= NF_FLOW_RULE_ACTION_MAX))
+ return NULL;
+
+ i = flow_rule->rule->action.num_entries++;
return &flow_rule->rule->action.entries[i];
}
@@ -233,6 +240,9 @@ static int flow_offload_eth_src(struct net *net,
u32 mask, val;
u16 val16;
+ if (!entry0 || !entry1)
+ return -E2BIG;
+
this_tuple = &flow->tuplehash[dir].tuple;
switch (this_tuple->xmit_type) {
@@ -283,6 +293,9 @@ static int flow_offload_eth_dst(struct net *net,
u8 nud_state;
u16 val16;
+ if (!entry0 || !entry1)
+ return -E2BIG;
+
this_tuple = &flow->tuplehash[dir].tuple;
switch (this_tuple->xmit_type) {
@@ -324,16 +337,19 @@ static int flow_offload_eth_dst(struct net *net,
return 0;
}
-static void flow_offload_ipv4_snat(struct net *net,
- const struct flow_offload *flow,
- enum flow_offload_tuple_dir dir,
- struct nf_flow_rule *flow_rule)
+static int flow_offload_ipv4_snat(struct net *net,
+ const struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
{
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
u32 mask = ~htonl(0xffffffff);
__be32 addr;
u32 offset;
+ if (!entry)
+ return -E2BIG;
+
switch (dir) {
case FLOW_OFFLOAD_DIR_ORIGINAL:
addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_v4.s_addr;
@@ -344,23 +360,27 @@ static void flow_offload_ipv4_snat(struct net *net,
offset = offsetof(struct iphdr, daddr);
break;
default:
- return;
+ return -EOPNOTSUPP;
}
flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP4, offset,
&addr, &mask);
+ return 0;
}
-static void flow_offload_ipv4_dnat(struct net *net,
- const struct flow_offload *flow,
- enum flow_offload_tuple_dir dir,
- struct nf_flow_rule *flow_rule)
+static int flow_offload_ipv4_dnat(struct net *net,
+ const struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
{
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
u32 mask = ~htonl(0xffffffff);
__be32 addr;
u32 offset;
+ if (!entry)
+ return -E2BIG;
+
switch (dir) {
case FLOW_OFFLOAD_DIR_ORIGINAL:
addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_v4.s_addr;
@@ -371,14 +391,15 @@ static void flow_offload_ipv4_dnat(struct net *net,
offset = offsetof(struct iphdr, saddr);
break;
default:
- return;
+ return -EOPNOTSUPP;
}
flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP4, offset,
&addr, &mask);
+ return 0;
}
-static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule,
+static int flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule,
unsigned int offset,
const __be32 *addr, const __be32 *mask)
{
@@ -387,15 +408,20 @@ static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule,
for (i = 0; i < sizeof(struct in6_addr) / sizeof(u32); i++) {
entry = flow_action_entry_next(flow_rule);
+ if (!entry)
+ return -E2BIG;
+
flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP6,
offset + i * sizeof(u32), &addr[i], mask);
}
+
+ return 0;
}
-static void flow_offload_ipv6_snat(struct net *net,
- const struct flow_offload *flow,
- enum flow_offload_tuple_dir dir,
- struct nf_flow_rule *flow_rule)
+static int flow_offload_ipv6_snat(struct net *net,
+ const struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
{
u32 mask = ~htonl(0xffffffff);
const __be32 *addr;
@@ -411,16 +437,16 @@ static void flow_offload_ipv6_snat(struct net *net,
offset = offsetof(struct ipv6hdr, daddr);
break;
default:
- return;
+ return -EOPNOTSUPP;
}
- flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask);
+ return flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask);
}
-static void flow_offload_ipv6_dnat(struct net *net,
- const struct flow_offload *flow,
- enum flow_offload_tuple_dir dir,
- struct nf_flow_rule *flow_rule)
+static int flow_offload_ipv6_dnat(struct net *net,
+ const struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
{
u32 mask = ~htonl(0xffffffff);
const __be32 *addr;
@@ -436,10 +462,10 @@ static void flow_offload_ipv6_dnat(struct net *net,
offset = offsetof(struct ipv6hdr, saddr);
break;
default:
- return;
+ return -EOPNOTSUPP;
}
- flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask);
+ return flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask);
}
static int flow_offload_l4proto(const struct flow_offload *flow)
@@ -461,15 +487,18 @@ static int flow_offload_l4proto(const struct flow_offload *flow)
return type;
}
-static void flow_offload_port_snat(struct net *net,
- const struct flow_offload *flow,
- enum flow_offload_tuple_dir dir,
- struct nf_flow_rule *flow_rule)
+static int flow_offload_port_snat(struct net *net,
+ const struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
{
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
u32 mask, port;
u32 offset;
+ if (!entry)
+ return -E2BIG;
+
switch (dir) {
case FLOW_OFFLOAD_DIR_ORIGINAL:
port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_port);
@@ -484,22 +513,26 @@ static void flow_offload_port_snat(struct net *net,
mask = ~htonl(0xffff);
break;
default:
- return;
+ return -EOPNOTSUPP;
}
flow_offload_mangle(entry, flow_offload_l4proto(flow), offset,
&port, &mask);
+ return 0;
}
-static void flow_offload_port_dnat(struct net *net,
- const struct flow_offload *flow,
- enum flow_offload_tuple_dir dir,
- struct nf_flow_rule *flow_rule)
+static int flow_offload_port_dnat(struct net *net,
+ const struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
{
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
u32 mask, port;
u32 offset;
+ if (!entry)
+ return -E2BIG;
+
switch (dir) {
case FLOW_OFFLOAD_DIR_ORIGINAL:
port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_port);
@@ -514,20 +547,24 @@ static void flow_offload_port_dnat(struct net *net,
mask = ~htonl(0xffff0000);
break;
default:
- return;
+ return -EOPNOTSUPP;
}
flow_offload_mangle(entry, flow_offload_l4proto(flow), offset,
&port, &mask);
+ return 0;
}
-static void flow_offload_ipv4_checksum(struct net *net,
- const struct flow_offload *flow,
- struct nf_flow_rule *flow_rule)
+static int flow_offload_ipv4_checksum(struct net *net,
+ const struct flow_offload *flow,
+ struct nf_flow_rule *flow_rule)
{
u8 protonum = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.l4proto;
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
+ if (!entry)
+ return -E2BIG;
+
entry->id = FLOW_ACTION_CSUM;
entry->csum_flags = TCA_CSUM_UPDATE_FLAG_IPV4HDR;
@@ -539,12 +576,14 @@ static void flow_offload_ipv4_checksum(struct net *net,
entry->csum_flags |= TCA_CSUM_UPDATE_FLAG_UDP;
break;
}
+
+ return 0;
}
-static void flow_offload_redirect(struct net *net,
- const struct flow_offload *flow,
- enum flow_offload_tuple_dir dir,
- struct nf_flow_rule *flow_rule)
+static int flow_offload_redirect(struct net *net,
+ const struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
{
const struct flow_offload_tuple *this_tuple, *other_tuple;
struct flow_action_entry *entry;
@@ -562,21 +601,28 @@ static void flow_offload_redirect(struct net *net,
ifindex = other_tuple->iifidx;
break;
default:
- return;
+ return -EOPNOTSUPP;
}
dev = dev_get_by_index(net, ifindex);
if (!dev)
- return;
+ return -ENODEV;
entry = flow_action_entry_next(flow_rule);
+ if (!entry) {
+ dev_put(dev);
+ return -E2BIG;
+ }
+
entry->id = FLOW_ACTION_REDIRECT;
entry->dev = dev;
+
+ return 0;
}
-static void flow_offload_encap_tunnel(const struct flow_offload *flow,
- enum flow_offload_tuple_dir dir,
- struct nf_flow_rule *flow_rule)
+static int flow_offload_encap_tunnel(const struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
{
const struct flow_offload_tuple *this_tuple;
struct flow_action_entry *entry;
@@ -584,7 +630,7 @@ static void flow_offload_encap_tunnel(const struct flow_offload *flow,
this_tuple = &flow->tuplehash[dir].tuple;
if (this_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
- return;
+ return 0;
dst = this_tuple->dst_cache;
if (dst && dst->lwtstate) {
@@ -593,15 +639,19 @@ static void flow_offload_encap_tunnel(const struct flow_offload *flow,
tun_info = lwt_tun_info(dst->lwtstate);
if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX)) {
entry = flow_action_entry_next(flow_rule);
+ if (!entry)
+ return -E2BIG;
entry->id = FLOW_ACTION_TUNNEL_ENCAP;
entry->tunnel = tun_info;
}
}
+
+ return 0;
}
-static void flow_offload_decap_tunnel(const struct flow_offload *flow,
- enum flow_offload_tuple_dir dir,
- struct nf_flow_rule *flow_rule)
+static int flow_offload_decap_tunnel(const struct flow_offload *flow,
+ enum flow_offload_tuple_dir dir,
+ struct nf_flow_rule *flow_rule)
{
const struct flow_offload_tuple *other_tuple;
struct flow_action_entry *entry;
@@ -609,7 +659,7 @@ static void flow_offload_decap_tunnel(const struct flow_offload *flow,
other_tuple = &flow->tuplehash[!dir].tuple;
if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
- return;
+ return 0;
dst = other_tuple->dst_cache;
if (dst && dst->lwtstate) {
@@ -618,9 +668,13 @@ static void flow_offload_decap_tunnel(const struct flow_offload *flow,
tun_info = lwt_tun_info(dst->lwtstate);
if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX)) {
entry = flow_action_entry_next(flow_rule);
+ if (!entry)
+ return -E2BIG;
entry->id = FLOW_ACTION_TUNNEL_DECAP;
}
}
+
+ return 0;
}
static int
@@ -632,8 +686,9 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
const struct flow_offload_tuple *tuple;
int i;
- flow_offload_decap_tunnel(flow, dir, flow_rule);
- flow_offload_encap_tunnel(flow, dir, flow_rule);
+ if (flow_offload_decap_tunnel(flow, dir, flow_rule) < 0 ||
+ flow_offload_encap_tunnel(flow, dir, flow_rule) < 0)
+ return -1;
if (flow_offload_eth_src(net, flow, dir, flow_rule) < 0 ||
flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
@@ -649,6 +704,8 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
if (tuple->encap[i].proto == htons(ETH_P_8021Q)) {
entry = flow_action_entry_next(flow_rule);
+ if (!entry)
+ return -1;
entry->id = FLOW_ACTION_VLAN_POP;
}
}
@@ -662,6 +719,8 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
continue;
entry = flow_action_entry_next(flow_rule);
+ if (!entry)
+ return -1;
switch (other_tuple->encap[i].proto) {
case htons(ETH_P_PPP_SES):
@@ -687,18 +746,22 @@ int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow,
return -1;
if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
- flow_offload_ipv4_snat(net, flow, dir, flow_rule);
- flow_offload_port_snat(net, flow, dir, flow_rule);
+ if (flow_offload_ipv4_snat(net, flow, dir, flow_rule) < 0 ||
+ flow_offload_port_snat(net, flow, dir, flow_rule) < 0)
+ return -1;
}
if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
- flow_offload_ipv4_dnat(net, flow, dir, flow_rule);
- flow_offload_port_dnat(net, flow, dir, flow_rule);
+ if (flow_offload_ipv4_dnat(net, flow, dir, flow_rule) < 0 ||
+ flow_offload_port_dnat(net, flow, dir, flow_rule) < 0)
+ return -1;
}
if (test_bit(NF_FLOW_SNAT, &flow->flags) ||
test_bit(NF_FLOW_DNAT, &flow->flags))
- flow_offload_ipv4_checksum(net, flow, flow_rule);
+ if (flow_offload_ipv4_checksum(net, flow, flow_rule) < 0)
+ return -1;
- flow_offload_redirect(net, flow, dir, flow_rule);
+ if (flow_offload_redirect(net, flow, dir, flow_rule) < 0)
+ return -1;
return 0;
}
@@ -712,22 +775,23 @@ int nf_flow_rule_route_ipv6(struct net *net, struct flow_offload *flow,
return -1;
if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
- flow_offload_ipv6_snat(net, flow, dir, flow_rule);
- flow_offload_port_snat(net, flow, dir, flow_rule);
+ if (flow_offload_ipv6_snat(net, flow, dir, flow_rule) < 0 ||
+ flow_offload_port_snat(net, flow, dir, flow_rule) < 0)
+ return -1;
}
if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
- flow_offload_ipv6_dnat(net, flow, dir, flow_rule);
- flow_offload_port_dnat(net, flow, dir, flow_rule);
+ if (flow_offload_ipv6_dnat(net, flow, dir, flow_rule) < 0 ||
+ flow_offload_port_dnat(net, flow, dir, flow_rule) < 0)
+ return -1;
}
- flow_offload_redirect(net, flow, dir, flow_rule);
+ if (flow_offload_redirect(net, flow, dir, flow_rule) < 0)
+ return -1;
return 0;
}
EXPORT_SYMBOL_GPL(nf_flow_rule_route_ipv6);
-#define NF_FLOW_RULE_ACTION_MAX 16
-
static struct nf_flow_rule *
nf_flow_offload_rule_alloc(struct net *net,
const struct flow_offload_work *offload,
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index fdbb1e20499b..0349787e1846 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -11667,8 +11667,6 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
switch (data->verdict.code) {
case NF_ACCEPT:
case NF_DROP:
- case NF_QUEUE:
- break;
case NFT_CONTINUE:
case NFT_BREAK:
case NFT_RETURN:
@@ -11703,6 +11701,11 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
data->verdict.chain = chain;
break;
+ case NF_QUEUE:
+ /* The nft_queue expression is used for this purpose, an
+ * immediate NF_QUEUE verdict should not ever be seen here.
+ */
+ fallthrough;
default:
return -EINVAL;
}
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 27dd35224e62..dcd2493a9a40 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -726,7 +726,7 @@ nfulnl_log_packet(struct net *net,
+ nla_total_size(plen) /* prefix */
+ nla_total_size(sizeof(struct nfulnl_msg_packet_hw))
+ nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp))
- + nla_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */
+ + nlmsg_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */
if (in && skb_mac_header_was_set(skb)) {
size += nla_total_size(skb->dev->hard_header_len)
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index 48105ea3df15..1ca4fa9d249b 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -501,6 +501,17 @@ int xt_check_match(struct xt_mtchk_param *par,
par->match->table, par->table);
return -EINVAL;
}
+
+ /* NFPROTO_UNSPEC implies NF_INET_* hooks which do not overlap with
+ * NF_ARP_IN,OUT,FORWARD, allow explicit extensions with NFPROTO_ARP
+ * support.
+ */
+ if (par->family == NFPROTO_ARP &&
+ par->match->family != NFPROTO_ARP) {
+ pr_info_ratelimited("%s_tables: %s match: not valid for this family\n",
+ xt_prefix[par->family], par->match->name);
+ return -EINVAL;
+ }
if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) {
char used[64], allow[64];
@@ -1016,6 +1027,18 @@ int xt_check_target(struct xt_tgchk_param *par,
par->target->table, par->table);
return -EINVAL;
}
+
+ /* NFPROTO_UNSPEC implies NF_INET_* hooks which do not overlap with
+ * NF_ARP_IN,OUT,FORWARD, allow explicit extensions with NFPROTO_ARP
+ * support.
+ */
+ if (par->family == NFPROTO_ARP &&
+ par->target->family != NFPROTO_ARP) {
+ pr_info_ratelimited("%s_tables: %s target: not valid for this family\n",
+ xt_prefix[par->family], par->target->name);
+ return -EINVAL;
+ }
+
if (par->target->hooks && (par->hook_mask & ~par->target->hooks) != 0) {
char used[64], allow[64];
diff --git a/net/netfilter/xt_cgroup.c b/net/netfilter/xt_cgroup.c
index c437fbd59ec1..43d2ae2be628 100644
--- a/net/netfilter/xt_cgroup.c
+++ b/net/netfilter/xt_cgroup.c
@@ -65,6 +65,9 @@ static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
info->priv = NULL;
if (info->has_path) {
+ if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
+ return -ENAMETOOLONG;
+
cgrp = cgroup_get_from_path(info->path);
if (IS_ERR(cgrp)) {
pr_info_ratelimited("invalid path, errno=%ld\n",
@@ -102,6 +105,9 @@ static int cgroup_mt_check_v2(const struct xt_mtchk_param *par)
info->priv = NULL;
if (info->has_path) {
+ if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
+ return -ENAMETOOLONG;
+
cgrp = cgroup_get_from_path(info->path);
if (IS_ERR(cgrp)) {
pr_info_ratelimited("invalid path, errno=%ld\n",
diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c
index 72324bd976af..b1d736c15fcb 100644
--- a/net/netfilter/xt_rateest.c
+++ b/net/netfilter/xt_rateest.c
@@ -91,6 +91,11 @@ static int xt_rateest_mt_checkentry(const struct xt_mtchk_param *par)
goto err1;
}
+ if (strnlen(info->name1, sizeof(info->name1)) >= sizeof(info->name1))
+ return -ENAMETOOLONG;
+ if (strnlen(info->name2, sizeof(info->name2)) >= sizeof(info->name2))
+ return -ENAMETOOLONG;
+
ret = -ENOENT;
est1 = xt_rateest_lookup(par->net, info->name1);
if (!est1)
diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c
index dab839f61ee9..26609feff4f8 100644
--- a/net/qrtr/af_qrtr.c
+++ b/net/qrtr/af_qrtr.c
@@ -118,7 +118,7 @@ static DEFINE_XARRAY_ALLOC(qrtr_ports);
* @ep: endpoint
* @ref: reference count for node
* @nid: node id
- * @qrtr_tx_flow: tree of qrtr_tx_flow, keyed by node << 32 | port
+ * @qrtr_tx_flow: xarray of qrtr_tx_flow, keyed by node << 32 | port
* @qrtr_tx_lock: lock for qrtr_tx_flow inserts
* @rx_queue: receive queue
* @item: list item for broadcast list
@@ -129,7 +129,7 @@ struct qrtr_node {
struct kref ref;
unsigned int nid;
- struct radix_tree_root qrtr_tx_flow;
+ struct xarray qrtr_tx_flow;
struct mutex qrtr_tx_lock; /* for qrtr_tx_flow */
struct sk_buff_head rx_queue;
@@ -172,6 +172,7 @@ static void __qrtr_node_release(struct kref *kref)
struct qrtr_tx_flow *flow;
unsigned long flags;
void __rcu **slot;
+ unsigned long index;
spin_lock_irqsave(&qrtr_nodes_lock, flags);
/* If the node is a bridge for other nodes, there are possibly
@@ -189,11 +190,9 @@ static void __qrtr_node_release(struct kref *kref)
skb_queue_purge(&node->rx_queue);
/* Free tx flow counters */
- radix_tree_for_each_slot(slot, &node->qrtr_tx_flow, &iter, 0) {
- flow = *slot;
- radix_tree_iter_delete(&node->qrtr_tx_flow, &iter, slot);
+ xa_for_each(&node->qrtr_tx_flow, index, flow)
kfree(flow);
- }
+ xa_destroy(&node->qrtr_tx_flow);
kfree(node);
}
@@ -228,9 +227,7 @@ static void qrtr_tx_resume(struct qrtr_node *node, struct sk_buff *skb)
key = remote_node << 32 | remote_port;
- rcu_read_lock();
- flow = radix_tree_lookup(&node->qrtr_tx_flow, key);
- rcu_read_unlock();
+ flow = xa_load(&node->qrtr_tx_flow, key);
if (flow) {
spin_lock(&flow->resume_tx.lock);
flow->pending = 0;
@@ -269,12 +266,13 @@ static int qrtr_tx_wait(struct qrtr_node *node, int dest_node, int dest_port,
return 0;
mutex_lock(&node->qrtr_tx_lock);
- flow = radix_tree_lookup(&node->qrtr_tx_flow, key);
+ flow = xa_load(&node->qrtr_tx_flow, key);
if (!flow) {
flow = kzalloc(sizeof(*flow), GFP_KERNEL);
if (flow) {
init_waitqueue_head(&flow->resume_tx);
- if (radix_tree_insert(&node->qrtr_tx_flow, key, flow)) {
+ if (xa_err(xa_store(&node->qrtr_tx_flow, key, flow,
+ GFP_KERNEL))) {
kfree(flow);
flow = NULL;
}
@@ -326,9 +324,7 @@ static void qrtr_tx_flow_failed(struct qrtr_node *node, int dest_node,
unsigned long key = (u64)dest_node << 32 | dest_port;
struct qrtr_tx_flow *flow;
- rcu_read_lock();
- flow = radix_tree_lookup(&node->qrtr_tx_flow, key);
- rcu_read_unlock();
+ flow = xa_load(&node->qrtr_tx_flow, key);
if (flow) {
spin_lock_irq(&flow->resume_tx.lock);
flow->tx_failed = 1;
@@ -599,7 +595,7 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid)
node->nid = QRTR_EP_NID_AUTO;
node->ep = ep;
- INIT_RADIX_TREE(&node->qrtr_tx_flow, GFP_KERNEL);
+ xa_init(&node->qrtr_tx_flow);
mutex_init(&node->qrtr_tx_lock);
qrtr_node_assign(node, nid);
@@ -627,6 +623,7 @@ void qrtr_endpoint_unregister(struct qrtr_endpoint *ep)
struct qrtr_tx_flow *flow;
struct sk_buff *skb;
unsigned long flags;
+ unsigned long index;
void __rcu **slot;
mutex_lock(&node->ep_lock);
@@ -649,10 +646,8 @@ void qrtr_endpoint_unregister(struct qrtr_endpoint *ep)
/* Wake up any transmitters waiting for resume-tx from the node */
mutex_lock(&node->qrtr_tx_lock);
- radix_tree_for_each_slot(slot, &node->qrtr_tx_flow, &iter, 0) {
- flow = *slot;
+ xa_for_each(&node->qrtr_tx_flow, index, flow)
wake_up_interruptible_all(&flow->resume_tx);
- }
mutex_unlock(&node->qrtr_tx_lock);
qrtr_node_release(node);
diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c
index 6585164c7059..dd08ccc4246d 100644
--- a/net/rds/ib_rdma.c
+++ b/net/rds/ib_rdma.c
@@ -604,8 +604,13 @@ void *rds_ib_get_mr(struct scatterlist *sg, unsigned long nents,
return ibmr;
}
- if (conn)
+ if (conn) {
ic = conn->c_transport_data;
+ if (!ic || !ic->i_cm_id || !ic->i_cm_id->qp) {
+ ret = -ENODEV;
+ goto out;
+ }
+ }
if (!rds_ibdev->mr_8k_pool || !rds_ibdev->mr_1m_pool) {
ret = -ENODEV;
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 8c72faf3314d..9edaff15052f 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -2969,6 +2969,7 @@ static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops,
tcm->tcm__pad1 = 0;
tcm->tcm__pad2 = 0;
tcm->tcm_handle = 0;
+ tcm->tcm_info = 0;
if (block->q) {
tcm->tcm_ifindex = qdisc_dev(block->q)->ifindex;
tcm->tcm_parent = block->q->handle;
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index 5693b41b093f..edf1252c1fde 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -503,8 +503,16 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
}
if (TC_H_MAJ(baseclass) == 0) {
- struct Qdisc *q = tcf_block_q(tp->chain->block);
+ struct tcf_block *block = tp->chain->block;
+ struct Qdisc *q;
+ if (tcf_block_shared(block)) {
+ NL_SET_ERR_MSG(extack,
+ "Must specify baseclass when attaching flow filter to block");
+ goto err2;
+ }
+
+ q = tcf_block_q(block);
baseclass = TC_H_MAKE(q->handle, baseclass);
}
if (TC_H_MIN(baseclass) == 0)
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index cdddc8695228..83a7372ea15c 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -247,8 +247,18 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
struct nlattr *tb[TCA_FW_MAX + 1];
int err;
- if (!opt)
- return handle ? -EINVAL : 0; /* Succeed if it is old method. */
+ if (!opt) {
+ if (handle)
+ return -EINVAL;
+
+ if (tcf_block_shared(tp->chain->block)) {
+ NL_SET_ERR_MSG(extack,
+ "Must specify mark when attaching fw filter to block");
+ return -EINVAL;
+ }
+
+ return 0; /* Succeed if it is old method. */
+ }
err = nla_parse_nested_deprecated(tb, TCA_FW_MAX, opt, fw_policy,
NULL);
diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
index d8fd35da32a7..57221522fe56 100644
--- a/net/sched/sch_hfsc.c
+++ b/net/sched/sch_hfsc.c
@@ -555,7 +555,7 @@ static void
rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u64 x, u64 y)
{
u64 y1, y2, dx, dy;
- u32 dsm;
+ u64 dsm;
if (isc->sm1 <= isc->sm2) {
/* service curve is convex */
@@ -598,7 +598,7 @@ rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u64 x, u64 y)
*/
dx = (y1 - y) << SM_SHIFT;
dsm = isc->sm1 - isc->sm2;
- do_div(dx, dsm);
+ dx = div64_u64(dx, dsm);
/*
* check if (x, y1) belongs to the 1st segment of rtsc.
* if so, add the offset.
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 32a5f3304046..3356d62ad054 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -519,8 +519,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
goto finish_segs;
}
- skb->data[get_random_u32_below(skb_headlen(skb))] ^=
- 1<<get_random_u32_below(8);
+ if (skb_headlen(skb))
+ skb->data[get_random_u32_below(skb_headlen(skb))] ^=
+ 1 << get_random_u32_below(8);
}
if (unlikely(q->t_len >= sch->limit)) {
diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c
index b981a4828d08..e47ebd8acd21 100644
--- a/net/x25/x25_in.c
+++ b/net/x25/x25_in.c
@@ -34,6 +34,10 @@ static int x25_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
struct sk_buff *skbo, *skbn = skb;
struct x25_sock *x25 = x25_sk(sk);
+ /* make sure we don't overflow */
+ if (x25->fraglen + skb->len > USHRT_MAX)
+ return 1;
+
if (more) {
x25->fraglen += skb->len;
skb_queue_tail(&x25->fragment_queue, skb);
@@ -44,10 +48,9 @@ static int x25_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
if (x25->fraglen > 0) { /* End of fragment */
int len = x25->fraglen + skb->len;
- if ((skbn = alloc_skb(len, GFP_ATOMIC)) == NULL){
- kfree_skb(skb);
+ skbn = alloc_skb(len, GFP_ATOMIC);
+ if (!skbn)
return 1;
- }
skb_queue_tail(&x25->fragment_queue, skb);
diff --git a/net/x25/x25_subr.c b/net/x25/x25_subr.c
index 0285aaa1e93c..159708d9ad20 100644
--- a/net/x25/x25_subr.c
+++ b/net/x25/x25_subr.c
@@ -40,6 +40,7 @@ void x25_clear_queues(struct sock *sk)
skb_queue_purge(&x25->interrupt_in_queue);
skb_queue_purge(&x25->interrupt_out_queue);
skb_queue_purge(&x25->fragment_queue);
+ x25->fraglen = 0;
}
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index 9a00e1d324ce..cb39054bfe79 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -4102,6 +4102,7 @@ enum {
ALC233_FIXUP_LENOVO_GPIO2_MIC_HOTKEY,
ALC245_FIXUP_BASS_HP_DAC,
ALC245_FIXUP_ACER_MICMUTE_LED,
+ ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@@ -6631,6 +6632,12 @@ static const struct hda_fixup alc269_fixups[] = {
.v.func = alc285_fixup_hp_coef_micmute_led,
.chained = true,
.chain_id = ALC2XX_FIXUP_HEADSET_MIC,
+ },
+ [ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_mute_led_coefbit,
+ .chained = true,
+ .chain_id = ALC287_FIXUP_CS35L41_I2C_2,
}
};
@@ -6685,6 +6692,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC),
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, 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),
SND_PCI_QUIRK(0x1025, 0x1844, "Acer Helios ZPS", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
@@ -6980,6 +6988,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8a34, "HP Pavilion x360 2-in-1 Laptop 14-ek0xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8a3d, "HP Victus 15-fb0xxx (MB 8A3D)", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4),
SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
@@ -7154,7 +7163,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8e37, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x103c, 0x8e60, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e60, "HP OmniBook 7 Laptop 16-bh0xxx", ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e8a, "HP NexusX", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
@@ -7237,6 +7246,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1533, "ASUS GV302XA/XJ/XQ/XU/XV/XI", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x1043, 0x1573, "ASUS GZ301VV/VQ/VU/VJ/VA/VC/VE/VVC/VQC/VUC/VJC/VEC/VCC", ALC285_FIXUP_ASUS_HEADSET_MIC),
SND_PCI_QUIRK(0x1043, 0x1584, "ASUS UM3406GA ", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1602, "ASUS ROG Strix SCAR 15", ALC285_FIXUP_ASUS_G533Z_PINS),
SND_PCI_QUIRK(0x1043, 0x1652, "ASUS ROG Zephyrus Do 15 SE", ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK),
SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK),
SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC),
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index a25a599fc5be..f50cff4654c2 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -1427,10 +1427,14 @@ static int atc_get_resources(struct ct_atc *atc)
daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
da_desc.msr = atc->msr;
for (i = 0; i < NUM_DAIOTYP; i++) {
- if (((i == MIC) && !cap.dedicated_mic) || ((i == RCA) && !cap.dedicated_rca))
+ if (((i == MIC) && !cap.dedicated_mic) ||
+ ((i == RCA) && !cap.dedicated_rca) ||
+ i == SPDIFI1)
continue;
- da_desc.type = (atc->model != CTSB073X) ? i :
- ((i == SPDIFIO) ? SPDIFI1 : i);
+ if (atc->model == CTSB073X && i == SPDIFIO)
+ da_desc.type = SPDIFI1;
+ else
+ da_desc.type = i;
da_desc.output = (i < LINEIM) || (i == RCA);
err = daio_mgr->get_daio(daio_mgr, &da_desc,
(struct daio **)&atc->daios[i]);
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c
index 1c8f8efd836c..6166f07c010c 100644
--- a/sound/pci/ctxfi/ctdaio.c
+++ b/sound/pci/ctxfi/ctdaio.c
@@ -99,7 +99,7 @@ static const struct rsc_ops daio_in_rsc_ops_20k2 = {
.output_slot = daio_index,
};
-static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
+static int daio_device_index(enum DAIOTYP type, struct hw *hw)
{
switch (hw->chip_type) {
case ATC20K1:
@@ -112,12 +112,15 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
case LINEO3: return 5;
case LINEO4: return 6;
case LINEIM: return 7;
- default: return -EINVAL;
+ default:
+ pr_err("ctxfi: Invalid type %d for hw20k1\n", type);
+ return -EINVAL;
}
case ATC20K2:
switch (type) {
case SPDIFOO: return 0;
case SPDIFIO: return 0;
+ case SPDIFI1: return 1;
case LINEO1: return 4;
case LINEO2: return 7;
case LINEO3: return 5;
@@ -125,9 +128,12 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
case LINEIM: return 4;
case MIC: return 5;
case RCA: return 3;
- default: return -EINVAL;
+ default:
+ pr_err("ctxfi: Invalid type %d for hw20k2\n", type);
+ return -EINVAL;
}
default:
+ pr_err("ctxfi: Invalid chip type %d\n", hw->chip_type);
return -EINVAL;
}
}
@@ -148,8 +154,11 @@ static int dao_spdif_set_spos(struct dao *dao, unsigned int spos)
static int dao_commit_write(struct dao *dao)
{
- dao->hw->dao_commit_write(dao->hw,
- daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk);
+ int idx = daio_device_index(dao->daio.type, dao->hw);
+
+ if (idx < 0)
+ return idx;
+ dao->hw->dao_commit_write(dao->hw, idx, dao->ctrl_blk);
return 0;
}
@@ -287,8 +296,11 @@ static int dai_set_enb_srt(struct dai *dai, unsigned int enb)
static int dai_commit_write(struct dai *dai)
{
- dai->hw->dai_commit_write(dai->hw,
- daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
+ int idx = daio_device_index(dai->daio.type, dai->hw);
+
+ if (idx < 0)
+ return idx;
+ dai->hw->dai_commit_write(dai->hw, idx, dai->ctrl_blk);
return 0;
}
@@ -367,7 +379,7 @@ static int dao_rsc_init(struct dao *dao,
{
struct hw *hw = mgr->mgr.hw;
unsigned int conf;
- int err;
+ int idx, err;
err = daio_rsc_init(&dao->daio, desc, mgr->mgr.hw);
if (err)
@@ -386,15 +398,18 @@ static int dao_rsc_init(struct dao *dao,
if (err)
goto error2;
- hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
- daio_device_index(dao->daio.type, hw));
+ idx = daio_device_index(dao->daio.type, hw);
+ if (idx < 0) {
+ err = idx;
+ goto error2;
+ }
+
+ hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk, idx);
hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
conf = (desc->msr & 0x7) | (desc->passthru << 3);
- hw->daio_mgr_dao_init(hw, mgr->mgr.ctrl_blk,
- daio_device_index(dao->daio.type, hw), conf);
- hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
- daio_device_index(dao->daio.type, hw));
+ hw->daio_mgr_dao_init(hw, mgr->mgr.ctrl_blk, idx, conf);
+ hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, idx);
hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
return 0;
@@ -443,7 +458,7 @@ static int dai_rsc_init(struct dai *dai,
const struct daio_desc *desc,
struct daio_mgr *mgr)
{
- int err;
+ int idx, err;
struct hw *hw = mgr->mgr.hw;
unsigned int rsr, msr;
@@ -457,6 +472,12 @@ static int dai_rsc_init(struct dai *dai,
if (err)
goto error1;
+ idx = daio_device_index(dai->daio.type, dai->hw);
+ if (idx < 0) {
+ err = idx;
+ goto error1;
+ }
+
for (rsr = 0, msr = desc->msr; msr > 1; msr >>= 1)
rsr++;
@@ -465,8 +486,7 @@ static int dai_rsc_init(struct dai *dai,
/* default to disabling control of a SRC */
hw->dai_srt_set_ec(dai->ctrl_blk, 0);
hw->dai_srt_set_et(dai->ctrl_blk, 0); /* default to disabling SRT */
- hw->dai_commit_write(hw,
- daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
+ hw->dai_commit_write(hw, idx, dai->ctrl_blk);
return 0;
@@ -581,28 +601,28 @@ static int put_daio_rsc(struct daio_mgr *mgr, struct daio *daio)
static int daio_mgr_enb_daio(struct daio_mgr *mgr, struct daio *daio)
{
struct hw *hw = mgr->mgr.hw;
-
- if (daio->output) {
- hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
- daio_device_index(daio->type, hw));
- } else {
- hw->daio_mgr_enb_dai(mgr->mgr.ctrl_blk,
- daio_device_index(daio->type, hw));
- }
+ int idx = daio_device_index(daio->type, hw);
+
+ if (idx < 0)
+ return idx;
+ if (daio->output)
+ hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk, idx);
+ else
+ hw->daio_mgr_enb_dai(mgr->mgr.ctrl_blk, idx);
return 0;
}
static int daio_mgr_dsb_daio(struct daio_mgr *mgr, struct daio *daio)
{
struct hw *hw = mgr->mgr.hw;
-
- if (daio->output) {
- hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
- daio_device_index(daio->type, hw));
- } else {
- hw->daio_mgr_dsb_dai(mgr->mgr.ctrl_blk,
- daio_device_index(daio->type, hw));
- }
+ int idx = daio_device_index(daio->type, hw);
+
+ if (idx < 0)
+ return idx;
+ if (daio->output)
+ hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk, idx);
+ else
+ hw->daio_mgr_dsb_dai(mgr->mgr.ctrl_blk, idx);
return 0;
}
diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c
index cca01c03f048..5dba741594fa 100644
--- a/sound/soc/cirrus/ep93xx-i2s.c
+++ b/sound/soc/cirrus/ep93xx-i2s.c
@@ -91,16 +91,28 @@ static inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info,
return __raw_readl(info->regs + reg);
}
-static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream)
+static int ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream)
{
unsigned base_reg;
+ int err;
if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 &&
(ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) {
/* Enable clocks */
- clk_prepare_enable(info->mclk);
- clk_prepare_enable(info->sclk);
- clk_prepare_enable(info->lrclk);
+ err = clk_prepare_enable(info->mclk);
+ if (err)
+ return err;
+ err = clk_prepare_enable(info->sclk);
+ if (err) {
+ clk_disable_unprepare(info->mclk);
+ return err;
+ }
+ err = clk_prepare_enable(info->lrclk);
+ if (err) {
+ clk_disable_unprepare(info->sclk);
+ clk_disable_unprepare(info->mclk);
+ return err;
+ }
/* Enable i2s */
ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1);
@@ -119,6 +131,8 @@ static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream)
ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCTRL,
EP93XX_I2S_TXCTRL_TXEMPTY_LVL |
EP93XX_I2S_TXCTRL_TXUFIE);
+
+ return 0;
}
static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream)
@@ -195,9 +209,7 @@ static int ep93xx_i2s_startup(struct snd_pcm_substream *substream,
{
struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
- ep93xx_i2s_enable(info, substream->stream);
-
- return 0;
+ return ep93xx_i2s_enable(info, substream->stream);
}
static void ep93xx_i2s_shutdown(struct snd_pcm_substream *substream,
@@ -373,14 +385,16 @@ static int ep93xx_i2s_suspend(struct snd_soc_component *component)
static int ep93xx_i2s_resume(struct snd_soc_component *component)
{
struct ep93xx_i2s_info *info = snd_soc_component_get_drvdata(component);
+ int err;
if (!snd_soc_component_active(component))
return 0;
- ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
- ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE);
+ err = ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
+ if (err)
+ return err;
- return 0;
+ return ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE);
}
#else
#define ep93xx_i2s_suspend NULL
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index c23fdb6aad4c..1031d6497f55 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -525,8 +525,6 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
select SND_SOC_CS42L43_SDW
select MFD_CS42L43
select MFD_CS42L43_SDW
- select PINCTRL_CS42L43
- select SPI_CS42L43
select SND_SOC_CS35L56_SPI
select SND_SOC_CS35L56_SDW
select SND_SOC_DMIC
diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c
index dfd820483849..3a71bab8a477 100644
--- a/sound/usb/caiaq/device.c
+++ b/sound/usb/caiaq/device.c
@@ -488,7 +488,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev)
memset(id, 0, sizeof(id));
for (c = card->shortname, len = 0;
- *c && len < sizeof(card->id); c++)
+ *c && len < sizeof(card->id) - 1; c++)
if (*c != ' ')
id[len++] = *c;
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
index 297490f0f587..542eae3a57d9 100644
--- a/sound/usb/qcom/qc_audio_offload.c
+++ b/sound/usb/qcom/qc_audio_offload.c
@@ -699,6 +699,7 @@ static void uaudio_event_ring_cleanup_free(struct uaudio_dev *dev)
uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE,
PAGE_SIZE);
xhci_sideband_remove_interrupter(uadev[dev->chip->card->number].sb);
+ usb_offload_put(dev->udev);
}
}
@@ -1182,12 +1183,16 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs,
dma_coherent = dev_is_dma_coherent(subs->dev->bus->sysdev);
er_pa = 0;
+ ret = usb_offload_get(subs->dev);
+ if (ret < 0)
+ goto exit;
+
/* event ring */
ret = xhci_sideband_create_interrupter(uadev[card_num].sb, 1, false,
0, uaudio_qdev->data->intr_num);
if (ret < 0) {
dev_err(&subs->dev->dev, "failed to fetch interrupter\n");
- goto exit;
+ goto put_offload;
}
sgt = xhci_sideband_get_event_buffer(uadev[card_num].sb);
@@ -1219,6 +1224,8 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs,
mem_info->dma = 0;
remove_interrupter:
xhci_sideband_remove_interrupter(uadev[card_num].sb);
+put_offload:
+ usb_offload_put(subs->dev);
exit:
return ret;
}
@@ -1483,6 +1490,7 @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE, PAGE_SIZE);
free_sec_ring:
xhci_sideband_remove_interrupter(uadev[card_num].sb);
+ usb_offload_put(subs->dev);
drop_sync_ep:
if (subs->sync_endpoint) {
uaudio_iommu_unmap(MEM_XFER_RING,
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 09ed93510758..a56fb8ef987e 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2427,6 +2427,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_VALIDATE_RATES),
DEVICE_FLG(0x1235, 0x8006, 0), /* Focusrite Scarlett 2i2 1st Gen */
DEVICE_FLG(0x1235, 0x800a, 0), /* Focusrite Scarlett 2i4 1st Gen */
+ DEVICE_FLG(0x1235, 0x8016, 0), /* Focusrite Scarlett 2i2 1st Gen */
+ DEVICE_FLG(0x1235, 0x801c, 0), /* Focusrite Scarlett Solo 1st Gen */
VENDOR_FLG(0x1235, /* Focusrite Novation */
QUIRK_FLAG_SKIP_IFACE_SETUP),
VENDOR_FLG(0x1511, /* AURALiC */
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 30609aed5d37..2f63f938d089 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2144,12 +2144,11 @@ static void mark_func_jump_tables(struct objtool_file *file,
last = insn;
/*
- * Store back-pointers for unconditional forward jumps such
+ * Store back-pointers for forward jumps such
* that find_jump_table() can back-track using those and
* avoid some potentially confusing code.
*/
- if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
- insn->offset > last->offset &&
+ if (insn->jump_dest &&
insn->jump_dest->offset > insn->offset &&
!insn->jump_dest->first_jump_src) {
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 3da90686350d..2ffe3ebfbe37 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -16,7 +16,6 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
-#include <libgen.h>
#include <ctype.h>
#include <linux/align.h>
#include <linux/kernel.h>
@@ -1189,7 +1188,7 @@ struct elf *elf_open_read(const char *name, int flags)
struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name)
{
struct section *null, *symtab, *strtab, *shstrtab;
- char *dir, *base, *tmp_name;
+ char *tmp_name;
struct symbol *sym;
struct elf *elf;
@@ -1203,29 +1202,13 @@ struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name)
INIT_LIST_HEAD(&elf->sections);
- dir = strdup(name);
- if (!dir) {
- ERROR_GLIBC("strdup");
- return NULL;
- }
-
- dir = dirname(dir);
-
- base = strdup(name);
- if (!base) {
- ERROR_GLIBC("strdup");
- return NULL;
- }
-
- base = basename(base);
-
- tmp_name = malloc(256);
+ tmp_name = malloc(strlen(name) + 8);
if (!tmp_name) {
ERROR_GLIBC("malloc");
return NULL;
}
- snprintf(tmp_name, 256, "%s/%s.XXXXXX", dir, base);
+ sprintf(tmp_name, "%s.XXXXXX", name);
elf->fd = mkstemp(tmp_name);
if (elf->fd == -1) {
diff --git a/tools/testing/selftests/cgroup/lib/cgroup_util.c b/tools/testing/selftests/cgroup/lib/cgroup_util.c
index 44c52f620fda..4b0f2c46d432 100644
--- a/tools/testing/selftests/cgroup/lib/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/lib/cgroup_util.c
@@ -123,6 +123,21 @@ int cg_read_strcmp(const char *cgroup, const char *control,
return ret;
}
+int cg_read_strcmp_wait(const char *cgroup, const char *control,
+ const char *expected)
+{
+ int i, ret;
+
+ for (i = 0; i < 100; i++) {
+ ret = cg_read_strcmp(cgroup, control, expected);
+ if (!ret)
+ return ret;
+ usleep(10000);
+ }
+
+ return ret;
+}
+
int cg_read_strstr(const char *cgroup, const char *control, const char *needle)
{
char buf[PAGE_SIZE];
diff --git a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h
index 7ab2824ed7b5..1cbe3b0ac6f7 100644
--- a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h
+++ b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h
@@ -59,6 +59,8 @@ extern int cg_read(const char *cgroup, const char *control,
char *buf, size_t len);
extern int cg_read_strcmp(const char *cgroup, const char *control,
const char *expected);
+extern int cg_read_strcmp_wait(const char *cgroup, const char *control,
+ const char *expected);
extern int cg_read_strstr(const char *cgroup, const char *control,
const char *needle);
extern long cg_read_long(const char *cgroup, const char *control);
diff --git a/tools/testing/selftests/cgroup/test_core.c b/tools/testing/selftests/cgroup/test_core.c
index 102262555a59..7b83c7e7c9d4 100644
--- a/tools/testing/selftests/cgroup/test_core.c
+++ b/tools/testing/selftests/cgroup/test_core.c
@@ -233,7 +233,8 @@ static int test_cgcore_populated(const char *root)
if (err)
goto cleanup;
- if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
+ if (cg_read_strcmp_wait(cg_test_d, "cgroup.events",
+ "populated 0\n"))
goto cleanup;
/* Remove cgroup. */
diff --git a/tools/testing/selftests/cgroup/test_kill.c b/tools/testing/selftests/cgroup/test_kill.c
index c8c9d306925b..f6cd23a8ecc7 100644
--- a/tools/testing/selftests/cgroup/test_kill.c
+++ b/tools/testing/selftests/cgroup/test_kill.c
@@ -86,7 +86,7 @@ static int test_cgkill_simple(const char *root)
wait_for_pid(pids[i]);
if (ret == KSFT_PASS &&
- cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n"))
+ cg_read_strcmp_wait(cgroup, "cgroup.events", "populated 0\n"))
ret = KSFT_FAIL;
if (cgroup)
@@ -190,7 +190,8 @@ static int test_cgkill_tree(const char *root)
wait_for_pid(pids[i]);
if (ret == KSFT_PASS &&
- cg_read_strcmp(cgroup[0], "cgroup.events", "populated 0\n"))
+ cg_read_strcmp_wait(cgroup[0], "cgroup.events",
+ "populated 0\n"))
ret = KSFT_FAIL;
for (i = 9; i >= 0 && cgroup[i]; i--) {
@@ -251,7 +252,7 @@ static int test_cgkill_forkbomb(const char *root)
wait_for_pid(pid);
if (ret == KSFT_PASS &&
- cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n"))
+ cg_read_strcmp_wait(cgroup, "cgroup.events", "populated 0\n"))
ret = KSFT_FAIL;
if (cgroup)
prev parent reply other threads:[~2026-04-11 12:54 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-11 12:53 Linux 6.19.12 Greg Kroah-Hartman
2026-04-11 12:54 ` Greg Kroah-Hartman [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2026041100-wackiness-broadly-b54d@gregkh \
--to=gregkh@linuxfoundation.org \
--cc=akpm@linux-foundation.org \
--cc=jslaby@suse.cz \
--cc=linux-kernel@vger.kernel.org \
--cc=lwn@lwn.net \
--cc=stable@vger.kernel.org \
--cc=torvalds@linux-foundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox