diff options
author | Alice Ferrazzi <alicef@gentoo.org> | 2023-04-06 19:40:48 +0900 |
---|---|---|
committer | Alice Ferrazzi <alicef@gentoo.org> | 2023-04-06 19:40:48 +0900 |
commit | f97107ed670fe757007eb080b0b2accc643d82f2 (patch) | |
tree | 6b9cc6e4f45f679f527189aa32219a3aef9c19a7 | |
parent | Update namespace user.pax.* on tmpfs patch (diff) | |
download | linux-patches-f97107ed670fe757007eb080b0b2accc643d82f2.tar.gz linux-patches-f97107ed670fe757007eb080b0b2accc643d82f2.tar.bz2 linux-patches-f97107ed670fe757007eb080b0b2accc643d82f2.zip |
Linux patch 6.1.236.1-27
Signed-off-by: Alice Ferrazzi <alicef@gentoo.org>
-rw-r--r-- | 0000_README | 4 | ||||
-rw-r--r-- | 1022_linux-6.1.23.patch | 10192 |
2 files changed, 10196 insertions, 0 deletions
diff --git a/0000_README b/0000_README index dac87a0b..82189ada 100644 --- a/0000_README +++ b/0000_README @@ -131,6 +131,10 @@ Patch: 1021_linux-6.1.22.patch From: https://www.kernel.org Desc: Linux 6.1.22 +Patch: 1022_linux-6.1.23.patch +From: https://www.kernel.org +Desc: Linux 6.1.23 + Patch: 1500_XATTR_USER_PREFIX.patch From: https://bugs.gentoo.org/show_bug.cgi?id=470644 Desc: Support for namespace user.pax.* on tmpfs. diff --git a/1022_linux-6.1.23.patch b/1022_linux-6.1.23.patch new file mode 100644 index 00000000..885f13e9 --- /dev/null +++ b/1022_linux-6.1.23.patch @@ -0,0 +1,10192 @@ +diff --git a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml +index 7149784a36ac7..3ee77af5a74cf 100644 +--- a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml ++++ b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml +@@ -84,6 +84,13 @@ patternProperties: + "^otp(-[0-9]+)?$": + type: object + ++ spi-cpol: true ++ spi-cpha: true ++ ++dependencies: ++ spi-cpol: [ spi-cpha ] ++ spi-cpha: [ spi-cpol ] ++ + unevaluatedProperties: false + + examples: +diff --git a/Makefile b/Makefile +index c3d44de8850cf..a162f6cdf77c6 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + VERSION = 6 + PATCHLEVEL = 1 +-SUBLEVEL = 22 ++SUBLEVEL = 23 + EXTRAVERSION = + NAME = Hurr durr I'ma ninja sloth + +diff --git a/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts b/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts +index fcc890e3ad735..f11feb98fde33 100644 +--- a/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts ++++ b/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts +@@ -244,7 +244,7 @@ + }; + }; + +- iio-hwmon-battery { ++ iio-hwmon { + compatible = "iio-hwmon"; + io-channels = <&adc1 7>; + }; +diff --git a/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts b/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts +index 4879da4cdbd25..77a3a27b04e26 100644 +--- a/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts ++++ b/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts +@@ -220,7 +220,7 @@ + }; + }; + +- iio-hwmon-battery { ++ iio-hwmon { + compatible = "iio-hwmon"; + io-channels = <&adc1 7>; + }; +diff --git a/arch/arm64/kernel/efi-header.S b/arch/arm64/kernel/efi-header.S +index 28d8a5dca5f12..d731b4655df8e 100644 +--- a/arch/arm64/kernel/efi-header.S ++++ b/arch/arm64/kernel/efi-header.S +@@ -66,7 +66,7 @@ + .long .Lefi_header_end - .L_head // SizeOfHeaders + .long 0 // CheckSum + .short IMAGE_SUBSYSTEM_EFI_APPLICATION // Subsystem +- .short 0 // DllCharacteristics ++ .short IMAGE_DLL_CHARACTERISTICS_NX_COMPAT // DllCharacteristics + .quad 0 // SizeOfStackReserve + .quad 0 // SizeOfStackCommit + .quad 0 // SizeOfHeapReserve +diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c +index 2c3759f1f2c56..019472dd98ff7 100644 +--- a/arch/arm64/kvm/mmu.c ++++ b/arch/arm64/kvm/mmu.c +@@ -646,14 +646,33 @@ static int get_user_mapping_size(struct kvm *kvm, u64 addr) + CONFIG_PGTABLE_LEVELS), + .mm_ops = &kvm_user_mm_ops, + }; ++ unsigned long flags; + kvm_pte_t pte = 0; /* Keep GCC quiet... */ + u32 level = ~0; + int ret; + ++ /* ++ * Disable IRQs so that we hazard against a concurrent ++ * teardown of the userspace page tables (which relies on ++ * IPI-ing threads). ++ */ ++ local_irq_save(flags); + ret = kvm_pgtable_get_leaf(&pgt, addr, &pte, &level); +- VM_BUG_ON(ret); +- VM_BUG_ON(level >= KVM_PGTABLE_MAX_LEVELS); +- VM_BUG_ON(!(pte & PTE_VALID)); ++ local_irq_restore(flags); ++ ++ if (ret) ++ return ret; ++ ++ /* ++ * Not seeing an error, but not updating level? Something went ++ * deeply wrong... ++ */ ++ if (WARN_ON(level >= KVM_PGTABLE_MAX_LEVELS)) ++ return -EFAULT; ++ ++ /* Oops, the userspace PTs are gone... Replay the fault */ ++ if (!kvm_pte_valid(pte)) ++ return -EAGAIN; + + return BIT(ARM64_HW_PGTABLE_LEVEL_SHIFT(level)); + } +@@ -1006,7 +1025,7 @@ static bool fault_supports_stage2_huge_mapping(struct kvm_memory_slot *memslot, + * + * Returns the size of the mapping. + */ +-static unsigned long ++static long + transparent_hugepage_adjust(struct kvm *kvm, struct kvm_memory_slot *memslot, + unsigned long hva, kvm_pfn_t *pfnp, + phys_addr_t *ipap) +@@ -1018,8 +1037,15 @@ transparent_hugepage_adjust(struct kvm *kvm, struct kvm_memory_slot *memslot, + * sure that the HVA and IPA are sufficiently aligned and that the + * block map is contained within the memslot. + */ +- if (fault_supports_stage2_huge_mapping(memslot, hva, PMD_SIZE) && +- get_user_mapping_size(kvm, hva) >= PMD_SIZE) { ++ if (fault_supports_stage2_huge_mapping(memslot, hva, PMD_SIZE)) { ++ int sz = get_user_mapping_size(kvm, hva); ++ ++ if (sz < 0) ++ return sz; ++ ++ if (sz < PMD_SIZE) ++ return PAGE_SIZE; ++ + /* + * The address we faulted on is backed by a transparent huge + * page. However, because we map the compound huge page and +@@ -1138,7 +1164,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, + bool logging_active = memslot_is_logging(memslot); + bool use_read_lock = false; + unsigned long fault_level = kvm_vcpu_trap_get_fault_level(vcpu); +- unsigned long vma_pagesize, fault_granule; ++ long vma_pagesize, fault_granule; + enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R; + struct kvm_pgtable *pgt; + +@@ -1295,6 +1321,11 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, + vma_pagesize = transparent_hugepage_adjust(kvm, memslot, + hva, &pfn, + &fault_ipa); ++ ++ if (vma_pagesize < 0) { ++ ret = vma_pagesize; ++ goto out_unlock; ++ } + } + + if (fault_status != FSC_PERM && !device && kvm_has_mte(kvm)) { +diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c +index f4a7c5abcbca4..bfe4f17232b3e 100644 +--- a/arch/arm64/kvm/sys_regs.c ++++ b/arch/arm64/kvm/sys_regs.c +@@ -767,6 +767,22 @@ static bool pmu_counter_idx_valid(struct kvm_vcpu *vcpu, u64 idx) + return true; + } + ++static int get_pmu_evcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, ++ u64 *val) ++{ ++ u64 idx; ++ ++ if (r->CRn == 9 && r->CRm == 13 && r->Op2 == 0) ++ /* PMCCNTR_EL0 */ ++ idx = ARMV8_PMU_CYCLE_IDX; ++ else ++ /* PMEVCNTRn_EL0 */ ++ idx = ((r->CRm & 3) << 3) | (r->Op2 & 7); ++ ++ *val = kvm_pmu_get_counter_value(vcpu, idx); ++ return 0; ++} ++ + static bool access_pmu_evcntr(struct kvm_vcpu *vcpu, + struct sys_reg_params *p, + const struct sys_reg_desc *r) +@@ -983,7 +999,7 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, + /* Macro to expand the PMEVCNTRn_EL0 register */ + #define PMU_PMEVCNTR_EL0(n) \ + { PMU_SYS_REG(SYS_PMEVCNTRn_EL0(n)), \ +- .reset = reset_pmevcntr, \ ++ .reset = reset_pmevcntr, .get_user = get_pmu_evcntr, \ + .access = access_pmu_evcntr, .reg = (PMEVCNTR0_EL0 + n), } + + /* Macro to expand the PMEVTYPERn_EL0 register */ +@@ -1632,7 +1648,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { + { PMU_SYS_REG(SYS_PMCEID1_EL0), + .access = access_pmceid, .reset = NULL }, + { PMU_SYS_REG(SYS_PMCCNTR_EL0), +- .access = access_pmu_evcntr, .reset = reset_unknown, .reg = PMCCNTR_EL0 }, ++ .access = access_pmu_evcntr, .reset = reset_unknown, ++ .reg = PMCCNTR_EL0, .get_user = get_pmu_evcntr}, + { PMU_SYS_REG(SYS_PMXEVTYPER_EL0), + .access = access_pmu_evtyper, .reset = NULL }, + { PMU_SYS_REG(SYS_PMXEVCNTR_EL0), +diff --git a/arch/mips/bmips/dma.c b/arch/mips/bmips/dma.c +index 33788668cbdbf..3779e7855bd75 100644 +--- a/arch/mips/bmips/dma.c ++++ b/arch/mips/bmips/dma.c +@@ -5,6 +5,8 @@ + #include <asm/bmips.h> + #include <asm/io.h> + ++bool bmips_rac_flush_disable; ++ + void arch_sync_dma_for_cpu_all(void) + { + void __iomem *cbr = BMIPS_GET_CBR(); +@@ -15,6 +17,9 @@ void arch_sync_dma_for_cpu_all(void) + boot_cpu_type() != CPU_BMIPS4380) + return; + ++ if (unlikely(bmips_rac_flush_disable)) ++ return; ++ + /* Flush stale data out of the readahead cache */ + cfg = __raw_readl(cbr + BMIPS_RAC_CONFIG); + __raw_writel(cfg | 0x100, cbr + BMIPS_RAC_CONFIG); +diff --git a/arch/mips/bmips/setup.c b/arch/mips/bmips/setup.c +index e95b3f78e7cd4..549a6392a3d2d 100644 +--- a/arch/mips/bmips/setup.c ++++ b/arch/mips/bmips/setup.c +@@ -35,6 +35,8 @@ + #define REG_BCM6328_OTP ((void __iomem *)CKSEG1ADDR(0x1000062c)) + #define BCM6328_TP1_DISABLED BIT(9) + ++extern bool bmips_rac_flush_disable; ++ + static const unsigned long kbase = VMLINUX_LOAD_ADDRESS & 0xfff00000; + + struct bmips_quirk { +@@ -104,6 +106,12 @@ static void bcm6358_quirks(void) + * disable SMP for now + */ + bmips_smp_enabled = 0; ++ ++ /* ++ * RAC flush causes kernel panics on BCM6358 when booting from TP1 ++ * because the bootloader is not initializing it properly. ++ */ ++ bmips_rac_flush_disable = !!(read_c0_brcm_cmt_local() & (1 << 31)); + } + + static void bcm6368_quirks(void) +diff --git a/arch/powerpc/include/asm/book3s/64/tlbflush.h b/arch/powerpc/include/asm/book3s/64/tlbflush.h +index 67655cd605456..985bd8e69bdc2 100644 +--- a/arch/powerpc/include/asm/book3s/64/tlbflush.h ++++ b/arch/powerpc/include/asm/book3s/64/tlbflush.h +@@ -163,6 +163,11 @@ static inline void flush_tlb_fix_spurious_fault(struct vm_area_struct *vma, + */ + } + ++static inline bool __pte_protnone(unsigned long pte) ++{ ++ return (pte & (pgprot_val(PAGE_NONE) | _PAGE_RWX)) == pgprot_val(PAGE_NONE); ++} ++ + static inline bool __pte_flags_need_flush(unsigned long oldval, + unsigned long newval) + { +@@ -179,8 +184,8 @@ static inline bool __pte_flags_need_flush(unsigned long oldval, + /* + * We do not expect kernel mappings or non-PTEs or not-present PTEs. + */ +- VM_WARN_ON_ONCE(oldval & _PAGE_PRIVILEGED); +- VM_WARN_ON_ONCE(newval & _PAGE_PRIVILEGED); ++ VM_WARN_ON_ONCE(!__pte_protnone(oldval) && oldval & _PAGE_PRIVILEGED); ++ VM_WARN_ON_ONCE(!__pte_protnone(newval) && newval & _PAGE_PRIVILEGED); + VM_WARN_ON_ONCE(!(oldval & _PAGE_PTE)); + VM_WARN_ON_ONCE(!(newval & _PAGE_PTE)); + VM_WARN_ON_ONCE(!(oldval & _PAGE_PRESENT)); +diff --git a/arch/powerpc/kernel/ptrace/ptrace-view.c b/arch/powerpc/kernel/ptrace/ptrace-view.c +index 076d867412c70..31876db8e996f 100644 +--- a/arch/powerpc/kernel/ptrace/ptrace-view.c ++++ b/arch/powerpc/kernel/ptrace/ptrace-view.c +@@ -290,6 +290,9 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset, + static int ppr_get(struct task_struct *target, const struct user_regset *regset, + struct membuf to) + { ++ if (!target->thread.regs) ++ return -EINVAL; ++ + return membuf_write(&to, &target->thread.regs->ppr, sizeof(u64)); + } + +@@ -297,6 +300,9 @@ static int ppr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, const void *kbuf, + const void __user *ubuf) + { ++ if (!target->thread.regs) ++ return -EINVAL; ++ + return user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.regs->ppr, 0, sizeof(u64)); + } +diff --git a/arch/powerpc/platforms/pseries/vas.c b/arch/powerpc/platforms/pseries/vas.c +index 4ad6e510d405f..94c023bb13e05 100644 +--- a/arch/powerpc/platforms/pseries/vas.c ++++ b/arch/powerpc/platforms/pseries/vas.c +@@ -857,6 +857,13 @@ int pseries_vas_dlpar_cpu(void) + { + int new_nr_creds, rc; + ++ /* ++ * NX-GZIP is not enabled. Nothing to do for DLPAR event ++ */ ++ if (!copypaste_feat) ++ return 0; ++ ++ + rc = h_query_vas_capabilities(H_QUERY_VAS_CAPABILITIES, + vascaps[VAS_GZIP_DEF_FEAT_TYPE].feat, + (u64)virt_to_phys(&hv_cop_caps)); +@@ -1013,6 +1020,7 @@ static int __init pseries_vas_init(void) + * Linux supports user space COPY/PASTE only with Radix + */ + if (!radix_enabled()) { ++ copypaste_feat = false; + pr_err("API is supported only with radix page tables\n"); + return -ENOTSUPP; + } +diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig +index ae11d5647f9d4..06b9b2f60b9fb 100644 +--- a/arch/riscv/Kconfig ++++ b/arch/riscv/Kconfig +@@ -278,7 +278,7 @@ config ARCH_RV64I + select HAVE_DYNAMIC_FTRACE_WITH_REGS if HAVE_DYNAMIC_FTRACE + select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL + select HAVE_FUNCTION_GRAPH_TRACER +- select HAVE_FUNCTION_TRACER if !XIP_KERNEL ++ select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !PREEMPTION + select SWIOTLB if MMU + + endchoice +diff --git a/arch/riscv/kvm/vcpu_timer.c b/arch/riscv/kvm/vcpu_timer.c +index ad34519c8a13d..3ac2ff6a65dac 100644 +--- a/arch/riscv/kvm/vcpu_timer.c ++++ b/arch/riscv/kvm/vcpu_timer.c +@@ -147,10 +147,8 @@ static void kvm_riscv_vcpu_timer_blocking(struct kvm_vcpu *vcpu) + return; + + delta_ns = kvm_riscv_delta_cycles2ns(t->next_cycles, gt, t); +- if (delta_ns) { +- hrtimer_start(&t->hrt, ktime_set(0, delta_ns), HRTIMER_MODE_REL); +- t->next_set = true; +- } ++ hrtimer_start(&t->hrt, ktime_set(0, delta_ns), HRTIMER_MODE_REL); ++ t->next_set = true; + } + + static void kvm_riscv_vcpu_timer_unblocking(struct kvm_vcpu *vcpu) +diff --git a/arch/s390/Makefile b/arch/s390/Makefile +index b3235ab0ace83..ed646c583e4fe 100644 +--- a/arch/s390/Makefile ++++ b/arch/s390/Makefile +@@ -162,7 +162,7 @@ vdso_prepare: prepare0 + + ifdef CONFIG_EXPOLINE_EXTERN + modules_prepare: expoline_prepare +-expoline_prepare: ++expoline_prepare: scripts + $(Q)$(MAKE) $(build)=arch/s390/lib/expoline arch/s390/lib/expoline/expoline.o + endif + endif +diff --git a/arch/s390/lib/uaccess.c b/arch/s390/lib/uaccess.c +index 720036fb19242..d44214072779e 100644 +--- a/arch/s390/lib/uaccess.c ++++ b/arch/s390/lib/uaccess.c +@@ -172,7 +172,7 @@ unsigned long __clear_user(void __user *to, unsigned long size) + "4: slgr %0,%0\n" + "5:\n" + EX_TABLE(0b,2b) EX_TABLE(6b,2b) EX_TABLE(3b,5b) EX_TABLE(7b,5b) +- : "+a" (size), "+a" (to), "+a" (tmp1), "=a" (tmp2) ++ : "+&a" (size), "+&a" (to), "+a" (tmp1), "=&a" (tmp2) + : "a" (empty_zero_page), [spec] "d" (spec.val) + : "cc", "memory", "0"); + return size; +diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile +index 3c5b52fbe4a7f..a9ec8c9f5c5dd 100644 +--- a/arch/x86/xen/Makefile ++++ b/arch/x86/xen/Makefile +@@ -45,6 +45,6 @@ obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= spinlock.o + + obj-$(CONFIG_XEN_DEBUG_FS) += debugfs.o + +-obj-$(CONFIG_XEN_PV_DOM0) += vga.o ++obj-$(CONFIG_XEN_DOM0) += vga.o + + obj-$(CONFIG_XEN_EFI) += efi.o +diff --git a/arch/x86/xen/enlighten_pv.c b/arch/x86/xen/enlighten_pv.c +index 8944726255c9c..333539bdbdaae 100644 +--- a/arch/x86/xen/enlighten_pv.c ++++ b/arch/x86/xen/enlighten_pv.c +@@ -1389,7 +1389,8 @@ asmlinkage __visible void __init xen_start_kernel(struct start_info *si) + + x86_platform.set_legacy_features = + xen_dom0_set_legacy_features; +- xen_init_vga(info, xen_start_info->console.dom0.info_size); ++ xen_init_vga(info, xen_start_info->console.dom0.info_size, ++ &boot_params.screen_info); + xen_start_info->console.domU.mfn = 0; + xen_start_info->console.domU.evtchn = 0; + +diff --git a/arch/x86/xen/enlighten_pvh.c b/arch/x86/xen/enlighten_pvh.c +index bcae606bbc5cf..ada3868c02c23 100644 +--- a/arch/x86/xen/enlighten_pvh.c ++++ b/arch/x86/xen/enlighten_pvh.c +@@ -43,6 +43,19 @@ void __init xen_pvh_init(struct boot_params *boot_params) + x86_init.oem.banner = xen_banner; + + xen_efi_init(boot_params); ++ ++ if (xen_initial_domain()) { ++ struct xen_platform_op op = { ++ .cmd = XENPF_get_dom0_console, ++ }; ++ int ret = HYPERVISOR_platform_op(&op); ++ ++ if (ret > 0) ++ xen_init_vga(&op.u.dom0_console, ++ min(ret * sizeof(char), ++ sizeof(op.u.dom0_console)), ++ &boot_params->screen_info); ++ } + } + + void __init mem_map_via_hcall(struct boot_params *boot_params_p) +diff --git a/arch/x86/xen/vga.c b/arch/x86/xen/vga.c +index 14ea32e734d59..d97adab8420f4 100644 +--- a/arch/x86/xen/vga.c ++++ b/arch/x86/xen/vga.c +@@ -9,10 +9,9 @@ + + #include "xen-ops.h" + +-void __init xen_init_vga(const struct dom0_vga_console_info *info, size_t size) ++void __init xen_init_vga(const struct dom0_vga_console_info *info, size_t size, ++ struct screen_info *screen_info) + { +- struct screen_info *screen_info = &boot_params.screen_info; +- + /* This is drawn from a dump from vgacon:startup in + * standard Linux. */ + screen_info->orig_video_mode = 3; +diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h +index 9a8bb972193d8..a10903785a338 100644 +--- a/arch/x86/xen/xen-ops.h ++++ b/arch/x86/xen/xen-ops.h +@@ -108,11 +108,12 @@ static inline void xen_uninit_lock_cpu(int cpu) + + struct dom0_vga_console_info; + +-#ifdef CONFIG_XEN_PV_DOM0 +-void __init xen_init_vga(const struct dom0_vga_console_info *, size_t size); ++#ifdef CONFIG_XEN_DOM0 ++void __init xen_init_vga(const struct dom0_vga_console_info *, size_t size, ++ struct screen_info *); + #else + static inline void __init xen_init_vga(const struct dom0_vga_console_info *info, +- size_t size) ++ size_t size, struct screen_info *si) + { + } + #endif +diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c +index 0c25e035ff107..9e9ade20a7ce4 100644 +--- a/arch/xtensa/kernel/traps.c ++++ b/arch/xtensa/kernel/traps.c +@@ -541,7 +541,7 @@ static size_t kstack_depth_to_print = CONFIG_PRINT_STACK_DEPTH; + + void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl) + { +- size_t len; ++ size_t len, off = 0; + + if (!sp) + sp = stack_pointer(task); +@@ -550,9 +550,17 @@ void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl) + kstack_depth_to_print * STACK_DUMP_ENTRY_SIZE); + + printk("%sStack:\n", loglvl); +- print_hex_dump(loglvl, " ", DUMP_PREFIX_NONE, +- STACK_DUMP_LINE_SIZE, STACK_DUMP_ENTRY_SIZE, +- sp, len, false); ++ while (off < len) { ++ u8 line[STACK_DUMP_LINE_SIZE]; ++ size_t line_len = len - off > STACK_DUMP_LINE_SIZE ? ++ STACK_DUMP_LINE_SIZE : len - off; ++ ++ __memcpy(line, (u8 *)sp + off, line_len); ++ print_hex_dump(loglvl, " ", DUMP_PREFIX_NONE, ++ STACK_DUMP_LINE_SIZE, STACK_DUMP_ENTRY_SIZE, ++ line, line_len, false); ++ off += STACK_DUMP_LINE_SIZE; ++ } + show_trace(task, sp, loglvl); + } + +diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c +index d466c81953146..3b6146b1e25cc 100644 +--- a/drivers/acpi/bus.c ++++ b/drivers/acpi/bus.c +@@ -456,85 +456,67 @@ out_free: + Notification Handling + -------------------------------------------------------------------------- */ + +-/* +- * acpi_bus_notify +- * --------------- +- * Callback for all 'system-level' device notifications (values 0x00-0x7F). ++/** ++ * acpi_bus_notify - Global system-level (0x00-0x7F) notifications handler ++ * @handle: Target ACPI object. ++ * @type: Notification type. ++ * @data: Ignored. ++ * ++ * This only handles notifications related to device hotplug. + */ + static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) + { + struct acpi_device *adev; +- u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; +- bool hotplug_event = false; + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); +- hotplug_event = true; + break; + + case ACPI_NOTIFY_DEVICE_CHECK: + acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n"); +- hotplug_event = true; + break; + + case ACPI_NOTIFY_DEVICE_WAKE: + acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_WAKE event\n"); +- break; ++ return; + + case ACPI_NOTIFY_EJECT_REQUEST: + acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n"); +- hotplug_event = true; + break; + + case ACPI_NOTIFY_DEVICE_CHECK_LIGHT: + acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK_LIGHT event\n"); + /* TBD: Exactly what does 'light' mean? */ +- break; ++ return; + + case ACPI_NOTIFY_FREQUENCY_MISMATCH: + acpi_handle_err(handle, "Device cannot be configured due " + "to a frequency mismatch\n"); +- break; ++ return; + + case ACPI_NOTIFY_BUS_MODE_MISMATCH: + acpi_handle_err(handle, "Device cannot be configured due " + "to a bus mode mismatch\n"); +- break; ++ return; + + case ACPI_NOTIFY_POWER_FAULT: + acpi_handle_err(handle, "Device has suffered a power fault\n"); +- break; ++ return; + + default: + acpi_handle_debug(handle, "Unknown event type 0x%x\n", type); +- break; ++ return; + } + + adev = acpi_get_acpi_dev(handle); +- if (!adev) +- goto err; +- +- if (adev->dev.driver) { +- struct acpi_driver *driver = to_acpi_driver(adev->dev.driver); +- +- if (driver && driver->ops.notify && +- (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS)) +- driver->ops.notify(adev, type); +- } +- +- if (!hotplug_event) { +- acpi_put_acpi_dev(adev); +- return; +- } + +- if (ACPI_SUCCESS(acpi_hotplug_schedule(adev, type))) ++ if (adev && ACPI_SUCCESS(acpi_hotplug_schedule(adev, type))) + return; + + acpi_put_acpi_dev(adev); + +- err: +- acpi_evaluate_ost(handle, type, ost_code, NULL); ++ acpi_evaluate_ost(handle, type, ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); + } + + static void acpi_notify_device(acpi_handle handle, u32 event, void *data) +@@ -559,42 +541,51 @@ static u32 acpi_device_fixed_event(void *data) + return ACPI_INTERRUPT_HANDLED; + } + +-static int acpi_device_install_notify_handler(struct acpi_device *device) ++static int acpi_device_install_notify_handler(struct acpi_device *device, ++ struct acpi_driver *acpi_drv) + { + acpi_status status; + +- if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) ++ if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) { + status = + acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, + acpi_device_fixed_event, + device); +- else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) ++ } else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) { + status = + acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, + acpi_device_fixed_event, + device); +- else +- status = acpi_install_notify_handler(device->handle, +- ACPI_DEVICE_NOTIFY, ++ } else { ++ u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ? ++ ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY; ++ ++ status = acpi_install_notify_handler(device->handle, type, + acpi_notify_device, + device); ++ } + + if (ACPI_FAILURE(status)) + return -EINVAL; + return 0; + } + +-static void acpi_device_remove_notify_handler(struct acpi_device *device) ++static void acpi_device_remove_notify_handler(struct acpi_device *device, ++ struct acpi_driver *acpi_drv) + { +- if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) ++ if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) { + acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, + acpi_device_fixed_event); +- else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) ++ } else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) { + acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, + acpi_device_fixed_event); +- else +- acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, ++ } else { ++ u32 type = acpi_drv->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS ? ++ ACPI_ALL_NOTIFY : ACPI_DEVICE_NOTIFY; ++ ++ acpi_remove_notify_handler(device->handle, type, + acpi_notify_device); ++ } + } + + /* Handle events targeting \_SB device (at present only graceful shutdown) */ +@@ -1036,7 +1027,7 @@ static int acpi_device_probe(struct device *dev) + acpi_drv->name, acpi_dev->pnp.bus_id); + + if (acpi_drv->ops.notify) { +- ret = acpi_device_install_notify_handler(acpi_dev); ++ ret = acpi_device_install_notify_handler(acpi_dev, acpi_drv); + if (ret) { + if (acpi_drv->ops.remove) + acpi_drv->ops.remove(acpi_dev); +@@ -1059,7 +1050,7 @@ static void acpi_device_remove(struct device *dev) + struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver); + + if (acpi_drv->ops.notify) +- acpi_device_remove_notify_handler(acpi_dev); ++ acpi_device_remove_notify_handler(acpi_dev, acpi_drv); + + if (acpi_drv->ops.remove) + acpi_drv->ops.remove(acpi_dev); +diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c +index 7f0ed845cd6ad..f06b3d3556710 100644 +--- a/drivers/acpi/video_detect.c ++++ b/drivers/acpi/video_detect.c +@@ -714,6 +714,13 @@ static const struct dmi_system_id video_detect_dmi_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"), + }, + }, ++ { ++ .callback = video_detect_force_native, ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 15 3535"), ++ }, ++ }, + + /* + * Desktops which falsely report a backlight and which our heuristics +diff --git a/drivers/block/loop.c b/drivers/block/loop.c +index 793ae876918ce..426d0b42685a0 100644 +--- a/drivers/block/loop.c ++++ b/drivers/block/loop.c +@@ -1010,9 +1010,6 @@ static int loop_configure(struct loop_device *lo, fmode_t mode, + /* This is safe, since we have a reference from open(). */ + __module_get(THIS_MODULE); + +- /* suppress uevents while reconfiguring the device */ +- dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 1); +- + /* + * If we don't hold exclusive handle for the device, upgrade to it + * here to avoid changing device under exclusive owner. +@@ -1067,6 +1064,9 @@ static int loop_configure(struct loop_device *lo, fmode_t mode, + } + } + ++ /* suppress uevents while reconfiguring the device */ ++ dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 1); ++ + disk_force_media_change(lo->lo_disk, DISK_EVENT_MEDIA_CHANGE); + set_disk_ro(lo->lo_disk, (lo->lo_flags & LO_FLAGS_READ_ONLY) != 0); + +@@ -1109,17 +1109,17 @@ static int loop_configure(struct loop_device *lo, fmode_t mode, + if (partscan) + clear_bit(GD_SUPPRESS_PART_SCAN, &lo->lo_disk->state); + ++ /* enable and uncork uevent now that we are done */ ++ dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 0); ++ + loop_global_unlock(lo, is_loop); + if (partscan) + loop_reread_partitions(lo); ++ + if (!(mode & FMODE_EXCL)) + bd_abort_claiming(bdev, loop_configure); + +- error = 0; +-done: +- /* enable and uncork uevent now that we are done */ +- dev_set_uevent_suppress(disk_to_dev(lo->lo_disk), 0); +- return error; ++ return 0; + + out_unlock: + loop_global_unlock(lo, is_loop); +@@ -1130,7 +1130,7 @@ out_putf: + fput(file); + /* This is safe: open() is still holding a reference. */ + module_put(THIS_MODULE); +- goto done; ++ return error; + } + + static void __loop_clr_fd(struct loop_device *lo, bool release) +diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c +index 4aec9be0ab77e..22a790d512842 100644 +--- a/drivers/block/ublk_drv.c ++++ b/drivers/block/ublk_drv.c +@@ -656,7 +656,8 @@ static void __ublk_fail_req(struct ublk_queue *ubq, struct ublk_io *io, + } + } + +-static void ubq_complete_io_cmd(struct ublk_io *io, int res) ++static void ubq_complete_io_cmd(struct ublk_io *io, int res, ++ unsigned issue_flags) + { + /* mark this cmd owned by ublksrv */ + io->flags |= UBLK_IO_FLAG_OWNED_BY_SRV; +@@ -668,7 +669,7 @@ static void ubq_complete_io_cmd(struct ublk_io *io, int res) + io->flags &= ~UBLK_IO_FLAG_ACTIVE; + + /* tell ublksrv one io request is coming */ +- io_uring_cmd_done(io->cmd, res, 0); ++ io_uring_cmd_done(io->cmd, res, 0, issue_flags); + } + + #define UBLK_REQUEUE_DELAY_MS 3 +@@ -685,7 +686,8 @@ static inline void __ublk_abort_rq(struct ublk_queue *ubq, + mod_delayed_work(system_wq, &ubq->dev->monitor_work, 0); + } + +-static inline void __ublk_rq_task_work(struct request *req) ++static inline void __ublk_rq_task_work(struct request *req, ++ unsigned issue_flags) + { + struct ublk_queue *ubq = req->mq_hctx->driver_data; + int tag = req->tag; +@@ -723,7 +725,7 @@ static inline void __ublk_rq_task_work(struct request *req) + pr_devel("%s: need get data. op %d, qid %d tag %d io_flags %x\n", + __func__, io->cmd->cmd_op, ubq->q_id, + req->tag, io->flags); +- ubq_complete_io_cmd(io, UBLK_IO_RES_NEED_GET_DATA); ++ ubq_complete_io_cmd(io, UBLK_IO_RES_NEED_GET_DATA, issue_flags); + return; + } + /* +@@ -761,17 +763,18 @@ static inline void __ublk_rq_task_work(struct request *req) + mapped_bytes >> 9; + } + +- ubq_complete_io_cmd(io, UBLK_IO_RES_OK); ++ ubq_complete_io_cmd(io, UBLK_IO_RES_OK, issue_flags); + } + +-static inline void ublk_forward_io_cmds(struct ublk_queue *ubq) ++static inline void ublk_forward_io_cmds(struct ublk_queue *ubq, ++ unsigned issue_flags) + { + struct llist_node *io_cmds = llist_del_all(&ubq->io_cmds); + struct ublk_rq_data *data, *tmp; + + io_cmds = llist_reverse_order(io_cmds); + llist_for_each_entry_safe(data, tmp, io_cmds, node) +- __ublk_rq_task_work(blk_mq_rq_from_pdu(data)); ++ __ublk_rq_task_work(blk_mq_rq_from_pdu(data), issue_flags); + } + + static inline void ublk_abort_io_cmds(struct ublk_queue *ubq) +@@ -783,12 +786,12 @@ static inline void ublk_abort_io_cmds(struct ublk_queue *ubq) + __ublk_abort_rq(ubq, blk_mq_rq_from_pdu(data)); + } + +-static void ublk_rq_task_work_cb(struct io_uring_cmd *cmd) ++static void ublk_rq_task_work_cb(struct io_uring_cmd *cmd, unsigned issue_flags) + { + struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd); + struct ublk_queue *ubq = pdu->ubq; + +- ublk_forward_io_cmds(ubq); ++ ublk_forward_io_cmds(ubq, issue_flags); + } + + static void ublk_rq_task_work_fn(struct callback_head *work) +@@ -797,8 +800,9 @@ static void ublk_rq_task_work_fn(struct callback_head *work) + struct ublk_rq_data, work); + struct request *req = blk_mq_rq_from_pdu(data); + struct ublk_queue *ubq = req->mq_hctx->driver_data; ++ unsigned issue_flags = IO_URING_F_UNLOCKED; + +- ublk_forward_io_cmds(ubq); ++ ublk_forward_io_cmds(ubq, issue_flags); + } + + static void ublk_queue_cmd(struct ublk_queue *ubq, struct request *rq) +@@ -1052,7 +1056,8 @@ static void ublk_cancel_queue(struct ublk_queue *ubq) + struct ublk_io *io = &ubq->ios[i]; + + if (io->flags & UBLK_IO_FLAG_ACTIVE) +- io_uring_cmd_done(io->cmd, UBLK_IO_RES_ABORT, 0); ++ io_uring_cmd_done(io->cmd, UBLK_IO_RES_ABORT, 0, ++ IO_URING_F_UNLOCKED); + } + + /* all io commands are canceled */ +@@ -1295,7 +1300,7 @@ static int ublk_ch_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) + return -EIOCBQUEUED; + + out: +- io_uring_cmd_done(cmd, ret, 0); ++ io_uring_cmd_done(cmd, ret, 0, issue_flags); + pr_devel("%s: complete: cmd op %d, tag %d ret %x io_flags %x\n", + __func__, cmd_op, tag, ret, io->flags); + return -EIOCBQUEUED; +@@ -2053,7 +2058,7 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd, + break; + } + out: +- io_uring_cmd_done(cmd, ret, 0); ++ io_uring_cmd_done(cmd, ret, 0, issue_flags); + pr_devel("%s: cmd done ret %d cmd_op %x, dev id %d qid %d\n", + __func__, ret, cmd->cmd_op, header->dev_id, header->queue_id); + return -EIOCBQUEUED; +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c +index 435d81d6ffd9b..a78e80f9f65cd 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c +@@ -981,7 +981,12 @@ static bool amdgpu_atcs_pci_probe_handle(struct pci_dev *pdev) + */ + bool amdgpu_acpi_should_gpu_reset(struct amdgpu_device *adev) + { +- if (adev->flags & AMD_IS_APU) ++ if ((adev->flags & AMD_IS_APU) && ++ adev->gfx.imu.funcs) /* Not need to do mode2 reset for IMU enabled APUs */ ++ return false; ++ ++ if ((adev->flags & AMD_IS_APU) && ++ amdgpu_acpi_is_s3_active(adev)) + return false; + + if (amdgpu_sriov_vf(adev)) +diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +index f79b8e964140e..e191d38f3da62 100644 +--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c ++++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +@@ -1298,14 +1298,14 @@ static int kfd_ioctl_map_memory_to_gpu(struct file *filep, + args->n_success = i+1; + } + +- mutex_unlock(&p->mutex); +- + err = amdgpu_amdkfd_gpuvm_sync_memory(dev->adev, (struct kgd_mem *) mem, true); + if (err) { + pr_debug("Sync memory failed, wait interrupted by user signal\n"); + goto sync_memory_failed; + } + ++ mutex_unlock(&p->mutex); ++ + /* Flush TLBs after waiting for the page table updates to complete */ + for (i = 0; i < args->n_devices; i++) { + peer_pdd = kfd_process_device_data_by_id(p, devices_arr[i]); +@@ -1321,9 +1321,9 @@ get_process_device_data_failed: + bind_process_to_device_failed: + get_mem_obj_from_handle_failed: + map_memory_to_gpu_failed: ++sync_memory_failed: + mutex_unlock(&p->mutex); + copy_from_user_failed: +-sync_memory_failed: + kfree(devices_arr); + + return err; +@@ -1337,6 +1337,7 @@ static int kfd_ioctl_unmap_memory_from_gpu(struct file *filep, + void *mem; + long err = 0; + uint32_t *devices_arr = NULL, i; ++ bool flush_tlb; + + if (!args->n_devices) { + pr_debug("Device IDs array empty\n"); +@@ -1389,16 +1390,19 @@ static int kfd_ioctl_unmap_memory_from_gpu(struct file *filep, + } + args->n_success = i+1; + } +- mutex_unlock(&p->mutex); + +- if (kfd_flush_tlb_after_unmap(pdd->dev)) { ++ flush_tlb = kfd_flush_tlb_after_unmap(pdd->dev); ++ if (flush_tlb) { + err = amdgpu_amdkfd_gpuvm_sync_memory(pdd->dev->adev, + (struct kgd_mem *) mem, true); + if (err) { + pr_debug("Sync memory failed, wait interrupted by user signal\n"); + goto sync_memory_failed; + } ++ } ++ mutex_unlock(&p->mutex); + ++ if (flush_tlb) { + /* Flush TLBs after waiting for the page table updates to complete */ + for (i = 0; i < args->n_devices; i++) { + peer_pdd = kfd_process_device_data_by_id(p, devices_arr[i]); +@@ -1414,9 +1418,9 @@ static int kfd_ioctl_unmap_memory_from_gpu(struct file *filep, + bind_process_to_device_failed: + get_mem_obj_from_handle_failed: + unmap_memory_from_gpu_failed: ++sync_memory_failed: + mutex_unlock(&p->mutex); + copy_from_user_failed: +-sync_memory_failed: + kfree(devices_arr); + return err; + } +diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +index 22b077ac9a196..88bf6221d4bef 100644 +--- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c ++++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +@@ -295,7 +295,7 @@ static unsigned long svm_migrate_unsuccessful_pages(struct migrate_vma *migrate) + static int + svm_migrate_copy_to_vram(struct amdgpu_device *adev, struct svm_range *prange, + struct migrate_vma *migrate, struct dma_fence **mfence, +- dma_addr_t *scratch) ++ dma_addr_t *scratch, uint64_t ttm_res_offset) + { + uint64_t npages = migrate->npages; + struct device *dev = adev->dev; +@@ -305,19 +305,13 @@ svm_migrate_copy_to_vram(struct amdgpu_device *adev, struct svm_range *prange, + uint64_t i, j; + int r; + +- pr_debug("svms 0x%p [0x%lx 0x%lx]\n", prange->svms, prange->start, +- prange->last); ++ pr_debug("svms 0x%p [0x%lx 0x%lx 0x%llx]\n", prange->svms, prange->start, ++ prange->last, ttm_res_offset); + + src = scratch; + dst = (uint64_t *)(scratch + npages); + +- r = svm_range_vram_node_new(adev, prange, true); +- if (r) { +- dev_dbg(adev->dev, "fail %d to alloc vram\n", r); +- goto out; +- } +- +- amdgpu_res_first(prange->ttm_res, prange->offset << PAGE_SHIFT, ++ amdgpu_res_first(prange->ttm_res, ttm_res_offset, + npages << PAGE_SHIFT, &cursor); + for (i = j = 0; i < npages; i++) { + struct page *spage; +@@ -397,14 +391,14 @@ out_free_vram_pages: + migrate->dst[i + 3] = 0; + } + #endif +-out: ++ + return r; + } + + static long + svm_migrate_vma_to_vram(struct amdgpu_device *adev, struct svm_range *prange, + struct vm_area_struct *vma, uint64_t start, +- uint64_t end, uint32_t trigger) ++ uint64_t end, uint32_t trigger, uint64_t ttm_res_offset) + { + struct kfd_process *p = container_of(prange->svms, struct kfd_process, svms); + uint64_t npages = (end - start) >> PAGE_SHIFT; +@@ -457,7 +451,7 @@ svm_migrate_vma_to_vram(struct amdgpu_device *adev, struct svm_range *prange, + else + pr_debug("0x%lx pages migrated\n", cpages); + +- r = svm_migrate_copy_to_vram(adev, prange, &migrate, &mfence, scratch); ++ r = svm_migrate_copy_to_vram(adev, prange, &migrate, &mfence, scratch, ttm_res_offset); + migrate_vma_pages(&migrate); + + pr_debug("successful/cpages/npages 0x%lx/0x%lx/0x%lx\n", +@@ -505,6 +499,7 @@ svm_migrate_ram_to_vram(struct svm_range *prange, uint32_t best_loc, + unsigned long addr, start, end; + struct vm_area_struct *vma; + struct amdgpu_device *adev; ++ uint64_t ttm_res_offset; + unsigned long cpages = 0; + long r = 0; + +@@ -526,6 +521,13 @@ svm_migrate_ram_to_vram(struct svm_range *prange, uint32_t best_loc, + start = prange->start << PAGE_SHIFT; + end = (prange->last + 1) << PAGE_SHIFT; + ++ r = svm_range_vram_node_new(adev, prange, true); ++ if (r) { ++ dev_dbg(adev->dev, "fail %ld to alloc vram\n", r); ++ return r; ++ } ++ ttm_res_offset = prange->offset << PAGE_SHIFT; ++ + for (addr = start; addr < end;) { + unsigned long next; + +@@ -534,18 +536,21 @@ svm_migrate_ram_to_vram(struct svm_range *prange, uint32_t best_loc, + break; + + next = min(vma->vm_end, end); +- r = svm_migrate_vma_to_vram(adev, prange, vma, addr, next, trigger); ++ r = svm_migrate_vma_to_vram(adev, prange, vma, addr, next, trigger, ttm_res_offset); + if (r < 0) { + pr_debug("failed %ld to migrate\n", r); + break; + } else { + cpages += r; + } ++ ttm_res_offset += next - addr; + addr = next; + } + + if (cpages) + prange->actual_loc = best_loc; ++ else ++ svm_range_vram_node_free(prange); + + return r < 0 ? r : 0; + } +diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_module.c b/drivers/gpu/drm/amd/amdkfd/kfd_module.c +index 09b966dc37681..aee2212e52f69 100644 +--- a/drivers/gpu/drm/amd/amdkfd/kfd_module.c ++++ b/drivers/gpu/drm/amd/amdkfd/kfd_module.c +@@ -77,6 +77,7 @@ err_ioctl: + + static void kfd_exit(void) + { ++ kfd_cleanup_processes(); + kfd_debugfs_fini(); + kfd_process_destroy_wq(); + kfd_procfs_shutdown(); +diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +index bf610e3b683bb..6d6588b9beed7 100644 +--- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h ++++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +@@ -928,6 +928,7 @@ bool kfd_dev_is_large_bar(struct kfd_dev *dev); + + int kfd_process_create_wq(void); + void kfd_process_destroy_wq(void); ++void kfd_cleanup_processes(void); + struct kfd_process *kfd_create_process(struct file *filep); + struct kfd_process *kfd_get_process(const struct task_struct *task); + struct kfd_process *kfd_lookup_process_by_pasid(u32 pasid); +diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c +index dd351105c1bcf..7f68d51541e8e 100644 +--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c ++++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c +@@ -1167,6 +1167,17 @@ static void kfd_process_free_notifier(struct mmu_notifier *mn) + kfd_unref_process(container_of(mn, struct kfd_process, mmu_notifier)); + } + ++static void kfd_process_notifier_release_internal(struct kfd_process *p) ++{ ++ cancel_delayed_work_sync(&p->eviction_work); ++ cancel_delayed_work_sync(&p->restore_work); ++ ++ /* Indicate to other users that MM is no longer valid */ ++ p->mm = NULL; ++ ++ mmu_notifier_put(&p->mmu_notifier); ++} ++ + static void kfd_process_notifier_release(struct mmu_notifier *mn, + struct mm_struct *mm) + { +@@ -1181,17 +1192,22 @@ static void kfd_process_notifier_release(struct mmu_notifier *mn, + return; + + mutex_lock(&kfd_processes_mutex); ++ /* ++ * Do early return if table is empty. ++ * ++ * This could potentially happen if this function is called concurrently ++ * by mmu_notifier and by kfd_cleanup_pocesses. ++ * ++ */ ++ if (hash_empty(kfd_processes_table)) { ++ mutex_unlock(&kfd_processes_mutex); ++ return; ++ } + hash_del_rcu(&p->kfd_processes); + mutex_unlock(&kfd_processes_mutex); + synchronize_srcu(&kfd_processes_srcu); + +- cancel_delayed_work_sync(&p->eviction_work); +- cancel_delayed_work_sync(&p->restore_work); +- +- /* Indicate to other users that MM is no longer valid */ +- p->mm = NULL; +- +- mmu_notifier_put(&p->mmu_notifier); ++ kfd_process_notifier_release_internal(p); + } + + static const struct mmu_notifier_ops kfd_process_mmu_notifier_ops = { +@@ -1200,6 +1216,43 @@ static const struct mmu_notifier_ops kfd_process_mmu_notifier_ops = { + .free_notifier = kfd_process_free_notifier, + }; + ++/* ++ * This code handles the case when driver is being unloaded before all ++ * mm_struct are released. We need to safely free the kfd_process and ++ * avoid race conditions with mmu_notifier that might try to free them. ++ * ++ */ ++void kfd_cleanup_processes(void) ++{ ++ struct kfd_process *p; ++ struct hlist_node *p_temp; ++ unsigned int temp; ++ HLIST_HEAD(cleanup_list); ++ ++ /* ++ * Move all remaining kfd_process from the process table to a ++ * temp list for processing. Once done, callback from mmu_notifier ++ * release will not see the kfd_process in the table and do early return, ++ * avoiding double free issues. ++ */ ++ mutex_lock(&kfd_processes_mutex); ++ hash_for_each_safe(kfd_processes_table, temp, p_temp, p, kfd_processes) { ++ hash_del_rcu(&p->kfd_processes); ++ synchronize_srcu(&kfd_processes_srcu); ++ hlist_add_head(&p->kfd_processes, &cleanup_list); ++ } ++ mutex_unlock(&kfd_processes_mutex); ++ ++ hlist_for_each_entry_safe(p, p_temp, &cleanup_list, kfd_processes) ++ kfd_process_notifier_release_internal(p); ++ ++ /* ++ * Ensures that all outstanding free_notifier get called, triggering ++ * the release of the kfd_process struct. ++ */ ++ mmu_notifier_synchronize(); ++} ++ + static int kfd_process_init_cwsr_apu(struct kfd_process *p, struct file *filep) + { + unsigned long offset; +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 5137476ec18e6..4236539d9f932 100644 +--- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c ++++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +@@ -218,8 +218,8 @@ static int init_user_queue(struct process_queue_manager *pqm, + return 0; + + cleanup: +- if (dev->shared_resources.enable_mes) +- uninit_queue(*q); ++ uninit_queue(*q); ++ *q = NULL; + return retval; + } + +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +index 8561e9b017a2e..df74bc88e4600 100644 +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +@@ -208,6 +208,21 @@ bool needs_dsc_aux_workaround(struct dc_link *link) + return false; + } + ++bool is_synaptics_cascaded_panamera(struct dc_link *link, struct drm_dp_mst_port *port) ++{ ++ u8 branch_vendor_data[4] = { 0 }; // Vendor data 0x50C ~ 0x50F ++ ++ if (drm_dp_dpcd_read(port->mgr->aux, DP_BRANCH_VENDOR_SPECIFIC_START, &branch_vendor_data, 4) == 4) { ++ if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_90CC24 && ++ IS_SYNAPTICS_CASCADED_PANAMERA(link->dpcd_caps.branch_dev_name, branch_vendor_data)) { ++ DRM_INFO("Synaptics Cascaded MST hub\n"); ++ return true; ++ } ++ } ++ ++ return false; ++} ++ + static bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector) + { + struct dc_sink *dc_sink = aconnector->dc_sink; +@@ -231,6 +246,10 @@ static bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnecto + needs_dsc_aux_workaround(aconnector->dc_link)) + aconnector->dsc_aux = &aconnector->mst_port->dm_dp_aux.aux; + ++ /* synaptics cascaded MST hub case */ ++ if (!aconnector->dsc_aux && is_synaptics_cascaded_panamera(aconnector->dc_link, port)) ++ aconnector->dsc_aux = port->mgr->aux; ++ + if (!aconnector->dsc_aux) + return false; + +@@ -627,12 +646,25 @@ struct dsc_mst_fairness_params { + struct amdgpu_dm_connector *aconnector; + }; + +-static int kbps_to_peak_pbn(int kbps) ++static uint16_t get_fec_overhead_multiplier(struct dc_link *dc_link) ++{ ++ u8 link_coding_cap; ++ uint16_t fec_overhead_multiplier_x1000 = PBN_FEC_OVERHEAD_MULTIPLIER_8B_10B; ++ ++ link_coding_cap = dc_link_dp_mst_decide_link_encoding_format(dc_link); ++ if (link_coding_cap == DP_128b_132b_ENCODING) ++ fec_overhead_multiplier_x1000 = PBN_FEC_OVERHEAD_MULTIPLIER_128B_132B; ++ ++ return fec_overhead_multiplier_x1000; ++} ++ ++static int kbps_to_peak_pbn(int kbps, uint16_t fec_overhead_multiplier_x1000) + { + u64 peak_kbps = kbps; + + peak_kbps *= 1006; +- peak_kbps = div_u64(peak_kbps, 1000); ++ peak_kbps *= fec_overhead_multiplier_x1000; ++ peak_kbps = div_u64(peak_kbps, 1000 * 1000); + return (int) DIV64_U64_ROUND_UP(peak_kbps * 64, (54 * 8 * 1000)); + } + +@@ -719,11 +751,12 @@ static int increase_dsc_bpp(struct drm_atomic_state *state, + int link_timeslots_used; + int fair_pbn_alloc; + int ret = 0; ++ uint16_t fec_overhead_multiplier_x1000 = get_fec_overhead_multiplier(dc_link); + + for (i = 0; i < count; i++) { + if (vars[i + k].dsc_enabled) { + initial_slack[i] = +- kbps_to_peak_pbn(params[i].bw_range.max_kbps) - vars[i + k].pbn; ++ kbps_to_peak_pbn(params[i].bw_range.max_kbps, fec_overhead_multiplier_x1000) - vars[i + k].pbn; + bpp_increased[i] = false; + remaining_to_increase += 1; + } else { +@@ -819,6 +852,7 @@ static int try_disable_dsc(struct drm_atomic_state *state, + int next_index; + int remaining_to_try = 0; + int ret; ++ uint16_t fec_overhead_multiplier_x1000 = get_fec_overhead_multiplier(dc_link); + + for (i = 0; i < count; i++) { + if (vars[i + k].dsc_enabled +@@ -848,7 +882,7 @@ static int try_disable_dsc(struct drm_atomic_state *state, + if (next_index == -1) + break; + +- vars[next_index].pbn = kbps_to_peak_pbn(params[next_index].bw_range.stream_kbps); ++ vars[next_index].pbn = kbps_to_peak_pbn(params[next_index].bw_range.stream_kbps, fec_overhead_multiplier_x1000); + ret = drm_dp_atomic_find_time_slots(state, + params[next_index].port->mgr, + params[next_index].port, +@@ -861,7 +895,7 @@ static int try_disable_dsc(struct drm_atomic_state *state, + vars[next_index].dsc_enabled = false; + vars[next_index].bpp_x16 = 0; + } else { +- vars[next_index].pbn = kbps_to_peak_pbn(params[next_index].bw_range.max_kbps); ++ vars[next_index].pbn = kbps_to_peak_pbn(params[next_index].bw_range.max_kbps, fec_overhead_multiplier_x1000); + ret = drm_dp_atomic_find_time_slots(state, + params[next_index].port->mgr, + params[next_index].port, +@@ -890,6 +924,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state, + int count = 0; + int i, k, ret; + bool debugfs_overwrite = false; ++ uint16_t fec_overhead_multiplier_x1000 = get_fec_overhead_multiplier(dc_link); + + memset(params, 0, sizeof(params)); + +@@ -951,7 +986,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state, + /* Try no compression */ + for (i = 0; i < count; i++) { + vars[i + k].aconnector = params[i].aconnector; +- vars[i + k].pbn = kbps_to_peak_pbn(params[i].bw_range.stream_kbps); ++ vars[i + k].pbn = kbps_to_peak_pbn(params[i].bw_range.stream_kbps, fec_overhead_multiplier_x1000); + vars[i + k].dsc_enabled = false; + vars[i + k].bpp_x16 = 0; + ret = drm_dp_atomic_find_time_slots(state, params[i].port->mgr, params[i].port, +@@ -970,7 +1005,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state, + /* Try max compression */ + for (i = 0; i < count; i++) { + if (params[i].compression_possible && params[i].clock_force_enable != DSC_CLK_FORCE_DISABLE) { +- vars[i + k].pbn = kbps_to_peak_pbn(params[i].bw_range.min_kbps); ++ vars[i + k].pbn = kbps_to_peak_pbn(params[i].bw_range.min_kbps, fec_overhead_multiplier_x1000); + vars[i + k].dsc_enabled = true; + vars[i + k].bpp_x16 = params[i].bw_range.min_target_bpp_x16; + ret = drm_dp_atomic_find_time_slots(state, params[i].port->mgr, +@@ -978,7 +1013,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state, + if (ret < 0) + return ret; + } else { +- vars[i + k].pbn = kbps_to_peak_pbn(params[i].bw_range.stream_kbps); ++ vars[i + k].pbn = kbps_to_peak_pbn(params[i].bw_range.stream_kbps, fec_overhead_multiplier_x1000); + vars[i + k].dsc_enabled = false; + vars[i + k].bpp_x16 = 0; + ret = drm_dp_atomic_find_time_slots(state, params[i].port->mgr, +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h +index 97fd70df531bf..1e4ede1e57abd 100644 +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h +@@ -34,6 +34,21 @@ + #define SYNAPTICS_RC_OFFSET 0x4BC + #define SYNAPTICS_RC_DATA 0x4C0 + ++#define DP_BRANCH_VENDOR_SPECIFIC_START 0x50C ++ ++/** ++ * Panamera MST Hub detection ++ * Offset DPCD 050Eh == 0x5A indicates cascaded MST hub case ++ * Check from beginning of branch device vendor specific field (050Ch) ++ */ ++#define IS_SYNAPTICS_PANAMERA(branchDevName) (((int)branchDevName[4] & 0xF0) == 0x50 ? 1 : 0) ++#define BRANCH_HW_REVISION_PANAMERA_A2 0x10 ++#define SYNAPTICS_CASCADED_HUB_ID 0x5A ++#define IS_SYNAPTICS_CASCADED_PANAMERA(devName, data) ((IS_SYNAPTICS_PANAMERA(devName) && ((int)data[2] == SYNAPTICS_CASCADED_HUB_ID)) ? 1 : 0) ++ ++#define PBN_FEC_OVERHEAD_MULTIPLIER_8B_10B 1031 ++#define PBN_FEC_OVERHEAD_MULTIPLIER_128B_132B 1000 ++ + struct amdgpu_display_manager; + struct amdgpu_dm_connector; + +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +index 3fa2da1496398..fc594ea5bbc1d 100644 +--- a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c ++++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +@@ -91,7 +91,15 @@ static void *etnaviv_gem_prime_vmap_impl(struct etnaviv_gem_object *etnaviv_obj) + static int etnaviv_gem_prime_mmap_obj(struct etnaviv_gem_object *etnaviv_obj, + struct vm_area_struct *vma) + { +- return dma_buf_mmap(etnaviv_obj->base.dma_buf, vma, 0); ++ int ret; ++ ++ ret = dma_buf_mmap(etnaviv_obj->base.dma_buf, vma, 0); ++ if (!ret) { ++ /* Drop the reference acquired by drm_gem_mmap_obj(). */ ++ drm_gem_object_put(&etnaviv_obj->base); ++ } ++ ++ return ret; + } + + static const struct etnaviv_gem_ops etnaviv_gem_prime_ops = { +diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c +index 6bda4274eae92..c85757f55112b 100644 +--- a/drivers/gpu/drm/i915/display/intel_color.c ++++ b/drivers/gpu/drm/i915/display/intel_color.c +@@ -499,6 +499,22 @@ static void icl_color_commit_noarm(const struct intel_crtc_state *crtc_state) + icl_load_csc_matrix(crtc_state); + } + ++static void skl_color_commit_noarm(const struct intel_crtc_state *crtc_state) ++{ ++ /* ++ * Possibly related to display WA #1184, SKL CSC loses the latched ++ * CSC coeff/offset register values if the CSC registers are disarmed ++ * between DC5 exit and PSR exit. This will cause the plane(s) to ++ * output all black (until CSC_MODE is rearmed and properly latched). ++ * Once PSR exit (and proper register latching) has occurred the ++ * danger is over. Thus when PSR is enabled the CSC coeff/offset ++ * register programming will be peformed from skl_color_commit_arm() ++ * which is called after PSR exit. ++ */ ++ if (!crtc_state->has_psr) ++ ilk_load_csc_matrix(crtc_state); ++} ++ + static void ilk_color_commit_noarm(const struct intel_crtc_state *crtc_state) + { + ilk_load_csc_matrix(crtc_state); +@@ -541,6 +557,9 @@ static void skl_color_commit_arm(const struct intel_crtc_state *crtc_state) + enum pipe pipe = crtc->pipe; + u32 val = 0; + ++ if (crtc_state->has_psr) ++ ilk_load_csc_matrix(crtc_state); ++ + /* + * We don't (yet) allow userspace to control the pipe background color, + * so force it to black, but apply pipe gamma and CSC appropriately +@@ -2171,7 +2190,7 @@ static const struct intel_color_funcs icl_color_funcs = { + + static const struct intel_color_funcs glk_color_funcs = { + .color_check = glk_color_check, +- .color_commit_noarm = ilk_color_commit_noarm, ++ .color_commit_noarm = skl_color_commit_noarm, + .color_commit_arm = skl_color_commit_arm, + .load_luts = glk_load_luts, + .read_luts = glk_read_luts, +@@ -2179,7 +2198,7 @@ static const struct intel_color_funcs glk_color_funcs = { + + static const struct intel_color_funcs skl_color_funcs = { + .color_check = ivb_color_check, +- .color_commit_noarm = ilk_color_commit_noarm, ++ .color_commit_noarm = skl_color_commit_noarm, + .color_commit_arm = skl_color_commit_arm, + .load_luts = bdw_load_luts, + .read_luts = NULL, +diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c +index e0d36edd1e3fb..3f3982ae9974b 100644 +--- a/drivers/gpu/drm/i915/display/intel_display.c ++++ b/drivers/gpu/drm/i915/display/intel_display.c +@@ -7124,6 +7124,8 @@ static void intel_update_crtc(struct intel_atomic_state *state, + + intel_fbc_update(state, crtc); + ++ drm_WARN_ON(&i915->drm, !intel_display_power_is_enabled(i915, POWER_DOMAIN_DC_OFF)); ++ + if (!modeset && + (new_crtc_state->uapi.color_mgmt_changed || + new_crtc_state->update_pipe)) +@@ -7500,8 +7502,28 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) + drm_atomic_helper_wait_for_dependencies(&state->base); + drm_dp_mst_atomic_wait_for_dependencies(&state->base); + +- if (state->modeset) +- wakeref = intel_display_power_get(dev_priv, POWER_DOMAIN_MODESET); ++ /* ++ * During full modesets we write a lot of registers, wait ++ * for PLLs, etc. Doing that while DC states are enabled ++ * is not a good idea. ++ * ++ * During fastsets and other updates we also need to ++ * disable DC states due to the following scenario: ++ * 1. DC5 exit and PSR exit happen ++ * 2. Some or all _noarm() registers are written ++ * 3. Due to some long delay PSR is re-entered ++ * 4. DC5 entry -> DMC saves the already written new ++ * _noarm() registers and the old not yet written ++ * _arm() registers ++ * 5. DC5 exit -> DMC restores a mixture of old and ++ * new register values and arms the update ++ * 6. PSR exit -> hardware latches a mixture of old and ++ * new register values -> corrupted frame, or worse ++ * 7. New _arm() registers are finally written ++ * 8. Hardware finally latches a complete set of new ++ * register values, and subsequent frames will be OK again ++ */ ++ wakeref = intel_display_power_get(dev_priv, POWER_DOMAIN_DC_OFF); + + intel_atomic_prepare_plane_clear_colors(state); + +@@ -7640,8 +7662,8 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) + * the culprit. + */ + intel_uncore_arm_unclaimed_mmio_detection(&dev_priv->uncore); +- intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET, wakeref); + } ++ intel_display_power_put(dev_priv, POWER_DOMAIN_DC_OFF, wakeref); + intel_runtime_pm_put(&dev_priv->runtime_pm, state->wakeref); + + /* +diff --git a/drivers/gpu/drm/i915/display/intel_dpt.c b/drivers/gpu/drm/i915/display/intel_dpt.c +index ac587647e1f50..a3893aff38611 100644 +--- a/drivers/gpu/drm/i915/display/intel_dpt.c ++++ b/drivers/gpu/drm/i915/display/intel_dpt.c +@@ -300,6 +300,7 @@ intel_dpt_create(struct intel_framebuffer *fb) + vm->pte_encode = gen8_ggtt_pte_encode; + + dpt->obj = dpt_obj; ++ dpt->obj->is_dpt = true; + + return &dpt->vm; + } +@@ -308,5 +309,6 @@ void intel_dpt_destroy(struct i915_address_space *vm) + { + struct i915_dpt *dpt = i915_vm_to_dpt(vm); + ++ dpt->obj->is_dpt = false; + i915_vm_put(&dpt->vm); + } +diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c +index e5af955b5600f..8d6dac32c8960 100644 +--- a/drivers/gpu/drm/i915/display/intel_tc.c ++++ b/drivers/gpu/drm/i915/display/intel_tc.c +@@ -440,9 +440,9 @@ static bool icl_tc_phy_is_owned(struct intel_digital_port *dig_port) + PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia)); + if (val == 0xffffffff) { + drm_dbg_kms(&i915->drm, +- "Port %s: PHY in TCCOLD, assume safe mode\n", ++ "Port %s: PHY in TCCOLD, assume not owned\n", + dig_port->tc_port_name); +- return true; ++ return false; + } + + return val & DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx); +diff --git a/drivers/gpu/drm/i915/gem/i915_gem_lmem.c b/drivers/gpu/drm/i915/gem/i915_gem_lmem.c +index 8949fb0a944f6..3198b64ad7dbc 100644 +--- a/drivers/gpu/drm/i915/gem/i915_gem_lmem.c ++++ b/drivers/gpu/drm/i915/gem/i915_gem_lmem.c +@@ -127,7 +127,8 @@ i915_gem_object_create_lmem_from_data(struct drm_i915_private *i915, + + memcpy(map, data, size); + +- i915_gem_object_unpin_map(obj); ++ i915_gem_object_flush_map(obj); ++ __i915_gem_object_release_map(obj); + + return obj; + } +diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h +index 1723af9b0f6a2..ea951e2f55b17 100644 +--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h ++++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h +@@ -319,7 +319,7 @@ i915_gem_object_never_mmap(const struct drm_i915_gem_object *obj) + static inline bool + i915_gem_object_is_framebuffer(const struct drm_i915_gem_object *obj) + { +- return READ_ONCE(obj->frontbuffer); ++ return READ_ONCE(obj->frontbuffer) || obj->is_dpt; + } + + static inline unsigned int +diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h +index ab4c2f90a5643..1d0d8ee9d707d 100644 +--- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h ++++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h +@@ -491,6 +491,9 @@ struct drm_i915_gem_object { + */ + unsigned int cache_dirty:1; + ++ /* @is_dpt: Object houses a display page table (DPT) */ ++ unsigned int is_dpt:1; ++ + /** + * @read_domains: Read memory domains. + * +diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +index 41c93a18d5cb3..32a3c42ec45b1 100644 +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +@@ -356,7 +356,7 @@ static const struct dpu_caps sc8180x_dpu_caps = { + static const struct dpu_caps sm8250_dpu_caps = { + .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH, + .max_mixer_blendstages = 0xb, +- .qseed_type = DPU_SSPP_SCALER_QSEED3LITE, ++ .qseed_type = DPU_SSPP_SCALER_QSEED4, + .smart_dma_rev = DPU_SSPP_SMART_DMA_V2, /* TODO: v2.5 */ + .ubwc_version = DPU_HW_UBWC_VER_40, + .has_src_split = true, +@@ -855,22 +855,22 @@ static const struct dpu_sspp_cfg sc7180_sspp[] = { + }; + + static const struct dpu_sspp_sub_blks sm8250_vig_sblk_0 = +- _VIG_SBLK("0", 5, DPU_SSPP_SCALER_QSEED3LITE); ++ _VIG_SBLK("0", 5, DPU_SSPP_SCALER_QSEED4); + static const struct dpu_sspp_sub_blks sm8250_vig_sblk_1 = +- _VIG_SBLK("1", 6, DPU_SSPP_SCALER_QSEED3LITE); ++ _VIG_SBLK("1", 6, DPU_SSPP_SCALER_QSEED4); + static const struct dpu_sspp_sub_blks sm8250_vig_sblk_2 = +- _VIG_SBLK("2", 7, DPU_SSPP_SCALER_QSEED3LITE); ++ _VIG_SBLK("2", 7, DPU_SSPP_SCALER_QSEED4); + static const struct dpu_sspp_sub_blks sm8250_vig_sblk_3 = +- _VIG_SBLK("3", 8, DPU_SSPP_SCALER_QSEED3LITE); ++ _VIG_SBLK("3", 8, DPU_SSPP_SCALER_QSEED4); + + static const struct dpu_sspp_cfg sm8250_sspp[] = { +- SSPP_BLK("sspp_0", SSPP_VIG0, 0x4000, VIG_SM8250_MASK, ++ SSPP_BLK("sspp_0", SSPP_VIG0, 0x4000, VIG_SC7180_MASK, + sm8250_vig_sblk_0, 0, SSPP_TYPE_VIG, DPU_CLK_CTRL_VIG0), +- SSPP_BLK("sspp_1", SSPP_VIG1, 0x6000, VIG_SM8250_MASK, ++ SSPP_BLK("sspp_1", SSPP_VIG1, 0x6000, VIG_SC7180_MASK, + sm8250_vig_sblk_1, 4, SSPP_TYPE_VIG, DPU_CLK_CTRL_VIG1), +- SSPP_BLK("sspp_2", SSPP_VIG2, 0x8000, VIG_SM8250_MASK, ++ SSPP_BLK("sspp_2", SSPP_VIG2, 0x8000, VIG_SC7180_MASK, + sm8250_vig_sblk_2, 8, SSPP_TYPE_VIG, DPU_CLK_CTRL_VIG2), +- SSPP_BLK("sspp_3", SSPP_VIG3, 0xa000, VIG_SM8250_MASK, ++ SSPP_BLK("sspp_3", SSPP_VIG3, 0xa000, VIG_SC7180_MASK, + sm8250_vig_sblk_3, 12, SSPP_TYPE_VIG, DPU_CLK_CTRL_VIG3), + SSPP_BLK("sspp_8", SSPP_DMA0, 0x24000, DMA_SDM845_MASK, + sdm845_dma_sblk_0, 1, SSPP_TYPE_DMA, DPU_CLK_CTRL_DMA0), +@@ -1180,6 +1180,13 @@ static const struct dpu_pingpong_cfg sm8150_pp[] = { + -1), + }; + ++static const struct dpu_pingpong_cfg sc7280_pp[] = { ++ PP_BLK("pingpong_0", PINGPONG_0, 0x69000, 0, sc7280_pp_sblk, -1, -1), ++ PP_BLK("pingpong_1", PINGPONG_1, 0x6a000, 0, sc7280_pp_sblk, -1, -1), ++ PP_BLK("pingpong_2", PINGPONG_2, 0x6b000, 0, sc7280_pp_sblk, -1, -1), ++ PP_BLK("pingpong_3", PINGPONG_3, 0x6c000, 0, sc7280_pp_sblk, -1, -1), ++}; ++ + static struct dpu_pingpong_cfg qcm2290_pp[] = { + PP_BLK("pingpong_0", PINGPONG_0, 0x70000, 0, sdm845_pp_sblk, + DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 8), +@@ -1203,13 +1210,6 @@ static const struct dpu_merge_3d_cfg sm8150_merge_3d[] = { + MERGE_3D_BLK("merge_3d_2", MERGE_3D_2, 0x83200), + }; + +-static const struct dpu_pingpong_cfg sc7280_pp[] = { +- PP_BLK("pingpong_0", PINGPONG_0, 0x59000, 0, sc7280_pp_sblk, -1, -1), +- PP_BLK("pingpong_1", PINGPONG_1, 0x6a000, 0, sc7280_pp_sblk, -1, -1), +- PP_BLK("pingpong_2", PINGPONG_2, 0x6b000, 0, sc7280_pp_sblk, -1, -1), +- PP_BLK("pingpong_3", PINGPONG_3, 0x6c000, 0, sc7280_pp_sblk, -1, -1), +-}; +- + /************************************************************* + * DSC sub blocks config + *************************************************************/ +diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c +index f060ac7376e69..cfeb24d40d378 100644 +--- a/drivers/idle/intel_idle.c ++++ b/drivers/idle/intel_idle.c +@@ -168,7 +168,13 @@ static __cpuidle int intel_idle_irq(struct cpuidle_device *dev, + + raw_local_irq_enable(); + ret = __intel_idle(dev, drv, index); +- raw_local_irq_disable(); ++ ++ /* ++ * The lockdep hardirqs state may be changed to 'on' with timer ++ * tick interrupt followed by __do_softirq(). Use local_irq_disable() ++ * to keep the hardirqs state correct. ++ */ ++ local_irq_disable(); + + return ret; + } +diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c +index 2959d80f7fdb6..cd36cf7165423 100644 +--- a/drivers/input/joystick/xpad.c ++++ b/drivers/input/joystick/xpad.c +@@ -779,9 +779,6 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d + input_report_key(dev, BTN_C, data[8]); + input_report_key(dev, BTN_Z, data[9]); + +- /* Profile button has a value of 0-3, so it is reported as an axis */ +- if (xpad->mapping & MAP_PROFILE_BUTTON) +- input_report_abs(dev, ABS_PROFILE, data[34]); + + input_sync(dev); + } +@@ -1059,6 +1056,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char + (__u16) le16_to_cpup((__le16 *)(data + 8))); + } + ++ /* Profile button has a value of 0-3, so it is reported as an axis */ ++ if (xpad->mapping & MAP_PROFILE_BUTTON) ++ input_report_abs(dev, ABS_PROFILE, data[34]); ++ + /* paddle handling */ + /* based on SDL's SDL_hidapi_xboxone.c */ + if (xpad->mapping & MAP_PADDLES) { +diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c +index 4a6b33bbe7eaf..dd08ce97e7c90 100644 +--- a/drivers/input/mouse/alps.c ++++ b/drivers/input/mouse/alps.c +@@ -852,8 +852,8 @@ static void alps_process_packet_v6(struct psmouse *psmouse) + x = y = z = 0; + + /* Divide 4 since trackpoint's speed is too fast */ +- input_report_rel(dev2, REL_X, (char)x / 4); +- input_report_rel(dev2, REL_Y, -((char)y / 4)); ++ input_report_rel(dev2, REL_X, (s8)x / 4); ++ input_report_rel(dev2, REL_Y, -((s8)y / 4)); + + psmouse_report_standard_buttons(dev2, packet[3]); + +@@ -1104,8 +1104,8 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse) + ((packet[3] & 0x20) << 1); + z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1); + +- input_report_rel(dev2, REL_X, (char)x); +- input_report_rel(dev2, REL_Y, -((char)y)); ++ input_report_rel(dev2, REL_X, (s8)x); ++ input_report_rel(dev2, REL_Y, -((s8)y)); + input_report_abs(dev2, ABS_PRESSURE, z); + + psmouse_report_standard_buttons(dev2, packet[1]); +@@ -2294,20 +2294,20 @@ static int alps_get_v3_v7_resolution(struct psmouse *psmouse, int reg_pitch) + if (reg < 0) + return reg; + +- x_pitch = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */ ++ x_pitch = (s8)(reg << 4) >> 4; /* sign extend lower 4 bits */ + x_pitch = 50 + 2 * x_pitch; /* In 0.1 mm units */ + +- y_pitch = (char)reg >> 4; /* sign extend upper 4 bits */ ++ y_pitch = (s8)reg >> 4; /* sign extend upper 4 bits */ + y_pitch = 36 + 2 * y_pitch; /* In 0.1 mm units */ + + reg = alps_command_mode_read_reg(psmouse, reg_pitch + 1); + if (reg < 0) + return reg; + +- x_electrode = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */ ++ x_electrode = (s8)(reg << 4) >> 4; /* sign extend lower 4 bits */ + x_electrode = 17 + x_electrode; + +- y_electrode = (char)reg >> 4; /* sign extend upper 4 bits */ ++ y_electrode = (s8)reg >> 4; /* sign extend upper 4 bits */ + y_electrode = 13 + y_electrode; + + x_phys = x_pitch * (x_electrode - 1); /* In 0.1 mm units */ +diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c +index 6fd5fff0cbfff..c74b99077d16a 100644 +--- a/drivers/input/mouse/focaltech.c ++++ b/drivers/input/mouse/focaltech.c +@@ -202,8 +202,8 @@ static void focaltech_process_rel_packet(struct psmouse *psmouse, + state->pressed = packet[0] >> 7; + finger1 = ((packet[0] >> 4) & 0x7) - 1; + if (finger1 < FOC_MAX_FINGERS) { +- state->fingers[finger1].x += (char)packet[1]; +- state->fingers[finger1].y += (char)packet[2]; ++ state->fingers[finger1].x += (s8)packet[1]; ++ state->fingers[finger1].y += (s8)packet[2]; + } else { + psmouse_err(psmouse, "First finger in rel packet invalid: %d\n", + finger1); +@@ -218,8 +218,8 @@ static void focaltech_process_rel_packet(struct psmouse *psmouse, + */ + finger2 = ((packet[3] >> 4) & 0x7) - 1; + if (finger2 < FOC_MAX_FINGERS) { +- state->fingers[finger2].x += (char)packet[4]; +- state->fingers[finger2].y += (char)packet[5]; ++ state->fingers[finger2].x += (s8)packet[4]; ++ state->fingers[finger2].y += (s8)packet[5]; + } + } + +diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h +index efc61736099b9..028e45bd050bf 100644 +--- a/drivers/input/serio/i8042-acpipnpio.h ++++ b/drivers/input/serio/i8042-acpipnpio.h +@@ -610,6 +610,14 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { + }, + .driver_data = (void *)(SERIO_QUIRK_NOMUX) + }, ++ { ++ /* Fujitsu Lifebook A574/H */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "FMVA0501PZ"), ++ }, ++ .driver_data = (void *)(SERIO_QUIRK_NOMUX) ++ }, + { + /* Gigabyte M912 */ + .matches = { +@@ -1116,6 +1124,20 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) + }, ++ { ++ /* ++ * Setting SERIO_QUIRK_NOMUX or SERIO_QUIRK_RESET_ALWAYS makes ++ * the keyboard very laggy for ~5 seconds after boot and ++ * sometimes also after resume. ++ * However both are required for the keyboard to not fail ++ * completely sometimes after boot or resume. ++ */ ++ .matches = { ++ DMI_MATCH(DMI_BOARD_NAME, "N150CU"), ++ }, ++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | ++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) ++ }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"), +@@ -1123,6 +1145,20 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { + .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | + SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) + }, ++ { ++ /* ++ * Setting SERIO_QUIRK_NOMUX or SERIO_QUIRK_RESET_ALWAYS makes ++ * the keyboard very laggy for ~5 seconds after boot and ++ * sometimes also after resume. ++ * However both are required for the keyboard to not fail ++ * completely sometimes after boot or resume. ++ */ ++ .matches = { ++ DMI_MATCH(DMI_BOARD_NAME, "NHxxRZQ"), ++ }, ++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | ++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) ++ }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), +diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c +index c281e49826c23..25e575183dd18 100644 +--- a/drivers/input/touchscreen/goodix.c ++++ b/drivers/input/touchscreen/goodix.c +@@ -124,10 +124,18 @@ static const unsigned long goodix_irq_flags[] = { + static const struct dmi_system_id nine_bytes_report[] = { + #if defined(CONFIG_DMI) && defined(CONFIG_X86) + { +- .ident = "Lenovo YogaBook", +- /* YB1-X91L/F and YB1-X90L/F */ ++ /* Lenovo Yoga Book X90F / X90L */ + .matches = { +- DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9") ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), ++ } ++ }, ++ { ++ /* Lenovo Yoga Book X91F / X91L */ ++ .matches = { ++ /* Non exact match to match F + L versions */ ++ DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"), + } + }, + #endif +diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c +index bc94059a5b870..f800989ea0462 100644 +--- a/drivers/iommu/intel/dmar.c ++++ b/drivers/iommu/intel/dmar.c +@@ -1057,7 +1057,8 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd) + } + + err = -EINVAL; +- if (cap_sagaw(iommu->cap) == 0) { ++ if (!cap_sagaw(iommu->cap) && ++ (!ecap_smts(iommu->ecap) || ecap_slts(iommu->ecap))) { + pr_info("%s: No supported address widths. Not attempting DMA translation.\n", + iommu->name); + drhd->ignored = 1; +diff --git a/drivers/md/dm.c b/drivers/md/dm.c +index 89bf28ed874c2..38d8aa21ef7ac 100644 +--- a/drivers/md/dm.c ++++ b/drivers/md/dm.c +@@ -1522,6 +1522,8 @@ static int __send_duplicate_bios(struct clone_info *ci, struct dm_target *ti, + ret = 1; + break; + default: ++ if (len) ++ setup_split_accounting(ci, *len); + /* dm_accept_partial_bio() is not supported with shared tio->len_ptr */ + alloc_multiple_bios(&blist, ci, ti, num_bios); + while ((clone = bio_list_pop(&blist))) { +diff --git a/drivers/md/md.c b/drivers/md/md.c +index 0368b3c51c7f7..d5c362b1602b6 100644 +--- a/drivers/md/md.c ++++ b/drivers/md/md.c +@@ -3152,6 +3152,9 @@ slot_store(struct md_rdev *rdev, const char *buf, size_t len) + err = kstrtouint(buf, 10, (unsigned int *)&slot); + if (err < 0) + return err; ++ if (slot < 0) ++ /* overflow */ ++ return -ENOSPC; + } + if (rdev->mddev->pers && slot == -1) { + /* Setting 'slot' on an active array requires also +diff --git a/drivers/mtd/nand/ecc-mxic.c b/drivers/mtd/nand/ecc-mxic.c +index 8afdca731b874..6b487ffe2f2dc 100644 +--- a/drivers/mtd/nand/ecc-mxic.c ++++ b/drivers/mtd/nand/ecc-mxic.c +@@ -429,6 +429,7 @@ static int mxic_ecc_data_xfer_wait_for_completion(struct mxic_ecc_engine *mxic) + mxic_ecc_enable_int(mxic); + ret = wait_for_completion_timeout(&mxic->complete, + msecs_to_jiffies(1000)); ++ ret = ret ? 0 : -ETIMEDOUT; + mxic_ecc_disable_int(mxic); + } else { + ret = readl_poll_timeout(mxic->regs + INTRPT_STS, val, +diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c +index 5ee01231ac4cd..a28574c009003 100644 +--- a/drivers/mtd/nand/raw/meson_nand.c ++++ b/drivers/mtd/nand/raw/meson_nand.c +@@ -176,6 +176,7 @@ struct meson_nfc { + + dma_addr_t daddr; + dma_addr_t iaddr; ++ u32 info_bytes; + + unsigned long assigned_cs; + }; +@@ -503,6 +504,7 @@ static int meson_nfc_dma_buffer_setup(struct nand_chip *nand, void *databuf, + nfc->daddr, datalen, dir); + return ret; + } ++ nfc->info_bytes = infolen; + cmd = GENCMDIADDRL(NFC_CMD_AIL, nfc->iaddr); + writel(cmd, nfc->reg_base + NFC_REG_CMD); + +@@ -520,8 +522,10 @@ static void meson_nfc_dma_buffer_release(struct nand_chip *nand, + struct meson_nfc *nfc = nand_get_controller_data(nand); + + dma_unmap_single(nfc->dev, nfc->daddr, datalen, dir); +- if (infolen) ++ if (infolen) { + dma_unmap_single(nfc->dev, nfc->iaddr, infolen, dir); ++ nfc->info_bytes = 0; ++ } + } + + static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len) +@@ -710,6 +714,8 @@ static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc, + usleep_range(10, 15); + /* info is updated by nfc dma engine*/ + smp_rmb(); ++ dma_sync_single_for_cpu(nfc->dev, nfc->iaddr, nfc->info_bytes, ++ DMA_FROM_DEVICE); + ret = *info & ECC_COMPLETE; + } while (!ret); + } +@@ -991,7 +997,7 @@ static const struct mtd_ooblayout_ops meson_ooblayout_ops = { + + static int meson_nfc_clk_init(struct meson_nfc *nfc) + { +- struct clk_parent_data nfc_divider_parent_data[1]; ++ struct clk_parent_data nfc_divider_parent_data[1] = {0}; + struct clk_init_data init = {0}; + int ret; + +diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c +index bd3b133e7085b..22250ae222b5b 100644 +--- a/drivers/net/dsa/microchip/ksz8795.c ++++ b/drivers/net/dsa/microchip/ksz8795.c +@@ -907,15 +907,14 @@ int ksz8_fdb_dump(struct ksz_device *dev, int port, + u16 entries = 0; + u8 timestamp = 0; + u8 fid; +- u8 member; +- struct alu_struct alu; ++ u8 src_port; ++ u8 mac[ETH_ALEN]; + + do { +- alu.is_static = false; +- ret = ksz8_r_dyn_mac_table(dev, i, alu.mac, &fid, &member, ++ ret = ksz8_r_dyn_mac_table(dev, i, mac, &fid, &src_port, + ×tamp, &entries); +- if (!ret && (member & BIT(port))) { +- ret = cb(alu.mac, alu.fid, alu.is_static, data); ++ if (!ret && port == src_port) { ++ ret = cb(mac, fid, false, data); + if (ret) + break; + } +diff --git a/drivers/net/dsa/microchip/ksz8863_smi.c b/drivers/net/dsa/microchip/ksz8863_smi.c +index ddb40838181ef..ed77ac2228951 100644 +--- a/drivers/net/dsa/microchip/ksz8863_smi.c ++++ b/drivers/net/dsa/microchip/ksz8863_smi.c +@@ -82,22 +82,16 @@ static const struct regmap_bus regmap_smi[] = { + { + .read = ksz8863_mdio_read, + .write = ksz8863_mdio_write, +- .max_raw_read = 1, +- .max_raw_write = 1, + }, + { + .read = ksz8863_mdio_read, + .write = ksz8863_mdio_write, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +- .max_raw_read = 2, +- .max_raw_write = 2, + }, + { + .read = ksz8863_mdio_read, + .write = ksz8863_mdio_write, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +- .max_raw_read = 4, +- .max_raw_write = 4, + } + }; + +@@ -108,7 +102,6 @@ static const struct regmap_config ksz8863_regmap_config[] = { + .pad_bits = 24, + .val_bits = 8, + .cache_type = REGCACHE_NONE, +- .use_single_read = 1, + .lock = ksz_regmap_lock, + .unlock = ksz_regmap_unlock, + }, +@@ -118,7 +111,6 @@ static const struct regmap_config ksz8863_regmap_config[] = { + .pad_bits = 24, + .val_bits = 16, + .cache_type = REGCACHE_NONE, +- .use_single_read = 1, + .lock = ksz_regmap_lock, + .unlock = ksz_regmap_unlock, + }, +@@ -128,7 +120,6 @@ static const struct regmap_config ksz8863_regmap_config[] = { + .pad_bits = 24, + .val_bits = 32, + .cache_type = REGCACHE_NONE, +- .use_single_read = 1, + .lock = ksz_regmap_lock, + .unlock = ksz_regmap_unlock, + } +diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c +index 07f6776bba12b..3d59298eaa5cf 100644 +--- a/drivers/net/dsa/microchip/ksz_common.c ++++ b/drivers/net/dsa/microchip/ksz_common.c +@@ -357,13 +357,13 @@ static const u32 ksz8863_masks[] = { + [VLAN_TABLE_VALID] = BIT(19), + [STATIC_MAC_TABLE_VALID] = BIT(19), + [STATIC_MAC_TABLE_USE_FID] = BIT(21), +- [STATIC_MAC_TABLE_FID] = GENMASK(29, 26), ++ [STATIC_MAC_TABLE_FID] = GENMASK(25, 22), + [STATIC_MAC_TABLE_OVERRIDE] = BIT(20), + [STATIC_MAC_TABLE_FWD_PORTS] = GENMASK(18, 16), +- [DYNAMIC_MAC_TABLE_ENTRIES_H] = GENMASK(5, 0), +- [DYNAMIC_MAC_TABLE_MAC_EMPTY] = BIT(7), ++ [DYNAMIC_MAC_TABLE_ENTRIES_H] = GENMASK(1, 0), ++ [DYNAMIC_MAC_TABLE_MAC_EMPTY] = BIT(2), + [DYNAMIC_MAC_TABLE_NOT_READY] = BIT(7), +- [DYNAMIC_MAC_TABLE_ENTRIES] = GENMASK(31, 28), ++ [DYNAMIC_MAC_TABLE_ENTRIES] = GENMASK(31, 24), + [DYNAMIC_MAC_TABLE_FID] = GENMASK(19, 16), + [DYNAMIC_MAC_TABLE_SRC_PORT] = GENMASK(21, 20), + [DYNAMIC_MAC_TABLE_TIMESTAMP] = GENMASK(23, 22), +@@ -373,10 +373,10 @@ static u8 ksz8863_shifts[] = { + [VLAN_TABLE_MEMBERSHIP_S] = 16, + [STATIC_MAC_FWD_PORTS] = 16, + [STATIC_MAC_FID] = 22, +- [DYNAMIC_MAC_ENTRIES_H] = 3, ++ [DYNAMIC_MAC_ENTRIES_H] = 8, + [DYNAMIC_MAC_ENTRIES] = 24, + [DYNAMIC_MAC_FID] = 16, +- [DYNAMIC_MAC_TIMESTAMP] = 24, ++ [DYNAMIC_MAC_TIMESTAMP] = 22, + [DYNAMIC_MAC_SRC_PORT] = 20, + }; + +diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile +index c8eca2b6f9594..49bf358b9c4fa 100644 +--- a/drivers/net/dsa/mv88e6xxx/Makefile ++++ b/drivers/net/dsa/mv88e6xxx/Makefile +@@ -15,3 +15,7 @@ mv88e6xxx-objs += port_hidden.o + mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o + mv88e6xxx-objs += serdes.o + mv88e6xxx-objs += smi.o ++mv88e6xxx-objs += trace.o ++ ++# for tracing framework to find trace.h ++CFLAGS_trace.o := -I$(src) +diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c +index 3a6db36574ad7..8cf27e2654fcf 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -3354,9 +3354,14 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) + * If this is the upstream port for this switch, enable + * forwarding of unknown unicasts and multicasts. + */ +- reg = MV88E6XXX_PORT_CTL0_IGMP_MLD_SNOOP | +- MV88E6185_PORT_CTL0_USE_TAG | MV88E6185_PORT_CTL0_USE_IP | ++ reg = MV88E6185_PORT_CTL0_USE_TAG | MV88E6185_PORT_CTL0_USE_IP | + MV88E6XXX_PORT_CTL0_STATE_FORWARDING; ++ /* Forward any IPv4 IGMP or IPv6 MLD frames received ++ * by a USER port to the CPU port to allow snooping. ++ */ ++ if (dsa_is_user_port(ds, port)) ++ reg |= MV88E6XXX_PORT_CTL0_IGMP_MLD_SNOOP; ++ + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg); + if (err) + return err; +diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c +index 40bd67a5c8e93..7c513a03789cf 100644 +--- a/drivers/net/dsa/mv88e6xxx/global1_atu.c ++++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c +@@ -12,6 +12,7 @@ + + #include "chip.h" + #include "global1.h" ++#include "trace.h" + + /* Offset 0x01: ATU FID Register */ + +@@ -114,6 +115,19 @@ static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip) + return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_ATU_OP, bit, 0); + } + ++static int mv88e6xxx_g1_read_atu_violation(struct mv88e6xxx_chip *chip) ++{ ++ int err; ++ ++ err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_OP, ++ MV88E6XXX_G1_ATU_OP_BUSY | ++ MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION); ++ if (err) ++ return err; ++ ++ return mv88e6xxx_g1_atu_op_wait(chip); ++} ++ + static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op) + { + u16 val; +@@ -159,6 +173,41 @@ int mv88e6xxx_g1_atu_get_next(struct mv88e6xxx_chip *chip, u16 fid) + return mv88e6xxx_g1_atu_op(chip, fid, MV88E6XXX_G1_ATU_OP_GET_NEXT_DB); + } + ++static int mv88e6xxx_g1_atu_fid_read(struct mv88e6xxx_chip *chip, u16 *fid) ++{ ++ u16 val = 0, upper = 0, op = 0; ++ int err = -EOPNOTSUPP; ++ ++ if (mv88e6xxx_num_databases(chip) > 256) { ++ err = mv88e6xxx_g1_read(chip, MV88E6352_G1_ATU_FID, &val); ++ val &= 0xfff; ++ if (err) ++ return err; ++ } else { ++ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_OP, &op); ++ if (err) ++ return err; ++ if (mv88e6xxx_num_databases(chip) > 64) { ++ /* ATU DBNum[7:4] are located in ATU Control 15:12 */ ++ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, ++ &upper); ++ if (err) ++ return err; ++ ++ upper = (upper >> 8) & 0x00f0; ++ } else if (mv88e6xxx_num_databases(chip) > 16) { ++ /* ATU DBNum[5:4] are located in ATU Operation 9:8 */ ++ upper = (op >> 4) & 0x30; ++ } ++ ++ /* ATU DBNum[3:0] are located in ATU Operation 3:0 */ ++ val = (op & 0xf) | upper; ++ } ++ *fid = val; ++ ++ return err; ++} ++ + /* Offset 0x0C: ATU Data Register */ + + static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip, +@@ -353,14 +402,12 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id) + { + struct mv88e6xxx_chip *chip = dev_id; + struct mv88e6xxx_atu_entry entry; +- int spid; +- int err; +- u16 val; ++ int err, spid; ++ u16 val, fid; + + mv88e6xxx_reg_lock(chip); + +- err = mv88e6xxx_g1_atu_op(chip, 0, +- MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION); ++ err = mv88e6xxx_g1_read_atu_violation(chip); + if (err) + goto out; + +@@ -368,6 +415,10 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id) + if (err) + goto out; + ++ err = mv88e6xxx_g1_atu_fid_read(chip, &fid); ++ if (err) ++ goto out; ++ + err = mv88e6xxx_g1_atu_data_read(chip, &entry); + if (err) + goto out; +@@ -385,23 +436,23 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id) + } + + if (val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION) { +- dev_err_ratelimited(chip->dev, +- "ATU member violation for %pM portvec %x spid %d\n", +- entry.mac, entry.portvec, spid); ++ trace_mv88e6xxx_atu_member_violation(chip->dev, spid, ++ entry.portvec, entry.mac, ++ fid); + chip->ports[spid].atu_member_violation++; + } + + if (val & MV88E6XXX_G1_ATU_OP_MISS_VIOLATION) { +- dev_err_ratelimited(chip->dev, +- "ATU miss violation for %pM portvec %x spid %d\n", +- entry.mac, entry.portvec, spid); ++ trace_mv88e6xxx_atu_miss_violation(chip->dev, spid, ++ entry.portvec, entry.mac, ++ fid); + chip->ports[spid].atu_miss_violation++; + } + + if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION) { +- dev_err_ratelimited(chip->dev, +- "ATU full violation for %pM portvec %x spid %d\n", +- entry.mac, entry.portvec, spid); ++ trace_mv88e6xxx_atu_full_violation(chip->dev, spid, ++ entry.portvec, entry.mac, ++ fid); + chip->ports[spid].atu_full_violation++; + } + mv88e6xxx_reg_unlock(chip); +diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c +index 38e18f5811bfb..bcfb4a812055c 100644 +--- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c ++++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c +@@ -13,6 +13,7 @@ + + #include "chip.h" + #include "global1.h" ++#include "trace.h" + + /* Offset 0x02: VTU FID Register */ + +@@ -628,14 +629,12 @@ static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id) + spid = val & MV88E6XXX_G1_VTU_OP_SPID_MASK; + + if (val & MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION) { +- dev_err_ratelimited(chip->dev, "VTU member violation for vid %d, source port %d\n", +- vid, spid); ++ trace_mv88e6xxx_vtu_member_violation(chip->dev, spid, vid); + chip->ports[spid].vtu_member_violation++; + } + + if (val & MV88E6XXX_G1_VTU_OP_MISS_VIOLATION) { +- dev_dbg_ratelimited(chip->dev, "VTU miss violation for vid %d, source port %d\n", +- vid, spid); ++ trace_mv88e6xxx_vtu_miss_violation(chip->dev, spid, vid); + chip->ports[spid].vtu_miss_violation++; + } + +diff --git a/drivers/net/dsa/mv88e6xxx/trace.c b/drivers/net/dsa/mv88e6xxx/trace.c +new file mode 100644 +index 0000000000000..7833cb50ca5d7 +--- /dev/null ++++ b/drivers/net/dsa/mv88e6xxx/trace.c +@@ -0,0 +1,6 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* Copyright 2022 NXP ++ */ ++ ++#define CREATE_TRACE_POINTS ++#include "trace.h" +diff --git a/drivers/net/dsa/mv88e6xxx/trace.h b/drivers/net/dsa/mv88e6xxx/trace.h +new file mode 100644 +index 0000000000000..f59ca04768e79 +--- /dev/null ++++ b/drivers/net/dsa/mv88e6xxx/trace.h +@@ -0,0 +1,96 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* Copyright 2022 NXP ++ */ ++ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM mv88e6xxx ++ ++#if !defined(_MV88E6XXX_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _MV88E6XXX_TRACE_H ++ ++#include <linux/device.h> ++#include <linux/if_ether.h> ++#include <linux/tracepoint.h> ++ ++DECLARE_EVENT_CLASS(mv88e6xxx_atu_violation, ++ ++ TP_PROTO(const struct device *dev, int spid, u16 portvec, ++ const unsigned char *addr, u16 fid), ++ ++ TP_ARGS(dev, spid, portvec, addr, fid), ++ ++ TP_STRUCT__entry( ++ __string(name, dev_name(dev)) ++ __field(int, spid) ++ __field(u16, portvec) ++ __array(unsigned char, addr, ETH_ALEN) ++ __field(u16, fid) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(name, dev_name(dev)); ++ __entry->spid = spid; ++ __entry->portvec = portvec; ++ memcpy(__entry->addr, addr, ETH_ALEN); ++ __entry->fid = fid; ++ ), ++ ++ TP_printk("dev %s spid %d portvec 0x%x addr %pM fid %u", ++ __get_str(name), __entry->spid, __entry->portvec, ++ __entry->addr, __entry->fid) ++); ++ ++DEFINE_EVENT(mv88e6xxx_atu_violation, mv88e6xxx_atu_member_violation, ++ TP_PROTO(const struct device *dev, int spid, u16 portvec, ++ const unsigned char *addr, u16 fid), ++ TP_ARGS(dev, spid, portvec, addr, fid)); ++ ++DEFINE_EVENT(mv88e6xxx_atu_violation, mv88e6xxx_atu_miss_violation, ++ TP_PROTO(const struct device *dev, int spid, u16 portvec, ++ const unsigned char *addr, u16 fid), ++ TP_ARGS(dev, spid, portvec, addr, fid)); ++ ++DEFINE_EVENT(mv88e6xxx_atu_violation, mv88e6xxx_atu_full_violation, ++ TP_PROTO(const struct device *dev, int spid, u16 portvec, ++ const unsigned char *addr, u16 fid), ++ TP_ARGS(dev, spid, portvec, addr, fid)); ++ ++DECLARE_EVENT_CLASS(mv88e6xxx_vtu_violation, ++ ++ TP_PROTO(const struct device *dev, int spid, u16 vid), ++ ++ TP_ARGS(dev, spid, vid), ++ ++ TP_STRUCT__entry( ++ __string(name, dev_name(dev)) ++ __field(int, spid) ++ __field(u16, vid) ++ ), ++ ++ TP_fast_assign( ++ __assign_str(name, dev_name(dev)); ++ __entry->spid = spid; ++ __entry->vid = vid; ++ ), ++ ++ TP_printk("dev %s spid %d vid %u", ++ __get_str(name), __entry->spid, __entry->vid) ++); ++ ++DEFINE_EVENT(mv88e6xxx_vtu_violation, mv88e6xxx_vtu_member_violation, ++ TP_PROTO(const struct device *dev, int spid, u16 vid), ++ TP_ARGS(dev, spid, vid)); ++ ++DEFINE_EVENT(mv88e6xxx_vtu_violation, mv88e6xxx_vtu_miss_violation, ++ TP_PROTO(const struct device *dev, int spid, u16 vid), ++ TP_ARGS(dev, spid, vid)); ++ ++#endif /* _MV88E6XXX_TRACE_H */ ++ ++/* We don't want to use include/trace/events */ ++#undef TRACE_INCLUDE_PATH ++#define TRACE_INCLUDE_PATH . ++#undef TRACE_INCLUDE_FILE ++#define TRACE_INCLUDE_FILE trace ++/* This part must be outside protection */ ++#include <trace/define_trace.h> +diff --git a/drivers/net/dsa/realtek/realtek-mdio.c b/drivers/net/dsa/realtek/realtek-mdio.c +index 3e54fac5f9027..5a8fe707ca25e 100644 +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -21,6 +21,7 @@ + + #include <linux/module.h> + #include <linux/of_device.h> ++#include <linux/overflow.h> + #include <linux/regmap.h> + + #include "realtek.h" +@@ -152,7 +153,9 @@ static int realtek_mdio_probe(struct mdio_device *mdiodev) + if (!var) + return -EINVAL; + +- priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); ++ priv = devm_kzalloc(&mdiodev->dev, ++ size_add(sizeof(*priv), var->chip_data_sz), ++ GFP_KERNEL); + if (!priv) + return -ENOMEM; + +diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c +index 251b102d2792b..c6e36603bd2db 100644 +--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c ++++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c +@@ -175,12 +175,12 @@ static const struct pci_device_id bnxt_pci_tbl[] = { + { PCI_VDEVICE(BROADCOM, 0x1750), .driver_data = BCM57508 }, + { PCI_VDEVICE(BROADCOM, 0x1751), .driver_data = BCM57504 }, + { PCI_VDEVICE(BROADCOM, 0x1752), .driver_data = BCM57502 }, +- { PCI_VDEVICE(BROADCOM, 0x1800), .driver_data = BCM57508_NPAR }, ++ { PCI_VDEVICE(BROADCOM, 0x1800), .driver_data = BCM57502_NPAR }, + { PCI_VDEVICE(BROADCOM, 0x1801), .driver_data = BCM57504_NPAR }, +- { PCI_VDEVICE(BROADCOM, 0x1802), .driver_data = BCM57502_NPAR }, +- { PCI_VDEVICE(BROADCOM, 0x1803), .driver_data = BCM57508_NPAR }, ++ { PCI_VDEVICE(BROADCOM, 0x1802), .driver_data = BCM57508_NPAR }, ++ { PCI_VDEVICE(BROADCOM, 0x1803), .driver_data = BCM57502_NPAR }, + { PCI_VDEVICE(BROADCOM, 0x1804), .driver_data = BCM57504_NPAR }, +- { PCI_VDEVICE(BROADCOM, 0x1805), .driver_data = BCM57502_NPAR }, ++ { PCI_VDEVICE(BROADCOM, 0x1805), .driver_data = BCM57508_NPAR }, + { PCI_VDEVICE(BROADCOM, 0xd802), .driver_data = BCM58802 }, + { PCI_VDEVICE(BROADCOM, 0xd804), .driver_data = BCM58804 }, + #ifdef CONFIG_BNXT_SRIOV +diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h +index 02741d499bf4a..1d2588c92977e 100644 +--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h ++++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h +@@ -1225,6 +1225,7 @@ struct bnxt_link_info { + #define BNXT_LINK_SPEED_40GB PORT_PHY_QCFG_RESP_LINK_SPEED_40GB + #define BNXT_LINK_SPEED_50GB PORT_PHY_QCFG_RESP_LINK_SPEED_50GB + #define BNXT_LINK_SPEED_100GB PORT_PHY_QCFG_RESP_LINK_SPEED_100GB ++#define BNXT_LINK_SPEED_200GB PORT_PHY_QCFG_RESP_LINK_SPEED_200GB + u16 support_speeds; + u16 support_pam4_speeds; + u16 auto_link_speeds; /* fw adv setting */ +diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +index 703fc163235f9..01b973bc509f5 100644 +--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c ++++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +@@ -1712,6 +1712,8 @@ u32 bnxt_fw_to_ethtool_speed(u16 fw_link_speed) + return SPEED_50000; + case BNXT_LINK_SPEED_100GB: + return SPEED_100000; ++ case BNXT_LINK_SPEED_200GB: ++ return SPEED_200000; + default: + return SPEED_UNKNOWN; + } +@@ -3634,6 +3636,7 @@ static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, + bnxt_ulp_stop(bp); + rc = bnxt_close_nic(bp, true, false); + if (rc) { ++ etest->flags |= ETH_TEST_FL_FAILED; + bnxt_ulp_start(bp, rc); + return; + } +diff --git a/drivers/net/ethernet/intel/i40e/i40e_diag.c b/drivers/net/ethernet/intel/i40e/i40e_diag.c +index ef4d3762bf371..ca229b0efeb65 100644 +--- a/drivers/net/ethernet/intel/i40e/i40e_diag.c ++++ b/drivers/net/ethernet/intel/i40e/i40e_diag.c +@@ -44,7 +44,7 @@ static i40e_status i40e_diag_reg_pattern_test(struct i40e_hw *hw, + return 0; + } + +-struct i40e_diag_reg_test_info i40e_reg_list[] = { ++const struct i40e_diag_reg_test_info i40e_reg_list[] = { + /* offset mask elements stride */ + {I40E_QTX_CTL(0), 0x0000FFBF, 1, + I40E_QTX_CTL(1) - I40E_QTX_CTL(0)}, +@@ -78,27 +78,28 @@ i40e_status i40e_diag_reg_test(struct i40e_hw *hw) + { + i40e_status ret_code = 0; + u32 reg, mask; ++ u32 elements; + u32 i, j; + + for (i = 0; i40e_reg_list[i].offset != 0 && + !ret_code; i++) { + ++ elements = i40e_reg_list[i].elements; + /* set actual reg range for dynamically allocated resources */ + if (i40e_reg_list[i].offset == I40E_QTX_CTL(0) && + hw->func_caps.num_tx_qp != 0) +- i40e_reg_list[i].elements = hw->func_caps.num_tx_qp; ++ elements = hw->func_caps.num_tx_qp; + if ((i40e_reg_list[i].offset == I40E_PFINT_ITRN(0, 0) || + i40e_reg_list[i].offset == I40E_PFINT_ITRN(1, 0) || + i40e_reg_list[i].offset == I40E_PFINT_ITRN(2, 0) || + i40e_reg_list[i].offset == I40E_QINT_TQCTL(0) || + i40e_reg_list[i].offset == I40E_QINT_RQCTL(0)) && + hw->func_caps.num_msix_vectors != 0) +- i40e_reg_list[i].elements = +- hw->func_caps.num_msix_vectors - 1; ++ elements = hw->func_caps.num_msix_vectors - 1; + + /* test register access */ + mask = i40e_reg_list[i].mask; +- for (j = 0; j < i40e_reg_list[i].elements && !ret_code; j++) { ++ for (j = 0; j < elements && !ret_code; j++) { + reg = i40e_reg_list[i].offset + + (j * i40e_reg_list[i].stride); + ret_code = i40e_diag_reg_pattern_test(hw, reg, mask); +diff --git a/drivers/net/ethernet/intel/i40e/i40e_diag.h b/drivers/net/ethernet/intel/i40e/i40e_diag.h +index c3340f320a18c..1db7c6d572311 100644 +--- a/drivers/net/ethernet/intel/i40e/i40e_diag.h ++++ b/drivers/net/ethernet/intel/i40e/i40e_diag.h +@@ -20,7 +20,7 @@ struct i40e_diag_reg_test_info { + u32 stride; /* bytes between each element */ + }; + +-extern struct i40e_diag_reg_test_info i40e_reg_list[]; ++extern const struct i40e_diag_reg_test_info i40e_reg_list[]; + + i40e_status i40e_diag_reg_test(struct i40e_hw *hw); + i40e_status i40e_diag_eeprom_test(struct i40e_hw *hw); +diff --git a/drivers/net/ethernet/intel/ice/ice_sched.c b/drivers/net/ethernet/intel/ice/ice_sched.c +index 118595763bba3..2c62c1763ee0d 100644 +--- a/drivers/net/ethernet/intel/ice/ice_sched.c ++++ b/drivers/net/ethernet/intel/ice/ice_sched.c +@@ -2756,7 +2756,7 @@ static int + ice_sched_assoc_vsi_to_agg(struct ice_port_info *pi, u32 agg_id, + u16 vsi_handle, unsigned long *tc_bitmap) + { +- struct ice_sched_agg_vsi_info *agg_vsi_info, *old_agg_vsi_info = NULL; ++ struct ice_sched_agg_vsi_info *agg_vsi_info, *iter, *old_agg_vsi_info = NULL; + struct ice_sched_agg_info *agg_info, *old_agg_info; + struct ice_hw *hw = pi->hw; + int status = 0; +@@ -2774,11 +2774,13 @@ ice_sched_assoc_vsi_to_agg(struct ice_port_info *pi, u32 agg_id, + if (old_agg_info && old_agg_info != agg_info) { + struct ice_sched_agg_vsi_info *vtmp; + +- list_for_each_entry_safe(old_agg_vsi_info, vtmp, ++ list_for_each_entry_safe(iter, vtmp, + &old_agg_info->agg_vsi_list, + list_entry) +- if (old_agg_vsi_info->vsi_handle == vsi_handle) ++ if (iter->vsi_handle == vsi_handle) { ++ old_agg_vsi_info = iter; + break; ++ } + } + + /* check if entry already exist */ +diff --git a/drivers/net/ethernet/intel/ice/ice_switch.c b/drivers/net/ethernet/intel/ice/ice_switch.c +index 61f844d225123..46b36851af460 100644 +--- a/drivers/net/ethernet/intel/ice/ice_switch.c ++++ b/drivers/net/ethernet/intel/ice/ice_switch.c +@@ -1780,18 +1780,36 @@ ice_update_vsi(struct ice_hw *hw, u16 vsi_handle, struct ice_vsi_ctx *vsi_ctx, + int + ice_cfg_rdma_fltr(struct ice_hw *hw, u16 vsi_handle, bool enable) + { +- struct ice_vsi_ctx *ctx; ++ struct ice_vsi_ctx *ctx, *cached_ctx; ++ int status; ++ ++ cached_ctx = ice_get_vsi_ctx(hw, vsi_handle); ++ if (!cached_ctx) ++ return -ENOENT; + +- ctx = ice_get_vsi_ctx(hw, vsi_handle); ++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) +- return -EIO; ++ return -ENOMEM; ++ ++ ctx->info.q_opt_rss = cached_ctx->info.q_opt_rss; ++ ctx->info.q_opt_tc = cached_ctx->info.q_opt_tc; ++ ctx->info.q_opt_flags = cached_ctx->info.q_opt_flags; ++ ++ ctx->info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_Q_OPT_VALID); + + if (enable) + ctx->info.q_opt_flags |= ICE_AQ_VSI_Q_OPT_PE_FLTR_EN; + else + ctx->info.q_opt_flags &= ~ICE_AQ_VSI_Q_OPT_PE_FLTR_EN; + +- return ice_update_vsi(hw, vsi_handle, ctx, NULL); ++ status = ice_update_vsi(hw, vsi_handle, ctx, NULL); ++ if (!status) { ++ cached_ctx->info.q_opt_flags = ctx->info.q_opt_flags; ++ cached_ctx->info.valid_sections |= ctx->info.valid_sections; ++ } ++ ++ kfree(ctx); ++ return status; + } + + /** +diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.c b/drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.c +index c6a58343d81d8..a2645ff3100e4 100644 +--- a/drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.c ++++ b/drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.c +@@ -541,6 +541,72 @@ static void ice_vc_fdir_rem_prof_all(struct ice_vf *vf) + } + } + ++/** ++ * ice_vc_fdir_has_prof_conflict ++ * @vf: pointer to the VF structure ++ * @conf: FDIR configuration for each filter ++ * ++ * Check if @conf has conflicting profile with existing profiles ++ * ++ * Return: true on success, and false on error. ++ */ ++static bool ++ice_vc_fdir_has_prof_conflict(struct ice_vf *vf, ++ struct virtchnl_fdir_fltr_conf *conf) ++{ ++ struct ice_fdir_fltr *desc; ++ ++ list_for_each_entry(desc, &vf->fdir.fdir_rule_list, fltr_node) { ++ struct virtchnl_fdir_fltr_conf *existing_conf; ++ enum ice_fltr_ptype flow_type_a, flow_type_b; ++ struct ice_fdir_fltr *a, *b; ++ ++ existing_conf = to_fltr_conf_from_desc(desc); ++ a = &existing_conf->input; ++ b = &conf->input; ++ flow_type_a = a->flow_type; ++ flow_type_b = b->flow_type; ++ ++ /* No need to compare two rules with different tunnel types or ++ * with the same protocol type. ++ */ ++ if (existing_conf->ttype != conf->ttype || ++ flow_type_a == flow_type_b) ++ continue; ++ ++ switch (flow_type_a) { ++ case ICE_FLTR_PTYPE_NONF_IPV4_UDP: ++ case ICE_FLTR_PTYPE_NONF_IPV4_TCP: ++ case ICE_FLTR_PTYPE_NONF_IPV4_SCTP: ++ if (flow_type_b == ICE_FLTR_PTYPE_NONF_IPV4_OTHER) ++ return true; ++ break; ++ case ICE_FLTR_PTYPE_NONF_IPV4_OTHER: ++ if (flow_type_b == ICE_FLTR_PTYPE_NONF_IPV4_UDP || ++ flow_type_b == ICE_FLTR_PTYPE_NONF_IPV4_TCP || ++ flow_type_b == ICE_FLTR_PTYPE_NONF_IPV4_SCTP) ++ return true; ++ break; ++ case ICE_FLTR_PTYPE_NONF_IPV6_UDP: ++ case ICE_FLTR_PTYPE_NONF_IPV6_TCP: ++ case ICE_FLTR_PTYPE_NONF_IPV6_SCTP: ++ if (flow_type_b == ICE_FLTR_PTYPE_NONF_IPV6_OTHER) ++ return true; ++ break; ++ case ICE_FLTR_PTYPE_NONF_IPV6_OTHER: ++ if (flow_type_b == ICE_FLTR_PTYPE_NONF_IPV6_UDP || ++ flow_type_b == ICE_FLTR_PTYPE_NONF_IPV6_TCP || ++ flow_type_b == ICE_FLTR_PTYPE_NONF_IPV6_SCTP) ++ return true; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return false; ++} ++ + /** + * ice_vc_fdir_write_flow_prof + * @vf: pointer to the VF structure +@@ -677,6 +743,13 @@ ice_vc_fdir_config_input_set(struct ice_vf *vf, struct virtchnl_fdir_add *fltr, + enum ice_fltr_ptype flow; + int ret; + ++ ret = ice_vc_fdir_has_prof_conflict(vf, conf); ++ if (ret) { ++ dev_dbg(dev, "Found flow profile conflict for VF %d\n", ++ vf->vf_id); ++ return ret; ++ } ++ + flow = input->flow_type; + ret = ice_vc_fdir_alloc_prof(vf, flow); + if (ret) { +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c +index 41d935d1aaf6f..40aeaa7bd739f 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c +@@ -62,35 +62,38 @@ static const struct mvpp2_cls_flow cls_flows[MVPP2_N_PRS_FLOWS] = { + MVPP2_DEF_FLOW(MVPP22_FLOW_TCP4, MVPP2_FL_IP4_TCP_FRAG_UNTAG, + MVPP22_CLS_HEK_IP4_2T, + MVPP2_PRS_RI_VLAN_NONE | MVPP2_PRS_RI_L3_IP4 | +- MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_IP_FRAG_TRUE | MVPP2_PRS_RI_L4_TCP, + MVPP2_PRS_IP_MASK | MVPP2_PRS_RI_VLAN_MASK), + + MVPP2_DEF_FLOW(MVPP22_FLOW_TCP4, MVPP2_FL_IP4_TCP_FRAG_UNTAG, + MVPP22_CLS_HEK_IP4_2T, + MVPP2_PRS_RI_VLAN_NONE | MVPP2_PRS_RI_L3_IP4_OPT | +- MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_IP_FRAG_TRUE | MVPP2_PRS_RI_L4_TCP, + MVPP2_PRS_IP_MASK | MVPP2_PRS_RI_VLAN_MASK), + + MVPP2_DEF_FLOW(MVPP22_FLOW_TCP4, MVPP2_FL_IP4_TCP_FRAG_UNTAG, + MVPP22_CLS_HEK_IP4_2T, + MVPP2_PRS_RI_VLAN_NONE | MVPP2_PRS_RI_L3_IP4_OTHER | +- MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_IP_FRAG_TRUE | MVPP2_PRS_RI_L4_TCP, + MVPP2_PRS_IP_MASK | MVPP2_PRS_RI_VLAN_MASK), + + /* TCP over IPv4 flows, fragmented, with vlan tag */ + MVPP2_DEF_FLOW(MVPP22_FLOW_TCP4, MVPP2_FL_IP4_TCP_FRAG_TAG, + MVPP22_CLS_HEK_IP4_2T | MVPP22_CLS_HEK_TAGGED, +- MVPP2_PRS_RI_L3_IP4 | MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_IP4 | MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, + MVPP2_PRS_IP_MASK), + + MVPP2_DEF_FLOW(MVPP22_FLOW_TCP4, MVPP2_FL_IP4_TCP_FRAG_TAG, + MVPP22_CLS_HEK_IP4_2T | MVPP22_CLS_HEK_TAGGED, +- MVPP2_PRS_RI_L3_IP4_OPT | MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_IP4_OPT | MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, + MVPP2_PRS_IP_MASK), + + MVPP2_DEF_FLOW(MVPP22_FLOW_TCP4, MVPP2_FL_IP4_TCP_FRAG_TAG, + MVPP22_CLS_HEK_IP4_2T | MVPP22_CLS_HEK_TAGGED, +- MVPP2_PRS_RI_L3_IP4_OTHER | MVPP2_PRS_RI_L4_TCP, ++ MVPP2_PRS_RI_L3_IP4_OTHER | MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_TCP, + MVPP2_PRS_IP_MASK), + + /* UDP over IPv4 flows, Not fragmented, no vlan tag */ +@@ -132,35 +135,38 @@ static const struct mvpp2_cls_flow cls_flows[MVPP2_N_PRS_FLOWS] = { + MVPP2_DEF_FLOW(MVPP22_FLOW_UDP4, MVPP2_FL_IP4_UDP_FRAG_UNTAG, + MVPP22_CLS_HEK_IP4_2T, + MVPP2_PRS_RI_VLAN_NONE | MVPP2_PRS_RI_L3_IP4 | +- MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_IP_FRAG_TRUE | MVPP2_PRS_RI_L4_UDP, + MVPP2_PRS_IP_MASK | MVPP2_PRS_RI_VLAN_MASK), + + MVPP2_DEF_FLOW(MVPP22_FLOW_UDP4, MVPP2_FL_IP4_UDP_FRAG_UNTAG, + MVPP22_CLS_HEK_IP4_2T, + MVPP2_PRS_RI_VLAN_NONE | MVPP2_PRS_RI_L3_IP4_OPT | +- MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_IP_FRAG_TRUE | MVPP2_PRS_RI_L4_UDP, + MVPP2_PRS_IP_MASK | MVPP2_PRS_RI_VLAN_MASK), + + MVPP2_DEF_FLOW(MVPP22_FLOW_UDP4, MVPP2_FL_IP4_UDP_FRAG_UNTAG, + MVPP22_CLS_HEK_IP4_2T, + MVPP2_PRS_RI_VLAN_NONE | MVPP2_PRS_RI_L3_IP4_OTHER | +- MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_IP_FRAG_TRUE | MVPP2_PRS_RI_L4_UDP, + MVPP2_PRS_IP_MASK | MVPP2_PRS_RI_VLAN_MASK), + + /* UDP over IPv4 flows, fragmented, with vlan tag */ + MVPP2_DEF_FLOW(MVPP22_FLOW_UDP4, MVPP2_FL_IP4_UDP_FRAG_TAG, + MVPP22_CLS_HEK_IP4_2T | MVPP22_CLS_HEK_TAGGED, +- MVPP2_PRS_RI_L3_IP4 | MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_IP4 | MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, + MVPP2_PRS_IP_MASK), + + MVPP2_DEF_FLOW(MVPP22_FLOW_UDP4, MVPP2_FL_IP4_UDP_FRAG_TAG, + MVPP22_CLS_HEK_IP4_2T | MVPP22_CLS_HEK_TAGGED, +- MVPP2_PRS_RI_L3_IP4_OPT | MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_IP4_OPT | MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, + MVPP2_PRS_IP_MASK), + + MVPP2_DEF_FLOW(MVPP22_FLOW_UDP4, MVPP2_FL_IP4_UDP_FRAG_TAG, + MVPP22_CLS_HEK_IP4_2T | MVPP22_CLS_HEK_TAGGED, +- MVPP2_PRS_RI_L3_IP4_OTHER | MVPP2_PRS_RI_L4_UDP, ++ MVPP2_PRS_RI_L3_IP4_OTHER | MVPP2_PRS_RI_IP_FRAG_TRUE | ++ MVPP2_PRS_RI_L4_UDP, + MVPP2_PRS_IP_MASK), + + /* TCP over IPv6 flows, not fragmented, no vlan tag */ +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_prs.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_prs.c +index 75ba57bd1d46d..9af22f497a40f 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_prs.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_prs.c +@@ -1539,8 +1539,8 @@ static int mvpp2_prs_vlan_init(struct platform_device *pdev, struct mvpp2 *priv) + if (!priv->prs_double_vlans) + return -ENOMEM; + +- /* Double VLAN: 0x8100, 0x88A8 */ +- err = mvpp2_prs_double_vlan_add(priv, ETH_P_8021Q, ETH_P_8021AD, ++ /* Double VLAN: 0x88A8, 0x8100 */ ++ err = mvpp2_prs_double_vlan_add(priv, ETH_P_8021AD, ETH_P_8021Q, + MVPP2_PRS_PORT_MASK); + if (err) + return err; +@@ -1607,59 +1607,45 @@ static int mvpp2_prs_vlan_init(struct platform_device *pdev, struct mvpp2 *priv) + static int mvpp2_prs_pppoe_init(struct mvpp2 *priv) + { + struct mvpp2_prs_entry pe; +- int tid; +- +- /* IPv4 over PPPoE with options */ +- tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, +- MVPP2_PE_LAST_FREE_TID); +- if (tid < 0) +- return tid; +- +- memset(&pe, 0, sizeof(pe)); +- mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); +- pe.index = tid; +- +- mvpp2_prs_match_etype(&pe, 0, PPP_IP); +- +- mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); +- mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4_OPT, +- MVPP2_PRS_RI_L3_PROTO_MASK); +- /* goto ipv4 dest-address (skip eth_type + IP-header-size - 4) */ +- mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + +- sizeof(struct iphdr) - 4, +- MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); +- /* Set L3 offset */ +- mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, +- MVPP2_ETH_TYPE_LEN, +- MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); +- +- /* Update shadow table and hw entry */ +- mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_PPPOE); +- mvpp2_prs_hw_write(priv, &pe); ++ int tid, ihl; + +- /* IPv4 over PPPoE without options */ +- tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, +- MVPP2_PE_LAST_FREE_TID); +- if (tid < 0) +- return tid; ++ /* IPv4 over PPPoE with header length >= 5 */ ++ for (ihl = MVPP2_PRS_IPV4_IHL_MIN; ihl <= MVPP2_PRS_IPV4_IHL_MAX; ihl++) { ++ tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, ++ MVPP2_PE_LAST_FREE_TID); ++ if (tid < 0) ++ return tid; + +- pe.index = tid; ++ memset(&pe, 0, sizeof(pe)); ++ mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); ++ pe.index = tid; + +- mvpp2_prs_tcam_data_byte_set(&pe, MVPP2_ETH_TYPE_LEN, +- MVPP2_PRS_IPV4_HEAD | +- MVPP2_PRS_IPV4_IHL_MIN, +- MVPP2_PRS_IPV4_HEAD_MASK | +- MVPP2_PRS_IPV4_IHL_MASK); ++ mvpp2_prs_match_etype(&pe, 0, PPP_IP); ++ mvpp2_prs_tcam_data_byte_set(&pe, MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_IPV4_HEAD | ihl, ++ MVPP2_PRS_IPV4_HEAD_MASK | ++ MVPP2_PRS_IPV4_IHL_MASK); + +- /* Clear ri before updating */ +- pe.sram[MVPP2_PRS_SRAM_RI_WORD] = 0x0; +- pe.sram[MVPP2_PRS_SRAM_RI_CTRL_WORD] = 0x0; +- mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4, +- MVPP2_PRS_RI_L3_PROTO_MASK); ++ mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); ++ mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4, ++ MVPP2_PRS_RI_L3_PROTO_MASK); ++ /* goto ipv4 dst-address (skip eth_type + IP-header-size - 4) */ ++ mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + ++ sizeof(struct iphdr) - 4, ++ MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); ++ /* Set L3 offset */ ++ mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, ++ MVPP2_ETH_TYPE_LEN, ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); ++ /* Set L4 offset */ ++ mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4, ++ MVPP2_ETH_TYPE_LEN + (ihl * 4), ++ MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); + +- /* Update shadow table and hw entry */ +- mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_PPPOE); +- mvpp2_prs_hw_write(priv, &pe); ++ /* Update shadow table and hw entry */ ++ mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_PPPOE); ++ mvpp2_prs_hw_write(priv, &pe); ++ } + + /* IPv6 over PPPoE */ + tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, +diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c +index 34ea8af48c3d0..d6eed204574a9 100644 +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -438,6 +438,7 @@ __mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + hwe->ib1 &= ~MTK_FOE_IB1_STATE; + hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID); + dma_wmb(); ++ mtk_ppe_cache_clear(ppe); + } + entry->hash = 0xffff; + +diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +index 28bbd1df3e305..6a72687d5b83f 100644 +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -570,6 +570,7 @@ mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f) + if (IS_ERR(block_cb)) + return PTR_ERR(block_cb); + ++ flow_block_cb_incref(block_cb); + flow_block_cb_add(block_cb, f); + list_add_tail(&block_cb->driver_list, &block_cb_list); + return 0; +@@ -578,7 +579,7 @@ mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f) + if (!block_cb) + return -ENOENT; + +- if (flow_block_cb_decref(block_cb)) { ++ if (!flow_block_cb_decref(block_cb)) { + flow_block_cb_remove(block_cb, f); + list_del(&block_cb->driver_list); + } +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +index 3b5c5064cfafc..5e01de4c32037 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +@@ -4104,13 +4104,17 @@ static bool mlx5e_xsk_validate_mtu(struct net_device *netdev, + struct xsk_buff_pool *xsk_pool = + mlx5e_xsk_get_pool(&chs->params, chs->params.xsk, ix); + struct mlx5e_xsk_param xsk; ++ int max_xdp_mtu; + + if (!xsk_pool) + continue; + + mlx5e_build_xsk_param(xsk_pool, &xsk); ++ max_xdp_mtu = mlx5e_xdp_max_mtu(new_params, &xsk); + +- if (!mlx5e_validate_xsk_param(new_params, &xsk, mdev)) { ++ /* Validate XSK params and XDP MTU in advance */ ++ if (!mlx5e_validate_xsk_param(new_params, &xsk, mdev) || ++ new_params->sw_mtu > max_xdp_mtu) { + u32 hr = mlx5e_get_linear_rq_headroom(new_params, &xsk); + int max_mtu_frame, max_mtu_page, max_mtu; + +@@ -4120,9 +4124,9 @@ static bool mlx5e_xsk_validate_mtu(struct net_device *netdev, + */ + max_mtu_frame = MLX5E_HW2SW_MTU(new_params, xsk.chunk_size - hr); + max_mtu_page = MLX5E_HW2SW_MTU(new_params, SKB_MAX_HEAD(0)); +- max_mtu = min(max_mtu_frame, max_mtu_page); ++ max_mtu = min3(max_mtu_frame, max_mtu_page, max_xdp_mtu); + +- netdev_err(netdev, "MTU %d is too big for an XSK running on channel %u. Try MTU <= %d\n", ++ netdev_err(netdev, "MTU %d is too big for an XSK running on channel %u or its redirection XDP program. Try MTU <= %d\n", + new_params->sw_mtu, ix, max_mtu); + return false; + } +diff --git a/drivers/net/ethernet/mscc/ocelot_stats.c b/drivers/net/ethernet/mscc/ocelot_stats.c +index dbd20b125ceaf..0066219bb0e89 100644 +--- a/drivers/net/ethernet/mscc/ocelot_stats.c ++++ b/drivers/net/ethernet/mscc/ocelot_stats.c +@@ -392,7 +392,8 @@ static int ocelot_prepare_stats_regions(struct ocelot *ocelot) + if (!ocelot->stats_layout[i].reg) + continue; + +- if (region && ocelot->stats_layout[i].reg == last + 4) { ++ if (region && ocelot->map[SYS][ocelot->stats_layout[i].reg & REG_MASK] == ++ ocelot->map[SYS][last & REG_MASK] + 4) { + region->count++; + } else { + region = devm_kzalloc(ocelot->dev, sizeof(*region), +diff --git a/drivers/net/ethernet/realtek/r8169_phy_config.c b/drivers/net/ethernet/realtek/r8169_phy_config.c +index 930496cd34ed0..b50f16786c246 100644 +--- a/drivers/net/ethernet/realtek/r8169_phy_config.c ++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c +@@ -826,6 +826,9 @@ static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp, + /* disable phy pfm mode */ + phy_modify_paged(phydev, 0x0a44, 0x11, BIT(7), 0); + ++ /* disable 10m pll off */ ++ phy_modify_paged(phydev, 0x0a43, 0x10, BIT(0), 0); ++ + rtl8168g_disable_aldps(phydev); + rtl8168g_config_eee_phy(phydev); + } +diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c +index 7022fb2005a2f..d30459dbfe8f8 100644 +--- a/drivers/net/ethernet/sfc/ef10.c ++++ b/drivers/net/ethernet/sfc/ef10.c +@@ -1304,7 +1304,8 @@ static void efx_ef10_fini_nic(struct efx_nic *efx) + static int efx_ef10_init_nic(struct efx_nic *efx) + { + struct efx_ef10_nic_data *nic_data = efx->nic_data; +- netdev_features_t hw_enc_features = 0; ++ struct net_device *net_dev = efx->net_dev; ++ netdev_features_t tun_feats, tso_feats; + int rc; + + if (nic_data->must_check_datapath_caps) { +@@ -1349,20 +1350,30 @@ static int efx_ef10_init_nic(struct efx_nic *efx) + nic_data->must_restore_piobufs = false; + } + +- /* add encapsulated checksum offload features */ ++ /* encap features might change during reset if fw variant changed */ + if (efx_has_cap(efx, VXLAN_NVGRE) && !efx_ef10_is_vf(efx)) +- hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; +- /* add encapsulated TSO features */ +- if (efx_has_cap(efx, TX_TSO_V2_ENCAP)) { +- netdev_features_t encap_tso_features; ++ net_dev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; ++ else ++ net_dev->hw_enc_features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); + +- encap_tso_features = NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE | +- NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_GRE_CSUM; ++ tun_feats = NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE | ++ NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_GRE_CSUM; ++ tso_feats = NETIF_F_TSO | NETIF_F_TSO6; + +- hw_enc_features |= encap_tso_features | NETIF_F_TSO; +- efx->net_dev->features |= encap_tso_features; ++ if (efx_has_cap(efx, TX_TSO_V2_ENCAP)) { ++ /* If this is first nic_init, or if it is a reset and a new fw ++ * variant has added new features, enable them by default. ++ * If the features are not new, maintain their current value. ++ */ ++ if (!(net_dev->hw_features & tun_feats)) ++ net_dev->features |= tun_feats; ++ net_dev->hw_enc_features |= tun_feats | tso_feats; ++ net_dev->hw_features |= tun_feats; ++ } else { ++ net_dev->hw_enc_features &= ~(tun_feats | tso_feats); ++ net_dev->hw_features &= ~tun_feats; ++ net_dev->features &= ~tun_feats; + } +- efx->net_dev->hw_enc_features = hw_enc_features; + + /* don't fail init if RSS setup doesn't work */ + rc = efx->type->rx_push_rss_config(efx, false, +@@ -4021,7 +4032,10 @@ static unsigned int efx_ef10_recycle_ring_size(const struct efx_nic *efx) + NETIF_F_HW_VLAN_CTAG_FILTER | \ + NETIF_F_IPV6_CSUM | \ + NETIF_F_RXHASH | \ +- NETIF_F_NTUPLE) ++ NETIF_F_NTUPLE | \ ++ NETIF_F_SG | \ ++ NETIF_F_RXCSUM | \ ++ NETIF_F_RXALL) + + const struct efx_nic_type efx_hunt_a0_vf_nic_type = { + .is_vf = true, +diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c +index 3a86f1213a051..6a1bff54bc6c3 100644 +--- a/drivers/net/ethernet/sfc/efx.c ++++ b/drivers/net/ethernet/sfc/efx.c +@@ -1001,21 +1001,18 @@ static int efx_pci_probe_post_io(struct efx_nic *efx) + } + + /* Determine netdevice features */ +- net_dev->features |= (efx->type->offload_features | NETIF_F_SG | +- NETIF_F_TSO | NETIF_F_RXCSUM | NETIF_F_RXALL); +- if (efx->type->offload_features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM)) { +- net_dev->features |= NETIF_F_TSO6; +- if (efx_has_cap(efx, TX_TSO_V2_ENCAP)) +- net_dev->hw_enc_features |= NETIF_F_TSO6; +- } +- /* Check whether device supports TSO */ +- if (!efx->type->tso_versions || !efx->type->tso_versions(efx)) +- net_dev->features &= ~NETIF_F_ALL_TSO; ++ net_dev->features |= efx->type->offload_features; ++ ++ /* Add TSO features */ ++ if (efx->type->tso_versions && efx->type->tso_versions(efx)) ++ net_dev->features |= NETIF_F_TSO | NETIF_F_TSO6; ++ + /* Mask for features that also apply to VLAN devices */ + net_dev->vlan_features |= (NETIF_F_HW_CSUM | NETIF_F_SG | + NETIF_F_HIGHDMA | NETIF_F_ALL_TSO | + NETIF_F_RXCSUM); + ++ /* Determine user configurable features */ + net_dev->hw_features |= net_dev->features & ~efx->fixed_features; + + /* Disable receiving frames with bad FCS, by default. */ +diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c +index a2e511912e6a9..a690d139e1770 100644 +--- a/drivers/net/ethernet/smsc/smsc911x.c ++++ b/drivers/net/ethernet/smsc/smsc911x.c +@@ -1037,8 +1037,6 @@ static int smsc911x_mii_probe(struct net_device *dev) + return ret; + } + +- /* Indicate that the MAC is responsible for managing PHY PM */ +- phydev->mac_managed_pm = true; + phy_attached_info(phydev); + + phy_set_max_speed(phydev, SPEED_100); +@@ -1066,6 +1064,7 @@ static int smsc911x_mii_init(struct platform_device *pdev, + struct net_device *dev) + { + struct smsc911x_data *pdata = netdev_priv(dev); ++ struct phy_device *phydev; + int err = -ENXIO; + + pdata->mii_bus = mdiobus_alloc(); +@@ -1108,6 +1107,10 @@ static int smsc911x_mii_init(struct platform_device *pdev, + goto err_out_free_bus_2; + } + ++ phydev = phy_find_first(pdata->mii_bus); ++ if (phydev) ++ phydev->mac_managed_pm = true; ++ + return 0; + + err_out_free_bus_2: +diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h +index ec9c130276d89..54bb072aeb2d3 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/common.h ++++ b/drivers/net/ethernet/stmicro/stmmac/common.h +@@ -532,7 +532,6 @@ struct mac_device_info { + unsigned int xlgmac; + unsigned int num_vlan; + u32 vlan_filter[32]; +- unsigned int promisc; + bool vlan_fail_q_en; + u8 vlan_fail_q; + }; +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +index e5cfde1cbd5ce..188a00065f66c 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +@@ -481,12 +481,6 @@ static int dwmac4_add_hw_vlan_rx_fltr(struct net_device *dev, + if (vid > 4095) + return -EINVAL; + +- if (hw->promisc) { +- netdev_err(dev, +- "Adding VLAN in promisc mode not supported\n"); +- return -EPERM; +- } +- + /* Single Rx VLAN Filter */ + if (hw->num_vlan == 1) { + /* For single VLAN filter, VID 0 means VLAN promiscuous */ +@@ -536,12 +530,6 @@ static int dwmac4_del_hw_vlan_rx_fltr(struct net_device *dev, + { + int i, ret = 0; + +- if (hw->promisc) { +- netdev_err(dev, +- "Deleting VLAN in promisc mode not supported\n"); +- return -EPERM; +- } +- + /* Single Rx VLAN Filter */ + if (hw->num_vlan == 1) { + if ((hw->vlan_filter[0] & GMAC_VLAN_TAG_VID) == vid) { +@@ -566,39 +554,6 @@ static int dwmac4_del_hw_vlan_rx_fltr(struct net_device *dev, + return ret; + } + +-static void dwmac4_vlan_promisc_enable(struct net_device *dev, +- struct mac_device_info *hw) +-{ +- void __iomem *ioaddr = hw->pcsr; +- u32 value; +- u32 hash; +- u32 val; +- int i; +- +- /* Single Rx VLAN Filter */ +- if (hw->num_vlan == 1) { +- dwmac4_write_single_vlan(dev, 0); +- return; +- } +- +- /* Extended Rx VLAN Filter Enable */ +- for (i = 0; i < hw->num_vlan; i++) { +- if (hw->vlan_filter[i] & GMAC_VLAN_TAG_DATA_VEN) { +- val = hw->vlan_filter[i] & ~GMAC_VLAN_TAG_DATA_VEN; +- dwmac4_write_vlan_filter(dev, hw, i, val); +- } +- } +- +- hash = readl(ioaddr + GMAC_VLAN_HASH_TABLE); +- if (hash & GMAC_VLAN_VLHT) { +- value = readl(ioaddr + GMAC_VLAN_TAG); +- if (value & GMAC_VLAN_VTHM) { +- value &= ~GMAC_VLAN_VTHM; +- writel(value, ioaddr + GMAC_VLAN_TAG); +- } +- } +-} +- + static void dwmac4_restore_hw_vlan_rx_fltr(struct net_device *dev, + struct mac_device_info *hw) + { +@@ -718,22 +673,12 @@ static void dwmac4_set_filter(struct mac_device_info *hw, + } + + /* VLAN filtering */ +- if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) ++ if (dev->flags & IFF_PROMISC && !hw->vlan_fail_q_en) ++ value &= ~GMAC_PACKET_FILTER_VTFE; ++ else if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) + value |= GMAC_PACKET_FILTER_VTFE; + + writel(value, ioaddr + GMAC_PACKET_FILTER); +- +- if (dev->flags & IFF_PROMISC && !hw->vlan_fail_q_en) { +- if (!hw->promisc) { +- hw->promisc = 1; +- dwmac4_vlan_promisc_enable(dev, hw); +- } +- } else { +- if (hw->promisc) { +- hw->promisc = 0; +- dwmac4_restore_hw_vlan_rx_fltr(dev, hw); +- } +- } + } + + static void dwmac4_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, +diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c +index 00911e9360525..8ff1c84a23ce7 100644 +--- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c ++++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c +@@ -2817,6 +2817,7 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) + + err_free_phylink: + am65_cpsw_nuss_phylink_cleanup(common); ++ am65_cpts_release(common->cpts); + err_of_clear: + of_platform_device_destroy(common->mdio_dev, NULL); + err_pm_clear: +@@ -2845,6 +2846,7 @@ static int am65_cpsw_nuss_remove(struct platform_device *pdev) + */ + am65_cpsw_nuss_cleanup_ndev(common); + am65_cpsw_nuss_phylink_cleanup(common); ++ am65_cpts_release(common->cpts); + + of_platform_device_destroy(common->mdio_dev, NULL); + +diff --git a/drivers/net/ethernet/ti/am65-cpts.c b/drivers/net/ethernet/ti/am65-cpts.c +index e2f0fb286143b..9948ac14e68db 100644 +--- a/drivers/net/ethernet/ti/am65-cpts.c ++++ b/drivers/net/ethernet/ti/am65-cpts.c +@@ -918,14 +918,13 @@ static int am65_cpts_of_parse(struct am65_cpts *cpts, struct device_node *node) + return cpts_of_mux_clk_setup(cpts, node); + } + +-static void am65_cpts_release(void *data) ++void am65_cpts_release(struct am65_cpts *cpts) + { +- struct am65_cpts *cpts = data; +- + ptp_clock_unregister(cpts->ptp_clock); + am65_cpts_disable(cpts); + clk_disable_unprepare(cpts->refclk); + } ++EXPORT_SYMBOL_GPL(am65_cpts_release); + + struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs, + struct device_node *node) +@@ -1003,18 +1002,12 @@ struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs, + } + cpts->phc_index = ptp_clock_index(cpts->ptp_clock); + +- ret = devm_add_action_or_reset(dev, am65_cpts_release, cpts); +- if (ret) { +- dev_err(dev, "failed to add ptpclk reset action %d", ret); +- return ERR_PTR(ret); +- } +- + ret = devm_request_threaded_irq(dev, cpts->irq, NULL, + am65_cpts_interrupt, + IRQF_ONESHOT, dev_name(dev), cpts); + if (ret < 0) { + dev_err(cpts->dev, "error attaching irq %d\n", ret); +- return ERR_PTR(ret); ++ goto reset_ptpclk; + } + + dev_info(dev, "CPTS ver 0x%08x, freq:%u, add_val:%u\n", +@@ -1023,6 +1016,8 @@ struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs, + + return cpts; + ++reset_ptpclk: ++ am65_cpts_release(cpts); + refclk_disable: + clk_disable_unprepare(cpts->refclk); + return ERR_PTR(ret); +diff --git a/drivers/net/ethernet/ti/am65-cpts.h b/drivers/net/ethernet/ti/am65-cpts.h +index cf9fbc28fd032..c0ae0117e5737 100644 +--- a/drivers/net/ethernet/ti/am65-cpts.h ++++ b/drivers/net/ethernet/ti/am65-cpts.h +@@ -18,6 +18,7 @@ struct am65_cpts_estf_cfg { + }; + + #if IS_ENABLED(CONFIG_TI_K3_AM65_CPTS) ++void am65_cpts_release(struct am65_cpts *cpts); + struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs, + struct device_node *node); + int am65_cpts_phc_index(struct am65_cpts *cpts); +@@ -29,6 +30,10 @@ int am65_cpts_estf_enable(struct am65_cpts *cpts, int idx, + struct am65_cpts_estf_cfg *cfg); + void am65_cpts_estf_disable(struct am65_cpts *cpts, int idx); + #else ++static inline void am65_cpts_release(struct am65_cpts *cpts) ++{ ++} ++ + static inline struct am65_cpts *am65_cpts_create(struct device *dev, + void __iomem *regs, + struct device_node *node) +diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c +index 0b0c6c0764fe9..d0b5129439ed6 100644 +--- a/drivers/net/ieee802154/ca8210.c ++++ b/drivers/net/ieee802154/ca8210.c +@@ -1902,10 +1902,9 @@ static int ca8210_skb_tx( + struct ca8210_priv *priv + ) + { +- int status; + struct ieee802154_hdr header = { }; + struct secspec secspec; +- unsigned int mac_len; ++ int mac_len, status; + + dev_dbg(&priv->spi->dev, "%s called\n", __func__); + +diff --git a/drivers/net/ipa/gsi_trans.c b/drivers/net/ipa/gsi_trans.c +index 26b7f683a3e17..fa6863c265eb9 100644 +--- a/drivers/net/ipa/gsi_trans.c ++++ b/drivers/net/ipa/gsi_trans.c +@@ -153,7 +153,7 @@ int gsi_trans_pool_init_dma(struct device *dev, struct gsi_trans_pool *pool, + * gsi_trans_pool_exit_dma() can assume the total allocated + * size is exactly (count * size). + */ +- total_size = get_order(total_size) << PAGE_SHIFT; ++ total_size = PAGE_SIZE << get_order(total_size); + + virt = dma_alloc_coherent(dev, total_size, &addr, GFP_KERNEL); + if (!virt) +diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c +index 7a28e082436e4..d0c916a53d7ce 100644 +--- a/drivers/net/net_failover.c ++++ b/drivers/net/net_failover.c +@@ -130,14 +130,10 @@ static u16 net_failover_select_queue(struct net_device *dev, + txq = ops->ndo_select_queue(primary_dev, skb, sb_dev); + else + txq = netdev_pick_tx(primary_dev, skb, NULL); +- +- qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping; +- +- return txq; ++ } else { ++ txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : 0; + } + +- txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : 0; +- + /* Save the original txq to restore before passing to the driver */ + qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping; + +diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c +index b4ff9c5073a3c..9ab5eff502b71 100644 +--- a/drivers/net/phy/dp83869.c ++++ b/drivers/net/phy/dp83869.c +@@ -588,15 +588,13 @@ static int dp83869_of_init(struct phy_device *phydev) + &dp83869_internal_delay[0], + delay_size, true); + if (dp83869->rx_int_delay < 0) +- dp83869->rx_int_delay = +- dp83869_internal_delay[DP83869_CLK_DELAY_DEF]; ++ dp83869->rx_int_delay = DP83869_CLK_DELAY_DEF; + + dp83869->tx_int_delay = phy_get_internal_delay(phydev, dev, + &dp83869_internal_delay[0], + delay_size, false); + if (dp83869->tx_int_delay < 0) +- dp83869->tx_int_delay = +- dp83869_internal_delay[DP83869_CLK_DELAY_DEF]; ++ dp83869->tx_int_delay = DP83869_CLK_DELAY_DEF; + + return ret; + } +diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c +index 682987040ea82..da488cbb05428 100644 +--- a/drivers/net/vmxnet3/vmxnet3_drv.c ++++ b/drivers/net/vmxnet3/vmxnet3_drv.c +@@ -1688,7 +1688,9 @@ not_lro: + if (unlikely(rcd->ts)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rcd->tci); + +- if (adapter->netdev->features & NETIF_F_LRO) ++ /* Use GRO callback if UPT is enabled */ ++ if ((adapter->netdev->features & NETIF_F_LRO) && ++ !rq->shared->updateRxProd) + netif_receive_skb(skb); + else + napi_gro_receive(&rq->napi, skb); +diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h +index 3dbfc8a6924ed..1fcbd83f7ff2e 100644 +--- a/drivers/net/xen-netback/common.h ++++ b/drivers/net/xen-netback/common.h +@@ -166,7 +166,7 @@ struct xenvif_queue { /* Per-queue data for xenvif */ + struct pending_tx_info pending_tx_info[MAX_PENDING_REQS]; + grant_handle_t grant_tx_handle[MAX_PENDING_REQS]; + +- struct gnttab_copy tx_copy_ops[MAX_PENDING_REQS]; ++ struct gnttab_copy tx_copy_ops[2 * MAX_PENDING_REQS]; + struct gnttab_map_grant_ref tx_map_ops[MAX_PENDING_REQS]; + struct gnttab_unmap_grant_ref tx_unmap_ops[MAX_PENDING_REQS]; + /* passed to gnttab_[un]map_refs with pages under (un)mapping */ +diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c +index bf627af723bf9..5c266062c08f0 100644 +--- a/drivers/net/xen-netback/netback.c ++++ b/drivers/net/xen-netback/netback.c +@@ -334,6 +334,7 @@ static int xenvif_count_requests(struct xenvif_queue *queue, + struct xenvif_tx_cb { + u16 copy_pending_idx[XEN_NETBK_LEGACY_SLOTS_MAX + 1]; + u8 copy_count; ++ u32 split_mask; + }; + + #define XENVIF_TX_CB(skb) ((struct xenvif_tx_cb *)(skb)->cb) +@@ -361,6 +362,8 @@ static inline struct sk_buff *xenvif_alloc_skb(unsigned int size) + struct sk_buff *skb = + alloc_skb(size + NET_SKB_PAD + NET_IP_ALIGN, + GFP_ATOMIC | __GFP_NOWARN); ++ ++ BUILD_BUG_ON(sizeof(*XENVIF_TX_CB(skb)) > sizeof(skb->cb)); + if (unlikely(skb == NULL)) + return NULL; + +@@ -396,11 +399,13 @@ static void xenvif_get_requests(struct xenvif_queue *queue, + nr_slots = shinfo->nr_frags + 1; + + copy_count(skb) = 0; ++ XENVIF_TX_CB(skb)->split_mask = 0; + + /* Create copy ops for exactly data_len bytes into the skb head. */ + __skb_put(skb, data_len); + while (data_len > 0) { + int amount = data_len > txp->size ? txp->size : data_len; ++ bool split = false; + + cop->source.u.ref = txp->gref; + cop->source.domid = queue->vif->domid; +@@ -413,6 +418,13 @@ static void xenvif_get_requests(struct xenvif_queue *queue, + cop->dest.u.gmfn = virt_to_gfn(skb->data + skb_headlen(skb) + - data_len); + ++ /* Don't cross local page boundary! */ ++ if (cop->dest.offset + amount > XEN_PAGE_SIZE) { ++ amount = XEN_PAGE_SIZE - cop->dest.offset; ++ XENVIF_TX_CB(skb)->split_mask |= 1U << copy_count(skb); ++ split = true; ++ } ++ + cop->len = amount; + cop->flags = GNTCOPY_source_gref; + +@@ -420,7 +432,8 @@ static void xenvif_get_requests(struct xenvif_queue *queue, + pending_idx = queue->pending_ring[index]; + callback_param(queue, pending_idx).ctx = NULL; + copy_pending_idx(skb, copy_count(skb)) = pending_idx; +- copy_count(skb)++; ++ if (!split) ++ copy_count(skb)++; + + cop++; + data_len -= amount; +@@ -441,7 +454,8 @@ static void xenvif_get_requests(struct xenvif_queue *queue, + nr_slots--; + } else { + /* The copy op partially covered the tx_request. +- * The remainder will be mapped. ++ * The remainder will be mapped or copied in the next ++ * iteration. + */ + txp->offset += amount; + txp->size -= amount; +@@ -539,6 +553,13 @@ static int xenvif_tx_check_gop(struct xenvif_queue *queue, + pending_idx = copy_pending_idx(skb, i); + + newerr = (*gopp_copy)->status; ++ ++ /* Split copies need to be handled together. */ ++ if (XENVIF_TX_CB(skb)->split_mask & (1U << i)) { ++ (*gopp_copy)++; ++ if (!newerr) ++ newerr = (*gopp_copy)->status; ++ } + if (likely(!newerr)) { + /* The first frag might still have this slot mapped */ + if (i < copy_count(skb) - 1 || !sharedslot) +diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c +index 81f5550b670da..8224675f8de25 100644 +--- a/drivers/nvme/host/ioctl.c ++++ b/drivers/nvme/host/ioctl.c +@@ -387,7 +387,8 @@ static inline struct nvme_uring_cmd_pdu *nvme_uring_cmd_pdu( + return (struct nvme_uring_cmd_pdu *)&ioucmd->pdu; + } + +-static void nvme_uring_task_meta_cb(struct io_uring_cmd *ioucmd) ++static void nvme_uring_task_meta_cb(struct io_uring_cmd *ioucmd, ++ unsigned issue_flags) + { + struct nvme_uring_cmd_pdu *pdu = nvme_uring_cmd_pdu(ioucmd); + struct request *req = pdu->req; +@@ -408,17 +409,18 @@ static void nvme_uring_task_meta_cb(struct io_uring_cmd *ioucmd) + blk_rq_unmap_user(req->bio); + blk_mq_free_request(req); + +- io_uring_cmd_done(ioucmd, status, result); ++ io_uring_cmd_done(ioucmd, status, result, issue_flags); + } + +-static void nvme_uring_task_cb(struct io_uring_cmd *ioucmd) ++static void nvme_uring_task_cb(struct io_uring_cmd *ioucmd, ++ unsigned issue_flags) + { + struct nvme_uring_cmd_pdu *pdu = nvme_uring_cmd_pdu(ioucmd); + + if (pdu->bio) + blk_rq_unmap_user(pdu->bio); + +- io_uring_cmd_done(ioucmd, pdu->nvme_status, pdu->u.result); ++ io_uring_cmd_done(ioucmd, pdu->nvme_status, pdu->u.result, issue_flags); + } + + static enum rq_end_io_ret nvme_uring_cmd_end_io(struct request *req, +@@ -440,7 +442,7 @@ static enum rq_end_io_ret nvme_uring_cmd_end_io(struct request *req, + * Otherwise, move the completion to task work. + */ + if (cookie != NULL && blk_rq_is_poll(req)) +- nvme_uring_task_cb(ioucmd); ++ nvme_uring_task_cb(ioucmd, IO_URING_F_UNLOCKED); + else + io_uring_cmd_complete_in_task(ioucmd, nvme_uring_task_cb); + +@@ -462,7 +464,7 @@ static enum rq_end_io_ret nvme_uring_cmd_end_io_meta(struct request *req, + * Otherwise, move the completion to task work. + */ + if (cookie != NULL && blk_rq_is_poll(req)) +- nvme_uring_task_meta_cb(ioucmd); ++ nvme_uring_task_meta_cb(ioucmd, IO_URING_F_UNLOCKED); + else + io_uring_cmd_complete_in_task(ioucmd, nvme_uring_task_meta_cb); + +diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c +index 100f774bc97fa..60452f6a9f711 100644 +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -3547,6 +3547,8 @@ static const struct pci_device_id nvme_id_table[] = { + .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1d97, 0x2263), /* Lexar NM610 */ + .driver_data = NVME_QUIRK_BOGUS_NID, }, ++ { PCI_DEVICE(0x1d97, 0x1d97), /* Lexar NM620 */ ++ .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1d97, 0x2269), /* Lexar NM760 */ + .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(PCI_VENDOR_ID_AMAZON, 0x0061), +diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c +index 9e4d96e5a3f5a..575834cae3b9e 100644 +--- a/drivers/pci/controller/dwc/pcie-designware.c ++++ b/drivers/pci/controller/dwc/pcie-designware.c +@@ -645,11 +645,6 @@ void dw_pcie_setup(struct dw_pcie *pci) + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + } + +- val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); +- val &= ~PORT_LINK_FAST_LINK_MODE; +- val |= PORT_LINK_DLL_LINK_EN; +- dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); +- + if (of_property_read_bool(np, "snps,enable-cdm-check")) { + val = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS); + val |= PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS | +@@ -657,6 +652,11 @@ void dw_pcie_setup(struct dw_pcie *pci) + dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, val); + } + ++ val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); ++ val &= ~PORT_LINK_FAST_LINK_MODE; ++ val |= PORT_LINK_DLL_LINK_EN; ++ dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); ++ + of_property_read_u32(np, "num-lanes", &pci->num_lanes); + if (!pci->num_lanes) { + dev_dbg(pci->dev, "Using h/w default number of lanes\n"); +diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c +index 32c3edaf90385..5e7b82a2b13d0 100644 +--- a/drivers/pinctrl/pinctrl-amd.c ++++ b/drivers/pinctrl/pinctrl-amd.c +@@ -865,32 +865,34 @@ static const struct pinconf_ops amd_pinconf_ops = { + .pin_config_group_set = amd_pinconf_group_set, + }; + +-static void amd_gpio_irq_init(struct amd_gpio *gpio_dev) ++static void amd_gpio_irq_init_pin(struct amd_gpio *gpio_dev, int pin) + { +- struct pinctrl_desc *desc = gpio_dev->pctrl->desc; ++ const struct pin_desc *pd; + unsigned long flags; + u32 pin_reg, mask; +- int i; + + mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) | + BIT(INTERRUPT_MASK_OFF) | BIT(INTERRUPT_ENABLE_OFF) | + BIT(WAKE_CNTRL_OFF_S4); + +- for (i = 0; i < desc->npins; i++) { +- int pin = desc->pins[i].number; +- const struct pin_desc *pd = pin_desc_get(gpio_dev->pctrl, pin); +- +- if (!pd) +- continue; ++ pd = pin_desc_get(gpio_dev->pctrl, pin); ++ if (!pd) ++ return; + +- raw_spin_lock_irqsave(&gpio_dev->lock, flags); ++ raw_spin_lock_irqsave(&gpio_dev->lock, flags); ++ pin_reg = readl(gpio_dev->base + pin * 4); ++ pin_reg &= ~mask; ++ writel(pin_reg, gpio_dev->base + pin * 4); ++ raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); ++} + +- pin_reg = readl(gpio_dev->base + i * 4); +- pin_reg &= ~mask; +- writel(pin_reg, gpio_dev->base + i * 4); ++static void amd_gpio_irq_init(struct amd_gpio *gpio_dev) ++{ ++ struct pinctrl_desc *desc = gpio_dev->pctrl->desc; ++ int i; + +- raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); +- } ++ for (i = 0; i < desc->npins; i++) ++ amd_gpio_irq_init_pin(gpio_dev, i); + } + + #ifdef CONFIG_PM_SLEEP +@@ -943,8 +945,10 @@ static int amd_gpio_resume(struct device *dev) + for (i = 0; i < desc->npins; i++) { + int pin = desc->pins[i].number; + +- if (!amd_gpio_should_save(gpio_dev, pin)) ++ if (!amd_gpio_should_save(gpio_dev, pin)) { ++ amd_gpio_irq_init_pin(gpio_dev, pin); + continue; ++ } + + raw_spin_lock_irqsave(&gpio_dev->lock, flags); + gpio_dev->saved_regs[i] |= readl(gpio_dev->base + pin * 4) & PIN_IRQ_PENDING; +diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c +index 7f193f2b1566a..0b7cc6f063e00 100644 +--- a/drivers/pinctrl/pinctrl-at91-pio4.c ++++ b/drivers/pinctrl/pinctrl-at91-pio4.c +@@ -1176,7 +1176,6 @@ static int atmel_pinctrl_probe(struct platform_device *pdev) + dev_err(dev, "can't add the irq domain\n"); + return -ENODEV; + } +- atmel_pioctrl->irq_domain->name = "atmel gpio"; + + for (i = 0; i < atmel_pioctrl->npins; i++) { + int irq = irq_create_mapping(atmel_pioctrl->irq_domain, i); +diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c +index 3d5995cbcb782..c1d58939dd89a 100644 +--- a/drivers/pinctrl/pinctrl-ocelot.c ++++ b/drivers/pinctrl/pinctrl-ocelot.c +@@ -1202,7 +1202,7 @@ static int ocelot_pinmux_set_mux(struct pinctrl_dev *pctldev, + regmap_update_bits(info->map, REG_ALT(0, info, pin->pin), + BIT(p), f << p); + regmap_update_bits(info->map, REG_ALT(1, info, pin->pin), +- BIT(p), f << (p - 1)); ++ BIT(p), (f >> 1) << p); + + return 0; + } +diff --git a/drivers/platform/surface/aggregator/bus.c b/drivers/platform/surface/aggregator/bus.c +index de539938896e2..b501a79f2a08a 100644 +--- a/drivers/platform/surface/aggregator/bus.c ++++ b/drivers/platform/surface/aggregator/bus.c +@@ -485,8 +485,10 @@ int __ssam_register_clients(struct device *parent, struct ssam_controller *ctrl, + * device, so ignore it and continue with the next one. + */ + status = ssam_add_client_device(parent, ctrl, child); +- if (status && status != -ENODEV) ++ if (status && status != -ENODEV) { ++ fwnode_handle_put(child); + goto err; ++ } + } + + return 0; +diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c +index 4e28c55f0ea52..bd38c7dcae347 100644 +--- a/drivers/platform/x86/ideapad-laptop.c ++++ b/drivers/platform/x86/ideapad-laptop.c +@@ -1164,7 +1164,6 @@ static const struct key_entry ideapad_keymap[] = { + { KE_KEY, 65, { KEY_PROG4 } }, + { KE_KEY, 66, { KEY_TOUCHPAD_OFF } }, + { KE_KEY, 67, { KEY_TOUCHPAD_ON } }, +- { KE_KEY, 68, { KEY_TOUCHPAD_TOGGLE } }, + { KE_KEY, 128, { KEY_ESC } }, + + /* +@@ -1520,18 +1519,16 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_ + if (priv->features.ctrl_ps2_aux_port) + i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE); + +- if (send_events) { +- /* +- * On older models the EC controls the touchpad and toggles it +- * on/off itself, in this case we report KEY_TOUCHPAD_ON/_OFF. +- * If the EC did not toggle, report KEY_TOUCHPAD_TOGGLE. +- */ +- if (value != priv->r_touchpad_val) { +- ideapad_input_report(priv, value ? 67 : 66); +- sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad"); +- } else { +- ideapad_input_report(priv, 68); +- } ++ /* ++ * On older models the EC controls the touchpad and toggles it on/off ++ * itself, in this case we report KEY_TOUCHPAD_ON/_OFF. Some models do ++ * an acpi-notify with VPC bit 5 set on resume, so this function get ++ * called with send_events=true on every resume. Therefor if the EC did ++ * not toggle, do nothing to avoid sending spurious KEY_TOUCHPAD_TOGGLE. ++ */ ++ if (send_events && value != priv->r_touchpad_val) { ++ ideapad_input_report(priv, value ? 67 : 66); ++ sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad"); + } + + priv->r_touchpad_val = value; +diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c +index 17ec5825d13d7..be0fb9401202a 100644 +--- a/drivers/platform/x86/intel/pmc/core.c ++++ b/drivers/platform/x86/intel/pmc/core.c +@@ -958,7 +958,18 @@ static inline void pmc_core_reg_write(struct pmc_dev *pmcdev, int reg_offset, + + static inline u64 pmc_core_adjust_slp_s0_step(struct pmc_dev *pmcdev, u32 value) + { +- return (u64)value * pmcdev->map->slp_s0_res_counter_step; ++ /* ++ * ADL PCH does not have the SLP_S0 counter and LPM Residency counters are ++ * used as a workaround which uses 30.5 usec tick. All other client ++ * programs have the legacy SLP_S0 residency counter that is using the 122 ++ * usec tick. ++ */ ++ const int lpm_adj_x2 = pmcdev->map->lpm_res_counter_step_x2; ++ ++ if (pmcdev->map == &adl_reg_map) ++ return (u64)value * GET_X2_COUNTER((u64)lpm_adj_x2); ++ else ++ return (u64)value * pmcdev->map->slp_s0_res_counter_step; + } + + static int set_etr3(struct pmc_dev *pmcdev) +diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c +index a01a92769c1a3..74af3e593b2ca 100644 +--- a/drivers/platform/x86/think-lmi.c ++++ b/drivers/platform/x86/think-lmi.c +@@ -941,12 +941,23 @@ static ssize_t possible_values_show(struct kobject *kobj, struct kobj_attribute + { + struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); + +- if (!tlmi_priv.can_get_bios_selections) +- return -EOPNOTSUPP; +- + return sysfs_emit(buf, "%s\n", setting->possible_values); + } + ++static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, ++ char *buf) ++{ ++ struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); ++ ++ if (setting->possible_values) { ++ /* Figure out what setting type is as BIOS does not return this */ ++ if (strchr(setting->possible_values, ';')) ++ return sysfs_emit(buf, "enumeration\n"); ++ } ++ /* Anything else is going to be a string */ ++ return sysfs_emit(buf, "string\n"); ++} ++ + static ssize_t current_value_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +@@ -1036,14 +1047,30 @@ static struct kobj_attribute attr_possible_values = __ATTR_RO(possible_values); + + static struct kobj_attribute attr_current_val = __ATTR_RW_MODE(current_value, 0600); + ++static struct kobj_attribute attr_type = __ATTR_RO(type); ++ ++static umode_t attr_is_visible(struct kobject *kobj, ++ struct attribute *attr, int n) ++{ ++ struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); ++ ++ /* We don't want to display possible_values attributes if not available */ ++ if ((attr == &attr_possible_values.attr) && (!setting->possible_values)) ++ return 0; ++ ++ return attr->mode; ++} ++ + static struct attribute *tlmi_attrs[] = { + &attr_displ_name.attr, + &attr_current_val.attr, + &attr_possible_values.attr, ++ &attr_type.attr, + NULL + }; + + static const struct attribute_group tlmi_attr_group = { ++ .is_visible = attr_is_visible, + .attrs = tlmi_attrs, + }; + +@@ -1423,7 +1450,34 @@ static int tlmi_analyze(void) + if (ret || !setting->possible_values) + pr_info("Error retrieving possible values for %d : %s\n", + i, setting->display_name); ++ } else { ++ /* ++ * Older Thinkstations don't support the bios_selections API. ++ * Instead they store this as a [Optional:Option1,Option2] section of the ++ * name string. ++ * Try and pull that out if it's available. ++ */ ++ char *item, *optstart, *optend; ++ ++ if (!tlmi_setting(setting->index, &item, LENOVO_BIOS_SETTING_GUID)) { ++ optstart = strstr(item, "[Optional:"); ++ if (optstart) { ++ optstart += strlen("[Optional:"); ++ optend = strstr(optstart, "]"); ++ if (optend) ++ setting->possible_values = ++ kstrndup(optstart, optend - optstart, ++ GFP_KERNEL); ++ } ++ } + } ++ /* ++ * firmware-attributes requires that possible_values are separated by ';' but ++ * Lenovo FW uses ','. Replace appropriately. ++ */ ++ if (setting->possible_values) ++ strreplace(setting->possible_values, ',', ';'); ++ + kobject_init(&setting->kobj, &tlmi_attr_setting_ktype); + tlmi_priv.setting[i] = setting; + kfree(item); +diff --git a/drivers/ptp/ptp_qoriq.c b/drivers/ptp/ptp_qoriq.c +index 08f4cf0ad9e3c..8fa9772acf79b 100644 +--- a/drivers/ptp/ptp_qoriq.c ++++ b/drivers/ptp/ptp_qoriq.c +@@ -601,7 +601,7 @@ static int ptp_qoriq_probe(struct platform_device *dev) + return 0; + + no_clock: +- iounmap(ptp_qoriq->base); ++ iounmap(base); + no_ioremap: + release_resource(ptp_qoriq->rsrc); + no_resource: +diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c +index 2a9867abba20c..e6724a229d237 100644 +--- a/drivers/regulator/fixed.c ++++ b/drivers/regulator/fixed.c +@@ -215,7 +215,7 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) + drvdata->enable_clock = devm_clk_get(dev, NULL); + if (IS_ERR(drvdata->enable_clock)) { + dev_err(dev, "Can't get enable-clock from devicetree\n"); +- return -ENOENT; ++ return PTR_ERR(drvdata->enable_clock); + } + } else if (drvtype && drvtype->has_performance_state) { + drvdata->desc.ops = &fixed_voltage_domain_ops; +diff --git a/drivers/s390/crypto/vfio_ap_drv.c b/drivers/s390/crypto/vfio_ap_drv.c +index f43cfeabd2cc8..0afceb63ac43c 100644 +--- a/drivers/s390/crypto/vfio_ap_drv.c ++++ b/drivers/s390/crypto/vfio_ap_drv.c +@@ -54,8 +54,9 @@ static struct ap_driver vfio_ap_drv = { + + static void vfio_ap_matrix_dev_release(struct device *dev) + { +- struct ap_matrix_dev *matrix_dev = dev_get_drvdata(dev); ++ struct ap_matrix_dev *matrix_dev; + ++ matrix_dev = container_of(dev, struct ap_matrix_dev, device); + kfree(matrix_dev); + } + +diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c +index 6650f8c8e9b04..af22ffa8f6a25 100644 +--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c ++++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c +@@ -4768,7 +4768,7 @@ int megasas_task_abort_fusion(struct scsi_cmnd *scmd) + devhandle = megasas_get_tm_devhandle(scmd->device); + + if (devhandle == (u16)ULONG_MAX) { +- ret = SUCCESS; ++ ret = FAILED; + sdev_printk(KERN_INFO, scmd->device, + "task abort issued for invalid devhandle\n"); + mutex_unlock(&instance->reset_mutex); +@@ -4838,7 +4838,7 @@ int megasas_reset_target_fusion(struct scsi_cmnd *scmd) + devhandle = megasas_get_tm_devhandle(scmd->device); + + if (devhandle == (u16)ULONG_MAX) { +- ret = SUCCESS; ++ ret = FAILED; + sdev_printk(KERN_INFO, scmd->device, + "target reset issued for invalid devhandle\n"); + mutex_unlock(&instance->reset_mutex); +diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c +index 2ee9ea57554d7..14ae0a9c5d3d8 100644 +--- a/drivers/scsi/mpt3sas/mpt3sas_base.c ++++ b/drivers/scsi/mpt3sas/mpt3sas_base.c +@@ -6616,11 +6616,6 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) + else if (rc == -EAGAIN) + goto try_32bit_dma; + total_sz += sense_sz; +- ioc_info(ioc, +- "sense pool(0x%p)- dma(0x%llx): depth(%d)," +- "element_size(%d), pool_size(%d kB)\n", +- ioc->sense, (unsigned long long)ioc->sense_dma, ioc->scsiio_depth, +- SCSI_SENSE_BUFFERSIZE, sz / 1024); + /* reply pool, 4 byte align */ + sz = ioc->reply_free_queue_depth * ioc->reply_sz; + rc = _base_allocate_reply_pool(ioc, sz); +diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c +index ae28a03fa890b..1157b8869bcca 100644 +--- a/drivers/thunderbolt/quirks.c ++++ b/drivers/thunderbolt/quirks.c +@@ -26,6 +26,19 @@ static void quirk_clx_disable(struct tb_switch *sw) + tb_sw_dbg(sw, "disabling CL states\n"); + } + ++static void quirk_usb3_maximum_bandwidth(struct tb_switch *sw) ++{ ++ struct tb_port *port; ++ ++ tb_switch_for_each_port(sw, port) { ++ if (!tb_port_is_usb3_down(port)) ++ continue; ++ port->max_bw = 16376; ++ tb_port_dbg(port, "USB3 maximum bandwidth limited to %u Mb/s\n", ++ port->max_bw); ++ } ++} ++ + struct tb_quirk { + u16 hw_vendor_id; + u16 hw_device_id; +@@ -43,6 +56,24 @@ static const struct tb_quirk tb_quirks[] = { + * DP buffers. + */ + { 0x8087, 0x0b26, 0x0000, 0x0000, quirk_dp_credit_allocation }, ++ /* ++ * Limit the maximum USB3 bandwidth for the following Intel USB4 ++ * host routers due to a hardware issue. ++ */ ++ { 0x8087, PCI_DEVICE_ID_INTEL_ADL_NHI0, 0x0000, 0x0000, ++ quirk_usb3_maximum_bandwidth }, ++ { 0x8087, PCI_DEVICE_ID_INTEL_ADL_NHI1, 0x0000, 0x0000, ++ quirk_usb3_maximum_bandwidth }, ++ { 0x8087, PCI_DEVICE_ID_INTEL_RPL_NHI0, 0x0000, 0x0000, ++ quirk_usb3_maximum_bandwidth }, ++ { 0x8087, PCI_DEVICE_ID_INTEL_RPL_NHI1, 0x0000, 0x0000, ++ quirk_usb3_maximum_bandwidth }, ++ { 0x8087, PCI_DEVICE_ID_INTEL_MTL_M_NHI0, 0x0000, 0x0000, ++ quirk_usb3_maximum_bandwidth }, ++ { 0x8087, PCI_DEVICE_ID_INTEL_MTL_P_NHI0, 0x0000, 0x0000, ++ quirk_usb3_maximum_bandwidth }, ++ { 0x8087, PCI_DEVICE_ID_INTEL_MTL_P_NHI1, 0x0000, 0x0000, ++ quirk_usb3_maximum_bandwidth }, + /* + * CLx is not supported on AMD USB4 Yellow Carp and Pink Sardine platforms. + */ +diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h +index e11d973a8f9b6..f034723b1b40e 100644 +--- a/drivers/thunderbolt/tb.h ++++ b/drivers/thunderbolt/tb.h +@@ -252,6 +252,8 @@ struct tb_switch { + * @ctl_credits: Buffers reserved for control path + * @dma_credits: Number of credits allocated for DMA tunneling for all + * DMA paths through this port. ++ * @max_bw: Maximum possible bandwidth through this adapter if set to ++ * non-zero. + * + * In USB4 terminology this structure represents an adapter (protocol or + * lane adapter). +@@ -277,6 +279,7 @@ struct tb_port { + unsigned int total_credits; + unsigned int ctl_credits; + unsigned int dma_credits; ++ unsigned int max_bw; + }; + + /** +diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c +index cf8d4f769579e..3c821f5e44814 100644 +--- a/drivers/thunderbolt/usb4.c ++++ b/drivers/thunderbolt/usb4.c +@@ -1865,6 +1865,15 @@ int usb4_port_retimer_nvm_read(struct tb_port *port, u8 index, + usb4_port_retimer_nvm_read_block, &info); + } + ++static inline unsigned int ++usb4_usb3_port_max_bandwidth(const struct tb_port *port, unsigned int bw) ++{ ++ /* Take the possible bandwidth limitation into account */ ++ if (port->max_bw) ++ return min(bw, port->max_bw); ++ return bw; ++} ++ + /** + * usb4_usb3_port_max_link_rate() - Maximum support USB3 link rate + * @port: USB3 adapter port +@@ -1886,7 +1895,9 @@ int usb4_usb3_port_max_link_rate(struct tb_port *port) + return ret; + + lr = (val & ADP_USB3_CS_4_MSLR_MASK) >> ADP_USB3_CS_4_MSLR_SHIFT; +- return lr == ADP_USB3_CS_4_MSLR_20G ? 20000 : 10000; ++ ret = lr == ADP_USB3_CS_4_MSLR_20G ? 20000 : 10000; ++ ++ return usb4_usb3_port_max_bandwidth(port, ret); + } + + /** +@@ -1913,7 +1924,9 @@ int usb4_usb3_port_actual_link_rate(struct tb_port *port) + return 0; + + lr = val & ADP_USB3_CS_4_ALR_MASK; +- return lr == ADP_USB3_CS_4_ALR_20G ? 20000 : 10000; ++ ret = lr == ADP_USB3_CS_4_ALR_20G ? 20000 : 10000; ++ ++ return usb4_usb3_port_max_bandwidth(port, ret); + } + + static int usb4_usb3_port_cm_request(struct tb_port *port, bool request) +diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c +index c51883f34ac2b..cd98c04de0330 100644 +--- a/drivers/tty/serial/fsl_lpuart.c ++++ b/drivers/tty/serial/fsl_lpuart.c +@@ -582,7 +582,7 @@ static void lpuart_flush_buffer(struct uart_port *port) + sport->dma_tx_nents, DMA_TO_DEVICE); + sport->dma_tx_in_progress = false; + } +- dmaengine_terminate_all(chan); ++ dmaengine_terminate_async(chan); + } + + if (lpuart_is_32(sport)) { +@@ -1333,7 +1333,8 @@ static void lpuart_dma_rx_free(struct uart_port *port) + struct lpuart_port, port); + struct dma_chan *chan = sport->dma_rx_chan; + +- dmaengine_terminate_all(chan); ++ dmaengine_terminate_sync(chan); ++ del_timer_sync(&sport->lpuart_timer); + dma_unmap_sg(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); + kfree(sport->rx_ring.buf); + sport->rx_ring.tail = 0; +@@ -1757,7 +1758,6 @@ static int lpuart32_startup(struct uart_port *port) + static void lpuart_dma_shutdown(struct lpuart_port *sport) + { + if (sport->lpuart_dma_rx_use) { +- del_timer_sync(&sport->lpuart_timer); + lpuart_dma_rx_free(&sport->port); + sport->lpuart_dma_rx_use = false; + } +@@ -1766,7 +1766,7 @@ static void lpuart_dma_shutdown(struct lpuart_port *sport) + if (wait_event_interruptible_timeout(sport->dma_wait, + !sport->dma_tx_in_progress, msecs_to_jiffies(300)) <= 0) { + sport->dma_tx_in_progress = false; +- dmaengine_terminate_all(sport->dma_tx_chan); ++ dmaengine_terminate_sync(sport->dma_tx_chan); + } + sport->lpuart_dma_tx_use = false; + } +@@ -1917,10 +1917,8 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, + * Since timer function acqures sport->port.lock, need to stop before + * acquring same lock because otherwise del_timer_sync() can deadlock. + */ +- if (old && sport->lpuart_dma_rx_use) { +- del_timer_sync(&sport->lpuart_timer); ++ if (old && sport->lpuart_dma_rx_use) + lpuart_dma_rx_free(&sport->port); +- } + + spin_lock_irqsave(&sport->port.lock, flags); + +@@ -2154,10 +2152,8 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, + * Since timer function acqures sport->port.lock, need to stop before + * acquring same lock because otherwise del_timer_sync() can deadlock. + */ +- if (old && sport->lpuart_dma_rx_use) { +- del_timer_sync(&sport->lpuart_timer); ++ if (old && sport->lpuart_dma_rx_use) + lpuart_dma_rx_free(&sport->port); +- } + + spin_lock_irqsave(&sport->port.lock, flags); + +@@ -2850,7 +2846,6 @@ static int __maybe_unused lpuart_suspend(struct device *dev) + * Rx DMA path before suspend and start Rx DMA path on resume. + */ + if (irq_wake) { +- del_timer_sync(&sport->lpuart_timer); + lpuart_dma_rx_free(&sport->port); + } + +@@ -2867,7 +2862,7 @@ static int __maybe_unused lpuart_suspend(struct device *dev) + + if (sport->lpuart_dma_tx_use) { + sport->dma_tx_in_progress = false; +- dmaengine_terminate_all(sport->dma_tx_chan); ++ dmaengine_terminate_sync(sport->dma_tx_chan); + } + + if (sport->port.suspended && !irq_wake) +diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c +index 8cbbb002fefe0..086b509689839 100644 +--- a/drivers/usb/typec/ucsi/ucsi.c ++++ b/drivers/usb/typec/ucsi/ucsi.c +@@ -1039,9 +1039,8 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) + return NULL; + } + +-static int ucsi_register_port(struct ucsi *ucsi, int index) ++static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) + { +- struct ucsi_connector *con = &ucsi->connector[index]; + struct typec_capability *cap = &con->typec_cap; + enum typec_accessory *accessory = cap->accessory; + enum usb_role u_role = USB_ROLE_NONE; +@@ -1062,7 +1061,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) + init_completion(&con->complete); + mutex_init(&con->lock); + INIT_LIST_HEAD(&con->partner_tasks); +- con->num = index + 1; + con->ucsi = ucsi; + + cap->fwnode = ucsi_find_fwnode(con); +@@ -1204,7 +1202,7 @@ out_unlock: + */ + static int ucsi_init(struct ucsi *ucsi) + { +- struct ucsi_connector *con; ++ struct ucsi_connector *con, *connector; + u64 command, ntfy; + int ret; + int i; +@@ -1235,16 +1233,16 @@ static int ucsi_init(struct ucsi *ucsi) + } + + /* Allocate the connectors. Released in ucsi_unregister() */ +- ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1, +- sizeof(*ucsi->connector), GFP_KERNEL); +- if (!ucsi->connector) { ++ connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL); ++ if (!connector) { + ret = -ENOMEM; + goto err_reset; + } + + /* Register all connectors */ + for (i = 0; i < ucsi->cap.num_connectors; i++) { +- ret = ucsi_register_port(ucsi, i); ++ connector[i].num = i + 1; ++ ret = ucsi_register_port(ucsi, &connector[i]); + if (ret) + goto err_unregister; + } +@@ -1256,11 +1254,12 @@ static int ucsi_init(struct ucsi *ucsi) + if (ret < 0) + goto err_unregister; + ++ ucsi->connector = connector; + ucsi->ntfy = ntfy; + return 0; + + err_unregister: +- for (con = ucsi->connector; con->port; con++) { ++ for (con = connector; con->port; con++) { + ucsi_unregister_partner(con); + ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); + ucsi_unregister_port_psy(con); +@@ -1269,10 +1268,7 @@ err_unregister: + typec_unregister_port(con->port); + con->port = NULL; + } +- +- kfree(ucsi->connector); +- ucsi->connector = NULL; +- ++ kfree(connector); + err_reset: + memset(&ucsi->cap, 0, sizeof(ucsi->cap)); + ucsi_reset_ppm(ucsi); +diff --git a/drivers/video/fbdev/au1200fb.c b/drivers/video/fbdev/au1200fb.c +index 81c3154544287..b6b22fa4a8a01 100644 +--- a/drivers/video/fbdev/au1200fb.c ++++ b/drivers/video/fbdev/au1200fb.c +@@ -1040,6 +1040,9 @@ static int au1200fb_fb_check_var(struct fb_var_screeninfo *var, + u32 pixclock; + int screen_size, plane; + ++ if (!var->pixclock) ++ return -EINVAL; ++ + plane = fbdev->plane; + + /* Make sure that the mode respect all LCD controller and +diff --git a/drivers/video/fbdev/geode/lxfb_core.c b/drivers/video/fbdev/geode/lxfb_core.c +index 9d26592dbfce9..41fda498406c1 100644 +--- a/drivers/video/fbdev/geode/lxfb_core.c ++++ b/drivers/video/fbdev/geode/lxfb_core.c +@@ -235,6 +235,9 @@ static void get_modedb(struct fb_videomode **modedb, unsigned int *size) + + static int lxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) + { ++ if (!var->pixclock) ++ return -EINVAL; ++ + if (var->xres > 1920 || var->yres > 1440) + return -EINVAL; + +diff --git a/drivers/video/fbdev/intelfb/intelfbdrv.c b/drivers/video/fbdev/intelfb/intelfbdrv.c +index d4a2891a9a7ac..a93dd531d00df 100644 +--- a/drivers/video/fbdev/intelfb/intelfbdrv.c ++++ b/drivers/video/fbdev/intelfb/intelfbdrv.c +@@ -1219,6 +1219,9 @@ static int intelfb_check_var(struct fb_var_screeninfo *var, + + dinfo = GET_DINFO(info); + ++ if (!var->pixclock) ++ return -EINVAL; ++ + /* update the pitch */ + if (intelfbhw_validate_mode(dinfo, var) != 0) + return -EINVAL; +diff --git a/drivers/video/fbdev/nvidia/nvidia.c b/drivers/video/fbdev/nvidia/nvidia.c +index a6c3bc2222463..1b8904824ad83 100644 +--- a/drivers/video/fbdev/nvidia/nvidia.c ++++ b/drivers/video/fbdev/nvidia/nvidia.c +@@ -764,6 +764,8 @@ static int nvidiafb_check_var(struct fb_var_screeninfo *var, + int pitch, err = 0; + + NVTRACE_ENTER(); ++ if (!var->pixclock) ++ return -EINVAL; + + var->transp.offset = 0; + var->transp.length = 0; +diff --git a/drivers/video/fbdev/tgafb.c b/drivers/video/fbdev/tgafb.c +index 251dbd282f5ed..84d5daef97666 100644 +--- a/drivers/video/fbdev/tgafb.c ++++ b/drivers/video/fbdev/tgafb.c +@@ -173,6 +173,9 @@ tgafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) + { + struct tga_par *par = (struct tga_par *)info->par; + ++ if (!var->pixclock) ++ return -EINVAL; ++ + if (par->tga_type == TGA_TYPE_8PLANE) { + if (var->bits_per_pixel != 8) + return -EINVAL; +diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c +index 4b69945755e4f..f33ddd5922b8c 100644 +--- a/fs/btrfs/block-group.c ++++ b/fs/btrfs/block-group.c +@@ -3259,13 +3259,15 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans, + spin_unlock(&info->delalloc_root_lock); + + while (total) { +- bool reclaim; ++ struct btrfs_space_info *space_info; ++ bool reclaim = false; + + cache = btrfs_lookup_block_group(info, bytenr); + if (!cache) { + ret = -ENOENT; + break; + } ++ space_info = cache->space_info; + factor = btrfs_bg_type_to_factor(cache->flags); + + /* +@@ -3280,7 +3282,7 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans, + byte_in_group = bytenr - cache->start; + WARN_ON(byte_in_group > cache->length); + +- spin_lock(&cache->space_info->lock); ++ spin_lock(&space_info->lock); + spin_lock(&cache->lock); + + if (btrfs_test_opt(info, SPACE_CACHE) && +@@ -3293,23 +3295,23 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans, + old_val += num_bytes; + cache->used = old_val; + cache->reserved -= num_bytes; +- cache->space_info->bytes_reserved -= num_bytes; +- cache->space_info->bytes_used += num_bytes; +- cache->space_info->disk_used += num_bytes * factor; ++ space_info->bytes_reserved -= num_bytes; ++ space_info->bytes_used += num_bytes; ++ space_info->disk_used += num_bytes * factor; + spin_unlock(&cache->lock); +- spin_unlock(&cache->space_info->lock); ++ spin_unlock(&space_info->lock); + } else { + old_val -= num_bytes; + cache->used = old_val; + cache->pinned += num_bytes; +- btrfs_space_info_update_bytes_pinned(info, +- cache->space_info, num_bytes); +- cache->space_info->bytes_used -= num_bytes; +- cache->space_info->disk_used -= num_bytes * factor; ++ btrfs_space_info_update_bytes_pinned(info, space_info, ++ num_bytes); ++ space_info->bytes_used -= num_bytes; ++ space_info->disk_used -= num_bytes * factor; + + reclaim = should_reclaim_block_group(cache, num_bytes); + spin_unlock(&cache->lock); +- spin_unlock(&cache->space_info->lock); ++ spin_unlock(&space_info->lock); + + set_extent_dirty(&trans->transaction->pinned_extents, + bytenr, bytenr + num_bytes - 1, +diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h +index a3febabacec04..3bcef0c4d6fc4 100644 +--- a/fs/btrfs/ctree.h ++++ b/fs/btrfs/ctree.h +@@ -590,11 +590,8 @@ enum { + /* Indicate we have to finish a zone to do next allocation. */ + BTRFS_FS_NEED_ZONE_FINISH, + +- /* +- * Indicate metadata over-commit is disabled. This is set when active +- * zone tracking is needed. +- */ +- BTRFS_FS_NO_OVERCOMMIT, ++ /* This is set when active zone tracking is needed. */ ++ BTRFS_FS_ACTIVE_ZONE_TRACKING, + + #if BITS_PER_LONG == 32 + /* Indicate if we have error/warn message printed on 32bit systems */ +diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c +index f4023651dd68b..6a8f2bd350f4b 100644 +--- a/fs/btrfs/free-space-cache.c ++++ b/fs/btrfs/free-space-cache.c +@@ -2684,8 +2684,13 @@ static int __btrfs_add_free_space_zoned(struct btrfs_block_group *block_group, + bg_reclaim_threshold = READ_ONCE(sinfo->bg_reclaim_threshold); + + spin_lock(&ctl->tree_lock); ++ /* Count initial region as zone_unusable until it gets activated. */ + if (!used) + to_free = size; ++ else if (initial && ++ test_bit(BTRFS_FS_ACTIVE_ZONE_TRACKING, &block_group->fs_info->flags) && ++ (block_group->flags & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_SYSTEM))) ++ to_free = 0; + else if (initial) + to_free = block_group->zone_capacity; + else if (offset >= block_group->alloc_offset) +@@ -2713,7 +2718,8 @@ static int __btrfs_add_free_space_zoned(struct btrfs_block_group *block_group, + reclaimable_unusable = block_group->zone_unusable - + (block_group->length - block_group->zone_capacity); + /* All the region is now unusable. Mark it as unused and reclaim */ +- if (block_group->zone_unusable == block_group->length) { ++ if (block_group->zone_unusable == block_group->length && ++ block_group->alloc_offset) { + btrfs_mark_bg_unused(block_group); + } else if (bg_reclaim_threshold && + reclaimable_unusable >= +diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c +index c05f16a35bcaf..fe2fb81da46ba 100644 +--- a/fs/btrfs/ioctl.c ++++ b/fs/btrfs/ioctl.c +@@ -4621,7 +4621,9 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg) + } + + /* update qgroup status and info */ ++ mutex_lock(&fs_info->qgroup_ioctl_lock); + err = btrfs_run_qgroups(trans); ++ mutex_unlock(&fs_info->qgroup_ioctl_lock); + if (err < 0) + btrfs_handle_fs_error(fs_info, err, + "failed to update qgroup status and info"); +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index 5ac65384c9471..f8b0548988858 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -2812,13 +2812,22 @@ cleanup: + } + + /* +- * called from commit_transaction. Writes all changed qgroups to disk. ++ * Writes all changed qgroups to disk. ++ * Called by the transaction commit path and the qgroup assign ioctl. + */ + int btrfs_run_qgroups(struct btrfs_trans_handle *trans) + { + struct btrfs_fs_info *fs_info = trans->fs_info; + int ret = 0; + ++ /* ++ * In case we are called from the qgroup assign ioctl, assert that we ++ * are holding the qgroup_ioctl_lock, otherwise we can race with a quota ++ * disable operation (ioctl) and access a freed quota root. ++ */ ++ if (trans->transaction->state != TRANS_STATE_COMMIT_DOING) ++ lockdep_assert_held(&fs_info->qgroup_ioctl_lock); ++ + if (!fs_info->quota_root) + return ret; + +diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c +index 65c010159fb5f..c7642c00a65d0 100644 +--- a/fs/btrfs/space-info.c ++++ b/fs/btrfs/space-info.c +@@ -404,7 +404,7 @@ int btrfs_can_overcommit(struct btrfs_fs_info *fs_info, + return 0; + + used = btrfs_space_info_used(space_info, true); +- if (test_bit(BTRFS_FS_NO_OVERCOMMIT, &fs_info->flags) && ++ if (test_bit(BTRFS_FS_ACTIVE_ZONE_TRACKING, &fs_info->flags) && + (space_info->flags & BTRFS_BLOCK_GROUP_METADATA)) + avail = 0; + else +diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c +index d1f1da6820fb0..682b463a7633a 100644 +--- a/fs/btrfs/transaction.c ++++ b/fs/btrfs/transaction.c +@@ -2009,7 +2009,20 @@ static void cleanup_transaction(struct btrfs_trans_handle *trans, int err) + + if (current->journal_info == trans) + current->journal_info = NULL; +- btrfs_scrub_cancel(fs_info); ++ ++ /* ++ * If relocation is running, we can't cancel scrub because that will ++ * result in a deadlock. Before relocating a block group, relocation ++ * pauses scrub, then starts and commits a transaction before unpausing ++ * scrub. If the transaction commit is being done by the relocation ++ * task or triggered by another task and the relocation task is waiting ++ * for the commit, and we end up here due to an error in the commit ++ * path, then calling btrfs_scrub_cancel() will deadlock, as we are ++ * asking for scrub to stop while having it asked to be paused higher ++ * above in relocation code. ++ */ ++ if (!test_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags)) ++ btrfs_scrub_cancel(fs_info); + + kmem_cache_free(btrfs_trans_handle_cachep, trans); + } +diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c +index f02b8cbd6ec41..67b2aa552d228 100644 +--- a/fs/btrfs/volumes.c ++++ b/fs/btrfs/volumes.c +@@ -1379,8 +1379,17 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags, + * So, we need to add a special mount option to scan for + * later supers, using BTRFS_SUPER_MIRROR_MAX instead + */ +- flags |= FMODE_EXCL; + ++ /* ++ * Avoid using flag |= FMODE_EXCL here, as the systemd-udev may ++ * initiate the device scan which may race with the user's mount ++ * or mkfs command, resulting in failure. ++ * Since the device scan is solely for reading purposes, there is ++ * no need for FMODE_EXCL. Additionally, the devices are read again ++ * during the mount process. It is ok to get some inconsistent ++ * values temporarily, as the device paths of the fsid are the only ++ * required information for assembling the volume. ++ */ + bdev = blkdev_get_by_path(path, flags, holder); + if (IS_ERR(bdev)) + return ERR_CAST(bdev); +@@ -3286,8 +3295,15 @@ int btrfs_relocate_chunk(struct btrfs_fs_info *fs_info, u64 chunk_offset) + btrfs_scrub_pause(fs_info); + ret = btrfs_relocate_block_group(fs_info, chunk_offset); + btrfs_scrub_continue(fs_info); +- if (ret) ++ if (ret) { ++ /* ++ * If we had a transaction abort, stop all running scrubs. ++ * See transaction.c:cleanup_transaction() why we do it here. ++ */ ++ if (BTRFS_FS_ERROR(fs_info)) ++ btrfs_scrub_cancel(fs_info); + return ret; ++ } + + block_group = btrfs_lookup_block_group(fs_info, chunk_offset); + if (!block_group) +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +index 1b72004136ef8..e97c5a1ac95d6 100644 +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -538,8 +538,7 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache) + } + atomic_set(&zone_info->active_zones_left, + max_active_zones - nactive); +- /* Overcommit does not work well with active zone tacking. */ +- set_bit(BTRFS_FS_NO_OVERCOMMIT, &fs_info->flags); ++ set_bit(BTRFS_FS_ACTIVE_ZONE_TRACKING, &fs_info->flags); + } + + /* Validate superblock log */ +@@ -1576,9 +1575,19 @@ void btrfs_calc_zone_unusable(struct btrfs_block_group *cache) + return; + + WARN_ON(cache->bytes_super != 0); +- unusable = (cache->alloc_offset - cache->used) + +- (cache->length - cache->zone_capacity); +- free = cache->zone_capacity - cache->alloc_offset; ++ ++ /* Check for block groups never get activated */ ++ if (test_bit(BTRFS_FS_ACTIVE_ZONE_TRACKING, &cache->fs_info->flags) && ++ cache->flags & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_SYSTEM) && ++ !test_bit(BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE, &cache->runtime_flags) && ++ cache->alloc_offset == 0) { ++ unusable = cache->length; ++ free = 0; ++ } else { ++ unusable = (cache->alloc_offset - cache->used) + ++ (cache->length - cache->zone_capacity); ++ free = cache->zone_capacity - cache->alloc_offset; ++ } + + /* We only need ->free_space in ALLOC_SEQ block groups */ + cache->cached = BTRFS_CACHE_FINISHED; +@@ -1915,7 +1924,11 @@ bool btrfs_zone_activate(struct btrfs_block_group *block_group) + + /* Successfully activated all the zones */ + set_bit(BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE, &block_group->runtime_flags); +- space_info->active_total_bytes += block_group->length; ++ WARN_ON(block_group->alloc_offset != 0); ++ if (block_group->zone_unusable == block_group->length) { ++ block_group->zone_unusable = block_group->length - block_group->zone_capacity; ++ space_info->bytes_zone_unusable -= block_group->zone_capacity; ++ } + spin_unlock(&block_group->lock); + btrfs_try_granting_tickets(fs_info, space_info); + spin_unlock(&space_info->lock); +@@ -2278,7 +2291,7 @@ int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info) + u64 avail; + + spin_lock(&block_group->lock); +- if (block_group->reserved || ++ if (block_group->reserved || block_group->alloc_offset == 0 || + (block_group->flags & BTRFS_BLOCK_GROUP_SYSTEM)) { + spin_unlock(&block_group->lock); + continue; +diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h +index 388b745a978e2..b6c38896fb2db 100644 +--- a/fs/cifs/cifsfs.h ++++ b/fs/cifs/cifsfs.h +@@ -118,7 +118,10 @@ extern const struct dentry_operations cifs_ci_dentry_ops; + #ifdef CONFIG_CIFS_DFS_UPCALL + extern struct vfsmount *cifs_dfs_d_automount(struct path *path); + #else +-#define cifs_dfs_d_automount NULL ++static inline struct vfsmount *cifs_dfs_d_automount(struct path *path) ++{ ++ return ERR_PTR(-EREMOTE); ++} + #endif + + /* Functions related to symlinks */ +diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h +index bc4475f6c0827..98513f5af3f96 100644 +--- a/fs/cifs/cifsproto.h ++++ b/fs/cifs/cifsproto.h +@@ -691,5 +691,6 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options) + + struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon); + void cifs_put_tcon_super(struct super_block *sb); ++int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry); + + #endif /* _CIFSPROTO_H */ +diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c +index 6c6a7fc47f3e3..c90d4ec9292ca 100644 +--- a/fs/cifs/cifssmb.c ++++ b/fs/cifs/cifssmb.c +@@ -70,7 +70,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) + struct cifs_ses *ses; + struct TCP_Server_Info *server; + struct nls_table *nls_codepage; +- int retries; + + /* + * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for +@@ -98,45 +97,9 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) + } + spin_unlock(&tcon->tc_lock); + +- retries = server->nr_targets; +- +- /* +- * Give demultiplex thread up to 10 seconds to each target available for +- * reconnect -- should be greater than cifs socket timeout which is 7 +- * seconds. +- */ +- while (server->tcpStatus == CifsNeedReconnect) { +- rc = wait_event_interruptible_timeout(server->response_q, +- (server->tcpStatus != CifsNeedReconnect), +- 10 * HZ); +- if (rc < 0) { +- cifs_dbg(FYI, "%s: aborting reconnect due to a received signal by the process\n", +- __func__); +- return -ERESTARTSYS; +- } +- +- /* are we still trying to reconnect? */ +- spin_lock(&server->srv_lock); +- if (server->tcpStatus != CifsNeedReconnect) { +- spin_unlock(&server->srv_lock); +- break; +- } +- spin_unlock(&server->srv_lock); +- +- if (retries && --retries) +- continue; +- +- /* +- * on "soft" mounts we wait once. Hard mounts keep +- * retrying until process is killed or server comes +- * back on-line +- */ +- if (!tcon->retry) { +- cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n"); +- return -EHOSTDOWN; +- } +- retries = server->nr_targets; +- } ++ rc = cifs_wait_for_server_reconnect(server, tcon->retry); ++ if (rc) ++ return rc; + + spin_lock(&ses->chan_lock); + if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) { +@@ -4356,8 +4319,13 @@ CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, + return -ENODEV; + + getDFSRetry: +- rc = smb_init(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc, (void **) &pSMB, +- (void **) &pSMBr); ++ /* ++ * Use smb_init_no_reconnect() instead of smb_init() as ++ * CIFSGetDFSRefer() may be called from cifs_reconnect_tcon() and thus ++ * causing an infinite recursion. ++ */ ++ rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc, ++ (void **)&pSMB, (void **)&pSMBr); + if (rc) + return rc; + +diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c +index 7aecb1646b6fc..077c88c49dfdf 100644 +--- a/fs/cifs/connect.c ++++ b/fs/cifs/connect.c +@@ -261,31 +261,42 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, + cifs_chan_update_iface(ses, server); + + spin_lock(&ses->chan_lock); +- if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) +- goto next_session; ++ if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) { ++ spin_unlock(&ses->chan_lock); ++ continue; ++ } + + if (mark_smb_session) + CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses); + else + cifs_chan_set_need_reconnect(ses, server); + ++ cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n", ++ __func__, ses->chans_need_reconnect); ++ + /* If all channels need reconnect, then tcon needs reconnect */ +- if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses)) +- goto next_session; ++ if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { ++ spin_unlock(&ses->chan_lock); ++ continue; ++ } ++ spin_unlock(&ses->chan_lock); + ++ spin_lock(&ses->ses_lock); + ses->ses_status = SES_NEED_RECON; ++ spin_unlock(&ses->ses_lock); + + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + tcon->need_reconnect = true; ++ spin_lock(&tcon->tc_lock); + tcon->status = TID_NEED_RECON; ++ spin_unlock(&tcon->tc_lock); + } + if (ses->tcon_ipc) { + ses->tcon_ipc->need_reconnect = true; ++ spin_lock(&ses->tcon_ipc->tc_lock); + ses->tcon_ipc->status = TID_NEED_RECON; ++ spin_unlock(&ses->tcon_ipc->tc_lock); + } +- +-next_session: +- spin_unlock(&ses->chan_lock); + } + spin_unlock(&cifs_tcp_ses_lock); + } +@@ -4050,11 +4061,19 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses, + + /* only send once per connect */ + spin_lock(&server->srv_lock); +- if (!server->ops->need_neg(server) || ++ if (server->tcpStatus != CifsGood && ++ server->tcpStatus != CifsNew && + server->tcpStatus != CifsNeedNegotiate) { ++ spin_unlock(&server->srv_lock); ++ return -EHOSTDOWN; ++ } ++ ++ if (!server->ops->need_neg(server) && ++ server->tcpStatus == CifsGood) { + spin_unlock(&server->srv_lock); + return 0; + } ++ + server->tcpStatus = CifsInNegotiate; + spin_unlock(&server->srv_lock); + +@@ -4082,39 +4101,48 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, + struct nls_table *nls_info) + { + int rc = -ENOSYS; +- struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr; +- struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr; ++ struct TCP_Server_Info *pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; ++ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&pserver->dstaddr; ++ struct sockaddr_in *addr = (struct sockaddr_in *)&pserver->dstaddr; + bool is_binding = false; + + spin_lock(&ses->ses_lock); +- if (server->dstaddr.ss_family == AF_INET6) +- scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI6", &addr6->sin6_addr); +- else +- scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI4", &addr->sin_addr); ++ cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n", ++ __func__, ses->chans_need_reconnect); + + if (ses->ses_status != SES_GOOD && + ses->ses_status != SES_NEW && + ses->ses_status != SES_NEED_RECON) { + spin_unlock(&ses->ses_lock); +- return 0; ++ return -EHOSTDOWN; + } + + /* only send once per connect */ + spin_lock(&ses->chan_lock); +- if (CIFS_ALL_CHANS_GOOD(ses) || +- cifs_chan_in_reconnect(ses, server)) { ++ if (CIFS_ALL_CHANS_GOOD(ses)) { ++ if (ses->ses_status == SES_NEED_RECON) ++ ses->ses_status = SES_GOOD; + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + return 0; + } +- is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); ++ + cifs_chan_set_in_reconnect(ses, server); ++ is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); + spin_unlock(&ses->chan_lock); + + if (!is_binding) + ses->ses_status = SES_IN_SETUP; + spin_unlock(&ses->ses_lock); + ++ /* update ses ip_addr only for primary chan */ ++ if (server == pserver) { ++ if (server->dstaddr.ss_family == AF_INET6) ++ scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI6", &addr6->sin6_addr); ++ else ++ scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI4", &addr->sin_addr); ++ } ++ + if (!is_binding) { + ses->capabilities = server->capabilities; + if (!linuxExtEnabled) +diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c +index 4e54736a06996..832856aef4b7a 100644 +--- a/fs/cifs/misc.c ++++ b/fs/cifs/misc.c +@@ -1382,3 +1382,47 @@ int cifs_inval_name_dfs_link_error(const unsigned int xid, + return 0; + } + #endif ++ ++int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry) ++{ ++ int timeout = 10; ++ int rc; ++ ++ spin_lock(&server->srv_lock); ++ if (server->tcpStatus != CifsNeedReconnect) { ++ spin_unlock(&server->srv_lock); ++ return 0; ++ } ++ timeout *= server->nr_targets; ++ spin_unlock(&server->srv_lock); ++ ++ /* ++ * Give demultiplex thread up to 10 seconds to each target available for ++ * reconnect -- should be greater than cifs socket timeout which is 7 ++ * seconds. ++ * ++ * On "soft" mounts we wait once. Hard mounts keep retrying until ++ * process is killed or server comes back on-line. ++ */ ++ do { ++ rc = wait_event_interruptible_timeout(server->response_q, ++ (server->tcpStatus != CifsNeedReconnect), ++ timeout * HZ); ++ if (rc < 0) { ++ cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n", ++ __func__); ++ return -ERESTARTSYS; ++ } ++ ++ /* are we still trying to reconnect? */ ++ spin_lock(&server->srv_lock); ++ if (server->tcpStatus != CifsNeedReconnect) { ++ spin_unlock(&server->srv_lock); ++ return 0; ++ } ++ spin_unlock(&server->srv_lock); ++ } while (retry); ++ ++ cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__); ++ return -EHOSTDOWN; ++} +diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c +index 6e6e44d8b4c79..b37379b62cc77 100644 +--- a/fs/cifs/smb2pdu.c ++++ b/fs/cifs/smb2pdu.c +@@ -139,72 +139,12 @@ out: + return; + } + +-static int wait_for_server_reconnect(struct TCP_Server_Info *server, +- __le16 smb2_command, bool retry) +-{ +- int timeout = 10; +- int rc; +- +- spin_lock(&server->srv_lock); +- if (server->tcpStatus != CifsNeedReconnect) { +- spin_unlock(&server->srv_lock); +- return 0; +- } +- timeout *= server->nr_targets; +- spin_unlock(&server->srv_lock); +- +- /* +- * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE +- * here since they are implicitly done when session drops. +- */ +- switch (smb2_command) { +- /* +- * BB Should we keep oplock break and add flush to exceptions? +- */ +- case SMB2_TREE_DISCONNECT: +- case SMB2_CANCEL: +- case SMB2_CLOSE: +- case SMB2_OPLOCK_BREAK: +- return -EAGAIN; +- } +- +- /* +- * Give demultiplex thread up to 10 seconds to each target available for +- * reconnect -- should be greater than cifs socket timeout which is 7 +- * seconds. +- * +- * On "soft" mounts we wait once. Hard mounts keep retrying until +- * process is killed or server comes back on-line. +- */ +- do { +- rc = wait_event_interruptible_timeout(server->response_q, +- (server->tcpStatus != CifsNeedReconnect), +- timeout * HZ); +- if (rc < 0) { +- cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n", +- __func__); +- return -ERESTARTSYS; +- } +- +- /* are we still trying to reconnect? */ +- spin_lock(&server->srv_lock); +- if (server->tcpStatus != CifsNeedReconnect) { +- spin_unlock(&server->srv_lock); +- return 0; +- } +- spin_unlock(&server->srv_lock); +- } while (retry); +- +- cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__); +- return -EHOSTDOWN; +-} +- + static int + smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, + struct TCP_Server_Info *server) + { + int rc = 0; +- struct nls_table *nls_codepage; ++ struct nls_table *nls_codepage = NULL; + struct cifs_ses *ses; + + /* +@@ -239,7 +179,28 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, + (!tcon->ses->server) || !server) + return -EIO; + +- rc = wait_for_server_reconnect(server, smb2_command, tcon->retry); ++ spin_lock(&server->srv_lock); ++ if (server->tcpStatus == CifsNeedReconnect) { ++ /* ++ * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE ++ * here since they are implicitly done when session drops. ++ */ ++ switch (smb2_command) { ++ /* ++ * BB Should we keep oplock break and add flush to exceptions? ++ */ ++ case SMB2_TREE_DISCONNECT: ++ case SMB2_CANCEL: ++ case SMB2_CLOSE: ++ case SMB2_OPLOCK_BREAK: ++ spin_unlock(&server->srv_lock); ++ return -EAGAIN; ++ } ++ } ++ spin_unlock(&server->srv_lock); ++ ++again: ++ rc = cifs_wait_for_server_reconnect(server, tcon->retry); + if (rc) + return rc; + +@@ -255,8 +216,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, + tcon->ses->chans_need_reconnect, + tcon->need_reconnect); + +- nls_codepage = load_nls_default(); +- ++ mutex_lock(&ses->session_mutex); + /* + * Recheck after acquire mutex. If another thread is negotiating + * and the server never sends an answer the socket will be closed +@@ -265,28 +225,38 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, + spin_lock(&server->srv_lock); + if (server->tcpStatus == CifsNeedReconnect) { + spin_unlock(&server->srv_lock); ++ mutex_unlock(&ses->session_mutex); ++ ++ if (tcon->retry) ++ goto again; ++ + rc = -EHOSTDOWN; + goto out; + } + spin_unlock(&server->srv_lock); + ++ nls_codepage = load_nls_default(); ++ + /* + * need to prevent multiple threads trying to simultaneously + * reconnect the same SMB session + */ ++ spin_lock(&ses->ses_lock); + spin_lock(&ses->chan_lock); +- if (!cifs_chan_needs_reconnect(ses, server)) { ++ if (!cifs_chan_needs_reconnect(ses, server) && ++ ses->ses_status == SES_GOOD) { + spin_unlock(&ses->chan_lock); +- ++ spin_unlock(&ses->ses_lock); + /* this means that we only need to tree connect */ + if (tcon->need_reconnect) + goto skip_sess_setup; + ++ mutex_unlock(&ses->session_mutex); + goto out; + } + spin_unlock(&ses->chan_lock); ++ spin_unlock(&ses->ses_lock); + +- mutex_lock(&ses->session_mutex); + rc = cifs_negotiate_protocol(0, ses, server); + if (!rc) { + rc = cifs_setup_session(0, ses, server, nls_codepage); +@@ -302,10 +272,8 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, + mutex_unlock(&ses->session_mutex); + goto out; + } +- mutex_unlock(&ses->session_mutex); + + skip_sess_setup: +- mutex_lock(&ses->session_mutex); + if (!tcon->need_reconnect) { + mutex_unlock(&ses->session_mutex); + goto out; +@@ -320,7 +288,7 @@ skip_sess_setup: + cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); + if (rc) { + /* If sess reconnected but tcon didn't, something strange ... */ +- pr_warn_once("reconnect tcon failed rc = %d\n", rc); ++ cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc); + goto out; + } + +@@ -1292,9 +1260,9 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) + if (rc) + return rc; + +- spin_lock(&ses->chan_lock); +- is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); +- spin_unlock(&ses->chan_lock); ++ spin_lock(&ses->ses_lock); ++ is_binding = (ses->ses_status == SES_GOOD); ++ spin_unlock(&ses->ses_lock); + + if (is_binding) { + req->hdr.SessionId = cpu_to_le64(ses->Suid); +@@ -1452,9 +1420,9 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) + goto out_put_spnego_key; + } + +- spin_lock(&ses->chan_lock); +- is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); +- spin_unlock(&ses->chan_lock); ++ spin_lock(&ses->ses_lock); ++ is_binding = (ses->ses_status == SES_GOOD); ++ spin_unlock(&ses->ses_lock); + + /* keep session key if binding */ + if (!is_binding) { +@@ -1578,9 +1546,9 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) + + cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); + +- spin_lock(&ses->chan_lock); +- is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); +- spin_unlock(&ses->chan_lock); ++ spin_lock(&ses->ses_lock); ++ is_binding = (ses->ses_status == SES_GOOD); ++ spin_unlock(&ses->ses_lock); + + /* keep existing ses id and flags if binding */ + if (!is_binding) { +@@ -1646,9 +1614,9 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) + + rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; + +- spin_lock(&ses->chan_lock); +- is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); +- spin_unlock(&ses->chan_lock); ++ spin_lock(&ses->ses_lock); ++ is_binding = (ses->ses_status == SES_GOOD); ++ spin_unlock(&ses->ses_lock); + + /* keep existing ses id and flags if binding */ + if (!is_binding) { +diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c +index d827b7547ffad..790acf65a0926 100644 +--- a/fs/cifs/smb2transport.c ++++ b/fs/cifs/smb2transport.c +@@ -81,6 +81,7 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) + struct cifs_ses *ses = NULL; + int i; + int rc = 0; ++ bool is_binding = false; + + spin_lock(&cifs_tcp_ses_lock); + +@@ -97,9 +98,12 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) + goto out; + + found: ++ spin_lock(&ses->ses_lock); + spin_lock(&ses->chan_lock); +- if (cifs_chan_needs_reconnect(ses, server) && +- !CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { ++ ++ is_binding = (cifs_chan_needs_reconnect(ses, server) && ++ ses->ses_status == SES_GOOD); ++ if (is_binding) { + /* + * If we are in the process of binding a new channel + * to an existing session, use the master connection +@@ -107,6 +111,7 @@ found: + */ + memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE); + spin_unlock(&ses->chan_lock); ++ spin_unlock(&ses->ses_lock); + goto out; + } + +@@ -119,10 +124,12 @@ found: + if (chan->server == server) { + memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE); + spin_unlock(&ses->chan_lock); ++ spin_unlock(&ses->ses_lock); + goto out; + } + } + spin_unlock(&ses->chan_lock); ++ spin_unlock(&ses->ses_lock); + + cifs_dbg(VFS, + "%s: Could not find channel signing key for session 0x%llx\n", +@@ -392,11 +399,15 @@ generate_smb3signingkey(struct cifs_ses *ses, + bool is_binding = false; + int chan_index = 0; + ++ spin_lock(&ses->ses_lock); + spin_lock(&ses->chan_lock); +- is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses); ++ is_binding = (cifs_chan_needs_reconnect(ses, server) && ++ ses->ses_status == SES_GOOD); ++ + chan_index = cifs_ses_get_chan_index(ses, server); + /* TODO: introduce ref counting for channels when the can be freed */ + spin_unlock(&ses->chan_lock); ++ spin_unlock(&ses->ses_lock); + + /* + * All channels use the same encryption/decryption keys but +diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c +index d70da78e698d2..70e76359909cc 100644 +--- a/fs/nfs/nfs4proc.c ++++ b/fs/nfs/nfs4proc.c +@@ -1980,8 +1980,7 @@ _nfs4_opendata_reclaim_to_nfs4_state(struct nfs4_opendata *data) + if (!data->rpc_done) { + if (data->rpc_status) + return ERR_PTR(data->rpc_status); +- /* cached opens have already been processed */ +- goto update; ++ return nfs4_try_open_cached(data); + } + + ret = nfs_refresh_inode(inode, &data->f_attr); +@@ -1990,7 +1989,7 @@ _nfs4_opendata_reclaim_to_nfs4_state(struct nfs4_opendata *data) + + if (data->o_res.delegation_type != 0) + nfs4_opendata_check_deleg(data, state); +-update: ++ + if (!update_open_stateid(state, &data->o_res.stateid, + NULL, data->o_arg.fmode)) + return ERR_PTR(-EAGAIN); +diff --git a/fs/verity/enable.c b/fs/verity/enable.c +index df6b499bf6a14..400c264bf8930 100644 +--- a/fs/verity/enable.c ++++ b/fs/verity/enable.c +@@ -390,25 +390,27 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg) + goto out_drop_write; + + err = enable_verity(filp, &arg); +- if (err) +- goto out_allow_write_access; + + /* +- * Some pages of the file may have been evicted from pagecache after +- * being used in the Merkle tree construction, then read into pagecache +- * again by another process reading from the file concurrently. Since +- * these pages didn't undergo verification against the file digest which +- * fs-verity now claims to be enforcing, we have to wipe the pagecache +- * to ensure that all future reads are verified. ++ * We no longer drop the inode's pagecache after enabling verity. This ++ * used to be done to try to avoid a race condition where pages could be ++ * evicted after being used in the Merkle tree construction, then ++ * re-instantiated by a concurrent read. Such pages are unverified, and ++ * the backing storage could have filled them with different content, so ++ * they shouldn't be used to fulfill reads once verity is enabled. ++ * ++ * But, dropping the pagecache has a big performance impact, and it ++ * doesn't fully solve the race condition anyway. So for those reasons, ++ * and also because this race condition isn't very important relatively ++ * speaking (especially for small-ish files, where the chance of a page ++ * being used, evicted, *and* re-instantiated all while enabling verity ++ * is quite small), we no longer drop the inode's pagecache. + */ +- filemap_write_and_wait(inode->i_mapping); +- invalidate_inode_pages2(inode->i_mapping); + + /* + * allow_write_access() is needed to pair with deny_write_access(). + * Regardless, the filesystem won't allow writing to verity files. + */ +-out_allow_write_access: + allow_write_access(filp); + out_drop_write: + mnt_drop_write_file(filp); +diff --git a/fs/zonefs/Makefile b/fs/zonefs/Makefile +index 9fe54f5319f22..645f7229de4a0 100644 +--- a/fs/zonefs/Makefile ++++ b/fs/zonefs/Makefile +@@ -3,4 +3,4 @@ ccflags-y += -I$(src) + + obj-$(CONFIG_ZONEFS_FS) += zonefs.o + +-zonefs-y := super.o sysfs.o ++zonefs-y := super.o file.o sysfs.o +diff --git a/fs/zonefs/file.c b/fs/zonefs/file.c +new file mode 100644 +index 0000000000000..63cd50840419c +--- /dev/null ++++ b/fs/zonefs/file.c +@@ -0,0 +1,902 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Simple file system for zoned block devices exposing zones as files. ++ * ++ * Copyright (C) 2022 Western Digital Corporation or its affiliates. ++ */ ++#include <linux/module.h> ++#include <linux/pagemap.h> ++#include <linux/iomap.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/blkdev.h> ++#include <linux/statfs.h> ++#include <linux/writeback.h> ++#include <linux/quotaops.h> ++#include <linux/seq_file.h> ++#include <linux/parser.h> ++#include <linux/uio.h> ++#include <linux/mman.h> ++#include <linux/sched/mm.h> ++#include <linux/task_io_accounting_ops.h> ++ ++#include "zonefs.h" ++ ++#include "trace.h" ++ ++static int zonefs_read_iomap_begin(struct inode *inode, loff_t offset, ++ loff_t length, unsigned int flags, ++ struct iomap *iomap, struct iomap *srcmap) ++{ ++ struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ struct super_block *sb = inode->i_sb; ++ loff_t isize; ++ ++ /* ++ * All blocks are always mapped below EOF. If reading past EOF, ++ * act as if there is a hole up to the file maximum size. ++ */ ++ mutex_lock(&zi->i_truncate_mutex); ++ iomap->bdev = inode->i_sb->s_bdev; ++ iomap->offset = ALIGN_DOWN(offset, sb->s_blocksize); ++ isize = i_size_read(inode); ++ if (iomap->offset >= isize) { ++ iomap->type = IOMAP_HOLE; ++ iomap->addr = IOMAP_NULL_ADDR; ++ iomap->length = length; ++ } else { ++ iomap->type = IOMAP_MAPPED; ++ iomap->addr = (z->z_sector << SECTOR_SHIFT) + iomap->offset; ++ iomap->length = isize - iomap->offset; ++ } ++ mutex_unlock(&zi->i_truncate_mutex); ++ ++ trace_zonefs_iomap_begin(inode, iomap); ++ ++ return 0; ++} ++ ++static const struct iomap_ops zonefs_read_iomap_ops = { ++ .iomap_begin = zonefs_read_iomap_begin, ++}; ++ ++static int zonefs_write_iomap_begin(struct inode *inode, loff_t offset, ++ loff_t length, unsigned int flags, ++ struct iomap *iomap, struct iomap *srcmap) ++{ ++ struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ struct super_block *sb = inode->i_sb; ++ loff_t isize; ++ ++ /* All write I/Os should always be within the file maximum size */ ++ if (WARN_ON_ONCE(offset + length > z->z_capacity)) ++ return -EIO; ++ ++ /* ++ * Sequential zones can only accept direct writes. This is already ++ * checked when writes are issued, so warn if we see a page writeback ++ * operation. ++ */ ++ if (WARN_ON_ONCE(zonefs_zone_is_seq(z) && !(flags & IOMAP_DIRECT))) ++ return -EIO; ++ ++ /* ++ * For conventional zones, all blocks are always mapped. For sequential ++ * zones, all blocks after always mapped below the inode size (zone ++ * write pointer) and unwriten beyond. ++ */ ++ mutex_lock(&zi->i_truncate_mutex); ++ iomap->bdev = inode->i_sb->s_bdev; ++ iomap->offset = ALIGN_DOWN(offset, sb->s_blocksize); ++ iomap->addr = (z->z_sector << SECTOR_SHIFT) + iomap->offset; ++ isize = i_size_read(inode); ++ if (iomap->offset >= isize) { ++ iomap->type = IOMAP_UNWRITTEN; ++ iomap->length = z->z_capacity - iomap->offset; ++ } else { ++ iomap->type = IOMAP_MAPPED; ++ iomap->length = isize - iomap->offset; ++ } ++ mutex_unlock(&zi->i_truncate_mutex); ++ ++ trace_zonefs_iomap_begin(inode, iomap); ++ ++ return 0; ++} ++ ++static const struct iomap_ops zonefs_write_iomap_ops = { ++ .iomap_begin = zonefs_write_iomap_begin, ++}; ++ ++static int zonefs_read_folio(struct file *unused, struct folio *folio) ++{ ++ return iomap_read_folio(folio, &zonefs_read_iomap_ops); ++} ++ ++static void zonefs_readahead(struct readahead_control *rac) ++{ ++ iomap_readahead(rac, &zonefs_read_iomap_ops); ++} ++ ++/* ++ * Map blocks for page writeback. This is used only on conventional zone files, ++ * which implies that the page range can only be within the fixed inode size. ++ */ ++static int zonefs_write_map_blocks(struct iomap_writepage_ctx *wpc, ++ struct inode *inode, loff_t offset) ++{ ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ ++ if (WARN_ON_ONCE(zonefs_zone_is_seq(z))) ++ return -EIO; ++ if (WARN_ON_ONCE(offset >= i_size_read(inode))) ++ return -EIO; ++ ++ /* If the mapping is already OK, nothing needs to be done */ ++ if (offset >= wpc->iomap.offset && ++ offset < wpc->iomap.offset + wpc->iomap.length) ++ return 0; ++ ++ return zonefs_write_iomap_begin(inode, offset, ++ z->z_capacity - offset, ++ IOMAP_WRITE, &wpc->iomap, NULL); ++} ++ ++static const struct iomap_writeback_ops zonefs_writeback_ops = { ++ .map_blocks = zonefs_write_map_blocks, ++}; ++ ++static int zonefs_writepages(struct address_space *mapping, ++ struct writeback_control *wbc) ++{ ++ struct iomap_writepage_ctx wpc = { }; ++ ++ return iomap_writepages(mapping, wbc, &wpc, &zonefs_writeback_ops); ++} ++ ++static int zonefs_swap_activate(struct swap_info_struct *sis, ++ struct file *swap_file, sector_t *span) ++{ ++ struct inode *inode = file_inode(swap_file); ++ ++ if (zonefs_inode_is_seq(inode)) { ++ zonefs_err(inode->i_sb, ++ "swap file: not a conventional zone file\n"); ++ return -EINVAL; ++ } ++ ++ return iomap_swapfile_activate(sis, swap_file, span, ++ &zonefs_read_iomap_ops); ++} ++ ++const struct address_space_operations zonefs_file_aops = { ++ .read_folio = zonefs_read_folio, ++ .readahead = zonefs_readahead, ++ .writepages = zonefs_writepages, ++ .dirty_folio = filemap_dirty_folio, ++ .release_folio = iomap_release_folio, ++ .invalidate_folio = iomap_invalidate_folio, ++ .migrate_folio = filemap_migrate_folio, ++ .is_partially_uptodate = iomap_is_partially_uptodate, ++ .error_remove_page = generic_error_remove_page, ++ .direct_IO = noop_direct_IO, ++ .swap_activate = zonefs_swap_activate, ++}; ++ ++int zonefs_file_truncate(struct inode *inode, loff_t isize) ++{ ++ struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ loff_t old_isize; ++ enum req_op op; ++ int ret = 0; ++ ++ /* ++ * Only sequential zone files can be truncated and truncation is allowed ++ * only down to a 0 size, which is equivalent to a zone reset, and to ++ * the maximum file size, which is equivalent to a zone finish. ++ */ ++ if (!zonefs_zone_is_seq(z)) ++ return -EPERM; ++ ++ if (!isize) ++ op = REQ_OP_ZONE_RESET; ++ else if (isize == z->z_capacity) ++ op = REQ_OP_ZONE_FINISH; ++ else ++ return -EPERM; ++ ++ inode_dio_wait(inode); ++ ++ /* Serialize against page faults */ ++ filemap_invalidate_lock(inode->i_mapping); ++ ++ /* Serialize against zonefs_iomap_begin() */ ++ mutex_lock(&zi->i_truncate_mutex); ++ ++ old_isize = i_size_read(inode); ++ if (isize == old_isize) ++ goto unlock; ++ ++ ret = zonefs_inode_zone_mgmt(inode, op); ++ if (ret) ++ goto unlock; ++ ++ /* ++ * If the mount option ZONEFS_MNTOPT_EXPLICIT_OPEN is set, ++ * take care of open zones. ++ */ ++ if (z->z_flags & ZONEFS_ZONE_OPEN) { ++ /* ++ * Truncating a zone to EMPTY or FULL is the equivalent of ++ * closing the zone. For a truncation to 0, we need to ++ * re-open the zone to ensure new writes can be processed. ++ * For a truncation to the maximum file size, the zone is ++ * closed and writes cannot be accepted anymore, so clear ++ * the open flag. ++ */ ++ if (!isize) ++ ret = zonefs_inode_zone_mgmt(inode, REQ_OP_ZONE_OPEN); ++ else ++ z->z_flags &= ~ZONEFS_ZONE_OPEN; ++ } ++ ++ zonefs_update_stats(inode, isize); ++ truncate_setsize(inode, isize); ++ z->z_wpoffset = isize; ++ zonefs_inode_account_active(inode); ++ ++unlock: ++ mutex_unlock(&zi->i_truncate_mutex); ++ filemap_invalidate_unlock(inode->i_mapping); ++ ++ return ret; ++} ++ ++static int zonefs_file_fsync(struct file *file, loff_t start, loff_t end, ++ int datasync) ++{ ++ struct inode *inode = file_inode(file); ++ int ret = 0; ++ ++ if (unlikely(IS_IMMUTABLE(inode))) ++ return -EPERM; ++ ++ /* ++ * Since only direct writes are allowed in sequential files, page cache ++ * flush is needed only for conventional zone files. ++ */ ++ if (zonefs_inode_is_cnv(inode)) ++ ret = file_write_and_wait_range(file, start, end); ++ if (!ret) ++ ret = blkdev_issue_flush(inode->i_sb->s_bdev); ++ ++ if (ret) ++ zonefs_io_error(inode, true); ++ ++ return ret; ++} ++ ++static vm_fault_t zonefs_filemap_page_mkwrite(struct vm_fault *vmf) ++{ ++ struct inode *inode = file_inode(vmf->vma->vm_file); ++ vm_fault_t ret; ++ ++ if (unlikely(IS_IMMUTABLE(inode))) ++ return VM_FAULT_SIGBUS; ++ ++ /* ++ * Sanity check: only conventional zone files can have shared ++ * writeable mappings. ++ */ ++ if (zonefs_inode_is_seq(inode)) ++ return VM_FAULT_NOPAGE; ++ ++ sb_start_pagefault(inode->i_sb); ++ file_update_time(vmf->vma->vm_file); ++ ++ /* Serialize against truncates */ ++ filemap_invalidate_lock_shared(inode->i_mapping); ++ ret = iomap_page_mkwrite(vmf, &zonefs_write_iomap_ops); ++ filemap_invalidate_unlock_shared(inode->i_mapping); ++ ++ sb_end_pagefault(inode->i_sb); ++ return ret; ++} ++ ++static const struct vm_operations_struct zonefs_file_vm_ops = { ++ .fault = filemap_fault, ++ .map_pages = filemap_map_pages, ++ .page_mkwrite = zonefs_filemap_page_mkwrite, ++}; ++ ++static int zonefs_file_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ /* ++ * Conventional zones accept random writes, so their files can support ++ * shared writable mappings. For sequential zone files, only read ++ * mappings are possible since there are no guarantees for write ++ * ordering between msync() and page cache writeback. ++ */ ++ if (zonefs_inode_is_seq(file_inode(file)) && ++ (vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) ++ return -EINVAL; ++ ++ file_accessed(file); ++ vma->vm_ops = &zonefs_file_vm_ops; ++ ++ return 0; ++} ++ ++static loff_t zonefs_file_llseek(struct file *file, loff_t offset, int whence) ++{ ++ loff_t isize = i_size_read(file_inode(file)); ++ ++ /* ++ * Seeks are limited to below the zone size for conventional zones ++ * and below the zone write pointer for sequential zones. In both ++ * cases, this limit is the inode size. ++ */ ++ return generic_file_llseek_size(file, offset, whence, isize, isize); ++} ++ ++static int zonefs_file_write_dio_end_io(struct kiocb *iocb, ssize_t size, ++ int error, unsigned int flags) ++{ ++ struct inode *inode = file_inode(iocb->ki_filp); ++ struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ ++ if (error) { ++ zonefs_io_error(inode, true); ++ return error; ++ } ++ ++ if (size && zonefs_inode_is_seq(inode)) { ++ /* ++ * Note that we may be seeing completions out of order, ++ * but that is not a problem since a write completed ++ * successfully necessarily means that all preceding writes ++ * were also successful. So we can safely increase the inode ++ * size to the write end location. ++ */ ++ mutex_lock(&zi->i_truncate_mutex); ++ if (i_size_read(inode) < iocb->ki_pos + size) { ++ zonefs_update_stats(inode, iocb->ki_pos + size); ++ zonefs_i_size_write(inode, iocb->ki_pos + size); ++ } ++ mutex_unlock(&zi->i_truncate_mutex); ++ } ++ ++ return 0; ++} ++ ++static const struct iomap_dio_ops zonefs_write_dio_ops = { ++ .end_io = zonefs_file_write_dio_end_io, ++}; ++ ++static ssize_t zonefs_file_dio_append(struct kiocb *iocb, struct iov_iter *from) ++{ ++ struct inode *inode = file_inode(iocb->ki_filp); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ struct block_device *bdev = inode->i_sb->s_bdev; ++ unsigned int max = bdev_max_zone_append_sectors(bdev); ++ pgoff_t start, end; ++ struct bio *bio; ++ ssize_t size; ++ int nr_pages; ++ ssize_t ret; ++ ++ max = ALIGN_DOWN(max << SECTOR_SHIFT, inode->i_sb->s_blocksize); ++ iov_iter_truncate(from, max); ++ ++ /* ++ * If the inode block size (zone write granularity) is smaller than the ++ * page size, we may be appending data belonging to the last page of the ++ * inode straddling inode->i_size, with that page already cached due to ++ * a buffered read or readahead. So make sure to invalidate that page. ++ * This will always be a no-op for the case where the block size is ++ * equal to the page size. ++ */ ++ start = iocb->ki_pos >> PAGE_SHIFT; ++ end = (iocb->ki_pos + iov_iter_count(from) - 1) >> PAGE_SHIFT; ++ if (invalidate_inode_pages2_range(inode->i_mapping, start, end)) ++ return -EBUSY; ++ ++ nr_pages = iov_iter_npages(from, BIO_MAX_VECS); ++ if (!nr_pages) ++ return 0; ++ ++ bio = bio_alloc(bdev, nr_pages, ++ REQ_OP_ZONE_APPEND | REQ_SYNC | REQ_IDLE, GFP_NOFS); ++ bio->bi_iter.bi_sector = z->z_sector; ++ bio->bi_ioprio = iocb->ki_ioprio; ++ if (iocb_is_dsync(iocb)) ++ bio->bi_opf |= REQ_FUA; ++ ++ ret = bio_iov_iter_get_pages(bio, from); ++ if (unlikely(ret)) ++ goto out_release; ++ ++ size = bio->bi_iter.bi_size; ++ task_io_account_write(size); ++ ++ if (iocb->ki_flags & IOCB_HIPRI) ++ bio_set_polled(bio, iocb); ++ ++ ret = submit_bio_wait(bio); ++ ++ /* ++ * If the file zone was written underneath the file system, the zone ++ * write pointer may not be where we expect it to be, but the zone ++ * append write can still succeed. So check manually that we wrote where ++ * we intended to, that is, at zi->i_wpoffset. ++ */ ++ if (!ret) { ++ sector_t wpsector = ++ z->z_sector + (z->z_wpoffset >> SECTOR_SHIFT); ++ ++ if (bio->bi_iter.bi_sector != wpsector) { ++ zonefs_warn(inode->i_sb, ++ "Corrupted write pointer %llu for zone at %llu\n", ++ bio->bi_iter.bi_sector, z->z_sector); ++ ret = -EIO; ++ } ++ } ++ ++ zonefs_file_write_dio_end_io(iocb, size, ret, 0); ++ trace_zonefs_file_dio_append(inode, size, ret); ++ ++out_release: ++ bio_release_pages(bio, false); ++ bio_put(bio); ++ ++ if (ret >= 0) { ++ iocb->ki_pos += size; ++ return size; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Do not exceed the LFS limits nor the file zone size. If pos is under the ++ * limit it becomes a short access. If it exceeds the limit, return -EFBIG. ++ */ ++static loff_t zonefs_write_check_limits(struct file *file, loff_t pos, ++ loff_t count) ++{ ++ struct inode *inode = file_inode(file); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ loff_t limit = rlimit(RLIMIT_FSIZE); ++ loff_t max_size = z->z_capacity; ++ ++ if (limit != RLIM_INFINITY) { ++ if (pos >= limit) { ++ send_sig(SIGXFSZ, current, 0); ++ return -EFBIG; ++ } ++ count = min(count, limit - pos); ++ } ++ ++ if (!(file->f_flags & O_LARGEFILE)) ++ max_size = min_t(loff_t, MAX_NON_LFS, max_size); ++ ++ if (unlikely(pos >= max_size)) ++ return -EFBIG; ++ ++ return min(count, max_size - pos); ++} ++ ++static ssize_t zonefs_write_checks(struct kiocb *iocb, struct iov_iter *from) ++{ ++ struct file *file = iocb->ki_filp; ++ struct inode *inode = file_inode(file); ++ struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ loff_t count; ++ ++ if (IS_SWAPFILE(inode)) ++ return -ETXTBSY; ++ ++ if (!iov_iter_count(from)) ++ return 0; ++ ++ if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT)) ++ return -EINVAL; ++ ++ if (iocb->ki_flags & IOCB_APPEND) { ++ if (zonefs_zone_is_cnv(z)) ++ return -EINVAL; ++ mutex_lock(&zi->i_truncate_mutex); ++ iocb->ki_pos = z->z_wpoffset; ++ mutex_unlock(&zi->i_truncate_mutex); ++ } ++ ++ count = zonefs_write_check_limits(file, iocb->ki_pos, ++ iov_iter_count(from)); ++ if (count < 0) ++ return count; ++ ++ iov_iter_truncate(from, count); ++ return iov_iter_count(from); ++} ++ ++/* ++ * Handle direct writes. For sequential zone files, this is the only possible ++ * write path. For these files, check that the user is issuing writes ++ * sequentially from the end of the file. This code assumes that the block layer ++ * delivers write requests to the device in sequential order. This is always the ++ * case if a block IO scheduler implementing the ELEVATOR_F_ZBD_SEQ_WRITE ++ * elevator feature is being used (e.g. mq-deadline). The block layer always ++ * automatically select such an elevator for zoned block devices during the ++ * device initialization. ++ */ ++static ssize_t zonefs_file_dio_write(struct kiocb *iocb, struct iov_iter *from) ++{ ++ struct inode *inode = file_inode(iocb->ki_filp); ++ struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ struct super_block *sb = inode->i_sb; ++ bool sync = is_sync_kiocb(iocb); ++ bool append = false; ++ ssize_t ret, count; ++ ++ /* ++ * For async direct IOs to sequential zone files, refuse IOCB_NOWAIT ++ * as this can cause write reordering (e.g. the first aio gets EAGAIN ++ * on the inode lock but the second goes through but is now unaligned). ++ */ ++ if (zonefs_zone_is_seq(z) && !sync && (iocb->ki_flags & IOCB_NOWAIT)) ++ return -EOPNOTSUPP; ++ ++ if (iocb->ki_flags & IOCB_NOWAIT) { ++ if (!inode_trylock(inode)) ++ return -EAGAIN; ++ } else { ++ inode_lock(inode); ++ } ++ ++ count = zonefs_write_checks(iocb, from); ++ if (count <= 0) { ++ ret = count; ++ goto inode_unlock; ++ } ++ ++ if ((iocb->ki_pos | count) & (sb->s_blocksize - 1)) { ++ ret = -EINVAL; ++ goto inode_unlock; ++ } ++ ++ /* Enforce sequential writes (append only) in sequential zones */ ++ if (zonefs_zone_is_seq(z)) { ++ mutex_lock(&zi->i_truncate_mutex); ++ if (iocb->ki_pos != z->z_wpoffset) { ++ mutex_unlock(&zi->i_truncate_mutex); ++ ret = -EINVAL; ++ goto inode_unlock; ++ } ++ mutex_unlock(&zi->i_truncate_mutex); ++ append = sync; ++ } ++ ++ if (append) { ++ ret = zonefs_file_dio_append(iocb, from); ++ } else { ++ /* ++ * iomap_dio_rw() may return ENOTBLK if there was an issue with ++ * page invalidation. Overwrite that error code with EBUSY to ++ * be consistent with zonefs_file_dio_append() return value for ++ * similar issues. ++ */ ++ ret = iomap_dio_rw(iocb, from, &zonefs_write_iomap_ops, ++ &zonefs_write_dio_ops, 0, NULL, 0); ++ if (ret == -ENOTBLK) ++ ret = -EBUSY; ++ } ++ ++ if (zonefs_zone_is_seq(z) && ++ (ret > 0 || ret == -EIOCBQUEUED)) { ++ if (ret > 0) ++ count = ret; ++ ++ /* ++ * Update the zone write pointer offset assuming the write ++ * operation succeeded. If it did not, the error recovery path ++ * will correct it. Also do active seq file accounting. ++ */ ++ mutex_lock(&zi->i_truncate_mutex); ++ z->z_wpoffset += count; ++ zonefs_inode_account_active(inode); ++ mutex_unlock(&zi->i_truncate_mutex); ++ } ++ ++inode_unlock: ++ inode_unlock(inode); ++ ++ return ret; ++} ++ ++static ssize_t zonefs_file_buffered_write(struct kiocb *iocb, ++ struct iov_iter *from) ++{ ++ struct inode *inode = file_inode(iocb->ki_filp); ++ ssize_t ret; ++ ++ /* ++ * Direct IO writes are mandatory for sequential zone files so that the ++ * write IO issuing order is preserved. ++ */ ++ if (zonefs_inode_is_seq(inode)) ++ return -EIO; ++ ++ if (iocb->ki_flags & IOCB_NOWAIT) { ++ if (!inode_trylock(inode)) ++ return -EAGAIN; ++ } else { ++ inode_lock(inode); ++ } ++ ++ ret = zonefs_write_checks(iocb, from); ++ if (ret <= 0) ++ goto inode_unlock; ++ ++ ret = iomap_file_buffered_write(iocb, from, &zonefs_write_iomap_ops); ++ if (ret > 0) ++ iocb->ki_pos += ret; ++ else if (ret == -EIO) ++ zonefs_io_error(inode, true); ++ ++inode_unlock: ++ inode_unlock(inode); ++ if (ret > 0) ++ ret = generic_write_sync(iocb, ret); ++ ++ return ret; ++} ++ ++static ssize_t zonefs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ++{ ++ struct inode *inode = file_inode(iocb->ki_filp); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ ++ if (unlikely(IS_IMMUTABLE(inode))) ++ return -EPERM; ++ ++ if (sb_rdonly(inode->i_sb)) ++ return -EROFS; ++ ++ /* Write operations beyond the zone capacity are not allowed */ ++ if (iocb->ki_pos >= z->z_capacity) ++ return -EFBIG; ++ ++ if (iocb->ki_flags & IOCB_DIRECT) { ++ ssize_t ret = zonefs_file_dio_write(iocb, from); ++ ++ if (ret != -ENOTBLK) ++ return ret; ++ } ++ ++ return zonefs_file_buffered_write(iocb, from); ++} ++ ++static int zonefs_file_read_dio_end_io(struct kiocb *iocb, ssize_t size, ++ int error, unsigned int flags) ++{ ++ if (error) { ++ zonefs_io_error(file_inode(iocb->ki_filp), false); ++ return error; ++ } ++ ++ return 0; ++} ++ ++static const struct iomap_dio_ops zonefs_read_dio_ops = { ++ .end_io = zonefs_file_read_dio_end_io, ++}; ++ ++static ssize_t zonefs_file_read_iter(struct kiocb *iocb, struct iov_iter *to) ++{ ++ struct inode *inode = file_inode(iocb->ki_filp); ++ struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ struct super_block *sb = inode->i_sb; ++ loff_t isize; ++ ssize_t ret; ++ ++ /* Offline zones cannot be read */ ++ if (unlikely(IS_IMMUTABLE(inode) && !(inode->i_mode & 0777))) ++ return -EPERM; ++ ++ if (iocb->ki_pos >= z->z_capacity) ++ return 0; ++ ++ if (iocb->ki_flags & IOCB_NOWAIT) { ++ if (!inode_trylock_shared(inode)) ++ return -EAGAIN; ++ } else { ++ inode_lock_shared(inode); ++ } ++ ++ /* Limit read operations to written data */ ++ mutex_lock(&zi->i_truncate_mutex); ++ isize = i_size_read(inode); ++ if (iocb->ki_pos >= isize) { ++ mutex_unlock(&zi->i_truncate_mutex); ++ ret = 0; ++ goto inode_unlock; ++ } ++ iov_iter_truncate(to, isize - iocb->ki_pos); ++ mutex_unlock(&zi->i_truncate_mutex); ++ ++ if (iocb->ki_flags & IOCB_DIRECT) { ++ size_t count = iov_iter_count(to); ++ ++ if ((iocb->ki_pos | count) & (sb->s_blocksize - 1)) { ++ ret = -EINVAL; ++ goto inode_unlock; ++ } ++ file_accessed(iocb->ki_filp); ++ ret = iomap_dio_rw(iocb, to, &zonefs_read_iomap_ops, ++ &zonefs_read_dio_ops, 0, NULL, 0); ++ } else { ++ ret = generic_file_read_iter(iocb, to); ++ if (ret == -EIO) ++ zonefs_io_error(inode, false); ++ } ++ ++inode_unlock: ++ inode_unlock_shared(inode); ++ ++ return ret; ++} ++ ++/* ++ * Write open accounting is done only for sequential files. ++ */ ++static inline bool zonefs_seq_file_need_wro(struct inode *inode, ++ struct file *file) ++{ ++ if (zonefs_inode_is_cnv(inode)) ++ return false; ++ ++ if (!(file->f_mode & FMODE_WRITE)) ++ return false; ++ ++ return true; ++} ++ ++static int zonefs_seq_file_write_open(struct inode *inode) ++{ ++ struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ int ret = 0; ++ ++ mutex_lock(&zi->i_truncate_mutex); ++ ++ if (!zi->i_wr_refcnt) { ++ struct zonefs_sb_info *sbi = ZONEFS_SB(inode->i_sb); ++ unsigned int wro = atomic_inc_return(&sbi->s_wro_seq_files); ++ ++ if (sbi->s_mount_opts & ZONEFS_MNTOPT_EXPLICIT_OPEN) { ++ ++ if (sbi->s_max_wro_seq_files ++ && wro > sbi->s_max_wro_seq_files) { ++ atomic_dec(&sbi->s_wro_seq_files); ++ ret = -EBUSY; ++ goto unlock; ++ } ++ ++ if (i_size_read(inode) < z->z_capacity) { ++ ret = zonefs_inode_zone_mgmt(inode, ++ REQ_OP_ZONE_OPEN); ++ if (ret) { ++ atomic_dec(&sbi->s_wro_seq_files); ++ goto unlock; ++ } ++ z->z_flags |= ZONEFS_ZONE_OPEN; ++ zonefs_inode_account_active(inode); ++ } ++ } ++ } ++ ++ zi->i_wr_refcnt++; ++ ++unlock: ++ mutex_unlock(&zi->i_truncate_mutex); ++ ++ return ret; ++} ++ ++static int zonefs_file_open(struct inode *inode, struct file *file) ++{ ++ int ret; ++ ++ ret = generic_file_open(inode, file); ++ if (ret) ++ return ret; ++ ++ if (zonefs_seq_file_need_wro(inode, file)) ++ return zonefs_seq_file_write_open(inode); ++ ++ return 0; ++} ++ ++static void zonefs_seq_file_write_close(struct inode *inode) ++{ ++ struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ struct super_block *sb = inode->i_sb; ++ struct zonefs_sb_info *sbi = ZONEFS_SB(sb); ++ int ret = 0; ++ ++ mutex_lock(&zi->i_truncate_mutex); ++ ++ zi->i_wr_refcnt--; ++ if (zi->i_wr_refcnt) ++ goto unlock; ++ ++ /* ++ * The file zone may not be open anymore (e.g. the file was truncated to ++ * its maximum size or it was fully written). For this case, we only ++ * need to decrement the write open count. ++ */ ++ if (z->z_flags & ZONEFS_ZONE_OPEN) { ++ ret = zonefs_inode_zone_mgmt(inode, REQ_OP_ZONE_CLOSE); ++ if (ret) { ++ __zonefs_io_error(inode, false); ++ /* ++ * Leaving zones explicitly open may lead to a state ++ * where most zones cannot be written (zone resources ++ * exhausted). So take preventive action by remounting ++ * read-only. ++ */ ++ if (z->z_flags & ZONEFS_ZONE_OPEN && ++ !(sb->s_flags & SB_RDONLY)) { ++ zonefs_warn(sb, ++ "closing zone at %llu failed %d\n", ++ z->z_sector, ret); ++ zonefs_warn(sb, ++ "remounting filesystem read-only\n"); ++ sb->s_flags |= SB_RDONLY; ++ } ++ goto unlock; ++ } ++ ++ z->z_flags &= ~ZONEFS_ZONE_OPEN; ++ zonefs_inode_account_active(inode); ++ } ++ ++ atomic_dec(&sbi->s_wro_seq_files); ++ ++unlock: ++ mutex_unlock(&zi->i_truncate_mutex); ++} ++ ++static int zonefs_file_release(struct inode *inode, struct file *file) ++{ ++ /* ++ * If we explicitly open a zone we must close it again as well, but the ++ * zone management operation can fail (either due to an IO error or as ++ * the zone has gone offline or read-only). Make sure we don't fail the ++ * close(2) for user-space. ++ */ ++ if (zonefs_seq_file_need_wro(inode, file)) ++ zonefs_seq_file_write_close(inode); ++ ++ return 0; ++} ++ ++const struct file_operations zonefs_file_operations = { ++ .open = zonefs_file_open, ++ .release = zonefs_file_release, ++ .fsync = zonefs_file_fsync, ++ .mmap = zonefs_file_mmap, ++ .llseek = zonefs_file_llseek, ++ .read_iter = zonefs_file_read_iter, ++ .write_iter = zonefs_file_write_iter, ++ .splice_read = generic_file_splice_read, ++ .splice_write = iter_file_splice_write, ++ .iopoll = iocb_bio_iopoll, ++}; +diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c +index a9c5c3f720adf..270ded209dde5 100644 +--- a/fs/zonefs/super.c ++++ b/fs/zonefs/super.c +@@ -28,33 +28,47 @@ + #include "trace.h" + + /* +- * Manage the active zone count. Called with zi->i_truncate_mutex held. ++ * Get the name of a zone group directory. + */ +-static void zonefs_account_active(struct inode *inode) ++static const char *zonefs_zgroup_name(enum zonefs_ztype ztype) + { +- struct zonefs_sb_info *sbi = ZONEFS_SB(inode->i_sb); +- struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ switch (ztype) { ++ case ZONEFS_ZTYPE_CNV: ++ return "cnv"; ++ case ZONEFS_ZTYPE_SEQ: ++ return "seq"; ++ default: ++ WARN_ON_ONCE(1); ++ return "???"; ++ } ++} + +- lockdep_assert_held(&zi->i_truncate_mutex); ++/* ++ * Manage the active zone count. ++ */ ++static void zonefs_account_active(struct super_block *sb, ++ struct zonefs_zone *z) ++{ ++ struct zonefs_sb_info *sbi = ZONEFS_SB(sb); + +- if (zi->i_ztype != ZONEFS_ZTYPE_SEQ) ++ if (zonefs_zone_is_cnv(z)) + return; + + /* + * For zones that transitioned to the offline or readonly condition, + * we only need to clear the active state. + */ +- if (zi->i_flags & (ZONEFS_ZONE_OFFLINE | ZONEFS_ZONE_READONLY)) ++ if (z->z_flags & (ZONEFS_ZONE_OFFLINE | ZONEFS_ZONE_READONLY)) + goto out; + + /* + * If the zone is active, that is, if it is explicitly open or + * partially written, check if it was already accounted as active. + */ +- if ((zi->i_flags & ZONEFS_ZONE_OPEN) || +- (zi->i_wpoffset > 0 && zi->i_wpoffset < zi->i_max_size)) { +- if (!(zi->i_flags & ZONEFS_ZONE_ACTIVE)) { +- zi->i_flags |= ZONEFS_ZONE_ACTIVE; ++ if ((z->z_flags & ZONEFS_ZONE_OPEN) || ++ (z->z_wpoffset > 0 && z->z_wpoffset < z->z_capacity)) { ++ if (!(z->z_flags & ZONEFS_ZONE_ACTIVE)) { ++ z->z_flags |= ZONEFS_ZONE_ACTIVE; + atomic_inc(&sbi->s_active_seq_files); + } + return; +@@ -62,18 +76,29 @@ static void zonefs_account_active(struct inode *inode) + + out: + /* The zone is not active. If it was, update the active count */ +- if (zi->i_flags & ZONEFS_ZONE_ACTIVE) { +- zi->i_flags &= ~ZONEFS_ZONE_ACTIVE; ++ if (z->z_flags & ZONEFS_ZONE_ACTIVE) { ++ z->z_flags &= ~ZONEFS_ZONE_ACTIVE; + atomic_dec(&sbi->s_active_seq_files); + } + } + +-static inline int zonefs_zone_mgmt(struct inode *inode, enum req_op op) ++/* ++ * Manage the active zone count. Called with zi->i_truncate_mutex held. ++ */ ++void zonefs_inode_account_active(struct inode *inode) + { +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- int ret; ++ lockdep_assert_held(&ZONEFS_I(inode)->i_truncate_mutex); + +- lockdep_assert_held(&zi->i_truncate_mutex); ++ return zonefs_account_active(inode->i_sb, zonefs_inode_zone(inode)); ++} ++ ++/* ++ * Execute a zone management operation. ++ */ ++static int zonefs_zone_mgmt(struct super_block *sb, ++ struct zonefs_zone *z, enum req_op op) ++{ ++ int ret; + + /* + * With ZNS drives, closing an explicitly open zone that has not been +@@ -83,201 +108,49 @@ static inline int zonefs_zone_mgmt(struct inode *inode, enum req_op op) + * are exceeded, make sure that the zone does not remain active by + * resetting it. + */ +- if (op == REQ_OP_ZONE_CLOSE && !zi->i_wpoffset) ++ if (op == REQ_OP_ZONE_CLOSE && !z->z_wpoffset) + op = REQ_OP_ZONE_RESET; + +- trace_zonefs_zone_mgmt(inode, op); +- ret = blkdev_zone_mgmt(inode->i_sb->s_bdev, op, zi->i_zsector, +- zi->i_zone_size >> SECTOR_SHIFT, GFP_NOFS); ++ trace_zonefs_zone_mgmt(sb, z, op); ++ ret = blkdev_zone_mgmt(sb->s_bdev, op, z->z_sector, ++ z->z_size >> SECTOR_SHIFT, GFP_NOFS); + if (ret) { +- zonefs_err(inode->i_sb, ++ zonefs_err(sb, + "Zone management operation %s at %llu failed %d\n", +- blk_op_str(op), zi->i_zsector, ret); ++ blk_op_str(op), z->z_sector, ret); + return ret; + } + + return 0; + } + +-static inline void zonefs_i_size_write(struct inode *inode, loff_t isize) ++int zonefs_inode_zone_mgmt(struct inode *inode, enum req_op op) + { +- struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ lockdep_assert_held(&ZONEFS_I(inode)->i_truncate_mutex); + +- i_size_write(inode, isize); +- /* +- * A full zone is no longer open/active and does not need +- * explicit closing. +- */ +- if (isize >= zi->i_max_size) { +- struct zonefs_sb_info *sbi = ZONEFS_SB(inode->i_sb); +- +- if (zi->i_flags & ZONEFS_ZONE_ACTIVE) +- atomic_dec(&sbi->s_active_seq_files); +- zi->i_flags &= ~(ZONEFS_ZONE_OPEN | ZONEFS_ZONE_ACTIVE); +- } ++ return zonefs_zone_mgmt(inode->i_sb, zonefs_inode_zone(inode), op); + } + +-static int zonefs_read_iomap_begin(struct inode *inode, loff_t offset, +- loff_t length, unsigned int flags, +- struct iomap *iomap, struct iomap *srcmap) ++void zonefs_i_size_write(struct inode *inode, loff_t isize) + { +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- struct super_block *sb = inode->i_sb; +- loff_t isize; ++ struct zonefs_zone *z = zonefs_inode_zone(inode); + +- /* +- * All blocks are always mapped below EOF. If reading past EOF, +- * act as if there is a hole up to the file maximum size. +- */ +- mutex_lock(&zi->i_truncate_mutex); +- iomap->bdev = inode->i_sb->s_bdev; +- iomap->offset = ALIGN_DOWN(offset, sb->s_blocksize); +- isize = i_size_read(inode); +- if (iomap->offset >= isize) { +- iomap->type = IOMAP_HOLE; +- iomap->addr = IOMAP_NULL_ADDR; +- iomap->length = length; +- } else { +- iomap->type = IOMAP_MAPPED; +- iomap->addr = (zi->i_zsector << SECTOR_SHIFT) + iomap->offset; +- iomap->length = isize - iomap->offset; +- } +- mutex_unlock(&zi->i_truncate_mutex); +- +- trace_zonefs_iomap_begin(inode, iomap); +- +- return 0; +-} +- +-static const struct iomap_ops zonefs_read_iomap_ops = { +- .iomap_begin = zonefs_read_iomap_begin, +-}; +- +-static int zonefs_write_iomap_begin(struct inode *inode, loff_t offset, +- loff_t length, unsigned int flags, +- struct iomap *iomap, struct iomap *srcmap) +-{ +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- struct super_block *sb = inode->i_sb; +- loff_t isize; +- +- /* All write I/Os should always be within the file maximum size */ +- if (WARN_ON_ONCE(offset + length > zi->i_max_size)) +- return -EIO; +- +- /* +- * Sequential zones can only accept direct writes. This is already +- * checked when writes are issued, so warn if we see a page writeback +- * operation. +- */ +- if (WARN_ON_ONCE(zi->i_ztype == ZONEFS_ZTYPE_SEQ && +- !(flags & IOMAP_DIRECT))) +- return -EIO; ++ i_size_write(inode, isize); + + /* +- * For conventional zones, all blocks are always mapped. For sequential +- * zones, all blocks after always mapped below the inode size (zone +- * write pointer) and unwriten beyond. ++ * A full zone is no longer open/active and does not need ++ * explicit closing. + */ +- mutex_lock(&zi->i_truncate_mutex); +- iomap->bdev = inode->i_sb->s_bdev; +- iomap->offset = ALIGN_DOWN(offset, sb->s_blocksize); +- iomap->addr = (zi->i_zsector << SECTOR_SHIFT) + iomap->offset; +- isize = i_size_read(inode); +- if (iomap->offset >= isize) { +- iomap->type = IOMAP_UNWRITTEN; +- iomap->length = zi->i_max_size - iomap->offset; +- } else { +- iomap->type = IOMAP_MAPPED; +- iomap->length = isize - iomap->offset; +- } +- mutex_unlock(&zi->i_truncate_mutex); +- +- trace_zonefs_iomap_begin(inode, iomap); +- +- return 0; +-} +- +-static const struct iomap_ops zonefs_write_iomap_ops = { +- .iomap_begin = zonefs_write_iomap_begin, +-}; +- +-static int zonefs_read_folio(struct file *unused, struct folio *folio) +-{ +- return iomap_read_folio(folio, &zonefs_read_iomap_ops); +-} +- +-static void zonefs_readahead(struct readahead_control *rac) +-{ +- iomap_readahead(rac, &zonefs_read_iomap_ops); +-} +- +-/* +- * Map blocks for page writeback. This is used only on conventional zone files, +- * which implies that the page range can only be within the fixed inode size. +- */ +-static int zonefs_write_map_blocks(struct iomap_writepage_ctx *wpc, +- struct inode *inode, loff_t offset) +-{ +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- +- if (WARN_ON_ONCE(zi->i_ztype != ZONEFS_ZTYPE_CNV)) +- return -EIO; +- if (WARN_ON_ONCE(offset >= i_size_read(inode))) +- return -EIO; +- +- /* If the mapping is already OK, nothing needs to be done */ +- if (offset >= wpc->iomap.offset && +- offset < wpc->iomap.offset + wpc->iomap.length) +- return 0; +- +- return zonefs_write_iomap_begin(inode, offset, zi->i_max_size - offset, +- IOMAP_WRITE, &wpc->iomap, NULL); +-} +- +-static const struct iomap_writeback_ops zonefs_writeback_ops = { +- .map_blocks = zonefs_write_map_blocks, +-}; +- +-static int zonefs_writepages(struct address_space *mapping, +- struct writeback_control *wbc) +-{ +- struct iomap_writepage_ctx wpc = { }; +- +- return iomap_writepages(mapping, wbc, &wpc, &zonefs_writeback_ops); +-} +- +-static int zonefs_swap_activate(struct swap_info_struct *sis, +- struct file *swap_file, sector_t *span) +-{ +- struct inode *inode = file_inode(swap_file); +- struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ if (isize >= z->z_capacity) { ++ struct zonefs_sb_info *sbi = ZONEFS_SB(inode->i_sb); + +- if (zi->i_ztype != ZONEFS_ZTYPE_CNV) { +- zonefs_err(inode->i_sb, +- "swap file: not a conventional zone file\n"); +- return -EINVAL; ++ if (z->z_flags & ZONEFS_ZONE_ACTIVE) ++ atomic_dec(&sbi->s_active_seq_files); ++ z->z_flags &= ~(ZONEFS_ZONE_OPEN | ZONEFS_ZONE_ACTIVE); + } +- +- return iomap_swapfile_activate(sis, swap_file, span, +- &zonefs_read_iomap_ops); + } + +-static const struct address_space_operations zonefs_file_aops = { +- .read_folio = zonefs_read_folio, +- .readahead = zonefs_readahead, +- .writepages = zonefs_writepages, +- .dirty_folio = filemap_dirty_folio, +- .release_folio = iomap_release_folio, +- .invalidate_folio = iomap_invalidate_folio, +- .migrate_folio = filemap_migrate_folio, +- .is_partially_uptodate = iomap_is_partially_uptodate, +- .error_remove_page = generic_error_remove_page, +- .direct_IO = noop_direct_IO, +- .swap_activate = zonefs_swap_activate, +-}; +- +-static void zonefs_update_stats(struct inode *inode, loff_t new_isize) ++void zonefs_update_stats(struct inode *inode, loff_t new_isize) + { + struct super_block *sb = inode->i_sb; + struct zonefs_sb_info *sbi = ZONEFS_SB(sb); +@@ -310,63 +183,68 @@ static void zonefs_update_stats(struct inode *inode, loff_t new_isize) + } + + /* +- * Check a zone condition and adjust its file inode access permissions for +- * offline and readonly zones. Return the inode size corresponding to the +- * amount of readable data in the zone. ++ * Check a zone condition. Return the amount of written (and still readable) ++ * data in the zone. + */ +-static loff_t zonefs_check_zone_condition(struct inode *inode, +- struct blk_zone *zone, bool warn, +- bool mount) ++static loff_t zonefs_check_zone_condition(struct super_block *sb, ++ struct zonefs_zone *z, ++ struct blk_zone *zone) + { +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- + switch (zone->cond) { + case BLK_ZONE_COND_OFFLINE: +- /* +- * Dead zone: make the inode immutable, disable all accesses +- * and set the file size to 0 (zone wp set to zone start). +- */ +- if (warn) +- zonefs_warn(inode->i_sb, "inode %lu: offline zone\n", +- inode->i_ino); +- inode->i_flags |= S_IMMUTABLE; +- inode->i_mode &= ~0777; +- zone->wp = zone->start; +- zi->i_flags |= ZONEFS_ZONE_OFFLINE; ++ zonefs_warn(sb, "Zone %llu: offline zone\n", ++ z->z_sector); ++ z->z_flags |= ZONEFS_ZONE_OFFLINE; + return 0; + case BLK_ZONE_COND_READONLY: + /* +- * The write pointer of read-only zones is invalid. If such a +- * zone is found during mount, the file size cannot be retrieved +- * so we treat the zone as offline (mount == true case). +- * Otherwise, keep the file size as it was when last updated +- * so that the user can recover data. In both cases, writes are +- * always disabled for the zone. ++ * The write pointer of read-only zones is invalid, so we cannot ++ * determine the zone wpoffset (inode size). We thus keep the ++ * zone wpoffset as is, which leads to an empty file ++ * (wpoffset == 0) on mount. For a runtime error, this keeps ++ * the inode size as it was when last updated so that the user ++ * can recover data. + */ +- if (warn) +- zonefs_warn(inode->i_sb, "inode %lu: read-only zone\n", +- inode->i_ino); +- inode->i_flags |= S_IMMUTABLE; +- if (mount) { +- zone->cond = BLK_ZONE_COND_OFFLINE; +- inode->i_mode &= ~0777; +- zone->wp = zone->start; +- zi->i_flags |= ZONEFS_ZONE_OFFLINE; +- return 0; +- } +- zi->i_flags |= ZONEFS_ZONE_READONLY; +- inode->i_mode &= ~0222; +- return i_size_read(inode); ++ zonefs_warn(sb, "Zone %llu: read-only zone\n", ++ z->z_sector); ++ z->z_flags |= ZONEFS_ZONE_READONLY; ++ if (zonefs_zone_is_cnv(z)) ++ return z->z_capacity; ++ return z->z_wpoffset; + case BLK_ZONE_COND_FULL: + /* The write pointer of full zones is invalid. */ +- return zi->i_max_size; ++ return z->z_capacity; + default: +- if (zi->i_ztype == ZONEFS_ZTYPE_CNV) +- return zi->i_max_size; ++ if (zonefs_zone_is_cnv(z)) ++ return z->z_capacity; + return (zone->wp - zone->start) << SECTOR_SHIFT; + } + } + ++/* ++ * Check a zone condition and adjust its inode access permissions for ++ * offline and readonly zones. ++ */ ++static void zonefs_inode_update_mode(struct inode *inode) ++{ ++ struct zonefs_zone *z = zonefs_inode_zone(inode); ++ ++ if (z->z_flags & ZONEFS_ZONE_OFFLINE) { ++ /* Offline zones cannot be read nor written */ ++ inode->i_flags |= S_IMMUTABLE; ++ inode->i_mode &= ~0777; ++ } else if (z->z_flags & ZONEFS_ZONE_READONLY) { ++ /* Readonly zones cannot be written */ ++ inode->i_flags |= S_IMMUTABLE; ++ if (z->z_flags & ZONEFS_ZONE_INIT_MODE) ++ inode->i_mode &= ~0777; ++ else ++ inode->i_mode &= ~0222; ++ } ++ ++ z->z_flags &= ~ZONEFS_ZONE_INIT_MODE; ++} ++ + struct zonefs_ioerr_data { + struct inode *inode; + bool write; +@@ -377,7 +255,7 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx, + { + struct zonefs_ioerr_data *err = data; + struct inode *inode = err->inode; +- struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); + struct super_block *sb = inode->i_sb; + struct zonefs_sb_info *sbi = ZONEFS_SB(sb); + loff_t isize, data_size; +@@ -388,10 +266,9 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx, + * as there is no inconsistency between the inode size and the amount of + * data writen in the zone (data_size). + */ +- data_size = zonefs_check_zone_condition(inode, zone, true, false); ++ data_size = zonefs_check_zone_condition(sb, z, zone); + isize = i_size_read(inode); +- if (zone->cond != BLK_ZONE_COND_OFFLINE && +- zone->cond != BLK_ZONE_COND_READONLY && ++ if (!(z->z_flags & (ZONEFS_ZONE_READONLY | ZONEFS_ZONE_OFFLINE)) && + !err->write && isize == data_size) + return 0; + +@@ -414,8 +291,9 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx, + * In all cases, warn about inode size inconsistency and handle the + * IO error according to the zone condition and to the mount options. + */ +- if (zi->i_ztype == ZONEFS_ZTYPE_SEQ && isize != data_size) +- zonefs_warn(sb, "inode %lu: invalid size %lld (should be %lld)\n", ++ if (zonefs_zone_is_seq(z) && isize != data_size) ++ zonefs_warn(sb, ++ "inode %lu: invalid size %lld (should be %lld)\n", + inode->i_ino, isize, data_size); + + /* +@@ -424,24 +302,22 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx, + * zone condition to read-only and offline respectively, as if the + * condition was signaled by the hardware. + */ +- if (zone->cond == BLK_ZONE_COND_OFFLINE || +- sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZOL) { ++ if ((z->z_flags & ZONEFS_ZONE_OFFLINE) || ++ (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZOL)) { + zonefs_warn(sb, "inode %lu: read/write access disabled\n", + inode->i_ino); +- if (zone->cond != BLK_ZONE_COND_OFFLINE) { +- zone->cond = BLK_ZONE_COND_OFFLINE; +- data_size = zonefs_check_zone_condition(inode, zone, +- false, false); +- } +- } else if (zone->cond == BLK_ZONE_COND_READONLY || +- sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZRO) { ++ if (!(z->z_flags & ZONEFS_ZONE_OFFLINE)) ++ z->z_flags |= ZONEFS_ZONE_OFFLINE; ++ zonefs_inode_update_mode(inode); ++ data_size = 0; ++ } else if ((z->z_flags & ZONEFS_ZONE_READONLY) || ++ (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZRO)) { + zonefs_warn(sb, "inode %lu: write access disabled\n", + inode->i_ino); +- if (zone->cond != BLK_ZONE_COND_READONLY) { +- zone->cond = BLK_ZONE_COND_READONLY; +- data_size = zonefs_check_zone_condition(inode, zone, +- false, false); +- } ++ if (!(z->z_flags & ZONEFS_ZONE_READONLY)) ++ z->z_flags |= ZONEFS_ZONE_READONLY; ++ zonefs_inode_update_mode(inode); ++ data_size = isize; + } else if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_RO && + data_size > isize) { + /* Do not expose garbage data */ +@@ -455,9 +331,8 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx, + * close of the zone when the inode file is closed. + */ + if ((sbi->s_mount_opts & ZONEFS_MNTOPT_EXPLICIT_OPEN) && +- (zone->cond == BLK_ZONE_COND_OFFLINE || +- zone->cond == BLK_ZONE_COND_READONLY)) +- zi->i_flags &= ~ZONEFS_ZONE_OPEN; ++ (z->z_flags & (ZONEFS_ZONE_READONLY | ZONEFS_ZONE_OFFLINE))) ++ z->z_flags &= ~ZONEFS_ZONE_OPEN; + + /* + * If error=remount-ro was specified, any error result in remounting +@@ -474,8 +349,8 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx, + */ + zonefs_update_stats(inode, data_size); + zonefs_i_size_write(inode, data_size); +- zi->i_wpoffset = data_size; +- zonefs_account_active(inode); ++ z->z_wpoffset = data_size; ++ zonefs_inode_account_active(inode); + + return 0; + } +@@ -487,9 +362,9 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx, + * eventually correct the file size and zonefs inode write pointer offset + * (which can be out of sync with the drive due to partial write failures). + */ +-static void __zonefs_io_error(struct inode *inode, bool write) ++void __zonefs_io_error(struct inode *inode, bool write) + { +- struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ struct zonefs_zone *z = zonefs_inode_zone(inode); + struct super_block *sb = inode->i_sb; + struct zonefs_sb_info *sbi = ZONEFS_SB(sb); + unsigned int noio_flag; +@@ -505,8 +380,8 @@ static void __zonefs_io_error(struct inode *inode, bool write) + * files with aggregated conventional zones, for which the inode zone + * size is always larger than the device zone size. + */ +- if (zi->i_zone_size > bdev_zone_sectors(sb->s_bdev)) +- nr_zones = zi->i_zone_size >> ++ if (z->z_size > bdev_zone_sectors(sb->s_bdev)) ++ nr_zones = z->z_size >> + (sbi->s_zone_sectors_shift + SECTOR_SHIFT); + + /* +@@ -518,7 +393,7 @@ static void __zonefs_io_error(struct inode *inode, bool write) + * the GFP_NOIO context avoids both problems. + */ + noio_flag = memalloc_noio_save(); +- ret = blkdev_report_zones(sb->s_bdev, zi->i_zsector, nr_zones, ++ ret = blkdev_report_zones(sb->s_bdev, z->z_sector, nr_zones, + zonefs_io_error_cb, &err); + if (ret != nr_zones) + zonefs_err(sb, "Get inode %lu zone information failed %d\n", +@@ -526,749 +401,6 @@ static void __zonefs_io_error(struct inode *inode, bool write) + memalloc_noio_restore(noio_flag); + } + +-static void zonefs_io_error(struct inode *inode, bool write) +-{ +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- +- mutex_lock(&zi->i_truncate_mutex); +- __zonefs_io_error(inode, write); +- mutex_unlock(&zi->i_truncate_mutex); +-} +- +-static int zonefs_file_truncate(struct inode *inode, loff_t isize) +-{ +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- loff_t old_isize; +- enum req_op op; +- int ret = 0; +- +- /* +- * Only sequential zone files can be truncated and truncation is allowed +- * only down to a 0 size, which is equivalent to a zone reset, and to +- * the maximum file size, which is equivalent to a zone finish. +- */ +- if (zi->i_ztype != ZONEFS_ZTYPE_SEQ) +- return -EPERM; +- +- if (!isize) +- op = REQ_OP_ZONE_RESET; +- else if (isize == zi->i_max_size) +- op = REQ_OP_ZONE_FINISH; +- else +- return -EPERM; +- +- inode_dio_wait(inode); +- +- /* Serialize against page faults */ +- filemap_invalidate_lock(inode->i_mapping); +- +- /* Serialize against zonefs_iomap_begin() */ +- mutex_lock(&zi->i_truncate_mutex); +- +- old_isize = i_size_read(inode); +- if (isize == old_isize) +- goto unlock; +- +- ret = zonefs_zone_mgmt(inode, op); +- if (ret) +- goto unlock; +- +- /* +- * If the mount option ZONEFS_MNTOPT_EXPLICIT_OPEN is set, +- * take care of open zones. +- */ +- if (zi->i_flags & ZONEFS_ZONE_OPEN) { +- /* +- * Truncating a zone to EMPTY or FULL is the equivalent of +- * closing the zone. For a truncation to 0, we need to +- * re-open the zone to ensure new writes can be processed. +- * For a truncation to the maximum file size, the zone is +- * closed and writes cannot be accepted anymore, so clear +- * the open flag. +- */ +- if (!isize) +- ret = zonefs_zone_mgmt(inode, REQ_OP_ZONE_OPEN); +- else +- zi->i_flags &= ~ZONEFS_ZONE_OPEN; +- } +- +- zonefs_update_stats(inode, isize); +- truncate_setsize(inode, isize); +- zi->i_wpoffset = isize; +- zonefs_account_active(inode); +- +-unlock: +- mutex_unlock(&zi->i_truncate_mutex); +- filemap_invalidate_unlock(inode->i_mapping); +- +- return ret; +-} +- +-static int zonefs_inode_setattr(struct user_namespace *mnt_userns, +- struct dentry *dentry, struct iattr *iattr) +-{ +- struct inode *inode = d_inode(dentry); +- int ret; +- +- if (unlikely(IS_IMMUTABLE(inode))) +- return -EPERM; +- +- ret = setattr_prepare(&init_user_ns, dentry, iattr); +- if (ret) +- return ret; +- +- /* +- * Since files and directories cannot be created nor deleted, do not +- * allow setting any write attributes on the sub-directories grouping +- * files by zone type. +- */ +- if ((iattr->ia_valid & ATTR_MODE) && S_ISDIR(inode->i_mode) && +- (iattr->ia_mode & 0222)) +- return -EPERM; +- +- if (((iattr->ia_valid & ATTR_UID) && +- !uid_eq(iattr->ia_uid, inode->i_uid)) || +- ((iattr->ia_valid & ATTR_GID) && +- !gid_eq(iattr->ia_gid, inode->i_gid))) { +- ret = dquot_transfer(mnt_userns, inode, iattr); +- if (ret) +- return ret; +- } +- +- if (iattr->ia_valid & ATTR_SIZE) { +- ret = zonefs_file_truncate(inode, iattr->ia_size); +- if (ret) +- return ret; +- } +- +- setattr_copy(&init_user_ns, inode, iattr); +- +- return 0; +-} +- +-static const struct inode_operations zonefs_file_inode_operations = { +- .setattr = zonefs_inode_setattr, +-}; +- +-static int zonefs_file_fsync(struct file *file, loff_t start, loff_t end, +- int datasync) +-{ +- struct inode *inode = file_inode(file); +- int ret = 0; +- +- if (unlikely(IS_IMMUTABLE(inode))) +- return -EPERM; +- +- /* +- * Since only direct writes are allowed in sequential files, page cache +- * flush is needed only for conventional zone files. +- */ +- if (ZONEFS_I(inode)->i_ztype == ZONEFS_ZTYPE_CNV) +- ret = file_write_and_wait_range(file, start, end); +- if (!ret) +- ret = blkdev_issue_flush(inode->i_sb->s_bdev); +- +- if (ret) +- zonefs_io_error(inode, true); +- +- return ret; +-} +- +-static vm_fault_t zonefs_filemap_page_mkwrite(struct vm_fault *vmf) +-{ +- struct inode *inode = file_inode(vmf->vma->vm_file); +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- vm_fault_t ret; +- +- if (unlikely(IS_IMMUTABLE(inode))) +- return VM_FAULT_SIGBUS; +- +- /* +- * Sanity check: only conventional zone files can have shared +- * writeable mappings. +- */ +- if (WARN_ON_ONCE(zi->i_ztype != ZONEFS_ZTYPE_CNV)) +- return VM_FAULT_NOPAGE; +- +- sb_start_pagefault(inode->i_sb); +- file_update_time(vmf->vma->vm_file); +- +- /* Serialize against truncates */ +- filemap_invalidate_lock_shared(inode->i_mapping); +- ret = iomap_page_mkwrite(vmf, &zonefs_write_iomap_ops); +- filemap_invalidate_unlock_shared(inode->i_mapping); +- +- sb_end_pagefault(inode->i_sb); +- return ret; +-} +- +-static const struct vm_operations_struct zonefs_file_vm_ops = { +- .fault = filemap_fault, +- .map_pages = filemap_map_pages, +- .page_mkwrite = zonefs_filemap_page_mkwrite, +-}; +- +-static int zonefs_file_mmap(struct file *file, struct vm_area_struct *vma) +-{ +- /* +- * Conventional zones accept random writes, so their files can support +- * shared writable mappings. For sequential zone files, only read +- * mappings are possible since there are no guarantees for write +- * ordering between msync() and page cache writeback. +- */ +- if (ZONEFS_I(file_inode(file))->i_ztype == ZONEFS_ZTYPE_SEQ && +- (vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) +- return -EINVAL; +- +- file_accessed(file); +- vma->vm_ops = &zonefs_file_vm_ops; +- +- return 0; +-} +- +-static loff_t zonefs_file_llseek(struct file *file, loff_t offset, int whence) +-{ +- loff_t isize = i_size_read(file_inode(file)); +- +- /* +- * Seeks are limited to below the zone size for conventional zones +- * and below the zone write pointer for sequential zones. In both +- * cases, this limit is the inode size. +- */ +- return generic_file_llseek_size(file, offset, whence, isize, isize); +-} +- +-static int zonefs_file_write_dio_end_io(struct kiocb *iocb, ssize_t size, +- int error, unsigned int flags) +-{ +- struct inode *inode = file_inode(iocb->ki_filp); +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- +- if (error) { +- zonefs_io_error(inode, true); +- return error; +- } +- +- if (size && zi->i_ztype != ZONEFS_ZTYPE_CNV) { +- /* +- * Note that we may be seeing completions out of order, +- * but that is not a problem since a write completed +- * successfully necessarily means that all preceding writes +- * were also successful. So we can safely increase the inode +- * size to the write end location. +- */ +- mutex_lock(&zi->i_truncate_mutex); +- if (i_size_read(inode) < iocb->ki_pos + size) { +- zonefs_update_stats(inode, iocb->ki_pos + size); +- zonefs_i_size_write(inode, iocb->ki_pos + size); +- } +- mutex_unlock(&zi->i_truncate_mutex); +- } +- +- return 0; +-} +- +-static const struct iomap_dio_ops zonefs_write_dio_ops = { +- .end_io = zonefs_file_write_dio_end_io, +-}; +- +-static ssize_t zonefs_file_dio_append(struct kiocb *iocb, struct iov_iter *from) +-{ +- struct inode *inode = file_inode(iocb->ki_filp); +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- struct block_device *bdev = inode->i_sb->s_bdev; +- unsigned int max = bdev_max_zone_append_sectors(bdev); +- struct bio *bio; +- ssize_t size; +- int nr_pages; +- ssize_t ret; +- +- max = ALIGN_DOWN(max << SECTOR_SHIFT, inode->i_sb->s_blocksize); +- iov_iter_truncate(from, max); +- +- nr_pages = iov_iter_npages(from, BIO_MAX_VECS); +- if (!nr_pages) +- return 0; +- +- bio = bio_alloc(bdev, nr_pages, +- REQ_OP_ZONE_APPEND | REQ_SYNC | REQ_IDLE, GFP_NOFS); +- bio->bi_iter.bi_sector = zi->i_zsector; +- bio->bi_ioprio = iocb->ki_ioprio; +- if (iocb_is_dsync(iocb)) +- bio->bi_opf |= REQ_FUA; +- +- ret = bio_iov_iter_get_pages(bio, from); +- if (unlikely(ret)) +- goto out_release; +- +- size = bio->bi_iter.bi_size; +- task_io_account_write(size); +- +- if (iocb->ki_flags & IOCB_HIPRI) +- bio_set_polled(bio, iocb); +- +- ret = submit_bio_wait(bio); +- +- /* +- * If the file zone was written underneath the file system, the zone +- * write pointer may not be where we expect it to be, but the zone +- * append write can still succeed. So check manually that we wrote where +- * we intended to, that is, at zi->i_wpoffset. +- */ +- if (!ret) { +- sector_t wpsector = +- zi->i_zsector + (zi->i_wpoffset >> SECTOR_SHIFT); +- +- if (bio->bi_iter.bi_sector != wpsector) { +- zonefs_warn(inode->i_sb, +- "Corrupted write pointer %llu for zone at %llu\n", +- wpsector, zi->i_zsector); +- ret = -EIO; +- } +- } +- +- zonefs_file_write_dio_end_io(iocb, size, ret, 0); +- trace_zonefs_file_dio_append(inode, size, ret); +- +-out_release: +- bio_release_pages(bio, false); +- bio_put(bio); +- +- if (ret >= 0) { +- iocb->ki_pos += size; +- return size; +- } +- +- return ret; +-} +- +-/* +- * Do not exceed the LFS limits nor the file zone size. If pos is under the +- * limit it becomes a short access. If it exceeds the limit, return -EFBIG. +- */ +-static loff_t zonefs_write_check_limits(struct file *file, loff_t pos, +- loff_t count) +-{ +- struct inode *inode = file_inode(file); +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- loff_t limit = rlimit(RLIMIT_FSIZE); +- loff_t max_size = zi->i_max_size; +- +- if (limit != RLIM_INFINITY) { +- if (pos >= limit) { +- send_sig(SIGXFSZ, current, 0); +- return -EFBIG; +- } +- count = min(count, limit - pos); +- } +- +- if (!(file->f_flags & O_LARGEFILE)) +- max_size = min_t(loff_t, MAX_NON_LFS, max_size); +- +- if (unlikely(pos >= max_size)) +- return -EFBIG; +- +- return min(count, max_size - pos); +-} +- +-static ssize_t zonefs_write_checks(struct kiocb *iocb, struct iov_iter *from) +-{ +- struct file *file = iocb->ki_filp; +- struct inode *inode = file_inode(file); +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- loff_t count; +- +- if (IS_SWAPFILE(inode)) +- return -ETXTBSY; +- +- if (!iov_iter_count(from)) +- return 0; +- +- if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT)) +- return -EINVAL; +- +- if (iocb->ki_flags & IOCB_APPEND) { +- if (zi->i_ztype != ZONEFS_ZTYPE_SEQ) +- return -EINVAL; +- mutex_lock(&zi->i_truncate_mutex); +- iocb->ki_pos = zi->i_wpoffset; +- mutex_unlock(&zi->i_truncate_mutex); +- } +- +- count = zonefs_write_check_limits(file, iocb->ki_pos, +- iov_iter_count(from)); +- if (count < 0) +- return count; +- +- iov_iter_truncate(from, count); +- return iov_iter_count(from); +-} +- +-/* +- * Handle direct writes. For sequential zone files, this is the only possible +- * write path. For these files, check that the user is issuing writes +- * sequentially from the end of the file. This code assumes that the block layer +- * delivers write requests to the device in sequential order. This is always the +- * case if a block IO scheduler implementing the ELEVATOR_F_ZBD_SEQ_WRITE +- * elevator feature is being used (e.g. mq-deadline). The block layer always +- * automatically select such an elevator for zoned block devices during the +- * device initialization. +- */ +-static ssize_t zonefs_file_dio_write(struct kiocb *iocb, struct iov_iter *from) +-{ +- struct inode *inode = file_inode(iocb->ki_filp); +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- struct super_block *sb = inode->i_sb; +- bool sync = is_sync_kiocb(iocb); +- bool append = false; +- ssize_t ret, count; +- +- /* +- * For async direct IOs to sequential zone files, refuse IOCB_NOWAIT +- * as this can cause write reordering (e.g. the first aio gets EAGAIN +- * on the inode lock but the second goes through but is now unaligned). +- */ +- if (zi->i_ztype == ZONEFS_ZTYPE_SEQ && !sync && +- (iocb->ki_flags & IOCB_NOWAIT)) +- return -EOPNOTSUPP; +- +- if (iocb->ki_flags & IOCB_NOWAIT) { +- if (!inode_trylock(inode)) +- return -EAGAIN; +- } else { +- inode_lock(inode); +- } +- +- count = zonefs_write_checks(iocb, from); +- if (count <= 0) { +- ret = count; +- goto inode_unlock; +- } +- +- if ((iocb->ki_pos | count) & (sb->s_blocksize - 1)) { +- ret = -EINVAL; +- goto inode_unlock; +- } +- +- /* Enforce sequential writes (append only) in sequential zones */ +- if (zi->i_ztype == ZONEFS_ZTYPE_SEQ) { +- mutex_lock(&zi->i_truncate_mutex); +- if (iocb->ki_pos != zi->i_wpoffset) { +- mutex_unlock(&zi->i_truncate_mutex); +- ret = -EINVAL; +- goto inode_unlock; +- } +- mutex_unlock(&zi->i_truncate_mutex); +- append = sync; +- } +- +- if (append) +- ret = zonefs_file_dio_append(iocb, from); +- else +- ret = iomap_dio_rw(iocb, from, &zonefs_write_iomap_ops, +- &zonefs_write_dio_ops, 0, NULL, 0); +- if (zi->i_ztype == ZONEFS_ZTYPE_SEQ && +- (ret > 0 || ret == -EIOCBQUEUED)) { +- if (ret > 0) +- count = ret; +- +- /* +- * Update the zone write pointer offset assuming the write +- * operation succeeded. If it did not, the error recovery path +- * will correct it. Also do active seq file accounting. +- */ +- mutex_lock(&zi->i_truncate_mutex); +- zi->i_wpoffset += count; +- zonefs_account_active(inode); +- mutex_unlock(&zi->i_truncate_mutex); +- } +- +-inode_unlock: +- inode_unlock(inode); +- +- return ret; +-} +- +-static ssize_t zonefs_file_buffered_write(struct kiocb *iocb, +- struct iov_iter *from) +-{ +- struct inode *inode = file_inode(iocb->ki_filp); +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- ssize_t ret; +- +- /* +- * Direct IO writes are mandatory for sequential zone files so that the +- * write IO issuing order is preserved. +- */ +- if (zi->i_ztype != ZONEFS_ZTYPE_CNV) +- return -EIO; +- +- if (iocb->ki_flags & IOCB_NOWAIT) { +- if (!inode_trylock(inode)) +- return -EAGAIN; +- } else { +- inode_lock(inode); +- } +- +- ret = zonefs_write_checks(iocb, from); +- if (ret <= 0) +- goto inode_unlock; +- +- ret = iomap_file_buffered_write(iocb, from, &zonefs_write_iomap_ops); +- if (ret > 0) +- iocb->ki_pos += ret; +- else if (ret == -EIO) +- zonefs_io_error(inode, true); +- +-inode_unlock: +- inode_unlock(inode); +- if (ret > 0) +- ret = generic_write_sync(iocb, ret); +- +- return ret; +-} +- +-static ssize_t zonefs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) +-{ +- struct inode *inode = file_inode(iocb->ki_filp); +- +- if (unlikely(IS_IMMUTABLE(inode))) +- return -EPERM; +- +- if (sb_rdonly(inode->i_sb)) +- return -EROFS; +- +- /* Write operations beyond the zone size are not allowed */ +- if (iocb->ki_pos >= ZONEFS_I(inode)->i_max_size) +- return -EFBIG; +- +- if (iocb->ki_flags & IOCB_DIRECT) { +- ssize_t ret = zonefs_file_dio_write(iocb, from); +- if (ret != -ENOTBLK) +- return ret; +- } +- +- return zonefs_file_buffered_write(iocb, from); +-} +- +-static int zonefs_file_read_dio_end_io(struct kiocb *iocb, ssize_t size, +- int error, unsigned int flags) +-{ +- if (error) { +- zonefs_io_error(file_inode(iocb->ki_filp), false); +- return error; +- } +- +- return 0; +-} +- +-static const struct iomap_dio_ops zonefs_read_dio_ops = { +- .end_io = zonefs_file_read_dio_end_io, +-}; +- +-static ssize_t zonefs_file_read_iter(struct kiocb *iocb, struct iov_iter *to) +-{ +- struct inode *inode = file_inode(iocb->ki_filp); +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- struct super_block *sb = inode->i_sb; +- loff_t isize; +- ssize_t ret; +- +- /* Offline zones cannot be read */ +- if (unlikely(IS_IMMUTABLE(inode) && !(inode->i_mode & 0777))) +- return -EPERM; +- +- if (iocb->ki_pos >= zi->i_max_size) +- return 0; +- +- if (iocb->ki_flags & IOCB_NOWAIT) { +- if (!inode_trylock_shared(inode)) +- return -EAGAIN; +- } else { +- inode_lock_shared(inode); +- } +- +- /* Limit read operations to written data */ +- mutex_lock(&zi->i_truncate_mutex); +- isize = i_size_read(inode); +- if (iocb->ki_pos >= isize) { +- mutex_unlock(&zi->i_truncate_mutex); +- ret = 0; +- goto inode_unlock; +- } +- iov_iter_truncate(to, isize - iocb->ki_pos); +- mutex_unlock(&zi->i_truncate_mutex); +- +- if (iocb->ki_flags & IOCB_DIRECT) { +- size_t count = iov_iter_count(to); +- +- if ((iocb->ki_pos | count) & (sb->s_blocksize - 1)) { +- ret = -EINVAL; +- goto inode_unlock; +- } +- file_accessed(iocb->ki_filp); +- ret = iomap_dio_rw(iocb, to, &zonefs_read_iomap_ops, +- &zonefs_read_dio_ops, 0, NULL, 0); +- } else { +- ret = generic_file_read_iter(iocb, to); +- if (ret == -EIO) +- zonefs_io_error(inode, false); +- } +- +-inode_unlock: +- inode_unlock_shared(inode); +- +- return ret; +-} +- +-/* +- * Write open accounting is done only for sequential files. +- */ +-static inline bool zonefs_seq_file_need_wro(struct inode *inode, +- struct file *file) +-{ +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- +- if (zi->i_ztype != ZONEFS_ZTYPE_SEQ) +- return false; +- +- if (!(file->f_mode & FMODE_WRITE)) +- return false; +- +- return true; +-} +- +-static int zonefs_seq_file_write_open(struct inode *inode) +-{ +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- int ret = 0; +- +- mutex_lock(&zi->i_truncate_mutex); +- +- if (!zi->i_wr_refcnt) { +- struct zonefs_sb_info *sbi = ZONEFS_SB(inode->i_sb); +- unsigned int wro = atomic_inc_return(&sbi->s_wro_seq_files); +- +- if (sbi->s_mount_opts & ZONEFS_MNTOPT_EXPLICIT_OPEN) { +- +- if (sbi->s_max_wro_seq_files +- && wro > sbi->s_max_wro_seq_files) { +- atomic_dec(&sbi->s_wro_seq_files); +- ret = -EBUSY; +- goto unlock; +- } +- +- if (i_size_read(inode) < zi->i_max_size) { +- ret = zonefs_zone_mgmt(inode, REQ_OP_ZONE_OPEN); +- if (ret) { +- atomic_dec(&sbi->s_wro_seq_files); +- goto unlock; +- } +- zi->i_flags |= ZONEFS_ZONE_OPEN; +- zonefs_account_active(inode); +- } +- } +- } +- +- zi->i_wr_refcnt++; +- +-unlock: +- mutex_unlock(&zi->i_truncate_mutex); +- +- return ret; +-} +- +-static int zonefs_file_open(struct inode *inode, struct file *file) +-{ +- int ret; +- +- ret = generic_file_open(inode, file); +- if (ret) +- return ret; +- +- if (zonefs_seq_file_need_wro(inode, file)) +- return zonefs_seq_file_write_open(inode); +- +- return 0; +-} +- +-static void zonefs_seq_file_write_close(struct inode *inode) +-{ +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- struct super_block *sb = inode->i_sb; +- struct zonefs_sb_info *sbi = ZONEFS_SB(sb); +- int ret = 0; +- +- mutex_lock(&zi->i_truncate_mutex); +- +- zi->i_wr_refcnt--; +- if (zi->i_wr_refcnt) +- goto unlock; +- +- /* +- * The file zone may not be open anymore (e.g. the file was truncated to +- * its maximum size or it was fully written). For this case, we only +- * need to decrement the write open count. +- */ +- if (zi->i_flags & ZONEFS_ZONE_OPEN) { +- ret = zonefs_zone_mgmt(inode, REQ_OP_ZONE_CLOSE); +- if (ret) { +- __zonefs_io_error(inode, false); +- /* +- * Leaving zones explicitly open may lead to a state +- * where most zones cannot be written (zone resources +- * exhausted). So take preventive action by remounting +- * read-only. +- */ +- if (zi->i_flags & ZONEFS_ZONE_OPEN && +- !(sb->s_flags & SB_RDONLY)) { +- zonefs_warn(sb, +- "closing zone at %llu failed %d\n", +- zi->i_zsector, ret); +- zonefs_warn(sb, +- "remounting filesystem read-only\n"); +- sb->s_flags |= SB_RDONLY; +- } +- goto unlock; +- } +- +- zi->i_flags &= ~ZONEFS_ZONE_OPEN; +- zonefs_account_active(inode); +- } +- +- atomic_dec(&sbi->s_wro_seq_files); +- +-unlock: +- mutex_unlock(&zi->i_truncate_mutex); +-} +- +-static int zonefs_file_release(struct inode *inode, struct file *file) +-{ +- /* +- * If we explicitly open a zone we must close it again as well, but the +- * zone management operation can fail (either due to an IO error or as +- * the zone has gone offline or read-only). Make sure we don't fail the +- * close(2) for user-space. +- */ +- if (zonefs_seq_file_need_wro(inode, file)) +- zonefs_seq_file_write_close(inode); +- +- return 0; +-} +- +-static const struct file_operations zonefs_file_operations = { +- .open = zonefs_file_open, +- .release = zonefs_file_release, +- .fsync = zonefs_file_fsync, +- .mmap = zonefs_file_mmap, +- .llseek = zonefs_file_llseek, +- .read_iter = zonefs_file_read_iter, +- .write_iter = zonefs_file_write_iter, +- .splice_read = generic_file_splice_read, +- .splice_write = iter_file_splice_write, +- .iopoll = iocb_bio_iopoll, +-}; +- + static struct kmem_cache *zonefs_inode_cachep; + + static struct inode *zonefs_alloc_inode(struct super_block *sb) +@@ -1282,7 +414,6 @@ static struct inode *zonefs_alloc_inode(struct super_block *sb) + inode_init_once(&zi->i_vnode); + mutex_init(&zi->i_truncate_mutex); + zi->i_wr_refcnt = 0; +- zi->i_flags = 0; + + return &zi->i_vnode; + } +@@ -1315,8 +446,8 @@ static int zonefs_statfs(struct dentry *dentry, struct kstatfs *buf) + buf->f_bavail = buf->f_bfree; + + for (t = 0; t < ZONEFS_ZTYPE_MAX; t++) { +- if (sbi->s_nr_files[t]) +- buf->f_files += sbi->s_nr_files[t] + 1; ++ if (sbi->s_zgroup[t].g_nr_zones) ++ buf->f_files += sbi->s_zgroup[t].g_nr_zones + 1; + } + buf->f_ffree = 0; + +@@ -1382,51 +513,85 @@ static int zonefs_parse_options(struct super_block *sb, char *options) + } + } + +- return 0; +-} ++ return 0; ++} ++ ++static int zonefs_show_options(struct seq_file *seq, struct dentry *root) ++{ ++ struct zonefs_sb_info *sbi = ZONEFS_SB(root->d_sb); ++ ++ if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_RO) ++ seq_puts(seq, ",errors=remount-ro"); ++ if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZRO) ++ seq_puts(seq, ",errors=zone-ro"); ++ if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZOL) ++ seq_puts(seq, ",errors=zone-offline"); ++ if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_REPAIR) ++ seq_puts(seq, ",errors=repair"); ++ ++ return 0; ++} ++ ++static int zonefs_remount(struct super_block *sb, int *flags, char *data) ++{ ++ sync_filesystem(sb); ++ ++ return zonefs_parse_options(sb, data); ++} ++ ++static int zonefs_inode_setattr(struct user_namespace *mnt_userns, ++ struct dentry *dentry, struct iattr *iattr) ++{ ++ struct inode *inode = d_inode(dentry); ++ int ret; ++ ++ if (unlikely(IS_IMMUTABLE(inode))) ++ return -EPERM; ++ ++ ret = setattr_prepare(&init_user_ns, dentry, iattr); ++ if (ret) ++ return ret; ++ ++ /* ++ * Since files and directories cannot be created nor deleted, do not ++ * allow setting any write attributes on the sub-directories grouping ++ * files by zone type. ++ */ ++ if ((iattr->ia_valid & ATTR_MODE) && S_ISDIR(inode->i_mode) && ++ (iattr->ia_mode & 0222)) ++ return -EPERM; ++ ++ if (((iattr->ia_valid & ATTR_UID) && ++ !uid_eq(iattr->ia_uid, inode->i_uid)) || ++ ((iattr->ia_valid & ATTR_GID) && ++ !gid_eq(iattr->ia_gid, inode->i_gid))) { ++ ret = dquot_transfer(mnt_userns, inode, iattr); ++ if (ret) ++ return ret; ++ } + +-static int zonefs_show_options(struct seq_file *seq, struct dentry *root) +-{ +- struct zonefs_sb_info *sbi = ZONEFS_SB(root->d_sb); ++ if (iattr->ia_valid & ATTR_SIZE) { ++ ret = zonefs_file_truncate(inode, iattr->ia_size); ++ if (ret) ++ return ret; ++ } + +- if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_RO) +- seq_puts(seq, ",errors=remount-ro"); +- if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZRO) +- seq_puts(seq, ",errors=zone-ro"); +- if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_ZOL) +- seq_puts(seq, ",errors=zone-offline"); +- if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_REPAIR) +- seq_puts(seq, ",errors=repair"); ++ setattr_copy(&init_user_ns, inode, iattr); + + return 0; + } + +-static int zonefs_remount(struct super_block *sb, int *flags, char *data) +-{ +- sync_filesystem(sb); +- +- return zonefs_parse_options(sb, data); +-} +- +-static const struct super_operations zonefs_sops = { +- .alloc_inode = zonefs_alloc_inode, +- .free_inode = zonefs_free_inode, +- .statfs = zonefs_statfs, +- .remount_fs = zonefs_remount, +- .show_options = zonefs_show_options, +-}; +- + static const struct inode_operations zonefs_dir_inode_operations = { + .lookup = simple_lookup, + .setattr = zonefs_inode_setattr, + }; + + static void zonefs_init_dir_inode(struct inode *parent, struct inode *inode, +- enum zonefs_ztype type) ++ enum zonefs_ztype ztype) + { + struct super_block *sb = parent->i_sb; + +- inode->i_ino = bdev_nr_zones(sb->s_bdev) + type + 1; ++ inode->i_ino = bdev_nr_zones(sb->s_bdev) + ztype + 1; + inode_init_owner(&init_user_ns, inode, parent, S_IFDIR | 0555); + inode->i_op = &zonefs_dir_inode_operations; + inode->i_fop = &simple_dir_operations; +@@ -1434,73 +599,38 @@ static void zonefs_init_dir_inode(struct inode *parent, struct inode *inode, + inc_nlink(parent); + } + +-static int zonefs_init_file_inode(struct inode *inode, struct blk_zone *zone, +- enum zonefs_ztype type) ++static const struct inode_operations zonefs_file_inode_operations = { ++ .setattr = zonefs_inode_setattr, ++}; ++ ++static void zonefs_init_file_inode(struct inode *inode, ++ struct zonefs_zone *z) + { + struct super_block *sb = inode->i_sb; + struct zonefs_sb_info *sbi = ZONEFS_SB(sb); +- struct zonefs_inode_info *zi = ZONEFS_I(inode); +- int ret = 0; +- +- inode->i_ino = zone->start >> sbi->s_zone_sectors_shift; +- inode->i_mode = S_IFREG | sbi->s_perm; +- +- zi->i_ztype = type; +- zi->i_zsector = zone->start; +- zi->i_zone_size = zone->len << SECTOR_SHIFT; +- if (zi->i_zone_size > bdev_zone_sectors(sb->s_bdev) << SECTOR_SHIFT && +- !(sbi->s_features & ZONEFS_F_AGGRCNV)) { +- zonefs_err(sb, +- "zone size %llu doesn't match device's zone sectors %llu\n", +- zi->i_zone_size, +- bdev_zone_sectors(sb->s_bdev) << SECTOR_SHIFT); +- return -EINVAL; +- } + +- zi->i_max_size = min_t(loff_t, MAX_LFS_FILESIZE, +- zone->capacity << SECTOR_SHIFT); +- zi->i_wpoffset = zonefs_check_zone_condition(inode, zone, true, true); ++ inode->i_private = z; + ++ inode->i_ino = z->z_sector >> sbi->s_zone_sectors_shift; ++ inode->i_mode = S_IFREG | sbi->s_perm; + inode->i_uid = sbi->s_uid; + inode->i_gid = sbi->s_gid; +- inode->i_size = zi->i_wpoffset; +- inode->i_blocks = zi->i_max_size >> SECTOR_SHIFT; ++ inode->i_size = z->z_wpoffset; ++ inode->i_blocks = z->z_capacity >> SECTOR_SHIFT; + + inode->i_op = &zonefs_file_inode_operations; + inode->i_fop = &zonefs_file_operations; + inode->i_mapping->a_ops = &zonefs_file_aops; + +- sb->s_maxbytes = max(zi->i_max_size, sb->s_maxbytes); +- sbi->s_blocks += zi->i_max_size >> sb->s_blocksize_bits; +- sbi->s_used_blocks += zi->i_wpoffset >> sb->s_blocksize_bits; +- +- mutex_lock(&zi->i_truncate_mutex); +- +- /* +- * For sequential zones, make sure that any open zone is closed first +- * to ensure that the initial number of open zones is 0, in sync with +- * the open zone accounting done when the mount option +- * ZONEFS_MNTOPT_EXPLICIT_OPEN is used. +- */ +- if (type == ZONEFS_ZTYPE_SEQ && +- (zone->cond == BLK_ZONE_COND_IMP_OPEN || +- zone->cond == BLK_ZONE_COND_EXP_OPEN)) { +- ret = zonefs_zone_mgmt(inode, REQ_OP_ZONE_CLOSE); +- if (ret) +- goto unlock; +- } +- +- zonefs_account_active(inode); +- +-unlock: +- mutex_unlock(&zi->i_truncate_mutex); +- +- return ret; ++ /* Update the inode access rights depending on the zone condition */ ++ z->z_flags |= ZONEFS_ZONE_INIT_MODE; ++ zonefs_inode_update_mode(inode); + } + + static struct dentry *zonefs_create_inode(struct dentry *parent, +- const char *name, struct blk_zone *zone, +- enum zonefs_ztype type) ++ const char *name, ++ struct zonefs_zone *z, ++ enum zonefs_ztype ztype) + { + struct inode *dir = d_inode(parent); + struct dentry *dentry; +@@ -1516,15 +646,10 @@ static struct dentry *zonefs_create_inode(struct dentry *parent, + goto dput; + + inode->i_ctime = inode->i_mtime = inode->i_atime = dir->i_ctime; +- if (zone) { +- ret = zonefs_init_file_inode(inode, zone, type); +- if (ret) { +- iput(inode); +- goto dput; +- } +- } else { +- zonefs_init_dir_inode(dir, inode, type); +- } ++ if (z) ++ zonefs_init_file_inode(inode, z); ++ else ++ zonefs_init_dir_inode(dir, inode, ztype); + + d_add(dentry, inode); + dir->i_size++; +@@ -1540,100 +665,51 @@ dput: + struct zonefs_zone_data { + struct super_block *sb; + unsigned int nr_zones[ZONEFS_ZTYPE_MAX]; ++ sector_t cnv_zone_start; + struct blk_zone *zones; + }; + + /* +- * Create a zone group and populate it with zone files. ++ * Create the inodes for a zone group. + */ +-static int zonefs_create_zgroup(struct zonefs_zone_data *zd, +- enum zonefs_ztype type) ++static int zonefs_create_zgroup_inodes(struct super_block *sb, ++ enum zonefs_ztype ztype) + { +- struct super_block *sb = zd->sb; + struct zonefs_sb_info *sbi = ZONEFS_SB(sb); +- struct blk_zone *zone, *next, *end; +- const char *zgroup_name; +- char *file_name; ++ struct zonefs_zone_group *zgroup = &sbi->s_zgroup[ztype]; + struct dentry *dir, *dent; +- unsigned int n = 0; +- int ret; ++ char *file_name; ++ int i, ret = 0; ++ ++ if (!zgroup) ++ return -ENOMEM; + + /* If the group is empty, there is nothing to do */ +- if (!zd->nr_zones[type]) ++ if (!zgroup->g_nr_zones) + return 0; + + file_name = kmalloc(ZONEFS_NAME_MAX, GFP_KERNEL); + if (!file_name) + return -ENOMEM; + +- if (type == ZONEFS_ZTYPE_CNV) +- zgroup_name = "cnv"; +- else +- zgroup_name = "seq"; +- +- dir = zonefs_create_inode(sb->s_root, zgroup_name, NULL, type); ++ dir = zonefs_create_inode(sb->s_root, zonefs_zgroup_name(ztype), ++ NULL, ztype); + if (IS_ERR(dir)) { + ret = PTR_ERR(dir); + goto free; + } + +- /* +- * The first zone contains the super block: skip it. +- */ +- end = zd->zones + bdev_nr_zones(sb->s_bdev); +- for (zone = &zd->zones[1]; zone < end; zone = next) { +- +- next = zone + 1; +- if (zonefs_zone_type(zone) != type) +- continue; +- +- /* +- * For conventional zones, contiguous zones can be aggregated +- * together to form larger files. Note that this overwrites the +- * length of the first zone of the set of contiguous zones +- * aggregated together. If one offline or read-only zone is +- * found, assume that all zones aggregated have the same +- * condition. +- */ +- if (type == ZONEFS_ZTYPE_CNV && +- (sbi->s_features & ZONEFS_F_AGGRCNV)) { +- for (; next < end; next++) { +- if (zonefs_zone_type(next) != type) +- break; +- zone->len += next->len; +- zone->capacity += next->capacity; +- if (next->cond == BLK_ZONE_COND_READONLY && +- zone->cond != BLK_ZONE_COND_OFFLINE) +- zone->cond = BLK_ZONE_COND_READONLY; +- else if (next->cond == BLK_ZONE_COND_OFFLINE) +- zone->cond = BLK_ZONE_COND_OFFLINE; +- } +- if (zone->capacity != zone->len) { +- zonefs_err(sb, "Invalid conventional zone capacity\n"); +- ret = -EINVAL; +- goto free; +- } +- } +- +- /* +- * Use the file number within its group as file name. +- */ +- snprintf(file_name, ZONEFS_NAME_MAX - 1, "%u", n); +- dent = zonefs_create_inode(dir, file_name, zone, type); ++ for (i = 0; i < zgroup->g_nr_zones; i++) { ++ /* Use the zone number within its group as the file name */ ++ snprintf(file_name, ZONEFS_NAME_MAX - 1, "%u", i); ++ dent = zonefs_create_inode(dir, file_name, ++ &zgroup->g_zones[i], ztype); + if (IS_ERR(dent)) { + ret = PTR_ERR(dent); +- goto free; ++ break; + } +- +- n++; + } + +- zonefs_info(sb, "Zone group \"%s\" has %u file%s\n", +- zgroup_name, n, n > 1 ? "s" : ""); +- +- sbi->s_nr_files[type] = n; +- ret = 0; +- + free: + kfree(file_name); + +@@ -1644,21 +720,38 @@ static int zonefs_get_zone_info_cb(struct blk_zone *zone, unsigned int idx, + void *data) + { + struct zonefs_zone_data *zd = data; ++ struct super_block *sb = zd->sb; ++ struct zonefs_sb_info *sbi = ZONEFS_SB(sb); ++ ++ /* ++ * We do not care about the first zone: it contains the super block ++ * and not exposed as a file. ++ */ ++ if (!idx) ++ return 0; + + /* +- * Count the number of usable zones: the first zone at index 0 contains +- * the super block and is ignored. ++ * Count the number of zones that will be exposed as files. ++ * For sequential zones, we always have as many files as zones. ++ * FOr conventional zones, the number of files depends on if we have ++ * conventional zones aggregation enabled. + */ + switch (zone->type) { + case BLK_ZONE_TYPE_CONVENTIONAL: +- zone->wp = zone->start + zone->len; +- if (idx) +- zd->nr_zones[ZONEFS_ZTYPE_CNV]++; ++ if (sbi->s_features & ZONEFS_F_AGGRCNV) { ++ /* One file per set of contiguous conventional zones */ ++ if (!(sbi->s_zgroup[ZONEFS_ZTYPE_CNV].g_nr_zones) || ++ zone->start != zd->cnv_zone_start) ++ sbi->s_zgroup[ZONEFS_ZTYPE_CNV].g_nr_zones++; ++ zd->cnv_zone_start = zone->start + zone->len; ++ } else { ++ /* One file per zone */ ++ sbi->s_zgroup[ZONEFS_ZTYPE_CNV].g_nr_zones++; ++ } + break; + case BLK_ZONE_TYPE_SEQWRITE_REQ: + case BLK_ZONE_TYPE_SEQWRITE_PREF: +- if (idx) +- zd->nr_zones[ZONEFS_ZTYPE_SEQ]++; ++ sbi->s_zgroup[ZONEFS_ZTYPE_SEQ].g_nr_zones++; + break; + default: + zonefs_err(zd->sb, "Unsupported zone type 0x%x\n", +@@ -1698,11 +791,173 @@ static int zonefs_get_zone_info(struct zonefs_zone_data *zd) + return 0; + } + +-static inline void zonefs_cleanup_zone_info(struct zonefs_zone_data *zd) ++static inline void zonefs_free_zone_info(struct zonefs_zone_data *zd) + { + kvfree(zd->zones); + } + ++/* ++ * Create a zone group and populate it with zone files. ++ */ ++static int zonefs_init_zgroup(struct super_block *sb, ++ struct zonefs_zone_data *zd, ++ enum zonefs_ztype ztype) ++{ ++ struct zonefs_sb_info *sbi = ZONEFS_SB(sb); ++ struct zonefs_zone_group *zgroup = &sbi->s_zgroup[ztype]; ++ struct blk_zone *zone, *next, *end; ++ struct zonefs_zone *z; ++ unsigned int n = 0; ++ int ret; ++ ++ /* Allocate the zone group. If it is empty, we have nothing to do. */ ++ if (!zgroup->g_nr_zones) ++ return 0; ++ ++ zgroup->g_zones = kvcalloc(zgroup->g_nr_zones, ++ sizeof(struct zonefs_zone), GFP_KERNEL); ++ if (!zgroup->g_zones) ++ return -ENOMEM; ++ ++ /* ++ * Initialize the zone groups using the device zone information. ++ * We always skip the first zone as it contains the super block ++ * and is not use to back a file. ++ */ ++ end = zd->zones + bdev_nr_zones(sb->s_bdev); ++ for (zone = &zd->zones[1]; zone < end; zone = next) { ++ ++ next = zone + 1; ++ if (zonefs_zone_type(zone) != ztype) ++ continue; ++ ++ if (WARN_ON_ONCE(n >= zgroup->g_nr_zones)) ++ return -EINVAL; ++ ++ /* ++ * For conventional zones, contiguous zones can be aggregated ++ * together to form larger files. Note that this overwrites the ++ * length of the first zone of the set of contiguous zones ++ * aggregated together. If one offline or read-only zone is ++ * found, assume that all zones aggregated have the same ++ * condition. ++ */ ++ if (ztype == ZONEFS_ZTYPE_CNV && ++ (sbi->s_features & ZONEFS_F_AGGRCNV)) { ++ for (; next < end; next++) { ++ if (zonefs_zone_type(next) != ztype) ++ break; ++ zone->len += next->len; ++ zone->capacity += next->capacity; ++ if (next->cond == BLK_ZONE_COND_READONLY && ++ zone->cond != BLK_ZONE_COND_OFFLINE) ++ zone->cond = BLK_ZONE_COND_READONLY; ++ else if (next->cond == BLK_ZONE_COND_OFFLINE) ++ zone->cond = BLK_ZONE_COND_OFFLINE; ++ } ++ } ++ ++ z = &zgroup->g_zones[n]; ++ if (ztype == ZONEFS_ZTYPE_CNV) ++ z->z_flags |= ZONEFS_ZONE_CNV; ++ z->z_sector = zone->start; ++ z->z_size = zone->len << SECTOR_SHIFT; ++ if (z->z_size > bdev_zone_sectors(sb->s_bdev) << SECTOR_SHIFT && ++ !(sbi->s_features & ZONEFS_F_AGGRCNV)) { ++ zonefs_err(sb, ++ "Invalid zone size %llu (device zone sectors %llu)\n", ++ z->z_size, ++ bdev_zone_sectors(sb->s_bdev) << SECTOR_SHIFT); ++ return -EINVAL; ++ } ++ ++ z->z_capacity = min_t(loff_t, MAX_LFS_FILESIZE, ++ zone->capacity << SECTOR_SHIFT); ++ z->z_wpoffset = zonefs_check_zone_condition(sb, z, zone); ++ ++ sb->s_maxbytes = max(z->z_capacity, sb->s_maxbytes); ++ sbi->s_blocks += z->z_capacity >> sb->s_blocksize_bits; ++ sbi->s_used_blocks += z->z_wpoffset >> sb->s_blocksize_bits; ++ ++ /* ++ * For sequential zones, make sure that any open zone is closed ++ * first to ensure that the initial number of open zones is 0, ++ * in sync with the open zone accounting done when the mount ++ * option ZONEFS_MNTOPT_EXPLICIT_OPEN is used. ++ */ ++ if (ztype == ZONEFS_ZTYPE_SEQ && ++ (zone->cond == BLK_ZONE_COND_IMP_OPEN || ++ zone->cond == BLK_ZONE_COND_EXP_OPEN)) { ++ ret = zonefs_zone_mgmt(sb, z, REQ_OP_ZONE_CLOSE); ++ if (ret) ++ return ret; ++ } ++ ++ zonefs_account_active(sb, z); ++ ++ n++; ++ } ++ ++ if (WARN_ON_ONCE(n != zgroup->g_nr_zones)) ++ return -EINVAL; ++ ++ zonefs_info(sb, "Zone group \"%s\" has %u file%s\n", ++ zonefs_zgroup_name(ztype), ++ zgroup->g_nr_zones, ++ zgroup->g_nr_zones > 1 ? "s" : ""); ++ ++ return 0; ++} ++ ++static void zonefs_free_zgroups(struct super_block *sb) ++{ ++ struct zonefs_sb_info *sbi = ZONEFS_SB(sb); ++ enum zonefs_ztype ztype; ++ ++ if (!sbi) ++ return; ++ ++ for (ztype = 0; ztype < ZONEFS_ZTYPE_MAX; ztype++) { ++ kvfree(sbi->s_zgroup[ztype].g_zones); ++ sbi->s_zgroup[ztype].g_zones = NULL; ++ } ++} ++ ++/* ++ * Create a zone group and populate it with zone files. ++ */ ++static int zonefs_init_zgroups(struct super_block *sb) ++{ ++ struct zonefs_zone_data zd; ++ enum zonefs_ztype ztype; ++ int ret; ++ ++ /* First get the device zone information */ ++ memset(&zd, 0, sizeof(struct zonefs_zone_data)); ++ zd.sb = sb; ++ ret = zonefs_get_zone_info(&zd); ++ if (ret) ++ goto cleanup; ++ ++ /* Allocate and initialize the zone groups */ ++ for (ztype = 0; ztype < ZONEFS_ZTYPE_MAX; ztype++) { ++ ret = zonefs_init_zgroup(sb, &zd, ztype); ++ if (ret) { ++ zonefs_info(sb, ++ "Zone group \"%s\" initialization failed\n", ++ zonefs_zgroup_name(ztype)); ++ break; ++ } ++ } ++ ++cleanup: ++ zonefs_free_zone_info(&zd); ++ if (ret) ++ zonefs_free_zgroups(sb); ++ ++ return ret; ++} ++ + /* + * Read super block information from the device. + */ +@@ -1785,6 +1040,14 @@ free_page: + return ret; + } + ++static const struct super_operations zonefs_sops = { ++ .alloc_inode = zonefs_alloc_inode, ++ .free_inode = zonefs_free_inode, ++ .statfs = zonefs_statfs, ++ .remount_fs = zonefs_remount, ++ .show_options = zonefs_show_options, ++}; ++ + /* + * Check that the device is zoned. If it is, get the list of zones and create + * sub-directories and files according to the device zone configuration and +@@ -1792,7 +1055,6 @@ free_page: + */ + static int zonefs_fill_super(struct super_block *sb, void *data, int silent) + { +- struct zonefs_zone_data zd; + struct zonefs_sb_info *sbi; + struct inode *inode; + enum zonefs_ztype t; +@@ -1845,16 +1107,6 @@ static int zonefs_fill_super(struct super_block *sb, void *data, int silent) + if (ret) + return ret; + +- memset(&zd, 0, sizeof(struct zonefs_zone_data)); +- zd.sb = sb; +- ret = zonefs_get_zone_info(&zd); +- if (ret) +- goto cleanup; +- +- ret = zonefs_sysfs_register(sb); +- if (ret) +- goto cleanup; +- + zonefs_info(sb, "Mounting %u zones", bdev_nr_zones(sb->s_bdev)); + + if (!sbi->s_max_wro_seq_files && +@@ -1865,6 +1117,11 @@ static int zonefs_fill_super(struct super_block *sb, void *data, int silent) + sbi->s_mount_opts &= ~ZONEFS_MNTOPT_EXPLICIT_OPEN; + } + ++ /* Initialize the zone groups */ ++ ret = zonefs_init_zgroups(sb); ++ if (ret) ++ goto cleanup; ++ + /* Create root directory inode */ + ret = -ENOMEM; + inode = new_inode(sb); +@@ -1884,13 +1141,19 @@ static int zonefs_fill_super(struct super_block *sb, void *data, int silent) + + /* Create and populate files in zone groups directories */ + for (t = 0; t < ZONEFS_ZTYPE_MAX; t++) { +- ret = zonefs_create_zgroup(&zd, t); ++ ret = zonefs_create_zgroup_inodes(sb, t); + if (ret) +- break; ++ goto cleanup; + } + ++ ret = zonefs_sysfs_register(sb); ++ if (ret) ++ goto cleanup; ++ ++ return 0; ++ + cleanup: +- zonefs_cleanup_zone_info(&zd); ++ zonefs_free_zgroups(sb); + + return ret; + } +@@ -1909,6 +1172,7 @@ static void zonefs_kill_super(struct super_block *sb) + d_genocide(sb->s_root); + + zonefs_sysfs_unregister(sb); ++ zonefs_free_zgroups(sb); + kill_block_super(sb); + kfree(sbi); + } +diff --git a/fs/zonefs/trace.h b/fs/zonefs/trace.h +index 42edcfd393ed2..9969db3a9c7dc 100644 +--- a/fs/zonefs/trace.h ++++ b/fs/zonefs/trace.h +@@ -20,8 +20,9 @@ + #define show_dev(dev) MAJOR(dev), MINOR(dev) + + TRACE_EVENT(zonefs_zone_mgmt, +- TP_PROTO(struct inode *inode, enum req_op op), +- TP_ARGS(inode, op), ++ TP_PROTO(struct super_block *sb, struct zonefs_zone *z, ++ enum req_op op), ++ TP_ARGS(sb, z, op), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) +@@ -30,12 +31,12 @@ TRACE_EVENT(zonefs_zone_mgmt, + __field(sector_t, nr_sectors) + ), + TP_fast_assign( +- __entry->dev = inode->i_sb->s_dev; +- __entry->ino = inode->i_ino; ++ __entry->dev = sb->s_dev; ++ __entry->ino = ++ z->z_sector >> ZONEFS_SB(sb)->s_zone_sectors_shift; + __entry->op = op; +- __entry->sector = ZONEFS_I(inode)->i_zsector; +- __entry->nr_sectors = +- ZONEFS_I(inode)->i_zone_size >> SECTOR_SHIFT; ++ __entry->sector = z->z_sector; ++ __entry->nr_sectors = z->z_size >> SECTOR_SHIFT; + ), + TP_printk("bdev=(%d,%d), ino=%lu op=%s, sector=%llu, nr_sectors=%llu", + show_dev(__entry->dev), (unsigned long)__entry->ino, +@@ -58,9 +59,10 @@ TRACE_EVENT(zonefs_file_dio_append, + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; +- __entry->sector = ZONEFS_I(inode)->i_zsector; ++ __entry->sector = zonefs_inode_zone(inode)->z_sector; + __entry->size = size; +- __entry->wpoffset = ZONEFS_I(inode)->i_wpoffset; ++ __entry->wpoffset = ++ zonefs_inode_zone(inode)->z_wpoffset; + __entry->ret = ret; + ), + TP_printk("bdev=(%d, %d), ino=%lu, sector=%llu, size=%zu, wpoffset=%llu, ret=%zu", +diff --git a/fs/zonefs/zonefs.h b/fs/zonefs/zonefs.h +index 1dbe78119ff16..2d626e18b1411 100644 +--- a/fs/zonefs/zonefs.h ++++ b/fs/zonefs/zonefs.h +@@ -39,31 +39,47 @@ static inline enum zonefs_ztype zonefs_zone_type(struct blk_zone *zone) + return ZONEFS_ZTYPE_SEQ; + } + +-#define ZONEFS_ZONE_OPEN (1U << 0) +-#define ZONEFS_ZONE_ACTIVE (1U << 1) +-#define ZONEFS_ZONE_OFFLINE (1U << 2) +-#define ZONEFS_ZONE_READONLY (1U << 3) ++#define ZONEFS_ZONE_INIT_MODE (1U << 0) ++#define ZONEFS_ZONE_OPEN (1U << 1) ++#define ZONEFS_ZONE_ACTIVE (1U << 2) ++#define ZONEFS_ZONE_OFFLINE (1U << 3) ++#define ZONEFS_ZONE_READONLY (1U << 4) ++#define ZONEFS_ZONE_CNV (1U << 31) + + /* +- * In-memory inode data. ++ * In-memory per-file inode zone data. + */ +-struct zonefs_inode_info { +- struct inode i_vnode; ++struct zonefs_zone { ++ /* Zone state flags */ ++ unsigned int z_flags; + +- /* File zone type */ +- enum zonefs_ztype i_ztype; ++ /* Zone start sector (512B unit) */ ++ sector_t z_sector; + +- /* File zone start sector (512B unit) */ +- sector_t i_zsector; ++ /* Zone size (bytes) */ ++ loff_t z_size; + +- /* File zone write pointer position (sequential zones only) */ +- loff_t i_wpoffset; ++ /* Zone capacity (file maximum size, bytes) */ ++ loff_t z_capacity; + +- /* File maximum size */ +- loff_t i_max_size; ++ /* Write pointer offset in the zone (sequential zones only, bytes) */ ++ loff_t z_wpoffset; ++}; + +- /* File zone size */ +- loff_t i_zone_size; ++/* ++ * In memory zone group information: all zones of a group are exposed ++ * as files, one file per zone. ++ */ ++struct zonefs_zone_group { ++ unsigned int g_nr_zones; ++ struct zonefs_zone *g_zones; ++}; ++ ++/* ++ * In-memory inode data. ++ */ ++struct zonefs_inode_info { ++ struct inode i_vnode; + + /* + * To serialise fully against both syscall and mmap based IO and +@@ -82,7 +98,6 @@ struct zonefs_inode_info { + + /* guarded by i_truncate_mutex */ + unsigned int i_wr_refcnt; +- unsigned int i_flags; + }; + + static inline struct zonefs_inode_info *ZONEFS_I(struct inode *inode) +@@ -90,6 +105,31 @@ static inline struct zonefs_inode_info *ZONEFS_I(struct inode *inode) + return container_of(inode, struct zonefs_inode_info, i_vnode); + } + ++static inline bool zonefs_zone_is_cnv(struct zonefs_zone *z) ++{ ++ return z->z_flags & ZONEFS_ZONE_CNV; ++} ++ ++static inline bool zonefs_zone_is_seq(struct zonefs_zone *z) ++{ ++ return !zonefs_zone_is_cnv(z); ++} ++ ++static inline struct zonefs_zone *zonefs_inode_zone(struct inode *inode) ++{ ++ return inode->i_private; ++} ++ ++static inline bool zonefs_inode_is_cnv(struct inode *inode) ++{ ++ return zonefs_zone_is_cnv(zonefs_inode_zone(inode)); ++} ++ ++static inline bool zonefs_inode_is_seq(struct inode *inode) ++{ ++ return zonefs_zone_is_seq(zonefs_inode_zone(inode)); ++} ++ + /* + * On-disk super block (block 0). + */ +@@ -181,7 +221,7 @@ struct zonefs_sb_info { + uuid_t s_uuid; + unsigned int s_zone_sectors_shift; + +- unsigned int s_nr_files[ZONEFS_ZTYPE_MAX]; ++ struct zonefs_zone_group s_zgroup[ZONEFS_ZTYPE_MAX]; + + loff_t s_blocks; + loff_t s_used_blocks; +@@ -209,6 +249,28 @@ static inline struct zonefs_sb_info *ZONEFS_SB(struct super_block *sb) + #define zonefs_warn(sb, format, args...) \ + pr_warn("zonefs (%s) WARNING: " format, sb->s_id, ## args) + ++/* In super.c */ ++void zonefs_inode_account_active(struct inode *inode); ++int zonefs_inode_zone_mgmt(struct inode *inode, enum req_op op); ++void zonefs_i_size_write(struct inode *inode, loff_t isize); ++void zonefs_update_stats(struct inode *inode, loff_t new_isize); ++void __zonefs_io_error(struct inode *inode, bool write); ++ ++static inline void zonefs_io_error(struct inode *inode, bool write) ++{ ++ struct zonefs_inode_info *zi = ZONEFS_I(inode); ++ ++ mutex_lock(&zi->i_truncate_mutex); ++ __zonefs_io_error(inode, write); ++ mutex_unlock(&zi->i_truncate_mutex); ++} ++ ++/* In file.c */ ++extern const struct address_space_operations zonefs_file_aops; ++extern const struct file_operations zonefs_file_operations; ++int zonefs_file_truncate(struct inode *inode, loff_t isize); ++ ++/* In sysfs.c */ + int zonefs_sysfs_register(struct super_block *sb); + void zonefs_sysfs_unregister(struct super_block *sb); + int zonefs_sysfs_init(void); +diff --git a/include/linux/io_uring.h b/include/linux/io_uring.h +index 0ded9e271523f..a1484cdb3158e 100644 +--- a/include/linux/io_uring.h ++++ b/include/linux/io_uring.h +@@ -26,7 +26,7 @@ struct io_uring_cmd { + const void *cmd; + union { + /* callback to defer completions to task context */ +- void (*task_work_cb)(struct io_uring_cmd *cmd); ++ void (*task_work_cb)(struct io_uring_cmd *cmd, unsigned); + /* used for polled completion */ + void *cookie; + }; +@@ -38,9 +38,10 @@ struct io_uring_cmd { + #if defined(CONFIG_IO_URING) + int io_uring_cmd_import_fixed(u64 ubuf, unsigned long len, int rw, + struct iov_iter *iter, void *ioucmd); +-void io_uring_cmd_done(struct io_uring_cmd *cmd, ssize_t ret, ssize_t res2); ++void io_uring_cmd_done(struct io_uring_cmd *cmd, ssize_t ret, ssize_t res2, ++ unsigned issue_flags); + void io_uring_cmd_complete_in_task(struct io_uring_cmd *ioucmd, +- void (*task_work_cb)(struct io_uring_cmd *)); ++ void (*task_work_cb)(struct io_uring_cmd *, unsigned)); + struct sock *io_uring_get_socket(struct file *file); + void __io_uring_cancel(bool cancel_all); + void __io_uring_free(struct task_struct *tsk); +@@ -71,11 +72,11 @@ static inline int io_uring_cmd_import_fixed(u64 ubuf, unsigned long len, int rw, + return -EOPNOTSUPP; + } + static inline void io_uring_cmd_done(struct io_uring_cmd *cmd, ssize_t ret, +- ssize_t ret2) ++ ssize_t ret2, unsigned issue_flags) + { + } + static inline void io_uring_cmd_complete_in_task(struct io_uring_cmd *ioucmd, +- void (*task_work_cb)(struct io_uring_cmd *)) ++ void (*task_work_cb)(struct io_uring_cmd *, unsigned)) + { + } + static inline struct sock *io_uring_get_socket(struct file *file) +diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h +index 90b2fb0292cb1..012fa0d171b27 100644 +--- a/include/trace/events/rcu.h ++++ b/include/trace/events/rcu.h +@@ -768,7 +768,7 @@ TRACE_EVENT_RCU(rcu_torture_read, + TP_ARGS(rcutorturename, rhp, secs, c_old, c), + + TP_STRUCT__entry( +- __field(char, rcutorturename[RCUTORTURENAME_LEN]) ++ __array(char, rcutorturename, RCUTORTURENAME_LEN) + __field(struct rcu_head *, rhp) + __field(unsigned long, secs) + __field(unsigned long, c_old) +diff --git a/include/xen/interface/platform.h b/include/xen/interface/platform.h +index 655d92e803e14..79a443c65ea93 100644 +--- a/include/xen/interface/platform.h ++++ b/include/xen/interface/platform.h +@@ -483,6 +483,8 @@ struct xenpf_symdata { + }; + DEFINE_GUEST_HANDLE_STRUCT(xenpf_symdata); + ++#define XENPF_get_dom0_console 64 ++ + struct xen_platform_op { + uint32_t cmd; + uint32_t interface_version; /* XENPF_INTERFACE_VERSION */ +@@ -506,6 +508,7 @@ struct xen_platform_op { + struct xenpf_mem_hotadd mem_add; + struct xenpf_core_parking core_parking; + struct xenpf_symdata symdata; ++ struct dom0_vga_console_info dom0_console; + uint8_t pad[128]; + } u; + }; +diff --git a/io_uring/alloc_cache.h b/io_uring/alloc_cache.h +index 729793ae97127..c2cde88aeed53 100644 +--- a/io_uring/alloc_cache.h ++++ b/io_uring/alloc_cache.h +@@ -27,6 +27,7 @@ static inline struct io_cache_entry *io_alloc_cache_get(struct io_alloc_cache *c + struct hlist_node *node = cache->list.first; + + hlist_del(node); ++ cache->nr_cached--; + return container_of(node, struct io_cache_entry, node); + } + +diff --git a/io_uring/poll.c b/io_uring/poll.c +index 56dbd1863c785..4788073ec45d2 100644 +--- a/io_uring/poll.c ++++ b/io_uring/poll.c +@@ -742,6 +742,7 @@ int io_arm_poll_handler(struct io_kiocb *req, unsigned issue_flags) + apoll = io_req_alloc_apoll(req, issue_flags); + if (!apoll) + return IO_APOLL_ABORTED; ++ req->flags &= ~(REQ_F_SINGLE_POLL | REQ_F_DOUBLE_POLL); + req->flags |= REQ_F_POLLED; + ipt.pt._qproc = io_async_queue_proc; + +diff --git a/io_uring/rsrc.h b/io_uring/rsrc.h +index 81445a477622b..d60c758326b42 100644 +--- a/io_uring/rsrc.h ++++ b/io_uring/rsrc.h +@@ -143,15 +143,13 @@ static inline void io_req_set_rsrc_node(struct io_kiocb *req, + unsigned int issue_flags) + { + if (!req->rsrc_node) { +- req->rsrc_node = ctx->rsrc_node; ++ io_ring_submit_lock(ctx, issue_flags); + +- if (!(issue_flags & IO_URING_F_UNLOCKED)) { +- lockdep_assert_held(&ctx->uring_lock); ++ lockdep_assert_held(&ctx->uring_lock); + +- io_charge_rsrc_node(ctx); +- } else { +- percpu_ref_get(&req->rsrc_node->refs); +- } ++ req->rsrc_node = ctx->rsrc_node; ++ io_charge_rsrc_node(ctx); ++ io_ring_submit_unlock(ctx, issue_flags); + } + } + +diff --git a/io_uring/uring_cmd.c b/io_uring/uring_cmd.c +index 18dfc5f6a8b72..92f310dfb9fd6 100644 +--- a/io_uring/uring_cmd.c ++++ b/io_uring/uring_cmd.c +@@ -15,12 +15,13 @@ + static void io_uring_cmd_work(struct io_kiocb *req, bool *locked) + { + struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd); ++ unsigned issue_flags = *locked ? 0 : IO_URING_F_UNLOCKED; + +- ioucmd->task_work_cb(ioucmd); ++ ioucmd->task_work_cb(ioucmd, issue_flags); + } + + void io_uring_cmd_complete_in_task(struct io_uring_cmd *ioucmd, +- void (*task_work_cb)(struct io_uring_cmd *)) ++ void (*task_work_cb)(struct io_uring_cmd *, unsigned)) + { + struct io_kiocb *req = cmd_to_io_kiocb(ioucmd); + +@@ -42,7 +43,8 @@ static inline void io_req_set_cqe32_extra(struct io_kiocb *req, + * Called by consumers of io_uring_cmd, if they originally returned + * -EIOCBQUEUED upon receiving the command. + */ +-void io_uring_cmd_done(struct io_uring_cmd *ioucmd, ssize_t ret, ssize_t res2) ++void io_uring_cmd_done(struct io_uring_cmd *ioucmd, ssize_t ret, ssize_t res2, ++ unsigned issue_flags) + { + struct io_kiocb *req = cmd_to_io_kiocb(ioucmd); + +@@ -56,7 +58,7 @@ void io_uring_cmd_done(struct io_uring_cmd *ioucmd, ssize_t ret, ssize_t res2) + /* order with io_iopoll_req_issued() checking ->iopoll_complete */ + smp_store_release(&req->iopoll_completed, 1); + else +- __io_req_complete(req, 0); ++ __io_req_complete(req, issue_flags); + } + EXPORT_SYMBOL_GPL(io_uring_cmd_done); + +diff --git a/kernel/compat.c b/kernel/compat.c +index 55551989d9da5..fb50f29d9b361 100644 +--- a/kernel/compat.c ++++ b/kernel/compat.c +@@ -152,7 +152,7 @@ COMPAT_SYSCALL_DEFINE3(sched_getaffinity, compat_pid_t, pid, unsigned int, len, + if (len & (sizeof(compat_ulong_t)-1)) + return -EINVAL; + +- if (!alloc_cpumask_var(&mask, GFP_KERNEL)) ++ if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) + return -ENOMEM; + + ret = sched_getaffinity(pid, mask); +diff --git a/kernel/kcsan/Makefile b/kernel/kcsan/Makefile +index 4f35d1bced6a2..a45f3dfc8d141 100644 +--- a/kernel/kcsan/Makefile ++++ b/kernel/kcsan/Makefile +@@ -16,5 +16,6 @@ obj-y := core.o debugfs.o report.o + KCSAN_INSTRUMENT_BARRIERS_selftest.o := y + obj-$(CONFIG_KCSAN_SELFTEST) += selftest.o + +-CFLAGS_kcsan_test.o := $(CFLAGS_KCSAN) -g -fno-omit-frame-pointer ++CFLAGS_kcsan_test.o := $(CFLAGS_KCSAN) -fno-omit-frame-pointer ++CFLAGS_kcsan_test.o += $(DISABLE_STRUCTLEAK_PLUGIN) + obj-$(CONFIG_KCSAN_KUNIT_TEST) += kcsan_test.o +diff --git a/kernel/sched/core.c b/kernel/sched/core.c +index 9ebfd484189b3..b23dcbeacdf33 100644 +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -8304,14 +8304,14 @@ SYSCALL_DEFINE3(sched_getaffinity, pid_t, pid, unsigned int, len, + if (len & (sizeof(unsigned long)-1)) + return -EINVAL; + +- if (!alloc_cpumask_var(&mask, GFP_KERNEL)) ++ if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) + return -ENOMEM; + + ret = sched_getaffinity(pid, mask); + if (ret == 0) { + unsigned int retlen = min(len, cpumask_size()); + +- if (copy_to_user(user_mask_ptr, mask, retlen)) ++ if (copy_to_user(user_mask_ptr, cpumask_bits(mask), retlen)) + ret = -EFAULT; + else + ret = retlen; +diff --git a/kernel/trace/kprobe_event_gen_test.c b/kernel/trace/kprobe_event_gen_test.c +index c736487fc0e48..e0c420eb0b2b4 100644 +--- a/kernel/trace/kprobe_event_gen_test.c ++++ b/kernel/trace/kprobe_event_gen_test.c +@@ -146,7 +146,7 @@ static int __init test_gen_kprobe_cmd(void) + if (trace_event_file_is_valid(gen_kprobe_test)) + gen_kprobe_test = NULL; + /* We got an error after creating the event, delete it */ +- ret = kprobe_event_delete("gen_kprobe_test"); ++ kprobe_event_delete("gen_kprobe_test"); + goto out; + } + +@@ -211,7 +211,7 @@ static int __init test_gen_kretprobe_cmd(void) + if (trace_event_file_is_valid(gen_kretprobe_test)) + gen_kretprobe_test = NULL; + /* We got an error after creating the event, delete it */ +- ret = kprobe_event_delete("gen_kretprobe_test"); ++ kprobe_event_delete("gen_kretprobe_test"); + goto out; + } + +diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c +index 888980257340f..78d69b9488e45 100644 +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -5727,7 +5727,9 @@ static const char readme_msg[] = + "\t .syscall display a syscall id as a syscall name\n" + "\t .log2 display log2 value rather than raw number\n" + "\t .buckets=size display values in groups of size rather than raw number\n" +- "\t .usecs display a common_timestamp in microseconds\n\n" ++ "\t .usecs display a common_timestamp in microseconds\n" ++ "\t .percent display a number of percentage value\n" ++ "\t .graph display a bar-graph of a value\n\n" + "\t The 'pause' parameter can be used to pause an existing hist\n" + "\t trigger or to start a hist trigger but not log any events\n" + "\t until told to do so. 'continue' can be used to start or\n" +diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c +index e3df03cdecbcb..2b2120ed2460f 100644 +--- a/kernel/trace/trace_events_hist.c ++++ b/kernel/trace/trace_events_hist.c +@@ -506,6 +506,8 @@ enum hist_field_flags { + HIST_FIELD_FL_ALIAS = 1 << 16, + HIST_FIELD_FL_BUCKET = 1 << 17, + HIST_FIELD_FL_CONST = 1 << 18, ++ HIST_FIELD_FL_PERCENT = 1 << 19, ++ HIST_FIELD_FL_GRAPH = 1 << 20, + }; + + struct var_defs { +@@ -1708,6 +1710,10 @@ static const char *get_hist_field_flags(struct hist_field *hist_field) + flags_str = "buckets"; + else if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP_USECS) + flags_str = "usecs"; ++ else if (hist_field->flags & HIST_FIELD_FL_PERCENT) ++ flags_str = "percent"; ++ else if (hist_field->flags & HIST_FIELD_FL_GRAPH) ++ flags_str = "graph"; + + return flags_str; + } +@@ -2320,6 +2326,14 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file, + if (ret || !(*buckets)) + goto error; + *flags |= HIST_FIELD_FL_BUCKET; ++ } else if (strncmp(modifier, "percent", 7) == 0) { ++ if (*flags & (HIST_FIELD_FL_VAR | HIST_FIELD_FL_KEY)) ++ goto error; ++ *flags |= HIST_FIELD_FL_PERCENT; ++ } else if (strncmp(modifier, "graph", 5) == 0) { ++ if (*flags & (HIST_FIELD_FL_VAR | HIST_FIELD_FL_KEY)) ++ goto error; ++ *flags |= HIST_FIELD_FL_GRAPH; + } else { + error: + hist_err(tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(modifier)); +@@ -4179,6 +4193,15 @@ static int __create_val_field(struct hist_trigger_data *hist_data, + goto out; + } + ++ /* Some types cannot be a value */ ++ if (hist_field->flags & (HIST_FIELD_FL_GRAPH | HIST_FIELD_FL_PERCENT | ++ HIST_FIELD_FL_BUCKET | HIST_FIELD_FL_LOG2 | ++ HIST_FIELD_FL_SYM | HIST_FIELD_FL_SYM_OFFSET | ++ HIST_FIELD_FL_SYSCALL | HIST_FIELD_FL_STACKTRACE)) { ++ hist_err(file->tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(field_str)); ++ ret = -EINVAL; ++ } ++ + hist_data->fields[val_idx] = hist_field; + + ++hist_data->n_vals; +@@ -5297,33 +5320,101 @@ static void hist_trigger_print_key(struct seq_file *m, + seq_puts(m, "}"); + } + ++/* Get the 100 times of the percentage of @val in @total */ ++static inline unsigned int __get_percentage(u64 val, u64 total) ++{ ++ if (!total) ++ goto div0; ++ ++ if (val < (U64_MAX / 10000)) ++ return (unsigned int)div64_ul(val * 10000, total); ++ ++ total = div64_u64(total, 10000); ++ if (!total) ++ goto div0; ++ ++ return (unsigned int)div64_ul(val, total); ++div0: ++ return val ? UINT_MAX : 0; ++} ++ ++#define BAR_CHAR '#' ++ ++static inline const char *__fill_bar_str(char *buf, int size, u64 val, u64 max) ++{ ++ unsigned int len = __get_percentage(val, max); ++ int i; ++ ++ if (len == UINT_MAX) { ++ snprintf(buf, size, "[ERROR]"); ++ return buf; ++ } ++ ++ len = len * size / 10000; ++ for (i = 0; i < len && i < size; i++) ++ buf[i] = BAR_CHAR; ++ while (i < size) ++ buf[i++] = ' '; ++ buf[size] = '\0'; ++ ++ return buf; ++} ++ ++struct hist_val_stat { ++ u64 max; ++ u64 total; ++}; ++ ++static void hist_trigger_print_val(struct seq_file *m, unsigned int idx, ++ const char *field_name, unsigned long flags, ++ struct hist_val_stat *stats, ++ struct tracing_map_elt *elt) ++{ ++ u64 val = tracing_map_read_sum(elt, idx); ++ unsigned int pc; ++ char bar[21]; ++ ++ if (flags & HIST_FIELD_FL_PERCENT) { ++ pc = __get_percentage(val, stats[idx].total); ++ if (pc == UINT_MAX) ++ seq_printf(m, " %s (%%):[ERROR]", field_name); ++ else ++ seq_printf(m, " %s (%%): %3u.%02u", field_name, ++ pc / 100, pc % 100); ++ } else if (flags & HIST_FIELD_FL_GRAPH) { ++ seq_printf(m, " %s: %20s", field_name, ++ __fill_bar_str(bar, 20, val, stats[idx].max)); ++ } else if (flags & HIST_FIELD_FL_HEX) { ++ seq_printf(m, " %s: %10llx", field_name, val); ++ } else { ++ seq_printf(m, " %s: %10llu", field_name, val); ++ } ++} ++ + static void hist_trigger_entry_print(struct seq_file *m, + struct hist_trigger_data *hist_data, ++ struct hist_val_stat *stats, + void *key, + struct tracing_map_elt *elt) + { + const char *field_name; +- unsigned int i; ++ unsigned int i = HITCOUNT_IDX; ++ unsigned long flags; + + hist_trigger_print_key(m, hist_data, key, elt); + +- seq_printf(m, " hitcount: %10llu", +- tracing_map_read_sum(elt, HITCOUNT_IDX)); ++ /* At first, show the raw hitcount always */ ++ hist_trigger_print_val(m, i, "hitcount", 0, stats, elt); + + for (i = 1; i < hist_data->n_vals; i++) { + field_name = hist_field_name(hist_data->fields[i], 0); ++ flags = hist_data->fields[i]->flags; + +- if (hist_data->fields[i]->flags & HIST_FIELD_FL_VAR || +- hist_data->fields[i]->flags & HIST_FIELD_FL_EXPR) ++ if (flags & HIST_FIELD_FL_VAR || flags & HIST_FIELD_FL_EXPR) + continue; + +- if (hist_data->fields[i]->flags & HIST_FIELD_FL_HEX) { +- seq_printf(m, " %s: %10llx", field_name, +- tracing_map_read_sum(elt, i)); +- } else { +- seq_printf(m, " %s: %10llu", field_name, +- tracing_map_read_sum(elt, i)); +- } ++ seq_puts(m, " "); ++ hist_trigger_print_val(m, i, field_name, flags, stats, elt); + } + + print_actions(m, hist_data, elt); +@@ -5336,7 +5427,9 @@ static int print_entries(struct seq_file *m, + { + struct tracing_map_sort_entry **sort_entries = NULL; + struct tracing_map *map = hist_data->map; +- int i, n_entries; ++ int i, j, n_entries; ++ struct hist_val_stat *stats = NULL; ++ u64 val; + + n_entries = tracing_map_sort_entries(map, hist_data->sort_keys, + hist_data->n_sort_keys, +@@ -5344,11 +5437,34 @@ static int print_entries(struct seq_file *m, + if (n_entries < 0) + return n_entries; + ++ /* Calculate the max and the total for each field if needed. */ ++ for (j = 0; j < hist_data->n_vals; j++) { ++ if (!(hist_data->fields[j]->flags & ++ (HIST_FIELD_FL_PERCENT | HIST_FIELD_FL_GRAPH))) ++ continue; ++ if (!stats) { ++ stats = kcalloc(hist_data->n_vals, sizeof(*stats), ++ GFP_KERNEL); ++ if (!stats) { ++ n_entries = -ENOMEM; ++ goto out; ++ } ++ } ++ for (i = 0; i < n_entries; i++) { ++ val = tracing_map_read_sum(sort_entries[i]->elt, j); ++ stats[j].total += val; ++ if (stats[j].max < val) ++ stats[j].max = val; ++ } ++ } ++ + for (i = 0; i < n_entries; i++) +- hist_trigger_entry_print(m, hist_data, ++ hist_trigger_entry_print(m, hist_data, stats, + sort_entries[i]->key, + sort_entries[i]->elt); + ++ kfree(stats); ++out: + tracing_map_destroy_sort_entries(sort_entries, n_entries); + + return n_entries; +diff --git a/lib/zstd/common/zstd_deps.h b/lib/zstd/common/zstd_deps.h +index 7a5bf44839c9c..f06df065dec01 100644 +--- a/lib/zstd/common/zstd_deps.h ++++ b/lib/zstd/common/zstd_deps.h +@@ -84,7 +84,7 @@ static uint64_t ZSTD_div64(uint64_t dividend, uint32_t divisor) { + + #include <linux/kernel.h> + +-#define assert(x) WARN_ON((x)) ++#define assert(x) WARN_ON(!(x)) + + #endif /* ZSTD_DEPS_ASSERT */ + #endif /* ZSTD_DEPS_NEED_ASSERT */ +diff --git a/net/can/bcm.c b/net/can/bcm.c +index 27706f6ace34a..a962ec2b8ba5b 100644 +--- a/net/can/bcm.c ++++ b/net/can/bcm.c +@@ -941,6 +941,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, + + cf = op->frames + op->cfsiz * i; + err = memcpy_from_msg((u8 *)cf, msg, op->cfsiz); ++ if (err < 0) ++ goto free_op; + + if (op->flags & CAN_FD_FRAME) { + if (cf->len > 64) +@@ -950,12 +952,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, + err = -EINVAL; + } + +- if (err < 0) { +- if (op->frames != &op->sframe) +- kfree(op->frames); +- kfree(op); +- return err; +- } ++ if (err < 0) ++ goto free_op; + + if (msg_head->flags & TX_CP_CAN_ID) { + /* copy can_id into frame */ +@@ -1026,6 +1024,12 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, + bcm_tx_start_timer(op); + + return msg_head->nframes * op->cfsiz + MHSIZ; ++ ++free_op: ++ if (op->frames != &op->sframe) ++ kfree(op->frames); ++ kfree(op); ++ return err; + } + + /* +diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c +index 4177e96170703..848b60c9ef79e 100644 +--- a/net/can/j1939/transport.c ++++ b/net/can/j1939/transport.c +@@ -1124,8 +1124,6 @@ static void __j1939_session_cancel(struct j1939_session *session, + + if (session->sk) + j1939_sk_send_loop_abort(session->sk, session->err); +- else +- j1939_sk_errqueue(session, J1939_ERRQUEUE_RX_ABORT); + } + + static void j1939_session_cancel(struct j1939_session *session, +@@ -1140,6 +1138,9 @@ static void j1939_session_cancel(struct j1939_session *session, + } + + j1939_session_list_unlock(session->priv); ++ ++ if (!session->sk) ++ j1939_sk_errqueue(session, J1939_ERRQUEUE_RX_ABORT); + } + + static enum hrtimer_restart j1939_tp_txtimer(struct hrtimer *hrtimer) +@@ -1253,6 +1254,9 @@ static enum hrtimer_restart j1939_tp_rxtimer(struct hrtimer *hrtimer) + __j1939_session_cancel(session, J1939_XTP_ABORT_TIMEOUT); + } + j1939_session_list_unlock(session->priv); ++ ++ if (!session->sk) ++ j1939_sk_errqueue(session, J1939_ERRQUEUE_RX_ABORT); + } + + j1939_session_put(session); +diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c +index 39a6088080e93..a16f0445023aa 100644 +--- a/net/hsr/hsr_framereg.c ++++ b/net/hsr/hsr_framereg.c +@@ -422,7 +422,7 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb, + node_dst = find_node_by_addr_A(&port->hsr->node_db, + eth_hdr(skb)->h_dest); + if (!node_dst) { +- if (net_ratelimit()) ++ if (port->hsr->prot_version != PRP_V1 && net_ratelimit()) + netdev_err(skb->dev, "%s: Unknown node\n", __func__); + return; + } +diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c +index b3ab6d9d752ea..05aa32696e7c2 100644 +--- a/net/sunrpc/xprtsock.c ++++ b/net/sunrpc/xprtsock.c +@@ -2153,6 +2153,7 @@ static void xs_tcp_shutdown(struct rpc_xprt *xprt) + switch (skst) { + case TCP_FIN_WAIT1: + case TCP_FIN_WAIT2: ++ case TCP_LAST_ACK: + break; + case TCP_ESTABLISHED: + case TCP_CLOSE_WAIT: +diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c +index e73f9efc54c12..83f35ecacf24f 100644 +--- a/net/xfrm/xfrm_user.c ++++ b/net/xfrm/xfrm_user.c +@@ -997,7 +997,9 @@ static int copy_to_user_aead(struct xfrm_algo_aead *aead, struct sk_buff *skb) + return -EMSGSIZE; + + ap = nla_data(nla); +- memcpy(ap, aead, sizeof(*aead)); ++ strscpy_pad(ap->alg_name, aead->alg_name, sizeof(ap->alg_name)); ++ ap->alg_key_len = aead->alg_key_len; ++ ap->alg_icv_len = aead->alg_icv_len; + + if (redact_secret && aead->alg_key_len) + memset(ap->alg_key, 0, (aead->alg_key_len + 7) / 8); +@@ -1017,7 +1019,8 @@ static int copy_to_user_ealg(struct xfrm_algo *ealg, struct sk_buff *skb) + return -EMSGSIZE; + + ap = nla_data(nla); +- memcpy(ap, ealg, sizeof(*ealg)); ++ strscpy_pad(ap->alg_name, ealg->alg_name, sizeof(ap->alg_name)); ++ ap->alg_key_len = ealg->alg_key_len; + + if (redact_secret && ealg->alg_key_len) + memset(ap->alg_key, 0, (ealg->alg_key_len + 7) / 8); +@@ -1028,6 +1031,40 @@ static int copy_to_user_ealg(struct xfrm_algo *ealg, struct sk_buff *skb) + return 0; + } + ++static int copy_to_user_calg(struct xfrm_algo *calg, struct sk_buff *skb) ++{ ++ struct nlattr *nla = nla_reserve(skb, XFRMA_ALG_COMP, sizeof(*calg)); ++ struct xfrm_algo *ap; ++ ++ if (!nla) ++ return -EMSGSIZE; ++ ++ ap = nla_data(nla); ++ strscpy_pad(ap->alg_name, calg->alg_name, sizeof(ap->alg_name)); ++ ap->alg_key_len = 0; ++ ++ return 0; ++} ++ ++static int copy_to_user_encap(struct xfrm_encap_tmpl *ep, struct sk_buff *skb) ++{ ++ struct nlattr *nla = nla_reserve(skb, XFRMA_ENCAP, sizeof(*ep)); ++ struct xfrm_encap_tmpl *uep; ++ ++ if (!nla) ++ return -EMSGSIZE; ++ ++ uep = nla_data(nla); ++ memset(uep, 0, sizeof(*uep)); ++ ++ uep->encap_type = ep->encap_type; ++ uep->encap_sport = ep->encap_sport; ++ uep->encap_dport = ep->encap_dport; ++ uep->encap_oa = ep->encap_oa; ++ ++ return 0; ++} ++ + static int xfrm_smark_put(struct sk_buff *skb, struct xfrm_mark *m) + { + int ret = 0; +@@ -1083,12 +1120,12 @@ static int copy_to_user_state_extra(struct xfrm_state *x, + goto out; + } + if (x->calg) { +- ret = nla_put(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); ++ ret = copy_to_user_calg(x->calg, skb); + if (ret) + goto out; + } + if (x->encap) { +- ret = nla_put(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); ++ ret = copy_to_user_encap(x->encap, skb); + if (ret) + goto out; + } +diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c +index 2c80da0220c32..1dfa80c6b471e 100644 +--- a/scripts/mod/modpost.c ++++ b/scripts/mod/modpost.c +@@ -1722,7 +1722,7 @@ static void extract_crcs_for_object(const char *object, struct module *mod) + if (!isdigit(*p)) + continue; /* skip this line */ + +- crc = strtol(p, &p, 0); ++ crc = strtoul(p, &p, 0); + if (*p != '\n') + continue; /* skip this line */ + +diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c +index 8b6aeb8a78f7d..02fd65993e7e5 100644 +--- a/sound/core/pcm_lib.c ++++ b/sound/core/pcm_lib.c +@@ -2155,6 +2155,8 @@ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, + ret = substream->ops->ack(substream); + if (ret < 0) { + runtime->control->appl_ptr = old_appl_ptr; ++ if (ret == -EPIPE) ++ __snd_pcm_xrun(substream); + return ret; + } + } +diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c +index 27e11b5f70b97..c7d7eff86727f 100644 +--- a/sound/pci/asihpi/hpi6205.c ++++ b/sound/pci/asihpi/hpi6205.c +@@ -430,7 +430,7 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr) + pao = hpi_find_adapter(phm->adapter_index); + } else { + /* subsys messages don't address an adapter */ +- _HPI_6205(NULL, phm, phr); ++ phr->error = HPI_ERROR_INVALID_OBJ_INDEX; + return; + } + +diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c +index acde4cd58785e..099722ebaed83 100644 +--- a/sound/pci/hda/patch_ca0132.c ++++ b/sound/pci/hda/patch_ca0132.c +@@ -4228,8 +4228,10 @@ static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid, + + for (i = 0; i < TUNING_CTLS_COUNT; i++) + if (nid == ca0132_tuning_ctls[i].nid) +- break; ++ goto found; + ++ return -EINVAL; ++found: + snd_hda_power_up(codec); + dspio_set_param(codec, ca0132_tuning_ctls[i].mid, 0x20, + ca0132_tuning_ctls[i].req, +diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c +index 75e1d00074b9f..a889cccdd607c 100644 +--- a/sound/pci/hda/patch_conexant.c ++++ b/sound/pci/hda/patch_conexant.c +@@ -980,7 +980,10 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { + SND_PCI_QUIRK(0x17aa, 0x3905, "Lenovo G50-30", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), +- SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_PINCFG_LENOVO_NOTEBOOK), ++ /* NOTE: we'd need to extend the quirk for 17aa:3977 as the same ++ * PCI SSID is used on multiple Lenovo models ++ */ ++ SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI), +@@ -1003,6 +1006,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = { + { .id = CXT_FIXUP_MUTE_LED_GPIO, .name = "mute-led-gpio" }, + { .id = CXT_FIXUP_HP_ZBOOK_MUTE_LED, .name = "hp-zbook-mute-led" }, + { .id = CXT_FIXUP_HP_MIC_NO_PRESENCE, .name = "hp-mic-fix" }, ++ { .id = CXT_PINCFG_LENOVO_NOTEBOOK, .name = "lenovo-20149" }, + {} + }; + +diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c +index 28ac6c159b2a2..070150bbd3559 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -2631,6 +2631,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { + SND_PCI_QUIRK(0x1558, 0x65e5, "Clevo PC50D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65f1, "Clevo PC50HS", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65f5, "Clevo PD50PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), ++ SND_PCI_QUIRK(0x1558, 0x66a2, "Clevo PE60RNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67e1, "Clevo PB71[DE][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67e5, "Clevo PC70D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS), +@@ -2651,6 +2652,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { + SND_PCI_QUIRK(0x1558, 0x96e1, "Clevo P960[ER][CDFN]-K", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x97e2, "Clevo P970RC-M", ALC1220_FIXUP_CLEVO_P950), ++ SND_PCI_QUIRK(0x1558, 0xd502, "Clevo PD50SNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530), +@@ -9574,6 +9576,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1558, 0x5101, "Clevo S510WU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x5157, "Clevo W517GU1", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x51a1, "Clevo NS50MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), ++ SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x70f2, "Clevo NH79EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), +@@ -9608,6 +9611,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1558, 0x971d, "Clevo N970T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xa500, "Clevo NL5[03]RU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xa600, "Clevo NL50NU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), ++ SND_PCI_QUIRK(0x1558, 0xa671, "Clevo NP70SN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xb018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xb019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xb022, "Clevo NH77D[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), +@@ -9708,6 +9712,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x511e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), ++ SND_PCI_QUIRK(0x17aa, 0x9e56, "Lenovo ZhaoYang CF4620Z", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1849, 0x1233, "ASRock NUC Box 1100", ALC233_FIXUP_NO_AUDIO_JACK), + SND_PCI_QUIRK(0x1849, 0xa233, "Positivo Master C6300", ALC269_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS), +diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c +index 1e198e4d57b8d..82d4e0fda91be 100644 +--- a/sound/pci/ymfpci/ymfpci.c ++++ b/sound/pci/ymfpci/ymfpci.c +@@ -170,7 +170,7 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci, + return -ENOENT; + } + +- err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, ++ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, + sizeof(*chip), &card); + if (err < 0) + return err; +diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c +index c80114c0ad7bf..b492c32ce0704 100644 +--- a/sound/pci/ymfpci/ymfpci_main.c ++++ b/sound/pci/ymfpci/ymfpci_main.c +@@ -2165,7 +2165,7 @@ static int snd_ymfpci_memalloc(struct snd_ymfpci *chip) + chip->work_base = ptr; + chip->work_base_addr = ptr_addr; + +- snd_BUG_ON(ptr + chip->work_size != ++ snd_BUG_ON(ptr + PAGE_ALIGN(chip->work_size) != + chip->work_ptr->area + chip->work_ptr->bytes); + + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr); +diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c +index 5d1c58df081ac..e5611f655beda 100644 +--- a/sound/soc/codecs/lpass-tx-macro.c ++++ b/sound/soc/codecs/lpass-tx-macro.c +@@ -241,7 +241,7 @@ enum { + + struct tx_mute_work { + struct tx_macro *tx; +- u32 decimator; ++ u8 decimator; + struct delayed_work dwork; + }; + +@@ -634,7 +634,7 @@ exit: + return 0; + } + +-static bool is_amic_enabled(struct snd_soc_component *component, int decimator) ++static bool is_amic_enabled(struct snd_soc_component *component, u8 decimator) + { + u16 adc_mux_reg, adc_reg, adc_n; + +@@ -845,7 +845,7 @@ static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) + { + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); +- unsigned int decimator; ++ u8 decimator; + u16 tx_vol_ctl_reg, dec_cfg_reg, hpf_gate_reg, tx_gain_ctl_reg; + u8 hpf_cut_off_freq; + int hpf_delay = TX_MACRO_DMIC_HPF_DELAY_MS; +@@ -1060,7 +1060,8 @@ static int tx_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + { + struct snd_soc_component *component = dai->component; +- u32 decimator, sample_rate; ++ u32 sample_rate; ++ u8 decimator; + int tx_fs_rate; + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + +@@ -1124,7 +1125,7 @@ static int tx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) + { + struct snd_soc_component *component = dai->component; + struct tx_macro *tx = snd_soc_component_get_drvdata(component); +- u16 decimator; ++ u8 decimator; + + /* active decimator not set yet */ + if (tx->active_decimator[dai->id] == -1) +diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c +index 02ae542ad7792..a63563594b4cd 100644 +--- a/sound/soc/intel/avs/boards/da7219.c ++++ b/sound/soc/intel/avs/boards/da7219.c +@@ -111,6 +111,26 @@ static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime) + return 0; + } + ++static int ++avs_da7219_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params) ++{ ++ struct snd_interval *rate, *channels; ++ struct snd_mask *fmt; ++ ++ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); ++ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); ++ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); ++ ++ /* The ADSP will convert the FE rate to 48k, stereo */ ++ rate->min = rate->max = 48000; ++ channels->min = channels->max = 2; ++ ++ /* set SSP0 to 24 bit */ ++ snd_mask_none(fmt); ++ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); ++ return 0; ++} ++ + static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port, + struct snd_soc_dai_link **dai_link) + { +@@ -142,6 +162,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in + dl->num_platforms = 1; + dl->id = 0; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; ++ dl->be_hw_params_fixup = avs_da7219_be_fixup; + dl->init = avs_da7219_codec_init; + dl->nonatomic = 1; + dl->no_pcm = 1; +diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c +index 921f42caf7e09..183123d08c5a3 100644 +--- a/sound/soc/intel/avs/boards/max98357a.c ++++ b/sound/soc/intel/avs/boards/max98357a.c +@@ -8,6 +8,7 @@ + + #include <linux/module.h> + #include <linux/platform_device.h> ++#include <sound/pcm_params.h> + #include <sound/soc.h> + #include <sound/soc-acpi.h> + #include <sound/soc-dapm.h> +@@ -24,6 +25,26 @@ static const struct snd_soc_dapm_route card_base_routes[] = { + { "Spk", NULL, "Speaker" }, + }; + ++static int ++avs_max98357a_be_fixup(struct snd_soc_pcm_runtime *runrime, struct snd_pcm_hw_params *params) ++{ ++ struct snd_interval *rate, *channels; ++ struct snd_mask *fmt; ++ ++ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); ++ channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); ++ fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); ++ ++ /* The ADSP will convert the FE rate to 48k, stereo */ ++ rate->min = rate->max = 48000; ++ channels->min = channels->max = 2; ++ ++ /* set SSP0 to 16 bit */ ++ snd_mask_none(fmt); ++ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); ++ return 0; ++} ++ + static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port, + struct snd_soc_dai_link **dai_link) + { +@@ -55,6 +76,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in + dl->num_platforms = 1; + dl->id = 0; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; ++ dl->be_hw_params_fixup = avs_max98357a_be_fixup; + dl->nonatomic = 1; + dl->no_pcm = 1; + dl->dpcm_playback = 1; +diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c +index f76909e9f990a..8392d8fac8f9c 100644 +--- a/sound/soc/intel/avs/boards/nau8825.c ++++ b/sound/soc/intel/avs/boards/nau8825.c +@@ -33,15 +33,15 @@ avs_nau8825_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *co + return -EINVAL; + } + +- if (!SND_SOC_DAPM_EVENT_ON(event)) { ++ if (SND_SOC_DAPM_EVENT_ON(event)) ++ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_MCLK, 24000000, ++ SND_SOC_CLOCK_IN); ++ else + ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN); +- if (ret < 0) { +- dev_err(card->dev, "set sysclk err = %d\n", ret); +- return ret; +- } +- } ++ if (ret < 0) ++ dev_err(card->dev, "Set sysclk failed: %d\n", ret); + +- return 0; ++ return ret; + } + + static const struct snd_kcontrol_new card_controls[] = { +diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c +index 9f84c8ab34478..51a8867326b47 100644 +--- a/sound/soc/intel/avs/boards/ssm4567.c ++++ b/sound/soc/intel/avs/boards/ssm4567.c +@@ -15,7 +15,6 @@ + #include <sound/soc-acpi.h> + #include "../../../codecs/nau8825.h" + +-#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" + #define SKL_SSM_CODEC_DAI "ssm4567-hifi" + + static struct snd_soc_codec_conf card_codec_conf[] = { +@@ -34,41 +33,11 @@ static const struct snd_kcontrol_new card_controls[] = { + SOC_DAPM_PIN_SWITCH("Right Speaker"), + }; + +-static int +-platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *control, int event) +-{ +- struct snd_soc_dapm_context *dapm = w->dapm; +- struct snd_soc_card *card = dapm->card; +- struct snd_soc_dai *codec_dai; +- int ret; +- +- codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI); +- if (!codec_dai) { +- dev_err(card->dev, "Codec dai not found\n"); +- return -EINVAL; +- } +- +- if (SND_SOC_DAPM_EVENT_ON(event)) { +- ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_MCLK, 24000000, +- SND_SOC_CLOCK_IN); +- if (ret < 0) +- dev_err(card->dev, "set sysclk err = %d\n", ret); +- } else { +- ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN); +- if (ret < 0) +- dev_err(card->dev, "set sysclk err = %d\n", ret); +- } +- +- return ret; +-} +- + static const struct snd_soc_dapm_widget card_widgets[] = { + SND_SOC_DAPM_SPK("Left Speaker", NULL), + SND_SOC_DAPM_SPK("Right Speaker", NULL), + SND_SOC_DAPM_SPK("DP1", NULL), + SND_SOC_DAPM_SPK("DP2", NULL), +- SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, platform_clock_control, +- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + }; + + static const struct snd_soc_dapm_route card_base_routes[] = { +diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c +index f0f6d9ba88037..0b17d1bb225e2 100644 +--- a/sound/soc/sof/intel/pci-tng.c ++++ b/sound/soc/sof/intel/pci-tng.c +@@ -75,11 +75,7 @@ static int tangier_pci_probe(struct snd_sof_dev *sdev) + + /* LPE base */ + base = pci_resource_start(pci, desc->resindex_lpe_base) - IRAM_OFFSET; +- size = pci_resource_len(pci, desc->resindex_lpe_base); +- if (size < PCI_BAR_SIZE) { +- dev_err(sdev->dev, "error: I/O region is too small.\n"); +- return -ENODEV; +- } ++ size = PCI_BAR_SIZE; + + dev_dbg(sdev->dev, "LPE PHY base at 0x%x size 0x%x", base, size); + sdev->bar[DSP_BAR] = devm_ioremap(sdev->dev, base, size); +diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c +index b28af3a48b707..60b96b0c2412f 100644 +--- a/sound/soc/sof/ipc3.c ++++ b/sound/soc/sof/ipc3.c +@@ -970,8 +970,9 @@ static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev) + return; + } + +- if (hdr.size < sizeof(hdr)) { +- dev_err(sdev->dev, "The received message size is invalid\n"); ++ if (hdr.size < sizeof(hdr) || hdr.size > SOF_IPC_MSG_MAX_SIZE) { ++ dev_err(sdev->dev, "The received message size is invalid: %u\n", ++ hdr.size); + return; + } + +diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c +index 0d5a578c34962..7442ec1c5a4d4 100644 +--- a/sound/soc/sof/ipc4-control.c ++++ b/sound/soc/sof/ipc4-control.c +@@ -84,7 +84,8 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge + } + + /* set curve type and duration from topology */ +- data.curve_duration = gain->data.curve_duration; ++ data.curve_duration_l = gain->data.curve_duration_l; ++ data.curve_duration_h = gain->data.curve_duration_h; + data.curve_type = gain->data.curve_type; + + msg->data_ptr = &data; +diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c +index a81af5f73a4b4..49289932ba7e6 100644 +--- a/sound/soc/sof/ipc4-topology.c ++++ b/sound/soc/sof/ipc4-topology.c +@@ -106,7 +106,7 @@ static const struct sof_topology_token gain_tokens[] = { + get_token_u32, offsetof(struct sof_ipc4_gain_data, curve_type)}, + {SOF_TKN_GAIN_RAMP_DURATION, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, +- offsetof(struct sof_ipc4_gain_data, curve_duration)}, ++ offsetof(struct sof_ipc4_gain_data, curve_duration_l)}, + {SOF_TKN_GAIN_VAL, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, offsetof(struct sof_ipc4_gain_data, init_val)}, + }; +@@ -154,7 +154,7 @@ static void sof_ipc4_dbg_audio_format(struct device *dev, + for (i = 0; i < num_format; i++, ptr = (u8 *)ptr + object_size) { + fmt = ptr; + dev_dbg(dev, +- " #%d: %uKHz, %ubit (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x)\n", ++ " #%d: %uHz, %ubit (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x)\n", + i, fmt->sampling_frequency, fmt->bit_depth, fmt->ch_map, + fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg); + } +@@ -682,7 +682,7 @@ static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget) + + dev_dbg(scomp->dev, + "pga widget %s: ramp type: %d, ramp duration %d, initial gain value: %#x, cpc %d\n", +- swidget->widget->name, gain->data.curve_type, gain->data.curve_duration, ++ swidget->widget->name, gain->data.curve_type, gain->data.curve_duration_l, + gain->data.init_val, gain->base_config.cpc); + + ret = sof_ipc4_widget_setup_msg(swidget, &gain->msg); +diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h +index 2363a7cc0b57d..cf9d278524572 100644 +--- a/sound/soc/sof/ipc4-topology.h ++++ b/sound/soc/sof/ipc4-topology.h +@@ -217,14 +217,16 @@ struct sof_ipc4_control_data { + * @init_val: Initial value + * @curve_type: Curve type + * @reserved: reserved for future use +- * @curve_duration: Curve duration ++ * @curve_duration_l: Curve duration low part ++ * @curve_duration_h: Curve duration high part + */ + struct sof_ipc4_gain_data { + uint32_t channels; + uint32_t init_val; + uint32_t curve_type; + uint32_t reserved; +- uint32_t curve_duration; ++ uint32_t curve_duration_l; ++ uint32_t curve_duration_h; + } __aligned(8); + + /** +diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c +index 419302e2057e8..647fa054d8b1d 100644 +--- a/sound/usb/endpoint.c ++++ b/sound/usb/endpoint.c +@@ -455,8 +455,8 @@ static void push_back_to_ready_list(struct snd_usb_endpoint *ep, + * This function is used both for implicit feedback endpoints and in low- + * latency playback mode. + */ +-void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, +- bool in_stream_lock) ++int snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, ++ bool in_stream_lock) + { + bool implicit_fb = snd_usb_endpoint_implicit_feedback_sink(ep); + +@@ -480,7 +480,7 @@ void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, + spin_unlock_irqrestore(&ep->lock, flags); + + if (ctx == NULL) +- return; ++ break; + + /* copy over the length information */ + if (implicit_fb) { +@@ -495,11 +495,14 @@ void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, + break; + if (err < 0) { + /* push back to ready list again for -EAGAIN */ +- if (err == -EAGAIN) ++ if (err == -EAGAIN) { + push_back_to_ready_list(ep, ctx); +- else ++ break; ++ } ++ ++ if (!in_stream_lock) + notify_xrun(ep); +- return; ++ return -EPIPE; + } + + err = usb_submit_urb(ctx->urb, GFP_ATOMIC); +@@ -507,13 +510,16 @@ void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, + usb_audio_err(ep->chip, + "Unable to submit urb #%d: %d at %s\n", + ctx->index, err, __func__); +- notify_xrun(ep); +- return; ++ if (!in_stream_lock) ++ notify_xrun(ep); ++ return -EPIPE; + } + + set_bit(ctx->index, &ep->active_mask); + atomic_inc(&ep->submitted_urbs); + } ++ ++ return 0; + } + + /* +diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h +index 924f4351588ce..c09f68ce08b18 100644 +--- a/sound/usb/endpoint.h ++++ b/sound/usb/endpoint.h +@@ -52,7 +52,7 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); + int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *ctx, int idx, + unsigned int avail); +-void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, +- bool in_stream_lock); ++int snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, ++ bool in_stream_lock); + + #endif /* __USBAUDIO_ENDPOINT_H */ +diff --git a/sound/usb/format.c b/sound/usb/format.c +index 405dc0bf6678c..4b1c5ba121f39 100644 +--- a/sound/usb/format.c ++++ b/sound/usb/format.c +@@ -39,8 +39,12 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, + case UAC_VERSION_1: + default: { + struct uac_format_type_i_discrete_descriptor *fmt = _fmt; +- if (format >= 64) +- return 0; /* invalid format */ ++ if (format >= 64) { ++ usb_audio_info(chip, ++ "%u:%d: invalid format type 0x%llx is detected, processed as PCM\n", ++ fp->iface, fp->altsetting, format); ++ format = UAC_FORMAT_TYPE_I_PCM; ++ } + sample_width = fmt->bBitResolution; + sample_bytes = fmt->bSubframeSize; + format = 1ULL << format; +diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c +index 2c5765cbed2d6..1e1d7458bce10 100644 +--- a/sound/usb/pcm.c ++++ b/sound/usb/pcm.c +@@ -1595,7 +1595,7 @@ static int snd_usb_pcm_playback_ack(struct snd_pcm_substream *substream) + * outputs here + */ + if (!ep->active_mask) +- snd_usb_queue_pending_output_urbs(ep, true); ++ return snd_usb_queue_pending_output_urbs(ep, true); + return 0; + } + +diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c +index 0b470169729e6..56102711f395a 100644 +--- a/tools/lib/bpf/btf_dump.c ++++ b/tools/lib/bpf/btf_dump.c +@@ -833,14 +833,9 @@ static bool btf_is_struct_packed(const struct btf *btf, __u32 id, + const struct btf_type *t) + { + const struct btf_member *m; +- int align, i, bit_sz; ++ int max_align = 1, align, i, bit_sz; + __u16 vlen; + +- align = btf__align_of(btf, id); +- /* size of a non-packed struct has to be a multiple of its alignment*/ +- if (align && t->size % align) +- return true; +- + m = btf_members(t); + vlen = btf_vlen(t); + /* all non-bitfield fields have to be naturally aligned */ +@@ -849,8 +844,11 @@ static bool btf_is_struct_packed(const struct btf *btf, __u32 id, + bit_sz = btf_member_bitfield_size(t, i); + if (align && bit_sz == 0 && m->offset % (8 * align) != 0) + return true; ++ max_align = max(align, max_align); + } +- ++ /* size of a non-packed struct has to be a multiple of its alignment */ ++ if (t->size % max_align != 0) ++ return true; + /* + * if original struct was marked as packed, but its layout is + * naturally aligned, we'll detect that it's not packed +@@ -858,44 +856,97 @@ static bool btf_is_struct_packed(const struct btf *btf, __u32 id, + return false; + } + +-static int chip_away_bits(int total, int at_most) +-{ +- return total % at_most ? : at_most; +-} +- + static void btf_dump_emit_bit_padding(const struct btf_dump *d, +- int cur_off, int m_off, int m_bit_sz, +- int align, int lvl) ++ int cur_off, int next_off, int next_align, ++ bool in_bitfield, int lvl) + { +- int off_diff = m_off - cur_off; +- int ptr_bits = d->ptr_sz * 8; ++ const struct { ++ const char *name; ++ int bits; ++ } pads[] = { ++ {"long", d->ptr_sz * 8}, {"int", 32}, {"short", 16}, {"char", 8} ++ }; ++ int new_off, pad_bits, bits, i; ++ const char *pad_type; ++ ++ if (cur_off >= next_off) ++ return; /* no gap */ ++ ++ /* For filling out padding we want to take advantage of ++ * natural alignment rules to minimize unnecessary explicit ++ * padding. First, we find the largest type (among long, int, ++ * short, or char) that can be used to force naturally aligned ++ * boundary. Once determined, we'll use such type to fill in ++ * the remaining padding gap. In some cases we can rely on ++ * compiler filling some gaps, but sometimes we need to force ++ * alignment to close natural alignment with markers like ++ * `long: 0` (this is always the case for bitfields). Note ++ * that even if struct itself has, let's say 4-byte alignment ++ * (i.e., it only uses up to int-aligned types), using `long: ++ * X;` explicit padding doesn't actually change struct's ++ * overall alignment requirements, but compiler does take into ++ * account that type's (long, in this example) natural ++ * alignment requirements when adding implicit padding. We use ++ * this fact heavily and don't worry about ruining correct ++ * struct alignment requirement. ++ */ ++ for (i = 0; i < ARRAY_SIZE(pads); i++) { ++ pad_bits = pads[i].bits; ++ pad_type = pads[i].name; + +- if (off_diff <= 0) +- /* no gap */ +- return; +- if (m_bit_sz == 0 && off_diff < align * 8) +- /* natural padding will take care of a gap */ +- return; ++ new_off = roundup(cur_off, pad_bits); ++ if (new_off <= next_off) ++ break; ++ } + +- while (off_diff > 0) { +- const char *pad_type; +- int pad_bits; +- +- if (ptr_bits > 32 && off_diff > 32) { +- pad_type = "long"; +- pad_bits = chip_away_bits(off_diff, ptr_bits); +- } else if (off_diff > 16) { +- pad_type = "int"; +- pad_bits = chip_away_bits(off_diff, 32); +- } else if (off_diff > 8) { +- pad_type = "short"; +- pad_bits = chip_away_bits(off_diff, 16); +- } else { +- pad_type = "char"; +- pad_bits = chip_away_bits(off_diff, 8); ++ if (new_off > cur_off && new_off <= next_off) { ++ /* We need explicit `<type>: 0` aligning mark if next ++ * field is right on alignment offset and its ++ * alignment requirement is less strict than <type>'s ++ * alignment (so compiler won't naturally align to the ++ * offset we expect), or if subsequent `<type>: X`, ++ * will actually completely fit in the remaining hole, ++ * making compiler basically ignore `<type>: X` ++ * completely. ++ */ ++ if (in_bitfield || ++ (new_off == next_off && roundup(cur_off, next_align * 8) != new_off) || ++ (new_off != next_off && next_off - new_off <= new_off - cur_off)) ++ /* but for bitfields we'll emit explicit bit count */ ++ btf_dump_printf(d, "\n%s%s: %d;", pfx(lvl), pad_type, ++ in_bitfield ? new_off - cur_off : 0); ++ cur_off = new_off; ++ } ++ ++ /* Now we know we start at naturally aligned offset for a chosen ++ * padding type (long, int, short, or char), and so the rest is just ++ * a straightforward filling of remaining padding gap with full ++ * `<type>: sizeof(<type>);` markers, except for the last one, which ++ * might need smaller than sizeof(<type>) padding. ++ */ ++ while (cur_off != next_off) { ++ bits = min(next_off - cur_off, pad_bits); ++ if (bits == pad_bits) { ++ btf_dump_printf(d, "\n%s%s: %d;", pfx(lvl), pad_type, pad_bits); ++ cur_off += bits; ++ continue; ++ } ++ /* For the remainder padding that doesn't cover entire ++ * pad_type bit length, we pick the smallest necessary type. ++ * This is pure aesthetics, we could have just used `long`, ++ * but having smallest necessary one communicates better the ++ * scale of the padding gap. ++ */ ++ for (i = ARRAY_SIZE(pads) - 1; i >= 0; i--) { ++ pad_type = pads[i].name; ++ pad_bits = pads[i].bits; ++ if (pad_bits < bits) ++ continue; ++ ++ btf_dump_printf(d, "\n%s%s: %d;", pfx(lvl), pad_type, bits); ++ cur_off += bits; ++ break; + } +- btf_dump_printf(d, "\n%s%s: %d;", pfx(lvl), pad_type, pad_bits); +- off_diff -= pad_bits; + } + } + +@@ -915,9 +966,11 @@ static void btf_dump_emit_struct_def(struct btf_dump *d, + { + const struct btf_member *m = btf_members(t); + bool is_struct = btf_is_struct(t); +- int align, i, packed, off = 0; ++ bool packed, prev_bitfield = false; ++ int align, i, off = 0; + __u16 vlen = btf_vlen(t); + ++ align = btf__align_of(d->btf, id); + packed = is_struct ? btf_is_struct_packed(d->btf, id, t) : 0; + + btf_dump_printf(d, "%s%s%s {", +@@ -927,33 +980,36 @@ static void btf_dump_emit_struct_def(struct btf_dump *d, + + for (i = 0; i < vlen; i++, m++) { + const char *fname; +- int m_off, m_sz; ++ int m_off, m_sz, m_align; ++ bool in_bitfield; + + fname = btf_name_of(d, m->name_off); + m_sz = btf_member_bitfield_size(t, i); + m_off = btf_member_bit_offset(t, i); +- align = packed ? 1 : btf__align_of(d->btf, m->type); ++ m_align = packed ? 1 : btf__align_of(d->btf, m->type); + +- btf_dump_emit_bit_padding(d, off, m_off, m_sz, align, lvl + 1); ++ in_bitfield = prev_bitfield && m_sz != 0; ++ ++ btf_dump_emit_bit_padding(d, off, m_off, m_align, in_bitfield, lvl + 1); + btf_dump_printf(d, "\n%s", pfx(lvl + 1)); + btf_dump_emit_type_decl(d, m->type, fname, lvl + 1); + + if (m_sz) { + btf_dump_printf(d, ": %d", m_sz); + off = m_off + m_sz; ++ prev_bitfield = true; + } else { + m_sz = max((__s64)0, btf__resolve_size(d->btf, m->type)); + off = m_off + m_sz * 8; ++ prev_bitfield = false; + } ++ + btf_dump_printf(d, ";"); + } + + /* pad at the end, if necessary */ +- if (is_struct) { +- align = packed ? 1 : btf__align_of(d->btf, id); +- btf_dump_emit_bit_padding(d, off, t->size * 8, 0, align, +- lvl + 1); +- } ++ if (is_struct) ++ btf_dump_emit_bit_padding(d, off, t->size * 8, align, false, lvl + 1); + + if (vlen) + btf_dump_printf(d, "\n"); +diff --git a/tools/power/acpi/tools/pfrut/pfrut.c b/tools/power/acpi/tools/pfrut/pfrut.c +index 52aa0351533c3..388c9e3ad0407 100644 +--- a/tools/power/acpi/tools/pfrut/pfrut.c ++++ b/tools/power/acpi/tools/pfrut/pfrut.c +@@ -97,7 +97,7 @@ static struct option long_options[] = { + static void parse_options(int argc, char **argv) + { + int option_index = 0; +- char *pathname; ++ char *pathname, *endptr; + int opt; + + pathname = strdup(argv[0]); +@@ -125,11 +125,23 @@ static void parse_options(int argc, char **argv) + log_getinfo = 1; + break; + case 'T': +- log_type = atoi(optarg); ++ log_type = strtol(optarg, &endptr, 0); ++ if (*endptr || (log_type != 0 && log_type != 1)) { ++ printf("Number expected: type(0:execution, 1:history) - Quit.\n"); ++ exit(1); ++ } ++ + set_log_type = 1; + break; + case 'L': +- log_level = atoi(optarg); ++ log_level = strtol(optarg, &endptr, 0); ++ if (*endptr || ++ (log_level != 0 && log_level != 1 && ++ log_level != 2 && log_level != 4)) { ++ printf("Number expected: level(0, 1, 2, 4) - Quit.\n"); ++ exit(1); ++ } ++ + set_log_level = 1; + break; + case 'R': +diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8 +index c7b26a3603afe..3e1a4c4be001a 100644 +--- a/tools/power/x86/turbostat/turbostat.8 ++++ b/tools/power/x86/turbostat/turbostat.8 +@@ -344,6 +344,8 @@ Alternatively, non-root users can be enabled to run turbostat this way: + + # chmod +r /dev/cpu/*/msr + ++# chmod +r /dev/cpu_dma_latency ++ + .B "turbostat " + reads hardware counters, but doesn't write them. + So it will not interfere with the OS or other programs, including +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index aba460410dbd1..c61c6c704fbe6 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -4426,7 +4426,7 @@ int print_hwp(struct thread_data *t, struct core_data *c, struct pkg_data *p) + + fprintf(outf, "cpu%d: MSR_HWP_STATUS: 0x%08llx " + "(%sGuaranteed_Perf_Change, %sExcursion_Min)\n", +- cpu, msr, ((msr) & 0x1) ? "" : "No-", ((msr) & 0x2) ? "" : "No-"); ++ cpu, msr, ((msr) & 0x1) ? "" : "No-", ((msr) & 0x4) ? "" : "No-"); + + return 0; + } +@@ -5482,7 +5482,7 @@ void print_dev_latency(void) + + retval = read(fd, (void *)&value, sizeof(int)); + if (retval != sizeof(int)) { +- warn("read %s\n", path); ++ warn("read failed %s\n", path); + close(fd); + return; + } +diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_bitfields.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_bitfields.c +index e5560a6560309..e01690618e1ee 100644 +--- a/tools/testing/selftests/bpf/progs/btf_dump_test_case_bitfields.c ++++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_bitfields.c +@@ -53,7 +53,7 @@ struct bitfields_only_mixed_types { + */ + /* ------ END-EXPECTED-OUTPUT ------ */ + struct bitfield_mixed_with_others { +- long: 4; /* char is enough as a backing field */ ++ char: 4; /* char is enough as a backing field */ + int a: 4; + /* 8-bit implicit padding */ + short b; /* combined with previous bitfield */ +diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_packing.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_packing.c +index e304b6204bd9d..7998f27df7ddd 100644 +--- a/tools/testing/selftests/bpf/progs/btf_dump_test_case_packing.c ++++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_packing.c +@@ -58,7 +58,81 @@ union jump_code_union { + } __attribute__((packed)); + }; + +-/*------ END-EXPECTED-OUTPUT ------ */ ++/* ----- START-EXPECTED-OUTPUT ----- */ ++/* ++ *struct nested_packed_but_aligned_struct { ++ * int x1; ++ * int x2; ++ *}; ++ * ++ *struct outer_implicitly_packed_struct { ++ * char y1; ++ * struct nested_packed_but_aligned_struct y2; ++ *} __attribute__((packed)); ++ * ++ */ ++/* ------ END-EXPECTED-OUTPUT ------ */ ++ ++struct nested_packed_but_aligned_struct { ++ int x1; ++ int x2; ++} __attribute__((packed)); ++ ++struct outer_implicitly_packed_struct { ++ char y1; ++ struct nested_packed_but_aligned_struct y2; ++}; ++/* ----- START-EXPECTED-OUTPUT ----- */ ++/* ++ *struct usb_ss_ep_comp_descriptor { ++ * char: 8; ++ * char bDescriptorType; ++ * char bMaxBurst; ++ * short wBytesPerInterval; ++ *}; ++ * ++ *struct usb_host_endpoint { ++ * long: 64; ++ * char: 8; ++ * struct usb_ss_ep_comp_descriptor ss_ep_comp; ++ * long: 0; ++ *} __attribute__((packed)); ++ * ++ */ ++/* ------ END-EXPECTED-OUTPUT ------ */ ++ ++struct usb_ss_ep_comp_descriptor { ++ char: 8; ++ char bDescriptorType; ++ char bMaxBurst; ++ int: 0; ++ short wBytesPerInterval; ++} __attribute__((packed)); ++ ++struct usb_host_endpoint { ++ long: 64; ++ char: 8; ++ struct usb_ss_ep_comp_descriptor ss_ep_comp; ++ long: 0; ++}; ++ ++/* ----- START-EXPECTED-OUTPUT ----- */ ++struct nested_packed_struct { ++ int a; ++ char b; ++} __attribute__((packed)); ++ ++struct outer_nonpacked_struct { ++ short a; ++ struct nested_packed_struct b; ++}; ++ ++struct outer_packed_struct { ++ short a; ++ struct nested_packed_struct b; ++} __attribute__((packed)); ++ ++/* ------ END-EXPECTED-OUTPUT ------ */ + + int f(struct { + struct packed_trailing_space _1; +@@ -69,6 +143,10 @@ int f(struct { + union union_is_never_packed _6; + union union_does_not_need_packing _7; + union jump_code_union _8; ++ struct outer_implicitly_packed_struct _9; ++ struct usb_host_endpoint _10; ++ struct outer_nonpacked_struct _11; ++ struct outer_packed_struct _12; + } *_) + { + return 0; +diff --git a/tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c b/tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c +index f2661c8d2d900..79276fbe454a8 100644 +--- a/tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c ++++ b/tools/testing/selftests/bpf/progs/btf_dump_test_case_padding.c +@@ -19,7 +19,7 @@ struct padded_implicitly { + /* + *struct padded_explicitly { + * int a; +- * int: 32; ++ * long: 0; + * int b; + *}; + * +@@ -28,41 +28,28 @@ struct padded_implicitly { + + struct padded_explicitly { + int a; +- int: 1; /* algo will explicitly pad with full 32 bits here */ ++ int: 1; /* algo will emit aligning `long: 0;` here */ + int b; + }; + + /* ----- START-EXPECTED-OUTPUT ----- */ +-/* +- *struct padded_a_lot { +- * int a; +- * long: 32; +- * long: 64; +- * long: 64; +- * int b; +- *}; +- * +- */ +-/* ------ END-EXPECTED-OUTPUT ------ */ +- + struct padded_a_lot { + int a; +- /* 32 bit of implicit padding here, which algo will make explicit */ + long: 64; + long: 64; + int b; + }; + ++/* ------ END-EXPECTED-OUTPUT ------ */ ++ + /* ----- START-EXPECTED-OUTPUT ----- */ + /* + *struct padded_cache_line { + * int a; +- * long: 32; + * long: 64; + * long: 64; + * long: 64; + * int b; +- * long: 32; + * long: 64; + * long: 64; + * long: 64; +@@ -85,7 +72,7 @@ struct padded_cache_line { + *struct zone { + * int a; + * short b; +- * short: 16; ++ * long: 0; + * struct zone_padding __pad__; + *}; + * +@@ -102,12 +89,160 @@ struct zone { + struct zone_padding __pad__; + }; + ++/* ----- START-EXPECTED-OUTPUT ----- */ ++struct padding_wo_named_members { ++ long: 64; ++ long: 64; ++}; ++ ++struct padding_weird_1 { ++ int a; ++ long: 64; ++ short: 16; ++ short b; ++}; ++ ++/* ------ END-EXPECTED-OUTPUT ------ */ ++ ++/* ----- START-EXPECTED-OUTPUT ----- */ ++/* ++ *struct padding_weird_2 { ++ * long: 56; ++ * char a; ++ * long: 56; ++ * char b; ++ * char: 8; ++ *}; ++ * ++ */ ++/* ------ END-EXPECTED-OUTPUT ------ */ ++struct padding_weird_2 { ++ int: 32; /* these paddings will be collapsed into `long: 56;` */ ++ short: 16; ++ char: 8; ++ char a; ++ int: 32; /* these paddings will be collapsed into `long: 56;` */ ++ short: 16; ++ char: 8; ++ char b; ++ char: 8; ++}; ++ ++/* ----- START-EXPECTED-OUTPUT ----- */ ++struct exact_1byte { ++ char x; ++}; ++ ++struct padded_1byte { ++ char: 8; ++}; ++ ++struct exact_2bytes { ++ short x; ++}; ++ ++struct padded_2bytes { ++ short: 16; ++}; ++ ++struct exact_4bytes { ++ int x; ++}; ++ ++struct padded_4bytes { ++ int: 32; ++}; ++ ++struct exact_8bytes { ++ long x; ++}; ++ ++struct padded_8bytes { ++ long: 64; ++}; ++ ++struct ff_periodic_effect { ++ int: 32; ++ short magnitude; ++ long: 0; ++ short phase; ++ long: 0; ++ int: 32; ++ int custom_len; ++ short *custom_data; ++}; ++ ++struct ib_wc { ++ long: 64; ++ long: 64; ++ int: 32; ++ int byte_len; ++ void *qp; ++ union {} ex; ++ long: 64; ++ int slid; ++ int wc_flags; ++ long: 64; ++ char smac[6]; ++ long: 0; ++ char network_hdr_type; ++}; ++ ++struct acpi_object_method { ++ long: 64; ++ char: 8; ++ char type; ++ short reference_count; ++ char flags; ++ short: 0; ++ char: 8; ++ char sync_level; ++ long: 64; ++ void *node; ++ void *aml_start; ++ union {} dispatch; ++ long: 64; ++ int aml_length; ++}; ++ ++struct nested_unpacked { ++ int x; ++}; ++ ++struct nested_packed { ++ struct nested_unpacked a; ++ char c; ++} __attribute__((packed)); ++ ++struct outer_mixed_but_unpacked { ++ struct nested_packed b1; ++ short a1; ++ struct nested_packed b2; ++}; ++ ++/* ------ END-EXPECTED-OUTPUT ------ */ ++ + int f(struct { + struct padded_implicitly _1; + struct padded_explicitly _2; + struct padded_a_lot _3; + struct padded_cache_line _4; + struct zone _5; ++ struct padding_wo_named_members _6; ++ struct padding_weird_1 _7; ++ struct padding_weird_2 _8; ++ struct exact_1byte _100; ++ struct padded_1byte _101; ++ struct exact_2bytes _102; ++ struct padded_2bytes _103; ++ struct exact_4bytes _104; ++ struct padded_4bytes _105; ++ struct exact_8bytes _106; ++ struct padded_8bytes _107; ++ struct ff_periodic_effect _200; ++ struct ib_wc _201; ++ struct acpi_object_method _202; ++ struct outer_mixed_but_unpacked _203; + } *_) + { + return 0; |