Re: Linux 7.1.6

From: Greg Kroah-Hartman

Date: Mon Aug 03 2026 - 05:45:33 EST


diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst
index 6efd0095ed99..5cc33d87c3df 100644
--- a/Documentation/admin-guide/cgroup-v2.rst
+++ b/Documentation/admin-guide/cgroup-v2.rst
@@ -1570,7 +1570,7 @@ The following nested keys are defined.
sock (npn)
Amount of memory used in network transmission buffers

- vmalloc (npn)
+ vmalloc
Amount of memory used for vmap backed memory.

shmem
@@ -1735,7 +1735,7 @@ The following nested keys are defined.
Number of pages written from zswap to swap.

zswap_incomp
- Number of incompressible pages currently stored in zswap
+ Amount of memory used by incompressible pages currently stored in zswap
without compression. These pages could not be compressed to
a size smaller than PAGE_SIZE, so they are stored as-is.

diff --git a/Documentation/hwmon/max34440.rst b/Documentation/hwmon/max34440.rst
index d6d4fbc863d9..e7421f4dbf38 100644
--- a/Documentation/hwmon/max34440.rst
+++ b/Documentation/hwmon/max34440.rst
@@ -19,6 +19,14 @@ Supported chips:

Datasheet: -

+ * ADI ADPM12250
+
+ Prefixes: 'adpm12250'
+
+ Addresses scanned: -
+
+ Datasheet: -
+
* Maxim MAX34440

Prefixes: 'max34440'
@@ -87,11 +95,11 @@ This driver supports multiple devices: hardware monitoring for Maxim MAX34440
PMBus 6-Channel Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply
Manager and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data
Logger; PMBus Voltage Monitor and Sequencers for MAX34451, MAX34460, and
-MAX34461; PMBus DC/DC Power Module ADPM12160, and ADPM12200. The MAX34451
-supports monitoring voltage or current of 12 channels based on GIN pins. The
-MAX34460 supports 12 voltage channels, and the MAX34461 supports 16 voltage
-channels. The ADPM12160, and ADPM12200 also monitors both input and output
-of voltage and current.
+MAX34461; PMBus DC/DC Power Module ADPM12160, ADPM12200, and ADPM12250. The
+MAX34451 supports monitoring voltage or current of 12 channels based on GIN
+pins. The MAX34460 supports 12 voltage channels, and the MAX34461 supports 16
+voltage channels. The ADPM12160, ADPM12200, and ADPM12250 also monitors both
+input and output of voltage and current.

The driver is a client driver to the core PMBus driver. Please see
Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
@@ -149,7 +157,7 @@ in[1-6]_reset_history Write any value to reset history.
.. note::

- MAX34446 only supports in[1-4].
- - ADPM12160, and ADPM12200 only supports in[1-2]. Label is "vin1"
+ - ADPM12160, ADPM12200, and ADPM12250 only supports in[1-2]. Label is "vin1"
and "vout1" respectively.

Curr
@@ -172,8 +180,9 @@ curr[1-6]_reset_history Write any value to reset history.

- in6 and curr6 attributes only exist for MAX34440.
- MAX34446 only supports curr[1-4].
- - For ADPM12160, and ADPM12200, curr[1] is "iin1" and curr[2-6]
- are "iout[1-5]".
+ - For ADPM12160, ADPM12200, and ADPM12250, curr[1] is "iin1"
+ - For ADPM12160, and ADPM12200 curr[2-6] are "iout[1-5]".
+ - For ADPM12250, curr[2-4] are "iout[1-3]".

Power
~~~~~
@@ -209,7 +218,7 @@ temp[1-8]_reset_history Write any value to reset history.
.. note::
- temp7 and temp8 attributes only exist for MAX34440.
- MAX34446 only supports temp[1-3].
- - ADPM12160, and ADPM12200 only supports temp[1].
+ - ADPM12160, ADPM12200, and ADPM12250 only supports temp[1].


.. note::
diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml
index 644a8bd7b93c..e656f05268ed 100644
--- a/Documentation/netlink/specs/rt-link.yaml
+++ b/Documentation/netlink/specs/rt-link.yaml
@@ -1583,31 +1583,31 @@ attribute-sets:
type: u32
-
name: mode
- type: flag
+ type: u8
-
name: guard
- type: flag
+ type: u8
-
name: protect
- type: flag
+ type: u8
-
name: fast-leave
- type: flag
+ type: u8
-
name: learning
- type: flag
+ type: u8
-
name: unicast-flood
- type: flag
+ type: u8
-
name: proxyarp
- type: flag
+ type: u8
-
name: learning-sync
- type: flag
+ type: u8
-
name: proxyarp-wifi
- type: flag
+ type: u8
-
name: root-id
type: binary
@@ -1654,34 +1654,34 @@ attribute-sets:
type: pad
-
name: mcast-flood
- type: flag
+ type: u8
-
name: mcast-to-ucast
- type: flag
+ type: u8
-
name: vlan-tunnel
- type: flag
+ type: u8
-
name: bcast-flood
- type: flag
+ type: u8
-
name: group-fwd-mask
type: u16
-
name: neigh-suppress
- type: flag
+ type: u8
-
name: isolated
- type: flag
+ type: u8
-
name: backup-port
type: u32
-
name: mrp-ring-open
- type: flag
+ type: u8
-
name: mrp-in-open
- type: flag
+ type: u8
-
name: mcast-eht-hosts-limit
type: u32
@@ -1690,10 +1690,10 @@ attribute-sets:
type: u32
-
name: locked
- type: flag
+ type: u8
-
name: mab
- type: flag
+ type: u8
-
name: mcast-n-groups
type: u32
@@ -1702,7 +1702,7 @@ attribute-sets:
type: u32
-
name: neigh-vlan-suppress
- type: flag
+ type: u8
-
name: backup-nhid
type: u32
diff --git a/Makefile b/Makefile
index 58f3b3e2dc1c..75e5c9573863 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 7
PATCHLEVEL = 1
-SUBLEVEL = 5
+SUBLEVEL = 6
EXTRAVERSION =
NAME = Baby Opossum Posse

@@ -473,6 +473,10 @@ KBUILD_USERLDFLAGS := $(USERLDFLAGS)

# These flags apply to all Rust code in the tree, including the kernel and
# host programs.
+#
+# `-Aclippy::unwrap_or_default`: the lint is buggy [1] and ignores our
+# MSRV. It can trigger depending on the optimization level.
+# [1] https://github.com/rust-lang/rust-clippy/issues/17379
export rust_common_flags := --edition=2021 \
-Zbinary_dep_depinfo=y \
-Astable_features \
@@ -501,6 +505,7 @@ export rust_common_flags := --edition=2021 \
-Aclippy::uninlined_format_args \
-Wclippy::unnecessary_safety_comment \
-Wclippy::unnecessary_safety_doc \
+ -Aclippy::unwrap_or_default \
-Wrustdoc::missing_crate_level_docs \
-Wrustdoc::unescaped_backticks

diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
index 18220cdac9f9..eda23431ed05 100644
--- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
@@ -5339,7 +5339,7 @@ cpus {
#size-cells = <0>;

cpu0_0: cpu@0 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x00000>;

@@ -5358,7 +5358,7 @@ cpu0_0: cpu@0 {
};

cpu0_1: cpu@100 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x00100>;

@@ -5377,7 +5377,7 @@ cpu0_1: cpu@100 {
};

cpu0_2: cpu@200 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x00200>;

@@ -5396,7 +5396,7 @@ cpu0_2: cpu@200 {
};

cpu0_3: cpu@300 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x00300>;

@@ -5415,7 +5415,7 @@ cpu0_3: cpu@300 {
};

cpu1_0: cpu@10000 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x10000>;

@@ -5434,7 +5434,7 @@ cpu1_0: cpu@10000 {
};

cpu1_1: cpu@10100 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x10100>;

@@ -5453,7 +5453,7 @@ cpu1_1: cpu@10100 {
};

cpu1_2: cpu@10200 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x10200>;

@@ -5472,7 +5472,7 @@ cpu1_2: cpu@10200 {
};

cpu1_3: cpu@10300 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x10300>;

@@ -5491,7 +5491,7 @@ cpu1_3: cpu@10300 {
};

cpu2_0: cpu@20000 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x20000>;

@@ -5510,7 +5510,7 @@ cpu2_0: cpu@20000 {
};

cpu2_1: cpu@20100 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x20100>;

@@ -5529,7 +5529,7 @@ cpu2_1: cpu@20100 {
};

cpu2_2: cpu@20200 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x20200>;

@@ -5548,7 +5548,7 @@ cpu2_2: cpu@20200 {
};

cpu2_3: cpu@20300 {
- compatible = "arm,cortex-a78";
+ compatible = "arm,cortex-a78ae";
device_type = "cpu";
reg = <0x20300>;

diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
index 2d8e7e37830f..3dfdd7bb28a9 100644
--- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
@@ -3208,7 +3208,7 @@ agic_page5: interrupt-controller@99b0000 {
};

gpcdma: dma-controller@8400000 {
- compatible = "nvidia,tegra264-gpcdma", "nvidia,tegra186-gpcdma";
+ compatible = "nvidia,tegra264-gpcdma";
reg = <0x0 0x08400000 0x0 0x210000>;
interrupts = <GIC_SPI 584 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 585 IRQ_TYPE_LEVEL_HIGH>,
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 7e86d400864e..64e9bd137b60 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -131,7 +131,7 @@
* Annoyingly, the negative levels for Address size faults aren't laid out
* contiguously (or in the desired order)
*/
-#define ESR_ELx_FSC_ADDRSZ_nL(n) ((n) == -1 ? 0x25 : 0x2C)
+#define ESR_ELx_FSC_ADDRSZ_nL(n) ((n) == -1 ? 0x29 : 0x2C)
#define ESR_ELx_FSC_ADDRSZ_L(n) ((n) < 0 ? ESR_ELx_FSC_ADDRSZ_nL(n) : \
(ESR_ELx_FSC_ADDRSZ + (n)))

diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
index 30772a909aea..8e799c1fe0aa 100644
--- a/arch/arm64/mm/hugetlbpage.c
+++ b/arch/arm64/mm/hugetlbpage.c
@@ -87,7 +87,7 @@ static int find_num_contig(struct mm_struct *mm, unsigned long addr,
p4dp = p4d_offset(pgdp, addr);
pudp = pud_offset(p4dp, addr);
pmdp = pmd_offset(pudp, addr);
- if ((pte_t *)pmdp == ptep) {
+ if ((pte_t *)PTR_ALIGN_DOWN(pmdp, sizeof(*pmdp) * CONT_PMDS) == ptep) {
*pgsize = PMD_SIZE;
return CONT_PMDS;
}
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index fd756390fe92..92fedf4db927 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -2032,12 +2032,13 @@ int arch_add_memory(int nid, u64 start, u64 size,
return ret;
}

-void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap)
+void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap,
+ struct dev_pagemap *pgmap)
{
unsigned long start_pfn = start >> PAGE_SHIFT;
unsigned long nr_pages = size >> PAGE_SHIFT;

- __remove_pages(start_pfn, nr_pages, altmap);
+ __remove_pages(start_pfn, nr_pages, altmap, pgmap);
__remove_pgd_mapping(swapper_pg_dir, __phys_to_virt(start), size);
}

@@ -2201,7 +2202,7 @@ static int prevent_memory_remove_notifier(struct notifier_block *nb,
}
}

- if (!can_unmap_without_split(pfn, arg->nr_pages))
+ if (!can_unmap_without_split(arg->start_pfn, arg->nr_pages))
return NOTIFY_BAD;

return NOTIFY_OK;
diff --git a/arch/loongarch/include/asm/asmmacro.h b/arch/loongarch/include/asm/asmmacro.h
index a648be5f723f..b7423d1ac568 100644
--- a/arch/loongarch/include/asm/asmmacro.h
+++ b/arch/loongarch/include/asm/asmmacro.h
@@ -14,7 +14,7 @@
#ifdef CONFIG_64BIT
#define TASK_STRUCT_OFFSET 0
#else
-#define TASK_STRUCT_OFFSET 2000
+#define TASK_STRUCT_OFFSET 2040
#endif

.macro cpu_save_nonscratch thread
diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c
index 8f650c9ffecd..873e90990771 100644
--- a/arch/loongarch/kernel/acpi.c
+++ b/arch/loongarch/kernel/acpi.c
@@ -201,10 +201,12 @@ static void __init acpi_process_madt(void)
}

int pptt_enabled;
+static int acpi_nr_packages;
+static int acpi_package_ids[MAX_PACKAGES];

int __init parse_acpi_topology(void)
{
- int cpu, topology_id;
+ int i, cpu, topology_id;

for_each_possible_cpu(cpu) {
topology_id = find_acpi_cpu_topology(cpu, 0);
@@ -222,6 +224,29 @@ int __init parse_acpi_topology(void)

cpu_data[cpu].core = topology_id;
}
+
+ topology_id = find_acpi_cpu_topology_package(cpu);
+ if (topology_id < 0) {
+ pr_warn("Invalid BIOS PPTT\n");
+ return -ENOENT;
+ }
+
+ for (i = 0; i < acpi_nr_packages; i++)
+ if (acpi_package_ids[i] == topology_id)
+ break;
+
+ if (i == acpi_nr_packages)
+ acpi_package_ids[acpi_nr_packages++] = topology_id;
+
+ cpu_data[cpu].package = topology_id;
+ }
+
+ for_each_possible_cpu(cpu) {
+ for (i = 0; i < acpi_nr_packages; i++)
+ if (cpu_data[cpu].package == acpi_package_ids[i]) {
+ cpu_data[cpu].package = i; /* Canonicalize */
+ break;
+ }
}

pptt_enabled = 1;
diff --git a/arch/loongarch/kernel/kgdb.c b/arch/loongarch/kernel/kgdb.c
index 17664a6043b1..e7b59f8a4b05 100644
--- a/arch/loongarch/kernel/kgdb.c
+++ b/arch/loongarch/kernel/kgdb.c
@@ -252,7 +252,8 @@ static int kgdb_loongarch_notify(struct notifier_block *self, unsigned long cmd,
if (atomic_read(&kgdb_active) != -1)
kgdb_nmicallback(smp_processor_id(), regs);

- if (kgdb_handle_exception(args->trapnr, args->signr, cmd, regs))
+ if (kgdb_handle_exception(regs->csr_era == stepped_address ? 0 : args->trapnr,
+ args->signr, cmd, regs))
return NOTIFY_DONE;

if (atomic_read(&kgdb_setting_breakpoint))
diff --git a/arch/loongarch/kernel/machine_kexec.c b/arch/loongarch/kernel/machine_kexec.c
index d7fafda1d541..1883cae93bc3 100644
--- a/arch/loongarch/kernel/machine_kexec.c
+++ b/arch/loongarch/kernel/machine_kexec.c
@@ -42,6 +42,7 @@ static unsigned long first_ind_entry;
int machine_kexec_prepare(struct kimage *kimage)
{
int i;
+ char head[8];
char *bootloader = "kexec";
void *cmdline_ptr = (void *)KEXEC_CMDLINE_ADDR;

@@ -59,7 +60,9 @@ int machine_kexec_prepare(struct kimage *kimage)
} else {
/* Find the command line */
for (i = 0; i < kimage->nr_segments; i++) {
- if (!strncmp(bootloader, (char __user *)kimage->segment[i].buf, strlen(bootloader))) {
+ if (copy_from_user(head, kimage->segment[i].buf, strlen(bootloader)))
+ continue;
+ if (!strncmp(bootloader, head, strlen(bootloader))) {
if (!copy_from_user(cmdline_ptr, kimage->segment[i].buf, COMMAND_LINE_SIZE))
kimage->arch.cmdline_ptr = (unsigned long)cmdline_ptr;
break;
diff --git a/arch/loongarch/kernel/rethook_trampoline.S b/arch/loongarch/kernel/rethook_trampoline.S
index d4ceb2fa2a5c..2e009fbea53f 100644
--- a/arch/loongarch/kernel/rethook_trampoline.S
+++ b/arch/loongarch/kernel/rethook_trampoline.S
@@ -71,27 +71,27 @@
cfi_ld s7, PT_R30
cfi_ld s8, PT_R31
LONG_L t0, sp, PT_CRMD
- li.d t1, 0x7 /* mask bit[1:0] PLV, bit[2] IE */
+ LONG_LI t1, 0x7 /* mask bit[1:0] PLV, bit[2] IE */
csrxchg t0, t1, LOONGARCH_CSR_CRMD
.endm

SYM_CODE_START(arch_rethook_trampoline)
UNWIND_HINT_UNDEFINED
- addi.d sp, sp, -PT_SIZE
+ PTR_ADDI sp, sp, -PT_SIZE
save_all_base_regs

- addi.d t0, sp, PT_SIZE
- LONG_S t0, sp, PT_R3
+ PTR_ADDI t0, sp, PT_SIZE
+ LONG_S t0, sp, PT_R3

- move a0, sp /* pt_regs */
+ move a0, sp /* pt_regs */

- bl arch_rethook_trampoline_callback
+ bl arch_rethook_trampoline_callback

/* use the result as the return-address */
- move ra, a0
+ move ra, a0

restore_all_base_regs
- addi.d sp, sp, PT_SIZE
+ PTR_ADDI sp, sp, PT_SIZE

- jr ra
+ jr ra
SYM_CODE_END(arch_rethook_trampoline)
diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
index bfc6f274e6e2..d37e2439f4f2 100644
--- a/arch/loongarch/kernel/setup.c
+++ b/arch/loongarch/kernel/setup.c
@@ -602,6 +602,7 @@ void __init setup_arch(char **cmdline_p)
memblock_init();
pagetable_init();
bootcmdline_init(cmdline_p);
+ jump_label_init(); /* Initialise the static keys for early params */
parse_early_param();
reserve_initrd_mem();

@@ -609,8 +610,6 @@ void __init setup_arch(char **cmdline_p)
arch_mem_init(cmdline_p);

resource_init();
- jump_label_init(); /* Initialise the static keys for paravirtualization */
-
#ifdef CONFIG_SMP
plat_smp_setup();
prefill_possible_map();
diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c
index b077b2aa1ab3..3d16f8311dce 100644
--- a/arch/loongarch/kernel/smp.c
+++ b/arch/loongarch/kernel/smp.c
@@ -425,10 +425,10 @@ void loongson_init_secondary(void)
numa_add_cpu(cpu);
#endif
per_cpu(cpu_state, cpu) = CPU_ONLINE;
- cpu_data[cpu].package =
- cpu_logical_map(cpu) / loongson_sysconf.cores_per_package;
cpu_data[cpu].core = pptt_enabled ? cpu_data[cpu].core :
cpu_logical_map(cpu) % loongson_sysconf.cores_per_package;
+ cpu_data[cpu].package = pptt_enabled ? cpu_data[cpu].package :
+ cpu_logical_map(cpu) / loongson_sysconf.cores_per_package;
cpu_data[cpu].global_id = cpu_logical_map(cpu);
}

diff --git a/arch/loongarch/mm/init.c b/arch/loongarch/mm/init.c
index 031b39eb081c..687980b6e91f 100644
--- a/arch/loongarch/mm/init.c
+++ b/arch/loongarch/mm/init.c
@@ -119,12 +119,13 @@ int arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *params)
return ret;
}

-void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap)
+void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap,
+ struct dev_pagemap *pgmap)
{
unsigned long start_pfn = start >> PAGE_SHIFT;
unsigned long nr_pages = size >> PAGE_SHIFT;

- __remove_pages(start_pfn, nr_pages, altmap);
+ __remove_pages(start_pfn, nr_pages, altmap, pgmap);
}
#endif

diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c
index e4966c178abc..4933869b2c11 100644
--- a/arch/loongarch/net/bpf_jit.c
+++ b/arch/loongarch/net/bpf_jit.c
@@ -834,7 +834,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext
move_reg(ctx, t1, src);
emit_sext_32(ctx, t1, is32);
emit_insn(ctx, divd, dst, dst, t1);
- emit_sext_32(ctx, dst, is32);
+ emit_zext_32(ctx, dst, is32);
}
break;

@@ -851,7 +851,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext
emit_sext_32(ctx, t1, is32);
emit_sext_32(ctx, dst, is32);
emit_insn(ctx, divd, dst, dst, t1);
- emit_sext_32(ctx, dst, is32);
+ emit_zext_32(ctx, dst, is32);
}
break;

@@ -869,7 +869,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext
move_reg(ctx, t1, src);
emit_sext_32(ctx, t1, is32);
emit_insn(ctx, modd, dst, dst, t1);
- emit_sext_32(ctx, dst, is32);
+ emit_zext_32(ctx, dst, is32);
}
break;

@@ -886,7 +886,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext
emit_sext_32(ctx, t1, is32);
emit_sext_32(ctx, dst, is32);
emit_insn(ctx, modd, dst, dst, t1);
- emit_sext_32(ctx, dst, is32);
+ emit_zext_32(ctx, dst, is32);
}
break;

@@ -2347,6 +2347,7 @@ void bpf_jit_free(struct bpf_prog *prog)
*/
if (jit_data) {
bpf_jit_binary_pack_finalize(jit_data->ro_header, jit_data->header);
+ kvfree(jit_data->ctx.offset);
kfree(jit_data);
}
hdr = bpf_jit_binary_pack_hdr(prog);
diff --git a/arch/m68k/include/asm/page_mm.h b/arch/m68k/include/asm/page_mm.h
index ed782609ca41..0971a0651d49 100644
--- a/arch/m68k/include/asm/page_mm.h
+++ b/arch/m68k/include/asm/page_mm.h
@@ -55,10 +55,12 @@ static inline void clear_page(void *page)
#define clear_user_page(addr, vaddr, page) \
do { clear_page(addr); \
flush_dcache_page(page); \
+ (void)(vaddr); \
} while (0)
#define copy_user_page(to, from, vaddr, page) \
do { copy_page(to, from); \
flush_dcache_page(page); \
+ (void)(vaddr); \
} while (0)

extern unsigned long m68k_memoffset;
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index b4472288e0d4..11145c40183d 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -376,6 +376,47 @@ void vtime_task_switch(struct task_struct *prev)
acct->starttime = acct0->starttime;
}
}
+
+/**
+ * vtime_reset - Fast forward vtime entry clocks
+ *
+ * Called from dynticks idle IRQ entry to fast-forward the clocks to current time
+ * so that the IRQ time is still accounted by vtime while nohz cputime is paused.
+ */
+void vtime_reset(void)
+{
+ struct cpu_accounting_data *acct = get_accounting(current);
+
+ acct->starttime = mftb();
+#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
+ acct->startspurr = read_spurr(acct->starttime);
+#endif
+}
+
+#ifdef CONFIG_NO_HZ_COMMON
+/**
+ * vtime_dyntick_start - Inform vtime about entry to idle-dynticks
+ *
+ * Called when idle enters in dyntick mode. The idle cputime that elapsed so far
+ * is accumulated and the tick subsystem takes over the idle cputime accounting.
+ */
+void vtime_dyntick_start(void)
+{
+ vtime_account_idle(current);
+}
+
+/**
+ * vtime_dyntick_stop - Inform vtime about exit from idle-dynticks
+ *
+ * Called when idle exits from dyntick mode. The vtime entry clocks are
+ * fast-forward to current time so that idle accounting restarts elapsing from
+ * now.
+ */
+void vtime_dyntick_stop(void)
+{
+ vtime_reset();
+}
+#endif /* CONFIG_NO_HZ_COMMON */
#endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */

void __no_kcsan __delay(unsigned long loops)
@@ -892,6 +933,7 @@ static void __init set_decrementer_max(void)
static void __init init_decrementer_clockevent(void)
{
register_decrementer_clockevent(smp_processor_id());
+ vtime_reset();
}

void secondary_cpu_time_init(void)
@@ -907,6 +949,7 @@ void secondary_cpu_time_init(void)
/* FIME: Should make unrelated change to move snapshot_timebase
* call here ! */
register_decrementer_clockevent(smp_processor_id());
+ vtime_reset();
}

/*
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index 648d0c5602ec..4c1afab91996 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -158,12 +158,13 @@ int __ref arch_add_memory(int nid, u64 start, u64 size,
return rc;
}

-void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap)
+void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap,
+ struct dev_pagemap *pgmap)
{
unsigned long start_pfn = start >> PAGE_SHIFT;
unsigned long nr_pages = size >> PAGE_SHIFT;

- __remove_pages(start_pfn, nr_pages, altmap);
+ __remove_pages(start_pfn, nr_pages, altmap, pgmap);
arch_remove_linear_mapping(start, size);
}
#endif
diff --git a/arch/powerpc/platforms/85xx/common.c b/arch/powerpc/platforms/85xx/common.c
index 757811155587..c11deb2f50ed 100644
--- a/arch/powerpc/platforms/85xx/common.c
+++ b/arch/powerpc/platforms/85xx/common.c
@@ -42,6 +42,8 @@ static const struct of_device_id mpc85xx_common_ids[] __initconst = {
{ .compatible = "fsl,qoriq-pcie-v2.3", },
{ .compatible = "fsl,qoriq-pcie-v2.2", },
{ .compatible = "fsl,fman", },
+ /* IFC NAND and NOR controllers */
+ { .compatible = "fsl,ifc", },
{},
};

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index c5754942cf85..e73f556720c1 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -155,7 +155,7 @@ config RISCV
select HAVE_DEBUG_KMEMLEAK
select HAVE_DMA_CONTIGUOUS if MMU
select HAVE_DYNAMIC_FTRACE if MMU && (CLANG_SUPPORTS_DYNAMIC_FTRACE || GCC_SUPPORTS_DYNAMIC_FTRACE)
- select FUNCTION_ALIGNMENT_4B if HAVE_DYNAMIC_FTRACE && RISCV_ISA_C
+ select FUNCTION_ALIGNMENT_4B if DYNAMIC_FTRACE && RISCV_ISA_C
select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS if HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS
select HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS if (DYNAMIC_FTRACE_WITH_ARGS && !CFI)
select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE
diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h
index 75b0a951c1bc..97e42645cbab 100644
--- a/arch/riscv/include/asm/kvm_host.h
+++ b/arch/riscv/include/asm/kvm_host.h
@@ -207,13 +207,13 @@ struct kvm_vcpu_arch {
/*
* VCPU interrupts
*
- * We have a lockless approach for tracking pending VCPU interrupts
- * implemented using atomic bitops. The irqs_pending bitmap represent
- * pending interrupts whereas irqs_pending_mask represent bits changed
- * in irqs_pending. Our approach is modeled around multiple producer
- * and single consumer problem where the consumer is the VCPU itself.
+ * The irqs_pending bitmap represents pending interrupts whereas
+ * irqs_pending_mask represents bits changed in irqs_pending. Updates
+ * to these bitmaps are serialized so vcpu interrupt sync/flush cannot
+ * drop a newly injected interrupt while syncing guest-visible HVIP.
*/
#define KVM_RISCV_VCPU_NR_IRQS 64
+ raw_spinlock_t irqs_pending_lock;
DECLARE_BITMAP(irqs_pending, KVM_RISCV_VCPU_NR_IRQS);
DECLARE_BITMAP(irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS);

diff --git a/arch/riscv/kernel/sys_hwprobe.c b/arch/riscv/kernel/sys_hwprobe.c
index 1659d31fd288..caf6762427c8 100644
--- a/arch/riscv/kernel/sys_hwprobe.c
+++ b/arch/riscv/kernel/sys_hwprobe.c
@@ -450,6 +450,7 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
if (cpusetsize > cpumask_size())
cpusetsize = cpumask_size();

+ cpumask_clear(&cpus);
ret = copy_from_user(&cpus, cpus_user, cpusetsize);
if (ret)
return -EFAULT;
diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c
index 5ec503288555..5e92fe14f79a 100644
--- a/arch/riscv/kvm/aia.c
+++ b/arch/riscv/kvm/aia.c
@@ -51,12 +51,15 @@ void kvm_riscv_vcpu_aia_flush_interrupts(struct kvm_vcpu *vcpu)
struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
unsigned long mask, val;

+ lockdep_assert_held(&vcpu->arch.irqs_pending_lock);
+
if (!kvm_riscv_aia_available())
return;

- if (READ_ONCE(vcpu->arch.irqs_pending_mask[1])) {
- mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[1], 0);
- val = READ_ONCE(vcpu->arch.irqs_pending[1]) & mask;
+ mask = vcpu->arch.irqs_pending_mask[1];
+ if (mask) {
+ vcpu->arch.irqs_pending_mask[1] = 0;
+ val = vcpu->arch.irqs_pending[1] & mask;

csr->hviph &= ~mask;
csr->hviph |= val;
@@ -67,6 +70,8 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu)
{
struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;

+ lockdep_assert_held(&vcpu->arch.irqs_pending_lock);
+
if (kvm_riscv_aia_available())
csr->vsieh = ncsr_read(CSR_VSIEH);
}
@@ -75,13 +80,22 @@ void kvm_riscv_vcpu_aia_sync_interrupts(struct kvm_vcpu *vcpu)
bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask)
{
unsigned long seip;
+#ifdef CONFIG_32BIT
+ unsigned long flags;
+ bool pending;
+#endif

if (!kvm_riscv_aia_available())
return false;

#ifdef CONFIG_32BIT
- if (READ_ONCE(vcpu->arch.irqs_pending[1]) &
- (vcpu->arch.aia_context.guest_csr.vsieh & upper_32_bits(mask)))
+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+ pending = vcpu->arch.irqs_pending[1] &
+ (vcpu->arch.aia_context.guest_csr.vsieh &
+ upper_32_bits(mask));
+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+
+ if (pending)
return true;
#endif

@@ -205,6 +219,9 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu,
{
struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
unsigned long regs_max = sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long);
+#ifdef CONFIG_32BIT
+ unsigned long flags;
+#endif

if (!riscv_isa_extension_available(vcpu->arch.isa, SSAIA))
return -ENOENT;
@@ -217,8 +234,12 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu,
((unsigned long *)csr)[reg_num] = val;

#ifdef CONFIG_32BIT
- if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph))
- WRITE_ONCE(vcpu->arch.irqs_pending_mask[1], 0);
+ if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) {
+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+ vcpu->arch.irqs_pending_mask[1] = 0;
+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock,
+ flags);
+ }
#endif
}

diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
index a73690eda84b..61b3029080b1 100644
--- a/arch/riscv/kvm/vcpu.c
+++ b/arch/riscv/kvm/vcpu.c
@@ -80,6 +80,7 @@ static void kvm_riscv_vcpu_context_reset(struct kvm_vcpu *vcpu,

static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset)
{
+ unsigned long flags;
bool loaded;

/**
@@ -104,8 +105,10 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset)

kvm_riscv_vcpu_aia_reset(vcpu);

+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
bitmap_zero(vcpu->arch.irqs_pending, KVM_RISCV_VCPU_NR_IRQS);
bitmap_zero(vcpu->arch.irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS);
+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);

kvm_riscv_vcpu_pmu_reset(vcpu);

@@ -151,6 +154,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)

/* Setup VCPU hfence queue */
spin_lock_init(&vcpu->arch.hfence_lock);
+ raw_spin_lock_init(&vcpu->arch.irqs_pending_lock);

spin_lock_init(&vcpu->arch.reset_state.lock);

@@ -352,10 +356,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu)
{
struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
unsigned long mask, val;
+ unsigned long flags;

- if (READ_ONCE(vcpu->arch.irqs_pending_mask[0])) {
- mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[0], 0);
- val = READ_ONCE(vcpu->arch.irqs_pending[0]) & mask;
+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+
+ mask = vcpu->arch.irqs_pending_mask[0];
+ if (mask) {
+ vcpu->arch.irqs_pending_mask[0] = 0;
+ val = vcpu->arch.irqs_pending[0] & mask;

csr->hvip &= ~mask;
csr->hvip |= val;
@@ -363,11 +371,14 @@ void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu)

/* Flush AIA high interrupts */
kvm_riscv_vcpu_aia_flush_interrupts(vcpu);
+
+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
}

void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu)
{
unsigned long hvip;
+ unsigned long flags;
struct kvm_vcpu_arch *v = &vcpu->arch;
struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;

@@ -376,34 +387,41 @@ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu)

/* Sync-up HVIP.VSSIP bit changes does by Guest */
hvip = ncsr_read(CSR_HVIP);
+
+ raw_spin_lock_irqsave(&v->irqs_pending_lock, flags);
+
if ((csr->hvip ^ hvip) & (1UL << IRQ_VS_SOFT)) {
if (hvip & (1UL << IRQ_VS_SOFT)) {
- if (!test_and_set_bit(IRQ_VS_SOFT,
- v->irqs_pending_mask))
- set_bit(IRQ_VS_SOFT, v->irqs_pending);
+ if (!__test_and_set_bit(IRQ_VS_SOFT,
+ v->irqs_pending_mask))
+ __set_bit(IRQ_VS_SOFT, v->irqs_pending);
} else {
- if (!test_and_set_bit(IRQ_VS_SOFT,
- v->irqs_pending_mask))
- clear_bit(IRQ_VS_SOFT, v->irqs_pending);
+ if (!__test_and_set_bit(IRQ_VS_SOFT,
+ v->irqs_pending_mask))
+ __clear_bit(IRQ_VS_SOFT, v->irqs_pending);
}
}

/* Sync up the HVIP.LCOFIP bit changes (only clear) by the guest */
if ((csr->hvip ^ hvip) & (1UL << IRQ_PMU_OVF)) {
if (!(hvip & (1UL << IRQ_PMU_OVF)) &&
- !test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask))
- clear_bit(IRQ_PMU_OVF, v->irqs_pending);
+ !__test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask))
+ __clear_bit(IRQ_PMU_OVF, v->irqs_pending);
}

/* Sync-up AIA high interrupts */
kvm_riscv_vcpu_aia_sync_interrupts(vcpu);

+ raw_spin_unlock_irqrestore(&v->irqs_pending_lock, flags);
+
/* Sync-up timer CSRs */
kvm_riscv_vcpu_timer_sync(vcpu);
}

int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
{
+ unsigned long flags;
+
/*
* We only allow VS-mode software, timer, and external
* interrupts when irq is one of the local interrupts
@@ -416,9 +434,10 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
irq != IRQ_PMU_OVF)
return -EINVAL;

- set_bit(irq, vcpu->arch.irqs_pending);
- smp_mb__before_atomic();
- set_bit(irq, vcpu->arch.irqs_pending_mask);
+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+ __set_bit(irq, vcpu->arch.irqs_pending);
+ __set_bit(irq, vcpu->arch.irqs_pending_mask);
+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);

kvm_vcpu_kick(vcpu);

@@ -427,6 +446,8 @@ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)

int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
{
+ unsigned long flags;
+
/*
* We only allow VS-mode software, timer, counter overflow and external
* interrupts when irq is one of the local interrupts
@@ -439,26 +460,33 @@ int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
irq != IRQ_PMU_OVF)
return -EINVAL;

- clear_bit(irq, vcpu->arch.irqs_pending);
- smp_mb__before_atomic();
- set_bit(irq, vcpu->arch.irqs_pending_mask);
+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+ __clear_bit(irq, vcpu->arch.irqs_pending);
+ __set_bit(irq, vcpu->arch.irqs_pending_mask);
+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);

return 0;
}

bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, u64 mask)
{
+ unsigned long flags;
unsigned long ie;
+ bool ret;

+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
ie = ((vcpu->arch.guest_csr.vsie & VSIP_VALID_MASK)
<< VSIP_TO_HVIP_SHIFT) & (unsigned long)mask;
ie |= vcpu->arch.guest_csr.vsie & ~IRQ_LOCAL_MASK &
(unsigned long)mask;
- if (READ_ONCE(vcpu->arch.irqs_pending[0]) & ie)
- return true;
+ ret = vcpu->arch.irqs_pending[0] & ie;
+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);

/* Check AIA high interrupts */
- return kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask);
+ if (!ret)
+ ret = kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask);
+
+ return ret;
}

void __kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu)
diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c
index bb920e8923c9..2031beb9bba6 100644
--- a/arch/riscv/kvm/vcpu_onereg.c
+++ b/arch/riscv/kvm/vcpu_onereg.c
@@ -298,6 +298,7 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu,
{
struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
unsigned long regs_max = sizeof(struct kvm_riscv_csr) / sizeof(unsigned long);
+ unsigned long flags;

if (reg_num >= regs_max)
return -ENOENT;
@@ -311,8 +312,11 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu,

((unsigned long *)csr)[reg_num] = reg_val;

- if (reg_num == KVM_REG_RISCV_CSR_REG(sip))
- WRITE_ONCE(vcpu->arch.irqs_pending_mask[0], 0);
+ if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) {
+ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+ vcpu->arch.irqs_pending_mask[0] = 0;
+ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+ }

return 0;
}
diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
index fa8d2f6f554b..885f1db4e9bf 100644
--- a/arch/riscv/mm/init.c
+++ b/arch/riscv/mm/init.c
@@ -1742,9 +1742,10 @@ int __ref arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *param
return ret;
}

-void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap)
+void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap,
+ struct dev_pagemap *pgmap)
{
- __remove_pages(start >> PAGE_SHIFT, size >> PAGE_SHIFT, altmap);
+ __remove_pages(start >> PAGE_SHIFT, size >> PAGE_SHIFT, altmap, pgmap);
remove_linear_mapping(start, size);
flush_tlb_all();
}
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index bd0df61d1907..2b989bebd220 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -65,6 +65,7 @@ ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier);
EXPORT_SYMBOL(s390_epoch_delta_notifier);

unsigned char ptff_function_mask[16];
+EXPORT_SYMBOL(ptff_function_mask);

static unsigned long lpar_offset;
static unsigned long initial_leap_seconds;
diff --git a/arch/s390/lib/csum-partial.c b/arch/s390/lib/csum-partial.c
index 458abd9bac70..9d74ceff136c 100644
--- a/arch/s390/lib/csum-partial.c
+++ b/arch/s390/lib/csum-partial.c
@@ -23,7 +23,7 @@ static __always_inline __wsum csum_copy(void *dst, const void *src, int len, __w
if (!cpu_has_vx()) {
if (copy)
memcpy(dst, src, len);
- return cksm(dst, len, sum);
+ return cksm(src, len, sum);
}
kernel_fpu_begin(&vxstate, KERNEL_VXR_V16V23);
fpu_vlvgf(16, (__force u32)sum, 1);
diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c
index 1f72efc2a579..11a689423440 100644
--- a/arch/s390/mm/init.c
+++ b/arch/s390/mm/init.c
@@ -276,12 +276,13 @@ int arch_add_memory(int nid, u64 start, u64 size,
return rc;
}

-void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap)
+void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap,
+ struct dev_pagemap *pgmap)
{
unsigned long start_pfn = start >> PAGE_SHIFT;
unsigned long nr_pages = size >> PAGE_SHIFT;

- __remove_pages(start_pfn, nr_pages, altmap);
+ __remove_pages(start_pfn, nr_pages, altmap, pgmap);
vmem_remove_mapping(start, size);
}
#endif /* CONFIG_MEMORY_HOTPLUG */
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 07e0e64b9a98..06934f9691d6 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -27,6 +27,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
KBUILD_CFLAGS := -m$(BITS) -O2 $(CLANG_FLAGS)
KBUILD_CFLAGS += $(CC_FLAGS_DIALECT)
KBUILD_CFLAGS += -fno-strict-aliasing -fPIE
+KBUILD_CFLAGS += -fno-jump-tables
KBUILD_CFLAGS += -Wundef
KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
cflags-$(CONFIG_X86_32) := -march=i386
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index d806abaeda3d..3a0eef95e905 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -2052,7 +2052,7 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn)
if (apic_lvtt_tscdeadline(apic) || ktimer->hv_timer_in_use)
ktimer->expired_tscdeadline = ktimer->tscdeadline;

- if (!from_timer_fn && apic->apicv_active) {
+ if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) {
WARN_ON(kvm_get_running_vcpu() != vcpu);
kvm_apic_inject_pending_timer_irqs(apic);
return;
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 0c2e35e3ffbd..34a312705253 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -4837,16 +4837,17 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
if (r != RET_PF_CONTINUE)
return r;

- r = RET_PF_RETRY;
write_lock(&vcpu->kvm->mmu_lock);

- if (is_page_fault_stale(vcpu, fault))
- goto out_unlock;
-
r = make_mmu_pages_available(vcpu);
if (r)
goto out_unlock;

+ if (is_page_fault_stale(vcpu, fault)) {
+ r = RET_PF_RETRY;
+ goto out_unlock;
+ }
+
r = direct_map(vcpu, fault);

out_unlock:
@@ -7501,7 +7502,9 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen)
static void mmu_destroy_caches(void)
{
kmem_cache_destroy(pte_list_desc_cache);
+ pte_list_desc_cache = NULL;
kmem_cache_destroy(mmu_page_header_cache);
+ mmu_page_header_cache = NULL;
}

static void kvm_wake_nx_recovery_thread(struct kvm *kvm)
diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h
index 901cd2bd40b8..6465de820e70 100644
--- a/arch/x86/kvm/mmu/paging_tmpl.h
+++ b/arch/x86/kvm/mmu/paging_tmpl.h
@@ -827,15 +827,17 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
}
#endif

- r = RET_PF_RETRY;
write_lock(&vcpu->kvm->mmu_lock);

- if (is_page_fault_stale(vcpu, fault))
- goto out_unlock;
-
r = make_mmu_pages_available(vcpu);
if (r)
goto out_unlock;
+
+ if (is_page_fault_stale(vcpu, fault)) {
+ r = RET_PF_RETRY;
+ goto out_unlock;
+ }
+
r = FNAME(fetch)(vcpu, fault, &walker);

out_unlock:
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 2544c551e702..d8eff8f11c5f 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -566,7 +566,12 @@ static int svm_enable_virtualization_cpu(void)
return r;

sd = per_cpu_ptr(&svm_data, me);
- sd->asid_generation = 1;
+ /*
+ * Bump the current asid_generation value to ensure any vCPU that
+ * previously ran on this CPU sees a stale generation and is forced
+ * to acquire a new ASID, preventing a latent ASID collision.
+ */
+ sd->asid_generation++;
sd->max_asid = cpuid_ebx(SVM_CPUID_FUNC) - 1;
sd->next_asid = sd->max_asid + 1;
sd->min_asid = max_sev_asid + 1;
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 52ab52b0e1cc..170b0c4d865f 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -336,6 +336,7 @@ static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu)
static void free_nested(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
+ struct vmcs *shadow_vmcs;

if (WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01))
vmx_switch_vmcs(vcpu, &vmx->vmcs01);
@@ -353,9 +354,15 @@ static void free_nested(struct kvm_vcpu *vcpu)
vmx->nested.current_vmptr = INVALID_GPA;
if (enable_shadow_vmcs) {
vmx_disable_shadow_vmcs(vmx);
- vmcs_clear(vmx->vmcs01.shadow_vmcs);
- free_vmcs(vmx->vmcs01.shadow_vmcs);
+
+ /*
+ * Keep the pointer visible until after VMCLEAR, so migration
+ * can clear an active shadow VMCS on the old CPU.
+ */
+ shadow_vmcs = vmx->vmcs01.shadow_vmcs;
+ vmcs_clear(shadow_vmcs);
vmx->vmcs01.shadow_vmcs = NULL;
+ free_vmcs(shadow_vmcs);
}
kfree(vmx->nested.cached_vmcs12);
vmx->nested.cached_vmcs12 = NULL;
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 7e20b22d658b..d57f29ca23a5 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -1300,12 +1300,13 @@ kernel_physical_mapping_remove(unsigned long start, unsigned long end)
remove_pagetable(start, end, true, NULL);
}

-void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap)
+void __ref arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap,
+ struct dev_pagemap *pgmap)
{
unsigned long start_pfn = start >> PAGE_SHIFT;
unsigned long nr_pages = size >> PAGE_SHIFT;

- __remove_pages(start_pfn, nr_pages, altmap);
+ __remove_pages(start_pfn, nr_pages, altmap, pgmap);
kernel_physical_mapping_remove(start, start + size);
}
#endif /* CONFIG_MEMORY_HOTPLUG */
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c
index 5315466f137b..f6fece060dfe 100644
--- a/drivers/accel/amdxdna/amdxdna_ctx.c
+++ b/drivers/accel/amdxdna/amdxdna_ctx.c
@@ -483,6 +483,7 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job)
amdxdna_arg_bos_put(job);
amdxdna_gem_put_obj(job->cmd_bo);
dma_fence_put(job->fence);
+ mmdrop(job->mm);
}

int amdxdna_cmd_submit(struct amdxdna_client *client,
@@ -549,6 +550,7 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,

job->hwctx = hwctx;
job->mm = current->mm;
+ mmgrab(job->mm);

job->fence = amdxdna_fence_create(hwctx);
if (!job->fence) {
@@ -583,6 +585,8 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
cmd_put:
amdxdna_gem_put_obj(job->cmd_bo);
free_job:
+ if (job->mm)
+ mmdrop(job->mm);
kfree(job);
return ret;
}
diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c
index 3401883e207f..d50fed64d4d9 100644
--- a/drivers/accel/ethosu/ethosu_gem.c
+++ b/drivers/accel/ethosu/ethosu_gem.c
@@ -192,9 +192,17 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
return len;
}

-static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
+static bool feat_matrix_chained(struct ethosu_device *edev, struct feat_matrix *fm)
+{
+ u32 storage = fm->precision >> 14;
+
+ return !ethosu_is_u65(edev) && storage == 2;
+}
+
+static u64 feat_matrix_length(struct ethosu_device *edev,
+ struct ethosu_validated_cmdstream_info *info,
struct feat_matrix *fm,
- u32 x, u32 y, u32 c)
+ u32 x, u32 y, u32 c, bool ofm)
{
u32 element_size, storage = fm->precision >> 14;
int tile = 0;
@@ -203,6 +211,9 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
if (fm->region < 0)
return U64_MAX;

+ if (feat_matrix_chained(edev, fm))
+ return 0;
+
switch (storage) {
case 0:
if (x >= fm->width0 + 1) {
@@ -223,6 +234,8 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
tile = 1;
}
break;
+ default:
+ return U64_MAX;
}
if (fm->base[tile] == U64_MAX)
return U64_MAX;
@@ -231,10 +244,11 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,

switch ((fm->precision >> 6) & 0x3) { // format
case 0: //nhwc:
- addr += x * fm->stride_x + c;
+ element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3);
+ addr += x * fm->stride_x + c * element_size;
break;
case 1: //nhcwb16:
- element_size = BIT((fm->precision >> 1) & 0x3);
+ element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3);

addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size;
break;
@@ -250,6 +264,7 @@ static int calc_sizes(struct drm_device *ddev,
u16 op, struct cmd_state *st,
bool ifm, bool ifm2, bool weight, bool scale)
{
+ struct ethosu_device *edev = to_ethosu_device(ddev);
u64 len;

if (ifm) {
@@ -267,8 +282,8 @@ static int calc_sizes(struct drm_device *ddev,
if (ifm_height < 0 || ifm_width < 0)
return -EINVAL;

- len = feat_matrix_length(info, &st->ifm, ifm_width,
- ifm_height, st->ifm.depth);
+ len = feat_matrix_length(edev, info, &st->ifm, ifm_width,
+ ifm_height, st->ifm.depth, false);
dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
op, st->ifm.region, st->ifm.base[0], len);
if (len == U64_MAX)
@@ -276,8 +291,8 @@ static int calc_sizes(struct drm_device *ddev,
}

if (ifm2) {
- len = feat_matrix_length(info, &st->ifm2, st->ifm.depth,
- 0, st->ofm.depth);
+ len = feat_matrix_length(edev, info, &st->ifm2, st->ifm.depth,
+ 0, st->ofm.depth, false);
dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
op, st->ifm2.region, st->ifm2.base[0], len);
if (len == U64_MAX)
@@ -308,13 +323,14 @@ static int calc_sizes(struct drm_device *ddev,
st->scale[0].base + st->scale[0].length);
}

- len = feat_matrix_length(info, &st->ofm, st->ofm.width,
- st->ofm.height[2], st->ofm.depth);
+ len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width,
+ st->ofm.height[2], st->ofm.depth, true);
dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
op, st->ofm.region, st->ofm.base[0], len);
if (len == U64_MAX)
return -EINVAL;
- info->output_region[st->ofm.region] = true;
+ if (!feat_matrix_chained(edev, &st->ofm))
+ info->output_region[st->ofm.region] = true;

return 0;
}
@@ -324,6 +340,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
u16 op, struct cmd_state *st,
bool ifm, bool ifm2)
{
+ struct ethosu_device *edev = to_ethosu_device(ddev);
u32 height, width, depth;
u64 len;

@@ -332,8 +349,8 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width;
depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;

- len = feat_matrix_length(info, &st->ifm, width,
- height, depth);
+ len = feat_matrix_length(edev, info, &st->ifm, width,
+ height, depth, false);
dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
op, st->ifm.region, st->ifm.base[0], len);
if (len == U64_MAX)
@@ -345,21 +362,22 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width;
depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth;

- len = feat_matrix_length(info, &st->ifm2, width,
- height, depth);
+ len = feat_matrix_length(edev, info, &st->ifm2, width,
+ height, depth, false);
dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
op, st->ifm2.region, st->ifm2.base[0], len);
if (len == U64_MAX)
return -EINVAL;
}

- len = feat_matrix_length(info, &st->ofm, st->ofm.width,
- st->ofm.height[2], st->ofm.depth);
+ len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width,
+ st->ofm.height[2], st->ofm.depth, true);
dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
op, st->ofm.region, st->ofm.base[0], len);
if (len == U64_MAX)
return -EINVAL;
- info->output_region[st->ofm.region] = true;
+ if (!feat_matrix_chained(edev, &st->ofm))
+ info->output_region[st->ofm.region] = true;

return 0;
}
diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.c b/drivers/accel/ivpu/ivpu_hw_btrs.c
index 06e65c592618..992addabbd3c 100644
--- a/drivers/accel/ivpu/ivpu_hw_btrs.c
+++ b/drivers/accel/ivpu/ivpu_hw_btrs.c
@@ -861,7 +861,7 @@ static void diagnose_failure_mtl(struct ivpu_device *vdev)

static void diagnose_failure_lnl(struct ivpu_device *vdev)
{
- u32 reg = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK;
+ u32 reg = REGB_RD32(VPU_HW_BTRS_LNL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK;

if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, reg)) {
ivpu_err(vdev, "ATS_ERR_LOG1 0x%08x ATS_ERR_LOG2 0x%08x\n",
diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
index 3b8520813941..bc0ef8927905 100644
--- a/drivers/android/binder/thread.rs
+++ b/drivers/android/binder/thread.rs
@@ -1273,14 +1273,14 @@ fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Resu
inner.extended_error =
ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno());
}
- }

- pr_warn!(
- "{}:{} transaction to {} failed: {err:?}",
- info.from_pid,
- info.from_tid,
- info.to_pid
- );
+ pr_warn!(
+ "{}:{} transaction to {} failed: {err:?}",
+ info.from_pid,
+ info.from_tid,
+ info.to_pid
+ );
+ }
}
}

diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c
index 64cb544903d8..cf723ef39d7c 100644
--- a/drivers/ata/sata_dwc_460ex.c
+++ b/drivers/ata/sata_dwc_460ex.c
@@ -19,7 +19,6 @@
#include <linux/device.h>
#include <linux/dmaengine.h>
#include <linux/of.h>
-#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/libata.h>
@@ -226,7 +225,6 @@ static int sata_dwc_dma_init_old(struct platform_device *pdev,
struct sata_dwc_device *hsdev)
{
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;

hsdev->dma = devm_kzalloc(dev, sizeof(*hsdev->dma), GFP_KERNEL);
if (!hsdev->dma)
@@ -236,11 +234,9 @@ static int sata_dwc_dma_init_old(struct platform_device *pdev,
hsdev->dma->id = pdev->id;

/* Get SATA DMA interrupt number */
- hsdev->dma->irq = irq_of_parse_and_map(np, 1);
- if (!hsdev->dma->irq) {
- dev_err(dev, "no SATA DMA irq\n");
- return -ENODEV;
- }
+ hsdev->dma->irq = platform_get_irq(pdev, 1);
+ if (hsdev->dma->irq < 0)
+ return hsdev->dma->irq;

/* Get physical SATA DMA register base address */
hsdev->dma->regs = devm_platform_ioremap_resource(pdev, 1);
@@ -398,8 +394,7 @@ static void clear_serror(struct ata_port *ap)

static void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit)
{
- sata_dwc_writel(&hsdev->sata_dwc_regs->intpr,
- sata_dwc_readl(&hsdev->sata_dwc_regs->intpr));
+ sata_dwc_writel(&hsdev->sata_dwc_regs->intpr, bit);
}

static u32 qcmd_tag_to_mask(u8 tag)
@@ -612,14 +607,9 @@ static irqreturn_t sata_dwc_isr(int irq, void *dev_instance)
status = ap->ops->sff_check_status(ap);
dev_dbg(ap->dev, "%s ATA status register=0x%x\n", __func__, status);

- tag = 0;
while (tag_mask) {
- while (!(tag_mask & 0x00000001)) {
- tag++;
- tag_mask <<= 1;
- }
-
- tag_mask &= (~0x00000001);
+ tag = __ffs(tag_mask);
+ tag_mask &= ~(1U << tag);
qc = ata_qc_from_tag(ap, tag);
if (unlikely(!qc)) {
dev_err(ap->dev, "failed to get qc");
@@ -1125,7 +1115,6 @@ static const struct ata_port_info sata_dwc_port_info[] = {
static int sata_dwc_probe(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
- struct device_node *np = dev->of_node;
struct sata_dwc_device *hsdev;
u32 idr, versionr;
char *ver = (char *)&versionr;
@@ -1168,18 +1157,13 @@ static int sata_dwc_probe(struct platform_device *ofdev)
/* Save dev for later use in dev_xxx() routines */
hsdev->dev = dev;

- /* Enable SATA Interrupts */
- sata_dwc_enable_interrupts(hsdev);
-
/* Get SATA interrupt number */
- irq = irq_of_parse_and_map(np, 0);
- if (!irq) {
- dev_err(dev, "no SATA DMA irq\n");
- return -ENODEV;
- }
+ irq = platform_get_irq(ofdev, 0);
+ if (irq < 0)
+ return irq;

#ifdef CONFIG_SATA_DWC_OLD_DMA
- if (!of_property_present(np, "dmas")) {
+ if (!of_property_present(dev->of_node, "dmas")) {
err = sata_dwc_dma_init_old(ofdev, hsdev);
if (err)
return err;
@@ -1203,6 +1187,8 @@ static int sata_dwc_probe(struct platform_device *ofdev)
if (err)
dev_err(dev, "failed to activate host");

+ /* Enable SATA Interrupts */
+ sata_dwc_enable_interrupts(hsdev);
return 0;

error_out:
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 6c1e7347e6a7..8b9f84398f92 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -1957,9 +1957,14 @@ static int rbd_object_map_update_finish(struct rbd_obj_request *obj_req,
bool has_current_state;
void *p;

- if (osd_req->r_result)
+ if (osd_req->r_result < 0)
return osd_req->r_result;

+ /*
+ * Writes aren't allowed to return a data payload.
+ */
+ WARN_ON_ONCE(osd_req->r_result > 0);
+
/*
* Nothing to do for a snapshot object map.
*/
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 6c041eaebdb9..802d521a6184 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -19,6 +19,7 @@
#include <linux/errno.h>
#include <linux/major.h>
#include <linux/wait.h>
+#include <linux/wait_bit.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#include <linux/swap.h>
@@ -26,7 +27,6 @@
#include <linux/compat.h>
#include <linux/mutex.h>
#include <linux/writeback.h>
-#include <linux/completion.h>
#include <linux/highmem.h>
#include <linux/sysfs.h>
#include <linux/miscdevice.h>
@@ -327,7 +327,6 @@ struct ublk_device {

struct ublk_params params;

- struct completion completion;
u32 nr_queue_ready;
bool unprivileged_daemons;
struct mutex cancel_mutex;
@@ -3058,12 +3057,12 @@ static void ublk_mark_io_ready(struct ublk_device *ub, u16 q_id,
if (ublk_dev_ready(ub)) {
/*
* All queues ready - clear device-level canceling flag
- * and complete the recovery/initialization.
+ * and wake ublk_dev_ready() waiters.
*/
mutex_lock(&ub->cancel_mutex);
ub->canceling = false;
mutex_unlock(&ub->cancel_mutex);
- complete_all(&ub->completion);
+ wake_up_var(&ub->nr_queue_ready);
}
}

@@ -4266,7 +4265,6 @@ static int ublk_init_queues(struct ublk_device *ub)
goto fail;
}

- init_completion(&ub->completion);
return 0;

fail:
@@ -4410,6 +4408,26 @@ static bool ublk_validate_user_pid(struct ublk_device *ub, pid_t ublksrv_pid)
return ub->ublksrv_tgid == ublksrv_pid;
}

+/*
+ * Wait until all queues have fetched their I/O commands, and return with
+ * ub->mutex held and readiness guaranteed: then every queue's ->canceling
+ * is cleared. Ready may regress between wakeup and mutex_lock() (F_BATCH
+ * UNPREP, daemon death), so re-check it under the mutex and wait again.
+ */
+static int ublk_wait_dev_ready_and_lock(struct ublk_device *ub)
+{
+ while (true) {
+ if (wait_var_event_interruptible(&ub->nr_queue_ready,
+ ublk_dev_ready(ub)))
+ return -EINTR;
+
+ mutex_lock(&ub->mutex);
+ if (ublk_dev_ready(ub))
+ return 0;
+ mutex_unlock(&ub->mutex);
+ }
+}
+
static int ublk_ctrl_start_dev(struct ublk_device *ub,
const struct ublksrv_ctrl_cmd *header)
{
@@ -4492,15 +4510,10 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub,
};
}

- if (wait_for_completion_interruptible(&ub->completion) != 0)
+ if (ublk_wait_dev_ready_and_lock(ub))
return -EINTR;

- if (!ublk_validate_user_pid(ub, ublksrv_pid))
- return -EINVAL;
-
- mutex_lock(&ub->mutex);
- /* device may become not ready in case of F_BATCH */
- if (!ublk_dev_ready(ub)) {
+ if (!ublk_validate_user_pid(ub, ublksrv_pid)) {
ret = -EINVAL;
goto out_unlock;
}
@@ -5064,7 +5077,6 @@ static int ublk_ctrl_start_recovery(struct ublk_device *ub)
goto out_unlock;
}
pr_devel("%s: start recovery for dev id %d\n", __func__, ub->ub_number);
- init_completion(&ub->completion);
ret = 0;
out_unlock:
mutex_unlock(&ub->mutex);
@@ -5080,16 +5092,17 @@ static int ublk_ctrl_end_recovery(struct ublk_device *ub,
pr_devel("%s: Waiting for all FETCH_REQs, dev id %d...\n", __func__,
header->dev_id);

- if (wait_for_completion_interruptible(&ub->completion))
+ if (ublk_wait_dev_ready_and_lock(ub))
return -EINTR;

pr_devel("%s: All FETCH_REQs received, dev id %d\n", __func__,
header->dev_id);

- if (!ublk_validate_user_pid(ub, ublksrv_pid))
- return -EINVAL;
+ if (!ublk_validate_user_pid(ub, ublksrv_pid)) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }

- mutex_lock(&ub->mutex);
if (ublk_nosrv_should_stop_dev(ub))
goto out_unlock;

diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index dda76365726f..fff1dd64383a 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -413,7 +413,7 @@ static int qca_tlv_check_data(struct hci_dev *hdev,

idx = 0;
data = tlv->data;
- while (idx < length - sizeof(struct tlv_type_nvm)) {
+ while (idx + sizeof(struct tlv_type_nvm) <= length) {
tlv_nvm = (struct tlv_type_nvm *)(data + idx);

tag_id = le16_to_cpu(tlv_nvm->tag_id);
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index cc6392f8d98c..7f66e7c6e77a 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -2765,7 +2765,9 @@ static int btusb_setup_realtek(struct hci_dev *hdev)

static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
{
- if (skb->data[0] == HCI_VENDOR_PKT && skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) {
+ if (skb->len >= HCI_EVENT_HDR_SIZE + 1 &&
+ skb->data[0] == HCI_VENDOR_PKT &&
+ skb->data[2] == RTK_SUB_EVENT_CODE_COREDUMP) {
struct rtk_dev_coredump_hdr hdr = {
.code = RTK_DEVCOREDUMP_CODE_MEMDUMP,
};
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 244447195619..bd29d422c209 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1088,6 +1088,10 @@ static void qca_controller_memdump(struct work_struct *work)
if (!(qca_memdump->ram_dump_size)) {
bt_dev_err(hu->hdev, "Rx invalid memdump size");
kfree(qca_memdump);
+ qca->qca_memdump = NULL;
+ qca->memdump_state = QCA_MEMDUMP_COLLECTED;
+ clear_and_wake_up_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
+ clear_bit(QCA_IBS_DISABLED, &qca->flags);
kfree_skb(skb);
mutex_unlock(&qca->hci_memdump_lock);
return;
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 62934cf4b10d..4f1fd389260f 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -3187,6 +3187,7 @@ static noinline int mmc_ioctl_cdrom_volume(struct cdrom_device_info *cdi,

/* set volume */
cgc->buffer = buffer + offset - 8;
+ cgc->buflen -= offset - 8;
memset(cgc->buffer, 0, 8);
return cdrom_mode_select(cdi, cgc);
}
diff --git a/drivers/comedi/drivers/comedi_parport.c b/drivers/comedi/drivers/comedi_parport.c
index 2604680d86c4..57ee3f9dfba2 100644
--- a/drivers/comedi/drivers/comedi_parport.c
+++ b/drivers/comedi/drivers/comedi_parport.c
@@ -211,6 +211,13 @@ static irqreturn_t parport_interrupt(int irq, void *d)
unsigned int ctrl;
unsigned short val = 0;

+ /*
+ * Check device is fully attached. Device interrupts should have
+ * been disabled, but do this in case of bad hardware.
+ */
+ if (!dev->attached)
+ return IRQ_NONE;
+
ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
if (!(ctrl & PARPORT_CTRL_IRQ_ENA))
return IRQ_NONE;
@@ -233,6 +240,9 @@ static int parport_attach(struct comedi_device *dev,
if (ret)
return ret;

+ outb(0, dev->iobase + PARPORT_DATA_REG);
+ outb(0, dev->iobase + PARPORT_CTRL_REG);
+
if (it->options[1]) {
ret = request_irq(it->options[1], parport_interrupt, 0,
dev->board_name, dev);
@@ -288,9 +298,6 @@ static int parport_attach(struct comedi_device *dev,
s->cancel = parport_intr_cancel;
}

- outb(0, dev->iobase + PARPORT_DATA_REG);
- outb(0, dev->iobase + PARPORT_CTRL_REG);
-
return 0;
}

diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index d41067cd1f1b..2debe39fc477 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -2583,6 +2583,9 @@ static void cpufreq_update_pressure(struct cpufreq_policy *policy)

cpu = cpumask_first(policy->related_cpus);
max_freq = arch_scale_freq_ref(cpu);
+ if (!max_freq)
+ max_freq = policy->cpuinfo.max_freq;
+
capped_freq = policy->max;

/*
diff --git a/drivers/crypto/tegra/tegra-se-main.c b/drivers/crypto/tegra/tegra-se-main.c
index e8d8c3a23d7a..d2f518ef9a10 100644
--- a/drivers/crypto/tegra/tegra-se-main.c
+++ b/drivers/crypto/tegra/tegra-se-main.c
@@ -52,7 +52,7 @@ tegra_se_cmdbuf_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_dire
return ERR_PTR(-ENOMEM);

kref_init(&map->ref);
- map->bo = host1x_bo_get(bo);
+ map->bo = bo;
map->direction = direction;
map->dev = dev;

@@ -93,7 +93,6 @@ static void tegra_se_cmdbuf_unpin(struct host1x_bo_mapping *map)
dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0);
sg_free_table(map->sgt);
kfree(map->sgt);
- host1x_bo_put(map->bo);

kfree(map);
}
diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
index 2f94be1df525..2e7b5d4d11f4 100644
--- a/drivers/dma-buf/udmabuf.c
+++ b/drivers/dma-buf/udmabuf.c
@@ -247,21 +247,22 @@ static int begin_cpu_udmabuf(struct dma_buf *buf,
{
struct udmabuf *ubuf = buf->priv;
struct device *dev = ubuf->device->this_device;
- int ret = 0;

if (!ubuf->sg) {
ubuf->sg = get_sg_table(dev, buf, direction);
if (IS_ERR(ubuf->sg)) {
+ int ret;
+
ret = PTR_ERR(ubuf->sg);
ubuf->sg = NULL;
+ return ret;
} else {
ubuf->sg_dir = direction;
}
- } else {
- dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
}

- return ret;
+ dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
+ return 0;
}

static int end_cpu_udmabuf(struct dma_buf *buf,
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 75e3ae0c16d0..8163521188c0 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -557,6 +557,9 @@ dpll_msg_add_pin_ref_sync(struct sk_buff *msg, struct dpll_pin *pin,
if (!dpll_pin_available(ref_sync_pin))
continue;
ref_sync_pin_priv = dpll_pin_on_dpll_priv(dpll, ref_sync_pin);
+ /* Pin may have been unregistered from this dpll already */
+ if (!ref_sync_pin_priv)
+ continue;
if (WARN_ON(!ops->ref_sync_get))
return -EOPNOTSUPP;
ret = ops->ref_sync_get(pin, pin_priv, ref_sync_pin,
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index 82b3b6d9ed2d..2a0727935d73 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -298,31 +298,34 @@ static struct fwnet_fragment_info *fwnet_frag_new(
if (fi->offset + fi->len == offset) {
/* The new fragment can be tacked on to the end */
/* Did the new fragment plug a hole? */
- fi2 = list_entry(fi->fi_link.next,
- struct fwnet_fragment_info, fi_link);
- if (fi->offset + fi->len == fi2->offset) {
- /* glue fragments together */
- fi->len += len + fi2->len;
- list_del(&fi2->fi_link);
- kfree(fi2);
- } else {
- fi->len += len;
+ if (!list_is_last(&fi->fi_link, &pd->fi_list)) {
+ fi2 = list_next_entry(fi, fi_link);
+ if (offset + len == fi2->offset) {
+ /* glue fragments together */
+ fi->len += len + fi2->len;
+ list_del(&fi2->fi_link);
+ kfree(fi2);
+
+ return fi;
+ }
}
+ fi->len += len;

return fi;
}
if (offset + len == fi->offset) {
/* The new fragment can be tacked on to the beginning */
/* Did the new fragment plug a hole? */
- fi2 = list_entry(fi->fi_link.prev,
- struct fwnet_fragment_info, fi_link);
- if (fi2->offset + fi2->len == fi->offset) {
- /* glue fragments together */
- fi2->len += fi->len + len;
- list_del(&fi->fi_link);
- kfree(fi);
-
- return fi2;
+ if (!list_is_first(&fi->fi_link, &pd->fi_list)) {
+ fi2 = list_prev_entry(fi, fi_link);
+ if (fi2->offset + fi2->len == offset) {
+ /* glue fragments together */
+ fi2->len += fi->len + len;
+ list_del(&fi->fi_link);
+ kfree(fi);
+
+ return fi2;
+ }
}
fi->offset = offset;
fi->len += len;
diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index cab32cfdac42..4cbf7537e624 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -32,6 +32,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/mutex.h>
@@ -55,7 +56,9 @@
(FIELD_PREP(SENDER_ID_MASK, (s)) | FIELD_PREP(RECEIVER_ID_MASK, (r)))

#define RXTX_MAP_MIN_BUFSZ_MASK GENMASK(1, 0)
-#define RXTX_MAP_MIN_BUFSZ(x) ((x) & RXTX_MAP_MIN_BUFSZ_MASK)
+#define RXTX_MAP_MAX_BUFSZ_MASK GENMASK(31, 16)
+#define RXTX_MAP_MIN_BUFSZ(x) (FIELD_GET(RXTX_MAP_MIN_BUFSZ_MASK, (x)))
+#define RXTX_MAP_MAX_BUFSZ(x) (FIELD_GET(RXTX_MAP_MAX_BUFSZ_MASK, (x)))

#define FFA_MAX_NOTIFICATIONS 64

@@ -708,30 +711,39 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize,
struct ffa_composite_mem_region *composite;
struct ffa_mem_region_addr_range *constituents;
struct ffa_mem_region_attributes *ep_mem_access;
- u32 idx, frag_len, length, buf_sz = 0, num_entries = sg_nents(args->sg);
+ u32 idx, frag_len, length, buf_sz = 0, num_entries = sg_nents(args->sg), ep_offset;
+ u32 emad_end, emad_size = ffa_emad_size_get(drv_info->version);

mem_region->tag = args->tag;
mem_region->flags = args->flags;
mem_region->sender_id = drv_info->vm_id;
mem_region->attributes = ffa_memory_attributes_get(func_id);
+
+ ffa_mem_region_additional_setup(drv_info->version, mem_region);
composite_offset = ffa_mem_desc_offset(buffer, args->nattrs,
drv_info->version);
+ if (composite_offset + sizeof(*composite) > max_fragsize)
+ return -ENXIO;

for (idx = 0; idx < args->nattrs; idx++) {
- ep_mem_access = buffer +
- ffa_mem_desc_offset(buffer, idx, drv_info->version);
+ ep_offset = ffa_mem_desc_offset(buffer, idx, drv_info->version);
+ if (check_add_overflow(ep_offset, emad_size, &emad_end))
+ return -ENXIO;
+
+ if (emad_end > max_fragsize)
+ return -ENXIO;
+
+ ep_mem_access = buffer + ep_offset;
+ memset(ep_mem_access, 0, emad_size);
ep_mem_access->receiver = args->attrs[idx].receiver;
ep_mem_access->attrs = args->attrs[idx].attrs;
ep_mem_access->composite_off = composite_offset;
- ep_mem_access->flag = 0;
- ep_mem_access->reserved = 0;
ffa_emad_impdef_value_init(drv_info->version,
ep_mem_access->impdef_val,
args->attrs[idx].impdef_val);
}
mem_region->handle = 0;
mem_region->ep_count = args->nattrs;
- ffa_mem_region_additional_setup(drv_info->version, mem_region);

composite = buffer + composite_offset;
composite->total_pg_cnt = ffa_get_num_pages_sg(args->sg);
@@ -764,7 +776,7 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize,
constituents = buffer;
}

- if ((void *)constituents - buffer > max_fragsize) {
+ if ((void *)constituents + sizeof(*constituents) - buffer > max_fragsize) {
pr_err("Memory Region Fragment > Tx Buffer size\n");
return -EFAULT;
}
@@ -773,7 +785,7 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize,
constituents->pg_cnt = args->sg->length / FFA_PAGE_SIZE;
constituents->reserved = 0;
constituents++;
- frag_len += sizeof(struct ffa_mem_region_addr_range);
+ frag_len += sizeof(*constituents);
} while ((args->sg = sg_next(args->sg)));

return ffa_transmit_fragment(func_id, addr, buf_sz, frag_len,
@@ -1134,7 +1146,7 @@ static int ffa_partition_info_get(const char *uuid_str,
uuid_t uuid;
struct ffa_partition_info *pbuf;

- if (uuid_parse(uuid_str, &uuid)) {
+ if (!uuid_str || uuid_parse(uuid_str, &uuid)) {
pr_err("invalid uuid (%s)\n", uuid_str);
return -ENODEV;
}
@@ -2095,7 +2107,7 @@ static int __init ffa_init(void)
{
int ret;
u32 buf_sz;
- size_t rxtx_bufsz = SZ_4K;
+ size_t rxtx_min_bufsz = SZ_4K, rxtx_max_bufsz = 0, rxtx_bufsz;

ret = ffa_transport_init(&invoke_ffa_fn);
if (ret)
@@ -2118,15 +2130,18 @@ static int __init ffa_init(void)
ret = ffa_features(FFA_FN_NATIVE(RXTX_MAP), 0, &buf_sz, NULL);
if (!ret) {
if (RXTX_MAP_MIN_BUFSZ(buf_sz) == 1)
- rxtx_bufsz = SZ_64K;
+ rxtx_min_bufsz = SZ_64K;
else if (RXTX_MAP_MIN_BUFSZ(buf_sz) == 2)
- rxtx_bufsz = SZ_16K;
+ rxtx_min_bufsz = SZ_16K;
else
- rxtx_bufsz = SZ_4K;
+ rxtx_min_bufsz = SZ_4K;
+
+ rxtx_max_bufsz = RXTX_MAP_MAX_BUFSZ(buf_sz) * SZ_4K;
+ if (rxtx_max_bufsz != 0 && rxtx_max_bufsz < rxtx_min_bufsz)
+ rxtx_max_bufsz = rxtx_min_bufsz;
}

- rxtx_bufsz = PAGE_ALIGN(rxtx_bufsz);
- drv_info->rxtx_bufsz = rxtx_bufsz;
+ rxtx_bufsz = min_not_zero(PAGE_ALIGN(rxtx_min_bufsz), rxtx_max_bufsz);
drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL);
if (!drv_info->rx_buffer) {
ret = -ENOMEM;
@@ -2142,10 +2157,17 @@ static int __init ffa_init(void)
ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer),
virt_to_phys(drv_info->rx_buffer),
rxtx_bufsz / FFA_PAGE_SIZE);
+ if (ret == -EINVAL && !rxtx_max_bufsz && rxtx_min_bufsz < rxtx_bufsz) {
+ rxtx_bufsz = rxtx_min_bufsz;
+ ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer),
+ virt_to_phys(drv_info->rx_buffer),
+ rxtx_bufsz / FFA_PAGE_SIZE);
+ }
if (ret) {
pr_err("failed to register FFA RxTx buffers\n");
goto free_pages;
}
+ drv_info->rxtx_bufsz = rxtx_bufsz;

mutex_init(&drv_info->rx_lock);
mutex_init(&drv_info->tx_lock);
diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
index 40ec184eedae..0a192cf2deab 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -600,9 +600,9 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,
return -EINVAL;
}
if (kfifo_avail(&r_evt->proto->equeue.kfifo) < sizeof(eh) + len) {
- dev_warn(handle->dev,
- "queue full, dropping proto_id:%d evt_id:%d ts:%lld\n",
- proto_id, evt_id, ktime_to_ns(ts));
+ dev_warn_ratelimited(handle->dev,
+ "queue full, dropping proto_id:%d evt_id:%d ts:%lld\n",
+ proto_id, evt_id, ktime_to_ns(ts));
return -ENOMEM;
}

diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c
index 39eb78f5905b..7f73703407ee 100644
--- a/drivers/firmware/stratix10-svc.c
+++ b/drivers/firmware/stratix10-svc.c
@@ -1487,8 +1487,9 @@ int stratix10_svc_async_poll(struct stratix10_svc_chan *chan,
WARN_ON_ONCE(1);
}
return 0;
- } else if (handle->res.a0 == INTEL_SIP_SMC_STATUS_BUSY) {
- dev_dbg(ctrl->dev, "async message is still in progress\n");
+ } else if (handle->res.a0 == INTEL_SIP_SMC_STATUS_BUSY ||
+ handle->res.a0 == INTEL_SIP_SMC_STATUS_NO_RESPONSE) {
+ dev_dbg(ctrl->dev, "async message is not ready yet\n");
return -EAGAIN;
}

@@ -1845,14 +1846,16 @@ void *stratix10_svc_allocate_memory(struct stratix10_svc_chan *chan,
struct gen_pool *genpool = chan->ctrl->genpool;
size_t s = roundup(size, 1 << genpool->min_alloc_order);

- pmem = devm_kzalloc(chan->ctrl->dev, sizeof(*pmem), GFP_KERNEL);
+ pmem = kzalloc_obj(*pmem);
if (!pmem)
return ERR_PTR(-ENOMEM);

guard(mutex)(&svc_mem_lock);
va = gen_pool_alloc(genpool, s);
- if (!va)
+ if (!va) {
+ kfree(pmem);
return ERR_PTR(-ENOMEM);
+ }

memset((void *)va, 0, s);
pa = gen_pool_virt_to_phys(genpool, va);
@@ -1878,6 +1881,7 @@ EXPORT_SYMBOL_GPL(stratix10_svc_allocate_memory);
void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr)
{
struct stratix10_svc_data_mem *pmem;
+
guard(mutex)(&svc_mem_lock);

list_for_each_entry(pmem, &svc_data_mem, node)
@@ -1886,10 +1890,9 @@ void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr)
(unsigned long)kaddr, pmem->size);
pmem->vaddr = NULL;
list_del(&pmem->node);
+ kfree(pmem);
return;
}
-
- list_del(&svc_data_mem);
}
EXPORT_SYMBOL_GPL(stratix10_svc_free_memory);

@@ -2042,12 +2045,12 @@ static void stratix10_svc_drv_remove(struct platform_device *pdev)
struct stratix10_svc_controller *ctrl = platform_get_drvdata(pdev);
struct stratix10_svc *svc = ctrl->svc;

+ platform_device_unregister(svc->stratix10_svc_rsu);
+
stratix10_svc_async_exit(ctrl);

of_platform_depopulate(ctrl->dev);

- platform_device_unregister(svc->stratix10_svc_rsu);
-
for (i = 0; i < SVC_NUM_CHANNEL; i++) {
if (ctrl->chans[i].task) {
kthread_stop(ctrl->chans[i].task);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
index 62807b65f2af..0365cc2ce34f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
@@ -509,6 +509,7 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block)
u32 val = 0;
u32 count = 0;
struct amdgpu_device *adev = ip_block->adev;
+ int ret = 0;

/* return early if no ACP */
if (!adev->acp.acp_genpd) {
@@ -530,7 +531,8 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block)
break;
if (--count == 0) {
dev_err(&adev->pdev->dev, "Failed to reset ACP\n");
- return -ETIMEDOUT;
+ ret = -ETIMEDOUT;
+ goto out;
}
udelay(100);
}
@@ -547,20 +549,23 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block)
break;
if (--count == 0) {
dev_err(&adev->pdev->dev, "Failed to reset ACP\n");
- return -ETIMEDOUT;
+ ret = -ETIMEDOUT;
+ goto out;
}
udelay(100);
}
-
+out:
device_for_each_child(adev->acp.parent, NULL,
acp_genpd_remove_device);

mfd_remove_devices(adev->acp.parent);
kfree(adev->acp.acp_res);
+ pm_genpd_remove(&adev->acp.acp_genpd->gpd);
kfree(adev->acp.acp_genpd);
+ adev->acp.acp_genpd = NULL;
kfree(adev->acp.acp_cell);

- return 0;
+ return ret;
}

static int acp_suspend(struct amdgpu_ip_block *ip_block)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
index 35d04e69aec0..af88cab07413 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
@@ -371,19 +371,59 @@ static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev)
}

#ifdef CONFIG_ACPI
+/**
+ * amdgpu_acpi_vfct_match() - Check if a VFCT entry matches the device
+ * @adev: AMDGPU device
+ * @vhdr: VFCT image header to check
+ *
+ * VFCT entries contain the PCI bus number as recorded during BIOS POST.
+ * On systems where the kernel renumbers PCI buses (e.g. pci=realloc or
+ * resource conflicts), the runtime bus number may differ from the POST
+ * value. Match by device identity (vendor + device + function) and use
+ * the bus number as a preference: exact bus match is preferred, but when
+ * the bus numbers disagree we accept the entry if the device identity
+ * matches.
+ *
+ * Returns: 0 on match, -ENODEV on no match
+ */
+static int amdgpu_acpi_vfct_match(struct amdgpu_device *adev,
+ VFCT_IMAGE_HEADER *vhdr)
+{
+ /* Vendor and device IDs must always match */
+ if (vhdr->VendorID != adev->pdev->vendor ||
+ vhdr->DeviceID != adev->pdev->device)
+ return -ENODEV;
+
+ if (vhdr->PCIDevice != PCI_SLOT(adev->pdev->devfn) ||
+ vhdr->PCIFunction != PCI_FUNC(adev->pdev->devfn))
+ return -ENODEV;
+
+ /* Exact bus number match - preferred */
+ if (vhdr->PCIBus == adev->pdev->bus->number)
+ return 0;
+
+ /* Bus mismatch but device identity matches (PCI renumbering case) */
+ dev_notice(adev->dev,
+ "VFCT bus number mismatch: table %u != runtime %u, matching by device identity (vendor 0x%04x device 0x%04x)\n",
+ vhdr->PCIBus, adev->pdev->bus->number,
+ adev->pdev->vendor, adev->pdev->device);
+ return 0;
+}
+
static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
{
struct acpi_table_header *hdr;
acpi_size tbl_size;
UEFI_ACPI_VFCT *vfct;
unsigned int offset;
+ bool r = false;

if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr)))
return false;
tbl_size = hdr->length;
if (tbl_size < sizeof(UEFI_ACPI_VFCT)) {
dev_info(adev->dev, "ACPI VFCT table present but broken (too short #1),skipping\n");
- return false;
+ goto out;
}

vfct = (UEFI_ACPI_VFCT *)hdr;
@@ -396,36 +436,36 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
offset += sizeof(VFCT_IMAGE_HEADER);
if (offset > tbl_size) {
dev_info(adev->dev, "ACPI VFCT image header truncated,skipping\n");
- return false;
+ goto out;
}

offset += vhdr->ImageLength;
if (offset > tbl_size) {
dev_info(adev->dev, "ACPI VFCT image truncated,skipping\n");
- return false;
+ goto out;
}

if (vhdr->ImageLength &&
- vhdr->PCIBus == adev->pdev->bus->number &&
- vhdr->PCIDevice == PCI_SLOT(adev->pdev->devfn) &&
- vhdr->PCIFunction == PCI_FUNC(adev->pdev->devfn) &&
- vhdr->VendorID == adev->pdev->vendor &&
- vhdr->DeviceID == adev->pdev->device) {
+ !amdgpu_acpi_vfct_match(adev, vhdr)) {
adev->bios = kmemdup(&vbios->VbiosContent,
vhdr->ImageLength,
GFP_KERNEL);

if (!check_atom_bios(adev, vhdr->ImageLength)) {
amdgpu_bios_release(adev);
- return false;
+ goto out;
}
adev->bios_size = vhdr->ImageLength;
- return true;
+ r = true;
+ goto out;
}
}

dev_info(adev->dev, "ACPI VFCT table present but broken (too short #2),skipping\n");
- return false;
+
+out:
+ acpi_put_table(hdr);
+ return r;
}
#else
static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index 583fd67a2c64..993586970916 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -252,13 +252,17 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p,
goto free_partial_kdata;
break;

+ case AMDGPU_CHUNK_ID_CP_GFX_SHADOW:
+ if (size < sizeof(struct drm_amdgpu_cs_chunk_cp_gfx_shadow))
+ goto free_partial_kdata;
+ break;
+
case AMDGPU_CHUNK_ID_DEPENDENCIES:
case AMDGPU_CHUNK_ID_SYNCOBJ_IN:
case AMDGPU_CHUNK_ID_SYNCOBJ_OUT:
case AMDGPU_CHUNK_ID_SCHEDULED_DEPENDENCIES:
case AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_WAIT:
case AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_SIGNAL:
- case AMDGPU_CHUNK_ID_CP_GFX_SHADOW:
break;

default:
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
index 7af86a32c0c5..ff77126c5644 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
@@ -326,7 +326,6 @@ static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority,
struct drm_file *filp, struct amdgpu_ctx *ctx)
{
struct amdgpu_fpriv *fpriv = filp->driver_priv;
- u32 current_stable_pstate;
int r;

r = amdgpu_ctx_priority_permit(filp, priority);
@@ -344,36 +343,21 @@ static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority,
ctx->generation = amdgpu_vm_generation(mgr->adev, &fpriv->vm);
ctx->init_priority = priority;
ctx->override_priority = AMDGPU_CTX_PRIORITY_UNSET;
-
- r = amdgpu_ctx_get_stable_pstate(ctx, &current_stable_pstate);
- if (r)
- return r;
-
- if (mgr->adev->pm.stable_pstate_ctx)
- ctx->stable_pstate = mgr->adev->pm.stable_pstate_ctx->stable_pstate;
- else
- ctx->stable_pstate = current_stable_pstate;
+ ctx->stable_pstate = AMDGPU_CTX_STABLE_PSTATE_NONE;

return 0;
}

-static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx,
- u32 stable_pstate)
+static int __amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx,
+ u32 stable_pstate)
{
struct amdgpu_device *adev = ctx->mgr->adev;
enum amd_dpm_forced_level level;
+ struct amdgpu_ctx *current_ctx;
u32 current_stable_pstate;
- int r;
+ int r = 0;

- mutex_lock(&adev->pm.stable_pstate_ctx_lock);
- if (adev->pm.stable_pstate_ctx && adev->pm.stable_pstate_ctx != ctx) {
- r = -EBUSY;
- goto done;
- }
-
- r = amdgpu_ctx_get_stable_pstate(ctx, &current_stable_pstate);
- if (r || (stable_pstate == current_stable_pstate))
- goto done;
+ lockdep_assert_held(&adev->pm.stable_pstate_ctx_lock);

switch (stable_pstate) {
case AMDGPU_CTX_STABLE_PSTATE_NONE:
@@ -392,17 +376,41 @@ static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx,
level = AMD_DPM_FORCED_LEVEL_PROFILE_PEAK;
break;
default:
- r = -EINVAL;
- goto done;
+ return -EINVAL;
}

+ current_ctx = adev->pm.stable_pstate_ctx;
+ if (current_ctx && current_ctx != ctx)
+ return -EBUSY;
+
+ r = amdgpu_ctx_get_stable_pstate(ctx, &current_stable_pstate);
+ if (r || current_stable_pstate == stable_pstate)
+ return r;
+
r = amdgpu_dpm_force_performance_level(adev, level);
+ if (r)
+ return r;

- if (level == AMD_DPM_FORCED_LEVEL_AUTO)
- adev->pm.stable_pstate_ctx = NULL;
- else
+ if (!current_ctx) {
adev->pm.stable_pstate_ctx = ctx;
-done:
+ /*
+ * Serialized by context taking ownership for the first time
+ * while holding adev->pm.stable_pstate_ctx_lock).
+ */
+ WRITE_ONCE(ctx->stable_pstate, current_stable_pstate);
+ }
+
+ return 0;
+}
+
+static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx,
+ u32 stable_pstate)
+{
+ struct amdgpu_device *adev = ctx->mgr->adev;
+ int r;
+
+ mutex_lock(&adev->pm.stable_pstate_ctx_lock);
+ r = __amdgpu_ctx_set_stable_pstate(ctx, stable_pstate);
mutex_unlock(&adev->pm.stable_pstate_ctx_lock);

return r;
@@ -428,7 +436,12 @@ static void amdgpu_ctx_fini(struct kref *ref)
}

if (drm_dev_enter(adev_to_drm(adev), &idx)) {
- amdgpu_ctx_set_stable_pstate(ctx, ctx->stable_pstate);
+ mutex_lock(&adev->pm.stable_pstate_ctx_lock);
+ if (adev->pm.stable_pstate_ctx == ctx) {
+ __amdgpu_ctx_set_stable_pstate(ctx, ctx->stable_pstate);
+ adev->pm.stable_pstate_ctx = NULL;
+ }
+ mutex_unlock(&adev->pm.stable_pstate_ctx_lock);
drm_dev_exit(idx);
}

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
index f89f3a37a45b..6b5384e5fd71 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
@@ -240,10 +240,14 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf
drm_printf(&p, "kernel: " UTS_RELEASE "\n");
drm_printf(&p, "module: " KBUILD_MODNAME "\n");
drm_printf(&p, "time: %ptSp\n", &coredump->reset_time);
+ drm_printf(&p, "pasid: %u\n", coredump->pasid);
+ drm_printf(&p, "vmid: %u\n", coredump->vmid);

if (coredump->reset_task_info.task.pid)
- drm_printf(&p, "process_name: %s PID: %d\n",
+ drm_printf(&p, "process_name: %s TGID: %d thread: %s PID: %d\n",
coredump->reset_task_info.process_name,
+ coredump->reset_task_info.tgid,
+ coredump->reset_task_info.task.comm,
coredump->reset_task_info.task.pid);

/* SOC Information */
@@ -525,6 +529,7 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
amdgpu_vm_put_task_info(ti);
}
coredump->pasid = job->pasid;
+ coredump->vmid = job->vmid;
coredump->num_ibs = job->num_ibs;
for (i = 0; i < job->num_ibs; ++i) {
coredump->ibs[i].gpu_addr = job->ibs[i].gpu_addr;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h
index 2371e20fc68b..63f27337c09a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.h
@@ -63,6 +63,7 @@ struct amdgpu_coredump_info {
char *formatted;

unsigned int pasid;
+ unsigned int vmid;
int num_ibs;
struct amdgpu_coredump_ib_info ibs[] __counted_by(num_ibs);
};
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 4f8489626624..3fad14824d1e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -1318,6 +1318,15 @@ static bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device

if (c->x86_vendor == X86_VENDOR_INTEL)
return false;
+
+ /*
+ * AMD Ryzen Pinnacle Ridge (Zen+, family 0x17 model 0x08) CPUs don't
+ * support PCIe dynamic speed switching.
+ * https://gitlab.freedesktop.org/drm/amd/-/work_items/5436
+ */
+ if (c->x86_vendor == X86_VENDOR_AMD && c->x86 == 0x17 &&
+ c->x86_model == 0x08)
+ return false;
#endif
return true;
}
@@ -1328,7 +1337,8 @@ static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev)
* It's unclear if this is a platform-specific or GPU-specific issue.
* Disable ASPM on SI for the time being.
*/
- if (adev->family == AMDGPU_FAMILY_SI)
+ if (adev->family == AMDGPU_FAMILY_SI ||
+ (!(adev->pm.pp_feature & PP_PCIE_DPM_MASK) && adev->family == AMDGPU_FAMILY_VI))
return true;

#if IS_ENABLED(CONFIG_X86)
@@ -4159,8 +4169,6 @@ static void amdgpu_device_unmap_mmio(struct amdgpu_device *adev)

iounmap(adev->rmmio);
adev->rmmio = NULL;
- if (adev->mman.aper_base_kaddr)
- iounmap(adev->mman.aper_base_kaddr);
adev->mman.aper_base_kaddr = NULL;

/* Memory manager related */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
index 1120f8225ac0..c08f01936675 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
@@ -535,6 +535,7 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data,
bo = gem_to_amdgpu_bo(gobj);
bo->preferred_domains = AMDGPU_GEM_DOMAIN_GTT;
bo->allowed_domains = AMDGPU_GEM_DOMAIN_GTT;
+ bo->parent = amdgpu_bo_ref(fpriv->vm.root.bo);
r = amdgpu_ttm_tt_set_userptr(&bo->tbo, args->addr, args->flags);
if (r)
goto release_object;
@@ -1094,6 +1095,11 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data,
* If that number is larger than the size of the array, the ioctl must
* be retried.
*/
+ if (!bo_va) {
+ r = -ENOENT;
+ goto out_exec;
+ }
+
if (args->num_entries > INT_MAX / sizeof(*vm_entries)) {
r = -EINVAL;
goto out_exec;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
index b8ca876694ff..839bc93a941a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
@@ -1646,12 +1646,13 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring)
struct amdgpu_device *adev = ring->adev;
struct drm_gpu_scheduler *sched = &ring->sched;
struct drm_sched_entity entity;
+ unsigned int ib_size_dw = 16;
static atomic_t counter;
struct dma_fence *f;
struct amdgpu_job *job;
struct amdgpu_ib *ib;
void *owner;
- int i, r;
+ int r;

/* Initialize the scheduler entity */
r = drm_sched_entity_init(&entity, DRM_SCHED_PRIORITY_NORMAL,
@@ -1669,7 +1670,7 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring)
owner = (void *)(unsigned long)atomic_inc_return(&counter);

r = amdgpu_job_alloc_with_ib(ring->adev, &entity, owner,
- 64, 0, &job,
+ ib_size_dw * sizeof(uint32_t), 0, &job,
AMDGPU_KERNEL_JOB_ID_CLEANER_SHADER);
if (r)
goto err;
@@ -1679,9 +1680,8 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring)
job->run_cleaner_shader = true;

ib = &job->ibs[0];
- for (i = 0; i <= ring->funcs->align_mask; ++i)
- ib->ptr[i] = ring->funcs->nop;
- ib->length_dw = ring->funcs->align_mask + 1;
+ memset32(ib->ptr, ring->funcs->nop, ib_size_dw);
+ ib->length_dw = ib_size_dw;

f = amdgpu_job_submit(job);

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
index d23a91d029aa..0ea32561c4bc 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
@@ -272,7 +272,20 @@ static bool amdgpu_gtt_mgr_intersects(struct ttm_resource_manager *man,
const struct ttm_place *place,
size_t size)
{
- return !place->lpfn || amdgpu_gtt_mgr_has_gart_addr(res);
+ const struct drm_mm_node *const node = &to_ttm_range_mgr_node(res)->mm_nodes[0];
+ const u32 num_pages = PFN_UP(size);
+
+ if (!place->lpfn)
+ return true;
+
+ if (!amdgpu_gtt_mgr_has_gart_addr(res))
+ return false;
+
+ if (place->fpfn >= (node->start + num_pages) ||
+ (place->lpfn && place->lpfn <= node->start))
+ return false;
+
+ return true;
}

/**
@@ -290,7 +303,20 @@ static bool amdgpu_gtt_mgr_compatible(struct ttm_resource_manager *man,
const struct ttm_place *place,
size_t size)
{
- return !place->lpfn || amdgpu_gtt_mgr_has_gart_addr(res);
+ const struct drm_mm_node *const node = &to_ttm_range_mgr_node(res)->mm_nodes[0];
+ const u32 num_pages = PFN_UP(size);
+
+ if (!place->lpfn)
+ return true;
+
+ if (!amdgpu_gtt_mgr_has_gart_addr(res))
+ return false;
+
+ if (node->start < place->fpfn ||
+ (place->lpfn && (node->start + num_pages) > place->lpfn))
+ return false;
+
+ return true;
}

/**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c
index 99bc9ad67d5b..a7d13e337d84 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c
@@ -67,7 +67,6 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni,
{
struct amdgpu_bo *bo = container_of(mni, struct amdgpu_bo, notifier);
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
- struct amdgpu_bo *vm_root = bo->vm_bo->vm->root.bo;
long r;

if (!mmu_notifier_range_blockable(range))
@@ -78,7 +77,7 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni,
mmu_interval_set_seq(mni, cur_seq);

amdgpu_vm_bo_invalidate(bo, false);
- r = dma_resv_wait_timeout(vm_root->tbo.base.resv,
+ r = dma_resv_wait_timeout(bo->parent->tbo.base.resv,
DMA_RESV_USAGE_BOOKKEEP, false,
MAX_SCHEDULE_TIMEOUT);
mutex_unlock(&adev->notifier_lock);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h
index f80e3aca9c78..ab6e4aa7a34a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h
@@ -288,12 +288,14 @@ struct mes_suspend_gang_input {
uint64_t gang_context_addr;
uint64_t suspend_fence_addr;
uint32_t suspend_fence_value;
+ uint32_t doorbell_offset;
};

struct mes_resume_gang_input {
uint32_t xcc_id;
bool resume_all_gangs;
uint64_t gang_context_addr;
+ uint32_t doorbell_offset;
};

struct mes_reset_queue_input {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index b6aabac39b46..066ac0089e22 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -276,10 +276,12 @@ int amdgpu_bo_create_reserved(struct amdgpu_device *adev,
goto error_free;
}

- r = amdgpu_bo_pin(*bo_ptr, domain);
- if (r) {
- dev_err(adev->dev, "(%d) kernel bo pin failed\n", r);
- goto error_unreserve;
+ if (free) {
+ r = amdgpu_bo_pin(*bo_ptr, domain);
+ if (r) {
+ dev_err(adev->dev, "(%d) kernel bo pin failed\n", r);
+ goto error_unreserve;
+ }
}

r = amdgpu_ttm_alloc_gart(&(*bo_ptr)->tbo);
@@ -302,7 +304,8 @@ int amdgpu_bo_create_reserved(struct amdgpu_device *adev,
return 0;

error_unpin:
- amdgpu_bo_unpin(*bo_ptr);
+ if (free)
+ amdgpu_bo_unpin(*bo_ptr);
error_unreserve:
amdgpu_bo_unreserve(*bo_ptr);

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 3d2e00efc741..3eae6e620f8d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -515,6 +515,15 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,

if (new_mem->mem_type == TTM_PL_TT ||
new_mem->mem_type == AMDGPU_PL_PREEMPT) {
+ if (old_mem && (old_mem->mem_type == TTM_PL_TT ||
+ old_mem->mem_type == AMDGPU_PL_PREEMPT)) {
+ r = ttm_bo_wait_ctx(bo, ctx);
+ if (r)
+ return r;
+
+ amdgpu_ttm_backend_unbind(bo->bdev, bo->ttm);
+ }
+
r = amdgpu_ttm_backend_bind(bo->bdev, bo->ttm, new_mem);
if (r)
return r;
@@ -549,6 +558,15 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
ttm_bo_assign_mem(bo, new_mem);
return 0;
}
+ if ((old_mem->mem_type == TTM_PL_TT ||
+ old_mem->mem_type == AMDGPU_PL_PREEMPT) &&
+ (new_mem->mem_type == TTM_PL_TT ||
+ new_mem->mem_type == AMDGPU_PL_PREEMPT)) {
+ amdgpu_bo_move_notify(bo, evict, new_mem);
+ ttm_resource_free(bo, &bo->resource);
+ ttm_bo_assign_mem(bo, new_mem);
+ return 0;
+ }

if (old_mem->mem_type == AMDGPU_PL_GDS ||
old_mem->mem_type == AMDGPU_PL_GWS ||
@@ -2103,18 +2121,23 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
/* Change the size here instead of the init above so only lpfn is affected */
amdgpu_ttm_set_buffer_funcs_status(adev, false);
#ifdef CONFIG_64BIT
-#ifdef CONFIG_X86
- if (adev->gmc.xgmi.connected_to_cpu)
- adev->mman.aper_base_kaddr = ioremap_cache(adev->gmc.aper_base,
- adev->gmc.visible_vram_size);
-
- else if (adev->gmc.is_app_apu)
+ if (adev->gmc.xgmi.connected_to_cpu) {
+ void *kaddr = devm_memremap(adev->dev, adev->gmc.aper_base,
+ adev->gmc.visible_vram_size,
+ MEMREMAP_WB);
+ if (IS_ERR(kaddr))
+ return PTR_ERR(kaddr);
+ adev->mman.aper_base_kaddr = (__force void __iomem *)kaddr;
+ } else if (adev->gmc.is_app_apu) {
DRM_DEBUG_DRIVER(
"No need to ioremap when real vram size is 0\n");
- else
-#endif
- adev->mman.aper_base_kaddr = ioremap_wc(adev->gmc.aper_base,
- adev->gmc.visible_vram_size);
+ } else {
+ adev->mman.aper_base_kaddr = devm_ioremap_wc(adev->dev,
+ adev->gmc.aper_base,
+ adev->gmc.visible_vram_size);
+ if (!adev->mman.aper_base_kaddr)
+ return -ENOMEM;
+ }
#endif

amdgpu_ttm_init_vram_resv_regions(adev);
@@ -2231,8 +2254,6 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
*/
void amdgpu_ttm_fini(struct amdgpu_device *adev)
{
- int idx;
-
if (!adev->mman.initialized)
return;

@@ -2255,14 +2276,7 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev)
amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_FW_VRAM_USAGE);
amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_DRV_VRAM_USAGE);

- if (drm_dev_enter(adev_to_drm(adev), &idx)) {
-
- if (adev->mman.aper_base_kaddr)
- iounmap(adev->mman.aper_base_kaddr);
- adev->mman.aper_base_kaddr = NULL;
-
- drm_dev_exit(idx);
- }
+ adev->mman.aper_base_kaddr = NULL;

if (!adev->gmc.is_app_apu)
amdgpu_vram_mgr_fini(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index 59ffaa7b61c2..73421f8021fb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -523,6 +523,15 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que
amdgpu_userq_cleanup(queue);
mutex_unlock(&uq_mgr->userq_mutex);

+ /*
+ * A failed unmap means MES could not remove the hung queue and is now
+ * unresponsive. Recover the GPU here so the wedged MES does not fail
+ * the next, unrelated queue submission and trigger a reset attributed
+ * to an innocent workload.
+ */
+ if (r)
+ queue_work(adev->reset_domain->wq, &uq_mgr->reset_work);
+
cancel_delayed_work_sync(&queue->hang_detect_work);
uq_funcs->mqd_destroy(queue);
queue->userq_mgr = NULL;
@@ -680,8 +689,8 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
/* Update VM owner at userq submit-time for page-fault attribution. */
amdgpu_vm_set_task_info(&fpriv->vm);

- r = xa_err(xa_store_irq(&adev->userq_doorbell_xa, index, queue,
- GFP_KERNEL));
+ r = xa_insert_irq(&adev->userq_doorbell_xa, index, queue,
+ GFP_KERNEL);
if (r)
goto clean_mqd;

@@ -1367,16 +1376,19 @@ void amdgpu_userq_pre_reset(struct amdgpu_device *adev)

/* TODO: We probably need a new lock for the queue state */
xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
- if (queue->state != AMDGPU_USERQ_STATE_MAPPED)
- continue;
-
- userq_funcs = adev->userq_funcs[queue->queue_type];
- userq_funcs->unmap(queue);
- /* just mark all queues as hung at this point.
- * if unmap succeeds, we could map again
- * in amdgpu_userq_post_reset() if vram is not lost
+ if (queue->state == AMDGPU_USERQ_STATE_MAPPED) {
+ userq_funcs = adev->userq_funcs[queue->queue_type];
+ userq_funcs->unmap(queue);
+ /* just mark all queues as hung at this point.
+ * if unmap succeeds, we could map again
+ * in amdgpu_userq_post_reset() if vram is not lost
+ */
+ queue->state = AMDGPU_USERQ_STATE_HUNG;
+ }
+ /* Force-complete any pending fence regardless of queue state so
+ * that eviction/suspend and queue teardown waiters don't block
+ * forever on a fence that will never signal after the reset.
*/
- queue->state = AMDGPU_USERQ_STATE_HUNG;
amdgpu_userq_fence_driver_force_completion(queue);
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
index 9d5cca7da1d9..47a256e1acf6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
@@ -135,7 +135,7 @@ MODULE_FIRMWARE(FIRMWARE_VEGA12);
MODULE_FIRMWARE(FIRMWARE_VEGA20);

static void amdgpu_uvd_idle_work_handler(struct work_struct *work);
-static void amdgpu_uvd_force_into_uvd_segment(struct amdgpu_bo *abo);
+static void amdgpu_uvd_force_into_vcpu_segment(struct amdgpu_bo *abo);

static int amdgpu_uvd_create_msg_bo_helper(struct amdgpu_device *adev,
uint32_t size,
@@ -158,7 +158,7 @@ static int amdgpu_uvd_create_msg_bo_helper(struct amdgpu_device *adev,
amdgpu_bo_kunmap(bo);
amdgpu_bo_unpin(bo);
amdgpu_bo_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_VRAM);
- amdgpu_uvd_force_into_uvd_segment(bo);
+ amdgpu_uvd_force_into_vcpu_segment(bo);
r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx);
if (r)
goto err;
@@ -188,6 +188,7 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev)
const struct common_firmware_header *hdr;
unsigned int family_id;
int i, j, r;
+ u32 vcpu_bo_domain;

INIT_DELAYED_WORK(&adev->uvd.idle_work, amdgpu_uvd_idle_work_handler);

@@ -319,12 +320,20 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev)
if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP)
bo_size += AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8);

+ /* UVD 5.0 and newer HW can use 64 bit addressing. */
+ adev->uvd.address_64_bit =
+ !amdgpu_device_ip_block_version_cmp(adev, AMD_IP_BLOCK_TYPE_UVD, 5, 0);
+
+ vcpu_bo_domain = AMDGPU_GEM_DOMAIN_VRAM;
+ if (adev->uvd.address_64_bit)
+ vcpu_bo_domain |= AMDGPU_GEM_DOMAIN_GTT;
+
for (j = 0; j < adev->uvd.num_uvd_inst; j++) {
if (adev->uvd.harvest_config & (1 << j))
continue;
+
r = amdgpu_bo_create_kernel(adev, bo_size, PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_VRAM |
- AMDGPU_GEM_DOMAIN_GTT,
+ vcpu_bo_domain,
&adev->uvd.inst[j].vcpu_bo,
&adev->uvd.inst[j].gpu_addr,
&adev->uvd.inst[j].cpu_addr);
@@ -339,10 +348,6 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev)
adev->uvd.filp[i] = NULL;
}

- /* from uvd v5.0 HW addressing capacity increased to 64 bits */
- if (!amdgpu_device_ip_block_version_cmp(adev, AMD_IP_BLOCK_TYPE_UVD, 5, 0))
- adev->uvd.address_64_bit = true;
-
r = amdgpu_uvd_create_msg_bo_helper(adev, 128 << 10, &adev->uvd.ib_bo);
if (r)
return r;
@@ -545,6 +550,24 @@ void amdgpu_uvd_free_handles(struct amdgpu_device *adev, struct drm_file *filp)
}
}

+static void amdgpu_uvd_force_into_vcpu_segment(struct amdgpu_bo *bo)
+{
+ struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
+ struct amdgpu_bo *vcpu_bo = adev->uvd.inst[0].vcpu_bo;
+ struct amdgpu_res_cursor vcpu_cur;
+
+ amdgpu_res_first(vcpu_bo->tbo.resource, 0,
+ amdgpu_bo_size(vcpu_bo), &vcpu_cur);
+
+ bo->placement.num_placement = 1;
+ bo->placement.placement = &bo->placements[0];
+ bo->placements[0].fpfn = ALIGN_DOWN(vcpu_cur.start, SZ_256M) >> PAGE_SHIFT;
+ bo->placements[0].lpfn = bo->placements[0].fpfn + (SZ_256M >> PAGE_SHIFT);
+ bo->placements[0].mem_type = vcpu_bo->tbo.resource->mem_type;
+ if (bo->placements[0].mem_type == TTM_PL_VRAM)
+ bo->placements[0].flags |= TTM_PL_FLAG_CONTIGUOUS;
+}
+
static void amdgpu_uvd_force_into_uvd_segment(struct amdgpu_bo *abo)
{
int i;
@@ -595,13 +618,10 @@ static int amdgpu_uvd_cs_pass1(struct amdgpu_uvd_cs_ctx *ctx)
if (!ctx->parser->adev->uvd.address_64_bit) {
/* check if it's a message or feedback command */
cmd = amdgpu_ib_get_value(ctx->ib, ctx->idx) >> 1;
- if (cmd == 0x0 || cmd == 0x3) {
- /* yes, force it into VRAM */
- uint32_t domain = AMDGPU_GEM_DOMAIN_VRAM;
-
- amdgpu_bo_placement_from_domain(bo, domain);
- }
- amdgpu_uvd_force_into_uvd_segment(bo);
+ if (cmd == 0x0 || cmd == 0x3)
+ amdgpu_uvd_force_into_vcpu_segment(bo);
+ else
+ amdgpu_uvd_force_into_uvd_segment(bo);

r = ttm_bo_validate(&bo->tbo, &bo->placement, &tctx);
}
@@ -635,6 +655,14 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg,
unsigned int image_size, tmp, min_dpb_size, num_dpb_buffer;
unsigned int min_ctx_size = ~0;

+ /* Reject invalid dimensions to prevent division by zero */
+ if (width < 16 || height < 16) {
+ dev_WARN_ONCE(adev->dev, 1,
+ "Invalid UVD decoding dimensions (%dx%d)!\n",
+ width, height);
+ return -EINVAL;
+ }
+
image_size = width * height;
image_size += image_size / 2;
image_size = ALIGN(image_size, 1024);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
index efdebd9c0a1f..eef3c9853a5c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
@@ -877,9 +877,20 @@ int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p,
goto out;
}

- *size = amdgpu_ib_get_value(ib, idx + 8) *
- amdgpu_ib_get_value(ib, idx + 10) *
- 8 * 3 / 2;
+ uint32_t width, height;
+ width = amdgpu_ib_get_value(ib, idx + 8);
+ height = amdgpu_ib_get_value(ib, idx + 10);
+
+ if (width == 0 || height == 0 ||
+ width > 4096 || height > 2304) {
+ DRM_ERROR("invalid VCE image size: %ux%u\n",
+ width, height);
+ r = -EINVAL;
+ goto out;
+ }
+
+ *size = width * height * 8 * 3 / 2;
+
break;

case 0x04000001: /* config extension */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index 381901bc539f..7ed027b28889 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -865,12 +865,10 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job,
job->oa_size);
}

- if (vm_flush_needed || pasid_mapping_needed || cleaner_shader_needed) {
- amdgpu_fence_emit(ring, job->hw_vm_fence, 0);
- fence = &job->hw_vm_fence->base;
- /* get a ref for the job */
- dma_fence_get(fence);
- }
+ amdgpu_fence_emit(ring, job->hw_vm_fence, 0);
+ fence = &job->hw_vm_fence->base;
+ /* get a ref for the job */
+ dma_fence_get(fence);

if (vm_flush_needed) {
mutex_lock(&id_mgr->lock);
@@ -2487,19 +2485,6 @@ static void amdgpu_vm_destroy_task_info(struct kref *kref)
kfree(ti);
}

-static inline struct amdgpu_vm *
-amdgpu_vm_get_vm_from_pasid(struct amdgpu_device *adev, u32 pasid)
-{
- struct amdgpu_vm *vm;
- unsigned long flags;
-
- xa_lock_irqsave(&adev->vm_manager.pasids, flags);
- vm = xa_load(&adev->vm_manager.pasids, pasid);
- xa_unlock_irqrestore(&adev->vm_manager.pasids, flags);
-
- return vm;
-}
-
/**
* amdgpu_vm_put_task_info - reference down the vm task_info ptr
*
@@ -2546,8 +2531,16 @@ amdgpu_vm_get_task_info_vm(struct amdgpu_vm *vm)
struct amdgpu_task_info *
amdgpu_vm_get_task_info_pasid(struct amdgpu_device *adev, u32 pasid)
{
- return amdgpu_vm_get_task_info_vm(
- amdgpu_vm_get_vm_from_pasid(adev, pasid));
+ struct amdgpu_task_info *ti;
+ struct amdgpu_vm *vm;
+ unsigned long flags;
+
+ xa_lock_irqsave(&adev->vm_manager.pasids, flags);
+ vm = xa_load(&adev->vm_manager.pasids, pasid);
+ ti = amdgpu_vm_get_task_info_vm(vm);
+ xa_unlock_irqrestore(&adev->vm_manager.pasids, flags);
+
+ return ti;
}

static int amdgpu_vm_create_task_info(struct amdgpu_vm *vm)
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
index 8b60299b73ef..8454edaf0225 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
@@ -4022,7 +4022,7 @@ static void gfx_v10_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel,
WAIT_REG_MEM_ENGINE(eng_sel)));

if (mem_space)
- BUG_ON(addr0 & 0x3); /* Dword align */
+ WARN_ON(addr0 & 0x3); /* Dword align */
amdgpu_ring_write(ring, addr0);
amdgpu_ring_write(ring, addr1);
amdgpu_ring_write(ring, ref);
@@ -5350,6 +5350,15 @@ static void gfx_v10_0_constants_init(struct amdgpu_device *adev)
gfx_v10_0_get_tcc_info(adev);
adev->gfx.config.pa_sc_tile_steering_override =
gfx_v10_0_init_pa_sc_tile_steering_override(adev);
+ /* Program DB_RING_CONTROL for multiple GFX pipes
+ * Default power up value is 1.
+ * Possible values:
+ * 0 - split occlusion counters between gfx pipes
+ * 1 - all occlusion counters to pipe 0
+ * 2 - all occlusion counters to pipe 1
+ */
+ WREG32_FIELD15(GC, 0, DB_RING_CONTROL, COUNTER_CONTROL,
+ (adev->gfx.me.num_pipe_per_me > 1) ? 0 : 1);

/* XXX SH_MEM regs */
/* where to put LDS, scratch, GPUVM in FSA64 space */
@@ -8655,7 +8664,7 @@ static void gfx_v10_0_ring_emit_ib_gfx(struct amdgpu_ring *ring,
}

amdgpu_ring_write(ring, header);
- BUG_ON(ib->gpu_addr & 0x3); /* Dword align */
+ WARN_ON(ib->gpu_addr & 0x3); /* Dword align */
amdgpu_ring_write(ring,
#ifdef __BIG_ENDIAN
(2 << 0) |
@@ -8690,7 +8699,7 @@ static void gfx_v10_0_ring_emit_ib_compute(struct amdgpu_ring *ring,
}

amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2));
- BUG_ON(ib->gpu_addr & 0x3); /* Dword align */
+ WARN_ON(ib->gpu_addr & 0x3); /* Dword align */
amdgpu_ring_write(ring,
#ifdef __BIG_ENDIAN
(2 << 0) |
@@ -8723,9 +8732,9 @@ static void gfx_v10_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr,
* aligned if only send 32bit data low (discard data high)
*/
if (write64bit)
- BUG_ON(addr & 0x7);
+ WARN_ON(addr & 0x7);
else
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
@@ -8773,9 +8782,6 @@ static void gfx_v10_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr,
{
struct amdgpu_device *adev = ring->adev;

- /* we only allocate 32bit for each seq wb address */
- BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT);
-
/* write fence seq to the "addr" */
amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
index d40ab1e95480..f941a4cd8c7d 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
@@ -542,7 +542,7 @@ static void gfx_v11_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel,
WAIT_REG_MEM_ENGINE(eng_sel)));

if (mem_space)
- BUG_ON(addr0 & 0x3); /* Dword align */
+ WARN_ON(addr0 & 0x3); /* Dword align */
amdgpu_ring_write(ring, addr0);
amdgpu_ring_write(ring, addr1);
amdgpu_ring_write(ring, ref);
@@ -5953,7 +5953,7 @@ static void gfx_v11_0_ring_emit_ib_gfx(struct amdgpu_ring *ring,
}

amdgpu_ring_write(ring, header);
- BUG_ON(ib->gpu_addr & 0x3); /* Dword align */
+ WARN_ON(ib->gpu_addr & 0x3); /* Dword align */
amdgpu_ring_write(ring,
#ifdef __BIG_ENDIAN
(2 << 0) |
@@ -5988,7 +5988,7 @@ static void gfx_v11_0_ring_emit_ib_compute(struct amdgpu_ring *ring,
}

amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2));
- BUG_ON(ib->gpu_addr & 0x3); /* Dword align */
+ WARN_ON(ib->gpu_addr & 0x3); /* Dword align */
amdgpu_ring_write(ring,
#ifdef __BIG_ENDIAN
(2 << 0) |
@@ -6021,9 +6021,9 @@ static void gfx_v11_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr,
* aligned if only send 32bit data low (discard data high)
*/
if (write64bit)
- BUG_ON(addr & 0x7);
+ WARN_ON(addr & 0x7);
else
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
@@ -6077,9 +6077,6 @@ static void gfx_v11_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr,
{
struct amdgpu_device *adev = ring->adev;

- /* we only allocate 32bit for each seq wb address */
- BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT);
-
/* write fence seq to the "addr" */
amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
@@ -6516,25 +6513,33 @@ static int gfx_v11_0_eop_irq(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry)
{
u32 doorbell_offset = entry->src_data[0];
- u8 me_id, pipe_id, queue_id;
- struct amdgpu_ring *ring;
- int i;

DRM_DEBUG("IH: CP EOP\n");

- if (adev->enable_mes && doorbell_offset) {
- amdgpu_userq_process_fence_irq(adev, doorbell_offset);
- } else {
- me_id = (entry->ring_id & 0x0c) >> 2;
- pipe_id = (entry->ring_id & 0x03) >> 0;
- queue_id = (entry->ring_id & 0x70) >> 4;
+ if (!adev->gfx.disable_kq) {
+ u8 me_id = (entry->ring_id & 0x0c) >> 2;
+ u8 pipe_id = (entry->ring_id & 0x03) >> 0;
+ u8 queue_id = (entry->ring_id & 0x70) >> 4;
+ struct amdgpu_ring *ring;
+ int i;

switch (me_id) {
case 0:
- if (pipe_id == 0)
- amdgpu_fence_process(&adev->gfx.gfx_ring[0]);
- else
- amdgpu_fence_process(&adev->gfx.gfx_ring[1]);
+ /*
+ * MES splits gfx HQDs per (me,pipe): KGQ owns queue=0,
+ * userq gfx owns queue>=1 (see amdgpu_mes_get_hqd_mask).
+ * Require a strict (me,pipe,queue) match so userq gfx
+ * EOPs fall through to amdgpu_userq_process_fence_irq().
+ */
+ for (i = 0; i < adev->gfx.num_gfx_rings; i++) {
+ ring = &adev->gfx.gfx_ring[i];
+ if ((ring->me == me_id) &&
+ (ring->pipe == pipe_id) &&
+ (ring->queue == queue_id)) {
+ amdgpu_fence_process(ring);
+ return 0;
+ }
+ }
break;
case 1:
case 2:
@@ -6546,13 +6551,20 @@ static int gfx_v11_0_eop_irq(struct amdgpu_device *adev,
*/
if ((ring->me == me_id) &&
(ring->pipe == pipe_id) &&
- (ring->queue == queue_id))
+ (ring->queue == queue_id)) {
amdgpu_fence_process(ring);
+ return 0;
+ }
}
break;
+ default:
+ break;
}
}

+ if (adev->enable_mes && doorbell_offset)
+ amdgpu_userq_process_fence_irq(adev, doorbell_offset);
+
return 0;
}

diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
index c35372e21261..d66b6b4f3cb2 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
@@ -440,7 +440,7 @@ static void gfx_v12_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel,
WAIT_REG_MEM_ENGINE(eng_sel)));

if (mem_space)
- BUG_ON(addr0 & 0x3); /* Dword align */
+ WARN_ON(addr0 & 0x3); /* Dword align */
amdgpu_ring_write(ring, addr0);
amdgpu_ring_write(ring, addr1);
amdgpu_ring_write(ring, ref);
@@ -3514,10 +3514,19 @@ static int gfx_v12_0_cp_resume(struct amdgpu_device *adev)
gfx_v12_0_cp_gfx_enable(adev, true);
}

- if (adev->enable_mes_kiq && adev->mes.kiq_hw_init)
+ if (adev->enable_mes_kiq && adev->mes.kiq_hw_init) {
r = amdgpu_mes_kiq_hw_init(adev, 0);
- else
+ /*
+ * With MES, GFX KIQ ring is owned by the MES and is never
+ * initialized/used directly by the driver, so it must
+ * not be left flagged as ready. mes_v12_0_hw_init() clears
+ * but clear here if MES init fails
+ */
+ if (r)
+ adev->gfx.kiq[0].ring.sched.ready = false;
+ } else {
r = gfx_v12_0_kiq_resume(adev);
+ }
if (r)
return r;

@@ -4459,7 +4468,7 @@ static void gfx_v12_0_ring_emit_ib_gfx(struct amdgpu_ring *ring,
control |= ib->length_dw | (vmid << 24);

amdgpu_ring_write(ring, header);
- BUG_ON(ib->gpu_addr & 0x3); /* Dword align */
+ WARN_ON(ib->gpu_addr & 0x3); /* Dword align */
amdgpu_ring_write(ring,
#ifdef __BIG_ENDIAN
(2 << 0) |
@@ -4478,7 +4487,7 @@ static void gfx_v12_0_ring_emit_ib_compute(struct amdgpu_ring *ring,
u32 control = INDIRECT_BUFFER_VALID | ib->length_dw | (vmid << 24);

amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2));
- BUG_ON(ib->gpu_addr & 0x3); /* Dword align */
+ WARN_ON(ib->gpu_addr & 0x3); /* Dword align */
amdgpu_ring_write(ring,
#ifdef __BIG_ENDIAN
(2 << 0) |
@@ -4509,9 +4518,9 @@ static void gfx_v12_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr,
* aligned if only send 32bit data low (discard data high)
*/
if (write64bit)
- BUG_ON(addr & 0x7);
+ WARN_ON(addr & 0x7);
else
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
@@ -4559,9 +4568,6 @@ static void gfx_v12_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr,
{
struct amdgpu_device *adev = ring->adev;

- /* we only allocate 32bit for each seq wb address */
- BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT);
-
/* write fence seq to the "addr" */
amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
@@ -4854,25 +4860,33 @@ static int gfx_v12_0_eop_irq(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry)
{
u32 doorbell_offset = entry->src_data[0];
- u8 me_id, pipe_id, queue_id;
- struct amdgpu_ring *ring;
- int i;

DRM_DEBUG("IH: CP EOP\n");

- if (adev->enable_mes && doorbell_offset) {
- amdgpu_userq_process_fence_irq(adev, doorbell_offset);
- } else {
- me_id = (entry->ring_id & 0x0c) >> 2;
- pipe_id = (entry->ring_id & 0x03) >> 0;
- queue_id = (entry->ring_id & 0x70) >> 4;
+ if (!adev->gfx.disable_kq) {
+ u8 me_id = (entry->ring_id & 0x0c) >> 2;
+ u8 pipe_id = (entry->ring_id & 0x03) >> 0;
+ u8 queue_id = (entry->ring_id & 0x70) >> 4;
+ struct amdgpu_ring *ring;
+ int i;

switch (me_id) {
case 0:
- if (pipe_id == 0)
- amdgpu_fence_process(&adev->gfx.gfx_ring[0]);
- else
- amdgpu_fence_process(&adev->gfx.gfx_ring[1]);
+ /*
+ * MES splits gfx HQDs per (me,pipe): KGQ owns queue=0,
+ * userq gfx owns queue>=1 (see amdgpu_mes_get_hqd_mask).
+ * Require a strict (me,pipe,queue) match so userq gfx
+ * EOPs fall through to amdgpu_userq_process_fence_irq().
+ */
+ for (i = 0; i < adev->gfx.num_gfx_rings; i++) {
+ ring = &adev->gfx.gfx_ring[i];
+ if ((ring->me == me_id) &&
+ (ring->pipe == pipe_id) &&
+ (ring->queue == queue_id)) {
+ amdgpu_fence_process(ring);
+ return 0;
+ }
+ }
break;
case 1:
case 2:
@@ -4884,13 +4898,20 @@ static int gfx_v12_0_eop_irq(struct amdgpu_device *adev,
*/
if ((ring->me == me_id) &&
(ring->pipe == pipe_id) &&
- (ring->queue == queue_id))
+ (ring->queue == queue_id)) {
amdgpu_fence_process(ring);
+ return 0;
+ }
}
break;
+ default:
+ break;
}
}

+ if (adev->enable_mes && doorbell_offset)
+ amdgpu_userq_process_fence_irq(adev, doorbell_offset);
+
return 0;
}

diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c
index 68db1bc73bc7..62d7ebba27b2 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c
@@ -248,7 +248,7 @@ static void gfx_v12_1_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel,
WAIT_REG_MEM_FUNCTION(3))); /* equal */

if (mem_space)
- BUG_ON(addr0 & 0x3); /* Dword align */
+ WARN_ON(addr0 & 0x3); /* Dword align */
amdgpu_ring_write(ring, addr0);
amdgpu_ring_write(ring, addr1);
amdgpu_ring_write(ring, ref);
@@ -2502,10 +2502,19 @@ static int gfx_v12_1_xcc_cp_resume(struct amdgpu_device *adev, uint16_t xcc_mask

gfx_v12_1_xcc_cp_compute_enable(adev, true, xcc_id);

- if (adev->enable_mes_kiq && adev->mes.kiq_hw_init)
+ if (adev->enable_mes_kiq && adev->mes.kiq_hw_init) {
r = amdgpu_mes_kiq_hw_init(adev, xcc_id);
- else
+ /*
+ * With MES, GFX KIQ ring is owned by the MES and is never
+ * initialized/used directly by the driver, so it must
+ * not be left flagged as ready. mes_v12_0_hw_init() clears
+ * but clear here if MES init fails
+ */
+ if (r)
+ adev->gfx.kiq[xcc_id].ring.sched.ready = false;
+ } else {
r = gfx_v12_1_xcc_kiq_resume(adev, xcc_id);
+ }
if (r)
return r;

@@ -3373,7 +3382,7 @@ static void gfx_v12_1_ring_emit_ib_compute(struct amdgpu_ring *ring,
}

amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2));
- BUG_ON(ib->gpu_addr & 0x3); /* Dword align */
+ WARN_ON(ib->gpu_addr & 0x3); /* Dword align */
amdgpu_ring_write(ring,
#ifdef __BIG_ENDIAN
(2 << 0) |
@@ -3406,9 +3415,9 @@ static void gfx_v12_1_ring_emit_fence(struct amdgpu_ring *ring, u64 addr,
* aligned if only send 32bit data low (discard data high)
*/
if (write64bit)
- BUG_ON(addr & 0x7);
+ WARN_ON(addr & 0x7);
else
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
@@ -3455,9 +3464,6 @@ static void gfx_v12_1_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr,
{
struct amdgpu_device *adev = ring->adev;

- /* we only allocate 32bit for each seq wb address */
- BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT);
-
/* write fence seq to the "addr" */
amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
amdgpu_ring_write(ring, (WRITE_DATA_DST_SEL(5) | WR_CONFIRM));
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
index a6b4c8f41dc1..36b6214f4c66 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
@@ -6252,9 +6252,6 @@ static void gfx_v8_0_ring_emit_fence_compute(struct amdgpu_ring *ring,
static void gfx_v8_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr,
u64 seq, unsigned int flags)
{
- /* we only allocate 32bit for each seq wb address */
- BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT);
-
/* write fence seq to the "addr" */
amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
index 86c7c2a429b7..d54ecc520c76 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
@@ -1183,7 +1183,7 @@ static void gfx_v9_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel,
WAIT_REG_MEM_ENGINE(eng_sel)));

if (mem_space)
- BUG_ON(addr0 & 0x3); /* Dword align */
+ WARN_ON(addr0 & 0x3); /* Dword align */
amdgpu_ring_write(ring, addr0);
amdgpu_ring_write(ring, addr1);
amdgpu_ring_write(ring, ref);
@@ -4049,6 +4049,41 @@ static int gfx_v9_0_hw_init(struct amdgpu_ip_block *ip_block)
return r;
}

+static void gfx_v9_0_deactivate_kcq_hqd(struct amdgpu_device *adev)
+{
+ amdgpu_gfx_rlc_enter_safe_mode(adev, 0);
+ for (int i = 0; i < adev->gfx.num_compute_rings; i++) {
+ u32 tmp;
+ struct amdgpu_ring *ring = &adev->gfx.compute_ring[i];
+
+ mutex_lock(&adev->srbm_mutex);
+ soc15_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0, 0);
+ tmp = RREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE);
+ /* disable the queue if it's active */
+ if (tmp & CP_HQD_ACTIVE__ACTIVE_MASK) {
+ int j;
+
+ WREG32_SOC15(GC, 0, mmCP_HQD_DEQUEUE_REQUEST, 1);
+ for (j = 0; j < adev->usec_timeout; j++) {
+ tmp = RREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE);
+ if (!(tmp & CP_HQD_ACTIVE__ACTIVE_MASK))
+ break;
+ udelay(1);
+ }
+ if (j == AMDGPU_MAX_USEC_TIMEOUT) {
+ DRM_DEBUG("comp_%u_%u_%u dequeue request failed.\n",
+ ring->me, ring->pipe, ring->queue);
+ /* Manual disable if dequeue request times out */
+ WREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE, 0);
+ }
+ WREG32_SOC15(GC, 0, mmCP_HQD_DEQUEUE_REQUEST, 0);
+ }
+ soc15_grbm_select(adev, 0, 0, 0, 0, 0);
+ mutex_unlock(&adev->srbm_mutex);
+ }
+ amdgpu_gfx_rlc_exit_safe_mode(adev, 0);
+}
+
static int gfx_v9_0_hw_fini(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@ -4075,6 +4110,10 @@ static int gfx_v9_0_hw_fini(struct amdgpu_ip_block *ip_block)
return 0;
}

+ if ((adev->flags & AMD_IS_APU) && amdgpu_in_reset(adev) &&
+ amdgpu_asic_reset_method(adev) == AMD_RESET_METHOD_MODE2)
+ gfx_v9_0_deactivate_kcq_hqd(adev);
+
/* Use deinitialize sequence from CAIL when unbinding device from driver,
* otherwise KIQ is hanging when binding back
*/
@@ -5427,7 +5466,7 @@ static void gfx_v9_0_ring_emit_ib_gfx(struct amdgpu_ring *ring,
}

amdgpu_ring_write(ring, header);
- BUG_ON(ib->gpu_addr & 0x3); /* Dword align */
+ WARN_ON(ib->gpu_addr & 0x3); /* Dword align */
amdgpu_ring_write(ring,
#ifdef __BIG_ENDIAN
(2 << 0) |
@@ -5523,7 +5562,7 @@ static void gfx_v9_0_ring_emit_ib_compute(struct amdgpu_ring *ring,
}

amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2));
- BUG_ON(ib->gpu_addr & 0x3); /* Dword align */
+ WARN_ON(ib->gpu_addr & 0x3); /* Dword align */
amdgpu_ring_write(ring,
#ifdef __BIG_ENDIAN
(2 << 0) |
@@ -5564,9 +5603,9 @@ static void gfx_v9_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr,
* aligned if only send 32bit data low (discard data high)
*/
if (write64bit)
- BUG_ON(addr & 0x7);
+ WARN_ON(addr & 0x7);
else
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c
index ad4d442e7345..c4dffd9fc536 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c
@@ -405,7 +405,7 @@ static void gfx_v9_4_3_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel,
WAIT_REG_MEM_ENGINE(eng_sel)));

if (mem_space)
- BUG_ON(addr0 & 0x3); /* Dword align */
+ WARN_ON(addr0 & 0x3); /* Dword align */
amdgpu_ring_write(ring, addr0);
amdgpu_ring_write(ring, addr1);
amdgpu_ring_write(ring, ref);
@@ -2857,7 +2857,7 @@ static void gfx_v9_4_3_ring_emit_ib_compute(struct amdgpu_ring *ring,
}

amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2));
- BUG_ON(ib->gpu_addr & 0x3); /* Dword align */
+ WARN_ON(ib->gpu_addr & 0x3); /* Dword align */
amdgpu_ring_write(ring,
#ifdef __BIG_ENDIAN
(2 << 0) |
@@ -2891,9 +2891,9 @@ static void gfx_v9_4_3_ring_emit_fence(struct amdgpu_ring *ring, u64 addr,
* aligned if only send 32bit data low (discard data high)
*/
if (write64bit)
- BUG_ON(addr & 0x7);
+ WARN_ON(addr & 0x7);
else
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
@@ -2953,9 +2953,6 @@ static void gfx_v9_4_3_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr,
{
struct amdgpu_device *adev = ring->adev;

- /* we only allocate 32bit for each seq wb address */
- BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT);
-
/* write fence seq to the "addr" */
amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c
index 0c746580de11..d8204fbc198d 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c
@@ -1010,7 +1010,7 @@ void jpeg_v4_0_3_dec_ring_nop(struct amdgpu_ring *ring, uint32_t count)
static bool jpeg_v4_0_3_is_idle(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
- bool ret = false;
+ bool ret = true;
int i, j;

for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) {
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
index 250316704dfa..ae3afc7ab326 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
@@ -657,7 +657,7 @@ static void jpeg_v5_0_1_dec_ring_set_wptr(struct amdgpu_ring *ring)
static bool jpeg_v5_0_1_is_idle(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
- bool ret = false;
+ bool ret = true;
int i, j;

for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) {
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c
index a926a330700e..fcb5099d5e35 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c
@@ -557,6 +557,7 @@ static int mes_v11_0_suspend_gang(struct amdgpu_mes *mes,
mes_suspend_gang_pkt.gang_context_addr = input->gang_context_addr;
mes_suspend_gang_pkt.suspend_fence_addr = input->suspend_fence_addr;
mes_suspend_gang_pkt.suspend_fence_value = input->suspend_fence_value;
+ mes_suspend_gang_pkt.doorbell_offset = input->doorbell_offset;

return mes_v11_0_submit_pkt_and_poll_completion(mes,
&mes_suspend_gang_pkt, sizeof(mes_suspend_gang_pkt),
@@ -576,6 +577,7 @@ static int mes_v11_0_resume_gang(struct amdgpu_mes *mes,

mes_resume_gang_pkt.resume_all_gangs = input->resume_all_gangs;
mes_resume_gang_pkt.gang_context_addr = input->gang_context_addr;
+ mes_resume_gang_pkt.doorbell_offset = input->doorbell_offset;

return mes_v11_0_submit_pkt_and_poll_completion(mes,
&mes_resume_gang_pkt, sizeof(mes_resume_gang_pkt),
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c
index 78bdfed0a7fd..5e82a494662f 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c
@@ -457,7 +457,7 @@ static void sdma_v4_4_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64
/* write the fence */
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE));
/* zero in first two bits */
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
@@ -467,7 +467,7 @@ static void sdma_v4_4_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64
addr += 4;
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE));
/* zero in first two bits */
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(seq));
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c
index 52f4e9e099cb..00b51fae2d8d 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c
@@ -527,7 +527,7 @@ static void sdma_v5_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) |
SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */
/* zero in first two bits */
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
@@ -538,7 +538,7 @@ static void sdma_v5_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) |
SDMA_PKT_FENCE_HEADER_MTYPE(0x3));
/* zero in first two bits */
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(seq));
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c
index b4fb90cc8f7d..34e20adfe7bc 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c
@@ -377,7 +377,7 @@ static void sdma_v5_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) |
SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */
/* zero in first two bits */
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
@@ -388,7 +388,7 @@ static void sdma_v5_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) |
SDMA_PKT_FENCE_HEADER_MTYPE(0x3));
/* zero in first two bits */
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(seq));
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c
index 8ca46e1e474e..9438a639c560 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c
@@ -360,7 +360,7 @@ static void sdma_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se
amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) |
SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */
/* zero in first two bits */
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
@@ -371,7 +371,7 @@ static void sdma_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se
amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) |
SDMA_PKT_FENCE_HEADER_MTYPE(0x3));
/* zero in first two bits */
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(seq));
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c
index 37191e2918d4..8da147e279c6 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c
@@ -363,7 +363,7 @@ static void sdma_v7_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se
amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) |
SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */
/* zero in first two bits */
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
@@ -374,7 +374,7 @@ static void sdma_v7_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se
amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) |
SDMA_PKT_FENCE_HEADER_MTYPE(0x3));
/* zero in first two bits */
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(seq));
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c
index 9c9bbe043a47..116b42f875d5 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c
@@ -331,7 +331,7 @@ static void sdma_v7_1_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se
amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) |
SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */
/* zero in first two bits */
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
@@ -342,7 +342,7 @@ static void sdma_v7_1_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se
amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) |
SDMA_PKT_FENCE_HEADER_MTYPE(0x3));
/* zero in first two bits */
- BUG_ON(addr & 0x3);
+ WARN_ON(addr & 0x3);
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(seq));
diff --git a/drivers/gpu/drm/amd/amdgpu/soc24.c b/drivers/gpu/drm/amd/amdgpu/soc24.c
index d1adf19a51c4..d5bbb022426e 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc24.c
+++ b/drivers/gpu/drm/amd/amdgpu/soc24.c
@@ -515,8 +515,36 @@ static int soc24_common_suspend(struct amdgpu_ip_block *ip_block)
return soc24_common_hw_fini(ip_block);
}

+static bool soc24_need_reset_on_resume(struct amdgpu_device *adev)
+{
+ u32 sol_reg1, sol_reg2;
+
+ /* Will reset for the following suspend abort cases.
+ * 1) Only reset dGPU side.
+ * 2) S3 suspend got aborted and TOS is active.
+ * As for dGPU suspend abort cases the SOL value
+ * will be kept as zero at this resume point.
+ */
+ if (!(adev->flags & AMD_IS_APU) && adev->in_s3) {
+ sol_reg1 = RREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_81);
+ msleep(100);
+ sol_reg2 = RREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_81);
+
+ return (sol_reg1 != sol_reg2);
+ }
+
+ return false;
+}
+
static int soc24_common_resume(struct amdgpu_ip_block *ip_block)
{
+ struct amdgpu_device *adev = ip_block->adev;
+
+ if (soc24_need_reset_on_resume(adev)) {
+ dev_info(adev->dev, "S3 suspend aborted, resetting...");
+ soc24_asic_reset(adev);
+ }
+
return soc24_common_hw_init(ip_block);
}

diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
index ff7269bafae8..894780669f9c 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
@@ -1927,14 +1927,17 @@ static int vcn_v4_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job,
#define RENCODE_IB_PARAM_SESSION_INIT 0x00000003

/* return the offset in ib if id is found, -1 otherwise */
-static int vcn_v4_0_enc_find_ib_param(struct amdgpu_ib *ib, uint32_t id, int start)
+static int vcn_v4_0_enc_find_ib_param(struct amdgpu_ib *ib, uint32_t id, int start, uint32_t *length)
{
int i;
uint32_t len;

for (i = start; (len = amdgpu_ib_get_value(ib, i)) >= 8; i += len / 4) {
- if (amdgpu_ib_get_value(ib, i + 1) == id)
+ if (amdgpu_ib_get_value(ib, i + 1) == id) {
+ if (length)
+ *length = len;
return i;
+ }
}
return -1;
}
@@ -1944,14 +1947,14 @@ static int vcn_v4_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p,
struct amdgpu_ib *ib)
{
struct amdgpu_ring *ring = amdgpu_job_ring(job);
- uint32_t val;
+ uint32_t val, len;
int idx = 0, sidx;

/* The first instance can decode anything */
if (!ring->me)
return 0;

- while ((idx = vcn_v4_0_enc_find_ib_param(ib, RADEON_VCN_ENGINE_INFO, idx)) >= 0) {
+ while ((idx = vcn_v4_0_enc_find_ib_param(ib, RADEON_VCN_ENGINE_INFO, idx, &len)) >= 0) {
val = amdgpu_ib_get_value(ib, idx + 2); /* RADEON_VCN_ENGINE_TYPE */
if (val == RADEON_VCN_ENGINE_TYPE_DECODE) {
uint32_t valid_buf_flag = amdgpu_ib_get_value(ib, idx + 6);
@@ -1964,12 +1967,12 @@ static int vcn_v4_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p,
amdgpu_ib_get_value(ib, idx + 8);
return vcn_v4_0_dec_msg(p, job, msg_buffer_addr);
} else if (val == RADEON_VCN_ENGINE_TYPE_ENCODE) {
- sidx = vcn_v4_0_enc_find_ib_param(ib, RENCODE_IB_PARAM_SESSION_INIT, idx);
+ sidx = vcn_v4_0_enc_find_ib_param(ib, RENCODE_IB_PARAM_SESSION_INIT, idx, NULL);
if (sidx >= 0 &&
amdgpu_ib_get_value(ib, sidx + 2) == RENCODE_ENCODE_STANDARD_AV1)
return vcn_v4_0_limit_sched(p, job);
}
- idx += amdgpu_ib_get_value(ib, idx) / 4;
+ idx += len / 4;
}
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index 78068b2c9685..4f7f9d0bd800 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -1837,13 +1837,13 @@ static int criu_checkpoint_devices(struct kfd_process *p,
struct kfd_criu_device_bucket *device_buckets = NULL;
int ret = 0, i;

- device_buckets = kvzalloc(num_devices * sizeof(*device_buckets), GFP_KERNEL);
+ device_buckets = kvcalloc(num_devices, sizeof(*device_buckets), GFP_KERNEL);
if (!device_buckets) {
ret = -ENOMEM;
goto exit;
}

- device_priv = kvzalloc(num_devices * sizeof(*device_priv), GFP_KERNEL);
+ device_priv = kvcalloc(num_devices, sizeof(*device_priv), GFP_KERNEL);
if (!device_priv) {
ret = -ENOMEM;
goto exit;
@@ -1963,17 +1963,17 @@ static int criu_checkpoint_bos(struct kfd_process *p,
int ret = 0, pdd_index, bo_index = 0, id;
void *mem;

- bo_buckets = kvzalloc(num_bos * sizeof(*bo_buckets), GFP_KERNEL);
+ bo_buckets = kvcalloc(num_bos, sizeof(*bo_buckets), GFP_KERNEL);
if (!bo_buckets)
return -ENOMEM;

- bo_privs = kvzalloc(num_bos * sizeof(*bo_privs), GFP_KERNEL);
+ bo_privs = kvcalloc(num_bos, sizeof(*bo_privs), GFP_KERNEL);
if (!bo_privs) {
ret = -ENOMEM;
goto exit;
}

- files = kvzalloc(num_bos * sizeof(struct file *), GFP_KERNEL);
+ files = kvcalloc(num_bos, sizeof(struct file *), GFP_KERNEL);
if (!files) {
ret = -ENOMEM;
goto exit;
@@ -2510,7 +2510,7 @@ static int criu_restore_bos(struct kfd_process *p,
if (!bo_buckets)
return -ENOMEM;

- files = kvzalloc(args->num_bos * sizeof(struct file *), GFP_KERNEL);
+ files = kvcalloc(args->num_bos, sizeof(struct file *), GFP_KERNEL);
if (!files) {
ret = -ENOMEM;
goto exit;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index ee413117b0ff..10a5916ec6c2 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -2936,6 +2936,7 @@ static void deallocate_hiq_sdma_mqd(struct kfd_node *dev,
struct device_queue_manager *device_queue_manager_init(struct kfd_node *dev)
{
struct device_queue_manager *dqm;
+ int i;

pr_debug("Loading device queue manager\n");

@@ -3062,6 +3063,9 @@ struct device_queue_manager *device_queue_manager_init(struct kfd_node *dev)
deallocate_hiq_sdma_mqd(dev, &dqm->hiq_sdma_mqd);

out_free:
+ for (i = 0; i < KFD_MQD_TYPE_MAX; i++)
+ kfree(dqm->mqd_mgrs[i]);
+
kfree(dqm);
return NULL;
}
@@ -3649,6 +3653,12 @@ bool kfd_dqm_is_queue_in_process(struct device_queue_manager *dqm,
dqm_unlock(dqm);
return r;
}
+
+size_t mqd_size_from_queue_type(struct device_queue_manager *dqm, enum kfd_queue_type type)
+{
+ return dqm->mqd_mgrs[get_mqd_type_from_queue_type(type)]->mqd_size;
+}
+
#if defined(CONFIG_DEBUG_FS)

static void seq_reg_dump(struct seq_file *m,
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
index 3272328da11f..4a1dcae95f75 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
@@ -329,6 +329,8 @@ int debug_refresh_runlist(struct device_queue_manager *dqm);
bool kfd_dqm_is_queue_in_process(struct device_queue_manager *dqm,
struct qcm_process_device *qpd,
int doorbell_off, u32 *queue_format);
+size_t mqd_size_from_queue_type(struct device_queue_manager *dqm,
+ enum kfd_queue_type type);

static inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd)
{
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
index 81900b49d9d5..ebdd1fbf9ac0 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
@@ -107,6 +107,9 @@ static int allocate_event_notification_slot(struct kfd_process *p,
}

if (restore_id) {
+ if (*restore_id >= KFD_SIGNAL_EVENT_LIMIT)
+ return -EINVAL;
+
id = idr_alloc(&p->event_idr, ev, *restore_id, *restore_id + 1,
GFP_KERNEL);
} else {
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
index 06ca6235ff1b..63ea70e5c0e6 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
@@ -127,6 +127,7 @@ struct mqd_manager {
struct mutex mqd_mutex;
struct kfd_node *dev;
uint32_t mqd_size;
+ uint32_t ctl_stack_size;
};

struct mqd_user_context_save_area_header {
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c
index 77fb41e2486a..f36f0e8631d4 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c
@@ -198,8 +198,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd,
* more than (EOP entry count - 1) so a queue size of 0x800 dwords
* is safe, giving a maximum field value of 0xA.
*/
- m->cp_hqd_eop_control = min(0xA,
- ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1);
+ m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA,
+ ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0;
m->cp_hqd_eop_base_addr_lo =
lower_32_bits(q->eop_ring_buffer_address >> 8);
m->cp_hqd_eop_base_addr_hi =
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c
index 527c531676e4..d09ba9b30b6e 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c
@@ -237,8 +237,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd,
* more than (EOP entry count - 1) so a queue size of 0x800 dwords
* is safe, giving a maximum field value of 0xA.
*/
- m->cp_hqd_eop_control = min(0xA,
- ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1);
+ m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA,
+ ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0;
m->cp_hqd_eop_base_addr_lo =
lower_32_bits(q->eop_ring_buffer_address >> 8);
m->cp_hqd_eop_base_addr_hi =
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c
index b3e122d7876e..9897cc604865 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c
@@ -212,8 +212,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd,
* more than (EOP entry count - 1) so a queue size of 0x800 dwords
* is safe, giving a maximum field value of 0xA.
*/
- m->cp_hqd_eop_control = min(0xA,
- ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1);
+ m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA,
+ ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0;
m->cp_hqd_eop_base_addr_lo =
lower_32_bits(q->eop_ring_buffer_address >> 8);
m->cp_hqd_eop_base_addr_hi =
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c
index 475589b924e9..431a940f91f3 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c
@@ -294,8 +294,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd,
* more than (EOP entry count - 1) so a queue size of 0x800 dwords
* is safe, giving a maximum field value of 0xA.
*/
- m->cp_hqd_eop_control = min(0xA,
- ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1);
+ m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA,
+ ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0;
m->cp_hqd_eop_base_addr_lo =
lower_32_bits(q->eop_ring_buffer_address >> 8);
m->cp_hqd_eop_base_addr_hi =
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
index f6d9d81003dc..9164735058bc 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
@@ -27,6 +27,7 @@
#include <linux/uaccess.h>
#include "kfd_priv.h"
#include "kfd_mqd_manager.h"
+#include "kfd_topology.h"
#include "v9_structs.h"
#include "gc/gc_9_0_offset.h"
#include "gc/gc_9_0_sh_mask.h"
@@ -400,8 +401,11 @@ static int get_wave_state(struct mqd_manager *mm, void *mqd,
static int get_checkpoint_info(struct mqd_manager *mm, void *mqd, u32 *ctl_stack_size)
{
struct v9_mqd *m = get_mqd(mqd);
+ u32 per_xcc_size;

- if (check_mul_overflow(m->cp_hqd_cntl_stack_size, NUM_XCC(mm->dev->xcc_mask), ctl_stack_size))
+ per_xcc_size = min_t(u32, m->cp_hqd_cntl_stack_size, mm->ctl_stack_size);
+
+ if (check_mul_overflow(per_xcc_size, NUM_XCC(mm->dev->xcc_mask), ctl_stack_size))
return -EINVAL;

return 0;
@@ -410,13 +414,15 @@ static int get_checkpoint_info(struct mqd_manager *mm, void *mqd, u32 *ctl_stack
static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, void *ctl_stack_dst)
{
struct v9_mqd *m;
+ u32 ctl_stack_copy_size;
/* Control stack is located one page after MQD. */
void *ctl_stack = (void *)((uintptr_t)mqd + AMDGPU_GPU_PAGE_SIZE);

m = get_mqd(mqd);
+ ctl_stack_copy_size = min_t(u32, m->cp_hqd_cntl_stack_size, mm->ctl_stack_size);

memcpy(mqd_dst, m, sizeof(struct v9_mqd));
- memcpy(ctl_stack_dst, ctl_stack, m->cp_hqd_cntl_stack_size);
+ memcpy(ctl_stack_dst, ctl_stack, ctl_stack_copy_size);
}

static void checkpoint_mqd_v9_4_3(struct mqd_manager *mm,
@@ -425,15 +431,19 @@ static void checkpoint_mqd_v9_4_3(struct mqd_manager *mm,
void *ctl_stack_dst)
{
struct v9_mqd *m;
+ u32 ctl_stack_stride;
int xcc;
uint64_t size = get_mqd(mqd)->cp_mqd_stride_size;

+ ctl_stack_stride = min_t(u32, get_mqd(mqd)->cp_hqd_cntl_stack_size,
+ mm->ctl_stack_size);
+
for (xcc = 0; xcc < NUM_XCC(mm->dev->xcc_mask); xcc++) {
m = get_mqd(mqd + size * xcc);

checkpoint_mqd(mm, m,
(uint8_t *)mqd_dst + sizeof(*m) * xcc,
- (uint8_t *)ctl_stack_dst + m->cp_hqd_cntl_stack_size * xcc);
+ (uint8_t *)ctl_stack_dst + ctl_stack_stride * xcc);
}
}

@@ -987,6 +997,15 @@ struct mqd_manager *mqd_manager_init_v9(enum KFD_MQD_TYPE type,
mqd->is_occupied = kfd_is_occupied_cp;
mqd->get_checkpoint_info = get_checkpoint_info;
mqd->mqd_size = sizeof(struct v9_mqd);
+ if (dev->kfd->cwsr_enabled) {
+ struct kfd_topology_device *topo_dev;
+
+ topo_dev = kfd_topology_device_by_id(dev->id);
+ if (topo_dev)
+ mqd->ctl_stack_size =
+ ALIGN(topo_dev->node_props.ctl_stack_size,
+ AMDGPU_GPU_PAGE_SIZE);
+ }
mqd->mqd_stride = mqd_stride_v9;
#if defined(CONFIG_DEBUG_FS)
mqd->debugfs_show_mqd = debugfs_show_mqd;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c
index 431a20323146..f2a24edeba84 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c
@@ -209,8 +209,8 @@ static void __update_mqd(struct mqd_manager *mm, void *mqd,
* more than (EOP entry count - 1) so a queue size of 0x800 dwords
* is safe, giving a maximum field value of 0xA.
*/
- m->cp_hqd_eop_control |= min(0xA,
- order_base_2(q->eop_ring_buffer_size / 4) - 1);
+ m->cp_hqd_eop_control |= q->eop_ring_buffer_size ? min(0xA,
+ order_base_2(q->eop_ring_buffer_size / 4) - 1) : 0;
m->cp_hqd_eop_base_addr_lo =
lower_32_bits(q->eop_ring_buffer_address >> 8);
m->cp_hqd_eop_base_addr_hi =
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
index d5b07789eda4..347e54456f7d 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
@@ -436,7 +436,8 @@ enum kfd_queue_type {
KFD_QUEUE_TYPE_SDMA,
KFD_QUEUE_TYPE_HIQ,
KFD_QUEUE_TYPE_SDMA_XGMI,
- KFD_QUEUE_TYPE_SDMA_BY_ENG_ID
+ KFD_QUEUE_TYPE_SDMA_BY_ENG_ID,
+ KFD_QUEUE_TYPE_MAX,
};

enum kfd_queue_format {
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
index 44e39ce222b7..858cf5801f05 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
@@ -1003,6 +1003,23 @@ int kfd_criu_restore_queue(struct kfd_process *p,
goto exit;
}

+ pdd = kfd_process_device_data_by_id(p, q_data->gpu_id);
+ if (!pdd) {
+ pr_err("Failed to get pdd\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (q_data->type >= KFD_QUEUE_TYPE_MAX) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (q_data->mqd_size != mqd_size_from_queue_type(pdd->dev->dqm, q_data->type)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
*priv_data_offset += sizeof(*q_data);
q_extra_data_size = (uint64_t)q_data->ctl_stack_size + q_data->mqd_size;

@@ -1025,13 +1042,6 @@ int kfd_criu_restore_queue(struct kfd_process *p,

*priv_data_offset += q_extra_data_size;

- pdd = kfd_process_device_data_by_id(p, q_data->gpu_id);
- if (!pdd) {
- pr_err("Failed to get pdd\n");
- ret = -EINVAL;
- goto exit;
- }
-
/*
* data stored in this order:
* mqd[xcc0], mqd[xcc1],..., ctl_stack[xcc0], ctl_stack[xcc1]...
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
index 28354a4e5dd5..98a5512b701b 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
@@ -23,6 +23,7 @@
*/

#include <linux/slab.h>
+#include <linux/overflow.h>
#include "kfd_priv.h"
#include "kfd_topology.h"
#include "kfd_svm.h"
@@ -235,7 +236,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope
struct kfd_topology_device *topo_dev;
u64 expected_queue_size;
struct amdgpu_vm *vm;
- u32 total_cwsr_size;
+ u64 total_cwsr_size;
int err;

topo_dev = kfd_topology_device_by_id(pdd->dev->id);
@@ -308,8 +309,14 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope
goto out_err_unreserve;
}

- total_cwsr_size = (properties->ctx_save_restore_area_size +
- topo_dev->node_props.debug_memory_size) * NUM_XCC(pdd->dev->xcc_mask);
+ total_cwsr_size = (u64)properties->ctx_save_restore_area_size +
+ topo_dev->node_props.debug_memory_size;
+ if (check_mul_overflow(total_cwsr_size,
+ NUM_XCC(pdd->dev->xcc_mask),
+ &total_cwsr_size)) {
+ err = -EINVAL;
+ goto out_err_unreserve;
+ }
total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE);

err = kfd_queue_buffer_get(vm, (void *)properties->ctx_save_restore_area_address,
@@ -344,7 +351,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope
int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_properties *properties)
{
struct kfd_topology_device *topo_dev;
- u32 total_cwsr_size;
+ u64 total_cwsr_size;

kfd_queue_buffer_put(&properties->wptr_bo);
kfd_queue_buffer_put(&properties->rptr_bo);
@@ -355,8 +362,12 @@ int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_prope
topo_dev = kfd_topology_device_by_id(pdd->dev->id);
if (!topo_dev)
return -EINVAL;
- total_cwsr_size = (properties->ctx_save_restore_area_size +
- topo_dev->node_props.debug_memory_size) * NUM_XCC(pdd->dev->xcc_mask);
+ total_cwsr_size = (u64)properties->ctx_save_restore_area_size +
+ topo_dev->node_props.debug_memory_size;
+ if (check_mul_overflow(total_cwsr_size,
+ NUM_XCC(pdd->dev->xcc_mask),
+ &total_cwsr_size))
+ return -EINVAL;
total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE);

kfd_queue_buffer_svm_put(pdd, properties->ctx_save_restore_area_address, total_cwsr_size);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
index 0b54ad8036b6..13f170a6de1f 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
@@ -1144,7 +1144,7 @@ static int
svm_range_split_tail(struct svm_range *prange, uint64_t new_last,
struct list_head *insert_list, struct list_head *remap_list)
{
- unsigned long last_align_down = ALIGN_DOWN(prange->last, 512);
+ unsigned long last_align_down = ALIGN_DOWN(prange->last + 1, 512);
unsigned long start_align = ALIGN(prange->start, 512);
bool huge_page_mapping = last_align_down > start_align;
struct svm_range *tail = NULL;
@@ -1168,7 +1168,7 @@ static int
svm_range_split_head(struct svm_range *prange, uint64_t new_start,
struct list_head *insert_list, struct list_head *remap_list)
{
- unsigned long last_align_down = ALIGN_DOWN(prange->last, 512);
+ unsigned long last_align_down = ALIGN_DOWN(prange->last + 1, 512);
unsigned long start_align = ALIGN(prange->start, 512);
bool huge_page_mapping = last_align_down > start_align;
struct svm_range *head = NULL;
@@ -1181,8 +1181,8 @@ svm_range_split_head(struct svm_range *prange, uint64_t new_start,

list_add(&head->list, insert_list);

- if (huge_page_mapping && head->last + 1 > start_align &&
- head->last + 1 < last_align_down && (!IS_ALIGNED(head->last, 512)))
+ if (huge_page_mapping && new_start > start_align &&
+ new_start < last_align_down && !IS_ALIGNED(new_start, 512))
list_add(&head->update_list, remap_list);

return 0;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 1ecd7bef9ed8..93cd70183c18 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -576,89 +576,25 @@ static void schedule_dc_vmin_vmax(struct amdgpu_device *adev,
queue_work(system_percpu_wq, &offload_work->work);
}

-static void dm_vupdate_high_irq(void *interrupt_params)
-{
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_crtc *acrtc;
- struct drm_device *drm_dev;
- struct drm_vblank_crtc *vblank;
- ktime_t frame_duration_ns, previous_timestamp;
- unsigned long flags;
- int vrr_active;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
-
- if (acrtc) {
- vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
- drm_dev = acrtc->base.dev;
- vblank = drm_crtc_vblank_crtc(&acrtc->base);
- previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
- frame_duration_ns = vblank->time - previous_timestamp;
-
- if (frame_duration_ns > 0) {
- trace_amdgpu_refresh_rate_track(acrtc->base.index,
- frame_duration_ns,
- ktime_divns(NSEC_PER_SEC, frame_duration_ns));
- atomic64_set(&irq_params->previous_timestamp, vblank->time);
- }
-
- drm_dbg_vbl(drm_dev,
- "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
- vrr_active);
-
- /* Core vblank handling is done here after end of front-porch in
- * vrr mode, as vblank timestamping will give valid results
- * while now done after front-porch. This will also deliver
- * page-flip completion events that have been queued to us
- * if a pageflip happened inside front-porch.
- */
- if (vrr_active && acrtc->dm_irq_params.stream) {
- bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
- bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
- bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
- == VRR_STATE_ACTIVE_VARIABLE;
-
- amdgpu_dm_crtc_handle_vblank(acrtc);
-
- /* BTR processing for pre-DCE12 ASICs */
- if (adev->family < AMDGPU_FAMILY_AI) {
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- mod_freesync_handle_v_update(
- adev->dm.freesync_module,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params);
-
- if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
- schedule_dc_vmin_vmax(adev,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params.adjust);
- }
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- }
- }
- }
-}
-
/**
- * dm_crtc_high_irq() - Handles CRTC interrupt
- * @interrupt_params: used for determining the CRTC instance
+ * dm_crtc_high_irq_handler() - Common OTG vblank/flip event handling
+ * @adev: amdgpu device
+ * @acrtc: the CRTC to service
*
- * Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK
- * event handler.
+ * Performs writeback completion, vblank event handling, CRC processing, VRR BTR
+ * updates and pageflip completion delivery.
+ *
+ * On DCN this is driven by VUPDATE_NO_LOCK (the register latch point) from
+ * dm_vupdate_high_irq(); on DCE it is driven by VLINE0 at the start of vblank
+ * from dm_crtc_high_irq().
*/
-static void dm_crtc_high_irq(void *interrupt_params)
+static void dm_crtc_high_irq_handler(struct amdgpu_device *adev,
+ struct amdgpu_crtc *acrtc)
{
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
struct drm_writeback_job *job;
- struct amdgpu_crtc *acrtc;
unsigned long flags;
int vrr_active;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
- if (!acrtc)
- return;
+ bool is_dcn = amdgpu_ip_version(adev, DCE_HWIP, 0) != 0;

if (acrtc->wb_conn) {
spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags);
@@ -695,12 +631,17 @@ static void dm_crtc_high_irq(void *interrupt_params)
vrr_active, acrtc->dm_irq_params.active_planes);

/**
- * Core vblank handling at start of front-porch is only possible
- * in non-vrr mode, as only there vblank timestamping will give
- * valid results while done in front-porch. Otherwise defer it
- * to dm_vupdate_high_irq after end of front-porch.
+ * Core vblank handling.
+ *
+ * On DCN this handler runs at VUPDATE_NO_LOCK, the register latch
+ * point, which is the correct place to timestamp both VRR and non-VRR
+ * vblanks.
+ *
+ * On DCE this handler runs at the start of front-porch, where only
+ * non-VRR timestamping is valid; VRR vblank is deferred to
+ * dm_vupdate_high_irq() after end of front-porch.
*/
- if (!vrr_active)
+ if (is_dcn || !vrr_active)
amdgpu_dm_crtc_handle_vblank(acrtc);

/**
@@ -733,18 +674,16 @@ static void dm_crtc_high_irq(void *interrupt_params)
}

/*
- * If there aren't any active_planes then DCH HUBP may be clock-gated.
- * In that case, pageflip completion interrupts won't fire and pageflip
- * completion events won't get delivered. Prevent this by sending
- * pending pageflip events from here if a flip is still pending.
+ * Deliver pageflip completion events (DCN only).
+ *
+ * Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch
+ * point. Deliver any pending pageflip completion event from here.
*
- * If any planes are enabled, use dm_pflip_high_irq() instead, to
- * avoid race conditions between flip programming and completion,
- * which could cause too early flip completion events.
+ * NOTE: This can deliver an event for a flip that was armed but not yet
+ * programmed into HW; that race is closed in a follow-up change by
+ * checking the programmed flip status.
*/
- if (adev->family >= AMDGPU_FAMILY_RV &&
- acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
- acrtc->dm_irq_params.active_planes == 0) {
+ if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED) {
if (acrtc->event) {
drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
acrtc->event = NULL;
@@ -756,6 +695,104 @@ static void dm_crtc_high_irq(void *interrupt_params)
spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
}

+static void dm_vupdate_high_irq(void *interrupt_params)
+{
+ struct common_irq_params *irq_params = interrupt_params;
+ struct amdgpu_device *adev = irq_params->adev;
+ struct amdgpu_crtc *acrtc;
+ struct drm_device *drm_dev;
+ struct drm_vblank_crtc *vblank;
+ ktime_t frame_duration_ns, previous_timestamp;
+ unsigned long flags;
+ int vrr_active;
+
+ acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
+ if (!acrtc)
+ return;
+
+ vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
+ drm_dev = acrtc->base.dev;
+ vblank = drm_crtc_vblank_crtc(&acrtc->base);
+ previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
+ frame_duration_ns = vblank->time - previous_timestamp;
+
+ if (frame_duration_ns > 0) {
+ trace_amdgpu_refresh_rate_track(acrtc->base.index,
+ frame_duration_ns,
+ ktime_divns(NSEC_PER_SEC, frame_duration_ns));
+ atomic64_set(&irq_params->previous_timestamp, vblank->time);
+ }
+
+ drm_dbg_vbl(drm_dev,
+ "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
+ vrr_active);
+
+ /*
+ * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver
+ * vblank and pageflip completion events; VSTARTUP and GRPH_PFLIP are
+ * not used. Run the full handler here.
+ */
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
+ dm_crtc_high_irq_handler(adev, acrtc);
+ return;
+ }
+
+ /* DCE only below. */
+
+ /* Core vblank handling is done here after end of front-porch in
+ * vrr mode, as vblank timestamping will give valid results
+ * while now done after front-porch. This will also deliver
+ * page-flip completion events that have been queued to us
+ * if a pageflip happened inside front-porch.
+ */
+ if (vrr_active && acrtc->dm_irq_params.stream) {
+ bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
+ bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
+ bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
+ == VRR_STATE_ACTIVE_VARIABLE;
+
+ amdgpu_dm_crtc_handle_vblank(acrtc);
+
+ /* BTR processing for pre-DCE12 ASICs */
+ if (adev->family < AMDGPU_FAMILY_AI) {
+ spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
+ mod_freesync_handle_v_update(
+ adev->dm.freesync_module,
+ acrtc->dm_irq_params.stream,
+ &acrtc->dm_irq_params.vrr_params);
+
+ if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
+ schedule_dc_vmin_vmax(adev,
+ acrtc->dm_irq_params.stream,
+ &acrtc->dm_irq_params.vrr_params.adjust);
+ }
+ spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
+ }
+ }
+}
+
+/**
+ * dm_crtc_high_irq() - Handles CRTC interrupt
+ * @interrupt_params: used for determining the CRTC instance
+ *
+ * Handles the CRTC/VSYNC interrupt by notifying DRM's VBLANK event handler.
+ *
+ * Used on DCE (VLINE0, set to vblank start). On DCN the equivalent handling is
+ * driven by VUPDATE_NO_LOCK in dm_vupdate_high_irq().
+ */
+static void dm_crtc_high_irq(void *interrupt_params)
+{
+ struct common_irq_params *irq_params = interrupt_params;
+ struct amdgpu_device *adev = irq_params->adev;
+ struct amdgpu_crtc *acrtc;
+
+ acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
+ if (!acrtc)
+ return;
+
+ dm_crtc_high_irq_handler(adev, acrtc);
+}
+
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
/**
* dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for
@@ -2260,9 +2297,16 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev)
adev->dm.idle_workqueue = NULL;
}

- /* Disable ISM before dc_destroy() invalidates dm->dc */
+ /*
+ * Disable ISM before dc_destroy() invalidates dm->dc.
+ *
+ * Quiesce workers first without dc_lock (they take dc_lock
+ * themselves, so syncing under it would deadlock), then drive the
+ * FSM back to FULL_POWER_RUNNING under dc_lock.
+ */
+ amdgpu_dm_ism_disable(&adev->dm);
scoped_guard(mutex, &adev->dm.dc_lock)
- amdgpu_dm_ism_disable(&adev->dm);
+ amdgpu_dm_ism_force_full_power(&adev->dm);

amdgpu_dm_destroy_drm_device(&adev->dm);

@@ -3134,7 +3178,8 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
acrtc = get_crtc_by_otg_inst(
adev, state->stream_status[i].primary_otg_inst);

- if (acrtc && state->stream_status[i].plane_count != 0) {
+ if (acrtc && state->stream_status[i].plane_count != 0 &&
+ amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) {
irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst;
rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY;
if (rc)
@@ -3162,6 +3207,13 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
*/
if (!dc_interrupt_set(adev->dm.dc, irq_source, enable))
drm_warn(adev_to_drm(adev), "Failed to %sable vblank interrupt\n", enable ? "en" : "dis");
+
+ } else if (acrtc && state->stream_status[i].plane_count != 0) {
+ /* DCN only needs to toggle VUPDATE_NO_LOCK */
+ rc = amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, enable);
+ if (rc)
+ drm_warn(adev_to_drm(adev), "Failed to %sable vupdate interrupt\n",
+ enable ? "en" : "dis");
}
}

@@ -3290,9 +3342,14 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
if (amdgpu_in_reset(adev)) {
enum dc_status res;

+ /* Quiesce ISM workers before taking dc_lock (workers take
+ * dc_lock themselves; syncing under it would deadlock).
+ */
+ amdgpu_dm_ism_disable(dm);
+
mutex_lock(&dm->dc_lock);

- amdgpu_dm_ism_disable(dm);
+ amdgpu_dm_ism_force_full_power(dm);
dc_allow_idle_optimizations(adev->dm.dc, false);

dm->cached_dc_state = dc_state_create_copy(dm->dc->current_state);
@@ -3326,8 +3383,13 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)

amdgpu_dm_irq_suspend(adev);

+ /*
+ * Quiesce ISM workers before taking dc_lock (workers take dc_lock
+ * themselves; syncing under it would deadlock).
+ */
+ amdgpu_dm_ism_disable(dm);
scoped_guard(mutex, &dm->dc_lock)
- amdgpu_dm_ism_disable(dm);
+ amdgpu_dm_ism_force_full_power(dm);

hpd_rx_irq_work_suspend(dm);

@@ -3923,6 +3985,8 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps;
caps->aux_support = false;

+ panel_backlight_quirk = drm_get_panel_backlight_quirk(aconnector->drm_edid);
+
if (caps->ext_caps->bits.oled == 1
/*
* ||
@@ -3935,6 +3999,9 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
caps->aux_support = false;
else if (amdgpu_backlight == 1)
caps->aux_support = true;
+ else if (!IS_ERR_OR_NULL(panel_backlight_quirk) &&
+ panel_backlight_quirk->force_pwm)
+ caps->aux_support = false;
if (caps->aux_support)
aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;

@@ -3950,8 +4017,6 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
else
caps->aux_min_input_signal = 1;

- panel_backlight_quirk =
- drm_get_panel_backlight_quirk(aconnector->drm_edid);
if (!IS_ERR_OR_NULL(panel_backlight_quirk)) {
if (panel_backlight_quirk->min_brightness) {
caps->min_input_signal =
@@ -4717,38 +4782,6 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
* for acknowledging and handling.
*/

- /* Use VSTARTUP interrupt */
- for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP;
- i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1;
- i++) {
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->crtc_irq);
-
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 ||
- int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) {
- drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_crtc_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
/* Use otg vertical line interrupt */
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) {
@@ -4820,37 +4853,6 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
return -ENOMEM;
}

- /* Use GRPH_PFLIP interrupt */
- for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT;
- i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + dc->caps.max_otg_num - 1;
- i++) {
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->pageflip_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST ||
- int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) {
- drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_pflip_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
/* HPD */
r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT,
&adev->hpd_irq);
@@ -5382,11 +5384,11 @@ amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector)
caps = &dm->backlight_caps[aconnector->bl_idx];
if (get_brightness_range(caps, &min, &max)) {
if (power_supply_is_system_supplied() > 0)
- props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->ac_level, 100);
+ props.brightness = DIV_ROUND_CLOSEST(max * caps->ac_level, 100);
else
- props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->dc_level, 100);
+ props.brightness = DIV_ROUND_CLOSEST(max * caps->dc_level, 100);
/* min is zero, so max needs to be adjusted */
- props.max_brightness = max - min;
+ props.max_brightness = max;
drm_dbg(drm, "Backlight caps: min: %d, max: %d, ac %d, dc %d\n", min, max,
caps->ac_level, caps->dc_level);
} else
@@ -6423,8 +6425,8 @@ static void fill_dc_dirty_rects(struct drm_plane *plane,
{
struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(crtc_state);
struct rect *dirty_rects = flip_addrs->dirty_rects;
- u32 num_clips;
- struct drm_mode_rect *clips;
+ u32 num_clips = 0;
+ struct drm_mode_rect *clips = NULL;
bool bb_changed;
bool fb_changed;
u32 i = 0;
@@ -6440,8 +6442,10 @@ static void fill_dc_dirty_rects(struct drm_plane *plane,
if (new_plane_state->rotation != DRM_MODE_ROTATE_0)
goto ffu;

- num_clips = drm_plane_get_damage_clips_count(new_plane_state);
- clips = drm_plane_get_damage_clips(new_plane_state);
+ if (!new_plane_state->ignore_damage_clips) {
+ num_clips = drm_plane_get_damage_clips_count(new_plane_state);
+ clips = drm_plane_get_damage_clips(new_plane_state);
+ }

if (num_clips && (!amdgpu_damage_clips || (amdgpu_damage_clips < 0 &&
is_psr_su)))
@@ -9408,21 +9412,9 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,
if (acrtc_state) {
timing = &acrtc_state->stream->timing;

- if (amdgpu_ip_version(adev, DCE_HWIP, 0) >=
- IP_VERSION(3, 2, 0) &&
- !(adev->flags & AMD_IS_APU)) {
- /*
- * DGPUs NV3x and newer that support idle optimizations
- * experience intermittent flip-done timeouts on cursor
- * updates. Restore 5s offdelay behavior for now.
- *
- * Discussion on the issue:
- * https://lore.kernel.org/amd-gfx/20260217191632.1243826-1-sysdadmin@m1k.cloud/
- */
- config.offdelay_ms = 5000;
- config.disable_immediate = false;
- } else if (amdgpu_ip_version(adev, DCE_HWIP, 0) <
- IP_VERSION(3, 5, 0)) {
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) <
+ IP_VERSION(3, 5, 0) ||
+ !(adev->flags & AMD_IS_APU)) {
/*
* Older HW and DGPU have issues with instant off;
* use a 2 frame offdelay.
@@ -9441,14 +9433,22 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,

drm_crtc_vblank_on_config(&acrtc->base,
&config);
- /* Allow RX6xxx, RX7700, RX7800 GPUs to call amdgpu_irq_get.*/
+ /*
+ * Since pflip_high_irq is no longer registered for DCN, grab an
+ * extra reference to vupdate irq instead to workaround this
+ * issue:
+ * https://gitlab.freedesktop.org/drm/amd/-/work_items/3936
+ *
+ * The callbacks to drm_vblank_on/off should really take care of
+ * this though.
+ */
switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
case IP_VERSION(3, 0, 0):
case IP_VERSION(3, 0, 2):
case IP_VERSION(3, 0, 3):
case IP_VERSION(3, 2, 0):
- if (amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type))
- drm_err(dev, "DM_IRQ: Cannot get pageflip irq!\n");
+ if (amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type))
+ drm_err(dev, "DM_IRQ: Cannot get vupdate irq!\n");
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
if (amdgpu_irq_get(adev, &adev->vline0_irq, irq_type))
drm_err(dev, "DM_IRQ: Cannot get vline0 irq!\n");
@@ -9466,8 +9466,8 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,
if (amdgpu_irq_put(adev, &adev->vline0_irq, irq_type))
drm_err(dev, "DM_IRQ: Cannot put vline0 irq!\n");
#endif
- if (amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type))
- drm_err(dev, "DM_IRQ: Cannot put pageflip irq!\n");
+ if (amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type))
+ drm_err(dev, "DM_IRQ: Cannot put vupdate irq!\n");
}

drm_crtc_vblank_off(&acrtc->base);
@@ -9480,6 +9480,10 @@ static void dm_update_pflip_irq_state(struct amdgpu_device *adev,
int irq_type =
amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id);

+ /* GRPH_PFLIP is not used on DCN; nothing to reapply. */
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0)
+ return;
+
/**
* This reads the current state for the IRQ and force reapplies
* the setting to hardware.
@@ -9811,9 +9815,13 @@ static void update_stream_irq_parameters(
static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state)
{
+ struct amdgpu_device *adev = drm_to_adev(new_state->base.crtc->dev);
bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state);
bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state);

+ /* Only DCE gates vupdate on VRR, keep it enabled for DCN */
+ bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0;
+
if (!old_vrr_active && new_vrr_active) {
/* Transition VRR inactive -> active:
* While VRR is active, we must not disable vblank irq, as a
@@ -9823,7 +9831,8 @@ static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state,
* We also need vupdate irq for the actual core vblank handling
* at end of vblank.
*/
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
+ if (vrr_gates_vupdate)
+ WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0);
drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n",
__func__, new_state->base.crtc->base.id);
@@ -9831,7 +9840,8 @@ static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state,
/* Transition VRR active -> inactive:
* Allow vblank irq disable again for fixed refresh rate.
*/
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
+ if (vrr_gates_vupdate)
+ WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
drm_crtc_vblank_put(new_state->base.crtc);
drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n",
__func__, new_state->base.crtc->base.id);
@@ -10007,6 +10017,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
bool vrr_active = amdgpu_dm_crtc_vrr_active(acrtc_state);
bool cursor_update = false;
bool pflip_present = false;
+ bool immediate_flip = false;
bool dirty_rects_changed = false;
bool updated_planes_and_streams = false;
struct {
@@ -10173,6 +10184,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
acrtc_state->update_type == UPDATE_TYPE_FAST &&
get_mem_type(old_plane_state->fb) == get_mem_type(fb);

+ immediate_flip |= bundle->flip_addrs[planes_count].flip_immediate;
+
timestamp_ns = ktime_get_ns();
bundle->flip_addrs[planes_count].flip_timestamp_in_us = div_u64(timestamp_ns, 1000);
bundle->surface_updates[planes_count].flip_addr = &bundle->flip_addrs[planes_count];
@@ -10374,6 +10387,29 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE)
amdgpu_dm_commit_cursors(state);

+ /*
+ * On DCN, flip completion is normally delivered from VUPDATE_NO_LOCK.
+ * However, an immediate (tearing / async) flip is latched by HW right
+ * away and does not wait for the next vupdate, so deliver its
+ * completion event here after programming.
+ *
+ * On DCE, GRPH_PFLIP already fires immediately for immediate flips, so
+ * this is DCN-only.
+ */
+ if (immediate_flip && amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) {
+ spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
+ if (acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED &&
+ acrtc_attach->event) {
+ drm_crtc_accurate_vblank_count(&acrtc_attach->base);
+ drm_crtc_send_vblank_event(&acrtc_attach->base,
+ acrtc_attach->event);
+ acrtc_attach->event = NULL;
+ drm_crtc_vblank_put(&acrtc_attach->base);
+ acrtc_attach->pflip_status = AMDGPU_FLIP_NONE;
+ }
+ spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
+ }
+
cleanup:
kfree(bundle);
}
@@ -11708,6 +11744,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
/* Release extra reference */
if (new_stream)
dc_stream_release(new_stream);
+ new_stream = NULL;

/*
* We want to do dc stream updates that do not require a
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
index d69f5a75b685..9d5782b4958c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@ -308,9 +308,25 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
drm_crtc_vblank_restore(crtc);
}

- if (dc_supports_vrr(dm->dc->ctx->dce_version)) {
+ /*
+ * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver
+ * vblank and pageflip completion events, so enable it whenever vblank
+ * is enabled. On DCE, vupdate is only needed in VRR mode.
+ */
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
if (enable) {
- /* vblank irq on -> Only need vupdate irq in vrr mode */
+ rc = amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Get vupdate_irq ret=%d\n", rc);
+ } else {
+ rc = amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Put vupdate_irq ret=%d\n", rc);
+ }
+ } else if (dc_supports_vrr(dm->dc->ctx->dce_version)) {
+ if (enable) {
+ /* vblank irq on -> Only need vupdate irq in vrr mode
+ * Not ref-counted since we need explicit enable/disable
+ * for DCE VRR handling
+ */
if (amdgpu_dm_crtc_vrr_active(acrtc_state))
rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, true);
} else {
@@ -319,39 +335,46 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
}
}

- if (rc)
- return rc;
-
- /* crtc vblank or vstartup interrupt */
- if (enable) {
- rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc);
- } else {
- rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc);
- }
-
if (rc)
return rc;

/*
- * hubp surface flip interrupt
- *
- * We have no guarantee that the frontend index maps to the same
- * backend index - some even map to more than one.
- *
- * TODO: Use a different interrupt or check DC itself for the mapping.
+ * VLINE0 (crtc_irq) and GRPH_PFLIP (pageflip_irq) are only used on
+ * DCE. On DCN, vblank and pageflip completion are delivered from
+ * VUPDATE_NO_LOCK (enabled above), so don't touch them here.
*/
- if (enable) {
- rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc);
- } else {
- rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type);
- drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc);
- }
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) {
+ /* crtc vblank or vstartup interrupt */
+ if (enable) {
+ rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc);
+ } else {
+ rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc);
+ }

- if (rc)
- return rc;
+ if (rc)
+ return rc;
+
+ /*
+ * hubp surface flip interrupt
+ *
+ * We have no guarantee that the frontend index maps to the same
+ * backend index - some even map to more than one.
+ *
+ * TODO: Use a different interrupt or check DC itself for the mapping.
+ */
+ if (enable) {
+ rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc);
+ } else {
+ rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc);
+ }
+
+ if (rc)
+ return rc;
+ }

#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
/* crtc vline0 interrupt, only available on DCN+ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c
index a64e95860e99..b32c8d3ac152 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c
@@ -524,13 +524,20 @@ static void dm_ism_sso_delayed_work_func(struct work_struct *work)
}

/**
- * amdgpu_dm_ism_disable - Disable the ISM
+ * amdgpu_dm_ism_disable - Quiesce ISM workers
*
* @dm: The amdgpu display manager
*
- * Disable the idle state manager by disabling any ISM work, canceling pending
- * work, and waiting for in-progress work to finish. After disabling, the system
- * is left in DM_ISM_STATE_FULL_POWER_RUNNING state.
+ * Cancels and disables any pending or in-flight ISM delayed work and waits
+ * for in-progress work to finish. After this returns, no ISM worker can run
+ * and subsequent mod_delayed_work() calls become no-ops via
+ * clear_pending_if_disabled().
+ *
+ * Must NOT be called with dc_lock held: the workers themselves take dc_lock,
+ * so a synchronous wait under dc_lock would deadlock.
+ *
+ * The caller is responsible for driving the FSM back to FULL_POWER_RUNNING
+ * (under dc_lock) by calling amdgpu_dm_ism_force_full_power().
*/
void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm)
{
@@ -538,21 +545,54 @@ void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm)
struct amdgpu_crtc *acrtc;
struct amdgpu_dm_ism *ism;

- ASSERT(mutex_is_locked(&dm->dc_lock));
+ /*
+ * Caller must NOT hold dc_lock: the ISM delayed work handlers
+ * acquire dc_lock themselves, so waiting for them via
+ * disable_delayed_work_sync() while holding dc_lock would
+ * self-deadlock against an in-flight worker.
+ */
+ lockdep_assert_not_held(&dm->dc_lock);

drm_for_each_crtc(crtc, dm->ddev) {
acrtc = to_amdgpu_crtc(crtc);
ism = &acrtc->ism;

- /* Cancel and disable any pending work */
disable_delayed_work_sync(&ism->delayed_work);
disable_delayed_work_sync(&ism->sso_delayed_work);
+ }
+}
+
+/**
+ * amdgpu_dm_ism_force_full_power - Force every CRTC's ISM FSM to FULL_POWER
+ *
+ * @dm: The amdgpu display manager
+ *
+ * Sends DM_ISM_EVENT_EXIT_IDLE_REQUESTED to every CRTC's ISM, leaving each
+ * FSM in FULL_POWER_RUNNING. Intended to be paired with
+ * amdgpu_dm_ism_disable(): callers should first quiesce workers (without
+ * dc_lock), then take dc_lock and call this helper.
+ *
+ * Must be called with dc_lock held.
+ */
+void amdgpu_dm_ism_force_full_power(struct amdgpu_display_manager *dm)
+{
+ struct drm_crtc *crtc;
+ struct amdgpu_crtc *acrtc;
+
+ /*
+ * Caller must hold dc_lock: commit_event() drives the FSM and
+ * may touch dc state via dc_allow_idle_optimizations() etc.
+ */
+ lockdep_assert_held(&dm->dc_lock);
+
+ drm_for_each_crtc(crtc, dm->ddev) {
+ acrtc = to_amdgpu_crtc(crtc);

/*
* When disabled, leave in FULL_POWER_RUNNING state.
- * EXIT_IDLE will not queue any work
+ * EXIT_IDLE will not queue any work.
*/
- amdgpu_dm_ism_commit_event(ism,
+ amdgpu_dm_ism_commit_event(&acrtc->ism,
DM_ISM_EVENT_EXIT_IDLE_REQUESTED);
}
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h
index fde0ddc8d4e4..964408cd9a83 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h
@@ -146,6 +146,7 @@ void amdgpu_dm_ism_fini(struct amdgpu_dm_ism *ism);
void amdgpu_dm_ism_commit_event(struct amdgpu_dm_ism *ism,
enum amdgpu_dm_ism_event event);
void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm);
+void amdgpu_dm_ism_force_full_power(struct amdgpu_display_manager *dm);
void amdgpu_dm_ism_enable(struct amdgpu_display_manager *dm);

#endif
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c
index 89fc482947ef..495157072079 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c
@@ -155,7 +155,6 @@ void dcn31_update_clocks(struct clk_mgr *clk_mgr_base,
if (new_clocks->zstate_support != DCN_ZSTATE_SUPPORT_DISALLOW &&
new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) {
dcn31_smu_set_zstate_support(clk_mgr, new_clocks->zstate_support);
- dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, true);
clk_mgr_base->clks.zstate_support = new_clocks->zstate_support;
}

@@ -181,7 +180,6 @@ void dcn31_update_clocks(struct clk_mgr *clk_mgr_base,
if (new_clocks->zstate_support == DCN_ZSTATE_SUPPORT_DISALLOW &&
new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) {
dcn31_smu_set_zstate_support(clk_mgr, DCN_ZSTATE_SUPPORT_DISALLOW);
- dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, false);
clk_mgr_base->clks.zstate_support = new_clocks->zstate_support;
}

diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c
index b08a70a2f571..9b9524fde2b9 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c
@@ -227,7 +227,6 @@ void dcn314_update_clocks(struct clk_mgr *clk_mgr_base,
if (new_clocks->zstate_support != DCN_ZSTATE_SUPPORT_DISALLOW &&
new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) {
dcn314_smu_set_zstate_support(clk_mgr, new_clocks->zstate_support);
- dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, true);
clk_mgr_base->clks.zstate_support = new_clocks->zstate_support;
}

@@ -252,7 +251,6 @@ void dcn314_update_clocks(struct clk_mgr *clk_mgr_base,
if (new_clocks->zstate_support == DCN_ZSTATE_SUPPORT_DISALLOW &&
new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) {
dcn314_smu_set_zstate_support(clk_mgr, DCN_ZSTATE_SUPPORT_DISALLOW);
- dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, false);
clk_mgr_base->clks.zstate_support = new_clocks->zstate_support;
}

diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c
index fda6cade30a8..e2787c7084ec 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c
@@ -276,13 +276,20 @@ static void dcn32_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr,
struct dtbclk_dto_params dto_params = {0};

/* use mask to program DTO once per tg */
- if (pipe_ctx->stream_res.tg &&
+ if (pipe_ctx->stream && pipe_ctx->stream_res.tg &&
!(tg_mask & (1 << pipe_ctx->stream_res.tg->inst))) {
tg_mask |= (1 << pipe_ctx->stream_res.tg->inst);

dto_params.otg_inst = pipe_ctx->stream_res.tg->inst;
dto_params.ref_dtbclk_khz = ref_dtbclk_khz;

+ if (dccg->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx))
+ dto_params.pixclk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10;
+
+ if (dc_is_hdmi_signal(pipe_ctx->stream->signal) ||
+ dc_is_dvi_signal(pipe_ctx->stream->signal))
+ dto_params.is_hdmi = true;
+
dccg->funcs->set_dtbclk_dto(clk_mgr->dccg, &dto_params);
//dccg->funcs->set_audio_dtbclk_dto(clk_mgr->dccg, &dto_params);
}
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c
index 2798088842f4..020d43c7fab8 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c
@@ -264,13 +264,20 @@ static void dcn35_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr,
struct dtbclk_dto_params dto_params = {0};

/* use mask to program DTO once per tg */
- if (pipe_ctx->stream_res.tg &&
+ if (pipe_ctx->stream && pipe_ctx->stream_res.tg &&
!(tg_mask & (1 << pipe_ctx->stream_res.tg->inst))) {
tg_mask |= (1 << pipe_ctx->stream_res.tg->inst);

dto_params.otg_inst = pipe_ctx->stream_res.tg->inst;
dto_params.ref_dtbclk_khz = ref_dtbclk_khz;

+ if (dccg->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx))
+ dto_params.pixclk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10;
+
+ if (dc_is_hdmi_signal(pipe_ctx->stream->signal) ||
+ dc_is_dvi_signal(pipe_ctx->stream->signal))
+ dto_params.is_hdmi = true;
+
dccg->funcs->set_dtbclk_dto(clk_mgr->dccg, &dto_params);
//dccg->funcs->set_audio_dtbclk_dto(clk_mgr->dccg, &dto_params);
}
@@ -405,6 +412,7 @@ void dcn35_update_clocks(struct clk_mgr *clk_mgr_base,
if (new_clocks->zstate_support != DCN_ZSTATE_SUPPORT_DISALLOW &&
new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) {
dcn35_smu_set_zstate_support(clk_mgr, new_clocks->zstate_support);
+ dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, true);
clk_mgr_base->clks.zstate_support = new_clocks->zstate_support;
}

@@ -424,6 +432,7 @@ void dcn35_update_clocks(struct clk_mgr *clk_mgr_base,
if (new_clocks->zstate_support == DCN_ZSTATE_SUPPORT_DISALLOW &&
new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) {
dcn35_smu_set_zstate_support(clk_mgr, DCN_ZSTATE_SUPPORT_DISALLOW);
+ dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, false);
clk_mgr_base->clks.zstate_support = new_clocks->zstate_support;
}

diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 5f2c62fcba9b..ec194dd3b798 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -1507,7 +1507,7 @@ static void disable_vbios_mode_if_required(

struct dc *dc_create(const struct dc_init_data *init_params)
{
- struct dc *dc = kzalloc_obj(*dc);
+ struct dc *dc = kvzalloc_obj(*dc);
unsigned int full_pipe_count;

if (!dc)
@@ -1555,7 +1555,7 @@ struct dc *dc_create(const struct dc_init_data *init_params)

destruct_dc:
dc_destruct(dc);
- kfree(dc);
+ kvfree(dc);
return NULL;
}

@@ -1604,7 +1604,7 @@ void dc_deinit_callbacks(struct dc *dc)
void dc_destroy(struct dc **dc)
{
dc_destruct(*dc);
- kfree(*dc);
+ kvfree(*dc);
*dc = NULL;
}

diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index 55152f12af48..61a28e287c1b 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -1727,6 +1727,8 @@ struct dc_scratch_space {
bool dp_skip_DID2;
bool dp_skip_reset_segment;
bool dp_skip_fs_144hz;
+ /* Some DP bridges don't work with RBR and must use HBR. */
+ bool dp_skip_rbr;
bool dp_mot_reset_segment;
/* Some USB4 docks do not handle turning off MST DSC once it has been enabled. */
bool dpia_mst_dsc_always_on;
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c
index ed407e779c12..2c3a20d35fe9 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c
@@ -271,7 +271,6 @@ static void dce110_stream_encoder_dp_set_stream_attribute(
bool use_vsc_sdp_for_colorimetry,
uint32_t enable_sdp_splitting)
{
- (void)use_vsc_sdp_for_colorimetry;
(void)enable_sdp_splitting;
uint32_t h_active_start;
uint32_t v_active_start;
@@ -334,6 +333,16 @@ static void dce110_stream_encoder_dp_set_stream_attribute(
if (REG(DP_MSA_MISC))
misc1 = REG_READ(DP_MSA_MISC);

+ /* For YCbCr420 and BT2020 Colorimetry Formats, VSC SDP shall be used.
+ * When MISC1, bit 6, is Set to 1, a Source device uses a VSC SDP to indicate the
+ * Pixel Encoding/Colorimetry Format and that a Sink device shall ignore MISC1, bit 7,
+ * and MISC0, bits 7:1 (MISC1, bit 7, and MISC0, bits 7:1, become "don't care").
+ */
+ if (use_vsc_sdp_for_colorimetry)
+ misc1 = misc1 | 0x40;
+ else
+ misc1 = misc1 & ~0x40;
+
/* set color depth */

switch (hw_crtc_timing.display_color_depth) {
@@ -499,6 +508,10 @@ static void dce110_stream_encoder_dp_set_stream_attribute(
hw_crtc_timing.h_addressable + hw_crtc_timing.h_border_right,
DP_MSA_VHEIGHT, hw_crtc_timing.v_border_top +
hw_crtc_timing.v_addressable + hw_crtc_timing.v_border_bottom);
+ } else {
+ /* DCE-only path */
+ if (REG(DP_MSA_MISC))
+ REG_WRITE(DP_MSA_MISC, misc1); /* MSA_MISC1 */
}
}

diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h
index 342c0afe6a94..88d6044904d1 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h
@@ -96,7 +96,8 @@

#define SE_COMMON_REG_LIST(id)\
SE_COMMON_REG_LIST_DCE_BASE(id), \
- SRI(AFMT_CNTL, DIG, id)
+ SRI(AFMT_CNTL, DIG, id), \
+ SRI(DP_MSA_MISC, DP, id)

#define SE_DCN_REG_LIST(id)\
SE_COMMON_REG_LIST_BASE(id),\
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
index 794dd6a95918..2ba323e6c3f7 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
@@ -621,7 +621,7 @@ static bool detect_dp(struct dc_link *link,
link->dpcd_caps.sink_count.bits.SINK_COUNT = 1;
/* NUTMEG requires that we use HBR, doesn't work with RBR. */
if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_00001A)
- link->preferred_link_setting.link_rate = LINK_RATE_HIGH;
+ link->wa_flags.dp_skip_rbr = true;
}

return true;
@@ -1069,8 +1069,11 @@ static bool detect_link_and_local_sink(struct dc_link *link,
link->link_enc->features.flags.bits.DP_IS_USB_C == 1) {

/* if alt mode times out, return false */
- if (!wait_for_entering_dp_alt_mode(link))
+ if (!wait_for_entering_dp_alt_mode(link)) {
+ if (prev_sink)
+ dc_sink_release(prev_sink);
return false;
+ }
}

if (!detect_dp(link, &sink_caps, reason)) {
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
index 782a45caa13d..fe8420b803ee 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
@@ -750,8 +750,10 @@ static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_setting
if (req_bw > dp_link_bandwidth_kbps(link, &link->verified_link_cap))
return false;

- if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN)
- initial_link_setting.link_rate = link->preferred_link_setting.link_rate;
+ if (link->wa_flags.dp_skip_rbr) {
+ initial_link_setting.link_rate = LINK_RATE_HIGH;
+ current_link_setting.link_rate = LINK_RATE_HIGH;
+ }

/* search for the minimum link setting that:
* 1. is supported according to the link training result
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c
index caafebe92129..3f9228033cd0 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c
@@ -992,6 +992,11 @@ struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link(
for (i = 0; i < pool->stream_enc_count; i++) {
if (!res_ctx->is_stream_enc_acquired[i] &&
pool->stream_enc[i]) {
+ /* DP/MST needs a digital encoder; skip analog/no-DP encoders */
+ if (dc_is_dp_signal(stream->signal) &&
+ (!pool->stream_enc[i]->funcs ||
+ !pool->stream_enc[i]->funcs->dp_set_stream_attribute))
+ continue;
/* Store first available for MST second display
* in daisy chain use case
*/
@@ -1014,7 +1019,7 @@ struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link(
* required for non DP connectors.
*/

- if (j >= 0 && link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT)
+ if (j >= 0 && dc_is_dp_signal(stream->signal))
return pool->stream_enc[j];

return NULL;
diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
index 614db22d62f3..00473c6284d5 100644
--- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
@@ -502,8 +502,6 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream,
*
* @stream: contains data we may need to construct VSIF (i.e. timing_3d_format, etc.)
* @info_packet: output structure where to store VSIF
- * @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated
- * @ALLMValue: ALLM bit value to advertise in HF-VSIF
*/
void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
struct dc_info_packet *info_packet)
diff --git a/drivers/gpu/drm/amd/include/mes_v11_api_def.h b/drivers/gpu/drm/amd/include/mes_v11_api_def.h
index f9629d42ada2..7808147ada38 100644
--- a/drivers/gpu/drm/amd/include/mes_v11_api_def.h
+++ b/drivers/gpu/drm/amd/include/mes_v11_api_def.h
@@ -427,6 +427,7 @@ union MESAPI__SUSPEND {
uint32_t suspend_fence_value;

struct MES_API_STATUS api_status;
+ uint32_t doorbell_offset;
};

uint32_t max_dwords_in_api[API_FRAME_SIZE_IN_DWORDS];
@@ -444,6 +445,7 @@ union MESAPI__RESUME {
uint64_t gang_context_addr;

struct MES_API_STATUS api_status;
+ uint32_t doorbell_offset;
};

uint32_t max_dwords_in_api[API_FRAME_SIZE_IN_DWORDS];
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
index 952391aecf2d..8915a4d18ffa 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
@@ -41,6 +41,8 @@

#define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name)

+#define power_2_mwatt(power) (((power) >> 8) * 1000 + ((power) & 0xff))
+
struct od_attribute {
struct kobj_attribute attribute;
struct list_head entry;
@@ -2680,6 +2682,11 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_
gc_ver != IP_VERSION(9, 4, 3)) ||
gc_ver < IP_VERSION(9, 0, 0))
*states = ATTR_STATE_UNSUPPORTED;
+
+ if (adev->scpm_enabled) {
+ dev_attr->attr.mode &= ~S_IWUGO;
+ dev_attr->store = NULL;
+ }
} else if (DEVICE_ATTR_IS(gpu_metrics)) {
if (gc_ver < IP_VERSION(9, 1, 0))
*states = ATTR_STATE_UNSUPPORTED;
@@ -3333,7 +3340,6 @@ static int amdgpu_hwmon_get_power(struct device *dev,
enum amd_pp_sensors sensor)
{
struct amdgpu_device *adev = dev_get_drvdata(dev);
- unsigned int uw;
u32 query = 0;
int r;

@@ -3342,9 +3348,7 @@ static int amdgpu_hwmon_get_power(struct device *dev,
return r;

/* convert to microwatts */
- uw = (query >> 8) * 1000000 + (query & 0xff) * 1000;
-
- return uw;
+ return power_2_mwatt(query) * 1000;
}

static ssize_t amdgpu_hwmon_show_power_avg(struct device *dev,
@@ -4882,7 +4886,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a
{
uint32_t mp1_ver = amdgpu_ip_version(adev, MP1_HWIP, 0);
uint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);
- uint32_t value;
+ uint32_t value, mwatt, centiwatt;
uint64_t value64 = 0;
uint32_t query = 0;
int size;
@@ -4907,17 +4911,21 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a
seq_printf(m, "\t%u mV (VDDNB)\n", value);
size = sizeof(uint32_t);
if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) {
+ mwatt = power_2_mwatt(query);
+ centiwatt = DIV_ROUND_CLOSEST(mwatt, 10);
if (adev->flags & AMD_IS_APU)
- seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", query >> 8, query & 0xff);
+ seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", centiwatt / 100, centiwatt % 100);
else
- seq_printf(m, "\t%u.%02u W (average SoC)\n", query >> 8, query & 0xff);
+ seq_printf(m, "\t%u.%02u W (average SoC)\n", centiwatt / 100, centiwatt % 100);
}
size = sizeof(uint32_t);
if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) {
+ mwatt = power_2_mwatt(query);
+ centiwatt = DIV_ROUND_CLOSEST(mwatt, 10);
if (adev->flags & AMD_IS_APU)
- seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", query >> 8, query & 0xff);
+ seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", centiwatt / 100, centiwatt % 100);
else
- seq_printf(m, "\t%u.%02u W (current SoC)\n", query >> 8, query & 0xff);
+ seq_printf(m, "\t%u.%02u W (current SoC)\n", centiwatt / 100, centiwatt % 100);
}
size = sizeof(value);
seq_printf(m, "\n");
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c
index 1d6e30269d56..4d553be56396 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c
@@ -106,11 +106,8 @@ int hwmgr_early_init(struct pp_hwmgr *hwmgr)
hwmgr->od_enabled = false;
switch (hwmgr->chip_id) {
case CHIP_BONAIRE:
- /* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM
- * R7 260X cards with old MC ucode: MCLK DPM is unstable
- */
- if (adev->pdev->subsystem_vendor == 0x106B ||
- adev->pdev->device == 0x6658) {
+ /* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM */
+ if (adev->pdev->subsystem_vendor == 0x106B) {
dev_info(adev->dev, "disabling MCLK DPM on quirky ASIC");
adev->pm.pp_feature &= ~PP_MCLK_DPM_MASK;
hwmgr->feature_mask &= ~PP_MCLK_DPM_MASK;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index 8faf7de7aaa9..f65bb1dc44ac 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -1365,6 +1365,14 @@ static void smu_feature_cap_init(struct smu_context *smu)
bitmap_zero(fea_cap->cap_map, SMU_FEATURE_CAP_ID__COUNT);
}

+static int smu_set_power_dep(struct smu_context *smu, bool enable)
+{
+ if (!smu->ppt_funcs->set_power_dep)
+ return 0;
+
+ return smu->ppt_funcs->set_power_dep(smu, enable);
+}
+
static int smu_sw_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@ -1426,6 +1434,8 @@ static int smu_sw_init(struct amdgpu_ip_block *ip_block)
if (!smu->ppt_funcs->get_fan_control_mode)
smu->adev->pm.no_fan = true;

+ smu_set_power_dep(smu, true);
+
return 0;
}

@@ -1448,6 +1458,8 @@ static int smu_sw_fini(struct amdgpu_ip_block *ip_block)

smu_fini_microcode(smu);

+ smu_set_power_dep(smu, false);
+
return 0;
}

diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
index d76e0b005308..e3a89e9a9df4 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -749,6 +749,9 @@ struct smu_context {
bool pm_enabled;
bool is_apu;

+ /* Power dependency link from an integrated xHCI controller to the GPU */
+ struct device_link *usb_power_link;
+
uint32_t smc_driver_if_version;
uint32_t smc_fw_if_version;
uint32_t smc_fw_version;
@@ -1648,12 +1651,19 @@ struct pptable_funcs {
int (*ras_send_msg)(struct smu_context *smu,
enum smu_message_type msg, uint32_t param, uint32_t *read_arg);

-
/**
* @get_ras_smu_drv: Get RAS smu driver interface
* Return: ras_smu_drv *
*/
int (*get_ras_smu_drv)(struct smu_context *smu, const struct ras_smu_drv **ras_smu_drv);
+
+ /**
+ * @set_power_dep: Create or destroy a power dependency link
+ * from an integrated xHCI controller to the GPU so that the GPU is
+ * resumed before the USB controller during PM resume. @enable is true
+ * to create the link and false to tear it down.
+ */
+ int (*set_power_dep)(struct smu_context *smu, bool enable);
};

typedef enum {
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 7f8d4bb47d02..acbd7046d8a5 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
@@ -2403,11 +2403,14 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu,
uint32_t pp_limit = smu->adev->pm.ac_power ?
skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
- uint32_t power_limit = 0, od_percent_upper = 0, od_percent_lower = 0;
+ uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
+ uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit);
+ uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit);
+ uint32_t od_percent_upper = 0, od_percent_lower = 0;
int ret;

if (current_power_limit) {
- ret = smu_v13_0_get_current_power_limit(smu, &power_limit);
+ ret = smu_v13_0_get_current_power_limit(smu, current_power_limit);
if (ret)
*current_power_limit = pp_limit;
}
@@ -2430,12 +2433,12 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu,
od_percent_upper, od_percent_lower, pp_limit);

if (max_power_limit) {
- *max_power_limit = pp_limit * (100 + od_percent_upper);
+ *max_power_limit = max_limit * (100 + od_percent_upper);
*max_power_limit /= 100;
}

if (min_power_limit) {
- *min_power_limit = pp_limit * (100 - od_percent_lower);
+ *min_power_limit = min_limit * (100 - od_percent_lower);
*min_power_limit /= 100;
}

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 0f774b0920ce..42c9ceeb4f7d 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
@@ -2385,15 +2385,16 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu,
uint32_t pp_limit = smu->adev->pm.ac_power ?
skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
- uint32_t power_limit = 0, od_percent_upper = 0, od_percent_lower = 0;
+ uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
+ uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit);
+ uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit);
+ uint32_t od_percent_upper = 0, od_percent_lower = 0;
int ret;

if (current_power_limit) {
- ret = smu_v13_0_get_current_power_limit(smu, &power_limit);
+ ret = smu_v13_0_get_current_power_limit(smu, current_power_limit);
if (ret)
- power_limit = pp_limit;
-
- *current_power_limit = power_limit;
+ *current_power_limit = pp_limit;
}

if (default_power_limit)
@@ -2414,12 +2415,12 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu,
od_percent_upper, od_percent_lower, pp_limit);

if (max_power_limit) {
- *max_power_limit = pp_limit * (100 + od_percent_upper);
+ *max_power_limit = max_limit * (100 + od_percent_upper);
*max_power_limit /= 100;
}

if (min_power_limit) {
- *min_power_limit = pp_limit * (100 - od_percent_lower);
+ *min_power_limit = min_limit * (100 - od_percent_lower);
*min_power_limit /= 100;
}

diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
index 75719c47a41e..3d73f2050bbe 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
@@ -1701,6 +1701,50 @@ static int smu_v14_0_0_restore_user_od_settings(struct smu_context *smu)
return 0;
}

+/*
+ * Link any xHCI controller sharing the GPU's PCIe root port as a consumer
+ * of the GPU so the GPU resumes first, avoiding an xHCI resume race.
+ */
+static int smu_v14_0_0_set_power_dep(struct smu_context *smu, bool enable)
+{
+ struct amdgpu_device *adev = smu->adev;
+ struct pci_dev *gpu_pdev = adev->pdev;
+ struct pci_dev *root_port, *usb_pdev = NULL;
+ struct device_link *link;
+
+ if (!enable) {
+ if (smu->usb_power_link) {
+ device_link_del(smu->usb_power_link);
+ smu->usb_power_link = NULL;
+ }
+ return 0;
+ }
+
+ root_port = pcie_find_root_port(gpu_pdev);
+ while ((usb_pdev = pci_get_class(PCI_CLASS_SERIAL_USB_XHCI, usb_pdev))) {
+ struct pci_dev *usb_root;
+
+ usb_root = pcie_find_root_port(usb_pdev);
+ if (usb_root != root_port)
+ continue;
+
+ /* Create device link: USB (consumer) depends on GPU (supplier) */
+ link = device_link_add(&usb_pdev->dev, &gpu_pdev->dev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (link) {
+ smu->usb_power_link = link;
+ drm_info(adev_to_drm(adev), "USB controller %s D0 power state depends on %s\n",
+ pci_name(usb_pdev), pci_name(gpu_pdev));
+ /* Only create one link for the first USB controller found */
+ break;
+ }
+ }
+
+ pci_dev_put(usb_pdev);
+
+ return 0;
+}
+
static const struct pptable_funcs smu_v14_0_0_ppt_funcs = {
.check_fw_status = smu_v14_0_check_fw_status,
.check_fw_version = smu_cmn_check_fw_version,
@@ -1734,6 +1778,7 @@ static const struct pptable_funcs smu_v14_0_0_ppt_funcs = {
.dpm_set_umsch_mm_enable = smu_v14_0_0_set_umsch_mm_enable,
.get_dpm_clock_table = smu_v14_0_common_get_dpm_table,
.set_mall_enable = smu_v14_0_common_set_mall_enable,
+ .set_power_dep = smu_v14_0_0_set_power_dep,
};

static void smu_v14_0_0_init_msg_ctl(struct smu_context *smu)
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
index fdc1456b885c..a6a88e7b2668 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
@@ -1621,19 +1621,23 @@ static int smu_v14_0_2_get_power_limit(struct smu_context *smu,
table_context->power_play_table;
PPTable_t *pptable = table_context->driver_pptable;
CustomSkuTable_t *skutable = &pptable->CustomSkuTable;
- int16_t od_percent_upper = 0, od_percent_lower = 0;
+ uint32_t pp_limit = smu->adev->pm.ac_power ?
+ skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
+ skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
- uint32_t power_limit;
+ uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit);
+ uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit);
+ int16_t od_percent_upper = 0, od_percent_lower = 0;
+ int ret;

- if (smu_v14_0_get_current_power_limit(smu, &power_limit))
- power_limit = smu->adev->pm.ac_power ?
- skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
- skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
+ if (current_power_limit) {
+ ret = smu_v14_0_get_current_power_limit(smu, current_power_limit);
+ if (ret)
+ *current_power_limit = pp_limit;
+ }

- if (current_power_limit)
- *current_power_limit = power_limit;
if (default_power_limit)
- *default_power_limit = power_limit;
+ *default_power_limit = pp_limit;

if (powerplay_table) {
if (smu->od_enabled &&
@@ -1647,15 +1651,15 @@ static int smu_v14_0_2_get_power_limit(struct smu_context *smu,
}

dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n",
- od_percent_upper, od_percent_lower, power_limit);
+ od_percent_upper, od_percent_lower, pp_limit);

if (max_power_limit) {
- *max_power_limit = msg_limit * (100 + od_percent_upper);
+ *max_power_limit = max_limit * (100 + od_percent_upper);
*max_power_limit /= 100;
}

if (min_power_limit) {
- *min_power_limit = power_limit * (100 + od_percent_lower);
+ *min_power_limit = min_limit * (100 + od_percent_lower);
*min_power_limit /= 100;
}

diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
index 8d092c347076..6cd4c38a1341 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
@@ -227,9 +227,14 @@ static int smu_v15_0_0_system_features_control(struct smu_context *smu, bool en)
struct amdgpu_device *adev = smu->adev;
int ret = 0;

- if (!en && !adev->in_s0ix)
+ if (!en && !adev->in_s0ix) {
ret = smu_cmn_send_smc_msg(smu, SMU_MSG_PrepareMp1ForUnload, NULL);

+ /* SMU resets BIF_FB_EN to zero, re-enable MC access on APUs with SMU V15 */
+ if (!ret && adev->nbio.funcs && adev->nbio.funcs->mc_access_enable)
+ adev->nbio.funcs->mc_access_enable(adev, true);
+ }
+
return ret;
}

diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
index 0dd85e26248c..e07a9892df4e 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c
@@ -1230,7 +1230,7 @@ static const struct mipi_dsi_host_ops cdns_dsi_ops = {
.transfer = cdns_dsi_transfer,
};

-static int __maybe_unused cdns_dsi_resume(struct device *dev)
+static int cdns_dsi_resume(struct device *dev)
{
struct cdns_dsi *dsi = dev_get_drvdata(dev);

@@ -1241,7 +1241,7 @@ static int __maybe_unused cdns_dsi_resume(struct device *dev)
return 0;
}

-static int __maybe_unused cdns_dsi_suspend(struct device *dev)
+static int cdns_dsi_suspend(struct device *dev)
{
struct cdns_dsi *dsi = dev_get_drvdata(dev);

@@ -1251,8 +1251,9 @@ static int __maybe_unused cdns_dsi_suspend(struct device *dev)
return 0;
}

-static UNIVERSAL_DEV_PM_OPS(cdns_dsi_pm_ops, cdns_dsi_suspend, cdns_dsi_resume,
- NULL);
+static const struct dev_pm_ops cdns_dsi_pm_ops = {
+ RUNTIME_PM_OPS(cdns_dsi_suspend, cdns_dsi_resume, NULL)
+};

static int cdns_dsi_drm_probe(struct platform_device *pdev)
{
@@ -1399,7 +1400,7 @@ static struct platform_driver cdns_dsi_platform_driver = {
.driver = {
.name = "cdns-dsi",
.of_match_table = cdns_dsi_of_match,
- .pm = &cdns_dsi_pm_ops,
+ .pm = pm_ptr(&cdns_dsi_pm_ops),
},
};
module_platform_driver(cdns_dsi_platform_driver);
diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c
index 8757972e8e24..41151c533d2f 100644
--- a/drivers/gpu/drm/display/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c
@@ -789,6 +789,12 @@ static bool drm_dp_sideband_append_payload(struct drm_dp_sideband_msg_rx *msg,
{
u8 crc4;

+ /* curchunk_len must be >= 1 (min 1 CRC byte) and fit in chunk[] */
+ if (!msg->curchunk_len ||
+ msg->curchunk_len > ARRAY_SIZE(msg->chunk) ||
+ msg->curchunk_idx + replybuflen > ARRAY_SIZE(msg->chunk))
+ return false;
+
memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen);
msg->curchunk_idx += replybuflen;

@@ -799,6 +805,9 @@ static bool drm_dp_sideband_append_payload(struct drm_dp_sideband_msg_rx *msg,
print_hex_dump(KERN_DEBUG, "wrong crc",
DUMP_PREFIX_NONE, 16, 1,
msg->chunk, msg->curchunk_len, false);
+ /* Guard against accumulated msg[] overflow */
+ if (msg->curlen + msg->curchunk_len - 1 > ARRAY_SIZE(msg->msg))
+ return false;
/* copy chunk into bigger msg */
memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1);
msg->curlen += msg->curchunk_len - 1;
@@ -871,7 +880,7 @@ static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx
goto fail_len;
repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx];
idx++;
- if (idx > raw->curlen)
+ if (idx + repmsg->u.remote_dpcd_read_ack.num_bytes > raw->curlen)
goto fail_len;

memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
@@ -907,7 +916,9 @@ static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg
goto fail_len;
repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx];
idx++;
- /* TODO check */
+ if (idx + repmsg->u.remote_i2c_read_ack.num_bytes > raw->curlen)
+ goto fail_len;
+
memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes);
return true;
fail_len:
@@ -923,16 +934,13 @@ static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband
repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf;
repmsg->u.path_resources.fec_capable = raw->msg[idx] & 0x1;
idx++;
- if (idx > raw->curlen)
+ if (idx + 2 > raw->curlen)
goto fail_len;
repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
idx += 2;
- if (idx > raw->curlen)
+ if (idx + 2 > raw->curlen)
goto fail_len;
repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
- idx += 2;
- if (idx > raw->curlen)
- goto fail_len;
return true;
fail_len:
DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen);
@@ -950,12 +958,9 @@ static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_ms
goto fail_len;
repmsg->u.allocate_payload.vcpi = raw->msg[idx];
idx++;
- if (idx > raw->curlen)
+ if (idx + 2 > raw->curlen)
goto fail_len;
repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
- idx += 2;
- if (idx > raw->curlen)
- goto fail_len;
return true;
fail_len:
DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen);
@@ -969,12 +974,9 @@ static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_r

repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
idx++;
- if (idx > raw->curlen)
+ if (idx + 2 > raw->curlen)
goto fail_len;
repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
- idx += 2;
- if (idx > raw->curlen)
- goto fail_len;
return true;
fail_len:
DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen);
@@ -3738,8 +3740,10 @@ void drm_dp_mst_topology_queue_probe(struct drm_dp_mst_topology_mgr *mgr)
{
mutex_lock(&mgr->lock);

- if (drm_WARN_ON(mgr->dev, !mgr->mst_state || !mgr->mst_primary))
+ if (!mgr->mst_state || !mgr->mst_primary) {
+ drm_dbg_kms(mgr->dev, "queue_probe skipped: topology torn down\n");
goto out_unlock;
+ }

drm_dp_mst_topology_mgr_invalidate_mstb(mgr->mst_primary);
drm_dp_mst_queue_probe_work(mgr);
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 47dc53c4a738..29634757c06d 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -3576,7 +3576,7 @@ EXPORT_SYMBOL(drm_mode_put_tile_group);
/**
* drm_mode_get_tile_group - get a reference to an existing tile group
* @dev: DRM device
- * @topology: 8-bytes unique per monitor.
+ * @topology_id: 9-byte unique ID per monitor.
*
* Use the unique bytes to get a reference to an existing tile group.
*
@@ -3584,14 +3584,14 @@ EXPORT_SYMBOL(drm_mode_put_tile_group);
* tile group or NULL if not found.
*/
struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev,
- const char topology[8])
+ const char topology_id[9])
{
struct drm_tile_group *tg;
int id;

mutex_lock(&dev->mode_config.idr_mutex);
idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) {
- if (!memcmp(tg->group_data, topology, 8)) {
+ if (!memcmp(tg->group_data, topology_id, sizeof(tg->group_data))) {
if (!kref_get_unless_zero(&tg->refcount))
tg = NULL;
mutex_unlock(&dev->mode_config.idr_mutex);
@@ -3606,7 +3606,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group);
/**
* drm_mode_create_tile_group - create a tile group from a displayid description
* @dev: DRM device
- * @topology: 8-bytes unique per monitor.
+ * @topology_id: 9-byte unique ID per monitor.
*
* Create a tile group for the unique monitor, and get a unique
* identifier for the tile group.
@@ -3615,7 +3615,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group);
* new tile group or NULL.
*/
struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev,
- const char topology[8])
+ const char topology_id[9])
{
struct drm_tile_group *tg;
int ret;
@@ -3625,7 +3625,7 @@ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev,
return NULL;

kref_init(&tg->refcount);
- memcpy(tg->group_data, topology, 8);
+ memcpy(tg->group_data, topology_id, sizeof(tg->group_data));
tg->dev = dev;

mutex_lock(&dev->mode_config.idr_mutex);
diff --git a/drivers/gpu/drm/drm_displayid_internal.h b/drivers/gpu/drm/drm_displayid_internal.h
index 5b1b32f73516..4590d6a3d821 100644
--- a/drivers/gpu/drm/drm_displayid_internal.h
+++ b/drivers/gpu/drm/drm_displayid_internal.h
@@ -109,7 +109,7 @@ struct displayid_tiled_block {
u8 topo[3];
u8 tile_size[4];
u8 tile_pixel_bezel[5];
- u8 topology_id[8];
+ u8 topology_id[9];
} __packed;

struct displayid_detailed_timings_1 {
diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c
index 7993e85c0566..a2bba46b72c1 100644
--- a/drivers/gpu/drm/drm_gpusvm.c
+++ b/drivers/gpu/drm/drm_gpusvm.c
@@ -781,7 +781,7 @@ enum drm_gpusvm_scan_result drm_gpusvm_scan_mm(struct drm_gpusvm_range *range,
const struct dev_pagemap *other = NULL;
int err, i;

- pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL);
+ pfns = kvcalloc(npages, sizeof(*pfns), GFP_KERNEL);
if (!pfns)
return DRM_GPUSVM_SCAN_UNPOPULATED;

@@ -1519,6 +1519,16 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
err = -EAGAIN;
goto err_unmap;
}
+
+ /*
+ * Set the dpagemap as soon as the first
+ * device page is mapped so the err_unmap path
+ * can device_unmap() the device mappings that
+ * have already been created.
+ */
+ drm_pagemap_get(dpagemap);
+ drm_pagemap_put(svm_pages->dpagemap);
+ svm_pages->dpagemap = dpagemap;
}
svm_pages->dma_addr[j] =
dpagemap->ops->device_map(dpagemap,
@@ -1562,12 +1572,8 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
flags.has_dma_mapping = true;
}

- if (pagemap) {
+ if (pagemap)
flags.has_devmem_pages = true;
- drm_pagemap_get(dpagemap);
- drm_pagemap_put(svm_pages->dpagemap);
- svm_pages->dpagemap = dpagemap;
- }

/* WRITE_ONCE pairs with READ_ONCE for opportunistic checks */
WRITE_ONCE(svm_pages->flags.__flags, flags.__flags);
@@ -1698,8 +1704,10 @@ int drm_gpusvm_range_evict(struct drm_gpusvm *gpusvm,
return -EFAULT;

pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL);
- if (!pfns)
+ if (!pfns) {
+ mmput(mm);
return -ENOMEM;
+ }

hmm_range.hmm_pfns = pfns;
while (!time_after(jiffies, timeout)) {
diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c
index 5002049e0198..43ed97d5cc03 100644
--- a/drivers/gpu/drm/drm_pagemap.c
+++ b/drivers/gpu/drm/drm_pagemap.c
@@ -12,6 +12,12 @@
#include <drm/drm_pagemap_util.h>
#include <drm/drm_print.h>

+#if IS_ENABLED(CONFIG_ARCH_ENABLE_THP_MIGRATION)
+#define DRM_PAGEMAP_PMD_ORDER HPAGE_PMD_ORDER
+#else
+#define DRM_PAGEMAP_PMD_ORDER (-1)
+#endif
+
/**
* DOC: Overview
*
@@ -480,7 +486,7 @@ static int drm_pagemap_cpages(unsigned long *migrate_pfn, unsigned long npages)
order = folio_order(folio);
cpages += NR_PAGES(order);
} else if (migrate_pfn[i] & MIGRATE_PFN_COMPOUND) {
- order = HPAGE_PMD_ORDER;
+ order = DRM_PAGEMAP_PMD_ORDER;
cpages += NR_PAGES(order);
}

@@ -628,8 +634,10 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
}

err = ops->populate_devmem_pfn(devmem_allocation, npages, migrate.dst);
- if (err)
- goto err_aborted_migration;
+ if (err) {
+ npages = 0;
+ goto err_finalize;
+ }

own_pages = 0;

@@ -668,10 +676,11 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,

if (migrate.src[i] & MIGRATE_PFN_COMPOUND) {
drm_WARN_ONCE(dpagemap->drm, src_page &&
- folio_order(page_folio(src_page)) != HPAGE_PMD_ORDER,
+ folio_order(page_folio(src_page)) !=
+ DRM_PAGEMAP_PMD_ORDER,
"Unexpected folio order\n");

- order = HPAGE_PMD_ORDER;
+ order = DRM_PAGEMAP_PMD_ORDER;
migrate.dst[i] |= MIGRATE_PFN_COMPOUND;

for (j = 1; j < NR_PAGES(order) && i + j < npages; j++)
@@ -710,8 +719,11 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
msecs_to_jiffies(mdetails->timeslice_ms);

err_finalize:
- if (err)
+ if (err) {
drm_pagemap_migration_unlock_put_pages(npages, migrate.dst);
+ for (i = npages; i < npages_in_range(start, end); ++i)
+ migrate.dst[i] = 0;
+ }
err_aborted_migration:
migrate_vma_pages(&migrate);

diff --git a/drivers/gpu/drm/drm_panel_backlight_quirks.c b/drivers/gpu/drm/drm_panel_backlight_quirks.c
index 537dc6dd0534..2d0238382ebd 100644
--- a/drivers/gpu/drm/drm_panel_backlight_quirks.c
+++ b/drivers/gpu/drm/drm_panel_backlight_quirks.c
@@ -21,6 +21,15 @@ struct drm_get_panel_backlight_quirk {
};

static const struct drm_get_panel_backlight_quirk drm_panel_min_backlight_quirks[] = {
+ /* Lenovo Legion 5 15ARH05, AUX backlight non-functional, force PWM */
+ {
+ .dmi_match.field = DMI_SYS_VENDOR,
+ .dmi_match.value = "LENOVO",
+ .dmi_match_other.field = DMI_PRODUCT_VERSION,
+ .dmi_match_other.value = "Lenovo Legion 5 15ARH05",
+ .ident.panel_id = drm_edid_encode_panel_id('B', 'O', 'E', 0x08df),
+ .quirk = { .force_pwm = true, },
+ },
/* 13 inch matte panel */
{
.dmi_match.field = DMI_BOARD_VENDOR,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index 637927818dfe..d283ded266d5 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -61,17 +61,13 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
struct fb_info *fbi = helper->info;
struct drm_framebuffer *fb = helper->fb;
unsigned int size = fb->width * fb->height * fb->format->cpp[0];
- unsigned long offset;

fbi->fbops = &exynos_drm_fb_ops;

drm_fb_helper_fill_info(fbi, helper, sizes);

- offset = fbi->var.xoffset * fb->format->cpp[0];
- offset += fbi->var.yoffset * fb->pitches[0];
-
fbi->flags |= FBINFO_VIRTFB;
- fbi->screen_buffer = exynos_gem->kvaddr + offset;
+ fbi->screen_buffer = exynos_gem->kvaddr;
fbi->screen_size = size;
fbi->fix.smem_len = size;

diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
index b6fe87c29aa7..ded2ee497bbf 100644
--- a/drivers/gpu/drm/i915/display/intel_bios.c
+++ b/drivers/gpu/drm/i915/display/intel_bios.c
@@ -623,6 +623,21 @@ get_lfp_data_tail(const struct bdb_lfp_data *data,
return NULL;
}

+static bool is_panel_type_valid(int panel_type)
+{
+ return panel_type >= 0 && panel_type < 16;
+}
+
+static bool is_panel_type_pnp(int panel_type)
+{
+ return panel_type == 0xff;
+}
+
+static bool is_panel_type_valid_or_pnp(int panel_type)
+{
+ return is_panel_type_valid(panel_type) || is_panel_type_pnp(panel_type);
+}
+
static int opregion_get_panel_type(struct intel_display *display,
const struct intel_bios_encoder_data *devdata,
const struct drm_edid *drm_edid, bool use_fallback)
@@ -640,15 +655,21 @@ static int vbt_get_panel_type(struct intel_display *display,
if (!lfp_options)
return -1;

- if (lfp_options->panel_type > 0xf &&
- lfp_options->panel_type != 0xff) {
+ if (!is_panel_type_valid_or_pnp(lfp_options->panel_type)) {
drm_dbg_kms(display->drm, "Invalid VBT panel type 0x%x\n",
lfp_options->panel_type);
return -1;
}

- if (devdata && devdata->child.handle == DEVICE_HANDLE_LFP2)
+ if (devdata && devdata->child.handle == DEVICE_HANDLE_LFP2) {
+ if (!is_panel_type_valid_or_pnp(lfp_options->panel_type2)) {
+ drm_dbg_kms(display->drm, "Invalid VBT panel type 2 0x%x\n",
+ lfp_options->panel_type2);
+ return -1;
+ }
+
return lfp_options->panel_type2;
+ }

drm_WARN_ON(display->drm,
devdata && devdata->child.handle != DEVICE_HANDLE_LFP1);
@@ -762,13 +783,12 @@ static int get_panel_type(struct intel_display *display,
panel_types[i].name, panel_types[i].panel_type);
}

- if (panel_types[PANEL_TYPE_OPREGION].panel_type >= 0)
+ if (is_panel_type_valid(panel_types[PANEL_TYPE_OPREGION].panel_type))
i = PANEL_TYPE_OPREGION;
- else if (panel_types[PANEL_TYPE_VBT].panel_type == 0xff &&
- panel_types[PANEL_TYPE_PNPID].panel_type >= 0)
+ else if (is_panel_type_pnp(panel_types[PANEL_TYPE_VBT].panel_type) &&
+ is_panel_type_valid(panel_types[PANEL_TYPE_PNPID].panel_type))
i = PANEL_TYPE_PNPID;
- else if (panel_types[PANEL_TYPE_VBT].panel_type != 0xff &&
- panel_types[PANEL_TYPE_VBT].panel_type >= 0)
+ else if (is_panel_type_valid(panel_types[PANEL_TYPE_VBT].panel_type))
i = PANEL_TYPE_VBT;
else
i = PANEL_TYPE_FALLBACK;
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c
index a47736613f6e..2417438e8d5d 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.c
@@ -1256,9 +1256,22 @@ static void skl_sanitize_cdclk(struct intel_display *display)
cdctl = intel_de_read(display, CDCLK_CTL);
expected = (cdctl & CDCLK_FREQ_SEL_MASK) |
skl_cdclk_decimal(display->cdclk.hw.cdclk);
- if (cdctl == expected)
- /* All well; nothing to sanitize */
- return;
+
+ if (cdctl != expected) {
+ cdctl &= ~CDCLK_FREQ_DECIMAL_MASK;
+ cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK;
+
+ if (cdctl != expected)
+ goto sanitize;
+
+ drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n",
+ intel_de_read(display, CDCLK_CTL), expected);
+
+ intel_de_write(display, CDCLK_CTL, expected);
+ }
+
+ /* All well; nothing to sanitize */
+ return;

sanitize:
drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n");
@@ -2354,11 +2367,25 @@ static void bxt_sanitize_cdclk(struct intel_display *display)
* (PIPE_NONE).
*/
cdctl &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE);
- expected &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE);
+ cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE);

- if (cdctl == expected)
- /* All well; nothing to sanitize */
- return;
+ if (cdctl != expected) {
+ if (DISPLAY_VER(display) < 20) {
+ cdctl &= ~CDCLK_FREQ_DECIMAL_MASK;
+ cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK;
+ }
+
+ if (cdctl != expected)
+ goto sanitize;
+
+ drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n",
+ intel_de_read(display, CDCLK_CTL), expected);
+
+ intel_de_write(display, CDCLK_CTL, expected);
+ }
+
+ /* All well; nothing to sanitize */
+ return;

sanitize:
drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n");
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
index ebefa889bc8c..0987eee38bd1 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -2652,9 +2652,6 @@ static void mtl_ddi_pre_enable_dp(struct intel_atomic_state *state,
/* 3. Select Thunderbolt */
mtl_port_buf_ctl_io_selection(encoder);

- /* 4. Enable Panel Power if PPS is required */
- intel_pps_on(intel_dp);
-
/* 5. Enable the port PLL */
intel_ddi_enable_clock(encoder, crtc_state);

@@ -3710,6 +3707,14 @@ intel_ddi_pre_pll_enable(struct intel_atomic_state *state,
else if (display->platform.geminilake || display->platform.broxton)
bxt_dpio_phy_set_lane_optim_mask(encoder,
crtc_state->lane_lat_optim_mask);
+
+ /*
+ * There is no direct connection between the PLL and PPS, however
+ * enabling PPS before PLL is required to avoid PLL/DDI BUF timeouts
+ * during system resume. Do that matching the Bspec order as well.
+ */
+ if (DISPLAY_VER(display) >= 14)
+ intel_pps_on(&dig_port->dp);
}

static void adlp_tbt_to_dp_alt_switch_wa(struct intel_encoder *encoder)
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 5c3e816b0135..f34da1a055dd 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -5589,8 +5589,9 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp)
struct intel_display *display = to_intel_display(intel_dp);
bool force_retrain = intel_dp->link.force_retrain;
bool reprobe_needed = false;
+ int tries = 33;

- for (;;) {
+ while (--tries) {
u8 esi[4] = {};
u8 ack[4] = {};
bool new_irqs;
@@ -5633,6 +5634,11 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp)
break;
}

+ if (!tries) {
+ drm_dbg_kms(display->drm, "DPRX ESI not clearing, device may be stuck\n");
+ reprobe_needed = true;
+ }
+
return !reprobe_needed;
}

diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
index 7a6c07f6aaeb..266e042e0023 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
@@ -615,12 +615,7 @@ check_if_vesa_backlight_possible(struct intel_dp *intel_dp)
int ret;
u8 bit_min, bit_max;

- /*
- * Since we only support Fully AUX Based VESA Backlight interface make sure
- * backlight enable is possible via AUX along with backlight adjustment
- */
- if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP &&
- intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP))
+ if (!(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP))
return false;

ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &bit_min);
diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c
index 892eab4b6f92..878df0e2d678 100644
--- a/drivers/gpu/drm/i915/display/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/display/intel_hdcp.c
@@ -132,6 +132,9 @@ intel_hdcp_required_content_stream(struct intel_atomic_state *state,
if (conn_dig_port != dig_port)
continue;

+ if (drm_WARN_ON(display->drm, data->k >= INTEL_NUM_PIPES(display)))
+ return -EINVAL;
+
data->streams[data->k].stream_id =
intel_conn_to_vcpi(state, connector);
data->k++;
@@ -142,7 +145,7 @@ intel_hdcp_required_content_stream(struct intel_atomic_state *state,
}
drm_connector_list_iter_end(&conn_iter);

- if (drm_WARN_ON(display->drm, data->k > INTEL_NUM_PIPES(display) || data->k == 0))
+ if (drm_WARN_ON(display->drm, !data->k))
return -EINVAL;

/*
@@ -1785,9 +1788,10 @@ int hdcp2_authenticate_repeater_topology(struct intel_connector *connector)
return -EINVAL;
}

- if (seq_num_v < hdcp->seq_num_v) {
- /* Roll over of the seq_num_v from repeater. Reauthenticate. */
- drm_dbg_kms(display->drm, "Seq_num_v roll over.\n");
+ if (hdcp->hdcp2_encrypted && seq_num_v <= hdcp->seq_num_v) {
+ /* Reauthenticate on Seq_num_v repeat or rollover */
+ drm_dbg_kms(display->drm, "Seq_num_v %s\n",
+ seq_num_v == hdcp->seq_num_v ? "repeat" : "rollover");
return -EINVAL;
}

diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c
index 8a957804cb97..4908cc34b52d 100644
--- a/drivers/gpu/drm/i915/display/intel_vrr.c
+++ b/drivers/gpu/drm/i915/display/intel_vrr.c
@@ -64,6 +64,10 @@ bool intel_vrr_is_capable(struct intel_connector *connector)
return false;
}

+ if (!info->monitor_range.min_vfreq || !info->monitor_range.max_vfreq ||
+ info->monitor_range.min_vfreq > info->monitor_range.max_vfreq)
+ return false;
+
return info->monitor_range.max_vfreq - info->monitor_range.min_vfreq > 10;
}

diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c
index e0ac4e2ce4dc..29b7661f8814 100644
--- a/drivers/gpu/drm/i915/display/skl_watermark.c
+++ b/drivers/gpu/drm/i915/display/skl_watermark.c
@@ -3908,7 +3908,7 @@ void skl_wm_plane_disable_noatomic(struct intel_crtc *crtc,
return;

skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb[plane->id], 0, 0);
- skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb[plane->id], 0, 0);
+ skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb_y[plane->id], 0, 0);

crtc_state->wm.skl.plane_min_ddb[plane->id] = 0;
crtc_state->wm.skl.plane_interim_ddb[plane->id] = 0;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
index 6ac0f23570f3..c58ffa5a8fa6 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
@@ -613,6 +613,7 @@ set_proto_ctx_engines_parallel_submit(struct i915_user_extension __user *base,
return -EINVAL;
}

+ slot = array_index_nospec(slot, set->num_engines);
if (set->engines[slot].type != I915_GEM_ENGINE_TYPE_INVALID) {
drm_dbg(&i915->drm,
"Invalid placement[%d], already occupied\n", slot);
@@ -768,8 +769,8 @@ static int set_proto_ctx_engines(struct drm_i915_file_private *fpriv,
struct intel_engine_cs *engine;

if (copy_from_user(&ci, &user->engines[n], sizeof(ci))) {
- kfree(set.engines);
- return -EFAULT;
+ err = -EFAULT;
+ goto err;
}

memset(&set.engines[n], 0, sizeof(set.engines[n]));
@@ -785,8 +786,8 @@ static int set_proto_ctx_engines(struct drm_i915_file_private *fpriv,
drm_dbg(&i915->drm,
"Invalid engine[%d]: { class:%d, instance:%d }\n",
n, ci.engine_class, ci.engine_instance);
- kfree(set.engines);
- return -ENOENT;
+ err = -ENOENT;
+ goto err;
}

set.engines[n].type = I915_GEM_ENGINE_TYPE_PHYSICAL;
@@ -799,15 +800,21 @@ static int set_proto_ctx_engines(struct drm_i915_file_private *fpriv,
set_proto_ctx_engines_extensions,
ARRAY_SIZE(set_proto_ctx_engines_extensions),
&set);
- if (err) {
- kfree(set.engines);
- return err;
- }
+ if (err)
+ goto err_extensions;

pc->num_user_engines = set.num_engines;
pc->user_engines = set.engines;

return 0;
+
+err_extensions:
+ for (n = 0; n < set.num_engines; n++)
+ kfree(set.engines[n].siblings);
+err:
+ kfree(set.engines);
+
+ return err;
}

static int set_proto_ctx_sseu(struct drm_i915_file_private *fpriv,
@@ -849,7 +856,7 @@ static int set_proto_ctx_sseu(struct drm_i915_file_private *fpriv,
pe = &pc->user_engines[idx];

/* Only render engine supports RPCS configuration. */
- if (pe->engine->class != RENDER_CLASS)
+ if (!pe->engine || pe->engine->class != RENDER_CLASS)
return -EINVAL;

sseu = &pe->sseu;
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_user.c b/drivers/gpu/drm/i915/gt/intel_engine_user.c
index be4bbff1a57c..d5190e11b270 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_user.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_user.c
@@ -259,7 +259,7 @@ void intel_engines_driver_register(struct drm_i915_private *i915)
p = &prev->rb_right;
}

- if (IS_ENABLED(CONFIG_DRM_I915_SELFTESTS) &&
+ if (IS_ENABLED(CONFIG_DRM_I915_SELFTEST) &&
IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)) {
struct intel_engine_cs *engine;
unsigned int isolation;
diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
index 1359fc9cb88e..e693b0c9d2a3 100644
--- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
+++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
@@ -3932,11 +3932,11 @@ execlists_create_virtual(struct intel_engine_cs **siblings, unsigned int count,
struct drm_i915_private *i915 = siblings[0]->i915;
struct virtual_engine *ve;
unsigned int n;
- int err;
+ int err = -ENOMEM;

ve = kzalloc_flex(*ve, siblings, count);
if (!ve)
- return ERR_PTR(-ENOMEM);
+ goto err;

ve->base.i915 = i915;
ve->base.gt = siblings[0]->gt;
@@ -3968,10 +3968,8 @@ execlists_create_virtual(struct intel_engine_cs **siblings, unsigned int count,
intel_engine_init_execlists(&ve->base);

ve->base.sched_engine = i915_sched_engine_create(ENGINE_VIRTUAL);
- if (!ve->base.sched_engine) {
- err = -ENOMEM;
- goto err_put;
- }
+ if (!ve->base.sched_engine)
+ goto err_noput;
ve->base.sched_engine->private_data = &ve->base;

ve->base.cops = &virtual_context_ops;
@@ -3987,10 +3985,8 @@ execlists_create_virtual(struct intel_engine_cs **siblings, unsigned int count,
intel_context_init(&ve->context, &ve->base);

ve->base.breadcrumbs = intel_breadcrumbs_create(NULL);
- if (!ve->base.breadcrumbs) {
- err = -ENOMEM;
+ if (!ve->base.breadcrumbs)
goto err_put;
- }

for (n = 0; n < count; n++) {
struct intel_engine_cs *sibling = siblings[n];
@@ -4065,8 +4061,13 @@ execlists_create_virtual(struct intel_engine_cs **siblings, unsigned int count,
virtual_engine_initial_hint(ve);
return &ve->context;

+err_noput:
+ kfree(ve);
+ goto err;
+
err_put:
intel_context_put(&ve->context);
+err:
return ERR_PTR(err);
}

diff --git a/drivers/gpu/drm/i915/gt/selftest_gt_pm.c b/drivers/gpu/drm/i915/gt/selftest_gt_pm.c
index 33351deeea4f..07eaf71955c4 100644
--- a/drivers/gpu/drm/i915/gt/selftest_gt_pm.c
+++ b/drivers/gpu/drm/i915/gt/selftest_gt_pm.c
@@ -16,9 +16,9 @@ static int cmp_u64(const void *A, const void *B)
{
const u64 *a = A, *b = B;

- if (a < b)
+ if (*a < *b)
return -1;
- else if (a > b)
+ else if (*a > *b)
return 1;
else
return 0;
@@ -28,9 +28,9 @@ static int cmp_u32(const void *A, const void *B)
{
const u32 *a = A, *b = B;

- if (a < b)
+ if (*a < *b)
return -1;
- else if (a > b)
+ else if (*a > *b)
return 1;
else
return 0;
diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
index 5cb7a72774a0..aa77def0bc0d 100644
--- a/drivers/gpu/drm/i915/i915_active.c
+++ b/drivers/gpu/drm/i915/i915_active.c
@@ -318,7 +318,7 @@ active_instance(struct i915_active *ref, u64 idx)
*/
node = kmem_cache_alloc(slab_cache, GFP_ATOMIC);
if (!node)
- goto out;
+ goto err;

__i915_active_fence_init(&node->base, NULL, node_retire);
node->ref = ref;
@@ -332,6 +332,11 @@ active_instance(struct i915_active *ref, u64 idx)
spin_unlock_irq(&ref->tree_lock);

return &node->base;
+
+err:
+ spin_unlock_irq(&ref->tree_lock);
+
+ return NULL;
}

void __i915_active_init(struct i915_active *ref,
diff --git a/drivers/gpu/drm/imagination/pvr_context.c b/drivers/gpu/drm/imagination/pvr_context.c
index 8de70c30b9de..5131fcfccffc 100644
--- a/drivers/gpu/drm/imagination/pvr_context.c
+++ b/drivers/gpu/drm/imagination/pvr_context.c
@@ -161,22 +161,24 @@ ctx_fw_data_init(void *cpu_ptr, void *priv)
/**
* pvr_context_destroy_queues() - Destroy all queues attached to a context.
* @ctx: Context to destroy queues on.
+ * @cleanup_queue_entity: Whether to cleanup the queue entity e.g. context
+ * creation failure path.
*
* Should be called when the last reference to a context object is dropped.
* It releases all resources attached to the queues bound to this context.
*/
-static void pvr_context_destroy_queues(struct pvr_context *ctx)
+static void pvr_context_destroy_queues(struct pvr_context *ctx, bool cleanup_queue_entity)
{
switch (ctx->type) {
case DRM_PVR_CTX_TYPE_RENDER:
- pvr_queue_destroy(ctx->queues.fragment);
- pvr_queue_destroy(ctx->queues.geometry);
+ pvr_queue_destroy(ctx->queues.fragment, cleanup_queue_entity);
+ pvr_queue_destroy(ctx->queues.geometry, cleanup_queue_entity);
break;
case DRM_PVR_CTX_TYPE_COMPUTE:
- pvr_queue_destroy(ctx->queues.compute);
+ pvr_queue_destroy(ctx->queues.compute, cleanup_queue_entity);
break;
case DRM_PVR_CTX_TYPE_TRANSFER_FRAG:
- pvr_queue_destroy(ctx->queues.transfer);
+ pvr_queue_destroy(ctx->queues.transfer, cleanup_queue_entity);
break;
}
}
@@ -240,7 +242,7 @@ static int pvr_context_create_queues(struct pvr_context *ctx,
return -EINVAL;

err_destroy_queues:
- pvr_context_destroy_queues(ctx);
+ pvr_context_destroy_queues(ctx, true);
return err;
}

@@ -307,8 +309,8 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co
goto err_free_ctx;

ctx->vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle);
- if (IS_ERR(ctx->vm_ctx)) {
- err = PTR_ERR(ctx->vm_ctx);
+ if (!ctx->vm_ctx) {
+ err = -EINVAL;
goto err_free_ctx;
}

@@ -356,7 +358,7 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co
pvr_fw_object_destroy(ctx->fw_obj);

err_destroy_queues:
- pvr_context_destroy_queues(ctx);
+ pvr_context_destroy_queues(ctx, true);

err_free_ctx_data:
kfree(ctx->data);
@@ -382,7 +384,7 @@ pvr_context_release(struct kref *ref_count)
spin_unlock(&pvr_dev->ctx_list_lock);

xa_erase(&pvr_dev->ctx_ids, ctx->ctx_id);
- pvr_context_destroy_queues(ctx);
+ pvr_context_destroy_queues(ctx, false);
pvr_fw_object_destroy(ctx->fw_obj);
kfree(ctx->data);
pvr_vm_context_put(ctx->vm_ctx);
diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c
index cfb67f77bde6..1ef7e4fbc74f 100644
--- a/drivers/gpu/drm/imagination/pvr_drv.c
+++ b/drivers/gpu/drm/imagination/pvr_drv.c
@@ -1254,14 +1254,13 @@ pvr_set_uobj_array(const struct drm_pvr_obj_array *out, u32 min_stride, u32 obj_
if (copy_to_user(out_ptr, in_ptr, cpy_elem_size))
return -EFAULT;

- out_ptr += obj_size;
- in_ptr += out->stride;
- }
+ if (out->stride > obj_size &&
+ clear_user(out_ptr + cpy_elem_size, out->stride - obj_size)) {
+ return -EFAULT;
+ }

- if (out->stride > obj_size &&
- clear_user(u64_to_user_ptr(out->array + obj_size),
- out->stride - obj_size)) {
- return -EFAULT;
+ out_ptr += out->stride;
+ in_ptr += obj_size;
}
}

diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c
index dd88949f6194..b5b82ae12892 100644
--- a/drivers/gpu/drm/imagination/pvr_queue.c
+++ b/drivers/gpu/drm/imagination/pvr_queue.c
@@ -179,7 +179,7 @@ static const struct dma_fence_ops pvr_queue_job_fence_ops = {

/**
* to_pvr_queue_job_fence() - Return a pvr_queue_fence object if the fence is
- * backed by a UFO.
+ * already backed by a UFO.
* @f: The dma_fence to turn into a pvr_queue_fence.
*
* Return:
@@ -356,6 +356,15 @@ static u32 job_cmds_size(struct pvr_job *job, u32 ufo_wait_count)
pvr_cccb_get_size_of_cmd_with_hdr(job->cmd_len);
}

+static bool
+is_paired_job_fence(struct dma_fence *fence, struct pvr_job *job)
+{
+ /* This assumes "fence" is one of "job"'s drm_sched_job::dependencies */
+ return job->type == DRM_PVR_JOB_TYPE_FRAGMENT &&
+ job->paired_job &&
+ &job->paired_job->base.s_fence->scheduled == fence;
+}
+
/**
* job_count_remaining_native_deps() - Count the number of non-signaled native dependencies.
* @job: Job to operate on.
@@ -371,6 +380,17 @@ static unsigned long job_count_remaining_native_deps(struct pvr_job *job)
xa_for_each(&job->base.dependencies, index, fence) {
struct pvr_queue_fence *jfence;

+ if (is_paired_job_fence(fence, job)) {
+ /*
+ * A fence between paired jobs won't resolve to a pvr_queue_fence (i.e.
+ * be backed by a UFO) until the jobs have been submitted, together.
+ * The submitting code will insert a partial render fence command for this.
+ */
+ WARN_ON(dma_fence_is_signaled(fence));
+ remaining_count++;
+ continue;
+ }
+
jfence = to_pvr_queue_job_fence(fence);
if (!jfence)
continue;
@@ -468,10 +488,11 @@ pvr_queue_get_job_kccb_fence(struct pvr_queue *queue, struct pvr_job *job)
}

static struct dma_fence *
-pvr_queue_get_paired_frag_job_dep(struct pvr_queue *queue, struct pvr_job *job)
+pvr_queue_get_paired_frag_job_dep(struct pvr_job *job)
{
struct pvr_job *frag_job = job->type == DRM_PVR_JOB_TYPE_GEOMETRY ?
job->paired_job : NULL;
+ struct pvr_queue *frag_queue = frag_job ? frag_job->ctx->queues.fragment : NULL;
struct dma_fence *f;
unsigned long index;

@@ -490,7 +511,10 @@ pvr_queue_get_paired_frag_job_dep(struct pvr_queue *queue, struct pvr_job *job)
return dma_fence_get(f);
}

- return frag_job->base.sched->ops->prepare_job(&frag_job->base, &queue->entity);
+ /* Initialize the paired fragment job's done_fence, so we can signal it. */
+ pvr_queue_job_fence_init(frag_job->done_fence, frag_queue);
+
+ return pvr_queue_get_job_cccb_fence(frag_queue, frag_job);
}

/**
@@ -509,11 +533,6 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job,
struct pvr_queue *queue = container_of(s_entity, struct pvr_queue, entity);
struct dma_fence *internal_dep = NULL;

- /*
- * Initialize the done_fence, so we can signal it. This must be done
- * here because otherwise by the time of run_job() the job will end up
- * in the pending list without a valid fence.
- */
if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) {
/*
* This will be called on a paired fragment job after being
@@ -523,18 +542,15 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job,
*/
if (job->paired_job->has_pm_ref)
return NULL;
-
- /*
- * In this case we need to use the job's own ctx to initialise
- * the done_fence. The other steps are done in the ctx of the
- * paired geometry job.
- */
- pvr_queue_job_fence_init(job->done_fence,
- job->ctx->queues.fragment);
- } else {
- pvr_queue_job_fence_init(job->done_fence, queue);
}

+ /*
+ * Initialize the done_fence, so we can signal it. This must be done
+ * here because otherwise by the time of run_job() the job will end up
+ * in the pending list without a valid fence.
+ */
+ pvr_queue_job_fence_init(job->done_fence, queue);
+
/* CCCB fence is used to make sure we have enough space in the CCCB to
* submit our commands.
*/
@@ -555,7 +571,7 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job,

/* The paired job fence should come last, when everything else is ready. */
if (!internal_dep)
- internal_dep = pvr_queue_get_paired_frag_job_dep(queue, job);
+ internal_dep = pvr_queue_get_paired_frag_job_dep(job);

return internal_dep;
}
@@ -630,9 +646,8 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job)
if (!jfence)
continue;

- /* Skip the partial render fence, we will place it at the end. */
- if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job &&
- &job->paired_job->base.s_fence->scheduled == fence)
+ /* This fence will be placed last, as partial render fence. */
+ if (is_paired_job_fence(fence, job))
continue;

if (dma_fence_is_signaled(&jfence->base))
@@ -1386,11 +1401,12 @@ void pvr_queue_kill(struct pvr_queue *queue)
/**
* pvr_queue_destroy() - Destroy a queue.
* @queue: The queue to destroy.
+ * @cleanup_queue_entity: Whether to cleanup the queue entity.
*
* Cleanup the queue and free the resources attached to it. Should be
* called from the context release function.
*/
-void pvr_queue_destroy(struct pvr_queue *queue)
+void pvr_queue_destroy(struct pvr_queue *queue, bool cleanup_queue_entity)
{
if (!queue)
return;
@@ -1400,7 +1416,8 @@ void pvr_queue_destroy(struct pvr_queue *queue)
mutex_unlock(&queue->ctx->pvr_dev->queues.lock);

drm_sched_fini(&queue->scheduler);
- drm_sched_entity_fini(&queue->entity);
+ if (cleanup_queue_entity)
+ drm_sched_entity_fini(&queue->entity);

if (WARN_ON(queue->last_queued_job_scheduled_fence))
dma_fence_put(queue->last_queued_job_scheduled_fence);
diff --git a/drivers/gpu/drm/imagination/pvr_queue.h b/drivers/gpu/drm/imagination/pvr_queue.h
index fc1986d73fc8..a205e29437f4 100644
--- a/drivers/gpu/drm/imagination/pvr_queue.h
+++ b/drivers/gpu/drm/imagination/pvr_queue.h
@@ -158,7 +158,7 @@ struct pvr_queue *pvr_queue_create(struct pvr_context *ctx,

void pvr_queue_kill(struct pvr_queue *queue);

-void pvr_queue_destroy(struct pvr_queue *queue);
+void pvr_queue_destroy(struct pvr_queue *queue, bool cleanup_queue_entity);

void pvr_queue_process(struct pvr_queue *queue);

diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c
index 396d349fb6ce..ceb78694cd98 100644
--- a/drivers/gpu/drm/imagination/pvr_vm.c
+++ b/drivers/gpu/drm/imagination/pvr_vm.c
@@ -747,6 +747,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj,

pvr_gem_object_get(pvr_obj);

+ mutex_lock(&vm_ctx->lock);
err = drm_gpuvm_exec_lock(&vm_exec);
if (err)
goto err_cleanup;
@@ -756,6 +757,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj,
drm_gpuvm_exec_unlock(&vm_exec);

err_cleanup:
+ mutex_unlock(&vm_ctx->lock);
pvr_vm_bind_op_fini(&bind_op);

return err;
diff --git a/drivers/gpu/drm/nouveau/nouveau_exec.c b/drivers/gpu/drm/nouveau/nouveau_exec.c
index c01a01aee32b..a08ab1cfea9b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_exec.c
+++ b/drivers/gpu/drm/nouveau/nouveau_exec.c
@@ -331,10 +331,10 @@ nouveau_exec_ucopy(struct nouveau_exec_job_args *args,

return 0;

-err_free_pushs:
- u_free(args->push.s);
err_free_ins:
u_free(args->in_sync.s);
+err_free_pushs:
+ u_free(args->push.s);
return ret;
}

diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
index 36445915aa58..f5e4756b4de4 100644
--- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
@@ -1779,10 +1779,10 @@ nouveau_uvmm_vm_bind_ucopy(struct nouveau_uvmm_bind_job_args *args,

return 0;

-err_free_ops:
- u_free(args->op.s);
err_free_ins:
u_free(args->in_sync.s);
+err_free_ops:
+ u_free(args->op.s);
return ret;
}

diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c
index 4c7745cd6ae5..7fd967a2554f 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c
@@ -315,6 +315,7 @@ nvkm_acr_oneinit(struct nvkm_subdev *subdev)
i, us, fw);
}
}
+ nvkm_done(acr->wpr);
return -EINVAL;
}
nvkm_done(acr->wpr);
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index b2153e04a59a..adab94b3ebdf 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -208,6 +208,7 @@ config DRM_PANEL_HIMAX_HX83121A
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DSC_HELPER
select DRM_KMS_HELPER
help
@@ -308,6 +309,7 @@ config DRM_PANEL_ILITEK_ILI9882T
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DSC_HELPER
help
Say Y if you want to enable support for panels based on the
@@ -914,6 +916,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA8
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DSC_HELPER
help
Say Y or M here if you want to enable support for the
diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index 4f522a912d89..48bb33bac366 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -207,7 +207,10 @@ int panthor_device_init(struct panthor_device *ptdev)
return ret;

#ifdef CONFIG_DEBUG_FS
- drmm_mutex_init(&ptdev->base, &ptdev->gems.lock);
+ ret = drmm_mutex_init(&ptdev->base, &ptdev->gems.lock);
+ if (ret)
+ return ret;
+
INIT_LIST_HEAD(&ptdev->gems.node);
#endif

diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 834c48e368fc..6f498303f4f3 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -824,6 +824,7 @@ static int panthor_fw_load(struct panthor_device *ptdev)
}

if (hdr.size > iter.size) {
+ ret = -EINVAL;
drm_err(&ptdev->base, "Firmware image is truncated\n");
goto out;
}
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 3ac1a79b6f13..533215d6e9cb 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -906,6 +906,7 @@ struct radeon_fence *r100_copy_blit(struct radeon_device *rdev,
{
struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
struct radeon_fence *fence;
+ uint64_t cur_src_offset, cur_dst_offset;
uint32_t cur_pages;
uint32_t stride_bytes = RADEON_GPU_PAGE_SIZE;
uint32_t pitch;
@@ -934,6 +935,10 @@ struct radeon_fence *r100_copy_blit(struct radeon_device *rdev,
cur_pages = 8191;
}
num_gpu_pages -= cur_pages;
+ cur_src_offset = src_offset +
+ (uint64_t)num_gpu_pages * RADEON_GPU_PAGE_SIZE;
+ cur_dst_offset = dst_offset +
+ (uint64_t)num_gpu_pages * RADEON_GPU_PAGE_SIZE;

/* pages are in Y direction - height
page width in X direction - width */
@@ -950,13 +955,13 @@ struct radeon_fence *r100_copy_blit(struct radeon_device *rdev,
RADEON_DP_SRC_SOURCE_MEMORY |
RADEON_GMC_CLR_CMP_CNTL_DIS |
RADEON_GMC_WR_MSK_DIS);
- radeon_ring_write(ring, (pitch << 22) | (src_offset >> 10));
- radeon_ring_write(ring, (pitch << 22) | (dst_offset >> 10));
+ radeon_ring_write(ring, (pitch << 22) | (cur_src_offset >> 10));
+ radeon_ring_write(ring, (pitch << 22) | (cur_dst_offset >> 10));
radeon_ring_write(ring, (0x1fff) | (0x1fff << 16));
radeon_ring_write(ring, 0);
radeon_ring_write(ring, (0x1fff) | (0x1fff << 16));
- radeon_ring_write(ring, num_gpu_pages);
- radeon_ring_write(ring, num_gpu_pages);
+ radeon_ring_write(ring, 0);
+ radeon_ring_write(ring, 0);
radeon_ring_write(ring, cur_pages | (stride_pixels << 16));
}
radeon_ring_write(ring, PACKET0(RADEON_DSTCACHE_CTLSTAT, 0));
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
index 0d0cf10225bb..9a83f4e03e9b 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
@@ -528,7 +528,7 @@ static int rzg2l_mipi_dsi_dphy_init(struct rzg2l_mipi_dsi *dsi,
if (ret < 0)
return ret;

- udelay(1);
+ fsleep(1000);

return 0;
}
@@ -1025,29 +1025,33 @@ static void rzg2l_mipi_dsi_atomic_pre_enable(struct drm_bridge *bridge,
const struct drm_display_mode *mode;
struct drm_connector *connector;
struct drm_crtc *crtc;
- int ret;

connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
mode = &drm_atomic_get_new_crtc_state(state, crtc)->adjusted_mode;

- ret = rzg2l_mipi_dsi_startup(dsi, mode);
- if (ret < 0)
- return;
-
- rzg2l_mipi_dsi_set_display_timing(dsi, mode);
+ rzg2l_mipi_dsi_startup(dsi, mode);
}

static void rzg2l_mipi_dsi_atomic_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge);
+ const struct drm_display_mode *mode;
+ struct drm_connector *connector;
+ struct drm_crtc *crtc;
int ret;

ret = rzg2l_mipi_dsi_start_hs_clock(dsi);
if (ret < 0)
goto err_stop;

+ connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+ crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
+ mode = &drm_atomic_get_new_crtc_state(state, crtc)->adjusted_mode;
+
+ rzg2l_mipi_dsi_set_display_timing(dsi, mode);
+
ret = rzg2l_mipi_dsi_start_video(dsi);
if (ret < 0)
goto err_stop_clock;
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 96bd3dd239d2..387fb6259edd 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -461,6 +461,8 @@ static int rockchip_dp_probe(struct platform_device *pdev)
return -ENOMEM;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;

i = 0;
while (dp_data[i].reg) {
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index 0dc3804051a9..9b82b27770e5 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -685,6 +685,8 @@ int cdn_dp_config_video(struct cdn_dp_device *dp)
val = div_u64(8 * (symbol + 1), bit_per_pix) - val;
val += 2;
ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val);
+ if (ret)
+ goto err_config_video;

switch (video->color_depth) {
case 6:
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
index b14df5b54bc9..d48b92a903f7 100644
--- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
+++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
@@ -50,7 +50,7 @@ struct resource *drm_sysfb_get_memory_si(struct drm_device *dev,
int drm_sysfb_get_stride_si(struct drm_device *dev, const struct screen_info *si,
const struct drm_format_info *format,
unsigned int width, unsigned int height, u64 size);
-u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si,
+s64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si,
unsigned int height, unsigned int stride, u64 size);
#endif

diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c
index 749290196c6a..1c479ce0e503 100644
--- a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c
+++ b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c
@@ -2,6 +2,7 @@

#include <linux/export.h>
#include <linux/limits.h>
+#include <linux/math64.h>
#include <linux/minmax.h>
#include <linux/screen_info.h>

@@ -56,18 +57,24 @@ int drm_sysfb_get_stride_si(struct drm_device *dev, const struct screen_info *si
unsigned int width, unsigned int height, u64 size)
{
u64 lfb_linelength = si->lfb_linelength;
+ s64 stride;

if (!lfb_linelength)
lfb_linelength = drm_format_info_min_pitch(format, 0, width);

- return drm_sysfb_get_validated_int0(dev, "stride", lfb_linelength, div64_u64(size, height));
+ stride = drm_sysfb_get_validated_size0(dev, "stride", lfb_linelength,
+ div64_u64(size, height));
+ if (stride < INT_MIN || stride > INT_MAX)
+ return -EINVAL;
+
+ return (int)stride; /* stride or negative errno code */
}
EXPORT_SYMBOL(drm_sysfb_get_stride_si);

-u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si,
+s64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si,
unsigned int height, unsigned int stride, u64 size)
{
- u64 vsize = PAGE_ALIGN(height * stride);
+ u64 vsize = mul_u32_u32(height, stride);

return drm_sysfb_get_validated_size0(dev, "visible size", vsize, size);
}
diff --git a/drivers/gpu/drm/sysfb/efidrm.c b/drivers/gpu/drm/sysfb/efidrm.c
index a335c94a7bd7..d5adef5deb63 100644
--- a/drivers/gpu/drm/sysfb/efidrm.c
+++ b/drivers/gpu/drm/sysfb/efidrm.c
@@ -150,7 +150,8 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv,
const struct screen_info *si;
const struct drm_format_info *format;
int width, height, stride;
- u64 vsize, mem_flags;
+ s64 vsize;
+ u64 mem_flags;
struct resource resbuf;
struct resource *res;
struct efidrm_device *efi;
@@ -204,8 +205,8 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv,
if (stride < 0)
return ERR_PTR(stride);
vsize = drm_sysfb_get_visible_size_si(dev, si, height, stride, resource_size(res));
- if (!vsize)
- return ERR_PTR(-EINVAL);
+ if (vsize < 0)
+ return ERR_PTR(vsize);

drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d bytes\n",
&format->format, width, height, stride);
diff --git a/drivers/gpu/drm/sysfb/vesadrm.c b/drivers/gpu/drm/sysfb/vesadrm.c
index 4e00113e5c77..d60a67fc1d5b 100644
--- a/drivers/gpu/drm/sysfb/vesadrm.c
+++ b/drivers/gpu/drm/sysfb/vesadrm.c
@@ -400,7 +400,7 @@ static struct vesadrm_device *vesadrm_device_create(struct drm_driver *drv,
const struct screen_info *si;
const struct drm_format_info *format;
int width, height, stride;
- u64 vsize;
+ s64 vsize;
struct resource resbuf;
struct resource *res;
struct vesadrm_device *vesa;
@@ -455,8 +455,8 @@ static struct vesadrm_device *vesadrm_device_create(struct drm_driver *drv,
if (stride < 0)
return ERR_PTR(stride);
vsize = drm_sysfb_get_visible_size_si(dev, si, height, stride, resource_size(res));
- if (!vsize)
- return ERR_PTR(-EINVAL);
+ if (vsize < 0)
+ return ERR_PTR(vsize);

drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d bytes\n",
&format->format, width, height, stride);
diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c
index 19e39fa54bfa..793849199783 100644
--- a/drivers/gpu/drm/tegra/fbdev.c
+++ b/drivers/gpu/drm/tegra/fbdev.c
@@ -76,7 +76,6 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
struct fb_info *info = helper->info;
unsigned int bytes_per_pixel;
struct drm_framebuffer *fb;
- unsigned long offset;
struct tegra_bo *bo;
size_t size;
int err;
@@ -115,9 +114,6 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,

drm_fb_helper_fill_info(info, helper, sizes);

- offset = info->var.xoffset * bytes_per_pixel +
- info->var.yoffset * fb->pitches[0];
-
if (bo->pages) {
bo->vaddr = vmap(bo->pages, bo->num_pages, VM_MAP,
pgprot_writecombine(PAGE_KERNEL));
@@ -129,9 +125,9 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
}

info->flags |= FBINFO_VIRTFB;
- info->screen_buffer = bo->vaddr + offset;
+ info->screen_buffer = bo->vaddr;
info->screen_size = size;
- info->fix.smem_start = (unsigned long)(bo->iova + offset);
+ info->fix.smem_start = (unsigned long)(bo->iova);
info->fix.smem_len = size;

return 0;
diff --git a/drivers/gpu/drm/tests/drm_gem_shmem_test.c b/drivers/gpu/drm/tests/drm_gem_shmem_test.c
index 44a190109249..5e69ff1d1ad6 100644
--- a/drivers/gpu/drm/tests/drm_gem_shmem_test.c
+++ b/drivers/gpu/drm/tests/drm_gem_shmem_test.c
@@ -95,13 +95,9 @@ static void drm_gem_shmem_test_obj_create_private(struct kunit *test)
sg_init_one(sgt->sgl, buf, TEST_SIZE);

/*
- * Set the DMA mask to 64-bits and map the sgtables
- * otherwise drm_gem_shmem_free will cause a warning
- * on debug kernels.
+ * Map the sgtables otherwise drm_gem_shmem_free will cause a warning on
+ * debug kernels.
*/
- ret = dma_set_mask(drm_dev->dev, DMA_BIT_MASK(64));
- KUNIT_ASSERT_EQ(test, ret, 0);
-
ret = dma_map_sgtable(drm_dev->dev, sgt, DMA_BIDIRECTIONAL, 0);
KUNIT_ASSERT_EQ(test, ret, 0);

@@ -352,11 +348,19 @@ static int drm_gem_shmem_test_init(struct kunit *test)
{
struct device *dev;
struct drm_device *drm_dev;
+ int ret;

/* Allocate a parent device */
dev = drm_kunit_helper_alloc_device(test);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);

+ /*
+ * Set the DMA mask to 64-bits to avoid intermittent failures calling
+ * drm_gem_shmem_get_pages_sgt().
+ */
+ ret = dma_set_mask(dev, DMA_BIT_MASK(64));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
/*
* The DRM core will automatically initialize the GEM core and create
* a DRM Memory Manager object which provides an address space pool
diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c
index 81a04f767770..db467bbcdb77 100644
--- a/drivers/gpu/drm/tidss/tidss_encoder.c
+++ b/drivers/gpu/drm/tidss/tidss_encoder.c
@@ -106,6 +106,8 @@ int tidss_encoder_create(struct tidss_device *tidss,
enc = &t_enc->encoder;
enc->possible_crtcs = possible_crtcs;

+ devm_drm_bridge_add(tidss->dev, &t_enc->bridge);
+
/* Attaching first bridge to the encoder */
ret = drm_bridge_attach(enc, &t_enc->bridge, NULL,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
diff --git a/drivers/gpu/drm/ttm/ttm_backup.c b/drivers/gpu/drm/ttm/ttm_backup.c
index 81df4cb5606b..3c067aadc52d 100644
--- a/drivers/gpu/drm/ttm/ttm_backup.c
+++ b/drivers/gpu/drm/ttm/ttm_backup.c
@@ -6,9 +6,10 @@
#include <drm/ttm/ttm_backup.h>

#include <linux/export.h>
-#include <linux/page-flags.h>
#include <linux/swap.h>

+#include "ttm_pool_internal.h"
+
/*
* Need to map shmem indices to handle since a handle value
* of 0 means error, following the swp_entry_t convention.
@@ -68,17 +69,23 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst,
}

/**
- * ttm_backup_backup_page() - Backup a page
+ * ttm_backup_backup_folio() - Backup a folio
* @backup: The struct backup pointer to use.
- * @page: The page to back up.
- * @writeback: Whether to perform immediate writeback of the page.
+ * @folio: The folio to back up.
+ * @order: The allocation order of @folio. Since TTM allocates higher-order
+ * pages without __GFP_COMP, folio_nr_pages(@folio) would always
+ * return 1; the caller must pass the true order explicitly.
+ * @writeback: Whether to perform immediate writeback of the folio's pages.
* This may have performance implications.
- * @idx: A unique integer for each page and each struct backup.
+ * @idx: A unique integer for the first page of the folio and each struct backup.
* This allows the backup implementation to avoid managing
* its address space separately.
- * @page_gfp: The gfp value used when the page was allocated.
- * This is used for accounting purposes.
+ * @folio_gfp: The gfp value used when the folio was allocated.
+ * Currently unused.
* @alloc_gfp: The gfp to be used when allocating memory.
+ * @nr_pages_backed: Output. On a successful return, set to the number of
+ * pages actually backed up, which may be less than (1 << @order)
+ * if an -ENOMEM was encountered mid-folio.
*
* Context: If called from reclaim context, the caller needs to
* assert that the shrinker gfp has __GFP_FS set, to avoid
@@ -87,53 +94,87 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst,
* that the shrinker gfp has __GFP_IO set, since without it,
* we're not allowed to start backup IO.
*
- * Return: A handle on success. Negative error code on failure.
- *
- * Note: This function could be extended to back up a folio and
- * implementations would then split the folio internally if needed.
- * Drawback is that the caller would then have to keep track of
- * the folio size- and usage.
+ * Return: A handle for the first backed-up page on success (handles for
+ * subsequent pages follow sequentially). -ENOMEM if no pages could be backed
+ * up. Any other negative error code if a non-ENOMEM failure occurred; in that
+ * case any pages backed up so far are truncated before returning.
*/
s64
-ttm_backup_backup_page(struct file *backup, struct page *page,
- bool writeback, pgoff_t idx, gfp_t page_gfp,
- gfp_t alloc_gfp)
+ttm_backup_backup_folio(struct file *backup, struct folio *folio,
+ unsigned int order, bool writeback, pgoff_t idx,
+ gfp_t folio_gfp, gfp_t alloc_gfp,
+ pgoff_t *nr_pages_backed)
{
struct address_space *mapping = backup->f_mapping;
- unsigned long handle = 0;
+ int nr_pages = 1 << order;
struct folio *to_folio;
- int ret;
-
- to_folio = shmem_read_folio_gfp(mapping, idx, alloc_gfp);
- if (IS_ERR(to_folio))
- return PTR_ERR(to_folio);
-
- folio_mark_accessed(to_folio);
- folio_lock(to_folio);
- folio_mark_dirty(to_folio);
- copy_highpage(folio_file_page(to_folio, idx), page);
- handle = ttm_backup_shmem_idx_to_handle(idx);
-
- if (writeback && !folio_mapped(to_folio) &&
- folio_clear_dirty_for_io(to_folio)) {
- folio_set_reclaim(to_folio);
- ret = shmem_writeout(to_folio, NULL, NULL);
- if (!folio_test_writeback(to_folio))
- folio_clear_reclaim(to_folio);
+ int ret, i;
+
+ *nr_pages_backed = 0;
+
+ for (i = 0; i < nr_pages; ) {
+ int to_nr, j;
+
/*
- * If writeout succeeds, it unlocks the folio. errors
- * are otherwise dropped, since writeout is only best
- * effort here.
+ * Only inject past the first subpage so *nr_pages_backed is
+ * always > 0 here, matching a genuine mid-compound -ENOMEM
+ * and driving the caller's reactive split fallback instead
+ * of an early, no-progress failure.
*/
- if (ret)
+ if (IS_ENABLED(CONFIG_FAULT_INJECTION) && i &&
+ ttm_backup_fault_inject_folio())
+ to_folio = ERR_PTR(-ENOMEM);
+ else
+ to_folio = shmem_read_folio_gfp(mapping, idx + i, alloc_gfp);
+ if (IS_ERR(to_folio)) {
+ int err = PTR_ERR(to_folio);
+
+ if (err == -ENOMEM && *nr_pages_backed)
+ return ttm_backup_shmem_idx_to_handle(idx);
+
+ if (*nr_pages_backed) {
+ shmem_truncate_range(file_inode(backup),
+ (loff_t)idx << PAGE_SHIFT,
+ ((loff_t)(idx + i) << PAGE_SHIFT) - 1);
+ /*
+ * The pages just truncated are no longer
+ * backed up; don't let the caller mistake
+ * them for valid handles.
+ */
+ *nr_pages_backed = 0;
+ }
+ return err;
+ }
+
+ to_nr = min_t(int, nr_pages - i,
+ folio_next_index(to_folio) - (idx + i));
+
+ folio_mark_accessed(to_folio);
+ folio_lock(to_folio);
+ folio_mark_dirty(to_folio);
+
+ for (j = 0; j < to_nr; j++)
+ copy_highpage(folio_file_page(to_folio, idx + i + j),
+ folio_page(folio, i + j));
+
+ if (writeback && !folio_mapped(to_folio) &&
+ folio_clear_dirty_for_io(to_folio)) {
+ folio_set_reclaim(to_folio);
+ ret = shmem_writeout(to_folio, NULL, NULL);
+ if (!folio_test_writeback(to_folio))
+ folio_clear_reclaim(to_folio);
+ if (ret == AOP_WRITEPAGE_ACTIVATE)
+ folio_unlock(to_folio);
+ } else {
folio_unlock(to_folio);
- } else {
- folio_unlock(to_folio);
- }
+ }

- folio_put(to_folio);
+ folio_put(to_folio);
+ i += to_nr;
+ *nr_pages_backed = i;
+ }

- return handle;
+ return ttm_backup_shmem_idx_to_handle(idx);
}

/**
diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c
index 278bbe7a11ad..e7648ad3e615 100644
--- a/drivers/gpu/drm/ttm/ttm_pool.c
+++ b/drivers/gpu/drm/ttm/ttm_pool.c
@@ -53,8 +53,23 @@
#ifdef CONFIG_FAULT_INJECTION
#include <linux/fault-inject.h>
static DECLARE_FAULT_ATTR(backup_fault_inject);
+
+/*
+ * Exposed to ttm_backup.c so a mid-compound subpage can be made to fail
+ * with -ENOMEM, exercising the reactive split-and-retry fallback in
+ * ttm_pool_backup() for high-order backups.
+ */
+bool ttm_backup_fault_inject_folio(void)
+{
+ return should_fail(&backup_fault_inject, 1);
+}
#else
#define should_fail(...) false
+
+bool ttm_backup_fault_inject_folio(void)
+{
+ return false;
+}
#endif

/**
@@ -487,7 +502,7 @@ static void ttm_pool_split_for_swap(struct ttm_pool *pool, struct page *p)
/**
* DOC: Partial backup and restoration of a struct ttm_tt.
*
- * Swapout using ttm_backup_backup_page() and swapin using
+ * Swapout using ttm_backup_backup_folio() and swapin using
* ttm_backup_copy_page() may fail.
* The former most likely due to lack of swap-space or memory, the latter due
* to lack of memory or because of signal interruption during waits.
@@ -1036,12 +1051,12 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
{
struct file *backup = tt->backup;
struct page *page;
- unsigned long handle;
gfp_t alloc_gfp;
gfp_t gfp;
int ret = 0;
pgoff_t shrunken = 0;
- pgoff_t i, num_pages;
+ pgoff_t i, j, num_pages, npages;
+ pgoff_t nr_backed;

if (WARN_ON(ttm_tt_is_backed_up(tt)))
return -EINVAL;
@@ -1051,9 +1066,31 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
return -EBUSY;

#ifdef CONFIG_X86
- /* Anything returned to the system needs to be cached. */
- if (tt->caching != ttm_cached)
- set_pages_array_wb(tt->pages, tt->num_pages);
+ /* Anything returned to the system needs to be cached. Walk allocations
+ * skipping NULL pages and issue set_pages_array_wb() per contiguous run.
+ */
+ if (tt->caching != ttm_cached) {
+ pgoff_t run_start = 0, run_count = 0;
+
+ for (i = 0; i < tt->num_pages; i += num_pages) {
+ page = tt->pages[i];
+ if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) {
+ if (run_count) {
+ set_pages_array_wb(&tt->pages[run_start],
+ run_count);
+ run_count = 0;
+ }
+ num_pages = 1;
+ continue;
+ }
+ num_pages = 1UL << ttm_pool_page_order(pool, page);
+ if (!run_count)
+ run_start = i;
+ run_count += num_pages;
+ }
+ if (run_count)
+ set_pages_array_wb(&tt->pages[run_start], run_count);
+ }
#endif

if (tt->dma_address || flags->purge) {
@@ -1061,7 +1098,7 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
unsigned int order;

page = tt->pages[i];
- if (unlikely(!page)) {
+ if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) {
num_pages = 1;
continue;
}
@@ -1097,26 +1134,74 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
if (IS_ENABLED(CONFIG_FAULT_INJECTION) && should_fail(&backup_fault_inject, 1))
num_pages = DIV_ROUND_UP(num_pages, 2);

- for (i = 0; i < num_pages; ++i) {
- s64 shandle;
+ for (i = 0; i < num_pages; i += npages) {
+ unsigned int order;
+ s64 handle;

+ npages = 1;
page = tt->pages[i];
if (unlikely(!page))
continue;

- ttm_pool_split_for_swap(pool, page);
+ /* Already-handled entry from a previous attempt. */
+ if (unlikely(ttm_backup_page_ptr_is_handle(page)))
+ continue;

- shandle = ttm_backup_backup_page(backup, page, flags->writeback, i,
- gfp, alloc_gfp);
- if (shandle < 0) {
- /* We allow partially shrunken tts */
- ret = shandle;
+ order = ttm_pool_page_order(pool, page);
+ npages = 1UL << order;
+
+ /*
+ * We don't allow dipping kernel reserves for high order backup
+ */
+ if (order)
+ alloc_gfp |= __GFP_NOMEMALLOC;
+ else
+ alloc_gfp &= ~__GFP_NOMEMALLOC;
+
+ /*
+ * Back up the compound atomically at its native order. If
+ * fault injection truncated num_pages mid-compound, skip
+ * the partial tail rather than splitting.
+ */
+ if (unlikely(i + npages > num_pages))
+ break;
+
+ handle = ttm_backup_backup_folio(backup, page_folio(page),
+ order, flags->writeback, i,
+ gfp, alloc_gfp,
+ &nr_backed);
+ /*
+ * Zero progress on this compound (whether order 0 or a
+ * high-order compound that failed before backing up even
+ * its first subpage) is unrecoverable: bail out rather than
+ * looping forever with npages == nr_backed == 0 below.
+ */
+ if (unlikely(handle < 0 && !nr_backed)) {
+ ret = handle;
break;
}
- handle = shandle;
- tt->pages[i] = ttm_backup_handle_to_page_ptr(handle);
- __free_pages_gpu_account(page, 0, false);
- shrunken++;
+
+ for (j = 0; j < nr_backed; j++)
+ tt->pages[i + j] = ttm_backup_handle_to_page_ptr(handle + j);
+
+ shrunken += nr_backed;
+
+ if (unlikely(nr_backed < npages)) {
+ /*
+ * Partial OOM backup: split the compound and free the
+ * subpages whose content is now in shmem. Continue the
+ * loop from the first un-backed order-0 page.
+ */
+ ttm_pool_split_for_swap(pool, page);
+ for (j = 0; j < nr_backed; j++)
+ __free_pages_gpu_account(page + j, 0, false);
+ npages = nr_backed;
+ continue;
+ }
+
+ /* Fully backed up: free at native order. */
+ page->private = 0;
+ __free_pages_gpu_account(page, order, false);
}

return shrunken ? shrunken : ret;
diff --git a/drivers/gpu/drm/ttm/ttm_pool_internal.h b/drivers/gpu/drm/ttm/ttm_pool_internal.h
index 24c179fd69d1..cbb17a2129fe 100644
--- a/drivers/gpu/drm/ttm/ttm_pool_internal.h
+++ b/drivers/gpu/drm/ttm/ttm_pool_internal.h
@@ -22,4 +22,12 @@ static inline unsigned int ttm_pool_beneficial_order(struct ttm_pool *pool)
return pool->alloc_flags & 0xff;
}

+/*
+ * Implemented in ttm_pool.c, used by ttm_backup.c. Returns true if a fault
+ * should be injected mid-compound to test the reactive split-and-retry
+ * fallback in ttm_pool_backup(). Always returns false when
+ * CONFIG_FAULT_INJECTION is disabled.
+ */
+bool ttm_backup_fault_inject_folio(void);
+
#endif
diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c
index dd7da419702f..8277b3182754 100644
--- a/drivers/gpu/drm/v3d/v3d_gem.c
+++ b/drivers/gpu/drm/v3d/v3d_gem.c
@@ -46,6 +46,18 @@ v3d_init_hw_state(struct v3d_dev *v3d)
static void
v3d_idle_axi(struct v3d_dev *v3d, int core)
{
+ if (v3d->ver >= V3D_GEN_71) {
+ V3D_WRITE(V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ);
+
+ if (wait_for((V3D_READ(V3D_GMP_STATUS(v3d->ver)) &
+ (V3D_GMP_STATUS_RD_COUNT_MASK |
+ V3D_GMP_STATUS_WR_COUNT_MASK |
+ V3D_GMP_STATUS_CFG_BUSY)) == 0, 100)) {
+ drm_err(&v3d->drm, "Failed to wait for safe GMP shutdown\n");
+ }
+ return;
+ }
+
V3D_CORE_WRITE(core, V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ);

if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS(v3d->ver)) &
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index 9377e58f9bc2..9e7c26244aed 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -733,9 +733,13 @@ static int vc4_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct
{
struct vc4_bo *bo = to_vc4_bo(obj);

- if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) {
- DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n");
- return -EINVAL;
+ if (bo->validated_shader) {
+ if (vma->vm_flags & VM_WRITE) {
+ DRM_DEBUG("mmapping of shader BOs for writing not allowed.\n");
+ return -EINVAL;
+ }
+
+ vm_flags_clear(vma, VM_MAYWRITE);
}

mutex_lock(&bo->madv_lock);
@@ -1045,7 +1049,7 @@ static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused)
struct vc4_dev *vc4 = to_vc4_dev(dev);
int i;

- timer_delete(&vc4->bo_cache.time_timer);
+ timer_shutdown_sync(&vc4->bo_cache.time_timer);
cancel_work_sync(&vc4->bo_cache.time_work);

vc4_bo_cache_purge(dev);
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index ee8d0738501b..89359d190008 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -1752,7 +1752,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
static void vc4_hvs_unbind(struct device *dev, struct device *master,
void *data)
{
- struct drm_device *drm = dev_get_drvdata(master);
+ struct drm_device *drm = data;
struct vc4_dev *vc4 = to_vc4_dev(drm);
struct vc4_hvs *hvs = vc4->hvs;
struct drm_mm_node *node, *next;
diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
index d31b906cb8e7..f32410420d3e 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -494,7 +494,7 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
static void vc4_v3d_unbind(struct device *dev, struct device *master,
void *data)
{
- struct drm_device *drm = dev_get_drvdata(master);
+ struct drm_device *drm = data;
struct vc4_dev *vc4 = to_vc4_dev(drm);

vc4_irq_uninstall(drm);
diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
index 435d37d36034..66c3f6f74e9c 100644
--- a/drivers/gpu/drm/virtio/virtgpu_gem.c
+++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
@@ -139,13 +139,15 @@ void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
if (!vgdev->has_virgl_3d)
return;

- objs = virtio_gpu_array_alloc(1);
- if (!objs)
- return;
- virtio_gpu_array_add_obj(objs, obj);
+ if (vfpriv->context_created) {
+ objs = virtio_gpu_array_alloc(1);
+ if (!objs)
+ return;
+ virtio_gpu_array_add_obj(objs, obj);

- virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
- objs);
+ virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
+ objs);
+ }
virtio_gpu_notify(vgdev);
}

diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 80ba69b4860b..b79fd8350b6a 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -49,7 +49,10 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
virtio_gpu_cmd_get_edids(vgdev);
virtio_gpu_cmd_get_display_info(vgdev);
virtio_gpu_notify(vgdev);
- drm_helper_hpd_irq_event(vgdev->ddev);
+ wait_event_timeout(vgdev->resp_wq,
+ !vgdev->display_info_pending,
+ 5 * HZ);
+ drm_kms_helper_hotplug_event(vgdev->ddev);
}
events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index 67865810a2e7..e5e1af8b8e8a 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -840,9 +840,6 @@ static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
vgdev->display_info_pending = false;
spin_unlock(&vgdev->display_info_lock);
wake_up(&vgdev->resp_wq);
-
- if (!drm_helper_hpd_irq_event(vgdev->ddev))
- drm_kms_helper_hotplug_event(vgdev->ddev);
}

static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev,
@@ -897,7 +894,8 @@ static int virtio_get_edid_block(void *data, u8 *buf,
struct virtio_gpu_resp_edid *resp = data;
size_t start = block * EDID_LENGTH;

- if (start + len > le32_to_cpu(resp->size))
+ if (start + len > le32_to_cpu(resp->size) ||
+ start + len > sizeof(resp->edid))
return -EINVAL;
memcpy(buf, resp->edid + start, len);
return 0;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index b2d3927b5567..bd0563741e89 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -78,7 +78,7 @@ static int vmw_gb_surface_unbind(struct vmw_resource *res,
static int vmw_gb_surface_destroy(struct vmw_resource *res);
static int
vmw_gb_surface_define_internal(struct drm_device *dev,
- struct drm_vmw_gb_surface_create_ext_req *req,
+ const struct drm_vmw_gb_surface_create_ext_req *req,
struct drm_vmw_gb_surface_create_rep *rep,
struct drm_file *file_priv);
static int
@@ -1503,7 +1503,7 @@ int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, void *data,
*/
static int
vmw_gb_surface_define_internal(struct drm_device *dev,
- struct drm_vmw_gb_surface_create_ext_req *req,
+ const struct drm_vmw_gb_surface_create_ext_req *req,
struct drm_vmw_gb_surface_create_rep *rep,
struct drm_file *file_priv)
{
@@ -1521,9 +1521,21 @@ vmw_gb_surface_define_internal(struct drm_device *dev,
req->base.svga3d_flags);

/* array_size must be null for non-GL3 host. */
- if (req->base.array_size > 0 && !has_sm4_context(dev_priv)) {
- VMW_DEBUG_USER("SM4 surface not supported.\n");
- return -EINVAL;
+ if (req->base.array_size > 0) {
+ if (has_sm5_context(dev_priv)) {
+ if (req->base.array_size > SVGA3D_SM5_MAX_SURFACE_ARRAYSIZE) {
+ VMW_DEBUG_USER("Invalid Surface Array Size.\n");
+ return -EINVAL;
+ }
+ } else if (has_sm4_context(dev_priv)) {
+ if (req->base.array_size > SVGA3D_SM4_MAX_SURFACE_ARRAYSIZE) {
+ VMW_DEBUG_USER("Invalid Surface Array Size.\n");
+ return -EINVAL;
+ }
+ } else {
+ VMW_DEBUG_USER("SM4+ surface not supported.\n");
+ return -EINVAL;
+ }
}

if (!has_sm4_1_context(dev_priv)) {
diff --git a/drivers/gpu/drm/xe/display/xe_display_bo.c b/drivers/gpu/drm/xe/display/xe_display_bo.c
index 1d81b9908265..495821717206 100644
--- a/drivers/gpu/drm/xe/display/xe_display_bo.c
+++ b/drivers/gpu/drm/xe/display/xe_display_bo.c
@@ -42,7 +42,8 @@ static int xe_display_bo_framebuffer_init(struct drm_gem_object *obj,
if (ret)
goto err;

- if (!(bo->flags & XE_BO_FLAG_FORCE_WC)) {
+ if (!(bo->flags & XE_BO_FLAG_FORCE_WC) &&
+ bo->ttm.type != ttm_bo_type_sg) {
/*
* XE_BO_FLAG_FORCE_WC should ideally be set at creation, or is
* automatically set when creating FB. We cannot change caching
diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c
index e45a1e7a4670..bfbe3e44e78d 100644
--- a/drivers/gpu/drm/xe/display/xe_fb_pin.c
+++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c
@@ -481,7 +481,8 @@ int intel_plane_pin_fb(struct intel_plane_state *new_plane_state,
return 0;

/* We reject creating !SCANOUT fb's, so this is weird.. */
- drm_WARN_ON(bo->ttm.base.dev, !(bo->flags & XE_BO_FLAG_FORCE_WC));
+ drm_WARN_ON(bo->ttm.base.dev, !(bo->flags & XE_BO_FLAG_FORCE_WC) &&
+ bo->ttm.type != ttm_bo_type_sg);

vma = __xe_pin_fb_vma(intel_fb, &new_plane_state->view.gtt, alignment);

diff --git a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h
index 4d83461e538b..d6bc19ef277b 100644
--- a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h
@@ -9,7 +9,11 @@
#define XELPG_GGTT_PTE_PAT0 BIT_ULL(52)
#define XELPG_GGTT_PTE_PAT1 BIT_ULL(53)

-#define XE_PTE_ADDR_MASK GENMASK_ULL(51, 12)
+/*
+ * Mask for PTE address bits [51:shift].
+ * shift is the lower address boundary of page.
+ */
+#define XE_PAGE_ADDR_MASK(shift) GENMASK_ULL(51, (shift))
#define GGTT_PTE_VFID GENMASK_ULL(11, 2)

#define GUC_GGTT_TOP 0xFEE00000
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index 6b518858538f..013b7f3e0649 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -1109,6 +1109,21 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict,
xe_pm_runtime_get_noresume(xe);
}

+ /*
+ * Attach CCS BBs before submitting the copy job below so a VF
+ * migration racing the copy sees valid, up to date attach state.
+ */
+ if (IS_VF_CCS_READY(xe) &&
+ ((move_lacks_source && new_mem->mem_type == XE_PL_TT) ||
+ (old_mem_type == XE_PL_SYSTEM && new_mem->mem_type == XE_PL_TT)) &&
+ handle_system_ccs) {
+ ret = xe_sriov_vf_ccs_attach_bo(bo, new_mem);
+ if (ret) {
+ xe_pm_runtime_put(xe);
+ goto out;
+ }
+ }
+
if (move_lacks_source) {
u32 flags = 0;

@@ -1146,22 +1161,19 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict,
ttm_bo_move_null(ttm_bo, new_mem);
}

- dma_fence_put(fence);
- xe_pm_runtime_put(xe);
-
/*
- * CCS meta data is migrated from TT -> SMEM. So, let us detach the
- * BBs from BO as it is no longer needed.
+ * Detach must wait for the copy above to complete: a VF migration
+ * racing an in-flight copy must still see valid CCS BBs, so don't
+ * tear them down until the copy fence has signaled.
*/
if (IS_VF_CCS_READY(xe) && old_mem_type == XE_PL_TT &&
- new_mem->mem_type == XE_PL_SYSTEM)
+ new_mem->mem_type == XE_PL_SYSTEM) {
+ dma_fence_wait(fence, false);
xe_sriov_vf_ccs_detach_bo(bo);
+ }

- if (IS_VF_CCS_READY(xe) &&
- ((move_lacks_source && new_mem->mem_type == XE_PL_TT) ||
- (old_mem_type == XE_PL_SYSTEM && new_mem->mem_type == XE_PL_TT)) &&
- handle_system_ccs)
- ret = xe_sriov_vf_ccs_attach_bo(bo);
+ dma_fence_put(fence);
+ xe_pm_runtime_put(xe);

out:
if ((!ttm_bo->resource || ttm_bo->resource->mem_type == XE_PL_SYSTEM) &&
@@ -1356,7 +1368,7 @@ int xe_bo_notifier_prepare_pinned(struct xe_bo *bo)
backup = xe_bo_init_locked(xe, NULL, NULL, bo->ttm.base.resv, NULL, xe_bo_size(bo),
DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS |
- XE_BO_FLAG_PINNED, &exec);
+ XE_BO_FLAG_PINNED, NULL, &exec);
if (IS_ERR(backup)) {
drm_exec_retry_on_contention(&exec);
ret = PTR_ERR(backup);
@@ -1497,7 +1509,7 @@ int xe_bo_evict_pinned(struct xe_bo *bo)
xe_bo_size(bo),
DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS |
- XE_BO_FLAG_PINNED, &exec);
+ XE_BO_FLAG_PINNED, NULL, &exec);
if (IS_ERR(backup)) {
drm_exec_retry_on_contention(&exec);
ret = PTR_ERR(backup);
@@ -1833,6 +1845,8 @@ static void xe_ttm_bo_destroy(struct ttm_buffer_object *ttm_bo)

if (bo->ttm.base.import_attach)
drm_prime_gem_destroy(&bo->ttm.base, NULL);
+ if (bo->dma_buf)
+ dma_buf_put(bo->dma_buf);
drm_gem_object_release(&bo->ttm.base);

xe_assert(xe, list_empty(&ttm_bo->base.gpuva.list));
@@ -2290,6 +2304,8 @@ void xe_bo_free(struct xe_bo *bo)
* @cpu_caching: The cpu caching used for system memory backing store.
* @type: The TTM buffer object type.
* @flags: XE_BO_FLAG_ flags.
+ * @dma_buf: The dma-buf to reference for the BO lifetime (imported BOs),
+ * or NULL.
* @exec: The drm_exec transaction to use for exhaustive eviction.
*
* Initialize or create an xe buffer object. On failure, any allocated buffer
@@ -2301,7 +2317,8 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
struct xe_tile *tile, struct dma_resv *resv,
struct ttm_lru_bulk_move *bulk, size_t size,
u16 cpu_caching, enum ttm_bo_type type,
- u32 flags, struct drm_exec *exec)
+ u32 flags, struct dma_buf *dma_buf,
+ struct drm_exec *exec)
{
struct ttm_operation_ctx ctx = {
.interruptible = true,
@@ -2390,6 +2407,17 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
placement = (type == ttm_bo_type_sg ||
bo->flags & XE_BO_FLAG_DEFER_BACKING) ? &sys_placement :
&bo->placement;
+
+ /*
+ * For imported BOs, keep the exporter dma-buf alive for the BO
+ * lifetime. Taken before ttm_bo_init_reserved() to also cover a
+ * creation failure there. Released in xe_ttm_bo_destroy().
+ */
+ if (dma_buf) {
+ get_dma_buf(dma_buf);
+ bo->dma_buf = dma_buf;
+ }
+
err = ttm_bo_init_reserved(&xe->ttm, &bo->ttm, type,
placement, alignment,
&ctx, NULL, resv, xe_ttm_bo_destroy);
@@ -2507,7 +2535,7 @@ __xe_bo_create_locked(struct xe_device *xe,
vm && !xe_vm_in_fault_mode(vm) &&
flags & XE_BO_FLAG_USER ?
&vm->lru_bulk_move : NULL, size,
- cpu_caching, type, flags, exec);
+ cpu_caching, type, flags, NULL, exec);
if (IS_ERR(bo))
return bo;

diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h
index 6340317f7d2e..7ae1d9ac0574 100644
--- a/drivers/gpu/drm/xe/xe_bo.h
+++ b/drivers/gpu/drm/xe/xe_bo.h
@@ -118,7 +118,8 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
struct xe_tile *tile, struct dma_resv *resv,
struct ttm_lru_bulk_move *bulk, size_t size,
u16 cpu_caching, enum ttm_bo_type type,
- u32 flags, struct drm_exec *exec);
+ u32 flags, struct dma_buf *dma_buf,
+ struct drm_exec *exec);
struct xe_bo *xe_bo_create_locked(struct xe_device *xe, struct xe_tile *tile,
struct xe_vm *vm, size_t size,
enum ttm_bo_type type, u32 flags,
diff --git a/drivers/gpu/drm/xe/xe_bo_types.h b/drivers/gpu/drm/xe/xe_bo_types.h
index 077e35b4cdce..b1cdd7acc15c 100644
--- a/drivers/gpu/drm/xe/xe_bo_types.h
+++ b/drivers/gpu/drm/xe/xe_bo_types.h
@@ -36,6 +36,8 @@ struct xe_bo {
struct xe_bo *backup_obj;
/** @parent_obj: Ref to parent bo if this a backup_obj */
struct xe_bo *parent_obj;
+ /** @dma_buf: Imported dma-buf ref to keep its resv alive. */
+ struct dma_buf *dma_buf;
/** @flags: flags for this buffer object */
u32 flags;
/** @vm: VM this BO is attached to, for extobj this will be NULL */
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index ffea4a453c01..991b0ea9218b 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -509,7 +509,7 @@ struct xe_device *xe_device_create(struct pci_dev *pdev,
WQ_MEM_RECLAIM);
xe->ordered_wq = alloc_ordered_workqueue("xe-ordered-wq", 0);
xe->unordered_wq = alloc_workqueue("xe-unordered-wq", WQ_PERCPU, 0);
- xe->destroy_wq = alloc_workqueue("xe-destroy-wq", WQ_PERCPU, 0);
+ xe->destroy_wq = alloc_workqueue("xe-destroy-wq", WQ_PERCPU | WQ_MEM_RECLAIM, 0);
if (!xe->ordered_wq || !xe->unordered_wq ||
!xe->preempt_fence_wq || !xe->destroy_wq) {
/*
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 615218d775b1..7e0901ac95c9 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -351,7 +351,7 @@ struct xe_device {
/** @unordered_wq: used to serialize unordered work */
struct workqueue_struct *unordered_wq;

- /** @destroy_wq: used to serialize user destroy work, like queue */
+ /** @destroy_wq: used to serialize SVM pagemap destroy work */
struct workqueue_struct *destroy_wq;

/** @tiles: device tiles */
diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
index 8a920e58245c..bf0728838ead 100644
--- a/drivers/gpu/drm/xe/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/xe_dma_buf.c
@@ -302,7 +302,7 @@ xe_dma_buf_create_obj(struct drm_device *dev, struct dma_buf *dma_buf)

bo = xe_bo_init_locked(xe, NULL, NULL, resv, NULL, dma_buf->size,
0, /* Will require 1way or 2way for vm_bind */
- ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec);
+ ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, dma_buf, &exec);
drm_exec_retry_on_contention(&exec);
if (IS_ERR(bo)) {
ret = PTR_ERR(bo);
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c
index 2f5816c78fba..14d175f22e0e 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture.c
+++ b/drivers/gpu/drm/xe/xe_guc_capture.c
@@ -461,8 +461,14 @@ static void guc_capture_alloc_steered_lists(struct xe_guc *guc)
if (!list || guc->capture->extlists)
return;

- total = bitmap_weight(gt->fuse_topo.g_dss_mask, sizeof(gt->fuse_topo.g_dss_mask) * 8) *
- guc_capture_get_steer_reg_num(guc_to_xe(guc));
+ {
+ xe_dss_mask_t all_dss;
+
+ total = bitmap_weighted_or(all_dss, gt->fuse_topo.g_dss_mask,
+ gt->fuse_topo.c_dss_mask,
+ XE_MAX_DSS_FUSE_BITS) *
+ guc_capture_get_steer_reg_num(guc_to_xe(guc));
+ }

if (!total)
return;
diff --git a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
index e5e53b421f29..cda14d954e57 100644
--- a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
@@ -10,6 +10,7 @@
#include <linux/workqueue.h>

#include "xe_gpu_scheduler_types.h"
+#include "xe_hw_fence_types.h"

struct dma_fence;
struct xe_exec_queue;
@@ -24,6 +25,10 @@ struct xe_guc_exec_queue {
struct rcu_head rcu;
/** @sched: GPU scheduler for this xe_exec_queue */
struct xe_gpu_scheduler sched;
+ /**
+ * @name: Scheduler timeline name, kept with @sched until RCU free.
+ */
+ char name[MAX_FENCE_NAME_LEN];
/** @entity: Scheduler entity for this xe_exec_queue */
struct xe_sched_entity entity;
/**
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index 42110e01b7d0..b8465553aed2 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -10,6 +10,7 @@
#include <linux/circ_buf.h>
#include <linux/dma-fence-array.h>

+#include <drm/drm_drv.h>
#include <drm/drm_managed.h>

#include "abi/guc_actions_abi.h"
@@ -37,6 +38,7 @@
#include "xe_macros.h"
#include "xe_map.h"
#include "xe_mocs.h"
+#include "xe_module.h"
#include "xe_pm.h"
#include "xe_ring_ops_types.h"
#include "xe_sched_job.h"
@@ -232,17 +234,9 @@ static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q)
static void guc_submit_sw_fini(struct drm_device *drm, void *arg)
{
struct xe_guc *guc = arg;
- struct xe_device *xe = guc_to_xe(guc);
struct xe_gt *gt = guc_to_gt(guc);
- int ret;
-
- ret = wait_event_timeout(guc->submission_state.fini_wq,
- xa_empty(&guc->submission_state.exec_queue_lookup),
- HZ * 5);
-
- drain_workqueue(xe->destroy_wq);

- xe_gt_assert(gt, ret);
+ xe_gt_assert(gt, xa_empty(&guc->submission_state.exec_queue_lookup));

xa_destroy(&guc->submission_state.exec_queue_lookup);
}
@@ -319,8 +313,6 @@ int xe_guc_submit_init(struct xe_guc *guc, unsigned int num_ids)

xa_init(&guc->submission_state.exec_queue_lookup);

- init_waitqueue_head(&guc->submission_state.fini_wq);
-
primelockdep(guc);

guc->submission_state.initialized = true;
@@ -397,46 +389,40 @@ void xe_guc_submit_disable(struct xe_guc *guc)
guc->submission_state.enabled = false;
}

-static void __release_guc_id(struct xe_guc *guc, struct xe_exec_queue *q, u32 xa_count)
+static void __release_guc_id(struct xe_guc *guc, struct xe_exec_queue *q,
+ int count)
{
int i;

- lockdep_assert_held(&guc->submission_state.lock);
+ mutex_lock(&guc->submission_state.lock);

- for (i = 0; i < xa_count; ++i)
- xa_erase(&guc->submission_state.exec_queue_lookup, q->guc->id + i);
+ for (i = 0; i < count; ++i)
+ xa_erase(&guc->submission_state.exec_queue_lookup,
+ q->guc->id + i);

xe_guc_id_mgr_release_locked(&guc->submission_state.idm,
q->guc->id, q->width);

- if (xa_empty(&guc->submission_state.exec_queue_lookup))
- wake_up(&guc->submission_state.fini_wq);
+ mutex_unlock(&guc->submission_state.lock);
}

static int alloc_guc_id(struct xe_guc *guc, struct xe_exec_queue *q)
{
- int ret;
- int i;
-
- /*
- * Must use GFP_NOWAIT as this lock is in the dma fence signalling path,
- * worse case user gets -ENOMEM on engine create and has to try again.
- *
- * FIXME: Have caller pre-alloc or post-alloc /w GFP_KERNEL to prevent
- * failure.
- */
- lockdep_assert_held(&guc->submission_state.lock);
+ int ret, i;

+ mutex_lock(&guc->submission_state.lock);
ret = xe_guc_id_mgr_reserve_locked(&guc->submission_state.idm,
q->width);
+ mutex_unlock(&guc->submission_state.lock);
if (ret < 0)
return ret;

q->guc->id = ret;

+ /* Reserve empty slots. */
for (i = 0; i < q->width; ++i) {
- ret = xa_err(xa_store(&guc->submission_state.exec_queue_lookup,
- q->guc->id + i, q, GFP_NOWAIT));
+ ret = xa_insert(&guc->submission_state.exec_queue_lookup,
+ q->guc->id + i, NULL, GFP_KERNEL);
if (ret)
goto err_release;
}
@@ -449,11 +435,24 @@ static int alloc_guc_id(struct xe_guc *guc, struct xe_exec_queue *q)
return ret;
}

+static void publish_guc_id(struct xe_guc *guc, struct xe_exec_queue *q)
+{
+ int i;
+
+ lockdep_assert_held(&guc->submission_state.lock);
+
+ for (i = 0; i < q->width; ++i) {
+ void *old;
+
+ old = xa_store(&guc->submission_state.exec_queue_lookup,
+ q->guc->id + i, q, GFP_NOWAIT);
+ XE_WARN_ON(old || xa_is_err(old));
+ }
+}
+
static void release_guc_id(struct xe_guc *guc, struct xe_exec_queue *q)
{
- mutex_lock(&guc->submission_state.lock);
__release_guc_id(guc, q, q->width);
- mutex_unlock(&guc->submission_state.lock);
}

struct exec_queue_policy {
@@ -1680,6 +1679,7 @@ static void guc_exec_queue_fini(struct xe_exec_queue *q)
{
struct xe_guc_exec_queue *ge = q->guc;
struct xe_guc *guc = exec_queue_to_guc(q);
+ struct drm_device *drm = &guc_to_xe(guc)->drm;

if (xe_exec_queue_is_multi_queue_secondary(q)) {
struct xe_exec_queue_group *group = q->multi_queue.group;
@@ -1698,36 +1698,52 @@ static void guc_exec_queue_fini(struct xe_exec_queue *q)
* (timeline name).
*/
kfree_rcu(ge, rcu);
+
+ drm_dev_put(drm);
}

-static void __guc_exec_queue_destroy_async(struct work_struct *w)
+static void guc_exec_queue_do_destroy(struct xe_exec_queue *q)
{
- struct xe_guc_exec_queue *ge =
- container_of(w, struct xe_guc_exec_queue, destroy_async);
- struct xe_exec_queue *q = ge->q;
+ struct xe_guc_exec_queue *ge = q->guc;
struct xe_guc *guc = exec_queue_to_guc(q);
+ struct xe_device *xe = guc_to_xe(guc);
+ struct drm_device *drm = &xe->drm;

- guard(xe_pm_runtime)(guc_to_xe(guc));
- trace_xe_exec_queue_destroy(q);
+ /*
+ * guc_exec_queue_fini() drops the queue's drm_device ref.
+ * Keep the device alive until the PM-runtime guard unwinds.
+ */
+ drm_dev_get(drm);
+
+ scoped_guard(xe_pm_runtime, xe) {
+ trace_xe_exec_queue_destroy(q);

- /* Confirm no work left behind accessing device structures */
- cancel_delayed_work_sync(&ge->sched.base.work_tdr);
+ /* Confirm no work left behind accessing device structures */
+ cancel_delayed_work_sync(&ge->sched.base.work_tdr);
+
+ xe_exec_queue_fini(q);
+ }

- xe_exec_queue_fini(q);
+ drm_dev_put(drm);
}

-static void guc_exec_queue_destroy_async(struct xe_exec_queue *q)
+static void __guc_exec_queue_destroy_async(struct work_struct *w)
{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
+ struct xe_guc_exec_queue *ge =
+ container_of(w, struct xe_guc_exec_queue, destroy_async);

+ guc_exec_queue_do_destroy(ge->q);
+}
+
+static void guc_exec_queue_destroy_async(struct xe_exec_queue *q)
+{
INIT_WORK(&q->guc->destroy_async, __guc_exec_queue_destroy_async);

/* We must block on kernel engines so slabs are empty on driver unload */
if (q->flags & EXEC_QUEUE_FLAG_PERMANENT || exec_queue_wedged(q))
- __guc_exec_queue_destroy_async(&q->guc->destroy_async);
+ guc_exec_queue_do_destroy(q);
else
- queue_work(xe->destroy_wq, &q->guc->destroy_async);
+ xe_destroy_wq_queue(&q->guc->destroy_async);
}

static void __guc_exec_queue_destroy(struct xe_guc *guc, struct xe_exec_queue *q)
@@ -1935,6 +1951,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
{
struct xe_gpu_scheduler *sched;
struct xe_guc *guc = exec_queue_to_guc(q);
+ struct drm_device *drm = &guc_to_xe(guc)->drm;
struct workqueue_struct *submit_wq = NULL;
struct xe_guc_exec_queue *ge;
long timeout;
@@ -1946,6 +1963,8 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
if (!ge)
return -ENOMEM;

+ drm_dev_get(drm);
+
q->guc = ge;
ge->q = q;
init_rcu_head(&ge->rcu);
@@ -1957,6 +1976,14 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
timeout = (q->vm && xe_vm_in_lr_mode(q->vm)) ? MAX_SCHEDULE_TIMEOUT :
msecs_to_jiffies(q->sched_props.job_timeout_ms);

+ err = alloc_guc_id(guc, q);
+ if (err)
+ goto err_free;
+
+ xe_exec_queue_assign_name(q, q->guc->id);
+
+ strscpy(ge->name, q->name, sizeof(ge->name));
+
/*
* Use primary queue's submit_wq for all secondary queues of a
* multi queue group. This serialization avoids any locking around
@@ -1971,30 +1998,23 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops,
submit_wq, xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES, 64,
timeout, guc_to_gt(guc)->ordered_wq, NULL,
- q->name, gt_to_xe(q->gt)->drm.dev);
+ ge->name, gt_to_xe(q->gt)->drm.dev);
if (err)
- goto err_free;
+ goto err_release_id;

sched = &ge->sched;
err = xe_sched_entity_init(&ge->entity, sched);
if (err)
goto err_sched;

- mutex_lock(&guc->submission_state.lock);
-
- err = alloc_guc_id(guc, q);
- if (err)
- goto err_entity;
-
q->entity = &ge->entity;

+ mutex_lock(&guc->submission_state.lock);
if (xe_guc_read_stopped(guc) || vf_recovery(guc))
xe_sched_stop(sched);
-
+ publish_guc_id(guc, q);
mutex_unlock(&guc->submission_state.lock);

- xe_exec_queue_assign_name(q, q->guc->id);
-
/*
* Maintain secondary queues of the multi queue group in a list
* for handling dependencies across the queues in the group.
@@ -2017,13 +2037,13 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)

return 0;

-err_entity:
- mutex_unlock(&guc->submission_state.lock);
- xe_sched_entity_fini(&ge->entity);
err_sched:
xe_sched_fini(&ge->sched);
+err_release_id:
+ release_guc_id(guc, q);
err_free:
kfree(ge);
+ drm_dev_put(drm);

return err;
}
diff --git a/drivers/gpu/drm/xe/xe_guc_types.h b/drivers/gpu/drm/xe/xe_guc_types.h
index c7b9642b41ba..31a2acb63ac3 100644
--- a/drivers/gpu/drm/xe/xe_guc_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_types.h
@@ -100,8 +100,6 @@ struct xe_guc {
* even initialized - before that not even the lock is valid
*/
bool initialized;
- /** @submission_state.fini_wq: submit fini wait queue */
- wait_queue_head_t fini_wq;
} submission_state;

/** @hwconfig: Hardware config state */
diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c
index 706783863d07..f05f23221c1b 100644
--- a/drivers/gpu/drm/xe/xe_i2c.c
+++ b/drivers/gpu/drm/xe/xe_i2c.c
@@ -95,18 +95,21 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c)
struct platform_device *pdev;
struct fwnode_handle *fwnode;
int ret;
+ u32 id;

fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL);
if (IS_ERR(fwnode))
return PTR_ERR(fwnode);

+ id = (pci_domain_nr(pci->bus) << 16) | pci_dev_id(pci);
+
/*
* Not using platform_device_register_full() here because we don't have
* a handle to the platform_device before it returns. xe_i2c_notifier()
* uses that handle, but it may be called before
* platform_device_register_full() is done.
*/
- pdev = platform_device_alloc(adapter_name, pci_dev_id(pci));
+ pdev = platform_device_alloc(adapter_name, id);
if (!pdev) {
ret = -ENOMEM;
goto err_fwnode_remove;
diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c
index a22413f892a0..5dd03d4cbf5b 100644
--- a/drivers/gpu/drm/xe/xe_migrate.c
+++ b/drivers/gpu/drm/xe/xe_migrate.c
@@ -1145,6 +1145,8 @@ static int emit_flush_invalidate(u32 *dw, int i, u32 flags)
* @tile: Tile whose migration context to be used.
* @q : Execution to be used along with migration context.
* @src_bo: The buffer object @src is currently bound to.
+ * @new_mem: The (not yet committed) destination resource @src_bo is being
+ * moved into; src_bo->ttm.resource is still the old resource.
* @read_write : Creates BB commands for CCS read/write.
*
* Creates batch buffer instructions to copy CCS metadata from CCS pool to
@@ -1156,12 +1158,13 @@ static int emit_flush_invalidate(u32 *dw, int i, u32 flags)
*/
int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
struct xe_bo *src_bo,
+ struct ttm_resource *new_mem,
enum xe_sriov_vf_ccs_rw_ctxs read_write)

{
bool src_is_pltt = read_write == XE_SRIOV_VF_CCS_READ_CTX;
bool dst_is_pltt = read_write == XE_SRIOV_VF_CCS_WRITE_CTX;
- struct ttm_resource *src = src_bo->ttm.resource;
+ struct ttm_resource *src = new_mem;
struct xe_migrate *m = tile->migrate;
struct xe_gt *gt = tile->primary_gt;
u32 batch_size, batch_size_allocated;
@@ -1289,6 +1292,7 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
* content.
* @src_bo: The buffer object @src is currently bound to.
* @read_write : Creates BB commands for CCS read/write.
+ * @bound: Device is bound
*
* Directly clearing the BB lacks atomicity and can lead to undefined
* behavior if the vCPU is halted mid-operation during the clearing
@@ -1301,7 +1305,8 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
* Returns: None.
*/
void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
- enum xe_sriov_vf_ccs_rw_ctxs read_write)
+ enum xe_sriov_vf_ccs_rw_ctxs read_write,
+ bool bound)
{
struct xe_mem_pool_node *bb = src_bo->bb_ccs[read_write];
struct xe_device *xe = xe_bo_device(src_bo);
@@ -1315,13 +1320,15 @@ void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
bb_pool = ctx->mem.ccs_bb_pool;

scoped_guard(mutex, xe_mem_pool_bo_swap_guard(bb_pool)) {
- xe_mem_pool_swap_shadow_locked(bb_pool);
+ if (bound) {
+ xe_mem_pool_swap_shadow_locked(bb_pool);

- cs = xe_mem_pool_node_cpu_addr(bb);
- memset(cs, MI_NOOP, bb->sa_node.size);
- xe_sriov_vf_ccs_rw_update_bb_addr(ctx);
+ cs = xe_mem_pool_node_cpu_addr(bb);
+ memset(cs, MI_NOOP, bb->sa_node.size);
+ xe_sriov_vf_ccs_rw_update_bb_addr(ctx);

- xe_mem_pool_sync_shadow_locked(bb);
+ xe_mem_pool_sync_shadow_locked(bb);
+ }
xe_mem_pool_free_node(bb);
src_bo->bb_ccs[read_write] = NULL;
}
diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h
index 169279d9d8c2..0bcc122c1c34 100644
--- a/drivers/gpu/drm/xe/xe_migrate.h
+++ b/drivers/gpu/drm/xe/xe_migrate.h
@@ -138,10 +138,12 @@ struct dma_fence *xe_migrate_resolve(struct xe_migrate *m,

int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
struct xe_bo *src_bo,
+ struct ttm_resource *new_mem,
enum xe_sriov_vf_ccs_rw_ctxs read_write);

void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
- enum xe_sriov_vf_ccs_rw_ctxs read_write);
+ enum xe_sriov_vf_ccs_rw_ctxs read_write,
+ bool bound);

struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate);
struct xe_exec_queue *xe_migrate_exec_queue(struct xe_migrate *migrate);
diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c
index 4cb578182912..99347f216ec8 100644
--- a/drivers/gpu/drm/xe/xe_module.c
+++ b/drivers/gpu/drm/xe/xe_module.c
@@ -7,6 +7,7 @@

#include <linux/init.h>
#include <linux/module.h>
+#include <linux/workqueue.h>

#include <drm/drm_module.h>

@@ -91,6 +92,50 @@ static int xe_check_nomodeset(void)
return 0;
}

+static struct workqueue_struct *xe_destroy_wq;
+
+static int __init xe_destroy_wq_module_init(void)
+{
+ xe_destroy_wq = alloc_workqueue("xe-guc-destroy-wq", WQ_UNBOUND, 0);
+ if (!xe_destroy_wq)
+ return -ENOMEM;
+ return 0;
+}
+
+static void xe_destroy_wq_module_exit(void)
+{
+ if (xe_destroy_wq)
+ destroy_workqueue(xe_destroy_wq);
+ xe_destroy_wq = NULL;
+}
+
+/**
+ * xe_destroy_wq_queue() - Queue work on the destroy workqueue
+ * @work: work item to queue
+ *
+ * The destroy workqueue has module lifetime and is used for GuC exec queue
+ * teardown that can outlive a single xe_device. SVM pagemap destroy uses the
+ * per-device xe->destroy_wq instead.
+ *
+ * Return: %true if @work was queued, %false if it was already pending.
+ */
+bool xe_destroy_wq_queue(struct work_struct *work)
+{
+ return queue_work(xe_destroy_wq, work);
+}
+
+/**
+ * xe_destroy_wq_flush() - Flush the destroy workqueue
+ *
+ * Drains all pending destroy work. Called from PCI remove to ensure
+ * teardown ordering before the device is destroyed.
+ */
+void xe_destroy_wq_flush(void)
+{
+ if (xe_destroy_wq)
+ flush_workqueue(xe_destroy_wq);
+}
+
struct init_funcs {
int (*init)(void);
void (*exit)(void);
@@ -112,6 +157,10 @@ static const struct init_funcs init_funcs[] = {
.init = xe_sched_job_module_init,
.exit = xe_sched_job_module_exit,
},
+ {
+ .init = xe_destroy_wq_module_init,
+ .exit = xe_destroy_wq_module_exit,
+ },
{
.init = xe_register_pci_driver,
.exit = xe_unregister_pci_driver,
diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h
index 79cb9639c0f3..e8e54f701cf3 100644
--- a/drivers/gpu/drm/xe/xe_module.h
+++ b/drivers/gpu/drm/xe/xe_module.h
@@ -8,6 +8,8 @@

#include <linux/types.h>

+struct work_struct;
+
/* Module modprobe variables */
struct xe_modparam {
bool force_execlist;
@@ -27,5 +29,8 @@ struct xe_modparam {

extern struct xe_modparam xe_modparam;

+bool xe_destroy_wq_queue(struct work_struct *work);
+void xe_destroy_wq_flush(void);
+
#endif

diff --git a/drivers/gpu/drm/xe/xe_nvm.c b/drivers/gpu/drm/xe/xe_nvm.c
index 33487e91f366..1ea67eaeae24 100644
--- a/drivers/gpu/drm/xe/xe_nvm.c
+++ b/drivers/gpu/drm/xe/xe_nvm.c
@@ -60,35 +60,40 @@ static bool xe_nvm_writable_override(struct xe_device *xe)
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
bool writable_override;
struct xe_reg reg;
- u32 test_bit;
+ u32 test_bit, test_val;

switch (xe->info.platform) {
case XE_CRESCENTISLAND:
reg = PCODE_SCRATCH(0);
test_bit = FDO_MODE;
+ test_val = FDO_MODE;
break;
case XE_BATTLEMAGE:
reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE);
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
+ test_val = 0;
break;
case XE_PVC:
reg = HECI_FWSTS2(PVC_GSC_HECI2_BASE);
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
+ test_val = 0;
break;
case XE_DG2:
reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE);
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
+ test_val = 0;
break;
case XE_DG1:
reg = HECI_FWSTS2(DG1_GSC_HECI2_BASE);
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
+ test_val = 0;
break;
default:
drm_err(&xe->drm, "Unknown platform\n");
return true;
}

- writable_override = !(xe_mmio_read32(mmio, reg) & test_bit);
+ writable_override = (xe_mmio_read32(mmio, reg) & test_bit) == test_val;
if (writable_override)
drm_info(&xe->drm, "NVM access overridden by jumper\n");
return writable_override;
diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
index c2ecd27ec770..ad4955fe5ade 100644
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@ -1029,6 +1029,12 @@ static void xe_pci_remove(struct pci_dev *pdev)
return;

xe_device_remove(xe);
+
+ /*
+ * Preserve remove-time flush after moving destroy work to module
+ * lifetime.
+ */
+ xe_destroy_wq_flush();
xe_pm_fini(xe);
}

diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
index c3a69103b9bb..cf371c0d7b2b 100644
--- a/drivers/gpu/drm/xe/xe_pt.c
+++ b/drivers/gpu/drm/xe/xe_pt.c
@@ -1025,12 +1025,22 @@ xe_vm_populate_pgtable(struct xe_migrate_pt_update *pt_update, struct xe_tile *t
u64 *ptr = data;
u32 i;

+ /*
+ * @qword_ofs is the absolute entry offset within the page table, while
+ * @ptes is indexed relative to @update->ofs (its first entry). The GPU
+ * path (write_pgtable) splits a single update into MAX_PTE_PER_SDI-sized
+ * chunks, calling this with an advancing @qword_ofs but a fresh @data
+ * pointer per chunk, so translate back into a @ptes index rather than
+ * assuming the chunk starts at ptes[0].
+ */
for (i = 0; i < num_qwords; i++) {
+ u32 idx = qword_ofs - update->ofs + i;
+
if (map)
xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) *
- sizeof(u64), u64, ptes[i].pte);
+ sizeof(u64), u64, ptes[idx].pte);
else
- ptr[i] = ptes[i].pte;
+ ptr[i] = ptes[idx].pte;
}
}

@@ -1643,23 +1653,21 @@ static bool xe_pt_check_kill(u64 addr, u64 next, unsigned int level,
return false;
}

-/* page_size = 2^(reclamation_size + XE_PTE_SHIFT) */
-#define COMPUTE_RECLAIM_ADDRESS_MASK(page_size) \
-({ \
- BUILD_BUG_ON(!__builtin_constant_p(page_size)); \
- ilog2(page_size) - XE_PTE_SHIFT; \
-})
-
static int generate_reclaim_entry(struct xe_tile *tile,
struct xe_page_reclaim_list *prl,
u64 pte, struct xe_pt *xe_child)
{
struct xe_gt *gt = tile->primary_gt;
struct xe_guc_page_reclaim_entry *reclaim_entries = prl->entries;
- u64 phys_addr = pte & XE_PTE_ADDR_MASK;
+ bool is_2m = xe_child->level == 1 && (pte & XE_PDE_PS_2M);
+ bool is_64k = xe_child->level == 0 && ((pte & XE_PTE_PS64) || xe_child->is_compact);
+ u32 page_shift = is_2m ? ilog2(SZ_2M) : is_64k ? ilog2(SZ_64K) : ilog2(SZ_4K);
+ /* Physical address bits start at page shift: 2M->[51:21], 64K->[51:16], 4K->[51:12] */
+ u64 phys_addr = pte & XE_PAGE_ADDR_MASK(page_shift);
+ /* Page address is relative to 4K page regardless of entry level */
u64 phys_page = phys_addr >> XE_PTE_SHIFT;
int num_entries = prl->num_entries;
- u32 reclamation_size;
+ u32 reclamation_size = page_shift - XE_PTE_SHIFT;

xe_tile_assert(tile, xe_child->level <= MAX_HUGEPTE_LEVEL);
xe_tile_assert(tile, reclaim_entries);
@@ -1674,18 +1682,12 @@ static int generate_reclaim_entry(struct xe_tile *tile,
* Page size is computed as 2^(reclamation_size + XE_PTE_SHIFT) bytes.
* Only 4K, 64K (level 0), and 2M pages are supported by hardware for page reclaim
*/
- if (xe_child->level == 0 && !(pte & XE_PTE_PS64)) {
- xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_4K_ENTRY_COUNT, 1);
- reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_4K); /* reclamation_size = 0 */
- xe_tile_assert(tile, phys_addr % SZ_4K == 0);
- } else if (xe_child->level == 0) {
- xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_64K_ENTRY_COUNT, 1);
- reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_64K); /* reclamation_size = 4 */
- xe_tile_assert(tile, phys_addr % SZ_64K == 0);
- } else if (xe_child->level == 1 && pte & XE_PDE_PS_2M) {
+ if (is_2m) {
xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_2M_ENTRY_COUNT, 1);
- reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_2M); /* reclamation_size = 9 */
- xe_tile_assert(tile, phys_addr % SZ_2M == 0);
+ } else if (is_64k) {
+ xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_64K_ENTRY_COUNT, 1);
+ } else if (xe_child->level == 0) {
+ xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_4K_ENTRY_COUNT, 1);
} else {
xe_page_reclaim_list_abort(tile->primary_gt, prl,
"unsupported PTE level=%u pte=%#llx",
@@ -1706,6 +1708,48 @@ static int generate_reclaim_entry(struct xe_tile *tile,
return 0;
}

+static int add_pte_to_prl(struct xe_tile *tile, struct xe_page_reclaim_list *prl,
+ struct xe_pt *xe_child, u64 pte, u64 addr)
+{
+ /*
+ * In rare scenarios, pte may not be written yet due to racy conditions.
+ * In such cases, invalidate the PRL and fallback to full PPC invalidation.
+ */
+ if (!pte) {
+ xe_page_reclaim_list_abort(tile->primary_gt, prl,
+ "found zero pte at addr=%#llx", addr);
+ return -EINVAL;
+ }
+
+ /* Ensure it is a defined page */
+ xe_tile_assert(tile, xe_child->level == 0 ||
+ (pte & (XE_PDE_PS_2M | XE_PDPE_PS_1G)));
+
+ /* Account for NULL terminated entry on end (-1) */
+ if (prl->num_entries >= XE_PAGE_RECLAIM_MAX_ENTRIES - 1) {
+ xe_page_reclaim_list_abort(tile->primary_gt, prl,
+ "overflow while adding pte=%#llx", pte);
+ return -ENOSPC;
+ }
+
+ return generate_reclaim_entry(tile, prl, pte, xe_child);
+}
+
+static bool add_compact_pt_prl(struct xe_tile *tile, struct xe_page_reclaim_list *prl,
+ struct xe_device *xe, struct xe_pt *compact_pt, u64 addr)
+{
+ struct iosys_map *map = &compact_pt->bo->vmap;
+
+ for (pgoff_t i = 0; i < SZ_2M / SZ_64K && xe_page_reclaim_list_valid(prl); i++) {
+ u64 pte = xe_map_rd(xe, map, i * sizeof(u64), u64);
+
+ if (add_pte_to_prl(tile, prl, compact_pt, pte, addr + i * SZ_64K))
+ break;
+ }
+
+ return xe_page_reclaim_list_valid(prl);
+}
+
static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset,
unsigned int level, u64 addr, u64 next,
struct xe_ptw **child,
@@ -1715,21 +1759,22 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset,
struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base);
struct xe_pt_stage_unbind_walk *xe_walk =
container_of(walk, typeof(*xe_walk), base);
- struct xe_device *xe = tile_to_xe(xe_walk->tile);
+ struct xe_page_reclaim_list *prl = xe_walk->prl;
+ struct xe_tile *tile = xe_walk->tile;
+ struct xe_device *xe = tile_to_xe(tile);
pgoff_t first = xe_pt_offset(addr, xe_child->level, walk);
bool killed;

XE_WARN_ON(!*child);
XE_WARN_ON(!level);
/* Check for leaf node */
- if (xe_walk->prl && xe_page_reclaim_list_valid(xe_walk->prl) &&
+ if (prl && xe_page_reclaim_list_valid(prl) &&
xe_child->level <= MAX_HUGEPTE_LEVEL) {
struct iosys_map *leaf_map = &xe_child->bo->vmap;
pgoff_t count = xe_pt_num_entries(addr, next, xe_child->level, walk);

for (pgoff_t i = 0; i < count; i++) {
u64 pte;
- int ret;

/*
* If not a leaf pt, skip unless non-leaf pt is interleaved between
@@ -1739,10 +1784,23 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset,
u64 pt_size = 1ULL << walk->shifts[xe_child->level];
bool edge_pt = (i == 0 && !IS_ALIGNED(addr, pt_size)) ||
(i == count - 1 && !IS_ALIGNED(next, pt_size));
-
- if (!edge_pt) {
- xe_page_reclaim_list_abort(xe_walk->tile->primary_gt,
- xe_walk->prl,
+ struct xe_pt *child_pt =
+ container_of(xe_child->base.children[first + i],
+ struct xe_pt, base);
+
+ /* Compact PTs always fill a full 2M-aligned slot, never an edge. */
+ XE_WARN_ON(child_pt->is_compact && edge_pt);
+ if (edge_pt)
+ continue;
+
+ /* Walker never descends into compact PTs, descend now */
+ if (child_pt->is_compact) {
+ if (!add_compact_pt_prl(tile, prl, xe, child_pt,
+ addr + (u64)i * pt_size))
+ break;
+ } else {
+ xe_page_reclaim_list_abort(tile->primary_gt,
+ prl,
"PT is skipped by walk at level=%u offset=%lu",
xe_child->level, first + i);
break;
@@ -1752,37 +1810,12 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset,

pte = xe_map_rd(xe, leaf_map, (first + i) * sizeof(u64), u64);

- /*
- * In rare scenarios, pte may not be written yet due to racy conditions.
- * In such cases, invalidate the PRL and fallback to full PPC invalidation.
- */
- if (!pte) {
- xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl,
- "found zero pte at addr=%#llx", addr);
+ if (add_pte_to_prl(tile, prl, xe_child, pte, addr))
break;
- }
-
- /* Ensure it is a defined page */
- xe_tile_assert(xe_walk->tile, xe_child->level == 0 ||
- (pte & (XE_PDE_PS_2M | XE_PDPE_PS_1G)));

/* An entry should be added for 64KB but contigious 4K have XE_PTE_PS64 */
if (pte & XE_PTE_PS64)
i += 15; /* Skip other 15 consecutive 4K pages in the 64K page */
-
- /* Account for NULL terminated entry on end (-1) */
- if (xe_walk->prl->num_entries < XE_PAGE_RECLAIM_MAX_ENTRIES - 1) {
- ret = generate_reclaim_entry(xe_walk->tile, xe_walk->prl,
- pte, xe_child);
- if (ret)
- break;
- } else {
- /* overflow, mark as invalid */
- xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl,
- "overflow while adding pte=%#llx",
- pte);
- break;
- }
}
}

@@ -1792,7 +1825,7 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset,
* Verify if any PTE are potentially dropped at non-leaf levels, either from being
* killed or the page walk covers the region.
*/
- if (xe_walk->prl && xe_page_reclaim_list_valid(xe_walk->prl) &&
+ if (prl && xe_page_reclaim_list_valid(prl) &&
xe_child->level > MAX_HUGEPTE_LEVEL && xe_child->num_live) {
bool covered = xe_pt_covers(addr, next, xe_child->level, &xe_walk->base);

@@ -1801,7 +1834,7 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset,
* we need to invalidate the PRL.
*/
if (killed || covered)
- xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl,
+ xe_page_reclaim_list_abort(tile->primary_gt, prl,
"kill at level=%u addr=%#llx next=%#llx num_live=%u",
level, addr, next, xe_child->num_live);
}
@@ -2331,8 +2364,11 @@ static void
xe_pt_update_ops_init(struct xe_vm_pgtable_update_ops *pt_update_ops)
{
init_llist_head(&pt_update_ops->deferred);
+ pt_update_ops->current_op = 0;
pt_update_ops->start = ~0x0ull;
pt_update_ops->last = 0x0ull;
+ pt_update_ops->needs_svm_lock = false;
+ pt_update_ops->needs_invalidation = false;
xe_page_reclaim_list_init(&pt_update_ops->prl);
}

diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c
index 8cc313182968..506501d77f38 100644
--- a/drivers/gpu/drm/xe/xe_reg_whitelist.c
+++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c
@@ -90,10 +90,12 @@ static const struct xe_rtp_entry_sr register_whitelist[] = {
RING_FORCE_TO_NONPRIV_ACCESS_RW))
},

+#define WHITELIST_DENY(r, f) WHITELIST(r, (f) | RING_FORCE_TO_NONPRIV_DENY)
+
#define WHITELIST_OA_MMIO_TRG(trg, status, head) \
- WHITELIST(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \
- WHITELIST(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \
- WHITELIST(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4)
+ WHITELIST_DENY(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \
+ WHITELIST_DENY(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \
+ WHITELIST_DENY(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4)

#define WHITELIST_OAG_MMIO_TRG \
WHITELIST_OA_MMIO_TRG(OAG_MMIOTRIGGER, OAG_OASTATUS, OAG_OAHEADPTR)
@@ -110,7 +112,7 @@ static const struct xe_rtp_entry_sr register_whitelist[] = {
OAM_HEAD_POINTER(XE_OAM_SCMI_1_BASE_ADJ))

#define WHITELIST_OA_MERT_MMIO_TRG \
- WHITELIST_OA_MMIO_TRG(OAMERT_MMIO_TRG, OAMERT_STATUS, OAMERT_HEAD_POINTER)
+ WHITELIST_OA_MMIO_TRG(OAMERT_MMIO_TRG, OAMERT_STATUS, OAMERT_TAIL_POINTER)

{ XE_RTP_NAME("oag_mmio_trg_rcs"),
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED),
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
index 09b99fb2608b..a8c831fbee3b 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
@@ -3,6 +3,8 @@
* Copyright © 2025 Intel Corporation
*/

+#include <drm/drm_drv.h>
+
#include "instructions/xe_mi_commands.h"
#include "instructions/xe_gpu_commands.h"
#include "xe_bb.h"
@@ -404,6 +406,8 @@ void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx)
/**
* xe_sriov_vf_ccs_attach_bo - Insert CCS read write commands in the BO.
* @bo: the &buffer object to which batch buffer commands will be added.
+ * @new_mem: the (not yet committed) destination resource @bo is being moved
+ * into; bo->ttm.resource is still the old resource at this point.
*
* This function shall be called only by VF. It inserts the PTEs and copy
* command instructions in the BO by calling xe_migrate_ccs_rw_copy()
@@ -411,7 +415,7 @@ void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx)
*
* Returns: 0 if successful, negative error code on failure.
*/
-int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo)
+int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo, struct ttm_resource *new_mem)
{
struct xe_device *xe = xe_bo_device(bo);
enum xe_sriov_vf_ccs_rw_ctxs ctx_id;
@@ -430,7 +434,21 @@ int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo)
xe_assert(xe, !bb);

ctx = &xe->sriov.vf.ccs.contexts[ctx_id];
- err = xe_migrate_ccs_rw_copy(tile, ctx->mig_q, bo, ctx_id);
+ err = xe_migrate_ccs_rw_copy(tile, ctx->mig_q, bo, new_mem, ctx_id);
+ if (err)
+ goto err_unwind;
+ }
+ return 0;
+
+err_unwind:
+ /*
+ * Clean up any contexts already attached. Can't reuse
+ * xe_sriov_vf_ccs_detach_bo() here as it requires both contexts
+ * attached before cleaning up either one.
+ */
+ for_each_ccs_rw_ctx(ctx_id) {
+ if (bo->bb_ccs[ctx_id])
+ xe_migrate_ccs_rw_copy_clear(bo, ctx_id, true);
}
return err;
}
@@ -450,19 +468,27 @@ int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo)
struct xe_device *xe = xe_bo_device(bo);
enum xe_sriov_vf_ccs_rw_ctxs ctx_id;
struct xe_mem_pool_node *bb;
+ bool bound;
+ int idx;

xe_assert(xe, IS_VF_CCS_READY(xe));

if (!xe_bo_has_valid_ccs_bb(bo))
return 0;

+ bound = drm_dev_enter(&xe->drm, &idx);
+
for_each_ccs_rw_ctx(ctx_id) {
bb = bo->bb_ccs[ctx_id];
if (!bb)
continue;

- xe_migrate_ccs_rw_copy_clear(bo, ctx_id);
+ xe_migrate_ccs_rw_copy_clear(bo, ctx_id, bound);
}
+
+ if (bound)
+ drm_dev_exit(idx);
+
return 0;
}

diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
index 00e58b36c510..e1034d852104 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
@@ -11,11 +11,12 @@
#include "xe_sriov_vf_ccs_types.h"

struct drm_printer;
+struct ttm_resource;
struct xe_device;
struct xe_bo;

int xe_sriov_vf_ccs_init(struct xe_device *xe);
-int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo);
+int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo, struct ttm_resource *new_mem);
int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo);
int xe_sriov_vf_ccs_register_context(struct xe_device *xe);
void xe_sriov_vf_ccs_rebase(struct xe_device *xe);
diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
index 5933b2b6392b..1c0df523a436 100644
--- a/drivers/gpu/drm/xe/xe_svm.c
+++ b/drivers/gpu/drm/xe/xe_svm.c
@@ -1255,10 +1255,8 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,

xe_svm_range_fault_count_stats_incr(gt, range);

- if (ctx.devmem_only && !range->base.pages.flags.migrate_devmem) {
- err = -EACCES;
- goto out;
- }
+ if (ctx.devmem_only && !range->base.pages.flags.migrate_devmem)
+ return -EACCES;

if (xe_svm_range_is_valid(range, tile, ctx.devmem_only, dpagemap)) {
xe_svm_range_valid_fault_count_stats_incr(gt, range);
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index ab6cc1f0a789..70b71ab80272 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -1810,10 +1810,10 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
return ERR_PTR(err);

err_svm_fini:
- if (flags & XE_VM_FLAG_FAULT_MODE) {
- vm->size = 0; /* close the vm */
- xe_svm_fini(vm);
- }
+ vm->size = 0; /* close the vm */
+ if (flags & XE_VM_FLAG_FAULT_MODE)
+ xe_svm_close(vm);
+ xe_svm_fini(vm);
err_no_resv:
mutex_destroy(&vm->snap_mutex);
for_each_tile(tile, xe, id)
@@ -3256,11 +3256,26 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm,
.request_decompress = false,
.check_purged = true,
});
- if (!err && !xe_vma_has_no_bo(vma))
- err = xe_bo_migrate(xe_vma_bo(vma),
- region_to_mem_type[region],
- NULL,
- exec);
+ if (!err && !xe_vma_has_no_bo(vma)) {
+ struct xe_bo *bo = xe_vma_bo(vma);
+ u32 mem_type;
+
+ if (region == DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC) {
+ unsigned int i;
+
+ mem_type = XE_PL_TT;
+ for (i = 0; i < bo->placement.num_placement; i++) {
+ if (mem_type_is_vram(bo->placements[i].mem_type)) {
+ mem_type = bo->placements[i].mem_type;
+ break;
+ }
+ }
+ } else {
+ mem_type = region_to_mem_type[region];
+ }
+
+ err = xe_bo_migrate(bo, mem_type, NULL, exec);
+ }
break;
}
default:
diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c
index 246fe1843142..0474768a38aa 100644
--- a/drivers/gpu/drm/xe/xe_vm_madvise.c
+++ b/drivers/gpu/drm/xe/xe_vm_madvise.c
@@ -332,6 +332,20 @@ static int xe_vm_invalidate_madvise_range(struct xe_vm *vm, u64 start, u64 end)
return err;
}

+/**
+ * madvise_range_needs_invalidation() - Check whether madvise needs invalidation
+ * @args: madvise ioctl arguments
+ *
+ * Purgeable state updates only touch VMA/BO metadata. PTEs stay valid and are
+ * zapped only if the BO is later purged.
+ *
+ * Return: true when the update needs PTE invalidation.
+ */
+static bool madvise_range_needs_invalidation(const struct drm_xe_madvise *args)
+{
+ return args->type != DRM_XE_VMA_ATTR_PURGEABLE_STATE;
+}
+
static bool madvise_args_are_sane(struct xe_device *xe, const struct drm_xe_madvise *args)
{
if (XE_IOCTL_DBG(xe, !args))
@@ -708,8 +722,9 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args,
&details);

- err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr,
- madvise_range.addr + args->range);
+ if (madvise_range_needs_invalidation(args))
+ err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr,
+ madvise_range.addr + args->range);

if (madvise_range.has_svm_userptr_vmas)
xe_svm_notifier_unlock(vm);
diff --git a/drivers/gpu/drm/xe/xe_wopcm.c b/drivers/gpu/drm/xe/xe_wopcm.c
index 900daf1d1b1b..fe65ed246775 100644
--- a/drivers/gpu/drm/xe/xe_wopcm.c
+++ b/drivers/gpu/drm/xe/xe_wopcm.c
@@ -49,9 +49,9 @@
*/

/* Default WOPCM size is 2MB from Gen11, 1MB on previous platforms */
-/* FIXME: Larger size require for 2 tile PVC, do a proper probe sooner or later */
+/* FIXME: Larger size require for some platforms, do a proper probe sooner or later */
#define DGFX_WOPCM_SIZE SZ_4M
-/* FIXME: Larger size require for MTL, do a proper probe sooner or later */
+#define LNL_WOPCM_SIZE SZ_8M
#define MTL_WOPCM_SIZE SZ_4M
#define WOPCM_SIZE SZ_2M

@@ -179,9 +179,14 @@ static int __wopcm_init_regs(struct xe_device *xe, struct xe_gt *gt,

u32 xe_wopcm_size(struct xe_device *xe)
{
- return IS_DGFX(xe) ? DGFX_WOPCM_SIZE :
- xe->info.platform == XE_METEORLAKE ? MTL_WOPCM_SIZE :
- WOPCM_SIZE;
+ if (xe->info.platform >= XE_LUNARLAKE)
+ return LNL_WOPCM_SIZE;
+ else if (IS_DGFX(xe))
+ return DGFX_WOPCM_SIZE;
+ else if (xe->info.platform == XE_METEORLAKE)
+ return MTL_WOPCM_SIZE;
+ else
+ return WOPCM_SIZE;
}

static u32 max_wopcm_size(struct xe_device *xe)
diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
index c273a4476f23..a8cddb3425b4 100644
--- a/drivers/gpu/host1x/bus.c
+++ b/drivers/gpu/host1x/bus.c
@@ -1012,10 +1012,10 @@ void host1x_bo_clear_cached_mappings(struct host1x_bo *bo)
if (WARN_ON(!cache))
continue;

- mutex_lock(&mapping->cache->lock);
+ mutex_lock(&cache->lock);
WARN_ON(kref_read(&mapping->ref) != 1);
__host1x_bo_unpin(&mapping->ref);
- mutex_unlock(&mapping->cache->lock);
+ mutex_unlock(&cache->lock);
}
}
EXPORT_SYMBOL(host1x_bo_clear_cached_mappings);
diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c
index f0b17e59827f..c2df631ae93a 100644
--- a/drivers/hwmon/ad7414.c
+++ b/drivers/hwmon/ad7414.c
@@ -205,8 +205,8 @@ static int ad7414_probe(struct i2c_client *client)
}

static const struct i2c_device_id ad7414_id[] = {
- { "ad7414" },
- {}
+ { .name = "ad7414" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ad7414_id);

diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c
index 7a132accdf8a..a0c9cf5e12cc 100644
--- a/drivers/hwmon/ad7418.c
+++ b/drivers/hwmon/ad7418.c
@@ -281,9 +281,9 @@ static int ad7418_probe(struct i2c_client *client)
}

static const struct i2c_device_id ad7418_id[] = {
- { "ad7416", ad7416 },
- { "ad7417", ad7417 },
- { "ad7418", ad7418 },
+ { .name = "ad7416", .driver_data = ad7416 },
+ { .name = "ad7417", .driver_data = ad7417 },
+ { .name = "ad7418", .driver_data = ad7418 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad7418_id);
diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c
index 5e805d4ee76a..e45adc0b84d1 100644
--- a/drivers/hwmon/adc128d818.c
+++ b/drivers/hwmon/adc128d818.c
@@ -480,7 +480,7 @@ static int adc128_probe(struct i2c_client *client)
}

static const struct i2c_device_id adc128_id[] = {
- { "adc128d818" },
+ { .name = "adc128d818" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adc128_id);
diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c
index 389382d54752..ccac7ba601e9 100644
--- a/drivers/hwmon/adm1025.c
+++ b/drivers/hwmon/adm1025.c
@@ -548,8 +548,8 @@ static int adm1025_probe(struct i2c_client *client)
}

static const struct i2c_device_id adm1025_id[] = {
- { "adm1025", adm1025 },
- { "ne1619", ne1619 },
+ { .name = "adm1025", .driver_data = adm1025 },
+ { .name = "ne1619", .driver_data = ne1619 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adm1025_id);
diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c
index c38c932e5d2a..3ad82c01f81d 100644
--- a/drivers/hwmon/adm1026.c
+++ b/drivers/hwmon/adm1026.c
@@ -1857,7 +1857,7 @@ static int adm1026_probe(struct i2c_client *client)
}

static const struct i2c_device_id adm1026_id[] = {
- { "adm1026" },
+ { .name = "adm1026" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adm1026_id);
diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c
index 71eea8ae51b9..6cb0a238e059 100644
--- a/drivers/hwmon/adm1029.c
+++ b/drivers/hwmon/adm1029.c
@@ -382,7 +382,7 @@ static int adm1029_probe(struct i2c_client *client)
}

static const struct i2c_device_id adm1029_id[] = {
- { "adm1029" },
+ { .name = "adm1029" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adm1029_id);
diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c
index 343118532cdb..24d0d4146c87 100644
--- a/drivers/hwmon/adm1031.c
+++ b/drivers/hwmon/adm1031.c
@@ -1055,8 +1055,8 @@ static int adm1031_probe(struct i2c_client *client)
}

static const struct i2c_device_id adm1031_id[] = {
- { "adm1030", adm1030 },
- { "adm1031", adm1031 },
+ { .name = "adm1030", .driver_data = adm1030 },
+ { .name = "adm1031", .driver_data = adm1031 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adm1031_id);
diff --git a/drivers/hwmon/adm1177.c b/drivers/hwmon/adm1177.c
index 7888afe8dafd..dc4d8a214d1b 100644
--- a/drivers/hwmon/adm1177.c
+++ b/drivers/hwmon/adm1177.c
@@ -246,8 +246,8 @@ static int adm1177_probe(struct i2c_client *client)
}

static const struct i2c_device_id adm1177_id[] = {
- {"adm1177"},
- {}
+ { .name = "adm1177" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, adm1177_id);

diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c
index 86f6044b5bd0..586650e8d043 100644
--- a/drivers/hwmon/adm9240.c
+++ b/drivers/hwmon/adm9240.c
@@ -794,9 +794,9 @@ static int adm9240_probe(struct i2c_client *client)
}

static const struct i2c_device_id adm9240_id[] = {
- { "adm9240", adm9240 },
- { "ds1780", ds1780 },
- { "lm81", lm81 },
+ { .name = "adm9240", .driver_data = adm9240 },
+ { .name = "ds1780", .driver_data = ds1780 },
+ { .name = "lm81", .driver_data = lm81 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adm9240_id);
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index 7f43565ca284..149cfcec78dc 100644
--- a/drivers/hwmon/ads7828.c
+++ b/drivers/hwmon/ads7828.c
@@ -176,8 +176,8 @@ static int ads7828_probe(struct i2c_client *client)
}

static const struct i2c_device_id ads7828_device_ids[] = {
- { "ads7828", ads7828 },
- { "ads7830", ads7830 },
+ { .name = "ads7828", .driver_data = ads7828 },
+ { .name = "ads7830", .driver_data = ads7830 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ads7828_device_ids);
diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index 73b196a78f3a..0aa7ce0a04be 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -89,10 +89,10 @@ static int adt7410_i2c_probe(struct i2c_client *client)
}

static const struct i2c_device_id adt7410_ids[] = {
- { "adt7410" },
- { "adt7420" },
- { "adt7422" },
- {}
+ { .name = "adt7410" },
+ { .name = "adt7420" },
+ { .name = "adt7422" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, adt7410_ids);

diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c
index b9991a69e6c6..5dce2a701725 100644
--- a/drivers/hwmon/adt7411.c
+++ b/drivers/hwmon/adt7411.c
@@ -670,7 +670,7 @@ static int adt7411_probe(struct i2c_client *client)
}

static const struct i2c_device_id adt7411_id[] = {
- { "adt7411" },
+ { .name = "adt7411" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adt7411_id);
diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c
index 174dfee47f7a..5101d30ed9c6 100644
--- a/drivers/hwmon/adt7462.c
+++ b/drivers/hwmon/adt7462.c
@@ -1809,7 +1809,7 @@ static int adt7462_probe(struct i2c_client *client)
}

static const struct i2c_device_id adt7462_id[] = {
- { "adt7462" },
+ { .name = "adt7462" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adt7462_id);
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index dbee6926fa05..664349756dc2 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -1296,7 +1296,7 @@ static void adt7470_remove(struct i2c_client *client)
}

static const struct i2c_device_id adt7470_id[] = {
- { "adt7470" },
+ { .name = "adt7470" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adt7470_id);
diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c
index 8cefa14e1633..7fb96f5395fa 100644
--- a/drivers/hwmon/adt7475.c
+++ b/drivers/hwmon/adt7475.c
@@ -165,10 +165,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
enum chips { adt7473, adt7475, adt7476, adt7490 };

static const struct i2c_device_id adt7475_id[] = {
- { "adt7473", adt7473 },
- { "adt7475", adt7475 },
- { "adt7476", adt7476 },
- { "adt7490", adt7490 },
+ { .name = "adt7473", .driver_data = adt7473 },
+ { .name = "adt7475", .driver_data = adt7475 },
+ { .name = "adt7476", .driver_data = adt7476 },
+ { .name = "adt7490", .driver_data = adt7490 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adt7475_id);
diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c
index 66955395d058..6c263cb57766 100644
--- a/drivers/hwmon/aht10.c
+++ b/drivers/hwmon/aht10.c
@@ -55,10 +55,10 @@
enum aht10_variant { aht10, aht20, dht20};

static const struct i2c_device_id aht10_id[] = {
- { "aht10", aht10 },
- { "aht20", aht20 },
- { "dht20", dht20 },
- { },
+ { .name = "aht10", .driver_data = aht10 },
+ { .name = "aht20", .driver_data = aht20 },
+ { .name = "dht20", .driver_data = dht20 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, aht10_id);

diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c
index d5f864b360b0..bbc0542a7d38 100644
--- a/drivers/hwmon/amc6821.c
+++ b/drivers/hwmon/amc6821.c
@@ -1083,7 +1083,7 @@ static int amc6821_probe(struct i2c_client *client)
}

static const struct i2c_device_id amc6821_id[] = {
- { "amc6821" },
+ { .name = "amc6821" },
{ }
};

diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c
index 14e7737866c2..5f5c94505166 100644
--- a/drivers/hwmon/asb100.c
+++ b/drivers/hwmon/asb100.c
@@ -213,7 +213,7 @@ static struct asb100_data *asb100_update_device(struct device *dev);
static void asb100_init_client(struct i2c_client *client);

static const struct i2c_device_id asb100_id[] = {
- { "asb100" },
+ { .name = "asb100" },
{ }
};
MODULE_DEVICE_TABLE(i2c, asb100_id);
diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c
index 87e186700849..e8145bb13ab3 100644
--- a/drivers/hwmon/asc7621.c
+++ b/drivers/hwmon/asc7621.c
@@ -1179,9 +1179,9 @@ static void asc7621_remove(struct i2c_client *client)
}

static const struct i2c_device_id asc7621_id[] = {
- {"asc7621", asc7621},
- {"asc7621a", asc7621a},
- {},
+ { .name = "asc7621", .driver_data = asc7621 },
+ { .name = "asc7621a", .driver_data = asc7621a },
+ { }
};

MODULE_DEVICE_TABLE(i2c, asc7621_id);
diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c
index b5d97a27f80d..7c281c6005ec 100644
--- a/drivers/hwmon/asus-ec-sensors.c
+++ b/drivers/hwmon/asus-ec-sensors.c
@@ -974,7 +974,7 @@ struct ec_sensors_data {
/* sorted list of unique register banks */
u8 banks[ASUS_EC_MAX_BANK + 1];
/* in jiffies */
- unsigned long last_updated;
+ u64 next_update;
struct lock_data lock_data;
/* number of board EC sensors */
u8 nr_sensors;
@@ -1154,7 +1154,7 @@ static int asus_ec_block_read(const struct device *dev,
}
for (ireg = 0; ireg < ec->nr_registers; ireg++) {
reg_bank = register_bank(ec->registers[ireg]);
- if (reg_bank < bank) {
+ if (reg_bank != bank) {
continue;
}
ec_read(register_index(ec->registers[ireg]),
@@ -1243,13 +1243,12 @@ static int get_cached_value_or_update(const struct device *dev,
int sensor_index,
struct ec_sensors_data *state, s32 *value)
{
- if (time_after(jiffies, state->last_updated + HZ)) {
+ if (time_after64(get_jiffies_64(), state->next_update)) {
if (update_ec_sensors(dev, state)) {
dev_err(dev, "update_ec_sensors() failure\n");
return -EIO;
}
-
- state->last_updated = jiffies;
+ state->next_update = get_jiffies_64() + HZ;
}

*value = state->sensors[sensor_index].cached_value;
@@ -1367,6 +1366,7 @@ static int asus_ec_probe(struct platform_device *pdev)
if (!ec_data)
return -ENOMEM;

+ ec_data->next_update = INITIAL_JIFFIES;
dev_set_drvdata(dev, ec_data);
ec_data->board_info = pboard_info;

@@ -1460,9 +1460,11 @@ static int asus_ec_probe(struct platform_device *pdev)
if (!nr_count[type])
continue;

- asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev,
- nr_count[type], type,
- hwmon_attributes[type]);
+ status = asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev,
+ nr_count[type], type,
+ hwmon_attributes[type]);
+ if (status)
+ return status;
*ptr_asus_ec_ci++ = asus_ec_hwmon_chan++;
}

diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c
index 1c7e9a98b757..f5de4894f0a2 100644
--- a/drivers/hwmon/atxp1.c
+++ b/drivers/hwmon/atxp1.c
@@ -278,7 +278,7 @@ static int atxp1_probe(struct i2c_client *client)
};

static const struct i2c_device_id atxp1_id[] = {
- { "atxp1" },
+ { .name = "atxp1" },
{ }
};
MODULE_DEVICE_TABLE(i2c, atxp1_id);
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index 645b8c2e704e..4aecf463180f 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -736,14 +736,14 @@ static void cc2_remove(struct i2c_client *client)
}

static const struct i2c_device_id cc2_id[] = {
- { "cc2d23" },
- { "cc2d23s" },
- { "cc2d25" },
- { "cc2d25s" },
- { "cc2d33" },
- { "cc2d33s" },
- { "cc2d35" },
- { "cc2d35s" },
+ { .name = "cc2d23" },
+ { .name = "cc2d23s" },
+ { .name = "cc2d25" },
+ { .name = "cc2d25s" },
+ { .name = "cc2d33" },
+ { .name = "cc2d33s" },
+ { .name = "cc2d35" },
+ { .name = "cc2d35s" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cc2_id);
diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c
index b6e508e43fa1..8354a002f4c5 100644
--- a/drivers/hwmon/corsair-cpro.c
+++ b/drivers/hwmon/corsair-cpro.c
@@ -645,6 +645,7 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)

out_hw_close:
hid_hw_close(hdev);
+ hid_device_io_stop(hdev);
out_hw_stop:
hid_hw_stop(hdev);
return ret;
diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c
index 76f3e1da68d0..ce958cdaef58 100644
--- a/drivers/hwmon/corsair-psu.c
+++ b/drivers/hwmon/corsair-psu.c
@@ -822,6 +822,7 @@ static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id

fail_and_close:
hid_hw_close(hdev);
+ hid_device_io_stop(hdev);
fail_and_stop:
hid_hw_stop(hdev);
return ret;
diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c
index 3d4057309950..7e839308e58f 100644
--- a/drivers/hwmon/dme1737.c
+++ b/drivers/hwmon/dme1737.c
@@ -2515,8 +2515,8 @@ static void dme1737_i2c_remove(struct i2c_client *client)
}

static const struct i2c_device_id dme1737_id[] = {
- { "dme1737", dme1737 },
- { "sch5027", sch5027 },
+ { .name = "dme1737", .driver_data = dme1737 },
+ { .name = "sch5027", .driver_data = sch5027 },
{ }
};
MODULE_DEVICE_TABLE(i2c, dme1737_id);
diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c
index 42ec34cb8a5f..0618f6de9679 100644
--- a/drivers/hwmon/ds1621.c
+++ b/drivers/hwmon/ds1621.c
@@ -367,11 +367,11 @@ static int ds1621_probe(struct i2c_client *client)
}

static const struct i2c_device_id ds1621_id[] = {
- { "ds1621", ds1621 },
- { "ds1625", ds1625 },
- { "ds1631", ds1631 },
- { "ds1721", ds1721 },
- { "ds1731", ds1731 },
+ { .name = "ds1621", .driver_data = ds1621 },
+ { .name = "ds1625", .driver_data = ds1625 },
+ { .name = "ds1631", .driver_data = ds1631 },
+ { .name = "ds1721", .driver_data = ds1721 },
+ { .name = "ds1731", .driver_data = ds1731 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds1621_id);
diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c
index ce397042d90b..25287f0fa4c2 100644
--- a/drivers/hwmon/ds620.c
+++ b/drivers/hwmon/ds620.c
@@ -233,8 +233,8 @@ static int ds620_probe(struct i2c_client *client)
}

static const struct i2c_device_id ds620_id[] = {
- {"ds620"},
- {}
+ { .name = "ds620" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, ds620_id);
diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c
index 964a8cb278f1..cd753b38709f 100644
--- a/drivers/hwmon/emc1403.c
+++ b/drivers/hwmon/emc1403.c
@@ -639,18 +639,18 @@ static const struct hwmon_chip_info emc1403_chip_info = {

/* Last digit of chip name indicates number of channels */
static const struct i2c_device_id emc1403_idtable[] = {
- { "emc1402", emc1402 },
- { "emc1403", emc1403 },
- { "emc1404", emc1404 },
- { "emc1412", emc1402 },
- { "emc1413", emc1403 },
- { "emc1414", emc1404 },
- { "emc1422", emc1402 },
- { "emc1423", emc1403 },
- { "emc1424", emc1404 },
- { "emc1428", emc1428 },
- { "emc1438", emc1428 },
- { "emc1442", emc1402 },
+ { .name = "emc1402", .driver_data = emc1402 },
+ { .name = "emc1403", .driver_data = emc1403 },
+ { .name = "emc1404", .driver_data = emc1404 },
+ { .name = "emc1412", .driver_data = emc1402 },
+ { .name = "emc1413", .driver_data = emc1403 },
+ { .name = "emc1414", .driver_data = emc1404 },
+ { .name = "emc1422", .driver_data = emc1402 },
+ { .name = "emc1423", .driver_data = emc1403 },
+ { .name = "emc1424", .driver_data = emc1404 },
+ { .name = "emc1428", .driver_data = emc1428 },
+ { .name = "emc1438", .driver_data = emc1428 },
+ { .name = "emc1442", .driver_data = emc1402 },
{ }
};
MODULE_DEVICE_TABLE(i2c, emc1403_idtable);
diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c
index 9b8e925af030..27dc149a3ed9 100644
--- a/drivers/hwmon/emc2103.c
+++ b/drivers/hwmon/emc2103.c
@@ -624,7 +624,7 @@ emc2103_probe(struct i2c_client *client)
}

static const struct i2c_device_id emc2103_ids[] = {
- { "emc2103" },
+ { .name = "emc2103" },
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(i2c, emc2103_ids);
diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c
index 64b213e1451e..8fcdac6cfb24 100644
--- a/drivers/hwmon/emc2305.c
+++ b/drivers/hwmon/emc2305.c
@@ -59,10 +59,10 @@ enum emc230x_product_id {
};

static const struct i2c_device_id emc2305_ids[] = {
- { "emc2305" },
- { "emc2303" },
- { "emc2302" },
- { "emc2301" },
+ { .name = "emc2305" },
+ { .name = "emc2303" },
+ { .name = "emc2302" },
+ { .name = "emc2301" },
{ }
};
MODULE_DEVICE_TABLE(i2c, emc2305_ids);
diff --git a/drivers/hwmon/emc6w201.c b/drivers/hwmon/emc6w201.c
index 1100c6e5daa7..c13ea8748683 100644
--- a/drivers/hwmon/emc6w201.c
+++ b/drivers/hwmon/emc6w201.c
@@ -464,7 +464,7 @@ static int emc6w201_probe(struct i2c_client *client)
}

static const struct i2c_device_id emc6w201_id[] = {
- { "emc6w201" },
+ { .name = "emc6w201" },
{ }
};
MODULE_DEVICE_TABLE(i2c, emc6w201_id);
diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c
index 7e867f132420..cc3a9612f2c0 100644
--- a/drivers/hwmon/f75375s.c
+++ b/drivers/hwmon/f75375s.c
@@ -877,9 +877,9 @@ static int f75375_detect(struct i2c_client *client,
}

static const struct i2c_device_id f75375_id[] = {
- { "f75373", f75373 },
- { "f75375", f75375 },
- { "f75387", f75387 },
+ { .name = "f75373", .driver_data = f75373 },
+ { .name = "f75375", .driver_data = f75375 },
+ { .name = "f75387", .driver_data = f75387 },
{ }
};
MODULE_DEVICE_TABLE(i2c, f75375_id);
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c
index 1211fa2259e5..019fc32bf318 100644
--- a/drivers/hwmon/fschmd.c
+++ b/drivers/hwmon/fschmd.c
@@ -225,13 +225,13 @@ static struct fschmd_data *fschmd_update_device(struct device *dev);
*/

static const struct i2c_device_id fschmd_id[] = {
- { "fscpos", fscpos },
- { "fscher", fscher },
- { "fscscy", fscscy },
- { "fschrc", fschrc },
- { "fschmd", fschmd },
- { "fschds", fschds },
- { "fscsyl", fscsyl },
+ { .name = "fscpos", .driver_data = fscpos },
+ { .name = "fscher", .driver_data = fscher },
+ { .name = "fscscy", .driver_data = fscscy },
+ { .name = "fschrc", .driver_data = fschrc },
+ { .name = "fschmd", .driver_data = fschmd },
+ { .name = "fschds", .driver_data = fschds },
+ { .name = "fscsyl", .driver_data = fscsyl },
{ }
};
MODULE_DEVICE_TABLE(i2c, fschmd_id);
diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c
index 08dcc6a7fb62..06be120a349d 100644
--- a/drivers/hwmon/ftsteutates.c
+++ b/drivers/hwmon/ftsteutates.c
@@ -49,7 +49,7 @@
static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };

static const struct i2c_device_id fts_id[] = {
- { "ftsteutates" },
+ { .name = "ftsteutates" },
{ }
};
MODULE_DEVICE_TABLE(i2c, fts_id);
diff --git a/drivers/hwmon/g760a.c b/drivers/hwmon/g760a.c
index 39ae8f826417..e2166ee76286 100644
--- a/drivers/hwmon/g760a.c
+++ b/drivers/hwmon/g760a.c
@@ -197,7 +197,7 @@ static int g760a_probe(struct i2c_client *client)
}

static const struct i2c_device_id g760a_id[] = {
- { "g760a" },
+ { .name = "g760a" },
{ }
};
MODULE_DEVICE_TABLE(i2c, g760a_id);
diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c
index 4fa3aa1271da..407cf7af4fdd 100644
--- a/drivers/hwmon/g762.c
+++ b/drivers/hwmon/g762.c
@@ -44,9 +44,9 @@
#define DRVNAME "g762"

static const struct i2c_device_id g762_id[] = {
- { "g761" },
- { "g762" },
- { "g763" },
+ { .name = "g761" },
+ { .name = "g762" },
+ { .name = "g763" },
{ }
};
MODULE_DEVICE_TABLE(i2c, g762_id);
diff --git a/drivers/hwmon/gigabyte_waterforce.c b/drivers/hwmon/gigabyte_waterforce.c
index 27487e215bdd..4eea05f8b569 100644
--- a/drivers/hwmon/gigabyte_waterforce.c
+++ b/drivers/hwmon/gigabyte_waterforce.c
@@ -371,13 +371,15 @@ static int waterforce_probe(struct hid_device *hdev, const struct hid_device_id
if (IS_ERR(priv->hwmon_dev)) {
ret = PTR_ERR(priv->hwmon_dev);
hid_err(hdev, "hwmon registration failed with %d\n", ret);
- goto fail_and_close;
+ goto fail_and_io_stop;
}

waterforce_debugfs_init(priv);

return 0;

+fail_and_io_stop:
+ hid_device_io_stop(hdev);
fail_and_close:
hid_hw_close(hdev);
fail_and_stop:
diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c
index 9c68bc013950..742c130cec7f 100644
--- a/drivers/hwmon/gl518sm.c
+++ b/drivers/hwmon/gl518sm.c
@@ -642,7 +642,7 @@ static int gl518_probe(struct i2c_client *client)
}

static const struct i2c_device_id gl518_id[] = {
- { "gl518sm" },
+ { .name = "gl518sm" },
{ }
};
MODULE_DEVICE_TABLE(i2c, gl518_id);
diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c
index 972f4f8caa2b..f4fe39912ee2 100644
--- a/drivers/hwmon/gl520sm.c
+++ b/drivers/hwmon/gl520sm.c
@@ -885,7 +885,7 @@ static int gl520_probe(struct i2c_client *client)
}

static const struct i2c_device_id gl520_id[] = {
- { "gl520sm" },
+ { .name = "gl520sm" },
{ }
};
MODULE_DEVICE_TABLE(i2c, gl520_id);
diff --git a/drivers/hwmon/hih6130.c b/drivers/hwmon/hih6130.c
index 85af8299150a..7984be1e706d 100644
--- a/drivers/hwmon/hih6130.c
+++ b/drivers/hwmon/hih6130.c
@@ -233,7 +233,7 @@ static int hih6130_probe(struct i2c_client *client)

/* Device ID table */
static const struct i2c_device_id hih6130_id[] = {
- { "hih6130" },
+ { .name = "hih6130" },
{ }
};
MODULE_DEVICE_TABLE(i2c, hih6130_id);
diff --git a/drivers/hwmon/hs3001.c b/drivers/hwmon/hs3001.c
index 50c6c15f8b18..b263cbeca074 100644
--- a/drivers/hwmon/hs3001.c
+++ b/drivers/hwmon/hs3001.c
@@ -169,8 +169,8 @@ static const struct hwmon_chip_info hs3001_chip_info = {

/* device ID table */
static const struct i2c_device_id hs3001_ids[] = {
- { "hs3001" },
- { },
+ { .name = "hs3001" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, hs3001_ids);
diff --git a/drivers/hwmon/htu31.c b/drivers/hwmon/htu31.c
index 7521a371aa6c..5d2cdbdb773b 100644
--- a/drivers/hwmon/htu31.c
+++ b/drivers/hwmon/htu31.c
@@ -322,7 +322,7 @@ static int htu31_probe(struct i2c_client *client)
}

static const struct i2c_device_id htu31_id[] = {
- { "htu31" },
+ { .name = "htu31" },
{ }
};
MODULE_DEVICE_TABLE(i2c, htu31_id);
diff --git a/drivers/hwmon/ina209.c b/drivers/hwmon/ina209.c
index a116f1600e81..39aca2cdf27f 100644
--- a/drivers/hwmon/ina209.c
+++ b/drivers/hwmon/ina209.c
@@ -569,7 +569,7 @@ static void ina209_remove(struct i2c_client *client)
}

static const struct i2c_device_id ina209_id[] = {
- { "ina209" },
+ { .name = "ina209" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ina209_id);
diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c
index ff67b03189f7..c20dd164a6a6 100644
--- a/drivers/hwmon/ina238.c
+++ b/drivers/hwmon/ina238.c
@@ -837,12 +837,12 @@ static int ina238_probe(struct i2c_client *client)
}

static const struct i2c_device_id ina238_id[] = {
- { "ina228", ina228 },
- { "ina237", ina237 },
- { "ina238", ina238 },
- { "ina700", ina700 },
- { "ina780", ina780 },
- { "sq52206", sq52206 },
+ { .name = "ina228", .driver_data = ina228 },
+ { .name = "ina237", .driver_data = ina237 },
+ { .name = "ina238", .driver_data = ina238 },
+ { .name = "ina700", .driver_data = ina700 },
+ { .name = "ina780", .driver_data = ina780 },
+ { .name = "sq52206", .driver_data = sq52206 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ina238_id);
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index 613ffb622b7c..32bf4595bcb4 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -1000,14 +1000,14 @@ static int ina2xx_probe(struct i2c_client *client)
}

static const struct i2c_device_id ina2xx_id[] = {
- { "ina219", ina219 },
- { "ina220", ina219 },
- { "ina226", ina226 },
- { "ina230", ina226 },
- { "ina231", ina226 },
- { "ina234", ina234 },
- { "ina260", ina260 },
- { "sy24655", sy24655 },
+ { .name = "ina219", .driver_data = ina219 },
+ { .name = "ina220", .driver_data = ina219 },
+ { .name = "ina226", .driver_data = ina226 },
+ { .name = "ina230", .driver_data = ina226 },
+ { .name = "ina231", .driver_data = ina226 },
+ { .name = "ina234", .driver_data = ina234 },
+ { .name = "ina260", .driver_data = ina260 },
+ { .name = "sy24655", .driver_data = sy24655 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ina2xx_id);
diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
index 5ecc68dcf169..3ab5de3a1110 100644
--- a/drivers/hwmon/ina3221.c
+++ b/drivers/hwmon/ina3221.c
@@ -1002,7 +1002,7 @@ static const struct of_device_id ina3221_of_match_table[] = {
MODULE_DEVICE_TABLE(of, ina3221_of_match_table);

static const struct i2c_device_id ina3221_ids[] = {
- { "ina3221" },
+ { .name = "ina3221" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, ina3221_ids);
diff --git a/drivers/hwmon/isl28022.c b/drivers/hwmon/isl28022.c
index 96fcfbfff48f..0c60d2ef28f5 100644
--- a/drivers/hwmon/isl28022.c
+++ b/drivers/hwmon/isl28022.c
@@ -475,7 +475,7 @@ static int isl28022_probe(struct i2c_client *client)
}

static const struct i2c_device_id isl28022_ids[] = {
- { "isl28022" },
+ { .name = "isl28022" },
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(i2c, isl28022_ids);
diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c
index 6549dc543781..77fece680358 100644
--- a/drivers/hwmon/jc42.c
+++ b/drivers/hwmon/jc42.c
@@ -579,7 +579,7 @@ static const struct dev_pm_ops jc42_dev_pm_ops = {
#endif /* CONFIG_PM */

static const struct i2c_device_id jc42_id[] = {
- { "jc42" },
+ { .name = "jc42" },
{ }
};
MODULE_DEVICE_TABLE(i2c, jc42_id);
diff --git a/drivers/hwmon/lineage-pem.c b/drivers/hwmon/lineage-pem.c
index 64a335a64a2e..2553e49f8401 100644
--- a/drivers/hwmon/lineage-pem.c
+++ b/drivers/hwmon/lineage-pem.c
@@ -502,8 +502,8 @@ static int pem_probe(struct i2c_client *client)
}

static const struct i2c_device_id pem_id[] = {
- {"lineage_pem"},
- {}
+ { .name = "lineage_pem" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, pem_id);

diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
index 30500b4d2221..a0d77a7386a9 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -1152,9 +1152,9 @@ static int lm63_probe(struct i2c_client *client)
*/

static const struct i2c_device_id lm63_id[] = {
- { "lm63", lm63 },
- { "lm64", lm64 },
- { "lm96163", lm96163 },
+ { .name = "lm63", .driver_data = lm63 },
+ { .name = "lm64", .driver_data = lm64 },
+ { .name = "lm96163", .driver_data = lm96163 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm63_id);
diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c
index 581b01572e1b..63ee67481a16 100644
--- a/drivers/hwmon/lm73.c
+++ b/drivers/hwmon/lm73.c
@@ -220,7 +220,7 @@ lm73_probe(struct i2c_client *client)
}

static const struct i2c_device_id lm73_ids[] = {
- { "lm73" },
+ { .name = "lm73" },
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(i2c, lm73_ids);
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index c283443e363b..05293383f9cc 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -816,37 +816,37 @@ static int lm75_i2c_probe(struct i2c_client *client)
}

static const struct i2c_device_id lm75_i2c_ids[] = {
- { "adt75", adt75, },
- { "as6200", as6200, },
- { "at30ts74", at30ts74, },
- { "ds1775", ds1775, },
- { "ds75", ds75, },
- { "ds7505", ds7505, },
- { "g751", g751, },
- { "lm75", lm75, },
- { "lm75a", lm75a, },
- { "lm75b", lm75b, },
- { "max6625", max6625, },
- { "max6626", max6626, },
- { "max31725", max31725, },
- { "max31726", max31725, },
- { "mcp980x", mcp980x, },
- { "p3t1750", p3t1750, },
- { "p3t1755", p3t1755, },
- { "pct2075", pct2075, },
- { "stds75", stds75, },
- { "stlm75", stlm75, },
- { "tcn75", tcn75, },
- { "tmp100", tmp100, },
- { "tmp101", tmp101, },
- { "tmp105", tmp105, },
- { "tmp112", tmp112, },
- { "tmp175", tmp175, },
- { "tmp275", tmp275, },
- { "tmp75", tmp75, },
- { "tmp75b", tmp75b, },
- { "tmp75c", tmp75c, },
- { "tmp1075", tmp1075, },
+ { .name = "adt75", .driver_data = adt75 },
+ { .name = "as6200", .driver_data = as6200 },
+ { .name = "at30ts74", .driver_data = at30ts74 },
+ { .name = "ds1775", .driver_data = ds1775 },
+ { .name = "ds75", .driver_data = ds75 },
+ { .name = "ds7505", .driver_data = ds7505 },
+ { .name = "g751", .driver_data = g751 },
+ { .name = "lm75", .driver_data = lm75 },
+ { .name = "lm75a", .driver_data = lm75a },
+ { .name = "lm75b", .driver_data = lm75b },
+ { .name = "max6625", .driver_data = max6625 },
+ { .name = "max6626", .driver_data = max6626 },
+ { .name = "max31725", .driver_data = max31725 },
+ { .name = "max31726", .driver_data = max31725 },
+ { .name = "mcp980x", .driver_data = mcp980x },
+ { .name = "p3t1750", .driver_data = p3t1750 },
+ { .name = "p3t1755", .driver_data = p3t1755 },
+ { .name = "pct2075", .driver_data = pct2075 },
+ { .name = "stds75", .driver_data = stds75 },
+ { .name = "stlm75", .driver_data = stlm75 },
+ { .name = "tcn75", .driver_data = tcn75 },
+ { .name = "tmp100", .driver_data = tmp100 },
+ { .name = "tmp101", .driver_data = tmp101 },
+ { .name = "tmp105", .driver_data = tmp105 },
+ { .name = "tmp112", .driver_data = tmp112 },
+ { .name = "tmp175", .driver_data = tmp175 },
+ { .name = "tmp275", .driver_data = tmp275 },
+ { .name = "tmp75", .driver_data = tmp75 },
+ { .name = "tmp75b", .driver_data = tmp75b },
+ { .name = "tmp75c", .driver_data = tmp75c },
+ { .name = "tmp1075", .driver_data = tmp1075 },
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(i2c, lm75_i2c_ids);
diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c
index 80f7a6a3f9a2..96c5c2584d37 100644
--- a/drivers/hwmon/lm77.c
+++ b/drivers/hwmon/lm77.c
@@ -337,7 +337,7 @@ static int lm77_probe(struct i2c_client *client)
}

static const struct i2c_device_id lm77_id[] = {
- { "lm77" },
+ { .name = "lm77" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm77_id);
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index 9378a47bf5af..e4834f9eca6a 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -649,8 +649,8 @@ static int lm78_i2c_probe(struct i2c_client *client)
}

static const struct i2c_device_id lm78_i2c_id[] = {
- { "lm78", lm78 },
- { "lm79", lm79 },
+ { .name = "lm78", .driver_data = lm78 },
+ { .name = "lm79", .driver_data = lm79 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm78_i2c_id);
diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c
index 63c7831bd3e1..94f3dabaaa6f 100644
--- a/drivers/hwmon/lm80.c
+++ b/drivers/hwmon/lm80.c
@@ -622,8 +622,8 @@ static int lm80_probe(struct i2c_client *client)
*/

static const struct i2c_device_id lm80_id[] = {
- { "lm80", 0 },
- { "lm96080", 1 },
+ { .name = "lm80" },
+ { .name = "lm96080" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm80_id);
diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c
index f800fe2ef18b..8d49df8c9314 100644
--- a/drivers/hwmon/lm83.c
+++ b/drivers/hwmon/lm83.c
@@ -443,8 +443,8 @@ static int lm83_probe(struct i2c_client *client)
*/

static const struct i2c_device_id lm83_id[] = {
- { "lm83", lm83 },
- { "lm82", lm82 },
+ { .name = "lm83", .driver_data = lm83 },
+ { .name = "lm82", .driver_data = lm82 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm83_id);
diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index 1c244ed75122..c56e164d61c1 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -1618,18 +1618,18 @@ static int lm85_probe(struct i2c_client *client)
}

static const struct i2c_device_id lm85_id[] = {
- { "adm1027", adm1027 },
- { "adt7463", adt7463 },
- { "adt7468", adt7468 },
- { "lm85", lm85 },
- { "lm85b", lm85 },
- { "lm85c", lm85 },
- { "lm96000", lm96000 },
- { "emc6d100", emc6d100 },
- { "emc6d101", emc6d100 },
- { "emc6d102", emc6d102 },
- { "emc6d103", emc6d103 },
- { "emc6d103s", emc6d103s },
+ { .name = "adm1027", .driver_data = adm1027 },
+ { .name = "adt7463", .driver_data = adt7463 },
+ { .name = "adt7468", .driver_data = adt7468 },
+ { .name = "lm85", .driver_data = lm85 },
+ { .name = "lm85b", .driver_data = lm85 },
+ { .name = "lm85c", .driver_data = lm85 },
+ { .name = "lm96000", .driver_data = lm96000 },
+ { .name = "emc6d100", .driver_data = emc6d100 },
+ { .name = "emc6d101", .driver_data = emc6d100 },
+ { .name = "emc6d102", .driver_data = emc6d102 },
+ { .name = "emc6d103", .driver_data = emc6d103 },
+ { .name = "emc6d103s", .driver_data = emc6d103s },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm85_id);
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index 37bf2d1d3d09..c09074f44708 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -981,8 +981,8 @@ static int lm87_probe(struct i2c_client *client)
*/

static const struct i2c_device_id lm87_id[] = {
- { "lm87" },
- { "adm1024" },
+ { .name = "lm87" },
+ { .name = "adm1024" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm87_id);
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 1eeb608e5903..c78c96e1bd83 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -243,54 +243,54 @@ enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481,
*/

static const struct i2c_device_id lm90_id[] = {
- { "adm1020", max1617 },
- { "adm1021", max1617 },
- { "adm1023", adm1023 },
- { "adm1032", adm1032 },
- { "adt7421", adt7461a },
- { "adt7461", adt7461 },
- { "adt7461a", adt7461a },
- { "adt7481", adt7481 },
- { "adt7482", adt7481 },
- { "adt7483a", adt7481 },
- { "g781", g781 },
- { "gl523sm", max1617 },
- { "lm84", lm84 },
- { "lm86", lm90 },
- { "lm89", lm90 },
- { "lm90", lm90 },
- { "lm99", lm99 },
- { "max1617", max1617 },
- { "max6642", max6642 },
- { "max6646", max6646 },
- { "max6647", max6646 },
- { "max6648", max6648 },
- { "max6649", max6646 },
- { "max6654", max6654 },
- { "max6657", max6657 },
- { "max6658", max6657 },
- { "max6659", max6659 },
- { "max6680", max6680 },
- { "max6681", max6680 },
- { "max6690", max6654 },
- { "max6692", max6648 },
- { "max6695", max6696 },
- { "max6696", max6696 },
- { "mc1066", max1617 },
- { "nct1008", adt7461a },
- { "nct210", nct210 },
- { "nct214", nct72 },
- { "nct218", nct72 },
- { "nct72", nct72 },
- { "nct7716", nct7716 },
- { "nct7717", nct7717 },
- { "nct7718", nct7718 },
- { "ne1618", ne1618 },
- { "w83l771", w83l771 },
- { "sa56004", sa56004 },
- { "thmc10", max1617 },
- { "tmp451", tmp451 },
- { "tmp461", tmp461 },
+ { .name = "adm1020", .driver_data = max1617 },
+ { .name = "adm1021", .driver_data = max1617 },
+ { .name = "adm1023", .driver_data = adm1023 },
+ { .name = "adm1032", .driver_data = adm1032 },
+ { .name = "adt7421", .driver_data = adt7461a },
+ { .name = "adt7461", .driver_data = adt7461 },
+ { .name = "adt7461a", .driver_data = adt7461a },
+ { .name = "adt7481", .driver_data = adt7481 },
+ { .name = "adt7482", .driver_data = adt7481 },
+ { .name = "adt7483a", .driver_data = adt7481 },
+ { .name = "g781", .driver_data = g781 },
+ { .name = "gl523sm", .driver_data = max1617 },
+ { .name = "lm84", .driver_data = lm84 },
+ { .name = "lm86", .driver_data = lm90 },
+ { .name = "lm89", .driver_data = lm90 },
+ { .name = "lm90", .driver_data = lm90 },
+ { .name = "lm99", .driver_data = lm99 },
+ { .name = "max1617", .driver_data = max1617 },
+ { .name = "max6642", .driver_data = max6642 },
+ { .name = "max6646", .driver_data = max6646 },
+ { .name = "max6647", .driver_data = max6646 },
+ { .name = "max6648", .driver_data = max6648 },
+ { .name = "max6649", .driver_data = max6646 },
+ { .name = "max6654", .driver_data = max6654 },
+ { .name = "max6657", .driver_data = max6657 },
+ { .name = "max6658", .driver_data = max6657 },
+ { .name = "max6659", .driver_data = max6659 },
+ { .name = "max6680", .driver_data = max6680 },
+ { .name = "max6681", .driver_data = max6680 },
+ { .name = "max6690", .driver_data = max6654 },
+ { .name = "max6692", .driver_data = max6648 },
+ { .name = "max6695", .driver_data = max6696 },
+ { .name = "max6696", .driver_data = max6696 },
+ { .name = "mc1066", .driver_data = max1617 },
+ { .name = "nct1008", .driver_data = adt7461a },
+ { .name = "nct210", .driver_data = nct210 },
+ { .name = "nct214", .driver_data = nct72 },
+ { .name = "nct218", .driver_data = nct72 },
+ { .name = "nct72", .driver_data = nct72 },
+ { .name = "nct7716", .driver_data = nct7716 },
+ { .name = "nct7717", .driver_data = nct7717 },
+ { .name = "nct7718", .driver_data = nct7718 },
+ { .name = "ne1618", .driver_data = ne1618 },
+ { .name = "w83l771", .driver_data = w83l771 },
+ { .name = "sa56004", .driver_data = sa56004 },
+ { .name = "thmc10", .driver_data = max1617 },
+ { .name = "tmp451", .driver_data = tmp451 },
+ { .name = "tmp461", .driver_data = tmp461 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm90_id);
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
index 91a6b7525bb6..4aadbabc27bb 100644
--- a/drivers/hwmon/lm92.c
+++ b/drivers/hwmon/lm92.c
@@ -405,10 +405,10 @@ static int lm92_probe(struct i2c_client *client)
* Module and driver stuff
*/

-/* .driver_data is limit register resolution */
+/* .driver_data is limit register resolution */
static const struct i2c_device_id lm92_id[] = {
- { "lm92", 13 },
- { "max6635", 9 },
+ { .name = "lm92", .driver_data = 13 },
+ { .name = "max6635", .driver_data = 9 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm92_id);
diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c
index be4853fad80f..d58a3c2a8593 100644
--- a/drivers/hwmon/lm93.c
+++ b/drivers/hwmon/lm93.c
@@ -2624,8 +2624,8 @@ static int lm93_probe(struct i2c_client *client)
}

static const struct i2c_device_id lm93_id[] = {
- { "lm93" },
- { "lm94" },
+ { .name = "lm93" },
+ { .name = "lm94" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm93_id);
diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c
index 387b3ba81dbf..74f280b90e3e 100644
--- a/drivers/hwmon/lm95234.c
+++ b/drivers/hwmon/lm95234.c
@@ -532,8 +532,8 @@ static int lm95234_probe(struct i2c_client *client)

/* Driver data (common to all clients) */
static const struct i2c_device_id lm95234_id[] = {
- { "lm95233", lm95233 },
- { "lm95234", lm95234 },
+ { .name = "lm95233", .driver_data = lm95233 },
+ { .name = "lm95234", .driver_data = lm95234 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm95234_id);
diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c
index 456381b0938e..0cb0edb3845d 100644
--- a/drivers/hwmon/lm95241.c
+++ b/drivers/hwmon/lm95241.c
@@ -441,8 +441,8 @@ static int lm95241_probe(struct i2c_client *client)

/* Driver data (common to all clients) */
static const struct i2c_device_id lm95241_id[] = {
- { "lm95231" },
- { "lm95241" },
+ { .name = "lm95231" },
+ { .name = "lm95241" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm95241_id);
diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c
index 9ed300c6b5f7..11553391f54b 100644
--- a/drivers/hwmon/lm95245.c
+++ b/drivers/hwmon/lm95245.c
@@ -546,8 +546,8 @@ static int lm95245_probe(struct i2c_client *client)

/* Driver data (common to all clients) */
static const struct i2c_device_id lm95245_id[] = {
- { "lm95235" },
- { "lm95245" },
+ { .name = "lm95235" },
+ { .name = "lm95245" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm95245_id);
diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c
index 3e0e0e0687bd..b521100afe53 100644
--- a/drivers/hwmon/ltc2945.c
+++ b/drivers/hwmon/ltc2945.c
@@ -508,7 +508,7 @@ static int ltc2945_probe(struct i2c_client *client)
}

static const struct i2c_device_id ltc2945_id[] = {
- {"ltc2945"},
+ { .name = "ltc2945" },
{ }
};

diff --git a/drivers/hwmon/ltc2947-i2c.c b/drivers/hwmon/ltc2947-i2c.c
index 176d710706dd..e07d33983d5c 100644
--- a/drivers/hwmon/ltc2947-i2c.c
+++ b/drivers/hwmon/ltc2947-i2c.c
@@ -27,8 +27,8 @@ static int ltc2947_probe(struct i2c_client *i2c)
}

static const struct i2c_device_id ltc2947_id[] = {
- {"ltc2947"},
- {}
+ { .name = "ltc2947" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ltc2947_id);

diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c
index f1c1933c52cf..a2725e4b2f21 100644
--- a/drivers/hwmon/ltc2990.c
+++ b/drivers/hwmon/ltc2990.c
@@ -259,8 +259,8 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c)
}

static const struct i2c_device_id ltc2990_i2c_id[] = {
- { "ltc2990" },
- {}
+ { .name = "ltc2990" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ltc2990_i2c_id);

diff --git a/drivers/hwmon/ltc2991.c b/drivers/hwmon/ltc2991.c
index 6d5d4cb846da..bc8d803e8cc9 100644
--- a/drivers/hwmon/ltc2991.c
+++ b/drivers/hwmon/ltc2991.c
@@ -409,8 +409,8 @@ static const struct of_device_id ltc2991_of_match[] = {
MODULE_DEVICE_TABLE(of, ltc2991_of_match);

static const struct i2c_device_id ltc2991_i2c_id[] = {
- { "ltc2991" },
- {}
+ { .name = "ltc2991" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ltc2991_i2c_id);

diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c
index 2617c4538af9..43b6029c0840 100644
--- a/drivers/hwmon/ltc2992.c
+++ b/drivers/hwmon/ltc2992.c
@@ -948,8 +948,8 @@ static const struct of_device_id ltc2992_of_match[] = {
MODULE_DEVICE_TABLE(of, ltc2992_of_match);

static const struct i2c_device_id ltc2992_i2c_id[] = {
- {"ltc2992"},
- {}
+ { .name = "ltc2992" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ltc2992_i2c_id);

diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c
index fa66eda78efe..fa7c57aae5f5 100644
--- a/drivers/hwmon/ltc4151.c
+++ b/drivers/hwmon/ltc4151.c
@@ -188,7 +188,7 @@ static int ltc4151_probe(struct i2c_client *client)
}

static const struct i2c_device_id ltc4151_id[] = {
- { "ltc4151" },
+ { .name = "ltc4151" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ltc4151_id);
diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c
index cce452711cec..3d439fbbbef9 100644
--- a/drivers/hwmon/ltc4215.c
+++ b/drivers/hwmon/ltc4215.c
@@ -245,7 +245,7 @@ static int ltc4215_probe(struct i2c_client *client)
}

static const struct i2c_device_id ltc4215_id[] = {
- { "ltc4215" },
+ { .name = "ltc4215" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ltc4215_id);
diff --git a/drivers/hwmon/ltc4222.c b/drivers/hwmon/ltc4222.c
index f7eb007fd766..4fc69be5f2bc 100644
--- a/drivers/hwmon/ltc4222.c
+++ b/drivers/hwmon/ltc4222.c
@@ -200,7 +200,7 @@ static int ltc4222_probe(struct i2c_client *client)
}

static const struct i2c_device_id ltc4222_id[] = {
- {"ltc4222"},
+ { .name = "ltc4222" },
{ }
};

diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c
index e8131a48bda7..d87aa9c32c87 100644
--- a/drivers/hwmon/ltc4245.c
+++ b/drivers/hwmon/ltc4245.c
@@ -461,7 +461,7 @@ static int ltc4245_probe(struct i2c_client *client)
}

static const struct i2c_device_id ltc4245_id[] = {
- { "ltc4245" },
+ { .name = "ltc4245" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ltc4245_id);
diff --git a/drivers/hwmon/ltc4260.c b/drivers/hwmon/ltc4260.c
index 9750dc9aa336..37a85125d619 100644
--- a/drivers/hwmon/ltc4260.c
+++ b/drivers/hwmon/ltc4260.c
@@ -163,7 +163,7 @@ static int ltc4260_probe(struct i2c_client *client)
}

static const struct i2c_device_id ltc4260_id[] = {
- {"ltc4260"},
+ { .name = "ltc4260" },
{ }
};

diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c
index 2cd218a6a3be..a2e52ca0b06e 100644
--- a/drivers/hwmon/ltc4261.c
+++ b/drivers/hwmon/ltc4261.c
@@ -222,8 +222,8 @@ static int ltc4261_probe(struct i2c_client *client)
}

static const struct i2c_device_id ltc4261_id[] = {
- {"ltc4261"},
- {}
+ { .name = "ltc4261" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, ltc4261_id);
diff --git a/drivers/hwmon/max127.c b/drivers/hwmon/max127.c
index 5102d86d2619..b294e86d52d1 100644
--- a/drivers/hwmon/max127.c
+++ b/drivers/hwmon/max127.c
@@ -312,7 +312,7 @@ static int max127_probe(struct i2c_client *client)
}

static const struct i2c_device_id max127_id[] = {
- { "max127" },
+ { .name = "max127" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max127_id);
diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c
index 43fbb9b26b10..f8b421754220 100644
--- a/drivers/hwmon/max16065.c
+++ b/drivers/hwmon/max16065.c
@@ -592,12 +592,12 @@ static int max16065_probe(struct i2c_client *client)
}

static const struct i2c_device_id max16065_id[] = {
- { "max16065", max16065 },
- { "max16066", max16066 },
- { "max16067", max16067 },
- { "max16068", max16068 },
- { "max16070", max16070 },
- { "max16071", max16071 },
+ { .name = "max16065", .driver_data = max16065 },
+ { .name = "max16066", .driver_data = max16066 },
+ { .name = "max16067", .driver_data = max16067 },
+ { .name = "max16068", .driver_data = max16068 },
+ { .name = "max16070", .driver_data = max16070 },
+ { .name = "max16071", .driver_data = max16071 },
{ }
};

diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c
index 9b6d03cff4df..77ef39e8ae7e 100644
--- a/drivers/hwmon/max1619.c
+++ b/drivers/hwmon/max1619.c
@@ -366,7 +366,7 @@ static int max1619_probe(struct i2c_client *client)
}

static const struct i2c_device_id max1619_id[] = {
- { "max1619" },
+ { .name = "max1619" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max1619_id);
diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c
index a8197a86f559..32548f56409c 100644
--- a/drivers/hwmon/max1668.c
+++ b/drivers/hwmon/max1668.c
@@ -293,9 +293,9 @@ static int max1668_probe(struct i2c_client *client)
}

static const struct i2c_device_id max1668_id[] = {
- { "max1668", 5 },
- { "max1805", 3 },
- { "max1989", 5 },
+ { .name = "max1668", .driver_data = 5 },
+ { .name = "max1805", .driver_data = 3 },
+ { .name = "max1989", .driver_data = 5 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max1668_id);
diff --git a/drivers/hwmon/max31730.c b/drivers/hwmon/max31730.c
index 2f4b419b6c9e..d132be2d6487 100644
--- a/drivers/hwmon/max31730.c
+++ b/drivers/hwmon/max31730.c
@@ -345,7 +345,7 @@ max31730_probe(struct i2c_client *client)
}

static const struct i2c_device_id max31730_ids[] = {
- { "max31730" },
+ { .name = "max31730" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max31730_ids);
diff --git a/drivers/hwmon/max31760.c b/drivers/hwmon/max31760.c
index 127e31ca3c87..8d3be064b025 100644
--- a/drivers/hwmon/max31760.c
+++ b/drivers/hwmon/max31760.c
@@ -555,7 +555,7 @@ static const struct of_device_id max31760_of_match[] = {
MODULE_DEVICE_TABLE(of, max31760_of_match);

static const struct i2c_device_id max31760_id[] = {
- {"max31760"},
+ { .name = "max31760" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max31760_id);
diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c
index 4f6171a17d9f..db8a0f6f1829 100644
--- a/drivers/hwmon/max31790.c
+++ b/drivers/hwmon/max31790.c
@@ -517,7 +517,7 @@ static int max31790_probe(struct i2c_client *client)
}

static const struct i2c_device_id max31790_id[] = {
- { "max31790" },
+ { .name = "max31790" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max31790_id);
diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c
index 9b2e56c040df..6a7048fdd8ab 100644
--- a/drivers/hwmon/max31827.c
+++ b/drivers/hwmon/max31827.c
@@ -463,9 +463,9 @@ static struct attribute *max31827_attrs[] = {
ATTRIBUTE_GROUPS(max31827);

static const struct i2c_device_id max31827_i2c_ids[] = {
- { "max31827", max31827 },
- { "max31828", max31828 },
- { "max31829", max31829 },
+ { .name = "max31827", .driver_data = max31827 },
+ { .name = "max31828", .driver_data = max31828 },
+ { .name = "max31829", .driver_data = max31829 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max31827_i2c_ids);
diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c
index 4316dcdd03fc..100acf357b5f 100644
--- a/drivers/hwmon/max6620.c
+++ b/drivers/hwmon/max6620.c
@@ -474,7 +474,7 @@ static int max6620_probe(struct i2c_client *client)
}

static const struct i2c_device_id max6620_id[] = {
- { "max6620" },
+ { .name = "max6620" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max6620_id);
diff --git a/drivers/hwmon/max6621.c b/drivers/hwmon/max6621.c
index a7066f3a0bb4..e86ec6d237ca 100644
--- a/drivers/hwmon/max6621.c
+++ b/drivers/hwmon/max6621.c
@@ -537,7 +537,7 @@ static int max6621_probe(struct i2c_client *client)
}

static const struct i2c_device_id max6621_id[] = {
- { MAX6621_DRV_NAME },
+ { .name = MAX6621_DRV_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, max6621_id);
diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c
index 163d31f17bd4..dd5f4b3b128d 100644
--- a/drivers/hwmon/max6639.c
+++ b/drivers/hwmon/max6639.c
@@ -778,7 +778,7 @@ static int max6639_resume(struct device *dev)
}

static const struct i2c_device_id max6639_id[] = {
- {"max6639"},
+ { .name = "max6639" },
{ }
};

diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c
index 56b8157885bb..f0140ef9c40e 100644
--- a/drivers/hwmon/max6650.c
+++ b/drivers/hwmon/max6650.c
@@ -807,8 +807,8 @@ static int max6650_probe(struct i2c_client *client)
}

static const struct i2c_device_id max6650_id[] = {
- { "max6650", 1 },
- { "max6651", 4 },
+ { .name = "max6650", .driver_data = 1 },
+ { .name = "max6651", .driver_data = 4 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max6650_id);
diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c
index dd906cf491ca..c864093f015c 100644
--- a/drivers/hwmon/max6697.c
+++ b/drivers/hwmon/max6697.c
@@ -564,16 +564,16 @@ static int max6697_probe(struct i2c_client *client)
}

static const struct i2c_device_id max6697_id[] = {
- { "max6581", max6581 },
- { "max6602", max6602 },
- { "max6622", max6622 },
- { "max6636", max6636 },
- { "max6689", max6689 },
- { "max6693", max6693 },
- { "max6694", max6694 },
- { "max6697", max6697 },
- { "max6698", max6698 },
- { "max6699", max6699 },
+ { .name = "max6581", .driver_data = max6581 },
+ { .name = "max6602", .driver_data = max6602 },
+ { .name = "max6622", .driver_data = max6622 },
+ { .name = "max6636", .driver_data = max6636 },
+ { .name = "max6689", .driver_data = max6689 },
+ { .name = "max6693", .driver_data = max6693 },
+ { .name = "max6694", .driver_data = max6694 },
+ { .name = "max6697", .driver_data = max6697 },
+ { .name = "max6698", .driver_data = max6698 },
+ { .name = "max6699", .driver_data = max6699 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max6697_id);
diff --git a/drivers/hwmon/mc34vr500.c b/drivers/hwmon/mc34vr500.c
index 84458e4533d8..8cb9d5c09033 100644
--- a/drivers/hwmon/mc34vr500.c
+++ b/drivers/hwmon/mc34vr500.c
@@ -235,8 +235,8 @@ static int mc34vr500_probe(struct i2c_client *client)
}

static const struct i2c_device_id mc34vr500_id[] = {
- { "mc34vr500" },
- { },
+ { .name = "mc34vr500" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mc34vr500_id);

diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c
index bcddf6804d3a..d6b0ebf27941 100644
--- a/drivers/hwmon/mcp3021.c
+++ b/drivers/hwmon/mcp3021.c
@@ -177,8 +177,8 @@ static int mcp3021_probe(struct i2c_client *client)
}

static const struct i2c_device_id mcp3021_id[] = {
- { "mcp3021", mcp3021 },
- { "mcp3221", mcp3221 },
+ { .name = "mcp3021", .driver_data = mcp3021 },
+ { .name = "mcp3221", .driver_data = mcp3221 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mcp3021_id);
diff --git a/drivers/hwmon/nct6775-i2c.c b/drivers/hwmon/nct6775-i2c.c
index ba71d776a291..07783910c058 100644
--- a/drivers/hwmon/nct6775-i2c.c
+++ b/drivers/hwmon/nct6775-i2c.c
@@ -93,19 +93,19 @@ static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = {
MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match);

static const struct i2c_device_id nct6775_i2c_id[] = {
- { "nct6106", nct6106 },
- { "nct6116", nct6116 },
- { "nct6775", nct6775 },
- { "nct6776", nct6776 },
- { "nct6779", nct6779 },
- { "nct6791", nct6791 },
- { "nct6792", nct6792 },
- { "nct6793", nct6793 },
- { "nct6795", nct6795 },
- { "nct6796", nct6796 },
- { "nct6797", nct6797 },
- { "nct6798", nct6798 },
- { "nct6799", nct6799 },
+ { .name = "nct6106", .driver_data = nct6106 },
+ { .name = "nct6116", .driver_data = nct6116 },
+ { .name = "nct6775", .driver_data = nct6775 },
+ { .name = "nct6776", .driver_data = nct6776 },
+ { .name = "nct6779", .driver_data = nct6779 },
+ { .name = "nct6791", .driver_data = nct6791 },
+ { .name = "nct6792", .driver_data = nct6792 },
+ { .name = "nct6793", .driver_data = nct6793 },
+ { .name = "nct6795", .driver_data = nct6795 },
+ { .name = "nct6796", .driver_data = nct6796 },
+ { .name = "nct6797", .driver_data = nct6797 },
+ { .name = "nct6798", .driver_data = nct6798 },
+ { .name = "nct6799", .driver_data = nct6799 },
{ }
};
MODULE_DEVICE_TABLE(i2c, nct6775_i2c_id);
diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c
index 8c9351da12c6..9c3e169547a9 100644
--- a/drivers/hwmon/nct7802.c
+++ b/drivers/hwmon/nct7802.c
@@ -1193,7 +1193,7 @@ static const unsigned short nct7802_address_list[] = {
};

static const struct i2c_device_id nct7802_idtable[] = {
- { "nct7802" },
+ { .name = "nct7802" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nct7802_idtable);
diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c
index 2fa091720c79..976b8a008e44 100644
--- a/drivers/hwmon/nct7904.c
+++ b/drivers/hwmon/nct7904.c
@@ -1146,8 +1146,8 @@ static int nct7904_probe(struct i2c_client *client)
}

static const struct i2c_device_id nct7904_id[] = {
- {"nct7904"},
- {}
+ { .name = "nct7904" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, nct7904_id);

diff --git a/drivers/hwmon/nzxt-kraken3.c b/drivers/hwmon/nzxt-kraken3.c
index d00409bcab93..05525406c5fb 100644
--- a/drivers/hwmon/nzxt-kraken3.c
+++ b/drivers/hwmon/nzxt-kraken3.c
@@ -948,7 +948,7 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id
ret = kraken3_init_device(hdev);
if (ret < 0) {
hid_err(hdev, "device init failed with %d\n", ret);
- goto fail_and_close;
+ goto fail_and_stop_io;
}

ret = kraken3_get_fw_ver(hdev);
@@ -960,13 +960,15 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id
if (IS_ERR(priv->hwmon_dev)) {
ret = PTR_ERR(priv->hwmon_dev);
hid_err(hdev, "hwmon registration failed with %d\n", ret);
- goto fail_and_close;
+ goto fail_and_stop_io;
}

kraken3_debugfs_init(priv, device_name);

return 0;

+fail_and_stop_io:
+ hid_device_io_stop(hdev);
fail_and_close:
hid_hw_close(hdev);
fail_and_stop:
diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c
index 58ef9fa0184b..e2316c46629d 100644
--- a/drivers/hwmon/nzxt-smart2.c
+++ b/drivers/hwmon/nzxt-smart2.c
@@ -768,7 +768,7 @@ static int nzxt_smart2_hid_probe(struct hid_device *hdev,

out_hw_close:
hid_hw_close(hdev);
-
+ hid_device_io_stop(hdev);
out_hw_stop:
hid_hw_stop(hdev);
return ret;
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index e18e80e832fd..175208d712b0 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -1052,32 +1052,49 @@ static int occ_setup_sensor_attrs(struct occ *occ)
}

/* only need to do this once at startup, as OCC won't change sensors on us */
-static void occ_parse_poll_response(struct occ *occ)
+static int occ_parse_poll_response(struct occ *occ)
{
unsigned int i, old_offset, offset = 0, size = 0;
+ u16 data_length;
struct occ_sensor *sensor;
- struct occ_sensors *sensors = &occ->sensors;
+ struct occ_sensors parsed = {};
+ struct occ_sensors *sensors = &parsed;
struct occ_response *resp = &occ->resp;
struct occ_poll_response *poll =
(struct occ_poll_response *)&resp->data[0];
struct occ_poll_response_header *header = &poll->header;
struct occ_sensor_data_block *block = &poll->block;

+ data_length = get_unaligned_be16(&resp->data_length);
+ if (data_length < sizeof(*header) || data_length > OCC_RESP_DATA_BYTES) {
+ dev_err(occ->bus_dev, "invalid OCC poll response length %u\n",
+ data_length);
+ return -EMSGSIZE;
+ }
+
dev_info(occ->bus_dev, "OCC found, code level: %.16s\n",
header->occ_code_level);

for (i = 0; i < header->num_sensor_data_blocks; ++i) {
block = (struct occ_sensor_data_block *)((u8 *)block + offset);
+ if (size + sizeof(*header) + sizeof(block->header) >
+ data_length) {
+ dev_err(occ->bus_dev,
+ "truncated OCC sensor block header\n");
+ return -EMSGSIZE;
+ }
+
old_offset = offset;
offset = (block->header.num_sensors *
block->header.sensor_length) + sizeof(block->header);
- size += offset;

/* validate all the length/size fields */
- if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) {
- dev_warn(occ->bus_dev, "exceeded response buffer\n");
- return;
+ if (size + sizeof(*header) + offset > data_length) {
+ dev_err(occ->bus_dev,
+ "exceeded OCC poll response length\n");
+ return -EMSGSIZE;
}
+ size += offset;

dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n",
old_offset, offset - 1, block->header.eye_catcher,
@@ -1107,6 +1124,9 @@ static void occ_parse_poll_response(struct occ *occ)

dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size,
sizeof(*header), size + sizeof(*header));
+ occ->sensors = parsed;
+
+ return 0;
}

int occ_active(struct occ *occ, bool active)
@@ -1138,10 +1158,12 @@ int occ_active(struct occ *occ, bool active)
goto unlock;
}

- occ->active = true;
occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
- occ_parse_poll_response(occ);
+ rc = occ_parse_poll_response(occ);
+ if (rc)
+ goto unlock;

+ occ->active = true;
rc = occ_setup_sensor_attrs(occ);
if (rc) {
dev_err(occ->bus_dev,
diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c
index 167d2fe4d543..c0220ebfd4b6 100644
--- a/drivers/hwmon/pcf8591.c
+++ b/drivers/hwmon/pcf8591.c
@@ -285,7 +285,7 @@ static int pcf8591_read_channel(struct device *dev, int channel)
}

static const struct i2c_device_id pcf8591_id[] = {
- { "pcf8591" },
+ { .name = "pcf8591" },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcf8591_id);
diff --git a/drivers/hwmon/pmbus/acbel-fsg032.c b/drivers/hwmon/pmbus/acbel-fsg032.c
index 9f07fb4abaff..d283005d92ae 100644
--- a/drivers/hwmon/pmbus/acbel-fsg032.c
+++ b/drivers/hwmon/pmbus/acbel-fsg032.c
@@ -49,8 +49,8 @@ static void acbel_fsg032_init_debugfs(struct i2c_client *client)
}

static const struct i2c_device_id acbel_fsg032_id[] = {
- { "acbel_fsg032" },
- {}
+ { .name = "acbel_fsg032" },
+ { }
};

static struct pmbus_driver_info acbel_fsg032_info = {
diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c
index 6f6ad7b20e9a..360a7730b807 100644
--- a/drivers/hwmon/pmbus/adm1266.c
+++ b/drivers/hwmon/pmbus/adm1266.c
@@ -510,7 +510,7 @@ static const struct of_device_id adm1266_of_match[] = {
MODULE_DEVICE_TABLE(of, adm1266_of_match);

static const struct i2c_device_id adm1266_id[] = {
- { "adm1266" },
+ { .name = "adm1266" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adm1266_id);
diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c
index 43baa5ded35e..cf7790bef652 100644
--- a/drivers/hwmon/pmbus/adm1275.c
+++ b/drivers/hwmon/pmbus/adm1275.c
@@ -478,16 +478,16 @@ static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg)
}

static const struct i2c_device_id adm1275_id[] = {
- { "adm1075", adm1075 },
- { "adm1272", adm1272 },
- { "adm1273", adm1273 },
- { "adm1275", adm1275 },
- { "adm1276", adm1276 },
- { "adm1278", adm1278 },
- { "adm1281", adm1281 },
- { "adm1293", adm1293 },
- { "adm1294", adm1294 },
- { "mc09c", sq24905c },
+ { .name = "adm1075", .driver_data = adm1075 },
+ { .name = "adm1272", .driver_data = adm1272 },
+ { .name = "adm1273", .driver_data = adm1273 },
+ { .name = "adm1275", .driver_data = adm1275 },
+ { .name = "adm1276", .driver_data = adm1276 },
+ { .name = "adm1278", .driver_data = adm1278 },
+ { .name = "adm1281", .driver_data = adm1281 },
+ { .name = "adm1293", .driver_data = adm1293 },
+ { .name = "adm1294", .driver_data = adm1294 },
+ { .name = "mc09c", .driver_data = sq24905c },
{ }
};
MODULE_DEVICE_TABLE(i2c, adm1275_id);
diff --git a/drivers/hwmon/pmbus/aps-379.c b/drivers/hwmon/pmbus/aps-379.c
index 7d46cd647e20..3ec0940ae564 100644
--- a/drivers/hwmon/pmbus/aps-379.c
+++ b/drivers/hwmon/pmbus/aps-379.c
@@ -100,8 +100,8 @@ static struct pmbus_driver_info aps_379_info = {
};

static const struct i2c_device_id aps_379_id[] = {
- { "aps-379", 0 },
- {},
+ { .name = "aps-379" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, aps_379_id);

diff --git a/drivers/hwmon/pmbus/bel-pfe.c b/drivers/hwmon/pmbus/bel-pfe.c
index 6499556f735b..9e3dc9d29c56 100644
--- a/drivers/hwmon/pmbus/bel-pfe.c
+++ b/drivers/hwmon/pmbus/bel-pfe.c
@@ -106,9 +106,9 @@ static int pfe_pmbus_probe(struct i2c_client *client)
}

static const struct i2c_device_id pfe_device_id[] = {
- {"pfe1100", pfe1100},
- {"pfe3000", pfe3000},
- {}
+ { .name = "pfe1100", .driver_data = pfe1100 },
+ { .name = "pfe3000", .driver_data = pfe3000 },
+ { }
};

MODULE_DEVICE_TABLE(i2c, pfe_device_id);
diff --git a/drivers/hwmon/pmbus/bpa-rs600.c b/drivers/hwmon/pmbus/bpa-rs600.c
index 6c3875ba37a0..e364dcb59dd4 100644
--- a/drivers/hwmon/pmbus/bpa-rs600.c
+++ b/drivers/hwmon/pmbus/bpa-rs600.c
@@ -147,9 +147,9 @@ static struct pmbus_driver_info bpa_rs600_info = {
};

static const struct i2c_device_id bpa_rs600_id[] = {
- { "bpa-rs600", bpa_rs600 },
- { "bpd-rs600", bpd_rs600 },
- {},
+ { .name = "bpa-rs600", .driver_data = bpa_rs600 },
+ { .name = "bpd-rs600", .driver_data = bpd_rs600 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, bpa_rs600_id);

diff --git a/drivers/hwmon/pmbus/crps.c b/drivers/hwmon/pmbus/crps.c
index 164b33fed312..266ec8947519 100644
--- a/drivers/hwmon/pmbus/crps.c
+++ b/drivers/hwmon/pmbus/crps.c
@@ -10,8 +10,8 @@
#include "pmbus.h"

static const struct i2c_device_id crps_id[] = {
- { "intel_crps185" },
- {}
+ { .name = "intel_crps185" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, crps_id);

diff --git a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c
index 3850eaea75da..2df655b20ea5 100644
--- a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c
+++ b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c
@@ -103,7 +103,7 @@ static int ahe50dc_fan_probe(struct i2c_client *client)
}

static const struct i2c_device_id ahe50dc_fan_id[] = {
- { "ahe50dc_fan" },
+ { .name = "ahe50dc_fan" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ahe50dc_fan_id);
diff --git a/drivers/hwmon/pmbus/dps920ab.c b/drivers/hwmon/pmbus/dps920ab.c
index 325111a955e6..0d2901c314c2 100644
--- a/drivers/hwmon/pmbus/dps920ab.c
+++ b/drivers/hwmon/pmbus/dps920ab.c
@@ -191,8 +191,8 @@ static const struct of_device_id __maybe_unused dps920ab_of_match[] = {
MODULE_DEVICE_TABLE(of, dps920ab_of_match);

static const struct i2c_device_id dps920ab_device_id[] = {
- { "dps920ab" },
- {}
+ { .name = "dps920ab" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, dps920ab_device_id);

diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c
index cad4d2330003..44cf2db9364b 100644
--- a/drivers/hwmon/pmbus/fsp-3y.c
+++ b/drivers/hwmon/pmbus/fsp-3y.c
@@ -271,8 +271,8 @@ static int fsp3y_probe(struct i2c_client *client)
}

static const struct i2c_device_id fsp3y_id[] = {
- {"ym2151e", ym2151e},
- {"yh5151e", yh5151e},
+ { .name = "ym2151e", .driver_data = ym2151e },
+ { .name = "yh5151e", .driver_data = yh5151e },
{ }
};
MODULE_DEVICE_TABLE(i2c, fsp3y_id);
diff --git a/drivers/hwmon/pmbus/hac300s.c b/drivers/hwmon/pmbus/hac300s.c
index a073db1cfe2e..761e53890ebc 100644
--- a/drivers/hwmon/pmbus/hac300s.c
+++ b/drivers/hwmon/pmbus/hac300s.c
@@ -112,8 +112,8 @@ static const struct of_device_id hac300s_of_match[] = {
MODULE_DEVICE_TABLE(of, hac300s_of_match);

static const struct i2c_device_id hac300s_id[] = {
- {"hac300s", 0},
- {}
+ { .name = "hac300s" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, hac300s_id);

diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c
index 6c7256d997f4..aad94bcb9ceb 100644
--- a/drivers/hwmon/pmbus/ibm-cffps.c
+++ b/drivers/hwmon/pmbus/ibm-cffps.c
@@ -472,10 +472,10 @@ static struct pmbus_platform_data ibm_cffps_pdata = {
};

static const struct i2c_device_id ibm_cffps_id[] = {
- { "ibm_cffps1", cffps1 },
- { "ibm_cffps2", cffps2 },
- { "ibm_cffps", cffps_unknown },
- {}
+ { .name = "ibm_cffps1", .driver_data = cffps1 },
+ { .name = "ibm_cffps2", .driver_data = cffps2 },
+ { .name = "ibm_cffps", .driver_data = cffps_unknown },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);

diff --git a/drivers/hwmon/pmbus/ina233.c b/drivers/hwmon/pmbus/ina233.c
index 652087589c55..c72aa258b7c6 100644
--- a/drivers/hwmon/pmbus/ina233.c
+++ b/drivers/hwmon/pmbus/ina233.c
@@ -168,8 +168,8 @@ static int ina233_probe(struct i2c_client *client)
}

static const struct i2c_device_id ina233_id[] = {
- {"ina233", 0},
- {}
+ { .name = "ina233" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ina233_id);

diff --git a/drivers/hwmon/pmbus/inspur-ipsps.c b/drivers/hwmon/pmbus/inspur-ipsps.c
index 074e0f164ee1..57c0fc16909c 100644
--- a/drivers/hwmon/pmbus/inspur-ipsps.c
+++ b/drivers/hwmon/pmbus/inspur-ipsps.c
@@ -197,8 +197,8 @@ static int ipsps_probe(struct i2c_client *client)
}

static const struct i2c_device_id ipsps_id[] = {
- { "ipsps1" },
- {}
+ { .name = "ipsps1" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ipsps_id);

diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c
index 46d8f334d49a..b2120fc76f40 100644
--- a/drivers/hwmon/pmbus/ir35221.c
+++ b/drivers/hwmon/pmbus/ir35221.c
@@ -126,8 +126,8 @@ static int ir35221_probe(struct i2c_client *client)
}

static const struct i2c_device_id ir35221_id[] = {
- {"ir35221"},
- {}
+ { .name = "ir35221" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, ir35221_id);
diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c
index 34ce15fc708b..0dce4c3f666f 100644
--- a/drivers/hwmon/pmbus/ir36021.c
+++ b/drivers/hwmon/pmbus/ir36021.c
@@ -51,8 +51,8 @@ static int ir36021_probe(struct i2c_client *client)
}

static const struct i2c_device_id ir36021_id[] = {
- { "ir36021" },
- {},
+ { .name = "ir36021" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ir36021_id);

diff --git a/drivers/hwmon/pmbus/ir38064.c b/drivers/hwmon/pmbus/ir38064.c
index 7b4188e8bf48..47ce88e9d13a 100644
--- a/drivers/hwmon/pmbus/ir38064.c
+++ b/drivers/hwmon/pmbus/ir38064.c
@@ -53,11 +53,11 @@ static int ir38064_probe(struct i2c_client *client)
}

static const struct i2c_device_id ir38064_id[] = {
- {"ir38060"},
- {"ir38064"},
- {"ir38164"},
- {"ir38263"},
- {}
+ { .name = "ir38060" },
+ { .name = "ir38064" },
+ { .name = "ir38164" },
+ { .name = "ir38263" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, ir38064_id);
diff --git a/drivers/hwmon/pmbus/irps5401.c b/drivers/hwmon/pmbus/irps5401.c
index 43674c64841d..1694b96d7abf 100644
--- a/drivers/hwmon/pmbus/irps5401.c
+++ b/drivers/hwmon/pmbus/irps5401.c
@@ -44,8 +44,8 @@ static int irps5401_probe(struct i2c_client *client)
}

static const struct i2c_device_id irps5401_id[] = {
- {"irps5401"},
- {}
+ { .name = "irps5401" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, irps5401_id);
diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c
index 21d047b577a4..2f7f825bfb69 100644
--- a/drivers/hwmon/pmbus/isl68137.c
+++ b/drivers/hwmon/pmbus/isl68137.c
@@ -409,54 +409,54 @@ static int isl68137_probe(struct i2c_client *client)
}

static const struct i2c_device_id raa_dmpvr_id[] = {
- {"isl68137", raa_dmpvr1_2rail},
- {"isl68220", raa_dmpvr2_2rail},
- {"isl68221", raa_dmpvr2_3rail},
- {"isl68222", raa_dmpvr2_2rail},
- {"isl68223", raa_dmpvr2_2rail},
- {"isl68224", raa_dmpvr2_3rail},
- {"isl68225", raa_dmpvr2_2rail},
- {"isl68226", raa_dmpvr2_3rail},
- {"isl68227", raa_dmpvr2_1rail},
- {"isl68229", raa_dmpvr2_3rail},
- {"isl68233", raa_dmpvr2_2rail},
- {"isl68239", raa_dmpvr2_3rail},
-
- {"isl69222", raa_dmpvr2_2rail},
- {"isl69223", raa_dmpvr2_3rail},
- {"isl69224", raa_dmpvr2_2rail},
- {"isl69225", raa_dmpvr2_2rail},
- {"isl69227", raa_dmpvr2_3rail},
- {"isl69228", raa_dmpvr2_3rail},
- {"isl69234", raa_dmpvr2_2rail},
- {"isl69236", raa_dmpvr2_2rail},
- {"isl69239", raa_dmpvr2_3rail},
- {"isl69242", raa_dmpvr2_2rail},
- {"isl69243", raa_dmpvr2_1rail},
- {"isl69247", raa_dmpvr2_2rail},
- {"isl69248", raa_dmpvr2_2rail},
- {"isl69254", raa_dmpvr2_2rail},
- {"isl69255", raa_dmpvr2_2rail},
- {"isl69256", raa_dmpvr2_2rail},
- {"isl69259", raa_dmpvr2_2rail},
- {"isl69260", raa_dmpvr2_2rail},
- {"isl69268", raa_dmpvr2_2rail},
- {"isl69269", raa_dmpvr2_3rail},
- {"isl69298", raa_dmpvr2_2rail},
-
- {"raa228000", raa_dmpvr2_hv},
- {"raa228004", raa_dmpvr2_hv},
- {"raa228006", raa_dmpvr2_hv},
- {"raa228228", raa_dmpvr2_2rail_nontc},
- {"raa228244", raa_dmpvr2_2rail_nontc},
- {"raa228246", raa_dmpvr2_2rail_nontc},
- {"raa228942", raa_dmpvr2_2rail_nontc},
- {"raa228943", raa_dmpvr2_2rail_nontc},
- {"raa229001", raa_dmpvr2_2rail},
- {"raa229004", raa_dmpvr2_2rail},
- {"raa229141", raa_dmpvr2_2rail_pmbus},
- {"raa229621", raa_dmpvr2_2rail},
- {}
+ { .name = "isl68137", .driver_data = raa_dmpvr1_2rail },
+ { .name = "isl68220", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl68221", .driver_data = raa_dmpvr2_3rail },
+ { .name = "isl68222", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl68223", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl68224", .driver_data = raa_dmpvr2_3rail },
+ { .name = "isl68225", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl68226", .driver_data = raa_dmpvr2_3rail },
+ { .name = "isl68227", .driver_data = raa_dmpvr2_1rail },
+ { .name = "isl68229", .driver_data = raa_dmpvr2_3rail },
+ { .name = "isl68233", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl68239", .driver_data = raa_dmpvr2_3rail },
+
+ { .name = "isl69222", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69223", .driver_data = raa_dmpvr2_3rail },
+ { .name = "isl69224", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69225", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69227", .driver_data = raa_dmpvr2_3rail },
+ { .name = "isl69228", .driver_data = raa_dmpvr2_3rail },
+ { .name = "isl69234", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69236", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69239", .driver_data = raa_dmpvr2_3rail },
+ { .name = "isl69242", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69243", .driver_data = raa_dmpvr2_1rail },
+ { .name = "isl69247", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69248", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69254", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69255", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69256", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69259", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69260", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69268", .driver_data = raa_dmpvr2_2rail },
+ { .name = "isl69269", .driver_data = raa_dmpvr2_3rail },
+ { .name = "isl69298", .driver_data = raa_dmpvr2_2rail },
+
+ { .name = "raa228000", .driver_data = raa_dmpvr2_hv },
+ { .name = "raa228004", .driver_data = raa_dmpvr2_hv },
+ { .name = "raa228006", .driver_data = raa_dmpvr2_hv },
+ { .name = "raa228228", .driver_data = raa_dmpvr2_2rail_nontc },
+ { .name = "raa228244", .driver_data = raa_dmpvr2_2rail_nontc },
+ { .name = "raa228246", .driver_data = raa_dmpvr2_2rail_nontc },
+ { .name = "raa228942", .driver_data = raa_dmpvr2_2rail_nontc },
+ { .name = "raa228943", .driver_data = raa_dmpvr2_2rail_nontc },
+ { .name = "raa229001", .driver_data = raa_dmpvr2_2rail },
+ { .name = "raa229004", .driver_data = raa_dmpvr2_2rail },
+ { .name = "raa229141", .driver_data = raa_dmpvr2_2rail_pmbus },
+ { .name = "raa229621", .driver_data = raa_dmpvr2_2rail },
+ { }
};

MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id);
diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c
index dd7275a67a0a..0f7df7e2c9c5 100644
--- a/drivers/hwmon/pmbus/lm25066.c
+++ b/drivers/hwmon/pmbus/lm25066.c
@@ -442,11 +442,11 @@ static const struct regulator_desc lm25066_reg_desc[] = {
#endif

static const struct i2c_device_id lm25066_id[] = {
- {"lm25056", lm25056},
- {"lm25066", lm25066},
- {"lm5064", lm5064},
- {"lm5066", lm5066},
- {"lm5066i", lm5066i},
+ { .name = "lm25056", .driver_data = lm25056 },
+ { .name = "lm25066", .driver_data = lm25066 },
+ { .name = "lm5064", .driver_data = lm5064 },
+ { .name = "lm5066", .driver_data = lm5066 },
+ { .name = "lm5066i", .driver_data = lm5066i },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm25066_id);
diff --git a/drivers/hwmon/pmbus/lt3074.c b/drivers/hwmon/pmbus/lt3074.c
index 3704dbe7b54a..ed932ddb4f77 100644
--- a/drivers/hwmon/pmbus/lt3074.c
+++ b/drivers/hwmon/pmbus/lt3074.c
@@ -95,8 +95,8 @@ static int lt3074_probe(struct i2c_client *client)
}

static const struct i2c_device_id lt3074_id[] = {
- { "lt3074", 0 },
- {}
+ { .name = "lt3074" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lt3074_id);

diff --git a/drivers/hwmon/pmbus/lt7182s.c b/drivers/hwmon/pmbus/lt7182s.c
index 9d6d50f39bd6..f8971a786f25 100644
--- a/drivers/hwmon/pmbus/lt7182s.c
+++ b/drivers/hwmon/pmbus/lt7182s.c
@@ -168,8 +168,8 @@ static int lt7182s_probe(struct i2c_client *client)
}

static const struct i2c_device_id lt7182s_id[] = {
- { "lt7182s" },
- {}
+ { .name = "lt7182s" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lt7182s_id);

diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
index d69a5e675e80..10877b0867fd 100644
--- a/drivers/hwmon/pmbus/ltc2978.c
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -538,36 +538,36 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page,
}

static const struct i2c_device_id ltc2978_id[] = {
- {"lt7170", lt7170},
- {"lt7171", lt7171},
- {"ltc2972", ltc2972},
- {"ltc2974", ltc2974},
- {"ltc2975", ltc2975},
- {"ltc2977", ltc2977},
- {"ltc2978", ltc2978},
- {"ltc2979", ltc2979},
- {"ltc2980", ltc2980},
- {"ltc3880", ltc3880},
- {"ltc3882", ltc3882},
- {"ltc3883", ltc3883},
- {"ltc3884", ltc3884},
- {"ltc3886", ltc3886},
- {"ltc3887", ltc3887},
- {"ltc3889", ltc3889},
- {"ltc7132", ltc7132},
- {"ltc7841", ltc7841},
- {"ltc7880", ltc7880},
- {"ltm2987", ltm2987},
- {"ltm4664", ltm4664},
- {"ltm4673", ltm4673},
- {"ltm4675", ltm4675},
- {"ltm4676", ltm4676},
- {"ltm4677", ltm4677},
- {"ltm4678", ltm4678},
- {"ltm4680", ltm4680},
- {"ltm4686", ltm4686},
- {"ltm4700", ltm4700},
- {}
+ { .name = "lt7170", .driver_data = lt7170 },
+ { .name = "lt7171", .driver_data = lt7171 },
+ { .name = "ltc2972", .driver_data = ltc2972 },
+ { .name = "ltc2974", .driver_data = ltc2974 },
+ { .name = "ltc2975", .driver_data = ltc2975 },
+ { .name = "ltc2977", .driver_data = ltc2977 },
+ { .name = "ltc2978", .driver_data = ltc2978 },
+ { .name = "ltc2979", .driver_data = ltc2979 },
+ { .name = "ltc2980", .driver_data = ltc2980 },
+ { .name = "ltc3880", .driver_data = ltc3880 },
+ { .name = "ltc3882", .driver_data = ltc3882 },
+ { .name = "ltc3883", .driver_data = ltc3883 },
+ { .name = "ltc3884", .driver_data = ltc3884 },
+ { .name = "ltc3886", .driver_data = ltc3886 },
+ { .name = "ltc3887", .driver_data = ltc3887 },
+ { .name = "ltc3889", .driver_data = ltc3889 },
+ { .name = "ltc7132", .driver_data = ltc7132 },
+ { .name = "ltc7841", .driver_data = ltc7841 },
+ { .name = "ltc7880", .driver_data = ltc7880 },
+ { .name = "ltm2987", .driver_data = ltm2987 },
+ { .name = "ltm4664", .driver_data = ltm4664 },
+ { .name = "ltm4673", .driver_data = ltm4673 },
+ { .name = "ltm4675", .driver_data = ltm4675 },
+ { .name = "ltm4676", .driver_data = ltm4676 },
+ { .name = "ltm4677", .driver_data = ltm4677 },
+ { .name = "ltm4678", .driver_data = ltm4678 },
+ { .name = "ltm4680", .driver_data = ltm4680 },
+ { .name = "ltm4686", .driver_data = ltm4686 },
+ { .name = "ltm4700", .driver_data = ltm4700 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ltc2978_id);

diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c
index 824c16a75e2c..0219d03adb03 100644
--- a/drivers/hwmon/pmbus/ltc3815.c
+++ b/drivers/hwmon/pmbus/ltc3815.c
@@ -143,7 +143,7 @@ static int ltc3815_write_word_data(struct i2c_client *client, int page,
}

static const struct i2c_device_id ltc3815_id[] = {
- {"ltc3815"},
+ { .name = "ltc3815" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ltc3815_id);
diff --git a/drivers/hwmon/pmbus/max15301.c b/drivers/hwmon/pmbus/max15301.c
index d5810b88ea8d..4c36f1ea27ee 100644
--- a/drivers/hwmon/pmbus/max15301.c
+++ b/drivers/hwmon/pmbus/max15301.c
@@ -23,10 +23,10 @@
#include "pmbus.h"

static const struct i2c_device_id max15301_id[] = {
- { "bmr461" },
- { "max15301" },
- { "max15303" },
- {}
+ { .name = "bmr461" },
+ { .name = "max15301" },
+ { .name = "max15303" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, max15301_id);

diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c
index eb84915c2a83..351a1f535999 100644
--- a/drivers/hwmon/pmbus/max16064.c
+++ b/drivers/hwmon/pmbus/max16064.c
@@ -91,8 +91,8 @@ static int max16064_probe(struct i2c_client *client)
}

static const struct i2c_device_id max16064_id[] = {
- {"max16064"},
- {}
+ { .name = "max16064" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, max16064_id);
diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c
index 36dc13424d92..3dd1f6fd003b 100644
--- a/drivers/hwmon/pmbus/max16601.c
+++ b/drivers/hwmon/pmbus/max16601.c
@@ -263,11 +263,11 @@ static void max16601_remove(void *_data)
}

static const struct i2c_device_id max16601_id[] = {
- {"max16508", max16508},
- {"max16600", max16600},
- {"max16601", max16601},
- {"max16602", max16602},
- {}
+ { .name = "max16508", .driver_data = max16508 },
+ { .name = "max16600", .driver_data = max16600 },
+ { .name = "max16601", .driver_data = max16601 },
+ { .name = "max16602", .driver_data = max16602 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, max16601_id);

diff --git a/drivers/hwmon/pmbus/max17616.c b/drivers/hwmon/pmbus/max17616.c
index 1d4a0ddb95bb..744fa5aefe93 100644
--- a/drivers/hwmon/pmbus/max17616.c
+++ b/drivers/hwmon/pmbus/max17616.c
@@ -46,7 +46,7 @@ static int max17616_probe(struct i2c_client *client)
}

static const struct i2c_device_id max17616_id[] = {
- { "max17616" },
+ { .name = "max17616" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max17616_id);
diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c
index fe03164788df..4031f894e6ae 100644
--- a/drivers/hwmon/pmbus/max20730.c
+++ b/drivers/hwmon/pmbus/max20730.c
@@ -751,11 +751,11 @@ static int max20730_probe(struct i2c_client *client)
}

static const struct i2c_device_id max20730_id[] = {
- { "max20710", max20710 },
- { "max20730", max20730 },
- { "max20734", max20734 },
- { "max20743", max20743 },
- { },
+ { .name = "max20710", .driver_data = max20710 },
+ { .name = "max20730", .driver_data = max20730 },
+ { .name = "max20734", .driver_data = max20734 },
+ { .name = "max20743", .driver_data = max20743 },
+ { }
};

MODULE_DEVICE_TABLE(i2c, max20730_id);
diff --git a/drivers/hwmon/pmbus/max20751.c b/drivers/hwmon/pmbus/max20751.c
index ac8c43122133..e85676433dca 100644
--- a/drivers/hwmon/pmbus/max20751.c
+++ b/drivers/hwmon/pmbus/max20751.c
@@ -32,8 +32,8 @@ static int max20751_probe(struct i2c_client *client)
}

static const struct i2c_device_id max20751_id[] = {
- {"max20751"},
- {}
+ { .name = "max20751" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, max20751_id);
diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c
index 3caa76bcbeb5..bdcce810a479 100644
--- a/drivers/hwmon/pmbus/max31785.c
+++ b/drivers/hwmon/pmbus/max31785.c
@@ -447,10 +447,10 @@ static int max31785_probe(struct i2c_client *client)
}

static const struct i2c_device_id max31785_id[] = {
- { "max31785" },
- { "max31785a" },
- { "max31785b" },
- { },
+ { .name = "max31785" },
+ { .name = "max31785a" },
+ { .name = "max31785b" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, max31785_id);
diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c
index cc96bb22f8f5..e56057e9273c 100644
--- a/drivers/hwmon/pmbus/max34440.c
+++ b/drivers/hwmon/pmbus/max34440.c
@@ -18,6 +18,7 @@
enum chips {
adpm12160,
adpm12200,
+ adpm12250,
max34440,
max34441,
max34446,
@@ -87,6 +88,33 @@ static int max34440_read_word_data(struct i2c_client *client, int page,
ret = pmbus_read_word_data(client, page, phase,
data->iout_oc_warn_limit);
break;
+ case PMBUS_VIN_OV_FAULT_LIMIT:
+ case PMBUS_VIN_OV_WARN_LIMIT:
+ case PMBUS_VIN_UV_WARN_LIMIT:
+ case PMBUS_VIN_UV_FAULT_LIMIT:
+ case PMBUS_MFR_VIN_MIN:
+ case PMBUS_MFR_VIN_MAX:
+ case PMBUS_IIN_OC_WARN_LIMIT:
+ case PMBUS_IIN_OC_FAULT_LIMIT:
+ case PMBUS_MFR_IIN_MAX:
+ case PMBUS_MFR_VOUT_MIN:
+ case PMBUS_MFR_VOUT_MAX:
+ case PMBUS_IOUT_UC_FAULT_LIMIT:
+ case PMBUS_MFR_IOUT_MAX:
+ case PMBUS_UT_WARN_LIMIT:
+ case PMBUS_UT_FAULT_LIMIT:
+ case PMBUS_MFR_MAX_TEMP_1:
+ /*
+ * MAX34451/ADPM family do not support VIN/IIN limit registers,
+ * manufacturer-specific min/max registers, or undercurrent/
+ * undertemperature fault limits. Accessing these triggers CML
+ * error and asserts ALERT.
+ */
+ if (data->id == max34451 || data->id == adpm12160 ||
+ data->id == adpm12200 || data->id == adpm12250)
+ return -ENXIO;
+ ret = -ENODATA;
+ break;
case PMBUS_VIRT_READ_VOUT_MIN:
ret = pmbus_read_word_data(client, page, phase,
MAX34440_MFR_VOUT_MIN);
@@ -97,7 +125,8 @@ static int max34440_read_word_data(struct i2c_client *client, int page,
break;
case PMBUS_VIRT_READ_IOUT_AVG:
if (data->id != max34446 && data->id != max34451 &&
- data->id != adpm12160 && data->id != adpm12200)
+ data->id != adpm12160 && data->id != adpm12200 &&
+ data->id != adpm12250)
return -ENXIO;
ret = pmbus_read_word_data(client, page, phase,
MAX34446_MFR_IOUT_AVG);
@@ -182,7 +211,8 @@ static int max34440_write_word_data(struct i2c_client *client, int page,
ret = pmbus_write_word_data(client, page,
MAX34440_MFR_IOUT_PEAK, 0);
if (!ret && (data->id == max34446 || data->id == max34451 ||
- data->id == adpm12160 || data->id == adpm12200))
+ data->id == adpm12160 || data->id == adpm12200 ||
+ data->id == adpm12250))
ret = pmbus_write_word_data(client, page,
MAX34446_MFR_IOUT_AVG, 0);

@@ -241,6 +271,51 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
return ret;
}

+static int max34451_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct max34440_data *data = to_max34440_data(info);
+
+ switch (reg) {
+ case PMBUS_STATUS_BYTE:
+ case PMBUS_STATUS_OTHER:
+ /*
+ * MAX34451/ADPM family do not support STATUS_BYTE or
+ * STATUS_OTHER registers. Accessing them triggers CML
+ * error and asserts ALERT.
+ */
+ if (data->id == max34451 || data->id == adpm12160 ||
+ data->id == adpm12200 || data->id == adpm12250)
+ return -ENXIO;
+ return -ENODATA;
+ default:
+ return -ENODATA;
+ }
+}
+
+static int max34451_write_byte_data(struct i2c_client *client, int page,
+ int reg, u8 byte)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ const struct max34440_data *data = to_max34440_data(info);
+
+ switch (reg) {
+ case PMBUS_STATUS_BYTE:
+ case PMBUS_STATUS_OTHER:
+ /*
+ * MAX34451/ADPM family do not support STATUS_BYTE or
+ * STATUS_OTHER registers. Writing to them triggers CML
+ * error and asserts ALERT.
+ */
+ if (data->id == max34451 || data->id == adpm12160 ||
+ data->id == adpm12200 || data->id == adpm12250)
+ return -ENXIO;
+ return -ENODATA;
+ default:
+ return -ENODATA;
+ }
+}
+
static int max34451_set_supported_funcs(struct i2c_client *client,
struct max34440_data *data)
{
@@ -360,7 +435,9 @@ static struct pmbus_driver_info max34440_info[] = {
.func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT,
.func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT,
.func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .read_byte_data = max34451_read_byte_data,
.read_word_data = max34440_read_word_data,
+ .write_byte_data = max34451_write_byte_data,
.write_word_data = max34440_write_word_data,
},
[adpm12200] = {
@@ -396,7 +473,45 @@ static struct pmbus_driver_info max34440_info[] = {
.func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT,
.func[14] = PMBUS_HAVE_IOUT,
.func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .read_byte_data = max34451_read_byte_data,
+ .read_word_data = max34440_read_word_data,
+ .write_byte_data = max34451_write_byte_data,
+ .write_word_data = max34440_write_word_data,
+ },
+ [adpm12250] = {
+ .pages = 19,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_CURRENT_IN] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_VOLTAGE_IN] = 125,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = 0,
+ .m[PSC_VOLTAGE_OUT] = 125,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 0,
+ .m[PSC_CURRENT_IN] = 250,
+ .b[PSC_CURRENT_IN] = 0,
+ .R[PSC_CURRENT_IN] = -1,
+ .m[PSC_CURRENT_OUT] = 250,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = -1,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 2,
+ /* absent func below [18] are not for monitoring */
+ .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[4] = PMBUS_HAVE_STATUS_IOUT,
+ .func[5] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[6] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT,
+ .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT,
+ .func[14] = PMBUS_HAVE_IOUT,
+ .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .read_byte_data = max34451_read_byte_data,
.read_word_data = max34440_read_word_data,
+ .write_byte_data = max34451_write_byte_data,
.write_word_data = max34440_write_word_data,
},
[max34440] = {
@@ -544,7 +659,9 @@ static struct pmbus_driver_info max34440_info[] = {
.func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .read_byte_data = max34451_read_byte_data,
.read_word_data = max34440_read_word_data,
+ .write_byte_data = max34451_write_byte_data,
.write_word_data = max34440_write_word_data,
.page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
@@ -635,7 +752,8 @@ static int max34440_probe(struct i2c_client *client)
rv = max34451_set_supported_funcs(client, data);
if (rv)
return rv;
- } else if (data->id == adpm12160 || data->id == adpm12200) {
+ } else if (data->id == adpm12160 || data->id == adpm12200 ||
+ data->id == adpm12250) {
data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT;
data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT;
}
@@ -644,15 +762,16 @@ static int max34440_probe(struct i2c_client *client)
}

static const struct i2c_device_id max34440_id[] = {
- {"adpm12160", adpm12160},
- {"adpm12200", adpm12200},
- {"max34440", max34440},
- {"max34441", max34441},
- {"max34446", max34446},
- {"max34451", max34451},
- {"max34460", max34460},
- {"max34461", max34461},
- {}
+ { .name = "adpm12160", .driver_data = adpm12160 },
+ { .name = "adpm12200", .driver_data = adpm12200 },
+ { .name = "adpm12250", .driver_data = adpm12250 },
+ { .name = "max34440", .driver_data = max34440 },
+ { .name = "max34441", .driver_data = max34441 },
+ { .name = "max34446", .driver_data = max34446 },
+ { .name = "max34451", .driver_data = max34451 },
+ { .name = "max34460", .driver_data = max34460 },
+ { .name = "max34461", .driver_data = max34461 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, max34440_id);

diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c
index b3a2a7492bbf..212b28fa4bf2 100644
--- a/drivers/hwmon/pmbus/max8688.c
+++ b/drivers/hwmon/pmbus/max8688.c
@@ -171,7 +171,7 @@ static int max8688_probe(struct i2c_client *client)
}

static const struct i2c_device_id max8688_id[] = {
- {"max8688"},
+ { .name = "max8688" },
{ }
};

diff --git a/drivers/hwmon/pmbus/mp2856.c b/drivers/hwmon/pmbus/mp2856.c
index e83c70a3583f..3d6621e36fd3 100644
--- a/drivers/hwmon/pmbus/mp2856.c
+++ b/drivers/hwmon/pmbus/mp2856.c
@@ -54,9 +54,9 @@ static const int mp2856_max_phases[][MP2856_PAGE_NUM] = {
};

static const struct i2c_device_id mp2856_id[] = {
- {"mp2856", mp2856},
- {"mp2857", mp2857},
- {}
+ { .name = "mp2856", .driver_data = mp2856 },
+ { .name = "mp2857", .driver_data = mp2857 },
+ { }
};

MODULE_DEVICE_TABLE(i2c, mp2856_id);
diff --git a/drivers/hwmon/pmbus/mp2888.c b/drivers/hwmon/pmbus/mp2888.c
index 772a623ca7d0..c5f35daa3fe1 100644
--- a/drivers/hwmon/pmbus/mp2888.c
+++ b/drivers/hwmon/pmbus/mp2888.c
@@ -378,8 +378,8 @@ static int mp2888_probe(struct i2c_client *client)
}

static const struct i2c_device_id mp2888_id[] = {
- {"mp2888"},
- {}
+ { .name = "mp2888" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, mp2888_id);
diff --git a/drivers/hwmon/pmbus/mp2891.c b/drivers/hwmon/pmbus/mp2891.c
index f8f4c91ec23c..316c1fc0d6cc 100644
--- a/drivers/hwmon/pmbus/mp2891.c
+++ b/drivers/hwmon/pmbus/mp2891.c
@@ -572,7 +572,7 @@ static int mp2891_probe(struct i2c_client *client)
}

static const struct i2c_device_id mp2891_id[] = {
- { "mp2891" },
+ { .name = "mp2891" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mp2891_id);
diff --git a/drivers/hwmon/pmbus/mp2925.c b/drivers/hwmon/pmbus/mp2925.c
index ad094842cf2d..0a58b1ffd791 100644
--- a/drivers/hwmon/pmbus/mp2925.c
+++ b/drivers/hwmon/pmbus/mp2925.c
@@ -305,9 +305,9 @@ static int mp2925_probe(struct i2c_client *client)
}

static const struct i2c_device_id mp2925_id[] = {
- {"mp2925"},
- {"mp2929"},
- {}
+ { .name = "mp2925" },
+ { .name = "mp2929" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mp2925_id);

diff --git a/drivers/hwmon/pmbus/mp29502.c b/drivers/hwmon/pmbus/mp29502.c
index 7241373f1557..afc5e8c07e25 100644
--- a/drivers/hwmon/pmbus/mp29502.c
+++ b/drivers/hwmon/pmbus/mp29502.c
@@ -642,8 +642,8 @@ static int mp29502_probe(struct i2c_client *client)
}

static const struct i2c_device_id mp29502_id[] = {
- {"mp29502", 0},
- {}
+ { .name = "mp29502", .driver_data = 0 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mp29502_id);

diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c
index d0bc47b12cb0..dca7e2fbcb44 100644
--- a/drivers/hwmon/pmbus/mp2975.c
+++ b/drivers/hwmon/pmbus/mp2975.c
@@ -1082,10 +1082,10 @@ static const struct of_device_id mp2975_of_match[] = {
MODULE_DEVICE_TABLE(of, mp2975_of_match);

static const struct i2c_device_id mp2975_id[] = {
- {"mp2971", (kernel_ulong_t)&mp2975_ddinfo[mp2971]},
- {"mp2973", (kernel_ulong_t)&mp2975_ddinfo[mp2973]},
- {"mp2975", (kernel_ulong_t)&mp2975_ddinfo[mp2975]},
- {}
+ { .name = "mp2971", .driver_data = (kernel_ulong_t)&mp2975_ddinfo[mp2971] },
+ { .name = "mp2973", .driver_data = (kernel_ulong_t)&mp2975_ddinfo[mp2973] },
+ { .name = "mp2975", .driver_data = (kernel_ulong_t)&mp2975_ddinfo[mp2975] },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mp2975_id);

diff --git a/drivers/hwmon/pmbus/mp2993.c b/drivers/hwmon/pmbus/mp2993.c
index 81c84fc8ed47..3a6a6c55a1f1 100644
--- a/drivers/hwmon/pmbus/mp2993.c
+++ b/drivers/hwmon/pmbus/mp2993.c
@@ -233,7 +233,7 @@ static int mp2993_probe(struct i2c_client *client)
}

static const struct i2c_device_id mp2993_id[] = {
- { "mp2993" },
+ { .name = "mp2993" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mp2993_id);
diff --git a/drivers/hwmon/pmbus/mp5920.c b/drivers/hwmon/pmbus/mp5920.c
index 319ae2721bcf..b803f3ddf8ea 100644
--- a/drivers/hwmon/pmbus/mp5920.c
+++ b/drivers/hwmon/pmbus/mp5920.c
@@ -66,7 +66,7 @@ static const struct of_device_id mp5920_of_match[] = {
MODULE_DEVICE_TABLE(of, mp5920_of_match);

static const struct i2c_device_id mp5920_id[] = {
- { "mp5920" },
+ { .name = "mp5920" },
{ }
};

diff --git a/drivers/hwmon/pmbus/mp5926.c b/drivers/hwmon/pmbus/mp5926.c
index f0d1b30c7013..a40647472b16 100644
--- a/drivers/hwmon/pmbus/mp5926.c
+++ b/drivers/hwmon/pmbus/mp5926.c
@@ -157,8 +157,8 @@ static int mp5926_probe(struct i2c_client *client)
}

static const struct i2c_device_id mp5926_id[] = {
- { "mp5926", 0 },
- {}
+ { .name = "mp5926", .driver_data = 0 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mp5926_id);

diff --git a/drivers/hwmon/pmbus/mp5990.c b/drivers/hwmon/pmbus/mp5990.c
index 9a4ee79712cf..f8a5d1b42ff7 100644
--- a/drivers/hwmon/pmbus/mp5990.c
+++ b/drivers/hwmon/pmbus/mp5990.c
@@ -148,8 +148,8 @@ static struct pmbus_driver_info mp5998_info = {
};

static const struct i2c_device_id mp5990_id[] = {
- {"mp5990", mp5990},
- {"mp5998", mp5998},
+ { .name = "mp5990", .driver_data = mp5990 },
+ { .name = "mp5998", .driver_data = mp5998 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mp5990_id);
diff --git a/drivers/hwmon/pmbus/mp9941.c b/drivers/hwmon/pmbus/mp9941.c
index 42ca6748777a..d9049d326c80 100644
--- a/drivers/hwmon/pmbus/mp9941.c
+++ b/drivers/hwmon/pmbus/mp9941.c
@@ -291,7 +291,7 @@ static int mp9941_probe(struct i2c_client *client)
}

static const struct i2c_device_id mp9941_id[] = {
- { "mp9941" },
+ { .name = "mp9941" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mp9941_id);
diff --git a/drivers/hwmon/pmbus/mp9945.c b/drivers/hwmon/pmbus/mp9945.c
index 34822e0de812..199480d85515 100644
--- a/drivers/hwmon/pmbus/mp9945.c
+++ b/drivers/hwmon/pmbus/mp9945.c
@@ -215,8 +215,8 @@ static int mp9945_probe(struct i2c_client *client)
}

static const struct i2c_device_id mp9945_id[] = {
- {"mp9945"},
- {}
+ { .name = "mp9945" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mp9945_id);

diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c
index 8f10e37a7a76..f49610adff89 100644
--- a/drivers/hwmon/pmbus/mpq7932.c
+++ b/drivers/hwmon/pmbus/mpq7932.c
@@ -145,9 +145,9 @@ static const struct of_device_id mpq7932_of_match[] = {
MODULE_DEVICE_TABLE(of, mpq7932_of_match);

static const struct i2c_device_id mpq7932_id[] = {
- { "mpq2286", },
- { "mpq7932", },
- { },
+ { .name = "mpq2286" },
+ { .name = "mpq7932" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mpq7932_id);

diff --git a/drivers/hwmon/pmbus/mpq8785.c b/drivers/hwmon/pmbus/mpq8785.c
index 87bd039c77b9..bbde55028290 100644
--- a/drivers/hwmon/pmbus/mpq8785.c
+++ b/drivers/hwmon/pmbus/mpq8785.c
@@ -110,11 +110,11 @@ static struct pmbus_driver_info mpq8785_info = {
};

static const struct i2c_device_id mpq8785_id[] = {
- { "mpm3695", mpm3695 },
- { "mpm3695-25", mpm3695_25 },
- { "mpm82504", mpm82504 },
- { "mpq8785", mpq8785 },
- { },
+ { .name = "mpm3695", .driver_data = mpm3695 },
+ { .name = "mpm3695-25", .driver_data = mpm3695_25 },
+ { .name = "mpm82504", .driver_data = mpm82504 },
+ { .name = "mpq8785", .driver_data = mpq8785 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mpq8785_id);

diff --git a/drivers/hwmon/pmbus/pim4328.c b/drivers/hwmon/pmbus/pim4328.c
index aa98284bbdd8..9056dc387e87 100644
--- a/drivers/hwmon/pmbus/pim4328.c
+++ b/drivers/hwmon/pmbus/pim4328.c
@@ -39,15 +39,15 @@ struct pim4328_data {
#define PIM4328_MFR_READ_STATUS 0xd0

static const struct i2c_device_id pim4328_id[] = {
- {"bmr455", pim4328},
- {"pim4006", pim4006},
- {"pim4106", pim4006},
- {"pim4206", pim4006},
- {"pim4306", pim4006},
- {"pim4328", pim4328},
- {"pim4406", pim4006},
- {"pim4820", pim4820},
- {}
+ { .name = "bmr455", .driver_data = pim4328 },
+ { .name = "pim4006", .driver_data = pim4006 },
+ { .name = "pim4106", .driver_data = pim4006 },
+ { .name = "pim4206", .driver_data = pim4006 },
+ { .name = "pim4306", .driver_data = pim4006 },
+ { .name = "pim4328", .driver_data = pim4328 },
+ { .name = "pim4406", .driver_data = pim4006 },
+ { .name = "pim4820", .driver_data = pim4820 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, pim4328_id);

diff --git a/drivers/hwmon/pmbus/pli1209bc.c b/drivers/hwmon/pmbus/pli1209bc.c
index 569b61dc1a32..d5ab085a2273 100644
--- a/drivers/hwmon/pmbus/pli1209bc.c
+++ b/drivers/hwmon/pmbus/pli1209bc.c
@@ -117,8 +117,8 @@ static int pli1209bc_probe(struct i2c_client *client)
}

static const struct i2c_device_id pli1209bc_id[] = {
- {"pli1209bc"},
- {}
+ { .name = "pli1209bc" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, pli1209bc_id);
diff --git a/drivers/hwmon/pmbus/pm6764tr.c b/drivers/hwmon/pmbus/pm6764tr.c
index c96c0aecb920..613654b15b4a 100644
--- a/drivers/hwmon/pmbus/pm6764tr.c
+++ b/drivers/hwmon/pmbus/pm6764tr.c
@@ -48,8 +48,8 @@ static int pm6764tr_probe(struct i2c_client *client)
}

static const struct i2c_device_id pm6764tr_id[] = {
- {"pm6764tr"},
- {}
+ { .name = "pm6764tr" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, pm6764tr_id);

diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c
index d1844c7a51ee..34945ccd3afc 100644
--- a/drivers/hwmon/pmbus/pmbus.c
+++ b/drivers/hwmon/pmbus/pmbus.c
@@ -213,36 +213,36 @@ static const struct pmbus_device_info pmbus_info_one_status = {
* Use driver_data to set the number of pages supported by the chip.
*/
static const struct i2c_device_id pmbus_id[] = {
- {"adp4000", (kernel_ulong_t)&pmbus_info_one},
- {"bmr310", (kernel_ulong_t)&pmbus_info_one_status},
- {"bmr453", (kernel_ulong_t)&pmbus_info_one},
- {"bmr454", (kernel_ulong_t)&pmbus_info_one},
- {"bmr456", (kernel_ulong_t)&pmbus_info_one},
- {"bmr457", (kernel_ulong_t)&pmbus_info_one},
- {"bmr458", (kernel_ulong_t)&pmbus_info_one_status},
- {"bmr480", (kernel_ulong_t)&pmbus_info_one_status},
- {"bmr490", (kernel_ulong_t)&pmbus_info_one_status},
- {"bmr491", (kernel_ulong_t)&pmbus_info_one_status},
- {"bmr492", (kernel_ulong_t)&pmbus_info_one},
- {"dps460", (kernel_ulong_t)&pmbus_info_one_skip},
- {"dps650ab", (kernel_ulong_t)&pmbus_info_one_skip},
- {"dps800", (kernel_ulong_t)&pmbus_info_one_skip},
- {"max20796", (kernel_ulong_t)&pmbus_info_one},
- {"mdt040", (kernel_ulong_t)&pmbus_info_one},
- {"ncp4200", (kernel_ulong_t)&pmbus_info_one},
- {"ncp4208", (kernel_ulong_t)&pmbus_info_one},
- {"pdt003", (kernel_ulong_t)&pmbus_info_one},
- {"pdt006", (kernel_ulong_t)&pmbus_info_one},
- {"pdt012", (kernel_ulong_t)&pmbus_info_one},
- {"pmbus", (kernel_ulong_t)&pmbus_info_zero},
- {"sgd009", (kernel_ulong_t)&pmbus_info_one_skip},
- {"tps40400", (kernel_ulong_t)&pmbus_info_one},
- {"tps544b20", (kernel_ulong_t)&pmbus_info_one},
- {"tps544b25", (kernel_ulong_t)&pmbus_info_one},
- {"tps544c20", (kernel_ulong_t)&pmbus_info_one},
- {"tps544c25", (kernel_ulong_t)&pmbus_info_one},
- {"udt020", (kernel_ulong_t)&pmbus_info_one},
- {}
+ { .name = "adp4000", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "bmr310", .driver_data = (kernel_ulong_t)&pmbus_info_one_status },
+ { .name = "bmr453", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "bmr454", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "bmr456", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "bmr457", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "bmr458", .driver_data = (kernel_ulong_t)&pmbus_info_one_status },
+ { .name = "bmr480", .driver_data = (kernel_ulong_t)&pmbus_info_one_status },
+ { .name = "bmr490", .driver_data = (kernel_ulong_t)&pmbus_info_one_status },
+ { .name = "bmr491", .driver_data = (kernel_ulong_t)&pmbus_info_one_status },
+ { .name = "bmr492", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "dps460", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip },
+ { .name = "dps650ab", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip },
+ { .name = "dps800", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip },
+ { .name = "max20796", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "mdt040", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "ncp4200", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "ncp4208", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "pdt003", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "pdt006", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "pdt012", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "pmbus", .driver_data = (kernel_ulong_t)&pmbus_info_zero },
+ { .name = "sgd009", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip },
+ { .name = "tps40400", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "tps544b20", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "tps544b25", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "tps544c20", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "tps544c25", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { .name = "udt020", .driver_data = (kernel_ulong_t)&pmbus_info_one },
+ { }
};

MODULE_DEVICE_TABLE(i2c, pmbus_id);
diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c
index 24c1f961c766..6ba2b3e0e565 100644
--- a/drivers/hwmon/pmbus/pxe1610.c
+++ b/drivers/hwmon/pmbus/pxe1610.c
@@ -130,10 +130,10 @@ static int pxe1610_probe(struct i2c_client *client)
}

static const struct i2c_device_id pxe1610_id[] = {
- {"pxe1610"},
- {"pxe1110"},
- {"pxm1310"},
- {}
+ { .name = "pxe1610" },
+ { .name = "pxe1110" },
+ { .name = "pxm1310" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, pxe1610_id);
diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c
index a368cfa9d45a..ecac405d7536 100644
--- a/drivers/hwmon/pmbus/q54sj108a2.c
+++ b/drivers/hwmon/pmbus/q54sj108a2.c
@@ -270,10 +270,10 @@ static const struct file_operations q54sj108a2_fops = {
};

static const struct i2c_device_id q54sj108a2_id[] = {
- { "q54sj108a2", q54sj108a2 },
- { "q54sn120a1", q54sj108a2 },
- { "q54sw120a7", q54sj108a2 },
- { },
+ { .name = "q54sj108a2", .driver_data = q54sj108a2 },
+ { .name = "q54sn120a1", .driver_data = q54sj108a2 },
+ { .name = "q54sw120a7", .driver_data = q54sj108a2 },
+ { }
};

MODULE_DEVICE_TABLE(i2c, q54sj108a2_id);
diff --git a/drivers/hwmon/pmbus/stef48h28.c b/drivers/hwmon/pmbus/stef48h28.c
index 4bde2215697c..8e48dd3ba74b 100644
--- a/drivers/hwmon/pmbus/stef48h28.c
+++ b/drivers/hwmon/pmbus/stef48h28.c
@@ -48,8 +48,8 @@ static int stef48h28_probe(struct i2c_client *client)
}

static const struct i2c_device_id stef48h28_id[] = {
- {"stef48h28"},
- {}
+ { .name = "stef48h28" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, stef48h28_id);

diff --git a/drivers/hwmon/pmbus/stpddc60.c b/drivers/hwmon/pmbus/stpddc60.c
index 5cb905ed8ae5..b5ce7f975eea 100644
--- a/drivers/hwmon/pmbus/stpddc60.c
+++ b/drivers/hwmon/pmbus/stpddc60.c
@@ -18,9 +18,9 @@
#define STPDDC60_MFR_UV_LIMIT_OFFSET 0xe6

static const struct i2c_device_id stpddc60_id[] = {
- {"stpddc60"},
- {"bmr481"},
- {}
+ { .name = "stpddc60" },
+ { .name = "bmr481" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, stpddc60_id);

diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c
index d902d39f49f4..803926633370 100644
--- a/drivers/hwmon/pmbus/tda38640.c
+++ b/drivers/hwmon/pmbus/tda38640.c
@@ -195,8 +195,8 @@ static int tda38640_probe(struct i2c_client *client)
}

static const struct i2c_device_id tda38640_id[] = {
- {"tda38640"},
- {}
+ { .name = "tda38640" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tda38640_id);

diff --git a/drivers/hwmon/pmbus/tps25990.c b/drivers/hwmon/pmbus/tps25990.c
index 05c6288ecafc..9d318e6509ab 100644
--- a/drivers/hwmon/pmbus/tps25990.c
+++ b/drivers/hwmon/pmbus/tps25990.c
@@ -387,8 +387,8 @@ static const struct pmbus_driver_info tps25990_base_info = {
};

static const struct i2c_device_id tps25990_i2c_id[] = {
- { "tps25990" },
- {}
+ { .name = "tps25990" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tps25990_i2c_id);

diff --git a/drivers/hwmon/pmbus/tps40422.c b/drivers/hwmon/pmbus/tps40422.c
index 7c9fedaa068c..f7be075dc707 100644
--- a/drivers/hwmon/pmbus/tps40422.c
+++ b/drivers/hwmon/pmbus/tps40422.c
@@ -31,8 +31,8 @@ static int tps40422_probe(struct i2c_client *client)
}

static const struct i2c_device_id tps40422_id[] = {
- {"tps40422"},
- {}
+ { .name = "tps40422" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, tps40422_id);
diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c
index 94258e8cfd90..31e54608b3c9 100644
--- a/drivers/hwmon/pmbus/tps53679.c
+++ b/drivers/hwmon/pmbus/tps53679.c
@@ -291,15 +291,15 @@ static int tps53679_probe(struct i2c_client *client)
}

static const struct i2c_device_id tps53679_id[] = {
- {"bmr474", tps53676},
- {"tps53647", tps53647},
- {"tps53667", tps53667},
- {"tps53676", tps53676},
- {"tps53679", tps53679},
- {"tps53681", tps53681},
- {"tps53685", tps53685},
- {"tps53688", tps53688},
- {}
+ { .name = "bmr474", .driver_data = tps53676 },
+ { .name = "tps53647", .driver_data = tps53647 },
+ { .name = "tps53667", .driver_data = tps53667 },
+ { .name = "tps53676", .driver_data = tps53676 },
+ { .name = "tps53679", .driver_data = tps53679 },
+ { .name = "tps53681", .driver_data = tps53681 },
+ { .name = "tps53685", .driver_data = tps53685 },
+ { .name = "tps53688", .driver_data = tps53688 },
+ { }
};

MODULE_DEVICE_TABLE(i2c, tps53679_id);
diff --git a/drivers/hwmon/pmbus/tps546d24.c b/drivers/hwmon/pmbus/tps546d24.c
index 44d7a6df1dbd..4222ff355e02 100644
--- a/drivers/hwmon/pmbus/tps546d24.c
+++ b/drivers/hwmon/pmbus/tps546d24.c
@@ -42,8 +42,8 @@ static int tps546d24_probe(struct i2c_client *client)
}

static const struct i2c_device_id tps546d24_id[] = {
- {"tps546d24"},
- {}
+ { .name = "tps546d24" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tps546d24_id);

diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index 9b5d34a110ba..f76c2913c9ca 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -143,14 +143,14 @@ static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg)
}

static const struct i2c_device_id ucd9000_id[] = {
- {"ucd9000", ucd9000},
- {"ucd90120", ucd90120},
- {"ucd90124", ucd90124},
- {"ucd90160", ucd90160},
- {"ucd90320", ucd90320},
- {"ucd9090", ucd9090},
- {"ucd90910", ucd90910},
- {}
+ { .name = "ucd9000", .driver_data = ucd9000 },
+ { .name = "ucd90120", .driver_data = ucd90120 },
+ { .name = "ucd90124", .driver_data = ucd90124 },
+ { .name = "ucd90160", .driver_data = ucd90160 },
+ { .name = "ucd90320", .driver_data = ucd90320 },
+ { .name = "ucd9090", .driver_data = ucd9090 },
+ { .name = "ucd90910", .driver_data = ucd90910 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ucd9000_id);

diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c
index f68adaf4a110..5e07bba111a1 100644
--- a/drivers/hwmon/pmbus/ucd9200.c
+++ b/drivers/hwmon/pmbus/ucd9200.c
@@ -22,15 +22,15 @@ enum chips { ucd9200, ucd9220, ucd9222, ucd9224, ucd9240, ucd9244, ucd9246,
ucd9248 };

static const struct i2c_device_id ucd9200_id[] = {
- {"ucd9200", ucd9200},
- {"ucd9220", ucd9220},
- {"ucd9222", ucd9222},
- {"ucd9224", ucd9224},
- {"ucd9240", ucd9240},
- {"ucd9244", ucd9244},
- {"ucd9246", ucd9246},
- {"ucd9248", ucd9248},
- {}
+ { .name = "ucd9200", .driver_data = ucd9200 },
+ { .name = "ucd9220", .driver_data = ucd9220 },
+ { .name = "ucd9222", .driver_data = ucd9222 },
+ { .name = "ucd9224", .driver_data = ucd9224 },
+ { .name = "ucd9240", .driver_data = ucd9240 },
+ { .name = "ucd9244", .driver_data = ucd9244 },
+ { .name = "ucd9246", .driver_data = ucd9246 },
+ { .name = "ucd9248", .driver_data = ucd9248 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ucd9200_id);

diff --git a/drivers/hwmon/pmbus/xdp710.c b/drivers/hwmon/pmbus/xdp710.c
index 660bbfe16e1e..494dbe45ebc6 100644
--- a/drivers/hwmon/pmbus/xdp710.c
+++ b/drivers/hwmon/pmbus/xdp710.c
@@ -110,7 +110,7 @@ static const struct of_device_id xdp710_of_match[] = {
};

static const struct i2c_device_id xdp710_id[] = {
- {"xdp710"},
+ { .name = "xdp710" },
{ }
};
MODULE_DEVICE_TABLE(i2c, xdp710_id);
diff --git a/drivers/hwmon/pmbus/xdp720.c b/drivers/hwmon/pmbus/xdp720.c
index 8729a771f216..60491e9217bf 100644
--- a/drivers/hwmon/pmbus/xdp720.c
+++ b/drivers/hwmon/pmbus/xdp720.c
@@ -106,8 +106,8 @@ static const struct of_device_id xdp720_of_match[] = {
MODULE_DEVICE_TABLE(of, xdp720_of_match);

static const struct i2c_device_id xdp720_id[] = {
- { "xdp720" },
- {}
+ { .name = "xdp720" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, xdp720_id);

diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c
index f3aa6339d60d..ed93e17ce8d5 100644
--- a/drivers/hwmon/pmbus/xdpe12284.c
+++ b/drivers/hwmon/pmbus/xdpe12284.c
@@ -164,10 +164,10 @@ static int xdpe122_probe(struct i2c_client *client)
}

static const struct i2c_device_id xdpe122_id[] = {
- {"xdpe11280"},
- {"xdpe12254"},
- {"xdpe12284"},
- {}
+ { .name = "xdpe11280" },
+ { .name = "xdpe12254" },
+ { .name = "xdpe12284" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, xdpe122_id);
diff --git a/drivers/hwmon/pmbus/xdpe152c4.c b/drivers/hwmon/pmbus/xdpe152c4.c
index 67a3d5fe1daf..b557d2971d5f 100644
--- a/drivers/hwmon/pmbus/xdpe152c4.c
+++ b/drivers/hwmon/pmbus/xdpe152c4.c
@@ -44,9 +44,9 @@ static int xdpe152_probe(struct i2c_client *client)
}

static const struct i2c_device_id xdpe152_id[] = {
- {"xdpe152c4"},
- {"xdpe15284"},
- {}
+ { .name = "xdpe152c4" },
+ { .name = "xdpe15284" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, xdpe152_id);
diff --git a/drivers/hwmon/pmbus/xdpe1a2g7b.c b/drivers/hwmon/pmbus/xdpe1a2g7b.c
index 1755e3522ede..971e7b73752e 100644
--- a/drivers/hwmon/pmbus/xdpe1a2g7b.c
+++ b/drivers/hwmon/pmbus/xdpe1a2g7b.c
@@ -87,9 +87,9 @@ static int xdpe1a2g7b_probe(struct i2c_client *client)
}

static const struct i2c_device_id xdpe1a2g7b_id[] = {
- { "xdpe1a2g5b" },
- { "xdpe1a2g7b" },
- {}
+ { .name = "xdpe1a2g5b" },
+ { .name = "xdpe1a2g7b" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, xdpe1a2g7b_id);
diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c
index 97be69630cfb..5db2a7818d16 100644
--- a/drivers/hwmon/pmbus/zl6100.c
+++ b/drivers/hwmon/pmbus/zl6100.c
@@ -250,28 +250,28 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
}

static const struct i2c_device_id zl6100_id[] = {
- {"bmr450", zl2005},
- {"bmr451", zl2005},
- {"bmr462", zl2008},
- {"bmr463", zl2008},
- {"bmr464", zl2008},
- {"bmr465", zls4009},
- {"bmr466", zls1003},
- {"bmr467", zls4009},
- {"bmr469", zl8802},
- {"zl2004", zl2004},
- {"zl2005", zl2005},
- {"zl2006", zl2006},
- {"zl2008", zl2008},
- {"zl2105", zl2105},
- {"zl2106", zl2106},
- {"zl6100", zl6100},
- {"zl6105", zl6105},
- {"zl8802", zl8802},
- {"zl9101", zl9101},
- {"zl9117", zl9117},
- {"zls1003", zls1003},
- {"zls4009", zls4009},
+ { .name = "bmr450", .driver_data = zl2005 },
+ { .name = "bmr451", .driver_data = zl2005 },
+ { .name = "bmr462", .driver_data = zl2008 },
+ { .name = "bmr463", .driver_data = zl2008 },
+ { .name = "bmr464", .driver_data = zl2008 },
+ { .name = "bmr465", .driver_data = zls4009 },
+ { .name = "bmr466", .driver_data = zls1003 },
+ { .name = "bmr467", .driver_data = zls4009 },
+ { .name = "bmr469", .driver_data = zl8802 },
+ { .name = "zl2004", .driver_data = zl2004 },
+ { .name = "zl2005", .driver_data = zl2005 },
+ { .name = "zl2006", .driver_data = zl2006 },
+ { .name = "zl2008", .driver_data = zl2008 },
+ { .name = "zl2105", .driver_data = zl2105 },
+ { .name = "zl2106", .driver_data = zl2106 },
+ { .name = "zl6100", .driver_data = zl6100 },
+ { .name = "zl6105", .driver_data = zl6105 },
+ { .name = "zl8802", .driver_data = zl8802 },
+ { .name = "zl9101", .driver_data = zl9101 },
+ { .name = "zl9117", .driver_data = zl9117 },
+ { .name = "zls1003", .driver_data = zls1003 },
+ { .name = "zls4009", .driver_data = zls4009 },
{ }
};
MODULE_DEVICE_TABLE(i2c, zl6100_id);
diff --git a/drivers/hwmon/powr1220.c b/drivers/hwmon/powr1220.c
index 06a2c56016d1..6e211601ea26 100644
--- a/drivers/hwmon/powr1220.c
+++ b/drivers/hwmon/powr1220.c
@@ -306,8 +306,8 @@ static int powr1220_probe(struct i2c_client *client)
}

static const struct i2c_device_id powr1220_ids[] = {
- { "powr1014", powr1014, },
- { "powr1220", powr1220, },
+ { .name = "powr1014", .driver_data = powr1014 },
+ { .name = "powr1220", .driver_data = powr1220 },
{ }
};

diff --git a/drivers/hwmon/pt5161l.c b/drivers/hwmon/pt5161l.c
index 89d4da8aa4c0..2b408a69b085 100644
--- a/drivers/hwmon/pt5161l.c
+++ b/drivers/hwmon/pt5161l.c
@@ -617,8 +617,8 @@ static const struct acpi_device_id __maybe_unused pt5161l_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, pt5161l_acpi_match);

static const struct i2c_device_id pt5161l_id[] = {
- { "pt5161l" },
- {}
+ { .name = "pt5161l" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, pt5161l_id);

diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c
index c5b2488c4c7f..c28f8625cd3a 100644
--- a/drivers/hwmon/sbtsi_temp.c
+++ b/drivers/hwmon/sbtsi_temp.c
@@ -221,8 +221,8 @@ static int sbtsi_probe(struct i2c_client *client)
}

static const struct i2c_device_id sbtsi_id[] = {
- {"sbtsi"},
- {}
+ { .name = "sbtsi" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, sbtsi_id);

diff --git a/drivers/hwmon/sg2042-mcu.c b/drivers/hwmon/sg2042-mcu.c
index 105131c4acf7..591f5f572fe4 100644
--- a/drivers/hwmon/sg2042-mcu.c
+++ b/drivers/hwmon/sg2042-mcu.c
@@ -333,7 +333,7 @@ static int sg2042_mcu_i2c_probe(struct i2c_client *client)
}

static const struct i2c_device_id sg2042_mcu_id[] = {
- { "sg2042-hwmon-mcu" },
+ { .name = "sg2042-hwmon-mcu" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sg2042_mcu_id);
diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c
index 627d35070a42..085492669eeb 100644
--- a/drivers/hwmon/sht21.c
+++ b/drivers/hwmon/sht21.c
@@ -275,9 +275,9 @@ static int sht21_probe(struct i2c_client *client)

/* Device ID table */
static const struct i2c_device_id sht21_id[] = {
- { "sht20" },
- { "sht21" },
- { "sht25" },
+ { .name = "sht20" },
+ { .name = "sht21" },
+ { .name = "sht25" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sht21_id);
diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c
index 08306ccb6d0b..c2f6b73aa7f3 100644
--- a/drivers/hwmon/sht3x.c
+++ b/drivers/hwmon/sht3x.c
@@ -931,10 +931,10 @@ static int sht3x_probe(struct i2c_client *client)

/* device ID table */
static const struct i2c_device_id sht3x_ids[] = {
- {"sht3x", sht3x},
- {"sts3x", sts3x},
- {"sht85", sht3x},
- {}
+ { .name = "sht3x", .driver_data = sht3x },
+ { .name = "sts3x", .driver_data = sts3x },
+ { .name = "sht85", .driver_data = sht3x },
+ { }
};

MODULE_DEVICE_TABLE(i2c, sht3x_ids);
diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c
index 5abe1227e109..9cace0e8acda 100644
--- a/drivers/hwmon/sht4x.c
+++ b/drivers/hwmon/sht4x.c
@@ -424,8 +424,8 @@ static int sht4x_probe(struct i2c_client *client)
}

static const struct i2c_device_id sht4x_id[] = {
- { "sht4x" },
- { },
+ { .name = "sht4x" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, sht4x_id);

diff --git a/drivers/hwmon/shtc1.c b/drivers/hwmon/shtc1.c
index 2ac906e8e173..362a73cf661b 100644
--- a/drivers/hwmon/shtc1.c
+++ b/drivers/hwmon/shtc1.c
@@ -257,9 +257,9 @@ static int shtc1_probe(struct i2c_client *client)

/* device ID table */
static const struct i2c_device_id shtc1_id[] = {
- { "shtc1", shtc1 },
- { "shtw1", shtc1 },
- { "shtc3", shtc3 },
+ { .name = "shtc1", .driver_data = shtc1 },
+ { .name = "shtw1", .driver_data = shtc1 },
+ { .name = "shtc3", .driver_data = shtc3 },
{ }
};
MODULE_DEVICE_TABLE(i2c, shtc1_id);
diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c
index 21103af4e139..1429b66e09b0 100644
--- a/drivers/hwmon/smsc47m192.c
+++ b/drivers/hwmon/smsc47m192.c
@@ -618,7 +618,7 @@ static int smsc47m192_probe(struct i2c_client *client)
}

static const struct i2c_device_id smsc47m192_id[] = {
- { "smsc47m192" },
+ { .name = "smsc47m192" },
{ }
};
MODULE_DEVICE_TABLE(i2c, smsc47m192_id);
diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c
index 5da44571b6a0..cc40661cab21 100644
--- a/drivers/hwmon/spd5118.c
+++ b/drivers/hwmon/spd5118.c
@@ -746,7 +746,7 @@ static int spd5118_i2c_probe(struct i2c_client *client)
}

static const struct i2c_device_id spd5118_i2c_id[] = {
- { "spd5118" },
+ { .name = "spd5118" },
{ }
};
MODULE_DEVICE_TABLE(i2c, spd5118_i2c_id);
diff --git a/drivers/hwmon/stts751.c b/drivers/hwmon/stts751.c
index f9e8b2869164..ce23681f7981 100644
--- a/drivers/hwmon/stts751.c
+++ b/drivers/hwmon/stts751.c
@@ -72,7 +72,7 @@ static const int stts751_intervals[] = {
};

static const struct i2c_device_id stts751_id[] = {
- { "stts751" },
+ { .name = "stts751" },
{ }
};

diff --git a/drivers/hwmon/tc654.c b/drivers/hwmon/tc654.c
index 39fe5836f237..4b22e305b203 100644
--- a/drivers/hwmon/tc654.c
+++ b/drivers/hwmon/tc654.c
@@ -550,9 +550,9 @@ static int tc654_probe(struct i2c_client *client)
}

static const struct i2c_device_id tc654_id[] = {
- {"tc654"},
- {"tc655"},
- {}
+ { .name = "tc654" },
+ { .name = "tc655" },
+ { }
};

MODULE_DEVICE_TABLE(i2c, tc654_id);
diff --git a/drivers/hwmon/tc74.c b/drivers/hwmon/tc74.c
index 7fb7b50ad1ad..e9113519be53 100644
--- a/drivers/hwmon/tc74.c
+++ b/drivers/hwmon/tc74.c
@@ -151,8 +151,8 @@ static int tc74_probe(struct i2c_client *client)
}

static const struct i2c_device_id tc74_id[] = {
- { "tc74" },
- {}
+ { .name = "tc74" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tc74_id);

diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c
index 0cbdb91698b1..b385ef64af75 100644
--- a/drivers/hwmon/thmc50.c
+++ b/drivers/hwmon/thmc50.c
@@ -407,8 +407,8 @@ static int thmc50_probe(struct i2c_client *client)
}

static const struct i2c_device_id thmc50_id[] = {
- { "adm1022", adm1022 },
- { "thmc50", thmc50 },
+ { .name = "adm1022", .driver_data = adm1022 },
+ { .name = "thmc50", .driver_data = thmc50 },
{ }
};
MODULE_DEVICE_TABLE(i2c, thmc50_id);
diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
index 3aa1a3fbeaa9..500286676174 100644
--- a/drivers/hwmon/tmp102.c
+++ b/drivers/hwmon/tmp102.c
@@ -395,7 +395,7 @@ static int tmp102_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume);

static const struct i2c_device_id tmp102_id[] = {
- { "tmp102" },
+ { .name = "tmp102" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp102_id);
diff --git a/drivers/hwmon/tmp103.c b/drivers/hwmon/tmp103.c
index 221bba8a215d..f13d24767575 100644
--- a/drivers/hwmon/tmp103.c
+++ b/drivers/hwmon/tmp103.c
@@ -194,7 +194,7 @@ static int tmp103_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(tmp103_dev_pm_ops, tmp103_suspend, tmp103_resume);

static const struct i2c_device_id tmp103_id[] = {
- { "tmp103" },
+ { .name = "tmp103" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp103_id);
diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c
index 3ea5f6485744..1c4a58855e2d 100644
--- a/drivers/hwmon/tmp108.c
+++ b/drivers/hwmon/tmp108.c
@@ -537,10 +537,10 @@ static int tmp108_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(tmp108_dev_pm_ops, tmp108_suspend, tmp108_resume);

static const struct i2c_device_id tmp108_i2c_ids[] = {
- { "p3t1035", (unsigned long)&p3t1035_data },
- { "p3t1085", (unsigned long)&tmp108_data },
- { "tmp108", (unsigned long)&tmp108_data },
- {}
+ { .name = "p3t1035", .driver_data = (unsigned long)&p3t1035_data },
+ { .name = "p3t1085", .driver_data = (unsigned long)&tmp108_data },
+ { .name = "tmp108", .driver_data = (unsigned long)&tmp108_data },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tmp108_i2c_ids);

diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
index 07f596581c6e..a09225a87355 100644
--- a/drivers/hwmon/tmp401.c
+++ b/drivers/hwmon/tmp401.c
@@ -90,11 +90,11 @@ static const u8 TMP432_STATUS_REG[] = {
*/

static const struct i2c_device_id tmp401_id[] = {
- { "tmp401", tmp401 },
- { "tmp411", tmp411 },
- { "tmp431", tmp431 },
- { "tmp432", tmp432 },
- { "tmp435", tmp435 },
+ { .name = "tmp401", .driver_data = tmp401 },
+ { .name = "tmp411", .driver_data = tmp411 },
+ { .name = "tmp431", .driver_data = tmp431 },
+ { .name = "tmp432", .driver_data = tmp432 },
+ { .name = "tmp435", .driver_data = tmp435 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp401_id);
diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c
index 2ea9d3e9553d..ed00ccfdd7b3 100644
--- a/drivers/hwmon/tmp421.c
+++ b/drivers/hwmon/tmp421.c
@@ -56,11 +56,11 @@ static const u8 TMP421_TEMP_LSB[MAX_CHANNELS] = { 0x10, 0x11, 0x12, 0x13 };
#define TMP442_DEVICE_ID 0x42

static const struct i2c_device_id tmp421_id[] = {
- { "tmp421", 2 },
- { "tmp422", 3 },
- { "tmp423", 4 },
- { "tmp441", 2 },
- { "tmp442", 3 },
+ { .name = "tmp421", .driver_data = 2 },
+ { .name = "tmp422", .driver_data = 3 },
+ { .name = "tmp423", .driver_data = 4 },
+ { .name = "tmp441", .driver_data = 2 },
+ { .name = "tmp442", .driver_data = 3 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp421_id);
diff --git a/drivers/hwmon/tmp464.c b/drivers/hwmon/tmp464.c
index 98f2576d94c6..c3e031044d1e 100644
--- a/drivers/hwmon/tmp464.c
+++ b/drivers/hwmon/tmp464.c
@@ -65,8 +65,8 @@ static const u8 TMP464_THERM2_LIMIT[MAX_CHANNELS] = {
#define TMP468_DEVICE_ID 0x0468

static const struct i2c_device_id tmp464_id[] = {
- { "tmp464", TMP464_NUM_CHANNELS },
- { "tmp468", TMP468_NUM_CHANNELS },
+ { .name = "tmp464", .driver_data = TMP464_NUM_CHANNELS },
+ { .name = "tmp468", .driver_data = TMP468_NUM_CHANNELS },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp464_id);
diff --git a/drivers/hwmon/tmp513.c b/drivers/hwmon/tmp513.c
index 5acbfd7d088d..b160aa045f89 100644
--- a/drivers/hwmon/tmp513.c
+++ b/drivers/hwmon/tmp513.c
@@ -611,8 +611,8 @@ static int tmp51x_init(struct tmp51x_data *data)
}

static const struct i2c_device_id tmp51x_id[] = {
- { "tmp512", TMP512_MAX_CHANNELS },
- { "tmp513", TMP513_MAX_CHANNELS },
+ { .name = "tmp512", .driver_data = TMP512_MAX_CHANNELS },
+ { .name = "tmp513", .driver_data = TMP513_MAX_CHANNELS },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp51x_id);
diff --git a/drivers/hwmon/tsc1641.c b/drivers/hwmon/tsc1641.c
index 2b5d34bab146..fc53cd5bb6e0 100644
--- a/drivers/hwmon/tsc1641.c
+++ b/drivers/hwmon/tsc1641.c
@@ -721,7 +721,7 @@ static int tsc1641_probe(struct i2c_client *client)
}

static const struct i2c_device_id tsc1641_id[] = {
- { "tsc1641", 0 },
+ { .name = "tsc1641" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tsc1641_id);
diff --git a/drivers/hwmon/w83773g.c b/drivers/hwmon/w83773g.c
index 401a28f55f93..54224dac10ae 100644
--- a/drivers/hwmon/w83773g.c
+++ b/drivers/hwmon/w83773g.c
@@ -34,7 +34,7 @@ static const u8 W83773_OFFSET_MSB[2] = { 0x11, 0x15 };

/* this is the number of sensors in the device */
static const struct i2c_device_id w83773_id[] = {
- { "w83773g" },
+ { .name = "w83773g" },
{ }
};

diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index f664c2152a6d..c40f84e53469 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -1559,10 +1559,10 @@ static struct w83781d_data *w83781d_update_device(struct device *dev)
}

static const struct i2c_device_id w83781d_ids[] = {
- { "w83781d", w83781d, },
- { "w83782d", w83782d, },
- { "w83783s", w83783s, },
- { "as99127f", as99127f },
+ { .name = "w83781d", .driver_data = w83781d },
+ { .name = "w83782d", .driver_data = w83782d },
+ { .name = "w83783s", .driver_data = w83783s },
+ { .name = "as99127f", .driver_data = as99127f },
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(i2c, w83781d_ids);
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
index 996e36951f9d..4a777430af5c 100644
--- a/drivers/hwmon/w83791d.c
+++ b/drivers/hwmon/w83791d.c
@@ -333,7 +333,7 @@ static void w83791d_print_debug(struct w83791d_data *data, struct device *dev);
static void w83791d_init_client(struct i2c_client *client);

static const struct i2c_device_id w83791d_id[] = {
- { "w83791d" },
+ { .name = "w83791d" },
{ }
};
MODULE_DEVICE_TABLE(i2c, w83791d_id);
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index b0b5f60eea53..f715c79389a5 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -296,7 +296,7 @@ static void w83792d_print_debug(struct w83792d_data *data, struct device *dev);
static void w83792d_init_client(struct i2c_client *client);

static const struct i2c_device_id w83792d_id[] = {
- { "w83792d" },
+ { .name = "w83792d" },
{ }
};
MODULE_DEVICE_TABLE(i2c, w83792d_id);
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c
index 8726c5fe8a95..a548586369e1 100644
--- a/drivers/hwmon/w83793.c
+++ b/drivers/hwmon/w83793.c
@@ -291,7 +291,7 @@ static void w83793_update_nonvolatile(struct device *dev);
static struct w83793_data *w83793_update_device(struct device *dev);

static const struct i2c_device_id w83793_id[] = {
- { "w83793" },
+ { .name = "w83793" },
{ }
};
MODULE_DEVICE_TABLE(i2c, w83793_id);
diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c
index 5174db69db5e..c5ce0bf1b08e 100644
--- a/drivers/hwmon/w83795.c
+++ b/drivers/hwmon/w83795.c
@@ -2243,8 +2243,8 @@ static void w83795_remove(struct i2c_client *client)


static const struct i2c_device_id w83795_id[] = {
- { "w83795g", w83795g },
- { "w83795adg", w83795adg },
+ { .name = "w83795g", .driver_data = w83795g },
+ { .name = "w83795adg", .driver_data = w83795adg },
{ }
};
MODULE_DEVICE_TABLE(i2c, w83795_id);
diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c
index df77b53a1b2f..e42506a3bcbe 100644
--- a/drivers/hwmon/w83l785ts.c
+++ b/drivers/hwmon/w83l785ts.c
@@ -74,7 +74,7 @@ static struct w83l785ts_data *w83l785ts_update_device(struct device *dev);
*/

static const struct i2c_device_id w83l785ts_id[] = {
- { "w83l785ts" },
+ { .name = "w83l785ts" },
{ }
};
MODULE_DEVICE_TABLE(i2c, w83l785ts_id);
diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c
index 1d9109ca1585..a72397083cc4 100644
--- a/drivers/hwmon/w83l786ng.c
+++ b/drivers/hwmon/w83l786ng.c
@@ -751,7 +751,7 @@ w83l786ng_probe(struct i2c_client *client)
}

static const struct i2c_device_id w83l786ng_id[] = {
- { "w83l786ng" },
+ { .name = "w83l786ng" },
{ }
};
MODULE_DEVICE_TABLE(i2c, w83l786ng_id);
diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index 3924e63e2eee..56acf31546da 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -843,18 +843,8 @@ static int intel_th_output_open(struct inode *inode, struct file *file)
return err;
}

-static int intel_th_output_release(struct inode *inode, struct file *file)
-{
- struct intel_th_device *thdev = file->private_data;
-
- put_device(&thdev->dev);
-
- return 0;
-}
-
static const struct file_operations intel_th_output_fops = {
.open = intel_th_output_open,
- .release = intel_th_output_release,
.llseek = noop_llseek,
};

diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index a82cf74f39ad..84d99d7b1d20 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -1490,8 +1490,10 @@ static int intel_th_msc_release(struct inode *inode, struct file *file)
{
struct msc_iter *iter = file->private_data;
struct msc *msc = iter->msc;
+ struct intel_th_device *thdev = msc->thdev;

msc_iter_remove(iter, msc);
+ put_device(&thdev->dev);

return 0;
}
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 9480d1a51c11..e88d3efb967b 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -5270,7 +5270,7 @@ static int cma_netevent_callback(struct notifier_block *self,

list_for_each_entry(current_id, &ips_node->id_list, id_list_entry) {
if (!memcmp(current_id->id.route.addr.dev_addr.dst_dev_addr,
- neigh->ha, ETH_ALEN))
+ neigh->ha, neigh->dev->addr_len))
continue;
cma_id_get(current_id);
if (!queue_work(cma_wq, &current_id->id.net_work))
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 8d19613179e3..e0b3b36b8b14 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -2031,6 +2031,24 @@ void ib_mark_mad_done(struct ib_mad_send_wr_private *mad_send_wr)
change_mad_state(mad_send_wr, IB_MAD_STATE_EARLY_RESP);
}

+static bool is_kernel_rmpp_data_response(struct ib_mad_agent_private *agent,
+ struct ib_mad_recv_wc *mad_recv_wc)
+{
+ const struct ib_mad_hdr *mad_hdr = &mad_recv_wc->recv_buf.mad->mad_hdr;
+ struct ib_rmpp_mad *rmpp_mad;
+
+ if (!ib_mad_kernel_rmpp_agent(&agent->agent) ||
+ !ib_response_mad(mad_hdr) ||
+ !ib_is_mad_class_rmpp(mad_hdr->mgmt_class))
+ return false;
+
+ rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad;
+
+ return (ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) &
+ IB_MGMT_RMPP_FLAG_ACTIVE) &&
+ rmpp_mad->rmpp_hdr.rmpp_type == IB_MGMT_RMPP_TYPE_DATA;
+}
+
static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
struct ib_mad_recv_wc *mad_recv_wc)
{
@@ -2050,6 +2068,18 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
}

list_add(&mad_recv_wc->recv_buf.list, &mad_recv_wc->rmpp_list);
+ if (is_kernel_rmpp_data_response(mad_agent_priv, mad_recv_wc)) {
+ spin_lock_irqsave(&mad_agent_priv->lock, flags);
+ mad_send_wr = ib_find_send_mad(mad_agent_priv, mad_recv_wc);
+ spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+
+ if (!mad_send_wr) {
+ ib_free_recv_mad(mad_recv_wc);
+ deref_mad_agent(mad_agent_priv);
+ return;
+ }
+ }
+
if (ib_mad_kernel_rmpp_agent(&mad_agent_priv->agent)) {
mad_recv_wc = ib_process_rmpp_recv_wc(mad_agent_priv,
mad_recv_wc);
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index bac87de9cc67..2f3237e6086b 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -2199,13 +2199,13 @@ struct ib_cq *__ib_create_cq(struct ib_device *device,
struct ib_cq *cq;
int ret;

+ if (WARN_ON_ONCE(!cq_attr->cqe))
+ return ERR_PTR(-EINVAL);
+
cq = rdma_zalloc_drv_obj(device, ib_cq);
if (!cq)
return ERR_PTR(-ENOMEM);

- if (WARN_ON_ONCE(!cq_attr->cqe))
- return ERR_PTR(-EINVAL);
-
cq->device = device;
cq->comp_handler = comp_handler;
cq->event_handler = event_handler;
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index 71a7ca2196ad..311c8bc93160 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -113,7 +113,7 @@ static void bng_re_fill_fw_msg(struct bnge_fw_msg *fw_msg, void *msg,
}

static int bng_re_net_ring_free(struct bng_re_dev *rdev,
- u16 fw_ring_id, int type)
+ u32 fw_ring_id, int type)
{
struct bnge_auxr_dev *aux_dev = rdev->aux_dev;
struct hwrm_ring_free_input req = {};
@@ -123,7 +123,7 @@ static int bng_re_net_ring_free(struct bng_re_dev *rdev,

bng_re_init_hwrm_hdr((void *)&req, HWRM_RING_FREE);
req.ring_type = type;
- req.ring_id = cpu_to_le16(fw_ring_id);
+ req.ring_id = cpu_to_le32(fw_ring_id);
bng_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp,
sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT);
rc = bnge_send_msg(aux_dev, &fw_msg);
@@ -161,7 +161,7 @@ static int bng_re_net_ring_alloc(struct bng_re_dev *rdev,
sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT);
rc = bnge_send_msg(aux_dev, &fw_msg);
if (!rc)
- *fw_ring_id = le16_to_cpu(resp.ring_id);
+ *fw_ring_id = (u16)le32_to_cpu(resp.ring_id);

return rc;
}
diff --git a/drivers/infiniband/hw/erdma/erdma_qp.c b/drivers/infiniband/hw/erdma/erdma_qp.c
index 25f6c49aec77..e002343832f7 100644
--- a/drivers/infiniband/hw/erdma/erdma_qp.c
+++ b/drivers/infiniband/hw/erdma/erdma_qp.c
@@ -734,7 +734,7 @@ int erdma_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *recv_wr,
const struct ib_recv_wr *wr = recv_wr;
struct erdma_qp *qp = to_eqp(ibqp);
unsigned long flags;
- int ret;
+ int ret = 0;

spin_lock_irqsave(&qp->lock, flags);

diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c
index ccb40f8a48b7..a38bfd9a0fe2 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hem.c
+++ b/drivers/infiniband/hw/hns/hns_roce_hem.c
@@ -836,7 +836,7 @@ static void hns_roce_cleanup_mhop_hem_table(struct hns_roce_dev *hr_dev,
mhop.bt_chunk_size;

for (i = 0; i < table->num_hem; ++i) {
- obj = i * buf_chunk_size / table->obj_size;
+ obj = (u64)i * buf_chunk_size / table->obj_size;
if (table->hem[i])
hns_roce_table_mhop_put(hr_dev, table, obj, 0);
}
diff --git a/drivers/infiniband/hw/irdma/uk.c b/drivers/infiniband/hw/irdma/uk.c
index 4718acf6c6fd..a34883fe9983 100644
--- a/drivers/infiniband/hw/irdma/uk.c
+++ b/drivers/infiniband/hw/irdma/uk.c
@@ -1568,15 +1568,12 @@ static const struct irdma_wqe_uk_ops iw_wqe_uk_ops_gen_1 = {
* irdma_setup_connection_wqes - setup WQEs necessary to complete
* connection.
* @qp: hw qp (user and kernel)
- * @info: qp initialization info
*/
-static void irdma_setup_connection_wqes(struct irdma_qp_uk *qp,
- struct irdma_qp_uk_init_info *info)
+static void irdma_setup_connection_wqes(struct irdma_qp_uk *qp)
{
u16 move_cnt = 1;

- if (!info->legacy_mode &&
- (qp->uk_attrs->feature_flags & IRDMA_FEATURE_RTS_AE))
+ if (qp->uk_attrs->feature_flags & IRDMA_FEATURE_RTS_AE)
move_cnt = 3;

qp->conn_wqes = move_cnt;
@@ -1727,7 +1724,7 @@ int irdma_uk_qp_init(struct irdma_qp_uk *qp, struct irdma_qp_uk_init_info *info)
sq_ring_size = qp->sq_size << info->sq_shift;
IRDMA_RING_INIT(qp->sq_ring, sq_ring_size);
if (info->first_sq_wq) {
- irdma_setup_connection_wqes(qp, info);
+ irdma_setup_connection_wqes(qp);
qp->swqe_polarity = 1;
qp->first_sq_wq = true;
} else {
diff --git a/drivers/infiniband/hw/irdma/user.h b/drivers/infiniband/hw/irdma/user.h
index 008af1acc928..4dd3776a4cdd 100644
--- a/drivers/infiniband/hw/irdma/user.h
+++ b/drivers/infiniband/hw/irdma/user.h
@@ -563,7 +563,6 @@ struct irdma_qp_uk_init_info {
u8 sq_shift;
u8 rq_shift;
int abi_ver;
- bool legacy_mode;
struct irdma_srq_uk *srq_uk;
};

diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c
index 7da7a7e8b30c..a886029411b8 100644
--- a/drivers/infiniband/hw/irdma/verbs.c
+++ b/drivers/infiniband/hw/irdma/verbs.c
@@ -633,18 +633,16 @@ static int irdma_setup_umode_qp(struct ib_udata *udata,

iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx;
iwqp->user_mode = 1;
- if (req.user_wqe_bufs) {
- info->qp_uk_init_info.legacy_mode = ucontext->legacy_mode;
- spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags);
- iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs,
- &ucontext->qp_reg_mem_list);
- spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags);

- if (!iwqp->iwpbl) {
- ret = -ENODATA;
- ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n");
- return ret;
- }
+ spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags);
+ iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs,
+ &ucontext->qp_reg_mem_list);
+ spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags);
+
+ if (!iwqp->iwpbl) {
+ ret = -ENODATA;
+ ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n");
+ return ret;
}

if (!ucontext->use_raw_attrs) {
@@ -2074,10 +2072,6 @@ static int irdma_resize_cq(struct ib_cq *ibcq, unsigned int entries,
rdma_udata_to_drv_context(udata, struct irdma_ucontext,
ibucontext);

- /* CQ resize not supported with legacy GEN_1 libi40iw */
- if (ucontext->legacy_mode)
- return -EOPNOTSUPP;
-
if (ib_copy_from_udata(&req, udata,
min(sizeof(req), udata->inlen)))
return -EINVAL;
@@ -2559,7 +2553,7 @@ static int irdma_create_cq(struct ib_cq *ibcq,
cqmr = &iwpbl->cq_mr;

if (rf->sc_dev.hw_attrs.uk_attrs.feature_flags &
- IRDMA_FEATURE_CQ_RESIZE && !ucontext->legacy_mode) {
+ IRDMA_FEATURE_CQ_RESIZE) {
spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags);
iwpbl_shadow = irdma_get_pbl(
(unsigned long)req.user_shadow_area,
@@ -2821,7 +2815,7 @@ static bool irdma_check_mem_contiguous(u64 *arr, u32 npages, u32 pg_size)
u32 pg_idx;

for (pg_idx = 0; pg_idx < npages; pg_idx++) {
- if ((*arr + (pg_size * pg_idx)) != arr[pg_idx])
+ if ((*arr + ((u64)pg_size * pg_idx)) != arr[pg_idx])
return false;
}

@@ -2854,7 +2848,7 @@ static bool irdma_check_mr_contiguous(struct irdma_pble_alloc *palloc,

for (i = 0; i < lvl2->leaf_cnt; i++, leaf++) {
arr = leaf->addr;
- if ((*start_addr + (i * pg_size * PBLE_PER_PAGE)) != *arr)
+ if ((*start_addr + ((u64)i * pg_size * PBLE_PER_PAGE)) != *arr)
return false;
ret = irdma_check_mem_contiguous(arr, leaf->cnt, pg_size);
if (!ret)
@@ -3810,6 +3804,9 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags,
if (flags & ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS))
return ERR_PTR(-EOPNOTSUPP);

+ if (iwmr->type != IRDMA_MEMREG_TYPE_MEM)
+ return ERR_PTR(-EINVAL);
+
ret = ib_umem_check_rereg(iwmr->region, flags, new_access);
if (ret)
return ERR_PTR(ret);
diff --git a/drivers/infiniband/hw/mana/wr.c b/drivers/infiniband/hw/mana/wr.c
index 1813567d3b16..36a1d506f08f 100644
--- a/drivers/infiniband/hw/mana/wr.c
+++ b/drivers/infiniband/hw/mana/wr.c
@@ -144,7 +144,7 @@ static int mana_ib_post_send_ud(struct mana_ib_qp *qp, const struct ib_ud_wr *wr
int mana_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
const struct ib_send_wr **bad_wr)
{
- int err;
+ int err = 0;
struct mana_ib_qp *qp = container_of(ibqp, struct mana_ib_qp, ibqp);

for (; wr; wr = wr->next) {
diff --git a/drivers/infiniband/sw/siw/siw_verbs.c b/drivers/infiniband/sw/siw/siw_verbs.c
index 1e1d262a4ae2..a513ca96259f 100644
--- a/drivers/infiniband/sw/siw/siw_verbs.c
+++ b/drivers/infiniband/sw/siw/siw_verbs.c
@@ -316,6 +316,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
struct siw_ucontext *uctx =
rdma_udata_to_drv_context(udata, struct siw_ucontext,
base_ucontext);
+ struct siw_uresp_create_qp uresp = {};
unsigned long flags;
int num_sqe, num_rqe, rv = 0;
size_t length;
@@ -369,11 +370,6 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
spin_lock_init(&qp->rq_lock);
spin_lock_init(&qp->orq_lock);

- rv = siw_qp_add(sdev, qp);
- if (rv)
- goto err_atomic;
-
-
/* All queue indices are derived from modulo operations
* on a free running 'get' (consumer) and 'put' (producer)
* unsigned counter. Having queue sizes at power of two
@@ -391,14 +387,14 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,

if (qp->sendq == NULL) {
rv = -ENOMEM;
- goto err_out_xa;
+ goto err_out;
}
if (attrs->sq_sig_type != IB_SIGNAL_REQ_WR) {
if (attrs->sq_sig_type == IB_SIGNAL_ALL_WR)
qp->attrs.flags |= SIW_SIGNAL_ALL_WR;
else {
rv = -EINVAL;
- goto err_out_xa;
+ goto err_out;
}
}
qp->pd = pd;
@@ -424,7 +420,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,

if (qp->recvq == NULL) {
rv = -ENOMEM;
- goto err_out_xa;
+ goto err_out;
}
qp->attrs.rq_size = num_rqe;
}
@@ -439,11 +435,8 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
qp->attrs.state = SIW_QP_STATE_IDLE;

if (udata) {
- struct siw_uresp_create_qp uresp = {};
-
uresp.num_sqe = num_sqe;
uresp.num_rqe = num_rqe;
- uresp.qp_id = qp_id(qp);

if (qp->sendq) {
length = num_sqe * sizeof(struct siw_sqe);
@@ -452,7 +445,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
length, &uresp.sq_key);
if (!qp->sq_entry) {
rv = -ENOMEM;
- goto err_out_xa;
+ goto err_out;
}
}

@@ -464,9 +457,23 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
if (!qp->rq_entry) {
uresp.sq_key = SIW_INVAL_UOBJ_KEY;
rv = -ENOMEM;
- goto err_out_xa;
+ goto err_out;
}
}
+ }
+ qp->tx_cpu = siw_get_tx_cpu(sdev);
+ if (qp->tx_cpu < 0) {
+ rv = -EINVAL;
+ goto err_out;
+ }
+ init_completion(&qp->qp_free);
+
+ rv = siw_qp_add(sdev, qp);
+ if (rv)
+ goto err_out_tx;
+
+ if (udata) {
+ uresp.qp_id = qp_id(qp);

if (udata->outlen < sizeof(uresp)) {
rv = -EINVAL;
@@ -476,22 +483,19 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
if (rv)
goto err_out_xa;
}
- qp->tx_cpu = siw_get_tx_cpu(sdev);
- if (qp->tx_cpu < 0) {
- rv = -EINVAL;
- goto err_out_xa;
- }
+
INIT_LIST_HEAD(&qp->devq);
spin_lock_irqsave(&sdev->lock, flags);
list_add_tail(&qp->devq, &sdev->qp_list);
spin_unlock_irqrestore(&sdev->lock, flags);

- init_completion(&qp->qp_free);
-
return 0;

err_out_xa:
xa_erase(&sdev->qp_xa, qp_id(qp));
+err_out_tx:
+ siw_put_tx_cpu(qp->tx_cpu);
+err_out:
if (uctx) {
rdma_user_mmap_entry_remove(qp->sq_entry);
rdma_user_mmap_entry_remove(qp->rq_entry);
diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index 9a846dcd0306..0df711c082cb 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -3872,6 +3872,12 @@ static int __init parse_ivrs_acpihid(char *str)
return 1;

found:
+ if (early_acpihid_map_size == EARLY_MAP_SIZE) {
+ pr_err("Early ACPI HID map overflow - ignoring ivrs_acpihid%s\n",
+ str);
+ return 1;
+ }
+
p = acpiid;
hid = strsep(&p, ":");
uid = p;
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 15ffc4742183..345178a285c9 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -1456,11 +1456,23 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
int ret;
u64 data;

- if (!iommu->need_sync)
- return 0;
-
raw_spin_lock_irqsave(&iommu->lock, flags);

+ if (!iommu->need_sync) {
+ /*
+ * No command has been queued since the last completion-wait.
+ * A concurrent CPU may have already queued that CWAIT and
+ * cleared need_sync; need_sync == false only means a covering
+ * CWAIT is queued, not that all prior commands have completed.
+ * Wait for the last allocated sequence number so that any
+ * command queued before this call (possibly on another CPU)
+ * is guaranteed to have completed before returning.
+ */
+ data = iommu->cmd_sem_val;
+ raw_spin_unlock_irqrestore(&iommu->lock, flags);
+ return wait_on_sem(iommu, data);
+ }
+
data = get_cmdsem_val(iommu);
build_completion_wait(&cmd, iommu, data);

@@ -1470,9 +1482,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
if (ret)
return ret;

- ret = wait_on_sem(iommu, data);
-
- return ret;
+ return wait_on_sem(iommu, data);
}

static void domain_flush_complete(struct protection_domain *domain)
diff --git a/drivers/iommu/amd/nested.c b/drivers/iommu/amd/nested.c
index 5b902598e68a..63b53b29e029 100644
--- a/drivers/iommu/amd/nested.c
+++ b/drivers/iommu/amd/nested.c
@@ -59,7 +59,9 @@ static int validate_gdte_nested(struct iommu_hwpt_amd_guest *gdte)
return 0;
}

-static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long index)
+static void *gdom_info_load_or_alloc_locked(struct xarray *xa,
+ unsigned long index,
+ unsigned long *flags)
{
struct guest_domain_mapping_info *elm, *res;

@@ -67,13 +69,13 @@ static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long ind
if (elm)
return elm;

- xa_unlock(xa);
+ xa_unlock_irqrestore(xa, *flags);
elm = kzalloc_obj(struct guest_domain_mapping_info);
- xa_lock(xa);
+ xa_lock_irqsave(xa, *flags);
if (!elm)
return ERR_PTR(-ENOMEM);

- res = __xa_cmpxchg(xa, index, NULL, elm, GFP_KERNEL);
+ res = __xa_cmpxchg(xa, index, NULL, elm, GFP_ATOMIC);
if (xa_is_err(res))
res = ERR_PTR(xa_err(res));

@@ -95,6 +97,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
const struct iommu_user_data *user_data)
{
int ret;
+ unsigned long irqflags;
struct nested_domain *ndom;
struct guest_domain_mapping_info *gdom_info;
struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core);
@@ -136,11 +139,12 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
* keep track of the gDomID mapping. When the S2 is changed, the INVALIDATE_IOMMU_PAGES
* command must be issued for each hDomID in the xarray.
*/
- xa_lock(&aviommu->gdomid_array);
+ xa_lock_irqsave(&aviommu->gdomid_array, irqflags);

- gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array, ndom->gdom_id);
+ gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array,
+ ndom->gdom_id, &irqflags);
if (IS_ERR(gdom_info)) {
- xa_unlock(&aviommu->gdomid_array);
+ xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
ret = PTR_ERR(gdom_info);
goto out_err;
}
@@ -148,7 +152,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
/* Check if gDomID exist */
if (refcount_inc_not_zero(&gdom_info->users)) {
ndom->gdom_info = gdom_info;
- xa_unlock(&aviommu->gdomid_array);
+ xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);

pr_debug("%s: Found gdom_id=%#x, hdom_id=%#x\n",
__func__, ndom->gdom_id, gdom_info->hdom_id);
@@ -161,7 +165,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
if (gdom_info->hdom_id <= 0) {
__xa_cmpxchg(&aviommu->gdomid_array,
ndom->gdom_id, gdom_info, NULL, GFP_ATOMIC);
- xa_unlock(&aviommu->gdomid_array);
+ xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
ret = -ENOSPC;
goto out_err_gdom_info;
}
@@ -169,7 +173,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
ndom->gdom_info = gdom_info;
refcount_set(&gdom_info->users, 1);

- xa_unlock(&aviommu->gdomid_array);
+ xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);

pr_debug("%s: Allocate gdom_id=%#x, hdom_id=%#x\n",
__func__, ndom->gdom_id, gdom_info->hdom_id);
@@ -257,14 +261,15 @@ static int nested_attach_device(struct iommu_domain *dom, struct device *dev,

static void nested_domain_free(struct iommu_domain *dom)
{
+ unsigned long irqflags;
struct guest_domain_mapping_info *curr;
- struct nested_domain *ndom = to_ndomain(dom);
+ struct nested_domain *ndom __free(kfree) = to_ndomain(dom);
struct amd_iommu_viommu *aviommu = ndom->viommu;

- xa_lock(&aviommu->gdomid_array);
+ xa_lock_irqsave(&aviommu->gdomid_array, irqflags);

if (!refcount_dec_and_test(&ndom->gdom_info->users)) {
- xa_unlock(&aviommu->gdomid_array);
+ xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
return;
}

@@ -275,7 +280,7 @@ static void nested_domain_free(struct iommu_domain *dom)
curr = __xa_cmpxchg(&aviommu->gdomid_array, ndom->gdom_id,
ndom->gdom_info, NULL, GFP_ATOMIC);

- xa_unlock(&aviommu->gdomid_array);
+ xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
if (WARN_ON(!curr || xa_err(curr)))
return;

@@ -285,7 +290,6 @@ static void nested_domain_free(struct iommu_domain *dom)

amd_iommu_pdom_id_free(ndom->gdom_info->hdom_id);
kfree(curr);
- kfree(ndom);
}

static const struct iommu_domain_ops nested_domain_ops = {
diff --git a/drivers/iommu/intel/perf.c b/drivers/iommu/intel/perf.c
index 02168f2f20a4..bec98dcbb9ec 100644
--- a/drivers/iommu/intel/perf.c
+++ b/drivers/iommu/intel/perf.c
@@ -63,7 +63,7 @@ void dmar_latency_disable(struct intel_iommu *iommu, enum latency_type type)
return;

spin_lock_irqsave(&latency_lock, flags);
- memset(&lstat[type], 0, sizeof(*lstat) * DMAR_LATENCY_NUM);
+ memset(&lstat[type], 0, sizeof(*lstat));
spin_unlock_irqrestore(&latency_lock, flags);
}

diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c
index fea10acd4f02..726f7b6d0bff 100644
--- a/drivers/iommu/intel/svm.c
+++ b/drivers/iommu/intel/svm.c
@@ -27,7 +27,7 @@

void intel_svm_check(struct intel_iommu *iommu)
{
- if (!pasid_supported(iommu))
+ if (!pasid_supported(iommu) || !ecap_smpwc(iommu->ecap))
return;

if (cpu_feature_enabled(X86_FEATURE_GBPAGES) &&
diff --git a/drivers/media/cec/platform/seco/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c
index b7bb49f02395..97ed9654c78a 100644
--- a/drivers/media/cec/platform/seco/seco-cec.c
+++ b/drivers/media/cec/platform/seco/seco-cec.c
@@ -649,7 +649,7 @@ static int secocec_probe(struct platform_device *pdev)

ret = secocec_ir_probe(secocec);
if (ret)
- goto err_notifier;
+ goto err_unregister_adapter;

platform_set_drvdata(pdev, secocec);

@@ -657,6 +657,10 @@ static int secocec_probe(struct platform_device *pdev)

return ret;

+err_unregister_adapter:
+ cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
+ cec_unregister_adapter(secocec->cec_adap);
+ goto err;
err_notifier:
cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
err_delete_adapter:
diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index adf668b213c2..b0a6084f1757 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -2990,8 +2990,8 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q)
* @nonblock: mode selector (1 means blocking calls, 0 means nonblocking)
* @read: access mode selector (1 means read, 0 means write)
*/
-static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
- loff_t *ppos, int nonblock, int read)
+static ssize_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblock, int read)
{
struct vb2_fileio_data *fileio;
struct vb2_fileio_buf *buf;
@@ -3154,15 +3154,15 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
return ret;
}

-size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
- loff_t *ppos, int nonblocking)
+ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
{
return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
}
EXPORT_SYMBOL_GPL(vb2_read);

-size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
- loff_t *ppos, int nonblocking)
+ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
{
return __vb2_perform_fileio(q, (char __user *) data, count,
ppos, nonblocking, 0);
diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c
index d8e1546aea5e..9898f729304a 100644
--- a/drivers/media/dvb-frontends/rtl2832.c
+++ b/drivers/media/dvb-frontends/rtl2832.c
@@ -1115,10 +1115,10 @@ static void rtl2832_remove(struct i2c_client *client)

dev_dbg(&client->dev, "\n");

- cancel_delayed_work_sync(&dev->i2c_gate_work);
-
i2c_mux_del_adapters(dev->muxc);

+ cancel_delayed_work_sync(&dev->i2c_gate_work);
+
regmap_exit(dev->regmap);

kfree(dev);
diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c
index 422d1a7b5456..c564485e3bbb 100644
--- a/drivers/media/dvb-frontends/rtl2832_sdr.c
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.c
@@ -399,7 +399,8 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev)
}

/* Must be called with vb_queue_lock hold */
-static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev)
+static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev,
+ enum vb2_buffer_state state)
{
struct platform_device *pdev = dev->pdev;
unsigned long flags;
@@ -413,7 +414,7 @@ static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev)
buf = list_entry(dev->queued_bufs.next,
struct rtl2832_sdr_frame_buf, list);
list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
}
spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
}
@@ -855,11 +856,15 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)

dev_dbg(&pdev->dev, "\n");

- if (!dev->udev)
+ if (!dev->udev) {
+ rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
return -ENODEV;
+ }

- if (mutex_lock_interruptible(&dev->v4l2_lock))
+ if (mutex_lock_interruptible(&dev->v4l2_lock)) {
+ rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
return -ERESTARTSYS;
+ }

if (d->props->power_ctrl)
d->props->power_ctrl(d, 1);
@@ -900,7 +905,11 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
if (ret)
goto err;

+ mutex_unlock(&dev->v4l2_lock);
+ return 0;
+
err:
+ rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
mutex_unlock(&dev->v4l2_lock);

return ret;
@@ -920,7 +929,7 @@ static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
rtl2832_sdr_kill_urbs(dev);
rtl2832_sdr_free_urbs(dev);
rtl2832_sdr_free_stream_bufs(dev);
- rtl2832_sdr_cleanup_queued_bufs(dev);
+ rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR);
rtl2832_sdr_unset_adc(dev);

/* sleep tuner */
diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c
index 955b7072a560..dd991c2ee700 100644
--- a/drivers/media/i2c/alvium-csi2.c
+++ b/drivers/media/i2c/alvium-csi2.c
@@ -2100,20 +2100,21 @@ static int alvium_ctrl_init(struct alvium_dev *alvium)
V4L2_CID_PIXEL_RATE, 0,
ALVIUM_DEFAULT_PIXEL_RATE_MHZ, 1,
ALVIUM_DEFAULT_PIXEL_RATE_MHZ);
- ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;

/* Link freq is fixed */
ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops,
V4L2_CID_LINK_FREQ,
0, 0, &alvium->link_freq);
- ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ if (ctrls->link_freq)
+ ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;

/* Auto/manual white balance */
if (alvium->avail_ft.auto_whiteb) {
ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
V4L2_CID_AUTO_WHITE_BALANCE,
0, 1, 1, 1);
- v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
+ if (ctrls->auto_wb)
+ v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
}

ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops,
@@ -2122,6 +2123,7 @@ static int alvium_ctrl_init(struct alvium_dev *alvium)
alvium->max_bbalance,
alvium->inc_bbalance,
alvium->dft_bbalance);
+
ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops,
V4L2_CID_RED_BALANCE,
alvium->min_rbalance,
@@ -2136,7 +2138,9 @@ static int alvium_ctrl_init(struct alvium_dev *alvium)
V4L2_CID_EXPOSURE_AUTO,
V4L2_EXPOSURE_MANUAL, 0,
V4L2_EXPOSURE_AUTO);
- v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
+ if (ctrls->auto_exp)
+ v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp,
+ V4L2_EXPOSURE_MANUAL, true);
}

ctrls->exposure = v4l2_ctrl_new_std(hdl, ops,
@@ -2145,14 +2149,16 @@ static int alvium_ctrl_init(struct alvium_dev *alvium)
alvium->max_exp,
alvium->inc_exp,
alvium->dft_exp);
- ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ if (ctrls->exposure)
+ ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;

/* Auto/manual gain */
if (alvium->avail_ft.auto_gain) {
ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops,
V4L2_CID_AUTOGAIN,
0, 1, 1, 1);
- v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
+ if (ctrls->auto_gain)
+ v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
}

if (alvium->avail_ft.gain) {
@@ -2162,7 +2168,8 @@ static int alvium_ctrl_init(struct alvium_dev *alvium)
alvium->max_gain,
alvium->inc_gain,
alvium->dft_gain);
- ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ if (ctrls->gain)
+ ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
}

if (alvium->avail_ft.sat)
diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c
index 59558335989e..3b7ba88fd67c 100644
--- a/drivers/media/i2c/dw9719.c
+++ b/drivers/media/i2c/dw9719.c
@@ -439,6 +439,15 @@ static void dw9719_remove(struct i2c_client *client)
pm_runtime_set_suspended(&client->dev);
}

+static const struct i2c_device_id dw9719_id_table[] = {
+ { .name = "dw9718s", .driver_data = (kernel_ulong_t)DW9718S },
+ { .name = "dw9719", .driver_data = (kernel_ulong_t)DW9719 },
+ { .name = "dw9761", .driver_data = (kernel_ulong_t)DW9761 },
+ { .name = "dw9800k", .driver_data = (kernel_ulong_t)DW9800K },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, dw9719_id_table);
+
static const struct of_device_id dw9719_of_table[] = {
{ .compatible = "dongwoon,dw9718s", .data = (const void *)DW9718S },
{ .compatible = "dongwoon,dw9719", .data = (const void *)DW9719 },
@@ -459,6 +468,7 @@ static struct i2c_driver dw9719_i2c_driver = {
},
.probe = dw9719_probe,
.remove = dw9719_remove,
+ .id_table = dw9719_id_table,
};
module_i2c_driver(dw9719_i2c_driver);

diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
index 7da02ce5da15..894229a7e832 100644
--- a/drivers/media/i2c/imx219.c
+++ b/drivers/media/i2c/imx219.c
@@ -72,7 +72,7 @@

/* V_TIMING internal */
#define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160)
-#define IMX219_FLL_MAX 0xffff
+#define IMX219_FLL_MAX 0xfffe
#define IMX219_VBLANK_MIN 32
#define IMX219_REG_LINE_LENGTH_A CCI_REG16(0x0162)
#define IMX219_LLP_MIN 0x0d78
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index 4a8af8b88d84..9b92e8db494c 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -1002,8 +1002,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
}

/* PCIe stuff */
- dev->lmmio = ioremap(pci_resource_start(dev->pci, 0),
- pci_resource_len(dev->pci, 0));
+ dev->lmmio = pci_ioremap_bar(dev->pci, 0);
+ if (!dev->lmmio) {
+ dev_err(&dev->pci->dev, "CORE %s: can't ioremap MMIO memory\n",
+ dev->name);
+ goto err_release_region;
+ }

dev->bmmio = (u8 __iomem *)dev->lmmio;

@@ -1109,6 +1113,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
}

return 0;
+
+err_release_region:
+ release_mem_region(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0));
+ cx23885_devcount--;
+ return -ENODEV;
}

static void cx23885_dev_unregister(struct cx23885_dev *dev)
diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
index bbd24769ae56..e915d9a3f785 100644
--- a/drivers/media/pci/dm1105/dm1105.c
+++ b/drivers/media/pci/dm1105/dm1105.c
@@ -1194,6 +1194,7 @@ static void dm1105_remove(struct pci_dev *pdev)

dm1105_hw_exit(dev);
free_irq(pdev->irq, dev);
+ destroy_workqueue(dev->wq);
pci_iounmap(pdev, dev->io_mem);
pci_release_regions(pdev);
pci_disable_device(pdev);
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
index db2874843453..237906cb131a 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
@@ -288,15 +288,27 @@ static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = {

static u16 get_hsfreq_by_mbps(u32 mbps)
{
- unsigned int i = DPHY_FREQ_RANGE_NUM;
-
- while (i--) {
- if (freqranges[i].default_mbps == mbps ||
- (mbps >= freqranges[i].min && mbps <= freqranges[i].max))
- return i;
+ u16 best = DPHY_FREQ_RANGE_INVALID_INDEX;
+ unsigned int i;
+
+ for (i = 0; i < DPHY_FREQ_RANGE_NUM; i++) {
+ if (mbps > freqranges[i].max)
+ continue;
+
+ if (mbps < freqranges[i].min)
+ break;
+
+ if (best == DPHY_FREQ_RANGE_INVALID_INDEX ||
+ freqranges[i].osc_freq_target >
+ freqranges[best].osc_freq_target ||
+ (freqranges[i].osc_freq_target ==
+ freqranges[best].osc_freq_target &&
+ abs((int)mbps - (int)freqranges[i].default_mbps) <
+ abs((int)mbps - (int)freqranges[best].default_mbps)))
+ best = i;
}

- return DPHY_FREQ_RANGE_INVALID_INDEX;
+ return best;
}

static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys,
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index 4a51b873e47a..2b1672737d84 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -1714,8 +1714,10 @@ int saa7134_video_init1(struct saa7134_dev *dev)
q->dev = &dev->pci->dev;
ret = vb2_queue_init(q);
if (ret)
- return ret;
- saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt);
+ goto err_free_ctrl;
+ ret = saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt);
+ if (ret)
+ goto err_free_ctrl;

q = &dev->vbi_vbq;
q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
@@ -1732,11 +1734,24 @@ int saa7134_video_init1(struct saa7134_dev *dev)
q->lock = &dev->lock;
q->dev = &dev->pci->dev;
ret = vb2_queue_init(q);
- if (ret)
- return ret;
- saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt);
+ if (ret) {
+ saa7134_pgtable_free(dev->pci, &dev->video_q.pt);
+ goto err_free_ctrl;
+ }
+
+ ret = saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt);
+ if (ret) {
+ saa7134_pgtable_free(dev->pci, &dev->video_q.pt);
+ goto err_free_ctrl;
+ }

return 0;
+
+err_free_ctrl:
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+ if (card_has_radio(dev))
+ v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
+ return ret;
}

void saa7134_video_fini(struct saa7134_dev *dev)
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
index 6f9ca7a7dd88..aec3eed0e443 100644
--- a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
@@ -104,6 +104,8 @@ static void c3_isp_params_awb_wt(struct c3_isp_device *isp,
c3_isp_write(isp, ISP_AWB_BLK_WT_ADDR, 0);

zones_num = cfg->horiz_zones_num * cfg->vert_zones_num;
+ if (zones_num > C3_ISP_AWB_MAX_ZONES)
+ zones_num = C3_ISP_AWB_MAX_ZONES;

/* Need to write 8 weights at once */
for (i = 0; i < zones_num / 8; i++) {
@@ -220,6 +222,8 @@ static void c3_isp_params_ae_wt(struct c3_isp_device *isp,
c3_isp_write(isp, ISP_AE_BLK_WT_ADDR, 0);

zones_num = cfg->horiz_zones_num * cfg->vert_zones_num;
+ if (zones_num > C3_ISP_AE_MAX_ZONES)
+ zones_num = C3_ISP_AE_MAX_ZONES;

/* Need to write 8 weights at once */
for (i = 0; i < zones_num / 8; i++) {
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c
index c1a562cd214e..0f0043927cfa 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c
@@ -806,8 +806,10 @@ static int mali_c55_probe(struct platform_device *pdev)
vb2_dma_contig_set_max_seg_size(dev, UINT_MAX);

ret = __mali_c55_power_on(mali_c55);
- if (ret)
- return dev_err_probe(dev, ret, "failed to power on\n");
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to power on\n");
+ goto err_release_mem;
+ }

ret = mali_c55_check_hwcfg(mali_c55);
if (ret)
@@ -826,7 +828,7 @@ static int mali_c55_probe(struct platform_device *pdev)

ret = mali_c55_media_frameworks_init(mali_c55);
if (ret)
- goto err_free_context_registers;
+ goto err_pm_runtime_disable;

pm_runtime_idle(&pdev->dev);

@@ -841,11 +843,14 @@ static int mali_c55_probe(struct platform_device *pdev)

err_deinit_media_frameworks:
mali_c55_media_frameworks_deinit(mali_c55);
+err_pm_runtime_disable:
+ pm_runtime_set_suspended(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-err_free_context_registers:
kfree(mali_c55->context.registers);
err_power_off:
__mali_c55_power_off(mali_c55);
+err_release_mem:
+ of_reserved_mem_device_release(dev);

return ret;
}
@@ -854,8 +859,14 @@ static void mali_c55_remove(struct platform_device *pdev)
{
struct mali_c55 *mali_c55 = platform_get_drvdata(pdev);

- kfree(mali_c55->context.registers);
mali_c55_media_frameworks_deinit(mali_c55);
+ if (!pm_runtime_suspended(&pdev->dev)) {
+ __mali_c55_power_off(mali_c55);
+ pm_runtime_set_suspended(&pdev->dev);
+ }
+ pm_runtime_disable(&pdev->dev);
+ kfree(mali_c55->context.registers);
+ of_reserved_mem_device_release(&pdev->dev);
}

static const struct of_device_id mali_c55_of_match[] = {
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
index 4c0fd1ec741c..60cc021ab7ab 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
@@ -333,6 +333,13 @@ static int mali_c55_isp_enable_streams(struct v4l2_subdev *sd,

sink_pad = &isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO];
isp->remote_src = media_pad_remote_pad_unique(sink_pad);
+ if (IS_ERR(isp->remote_src)) {
+ ret = PTR_ERR(isp->remote_src);
+ dev_err(mali_c55->dev, "Failed to get remote source pad: %d\n", ret);
+ isp->remote_src = NULL;
+ return ret;
+ }
+
src_sd = media_entity_to_v4l2_subdev(isp->remote_src->entity);

isp->frame_sequence = 0;
diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c
index 41cb96f60110..a292275f6b7b 100644
--- a/drivers/media/platform/aspeed/aspeed-video.c
+++ b/drivers/media/platform/aspeed/aspeed-video.c
@@ -2343,6 +2343,7 @@ static int aspeed_video_probe(struct platform_device *pdev)
rc = aspeed_video_setup_video(video);
if (rc) {
aspeed_video_free_buf(video, &video->jpeg);
+ of_reserved_mem_device_release(&pdev->dev);
clk_unprepare(video->vclk);
clk_unprepare(video->eclk);
return rc;
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
index 7613fcdbafed..c605a91718d8 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
@@ -226,13 +226,6 @@ static int start_encode(struct vpu_instance *inst, u32 *fail_res)
} else {
dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame success\n",
__func__);
- /*
- * Remove the source buffer from the ready-queue now and finish
- * it in the videobuf2 framework once the index is returned by the
- * firmware in finish_encode
- */
- if (src_buf)
- v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index);
}

return 0;
@@ -259,27 +252,13 @@ static void wave5_vpu_enc_finish_encode(struct vpu_instance *inst)
__func__, enc_output_info.pic_type, enc_output_info.recon_frame_index,
enc_output_info.enc_src_idx, enc_output_info.enc_pic_byte, enc_output_info.pts);

- /*
- * The source buffer will not be found in the ready-queue as it has been
- * dropped after sending of the encode firmware command, locate it in
- * the videobuf2 queue directly
- */
if (enc_output_info.enc_src_idx >= 0) {
- struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx),
- enc_output_info.enc_src_idx);
- if (vb->state != VB2_BUF_STATE_ACTIVE)
- dev_warn(inst->dev->dev,
- "%s: encoded buffer (%d) was not in ready queue %i.",
- __func__, enc_output_info.enc_src_idx, vb->state);
- else
- src_buf = to_vb2_v4l2_buffer(vb);
-
- if (src_buf) {
+ src_buf = v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, enc_output_info.enc_src_idx);
+ if (!src_buf) {
+ dev_warn(inst->dev->dev, "%s: no source buffer found\n", __func__);
+ } else {
inst->timestamp = src_buf->vb2_buf.timestamp;
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
- } else {
- dev_warn(inst->dev->dev, "%s: no source buffer with index: %d found\n",
- __func__, enc_output_info.enc_src_idx);
}
}

diff --git a/drivers/media/platform/marvell/cafe-driver.c b/drivers/media/platform/marvell/cafe-driver.c
index 632c15572aa8..22034df6cba9 100644
--- a/drivers/media/platform/marvell/cafe-driver.c
+++ b/drivers/media/platform/marvell/cafe-driver.c
@@ -609,6 +609,7 @@ static void cafe_pci_remove(struct pci_dev *pdev)
return;
}
cafe_shutdown(cam);
+ pci_disable_device(pdev);
kfree(cam);
}

diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c
index b2a562e1ee1c..52505af35c08 100644
--- a/drivers/media/platform/nuvoton/npcm-video.c
+++ b/drivers/media/platform/nuvoton/npcm-video.c
@@ -1720,10 +1720,12 @@ static int npcm_video_init(struct npcm_video *video)
if (rc) {
dev_err(dev, "Failed to set DMA mask\n");
of_reserved_mem_device_release(dev);
+ return rc;
}

rc = npcm_video_ece_init(video);
if (rc) {
+ of_reserved_mem_device_release(dev);
dev_err(dev, "Failed to initialize ECE\n");
return rc;
}
@@ -1748,42 +1750,55 @@ static int npcm_video_probe(struct platform_device *pdev)
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs)) {
dev_err(&pdev->dev, "Failed to parse VCD reg in DTS\n");
- return PTR_ERR(regs);
+ rc = PTR_ERR(regs);
+ goto err_free;
}

video->vcd_regmap = devm_regmap_init_mmio(&pdev->dev, regs,
&npcm_video_regmap_cfg);
if (IS_ERR(video->vcd_regmap)) {
dev_err(&pdev->dev, "Failed to initialize VCD regmap\n");
- return PTR_ERR(video->vcd_regmap);
+ rc = PTR_ERR(video->vcd_regmap);
+ goto err_free;
}

video->reset = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(video->reset)) {
dev_err(&pdev->dev, "Failed to get VCD reset control in DTS\n");
- return PTR_ERR(video->reset);
+ rc = PTR_ERR(video->reset);
+ goto err_free;
}

video->gcr_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"nuvoton,sysgcr");
- if (IS_ERR(video->gcr_regmap))
- return PTR_ERR(video->gcr_regmap);
+ if (IS_ERR(video->gcr_regmap)) {
+ rc = PTR_ERR(video->gcr_regmap);
+ goto err_free;
+ }

video->gfx_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"nuvoton,sysgfxi");
- if (IS_ERR(video->gfx_regmap))
- return PTR_ERR(video->gfx_regmap);
+ if (IS_ERR(video->gfx_regmap)) {
+ rc = PTR_ERR(video->gfx_regmap);
+ goto err_free;
+ }

rc = npcm_video_init(video);
if (rc)
- return rc;
+ goto err_free;

rc = npcm_video_setup_video(video);
if (rc)
- return rc;
+ goto err_release_mem;

dev_info(video->dev, "NPCM video driver probed\n");
return 0;
+
+err_release_mem:
+ of_reserved_mem_device_release(&pdev->dev);
+err_free:
+ kfree(video);
+ return rc;
}

static void npcm_video_remove(struct platform_device *pdev)
@@ -1798,6 +1813,7 @@ static void npcm_video_remove(struct platform_device *pdev)
v4l2_device_unregister(v4l2_dev);
if (video->ece.enable)
npcm_video_ece_stop(video);
+ kfree(video);
of_reserved_mem_device_release(dev);
}

diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
index 2d639b789910..e8545761b5ff 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
@@ -538,6 +538,8 @@ static int mxc_isi_probe(struct platform_device *pdev)
return 0;

err_xbar:
+ while (i--)
+ mxc_isi_pipe_cleanup(&isi->pipes[i]);
mxc_isi_crossbar_cleanup(&isi->crossbar);

return ret;
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
index 14d63ec36416..7547a6559d4c 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
@@ -11,6 +11,7 @@
#define __MXC_ISI_CORE_H__

#include <linux/list.h>
+#include <linux/math.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/types.h>
@@ -414,4 +415,19 @@ static inline void mxc_isi_debug_cleanup(struct mxc_isi_dev *isi)
}
#endif

+/*
+ * ISI scaling engine works in two parts: it performs pre-decimation of
+ * the image followed by bilinear filtering to achieve the desired
+ * downscaling factor.
+ *
+ * The decimation filter provides a maximum downscaling factor of 8, and
+ * the subsequent bilinear filter provides a maximum downscaling factor
+ * of 2. Combined, the maximum scaling factor can be up to 16.
+ */
+static inline unsigned int
+mxc_isi_clamp_downscale_16(unsigned int val, unsigned int max_val)
+{
+ return clamp(val, max(1U, DIV_ROUND_UP(max_val, 16)), max_val);
+}
+
#endif /* __MXC_ISI_CORE_H__ */
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
index 605a45124103..c580c831972e 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
@@ -491,6 +491,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi)

void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar)
{
+ v4l2_subdev_cleanup(&xbar->sd);
media_entity_cleanup(&xbar->sd.entity);
kfree(xbar->pads);
kfree(xbar->inputs);
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
index 0187d4ab97e8..16b20ea2d1db 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
@@ -112,7 +112,14 @@ static u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to,
else
*dec = 8;

- return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD);
+ /*
+ * The ISI rounds output dimensions up to the next integer (i.MX93 RM
+ * section 57.7.8). Calculate the scale factor such that the theoretical
+ * output (input / scale_factor) rounds up to exactly the desired
+ * output.
+ */
+ return min_t(u32, DIV_ROUND_UP(from * 0x1000, to * *dec),
+ ISI_DOWNSCALE_THRESHOLD);
}

static void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe,
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
index a39ad7a1ab18..de398b232d74 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
@@ -509,9 +509,14 @@ __mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx *ctx,
const enum mxc_isi_video_type type)
{
if (type == MXC_ISI_VIDEO_M2M_CAP) {
- /* Downscaling only */
- pix->width = min(pix->width, ctx->queues.out.format.width);
- pix->height = min(pix->height, ctx->queues.out.format.height);
+ const struct v4l2_pix_format_mplane *format =
+ &ctx->queues.out.format;
+
+ /* Downscaling only, by up to 16. */
+ pix->width = mxc_isi_clamp_downscale_16(pix->width,
+ format->width);
+ pix->height = mxc_isi_clamp_downscale_16(pix->height,
+ format->height);
}

return mxc_isi_format_try(ctx->m2m->pipe, pix, type);
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
index a41c51dd9ce0..2d0843c86534 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
@@ -641,16 +641,19 @@ static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd,
/* Composing is supported on the sink only. */
return -EINVAL;

- /* The sink crop is bound by the sink format downscaling only). */
+ /*
+ * The ISI supports downscaling only, with a factor up to 16.
+ * Clamp the compose rectangle size accordingly.
+ */
format = mxc_isi_pipe_get_pad_format(pipe, state,
MXC_ISI_PIPE_PAD_SINK);

sel->r.left = 0;
sel->r.top = 0;
- sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH,
- format->width);
- sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT,
- format->height);
+ sel->r.width = mxc_isi_clamp_downscale_16(sel->r.width,
+ format->width);
+ sel->r.height = mxc_isi_clamp_downscale_16(sel->r.height,
+ format->height);

rect = mxc_isi_pipe_get_pad_compose(pipe, state,
MXC_ISI_PIPE_PAD_SINK);
@@ -796,18 +799,20 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id)
irq = platform_get_irq(to_platform_device(isi->dev), id);
if (irq < 0) {
ret = irq;
- goto error;
+ goto error_subdev;
}

ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler,
0, dev_name(isi->dev), pipe);
if (ret < 0) {
dev_err(isi->dev, "failed to request IRQ (%d)\n", ret);
- goto error;
+ goto error_subdev;
}

return 0;

+error_subdev:
+ v4l2_subdev_cleanup(sd);
error:
media_entity_cleanup(&sd->entity);
mutex_destroy(&pipe->lock);
@@ -819,6 +824,7 @@ void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe)
{
struct v4l2_subdev *sd = &pipe->sd;

+ v4l2_subdev_cleanup(sd);
media_entity_cleanup(&sd->entity);
mutex_destroy(&pipe->lock);
}
diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c
index 2b50f9b96a34..0231985746ed 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-340.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-340.c
@@ -74,9 +74,9 @@ static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi)
writel_relaxed(!!enable, csid->base + CSID_RDI_CTRL(rdi));
}

-static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
{
- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
csid->res->formats->nformats,
input_format->code);
@@ -88,14 +88,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
* the four least significant bits of the five bit VC
* bitfield to generate an internal CID value.
*
- * CSID_RDI_CFG0(vc)
+ * CSID_RDI_CFG0(port)
* DT_ID : 28:27
* VC : 26:22
* DT : 21:16
*
* CID : VC 3:0 << 2 | DT_ID 1:0
*/
- dt_id = vc & 0x03;
+ dt_id = port & 0x03;

val = CSID_RDI_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */
val |= FIELD_PREP(CSID_RDI_CFG0_DT_MASK, format->data_type);
@@ -105,10 +105,11 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
if (enable)
val |= CSID_RDI_CFG0_ENABLE;

- dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x vc=%u)\n",
- csid->id, enable ? "enable" : "disable", format->data_type, vc);
+ dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x port=%u vc=%u)\n",
+ csid->id, enable ? "enable" : "disable", format->data_type,
+ port, vc);

- writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_CFG0(port));
}

static void csid_configure_stream(struct csid_device *csid, u8 enable)
@@ -117,9 +118,10 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)

__csid_configure_rx(csid, &csid->phy);

+ /* Loop through all enabled ports and configure a stream for each */
for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
if (csid->phy.en_vc & BIT(i)) {
- __csid_configure_rdi_stream(csid, enable, i);
+ __csid_configure_rdi_stream(csid, enable, i, 0);
__csid_ctrl_rdi(csid, enable, i);
}
}
diff --git a/drivers/media/platform/qcom/camss/camss-csid-680.c b/drivers/media/platform/qcom/camss/camss-csid-680.c
index 3ad3a174bcfb..edf01ba79907 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-680.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-680.c
@@ -219,9 +219,9 @@ static void __csid_configure_top(struct csid_device *csid)
CSID_TOP_IO_PATH_CFG0(csid->id));
}

-static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
{
- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
csid->res->formats->nformats,
input_format->code);
@@ -233,28 +233,28 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
lane_cnt = 4;

val = 0;
- writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc));
+ writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port));

/*
* DT_ID is a two bit bitfield that is concatenated with
* the four least significant bits of the five bit VC
* bitfield to generate an internal CID value.
*
- * CSID_RDI_CFG0(vc)
+ * CSID_RDI_CFG0(port)
* DT_ID : 28:27
* VC : 26:22
* DT : 21:16
*
* CID : VC 3:0 << 2 | DT_ID 1:0
*/
- dt_id = vc & 0x03;
+ dt_id = port & 0x03;

/* note: for non-RDI path, this should be format->decode_format */
val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT;
val |= format->data_type << RDI_CFG0_DATA_TYPE;
val |= vc << RDI_CFG0_VIRTUAL_CHANNEL;
val |= dt_id << RDI_CFG0_DT_ID;
- writel(val, csid->base + CSID_RDI_CFG0(vc));
+ writel(val, csid->base + CSID_RDI_CFG0(port));

val = RDI_CFG1_TIMESTAMP_STB_FRAME;
val |= RDI_CFG1_BYTE_CNTR_EN;
@@ -265,23 +265,23 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
val |= RDI_CFG1_CROP_V_EN;
val |= RDI_CFG1_PACKING_MIPI;

- writel(val, csid->base + CSID_RDI_CFG1(vc));
+ writel(val, csid->base + CSID_RDI_CFG1(port));

val = 0;
- writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port));

val = 1;
- writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port));

val = 0;
- writel(val, csid->base + CSID_RDI_CTRL(vc));
+ writel(val, csid->base + CSID_RDI_CTRL(port));

- val = readl(csid->base + CSID_RDI_CFG0(vc));
+ val = readl(csid->base + CSID_RDI_CFG0(port));
if (enable)
val |= RDI_CFG0_ENABLE;
else
val &= ~RDI_CFG0_ENABLE;
- writel(val, csid->base + CSID_RDI_CFG0(vc));
+ writel(val, csid->base + CSID_RDI_CFG0(port));
}

static void csid_configure_stream(struct csid_device *csid, u8 enable)
@@ -290,11 +290,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)

__csid_configure_top(csid);

- /* Loop through all enabled VCs and configure stream for each */
+ /* Loop through all enabled ports and configure a stream for each */
for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
if (csid->phy.en_vc & BIT(i)) {
- __csid_configure_rdi_stream(csid, enable, i);
- __csid_configure_rx(csid, &csid->phy, i);
+ __csid_configure_rdi_stream(csid, enable, i, 0);
+ __csid_configure_rx(csid, &csid->phy, 0);
__csid_ctrl_rdi(csid, enable, i);
}
}
diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
index 2a1746dcc1c5..eadcb2f7e3aa 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
@@ -203,10 +203,10 @@ static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi)
writel_relaxed(val, csid->base + CSID_RDI_CTRL(rdi));
}

-static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 port, u8 vc)
{
struct csid_testgen_config *tg = &csid->testgen;
- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
csid->res->formats->nformats,
input_format->code);
@@ -253,10 +253,10 @@ static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc)
writel_relaxed(val, csid->base + CSID_TPG_CTRL);
}

-static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
{
/* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */
- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
csid->res->formats->nformats,
input_format->code);
@@ -267,14 +267,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
* the four least significant bits of the five bit VC
* bitfield to generate an internal CID value.
*
- * CSID_RDI_CFG0(vc)
+ * CSID_RDI_CFG0(port)
* DT_ID : 28:27
* VC : 26:22
* DT : 21:16
*
* CID : VC 3:0 << 2 | DT_ID 1:0
*/
- u8 dt_id = vc & 0x03;
+ u8 dt_id = port & 0x03;

val = 1 << RDI_CFG0_BYTE_CNTR_EN;
val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN;
@@ -284,56 +284,57 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
val |= format->data_type << RDI_CFG0_DATA_TYPE;
val |= vc << RDI_CFG0_VIRTUAL_CHANNEL;
val |= dt_id << RDI_CFG0_DT_ID;
- writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_CFG0(port));

/* CSID_TIMESTAMP_STB_POST_IRQ */
val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL;
- writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_CFG1(port));

val = 1;
- writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port));

val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(port));

val = 1;
- writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port));

val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port));

val = 1;
- writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(port));

val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(port));

val = 1;
- writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(port));

val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(port));

val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_CTRL(port));

- val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc));
+ val = readl_relaxed(csid->base + CSID_RDI_CFG0(port));
val |= enable << RDI_CFG0_ENABLE;
- writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_CFG0(port));
}

static void csid_configure_stream(struct csid_device *csid, u8 enable)
{
struct csid_testgen_config *tg = &csid->testgen;
u8 i;
- /* Loop through all enabled VCs and configure stream for each */
+
+ /* Loop through all enabled ports and configure a stream for each */
for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
if (csid->phy.en_vc & BIT(i)) {
if (tg->enabled)
- __csid_configure_testgen(csid, enable, i);
+ __csid_configure_testgen(csid, enable, i, 0);

- __csid_configure_rdi_stream(csid, enable, i);
- __csid_configure_rx(csid, &csid->phy, i);
+ __csid_configure_rdi_stream(csid, enable, i, 0);
+ __csid_configure_rx(csid, &csid->phy, 0);
__csid_ctrl_rdi(csid, enable, i);
}
}
diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen3.c b/drivers/media/platform/qcom/camss/camss-csid-gen3.c
index bd059243790e..ed5c5766efd3 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-gen3.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen3.c
@@ -145,12 +145,12 @@ static void __csid_configure_wrapper(struct csid_device *csid)
writel(val, csid->camss->csid_wrapper_base + CSID_IO_PATH_CFG0(csid->id));
}

-static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
{
u32 val;
u8 lane_cnt = csid->phy.lane_cnt;
/* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */
- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
csid->res->formats->nformats,
input_format->code);
@@ -163,14 +163,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
* the four least significant bits of the five bit VC
* bitfield to generate an internal CID value.
*
- * CSID_RDI_CFG0(vc)
+ * CSID_RDI_CFG0(port)
* DT_ID : 28:27
* VC : 26:22
* DT : 21:16
*
* CID : VC 3:0 << 2 | DT_ID 1:0
*/
- u8 dt_id = vc & 0x03;
+ u8 dt_id = port & 0x03;

val = RDI_CFG0_TIMESTAMP_EN;
val |= RDI_CFG0_TIMESTAMP_STB_SEL;
@@ -180,7 +180,7 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
val |= format->data_type << RDI_CFG0_DT;
val |= dt_id << RDI_CFG0_DT_ID;

- writel(val, csid->base + CSID_RDI_CFG0(vc));
+ writel(val, csid->base + CSID_RDI_CFG0(port));

val = RDI_CFG1_PACKING_FORMAT_MIPI;
val |= RDI_CFG1_PIX_STORE;
@@ -189,22 +189,22 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
val |= RDI_CFG1_CROP_H_EN;
val |= RDI_CFG1_CROP_V_EN;

- writel(val, csid->base + CSID_RDI_CFG1(vc));
+ writel(val, csid->base + CSID_RDI_CFG1(port));

val = 0;
- writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port));

val = 1;
- writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port));

val = 0;
- writel(val, csid->base + CSID_RDI_CTRL(vc));
+ writel(val, csid->base + CSID_RDI_CTRL(port));

- val = readl(csid->base + CSID_RDI_CFG0(vc));
+ val = readl(csid->base + CSID_RDI_CFG0(port));

if (enable)
val |= RDI_CFG0_EN;
- writel(val, csid->base + CSID_RDI_CFG0(vc));
+ writel(val, csid->base + CSID_RDI_CFG0(port));
}

static void csid_configure_stream(struct csid_device *csid, u8 enable)
@@ -213,11 +213,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)

__csid_configure_wrapper(csid);

- /* Loop through all enabled VCs and configure stream for each */
+ /* Loop through all enabled ports and configure a stream for each */
for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
if (csid->phy.en_vc & BIT(i)) {
- __csid_configure_rdi_stream(csid, enable, i);
- __csid_configure_rx(csid, &csid->phy, i);
+ __csid_configure_rdi_stream(csid, enable, i, 0);
+ __csid_configure_rx(csid, &csid->phy, 0);
__csid_ctrl_rdi(csid, enable, i);
}
}
diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c
index 487eb0917c0e..5e2b0a571c47 100644
--- a/drivers/media/platform/qcom/iris/iris_probe.c
+++ b/drivers/media/platform/qcom/iris/iris_probe.c
@@ -252,12 +252,12 @@ static int iris_probe(struct platform_device *pdev)
core->iris_platform_data = of_device_get_match_data(core->dev);

ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr,
- iris_hfi_isr_handler, IRQF_TRIGGER_HIGH, "iris", core);
+ iris_hfi_isr_handler,
+ IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN,
+ "iris", core);
if (ret)
return ret;

- disable_irq_nosync(core->irq);
-
iris_init_ops(core);
core->iris_platform_data->init_hfi_command_ops(core);
core->iris_platform_data->init_hfi_response_ops(core);
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h
index a5a57369ef0e..10e62f2646d0 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h
@@ -60,6 +60,7 @@
#define ICnMC_CSCTHR BIT(5)
#define ICnMC_INF(x) ((x) << 16)
#define ICnMC_VCSEL(x) ((x) << 22)
+#define ICnMC_VCSEL_MASK GENMASK(23, 22)
#define ICnMC_INF_MASK GENMASK(21, 16)

#define ICnMS_IA BIT(2)
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
index 162e2ace6931..6aea7c244df1 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
@@ -262,19 +262,24 @@ static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru,
u8 csi_vc)
{
const struct rzg2l_cru_info *info = cru->info;
- u32 icnmc = ICnMC_INF(ip_fmt->datatype);
+ u32 icnmc = rzg2l_cru_read(cru, info->image_conv) & ~(ICnMC_INF_MASK |
+ ICnMC_VCSEL_MASK);
+ icnmc |= ICnMC_INF(ip_fmt->datatype);

+ /*
+ * VC filtering goes through SVC register on G3E/V2H.
+ *
+ * FIXME: virtual channel filtering is likely broken and only VC=0
+ * works.
+ */
if (cru->info->regs[ICnSVC]) {
rzg2l_cru_write(cru, ICnSVCNUM, csi_vc);
rzg2l_cru_write(cru, ICnSVC, ICnSVC_SVC0(0) | ICnSVC_SVC1(1) |
ICnSVC_SVC2(2) | ICnSVC_SVC3(3));
+ } else {
+ icnmc |= ICnMC_VCSEL(csi_vc);
}

- icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK;
-
- /* Set virtual channel CSI2 */
- icnmc |= ICnMC_VCSEL(csi_vc);
-
rzg2l_cru_write(cru, info->image_conv, icnmc);
}

diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c
index b167f1bab7ef..932fed38cf3f 100644
--- a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c
+++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c
@@ -297,12 +297,33 @@ static int rzv2h_ivc_start_streaming(struct vb2_queue *q, unsigned int count)
static void rzv2h_ivc_stop_streaming(struct vb2_queue *q)
{
struct rzv2h_ivc *ivc = vb2_get_drv_priv(q);
- u32 val = 0;
+ unsigned int loop = 5;

- rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_STOP, RZV2H_IVC_REG_FM_STOP_FSTOP);
- readl_poll_timeout(ivc->base + RZV2H_IVC_REG_FM_STOP,
- val, !(val & RZV2H_IVC_REG_FM_STOP_FSTOP),
- 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC);
+ /*
+ * If no frame transfer is in progress, we're done, otherwise, wait for
+ * the transfer to complete.
+ *
+ * Transferring a 1920x1080@10bit frame to the ISP takes less than 5
+ * msec so sleep for 2.5 msec (+- 25%) and give up after 5 attempts.
+ */
+ for (; loop > 0; loop--) {
+ unsigned int vvalid_ifp;
+
+ /*
+ * Inspect the ivc->vvalid_ifp variable holding the spinlock not
+ * to the race with the rzv2h_ivc_buffer_done() call in the irq
+ * handler.
+ */
+ scoped_guard(spinlock_irq, &ivc->spinlock) {
+ vvalid_ifp = ivc->vvalid_ifp;
+ }
+ if (vvalid_ifp < 2)
+ break;
+
+ fsleep(2500);
+ }
+ if (!loop)
+ dev_err(ivc->dev, "Failed to stop streaming\n");

rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_ERROR);
video_device_pipeline_stop(&ivc->vdev.dev);
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index e5663fbe6422..eeb0199864dd 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -2195,6 +2195,7 @@ static int dcmi_probe(struct platform_device *pdev)
return 0;

err_cleanup:
+ v4l2_async_nf_unregister(&dcmi->notifier);
v4l2_async_nf_cleanup(&dcmi->notifier);
err_media_entity_cleanup:
media_entity_cleanup(&dcmi->vdev->entity);
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
index a42f43d19f9e..f0e809458489 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
@@ -401,8 +401,10 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
*/
if (!vcap->s_subdev) {
pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]);
- if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
- return -EINVAL;
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) {
+ ret = -EINVAL;
+ goto err_buffer_done;
+ }
vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity);
vcap->s_subdev_pad_nb = pad->index;
}
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
index e911c7f7acc5..4781db21c205 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
@@ -234,8 +234,10 @@ static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count)
int ret;

csi_fmt = sun4i_csi_find_format(&csi->fmt.pixelformat, NULL);
- if (!csi_fmt)
- return -EINVAL;
+ if (!csi_fmt) {
+ ret = -EINVAL;
+ goto err_clear_dma_queue;
+ }

dev_dbg(csi->dev, "Starting capture\n");

diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
index 61ad20b18b8d..4c8957505a50 100644
--- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
@@ -506,9 +506,9 @@ static void hdmirx_hpd_ctrl(struct snps_hdmirx_dev *hdmirx_dev, bool en)
hdmirx_writel(hdmirx_dev, CORE_CONFIG,
hdmirx_dev->hpd_trigger_level_high ? en : !en);

- /* 100ms delay as per HDMI spec */
+ /* 100ms delay as per HDMI spec + extra 50ms to cover internal delay */
if (!en)
- msleep(100);
+ msleep(100 + 50);
}

static void hdmirx_write_edid_data(struct snps_hdmirx_dev *hdmirx_dev,
diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c
index 15df3ea2f77e..91cb6223561a 100644
--- a/drivers/media/platform/ti/davinci/vpif_capture.c
+++ b/drivers/media/platform/ti/davinci/vpif_capture.c
@@ -1498,7 +1498,7 @@ vpif_capture_get_pdata(struct platform_device *pdev,
* video ports & endpoints data.
*/
if (pdev->dev.parent && pdev->dev.parent->of_node)
- pdev->dev.of_node = pdev->dev.parent->of_node;
+ device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
return pdev->dev.platform_data;

diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c
index 0e91e87bda9b..cb0a5a07a3d4 100644
--- a/drivers/media/platform/ti/vpe/vip.c
+++ b/drivers/media/platform/ti/vpe/vip.c
@@ -9,6 +9,7 @@
*/

#include <linux/clk.h>
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
@@ -3389,7 +3390,6 @@ static int vip_probe_complete(struct platform_device *pdev)
struct vip_port *port;
struct vip_dev *dev;
struct device_node *parent = pdev->dev.of_node;
- struct fwnode_handle *ep = NULL;
unsigned int syscon_args[5];
int ret, i, slice_id, port_id, p;

@@ -3411,8 +3411,9 @@ static int vip_probe_complete(struct platform_device *pdev)
ctrl->syscon_bit_field[i] = syscon_args[i + 1];

for (p = 0; p < (VIP_NUM_PORTS * VIP_NUM_SLICES); p++) {
- ep = fwnode_graph_get_next_endpoint_by_regs(of_fwnode_handle(parent),
- p, 0);
+ struct fwnode_handle *ep __free(fwnode_handle) =
+ fwnode_graph_get_next_endpoint_by_regs(
+ of_fwnode_handle(parent), p, 0);
if (!ep)
continue;

@@ -3447,7 +3448,6 @@ static int vip_probe_complete(struct platform_device *pdev)
port = dev->ports[port_id];

vip_register_subdev_notify(port, ep);
- fwnode_handle_put(ep);
}
return 0;
}
@@ -3472,7 +3472,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice)
ret = devm_request_irq(&pdev->dev, dev->irq, vip_irq,
0, VIP_MODULE_NAME, dev);
if (ret < 0)
- return -ENOMEM;
+ return ret;

spin_lock_init(&dev->slock);
mutex_init(&dev->mutex);
@@ -3490,7 +3490,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice)

parser = devm_kzalloc(&pdev->dev, sizeof(*dev->parser), GFP_KERNEL);
if (!parser)
- return PTR_ERR_OR_ZERO(parser);
+ return -ENOMEM;

parser->base = dev->base + (slice ? VIP_SLICE1_PARSER : VIP_SLICE0_PARSER);
if (IS_ERR(parser->base))
@@ -3502,7 +3502,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice)
dev->sc_assigned = VIP_NOT_ASSIGNED;
sc = devm_kzalloc(&pdev->dev, sizeof(*dev->sc), GFP_KERNEL);
if (!sc)
- return PTR_ERR_OR_ZERO(sc);
+ return -ENOMEM;

sc->base = dev->base + (slice ? VIP_SLICE1_SC : VIP_SLICE0_SC);
if (IS_ERR(sc->base))
@@ -3514,7 +3514,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice)
dev->csc_assigned = VIP_NOT_ASSIGNED;
csc = devm_kzalloc(&pdev->dev, sizeof(*dev->csc), GFP_KERNEL);
if (!csc)
- return PTR_ERR_OR_ZERO(csc);
+ return -ENOMEM;

csc->base = dev->base + (slice ? VIP_SLICE1_CSC : VIP_SLICE0_CSC);
if (IS_ERR(csc->base))
diff --git a/drivers/media/platform/ti/vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c
index a7e5a85e72a1..81bd1f9cee30 100644
--- a/drivers/media/platform/ti/vpe/vpe.c
+++ b/drivers/media/platform/ti/vpe/vpe.c
@@ -2539,7 +2539,8 @@ static int vpe_probe(struct platform_device *pdev)
"vpe_top");
if (!dev->res) {
dev_err(&pdev->dev, "missing 'vpe_top' resources data\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto v4l2_dev_unreg;
}

/*
diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c
index fcf3bd9bcda2..83af9fa1ce94 100644
--- a/drivers/media/platform/verisilicon/hantro_v4l2.c
+++ b/drivers/media/platform/verisilicon/hantro_v4l2.c
@@ -222,6 +222,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv,
unsigned int num_fmts, i, j = 0;
bool skip_mode_none, enum_all_formats;
u32 index = f->index & ~V4L2_FMTDESC_FLAG_ENUM_ALL;
+ bool need_postproc = ctx->need_postproc;

/*
* If the V4L2_FMTDESC_FLAG_ENUM_ALL flag is set, we want to enumerate all
@@ -230,6 +231,9 @@ static int vidioc_enum_fmt(struct file *file, void *priv,
enum_all_formats = !!(f->index & V4L2_FMTDESC_FLAG_ENUM_ALL);
f->index = index;

+ if (enum_all_formats)
+ need_postproc = HANTRO_AUTO_POSTPROC;
+
/*
* When dealing with an encoder:
* - on the capture side we want to filter out all MODE_NONE formats.
@@ -242,7 +246,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv,
*/
skip_mode_none = capture == ctx->is_encoder;

- formats = hantro_get_formats(ctx, &num_fmts, HANTRO_AUTO_POSTPROC);
+ formats = hantro_get_formats(ctx, &num_fmts, need_postproc);
for (i = 0; i < num_fmts; i++) {
bool mode_none = formats[i].codec_mode == HANTRO_MODE_NONE;
fmt = &formats[i];
diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c
index 9980346cb5ea..bfe89782dce4 100644
--- a/drivers/media/radio/radio-si476x.c
+++ b/drivers/media/radio/radio-si476x.c
@@ -1493,6 +1493,7 @@ static int si476x_radio_probe(struct platform_device *pdev)
return 0;
exit:
v4l2_ctrl_handler_free(radio->videodev.ctrl_handler);
+ v4l2_device_unregister(&radio->v4l2dev);
return rval;
}

diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c
index a8a76434989c..fd69b4ee16f4 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c
@@ -594,8 +594,10 @@ static int __init vidtv_bridge_init(void)
int ret;

ret = platform_device_register(&vidtv_bridge_dev);
- if (ret)
+ if (ret) {
+ platform_device_put(&vidtv_bridge_dev);
return ret;
+ }

ret = platform_driver_register(&vidtv_bridge_driver);
if (ret)
diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
index 15167e127461..fee0c7a09c4f 100644
--- a/drivers/media/test-drivers/vimc/vimc-core.c
+++ b/drivers/media/test-drivers/vimc/vimc-core.c
@@ -421,6 +421,7 @@ static int __init vimc_init(void)
if (ret) {
dev_err(&vimc_pdev.dev,
"platform device registration failed (err=%d)\n", ret);
+ platform_device_put(&vimc_pdev);
return ret;
}

diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c
index c8bf9b4d406c..62cfb5feb2cf 100644
--- a/drivers/media/test-drivers/vivid/vivid-core.c
+++ b/drivers/media/test-drivers/vivid/vivid-core.c
@@ -2289,8 +2289,10 @@ static int __init vivid_init(void)
}
}
ret = platform_device_register(&vivid_pdev);
- if (ret)
+ if (ret) {
+ platform_device_put(&vivid_pdev);
goto free_output_strings;
+ }
ret = platform_driver_register(&vivid_pdrv);
if (ret)
goto unreg_device;
@@ -2311,7 +2313,7 @@ static int __init vivid_init(void)
destroy_hdmi_wq:
destroy_workqueue(update_hdmi_ctrls_workqueue);
unreg_driver:
- platform_driver_register(&vivid_pdrv);
+ platform_driver_unregister(&vivid_pdrv);
unreg_device:
platform_device_unregister(&vivid_pdev);
free_output_strings:
diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c
index f94c15ff84f7..a8a134b36720 100644
--- a/drivers/media/test-drivers/vivid/vivid-ctrls.c
+++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c
@@ -609,17 +609,24 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
break;
case VIVID_CID_REDUCED_FPS:
dev->reduced_fps = ctrl->val;
- vivid_update_format_cap(dev, true);
+ if (dev->input_type[dev->input] == HDMI)
+ vivid_update_reduced_fps(dev);
break;
case VIVID_CID_HAS_CROP_CAP:
+ if (vb2_is_busy(&dev->vb_vid_cap_q))
+ return -EBUSY;
dev->has_crop_cap = ctrl->val;
vivid_update_format_cap(dev, true);
break;
case VIVID_CID_HAS_COMPOSE_CAP:
+ if (vb2_is_busy(&dev->vb_vid_cap_q))
+ return -EBUSY;
dev->has_compose_cap = ctrl->val;
vivid_update_format_cap(dev, true);
break;
case VIVID_CID_HAS_SCALER_CAP:
+ if (vb2_is_busy(&dev->vb_vid_cap_q))
+ return -EBUSY;
dev->has_scaler_cap = ctrl->val;
vivid_update_format_cap(dev, true);
break;
@@ -1116,14 +1123,20 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl)

switch (ctrl->id) {
case VIVID_CID_HAS_CROP_OUT:
+ if (vb2_is_busy(&dev->vb_vid_out_q))
+ return -EBUSY;
dev->has_crop_out = ctrl->val;
vivid_update_format_out(dev);
break;
case VIVID_CID_HAS_COMPOSE_OUT:
+ if (vb2_is_busy(&dev->vb_vid_out_q))
+ return -EBUSY;
dev->has_compose_out = ctrl->val;
vivid_update_format_out(dev);
break;
case VIVID_CID_HAS_SCALER_OUT:
+ if (vb2_is_busy(&dev->vb_vid_out_q))
+ return -EBUSY;
dev->has_scaler_out = ctrl->val;
vivid_update_format_out(dev);
break;
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
index b95f06a9b5ae..e20449084709 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
@@ -364,6 +364,24 @@ static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev)
return TPG_PIXEL_ASPECT_SQUARE;
}

+void vivid_update_reduced_fps(struct vivid_dev *dev)
+{
+ struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt;
+ unsigned int size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
+ u64 pixelclock;
+
+ if (dev->reduced_fps && can_reduce_fps(bt)) {
+ pixelclock = div_u64(bt->pixelclock * 1000, 1001);
+ bt->flags |= V4L2_DV_FL_REDUCED_FPS;
+ } else {
+ pixelclock = bt->pixelclock;
+ bt->flags &= ~V4L2_DV_FL_REDUCED_FPS;
+ }
+ dev->timeperframe_vid_cap = (struct v4l2_fract) {
+ size / 100, (u32)pixelclock / 100
+ };
+}
+
/*
* Called whenever the format has to be reset which can occur when
* changing inputs, standard, timings, etc.
@@ -372,8 +390,12 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls)
{
struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt;
u32 dims[V4L2_CTRL_MAX_DIMS] = {};
- unsigned size;
- u64 pixelclock;
+
+ /*
+ * This resets the format, so must never be called while vb2_is_busy().
+ */
+ if (WARN_ON(vb2_is_busy(&dev->vb_vid_cap_q)))
+ return;

switch (dev->input_type[dev->input]) {
case WEBCAM:
@@ -402,17 +424,7 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls)
case HDMI:
dev->src_rect.width = bt->width;
dev->src_rect.height = bt->height;
- size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
- if (dev->reduced_fps && can_reduce_fps(bt)) {
- pixelclock = div_u64(bt->pixelclock * 1000, 1001);
- bt->flags |= V4L2_DV_FL_REDUCED_FPS;
- } else {
- pixelclock = bt->pixelclock;
- bt->flags &= ~V4L2_DV_FL_REDUCED_FPS;
- }
- dev->timeperframe_vid_cap = (struct v4l2_fract) {
- size / 100, (u32)pixelclock / 100
- };
+ vivid_update_reduced_fps(dev);
if (bt->interlaced)
dev->field_cap = V4L2_FIELD_ALTERNATE;
else
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.h b/drivers/media/test-drivers/vivid/vivid-vid-cap.h
index 38a99f7e038e..d08a85927510 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.h
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.h
@@ -9,6 +9,7 @@
#define _VIVID_VID_CAP_H_

void vivid_update_quality(struct vivid_dev *dev);
+void vivid_update_reduced_fps(struct vivid_dev *dev);
void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls);
void vivid_update_outputs(struct vivid_dev *dev);
void vivid_update_connected_outputs(struct vivid_dev *dev);
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c
index 8c037b90833e..23e1d5a189ee 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-out.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c
@@ -214,6 +214,12 @@ void vivid_update_format_out(struct vivid_dev *dev)
unsigned size, p;
u64 pixelclock;

+ /*
+ * This resets the format, so must never be called while vb2_is_busy().
+ */
+ if (WARN_ON(vb2_is_busy(&dev->vb_vid_out_q)))
+ return;
+
switch (dev->output_type[dev->output]) {
case SVID:
default:
diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c
index 8f6b721ba107..57edb42463e8 100644
--- a/drivers/media/usb/airspy/airspy.c
+++ b/drivers/media/usb/airspy/airspy.c
@@ -522,11 +522,13 @@ static int airspy_start_streaming(struct vb2_queue *vq, unsigned int count)

dev_dbg(s->dev, "\n");

- if (!s->udev)
- return -ENODEV;
-
mutex_lock(&s->v4l2_lock);

+ if (!s->udev) {
+ ret = -ENODEV;
+ goto err_clear_bit;
+ }
+
s->sequence = 0;

set_bit(POWER_ON, &s->flags);
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index b75535d6abaf..69b24205bc56 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -1573,7 +1573,8 @@ static int cx231xx_init_v4l2(struct cx231xx *dev,
dev->video_mode.end_point_addr,
dev->video_mode.num_alt);

- dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->video_mode.num_alt, GFP_KERNEL);
+ dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32,
+ dev->video_mode.num_alt, GFP_KERNEL);
if (dev->video_mode.alt_max_pkt_size == NULL)
return -ENOMEM;

@@ -1614,7 +1615,8 @@ static int cx231xx_init_v4l2(struct cx231xx *dev,
dev->vbi_mode.num_alt);

/* compute alternate max packet sizes for vbi */
- dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->vbi_mode.num_alt, GFP_KERNEL);
+ dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32,
+ dev->vbi_mode.num_alt, GFP_KERNEL);
if (dev->vbi_mode.alt_max_pkt_size == NULL)
return -ENOMEM;

@@ -1656,7 +1658,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev,
"sliced CC EndPoint Addr 0x%x, Alternate settings: %i\n",
dev->sliced_cc_mode.end_point_addr,
dev->sliced_cc_mode.num_alt);
- dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->sliced_cc_mode.num_alt, GFP_KERNEL);
+ dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32,
+ dev->sliced_cc_mode.num_alt,
+ GFP_KERNEL);
if (dev->sliced_cc_mode.alt_max_pkt_size == NULL)
return -ENOMEM;

@@ -1720,7 +1724,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
udev = interface_to_usbdev(interface);

/* allocate memory for our device state and initialize it */
- dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL);
+ dev = devm_kzalloc(&interface->dev, sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
retval = -ENOMEM;
goto err_if;
@@ -1850,7 +1854,9 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
dev->ts1_mode.end_point_addr,
dev->ts1_mode.num_alt);

- dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->ts1_mode.num_alt, GFP_KERNEL);
+ dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32,
+ dev->ts1_mode.num_alt,
+ GFP_KERNEL);
if (dev->ts1_mode.alt_max_pkt_size == NULL) {
retval = -ENOMEM;
goto err_video_alt;
diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c
index 1ff98956b680..0614087c3c3c 100644
--- a/drivers/media/usb/msi2500/msi2500.c
+++ b/drivers/media/usb/msi2500/msi2500.c
@@ -541,7 +541,8 @@ static int msi2500_isoc_init(struct msi2500_dev *dev)
}

/* Must be called with vb_queue_lock hold */
-static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev)
+static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev,
+ enum vb2_buffer_state state)
{
unsigned long flags;

@@ -554,7 +555,7 @@ static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev)
buf = list_entry(dev->queued_bufs.next,
struct msi2500_frame_buf, list);
list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
}
spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
}
@@ -830,25 +831,40 @@ static int msi2500_start_streaming(struct vb2_queue *vq, unsigned int count)

dev_dbg(dev->dev, "\n");

- if (!dev->udev)
- return -ENODEV;
+ if (!dev->udev) {
+ ret = -ENODEV;
+ goto err_cleanup;
+ }

- if (mutex_lock_interruptible(&dev->v4l2_lock))
- return -ERESTARTSYS;
+ if (mutex_lock_interruptible(&dev->v4l2_lock)) {
+ ret = -ERESTARTSYS;
+ goto err_cleanup;
+ }

/* wake-up tuner */
v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 1);

ret = msi2500_set_usb_adc(dev);
+ if (ret)
+ goto err_unlock_cleanup;

ret = msi2500_isoc_init(dev);
if (ret)
- msi2500_cleanup_queued_bufs(dev);
+ goto err_unlock_cleanup;

ret = msi2500_ctrl_msg(dev, CMD_START_STREAMING, 0);
+ if (ret)
+ goto err_isoc_cleanup;

mutex_unlock(&dev->v4l2_lock);
+ return 0;

+err_isoc_cleanup:
+ msi2500_isoc_cleanup(dev);
+err_unlock_cleanup:
+ mutex_unlock(&dev->v4l2_lock);
+err_cleanup:
+ msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
return ret;
}

@@ -863,7 +879,7 @@ static void msi2500_stop_streaming(struct vb2_queue *vq)
if (dev->udev)
msi2500_isoc_cleanup(dev);

- msi2500_cleanup_queued_bufs(dev);
+ msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR);

/* according to tests, at least 700us delay is required */
msleep(20);
diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c
index c416e2fc5754..e2884b04d952 100644
--- a/drivers/media/usb/pwc/pwc-if.c
+++ b/drivers/media/usb/pwc/pwc-if.c
@@ -710,11 +710,15 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
struct pwc_device *pdev = vb2_get_drv_priv(vq);
int r;

- if (!pdev->udev)
+ if (!pdev->udev) {
+ pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
return -ENODEV;
+ }

- if (mutex_lock_interruptible(&pdev->v4l2_lock))
+ if (mutex_lock_interruptible(&pdev->v4l2_lock)) {
+ pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
return -ERESTARTSYS;
+ }
/* Turn on camera and set LEDS on */
pwc_camera_power(pdev, 1);
pwc_set_leds(pdev, leds[0], leds[1]);
@@ -726,6 +730,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
pwc_camera_power(pdev, 0);
/* And cleanup any queued bufs!! */
pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
+ if (pdev->fill_buf) {
+ vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf,
+ VB2_BUF_STATE_QUEUED);
+ pdev->fill_buf = NULL;
+ }
}
mutex_unlock(&pdev->v4l2_lock);

diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 6b375720e395..ba047d7d8601 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -971,6 +971,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
struct v4l2_ctrl_hevc_ext_sps_st_rps *p_hevc_st_rps;
struct v4l2_ctrl_hevc_sps *p_hevc_sps;
struct v4l2_ctrl_hevc_pps *p_hevc_pps;
+ struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params;
struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
struct v4l2_area *area;
@@ -1260,6 +1261,18 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
break;

case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
+ p_hevc_slice_params = p;
+
+ if (p_hevc_slice_params->num_ref_idx_l0_active_minus1 >=
+ V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
+ return -EINVAL;
+
+ if (p_hevc_slice_params->slice_type != V4L2_HEVC_SLICE_TYPE_B)
+ break;
+
+ if (p_hevc_slice_params->num_ref_idx_l1_active_minus1 >=
+ V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
+ return -EINVAL;
break;

case V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS:
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-request.c b/drivers/media/v4l2-core/v4l2-ctrls-request.c
index 4b7e6981b8d6..5c2fc767c6a6 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-request.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-request.c
@@ -348,13 +348,12 @@ void v4l2_ctrl_request_complete(struct media_request *req,
ret = v4l2_ctrl_handler_init(hdl, (main_hdl->nr_of_buckets - 1) * 8);
if (!ret)
ret = v4l2_ctrl_request_bind(req, hdl, main_hdl);
- if (ret) {
- v4l2_ctrl_handler_free(hdl);
- kfree(hdl);
- return;
- }
+ if (ret)
+ goto error;
hdl->request_is_queued = true;
obj = media_request_object_find(req, &req_ops, main_hdl);
+ if (!obj)
+ goto error;
}
hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj);

@@ -389,6 +388,11 @@ void v4l2_ctrl_request_complete(struct media_request *req,
mutex_unlock(main_hdl->lock);
media_request_object_complete(obj);
media_request_object_put(obj);
+ return;
+
+error:
+ v4l2_ctrl_handler_free(hdl);
+ kfree(hdl);
}
EXPORT_SYMBOL(v4l2_ctrl_request_complete);

diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 77f3298821b5..62a3a452f788 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -1256,7 +1256,7 @@ v4l2_async_nf_parse_fwnode_sensor(struct device *dev,
return 0;
}

-int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
+int __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module)
{
struct v4l2_async_notifier *notifier;
int ret;
@@ -1282,7 +1282,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
if (ret < 0)
goto out_cleanup;

- ret = v4l2_async_register_subdev(sd);
+ ret = __v4l2_async_register_subdev(sd, module);
if (ret < 0)
goto out_unregister;

@@ -1300,7 +1300,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)

return ret;
}
-EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor);
+EXPORT_SYMBOL_GPL(__v4l2_async_register_subdev_sensor);

MODULE_DESCRIPTION("V4L2 fwnode binding parsing library");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 831c69c958b8..d00d27d49060 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -2504,6 +2504,10 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable)
u64 source_mask = 0;
int pad_index = -1;

+ if (WARN_ON(!v4l2_subdev_has_op(sd, pad, enable_streams) ||
+ !v4l2_subdev_has_op(sd, pad, disable_streams)))
+ return -ENOIOCTLCMD;
+
/*
* Find the source pad. This helper is meant for subdevs that have a
* single source pad, so failures shouldn't happen, but catch them
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index fcde082eb5e3..cfb87ab8667f 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -4,6 +4,7 @@
* Intel Management Engine Interface (Intel MEI) Linux driver
*/

+#include <linux/cleanup.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
@@ -1330,15 +1331,16 @@ static void mei_dev_bus_put(struct mei_device *bus)
static void mei_cl_bus_dev_release(struct device *dev)
{
struct mei_cl_device *cldev = to_mei_cl_device(dev);
- struct mei_device *mdev = cldev->cl->dev;
+ struct mei_device *bus = cldev->bus;
struct mei_cl *cl;

- mei_cl_flush_queues(cldev->cl, NULL);
- mei_me_cl_put(cldev->me_cl);
- mei_dev_bus_put(cldev->bus);
-
- list_for_each_entry(cl, &mdev->file_list, link)
- WARN_ON(cl == cldev->cl);
+ scoped_guard(mutex, &bus->device_lock) {
+ mei_cl_flush_queues(cldev->cl, NULL);
+ mei_me_cl_put(cldev->me_cl);
+ list_for_each_entry(cl, &bus->file_list, link)
+ WARN_ON(cl == cldev->cl);
+ }
+ mei_dev_bus_put(bus);

kfree(cldev->cl);
kfree(cldev);
diff --git a/drivers/misc/nsm.c b/drivers/misc/nsm.c
index ef7b32742340..3960506eb7ab 100644
--- a/drivers/misc/nsm.c
+++ b/drivers/misc/nsm.c
@@ -367,7 +367,7 @@ static long nsm_dev_ioctl(struct file *file, unsigned int cmd,
/* Copy user argument struct to kernel argument struct */
r = -EFAULT;
if (copy_from_user(&raw, argp, _IOC_SIZE(cmd)))
- goto out;
+ return r;

mutex_lock(&nsm->lock);

@@ -413,6 +413,7 @@ static int nsm_device_init_vq(struct virtio_device *vdev)
}

static const struct file_operations nsm_dev_fops = {
+ .owner = THIS_MODULE,
.unlocked_ioctl = nsm_dev_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
diff --git a/drivers/mtd/mtd_virt_concat.c b/drivers/mtd/mtd_virt_concat.c
index 5db6e648927e..da4277ced4d6 100644
--- a/drivers/mtd/mtd_virt_concat.c
+++ b/drivers/mtd/mtd_virt_concat.c
@@ -75,8 +75,8 @@ void mtd_virt_concat_destroy_joins(void)
if (item->concat) {
mtd_device_unregister(mtd);
kfree(mtd->name);
- mtd_concat_destroy(mtd);
mtd_virt_concat_put_mtd_devices(item->concat);
+ mtd_concat_destroy(mtd);
}
}
}
@@ -126,8 +126,8 @@ int mtd_virt_concat_destroy(struct mtd_info *mtd)
if (concat->mtd.name) {
del_mtd_device(&concat->mtd);
kfree(concat->mtd.name);
- mtd_concat_destroy(&concat->mtd);
mtd_virt_concat_put_mtd_devices(item->concat);
+ mtd_concat_destroy(&concat->mtd);
}

for (idx = 0; idx < item->count; idx++)
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 576537774628..16629382a787 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -105,6 +105,15 @@ static void mtd_release(struct device *dev)
device_destroy(&mtd_class, index + 1);
}

+/*
+ * No-op device release used in add_mtd_device() error paths.
+ * Prevents mtd_release() from being called via device_release(),
+ * which would free the mtd_info that the caller still manages.
+ */
+static void mtd_dev_release_nop(struct device *dev)
+{
+}
+
static void mtd_device_release(struct kref *kref)
{
struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt);
@@ -799,10 +808,8 @@ int add_mtd_device(struct mtd_info *mtd)
mtd_check_of_node(mtd);
of_node_get(mtd_get_of_node(mtd));
error = device_register(&mtd->dev);
- if (error) {
- put_device(&mtd->dev);
+ if (error)
goto fail_added;
- }

/* Add the nvmem provider */
error = mtd_nvmem_add(mtd);
@@ -840,8 +847,16 @@ int add_mtd_device(struct mtd_info *mtd)
return 0;

fail_nvmem_add:
- device_unregister(&mtd->dev);
+ device_del(&mtd->dev);
fail_added:
+ /*
+ * Clear type and set nop release to prevent mtd_release() ->
+ * release_mtd_partition() -> free_partition() from freeing mtd.
+ * The caller handles cleanup on failure.
+ */
+ mtd->dev.type = NULL;
+ mtd->dev.release = mtd_dev_release_nop;
+ put_device(&mtd->dev);
of_node_put(mtd_get_of_node(mtd));
fail_devname:
idr_remove(&mtd_idr, i);
diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c
index 866933fc8426..f33f753f0a9f 100644
--- a/drivers/mtd/mtdswap.c
+++ b/drivers/mtd/mtdswap.c
@@ -125,6 +125,7 @@ struct mtdswap_dev {

char *page_buf;
char *oob_buf;
+ struct dentry *debugfs_stats;
};

struct mtdswap_oobdata {
@@ -1262,7 +1263,8 @@ static int mtdswap_add_debugfs(struct mtdswap_dev *d)
if (IS_ERR_OR_NULL(root))
return -1;

- debugfs_create_file("mtdswap_stats", S_IRUSR, root, d, &mtdswap_fops);
+ d->debugfs_stats = debugfs_create_file("mtdswap_stats", 0400, root,
+ d, &mtdswap_fops);

return 0;
}
@@ -1463,6 +1465,7 @@ static void mtdswap_remove_dev(struct mtd_blktrans_dev *dev)
{
struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev);

+ debugfs_remove(d->debugfs_stats);
del_mtd_blktrans_dev(dev);
mtdswap_cleanup(d);
kfree(d);
diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c
index c75bb8b80cc1..96703f0a418e 100644
--- a/drivers/mtd/nand/ecc-mtk.c
+++ b/drivers/mtd/nand/ecc-mtk.c
@@ -123,8 +123,8 @@ static int mt7622_ecc_regs[] = {
[ECC_DECIRQ_STA] = 0x144,
};

-static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
- enum mtk_ecc_operation op)
+static inline int mtk_ecc_wait_idle(struct mtk_ecc *ecc,
+ enum mtk_ecc_operation op)
{
struct device *dev = ecc->dev;
u32 val;
@@ -136,6 +136,8 @@ static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
if (ret)
dev_warn(dev, "%s NOT idle\n",
op == ECC_ENCODE ? "encoder" : "decoder");
+
+ return ret;
}

static irqreturn_t mtk_ecc_irq(int irq, void *id)
@@ -312,7 +314,11 @@ int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
return ret;
}

- mtk_ecc_wait_idle(ecc, op);
+ ret = mtk_ecc_wait_idle(ecc, op);
+ if (ret) {
+ mutex_unlock(&ecc->lock);
+ return ret;
+ }

ret = mtk_ecc_config(ecc, config);
if (ret) {
@@ -412,7 +418,9 @@ int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
if (ret)
goto timeout;

- mtk_ecc_wait_idle(ecc, ECC_ENCODE);
+ ret = mtk_ecc_wait_idle(ecc, ECC_ENCODE);
+ if (ret)
+ goto timeout;

/* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
len = (config->strength * ecc->caps->parity_bits + 7) >> 3;
diff --git a/drivers/net/amt.c b/drivers/net/amt.c
index 0f4ff41d0535..a170b1a05d24 100644
--- a/drivers/net/amt.c
+++ b/drivers/net/amt.c
@@ -1211,7 +1211,7 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev)
data = true;
}
v6 = false;
- group.ip4 = iph->daddr;
+ group.ip4 = ip_hdr(skb)->daddr;
#if IS_ENABLED(CONFIG_IPV6)
} else if (iph->version == 6) {
ip6h = ipv6_hdr(skb);
@@ -1235,7 +1235,7 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev)
data = true;
}
v6 = true;
- group.ip6 = ip6h->daddr;
+ group.ip6 = ipv6_hdr(skb)->daddr;
#endif
} else {
dev->stats.tx_errors++;
@@ -1278,12 +1278,12 @@ static netdev_tx_t amt_dev_xmit(struct sk_buff *skb, struct net_device *dev)
hlist_for_each_entry_rcu(gnode, &tunnel->groups[hash],
node) {
if (!v6) {
- if (gnode->group_addr.ip4 == iph->daddr)
+ if (gnode->group_addr.ip4 == group.ip4)
goto found;
#if IS_ENABLED(CONFIG_IPV6)
} else {
if (ipv6_addr_equal(&gnode->group_addr.ip6,
- &ip6h->daddr))
+ &group.ip6))
goto found;
#endif
}
@@ -2000,14 +2000,18 @@ static void amt_igmpv3_report_handler(struct amt_dev *amt, struct sk_buff *skb,
struct igmpv3_report *ihrv3 = igmpv3_report_hdr(skb);
int len = skb_transport_offset(skb) + sizeof(*ihrv3);
void *zero_grec = (void *)&igmpv3_zero_grec;
- struct iphdr *iph = ip_hdr(skb);
struct amt_group_node *gnode;
union amt_addr group, host;
struct igmpv3_grec *grec;
+ __be32 saddr;
u16 nsrcs;
+ u16 ngrec;
int i;

- for (i = 0; i < ntohs(ihrv3->ngrec); i++) {
+ saddr = ip_hdr(skb)->saddr;
+ ngrec = ntohs(ihrv3->ngrec);
+
+ for (i = 0; i < ngrec; i++) {
len += sizeof(*grec);
if (!ip_mc_may_pull(skb, len))
break;
@@ -2019,10 +2023,13 @@ static void amt_igmpv3_report_handler(struct amt_dev *amt, struct sk_buff *skb,
if (!ip_mc_may_pull(skb, len))
break;

+ grec = (void *)(skb->data + len - sizeof(*grec) -
+ nsrcs * sizeof(__be32));
+
memset(&group, 0, sizeof(union amt_addr));
group.ip4 = grec->grec_mca;
memset(&host, 0, sizeof(union amt_addr));
- host.ip4 = iph->saddr;
+ host.ip4 = saddr;
gnode = amt_lookup_group(tunnel, &group, &host, false);
if (!gnode) {
gnode = amt_add_group(amt, tunnel, &group, &host,
@@ -2162,14 +2169,18 @@ static void amt_mldv2_report_handler(struct amt_dev *amt, struct sk_buff *skb,
struct mld2_report *mld2r = (struct mld2_report *)icmp6_hdr(skb);
int len = skb_transport_offset(skb) + sizeof(*mld2r);
void *zero_grec = (void *)&mldv2_zero_grec;
- struct ipv6hdr *ip6h = ipv6_hdr(skb);
struct amt_group_node *gnode;
union amt_addr group, host;
struct mld2_grec *grec;
+ struct in6_addr saddr;
u16 nsrcs;
+ u16 ngrec;
int i;

- for (i = 0; i < ntohs(mld2r->mld2r_ngrec); i++) {
+ saddr = ipv6_hdr(skb)->saddr;
+ ngrec = ntohs(mld2r->mld2r_ngrec);
+
+ for (i = 0; i < ngrec; i++) {
len += sizeof(*grec);
if (!ipv6_mc_may_pull(skb, len))
break;
@@ -2181,10 +2192,13 @@ static void amt_mldv2_report_handler(struct amt_dev *amt, struct sk_buff *skb,
if (!ipv6_mc_may_pull(skb, len))
break;

+ grec = (void *)(skb->data + len - sizeof(*grec) -
+ nsrcs * sizeof(struct in6_addr));
+
memset(&group, 0, sizeof(union amt_addr));
group.ip6 = grec->grec_mca;
memset(&host, 0, sizeof(union amt_addr));
- host.ip6 = ip6h->saddr;
+ host.ip6 = saddr;
gnode = amt_lookup_group(tunnel, &group, &host, true);
if (!gnode) {
gnode = amt_add_group(amt, tunnel, &group, &host,
@@ -2305,7 +2319,9 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb)
skb_push(skb, sizeof(*eth));
skb_reset_mac_header(skb);
skb_pull(skb, sizeof(*eth));
- eth = eth_hdr(skb);
+
+ if (skb_cow_head(skb, 0))
+ return true;

if (!pskb_may_pull(skb, sizeof(*iph)))
return true;
@@ -2315,6 +2331,7 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb)
if (!ipv4_is_multicast(iph->daddr))
return true;
skb->protocol = htons(ETH_P_IP);
+ eth = eth_hdr(skb);
eth->h_proto = htons(ETH_P_IP);
ip_eth_mc_map(iph->daddr, eth->h_dest);
#if IS_ENABLED(CONFIG_IPV6)
@@ -2328,6 +2345,7 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb)
if (!ipv6_addr_is_multicast(&ip6h->daddr))
return true;
skb->protocol = htons(ETH_P_IPV6);
+ eth = eth_hdr(skb);
eth->h_proto = htons(ETH_P_IPV6);
ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
#endif
@@ -2351,10 +2369,12 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
struct sk_buff *skb)
{
struct amt_header_membership_query *amtmq;
- struct igmpv3_query *ihv3;
struct ethhdr *eth, *oeth;
+ struct igmpv3_query *ihv3;
+ u8 h_source[ETH_ALEN];
struct iphdr *iph;
int hdr_size, len;
+ u64 response_mac;

hdr_size = sizeof(*amtmq) + sizeof(struct udphdr);
if (!pskb_may_pull(skb, hdr_size))
@@ -2367,6 +2387,8 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
if (amtmq->nonce != amt->nonce)
return true;

+ response_mac = amtmq->response_mac;
+
hdr_size -= sizeof(*eth);
if (iptunnel_pull_header(skb, hdr_size, htons(ETH_P_TEB), false))
return true;
@@ -2376,6 +2398,9 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
skb_pull(skb, sizeof(*eth));
skb_reset_network_header(skb);
eth = eth_hdr(skb);
+ ether_addr_copy(h_source, oeth->h_source);
+ if (skb_cow_head(skb, 0))
+ return true;
if (!pskb_may_pull(skb, sizeof(*iph)))
return true;

@@ -2388,6 +2413,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
sizeof(*ihv3)))
return true;

+ iph = ip_hdr(skb);
if (!ipv4_is_multicast(iph->daddr))
return true;

@@ -2395,10 +2421,11 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
skb_reset_transport_header(skb);
skb_push(skb, sizeof(*iph) + AMT_IPHDR_OPTS);
WRITE_ONCE(amt->ready4, true);
- amt->mac = amtmq->response_mac;
+ amt->mac = response_mac;
amt->req_cnt = 0;
amt->qi = ihv3->qqic;
skb->protocol = htons(ETH_P_IP);
+ eth = eth_hdr(skb);
eth->h_proto = htons(ETH_P_IP);
ip_eth_mc_map(iph->daddr, eth->h_dest);
#if IS_ENABLED(CONFIG_IPV6)
@@ -2421,10 +2448,11 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
skb_reset_transport_header(skb);
skb_push(skb, sizeof(*ip6h) + AMT_IP6HDR_OPTS);
WRITE_ONCE(amt->ready6, true);
- amt->mac = amtmq->response_mac;
+ amt->mac = response_mac;
amt->req_cnt = 0;
amt->qi = mld2q->mld2q_qqic;
skb->protocol = htons(ETH_P_IPV6);
+ eth = eth_hdr(skb);
eth->h_proto = htons(ETH_P_IPV6);
ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
#endif
@@ -2432,7 +2460,7 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
return true;
}

- ether_addr_copy(eth->h_source, oeth->h_source);
+ ether_addr_copy(eth->h_source, h_source);
skb->pkt_type = PACKET_MULTICAST;
skb->ip_summed = CHECKSUM_NONE;
len = skb->len;
@@ -2455,8 +2483,11 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
struct ethhdr *eth;
struct iphdr *iph;
int len, hdr_size;
+ u64 response_mac;
+ __be32 saddr;
+ __be32 nonce;

- iph = ip_hdr(skb);
+ saddr = ip_hdr(skb)->saddr;

hdr_size = sizeof(*amtmu) + sizeof(struct udphdr);
if (!pskb_may_pull(skb, hdr_size))
@@ -2466,15 +2497,18 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
if (amtmu->reserved || amtmu->version)
return true;

+ nonce = amtmu->nonce;
+ response_mac = amtmu->response_mac;
+
if (iptunnel_pull_header(skb, hdr_size, skb->protocol, false))
return true;

skb_reset_network_header(skb);

list_for_each_entry_rcu(tunnel, &amt->tunnel_list, list) {
- if (tunnel->ip4 == iph->saddr) {
- if ((amtmu->nonce == tunnel->nonce &&
- amtmu->response_mac == tunnel->mac)) {
+ if (tunnel->ip4 == saddr) {
+ if ((nonce == tunnel->nonce &&
+ response_mac == tunnel->mac)) {
mod_delayed_work(amt_wq, &tunnel->gc_wq,
msecs_to_jiffies(amt_gmi(amt))
* 3);
@@ -2492,6 +2526,9 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
if (!pskb_may_pull(skb, sizeof(*iph)))
return true;

+ if (skb_cow_head(skb, 0))
+ return true;
+
iph = ip_hdr(skb);
if (iph->version == 4) {
if (ip_mc_check_igmp(skb)) {
@@ -2508,6 +2545,7 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
eth = eth_hdr(skb);
skb->protocol = htons(ETH_P_IP);
eth->h_proto = htons(ETH_P_IP);
+ iph = ip_hdr(skb);
ip_eth_mc_map(iph->daddr, eth->h_dest);
#if IS_ENABLED(CONFIG_IPV6)
} else if (iph->version == 6) {
@@ -2527,6 +2565,7 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
eth = eth_hdr(skb);
skb->protocol = htons(ETH_P_IPV6);
eth->h_proto = htons(ETH_P_IPV6);
+ ip6h = ipv6_hdr(skb);
ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
#endif
} else {
@@ -2772,7 +2811,7 @@ static void amt_gw_rcv(struct amt_dev *amt, struct sk_buff *skb)
static int amt_rcv(struct sock *sk, struct sk_buff *skb)
{
struct amt_dev *amt;
- struct iphdr *iph;
+ __be32 saddr;
int type;
bool err;

@@ -2785,7 +2824,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
}

skb->dev = amt->dev;
- iph = ip_hdr(skb);
+ saddr = ip_hdr(skb)->saddr;
type = amt_parse_type(skb);
if (type == -1) {
err = true;
@@ -2795,7 +2834,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
if (amt->mode == AMT_MODE_GATEWAY) {
switch (type) {
case AMT_MSG_ADVERTISEMENT:
- if (iph->saddr != amt->discovery_ip) {
+ if (saddr != amt->discovery_ip) {
netdev_dbg(amt->dev, "Invalid Relay IP\n");
err = true;
goto drop;
@@ -2807,7 +2846,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
}
goto out;
case AMT_MSG_MULTICAST_DATA:
- if (iph->saddr != amt->remote_ip) {
+ if (saddr != amt->remote_ip) {
netdev_dbg(amt->dev, "Invalid Relay IP\n");
err = true;
goto drop;
@@ -2818,7 +2857,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
else
goto out;
case AMT_MSG_MEMBERSHIP_QUERY:
- if (iph->saddr != amt->remote_ip) {
+ if (saddr != amt->remote_ip) {
netdev_dbg(amt->dev, "Invalid Relay IP\n");
err = true;
goto drop;
@@ -2995,9 +3034,15 @@ static int amt_dev_open(struct net_device *dev)
amt->event_idx = 0;
amt->nr_events = 0;

+ enable_delayed_work(&amt->discovery_wq);
+ enable_delayed_work(&amt->req_wq);
+
err = amt_socket_create(amt);
- if (err)
+ if (err) {
+ disable_delayed_work(&amt->req_wq);
+ disable_delayed_work(&amt->discovery_wq);
return err;
+ }

amt->req_cnt = 0;
amt->remote_ip = 0;
@@ -3023,8 +3068,8 @@ static int amt_dev_stop(struct net_device *dev)
struct sk_buff *skb;
int i;

- cancel_delayed_work_sync(&amt->req_wq);
- cancel_delayed_work_sync(&amt->discovery_wq);
+ disable_delayed_work_sync(&amt->req_wq);
+ disable_delayed_work_sync(&amt->discovery_wq);
cancel_delayed_work_sync(&amt->secret_wq);

/* shutdown */
@@ -3278,6 +3323,8 @@ static int amt_newlink(struct net_device *dev,
INIT_DELAYED_WORK(&amt->req_wq, amt_req_work);
INIT_DELAYED_WORK(&amt->secret_wq, amt_secret_work);
INIT_WORK(&amt->event_wq, amt_event_work);
+ disable_delayed_work(&amt->req_wq);
+ disable_delayed_work(&amt->discovery_wq);
INIT_LIST_HEAD(&amt->tunnel_list);
return 0;
err:
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index cd9b0a6d6521..75abd6d9af3a 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3446,7 +3446,8 @@ static void bond_send_validate(struct bonding *bond, struct slave *slave)
{
bond_arp_send_all(bond, slave);
#if IS_ENABLED(CONFIG_IPV6)
- bond_ns_send_all(bond, slave);
+ if (likely(ipv6_mod_enabled()))
+ bond_ns_send_all(bond, slave);
#endif
}

diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 1e361a35ebd4..b03d1b2c820a 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2326,8 +2326,7 @@ static int airoha_tc_setup_qdisc_ets(struct net_device *dev,
if (opt->parent == TC_H_ROOT)
return -EINVAL;

- channel = TC_H_MAJ(opt->handle) >> 16;
- channel = channel % AIROHA_NUM_QOS_CHANNELS;
+ channel = TC_H_MIN(opt->parent) % AIROHA_NUM_QOS_CHANNELS;

switch (opt->command) {
case TC_ETS_REPLACE:
diff --git a/drivers/net/ethernet/airoha/airoha_npu.c b/drivers/net/ethernet/airoha/airoha_npu.c
index 17dbdc832533..eab3eb4a3ab2 100644
--- a/drivers/net/ethernet/airoha/airoha_npu.c
+++ b/drivers/net/ethernet/airoha/airoha_npu.c
@@ -168,7 +168,7 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
dma_addr_t dma_addr;
int ret;

- dma_addr = dma_map_single(npu->dev, p, size, DMA_TO_DEVICE);
+ dma_addr = dma_map_single(npu->dev, p, size, DMA_BIDIRECTIONAL);
ret = dma_mapping_error(npu->dev, dma_addr);
if (ret)
return ret;
@@ -191,7 +191,7 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,

spin_unlock_bh(&npu->cores[core].lock);

- dma_unmap_single(npu->dev, dma_addr, size, DMA_TO_DEVICE);
+ dma_unmap_single(npu->dev, dma_addr, size, DMA_BIDIRECTIONAL);

return ret;
}
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index a1f9939c21f2..75b33e5af9b2 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -1619,6 +1619,7 @@ void airoha_ppe_deinit(struct airoha_eth *eth)
npu = rcu_replace_pointer(eth->npu, NULL,
lockdep_is_held(&flow_offload_mutex));
if (npu) {
+ synchronize_rcu();
npu->ops.ppe_deinit(npu);
airoha_npu_put(npu);
}
diff --git a/drivers/net/ethernet/amd/pds_core/adminq.c b/drivers/net/ethernet/amd/pds_core/adminq.c
index 097bb092bdb8..eadb4b604fbe 100644
--- a/drivers/net/ethernet/amd/pds_core/adminq.c
+++ b/drivers/net/ethernet/amd/pds_core/adminq.c
@@ -18,7 +18,13 @@ static int pdsc_process_notifyq(struct pdsc_qcq *qcq)
comp = cq_info->comp;
eid = le64_to_cpu(comp->event.eid);
while (eid > pdsc->last_eid) {
- u16 ecode = le16_to_cpu(comp->event.ecode);
+ u16 ecode;
+
+ /* Order the payload read after the event id, the field the
+ * driver uses to detect a new completion.
+ */
+ dma_rmb();
+ ecode = le16_to_cpu(comp->event.ecode);

switch (ecode) {
case PDS_EVENT_LINK_CHANGE:
@@ -101,6 +107,10 @@ void pdsc_process_adminq(struct pdsc_qcq *qcq)
spin_lock_irqsave(&pdsc->adminq_lock, irqflags);
comp = cq->info[cq->tail_idx].comp;
while (pdsc_color_match(comp->color, cq->done_color)) {
+ /* Order the payload reads after the color bit, the field the
+ * driver uses to detect a new completion.
+ */
+ dma_rmb();
q_info = &q->info[q->tail_idx];
q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1);

diff --git a/drivers/net/ethernet/amd/pds_core/auxbus.c b/drivers/net/ethernet/amd/pds_core/auxbus.c
index 73b3481220b1..3acafe10a6d5 100644
--- a/drivers/net/ethernet/amd/pds_core/auxbus.c
+++ b/drivers/net/ethernet/amd/pds_core/auxbus.c
@@ -177,17 +177,21 @@ void pdsc_auxbus_dev_del(struct pdsc *cf, struct pdsc *pf,
{
struct pds_auxiliary_dev *padev;

- if (!*pd_ptr)
- return;
-
mutex_lock(&pf->config_lock);

+ /* A concurrent del may have already torn this device down and
+ * cleared it.
+ */
padev = *pd_ptr;
+ if (!padev)
+ goto out_unlock;
+
pds_client_unregister(pf, padev->client_id);
auxiliary_device_delete(&padev->aux_dev);
auxiliary_device_uninit(&padev->aux_dev);
*pd_ptr = NULL;

+out_unlock:
mutex_unlock(&pf->config_lock);
}

@@ -210,6 +214,13 @@ int pdsc_auxbus_dev_add(struct pdsc *cf, struct pdsc *pf,

mutex_lock(&pf->config_lock);

+ /* Nothing to do if the aux device is already present. This also
+ * guards against a second add overwriting *pd_ptr and leaking the
+ * first, symmetric with the check in pdsc_auxbus_dev_del().
+ */
+ if (*pd_ptr)
+ goto out_unlock;
+
mask = BIT_ULL(PDSC_S_FW_DEAD) |
BIT_ULL(PDSC_S_STOPPING_DRIVER);
if (cf->state & mask) {
diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c
index 705cab7b0727..34181ac84799 100644
--- a/drivers/net/ethernet/amd/pds_core/core.c
+++ b/drivers/net/ethernet/amd/pds_core/core.c
@@ -110,7 +110,6 @@ static void pdsc_qcq_intr_free(struct pdsc *pdsc, struct pdsc_qcq *qcq)
return;

pdsc_intr_free(pdsc, qcq->intx);
- qcq->intx = PDS_CORE_INTR_INDEX_NOT_ASSIGNED;
}

static int pdsc_qcq_intr_alloc(struct pdsc *pdsc, struct pdsc_qcq *qcq)
@@ -145,6 +144,12 @@ void pdsc_qcq_free(struct pdsc *pdsc, struct pdsc_qcq *qcq)

pdsc_qcq_intr_free(pdsc, qcq);

+ /* Drain any work queued by ISR before it was freed above */
+ if (qcq->work.func)
+ cancel_work_sync(&qcq->work);
+
+ qcq->intx = PDS_CORE_INTR_INDEX_NOT_ASSIGNED;
+
if (qcq->q_base)
dma_free_coherent(dev, qcq->q_size,
qcq->q_base, qcq->q_base_pa);
@@ -304,8 +309,11 @@ int pdsc_qcq_alloc(struct pdsc *pdsc, unsigned int type, unsigned int index,

static void pdsc_core_uninit(struct pdsc *pdsc)
{
- pdsc_qcq_free(pdsc, &pdsc->notifyqcq);
+ /* Free adminqcq first: its work accesses notifyqcq, so we must
+ * disable its IRQ and drain its work before freeing notifyqcq.
+ */
pdsc_qcq_free(pdsc, &pdsc->adminqcq);
+ pdsc_qcq_free(pdsc, &pdsc->notifyqcq);

if (pdsc->kern_dbpage) {
iounmap(pdsc->kern_dbpage);
@@ -477,8 +485,6 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing)
{
if (!pdsc->pdev->is_virtfn)
pdsc_devcmd_reset(pdsc);
- if (pdsc->adminqcq.work.func)
- cancel_work_sync(&pdsc->adminqcq.work);

pdsc_core_uninit(pdsc);

@@ -529,6 +535,7 @@ static void pdsc_adminq_wait_and_dec_once_unused(struct pdsc *pdsc)
dev_dbg_ratelimited(pdsc->dev, "%s: adminq in use\n",
__func__);
cpu_relax();
+ cond_resched();
}
}

@@ -602,9 +609,10 @@ void pdsc_pci_reset_thread(struct work_struct *work)
struct pdsc *pdsc = container_of(work, struct pdsc, pci_reset_work);
struct pci_dev *pdev = pdsc->pdev;

- pci_dev_get(pdev);
- pci_reset_function(pdev);
- pci_dev_put(pdev);
+ /* Use try variant to avoid deadlock with pdsc_remove().
+ * If lock is contended, the watchdog timer will retry.
+ */
+ pci_try_reset_function(pdev);
}

static void pdsc_check_pci_health(struct pdsc *pdsc)
diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c
index 3f0e56b951bf..d6fccd89476a 100644
--- a/drivers/net/ethernet/amd/pds_core/devlink.c
+++ b/drivers/net/ethernet/amd/pds_core/devlink.c
@@ -90,6 +90,12 @@ int pdsc_dl_flash_update(struct devlink *dl,
{
struct pdsc *pdsc = devlink_priv(dl);

+ if (params->component) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Component update not supported by this device");
+ return -EOPNOTSUPP;
+ }
+
return pdsc_firmware_update(pdsc, params->fw, extack);
}

diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c
index 22db78343eb0..8d94a4d70395 100644
--- a/drivers/net/ethernet/amd/pds_core/main.c
+++ b/drivers/net/ethernet/amd/pds_core/main.c
@@ -238,6 +238,10 @@ static int pdsc_init_pf(struct pdsc *pdsc)
/* General workqueue and timer, but don't start timer yet */
snprintf(wq_name, sizeof(wq_name), "%s.%d", PDS_CORE_DRV_NAME, pdsc->uid);
pdsc->wq = create_singlethread_workqueue(wq_name);
+ if (!pdsc->wq) {
+ err = -ENOMEM;
+ goto err_out_unmap_bars;
+ }
INIT_WORK(&pdsc->health_work, pdsc_health_thread);
INIT_WORK(&pdsc->pci_reset_work, pdsc_pci_reset_thread);
timer_setup(&pdsc->wdtimer, pdsc_wdtimer_cb, 0);
@@ -253,7 +257,7 @@ static int pdsc_init_pf(struct pdsc *pdsc)
err = pdsc_setup(pdsc, PDSC_SETUP_INIT);
if (err) {
mutex_unlock(&pdsc->config_lock);
- goto err_out_unmap_bars;
+ goto err_out_shutdown_timer;
}

err = pdsc_start(pdsc);
@@ -305,13 +309,14 @@ static int pdsc_init_pf(struct pdsc *pdsc)
pdsc_stop(pdsc);
err_out_teardown:
pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING);
-err_out_unmap_bars:
+err_out_shutdown_timer:
timer_shutdown_sync(&pdsc->wdtimer);
if (pdsc->wq)
destroy_workqueue(pdsc->wq);
mutex_destroy(&pdsc->config_lock);
mutex_destroy(&pdsc->devcmd_lock);
pci_free_irq_vectors(pdsc->pdev);
+err_out_unmap_bars:
pdsc_unmap_bars(pdsc);
err_out_release_regions:
pci_release_regions(pdsc->pdev);
@@ -435,8 +440,6 @@ static void pdsc_remove(struct pci_dev *pdev)
pdsc_auxbus_dev_del(pdsc, pdsc, &pdsc->padev);

timer_shutdown_sync(&pdsc->wdtimer);
- if (pdsc->wq)
- destroy_workqueue(pdsc->wq);

mutex_lock(&pdsc->config_lock);
set_bit(PDSC_S_STOPPING_DRIVER, &pdsc->state);
@@ -444,6 +447,9 @@ static void pdsc_remove(struct pci_dev *pdev)
pdsc_stop(pdsc);
pdsc_teardown(pdsc, PDSC_TEARDOWN_REMOVING);
mutex_unlock(&pdsc->config_lock);
+
+ if (pdsc->wq)
+ destroy_workqueue(pdsc->wq);
mutex_destroy(&pdsc->config_lock);
mutex_destroy(&pdsc->devcmd_lock);

diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
index fa0df6181207..12770af031eb 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
@@ -267,9 +267,14 @@ static void xgbe_an37_set(struct xgbe_prv_data *pdata, bool enable,

XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_CTRL1, reg);

- reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL);
- reg |= XGBE_VEND2_MAC_AUTO_SW;
- XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL, reg);
+ if (pdata->an_mode == XGBE_AN_MODE_CL37_SGMII) {
+ reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL);
+ if (enable)
+ reg |= XGBE_VEND2_MAC_AUTO_SW;
+ else
+ reg &= ~XGBE_VEND2_MAC_AUTO_SW;
+ XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_PCS_DIG_CTRL, reg);
+ }
}

static void xgbe_an37_restart(struct xgbe_prv_data *pdata)
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge.h b/drivers/net/ethernet/broadcom/bnge/bnge.h
index f21cff651fd4..4479ccd071f5 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge.h
@@ -36,6 +36,7 @@ struct bnge_pf_info {
};

#define INVALID_HW_RING_ID ((u16)-1)
+#define INVALID_HW_RING_ID_32BIT (U32_MAX)

enum {
BNGE_FW_CAP_SHORT_CMD = BIT_ULL(0),
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c
index 1c9cfec1b633..651c5e783516 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c
@@ -1283,7 +1283,7 @@ int bnge_hwrm_stat_ctx_alloc(struct bnge_net *bn)

int hwrm_ring_free_send_msg(struct bnge_net *bn,
struct bnge_ring_struct *ring,
- u32 ring_type, int cmpl_ring_id)
+ u32 ring_type, u32 cmpl_ring_id)
{
struct hwrm_ring_free_input *req;
struct bnge_dev *bd = bn->bd;
@@ -1295,7 +1295,7 @@ int hwrm_ring_free_send_msg(struct bnge_net *bn,

req->cmpl_ring = cpu_to_le16(cmpl_ring_id);
req->ring_type = ring_type;
- req->ring_id = cpu_to_le16(ring->fw_ring_id);
+ req->ring_id = cpu_to_le32(ring->fw_ring_id);

bnge_hwrm_req_hold(bd, req);
rc = bnge_hwrm_req_send(bd, req);
@@ -1317,7 +1317,7 @@ int hwrm_ring_alloc_send_msg(struct bnge_net *bn,
struct hwrm_ring_alloc_output *resp;
struct hwrm_ring_alloc_input *req;
struct bnge_dev *bd = bn->bd;
- u16 ring_id, flags = 0;
+ u32 ring_id, flags = 0;
int rc;

rc = bnge_hwrm_req_init(bd, req, HWRM_RING_ALLOC);
@@ -1401,7 +1401,7 @@ int hwrm_ring_alloc_send_msg(struct bnge_net *bn,

resp = bnge_hwrm_req_hold(bd, req);
rc = bnge_hwrm_req_send(bd, req);
- ring_id = le16_to_cpu(resp->ring_id);
+ ring_id = le32_to_cpu(resp->ring_id);
bnge_hwrm_req_drop(bd, req);

exit:
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h
index 3501de7a89b9..bf452e390d5b 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h
@@ -50,7 +50,7 @@ int bnge_hwrm_cfa_l2_set_rx_mask(struct bnge_dev *bd,
void bnge_hwrm_stat_ctx_free(struct bnge_net *bn);
int bnge_hwrm_stat_ctx_alloc(struct bnge_net *bn);
int hwrm_ring_free_send_msg(struct bnge_net *bn, struct bnge_ring_struct *ring,
- u32 ring_type, int cmpl_ring_id);
+ u32 ring_type, u32 cmpl_ring_id);
int hwrm_ring_alloc_send_msg(struct bnge_net *bn,
struct bnge_ring_struct *ring,
u32 ring_type, u32 map_index);
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
index 70768193004c..6f7ef506d4e1 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
@@ -1327,12 +1327,12 @@ static int bnge_alloc_core(struct bnge_net *bn)
return rc;
}

-u16 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr)
+u32 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr)
{
return rxr->rx_cpr->ring_struct.fw_ring_id;
}

-u16 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr)
+u32 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr)
{
return txr->tx_cpr->ring_struct.fw_ring_id;
}
@@ -1375,12 +1375,12 @@ static void bnge_init_nq_tree(struct bnge_net *bn)
struct bnge_nq_ring_info *nqr = &bn->bnapi[i]->nq_ring;
struct bnge_ring_struct *ring = &nqr->ring_struct;

- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
for (j = 0; j < nqr->cp_ring_count; j++) {
struct bnge_cp_ring_info *cpr = &nqr->cp_ring_arr[j];

ring = &cpr->ring_struct;
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
}
}
}
@@ -1637,7 +1637,7 @@ static void bnge_init_one_rx_ring_rxbd(struct bnge_net *bn,

ring = &rxr->rx_ring_struct;
bnge_init_rxbd_pages(ring, type);
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
}

static void bnge_init_one_agg_ring_rxbd(struct bnge_net *bn,
@@ -1647,7 +1647,7 @@ static void bnge_init_one_agg_ring_rxbd(struct bnge_net *bn,
u32 type;

ring = &rxr->rx_agg_ring_struct;
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
if (bnge_is_agg_reqd(bn->bd)) {
type = ((u32)BNGE_RX_PAGE_SIZE << RX_BD_LEN_SHIFT) |
RX_BD_TYPE_RX_AGG_BD | RX_BD_FLAGS_SOP;
@@ -1708,7 +1708,7 @@ static void bnge_init_tx_rings(struct bnge_net *bn)
struct bnge_tx_ring_info *txr = &bn->tx_ring[i];
struct bnge_ring_struct *ring = &txr->tx_ring_struct;

- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;

netif_queue_set_napi(bn->netdev, i, NETDEV_QUEUE_TYPE_TX,
&txr->bnapi->napi);
@@ -1867,7 +1867,7 @@ static int bnge_hwrm_rx_agg_ring_alloc(struct bnge_net *bn,
ring->fw_ring_id);
bnge_db_write(bn->bd, &rxr->rx_agg_db, rxr->rx_agg_prod);
bnge_db_write(bn->bd, &rxr->rx_db, rxr->rx_prod);
- bn->grp_info[grp_idx].agg_fw_ring_id = ring->fw_ring_id;
+ bn->grp_info[grp_idx].agg_fw_ring_id = (u16)ring->fw_ring_id;

return 0;
}
@@ -1886,7 +1886,7 @@ static int bnge_hwrm_rx_ring_alloc(struct bnge_net *bn,
return rc;

bnge_set_db(bn, &rxr->rx_db, type, map_idx, ring->fw_ring_id);
- bn->grp_info[map_idx].rx_fw_ring_id = ring->fw_ring_id;
+ bn->grp_info[map_idx].rx_fw_ring_id = (u16)ring->fw_ring_id;

return 0;
}
@@ -1916,7 +1916,7 @@ static int bnge_hwrm_ring_alloc(struct bnge_net *bn)
bnge_set_db(bn, &nqr->nq_db, type, map_idx, ring->fw_ring_id);
bnge_db_nq(bn, &nqr->nq_db, nqr->nq_raw_cons);
enable_irq(vector);
- bn->grp_info[i].nq_fw_ring_id = ring->fw_ring_id;
+ bn->grp_info[i].nq_fw_ring_id = (u16)ring->fw_ring_id;

if (!i) {
rc = bnge_hwrm_set_async_event_cr(bd, ring->fw_ring_id);
@@ -1986,15 +1986,13 @@ void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic)
tbl_size = bnge_get_rxfh_indir_size(bd);

for (i = 0; i < tbl_size; i++) {
- u16 ring_id, j;
+ u32 j;

j = bd->rss_indir_tbl[i];
rxr = &bn->rx_ring[j];

- ring_id = rxr->rx_ring_struct.fw_ring_id;
- *ring_tbl++ = cpu_to_le16(ring_id);
- ring_id = bnge_cp_ring_for_rx(rxr);
- *ring_tbl++ = cpu_to_le16(ring_id);
+ *ring_tbl++ = cpu_to_le16(rxr->rx_ring_struct.fw_ring_id);
+ *ring_tbl++ = cpu_to_le16(bnge_cp_ring_for_rx(rxr));
}
}

@@ -2285,7 +2283,7 @@ static void bnge_disable_int(struct bnge_net *bn)
nqr = &bnapi->nq_ring;
ring = &nqr->ring_struct;

- if (ring->fw_ring_id != INVALID_HW_RING_ID)
+ if (ring->fw_ring_id != INVALID_HW_RING_ID_32BIT)
bnge_db_nq(bn, &nqr->nq_db, nqr->nq_raw_cons);
}
}
@@ -2401,7 +2399,7 @@ static void bnge_hwrm_rx_ring_free(struct bnge_net *bn,
u32 grp_idx = rxr->bnapi->index;
u32 cmpl_ring_id;

- if (ring->fw_ring_id == INVALID_HW_RING_ID)
+ if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT)
return;

cmpl_ring_id = bnge_cp_ring_for_rx(rxr);
@@ -2409,7 +2407,7 @@ static void bnge_hwrm_rx_ring_free(struct bnge_net *bn,
RING_FREE_REQ_RING_TYPE_RX,
close_path ? cmpl_ring_id :
INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
bn->grp_info[grp_idx].rx_fw_ring_id = INVALID_HW_RING_ID;
}

@@ -2421,14 +2419,14 @@ static void bnge_hwrm_rx_agg_ring_free(struct bnge_net *bn,
u32 grp_idx = rxr->bnapi->index;
u32 cmpl_ring_id;

- if (ring->fw_ring_id == INVALID_HW_RING_ID)
+ if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT)
return;

cmpl_ring_id = bnge_cp_ring_for_rx(rxr);
hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_RX_AGG,
close_path ? cmpl_ring_id :
INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
bn->grp_info[grp_idx].agg_fw_ring_id = INVALID_HW_RING_ID;
}

@@ -2439,14 +2437,14 @@ static void bnge_hwrm_tx_ring_free(struct bnge_net *bn,
struct bnge_ring_struct *ring = &txr->tx_ring_struct;
u32 cmpl_ring_id;

- if (ring->fw_ring_id == INVALID_HW_RING_ID)
+ if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT)
return;

cmpl_ring_id = close_path ? bnge_cp_ring_for_tx(txr) :
INVALID_HW_RING_ID;
hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_TX,
cmpl_ring_id);
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
}

static void bnge_hwrm_cp_ring_free(struct bnge_net *bn,
@@ -2455,12 +2453,12 @@ static void bnge_hwrm_cp_ring_free(struct bnge_net *bn,
struct bnge_ring_struct *ring;

ring = &cpr->ring_struct;
- if (ring->fw_ring_id == INVALID_HW_RING_ID)
+ if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT)
return;

hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_L2_CMPL,
INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
}

static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path)
@@ -2496,11 +2494,11 @@ static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path)
bnge_hwrm_cp_ring_free(bn, &nqr->cp_ring_arr[j]);

ring = &nqr->ring_struct;
- if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+ if (ring->fw_ring_id != INVALID_HW_RING_ID_32BIT) {
hwrm_ring_free_send_msg(bn, ring,
RING_FREE_REQ_RING_TYPE_NQ,
INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
bn->grp_info[i].nq_fw_ring_id = INVALID_HW_RING_ID;
}
}
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h
index f4636b5b0cf3..d177919c2e11 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h
@@ -630,8 +630,8 @@ struct bnge_l2_filter {
refcount_t refcnt;
};

-u16 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr);
-u16 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr);
+u32 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr);
+u32 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr);
void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic);
int bnge_alloc_rx_data(struct bnge_net *bn, struct bnge_rx_ring_info *rxr,
u16 prod, gfp_t gfp);
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h b/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h
index 341c7f81ed09..bb0c79a1ee60 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_rmem.h
@@ -184,7 +184,7 @@ struct bnge_ctx_mem_info {
struct bnge_ring_struct {
struct bnge_ring_mem_info ring_mem;

- u16 fw_ring_id;
+ u32 fw_ring_id;
union {
u16 grp_idx;
u16 map_idx; /* Used by NQs */
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
index 5c751933da6a..a515c368bac0 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
@@ -566,6 +566,18 @@ void bnxt_aux_devices_init(struct bnxt *bp)
if (!aux_priv)
goto next_auxdev;

+ edev = kzalloc_obj(*edev);
+ if (!edev)
+ goto aux_priv_free;
+ aux_priv->edev = edev;
+ bnxt_set_edev_info(edev, bp);
+
+ ulp = kzalloc_obj(*ulp);
+ if (!ulp)
+ goto edev_free;
+ edev->ulp_tbl = ulp;
+ aux_priv->id = idx;
+
aux_dev = &aux_priv->aux_dev;
aux_dev->id = bp->auxdev_id;
aux_dev->name = bnxt_aux_devices[idx].name;
@@ -573,37 +585,26 @@ void bnxt_aux_devices_init(struct bnxt *bp)
aux_dev->dev.release = bnxt_aux_dev_release;

rc = auxiliary_device_init(aux_dev);
- if (rc) {
- kfree(aux_priv);
- goto next_auxdev;
- }
+ if (rc)
+ goto ulp_free;
bp->aux_priv[idx] = aux_priv;

/* From this point, all cleanup will happen via the .release
* callback & any error unwinding will need to include a call
* to auxiliary_device_uninit.
*/
- edev = kzalloc_obj(*edev);
- if (!edev)
- goto aux_dev_uninit;
-
- aux_priv->edev = edev;
- bnxt_set_edev_info(edev, bp);
-
- ulp = kzalloc_obj(*ulp);
- if (!ulp)
- goto aux_dev_uninit;
-
- edev->ulp_tbl = ulp;
bp->edev[idx] = edev;
if (idx == BNXT_AUXDEV_RDMA)
bp->ulp_num_msix_want = bnxt_set_dflt_ulp_msix(bp);
- aux_priv->id = idx;
bnxt_auxdev_set_state(bp, idx, BNXT_ADEV_STATE_INIT);

continue;
-aux_dev_uninit:
- auxiliary_device_uninit(aux_dev);
+ulp_free:
+ kfree(ulp);
+edev_free:
+ kfree(edev);
+aux_priv_free:
+ kfree(aux_priv);
next_auxdev:
if (idx == BNXT_AUXDEV_RDMA)
bp->flags &= ~BNXT_FLAG_ROCE_CAP;
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index 9335703768a9..764d2a09668f 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -4732,6 +4732,7 @@ static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv)
dpaa2_mac_disconnect(mac);

dpaa2_mac_close(mac);
+ put_device(&mac->mc_dev->dev);
kfree(mac);
}

diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
index fa4e4f479782..9f465033331d 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
@@ -1511,6 +1511,7 @@ static void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv)
dpaa2_mac_disconnect(mac);

dpaa2_mac_close(mac);
+ put_device(&mac->mc_dev->dev);
kfree(mac);
}

diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
index fe35703c509e..b8d70c0ecb6c 100644
--- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c
+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
@@ -900,22 +900,28 @@ static void dtsec_mac_config(struct phylink_config *config, unsigned int mode,
{
struct mac_device *mac_dev = fman_config_to_mac(config);
struct dtsec_regs __iomem *regs = mac_dev->fman_mac->regs;
- u32 tmp;
+ u32 ecntrl, maccfg2;
+
+ maccfg2 = ioread32be(&regs->maccfg2);
+ maccfg2 &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE);

switch (state->interface) {
case PHY_INTERFACE_MODE_RMII:
- tmp = DTSEC_ECNTRL_RMM;
+ ecntrl = DTSEC_ECNTRL_RMM;
+ maccfg2 |= MACCFG2_NIBBLE_MODE;
break;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
- tmp = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM;
+ ecntrl = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM;
+ maccfg2 |= MACCFG2_BYTE_MODE;
break;
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_2500BASEX:
- tmp = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM;
+ ecntrl = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM;
+ maccfg2 |= MACCFG2_BYTE_MODE;
break;
default:
dev_warn(mac_dev->dev, "cannot configure dTSEC for %s\n",
@@ -923,7 +929,8 @@ static void dtsec_mac_config(struct phylink_config *config, unsigned int mode,
return;
}

- iowrite32be(tmp, &regs->ecntrl);
+ iowrite32be(ecntrl, &regs->ecntrl);
+ iowrite32be(maccfg2, &regs->maccfg2);
}

static void dtsec_link_up(struct phylink_config *config, struct phy_device *phy,
diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h
index 1d66d3834f7e..c280ff35ee77 100644
--- a/drivers/net/ethernet/google/gve/gve.h
+++ b/drivers/net/ethernet/google/gve/gve.h
@@ -13,6 +13,7 @@
#include <linux/netdevice.h>
#include <linux/net_tstamp.h>
#include <linux/pci.h>
+#include <linux/timer.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/u64_stats_sync.h>
#include <net/page_pool/helpers.h>
@@ -41,6 +42,7 @@

/* Interval to schedule a stats report update, 20000ms. */
#define GVE_STATS_REPORT_TIMER_PERIOD 20000
+#define GVE_RX_NAPI_RESCHED_MS 20 /* msecs */

/* Numbers of NIC tx/rx stats in stats report. */
#define NIC_TX_STATS_REPORT_NUM 0
@@ -341,6 +343,7 @@ struct gve_rx_ring {
struct xdp_rxq_info xdp_rxq;
struct xsk_buff_pool *xsk_pool;
struct page_frag_cache page_cache; /* Page cache to allocate XDP frames */
+ struct timer_list starvation_timer; /* for queue starvation recovery */
};

/* A TX desc ring entry */
diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
index 02cba280d81a..8271f731a91f 100644
--- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c
+++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
@@ -18,6 +18,16 @@
#include <net/tcp.h>
#include <net/xdp_sock_drv.h>

+static void gve_rx_starvation_timer(struct timer_list *t)
+{
+ struct gve_rx_ring *rx = timer_container_of(rx, t, starvation_timer);
+ struct gve_priv *priv = rx->gve;
+ struct gve_notify_block *block;
+
+ block = &priv->ntfy_blocks[rx->ntfy_id];
+ napi_schedule(&block->napi);
+}
+
static void gve_rx_free_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx)
{
struct device *hdev = &priv->pdev->dev;
@@ -120,6 +130,7 @@ void gve_rx_stop_ring_dqo(struct gve_priv *priv, int idx)

if (rx->dqo.page_pool)
page_pool_disable_direct_recycling(rx->dqo.page_pool);
+ timer_shutdown_sync(&rx->starvation_timer);
gve_remove_napi(priv, ntfy_idx);
gve_rx_remove_from_block(priv, idx);
gve_rx_reset_ring_dqo(priv, idx);
@@ -208,8 +219,10 @@ static int gve_rx_alloc_hdr_bufs(struct gve_priv *priv, struct gve_rx_ring *rx,
void gve_rx_start_ring_dqo(struct gve_priv *priv, int idx)
{
int ntfy_idx = gve_rx_idx_to_ntfy(priv, idx);
+ struct gve_rx_ring *rx = &priv->rx[idx];

gve_rx_add_to_block(priv, idx);
+ timer_setup(&rx->starvation_timer, gve_rx_starvation_timer, 0);
gve_add_napi(priv, ntfy_idx, gve_napi_poll_dqo);
}

@@ -365,6 +378,7 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx)
struct gve_rx_compl_queue_dqo *complq = &rx->dqo.complq;
struct gve_rx_buf_queue_dqo *bufq = &rx->dqo.bufq;
struct gve_priv *priv = rx->gve;
+ u32 num_bufs_avail_to_hw;
u32 num_avail_slots;
u32 num_full_slots;
u32 num_posted = 0;
@@ -400,6 +414,26 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx)
}

rx->fill_cnt += num_posted;
+
+ /* If the queue has fewer than GVE_RX_BUF_THRESH_DQO descriptors
+ * visible to the hardware, the hardware is in danger of starving
+ * and cannot trigger interrupts.
+ *
+ * We use a threshold of 32 because a single maximum-sized RSC
+ * packet can consume up to 19 descriptors in the Rx path. Lower
+ * thresholds (e.g., 8 or 16) would be unsafe as they could cause
+ * the device to drop/stall on a maximum-sized RSC packet.
+ *
+ * Start the timer to periodically reschedule NAPI and recover.
+ */
+ num_bufs_avail_to_hw =
+ ((bufq->tail & ~(GVE_RX_BUF_THRESH_DQO - 1)) -
+ bufq->head) & bufq->mask;
+
+ if (num_bufs_avail_to_hw < GVE_RX_BUF_THRESH_DQO) {
+ mod_timer(&rx->starvation_timer,
+ jiffies + msecs_to_jiffies(GVE_RX_NAPI_RESCHED_MS));
+ }
}

static void gve_rx_skb_csum(struct sk_buff *skb,
diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c
index 18376bcc718a..fc2c47dcfaab 100644
--- a/drivers/net/ethernet/hisilicon/hip04_eth.c
+++ b/drivers/net/ethernet/hisilicon/hip04_eth.c
@@ -594,7 +594,11 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget)
skb = build_skb(buf, priv->rx_buf_size);
if (unlikely(!skb)) {
net_dbg_ratelimited("build_skb failed\n");
- goto refill;
+ /* Retain the slot; return budget so NAPI retries this
+ * buffer. Refill would overwrite rx_buf[]/rx_phys[]
+ * and leak them.
+ */
+ return budget;
}

dma_unmap_single(priv->dev, priv->rx_phys[priv->rx_head],
@@ -622,14 +626,15 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget)
rx++;
}

-refill:
buf = netdev_alloc_frag(priv->rx_buf_size);
if (!buf)
goto done;
phys = dma_map_single(priv->dev, buf,
RX_BUF_SIZE, DMA_FROM_DEVICE);
- if (dma_mapping_error(priv->dev, phys))
+ if (dma_mapping_error(priv->dev, phys)) {
+ skb_free_frag(buf);
goto done;
+ }
priv->rx_buf[priv->rx_head] = buf;
priv->rx_phys[priv->rx_head] = phys;
hip04_set_recv_desc(priv, phys);
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h
index 52ea97c818b8..d9ab94910a2a 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h
@@ -104,8 +104,6 @@ struct hinic_dev {
u16 num_rss;
u16 rss_limit;
struct hinic_rss_type rss_type;
- u8 *rss_hkey_user;
- s32 *rss_indir_user;
struct hinic_intr_coal_info *rx_intr_coalesce;
struct hinic_intr_coal_info *tx_intr_coalesce;
struct hinic_sriov_info sriov_info;
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c
index a8b129ce1b7e..f75e8563f23a 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c
@@ -1064,17 +1064,6 @@ static int __set_rss_rxfh(struct net_device *netdev,
int err;

if (indir) {
- if (!nic_dev->rss_indir_user) {
- nic_dev->rss_indir_user =
- kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE,
- GFP_KERNEL);
- if (!nic_dev->rss_indir_user)
- return -ENOMEM;
- }
-
- memcpy(nic_dev->rss_indir_user, indir,
- sizeof(u32) * HINIC_RSS_INDIR_SIZE);
-
err = hinic_rss_set_indir_tbl(nic_dev,
nic_dev->rss_tmpl_idx, indir);
if (err)
@@ -1082,16 +1071,6 @@ static int __set_rss_rxfh(struct net_device *netdev,
}

if (key) {
- if (!nic_dev->rss_hkey_user) {
- nic_dev->rss_hkey_user =
- kzalloc(HINIC_RSS_KEY_SIZE * 2, GFP_KERNEL);
-
- if (!nic_dev->rss_hkey_user)
- return -ENOMEM;
- }
-
- memcpy(nic_dev->rss_hkey_user, key, HINIC_RSS_KEY_SIZE);
-
err = hinic_rss_set_template_tbl(nic_dev,
nic_dev->rss_tmpl_idx, key);
if (err)
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 725b130dd3a2..f6671a653f21 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -767,6 +767,9 @@ static inline bool ice_is_txtime_ena(const struct ice_tx_ring *ring)
struct ice_vsi *vsi = ring->vsi;
struct ice_pf *pf = vsi->back;

+ if (vsi->type != ICE_VSI_PF)
+ return false;
+
return test_bit(ring->q_index, pf->txtime_txqs);
}

diff --git a/drivers/net/ethernet/intel/ice/ice_eswitch.c b/drivers/net/ethernet/intel/ice/ice_eswitch.c
index c30e27bbfe6e..b069e6c514fb 100644
--- a/drivers/net/ethernet/intel/ice/ice_eswitch.c
+++ b/drivers/net/ethernet/intel/ice/ice_eswitch.c
@@ -512,9 +512,6 @@ int ice_eswitch_attach_vf(struct ice_pf *pf, struct ice_vf *vf)
struct ice_repr *repr;
int err;

- if (!ice_is_eswitch_mode_switchdev(pf))
- return 0;
-
repr = ice_repr_create_vf(vf);
if (IS_ERR(repr))
return PTR_ERR(repr);
diff --git a/drivers/net/ethernet/intel/ice/ice_lag.c b/drivers/net/ethernet/intel/ice/ice_lag.c
index 310e8fe2925c..08a17ded0ad5 100644
--- a/drivers/net/ethernet/intel/ice/ice_lag.c
+++ b/drivers/net/ethernet/intel/ice/ice_lag.c
@@ -2623,7 +2623,7 @@ int ice_init_lag(struct ice_pf *pf)
goto free_lport_res;

/* associate recipes to profiles */
- for (n = 0; n < ICE_PROFID_IPV6_GTPU_IPV6_TCP_INNER; n++) {
+ for (n = 0; n < ICE_MAX_NUM_PROFILES; n++) {
err = ice_aq_get_recipe_to_profile(&pf->hw, n,
&recipe_bits, NULL);
if (err)
diff --git a/drivers/net/ethernet/intel/ice/ice_parser.c b/drivers/net/ethernet/intel/ice/ice_parser.c
index f8e69630fb72..3ede4c1a5a8a 100644
--- a/drivers/net/ethernet/intel/ice/ice_parser.c
+++ b/drivers/net/ethernet/intel/ice/ice_parser.c
@@ -2368,6 +2368,9 @@ int ice_parser_profile_init(struct ice_parser_result *rslt,
u16 proto_off = 0;
u16 off;

+ if (rslt->ptype >= ICE_FLOW_PTYPE_MAX)
+ return -EINVAL;
+
memset(prof, 0, sizeof(*prof));
set_bit(rslt->ptype, prof->ptypes);
if (blk == ICE_BLK_SW) {
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 36df742c326c..f22fe778edb8 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -350,7 +350,7 @@ static u64 ice_ptp_extend_40b_ts(struct ice_pf *pf, u64 in_tstamp)
return 0;
}

- return ice_ptp_extend_32b_ts(pf->ptp.cached_phc_time,
+ return ice_ptp_extend_32b_ts(READ_ONCE(pf->ptp.cached_phc_time),
(in_tstamp >> 8) & mask);
}

@@ -3035,6 +3035,11 @@ void ice_ptp_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type)
struct ice_ptp *ptp = &pf->ptp;
int err;

+ if (ptp->state == ICE_PTP_UNINIT) {
+ dev_dbg(ice_pf_to_dev(pf), "PTP was not initialized, skipping rebuild\n");
+ return;
+ }
+
if (ptp->state == ICE_PTP_READY) {
ice_ptp_prepare_for_reset(pf, reset_type);
} else if (ptp->state != ICE_PTP_RESETTING) {
diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c
index 7e00e091756d..e04de0215596 100644
--- a/drivers/net/ethernet/intel/ice/ice_sriov.c
+++ b/drivers/net/ethernet/intel/ice/ice_sriov.c
@@ -484,12 +484,14 @@ static int ice_start_vfs(struct ice_pf *pf)
goto teardown;
}

- retval = ice_eswitch_attach_vf(pf, vf);
- if (retval) {
- dev_err(ice_pf_to_dev(pf), "Failed to attach VF %d to eswitch, error %d",
- vf->vf_id, retval);
- ice_vf_vsi_release(vf);
- goto teardown;
+ if (ice_is_eswitch_mode_switchdev(pf)) {
+ retval = ice_eswitch_attach_vf(pf, vf);
+ if (retval) {
+ dev_err(ice_pf_to_dev(pf), "Failed to attach VF %d to eswitch, error %d",
+ vf->vf_id, retval);
+ ice_vf_vsi_release(vf);
+ goto teardown;
+ }
}

set_bit(ICE_VF_STATE_INIT, vf->vf_states);
diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c
index 27e4acb1620f..9052e71e9c99 100644
--- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c
@@ -812,7 +812,8 @@ void ice_reset_all_vfs(struct ice_pf *pf)
}
ice_vf_post_vsi_rebuild(vf);

- ice_eswitch_attach_vf(pf, vf);
+ if (ice_is_eswitch_mode_switchdev(pf))
+ ice_eswitch_attach_vf(pf, vf);

mutex_unlock(&vf->cfg_lock);
}
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index be66f9b2e101..dc5ad784f456 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -3555,7 +3555,6 @@ int idpf_vc_core_init(struct idpf_adapter *adapter)

pci_sriov_set_totalvfs(adapter->pdev, idpf_get_max_vfs(adapter));
num_max_vports = idpf_get_max_vports(adapter);
- adapter->max_vports = num_max_vports;
adapter->vports = kzalloc_objs(*adapter->vports, num_max_vports);
if (!adapter->vports)
return -ENOMEM;
@@ -3576,6 +3575,12 @@ int idpf_vc_core_init(struct idpf_adapter *adapter)
goto err_netdev_alloc;
}

+ /* Set max_vports only after vports, netdevs and vport_config buffers
+ * are allocated to make sure max_vport bound loops don't end up
+ * crashing, following allocation errors on init.
+ */
+ adapter->max_vports = num_max_vports;
+
/* Start the mailbox task before requesting vectors. This will ensure
* vector information response from mailbox is handled
*/
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c
index 38cc539d724d..3b3eee15715f 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c
@@ -270,6 +270,7 @@ int otx2_alloc_mcam_entries(struct otx2_nic *pfvf, u16 count)
if (allocated) {
pfvf->flags |= OTX2_FLAG_MCAM_ENTRIES_ALLOC;
pfvf->flags |= OTX2_FLAG_NTUPLE_SUPPORT;
+ pfvf->flags |= OTX2_FLAG_TC_FLOWER_SUPPORT;
}

if (allocated != count)
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
index 40162b08014d..0b46ec29e64e 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c
@@ -30,6 +30,7 @@
#define OTX2_UNSUPP_LSE_DEPTH GENMASK(6, 4)

#define MCAST_INVALID_GRP (-1U)
+#define RATE_MANTISSA_BITS 8

static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst,
u32 *burst_exp, u32 *burst_mantissa)
@@ -66,28 +67,30 @@ static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst,
static void otx2_get_egress_rate_cfg(u64 maxrate, u32 *exp,
u32 *mantissa, u32 *div_exp)
{
- u64 tmp;
-
/* Rate calculation by hardware
*
* PIR_ADD = ((256 + mantissa) << exp) / 256
* rate = (2 * PIR_ADD) / ( 1 << div_exp)
* The resultant rate is in Mbps.
+ *
+ * Use div_exp = 0 and compute exp/mantissa for maxrate / 2; the
+ * leading factor of two yields the full rate. Rates below 2 Mbps
+ * are floored to the smallest step (exp = 0, mantissa = 0).
*/

- /* 2Mbps to 100Gbps can be expressed with div_exp = 0.
- * Setting this to '0' will ease the calculation of
- * exponent and mantissa.
- */
*div_exp = 0;
-
if (maxrate) {
- *exp = ilog2(maxrate) ? ilog2(maxrate) - 1 : 0;
- tmp = maxrate - rounddown_pow_of_two(maxrate);
- if (maxrate < MAX_RATE_MANTISSA)
- *mantissa = tmp * 2;
- else
- *mantissa = tmp / (1ULL << (*exp - 7));
+ maxrate = maxrate / 2;
+ if (!maxrate) {
+ /* Rates below 2 Mbps map to the smallest step */
+ *exp = 0;
+ *mantissa = 0;
+ } else {
+ *exp = ilog2(maxrate);
+ /* Clear MSB and derive fractional bits */
+ maxrate &= ~BIT(*exp);
+ *mantissa = (maxrate << RATE_MANTISSA_BITS) >> *exp;
+ }
} else {
/* Instead of disabling rate limiting, set all values to max */
*exp = MAX_RATE_EXPONENT;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c
index 4b86df6d5b9e..00e706e1ede1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c
@@ -173,6 +173,13 @@ static int mlx5e_dcbnl_ieee_getets(struct net_device *netdev,
}
memcpy(ets->tc_tsa, priv->dcbx.tc_tsa, sizeof(ets->tc_tsa));

+ /* Report 0 for non ETS TSA */
+ for (i = 0; i < ets->ets_cap; i++) {
+ if (ets->tc_tx_bw[i] == MLX5E_MAX_BW_ALLOC &&
+ priv->dcbx.tc_tsa[i] != IEEE_8021QAZ_TSA_ETS)
+ ets->tc_tx_bw[i] = 0;
+ }
+
return err;
}

@@ -317,6 +324,14 @@ static int mlx5e_dbcnl_validate_ets(struct net_device *netdev,
}
}

+ for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
+ if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_CB_SHAPER) {
+ netdev_err(netdev,
+ "Failed to validate ETS: CB Shaper is not supported\n");
+ return -EOPNOTSUPP;
+ }
+ }
+
/* Validate Bandwidth Sum */
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS) {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 94e5352a246c..fd442109aea8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -211,11 +211,11 @@ static void mlx5e_disable_async_events(struct mlx5e_priv *priv)

static int mlx5e_devcom_event_mpv(int event, void *my_data, void *event_data)
{
- struct mlx5e_priv *slave_priv = my_data;
+ struct mlx5e_priv *master_priv = event_data;

switch (event) {
case MPV_DEVCOM_MASTER_UP:
- mlx5_devcom_comp_set_ready(slave_priv->devcom, true);
+ mlx5_devcom_comp_set_ready(master_priv->devcom, true);
break;
case MPV_DEVCOM_MASTER_DOWN:
/* no need for comp set ready false since we unregister after
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c
index ba5cce706ea2..9693c74e9b16 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/helper.c
@@ -71,7 +71,7 @@ int esw_egress_acl_vlan_create(struct mlx5_eswitch *esw,
flow_act.action = flow_action;
vport->egress.allowed_vlan =
mlx5_add_flow_rules(vport->egress.acl, spec,
- &flow_act, fwd_dest, 0);
+ &flow_act, fwd_dest, fwd_dest ? 1 : 0);
if (IS_ERR(vport->egress.allowed_vlan)) {
err = PTR_ERR(vport->egress.allowed_vlan);
esw_warn(esw->dev,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c
index ee8b9765c5ba..2cba370a01d4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/port.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c
@@ -314,7 +314,7 @@ static int mlx5_query_module_id(struct mlx5_core_dev *dev, int module_num,
return -EIO;
}

- ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0);
+ ptr = MLX5_ADDR_OF(mcia_reg, out, dwords);

*module_id = ptr[0];

@@ -399,7 +399,7 @@ static int mlx5_query_mcia(struct mlx5_core_dev *dev,
return -EIO;
}

- ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0);
+ ptr = MLX5_ADDR_OF(mcia_reg, out, dwords);
memcpy(data, ptr, size);

return size;
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index ac71ca8450bf..a0e30f4e4666 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -197,6 +197,8 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
} else {
/* If dynamic allocation is enabled we have already allocated
* hwc msi
+ * Also, we make sure in this case the following is always true
+ * (num_msix_usable - 1 HWC) <= num_online_cpus()
*/
gc->num_msix_usable = min(resp.max_msix, num_online_cpus() + 1);
}
@@ -1667,8 +1669,8 @@ void mana_gd_free_res_map(struct gdma_resource *r)
* do the same thing.
*/

-static int irq_setup(unsigned int *irqs, unsigned int len, int node,
- bool skip_first_cpu)
+static int mana_irq_setup_numa_aware(unsigned int *irqs, unsigned int len,
+ int node, bool skip_first_cpu)
{
const struct cpumask *next, *prev = cpu_none_mask;
cpumask_var_t cpus __free(free_cpumask_var);
@@ -1704,11 +1706,24 @@ static int irq_setup(unsigned int *irqs, unsigned int len, int node,
return 0;
}

+/* must be called with cpus_read_lock() held */
+static void mana_irq_setup_linear(unsigned int *irqs, unsigned int len)
+{
+ int cpu;
+
+ for_each_online_cpu(cpu) {
+ if (len == 0)
+ break;
+
+ irq_set_affinity_and_hint(*irqs++, cpumask_of(cpu));
+ len--;
+ }
+}
+
static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
struct gdma_irq_context *gic;
- bool skip_first_cpu = false;
int *irqs, irq, err, i;

irqs = kmalloc_objs(int, nvec);
@@ -1716,10 +1731,12 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
return -ENOMEM;

/*
+ * In this function, num_msix_usable = HWC IRQ + Queue IRQ.
+ * nvec is only Queue IRQ (HWC already setup).
* While processing the next pci irq vector, we start with index 1,
* as IRQ vector at index 0 is already processed for HWC.
* However, the population of irqs array starts with index 0, to be
- * further used in irq_setup()
+ * further used in mana_irq_setup_numa_aware()
*/
for (i = 1; i <= nvec; i++) {
gic = kzalloc_obj(*gic);
@@ -1749,18 +1766,51 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
}

/*
- * When calling irq_setup() for dynamically added IRQs, if number of
- * CPUs is more than or equal to allocated MSI-X, we need to skip the
- * first CPU sibling group since they are already affinitized to HWC IRQ
+ * When calling mana_irq_setup_numa_aware() for dynamically added IRQs,
+ * if number of CPUs is more than or equal to allocated MSI-X, we need to
+ * skip the first CPU sibling group since they are already affinitized to
+ * HWC IRQ
*/
cpus_read_lock();
- if (gc->num_msix_usable <= num_online_cpus())
- skip_first_cpu = true;
+ if (gc->num_msix_usable <= num_online_cpus()) {
+ err = mana_irq_setup_numa_aware(irqs, nvec, gc->numa_node,
+ true);
+ if (err) {
+ cpus_read_unlock();
+ goto free_irq;
+ }
+ } else {
+ /*
+ * When num_msix_usable are more than num_online_cpus, our
+ * queue IRQs should be equal to num of online vCPUs.
+ * We try to make sure queue IRQs spread across all vCPUs.
+ * In such a case NUMA or CPU core affinity does not matter.
+ * Note: in this case the total mana IRQ should always be
+ * num_online_cpus + 1. The first HWC IRQ is already handled
+ * in HWC setup calls
+ * However, if CPUs went offline since num_msix_usable was
+ * computed, queue IRQs will be more than num_online_cpus().
+ * In such cases remaining extra IRQs will retain their default
+ * affinity.
+ */
+ int first_unassigned = num_online_cpus();

- err = irq_setup(irqs, nvec, gc->numa_node, skip_first_cpu);
- if (err) {
- cpus_read_unlock();
- goto free_irq;
+ if (nvec > first_unassigned) {
+ char buf[32];
+
+ if (first_unassigned == nvec - 1)
+ snprintf(buf, sizeof(buf), "%d",
+ first_unassigned);
+ else
+ snprintf(buf, sizeof(buf), "%d-%d",
+ first_unassigned, nvec - 1);
+
+ dev_dbg(&pdev->dev,
+ "MANA IRQ indices #%s will retain the default CPU affinity\n",
+ buf);
+ }
+
+ mana_irq_setup_linear(irqs, nvec);
}

cpus_read_unlock();
@@ -1846,7 +1896,7 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev, int nvec)
nvec -= 1;
}

- err = irq_setup(irqs, nvec, gc->numa_node, false);
+ err = mana_irq_setup_numa_aware(irqs, nvec, gc->numa_node, false);
if (err) {
cpus_read_unlock();
goto free_irq;
diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c
index 6d5833479d12..237300b82b91 100644
--- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c
+++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c
@@ -96,6 +96,9 @@ static int nfp_cpp_resource_find(struct nfp_cpp *cpp, struct nfp_resource *res)
res->mutex =
nfp_cpp_mutex_alloc(cpp,
NFP_RESOURCE_TBL_TARGET, addr, key);
+ if (!res->mutex)
+ return -ENOMEM;
+
res->cpp_id = NFP_CPP_ID(entry.region.cpp_target,
entry.region.cpp_action,
entry.region.cpp_token);
diff --git a/drivers/net/ethernet/realtek/rtase/rtase.h b/drivers/net/ethernet/realtek/rtase/rtase.h
index b9209eb6ea73..43087b17a0ff 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase.h
+++ b/drivers/net/ethernet/realtek/rtase/rtase.h
@@ -188,6 +188,12 @@ enum rtase_sw_flag_content {
RTASE_SWF_MSIX_ENABLED = BIT(2),
};

+enum rtase_parse_result {
+ RTASE_PARSE_OK,
+ RTASE_PARSE_SKIP,
+ RTASE_PARSE_DROP,
+};
+
#define RSVD_MASK 0x3FFFC000

struct rtase_tx_desc {
@@ -359,4 +365,6 @@ struct rtase_private {

#define RTASE_MSS_MASK GENMASK(28, 18)

+#define RTASE_MIN_PAD_LEN 47
+
#endif /* RTASE_H */
diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index 55105d34bc79..a57a525327a3 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -61,6 +61,7 @@
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/prefetch.h>
+#include <linux/ptp_classify.h>
#include <linux/rtnetlink.h>
#include <linux/tcp.h>
#include <asm/irq.h>
@@ -1249,6 +1250,199 @@ static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev)
return csum_cmd;
}

+static enum rtase_parse_result rtase_get_l3_proto(struct sk_buff *skb,
+ __be16 *proto,
+ u32 *network_offset)
+{
+ struct vlan_hdr *vh, _vh;
+ struct ethhdr *eh, _eh;
+ u32 offset = ETH_HLEN;
+
+ eh = skb_header_pointer(skb, 0, sizeof(_eh), &_eh);
+ if (!eh)
+ return RTASE_PARSE_DROP;
+
+ *proto = eh->h_proto;
+
+ while (eth_type_vlan(*proto)) {
+ vh = skb_header_pointer(skb, offset, sizeof(_vh), &_vh);
+ if (!vh)
+ return RTASE_PARSE_DROP;
+
+ *proto = vh->h_vlan_encapsulated_proto;
+ offset += VLAN_HLEN;
+ }
+
+ *network_offset = offset;
+
+ return RTASE_PARSE_OK;
+}
+
+static bool rtase_pad_to_transport_len(struct sk_buff *skb,
+ u32 transport_offset,
+ u32 pad_to_len)
+{
+ u32 trans_data_len;
+ u32 pad_len;
+
+ trans_data_len = skb->len - transport_offset;
+ if (trans_data_len >= pad_to_len)
+ return true;
+
+ if (skb_is_nonlinear(skb)) {
+ if (skb_linearize(skb))
+ return false;
+ }
+
+ pad_len = pad_to_len - trans_data_len;
+ if (__skb_put_padto(skb, skb->len + pad_len, false))
+ return false;
+
+ return true;
+}
+
+static enum rtase_parse_result rtase_get_transport_offset(struct sk_buff *skb,
+ u32 *transport_offset,
+ u8 *transport_proto,
+ u32 *pad_to_len)
+{
+ enum rtase_parse_result ret;
+ struct ipv6hdr *i6h, _i6h;
+ struct iphdr *ih, _ih;
+ bool non_first_frag;
+ __be16 proto;
+ u32 offset;
+ u32 no;
+
+ ret = rtase_get_l3_proto(skb, &proto, &no);
+ if (ret != RTASE_PARSE_OK)
+ return ret;
+
+ switch (proto) {
+ case htons(ETH_P_IP):
+ ih = skb_header_pointer(skb, no, sizeof(_ih), &_ih);
+ if (!ih)
+ return RTASE_PARSE_DROP;
+
+ if (ih->ihl < 5)
+ return RTASE_PARSE_DROP;
+
+ offset = no + ih->ihl * 4;
+ if (offset > skb->len)
+ return RTASE_PARSE_DROP;
+
+ non_first_frag = ntohs(ih->frag_off) & IP_OFFSET;
+
+ if (ih->protocol == IPPROTO_TCP) {
+ if (skb->len - offset < sizeof(struct tcphdr)) {
+ if (non_first_frag) {
+ *transport_offset = offset;
+ *transport_proto = IPPROTO_TCP;
+ *pad_to_len = sizeof(struct tcphdr);
+
+ return RTASE_PARSE_OK;
+ }
+
+ return RTASE_PARSE_DROP;
+ }
+
+ return RTASE_PARSE_SKIP;
+ }
+
+ if (ih->protocol != IPPROTO_UDP)
+ return RTASE_PARSE_SKIP;
+
+ *transport_offset = offset;
+ *transport_proto = IPPROTO_UDP;
+
+ if (skb->len - offset < sizeof(struct udphdr)) {
+ if (non_first_frag) {
+ *pad_to_len = sizeof(struct udphdr);
+
+ return RTASE_PARSE_OK;
+ }
+
+ return RTASE_PARSE_DROP;
+ }
+
+ return RTASE_PARSE_OK;
+
+ case htons(ETH_P_IPV6):
+ i6h = skb_header_pointer(skb, no, sizeof(_i6h), &_i6h);
+ if (!i6h)
+ return RTASE_PARSE_DROP;
+
+ offset = no + sizeof(*i6h);
+
+ if (i6h->nexthdr == IPPROTO_TCP) {
+ if (skb->len - offset < sizeof(struct tcphdr))
+ return RTASE_PARSE_DROP;
+
+ return RTASE_PARSE_SKIP;
+ }
+
+ if (i6h->nexthdr != IPPROTO_UDP)
+ return RTASE_PARSE_SKIP;
+
+ if (skb->len - offset < sizeof(struct udphdr))
+ return RTASE_PARSE_DROP;
+
+ *transport_offset = offset;
+ *transport_proto = IPPROTO_UDP;
+
+ return RTASE_PARSE_OK;
+
+ default:
+ return RTASE_PARSE_SKIP;
+ }
+}
+
+static bool rtase_skb_pad(struct sk_buff *skb)
+{
+ enum rtase_parse_result ret;
+ u32 transport_offset;
+ __be16 *dest, _dest;
+ u32 trans_data_len;
+ u32 pad_to_len = 0;
+ u8 transport_proto;
+ u16 dest_port;
+
+ ret = rtase_get_transport_offset(skb, &transport_offset,
+ &transport_proto, &pad_to_len);
+ if (ret == RTASE_PARSE_SKIP) {
+ return true;
+ } else if (ret == RTASE_PARSE_DROP) {
+ netdev_dbg(skb->dev, "drop malformed packet\n");
+ return false;
+ }
+
+ if (pad_to_len &&
+ !rtase_pad_to_transport_len(skb, transport_offset, pad_to_len))
+ return false;
+
+ if (transport_proto != IPPROTO_UDP)
+ return true;
+
+ trans_data_len = skb->len - transport_offset;
+ if (trans_data_len < offsetof(struct udphdr, len) ||
+ trans_data_len >= RTASE_MIN_PAD_LEN)
+ return true;
+
+ dest = skb_header_pointer(skb,
+ transport_offset +
+ offsetof(struct udphdr, dest),
+ sizeof(_dest), &_dest);
+ if (!dest)
+ return true;
+
+ dest_port = ntohs(*dest);
+ if (dest_port != PTP_EV_PORT && dest_port != PTP_GEN_PORT)
+ return true;
+
+ return rtase_pad_to_transport_len(skb, transport_offset,
+ RTASE_MIN_PAD_LEN);
+}
+
static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb,
u32 opts1, u32 opts2)
{
@@ -1362,6 +1556,9 @@ static netdev_tx_t rtase_start_xmit(struct sk_buff *skb,
opts2 |= rtase_tx_csum(skb, dev);
}

+ if (!rtase_skb_pad(skb))
+ goto err_dma_0;
+
frags = rtase_xmit_frags(ring, skb, opts1, opts2);
if (unlikely(frags < 0))
goto err_dma_0;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
index 7898b5075a8b..807dfa277eaf 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
@@ -524,6 +524,32 @@ static int intel_set_reg_access(const struct pmc_serdes_regs *regs, int max_regs
return ret;
}

+/*
+ * Return true if the SerDes lane rate must change to serve @interface.
+ * If the current rate cannot be determined, reconfigure as before.
+ */
+static bool intel_serdes_needs_reconfig(struct stmmac_priv *priv,
+ struct intel_priv_data *intel_priv,
+ phy_interface_t interface)
+{
+ u32 cur_rate, want_rate;
+ int data;
+
+ if (!intel_priv->mdio_adhoc_addr)
+ return true;
+
+ data = mdiobus_read(priv->mii, intel_priv->mdio_adhoc_addr,
+ SERDES_GCR0);
+ if (data < 0)
+ return true;
+
+ cur_rate = (data & SERDES_RATE_MASK) >> SERDES_RATE_PCIE_SHIFT;
+ want_rate = interface == PHY_INTERFACE_MODE_2500BASEX ?
+ SERDES_RATE_PCIE_GEN2 : SERDES_RATE_PCIE_GEN1;
+
+ return cur_rate != want_rate;
+}
+
static int intel_mac_finish(struct net_device *ndev,
void *intel_data,
unsigned int mode,
@@ -535,6 +561,11 @@ static int intel_mac_finish(struct net_device *ndev,
int max_regs = 0;
int ret = 0;

+ if (!intel_serdes_needs_reconfig(priv, intel_priv, interface)) {
+ priv->plat->phy_interface = interface;
+ return 0;
+ }
+
ret = intel_tsn_lane_is_available(ndev, intel_priv);
if (ret < 0) {
netdev_info(priv->dev, "No TSN lane available to set the registers.\n");
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index 28728271fbc9..55c170b93010 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
@@ -106,6 +106,17 @@ static void dwmac4_dma_init_channel(struct stmmac_priv *priv,
ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
}

+static void dwmac4_dma_deinit_channel(struct stmmac_priv *priv,
+ void __iomem *ioaddr, u32 chan)
+{
+ const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
+ u32 value;
+
+ value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+ value &= ~DMA_CHAN_INTR_DEFAULT_MASK;
+ writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+}
+
static void dwmac410_dma_init_channel(struct stmmac_priv *priv,
void __iomem *ioaddr,
struct stmmac_dma_cfg *dma_cfg, u32 chan)
@@ -125,6 +136,17 @@ static void dwmac410_dma_init_channel(struct stmmac_priv *priv,
ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
}

+static void dwmac410_dma_deinit_channel(struct stmmac_priv *priv,
+ void __iomem *ioaddr, u32 chan)
+{
+ const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
+ u32 value;
+
+ value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+ value &= ~DMA_CHAN_INTR_DEFAULT_MASK_4_10;
+ writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+}
+
static void dwmac4_dma_init(void __iomem *ioaddr,
struct stmmac_dma_cfg *dma_cfg)
{
@@ -547,6 +569,7 @@ const struct stmmac_dma_ops dwmac4_dma_ops = {
.reset = dwmac4_dma_reset,
.init = dwmac4_dma_init,
.init_chan = dwmac4_dma_init_channel,
+ .deinit_chan = dwmac4_dma_deinit_channel,
.init_rx_chan = dwmac4_dma_init_rx_chan,
.init_tx_chan = dwmac4_dma_init_tx_chan,
.axi = dwmac4_dma_axi,
@@ -576,6 +599,7 @@ const struct stmmac_dma_ops dwmac410_dma_ops = {
.reset = dwmac4_dma_reset,
.init = dwmac4_dma_init,
.init_chan = dwmac410_dma_init_channel,
+ .deinit_chan = dwmac410_dma_deinit_channel,
.init_rx_chan = dwmac4_dma_init_rx_chan,
.init_tx_chan = dwmac4_dma_init_tx_chan,
.axi = dwmac4_dma_axi,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index f02b434bbd50..52054f31376d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -1370,36 +1370,40 @@ static int dwxgmac2_config_l4_filter(struct mac_device_info *hw, u32 filter_no,
value &= ~XGMAC_L4PEN0;
}

- value &= ~(XGMAC_L4SPM0 | XGMAC_L4SPIM0);
- value &= ~(XGMAC_L4DPM0 | XGMAC_L4DPIM0);
if (sa) {
value |= XGMAC_L4SPM0;
if (inv)
value |= XGMAC_L4SPIM0;
+ else
+ value &= ~XGMAC_L4SPIM0;
} else {
value |= XGMAC_L4DPM0;
if (inv)
value |= XGMAC_L4DPIM0;
+ else
+ value &= ~XGMAC_L4DPIM0;
}

ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L3L4_CTRL, value);
if (ret)
return ret;

- if (sa) {
- value = FIELD_PREP(XGMAC_L4SP0, match);
+ ret = dwxgmac2_filter_read(hw, filter_no, XGMAC_L4_ADDR, &value);
+ if (ret)
+ return ret;

- ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value);
- if (ret)
- return ret;
+ if (sa) {
+ value &= ~XGMAC_L4SP0;
+ value |= FIELD_PREP(XGMAC_L4SP0, match);
} else {
- value = FIELD_PREP(XGMAC_L4DP0, match);
-
- ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value);
- if (ret)
- return ret;
+ value &= ~XGMAC_L4DP0;
+ value |= FIELD_PREP(XGMAC_L4DP0, match);
}

+ ret = dwxgmac2_filter_write(hw, filter_no, XGMAC_L4_ADDR, value);
+ if (ret)
+ return ret;
+
if (!en)
return dwxgmac2_filter_write(hw, filter_no, XGMAC_L3L4_CTRL, 0);

diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index e6317b94fff7..04dafec021b4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -170,6 +170,8 @@ struct stmmac_dma_ops {
void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg);
void (*init_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
struct stmmac_dma_cfg *dma_cfg, u32 chan);
+ void (*deinit_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
+ u32 chan);
void (*init_rx_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
struct stmmac_dma_cfg *dma_cfg,
dma_addr_t phy, u32 chan);
@@ -235,6 +237,8 @@ struct stmmac_dma_ops {
stmmac_do_void_callback(__priv, dma, init, __args)
#define stmmac_init_chan(__priv, __args...) \
stmmac_do_void_callback(__priv, dma, init_chan, __priv, __args)
+#define stmmac_deinit_chan(__priv, __args...) \
+ stmmac_do_void_callback(__priv, dma, deinit_chan, __priv, __args)
#define stmmac_init_rx_chan(__priv, __args...) \
stmmac_do_void_callback(__priv, dma, init_rx_chan, __priv, __args)
#define stmmac_init_tx_chan(__priv, __args...) \
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 3591755ea30b..755f48a34314 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1083,63 +1083,45 @@ static void stmmac_mac_link_up(struct phylink_config *config,
old_ctrl = readl(priv->ioaddr + MAC_CTRL_REG);
ctrl = old_ctrl & ~priv->hw->link.speed_mask;

- if (interface == PHY_INTERFACE_MODE_USXGMII) {
- switch (speed) {
- case SPEED_10000:
- ctrl |= priv->hw->link.xgmii.speed10000;
- break;
- case SPEED_5000:
- ctrl |= priv->hw->link.xgmii.speed5000;
- break;
- case SPEED_2500:
+ switch (speed) {
+ case SPEED_100000:
+ ctrl |= priv->hw->link.xlgmii.speed100000;
+ break;
+ case SPEED_50000:
+ ctrl |= priv->hw->link.xlgmii.speed50000;
+ break;
+ case SPEED_40000:
+ ctrl |= priv->hw->link.xlgmii.speed40000;
+ break;
+ case SPEED_25000:
+ ctrl |= priv->hw->link.xlgmii.speed25000;
+ break;
+ case SPEED_10000:
+ ctrl |= priv->hw->link.xgmii.speed10000;
+ break;
+ case SPEED_5000:
+ ctrl |= priv->hw->link.xgmii.speed5000;
+ break;
+ case SPEED_2500:
+ if (interface == PHY_INTERFACE_MODE_USXGMII)
ctrl |= priv->hw->link.xgmii.speed2500;
- break;
- default:
- return;
- }
- } else if (interface == PHY_INTERFACE_MODE_XLGMII) {
- switch (speed) {
- case SPEED_100000:
- ctrl |= priv->hw->link.xlgmii.speed100000;
- break;
- case SPEED_50000:
- ctrl |= priv->hw->link.xlgmii.speed50000;
- break;
- case SPEED_40000:
- ctrl |= priv->hw->link.xlgmii.speed40000;
- break;
- case SPEED_25000:
- ctrl |= priv->hw->link.xlgmii.speed25000;
- break;
- case SPEED_10000:
- ctrl |= priv->hw->link.xgmii.speed10000;
- break;
- case SPEED_2500:
- ctrl |= priv->hw->link.speed2500;
- break;
- case SPEED_1000:
- ctrl |= priv->hw->link.speed1000;
- break;
- default:
- return;
- }
- } else {
- switch (speed) {
- case SPEED_2500:
+ else
ctrl |= priv->hw->link.speed2500;
- break;
- case SPEED_1000:
- ctrl |= priv->hw->link.speed1000;
- break;
- case SPEED_100:
- ctrl |= priv->hw->link.speed100;
- break;
- case SPEED_10:
- ctrl |= priv->hw->link.speed10;
- break;
- default:
- return;
- }
+ break;
+ case SPEED_1000:
+ ctrl |= priv->hw->link.speed1000;
+ break;
+ case SPEED_100:
+ ctrl |= priv->hw->link.speed100;
+ break;
+ case SPEED_10:
+ ctrl |= priv->hw->link.speed10;
+ break;
+ default:
+ netdev_err(priv->dev,
+ "unsupported speed %s on %s, leaving the MAC disabled\n",
+ phy_speed_to_str(speed), phy_modes(interface));
+ return;
}

if (priv->plat->fix_mac_speed)
@@ -2560,6 +2542,7 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv)
{
u8 rx_channels_count = priv->plat->rx_queues_to_use;
u8 tx_channels_count = priv->plat->tx_queues_to_use;
+ u8 dma_csr_ch = max(rx_channels_count, tx_channels_count);
u8 chan;

for (chan = 0; chan < rx_channels_count; chan++)
@@ -2567,6 +2550,9 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv)

for (chan = 0; chan < tx_channels_count; chan++)
stmmac_stop_tx_dma(priv, chan);
+
+ for (chan = 0; chan < dma_csr_ch; chan++)
+ stmmac_deinit_chan(priv, priv->ioaddr, chan);
}

/**
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
index d78652718599..14cabe76e53e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
@@ -446,6 +446,7 @@ static int tc_parse_flow_actions(struct stmmac_priv *priv,
}

#define ETHER_TYPE_FULL_MASK cpu_to_be16(~0)
+#define IP_PROTO_FULL_MASK 0xFF

static int tc_add_basic_flow(struct stmmac_priv *priv,
struct flow_cls_offload *cls,
@@ -461,6 +462,37 @@ static int tc_add_basic_flow(struct stmmac_priv *priv,

flow_rule_match_basic(rule, &match);

+ /* Both network proto and transport proto not present in the key */
+ if (!match.mask || !(match.mask->n_proto || match.mask->ip_proto)) {
+ NL_SET_ERR_MSG_MOD(cls->common.extack,
+ "filter must specify network or transport protocol");
+ return -EOPNOTSUPP;
+ }
+
+ /* If the proto is present in the key and is not full mask */
+ if ((match.mask->n_proto && match.mask->n_proto != ETHER_TYPE_FULL_MASK) ||
+ (match.mask->ip_proto && match.mask->ip_proto != IP_PROTO_FULL_MASK)) {
+ NL_SET_ERR_MSG_MOD(cls->common.extack,
+ "only full protocol mask is supported");
+ return -EOPNOTSUPP;
+ }
+
+ /* Network proto is present in the key and is not IPv4 */
+ if (match.mask->n_proto && match.key->n_proto != cpu_to_be16(ETH_P_IP)) {
+ NL_SET_ERR_MSG_MOD(cls->common.extack,
+ "only IPv4 network protocol is supported");
+ return -EOPNOTSUPP;
+ }
+
+ /* Transport proto is present in the key and is not TCP or UDP */
+ if (match.mask->ip_proto &&
+ match.key->ip_proto != IPPROTO_TCP &&
+ match.key->ip_proto != IPPROTO_UDP) {
+ NL_SET_ERR_MSG_MOD(cls->common.extack,
+ "only TCP and UDP transport protocols are supported");
+ return -EOPNOTSUPP;
+ }
+
entry->ip_proto = match.key->ip_proto;
return 0;
}
@@ -598,6 +630,8 @@ static int tc_add_flow(struct stmmac_priv *priv,
ret = tc_flow_parsers[i].fn(priv, cls, entry);
if (!ret)
entry->in_use = true;
+ else if (ret == -EOPNOTSUPP)
+ return ret;
}

if (!entry->in_use)
@@ -627,6 +661,7 @@ static int tc_del_flow(struct stmmac_priv *priv,
entry->in_use = false;
entry->cookie = 0;
entry->is_l4 = false;
+ entry->action = 0;
return ret;
}

diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
index 8fc32df8e49a..479c83f5d645 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
@@ -96,11 +96,13 @@ int txgbe_read_eeprom_hostif(struct wx *wx,
dword_len = round_up(length, 4) >> 2;

for (i = 0; i < dword_len; i++) {
+ u32 copy_len = min_t(u32, 4, length - i * 4);
+
value = rd32a(wx, WX_FW2SW_MBOX, i + offset);
le32_to_cpus(&value);

- memcpy(data, &value, 4);
- data += 4;
+ memcpy(data, &value, copy_len);
+ data += copy_len;
}

return 0;
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index 8b7c3753bb6a..1f633bdf1972 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -951,6 +951,7 @@ static void txgbe_remove(struct pci_dev *pdev)
netdev = wx->netdev;
wx_disable_sriov(wx);
unregister_netdev(netdev);
+ txgbe_fdir_filter_exit(wx);

timer_shutdown_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index c55bdb805a64..2f352f7cf961 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -43,8 +43,17 @@ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
#define GENEVE_OPT_GRO_HINT_LEN 1

struct geneve_opt_gro_hint {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
u8 inner_proto_id:2,
- nested_is_v6:1;
+ nested_is_v6:1,
+ rsvd:5;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ u8 rsvd:5,
+ nested_is_v6:1,
+ inner_proto_id:2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
u8 nested_nh_offset;
u8 nested_tp_offset;
u8 nested_hdr_len;
@@ -576,6 +585,7 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb,
struct iphdr *iph;
struct udphdr *uh;
__be16 p;
+ int err;

hint_off = geneve_sk_gro_hint_off(sk, *geneveh, &p, &len);
if (!hint_off)
@@ -600,12 +610,20 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb,
!geneve_opt_gro_hint_validate(skb->data, gro_hint)))
return -EINVAL;

- ipv6h = (void *)skb->data + gro_hint->nested_nh_offset;
- iph = (struct iphdr *)ipv6h;
total_len = skb->len - gro_hint->nested_nh_offset;
if (total_len >= GRO_LEGACY_MAX_SIZE)
return -E2BIG;

+ err = skb_ensure_writable(skb, gro_hint->nested_tp_offset + sizeof(*uh));
+ if (unlikely(err))
+ return err;
+
+ *geneveh = geneve_hdr(skb);
+ gro_hint = geneve_opt_gro_hint(*geneveh, hint_off);
+
+ ipv6h = (void *)skb->data + gro_hint->nested_nh_offset;
+ iph = (struct iphdr *)ipv6h;
+
/*
* After stripping the outer encap, the packet still carries a
* tunnel encapsulation: the nested one.
@@ -2233,6 +2251,9 @@ static int geneve_changelink(struct net_device *dev, struct nlattr *tb[],
struct geneve_config cfg;
int err;

+ if (!rtnl_dev_link_net_capable(dev, geneve->net))
+ return -EPERM;
+
/* If the geneve device is configured for metadata (or externally
* controlled, for example, OVS), then nothing can be changed.
*/
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index a60ef32b35b8..9a12cc53da00 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -669,8 +669,9 @@ static int gtp1u_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
return -1;

/* pull GTP and UDP headers */
- skb_pull_data(skb,
- sizeof(struct gtp1_header_long) + sizeof(struct udphdr));
+ if (!skb_pull_data(skb, sizeof(struct gtp1_header_long) +
+ sizeof(struct udphdr)))
+ return -1;

gtp_pkt = skb_push(skb, sizeof(struct gtp1u_packet));
memset(gtp_pkt, 0, sizeof(struct gtp1u_packet));
@@ -826,13 +827,17 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
if (!pskb_may_pull(skb, hdrlen))
return -1;

+ gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
+
+ if (gtp1->flags & GTP1_F_EXTHDR &&
+ gtp_parse_exthdrs(skb, &hdrlen) < 0)
+ return -1;
+
if (gtp_inner_proto(skb, hdrlen, &inner_proto) < 0) {
netdev_dbg(gtp->dev, "GTP packet does not encapsulate an IP packet\n");
return -1;
}

- gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
-
pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid),
gtp_proto_to_family(inner_proto));
if (!pctx) {
@@ -840,10 +845,6 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
return 1;
}

- if (gtp1->flags & GTP1_F_EXTHDR &&
- gtp_parse_exthdrs(skb, &hdrlen) < 0)
- return -1;
-
return gtp_rx(pctx, skb, hdrlen, gtp->role, inner_proto);
}

diff --git a/drivers/net/mctp/mctp-i3c.c b/drivers/net/mctp/mctp-i3c.c
index 6d2bbae7477b..88d9e36cd4a2 100644
--- a/drivers/net/mctp/mctp-i3c.c
+++ b/drivers/net/mctp/mctp-i3c.c
@@ -731,18 +731,21 @@ static __init int mctp_i3c_mod_init(void)
int rc;

rc = i3c_register_notifier(&mctp_i3c_notifier);
- if (rc < 0) {
- i3c_driver_unregister(&mctp_i3c_driver);
+ if (rc < 0)
return rc;
- }

i3c_for_each_bus_locked(mctp_i3c_bus_add_new, NULL);

rc = i3c_driver_register(&mctp_i3c_driver);
if (rc < 0)
- return rc;
+ goto err_unregister_notifier;

return 0;
+
+err_unregister_notifier:
+ i3c_unregister_notifier(&mctp_i3c_notifier);
+ mctp_i3c_bus_remove_all();
+ return rc;
}

static __exit void mctp_i3c_mod_exit(void)
diff --git a/drivers/net/mctp/mctp-serial.c b/drivers/net/mctp/mctp-serial.c
index 26c9a33fd636..a5070ffa9a95 100644
--- a/drivers/net/mctp/mctp-serial.c
+++ b/drivers/net/mctp/mctp-serial.c
@@ -318,7 +318,7 @@ static void mctp_serial_push_header(struct mctp_serial *dev, u8 c)
} else {
dev->rxlen = c;
dev->rxpos = 0;
- dev->rxstate = STATE_DATA;
+ dev->rxstate = c > 0 ? STATE_DATA : STATE_TRAILER;
dev->rxfcs = crc_ccitt_byte(dev->rxfcs, c);
}
break;
diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
index a6b777a9c2d9..9a66d693039a 100644
--- a/drivers/net/ovpn/io.c
+++ b/drivers/net/ovpn/io.c
@@ -142,7 +142,7 @@ void ovpn_decrypt_post(void *data, int ret)
}

/* keep track of last received authenticated packet for keepalive */
- WRITE_ONCE(peer->last_recv, ktime_get_real_seconds());
+ WRITE_ONCE(peer->last_recv, ktime_get_boottime_seconds());

rcu_read_lock();
sock = rcu_dereference(peer->sock);
@@ -294,7 +294,7 @@ void ovpn_encrypt_post(void *data, int ret)

ovpn_peer_stats_increment_tx(&peer->link_stats, orig_len);
/* keep track of last sent packet for keepalive */
- WRITE_ONCE(peer->last_sent, ktime_get_real_seconds());
+ WRITE_ONCE(peer->last_sent, ktime_get_boottime_seconds());
/* skb passed down the stack - don't free it */
skb = NULL;
err_unlock:
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index a09d61296425..a21d02ac715e 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -26,11 +26,12 @@ static void unlock_ovpn(struct ovpn_priv *ovpn,
struct llist_head *release_list)
__releases(&ovpn->lock)
{
- struct ovpn_peer *peer;
+ struct ovpn_peer *peer, *next;

spin_unlock_bh(&ovpn->lock);

- llist_for_each_entry(peer, release_list->first, release_entry) {
+ llist_for_each_entry_safe(peer, next, release_list->first,
+ release_entry) {
ovpn_socket_release(peer);
ovpn_peer_put(peer);
}
@@ -44,7 +45,7 @@ static void unlock_ovpn(struct ovpn_priv *ovpn,
*/
void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout)
{
- time64_t now = ktime_get_real_seconds();
+ time64_t now = ktime_get_boottime_seconds();

netdev_dbg(peer->ovpn->dev,
"scheduling keepalive for peer %u: interval=%u timeout=%u\n",
@@ -1167,7 +1168,6 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk,
ovpn_sock = rcu_access_pointer(peer->sock);
if (!ovpn_sock || ovpn_sock->sk != sk) {
spin_unlock_bh(&ovpn->lock);
- ovpn_peer_put(peer);
return;
}
}
@@ -1285,8 +1285,10 @@ static time64_t ovpn_peer_keepalive_work_single(struct ovpn_peer *peer,
netdev_dbg(peer->ovpn->dev,
"sending keepalive to peer %u\n",
peer->id);
- if (schedule_work(&peer->keepalive_work))
- ovpn_peer_hold(peer);
+ if (WARN_ON(!ovpn_peer_hold(peer)))
+ return 0;
+ if (!schedule_work(&peer->keepalive_work))
+ ovpn_peer_put(peer);
}

if (next_run1 < next_run2)
@@ -1357,7 +1359,7 @@ void ovpn_peer_keepalive_work(struct work_struct *work)
{
struct ovpn_priv *ovpn = container_of(work, struct ovpn_priv,
keepalive_work.work);
- time64_t next_run = 0, now = ktime_get_real_seconds();
+ time64_t next_run = 0, now = ktime_get_boottime_seconds();
LLIST_HEAD(release_list);

spin_lock_bh(&ovpn->lock);
diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c
index 433bd07a4f1b..0af14055c39a 100644
--- a/drivers/net/ovpn/tcp.c
+++ b/drivers/net/ovpn/tcp.c
@@ -151,7 +151,8 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb)
/* take reference for deferred peer deletion. should never fail */
if (WARN_ON(!ovpn_peer_hold(peer)))
goto err_nopeer;
- schedule_work(&peer->tcp.defer_del_work);
+ if (!schedule_work(&peer->tcp.defer_del_work))
+ ovpn_peer_put(peer);
ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
err_nopeer:
kfree_skb(skb);
@@ -283,7 +284,8 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk)
* stream therefore we abort the connection
*/
ovpn_peer_hold(peer);
- schedule_work(&peer->tcp.defer_del_work);
+ if (!schedule_work(&peer->tcp.defer_del_work))
+ ovpn_peer_put(peer);

/* we bail out immediately and keep tx_in_progress set
* to true. This way we prevent more TX attempts
diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c
index e69fa2f0a0e8..0337e2bcc012 100644
--- a/drivers/net/pcs/pcs-xpcs.c
+++ b/drivers/net/pcs/pcs-xpcs.c
@@ -1058,6 +1058,7 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,

/* Reset link_state */
state->link = false;
+ state->an_complete = false;
state->speed = SPEED_UNKNOWN;
state->duplex = DUPLEX_UNKNOWN;
state->pause = 0;
@@ -1069,6 +1070,8 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,
if (ret < 0)
return ret;

+ state->an_complete = ret & DW_VR_MII_AN_STS_C37_ANCMPLT_INTR;
+
if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) {
int speed_value;

@@ -1086,34 +1089,13 @@ static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,
state->duplex = DUPLEX_FULL;
else
state->duplex = DUPLEX_HALF;
- } else if (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) {
- int speed, duplex;
-
- state->link = true;
-
- speed = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_BMCR);
- if (speed < 0)
- return speed;
-
- speed &= BMCR_SPEED100 | BMCR_SPEED1000;
- if (speed == BMCR_SPEED1000)
- state->speed = SPEED_1000;
- else if (speed == BMCR_SPEED100)
- state->speed = SPEED_100;
- else if (speed == 0)
- state->speed = SPEED_10;
-
- duplex = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_ADVERTISE);
- if (duplex < 0)
- return duplex;

- if (duplex & ADVERTISE_1000XFULL)
- state->duplex = DUPLEX_FULL;
- else if (duplex & ADVERTISE_1000XHALF)
- state->duplex = DUPLEX_HALF;
+ return 0;
+ }

+ /* Clear AN complete status or interrupt */
+ if (state->an_complete)
xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0);
- }

return 0;
}
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 7a578b5aa2ed..f71cffa88406 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -753,7 +753,7 @@ static int m88e1111_config_inband(struct phy_device *phydev, unsigned int modes)
err = phy_modify(phydev, MII_M1111_PHY_EXT_SR,
MII_M1111_HWCFG_SERIAL_AN_BYPASS, extsr);
if (err < 0)
- return extsr;
+ return err;

return phy_modify_paged(phydev, MII_MARVELL_FIBER_PAGE, MII_BMCR,
BMCR_ANENABLE, bmcr);
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 57c68efa5ff8..cacc4c3a37d2 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -184,6 +184,7 @@ struct channel {
struct list_head clist; /* link in list of channels per unit */
spinlock_t upl; /* protects `ppp' and 'bridge' */
struct channel __rcu *bridge; /* "bridged" ppp channel */
+ struct rcu_head rcu; /* for RCU-deferred free of the channel */
#ifdef CONFIG_PPP_MULTILINK
u8 avail; /* flag used in multilink stuff */
u8 had_frag; /* >= 1 fragments have been sent */
@@ -809,7 +810,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case PPPIOCSMRU:
if (get_user(val, p))
break;
+ ppp_recv_lock(ppp);
ppp->mru = val;
+ ppp_recv_unlock(ppp);
err = 0;
break;

@@ -830,7 +833,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;

case PPPIOCGFLAGS:
+ ppp_lock(ppp);
val = ppp->flags | ppp->xstate | ppp->rstate;
+ ppp_unlock(ppp);
if (put_user(val, p))
break;
err = 0;
@@ -854,7 +859,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case PPPIOCSDEBUG:
if (get_user(val, p))
break;
- ppp->debug = val;
+ WRITE_ONCE(ppp->debug, val);
err = 0;
break;

@@ -865,16 +870,16 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;

case PPPIOCGIDLE32:
- idle32.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
- idle32.recv_idle = (jiffies - ppp->last_recv) / HZ;
- if (copy_to_user(argp, &idle32, sizeof(idle32)))
+ idle32.xmit_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_xmit))) / HZ;
+ idle32.recv_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_recv))) / HZ;
+ if (copy_to_user(argp, &idle32, sizeof(idle32)))
break;
err = 0;
break;

case PPPIOCGIDLE64:
- idle64.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
- idle64.recv_idle = (jiffies - ppp->last_recv) / HZ;
+ idle64.xmit_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_xmit))) / HZ;
+ idle64.recv_idle = max(0L, (long)(jiffies - READ_ONCE(ppp->last_recv))) / HZ;
if (copy_to_user(argp, &idle64, sizeof(idle64)))
break;
err = 0;
@@ -915,7 +920,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (copy_to_user(argp, &npi, sizeof(npi)))
break;
} else {
- ppp->npmode[i] = npi.mode;
+ WRITE_ONCE(ppp->npmode[i], npi.mode);
/* we may be able to transmit more packets now (??) */
netif_wake_queue(ppp->dev);
}
@@ -1453,7 +1458,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
goto outf;

/* Drop, accept or reject the packet */
- switch (ppp->npmode[npi]) {
+ switch (READ_ONCE(ppp->npmode[npi])) {
case NPMODE_PASS:
break;
case NPMODE_QUEUE:
@@ -1481,7 +1486,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)

outf:
kfree_skb(skb);
- ++dev->stats.tx_dropped;
+ DEV_STATS_INC(dev, tx_dropped);
return NETDEV_TX_OK;
}

@@ -1531,11 +1536,11 @@ ppp_net_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
static void
ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64)
{
- stats64->rx_errors = dev->stats.rx_errors;
- stats64->tx_errors = dev->stats.tx_errors;
- stats64->rx_dropped = dev->stats.rx_dropped;
- stats64->tx_dropped = dev->stats.tx_dropped;
- stats64->rx_length_errors = dev->stats.rx_length_errors;
+ stats64->rx_errors = DEV_STATS_READ(dev, rx_errors);
+ stats64->tx_errors = DEV_STATS_READ(dev, tx_errors);
+ stats64->rx_dropped = DEV_STATS_READ(dev, rx_dropped);
+ stats64->tx_dropped = DEV_STATS_READ(dev, tx_dropped);
+ stats64->rx_length_errors = DEV_STATS_READ(dev, rx_length_errors);
dev_fetch_sw_netstats(stats64, dev->tstats);
}

@@ -1789,7 +1794,7 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb)
*(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_OUTBOUND_TAG);
if (ppp->pass_filter &&
bpf_prog_run(ppp->pass_filter, skb) == 0) {
- if (ppp->debug & 1)
+ if (READ_ONCE(ppp->debug) & 1)
netdev_printk(KERN_DEBUG, ppp->dev,
"PPP: outbound frame "
"not passed\n");
@@ -1799,11 +1804,11 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb)
/* if this packet passes the active filter, record the time */
if (!(ppp->active_filter &&
bpf_prog_run(ppp->active_filter, skb) == 0))
- ppp->last_xmit = jiffies;
+ WRITE_ONCE(ppp->last_xmit, jiffies);
skb_pull(skb, 2);
#else
/* for data packets, record the time */
- ppp->last_xmit = jiffies;
+ WRITE_ONCE(ppp->last_xmit, jiffies);
#endif /* CONFIG_PPP_FILTER */
}

@@ -1888,7 +1893,7 @@ ppp_prepare_tx_skb(struct ppp *ppp, struct sk_buff **pskb)

drop:
kfree_skb(skb);
- ++ppp->dev->stats.tx_errors;
+ DEV_STATS_INC(ppp->dev, tx_errors);
return 1;
}

@@ -2153,9 +2158,9 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
noskb:
spin_unlock(&pch->downl);
err_linearize:
- if (ppp->debug & 1)
+ if (READ_ONCE(ppp->debug) & 1)
netdev_err(ppp->dev, "PPP: no memory (fragment)\n");
- ++ppp->dev->stats.tx_errors;
+ DEV_STATS_INC(ppp->dev, tx_errors);
++ppp->nxseq;
return 1; /* abandon the frame */
}
@@ -2328,7 +2333,7 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
if (!ppp_decompress_proto(skb)) {
kfree_skb(skb);
if (ppp) {
- ++ppp->dev->stats.rx_length_errors;
+ DEV_STATS_INC(ppp->dev, rx_length_errors);
ppp_receive_error(ppp);
}
goto done;
@@ -2390,7 +2395,7 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
static void
ppp_receive_error(struct ppp *ppp)
{
- ++ppp->dev->stats.rx_errors;
+ DEV_STATS_INC(ppp->dev, rx_errors);
if (ppp->vj)
slhc_toss(ppp->vj);
}
@@ -2501,7 +2506,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
*(__be16 *)skb_push(skb, 2) = htons(PPP_FILTER_INBOUND_TAG);
if (ppp->pass_filter &&
bpf_prog_run(ppp->pass_filter, skb) == 0) {
- if (ppp->debug & 1)
+ if (READ_ONCE(ppp->debug) & 1)
netdev_printk(KERN_DEBUG, ppp->dev,
"PPP: inbound frame "
"not passed\n");
@@ -2510,14 +2515,14 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
}
if (!(ppp->active_filter &&
bpf_prog_run(ppp->active_filter, skb) == 0))
- ppp->last_recv = jiffies;
+ WRITE_ONCE(ppp->last_recv, jiffies);
__skb_pull(skb, 2);
} else
#endif /* CONFIG_PPP_FILTER */
- ppp->last_recv = jiffies;
+ WRITE_ONCE(ppp->last_recv, jiffies);

if ((ppp->dev->flags & IFF_UP) == 0 ||
- ppp->npmode[npi] != NPMODE_PASS) {
+ READ_ONCE(ppp->npmode[npi]) != NPMODE_PASS) {
kfree_skb(skb);
} else {
/* chop off protocol */
@@ -2657,7 +2662,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
*/
if (seq_before(seq, ppp->nextseq)) {
kfree_skb(skb);
- ++ppp->dev->stats.rx_dropped;
+ DEV_STATS_INC(ppp->dev, rx_dropped);
ppp_receive_error(ppp);
return;
}
@@ -2693,7 +2698,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
if (pskb_may_pull(skb, 2))
ppp_receive_nonmp_frame(ppp, skb);
else {
- ++ppp->dev->stats.rx_length_errors;
+ DEV_STATS_INC(ppp->dev, rx_length_errors);
kfree_skb(skb);
ppp_receive_error(ppp);
}
@@ -2770,7 +2775,7 @@ ppp_mp_reconstruct(struct ppp *ppp)
seq = seq_before(minseq, PPP_MP_CB(p)->sequence)?
minseq + 1: PPP_MP_CB(p)->sequence;

- if (ppp->debug & 1)
+ if (READ_ONCE(ppp->debug) & 1)
netdev_printk(KERN_DEBUG, ppp->dev,
"lost frag %u..%u\n",
oldseq, seq-1);
@@ -2799,7 +2804,7 @@ ppp_mp_reconstruct(struct ppp *ppp)
if (lost == 0 && (PPP_MP_CB(p)->BEbits & E) &&
(PPP_MP_CB(head)->BEbits & B)) {
if (len > ppp->mrru + 2) {
- ++ppp->dev->stats.rx_length_errors;
+ DEV_STATS_INC(ppp->dev, rx_length_errors);
netdev_printk(KERN_DEBUG, ppp->dev,
"PPP: reconstructed packet"
" is too long (%d)\n", len);
@@ -2819,7 +2824,7 @@ ppp_mp_reconstruct(struct ppp *ppp)
struct sk_buff *tmp2;

skb_queue_reverse_walk_from_safe(list, p, tmp2) {
- if (ppp->debug & 1)
+ if (READ_ONCE(ppp->debug) & 1)
netdev_printk(KERN_DEBUG, ppp->dev,
"discarding frag %u\n",
PPP_MP_CB(p)->sequence);
@@ -2841,7 +2846,7 @@ ppp_mp_reconstruct(struct ppp *ppp)
skb_queue_walk_safe(list, p, tmp) {
if (p == head)
break;
- if (ppp->debug & 1)
+ if (READ_ONCE(ppp->debug) & 1)
netdev_printk(KERN_DEBUG, ppp->dev,
"discarding frag %u\n",
PPP_MP_CB(p)->sequence);
@@ -2849,12 +2854,12 @@ ppp_mp_reconstruct(struct ppp *ppp)
kfree_skb(p);
}

- if (ppp->debug & 1)
+ if (READ_ONCE(ppp->debug) & 1)
netdev_printk(KERN_DEBUG, ppp->dev,
" missed pkts %u..%u\n",
ppp->nextseq,
PPP_MP_CB(head)->sequence-1);
- ++ppp->dev->stats.rx_dropped;
+ DEV_STATS_INC(ppp->dev, rx_dropped);
ppp_receive_error(ppp);
}

@@ -3160,7 +3165,8 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
if (!ppp->rc_state)
break;
if (ppp->rcomp->decomp_init(ppp->rc_state, dp, len,
- ppp->file.index, 0, ppp->mru, ppp->debug)) {
+ ppp->file.index, 0, ppp->mru,
+ READ_ONCE(ppp->debug))) {
ppp->rstate |= SC_DECOMP_RUN;
ppp->rstate &= ~(SC_DC_ERROR | SC_DC_FERROR);
}
@@ -3169,7 +3175,8 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
if (!ppp->xc_state)
break;
if (ppp->xcomp->comp_init(ppp->xc_state, dp, len,
- ppp->file.index, 0, ppp->debug))
+ ppp->file.index, 0,
+ READ_ONCE(ppp->debug)))
ppp->xstate |= SC_COMP_RUN;
}
break;
@@ -3321,8 +3328,8 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st)
st->p.ppp_opackets += tx_packets;
st->p.ppp_obytes += tx_bytes;
}
- st->p.ppp_ierrors = ppp->dev->stats.rx_errors;
- st->p.ppp_oerrors = ppp->dev->stats.tx_errors;
+ st->p.ppp_ierrors = DEV_STATS_READ(ppp->dev, rx_errors);
+ st->p.ppp_oerrors = DEV_STATS_READ(ppp->dev, tx_errors);
if (!vj)
return;
st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed;
@@ -3562,6 +3569,18 @@ ppp_disconnect_channel(struct channel *pch)
return err;
}

+/* Purge after the grace period: a late ppp_input() may still queue an
+ * skb on pch->file.rq before the last RCU reader drains.
+ */
+static void ppp_release_channel_free(struct rcu_head *rcu)
+{
+ struct channel *pch = container_of(rcu, struct channel, rcu);
+
+ skb_queue_purge(&pch->file.xq);
+ skb_queue_purge(&pch->file.rq);
+ kfree(pch);
+}
+
/*
* Drop a reference to a ppp channel and free its memory if the refcount reaches
* zero.
@@ -3581,9 +3600,7 @@ static void ppp_release_channel(struct channel *pch)
pr_err("ppp: destroying undead channel %p !\n", pch);
return;
}
- skb_queue_purge(&pch->file.xq);
- skb_queue_purge(&pch->file.rq);
- kfree(pch);
+ call_rcu(&pch->rcu, ppp_release_channel_free);
}

static void __exit ppp_cleanup(void)
@@ -3596,6 +3613,7 @@ static void __exit ppp_cleanup(void)
device_destroy(&ppp_class, MKDEV(PPP_MAJOR, 0));
class_unregister(&ppp_class);
unregister_pernet_device(&ppp_net_ops);
+ rcu_barrier(); /* wait for RCU callbacks before module unload */
}

/*
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index bdd61c504a1c..45df7a89c5a0 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -824,6 +824,7 @@ static int pppoe_sendmsg(struct socket *sock, struct msghdr *m,
dev_hard_header(skb, dev, ETH_P_PPP_SES,
po->pppoe_pa.remote, NULL, total_len);

+ ph = pppoe_hdr(skb);
memcpy(ph, &hdr, sizeof(struct pppoe_hdr));

ph->length = htons(total_len);
diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c
index 820e1a8fc956..faae711cf793 100644
--- a/drivers/net/slip/slip.c
+++ b/drivers/net/slip/slip.c
@@ -693,6 +693,8 @@ static void slip_receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp,
if (!sl || sl->magic != SLIP_MAGIC || !netif_running(sl->dev))
return;

+ spin_lock_bh(&sl->lock);
+
/* Read the characters out of the buffer */
while (count--) {
if (fp && *fp++) {
@@ -708,6 +710,8 @@ static void slip_receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp,
#endif
slip_unesc(sl, *cp++);
}
+
+ spin_unlock_bh(&sl->lock);
}

/************************************
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index 40522afc0532..f8df83f9965d 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -1530,7 +1530,11 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb,
struct ipv6hdr *ipv6;
struct tcphdr *tcp;
} hdr;
- BUG_ON(gdesc->rcd.tcp == 0);
+
+ /* v4/v6/tcp then describe the inner header, which we can't locate. */
+ if ((le32_to_cpu(gdesc->dword[0]) & (1UL << VMXNET3_RCD_HDR_INNER_SHIFT)) ||
+ gdesc->rcd.tcp == 0)
+ return 0;

maplen = skb_headlen(skb);
if (unlikely(sizeof(struct iphdr) + sizeof(struct tcphdr) > maplen))
@@ -1544,15 +1548,21 @@ vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb,

hdr.eth = eth_hdr(skb);
if (gdesc->rcd.v4) {
- BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP) &&
- hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP));
+ if (hdr.eth->h_proto != htons(ETH_P_IP) &&
+ hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IP))
+ return 0;
+
hdr.ptr += hlen;
- BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP);
+ if (hdr.ipv4->protocol != IPPROTO_TCP)
+ return 0;
+
hlen = hdr.ipv4->ihl << 2;
hdr.ptr += hdr.ipv4->ihl << 2;
} else if (gdesc->rcd.v6) {
- BUG_ON(hdr.eth->h_proto != htons(ETH_P_IPV6) &&
- hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IPV6));
+ if (hdr.eth->h_proto != htons(ETH_P_IPV6) &&
+ hdr.veth->h_vlan_encapsulated_proto != htons(ETH_P_IPV6))
+ return 0;
+
hdr.ptr += hlen;
/* Use an estimated value, since we also need to handle
* TSO case.
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index dae82a4d6027..36c61f4f9b4d 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -4422,6 +4422,9 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
struct vxlan_rdst *dst;
int err;

+ if (!rtnl_dev_link_net_capable(dev, vxlan->net))
+ return -EPERM;
+
dst = &vxlan->default_dst;
err = vxlan_nl2conf(tb, data, dev, &conf, true, extack);
if (err)
diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c
index 055a4969f593..af7a0d7f95a5 100644
--- a/drivers/net/vxlan/vxlan_mdb.c
+++ b/drivers/net/vxlan/vxlan_mdb.c
@@ -42,6 +42,7 @@ struct vxlan_mdb_remote {
};

#define VXLAN_SGRP_F_DELETE BIT(0)
+#define VXLAN_SGRP_F_NEW BIT(1)

struct vxlan_mdb_src_entry {
struct hlist_node node;
@@ -844,6 +845,7 @@ vxlan_mdb_remote_src_add(const struct vxlan_mdb_config *cfg,
ent = vxlan_mdb_remote_src_entry_add(remote, &src->addr);
if (!ent)
return -ENOMEM;
+ ent->flags |= VXLAN_SGRP_F_NEW;
} else if (!(cfg->nlflags & NLM_F_REPLACE)) {
NL_SET_ERR_MSG_MOD(extack, "Source entry already exists");
return -EEXIST;
@@ -853,15 +855,16 @@ vxlan_mdb_remote_src_add(const struct vxlan_mdb_config *cfg,
if (err)
goto err_src_del;

- /* Clear flags in case source entry was marked for deletion as part of
- * replace flow.
+ /* Clear the deletion mark so the entry survives the replace sweep.
+ * The new mark is retained until the whole operation succeeds.
*/
- ent->flags = 0;
+ ent->flags &= ~VXLAN_SGRP_F_DELETE;

return 0;

err_src_del:
- vxlan_mdb_remote_src_entry_del(ent);
+ if (ent->flags & VXLAN_SGRP_F_NEW)
+ vxlan_mdb_remote_src_entry_del(ent);
return err;
}

@@ -889,11 +892,19 @@ static int vxlan_mdb_remote_srcs_add(const struct vxlan_mdb_config *cfg,
goto err_src_del;
}

+ hlist_for_each_entry(ent, &remote->src_list, node)
+ ent->flags &= ~VXLAN_SGRP_F_NEW;
+
return 0;

err_src_del:
- hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node)
- vxlan_mdb_remote_src_del(cfg->vxlan, &cfg->group, remote, ent);
+ hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) {
+ if (ent->flags & VXLAN_SGRP_F_NEW)
+ vxlan_mdb_remote_src_del(cfg->vxlan, &cfg->group, remote,
+ ent);
+ else
+ ent->flags &= ~VXLAN_SGRP_F_DELETE;
+ }
return err;
}

@@ -1069,7 +1080,7 @@ vxlan_mdb_remote_srcs_replace(const struct vxlan_mdb_config *cfg,

err = vxlan_mdb_remote_srcs_add(cfg, remote, extack);
if (err)
- goto err_clear_delete;
+ return err;

hlist_for_each_entry_safe(ent, tmp, &remote->src_list, node) {
if (ent->flags & VXLAN_SGRP_F_DELETE)
@@ -1078,11 +1089,6 @@ vxlan_mdb_remote_srcs_replace(const struct vxlan_mdb_config *cfg,
}

return 0;
-
-err_clear_delete:
- hlist_for_each_entry(ent, &remote->src_list, node)
- ent->flags &= ~VXLAN_SGRP_F_DELETE;
- return err;
}

static int vxlan_mdb_remote_replace(const struct vxlan_mdb_config *cfg,
diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c
index 3f7707118450..8790251feb7d 100644
--- a/drivers/net/wan/wanxl.c
+++ b/drivers/net/wan/wanxl.c
@@ -514,7 +514,8 @@ static void wanxl_pci_remove_one(struct pci_dev *pdev)
if (card->irq)
free_irq(card->irq, card);

- wanxl_reset(card);
+ if (card->plx)
+ wanxl_reset(card);

for (i = 0; i < RX_QUEUE_LENGTH; i++)
if (card->rx_skbs[i]) {
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 25ab945fecef..8f88ea116a32 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -707,6 +707,7 @@ static int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt,
if (!(__le32_to_cpu(rxd_attention->flags) &
RX_ATTENTION_FLAGS_MSDU_DONE)) {
ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n");
+ __skb_queue_purge(list);
return -EIO;
}
}
@@ -771,6 +772,7 @@ static int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt,
if (!(__le32_to_cpu(rxd_attention->flags) &
RX_ATTENTION_FLAGS_MSDU_DONE)) {
ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n");
+ __skb_queue_purge(list);
return -EIO;
}
}
diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c
index 08d3a0c8f105..8a08275db40a 100644
--- a/drivers/net/wireless/ath/ath11k/ahb.c
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
@@ -996,6 +996,7 @@ static int ath11k_ahb_fw_resources_init(struct ath11k_base *ab)
ret = ath11k_ahb_setup_msa_resources(ab);
if (ret) {
ath11k_err(ab, "failed to setup msa resources\n");
+ of_node_put(node);
return ret;
}

diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 2a413e3a07a7..c9f520c2a935 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -4565,6 +4565,9 @@ static void ath11k_hal_rx_msdu_list_get(struct ath11k *ar,
msdu_details = &msdu_link->msdu_link[0];

for (i = 0; i < HAL_RX_NUM_MSDU_DESC; i++) {
+ if (!i && FIELD_GET(BUFFER_ADDR_INFO0_ADDR,
+ msdu_details[i].buf_addr_info.info0) == 0)
+ break;
if (FIELD_GET(BUFFER_ADDR_INFO0_ADDR,
msdu_details[i].buf_addr_info.info0) == 0) {
msdu_desc_info = &msdu_details[i - 1].rx_msdu_info;
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index 35bb9e7a63a2..a163168f3617 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -199,6 +199,8 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab)
val |= PCIE_SOC_GLOBAL_RESET_V;

ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
+ /* Flush the posted write to the device */
+ ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET);

/* TODO: exact time to sleep is uncertain */
delay = 10;
@@ -208,6 +210,8 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab)
val &= ~PCIE_SOC_GLOBAL_RESET_V;

ath11k_pcic_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
+ /* Flush the posted write to the device */
+ ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET);

mdelay(delay);

diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index feebbc30f3df..a304feca7042 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
@@ -3295,9 +3295,14 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work)
clear_bit(ATH11K_FLAG_CRASH_FLUSH,
&ab->dev_flags);
clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags);
- ath11k_core_qmi_firmware_ready(ab);
- set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags);
-
+ if (!test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) {
+ ret = ath11k_core_qmi_firmware_ready(ab);
+ if (ret) {
+ set_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags);
+ break;
+ }
+ set_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags);
+ }
break;
case ATH11K_QMI_EVENT_COLD_BOOT_CAL_DONE:
break;
diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c
index a1100782d45e..185ef0dc8d4f 100644
--- a/drivers/net/wireless/ath/ath12k/dp_peer.c
+++ b/drivers/net/wireless/ath/ath12k/dp_peer.c
@@ -274,11 +274,14 @@ int ath12k_dp_link_peer_rhash_tbl_init(struct ath12k_dp *dp)

void ath12k_dp_link_peer_rhash_tbl_destroy(struct ath12k_dp *dp)
{
- mutex_lock(&dp->link_peer_rhash_tbl_lock);
+ guard(mutex)(&dp->link_peer_rhash_tbl_lock);
+
+ if (!dp->rhead_peer_addr)
+ return;
+
rhashtable_destroy(dp->rhead_peer_addr);
kfree(dp->rhead_peer_addr);
dp->rhead_peer_addr = NULL;
- mutex_unlock(&dp->link_peer_rhash_tbl_lock);
}

static int ath12k_dp_link_peer_rhash_insert(struct ath12k_dp *dp,
@@ -570,6 +573,8 @@ int ath12k_dp_link_peer_assign(struct ath12k_dp *dp, struct ath12k_dp_hw *dp_hw,
peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id);

rcu_assign_pointer(dp_peer->link_peers[peer->link_id], peer);
+ WRITE_ONCE(dp_peer->link_peers_map,
+ READ_ONCE(dp_peer->link_peers_map) | BIT(peer->link_id));

rcu_assign_pointer(dp_hw->dp_peers[peerid_index], dp_peer);

@@ -632,6 +637,8 @@ void ath12k_dp_link_peer_unassign(struct ath12k_dp *dp, struct ath12k_dp_hw *dp_
peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id);

rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL);
+ WRITE_ONCE(dp_peer->link_peers_map,
+ READ_ONCE(dp_peer->link_peers_map) & ~BIT(peer->link_id));

rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL);

diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.h b/drivers/net/wireless/ath/ath12k/dp_peer.h
index 113b8040010f..d4d2ff16e836 100644
--- a/drivers/net/wireless/ath/ath12k/dp_peer.h
+++ b/drivers/net/wireless/ath/ath12k/dp_peer.h
@@ -140,6 +140,7 @@ struct ath12k_dp_peer {

/* Info used in MMIC verification of * RX fragments */
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
+ unsigned long link_peers_map;
struct ath12k_dp_link_peer __rcu *link_peers[ATH12K_NUM_MAX_LINKS];
struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1];
struct ath12k_dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1];
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
index b108ccd0f637..b5dba7c0155f 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.c
@@ -1344,10 +1344,9 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc

pubsta = peer ? peer->sta : NULL;

- if (pubsta && pubsta->valid_links) {
- status->link_valid = 1;
- status->link_id = peer->hw_links[rxcb->hw_link_id];
- }
+ status->link_valid = 0;
+ if (pubsta && pubsta->valid_links)
+ ath12k_hw_set_rx_link_id(dp->hw_params, peer, rxcb, status);

ath12k_dbg(dp->ab, ATH12K_DBG_DATA,
"rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h
index a9888e0521a1..da75d19ae1a0 100644
--- a/drivers/net/wireless/ath/ath12k/hw.h
+++ b/drivers/net/wireless/ath/ath12k/hw.h
@@ -13,6 +13,10 @@
#include "wmi.h"
#include "hal.h"

+struct ath12k_dp_peer;
+struct ath12k_skb_rxcb;
+struct ieee80211_rx_status;
+
/* Target configuration defines */

/* Num VDEVS per radio */
@@ -224,6 +228,9 @@ struct ath12k_hw_ops {
bool (*dp_srng_is_tx_comp_ring)(int ring_num);
bool (*is_frame_link_agnostic)(struct ath12k_link_vif *arvif,
struct ieee80211_mgmt *mgmt);
+ void (*set_rx_link_id)(struct ath12k_dp_peer *dp_peer,
+ struct ath12k_skb_rxcb *rxcb,
+ struct ieee80211_rx_status *status);
};

static inline
@@ -254,6 +261,15 @@ static inline int ath12k_hw_mac_id_to_srng_id(const struct ath12k_hw_params *hw,
return 0;
}

+static inline void ath12k_hw_set_rx_link_id(const struct ath12k_hw_params *hw,
+ struct ath12k_dp_peer *dp_peer,
+ struct ath12k_skb_rxcb *rxcb,
+ struct ieee80211_rx_status *status)
+{
+ if (hw->hw_ops->set_rx_link_id)
+ hw->hw_ops->set_rx_link_id(dp_peer, rxcb, status);
+}
+
struct ath12k_fw_ie {
__le32 id;
__le32 len;
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 7190aafd3ae6..7408c2577dc2 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -1234,9 +1234,13 @@ void ath12k_mac_peer_cleanup_all(struct ath12k *ar)
/* cleanup dp peer */
spin_lock_bh(&dp_hw->peer_lock);
dp_peer = peer->dp_peer;
- peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id);
- rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL);
- rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL);
+ if (dp_peer) {
+ peerid_index = ath12k_dp_peer_get_peerid_index(dp, peer->peer_id);
+ rcu_assign_pointer(dp_peer->link_peers[peer->link_id], NULL);
+ WRITE_ONCE(dp_peer->link_peers_map,
+ READ_ONCE(dp_peer->link_peers_map) & ~BIT(peer->link_id));
+ rcu_assign_pointer(dp_hw->dp_peers[peerid_index], NULL);
+ }
spin_unlock_bh(&dp_hw->peer_lock);

ath12k_dp_link_peer_rhash_delete(dp, peer);
diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c
index d9a22d6afbb0..fee4129ea405 100644
--- a/drivers/net/wireless/ath/ath12k/pci.c
+++ b/drivers/net/wireless/ath/ath12k/pci.c
@@ -188,6 +188,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab)
val |= PCIE_SOC_GLOBAL_RESET_V;

ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
+ /* Flush the posted write to the device */
+ ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET);

/* TODO: exact time to sleep is uncertain */
delay = 10;
@@ -197,6 +199,8 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab)
val &= ~PCIE_SOC_GLOBAL_RESET_V;

ath12k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
+ /* Flush the posted write to the device */
+ ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET);

mdelay(delay);

diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c
index 2e875176baaa..80fee2ce68f1 100644
--- a/drivers/net/wireless/ath/ath12k/peer.c
+++ b/drivers/net/wireless/ath/ath12k/peer.c
@@ -444,6 +444,9 @@ int ath12k_link_sta_rhash_tbl_init(struct ath12k_base *ab)

void ath12k_link_sta_rhash_tbl_destroy(struct ath12k_base *ab)
{
+ if (!ab->rhead_sta_addr)
+ return;
+
rhashtable_destroy(ab->rhead_sta_addr);
kfree(ab->rhead_sta_addr);
ab->rhead_sta_addr = NULL;
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
index a5e290edaa89..99c565283469 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c
@@ -5,6 +5,7 @@
*/

#include "dp_rx.h"
+#include "../dp_peer.h"
#include "../dp_tx.h"
#include "../peer.h"
#include "hal_qcn9274.h"
@@ -2242,3 +2243,35 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,

return tlv_tag == HAL_RX_MPDU_START;
}
+
+void
+ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,
+ struct ath12k_skb_rxcb *rxcb,
+ struct ieee80211_rx_status *status)
+{
+ status->link_valid = 1;
+ status->link_id = dp_peer->hw_links[rxcb->hw_link_id];
+}
+
+void
+ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
+ struct ath12k_skb_rxcb *rxcb,
+ struct ieee80211_rx_status *status)
+{
+ struct ath12k_dp_link_peer *link_peer;
+ unsigned long links_map;
+ int i;
+
+ RCU_LOCKDEP_WARN(!rcu_read_lock_held(),
+ "ath12k set rx link id called without rcu lock");
+
+ links_map = READ_ONCE(dp_peer->link_peers_map);
+ for_each_set_bit(i, &links_map, ATH12K_NUM_MAX_LINKS) {
+ link_peer = rcu_dereference(dp_peer->link_peers[i]);
+ if (link_peer && link_peer->peer_id == rxcb->peer_id) {
+ status->link_valid = 1;
+ status->link_id = link_peer->link_id;
+ return;
+ }
+ }
+}
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h
index 8aa79faf567f..1d3a4788a2dd 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h
+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h
@@ -57,4 +57,10 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab,
struct hal_rx_desc *rx_desc);
int ath12k_wifi7_dp_rx_tid_delete_handler(struct ath12k_base *ab,
struct ath12k_dp_rx_tid_rxq *rx_tid);
+void ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer,
+ struct ath12k_skb_rxcb *rxcb,
+ struct ieee80211_rx_status *status);
+void ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer,
+ struct ath12k_skb_rxcb *rxcb,
+ struct ieee80211_rx_status *status);
#endif
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
index cb3185850439..f687eb69ea8d 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
@@ -158,6 +158,7 @@ static const struct ath12k_hw_ops qcn9274_ops = {
.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_qcn9274,
.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_qcn9274,
.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_qcn9274,
+ .set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_qcn9274,
};

static const struct ath12k_hw_ops wcn7850_ops = {
@@ -168,6 +169,7 @@ static const struct ath12k_hw_ops wcn7850_ops = {
.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850,
.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850,
.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850,
+ .set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850,
};

static const struct ath12k_hw_ops qcc2072_ops = {
@@ -178,6 +180,7 @@ static const struct ath12k_hw_ops qcc2072_ops = {
.get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850,
.dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850,
.is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850,
+ .set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850,
};

#define ATH12K_TX_RING_MASK_0 0x1
diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c
index 97fdac7237e2..d81825413906 100644
--- a/drivers/net/wireless/ath/ath6kl/txrx.c
+++ b/drivers/net/wireless/ath/ath6kl/txrx.c
@@ -1723,13 +1723,15 @@ void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid_mux, u16 seq_no,

rxtid = &aggr_conn->rx_tid[tid];

- if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX)
- ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n",
- __func__, win_sz, tid);
-
if (rxtid->aggr)
aggr_delete_tid_state(aggr_conn, tid);

+ if (win_sz < AGGR_WIN_SZ_MIN || win_sz > AGGR_WIN_SZ_MAX) {
+ ath6kl_dbg(ATH6KL_DBG_WLAN_RX, "%s: win_sz %d, tid %d\n",
+ __func__, win_sz, tid);
+ return;
+ }
+
rxtid->seq_next = seq_no;
hold_q_size = TID_WINDOW_SZ(win_sz) * sizeof(struct skb_hold_q);
rxtid->hold_q = kzalloc(hold_q_size, GFP_KERNEL);
@@ -1828,7 +1830,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn)
return;

if (aggr_conn->timer_scheduled) {
- timer_delete(&aggr_conn->timer);
+ timer_delete_sync(&aggr_conn->timer);
aggr_conn->timer_scheduled = false;
}

diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index 72611a2ceb9d..2b0c5038ae04 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -484,6 +484,18 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len)

evt = (struct wmi_tx_complete_event *) datap;

+ if (len < sizeof(*evt)) {
+ ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d\n",
+ len);
+ return -EINVAL;
+ }
+
+ if (len < sizeof(*evt) + evt->num_msg * sizeof(struct tx_complete_msg_v1)) {
+ ath6kl_dbg(ATH6KL_DBG_WMI, "tx complete: invalid len %d for %u msgs\n",
+ len, evt->num_msg);
+ return -EINVAL;
+ }
+
ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n",
evt->num_msg, evt->msg_len, evt->msg_type);

@@ -862,6 +874,14 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len,

ev = (struct wmi_connect_event *) datap;

+ if (len < sizeof(*ev) + ev->beacon_ie_len +
+ ev->assoc_req_len + ev->assoc_resp_len) {
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "connect event: IE lengths %u+%u+%u exceed buffer %d\n",
+ ev->beacon_ie_len, ev->assoc_req_len,
+ ev->assoc_resp_len, len);
+ return -EINVAL;
+ }
if (vif->nw_type == AP_NETWORK) {
/* AP mode start/STA connected event */
struct net_device *dev = vif->ndev;
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 821909b81ea9..44855ec5be74 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -1225,15 +1225,10 @@ static int ath9k_hif_request_firmware(struct hif_device_usb *hif_dev,
ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name,
&hif_dev->udev->dev, GFP_KERNEL,
hif_dev, ath9k_hif_usb_firmware_cb);
- if (ret) {
+ if (ret)
dev_err(&hif_dev->udev->dev,
"ath9k_htc: Async request for firmware %s failed\n",
hif_dev->fw_name);
- return ret;
- }
-
- dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n",
- hif_dev->fw_name);

return ret;
}
diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
index 6833430130f4..0383d5c9698b 100644
--- a/drivers/net/wireless/ath/carl9170/rx.c
+++ b/drivers/net/wireless/ath/carl9170/rx.c
@@ -150,7 +150,8 @@ static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer)
spin_lock(&ar->cmd_lock);
if (ar->readbuf) {
if (len >= 4)
- memcpy(ar->readbuf, buffer + 4, len - 4);
+ memcpy(ar->readbuf, buffer + 4,
+ min_t(u32, len - 4, ar->readlen));

ar->readbuf = NULL;
}
@@ -917,7 +918,9 @@ static void carl9170_rx_stream(struct ar9170 *ar, void *buf, unsigned int len)
}
}

- skb_put_data(ar->rx_failover, tbuf, tlen);
+ skb_put_data(ar->rx_failover, tbuf,
+ min_t(unsigned int, tlen,
+ ar->rx_failover_missing));
ar->rx_failover_missing -= tlen;

if (ar->rx_failover_missing <= 0) {
diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c
index 59caf1e4b158..06aaf281655b 100644
--- a/drivers/net/wireless/ath/carl9170/tx.c
+++ b/drivers/net/wireless/ath/carl9170/tx.c
@@ -692,7 +692,7 @@ void carl9170_tx_process_status(struct ar9170 *ar,
unsigned int i;

for (i = 0; i < cmd->hdr.ext; i++) {
- if (WARN_ON(i > ((cmd->hdr.len / 2) + 1))) {
+ if (WARN_ON(i >= (cmd->hdr.len / 2))) {
print_hex_dump_bytes("UU:", DUMP_PREFIX_NONE,
(void *) cmd, cmd->hdr.len + 4);
break;
diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c
index 32e3e09e7680..d9c2809be4ba 100644
--- a/drivers/net/wireless/atmel/at76c50x-usb.c
+++ b/drivers/net/wireless/atmel/at76c50x-usb.c
@@ -1521,13 +1521,16 @@ static inline int at76_guess_freq(struct at76_priv *priv)

if (ieee80211_is_probe_resp(hdr->frame_control)) {
el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
- el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable;
} else if (ieee80211_is_beacon(hdr->frame_control)) {
el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable);
- el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable;
} else {
goto exit;
}
+
+ if (len < el_off)
+ goto exit;
+
+ el = priv->rx_skb->data + el_off;
len -= el_off;

el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
index d24b80e492e0..869c4872d399 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
@@ -911,6 +911,7 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
return ret;
}
switch (sdiodev->func2->device) {
+ case SDIO_DEVICE_ID_BROADCOM_43752:
case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373:
f2_blksz = SDIO_4373_FUNC2_BLOCKSIZE;
break;
@@ -1069,6 +1070,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
bus_if = kzalloc_obj(*bus_if);
if (!bus_if)
return -ENOMEM;
+ mutex_init(&bus_if->bus_reset_lock);
sdiodev = kzalloc_obj(*sdiodev);
if (!sdiodev) {
kfree(bus_if);
@@ -1130,6 +1132,14 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func)
if (func->num != 1)
return;

+ /* Drain bus_reset before the shared brcmf_sdiod_remove()
+ * teardown, which the SDIO reset callback also reaches. The
+ * data worker can arm bus_reset via brcmf_fw_crashed(); cancel
+ * it first.
+ */
+ brcmf_sdio_cancel_datawork(sdiodev->bus);
+ brcmf_bus_cancel_reset_work(bus_if);
+
/* only proceed with rest of cleanup if func 1 */
brcmf_sdiod_remove(sdiodev);

@@ -1204,6 +1214,8 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
} else {
/* power will be cut so remove device, probe again in resume */
brcmf_sdiod_intr_unregister(sdiodev);
+ brcmf_sdio_cancel_datawork(sdiodev->bus);
+ brcmf_bus_cancel_reset_work(bus_if);
ret = brcmf_sdiod_remove(sdiodev);
if (ret)
brcmf_err("Failed to remove device on suspend\n");
@@ -1229,6 +1241,8 @@ static int brcmf_ops_sdio_resume(struct device *dev)
ret = brcmf_sdiod_probe(sdiodev);
if (ret)
brcmf_err("Failed to probe device on resume\n");
+ else
+ brcmf_bus_allow_reset_work(bus_if);
} else {
if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported)
disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
index fe31051a9e11..9371c1489948 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/firmware.h>
#include <linux/device.h>
+#include <linux/mutex.h>
#include "debug.h"

/* IDs of the 6 default common rings of msgbuf protocol */
@@ -179,6 +180,8 @@ struct brcmf_bus {
enum brcmf_fwvendor fwvid;
bool always_use_fws_queue;
bool wowl_supported;
+ bool removing; /* device removal in progress; quiesce async work */
+ struct mutex bus_reset_lock;

const struct brcmf_bus_ops *ops;
struct brcmf_bus_msgbuf *msgbuf;
@@ -186,6 +189,9 @@ struct brcmf_bus {
struct list_head list;
};

+void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if);
+void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if);
+
/*
* callback wrappers
*/
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 0b55d445895f..89f61710a210 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -2146,7 +2146,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
sme->crypto.akm_suites[0]);
return -EINVAL;
}
- } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
+ } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_1X_SHA256)) {
switch (sme->crypto.akm_suites[0]) {
case WLAN_AKM_SUITE_8021X:
val = WPA2_AUTH_UNSPECIFIED;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index ec170647800d..dad6f4563d14 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -1167,6 +1167,35 @@ static int brcmf_revinfo_read(struct seq_file *s, void *data)
return 0;
}

+/*
+ * Serialize arming from debugfs reset and brcmf_fw_crashed() against
+ * teardown. The remove path sets ->removing and drains the work while
+ * holding bus_reset_lock, so a racing armer is either drained or skips it.
+ */
+static void brcmf_bus_schedule_reset(struct brcmf_bus *bus_if)
+{
+ mutex_lock(&bus_if->bus_reset_lock);
+ if (bus_if->drvr && bus_if->drvr->bus_reset.func && !bus_if->removing)
+ schedule_work(&bus_if->drvr->bus_reset);
+ mutex_unlock(&bus_if->bus_reset_lock);
+}
+
+void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if)
+{
+ mutex_lock(&bus_if->bus_reset_lock);
+ bus_if->removing = true;
+ if (bus_if->drvr)
+ cancel_work_sync(&bus_if->drvr->bus_reset);
+ mutex_unlock(&bus_if->bus_reset_lock);
+}
+
+void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if)
+{
+ mutex_lock(&bus_if->bus_reset_lock);
+ bus_if->removing = false;
+ mutex_unlock(&bus_if->bus_reset_lock);
+}
+
static void brcmf_core_bus_reset(struct work_struct *work)
{
struct brcmf_pub *drvr = container_of(work, struct brcmf_pub,
@@ -1187,7 +1216,7 @@ static ssize_t bus_reset_write(struct file *file, const char __user *user_buf,
if (value != 1)
return -EINVAL;

- schedule_work(&drvr->bus_reset);
+ brcmf_bus_schedule_reset(drvr->bus_if);

return count;
}
@@ -1417,14 +1446,23 @@ void brcmf_dev_coredump(struct device *dev)
void brcmf_fw_crashed(struct device *dev)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_pub *drvr;
+
+ /* May fire before brcmf_attach() wires up drvr, or after removal
+ * has cleared it; guard the derefs below (and the arming gate in
+ * brcmf_bus_schedule_reset() already checks drvr/->removing).
+ */
+ if (!bus_if)
+ return;
+ drvr = bus_if->drvr;
+ if (!drvr)
+ return;

bphy_err(drvr, "Firmware has halted or crashed\n");

brcmf_dev_coredump(dev);

- if (drvr->bus_reset.func)
- schedule_work(&drvr->bus_reset);
+ brcmf_bus_schedule_reset(bus_if);
}

void brcmf_detach(struct device *dev)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index 45b342ea0637..9a0bdc89a536 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -1383,16 +1383,20 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo)
static void
brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo)
{
- if (devinfo->shared.scratch)
+ if (devinfo->shared.scratch) {
dma_free_coherent(&devinfo->pdev->dev,
BRCMF_DMA_D2H_SCRATCH_BUF_LEN,
devinfo->shared.scratch,
devinfo->shared.scratch_dmahandle);
- if (devinfo->shared.ringupd)
+ devinfo->shared.scratch = NULL;
+ }
+ if (devinfo->shared.ringupd) {
dma_free_coherent(&devinfo->pdev->dev,
BRCMF_DMA_D2H_RINGUPD_BUF_LEN,
devinfo->shared.ringupd,
devinfo->shared.ringupd_dmahandle);
+ devinfo->shared.ringupd = NULL;
+ }
}

static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo)
@@ -2499,6 +2503,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ret = -ENOMEM;
goto fail;
}
+ mutex_init(&bus->bus_reset_lock);
bus->msgbuf = kzalloc_obj(*bus->msgbuf);
if (!bus->msgbuf) {
ret = -ENOMEM;
@@ -2594,6 +2599,11 @@ brcmf_pcie_remove(struct pci_dev *pdev)
if (devinfo->ci)
brcmf_pcie_intr_disable(devinfo);

+ if (devinfo->irq_allocated)
+ synchronize_irq(pdev->irq);
+
+ brcmf_bus_cancel_reset_work(bus);
+
brcmf_detach(&pdev->dev);
brcmf_free(&pdev->dev);

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 8fb595733b9c..9f7ed1d293a0 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -4465,6 +4465,7 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
bus->sdiodev = sdiodev;
sdiodev->bus = bus;
skb_queue_head_init(&bus->glom);
+ INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
bus->txbound = BRCMF_TXBOUND;
bus->rxbound = BRCMF_RXBOUND;
bus->txminmax = BRCMF_TXMINMAX;
@@ -4479,7 +4480,6 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
goto fail;
}
brcmf_sdiod_freezer_count(sdiodev);
- INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
bus->brcmf_wq = wq;

/* attempt to attach to the dongle */
@@ -4560,6 +4560,12 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
return ret;
}

+void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus)
+{
+ if (bus)
+ cancel_work_sync(&bus->datawork);
+}
+
/* Detach and free everything */
void brcmf_sdio_remove(struct brcmf_sdio *bus)
{
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
index 80180d5c6c87..b93d153a8963 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
@@ -361,6 +361,7 @@ int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev);
int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
void brcmf_sdio_remove(struct brcmf_sdio *bus);
void brcmf_sdio_isr(struct brcmf_sdio *bus, bool in_isr);
+void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus);

void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active);
void brcmf_sdio_wowl_config(struct device *dev, bool enabled);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
index 0b52f968b907..b41949a9bdc8 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
@@ -1260,6 +1260,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo,
ret = -ENOMEM;
goto fail;
}
+ mutex_init(&bus->bus_reset_lock);

bus->dev = dev;
bus_pub->bus = bus;
@@ -1329,6 +1330,8 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo)
return;
brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo);

+ brcmf_bus_cancel_reset_work(devinfo->bus_pub.bus);
+
brcmf_detach(devinfo->dev);
brcmf_free(devinfo->dev);
kfree(devinfo->bus_pub.bus);
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
index c11428485dcc..2b8a23865bfb 100644
--- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
@@ -6157,6 +6157,8 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
if (err) {
printk(KERN_WARNING DRV_NAME
"Error calling pci_enable_device.\n");
+ free_libipw(dev, 0);
+ pci_iounmap(pci_dev, ioaddr);
return err;
}

@@ -6169,16 +6171,14 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
if (err) {
printk(KERN_WARNING DRV_NAME
"Error calling pci_set_dma_mask.\n");
- pci_disable_device(pci_dev);
- return err;
+ goto fail;
}

err = pci_request_regions(pci_dev, DRV_NAME);
if (err) {
printk(KERN_WARNING DRV_NAME
"Error calling pci_request_regions.\n");
- pci_disable_device(pci_dev);
- return err;
+ goto fail;
}

/* We disable the RETRY_TIMEOUT register (0x41) to keep
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c
index afff8d51ca95..ec0ff58ab312 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright(c) 2020-2025 Intel Corporation
+ * Copyright(c) 2020-2026 Intel Corporation
*/

#include "iwl-drv.h"
@@ -12,6 +12,7 @@
#include "fw/api/alive.h"
#include "fw/uefi.h"
#include "fw/img.h"
+#include "fw/dbg.h"

#define IWL_PNVM_REDUCED_CAP_BIT BIT(25)

@@ -26,6 +27,12 @@ static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait,
struct iwl_trans *trans = (struct iwl_trans *)data;
struct iwl_pnvm_init_complete_ntfy *pnvm_ntf = (void *)pkt->data;

+ if (IWL_FW_CHECK(trans,
+ iwl_rx_packet_payload_len(pkt) < sizeof(*pnvm_ntf),
+ "Bad notif len: %d\n",
+ iwl_rx_packet_payload_len(pkt)))
+ return true;
+
IWL_DEBUG_FW(trans,
"PNVM complete notification received with status 0x%0x\n",
le32_to_cpu(pnvm_ntf->status));
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
index 55128caac7ed..14c813cf530f 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
@@ -384,7 +384,7 @@ bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc)
if (*size >= IWL_WTAS_BLACK_LIST_MAX)
return false;

- list[*size++] = mcc;
+ list[(*size)++] = mcc;
return true;
}
IWL_EXPORT_SYMBOL(iwl_add_mcc_to_tas_block_list);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index 9a74f60c9185..72d7403de04d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -2756,7 +2756,7 @@ static int iwl_mvm_wowlan_store_wake_pkt(struct iwl_mvm *mvm,
struct iwl_wowlan_status_data *status,
u32 len)
{
- u32 data_size, packet_len = le32_to_cpu(notif->wake_packet_length);
+ u32 data_size, packet_len;

if (len < sizeof(*notif)) {
IWL_ERR(mvm, "Invalid WoWLAN wake packet notification!\n");
@@ -2775,6 +2775,7 @@ static int iwl_mvm_wowlan_store_wake_pkt(struct iwl_mvm *mvm,
return -EIO;
}

+ packet_len = le32_to_cpu(notif->wake_packet_length);
data_size = len - offsetof(struct iwl_wowlan_wake_pkt_notif, wake_packet);

/* data_size got the padding from the notification, remove it. */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index 6e507d6dcdd2..fa523be91d8a 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -964,12 +964,22 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
return ret;
}

+ if (IWL_FW_CHECK(mvm,
+ iwl_rx_packet_payload_len(cmd.resp_pkt) !=
+ sizeof(*resp),
+ "Wrong size for iwl_geo_tx_power_profiles_resp: %d\n",
+ iwl_rx_packet_payload_len(cmd.resp_pkt))) {
+ ret = -EIO;
+ goto out;
+ }
+
resp = (void *)cmd.resp_pkt->data;
ret = le32_to_cpu(resp->profile_idx);

if (WARN_ON(ret > BIOS_GEO_MAX_PROFILE_NUM))
ret = -EIO;

+out:
iwl_free_resp(&cmd);
return ret;
}
diff --git a/drivers/net/wireless/intersil/p54/txrx.c b/drivers/net/wireless/intersil/p54/txrx.c
index 1294a1d6528e..9f491334c8d0 100644
--- a/drivers/net/wireless/intersil/p54/txrx.c
+++ b/drivers/net/wireless/intersil/p54/txrx.c
@@ -499,11 +499,19 @@ static void p54_rx_eeprom_readback(struct p54_common *priv,
if (le16_to_cpu(eeprom->v2.len) != priv->eeprom_slice_size)
return;

+ if (eeprom->v2.data + priv->eeprom_slice_size >
+ skb_tail_pointer(skb))
+ return;
+
memcpy(priv->eeprom, eeprom->v2.data, priv->eeprom_slice_size);
} else {
if (le16_to_cpu(eeprom->v1.len) != priv->eeprom_slice_size)
return;

+ if (eeprom->v1.data + priv->eeprom_slice_size >
+ skb_tail_pointer(skb))
+ return;
+
memcpy(priv->eeprom, eeprom->v1.data, priv->eeprom_slice_size);
}

diff --git a/drivers/net/wireless/marvell/libertas/firmware.c b/drivers/net/wireless/marvell/libertas/firmware.c
index f124110944b7..9bf7d4c207b9 100644
--- a/drivers/net/wireless/marvell/libertas/firmware.c
+++ b/drivers/net/wireless/marvell/libertas/firmware.c
@@ -78,6 +78,7 @@ static void helper_firmware_cb(const struct firmware *firmware, void *context)
} else {
/* No main firmware needed for this helper --> success! */
lbs_fw_loaded(priv, 0, firmware, NULL);
+ release_firmware(firmware);
}
}

diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
index a6550548d3b4..9460d5352b23 100644
--- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
@@ -196,6 +196,7 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv)
struct mwifiex_adapter *adapter = priv->adapter;
char rgpower_table_name[30];
char country_code[3];
+ int ret;

strscpy(country_code, domain_info->country_code, sizeof(country_code));

@@ -214,16 +215,17 @@ static int mwifiex_request_rgpower_table(struct mwifiex_private *priv)
adapter->rgpower_data = NULL;
}

- if ((request_firmware(&adapter->rgpower_data, rgpower_table_name,
- adapter->dev))) {
+ ret = request_firmware_direct(&adapter->rgpower_data, rgpower_table_name,
+ adapter->dev);
+
+ if (ret) {
mwifiex_dbg(
adapter, INFO,
- "info: %s: failed to request regulatory power table\n",
- __func__);
- return -EIO;
+ "info: %s: failed to request regulatory power table: %d\n",
+ __func__, ret);
}

- return 0;
+ return ret;
}

static int mwifiex_dnld_rgpower_table(struct mwifiex_private *priv)
diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c
index 845f2a22e071..c71ffe8399e4 100644
--- a/drivers/net/wireless/marvell/mwifiex/tdls.c
+++ b/drivers/net/wireless/marvell/mwifiex/tdls.c
@@ -215,7 +215,7 @@ mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac,

/* follow AP's channel bandwidth */
if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) &&
- bss_desc->bcn_ht_cap &&
+ bss_desc->bcn_ht_oper &&
ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param))
ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param;

diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c
index 679fdae0f001..ba1bdbbff687 100644
--- a/drivers/net/wireless/marvell/mwifiex/uap_event.c
+++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c
@@ -123,11 +123,31 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
len = ETH_ALEN;

if (len != -1) {
+ u16 evt_len = le16_to_cpu(event->len);
+
sinfo->assoc_req_ies = &event->data[len];
len = (u8 *)sinfo->assoc_req_ies -
(u8 *)&event->frame_control;
- sinfo->assoc_req_ies_len =
- le16_to_cpu(event->len) - (u16)len;
+
+ /*
+ * event->len is reported by the device firmware
+ * and is not otherwise validated. Reject a
+ * length that underflows the header, or that
+ * would place the association request IEs
+ * outside the fixed-size event_body[] buffer the
+ * event was copied into; otherwise the IE walk
+ * in mwifiex_set_sta_ht_cap() reads past
+ * event_body and out of the adapter slab object.
+ */
+ if (evt_len < len ||
+ (u8 *)&event->frame_control + evt_len >
+ adapter->event_body + MAX_EVENT_SIZE) {
+ mwifiex_dbg(adapter, ERROR,
+ "invalid STA assoc event length\n");
+ kfree(sinfo);
+ return -1;
+ }
+ sinfo->assoc_req_ies_len = evt_len - (u16)len;
}
}
cfg80211_new_sta(priv->netdev->ieee80211_ptr, event->sta_addr,
diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig
index 502303622a53..2ca96e0527c0 100644
--- a/drivers/net/wireless/mediatek/mt76/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/Kconfig
@@ -38,8 +38,8 @@ config MT792x_USB
select MT76_USB

config MT76_NPU
- bool
- depends on MT76_CORE
+ tristate
+ depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU

source "drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig"
source "drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig"
diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile
index 1d42adfe8030..cacdd2b13d05 100644
--- a/drivers/net/wireless/mediatek/mt76/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/Makefile
@@ -12,7 +12,11 @@ mt76-y := \
mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \
tx.o agg-rx.o mcu.o wed.o scan.o channel.o

-mt76-$(CONFIG_MT76_NPU) += npu.o
+ifdef CONFIG_MT76_NPU
+# CONFIG_MT76_NPU is tristate to simplify dependency tracking,
+# but it behaves as a bool symbol here.
+mt76-y += npu.o
+endif
mt76-$(CONFIG_PCI) += pci.o
mt76-$(CONFIG_NL80211_TESTMODE) += testmode.o

diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 527bef97e122..942a7d3915d2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -1646,7 +1646,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state);
int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len);

-#ifdef CONFIG_MT76_NPU
+#if IS_ENABLED(CONFIG_MT76_NPU)
void mt76_npu_check_ppe(struct mt76_dev *dev, struct sk_buff *skb,
u32 info);
int mt76_npu_dma_add_buf(struct mt76_phy *phy, struct mt76_queue *q,
@@ -1735,12 +1735,12 @@ static inline int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex,

static inline bool mt76_npu_device_active(struct mt76_dev *dev)
{
- return !!rcu_access_pointer(dev->mmio.npu);
+ return mt76_is_mmio(dev) && !!rcu_access_pointer(dev->mmio.npu);
}

static inline bool mt76_ppe_device_active(struct mt76_dev *dev)
{
- return !!rcu_access_pointer(dev->mmio.ppe_dev);
+ return mt76_is_mmio(dev) && !!rcu_access_pointer(dev->mmio.ppe_dev);
}

static inline int mt76_npu_send_msg(struct airoha_npu *npu, int ifindex,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
index ce0051468501..aad232c5a6fa 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
@@ -1601,6 +1601,8 @@ bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len)

switch (type) {
case PKT_TYPE_TXRX_NOTIFY:
+ if (!mt76_is_mmio(mdev))
+ return false;
mt7615_mac_tx_free(dev, data, len);
return false;
case PKT_TYPE_TXS:
@@ -1634,6 +1636,10 @@ void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
dev_kfree_skb(skb);
break;
case PKT_TYPE_TXRX_NOTIFY:
+ if (!mt76_is_mmio(mdev)) {
+ dev_kfree_skb(skb);
+ break;
+ }
mt7615_mac_tx_free(dev, skb->data, skb->len);
dev_kfree_skb(skb);
break;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 89bd52ea8bf7..ca290ccb6fd6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -1446,6 +1446,8 @@ mt76_connac_mcu_uni_bss_he_tlv(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct bss_info_uni_he *he;

cap = mt76_connac_get_he_phy_cap(phy, vif);
+ if (!cap)
+ return;

he = (struct bss_info_uni_he *)tlv;
he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index 318c38149463..391c91675130 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -595,6 +595,8 @@ mt7915_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
struct tlv *tlv;

cap = mt76_connac_get_he_phy_cap(phy->mt76, vif);
+ if (!cap)
+ return;

tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HE_BASIC, sizeof(*he));

@@ -1177,13 +1179,12 @@ mt7915_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7915_phy *phy,
}

static void
-mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
- struct mt7915_phy *phy, struct sta_rec_bf *bf)
+mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta,
+ const struct ieee80211_sta_he_cap *vc,
+ struct sta_rec_bf *bf)
{
struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap;
struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
- const struct ieee80211_sta_he_cap *vc =
- mt76_connac_get_he_phy_cap(phy->mt76, vif);
const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem;
u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80);
u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map);
@@ -1242,6 +1243,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt7915_phy *phy = mvif->phy;
+ const struct ieee80211_sta_he_cap *vc = NULL;
int tx_ant = hweight8(phy->mt76->chainmask) - 1;
struct sta_rec_bf *bf;
struct tlv *tlv;
@@ -1260,6 +1262,12 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
if (!ebf && !dev->ibf)
return;

+ if (sta->deflink.he_cap.has_he && ebf) {
+ vc = mt76_connac_get_he_phy_cap(phy->mt76, vif);
+ if (!vc)
+ return;
+ }
+
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf));
bf = (struct sta_rec_bf *)tlv;

@@ -1268,7 +1276,7 @@ mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
* ht: iBF only, since mac80211 lacks of eBF support
*/
if (sta->deflink.he_cap.has_he && ebf)
- mt7915_mcu_sta_bfer_he(sta, vif, phy, bf);
+ mt7915_mcu_sta_bfer_he(sta, vc, bf);
else if (sta->deflink.vht_cap.vht_supported)
mt7915_mcu_sta_bfer_vht(sta, phy, bf, ebf);
else if (sta->deflink.ht_cap.ht_supported)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
index 668bfa195380..6d5f441ba437 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
@@ -569,8 +569,9 @@ bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len)

switch (type) {
case PKT_TYPE_TXRX_NOTIFY:
- /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
- mt7921_mac_tx_free(dev, data, len); /* mmio */
+ if (!mt76_is_mmio(mdev))
+ return false;
+ mt7921_mac_tx_free(dev, data, len);
return false;
case PKT_TYPE_TXS:
for (rxd += 2; rxd + 8 <= end; rxd += 8)
@@ -599,7 +600,10 @@ void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,

switch (type) {
case PKT_TYPE_TXRX_NOTIFY:
- /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
+ if (!mt76_is_mmio(mdev)) {
+ napi_consume_skb(skb, 1);
+ break;
+ }
mt7921_mac_tx_free(dev, skb->data, skb->len);
napi_consume_skb(skb, 1);
break;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index 50034d7c04f0..858b84f1c6e6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -1194,8 +1194,9 @@ bool mt7925_rx_check(struct mt76_dev *mdev, void *data, int len)

switch (type) {
case PKT_TYPE_TXRX_NOTIFY:
- /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
- mt7925_mac_tx_free(dev, data, len); /* mmio */
+ if (!mt76_is_mmio(mdev))
+ return false;
+ mt7925_mac_tx_free(dev, data, len);
return false;
case PKT_TYPE_TXS:
for (rxd += 4; rxd + 12 <= end; rxd += 12)
@@ -1231,7 +1232,10 @@ void mt7925_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,

switch (type) {
case PKT_TYPE_TXRX_NOTIFY:
- /* PKT_TYPE_TXRX_NOTIFY can be received only by mmio devices */
+ if (!mt76_is_mmio(mdev)) {
+ napi_consume_skb(skb, 1);
+ break;
+ }
mt7925_mac_tx_free(dev, skb->data, skb->len);
napi_consume_skb(skb, 1);
break;
@@ -1275,6 +1279,9 @@ mt7925_vif_connect_iter(void *priv, u8 *mac,

for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
bss_conf = mt792x_vif_to_bss_conf(vif, i);
+ if (!bss_conf)
+ continue;
+
mconf = mt792x_vif_to_link(mvif, i);

mt76_connac_mcu_uni_add_dev(&dev->mphy, bss_conf, &mconf->mt76,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 8765121b916a..e7fb49d07150 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1678,6 +1678,9 @@ static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw,
mconf = mt792x_vif_to_link(mvif, i);
mlink = mt792x_sta_to_link(msta, i);

+ if (!mlink)
+ continue;
+
if (enabled)
set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags);
else
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 37cdf3e8a067..4f20f62fb70a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -2694,6 +2694,8 @@ mt7925_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf,
struct tlv *tlv;

cap = mt76_connac_get_he_phy_cap(phy->mt76, link_conf->vif);
+ if (!cap)
+ return;

tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he));

diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
index 5503d03bf62c..5742bce12fbb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
@@ -16,6 +16,6 @@ config MT7996E
config MT7996_NPU
bool "MT7996 (PCIe) NPU support"
depends on MT7996E
- depends on NET_AIROHA_NPU=y || MT7996E=NET_AIROHA_NPU
+ depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU
select MT76_NPU
default n
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index 2748bfeb4797..9772475c5e6b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -935,6 +935,8 @@ mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
struct tlv *tlv;

cap = mt76_connac_get_he_phy_cap(phy->mt76, vif);
+ if (!cap)
+ return;

tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he));

@@ -1851,17 +1853,18 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta,
{
struct ieee80211_sta_he_cap *pc = &link_sta->he_cap;
struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
- const struct ieee80211_sta_he_cap *vc =
- mt76_connac_get_he_phy_cap(phy->mt76, vif);
- const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem;
u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80);
u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map);
+ const struct ieee80211_he_cap_elem *ve;
+ const struct ieee80211_sta_he_cap *vc;
u8 snd_dim, sts;

+ vc = mt76_connac_get_he_phy_cap(phy->mt76, vif);
if (!vc)
return;

bf->tx_mode = MT_PHY_TYPE_HE_SU;
+ ve = &vc->he_cap_elem;

mt7996_mcu_sta_sounding_rate(bf, phy);

@@ -1917,14 +1920,18 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta,
struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap;
struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem;
struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp;
- const struct ieee80211_sta_eht_cap *vc =
- mt76_connac_get_eht_phy_cap(phy->mt76, vif);
- const struct ieee80211_eht_cap_elem_fixed *ve = &vc->eht_cap_elem;
u8 nss_mcs = u8_get_bits(eht_nss->bw._80.rx_tx_mcs9_max_nss,
IEEE80211_EHT_MCS_NSS_RX) - 1;
+ const struct ieee80211_eht_cap_elem_fixed *ve;
+ const struct ieee80211_sta_eht_cap *vc;
u8 snd_dim, sts;

+ vc = mt76_connac_get_eht_phy_cap(phy->mt76, vif);
+ if (!vc)
+ return;
+
bf->tx_mode = MT_PHY_TYPE_EHT_MU;
+ ve = &vc->eht_cap_elem;

mt7996_mcu_sta_sounding_rate(bf, phy);

diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c
index 009c4770a6f9..60fe5f08964f 100644
--- a/drivers/net/wireless/microchip/wilc1000/hif.c
+++ b/drivers/net/wireless/microchip/wilc1000/hif.c
@@ -600,6 +600,11 @@ static s32 wilc_parse_assoc_resp_info(u8 *buffer, u32 buffer_len,
u16 ies_len;
struct wilc_assoc_resp *res = (struct wilc_assoc_resp *)buffer;

+ if (buffer_len < sizeof(*res)) {
+ ret_conn_info->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return -EINVAL;
+ }
+
ret_conn_info->status = le16_to_cpu(res->status_code);
if (ret_conn_info->status == WLAN_STATUS_SUCCESS) {
ies = &buffer[sizeof(*res)];
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index 1fcf5d0d2e13..17bf8e547b25 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -7122,6 +7122,7 @@ static void hwsim_virtio_rx_work(struct work_struct *work)

skb->data = skb->head;
skb_reset_tail_pointer(skb);
+ len = min(len, skb_end_offset(skb));
skb_put(skb, len);
hwsim_virtio_handle_cmd(skb);

diff --git a/drivers/platform/loongarch/loongson-laptop.c b/drivers/platform/loongarch/loongson-laptop.c
index 61b18ac206c9..dc8142f4fa95 100644
--- a/drivers/platform/loongarch/loongson-laptop.c
+++ b/drivers/platform/loongarch/loongson-laptop.c
@@ -189,6 +189,7 @@ static int __init setup_acpi_notify(struct generic_sub_driver *sub_driver)

static int loongson_hotkey_suspend(struct device *dev)
{
+ bl_powered = false;
return 0;
}

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 80144c412b90..a459b7ad18ba 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1566,6 +1566,8 @@ static DEVICE_ATTR_RW(charge_control_end_threshold);

static int asus_wmi_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
{
+ int ret, rv;
+
/* The WMI method does not provide a way to specific a battery, so we
* just assume it is the first battery.
* Note: On some newer ASUS laptops (Zenbook UM431DA), the primary/first
@@ -1583,12 +1585,30 @@ static int asus_wmi_battery_add(struct power_supply *battery, struct acpi_batter

/* The charge threshold is only reset when the system is power cycled,
* and we can't read the current threshold, however the majority of
- * platforms retains it, therefore signal the threshold as unknown
- * until user explicitly sets it to a new value.
+ * platforms retains it.
+ *
+ * Setting a negative value would signal the threshold as unknown
+ * until user explicitly sets it to a new value, however to avoid
+ * regressing userspace, we initialize it to a value of 100.
*/
- charge_end_threshold = -1;
+ charge_end_threshold = 100;
+ ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, charge_end_threshold, &rv);
+ if (ret) {
+ pr_err("Failed to reset battery charge threshold\n");
+ goto asus_wmi_battery_add_err;
+ }
+
+ if (rv != 1) {
+ pr_err("Error in battery charge threshold reset\n");
+ ret = -EIO;
+ goto asus_wmi_battery_add_err;
+ }

return 0;
+asus_wmi_battery_add_err:
+ device_remove_file(&battery->dev,
+ &dev_attr_charge_control_end_threshold);
+ return ret;
}

static int asus_wmi_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook)
diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c
index 7070c94324e0..f8137ee92e47 100644
--- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c
+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c
@@ -275,15 +275,20 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu)
data->package_id, data->die_id);
}

+ /*
+ * Set the control CPU before any read path so entry recreation after CPU
+ * hotplug can populate read-only attributes from the new online CPU.
+ */
+ data->control_cpu = cpu;
uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ);
uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ);

ret = create_attr_group(data, data->name);
if (ret) {
+ data->control_cpu = -1;
if (data->domain_id != UNCORE_DOMAIN_ID_INVALID)
ida_free(&intel_uncore_ida, data->instance_id);
} else {
- data->control_cpu = cpu;
data->valid = true;
}

diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c
index 439c0c8ac896..5ab2215fdd7f 100644
--- a/drivers/platform/x86/intel/vsec.c
+++ b/drivers/platform/x86/intel/vsec.c
@@ -103,6 +103,12 @@ static void intel_vsec_remove_aux(void *data)
auxiliary_device_uninit(data);
}

+static void intel_vsec_dev_free(struct intel_vsec_device *intel_vsec_dev)
+{
+ kfree(intel_vsec_dev->acpi_disc);
+ kfree(intel_vsec_dev);
+}
+
static void intel_vsec_dev_release(struct device *dev)
{
struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev);
@@ -111,9 +117,7 @@ static void intel_vsec_dev_release(struct device *dev)

ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id);

- kfree(intel_vsec_dev->acpi_disc);
- kfree(intel_vsec_dev->resource);
- kfree(intel_vsec_dev);
+ intel_vsec_dev_free(intel_vsec_dev);
}

static const struct vsec_feature_dependency *
@@ -219,22 +223,22 @@ int intel_vsec_add_aux(struct device *parent,
struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev;
int ret, id;

- if (!parent)
+ if (!parent) {
+ intel_vsec_dev_free(intel_vsec_dev);
return -EINVAL;
+ }

ret = xa_alloc(&auxdev_array, &intel_vsec_dev->id, intel_vsec_dev,
PMT_XA_LIMIT, GFP_KERNEL);
if (ret < 0) {
- kfree(intel_vsec_dev->resource);
- kfree(intel_vsec_dev);
+ intel_vsec_dev_free(intel_vsec_dev);
return ret;
}

id = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL);
if (id < 0) {
xa_erase(&auxdev_array, intel_vsec_dev->id);
- kfree(intel_vsec_dev->resource);
- kfree(intel_vsec_dev);
+ intel_vsec_dev_free(intel_vsec_dev);
return id;
}

@@ -282,7 +286,7 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head
unsigned long cap_id, u64 base_addr)
{
struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL;
- struct resource __free(kfree) *res = NULL;
+ struct resource *res;
struct resource *tmp;
struct device *parent;
unsigned long quirks = info->quirks;
@@ -306,13 +310,12 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head
return -EINVAL;
}

- intel_vsec_dev = kzalloc_obj(*intel_vsec_dev);
+ intel_vsec_dev = kzalloc_flex(*intel_vsec_dev, resource, header->num_entries);
if (!intel_vsec_dev)
return -ENOMEM;

- res = kzalloc_objs(*res, header->num_entries);
- if (!res)
- return -ENOMEM;
+ intel_vsec_dev->num_resources = header->num_entries;
+ res = intel_vsec_dev->resource;

if (quirks & VSEC_QUIRK_TABLE_SHIFT)
header->offset >>= TABLE_OFFSET_SHIFT;
@@ -342,8 +345,6 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head
}

intel_vsec_dev->dev = dev;
- intel_vsec_dev->resource = no_free_ptr(res);
- intel_vsec_dev->num_resources = header->num_entries;
intel_vsec_dev->quirks = info->quirks;
intel_vsec_dev->base_addr = info->base_addr;
intel_vsec_dev->priv_data = info->priv_data;
diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c
index 88f14d0ad410..3b76cccb975f 100644
--- a/drivers/platform/x86/intel/vsec_tpmi.c
+++ b/drivers/platform/x86/intel/vsec_tpmi.c
@@ -634,15 +634,12 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info,
if (!name)
return -EOPNOTSUPP;

- res = kzalloc_objs(*res, pfs->pfs_header.num_entries);
- if (!res)
+ feature_vsec_dev = kzalloc_flex(*feature_vsec_dev, resource, pfs->pfs_header.num_entries);
+ if (!feature_vsec_dev)
return -ENOMEM;

- feature_vsec_dev = kzalloc_obj(*feature_vsec_dev);
- if (!feature_vsec_dev) {
- kfree(res);
- return -ENOMEM;
- }
+ feature_vsec_dev->num_resources = pfs->pfs_header.num_entries;
+ res = feature_vsec_dev->resource;

snprintf(feature_id_name, sizeof(feature_id_name), "tpmi-%s", name);

@@ -655,8 +652,6 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info,
}

feature_vsec_dev->dev = vsec_dev->dev;
- feature_vsec_dev->resource = res;
- feature_vsec_dev->num_resources = pfs->pfs_header.num_entries;
feature_vsec_dev->priv_data = &tpmi_info->plat_info;
feature_vsec_dev->priv_data_size = sizeof(tpmi_info->plat_info);
feature_vsec_dev->ida = &intel_vsec_tpmi_ida;
diff --git a/drivers/ptp/ptp_netc.c b/drivers/ptp/ptp_netc.c
index 94e952ee6990..5e381c354d74 100644
--- a/drivers/ptp/ptp_netc.c
+++ b/drivers/ptp/ptp_netc.c
@@ -779,6 +779,7 @@ static void netc_timer_init(struct netc_timer *priv)
netc_timer_wr(priv, NETC_TMR_FIPER_CTRL, fiper_ctrl);
netc_timer_wr(priv, NETC_TMR_ECTRL, NETC_TMR_DEFAULT_ETTF_THR);

+ netc_timer_offset_write(priv, 0);
ktime_get_real_ts64(&now);
ns = timespec64_to_ns(&now);
netc_timer_cnt_write(priv, ns);
diff --git a/drivers/ptp/ptp_s390.c b/drivers/ptp/ptp_s390.c
index 29618eb9bf44..02d624d89a0a 100644
--- a/drivers/ptp/ptp_s390.c
+++ b/drivers/ptp/ptp_s390.c
@@ -107,6 +107,9 @@ static __init int ptp_s390_init(void)
if (IS_ERR(ptp_stcke_clock))
return PTR_ERR(ptp_stcke_clock);

+ if (!test_facility(28) || !ptff_query(PTFF_QPT))
+ return 0;
+
ptp_qpt_clock = ptp_clock_register(&ptp_s390_qpt_info, NULL);
if (IS_ERR(ptp_qpt_clock)) {
ptp_clock_unregister(ptp_stcke_clock);
@@ -117,7 +120,8 @@ static __init int ptp_s390_init(void)

static __exit void ptp_s390_exit(void)
{
- ptp_clock_unregister(ptp_qpt_clock);
+ if (ptp_qpt_clock)
+ ptp_clock_unregister(ptp_qpt_clock);
ptp_clock_unregister(ptp_stcke_clock);
}

diff --git a/drivers/regulator/mt6358-regulator.c b/drivers/regulator/mt6358-regulator.c
index 2604f674be49..d483f85936f8 100644
--- a/drivers/regulator/mt6358-regulator.c
+++ b/drivers/regulator/mt6358-regulator.c
@@ -492,7 +492,7 @@ static const struct regulator_ops mt6358_volt_fixed_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
- .get_voltage_sel = mt6358_get_buck_voltage_sel,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 988fc291241d..43dfce57fae1 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -1535,12 +1535,9 @@ static u16 mpam_wa_t241_calc_min_from_max(struct mpam_props *props,
static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
struct mpam_config *cfg)
{
- u32 pri_val = 0;
u16 cmax = MPAMCFG_CMAX_CMAX;
struct mpam_msc *msc = ris->vmsc->msc;
struct mpam_props *rprops = &ris->props;
- u16 dspri = GENMASK(rprops->dspri_wd, 0);
- u16 intpri = GENMASK(rprops->intpri_wd, 0);

mutex_lock(&msc->part_sel_lock);
__mpam_part_sel(ris->ris_idx, partid, msc);
@@ -1566,9 +1563,9 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,

if (mpam_has_feature(mpam_feat_mbw_part, rprops)) {
if (mpam_has_feature(mpam_feat_mbw_part, cfg))
- mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits);
- else
mpam_write_partsel_reg(msc, MBW_PBM, cfg->mbw_pbm);
+ else
+ mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits);
}

if (mpam_has_feature(mpam_feat_mbw_min, rprops)) {
@@ -1605,16 +1602,25 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,

if (mpam_has_feature(mpam_feat_intpri_part, rprops) ||
mpam_has_feature(mpam_feat_dspri_part, rprops)) {
- /* aces high? */
- if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops))
- intpri = 0;
- if (!mpam_has_feature(mpam_feat_dspri_part_0_low, rprops))
- dspri = 0;
+ u32 pri_val = 0;
+
+ if (mpam_has_feature(mpam_feat_intpri_part, rprops)) {
+ u16 intpri = GENMASK(rprops->intpri_wd - 1, 0);
+
+ /* aces high? */
+ if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops))
+ intpri = 0;

- if (mpam_has_feature(mpam_feat_intpri_part, rprops))
pri_val |= FIELD_PREP(MPAMCFG_PRI_INTPRI, intpri);
- if (mpam_has_feature(mpam_feat_dspri_part, rprops))
+ }
+ if (mpam_has_feature(mpam_feat_dspri_part, rprops)) {
+ u16 dspri = GENMASK(rprops->dspri_wd - 1, 0);
+
+ if (!mpam_has_feature(mpam_feat_dspri_part_0_low, rprops))
+ dspri = 0;
+
pri_val |= FIELD_PREP(MPAMCFG_PRI_DSPRI, dspri);
+ }

mpam_write_partsel_reg(msc, PRI, pri_val);
}
@@ -2589,8 +2595,10 @@ static void __destroy_component_cfg(struct mpam_component *comp)
msc = vmsc->msc;

if (mpam_mon_sel_lock(msc)) {
- list_for_each_entry(ris, &vmsc->ris, vmsc_list)
- add_to_garbage(ris->mbwu_state);
+ list_for_each_entry(ris, &vmsc->ris, vmsc_list) {
+ if (ris->mbwu_state)
+ add_to_garbage(ris->mbwu_state);
+ }
mpam_mon_sel_unlock(msc);
}
}
diff --git a/drivers/reset/spacemit/reset-spacemit-k3.c b/drivers/reset/spacemit/reset-spacemit-k3.c
index 9841f5e057b2..2e87f320cf11 100644
--- a/drivers/reset/spacemit/reset-spacemit-k3.c
+++ b/drivers/reset/spacemit/reset-spacemit-k3.c
@@ -112,7 +112,7 @@ static const struct ccu_reset_data k3_apmu_resets[] = {
[RESET_APMU_SDH0] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(1)),
[RESET_APMU_SDH1] = RESET_DATA(APMU_SDH1_CLK_RES_CTRL, 0, BIT(1)),
[RESET_APMU_SDH2] = RESET_DATA(APMU_SDH2_CLK_RES_CTRL, 0, BIT(1)),
- [RESET_APMU_USB2_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_APMU_USB2_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(0)),
[RESET_APMU_USB2_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(2)),
[RESET_APMU_USB2_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(3)),
[RESET_APMU_USB3_A_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(5)),
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index e047747d4ecf..46cc8e3c79a2 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -357,6 +357,7 @@ static void scsi_host_dev_release(struct device *dev)
/* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */
rcu_barrier();

+ cancel_work_sync(&shost->eh_work);
if (shost->tmf_work_q)
destroy_workqueue(shost->tmf_work_q);
if (shost->ehandler)
@@ -422,6 +423,7 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv
INIT_LIST_HEAD(&shost->starved_list);
init_waitqueue_head(&shost->host_wait);
mutex_init(&shost->scan_mutex);
+ INIT_WORK(&shost->eh_work, scsi_rcu_eh_wakeup);

index = ida_alloc(&host_index_ida, GFP_KERNEL);
if (index < 0) {
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 147127fb4db9..453a2232452d 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -73,6 +73,26 @@ void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy)
}
}

+void scsi_rcu_eh_wakeup(struct work_struct *work)
+{
+ struct Scsi_Host *shost = container_of(work, struct Scsi_Host, eh_work);
+ unsigned long flags;
+ unsigned int busy;
+
+ /*
+ * Ensure any running scsi_dec_host_busy has completed its rcu section
+ * so changes to host state and host_eh_scheduled are visible to all
+ * future calls of scsi_dec_host_busy
+ */
+ synchronize_rcu();
+
+ busy = scsi_host_busy(shost);
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ scsi_eh_wakeup(shost, busy);
+ spin_unlock_irqrestore(shost->host_lock, flags);
+}
+
/**
* scsi_schedule_eh - schedule EH for SCSI host
* @shost: SCSI host to invoke error handling on.
@@ -88,7 +108,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost)
if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 ||
scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) {
shost->host_eh_scheduled++;
- scsi_eh_wakeup(shost, scsi_host_busy(shost));
+ queue_work(shost->tmf_work_q, &shost->eh_work);
}

spin_unlock_irqrestore(shost->host_lock, flags);
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
index 7a193cc04e5b..304e8e79bd91 100644
--- a/drivers/scsi/scsi_priv.h
+++ b/drivers/scsi/scsi_priv.h
@@ -91,6 +91,7 @@ extern enum blk_eh_timer_return scsi_timeout(struct request *req);
extern int scsi_error_handler(void *host);
extern enum scsi_disposition scsi_decide_disposition(struct scsi_cmnd *cmd);
extern void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy);
+extern void scsi_rcu_eh_wakeup(struct work_struct *work);
extern void scsi_eh_scmd_add(struct scsi_cmnd *);
void scsi_eh_ready_devs(struct Scsi_Host *shost,
struct list_head *work_q,
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index 057381e56a7f..38aa83375c18 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -382,12 +382,16 @@ static irqreturn_t cqspi_irq_handler(int this_irq, void *dev)
/* Clear interrupt */
writel(irq_status, cqspi->iobase + CQSPI_REG_IRQSTATUS);

- if (cqspi->use_dma_read && ddata && ddata->get_dma_status)
- irq_status = ddata->get_dma_status(cqspi);
- else if (cqspi->slow_sram)
+ if (cqspi->use_dma_read && ddata && ddata->get_dma_status) {
+ if (ddata->get_dma_status(cqspi)) {
+ complete(&cqspi->transfer_complete);
+ return IRQ_HANDLED;
+ }
+ } else if (cqspi->slow_sram) {
irq_status &= CQSPI_IRQ_MASK_RD_SLOW_SRAM | CQSPI_IRQ_MASK_WR;
- else
+ } else {
irq_status &= CQSPI_IRQ_MASK_RD | CQSPI_IRQ_MASK_WR;
+ }

if (irq_status)
complete(&cqspi->transfer_complete);
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 4b77ec1af5a7..a039d925c0fe 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -889,7 +889,7 @@ static int vdec_open(struct file *file)

ret = vdec_init_ctrls(sess);
if (ret)
- goto err_m2m_release;
+ goto err_m2m_ctx_release;

sess->pixfmt_cap = formats[0].pixfmts_cap[0];
sess->fmt_out = &formats[0];
@@ -913,6 +913,8 @@ static int vdec_open(struct file *file)

return 0;

+err_m2m_ctx_release:
+ v4l2_m2m_ctx_release(sess->m2m_ctx);
err_m2m_release:
v4l2_m2m_release(sess->m2m_dev);
err_free_sess:
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c
index 27e43af6c7bd..bbd186b8035b 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.c
@@ -391,6 +391,7 @@ static int cedrus_open(struct file *file)
err_m2m_release:
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
err_free:
+ v4l2_fh_exit(&ctx->fh);
kfree(ctx);
mutex_unlock(&dev->dev_mutex);

@@ -507,7 +508,7 @@ static int cedrus_probe(struct platform_device *pdev)
ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
if (ret) {
v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
- goto err_m2m;
+ goto err_media;
}

v4l2_info(&dev->v4l2_dev,
@@ -533,7 +534,8 @@ static int cedrus_probe(struct platform_device *pdev)
v4l2_m2m_unregister_media_controller(dev->m2m_dev);
err_video:
video_unregister_device(&dev->vfd);
-err_m2m:
+err_media:
+ media_device_cleanup(&dev->mdev);
v4l2_m2m_release(dev->m2m_dev);
err_v4l2:
v4l2_device_unregister(&dev->v4l2_dev);
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
index 3e2843ef6cce..fc54d993b11f 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
@@ -210,6 +210,9 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx,
u8 dpb_idx;

dpb_idx = ref_list[i].index;
+ if (dpb_idx >= V4L2_H264_NUM_DPB_ENTRIES)
+ continue;
+
dpb = &decode->dpb[dpb_idx];

if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index f14cdc7b5211..456134a9e8cf 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -80,8 +80,8 @@ static int tegra_get_format_idx_by_code(struct tegra_vi *vi,
static u32 tegra_get_format_fourcc_by_idx(struct tegra_vi *vi,
unsigned int index)
{
- if (index >= vi->soc->nformats)
- return -EINVAL;
+ if (WARN_ON_ONCE(index >= vi->soc->nformats))
+ return vi->soc->video_formats[0].fourcc;

return vi->soc->video_formats[index].fourcc;
}
diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
index 3c1f0068cd92..82ccdec2f365 100644
--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
@@ -671,7 +671,14 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen)
while (cnt < in_len) {
eid = in_ie[cnt];

- if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) {
+ if (cnt + 2 > in_len)
+ break;
+
+ if (in_ie[cnt + 1] + 2 > in_len - cnt)
+ break;
+
+ if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (in_ie[cnt + 1] >= 4) &&
+ (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) {
wpsie_ptr = &in_ie[cnt];

if (wps_ie)
diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
index e7ba5ccfa03c..a0ef1e7e50a0 100644
--- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
+++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
@@ -1956,7 +1956,7 @@ static u8 rtw_get_chan_type(struct adapter *adapter)
else
return NL80211_CHAN_NO_HT;
case CHANNEL_WIDTH_40:
- if (mlme_ext->cur_ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
+ if (mlme_ext->cur_ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
return NL80211_CHAN_HT40PLUS;
else
return NL80211_CHAN_HT40MINUS;
diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c
index f88809ff370b..82656645b8a6 100644
--- a/drivers/tty/serial/8250/8250_mid.c
+++ b/drivers/tty/serial/8250/8250_mid.c
@@ -318,9 +318,11 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (!uart.port.membase)
return -ENOMEM;

- ret = mid->board->setup(mid, &uart.port);
- if (ret)
- return ret;
+ if (mid->board->setup) {
+ ret = mid->board->setup(mid, &uart.port);
+ if (ret)
+ return ret;
+ }

ret = mid8250_dma_setup(mid, &uart);
if (ret)
@@ -336,7 +338,8 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return 0;

err:
- mid->board->exit(mid);
+ if (mid->board->exit)
+ mid->board->exit(mid);
return ret;
}

@@ -346,7 +349,8 @@ static void mid8250_remove(struct pci_dev *pdev)

serial8250_unregister_port(mid->line);

- mid->board->exit(mid);
+ if (mid->board->exit)
+ mid->board->exit(mid);
}

static const struct mid8250_board pnw_board = {
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 1fd64a47341d..b323d1510a67 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -1274,6 +1274,17 @@ static int sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned int offset,
return 0;
}

+static int sc16is7xx_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+ struct sc16is7xx_port *s = gpiochip_get_data(chip);
+ struct uart_port *port = &s->p[0].port;
+ unsigned int val;
+
+ val = sc16is7xx_port_read(port, SC16IS7XX_IODIR_REG);
+
+ return val & BIT(offset) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip,
unsigned offset)
{
@@ -1351,6 +1362,7 @@ static int sc16is7xx_setup_gpio_chip(struct sc16is7xx_port *s)
s->gpio.parent = dev;
s->gpio.label = dev_name(dev);
s->gpio.init_valid_mask = sc16is7xx_gpio_init_valid_mask;
+ s->gpio.get_direction = sc16is7xx_gpio_get_direction;
s->gpio.direction_input = sc16is7xx_gpio_direction_input;
s->gpio.get = sc16is7xx_gpio_get;
s->gpio.direction_output = sc16is7xx_gpio_direction_output;
diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c
index 29ec2d15ada8..7b4cc456c453 100644
--- a/drivers/uio/uio_hv_generic.c
+++ b/drivers/uio/uio_hv_generic.c
@@ -396,9 +396,15 @@ hv_uio_remove(struct hv_device *dev)
vmbus_free_ring(dev->channel);
}

+static const struct hv_vmbus_device_id hv_uio_id_table[] = {
+ { HV_FCOPY_GUID },
+ {}
+};
+MODULE_DEVICE_TABLE(vmbus, hv_uio_id_table);
+
static struct hv_driver hv_uio_drv = {
.name = "uio_hv_generic",
- .id_table = NULL, /* only dynamic id's */
+ .id_table = hv_uio_id_table,
.probe = hv_uio_probe,
.remove = hv_uio_remove,
};
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index 1e12fc19f872..ee77c38f4ed1 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -2551,6 +2551,7 @@ static struct usbatm_driver uea_usbatm_driver = {
static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *usb = interface_to_usbdev(intf);
+ bool single_iface = usb->config->desc.bNumInterfaces == 1;
int ret;

uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n",
@@ -2559,6 +2560,22 @@ static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id)
le16_to_cpu(usb->descriptor.bcdDevice),
chip_name[UEA_CHIP_VERSION(id)]);

+ /*
+ * uea_probe() decides between the pre-firmware and post-firmware case
+ * from the USB id and stores a different object as interface data in
+ * each case: a struct completion for a pre-firmware device, a struct
+ * usbatm_data for a post-firmware one. uea_disconnect() instead tells
+ * the two apart by the number of interfaces (a pre-firmware device
+ * exposes a single interface, ADI930 has 2 and eagle has 3). A crafted
+ * device advertising a pre-firmware id together with a multi-interface
+ * descriptor (or the other way around) makes the two disagree, so that
+ * usbatm_usb_disconnect() treats the small completion object as a
+ * struct usbatm_data and reads out of bounds. Reject such inconsistent
+ * descriptors so both paths make the same decision.
+ */
+ if (UEA_IS_PREFIRM(id) != single_iface)
+ return -ENODEV;
+
usb_reset_device(usb);

if (UEA_IS_PREFIRM(id)) {
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 2ab3db3c1015..fa8d0d1e6255 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -1253,6 +1253,7 @@ static void ci_hdrc_remove(struct platform_device *pdev)
usb_role_switch_unregister(ci->role_switch);

if (ci->supports_runtime_pm) {
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index b1364f0c384c..b4452b665f59 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -740,6 +740,8 @@ static void connector_unbind(struct device *dev, struct device *connector, void

sysfs_remove_link(&connector->kobj, dev_name(dev));
sysfs_remove_link(&dev->kobj, "connector");
+ if (port_dev->child)
+ typec_deattach(port_dev->connector, &port_dev->child->dev);
port_dev->connector = NULL;
}

diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index a07866f1060c..d22dc78457d7 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -899,10 +899,15 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj,
{
struct device *dev = kobj_to_dev(kobj);
struct usb_device *udev = to_usb_device(dev);
- struct usb_host_bos *bos = udev->bos;
+ struct usb_host_bos *bos;
struct usb_bos_descriptor *desc;
size_t desclen, n = 0;
+ int rc;

+ rc = usb_lock_device_interruptible(udev);
+ if (rc < 0)
+ return -EINTR;
+ bos = udev->bos;
if (bos) {
desc = bos->desc;
desclen = le16_to_cpu(desc->wTotalLength);
@@ -911,6 +916,7 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj,
memcpy(buf, (void *) desc + off, n);
}
}
+ usb_unlock_device(udev);
return n;
}
static const BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 4d9e4bd700d8..fba8cf787d6c 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -1309,6 +1309,7 @@ static void f_midi_free(struct usb_function *f)
opts = container_of(f->fi, struct f_midi_opts, func_inst);
mutex_lock(&opts->lock);
if (!--midi->free_ref) {
+ cancel_work_sync(&midi->work);
kfree(midi->id);
kfifo_free(&midi->in_req_fifo);
kfree(midi);
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index c5bf8a448d64..64eabda2f546 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -1189,6 +1189,10 @@ static int ncm_unwrap_ntb(struct gether *port,
frame_max = ncm_opts->max_segment_size;

parse_ntb:
+ if (to_process < (int)opts->nth_size) {
+ INFO(port->func.config->cdev, "Packet too small for headers\n");
+ goto err;
+ }
tmp = (__le16 *)ntb_ptr;

/* dwSignature */
@@ -1209,8 +1213,12 @@ static int ncm_unwrap_ntb(struct gether *port,
tmp++; /* skip wSequence */

block_len = get_ncm(&tmp, opts->block_length);
+ if (block_len == 0)
+ block_len = to_process;
+
/* (d)wBlockLength */
- if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max)) {
+ if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max) ||
+ (block_len > to_process)) {
INFO(port->func.config->cdev, "Bad block length: %#X\n", block_len);
goto err;
}
@@ -1273,7 +1281,7 @@ static int ncm_unwrap_ntb(struct gether *port,
index = index2;
/* wDatagramIndex[0] */
if ((index < opts->nth_size) ||
- (index > block_len - opts->dpe_size)) {
+ (index > block_len)) {
INFO(port->func.config->cdev,
"Bad index: %#X\n", index);
goto err;
@@ -1285,7 +1293,8 @@ static int ncm_unwrap_ntb(struct gether *port,
* ethernet hdr + crc or larger than max frame size
*/
if ((dg_len < 14 + crc_len) ||
- (dg_len > frame_max)) {
+ (dg_len > frame_max) ||
+ (dg_len > block_len - index)) {
INFO(port->func.config->cdev,
"Bad dgram length: %#X\n", dg_len);
goto err;
@@ -1310,7 +1319,7 @@ static int ncm_unwrap_ntb(struct gether *port,
dg_len2 = get_ncm(&tmp, opts->dgram_item_len);

/* wDatagramIndex[1] */
- if (index2 > block_len - opts->dpe_size) {
+ if (index2 > block_len) {
INFO(port->func.config->cdev,
"Bad index: %#X\n", index2);
goto err;
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index 837f753d0cae..1857d786110b 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -431,7 +431,7 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
- size_t size;
+ size_t size, not_copied, copied;
size_t bytes_copied;
struct usb_request *req;
/* This is a pointer to the current USB rx request. */
@@ -524,10 +524,12 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
else
size = len;

- size -= copy_to_user(buf, current_rx_buf, size);
- bytes_copied += size;
- len -= size;
- buf += size;
+ not_copied = copy_to_user(buf, current_rx_buf, size);
+ copied = size - not_copied;
+
+ bytes_copied += copied;
+ len -= copied;
+ buf += copied;

spin_lock_irqsave(&dev->lock, flags);

@@ -542,6 +544,17 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
if (dev->interface < 0)
goto out_disabled;

+ if (!copied) {
+ dev->current_rx_req = current_rx_req;
+ dev->current_rx_bytes = current_rx_bytes;
+ dev->current_rx_buf = current_rx_buf;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock_printer_io);
+ return bytes_copied ? bytes_copied : -EFAULT;
+ }
+
+ size = copied;
+
/* If we not returning all the data left in this RX request
* buffer then adjust the amount of data left in the buffer.
* Othewise if we are done with this RX request buffer then
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 34d9f49e9987..b3fa5a17fd2d 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -2363,31 +2363,158 @@ static int tcm_bind(struct usb_configuration *c, struct usb_function *f)
return -ENOTSUPP;
}

-struct guas_setup_wq {
- struct work_struct work;
- struct f_uas *fu;
- unsigned int alt;
-};
+static void tcm_cleanup_old_alt(struct f_uas *fu)
+{
+ if (fu->flags & USBG_IS_UAS)
+ uasp_cleanup_old_alt(fu);
+ else if (fu->flags & USBG_IS_BOT)
+ bot_cleanup_old_alt(fu);
+ fu->flags = 0;
+}
+
+static void tcm_delayed_set_alt_done(struct f_uas *fu)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
+ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE;
+ fu->delayed_set_alt_cancel = false;
+ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
+}
+
+static bool tcm_delayed_set_alt_cancelled(struct f_uas *fu)
+{
+ bool cancelled;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
+ cancelled = fu->delayed_set_alt_cancel;
+ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
+
+ return cancelled;
+}
+
+static bool tcm_complete_delayed_status(struct f_uas *fu)
+{
+ struct usb_composite_dev *cdev = fu->function.config->cdev;
+ struct usb_request *req = cdev->req;
+ unsigned long cdev_flags;
+ bool cancelled;
+ int ret;
+
+ spin_lock_irqsave(&cdev->lock, cdev_flags);
+ spin_lock(&fu->delayed_set_alt_lock);
+ cancelled = fu->delayed_set_alt_cancel;
+ if (!cancelled) {
+ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE;
+ fu->delayed_set_alt_cancel = false;
+ }
+ spin_unlock(&fu->delayed_set_alt_lock);
+
+ if (cancelled) {
+ spin_unlock_irqrestore(&cdev->lock, cdev_flags);
+ return false;
+ }
+
+ if (cdev->delayed_status == 0) {
+ WARN(cdev, "%s: Unexpected call\n", __func__);
+ } else if (--cdev->delayed_status == 0) {
+ req->length = 0;
+ req->context = cdev;
+ ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (ret == 0) {
+ cdev->setup_pending = true;
+ } else {
+ req->status = 0;
+ req->complete(cdev->gadget->ep0, req);
+ }
+ }
+
+ spin_unlock_irqrestore(&cdev->lock, cdev_flags);
+
+ return true;
+}
+
+static bool tcm_cancel_delayed_set_alt(struct f_uas *fu)
+{
+ bool cleanup = false;
+ bool cancel = false;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
+ switch (fu->delayed_set_alt_state) {
+ case USBG_DELAYED_SET_ALT_IDLE:
+ cleanup = true;
+ break;
+ case USBG_DELAYED_SET_ALT_QUEUED:
+ case USBG_DELAYED_SET_ALT_RUNNING:
+ fu->delayed_set_alt_cancel = true;
+ cancel = true;
+ break;
+ }
+ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
+
+ if (cancel && cancel_work(&fu->delayed_set_alt)) {
+ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
+ if (fu->delayed_set_alt_state == USBG_DELAYED_SET_ALT_QUEUED) {
+ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE;
+ fu->delayed_set_alt_cancel = false;
+ cleanup = true;
+ }
+ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
+ }
+
+ return cleanup;
+}
+
+static void tcm_cancel_delayed_set_alt_sync(struct f_uas *fu)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
+ if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_IDLE)
+ fu->delayed_set_alt_cancel = true;
+ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
+
+ cancel_work_sync(&fu->delayed_set_alt);
+
+ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
+ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE;
+ fu->delayed_set_alt_cancel = false;
+ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
+}

static void tcm_delayed_set_alt(struct work_struct *wq)
{
- struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq,
- work);
- struct f_uas *fu = work->fu;
- int alt = work->alt;
+ struct f_uas *fu = container_of(wq, struct f_uas, delayed_set_alt);
+ unsigned long flags;
+ unsigned int alt;

- kfree(work);
+ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
+ if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_QUEUED) {
+ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
+ return;
+ }
+ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_RUNNING;
+ alt = fu->delayed_alt;
+ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);

- if (fu->flags & USBG_IS_BOT)
- bot_cleanup_old_alt(fu);
- if (fu->flags & USBG_IS_UAS)
- uasp_cleanup_old_alt(fu);
+ tcm_cleanup_old_alt(fu);
+
+ if (tcm_delayed_set_alt_cancelled(fu))
+ goto out_done;

if (alt == USB_G_ALT_INT_BBB)
bot_set_alt(fu);
else if (alt == USB_G_ALT_INT_UAS)
uasp_set_alt(fu);
- usb_composite_setup_continue(fu->function.config->cdev);
+
+ if (tcm_complete_delayed_status(fu))
+ return;
+
+ tcm_cleanup_old_alt(fu);
+out_done:
+ tcm_delayed_set_alt_done(fu);
}

static int tcm_get_alt(struct usb_function *f, unsigned intf)
@@ -2413,15 +2540,20 @@ static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
return -EOPNOTSUPP;

if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) {
- struct guas_setup_wq *work;
+ unsigned long flags;

- work = kmalloc_obj(*work, GFP_ATOMIC);
- if (!work)
- return -ENOMEM;
- INIT_WORK(&work->work, tcm_delayed_set_alt);
- work->fu = fu;
- work->alt = alt;
- schedule_work(&work->work);
+ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
+ if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_IDLE) {
+ spin_unlock_irqrestore(&fu->delayed_set_alt_lock,
+ flags);
+ return -EBUSY;
+ }
+ fu->delayed_alt = alt;
+ fu->delayed_set_alt_cancel = false;
+ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_QUEUED;
+ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
+
+ schedule_work(&fu->delayed_set_alt);
return USB_GADGET_DELAYED_STATUS;
}
return -EOPNOTSUPP;
@@ -2431,11 +2563,8 @@ static void tcm_disable(struct usb_function *f)
{
struct f_uas *fu = to_f_uas(f);

- if (fu->flags & USBG_IS_UAS)
- uasp_cleanup_old_alt(fu);
- else if (fu->flags & USBG_IS_BOT)
- bot_cleanup_old_alt(fu);
- fu->flags = 0;
+ if (tcm_cancel_delayed_set_alt(fu))
+ tcm_cleanup_old_alt(fu);
}

static int tcm_setup(struct usb_function *f,
@@ -2583,11 +2712,16 @@ static void tcm_free(struct usb_function *f)
{
struct f_uas *tcm = to_f_uas(f);

+ tcm_cancel_delayed_set_alt_sync(tcm);
kfree(tcm);
}

static void tcm_unbind(struct usb_configuration *c, struct usb_function *f)
{
+ struct f_uas *fu = to_f_uas(f);
+
+ tcm_cancel_delayed_set_alt_sync(fu);
+ tcm_cleanup_old_alt(fu);
usb_free_all_descriptors(f);
}

@@ -2620,6 +2754,8 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
fu->function.disable = tcm_disable;
fu->function.free_func = tcm_free;
fu->tpg = tpg_instances[i].tpg;
+ INIT_WORK(&fu->delayed_set_alt, tcm_delayed_set_alt);
+ spin_lock_init(&fu->delayed_set_alt_lock);

hash_init(fu->stream_hash);
mutex_unlock(&tpg_instances_lock);
diff --git a/drivers/usb/gadget/function/tcm.h b/drivers/usb/gadget/function/tcm.h
index 009974d81d66..e1d5a9391612 100644
--- a/drivers/usb/gadget/function/tcm.h
+++ b/drivers/usb/gadget/function/tcm.h
@@ -3,6 +3,7 @@
#define __TARGET_USB_GADGET_H__

#include <linux/kref.h>
+#include <linux/spinlock.h>
/* #include <linux/usb/uas.h> */
#include <linux/hashtable.h>
#include <linux/usb/composite.h>
@@ -29,6 +30,12 @@ enum {

#define USB_G_DEFAULT_SESSION_TAGS USBG_NUM_CMDS

+enum {
+ USBG_DELAYED_SET_ALT_IDLE = 0,
+ USBG_DELAYED_SET_ALT_QUEUED,
+ USBG_DELAYED_SET_ALT_RUNNING,
+};
+
struct tcm_usbg_nexus {
struct se_session *tvn_se_sess;
};
@@ -132,6 +139,12 @@ struct f_uas {
#define USBG_BOT_CMD_PEND (1 << 4)
#define USBG_BOT_WEDGED (1 << 5)

+ struct work_struct delayed_set_alt;
+ spinlock_t delayed_set_alt_lock; /* protects delayed_set_alt_* */
+ unsigned int delayed_alt;
+ unsigned int delayed_set_alt_state;
+ bool delayed_set_alt_cancel;
+
struct usbg_cdb cmd[USBG_NUM_CMDS];
struct usb_ep *ep_in;
struct usb_ep *ep_out;
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index 514e5930b9ca..dfa0521a243a 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -200,6 +200,8 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
return usb_ep_set_halt(cdev->gadget->ep0);

req->length = min_t(unsigned int, uvc->event_length, data->length);
+ if (req->length > sizeof(data->data))
+ req->length = sizeof(data->data);
req->zero = data->length < uvc->event_length;

memcpy(req->buf, data->data, req->length);
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index 438201dc96ca..a8dbaef54aba 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -585,9 +585,29 @@ static int bdc_probe(struct platform_device *pdev)
static void bdc_remove(struct platform_device *pdev)
{
struct bdc *bdc;
+ unsigned long flags;
+ u32 temp;

bdc = platform_get_drvdata(pdev);
dev_dbg(bdc->dev, "%s ()\n", __func__);
+ /*
+ * Disable the device interrupt source before freeing the IRQ:
+ * clear BDC_GIE so the controller stops asserting interrupts,
+ * then free_irq drains any in-flight handler.
+ */
+ spin_lock_irqsave(&bdc->lock, flags);
+ temp = bdc_readl(bdc->regs, BDC_BDCSC);
+ temp &= ~BDC_GIE;
+ bdc_writel(bdc->regs, BDC_BDCSC, temp);
+ spin_unlock_irqrestore(&bdc->lock, flags);
+ free_irq(bdc->irq, bdc);
+ /*
+ * Drain func_wake_notify after free_irq: the IRQ handler arms this
+ * delayed_work via bdc_sr_uspc -> handle_link_state_change ->
+ * schedule_delayed_work (self-rearmed in bdc_func_wake_timer), so
+ * the IRQ must be released first to prevent re-arm after cancel.
+ */
+ cancel_delayed_work_sync(&bdc->func_wake_notify);
bdc_udc_exit(bdc);
bdc_hw_exit(bdc);
bdc_phy_exit(bdc);
diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c
index 23826fd7a8e6..7a12219edac6 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_udc.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c
@@ -530,8 +530,8 @@ int bdc_udc_init(struct bdc *bdc)


bdc->gadget.name = BRCM_BDC_NAME;
- ret = devm_request_irq(bdc->dev, bdc->irq, bdc_udc_interrupt,
- IRQF_SHARED, BRCM_BDC_NAME, bdc);
+ ret = request_irq(bdc->irq, bdc_udc_interrupt, IRQF_SHARED,
+ BRCM_BDC_NAME, bdc);
if (ret) {
dev_err(bdc->dev,
"failed to request irq #%d %d\n",
@@ -542,7 +542,7 @@ int bdc_udc_init(struct bdc *bdc)
ret = bdc_init_ep(bdc);
if (ret) {
dev_err(bdc->dev, "bdc init ep fail: %d\n", ret);
- return ret;
+ goto err0;
}

ret = usb_add_gadget_udc(bdc->dev, &bdc->gadget);
@@ -571,6 +571,7 @@ int bdc_udc_init(struct bdc *bdc)
err1:
usb_del_gadget_udc(&bdc->gadget);
err0:
+ free_irq(bdc->irq, bdc);
bdc_free_ep(bdc);

return ret;
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index f47903461ed5..c0e40fa6dde5 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -278,6 +278,7 @@ struct dummy {
unsigned ints_enabled:1;
unsigned udc_suspended:1;
unsigned pullup:1;
+ unsigned fifo_req_busy:1;

/*
* HOST side support
@@ -329,6 +330,26 @@ static inline struct dummy *gadget_dev_to_dummy(struct device *dev)

/* DEVICE/GADGET SIDE UTILITY ROUTINES */

+/*
+ * Give back a gadget request with dum->lock dropped around the callback.
+ * If @req is the shared fifo_req, clear fifo_req_busy afterward: the flag
+ * was set in dummy_queue() when the shared request was taken and must stay
+ * set until its completion callback has returned; list_del_init() alone
+ * makes the request look idle while the callback is still running.
+ * Caller holds dum->lock and has already done list_del_init() + status.
+ */
+static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep,
+ struct dummy_request *req)
+{
+ bool fifo = req == &dum->fifo_req;
+
+ spin_unlock(&dum->lock);
+ usb_gadget_giveback_request(_ep, &req->req);
+ spin_lock(&dum->lock);
+ if (fifo)
+ dum->fifo_req_busy = 0;
+}
+
/* called with spinlock held */
static void nuke(struct dummy *dum, struct dummy_ep *ep)
{
@@ -339,9 +360,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep)
list_del_init(&req->queue);
req->req.status = -ESHUTDOWN;

- spin_unlock(&dum->lock);
- usb_gadget_giveback_request(&ep->ep, &req->req);
- spin_lock(&dum->lock);
+ dummy_giveback(dum, &ep->ep, req);
}
}

@@ -728,10 +747,11 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req,

/* implement an emulated single-request FIFO */
if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
- list_empty(&dum->fifo_req.queue) &&
+ !dum->fifo_req_busy &&
list_empty(&ep->queue) &&
_req->length <= FIFO_SIZE) {
req = &dum->fifo_req;
+ dum->fifo_req_busy = 1;
req->req = *_req;
req->req.buf = dum->fifo_buf;
memcpy(dum->fifo_buf, _req->buf, _req->length);
@@ -785,9 +805,7 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req)
dev_dbg(udc_dev(dum),
"dequeued req %p from %s, len %d buf %p\n",
req, _ep->name, _req->length, _req->buf);
- spin_unlock(&dum->lock);
- usb_gadget_giveback_request(_ep, _req);
- spin_lock(&dum->lock);
+ dummy_giveback(dum, _ep, req);
}
spin_unlock_irqrestore(&dum->lock, flags);
return retval;
@@ -1523,9 +1541,7 @@ static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb,
if (req->req.status != -EINPROGRESS) {
list_del_init(&req->queue);

- spin_unlock(&dum->lock);
- usb_gadget_giveback_request(&ep->ep, &req->req);
- spin_lock(&dum->lock);
+ dummy_giveback(dum, &ep->ep, req);

/* requests might have been unlinked... */
rescan = 1;
@@ -1910,9 +1926,7 @@ static enum hrtimer_restart dummy_timer(struct hrtimer *t)
dev_dbg(udc_dev(dum), "stale req = %p\n",
req);

- spin_unlock(&dum->lock);
- usb_gadget_giveback_request(&ep->ep, &req->req);
- spin_lock(&dum->lock);
+ dummy_giveback(dum, &ep->ep, req);
ep->already_seen = 0;
goto restart;
}
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 600ce8cc0fef..c4761933ec86 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -184,7 +184,7 @@ __acquires(ep->udc->lock)
usb_gadget_unmap_request(&ep->udc->gadget, &req->req, ep_is_in(ep));

if (status && (status != -ESHUTDOWN))
- dev_vdbg(&udc->gadget.dev, "complete %s req %p stat %d len %u/%u\n",
+ dev_vdbg(udc->dev, "complete %s req %p stat %d len %u/%u\n",
ep->ep.name, &req->req, status,
req->req.actual, req->req.length);

@@ -286,7 +286,7 @@ static int dr_controller_setup(struct fsl_udc *udc)
timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {
if (time_after(jiffies, timeout)) {
- dev_err(&udc->gadget.dev, "udc reset timeout!\n");
+ dev_err(udc->dev, "udc reset timeout!\n");
return -ETIMEDOUT;
}
cpu_relax();
@@ -309,7 +309,7 @@ static int dr_controller_setup(struct fsl_udc *udc)
tmp &= USB_EP_LIST_ADDRESS_MASK;
fsl_writel(tmp, &dr_regs->endpointlistaddr);

- dev_vdbg(&udc->gadget.dev,
+ dev_vdbg(udc->dev,
"vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x\n",
udc->ep_qh, (int)tmp,
fsl_readl(&dr_regs->endpointlistaddr));
@@ -500,7 +500,7 @@ static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num,
tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;
break;
default:
- dev_vdbg(&udc->gadget.dev, "error ep type is %d\n", ep_type);
+ dev_vdbg(udc->dev, "error ep type is %d\n", ep_type);
return;
}
if (zlt)
@@ -613,7 +613,7 @@ static int fsl_ep_enable(struct usb_ep *_ep,
spin_unlock_irqrestore(&udc->lock, flags);
retval = 0;

- dev_vdbg(&udc->gadget.dev, "enabled %s (ep%d%s) maxpacket %d\n",
+ dev_vdbg(udc->dev, "enabled %s (ep%d%s) maxpacket %d\n",
ep->ep.name, ep->ep.desc->bEndpointAddress & 0x0f,
(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
max);
@@ -634,13 +634,8 @@ static int fsl_ep_disable(struct usb_ep *_ep)
int ep_num;

ep = container_of(_ep, struct fsl_ep, ep);
- if (!_ep || !ep->ep.desc) {
- /*
- * dev_vdbg(&udc->gadget.dev, "%s not enabled\n",
- * _ep ? ep->ep.name : NULL);
- */
+ if (!_ep || !ep->ep.desc)
return -EINVAL;
- }

/* disable ep on controller */
ep_num = ep_index(ep);
@@ -664,7 +659,7 @@ static int fsl_ep_disable(struct usb_ep *_ep)
ep->stopped = 1;
spin_unlock_irqrestore(&udc->lock, flags);

- dev_vdbg(&udc->gadget.dev, "disabled %s OK\n", _ep->name);
+ dev_vdbg(udc->dev, "disabled %s OK\n", _ep->name);
return 0;
}

@@ -724,9 +719,6 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
{
u32 temp, bitmask, tmp_stat;

- /* dev_vdbg(&udc->gadget.dev, "QH addr Register 0x%8x\n", dr_regs->endpointlistaddr);
- dev_vdbg(&udc->gadget.dev, "ep_qh[%d] addr is 0x%8x\n", i, (u32)&(ep->udc->ep_qh[i])); */
-
bitmask = ep_is_in(ep)
? (1 << (ep_index(ep) + 16))
: (1 << (ep_index(ep)));
@@ -813,7 +805,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
*is_last = 0;

if ((*is_last) == 0)
- dev_vdbg(&udc_controller->gadget.dev, "multi-dtd request!\n");
+ dev_vdbg(udc_controller->dev, "multi-dtd request!\n");
/* Fill in the transfer size; set active bit */
swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);

@@ -825,7 +817,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,

mb();

- dev_vdbg(&udc_controller->gadget.dev, "length = %d address= 0x%x\n", *length, (int)*dma);
+ dev_vdbg(udc_controller->dev, "length = %d address= 0x%x\n", *length, (int)*dma);

return dtd;
}
@@ -876,11 +868,11 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
/* catch various bogus parameters */
if (!_req || !req->req.complete || !req->req.buf
|| !list_empty(&req->queue)) {
- dev_vdbg(&udc->gadget.dev, "%s, bad params\n", __func__);
+ dev_vdbg(udc->dev, "%s, bad params\n", __func__);
return -EINVAL;
}
if (unlikely(!ep->ep.desc)) {
- dev_vdbg(&udc->gadget.dev, "%s, bad ep\n", __func__);
+ dev_vdbg(udc->dev, "%s, bad ep\n", __func__);
return -EINVAL;
}
if (usb_endpoint_xfer_isoc(ep->ep.desc)) {
@@ -1040,7 +1032,7 @@ static int fsl_ep_set_halt(struct usb_ep *_ep, int value)
udc->ep0_dir = 0;
}
out:
- dev_vdbg(&udc->gadget.dev, "%s %s halt stat %d\n", ep->ep.name,
+ dev_vdbg(udc->dev, "%s %s halt stat %d\n", ep->ep.name,
value ? "set" : "clear", status);

return status;
@@ -1109,7 +1101,7 @@ static void fsl_ep_fifo_flush(struct usb_ep *_ep)
/* Wait until flush complete */
while (fsl_readl(&dr_regs->endptflush)) {
if (time_after(jiffies, timeout)) {
- dev_err(&udc_controller->gadget.dev,
+ dev_err(udc_controller->dev,
"ep flush timeout\n");
return;
}
@@ -1182,7 +1174,7 @@ static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)

udc = container_of(gadget, struct fsl_udc, gadget);
spin_lock_irqsave(&udc->lock, flags);
- dev_vdbg(&gadget->dev, "VBUS %s\n", str_on_off(is_active));
+ dev_vdbg(udc->dev, "VBUS %s\n", str_on_off(is_active));
udc->vbus_active = (is_active != 0);
if (can_pullup(udc))
fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
@@ -1548,7 +1540,7 @@ static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0,
udc->ep0_state = WAIT_FOR_SETUP;
break;
case WAIT_FOR_SETUP:
- dev_err(&udc->gadget.dev, "Unexpected ep0 packets\n");
+ dev_err(udc->dev, "Unexpected ep0 packets\n");
break;
default:
ep0stall(udc);
@@ -1617,7 +1609,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,
errors = hc32_to_cpu(curr_td->size_ioc_sts);
if (errors & DTD_ERROR_MASK) {
if (errors & DTD_STATUS_HALTED) {
- dev_err(&udc->gadget.dev, "dTD error %08x QH=%d\n", errors, pipe);
+ dev_err(udc->dev, "dTD error %08x QH=%d\n", errors, pipe);
/* Clear the errors and Halt condition */
tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts);
tmp &= ~errors;
@@ -1628,26 +1620,26 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,
break;
}
if (errors & DTD_STATUS_DATA_BUFF_ERR) {
- dev_vdbg(&udc->gadget.dev, "Transfer overflow\n");
+ dev_vdbg(udc->dev, "Transfer overflow\n");
status = -EPROTO;
break;
} else if (errors & DTD_STATUS_TRANSACTION_ERR) {
- dev_vdbg(&udc->gadget.dev, "ISO error\n");
+ dev_vdbg(udc->dev, "ISO error\n");
status = -EILSEQ;
break;
} else
- dev_err(&udc->gadget.dev,
+ dev_err(udc->dev,
"Unknown error has occurred (0x%x)!\n",
errors);

} else if (hc32_to_cpu(curr_td->size_ioc_sts)
& DTD_STATUS_ACTIVE) {
- dev_vdbg(&udc->gadget.dev, "Request not complete\n");
+ dev_vdbg(udc->dev, "Request not complete\n");
status = REQ_UNCOMPLETE;
return status;
} else if (remaining_length) {
if (direction) {
- dev_vdbg(&udc->gadget.dev,
+ dev_vdbg(udc->dev,
"Transmit dTD remaining length not zero\n");
status = -EPROTO;
break;
@@ -1655,8 +1647,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,
break;
}
} else {
- dev_vdbg(&udc->gadget.dev,
- "dTD transmitted successful\n");
+ dev_vdbg(udc->dev, "dTD transmitted successful\n");
}

if (j != curr_req->dtd_count - 1)
@@ -1699,7 +1690,7 @@ static void dtd_complete_irq(struct fsl_udc *udc)

/* If the ep is configured */
if (!curr_ep->ep.name) {
- dev_warn(&udc->gadget.dev, "Invalid EP?\n");
+ dev_warn(udc->dev, "Invalid EP?\n");
continue;
}

@@ -1708,7 +1699,7 @@ static void dtd_complete_irq(struct fsl_udc *udc)
queue) {
status = process_ep_req(udc, i, curr_req);

- dev_vdbg(&udc->gadget.dev,
+ dev_vdbg(udc->dev,
"status of process_ep_req= %d, ep = %d\n",
status, ep_num);
if (status == REQ_UNCOMPLETE)
@@ -1829,7 +1820,7 @@ static void reset_irq(struct fsl_udc *udc)
while (fsl_readl(&dr_regs->endpointprime)) {
/* Wait until all endptprime bits cleared */
if (time_after(jiffies, timeout)) {
- dev_err(&udc->gadget.dev, "Timeout for reset\n");
+ dev_err(udc->dev, "Timeout for reset\n");
break;
}
cpu_relax();
@@ -1839,7 +1830,7 @@ static void reset_irq(struct fsl_udc *udc)
fsl_writel(0xffffffff, &dr_regs->endptflush);

if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
- dev_vdbg(&udc->gadget.dev, "Bus reset\n");
+ dev_vdbg(udc->dev, "Bus reset\n");
/* Bus is reseting */
udc->bus_reset = 1;
/* Reset all the queues, include XD, dTD, EP queue
@@ -1847,7 +1838,7 @@ static void reset_irq(struct fsl_udc *udc)
reset_queues(udc, true);
udc->usb_state = USB_STATE_DEFAULT;
} else {
- dev_vdbg(&udc->gadget.dev, "Controller reset\n");
+ dev_vdbg(udc->dev, "Controller reset\n");
/* initialize usb hw reg except for regs for EP, not
* touch usbintr reg */
dr_controller_setup(udc);
@@ -1881,7 +1872,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
/* Clear notification bits */
fsl_writel(irq_src, &dr_regs->usbsts);

- /* dev_vdbg(&udc->gadget.dev, "irq_src [0x%8x]", irq_src); */
+ /* dev_vdbg(udc->dev, "irq_src [0x%8x]", irq_src); */

/* Need to resume? */
if (udc->usb_state == USB_STATE_SUSPENDED)
@@ -1890,7 +1881,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)

/* USB Interrupt */
if (irq_src & USB_STS_INT) {
- dev_vdbg(&udc->gadget.dev, "Packet int\n");
+ dev_vdbg(udc->dev, "Packet int\n");
/* Setup package, we only support ep0 as control ep */
if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) {
tripwire_handler(udc, 0,
@@ -1919,7 +1910,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)

/* Reset Received */
if (irq_src & USB_STS_RESET) {
- dev_vdbg(&udc->gadget.dev, "reset int\n");
+ dev_vdbg(udc->dev, "reset int\n");
reset_irq(udc);
status = IRQ_HANDLED;
}
@@ -1931,7 +1922,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
}

if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) {
- dev_vdbg(&udc->gadget.dev, "Error IRQ %x\n", irq_src);
+ dev_vdbg(udc->dev, "Error IRQ %x\n", irq_src);
}

spin_unlock_irqrestore(&udc->lock, flags);
@@ -1967,7 +1958,7 @@ static int fsl_udc_start(struct usb_gadget *g,
udc_controller->transceiver->otg,
&udc_controller->gadget);
if (retval < 0) {
- dev_err(&udc_controller->gadget.dev, "can't bind to transceiver\n");
+ dev_err(udc_controller->dev, "can't bind to transceiver\n");
udc_controller->driver = NULL;
return retval;
}
@@ -2252,7 +2243,7 @@ static int struct_udc_setup(struct fsl_udc *udc,

udc->eps = kzalloc_objs(struct fsl_ep, udc->max_ep);
if (!udc->eps) {
- dev_err(&udc->gadget.dev, "kmalloc udc endpoint status failed\n");
+ dev_err(udc->dev, "kmalloc udc endpoint status failed\n");
goto eps_alloc_failed;
}

@@ -2267,7 +2258,7 @@ static int struct_udc_setup(struct fsl_udc *udc,
udc->ep_qh = dma_alloc_coherent(&pdev->dev, size,
&udc->ep_qh_dma, GFP_KERNEL);
if (!udc->ep_qh) {
- dev_err(&udc->gadget.dev, "malloc QHs for udc failed\n");
+ dev_err(udc->dev, "malloc QHs for udc failed\n");
goto ep_queue_alloc_failed;
}

@@ -2278,14 +2269,14 @@ static int struct_udc_setup(struct fsl_udc *udc,
udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
struct fsl_req, req);
if (!udc->status_req) {
- dev_err(&udc->gadget.dev, "kzalloc for udc status request failed\n");
+ dev_err(udc->dev, "kzalloc for udc status request failed\n");
goto udc_status_alloc_failed;
}

/* allocate a small amount of memory to get valid address */
udc->status_req->req.buf = kmalloc(8, GFP_KERNEL);
if (!udc->status_req->req.buf) {
- dev_err(&udc->gadget.dev, "kzalloc for udc request buffer failed\n");
+ dev_err(udc->dev, "kzalloc for udc request buffer failed\n");
goto udc_req_buf_alloc_failed;
}

@@ -2373,6 +2364,7 @@ static int fsl_udc_probe(struct platform_device *pdev)
if (udc_controller == NULL)
return -ENOMEM;

+ udc_controller->dev = &pdev->dev;
pdata = dev_get_platdata(&pdev->dev);
udc_controller->pdata = pdata;
spin_lock_init(&udc_controller->lock);
@@ -2382,7 +2374,7 @@ static int fsl_udc_probe(struct platform_device *pdev)
if (pdata->operating_mode == FSL_USB2_DR_OTG) {
udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(udc_controller->transceiver)) {
- dev_err(&udc_controller->gadget.dev, "Can't find OTG driver!\n");
+ dev_err(&pdev->dev, "Can't find OTG driver!\n");
ret = -ENODEV;
goto err_kfree;
}
@@ -2398,7 +2390,7 @@ static int fsl_udc_probe(struct platform_device *pdev)
if (pdata->operating_mode == FSL_USB2_DR_DEVICE) {
if (!request_mem_region(res->start, resource_size(res),
driver_name)) {
- dev_err(&udc_controller->gadget.dev, "request mem region for %s failed\n", pdev->name);
+ dev_err(&pdev->dev, "failed to request mem region\n");
ret = -EBUSY;
goto err_kfree;
}
@@ -2429,7 +2421,7 @@ static int fsl_udc_probe(struct platform_device *pdev)
/* Read Device Controller Capability Parameters register */
dccparams = fsl_readl(&dr_regs->dccparams);
if (!(dccparams & DCCPARAMS_DC)) {
- dev_err(&udc_controller->gadget.dev, "This SOC doesn't support device role\n");
+ dev_err(&pdev->dev, "This SOC doesn't support device role\n");
ret = -ENODEV;
goto err_exit;
}
@@ -2447,14 +2439,14 @@ static int fsl_udc_probe(struct platform_device *pdev)
ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,
driver_name, udc_controller);
if (ret != 0) {
- dev_err(&udc_controller->gadget.dev, "cannot request irq %d err %d\n",
+ dev_err(&pdev->dev, "cannot request irq %d err %d\n",
udc_controller->irq, ret);
goto err_exit;
}

/* Initialize the udc structure including QH member and other member */
if (struct_udc_setup(udc_controller, pdev)) {
- dev_err(&udc_controller->gadget.dev, "Can't initialize udc data structure\n");
+ dev_err(&pdev->dev, "Can't initialize udc data structure\n");
ret = -ENOMEM;
goto err_free_irq;
}
@@ -2474,7 +2466,6 @@ static int fsl_udc_probe(struct platform_device *pdev)
udc_controller->gadget.name = driver_name;

/* Setup gadget.dev and register with kernel */
- dev_set_name(&udc_controller->gadget.dev, "gadget");
udc_controller->gadget.dev.of_node = pdev->dev.of_node;

if (!IS_ERR_OR_NULL(udc_controller->transceiver))
diff --git a/drivers/usb/gadget/udc/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h
index cc1756f3e89d..53922bc58ca0 100644
--- a/drivers/usb/gadget/udc/fsl_usb2_udc.h
+++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h
@@ -470,6 +470,7 @@ struct fsl_ep {
#define EP_DIR_OUT 0

struct fsl_udc {
+ struct device *dev;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct fsl_usb2_platform_data *pdata;
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 0e0db68e0b27..d506f9d92bca 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -3133,7 +3133,6 @@ int udc_probe(struct udc *dev)
/* device struct setup */
dev->gadget.ops = &udc_ops;

- dev_set_name(&dev->gadget.dev, "gadget");
dev->gadget.name = name;
dev->gadget.max_speed = USB_SPEED_HIGH;

diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 585b2f3117b0..b0377701a940 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -448,6 +448,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == PCI_DEVICE_ID_VIA_VL805) {
xhci->quirks |= XHCI_LPM_SUPPORT;
xhci->quirks |= XHCI_TRB_OVERFETCH;
+ xhci->dma_mask_bits = 36;
}

if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 15346fdd66bc..5b15eae233d3 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -5469,6 +5469,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
if (xhci->hci_version > 0x100)
xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);

+ xhci->dma_mask_bits = 64;
xhci->max_slots = HCS_MAX_SLOTS(hcs_params1);
xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS);
/* xhci-plat or xhci-pci might have set max_interrupters already */
@@ -5518,12 +5519,16 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
if (xhci->quirks & XHCI_NO_64BIT_SUPPORT)
xhci->hcc_params &= ~BIT(0);

- /* Set dma_mask and coherent_dma_mask to 64-bits,
- * if xHC supports 64-bit addressing */
+ /*
+ * Set dma_mask and coherent_dma_mask to 64-bits if xHC supports
+ * 64-bit addressing, unless a controller-specific quirk callback
+ * limits the usable address width.
+ */
if ((xhci->hcc_params & HCC_64BIT_ADDR) &&
- !dma_set_mask(dev, DMA_BIT_MASK(64))) {
- xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
- dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
+ !dma_set_mask(dev, DMA_BIT_MASK(xhci->dma_mask_bits))) {
+ xhci_dbg(xhci, "Enabling %u-bit DMA addresses.\n",
+ xhci->dma_mask_bits);
+ dma_set_coherent_mask(dev, DMA_BIT_MASK(xhci->dma_mask_bits));
} else {
/*
* This is to avoid error in cases where a 32-bit USB
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index aeecd301f207..d0f14fc23245 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1524,6 +1524,7 @@ struct xhci_hcd {
/* imod_interval in ns (I * 250ns) */
u32 imod_interval;
u32 page_size;
+ unsigned int dma_mask_bits;
/* MSI-X/MSI vectors */
int nvecs;
/* optional clocks */
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 333ab79f0ca9..6e749faac33c 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -454,7 +454,6 @@ static int omap2430_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to register musb device\n");
goto err_disable_rpm;
}
- of_node_put(np);

return 0;

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

return ret;
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index af14548fa03d..88876cc27a62 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1073,6 +1073,8 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 3) },
/* Abacus Electrics */
{ USB_DEVICE(FTDI_VID, ABACUS_OPTICAL_PROBE_PID) },
+ /* Endress+Hauser AG devices */
+ { USB_DEVICE(FTDI_VID, FTDI_EH_FXA291_PID) },
{ } /* Terminating entry */
};

diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 6c76cfebfd0e..9c83c17853c8 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -313,6 +313,11 @@
#define FTDI_ELV_UDF77_PID 0xFB5E /* USB DCF Funkuhr (UDF 77) */
#define FTDI_ELV_UIO88_PID 0xFB5F /* USB-I/O Interface (UIO 88) */

+/*
+ * Endress+Hauser AG product ids (FTDI_VID)
+ */
+#define FTDI_EH_FXA291_PID 0xE510
+
/*
* EVER Eco Pro UPS (http://www.ever.com.pl/)
*/
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 3f5889145e51..244bfeed7f5d 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -646,7 +646,8 @@ static void edge_interrupt_callback(struct urb *urb)
if (edge_port && edge_port->open) {
spin_lock_irqsave(&edge_port->ep_lock,
flags);
- edge_port->txCredits += txCredits;
+ edge_port->txCredits = min(edge_port->txCredits + txCredits,
+ edge_port->maxTxCredits);
spin_unlock_irqrestore(&edge_port->ep_lock,
flags);
dev_dbg(dev, "%s - txcredits for port%d = %d\n",
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 377f5695cfc6..988323669a0e 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -35,6 +35,8 @@ struct keyspan_pda_private {
struct work_struct unthrottle_work;
struct usb_serial *serial;
struct usb_serial_port *port;
+ bool throttled;
+ bool throttle_req;
};

static int keyspan_pda_write_start(struct usb_serial_port *port);
@@ -150,6 +152,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
int retval;
int status = urb->status;
struct keyspan_pda_private *priv;
+ bool throttled = false;
unsigned long flags;

priv = usb_get_serial_port_data(port);
@@ -211,16 +214,24 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
}

exit:
- retval = usb_submit_urb(urb, GFP_ATOMIC);
- if (retval)
- dev_err(&port->dev,
- "%s - usb_submit_urb failed with result %d\n",
- __func__, retval);
+ spin_lock_irqsave(&port->lock, flags);
+ if (priv->throttle_req) {
+ priv->throttled = true;
+ throttled = true;
+ }
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ if (!throttled) {
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&port->dev, "failed to resubmit in urb: %d\n", retval);
+ }
}

static void keyspan_pda_rx_throttle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
+ struct keyspan_pda_private *priv = usb_get_serial_port_data(port);

/*
* Stop receiving characters. We just turn off the URB request, and
@@ -230,16 +241,29 @@ static void keyspan_pda_rx_throttle(struct tty_struct *tty)
* send an XOFF, although it might make sense to foist that off upon
* the device too.
*/
- usb_kill_urb(port->interrupt_in_urb);
+ spin_lock_irq(&port->lock);
+ priv->throttle_req = true;
+ spin_unlock_irq(&port->lock);
}

static void keyspan_pda_rx_unthrottle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
+ struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
+ bool throttled;
+ int ret;

- /* just restart the receive interrupt URB */
- if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL))
- dev_dbg(&port->dev, "usb_submit_urb(read urb) failed\n");
+ spin_lock_irq(&port->lock);
+ throttled = priv->throttled;
+ priv->throttled = false;
+ priv->throttle_req = false;
+ spin_unlock_irq(&port->lock);
+
+ if (throttled) {
+ ret = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+ if (ret)
+ dev_err(&port->dev, "failed to submit in urb: %d\n", ret);
+ }
}

static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud)
@@ -579,6 +603,8 @@ static int keyspan_pda_open(struct tty_struct *tty,

spin_lock_irq(&port->lock);
priv->tx_room = rc;
+ priv->throttled = false;
+ priv->throttle_req = false;
spin_unlock_irq(&port->lock);

rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 7a0f37feb9f2..7470a04090f9 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -2497,6 +2497,7 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(5) },
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff), /* Rolling RW135R-GL (laptop MBIM) */
.driver_info = RSVD(5) },
+ { USB_DEVICE_INTERFACE_CLASS(0x3466, 0x3301, 0xff) }, /* TDTECH MT5710-CN */
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) },
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 255968f9ca42..ac22fa318734 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -395,6 +395,13 @@ UNUSUAL_DEV( 0x04b3, 0x4001, 0x0110, 0x0110,
USB_SC_DEVICE, USB_PR_CB, NULL,
US_FL_MAX_SECTORS_MIN),

+/* Reported by Ai Chao <aichao@xxxxxxxxxx> */
+UNUSUAL_DEV( 0x04b4, 0xb708, 0x0000, 0xffff,
+ "Longmai Technologies",
+ "USB Key",
+ USB_SC_SCSI, USB_PR_BULK, NULL,
+ US_FL_NO_ATA_1X),
+
/*
* Reported by Simon Levitt <simon@xxxxxxxxxx>
* This entry needs Sub and Proto fields
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 66b879957165..14cdc90821f0 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -498,6 +498,129 @@ static int ucsi_register_altmode(struct ucsi_connector *con,
return ret;
}

+static void ucsi_dump_duplicate_altmode(struct ucsi_connector *con,
+ u8 recipient, u16 svid,
+ u32 existing_vdo, u32 new_vdo,
+ int offset)
+{
+ static const char * const recipient_names[] = {
+ [UCSI_RECIPIENT_CON] = "port",
+ [UCSI_RECIPIENT_SOP] = "partner",
+ [UCSI_RECIPIENT_SOP_P] = "plug",
+ [UCSI_RECIPIENT_SOP_PP] = "cable plug prime",
+ };
+
+ dev_warn(con->ucsi->dev,
+ "con%d: Firmware bug: duplicate %s altmode SVID 0x%04x at offset %d, ignoring but please contact the BIOS vendor to fix this issue.\n",
+ con->num, recipient_names[recipient], svid, offset);
+
+ if (existing_vdo != new_vdo)
+ dev_warn(con->ucsi->dev,
+ "con%d: VDO mismatch: 0x%08x vs 0x%08x\n",
+ con->num, existing_vdo, new_vdo);
+}
+
+/* Count altmodes in @altmodes that advertise @svid. */
+static int ucsi_altmode_count_svid(struct typec_altmode **altmodes, u16 svid)
+{
+ int count = 0;
+ int k;
+
+ for (k = 0; k < UCSI_MAX_ALTMODES; k++) {
+ if (!altmodes[k])
+ break;
+ if (altmodes[k]->svid == svid)
+ count++;
+ }
+
+ return count;
+}
+
+/*
+ * Check if an altmode is a duplicate. Some firmware implementations
+ * incorrectly return the same altmode multiple times, causing sysfs errors.
+ * Returns true if the altmode should be skipped.
+ *
+ * The matching rules differ by recipient:
+ *
+ * - UCSI_RECIPIENT_CON (port) and UCSI_RECIPIENT_SOP_P (plug):
+ * Two altmodes with identical SVID and VDO are byte-for-byte duplicates
+ * and the second has no observable function. Drop them.
+ *
+ * - UCSI_RECIPIENT_SOP (partner):
+ * The typec class binds each partner altmode to a port altmode of the
+ * same SVID via altmode_match()/device_find_child(), which returns the
+ * first port altmode with a matching SVID. If the partner advertises
+ * more altmodes for SVID X than the port advertises, the surplus
+ * partner altmode(s) collapse onto an already-paired port altmode and
+ * trigger a "duplicate filename .../partner" sysfs error during
+ * typec_altmode_create_links(). Use the port-side altmode count for
+ * SVID X as the authoritative cap and reject any partner altmode that
+ * would exceed it. This preserves legitimate multi-Mode partner
+ * altmodes (e.g. vendor SVIDs that the port really does advertise
+ * twice) while filtering the firmware-generated duplicates that have
+ * no port counterpart.
+ */
+static bool ucsi_altmode_is_duplicate(struct ucsi_connector *con, u8 recipient,
+ const struct ucsi_altmode *alt_batch, int batch_idx,
+ u16 svid, u32 vdo, int offset)
+{
+ struct typec_altmode **altmodes;
+ int port_count, partner_count;
+ int k;
+
+ /* Check for duplicates within the current batch first */
+ for (k = 0; k < batch_idx; k++) {
+ if (alt_batch[k].svid == svid && alt_batch[k].mid == vdo) {
+ ucsi_dump_duplicate_altmode(con, recipient, svid,
+ vdo, vdo, offset);
+ return true;
+ }
+ }
+
+ switch (recipient) {
+ case UCSI_RECIPIENT_SOP:
+ /*
+ * Cap partner altmodes per SVID by the port-side count:
+ * any further partner altmode for that SVID would alias an
+ * already-paired port altmode and break typec sysfs.
+ */
+ port_count = ucsi_altmode_count_svid(con->port_altmode, svid);
+ partner_count = ucsi_altmode_count_svid(con->partner_altmode,
+ svid);
+ if (port_count && partner_count >= port_count) {
+ ucsi_dump_duplicate_altmode(con, recipient, svid,
+ con->partner_altmode[partner_count - 1]->vdo,
+ vdo, offset);
+ return true;
+ }
+ return false;
+ case UCSI_RECIPIENT_CON:
+ altmodes = con->port_altmode;
+ break;
+ case UCSI_RECIPIENT_SOP_P:
+ altmodes = con->plug_altmode;
+ break;
+ default:
+ return false;
+ }
+
+ /* CON and SOP_P: drop only exact SVID+VDO duplicates. */
+ for (k = 0; k < UCSI_MAX_ALTMODES; k++) {
+ if (!altmodes[k])
+ break;
+
+ if (altmodes[k]->svid != svid || altmodes[k]->vdo != vdo)
+ continue;
+
+ ucsi_dump_duplicate_altmode(con, recipient, svid,
+ altmodes[k]->vdo, vdo, offset);
+ return true;
+ }
+
+ return false;
+}
+
static int
ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
{
@@ -552,19 +675,25 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)

/* now register altmodes */
for (i = 0; i < max_altmodes; i++) {
- memset(&desc, 0, sizeof(desc));
- if (multi_dp) {
- desc.svid = updated[i].svid;
- desc.vdo = updated[i].mid;
- } else {
- desc.svid = orig[i].svid;
- desc.vdo = orig[i].mid;
- }
- desc.roles = TYPEC_PORT_DRD;
+ struct ucsi_altmode *altmode_array = multi_dp ? updated : orig;

- if (!desc.svid)
+ if (!altmode_array[i].svid)
return 0;

+ /*
+ * Check for duplicates in current array and already
+ * registered altmodes. Skip if duplicate found.
+ */
+ if (ucsi_altmode_is_duplicate(con, recipient, altmode_array, i,
+ altmode_array[i].svid,
+ altmode_array[i].mid, i))
+ continue;
+
+ memset(&desc, 0, sizeof(desc));
+ desc.svid = altmode_array[i].svid;
+ desc.vdo = altmode_array[i].mid;
+ desc.roles = TYPEC_PORT_DRD;
+
ret = ucsi_register_altmode(con, &desc, recipient);
if (ret)
return ret;
@@ -622,6 +751,15 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
if (!alt[j].svid)
return 0;

+ /*
+ * Check for duplicates in current batch and already
+ * registered altmodes. Skip if duplicate found.
+ */
+ if (ucsi_altmode_is_duplicate(con, recipient, alt, j,
+ alt[j].svid, alt[j].mid,
+ i - num + j))
+ continue;
+
memset(&desc, 0, sizeof(desc));
desc.vdo = alt[j].mid;
desc.svid = alt[j].svid;
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index b9af63fb6306..6949b704166d 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -722,10 +722,12 @@ static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq,
goto err;
}

- gso = buf + pad - sock_hlen;
-
- if (!sock_hlen)
+ if (!sock_hlen) {
memset(buf, 0, pad);
+ gso = buf;
+ } else {
+ gso = buf + pad - sock_hlen;
+ }

if ((gso->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
vhost16_to_cpu(vq, gso->csum_start) +
diff --git a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
index 4230b817a80b..d66291def0f4 100644
--- a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
+++ b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
@@ -82,8 +82,8 @@ static int mmio_guard_ioremap_hook(phys_addr_t phys, size_t size,
if (protval != PROT_DEVICE_nGnRE && protval != PROT_DEVICE_nGnRnE)
return 0;

+ end = PAGE_ALIGN(phys + size);
phys = PAGE_ALIGN_DOWN(phys);
- end = phys + PAGE_ALIGN(size);

while (phys < end) {
const int func_id = ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_FUNC_ID;
diff --git a/drivers/watchdog/airoha_wdt.c b/drivers/watchdog/airoha_wdt.c
index dc8ca11c14d8..4bd333189b87 100644
--- a/drivers/watchdog/airoha_wdt.c
+++ b/drivers/watchdog/airoha_wdt.c
@@ -147,6 +147,9 @@ static int airoha_wdt_probe(struct platform_device *pdev)

/* Watchdog ticks at half the bus rate */
airoha_wdt->wdt_freq = clk_get_rate(bus_clk) / 2;
+ if (!airoha_wdt->wdt_freq)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid clock frequency\n");

/* Initialize struct watchdog device */
wdog_dev = &airoha_wdt->wdog_dev;
diff --git a/drivers/watchdog/ni903x_wdt.c b/drivers/watchdog/ni903x_wdt.c
index 8b1b9baa914e..c72a9ee9cb8e 100644
--- a/drivers/watchdog/ni903x_wdt.c
+++ b/drivers/watchdog/ni903x_wdt.c
@@ -183,9 +183,14 @@ static int ni903x_acpi_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct watchdog_device *wdd;
struct ni903x_wdt *wdt;
+ acpi_handle handle;
acpi_status status;
int ret;

+ handle = ACPI_HANDLE(dev);
+ if (!handle)
+ return -ENODEV;
+
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
@@ -193,7 +198,7 @@ static int ni903x_acpi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, wdt);
wdt->dev = dev;

- status = acpi_walk_resources(ACPI_HANDLE(dev), METHOD_NAME__CRS,
+ status = acpi_walk_resources(handle, METHOD_NAME__CRS,
ni903x_resources, wdt);
if (ACPI_FAILURE(status) || wdt->io_base == 0) {
dev_err(dev, "failed to get resources\n");
diff --git a/drivers/watchdog/s32g_wdt.c b/drivers/watchdog/s32g_wdt.c
index ad55063060af..6422a694fc65 100644
--- a/drivers/watchdog/s32g_wdt.c
+++ b/drivers/watchdog/s32g_wdt.c
@@ -56,8 +56,7 @@ MODULE_PARM_DESC(early_enable,

static const struct watchdog_info s32g_wdt_info = {
.identity = "s32g watchdog",
- .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
- WDIOC_GETTIMEOUT | WDIOC_GETTIMELEFT,
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
};

static struct s32g_wdt_device *wdd_to_s32g_wdt(struct watchdog_device *wdd)
diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c
index 19eb2ed2c7cb..02e09b9e396d 100644
--- a/drivers/watchdog/watchdog_pretimeout.c
+++ b/drivers/watchdog/watchdog_pretimeout.c
@@ -167,6 +167,8 @@ void watchdog_unregister_governor(struct watchdog_governor *gov)
}

spin_lock_irq(&pretimeout_lock);
+ if (default_gov == gov)
+ default_gov = NULL;
list_for_each_entry(p, &pretimeout_list, entry)
if (p->wdd->gov == gov)
p->wdd->gov = default_gov;
diff --git a/fs/afs/dir_edit.c b/fs/afs/dir_edit.c
index fd3aa9f97ce6..3ead36a07048 100644
--- a/fs/afs/dir_edit.c
+++ b/fs/afs/dir_edit.c
@@ -415,7 +415,7 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
if (!afs_dir_init_iter(&iter, name))
return;

- meta = afs_dir_find_block(&iter, 0);
+ meta = afs_dir_get_block(&iter, 0);
if (!meta)
return;

diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 7e3108489c83..fe0b5c5ed2bc 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -231,6 +231,10 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm)
for (i = 0; i < exec_params.hdr.e_phnum; i++, phdr++) {
switch (phdr->p_type) {
case PT_INTERP:
+ /* elf ABI allows only one interpreter */
+ if (interpreter_name)
+ continue;
+
retval = -ENOMEM;
if (phdr->p_filesz > PATH_MAX)
goto error;
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index b3d8fd70e8b1..f8d2cdaa6a88 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -228,9 +228,6 @@ static int load_misc_binary(struct linux_binprm *bprm)
goto ret;
}

- if (fmt->flags & MISC_FMT_OPEN_BINARY)
- bprm->have_execfd = 1;
-
/* make argv[1] be the path to the binary */
retval = copy_string_kernel(bprm->interp, bprm);
if (retval < 0)
@@ -260,6 +257,8 @@ static int load_misc_binary(struct linux_binprm *bprm)
goto ret;

bprm->interpreter = interp_file;
+ if (fmt->flags & MISC_FMT_OPEN_BINARY)
+ bprm->have_execfd = 1;
if (fmt->flags & MISC_FMT_CREDENTIALS)
bprm->execfd_creds = 1;

diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 5d8fda94fe22..beb75f152d5c 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -507,6 +507,8 @@ static inline bool btrfs_inode_can_compress(const struct btrfs_inode *inode)
if (inode->flags & BTRFS_INODE_NODATACOW ||
inode->flags & BTRFS_INODE_NODATASUM)
return false;
+ if (btrfs_is_data_reloc_root(inode->root))
+ return false;
return true;
}

diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 2275189b7860..f0bfa8a6218a 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -1963,7 +1963,7 @@ static noinline_for_stack bool lock_extent_buffer_for_io(struct extent_buffer *e

btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
percpu_counter_add_batch(&fs_info->dirty_metadata_bytes,
- -eb->len,
+ -(s64)eb->len,
fs_info->dirty_metadata_batch);
ret = true;
} else {
@@ -3778,7 +3778,7 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans,
return;

buffer_tree_clear_mark(eb, PAGECACHE_TAG_DIRTY);
- percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -eb->len,
+ percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -(s64)eb->len,
fs_info->dirty_metadata_batch);

for (int i = 0; i < num_extent_folios(eb); i++) {
diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
index 6b79bff241f2..a8bce1d4e92c 100644
--- a/fs/btrfs/extent_map.c
+++ b/fs/btrfs/extent_map.c
@@ -866,13 +866,13 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end,
goto next;
}

- flags = em->flags;
/*
* In case we split the extent map, we want to preserve the
* EXTENT_FLAG_LOGGING flag on our extent map, but we don't want
* it on the new extent maps.
*/
- em->flags &= ~(EXTENT_FLAG_PINNED | EXTENT_FLAG_LOGGING);
+ flags = em->flags & ~EXTENT_FLAG_LOGGING;
+ em->flags &= ~EXTENT_FLAG_PINNED;
modified = !list_empty(&em->list);

/*
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index d72249390030..e50b10b30c71 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -356,6 +356,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
const unsigned int nblocks = orig_len >> fs_info->sectorsize_bits;
int ret = 0;
u32 bio_offset = 0;
+ bool using_commit_root = false;

if ((inode->flags & BTRFS_INODE_NODATASUM) ||
test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state))
@@ -429,6 +430,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
* from across transactions.
*/
if (bbio->csum_search_commit_root) {
+ using_commit_root = true;
path->search_commit_root = true;
path->skip_locking = true;
down_read(&fs_info->commit_root_sem);
@@ -461,6 +463,28 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
* assume this is the case.
*/
if (count == 0) {
+ /*
+ * If an extent is relocated in the current transaction
+ * then relocation writes a new csum without updating
+ * the extent map generation. Until the next commit, we
+ * will see a hole in that case, so we need to fallback
+ * to searching the transaction csum root.
+ *
+ * Note that a commit root lookup of a referenced extent can
+ * only miss, not return a stale csum. A freed extent's csum
+ * is deleted in the same transaction and its bytenr is not
+ * reusable until that transaction has committed and the
+ * extent is unpinned.
+ */
+ if (using_commit_root) {
+ up_read(&fs_info->commit_root_sem);
+ using_commit_root = false;
+ path->search_commit_root = false;
+ path->skip_locking = false;
+ btrfs_release_path(path);
+ continue;
+ }
+
memset(csum_dst, 0, csum_size);
count = 1;

@@ -479,7 +503,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
bio_offset += count * sectorsize;
}

- if (bbio->csum_search_commit_root)
+ if (using_commit_root)
up_read(&fs_info->commit_root_sem);
return ret;
}
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index ab22e4f9ffdd..bbc4db7fe74b 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -555,6 +555,9 @@ static int io_ctl_check_crc(struct btrfs_io_ctl *io_ctl, int index)
u32 crc = ~(u32)0;
unsigned offset = 0;

+ if (index >= io_ctl->num_pages)
+ return -EIO;
+
if (index == 0)
offset = sizeof(u32) * io_ctl->num_pages;

diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 3ebaf5880125..37dd9c8b352f 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -497,6 +497,7 @@ static int __add_reloc_root(struct btrfs_root *root)
btrfs_err(fs_info,
"Duplicate root found for start=%llu while inserting into relocation tree",
node->bytenr);
+ kfree(node);
return -EEXIST;
}

@@ -1850,6 +1851,7 @@ void merge_reloc_roots(struct reloc_control *rc)
* corruption, e.g. bad reloc tree key offset.
*/
ret = -EINVAL;
+ btrfs_put_root(root);
goto out;
}
ret = merge_reloc_root(rc, root);
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 0a86f672cc09..7fab73874068 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -790,6 +790,9 @@ static int write_folio_nounlock(struct folio *folio,
ceph_wbc.truncate_size, true);
if (IS_ERR(req)) {
folio_redirty_for_writepage(wbc, folio);
+ if (atomic_long_dec_return(&fsc->writeback_count) <
+ CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb))
+ fsc->write_congested = false;
return PTR_ERR(req);
}

@@ -809,6 +812,9 @@ static int write_folio_nounlock(struct folio *folio,
folio_redirty_for_writepage(wbc, folio);
folio_end_writeback(folio);
ceph_osdc_put_request(req);
+ if (atomic_long_dec_return(&fsc->writeback_count) <
+ CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb))
+ fsc->write_congested = false;
return PTR_ERR(bounce_page);
}
}
@@ -847,6 +853,9 @@ static int write_folio_nounlock(struct folio *folio,
ceph_vinop(inode), folio);
folio_redirty_for_writepage(wbc, folio);
folio_end_writeback(folio);
+ if (atomic_long_dec_return(&fsc->writeback_count) <
+ CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb))
+ fsc->write_congested = false;
return err;
}
if (err == -EBLOCKLISTED)
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index d51454e995a8..68435a4d6fa9 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -4365,6 +4365,7 @@ void ceph_handle_caps(struct ceph_mds_session *session,

snaptrace = h + 1;
snaptrace_len = le32_to_cpu(h->snap_trace_len);
+ ceph_decode_need(&snaptrace, end, snaptrace_len, bad);
p = snaptrace + snaptrace_len;

if (msg_version >= 2) {
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 27ce9e55e947..ef9e92e362d3 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -546,11 +546,16 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx)
pr_warn_client(cl,
"%p %llx.%llx rde->offset 0x%llx ctx->pos 0x%llx\n",
inode, ceph_vinop(inode), rde->offset, ctx->pos);
+ ceph_mdsc_put_request(dfi->last_readdir);
+ dfi->last_readdir = NULL;
return -EIO;
}

- if (WARN_ON_ONCE(!rde->inode.in))
+ if (WARN_ON_ONCE(!rde->inode.in)) {
+ ceph_mdsc_put_request(dfi->last_readdir);
+ dfi->last_readdir = NULL;
return -EIO;
+ }

ctx->pos = rde->offset;
doutc(cl, "%p %llx.%llx (%d/%d) -> %llx '%.*s' %p\n", inode,
diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c
index 47324062fee5..66b9c9150fed 100644
--- a/fs/crypto/inline_crypt.c
+++ b/fs/crypto/inline_crypt.c
@@ -22,22 +22,14 @@

#include "fscrypt_private.h"

-static struct block_device **fscrypt_get_devices(struct super_block *sb,
- unsigned int *num_devs)
+static unsigned int
+fscrypt_get_devices(struct super_block *sb,
+ struct block_device *devs[FSCRYPT_MAX_DEVICES])
{
- struct block_device **devs;
-
- if (sb->s_cop->get_devices) {
- devs = sb->s_cop->get_devices(sb, num_devs);
- if (devs)
- return devs;
- }
- devs = kmalloc_obj(*devs);
- if (!devs)
- return ERR_PTR(-ENOMEM);
+ if (sb->s_cop->get_devices)
+ return sb->s_cop->get_devices(sb, devs);
devs[0] = sb->s_bdev;
- *num_devs = 1;
- return devs;
+ return 1;
}

static unsigned int fscrypt_get_dun_bytes(const struct fscrypt_inode_info *ci)
@@ -96,7 +88,7 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci,
const struct inode *inode = ci->ci_inode;
struct super_block *sb = inode->i_sb;
struct blk_crypto_config crypto_cfg;
- struct block_device **devs;
+ struct block_device *devs[FSCRYPT_MAX_DEVICES];
unsigned int num_devs;
unsigned int i;

@@ -135,20 +127,15 @@ int fscrypt_select_encryption_impl(struct fscrypt_inode_info *ci,
crypto_cfg.key_type = is_hw_wrapped_key ?
BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW;

- devs = fscrypt_get_devices(sb, &num_devs);
- if (IS_ERR(devs))
- return PTR_ERR(devs);
-
+ num_devs = fscrypt_get_devices(sb, devs);
for (i = 0; i < num_devs; i++) {
if (!blk_crypto_config_supported(devs[i], &crypto_cfg))
- goto out_free_devs;
+ return 0;
}

fscrypt_log_blk_crypto_impl(ci->ci_mode, devs, num_devs, &crypto_cfg);

ci->ci_inlinecrypt = true;
-out_free_devs:
- kfree(devs);

return 0;
}
@@ -164,7 +151,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
enum blk_crypto_key_type key_type = is_hw_wrapped ?
BLK_CRYPTO_KEY_TYPE_HW_WRAPPED : BLK_CRYPTO_KEY_TYPE_RAW;
struct blk_crypto_key *blk_key;
- struct block_device **devs;
+ struct block_device *devs[FSCRYPT_MAX_DEVICES];
unsigned int num_devs;
unsigned int i;
int err;
@@ -182,17 +169,12 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
}

/* Start using blk-crypto on all the filesystem's block devices. */
- devs = fscrypt_get_devices(sb, &num_devs);
- if (IS_ERR(devs)) {
- err = PTR_ERR(devs);
- goto fail;
- }
+ num_devs = fscrypt_get_devices(sb, devs);
for (i = 0; i < num_devs; i++) {
err = blk_crypto_start_using_key(devs[i], blk_key);
if (err)
break;
}
- kfree(devs);
if (err) {
fscrypt_err(inode, "error %d starting to use blk-crypto", err);
goto fail;
@@ -210,20 +192,21 @@ void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
struct fscrypt_prepared_key *prep_key)
{
struct blk_crypto_key *blk_key = prep_key->blk_key;
- struct block_device **devs;
+ struct block_device *devs[FSCRYPT_MAX_DEVICES];
unsigned int num_devs;
unsigned int i;

if (!blk_key)
return;

- /* Evict the key from all the filesystem's block devices. */
- devs = fscrypt_get_devices(sb, &num_devs);
- if (!IS_ERR(devs)) {
- for (i = 0; i < num_devs; i++)
- blk_crypto_evict_key(devs[i], blk_key);
- kfree(devs);
- }
+ /*
+ * Evict the key from all the filesystem's block devices.
+ * This *must* be done before the key is freed.
+ */
+ num_devs = fscrypt_get_devices(sb, devs);
+ for (i = 0; i < num_devs; i++)
+ blk_crypto_evict_key(devs[i], blk_key);
+
kfree_sensitive(blk_key);
}

diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c
index e6e527c73f16..7e3a58dc4b56 100644
--- a/fs/crypto/keysetup_v1.c
+++ b/fs/crypto/keysetup_v1.c
@@ -147,13 +147,19 @@ find_or_insert_direct_key(struct fscrypt_direct_key *to_insert,
if (memcmp(ci->ci_policy.v1.master_key_descriptor,
dk->dk_descriptor, FSCRYPT_KEY_DESCRIPTOR_SIZE) != 0)
continue;
+ /* The sb is used at eviction time, so it must be the same. */
+ if (ci->ci_inode->i_sb != dk->dk_sb)
+ continue;
if (ci->ci_mode != dk->dk_mode)
continue;
if (!fscrypt_is_key_prepared(&dk->dk_key, ci))
continue;
if (crypto_memneq(raw_key, dk->dk_raw, ci->ci_mode->keysize))
continue;
- /* using existing tfm with same (descriptor, mode, raw_key) */
+ /*
+ * Use an existing prepared key with the same (descriptor, sb,
+ * mode, inlinecrypt, raw_key) combination.
+ */
refcount_inc(&dk->dk_refcount);
spin_unlock(&fscrypt_direct_keys_lock);
free_direct_key(to_insert);
diff --git a/fs/erofs/zmap.c b/fs/erofs/zmap.c
index e1a02a2c8406..53e8a9f2fb91 100644
--- a/fs/erofs/zmap.c
+++ b/fs/erofs/zmap.c
@@ -721,7 +721,8 @@ static int z_erofs_map_sanity_check(struct inode *inode,
map->m_algorithmformat, EROFS_I(inode)->nid);
return -EFSCORRUPTED;
}
- if (EROFS_MAP_FULL(map->m_flags) && map->m_llen < map->m_plen) {
+ if (EROFS_MAP_FULL(map->m_flags) && map->m_llen < map->m_plen &&
+ map->m_la + map->m_llen < inode->i_size) {
erofs_err(inode->i_sb, "too much compressed data @ la %llu of nid %llu",
map->m_la, EROFS_I(inode)->nid);
return -EFSCORRUPTED;
diff --git a/fs/exec.c b/fs/exec.c
index ba12b4c466f6..4e156bb78321 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -736,7 +736,7 @@ int transfer_args_to_stack(struct linux_binprm *bprm,
stop = bprm->p >> PAGE_SHIFT;
sp = *sp_location;

- for (index = MAX_ARG_PAGES - 1; index >= stop; index--) {
+ for (index = MAX_ARG_PAGES; index-- > stop; ) {
unsigned int offset = index == stop ? bprm->p & ~PAGE_MASK : 0;
char *src = kmap_local_page(bprm->page[index]) + offset;
sp -= PAGE_SIZE - offset;
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index ccf806b676f5..bcdad2e0ac54 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -3731,24 +3731,27 @@ static bool f2fs_has_stable_inodes(struct super_block *sb)
return true;
}

-static struct block_device **f2fs_get_devices(struct super_block *sb,
- unsigned int *num_devs)
+static unsigned int
+f2fs_get_devices(struct super_block *sb,
+ struct block_device *devs[FSCRYPT_MAX_DEVICES])
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
- struct block_device **devs;
+ int ndevs;
int i;

- if (!f2fs_is_multi_device(sbi))
- return NULL;
+ static_assert(MAX_DEVICES <= FSCRYPT_MAX_DEVICES);

- devs = kmalloc_objs(*devs, sbi->s_ndevs);
- if (!devs)
- return ERR_PTR(-ENOMEM);
+ if (!f2fs_is_multi_device(sbi)) {
+ devs[0] = sb->s_bdev;
+ return 1;
+ }
+ ndevs = sbi->s_ndevs;
+ if (WARN_ON_ONCE(ndevs > FSCRYPT_MAX_DEVICES))
+ ndevs = FSCRYPT_MAX_DEVICES;

- for (i = 0; i < sbi->s_ndevs; i++)
+ for (i = 0; i < ndevs; i++)
devs[i] = FDEV(i).bdev;
- *num_devs = sbi->s_ndevs;
- return devs;
+ return ndevs;
}

static const struct fscrypt_operations f2fs_cryptops = {
diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
index 4fde6336cf5c..8155c35cf56a 100644
--- a/fs/fuse/dev_uring.c
+++ b/fs/fuse/dev_uring.c
@@ -995,15 +995,26 @@ static bool is_ring_ready(struct fuse_ring *ring, int current_qid)
/*
* fuse_uring_req_fetch command handling
*/
-static void fuse_uring_do_register(struct fuse_ring_ent *ent,
- struct io_uring_cmd *cmd,
- unsigned int issue_flags)
+static int fuse_uring_do_register(struct fuse_ring_ent *ent,
+ struct io_uring_cmd *cmd,
+ unsigned int issue_flags)
{
struct fuse_ring_queue *queue = ent->queue;
struct fuse_ring *ring = queue->ring;
struct fuse_conn *fc = ring->fc;
struct fuse_iqueue *fiq = &fc->iq;

+ spin_lock(&fc->lock);
+ /* abort teardown path is running or has run */
+ if (!fc->connected) {
+ spin_unlock(&fc->lock);
+ if (atomic_dec_and_test(&ring->queue_refs))
+ wake_up_all(&ring->stop_waitq);
+ kfree(ent);
+ return -ECONNABORTED;
+ }
+ spin_unlock(&fc->lock);
+
fuse_uring_prepare_cancel(cmd, issue_flags, ent);

spin_lock(&queue->lock);
@@ -1020,6 +1031,7 @@ static void fuse_uring_do_register(struct fuse_ring_ent *ent,
wake_up_all(&fc->blocked_waitq);
}
}
+ return 0;
}

/*
@@ -1136,9 +1148,7 @@ static int fuse_uring_register(struct io_uring_cmd *cmd,
if (IS_ERR(ent))
return PTR_ERR(ent);

- fuse_uring_do_register(ent, cmd, issue_flags);
-
- return 0;
+ return fuse_uring_do_register(ent, cmd, issue_flags);
}

/*
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 5fa9a2c7e30e..8408e9685d25 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -67,11 +67,13 @@ static bool ifs_set_range_uptodate(struct folio *folio,
struct iomap_folio_state *ifs, size_t off, size_t len)
{
struct inode *inode = folio->mapping->host;
- unsigned int first_blk = off >> inode->i_blkbits;
- unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
- unsigned int nr_blks = last_blk - first_blk + 1;
+ unsigned int first_blk, last_blk;

- bitmap_set(ifs->state, first_blk, nr_blks);
+ if (len) {
+ first_blk = off >> inode->i_blkbits;
+ last_blk = (off + len - 1) >> inode->i_blkbits;
+ bitmap_set(ifs->state, first_blk, last_blk - first_blk + 1);
+ }
return ifs_is_fully_uptodate(folio, ifs);
}

@@ -176,13 +178,17 @@ static void ifs_clear_range_dirty(struct folio *folio,
{
struct inode *inode = folio->mapping->host;
unsigned int blks_per_folio = i_blocks_per_folio(inode, folio);
- unsigned int first_blk = (off >> inode->i_blkbits);
- unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
- unsigned int nr_blks = last_blk - first_blk + 1;
+ unsigned int first_blk = round_up(off, i_blocksize(inode)) >>
+ inode->i_blkbits;
+ unsigned int last_blk = (off + len) >> inode->i_blkbits;
unsigned long flags;

+ if (first_blk >= last_blk)
+ return;
+
spin_lock_irqsave(&ifs->state_lock, flags);
- bitmap_clear(ifs->state, first_blk + blks_per_folio, nr_blks);
+ bitmap_clear(ifs->state, first_blk + blks_per_folio,
+ last_blk - first_blk);
spin_unlock_irqrestore(&ifs->state_lock, flags);
}

@@ -199,13 +205,17 @@ static void ifs_set_range_dirty(struct folio *folio,
{
struct inode *inode = folio->mapping->host;
unsigned int blks_per_folio = i_blocks_per_folio(inode, folio);
- unsigned int first_blk = (off >> inode->i_blkbits);
- unsigned int last_blk = (off + len - 1) >> inode->i_blkbits;
- unsigned int nr_blks = last_blk - first_blk + 1;
+ unsigned int first_blk, last_blk;
unsigned long flags;

+ if (!len)
+ return;
+
+ first_blk = off >> inode->i_blkbits;
+ last_blk = (off + len - 1) >> inode->i_blkbits;
spin_lock_irqsave(&ifs->state_lock, flags);
- bitmap_set(ifs->state, first_blk + blks_per_folio, nr_blks);
+ bitmap_set(ifs->state, first_blk + blks_per_folio,
+ last_blk - first_blk + 1);
spin_unlock_irqrestore(&ifs->state_lock, flags);
}

@@ -1536,6 +1546,7 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
const struct iomap_write_ops *write_ops)
{
u64 bytes = iomap_length(iter);
+ bool zeroed = false;
int status;

do {
@@ -1554,6 +1565,8 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
/* a NULL folio means we're done with a folio batch */
if (!folio) {
status = iomap_iter_advance_full(iter);
+ if (status)
+ return status;
break;
}

@@ -1564,6 +1577,7 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
bytes);

folio_zero_range(folio, offset, bytes);
+ zeroed = true;
folio_mark_accessed(folio);

ret = iomap_write_end(iter, bytes, bytes, folio);
@@ -1573,10 +1587,10 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,

status = iomap_iter_advance(iter, bytes);
if (status)
- break;
+ return status;
} while ((bytes = iomap_length(iter)) > 0);

- if (did_zero)
+ if (did_zero && zeroed)
*did_zero = true;
return status;
}
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 2c557a998961..10c2ded3469a 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1439,7 +1439,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned long exp_op_flags = 0;
unsigned int pflags = current->flags;
bool restore_flags = false;
- unsigned int nvecs;
+ int nvecs;

trace_nfsd_write_opened(rqstp, fhp, offset, *cnt);

@@ -1479,6 +1479,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
}

nvecs = xdr_buf_to_bvec(rqstp->rq_bvec, rqstp->rq_maxpages, payload);
+ if (nvecs < 0) {
+ host_err = nvecs;
+ goto out_nfserr;
+ }

since = READ_ONCE(file->f_wb_err);
if (verf)
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index 27cc07738f33..f3d97eb146e8 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -528,6 +528,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
loff_t len, unsigned int flags, enum ovl_copyop op)
{
+ struct inode *inode_in = file_inode(file_in);
struct inode *inode_out = file_inode(file_out);
struct file *realfile_in, *realfile_out;
loff_t ret;
@@ -551,7 +552,20 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
if (IS_ERR(realfile_in))
goto out_unlock;

- with_ovl_creds(file_inode(file_out)->i_sb) {
+ /*
+ * For cross-sb copy, vfs_copy_file_range() will verify read access with
+ * the mounter creds of the dest fs mounter, so we need to explicitly
+ * verify read access with the source mounter creds.
+ */
+ if (unlikely(inode_in->i_sb != inode_out->i_sb)) {
+ with_ovl_creds(inode_in->i_sb) {
+ ret = rw_verify_area(READ, realfile_in, &pos_in, len);
+ if (unlikely(ret))
+ goto out_unlock;
+ }
+ }
+
+ with_ovl_creds(inode_out->i_sb) {
switch (op) {
case OVL_COPY:
ret = vfs_copy_file_range(realfile_in, pos_in,
diff --git a/fs/overlayfs/xattrs.c b/fs/overlayfs/xattrs.c
index aa95855c7023..859e80ae6f40 100644
--- a/fs/overlayfs/xattrs.c
+++ b/fs/overlayfs/xattrs.c
@@ -13,7 +13,7 @@ static bool ovl_is_escaped_xattr(struct super_block *sb, const char *name)
OVL_XATTR_ESCAPE_USER_PREFIX_LEN) == 0;
else
return strncmp(name, OVL_XATTR_ESCAPE_TRUSTED_PREFIX,
- OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN - 1) == 0;
+ OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN) == 0;
}

static bool ovl_is_own_xattr(struct super_block *sb, const char *name)
diff --git a/fs/pidfs.c b/fs/pidfs.c
index 143d0aec16af..33874c891d28 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -107,7 +107,7 @@ struct pidfs_attr {

#if BITS_PER_LONG == 32

-DEFINE_SPINLOCK(pidfs_ino_lock);
+static DEFINE_SPINLOCK(pidfs_ino_lock);
static u64 pidfs_ino_nr = 1;

static inline unsigned long pidfs_ino(u64 ino)
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 12591c95c925..20818b8c7b83 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -93,6 +93,13 @@ static void __forget_cached_acl(struct posix_acl **p)
{
struct posix_acl *old;

+ /*
+ * ACL_DONT_CACHE is expected to be a "const" value and xchg it with
+ * ACL_NOT_CACHED would enable acl caching for the inode -
+ * clearly not what the caller has intended.
+ */
+ if (READ_ONCE(*p) == ACL_DONT_CACHE)
+ return;
old = xchg(p, ACL_NOT_CACHED);
if (!is_uncached_acl(old))
posix_acl_release(old);
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index 2f46f1396744..ea6ec61a0430 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -46,7 +46,7 @@ static const char *proc_ns_get_link(struct dentry *dentry,
const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
struct task_struct *task;
struct path ns_path;
- int error = -EACCES;
+ int error;

if (!dentry)
return ERR_PTR(-ECHILD);
@@ -59,6 +59,7 @@ static const char *proc_ns_get_link(struct dentry *dentry,
if (error)
goto out_put_task;

+ error = -EACCES;
if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
goto out;

@@ -90,6 +91,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl
if (res)
goto out_put_task;

+ res = -EACCES;
if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
res = ns_get_name(name, sizeof(name), task, ns_ops);
if (res >= 0)
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index d6c30f8552e0..95845c877451 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -438,6 +438,7 @@ cifs_alloc_inode(struct super_block *sb)
return NULL;
cifs_inode->cifsAttrs = ATTR_ARCHIVE; /* default */
cifs_inode->time = 0;
+ cifs_inode->time_last_write = 0;
/*
* Until the file is open and we have gotten oplock info back from the
* server, can not assume caching of file data or metadata.
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 82e0adc1dabd..c755d314a819 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -1562,6 +1562,7 @@ struct cifsInodeInfo {
spinlock_t writers_lock;
unsigned int writers; /* Number of writers on this inode */
unsigned long time; /* jiffies of last update of inode */
+ unsigned long time_last_write; /* jiffies of last writable close or truncate */
u64 uniqueid; /* server inode number */
u64 createtime; /* creation time on server */
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index a26a464d6242..ed8c97ca619d 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -915,6 +915,14 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
cifs_set_oplock_level(cifsi, 0);
}

+ if (OPEN_FMODE(cifs_file->f_flags) & FMODE_WRITE) {
+ /* Stamp while open_file_lock is held; covers all close paths
+ * including background I/O. Pairs with smp_load_acquire() in
+ * is_size_safe_to_change().
+ */
+ smp_store_release(&cifsi->time_last_write, jiffies);
+ }
+
spin_unlock(&cifsi->open_file_lock);
spin_unlock(&tcon->open_file_lock);

@@ -1423,11 +1431,12 @@ void smb2_deferred_work_close(struct work_struct *work)
{
struct cifsFileInfo *cfile = container_of(work,
struct cifsFileInfo, deferred.work);
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));

- spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
+ spin_lock(&cinode->deferred_lock);
cifs_del_deferred_close(cfile);
cfile->deferred_close_scheduled = false;
- spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
+ spin_unlock(&cinode->deferred_lock);
_cifsFileInfo_put(cfile, true, false);
}

@@ -3171,13 +3180,26 @@ static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file,
bool from_readdir)
{
+ struct cifs_sb_info *cifs_sb;
+ unsigned long tlw;
+
if (!cifsInode)
return true;

+ cifs_sb = CIFS_SB(cifsInode);
+
if (is_inode_writable(cifsInode) ||
((cifsInode->oplock & CIFS_CACHE_RW_FLG) != 0 && from_readdir)) {
/* This inode is open for write at least once */
- struct cifs_sb_info *cifs_sb = CIFS_SB(cifsInode);
+
+ /*
+ * Readdir data is unreliable when we have writable handles or
+ * an exclusive lease -- never allow it to change i_size, even
+ * on direct-IO mounts where the server's directory metadata
+ * can still lag behind the actual file state.
+ */
+ if (from_readdir)
+ return false;

if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_DIRECT_IO) {
/* since no page cache to corrupt on directio
@@ -3189,8 +3211,40 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file,
return true;

return false;
- } else
- return true;
+ }
+
+ /*
+ * No writable handles open. Check whether we are within the attribute
+ * cache validity window of a recent local modification.
+ *
+ * For the close() path: _cifsFileInfo_put() stamps time_last_write
+ * (via smp_store_release()) before releasing open_file_lock. That
+ * spin_unlock() is a store-release that pairs with the spin_lock()
+ * (load-acquire) in is_inode_writable() above, so if
+ * is_inode_writable() returned false the smp_load_acquire() below is
+ * guaranteed to observe any time_last_write update from a concurrent
+ * close(), covering all close paths including background I/O.
+ *
+ * For the setattr/truncate paths: those callers use smp_store_release()
+ * directly; the smp_load_acquire() below pairs with that store. There
+ * is no shared lock between setattr and readdir, so this relies on
+ * acquire-release semantics alone. The store propagation latency on
+ * weakly-ordered architectures (nanoseconds) is negligible relative to
+ * the acregmax window (seconds) and the readdir RPC round-trip
+ * (milliseconds), making this a sound design choice in practice.
+ *
+ * time_last_write == 0 means the inode has never been written locally;
+ * skip the window check to avoid false positives near boot time when
+ * jiffies is still close to INITIAL_JIFFIES on 32-bit systems.
+ */
+ if (from_readdir) {
+ /* Pairs with smp_store_release() in _cifsFileInfo_put() and setattr. */
+ tlw = smp_load_acquire(&cifsInode->time_last_write);
+ if (tlw && time_before(jiffies, tlw + cifs_sb->ctx->acregmax))
+ return false;
+ }
+
+ return true;
}

void cifs_oplock_break(struct work_struct *work)
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 826d36ed13ec..56e54f8017b0 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -237,6 +237,8 @@ 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);
inode->i_blocks = CIFS_INO_BLOCKS(fattr->cf_bytes);
+ } else if (from_readdir && i_size_read(inode) != fattr->cf_eof) {
+ cifs_i->time = 0;
}

if (S_ISLNK(fattr->cf_mode) && fattr->cf_symlink_target) {
@@ -3180,6 +3182,17 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
rc = 0;

if (attrs->ia_valid & ATTR_SIZE) {
+ if (attrs->ia_size != i_size_read(inode)) {
+ /* Stamp before RPC. On failure the stamp remains: restoring a
+ * stale snapshot could silently erase a concurrent
+ * _cifsFileInfo_put() close stamp. readdir is suppressed
+ * until the stamp expires; stat() bypasses this via the
+ * from_readdir=false path in is_size_safe_to_change() and
+ * always returns an authoritative QUERY_INFO result.
+ * Pairs with smp_load_acquire() in is_size_safe_to_change().
+ */
+ smp_store_release(&cifsInode->time_last_write, jiffies);
+ }
rc = cifs_file_set_size(xid, direntry, full_path,
open_file, attrs->ia_size);
if (rc != 0)
@@ -3358,6 +3371,17 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
}

if (attrs->ia_valid & ATTR_SIZE) {
+ if (attrs->ia_size != i_size_read(inode)) {
+ /* Stamp before RPC. On failure the stamp remains: restoring a
+ * stale snapshot could silently erase a concurrent
+ * _cifsFileInfo_put() close stamp. readdir is suppressed
+ * until the stamp expires; stat() bypasses this via the
+ * from_readdir=false path in is_size_safe_to_change() and
+ * always returns an authoritative QUERY_INFO result.
+ * Pairs with smp_load_acquire() in is_size_safe_to_change().
+ */
+ smp_store_release(&cifsInode->time_last_write, jiffies);
+ }
rc = cifs_file_set_size(xid, direntry, full_path,
cfile, attrs->ia_size);
if (rc != 0)
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index ee1728eec8aa..8aaafc647bbe 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -493,7 +493,7 @@ cifs_del_deferred_close(struct cifsFileInfo *cfile)
void
cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
{
- struct cifsFileInfo *cfile = NULL;
+ struct cifsFileInfo *cfile = NULL, *failed_cfile = NULL;
struct file_list *tmp_list, *tmp_next_list;
LIST_HEAD(file_head);

@@ -510,8 +510,10 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)

tmp_list = kmalloc_obj(struct file_list,
GFP_ATOMIC);
- if (tmp_list == NULL)
+ if (tmp_list == NULL) {
+ failed_cfile = cfile;
break;
+ }
tmp_list->cfile = cfile;
list_add_tail(&tmp_list->list, &file_head);
}
@@ -519,6 +521,9 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
}
spin_unlock(&cifs_inode->open_file_lock);

+ if (failed_cfile)
+ _cifsFileInfo_put(failed_cfile, false, false);
+
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
_cifsFileInfo_put(tmp_list->cfile, false, false);
list_del(&tmp_list->list);
@@ -529,7 +534,7 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
void
cifs_close_all_deferred_files(struct cifs_tcon *tcon)
{
- struct cifsFileInfo *cfile;
+ struct cifsFileInfo *cfile, *failed_cfile = NULL;
struct file_list *tmp_list, *tmp_next_list;
LIST_HEAD(file_head);

@@ -543,8 +548,10 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)

tmp_list = kmalloc_obj(struct file_list,
GFP_ATOMIC);
- if (tmp_list == NULL)
+ if (tmp_list == NULL) {
+ failed_cfile = cfile;
break;
+ }
tmp_list->cfile = cfile;
list_add_tail(&tmp_list->list, &file_head);
}
@@ -552,6 +559,9 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
}
spin_unlock(&tcon->open_file_lock);

+ if (failed_cfile)
+ _cifsFileInfo_put(failed_cfile, true, false);
+
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
_cifsFileInfo_put(tmp_list->cfile, true, false);
list_del(&tmp_list->list);
@@ -600,7 +610,7 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
struct dentry *dentry)
{
struct file_list *tmp_list, *tmp_next_list;
- struct cifsFileInfo *cfile;
+ struct cifsFileInfo *cfile, *failed_cfile = NULL;
LIST_HEAD(file_head);

spin_lock(&tcon->open_file_lock);
@@ -613,14 +623,19 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);

tmp_list = kmalloc_obj(struct file_list, GFP_ATOMIC);
- if (tmp_list == NULL)
+ if (tmp_list == NULL) {
+ failed_cfile = cfile;
break;
+ }
tmp_list->cfile = cfile;
list_add_tail(&tmp_list->list, &file_head);
}
}
spin_unlock(&tcon->open_file_lock);

+ if (failed_cfile)
+ _cifsFileInfo_put(failed_cfile, true, false);
+
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
_cifsFileInfo_put(tmp_list->cfile, true, false);
list_del(&tmp_list->list);
@@ -678,6 +693,8 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
int i, rc = 0;
char *data_end;
struct dfs_referral_level_3 *ref;
+ unsigned int path_consumed;
+ size_t search_name_len;

if (rsp_size < sizeof(*rsp)) {
cifs_dbg(VFS | ONCE,
@@ -724,6 +741,7 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
rc = -ENOMEM;
goto parse_DFS_referrals_exit;
}
+ search_name_len = strlen(searchName);

/* collect necessary data from referrals */
for (i = 0; i < *num_of_nodes; i++) {
@@ -732,21 +750,34 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
struct dfs_info3_param *node = (*target_nodes)+i;

node->flags = le32_to_cpu(rsp->DFSFlags);
+ path_consumed = le16_to_cpu(rsp->PathConsumed);
if (is_unicode) {
- __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
- GFP_KERNEL);
- if (tmp == NULL) {
+ size_t search_name_utf16_len = search_name_len * 2 + 2;
+ __le16 *tmp;
+
+ if (path_consumed > search_name_utf16_len) {
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
+
+ tmp = kmalloc(search_name_utf16_len, GFP_KERNEL);
+ if (!tmp) {
rc = -ENOMEM;
goto parse_DFS_referrals_exit;
}
- cifsConvertToUTF16((__le16 *) tmp, searchName,
+ cifsConvertToUTF16((__le16 *)tmp, searchName,
PATH_MAX, nls_codepage, remap);
- node->path_consumed = cifs_utf16_bytes(tmp,
- le16_to_cpu(rsp->PathConsumed),
- nls_codepage);
+ node->path_consumed = cifs_utf16_bytes(tmp, path_consumed,
+ nls_codepage);
kfree(tmp);
- } else
- node->path_consumed = le16_to_cpu(rsp->PathConsumed);
+ } else {
+ if (path_consumed > search_name_len) {
+ rc = -EINVAL;
+ goto parse_DFS_referrals_exit;
+ }
+
+ node->path_consumed = path_consumed;
+ }

node->server_type = le16_to_cpu(ref->ServerType);
node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c
index 6860eff31693..a2f50bc163a9 100644
--- a/fs/smb/client/smb2file.c
+++ b/fs/smb/client/smb2file.c
@@ -30,6 +30,19 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
u8 *end = (u8 *)err + iov->iov_len;
u32 len;

+ /*
+ * Per [MS-SMB2] section 2.2.2, a STATUS_STOPPED_ON_SYMLINK response has to
+ * carry a Symbolic Link Error Response, so ByteCount cannot be zero. Some
+ * servers (e.g. the macOS built-in SMB server) violate this and return an
+ * empty error response, with both ErrorContextCount and ByteCount set to
+ * zero, i.e. without the symlink target. Detect this and return -ENODATA
+ * so that callers can tell "server did not send the target" apart from a
+ * malformed response, and retrieve the target with FSCTL_GET_REPARSE_POINT
+ * instead.
+ */
+ if (!err->ErrorContextCount && !le32_to_cpu(err->ByteCount))
+ return ERR_PTR(-ENODATA);
+
if (err->ErrorContextCount) {
struct smb2_error_context_rsp *p;

@@ -201,6 +214,14 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov,
oparms->path,
&data->symlink_target);
+ /*
+ * If smb2_parse_symlink_response returned -ENODATA then the
+ * symlink_target was not sent. Treat this as if the SMB2_open()
+ * failed with STATUS_IO_REPARSE_TAG_NOT_HANDLED status, which is
+ * indicated by the -EIO errno.
+ */
+ if (rc == -ENODATA)
+ rc = -EIO;
if (!rc) {
memset(smb2_data, 0, sizeof(*smb2_data));
oparms->create_options |= OPEN_REPARSE_POINT;
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index 6c9c229b91f6..213bc298cdf2 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -792,9 +792,19 @@ static int parse_create_response(struct cifs_open_info_data *data,
rc = smb2_parse_symlink_response(cifs_sb, iov,
full_path,
&data->symlink_target);
- if (rc)
+ if (rc != 0 && rc != -ENODATA)
return rc;
- tag = IO_REPARSE_TAG_SYMLINK;
+ /*
+ * -ENODATA means that the response was parsed but did not contain
+ * the symlink target at all (see symlink_data()). Treat it like
+ * STATUS_IO_REPARSE_TAG_NOT_HANDLED, which does not contain it
+ * either: leave the tag unset and clear rc, so that the caller
+ * retrieves the target with SMB2_OP_GET_REPARSE.
+ */
+ if (rc == -ENODATA)
+ rc = 0;
+ else
+ tag = IO_REPARSE_TAG_SYMLINK;
reparse_point = true;
break;
case STATUS_SUCCESS:
@@ -987,7 +997,14 @@ int smb2_query_path_info(const unsigned int xid,
rc = -EOPNOTSUPP;
}

- if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) {
+ /*
+ * If the symlink was already parsed in create response then it is needed to fix
+ * its type now (after the second call with OPEN_REPARSE_POINT which filled the
+ * data->fi.Attributes). If the symlink was not parsed in create response then
+ * the data->symlink_target was not filled yet and then the type will be fixed
+ * later after data->symlink_target is filled.
+ */
+ if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc && data->symlink_target) {
bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
}
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 02c2f83353e2..6f843c8b83f3 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3581,6 +3581,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data;
u32 out_data_len;
char *buf = NULL;
+ u64 range_start, range_len, range_end;
loff_t l;
int rc;

@@ -3617,13 +3618,21 @@ static int smb3_simple_fallocate_range(unsigned int xid,
goto out;
}

- if (off < le64_to_cpu(tmp_data->file_offset)) {
+ range_start = le64_to_cpu(tmp_data->file_offset);
+ range_len = le64_to_cpu(tmp_data->length);
+ if (check_add_overflow(range_start, range_len, &range_end) ||
+ range_end > S64_MAX) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (off < range_start) {
/*
* We are at a hole. Write until the end of the region
* or until the next allocated data,
* whichever comes next.
*/
- l = le64_to_cpu(tmp_data->file_offset) - off;
+ l = range_start - off;
if (len < l)
l = len;
rc = smb3_simple_fallocate_write_range(xid, tcon,
@@ -3640,11 +3649,13 @@ static int smb3_simple_fallocate_range(unsigned int xid,
* until the end of the data or the end of the region
* we are supposed to fallocate, whichever comes first.
*/
- l = le64_to_cpu(tmp_data->length);
- if (len < l)
- l = len;
- off += l;
- len -= l;
+ if (off < range_end) {
+ l = range_end - off;
+ if (len < l)
+ l = len;
+ off += l;
+ len -= l;
+ }

tmp_data = &tmp_data[1];
out_data_len -= sizeof(struct file_allocated_range_buffer);
diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
index 8347495dbc62..281561f10f13 100644
--- a/fs/smb/server/connection.c
+++ b/fs/smb/server/connection.c
@@ -440,6 +440,8 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn)
/* "+2" for BCC field (ByteCount, 2 bytes) */
#define SMB1_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb_hdr) + 2)
#define SMB2_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb2_pdu))
+#define SMB2_TRANSFORM_MIN_SUPPORTED_PDU_SIZE \
+ (sizeof(struct smb2_transform_hdr) + sizeof(struct smb2_hdr))

/**
* ksmbd_conn_handler_loop() - session thread to listen on new smb requests
@@ -454,6 +456,7 @@ int ksmbd_conn_handler_loop(void *p)
struct ksmbd_conn *conn = (struct ksmbd_conn *)p;
struct ksmbd_transport *t = conn->transport;
unsigned int pdu_size, max_allowed_pdu_size, max_req;
+ __le32 proto;
char hdr_buf[4] = {0,};
int size;

@@ -534,11 +537,14 @@ int ksmbd_conn_handler_loop(void *p)
if (!ksmbd_smb_request(conn))
break;

- if (((struct smb2_hdr *)smb_get_msg(conn->request_buf))->ProtocolId ==
- SMB2_PROTO_NUMBER) {
- if (pdu_size < SMB2_MIN_SUPPORTED_PDU_SIZE)
- break;
- }
+ proto = *(__le32 *)smb_get_msg(conn->request_buf);
+ if (proto == SMB2_PROTO_NUMBER &&
+ pdu_size < SMB2_MIN_SUPPORTED_PDU_SIZE)
+ break;
+
+ if (proto == SMB2_TRANSFORM_PROTO_NUM &&
+ pdu_size < SMB2_TRANSFORM_MIN_SUPPORTED_PDU_SIZE)
+ break;

if (!default_conn_ops.process_fn) {
pr_err("No connection request callback\n");
diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
index 60e7e821c245..3f28dc3b7fc4 100644
--- a/fs/smb/server/oplock.c
+++ b/fs/smb/server/oplock.c
@@ -705,6 +705,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
out:
ksmbd_free_work_struct(work);
ksmbd_conn_r_count_dec(conn);
+ ksmbd_conn_put(conn);
}

/**
@@ -740,7 +741,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
br_info->open_trunc = opinfo->open_trunc;

work->request_buf = (char *)br_info;
- work->conn = conn;
+ work->conn = ksmbd_conn_get(conn);
work->sess = opinfo->sess;

ksmbd_conn_r_count_inc(conn);
@@ -814,6 +815,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
out:
ksmbd_free_work_struct(work);
ksmbd_conn_r_count_dec(conn);
+ ksmbd_conn_put(conn);
}

/**
@@ -853,7 +855,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE);

work->request_buf = (char *)br_info;
- work->conn = conn;
+ work->conn = ksmbd_conn_get(conn);
work->sess = opinfo->sess;

ksmbd_conn_r_count_inc(conn);
diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c
index a1ddca21c47b..29f33ece30a9 100644
--- a/fs/smb/server/smb2misc.c
+++ b/fs/smb/server/smb2misc.c
@@ -399,6 +399,11 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
return 1;
}

+ if (len < __SMB2_HEADER_STRUCTURE_SIZE + sizeof(__le16)) {
+ ksmbd_debug(SMB, "Message is too small for StructureSize2\n");
+ return 1;
+ }
+
if (smb2_req_struct_sizes[command] != pdu->StructureSize2) {
if (!(command == SMB2_OPLOCK_BREAK_HE &&
(le16_to_cpu(pdu->StructureSize2) == OP_BREAK_STRUCT_SIZE_20 ||
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index ef65b2627081..c29850170b31 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -1535,11 +1535,6 @@ static int ntlm_authenticate(struct ksmbd_work *work,
return -EPERM;
}

- /* Check for previous session */
- prev_id = le64_to_cpu(req->PreviousSessionId);
- if (prev_id && prev_id != sess->id)
- destroy_previous_session(conn, user, prev_id);
-
if (sess->state == SMB2_SESSION_VALID) {
/*
* Reuse session if anonymous try to connect
@@ -1579,6 +1574,10 @@ static int ntlm_authenticate(struct ksmbd_work *work,
}
}

+ prev_id = le64_to_cpu(req->PreviousSessionId);
+ if (prev_id && prev_id != sess->id)
+ destroy_previous_session(conn, sess->user, prev_id);
+
/*
* If session state is SMB2_SESSION_VALID, We can assume
* that it is reauthentication. And the user/password
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index fc9937cedb01..0359cd103202 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -595,7 +595,8 @@ static void parse_dacl(struct mnt_idmap *idmap,
static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
struct smb_ace *pndace,
struct smb_fattr *fattr, u16 *num_aces,
- u16 *size, u32 nt_aces_num)
+ u16 *size, u16 existing_nt_aces,
+ bool had_nt_aces)
{
struct posix_acl_entry *pace;
struct smb_sid *sid;
@@ -627,14 +628,14 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,

gid = posix_acl_gid_translate(idmap, pace);
id_to_sid(gid, SIDUNIX_GROUP, sid);
- } else if (pace->e_tag == ACL_OTHER && !nt_aces_num) {
+ } else if (pace->e_tag == ACL_OTHER && !had_nt_aces) {
smb_copy_sid(sid, &sid_everyone);
} else {
kfree(sid);
continue;
}
ntace = pndace;
- for (j = 0; j < nt_aces_num; j++) {
+ for (j = 0; j < existing_nt_aces; j++) {
if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] ==
sid->sub_auth[sid->num_subauth - 1])
goto pass_same_sid;
@@ -649,6 +650,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags,
pace->e_perm, 0777);
if (check_add_overflow(*size, ace_sz, size)) {
+ *size -= ace_sz;
kfree(sid);
break;
}
@@ -663,6 +665,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED,
0x03, pace->e_perm, 0777);
if (check_add_overflow(*size, ace_sz, size)) {
+ *size -= ace_sz;
kfree(sid);
break;
}
@@ -676,7 +679,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
kfree(sid);
}

- if (nt_aces_num)
+ if (had_nt_aces)
return;

posix_default_acl:
@@ -708,6 +711,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b,
pace->e_perm, 0777);
if (check_add_overflow(*size, ace_sz, size)) {
+ *size -= ace_sz;
kfree(sid);
break;
}
@@ -729,6 +733,7 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,
{
struct smb_ace *ntace, *pndace;
u16 nt_num_aces = le16_to_cpu(nt_dacl->num_aces), num_aces = 0;
+ u16 copied_nt_aces;
unsigned short size = 0;
int i;

@@ -738,24 +743,41 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,
for (i = 0; i < nt_num_aces; i++) {
unsigned short nt_ace_size;

- if (offsetof(struct smb_ace, access_req) > aces_size)
+ if (aces_size < offsetof(struct smb_ace, sid) +
+ CIFS_SID_BASE_SIZE)
break;

nt_ace_size = le16_to_cpu(ntace->size);
- if (nt_ace_size > aces_size)
+ if (nt_ace_size > aces_size ||
+ nt_ace_size < offsetof(struct smb_ace, sid) +
+ CIFS_SID_BASE_SIZE)
break;

+ if (ntace->sid.num_subauth == 0 ||
+ ntace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
+ nt_ace_size < offsetof(struct smb_ace, sid) +
+ CIFS_SID_BASE_SIZE +
+ sizeof(__le32) *
+ ntace->sid.num_subauth)
+ goto next_ace;
+
memcpy((char *)pndace + size, ntace, nt_ace_size);
- if (check_add_overflow(size, nt_ace_size, &size))
+ if (check_add_overflow(size, nt_ace_size, &size)) {
+ size -= nt_ace_size;
break;
+ }
+ num_aces++;
+
+next_ace:
aces_size -= nt_ace_size;
ntace = (struct smb_ace *)((char *)ntace + nt_ace_size);
- num_aces++;
}
}

+ copied_nt_aces = num_aces;
set_posix_acl_entries_dacl(idmap, pndace, fattr,
- &num_aces, &size, nt_num_aces);
+ &num_aces, &size, copied_nt_aces,
+ nt_num_aces != 0);
pndacl->num_aces = cpu_to_le16(num_aces);
pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size);
}
@@ -773,7 +795,7 @@ static void set_mode_dacl(struct mnt_idmap *idmap,

if (fattr->cf_acls) {
set_posix_acl_entries_dacl(idmap, pndace, fattr,
- &num_aces, &size, num_aces);
+ &num_aces, &size, num_aces, false);
goto out;
}

diff --git a/fs/super.c b/fs/super.c
index 97df9e574d8b..3d87388a20a8 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1143,18 +1143,35 @@ void emergency_remount(void)
}
}

+static inline bool get_active_super(struct super_block *sb)
+{
+ bool active = false;
+
+ if (super_lock_excl(sb)) {
+ active = atomic_inc_not_zero(&sb->s_active);
+ super_unlock_excl(sb);
+ }
+ return active;
+}
+
static void do_thaw_all_callback(struct super_block *sb, void *unused)
{
+ if (!get_active_super(sb))
+ return;
+
+ /* fs_bdev_thaw() acquires s_umount so it must not be held here */
if (IS_ENABLED(CONFIG_BLOCK))
while (sb->s_bdev && !bdev_thaw(sb->s_bdev))
pr_warn("Emergency Thaw on %pg\n", sb->s_bdev);
- thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL);
- return;
+
+ if (super_lock_excl(sb))
+ thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE, NULL);
+ deactivate_super(sb);
}

static void do_thaw_all(struct work_struct *work)
{
- __iterate_supers(do_thaw_all_callback, NULL, SUPER_ITER_EXCL);
+ __iterate_supers(do_thaw_all_callback, NULL, SUPER_ITER_UNLOCKED);
kfree(work);
printk(KERN_WARNING "Emergency Thaw complete\n");
}
@@ -1175,17 +1192,6 @@ void emergency_thaw_all(void)
}
}

-static inline bool get_active_super(struct super_block *sb)
-{
- bool active = false;
-
- if (super_lock_excl(sb)) {
- active = atomic_inc_not_zero(&sb->s_active);
- super_unlock_excl(sb);
- }
- return active;
-}
-
static const char *filesystems_freeze_ptr = "filesystems_freeze";

static void filesystems_freeze_callback(struct super_block *sb, void *freeze_all_ptr)
diff --git a/fs/xfs/scrub/cow_repair.c b/fs/xfs/scrub/cow_repair.c
index c25716fc4fee..acd6e80e8e98 100644
--- a/fs/xfs/scrub/cow_repair.c
+++ b/fs/xfs/scrub/cow_repair.c
@@ -80,12 +80,6 @@ struct xrep_cow {
unsigned int next_bno;
};

-/* CoW staging extent. */
-struct xrep_cow_extent {
- xfs_fsblock_t fsbno;
- xfs_extlen_t len;
-};
-
/*
* Mark the part of the file range that corresponds to the given physical
* space. Caller must ensure that the physical range is within xc->irec.
@@ -401,22 +395,21 @@ xrep_cow_find_bad_rt(
STATIC int
xrep_cow_alloc(
struct xfs_scrub *sc,
- xfs_extlen_t maxlen,
- struct xrep_cow_extent *repl)
+ struct xfs_bmbt_irec *del)
{
struct xfs_alloc_arg args = {
.tp = sc->tp,
.mp = sc->mp,
.oinfo = XFS_RMAP_OINFO_SKIP_UPDATE,
.minlen = 1,
- .maxlen = maxlen,
+ .maxlen = del->br_blockcount,
.prod = 1,
.resv = XFS_AG_RESV_NONE,
.datatype = XFS_ALLOC_USERDATA,
};
int error;

- error = xfs_trans_reserve_more(sc->tp, maxlen, 0);
+ error = xfs_trans_reserve_more(sc->tp, del->br_blockcount, 0);
if (error)
return error;

@@ -429,8 +422,8 @@ xrep_cow_alloc(

xfs_refcount_alloc_cow_extent(sc->tp, false, args.fsbno, args.len);

- repl->fsbno = args.fsbno;
- repl->len = args.len;
+ del->br_startblock = args.fsbno;
+ del->br_blockcount = args.len;
return 0;
}

@@ -441,10 +434,12 @@ xrep_cow_alloc(
STATIC int
xrep_cow_alloc_rt(
struct xfs_scrub *sc,
- xfs_extlen_t maxlen,
- struct xrep_cow_extent *repl)
+ struct xfs_bmbt_irec *del)
{
- xfs_rtxlen_t maxrtx = xfs_rtb_to_rtx(sc->mp, maxlen);
+ xfs_fsblock_t fsbno;
+ xfs_rtxlen_t maxrtx =
+ min(U32_MAX, xfs_blen_to_rtbxlen(sc->mp, del->br_blockcount));
+ xfs_extlen_t len;
int error;

error = xfs_trans_reserve_more(sc->tp, 0, maxrtx);
@@ -452,11 +447,14 @@ xrep_cow_alloc_rt(
return error;

error = xfs_rtallocate_rtgs(sc->tp, NULLRTBLOCK, 1, maxrtx, 1, false,
- false, &repl->fsbno, &repl->len);
+ false, &fsbno, &len);
if (error)
return error;

- xfs_refcount_alloc_cow_extent(sc->tp, true, repl->fsbno, repl->len);
+ xfs_refcount_alloc_cow_extent(sc->tp, true, fsbno, len);
+
+ del->br_startblock = fsbno;
+ del->br_blockcount = len;
return 0;
}

@@ -470,19 +468,19 @@ static inline int
xrep_cow_find_mapping(
struct xrep_cow *xc,
struct xfs_iext_cursor *icur,
- xfs_fileoff_t startoff,
- struct xfs_bmbt_irec *got)
+ xfs_fileoff_t badoff,
+ xfs_extlen_t badlen,
+ struct xfs_bmbt_irec *got,
+ struct xfs_bmbt_irec *rep)
{
struct xfs_inode *ip = xc->sc->ip;
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);

- if (!xfs_iext_lookup_extent(ip, ifp, startoff, icur, got))
+ if (!xfs_iext_lookup_extent(ip, ifp, badoff, icur, got))
goto bad;
+ memcpy(rep, got, sizeof(*rep));

- if (got->br_startoff > startoff)
- goto bad;
-
- if (got->br_blockcount == 0)
+ if (got->br_startoff > badoff)
goto bad;

if (isnullstartblock(got->br_startblock))
@@ -491,6 +489,24 @@ xrep_cow_find_mapping(
if (xfs_bmap_is_written_extent(got))
goto bad;

+ if (got->br_startoff < badoff) {
+ const int64_t delta = badoff - got->br_startoff;
+
+ rep->br_blockcount -= delta;
+ rep->br_startoff += delta;
+ rep->br_startblock += delta;
+ }
+
+ if (got->br_startoff + got->br_blockcount > badoff + badlen) {
+ const int64_t delta = (got->br_startoff + got->br_blockcount) -
+ (badoff + badlen);
+
+ rep->br_blockcount -= delta;
+ }
+
+ if (got->br_blockcount == 0)
+ goto bad;
+
return 0;
bad:
ASSERT(0);
@@ -501,46 +517,92 @@ xrep_cow_find_mapping(
#define REPLACE_RIGHT_SIDE (1U << 1)

/*
- * Given a CoW fork mapping @got and a replacement mapping @repl, remap the
- * beginning of @got with the space described by @rep.
+ * Given a CoW fork mapping @got and a replacement mapping @rep, map the space
+ * described by @rep into the cow fork, pushing aside @got as necessary. @icur
+ * must point to iext tree leaf containing @got.
*/
static inline void
xrep_cow_replace_mapping(
- struct xfs_inode *ip,
- struct xfs_iext_cursor *icur,
- const struct xfs_bmbt_irec *got,
- const struct xrep_cow_extent *repl)
+ struct xfs_inode *ip,
+ struct xfs_iext_cursor *icur,
+ struct xfs_bmbt_irec *got,
+ struct xfs_bmbt_irec *rep)
{
- struct xfs_bmbt_irec new = *got; /* struct copy */
+ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK);
+ xfs_fileoff_t rep_endoff =
+ rep->br_startoff + rep->br_blockcount;
+ xfs_fileoff_t got_endoff =
+ got->br_startoff + got->br_blockcount;
+ uint32_t state = BMAP_COWFORK;

- ASSERT(repl->len > 0);
+ ASSERT(rep->br_blockcount > 0);
ASSERT(!isnullstartblock(got->br_startblock));
+ ASSERT(got->br_startoff <= rep->br_startoff);
+ ASSERT(got_endoff >= rep_endoff);
+
+ trace_xrep_cow_replace_mapping(ip, got, rep);

- trace_xrep_cow_replace_mapping(ip, got, repl->fsbno, repl->len);
+ if (got->br_startoff == rep->br_startoff)
+ state |= BMAP_LEFT_FILLING;
+ if (got_endoff == rep_endoff)
+ state |= BMAP_RIGHT_FILLING;

- if (got->br_blockcount == repl->len) {
+ switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) {
+ case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
/*
- * The new extent is a complete replacement for the existing
- * extent. Update the COW fork record.
+ * Replacement matches the whole mapping, update the record.
*/
- new.br_startblock = repl->fsbno;
- xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new);
- return;
- }
+ xfs_iext_update_extent(ip, state, icur, rep);
+ break;
+ case BMAP_LEFT_FILLING:
+ /*
+ * Replace the first part of the mapping: Update the cursor
+ * position with the new mapping, then add a record with the
+ * tail of the old mapping.
+ */
+ got->br_startoff = rep_endoff;
+ got->br_blockcount -= rep->br_blockcount;
+ got->br_startblock += rep->br_blockcount;
+
+ xfs_iext_update_extent(ip, state, icur, rep);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, got, state);
+ break;
+ case BMAP_RIGHT_FILLING:
+ /*
+ * Replacing the last part of the mapping. Shorten the current
+ * mapping then add a record with the new mapping.
+ */
+ got->br_blockcount -= rep->br_blockcount;

- /*
- * The new extent can replace the beginning of the COW fork record.
- * Move the left side of @got upwards, then insert the new record.
- */
- new.br_startoff += repl->len;
- new.br_startblock += repl->len;
- new.br_blockcount -= repl->len;
- xfs_iext_update_extent(ip, BMAP_COWFORK, icur, &new);
-
- new.br_startoff = got->br_startoff;
- new.br_startblock = repl->fsbno;
- new.br_blockcount = repl->len;
- xfs_iext_insert(ip, icur, &new, BMAP_COWFORK);
+ xfs_iext_update_extent(ip, state, icur, got);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, rep, state);
+ break;
+ case 0:
+ /*
+ * Replacing the middle of the extent. Shorten the current
+ * mapping, add a new record with the new mapping, and add a
+ * second new record with the tail of the old mapping.
+ */
+ got->br_blockcount = rep->br_startoff - got->br_startoff;
+
+ struct xfs_bmbt_irec new = {
+ .br_startoff = rep_endoff,
+ .br_blockcount = got_endoff - rep_endoff,
+ .br_state = got->br_state,
+ .br_startblock = got->br_startblock +
+ rep->br_blockcount +
+ got->br_blockcount,
+ };
+
+ xfs_iext_update_extent(ip, state, icur, got);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, rep, state);
+ xfs_iext_next(ifp, icur);
+ xfs_iext_insert(ip, icur, &new, state);
+ break;
+ }
}

/*
@@ -554,33 +616,30 @@ xrep_cow_replace_range(
xfs_extlen_t *blockcount)
{
struct xfs_iext_cursor icur;
- struct xrep_cow_extent repl;
- struct xfs_bmbt_irec got;
+ struct xfs_bmbt_irec got, rep;
struct xfs_scrub *sc = xc->sc;
- xfs_fileoff_t nextoff;
- xfs_extlen_t alloc_len;
+ xfs_fsblock_t old_fsbno;
int error;

/*
- * Put the existing CoW fork mapping in @got. If @got ends before
- * @rep, truncate @rep so we only replace one extent mapping at a time.
+ * Put the existing CoW fork mapping in @got, and put in @rep the
+ * contents of @got trimmed to @startoff/@blockcount. We only want
+ * to replace the bad region, and only one mapping at a time.
*/
- error = xrep_cow_find_mapping(xc, &icur, startoff, &got);
+ error = xrep_cow_find_mapping(xc, &icur, startoff, *blockcount, &got,
+ &rep);
if (error)
return error;
- nextoff = min(startoff + *blockcount,
- got.br_startoff + got.br_blockcount);
+ old_fsbno = rep.br_startblock;

/*
* Allocate a replacement extent. If we don't fill all the blocks,
* shorten the quantity that will be deleted in this step.
*/
- alloc_len = min_t(xfs_fileoff_t, XFS_MAX_BMBT_EXTLEN,
- nextoff - startoff);
if (XFS_IS_REALTIME_INODE(sc->ip))
- error = xrep_cow_alloc_rt(sc, alloc_len, &repl);
+ error = xrep_cow_alloc_rt(sc, &rep);
else
- error = xrep_cow_alloc(sc, alloc_len, &repl);
+ error = xrep_cow_alloc(sc, &rep);
if (error)
return error;

@@ -588,7 +647,7 @@ xrep_cow_replace_range(
* Replace the old mapping with the new one, and commit the metadata
* changes made so far.
*/
- xrep_cow_replace_mapping(sc->ip, &icur, &got, &repl);
+ xrep_cow_replace_mapping(sc->ip, &icur, &got, &rep);

xfs_inode_set_cowblocks_tag(sc->ip);
error = xfs_defer_finish(&sc->tp);
@@ -597,15 +656,15 @@ xrep_cow_replace_range(

/* Note the old CoW staging extents; we'll reap them all later. */
if (XFS_IS_REALTIME_INODE(sc->ip))
- error = xrtb_bitmap_set(&xc->old_cowfork_rtblocks,
- got.br_startblock, repl.len);
+ error = xrtb_bitmap_set(&xc->old_cowfork_rtblocks, old_fsbno,
+ rep.br_blockcount);
else
- error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks,
- got.br_startblock, repl.len);
+ error = xfsb_bitmap_set(&xc->old_cowfork_fsblocks, old_fsbno,
+ rep.br_blockcount);
if (error)
return error;

- *blockcount = repl.len;
+ *blockcount = rep.br_blockcount;
return 0;
}

diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index 286c5f5e0544..fab3ce323e4c 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -2672,9 +2672,9 @@ TRACE_EVENT(xrep_cow_mark_file_range,
);

TRACE_EVENT(xrep_cow_replace_mapping,
- TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *irec,
- xfs_fsblock_t new_startblock, xfs_extlen_t new_blockcount),
- TP_ARGS(ip, irec, new_startblock, new_blockcount),
+ TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *got,
+ const struct xfs_bmbt_irec *rep),
+ TP_ARGS(ip, got, rep),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
@@ -2682,28 +2682,34 @@ TRACE_EVENT(xrep_cow_replace_mapping,
__field(xfs_fileoff_t, startoff)
__field(xfs_filblks_t, blockcount)
__field(xfs_exntst_t, state)
+ __field(xfs_fileoff_t, new_startoff)
__field(xfs_fsblock_t, new_startblock)
__field(xfs_extlen_t, new_blockcount)
+ __field(xfs_exntst_t, new_state)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
- __entry->startoff = irec->br_startoff;
- __entry->startblock = irec->br_startblock;
- __entry->blockcount = irec->br_blockcount;
- __entry->state = irec->br_state;
- __entry->new_startblock = new_startblock;
- __entry->new_blockcount = new_blockcount;
+ __entry->startoff = got->br_startoff;
+ __entry->startblock = got->br_startblock;
+ __entry->blockcount = got->br_blockcount;
+ __entry->state = got->br_state;
+ __entry->new_startoff = rep->br_startoff;
+ __entry->new_startblock = rep->br_startblock;
+ __entry->new_blockcount = rep->br_blockcount;
+ __entry->new_state = rep->br_state;
),
- TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startblock 0x%llx new_fsbcount 0x%x",
+ TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startoff 0x%llx new_startblock 0x%llx new_fsbcount 0x%x new_state 0x%x",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->startoff,
__entry->startblock,
__entry->blockcount,
__entry->state,
+ __entry->new_startoff,
__entry->new_startblock,
- __entry->new_blockcount)
+ __entry->new_blockcount,
+ __entry->new_state)
);

TRACE_EVENT(xrep_cow_free_staging,
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index f83f28cae207..877b5ca87e95 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -2608,13 +2608,13 @@ struct drm_tile_group {
struct kref refcount;
struct drm_device *dev;
int id;
- u8 group_data[8];
+ u8 group_data[9];
};

struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev,
- const char topology[8]);
+ const char topology_id[9]);
struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev,
- const char topology[8]);
+ const char topology_id[9]);
void drm_mode_put_tile_group(struct drm_device *dev,
struct drm_tile_group *tg);

diff --git a/include/drm/drm_utils.h b/include/drm/drm_utils.h
index 6a46f755daba..7e077484c5bb 100644
--- a/include/drm/drm_utils.h
+++ b/include/drm/drm_utils.h
@@ -19,6 +19,7 @@ int drm_get_panel_orientation_quirk(int width, int height);
struct drm_panel_backlight_quirk {
u16 min_brightness;
u32 brightness_mask;
+ bool force_pwm;
};

const struct drm_panel_backlight_quirk *
diff --git a/include/drm/ttm/ttm_backup.h b/include/drm/ttm/ttm_backup.h
index 29b9c855af77..49efa713e87c 100644
--- a/include/drm/ttm/ttm_backup.h
+++ b/include/drm/ttm/ttm_backup.h
@@ -13,9 +13,8 @@
* ttm_backup_handle_to_page_ptr() - Convert handle to struct page pointer
* @handle: The handle to convert.
*
- * Converts an opaque handle received from the
- * ttm_backup_backup_page() function to an (invalid)
- * struct page pointer suitable for a struct page array.
+ * Converts an opaque handle received from a ttm_backup_backup_*()
+ * function to an (invalid) struct page pointer suitable for a struct page array.
*
* Return: An (invalid) struct page pointer.
*/
@@ -59,9 +58,10 @@ int ttm_backup_copy_page(struct file *backup, struct page *dst,
pgoff_t handle, bool intr, gfp_t additional_gfp);

s64
-ttm_backup_backup_page(struct file *backup, struct page *page,
- bool writeback, pgoff_t idx, gfp_t page_gfp,
- gfp_t alloc_gfp);
+ttm_backup_backup_folio(struct file *backup, struct folio *folio,
+ unsigned int order, bool writeback, pgoff_t idx,
+ gfp_t folio_gfp, gfp_t alloc_gfp,
+ pgoff_t *nr_pages_backed);

void ttm_backup_fini(struct file *backup);

diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h
index 81e603839c4a..62d67dae8b70 100644
--- a/include/linux/arm_ffa.h
+++ b/include/linux/arm_ffa.h
@@ -445,7 +445,7 @@ ffa_mem_desc_offset(struct ffa_mem_region *buf, int count, u32 ffa_version)
if (!FFA_MEM_REGION_HAS_EP_MEM_OFFSET(ffa_version))
offset += offsetof(struct ffa_mem_region, ep_mem_offset);
else
- offset += sizeof(struct ffa_mem_region);
+ offset += buf->ep_mem_offset;

return offset;
}
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 803b0183d98d..45abb3722d30 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -133,8 +133,8 @@ enum audit_nfcfgop {
AUDIT_NFT_OP_INVALID,
};

-extern int __init audit_register_class(int class, unsigned *list);
-extern int audit_classify_syscall(int abi, unsigned syscall);
+extern int __init audit_register_class(int class, unsigned int *list);
+extern int audit_classify_syscall(int abi, unsigned int syscall);
extern int audit_classify_arch(int arch);

/* audit_names->type values */
diff --git a/include/linux/audit_arch.h b/include/linux/audit_arch.h
index 2b8153791e6a..a35069a6c15d 100644
--- a/include/linux/audit_arch.h
+++ b/include/linux/audit_arch.h
@@ -21,13 +21,13 @@ enum auditsc_class_t {
AUDITSC_NVALS /* count */
};

-extern int audit_classify_compat_syscall(int abi, unsigned syscall);
+extern int audit_classify_compat_syscall(int abi, unsigned int syscall);

/* only for compat system calls */
-extern unsigned compat_write_class[];
-extern unsigned compat_read_class[];
-extern unsigned compat_dir_class[];
-extern unsigned compat_chattr_class[];
-extern unsigned compat_signal_class[];
+extern unsigned int compat_write_class[];
+extern unsigned int compat_read_class[];
+extern unsigned int compat_dir_class[];
+extern unsigned int compat_chattr_class[];
+extern unsigned int compat_signal_class[];

#endif
diff --git a/include/linux/bnge/hsi.h b/include/linux/bnge/hsi.h
index 8ea13d5407ee..1f7bd96415a5 100644
--- a/include/linux/bnge/hsi.h
+++ b/include/linux/bnge/hsi.h
@@ -8317,8 +8317,7 @@ struct hwrm_ring_alloc_output {
__le16 req_type;
__le16 seq_id;
__le16 resp_len;
- __le16 ring_id;
- __le16 logical_ring_id;
+ __le32 ring_id;
u8 push_buffer_index;
#define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PING_BUFFER 0x0UL
#define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PONG_BUFFER 0x1UL
@@ -8345,10 +8344,10 @@ struct hwrm_ring_free_input {
u8 flags;
#define RING_FREE_REQ_FLAGS_VIRTIO_RING_VALID 0x1UL
#define RING_FREE_REQ_FLAGS_LAST RING_FREE_REQ_FLAGS_VIRTIO_RING_VALID
- __le16 ring_id;
+ __le16 unused_1;
__le32 prod_idx;
__le32 opaque;
- __le32 unused_1;
+ __le32 ring_id;
};

/* hwrm_ring_free_output (size:128b/16B) */
diff --git a/include/linux/bootconfig.h b/include/linux/bootconfig.h
index 692a5acc2ffc..1c7f3b74ffcf 100644
--- a/include/linux/bootconfig.h
+++ b/include/linux/bootconfig.h
@@ -265,6 +265,9 @@ static inline struct xbc_node * __init xbc_node_get_subkey(struct xbc_node *node
int __init xbc_node_compose_key_after(struct xbc_node *root,
struct xbc_node *node, char *buf, size_t size);

+/* Render key/value pairs under @root as a flat cmdline string */
+int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root);
+
/**
* xbc_node_compose_key() - Compose full key string of the XBC node
* @node: An XBC node.
diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h
index 935dba3633b5..2719587b7433 100644
--- a/include/linux/firmware/intel/stratix10-smc.h
+++ b/include/linux/firmware/intel/stratix10-smc.h
@@ -67,6 +67,9 @@
* INTEL_SIP_SMC_STATUS_REJECTED:
* Secure monitor software reject the service client's request.
*
+ * INTEL_SIP_SMC_STATUS_NO_RESPONSE:
+ * Secure monitor software has no response for the request yet.
+ *
* INTEL_SIP_SMC_STATUS_ERROR:
* There is error during the process of service request.
*
@@ -77,6 +80,7 @@
#define INTEL_SIP_SMC_STATUS_OK 0x0
#define INTEL_SIP_SMC_STATUS_BUSY 0x1
#define INTEL_SIP_SMC_STATUS_REJECTED 0x2
+#define INTEL_SIP_SMC_STATUS_NO_RESPONSE 0x3
#define INTEL_SIP_SMC_STATUS_ERROR 0x4
#define INTEL_SIP_SMC_RSU_ERROR 0x7

diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h
index 54712ec61ffb..f6b235cd72b4 100644
--- a/include/linux/fscrypt.h
+++ b/include/linux/fscrypt.h
@@ -57,6 +57,9 @@ struct fscrypt_name {
/* Maximum value for the third parameter of fscrypt_operations.set_context(). */
#define FSCRYPT_SET_CONTEXT_MAX_SIZE 40

+/* Maximum supported number of block devices per filesystem */
+#define FSCRYPT_MAX_DEVICES 8
+
#ifdef CONFIG_FS_ENCRYPTION

/* Crypto operations for filesystems */
@@ -181,21 +184,20 @@ struct fscrypt_operations {
bool (*has_stable_inodes)(struct super_block *sb);

/*
- * Return an array of pointers to the block devices to which the
- * filesystem may write encrypted file contents, NULL if the filesystem
- * only has a single such block device, or an ERR_PTR() on error.
+ * Retrieve the list of block devices to which the filesystem may write
+ * encrypted file contents.
*
- * On successful non-NULL return, *num_devs is set to the number of
- * devices in the returned array. The caller must free the returned
- * array using kfree().
+ * This writes the block_device pointers to @devs and returns the count
+ * (between 1 and FSCRYPT_MAX_DEVICES inclusively).
*
* If the filesystem can use multiple block devices (other than block
* devices that aren't used for encrypted file contents, such as
* external journal devices), and wants to support inline encryption,
* then it must implement this function. Otherwise it's not needed.
*/
- struct block_device **(*get_devices)(struct super_block *sb,
- unsigned int *num_devs);
+ unsigned int (*get_devices)(
+ struct super_block *sb,
+ struct block_device *devs[FSCRYPT_MAX_DEVICES]);
};

int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name,
diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h
index 1fe5665a9d02..07ea563f524e 100644
--- a/include/linux/intel_vsec.h
+++ b/include/linux/intel_vsec.h
@@ -135,8 +135,6 @@ struct intel_vsec_platform_info {
* struct intel_vsec_device - Auxbus specific device information
* @auxdev: auxbus device struct for auxbus access
* @dev: struct device associated with the device
- * @resource: PCI discovery resources (BAR windows), one per discovery
- * instance. Valid only when @src == INTEL_VSEC_DISC_PCI
* @acpi_disc: ACPI discovery tables, each entry is two QWORDs
* in little-endian format as defined by the PMT ACPI spec.
* Valid only when @src == INTEL_VSEC_DISC_ACPI.
@@ -149,11 +147,12 @@ struct intel_vsec_platform_info {
* @quirks: specified quirks
* @base_addr: base address of entries (if specified)
* @cap_id: the enumerated id of the vsec feature
+ * @resource: PCI discovery resources (BAR windows), one per discovery
+ * instance. Valid only when @src == INTEL_VSEC_DISC_PCI
*/
struct intel_vsec_device {
struct auxiliary_device auxdev;
struct device *dev;
- struct resource *resource;
u32 (*acpi_disc)[4];
enum intel_vsec_disc_source src;
struct ida *ida;
@@ -164,6 +163,7 @@ struct intel_vsec_device {
unsigned long quirks;
u64 base_addr;
unsigned long cap_id;
+ struct resource resource[] __counted_by(num_resources);
};

/**
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index 815e908c4135..7c9d66729c60 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -135,9 +135,10 @@ static inline bool movable_node_is_enabled(void)
return movable_node_enabled;
}

-extern void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap);
+extern void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap,
+ struct dev_pagemap *pgmap);
extern void __remove_pages(unsigned long start_pfn, unsigned long nr_pages,
- struct vmem_altmap *altmap);
+ struct vmem_altmap *altmap, struct dev_pagemap *pgmap);

/* reasonably generic interface to expand the physical pages */
extern int __add_pages(int nid, unsigned long start_pfn, unsigned long nr_pages,
@@ -307,7 +308,8 @@ extern int sparse_add_section(int nid, unsigned long pfn,
unsigned long nr_pages, struct vmem_altmap *altmap,
struct dev_pagemap *pgmap);
extern void sparse_remove_section(unsigned long pfn, unsigned long nr_pages,
- struct vmem_altmap *altmap);
+ struct vmem_altmap *altmap,
+ struct dev_pagemap *pgmap);
extern struct zone *zone_for_pfn_range(enum mmop online_type,
int nid, struct memory_group *group, unsigned long start_pfn,
unsigned long nr_pages);
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index 49f3ad4b1a7c..dfd98e17fe68 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -12203,18 +12203,7 @@ struct mlx5_ifc_mcia_reg_bits {

u8 reserved_at_60[0x20];

- u8 dword_0[0x20];
- u8 dword_1[0x20];
- u8 dword_2[0x20];
- u8 dword_3[0x20];
- u8 dword_4[0x20];
- u8 dword_5[0x20];
- u8 dword_6[0x20];
- u8 dword_7[0x20];
- u8 dword_8[0x20];
- u8 dword_9[0x20];
- u8 dword_10[0x20];
- u8 dword_11[0x20];
+ u8 dwords[0x400];
};

struct mlx5_ifc_dcbx_param_bits {
diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h
index 5a40252b8334..f865491c4f2c 100644
--- a/include/linux/seqlock.h
+++ b/include/linux/seqlock.h
@@ -1259,14 +1259,15 @@ static __always_inline void __scoped_seqlock_cleanup(struct ss_tmp *sst)

extern void __scoped_seqlock_invalid_target(void);

-#if (defined(CONFIG_CC_IS_GCC) && CONFIG_GCC_VERSION < 90000) || defined(CONFIG_KASAN)
+#if (defined(CONFIG_CC_IS_GCC) && CONFIG_GCC_VERSION < 90000) || \
+ defined(CONFIG_KASAN) || defined(CONFIG_UBSAN_ALIGNMENT)
/*
* For some reason some GCC-8 architectures (nios2, alpha) have trouble
* determining that the ss_done state is impossible in __scoped_seqlock_next()
* below.
*
- * Similarly KASAN is known to confuse compilers enough to break this. But we
- * don't care about code quality for KASAN builds anyway.
+ * Similarly KASAN and UBSAN_ALIGNMENT are known to confuse compilers enough
+ * to break this. But we don't care about code quality for such builds anyway.
*/
static inline void __scoped_seqlock_bug(void) { }
#else
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index b639a6fafcbc..633af1b83ff8 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -138,8 +138,23 @@ void xdr_terminate_string(const struct xdr_buf *, const u32);
size_t xdr_buf_pagecount(const struct xdr_buf *buf);
int xdr_alloc_bvec(struct xdr_buf *buf, gfp_t gfp);
void xdr_free_bvec(struct xdr_buf *buf);
-unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
- const struct xdr_buf *xdr);
+int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
+ const struct xdr_buf *xdr);
+int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset,
+ unsigned int len, struct scatterlist *sg, unsigned int nsg);
+int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset,
+ unsigned int len, struct scatterlist *sg_head,
+ unsigned int sg_head_nents,
+ struct scatterlist **sg_overflow, gfp_t gfp);
+
+/*
+ * Inline scatterlist entries for xdr_buf_to_sg_alloc(). Sized to cover the
+ * head kvec, tail kvec, and a few page fragments without any heap allocation.
+ */
+enum {
+ XDR_BUF_TO_SG_NENTS = 8,
+};
+

static inline __be32 *xdr_encode_array(__be32 *p, const void *s, unsigned int len)
{
diff --git a/include/linux/vtime.h b/include/linux/vtime.h
index 29dd5b91dd7d..3fc04b849e4e 100644
--- a/include/linux/vtime.h
+++ b/include/linux/vtime.h
@@ -32,11 +32,17 @@ extern void vtime_account_irq(struct task_struct *tsk, unsigned int offset);
extern void vtime_account_softirq(struct task_struct *tsk);
extern void vtime_account_hardirq(struct task_struct *tsk);
extern void vtime_flush(struct task_struct *tsk);
+extern void vtime_reset(void);
+extern void vtime_dyntick_start(void);
+extern void vtime_dyntick_stop(void);
#else /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
static inline void vtime_account_irq(struct task_struct *tsk, unsigned int offset) { }
static inline void vtime_account_softirq(struct task_struct *tsk) { }
static inline void vtime_account_hardirq(struct task_struct *tsk) { }
static inline void vtime_flush(struct task_struct *tsk) { }
+static inline void vtime_reset(void) { }
+static inline void vtime_dyntick_start(void) { }
+static inline void vtime_dyntick_stop(void) { }
#endif

/*
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
index f26c323e9c96..54a2d9620ed5 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -333,8 +333,10 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module);
* An error is returned if the module is no longer loaded on any attempts
* to register it.
*/
+#define v4l2_async_register_subdev_sensor(sd) \
+ __v4l2_async_register_subdev_sensor(sd, THIS_MODULE)
int __must_check
-v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd);
+__v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module);

/**
* v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 4424d481d7f7..4b4f4c15c53a 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -1093,8 +1093,8 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file,
* @ppos: file handle position tracking pointer
* @nonblock: mode selector (1 means blocking calls, 0 means nonblocking)
*/
-size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
- loff_t *ppos, int nonblock);
+ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblock);
/**
* vb2_write() - implements write() syscall logic.
* @q: pointer to &struct vb2_queue with videobuf2 queue.
@@ -1103,8 +1103,8 @@ size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
* @ppos: file handle position tracking pointer
* @nonblock: mode selector (1 means blocking calls, 0 means nonblocking)
*/
-size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
- loff_t *ppos, int nonblock);
+ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
+ loff_t *ppos, int nonblock);

/**
* typedef vb2_thread_fnc - callback function for use with vb2_thread.
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index aa554c34f9ec..02ba1cba6b23 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -2433,6 +2433,7 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
bool persistent);
void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
u8 *name, u8 name_len);
+u8 hci_to_mgmt_reason(u8 err);
void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 reason,
bool mgmt_connected);
diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h
index c05882476900..7e40d3c5f5a9 100644
--- a/include/net/bluetooth/rfcomm.h
+++ b/include/net/bluetooth/rfcomm.h
@@ -229,6 +229,9 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
u8 bit_rate, u8 data_bits, u8 stop_bits,
u8 parity, u8 flow_ctrl_settings,
u8 xon_char, u8 xoff_char, u16 param_mask);
+int rfcomm_dlc_send_rpn(struct rfcomm_dlc *d, u8 bit_rate, u8 data_bits,
+ u8 stop_bits, u8 parity, u8 flow_ctrl_settings,
+ u8 xon_char, u8 xoff_char, u16 param_mask);

/* ---- RFCOMM DLCs (channels) ---- */
struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio);
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 9d3639ff9c28..909fb76751c5 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -6990,7 +6990,7 @@ struct wireless_dev {
enum ieee80211_bss_type conn_bss_type;
u32 conn_owner_nlportid;

- struct work_struct disconnect_wk;
+ struct wiphy_work disconnect_wk;
u8 disconnect_bssid[ETH_ALEN];

struct list_head event_list;
@@ -7027,7 +7027,7 @@ struct wireless_dev {

struct list_head pmsr_list;
spinlock_t pmsr_lock;
- struct work_struct pmsr_free_wk;
+ struct wiphy_work pmsr_free_wk;

unsigned long unprot_beacon_reported;

diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index 99ac747b7906..7f0c422307f4 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -156,8 +156,20 @@ static inline int tcf_classify(struct sk_buff *skb,
{
return TC_ACT_UNSPEC;
}
-
#endif
+static inline int tcf_classify_qdisc(struct sk_buff *skb,
+ const struct tcf_proto *tp,
+ struct tcf_result *res, bool compat_mode)
+{
+ int ret = tcf_classify(skb, NULL, tp, res, compat_mode);
+
+ /* TC_ACT_REDIRECT from qdisc filter chains is not supported.
+ * Use BPF via tcx or mirred redirect instead.
+ */
+ if (unlikely(ret == TC_ACT_REDIRECT))
+ ret = TC_ACT_SHOT;
+ return ret;
+}

static inline unsigned long
__cls_set_class(unsigned long *clp, unsigned long cl)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index affee44bd38e..cccc662561aa 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -312,7 +312,8 @@ struct sctp_cookie {

__u8 auth_random[sizeof(struct sctp_paramhdr) +
SCTP_AUTH_RANDOM_LENGTH];
- __u8 auth_hmacs[SCTP_AUTH_NUM_HMACS * sizeof(__u16) + 2];
+ __u8 auth_hmacs[sizeof(struct sctp_paramhdr) +
+ SCTP_AUTH_NUM_HMACS * sizeof(__u16)];
__u8 auth_chunks[sizeof(struct sctp_paramhdr) + SCTP_AUTH_MAX_CHUNKS];

/* This is a shim for my peer's INIT packet, followed by
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 607298501e12..284cde93bd30 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1965,6 +1965,8 @@ static inline void tcp_fast_path_check(struct sock *sk)

bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb,
int mib_idx, u32 *last_oow_ack_time);
+void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req);

static inline void tcp_mib_init(struct net *net)
{
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 7e2011830ba4..f6b286fa59f2 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -750,6 +750,9 @@ struct Scsi_Host {
*/
struct device *dma_dev;

+ /* Used for an rcu-synchronizing eh wakeup */
+ struct work_struct eh_work;
+
/* Delay for runtime autosuspend */
int rpm_autosuspend_delay;

diff --git a/include/trace/events/memory-failure.h b/include/trace/events/memory-failure.h
index aa57cc8f896b..7a8ee5d1a44e 100644
--- a/include/trace/events/memory-failure.h
+++ b/include/trace/events/memory-failure.h
@@ -1,6 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
-#define TRACE_SYSTEM memory_failure
+/*
+ * For historical versions, memory_failure_event is in ras subsystem,
+ * some user programs depend on it.
+ */
+#define TRACE_SYSTEM ras
#define TRACE_INCLUDE_FILE memory-failure

#if !defined(_TRACE_MEMORY_FAILURE_H) || defined(TRACE_HEADER_MULTI_READ)
diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h
index 9165154a274d..16ff7beab9ca 100644
--- a/include/uapi/linux/btrfs.h
+++ b/include/uapi/linux/btrfs.h
@@ -598,7 +598,7 @@ struct btrfs_ioctl_search_args_v2 {
__u64 buf_size; /* in - size of buffer
* out - on EOVERFLOW: needed size
* to store item */
- __u64 buf[]; /* out - found items */
+ __u8 buf[]; /* out - found items */
};

/* With a @src_length of zero, the range from @src_offset->EOF is cloned! */
diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h
index b2d2a71f7baf..7638b2220600 100644
--- a/include/uapi/linux/rkisp1-config.h
+++ b/include/uapi/linux/rkisp1-config.h
@@ -1535,11 +1535,11 @@ struct rkisp1_ext_params_wdr_config {
sizeof(struct rkisp1_ext_params_wdr_config))

/**
- * enum rksip1_ext_param_buffer_version - RkISP1 extensible parameters version
+ * enum rkisp1_ext_param_buffer_version - RkISP1 extensible parameters version
*
* @RKISP1_EXT_PARAM_BUFFER_V1: First version of RkISP1 extensible parameters
*/
-enum rksip1_ext_param_buffer_version {
+enum rkisp1_ext_param_buffer_version {
RKISP1_EXT_PARAM_BUFFER_V1 = V4L2_ISP_PARAMS_VERSION_V1,
};

@@ -1601,7 +1601,7 @@ enum rksip1_ext_param_buffer_version {
* +---------------------------------------------------------------------+
*
* @version: The RkISP1 extensible parameters buffer version, see
- * :c:type:`rksip1_ext_param_buffer_version`
+ * :c:type:`rkisp1_ext_param_buffer_version`
* @data_size: The RkISP1 configuration data effective size, excluding this
* header
* @data: The RkISP1 extensible configuration data blocks
diff --git a/init/Kconfig b/init/Kconfig
index 826a7d768ca3..d78a6e616311 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -190,6 +190,9 @@ config RUSTC_HAS_FILE_WITH_NUL
config RUSTC_HAS_FILE_AS_C_STR
def_bool RUSTC_VERSION >= 109100

+config RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS
+ def_bool RUSTC_VERSION >= 109800
+
config PAHOLE_VERSION
int
default "$(PAHOLE_VERSION)"
diff --git a/init/main.c b/init/main.c
index 96f93bb06c49..e363232b428b 100644
--- a/init/main.c
+++ b/init/main.c
@@ -324,51 +324,6 @@ static void * __init get_boot_config_from_initrd(size_t *_size)

#ifdef CONFIG_BOOT_CONFIG

-static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata;
-
-#define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0)
-
-static int __init xbc_snprint_cmdline(char *buf, size_t size,
- struct xbc_node *root)
-{
- struct xbc_node *knode, *vnode;
- char *end = buf + size;
- const char *val, *q;
- int ret;
-
- xbc_node_for_each_key_value(root, knode, val) {
- ret = xbc_node_compose_key_after(root, knode,
- xbc_namebuf, XBC_KEYLEN_MAX);
- if (ret < 0)
- return ret;
-
- vnode = xbc_node_get_child(knode);
- if (!vnode) {
- ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf);
- if (ret < 0)
- return ret;
- buf += ret;
- continue;
- }
- xbc_array_for_each_value(vnode, val) {
- /*
- * For prettier and more readable /proc/cmdline, only
- * quote the value when necessary, i.e. when it contains
- * whitespace.
- */
- q = strpbrk(val, " \t\r\n") ? "\"" : "";
- ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ",
- xbc_namebuf, q, val, q);
- if (ret < 0)
- return ret;
- buf += ret;
- }
- }
-
- return buf - (end - size);
-}
-#undef rest
-
/* Make an extra command line under given key word */
static char * __init xbc_make_cmdline(const char *key)
{
diff --git a/io_uring/rw.c b/io_uring/rw.c
index 63b6519e498c..95038cfda615 100644
--- a/io_uring/rw.c
+++ b/io_uring/rw.c
@@ -615,6 +615,24 @@ static void io_complete_rw_iopoll(struct kiocb *kiocb, long res)
smp_store_release(&req->iopoll_completed, 1);
}

+static inline ssize_t io_fixup_restart_res(ssize_t ret)
+{
+ switch (ret) {
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ case -ERESTARTNOHAND:
+ case -ERESTART_RESTARTBLOCK:
+ /*
+ * We can't just restart the syscall, since previously
+ * submitted sqes may already be in progress. Just fail
+ * this IO with EINTR.
+ */
+ return -EINTR;
+ default:
+ return ret;
+ }
+}
+
static inline void io_rw_done(struct io_kiocb *req, ssize_t ret)
{
struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw);
@@ -624,21 +642,8 @@ static inline void io_rw_done(struct io_kiocb *req, ssize_t ret)
return;

/* transform internal restart error codes */
- if (unlikely(ret < 0)) {
- switch (ret) {
- case -ERESTARTSYS:
- case -ERESTARTNOINTR:
- case -ERESTARTNOHAND:
- case -ERESTART_RESTARTBLOCK:
- /*
- * We can't just restart the syscall, since previously
- * submitted sqes may already be in progress. Just fail
- * this IO with EINTR.
- */
- ret = -EINTR;
- break;
- }
- }
+ if (unlikely(ret < 0))
+ ret = io_fixup_restart_res(ret);

if (req->flags & REQ_F_IOPOLL)
io_complete_rw_iopoll(&rw->kiocb, ret);
@@ -1034,7 +1039,8 @@ int io_read(struct io_kiocb *req, unsigned int issue_flags)

if (req->flags & REQ_F_BUFFERS_COMMIT)
io_kbuf_recycle(req, sel.buf_list, issue_flags);
- return ret;
+
+ return io_fixup_restart_res(ret);
}

int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags)
@@ -1068,8 +1074,10 @@ int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags)
return IOU_RETRY;
} else if (ret <= 0) {
io_kbuf_recycle(req, sel.buf_list, issue_flags);
- if (ret < 0)
+ if (ret < 0) {
+ ret = io_fixup_restart_res(ret);
req_set_fail(req);
+ }
} else if (!(req->flags & REQ_F_APOLL_MULTISHOT)) {
cflags = io_put_kbuf(req, ret, sel.buf_list);
} else {
diff --git a/kernel/audit.c b/kernel/audit.c
index 41a2de70fa43..562476937fa7 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -2035,7 +2035,7 @@ void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args)
* here and AUDIT_BUFSIZ is at least 1024, then we can
* log everything that printk could have logged. */
avail = audit_expand(ab,
- max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
+ max_t(unsigned int, AUDIT_BUFSIZ, 1+len-avail));
if (!avail)
goto out_va_end;
len = vsnprintf(skb_tail_pointer(skb), avail, fmt, args2);
diff --git a/kernel/audit.h b/kernel/audit.h
index ac81fa02bcd7..92d5e723d570 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -233,7 +233,7 @@ static inline int audit_hash_ino(u64 ino)
/* Indicates that audit should log the full pathname. */
#define AUDIT_NAME_FULL -1

-extern int audit_match_class(int class, unsigned syscall);
+extern int audit_match_class(int class, unsigned int syscall);
extern int audit_comparator(const u32 left, const u32 op, const u32 right);
extern int audit_uid_comparator(kuid_t left, u32 op, kuid_t right);
extern int audit_gid_comparator(kgid_t left, u32 op, kgid_t right);
@@ -256,8 +256,13 @@ extern int audit_del_rule(struct audit_entry *entry);
extern void audit_free_rule_rcu(struct rcu_head *head);
extern struct list_head audit_filter_list[];

-extern struct audit_entry *audit_dupe_rule(struct audit_krule *old);
+struct audit_watch_ctx {
+ struct inode *dir;
+ struct inode *child;
+};

+extern struct audit_entry *audit_dupe_rule(struct audit_krule *old,
+ struct audit_watch_ctx *ctx);
extern void audit_log_d_path_exe(struct audit_buffer *ab,
struct mm_struct *mm);

@@ -280,13 +285,15 @@ extern char *audit_watch_path(struct audit_watch *watch);
extern int audit_watch_compare(struct audit_watch *watch, u64 ino, dev_t dev);

extern struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule,
- char *pathname, int len);
+ char *pathname, int len,
+ struct audit_watch_ctx *ctx);
extern char *audit_mark_path(struct audit_fsnotify_mark *mark);
extern void audit_remove_mark(struct audit_fsnotify_mark *audit_mark);
extern void audit_remove_mark_rule(struct audit_krule *krule);
extern int audit_mark_compare(struct audit_fsnotify_mark *mark, u64 ino,
dev_t dev);
-extern int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old);
+extern int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old,
+ struct audit_watch_ctx *ctx);
extern int audit_exe_compare(struct task_struct *tsk,
struct audit_fsnotify_mark *mark);

@@ -317,13 +324,13 @@ extern struct list_head *audit_killed_trees(void);
#define audit_watch_path(w) ""
#define audit_watch_compare(w, i, d) 0

-#define audit_alloc_mark(k, p, l) (ERR_PTR(-EINVAL))
+#define audit_alloc_mark(k, p, l, c) (ERR_PTR(-EINVAL))
#define audit_mark_path(m) ""
#define audit_remove_mark(m) do { } while (0)
#define audit_remove_mark_rule(k) do { } while (0)
#define audit_mark_compare(m, i, d) 0
#define audit_exe_compare(t, m) (-EINVAL)
-#define audit_dupe_exe(n, o) (-EINVAL)
+#define audit_dupe_exe(n, o, c) (-EINVAL)

#define audit_remove_tree_rule(rule) BUG()
#define audit_add_tree_rule(rule) -EINVAL
diff --git a/kernel/audit_fsnotify.c b/kernel/audit_fsnotify.c
index ae0e75403f76..fa33d57e4320 100644
--- a/kernel/audit_fsnotify.c
+++ b/kernel/audit_fsnotify.c
@@ -71,19 +71,30 @@ static void audit_update_mark(struct audit_fsnotify_mark *audit_mark,
audit_mark->ino = inode ? inode->i_ino : AUDIT_INO_UNSET;
}

-struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pathname, int len)
+struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pathname,
+ int len, struct audit_watch_ctx *ctx)
{
struct audit_fsnotify_mark *audit_mark;
struct path path;
struct dentry *dentry;
- int ret;
+ struct inode *dir, *child;
+ int ret, allow_dups;

if (pathname[0] != '/' || pathname[len-1] == '/')
return ERR_PTR(-EINVAL);

- dentry = kern_path_parent(pathname, &path);
- if (IS_ERR(dentry))
- return ERR_CAST(dentry); /* returning an error */
+ if (!ctx) {
+ dentry = kern_path_parent(pathname, &path);
+ if (IS_ERR(dentry))
+ return ERR_CAST(dentry); /* returning an error */
+ dir = d_inode(path.dentry);
+ child = d_inode(dentry);
+ allow_dups = 0;
+ } else {
+ dir = ctx->dir;
+ child = ctx->child;
+ allow_dups = 1;
+ }

audit_mark = kzalloc_obj(*audit_mark);
if (unlikely(!audit_mark)) {
@@ -94,18 +105,21 @@ struct audit_fsnotify_mark *audit_alloc_mark(struct audit_krule *krule, char *pa
fsnotify_init_mark(&audit_mark->mark, audit_fsnotify_group);
audit_mark->mark.mask = AUDIT_FS_EVENTS;
audit_mark->path = pathname;
- audit_update_mark(audit_mark, dentry->d_inode);
audit_mark->rule = krule;

- ret = fsnotify_add_inode_mark(&audit_mark->mark, path.dentry->d_inode, 0);
+ audit_update_mark(audit_mark, child);
+ ret = fsnotify_add_inode_mark(&audit_mark->mark, dir, allow_dups);
+
if (ret < 0) {
audit_mark->path = NULL;
fsnotify_put_mark(&audit_mark->mark);
audit_mark = ERR_PTR(ret);
}
out:
- dput(dentry);
- path_put(&path);
+ if (!ctx) {
+ dput(dentry);
+ path_put(&path);
+ }
return audit_mark;
}

diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c
index ee84777fdfad..1ed19b775912 100644
--- a/kernel/audit_tree.c
+++ b/kernel/audit_tree.c
@@ -33,7 +33,7 @@ struct audit_chunk {
struct audit_node {
struct list_head list;
struct audit_tree *owner;
- unsigned index; /* index; upper bit indicates 'will prune' */
+ unsigned int index; /* index; upper bit indicates 'will prune' */
} owners[] __counted_by(count);
};

diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c
index 33577f0f54ef..06dd0ebe73e2 100644
--- a/kernel/audit_watch.c
+++ b/kernel/audit_watch.c
@@ -244,7 +244,8 @@ static void audit_watch_log_rule_change(struct audit_krule *r, struct audit_watc
/* Update inode info in audit rules based on filesystem event. */
static void audit_update_watch(struct audit_parent *parent,
const struct qstr *dname, dev_t dev,
- u64 ino, unsigned invalidating)
+ u64 ino, unsigned int invalidating,
+ struct audit_watch_ctx *ctx)
{
struct audit_watch *owatch, *nwatch, *nextw;
struct audit_krule *r, *nextr;
@@ -280,7 +281,7 @@ static void audit_update_watch(struct audit_parent *parent,
list_del(&oentry->rule.rlist);
list_del_rcu(&oentry->list);

- nentry = audit_dupe_rule(&oentry->rule);
+ nentry = audit_dupe_rule(&oentry->rule, ctx);
if (IS_ERR(nentry)) {
list_del(&oentry->rule.list);
audit_panic("error updating watch, removing");
@@ -479,10 +480,17 @@ static int audit_watch_handle_event(struct fsnotify_mark *inode_mark, u32 mask,
if (WARN_ON_ONCE(inode_mark->group != audit_watch_group))
return 0;

- if (mask & (FS_CREATE|FS_MOVED_TO) && inode)
- audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0);
- else if (mask & (FS_DELETE|FS_MOVED_FROM))
- audit_update_watch(parent, dname, AUDIT_DEV_UNSET, AUDIT_INO_UNSET, 1);
+ if (mask & (FS_CREATE|FS_MOVED_TO) && inode) {
+ struct audit_watch_ctx ctx = { .dir = dir, .child = inode };
+
+ audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0,
+ &ctx);
+ } else if (mask & (FS_DELETE|FS_MOVED_FROM)) {
+ struct audit_watch_ctx ctx = { .dir = dir, .child = NULL };
+
+ audit_update_watch(parent, dname, AUDIT_DEV_UNSET, AUDIT_INO_UNSET, 1,
+ &ctx);
+ }
else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF))
audit_remove_parent_watches(parent);

@@ -505,7 +513,8 @@ static int __init audit_watch_init(void)
}
device_initcall(audit_watch_init);

-int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old)
+int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old,
+ struct audit_watch_ctx *ctx)
{
struct audit_fsnotify_mark *audit_mark;
char *pathname;
@@ -514,7 +523,7 @@ int audit_dupe_exe(struct audit_krule *new, struct audit_krule *old)
if (!pathname)
return -ENOMEM;

- audit_mark = audit_alloc_mark(new, pathname, strlen(pathname));
+ audit_mark = audit_alloc_mark(new, pathname, strlen(pathname), ctx);
if (IS_ERR(audit_mark)) {
kfree(pathname);
return PTR_ERR(audit_mark);
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 093425123f6c..4401119b5275 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -165,13 +165,13 @@ static inline int audit_to_inode(struct audit_krule *krule,

static __u32 *classes[AUDIT_SYSCALL_CLASSES];

-int __init audit_register_class(int class, unsigned *list)
+int __init audit_register_class(int class, unsigned int *list)
{
__u32 *p = kcalloc(AUDIT_BITMASK_SIZE, sizeof(__u32), GFP_KERNEL);
if (!p)
return -ENOMEM;
while (*list != ~0U) {
- unsigned n = *list++;
+ unsigned int n = *list++;
if (n >= AUDIT_BITMASK_SIZE * 32 - AUDIT_SYSCALL_CLASSES) {
kfree(p);
return -EINVAL;
@@ -186,7 +186,7 @@ int __init audit_register_class(int class, unsigned *list)
return 0;
}

-int audit_match_class(int class, unsigned syscall)
+int audit_match_class(int class, unsigned int syscall)
{
if (unlikely(syscall >= AUDIT_BITMASK_SIZE * 32))
return 0;
@@ -237,7 +237,7 @@ static int audit_match_signal(struct audit_entry *entry)
/* Common user-space to kernel rule translation. */
static inline struct audit_entry *audit_to_entry_common(struct audit_rule_data *rule)
{
- unsigned listnr;
+ unsigned int listnr;
struct audit_entry *entry;
int i, err;

@@ -589,7 +589,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
err = PTR_ERR(str);
goto exit_free;
}
- audit_mark = audit_alloc_mark(&entry->rule, str, f_val);
+ audit_mark = audit_alloc_mark(&entry->rule, str, f_val, NULL);
if (IS_ERR(audit_mark)) {
kfree(str);
err = PTR_ERR(audit_mark);
@@ -816,7 +816,8 @@ static inline int audit_dupe_lsm_field(struct audit_field *df,
* rule with the new rule in the filterlist, then free the old rule.
* The rlist element is undefined; list manipulations are handled apart from
* the initial copy. */
-struct audit_entry *audit_dupe_rule(struct audit_krule *old)
+struct audit_entry *audit_dupe_rule(struct audit_krule *old,
+ struct audit_watch_ctx *ctx)
{
u32 fcount = old->field_count;
struct audit_entry *entry;
@@ -875,7 +876,7 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old)
new->filterkey = fk;
break;
case AUDIT_EXE:
- err = audit_dupe_exe(new, old);
+ err = audit_dupe_exe(new, old, ctx);
break;
}
if (err) {
@@ -1414,7 +1415,7 @@ static int update_lsm_rule(struct audit_krule *r)
if (!security_audit_rule_known(r))
return 0;

- nentry = audit_dupe_rule(r);
+ nentry = audit_dupe_rule(r, NULL);
if (entry->rule.exe)
audit_remove_mark(entry->rule.exe);
if (IS_ERR(nentry)) {
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index abdf8da3be93..6610e667c728 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -150,7 +150,7 @@ static const struct audit_nfcfgop_tab audit_nfcfgs[] = {

static int audit_match_perm(struct audit_context *ctx, int mask)
{
- unsigned n;
+ unsigned int n;

if (unlikely(!ctx))
return 0;
diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
index 5d2d19473a82..5c780756e362 100644
--- a/kernel/sched/ext.c
+++ b/kernel/sched/ext.c
@@ -218,8 +218,6 @@ static __printf(4, 5) bool scx_exit(struct scx_sched *sch,
#define scx_error(sch, fmt, args...) scx_exit((sch), SCX_EXIT_ERROR, 0, fmt, ##args)
#define scx_verror(sch, fmt, args) scx_vexit((sch), SCX_EXIT_ERROR, 0, fmt, args)

-#define SCX_HAS_OP(sch, op) test_bit(SCX_OP_IDX(op), (sch)->has_op)
-
static long jiffies_delta_msecs(unsigned long at, unsigned long now)
{
if (time_after(at, now))
@@ -234,20 +232,6 @@ static bool u32_before(u32 a, u32 b)
}

#ifdef CONFIG_EXT_SUB_SCHED
-/**
- * scx_parent - Find the parent sched
- * @sch: sched to find the parent of
- *
- * Returns the parent scheduler or %NULL if @sch is root.
- */
-static struct scx_sched *scx_parent(struct scx_sched *sch)
-{
- if (sch->level)
- return sch->ancestors[sch->level - 1];
- else
- return NULL;
-}
-
/**
* scx_next_descendant_pre - find the next descendant for pre-order walk
* @pos: the current position (%NULL to initiate traversal)
@@ -295,7 +279,6 @@ static void scx_set_task_sched(struct task_struct *p, struct scx_sched *sch)
rcu_assign_pointer(p->scx.sched, sch);
}
#else /* CONFIG_EXT_SUB_SCHED */
-static struct scx_sched *scx_parent(struct scx_sched *sch) { return NULL; }
static struct scx_sched *scx_next_descendant_pre(struct scx_sched *pos, struct scx_sched *root) { return pos ? NULL : root; }
static void scx_set_task_sched(struct task_struct *p, struct scx_sched *sch) {}
#endif /* CONFIG_EXT_SUB_SCHED */
@@ -455,16 +438,16 @@ static bool rq_is_open(struct rq *rq, u64 enq_flags)
*/
DEFINE_PER_CPU(struct rq *, scx_locked_rq_state);

-static inline void update_locked_rq(struct rq *rq)
+static void switch_rq_lock(struct rq *from, struct rq *to)
{
- /*
- * Check whether @rq is actually locked. This can help expose bugs
- * or incorrect assumptions about the context in which a kfunc or
- * callback is executed.
- */
- if (rq)
- lockdep_assert_rq_held(rq);
- __this_cpu_write(scx_locked_rq_state, rq);
+ bool tracked = scx_locked_rq() == from;
+
+ if (tracked)
+ update_locked_rq(NULL);
+ raw_spin_rq_unlock(from);
+ raw_spin_rq_lock(to);
+ if (tracked)
+ update_locked_rq(to);
}

/*
@@ -2291,8 +2274,7 @@ static void move_remote_task_to_local_dsq(struct task_struct *p, u64 enq_flags,
deactivate_task(src_rq, p, 0);
set_task_cpu(p, cpu_of(dst_rq));

- raw_spin_rq_unlock(src_rq);
- raw_spin_rq_lock(dst_rq);
+ switch_rq_lock(src_rq, dst_rq);

/*
* We want to pass scx-specific enq_flags but activate_task() will
@@ -2623,9 +2605,8 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq,

/* switch to @src_rq lock */
if (locked_rq != src_rq) {
- raw_spin_rq_unlock(locked_rq);
+ switch_rq_lock(locked_rq, src_rq);
locked_rq = src_rq;
- raw_spin_rq_lock(src_rq);
}

/* task_rq couldn't have changed if we're still the holding cpu */
@@ -2653,10 +2634,8 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq,
}

/* switch back to @rq lock */
- if (locked_rq != rq) {
- raw_spin_rq_unlock(locked_rq);
- raw_spin_rq_lock(rq);
- }
+ if (locked_rq != rq)
+ switch_rq_lock(locked_rq, rq);
}

/**
@@ -2985,24 +2964,38 @@ static void set_next_task_scx(struct rq *rq, struct task_struct *p, bool first)

/*
* @p is getting newly scheduled or got kicked after someone updated its
- * slice. Refresh whether tick can be stopped. See scx_can_stop_tick().
+ * slice. Update SCX_RQ_CAN_STOP_TICK to reflect whether the tick can be
+ * stopped. See scx_can_stop_tick().
+ *
+ * Moreover, refresh the load_avgs just when transitioning in and out of
+ * nohz. In the future, we might want to add a mechanism to update
+ * load_avgs periodically on tick-stopped CPUs.
*/
- if ((p->scx.slice == SCX_SLICE_INF) !=
- (bool)(rq->scx.flags & SCX_RQ_CAN_STOP_TICK)) {
- if (p->scx.slice == SCX_SLICE_INF)
+ if (p->scx.slice == SCX_SLICE_INF) {
+ if (!(rq->scx.flags & SCX_RQ_CAN_STOP_TICK)) {
+ /*
+ * Bypass mode always assigns finite slices, so @p
+ * can't have an infinite slice while bypassing.
+ * Therefore, sched_update_tick_dependency() can safely
+ * evaluate the outgoing task.
+ */
rq->scx.flags |= SCX_RQ_CAN_STOP_TICK;
- else
- rq->scx.flags &= ~SCX_RQ_CAN_STOP_TICK;
+ sched_update_tick_dependency(rq);

- sched_update_tick_dependency(rq);
+ update_other_load_avgs(rq);
+ }
+ } else {
+ if (rq->scx.flags & SCX_RQ_CAN_STOP_TICK) {
+ rq->scx.flags &= ~SCX_RQ_CAN_STOP_TICK;
+ update_other_load_avgs(rq);
+ }

/*
- * For now, let's refresh the load_avgs just when transitioning
- * in and out of nohz. In the future, we might want to add a
- * mechanism which calls the following periodically on
- * tick-stopped CPUs.
+ * @rq still references the outgoing scheduling context. A finite
+ * slice is sufficient by itself to require the tick.
*/
- update_other_load_avgs(rq);
+ if (tick_nohz_full_cpu(cpu_of(rq)))
+ tick_nohz_dep_set_cpu(cpu_of(rq), TICK_DEP_BIT_SCHED);
}
}

@@ -3097,9 +3090,14 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
* sched_class, %SCX_OPS_ENQ_LAST must be set. Tell
* ops.enqueue() that @p is the only one available for this cpu,
* which should trigger an explicit follow-up scheduling event.
+ *
+ * Core scheduling can force this CPU idle while @p stays
+ * runnable. @p's cookie then won't match the core's, so skip
+ * the warning in that case.
*/
if (next && sched_class_above(&ext_sched_class, next->sched_class)) {
- WARN_ON_ONCE(!(sch->ops.flags & SCX_OPS_ENQ_LAST));
+ WARN_ON_ONCE(sched_cpu_cookie_match(rq, p) &&
+ !(sch->ops.flags & SCX_OPS_ENQ_LAST));
do_enqueue_task(rq, p, SCX_ENQ_LAST, -1);
} else {
do_enqueue_task(rq, p, 0, -1);
@@ -3904,6 +3902,17 @@ static void reweight_task_scx(struct rq *rq, struct task_struct *p,
if (task_dead_and_done(p))
return;

+ /*
+ * When switching sched_class away from SCX, reweight_task_scx()
+ * is called _after_ scx_disable_task(). Skip calling ops.set_weight()
+ * since the BPF scheduler may have already forgotten the task in
+ * ops.disable().
+ * p->scx.weight will be recalculated in scx_enable_task() if the task
+ * ever returns to SCX class.
+ */
+ if (scx_get_task_state(p) != SCX_TASK_ENABLED)
+ return;
+
p->scx.weight = sched_weight_to_cgroup(scale_load_down(lw->weight));
if (SCX_HAS_OP(sch, set_weight))
SCX_CALL_OP_TASK(sch, set_weight, rq, p, p->scx.weight);
@@ -4280,6 +4289,15 @@ bool scx_can_stop_tick(struct rq *rq)
if (p->sched_class != &ext_sched_class)
return true;

+ /*
+ * @rq->curr may still reference an outgoing EXT task after it has been
+ * dequeued. If no EXT tasks are accounted on @rq, ignore its stale
+ * slice state. If another task is dispatched from a DSQ,
+ * set_next_task_scx() will update the dependency for the incoming task.
+ */
+ if (!rq->scx.nr_running)
+ return true;
+
if (scx_bypassing(sch, cpu_of(rq)))
return false;

@@ -5604,7 +5622,7 @@ static void free_kick_syncs(void)
int cpu;

for_each_possible_cpu(cpu) {
- struct scx_kick_syncs **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu);
+ struct scx_kick_syncs __rcu **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu);
struct scx_kick_syncs *to_free;

to_free = rcu_replace_pointer(*ksyncs, NULL, true);
@@ -6527,7 +6545,7 @@ static int alloc_kick_syncs(void)
* can exceed percpu allocator limits on large machines.
*/
for_each_possible_cpu(cpu) {
- struct scx_kick_syncs **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu);
+ struct scx_kick_syncs __rcu **ksyncs = per_cpu_ptr(&scx_kick_syncs, cpu);
struct scx_kick_syncs *new_ksyncs;

WARN_ON_ONCE(rcu_access_pointer(*ksyncs));
@@ -7401,6 +7419,12 @@ static void scx_sub_enable_workfn(struct kthread_work *work)
percpu_up_write(&scx_fork_rwsem);
err_disable:
mutex_unlock(&scx_enable_mutex);
+ /*
+ * Some enable failures only return an errno (e.g. -ENOMEM from an
+ * allocation) without calling scx_error(). Record it so
+ * scx_flush_disable_work() runs the disable and ops.exit() fires.
+ */
+ scx_error(sch, "scx_sub_enable() failed (%d)", ret);
scx_flush_disable_work(sch);
cmd->ret = 0;
}
@@ -8389,10 +8413,8 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
in_balance = this_rq->scx.flags & SCX_RQ_IN_BALANCE;

if (in_balance) {
- if (this_rq != src_rq) {
- raw_spin_rq_unlock(this_rq);
- raw_spin_rq_lock(src_rq);
- }
+ if (this_rq != src_rq)
+ switch_rq_lock(this_rq, src_rq);
} else {
raw_spin_rq_lock(src_rq);
}
@@ -8424,10 +8446,8 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
dispatched = true;
out:
if (in_balance) {
- if (this_rq != locked_rq) {
- raw_spin_rq_unlock(locked_rq);
- raw_spin_rq_lock(this_rq);
- }
+ if (this_rq != locked_rq)
+ switch_rq_lock(locked_rq, this_rq);
} else {
raw_spin_rq_unlock_irqrestore(locked_rq, flags);
}
diff --git a/kernel/sched/ext_internal.h b/kernel/sched/ext_internal.h
index a075732d4430..6d57f1d0b86f 100644
--- a/kernel/sched/ext_internal.h
+++ b/kernel/sched/ext_internal.h
@@ -1377,6 +1377,20 @@ static inline struct rq *scx_locked_rq(void)
return __this_cpu_read(scx_locked_rq_state);
}

+static inline void update_locked_rq(struct rq *rq)
+{
+ /*
+ * Check whether @rq is actually locked. This can help expose bugs
+ * or incorrect assumptions about the context in which a kfunc or
+ * callback is executed.
+ */
+ if (rq)
+ lockdep_assert_rq_held(rq);
+ __this_cpu_write(scx_locked_rq_state, rq);
+}
+
+#define SCX_HAS_OP(sch, op) test_bit(SCX_OP_IDX(op), (sch)->has_op)
+
static inline bool scx_bypassing(struct scx_sched *sch, s32 cpu)
{
return unlikely(per_cpu_ptr(sch->pcpu, cpu)->flags &
@@ -1457,6 +1471,20 @@ static inline struct scx_sched *scx_prog_sched(const struct bpf_prog_aux *aux)

return NULL;
}
+
+/**
+ * scx_parent - Find the parent sched
+ * @sch: sched to find the parent of
+ *
+ * Returns the parent scheduler or %NULL if @sch is root.
+ */
+static inline struct scx_sched *scx_parent(struct scx_sched *sch)
+{
+ if (sch->level)
+ return sch->ancestors[sch->level - 1];
+ else
+ return NULL;
+}
#else /* CONFIG_EXT_SUB_SCHED */
static inline struct scx_sched *scx_task_sched(const struct task_struct *p)
{
@@ -1480,4 +1508,6 @@ static struct scx_sched *scx_prog_sched(const struct bpf_prog_aux *aux)
{
return rcu_dereference_all(scx_root);
}
+
+static inline struct scx_sched *scx_parent(struct scx_sched *sch) { return NULL; }
#endif /* CONFIG_EXT_SUB_SCHED */
diff --git a/kernel/smp.c b/kernel/smp.c
index a0bb56bd8dda..52dffc86555c 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -137,10 +137,10 @@ csd_do_func(smp_call_func_t func, void *info, call_single_data_t *csd)
trace_csd_function_exit(func, csd);
}

-#ifdef CONFIG_CSD_LOCK_WAIT_DEBUG
-
static DEFINE_STATIC_KEY_MAYBE(CONFIG_CSD_LOCK_WAIT_DEBUG_DEFAULT, csdlock_debug_enabled);

+#ifdef CONFIG_CSD_LOCK_WAIT_DEBUG
+
/*
* Parse the csdlock_debug= kernel boot parameter.
*
@@ -342,6 +342,10 @@ static __always_inline void csd_lock_wait(call_single_data_t *csd)
smp_cond_load_acquire(&csd->node.u_flags, !(VAL & CSD_FLAG_LOCK));
}
#else
+static __always_inline void __csd_lock_wait(call_single_data_t *csd)
+{
+}
+
static void csd_lock_record(call_single_data_t *csd)
{
}
@@ -354,8 +358,23 @@ static __always_inline void csd_lock_wait(call_single_data_t *csd)

static __always_inline void csd_lock(call_single_data_t *csd)
{
- csd_lock_wait(csd);
- csd->node.u_flags |= CSD_FLAG_LOCK;
+ if (IS_ENABLED(CONFIG_CSD_LOCK_WAIT_DEBUG) &&
+ static_branch_unlikely(&csdlock_debug_enabled)) {
+
+ for (;;) {
+ unsigned int flags;
+
+ __csd_lock_wait(csd);
+ flags = READ_ONCE(csd->node.u_flags);
+
+ if (!(flags & CSD_FLAG_LOCK) &&
+ try_cmpxchg_acquire(&csd->node.u_flags, &flags, flags | CSD_FLAG_LOCK))
+ break;
+ }
+ } else {
+ csd_lock_wait(csd);
+ csd->node.u_flags |= CSD_FLAG_LOCK;
+ }

/*
* prevent CPU from reordering the above assignment
@@ -380,7 +399,8 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(call_single_data_t, csd_data);
#ifdef CONFIG_CSD_LOCK_WAIT_DEBUG
static call_single_data_t *get_single_csd_data(int cpu)
{
- if (static_branch_unlikely(&csdlock_debug_enabled))
+ if (static_branch_unlikely(&csdlock_debug_enabled) &&
+ (unsigned int)cpu < nr_cpu_ids)
return per_cpu_ptr(&csd_data, cpu);
return this_cpu_ptr(&csd_data);
}
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index b2611de3f594..ce360a306f2e 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1097,6 +1097,12 @@ struct ftrace_ops global_ops = {
FTRACE_OPS_FL_PID,
};

+/*
+ * parser_lock - Protects trace_parser state against concurrent operations.
+ * Held across trace_get_user() and subsequent buffer parsing to prevent races.
+ */
+static DEFINE_MUTEX(parser_lock);
+
/*
* Used by the stack unwinder to know about dynamic ftrace trampolines.
*/
@@ -5824,6 +5830,8 @@ ftrace_regex_write(struct file *file, const char __user *ubuf,
/* iter->hash is a local copy, so we don't need regex_lock */

parser = &iter->parser;
+
+ guard(mutex)(&parser_lock);
read = trace_get_user(parser, ubuf, cnt, ppos);

if (read >= 0 && trace_parser_loaded(parser) &&
@@ -6961,12 +6969,14 @@ int ftrace_regex_release(struct inode *inode, struct file *file)
iter = file->private_data;

parser = &iter->parser;
+ mutex_lock(&parser_lock);
if (trace_parser_loaded(parser)) {
int enable = !(iter->flags & FTRACE_ITER_NOTRACE);

ftrace_process_regex(iter, parser->buffer,
parser->idx, enable);
}
+ mutex_unlock(&parser_lock);

trace_parser_put(parser);

@@ -7298,10 +7308,12 @@ ftrace_graph_release(struct inode *inode, struct file *file)

parser = &fgd->parser;

+ mutex_lock(&parser_lock);
if (trace_parser_loaded((parser))) {
ret = ftrace_graph_set_hash(fgd->new_hash,
parser->buffer);
}
+ mutex_unlock(&parser_lock);

trace_parser_put(parser);

@@ -7414,6 +7426,7 @@ ftrace_graph_write(struct file *file, const char __user *ubuf,

parser = &fgd->parser;

+ guard(mutex)(&parser_lock);
read = trace_get_user(parser, ubuf, cnt, ppos);

if (read >= 0 && trace_parser_loaded(parser) &&
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 4c3729c8d5e2..56b385b05d53 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -6191,7 +6191,7 @@ char *trace_user_fault_read(struct trace_user_buf_info *tinfo,
{
int cpu = smp_processor_id();
char *buffer = per_cpu_ptr(tinfo->tbuf, cpu)->buf;
- unsigned int cnt;
+ unsigned long long cnt;
int trys = 0;
int ret;

diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 80fe152af1dd..bf77331f56a4 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1941,6 +1941,7 @@ struct event_trigger_data {
struct list_head named_list;
struct event_trigger_data *named_data;
struct llist_node llist;
+ void (*private_data_free)(struct event_trigger_data *data);
};

/* Avoid typos */
diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c
index 50518b071414..bcd97cb24ac9 100644
--- a/kernel/trace/trace_eprobe.c
+++ b/kernel/trace/trace_eprobe.c
@@ -172,7 +172,8 @@ static bool eprobe_dyn_event_match(const char *system, const char *event,
if (!slash)
return false;

- if (strncmp(ep->event_system, argv[0], slash - argv[0]))
+ if (strncmp(ep->event_system, argv[0], slash - argv[0]) ||
+ ep->event_system[slash - argv[0]] != '\0')
return false;
if (strcmp(ep->event_name, slash + 1))
return false;
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index c46e623e7e0d..956692856fa8 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1350,7 +1350,9 @@ __ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match,
call = file->event_call;

/* If a module is specified, skip events that are not that module */
- if (module && (!call->module || strcmp(module_name(call->module), module)))
+ if (module &&
+ ((call->flags & TRACE_EVENT_FL_DYNAMIC) ||
+ !call->module || strcmp(module_name(call->module), module)))
continue;

name = trace_event_name(call);
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 9701650c89b2..e287dad25fc3 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -6358,6 +6358,7 @@ static void event_hist_trigger_free(struct event_trigger_data *data)

trigger_data_free(data);

+ tracepoint_synchronize_unregister();
remove_hist_vars(hist_data);

unregister_field_var_hists(hist_data);
@@ -6397,6 +6398,7 @@ static void event_hist_trigger_named_free(struct event_trigger_data *data)

del_named_trigger(data);
trigger_data_free(data);
+ tracepoint_synchronize_unregister();
kfree(cmd_ops);
}
}
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 655db2e82513..ad83419cb420 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -38,6 +38,13 @@ static void trigger_create_kthread_locked(void)
}
}

+static void trigger_data_free_one(struct event_trigger_data *data)
+{
+ if (data->private_data_free)
+ data->private_data_free(data);
+ kfree(data);
+}
+
static void trigger_data_free_queued_locked(void)
{
struct event_trigger_data *data, *tmp;
@@ -52,7 +59,7 @@ static void trigger_data_free_queued_locked(void)
tracepoint_synchronize_unregister();

llist_for_each_entry_safe(data, tmp, llnodes, llist)
- kfree(data);
+ trigger_data_free_one(data);
}

/* Bulk garbage collection of event_trigger_data elements */
@@ -75,7 +82,7 @@ static int trigger_kthread_fn(void *ignore)
tracepoint_synchronize_unregister();

llist_for_each_entry_safe(data, tmp, llnodes, llist)
- kfree(data);
+ trigger_data_free_one(data);
}

return 0;
@@ -1717,6 +1724,14 @@ int event_enable_trigger_print(struct seq_file *m,
return 0;
}

+static void enable_trigger_private_data_free(struct event_trigger_data *data)
+{
+ struct enable_trigger_data *enable_data = data->private_data;
+
+ trace_event_put_ref(enable_data->file->event_call);
+ kfree(enable_data);
+}
+
void event_enable_trigger_free(struct event_trigger_data *data)
{
struct enable_trigger_data *enable_data = data->private_data;
@@ -1728,9 +1743,8 @@ void event_enable_trigger_free(struct event_trigger_data *data)
if (!data->ref) {
/* Remove the SOFT_MODE flag */
trace_event_enable_disable(enable_data->file, 0, 1);
- trace_event_put_ref(enable_data->file->event_call);
+ data->private_data_free = enable_trigger_private_data_free;
trigger_data_free(data);
- kfree(enable_data);
}
}

diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
index 226cf66e0d68..b88b8d9923ad 100644
--- a/kernel/trace/trace_mmiotrace.c
+++ b/kernel/trace/trace_mmiotrace.c
@@ -109,7 +109,6 @@ static void mmio_pipe_open(struct trace_iterator *iter)
iter->private = hiter;
}

-/* XXX: This is not called when the pipe is closed! */
static void mmio_close(struct trace_iterator *iter)
{
struct header_iter *hiter = iter->private;
@@ -146,7 +145,7 @@ static ssize_t mmio_read(struct trace_iterator *iter, struct file *filp,
goto print_out;
}

- if (!hiter)
+ if (!hiter || !hiter->dev)
return 0;

mmio_print_pcidev(s, hiter->dev);
@@ -279,6 +278,7 @@ static struct tracer mmio_tracer __read_mostly =
.start = mmio_trace_start,
.pipe_open = mmio_pipe_open,
.close = mmio_close,
+ .pipe_close = mmio_close,
.read = mmio_read,
.print_line = mmio_print_line,
.noboot = true,
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 733ce83e1e11..2ad377d02b61 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -188,7 +188,7 @@ void __trace_probe_log_err(int offset, int err_type)

lockdep_assert_held(&dyn_event_ops_mutex);

- if (!trace_probe_log.argv)
+ if (!trace_probe_log.argv || !trace_probe_log.argc)
return;

/* Recalculate the length and allocate buffer */
@@ -2018,7 +2018,7 @@ int traceprobe_update_arg(struct probe_arg *arg)
}

/* When len=0, we just calculate the needed length */
-#define LEN_OR_ZERO (len ? len - pos : 0)
+#define LEN_OR_ZERO (len > pos ? len - pos : 0)
static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
enum probe_print_type ptype)
{
@@ -2344,16 +2344,17 @@ int trace_probe_compare_arg_type(struct trace_probe *a, struct trace_probe *b)
bool trace_probe_match_command_args(struct trace_probe *tp,
int argc, const char **argv)
{
- char buf[MAX_ARGSTR_LEN + 1];
int i;

if (tp->nr_args < argc)
return false;

for (i = 0; i < argc; i++) {
- snprintf(buf, sizeof(buf), "%s=%s",
- tp->args[i].name, tp->args[i].comm);
- if (strcmp(buf, argv[i]))
+ int len = strlen(tp->args[i].name);
+
+ if (strncmp(argv[i], tp->args[i].name, len) ||
+ argv[i][len] != '=' ||
+ strcmp(argv[i] + len + 1, tp->args[i].comm))
return false;
}
return true;
diff --git a/kernel/trace/trace_remote.c b/kernel/trace/trace_remote.c
index 6dde6bdcbde7..de433476bc71 100644
--- a/kernel/trace/trace_remote.c
+++ b/kernel/trace/trace_remote.c
@@ -1004,11 +1004,10 @@ int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size,
desc->nr_cpus++;

for (id = 0; id < nr_pages; id++) {
+ rb_desc->nr_page_va++;
rb_desc->page_va[id] = (unsigned long)__get_free_page(GFP_KERNEL);
if (!rb_desc->page_va[id])
goto err;
-
- rb_desc->nr_page_va++;
}
rb_desc = __next_ring_buffer_desc(rb_desc);
}
@@ -1150,10 +1149,21 @@ static ssize_t remote_events_dir_enable_write(struct file *filp, const char __us

for (i = 0; i < remote->nr_events; i++) {
struct remote_event *evt = &remote->events[i];
+ int eret;

- trace_remote_enable_event(remote, evt, enable);
+ eret = trace_remote_enable_event(remote, evt, enable);
+ /*
+ * Save the first error and return that. Some events
+ * may still have been enabled, but let the user
+ * know that something went wrong.
+ */
+ if (!ret && eret)
+ ret = eret;
}

+ if (ret)
+ return ret;
+
return count;
}

diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c
index 8ad72e17d8eb..8dcedff8429a 100644
--- a/kernel/trace/trace_syscalls.c
+++ b/kernel/trace/trace_syscalls.c
@@ -1436,6 +1436,11 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)
if (syscall_get_data(sys_data, args, &user_ptr,
&size, user_sizes, &uargs, buf_size) < 0)
return;
+
+ /* The above may have caused a migration */
+ head = this_cpu_ptr(sys_data->enter_event->perf_events);
+ if (hlist_empty(head))
+ return;
}

head = this_cpu_ptr(sys_data->enter_event->perf_events);
diff --git a/lib/bootconfig.c b/lib/bootconfig.c
index c470b93d5dbc..2ed9ee3dc81c 100644
--- a/lib/bootconfig.c
+++ b/lib/bootconfig.c
@@ -408,6 +408,71 @@ const char * __init xbc_node_find_next_key_value(struct xbc_node *root,
return ""; /* No value key */
}

+static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata;
+
+#define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0)
+
+/**
+ * xbc_snprint_cmdline() - Render bootconfig keys under @root as a cmdline string
+ * @buf: Destination buffer (may be NULL when @size is 0 to query the length)
+ * @size: Size of @buf in bytes
+ * @root: Subtree root whose key=value pairs should be rendered
+ *
+ * Walk all key/value pairs under @root and emit them as a space-separated
+ * cmdline string into @buf. Values containing whitespace are quoted with
+ * double quotes. Returns the number of bytes that would be written if @buf
+ * were large enough (matching snprintf semantics), or a negative errno on
+ * failure.
+ */
+int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root)
+{
+ struct xbc_node *knode, *vnode;
+ const char *val, *q;
+ size_t len = 0;
+ int ret;
+
+ /*
+ * Track the running written length rather than advancing @buf, so we
+ * never form "buf + size" or "buf += ret" while @buf is NULL (the
+ * size-probe call passes buf=NULL, size=0). NULL pointer arithmetic
+ * is undefined behavior and trips host UBSan / FORTIFY_SOURCE when
+ * this renderer runs at kernel build time. snprintf(NULL, 0, ...)
+ * itself is well defined and returns the would-be length.
+ */
+ xbc_node_for_each_key_value(root, knode, val) {
+ ret = xbc_node_compose_key_after(root, knode,
+ xbc_namebuf, XBC_KEYLEN_MAX);
+ if (ret < 0)
+ return ret;
+
+ vnode = xbc_node_get_child(knode);
+ if (!vnode) {
+ ret = snprintf(buf ? buf + len : NULL, rest(len, size),
+ "%s ", xbc_namebuf);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ continue;
+ }
+ xbc_array_for_each_value(vnode, val) {
+ /*
+ * For prettier and more readable /proc/cmdline, only
+ * quote the value when necessary, i.e. when it contains
+ * whitespace.
+ */
+ q = strpbrk(val, " \t\r\n") ? "\"" : "";
+ ret = snprintf(buf ? buf + len : NULL, rest(len, size),
+ "%s=%s%s%s ", xbc_namebuf, q, val, q);
+ if (ret < 0)
+ return ret;
+ len += ret;
+ }
+ }
+
+ return len;
+}
+#undef rest
+
/* XBC parse and tree build */

static int __init xbc_init_node(struct xbc_node *node, char *data, uint16_t flag)
diff --git a/lib/compat_audit.c b/lib/compat_audit.c
index 3d6b8996f027..fee1dfccd116 100644
--- a/lib/compat_audit.c
+++ b/lib/compat_audit.c
@@ -4,32 +4,32 @@
#include <linux/audit_arch.h>
#include <asm/unistd32.h>

-unsigned compat_dir_class[] = {
+unsigned int compat_dir_class[] = {
#include <asm-generic/audit_dir_write.h>
~0U
};

-unsigned compat_read_class[] = {
+unsigned int compat_read_class[] = {
#include <asm-generic/audit_read.h>
~0U
};

-unsigned compat_write_class[] = {
+unsigned int compat_write_class[] = {
#include <asm-generic/audit_write.h>
~0U
};

-unsigned compat_chattr_class[] = {
+unsigned int compat_chattr_class[] = {
#include <asm-generic/audit_change_attr.h>
~0U
};

-unsigned compat_signal_class[] = {
+unsigned int compat_signal_class[] = {
#include <asm-generic/audit_signal.h>
~0U
};

-int audit_classify_compat_syscall(int abi, unsigned syscall)
+int audit_classify_compat_syscall(int abi, unsigned int syscall)
{
switch (syscall) {
#ifdef __NR_open
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index c0ba34eadb39..208cb3558f8c 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -776,6 +776,7 @@ int rhashtable_walk_start_check(struct rhashtable_iter *iter)
iter->walker.tbl = rht_dereference_rcu(ht->tbl, ht);
iter->slot = 0;
iter->skip = 0;
+ iter->p = NULL;
return -EAGAIN;
}

diff --git a/mm/damon/core.c b/mm/damon/core.c
index 60f5f191e17a..2503944c9aa7 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -240,8 +240,21 @@ int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges,
{
struct damon_region *r, *next;
unsigned int i;
+ unsigned long last_end;
int err;

+ for (i = 0; i < nr_ranges; i++) {
+ unsigned long start, end;
+
+ start = ALIGN_DOWN(ranges[i].start, min_region_sz);
+ end = ALIGN(ranges[i].end, min_region_sz);
+ if (start >= end)
+ return -EINVAL;
+ if (i > 0 && last_end > start)
+ return -EINVAL;
+ last_end = end;
+ }
+
/* Remove regions which are not in the new ranges */
damon_for_each_region_safe(r, next, t) {
for (i = 0; i < nr_ranges; i++) {
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 3f0466729c75..6f241cddd7db 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -3649,10 +3649,6 @@ static void __split_folio_to_order(struct folio *folio, int old_order,
(1L << PG_dropbehind) |
LRU_GEN_MASK | LRU_REFS_MASK));

- if (handle_hwpoison &&
- page_range_has_hwpoisoned(new_head, new_nr_pages))
- folio_set_has_hwpoisoned(new_folio);
-
new_folio->mapping = folio->mapping;
new_folio->index = folio->index + i;

@@ -3674,6 +3670,14 @@ static void __split_folio_to_order(struct folio *folio, int old_order,
folio_set_large_rmappable(new_folio);
}

+ /*
+ * PG_has_hwpoisoned is on the 2nd page, so set it after
+ * the compound head is prepped.
+ */
+ if (handle_hwpoison &&
+ page_range_has_hwpoisoned(new_head, new_nr_pages))
+ folio_set_has_hwpoisoned(new_folio);
+
if (folio_test_young(folio))
folio_set_young(new_folio);
if (folio_test_idle(folio))
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 2eff0d6b622b..643eb13300e5 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -676,7 +676,7 @@ static struct kmemleak_object *__alloc_object(gfp_t gfp)
atomic_set(&object->use_count, 1);
object->excess_ref = 0;
object->count = 0; /* white color initially */
- object->checksum = 0;
+ object->checksum = ~0;
object->del_state = 0;

/* task information */
@@ -970,7 +970,7 @@ static void reset_checksum(unsigned long ptr)
}

raw_spin_lock_irqsave(&object->lock, flags);
- object->checksum = 0;
+ object->checksum = ~0;
raw_spin_unlock_irqrestore(&object->lock, flags);
put_object(object);
}
@@ -1399,7 +1399,8 @@ static bool update_checksum(struct kmemleak_object *object)
for_each_possible_cpu(cpu) {
void *ptr = per_cpu_ptr((void __percpu *)object->pointer, cpu);

- object->checksum ^= crc32(0, kasan_reset_tag((void *)ptr), object->size);
+ object->checksum = crc32(object->checksum,
+ kasan_reset_tag((void *)ptr), object->size);
}
} else {
object->checksum = crc32(0, kasan_reset_tag((void *)object->pointer), object->size);
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index cf4f77108c43..462d8dcd636d 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -576,6 +576,7 @@ void remove_pfn_range_from_zone(struct zone *zone,
* @pfn: starting pageframe (must be aligned to start of a section)
* @nr_pages: number of pages to remove (must be multiple of section size)
* @altmap: alternative device page map or %NULL if default memmap is used
+ * @pgmap: device page map or %NULL if not ZONE_DEVICE
*
* Generic helper function to remove section mappings and sysfs entries
* for the section of the memory we are removing. Caller needs to make
@@ -583,7 +584,7 @@ void remove_pfn_range_from_zone(struct zone *zone,
* calling offline_pages().
*/
void __remove_pages(unsigned long pfn, unsigned long nr_pages,
- struct vmem_altmap *altmap)
+ struct vmem_altmap *altmap, struct dev_pagemap *pgmap)
{
const unsigned long end_pfn = pfn + nr_pages;
unsigned long cur_nr_pages;
@@ -598,7 +599,7 @@ void __remove_pages(unsigned long pfn, unsigned long nr_pages,
/* Select all remaining pages up to the next section boundary */
cur_nr_pages = min(end_pfn - pfn,
SECTION_ALIGN_UP(pfn + 1) - pfn);
- sparse_remove_section(pfn, cur_nr_pages, altmap);
+ sparse_remove_section(pfn, cur_nr_pages, altmap, pgmap);
}
}

@@ -1427,7 +1428,7 @@ static void remove_memory_blocks_and_altmaps(u64 start, u64 size)

remove_memory_block_devices(cur_start, memblock_size);

- arch_remove_memory(cur_start, memblock_size, altmap);
+ arch_remove_memory(cur_start, memblock_size, altmap, NULL);

/* Verify that all vmemmap pages have actually been freed. */
WARN(altmap->alloc, "Altmap not fully unmapped");
@@ -1470,7 +1471,7 @@ static int create_altmaps_and_memory_blocks(int nid, struct memory_group *group,
ret = create_memory_block_devices(cur_start, memblock_size, nid,
params.altmap, group);
if (ret) {
- arch_remove_memory(cur_start, memblock_size, params.altmap);
+ arch_remove_memory(cur_start, memblock_size, params.altmap, NULL);
kfree(params.altmap);
goto out;
}
@@ -1556,7 +1557,7 @@ int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
/* create memory block devices after memory was added */
ret = create_memory_block_devices(start, size, nid, NULL, group);
if (ret) {
- arch_remove_memory(start, size, params.altmap);
+ arch_remove_memory(start, size, params.altmap, NULL);
goto error;
}
}
@@ -2268,7 +2269,7 @@ static int try_remove_memory(u64 start, u64 size)
* No altmaps present, do the removal directly
*/
remove_memory_block_devices(start, size);
- arch_remove_memory(start, size, NULL);
+ arch_remove_memory(start, size, NULL, NULL);
} else {
/* all memblocks in the range have altmaps */
remove_memory_blocks_and_altmaps(start, size);
diff --git a/mm/memremap.c b/mm/memremap.c
index 053842d45cb1..81766d822400 100644
--- a/mm/memremap.c
+++ b/mm/memremap.c
@@ -97,10 +97,10 @@ static void pageunmap_range(struct dev_pagemap *pgmap, int range_id)
PHYS_PFN(range_len(range)));
if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
__remove_pages(PHYS_PFN(range->start),
- PHYS_PFN(range_len(range)), NULL);
+ PHYS_PFN(range_len(range)), NULL, pgmap);
} else {
arch_remove_memory(range->start, range_len(range),
- pgmap_altmap(pgmap));
+ pgmap_altmap(pgmap), pgmap);
kasan_remove_zero_shadow(__va(range->start), range_len(range));
}
mem_hotplug_done();
diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c
index a4d52fdb3056..ccd1c622c8b2 100644
--- a/mm/page_vma_mapped.c
+++ b/mm/page_vma_mapped.c
@@ -242,21 +242,31 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
*/
pmde = pmdp_get_lockless(pvmw->pmd);

- if (pmd_trans_huge(pmde) || pmd_is_migration_entry(pmde)) {
+ if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
+ (pmd_trans_huge(pmde) || pmd_is_migration_entry(pmde) ||
+ pmd_is_device_private_entry(pmde))) {
pvmw->ptl = pmd_lock(mm, pvmw->pmd);
pmde = *pvmw->pmd;
- if (!pmd_present(pmde)) {
+ if (pmd_is_migration_entry(pmde)) {
softleaf_t entry;

- if (!thp_migration_supported() ||
- !(pvmw->flags & PVMW_MIGRATION))
+ if (!(pvmw->flags & PVMW_MIGRATION))
return not_found(pvmw);
entry = softleaf_from_pmd(pmde);
+ if (!check_pmd(softleaf_to_pfn(entry), pvmw))
+ return not_found(pvmw);
+ return true;
+ } else if (pmd_is_device_private_entry(pmde)) {
+ softleaf_t entry;

- if (!softleaf_is_migration(entry) ||
- !check_pmd(softleaf_to_pfn(entry), pvmw))
+ if (pvmw->flags & PVMW_MIGRATION)
+ return not_found(pvmw);
+ entry = softleaf_from_pmd(pmde);
+ if (!check_pmd(softleaf_to_pfn(entry), pvmw))
return not_found(pvmw);
return true;
+ } else if (!pmd_present(pmde)) {
+ return not_found(pvmw);
}
if (likely(pmd_trans_huge(pmde))) {
if (pvmw->flags & PVMW_MIGRATION)
@@ -265,17 +275,10 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)
return not_found(pvmw);
return true;
}
- /* THP pmd was split under us: handle on pte level */
+ /* THP/device-private pmd was split under us: handle on pte level */
spin_unlock(pvmw->ptl);
pvmw->ptl = NULL;
} else if (!pmd_present(pmde)) {
- const softleaf_t entry = softleaf_from_pmd(pmde);
-
- if (softleaf_is_device_private(entry)) {
- pvmw->ptl = pmd_lock(mm, pvmw->pmd);
- return true;
- }
-
if ((pvmw->flags & PVMW_SYNC) &&
thp_vma_suitable_order(vma, pvmw->address,
PMD_ORDER) &&
diff --git a/mm/slub.c b/mm/slub.c
index eed3251eb7d0..12ace2932927 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -6066,7 +6066,6 @@ static void free_to_pcs_bulk(struct kmem_cache *s, size_t size, void **p)
void *remote_objects[PCS_BATCH_MAX];
unsigned int remote_nr = 0;

-next_remote_batch:
while (i < size) {
struct slab *slab = virt_to_slab(p[i]);

@@ -6081,8 +6080,11 @@ static void free_to_pcs_bulk(struct kmem_cache *s, size_t size, void **p)
if (unlikely(!can_free_to_pcs(slab))) {
remote_objects[remote_nr] = p[i];
p[i] = p[--size];
- if (++remote_nr >= PCS_BATCH_MAX)
- goto flush_remote;
+ if (++remote_nr >= PCS_BATCH_MAX) {
+ __kmem_cache_free_bulk(s, remote_nr, &remote_objects[0]);
+ stat_add(s, FREE_SLOWPATH, remote_nr);
+ remote_nr = 0;
+ }
continue;
}

@@ -6166,10 +6168,6 @@ static void free_to_pcs_bulk(struct kmem_cache *s, size_t size, void **p)
if (remote_nr) {
__kmem_cache_free_bulk(s, remote_nr, &remote_objects[0]);
stat_add(s, FREE_SLOWPATH, remote_nr);
- if (i < size) {
- remote_nr = 0;
- goto next_remote_batch;
- }
}
}

@@ -8425,6 +8423,8 @@ static void __init bootstrap_cache_sheaves(struct kmem_cache *s)
bool failed = false;
int node, cpu;

+ VM_WARN_ON_ONCE(cache_has_sheaves(s));
+
capacity = calculate_sheaf_capacity(s, &empty_args);

/* capacity can be 0 due to debugging or SLUB_TINY */
@@ -8475,8 +8475,11 @@ static void __init bootstrap_kmalloc_sheaves(void)

for (type = KMALLOC_NORMAL; type <= KMALLOC_RANDOM_END; type++) {
for (int idx = 0; idx < KMALLOC_SHIFT_HIGH + 1; idx++) {
- if (kmalloc_caches[type][idx])
- bootstrap_cache_sheaves(kmalloc_caches[type][idx]);
+ struct kmem_cache *s = kmalloc_caches[type][idx];
+
+ /* Do not bootstrap twice when caches are aliased */
+ if (s && !cache_has_sheaves(s))
+ bootstrap_cache_sheaves(s);
}
}
}
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index a7b11248b989..932082296e8d 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -652,6 +652,31 @@ void offline_mem_sections(unsigned long start_pfn, unsigned long end_pfn)
}
}

+static int __meminit section_nr_vmemmap_pages(unsigned long pfn, unsigned long nr_pages,
+ struct vmem_altmap *altmap, struct dev_pagemap *pgmap)
+{
+ const unsigned int order = pgmap ? pgmap->vmemmap_shift : 0;
+ const unsigned long pages_per_compound = 1UL << order;
+
+ VM_WARN_ON_ONCE(!IS_ALIGNED(pfn | nr_pages, PAGES_PER_SUBSECTION));
+ VM_WARN_ON_ONCE(nr_pages > PAGES_PER_SECTION);
+
+ if (!vmemmap_can_optimize(altmap, pgmap))
+ return DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE);
+
+ if (order < PFN_SECTION_SHIFT) {
+ VM_WARN_ON_ONCE(!IS_ALIGNED(pfn | nr_pages, pages_per_compound));
+ return VMEMMAP_RESERVE_NR * nr_pages / pages_per_compound;
+ }
+
+ VM_WARN_ON_ONCE(!IS_ALIGNED(pfn | nr_pages, PAGES_PER_SECTION));
+
+ if (IS_ALIGNED(pfn, pages_per_compound))
+ return VMEMMAP_RESERVE_NR;
+
+ return 0;
+}
+
static struct page * __meminit populate_section_memmap(unsigned long pfn,
unsigned long nr_pages, int nid, struct vmem_altmap *altmap,
struct dev_pagemap *pgmap)
@@ -659,18 +684,18 @@ static struct page * __meminit populate_section_memmap(unsigned long pfn,
struct page *page = __populate_section_memmap(pfn, nr_pages, nid, altmap,
pgmap);

- memmap_pages_add(DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE));
+ memmap_pages_add(section_nr_vmemmap_pages(pfn, nr_pages, altmap, pgmap));

return page;
}

static void depopulate_section_memmap(unsigned long pfn, unsigned long nr_pages,
- struct vmem_altmap *altmap)
+ struct vmem_altmap *altmap, struct dev_pagemap *pgmap)
{
unsigned long start = (unsigned long) pfn_to_page(pfn);
unsigned long end = start + nr_pages * sizeof(struct page);

- memmap_pages_add(-1L * (DIV_ROUND_UP(nr_pages * sizeof(struct page), PAGE_SIZE)));
+ memmap_pages_add(-section_nr_vmemmap_pages(pfn, nr_pages, altmap, pgmap));
vmemmap_free(start, end, altmap);
}

@@ -678,9 +703,10 @@ static void free_map_bootmem(struct page *memmap)
{
unsigned long start = (unsigned long)memmap;
unsigned long end = (unsigned long)(memmap + PAGES_PER_SECTION);
+ unsigned long pfn = page_to_pfn(memmap);

- memmap_boot_pages_add(-1L * (DIV_ROUND_UP(PAGES_PER_SECTION * sizeof(struct page),
- PAGE_SIZE)));
+ memmap_boot_pages_add(-section_nr_vmemmap_pages(pfn, PAGES_PER_SECTION,
+ NULL, NULL));
vmemmap_free(start, end, NULL);
}

@@ -746,7 +772,7 @@ static int fill_subsection_map(unsigned long pfn, unsigned long nr_pages)
* usage map, but still need to free the vmemmap range.
*/
static void section_deactivate(unsigned long pfn, unsigned long nr_pages,
- struct vmem_altmap *altmap)
+ struct vmem_altmap *altmap, struct dev_pagemap *pgmap)
{
struct mem_section *ms = __pfn_to_section(pfn);
bool section_is_early = early_section(ms);
@@ -784,7 +810,7 @@ static void section_deactivate(unsigned long pfn, unsigned long nr_pages,
* section_activate() and pfn_valid() .
*/
if (!section_is_early)
- depopulate_section_memmap(pfn, nr_pages, altmap);
+ depopulate_section_memmap(pfn, nr_pages, altmap, pgmap);
else if (memmap)
free_map_bootmem(memmap);

@@ -828,7 +854,7 @@ static struct page * __meminit section_activate(int nid, unsigned long pfn,

memmap = populate_section_memmap(pfn, nr_pages, nid, altmap, pgmap);
if (!memmap) {
- section_deactivate(pfn, nr_pages, altmap);
+ section_deactivate(pfn, nr_pages, altmap, pgmap);
return ERR_PTR(-ENOMEM);
}

@@ -889,13 +915,13 @@ int __meminit sparse_add_section(int nid, unsigned long start_pfn,
}

void sparse_remove_section(unsigned long pfn, unsigned long nr_pages,
- struct vmem_altmap *altmap)
+ struct vmem_altmap *altmap, struct dev_pagemap *pgmap)
{
struct mem_section *ms = __pfn_to_section(pfn);

if (WARN_ON_ONCE(!valid_section(ms)))
return;

- section_deactivate(pfn, nr_pages, altmap);
+ section_deactivate(pfn, nr_pages, altmap, pgmap);
}
#endif /* CONFIG_MEMORY_HOTPLUG */
diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
index 80cc8be5725f..01b151dca308 100644
--- a/mm/userfaultfd.c
+++ b/mm/userfaultfd.c
@@ -2095,7 +2095,10 @@ bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags,
{
const struct vm_uffd_ops *ops = vma_uffd_ops(vma);

- if (vma->vm_flags & VM_DROPPABLE)
+ if (vma->vm_flags & (VM_DROPPABLE | VM_SHADOW_STACK))
+ return false;
+
+ if (!is_vm_hugetlb_page(vma) && (vma->vm_flags & VM_SPECIAL))
return false;

vm_flags &= __VM_UFFD_FLAGS;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 1cd5f97daafe..55a2de5c8c8e 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2766,7 +2766,7 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
}

mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type,
- cp->reason, mgmt_conn);
+ hci_to_mgmt_reason(cp->reason), mgmt_conn);

hci_disconn_cfm(conn, cp->reason);

@@ -3384,22 +3384,6 @@ static void hci_conn_request_evt(struct hci_dev *hdev, void *data,
hci_dev_unlock(hdev);
}

-static u8 hci_to_mgmt_reason(u8 err)
-{
- switch (err) {
- case HCI_ERROR_CONNECTION_TIMEOUT:
- return MGMT_DEV_DISCONN_TIMEOUT;
- case HCI_ERROR_REMOTE_USER_TERM:
- case HCI_ERROR_REMOTE_LOW_RESOURCES:
- case HCI_ERROR_REMOTE_POWER_OFF:
- return MGMT_DEV_DISCONN_REMOTE;
- case HCI_ERROR_LOCAL_HOST_TERM:
- return MGMT_DEV_DISCONN_LOCAL_HOST;
- default:
- return MGMT_DEV_DISCONN_UNKNOWN;
- }
-}
-
static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index a3df69bdec1e..42bbffba2172 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -929,12 +929,16 @@ int hci_update_eir_sync(struct hci_dev *hdev)

memset(&cp, 0, sizeof(cp));

+ hci_dev_lock(hdev);
eir_create(hdev, cp.data);

- if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
+ if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) {
+ hci_dev_unlock(hdev);
return 0;
+ }

memcpy(hdev->eir, cp.data, sizeof(cp.data));
+ hci_dev_unlock(hdev);

return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp,
HCI_CMD_TIMEOUT);
@@ -966,6 +970,7 @@ int hci_update_class_sync(struct hci_dev *hdev)
if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
return 0;

+ hci_dev_lock(hdev);
cod[0] = hdev->minor_class;
cod[1] = hdev->major_class;
cod[2] = get_service_classes(hdev);
@@ -973,8 +978,12 @@ int hci_update_class_sync(struct hci_dev *hdev)
if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
cod[1] |= 0x20;

- if (memcmp(cod, hdev->dev_class, 3) == 0)
+ if (memcmp(cod, hdev->dev_class, 3) == 0) {
+ hci_dev_unlock(hdev);
return 0;
+ }
+
+ hci_dev_unlock(hdev);

return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_CLASS_OF_DEV,
sizeof(cod), cod, HCI_CMD_TIMEOUT);
@@ -1054,14 +1063,19 @@ static int hci_set_random_addr_sync(struct hci_dev *hdev, bdaddr_t *rpa)
* In this kind of scenario skip the update and let the random
* address be updated at the next cycle.
*/
+ rcu_read_lock();
+
if (bacmp(&hdev->random_addr, BDADDR_ANY) &&
(hci_dev_test_flag(hdev, HCI_LE_ADV) ||
hci_lookup_le_connect(hdev))) {
bt_dev_dbg(hdev, "Deferring random address update");
hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
+ rcu_read_unlock();
return 0;
}

+ rcu_read_unlock();
+
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_RANDOM_ADDR,
6, rpa, HCI_CMD_TIMEOUT);
}
@@ -2647,12 +2661,17 @@ static int hci_pause_addr_resolution(struct hci_dev *hdev)
/* Cannot disable addr resolution if scanning is enabled or
* when initiating an LE connection.
*/
+ rcu_read_lock();
+
if (hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
hci_lookup_le_connect(hdev)) {
+ rcu_read_unlock();
bt_dev_err(hdev, "Command not allowed when scan/LE connect");
return -EPERM;
}

+ rcu_read_unlock();
+
/* Cannot disable addr resolution if advertising is enabled. */
err = hci_pause_advertising_sync(hdev);
if (err) {
@@ -2790,6 +2809,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
if (hci_dev_test_flag(hdev, HCI_PA_SYNC)) {
struct hci_conn *conn;

+ rcu_read_lock();
+
conn = hci_conn_hash_lookup_create_pa_sync(hdev);
if (conn) {
struct conn_params pa;
@@ -2799,6 +2820,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
bacpy(&pa.addr, &conn->dst);
pa.addr_type = conn->dst_type;

+ rcu_read_unlock();
+
/* Clear first since there could be addresses left
* behind.
*/
@@ -2808,6 +2831,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
err = hci_le_add_accept_list_sync(hdev, &pa,
&num_entries);
goto done;
+ } else {
+ rcu_read_unlock();
}
}

@@ -2818,10 +2843,13 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
* the controller.
*/
list_for_each_entry_safe(b, t, &hdev->le_accept_list, list) {
- if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type))
+ rcu_read_lock();
+
+ if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type)) {
+ rcu_read_unlock();
continue;
+ }

- /* Pointers not dereferenced, no locks needed */
pend_conn = hci_pend_le_action_lookup(&hdev->pend_le_conns,
&b->bdaddr,
b->bdaddr_type);
@@ -2829,6 +2857,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
&b->bdaddr,
b->bdaddr_type);

+ rcu_read_unlock();
+
/* If the device is not likely to connect or report,
* remove it from the acceptlist.
*/
@@ -2955,6 +2985,8 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
if (sent) {
struct hci_conn *conn;

+ rcu_read_lock();
+
conn = hci_conn_hash_lookup_ba(hdev, PA_LINK,
&sent->bdaddr);
if (conn) {
@@ -2979,8 +3011,12 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
phy++;
}

+ rcu_read_unlock();
+
if (num_phy)
goto done;
+ } else {
+ rcu_read_unlock();
}
}
}
@@ -3231,12 +3267,16 @@ int hci_update_passive_scan_sync(struct hci_dev *hdev)
/* If there is at least one pending LE connection, we should
* keep the background scan running.
*/
+ bool exists;

/* If controller is connecting, we should not start scanning
* since some controllers are not able to scan and connect at
* the same time.
*/
- if (hci_lookup_le_connect(hdev))
+ rcu_read_lock();
+ exists = hci_lookup_le_connect(hdev);
+ rcu_read_unlock();
+ if (exists)
return 0;

bt_dev_dbg(hdev, "start background scanning");
@@ -3454,6 +3494,7 @@ int hci_write_fast_connectable_sync(struct hci_dev *hdev, bool enable)
}

static bool disconnected_accept_list_entries(struct hci_dev *hdev)
+ __must_hold(&hdev->lock)
{
struct bdaddr_list *b;

@@ -3494,12 +3535,16 @@ int hci_update_scan_sync(struct hci_dev *hdev)
if (hdev->scanning_paused)
return 0;

+ hci_dev_lock(hdev);
+
if (hci_dev_test_flag(hdev, HCI_CONNECTABLE) ||
disconnected_accept_list_entries(hdev))
scan = SCAN_PAGE;
else
scan = SCAN_DISABLED;

+ hci_dev_unlock(hdev);
+
if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
scan |= SCAN_INQUIRY;

@@ -6643,6 +6688,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
if (!hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES))
hci_pause_advertising_sync(hdev);

+ hci_dev_lock(hdev);
+
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
if (params) {
conn->le_conn_min_interval = params->conn_min_interval;
@@ -6656,6 +6703,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
conn->le_supv_timeout = hdev->le_supv_timeout;
}

+ hci_dev_unlock(hdev);
+
/* If controller is scanning, we stop it since some controllers are
* not able to scan and connect at the same time. Also set the
* HCI_LE_SCAN_INTERRUPTED flag so that the command complete
@@ -7213,13 +7262,13 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
}

static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn,
- struct hci_conn *acl, struct bt_iso_qos *qos)
+ u16 acl_handle, struct bt_iso_qos *qos)
{
struct hci_cp_le_past_params cp;
int err;

memset(&cp, 0, sizeof(cp));
- cp.handle = cpu_to_le16(acl->handle);
+ cp.handle = cpu_to_le16(acl_handle);
/* An HCI_LE_Periodic_Advertising_Sync_Transfer_Received event is sent
* to the Host. HCI_LE_Periodic_Advertising_Report events will be
* enabled with duplicate filtering enabled.
@@ -7284,16 +7333,28 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
* 2. Check if that HCI_CONN_FLAG_PAST has been set which indicates that
* user really intended to use PAST.
*/
+ hci_dev_lock(hdev);
+
le = hci_conn_hash_lookup_le(hdev, &conn->dst, conn->dst_type);
if (le) {
struct hci_conn_params *params;
+ hci_conn_flags_t flags = 0;
+ u16 le_handle = le->handle;

params = hci_conn_params_lookup(hdev, &le->dst, le->dst_type);
- if (params && params->flags & HCI_CONN_FLAG_PAST) {
- err = hci_le_past_params_sync(hdev, conn, le, qos);
+ if (params)
+ flags = params->flags;
+
+ hci_dev_unlock(hdev);
+
+ if (flags & HCI_CONN_FLAG_PAST) {
+ err = hci_le_past_params_sync(hdev, conn, le_handle,
+ qos);
if (!err)
goto done;
}
+ } else {
+ hci_dev_unlock(hdev);
}

/* SID has not been set listen for HCI_EV_LE_EXT_ADV_REPORT to update
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index e8544194a524..c18eea5eb781 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -3094,6 +3094,8 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
struct mgmt_cp_unpair_device *cp = cmd->param;
struct hci_conn *conn;

+ hci_dev_lock(hdev);
+
if (cp->addr.type == BDADDR_BREDR)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
&cp->addr.bdaddr);
@@ -3101,6 +3103,11 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
le_addr_type(cp->addr.type));

+ if (conn)
+ hci_conn_get(conn);
+
+ hci_dev_unlock(hdev);
+
if (!conn)
return 0;

@@ -3108,6 +3115,7 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
* will clean up the connection no matter the error.
*/
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
+ hci_conn_put(conn);

return 0;
}
@@ -3255,6 +3263,8 @@ static int disconnect_sync(struct hci_dev *hdev, void *data)
struct mgmt_cp_disconnect *cp = cmd->param;
struct hci_conn *conn;

+ hci_dev_lock(hdev);
+
if (cp->addr.type == BDADDR_BREDR)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
&cp->addr.bdaddr);
@@ -3262,6 +3272,11 @@ static int disconnect_sync(struct hci_dev *hdev, void *data)
conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
le_addr_type(cp->addr.type));

+ if (conn)
+ hci_conn_get(conn);
+
+ hci_dev_unlock(hdev);
+
if (!conn)
return -ENOTCONN;

@@ -3269,6 +3284,7 @@ static int disconnect_sync(struct hci_dev *hdev, void *data)
* will clean up the connection no matter the error.
*/
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
+ hci_conn_put(conn);

return 0;
}
@@ -7391,6 +7407,9 @@ static void get_conn_info_complete(struct hci_dev *hdev, void *data, int err)
rp.max_tx_power = HCI_TX_POWER_INVALID;
}

+ if (conn)
+ hci_conn_put(conn);
+
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, MGMT_OP_GET_CONN_INFO, status,
&rp, sizeof(rp));

@@ -7405,6 +7424,8 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data)
int err;
__le16 handle;

+ hci_dev_lock(hdev);
+
/* Make sure we are still connected */
if (cp->addr.type == BDADDR_BREDR)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
@@ -7412,12 +7433,16 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data)
else
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);

- if (!conn || conn->state != BT_CONNECTED)
+ if (!conn || conn->state != BT_CONNECTED) {
+ hci_dev_unlock(hdev);
return MGMT_STATUS_NOT_CONNECTED;
+ }

- cmd->user_data = conn;
+ cmd->user_data = hci_conn_get(conn);
handle = cpu_to_le16(conn->handle);

+ hci_dev_unlock(hdev);
+
/* Refresh RSSI each time */
err = hci_read_rssi_sync(hdev, handle);

@@ -7551,6 +7576,9 @@ static void get_clock_info_complete(struct hci_dev *hdev, void *data, int err)
}

complete:
+ if (conn)
+ hci_conn_put(conn);
+
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, status, &rp,
sizeof(rp));

@@ -7567,15 +7595,21 @@ static int get_clock_info_sync(struct hci_dev *hdev, void *data)
memset(&hci_cp, 0, sizeof(hci_cp));
hci_read_clock_sync(hdev, &hci_cp);

+ hci_dev_lock(hdev);
+
/* Make sure connection still exists */
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
- if (!conn || conn->state != BT_CONNECTED)
+ if (!conn || conn->state != BT_CONNECTED) {
+ hci_dev_unlock(hdev);
return MGMT_STATUS_NOT_CONNECTED;
+ }

- cmd->user_data = conn;
+ cmd->user_data = hci_conn_get(conn);
hci_cp.handle = cpu_to_le16(conn->handle);
hci_cp.which = 0x01; /* Piconet clock */

+ hci_dev_unlock(hdev);
+
return hci_read_clock_sync(hdev, &hci_cp);
}

@@ -7940,14 +7974,36 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,

static int conn_update_sync(struct hci_dev *hdev, void *data)
{
- struct hci_conn_params *params = data;
- struct hci_conn *conn;
+ struct hci_conn *conn = data;
+ struct hci_conn_params *params;
+ struct hci_conn_params local = {};

- conn = hci_conn_hash_lookup_le(hdev, &params->addr, params->addr_type);
- if (!conn)
- return -ECANCELED;
+ hci_dev_lock(hdev);
+
+ if (!hci_conn_valid(hdev, conn) || conn->role != HCI_ROLE_MASTER)
+ goto cancel;
+
+ params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
+ if (!params)
+ goto cancel;
+
+ local.conn_min_interval = params->conn_min_interval;
+ local.conn_max_interval = params->conn_max_interval;
+ local.conn_latency = params->conn_latency;
+ local.supervision_timeout = params->supervision_timeout;
+
+ hci_dev_unlock(hdev);
+
+ return hci_le_conn_update_sync(hdev, conn, &local);

- return hci_le_conn_update_sync(hdev, conn, params);
+cancel:
+ hci_dev_unlock(hdev);
+ return -ECANCELED;
+}
+
+static void conn_update_sync_destroy(struct hci_dev *hdev, void *data, int err)
+{
+ hci_conn_put(data);
}

static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -8057,9 +8113,13 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
(conn->le_conn_min_interval != min ||
conn->le_conn_max_interval != max ||
conn->le_conn_latency != latency ||
- conn->le_supv_timeout != timeout))
- hci_cmd_sync_queue(hdev, conn_update_sync,
- hci_param, NULL);
+ conn->le_supv_timeout != timeout)) {
+ hci_conn_get(conn);
+ if (hci_cmd_sync_queue(hdev, conn_update_sync,
+ conn,
+ conn_update_sync_destroy) < 0)
+ hci_conn_put(conn);
+ }
}
}

@@ -9851,6 +9911,22 @@ bool mgmt_powering_down(struct hci_dev *hdev)
return false;
}

+u8 hci_to_mgmt_reason(u8 err)
+{
+ switch (err) {
+ case HCI_ERROR_CONNECTION_TIMEOUT:
+ return MGMT_DEV_DISCONN_TIMEOUT;
+ case HCI_ERROR_REMOTE_USER_TERM:
+ case HCI_ERROR_REMOTE_LOW_RESOURCES:
+ case HCI_ERROR_REMOTE_POWER_OFF:
+ return MGMT_DEV_DISCONN_REMOTE;
+ case HCI_ERROR_LOCAL_HOST_TERM:
+ return MGMT_DEV_DISCONN_LOCAL_HOST;
+ default:
+ return MGMT_DEV_DISCONN_UNKNOWN;
+ }
+}
+
void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 reason,
bool mgmt_connected)
@@ -9912,7 +9988,8 @@ void mgmt_connect_failed(struct hci_dev *hdev, struct hci_conn *conn, u8 status)

if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) {
mgmt_device_disconnected(hdev, &conn->dst, conn->type,
- conn->dst_type, status, true);
+ conn->dst_type,
+ hci_to_mgmt_reason(status), true);
return;
}

diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 364b9381c2dc..7f7bab2728cb 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -1031,6 +1031,23 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
return rfcomm_send_frame(s, buf, ptr - buf);
}

+int rfcomm_dlc_send_rpn(struct rfcomm_dlc *d, u8 bit_rate, u8 data_bits,
+ u8 stop_bits, u8 parity, u8 flow_ctrl_settings,
+ u8 xon_char, u8 xoff_char, u16 param_mask)
+{
+ int err = -ENOTCONN;
+
+ rfcomm_lock();
+ if (d->session)
+ err = rfcomm_send_rpn(d->session, 1, d->dlci, bit_rate,
+ data_bits, stop_bits, parity,
+ flow_ctrl_settings, xon_char, xoff_char,
+ param_mask);
+ rfcomm_unlock();
+
+ return err;
+}
+
static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
{
struct rfcomm_hdr *hdr;
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index 91bf5274262e..45f04128b855 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -861,7 +861,7 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty,

BT_DBG("tty %p termios %p", tty, old);

- if (!dev || !dev->dlc || !dev->dlc->session)
+ if (!dev || !dev->dlc)
return;

/* Handle turning off CRTSCTS */
@@ -982,9 +982,8 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty,
}

if (changes)
- rfcomm_send_rpn(dev->dlc->session, 1, dev->dlc->dlci, baud,
- data_bits, stop_bits, parity,
- RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes);
+ rfcomm_dlc_send_rpn(dev->dlc, baud, data_bits, stop_bits, parity,
+ RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes);
}

static void rfcomm_tty_throttle(struct tty_struct *tty)
diff --git a/net/bridge/br_netlink_tunnel.c b/net/bridge/br_netlink_tunnel.c
index 71a12da30004..a713668ea34f 100644
--- a/net/bridge/br_netlink_tunnel.c
+++ b/net/bridge/br_netlink_tunnel.c
@@ -271,7 +271,8 @@ static void __vlan_tunnel_handle_range(const struct net_bridge_port *p,
if (!*v_start)
goto out_init;

- if (v && curr_change && br_vlan_can_enter_range(v, *v_end)) {
+ if (v && curr_change &&
+ br_vlan_can_enter_range(v, *v_end, br_get_pvid(vg))) {
*v_end = v;
return;
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index bed1b1d9b282..6b979a5cc424 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1625,7 +1625,8 @@ void br_vlan_notify(const struct net_bridge *br,
u16 vid, u16 vid_range,
int cmd);
bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
- const struct net_bridge_vlan *range_end);
+ const struct net_bridge_vlan *range_end,
+ u16 pvid);

void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
struct net_device_path_ctx *ctx,
@@ -1872,7 +1873,8 @@ static inline void br_vlan_notify(const struct net_bridge *br,
}

static inline bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
- const struct net_bridge_vlan *range_end)
+ const struct net_bridge_vlan *range_end,
+ u16 pvid)
{
return true;
}
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 84a180927eb7..64a4151761d4 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -1982,9 +1982,11 @@ void br_vlan_notify(const struct net_bridge *br,

/* check if v_curr can enter a range ending in range_end */
bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
- const struct net_bridge_vlan *range_end)
+ const struct net_bridge_vlan *range_end,
+ u16 pvid)
{
- return v_curr->vid - range_end->vid == 1 &&
+ return v_curr->vid != pvid && range_end->vid != pvid &&
+ v_curr->vid - range_end->vid == 1 &&
range_end->flags == v_curr->flags &&
br_vlan_opts_eq_range(v_curr, range_end);
}
@@ -2066,8 +2068,8 @@ static int br_vlan_dump_dev(const struct net_device *dev,
idx += range_end->vid - range_start->vid + 1;

range_start = v;
- } else if (dump_stats || v->vid == pvid ||
- !br_vlan_can_enter_range(v, range_end)) {
+ } else if (dump_stats ||
+ !br_vlan_can_enter_range(v, range_end, pvid)) {
u16 vlan_flags = br_vlan_flags(range_start, pvid);

if (!br_vlan_fill_vids(skb, range_start->vid,
diff --git a/net/bridge/br_vlan_options.c b/net/bridge/br_vlan_options.c
index 5514e1fc8d1f..831e6d3a9c3b 100644
--- a/net/bridge/br_vlan_options.c
+++ b/net/bridge/br_vlan_options.c
@@ -330,8 +330,7 @@ int br_vlan_process_options(const struct net_bridge *br,
continue;
}

- if (v->vid == pvid ||
- !br_vlan_can_enter_range(v, curr_end)) {
+ if (!br_vlan_can_enter_range(v, curr_end, pvid)) {
br_vlan_notify(br, p, curr_start->vid,
curr_end->vid, RTM_NEWVLAN);
curr_start = v;
diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
index df93d57907da..8a31cb23bc76 100644
--- a/net/can/j1939/transport.c
+++ b/net/can/j1939/transport.c
@@ -351,6 +351,18 @@ static void j1939_session_skb_drop_old(struct j1939_session *session)
}
}

+static bool j1939_address_is_local(struct j1939_priv *priv, u8 addr)
+{
+ bool local = false;
+
+ read_lock_bh(&priv->lock);
+ if (j1939_address_is_unicast(addr) && priv->ents[addr].nusers)
+ local = true;
+ read_unlock_bh(&priv->lock);
+
+ return local;
+}
+
void j1939_session_skb_queue(struct j1939_session *session,
struct sk_buff *skb)
{
@@ -359,8 +371,7 @@ void j1939_session_skb_queue(struct j1939_session *session,

j1939_ac_fixup(priv, skb);

- if (j1939_address_is_unicast(skcb->addr.da) &&
- priv->ents[skcb->addr.da].nusers)
+ if (j1939_address_is_local(priv, skcb->addr.da))
skcb->flags |= J1939_ECU_LOCAL_DST;

skcb->flags |= J1939_ECU_LOCAL_SRC;
@@ -2038,8 +2049,7 @@ struct j1939_session *j1939_tp_send(struct j1939_priv *priv,
return ERR_PTR(ret);

/* fix DST flags, it may be used there soon */
- if (j1939_address_is_unicast(skcb->addr.da) &&
- priv->ents[skcb->addr.da].nusers)
+ if (j1939_address_is_local(priv, skcb->addr.da))
skcb->flags |= J1939_ECU_LOCAL_DST;

/* src is always local, I'm sending ... */
diff --git a/net/can/raw.c b/net/can/raw.c
index a26942e78e68..82d9c0499c95 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -562,8 +562,8 @@ static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
return RAW_MIN_NAMELEN;
}

-static int raw_setsockopt(struct socket *sock, int level, int optname,
- sockptr_t optval, unsigned int optlen)
+static int raw_setsockopt_locked(struct socket *sock, int optname,
+ sockptr_t optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
struct raw_sock *ro = raw_sk(sk);
@@ -575,9 +575,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
int flag;
int err = 0;

- if (level != SOL_CAN_RAW)
- return -EINVAL;
-
switch (optname) {
case CAN_RAW_FILTER:
if (optlen % sizeof(struct can_filter) != 0)
@@ -598,17 +595,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
return -EFAULT;
}

- rtnl_lock();
- lock_sock(sk);
-
dev = ro->dev;
- if (ro->bound && dev) {
- if (dev->reg_state != NETREG_REGISTERED) {
- if (count > 1)
- kfree(filter);
- err = -ENODEV;
- goto out_fil;
- }
+ if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED) {
+ if (count > 1)
+ kfree(filter);
+ return -ENODEV;
}

if (ro->bound) {
@@ -622,7 +613,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
if (err) {
if (count > 1)
kfree(filter);
- goto out_fil;
+ return err;
}

/* remove old filter registrations */
@@ -642,11 +633,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
}
ro->filter = filter;
ro->count = count;
-
- out_fil:
- release_sock(sk);
- rtnl_unlock();
-
break;

case CAN_RAW_ERR_FILTER:
@@ -658,16 +644,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,

err_mask &= CAN_ERR_MASK;

- rtnl_lock();
- lock_sock(sk);
-
dev = ro->dev;
- if (ro->bound && dev) {
- if (dev->reg_state != NETREG_REGISTERED) {
- err = -ENODEV;
- goto out_err;
- }
- }
+ if (ro->bound && dev && dev->reg_state != NETREG_REGISTERED)
+ return -ENODEV;

/* remove current error mask */
if (ro->bound) {
@@ -676,7 +655,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
err_mask);

if (err)
- goto out_err;
+ return err;

/* remove old err_mask registration */
raw_disable_errfilter(sock_net(sk), dev, sk,
@@ -685,11 +664,6 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,

/* link new err_mask to the socket */
ro->err_mask = err_mask;
-
- out_err:
- release_sock(sk);
- rtnl_unlock();
-
break;

case CAN_RAW_LOOPBACK:
@@ -769,6 +743,26 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
return err;
}

+static int raw_setsockopt(struct socket *sock, int level, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ struct sock *sk = sock->sk;
+ int err;
+
+ if (level != SOL_CAN_RAW)
+ return -EINVAL;
+
+ rtnl_lock();
+ lock_sock(sk);
+
+ err = raw_setsockopt_locked(sock, optname, optval, optlen);
+
+ release_sock(sk);
+ rtnl_unlock();
+
+ return err;
+}
+
static int raw_getsockopt(struct socket *sock, int level, int optname,
sockopt_t *opt)
{
diff --git a/net/ceph/auth_x.c b/net/ceph/auth_x.c
index 9e64e82d0b63..50a79e8aa656 100644
--- a/net/ceph/auth_x.c
+++ b/net/ceph/auth_x.c
@@ -849,9 +849,16 @@ static int ceph_x_update_authorizer(

au = (struct ceph_x_authorizer *)auth->authorizer;
if (au->secret_id < th->secret_id) {
+ int ret;
+
dout("ceph_x_update_authorizer service %u secret %llu < %llu\n",
au->service, au->secret_id, th->secret_id);
- return ceph_x_build_authorizer(ac, th, au);
+ ret = ceph_x_build_authorizer(ac, th, au);
+ if (ret)
+ return ret;
+
+ auth->authorizer_buf = au->buf->vec.iov_base;
+ auth->authorizer_buf_len = au->buf->vec.iov_len;
}
return 0;
}
diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c
index 952121849180..a797c7360e3c 100644
--- a/net/ceph/ceph_common.c
+++ b/net/ceph/ceph_common.c
@@ -762,13 +762,13 @@ void ceph_destroy_client(struct ceph_client *client)

atomic_set(&client->msgr.stopping, 1);

+ ceph_debugfs_client_cleanup(client);
+
/* unmount */
ceph_osdc_stop(&client->osdc);
ceph_monc_stop(&client->monc);
ceph_messenger_fini(&client->msgr);

- ceph_debugfs_client_cleanup(client);
-
ceph_destroy_options(client->options);

kfree(client);
diff --git a/net/ceph/cls_lock_client.c b/net/ceph/cls_lock_client.c
index c6956f1df333..377336982f7d 100644
--- a/net/ceph/cls_lock_client.c
+++ b/net/ceph/cls_lock_client.c
@@ -299,7 +299,7 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag,
if (ret)
return ret;

- *num_lockers = ceph_decode_32(p);
+ ceph_decode_32_safe(p, end, *num_lockers, err_inval);
*lockers = kzalloc_objs(**lockers, *num_lockers, GFP_NOIO);
if (!*lockers)
return -ENOMEM;
@@ -310,7 +310,8 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag,
goto err_free_lockers;
}

- *type = ceph_decode_8(p);
+ ret = -EINVAL;
+ ceph_decode_8_safe(p, end, *type, err_free_lockers);
s = ceph_extract_encoded_string(p, end, NULL, GFP_NOIO);
if (IS_ERR(s)) {
ret = PTR_ERR(s);
@@ -320,6 +321,9 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag,
*tag = s;
return 0;

+err_inval:
+ return -EINVAL;
+
err_free_lockers:
ceph_free_lockers(*lockers, *num_lockers);
return ret;
diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c
index d2cdc8ee3155..c56457378d00 100644
--- a/net/ceph/mon_client.c
+++ b/net/ceph/mon_client.c
@@ -114,7 +114,7 @@ static struct ceph_monmap *ceph_monmap_decode(void **p, void *end, bool msgr2)

dout("%s fsid %pU epoch %u num_mon %u\n", __func__, &fsid, epoch,
num_mon);
- if (num_mon > CEPH_MAX_MON)
+ if (num_mon == 0 || num_mon > CEPH_MAX_MON)
goto e_inval;

monmap = kmalloc_flex(*monmap, mon_inst, num_mon, GFP_NOIO);
@@ -821,7 +821,7 @@ static void handle_get_version_reply(struct ceph_mon_client *monc,
struct ceph_mon_generic_request *req;
u64 tid = le64_to_cpu(msg->hdr.tid);
void *p = msg->front.iov_base;
- void *end = p + msg->front_alloc_len;
+ void *const end = p + msg->front.iov_len;
u64 handle;

dout("%s msg %p tid %llu\n", __func__, msg, tid);
diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index 8b5b0587a0cf..a4b0dd8672ec 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -518,6 +518,8 @@ static struct crush_map *crush_decode(void *pbyval, void *end)
ceph_decode_need(p, end, 4*sizeof(u32), bad);
b->id = ceph_decode_32(p);
b->type = ceph_decode_16(p);
+ if (b->type == 0)
+ goto bad;
b->alg = ceph_decode_8(p);
if (b->alg != alg) {
b->alg = 0;
@@ -1436,7 +1438,7 @@ static struct ceph_pg_mapping *__decode_pg_temp(void **p, void *end,
ceph_decode_32_safe(p, end, len, e_inval);
if (len == 0 && incremental)
return NULL; /* new_pg_temp: [] to remove */
- if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32))
+ if (len > CEPH_PG_MAX_SIZE)
return ERR_PTR(-EINVAL);

ceph_decode_need(p, end, len * sizeof(u32), e_inval);
@@ -1617,7 +1619,7 @@ static struct ceph_pg_mapping *__decode_pg_upmap_items(void **p, void *end,
u32 len, i;

ceph_decode_32_safe(p, end, len, e_inval);
- if ((size_t)len > (SIZE_MAX - sizeof(*pg)) / (2 * sizeof(u32)))
+ if (len > CEPH_PG_MAX_SIZE)
return ERR_PTR(-EINVAL);

ceph_decode_need(p, end, 2 * len * sizeof(u32), e_inval);
@@ -1842,6 +1844,8 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v,
void *new_up_client;
void *new_state;
void *new_weight_end;
+ const u32 new_state_item_size =
+ sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8));
u32 len;
int ret;
int i;
@@ -1862,7 +1866,8 @@ static int decode_new_up_state_weight(void **p, void *end, u8 struct_v,

new_state = *p;
ceph_decode_32_safe(p, end, len, e_inval);
- len *= sizeof(u32) + (struct_v >= 5 ? sizeof(u32) : sizeof(u8));
+ if (check_mul_overflow(len, new_state_item_size, &len))
+ goto e_inval;
ceph_decode_need(p, end, len, e_inval);
*p += len;

@@ -3055,8 +3060,11 @@ static int get_immediate_parent(struct crush_map *c, int id,
if (b->items[j] != id)
continue;

- *parent_type_id = b->type;
type_cn = lookup_crush_name(&c->type_names, b->type);
+ if (WARN_ON_ONCE(!type_cn))
+ continue;
+
+ *parent_type_id = b->type;
parent_loc->cl_type_name = type_cn->cn_name;
parent_loc->cl_name = cn->cn_name;
return b->id;
diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c
index ecd659f79fd4..1d295a8769fa 100644
--- a/net/core/bpf_sk_storage.c
+++ b/net/core/bpf_sk_storage.c
@@ -158,8 +158,6 @@ int bpf_sk_storage_clone(const struct sock *sk, struct sock *newsk)
struct bpf_local_storage_elem *selem;
int ret = 0;

- RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL);
-
rcu_read_lock_dont_migrate();
sk_storage = rcu_dereference(sk->sk_bpf_storage);

diff --git a/net/core/dev.c b/net/core/dev.c
index f81ce83fb325..31741169cb0b 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4016,6 +4016,9 @@ static struct sk_buff *validate_xmit_unreadable_skb(struct sk_buff *skb,
return NULL;
}

+/* Returns the skb on success, NULL if dropped, or ERR_PTR(-EINPROGRESS)
+ * if stolen by async xfrm crypto (delivered via xfrm_dev_resume()).
+ */
static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev, bool *again)
{
netdev_features_t features;
@@ -4087,7 +4090,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d
skb->prev = skb;

skb = validate_xmit_skb(skb, dev, again);
- if (!skb)
+ if (IS_ERR_OR_NULL(skb))
continue;

if (!head)
@@ -4858,8 +4861,11 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
goto recursion_alert;

skb = validate_xmit_skb(skb, dev, &again);
- if (!skb)
+ if (IS_ERR_OR_NULL(skb)) {
+ if (PTR_ERR(skb) == -EINPROGRESS)
+ rc = NET_XMIT_SUCCESS;
goto out;
+ }

HARD_TX_LOCK(dev, txq, cpu);

diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
index f23cea9e1aaf..5df3e18ebaf7 100644
--- a/net/core/drop_monitor.c
+++ b/net/core/drop_monitor.c
@@ -530,10 +530,10 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore,
return;

unlock_free:
- spin_unlock_irqrestore(&data->drop_queue.lock, flags);
u64_stats_update_begin(&data->stats.syncp);
u64_stats_inc(&data->stats.dropped);
u64_stats_update_end(&data->stats.syncp);
+ spin_unlock_irqrestore(&data->drop_queue.lock, flags);
consume_skb(nskb);
}

@@ -566,13 +566,13 @@ static size_t net_dm_packet_report_size(size_t payload_len)
/* NET_DM_ATTR_ORIGIN */
nla_total_size(sizeof(u16)) +
/* NET_DM_ATTR_PC */
- nla_total_size(sizeof(u64)) +
+ nla_total_size_64bit(sizeof(u64)) +
/* NET_DM_ATTR_SYMBOL */
nla_total_size(NET_DM_MAX_SYMBOL_LEN + 1) +
/* NET_DM_ATTR_IN_PORT */
net_dm_in_port_size() +
/* NET_DM_ATTR_TIMESTAMP */
- nla_total_size(sizeof(u64)) +
+ nla_total_size_64bit(sizeof(u64)) +
/* NET_DM_ATTR_ORIG_LEN */
nla_total_size(sizeof(u32)) +
/* NET_DM_ATTR_PROTO */
@@ -671,9 +671,7 @@ static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb,
if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol)))
goto nla_put_failure;

- attr = skb_put(msg, nla_total_size(payload_len));
- attr->nla_type = NET_DM_ATTR_PAYLOAD;
- attr->nla_len = nla_attr_size(payload_len);
+ attr = __nla_reserve(msg, NET_DM_ATTR_PAYLOAD, payload_len);
if (skb_copy_bits(skb, 0, nla_data(attr), payload_len))
goto nla_put_failure;

@@ -768,7 +766,7 @@ net_dm_hw_packet_report_size(size_t payload_len,
/* NET_DM_ATTR_FLOW_ACTION_COOKIE */
net_dm_flow_action_cookie_size(hw_metadata) +
/* NET_DM_ATTR_TIMESTAMP */
- nla_total_size(sizeof(u64)) +
+ nla_total_size_64bit(sizeof(u64)) +
/* NET_DM_ATTR_ORIG_LEN */
nla_total_size(sizeof(u32)) +
/* NET_DM_ATTR_PROTO */
@@ -831,9 +829,7 @@ static int net_dm_hw_packet_report_fill(struct sk_buff *msg,
if (nla_put_u16(msg, NET_DM_ATTR_PROTO, be16_to_cpu(skb->protocol)))
goto nla_put_failure;

- attr = skb_put(msg, nla_total_size(payload_len));
- attr->nla_type = NET_DM_ATTR_PAYLOAD;
- attr->nla_len = nla_attr_size(payload_len);
+ attr = __nla_reserve(msg, NET_DM_ATTR_PAYLOAD, payload_len);
if (skb_copy_bits(skb, 0, nla_data(attr), payload_len))
goto nla_put_failure;

@@ -1001,10 +997,10 @@ net_dm_hw_trap_packet_probe(void *ignore, const struct devlink *devlink,
return;

unlock_free:
- spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags);
u64_stats_update_begin(&hw_data->stats.syncp);
u64_stats_inc(&hw_data->stats.dropped);
u64_stats_update_end(&hw_data->stats.syncp);
+ spin_unlock_irqrestore(&hw_data->drop_queue.lock, flags);
net_dm_hw_metadata_free(n_hw_metadata);
free:
consume_skb(nskb);
diff --git a/net/core/filter.c b/net/core/filter.c
index f21aeff99cc9..66837e93d3f2 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2552,11 +2552,13 @@ int skb_do_redirect(struct sk_buff *skb)

BPF_CALL_2(bpf_redirect, u32, ifindex, u64, flags)
{
- struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
+ struct bpf_redirect_info *ri;

- if (unlikely(flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL)))
+ if (unlikely(!bpf_net_ctx_get() ||
+ (flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL))))
return TC_ACT_SHOT;

+ ri = bpf_net_ctx_get_ri();
ri->flags = flags;
ri->tgt_index = ifindex;

@@ -2573,11 +2575,12 @@ static const struct bpf_func_proto bpf_redirect_proto = {

BPF_CALL_2(bpf_redirect_peer, u32, ifindex, u64, flags)
{
- struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
+ struct bpf_redirect_info *ri;

- if (unlikely(flags))
+ if (unlikely(!bpf_net_ctx_get() || flags))
return TC_ACT_SHOT;

+ ri = bpf_net_ctx_get_ri();
ri->flags = BPF_F_PEER;
ri->tgt_index = ifindex;

@@ -2595,11 +2598,13 @@ static const struct bpf_func_proto bpf_redirect_peer_proto = {
BPF_CALL_4(bpf_redirect_neigh, u32, ifindex, struct bpf_redir_neigh *, params,
int, plen, u64, flags)
{
- struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
+ struct bpf_redirect_info *ri;

- if (unlikely((plen && plen < sizeof(*params)) || flags))
+ if (unlikely((plen && plen < sizeof(*params)) ||
+ !bpf_net_ctx_get() || flags))
return TC_ACT_SHOT;

+ ri = bpf_net_ctx_get_ri();
ri->flags = BPF_F_NEIGH | (plen ? BPF_F_NEXTHOP : 0);
ri->tgt_index = ifindex;

diff --git a/net/core/gro.c b/net/core/gro.c
index 35f2f708f010..29b4d02bf519 100644
--- a/net/core/gro.c
+++ b/net/core/gro.c
@@ -229,7 +229,9 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb)

int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
{
- if (unlikely(p->len + skb->len >= 65536))
+ /* make sure to check flush flag and to not merge */
+ if (unlikely(p->len + skb->len >= 65536 ||
+ NAPI_GRO_CB(skb)->flush))
return -E2BIG;

if (!pskb_may_pull(skb, skb_gro_offset(skb))) {
diff --git a/net/core/sock.c b/net/core/sock.c
index cab041b57d28..5714436b0871 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2492,6 +2492,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
sock_copy(newsk, sk);

newsk->sk_prot_creator = prot;
+#ifdef CONFIG_BPF_SYSCALL
+ RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL);
+#endif

/* SANITY */
if (likely(newsk->sk_net_refcnt)) {
@@ -2544,6 +2547,11 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,

cgroup_sk_clone(&newsk->sk_cgrp_data);

+ RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL);
+
+ if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP)
+ net_enable_timestamp();
+
rcu_read_lock();
filter = rcu_dereference(sk->sk_filter);
if (filter != NULL)
@@ -2566,8 +2574,6 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
goto free;
}

- RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL);
-
if (bpf_sk_storage_clone(sk, newsk))
goto free;

@@ -2595,9 +2601,6 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,

if (newsk->sk_prot->sockets_allocated)
sk_sockets_allocated_inc(newsk);
-
- if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP)
- net_enable_timestamp();
out:
return newsk;
free:
diff --git a/net/core/sock_map.c b/net/core/sock_map.c
index 99e3789492a0..9ece7bafe27c 100644
--- a/net/core/sock_map.c
+++ b/net/core/sock_map.c
@@ -542,6 +542,8 @@ static bool sock_map_sk_state_allowed(const struct sock *sk)
{
if (sk_is_tcp(sk))
return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_LISTEN);
+ if (sk_is_udp(sk))
+ return sk_hashed(sk);
if (sk_is_stream_unix(sk))
return (1 << READ_ONCE(sk->sk_state)) & TCPF_ESTABLISHED;
if (sk_is_vsock(sk) &&
diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c
index d9af9e65f72f..01c73b4b50dd 100644
--- a/net/hsr/hsr_slave.c
+++ b/net/hsr/hsr_slave.c
@@ -242,6 +242,8 @@ void hsr_del_port(struct hsr_port *port)
netdev_rx_handler_unregister(port->dev);
if (!port->hsr->fwd_offloaded)
dev_set_promiscuity(port->dev, -1);
+ if (port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B)
+ vlan_vids_del_by_dev(port->dev, master->dev);
netdev_upper_dev_unlink(port->dev, master->dev);
if (hsr->prot_version == PRP_V1 &&
port->type == HSR_PT_SLAVE_B) {
diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c
index 1614593b6d72..7d25519a6cdd 100644
--- a/net/ipv4/datagram.c
+++ b/net/ipv4/datagram.c
@@ -63,12 +63,12 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int
}

/* Update addresses before rehashing */
- inet->inet_daddr = fl4->daddr;
+ WRITE_ONCE(inet->inet_daddr, fl4->daddr);
inet->inet_dport = usin->sin_port;
if (!inet->inet_saddr)
inet->inet_saddr = fl4->saddr;
if (!inet->inet_rcv_saddr) {
- inet->inet_rcv_saddr = fl4->saddr;
+ WRITE_ONCE(inet->inet_rcv_saddr, fl4->saddr);
if (sk->sk_prot->rehash)
sk->sk_prot->rehash(sk);
}
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index dac543c1d686..9bf5550e2fb7 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1385,7 +1385,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb,
out_remove_new_fa:
fib_remove_alias(t, tp, l, new_fa);
out_free_new_fa:
- kmem_cache_free(fn_alias_kmem, new_fa);
+ alias_free_mem_rcu(new_fa);
out:
fib_release_info(fi);
err:
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 23e921d313b3..0caedfc7ca92 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -548,11 +548,23 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,
if (IS_ERR(rt2))
err = PTR_ERR(rt2);
} else {
- struct flowi4 fl4_2 = {};
+ struct flowi4 fl4_2 = fl4_dec;
unsigned long orefdst;

- fl4_2.daddr = fl4_dec.saddr;
- rt2 = ip_route_output_key(net, &fl4_2);
+ swap(fl4_2.daddr, fl4_2.saddr);
+ switch (fl4_2.flowi4_proto) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ case IPPROTO_SCTP:
+ case IPPROTO_DCCP:
+ swap(fl4_2.fl4_sport, fl4_2.fl4_dport);
+ break;
+ }
+
+ fl4_2.flowi4_oif = l3mdev_master_ifindex(route_lookup_dev);
+ fl4_2.flowi4_flags |= FLOWI_FLAG_ANYSRC;
+
+ rt2 = __ip_route_output_key(net, &fl4_2);
if (IS_ERR(rt2)) {
err = PTR_ERR(rt2);
goto relookup_failed;
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 0ebed1438f6c..739b7fae8d3d 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -1018,6 +1018,8 @@ static void __gre_tunnel_init(struct net_device *dev)
dev->features |= GRE_FEATURES;
dev->hw_features |= GRE_FEATURES;

+ dev->lltx = true;
+
/* TCP offload with GRE SEQ is not supported, nor can we support 2
* levels of outer headers requiring an update.
*/
@@ -1029,8 +1031,6 @@ static void __gre_tunnel_init(struct net_device *dev)

dev->features |= NETIF_F_GSO_SOFTWARE;
dev->hw_features |= NETIF_F_GSO_SOFTWARE;
-
- dev->lltx = true;
}

static int ipgre_tunnel_init(struct net_device *dev)
diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c
index f92fcc39fc4c..dc95a3d6e899 100644
--- a/net/ipv4/nexthop.c
+++ b/net/ipv4/nexthop.c
@@ -1788,8 +1788,8 @@ static bool nh_res_bucket_migrate(struct nh_res_table *res_table,
bool notify_nl, bool force)
{
struct nh_res_bucket *bucket = &res_table->nh_buckets[bucket_index];
+ struct netlink_ext_ack extack = {};
struct nh_grp_entry *new_nhge;
- struct netlink_ext_ack extack;
int err;

new_nhge = list_first_entry_or_null(&res_table->uw_nh_entries,
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 68e88cb3e55c..f47b8b49da3d 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -120,13 +120,21 @@ bool raw_v4_match(struct net *net, const struct sock *sk, unsigned short num,
__be32 raddr, __be32 laddr, int dif, int sdif)
{
const struct inet_sock *inet = inet_sk(sk);
+ __be32 daddr, rcv_saddr;

- if (net_eq(sock_net(sk), net) && inet->inet_num == num &&
- !(inet->inet_daddr && inet->inet_daddr != raddr) &&
- !(inet->inet_rcv_saddr && inet->inet_rcv_saddr != laddr) &&
- raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if, dif, sdif))
- return true;
- return false;
+ if (!net_eq(sock_net(sk), net) || inet->inet_num != num)
+ return false;
+
+ daddr = READ_ONCE(inet->inet_daddr);
+ if (daddr && daddr != raddr)
+ return false;
+
+ rcv_saddr = READ_ONCE(inet->inet_rcv_saddr);
+ if (rcv_saddr && rcv_saddr != laddr)
+ return false;
+
+ return raw_sk_bound_dev_eq(net, READ_ONCE(sk->sk_bound_dev_if),
+ dif, sdif);
}
EXPORT_SYMBOL_GPL(raw_v4_match);

@@ -724,7 +732,8 @@ static int raw_bind(struct sock *sk, struct sockaddr_unsized *uaddr,
chk_addr_ret))
goto out;

- inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr;
+ inet->inet_saddr = addr->sin_addr.s_addr;
+ WRITE_ONCE(inet->inet_rcv_saddr, addr->sin_addr.s_addr);
if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
inet->inet_saddr = 0; /* Use device */
sk_dst_reset(sk);
diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c
index cc0bd73f36b6..b9f036633365 100644
--- a/net/ipv4/tcp_bpf.c
+++ b/net/ipv4/tcp_bpf.c
@@ -589,7 +589,7 @@ static int tcp_bpf_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
wait_for_memory:
err = sk_stream_wait_memory(sk, &timeo);
if (err) {
- if (msg_tx && msg_tx != psock->cork)
+ if (msg_tx == &tmp)
sk_msg_free(sk, msg_tx);
goto out_err;
}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index de9f68a9c0cf..eed808c2adbb 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -4038,24 +4038,17 @@ static void tcp_send_ack_reflect_ect(struct sock *sk, bool accecn_reflector)
__tcp_send_ack(sk, tp->rcv_nxt, flags);
}

-/* RFC 5961 7 [ACK Throttling] */
-static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector)
+/* Consume one slot from the per-netns RFC 5961 challenge ACK quota.
+ * Returns true if a challenge ACK may be sent.
+ */
+static bool tcp_challenge_ack_allowed(struct net *net)
{
- struct tcp_sock *tp = tcp_sk(sk);
- struct net *net = sock_net(sk);
u32 count, now, ack_limit;

- /* First check our per-socket dupack rate limit. */
- if (__tcp_oow_rate_limited(net,
- LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
- &tp->last_oow_ack_time))
- return;
-
ack_limit = READ_ONCE(net->ipv4.sysctl_tcp_challenge_ack_limit);
if (ack_limit == INT_MAX)
- goto send_ack;
+ return true;

- /* Then check host-wide RFC 5961 rate limit. */
now = jiffies / HZ;
if (now != READ_ONCE(net->ipv4.tcp_challenge_timestamp)) {
u32 half = (ack_limit + 1) >> 1;
@@ -4067,12 +4060,49 @@ static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector)
count = READ_ONCE(net->ipv4.tcp_challenge_count);
if (count > 0) {
WRITE_ONCE(net->ipv4.tcp_challenge_count, count - 1);
-send_ack:
+ return true;
+ }
+ return false;
+}
+
+/* RFC 5961 7 [ACK Throttling] */
+static void tcp_send_challenge_ack(struct sock *sk, bool accecn_reflector)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct net *net = sock_net(sk);
+
+ /* First check our per-socket dupack rate limit. */
+ if (__tcp_oow_rate_limited(net,
+ LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
+ &tp->last_oow_ack_time))
+ return;
+
+ /* Then check the per-netns RFC 5961 rate limit. */
+ if (tcp_challenge_ack_allowed(net)) {
NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK);
tcp_send_ack_reflect_ect(sk, accecn_reflector);
}
}

+/* Send a challenge ACK from a SYN-RECEIVED request socket. Uses
+ * __tcp_oow_rate_limited() directly so that an RST carrying payload
+ * cannot bypass the per-request rate limit.
+ */
+void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+{
+ struct net *net = sock_net(sk);
+
+ if (__tcp_oow_rate_limited(net, LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
+ &tcp_rsk(req)->last_oow_ack_time))
+ return;
+
+ if (tcp_challenge_ack_allowed(net)) {
+ NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK);
+ req->rsk_ops->send_ack(sk, skb, req);
+ }
+}
+
static void tcp_store_ts_recent(struct tcp_sock *tp)
{
tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index bfdabad01fa7..ce572025eeeb 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -971,6 +971,9 @@ static void tcp_v4_send_ack(const struct sock *sk,
key->rcv_next);
arg.iov[0].iov_len += tcp_ao_len_aligned(key->ao_key);
rep.th.doff = arg.iov[0].iov_len / 4;
+ memset((u8 *)&rep.opt[offset] + tcp_ao_maclen(key->ao_key),
+ TCPOPT_NOP, tcp_ao_len_aligned(key->ao_key) -
+ tcp_ao_len(key->ao_key));

tcp_ao_hash_hdr(AF_INET, (char *)&rep.opt[offset],
key->ao_key, key->traffic_key,
@@ -2319,8 +2322,10 @@ int tcp_v4_rcv(struct sk_buff *skb)
}

drop_reason = psp_twsk_rx_policy_check(inet_twsk(sk), skb);
- if (drop_reason)
- break;
+ if (drop_reason) {
+ inet_twsk_put(inet_twsk(sk));
+ goto discard_it;
+ }
}
/* to ACK */
fallthrough;
@@ -3142,8 +3147,11 @@ static struct sock *bpf_iter_tcp_batch(struct seq_file *seq)
bpf_iter_tcp_put_batch(iter);
err = bpf_iter_tcp_realloc_batch(iter, expected * 3 / 2,
GFP_USER);
- if (err)
+ if (err) {
+ iter->cur_sk = 0;
+ iter->end_sk = 0;
return ERR_PTR(err);
+ }

sk = bpf_iter_tcp_resume(seq);
if (!sk)
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index e6092c3ac840..c2700c9ae815 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -835,7 +835,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
* elsewhere and is checked directly against the child socket rather
* than req because user data may have been sent out.
*/
- if ((flg & TCP_FLAG_ACK) && !fastopen &&
+ if ((flg & TCP_FLAG_ACK) && !(flg & TCP_FLAG_RST) && !fastopen &&
(TCP_SKB_CB(skb)->ack_seq !=
tcp_rsk(req)->snt_isn + 1))
return sk;
@@ -874,6 +874,16 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
flg &= ~TCP_FLAG_SYN;
}

+ /* RFC 5961 section 3.2, as clarified by RFC 9293 section
+ * 3.10.7.4, requires a challenge ACK for a non-exact
+ * in-window RST in SYN-RECEIVED.
+ */
+ if ((flg & TCP_FLAG_RST) &&
+ TCP_SKB_CB(skb)->seq != tcp_rsk(req)->rcv_nxt) {
+ tcp_reqsk_send_challenge_ack(sk, skb, req);
+ return NULL;
+ }
+
/* RFC793: "second check the RST bit" and
* "fourth, check the SYN bit"
*/
diff --git a/net/ipv6/ila/ila_common.c b/net/ipv6/ila/ila_common.c
index e71571455c8a..b78179bfc4c7 100644
--- a/net/ipv6/ila/ila_common.c
+++ b/net/ipv6/ila/ila_common.c
@@ -85,6 +85,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb,
struct tcphdr *th = (struct tcphdr *)
(skb_network_header(skb) + nhoff);

+ ip6h = ipv6_hdr(skb);
diff = get_csum_diff(ip6h, p);
inet_proto_csum_replace_by_diff(&th->check, skb,
diff, true, true);
@@ -96,6 +97,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb,
(skb_network_header(skb) + nhoff);

if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+ ip6h = ipv6_hdr(skb);
diff = get_csum_diff(ip6h, p);
inet_proto_csum_replace_by_diff(&uh->check, skb,
diff, true, true);
@@ -110,6 +112,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb,
struct icmp6hdr *ih = (struct icmp6hdr *)
(skb_network_header(skb) + nhoff);

+ ip6h = ipv6_hdr(skb);
diff = get_csum_diff(ip6h, p);
inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
diff, true, true);
@@ -127,6 +130,15 @@ void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p,
switch (p->csum_mode) {
case ILA_CSUM_ADJUST_TRANSPORT:
ila_csum_adjust_transport(skb, p);
+ /*
+ * ila_csum_adjust_transport() calls pskb_may_pull(), which can
+ * reallocate the skb head and leave ip6h (and the iaddr derived
+ * from it) dangling; reload both before the write below. The
+ * other csum modes do not pull, so their cached pointers stay
+ * valid.
+ */
+ ip6h = ipv6_hdr(skb);
+ iaddr = ila_a2i(&ip6h->daddr);
break;
case ILA_CSUM_NEUTRAL_MAP:
if (sir2ila) {
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 8ebc99a299c9..1a81e80a124a 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -1455,6 +1455,8 @@ static void ip6gre_tnl_init_features(struct net_device *dev)
dev->features |= GRE6_FEATURES;
dev->hw_features |= GRE6_FEATURES;

+ dev->lltx = true;
+
/* TCP offload with GRE SEQ is not supported, nor can we support 2
* levels of outer headers requiring an update.
*/
@@ -1466,8 +1468,6 @@ static void ip6gre_tnl_init_features(struct net_device *dev)

dev->features |= NETIF_F_GSO_SOFTWARE;
dev->hw_features |= NETIF_F_GSO_SOFTWARE;
-
- dev->lltx = true;
}

static int ip6gre_tunnel_init_common(struct net_device *dev)
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 3cc58698cbbd..b88d364e78aa 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -349,7 +349,7 @@ void raw6_icmp_error(struct sk_buff *skb, int nexthdr,
const struct ipv6hdr *ip6h = (const struct ipv6hdr *)skb->data;

if (!raw_v6_match(net, sk, nexthdr, &ip6h->saddr, &ip6h->daddr,
- inet6_iif(skb), inet6_iif(skb)))
+ inet6_iif(skb), inet6_sdif(skb)))
continue;
rawv6_err(sk, skb, type, code, inner_offset, info);
}
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 88b46967bb67..d4edc133c045 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -4020,7 +4020,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
struct fib6_node *fn;

/* prefer to send a single notification with all hops */
- skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
+ skb = nlmsg_new(rt6_nlmsg_size(rt), GFP_ATOMIC);
if (skb) {
u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;

@@ -4076,7 +4076,7 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)

if (skb) {
rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
- info->nlh, gfp_any());
+ info->nlh, GFP_ATOMIC);
}
return err;
}
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 36d75fb50a70..6223c2ce53e8 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -915,6 +915,8 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32
(tcp_ao_len(key->ao_key) << 16) |
(key->ao_key->sndid << 8) |
(key->rcv_next));
+ memset((u8 *)topt + tcp_ao_maclen(key->ao_key), TCPOPT_NOP,
+ tcp_ao_len_aligned(key->ao_key) - tcp_ao_len(key->ao_key));

tcp_ao_hash_hdr(AF_INET6, (char *)topt, key->ao_key,
key->traffic_key,
@@ -1962,8 +1964,10 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb)
}

drop_reason = psp_twsk_rx_policy_check(inet_twsk(sk), skb);
- if (drop_reason)
- break;
+ if (drop_reason) {
+ inet_twsk_put(inet_twsk(sk));
+ goto discard_it;
+ }
}
/* to ACK */
fallthrough;
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 125ea9a5b8a0..3b749475f6ed 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -88,6 +88,7 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev,
xdst->u.rt6.rt6i_idev = in6_dev_get(dev);
if (!xdst->u.rt6.rt6i_idev) {
netdev_put(dev, &xdst->u.dst.dev_tracker);
+ xdst->u.dst.dev = NULL;
return -ENODEV;
}

diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index c2dc3338670e..728c5da52896 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -336,6 +336,7 @@ static void iucv_sever_path(struct sock *sk, int with_user_data)
unsigned char user_data[16];
struct iucv_sock *iucv = iucv_sk(sk);
struct iucv_path *path = iucv->path;
+ struct sock_msg_q *p, *n;

/* Whoever resets the path pointer, must sever and free it. */
if (xchg(&iucv->path, NULL)) {
@@ -347,6 +348,19 @@ static void iucv_sever_path(struct sock *sk, int with_user_data)
} else
pr_iucv->path_sever(path, NULL);
iucv_path_free(path);
+
+ /*
+ * Message notifications queued on message_q still reference
+ * the now freed path; drop them, otherwise a later recvmsg()
+ * would pass the freed iucv_path to message_receive() via
+ * iucv_process_message_q().
+ */
+ spin_lock_bh(&iucv->message_q.lock);
+ list_for_each_entry_safe(p, n, &iucv->message_q.list, list) {
+ list_del(&p->list);
+ kfree(p);
+ }
+ spin_unlock_bh(&iucv->message_q.lock);
}
}

@@ -1874,7 +1888,8 @@ static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb)
afiucv_swap_src_dest(skb);
trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN;
err = dev_queue_xmit(skb);
- iucv_sock_kill(nsk);
+ if (nsk)
+ iucv_sock_kill(nsk);
bh_unlock_sock(sk);
goto out;
}
@@ -2091,6 +2106,8 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
}
}
}
+ if (sk)
+ sock_hold(sk);
read_unlock(&iucv_sk_list.lock);
if (!iucv)
sk = NULL;
@@ -2140,6 +2157,8 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
kfree_skb(skb);
}

+ if (sk)
+ sock_put(sk);
return err;
}

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index f9ee9947a94d..80210ef4cbc8 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1145,9 +1145,6 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata,
fd->max_interval = params->max_interval;

old = sdata_dereference(link->u.ap.fils_discovery, sdata);
- if (old)
- kfree_rcu(old, rcu_head);
-
if (params->tmpl && params->tmpl_len) {
new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
if (!new)
@@ -1159,6 +1156,9 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata,
RCU_INIT_POINTER(link->u.ap.fils_discovery, NULL);
}

+ if (old)
+ kfree_rcu(old, rcu_head);
+
*changed |= BSS_CHANGED_FILS_DISCOVERY;
return 0;
}
@@ -1178,8 +1178,6 @@ ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata,
link_conf->unsol_bcast_probe_resp_interval = params->interval;

old = sdata_dereference(link->u.ap.unsol_bcast_probe_resp, sdata);
- if (old)
- kfree_rcu(old, rcu_head);

if (params->tmpl && params->tmpl_len) {
new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
@@ -1192,6 +1190,9 @@ ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata,
RCU_INIT_POINTER(link->u.ap.unsol_bcast_probe_resp, NULL);
}

+ if (old)
+ kfree_rcu(old, rcu_head);
+
*changed |= BSS_CHANGED_UNSOL_BCAST_PROBE_RESP;
return 0;
}
@@ -2413,6 +2414,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
memcpy(&sta->deflink.pub->supp_rates,
&nmi_sta->deflink.pub->supp_rates,
sizeof(sta->deflink.pub->supp_rates));
+
+ sta->deflink.pub->agg = nmi_sta->deflink.pub->agg;
+ __ieee80211_sta_recalc_aggregates(sta, 0);
}

/* set the STA state after all sta info from usermode has been set */
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 95b779c4d627..a3a3b9f24721 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -588,6 +588,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
WARN_ON(!list_empty(&sdata->u.ap.vlans));
} else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
/* remove all packets in parent bc_buf pointing to this dev */
+ __skb_queue_head_init(&freeq);
ps = &sdata->bss->ps;

spin_lock_irqsave(&ps->bc_buf.lock, flags);
@@ -595,10 +596,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
if (skb->dev == sdata->dev) {
__skb_unlink(skb, &ps->bc_buf);
local->total_ps_buffered--;
- ieee80211_free_txskb(&local->hw, skb);
+ __skb_queue_tail(&freeq, skb);
}
}
spin_unlock_irqrestore(&ps->bc_buf.lock, flags);
+
+ skb_queue_walk_safe(&freeq, skb, tmp) {
+ __skb_unlink(skb, &freeq);
+ ieee80211_free_txskb(&local->hw, skb);
+ }
}

if (going_down)
diff --git a/net/mac80211/link.c b/net/mac80211/link.c
index 93e290dd783f..7e3dae12526b 100644
--- a/net/mac80211/link.c
+++ b/net/mac80211/link.c
@@ -394,6 +394,10 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata,
memcpy(sdata->link, old_data, sizeof(old_data));
memcpy(sdata->vif.link_conf, old, sizeof(old));
ieee80211_set_vif_links_bitmaps(sdata, old_links, dormant_links);
+ for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) {
+ ieee80211_link_debugfs_remove(&links[link_id]->data);
+ ieee80211_link_stop(&links[link_id]->data);
+ }
/* and free (only) the newly allocated links */
memset(to_free, 0, sizeof(links));
goto free;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index b98ddfa3003e..ddd3479f0b40 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -6659,7 +6659,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
- u16 capab_info, status_code, aid;
+ u16 capab_info, status_code, aid = 0;
struct ieee80211_elems_parse_params parse_params = {
.bss = NULL,
.link_id = -1,
@@ -6737,8 +6737,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,

if (elems->aid_resp)
aid = le16_to_cpu(elems->aid_resp->aid);
- else
+ else if (!assoc_data->s1g)
aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+ else if (status_code == WLAN_STATUS_SUCCESS)
+ goto abandon_assoc;

/*
* The 5 MSB of the AID field are reserved for a non-S1G STA. For
diff --git a/net/mac80211/nan.c b/net/mac80211/nan.c
index 4e262b624521..d089b18321e4 100644
--- a/net/mac80211/nan.c
+++ b/net/mac80211/nan.c
@@ -253,9 +253,12 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_nan_channel *sched_idx_to_chan[IEEE80211_NAN_MAX_CHANNELS] = {};
struct ieee80211_nan_sched_cfg *sched_cfg = &sdata->vif.cfg.nan_sched;
- struct ieee80211_nan_sched_cfg backup_sched;
+ struct ieee80211_nan_sched_cfg *backup_sched __free(kfree) = kmalloc_obj(*backup_sched);
int ret;

+ if (!backup_sched)
+ return -ENOMEM;
+
if (sched->n_channels > IEEE80211_NAN_MAX_CHANNELS)
return -EOPNOTSUPP;

@@ -275,13 +278,13 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata,

bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS);

- memcpy(backup_sched.schedule, sched_cfg->schedule,
- sizeof(backup_sched.schedule));
- memcpy(backup_sched.channels, sched_cfg->channels,
- sizeof(backup_sched.channels));
- memcpy(backup_sched.avail_blob, sched_cfg->avail_blob,
- sizeof(backup_sched.avail_blob));
- backup_sched.avail_blob_len = sched_cfg->avail_blob_len;
+ memcpy(backup_sched->schedule, sched_cfg->schedule,
+ sizeof(backup_sched->schedule));
+ memcpy(backup_sched->channels, sched_cfg->channels,
+ sizeof(backup_sched->channels));
+ memcpy(backup_sched->avail_blob, sched_cfg->avail_blob,
+ sizeof(backup_sched->avail_blob));
+ backup_sched->avail_blob_len = sched_cfg->avail_blob_len;

memcpy(sched_cfg->avail_blob, sched->nan_avail_blob,
sched->nan_avail_blob_len);
@@ -377,17 +380,17 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata,
if (!chan_def->chan)
continue;

- if (!cfg80211_chandef_identical(&backup_sched.channels[i].chanreq.oper,
+ if (!cfg80211_chandef_identical(&backup_sched->channels[i].chanreq.oper,
chan_def))
ieee80211_nan_remove_channel(sdata,
&sched_cfg->channels[i]);
}

/* Re-add all backed up channels */
- for (int i = 0; i < ARRAY_SIZE(backup_sched.channels); i++) {
+ for (int i = 0; i < ARRAY_SIZE(backup_sched->channels); i++) {
struct ieee80211_nan_channel *chan = &sched_cfg->channels[i];

- *chan = backup_sched.channels[i];
+ *chan = backup_sched->channels[i];

/*
* For deferred update, no channels were removed and the channel
@@ -418,11 +421,11 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata,
}
}

- memcpy(sched_cfg->schedule, backup_sched.schedule,
- sizeof(backup_sched.schedule));
- memcpy(sched_cfg->avail_blob, backup_sched.avail_blob,
- sizeof(backup_sched.avail_blob));
- sched_cfg->avail_blob_len = backup_sched.avail_blob_len;
+ memcpy(sched_cfg->schedule, backup_sched->schedule,
+ sizeof(backup_sched->schedule));
+ memcpy(sched_cfg->avail_blob, backup_sched->avail_blob,
+ sizeof(backup_sched->avail_blob));
+ sched_cfg->avail_blob_len = backup_sched->avail_blob_len;
sched_cfg->deferred = false;
bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS);

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index fe583636d326..7372025831c5 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1717,6 +1717,8 @@ static void sta_ps_start(struct sta_info *sta)
else
clear_bit(tid, &sta->txq_buffered_tids);
}
+
+ sta_info_recalc_tim(sta);
}

static void sta_ps_end(struct sta_info *sta)
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 4c31ef8817ce..6d58571a364d 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -355,6 +355,15 @@ static void sta_info_free_link(struct link_sta_info *link_sta)
free_percpu(link_sta->pcpu_rx_stats);
}

+static void sta_link_free_rcu(struct rcu_head *head)
+{
+ struct sta_link_alloc *alloc =
+ container_of(head, struct sta_link_alloc, rcu_head);
+
+ sta_info_free_link(&alloc->info);
+ kfree(alloc);
+}
+
static void sta_accumulate_removed_link_stats(struct sta_info *sta, int link_id)
{
struct link_sta_info *link_sta = wiphy_dereference(sta->local->hw.wiphy,
@@ -439,10 +448,8 @@ static void sta_remove_link(struct sta_info *sta, unsigned int link_id,

RCU_INIT_POINTER(sta->link[link_id], NULL);
RCU_INIT_POINTER(sta->sta.link[link_id], NULL);
- if (alloc) {
- sta_info_free_link(&alloc->info);
- kfree_rcu(alloc, rcu_head);
- }
+ if (alloc)
+ call_rcu(&alloc->rcu_head, sta_link_free_rcu);

ieee80211_sta_recalc_aggregates(&sta->sta);
}
diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c
index 5e7cc11fab3a..85452ef9a58c 100644
--- a/net/mac802154/llsec.c
+++ b/net/mac802154/llsec.c
@@ -891,6 +891,11 @@ llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec,
data = skb_mac_header(skb) + skb->mac_len;
datalen = skb_tail_pointer(skb) - data;

+ if (datalen < authlen) {
+ kfree_sensitive(req);
+ return -EBADMSG;
+ }
+
sg_init_one(&sg, skb_mac_header(skb), assoclen + datalen);

if (!(hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC)) {
diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c
index 300d4584533e..65089826ff59 100644
--- a/net/mac802154/scan.c
+++ b/net/mac802154/scan.c
@@ -179,6 +179,7 @@ void mac802154_scan_worker(struct work_struct *work)
enum nl802154_scan_types scan_req_type;
struct ieee802154_sub_if_data *sdata;
unsigned int scan_duration = 0;
+ netdevice_tracker dev_tracker;
struct wpan_phy *wpan_phy;
u8 scan_req_duration;
u8 page, channel;
@@ -209,6 +210,14 @@ void mac802154_scan_worker(struct work_struct *work)
return;
}

+ /*
+ * sdata->dev is dereferenced below after rcu_read_unlock() and outside
+ * the rtnl, and a concurrent DEL_INTERFACE / PHY teardown can free it
+ * asynchronously from netdev_run_todo(). Pin it with a reference taken
+ * while the RCU read lock is still held, and drop it at every exit.
+ */
+ netdev_hold(sdata->dev, &dev_tracker, GFP_ATOMIC);
+
wpan_phy = scan_req->wpan_phy;
scan_req_type = scan_req->type;
scan_req_duration = scan_req->duration;
@@ -262,12 +271,14 @@ void mac802154_scan_worker(struct work_struct *work)
"Scan page %u channel %u for %ums\n",
page, channel, jiffies_to_msecs(scan_duration));
queue_delayed_work(local->mac_wq, &local->scan_work, scan_duration);
+ netdev_put(sdata->dev, &dev_tracker);
return;

end_scan:
rtnl_lock();
mac802154_scan_cleanup_locked(local, sdata, false);
rtnl_unlock();
+ netdev_put(sdata->dev, &dev_tracker);
}

int mac802154_trigger_scan_locked(struct ieee802154_sub_if_data *sdata,
diff --git a/net/mctp/device.c b/net/mctp/device.c
index 2c84df674669..822120e860c8 100644
--- a/net/mctp/device.c
+++ b/net/mctp/device.c
@@ -536,7 +536,9 @@ int __init mctp_device_init(void)
{
int err;

- register_netdevice_notifier(&mctp_dev_nb);
+ err = register_netdevice_notifier(&mctp_dev_nb);
+ if (err)
+ return err;

err = rtnl_af_register(&mctp_af_ops);
if (err)
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index 26340a7306b5..35069183f59c 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -922,8 +922,7 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg,
struct nlattr *nla_via, *nla_newdst;
int remaining = cfg->rc_mp_len;
int err = 0;
-
- rt->rt_nhn = 0;
+ u8 nhs = 0;

change_nexthops(rt) {
int attrlen;
@@ -959,12 +958,15 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg,
rt->rt_nhn_alive--;

rtnh = rtnh_next(rtnh, &remaining);
- rt->rt_nhn++;
+ nhs++;
} endfor_nexthops(rt);

+ rt->rt_nhn = nhs;
+
return 0;

errout:
+ rt->rt_nhn = nhs;
return err;
}

@@ -2186,6 +2188,9 @@ static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
int ifindex;

if (i == RTA_OIF) {
+ if (!tb[i])
+ continue;
+
ifindex = nla_get_u32(tb[i]);
filter->dev = dev_get_by_index_rcu(net, ifindex);
if (!filter->dev)
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index b3ea7854818f..336bcc419066 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -157,17 +157,11 @@ static void mptcp_parse_option(const struct sk_buff *skb,
ptr++;

flags = (*ptr++) & MPTCP_DSS_FLAG_MASK;
- mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0;
mp_opt->dsn64 = (flags & MPTCP_DSS_DSN64) != 0;
mp_opt->use_map = (flags & MPTCP_DSS_HAS_MAP) != 0;
mp_opt->ack64 = (flags & MPTCP_DSS_ACK64) != 0;
mp_opt->use_ack = (flags & MPTCP_DSS_HAS_ACK);

- pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n",
- mp_opt->data_fin, mp_opt->dsn64,
- mp_opt->use_map, mp_opt->ack64,
- mp_opt->use_ack);
-
expected_opsize = TCPOLEN_MPTCP_DSS_BASE;

if (mp_opt->use_ack) {
@@ -178,12 +172,18 @@ static void mptcp_parse_option(const struct sk_buff *skb,
}

if (mp_opt->use_map) {
+ mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0;
if (mp_opt->dsn64)
expected_opsize += TCPOLEN_MPTCP_DSS_MAP64;
else
expected_opsize += TCPOLEN_MPTCP_DSS_MAP32;
}

+ pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n",
+ mp_opt->data_fin, mp_opt->dsn64,
+ mp_opt->use_map, mp_opt->ack64,
+ mp_opt->use_ack);
+
/* Always parse any csum presence combination, we will enforce
* RFC 8684 Section 3.3.0 checks later in subflow_data_ready
*/
@@ -573,10 +573,7 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb,
bool ret = false;

/* Zero `use_ack` and `use_map` flags with one shot. */
- BUILD_BUG_ON(sizeof_field(struct mptcp_ext, flags) != sizeof(u16));
- BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct mptcp_ext, flags),
- sizeof(u16)));
- *(u16 *)&opts->ext_copy.flags = 0;
+ memset(&opts->ext_copy.flags, 0, sizeof(opts->ext_copy.flags));
opts->csum_reqd = READ_ONCE(msk->csum_enabled);
mpext = skb ? mptcp_get_ext(skb) : NULL;

diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c
index 0d3a95e676f1..db5693262c8c 100644
--- a/net/mptcp/pm_userspace.c
+++ b/net/mptcp/pm_userspace.c
@@ -132,12 +132,15 @@ int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk,
__be16 msk_sport = ((struct inet_sock *)
inet_sk((struct sock *)msk))->inet_sport;
struct mptcp_pm_addr_entry *entry;
+ int id;

spin_lock_bh(&msk->pm.lock);
entry = mptcp_userspace_pm_lookup_addr(msk, &skc->addr);
+ id = entry ? entry->addr.id : -1;
spin_unlock_bh(&msk->pm.lock);
- if (entry)
- return entry->addr.id;
+
+ if (id != -1)
+ return id;

if (skc->addr.port == msk_sport)
skc->addr.port = 0;
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index cb9515f505aa..ca644ec53eed 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -2545,6 +2545,22 @@ static void __mptcp_subflow_disconnect(struct sock *ssk,
}
}

+static void mptcp_cleanup_ssk_backlog(struct sock *sk, struct sock *ssk)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct sk_buff *skb;
+
+ mptcp_data_lock(sk);
+ list_for_each_entry(skb, &msk->backlog_list, list) {
+ if (skb->sk != ssk)
+ continue;
+
+ atomic_sub(skb->truesize, &skb->sk->sk_rmem_alloc);
+ skb->sk = NULL;
+ }
+ mptcp_data_unlock(sk);
+}
+
/* subflow sockets can be either outgoing (connect) or incoming
* (accept).
*
@@ -2568,6 +2584,9 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
lock_sock_nested(ssk, SINGLE_DEPTH_NESTING);
subflow->closing = 1;

+ if (flags & MPTCP_CF_PUSH)
+ mptcp_cleanup_ssk_backlog(sk, ssk);
+
/* Borrow the fwd allocated page left-over; fwd memory for the subflow
* could be negative at this point, but will be reach zero soon - when
* the data allocated using such fragment will be freed.
@@ -2659,9 +2678,6 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
void mptcp_close_ssk(struct sock *sk, struct sock *ssk,
struct mptcp_subflow_context *subflow)
{
- struct mptcp_sock *msk = mptcp_sk(sk);
- struct sk_buff *skb;
-
/* The first subflow can already be closed or disconnected */
if (subflow->close_event_done || READ_ONCE(subflow->local_id) < 0)
return;
@@ -2671,17 +2687,6 @@ void mptcp_close_ssk(struct sock *sk, struct sock *ssk,
if (sk->sk_state == TCP_ESTABLISHED)
mptcp_event(MPTCP_EVENT_SUB_CLOSED, mptcp_sk(sk), ssk, GFP_KERNEL);

- /* Remove any reference from the backlog to this ssk; backlog skbs consume
- * space in the msk receive queue, no need to touch sk->sk_rmem_alloc
- */
- list_for_each_entry(skb, &msk->backlog_list, list) {
- if (skb->sk != ssk)
- continue;
-
- atomic_sub(skb->truesize, &skb->sk->sk_rmem_alloc);
- skb->sk = NULL;
- }
-
/* subflow aborted before reaching the fully_established status
* attempt the creation of the next subflow
*/
@@ -3907,6 +3912,7 @@ bool mptcp_finish_join(struct sock *ssk)
mptcp_data_unlock(parent);

if (!ret) {
+ mptcp_pm_close_subflow(msk);
err_prohibited:
subflow->reset_reason = MPTCP_RST_EPROHIBIT;
return false;
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 140388a18ae0..513fca6a8e8a 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -837,12 +837,8 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
u16 mru = OVS_CB(skb)->mru;
u32 cutlen = OVS_CB(skb)->cutlen;

- if (unlikely(cutlen > 0)) {
- if (skb->len - cutlen > ovs_mac_header_len(key))
- pskb_trim(skb, skb->len - cutlen);
- else
- pskb_trim(skb, ovs_mac_header_len(key));
- }
+ if (unlikely(cutlen < skb->len))
+ pskb_trim(skb, max(cutlen, ovs_mac_header_len(key)));

if (likely(!mru ||
(skb->len <= mru + vport->dev->hard_header_len))) {
@@ -1234,7 +1230,7 @@ static void execute_psample(struct datapath *dp, struct sk_buff *skb,

psample_group.net = ovs_dp_get_net(dp);
md.in_ifindex = OVS_CB(skb)->input_vport->dev->ifindex;
- md.trunc_size = skb->len - OVS_CB(skb)->cutlen;
+ md.trunc_size = min(skb->len, OVS_CB(skb)->cutlen);
md.rate_as_probability = 1;

rate = OVS_CB(skb)->probability ? OVS_CB(skb)->probability : U32_MAX;
@@ -1284,22 +1280,21 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
clone = skb_clone(skb, GFP_ATOMIC);
if (clone)
do_output(dp, clone, port, key);
- OVS_CB(skb)->cutlen = 0;
+ OVS_CB(skb)->cutlen = U32_MAX;
break;
}

case OVS_ACTION_ATTR_TRUNC: {
struct ovs_action_trunc *trunc = nla_data(a);

- if (skb->len > trunc->max_len)
- OVS_CB(skb)->cutlen = skb->len - trunc->max_len;
+ OVS_CB(skb)->cutlen = trunc->max_len;
break;
}

case OVS_ACTION_ATTR_USERSPACE:
output_userspace(dp, skb, key, a, attr,
len, OVS_CB(skb)->cutlen);
- OVS_CB(skb)->cutlen = 0;
+ OVS_CB(skb)->cutlen = U32_MAX;
if (nla_is_last(a, rem)) {
consume_skb(skb);
return 0;
@@ -1453,7 +1448,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,

case OVS_ACTION_ATTR_PSAMPLE:
execute_psample(dp, skb, a);
- OVS_CB(skb)->cutlen = 0;
+ OVS_CB(skb)->cutlen = U32_MAX;
if (nla_is_last(a, rem)) {
consume_skb(skb);
return 0;
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index f0164817d9b7..eaf332b156d7 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -276,7 +276,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
upcall.portid = ovs_vport_find_upcall_portid(p, skb);

upcall.mru = OVS_CB(skb)->mru;
- error = ovs_dp_upcall(dp, skb, key, &upcall, 0);
+ error = ovs_dp_upcall(dp, skb, key, &upcall, U32_MAX);
switch (error) {
case 0:
case -EAGAIN:
@@ -457,7 +457,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
struct sk_buff *nskb = NULL;
struct sk_buff *user_skb = NULL; /* to be queued to userspace */
struct nlattr *nla;
- size_t len;
+ size_t msg_size;
+ size_t skb_len;
unsigned int hlen;
int err, dp_ifindex;
u64 hash;
@@ -478,7 +479,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
skb = nskb;
}

- if (nla_attr_size(skb->len) > USHRT_MAX) {
+ skb_len = min(skb->len, cutlen);
+ if (nla_attr_size(skb_len) > USHRT_MAX) {
err = -EFBIG;
goto out;
}
@@ -493,13 +495,13 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
* padding logic. Only perform zerocopy if padding is not required.
*/
if (dp->user_features & OVS_DP_F_UNALIGNED)
- hlen = skb_zerocopy_headlen(skb);
+ hlen = min(skb_zerocopy_headlen(skb), cutlen);
else
- hlen = skb->len;
+ hlen = skb_len;

- len = upcall_msg_size(upcall_info, hlen - cutlen,
- OVS_CB(skb)->acts_origlen);
- user_skb = genlmsg_new(len, GFP_ATOMIC);
+ msg_size = upcall_msg_size(upcall_info, hlen,
+ OVS_CB(skb)->acts_origlen);
+ user_skb = genlmsg_new(msg_size, GFP_ATOMIC);
if (!user_skb) {
err = -ENOMEM;
goto out;
@@ -560,7 +562,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
}

/* Add OVS_PACKET_ATTR_LEN when packet is truncated */
- if (cutlen > 0 &&
+ if (skb_len < skb->len &&
nla_put_u32(user_skb, OVS_PACKET_ATTR_LEN, skb->len)) {
err = -ENOBUFS;
goto out;
@@ -585,9 +587,9 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
err = -ENOBUFS;
goto out;
}
- nla->nla_len = nla_attr_size(skb->len - cutlen);
+ nla->nla_len = nla_attr_size(skb_len);

- err = skb_zerocopy(user_skb, skb, skb->len - cutlen, hlen);
+ err = skb_zerocopy(user_skb, skb, skb_len, hlen);
if (err)
goto out;

@@ -644,6 +646,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
packet->ignore_df = 1;
}
OVS_CB(packet)->mru = mru;
+ OVS_CB(packet)->cutlen = U32_MAX;

if (a[OVS_PACKET_ATTR_HASH]) {
hash = nla_get_u64(a[OVS_PACKET_ATTR_HASH]);
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h
index db0c3e69d66c..696640e88fa7 100644
--- a/net/openvswitch/datapath.h
+++ b/net/openvswitch/datapath.h
@@ -118,7 +118,7 @@ struct datapath {
* @mru: The maximum received fragement size; 0 if the packet is not
* fragmented.
* @acts_origlen: The netlink size of the flow actions applied to this skb.
- * @cutlen: The number of bytes from the packet end to be removed.
+ * @cutlen: The number of bytes in the packet to preserve on output.
* @probability: The sampling probability that was applied to this skb; 0 means
* no sampling has occurred; U32_MAX means 100% probability.
* @upcall_pid: Netlink socket PID to use for sending this packet to userspace;
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 56b2e2d1a749..12741485c939 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -502,7 +502,7 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,

OVS_CB(skb)->input_vport = vport;
OVS_CB(skb)->mru = 0;
- OVS_CB(skb)->cutlen = 0;
+ OVS_CB(skb)->cutlen = U32_MAX;
OVS_CB(skb)->probability = 0;
OVS_CB(skb)->upcall_pid = 0;
if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) {
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 8e6f3a734ba0..e75d2932475a 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -4561,7 +4561,11 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,

spin_lock(&po->bind_lock);
WRITE_ONCE(po->num, num);
- if (was_running)
+ /*
+ * NETDEV_UNREGISTER may have invalidated the binding while bind_lock
+ * was dropped above. Do not re-add a fanout hook to a dead device.
+ */
+ if (was_running && READ_ONCE(po->ifindex) != -1)
register_prot_hook(sk);

spin_unlock(&po->bind_lock);
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
index 706927139393..31b29e3ca7bc 100644
--- a/net/phonet/pep.c
+++ b/net/phonet/pep.c
@@ -55,6 +55,8 @@ static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen,
ph = skb_header_pointer(skb, 0, 2, &h);
if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len))
return NULL;
+ /* pskb_may_pull() may have reallocated the head; refetch ph. */
+ ph = skb_header_pointer(skb, 0, 2, &h);
ph->sb_len -= 2;
*ptype = ph->sb_type;
*plen = ph->sb_len;
diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c
index 2288159f5b1b..7482a09adb54 100644
--- a/net/qrtr/af_qrtr.c
+++ b/net/qrtr/af_qrtr.c
@@ -1261,6 +1261,14 @@ static int qrtr_create(struct net *net, struct socket *sock,
if (sock->type != SOCK_DGRAM)
return -EPROTOTYPE;

+ /* QRTR keeps its port and node state in module-global variables that
+ * are not partitioned per network namespace, and the in-kernel name
+ * service only operates in init_net. Confine the family to init_net so
+ * a socket in another namespace cannot reach the global control plane.
+ */
+ if (!net_eq(net, &init_net))
+ return -EAFNOSUPPORT;
+
sk = sk_alloc(net, AF_QIPCRTR, GFP_KERNEL, &qrtr_proto, kern);
if (!sk)
return -ENOMEM;
diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
index b3f9bbcf9ab9..e5b2adb161d9 100644
--- a/net/qrtr/ns.c
+++ b/net/qrtr/ns.c
@@ -76,11 +76,11 @@ struct qrtr_node {
* requirements. If the requirement changes in the future, these values can be
* increased.
*/
-#define QRTR_NS_MAX_NODES 64
+#define QRTR_NS_MAX_NODES 512
#define QRTR_NS_MAX_SERVERS 256
#define QRTR_NS_MAX_LOOKUPS 64

-static u8 node_count;
+static u16 node_count;

static struct qrtr_node *node_get(unsigned int node_id)
{
diff --git a/net/rds/ib.c b/net/rds/ib.c
index 39f87272e071..8f9cf491984f 100644
--- a/net/rds/ib.c
+++ b/net/rds/ib.c
@@ -429,6 +429,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr,
sa = (struct sockaddr *)&sin;
} else {
#if IS_ENABLED(CONFIG_IPV6)
+ if (!ipv6_mod_enabled()) {
+ ret = -EADDRNOTAVAIL;
+ goto out;
+ }
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = *addr;
diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
index 4001de0c4959..9b25486fde81 100644
--- a/net/rds/ib_cm.c
+++ b/net/rds/ib_cm.c
@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id,
dp = event->param.conn.private_data;
if (isv6) {
#if IS_ENABLED(CONFIG_IPV6)
+ if (!ipv6_mod_enabled()) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
dp_cmn = &dp->ricp_v6.dp_cmn;
saddr6 = &dp->ricp_v6.dp_saddr;
daddr6 = &dp->ricp_v6.dp_daddr;
diff --git a/net/rds/recv.c b/net/rds/recv.c
index 4b3f9e4a8bfd..cf3884d87931 100644
--- a/net/rds/recv.c
+++ b/net/rds/recv.c
@@ -399,6 +399,21 @@ void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr,
goto out;
}

+ /*
+ * rds_find_bound() uses a global (netns-agnostic) hash table.
+ * An RDS connection created in netns A can match a socket bound
+ * in the init netns, delivering inc cross-netns with inc->i_conn
+ * pointing into netns A. When cleanup_net() then frees that conn,
+ * any subsequent dereference of inc->i_conn is a use-after-free.
+ * Drop the inc if the receiving socket lives in a different netns.
+ */
+ if (!net_eq(sock_net(rds_rs_to_sk(rs)), rds_conn_net(conn))) {
+ rds_stats_inc(s_recv_drop_no_sock);
+ rds_sock_put(rs);
+ rs = NULL;
+ goto out;
+ }
+
/* Process extension headers */
rds_recv_incoming_exthdrs(inc, rs);

diff --git a/net/rds/tcp.c b/net/rds/tcp.c
index 5830b31a1f37..76f441cbf50e 100644
--- a/net/rds/tcp.c
+++ b/net/rds/tcp.c
@@ -347,9 +347,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
rcu_read_unlock();
}
#if IS_ENABLED(CONFIG_IPV6)
- ret = ipv6_chk_addr(net, addr, dev, 0);
- if (ret)
- return 0;
+ if (ipv6_mod_enabled()) {
+ ret = ipv6_chk_addr(net, addr, dev, 0);
+ if (ret)
+ return 0;
+ }
#endif
return -EADDRNOTAVAIL;
}
@@ -636,13 +638,13 @@ static void __net_exit rds_tcp_exit_net(struct net *net)
{
struct rds_tcp_net *rtn = net_generic(net, rds_tcp_netid);

- rds_tcp_kill_sock(net);
-
if (rtn->rds_tcp_sysctl)
unregister_net_sysctl_table(rtn->rds_tcp_sysctl);

if (net != &init_net)
kfree(rtn->ctl_table);
+
+ rds_tcp_kill_sock(net);
}

static struct pernet_operations rds_tcp_net_ops = {
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index d2b31d15851b..38b0997ce0cb 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -1266,9 +1266,11 @@ int rxrpc_io_thread(void *data);
void rxrpc_post_response(struct rxrpc_connection *conn, struct sk_buff *skb);
static inline void rxrpc_wake_up_io_thread(struct rxrpc_local *local)
{
- if (!local->io_thread)
+ struct task_struct *io_thread = READ_ONCE(local->io_thread);
+
+ if (!io_thread)
return;
- wake_up_process(READ_ONCE(local->io_thread));
+ wake_up_process(io_thread);
}

static inline bool rxrpc_protocol_error(struct sk_buff *skb, enum rxrpc_abort_reason why)
diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c
index 876b30c5709e..b14807761d82 100644
--- a/net/sched/act_tunnel_key.c
+++ b/net/sched/act_tunnel_key.c
@@ -342,14 +342,20 @@ static const struct nla_policy tunnel_key_policy[TCA_TUNNEL_KEY_MAX + 1] = {
[TCA_TUNNEL_KEY_ENC_TTL] = { .type = NLA_U8 },
};

-static void tunnel_key_release_params(struct tcf_tunnel_key_params *p)
+static void tunnel_key_release_params_rcu(struct rcu_head *head)
{
- if (!p)
- return;
+ struct tcf_tunnel_key_params *p = container_of(head, typeof(*p), rcu);
+
if (p->tcft_action == TCA_TUNNEL_KEY_ACT_SET)
dst_release(&p->tcft_enc_metadata->dst);
+ kfree(p);
+}

- kfree_rcu(p, rcu);
+static void tunnel_key_release_params(struct tcf_tunnel_key_params *p)
+{
+ if (!p)
+ return;
+ call_rcu(&p->rcu, tunnel_key_release_params_rcu);
}

static int tunnel_key_init(struct net *net, struct nlattr *nla,
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index ffeea6db8337..fee4524adc98 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -4045,7 +4045,7 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru

fl = rcu_dereference_bh(qe->filter_chain);

- switch (tcf_classify(skb, NULL, fl, &cl_res, false)) {
+ switch (tcf_classify_qdisc(skb, fl, &cl_res, false)) {
case TC_ACT_SHOT:
qdisc_qstats_drop(sch);
__qdisc_drop(skb, to_free);
@@ -4057,10 +4057,6 @@ struct sk_buff *tcf_qevent_handle(struct tcf_qevent *qe, struct Qdisc *sch, stru
__qdisc_drop(skb, to_free);
*ret = __NET_XMIT_STOLEN;
return NULL;
- case TC_ACT_REDIRECT:
- skb_do_redirect(skb);
- *ret = __NET_XMIT_STOLEN;
- return NULL;
case TC_ACT_CONSUMED:
*ret = __NET_XMIT_STOLEN;
return NULL;
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 0dd3efd86393..487cbfdc5655 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -415,12 +415,13 @@ static __u8 __detect_linklayer(struct tc_ratespec *r, __u32 *rtab)
}

static struct qdisc_rate_table *qdisc_rtab_list;
+static DEFINE_SPINLOCK(qdisc_rtab_lock);

struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
struct nlattr *tab,
struct netlink_ext_ack *extack)
{
- struct qdisc_rate_table *rtab;
+ struct qdisc_rate_table *rtab, *new_rtab;

if (tab == NULL || r->rate == 0 ||
r->cell_log == 0 || r->cell_log >= 32 ||
@@ -429,15 +430,20 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
return NULL;
}

+ new_rtab = kmalloc_obj(*new_rtab);
+
+ spin_lock(&qdisc_rtab_lock);
for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) {
if (!memcmp(&rtab->rate, r, sizeof(struct tc_ratespec)) &&
!memcmp(&rtab->data, nla_data(tab), TC_RTAB_SIZE)) {
rtab->refcnt++;
+ spin_unlock(&qdisc_rtab_lock);
+ kfree(new_rtab);
return rtab;
}
}

- rtab = kmalloc_obj(*rtab);
+ rtab = new_rtab;
if (rtab) {
rtab->rate = *r;
rtab->refcnt = 1;
@@ -449,6 +455,7 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
} else {
NL_SET_ERR_MSG(extack, "Failed to allocate new qdisc rate table");
}
+ spin_unlock(&qdisc_rtab_lock);
return rtab;
}
EXPORT_SYMBOL(qdisc_get_rtab);
@@ -457,18 +464,25 @@ void qdisc_put_rtab(struct qdisc_rate_table *tab)
{
struct qdisc_rate_table *rtab, **rtabp;

- if (!tab || --tab->refcnt)
+ if (!tab)
return;

+ spin_lock(&qdisc_rtab_lock);
+ if (--tab->refcnt) {
+ spin_unlock(&qdisc_rtab_lock);
+ return;
+ }
+
for (rtabp = &qdisc_rtab_list;
(rtab = *rtabp) != NULL;
rtabp = &rtab->next) {
if (rtab == tab) {
*rtabp = rtab->next;
- kfree(rtab);
- return;
+ break;
}
}
+ spin_unlock(&qdisc_rtab_lock);
+ kfree(tab);
}
EXPORT_SYMBOL(qdisc_put_rtab);

diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index 07ea175657bb..b967b7153ad3 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1727,7 +1727,7 @@ static u32 cake_classify(struct Qdisc *sch, struct cake_tin_data **t,
goto hash;

*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
- result = tcf_classify(skb, NULL, filter, &res, false);
+ result = tcf_classify_qdisc(skb, filter, &res, false);

if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c
index 2eedd3a4322a..c7d489feec61 100644
--- a/net/sched/sch_drr.c
+++ b/net/sched/sch_drr.c
@@ -314,7 +314,7 @@ static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch,

*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
fl = rcu_dereference_bh(q->filter_list);
- result = tcf_classify(skb, NULL, fl, &res, false);
+ result = tcf_classify_qdisc(skb, fl, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c
index 27088760eff4..4f678d4ff10e 100644
--- a/net/sched/sch_dualpi2.c
+++ b/net/sched/sch_dualpi2.c
@@ -364,7 +364,7 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q,
if (!fl)
return NET_XMIT_SUCCESS;

- result = tcf_classify(skb, NULL, fl, &res, false);
+ result = tcf_classify_qdisc(skb, fl, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sched/sch_ets.c b/net/sched/sch_ets.c
index c817e0a6c146..dc72a7a44813 100644
--- a/net/sched/sch_ets.c
+++ b/net/sched/sch_ets.c
@@ -393,7 +393,7 @@ static struct ets_class *ets_classify(struct sk_buff *skb, struct Qdisc *sch,
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
if (TC_H_MAJ(skb->priority) != sch->handle) {
fl = rcu_dereference_bh(q->filter_list);
- err = tcf_classify(skb, NULL, fl, &res, false);
+ err = tcf_classify_qdisc(skb, fl, &res, false);
#ifdef CONFIG_NET_CLS_ACT
switch (err) {
case TC_ACT_STOLEN:
diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index 09ab84580160..ac4eeb5fe51c 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -91,7 +91,7 @@ static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch,
return fq_codel_hash(q, skb) + 1;

*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
- result = tcf_classify(skb, NULL, filter, &res, false);
+ result = tcf_classify_qdisc(skb, filter, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c
index 0a4eca4ab086..1815f93abb13 100644
--- a/net/sched/sch_fq_pie.c
+++ b/net/sched/sch_fq_pie.c
@@ -96,7 +96,7 @@ static unsigned int fq_pie_classify(struct sk_buff *skb, struct Qdisc *sch,
return fq_pie_hash(q, skb) + 1;

*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
- result = tcf_classify(skb, NULL, filter, &res, false);
+ result = tcf_classify_qdisc(skb, filter, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
index 50112c8a71e0..33801faf1620 100644
--- a/net/sched/sch_hfsc.c
+++ b/net/sched/sch_hfsc.c
@@ -1143,7 +1143,7 @@ hfsc_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
head = &q->root;
tcf = rcu_dereference_bh(q->root.filter_list);
- while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) {
+ while (tcf && (result = tcf_classify_qdisc(skb, tcf, &res, false)) >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
case TC_ACT_QUEUED:
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index ccabafed3fe4..75b9e534a806 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -242,7 +242,7 @@ static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch,
}

*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
- while (tcf && (result = tcf_classify(skb, NULL, tcf, &res, false)) >= 0) {
+ while (tcf && (result = tcf_classify_qdisc(skb, tcf, &res, false)) >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
case TC_ACT_QUEUED:
diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c
index a467dd122369..66df30939aa5 100644
--- a/net/sched/sch_multiq.c
+++ b/net/sched/sch_multiq.c
@@ -36,7 +36,7 @@ multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
int err;

*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
- err = tcf_classify(skb, NULL, fl, &res, false);
+ err = tcf_classify_qdisc(skb, fl, &res, false);
#ifdef CONFIG_NET_CLS_ACT
switch (err) {
case TC_ACT_STOLEN:
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
index fe42ae3d6b69..9da76296177a 100644
--- a/net/sched/sch_prio.c
+++ b/net/sched/sch_prio.c
@@ -39,7 +39,7 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
if (TC_H_MAJ(skb->priority) != sch->handle) {
fl = rcu_dereference_bh(q->filter_list);
- err = tcf_classify(skb, NULL, fl, &res, false);
+ err = tcf_classify_qdisc(skb, fl, &res, false);
#ifdef CONFIG_NET_CLS_ACT
switch (err) {
case TC_ACT_STOLEN:
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
index 195c434aae5f..413fa89673e7 100644
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -709,7 +709,7 @@ static struct qfq_class *qfq_classify(struct sk_buff *skb, struct Qdisc *sch,

*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
fl = rcu_dereference_bh(q->filter_list);
- result = tcf_classify(skb, NULL, fl, &res, false);
+ result = tcf_classify_qdisc(skb, fl, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c
index efd9251c3add..ea1998a21d3b 100644
--- a/net/sched/sch_sfb.c
+++ b/net/sched/sch_sfb.c
@@ -260,7 +260,7 @@ static bool sfb_classify(struct sk_buff *skb, struct tcf_proto *fl,
struct tcf_result res;
int result;

- result = tcf_classify(skb, NULL, fl, &res, false);
+ result = tcf_classify_qdisc(skb, fl, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index f9807ee2cf6c..04c8c2cae26b 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -171,7 +171,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch,
return sfq_hash(q, skb) + 1;

*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
- result = tcf_classify(skb, NULL, fl, &res, false);
+ result = tcf_classify_qdisc(skb, fl, &res, false);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
diff --git a/net/sctp/auth.c b/net/sctp/auth.c
index be9782760f50..c901d373af80 100644
--- a/net/sctp/auth.c
+++ b/net/sctp/auth.c
@@ -672,7 +672,7 @@ int sctp_auth_ep_add_chunkid(struct sctp_endpoint *ep, __u8 chunk_id)
/* Check if we can add this chunk to the array */
param_len = ntohs(p->param_hdr.length);
nchunks = param_len - sizeof(struct sctp_paramhdr);
- if (nchunks == SCTP_NUM_CHUNK_TYPES)
+ if (nchunks == SCTP_AUTH_MAX_CHUNKS)
return -EINVAL;

p->chunks[nchunks] = chunk_id;
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index e95061b672ef..657170bc94cb 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1377,10 +1377,6 @@ static int __net_init sctp_defaults_init(struct net *net)
net->sctp.l3mdev_accept = 1;
#endif

- status = sctp_sysctl_net_register(net);
- if (status)
- goto err_sysctl_register;
-
/* Allocate and initialise sctp mibs. */
status = init_sctp_mibs(net);
if (status)
@@ -1414,8 +1410,6 @@ static int __net_init sctp_defaults_init(struct net *net)
cleanup_sctp_mibs(net);
#endif
err_init_mibs:
- sctp_sysctl_net_unregister(net);
-err_sysctl_register:
return status;
}

@@ -1430,7 +1424,6 @@ static void __net_exit sctp_defaults_exit(struct net *net)
net->sctp.proc_net_sctp = NULL;
#endif
cleanup_sctp_mibs(net);
- sctp_sysctl_net_unregister(net);
}

static struct pernet_operations sctp_defaults_ops = {
@@ -1444,16 +1437,28 @@ static int __net_init sctp_ctrlsock_init(struct net *net)

/* Initialize the control inode/socket for handling OOTB packets. */
status = sctp_ctl_sock_init(net);
- if (status)
+ if (status) {
pr_err("Failed to initialize the SCTP control sock\n");
+ return status;
+ }
+
+ status = sctp_sysctl_net_register(net);
+ if (status) {
+ inet_ctl_sock_destroy(net->sctp.ctl_sock);
+ net->sctp.ctl_sock = NULL;
+ }

return status;
}

static void __net_exit sctp_ctrlsock_exit(struct net *net)
{
+ sctp_sysctl_net_unregister(net);
+ sctp_udp_sock_stop(net);
+
/* Free the control endpoint. */
inet_ctl_sock_destroy(net->sctp.ctl_sock);
+ net->sctp.ctl_sock = NULL;
}

static struct pernet_operations sctp_ctrlsock_ops = {
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 8adac9e0cd66..c02809264075 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3153,6 +3153,12 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
if (!peer)
return SCTP_ERROR_DNS_FAILED;

+ /* Don't free asconf->transport; a later wildcard DEL-IP
+ * parameter reuses it.
+ */
+ if (peer == asconf->transport)
+ return SCTP_ERROR_REQ_REFUSED;
+
sctp_assoc_rm_peer(asoc, peer);
break;
case SCTP_PARAM_SET_PRIMARY:
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 3893b44448b3..708fa07d5fff 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -643,7 +643,7 @@ static bool sctp_auth_chunk_verify(struct net *net, struct sctp_chunk *chunk,
struct sctp_chunk auth;

if (!chunk->auth_chunk)
- return true;
+ return !sctp_auth_recv_cid(chunk->chunk_hdr->type, asoc);

/* SCTP-AUTH: auth_chunk pointer is only set when the cookie-echo
* is supposed to be authenticated and we have to do delayed
diff --git a/net/sctp/stream.c b/net/sctp/stream.c
index 5c2fdedea088..34ffe6c945a4 100644
--- a/net/sctp/stream.c
+++ b/net/sctp/stream.c
@@ -308,7 +308,8 @@ int sctp_send_reset_streams(struct sctp_association *asoc,
goto out;

param_len += str_nums * sizeof(__u16) +
- sizeof(struct sctp_strreset_inreq);
+ (out ? sizeof(struct sctp_strreset_inreq)
+ : sizeof(struct sctp_strreset_outreq));
}

if (param_len > SCTP_MAX_CHUNK_LEN -
@@ -639,6 +640,9 @@ struct sctp_chunk *sctp_process_strreset_inreq(

nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16);
str_p = inreq->list_of_streams;
+ if (nums * sizeof(__u16) + sizeof(struct sctp_strreset_outreq) >
+ SCTP_MAX_CHUNK_LEN - sizeof(struct sctp_reconf_chunk))
+ goto out;
for (i = 0; i < nums; i++) {
if (ntohs(str_p[i]) >= stream->outcnt) {
result = SCTP_STRRESET_ERR_WRONG_SSN;
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 15e7db9a3ab2..fca840484ebf 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -615,11 +615,16 @@ int sctp_sysctl_net_register(struct net *net)

void sctp_sysctl_net_unregister(struct net *net)
{
+ struct ctl_table_header *header = net->sctp.sysctl_header;
const struct ctl_table *table;

- table = net->sctp.sysctl_header->ctl_table_arg;
- unregister_net_sysctl_table(net->sctp.sysctl_header);
+ if (!header)
+ return;
+
+ table = header->ctl_table_arg;
+ unregister_net_sysctl_table(header);
kfree(table);
+ net->sctp.sysctl_header = NULL;
}

static struct ctl_table_header *sctp_sysctl_header;
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index eb747493db82..50e5e7f5b762 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -742,7 +742,7 @@ static int svc_udp_sendto(struct svc_rqst *rqstp)
.msg_flags = MSG_SPLICE_PAGES,
.msg_controllen = sizeof(buffer),
};
- unsigned int count;
+ int count;
int err;

svc_udp_release_ctxt(xprt, rqstp->rq_xprt_ctxt);
@@ -756,6 +756,10 @@ static int svc_udp_sendto(struct svc_rqst *rqstp)
goto out_notconn;

count = xdr_buf_to_bvec(svsk->sk_bvec, SUNRPC_MAX_UDP_SENDPAGES, xdr);
+ if (count < 0) {
+ err = count;
+ goto out_trace;
+ }

iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec,
count, rqstp->rq_res.len);
@@ -767,6 +771,7 @@ static int svc_udp_sendto(struct svc_rqst *rqstp)
err = sock_sendmsg(svsk->sk_sock, &msg);
}

+out_trace:
trace_svcsock_udp_send(xprt, err);

mutex_unlock(&xprt->xpt_mutex);
@@ -1247,7 +1252,7 @@ static int svc_tcp_sendmsg(struct svc_sock *svsk, struct svc_rqst *rqstp,
struct msghdr msg = {
.msg_flags = MSG_SPLICE_PAGES,
};
- unsigned int count;
+ int count;
void *buf;
int ret;

@@ -1263,10 +1268,15 @@ static int svc_tcp_sendmsg(struct svc_sock *svsk, struct svc_rqst *rqstp,

count = xdr_buf_to_bvec(svsk->sk_bvec + 1, rqstp->rq_maxpages,
&rqstp->rq_res);
+ if (count < 0) {
+ ret = count;
+ goto out;
+ }

iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, svsk->sk_bvec,
1 + count, sizeof(marker) + rqstp->rq_res.len);
ret = sock_sendmsg(svsk->sk_sock, &msg);
+out:
page_frag_free(buf);
return ret;
}
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 4c41e83b7ca9..1119b796abd4 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -139,13 +139,14 @@ xdr_free_bvec(struct xdr_buf *buf)
/**
* xdr_buf_to_bvec - Copy components of an xdr_buf into a bio_vec array
* @bvec: bio_vec array to populate
- * @bvec_size: element count of @bio_vec
+ * @bvec_size: element count of @bvec
* @xdr: xdr_buf to be copied
*
- * Returns the number of entries consumed in @bvec.
+ * Returns the number of entries consumed in @bvec on success, or
+ * -ESERVERFAULT when @xdr does not fit within @bvec_size entries.
*/
-unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
- const struct xdr_buf *xdr)
+int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,
+ const struct xdr_buf *xdr)
{
const struct kvec *head = xdr->head;
const struct kvec *tail = xdr->tail;
@@ -187,10 +188,209 @@ unsigned int xdr_buf_to_bvec(struct bio_vec *bvec, unsigned int bvec_size,

bvec_overflow:
pr_warn_once("%s: bio_vec array overflow\n", __func__);
- return count;
+ return -ESERVERFAULT;
}
EXPORT_SYMBOL_GPL(xdr_buf_to_bvec);

+/**
+ * xdr_buf_to_sg - Populate a scatterlist from an xdr_buf range
+ * @buf: xdr_buf to map
+ * @offset: starting byte offset within @buf
+ * @len: number of bytes to cover
+ * @sg: scatterlist array initialized with sg_init_table()
+ * @nsg: number of entries available in @sg
+ *
+ * @sg is traversed with sg_next(), so callers may pass a list
+ * assembled with sg_chain().
+ *
+ * Return: on success, the number of scatterlist entries used; the
+ * last used entry is marked with sg_mark_end(). On failure, a
+ * negative errno.
+ */
+int xdr_buf_to_sg(const struct xdr_buf *buf, unsigned int offset,
+ unsigned int len, struct scatterlist *sg, unsigned int nsg)
+{
+ unsigned int page_len, thislen, page_offset;
+ struct scatterlist *cur = sg, *prev = NULL;
+ int nents = 0;
+ int i;
+
+ if (len == 0)
+ return 0;
+
+ if (offset >= buf->head[0].iov_len) {
+ offset -= buf->head[0].iov_len;
+ } else {
+ thislen = min_t(unsigned int,
+ buf->head[0].iov_len - offset, len);
+ if (nents >= nsg)
+ return -ENOSPC;
+ sg_set_buf(cur, buf->head[0].iov_base + offset,
+ thislen);
+ prev = cur;
+ cur = sg_next(cur);
+ nents++;
+ len -= thislen;
+ offset = 0;
+ }
+ if (len == 0)
+ goto done;
+
+ if (offset >= buf->page_len) {
+ offset -= buf->page_len;
+ } else {
+ page_len = min(buf->page_len - offset, len);
+ len -= page_len;
+ page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1);
+ i = (offset + buf->page_base) >> PAGE_SHIFT;
+ thislen = PAGE_SIZE - page_offset;
+ do {
+ if (thislen > page_len)
+ thislen = page_len;
+ if (nents >= nsg)
+ return -ENOSPC;
+ sg_set_page(cur, buf->pages[i],
+ thislen, page_offset);
+ prev = cur;
+ cur = sg_next(cur);
+ nents++;
+ page_len -= thislen;
+ i++;
+ page_offset = 0;
+ thislen = PAGE_SIZE;
+ } while (page_len != 0);
+ offset = 0;
+ }
+ if (len == 0)
+ goto done;
+
+ if (offset < buf->tail[0].iov_len) {
+ thislen = min_t(unsigned int,
+ buf->tail[0].iov_len - offset, len);
+ if (nents >= nsg)
+ return -ENOSPC;
+ sg_set_buf(cur, buf->tail[0].iov_base + offset,
+ thislen);
+ prev = cur;
+ nents++;
+ len -= thislen;
+ }
+ if (len != 0)
+ return -EINVAL;
+
+done:
+ if (prev)
+ sg_mark_end(prev);
+ return nents;
+}
+EXPORT_SYMBOL_GPL(xdr_buf_to_sg);
+
+/*
+ * Count the scatterlist entries needed to cover [offset, offset + len)
+ * within @buf. Mirrors the walk in xdr_buf_to_sg() so the caller can
+ * size an allocation that matches the requested sub-range rather than
+ * the full xdr_buf.
+ */
+static unsigned int xdr_buf_sg_nents(const struct xdr_buf *buf,
+ unsigned int offset, unsigned int len)
+{
+ unsigned int nsg = 0, thislen, page_offset;
+
+ if (len == 0)
+ return 0;
+
+ if (offset < buf->head[0].iov_len) {
+ thislen = min_t(unsigned int,
+ buf->head[0].iov_len - offset, len);
+ nsg++;
+ len -= thislen;
+ offset = 0;
+ } else {
+ offset -= buf->head[0].iov_len;
+ }
+ if (len == 0)
+ return nsg;
+
+ if (offset < buf->page_len) {
+ thislen = min(buf->page_len - offset, len);
+ page_offset = (offset + buf->page_base) & (PAGE_SIZE - 1);
+ nsg += DIV_ROUND_UP(page_offset + thislen, PAGE_SIZE);
+ len -= thislen;
+ offset = 0;
+ } else {
+ offset -= buf->page_len;
+ }
+ if (len == 0)
+ return nsg;
+
+ if (offset < buf->tail[0].iov_len)
+ nsg++;
+ return nsg;
+}
+
+/**
+ * xdr_buf_to_sg_alloc - Populate a scatterlist for an xdr_buf range
+ * @buf: xdr_buf to map
+ * @offset: starting byte offset within @buf
+ * @len: number of bytes to cover
+ * @sg_head: caller-provided scatterlist array (typically stack-allocated)
+ * @sg_head_nents: number of entries in @sg_head
+ * @sg_overflow: OUT: chained extension, or NULL when @sg_head sufficed
+ * @gfp: memory allocation flags for overflow
+ *
+ * Populates @sg_head directly when the xdr_buf fits. When more
+ * entries are needed, an overflow scatterlist is allocated and
+ * chained from @sg_head so that the result is traversable with
+ * sg_next().
+ *
+ * Return: on success, the number of populated scatterlist entries
+ * (counting only data entries, not chain entries). @sg_head is
+ * the head of the resulting list. Caller must kfree @sg_overflow
+ * when done. On failure, a negative errno.
+ */
+int xdr_buf_to_sg_alloc(const struct xdr_buf *buf, unsigned int offset,
+ unsigned int len, struct scatterlist *sg_head,
+ unsigned int sg_head_nents,
+ struct scatterlist **sg_overflow, gfp_t gfp)
+{
+ unsigned int nsg;
+ int ret;
+
+ *sg_overflow = NULL;
+ if (len == 0)
+ return 0;
+
+ nsg = xdr_buf_sg_nents(buf, offset, len);
+ if (nsg == 0)
+ return -EINVAL;
+
+ if (nsg <= sg_head_nents) {
+ sg_init_table(sg_head, nsg);
+ } else {
+ /* +1 replaces the slot sg_chain() consumes as the link. */
+ unsigned int overflow_nents = nsg - sg_head_nents + 1;
+ struct scatterlist *overflow;
+
+ overflow = kmalloc_array(overflow_nents, sizeof(*overflow),
+ gfp);
+ if (!overflow)
+ return -ENOMEM;
+
+ sg_init_table(sg_head, sg_head_nents);
+ sg_init_table(overflow, overflow_nents);
+ sg_chain(sg_head, sg_head_nents, overflow);
+ *sg_overflow = overflow;
+ }
+
+ ret = xdr_buf_to_sg(buf, offset, len, sg_head, nsg);
+ if (ret < 0) {
+ kfree(*sg_overflow);
+ *sg_overflow = NULL;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(xdr_buf_to_sg_alloc);
+
/**
* xdr_inline_pages - Prepare receive buffer for a large reply
* @xdr: xdr_buf into which reply will be placed
diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
index e8b5fc86a01b..81070b78512b 100644
--- a/net/sunrpc/xprtrdma/rpc_rdma.c
+++ b/net/sunrpc/xprtrdma/rpc_rdma.c
@@ -493,6 +493,7 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)

rpcrdma_sendctx_dma_unmap(sc);
sc->sc_req = NULL;
+ req->rl_sendctx = NULL;
rpcrdma_req_put(req);
}

@@ -501,8 +502,11 @@ void rpcrdma_sendctx_unmap(struct rpcrdma_sendctx *sc)
*/
static void rpcrdma_sendctx_cancel(struct rpcrdma_sendctx *sc)
{
+ struct rpcrdma_req *req = sc->sc_req;
+
rpcrdma_sendctx_dma_unmap(sc);
sc->sc_req = NULL;
+ req->rl_sendctx = NULL;
}

/* Prepare an SGE for the RPC-over-RDMA transport header.
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
index 0ac82f26bf6e..cbd20bef367d 100644
--- a/net/sunrpc/xprtrdma/verbs.c
+++ b/net/sunrpc/xprtrdma/verbs.c
@@ -1095,9 +1095,15 @@ static struct rpcrdma_rep *rpcrdma_rep_get_locked(struct rpcrdma_buffer *buf)
* @buf: buffer pool
* @rep: rep to release
*
+ * The rep's transient association with an rpc_rqst, established
+ * by rpcrdma_reply_handler() and torn down here, must not survive
+ * onto rb_free_reps: rpcrdma_post_recvs() pulls reps from the free
+ * list to re-post them, and a non-NULL rr_rqst on a free-listed rep
+ * would imply the rep is still referenced by a req.
*/
void rpcrdma_rep_put(struct rpcrdma_buffer *buf, struct rpcrdma_rep *rep)
{
+ rep->rr_rqst = NULL;
llist_add(&rep->rr_node, &buf->rb_free_reps);
}

@@ -1280,9 +1286,11 @@ rpcrdma_mr_get(struct rpcrdma_xprt *r_xprt)
*/
void rpcrdma_reply_put(struct rpcrdma_buffer *buffers, struct rpcrdma_req *req)
{
- if (req->rl_reply) {
- rpcrdma_rep_put(buffers, req->rl_reply);
+ struct rpcrdma_rep *rep = req->rl_reply;
+
+ if (rep) {
req->rl_reply = NULL;
+ rpcrdma_rep_put(buffers, rep);
}
/* I2: rl_reply NULL after the put closes the
* 'rep on rb_free_reps still referenced by req' window.
diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c
index 8336a9664703..1307dd1a9613 100644
--- a/net/tipc/netlink.c
+++ b/net/tipc/netlink.c
@@ -113,12 +113,16 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {
};

/* Properties valid for media, bearer and link */
+static const struct netlink_range_validation tipc_nl_mtu_range = {
+ .max = U16_MAX,
+};
+
const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
[TIPC_NLA_PROP_UNSPEC] = { .type = NLA_UNSPEC },
[TIPC_NLA_PROP_PRIO] = { .type = NLA_U32 },
[TIPC_NLA_PROP_TOL] = { .type = NLA_U32 },
[TIPC_NLA_PROP_WIN] = { .type = NLA_U32 },
- [TIPC_NLA_PROP_MTU] = { .type = NLA_U32 },
+ [TIPC_NLA_PROP_MTU] = NLA_POLICY_FULL_RANGE(NLA_U32, &tipc_nl_mtu_range),
[TIPC_NLA_PROP_BROADCAST] = { .type = NLA_U32 },
[TIPC_NLA_PROP_BROADCAST_RATIO] = { .type = NLA_U32 }
};
diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c
index 2a786c56c8c5..d9a4f94ea2d4 100644
--- a/net/tipc/netlink_compat.c
+++ b/net/tipc/netlink_compat.c
@@ -221,6 +221,10 @@ static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd,
int rem;

len = (*cmd->dumpit)(buf, &cb);
+ if (len < 0) {
+ err = len;
+ goto err_out;
+ }

nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) {
err = nlmsg_parse_deprecated(nlmsg, GENL_HDRLEN,
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 490f30899b5a..d86f1cfdc364 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -501,6 +501,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
tipc_set_sk_state(sk, TIPC_OPEN);
if (tipc_sk_insert(tsk)) {
sk_free(sk);
+ sock->sk = NULL;
pr_warn("Socket create failed; port number exhausted\n");
return -EINVAL;
}
@@ -1934,7 +1935,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m,
if (likely(!err)) {
int offset = skb_cb->bytes_read;

- copy = min_t(int, dlen - offset, buflen);
+ copy = min_t(size_t, dlen - offset, buflen);
rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy);
if (unlikely(rc))
goto exit;
@@ -2066,7 +2067,7 @@ static int tipc_recvstream(struct socket *sock, struct msghdr *m,
/* Copy data if msg ok, otherwise return error/partial data */
if (likely(!err)) {
offset = skb_cb->bytes_read;
- copy = min_t(int, dlen - offset, buflen - copied);
+ copy = min_t(size_t, dlen - offset, buflen - copied);
rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy);
if (unlikely(rc))
break;
diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c
index 62ae7f5b5840..230645cc01c9 100644
--- a/net/tipc/udp_media.c
+++ b/net/tipc/udp_media.c
@@ -94,6 +94,8 @@ struct udp_replicast {
* @ifindex: local address scope
* @work: used to schedule deferred work on a bearer
* @rcast: associated udp_replicast container
+ * @rcast_lock: serialize updates to @rcast.list against concurrent updaters
+ * @disabled: bearer is being torn down; reject further @rcast.list additions
*/
struct udp_bearer {
struct tipc_bearer __rcu *bearer;
@@ -101,6 +103,8 @@ struct udp_bearer {
u32 ifindex;
struct work_struct work;
struct udp_replicast rcast;
+ spinlock_t rcast_lock;
+ bool disabled;
};

static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr)
@@ -278,26 +282,6 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
return err;
}

-static bool tipc_udp_is_known_peer(struct tipc_bearer *b,
- struct udp_media_addr *addr)
-{
- struct udp_replicast *rcast, *tmp;
- struct udp_bearer *ub;
-
- ub = rcu_dereference_rtnl(b->media_ptr);
- if (!ub) {
- pr_err_ratelimited("UDP bearer instance not found\n");
- return false;
- }
-
- list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
- if (!memcmp(&rcast->addr, addr, sizeof(struct udp_media_addr)))
- return true;
- }
-
- return false;
-}
-
static int tipc_udp_rcast_add(struct tipc_bearer *b,
struct udp_media_addr *addr)
{
@@ -308,16 +292,34 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b,
if (!ub)
return -ENODEV;

+ spin_lock_bh(&ub->rcast_lock);
+ if (ub->disabled) {
+ spin_unlock_bh(&ub->rcast_lock);
+ return 0;
+ }
+ list_for_each_entry(rcast, &ub->rcast.list, list) {
+ if (!memcmp(&rcast->addr, addr, sizeof(*addr))) {
+ spin_unlock_bh(&ub->rcast_lock);
+ return 0;
+ }
+ }
+
rcast = kmalloc_obj(*rcast, GFP_ATOMIC);
- if (!rcast)
+ if (!rcast) {
+ spin_unlock_bh(&ub->rcast_lock);
return -ENOMEM;
+ }

if (dst_cache_init(&rcast->dst_cache, GFP_ATOMIC)) {
+ spin_unlock_bh(&ub->rcast_lock);
kfree(rcast);
return -ENOMEM;
}

memcpy(&rcast->addr, addr, sizeof(struct udp_media_addr));
+ list_add_rcu(&rcast->list, &ub->rcast.list);
+ b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
+ spin_unlock_bh(&ub->rcast_lock);

if (ntohs(addr->proto) == ETH_P_IP)
pr_info("New replicast peer: %pI4\n", &rcast->addr.ipv4);
@@ -325,8 +327,6 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b,
else if (ntohs(addr->proto) == ETH_P_IPV6)
pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6);
#endif
- b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
- list_add_rcu(&rcast->list, &ub->rcast.list);
return 0;
}

@@ -361,9 +361,6 @@ static int tipc_udp_rcast_disc(struct tipc_bearer *b, struct sk_buff *skb)
return 0;
}

- if (likely(tipc_udp_is_known_peer(b, &src)))
- return 0;
-
return tipc_udp_rcast_add(b, &src);
}

@@ -644,9 +641,6 @@ int tipc_udp_nl_bearer_add(struct tipc_bearer *b, struct nlattr *attr)
return -EINVAL;
}

- if (tipc_udp_is_known_peer(b, &addr))
- return 0;
-
return tipc_udp_rcast_add(b, &addr);
}

@@ -679,6 +673,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
return -ENOMEM;

INIT_LIST_HEAD(&ub->rcast.list);
+ spin_lock_init(&ub->rcast_lock);

if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])
goto err;
@@ -819,10 +814,13 @@ static void cleanup_bearer(struct work_struct *work)
struct udp_replicast *rcast, *tmp;
struct tipc_net *tn;

+ spin_lock_bh(&ub->rcast_lock);
list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
list_del_rcu(&rcast->list);
call_rcu_hurry(&rcast->rcu, rcast_free_rcu);
}
+ ub->disabled = true;
+ spin_unlock_bh(&ub->rcast_lock);

tn = tipc_net(sock_net(ub->sk));

diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c
index 99c8eff9783e..33c42f65364a 100644
--- a/net/tls/tls_device.c
+++ b/net/tls/tls_device.c
@@ -595,13 +595,15 @@ void tls_device_splice_eof(struct socket *sock)
struct tls_context *tls_ctx = tls_get_ctx(sk);
struct iov_iter iter = {};

- if (!tls_is_partially_sent_record(tls_ctx))
+ if (!tls_is_partially_sent_record(tls_ctx) &&
+ !tls_is_pending_open_record(tls_ctx))
return;

mutex_lock(&tls_ctx->tx_lock);
lock_sock(sk);

- if (tls_is_partially_sent_record(tls_ctx)) {
+ if (tls_is_partially_sent_record(tls_ctx) ||
+ tls_is_pending_open_record(tls_ctx)) {
iov_iter_bvec(&iter, ITER_SOURCE, NULL, 0, 0);
tls_push_data(sk, &iter, 0, 0, TLS_RECORD_TYPE_DATA);
}
diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index b10666937c49..7ab7d59105d0 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -26,6 +26,13 @@
/* Threshold for detecting small packets to copy */
#define GOOD_COPY_LEN 128

+/* Max payload that can be collapsed into a single linear skb, using the same
+ * allocation threshold as virtio_vsock_alloc_skb() to avoid adding pressure
+ * on the page allocator.
+ */
+#define MAX_COLLAPSE_LEN \
+ SKB_MAX_ORDER(VIRTIO_VSOCK_SKB_HEADROOM, PAGE_ALLOC_COSTLY_ORDER)
+
static void virtio_transport_cancel_close_work(struct vsock_sock *vsk,
bool cancel_timeout);
static s64 virtio_transport_has_space(struct virtio_vsock_sock *vvs);
@@ -420,6 +427,145 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,
return ret;
}

+static bool virtio_transport_can_collapse(struct sk_buff *skb)
+{
+ /* skbs that are partially consumed, mark a SEQPACKET message boundary,
+ * or are already large enough should not be collapsed: they either
+ * need special accounting, carry protocol state, or already have a
+ * good data-to-overhead ratio.
+ */
+ if (VIRTIO_VSOCK_SKB_CB(skb)->offset)
+ return false;
+ if (le32_to_cpu(virtio_vsock_hdr(skb)->flags) & VIRTIO_VSOCK_SEQ_EOM)
+ return false;
+ if (skb->len >= MAX_COLLAPSE_LEN)
+ return false;
+ return true;
+}
+
+/* Iterate through the packets in the queue starting from the current skb to
+ * count the number of bytes we can collapse.
+ */
+static unsigned int
+virtio_transport_collapse_size(struct sk_buff *skb, struct sk_buff_head *queue)
+{
+ unsigned int target = skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset;
+
+ while ((skb = skb_peek_next(skb, queue)) &&
+ virtio_transport_can_collapse(skb)) {
+ unsigned int len = skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset;
+
+ if (len > MAX_COLLAPSE_LEN - target)
+ return target;
+
+ target += len;
+ }
+
+ return target;
+}
+
+/* Called under lock_sock to compact the receive queue by merging small skbs.
+ * @min_to_free: minimum number of skbs to eliminate from the queue. May free
+ * more to fill each collapsed skb to capacity.
+ */
+static void
+virtio_transport_collapse_rx_queue(struct virtio_vsock_sock *vvs,
+ u32 min_to_free)
+{
+ struct sk_buff *skb, *next_skb, *new_skb = NULL;
+ struct sk_buff_head new_queue;
+ u32 saved = 0;
+
+ __skb_queue_head_init(&new_queue);
+
+ skb_queue_walk_safe(&vvs->rx_queue, skb, next_skb) {
+ struct virtio_vsock_hdr *hdr = virtio_vsock_hdr(skb);
+ u32 src_off = VIRTIO_VSOCK_SKB_CB(skb)->offset;
+ u32 src_len = skb->len - src_off;
+ bool keep;
+
+ keep = !virtio_transport_can_collapse(skb);
+ if (keep) {
+ /* Finalize pending collapsed skb to preserve packet
+ * ordering.
+ */
+ if (new_skb) {
+ __skb_queue_tail(&new_queue, new_skb);
+ new_skb = NULL;
+ saved--;
+ }
+ goto next;
+ }
+
+ /* Finalize if this packet won't fit in the remaining tailroom,
+ * so we can allocate a right-sized new_skb.
+ */
+ if (new_skb && src_len > skb_tailroom(new_skb)) {
+ __skb_queue_tail(&new_queue, new_skb);
+ new_skb = NULL;
+ saved--;
+ }
+
+ if (!new_skb) {
+ unsigned int alloc_size;
+
+ /* Check after finalizing to opportunistically fill
+ * each collapsed skb to capacity, merging more skbs
+ * than strictly required.
+ */
+ if (saved >= min_to_free)
+ break;
+
+ alloc_size = virtio_transport_collapse_size(skb, &vvs->rx_queue);
+
+ /* Only this skb's data is eligible, nothing to merge
+ * with. Keep as-is.
+ */
+ if (alloc_size <= src_len) {
+ keep = true;
+ goto next;
+ }
+
+ new_skb = virtio_vsock_alloc_linear_skb(alloc_size +
+ VIRTIO_VSOCK_SKB_HEADROOM, GFP_KERNEL);
+ if (!new_skb)
+ break;
+
+ memcpy(virtio_vsock_hdr(new_skb), hdr,
+ sizeof(struct virtio_vsock_hdr));
+ virtio_vsock_hdr(new_skb)->len = 0;
+ }
+
+ /* Cannot fail since src_off/src_len are within bounds, but if
+ * it does, discard new_skb to avoid queuing corrupted data.
+ */
+ if (WARN_ON_ONCE(skb_copy_bits(skb, src_off,
+ skb_put(new_skb, src_len),
+ src_len))) {
+ kfree_skb(new_skb);
+ new_skb = NULL;
+ break;
+ }
+
+ le32_add_cpu(&virtio_vsock_hdr(new_skb)->len, src_len);
+ virtio_vsock_hdr(new_skb)->flags |= hdr->flags;
+
+next:
+ __skb_unlink(skb, &vvs->rx_queue);
+ if (keep) {
+ __skb_queue_tail(&new_queue, skb);
+ } else {
+ consume_skb(skb);
+ saved++;
+ }
+ }
+
+ if (new_skb)
+ __skb_queue_tail(&new_queue, new_skb);
+
+ skb_queue_splice(&new_queue, &vvs->rx_queue);
+}
+
static bool virtio_transport_inc_rx_pkt(struct virtio_vsock_sock *vvs,
u32 len)
{
@@ -1354,12 +1500,29 @@ virtio_transport_recv_enqueue(struct vsock_sock *vsk,
{
struct virtio_vsock_sock *vvs = vsk->trans;
bool can_enqueue, free_pkt = false;
+ u32 len, queue_max, queue_len;
struct virtio_vsock_hdr *hdr;
- u32 len;

hdr = virtio_vsock_hdr(skb);
len = le32_to_cpu(hdr->len);

+ /* virtio_transport_inc_rx_pkt() rejects packets when the per-skb
+ * overhead (skb_queue_len * SKB_TRUESIZE(0)) exceeds buf_alloc.
+ * Proactively collapse the queue before that happens.
+ * No rx_lock needed: lock_sock is held by caller, preventing
+ * concurrent enqueue or dequeue.
+ */
+ queue_max = vvs->buf_alloc / SKB_TRUESIZE(0);
+ queue_len = skb_queue_len(&vvs->rx_queue);
+ if (queue_len >= queue_max) {
+ /* Walking a large queue may take a significant amount of time
+ * and cache misses, causing traffic burstiness. Limit the
+ * collapse to freeing room for this packet and the next one.
+ * It may free more to fill each collapsed skb to capacity.
+ */
+ virtio_transport_collapse_rx_queue(vvs, queue_len + 2 - queue_max);
+ }
+
spin_lock_bh(&vvs->rx_lock);

can_enqueue = virtio_transport_inc_rx_pkt(vvs, len);
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 6783e0672dcb..7f6a0418d830 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -1288,6 +1288,7 @@ void wiphy_unregister(struct wiphy *wiphy)
/* this has nothing to do now but make sure it's gone */
cancel_work_sync(&rdev->wiphy_work);

+ cancel_work_sync(&rdev->sched_scan_res_wk);
cancel_work_sync(&rdev->rfkill_block);
cancel_work_sync(&rdev->conn_work);
flush_work(&rdev->event_work);
@@ -1377,6 +1378,7 @@ static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,
list_del_rcu(&wdev->list);
synchronize_net();
rdev->devlist_generation++;
+ wiphy_work_cancel(wdev->wiphy, &wdev->disconnect_wk);

cfg80211_mlme_purge_registrations(wdev);

@@ -1560,7 +1562,7 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
INIT_LIST_HEAD(&wdev->mgmt_registrations);
INIT_LIST_HEAD(&wdev->pmsr_list);
spin_lock_init(&wdev->pmsr_lock);
- INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
+ wiphy_work_init(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);

#ifdef CONFIG_CFG80211_WEXT
wdev->wext.default_key = -1;
@@ -1584,7 +1586,7 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
wdev->netdev->priv_flags |= IFF_DONT_BRIDGE;

- INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
+ wiphy_work_init(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
}

void cfg80211_register_wdev(struct cfg80211_registered_device *rdev,
@@ -1690,11 +1692,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
break;
case NETDEV_GOING_DOWN:
cfg80211_leave(rdev, wdev, -1);
- scoped_guard(wiphy, &rdev->wiphy)
+ scoped_guard(wiphy, &rdev->wiphy) {
cfg80211_remove_links(wdev);
- /* since we just did cfg80211_leave() nothing to do there */
- cancel_work_sync(&wdev->disconnect_wk);
- cancel_work_sync(&wdev->pmsr_free_wk);
+ /* since we just did cfg80211_leave() nothing to do there */
+ wiphy_work_cancel(wdev->wiphy, &wdev->disconnect_wk);
+ }
break;
case NETDEV_DOWN:
wiphy_lock(&rdev->wiphy);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index ae2d56d3ad90..8d40f1e6538b 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -428,7 +428,7 @@ void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *peer_addr,
const u8 *td_bitmap, u8 td_bitmap_len);
int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
-void cfg80211_autodisconnect_wk(struct work_struct *work);
+void cfg80211_autodisconnect_wk(struct wiphy *wiphy, struct wiphy_work *work);

/* SME implementation */
void cfg80211_conn_work(struct work_struct *work);
@@ -583,7 +583,7 @@ cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len,

void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid);
void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev);
-void cfg80211_pmsr_free_wk(struct work_struct *work);
+void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work);

void cfg80211_remove_link(struct wireless_dev *wdev, unsigned int link_id);
void cfg80211_remove_links(struct wireless_dev *wdev);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 21e43e08b5d7..68f9219796e1 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -461,7 +461,9 @@ nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = {
static const struct nla_policy
nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = {
[NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG },
- [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = { .type = NLA_U32 },
+ [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] =
+ NLA_POLICY_RANGE(NLA_U32, NL80211_PREAMBLE_LEGACY,
+ NL80211_PREAMBLE_HE),
[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] =
NLA_POLICY_MAX(NLA_U8, 15),
[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 },
@@ -616,7 +618,7 @@ nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = {
[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX] = { .type = NLA_U32 },
[NL80211_MBSSID_CONFIG_ATTR_EMA] = { .type = NLA_FLAG },
[NL80211_MBSSID_CONFIG_ATTR_TX_LINK_ID] =
- NLA_POLICY_MAX(NLA_U8, IEEE80211_MLD_MAX_NUM_LINKS),
+ NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS - 1),
};

static const struct nla_policy
@@ -6321,7 +6323,8 @@ static int nl80211_parse_mbssid_config(struct wiphy *wiphy,
}

static struct cfg80211_mbssid_elems *
-nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs)
+nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs,
+ struct netlink_ext_ack *extack)
{
struct nlattr *nl_elems;
struct cfg80211_mbssid_elems *elems;
@@ -6332,6 +6335,12 @@ nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs)
return ERR_PTR(-EINVAL);

nla_for_each_nested(nl_elems, attrs, rem_elems) {
+ int ret;
+
+ ret = validate_ie_attr(nl_elems, extack);
+ if (ret)
+ return ERR_PTR(ret);
+
if (num_elems >= 255)
return ERR_PTR(-EINVAL);
num_elems++;
@@ -6503,7 +6512,8 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
if (attrs[NL80211_ATTR_MBSSID_ELEMS]) {
struct cfg80211_mbssid_elems *mbssid =
nl80211_parse_mbssid_elems(&rdev->wiphy,
- attrs[NL80211_ATTR_MBSSID_ELEMS]);
+ attrs[NL80211_ATTR_MBSSID_ELEMS],
+ extack);

if (IS_ERR(mbssid))
return PTR_ERR(mbssid);
@@ -6519,8 +6529,10 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
if (IS_ERR(rnr))
return PTR_ERR(rnr);

- if (rnr && rnr->cnt < bcn->mbssid_ies->cnt)
+ if (rnr && rnr->cnt < bcn->mbssid_ies->cnt) {
+ kfree(rnr);
return -EINVAL;
+ }

bcn->rnr_ies = rnr;
}
@@ -22545,7 +22557,8 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
wdev->nl_owner_dead = true;
schedule_work(&rdev->destroy_work);
} else if (wdev->conn_owner_nlportid == notify->portid) {
- schedule_work(&wdev->disconnect_wk);
+ wiphy_work_queue(wdev->wiphy,
+ &wdev->disconnect_wk);
}

cfg80211_release_pmsr(wdev, notify->portid);
diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c
index 0ca93fd7d46f..feced0f762e0 100644
--- a/net/wireless/pmsr.c
+++ b/net/wireless/pmsr.c
@@ -109,6 +109,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI],
"FTM: LCI request not supported");
+ return -EOPNOTSUPP;
}

out->ftm.request_civicloc =
@@ -117,6 +118,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC],
"FTM: civic location request not supported");
+ return -EOPNOTSUPP;
}

out->ftm.trigger_based =
@@ -214,6 +216,7 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
{
struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1];
struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1];
+ bool have_measurement_type = false;
struct nlattr *treq;
int err, rem;

@@ -267,6 +270,14 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
}

nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) {
+ if (have_measurement_type) {
+ NL_SET_ERR_MSG_ATTR(info->extack, treq,
+ "multiple measurement types in request data");
+ return -EINVAL;
+ }
+
+ have_measurement_type = true;
+
switch (nla_type(treq)) {
case NL80211_PMSR_TYPE_FTM:
err = pmsr_parse_ftm(rdev, treq, out, info);
@@ -276,10 +287,16 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
"unsupported measurement type");
err = -EINVAL;
}
+ if (err)
+ return err;
}

- if (err)
- return err;
+ if (!have_measurement_type) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ req[NL80211_PMSR_REQ_ATTR_DATA],
+ "missing measurement type in request data");
+ return -EINVAL;
+ }

return 0;
}
@@ -315,6 +332,11 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
}
}

+ if (!count) {
+ NL_SET_ERR_MSG_ATTR(info->extack, peers, "No peers specified");
+ return -EINVAL;
+ }
+
req = kzalloc_flex(*req, peers, count);
if (!req)
return -ENOMEM;
@@ -645,13 +667,11 @@ static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev)
}
}

-void cfg80211_pmsr_free_wk(struct work_struct *work)
+void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work)
{
struct wireless_dev *wdev = container_of(work, struct wireless_dev,
pmsr_free_wk);

- guard(wiphy)(wdev->wiphy);
-
cfg80211_pmsr_process_abort(wdev);
}

@@ -667,7 +687,7 @@ void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev)
}
spin_unlock_bh(&wdev->pmsr_lock);

- cancel_work_sync(&wdev->pmsr_free_wk);
+ wiphy_work_cancel(wdev->wiphy, &wdev->pmsr_free_wk);
if (found)
cfg80211_pmsr_process_abort(wdev);

@@ -682,7 +702,7 @@ void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid)
list_for_each_entry(req, &wdev->pmsr_list, list) {
if (req->nl_portid == portid) {
req->nl_portid = 0;
- schedule_work(&wdev->pmsr_free_wk);
+ wiphy_work_queue(wdev->wiphy, &wdev->pmsr_free_wk);
}
}
spin_unlock_bh(&wdev->pmsr_lock);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index d9f2a77e127c..8ea6946eb0a3 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -205,7 +205,7 @@ bool cfg80211_is_element_inherited(const struct element *elem,
return true;

if (elem->id == WLAN_EID_EXTENSION) {
- if (!ext_id_len)
+ if (!ext_id_len || !elem->datalen)
return true;
loop_len = ext_id_len;
list = &non_inherit_elem->data[3 + id_len];
@@ -3312,14 +3312,15 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
bssid = ext->u.s1g_beacon.sa;
capability = le16_to_cpu(compat->compat_info);
beacon_interval = le16_to_cpu(compat->beacon_int);
+ tsf = le32_to_cpu(ext->u.s1g_beacon.timestamp);
+ tsf |= (u64)le32_to_cpu(compat->tsf_completion) << 32;
} else {
bssid = mgmt->bssid;
beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
+ tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
}

- tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
-
if (ieee80211_is_probe_resp(mgmt->frame_control))
ftype = CFG80211_BSS_FTYPE_PRESP;
else if (ext)
@@ -3613,8 +3614,10 @@ int cfg80211_wext_siwscan(struct net_device *dev,
/* translate "Scan for SSID" request */
if (wreq) {
if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
- if (wreq->essid_len > IEEE80211_MAX_SSID_LEN)
- return -EINVAL;
+ if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
+ err = -EINVAL;
+ goto out;
+ }
memcpy(creq->req.ssids[0].ssid, wreq->essid,
wreq->essid_len);
creq->req.ssids[0].ssid_len = wreq->essid_len;
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 86e2ccaa678c..1ddaadbed74c 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -1577,13 +1577,11 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
* Used to clean up after the connection / connection attempt owner socket
* disconnects
*/
-void cfg80211_autodisconnect_wk(struct work_struct *work)
+void cfg80211_autodisconnect_wk(struct wiphy *wiphy, struct wiphy_work *work)
{
struct wireless_dev *wdev =
container_of(work, struct wireless_dev, disconnect_wk);
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-
- guard(wiphy)(wdev->wiphy);
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);

if (wdev->conn_owner_nlportid) {
switch (wdev->iftype) {
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index af8762b24039..f9758c140ba4 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -1772,15 +1772,19 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
{
struct sock *s;

+again:
write_lock_bh(&x25_list_lock);

sk_for_each(s, &x25_list) {
if (x25_sk(s)->neighbour == nb) {
+ sock_hold(s);
write_unlock_bh(&x25_list_lock);
lock_sock(s);
- x25_disconnect(s, ENETUNREACH, 0, 0);
+ if (x25_sk(s)->neighbour == nb)
+ x25_disconnect(s, ENETUNREACH, 0, 0);
release_sock(s);
- write_lock_bh(&x25_list_lock);
+ sock_put(s);
+ goto again;
}
}
write_unlock_bh(&x25_list_lock);
diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
index 550457e4c4f0..4719aba331cd 100644
--- a/net/xfrm/xfrm_device.c
+++ b/net/xfrm/xfrm_device.c
@@ -182,7 +182,7 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
err = x->type_offload->xmit(x, skb, esp_features);
if (err) {
if (err == -EINPROGRESS)
- return NULL;
+ return ERR_PTR(-EINPROGRESS);

XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR);
kfree_skb(skb);
@@ -224,7 +224,15 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
pskb = skb2;
}

- return skb;
+ /* skb_gso_segment() set skb->prev to the last segment, but async
+ * crypto may have stolen it above without updating ->prev. Repoint
+ * it at the last retained segment so validate_xmit_skb_list() does
+ * not chain onto a segment now owned by the crypto engine.
+ */
+ if (skb)
+ skb->prev = pskb;
+
+ return skb ? skb : ERR_PTR(-EINPROGRESS);
}
EXPORT_SYMBOL_GPL(validate_xmit_xfrm);

diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c
index ad810d1f97c0..597aedeac26e 100644
--- a/net/xfrm/xfrm_iptfs.c
+++ b/net/xfrm/xfrm_iptfs.c
@@ -480,6 +480,7 @@ static int iptfs_skb_add_frags(struct sk_buff *skb,
}
__skb_frag_ref(tofrag);
shinfo->nr_frags++;
+ shinfo->flags |= SKBFL_SHARED_FRAG;

/* see if we are done */
fraglen = tofrag->len;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 639934f30016..bed4b5a3fcce 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1329,8 +1329,8 @@ static void xfrm_hash_rebuild(struct work_struct *work)
}
}

- if (policy->selector.prefixlen_d < dbits ||
- policy->selector.prefixlen_s < sbits)
+ if (policy->selector.prefixlen_d >= dbits &&
+ policy->selector.prefixlen_s >= sbits)
continue;

bin = xfrm_policy_inexact_alloc_bin(policy, dir);
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index ec9ecf72c76b..37193d2fcc2f 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -2070,8 +2070,11 @@ static struct xfrm_state *xfrm_state_clone_and_setup(struct xfrm_state *orig,

x->mode_cbs = orig->mode_cbs;
if (x->mode_cbs && x->mode_cbs->clone_state) {
- if (x->mode_cbs->clone_state(x, orig))
+ if (x->mode_cbs->clone_state(x, orig)) {
+ if (!x->mode_data)
+ x->mode_cbs = NULL;
goto error;
+ }
}


@@ -2973,7 +2976,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
if (sockptr_is_null(optval) && !optlen) {
xfrm_sk_policy_insert(sk, XFRM_POLICY_IN, NULL);
xfrm_sk_policy_insert(sk, XFRM_POLICY_OUT, NULL);
- __sk_dst_reset(sk);
+ sk_dst_reset(sk);
return 0;
}

@@ -3013,7 +3016,7 @@ int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval, int optlen)
if (err >= 0) {
xfrm_sk_policy_insert(sk, err, pol);
xfrm_pol_put(pol);
- __sk_dst_reset(sk);
+ sk_dst_reset(sk);
err = 0;
}

@@ -3254,6 +3257,8 @@ int __xfrm_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
if (x->mode_cbs->init_state)
err = x->mode_cbs->init_state(x);
module_put(x->mode_cbs->owner);
+ if (err && !x->mode_data)
+ x->mode_cbs = NULL;
}
error:
return err;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 61eb5de33b87..b36741c4ea3d 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -2077,13 +2077,12 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family,
switch (ut[i].mode) {
case XFRM_MODE_TUNNEL:
case XFRM_MODE_BEET:
+ case XFRM_MODE_IPTFS:
if (ut[i].optional && dir == XFRM_POLICY_OUT) {
NL_SET_ERR_MSG(extack, "Mode in optional template not allowed in outbound policy");
return -EINVAL;
}
break;
- case XFRM_MODE_IPTFS:
- break;
default:
if (ut[i].family != prev_family) {
NL_SET_ERR_MSG(extack, "Mode in template doesn't support a family change");
diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs
index 854e7c471434..812f8e5a08d5 100644
--- a/rust/bindings/lib.rs
+++ b/rust/bindings/lib.rs
@@ -27,6 +27,10 @@
#[allow(clippy::ref_as_ptr)]
#[allow(clippy::undocumented_unsafe_blocks)]
#[cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))]
+#[cfg_attr(
+ CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS,
+ allow(suspicious_runtime_symbol_definitions)
+)]
mod bindings_raw {
use pin_init::{MaybeZeroable, Zeroable};

diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index 6d5396a43ebe..828c4cfdd4d6 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -737,9 +737,7 @@ macro_rules! impl_device_context_into_aref {
#[macro_export]
macro_rules! dev_printk {
($method:ident, $dev:expr, $($f:tt)*) => {
- {
- $crate::device::Device::$method($dev.as_ref(), $crate::prelude::fmt!($($f)*))
- }
+ $crate::device::Device::$method($dev.as_ref(), $crate::prelude::fmt!($($f)*))
}
}

@@ -766,7 +764,7 @@ macro_rules! dev_printk {
/// ```
#[macro_export]
macro_rules! dev_emerg {
- ($($f:tt)*) => { $crate::dev_printk!(pr_emerg, $($f)*); }
+ ($($f:tt)*) => { $crate::dev_printk!(pr_emerg, $($f)*) }
}

/// Prints an alert-level message (level 1) prefixed with device information.
@@ -792,7 +790,7 @@ macro_rules! dev_emerg {
/// ```
#[macro_export]
macro_rules! dev_alert {
- ($($f:tt)*) => { $crate::dev_printk!(pr_alert, $($f)*); }
+ ($($f:tt)*) => { $crate::dev_printk!(pr_alert, $($f)*) }
}

/// Prints a critical-level message (level 2) prefixed with device information.
@@ -818,7 +816,7 @@ macro_rules! dev_alert {
/// ```
#[macro_export]
macro_rules! dev_crit {
- ($($f:tt)*) => { $crate::dev_printk!(pr_crit, $($f)*); }
+ ($($f:tt)*) => { $crate::dev_printk!(pr_crit, $($f)*) }
}

/// Prints an error-level message (level 3) prefixed with device information.
@@ -844,7 +842,7 @@ macro_rules! dev_crit {
/// ```
#[macro_export]
macro_rules! dev_err {
- ($($f:tt)*) => { $crate::dev_printk!(pr_err, $($f)*); }
+ ($($f:tt)*) => { $crate::dev_printk!(pr_err, $($f)*) }
}

/// Prints a warning-level message (level 4) prefixed with device information.
@@ -870,7 +868,7 @@ macro_rules! dev_err {
/// ```
#[macro_export]
macro_rules! dev_warn {
- ($($f:tt)*) => { $crate::dev_printk!(pr_warn, $($f)*); }
+ ($($f:tt)*) => { $crate::dev_printk!(pr_warn, $($f)*) }
}

/// Prints a notice-level message (level 5) prefixed with device information.
@@ -896,7 +894,7 @@ macro_rules! dev_warn {
/// ```
#[macro_export]
macro_rules! dev_notice {
- ($($f:tt)*) => { $crate::dev_printk!(pr_notice, $($f)*); }
+ ($($f:tt)*) => { $crate::dev_printk!(pr_notice, $($f)*) }
}

/// Prints an info-level message (level 6) prefixed with device information.
@@ -922,7 +920,7 @@ macro_rules! dev_notice {
/// ```
#[macro_export]
macro_rules! dev_info {
- ($($f:tt)*) => { $crate::dev_printk!(pr_info, $($f)*); }
+ ($($f:tt)*) => { $crate::dev_printk!(pr_info, $($f)*) }
}

/// Prints a debug-level message (level 7) prefixed with device information.
@@ -948,5 +946,5 @@ macro_rules! dev_info {
/// ```
#[macro_export]
macro_rules! dev_dbg {
- ($($f:tt)*) => { $crate::dev_printk!(pr_dbg, $($f)*); }
+ ($($f:tt)*) => { $crate::dev_printk!(pr_dbg, $($f)*) }
}
diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs
index 363e93cbb139..b8463823aed9 100644
--- a/rust/kernel/time.rs
+++ b/rust/kernel/time.rs
@@ -441,15 +441,22 @@ pub const fn as_nanos(self) -> i64 {
/// to the value in the [`Delta`].
#[inline]
pub fn as_micros_ceil(self) -> i64 {
+ let n = self.as_nanos();
+ let n = if n >= 0 {
+ n.saturating_add(NSEC_PER_USEC - 1)
+ } else {
+ n
+ };
+
#[cfg(CONFIG_64BIT)]
{
- self.as_nanos().saturating_add(NSEC_PER_USEC - 1) / NSEC_PER_USEC
+ n / NSEC_PER_USEC
}

#[cfg(not(CONFIG_64BIT))]
// SAFETY: It is always safe to call `ktime_to_us()` with any value.
unsafe {
- bindings::ktime_to_us(self.as_nanos().saturating_add(NSEC_PER_USEC - 1))
+ bindings::ktime_to_us(n)
}
}

diff --git a/rust/uapi/lib.rs b/rust/uapi/lib.rs
index b8a515de31ca..797ead5b5626 100644
--- a/rust/uapi/lib.rs
+++ b/rust/uapi/lib.rs
@@ -24,6 +24,10 @@
unsafe_op_in_unsafe_fn
)]
#![cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))]
+#![cfg_attr(
+ CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS,
+ allow(suspicious_runtime_symbol_definitions)
+)]
#![feature(cfi_encoding)]

// Manual definition of blocklisted types.
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index 9bef2f792498..94a2f243a798 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -61,12 +61,23 @@ struct snd_seq_timer *snd_seq_timer_new(void)
void snd_seq_timer_delete(struct snd_seq_timer **tmr)
{
struct snd_seq_timer *t = *tmr;
- *tmr = NULL;
+ struct snd_timer_instance *ti;

if (t == NULL) {
pr_debug("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n");
return;
}
+
+ scoped_guard(spinlock_irq, &t->lock) {
+ ti = t->timeri;
+ t->timeri = NULL;
+ }
+ if (ti) {
+ snd_timer_close(ti);
+ snd_timer_instance_free(ti);
+ }
+
+ *tmr = NULL;
t->running = 0;

/* reset time */
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 3d72379e57a8..72d78902e9d5 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -448,10 +448,20 @@ static void snd_timer_close_locked(struct snd_timer_instance *timeri,
snd_timer_stop(timeri);

if (timer) {
+ struct snd_timer_instance *slave;
+ bool busy;
+
timer->num_instances--;
- /* wait, until the active callback is finished */
+ /* unqueue then drain the slaves' callbacks before remove_slave_links() severs them */
spin_lock_irq(&timer->lock);
- while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
+ list_for_each_entry(slave, &timeri->slave_list_head, open_list)
+ list_del_init(&slave->ack_list);
+ for (;;) {
+ busy = timeri->flags & SNDRV_TIMER_IFLG_CALLBACK;
+ list_for_each_entry(slave, &timeri->slave_list_head, open_list)
+ busy |= slave->flags & SNDRV_TIMER_IFLG_CALLBACK;
+ if (!busy)
+ break;
spin_unlock_irq(&timer->lock);
udelay(10);
spin_lock_irq(&timer->lock);
@@ -884,12 +894,15 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
ack_list_head = &timer->ack_list_head;
else
ack_list_head = &timer->sack_list_head;
- if (list_empty(&ti->ack_list))
+ /* don't requeue an instance whose callback is still running */
+ if (list_empty(&ti->ack_list) &&
+ !(ti->flags & SNDRV_TIMER_IFLG_CALLBACK))
list_add_tail(&ti->ack_list, ack_list_head);
list_for_each_entry(ts, &ti->slave_active_head, active_list) {
ts->pticks = ti->pticks;
ts->resolution = resolution;
- if (list_empty(&ts->ack_list))
+ if (list_empty(&ts->ack_list) &&
+ !(ts->flags & SNDRV_TIMER_IFLG_CALLBACK))
list_add_tail(&ts->ack_list, ack_list_head);
}
}
diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c
index 1f4d646724ed..a4a2f2f0540c 100644
--- a/sound/hda/codecs/hdmi/hdmi.c
+++ b/sound/hda/codecs/hdmi/hdmi.c
@@ -1688,6 +1688,9 @@ int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo,
per_pin->channels = substream->runtime->channels;
per_pin->setup = true;

+ if (spec->ops.prepare)
+ spec->ops.prepare(codec, per_pin);
+
if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) {
stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core,
substream);
diff --git a/sound/hda/codecs/hdmi/hdmi_local.h b/sound/hda/codecs/hdmi/hdmi_local.h
index 548241ad3fa9..dce9a335cff2 100644
--- a/sound/hda/codecs/hdmi/hdmi_local.h
+++ b/sound/hda/codecs/hdmi/hdmi_local.h
@@ -74,6 +74,15 @@ struct hdmi_ops {
hda_nid_t pin_nid, int dev_id, u32 stream_tag,
int format);

+ /*
+ * Optional hook invoked at the beginning of the PCM prepare
+ * sequence, before the audio infoframe and stream format are
+ * (re)programmed. Used to disable keep-alive / silent stream so
+ * that the format change is not done while keep-alive is active.
+ */
+ void (*prepare)(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin);
+
void (*pin_cvt_fixup)(struct hda_codec *codec,
struct hdmi_spec_per_pin *per_pin,
hda_nid_t cvt_nid);
diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c
index 6a7882544ab7..73141046d502 100644
--- a/sound/hda/codecs/hdmi/intelhdmi.c
+++ b/sound/hda/codecs/hdmi/intelhdmi.c
@@ -418,6 +418,28 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx);
}

+/*
+ * prepare ops override for HSW+
+ *
+ * Disable keep-alive before the converter format and audio infoframe are
+ * reprogrammed by the PCM prepare sequence. Changing the audio format (e.g.
+ * the channel count when switching to multichannel PCM) while a keep-alive
+ * stream is active is not safe, so release keep-alive here, early in the
+ * sequence. It is re-enabled once the new stream has been set up, in
+ * i915_hsw_setup_stream().
+ */
+static void i915_hsw_prepare(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin->silent_stream) {
+ silent_stream_set_kae(codec, per_pin, false);
+ /* wait for pending transfers in codec to clear */
+ usleep_range(100, 200);
+ }
+}
+
/* setup_stream ops override for HSW+ */
static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, int dev_id, u32 stream_tag,
@@ -435,15 +457,16 @@ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,

haswell_verify_D0(codec, cvt_nid, pin_nid);

- if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
- silent_stream_set_kae(codec, per_pin, false);
- /* wait for pending transfers in codec to clear */
- usleep_range(100, 200);
- }
-
res = snd_hda_hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
stream_tag, format);

+ /*
+ * Keep-alive was disabled in i915_hsw_prepare(), re-enable it now.
+ * The pin lookup above resolves to the same per_pin that prepare
+ * used (pin_nid comes from that per_pin), so this stays balanced; a
+ * NULL per_pin only occurs on a lookup failure that also implies no
+ * active keep-alive stream to restore.
+ */
if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
usleep_range(100, 200);
silent_stream_set_kae(codec, per_pin, true);
@@ -607,6 +630,7 @@ static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid,
codec->depop_delay = 0;
codec->auto_runtime_pm = 1;

+ spec->ops.prepare = i915_hsw_prepare;
spec->ops.setup_stream = i915_hsw_setup_stream;
spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
spec->ops.silent_stream = i915_set_silent_stream;
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index 5b385a82e7dc..427caf08145d 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -3891,6 +3891,7 @@ enum {
ALC269_FIXUP_DMIC_THINKPAD_ACPI,
ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13,
ALC269VC_FIXUP_INFINIX_Y4_MAX,
+ ALC269VC_FIXUP_LUNNEN_GROUND_14,
ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO,
ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
@@ -4345,6 +4346,13 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
},
+ [ALC269VC_FIXUP_LUNNEN_GROUND_14] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170150 }, /* internal speaker */
+ { }
+ },
+ },
[ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -7875,6 +7883,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX),
SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME),
SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK),
+ SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14),
SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER),
SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC),
SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED),
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c
index 64a5bd895fd1..237059ef22f5 100644
--- a/sound/hda/codecs/side-codecs/cs35l41_hda.c
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c
@@ -1434,10 +1434,19 @@ static int cs35l41_get_acpi_mute_state(struct cs35l41_hda *cs35l41, acpi_handle
guid_parse(CS35L41_UUID, &guid);

if (cs35l41_dsm_supported(handle, CS35L41_DSM_GET_MUTE)) {
- ret = acpi_evaluate_dsm(handle, &guid, 0, CS35L41_DSM_GET_MUTE, NULL);
+ ret = acpi_evaluate_dsm_typed(handle, &guid, 0,
+ CS35L41_DSM_GET_MUTE, NULL,
+ ACPI_TYPE_BUFFER);
+
if (!ret)
return -EINVAL;
+ if (!ret->buffer.length || !ret->buffer.pointer) {
+ ACPI_FREE(ret);
+ return -EINVAL;
+ }
+
mute = *ret->buffer.pointer;
+ ACPI_FREE(ret);
dev_dbg(cs35l41->dev, "CS35L41_DSM_GET_MUTE: %d\n", mute);
}

diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index 977e4f2a7a70..a04c301df4d6 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -15,13 +15,17 @@ config SND_SOC_AMD_ACP_COMMON

config SND_SOC_ACPI_AMD_MATCH
tristate
- select SND_SOC_ACPI_AMD_SDCA_QUIRKS if SND_SOC_SDCA
select SND_SOC_ACPI if ACPI

config SND_SOC_ACPI_AMD_SDCA_QUIRKS
- tristate
- depends on ACPI
+ bool "AMD ACPI SDCA quirks"
+ depends on SND_SOC_ACPI_AMD_MATCH
depends on SND_SOC_SDCA
+ depends on SND_SOC_ACPI_AMD_MATCH = m || SND_SOC_SDCA = y
+ default y
+ help
+ Enable SDCA quirk support for AMD ACPI match tables.
+ This is compiled into the snd-soc-acpi-amd-match module.

if SND_SOC_AMD_ACP_COMMON

diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
index 81d23aded348..ab5f9dc871a6 100644
--- a/sound/soc/amd/acp/Makefile
+++ b/sound/soc/amd/acp/Makefile
@@ -24,10 +24,10 @@ snd-acp-mach-y := acp-mach-common.o
snd-acp-legacy-mach-y := acp-legacy-mach.o acp3x-es83xx/acp3x-es83xx.o
snd-acp-sof-mach-y := acp-sof-mach.o
snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o amd-acp70-acpi-match.o
+snd-soc-acpi-amd-match-$(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) += soc-acpi-amd-sdca-quirks.o
snd-acp-sdw-mach-y := acp-sdw-mach-common.o
snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o
snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-mach.o
-snd-soc-acpi-amd-sdca-quirks-y += soc-acpi-amd-sdca-quirks.o

obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o
obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o
@@ -41,7 +41,6 @@ obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o
obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o
obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o

-obj-$(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) += snd-soc-acpi-amd-sdca-quirks.o
obj-$(CONFIG_SND_AMD_SOUNDWIRE_ACPI) += snd-amd-sdw-acpi.o
obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c
index 18f2918d4ada..9e97a805d0df 100644
--- a/sound/soc/amd/acp/amd-acp70-acpi-match.c
+++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c
@@ -775,5 +775,5 @@ MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vijendar.Mukunda@xxxxxxx");
#if IS_ENABLED(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS)
-MODULE_IMPORT_NS("SND_SOC_ACPI_AMD_SDCA_QUIRKS");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
#endif
diff --git a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c
index 63bf9e3c0ae1..9248b6d09e76 100644
--- a/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c
+++ b/sound/soc/amd/acp/soc-acpi-amd-sdca-quirks.c
@@ -35,8 +35,4 @@ bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg)

return false;
}
-EXPORT_SYMBOL_NS(snd_soc_acpi_amd_sdca_is_device_rt712_vb, "SND_SOC_ACPI_AMD_SDCA_QUIRKS");

-MODULE_DESCRIPTION("ASoC ACPI AMD SDCA quirks");
-MODULE_LICENSE("GPL");
-MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 9751cf0784a6..5e73fb20e152 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -248,7 +248,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
if (sdw_dma_irq_flag)
return IRQ_WAKE_THREAD;

- if (irq_flag | wake_irq_flag)
+ if (irq_flag || wake_irq_flag)
return IRQ_HANDLED;
else
return IRQ_NONE;
@@ -601,7 +601,7 @@ static int snd_acp63_probe(struct pci_dev *pci,
return -ENODEV;
}

- ret = pci_request_regions(pci, "AMD ACP6.2 audio");
+ ret = pci_request_regions(pci, "AMD ACP6.3 audio");
if (ret < 0) {
dev_err(&pci->dev, "pci_request_regions failed\n");
goto disable_pci;
@@ -692,8 +692,37 @@ static int snd_acp_runtime_resume(struct device *dev)
return acp_hw_runtime_resume(dev);
}

+static void acp_disable_msi_on_resume(struct pci_dev *pdev)
+{
+ u16 control;
+
+ if (!pdev->msi_cap)
+ return;
+
+ pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, &control);
+ if (control & PCI_MSI_FLAGS_ENABLE) {
+ dev_warn(&pdev->dev,
+ "ACP: MSI unexpectedly enabled after resume (flags=0x%04x), disabling\n",
+ control);
+ control &= ~PCI_MSI_FLAGS_ENABLE;
+ pci_write_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, control);
+ }
+}
+
static int snd_acp_resume(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ /*
+ * BIOS/firmware may re-enable MSI in PCI config space during
+ * system resume even though this driver only uses legacy INTx
+ * interrupts. If MSI is left enabled with stale address/data
+ * registers, the device will write interrupts to a bogus address
+ * causing IOMMU IO_PAGE_FAULT and interrupt delivery failure.
+ * Explicitly clear the MSI Enable bit before reinitializing
+ * the ACP hardware.
+ */
+ acp_disable_msi_on_resume(pdev);
return acp_hw_resume(dev);
}

diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index 3afcef2dfa35..c0bf45b76cb8 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -17,11 +17,17 @@ static const struct snd_soc_dapm_widget bt_sco_widgets[] = {
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("BT_SCO_TX", "Capture", 0,
SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("BT_SCO_RX_WB", "WB Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("BT_SCO_TX_WB", "WB Capture", 0,
+ SND_SOC_NOPM, 0, 0),
};

static const struct snd_soc_dapm_route bt_sco_routes[] = {
{ "BT_SCO_TX", NULL, "RX" },
{ "TX", NULL, "BT_SCO_RX" },
+ { "BT_SCO_TX_WB", NULL, "RX" },
+ { "TX", NULL, "BT_SCO_RX_WB" },
};

static struct snd_soc_dai_driver bt_sco_dai[] = {
@@ -45,14 +51,14 @@ static struct snd_soc_dai_driver bt_sco_dai[] = {
{
.name = "bt-sco-pcm-wb",
.playback = {
- .stream_name = "Playback",
+ .stream_name = "WB Playback",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
- .stream_name = "Capture",
+ .stream_name = "WB Capture",
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c
index 0492ddc4102d..8259714d8b26 100644
--- a/sound/soc/codecs/cs35l56-i2c.c
+++ b/sound/soc/codecs/cs35l56-i2c.c
@@ -55,9 +55,7 @@ static int cs35l56_i2c_probe(struct i2c_client *client)
if (ret != 0)
return ret;

- ret = cs35l56_init(cs35l56);
- if (ret == 0)
- ret = cs35l56_irq_request(&cs35l56->base, client->irq);
+ ret = cs35l56_irq_request(&cs35l56->base, client->irq);
if (ret < 0)
cs35l56_remove(cs35l56);

diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c
index 9bc9b7c98390..b1eb924a5b6c 100644
--- a/sound/soc/codecs/cs35l56-spi.c
+++ b/sound/soc/codecs/cs35l56-spi.c
@@ -44,9 +44,7 @@ static int cs35l56_spi_probe(struct spi_device *spi)
if (ret != 0)
return ret;

- ret = cs35l56_init(cs35l56);
- if (ret == 0)
- ret = cs35l56_irq_request(&cs35l56->base, spi->irq);
+ ret = cs35l56_irq_request(&cs35l56->base, spi->irq);
if (ret < 0)
cs35l56_remove(cs35l56);

diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index 033028a4734b..ad0f88cc0fb4 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -1975,6 +1975,16 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
goto err;
}

+ /*
+ * On SoundWire the cs35l56_init() cannot be run until after the
+ * device has been enumerated by the SoundWire core.
+ */
+ if (!cs35l56->sdw_peripheral) {
+ ret = cs35l56_init(cs35l56);
+ if (ret)
+ goto err_remove_wm_adsp;
+ }
+
ret = snd_soc_register_component(cs35l56->base.dev,
&soc_component_dev_cs35l56,
cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
@@ -1989,6 +1999,11 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
wm_adsp2_remove(&cs35l56->dsp);

err:
+ if (pm_runtime_enabled(cs35l56->base.dev)) {
+ pm_runtime_dont_use_autosuspend(cs35l56->base.dev);
+ pm_runtime_disable(cs35l56->base.dev);
+ }
+
gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);

@@ -2074,7 +2089,7 @@ int cs35l56_init(struct cs35l56_private *cs35l56)
return dev_err_probe(cs35l56->base.dev, ret, "Failed to write ASP1_CONTROL3\n");

cs35l56->base.init_done = true;
- complete(&cs35l56->init_completion);
+ complete_all(&cs35l56->init_completion);

return 0;
}
diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c
index 3e04e6897b14..e88109979c18 100644
--- a/sound/soc/codecs/cs42l43-jack.c
+++ b/sound/soc/codecs/cs42l43-jack.c
@@ -310,6 +310,7 @@ irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data)
#define CS42L43_JACK_ABSENT 0x0

#define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT)
+#define CS42L43_JACK_MICROPHONE (SND_JACK_MECHANICAL | SND_JACK_MICROPHONE)
#define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE)
#define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET)
#define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT)
@@ -871,7 +872,7 @@ static const struct cs42l43_jack_override_mode {
.hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
.mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) |
CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK,
- .report = CS42L43_JACK_LINEIN,
+ .report = CS42L43_JACK_MICROPHONE,
},
[CS42L43_JACK_RAW_OPTICAL] = {
.hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c
index ceb367ae05ba..adc4fdef3bee 100644
--- a/sound/soc/codecs/tas2562.c
+++ b/sound/soc/codecs/tas2562.c
@@ -675,11 +675,12 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562)
if (tas2562->sdz_gpio == NULL) {
tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shut-down",
GPIOD_OUT_HIGH);
- if (IS_ERR(tas2562->sdz_gpio))
+ if (IS_ERR(tas2562->sdz_gpio)) {
if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER)
return -EPROBE_DEFER;

- tas2562->sdz_gpio = NULL;
+ tas2562->sdz_gpio = NULL;
+ }
}

if (tas2562->model_id == TAS2110)
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
index 885e0b6fed00..583cda1f77fa 100644
--- a/sound/soc/codecs/tas2781-fmwlib.c
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -12,6 +12,7 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/limits.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -1099,13 +1100,42 @@ static int tasdevice_load_block_kernel(
return 0;
}

+static int tasdevice_fw_strnlen(const struct firmware *fmw, int offset)
+{
+ const u8 *start;
+ const u8 *nul;
+ size_t remaining;
+ size_t len;
+
+ if (offset < 0 || offset >= fmw->size)
+ return -EINVAL;
+
+ start = fmw->data + offset;
+ remaining = fmw->size - offset;
+ nul = memchr(start, '\0', remaining);
+ if (!nul)
+ return -EINVAL;
+
+ len = nul - start;
+ if (len > INT_MAX)
+ return -EOVERFLOW;
+
+ return len;
+}
+
static int fw_parse_variable_hdr(struct tasdevice_priv
*tas_priv, struct tasdevice_dspfw_hdr *fw_hdr,
const struct firmware *fmw, int offset)
{
const unsigned char *buf = fmw->data;
- int len = strlen((char *)&buf[offset]);
+ int len;

+ len = tasdevice_fw_strnlen(fmw, offset);
+ if (len < 0) {
+ dev_err(tas_priv->dev, "%s: Description error\n", __func__);
+ offset = len;
+ goto out;
+ }
len++;

if (offset + len + 8 > fmw->size) {
@@ -1237,7 +1267,12 @@ static int fw_parse_data(struct tasdevice_fw *tas_fmw,
memcpy(img_data->name, &data[offset], 64);
offset += 64;

- n = strlen((char *)&data[offset]);
+ n = tasdevice_fw_strnlen(fmw, offset);
+ if (n < 0) {
+ dev_err(tas_fmw->dev, "%s: Description error\n", __func__);
+ offset = n;
+ goto out;
+ }
n++;
if (offset + n + 2 > fmw->size) {
dev_err(tas_fmw->dev, "%s: Description error\n", __func__);
@@ -1308,7 +1343,12 @@ static int fw_parse_program_data(struct tasdevice_priv *tas_priv,
}
offset += 64;

- n = strlen((char *)&buf[offset]);
+ n = tasdevice_fw_strnlen(fmw, offset);
+ if (n < 0) {
+ dev_err(tas_priv->dev, "Description err\n");
+ offset = n;
+ goto out;
+ }
/* skip '\0' and 5 unused bytes */
n += 6;
if (offset + n > fmw->size) {
@@ -1371,7 +1411,12 @@ static int fw_parse_configuration_data(
memcpy(config->name, &data[offset], 64);
offset += 64;

- n = strlen((char *)&data[offset]);
+ n = tasdevice_fw_strnlen(fmw, offset);
+ if (n < 0) {
+ dev_err(tas_priv->dev, "Description err\n");
+ offset = n;
+ goto out;
+ }
n += 15;
if (offset + n > fmw->size) {
dev_err(tas_priv->dev, "Description err\n");
@@ -2133,7 +2178,8 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv,
{
struct tasdevice_calibration *calibration;
unsigned char *data = (unsigned char *)fmw->data;
- unsigned int i, n;
+ unsigned int i;
+ int n;

if (offset + 2 > fmw->size) {
dev_err(tas_priv->dev, "%s: Calibrations error\n", __func__);
@@ -2165,7 +2211,12 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv,
calibration = &(tas_fmw->calibrations[i]);
offset += 64;

- n = strlen((char *)&data[offset]);
+ n = tasdevice_fw_strnlen(fmw, offset);
+ if (n < 0) {
+ dev_err(tas_priv->dev, "Description err\n");
+ offset = n;
+ goto out;
+ }
/* skip '\0' and 2 unused bytes */
n += 3;
if (offset + n > fmw->size) {
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 78e953cd3e9f..ba22333b2c50 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -804,6 +804,8 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
unsigned int ofs = sai->soc_data->reg_offset;
+ int adir = tx ? RX : TX;
+ int dir = tx ? TX : RX;

/* Clear xMR to avoid channel swap with mclk_with_tere enabled case */
regmap_write(sai->regmap, FSL_SAI_xMR(tx), 0);
@@ -811,10 +813,29 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream,
regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs),
FSL_SAI_CR3_TRCE_MASK, 0);

- if (!sai->is_consumer_mode[tx] &&
- sai->mclk_streams & BIT(substream->stream)) {
- clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
- sai->mclk_streams &= ~BIT(substream->stream);
+ if (!sai->is_consumer_mode[tx]) {
+ bool adir_active = !!(sai->mclk_streams & BIT(!substream->stream));
+ /*
+ * If opposite stream provides clocks for synchronous mode and
+ * it is inactive, Clear BYP and BCI
+ */
+ if (fsl_sai_dir_is_synced(sai, adir) && !adir_active)
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs),
+ FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0);
+ /*
+ * Clear BYP and BCI of current stream if either of:
+ * 1. current stream doesn't provide clocks for synchronous mode
+ * 2. current stream provides clocks for synchronous mode but no
+ * more stream is active.
+ */
+ if (!fsl_sai_dir_is_synced(sai, dir) || !adir_active)
+ regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs),
+ FSL_SAI_CR2_BCI | FSL_SAI_CR2_BYP, 0);
+
+ if (sai->mclk_streams & BIT(substream->stream)) {
+ clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]);
+ sai->mclk_streams &= ~BIT(substream->stream);
+ }
}

return 0;
diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c
index a4518fefad69..43438af1e1c6 100644
--- a/sound/soc/fsl/imx-card.c
+++ b/sound/soc/fsl/imx-card.c
@@ -496,11 +496,15 @@ static void imx_aif_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_dai *codec_dai;
int i;

- for_each_rtd_cpu_dais(rtd, i, cpu_dai)
- snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT);
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ if (!snd_soc_dai_active(cpu_dai))
+ snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT);
+ }

- for_each_rtd_codec_dais(rtd, i, codec_dai)
- snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN);
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (!snd_soc_dai_active(codec_dai))
+ snd_soc_dai_set_sysclk(codec_dai, 0, 0, SND_SOC_CLOCK_IN);
+ }
}

static const struct snd_soc_ops imx_aif_ops = {
diff --git a/sound/soc/meson/aiu-fifo-spdif.c b/sound/soc/meson/aiu-fifo-spdif.c
index e0e00ec026dc..a9861c5d6637 100644
--- a/sound/soc/meson/aiu-fifo-spdif.c
+++ b/sound/soc/meson/aiu-fifo-spdif.c
@@ -24,6 +24,7 @@
#define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7)
#define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8)
#define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0)
+#define AIU_RST_SOFT_958_FAST BIT(2)

#define AIU_FIFO_SPDIF_BLOCK 8

@@ -68,11 +69,15 @@ static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_soc_component_write(component, AIU_RST_SOFT,
+ AIU_RST_SOFT_958_FAST);
fifo_spdif_dcu_enable(component, true);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_STOP:
+ snd_soc_component_write(component, AIU_RST_SOFT,
+ AIU_RST_SOFT_958_FAST);
fifo_spdif_dcu_enable(component, false);
break;
default:
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index f4e22af594fa..93f511fb6190 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -2237,6 +2237,7 @@ static const struct sun4i_codec_quirks sun50i_h616_codec_quirks = {
.reg_dac_fifoc = REG_FIELD(SUN50I_H616_CODEC_DAC_FIFOC, 0, 31),
.reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA,
.has_reset = true,
+ .playback_only = true,
.dma_max_burst = SUN4I_DMA_MAX_BURST,
};

diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index fb37bb8ad9a9..dbcefcaf7c36 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1447,8 +1447,8 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
if (!cval->cmask) {
snd_usb_set_cur_mix_value(cval, 0, 0, cval->max);
} else {
+ idx = 0;
for (i = 0; i < MAX_CHANNELS; i++) {
- idx = 0;
if (cval->cmask & BIT(i)) {
snd_usb_set_cur_mix_value(cval, i + 1, idx, cval->max);
idx++;
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index ed66e2af9514..33eb8be48af8 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2457,6 +2457,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_DSD_RAW),
DEVICE_FLG(0x2708, 0x0002, /* Audient iD14 */
QUIRK_FLAG_IGNORE_CTL_ERROR),
+ DEVICE_FLG(0x2772, 0x0502, /* Musical Fidelity M6s DAC */
+ 0), /* for avoiding QUIRK_FLAG_DSD_RAW with vendor match */
DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x2a70, 0x1881, /* OnePlus Technology (Shenzhen) Co., Ltd. BE02T */
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 9b11cf3193b9..4bcff2272667 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -211,6 +211,7 @@ static bool is_rust_noreturn(const struct symbol *func)
str_ends_with(func->name, "_4core9panicking18panic_nounwind_fmt") ||
str_ends_with(func->name, "_4core9panicking19assert_failed_inner") ||
str_ends_with(func->name, "_4core9panicking30panic_null_pointer_dereference") ||
+ str_ends_with(func->name, "_4core9panicking32panic_null_reference_constructed") ||
str_ends_with(func->name, "_4core9panicking36panic_misaligned_pointer_dereference") ||
str_ends_with(func->name, "_7___rustc17rust_begin_unwind") ||
strstr(func->name, "_4core9panicking13assert_failed") ||
diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c
index d4f845c32804..c5ae2a026ce9 100644
--- a/tools/testing/selftests/alsa/mixer-test.c
+++ b/tools/testing/selftests/alsa/mixer-test.c
@@ -84,6 +84,7 @@ static void find_controls(void)
if (err < 0) {
ksft_print_msg("Failed to get hctl for card %d: %s\n",
card, snd_strerror(err));
+ free(card_data);
goto next_card;
}

diff --git a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
index e2767d27d8aa..166193659870 100644
--- a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
+++ b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
@@ -70,17 +70,45 @@ __naked void bpf_map_ptr_write_rejected(void)
: __clobber_all);
}

-/* The first element of struct bpf_map is a SHA256 hash of 32 bytes, accessing
- * into this array is valid. The opts field is now at offset 33.
+/*
+ * struct bpf_map starts with the SHA256 hash sha[32] at offset 0 (a readable
+ * byte array), the u32 excl field at offset 32, and the ops pointer at offset
+ * 40. Reading a u32 at offset 41 reaches into the middle of the ops pointer,
+ * i.e. a partial pointer access, which is rejected.
*/
SEC("socket")
__description("bpf_map_ptr: read non-existent field rejected")
__failure
-__msg("cannot access ptr member ops with moff 32 in struct bpf_map with off 33 size 4")
+__msg("cannot access ptr member ops with moff 40 in struct bpf_map with off 41 size 4")
__failure_unpriv
__msg_unpriv("access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
__flag(BPF_F_ANY_ALIGNMENT)
__naked void read_non_existent_field_rejected(void)
+{
+ asm volatile (" \
+ r6 = 0; \
+ r1 = %[map_array_48b] ll; \
+ r6 = *(u32*)(r1 + 41); \
+ r0 = 1; \
+ exit; \
+" :
+ : __imm_addr(map_array_48b)
+ : __clobber_all);
+}
+
+/*
+ * The u32 excl field spans offsets 32..35 (mend 36). Reading a u32 at offset
+ * 33 starts inside excl but extends past its end, which the verifier rejects
+ * as an out-of-bounds scalar access.
+ */
+SEC("socket")
+__description("bpf_map_ptr: read beyond excl field rejected")
+__failure
+__msg("access beyond the end of member excl (mend:36) in struct bpf_map with off 33 size 4")
+__failure_unpriv
+__msg_unpriv("access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
+__flag(BPF_F_ANY_ALIGNMENT)
+__naked void read_beyond_excl_field_rejected(void)
{
asm volatile (" \
r6 = 0; \
@@ -103,7 +131,7 @@ __naked void ptr_read_ops_field_accepted(void)
asm volatile (" \
r6 = 0; \
r1 = %[map_array_48b] ll; \
- r6 = *(u64*)(r1 + 0); \
+ r6 = *(u64*)(r1 + 40); \
r0 = 1; \
exit; \
" :
diff --git a/tools/testing/selftests/drivers/net/.gitignore b/tools/testing/selftests/drivers/net/.gitignore
index 585ecb4d5dc4..e5314ce4bb2d 100644
--- a/tools/testing/selftests/drivers/net/.gitignore
+++ b/tools/testing/selftests/drivers/net/.gitignore
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
napi_id_helper
psp_responder
+so_txtime
diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile
index b72080c6d06b..d5bf4cb638a8 100644
--- a/tools/testing/selftests/drivers/net/Makefile
+++ b/tools/testing/selftests/drivers/net/Makefile
@@ -7,6 +7,7 @@ TEST_INCLUDES := $(wildcard lib/py/*.py) \

TEST_GEN_FILES := \
napi_id_helper \
+ so_txtime \
# end of TEST_GEN_FILES

TEST_PROGS := \
@@ -21,6 +22,7 @@ TEST_PROGS := \
queues.py \
ring_reconfig.py \
shaper.py \
+ so_txtime.py \
stats.py \
xdp.py \
# end of TEST_PROGS
diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config
index fd16994366f4..4df3f6eef2cb 100644
--- a/tools/testing/selftests/drivers/net/config
+++ b/tools/testing/selftests/drivers/net/config
@@ -4,9 +4,14 @@ CONFIG_DEBUG_INFO_BTF_MODULES=n
CONFIG_INET_PSP=y
CONFIG_IPV6=y
CONFIG_MACSEC=m
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_CLS_BPF=y
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_NETCONSOLE_EXTENDED_LOG=y
CONFIG_NETDEVSIM=m
+CONFIG_NET_SCH_ETF=m
+CONFIG_NET_SCH_FQ=m
+CONFIG_NET_SCH_INGRESS=y
CONFIG_VLAN_8021Q=m
CONFIG_XDP_SOCKETS=y
diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh
index cb59cf436dd0..d9111f2102bc 100755
--- a/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh
+++ b/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh
@@ -44,7 +44,8 @@ function trigger_reactivation() {
# Restore MACs
ip netns exec "${NAMESPACE}" ip link set "${DSTIF}" \
address "${SAVED_DSTMAC}"
- if [ "${BINDMODE}" == "mac" ]; then
+ if [ "${BINDMODE}" == "mac" ] &&
+ [ "$(mac_get "${SRCIF}")" != "${SAVED_SRCMAC}" ]; then
ip link set dev "${SRCIF}" down
ip link set dev "${SRCIF}" address "${SAVED_SRCMAC}"
# Rename device in order to trigger target resume, as initial
diff --git a/tools/testing/selftests/drivers/net/settings b/tools/testing/selftests/drivers/net/settings
new file mode 100644
index 000000000000..eef533824a3c
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/settings
@@ -0,0 +1 @@
+timeout=360
diff --git a/tools/testing/selftests/drivers/net/so_txtime.c b/tools/testing/selftests/drivers/net/so_txtime.c
new file mode 100644
index 000000000000..55a386f3d1b9
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/so_txtime.c
@@ -0,0 +1,538 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test the SO_TXTIME API
+ *
+ * Takes a stream of { payload, delivery time }[], to be sent across two
+ * processes. Start this program on two separate network namespaces or
+ * connected hosts, one instance in transmit mode and the other in receive
+ * mode using the '-r' option. Receiver will compare arrival timestamps to
+ * the expected stream. Sender will read transmit timestamps from the error
+ * queue. The streams can differ due to out-of-order delivery and drops.
+ */
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <error.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/net_tstamp.h>
+#include <linux/errqueue.h>
+#include <linux/if_ether.h>
+#include <linux/ipv6.h>
+#include <linux/udp.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include "kselftest.h"
+
+static int cfg_clockid = CLOCK_TAI;
+static uint16_t cfg_port = 8000;
+static int cfg_variance_us = 8000;
+static bool cfg_machine_slow;
+static uint64_t cfg_start_time_ns;
+static int cfg_mark;
+static bool cfg_rx;
+
+static uint64_t glob_tstart;
+static uint64_t tdeliver_max;
+
+static int errors;
+
+/* encode one timed transmission (of a 1B payload) */
+struct timed_send {
+ char data;
+ int64_t delay_us;
+};
+
+#define MAX_NUM_PKT 8
+static struct timed_send cfg_buf[MAX_NUM_PKT];
+static int cfg_num_pkt;
+
+static int cfg_errq_level;
+static int cfg_errq_type;
+
+static struct sockaddr_storage cfg_dst_addr;
+static struct sockaddr_storage cfg_src_addr;
+static socklen_t cfg_alen;
+
+static uint64_t gettime_ns(clockid_t clock)
+{
+ struct timespec ts;
+
+ if (clock_gettime(clock, &ts))
+ error(1, errno, "gettime");
+
+ return ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
+}
+
+static void do_send_one(int fdt, struct timed_send *ts)
+{
+ char control[CMSG_SPACE(sizeof(uint64_t))];
+ struct msghdr msg = {0};
+ struct iovec iov = {0};
+ struct cmsghdr *cm;
+ uint64_t tdeliver;
+ int ret;
+
+ iov.iov_base = &ts->data;
+ iov.iov_len = 1;
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = (struct sockaddr *)&cfg_dst_addr;
+ msg.msg_namelen = cfg_alen;
+
+ if (ts->delay_us >= 0) {
+ memset(control, 0, sizeof(control));
+ msg.msg_control = &control;
+ msg.msg_controllen = sizeof(control);
+
+ tdeliver = glob_tstart + ts->delay_us * 1000;
+ tdeliver_max = tdeliver_max > tdeliver ?
+ tdeliver_max : tdeliver;
+
+ cm = CMSG_FIRSTHDR(&msg);
+ cm->cmsg_level = SOL_SOCKET;
+ cm->cmsg_type = SCM_TXTIME;
+ cm->cmsg_len = CMSG_LEN(sizeof(tdeliver));
+ memcpy(CMSG_DATA(cm), &tdeliver, sizeof(tdeliver));
+ }
+
+ ret = sendmsg(fdt, &msg, 0);
+ if (ret == -1)
+ error(1, errno, "write");
+ if (ret == 0)
+ error(1, 0, "write: 0B");
+
+}
+
+static void do_recv_one(int fdr, struct timed_send *ts)
+{
+ int64_t tstop, texpect;
+ char rbuf[2];
+ int ret;
+
+ ret = recv(fdr, rbuf, sizeof(rbuf), 0);
+ if (ret == -1 && errno == EAGAIN)
+ error(1, EAGAIN, "recv: timeout");
+ if (ret == -1)
+ error(1, errno, "read");
+ if (ret != 1)
+ error(1, 0, "read: %dB", ret);
+
+ tstop = (gettime_ns(cfg_clockid) - glob_tstart) / 1000;
+ texpect = ts->delay_us >= 0 ? ts->delay_us : 0;
+
+ fprintf(stderr, "payload:%c delay:%lld expected:%lld (us)\n",
+ rbuf[0], (long long)tstop, (long long)texpect);
+
+ if (rbuf[0] != ts->data) {
+ fprintf(stderr, "payload mismatch. expected %c\n", ts->data);
+ errors++;
+ }
+
+ if (llabs(tstop - texpect) > cfg_variance_us) {
+ fprintf(stderr, "exceeds variance (%d us)\n", cfg_variance_us);
+ if (!cfg_machine_slow)
+ errors++;
+ }
+}
+
+static void do_recv_verify_empty(int fdr)
+{
+ char rbuf[1];
+ int ret;
+
+ ret = recv(fdr, rbuf, sizeof(rbuf), 0);
+ if (ret != -1 || errno != EAGAIN)
+ error(1, 0, "recv: not empty as expected (%d, %d)", ret, errno);
+}
+
+static int do_recv_errqueue_timeout(int fdt)
+{
+ char control[CMSG_SPACE(sizeof(struct sock_extended_err)) +
+ CMSG_SPACE(sizeof(struct sockaddr_in6))] = {0};
+ char data[sizeof(struct ethhdr) + sizeof(struct ipv6hdr) +
+ sizeof(struct udphdr) + 1];
+ struct sock_extended_err *err;
+ int ret, num_tstamp = 0;
+ struct msghdr msg = {0};
+ struct iovec iov = {0};
+ struct cmsghdr *cm;
+ int64_t tstamp = 0;
+
+ iov.iov_base = data;
+ iov.iov_len = sizeof(data);
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ while (1) {
+ const char *reason = NULL;
+
+ ret = recvmsg(fdt, &msg, MSG_ERRQUEUE);
+ if (ret == -1 && errno == EAGAIN)
+ break;
+ if (ret == -1)
+ error(1, errno, "errqueue");
+ if (msg.msg_flags != MSG_ERRQUEUE)
+ error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags);
+
+ cm = CMSG_FIRSTHDR(&msg);
+ if (cm->cmsg_level != cfg_errq_level ||
+ cm->cmsg_type != cfg_errq_type)
+ error(1, 0, "errqueue: type 0x%x.0x%x\n",
+ cm->cmsg_level, cm->cmsg_type);
+
+ err = (struct sock_extended_err *)CMSG_DATA(cm);
+ if (err->ee_origin != SO_EE_ORIGIN_TXTIME)
+ error(1, 0, "errqueue: origin 0x%x\n", err->ee_origin);
+
+ switch (err->ee_errno) {
+ case ECANCELED:
+ if (err->ee_code != SO_EE_CODE_TXTIME_MISSED)
+ error(1, 0, "errqueue: unknown ECANCELED %u\n",
+ err->ee_code);
+ reason = "missed txtime";
+ break;
+ case EINVAL:
+ if (err->ee_code != SO_EE_CODE_TXTIME_INVALID_PARAM)
+ error(1, 0, "errqueue: unknown EINVAL %u\n",
+ err->ee_code);
+ reason = "invalid txtime";
+ break;
+ default:
+ error(1, 0, "errqueue: errno %u code %u\n",
+ err->ee_errno, err->ee_code);
+ }
+
+ tstamp = ((int64_t) err->ee_data) << 32 | err->ee_info;
+ tstamp -= (int64_t) glob_tstart;
+ tstamp /= 1000 * 1000;
+ fprintf(stderr, "send: pkt %c at %" PRId64 "ms dropped: %s\n",
+ data[ret - 1], tstamp, reason);
+
+ msg.msg_flags = 0;
+ msg.msg_controllen = sizeof(control);
+ num_tstamp++;
+ }
+
+ return num_tstamp;
+}
+
+static void recv_errqueue_msgs(int fdt)
+{
+ struct pollfd pfd = { .fd = fdt, .events = POLLERR };
+ const int timeout_ms = 10;
+ int ret, num_tstamp = 0;
+
+ do {
+ ret = poll(&pfd, 1, timeout_ms);
+ if (ret == -1)
+ error(1, errno, "poll");
+
+ if (ret && (pfd.revents & POLLERR))
+ num_tstamp += do_recv_errqueue_timeout(fdt);
+
+ if (num_tstamp == cfg_num_pkt)
+ break;
+
+ } while (gettime_ns(cfg_clockid) < tdeliver_max);
+}
+
+static void start_time_wait(void)
+{
+ uint64_t now;
+ int err;
+
+ if (!cfg_start_time_ns)
+ return;
+
+ now = gettime_ns(CLOCK_REALTIME);
+ if (cfg_start_time_ns < now) {
+ fprintf(stderr, "FAIL: start time already passed\n");
+ if (!cfg_machine_slow)
+ errors++;
+ return;
+ }
+
+ err = usleep((cfg_start_time_ns - now) / 1000);
+ if (err)
+ error(1, errno, "usleep");
+}
+
+static void setsockopt_txtime(int fd)
+{
+ struct sock_txtime so_txtime_val = { .clockid = cfg_clockid };
+ struct sock_txtime so_txtime_val_read = { 0 };
+ socklen_t vallen = sizeof(so_txtime_val);
+
+ so_txtime_val.flags = SOF_TXTIME_REPORT_ERRORS;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_TXTIME,
+ &so_txtime_val, sizeof(so_txtime_val)))
+ error(1, errno, "setsockopt txtime");
+
+ if (getsockopt(fd, SOL_SOCKET, SO_TXTIME,
+ &so_txtime_val_read, &vallen))
+ error(1, errno, "getsockopt txtime");
+
+ if (vallen != sizeof(so_txtime_val) ||
+ memcmp(&so_txtime_val, &so_txtime_val_read, vallen))
+ error(1, 0, "getsockopt txtime: mismatch");
+}
+
+static int setup_tx(struct sockaddr *addr, socklen_t alen)
+{
+ int fd;
+
+ fd = socket(addr->sa_family, SOCK_DGRAM, 0);
+ if (fd == -1)
+ error(1, errno, "socket t");
+
+ if (connect(fd, addr, alen))
+ error(1, errno, "connect");
+
+ setsockopt_txtime(fd);
+
+ if (cfg_mark &&
+ setsockopt(fd, SOL_SOCKET, SO_MARK, &cfg_mark, sizeof(cfg_mark)))
+ error(1, errno, "setsockopt mark");
+
+ return fd;
+}
+
+static int setup_rx(struct sockaddr *addr, socklen_t alen)
+{
+ struct timeval tv = { .tv_usec = 100 * 1000 };
+ int fd;
+
+ fd = socket(addr->sa_family, SOCK_DGRAM, 0);
+ if (fd == -1)
+ error(1, errno, "socket r");
+
+ if (bind(fd, addr, alen))
+ error(1, errno, "bind");
+
+ if (cfg_machine_slow)
+ tv.tv_sec = 2;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
+ error(1, errno, "setsockopt rcv timeout");
+
+ return fd;
+}
+
+static void do_test_tx(struct sockaddr *addr, socklen_t alen)
+{
+ int fdt, i;
+
+ fprintf(stderr, "\nSO_TXTIME ipv%c clock %s\n",
+ addr->sa_family == PF_INET ? '4' : '6',
+ cfg_clockid == CLOCK_TAI ? "tai" : "monotonic");
+
+ fdt = setup_tx(addr, alen);
+
+ start_time_wait();
+ glob_tstart = gettime_ns(cfg_clockid);
+
+ for (i = 0; i < cfg_num_pkt; i++)
+ do_send_one(fdt, &cfg_buf[i]);
+
+ recv_errqueue_msgs(fdt);
+
+ if (close(fdt))
+ error(1, errno, "close t");
+}
+
+static void do_test_rx(struct sockaddr *addr, socklen_t alen)
+{
+ int fdr, i;
+
+ fdr = setup_rx(addr, alen);
+
+ start_time_wait();
+ glob_tstart = gettime_ns(cfg_clockid);
+
+ for (i = 0; i < cfg_num_pkt; i++)
+ do_recv_one(fdr, &cfg_buf[i]);
+
+ do_recv_verify_empty(fdr);
+
+ if (close(fdr))
+ error(1, errno, "close r");
+}
+
+static void setup_sockaddr(int domain, const char *str_addr,
+ struct sockaddr_storage *sockaddr)
+{
+ struct sockaddr_in6 *addr6 = (void *) sockaddr;
+ struct sockaddr_in *addr4 = (void *) sockaddr;
+
+ switch (domain) {
+ case PF_INET:
+ memset(addr4, 0, sizeof(*addr4));
+ addr4->sin_family = AF_INET;
+ addr4->sin_port = htons(cfg_port);
+ if (str_addr &&
+ inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
+ error(1, 0, "ipv4 parse error: %s", str_addr);
+ break;
+ case PF_INET6:
+ memset(addr6, 0, sizeof(*addr6));
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_port = htons(cfg_port);
+ if (str_addr &&
+ inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
+ error(1, 0, "ipv6 parse error: %s", str_addr);
+ break;
+ }
+}
+
+static int parse_io(const char *optarg, struct timed_send *array)
+{
+ char *arg, *tok;
+ int aoff = 0;
+
+ arg = strdup(optarg);
+ if (!arg)
+ error(1, errno, "strdup");
+
+ while ((tok = strtok(arg, ","))) {
+ arg = NULL; /* only pass non-zero on first call */
+
+ if (aoff / 2 == MAX_NUM_PKT)
+ error(1, 0, "exceeds max pkt count (%d)", MAX_NUM_PKT);
+
+ if (aoff & 1) { /* parse delay */
+ array->delay_us = strtol(tok, NULL, 0) * 1000;
+ array++;
+ } else { /* parse character */
+ array->data = tok[0];
+ }
+
+ aoff++;
+ }
+
+ free(arg);
+
+ return aoff / 2;
+}
+
+static void usage(const char *progname)
+{
+ fprintf(stderr, "\nUsage: %s [options] <payload>\n"
+ "Options:\n"
+ " -4 only IPv4\n"
+ " -6 only IPv6\n"
+ " -c <clock> monotonic or tai (default)\n"
+ " -D <addr> destination IP address (server)\n"
+ " -S <addr> source IP address (client)\n"
+ " -r run rx mode\n"
+ " -t <nsec> start time (UTC nanoseconds)\n"
+ " -m <mark> socket mark\n"
+ "\n",
+ progname);
+ exit(1);
+}
+
+static void parse_opts(int argc, char **argv)
+{
+ char *daddr = NULL, *saddr = NULL;
+ int domain = PF_UNSPEC;
+ int c;
+
+ while ((c = getopt(argc, argv, "46c:S:D:rt:m:")) != -1) {
+ switch (c) {
+ case '4':
+ if (domain != PF_UNSPEC)
+ error(1, 0, "Pass one of -4 or -6");
+ domain = PF_INET;
+ cfg_alen = sizeof(struct sockaddr_in);
+ cfg_errq_level = SOL_IP;
+ cfg_errq_type = IP_RECVERR;
+ break;
+ case '6':
+ if (domain != PF_UNSPEC)
+ error(1, 0, "Pass one of -4 or -6");
+ domain = PF_INET6;
+ cfg_alen = sizeof(struct sockaddr_in6);
+ cfg_errq_level = SOL_IPV6;
+ cfg_errq_type = IPV6_RECVERR;
+ break;
+ case 'c':
+ if (!strcmp(optarg, "tai"))
+ cfg_clockid = CLOCK_TAI;
+ else if (!strcmp(optarg, "monotonic") ||
+ !strcmp(optarg, "mono"))
+ cfg_clockid = CLOCK_MONOTONIC;
+ else
+ error(1, 0, "unknown clock id %s", optarg);
+ break;
+ case 'S':
+ saddr = optarg;
+ break;
+ case 'D':
+ daddr = optarg;
+ break;
+ case 'r':
+ cfg_rx = true;
+ break;
+ case 't':
+ cfg_start_time_ns = strtoll(optarg, NULL, 0);
+ break;
+ case 'm':
+ cfg_mark = strtol(optarg, NULL, 0);
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+
+ if (argc - optind != 1)
+ usage(argv[0]);
+
+ if (domain == PF_UNSPEC)
+ error(1, 0, "Pass one of -4 or -6");
+ if (!daddr)
+ error(1, 0, "-D <server addr> required\n");
+ if (!cfg_rx && !saddr)
+ error(1, 0, "-S <client addr> required\n");
+
+ setup_sockaddr(domain, daddr, &cfg_dst_addr);
+ setup_sockaddr(domain, saddr, &cfg_src_addr);
+
+ cfg_num_pkt = parse_io(argv[optind], cfg_buf);
+
+ cfg_machine_slow = getenv("KSFT_MACHINE_SLOW");
+}
+
+int main(int argc, char **argv)
+{
+ parse_opts(argc, argv);
+
+ if (cfg_rx)
+ do_test_rx((void *)&cfg_dst_addr, cfg_alen);
+ else
+ do_test_tx((void *)&cfg_src_addr, cfg_alen);
+
+ if (errors) {
+ fprintf(stderr, "FAIL: %d errors\n", errors);
+ return KSFT_FAIL;
+ }
+
+ return KSFT_PASS;
+}
diff --git a/tools/testing/selftests/drivers/net/so_txtime.py b/tools/testing/selftests/drivers/net/so_txtime.py
new file mode 100755
index 000000000000..e7de8fe22c1e
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/so_txtime.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+"""Regression tests for the SO_TXTIME interface.
+
+Test delivery time in FQ and ETF qdiscs.
+"""
+
+import os
+import time
+
+from lib.py import ksft_exit, ksft_run, ksft_variants
+from lib.py import KsftNamedVariant, KsftSkipEx
+from lib.py import NetDrvEpEnv, bkg, cmd, defer, tc
+
+
+def test_so_txtime(cfg, clockid, ipver, args_tx, args_rx, expect_success):
+ """Main function. Run so_txtime as sender and receiver."""
+ slow_machine = os.environ.get('KSFT_MACHINE_SLOW')
+
+ bin_path = cfg.test_dir / "so_txtime"
+
+ tstart = time.time_ns() + (2000_000_000 if slow_machine else 200_000_000)
+
+ cmd_addr = f"-S {cfg.addr_v[ipver]} -D {cfg.remote_addr_v[ipver]}"
+ cmd_base = f"{bin_path} -{ipver} -c {clockid} -t {tstart} {cmd_addr}"
+ cmd_rx = f"{cmd_base} {args_rx} -r"
+ cmd_tx = f"{cmd_base} {args_tx}"
+
+ expect_fail = not expect_success
+ if slow_machine:
+ expect_success = False
+
+ with bkg(cmd_rx, host=cfg.remote, fail=expect_success,
+ expect_fail=expect_fail, exit_wait=True):
+ cmd(cmd_tx)
+
+
+def _qdisc_setup(ifname, qdisc, optargs=""):
+ """Replace root qdisc. Restore the original after the test.
+
+ If the original is mq, children will be of type default_qdisc.
+ """
+ orig = tc(f"qdisc show dev {ifname} root", json=True)[0].get("kind", None)
+ defer(tc, f"qdisc replace dev {ifname} root {orig}")
+ tc(f"qdisc replace dev {ifname} root {qdisc} {optargs}")
+
+
+def _test_variants_mono():
+ for ipver in ["4", "6"]:
+ for testcase in [
+ ["no_delay", "a,-1", "a,-1"],
+ ["zero_delay", "a,0", "a,0"],
+ ["one_pkt", "a,10", "a,10"],
+ ["in_order", "a,10,b,20", "a,10,b,20"],
+ ["reverse_order", "a,20,b,10", "b,20,a,20"],
+ ]:
+ name = f"v{ipver}_{testcase[0]}"
+ yield KsftNamedVariant(name, ipver, testcase[1], testcase[2])
+
+
+@ksft_variants(_test_variants_mono())
+def test_so_txtime_mono(cfg, ipver, args_tx, args_rx):
+ """Run all variants of monotonic (fq) tests."""
+ _qdisc_setup(cfg.ifname, "fq")
+ test_so_txtime(cfg, "mono", ipver, args_tx, args_rx, True)
+
+
+def _test_variants_etf():
+ for ipver in ["4", "6"]:
+ for testcase in [
+ ["no_delay", "a,-1", "a,-1", False],
+ ["zero_delay", "a,0", "a,0", False],
+ ["one_pkt", "a,10", "a,10", True],
+ ["in_order", "a,10,b,20", "a,10,b,20", True],
+ ["reverse_order", "a,20,b,10", "b,10,a,20", True],
+ ]:
+ name = f"v{ipver}_{testcase[0]}"
+ yield KsftNamedVariant(
+ name, ipver, testcase[1], testcase[2], testcase[3]
+ )
+
+
+@ksft_variants(_test_variants_etf())
+def test_so_txtime_etf(cfg, ipver, args_tx, args_rx, expect_fail):
+ """Run all variants of etf tests."""
+ try:
+ _qdisc_setup(cfg.ifname, "etf", "clockid CLOCK_TAI delta 400000")
+ except Exception as e:
+ raise KsftSkipEx("tc does not support qdisc etf. skipping") from e
+
+ test_so_txtime(cfg, "tai", ipver, args_tx, args_rx, expect_fail)
+
+
+def main() -> None:
+ """Boilerplate ksft main."""
+ with NetDrvEpEnv(__file__) as cfg:
+ ksft_run([test_so_txtime_mono, test_so_txtime_etf], args=(cfg,))
+ ksft_exit()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/testing/selftests/drivers/ntsync/config b/tools/testing/selftests/drivers/ntsync/config
index 60539c826d06..0aa68de147af 100644
--- a/tools/testing/selftests/drivers/ntsync/config
+++ b/tools/testing/selftests/drivers/ntsync/config
@@ -1 +1 @@
-CONFIG_WINESYNC=y
+CONFIG_NTSYNC=y
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index 97ad4d551d44..02ad4c99a2b4 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -40,7 +40,6 @@ skf_net_off
socket
so_incoming_cpu
so_netns_cookie
-so_txtime
so_rcv_listener
stress_reuseport_listen
tap
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 2ed7d803eb54..2e4ff5b34f4c 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -83,7 +83,6 @@ TEST_PROGS := \
rxtimestamp.sh \
sctp_vrf.sh \
skf_net_off.sh \
- so_txtime.sh \
srv6_end_dt46_l3vpn_test.sh \
srv6_end_dt4_l3vpn_test.sh \
srv6_end_dt6_l3vpn_test.sh \
@@ -159,7 +158,6 @@ TEST_GEN_FILES := \
skf_net_off \
so_netns_cookie \
so_rcv_listener \
- so_txtime \
socket \
stress_reuseport_listen \
tcp_fastopen_backup_key \
diff --git a/tools/testing/selftests/net/af_unix/config b/tools/testing/selftests/net/af_unix/config
index b5429c15a53c..41dbb03c747e 100644
--- a/tools/testing/selftests/net/af_unix/config
+++ b/tools/testing/selftests/net/af_unix/config
@@ -1,3 +1,4 @@
CONFIG_AF_UNIX_OOB=y
CONFIG_UNIX=y
CONFIG_UNIX_DIAG=m
+CONFIG_USER_NS=y
diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh
index e9ae1806ab07..30a809752d1b 100755
--- a/tools/testing/selftests/net/mptcp/userspace_pm.sh
+++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh
@@ -212,7 +212,7 @@ make_connection()
./mptcp_connect -s MPTCP -w 300 -p $app_port -l $listen_addr > /dev/null 2>&1 &
local server_pid=$!

- mptcp_lib_wait_local_port_listen "${ns1}" "${port}"
+ mptcp_lib_wait_local_port_listen "${ns1}" "${app_port}"

# Run the client, transfer $file and stay connected to the server
# to conduct tests
diff --git a/tools/testing/selftests/net/openvswitch/config b/tools/testing/selftests/net/openvswitch/config
new file mode 100644
index 000000000000..c659749cd086
--- /dev/null
+++ b/tools/testing/selftests/net/openvswitch/config
@@ -0,0 +1,16 @@
+CONFIG_GENEVE=m
+CONFIG_INET_DIAG=y
+CONFIG_IPV6=y
+CONFIG_NETFILTER=y
+CONFIG_NET_IPGRE=m
+CONFIG_NET_IPGRE_DEMUX=m
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_CONNTRACK_OVS=y
+CONFIG_OPENVSWITCH=m
+CONFIG_OPENVSWITCH_GENEVE=m
+CONFIG_OPENVSWITCH_GRE=m
+CONFIG_OPENVSWITCH_VXLAN=m
+CONFIG_PSAMPLE=m
+CONFIG_VETH=y
+CONFIG_VLAN_8021Q=y
+CONFIG_VXLAN=m
diff --git a/tools/testing/selftests/net/ovpn/config b/tools/testing/selftests/net/ovpn/config
index d6cf033d555e..6b424762e46e 100644
--- a/tools/testing/selftests/net/ovpn/config
+++ b/tools/testing/selftests/net/ovpn/config
@@ -4,6 +4,7 @@ CONFIG_CRYPTO_CHACHA20POLY1305=y
CONFIG_CRYPTO_GCM=y
CONFIG_DST_CACHE=y
CONFIG_INET=y
+CONFIG_IPV6=y
CONFIG_NET=y
CONFIG_NETFILTER=y
CONFIG_NET_UDP_TUNNEL=y
@@ -11,3 +12,4 @@ CONFIG_NF_TABLES=m
CONFIG_NF_TABLES_INET=y
CONFIG_OVPN=m
CONFIG_STREAM_PARSER=y
+CONFIG_VETH=y
diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c
index d40953375c86..f4effa7580c0 100644
--- a/tools/testing/selftests/net/ovpn/ovpn-cli.c
+++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c
@@ -1785,7 +1785,7 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host,
const char *service, const char *vpnip)
{
int ret;
- struct addrinfo *result;
+ struct addrinfo *result = NULL;
struct addrinfo hints = {
.ai_family = ovpn->sa_family,
.ai_socktype = SOCK_DGRAM,
@@ -1809,6 +1809,8 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host,
}

memcpy(&ovpn->remote, result->ai_addr, result->ai_addrlen);
+ freeaddrinfo(result);
+ result = NULL;
}

if (vpnip) {
diff --git a/tools/testing/selftests/net/ovpn/settings b/tools/testing/selftests/net/ovpn/settings
new file mode 100644
index 000000000000..ba4d85f74cd6
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/settings
@@ -0,0 +1 @@
+timeout=90
diff --git a/tools/testing/selftests/net/so_txtime.c b/tools/testing/selftests/net/so_txtime.c
deleted file mode 100644
index b76df1efc2ef..000000000000
--- a/tools/testing/selftests/net/so_txtime.c
+++ /dev/null
@@ -1,517 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Test the SO_TXTIME API
- *
- * Takes a stream of { payload, delivery time }[], to be sent across two
- * processes. Start this program on two separate network namespaces or
- * connected hosts, one instance in transmit mode and the other in receive
- * mode using the '-r' option. Receiver will compare arrival timestamps to
- * the expected stream. Sender will read transmit timestamps from the error
- * queue. The streams can differ due to out-of-order delivery and drops.
- */
-
-#define _GNU_SOURCE
-
-#include <arpa/inet.h>
-#include <error.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <linux/net_tstamp.h>
-#include <linux/errqueue.h>
-#include <linux/if_ether.h>
-#include <linux/ipv6.h>
-#include <linux/udp.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-#include <poll.h>
-
-static int cfg_clockid = CLOCK_TAI;
-static uint16_t cfg_port = 8000;
-static int cfg_variance_us = 4000;
-static uint64_t cfg_start_time_ns;
-static int cfg_mark;
-static bool cfg_rx;
-
-static uint64_t glob_tstart;
-static uint64_t tdeliver_max;
-
-/* encode one timed transmission (of a 1B payload) */
-struct timed_send {
- char data;
- int64_t delay_us;
-};
-
-#define MAX_NUM_PKT 8
-static struct timed_send cfg_buf[MAX_NUM_PKT];
-static int cfg_num_pkt;
-
-static int cfg_errq_level;
-static int cfg_errq_type;
-
-static struct sockaddr_storage cfg_dst_addr;
-static struct sockaddr_storage cfg_src_addr;
-static socklen_t cfg_alen;
-
-static uint64_t gettime_ns(clockid_t clock)
-{
- struct timespec ts;
-
- if (clock_gettime(clock, &ts))
- error(1, errno, "gettime");
-
- return ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
-}
-
-static void do_send_one(int fdt, struct timed_send *ts)
-{
- char control[CMSG_SPACE(sizeof(uint64_t))];
- struct msghdr msg = {0};
- struct iovec iov = {0};
- struct cmsghdr *cm;
- uint64_t tdeliver;
- int ret;
-
- iov.iov_base = &ts->data;
- iov.iov_len = 1;
-
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_name = (struct sockaddr *)&cfg_dst_addr;
- msg.msg_namelen = cfg_alen;
-
- if (ts->delay_us >= 0) {
- memset(control, 0, sizeof(control));
- msg.msg_control = &control;
- msg.msg_controllen = sizeof(control);
-
- tdeliver = glob_tstart + ts->delay_us * 1000;
- tdeliver_max = tdeliver_max > tdeliver ?
- tdeliver_max : tdeliver;
-
- cm = CMSG_FIRSTHDR(&msg);
- cm->cmsg_level = SOL_SOCKET;
- cm->cmsg_type = SCM_TXTIME;
- cm->cmsg_len = CMSG_LEN(sizeof(tdeliver));
- memcpy(CMSG_DATA(cm), &tdeliver, sizeof(tdeliver));
- }
-
- ret = sendmsg(fdt, &msg, 0);
- if (ret == -1)
- error(1, errno, "write");
- if (ret == 0)
- error(1, 0, "write: 0B");
-
-}
-
-static void do_recv_one(int fdr, struct timed_send *ts)
-{
- int64_t tstop, texpect;
- char rbuf[2];
- int ret;
-
- ret = recv(fdr, rbuf, sizeof(rbuf), 0);
- if (ret == -1 && errno == EAGAIN)
- error(1, EAGAIN, "recv: timeout");
- if (ret == -1)
- error(1, errno, "read");
- if (ret != 1)
- error(1, 0, "read: %dB", ret);
-
- tstop = (gettime_ns(cfg_clockid) - glob_tstart) / 1000;
- texpect = ts->delay_us >= 0 ? ts->delay_us : 0;
-
- fprintf(stderr, "payload:%c delay:%lld expected:%lld (us)\n",
- rbuf[0], (long long)tstop, (long long)texpect);
-
- if (rbuf[0] != ts->data)
- error(1, 0, "payload mismatch. expected %c", ts->data);
-
- if (llabs(tstop - texpect) > cfg_variance_us) {
- fprintf(stderr, "exceeds variance (%d us)\n", cfg_variance_us);
- if (!getenv("KSFT_MACHINE_SLOW"))
- exit(1);
- }
-}
-
-static void do_recv_verify_empty(int fdr)
-{
- char rbuf[1];
- int ret;
-
- ret = recv(fdr, rbuf, sizeof(rbuf), 0);
- if (ret != -1 || errno != EAGAIN)
- error(1, 0, "recv: not empty as expected (%d, %d)", ret, errno);
-}
-
-static int do_recv_errqueue_timeout(int fdt)
-{
- char control[CMSG_SPACE(sizeof(struct sock_extended_err)) +
- CMSG_SPACE(sizeof(struct sockaddr_in6))] = {0};
- char data[sizeof(struct ethhdr) + sizeof(struct ipv6hdr) +
- sizeof(struct udphdr) + 1];
- struct sock_extended_err *err;
- int ret, num_tstamp = 0;
- struct msghdr msg = {0};
- struct iovec iov = {0};
- struct cmsghdr *cm;
- int64_t tstamp = 0;
-
- iov.iov_base = data;
- iov.iov_len = sizeof(data);
-
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
-
- while (1) {
- const char *reason = NULL;
-
- ret = recvmsg(fdt, &msg, MSG_ERRQUEUE);
- if (ret == -1 && errno == EAGAIN)
- break;
- if (ret == -1)
- error(1, errno, "errqueue");
- if (msg.msg_flags != MSG_ERRQUEUE)
- error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags);
-
- cm = CMSG_FIRSTHDR(&msg);
- if (cm->cmsg_level != cfg_errq_level ||
- cm->cmsg_type != cfg_errq_type)
- error(1, 0, "errqueue: type 0x%x.0x%x\n",
- cm->cmsg_level, cm->cmsg_type);
-
- err = (struct sock_extended_err *)CMSG_DATA(cm);
- if (err->ee_origin != SO_EE_ORIGIN_TXTIME)
- error(1, 0, "errqueue: origin 0x%x\n", err->ee_origin);
-
- switch (err->ee_errno) {
- case ECANCELED:
- if (err->ee_code != SO_EE_CODE_TXTIME_MISSED)
- error(1, 0, "errqueue: unknown ECANCELED %u\n",
- err->ee_code);
- reason = "missed txtime";
- break;
- case EINVAL:
- if (err->ee_code != SO_EE_CODE_TXTIME_INVALID_PARAM)
- error(1, 0, "errqueue: unknown EINVAL %u\n",
- err->ee_code);
- reason = "invalid txtime";
- break;
- default:
- error(1, 0, "errqueue: errno %u code %u\n",
- err->ee_errno, err->ee_code);
- }
-
- tstamp = ((int64_t) err->ee_data) << 32 | err->ee_info;
- tstamp -= (int64_t) glob_tstart;
- tstamp /= 1000 * 1000;
- fprintf(stderr, "send: pkt %c at %" PRId64 "ms dropped: %s\n",
- data[ret - 1], tstamp, reason);
-
- msg.msg_flags = 0;
- msg.msg_controllen = sizeof(control);
- num_tstamp++;
- }
-
- return num_tstamp;
-}
-
-static void recv_errqueue_msgs(int fdt)
-{
- struct pollfd pfd = { .fd = fdt, .events = POLLERR };
- const int timeout_ms = 10;
- int ret, num_tstamp = 0;
-
- do {
- ret = poll(&pfd, 1, timeout_ms);
- if (ret == -1)
- error(1, errno, "poll");
-
- if (ret && (pfd.revents & POLLERR))
- num_tstamp += do_recv_errqueue_timeout(fdt);
-
- if (num_tstamp == cfg_num_pkt)
- break;
-
- } while (gettime_ns(cfg_clockid) < tdeliver_max);
-}
-
-static void start_time_wait(void)
-{
- uint64_t now;
- int err;
-
- if (!cfg_start_time_ns)
- return;
-
- now = gettime_ns(CLOCK_REALTIME);
- if (cfg_start_time_ns < now)
- return;
-
- err = usleep((cfg_start_time_ns - now) / 1000);
- if (err)
- error(1, errno, "usleep");
-}
-
-static void setsockopt_txtime(int fd)
-{
- struct sock_txtime so_txtime_val = { .clockid = cfg_clockid };
- struct sock_txtime so_txtime_val_read = { 0 };
- socklen_t vallen = sizeof(so_txtime_val);
-
- so_txtime_val.flags = SOF_TXTIME_REPORT_ERRORS;
-
- if (setsockopt(fd, SOL_SOCKET, SO_TXTIME,
- &so_txtime_val, sizeof(so_txtime_val)))
- error(1, errno, "setsockopt txtime");
-
- if (getsockopt(fd, SOL_SOCKET, SO_TXTIME,
- &so_txtime_val_read, &vallen))
- error(1, errno, "getsockopt txtime");
-
- if (vallen != sizeof(so_txtime_val) ||
- memcmp(&so_txtime_val, &so_txtime_val_read, vallen))
- error(1, 0, "getsockopt txtime: mismatch");
-}
-
-static int setup_tx(struct sockaddr *addr, socklen_t alen)
-{
- int fd;
-
- fd = socket(addr->sa_family, SOCK_DGRAM, 0);
- if (fd == -1)
- error(1, errno, "socket t");
-
- if (connect(fd, addr, alen))
- error(1, errno, "connect");
-
- setsockopt_txtime(fd);
-
- if (cfg_mark &&
- setsockopt(fd, SOL_SOCKET, SO_MARK, &cfg_mark, sizeof(cfg_mark)))
- error(1, errno, "setsockopt mark");
-
- return fd;
-}
-
-static int setup_rx(struct sockaddr *addr, socklen_t alen)
-{
- struct timeval tv = { .tv_usec = 100 * 1000 };
- int fd;
-
- fd = socket(addr->sa_family, SOCK_DGRAM, 0);
- if (fd == -1)
- error(1, errno, "socket r");
-
- if (bind(fd, addr, alen))
- error(1, errno, "bind");
-
- if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
- error(1, errno, "setsockopt rcv timeout");
-
- return fd;
-}
-
-static void do_test_tx(struct sockaddr *addr, socklen_t alen)
-{
- int fdt, i;
-
- fprintf(stderr, "\nSO_TXTIME ipv%c clock %s\n",
- addr->sa_family == PF_INET ? '4' : '6',
- cfg_clockid == CLOCK_TAI ? "tai" : "monotonic");
-
- fdt = setup_tx(addr, alen);
-
- start_time_wait();
- glob_tstart = gettime_ns(cfg_clockid);
-
- for (i = 0; i < cfg_num_pkt; i++)
- do_send_one(fdt, &cfg_buf[i]);
-
- recv_errqueue_msgs(fdt);
-
- if (close(fdt))
- error(1, errno, "close t");
-}
-
-static void do_test_rx(struct sockaddr *addr, socklen_t alen)
-{
- int fdr, i;
-
- fdr = setup_rx(addr, alen);
-
- start_time_wait();
- glob_tstart = gettime_ns(cfg_clockid);
-
- for (i = 0; i < cfg_num_pkt; i++)
- do_recv_one(fdr, &cfg_buf[i]);
-
- do_recv_verify_empty(fdr);
-
- if (close(fdr))
- error(1, errno, "close r");
-}
-
-static void setup_sockaddr(int domain, const char *str_addr,
- struct sockaddr_storage *sockaddr)
-{
- struct sockaddr_in6 *addr6 = (void *) sockaddr;
- struct sockaddr_in *addr4 = (void *) sockaddr;
-
- switch (domain) {
- case PF_INET:
- memset(addr4, 0, sizeof(*addr4));
- addr4->sin_family = AF_INET;
- addr4->sin_port = htons(cfg_port);
- if (str_addr &&
- inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
- error(1, 0, "ipv4 parse error: %s", str_addr);
- break;
- case PF_INET6:
- memset(addr6, 0, sizeof(*addr6));
- addr6->sin6_family = AF_INET6;
- addr6->sin6_port = htons(cfg_port);
- if (str_addr &&
- inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
- error(1, 0, "ipv6 parse error: %s", str_addr);
- break;
- }
-}
-
-static int parse_io(const char *optarg, struct timed_send *array)
-{
- char *arg, *tok;
- int aoff = 0;
-
- arg = strdup(optarg);
- if (!arg)
- error(1, errno, "strdup");
-
- while ((tok = strtok(arg, ","))) {
- arg = NULL; /* only pass non-zero on first call */
-
- if (aoff / 2 == MAX_NUM_PKT)
- error(1, 0, "exceeds max pkt count (%d)", MAX_NUM_PKT);
-
- if (aoff & 1) { /* parse delay */
- array->delay_us = strtol(tok, NULL, 0) * 1000;
- array++;
- } else { /* parse character */
- array->data = tok[0];
- }
-
- aoff++;
- }
-
- free(arg);
-
- return aoff / 2;
-}
-
-static void usage(const char *progname)
-{
- fprintf(stderr, "\nUsage: %s [options] <payload>\n"
- "Options:\n"
- " -4 only IPv4\n"
- " -6 only IPv6\n"
- " -c <clock> monotonic or tai (default)\n"
- " -D <addr> destination IP address (server)\n"
- " -S <addr> source IP address (client)\n"
- " -r run rx mode\n"
- " -t <nsec> start time (UTC nanoseconds)\n"
- " -m <mark> socket mark\n"
- "\n",
- progname);
- exit(1);
-}
-
-static void parse_opts(int argc, char **argv)
-{
- char *daddr = NULL, *saddr = NULL;
- int domain = PF_UNSPEC;
- int c;
-
- while ((c = getopt(argc, argv, "46c:S:D:rt:m:")) != -1) {
- switch (c) {
- case '4':
- if (domain != PF_UNSPEC)
- error(1, 0, "Pass one of -4 or -6");
- domain = PF_INET;
- cfg_alen = sizeof(struct sockaddr_in);
- cfg_errq_level = SOL_IP;
- cfg_errq_type = IP_RECVERR;
- break;
- case '6':
- if (domain != PF_UNSPEC)
- error(1, 0, "Pass one of -4 or -6");
- domain = PF_INET6;
- cfg_alen = sizeof(struct sockaddr_in6);
- cfg_errq_level = SOL_IPV6;
- cfg_errq_type = IPV6_RECVERR;
- break;
- case 'c':
- if (!strcmp(optarg, "tai"))
- cfg_clockid = CLOCK_TAI;
- else if (!strcmp(optarg, "monotonic") ||
- !strcmp(optarg, "mono"))
- cfg_clockid = CLOCK_MONOTONIC;
- else
- error(1, 0, "unknown clock id %s", optarg);
- break;
- case 'S':
- saddr = optarg;
- break;
- case 'D':
- daddr = optarg;
- break;
- case 'r':
- cfg_rx = true;
- break;
- case 't':
- cfg_start_time_ns = strtoll(optarg, NULL, 0);
- break;
- case 'm':
- cfg_mark = strtol(optarg, NULL, 0);
- break;
- default:
- usage(argv[0]);
- }
- }
-
- if (argc - optind != 1)
- usage(argv[0]);
-
- if (domain == PF_UNSPEC)
- error(1, 0, "Pass one of -4 or -6");
- if (!daddr)
- error(1, 0, "-D <server addr> required\n");
- if (!cfg_rx && !saddr)
- error(1, 0, "-S <client addr> required\n");
-
- setup_sockaddr(domain, daddr, &cfg_dst_addr);
- setup_sockaddr(domain, saddr, &cfg_src_addr);
-
- cfg_num_pkt = parse_io(argv[optind], cfg_buf);
-}
-
-int main(int argc, char **argv)
-{
- parse_opts(argc, argv);
-
- if (cfg_rx)
- do_test_rx((void *)&cfg_dst_addr, cfg_alen);
- else
- do_test_tx((void *)&cfg_src_addr, cfg_alen);
-
- return 0;
-}
diff --git a/tools/testing/selftests/net/so_txtime.sh b/tools/testing/selftests/net/so_txtime.sh
deleted file mode 100755
index 5e861ad32a42..000000000000
--- a/tools/testing/selftests/net/so_txtime.sh
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-#
-# Regression tests for the SO_TXTIME interface
-
-set -e
-
-readonly ksft_skip=4
-readonly DEV="veth0"
-readonly BIN="./so_txtime"
-
-readonly RAND="$(mktemp -u XXXXXX)"
-readonly NSPREFIX="ns-${RAND}"
-readonly NS1="${NSPREFIX}1"
-readonly NS2="${NSPREFIX}2"
-
-readonly SADDR4='192.168.1.1'
-readonly DADDR4='192.168.1.2'
-readonly SADDR6='fd::1'
-readonly DADDR6='fd::2'
-
-cleanup() {
- ip netns del "${NS2}"
- ip netns del "${NS1}"
-}
-
-trap cleanup EXIT
-
-# Create virtual ethernet pair between network namespaces
-ip netns add "${NS1}"
-ip netns add "${NS2}"
-
-ip link add "${DEV}" netns "${NS1}" type veth \
- peer name "${DEV}" netns "${NS2}"
-
-# Bring the devices up
-ip -netns "${NS1}" link set "${DEV}" up
-ip -netns "${NS2}" link set "${DEV}" up
-
-# Set fixed MAC addresses on the devices
-ip -netns "${NS1}" link set dev "${DEV}" address 02:02:02:02:02:02
-ip -netns "${NS2}" link set dev "${DEV}" address 06:06:06:06:06:06
-
-# Add fixed IP addresses to the devices
-ip -netns "${NS1}" addr add 192.168.1.1/24 dev "${DEV}"
-ip -netns "${NS2}" addr add 192.168.1.2/24 dev "${DEV}"
-ip -netns "${NS1}" addr add fd::1/64 dev "${DEV}" nodad
-ip -netns "${NS2}" addr add fd::2/64 dev "${DEV}" nodad
-
-run_test() {
- local readonly IP="$1"
- local readonly CLOCK="$2"
- local readonly TXARGS="$3"
- local readonly RXARGS="$4"
-
- if [[ "${IP}" == "4" ]]; then
- local readonly SADDR="${SADDR4}"
- local readonly DADDR="${DADDR4}"
- elif [[ "${IP}" == "6" ]]; then
- local readonly SADDR="${SADDR6}"
- local readonly DADDR="${DADDR6}"
- else
- echo "Invalid IP version ${IP}"
- exit 1
- fi
-
- local readonly START="$(date +%s%N --date="+ 0.1 seconds")"
-
- ip netns exec "${NS2}" "${BIN}" -"${IP}" -c "${CLOCK}" -t "${START}" -S "${SADDR}" -D "${DADDR}" "${RXARGS}" -r &
- ip netns exec "${NS1}" "${BIN}" -"${IP}" -c "${CLOCK}" -t "${START}" -S "${SADDR}" -D "${DADDR}" "${TXARGS}"
- wait "$!"
-}
-
-do_test() {
- run_test $@
- [ $? -ne 0 ] && ret=1
-}
-
-do_fail_test() {
- run_test $@
- [ $? -eq 0 ] && ret=1
-}
-
-ip netns exec "${NS1}" tc qdisc add dev "${DEV}" root fq
-set +e
-ret=0
-do_test 4 mono a,-1 a,-1
-do_test 6 mono a,0 a,0
-do_test 6 mono a,10 a,10
-do_test 4 mono a,10,b,20 a,10,b,20
-do_test 6 mono a,20,b,10 b,20,a,20
-
-if ip netns exec "${NS1}" tc qdisc replace dev "${DEV}" root etf clockid CLOCK_TAI delta 400000; then
- do_fail_test 4 tai a,-1 a,-1
- do_fail_test 6 tai a,0 a,0
- do_test 6 tai a,10 a,10
- do_test 4 tai a,10,b,20 a,10,b,20
- do_test 6 tai a,20,b,10 b,10,a,20
-else
- echo "tc ($(tc -V)) does not support qdisc etf. skipping"
- [ $ret -eq 0 ] && ret=$ksft_skip
-fi
-
-if [ $ret -eq 0 ]; then
- echo OK. All tests passed
-elif [[ $ret -ne $ksft_skip && -n "$KSFT_MACHINE_SLOW" ]]; then
- echo "Ignoring errors due to slow environment" 1>&2
- ret=0
-fi
-exit $ret
diff --git a/tools/testing/selftests/net/tun.c b/tools/testing/selftests/net/tun.c
index cf106a49b55e..abe488bac50b 100644
--- a/tools/testing/selftests/net/tun.c
+++ b/tools/testing/selftests/net/tun.c
@@ -42,19 +42,19 @@ static struct in_addr param_ipaddr4_inner_src = {
};

static struct in6_addr param_ipaddr6_outer_dst = {
- { { 0x20, 0x02, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
+ { { 0xfd, 0x00, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
};

static struct in6_addr param_ipaddr6_outer_src = {
- { { 0x20, 0x02, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },
+ { { 0xfd, 0x00, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },
};

static struct in6_addr param_ipaddr6_inner_dst = {
- { { 0x20, 0x02, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
+ { { 0xfd, 0x00, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
};

static struct in6_addr param_ipaddr6_inner_src = {
- { { 0x20, 0x02, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },
+ { { 0xfd, 0x00, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },
};

#ifndef BIT