From 11b8b9afc202ff9178676a0e4ea79a99b46b03df Mon Sep 17 00:00:00 2001 From: Julian Sikorski Date: Wed, 17 Jan 2024 22:50:43 +0100 Subject: [PATCH] Update odroidxu4-current to 6.1.73 (#6174) * Update odroidxu4-current to 6.1.73 * Re-add kernel patchdir --------- Co-authored-by: Igor Pecovnik --- config/sources/families/odroidxu4.conf | 1 + .../odroidxu4-current/patch-6.1.69-70.patch | 5394 ++++++++++ .../odroidxu4-current/patch-6.1.70-71.patch | 7517 +++++++++++++ .../odroidxu4-current/patch-6.1.71-72.patch | 9332 +++++++++++++++++ .../odroidxu4-current/patch-6.1.72-73.patch | 294 + 5 files changed, 22538 insertions(+) create mode 100644 patch/kernel/odroidxu4-current/patch-6.1.69-70.patch create mode 100644 patch/kernel/odroidxu4-current/patch-6.1.70-71.patch create mode 100644 patch/kernel/odroidxu4-current/patch-6.1.71-72.patch create mode 100644 patch/kernel/odroidxu4-current/patch-6.1.72-73.patch diff --git a/config/sources/families/odroidxu4.conf b/config/sources/families/odroidxu4.conf index 505555413b..337132728a 100644 --- a/config/sources/families/odroidxu4.conf +++ b/config/sources/families/odroidxu4.conf @@ -22,6 +22,7 @@ case $BRANCH in declare -g KERNEL_MAJOR_MINOR="6.1" # Major and minor versions of this kernel. declare -g KERNEL_SKIP_MAKEFILE_VERSION="yes" # Armbian patches change the version here, so no use having it in the version string. KERNELBRANCH='branch:odroidxu4-6.1.y' + KERNELPATCHDIR=odroidxu4-current KERNEL_DRIVERS_SKIP+=(driver_rtl8811CU_rtl8821C) # rtl8821 is already shipped with hardkernel's source ;; diff --git a/patch/kernel/odroidxu4-current/patch-6.1.69-70.patch b/patch/kernel/odroidxu4-current/patch-6.1.69-70.patch new file mode 100644 index 0000000000..f22e31fe78 --- /dev/null +++ b/patch/kernel/odroidxu4-current/patch-6.1.69-70.patch @@ -0,0 +1,5394 @@ +diff --git a/Documentation/devicetree/bindings/nvmem/mxs-ocotp.yaml b/Documentation/devicetree/bindings/nvmem/mxs-ocotp.yaml +index ff317fd7c15bf..2e1fcff3c2801 100644 +--- a/Documentation/devicetree/bindings/nvmem/mxs-ocotp.yaml ++++ b/Documentation/devicetree/bindings/nvmem/mxs-ocotp.yaml +@@ -14,9 +14,11 @@ allOf: + + properties: + compatible: +- enum: +- - fsl,imx23-ocotp +- - fsl,imx28-ocotp ++ items: ++ - enum: ++ - fsl,imx23-ocotp ++ - fsl,imx28-ocotp ++ - const: fsl,ocotp + + "#address-cells": + const: 1 +@@ -40,7 +42,7 @@ additionalProperties: false + examples: + - | + ocotp: efuse@8002c000 { +- compatible = "fsl,imx28-ocotp"; ++ compatible = "fsl,imx28-ocotp", "fsl,ocotp"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x8002c000 0x2000>; +diff --git a/Makefile b/Makefile +index 9a3b34d2387fa..270593fcafdcd 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + VERSION = 6 + PATCHLEVEL = 1 +-SUBLEVEL = 69 ++SUBLEVEL = 70 + EXTRAVERSION = + NAME = Curry Ramen + +diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi +index 97ce0c4f1df7e..a79920ec461f0 100644 +--- a/arch/arm/boot/dts/dra7.dtsi ++++ b/arch/arm/boot/dts/dra7.dtsi +@@ -144,7 +144,7 @@ + + l3-noc@44000000 { + compatible = "ti,dra7-l3-noc"; +- reg = <0x44000000 0x1000>, ++ reg = <0x44000000 0x1000000>, + <0x45000000 0x1000>; + interrupts-extended = <&crossbar_mpu GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>, + <&wakeupgen GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>; +diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c +index 59755b5a1ad7a..75091aa7269ae 100644 +--- a/arch/arm/mach-omap2/id.c ++++ b/arch/arm/mach-omap2/id.c +@@ -793,11 +793,16 @@ void __init omap_soc_device_init(void) + + soc_dev_attr->machine = soc_name; + soc_dev_attr->family = omap_get_family(); ++ if (!soc_dev_attr->family) { ++ kfree(soc_dev_attr); ++ return; ++ } + soc_dev_attr->revision = soc_rev; + soc_dev_attr->custom_attr_group = omap_soc_groups[0]; + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { ++ kfree(soc_dev_attr->family); + kfree(soc_dev_attr); + return; + } +diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c +index 6cc380a15eb76..de94515fb17c6 100644 +--- a/arch/arm64/kvm/arm.c ++++ b/arch/arm64/kvm/arm.c +@@ -386,7 +386,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) + kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); + kvm_timer_vcpu_terminate(vcpu); + kvm_pmu_vcpu_destroy(vcpu); +- ++ kvm_vgic_vcpu_destroy(vcpu); + kvm_arm_vcpu_destroy(vcpu); + } + +diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c +index f2f3bf4a04b0b..0919e3b8f46ec 100644 +--- a/arch/arm64/kvm/vgic/vgic-init.c ++++ b/arch/arm64/kvm/vgic/vgic-init.c +@@ -368,7 +368,7 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm) + vgic_v4_teardown(kvm); + } + +-void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu) ++static void __kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu) + { + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + +@@ -379,29 +379,39 @@ void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu) + vgic_flush_pending_lpis(vcpu); + + INIT_LIST_HEAD(&vgic_cpu->ap_list_head); +- vgic_cpu->rd_iodev.base_addr = VGIC_ADDR_UNDEF; ++ if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) { ++ vgic_unregister_redist_iodev(vcpu); ++ vgic_cpu->rd_iodev.base_addr = VGIC_ADDR_UNDEF; ++ } + } + +-static void __kvm_vgic_destroy(struct kvm *kvm) ++void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu) ++{ ++ struct kvm *kvm = vcpu->kvm; ++ ++ mutex_lock(&kvm->slots_lock); ++ __kvm_vgic_vcpu_destroy(vcpu); ++ mutex_unlock(&kvm->slots_lock); ++} ++ ++void kvm_vgic_destroy(struct kvm *kvm) + { + struct kvm_vcpu *vcpu; + unsigned long i; + +- lockdep_assert_held(&kvm->arch.config_lock); ++ mutex_lock(&kvm->slots_lock); + + vgic_debug_destroy(kvm); + + kvm_for_each_vcpu(i, vcpu, kvm) +- kvm_vgic_vcpu_destroy(vcpu); ++ __kvm_vgic_vcpu_destroy(vcpu); ++ ++ mutex_lock(&kvm->arch.config_lock); + + kvm_vgic_dist_destroy(kvm); +-} + +-void kvm_vgic_destroy(struct kvm *kvm) +-{ +- mutex_lock(&kvm->arch.config_lock); +- __kvm_vgic_destroy(kvm); + mutex_unlock(&kvm->arch.config_lock); ++ mutex_unlock(&kvm->slots_lock); + } + + /** +@@ -469,25 +479,26 @@ int kvm_vgic_map_resources(struct kvm *kvm) + type = VGIC_V3; + } + +- if (ret) { +- __kvm_vgic_destroy(kvm); ++ if (ret) + goto out; +- } ++ + dist->ready = true; + dist_base = dist->vgic_dist_base; + mutex_unlock(&kvm->arch.config_lock); + + ret = vgic_register_dist_iodev(kvm, dist_base, type); +- if (ret) { ++ if (ret) + kvm_err("Unable to register VGIC dist MMIO regions\n"); +- kvm_vgic_destroy(kvm); +- } +- mutex_unlock(&kvm->slots_lock); +- return ret; + ++ goto out_slots; + out: + mutex_unlock(&kvm->arch.config_lock); ++out_slots: + mutex_unlock(&kvm->slots_lock); ++ ++ if (ret) ++ kvm_vgic_destroy(kvm); ++ + return ret; + } + +diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c +index 188d2187eede9..871a45d4fc84c 100644 +--- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c ++++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c +@@ -820,7 +820,7 @@ out_unlock: + return ret; + } + +-static void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu) ++void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu) + { + struct vgic_io_device *rd_dev = &vcpu->arch.vgic_cpu.rd_iodev; + +diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h +index 23e280fa0a16f..9f80a580ca771 100644 +--- a/arch/arm64/kvm/vgic/vgic.h ++++ b/arch/arm64/kvm/vgic/vgic.h +@@ -229,6 +229,7 @@ int vgic_v3_lpi_sync_pending_status(struct kvm *kvm, struct vgic_irq *irq); + int vgic_v3_save_pending_tables(struct kvm *kvm); + int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count); + int vgic_register_redist_iodev(struct kvm_vcpu *vcpu); ++void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu); + bool vgic_v3_check_base(struct kvm *kvm); + + void vgic_v3_load(struct kvm_vcpu *vcpu); +diff --git a/arch/riscv/include/asm/signal.h b/arch/riscv/include/asm/signal.h +index 532c29ef03769..956ae0a01bad1 100644 +--- a/arch/riscv/include/asm/signal.h ++++ b/arch/riscv/include/asm/signal.h +@@ -7,6 +7,6 @@ + #include + + asmlinkage __visible +-void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags); ++void do_work_pending(struct pt_regs *regs, unsigned long thread_info_flags); + + #endif +diff --git a/arch/s390/include/asm/fpu/api.h b/arch/s390/include/asm/fpu/api.h +index b714ed0ef6885..9acf48e53a87f 100644 +--- a/arch/s390/include/asm/fpu/api.h ++++ b/arch/s390/include/asm/fpu/api.h +@@ -79,7 +79,7 @@ static inline int test_fp_ctl(u32 fpc) + #define KERNEL_VXR_HIGH (KERNEL_VXR_V16V23|KERNEL_VXR_V24V31) + + #define KERNEL_VXR (KERNEL_VXR_LOW|KERNEL_VXR_HIGH) +-#define KERNEL_FPR (KERNEL_FPC|KERNEL_VXR_V0V7) ++#define KERNEL_FPR (KERNEL_FPC|KERNEL_VXR_LOW) + + struct kernel_fpu; + +diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c +index 46b7ee0ab01a4..6b8c93989aa31 100644 +--- a/arch/x86/kernel/alternative.c ++++ b/arch/x86/kernel/alternative.c +@@ -1015,8 +1015,8 @@ void __init_or_module text_poke_early(void *addr, const void *opcode, + } else { + local_irq_save(flags); + memcpy(addr, opcode, len); +- local_irq_restore(flags); + sync_core(); ++ local_irq_restore(flags); + + /* + * Could also do a CLFLUSH here to speed up CPU recovery; but +diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c +index 5e680e039d0e1..4686c1d9d0cfd 100644 +--- a/arch/x86/net/bpf_jit_comp.c ++++ b/arch/x86/net/bpf_jit_comp.c +@@ -2553,3 +2553,49 @@ void bpf_jit_free(struct bpf_prog *prog) + + bpf_prog_unlock_free(prog); + } ++ ++void bpf_arch_poke_desc_update(struct bpf_jit_poke_descriptor *poke, ++ struct bpf_prog *new, struct bpf_prog *old) ++{ ++ u8 *old_addr, *new_addr, *old_bypass_addr; ++ int ret; ++ ++ old_bypass_addr = old ? NULL : poke->bypass_addr; ++ old_addr = old ? (u8 *)old->bpf_func + poke->adj_off : NULL; ++ new_addr = new ? (u8 *)new->bpf_func + poke->adj_off : NULL; ++ ++ /* ++ * On program loading or teardown, the program's kallsym entry ++ * might not be in place, so we use __bpf_arch_text_poke to skip ++ * the kallsyms check. ++ */ ++ if (new) { ++ ret = __bpf_arch_text_poke(poke->tailcall_target, ++ BPF_MOD_JUMP, ++ old_addr, new_addr); ++ BUG_ON(ret < 0); ++ if (!old) { ++ ret = __bpf_arch_text_poke(poke->tailcall_bypass, ++ BPF_MOD_JUMP, ++ poke->bypass_addr, ++ NULL); ++ BUG_ON(ret < 0); ++ } ++ } else { ++ ret = __bpf_arch_text_poke(poke->tailcall_bypass, ++ BPF_MOD_JUMP, ++ old_bypass_addr, ++ poke->bypass_addr); ++ BUG_ON(ret < 0); ++ /* let other CPUs finish the execution of program ++ * so that it will not possible to expose them ++ * to invalid nop, stack unwind, nop state ++ */ ++ if (!ret) ++ synchronize_rcu(); ++ ret = __bpf_arch_text_poke(poke->tailcall_target, ++ BPF_MOD_JUMP, ++ old_addr, NULL); ++ BUG_ON(ret < 0); ++ } ++} +diff --git a/arch/x86/xen/Kconfig b/arch/x86/xen/Kconfig +index 9b1ec5d8c99c8..a65fc2ae15b49 100644 +--- a/arch/x86/xen/Kconfig ++++ b/arch/x86/xen/Kconfig +@@ -9,6 +9,7 @@ config XEN + select PARAVIRT_CLOCK + select X86_HV_CALLBACK_VECTOR + depends on X86_64 || (X86_32 && X86_PAE) ++ depends on X86_64 || (X86_GENERIC || MPENTIUM4 || MCORE2 || MATOM || MK8) + depends on X86_LOCAL_APIC && X86_TSC + help + This is the Linux Xen port. Enabling this will allow the +diff --git a/drivers/block/loop.c b/drivers/block/loop.c +index 426d0b42685a0..127e3ceb59799 100644 +--- a/drivers/block/loop.c ++++ b/drivers/block/loop.c +@@ -1777,14 +1777,43 @@ static const struct block_device_operations lo_fops = { + /* + * If max_loop is specified, create that many devices upfront. + * This also becomes a hard limit. If max_loop is not specified, ++ * the default isn't a hard limit (as before commit 85c50197716c ++ * changed the default value from 0 for max_loop=0 reasons), just + * create CONFIG_BLK_DEV_LOOP_MIN_COUNT loop devices at module + * init time. Loop devices can be requested on-demand with the + * /dev/loop-control interface, or be instantiated by accessing + * a 'dead' device node. + */ + static int max_loop = CONFIG_BLK_DEV_LOOP_MIN_COUNT; +-module_param(max_loop, int, 0444); ++ ++#ifdef CONFIG_BLOCK_LEGACY_AUTOLOAD ++static bool max_loop_specified; ++ ++static int max_loop_param_set_int(const char *val, ++ const struct kernel_param *kp) ++{ ++ int ret; ++ ++ ret = param_set_int(val, kp); ++ if (ret < 0) ++ return ret; ++ ++ max_loop_specified = true; ++ return 0; ++} ++ ++static const struct kernel_param_ops max_loop_param_ops = { ++ .set = max_loop_param_set_int, ++ .get = param_get_int, ++}; ++ ++module_param_cb(max_loop, &max_loop_param_ops, &max_loop, 0444); + MODULE_PARM_DESC(max_loop, "Maximum number of loop devices"); ++#else ++module_param(max_loop, int, 0444); ++MODULE_PARM_DESC(max_loop, "Initial number of loop devices"); ++#endif ++ + module_param(max_part, int, 0444); + MODULE_PARM_DESC(max_part, "Maximum number of partitions per loop device"); + +@@ -2089,14 +2118,18 @@ static void loop_remove(struct loop_device *lo) + put_disk(lo->lo_disk); + } + ++#ifdef CONFIG_BLOCK_LEGACY_AUTOLOAD + static void loop_probe(dev_t dev) + { + int idx = MINOR(dev) >> part_shift; + +- if (max_loop && idx >= max_loop) ++ if (max_loop_specified && max_loop && idx >= max_loop) + return; + loop_add(idx); + } ++#else ++#define loop_probe NULL ++#endif /* !CONFIG_BLOCK_LEGACY_AUTOLOAD */ + + static int loop_control_remove(int idx) + { +@@ -2277,6 +2310,9 @@ module_exit(loop_exit); + static int __init max_loop_setup(char *str) + { + max_loop = simple_strtol(str, NULL, 0); ++#ifdef CONFIG_BLOCK_LEGACY_AUTOLOAD ++ max_loop_specified = true; ++#endif + return 1; + } + +diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c +index c2f0f74193f0e..3fa74051f31b4 100644 +--- a/drivers/block/ublk_drv.c ++++ b/drivers/block/ublk_drv.c +@@ -103,6 +103,9 @@ struct ublk_uring_cmd_pdu { + */ + #define UBLK_IO_FLAG_NEED_GET_DATA 0x08 + ++/* atomic RW with ubq->cancel_lock */ ++#define UBLK_IO_FLAG_CANCELED 0x80000000 ++ + struct ublk_io { + /* userspace buffer address from io cmd */ + __u64 addr; +@@ -126,6 +129,7 @@ struct ublk_queue { + unsigned int max_io_sz; + bool force_abort; + unsigned short nr_io_ready; /* how many ios setup */ ++ spinlock_t cancel_lock; + struct ublk_device *dev; + struct ublk_io ios[]; + }; +@@ -1045,28 +1049,28 @@ static inline bool ublk_queue_ready(struct ublk_queue *ubq) + return ubq->nr_io_ready == ubq->q_depth; + } + +-static void ublk_cmd_cancel_cb(struct io_uring_cmd *cmd, unsigned issue_flags) +-{ +- io_uring_cmd_done(cmd, UBLK_IO_RES_ABORT, 0, issue_flags); +-} +- + static void ublk_cancel_queue(struct ublk_queue *ubq) + { + int i; + +- if (!ublk_queue_ready(ubq)) +- return; +- + for (i = 0; i < ubq->q_depth; i++) { + struct ublk_io *io = &ubq->ios[i]; + +- if (io->flags & UBLK_IO_FLAG_ACTIVE) +- io_uring_cmd_complete_in_task(io->cmd, +- ublk_cmd_cancel_cb); +- } ++ if (io->flags & UBLK_IO_FLAG_ACTIVE) { ++ bool done; + +- /* all io commands are canceled */ +- ubq->nr_io_ready = 0; ++ spin_lock(&ubq->cancel_lock); ++ done = !!(io->flags & UBLK_IO_FLAG_CANCELED); ++ if (!done) ++ io->flags |= UBLK_IO_FLAG_CANCELED; ++ spin_unlock(&ubq->cancel_lock); ++ ++ if (!done) ++ io_uring_cmd_done(io->cmd, ++ UBLK_IO_RES_ABORT, 0, ++ IO_URING_F_UNLOCKED); ++ } ++ } + } + + /* Cancel all pending commands, must be called after del_gendisk() returns */ +@@ -1113,7 +1117,6 @@ static void __ublk_quiesce_dev(struct ublk_device *ub) + blk_mq_quiesce_queue(ub->ub_disk->queue); + ublk_wait_tagset_rqs_idle(ub); + ub->dev_info.state = UBLK_S_DEV_QUIESCED; +- ublk_cancel_dev(ub); + /* we are going to release task_struct of ubq_daemon and resets + * ->ubq_daemon to NULL. So in monitor_work, check on ubq_daemon causes UAF. + * Besides, monitor_work is not necessary in QUIESCED state since we have +@@ -1136,6 +1139,7 @@ static void ublk_quiesce_work_fn(struct work_struct *work) + __ublk_quiesce_dev(ub); + unlock: + mutex_unlock(&ub->mutex); ++ ublk_cancel_dev(ub); + } + + static void ublk_unquiesce_dev(struct ublk_device *ub) +@@ -1175,8 +1179,8 @@ static void ublk_stop_dev(struct ublk_device *ub) + put_disk(ub->ub_disk); + ub->ub_disk = NULL; + unlock: +- ublk_cancel_dev(ub); + mutex_unlock(&ub->mutex); ++ ublk_cancel_dev(ub); + cancel_delayed_work_sync(&ub->monitor_work); + } + +@@ -1353,6 +1357,7 @@ static int ublk_init_queue(struct ublk_device *ub, int q_id) + void *ptr; + int size; + ++ spin_lock_init(&ubq->cancel_lock); + ubq->flags = ub->dev_info.flags; + ubq->q_id = q_id; + ubq->q_depth = ub->dev_info.queue_depth; +@@ -1882,8 +1887,9 @@ static void ublk_queue_reinit(struct ublk_device *ub, struct ublk_queue *ubq) + int i; + + WARN_ON_ONCE(!(ubq->ubq_daemon && ubq_daemon_is_dying(ubq))); ++ + /* All old ioucmds have to be completed */ +- WARN_ON_ONCE(ubq->nr_io_ready); ++ ubq->nr_io_ready = 0; + /* old daemon is PF_EXITING, put it now */ + put_task_struct(ubq->ubq_daemon); + /* We have to reset it to NULL, otherwise ub won't accept new FETCH_REQ */ +diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c +index 4415d850d698b..44dc91555aa00 100644 +--- a/drivers/bluetooth/hci_vhci.c ++++ b/drivers/bluetooth/hci_vhci.c +@@ -11,6 +11,7 @@ + #include + #include + ++#include + #include + #include + #include +@@ -44,6 +45,7 @@ struct vhci_data { + bool wakeup; + __u16 msft_opcode; + bool aosp_capable; ++ atomic_t initialized; + }; + + static int vhci_open_dev(struct hci_dev *hdev) +@@ -75,11 +77,10 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) + + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); + +- mutex_lock(&data->open_mutex); + skb_queue_tail(&data->readq, skb); +- mutex_unlock(&data->open_mutex); + +- wake_up_interruptible(&data->read_wait); ++ if (atomic_read(&data->initialized)) ++ wake_up_interruptible(&data->read_wait); + return 0; + } + +@@ -363,7 +364,8 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode) + skb_put_u8(skb, 0xff); + skb_put_u8(skb, opcode); + put_unaligned_le16(hdev->id, skb_put(skb, 2)); +- skb_queue_tail(&data->readq, skb); ++ skb_queue_head(&data->readq, skb); ++ atomic_inc(&data->initialized); + + wake_up_interruptible(&data->read_wait); + return 0; +diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c +index 59a2fe2448f17..15c6b85b125d4 100644 +--- a/drivers/bus/ti-sysc.c ++++ b/drivers/bus/ti-sysc.c +@@ -2174,13 +2174,23 @@ static int sysc_reset(struct sysc *ddata) + sysc_val = sysc_read_sysconfig(ddata); + sysc_val |= sysc_mask; + sysc_write(ddata, sysc_offset, sysc_val); +- /* Flush posted write */ ++ ++ /* ++ * Some devices need a delay before reading registers ++ * after reset. Presumably a srst_udelay is not needed ++ * for devices that use a rstctrl register reset. ++ */ ++ if (ddata->cfg.srst_udelay) ++ fsleep(ddata->cfg.srst_udelay); ++ ++ /* ++ * Flush posted write. For devices needing srst_udelay ++ * this should trigger an interconnect error if the ++ * srst_udelay value is needed but not configured. ++ */ + sysc_val = sysc_read_sysconfig(ddata); + } + +- if (ddata->cfg.srst_udelay) +- fsleep(ddata->cfg.srst_udelay); +- + if (ddata->post_reset_quirk) + ddata->post_reset_quirk(ddata); + +diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c +index c22fcaa44a614..6b7d47a52b10a 100644 +--- a/drivers/gpio/gpio-dwapb.c ++++ b/drivers/gpio/gpio-dwapb.c +@@ -283,13 +283,15 @@ static void dwapb_irq_enable(struct irq_data *d) + { + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct dwapb_gpio *gpio = to_dwapb_gpio(gc); ++ irq_hw_number_t hwirq = irqd_to_hwirq(d); + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&gc->bgpio_lock, flags); +- val = dwapb_read(gpio, GPIO_INTEN); +- val |= BIT(irqd_to_hwirq(d)); ++ val = dwapb_read(gpio, GPIO_INTEN) | BIT(hwirq); + dwapb_write(gpio, GPIO_INTEN, val); ++ val = dwapb_read(gpio, GPIO_INTMASK) & ~BIT(hwirq); ++ dwapb_write(gpio, GPIO_INTMASK, val); + raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); + } + +@@ -297,12 +299,14 @@ static void dwapb_irq_disable(struct irq_data *d) + { + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct dwapb_gpio *gpio = to_dwapb_gpio(gc); ++ irq_hw_number_t hwirq = irqd_to_hwirq(d); + unsigned long flags; + u32 val; + + raw_spin_lock_irqsave(&gc->bgpio_lock, flags); +- val = dwapb_read(gpio, GPIO_INTEN); +- val &= ~BIT(irqd_to_hwirq(d)); ++ val = dwapb_read(gpio, GPIO_INTMASK) | BIT(hwirq); ++ dwapb_write(gpio, GPIO_INTMASK, val); ++ val = dwapb_read(gpio, GPIO_INTEN) & ~BIT(hwirq); + dwapb_write(gpio, GPIO_INTEN, val); + raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); + } +diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c +index 6ab1cf489d035..e40c93f0960b4 100644 +--- a/drivers/gpio/gpiolib-cdev.c ++++ b/drivers/gpio/gpiolib-cdev.c +@@ -2444,10 +2444,7 @@ static int lineinfo_unwatch(struct gpio_chardev_data *cdev, void __user *ip) + return 0; + } + +-/* +- * gpio_ioctl() - ioctl handler for the GPIO chardev +- */ +-static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++static long gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) + { + struct gpio_chardev_data *cdev = file->private_data; + struct gpio_device *gdev = cdev->gdev; +@@ -2484,6 +2481,17 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + } + } + ++/* ++ * gpio_ioctl() - ioctl handler for the GPIO chardev ++ */ ++static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct gpio_chardev_data *cdev = file->private_data; ++ ++ return call_ioctl_locked(file, cmd, arg, cdev->gdev, ++ gpio_ioctl_unlocked); ++} ++ + #ifdef CONFIG_COMPAT + static long gpio_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg) +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +index 6d5f3c5fb4a62..13e0b521e3dba 100644 +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +@@ -5104,6 +5104,9 @@ static void fill_dc_dirty_rects(struct drm_plane *plane, + if (plane->type == DRM_PLANE_TYPE_CURSOR) + return; + ++ if (new_plane_state->rotation != DRM_MODE_ROTATE_0) ++ goto ffu; ++ + num_clips = drm_plane_get_damage_clips_count(new_plane_state); + clips = drm_plane_get_damage_clips(new_plane_state); + +diff --git a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h +index 848db8676adfd..46c2b991aa108 100644 +--- a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h ++++ b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h +@@ -465,6 +465,7 @@ struct dc_cursor_mi_param { + struct fixed31_32 v_scale_ratio; + enum dc_rotation_angle rotation; + bool mirror; ++ struct dc_stream_state *stream; + }; + + /* IPP related types */ +diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +index d84579da64003..009b5861a3fec 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +@@ -3427,7 +3427,8 @@ void dcn10_set_cursor_position(struct pipe_ctx *pipe_ctx) + .h_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.horz, + .v_scale_ratio = pipe_ctx->plane_res.scl_data.ratios.vert, + .rotation = pipe_ctx->plane_state->rotation, +- .mirror = pipe_ctx->plane_state->horizontal_mirror ++ .mirror = pipe_ctx->plane_state->horizontal_mirror, ++ .stream = pipe_ctx->stream, + }; + bool pipe_split_on = false; + bool odm_combine_on = (pipe_ctx->next_odm_pipe != NULL) || +diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubp.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubp.c +index 4566bc7abf17e..aa252dc263267 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubp.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubp.c +@@ -1075,8 +1075,16 @@ void hubp2_cursor_set_position( + if (src_y_offset < 0) + src_y_offset = 0; + /* Save necessary cursor info x, y position. w, h is saved in attribute func. */ +- hubp->cur_rect.x = src_x_offset + param->viewport.x; +- hubp->cur_rect.y = src_y_offset + param->viewport.y; ++ if (param->stream->link->psr_settings.psr_version >= DC_PSR_VERSION_SU_1 && ++ param->rotation != ROTATION_ANGLE_0) { ++ hubp->cur_rect.x = 0; ++ hubp->cur_rect.y = 0; ++ hubp->cur_rect.w = param->stream->timing.h_addressable; ++ hubp->cur_rect.h = param->stream->timing.v_addressable; ++ } else { ++ hubp->cur_rect.x = src_x_offset + param->viewport.x; ++ hubp->cur_rect.y = src_y_offset + param->viewport.y; ++ } + } + + void hubp2_clk_cntl(struct hubp *hubp, bool enable) +diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c +index 53262f6bc40b0..72bec33e371f3 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c +@@ -994,5 +994,8 @@ void dcn30_prepare_bandwidth(struct dc *dc, + dc->clk_mgr->funcs->set_max_memclk(dc->clk_mgr, dc->clk_mgr->bw_params->clk_table.entries[dc->clk_mgr->bw_params->clk_table.num_entries - 1].memclk_mhz); + + dcn20_prepare_bandwidth(dc, context); ++ ++ dc_dmub_srv_p_state_delegate(dc, ++ context->bw_ctx.bw.dcn.clk.fw_based_mclk_switching, context); + } + +diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c +index 18f0a5ae3bacd..a502af0b6dd47 100644 +--- a/drivers/gpu/drm/i915/display/intel_atomic.c ++++ b/drivers/gpu/drm/i915/display/intel_atomic.c +@@ -41,6 +41,7 @@ + #include "intel_global_state.h" + #include "intel_hdcp.h" + #include "intel_psr.h" ++#include "intel_fb.h" + #include "skl_universal_plane.h" + + /** +@@ -302,198 +303,6 @@ intel_crtc_destroy_state(struct drm_crtc *crtc, + kfree(crtc_state); + } + +-static void intel_atomic_setup_scaler(struct intel_crtc_scaler_state *scaler_state, +- int num_scalers_need, struct intel_crtc *intel_crtc, +- const char *name, int idx, +- struct intel_plane_state *plane_state, +- int *scaler_id) +-{ +- struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); +- int j; +- u32 mode; +- +- if (*scaler_id < 0) { +- /* find a free scaler */ +- for (j = 0; j < intel_crtc->num_scalers; j++) { +- if (scaler_state->scalers[j].in_use) +- continue; +- +- *scaler_id = j; +- scaler_state->scalers[*scaler_id].in_use = 1; +- break; +- } +- } +- +- if (drm_WARN(&dev_priv->drm, *scaler_id < 0, +- "Cannot find scaler for %s:%d\n", name, idx)) +- return; +- +- /* set scaler mode */ +- if (plane_state && plane_state->hw.fb && +- plane_state->hw.fb->format->is_yuv && +- plane_state->hw.fb->format->num_planes > 1) { +- struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); +- if (DISPLAY_VER(dev_priv) == 9) { +- mode = SKL_PS_SCALER_MODE_NV12; +- } else if (icl_is_hdr_plane(dev_priv, plane->id)) { +- /* +- * On gen11+'s HDR planes we only use the scaler for +- * scaling. They have a dedicated chroma upsampler, so +- * we don't need the scaler to upsample the UV plane. +- */ +- mode = PS_SCALER_MODE_NORMAL; +- } else { +- struct intel_plane *linked = +- plane_state->planar_linked_plane; +- +- mode = PS_SCALER_MODE_PLANAR; +- +- if (linked) +- mode |= PS_PLANE_Y_SEL(linked->id); +- } +- } else if (DISPLAY_VER(dev_priv) >= 10) { +- mode = PS_SCALER_MODE_NORMAL; +- } else if (num_scalers_need == 1 && intel_crtc->num_scalers > 1) { +- /* +- * when only 1 scaler is in use on a pipe with 2 scalers +- * scaler 0 operates in high quality (HQ) mode. +- * In this case use scaler 0 to take advantage of HQ mode +- */ +- scaler_state->scalers[*scaler_id].in_use = 0; +- *scaler_id = 0; +- scaler_state->scalers[0].in_use = 1; +- mode = SKL_PS_SCALER_MODE_HQ; +- } else { +- mode = SKL_PS_SCALER_MODE_DYN; +- } +- +- drm_dbg_kms(&dev_priv->drm, "Attached scaler id %u.%u to %s:%d\n", +- intel_crtc->pipe, *scaler_id, name, idx); +- scaler_state->scalers[*scaler_id].mode = mode; +-} +- +-/** +- * intel_atomic_setup_scalers() - setup scalers for crtc per staged requests +- * @dev_priv: i915 device +- * @intel_crtc: intel crtc +- * @crtc_state: incoming crtc_state to validate and setup scalers +- * +- * This function sets up scalers based on staged scaling requests for +- * a @crtc and its planes. It is called from crtc level check path. If request +- * is a supportable request, it attaches scalers to requested planes and crtc. +- * +- * This function takes into account the current scaler(s) in use by any planes +- * not being part of this atomic state +- * +- * Returns: +- * 0 - scalers were setup succesfully +- * error code - otherwise +- */ +-int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv, +- struct intel_crtc *intel_crtc, +- struct intel_crtc_state *crtc_state) +-{ +- struct drm_plane *plane = NULL; +- struct intel_plane *intel_plane; +- struct intel_plane_state *plane_state = NULL; +- struct intel_crtc_scaler_state *scaler_state = +- &crtc_state->scaler_state; +- struct drm_atomic_state *drm_state = crtc_state->uapi.state; +- struct intel_atomic_state *intel_state = to_intel_atomic_state(drm_state); +- int num_scalers_need; +- int i; +- +- num_scalers_need = hweight32(scaler_state->scaler_users); +- +- /* +- * High level flow: +- * - staged scaler requests are already in scaler_state->scaler_users +- * - check whether staged scaling requests can be supported +- * - add planes using scalers that aren't in current transaction +- * - assign scalers to requested users +- * - as part of plane commit, scalers will be committed +- * (i.e., either attached or detached) to respective planes in hw +- * - as part of crtc_commit, scaler will be either attached or detached +- * to crtc in hw +- */ +- +- /* fail if required scalers > available scalers */ +- if (num_scalers_need > intel_crtc->num_scalers){ +- drm_dbg_kms(&dev_priv->drm, +- "Too many scaling requests %d > %d\n", +- num_scalers_need, intel_crtc->num_scalers); +- return -EINVAL; +- } +- +- /* walkthrough scaler_users bits and start assigning scalers */ +- for (i = 0; i < sizeof(scaler_state->scaler_users) * 8; i++) { +- int *scaler_id; +- const char *name; +- int idx; +- +- /* skip if scaler not required */ +- if (!(scaler_state->scaler_users & (1 << i))) +- continue; +- +- if (i == SKL_CRTC_INDEX) { +- name = "CRTC"; +- idx = intel_crtc->base.base.id; +- +- /* panel fitter case: assign as a crtc scaler */ +- scaler_id = &scaler_state->scaler_id; +- } else { +- name = "PLANE"; +- +- /* plane scaler case: assign as a plane scaler */ +- /* find the plane that set the bit as scaler_user */ +- plane = drm_state->planes[i].ptr; +- +- /* +- * to enable/disable hq mode, add planes that are using scaler +- * into this transaction +- */ +- if (!plane) { +- struct drm_plane_state *state; +- +- /* +- * GLK+ scalers don't have a HQ mode so it +- * isn't necessary to change between HQ and dyn mode +- * on those platforms. +- */ +- if (DISPLAY_VER(dev_priv) >= 10) +- continue; +- +- plane = drm_plane_from_index(&dev_priv->drm, i); +- state = drm_atomic_get_plane_state(drm_state, plane); +- if (IS_ERR(state)) { +- drm_dbg_kms(&dev_priv->drm, +- "Failed to add [PLANE:%d] to drm_state\n", +- plane->base.id); +- return PTR_ERR(state); +- } +- } +- +- intel_plane = to_intel_plane(plane); +- idx = plane->base.id; +- +- /* plane on different crtc cannot be a scaler user of this crtc */ +- if (drm_WARN_ON(&dev_priv->drm, +- intel_plane->pipe != intel_crtc->pipe)) +- continue; +- +- plane_state = intel_atomic_get_new_plane_state(intel_state, +- intel_plane); +- scaler_id = &plane_state->scaler_id; +- } +- +- intel_atomic_setup_scaler(scaler_state, num_scalers_need, +- intel_crtc, name, idx, +- plane_state, scaler_id); +- } +- +- return 0; +-} +- + struct drm_atomic_state * + intel_atomic_state_alloc(struct drm_device *dev) + { +diff --git a/drivers/gpu/drm/i915/display/intel_atomic.h b/drivers/gpu/drm/i915/display/intel_atomic.h +index 1dc439983dd94..e506f6a873447 100644 +--- a/drivers/gpu/drm/i915/display/intel_atomic.h ++++ b/drivers/gpu/drm/i915/display/intel_atomic.h +@@ -52,8 +52,4 @@ struct intel_crtc_state * + intel_atomic_get_crtc_state(struct drm_atomic_state *state, + struct intel_crtc *crtc); + +-int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv, +- struct intel_crtc *intel_crtc, +- struct intel_crtc_state *crtc_state); +- + #endif /* __INTEL_ATOMIC_H__ */ +diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c +index 1777a12f2f421..fb8d1d63407a2 100644 +--- a/drivers/gpu/drm/i915/display/intel_display.c ++++ b/drivers/gpu/drm/i915/display/intel_display.c +@@ -6481,6 +6481,17 @@ static int intel_async_flip_check_uapi(struct intel_atomic_state *state, + return -EINVAL; + } + ++ /* ++ * FIXME: Bigjoiner+async flip is busted currently. ++ * Remove this check once the issues are fixed. ++ */ ++ if (new_crtc_state->bigjoiner_pipes) { ++ drm_dbg_kms(&i915->drm, ++ "[CRTC:%d:%s] async flip disallowed with bigjoiner\n", ++ crtc->base.base.id, crtc->base.name); ++ return -EINVAL; ++ } ++ + for_each_oldnew_intel_plane_in_state(state, plane, old_plane_state, + new_plane_state, i) { + if (plane->pipe != crtc->pipe) +diff --git a/drivers/gpu/drm/i915/display/intel_fb.c b/drivers/gpu/drm/i915/display/intel_fb.c +index 23d854bd73b77..c69a638796c62 100644 +--- a/drivers/gpu/drm/i915/display/intel_fb.c ++++ b/drivers/gpu/drm/i915/display/intel_fb.c +@@ -1176,7 +1176,8 @@ bool intel_fb_needs_pot_stride_remap(const struct intel_framebuffer *fb) + { + struct drm_i915_private *i915 = to_i915(fb->base.dev); + +- return IS_ALDERLAKE_P(i915) && fb->base.modifier != DRM_FORMAT_MOD_LINEAR; ++ return (IS_ALDERLAKE_P(i915) || DISPLAY_VER(i915) >= 14) && ++ intel_fb_uses_dpt(&fb->base); + } + + static int intel_fb_pitch(const struct intel_framebuffer *fb, int color_plane, unsigned int rotation) +@@ -1312,9 +1313,11 @@ plane_view_scanout_stride(const struct intel_framebuffer *fb, int color_plane, + unsigned int tile_width, + unsigned int src_stride_tiles, unsigned int dst_stride_tiles) + { ++ struct drm_i915_private *i915 = to_i915(fb->base.dev); + unsigned int stride_tiles; + +- if (IS_ALDERLAKE_P(to_i915(fb->base.dev))) ++ if ((IS_ALDERLAKE_P(i915) || DISPLAY_VER(i915) >= 14) && ++ src_stride_tiles < dst_stride_tiles) + stride_tiles = src_stride_tiles; + else + stride_tiles = dst_stride_tiles; +@@ -1520,7 +1523,8 @@ static void intel_fb_view_init(struct drm_i915_private *i915, struct intel_fb_vi + memset(view, 0, sizeof(*view)); + view->gtt.type = view_type; + +- if (view_type == I915_GTT_VIEW_REMAPPED && IS_ALDERLAKE_P(i915)) ++ if (view_type == I915_GTT_VIEW_REMAPPED && ++ (IS_ALDERLAKE_P(i915) || DISPLAY_VER(i915) >= 14)) + view->gtt.remapped.plane_alignment = SZ_2M / PAGE_SIZE; + } + +diff --git a/drivers/gpu/drm/i915/display/skl_scaler.c b/drivers/gpu/drm/i915/display/skl_scaler.c +index 90f42f63128ec..0b74f91e865d0 100644 +--- a/drivers/gpu/drm/i915/display/skl_scaler.c ++++ b/drivers/gpu/drm/i915/display/skl_scaler.c +@@ -337,6 +337,263 @@ int skl_update_scaler_plane(struct intel_crtc_state *crtc_state, + return 0; + } + ++static int intel_atomic_setup_scaler(struct intel_crtc_scaler_state *scaler_state, ++ int num_scalers_need, struct intel_crtc *intel_crtc, ++ const char *name, int idx, ++ struct intel_plane_state *plane_state, ++ int *scaler_id) ++{ ++ struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); ++ int j; ++ u32 mode; ++ ++ if (*scaler_id < 0) { ++ /* find a free scaler */ ++ for (j = 0; j < intel_crtc->num_scalers; j++) { ++ if (scaler_state->scalers[j].in_use) ++ continue; ++ ++ *scaler_id = j; ++ scaler_state->scalers[*scaler_id].in_use = 1; ++ break; ++ } ++ } ++ ++ if (drm_WARN(&dev_priv->drm, *scaler_id < 0, ++ "Cannot find scaler for %s:%d\n", name, idx)) ++ return -EINVAL; ++ ++ /* set scaler mode */ ++ if (plane_state && plane_state->hw.fb && ++ plane_state->hw.fb->format->is_yuv && ++ plane_state->hw.fb->format->num_planes > 1) { ++ struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); ++ ++ if (DISPLAY_VER(dev_priv) == 9) { ++ mode = SKL_PS_SCALER_MODE_NV12; ++ } else if (icl_is_hdr_plane(dev_priv, plane->id)) { ++ /* ++ * On gen11+'s HDR planes we only use the scaler for ++ * scaling. They have a dedicated chroma upsampler, so ++ * we don't need the scaler to upsample the UV plane. ++ */ ++ mode = PS_SCALER_MODE_NORMAL; ++ } else { ++ struct intel_plane *linked = ++ plane_state->planar_linked_plane; ++ ++ mode = PS_SCALER_MODE_PLANAR; ++ ++ if (linked) ++ mode |= PS_PLANE_Y_SEL(linked->id); ++ } ++ } else if (DISPLAY_VER(dev_priv) >= 10) { ++ mode = PS_SCALER_MODE_NORMAL; ++ } else if (num_scalers_need == 1 && intel_crtc->num_scalers > 1) { ++ /* ++ * when only 1 scaler is in use on a pipe with 2 scalers ++ * scaler 0 operates in high quality (HQ) mode. ++ * In this case use scaler 0 to take advantage of HQ mode ++ */ ++ scaler_state->scalers[*scaler_id].in_use = 0; ++ *scaler_id = 0; ++ scaler_state->scalers[0].in_use = 1; ++ mode = SKL_PS_SCALER_MODE_HQ; ++ } else { ++ mode = SKL_PS_SCALER_MODE_DYN; ++ } ++ ++ /* ++ * FIXME: we should also check the scaler factors for pfit, so ++ * this shouldn't be tied directly to planes. ++ */ ++ if (plane_state && plane_state->hw.fb) { ++ const struct drm_framebuffer *fb = plane_state->hw.fb; ++ const struct drm_rect *src = &plane_state->uapi.src; ++ const struct drm_rect *dst = &plane_state->uapi.dst; ++ int hscale, vscale, max_vscale, max_hscale; ++ ++ /* ++ * FIXME: When two scalers are needed, but only one of ++ * them needs to downscale, we should make sure that ++ * the one that needs downscaling support is assigned ++ * as the first scaler, so we don't reject downscaling ++ * unnecessarily. ++ */ ++ ++ if (DISPLAY_VER(dev_priv) >= 14) { ++ /* ++ * On versions 14 and up, only the first ++ * scaler supports a vertical scaling factor ++ * of more than 1.0, while a horizontal ++ * scaling factor of 3.0 is supported. ++ */ ++ max_hscale = 0x30000 - 1; ++ if (*scaler_id == 0) ++ max_vscale = 0x30000 - 1; ++ else ++ max_vscale = 0x10000; ++ ++ } else if (DISPLAY_VER(dev_priv) >= 10 || ++ !intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) { ++ max_hscale = 0x30000 - 1; ++ max_vscale = 0x30000 - 1; ++ } else { ++ max_hscale = 0x20000 - 1; ++ max_vscale = 0x20000 - 1; ++ } ++ ++ /* ++ * FIXME: We should change the if-else block above to ++ * support HQ vs dynamic scaler properly. ++ */ ++ ++ /* Check if required scaling is within limits */ ++ hscale = drm_rect_calc_hscale(src, dst, 1, max_hscale); ++ vscale = drm_rect_calc_vscale(src, dst, 1, max_vscale); ++ ++ if (hscale < 0 || vscale < 0) { ++ drm_dbg_kms(&dev_priv->drm, ++ "Scaler %d doesn't support required plane scaling\n", ++ *scaler_id); ++ drm_rect_debug_print("src: ", src, true); ++ drm_rect_debug_print("dst: ", dst, false); ++ ++ return -EINVAL; ++ } ++ } ++ ++ drm_dbg_kms(&dev_priv->drm, "Attached scaler id %u.%u to %s:%d\n", ++ intel_crtc->pipe, *scaler_id, name, idx); ++ scaler_state->scalers[*scaler_id].mode = mode; ++ ++ return 0; ++} ++ ++/** ++ * intel_atomic_setup_scalers() - setup scalers for crtc per staged requests ++ * @dev_priv: i915 device ++ * @intel_crtc: intel crtc ++ * @crtc_state: incoming crtc_state to validate and setup scalers ++ * ++ * This function sets up scalers based on staged scaling requests for ++ * a @crtc and its planes. It is called from crtc level check path. If request ++ * is a supportable request, it attaches scalers to requested planes and crtc. ++ * ++ * This function takes into account the current scaler(s) in use by any planes ++ * not being part of this atomic state ++ * ++ * Returns: ++ * 0 - scalers were setup successfully ++ * error code - otherwise ++ */ ++int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv, ++ struct intel_crtc *intel_crtc, ++ struct intel_crtc_state *crtc_state) ++{ ++ struct drm_plane *plane = NULL; ++ struct intel_plane *intel_plane; ++ struct intel_crtc_scaler_state *scaler_state = ++ &crtc_state->scaler_state; ++ struct drm_atomic_state *drm_state = crtc_state->uapi.state; ++ struct intel_atomic_state *intel_state = to_intel_atomic_state(drm_state); ++ int num_scalers_need; ++ int i; ++ ++ num_scalers_need = hweight32(scaler_state->scaler_users); ++ ++ /* ++ * High level flow: ++ * - staged scaler requests are already in scaler_state->scaler_users ++ * - check whether staged scaling requests can be supported ++ * - add planes using scalers that aren't in current transaction ++ * - assign scalers to requested users ++ * - as part of plane commit, scalers will be committed ++ * (i.e., either attached or detached) to respective planes in hw ++ * - as part of crtc_commit, scaler will be either attached or detached ++ * to crtc in hw ++ */ ++ ++ /* fail if required scalers > available scalers */ ++ if (num_scalers_need > intel_crtc->num_scalers) { ++ drm_dbg_kms(&dev_priv->drm, ++ "Too many scaling requests %d > %d\n", ++ num_scalers_need, intel_crtc->num_scalers); ++ return -EINVAL; ++ } ++ ++ /* walkthrough scaler_users bits and start assigning scalers */ ++ for (i = 0; i < sizeof(scaler_state->scaler_users) * 8; i++) { ++ struct intel_plane_state *plane_state = NULL; ++ int *scaler_id; ++ const char *name; ++ int idx, ret; ++ ++ /* skip if scaler not required */ ++ if (!(scaler_state->scaler_users & (1 << i))) ++ continue; ++ ++ if (i == SKL_CRTC_INDEX) { ++ name = "CRTC"; ++ idx = intel_crtc->base.base.id; ++ ++ /* panel fitter case: assign as a crtc scaler */ ++ scaler_id = &scaler_state->scaler_id; ++ } else { ++ name = "PLANE"; ++ ++ /* plane scaler case: assign as a plane scaler */ ++ /* find the plane that set the bit as scaler_user */ ++ plane = drm_state->planes[i].ptr; ++ ++ /* ++ * to enable/disable hq mode, add planes that are using scaler ++ * into this transaction ++ */ ++ if (!plane) { ++ struct drm_plane_state *state; ++ ++ /* ++ * GLK+ scalers don't have a HQ mode so it ++ * isn't necessary to change between HQ and dyn mode ++ * on those platforms. ++ */ ++ if (DISPLAY_VER(dev_priv) >= 10) ++ continue; ++ ++ plane = drm_plane_from_index(&dev_priv->drm, i); ++ state = drm_atomic_get_plane_state(drm_state, plane); ++ if (IS_ERR(state)) { ++ drm_dbg_kms(&dev_priv->drm, ++ "Failed to add [PLANE:%d] to drm_state\n", ++ plane->base.id); ++ return PTR_ERR(state); ++ } ++ } ++ ++ intel_plane = to_intel_plane(plane); ++ idx = plane->base.id; ++ ++ /* plane on different crtc cannot be a scaler user of this crtc */ ++ if (drm_WARN_ON(&dev_priv->drm, ++ intel_plane->pipe != intel_crtc->pipe)) ++ continue; ++ ++ plane_state = intel_atomic_get_new_plane_state(intel_state, ++ intel_plane); ++ scaler_id = &plane_state->scaler_id; ++ } ++ ++ ret = intel_atomic_setup_scaler(scaler_state, num_scalers_need, ++ intel_crtc, name, idx, ++ plane_state, scaler_id); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int glk_coef_tap(int i) + { + return i % 7; +diff --git a/drivers/gpu/drm/i915/display/skl_scaler.h b/drivers/gpu/drm/i915/display/skl_scaler.h +index 0097d5d08e102..f040f6ac061f2 100644 +--- a/drivers/gpu/drm/i915/display/skl_scaler.h ++++ b/drivers/gpu/drm/i915/display/skl_scaler.h +@@ -8,17 +8,22 @@ + #include + + enum drm_scaling_filter; ++enum pipe; + struct drm_i915_private; ++struct intel_crtc; + struct intel_crtc_state; +-struct intel_plane_state; + struct intel_plane; +-enum pipe; ++struct intel_plane_state; + + int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state); + + int skl_update_scaler_plane(struct intel_crtc_state *crtc_state, + struct intel_plane_state *plane_state); + ++int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv, ++ struct intel_crtc *intel_crtc, ++ struct intel_crtc_state *crtc_state); ++ + void skl_pfit_enable(const struct intel_crtc_state *crtc_state); + + void skl_program_plane_scaler(struct intel_plane *plane, +@@ -26,4 +31,5 @@ void skl_program_plane_scaler(struct intel_plane *plane, + const struct intel_plane_state *plane_state); + void skl_detach_scalers(const struct intel_crtc_state *crtc_state); + void skl_scaler_disable(const struct intel_crtc_state *old_crtc_state); ++ + #endif +diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.c b/drivers/hid/i2c-hid/i2c-hid-acpi.c +index b96ae15e0ad91..6d35bb3974818 100644 +--- a/drivers/hid/i2c-hid/i2c-hid-acpi.c ++++ b/drivers/hid/i2c-hid/i2c-hid-acpi.c +@@ -39,8 +39,13 @@ static const struct acpi_device_id i2c_hid_acpi_blacklist[] = { + * The CHPN0001 ACPI device, which is used to describe the Chipone + * ICN8505 controller, has a _CID of PNP0C50 but is not HID compatible. + */ +- {"CHPN0001", 0 }, +- { }, ++ { "CHPN0001" }, ++ /* ++ * The IDEA5002 ACPI device causes high interrupt usage and spurious ++ * wakeups from suspend. ++ */ ++ { "IDEA5002" }, ++ { } + }; + + /* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */ +@@ -115,9 +120,9 @@ static int i2c_hid_acpi_probe(struct i2c_client *client) + } + + static const struct acpi_device_id i2c_hid_acpi_match[] = { +- {"ACPI0C50", 0 }, +- {"PNP0C50", 0 }, +- { }, ++ { "ACPI0C50" }, ++ { "PNP0C50" }, ++ { } + }; + MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match); + +diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c +index 6adf3b141316b..86daf791aa27c 100644 +--- a/drivers/i2c/busses/i2c-aspeed.c ++++ b/drivers/i2c/busses/i2c-aspeed.c +@@ -249,18 +249,46 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + if (!slave) + return 0; + +- command = readl(bus->base + ASPEED_I2C_CMD_REG); ++ /* ++ * Handle stop conditions early, prior to SLAVE_MATCH. Some masters may drive ++ * transfers with low enough latency between the nak/stop phase of the current ++ * command and the start/address phase of the following command that the ++ * interrupts are coalesced by the time we process them. ++ */ ++ if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) { ++ irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; ++ bus->slave_state = ASPEED_I2C_SLAVE_STOP; ++ } ++ ++ if (irq_status & ASPEED_I2CD_INTR_TX_NAK && ++ bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { ++ irq_handled |= ASPEED_I2CD_INTR_TX_NAK; ++ bus->slave_state = ASPEED_I2C_SLAVE_STOP; ++ } ++ ++ /* Propagate any stop conditions to the slave implementation. */ ++ if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) { ++ i2c_slave_event(slave, I2C_SLAVE_STOP, &value); ++ bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; ++ } + +- /* Slave was requested, restart state machine. */ ++ /* ++ * Now that we've dealt with any potentially coalesced stop conditions, ++ * address any start conditions. ++ */ + if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) { + irq_handled |= ASPEED_I2CD_INTR_SLAVE_MATCH; + bus->slave_state = ASPEED_I2C_SLAVE_START; + } + +- /* Slave is not currently active, irq was for someone else. */ ++ /* ++ * If the slave has been stopped and not started then slave interrupt ++ * handling is complete. ++ */ + if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) + return irq_handled; + ++ command = readl(bus->base + ASPEED_I2C_CMD_REG); + dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", + irq_status, command); + +@@ -279,17 +307,6 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + irq_handled |= ASPEED_I2CD_INTR_RX_DONE; + } + +- /* Slave was asked to stop. */ +- if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) { +- irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; +- bus->slave_state = ASPEED_I2C_SLAVE_STOP; +- } +- if (irq_status & ASPEED_I2CD_INTR_TX_NAK && +- bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { +- irq_handled |= ASPEED_I2CD_INTR_TX_NAK; +- bus->slave_state = ASPEED_I2C_SLAVE_STOP; +- } +- + switch (bus->slave_state) { + case ASPEED_I2C_SLAVE_READ_REQUESTED: + if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK)) +@@ -324,8 +341,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) + i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value); + break; + case ASPEED_I2C_SLAVE_STOP: +- i2c_slave_event(slave, I2C_SLAVE_STOP, &value); +- bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; ++ /* Stop event handling is done early. Unreachable. */ + break; + case ASPEED_I2C_SLAVE_START: + /* Slave was just started. Waiting for the next event. */; +diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c +index 642c5c4895e37..3ac253a27dd97 100644 +--- a/drivers/iio/adc/ti_am335x_adc.c ++++ b/drivers/iio/adc/ti_am335x_adc.c +@@ -671,8 +671,10 @@ static int tiadc_probe(struct platform_device *pdev) + platform_set_drvdata(pdev, indio_dev); + + err = tiadc_request_dma(pdev, adc_dev); +- if (err && err == -EPROBE_DEFER) ++ if (err && err != -ENODEV) { ++ dev_err_probe(&pdev->dev, err, "DMA request failed\n"); + goto err_dma; ++ } + + return 0; + +diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c +index 8d4fc97d10059..2b7873e8a959b 100644 +--- a/drivers/iio/buffer/industrialio-triggered-buffer.c ++++ b/drivers/iio/buffer/industrialio-triggered-buffer.c +@@ -46,6 +46,16 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, + struct iio_buffer *buffer; + int ret; + ++ /* ++ * iio_triggered_buffer_cleanup() assumes that the buffer allocated here ++ * is assigned to indio_dev->buffer but this is only the case if this ++ * function is the first caller to iio_device_attach_buffer(). If ++ * indio_dev->buffer is already set then we can't proceed otherwise the ++ * cleanup function will try to free a buffer that was not allocated here. ++ */ ++ if (indio_dev->buffer) ++ return -EADDRINUSE; ++ + buffer = iio_kfifo_allocate(); + if (!buffer) { + ret = -ENOMEM; +diff --git a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c +index 6633b35a94e69..9c9bc77003c7f 100644 +--- a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c ++++ b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c +@@ -15,8 +15,8 @@ + /* Conversion times in us */ + static const u16 ms_sensors_ht_t_conversion_time[] = { 50000, 25000, + 13000, 7000 }; +-static const u16 ms_sensors_ht_h_conversion_time[] = { 16000, 3000, +- 5000, 8000 }; ++static const u16 ms_sensors_ht_h_conversion_time[] = { 16000, 5000, ++ 3000, 8000 }; + static const u16 ms_sensors_tp_conversion_time[] = { 500, 1100, 2100, + 4100, 8220, 16440 }; + +diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +index 86fbbe9040503..19a1ef5351d24 100644 +--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c ++++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +@@ -736,13 +736,13 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev, + ret = inv_mpu6050_sensor_show(st, st->reg->gyro_offset, + chan->channel2, val); + mutex_unlock(&st->lock); +- return IIO_VAL_INT; ++ return ret; + case IIO_ACCEL: + mutex_lock(&st->lock); + ret = inv_mpu6050_sensor_show(st, st->reg->accl_offset, + chan->channel2, val); + mutex_unlock(&st->lock); +- return IIO_VAL_INT; ++ return ret; + + default: + return -EINVAL; +diff --git a/drivers/input/keyboard/ipaq-micro-keys.c b/drivers/input/keyboard/ipaq-micro-keys.c +index 13a66a8e3411f..e0c51189e329c 100644 +--- a/drivers/input/keyboard/ipaq-micro-keys.c ++++ b/drivers/input/keyboard/ipaq-micro-keys.c +@@ -105,6 +105,9 @@ static int micro_key_probe(struct platform_device *pdev) + keys->codes = devm_kmemdup(&pdev->dev, micro_keycodes, + keys->input->keycodesize * keys->input->keycodemax, + GFP_KERNEL); ++ if (!keys->codes) ++ return -ENOMEM; ++ + keys->input->keycode = keys->codes; + + __set_bit(EV_KEY, keys->input->evbit); +diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c +index e79f5497948b8..9116f4248fd09 100644 +--- a/drivers/input/misc/soc_button_array.c ++++ b/drivers/input/misc/soc_button_array.c +@@ -299,6 +299,11 @@ static int soc_button_parse_btn_desc(struct device *dev, + info->name = "power"; + info->event_code = KEY_POWER; + info->wakeup = true; ++ } else if (upage == 0x01 && usage == 0xc6) { ++ info->name = "airplane mode switch"; ++ info->event_type = EV_SW; ++ info->event_code = SW_RFKILL_ALL; ++ info->active_low = false; + } else if (upage == 0x01 && usage == 0xca) { + info->name = "rotation lock switch"; + info->event_type = EV_SW; +diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c +index 0c6fc954e7296..1d9494f64a215 100644 +--- a/drivers/interconnect/core.c ++++ b/drivers/interconnect/core.c +@@ -381,6 +381,9 @@ struct icc_node_data *of_icc_get_from_provider(struct of_phandle_args *spec) + } + mutex_unlock(&icc_lock); + ++ if (!node) ++ return ERR_PTR(-EINVAL); ++ + if (IS_ERR(node)) + return ERR_CAST(node); + +diff --git a/drivers/interconnect/qcom/sm8250.c b/drivers/interconnect/qcom/sm8250.c +index 5cdb058fa0959..9c2dd40d9a559 100644 +--- a/drivers/interconnect/qcom/sm8250.c ++++ b/drivers/interconnect/qcom/sm8250.c +@@ -551,6 +551,7 @@ static struct platform_driver qnoc_driver = { + .driver = { + .name = "qnoc-sm8250", + .of_match_table = qnoc_of_match, ++ .sync_state = icc_sync_state, + }, + }; + module_platform_driver(qnoc_driver); +diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c +index 382c5cc471952..100a6a236d92a 100644 +--- a/drivers/md/dm-bufio.c ++++ b/drivers/md/dm-bufio.c +@@ -1914,6 +1914,13 @@ void dm_bufio_client_destroy(struct dm_bufio_client *c) + } + EXPORT_SYMBOL_GPL(dm_bufio_client_destroy); + ++void dm_bufio_client_reset(struct dm_bufio_client *c) ++{ ++ drop_buffers(c); ++ flush_work(&c->shrink_work); ++} ++EXPORT_SYMBOL_GPL(dm_bufio_client_reset); ++ + void dm_bufio_set_sector_offset(struct dm_bufio_client *c, sector_t start) + { + c->start = start; +diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c +index fe7dad3ffa75f..77fcff82c82ac 100644 +--- a/drivers/md/dm-integrity.c ++++ b/drivers/md/dm-integrity.c +@@ -1763,11 +1763,12 @@ static void integrity_metadata(struct work_struct *w) + sectors_to_process = dio->range.n_sectors; + + __bio_for_each_segment(bv, bio, iter, dio->bio_details.bi_iter) { ++ struct bio_vec bv_copy = bv; + unsigned int pos; + char *mem, *checksums_ptr; + + again: +- mem = bvec_kmap_local(&bv); ++ mem = bvec_kmap_local(&bv_copy); + pos = 0; + checksums_ptr = checksums; + do { +@@ -1776,7 +1777,7 @@ again: + sectors_to_process -= ic->sectors_per_block; + pos += ic->sectors_per_block << SECTOR_SHIFT; + sector += ic->sectors_per_block; +- } while (pos < bv.bv_len && sectors_to_process && checksums != checksums_onstack); ++ } while (pos < bv_copy.bv_len && sectors_to_process && checksums != checksums_onstack); + kunmap_local(mem); + + r = dm_integrity_rw_tag(ic, checksums, &dio->metadata_block, &dio->metadata_offset, +@@ -1801,9 +1802,9 @@ again: + if (!sectors_to_process) + break; + +- if (unlikely(pos < bv.bv_len)) { +- bv.bv_offset += pos; +- bv.bv_len -= pos; ++ if (unlikely(pos < bv_copy.bv_len)) { ++ bv_copy.bv_offset += pos; ++ bv_copy.bv_len -= pos; + goto again; + } + } +diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c +index 4a0e15109997b..bb0e0a270f62a 100644 +--- a/drivers/md/dm-thin-metadata.c ++++ b/drivers/md/dm-thin-metadata.c +@@ -597,6 +597,8 @@ static int __format_metadata(struct dm_pool_metadata *pmd) + r = dm_tm_create_with_sm(pmd->bm, THIN_SUPERBLOCK_LOCATION, + &pmd->tm, &pmd->metadata_sm); + if (r < 0) { ++ pmd->tm = NULL; ++ pmd->metadata_sm = NULL; + DMERR("tm_create_with_sm failed"); + return r; + } +@@ -605,6 +607,7 @@ static int __format_metadata(struct dm_pool_metadata *pmd) + if (IS_ERR(pmd->data_sm)) { + DMERR("sm_disk_create failed"); + r = PTR_ERR(pmd->data_sm); ++ pmd->data_sm = NULL; + goto bad_cleanup_tm; + } + +@@ -635,11 +638,15 @@ static int __format_metadata(struct dm_pool_metadata *pmd) + + bad_cleanup_nb_tm: + dm_tm_destroy(pmd->nb_tm); ++ pmd->nb_tm = NULL; + bad_cleanup_data_sm: + dm_sm_destroy(pmd->data_sm); ++ pmd->data_sm = NULL; + bad_cleanup_tm: + dm_tm_destroy(pmd->tm); ++ pmd->tm = NULL; + dm_sm_destroy(pmd->metadata_sm); ++ pmd->metadata_sm = NULL; + + return r; + } +@@ -705,6 +712,8 @@ static int __open_metadata(struct dm_pool_metadata *pmd) + sizeof(disk_super->metadata_space_map_root), + &pmd->tm, &pmd->metadata_sm); + if (r < 0) { ++ pmd->tm = NULL; ++ pmd->metadata_sm = NULL; + DMERR("tm_open_with_sm failed"); + goto bad_unlock_sblock; + } +@@ -714,6 +723,7 @@ static int __open_metadata(struct dm_pool_metadata *pmd) + if (IS_ERR(pmd->data_sm)) { + DMERR("sm_disk_open failed"); + r = PTR_ERR(pmd->data_sm); ++ pmd->data_sm = NULL; + goto bad_cleanup_tm; + } + +@@ -740,9 +750,12 @@ static int __open_metadata(struct dm_pool_metadata *pmd) + + bad_cleanup_data_sm: + dm_sm_destroy(pmd->data_sm); ++ pmd->data_sm = NULL; + bad_cleanup_tm: + dm_tm_destroy(pmd->tm); ++ pmd->tm = NULL; + dm_sm_destroy(pmd->metadata_sm); ++ pmd->metadata_sm = NULL; + bad_unlock_sblock: + dm_bm_unlock(sblock); + +@@ -789,9 +802,13 @@ static void __destroy_persistent_data_objects(struct dm_pool_metadata *pmd, + bool destroy_bm) + { + dm_sm_destroy(pmd->data_sm); ++ pmd->data_sm = NULL; + dm_sm_destroy(pmd->metadata_sm); ++ pmd->metadata_sm = NULL; + dm_tm_destroy(pmd->nb_tm); ++ pmd->nb_tm = NULL; + dm_tm_destroy(pmd->tm); ++ pmd->tm = NULL; + if (destroy_bm) + dm_block_manager_destroy(pmd->bm); + } +@@ -999,8 +1016,7 @@ int dm_pool_metadata_close(struct dm_pool_metadata *pmd) + __func__, r); + } + pmd_write_unlock(pmd); +- if (!pmd->fail_io) +- __destroy_persistent_data_objects(pmd, true); ++ __destroy_persistent_data_objects(pmd, true); + + kfree(pmd); + return 0; +@@ -1875,53 +1891,29 @@ static void __set_abort_with_changes_flags(struct dm_pool_metadata *pmd) + int dm_pool_abort_metadata(struct dm_pool_metadata *pmd) + { + int r = -EINVAL; +- struct dm_block_manager *old_bm = NULL, *new_bm = NULL; + + /* fail_io is double-checked with pmd->root_lock held below */ + if (unlikely(pmd->fail_io)) + return r; + +- /* +- * Replacement block manager (new_bm) is created and old_bm destroyed outside of +- * pmd root_lock to avoid ABBA deadlock that would result (due to life-cycle of +- * shrinker associated with the block manager's bufio client vs pmd root_lock). +- * - must take shrinker_rwsem without holding pmd->root_lock +- */ +- new_bm = dm_block_manager_create(pmd->bdev, THIN_METADATA_BLOCK_SIZE << SECTOR_SHIFT, +- THIN_MAX_CONCURRENT_LOCKS); +- + pmd_write_lock(pmd); + if (pmd->fail_io) { + pmd_write_unlock(pmd); +- goto out; ++ return r; + } +- + __set_abort_with_changes_flags(pmd); ++ ++ /* destroy data_sm/metadata_sm/nb_tm/tm */ + __destroy_persistent_data_objects(pmd, false); +- old_bm = pmd->bm; +- if (IS_ERR(new_bm)) { +- DMERR("could not create block manager during abort"); +- pmd->bm = NULL; +- r = PTR_ERR(new_bm); +- goto out_unlock; +- } + +- pmd->bm = new_bm; ++ /* reset bm */ ++ dm_block_manager_reset(pmd->bm); ++ ++ /* rebuild data_sm/metadata_sm/nb_tm/tm */ + r = __open_or_format_metadata(pmd, false); +- if (r) { +- pmd->bm = NULL; +- goto out_unlock; +- } +- new_bm = NULL; +-out_unlock: + if (r) + pmd->fail_io = true; + pmd_write_unlock(pmd); +- dm_block_manager_destroy(old_bm); +-out: +- if (new_bm && !IS_ERR(new_bm)) +- dm_block_manager_destroy(new_bm); +- + return r; + } + +diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c +index 1f40100908d7c..2bbfbb704c751 100644 +--- a/drivers/md/persistent-data/dm-block-manager.c ++++ b/drivers/md/persistent-data/dm-block-manager.c +@@ -415,6 +415,12 @@ void dm_block_manager_destroy(struct dm_block_manager *bm) + } + EXPORT_SYMBOL_GPL(dm_block_manager_destroy); + ++void dm_block_manager_reset(struct dm_block_manager *bm) ++{ ++ dm_bufio_client_reset(bm->bufio); ++} ++EXPORT_SYMBOL_GPL(dm_block_manager_reset); ++ + unsigned int dm_bm_block_size(struct dm_block_manager *bm) + { + return dm_bufio_get_block_size(bm->bufio); +diff --git a/drivers/md/persistent-data/dm-block-manager.h b/drivers/md/persistent-data/dm-block-manager.h +index 58a23b8ec1902..4371d85d3c258 100644 +--- a/drivers/md/persistent-data/dm-block-manager.h ++++ b/drivers/md/persistent-data/dm-block-manager.h +@@ -35,6 +35,7 @@ struct dm_block_manager *dm_block_manager_create( + struct block_device *bdev, unsigned int block_size, + unsigned int max_held_per_thread); + void dm_block_manager_destroy(struct dm_block_manager *bm); ++void dm_block_manager_reset(struct dm_block_manager *bm); + + unsigned int dm_bm_block_size(struct dm_block_manager *bm); + dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm); +diff --git a/drivers/md/persistent-data/dm-space-map.h b/drivers/md/persistent-data/dm-space-map.h +index a015cd11f6e97..85aa0a3974fe0 100644 +--- a/drivers/md/persistent-data/dm-space-map.h ++++ b/drivers/md/persistent-data/dm-space-map.h +@@ -76,7 +76,8 @@ struct dm_space_map { + + static inline void dm_sm_destroy(struct dm_space_map *sm) + { +- sm->destroy(sm); ++ if (sm) ++ sm->destroy(sm); + } + + static inline int dm_sm_extend(struct dm_space_map *sm, dm_block_t extra_blocks) +diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c +index 39885f8355847..557a3ecfe75a0 100644 +--- a/drivers/md/persistent-data/dm-transaction-manager.c ++++ b/drivers/md/persistent-data/dm-transaction-manager.c +@@ -197,6 +197,9 @@ EXPORT_SYMBOL_GPL(dm_tm_create_non_blocking_clone); + + void dm_tm_destroy(struct dm_transaction_manager *tm) + { ++ if (!tm) ++ return; ++ + if (!tm->is_clone) + wipe_shadow_table(tm); + +diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +index 5935be190b9e2..5f2a6fcba9670 100644 +--- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c ++++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +@@ -866,10 +866,13 @@ static int atl1e_setup_ring_resources(struct atl1e_adapter *adapter) + netdev_err(adapter->netdev, "offset(%d) > ring size(%d) !!\n", + offset, adapter->ring_size); + err = -1; +- goto failed; ++ goto free_buffer; + } + + return 0; ++free_buffer: ++ kfree(tx_ring->tx_buffer); ++ tx_ring->tx_buffer = NULL; + failed: + if (adapter->ring_vir_addr != NULL) { + dma_free_coherent(&pdev->dev, adapter->ring_size, +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c +index bfddbff7bcdfb..28fb643d2917f 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dcbnl.c +@@ -399,9 +399,10 @@ static int otx2_dcbnl_ieee_getpfc(struct net_device *dev, struct ieee_pfc *pfc) + static int otx2_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc) + { + struct otx2_nic *pfvf = netdev_priv(dev); ++ u8 old_pfc_en; + int err; + +- /* Save PFC configuration to interface */ ++ old_pfc_en = pfvf->pfc_en; + pfvf->pfc_en = pfc->pfc_en; + + if (pfvf->hw.tx_queues >= NIX_PF_PFC_PRIO_MAX) +@@ -411,13 +412,17 @@ static int otx2_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc) + * supported by the tx queue configuration + */ + err = otx2_check_pfc_config(pfvf); +- if (err) ++ if (err) { ++ pfvf->pfc_en = old_pfc_en; + return err; ++ } + + process_pfc: + err = otx2_config_priority_flow_ctrl(pfvf); +- if (err) ++ if (err) { ++ pfvf->pfc_en = old_pfc_en; + return err; ++ } + + /* Request Per channel Bpids */ + if (pfc->pfc_en) +@@ -425,6 +430,12 @@ process_pfc: + + err = otx2_pfc_txschq_update(pfvf); + if (err) { ++ if (pfc->pfc_en) ++ otx2_nix_config_bp(pfvf, false); ++ ++ otx2_pfc_txschq_stop(pfvf); ++ pfvf->pfc_en = old_pfc_en; ++ otx2_config_priority_flow_ctrl(pfvf); + dev_err(pfvf->dev, "%s failed to update TX schedulers\n", __func__); + return err; + } +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +index b3253e263ebc8..ac6a0785b10d8 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +@@ -48,6 +48,25 @@ + #define CREATE_TRACE_POINTS + #include "diag/cmd_tracepoint.h" + ++struct mlx5_ifc_mbox_out_bits { ++ u8 status[0x8]; ++ u8 reserved_at_8[0x18]; ++ ++ u8 syndrome[0x20]; ++ ++ u8 reserved_at_40[0x40]; ++}; ++ ++struct mlx5_ifc_mbox_in_bits { ++ u8 opcode[0x10]; ++ u8 uid[0x10]; ++ ++ u8 reserved_at_20[0x10]; ++ u8 op_mod[0x10]; ++ ++ u8 reserved_at_40[0x40]; ++}; ++ + enum { + CMD_IF_REV = 5, + }; +@@ -71,6 +90,26 @@ enum { + MLX5_CMD_DELIVERY_STAT_CMD_DESCR_ERR = 0x10, + }; + ++static u16 in_to_opcode(void *in) ++{ ++ return MLX5_GET(mbox_in, in, opcode); ++} ++ ++/* Returns true for opcodes that might be triggered very frequently and throttle ++ * the command interface. Limit their command slots usage. ++ */ ++static bool mlx5_cmd_is_throttle_opcode(u16 op) ++{ ++ switch (op) { ++ case MLX5_CMD_OP_CREATE_GENERAL_OBJECT: ++ case MLX5_CMD_OP_DESTROY_GENERAL_OBJECT: ++ case MLX5_CMD_OP_MODIFY_GENERAL_OBJECT: ++ case MLX5_CMD_OP_QUERY_GENERAL_OBJECT: ++ return true; ++ } ++ return false; ++} ++ + static struct mlx5_cmd_work_ent * + cmd_alloc_ent(struct mlx5_cmd *cmd, struct mlx5_cmd_msg *in, + struct mlx5_cmd_msg *out, void *uout, int uout_size, +@@ -92,6 +131,7 @@ cmd_alloc_ent(struct mlx5_cmd *cmd, struct mlx5_cmd_msg *in, + ent->context = context; + ent->cmd = cmd; + ent->page_queue = page_queue; ++ ent->op = in_to_opcode(in->first.data); + refcount_set(&ent->refcnt, 1); + + return ent; +@@ -116,24 +156,27 @@ static u8 alloc_token(struct mlx5_cmd *cmd) + return token; + } + +-static int cmd_alloc_index(struct mlx5_cmd *cmd) ++static int cmd_alloc_index(struct mlx5_cmd *cmd, struct mlx5_cmd_work_ent *ent) + { + unsigned long flags; + int ret; + + spin_lock_irqsave(&cmd->alloc_lock, flags); +- ret = find_first_bit(&cmd->bitmask, cmd->max_reg_cmds); +- if (ret < cmd->max_reg_cmds) +- clear_bit(ret, &cmd->bitmask); ++ ret = find_first_bit(&cmd->vars.bitmask, cmd->vars.max_reg_cmds); ++ if (ret < cmd->vars.max_reg_cmds) { ++ clear_bit(ret, &cmd->vars.bitmask); ++ ent->idx = ret; ++ cmd->ent_arr[ent->idx] = ent; ++ } + spin_unlock_irqrestore(&cmd->alloc_lock, flags); + +- return ret < cmd->max_reg_cmds ? ret : -ENOMEM; ++ return ret < cmd->vars.max_reg_cmds ? ret : -ENOMEM; + } + + static void cmd_free_index(struct mlx5_cmd *cmd, int idx) + { + lockdep_assert_held(&cmd->alloc_lock); +- set_bit(idx, &cmd->bitmask); ++ set_bit(idx, &cmd->vars.bitmask); + } + + static void cmd_ent_get(struct mlx5_cmd_work_ent *ent) +@@ -152,7 +195,7 @@ static void cmd_ent_put(struct mlx5_cmd_work_ent *ent) + + if (ent->idx >= 0) { + cmd_free_index(cmd, ent->idx); +- up(ent->page_queue ? &cmd->pages_sem : &cmd->sem); ++ up(ent->page_queue ? &cmd->vars.pages_sem : &cmd->vars.sem); + } + + cmd_free_ent(ent); +@@ -162,7 +205,7 @@ out: + + static struct mlx5_cmd_layout *get_inst(struct mlx5_cmd *cmd, int idx) + { +- return cmd->cmd_buf + (idx << cmd->log_stride); ++ return cmd->cmd_buf + (idx << cmd->vars.log_stride); + } + + static int mlx5_calc_cmd_blocks(struct mlx5_cmd_msg *msg) +@@ -753,25 +796,6 @@ static int cmd_status_to_err(u8 status) + } + } + +-struct mlx5_ifc_mbox_out_bits { +- u8 status[0x8]; +- u8 reserved_at_8[0x18]; +- +- u8 syndrome[0x20]; +- +- u8 reserved_at_40[0x40]; +-}; +- +-struct mlx5_ifc_mbox_in_bits { +- u8 opcode[0x10]; +- u8 uid[0x10]; +- +- u8 reserved_at_20[0x10]; +- u8 op_mod[0x10]; +- +- u8 reserved_at_40[0x40]; +-}; +- + void mlx5_cmd_out_err(struct mlx5_core_dev *dev, u16 opcode, u16 op_mod, void *out) + { + u32 syndrome = MLX5_GET(mbox_out, out, syndrome); +@@ -789,7 +813,7 @@ static void cmd_status_print(struct mlx5_core_dev *dev, void *in, void *out) + u16 opcode, op_mod; + u16 uid; + +- opcode = MLX5_GET(mbox_in, in, opcode); ++ opcode = in_to_opcode(in); + op_mod = MLX5_GET(mbox_in, in, op_mod); + uid = MLX5_GET(mbox_in, in, uid); + +@@ -801,7 +825,7 @@ int mlx5_cmd_check(struct mlx5_core_dev *dev, int err, void *in, void *out) + { + /* aborted due to PCI error or via reset flow mlx5_cmd_trigger_completions() */ + if (err == -ENXIO) { +- u16 opcode = MLX5_GET(mbox_in, in, opcode); ++ u16 opcode = in_to_opcode(in); + u32 syndrome; + u8 status; + +@@ -830,9 +854,9 @@ static void dump_command(struct mlx5_core_dev *dev, + struct mlx5_cmd_work_ent *ent, int input) + { + struct mlx5_cmd_msg *msg = input ? ent->in : ent->out; +- u16 op = MLX5_GET(mbox_in, ent->lay->in, opcode); + struct mlx5_cmd_mailbox *next = msg->next; + int n = mlx5_calc_cmd_blocks(msg); ++ u16 op = ent->op; + int data_only; + u32 offset = 0; + int dump_len; +@@ -884,11 +908,6 @@ static void dump_command(struct mlx5_core_dev *dev, + mlx5_core_dbg(dev, "cmd[%d]: end dump\n", ent->idx); + } + +-static u16 msg_to_opcode(struct mlx5_cmd_msg *in) +-{ +- return MLX5_GET(mbox_in, in->first.data, opcode); +-} +- + static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool forced); + + static void cb_timeout_handler(struct work_struct *work) +@@ -906,13 +925,13 @@ static void cb_timeout_handler(struct work_struct *work) + /* Maybe got handled by eq recover ? */ + if (!test_bit(MLX5_CMD_ENT_STATE_PENDING_COMP, &ent->state)) { + mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) Async, recovered after timeout\n", ent->idx, +- mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in)); ++ mlx5_command_str(ent->op), ent->op); + goto out; /* phew, already handled */ + } + + ent->ret = -ETIMEDOUT; + mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) Async, timeout. Will cause a leak of a command resource\n", +- ent->idx, mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in)); ++ ent->idx, mlx5_command_str(ent->op), ent->op); + mlx5_cmd_comp_handler(dev, 1ULL << ent->idx, true); + + out: +@@ -955,10 +974,10 @@ static void cmd_work_handler(struct work_struct *work) + cb_timeout = msecs_to_jiffies(mlx5_tout_ms(dev, CMD)); + + complete(&ent->handling); +- sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem; ++ sem = ent->page_queue ? &cmd->vars.pages_sem : &cmd->vars.sem; + down(sem); + if (!ent->page_queue) { +- alloc_ret = cmd_alloc_index(cmd); ++ alloc_ret = cmd_alloc_index(cmd, ent); + if (alloc_ret < 0) { + mlx5_core_err_rl(dev, "failed to allocate command entry\n"); + if (ent->callback) { +@@ -973,20 +992,18 @@ static void cmd_work_handler(struct work_struct *work) + up(sem); + return; + } +- ent->idx = alloc_ret; + } else { +- ent->idx = cmd->max_reg_cmds; ++ ent->idx = cmd->vars.max_reg_cmds; + spin_lock_irqsave(&cmd->alloc_lock, flags); +- clear_bit(ent->idx, &cmd->bitmask); ++ clear_bit(ent->idx, &cmd->vars.bitmask); ++ cmd->ent_arr[ent->idx] = ent; + spin_unlock_irqrestore(&cmd->alloc_lock, flags); + } + +- cmd->ent_arr[ent->idx] = ent; + lay = get_inst(cmd, ent->idx); + ent->lay = lay; + memset(lay, 0, sizeof(*lay)); + memcpy(lay->in, ent->in->first.data, sizeof(lay->in)); +- ent->op = be32_to_cpu(lay->in[0]) >> 16; + if (ent->in->next) + lay->in_ptr = cpu_to_be64(ent->in->next->dma); + lay->inlen = cpu_to_be32(ent->in->len); +@@ -1099,12 +1116,12 @@ static void wait_func_handle_exec_timeout(struct mlx5_core_dev *dev, + */ + if (wait_for_completion_timeout(&ent->done, timeout)) { + mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) recovered after timeout\n", ent->idx, +- mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in)); ++ mlx5_command_str(ent->op), ent->op); + return; + } + + mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) No done completion\n", ent->idx, +- mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in)); ++ mlx5_command_str(ent->op), ent->op); + + ent->ret = -ETIMEDOUT; + mlx5_cmd_comp_handler(dev, 1ULL << ent->idx, true); +@@ -1131,12 +1148,10 @@ out_err: + + if (err == -ETIMEDOUT) { + mlx5_core_warn(dev, "%s(0x%x) timeout. Will cause a leak of a command resource\n", +- mlx5_command_str(msg_to_opcode(ent->in)), +- msg_to_opcode(ent->in)); ++ mlx5_command_str(ent->op), ent->op); + } else if (err == -ECANCELED) { + mlx5_core_warn(dev, "%s(0x%x) canceled on out of queue timeout.\n", +- mlx5_command_str(msg_to_opcode(ent->in)), +- msg_to_opcode(ent->in)); ++ mlx5_command_str(ent->op), ent->op); + } + mlx5_core_dbg(dev, "err %d, delivery status %s(%d)\n", + err, deliv_status_to_str(ent->status), ent->status); +@@ -1170,7 +1185,6 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, + u8 status = 0; + int err = 0; + s64 ds; +- u16 op; + + if (callback && page_queue) + return -EINVAL; +@@ -1210,9 +1224,8 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, + goto out_free; + + ds = ent->ts2 - ent->ts1; +- op = MLX5_GET(mbox_in, in->first.data, opcode); +- if (op < MLX5_CMD_OP_MAX) { +- stats = &cmd->stats[op]; ++ if (ent->op < MLX5_CMD_OP_MAX) { ++ stats = &cmd->stats[ent->op]; + spin_lock_irq(&stats->lock); + stats->sum += ds; + ++stats->n; +@@ -1220,7 +1233,7 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, + } + mlx5_core_dbg_mask(dev, 1 << MLX5_CMD_TIME, + "fw exec time for %s is %lld nsec\n", +- mlx5_command_str(op), ds); ++ mlx5_command_str(ent->op), ds); + + out_free: + status = ent->status; +@@ -1558,15 +1571,15 @@ void mlx5_cmd_allowed_opcode(struct mlx5_core_dev *dev, u16 opcode) + struct mlx5_cmd *cmd = &dev->cmd; + int i; + +- for (i = 0; i < cmd->max_reg_cmds; i++) +- down(&cmd->sem); +- down(&cmd->pages_sem); ++ for (i = 0; i < cmd->vars.max_reg_cmds; i++) ++ down(&cmd->vars.sem); ++ down(&cmd->vars.pages_sem); + + cmd->allowed_opcode = opcode; + +- up(&cmd->pages_sem); +- for (i = 0; i < cmd->max_reg_cmds; i++) +- up(&cmd->sem); ++ up(&cmd->vars.pages_sem); ++ for (i = 0; i < cmd->vars.max_reg_cmds; i++) ++ up(&cmd->vars.sem); + } + + static void mlx5_cmd_change_mod(struct mlx5_core_dev *dev, int mode) +@@ -1574,15 +1587,15 @@ static void mlx5_cmd_change_mod(struct mlx5_core_dev *dev, int mode) + struct mlx5_cmd *cmd = &dev->cmd; + int i; + +- for (i = 0; i < cmd->max_reg_cmds; i++) +- down(&cmd->sem); +- down(&cmd->pages_sem); ++ for (i = 0; i < cmd->vars.max_reg_cmds; i++) ++ down(&cmd->vars.sem); ++ down(&cmd->vars.pages_sem); + + cmd->mode = mode; + +- up(&cmd->pages_sem); +- for (i = 0; i < cmd->max_reg_cmds; i++) +- up(&cmd->sem); ++ up(&cmd->vars.pages_sem); ++ for (i = 0; i < cmd->vars.max_reg_cmds; i++) ++ up(&cmd->vars.sem); + } + + static int cmd_comp_notifier(struct notifier_block *nb, +@@ -1641,7 +1654,7 @@ static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool force + + /* there can be at most 32 command queues */ + vector = vec & 0xffffffff; +- for (i = 0; i < (1 << cmd->log_sz); i++) { ++ for (i = 0; i < (1 << cmd->vars.log_sz); i++) { + if (test_bit(i, &vector)) { + ent = cmd->ent_arr[i]; + +@@ -1730,7 +1743,7 @@ static void mlx5_cmd_trigger_completions(struct mlx5_core_dev *dev) + /* wait for pending handlers to complete */ + mlx5_eq_synchronize_cmd_irq(dev); + spin_lock_irqsave(&dev->cmd.alloc_lock, flags); +- vector = ~dev->cmd.bitmask & ((1ul << (1 << dev->cmd.log_sz)) - 1); ++ vector = ~dev->cmd.vars.bitmask & ((1ul << (1 << dev->cmd.vars.log_sz)) - 1); + if (!vector) + goto no_trig; + +@@ -1739,14 +1752,14 @@ static void mlx5_cmd_trigger_completions(struct mlx5_core_dev *dev) + * to guarantee pending commands will not get freed in the meanwhile. + * For that reason, it also has to be done inside the alloc_lock. + */ +- for_each_set_bit(i, &bitmask, (1 << cmd->log_sz)) ++ for_each_set_bit(i, &bitmask, (1 << cmd->vars.log_sz)) + cmd_ent_get(cmd->ent_arr[i]); + vector |= MLX5_TRIGGERED_CMD_COMP; + spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags); + + mlx5_core_dbg(dev, "vector 0x%llx\n", vector); + mlx5_cmd_comp_handler(dev, vector, true); +- for_each_set_bit(i, &bitmask, (1 << cmd->log_sz)) ++ for_each_set_bit(i, &bitmask, (1 << cmd->vars.log_sz)) + cmd_ent_put(cmd->ent_arr[i]); + return; + +@@ -1759,22 +1772,22 @@ void mlx5_cmd_flush(struct mlx5_core_dev *dev) + struct mlx5_cmd *cmd = &dev->cmd; + int i; + +- for (i = 0; i < cmd->max_reg_cmds; i++) { +- while (down_trylock(&cmd->sem)) { ++ for (i = 0; i < cmd->vars.max_reg_cmds; i++) { ++ while (down_trylock(&cmd->vars.sem)) { + mlx5_cmd_trigger_completions(dev); + cond_resched(); + } + } + +- while (down_trylock(&cmd->pages_sem)) { ++ while (down_trylock(&cmd->vars.pages_sem)) { + mlx5_cmd_trigger_completions(dev); + cond_resched(); + } + + /* Unlock cmdif */ +- up(&cmd->pages_sem); +- for (i = 0; i < cmd->max_reg_cmds; i++) +- up(&cmd->sem); ++ up(&cmd->vars.pages_sem); ++ for (i = 0; i < cmd->vars.max_reg_cmds; i++) ++ up(&cmd->vars.sem); + } + + static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size, +@@ -1817,7 +1830,7 @@ cache_miss: + + static int is_manage_pages(void *in) + { +- return MLX5_GET(mbox_in, in, opcode) == MLX5_CMD_OP_MANAGE_PAGES; ++ return in_to_opcode(in) == MLX5_CMD_OP_MANAGE_PAGES; + } + + /* Notes: +@@ -1828,8 +1841,9 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, + int out_size, mlx5_cmd_cbk_t callback, void *context, + bool force_polling) + { +- u16 opcode = MLX5_GET(mbox_in, in, opcode); + struct mlx5_cmd_msg *inb, *outb; ++ u16 opcode = in_to_opcode(in); ++ bool throttle_op; + int pages_queue; + gfp_t gfp; + u8 token; +@@ -1838,13 +1852,21 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, + if (mlx5_cmd_is_down(dev) || !opcode_allowed(&dev->cmd, opcode)) + return -ENXIO; + ++ throttle_op = mlx5_cmd_is_throttle_opcode(opcode); ++ if (throttle_op) { ++ /* atomic context may not sleep */ ++ if (callback) ++ return -EINVAL; ++ down(&dev->cmd.vars.throttle_sem); ++ } ++ + pages_queue = is_manage_pages(in); + gfp = callback ? GFP_ATOMIC : GFP_KERNEL; + + inb = alloc_msg(dev, in_size, gfp); + if (IS_ERR(inb)) { + err = PTR_ERR(inb); +- return err; ++ goto out_up; + } + + token = alloc_token(&dev->cmd); +@@ -1878,6 +1900,9 @@ out_out: + mlx5_free_cmd_msg(dev, outb); + out_in: + free_msg(dev, inb); ++out_up: ++ if (throttle_op) ++ up(&dev->cmd.vars.throttle_sem); + return err; + } + +@@ -1952,8 +1977,8 @@ static int cmd_status_err(struct mlx5_core_dev *dev, int err, u16 opcode, u16 op + int mlx5_cmd_do(struct mlx5_core_dev *dev, void *in, int in_size, void *out, int out_size) + { + int err = cmd_exec(dev, in, in_size, out, out_size, NULL, NULL, false); +- u16 opcode = MLX5_GET(mbox_in, in, opcode); + u16 op_mod = MLX5_GET(mbox_in, in, op_mod); ++ u16 opcode = in_to_opcode(in); + + return cmd_status_err(dev, err, opcode, op_mod, out); + } +@@ -1998,8 +2023,8 @@ int mlx5_cmd_exec_polling(struct mlx5_core_dev *dev, void *in, int in_size, + void *out, int out_size) + { + int err = cmd_exec(dev, in, in_size, out, out_size, NULL, NULL, true); +- u16 opcode = MLX5_GET(mbox_in, in, opcode); + u16 op_mod = MLX5_GET(mbox_in, in, op_mod); ++ u16 opcode = in_to_opcode(in); + + err = cmd_status_err(dev, err, opcode, op_mod, out); + return mlx5_cmd_check(dev, err, in, out); +@@ -2051,7 +2076,7 @@ int mlx5_cmd_exec_cb(struct mlx5_async_ctx *ctx, void *in, int in_size, + + work->ctx = ctx; + work->user_callback = callback; +- work->opcode = MLX5_GET(mbox_in, in, opcode); ++ work->opcode = in_to_opcode(in); + work->op_mod = MLX5_GET(mbox_in, in, op_mod); + work->out = out; + if (WARN_ON(!atomic_inc_not_zero(&ctx->num_inflight))) +@@ -2187,16 +2212,16 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) + goto err_free_pool; + + cmd_l = ioread32be(&dev->iseg->cmdq_addr_l_sz) & 0xff; +- cmd->log_sz = cmd_l >> 4 & 0xf; +- cmd->log_stride = cmd_l & 0xf; +- if (1 << cmd->log_sz > MLX5_MAX_COMMANDS) { ++ cmd->vars.log_sz = cmd_l >> 4 & 0xf; ++ cmd->vars.log_stride = cmd_l & 0xf; ++ if (1 << cmd->vars.log_sz > MLX5_MAX_COMMANDS) { + mlx5_core_err(dev, "firmware reports too many outstanding commands %d\n", +- 1 << cmd->log_sz); ++ 1 << cmd->vars.log_sz); + err = -EINVAL; + goto err_free_page; + } + +- if (cmd->log_sz + cmd->log_stride > MLX5_ADAPTER_PAGE_SHIFT) { ++ if (cmd->vars.log_sz + cmd->vars.log_stride > MLX5_ADAPTER_PAGE_SHIFT) { + mlx5_core_err(dev, "command queue size overflow\n"); + err = -EINVAL; + goto err_free_page; +@@ -2204,13 +2229,13 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) + + cmd->state = MLX5_CMDIF_STATE_DOWN; + cmd->checksum_disabled = 1; +- cmd->max_reg_cmds = (1 << cmd->log_sz) - 1; +- cmd->bitmask = (1UL << cmd->max_reg_cmds) - 1; ++ cmd->vars.max_reg_cmds = (1 << cmd->vars.log_sz) - 1; ++ cmd->vars.bitmask = (1UL << cmd->vars.max_reg_cmds) - 1; + +- cmd->cmdif_rev = ioread32be(&dev->iseg->cmdif_rev_fw_sub) >> 16; +- if (cmd->cmdif_rev > CMD_IF_REV) { ++ cmd->vars.cmdif_rev = ioread32be(&dev->iseg->cmdif_rev_fw_sub) >> 16; ++ if (cmd->vars.cmdif_rev > CMD_IF_REV) { + mlx5_core_err(dev, "driver does not support command interface version. driver %d, firmware %d\n", +- CMD_IF_REV, cmd->cmdif_rev); ++ CMD_IF_REV, cmd->vars.cmdif_rev); + err = -EOPNOTSUPP; + goto err_free_page; + } +@@ -2220,8 +2245,9 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) + for (i = 0; i < MLX5_CMD_OP_MAX; i++) + spin_lock_init(&cmd->stats[i].lock); + +- sema_init(&cmd->sem, cmd->max_reg_cmds); +- sema_init(&cmd->pages_sem, 1); ++ sema_init(&cmd->vars.sem, cmd->vars.max_reg_cmds); ++ sema_init(&cmd->vars.pages_sem, 1); ++ sema_init(&cmd->vars.throttle_sem, DIV_ROUND_UP(cmd->vars.max_reg_cmds, 2)); + + cmd_h = (u32)((u64)(cmd->dma) >> 32); + cmd_l = (u32)(cmd->dma); +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c +index bb95b40d25eb5..e0b0729e238c1 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c +@@ -176,8 +176,8 @@ static ssize_t slots_read(struct file *filp, char __user *buf, size_t count, + int ret; + + cmd = filp->private_data; +- weight = bitmap_weight(&cmd->bitmask, cmd->max_reg_cmds); +- field = cmd->max_reg_cmds - weight; ++ weight = bitmap_weight(&cmd->vars.bitmask, cmd->vars.max_reg_cmds); ++ field = cmd->vars.max_reg_cmds - weight; + ret = snprintf(tbuf, sizeof(tbuf), "%d\n", field); + return simple_read_from_buffer(buf, count, pos, tbuf, ret); + } +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c +index 374c0011a127b..3ba54ffa54bfe 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c +@@ -691,7 +691,7 @@ static void mlx5_fw_tracer_handle_traces(struct work_struct *work) + + while (block_timestamp > tracer->last_timestamp) { + /* Check block override if it's not the first block */ +- if (!tracer->last_timestamp) { ++ if (tracer->last_timestamp) { + u64 *ts_event; + /* To avoid block override be the HW in case of buffer + * wraparound, the time stamp of the previous block +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c b/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c +index be83ad9db82a4..e1283531e0b81 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c +@@ -154,6 +154,7 @@ static int fs_udp_create_groups(struct mlx5e_flow_table *ft, enum fs_udp_type ty + in = kvzalloc(inlen, GFP_KERNEL); + if (!in || !ft->g) { + kfree(ft->g); ++ ft->g = NULL; + kvfree(in); + return -ENOMEM; + } +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c +index 4db0483c066a8..83bb0811e7741 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c +@@ -300,6 +300,9 @@ int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv, + if (err) + goto destroy_neigh_entry; + ++ e->encap_size = ipv4_encap_size; ++ e->encap_header = encap_header; ++ + if (!(nud_state & NUD_VALID)) { + neigh_event_send(attr.n, NULL); + /* the encap entry will be made valid on neigh update event +@@ -319,8 +322,6 @@ int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv, + goto destroy_neigh_entry; + } + +- e->encap_size = ipv4_encap_size; +- e->encap_header = encap_header; + e->flags |= MLX5_ENCAP_ENTRY_VALID; + mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev)); + mlx5e_route_lookup_ipv4_put(&attr); +@@ -403,12 +404,16 @@ int mlx5e_tc_tun_update_header_ipv4(struct mlx5e_priv *priv, + if (err) + goto free_encap; + ++ e->encap_size = ipv4_encap_size; ++ kfree(e->encap_header); ++ e->encap_header = encap_header; ++ + if (!(nud_state & NUD_VALID)) { + neigh_event_send(attr.n, NULL); + /* the encap entry will be made valid on neigh update event + * and not used before that. + */ +- goto free_encap; ++ goto release_neigh; + } + + memset(&reformat_params, 0, sizeof(reformat_params)); +@@ -422,10 +427,6 @@ int mlx5e_tc_tun_update_header_ipv4(struct mlx5e_priv *priv, + goto free_encap; + } + +- e->encap_size = ipv4_encap_size; +- kfree(e->encap_header); +- e->encap_header = encap_header; +- + e->flags |= MLX5_ENCAP_ENTRY_VALID; + mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev)); + mlx5e_route_lookup_ipv4_put(&attr); +@@ -567,6 +568,9 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv, + if (err) + goto destroy_neigh_entry; + ++ e->encap_size = ipv6_encap_size; ++ e->encap_header = encap_header; ++ + if (!(nud_state & NUD_VALID)) { + neigh_event_send(attr.n, NULL); + /* the encap entry will be made valid on neigh update event +@@ -586,8 +590,6 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv, + goto destroy_neigh_entry; + } + +- e->encap_size = ipv6_encap_size; +- e->encap_header = encap_header; + e->flags |= MLX5_ENCAP_ENTRY_VALID; + mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev)); + mlx5e_route_lookup_ipv6_put(&attr); +@@ -669,12 +671,16 @@ int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv, + if (err) + goto free_encap; + ++ e->encap_size = ipv6_encap_size; ++ kfree(e->encap_header); ++ e->encap_header = encap_header; ++ + if (!(nud_state & NUD_VALID)) { + neigh_event_send(attr.n, NULL); + /* the encap entry will be made valid on neigh update event + * and not used before that. + */ +- goto free_encap; ++ goto release_neigh; + } + + memset(&reformat_params, 0, sizeof(reformat_params)); +@@ -688,10 +694,6 @@ int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv, + goto free_encap; + } + +- e->encap_size = ipv6_encap_size; +- kfree(e->encap_header); +- e->encap_header = encap_header; +- + e->flags |= MLX5_ENCAP_ENTRY_VALID; + mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev)); + mlx5e_route_lookup_ipv6_put(&attr); +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +index eeba91d9c5211..ceeb23f478e15 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +@@ -49,7 +49,7 @@ void mlx5e_ethtool_get_drvinfo(struct mlx5e_priv *priv, + count = snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%04d (%.16s)", fw_rev_maj(mdev), + fw_rev_min(mdev), fw_rev_sub(mdev), mdev->board_id); +- if (count == sizeof(drvinfo->fw_version)) ++ if (count >= sizeof(drvinfo->fw_version)) + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%04d", fw_rev_maj(mdev), + fw_rev_min(mdev), fw_rev_sub(mdev)); +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +index 2653cb96c3105..5aeca9534f15a 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +@@ -76,7 +76,7 @@ static void mlx5e_rep_get_drvinfo(struct net_device *dev, + count = snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%04d (%.16s)", fw_rev_maj(mdev), + fw_rev_min(mdev), fw_rev_sub(mdev), mdev->board_id); +- if (count == sizeof(drvinfo->fw_version)) ++ if (count >= sizeof(drvinfo->fw_version)) + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%04d", fw_rev_maj(mdev), + fw_rev_min(mdev), fw_rev_sub(mdev)); +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c +index d5c3173250309..3f68e3198aa64 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c +@@ -277,7 +277,7 @@ int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, + req_list_size = max_list_size; + } + +- out_sz = MLX5_ST_SZ_BYTES(query_nic_vport_context_in) + ++ out_sz = MLX5_ST_SZ_BYTES(query_nic_vport_context_out) + + req_list_size * MLX5_ST_SZ_BYTES(mac_address_layout); + + out = kvzalloc(out_sz, GFP_KERNEL); +diff --git a/drivers/net/ethernet/micrel/ks8851.h b/drivers/net/ethernet/micrel/ks8851.h +index fecd43754cead..e5ec0a363aff8 100644 +--- a/drivers/net/ethernet/micrel/ks8851.h ++++ b/drivers/net/ethernet/micrel/ks8851.h +@@ -350,6 +350,8 @@ union ks8851_tx_hdr { + * @rxd: Space for receiving SPI data, in DMA-able space. + * @txd: Space for transmitting SPI data, in DMA-able space. + * @msg_enable: The message flags controlling driver output (see ethtool). ++ * @tx_space: Free space in the hardware TX buffer (cached copy of KS_TXMIR). ++ * @queued_len: Space required in hardware TX buffer for queued packets in txq. + * @fid: Incrementing frame id tag. + * @rc_ier: Cached copy of KS_IER. + * @rc_ccr: Cached copy of KS_CCR. +@@ -399,6 +401,7 @@ struct ks8851_net { + struct work_struct rxctrl_work; + + struct sk_buff_head txq; ++ unsigned int queued_len; + + struct eeprom_93cx6 eeprom; + struct regulator *vdd_reg; +diff --git a/drivers/net/ethernet/micrel/ks8851_common.c b/drivers/net/ethernet/micrel/ks8851_common.c +index cfbc900d4aeb9..0bf13b38b8f5b 100644 +--- a/drivers/net/ethernet/micrel/ks8851_common.c ++++ b/drivers/net/ethernet/micrel/ks8851_common.c +@@ -362,16 +362,18 @@ static irqreturn_t ks8851_irq(int irq, void *_ks) + handled |= IRQ_RXPSI; + + if (status & IRQ_TXI) { +- handled |= IRQ_TXI; ++ unsigned short tx_space = ks8851_rdreg16(ks, KS_TXMIR); + +- /* no lock here, tx queue should have been stopped */ ++ netif_dbg(ks, intr, ks->netdev, ++ "%s: txspace %d\n", __func__, tx_space); + +- /* update our idea of how much tx space is available to the +- * system */ +- ks->tx_space = ks8851_rdreg16(ks, KS_TXMIR); ++ spin_lock(&ks->statelock); ++ ks->tx_space = tx_space; ++ if (netif_queue_stopped(ks->netdev)) ++ netif_wake_queue(ks->netdev); ++ spin_unlock(&ks->statelock); + +- netif_dbg(ks, intr, ks->netdev, +- "%s: txspace %d\n", __func__, ks->tx_space); ++ handled |= IRQ_TXI; + } + + if (status & IRQ_RXI) +@@ -414,9 +416,6 @@ static irqreturn_t ks8851_irq(int irq, void *_ks) + if (status & IRQ_LCI) + mii_check_link(&ks->mii); + +- if (status & IRQ_TXI) +- netif_wake_queue(ks->netdev); +- + return IRQ_HANDLED; + } + +@@ -500,6 +499,7 @@ static int ks8851_net_open(struct net_device *dev) + ks8851_wrreg16(ks, KS_ISR, ks->rc_ier); + ks8851_wrreg16(ks, KS_IER, ks->rc_ier); + ++ ks->queued_len = 0; + netif_start_queue(ks->netdev); + + netif_dbg(ks, ifup, ks->netdev, "network device up\n"); +diff --git a/drivers/net/ethernet/micrel/ks8851_spi.c b/drivers/net/ethernet/micrel/ks8851_spi.c +index 70bc7253454f6..88e26c120b483 100644 +--- a/drivers/net/ethernet/micrel/ks8851_spi.c ++++ b/drivers/net/ethernet/micrel/ks8851_spi.c +@@ -286,6 +286,18 @@ static void ks8851_wrfifo_spi(struct ks8851_net *ks, struct sk_buff *txp, + netdev_err(ks->netdev, "%s: spi_sync() failed\n", __func__); + } + ++/** ++ * calc_txlen - calculate size of message to send packet ++ * @len: Length of data ++ * ++ * Returns the size of the TXFIFO message needed to send ++ * this packet. ++ */ ++static unsigned int calc_txlen(unsigned int len) ++{ ++ return ALIGN(len + 4, 4); ++} ++ + /** + * ks8851_rx_skb_spi - receive skbuff + * @ks: The device state +@@ -305,7 +317,9 @@ static void ks8851_rx_skb_spi(struct ks8851_net *ks, struct sk_buff *skb) + */ + static void ks8851_tx_work(struct work_struct *work) + { ++ unsigned int dequeued_len = 0; + struct ks8851_net_spi *kss; ++ unsigned short tx_space; + struct ks8851_net *ks; + unsigned long flags; + struct sk_buff *txb; +@@ -322,6 +336,8 @@ static void ks8851_tx_work(struct work_struct *work) + last = skb_queue_empty(&ks->txq); + + if (txb) { ++ dequeued_len += calc_txlen(txb->len); ++ + ks8851_wrreg16_spi(ks, KS_RXQCR, + ks->rc_rxqcr | RXQCR_SDA); + ks8851_wrfifo_spi(ks, txb, last); +@@ -332,6 +348,13 @@ static void ks8851_tx_work(struct work_struct *work) + } + } + ++ tx_space = ks8851_rdreg16_spi(ks, KS_TXMIR); ++ ++ spin_lock(&ks->statelock); ++ ks->queued_len -= dequeued_len; ++ ks->tx_space = tx_space; ++ spin_unlock(&ks->statelock); ++ + ks8851_unlock_spi(ks, &flags); + } + +@@ -346,18 +369,6 @@ static void ks8851_flush_tx_work_spi(struct ks8851_net *ks) + flush_work(&kss->tx_work); + } + +-/** +- * calc_txlen - calculate size of message to send packet +- * @len: Length of data +- * +- * Returns the size of the TXFIFO message needed to send +- * this packet. +- */ +-static unsigned int calc_txlen(unsigned int len) +-{ +- return ALIGN(len + 4, 4); +-} +- + /** + * ks8851_start_xmit_spi - transmit packet using SPI + * @skb: The buffer to transmit +@@ -386,16 +397,17 @@ static netdev_tx_t ks8851_start_xmit_spi(struct sk_buff *skb, + + spin_lock(&ks->statelock); + +- if (needed > ks->tx_space) { ++ if (ks->queued_len + needed > ks->tx_space) { + netif_stop_queue(dev); + ret = NETDEV_TX_BUSY; + } else { +- ks->tx_space -= needed; ++ ks->queued_len += needed; + skb_queue_tail(&ks->txq, skb); + } + + spin_unlock(&ks->statelock); +- schedule_work(&kss->tx_work); ++ if (ret == NETDEV_TX_OK) ++ schedule_work(&kss->tx_work); + + return ret; + } +diff --git a/drivers/net/ethernet/microsoft/Kconfig b/drivers/net/ethernet/microsoft/Kconfig +index fe4e7a7d9c0b5..8b6c4cc37c53c 100644 +--- a/drivers/net/ethernet/microsoft/Kconfig ++++ b/drivers/net/ethernet/microsoft/Kconfig +@@ -19,6 +19,7 @@ config MICROSOFT_MANA + tristate "Microsoft Azure Network Adapter (MANA) support" + depends on PCI_MSI && X86_64 + depends on PCI_HYPERV ++ select PAGE_POOL + help + This driver supports Microsoft Azure Network Adapter (MANA). + So far, the driver is only supported on X86_64. +diff --git a/drivers/net/ethernet/mscc/ocelot_stats.c b/drivers/net/ethernet/mscc/ocelot_stats.c +index 0066219bb0e89..6b95262dad904 100644 +--- a/drivers/net/ethernet/mscc/ocelot_stats.c ++++ b/drivers/net/ethernet/mscc/ocelot_stats.c +@@ -216,10 +216,10 @@ static void ocelot_port_rmon_stats_cb(struct ocelot *ocelot, int port, void *pri + rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_64]; + rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_65_127]; + rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_128_255]; +- rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_128_255]; +- rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_256_511]; +- rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_512_1023]; +- rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1024_1526]; ++ rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_256_511]; ++ rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_512_1023]; ++ rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_1024_1526]; ++ rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1527_MAX]; + } + + void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port, +diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c +index 4ea0e155bb0d5..5a1bf42ce1566 100644 +--- a/drivers/net/usb/ax88179_178a.c ++++ b/drivers/net/usb/ax88179_178a.c +@@ -173,6 +173,7 @@ struct ax88179_data { + u8 in_pm; + u32 wol_supported; + u32 wolopts; ++ u8 disconnecting; + }; + + struct ax88179_int_data { +@@ -208,6 +209,7 @@ static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, + { + int ret; + int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); ++ struct ax88179_data *ax179_data = dev->driver_priv; + + BUG_ON(!dev); + +@@ -219,7 +221,7 @@ static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, + ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, data, size); + +- if (unlikely(ret < 0)) ++ if (unlikely((ret < 0) && !(ret == -ENODEV && ax179_data->disconnecting))) + netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n", + index, ret); + +@@ -231,6 +233,7 @@ static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, + { + int ret; + int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); ++ struct ax88179_data *ax179_data = dev->driver_priv; + + BUG_ON(!dev); + +@@ -242,7 +245,7 @@ static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, + ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, data, size); + +- if (unlikely(ret < 0)) ++ if (unlikely((ret < 0) && !(ret == -ENODEV && ax179_data->disconnecting))) + netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n", + index, ret); + +@@ -492,6 +495,20 @@ static int ax88179_resume(struct usb_interface *intf) + return usbnet_resume(intf); + } + ++static void ax88179_disconnect(struct usb_interface *intf) ++{ ++ struct usbnet *dev = usb_get_intfdata(intf); ++ struct ax88179_data *ax179_data; ++ ++ if (!dev) ++ return; ++ ++ ax179_data = dev->driver_priv; ++ ax179_data->disconnecting = 1; ++ ++ usbnet_disconnect(intf); ++} ++ + static void + ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) + { +@@ -1906,7 +1923,7 @@ static struct usb_driver ax88179_178a_driver = { + .suspend = ax88179_suspend, + .resume = ax88179_resume, + .reset_resume = ax88179_resume, +- .disconnect = usbnet_disconnect, ++ .disconnect = ax88179_disconnect, + .supports_autosuspend = 1, + .disable_hub_initiated_lpm = 1, + }; +diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +index 39ab6526e6b85..796972f224326 100644 +--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c ++++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +@@ -3034,7 +3034,7 @@ static u32 iwl_trans_pcie_dump_rbs(struct iwl_trans *trans, + struct iwl_rxq *rxq = &trans_pcie->rxq[0]; + u32 i, r, j, rb_len = 0; + +- spin_lock(&rxq->lock); ++ spin_lock_bh(&rxq->lock); + + r = le16_to_cpu(iwl_get_closed_rb_stts(trans, rxq)) & 0x0FFF; + +@@ -3058,7 +3058,7 @@ static u32 iwl_trans_pcie_dump_rbs(struct iwl_trans *trans, + *data = iwl_fw_error_next_data(*data); + } + +- spin_unlock(&rxq->lock); ++ spin_unlock_bh(&rxq->lock); + + return rb_len; + } +diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c +index eb7c87b344b8f..5b906dbb1096c 100644 +--- a/drivers/nvme/host/core.c ++++ b/drivers/nvme/host/core.c +@@ -4835,6 +4835,8 @@ static void nvme_fw_act_work(struct work_struct *work) + struct nvme_ctrl, fw_act_work); + unsigned long fw_act_timeout; + ++ nvme_auth_stop(ctrl); ++ + if (ctrl->mtfa) + fw_act_timeout = jiffies + + msecs_to_jiffies(ctrl->mtfa * 100); +@@ -4890,7 +4892,6 @@ static bool nvme_handle_aen_notice(struct nvme_ctrl *ctrl, u32 result) + * firmware activation. + */ + if (nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING)) { +- nvme_auth_stop(ctrl); + requeue = false; + queue_work(nvme_wq, &ctrl->fw_act_work); + } +diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c +index f71c6457e3509..2425d4813c3c5 100644 +--- a/drivers/pinctrl/pinctrl-at91-pio4.c ++++ b/drivers/pinctrl/pinctrl-at91-pio4.c +@@ -1033,6 +1033,13 @@ static const struct of_device_id atmel_pctrl_of_match[] = { + } + }; + ++/* ++ * This lock class allows to tell lockdep that parent IRQ and children IRQ do ++ * not share the same class so it does not raise false positive ++ */ ++static struct lock_class_key atmel_lock_key; ++static struct lock_class_key atmel_request_key; ++ + static int atmel_pinctrl_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -1185,6 +1192,7 @@ static int atmel_pinctrl_probe(struct platform_device *pdev) + irq_set_chip_and_handler(irq, &atmel_gpio_irq_chip, + handle_simple_irq); + irq_set_chip_data(irq, atmel_pioctrl); ++ irq_set_lockdep_class(irq, &atmel_lock_key, &atmel_request_key); + dev_dbg(dev, + "atmel gpio irq domain: hwirq: %d, linux irq: %d\n", + i, irq); +diff --git a/drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c b/drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c +index 5b544fb7f3d88..3b18a03075f46 100644 +--- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c ++++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7100.c +@@ -489,7 +489,7 @@ static int starfive_dt_node_to_map(struct pinctrl_dev *pctldev, + + nmaps = 0; + ngroups = 0; +- for_each_child_of_node(np, child) { ++ for_each_available_child_of_node(np, child) { + int npinmux = of_property_count_u32_elems(child, "pinmux"); + int npins = of_property_count_u32_elems(child, "pins"); + +@@ -524,7 +524,7 @@ static int starfive_dt_node_to_map(struct pinctrl_dev *pctldev, + nmaps = 0; + ngroups = 0; + mutex_lock(&sfp->mutex); +- for_each_child_of_node(np, child) { ++ for_each_available_child_of_node(np, child) { + int npins; + int i; + +diff --git a/drivers/reset/core.c b/drivers/reset/core.c +index f0a076e94118f..92cc13ef3e566 100644 +--- a/drivers/reset/core.c ++++ b/drivers/reset/core.c +@@ -807,6 +807,9 @@ static void __reset_control_put_internal(struct reset_control *rstc) + { + lockdep_assert_held(&reset_list_mutex); + ++ if (IS_ERR_OR_NULL(rstc)) ++ return; ++ + kref_put(&rstc->refcnt, __reset_control_release); + } + +@@ -1017,11 +1020,8 @@ EXPORT_SYMBOL_GPL(reset_control_put); + void reset_control_bulk_put(int num_rstcs, struct reset_control_bulk_data *rstcs) + { + mutex_lock(&reset_list_mutex); +- while (num_rstcs--) { +- if (IS_ERR_OR_NULL(rstcs[num_rstcs].rstc)) +- continue; ++ while (num_rstcs--) + __reset_control_put_internal(rstcs[num_rstcs].rstc); +- } + mutex_unlock(&reset_list_mutex); + } + EXPORT_SYMBOL_GPL(reset_control_bulk_put); +diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h +index 7c6efde75da66..5e115e8b2ba46 100644 +--- a/drivers/scsi/aacraid/aacraid.h ++++ b/drivers/scsi/aacraid/aacraid.h +@@ -1678,7 +1678,6 @@ struct aac_dev + u32 handle_pci_error; + bool init_reset; + u8 soft_reset_support; +- u8 use_map_queue; + }; + + #define aac_adapter_interrupt(dev) \ +diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c +index 013a9a334972e..25cee03d7f973 100644 +--- a/drivers/scsi/aacraid/commsup.c ++++ b/drivers/scsi/aacraid/commsup.c +@@ -223,12 +223,8 @@ int aac_fib_setup(struct aac_dev * dev) + struct fib *aac_fib_alloc_tag(struct aac_dev *dev, struct scsi_cmnd *scmd) + { + struct fib *fibptr; +- u32 blk_tag; +- int i; + +- blk_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd)); +- i = blk_mq_unique_tag_to_tag(blk_tag); +- fibptr = &dev->fibs[i]; ++ fibptr = &dev->fibs[scsi_cmd_to_rq(scmd)->tag]; + /* + * Null out fields that depend on being zero at the start of + * each I/O +diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c +index bff49b8ab057d..5ba5c18b77b46 100644 +--- a/drivers/scsi/aacraid/linit.c ++++ b/drivers/scsi/aacraid/linit.c +@@ -19,7 +19,6 @@ + + #include + #include +-#include + #include + #include + #include +@@ -506,15 +505,6 @@ common_config: + return 0; + } + +-static void aac_map_queues(struct Scsi_Host *shost) +-{ +- struct aac_dev *aac = (struct aac_dev *)shost->hostdata; +- +- blk_mq_pci_map_queues(&shost->tag_set.map[HCTX_TYPE_DEFAULT], +- aac->pdev, 0); +- aac->use_map_queue = true; +-} +- + /** + * aac_change_queue_depth - alter queue depths + * @sdev: SCSI device we are considering +@@ -1499,7 +1489,6 @@ static struct scsi_host_template aac_driver_template = { + .bios_param = aac_biosparm, + .shost_groups = aac_host_groups, + .slave_configure = aac_slave_configure, +- .map_queues = aac_map_queues, + .change_queue_depth = aac_change_queue_depth, + .sdev_groups = aac_dev_groups, + .eh_abort_handler = aac_eh_abort, +@@ -1787,8 +1776,6 @@ static int aac_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) + shost->max_lun = AAC_MAX_LUN; + + pci_set_drvdata(pdev, shost); +- shost->nr_hw_queues = aac->max_msix; +- shost->host_tagset = 1; + + error = scsi_add_host(shost, &pdev->dev); + if (error) +@@ -1921,7 +1908,6 @@ static void aac_remove_one(struct pci_dev *pdev) + struct aac_dev *aac = (struct aac_dev *)shost->hostdata; + + aac_cancel_rescan_worker(aac); +- aac->use_map_queue = false; + scsi_remove_host(shost); + + __aac_shutdown(aac); +diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c +index 61949f3741886..11ef58204e96f 100644 +--- a/drivers/scsi/aacraid/src.c ++++ b/drivers/scsi/aacraid/src.c +@@ -493,10 +493,6 @@ static int aac_src_deliver_message(struct fib *fib) + #endif + + u16 vector_no; +- struct scsi_cmnd *scmd; +- u32 blk_tag; +- struct Scsi_Host *shost = dev->scsi_host_ptr; +- struct blk_mq_queue_map *qmap; + + atomic_inc(&q->numpending); + +@@ -509,25 +505,8 @@ static int aac_src_deliver_message(struct fib *fib) + if ((dev->comm_interface == AAC_COMM_MESSAGE_TYPE3) + && dev->sa_firmware) + vector_no = aac_get_vector(dev); +- else { +- if (!fib->vector_no || !fib->callback_data) { +- if (shost && dev->use_map_queue) { +- qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT]; +- vector_no = qmap->mq_map[raw_smp_processor_id()]; +- } +- /* +- * We hardcode the vector_no for +- * reserved commands as a valid shost is +- * absent during the init +- */ +- else +- vector_no = 0; +- } else { +- scmd = (struct scsi_cmnd *)fib->callback_data; +- blk_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd)); +- vector_no = blk_mq_unique_tag_to_hwq(blk_tag); +- } +- } ++ else ++ vector_no = fib->vector_no; + + if (native_hba) { + if (fib->flags & FIB_CONTEXT_FLAG_NATIVE_HBA_TMF) { +diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +index 05ddbb9bb7d8a..451a58e0fd969 100644 +--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c ++++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +@@ -429,7 +429,6 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev, + struct fcoe_ctlr *ctlr; + struct fcoe_rcv_info *fr; + struct fcoe_percpu_s *bg; +- struct sk_buff *tmp_skb; + + interface = container_of(ptype, struct bnx2fc_interface, + fcoe_packet_type); +@@ -441,11 +440,9 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev, + goto err; + } + +- tmp_skb = skb_share_check(skb, GFP_ATOMIC); +- if (!tmp_skb) +- goto err; +- +- skb = tmp_skb; ++ skb = skb_share_check(skb, GFP_ATOMIC); ++ if (!skb) ++ return -1; + + if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) { + printk(KERN_ERR PFX "bnx2fc_rcv: Wrong FC type frame\n"); +diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c +index 02520f9123066..9a289d6f2e5ee 100644 +--- a/drivers/scsi/scsi_error.c ++++ b/drivers/scsi/scsi_error.c +@@ -1108,6 +1108,7 @@ retry: + + scsi_log_send(scmd); + scmd->submitter = SUBMITTED_BY_SCSI_ERROR_HANDLER; ++ scmd->flags |= SCMD_LAST; + + /* + * Lock sdev->state_mutex to avoid that scsi_device_quiesce() can +@@ -2402,6 +2403,7 @@ scsi_ioctl_reset(struct scsi_device *dev, int __user *arg) + scsi_init_command(dev, scmd); + + scmd->submitter = SUBMITTED_BY_SCSI_RESET_IOCTL; ++ scmd->flags |= SCMD_LAST; + memset(&scmd->sdb, 0, sizeof(scmd->sdb)); + + scmd->cmd_len = 0; +diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c +index d89f92032c1c2..f691bce5c1477 100644 +--- a/drivers/thunderbolt/debugfs.c ++++ b/drivers/thunderbolt/debugfs.c +@@ -943,7 +943,7 @@ static void margining_port_remove(struct tb_port *port) + snprintf(dir_name, sizeof(dir_name), "port%d", port->port); + parent = debugfs_lookup(dir_name, port->sw->debugfs_dir); + if (parent) +- debugfs_remove_recursive(debugfs_lookup("margining", parent)); ++ debugfs_lookup_and_remove("margining", parent); + + kfree(port->usb4->margining); + port->usb4->margining = NULL; +diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c +index 05e28a5ce42b1..fe2173e37b061 100644 +--- a/drivers/usb/serial/ftdi_sio.c ++++ b/drivers/usb/serial/ftdi_sio.c +@@ -1033,9 +1033,9 @@ static const struct usb_device_id id_table_combined[] = { + { USB_DEVICE(FTDI_VID, ACTISENSE_USG_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_NGT_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_NGW_PID) }, +- { USB_DEVICE(FTDI_VID, ACTISENSE_D9AC_PID) }, +- { USB_DEVICE(FTDI_VID, ACTISENSE_D9AD_PID) }, +- { USB_DEVICE(FTDI_VID, ACTISENSE_D9AE_PID) }, ++ { USB_DEVICE(FTDI_VID, ACTISENSE_UID_PID) }, ++ { USB_DEVICE(FTDI_VID, ACTISENSE_USA_PID) }, ++ { USB_DEVICE(FTDI_VID, ACTISENSE_NGX_PID) }, + { USB_DEVICE(FTDI_VID, ACTISENSE_D9AF_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEAGAUGE_PID) }, + { USB_DEVICE(FTDI_VID, CHETCO_SEASWITCH_PID) }, +diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h +index e2099445db708..21a2b5a25fc09 100644 +--- a/drivers/usb/serial/ftdi_sio_ids.h ++++ b/drivers/usb/serial/ftdi_sio_ids.h +@@ -1568,9 +1568,9 @@ + #define ACTISENSE_USG_PID 0xD9A9 /* USG USB Serial Adapter */ + #define ACTISENSE_NGT_PID 0xD9AA /* NGT NMEA2000 Interface */ + #define ACTISENSE_NGW_PID 0xD9AB /* NGW NMEA2000 Gateway */ +-#define ACTISENSE_D9AC_PID 0xD9AC /* Actisense Reserved */ +-#define ACTISENSE_D9AD_PID 0xD9AD /* Actisense Reserved */ +-#define ACTISENSE_D9AE_PID 0xD9AE /* Actisense Reserved */ ++#define ACTISENSE_UID_PID 0xD9AC /* USB Isolating Device */ ++#define ACTISENSE_USA_PID 0xD9AD /* USB to Serial Adapter */ ++#define ACTISENSE_NGX_PID 0xD9AE /* NGX NMEA2000 Gateway */ + #define ACTISENSE_D9AF_PID 0xD9AF /* Actisense Reserved */ + #define CHETCO_SEAGAUGE_PID 0xA548 /* SeaGauge USB Adapter */ + #define CHETCO_SEASWITCH_PID 0xA549 /* SeaSwitch USB Adapter */ +diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c +index 7f2aa72d52e65..4adef92598709 100644 +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -272,6 +272,7 @@ static void option_instat_callback(struct urb *urb); + #define QUECTEL_PRODUCT_RM500Q 0x0800 + #define QUECTEL_PRODUCT_RM520N 0x0801 + #define QUECTEL_PRODUCT_EC200U 0x0901 ++#define QUECTEL_PRODUCT_EG912Y 0x6001 + #define QUECTEL_PRODUCT_EC200S_CN 0x6002 + #define QUECTEL_PRODUCT_EC200A 0x6005 + #define QUECTEL_PRODUCT_EM061K_LWW 0x6008 +@@ -1232,6 +1233,7 @@ static const struct usb_device_id option_ids[] = { + { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, 0x0700, 0xff), /* BG95 */ + .driver_info = RSVD(3) | ZLP }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0xff, 0x30) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0xff, 0x10), + .driver_info = ZLP }, +@@ -1244,6 +1246,7 @@ static const struct usb_device_id option_ids[] = { + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200U, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200S_CN, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200T, 0xff, 0, 0) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG912Y, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500K, 0xff, 0x00, 0x00) }, + + { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) }, +@@ -2242,6 +2245,8 @@ static const struct usb_device_id option_ids[] = { + .driver_info = RSVD(0) | RSVD(1) | RSVD(6) }, + { USB_DEVICE(0x0489, 0xe0b5), /* Foxconn T77W968 ESIM */ + .driver_info = RSVD(0) | RSVD(1) | RSVD(6) }, ++ { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0da, 0xff), /* Foxconn T99W265 MBIM variant */ ++ .driver_info = RSVD(3) | RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0db, 0xff), /* Foxconn T99W265 MBIM */ + .driver_info = RSVD(3) }, + { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0ee, 0xff), /* Foxconn T99W368 MBIM */ +diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h +index 20dcbccb290b3..fd68204374f2c 100644 +--- a/drivers/usb/storage/unusual_devs.h ++++ b/drivers/usb/storage/unusual_devs.h +@@ -1305,6 +1305,17 @@ UNUSUAL_DEV( 0x090c, 0x6000, 0x0100, 0x0100, + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_INITIAL_READ10 ), + ++/* ++ * Patch by Tasos Sahanidis ++ * This flash drive always shows up with write protect enabled ++ * during the first mode sense. ++ */ ++UNUSUAL_DEV(0x0951, 0x1697, 0x0100, 0x0100, ++ "Kingston", ++ "DT Ultimate G3", ++ USB_SC_DEVICE, USB_PR_DEVICE, NULL, ++ US_FL_NO_WP_DETECT), ++ + /* + * This Pentax still camera is not conformant + * to the USB storage specification: - +diff --git a/fs/afs/cell.c b/fs/afs/cell.c +index 988c2ac7cecec..926cb1188eba6 100644 +--- a/fs/afs/cell.c ++++ b/fs/afs/cell.c +@@ -409,10 +409,12 @@ static int afs_update_cell(struct afs_cell *cell) + if (ret == -ENOMEM) + goto out_wake; + +- ret = -ENOMEM; + vllist = afs_alloc_vlserver_list(0); +- if (!vllist) ++ if (!vllist) { ++ if (ret >= 0) ++ ret = -ENOMEM; + goto out_wake; ++ } + + switch (ret) { + case -ENODATA: +diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c +index 91e804c70dd0a..9937993cf29dc 100644 +--- a/fs/afs/dynroot.c ++++ b/fs/afs/dynroot.c +@@ -114,6 +114,7 @@ static int afs_probe_cell_name(struct dentry *dentry) + struct afs_net *net = afs_d2net(dentry); + const char *name = dentry->d_name.name; + size_t len = dentry->d_name.len; ++ char *result = NULL; + int ret; + + /* Names prefixed with a dot are R/W mounts. */ +@@ -131,9 +132,22 @@ static int afs_probe_cell_name(struct dentry *dentry) + } + + ret = dns_query(net->net, "afsdb", name, len, "srv=1", +- NULL, NULL, false); +- if (ret == -ENODATA || ret == -ENOKEY) ++ &result, NULL, false); ++ if (ret == -ENODATA || ret == -ENOKEY || ret == 0) + ret = -ENOENT; ++ if (ret > 0 && ret >= sizeof(struct dns_server_list_v1_header)) { ++ struct dns_server_list_v1_header *v1 = (void *)result; ++ ++ if (v1->hdr.zero == 0 && ++ v1->hdr.content == DNS_PAYLOAD_IS_SERVER_LIST && ++ v1->hdr.version == 1 && ++ (v1->status != DNS_LOOKUP_GOOD && ++ v1->status != DNS_LOOKUP_GOOD_WITH_BAD)) ++ return -ENOENT; ++ ++ } ++ ++ kfree(result); + return ret; + } + +@@ -252,20 +266,9 @@ static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags) + return 1; + } + +-/* +- * Allow the VFS to enquire as to whether a dentry should be unhashed (mustn't +- * sleep) +- * - called from dput() when d_count is going to 0. +- * - return 1 to request dentry be unhashed, 0 otherwise +- */ +-static int afs_dynroot_d_delete(const struct dentry *dentry) +-{ +- return d_really_is_positive(dentry); +-} +- + const struct dentry_operations afs_dynroot_dentry_operations = { + .d_revalidate = afs_dynroot_d_revalidate, +- .d_delete = afs_dynroot_d_delete, ++ .d_delete = always_delete_dentry, + .d_release = afs_d_release, + .d_automount = afs_d_automount, + }; +diff --git a/fs/afs/internal.h b/fs/afs/internal.h +index c2d70fc1698c0..fcbb598d8c85d 100644 +--- a/fs/afs/internal.h ++++ b/fs/afs/internal.h +@@ -585,6 +585,7 @@ struct afs_volume { + #define AFS_VOLUME_OFFLINE 4 /* - T if volume offline notice given */ + #define AFS_VOLUME_BUSY 5 /* - T if volume busy notice given */ + #define AFS_VOLUME_MAYBE_NO_IBULK 6 /* - T if some servers don't have InlineBulkStatus */ ++#define AFS_VOLUME_RM_TREE 7 /* - Set if volume removed from cell->volumes */ + #ifdef CONFIG_AFS_FSCACHE + struct fscache_volume *cache; /* Caching cookie */ + #endif +@@ -1517,6 +1518,7 @@ extern struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *, + extern struct afs_volume *afs_create_volume(struct afs_fs_context *); + extern int afs_activate_volume(struct afs_volume *); + extern void afs_deactivate_volume(struct afs_volume *); ++bool afs_try_get_volume(struct afs_volume *volume, enum afs_volume_trace reason); + extern struct afs_volume *afs_get_volume(struct afs_volume *, enum afs_volume_trace); + extern void afs_put_volume(struct afs_net *, struct afs_volume *, enum afs_volume_trace); + extern int afs_check_volume_status(struct afs_volume *, struct afs_operation *); +diff --git a/fs/afs/volume.c b/fs/afs/volume.c +index f4937029dcd72..1c9144e3e83ac 100644 +--- a/fs/afs/volume.c ++++ b/fs/afs/volume.c +@@ -32,8 +32,13 @@ static struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell, + } else if (p->vid > volume->vid) { + pp = &(*pp)->rb_right; + } else { +- volume = afs_get_volume(p, afs_volume_trace_get_cell_insert); +- goto found; ++ if (afs_try_get_volume(p, afs_volume_trace_get_cell_insert)) { ++ volume = p; ++ goto found; ++ } ++ ++ set_bit(AFS_VOLUME_RM_TREE, &volume->flags); ++ rb_replace_node_rcu(&p->cell_node, &volume->cell_node, &cell->volumes); + } + } + +@@ -56,7 +61,8 @@ static void afs_remove_volume_from_cell(struct afs_volume *volume) + afs_volume_trace_remove); + write_seqlock(&cell->volume_lock); + hlist_del_rcu(&volume->proc_link); +- rb_erase(&volume->cell_node, &cell->volumes); ++ if (!test_and_set_bit(AFS_VOLUME_RM_TREE, &volume->flags)) ++ rb_erase(&volume->cell_node, &cell->volumes); + write_sequnlock(&cell->volume_lock); + } + } +@@ -235,6 +241,20 @@ static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume) + _leave(" [destroyed]"); + } + ++/* ++ * Try to get a reference on a volume record. ++ */ ++bool afs_try_get_volume(struct afs_volume *volume, enum afs_volume_trace reason) ++{ ++ int r; ++ ++ if (__refcount_inc_not_zero(&volume->ref, &r)) { ++ trace_afs_volume(volume->vid, r + 1, reason); ++ return true; ++ } ++ return false; ++} ++ + /* + * Get a reference on a volume record. + */ +diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c +index 4cd8e44cba4c5..b27795e13ff31 100644 +--- a/fs/btrfs/free-space-cache.c ++++ b/fs/btrfs/free-space-cache.c +@@ -2685,13 +2685,8 @@ 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) +@@ -2719,8 +2714,7 @@ 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 && +- block_group->alloc_offset) { ++ if (block_group->zone_unusable == block_group->length) { + btrfs_mark_bg_unused(block_group); + } else if (bg_reclaim_threshold && + reclaimable_unusable >= +diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c +index 675dbed075d8e..99cb690da9893 100644 +--- a/fs/btrfs/zoned.c ++++ b/fs/btrfs/zoned.c +@@ -1574,19 +1574,9 @@ void btrfs_calc_zone_unusable(struct btrfs_block_group *cache) + return; + + WARN_ON(cache->bytes_super != 0); +- +- /* 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; +- } ++ 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; +@@ -1882,7 +1872,6 @@ struct btrfs_device *btrfs_zoned_get_device(struct btrfs_fs_info *fs_info, + bool btrfs_zone_activate(struct btrfs_block_group *block_group) + { + struct btrfs_fs_info *fs_info = block_group->fs_info; +- struct btrfs_space_info *space_info = block_group->space_info; + struct map_lookup *map; + struct btrfs_device *device; + u64 physical; +@@ -1894,7 +1883,6 @@ bool btrfs_zone_activate(struct btrfs_block_group *block_group) + + map = block_group->physical_map; + +- spin_lock(&space_info->lock); + spin_lock(&block_group->lock); + if (test_bit(BLOCK_GROUP_FLAG_ZONE_IS_ACTIVE, &block_group->runtime_flags)) { + ret = true; +@@ -1923,14 +1911,7 @@ 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); +- 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); + + /* For the active block group list */ + btrfs_get_block_group(block_group); +@@ -1943,7 +1924,6 @@ bool btrfs_zone_activate(struct btrfs_block_group *block_group) + + out_unlock: + spin_unlock(&block_group->lock); +- spin_unlock(&space_info->lock); + return ret; + } + +diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h +index 98a9cf5318731..a9681fecbd91f 100644 +--- a/fs/fuse/fuse_i.h ++++ b/fs/fuse/fuse_i.h +@@ -63,6 +63,19 @@ struct fuse_forget_link { + struct fuse_forget_link *next; + }; + ++/* Submount lookup tracking */ ++struct fuse_submount_lookup { ++ /** Refcount */ ++ refcount_t count; ++ ++ /** Unique ID, which identifies the inode between userspace ++ * and kernel */ ++ u64 nodeid; ++ ++ /** The request used for sending the FORGET message */ ++ struct fuse_forget_link *forget; ++}; ++ + /** FUSE inode */ + struct fuse_inode { + /** Inode data */ +@@ -155,6 +168,8 @@ struct fuse_inode { + */ + struct fuse_inode_dax *dax; + #endif ++ /** Submount specific lookup tracking */ ++ struct fuse_submount_lookup *submount_lookup; + }; + + /** FUSE inode state bits */ +diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c +index bc3c3e76c646d..f81000d968875 100644 +--- a/fs/fuse/inode.c ++++ b/fs/fuse/inode.c +@@ -68,6 +68,24 @@ struct fuse_forget_link *fuse_alloc_forget(void) + return kzalloc(sizeof(struct fuse_forget_link), GFP_KERNEL_ACCOUNT); + } + ++static struct fuse_submount_lookup *fuse_alloc_submount_lookup(void) ++{ ++ struct fuse_submount_lookup *sl; ++ ++ sl = kzalloc(sizeof(struct fuse_submount_lookup), GFP_KERNEL_ACCOUNT); ++ if (!sl) ++ return NULL; ++ sl->forget = fuse_alloc_forget(); ++ if (!sl->forget) ++ goto out_free; ++ ++ return sl; ++ ++out_free: ++ kfree(sl); ++ return NULL; ++} ++ + static struct inode *fuse_alloc_inode(struct super_block *sb) + { + struct fuse_inode *fi; +@@ -83,6 +101,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) + fi->attr_version = 0; + fi->orig_ino = 0; + fi->state = 0; ++ fi->submount_lookup = NULL; + mutex_init(&fi->mutex); + spin_lock_init(&fi->lock); + fi->forget = fuse_alloc_forget(); +@@ -113,6 +132,17 @@ static void fuse_free_inode(struct inode *inode) + kmem_cache_free(fuse_inode_cachep, fi); + } + ++static void fuse_cleanup_submount_lookup(struct fuse_conn *fc, ++ struct fuse_submount_lookup *sl) ++{ ++ if (!refcount_dec_and_test(&sl->count)) ++ return; ++ ++ fuse_queue_forget(fc, sl->forget, sl->nodeid, 1); ++ sl->forget = NULL; ++ kfree(sl); ++} ++ + static void fuse_evict_inode(struct inode *inode) + { + struct fuse_inode *fi = get_fuse_inode(inode); +@@ -132,6 +162,11 @@ static void fuse_evict_inode(struct inode *inode) + fi->nlookup); + fi->forget = NULL; + } ++ ++ if (fi->submount_lookup) { ++ fuse_cleanup_submount_lookup(fc, fi->submount_lookup); ++ fi->submount_lookup = NULL; ++ } + } + if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode)) { + WARN_ON(!list_empty(&fi->write_files)); +@@ -311,6 +346,13 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, + fuse_dax_dontcache(inode, attr->flags); + } + ++static void fuse_init_submount_lookup(struct fuse_submount_lookup *sl, ++ u64 nodeid) ++{ ++ sl->nodeid = nodeid; ++ refcount_set(&sl->count, 1); ++} ++ + static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) + { + inode->i_mode = attr->mode & S_IFMT; +@@ -368,12 +410,22 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, + */ + if (fc->auto_submounts && (attr->flags & FUSE_ATTR_SUBMOUNT) && + S_ISDIR(attr->mode)) { ++ struct fuse_inode *fi; ++ + inode = new_inode(sb); + if (!inode) + return NULL; + + fuse_init_inode(inode, attr); +- get_fuse_inode(inode)->nodeid = nodeid; ++ fi = get_fuse_inode(inode); ++ fi->nodeid = nodeid; ++ fi->submount_lookup = fuse_alloc_submount_lookup(); ++ if (!fi->submount_lookup) { ++ iput(inode); ++ return NULL; ++ } ++ /* Sets nlookup = 1 on fi->submount_lookup->nlookup */ ++ fuse_init_submount_lookup(fi->submount_lookup, nodeid); + inode->i_flags |= S_AUTOMOUNT; + goto done; + } +@@ -396,11 +448,11 @@ retry: + iput(inode); + goto retry; + } +-done: + fi = get_fuse_inode(inode); + spin_lock(&fi->lock); + fi->nlookup++; + spin_unlock(&fi->lock); ++done: + fuse_change_attributes(inode, attr, attr_valid, attr_version); + + return inode; +@@ -1439,6 +1491,8 @@ static int fuse_fill_super_submount(struct super_block *sb, + struct super_block *parent_sb = parent_fi->inode.i_sb; + struct fuse_attr root_attr; + struct inode *root; ++ struct fuse_submount_lookup *sl; ++ struct fuse_inode *fi; + + fuse_sb_defaults(sb); + fm->sb = sb; +@@ -1461,12 +1515,27 @@ static int fuse_fill_super_submount(struct super_block *sb, + * its nlookup should not be incremented. fuse_iget() does + * that, though, so undo it here. + */ +- get_fuse_inode(root)->nlookup--; ++ fi = get_fuse_inode(root); ++ fi->nlookup--; ++ + sb->s_d_op = &fuse_dentry_operations; + sb->s_root = d_make_root(root); + if (!sb->s_root) + return -ENOMEM; + ++ /* ++ * Grab the parent's submount_lookup pointer and take a ++ * reference on the shared nlookup from the parent. This is to ++ * prevent the last forget for this nodeid from getting ++ * triggered until all users have finished with it. ++ */ ++ sl = parent_fi->submount_lookup; ++ WARN_ON(!sl); ++ if (sl) { ++ refcount_inc(&sl->count); ++ fi->submount_lookup = sl; ++ } ++ + return 0; + } + +diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c +index 0acb455368f23..5df8d93233376 100644 +--- a/fs/smb/client/cifs_debug.c ++++ b/fs/smb/client/cifs_debug.c +@@ -38,11 +38,13 @@ void cifs_dump_detail(void *buf, struct TCP_Server_Info *server) + #ifdef CONFIG_CIFS_DEBUG2 + struct smb_hdr *smb = buf; + +- cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d\n", +- smb->Command, smb->Status.CifsError, +- smb->Flags, smb->Flags2, smb->Mid, smb->Pid); +- cifs_dbg(VFS, "smb buf %p len %u\n", smb, +- server->ops->calc_smb_size(smb)); ++ cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d Wct: %d\n", ++ smb->Command, smb->Status.CifsError, smb->Flags, ++ smb->Flags2, smb->Mid, smb->Pid, smb->WordCount); ++ if (!server->ops->check_message(buf, server->total_read, server)) { ++ cifs_dbg(VFS, "smb buf %p len %u\n", smb, ++ server->ops->calc_smb_size(smb)); ++ } + #endif /* CONFIG_CIFS_DEBUG2 */ + } + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 2e814eadd6aef..512ac9dea9787 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -513,7 +513,8 @@ struct smb_version_operations { + struct mid_q_entry **, char **, int *); + enum securityEnum (*select_sectype)(struct TCP_Server_Info *, + enum securityEnum); +- int (*next_header)(char *); ++ int (*next_header)(struct TCP_Server_Info *server, char *buf, ++ unsigned int *noff); + /* ioctl passthrough for query_info */ + int (*ioctl_query_info)(const unsigned int xid, + struct cifs_tcon *tcon, +diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c +index 5b19918938346..f725a119ce312 100644 +--- a/fs/smb/client/connect.c ++++ b/fs/smb/client/connect.c +@@ -1225,7 +1225,12 @@ next_pdu: + server->total_read += length; + + if (server->ops->next_header) { +- next_offset = server->ops->next_header(buf); ++ if (server->ops->next_header(server, buf, &next_offset)) { ++ cifs_dbg(VFS, "%s: malformed response (next_offset=%u)\n", ++ __func__, next_offset); ++ cifs_reconnect(server, true); ++ continue; ++ } + if (next_offset) + server->pdu_size = next_offset; + } +diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c +index 31e06133acc3d..41290c12d0bcc 100644 +--- a/fs/smb/client/misc.c ++++ b/fs/smb/client/misc.c +@@ -350,6 +350,10 @@ checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server) + cifs_dbg(VFS, "Length less than smb header size\n"); + } + return -EIO; ++ } else if (total_read < sizeof(*smb) + 2 * smb->WordCount) { ++ cifs_dbg(VFS, "%s: can't read BCC due to invalid WordCount(%u)\n", ++ __func__, smb->WordCount); ++ return -EIO; + } + + /* otherwise, there is enough to get to the BCC */ +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index 1b3489a2f0db7..df03d80ab6d5f 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -5196,17 +5196,22 @@ smb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid) + NULL, 0, 0, false); + } + +-static int +-smb2_next_header(char *buf) ++static int smb2_next_header(struct TCP_Server_Info *server, char *buf, ++ unsigned int *noff) + { + struct smb2_hdr *hdr = (struct smb2_hdr *)buf; + struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf; + +- if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) +- return sizeof(struct smb2_transform_hdr) + +- le32_to_cpu(t_hdr->OriginalMessageSize); +- +- return le32_to_cpu(hdr->NextCommand); ++ if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { ++ *noff = le32_to_cpu(t_hdr->OriginalMessageSize); ++ if (unlikely(check_add_overflow(*noff, sizeof(*t_hdr), noff))) ++ return -EINVAL; ++ } else { ++ *noff = le32_to_cpu(hdr->NextCommand); ++ } ++ if (unlikely(*noff && *noff < MID_HEADER_SIZE(server))) ++ return -EINVAL; ++ return 0; + } + + static int +diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c +index 847d69d327c2a..05ff8a457a3d7 100644 +--- a/fs/smb/client/smb2pdu.c ++++ b/fs/smb/client/smb2pdu.c +@@ -372,10 +372,15 @@ static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, + void **request_buf, unsigned int *total_len) + { + /* BB eventually switch this to SMB2 specific small buf size */ +- if (smb2_command == SMB2_SET_INFO) ++ switch (smb2_command) { ++ case SMB2_SET_INFO: ++ case SMB2_QUERY_INFO: + *request_buf = cifs_buf_get(); +- else ++ break; ++ default: + *request_buf = cifs_small_buf_get(); ++ break; ++ } + if (*request_buf == NULL) { + /* BB should we add a retry in here if not a writepage? */ + return -ENOMEM; +@@ -3523,8 +3528,13 @@ SMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + struct smb2_query_info_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; ++ size_t len; + int rc; + ++ if (unlikely(check_add_overflow(input_len, sizeof(*req), &len) || ++ len > CIFSMaxBufSize)) ++ return -EINVAL; ++ + rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, + (void **) &req, &total_len); + if (rc) +@@ -3546,7 +3556,7 @@ SMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + + iov[0].iov_base = (char *)req; + /* 1 for Buffer */ +- iov[0].iov_len = total_len - 1 + input_len; ++ iov[0].iov_len = len; + return 0; + } + +@@ -3554,7 +3564,7 @@ void + SMB2_query_info_free(struct smb_rqst *rqst) + { + if (rqst && rqst->rq_iov) +- cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ ++ cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ + } + + static int +@@ -5439,6 +5449,11 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, + return 0; + } + ++static inline void free_qfs_info_req(struct kvec *iov) ++{ ++ cifs_buf_release(iov->iov_base); ++} ++ + int + SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) +@@ -5470,7 +5485,7 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); +- cifs_small_buf_release(iov.iov_base); ++ free_qfs_info_req(&iov); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + goto posix_qfsinf_exit; +@@ -5521,7 +5536,7 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); +- cifs_small_buf_release(iov.iov_base); ++ free_qfs_info_req(&iov); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + goto qfsinf_exit; +@@ -5588,7 +5603,7 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); +- cifs_small_buf_release(iov.iov_base); ++ free_qfs_info_req(&iov); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + goto qfsattr_exit; +diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c +index 6b7d95b65f4b6..f4728e65d1bda 100644 +--- a/fs/ubifs/tnc.c ++++ b/fs/ubifs/tnc.c +@@ -65,6 +65,7 @@ static void do_insert_old_idx(struct ubifs_info *c, + else { + ubifs_err(c, "old idx added twice!"); + kfree(old_idx); ++ return; + } + } + rb_link_node(&old_idx->rb, parent, p); +diff --git a/include/linux/bpf.h b/include/linux/bpf.h +index 1fba826f0acef..3ce9e39ecdb85 100644 +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -2681,6 +2681,9 @@ enum bpf_text_poke_type { + int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t, + void *addr1, void *addr2); + ++void bpf_arch_poke_desc_update(struct bpf_jit_poke_descriptor *poke, ++ struct bpf_prog *new, struct bpf_prog *old); ++ + void *bpf_arch_text_copy(void *dst, void *src, size_t len); + int bpf_arch_text_invalidate(void *dst, size_t len); + +diff --git a/include/linux/damon.h b/include/linux/damon.h +index b13be7ae2275e..e6941b239f449 100644 +--- a/include/linux/damon.h ++++ b/include/linux/damon.h +@@ -8,6 +8,7 @@ + #ifndef _DAMON_H_ + #define _DAMON_H_ + ++#include + #include + #include + #include +@@ -452,6 +453,8 @@ struct damon_ctx { + /* private: internal use only */ + struct timespec64 last_aggregation; + struct timespec64 last_ops_update; ++ /* for waiting until the execution of the kdamond_fn is started */ ++ struct completion kdamond_started; + + /* public: */ + struct task_struct *kdamond; +diff --git a/include/linux/dm-bufio.h b/include/linux/dm-bufio.h +index 1262d92ab88fc..2e71ca35942e9 100644 +--- a/include/linux/dm-bufio.h ++++ b/include/linux/dm-bufio.h +@@ -37,6 +37,8 @@ dm_bufio_client_create(struct block_device *bdev, unsigned int block_size, + */ + void dm_bufio_client_destroy(struct dm_bufio_client *c); + ++void dm_bufio_client_reset(struct dm_bufio_client *c); ++ + /* + * Set the sector range. + * When this function is called, there must be no I/O in progress on the bufio +diff --git a/include/linux/kasan.h b/include/linux/kasan.h +index 6e6f0238d63cc..4603e6e30c0ea 100644 +--- a/include/linux/kasan.h ++++ b/include/linux/kasan.h +@@ -471,10 +471,10 @@ static inline void kasan_free_module_shadow(const struct vm_struct *vm) {} + + #endif /* (CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS) && !CONFIG_KASAN_VMALLOC */ + +-#ifdef CONFIG_KASAN ++#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) + void kasan_non_canonical_hook(unsigned long addr); +-#else /* CONFIG_KASAN */ ++#else /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */ + static inline void kasan_non_canonical_hook(unsigned long addr) { } +-#endif /* CONFIG_KASAN */ ++#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */ + + #endif /* LINUX_KASAN_H */ +diff --git a/include/linux/key-type.h b/include/linux/key-type.h +index 7d985a1dfe4af..5caf3ce823733 100644 +--- a/include/linux/key-type.h ++++ b/include/linux/key-type.h +@@ -73,6 +73,7 @@ struct key_type { + + unsigned int flags; + #define KEY_TYPE_NET_DOMAIN 0x00000001 /* Keys of this type have a net namespace domain */ ++#define KEY_TYPE_INSTANT_REAP 0x00000002 /* Keys of this type don't have a delay after expiring */ + + /* vet a description */ + int (*vet_description)(const char *description); +diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h +index 3660ce6a93496..93ec34a94b724 100644 +--- a/include/linux/mlx5/driver.h ++++ b/include/linux/mlx5/driver.h +@@ -282,18 +282,23 @@ struct mlx5_cmd_stats { + struct mlx5_cmd { + struct mlx5_nb nb; + ++ /* members which needs to be queried or reinitialized each reload */ ++ struct { ++ u16 cmdif_rev; ++ u8 log_sz; ++ u8 log_stride; ++ int max_reg_cmds; ++ unsigned long bitmask; ++ struct semaphore sem; ++ struct semaphore pages_sem; ++ struct semaphore throttle_sem; ++ } vars; + enum mlx5_cmdif_state state; + void *cmd_alloc_buf; + dma_addr_t alloc_dma; + int alloc_size; + void *cmd_buf; + dma_addr_t dma; +- u16 cmdif_rev; +- u8 log_sz; +- u8 log_stride; +- int max_reg_cmds; +- int events; +- u32 __iomem *vector; + + /* protect command queue allocations + */ +@@ -303,11 +308,8 @@ struct mlx5_cmd { + */ + spinlock_t token_lock; + u8 token; +- unsigned long bitmask; + char wq_name[MLX5_CMD_WQ_MAX_NAME]; + struct workqueue_struct *wq; +- struct semaphore sem; +- struct semaphore pages_sem; + int mode; + u16 allowed_opcode; + struct mlx5_cmd_work_ent *ent_arr[MLX5_MAX_COMMANDS]; +diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h +index 583aebd8c1e01..5f8a534b65746 100644 +--- a/include/net/bluetooth/hci_core.h ++++ b/include/net/bluetooth/hci_core.h +@@ -187,6 +187,7 @@ struct blocked_key { + struct smp_csrk { + bdaddr_t bdaddr; + u8 bdaddr_type; ++ u8 link_type; + u8 type; + u8 val[16]; + }; +@@ -196,6 +197,7 @@ struct smp_ltk { + struct rcu_head rcu; + bdaddr_t bdaddr; + u8 bdaddr_type; ++ u8 link_type; + u8 authenticated; + u8 type; + u8 enc_size; +@@ -210,6 +212,7 @@ struct smp_irk { + bdaddr_t rpa; + bdaddr_t bdaddr; + u8 addr_type; ++ u8 link_type; + u8 val[16]; + }; + +@@ -217,6 +220,8 @@ struct link_key { + struct list_head list; + struct rcu_head rcu; + bdaddr_t bdaddr; ++ u8 bdaddr_type; ++ u8 link_type; + u8 type; + u8 val[HCI_LINK_KEY_SIZE]; + u8 pin_len; +diff --git a/include/trace/events/9p.h b/include/trace/events/9p.h +index 4dfa6d7f83baa..cd104a1343e2d 100644 +--- a/include/trace/events/9p.h ++++ b/include/trace/events/9p.h +@@ -178,18 +178,21 @@ TRACE_EVENT(9p_protocol_dump, + __field( void *, clnt ) + __field( __u8, type ) + __field( __u16, tag ) +- __array( unsigned char, line, P9_PROTO_DUMP_SZ ) ++ __dynamic_array(unsigned char, line, ++ min_t(size_t, pdu->capacity, P9_PROTO_DUMP_SZ)) + ), + + TP_fast_assign( + __entry->clnt = clnt; + __entry->type = pdu->id; + __entry->tag = pdu->tag; +- memcpy(__entry->line, pdu->sdata, P9_PROTO_DUMP_SZ); ++ memcpy(__get_dynamic_array(line), pdu->sdata, ++ __get_dynamic_array_len(line)); + ), +- TP_printk("clnt %lu %s(tag = %d)\n%.3x: %16ph\n%.3x: %16ph\n", ++ TP_printk("clnt %lu %s(tag = %d)\n%*ph\n", + (unsigned long)__entry->clnt, show_9p_op(__entry->type), +- __entry->tag, 0, __entry->line, 16, __entry->line + 16) ++ __entry->tag, __get_dynamic_array_len(line), ++ __get_dynamic_array(line)) + ); + + +diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c +index 832b2659e96e2..00f23febb9a7d 100644 +--- a/kernel/bpf/arraymap.c ++++ b/kernel/bpf/arraymap.c +@@ -997,11 +997,16 @@ static void prog_array_map_poke_untrack(struct bpf_map *map, + mutex_unlock(&aux->poke_mutex); + } + ++void __weak bpf_arch_poke_desc_update(struct bpf_jit_poke_descriptor *poke, ++ struct bpf_prog *new, struct bpf_prog *old) ++{ ++ WARN_ON_ONCE(1); ++} ++ + static void prog_array_map_poke_run(struct bpf_map *map, u32 key, + struct bpf_prog *old, + struct bpf_prog *new) + { +- u8 *old_addr, *new_addr, *old_bypass_addr; + struct prog_poke_elem *elem; + struct bpf_array_aux *aux; + +@@ -1010,7 +1015,7 @@ static void prog_array_map_poke_run(struct bpf_map *map, u32 key, + + list_for_each_entry(elem, &aux->poke_progs, list) { + struct bpf_jit_poke_descriptor *poke; +- int i, ret; ++ int i; + + for (i = 0; i < elem->aux->size_poke_tab; i++) { + poke = &elem->aux->poke_tab[i]; +@@ -1029,21 +1034,10 @@ static void prog_array_map_poke_run(struct bpf_map *map, u32 key, + * activated, so tail call updates can arrive from here + * while JIT is still finishing its final fixup for + * non-activated poke entries. +- * 3) On program teardown, the program's kallsym entry gets +- * removed out of RCU callback, but we can only untrack +- * from sleepable context, therefore bpf_arch_text_poke() +- * might not see that this is in BPF text section and +- * bails out with -EINVAL. As these are unreachable since +- * RCU grace period already passed, we simply skip them. +- * 4) Also programs reaching refcount of zero while patching ++ * 3) Also programs reaching refcount of zero while patching + * is in progress is okay since we're protected under + * poke_mutex and untrack the programs before the JIT +- * buffer is freed. When we're still in the middle of +- * patching and suddenly kallsyms entry of the program +- * gets evicted, we just skip the rest which is fine due +- * to point 3). +- * 5) Any other error happening below from bpf_arch_text_poke() +- * is a unexpected bug. ++ * buffer is freed. + */ + if (!READ_ONCE(poke->tailcall_target_stable)) + continue; +@@ -1053,39 +1047,7 @@ static void prog_array_map_poke_run(struct bpf_map *map, u32 key, + poke->tail_call.key != key) + continue; + +- old_bypass_addr = old ? NULL : poke->bypass_addr; +- old_addr = old ? (u8 *)old->bpf_func + poke->adj_off : NULL; +- new_addr = new ? (u8 *)new->bpf_func + poke->adj_off : NULL; +- +- if (new) { +- ret = bpf_arch_text_poke(poke->tailcall_target, +- BPF_MOD_JUMP, +- old_addr, new_addr); +- BUG_ON(ret < 0 && ret != -EINVAL); +- if (!old) { +- ret = bpf_arch_text_poke(poke->tailcall_bypass, +- BPF_MOD_JUMP, +- poke->bypass_addr, +- NULL); +- BUG_ON(ret < 0 && ret != -EINVAL); +- } +- } else { +- ret = bpf_arch_text_poke(poke->tailcall_bypass, +- BPF_MOD_JUMP, +- old_bypass_addr, +- poke->bypass_addr); +- BUG_ON(ret < 0 && ret != -EINVAL); +- /* let other CPUs finish the execution of program +- * so that it will not possible to expose them +- * to invalid nop, stack unwind, nop state +- */ +- if (!ret) +- synchronize_rcu(); +- ret = bpf_arch_text_poke(poke->tailcall_target, +- BPF_MOD_JUMP, +- old_addr, NULL); +- BUG_ON(ret < 0 && ret != -EINVAL); +- } ++ bpf_arch_poke_desc_update(poke, new, old); + } + } + } +diff --git a/kernel/trace/synth_event_gen_test.c b/kernel/trace/synth_event_gen_test.c +index 8d77526892f45..d944924cd1e1c 100644 +--- a/kernel/trace/synth_event_gen_test.c ++++ b/kernel/trace/synth_event_gen_test.c +@@ -477,6 +477,17 @@ static int __init synth_event_gen_test_init(void) + + ret = test_trace_synth_event(); + WARN_ON(ret); ++ ++ /* Disable when done */ ++ trace_array_set_clr_event(gen_synth_test->tr, ++ "synthetic", ++ "gen_synth_test", false); ++ trace_array_set_clr_event(empty_synth_test->tr, ++ "synthetic", ++ "empty_synth_test", false); ++ trace_array_set_clr_event(create_synth_test->tr, ++ "synthetic", ++ "create_synth_test", false); + out: + return ret; + } +diff --git a/lib/vsprintf.c b/lib/vsprintf.c +index 24f37bab8bc1f..fa1c197018551 100644 +--- a/lib/vsprintf.c ++++ b/lib/vsprintf.c +@@ -2092,15 +2092,20 @@ char *fwnode_full_name_string(struct fwnode_handle *fwnode, char *buf, + + /* Loop starting from the root node to the current node. */ + for (depth = fwnode_count_parents(fwnode); depth >= 0; depth--) { +- struct fwnode_handle *__fwnode = +- fwnode_get_nth_parent(fwnode, depth); ++ /* ++ * Only get a reference for other nodes (i.e. parent nodes). ++ * fwnode refcount may be 0 here. ++ */ ++ struct fwnode_handle *__fwnode = depth ? ++ fwnode_get_nth_parent(fwnode, depth) : fwnode; + + buf = string(buf, end, fwnode_get_name_prefix(__fwnode), + default_str_spec); + buf = string(buf, end, fwnode_get_name(__fwnode), + default_str_spec); + +- fwnode_handle_put(__fwnode); ++ if (depth) ++ fwnode_handle_put(__fwnode); + } + + return buf; +diff --git a/mm/damon/core.c b/mm/damon/core.c +index 36d098d06c558..5db9bec8ae67c 100644 +--- a/mm/damon/core.c ++++ b/mm/damon/core.c +@@ -383,6 +383,8 @@ struct damon_ctx *damon_new_ctx(void) + if (!ctx) + return NULL; + ++ init_completion(&ctx->kdamond_started); ++ + ctx->attrs.sample_interval = 5 * 1000; + ctx->attrs.aggr_interval = 100 * 1000; + ctx->attrs.ops_update_interval = 60 * 1000 * 1000; +@@ -519,11 +521,14 @@ static int __damon_start(struct damon_ctx *ctx) + mutex_lock(&ctx->kdamond_lock); + if (!ctx->kdamond) { + err = 0; ++ reinit_completion(&ctx->kdamond_started); + ctx->kdamond = kthread_run(kdamond_fn, ctx, "kdamond.%d", + nr_running_ctxs); + if (IS_ERR(ctx->kdamond)) { + err = PTR_ERR(ctx->kdamond); + ctx->kdamond = NULL; ++ } else { ++ wait_for_completion(&ctx->kdamond_started); + } + } + mutex_unlock(&ctx->kdamond_lock); +@@ -1147,6 +1152,8 @@ static int kdamond_fn(void *data) + + pr_debug("kdamond (%d) starts\n", current->pid); + ++ complete(&ctx->kdamond_started); ++ + if (ctx->ops.init) + ctx->ops.init(ctx); + if (ctx->callback.before_start && ctx->callback.before_start(ctx)) +diff --git a/mm/kasan/report.c b/mm/kasan/report.c +index 66a37f177d231..5d9ae80df4954 100644 +--- a/mm/kasan/report.c ++++ b/mm/kasan/report.c +@@ -523,8 +523,9 @@ void kasan_report_async(void) + } + #endif /* CONFIG_KASAN_HW_TAGS */ + ++#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) + /* +- * With CONFIG_KASAN, accesses to bogus pointers (outside the high ++ * With CONFIG_KASAN_INLINE, accesses to bogus pointers (outside the high + * canonical half of the address space) cause out-of-bounds shadow memory reads + * before the actual access. For addresses in the low canonical half of the + * address space, as well as most non-canonical addresses, that out-of-bounds +@@ -560,3 +561,4 @@ void kasan_non_canonical_hook(unsigned long addr) + pr_alert("KASAN: %s in range [0x%016lx-0x%016lx]\n", bug_type, + orig_addr, orig_addr + KASAN_GRANULE_SIZE - 1); + } ++#endif +diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c +index 0beb44f2fe1f0..f001582345052 100644 +--- a/net/8021q/vlan_core.c ++++ b/net/8021q/vlan_core.c +@@ -407,6 +407,8 @@ int vlan_vids_add_by_dev(struct net_device *dev, + return 0; + + list_for_each_entry(vid_info, &vlan_info->vid_list, list) { ++ if (!vlan_hw_filter_capable(by_dev, vid_info->proto)) ++ continue; + err = vlan_vid_add(dev, vid_info->proto, vid_info->vid); + if (err) + goto unwind; +@@ -417,6 +419,8 @@ unwind: + list_for_each_entry_continue_reverse(vid_info, + &vlan_info->vid_list, + list) { ++ if (!vlan_hw_filter_capable(by_dev, vid_info->proto)) ++ continue; + vlan_vid_del(dev, vid_info->proto, vid_info->vid); + } + +@@ -436,8 +440,11 @@ void vlan_vids_del_by_dev(struct net_device *dev, + if (!vlan_info) + return; + +- list_for_each_entry(vid_info, &vlan_info->vid_list, list) ++ list_for_each_entry(vid_info, &vlan_info->vid_list, list) { ++ if (!vlan_hw_filter_capable(by_dev, vid_info->proto)) ++ continue; + vlan_vid_del(dev, vid_info->proto, vid_info->vid); ++ } + } + EXPORT_SYMBOL(vlan_vids_del_by_dev); + +diff --git a/net/9p/protocol.c b/net/9p/protocol.c +index 4e3a2a1ffcb3f..0e6603b1ec906 100644 +--- a/net/9p/protocol.c ++++ b/net/9p/protocol.c +@@ -394,6 +394,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, + uint16_t *nwname = va_arg(ap, uint16_t *); + char ***wnames = va_arg(ap, char ***); + ++ *wnames = NULL; ++ + errcode = p9pdu_readf(pdu, proto_version, + "w", nwname); + if (!errcode) { +@@ -403,6 +405,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, + GFP_NOFS); + if (!*wnames) + errcode = -ENOMEM; ++ else ++ (*wnames)[0] = NULL; + } + + if (!errcode) { +@@ -414,8 +418,10 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, + proto_version, + "s", + &(*wnames)[i]); +- if (errcode) ++ if (errcode) { ++ (*wnames)[i] = NULL; + break; ++ } + } + } + +@@ -423,11 +429,14 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, + if (*wnames) { + int i; + +- for (i = 0; i < *nwname; i++) ++ for (i = 0; i < *nwname; i++) { ++ if (!(*wnames)[i]) ++ break; + kfree((*wnames)[i]); ++ } ++ kfree(*wnames); ++ *wnames = NULL; + } +- kfree(*wnames); +- *wnames = NULL; + } + } + break; +diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c +index 1c3c7ff5c3c66..f1b7510359e4b 100644 +--- a/net/bluetooth/af_bluetooth.c ++++ b/net/bluetooth/af_bluetooth.c +@@ -264,11 +264,14 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + if (flags & MSG_OOB) + return -EOPNOTSUPP; + ++ lock_sock(sk); ++ + skb = skb_recv_datagram(sk, flags, &err); + if (!skb) { + if (sk->sk_shutdown & RCV_SHUTDOWN) +- return 0; ++ err = 0; + ++ release_sock(sk); + return err; + } + +@@ -294,6 +297,8 @@ int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + + skb_free_datagram(sk, skb); + ++ release_sock(sk); ++ + if (flags & MSG_TRUNC) + copied = skblen; + +diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c +index c86a45344fe28..dcb13c64e8e7c 100644 +--- a/net/bluetooth/hci_event.c ++++ b/net/bluetooth/hci_event.c +@@ -515,6 +515,9 @@ static u8 hci_cc_read_class_of_dev(struct hci_dev *hdev, void *data, + { + struct hci_rp_read_class_of_dev *rp = data; + ++ if (WARN_ON(!hdev)) ++ return HCI_ERROR_UNSPECIFIED; ++ + bt_dev_dbg(hdev, "status 0x%2.2x", rp->status); + + if (rp->status) +@@ -746,9 +749,23 @@ static u8 hci_cc_read_enc_key_size(struct hci_dev *hdev, void *data, + } else { + conn->enc_key_size = rp->key_size; + status = 0; ++ ++ if (conn->enc_key_size < hdev->min_enc_key_size) { ++ /* As slave role, the conn->state has been set to ++ * BT_CONNECTED and l2cap conn req might not be received ++ * yet, at this moment the l2cap layer almost does ++ * nothing with the non-zero status. ++ * So we also clear encrypt related bits, and then the ++ * handler of l2cap conn req will get the right secure ++ * state at a later time. ++ */ ++ status = HCI_ERROR_AUTH_FAILURE; ++ clear_bit(HCI_CONN_ENCRYPT, &conn->flags); ++ clear_bit(HCI_CONN_AES_CCM, &conn->flags); ++ } + } + +- hci_encrypt_cfm(conn, 0); ++ hci_encrypt_cfm(conn, status); + + done: + hci_dev_unlock(hdev); +@@ -2298,7 +2315,8 @@ static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) + return; + } + +- set_bit(HCI_INQUIRY, &hdev->flags); ++ if (hci_sent_cmd_data(hdev, HCI_OP_INQUIRY)) ++ set_bit(HCI_INQUIRY, &hdev->flags); + } + + static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index a7899857aee5d..4c5793053393f 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -6493,6 +6493,14 @@ drop: + kfree_skb(skb); + } + ++static inline void l2cap_sig_send_rej(struct l2cap_conn *conn, u16 ident) ++{ ++ struct l2cap_cmd_rej_unk rej; ++ ++ rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD); ++ l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); ++} ++ + static inline void l2cap_sig_channel(struct l2cap_conn *conn, + struct sk_buff *skb) + { +@@ -6518,23 +6526,24 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, + + if (len > skb->len || !cmd->ident) { + BT_DBG("corrupted command"); ++ l2cap_sig_send_rej(conn, cmd->ident); + break; + } + + err = l2cap_bredr_sig_cmd(conn, cmd, len, skb->data); + if (err) { +- struct l2cap_cmd_rej_unk rej; +- + BT_ERR("Wrong link type (%d)", err); +- +- rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD); +- l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ, +- sizeof(rej), &rej); ++ l2cap_sig_send_rej(conn, cmd->ident); + } + + skb_pull(skb, len); + } + ++ if (skb->len > 0) { ++ BT_DBG("corrupted command"); ++ l2cap_sig_send_rej(conn, 0); ++ } ++ + drop: + kfree_skb(skb); + } +diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c +index d2e8565d0b33f..6d631a2e60166 100644 +--- a/net/bluetooth/mgmt.c ++++ b/net/bluetooth/mgmt.c +@@ -2883,7 +2883,8 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, + for (i = 0; i < key_count; i++) { + struct mgmt_link_key_info *key = &cp->keys[i]; + +- if (key->addr.type != BDADDR_BREDR || key->type > 0x08) ++ /* Considering SMP over BREDR/LE, there is no need to check addr_type */ ++ if (key->type > 0x08) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_LOAD_LINK_KEYS, + MGMT_STATUS_INVALID_PARAMS); +@@ -7129,6 +7130,7 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data, + + for (i = 0; i < irk_count; i++) { + struct mgmt_irk_info *irk = &cp->irks[i]; ++ u8 addr_type = le_addr_type(irk->addr.type); + + if (hci_is_blocked_key(hdev, + HCI_BLOCKED_KEY_TYPE_IRK, +@@ -7138,8 +7140,12 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data, + continue; + } + ++ /* When using SMP over BR/EDR, the addr type should be set to BREDR */ ++ if (irk->addr.type == BDADDR_BREDR) ++ addr_type = BDADDR_BREDR; ++ + hci_add_irk(hdev, &irk->addr.bdaddr, +- le_addr_type(irk->addr.type), irk->val, ++ addr_type, irk->val, + BDADDR_ANY); + } + +@@ -7220,6 +7226,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, + for (i = 0; i < key_count; i++) { + struct mgmt_ltk_info *key = &cp->keys[i]; + u8 type, authenticated; ++ u8 addr_type = le_addr_type(key->addr.type); + + if (hci_is_blocked_key(hdev, + HCI_BLOCKED_KEY_TYPE_LTK, +@@ -7254,8 +7261,12 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, + continue; + } + ++ /* When using SMP over BR/EDR, the addr type should be set to BREDR */ ++ if (key->addr.type == BDADDR_BREDR) ++ addr_type = BDADDR_BREDR; ++ + hci_add_ltk(hdev, &key->addr.bdaddr, +- le_addr_type(key->addr.type), type, authenticated, ++ addr_type, type, authenticated, + key->val, key->enc_size, key->ediv, key->rand); + } + +@@ -9523,7 +9534,7 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, + + ev.store_hint = persistent; + bacpy(&ev.key.addr.bdaddr, &key->bdaddr); +- ev.key.addr.type = BDADDR_BREDR; ++ ev.key.addr.type = link_to_bdaddr(key->link_type, key->bdaddr_type); + ev.key.type = key->type; + memcpy(ev.key.val, key->val, HCI_LINK_KEY_SIZE); + ev.key.pin_len = key->pin_len; +@@ -9574,7 +9585,7 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent) + ev.store_hint = persistent; + + bacpy(&ev.key.addr.bdaddr, &key->bdaddr); +- ev.key.addr.type = link_to_bdaddr(LE_LINK, key->bdaddr_type); ++ ev.key.addr.type = link_to_bdaddr(key->link_type, key->bdaddr_type); + ev.key.type = mgmt_ltk_type(key); + ev.key.enc_size = key->enc_size; + ev.key.ediv = key->ediv; +@@ -9603,7 +9614,7 @@ void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk, bool persistent) + + bacpy(&ev.rpa, &irk->rpa); + bacpy(&ev.irk.addr.bdaddr, &irk->bdaddr); +- ev.irk.addr.type = link_to_bdaddr(LE_LINK, irk->addr_type); ++ ev.irk.addr.type = link_to_bdaddr(irk->link_type, irk->addr_type); + memcpy(ev.irk.val, irk->val, sizeof(irk->val)); + + mgmt_event(MGMT_EV_NEW_IRK, hdev, &ev, sizeof(ev), NULL); +@@ -9632,7 +9643,7 @@ void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, + ev.store_hint = persistent; + + bacpy(&ev.key.addr.bdaddr, &csrk->bdaddr); +- ev.key.addr.type = link_to_bdaddr(LE_LINK, csrk->bdaddr_type); ++ ev.key.addr.type = link_to_bdaddr(csrk->link_type, csrk->bdaddr_type); + ev.key.type = csrk->type; + memcpy(ev.key.val, csrk->val, sizeof(csrk->val)); + +diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c +index 70663229b3cc9..ecb005bce65ac 100644 +--- a/net/bluetooth/smp.c ++++ b/net/bluetooth/smp.c +@@ -1058,6 +1058,7 @@ static void smp_notify_keys(struct l2cap_conn *conn) + } + + if (smp->remote_irk) { ++ smp->remote_irk->link_type = hcon->type; + mgmt_new_irk(hdev, smp->remote_irk, persistent); + + /* Now that user space can be considered to know the +@@ -1072,24 +1073,28 @@ static void smp_notify_keys(struct l2cap_conn *conn) + } + + if (smp->csrk) { ++ smp->csrk->link_type = hcon->type; + smp->csrk->bdaddr_type = hcon->dst_type; + bacpy(&smp->csrk->bdaddr, &hcon->dst); + mgmt_new_csrk(hdev, smp->csrk, persistent); + } + + if (smp->responder_csrk) { ++ smp->responder_csrk->link_type = hcon->type; + smp->responder_csrk->bdaddr_type = hcon->dst_type; + bacpy(&smp->responder_csrk->bdaddr, &hcon->dst); + mgmt_new_csrk(hdev, smp->responder_csrk, persistent); + } + + if (smp->ltk) { ++ smp->ltk->link_type = hcon->type; + smp->ltk->bdaddr_type = hcon->dst_type; + bacpy(&smp->ltk->bdaddr, &hcon->dst); + mgmt_new_ltk(hdev, smp->ltk, persistent); + } + + if (smp->responder_ltk) { ++ smp->responder_ltk->link_type = hcon->type; + smp->responder_ltk->bdaddr_type = hcon->dst_type; + bacpy(&smp->responder_ltk->bdaddr, &hcon->dst); + mgmt_new_ltk(hdev, smp->responder_ltk, persistent); +@@ -1109,6 +1114,8 @@ static void smp_notify_keys(struct l2cap_conn *conn) + key = hci_add_link_key(hdev, smp->conn->hcon, &hcon->dst, + smp->link_key, type, 0, &persistent); + if (key) { ++ key->link_type = hcon->type; ++ key->bdaddr_type = hcon->dst_type; + mgmt_new_link_key(hdev, key, persistent); + + /* Don't keep debug keys around if the relevant +diff --git a/net/core/dev.c b/net/core/dev.c +index 0d5aa820fd830..0a5566b6f8a25 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -3551,6 +3551,9 @@ static netdev_features_t gso_features_check(const struct sk_buff *skb, + if (gso_segs > READ_ONCE(dev->gso_max_segs)) + return features & ~NETIF_F_GSO_MASK; + ++ if (unlikely(skb->len >= READ_ONCE(dev->gso_max_size))) ++ return features & ~NETIF_F_GSO_MASK; ++ + if (!skb_shinfo(skb)->gso_type) { + skb_warn_bad_offload(skb); + return features & ~NETIF_F_GSO_MASK; +diff --git a/net/core/stream.c b/net/core/stream.c +index 051aa71a8ad0f..30e7deff4c551 100644 +--- a/net/core/stream.c ++++ b/net/core/stream.c +@@ -79,7 +79,7 @@ int sk_stream_wait_connect(struct sock *sk, long *timeo_p) + remove_wait_queue(sk_sleep(sk), &wait); + sk->sk_write_pending--; + } while (!done); +- return 0; ++ return done < 0 ? done : 0; + } + EXPORT_SYMBOL(sk_stream_wait_connect); + +diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c +index 3aced951d5ab8..03f8f33dc134c 100644 +--- a/net/dns_resolver/dns_key.c ++++ b/net/dns_resolver/dns_key.c +@@ -91,6 +91,7 @@ const struct cred *dns_resolver_cache; + static int + dns_resolver_preparse(struct key_preparsed_payload *prep) + { ++ const struct dns_server_list_v1_header *v1; + const struct dns_payload_header *bin; + struct user_key_payload *upayload; + unsigned long derrno; +@@ -122,6 +123,13 @@ dns_resolver_preparse(struct key_preparsed_payload *prep) + return -EINVAL; + } + ++ v1 = (const struct dns_server_list_v1_header *)bin; ++ if ((v1->status != DNS_LOOKUP_GOOD && ++ v1->status != DNS_LOOKUP_GOOD_WITH_BAD)) { ++ if (prep->expiry == TIME64_MAX) ++ prep->expiry = ktime_get_real_seconds() + 1; ++ } ++ + result_len = datalen; + goto store_result; + } +@@ -314,7 +322,7 @@ static long dns_resolver_read(const struct key *key, + + struct key_type key_type_dns_resolver = { + .name = "dns_resolver", +- .flags = KEY_TYPE_NET_DOMAIN, ++ .flags = KEY_TYPE_NET_DOMAIN | KEY_TYPE_INSTANT_REAP, + .preparse = dns_resolver_preparse, + .free_preparse = dns_resolver_free_preparse, + .instantiate = generic_key_instantiate, +diff --git a/net/ife/ife.c b/net/ife/ife.c +index 13bbf8cb6a396..be05b690b9ef2 100644 +--- a/net/ife/ife.c ++++ b/net/ife/ife.c +@@ -82,6 +82,7 @@ void *ife_decode(struct sk_buff *skb, u16 *metalen) + if (unlikely(!pskb_may_pull(skb, total_pull))) + return NULL; + ++ ifehdr = (struct ifeheadr *)(skb->data + skb->dev->hard_header_len); + skb_set_mac_header(skb, total_pull); + __skb_pull(skb, total_pull); + *metalen = ifehdrln - IFE_METAHDRLEN; +diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c +index 2ca442f485132..a2c4866080bd7 100644 +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -1694,10 +1694,10 @@ static int sta_link_apply_parameters(struct ieee80211_local *local, + lockdep_is_held(&local->sta_mtx)); + + /* +- * If there are no changes, then accept a link that doesn't exist, ++ * If there are no changes, then accept a link that exist, + * unless it's a new link. + */ +- if (params->link_id < 0 && !new_link && ++ if (params->link_id >= 0 && !new_link && + !params->link_mac && !params->txpwr_set && + !params->supported_rates_len && + !params->ht_capa && !params->vht_capa && +diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c +index bd0b7c189adfa..711c3377f428b 100644 +--- a/net/mac80211/mesh_plink.c ++++ b/net/mac80211/mesh_plink.c +@@ -1051,8 +1051,8 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata, + case WLAN_SP_MESH_PEERING_OPEN: + if (!matches_local) + event = OPN_RJCT; +- if (!mesh_plink_free_count(sdata) || +- (sta->mesh->plid && sta->mesh->plid != plid)) ++ else if (!mesh_plink_free_count(sdata) || ++ (sta->mesh->plid && sta->mesh->plid != plid)) + event = OPN_IGNR; + else + event = OPN_ACPT; +@@ -1060,9 +1060,9 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata, + case WLAN_SP_MESH_PEERING_CONFIRM: + if (!matches_local) + event = CNF_RJCT; +- if (!mesh_plink_free_count(sdata) || +- sta->mesh->llid != llid || +- (sta->mesh->plid && sta->mesh->plid != plid)) ++ else if (!mesh_plink_free_count(sdata) || ++ sta->mesh->llid != llid || ++ (sta->mesh->plid && sta->mesh->plid != plid)) + event = CNF_IGNR; + else + event = CNF_ACPT; +@@ -1230,6 +1230,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, + return; + } + elems = ieee802_11_parse_elems(baseaddr, len - baselen, true, NULL); +- mesh_process_plink_frame(sdata, mgmt, elems, rx_status); +- kfree(elems); ++ if (elems) { ++ mesh_process_plink_frame(sdata, mgmt, elems, rx_status); ++ kfree(elems); ++ } + } +diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c +index 2cc95c8dc4c7b..f74baefd855d3 100644 +--- a/net/rfkill/rfkill-gpio.c ++++ b/net/rfkill/rfkill-gpio.c +@@ -116,6 +116,14 @@ static int rfkill_gpio_probe(struct platform_device *pdev) + return -EINVAL; + } + ++ ret = gpiod_direction_output(rfkill->reset_gpio, true); ++ if (ret) ++ return ret; ++ ++ ret = gpiod_direction_output(rfkill->shutdown_gpio, true); ++ if (ret) ++ return ret; ++ + rfkill->rfkill_dev = rfkill_alloc(rfkill->name, &pdev->dev, + rfkill->type, &rfkill_gpio_ops, + rfkill); +diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c +index 674937284b8d2..29b74a569e0b0 100644 +--- a/net/rose/af_rose.c ++++ b/net/rose/af_rose.c +@@ -182,21 +182,47 @@ void rose_kill_by_neigh(struct rose_neigh *neigh) + */ + static void rose_kill_by_device(struct net_device *dev) + { +- struct sock *s; ++ struct sock *sk, *array[16]; ++ struct rose_sock *rose; ++ bool rescan; ++ int i, cnt; + ++start: ++ rescan = false; ++ cnt = 0; + spin_lock_bh(&rose_list_lock); +- sk_for_each(s, &rose_list) { +- struct rose_sock *rose = rose_sk(s); ++ sk_for_each(sk, &rose_list) { ++ rose = rose_sk(sk); ++ if (rose->device == dev) { ++ if (cnt == ARRAY_SIZE(array)) { ++ rescan = true; ++ break; ++ } ++ sock_hold(sk); ++ array[cnt++] = sk; ++ } ++ } ++ spin_unlock_bh(&rose_list_lock); + ++ for (i = 0; i < cnt; i++) { ++ sk = array[cnt]; ++ rose = rose_sk(sk); ++ lock_sock(sk); ++ spin_lock_bh(&rose_list_lock); + if (rose->device == dev) { +- rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); ++ rose_disconnect(sk, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); + if (rose->neighbour) + rose->neighbour->use--; + netdev_put(rose->device, &rose->dev_tracker); + rose->device = NULL; + } ++ spin_unlock_bh(&rose_list_lock); ++ release_sock(sk); ++ sock_put(sk); ++ cond_resched(); + } +- spin_unlock_bh(&rose_list_lock); ++ if (rescan) ++ goto start; + } + + /* +@@ -656,7 +682,10 @@ static int rose_release(struct socket *sock) + break; + } + ++ spin_lock_bh(&rose_list_lock); + netdev_put(rose->device, &rose->dev_tracker); ++ rose->device = NULL; ++ spin_unlock_bh(&rose_list_lock); + sock->sk = NULL; + release_sock(sk); + sock_put(sk); +diff --git a/net/wireless/certs/wens.hex b/net/wireless/certs/wens.hex +new file mode 100644 +index 0000000000000..0d50369bede98 +--- /dev/null ++++ b/net/wireless/certs/wens.hex +@@ -0,0 +1,87 @@ ++/* Chen-Yu Tsai's regdb certificate */ ++0x30, 0x82, 0x02, 0xa7, 0x30, 0x82, 0x01, 0x8f, ++0x02, 0x14, 0x61, 0xc0, 0x38, 0x65, 0x1a, 0xab, ++0xdc, 0xf9, 0x4b, 0xd0, 0xac, 0x7f, 0xf0, 0x6c, ++0x72, 0x48, 0xdb, 0x18, 0xc6, 0x00, 0x30, 0x0d, ++0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, ++0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x0f, 0x31, ++0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, ++0x0c, 0x04, 0x77, 0x65, 0x6e, 0x73, 0x30, 0x20, ++0x17, 0x0d, 0x32, 0x33, 0x31, 0x32, 0x30, 0x31, ++0x30, 0x37, 0x34, 0x31, 0x31, 0x34, 0x5a, 0x18, ++0x0f, 0x32, 0x31, 0x32, 0x33, 0x31, 0x31, 0x30, ++0x37, 0x30, 0x37, 0x34, 0x31, 0x31, 0x34, 0x5a, ++0x30, 0x0f, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, ++0x55, 0x04, 0x03, 0x0c, 0x04, 0x77, 0x65, 0x6e, ++0x73, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, ++0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, ++0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, ++0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, ++0x01, 0x00, 0xa9, 0x7a, 0x2c, 0x78, 0x4d, 0xa7, ++0x19, 0x2d, 0x32, 0x52, 0xa0, 0x2e, 0x6c, 0xef, ++0x88, 0x7f, 0x15, 0xc5, 0xb6, 0x69, 0x54, 0x16, ++0x43, 0x14, 0x79, 0x53, 0xb7, 0xae, 0x88, 0xfe, ++0xc0, 0xb7, 0x5d, 0x47, 0x8e, 0x1a, 0xe1, 0xef, ++0xb3, 0x90, 0x86, 0xda, 0xd3, 0x64, 0x81, 0x1f, ++0xce, 0x5d, 0x9e, 0x4b, 0x6e, 0x58, 0x02, 0x3e, ++0xb2, 0x6f, 0x5e, 0x42, 0x47, 0x41, 0xf4, 0x2c, ++0xb8, 0xa8, 0xd4, 0xaa, 0xc0, 0x0e, 0xe6, 0x48, ++0xf0, 0xa8, 0xce, 0xcb, 0x08, 0xae, 0x37, 0xaf, ++0xf6, 0x40, 0x39, 0xcb, 0x55, 0x6f, 0x5b, 0x4f, ++0x85, 0x34, 0xe6, 0x69, 0x10, 0x50, 0x72, 0x5e, ++0x4e, 0x9d, 0x4c, 0xba, 0x38, 0x36, 0x0d, 0xce, ++0x73, 0x38, 0xd7, 0x27, 0x02, 0x2a, 0x79, 0x03, ++0xe1, 0xac, 0xcf, 0xb0, 0x27, 0x85, 0x86, 0x93, ++0x17, 0xab, 0xec, 0x42, 0x77, 0x37, 0x65, 0x8a, ++0x44, 0xcb, 0xd6, 0x42, 0x93, 0x92, 0x13, 0xe3, ++0x39, 0x45, 0xc5, 0x6e, 0x00, 0x4a, 0x7f, 0xcb, ++0x42, 0x17, 0x2b, 0x25, 0x8c, 0xb8, 0x17, 0x3b, ++0x15, 0x36, 0x59, 0xde, 0x42, 0xce, 0x21, 0xe6, ++0xb6, 0xc7, 0x6e, 0x5e, 0x26, 0x1f, 0xf7, 0x8a, ++0x57, 0x9e, 0xa5, 0x96, 0x72, 0xb7, 0x02, 0x32, ++0xeb, 0x07, 0x2b, 0x73, 0xe2, 0x4f, 0x66, 0x58, ++0x9a, 0xeb, 0x0f, 0x07, 0xb6, 0xab, 0x50, 0x8b, ++0xc3, 0x8f, 0x17, 0xfa, 0x0a, 0x99, 0xc2, 0x16, ++0x25, 0xbf, 0x2d, 0x6b, 0x1a, 0xaa, 0xe6, 0x3e, ++0x5f, 0xeb, 0x6d, 0x9b, 0x5d, 0x4d, 0x42, 0x83, ++0x2d, 0x39, 0xb8, 0xc9, 0xac, 0xdb, 0x3a, 0x91, ++0x50, 0xdf, 0xbb, 0xb1, 0x76, 0x6d, 0x15, 0x73, ++0xfd, 0xc6, 0xe6, 0x6b, 0x71, 0x9e, 0x67, 0x36, ++0x22, 0x83, 0x79, 0xb1, 0xd6, 0xb8, 0x84, 0x52, ++0xaf, 0x96, 0x5b, 0xc3, 0x63, 0x02, 0x4e, 0x78, ++0x70, 0x57, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, ++0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, ++0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, ++0x01, 0x01, 0x00, 0x24, 0x28, 0xee, 0x22, 0x74, ++0x7f, 0x7c, 0xfa, 0x6c, 0x1f, 0xb3, 0x18, 0xd1, ++0xc2, 0x3d, 0x7d, 0x29, 0x42, 0x88, 0xad, 0x82, ++0xa5, 0xb1, 0x8a, 0x05, 0xd0, 0xec, 0x5c, 0x91, ++0x20, 0xf6, 0x82, 0xfd, 0xd5, 0x67, 0x60, 0x5f, ++0x31, 0xf5, 0xbd, 0x88, 0x91, 0x70, 0xbd, 0xb8, ++0xb9, 0x8c, 0x88, 0xfe, 0x53, 0xc9, 0x54, 0x9b, ++0x43, 0xc4, 0x7a, 0x43, 0x74, 0x6b, 0xdd, 0xb0, ++0xb1, 0x3b, 0x33, 0x45, 0x46, 0x78, 0xa3, 0x1c, ++0xef, 0x54, 0x68, 0xf7, 0x85, 0x9c, 0xe4, 0x51, ++0x6f, 0x06, 0xaf, 0x81, 0xdb, 0x2a, 0x7b, 0x7b, ++0x6f, 0xa8, 0x9c, 0x67, 0xd8, 0xcb, 0xc9, 0x91, ++0x40, 0x00, 0xae, 0xd9, 0xa1, 0x9f, 0xdd, 0xa6, ++0x43, 0x0e, 0x28, 0x7b, 0xaa, 0x1b, 0xe9, 0x84, ++0xdb, 0x76, 0x64, 0x42, 0x70, 0xc9, 0xc0, 0xeb, ++0xae, 0x84, 0x11, 0x16, 0x68, 0x4e, 0x84, 0x9e, ++0x7e, 0x92, 0x36, 0xee, 0x1c, 0x3b, 0x08, 0x63, ++0xeb, 0x79, 0x84, 0x15, 0x08, 0x9d, 0xaf, 0xc8, ++0x9a, 0xc7, 0x34, 0xd3, 0x94, 0x4b, 0xd1, 0x28, ++0x97, 0xbe, 0xd1, 0x45, 0x75, 0xdc, 0x35, 0x62, ++0xac, 0x1d, 0x1f, 0xb7, 0xb7, 0x15, 0x87, 0xc8, ++0x98, 0xc0, 0x24, 0x31, 0x56, 0x8d, 0xed, 0xdb, ++0x06, 0xc6, 0x46, 0xbf, 0x4b, 0x6d, 0xa6, 0xd5, ++0xab, 0xcc, 0x60, 0xfc, 0xe5, 0x37, 0xb6, 0x53, ++0x7d, 0x58, 0x95, 0xa9, 0x56, 0xc7, 0xf7, 0xee, ++0xc3, 0xa0, 0x76, 0xf7, 0x65, 0x4d, 0x53, 0xfa, ++0xff, 0x5f, 0x76, 0x33, 0x5a, 0x08, 0xfa, 0x86, ++0x92, 0x5a, 0x13, 0xfa, 0x1a, 0xfc, 0xf2, 0x1b, ++0x8c, 0x7f, 0x42, 0x6d, 0xb7, 0x7e, 0xb7, 0xb4, ++0xf0, 0xc7, 0x83, 0xbb, 0xa2, 0x81, 0x03, 0x2d, ++0xd4, 0x2a, 0x63, 0x3f, 0xf7, 0x31, 0x2e, 0x40, ++0x33, 0x5c, 0x46, 0xbc, 0x9b, 0xc1, 0x05, 0xa5, ++0x45, 0x4e, 0xc3, +diff --git a/net/wireless/core.h b/net/wireless/core.h +index e1accacc6f233..ee980965a7cfb 100644 +--- a/net/wireless/core.h ++++ b/net/wireless/core.h +@@ -297,6 +297,7 @@ struct cfg80211_cqm_config { + u32 rssi_hyst; + s32 last_rssi_event_value; + enum nl80211_cqm_rssi_threshold_event last_rssi_event_type; ++ bool use_range_api; + int n_rssi_thresholds; + s32 rssi_thresholds[]; + }; +diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c +index b19b5acfaf3a9..70fb14b8bab07 100644 +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -12574,10 +12574,6 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev, + int i, n, low_index; + int err; + +- /* RSSI reporting disabled? */ +- if (!cqm_config) +- return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0); +- + /* + * Obtain current RSSI value if possible, if not and no RSSI threshold + * event has been received yet, we should receive an event after a +@@ -12652,18 +12648,6 @@ static int nl80211_set_cqm_rssi(struct genl_info *info, + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) + return -EOPNOTSUPP; + +- if (n_thresholds <= 1 && rdev->ops->set_cqm_rssi_config) { +- if (n_thresholds == 0 || thresholds[0] == 0) /* Disabling */ +- return rdev_set_cqm_rssi_config(rdev, dev, 0, 0); +- +- return rdev_set_cqm_rssi_config(rdev, dev, +- thresholds[0], hysteresis); +- } +- +- if (!wiphy_ext_feature_isset(&rdev->wiphy, +- NL80211_EXT_FEATURE_CQM_RSSI_LIST)) +- return -EOPNOTSUPP; +- + if (n_thresholds == 1 && thresholds[0] == 0) /* Disabling */ + n_thresholds = 0; + +@@ -12671,6 +12655,26 @@ static int nl80211_set_cqm_rssi(struct genl_info *info, + old = rcu_dereference_protected(wdev->cqm_config, + lockdep_is_held(&wdev->mtx)); + ++ /* if already disabled just succeed */ ++ if (!n_thresholds && !old) { ++ err = 0; ++ goto unlock; ++ } ++ ++ if (n_thresholds > 1) { ++ if (!wiphy_ext_feature_isset(&rdev->wiphy, ++ NL80211_EXT_FEATURE_CQM_RSSI_LIST) || ++ !rdev->ops->set_cqm_rssi_range_config) { ++ err = -EOPNOTSUPP; ++ goto unlock; ++ } ++ } else { ++ if (!rdev->ops->set_cqm_rssi_config) { ++ err = -EOPNOTSUPP; ++ goto unlock; ++ } ++ } ++ + if (n_thresholds) { + cqm_config = kzalloc(struct_size(cqm_config, rssi_thresholds, + n_thresholds), +@@ -12685,13 +12689,26 @@ static int nl80211_set_cqm_rssi(struct genl_info *info, + memcpy(cqm_config->rssi_thresholds, thresholds, + flex_array_size(cqm_config, rssi_thresholds, + n_thresholds)); ++ cqm_config->use_range_api = n_thresholds > 1 || ++ !rdev->ops->set_cqm_rssi_config; + + rcu_assign_pointer(wdev->cqm_config, cqm_config); ++ ++ if (cqm_config->use_range_api) ++ err = cfg80211_cqm_rssi_update(rdev, dev, cqm_config); ++ else ++ err = rdev_set_cqm_rssi_config(rdev, dev, ++ thresholds[0], ++ hysteresis); + } else { + RCU_INIT_POINTER(wdev->cqm_config, NULL); ++ /* if enabled as range also disable via range */ ++ if (old->use_range_api) ++ err = rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0); ++ else ++ err = rdev_set_cqm_rssi_config(rdev, dev, 0, 0); + } + +- err = cfg80211_cqm_rssi_update(rdev, dev, cqm_config); + if (err) { + rcu_assign_pointer(wdev->cqm_config, old); + kfree_rcu(cqm_config, rcu_head); +@@ -18758,10 +18775,11 @@ void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy, struct wiphy_work *work) + wdev_lock(wdev); + cqm_config = rcu_dereference_protected(wdev->cqm_config, + lockdep_is_held(&wdev->mtx)); +- if (!wdev->cqm_config) ++ if (!cqm_config) + goto unlock; + +- cfg80211_cqm_rssi_update(rdev, wdev->netdev, cqm_config); ++ if (cqm_config->use_range_api) ++ cfg80211_cqm_rssi_update(rdev, wdev->netdev, cqm_config); + + rssi_level = cqm_config->last_rssi_event_value; + rssi_event = cqm_config->last_rssi_event_type; +diff --git a/security/keys/gc.c b/security/keys/gc.c +index 3c90807476eb0..eaddaceda14ea 100644 +--- a/security/keys/gc.c ++++ b/security/keys/gc.c +@@ -66,6 +66,19 @@ void key_schedule_gc(time64_t gc_at) + } + } + ++/* ++ * Set the expiration time on a key. ++ */ ++void key_set_expiry(struct key *key, time64_t expiry) ++{ ++ key->expiry = expiry; ++ if (expiry != TIME64_MAX) { ++ if (!(key->type->flags & KEY_TYPE_INSTANT_REAP)) ++ expiry += key_gc_delay; ++ key_schedule_gc(expiry); ++ } ++} ++ + /* + * Schedule a dead links collection run. + */ +@@ -176,7 +189,6 @@ static void key_garbage_collector(struct work_struct *work) + static u8 gc_state; /* Internal persistent state */ + #define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */ + #define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */ +-#define KEY_GC_SET_TIMER 0x04 /* - We need to restart the timer */ + #define KEY_GC_REAPING_DEAD_1 0x10 /* - We need to mark dead keys */ + #define KEY_GC_REAPING_DEAD_2 0x20 /* - We need to reap dead key links */ + #define KEY_GC_REAPING_DEAD_3 0x40 /* - We need to reap dead keys */ +@@ -184,21 +196,17 @@ static void key_garbage_collector(struct work_struct *work) + + struct rb_node *cursor; + struct key *key; +- time64_t new_timer, limit; ++ time64_t new_timer, limit, expiry; + + kenter("[%lx,%x]", key_gc_flags, gc_state); + + limit = ktime_get_real_seconds(); +- if (limit > key_gc_delay) +- limit -= key_gc_delay; +- else +- limit = key_gc_delay; + + /* Work out what we're going to be doing in this pass */ + gc_state &= KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2; + gc_state <<= 1; + if (test_and_clear_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags)) +- gc_state |= KEY_GC_REAPING_LINKS | KEY_GC_SET_TIMER; ++ gc_state |= KEY_GC_REAPING_LINKS; + + if (test_and_clear_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) + gc_state |= KEY_GC_REAPING_DEAD_1; +@@ -233,8 +241,11 @@ continue_scanning: + } + } + +- if (gc_state & KEY_GC_SET_TIMER) { +- if (key->expiry > limit && key->expiry < new_timer) { ++ expiry = key->expiry; ++ if (expiry != TIME64_MAX) { ++ if (!(key->type->flags & KEY_TYPE_INSTANT_REAP)) ++ expiry += key_gc_delay; ++ if (expiry > limit && expiry < new_timer) { + kdebug("will expire %x in %lld", + key_serial(key), key->expiry - limit); + new_timer = key->expiry; +@@ -276,7 +287,7 @@ maybe_resched: + */ + kdebug("pass complete"); + +- if (gc_state & KEY_GC_SET_TIMER && new_timer != (time64_t)TIME64_MAX) { ++ if (new_timer != TIME64_MAX) { + new_timer += key_gc_delay; + key_schedule_gc(new_timer); + } +diff --git a/security/keys/internal.h b/security/keys/internal.h +index 3c1e7122076b9..ec2ec335b6133 100644 +--- a/security/keys/internal.h ++++ b/security/keys/internal.h +@@ -174,6 +174,7 @@ extern unsigned key_gc_delay; + extern void keyring_gc(struct key *keyring, time64_t limit); + extern void keyring_restriction_gc(struct key *keyring, + struct key_type *dead_type); ++void key_set_expiry(struct key *key, time64_t expiry); + extern void key_schedule_gc(time64_t gc_at); + extern void key_schedule_gc_links(void); + extern void key_gc_keytype(struct key_type *ktype); +@@ -222,10 +223,18 @@ extern struct key *key_get_instantiation_authkey(key_serial_t target_id); + */ + static inline bool key_is_dead(const struct key *key, time64_t limit) + { ++ time64_t expiry = key->expiry; ++ ++ if (expiry != TIME64_MAX) { ++ if (!(key->type->flags & KEY_TYPE_INSTANT_REAP)) ++ expiry += key_gc_delay; ++ if (expiry <= limit) ++ return true; ++ } ++ + return + key->flags & ((1 << KEY_FLAG_DEAD) | + (1 << KEY_FLAG_INVALIDATED)) || +- (key->expiry > 0 && key->expiry <= limit) || + key->domain_tag->removed; + } + +diff --git a/security/keys/key.c b/security/keys/key.c +index c45afdd1dfbb4..e65240641ca57 100644 +--- a/security/keys/key.c ++++ b/security/keys/key.c +@@ -294,6 +294,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, + key->uid = uid; + key->gid = gid; + key->perm = perm; ++ key->expiry = TIME64_MAX; + key->restrict_link = restrict_link; + key->last_used_at = ktime_get_real_seconds(); + +@@ -463,10 +464,7 @@ static int __key_instantiate_and_link(struct key *key, + if (authkey) + key_invalidate(authkey); + +- if (prep->expiry != TIME64_MAX) { +- key->expiry = prep->expiry; +- key_schedule_gc(prep->expiry + key_gc_delay); +- } ++ key_set_expiry(key, prep->expiry); + } + } + +@@ -606,8 +604,7 @@ int key_reject_and_link(struct key *key, + atomic_inc(&key->user->nikeys); + mark_key_instantiated(key, -error); + notify_key(key, NOTIFY_KEY_INSTANTIATED, -error); +- key->expiry = ktime_get_real_seconds() + timeout; +- key_schedule_gc(key->expiry + key_gc_delay); ++ key_set_expiry(key, ktime_get_real_seconds() + timeout); + + if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) + awaken = 1; +@@ -722,16 +719,14 @@ found_kernel_type: + + void key_set_timeout(struct key *key, unsigned timeout) + { +- time64_t expiry = 0; ++ time64_t expiry = TIME64_MAX; + + /* make the changes with the locks held to prevent races */ + down_write(&key->sem); + + if (timeout > 0) + expiry = ktime_get_real_seconds() + timeout; +- +- key->expiry = expiry; +- key_schedule_gc(key->expiry + key_gc_delay); ++ key_set_expiry(key, expiry); + + up_write(&key->sem); + } +diff --git a/security/keys/proc.c b/security/keys/proc.c +index d0cde6685627f..4f4e2c1824f18 100644 +--- a/security/keys/proc.c ++++ b/security/keys/proc.c +@@ -198,7 +198,7 @@ static int proc_keys_show(struct seq_file *m, void *v) + + /* come up with a suitable timeout value */ + expiry = READ_ONCE(key->expiry); +- if (expiry == 0) { ++ if (expiry == TIME64_MAX) { + memcpy(xbuf, "perm", 5); + } else if (now >= expiry) { + memcpy(xbuf, "expd", 5); +diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c +index a7c361e0daebe..a88ed60dcd96a 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -9735,6 +9735,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1473, "ASUS GU604V", ALC285_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1483, "ASUS GU603V", ALC285_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A), ++ SND_PCI_QUIRK(0x1043, 0x1533, "ASUS GV302XA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1573, "ASUS GZ301V", ALC285_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK), + SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZV", ALC285_FIXUP_ASUS_HEADSET_MIC), +diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c +index 4d3c3365488a2..d8259afc60b08 100644 +--- a/sound/soc/codecs/hdmi-codec.c ++++ b/sound/soc/codecs/hdmi-codec.c +@@ -834,8 +834,9 @@ static int hdmi_dai_probe(struct snd_soc_dai *dai) + static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp, + unsigned int jack_status) + { +- if (hcp->jack && jack_status != hcp->jack_status) { +- snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_LINEOUT); ++ if (jack_status != hcp->jack_status) { ++ if (hcp->jack) ++ snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_LINEOUT); + hcp->jack_status = jack_status; + } + } +@@ -864,6 +865,13 @@ static int hdmi_codec_set_jack(struct snd_soc_component *component, + + if (hcp->hcd.ops->hook_plugged_cb) { + hcp->jack = jack; ++ ++ /* ++ * Report the initial jack status which may have been provided ++ * by the parent hdmi driver while the hpd hook was registered. ++ */ ++ snd_soc_jack_report(jack, hcp->jack_status, SND_JACK_LINEOUT); ++ + return 0; + } + +diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c +index 6364d9be28fbb..cf1cd0460ad98 100644 +--- a/sound/soc/fsl/fsl_sai.c ++++ b/sound/soc/fsl/fsl_sai.c +@@ -715,6 +715,9 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream, + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + unsigned int ofs = sai->soc_data->reg_offset; + ++ /* Clear xMR to avoid channel swap with mclk_with_tere enabled case */ ++ regmap_write(sai->regmap, FSL_SAI_xMR(tx), 0); ++ + regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), + FSL_SAI_CR3_TRCE_MASK, 0); + +diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c +index f458328f9ec42..33380cad3a735 100644 +--- a/sound/usb/quirks.c ++++ b/sound/usb/quirks.c +@@ -1385,7 +1385,7 @@ free_buf: + + static int snd_usb_motu_m_series_boot_quirk(struct usb_device *dev) + { +- msleep(2000); ++ msleep(4000); + + return 0; + } +@@ -1628,7 +1628,7 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev, + unsigned int id) + { + switch (id) { +- case USB_ID(0x07fd, 0x0008): /* MOTU M Series */ ++ case USB_ID(0x07fd, 0x0008): /* MOTU M Series, 1st hardware version */ + return snd_usb_motu_m_series_boot_quirk(dev); + } + +diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh +index ea6fc59e9f62f..e52d513009fb0 100755 +--- a/tools/testing/selftests/net/mptcp/mptcp_join.sh ++++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh +@@ -2652,7 +2652,7 @@ backup_tests() + fi + + if reset "mpc backup" && +- continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then ++ continue_if mptcp_lib_kallsyms_doesnt_have "T mptcp_subflow_send_ack$"; then + pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow,backup + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow + chk_join_nr 0 0 0 +@@ -2660,7 +2660,7 @@ backup_tests() + fi + + if reset "mpc backup both sides" && +- continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then ++ continue_if mptcp_lib_kallsyms_doesnt_have "T mptcp_subflow_send_ack$"; then + pm_nl_add_endpoint $ns1 10.0.1.1 flags subflow,backup + pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow,backup + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow +@@ -2669,7 +2669,7 @@ backup_tests() + fi + + if reset "mpc switch to backup" && +- continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then ++ continue_if mptcp_lib_kallsyms_doesnt_have "T mptcp_subflow_send_ack$"; then + pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup + chk_join_nr 0 0 0 +@@ -2677,7 +2677,7 @@ backup_tests() + fi + + if reset "mpc switch to backup both sides" && +- continue_if mptcp_lib_kallsyms_doesnt_have "mptcp_subflow_send_ack$"; then ++ continue_if mptcp_lib_kallsyms_doesnt_have "T mptcp_subflow_send_ack$"; then + pm_nl_add_endpoint $ns1 10.0.1.1 flags subflow + pm_nl_add_endpoint $ns2 10.0.1.2 flags subflow + run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup diff --git a/patch/kernel/odroidxu4-current/patch-6.1.70-71.patch b/patch/kernel/odroidxu4-current/patch-6.1.70-71.patch new file mode 100644 index 0000000000..b085fc4d77 --- /dev/null +++ b/patch/kernel/odroidxu4-current/patch-6.1.70-71.patch @@ -0,0 +1,7517 @@ +diff --git a/Makefile b/Makefile +index 270593fcafdcd..2840e36fd5596 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + VERSION = 6 + PATCHLEVEL = 1 +-SUBLEVEL = 70 ++SUBLEVEL = 71 + EXTRAVERSION = + NAME = Curry Ramen + +diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi +index 32d397b3950b9..b2e7f6a710740 100644 +--- a/arch/arm/boot/dts/am33xx.dtsi ++++ b/arch/arm/boot/dts/am33xx.dtsi +@@ -349,6 +349,7 @@ + , + , + ; ++ ti,sysc-delay-us = <2>; + clocks = <&l3s_clkctrl AM3_L3S_USB_OTG_HS_CLKCTRL 0>; + clock-names = "fck"; + #address-cells = <1>; +diff --git a/drivers/base/property.c b/drivers/base/property.c +index b0c40d9734847..eb9b01c2ff1d9 100644 +--- a/drivers/base/property.c ++++ b/drivers/base/property.c +@@ -17,12 +17,19 @@ + #include + #include + +-struct fwnode_handle *dev_fwnode(const struct device *dev) ++struct fwnode_handle *__dev_fwnode(struct device *dev) + { + return IS_ENABLED(CONFIG_OF) && dev->of_node ? + of_fwnode_handle(dev->of_node) : dev->fwnode; + } +-EXPORT_SYMBOL_GPL(dev_fwnode); ++EXPORT_SYMBOL_GPL(__dev_fwnode); ++ ++const struct fwnode_handle *__dev_fwnode_const(const struct device *dev) ++{ ++ return IS_ENABLED(CONFIG_OF) && dev->of_node ? ++ of_fwnode_handle(dev->of_node) : dev->fwnode; ++} ++EXPORT_SYMBOL_GPL(__dev_fwnode_const); + + /** + * device_property_present - check if a property of a device is present +diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c +index aec55f7e1f260..2d939773445d7 100644 +--- a/drivers/iio/imu/adis16475.c ++++ b/drivers/iio/imu/adis16475.c +@@ -1243,50 +1243,6 @@ static int adis16475_config_irq_pin(struct adis16475 *st) + return 0; + } + +-static const struct of_device_id adis16475_of_match[] = { +- { .compatible = "adi,adis16470", +- .data = &adis16475_chip_info[ADIS16470] }, +- { .compatible = "adi,adis16475-1", +- .data = &adis16475_chip_info[ADIS16475_1] }, +- { .compatible = "adi,adis16475-2", +- .data = &adis16475_chip_info[ADIS16475_2] }, +- { .compatible = "adi,adis16475-3", +- .data = &adis16475_chip_info[ADIS16475_3] }, +- { .compatible = "adi,adis16477-1", +- .data = &adis16475_chip_info[ADIS16477_1] }, +- { .compatible = "adi,adis16477-2", +- .data = &adis16475_chip_info[ADIS16477_2] }, +- { .compatible = "adi,adis16477-3", +- .data = &adis16475_chip_info[ADIS16477_3] }, +- { .compatible = "adi,adis16465-1", +- .data = &adis16475_chip_info[ADIS16465_1] }, +- { .compatible = "adi,adis16465-2", +- .data = &adis16475_chip_info[ADIS16465_2] }, +- { .compatible = "adi,adis16465-3", +- .data = &adis16475_chip_info[ADIS16465_3] }, +- { .compatible = "adi,adis16467-1", +- .data = &adis16475_chip_info[ADIS16467_1] }, +- { .compatible = "adi,adis16467-2", +- .data = &adis16475_chip_info[ADIS16467_2] }, +- { .compatible = "adi,adis16467-3", +- .data = &adis16475_chip_info[ADIS16467_3] }, +- { .compatible = "adi,adis16500", +- .data = &adis16475_chip_info[ADIS16500] }, +- { .compatible = "adi,adis16505-1", +- .data = &adis16475_chip_info[ADIS16505_1] }, +- { .compatible = "adi,adis16505-2", +- .data = &adis16475_chip_info[ADIS16505_2] }, +- { .compatible = "adi,adis16505-3", +- .data = &adis16475_chip_info[ADIS16505_3] }, +- { .compatible = "adi,adis16507-1", +- .data = &adis16475_chip_info[ADIS16507_1] }, +- { .compatible = "adi,adis16507-2", +- .data = &adis16475_chip_info[ADIS16507_2] }, +- { .compatible = "adi,adis16507-3", +- .data = &adis16475_chip_info[ADIS16507_3] }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, adis16475_of_match); + + static int adis16475_probe(struct spi_device *spi) + { +@@ -1300,7 +1256,7 @@ static int adis16475_probe(struct spi_device *spi) + + st = iio_priv(indio_dev); + +- st->info = device_get_match_data(&spi->dev); ++ st->info = spi_get_device_match_data(spi); + if (!st->info) + return -EINVAL; + +@@ -1340,12 +1296,83 @@ static int adis16475_probe(struct spi_device *spi) + return 0; + } + ++static const struct of_device_id adis16475_of_match[] = { ++ { .compatible = "adi,adis16470", ++ .data = &adis16475_chip_info[ADIS16470] }, ++ { .compatible = "adi,adis16475-1", ++ .data = &adis16475_chip_info[ADIS16475_1] }, ++ { .compatible = "adi,adis16475-2", ++ .data = &adis16475_chip_info[ADIS16475_2] }, ++ { .compatible = "adi,adis16475-3", ++ .data = &adis16475_chip_info[ADIS16475_3] }, ++ { .compatible = "adi,adis16477-1", ++ .data = &adis16475_chip_info[ADIS16477_1] }, ++ { .compatible = "adi,adis16477-2", ++ .data = &adis16475_chip_info[ADIS16477_2] }, ++ { .compatible = "adi,adis16477-3", ++ .data = &adis16475_chip_info[ADIS16477_3] }, ++ { .compatible = "adi,adis16465-1", ++ .data = &adis16475_chip_info[ADIS16465_1] }, ++ { .compatible = "adi,adis16465-2", ++ .data = &adis16475_chip_info[ADIS16465_2] }, ++ { .compatible = "adi,adis16465-3", ++ .data = &adis16475_chip_info[ADIS16465_3] }, ++ { .compatible = "adi,adis16467-1", ++ .data = &adis16475_chip_info[ADIS16467_1] }, ++ { .compatible = "adi,adis16467-2", ++ .data = &adis16475_chip_info[ADIS16467_2] }, ++ { .compatible = "adi,adis16467-3", ++ .data = &adis16475_chip_info[ADIS16467_3] }, ++ { .compatible = "adi,adis16500", ++ .data = &adis16475_chip_info[ADIS16500] }, ++ { .compatible = "adi,adis16505-1", ++ .data = &adis16475_chip_info[ADIS16505_1] }, ++ { .compatible = "adi,adis16505-2", ++ .data = &adis16475_chip_info[ADIS16505_2] }, ++ { .compatible = "adi,adis16505-3", ++ .data = &adis16475_chip_info[ADIS16505_3] }, ++ { .compatible = "adi,adis16507-1", ++ .data = &adis16475_chip_info[ADIS16507_1] }, ++ { .compatible = "adi,adis16507-2", ++ .data = &adis16475_chip_info[ADIS16507_2] }, ++ { .compatible = "adi,adis16507-3", ++ .data = &adis16475_chip_info[ADIS16507_3] }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, adis16475_of_match); ++ ++static const struct spi_device_id adis16475_ids[] = { ++ { "adis16470", (kernel_ulong_t)&adis16475_chip_info[ADIS16470] }, ++ { "adis16475-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] }, ++ { "adis16475-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] }, ++ { "adis16475-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] }, ++ { "adis16477-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] }, ++ { "adis16477-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] }, ++ { "adis16477-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] }, ++ { "adis16465-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] }, ++ { "adis16465-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] }, ++ { "adis16465-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] }, ++ { "adis16467-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] }, ++ { "adis16467-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] }, ++ { "adis16467-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] }, ++ { "adis16500", (kernel_ulong_t)&adis16475_chip_info[ADIS16500] }, ++ { "adis16505-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] }, ++ { "adis16505-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] }, ++ { "adis16505-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] }, ++ { "adis16507-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] }, ++ { "adis16507-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] }, ++ { "adis16507-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] }, ++ { } ++}; ++MODULE_DEVICE_TABLE(spi, adis16475_ids); ++ + static struct spi_driver adis16475_driver = { + .driver = { + .name = "adis16475", + .of_match_table = adis16475_of_match, + }, + .probe = adis16475_probe, ++ .id_table = adis16475_ids, + }; + module_spi_driver(adis16475_driver); + +diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c +index c4f22d50dba58..78daf2b2143c5 100644 +--- a/drivers/spi/spi-atmel.c ++++ b/drivers/spi/spi-atmel.c +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + #include + + /* SPI register offsets */ +@@ -278,6 +279,7 @@ struct atmel_spi { + bool keep_cs; + + u32 fifo_size; ++ bool last_polarity; + u8 native_cs_free; + u8 native_cs_for_gpio; + }; +@@ -290,6 +292,22 @@ struct atmel_spi_device { + #define SPI_MAX_DMA_XFER 65535 /* true for both PDC and DMA */ + #define INVALID_DMA_ADDRESS 0xffffffff + ++/* ++ * This frequency can be anything supported by the controller, but to avoid ++ * unnecessary delay, the highest possible frequency is chosen. ++ * ++ * This frequency is the highest possible which is not interfering with other ++ * chip select registers (see Note for Serial Clock Bit Rate configuration in ++ * Atmel-11121F-ATARM-SAMA5D3-Series-Datasheet_02-Feb-16, page 1283) ++ */ ++#define DUMMY_MSG_FREQUENCY 0x02 ++/* ++ * 8 bits is the minimum data the controller is capable of sending. ++ * ++ * This message can be anything as it should not be treated by any SPI device. ++ */ ++#define DUMMY_MSG 0xAA ++ + /* + * Version 2 of the SPI controller has + * - CR.LASTXFER +@@ -303,6 +321,43 @@ static bool atmel_spi_is_v2(struct atmel_spi *as) + return as->caps.is_spi2; + } + ++/* ++ * Send a dummy message. ++ * ++ * This is sometimes needed when using a CS GPIO to force clock transition when ++ * switching between devices with different polarities. ++ */ ++static void atmel_spi_send_dummy(struct atmel_spi *as, struct spi_device *spi, int chip_select) ++{ ++ u32 status; ++ u32 csr; ++ ++ /* ++ * Set a clock frequency to allow sending message on SPI bus. ++ * The frequency here can be anything, but is needed for ++ * the controller to send the data. ++ */ ++ csr = spi_readl(as, CSR0 + 4 * chip_select); ++ csr = SPI_BFINS(SCBR, DUMMY_MSG_FREQUENCY, csr); ++ spi_writel(as, CSR0 + 4 * chip_select, csr); ++ ++ /* ++ * Read all data coming from SPI bus, needed to be able to send ++ * the message. ++ */ ++ spi_readl(as, RDR); ++ while (spi_readl(as, SR) & SPI_BIT(RDRF)) { ++ spi_readl(as, RDR); ++ cpu_relax(); ++ } ++ ++ spi_writel(as, TDR, DUMMY_MSG); ++ ++ readl_poll_timeout_atomic(as->regs + SPI_SR, status, ++ (status & SPI_BIT(TXEMPTY)), 1, 1000); ++} ++ ++ + /* + * Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby + * they assume that spi slave device state will not change on deselect, so +@@ -319,11 +374,17 @@ static bool atmel_spi_is_v2(struct atmel_spi *as) + * Master on Chip Select 0.") No workaround exists for that ... so for + * nCS0 on that chip, we (a) don't use the GPIO, (b) can't support CS_HIGH, + * and (c) will trigger that first erratum in some cases. ++ * ++ * When changing the clock polarity, the SPI controller waits for the next ++ * transmission to enforce the default clock state. This may be an issue when ++ * using a GPIO as Chip Select: the clock level is applied only when the first ++ * packet is sent, once the CS has already been asserted. The workaround is to ++ * avoid this by sending a first (dummy) message before toggling the CS state. + */ +- + static void cs_activate(struct atmel_spi *as, struct spi_device *spi) + { + struct atmel_spi_device *asd = spi->controller_state; ++ bool new_polarity; + int chip_select; + u32 mr; + +@@ -352,6 +413,25 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi) + } + + mr = spi_readl(as, MR); ++ ++ /* ++ * Ensures the clock polarity is valid before we actually ++ * assert the CS to avoid spurious clock edges to be ++ * processed by the spi devices. ++ */ ++ if (spi_get_csgpiod(spi, 0)) { ++ new_polarity = (asd->csr & SPI_BIT(CPOL)) != 0; ++ if (new_polarity != as->last_polarity) { ++ /* ++ * Need to disable the GPIO before sending the dummy ++ * message because it is already set by the spi core. ++ */ ++ gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), 0); ++ atmel_spi_send_dummy(as, spi, chip_select); ++ as->last_polarity = new_polarity; ++ gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), 1); ++ } ++ } + } else { + u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0; + int i; +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index 5d046be8b2dd5..22d227878bc44 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -360,6 +360,18 @@ const struct spi_device_id *spi_get_device_id(const struct spi_device *sdev) + } + EXPORT_SYMBOL_GPL(spi_get_device_id); + ++const void *spi_get_device_match_data(const struct spi_device *sdev) ++{ ++ const void *match; ++ ++ match = device_get_match_data(&sdev->dev); ++ if (match) ++ return match; ++ ++ return (const void *)spi_get_device_id(sdev)->driver_data; ++} ++EXPORT_SYMBOL_GPL(spi_get_device_match_data); ++ + static int spi_match_device(struct device *dev, struct device_driver *drv) + { + const struct spi_device *spi = to_spi_device(dev); +@@ -592,7 +604,7 @@ static void spi_dev_set_name(struct spi_device *spi) + } + + dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->controller->dev), +- spi->chip_select); ++ spi_get_chipselect(spi, 0)); + } + + static int spi_dev_check(struct device *dev, void *data) +@@ -601,7 +613,7 @@ static int spi_dev_check(struct device *dev, void *data) + struct spi_device *new_spi = data; + + if (spi->controller == new_spi->controller && +- spi->chip_select == new_spi->chip_select) ++ spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0)) + return -EBUSY; + return 0; + } +@@ -626,7 +638,7 @@ static int __spi_add_device(struct spi_device *spi) + status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check); + if (status) { + dev_err(dev, "chipselect %d already in use\n", +- spi->chip_select); ++ spi_get_chipselect(spi, 0)); + return status; + } + +@@ -637,7 +649,7 @@ static int __spi_add_device(struct spi_device *spi) + } + + if (ctlr->cs_gpiods) +- spi->cs_gpiod = ctlr->cs_gpiods[spi->chip_select]; ++ spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi, 0)]); + + /* + * Drivers may modify this initial i/o setup, but will +@@ -680,8 +692,8 @@ int spi_add_device(struct spi_device *spi) + int status; + + /* Chipselects are numbered 0..max; validate. */ +- if (spi->chip_select >= ctlr->num_chipselect) { +- dev_err(dev, "cs%d >= max %d\n", spi->chip_select, ++ if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) { ++ dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0), + ctlr->num_chipselect); + return -EINVAL; + } +@@ -702,8 +714,8 @@ static int spi_add_device_locked(struct spi_device *spi) + struct device *dev = ctlr->dev.parent; + + /* Chipselects are numbered 0..max; validate. */ +- if (spi->chip_select >= ctlr->num_chipselect) { +- dev_err(dev, "cs%d >= max %d\n", spi->chip_select, ++ if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) { ++ dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0), + ctlr->num_chipselect); + return -EINVAL; + } +@@ -749,7 +761,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr, + + WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias)); + +- proxy->chip_select = chip->chip_select; ++ spi_set_chipselect(proxy, 0, chip->chip_select); + proxy->max_speed_hz = chip->max_speed_hz; + proxy->mode = chip->mode; + proxy->irq = chip->irq; +@@ -958,24 +970,23 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) + * Avoid calling into the driver (or doing delays) if the chip select + * isn't actually changing from the last time this was called. + */ +- if (!force && ((enable && spi->controller->last_cs == spi->chip_select) || +- (!enable && spi->controller->last_cs != spi->chip_select)) && ++ if (!force && ((enable && spi->controller->last_cs == spi_get_chipselect(spi, 0)) || ++ (!enable && spi->controller->last_cs != spi_get_chipselect(spi, 0))) && + (spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH))) + return; + + trace_spi_set_cs(spi, activate); + +- spi->controller->last_cs = enable ? spi->chip_select : -1; ++ spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1; + spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH; + +- if ((spi->cs_gpiod || !spi->controller->set_cs_timing) && !activate) { ++ if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) && !activate) + spi_delay_exec(&spi->cs_hold, NULL); +- } + + if (spi->mode & SPI_CS_HIGH) + enable = !enable; + +- if (spi->cs_gpiod) { ++ if (spi_get_csgpiod(spi, 0)) { + if (!(spi->mode & SPI_NO_CS)) { + /* + * Historically ACPI has no means of the GPIO polarity and +@@ -988,10 +999,10 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) + * into account. + */ + if (has_acpi_companion(&spi->dev)) +- gpiod_set_value_cansleep(spi->cs_gpiod, !enable); ++ gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable); + else + /* Polarity handled by GPIO library */ +- gpiod_set_value_cansleep(spi->cs_gpiod, activate); ++ gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate); + } + /* Some SPI masters need both GPIO CS & slave_select */ + if ((spi->controller->flags & SPI_MASTER_GPIO_SS) && +@@ -1001,7 +1012,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) + spi->controller->set_cs(spi, !enable); + } + +- if (spi->cs_gpiod || !spi->controller->set_cs_timing) { ++ if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) { + if (activate) + spi_delay_exec(&spi->cs_setup, NULL); + else +@@ -2291,7 +2302,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, + nc, rc); + return rc; + } +- spi->chip_select = value; ++ spi_set_chipselect(spi, 0, value); + + /* Device speed */ + if (!of_property_read_u32(nc, "spi-max-frequency", &value)) +@@ -2405,7 +2416,7 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi, + strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias)); + + /* Use provided chip-select for ancillary device */ +- ancillary->chip_select = chip_select; ++ spi_set_chipselect(ancillary, 0, chip_select); + + /* Take over SPI mode/speed from SPI main device */ + ancillary->max_speed_hz = spi->max_speed_hz; +@@ -2652,7 +2663,7 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, + spi->mode |= lookup.mode; + spi->irq = lookup.irq; + spi->bits_per_word = lookup.bits_per_word; +- spi->chip_select = lookup.chip_select; ++ spi_set_chipselect(spi, 0, lookup.chip_select); + + return spi; + } +@@ -3611,6 +3622,37 @@ static int __spi_validate_bits_per_word(struct spi_controller *ctlr, + return 0; + } + ++/** ++ * spi_set_cs_timing - configure CS setup, hold, and inactive delays ++ * @spi: the device that requires specific CS timing configuration ++ * ++ * Return: zero on success, else a negative error code. ++ */ ++static int spi_set_cs_timing(struct spi_device *spi) ++{ ++ struct device *parent = spi->controller->dev.parent; ++ int status = 0; ++ ++ if (spi->controller->set_cs_timing && !spi_get_csgpiod(spi, 0)) { ++ if (spi->controller->auto_runtime_pm) { ++ status = pm_runtime_get_sync(parent); ++ if (status < 0) { ++ pm_runtime_put_noidle(parent); ++ dev_err(&spi->controller->dev, "Failed to power device: %d\n", ++ status); ++ return status; ++ } ++ ++ status = spi->controller->set_cs_timing(spi); ++ pm_runtime_mark_last_busy(parent); ++ pm_runtime_put_autosuspend(parent); ++ } else { ++ status = spi->controller->set_cs_timing(spi); ++ } ++ } ++ return status; ++} ++ + /** + * spi_setup - setup SPI mode and clock rate + * @spi: the device whose settings are being modified +@@ -3707,6 +3749,12 @@ int spi_setup(struct spi_device *spi) + } + } + ++ status = spi_set_cs_timing(spi); ++ if (status) { ++ mutex_unlock(&spi->controller->io_mutex); ++ return status; ++ } ++ + if (spi->controller->auto_runtime_pm && spi->controller->set_cs) { + status = pm_runtime_resume_and_get(spi->controller->dev.parent); + if (status < 0) { +@@ -3790,7 +3838,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) + * cs_change is set for each transfer. + */ + if ((spi->mode & SPI_CS_WORD) && (!(ctlr->mode_bits & SPI_CS_WORD) || +- spi->cs_gpiod)) { ++ spi_get_csgpiod(spi, 0))) { + size_t maxsize; + int ret; + +diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c +index c4c1fbc12b4cd..dc968960769e1 100644 +--- a/drivers/usb/host/fotg210-hcd.c ++++ b/drivers/usb/host/fotg210-hcd.c +@@ -429,8 +429,6 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, + temp = size; + size -= temp; + next += temp; +- if (temp == size) +- goto done; + } + + temp = snprintf(next, size, "\n"); +@@ -440,7 +438,6 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, + size -= temp; + next += temp; + +-done: + *sizep = size; + *nextp = next; + } +diff --git a/fs/namei.c b/fs/namei.c +index 5e1c2ab2ae709..b5578f4ce5d6e 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -253,6 +253,7 @@ getname_kernel(const char * filename) + + return result; + } ++EXPORT_SYMBOL(getname_kernel); + + void putname(struct filename *name) + { +@@ -271,6 +272,7 @@ void putname(struct filename *name) + } else + __putname(name); + } ++EXPORT_SYMBOL(putname); + + /** + * check_acl - perform ACL permission checking +@@ -1581,8 +1583,9 @@ static struct dentry *lookup_dcache(const struct qstr *name, + * when directory is guaranteed to have no in-lookup children + * at all. + */ +-static struct dentry *__lookup_hash(const struct qstr *name, +- struct dentry *base, unsigned int flags) ++struct dentry *lookup_one_qstr_excl(const struct qstr *name, ++ struct dentry *base, ++ unsigned int flags) + { + struct dentry *dentry = lookup_dcache(name, base, flags); + struct dentry *old; +@@ -1606,6 +1609,7 @@ static struct dentry *__lookup_hash(const struct qstr *name, + } + return dentry; + } ++EXPORT_SYMBOL(lookup_one_qstr_excl); + + static struct dentry *lookup_fast(struct nameidata *nd) + { +@@ -2532,16 +2536,17 @@ static int path_parentat(struct nameidata *nd, unsigned flags, + } + + /* Note: this does not consume "name" */ +-static int filename_parentat(int dfd, struct filename *name, +- unsigned int flags, struct path *parent, +- struct qstr *last, int *type) ++static int __filename_parentat(int dfd, struct filename *name, ++ unsigned int flags, struct path *parent, ++ struct qstr *last, int *type, ++ const struct path *root) + { + int retval; + struct nameidata nd; + + if (IS_ERR(name)) + return PTR_ERR(name); +- set_nameidata(&nd, dfd, name, NULL); ++ set_nameidata(&nd, dfd, name, root); + retval = path_parentat(&nd, flags | LOOKUP_RCU, parent); + if (unlikely(retval == -ECHILD)) + retval = path_parentat(&nd, flags, parent); +@@ -2556,6 +2561,13 @@ static int filename_parentat(int dfd, struct filename *name, + return retval; + } + ++static int filename_parentat(int dfd, struct filename *name, ++ unsigned int flags, struct path *parent, ++ struct qstr *last, int *type) ++{ ++ return __filename_parentat(dfd, name, flags, parent, last, type, NULL); ++} ++ + /* does lookup, returns the object with parent locked */ + static struct dentry *__kern_path_locked(struct filename *name, struct path *path) + { +@@ -2571,7 +2583,7 @@ static struct dentry *__kern_path_locked(struct filename *name, struct path *pat + return ERR_PTR(-EINVAL); + } + inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT); +- d = __lookup_hash(&last, path->dentry, 0); ++ d = lookup_one_qstr_excl(&last, path->dentry, 0); + if (IS_ERR(d)) { + inode_unlock(path->dentry->d_inode); + path_put(path); +@@ -2599,6 +2611,24 @@ int kern_path(const char *name, unsigned int flags, struct path *path) + } + EXPORT_SYMBOL(kern_path); + ++/** ++ * vfs_path_parent_lookup - lookup a parent path relative to a dentry-vfsmount pair ++ * @filename: filename structure ++ * @flags: lookup flags ++ * @parent: pointer to struct path to fill ++ * @last: last component ++ * @type: type of the last component ++ * @root: pointer to struct path of the base directory ++ */ ++int vfs_path_parent_lookup(struct filename *filename, unsigned int flags, ++ struct path *parent, struct qstr *last, int *type, ++ const struct path *root) ++{ ++ return __filename_parentat(AT_FDCWD, filename, flags, parent, last, ++ type, root); ++} ++EXPORT_SYMBOL(vfs_path_parent_lookup); ++ + /** + * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair + * @dentry: pointer to dentry of the base directory +@@ -2980,20 +3010,10 @@ static inline int may_create(struct user_namespace *mnt_userns, + return inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC); + } + +-/* +- * p1 and p2 should be directories on the same fs. +- */ +-struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) ++static struct dentry *lock_two_directories(struct dentry *p1, struct dentry *p2) + { + struct dentry *p; + +- if (p1 == p2) { +- inode_lock_nested(p1->d_inode, I_MUTEX_PARENT); +- return NULL; +- } +- +- mutex_lock(&p1->d_sb->s_vfs_rename_mutex); +- + p = d_ancestor(p2, p1); + if (p) { + inode_lock_nested(p2->d_inode, I_MUTEX_PARENT); +@@ -3012,8 +3032,64 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) + I_MUTEX_PARENT, I_MUTEX_PARENT2); + return NULL; + } ++ ++/* ++ * p1 and p2 should be directories on the same fs. ++ */ ++struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) ++{ ++ if (p1 == p2) { ++ inode_lock_nested(p1->d_inode, I_MUTEX_PARENT); ++ return NULL; ++ } ++ ++ mutex_lock(&p1->d_sb->s_vfs_rename_mutex); ++ return lock_two_directories(p1, p2); ++} + EXPORT_SYMBOL(lock_rename); + ++/* ++ * c1 and p2 should be on the same fs. ++ */ ++struct dentry *lock_rename_child(struct dentry *c1, struct dentry *p2) ++{ ++ if (READ_ONCE(c1->d_parent) == p2) { ++ /* ++ * hopefully won't need to touch ->s_vfs_rename_mutex at all. ++ */ ++ inode_lock_nested(p2->d_inode, I_MUTEX_PARENT); ++ /* ++ * now that p2 is locked, nobody can move in or out of it, ++ * so the test below is safe. ++ */ ++ if (likely(c1->d_parent == p2)) ++ return NULL; ++ ++ /* ++ * c1 got moved out of p2 while we'd been taking locks; ++ * unlock and fall back to slow case. ++ */ ++ inode_unlock(p2->d_inode); ++ } ++ ++ mutex_lock(&c1->d_sb->s_vfs_rename_mutex); ++ /* ++ * nobody can move out of any directories on this fs. ++ */ ++ if (likely(c1->d_parent != p2)) ++ return lock_two_directories(c1->d_parent, p2); ++ ++ /* ++ * c1 got moved into p2 while we were taking locks; ++ * we need p2 locked and ->s_vfs_rename_mutex unlocked, ++ * for consistency with lock_rename(). ++ */ ++ inode_lock_nested(p2->d_inode, I_MUTEX_PARENT); ++ mutex_unlock(&c1->d_sb->s_vfs_rename_mutex); ++ return NULL; ++} ++EXPORT_SYMBOL(lock_rename_child); ++ + void unlock_rename(struct dentry *p1, struct dentry *p2) + { + inode_unlock(p1->d_inode); +@@ -3806,7 +3882,8 @@ static struct dentry *filename_create(int dfd, struct filename *name, + if (last.name[last.len] && !want_dir) + create_flags = 0; + inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT); +- dentry = __lookup_hash(&last, path->dentry, reval_flag | create_flags); ++ dentry = lookup_one_qstr_excl(&last, path->dentry, ++ reval_flag | create_flags); + if (IS_ERR(dentry)) + goto unlock; + +@@ -4168,7 +4245,7 @@ retry: + goto exit2; + + inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); +- dentry = __lookup_hash(&last, path.dentry, lookup_flags); ++ dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto exit3; +@@ -4302,7 +4379,7 @@ retry: + goto exit2; + retry_deleg: + inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT); +- dentry = __lookup_hash(&last, path.dentry, lookup_flags); ++ dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags); + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct user_namespace *mnt_userns; +@@ -4876,7 +4953,8 @@ retry: + retry_deleg: + trap = lock_rename(new_path.dentry, old_path.dentry); + +- old_dentry = __lookup_hash(&old_last, old_path.dentry, lookup_flags); ++ old_dentry = lookup_one_qstr_excl(&old_last, old_path.dentry, ++ lookup_flags); + error = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) + goto exit3; +@@ -4884,7 +4962,8 @@ retry_deleg: + error = -ENOENT; + if (d_is_negative(old_dentry)) + goto exit4; +- new_dentry = __lookup_hash(&new_last, new_path.dentry, lookup_flags | target_flags); ++ new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry, ++ lookup_flags | target_flags); + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto exit4; +diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c +index 573de0d49e172..b3b4542e31ed5 100644 +--- a/fs/nfsd/nfsctl.c ++++ b/fs/nfsd/nfsctl.c +@@ -716,8 +716,10 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred + + err = svc_addsock(nn->nfsd_serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred); + +- if (err >= 0 && +- !nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1)) ++ if (err < 0 && !nn->nfsd_serv->sv_nrthreads && !nn->keep_active) ++ nfsd_last_thread(net); ++ else if (err >= 0 && ++ !nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1)) + svc_get(nn->nfsd_serv); + + nfsd_put(net); +@@ -767,6 +769,9 @@ out_close: + svc_xprt_put(xprt); + } + out_err: ++ if (!nn->nfsd_serv->sv_nrthreads && !nn->keep_active) ++ nfsd_last_thread(net); ++ + nfsd_put(net); + return err; + } +diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h +index 09726c5b9a317..53166cce7062c 100644 +--- a/fs/nfsd/nfsd.h ++++ b/fs/nfsd/nfsd.h +@@ -97,7 +97,12 @@ int nfsd_pool_stats_open(struct inode *, struct file *); + int nfsd_pool_stats_release(struct inode *, struct file *); + void nfsd_shutdown_threads(struct net *net); + +-void nfsd_put(struct net *net); ++static inline void nfsd_put(struct net *net) ++{ ++ struct nfsd_net *nn = net_generic(net, nfsd_net_id); ++ ++ svc_put(nn->nfsd_serv); ++} + + bool i_am_nfsd(void); + +@@ -134,6 +139,7 @@ int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change); + int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change); + void nfsd_reset_versions(struct nfsd_net *nn); + int nfsd_create_serv(struct net *net); ++void nfsd_last_thread(struct net *net); + + extern int nfsd_max_blksize; + +diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c +index f6cc99af81925..350c6c72f793f 100644 +--- a/fs/nfsd/nfssvc.c ++++ b/fs/nfsd/nfssvc.c +@@ -523,9 +523,14 @@ static struct notifier_block nfsd_inet6addr_notifier = { + /* Only used under nfsd_mutex, so this atomic may be overkill: */ + static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0); + +-static void nfsd_last_thread(struct svc_serv *serv, struct net *net) ++void nfsd_last_thread(struct net *net) + { + struct nfsd_net *nn = net_generic(net, nfsd_net_id); ++ struct svc_serv *serv = nn->nfsd_serv; ++ ++ spin_lock(&nfsd_notifier_lock); ++ nn->nfsd_serv = NULL; ++ spin_unlock(&nfsd_notifier_lock); + + /* check if the notifier still has clients */ + if (atomic_dec_return(&nfsd_notifier_refcount) == 0) { +@@ -535,6 +540,8 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net) + #endif + } + ++ svc_xprt_destroy_all(serv, net); ++ + /* + * write_ports can create the server without actually starting + * any threads--if we get shut down before any threads are +@@ -625,7 +632,8 @@ void nfsd_shutdown_threads(struct net *net) + svc_get(serv); + /* Kill outstanding nfsd threads */ + svc_set_num_threads(serv, NULL, 0); +- nfsd_put(net); ++ nfsd_last_thread(net); ++ svc_put(serv); + mutex_unlock(&nfsd_mutex); + } + +@@ -655,9 +663,6 @@ int nfsd_create_serv(struct net *net) + serv->sv_maxconn = nn->max_connections; + error = svc_bind(serv, net); + if (error < 0) { +- /* NOT nfsd_put() as notifiers (see below) haven't +- * been set up yet. +- */ + svc_put(serv); + return error; + } +@@ -700,29 +705,6 @@ int nfsd_get_nrthreads(int n, int *nthreads, struct net *net) + return 0; + } + +-/* This is the callback for kref_put() below. +- * There is no code here as the first thing to be done is +- * call svc_shutdown_net(), but we cannot get the 'net' from +- * the kref. So do all the work when kref_put returns true. +- */ +-static void nfsd_noop(struct kref *ref) +-{ +-} +- +-void nfsd_put(struct net *net) +-{ +- struct nfsd_net *nn = net_generic(net, nfsd_net_id); +- +- if (kref_put(&nn->nfsd_serv->sv_refcnt, nfsd_noop)) { +- svc_xprt_destroy_all(nn->nfsd_serv, net); +- nfsd_last_thread(nn->nfsd_serv, net); +- svc_destroy(&nn->nfsd_serv->sv_refcnt); +- spin_lock(&nfsd_notifier_lock); +- nn->nfsd_serv = NULL; +- spin_unlock(&nfsd_notifier_lock); +- } +-} +- + int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) + { + int i = 0; +@@ -773,7 +755,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) + if (err) + break; + } +- nfsd_put(net); ++ svc_put(nn->nfsd_serv); + return err; + } + +@@ -788,6 +770,7 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred) + int error; + bool nfsd_up_before; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); ++ struct svc_serv *serv; + + mutex_lock(&nfsd_mutex); + dprintk("nfsd: creating service\n"); +@@ -807,22 +790,25 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred) + goto out; + + nfsd_up_before = nn->nfsd_net_up; ++ serv = nn->nfsd_serv; + + error = nfsd_startup_net(net, cred); + if (error) + goto out_put; +- error = svc_set_num_threads(nn->nfsd_serv, NULL, nrservs); ++ error = svc_set_num_threads(serv, NULL, nrservs); + if (error) + goto out_shutdown; +- error = nn->nfsd_serv->sv_nrthreads; ++ error = serv->sv_nrthreads; ++ if (error == 0) ++ nfsd_last_thread(net); + out_shutdown: + if (error < 0 && !nfsd_up_before) + nfsd_shutdown_net(net); + out_put: + /* Threads now hold service active */ + if (xchg(&nn->keep_active, 0)) +- nfsd_put(net); +- nfsd_put(net); ++ svc_put(serv); ++ svc_put(serv); + out: + mutex_unlock(&nfsd_mutex); + return error; +@@ -1138,11 +1124,12 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file) + + int nfsd_pool_stats_release(struct inode *inode, struct file *file) + { ++ struct seq_file *seq = file->private_data; ++ struct svc_serv *serv = seq->private; + int ret = seq_release(inode, file); +- struct net *net = inode->i_sb->s_fs_info; + + mutex_lock(&nfsd_mutex); +- nfsd_put(net); ++ svc_put(serv); + mutex_unlock(&nfsd_mutex); + return ret; + } +diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h +index c8a4014f9d395..07549957b3099 100644 +--- a/fs/smb/common/smb2pdu.h ++++ b/fs/smb/common/smb2pdu.h +@@ -1196,6 +1196,7 @@ struct create_posix { + #define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04) + + #define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) ++#define SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE cpu_to_le32(0x04) + + #define SMB2_LEASE_KEY_SIZE 16 + +diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig +index e1fe17747ed69..d036ab80fec35 100644 +--- a/fs/smb/server/Kconfig ++++ b/fs/smb/server/Kconfig +@@ -1,5 +1,5 @@ + config SMB_SERVER +- tristate "SMB3 server support (EXPERIMENTAL)" ++ tristate "SMB3 server support" + depends on INET + depends on MULTIUSER + depends on FILE_LOCKING +@@ -33,14 +33,16 @@ config SMB_SERVER + in ksmbd-tools, available from + https://github.com/cifsd-team/ksmbd-tools. + More detail about how to run the ksmbd kernel server is +- available via README file ++ available via the README file + (https://github.com/cifsd-team/ksmbd-tools/blob/master/README). + + ksmbd kernel server includes support for auto-negotiation, + Secure negotiate, Pre-authentication integrity, oplock/lease, + compound requests, multi-credit, packet signing, RDMA(smbdirect), + smb3 encryption, copy-offload, secure per-user session +- establishment via NTLM or NTLMv2. ++ establishment via Kerberos or NTLMv2. ++ ++if SMB_SERVER + + config SMB_SERVER_SMBDIRECT + bool "Support for SMB Direct protocol" +@@ -54,6 +56,8 @@ config SMB_SERVER_SMBDIRECT + SMB Direct allows transferring SMB packets over RDMA. If unsure, + say N. + ++endif ++ + config SMB_SERVER_CHECK_CAP_NET_ADMIN + bool "Enable check network administration capability" + depends on SMB_SERVER +diff --git a/fs/smb/server/asn1.c b/fs/smb/server/asn1.c +index c03eba0903682..4a4b2b03ff33d 100644 +--- a/fs/smb/server/asn1.c ++++ b/fs/smb/server/asn1.c +@@ -208,32 +208,29 @@ int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen, + return 0; + } + +-int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen, +- unsigned char tag, const void *value, +- size_t vlen) ++static int ksmbd_neg_token_alloc(void *context, size_t hdrlen, ++ unsigned char tag, const void *value, ++ size_t vlen) + { + struct ksmbd_conn *conn = context; + +- conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); ++ conn->mechToken = kmemdup_nul(value, vlen, GFP_KERNEL); + if (!conn->mechToken) + return -ENOMEM; + +- memcpy(conn->mechToken, value, vlen); +- conn->mechToken[vlen] = '\0'; + return 0; + } + +-int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen, ++int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) + { +- struct ksmbd_conn *conn = context; +- +- conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); +- if (!conn->mechToken) +- return -ENOMEM; ++ return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen); ++} + +- memcpy(conn->mechToken, value, vlen); +- conn->mechToken[vlen] = '\0'; +- return 0; ++int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen, ++ unsigned char tag, const void *value, ++ size_t vlen) ++{ ++ return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen); + } +diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c +index 15e5684e328c1..229a6527870d0 100644 +--- a/fs/smb/server/auth.c ++++ b/fs/smb/server/auth.c +@@ -1032,11 +1032,15 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, + { + struct scatterlist *sg; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; +- int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; ++ int i, *nr_entries, total_entries = 0, sg_idx = 0; + + if (!nvec) + return NULL; + ++ nr_entries = kcalloc(nvec, sizeof(int), GFP_KERNEL); ++ if (!nr_entries) ++ return NULL; ++ + for (i = 0; i < nvec - 1; i++) { + unsigned long kaddr = (unsigned long)iov[i + 1].iov_base; + +@@ -1054,8 +1058,10 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, + total_entries += 2; + + sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL); +- if (!sg) ++ if (!sg) { ++ kfree(nr_entries); + return NULL; ++ } + + sg_init_table(sg, total_entries); + smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len); +@@ -1089,6 +1095,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, + } + } + smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE); ++ kfree(nr_entries); + return sg; + } + +diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c +index ff97cad8d5b45..b6fa1e285c401 100644 +--- a/fs/smb/server/connection.c ++++ b/fs/smb/server/connection.c +@@ -114,10 +114,8 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work) + struct ksmbd_conn *conn = work->conn; + struct list_head *requests_queue = NULL; + +- if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { ++ if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) + requests_queue = &conn->requests; +- work->syncronous = true; +- } + + if (requests_queue) { + atomic_inc(&conn->req_running); +@@ -127,28 +125,22 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work) + } + } + +-int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) ++void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) + { + struct ksmbd_conn *conn = work->conn; +- int ret = 1; + + if (list_empty(&work->request_entry) && + list_empty(&work->async_request_entry)) +- return 0; ++ return; + +- if (!work->multiRsp) +- atomic_dec(&conn->req_running); ++ atomic_dec(&conn->req_running); + spin_lock(&conn->request_lock); +- if (!work->multiRsp) { +- list_del_init(&work->request_entry); +- if (work->syncronous == false) +- list_del_init(&work->async_request_entry); +- ret = 0; +- } ++ list_del_init(&work->request_entry); + spin_unlock(&conn->request_lock); ++ if (work->asynchronous) ++ release_async_work(work); + + wake_up_all(&conn->req_running_q); +- return ret; + } + + void ksmbd_conn_lock(struct ksmbd_conn *conn) +@@ -175,63 +167,31 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status) + + void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id) + { +- struct ksmbd_conn *bind_conn; +- + wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); +- +- down_read(&conn_list_lock); +- list_for_each_entry(bind_conn, &conn_list, conns_list) { +- if (bind_conn == conn) +- continue; +- +- if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) && +- !ksmbd_conn_releasing(bind_conn) && +- atomic_read(&bind_conn->req_running)) { +- wait_event(bind_conn->req_running_q, +- atomic_read(&bind_conn->req_running) == 0); +- } +- } +- up_read(&conn_list_lock); + } + + int ksmbd_conn_write(struct ksmbd_work *work) + { + struct ksmbd_conn *conn = work->conn; +- size_t len = 0; + int sent; +- struct kvec iov[3]; +- int iov_idx = 0; + + if (!work->response_buf) { + pr_err("NULL response header\n"); + return -EINVAL; + } + +- if (work->tr_buf) { +- iov[iov_idx] = (struct kvec) { work->tr_buf, +- sizeof(struct smb2_transform_hdr) + 4 }; +- len += iov[iov_idx++].iov_len; +- } ++ if (work->send_no_response) ++ return 0; + +- if (work->aux_payload_sz) { +- iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz }; +- len += iov[iov_idx++].iov_len; +- iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; +- len += iov[iov_idx++].iov_len; +- } else { +- if (work->tr_buf) +- iov[iov_idx].iov_len = work->resp_hdr_sz; +- else +- iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4; +- iov[iov_idx].iov_base = work->response_buf; +- len += iov[iov_idx++].iov_len; +- } ++ if (!work->iov_idx) ++ return -EINVAL; + + ksmbd_conn_lock(conn); +- sent = conn->transport->ops->writev(conn->transport, &iov[0], +- iov_idx, len, +- work->need_invalidate_rkey, +- work->remote_key); ++ sent = conn->transport->ops->writev(conn->transport, work->iov, ++ work->iov_cnt, ++ get_rfc1002_len(work->iov[0].iov_base) + 4, ++ work->need_invalidate_rkey, ++ work->remote_key); + ksmbd_conn_unlock(conn); + + if (sent < 0) { +@@ -345,7 +305,7 @@ int ksmbd_conn_handler_loop(void *p) + max_allowed_pdu_size = SMB3_MAX_MSGSIZE; + + if (pdu_size > max_allowed_pdu_size) { +- pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n", ++ pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n", + pdu_size, max_allowed_pdu_size, + READ_ONCE(conn->status)); + break; +diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h +index 335fdd714d595..3c005246a32e8 100644 +--- a/fs/smb/server/connection.h ++++ b/fs/smb/server/connection.h +@@ -159,7 +159,7 @@ int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); + void ksmbd_conn_enqueue_request(struct ksmbd_work *work); +-int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); ++void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); + void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops); + int ksmbd_conn_handler_loop(void *p); + int ksmbd_conn_transport_init(void); +diff --git a/fs/smb/server/ksmbd_netlink.h b/fs/smb/server/ksmbd_netlink.h +index ce866ff159bfe..b7521e41402e0 100644 +--- a/fs/smb/server/ksmbd_netlink.h ++++ b/fs/smb/server/ksmbd_netlink.h +@@ -74,6 +74,7 @@ struct ksmbd_heartbeat { + #define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0) + #define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) + #define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) ++#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3) + + /* + * IPC request for ksmbd server startup +@@ -351,7 +352,8 @@ enum KSMBD_TREE_CONN_STATUS { + #define KSMBD_SHARE_FLAG_STREAMS BIT(11) + #define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) + #define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) +-#define KSMBD_SHARE_FLAG_UPDATE BIT(14) ++#define KSMBD_SHARE_FLAG_UPDATE BIT(14) ++#define KSMBD_SHARE_FLAG_CROSSMNT BIT(15) + + /* + * Tree connect request flags. +diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c +index 14b9caebf7a4f..d7c676c151e20 100644 +--- a/fs/smb/server/ksmbd_work.c ++++ b/fs/smb/server/ksmbd_work.c +@@ -27,18 +27,38 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void) + INIT_LIST_HEAD(&work->async_request_entry); + INIT_LIST_HEAD(&work->fp_entry); + INIT_LIST_HEAD(&work->interim_entry); ++ INIT_LIST_HEAD(&work->aux_read_list); ++ work->iov_alloc_cnt = 4; ++ work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec), ++ GFP_KERNEL); ++ if (!work->iov) { ++ kmem_cache_free(work_cache, work); ++ work = NULL; ++ } + } + return work; + } + + void ksmbd_free_work_struct(struct ksmbd_work *work) + { ++ struct aux_read *ar, *tmp; ++ + WARN_ON(work->saved_cred != NULL); + + kvfree(work->response_buf); +- kvfree(work->aux_payload_buf); ++ ++ list_for_each_entry_safe(ar, tmp, &work->aux_read_list, entry) { ++ kvfree(ar->buf); ++ list_del(&ar->entry); ++ kfree(ar); ++ } ++ + kfree(work->tr_buf); + kvfree(work->request_buf); ++ kfree(work->iov); ++ if (!list_empty(&work->interim_entry)) ++ list_del(&work->interim_entry); ++ + if (work->async_id) + ksmbd_release_id(&work->conn->async_ida, work->async_id); + kmem_cache_free(work_cache, work); +@@ -77,3 +97,81 @@ bool ksmbd_queue_work(struct ksmbd_work *work) + { + return queue_work(ksmbd_wq, &work->work); + } ++ ++static inline void __ksmbd_iov_pin(struct ksmbd_work *work, void *ib, ++ unsigned int ib_len) ++{ ++ work->iov[++work->iov_idx].iov_base = ib; ++ work->iov[work->iov_idx].iov_len = ib_len; ++ work->iov_cnt++; ++} ++ ++static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, ++ void *aux_buf, unsigned int aux_size) ++{ ++ struct aux_read *ar = NULL; ++ int need_iov_cnt = 1; ++ ++ if (aux_size) { ++ need_iov_cnt++; ++ ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL); ++ if (!ar) ++ return -ENOMEM; ++ } ++ ++ if (work->iov_alloc_cnt < work->iov_cnt + need_iov_cnt) { ++ struct kvec *new; ++ ++ work->iov_alloc_cnt += 4; ++ new = krealloc(work->iov, ++ sizeof(struct kvec) * work->iov_alloc_cnt, ++ GFP_KERNEL | __GFP_ZERO); ++ if (!new) { ++ kfree(ar); ++ work->iov_alloc_cnt -= 4; ++ return -ENOMEM; ++ } ++ work->iov = new; ++ } ++ ++ /* Plus rfc_length size on first iov */ ++ if (!work->iov_idx) { ++ work->iov[work->iov_idx].iov_base = work->response_buf; ++ *(__be32 *)work->iov[0].iov_base = 0; ++ work->iov[work->iov_idx].iov_len = 4; ++ work->iov_cnt++; ++ } ++ ++ __ksmbd_iov_pin(work, ib, len); ++ inc_rfc1001_len(work->iov[0].iov_base, len); ++ ++ if (aux_size) { ++ __ksmbd_iov_pin(work, aux_buf, aux_size); ++ inc_rfc1001_len(work->iov[0].iov_base, aux_size); ++ ++ ar->buf = aux_buf; ++ list_add(&ar->entry, &work->aux_read_list); ++ } ++ ++ return 0; ++} ++ ++int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len) ++{ ++ return __ksmbd_iov_pin_rsp(work, ib, len, NULL, 0); ++} ++ ++int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len, ++ void *aux_buf, unsigned int aux_size) ++{ ++ return __ksmbd_iov_pin_rsp(work, ib, len, aux_buf, aux_size); ++} ++ ++int allocate_interim_rsp_buf(struct ksmbd_work *work) ++{ ++ work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); ++ if (!work->response_buf) ++ return -ENOMEM; ++ work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; ++ return 0; ++} +diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h +index 5ece58e40c979..8ca2c813246e6 100644 +--- a/fs/smb/server/ksmbd_work.h ++++ b/fs/smb/server/ksmbd_work.h +@@ -19,6 +19,11 @@ enum { + KSMBD_WORK_CLOSED, + }; + ++struct aux_read { ++ void *buf; ++ struct list_head entry; ++}; ++ + /* one of these for every pending CIFS request at the connection */ + struct ksmbd_work { + /* Server corresponding to this mid */ +@@ -31,13 +36,19 @@ struct ksmbd_work { + /* Response buffer */ + void *response_buf; + +- /* Read data buffer */ +- void *aux_payload_buf; ++ struct list_head aux_read_list; ++ ++ struct kvec *iov; ++ int iov_alloc_cnt; ++ int iov_cnt; ++ int iov_idx; + + /* Next cmd hdr in compound req buf*/ + int next_smb2_rcv_hdr_off; + /* Next cmd hdr in compound rsp buf*/ + int next_smb2_rsp_hdr_off; ++ /* Current cmd hdr in compound rsp buf*/ ++ int curr_smb2_rsp_hdr_off; + + /* + * Current Local FID assigned compound response if SMB2 CREATE +@@ -53,22 +64,17 @@ struct ksmbd_work { + unsigned int credits_granted; + + /* response smb header size */ +- unsigned int resp_hdr_sz; + unsigned int response_sz; +- /* Read data count */ +- unsigned int aux_payload_sz; + + void *tr_buf; + + unsigned char state; +- /* Multiple responses for one request e.g. SMB ECHO */ +- bool multiRsp:1; + /* No response for cancelled request */ + bool send_no_response:1; + /* Request is encrypted */ + bool encrypted:1; + /* Is this SYNC or ASYNC ksmbd_work */ +- bool syncronous:1; ++ bool asynchronous:1; + bool need_invalidate_rkey:1; + + unsigned int remote_key; +@@ -95,6 +101,15 @@ static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) + return work->response_buf + work->next_smb2_rsp_hdr_off + 4; + } + ++/** ++ * ksmbd_resp_buf_curr - Get current buffer on compound response. ++ * @work: smb work containing response buffer ++ */ ++static inline void *ksmbd_resp_buf_curr(struct ksmbd_work *work) ++{ ++ return work->response_buf + work->curr_smb2_rsp_hdr_off + 4; ++} ++ + /** + * ksmbd_req_buf_next - Get next buffer on compound request. + * @work: smb work containing response buffer +@@ -113,5 +128,8 @@ int ksmbd_work_pool_init(void); + int ksmbd_workqueue_init(void); + void ksmbd_workqueue_destroy(void); + bool ksmbd_queue_work(struct ksmbd_work *work); +- ++int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len, ++ void *aux_buf, unsigned int aux_size); ++int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len); ++int allocate_interim_rsp_buf(struct ksmbd_work *work); + #endif /* __KSMBD_WORK_H__ */ +diff --git a/fs/smb/server/mgmt/share_config.h b/fs/smb/server/mgmt/share_config.h +index 3fd3382939421..5f591751b9236 100644 +--- a/fs/smb/server/mgmt/share_config.h ++++ b/fs/smb/server/mgmt/share_config.h +@@ -34,29 +34,22 @@ struct ksmbd_share_config { + #define KSMBD_SHARE_INVALID_UID ((__u16)-1) + #define KSMBD_SHARE_INVALID_GID ((__u16)-1) + +-static inline int share_config_create_mode(struct ksmbd_share_config *share, +- umode_t posix_mode) ++static inline umode_t ++share_config_create_mode(struct ksmbd_share_config *share, ++ umode_t posix_mode) + { +- if (!share->force_create_mode) { +- if (!posix_mode) +- return share->create_mask; +- else +- return posix_mode & share->create_mask; +- } +- return share->force_create_mode & share->create_mask; ++ umode_t mode = (posix_mode ?: (umode_t)-1) & share->create_mask; ++ ++ return mode | share->force_create_mode; + } + +-static inline int share_config_directory_mode(struct ksmbd_share_config *share, +- umode_t posix_mode) ++static inline umode_t ++share_config_directory_mode(struct ksmbd_share_config *share, ++ umode_t posix_mode) + { +- if (!share->force_directory_mode) { +- if (!posix_mode) +- return share->directory_mask; +- else +- return posix_mode & share->directory_mask; +- } ++ umode_t mode = (posix_mode ?: (umode_t)-1) & share->directory_mask; + +- return share->force_directory_mode & share->directory_mask; ++ return mode | share->force_directory_mode; + } + + static inline int test_share_config_flag(struct ksmbd_share_config *share, +diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c +index f07a05f376513..d2c81a8a11dda 100644 +--- a/fs/smb/server/mgmt/tree_connect.c ++++ b/fs/smb/server/mgmt/tree_connect.c +@@ -73,7 +73,10 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + + tree_conn->user = sess->user; + tree_conn->share_conf = sc; ++ tree_conn->t_state = TREE_NEW; + status.tree_conn = tree_conn; ++ atomic_set(&tree_conn->refcount, 1); ++ init_waitqueue_head(&tree_conn->refcount_q); + + ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, + GFP_KERNEL)); +@@ -93,14 +96,33 @@ out_error: + return status; + } + ++void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon) ++{ ++ /* ++ * Checking waitqueue to releasing tree connect on ++ * tree disconnect. waitqueue_active is safe because it ++ * uses atomic operation for condition. ++ */ ++ if (!atomic_dec_return(&tcon->refcount) && ++ waitqueue_active(&tcon->refcount_q)) ++ wake_up(&tcon->refcount_q); ++} ++ + int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn) + { + int ret; + ++ write_lock(&sess->tree_conns_lock); ++ xa_erase(&sess->tree_conns, tree_conn->id); ++ write_unlock(&sess->tree_conns_lock); ++ ++ if (!atomic_dec_and_test(&tree_conn->refcount)) ++ wait_event(tree_conn->refcount_q, ++ atomic_read(&tree_conn->refcount) == 0); ++ + ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); + ksmbd_release_tree_conn_id(sess, tree_conn->id); +- xa_erase(&sess->tree_conns, tree_conn->id); + ksmbd_share_config_put(tree_conn->share_conf); + kfree(tree_conn); + return ret; +@@ -111,26 +133,19 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + { + struct ksmbd_tree_connect *tcon; + ++ read_lock(&sess->tree_conns_lock); + tcon = xa_load(&sess->tree_conns, id); + if (tcon) { +- if (test_bit(TREE_CONN_EXPIRE, &tcon->status)) ++ if (tcon->t_state != TREE_CONNECTED) ++ tcon = NULL; ++ else if (!atomic_inc_not_zero(&tcon->refcount)) + tcon = NULL; + } ++ read_unlock(&sess->tree_conns_lock); + + return tcon; + } + +-struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, +- unsigned int id) +-{ +- struct ksmbd_tree_connect *tc; +- +- tc = ksmbd_tree_conn_lookup(sess, id); +- if (tc) +- return tc->share_conf; +- return NULL; +-} +- + int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) + { + int ret = 0; +@@ -140,8 +155,18 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) + if (!sess) + return -EINVAL; + +- xa_for_each(&sess->tree_conns, id, tc) ++ xa_for_each(&sess->tree_conns, id, tc) { ++ write_lock(&sess->tree_conns_lock); ++ if (tc->t_state == TREE_DISCONNECTED) { ++ write_unlock(&sess->tree_conns_lock); ++ ret = -ENOENT; ++ continue; ++ } ++ tc->t_state = TREE_DISCONNECTED; ++ write_unlock(&sess->tree_conns_lock); ++ + ret |= ksmbd_tree_conn_disconnect(sess, tc); ++ } + xa_destroy(&sess->tree_conns); + return ret; + } +diff --git a/fs/smb/server/mgmt/tree_connect.h b/fs/smb/server/mgmt/tree_connect.h +index 700df36cf3e30..6377a70b811c8 100644 +--- a/fs/smb/server/mgmt/tree_connect.h ++++ b/fs/smb/server/mgmt/tree_connect.h +@@ -14,7 +14,11 @@ struct ksmbd_share_config; + struct ksmbd_user; + struct ksmbd_conn; + +-#define TREE_CONN_EXPIRE 1 ++enum { ++ TREE_NEW = 0, ++ TREE_CONNECTED, ++ TREE_DISCONNECTED ++}; + + struct ksmbd_tree_connect { + int id; +@@ -27,7 +31,9 @@ struct ksmbd_tree_connect { + + int maximal_access; + bool posix_extensions; +- unsigned long status; ++ atomic_t refcount; ++ wait_queue_head_t refcount_q; ++ unsigned int t_state; + }; + + struct ksmbd_tree_conn_status { +@@ -46,6 +52,7 @@ struct ksmbd_session; + struct ksmbd_tree_conn_status + ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + const char *share_name); ++void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon); + + int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn); +@@ -53,9 +60,6 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + unsigned int id); + +-struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, +- unsigned int id); +- + int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess); + + #endif /* __TREE_CONNECT_MANAGEMENT_H__ */ +diff --git a/fs/smb/server/mgmt/user_config.h b/fs/smb/server/mgmt/user_config.h +index 6a44109617f14..e068a19fd9049 100644 +--- a/fs/smb/server/mgmt/user_config.h ++++ b/fs/smb/server/mgmt/user_config.h +@@ -18,7 +18,6 @@ struct ksmbd_user { + + size_t passkey_sz; + char *passkey; +- unsigned int failed_login_count; + }; + + static inline bool user_guest(struct ksmbd_user *user) +diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c +index cf6621e21ba36..15f68ee050894 100644 +--- a/fs/smb/server/mgmt/user_session.c ++++ b/fs/smb/server/mgmt/user_session.c +@@ -25,7 +25,6 @@ static DECLARE_RWSEM(sessions_table_lock); + struct ksmbd_session_rpc { + int id; + unsigned int method; +- struct list_head list; + }; + + static void free_channel_list(struct ksmbd_session *sess) +@@ -58,15 +57,14 @@ static void __session_rpc_close(struct ksmbd_session *sess, + static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess) + { + struct ksmbd_session_rpc *entry; ++ long index; + +- while (!list_empty(&sess->rpc_handle_list)) { +- entry = list_entry(sess->rpc_handle_list.next, +- struct ksmbd_session_rpc, +- list); +- +- list_del(&entry->list); ++ xa_for_each(&sess->rpc_handle_list, index, entry) { ++ xa_erase(&sess->rpc_handle_list, index); + __session_rpc_close(sess, entry); + } ++ ++ xa_destroy(&sess->rpc_handle_list); + } + + static int __rpc_method(char *rpc_name) +@@ -102,13 +100,13 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) + + entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL); + if (!entry) +- return -EINVAL; ++ return -ENOMEM; + +- list_add(&entry->list, &sess->rpc_handle_list); + entry->method = method; + entry->id = ksmbd_ipc_id_alloc(); + if (entry->id < 0) + goto free_entry; ++ xa_store(&sess->rpc_handle_list, entry->id, entry, GFP_KERNEL); + + resp = ksmbd_rpc_open(sess, entry->id); + if (!resp) +@@ -117,9 +115,9 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) + kvfree(resp); + return entry->id; + free_id: ++ xa_erase(&sess->rpc_handle_list, entry->id); + ksmbd_rpc_id_free(entry->id); + free_entry: +- list_del(&entry->list); + kfree(entry); + return -EINVAL; + } +@@ -128,24 +126,17 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id) + { + struct ksmbd_session_rpc *entry; + +- list_for_each_entry(entry, &sess->rpc_handle_list, list) { +- if (entry->id == id) { +- list_del(&entry->list); +- __session_rpc_close(sess, entry); +- break; +- } +- } ++ entry = xa_erase(&sess->rpc_handle_list, id); ++ if (entry) ++ __session_rpc_close(sess, entry); + } + + int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) + { + struct ksmbd_session_rpc *entry; + +- list_for_each_entry(entry, &sess->rpc_handle_list, list) { +- if (entry->id == id) +- return entry->method; +- } +- return 0; ++ entry = xa_load(&sess->rpc_handle_list, id); ++ return entry ? entry->method : 0; + } + + void ksmbd_session_destroy(struct ksmbd_session *sess) +@@ -362,8 +353,9 @@ static struct ksmbd_session *__session_create(int protocol) + set_session_flag(sess, protocol); + xa_init(&sess->tree_conns); + xa_init(&sess->ksmbd_chann_list); +- INIT_LIST_HEAD(&sess->rpc_handle_list); ++ xa_init(&sess->rpc_handle_list); + sess->sequence_number = 1; ++ rwlock_init(&sess->tree_conns_lock); + + ret = __init_smb2_session(sess); + if (ret) +diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h +index 51f38e5b61abb..63cb08fffde84 100644 +--- a/fs/smb/server/mgmt/user_session.h ++++ b/fs/smb/server/mgmt/user_session.h +@@ -52,7 +52,7 @@ struct ksmbd_session { + struct xarray ksmbd_chann_list; + struct xarray tree_conns; + struct ida tree_conn_ida; +- struct list_head rpc_handle_list; ++ struct xarray rpc_handle_list; + + __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; +@@ -60,6 +60,7 @@ struct ksmbd_session { + + struct ksmbd_file_table file_table; + unsigned long last_active; ++ rwlock_t tree_conns_lock; + }; + + static inline int test_session_flag(struct ksmbd_session *sess, int bit) +diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c +index c81aee9ce7ec4..af0f6914eca45 100644 +--- a/fs/smb/server/oplock.c ++++ b/fs/smb/server/oplock.c +@@ -102,9 +102,10 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) + lease->new_state = 0; + lease->flags = lctx->flags; + lease->duration = lctx->duration; ++ lease->is_dir = lctx->is_dir; + memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); + lease->version = lctx->version; +- lease->epoch = 0; ++ lease->epoch = le16_to_cpu(lctx->epoch); + INIT_LIST_HEAD(&opinfo->lease_entry); + opinfo->o_lease = lease; + +@@ -395,8 +396,8 @@ void close_id_del_oplock(struct ksmbd_file *fp) + { + struct oplock_info *opinfo; + +- if (S_ISDIR(file_inode(fp->filp)->i_mode)) +- return; ++ if (fp->reserve_lease_break) ++ smb_lazy_parent_lease_break_close(fp); + + opinfo = opinfo_get(fp); + if (!opinfo) +@@ -543,12 +544,13 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, + /* upgrading lease */ + if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) == 1) { +- if (lease->state == +- (lctx->req_state & lease->state)) { ++ if (lease->state != SMB2_LEASE_NONE_LE && ++ lease->state == (lctx->req_state & lease->state)) { + lease->state |= lctx->req_state; + if (lctx->req_state & + SMB2_LEASE_WRITE_CACHING_LE) + lease_read_to_write(opinfo); ++ + } + } else if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) > 1) { +@@ -616,15 +618,6 @@ static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) + return 0; + } + +-static inline int allocate_oplock_break_buf(struct ksmbd_work *work) +-{ +- work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); +- if (!work->response_buf) +- return -ENOMEM; +- work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; +- return 0; +-} +- + /** + * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn + * to client +@@ -639,7 +632,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) + { + struct smb2_oplock_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); +- struct ksmbd_conn *conn = work->conn; + struct oplock_break_info *br_info = work->request_buf; + struct smb2_hdr *rsp_hdr; + struct ksmbd_file *fp; +@@ -648,7 +640,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) + if (!fp) + goto out; + +- if (allocate_oplock_break_buf(work)) { ++ if (allocate_interim_rsp_buf(work)) { + pr_err("smb2_allocate_rsp_buf failed! "); + ksmbd_fd_put(work, fp); + goto out; +@@ -656,8 +648,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); +- *(__be32 *)work->response_buf = +- cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); +@@ -684,13 +674,15 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) + rsp->PersistentFid = fp->persistent_id; + rsp->VolatileFid = fp->volatile_id; + +- inc_rfc1001_len(work->response_buf, 24); ++ ksmbd_fd_put(work, fp); ++ if (ksmbd_iov_pin_rsp(work, (void *)rsp, ++ sizeof(struct smb2_oplock_break))) ++ goto out; + + ksmbd_debug(OPLOCK, + "sending oplock break v_id %llu p_id = %llu lock level = %d\n", + rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel); + +- ksmbd_fd_put(work, fp); + ksmbd_conn_write(work); + + out: +@@ -751,18 +743,15 @@ static void __smb2_lease_break_noti(struct work_struct *wk) + struct smb2_lease_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct lease_break_info *br_info = work->request_buf; +- struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *rsp_hdr; + +- if (allocate_oplock_break_buf(work)) { ++ if (allocate_interim_rsp_buf(work)) { + ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! "); + goto out; + } + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); +- *(__be32 *)work->response_buf = +- cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); +@@ -791,7 +780,9 @@ static void __smb2_lease_break_noti(struct work_struct *wk) + rsp->AccessMaskHint = 0; + rsp->ShareMaskHint = 0; + +- inc_rfc1001_len(work->response_buf, 44); ++ if (ksmbd_iov_pin_rsp(work, (void *)rsp, ++ sizeof(struct smb2_lease_break))) ++ goto out; + + ksmbd_conn_write(work); + +@@ -844,7 +835,8 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) + interim_entry); + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); +- list_del(&in_work->interim_entry); ++ list_del_init(&in_work->interim_entry); ++ release_async_work(in_work); + } + INIT_WORK(&work->work, __smb2_lease_break_noti); + ksmbd_queue_work(work); +@@ -910,7 +902,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + } else { +- if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) ++ if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE && ++ !lease->is_dir) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + else +@@ -1042,6 +1035,7 @@ static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) + SMB2_LEASE_KEY_SIZE); + lease2->duration = lease1->duration; + lease2->flags = lease1->flags; ++ lease2->epoch = lease1->epoch++; + } + + static int add_lease_global_list(struct oplock_info *opinfo) +@@ -1091,6 +1085,89 @@ static void set_oplock_level(struct oplock_info *opinfo, int level, + } + } + ++void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, ++ struct lease_ctx_info *lctx) ++{ ++ struct oplock_info *opinfo; ++ struct ksmbd_inode *p_ci = NULL; ++ ++ if (lctx->version != 2) ++ return; ++ ++ p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent); ++ if (!p_ci) ++ return; ++ ++ read_lock(&p_ci->m_lock); ++ list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) { ++ if (!opinfo->is_lease) ++ continue; ++ ++ if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE && ++ (!(lctx->flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) || ++ !compare_guid_key(opinfo, fp->conn->ClientGUID, ++ lctx->parent_lease_key))) { ++ if (!atomic_inc_not_zero(&opinfo->refcount)) ++ continue; ++ ++ atomic_inc(&opinfo->conn->r_count); ++ if (ksmbd_conn_releasing(opinfo->conn)) { ++ atomic_dec(&opinfo->conn->r_count); ++ continue; ++ } ++ ++ read_unlock(&p_ci->m_lock); ++ oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE); ++ opinfo_conn_put(opinfo); ++ read_lock(&p_ci->m_lock); ++ } ++ } ++ read_unlock(&p_ci->m_lock); ++ ++ ksmbd_inode_put(p_ci); ++} ++ ++void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) ++{ ++ struct oplock_info *opinfo; ++ struct ksmbd_inode *p_ci = NULL; ++ ++ rcu_read_lock(); ++ opinfo = rcu_dereference(fp->f_opinfo); ++ rcu_read_unlock(); ++ ++ if (!opinfo->is_lease || opinfo->o_lease->version != 2) ++ return; ++ ++ p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent); ++ if (!p_ci) ++ return; ++ ++ read_lock(&p_ci->m_lock); ++ list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) { ++ if (!opinfo->is_lease) ++ continue; ++ ++ if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) { ++ if (!atomic_inc_not_zero(&opinfo->refcount)) ++ continue; ++ ++ atomic_inc(&opinfo->conn->r_count); ++ if (ksmbd_conn_releasing(opinfo->conn)) { ++ atomic_dec(&opinfo->conn->r_count); ++ continue; ++ } ++ read_unlock(&p_ci->m_lock); ++ oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE); ++ opinfo_conn_put(opinfo); ++ read_lock(&p_ci->m_lock); ++ } ++ } ++ read_unlock(&p_ci->m_lock); ++ ++ ksmbd_inode_put(p_ci); ++} ++ + /** + * smb_grant_oplock() - handle oplock/lease request on file open + * @work: smb work +@@ -1114,10 +1191,6 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, + bool prev_op_has_lease; + __le32 prev_op_state = 0; + +- /* not support directory lease */ +- if (S_ISDIR(file_inode(fp->filp)->i_mode)) +- return 0; +- + opinfo = alloc_opinfo(work, pid, tid); + if (!opinfo) + return -ENOMEM; +@@ -1374,6 +1447,7 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) + memcpy(buf->lcontext.LeaseKey, lease->lease_key, + SMB2_LEASE_KEY_SIZE); + buf->lcontext.LeaseFlags = lease->flags; ++ buf->lcontext.Epoch = cpu_to_le16(++lease->epoch); + buf->lcontext.LeaseState = lease->state; + memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, + SMB2_LEASE_KEY_SIZE); +@@ -1410,10 +1484,11 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) + /** + * parse_lease_state() - parse lease context containted in file open request + * @open_req: buffer containing smb2 file open(create) request ++ * @is_dir: whether leasing file is directory + * + * Return: oplock state, -ENOENT if create lease context not found + */ +-struct lease_ctx_info *parse_lease_state(void *open_req) ++struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir) + { + struct create_context *cc; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; +@@ -1431,8 +1506,14 @@ struct lease_ctx_info *parse_lease_state(void *open_req) + struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; + + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); +- lreq->req_state = lc->lcontext.LeaseState; ++ if (is_dir) { ++ lreq->req_state = lc->lcontext.LeaseState & ++ ~SMB2_LEASE_WRITE_CACHING_LE; ++ lreq->is_dir = true; ++ } else ++ lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; ++ lreq->epoch = lc->lcontext.Epoch; + lreq->duration = lc->lcontext.LeaseDuration; + memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, + SMB2_LEASE_KEY_SIZE); +diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h +index 4b0fe6da76940..5b93ea9196c01 100644 +--- a/fs/smb/server/oplock.h ++++ b/fs/smb/server/oplock.h +@@ -34,7 +34,9 @@ struct lease_ctx_info { + __le32 flags; + __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; ++ __le16 epoch; + int version; ++ bool is_dir; + }; + + struct lease_table { +@@ -53,6 +55,7 @@ struct lease { + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; + unsigned short epoch; ++ bool is_dir; + struct lease_table *l_lb; + }; + +@@ -108,7 +111,7 @@ void opinfo_put(struct oplock_info *opinfo); + + /* Lease related functions */ + void create_lease_buf(u8 *rbuf, struct lease *lease); +-struct lease_ctx_info *parse_lease_state(void *open_req); ++struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir); + __u8 smb2_map_lease_to_oplock(__le32 lease_state); + int lease_read_to_write(struct oplock_info *opinfo); + +@@ -124,4 +127,7 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + struct lease_ctx_info *lctx); + void destroy_lease_table(struct ksmbd_conn *conn); ++void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, ++ struct lease_ctx_info *lctx); ++void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp); + #endif /* __KSMBD_OPLOCK_H */ +diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c +index 9804cabe72a84..11b201e6ee44b 100644 +--- a/fs/smb/server/server.c ++++ b/fs/smb/server/server.c +@@ -115,8 +115,10 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, + if (check_conn_state(work)) + return SERVER_HANDLER_CONTINUE; + +- if (ksmbd_verify_smb_message(work)) ++ if (ksmbd_verify_smb_message(work)) { ++ conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return SERVER_HANDLER_ABORT; ++ } + + command = conn->ops->get_cmd_val(work); + *cmd = command; +@@ -163,6 +165,7 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, + { + u16 command = 0; + int rc; ++ bool is_chained = false; + + if (conn->ops->allocate_rsp_buf(work)) + return; +@@ -229,16 +232,17 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, + } + } + ++ is_chained = is_chained_smb2_message(work); ++ + if (work->sess && + (work->sess->sign || smb3_11_final_sess_setup_resp(work) || + conn->ops->is_sign_req(work, command))) + conn->ops->set_sign_rsp(work); +- } while (is_chained_smb2_message(work)); +- +- if (work->send_no_response) +- return; ++ } while (is_chained == true); + + send: ++ if (work->tcon) ++ ksmbd_tree_connect_put(work->tcon); + smb3_preauth_hash_rsp(work); + if (work->sess && work->sess->enc && work->encrypted && + conn->ops->encrypt_resp) { +@@ -442,11 +446,9 @@ static ssize_t stats_show(struct class *class, struct class_attribute *attr, + "reset", + "shutdown" + }; +- +- ssize_t sz = scnprintf(buf, PAGE_SIZE, "%d %s %d %lu\n", stats_version, +- state[server_conf.state], server_conf.tcp_port, +- server_conf.ipc_last_active / HZ); +- return sz; ++ return sysfs_emit(buf, "%d %s %d %lu\n", stats_version, ++ state[server_conf.state], server_conf.tcp_port, ++ server_conf.ipc_last_active / HZ); + } + + static ssize_t kill_server_store(struct class *class, +@@ -478,19 +480,13 @@ static ssize_t debug_show(struct class *class, struct class_attribute *attr, + + for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { + if ((ksmbd_debug_types >> i) & 1) { +- pos = scnprintf(buf + sz, +- PAGE_SIZE - sz, +- "[%s] ", +- debug_type_strings[i]); ++ pos = sysfs_emit_at(buf, sz, "[%s] ", debug_type_strings[i]); + } else { +- pos = scnprintf(buf + sz, +- PAGE_SIZE - sz, +- "%s ", +- debug_type_strings[i]); ++ pos = sysfs_emit_at(buf, sz, "%s ", debug_type_strings[i]); + } + sz += pos; + } +- sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); ++ sz += sysfs_emit_at(buf, sz, "\n"); + return sz; + } + +@@ -599,8 +595,6 @@ static int __init ksmbd_server_init(void) + if (ret) + goto err_crypto_destroy; + +- pr_warn_once("The ksmbd server is experimental\n"); +- + return 0; + + err_crypto_destroy: +diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c +index e881df1d10cbd..03dded29a9804 100644 +--- a/fs/smb/server/smb2misc.c ++++ b/fs/smb/server/smb2misc.c +@@ -106,16 +106,25 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + break; + case SMB2_CREATE: + { ++ unsigned short int name_off = ++ le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset); ++ unsigned short int name_len = ++ le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); ++ + if (((struct smb2_create_req *)hdr)->CreateContextsLength) { + *off = le32_to_cpu(((struct smb2_create_req *) + hdr)->CreateContextsOffset); + *len = le32_to_cpu(((struct smb2_create_req *) + hdr)->CreateContextsLength); +- break; ++ if (!name_len) ++ break; ++ ++ if (name_off + name_len < (u64)*off + *len) ++ break; + } + +- *off = le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset); +- *len = le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); ++ *off = name_off; ++ *len = name_len; + break; + } + case SMB2_QUERY_INFO: +@@ -440,10 +449,8 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) + + validate_credit: + if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && +- smb2_validate_credit_charge(work->conn, hdr)) { +- work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); ++ smb2_validate_credit_charge(work->conn, hdr)) + return 1; +- } + + return 0; + } +diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c +index ab23da2120b94..535402629655e 100644 +--- a/fs/smb/server/smb2ops.c ++++ b/fs/smb/server/smb2ops.c +@@ -221,7 +221,8 @@ void init_smb3_0_server(struct ksmbd_conn *conn) + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) +- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; ++ conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING | ++ SMB2_GLOBAL_CAP_DIRECTORY_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) +@@ -245,10 +246,12 @@ void init_smb3_02_server(struct ksmbd_conn *conn) + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) +- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; ++ conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING | ++ SMB2_GLOBAL_CAP_DIRECTORY_LEASING; + +- if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && +- conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION) ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || ++ (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && ++ conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) +@@ -269,7 +272,13 @@ int init_smb3_11_server(struct ksmbd_conn *conn) + conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) +- conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; ++ conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING | ++ SMB2_GLOBAL_CAP_DIRECTORY_LEASING; ++ ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || ++ (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && ++ conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)) ++ conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 1598ad6155fef..ea48dd06d4da3 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -144,12 +144,18 @@ void smb2_set_err_rsp(struct ksmbd_work *work) + err_rsp = smb2_get_msg(work->response_buf); + + if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { ++ int err; ++ + err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; + err_rsp->ErrorContextCount = 0; + err_rsp->Reserved = 0; + err_rsp->ByteCount = 0; + err_rsp->ErrorData[0] = 0; +- inc_rfc1001_len(work->response_buf, SMB2_ERROR_STRUCTURE_SIZE2); ++ err = ksmbd_iov_pin_rsp(work, (void *)err_rsp, ++ __SMB2_HEADER_STRUCTURE_SIZE + ++ SMB2_ERROR_STRUCTURE_SIZE2); ++ if (err) ++ work->send_no_response = 1; + } + } + +@@ -224,11 +230,12 @@ void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) + { + struct smb2_hdr *rsp_hdr; + +- if (work->next_smb2_rcv_hdr_off) +- rsp_hdr = ksmbd_resp_buf_next(work); +- else +- rsp_hdr = smb2_get_msg(work->response_buf); ++ rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr->Status = err; ++ ++ work->iov_idx = 0; ++ work->iov_cnt = 0; ++ work->next_smb2_rcv_hdr_off = 0; + smb2_set_err_rsp(work); + } + +@@ -244,9 +251,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) + struct smb2_hdr *rsp_hdr; + struct smb2_negotiate_rsp *rsp; + struct ksmbd_conn *conn = work->conn; +- +- *(__be32 *)work->response_buf = +- cpu_to_be32(conn->vals->header_size); ++ int err; + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); +@@ -285,13 +290,14 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + + le16_to_cpu(rsp->SecurityBufferOffset)); +- inc_rfc1001_len(work->response_buf, +- sizeof(struct smb2_negotiate_rsp) - +- sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + +- AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; ++ err = ksmbd_iov_pin_rsp(work, rsp, ++ sizeof(struct smb2_negotiate_rsp) - ++ sizeof(rsp->Buffer) + AUTH_GSS_LENGTH); ++ if (err) ++ return err; + conn->use_spnego = true; + + ksmbd_conn_set_need_negotiate(conn); +@@ -390,11 +396,12 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) + next_hdr_offset = le32_to_cpu(req->NextCommand); + + new_len = ALIGN(len, 8); +- inc_rfc1001_len(work->response_buf, +- sizeof(struct smb2_hdr) + new_len - len); ++ work->iov[work->iov_idx].iov_len += (new_len - len); ++ inc_rfc1001_len(work->response_buf, new_len - len); + rsp->NextCommand = cpu_to_le32(new_len); + + work->next_smb2_rcv_hdr_off += next_hdr_offset; ++ work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off; + work->next_smb2_rsp_hdr_off += new_len; + ksmbd_debug(SMB, + "Compound req new_len = %d rcv off = %d rsp off = %d\n", +@@ -470,10 +477,10 @@ bool is_chained_smb2_message(struct ksmbd_work *work) + len = len - get_rfc1002_len(work->response_buf); + if (len) { + ksmbd_debug(SMB, "padding len %u\n", len); ++ work->iov[work->iov_idx].iov_len += len; + inc_rfc1001_len(work->response_buf, len); +- if (work->aux_payload_sz) +- work->aux_payload_sz += len; + } ++ work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off; + } + return false; + } +@@ -488,11 +495,8 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) + { + struct smb2_hdr *rsp_hdr = smb2_get_msg(work->response_buf); + struct smb2_hdr *rcv_hdr = smb2_get_msg(work->request_buf); +- struct ksmbd_conn *conn = work->conn; + + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); +- *(__be32 *)work->response_buf = +- cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->Command = rcv_hdr->Command; +@@ -508,12 +512,6 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); + +- work->syncronous = true; +- if (work->async_id) { +- ksmbd_release_id(&conn->async_ida, work->async_id); +- work->async_id = 0; +- } +- + return 0; + } + +@@ -549,7 +547,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) + if (le32_to_cpu(hdr->NextCommand) > 0) + sz = large_sz; + +- work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); ++ work->response_buf = kvzalloc(sz, GFP_KERNEL); + if (!work->response_buf) + return -ENOMEM; + +@@ -659,21 +657,16 @@ smb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls) + + int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) + { +- struct smb2_hdr *rsp_hdr; + struct ksmbd_conn *conn = work->conn; + int id; + +- rsp_hdr = smb2_get_msg(work->response_buf); +- rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; +- + id = ksmbd_acquire_async_msg_id(&conn->async_ida); + if (id < 0) { + pr_err("Failed to alloc async message id\n"); + return id; + } +- work->syncronous = false; ++ work->asynchronous = true; + work->async_id = id; +- rsp_hdr->Id.AsyncId = cpu_to_le64(id); + + ksmbd_debug(SMB, + "Send interim Response to inform async request id : %d\n", +@@ -691,18 +684,47 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) + return 0; + } + ++void release_async_work(struct ksmbd_work *work) ++{ ++ struct ksmbd_conn *conn = work->conn; ++ ++ spin_lock(&conn->request_lock); ++ list_del_init(&work->async_request_entry); ++ spin_unlock(&conn->request_lock); ++ ++ work->asynchronous = 0; ++ work->cancel_fn = NULL; ++ kfree(work->cancel_argv); ++ work->cancel_argv = NULL; ++ if (work->async_id) { ++ ksmbd_release_id(&conn->async_ida, work->async_id); ++ work->async_id = 0; ++ } ++} ++ + void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) + { + struct smb2_hdr *rsp_hdr; ++ struct ksmbd_work *in_work = ksmbd_alloc_work_struct(); + +- rsp_hdr = smb2_get_msg(work->response_buf); +- smb2_set_err_rsp(work); ++ if (allocate_interim_rsp_buf(in_work)) { ++ pr_err("smb_allocate_rsp_buf failed!\n"); ++ ksmbd_free_work_struct(in_work); ++ return; ++ } ++ ++ in_work->conn = work->conn; ++ memcpy(smb2_get_msg(in_work->response_buf), ksmbd_resp_buf_next(work), ++ __SMB2_HEADER_STRUCTURE_SIZE); ++ ++ rsp_hdr = smb2_get_msg(in_work->response_buf); ++ rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; ++ rsp_hdr->Id.AsyncId = cpu_to_le64(work->async_id); ++ smb2_set_err_rsp(in_work); + rsp_hdr->Status = status; + +- work->multiRsp = 1; +- ksmbd_conn_write(work); +- rsp_hdr->Status = 0; +- work->multiRsp = 0; ++ ksmbd_conn_write(in_work); ++ ksmbd_free_work_struct(in_work); + } + + static __le32 smb2_get_reparse_tag_special_file(umode_t mode) +@@ -774,19 +796,6 @@ static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, + pneg_ctxt->Ciphers[0] = cipher_type; + } + +-static void build_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt, +- __le16 comp_algo) +-{ +- pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; +- pneg_ctxt->DataLength = +- cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) +- - sizeof(struct smb2_neg_context)); +- pneg_ctxt->Reserved = cpu_to_le32(0); +- pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(1); +- pneg_ctxt->Flags = cpu_to_le32(0); +- pneg_ctxt->CompressionAlgorithms[0] = comp_algo; +-} +- + static void build_sign_cap_ctxt(struct smb2_signing_capabilities *pneg_ctxt, + __le16 sign_algo) + { +@@ -822,11 +831,10 @@ static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) + pneg_ctxt->Name[15] = 0x7C; + } + +-static void assemble_neg_contexts(struct ksmbd_conn *conn, +- struct smb2_negotiate_rsp *rsp, +- void *smb2_buf_len) ++static unsigned int assemble_neg_contexts(struct ksmbd_conn *conn, ++ struct smb2_negotiate_rsp *rsp) + { +- char *pneg_ctxt = (char *)rsp + ++ char * const pneg_ctxt = (char *)rsp + + le32_to_cpu(rsp->NegotiateContextOffset); + int neg_ctxt_cnt = 1; + int ctxt_size; +@@ -835,62 +843,46 @@ static void assemble_neg_contexts(struct ksmbd_conn *conn, + "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, + conn->preauth_info->Preauth_HashId); +- rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); +- inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING); + ctxt_size = sizeof(struct smb2_preauth_neg_context); +- /* Round to 8 byte boundary */ +- pneg_ctxt += round_up(sizeof(struct smb2_preauth_neg_context), 8); + + if (conn->cipher_type) { ++ /* Round to 8 byte boundary */ + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); +- build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt, ++ build_encrypt_ctxt((struct smb2_encryption_neg_context *) ++ (pneg_ctxt + ctxt_size), + conn->cipher_type); +- rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); ++ neg_ctxt_cnt++; + ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2; +- /* Round to 8 byte boundary */ +- pneg_ctxt += +- round_up(sizeof(struct smb2_encryption_neg_context) + 2, +- 8); + } + +- if (conn->compress_algorithm) { +- ctxt_size = round_up(ctxt_size, 8); +- ksmbd_debug(SMB, +- "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); +- /* Temporarily set to SMB3_COMPRESS_NONE */ +- build_compression_ctxt((struct smb2_compression_capabilities_context *)pneg_ctxt, +- conn->compress_algorithm); +- rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); +- ctxt_size += sizeof(struct smb2_compression_capabilities_context) + 2; +- /* Round to 8 byte boundary */ +- pneg_ctxt += round_up(sizeof(struct smb2_compression_capabilities_context) + 2, +- 8); +- } ++ /* compression context not yet supported */ ++ WARN_ON(conn->compress_algorithm != SMB3_COMPRESS_NONE); + + if (conn->posix_ext_supported) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); +- build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); +- rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); ++ build_posix_ctxt((struct smb2_posix_neg_context *) ++ (pneg_ctxt + ctxt_size)); ++ neg_ctxt_cnt++; + ctxt_size += sizeof(struct smb2_posix_neg_context); +- /* Round to 8 byte boundary */ +- pneg_ctxt += round_up(sizeof(struct smb2_posix_neg_context), 8); + } + + if (conn->signing_negotiated) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_SIGNING_CAPABILITIES context\n"); +- build_sign_cap_ctxt((struct smb2_signing_capabilities *)pneg_ctxt, ++ build_sign_cap_ctxt((struct smb2_signing_capabilities *) ++ (pneg_ctxt + ctxt_size), + conn->signing_algorithm); +- rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); ++ neg_ctxt_cnt++; + ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; + } + +- inc_rfc1001_len(smb2_buf_len, ctxt_size); ++ rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); ++ return ctxt_size + AUTH_GSS_PADDING; + } + + static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, +@@ -935,7 +927,7 @@ static void decode_encrypt_ctxt(struct ksmbd_conn *conn, + return; + } + +- if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)) ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) + return; + + for (i = 0; i < cph_cnt; i++) { +@@ -1106,7 +1098,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) + struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf); + struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf); + int rc = 0; +- unsigned int smb2_buf_len, smb2_neg_size; ++ unsigned int smb2_buf_len, smb2_neg_size, neg_ctxt_len = 0; + __le32 status; + + ksmbd_debug(SMB, "Received negotiate request\n"); +@@ -1199,7 +1191,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) + conn->preauth_info->Preauth_HashValue); + rsp->NegotiateContextOffset = + cpu_to_le32(OFFSET_OF_NEG_CONTEXT); +- assemble_neg_contexts(conn, rsp, work->response_buf); ++ neg_ctxt_len = assemble_neg_contexts(conn, rsp); + break; + case SMB302_PROT_ID: + init_smb3_02_server(conn); +@@ -1249,9 +1241,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work) + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + + le16_to_cpu(rsp->SecurityBufferOffset)); +- inc_rfc1001_len(work->response_buf, sizeof(struct smb2_negotiate_rsp) - +- sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + +- AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + conn->use_spnego = true; + +@@ -1269,9 +1258,16 @@ int smb2_handle_negotiate(struct ksmbd_work *work) + ksmbd_conn_set_need_negotiate(conn); + + err_out: ++ if (rc) ++ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; ++ ++ if (!rc) ++ rc = ksmbd_iov_pin_rsp(work, rsp, ++ sizeof(struct smb2_negotiate_rsp) - ++ sizeof(rsp->Buffer) + ++ AUTH_GSS_LENGTH + neg_ctxt_len); + if (rc < 0) + smb2_set_err_rsp(work); +- + return rc; + } + +@@ -1471,7 +1467,6 @@ static int ntlm_authenticate(struct ksmbd_work *work, + memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); + kfree(spnego_blob); +- inc_rfc1001_len(work->response_buf, spnego_blob_len - 1); + } + + user = session_user(conn, req); +@@ -1544,7 +1539,8 @@ static int ntlm_authenticate(struct ksmbd_work *work, + return -EINVAL; + } + sess->enc = true; +- rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) ++ rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + /* + * signing is disable if encryption is enable + * on this session +@@ -1616,7 +1612,6 @@ static int krb5_authenticate(struct ksmbd_work *work, + return -EINVAL; + } + rsp->SecurityBufferLength = cpu_to_le16(out_len); +- inc_rfc1001_len(work->response_buf, out_len - 1); + + if ((conn->sign || server_conf.enforced_signing) || + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) +@@ -1630,7 +1625,8 @@ static int krb5_authenticate(struct ksmbd_work *work, + return -EINVAL; + } + sess->enc = true; +- rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; ++ if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) ++ rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + sess->sign = false; + } + +@@ -1687,7 +1683,6 @@ int smb2_sess_setup(struct ksmbd_work *work) + rsp->SessionFlags = 0; + rsp->SecurityBufferOffset = cpu_to_le16(72); + rsp->SecurityBufferLength = 0; +- inc_rfc1001_len(work->response_buf, 9); + + ksmbd_conn_lock(conn); + if (!req->hdr.SessionId) { +@@ -1823,13 +1818,6 @@ int smb2_sess_setup(struct ksmbd_work *work) + goto out_err; + rsp->hdr.Status = + STATUS_MORE_PROCESSING_REQUIRED; +- /* +- * Note: here total size -1 is done as an +- * adjustment for 0 size blob +- */ +- inc_rfc1001_len(work->response_buf, +- le16_to_cpu(rsp->SecurityBufferLength) - 1); +- + } else if (negblob->MessageType == NtLmAuthenticate) { + rc = ntlm_authenticate(work, req, rsp); + if (rc) +@@ -1914,6 +1902,18 @@ out_err: + ksmbd_conn_set_need_negotiate(conn); + } + } ++ smb2_set_err_rsp(work); ++ } else { ++ unsigned int iov_len; ++ ++ if (rsp->SecurityBufferLength) ++ iov_len = offsetof(struct smb2_sess_setup_rsp, Buffer) + ++ le16_to_cpu(rsp->SecurityBufferLength); ++ else ++ iov_len = sizeof(struct smb2_sess_setup_rsp); ++ rc = ksmbd_iov_pin_rsp(work, rsp, iov_len); ++ if (rc) ++ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + } + + ksmbd_conn_unlock(conn); +@@ -1991,14 +1991,20 @@ int smb2_tree_connect(struct ksmbd_work *work) + if (conn->posix_ext_supported) + status.tree_conn->posix_extensions = true; + ++ write_lock(&sess->tree_conns_lock); ++ status.tree_conn->t_state = TREE_CONNECTED; ++ write_unlock(&sess->tree_conns_lock); + rsp->StructureSize = cpu_to_le16(16); +- inc_rfc1001_len(work->response_buf, 16); + out_err1: + rsp->Capabilities = 0; + rsp->Reserved = 0; + /* default manual caching */ + rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; + ++ rc = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_tree_connect_rsp)); ++ if (rc) ++ status.ret = KSMBD_TREE_CONN_STATUS_NOMEM; ++ + if (!IS_ERR(treename)) + kfree(treename); + if (!IS_ERR(name)) +@@ -2111,26 +2117,56 @@ int smb2_tree_disconnect(struct ksmbd_work *work) + struct smb2_tree_disconnect_req *req; + struct ksmbd_session *sess = work->sess; + struct ksmbd_tree_connect *tcon = work->tcon; ++ int err; + + WORK_BUFFERS(work, req, rsp); + +- rsp->StructureSize = cpu_to_le16(4); +- inc_rfc1001_len(work->response_buf, 4); +- + ksmbd_debug(SMB, "request\n"); + +- if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) { ++ if (!tcon) { + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; +- smb2_set_err_rsp(work); +- return 0; ++ err = -ENOENT; ++ goto err_out; + } + + ksmbd_close_tree_conn_fds(work); +- ksmbd_tree_conn_disconnect(sess, tcon); ++ ++ write_lock(&sess->tree_conns_lock); ++ if (tcon->t_state == TREE_DISCONNECTED) { ++ write_unlock(&sess->tree_conns_lock); ++ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; ++ err = -ENOENT; ++ goto err_out; ++ } ++ ++ WARN_ON_ONCE(atomic_dec_and_test(&tcon->refcount)); ++ tcon->t_state = TREE_DISCONNECTED; ++ write_unlock(&sess->tree_conns_lock); ++ ++ err = ksmbd_tree_conn_disconnect(sess, tcon); ++ if (err) { ++ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; ++ goto err_out; ++ } ++ + work->tcon = NULL; ++ ++ rsp->StructureSize = cpu_to_le16(4); ++ err = ksmbd_iov_pin_rsp(work, rsp, ++ sizeof(struct smb2_tree_disconnect_rsp)); ++ if (err) { ++ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; ++ goto err_out; ++ } ++ + return 0; ++ ++err_out: ++ smb2_set_err_rsp(work); ++ return err; ++ + } + + /** +@@ -2146,17 +2182,23 @@ int smb2_session_logoff(struct ksmbd_work *work) + struct smb2_logoff_rsp *rsp; + struct ksmbd_session *sess; + u64 sess_id; ++ int err; + + WORK_BUFFERS(work, req, rsp); + +- sess_id = le64_to_cpu(req->hdr.SessionId); +- +- rsp->StructureSize = cpu_to_le16(4); +- inc_rfc1001_len(work->response_buf, 4); +- + ksmbd_debug(SMB, "request\n"); + ++ ksmbd_conn_lock(conn); ++ if (!ksmbd_conn_good(conn)) { ++ ksmbd_conn_unlock(conn); ++ rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; ++ smb2_set_err_rsp(work); ++ return -ENOENT; ++ } ++ sess_id = le64_to_cpu(req->hdr.SessionId); + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); ++ ksmbd_conn_unlock(conn); ++ + ksmbd_close_session_fds(work); + ksmbd_conn_wait_idle(conn, sess_id); + +@@ -2169,7 +2211,7 @@ int smb2_session_logoff(struct ksmbd_work *work) + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); +- return 0; ++ return -ENOENT; + } + + ksmbd_destroy_file_table(&sess->file_table); +@@ -2178,6 +2220,14 @@ int smb2_session_logoff(struct ksmbd_work *work) + ksmbd_free_user(sess->user); + sess->user = NULL; + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); ++ ++ rsp->StructureSize = cpu_to_le16(4); ++ err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); ++ if (err) { ++ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; ++ smb2_set_err_rsp(work); ++ return err; ++ } + return 0; + } + +@@ -2230,7 +2280,10 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; + +- inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ ++ err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_create_rsp, Buffer)); ++ if (err) ++ goto out; ++ + kfree(name); + return 0; + +@@ -2309,7 +2362,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, + /* delete the EA only when it exits */ + if (rc > 0) { + rc = ksmbd_vfs_remove_xattr(user_ns, +- path->dentry, ++ path, + attr_name); + + if (rc < 0) { +@@ -2323,9 +2376,9 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, + /* if the EA doesn't exist, just do nothing. */ + rc = 0; + } else { +- rc = ksmbd_vfs_setxattr(user_ns, +- path->dentry, attr_name, value, +- le16_to_cpu(eabuf->EaValueLength), 0); ++ rc = ksmbd_vfs_setxattr(user_ns, path, attr_name, value, ++ le16_to_cpu(eabuf->EaValueLength), ++ 0, true); + if (rc < 0) { + ksmbd_debug(SMB, + "ksmbd_vfs_setxattr is failed(%d)\n", +@@ -2388,8 +2441,7 @@ static noinline int smb2_set_stream_name_xattr(const struct path *path, + return -EBADF; + } + +- rc = ksmbd_vfs_setxattr(user_ns, path->dentry, +- xattr_stream_name, NULL, 0, 0); ++ rc = ksmbd_vfs_setxattr(user_ns, path, xattr_stream_name, NULL, 0, 0, false); + if (rc < 0) + pr_err("Failed to store XATTR stream name :%d\n", rc); + return 0; +@@ -2417,7 +2469,7 @@ static int smb2_remove_smb_xattrs(const struct path *path) + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && + !strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, + STREAM_PREFIX_LEN)) { +- err = ksmbd_vfs_remove_xattr(user_ns, path->dentry, ++ err = ksmbd_vfs_remove_xattr(user_ns, path, + name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", +@@ -2464,8 +2516,7 @@ static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path * + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + +- rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), +- path->dentry, &da); ++ rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), path, &da, true); + if (rc) + ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); + } +@@ -2492,8 +2543,9 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, + } + } + +-static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, +- int open_flags, umode_t posix_mode, bool is_dir) ++static int smb2_creat(struct ksmbd_work *work, struct path *parent_path, ++ struct path *path, char *name, int open_flags, ++ umode_t posix_mode, bool is_dir) + { + struct ksmbd_tree_connect *tcon = work->tcon; + struct ksmbd_share_config *share = tcon->share_conf; +@@ -2520,7 +2572,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, + return rc; + } + +- rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); ++ rc = ksmbd_vfs_kern_path_locked(work, name, 0, parent_path, path, 0); + if (rc) { + pr_err("cannot get linux path (%s), err = %d\n", + name, rc); +@@ -2554,7 +2606,7 @@ static int smb2_create_sd_buffer(struct ksmbd_work *work, + sizeof(struct create_sd_buf_req)) + return -EINVAL; + return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd, +- le32_to_cpu(sd_buf->ccontext.DataLength), true); ++ le32_to_cpu(sd_buf->ccontext.DataLength), true, false); + } + + static void ksmbd_acls_fattr(struct smb_fattr *fattr, +@@ -2590,7 +2642,7 @@ int smb2_open(struct ksmbd_work *work) + struct ksmbd_tree_connect *tcon = work->tcon; + struct smb2_create_req *req; + struct smb2_create_rsp *rsp; +- struct path path; ++ struct path path, parent_path; + struct ksmbd_share_config *share = tcon->share_conf; + struct ksmbd_file *fp = NULL; + struct file *filp = NULL; +@@ -2614,6 +2666,7 @@ int smb2_open(struct ksmbd_work *work) + u64 time; + umode_t posix_mode = 0; + __le32 daccess, maximal_access = 0; ++ int iov_len = 0; + + WORK_BUFFERS(work, req, rsp); + +@@ -2635,7 +2688,7 @@ int smb2_open(struct ksmbd_work *work) + *(char *)req->Buffer == '\\') { + pr_err("not allow directory name included leading slash\n"); + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } + + name = smb2_get_name(req->Buffer, +@@ -2646,7 +2699,7 @@ int smb2_open(struct ksmbd_work *work) + if (rc != -ENOMEM) + rc = -ENOENT; + name = NULL; +- goto err_out1; ++ goto err_out2; + } + + ksmbd_debug(SMB, "converted name = %s\n", name); +@@ -2654,48 +2707,44 @@ int smb2_open(struct ksmbd_work *work) + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STREAMS)) { + rc = -EBADF; +- goto err_out1; ++ goto err_out2; + } + rc = parse_stream_name(name, &stream_name, &s_type); + if (rc < 0) +- goto err_out1; ++ goto err_out2; + } + + rc = ksmbd_validate_filename(name); + if (rc < 0) +- goto err_out1; ++ goto err_out2; + + if (ksmbd_share_veto_filename(share, name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", + name); +- goto err_out1; ++ goto err_out2; + } + } else { + name = kstrdup("", GFP_KERNEL); + if (!name) { + rc = -ENOMEM; +- goto err_out1; ++ goto err_out2; + } + } + +- req_op_level = req->RequestedOplockLevel; +- if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) +- lc = parse_lease_state(req); +- + if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) { + pr_err("Invalid impersonationlevel : 0x%x\n", + le32_to_cpu(req->ImpersonationLevel)); + rc = -EIO; + rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; +- goto err_out1; ++ goto err_out2; + } + + if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK_LE)) { + pr_err("Invalid create options : 0x%x\n", + le32_to_cpu(req->CreateOptions)); + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } else { + if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && + req->CreateOptions & FILE_RANDOM_ACCESS_LE) +@@ -2705,13 +2754,13 @@ int smb2_open(struct ksmbd_work *work) + (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION | + FILE_RESERVE_OPFILTER_LE)) { + rc = -EOPNOTSUPP; +- goto err_out1; ++ goto err_out2; + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) { + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) { + req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); + } +@@ -2723,21 +2772,21 @@ int smb2_open(struct ksmbd_work *work) + pr_err("Invalid create disposition : 0x%x\n", + le32_to_cpu(req->CreateDisposition)); + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } + + if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { + pr_err("Invalid desired access : 0x%x\n", + le32_to_cpu(req->DesiredAccess)); + rc = -EACCES; +- goto err_out1; ++ goto err_out2; + } + + if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { + pr_err("Invalid file attribute : 0x%x\n", + le32_to_cpu(req->FileAttributes)); + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } + + if (req->CreateContextsOffset) { +@@ -2745,19 +2794,19 @@ int smb2_open(struct ksmbd_work *work) + context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); +- goto err_out1; ++ goto err_out2; + } else if (context) { + ea_buf = (struct create_ea_buf_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_ea_buf_req)) { + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } + if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { + rsp->hdr.Status = STATUS_ACCESS_DENIED; + rc = -EACCES; +- goto err_out1; ++ goto err_out2; + } + } + +@@ -2765,7 +2814,7 @@ int smb2_open(struct ksmbd_work *work) + SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); +- goto err_out1; ++ goto err_out2; + } else if (context) { + ksmbd_debug(SMB, + "get query maximal access context\n"); +@@ -2776,11 +2825,11 @@ int smb2_open(struct ksmbd_work *work) + SMB2_CREATE_TIMEWARP_REQUEST, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); +- goto err_out1; ++ goto err_out2; + } else if (context) { + ksmbd_debug(SMB, "get timewarp context\n"); + rc = -EBADF; +- goto err_out1; ++ goto err_out2; + } + + if (tcon->posix_extensions) { +@@ -2788,7 +2837,7 @@ int smb2_open(struct ksmbd_work *work) + SMB2_CREATE_TAG_POSIX, 16); + if (IS_ERR(context)) { + rc = PTR_ERR(context); +- goto err_out1; ++ goto err_out2; + } else if (context) { + struct create_posix *posix = + (struct create_posix *)context; +@@ -2796,7 +2845,7 @@ int smb2_open(struct ksmbd_work *work) + le32_to_cpu(context->DataLength) < + sizeof(struct create_posix) - 4) { + rc = -EINVAL; +- goto err_out1; ++ goto err_out2; + } + ksmbd_debug(SMB, "get posix context\n"); + +@@ -2808,11 +2857,14 @@ int smb2_open(struct ksmbd_work *work) + + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; +- goto err_out1; ++ goto err_out2; + } + +- rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); ++ rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, ++ &parent_path, &path, 1); + if (!rc) { ++ file_present = true; ++ + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { + /* + * If file exists with under flags, return access +@@ -2821,7 +2873,6 @@ int smb2_open(struct ksmbd_work *work) + if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || + req->CreateDisposition == FILE_OPEN_IF_LE) { + rc = -EACCES; +- path_put(&path); + goto err_out; + } + +@@ -2829,26 +2880,23 @@ int smb2_open(struct ksmbd_work *work) + ksmbd_debug(SMB, + "User does not have write permission\n"); + rc = -EACCES; +- path_put(&path); + goto err_out; + } + } else if (d_is_symlink(path.dentry)) { + rc = -EACCES; +- path_put(&path); + goto err_out; + } +- } + +- if (rc) { ++ file_present = true; ++ user_ns = mnt_user_ns(path.mnt); ++ } else { + if (rc != -ENOENT) + goto err_out; + ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", + name, rc); + rc = 0; +- } else { +- file_present = true; +- user_ns = mnt_user_ns(path.mnt); + } ++ + if (stream_name) { + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (s_type == DATA_STREAM) { +@@ -2910,11 +2958,9 @@ int smb2_open(struct ksmbd_work *work) + if (!file_present) { + daccess = cpu_to_le32(GENERIC_ALL_FLAGS); + } else { +- rc = ksmbd_vfs_query_maximal_access(user_ns, ++ ksmbd_vfs_query_maximal_access(user_ns, + path.dentry, + &daccess); +- if (rc) +- goto err_out; + already_permitted = true; + } + maximal_access = daccess; +@@ -2935,7 +2981,8 @@ int smb2_open(struct ksmbd_work *work) + + /*create file if not present */ + if (!file_present) { +- rc = smb2_creat(work, &path, name, open_flags, posix_mode, ++ rc = smb2_creat(work, &parent_path, &path, name, open_flags, ++ posix_mode, + req->CreateOptions & FILE_DIRECTORY_FILE_LE); + if (rc) { + if (rc == -ENOENT) { +@@ -2976,15 +3023,16 @@ int smb2_open(struct ksmbd_work *work) + + if ((daccess & FILE_DELETE_LE) || + (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { +- rc = ksmbd_vfs_may_delete(user_ns, +- path.dentry); ++ rc = inode_permission(user_ns, ++ d_inode(path.dentry->d_parent), ++ MAY_EXEC | MAY_WRITE); + if (rc) + goto err_out; + } + } + } + +- rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); ++ rc = ksmbd_query_inode_status(path.dentry->d_parent); + if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { + rc = -EBUSY; + goto err_out; +@@ -3040,7 +3088,7 @@ int smb2_open(struct ksmbd_work *work) + struct inode *inode = d_inode(path.dentry); + + posix_acl_rc = ksmbd_vfs_inherit_posix_acl(user_ns, +- inode, ++ &path, + d_inode(path.dentry->d_parent)); + if (posix_acl_rc) + ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); +@@ -3056,7 +3104,7 @@ int smb2_open(struct ksmbd_work *work) + if (rc) { + if (posix_acl_rc) + ksmbd_vfs_set_init_posix_acl(user_ns, +- inode); ++ &path); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { +@@ -3096,9 +3144,10 @@ int smb2_open(struct ksmbd_work *work) + + rc = ksmbd_vfs_set_sd_xattr(conn, + user_ns, +- path.dentry, ++ &path, + pntsd, +- pntsd_size); ++ pntsd_size, ++ false); + kfree(pntsd); + if (rc) + pr_err("failed to store ntacl in xattr : %d\n", +@@ -3121,11 +3170,6 @@ int smb2_open(struct ksmbd_work *work) + + fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | + FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); +- if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && +- !fp->attrib_only && !stream_name) { +- smb_break_all_oplock(work, fp); +- need_truncate = 1; +- } + + /* fp should be searchable through ksmbd_inode.m_fp_list + * after daccess, saccess, attrib_only, and stream are +@@ -3141,23 +3185,43 @@ int smb2_open(struct ksmbd_work *work) + goto err_out; + } + ++ if (file_present || created) ++ ksmbd_vfs_kern_path_unlock(&parent_path, &path); ++ ++ if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && ++ !fp->attrib_only && !stream_name) { ++ smb_break_all_oplock(work, fp); ++ need_truncate = 1; ++ } ++ ++ req_op_level = req->RequestedOplockLevel; ++ if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) ++ lc = parse_lease_state(req, S_ISDIR(file_inode(filp)->i_mode)); ++ + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || + (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && + !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) { + if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { + rc = share_ret; +- goto err_out; ++ goto err_out1; + } + } else { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { ++ /* ++ * Compare parent lease using parent key. If there is no ++ * a lease that has same parent key, Send lease break ++ * notification. ++ */ ++ smb_send_parent_lease_break_noti(fp, lc); ++ + req_op_level = smb2_map_lease_to_oplock(lc->req_state); + ksmbd_debug(SMB, + "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", + name, req_op_level, lc->req_state); + rc = find_same_lease_key(sess, fp->f_ci, lc); + if (rc) +- goto err_out; ++ goto err_out1; + } else if (open_flags == O_RDONLY && + (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || + req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) +@@ -3168,16 +3232,16 @@ int smb2_open(struct ksmbd_work *work) + le32_to_cpu(req->hdr.Id.SyncId.TreeId), + lc, share_ret); + if (rc < 0) +- goto err_out; ++ goto err_out1; + } + + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) + ksmbd_fd_set_delete_on_close(fp, file_info); + + if (need_truncate) { +- rc = smb2_create_truncate(&path); ++ rc = smb2_create_truncate(&fp->filp->f_path); + if (rc) +- goto err_out; ++ goto err_out1; + } + + if (req->CreateContextsOffset) { +@@ -3187,7 +3251,7 @@ int smb2_open(struct ksmbd_work *work) + SMB2_CREATE_ALLOCATION_SIZE, 4); + if (IS_ERR(az_req)) { + rc = PTR_ERR(az_req); +- goto err_out; ++ goto err_out1; + } else if (az_req) { + loff_t alloc_size; + int err; +@@ -3196,7 +3260,7 @@ int smb2_open(struct ksmbd_work *work) + le32_to_cpu(az_req->ccontext.DataLength) < + sizeof(struct create_alloc_size_req)) { + rc = -EINVAL; +- goto err_out; ++ goto err_out1; + } + alloc_size = le64_to_cpu(az_req->AllocationSize); + ksmbd_debug(SMB, +@@ -3214,7 +3278,7 @@ int smb2_open(struct ksmbd_work *work) + context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); +- goto err_out; ++ goto err_out1; + } else if (context) { + ksmbd_debug(SMB, "get query on disk id context\n"); + query_disk_id = 1; +@@ -3223,7 +3287,7 @@ int smb2_open(struct ksmbd_work *work) + + rc = ksmbd_vfs_getattr(&path, &stat); + if (rc) +- goto err_out; ++ goto err_out1; + + if (stat.result_mask & STATX_BTIME) + fp->create_time = ksmbd_UnixTimeToNT(stat.btime); +@@ -3266,7 +3330,7 @@ int smb2_open(struct ksmbd_work *work) + + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; +- inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ ++ iov_len = offsetof(struct smb2_create_rsp, Buffer); + + /* If lease is request send lease context response */ + if (opinfo && opinfo->is_lease) { +@@ -3281,8 +3345,7 @@ int smb2_open(struct ksmbd_work *work) + create_lease_buf(rsp->Buffer, opinfo->o_lease); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_lease_size); +- inc_rfc1001_len(work->response_buf, +- conn->vals->create_lease_size); ++ iov_len += conn->vals->create_lease_size; + next_ptr = &lease_ccontext->Next; + next_off = conn->vals->create_lease_size; + } +@@ -3302,8 +3365,7 @@ int smb2_open(struct ksmbd_work *work) + le32_to_cpu(maximal_access)); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_mxac_size); +- inc_rfc1001_len(work->response_buf, +- conn->vals->create_mxac_size); ++ iov_len += conn->vals->create_mxac_size; + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &mxac_ccontext->Next; +@@ -3321,8 +3383,7 @@ int smb2_open(struct ksmbd_work *work) + stat.ino, tcon->id); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_disk_id_size); +- inc_rfc1001_len(work->response_buf, +- conn->vals->create_disk_id_size); ++ iov_len += conn->vals->create_disk_id_size; + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &disk_id_ccontext->Next; +@@ -3336,8 +3397,7 @@ int smb2_open(struct ksmbd_work *work) + fp); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_posix_size); +- inc_rfc1001_len(work->response_buf, +- conn->vals->create_posix_size); ++ iov_len += conn->vals->create_posix_size; + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + } +@@ -3348,10 +3408,17 @@ int smb2_open(struct ksmbd_work *work) + } + + err_out: +- if (file_present || created) +- path_put(&path); +- ksmbd_revert_fsids(work); ++ if (rc && (file_present || created)) ++ ksmbd_vfs_kern_path_unlock(&parent_path, &path); ++ + err_out1: ++ ksmbd_revert_fsids(work); ++ ++err_out2: ++ if (!rc) { ++ ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED); ++ rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); ++ } + if (rc) { + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; +@@ -3525,7 +3592,7 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, + goto free_conv_name; + } + +- struct_sz = readdir_info_level_struct_sz(info_level) - 1 + conv_len; ++ struct_sz = readdir_info_level_struct_sz(info_level) + conv_len; + next_entry_offset = ALIGN(struct_sz, KSMBD_DIR_INFO_ALIGNMENT); + d_info->last_entry_off_align = next_entry_offset - struct_sz; + +@@ -3777,7 +3844,7 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, + return -EOPNOTSUPP; + + conv_len = (d_info->name_len + 1) * 2; +- next_entry_offset = ALIGN(struct_sz - 1 + conv_len, ++ next_entry_offset = ALIGN(struct_sz + conv_len, + KSMBD_DIR_INFO_ALIGNMENT); + + if (next_entry_offset > d_info->out_buf_len) { +@@ -4077,7 +4144,10 @@ int smb2_query_dir(struct ksmbd_work *work) + rsp->OutputBufferOffset = cpu_to_le16(0); + rsp->OutputBufferLength = cpu_to_le32(0); + rsp->Buffer[0] = 0; +- inc_rfc1001_len(work->response_buf, 9); ++ rc = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ sizeof(struct smb2_query_directory_rsp)); ++ if (rc) ++ goto err_out; + } else { + no_buf_len: + ((struct file_directory_info *) +@@ -4089,7 +4159,11 @@ no_buf_len: + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + rsp->OutputBufferLength = cpu_to_le32(d_info.data_count); +- inc_rfc1001_len(work->response_buf, 8 + d_info.data_count); ++ rc = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ offsetof(struct smb2_query_directory_rsp, Buffer) + ++ d_info.data_count); ++ if (rc) ++ goto err_out; + } + + kfree(srch_ptr); +@@ -4130,27 +4204,18 @@ err_out2: + * @reqOutputBufferLength: max buffer length expected in command response + * @rsp: query info response buffer contains output buffer length + * @rsp_org: base response buffer pointer in case of chained response +- * @infoclass_size: query info class response buffer size + * + * Return: 0 on success, otherwise error + */ + static int buffer_check_err(int reqOutputBufferLength, + struct smb2_query_info_rsp *rsp, +- void *rsp_org, int infoclass_size) ++ void *rsp_org) + { + if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { +- if (reqOutputBufferLength < infoclass_size) { +- pr_err("Invalid Buffer Size Requested\n"); +- rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; +- *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); +- return -EINVAL; +- } +- +- ksmbd_debug(SMB, "Buffer Overflow\n"); +- rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; +- *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr) + +- reqOutputBufferLength); +- rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength); ++ pr_err("Invalid Buffer Size Requested\n"); ++ rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; ++ *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); ++ return -EINVAL; + } + return 0; + } +@@ -4169,7 +4234,6 @@ static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp, + sinfo->Directory = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_standard_info)); + } + + static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, +@@ -4183,7 +4247,6 @@ static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, + file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63)); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); + } + + static int smb2_get_info_file_pipe(struct ksmbd_session *sess, +@@ -4209,14 +4272,12 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, + case FILE_STANDARD_INFORMATION: + get_standard_info_pipe(rsp, rsp_org); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), +- rsp, rsp_org, +- FILE_STANDARD_INFORMATION_SIZE); ++ rsp, rsp_org); + break; + case FILE_INTERNAL_INFORMATION: + get_internal_info_pipe(rsp, id, rsp_org); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), +- rsp, rsp_org, +- FILE_INTERNAL_INFORMATION_SIZE); ++ rsp, rsp_org); + break; + default: + ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", +@@ -4384,7 +4445,6 @@ done: + if (rsp_data_cnt == 0) + rsp->hdr.Status = STATUS_NO_EAS_ON_FILE; + rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt); +- inc_rfc1001_len(rsp_org, rsp_data_cnt); + out: + kvfree(xattr_list); + return rc; +@@ -4399,7 +4459,6 @@ static void get_file_access_info(struct smb2_query_info_rsp *rsp, + file_info->AccessFlags = fp->daccess; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_access_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_access_info)); + } + + static int get_file_basic_info(struct smb2_query_info_rsp *rsp, +@@ -4429,7 +4488,6 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, + basic_info->Pad1 = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_basic_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_basic_info)); + return 0; + } + +@@ -4454,8 +4512,6 @@ static void get_file_standard_info(struct smb2_query_info_rsp *rsp, + sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); +- inc_rfc1001_len(rsp_org, +- sizeof(struct smb2_file_standard_info)); + } + + static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, +@@ -4467,8 +4523,6 @@ static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, + file_info->AlignmentRequirement = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alignment_info)); +- inc_rfc1001_len(rsp_org, +- sizeof(struct smb2_file_alignment_info)); + } + + static int get_file_all_info(struct ksmbd_work *work, +@@ -4532,7 +4586,6 @@ static int get_file_all_info(struct ksmbd_work *work, + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_all_info) + conv_len - 1); + kfree(filename); +- inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); + return 0; + } + +@@ -4555,7 +4608,6 @@ static void get_file_alternate_info(struct ksmbd_work *work, + file_info->FileNameLength = cpu_to_le32(conv_len); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len); +- inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); + } + + static void get_file_stream_info(struct ksmbd_work *work, +@@ -4655,7 +4707,6 @@ out: + kvfree(xattr_list); + + rsp->OutputBufferLength = cpu_to_le32(nbytes); +- inc_rfc1001_len(rsp_org, nbytes); + } + + static void get_file_internal_info(struct smb2_query_info_rsp *rsp, +@@ -4670,7 +4721,6 @@ static void get_file_internal_info(struct smb2_query_info_rsp *rsp, + file_info->IndexNumber = cpu_to_le64(stat.ino); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); + } + + static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, +@@ -4706,7 +4756,6 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, + file_info->Reserved = cpu_to_le32(0); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ntwrk_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ntwrk_info)); + return 0; + } + +@@ -4718,7 +4767,6 @@ static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org) + file_info->EASize = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ea_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ea_info)); + } + + static void get_file_position_info(struct smb2_query_info_rsp *rsp, +@@ -4730,7 +4778,6 @@ static void get_file_position_info(struct smb2_query_info_rsp *rsp, + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_pos_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_pos_info)); + } + + static void get_file_mode_info(struct smb2_query_info_rsp *rsp, +@@ -4742,7 +4789,6 @@ static void get_file_mode_info(struct smb2_query_info_rsp *rsp, + file_info->Mode = fp->coption & FILE_MODE_INFO_MASK; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_mode_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_mode_info)); + } + + static void get_file_compression_info(struct smb2_query_info_rsp *rsp, +@@ -4764,7 +4810,6 @@ static void get_file_compression_info(struct smb2_query_info_rsp *rsp, + + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_comp_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_comp_info)); + } + + static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, +@@ -4783,11 +4828,10 @@ static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, + file_info->ReparseTag = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_attr_tag_info)); +- inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_attr_tag_info)); + return 0; + } + +-static int find_file_posix_info(struct smb2_query_info_rsp *rsp, ++static void find_file_posix_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) + { + struct smb311_posix_qinfo *file_info; +@@ -4825,8 +4869,6 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, + SIDUNIX_GROUP, (struct smb_sid *)&file_info->Sids[16]); + + rsp->OutputBufferLength = cpu_to_le32(out_buf_len); +- inc_rfc1001_len(rsp_org, out_buf_len); +- return out_buf_len; + } + + static int smb2_get_info_file(struct ksmbd_work *work, +@@ -4836,7 +4878,6 @@ static int smb2_get_info_file(struct ksmbd_work *work, + struct ksmbd_file *fp; + int fileinfoclass = 0; + int rc = 0; +- int file_infoclass_size; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + + if (test_share_config_flag(work->tcon->share_conf, +@@ -4869,85 +4910,69 @@ static int smb2_get_info_file(struct ksmbd_work *work, + switch (fileinfoclass) { + case FILE_ACCESS_INFORMATION: + get_file_access_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_ACCESS_INFORMATION_SIZE; + break; + + case FILE_BASIC_INFORMATION: + rc = get_file_basic_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_BASIC_INFORMATION_SIZE; + break; + + case FILE_STANDARD_INFORMATION: + get_file_standard_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_STANDARD_INFORMATION_SIZE; + break; + + case FILE_ALIGNMENT_INFORMATION: + get_file_alignment_info(rsp, work->response_buf); +- file_infoclass_size = FILE_ALIGNMENT_INFORMATION_SIZE; + break; + + case FILE_ALL_INFORMATION: + rc = get_file_all_info(work, rsp, fp, work->response_buf); +- file_infoclass_size = FILE_ALL_INFORMATION_SIZE; + break; + + case FILE_ALTERNATE_NAME_INFORMATION: + get_file_alternate_info(work, rsp, fp, work->response_buf); +- file_infoclass_size = FILE_ALTERNATE_NAME_INFORMATION_SIZE; + break; + + case FILE_STREAM_INFORMATION: + get_file_stream_info(work, rsp, fp, work->response_buf); +- file_infoclass_size = FILE_STREAM_INFORMATION_SIZE; + break; + + case FILE_INTERNAL_INFORMATION: + get_file_internal_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_INTERNAL_INFORMATION_SIZE; + break; + + case FILE_NETWORK_OPEN_INFORMATION: + rc = get_file_network_open_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_NETWORK_OPEN_INFORMATION_SIZE; + break; + + case FILE_EA_INFORMATION: + get_file_ea_info(rsp, work->response_buf); +- file_infoclass_size = FILE_EA_INFORMATION_SIZE; + break; + + case FILE_FULL_EA_INFORMATION: + rc = smb2_get_ea(work, fp, req, rsp, work->response_buf); +- file_infoclass_size = FILE_FULL_EA_INFORMATION_SIZE; + break; + + case FILE_POSITION_INFORMATION: + get_file_position_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_POSITION_INFORMATION_SIZE; + break; + + case FILE_MODE_INFORMATION: + get_file_mode_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_MODE_INFORMATION_SIZE; + break; + + case FILE_COMPRESSION_INFORMATION: + get_file_compression_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_COMPRESSION_INFORMATION_SIZE; + break; + + case FILE_ATTRIBUTE_TAG_INFORMATION: + rc = get_file_attribute_tag_info(rsp, fp, work->response_buf); +- file_infoclass_size = FILE_ATTRIBUTE_TAG_INFORMATION_SIZE; + break; + case SMB_FIND_FILE_POSIX_INFO: + if (!work->tcon->posix_extensions) { + pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + rc = -EOPNOTSUPP; + } else { +- file_infoclass_size = find_file_posix_info(rsp, fp, +- work->response_buf); ++ find_file_posix_info(rsp, fp, work->response_buf); + } + break; + default: +@@ -4957,8 +4982,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, + } + if (!rc) + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), +- rsp, work->response_buf, +- file_infoclass_size); ++ rsp, work->response_buf); + ksmbd_fd_put(work, fp); + return rc; + } +@@ -4974,7 +4998,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + struct kstatfs stfs; + struct path path; + int rc = 0, len; +- int fs_infoclass_size = 0; + + if (!share->path) + return -EIO; +@@ -5004,8 +5027,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->DeviceType = cpu_to_le32(stfs.f_type); + info->DeviceCharacteristics = cpu_to_le32(0x00000020); + rsp->OutputBufferLength = cpu_to_le32(8); +- inc_rfc1001_len(work->response_buf, 8); +- fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE; + break; + } + case FS_ATTRIBUTE_INFORMATION: +@@ -5034,8 +5055,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->FileSystemNameLen = cpu_to_le32(len); + sz = sizeof(struct filesystem_attribute_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); +- inc_rfc1001_len(work->response_buf, sz); +- fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE; + break; + } + case FS_VOLUME_INFORMATION: +@@ -5062,8 +5081,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->Reserved = 0; + sz = sizeof(struct filesystem_vol_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); +- inc_rfc1001_len(work->response_buf, sz); +- fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE; + break; + } + case FS_SIZE_INFORMATION: +@@ -5076,8 +5093,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); + rsp->OutputBufferLength = cpu_to_le32(24); +- inc_rfc1001_len(work->response_buf, 24); +- fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; + break; + } + case FS_FULL_SIZE_INFORMATION: +@@ -5093,8 +5108,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); + rsp->OutputBufferLength = cpu_to_le32(32); +- inc_rfc1001_len(work->response_buf, 32); +- fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; + break; + } + case FS_OBJECT_ID_INFORMATION: +@@ -5114,8 +5127,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->extended_info.rel_date = 0; + memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0")); + rsp->OutputBufferLength = cpu_to_le32(64); +- inc_rfc1001_len(work->response_buf, 64); +- fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; + break; + } + case FS_SECTOR_SIZE_INFORMATION: +@@ -5137,8 +5148,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->ByteOffsetForSectorAlignment = 0; + info->ByteOffsetForPartitionAlignment = 0; + rsp->OutputBufferLength = cpu_to_le32(28); +- inc_rfc1001_len(work->response_buf, 28); +- fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE; + break; + } + case FS_CONTROL_INFORMATION: +@@ -5159,8 +5168,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); + info->Padding = 0; + rsp->OutputBufferLength = cpu_to_le32(48); +- inc_rfc1001_len(work->response_buf, 48); +- fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE; + break; + } + case FS_POSIX_INFORMATION: +@@ -5180,8 +5187,6 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + info->TotalFileNodes = cpu_to_le64(stfs.f_files); + info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); + rsp->OutputBufferLength = cpu_to_le32(56); +- inc_rfc1001_len(work->response_buf, 56); +- fs_infoclass_size = FS_POSIX_INFORMATION_SIZE; + } + break; + } +@@ -5190,8 +5195,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, + return -EOPNOTSUPP; + } + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), +- rsp, work->response_buf, +- fs_infoclass_size); ++ rsp, work->response_buf); + path_put(&path); + return rc; + } +@@ -5225,7 +5229,6 @@ static int smb2_get_info_sec(struct ksmbd_work *work, + + secdesclen = sizeof(struct smb_ntsd); + rsp->OutputBufferLength = cpu_to_le32(secdesclen); +- inc_rfc1001_len(work->response_buf, secdesclen); + + return 0; + } +@@ -5270,7 +5273,6 @@ static int smb2_get_info_sec(struct ksmbd_work *work, + return rc; + + rsp->OutputBufferLength = cpu_to_le32(secdesclen); +- inc_rfc1001_len(work->response_buf, secdesclen); + return 0; + } + +@@ -5309,6 +5311,14 @@ int smb2_query_info(struct ksmbd_work *work) + rc = -EOPNOTSUPP; + } + ++ if (!rc) { ++ rsp->StructureSize = cpu_to_le16(9); ++ rsp->OutputBufferOffset = cpu_to_le16(72); ++ rc = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ offsetof(struct smb2_query_info_rsp, Buffer) + ++ le32_to_cpu(rsp->OutputBufferLength)); ++ } ++ + if (rc < 0) { + if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; +@@ -5316,6 +5326,8 @@ int smb2_query_info(struct ksmbd_work *work) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (rc == -EIO) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; ++ else if (rc == -ENOMEM) ++ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + smb2_set_err_rsp(work); +@@ -5324,9 +5336,6 @@ int smb2_query_info(struct ksmbd_work *work) + rc); + return rc; + } +- rsp->StructureSize = cpu_to_le16(9); +- rsp->OutputBufferOffset = cpu_to_le16(72); +- inc_rfc1001_len(work->response_buf, 8); + return 0; + } + +@@ -5357,8 +5366,9 @@ static noinline int smb2_close_pipe(struct ksmbd_work *work) + rsp->AllocationSize = 0; + rsp->EndOfFile = 0; + rsp->Attributes = 0; +- inc_rfc1001_len(work->response_buf, 60); +- return 0; ++ ++ return ksmbd_iov_pin_rsp(work, (void *)rsp, ++ sizeof(struct smb2_close_rsp)); + } + + /** +@@ -5463,15 +5473,17 @@ int smb2_close(struct ksmbd_work *work) + + err = ksmbd_close_fd(work, volatile_id); + out: ++ if (!err) ++ err = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ sizeof(struct smb2_close_rsp)); ++ + if (err) { + if (rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_FILE_CLOSED; + smb2_set_err_rsp(work); +- } else { +- inc_rfc1001_len(work->response_buf, 60); + } + +- return 0; ++ return err; + } + + /** +@@ -5489,50 +5501,24 @@ int smb2_echo(struct ksmbd_work *work) + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; +- inc_rfc1001_len(work->response_buf, 4); +- return 0; ++ return ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_echo_rsp)); + } + + static int smb2_rename(struct ksmbd_work *work, + struct ksmbd_file *fp, +- struct user_namespace *user_ns, + struct smb2_file_rename_info *file_info, + struct nls_table *local_nls) + { + struct ksmbd_share_config *share = fp->tcon->share_conf; +- char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; +- char *pathname = NULL; +- struct path path; +- bool file_present = true; +- int rc; ++ char *new_name = NULL; ++ int rc, flags = 0; + + ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n"); +- pathname = kmalloc(PATH_MAX, GFP_KERNEL); +- if (!pathname) +- return -ENOMEM; +- +- abs_oldname = file_path(fp->filp, pathname, PATH_MAX); +- if (IS_ERR(abs_oldname)) { +- rc = -EINVAL; +- goto out; +- } +- old_name = strrchr(abs_oldname, '/'); +- if (old_name && old_name[1] != '\0') { +- old_name++; +- } else { +- ksmbd_debug(SMB, "can't get last component in path %s\n", +- abs_oldname); +- rc = -ENOENT; +- goto out; +- } +- + new_name = smb2_get_name(file_info->FileName, + le32_to_cpu(file_info->FileNameLength), + local_nls); +- if (IS_ERR(new_name)) { +- rc = PTR_ERR(new_name); +- goto out; +- } ++ if (IS_ERR(new_name)) ++ return PTR_ERR(new_name); + + if (strchr(new_name, ':')) { + int s_type; +@@ -5558,10 +5544,10 @@ static int smb2_rename(struct ksmbd_work *work, + if (rc) + goto out; + +- rc = ksmbd_vfs_setxattr(user_ns, +- fp->filp->f_path.dentry, ++ rc = ksmbd_vfs_setxattr(file_mnt_user_ns(fp->filp), ++ &fp->filp->f_path, + xattr_stream_name, +- NULL, 0, 0); ++ NULL, 0, 0, true); + if (rc < 0) { + pr_err("failed to store stream name in xattr: %d\n", + rc); +@@ -5573,47 +5559,18 @@ static int smb2_rename(struct ksmbd_work *work, + } + + ksmbd_debug(SMB, "new name %s\n", new_name); +- rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1); +- if (rc) { +- if (rc != -ENOENT) +- goto out; +- file_present = false; +- } else { +- path_put(&path); +- } +- + if (ksmbd_share_veto_filename(share, new_name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name); + goto out; + } + +- if (file_info->ReplaceIfExists) { +- if (file_present) { +- rc = ksmbd_vfs_remove_file(work, new_name); +- if (rc) { +- if (rc != -ENOTEMPTY) +- rc = -EINVAL; +- ksmbd_debug(SMB, "cannot delete %s, rc %d\n", +- new_name, rc); +- goto out; +- } +- } +- } else { +- if (file_present && +- strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) { +- rc = -EEXIST; +- ksmbd_debug(SMB, +- "cannot rename already existing file\n"); +- goto out; +- } +- } ++ if (!file_info->ReplaceIfExists) ++ flags = RENAME_NOREPLACE; + +- rc = ksmbd_vfs_fp_rename(work, fp, new_name); ++ rc = ksmbd_vfs_rename(work, &fp->filp->f_path, new_name, flags); + out: +- kfree(pathname); +- if (!IS_ERR(new_name)) +- kfree(new_name); ++ kfree(new_name); + return rc; + } + +@@ -5624,8 +5581,8 @@ static int smb2_create_link(struct ksmbd_work *work, + struct nls_table *local_nls) + { + char *link_name = NULL, *target_name = NULL, *pathname = NULL; +- struct path path; +- bool file_present = true; ++ struct path path, parent_path; ++ bool file_present = false; + int rc; + + if (buf_len < (u64)sizeof(struct smb2_file_link_info) + +@@ -5653,18 +5610,17 @@ static int smb2_create_link(struct ksmbd_work *work, + } + + ksmbd_debug(SMB, "target name is %s\n", target_name); +- rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0); ++ rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS, ++ &parent_path, &path, 0); + if (rc) { + if (rc != -ENOENT) + goto out; +- file_present = false; +- } else { +- path_put(&path); +- } ++ } else ++ file_present = true; + + if (file_info->ReplaceIfExists) { + if (file_present) { +- rc = ksmbd_vfs_remove_file(work, link_name); ++ rc = ksmbd_vfs_remove_file(work, &path); + if (rc) { + rc = -EINVAL; + ksmbd_debug(SMB, "cannot delete %s\n", +@@ -5684,6 +5640,9 @@ static int smb2_create_link(struct ksmbd_work *work, + if (rc) + rc = -EINVAL; + out: ++ if (file_present) ++ ksmbd_vfs_kern_path_unlock(&parent_path, &path); ++ + if (!IS_ERR(link_name)) + kfree(link_name); + kfree(pathname); +@@ -5750,8 +5709,8 @@ static int set_file_basic_info(struct ksmbd_file *fp, + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + +- rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, +- filp->f_path.dentry, &da); ++ rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, &filp->f_path, &da, ++ true); + if (rc) + ksmbd_debug(SMB, + "failed to restore file attribute in EA\n"); +@@ -5861,12 +5820,6 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_file_rename_info *rename_info, + unsigned int buf_len) + { +- struct user_namespace *user_ns; +- struct ksmbd_file *parent_fp; +- struct dentry *parent; +- struct dentry *dentry = fp->filp->f_path.dentry; +- int ret; +- + if (!(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; +@@ -5876,32 +5829,10 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, + le32_to_cpu(rename_info->FileNameLength)) + return -EINVAL; + +- user_ns = file_mnt_user_ns(fp->filp); +- if (ksmbd_stream_fd(fp)) +- goto next; +- +- parent = dget_parent(dentry); +- ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); +- if (ret) { +- dput(parent); +- return ret; +- } +- +- parent_fp = ksmbd_lookup_fd_inode(d_inode(parent)); +- inode_unlock(d_inode(parent)); +- dput(parent); ++ if (!le32_to_cpu(rename_info->FileNameLength)) ++ return -EINVAL; + +- if (parent_fp) { +- if (parent_fp->daccess & FILE_DELETE_LE) { +- pr_err("parent dir is opened with delete access\n"); +- ksmbd_fd_put(work, parent_fp); +- return -ESHARE; +- } +- ksmbd_fd_put(work, parent_fp); +- } +-next: +- return smb2_rename(work, fp, user_ns, rename_info, +- work->conn->local_nls); ++ return smb2_rename(work, fp, rename_info, work->conn->local_nls); + } + + static int set_file_disposition_info(struct ksmbd_file *fp, +@@ -6091,7 +6022,7 @@ static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, + fp->saccess |= FILE_SHARE_DELETE_LE; + + return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, +- buf_len, false); ++ buf_len, false, true); + } + + /** +@@ -6161,7 +6092,10 @@ int smb2_set_info(struct ksmbd_work *work) + goto err_out; + + rsp->StructureSize = cpu_to_le16(2); +- inc_rfc1001_len(work->response_buf, 2); ++ rc = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ sizeof(struct smb2_set_info_rsp)); ++ if (rc) ++ goto err_out; + ksmbd_fd_put(work, fp); + return 0; + +@@ -6208,28 +6142,36 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) + + id = req->VolatileFileId; + +- inc_rfc1001_len(work->response_buf, 16); + rpc_resp = ksmbd_rpc_read(work->sess, id); + if (rpc_resp) { ++ void *aux_payload_buf; ++ + if (rpc_resp->flags != KSMBD_RPC_OK) { + err = -EINVAL; + goto out; + } + +- work->aux_payload_buf = +- kvmalloc(rpc_resp->payload_sz, GFP_KERNEL | __GFP_ZERO); +- if (!work->aux_payload_buf) { ++ aux_payload_buf = ++ kvmalloc(rpc_resp->payload_sz, GFP_KERNEL); ++ if (!aux_payload_buf) { + err = -ENOMEM; + goto out; + } + +- memcpy(work->aux_payload_buf, rpc_resp->payload, +- rpc_resp->payload_sz); ++ memcpy(aux_payload_buf, rpc_resp->payload, rpc_resp->payload_sz); + + nbytes = rpc_resp->payload_sz; +- work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; +- work->aux_payload_sz = nbytes; ++ err = ksmbd_iov_pin_rsp_read(work, (void *)rsp, ++ offsetof(struct smb2_read_rsp, Buffer), ++ aux_payload_buf, nbytes); ++ if (err) ++ goto out; + kvfree(rpc_resp); ++ } else { ++ err = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ offsetof(struct smb2_read_rsp, Buffer)); ++ if (err) ++ goto out; + } + + rsp->StructureSize = cpu_to_le16(17); +@@ -6238,7 +6180,6 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Flags = 0; +- inc_rfc1001_len(work->response_buf, nbytes); + return 0; + + out: +@@ -6312,13 +6253,8 @@ int smb2_read(struct ksmbd_work *work) + int err = 0; + bool is_rdma_channel = false; + unsigned int max_read_size = conn->vals->max_read_size; +- +- WORK_BUFFERS(work, req, rsp); +- if (work->next_smb2_rcv_hdr_off) { +- work->send_no_response = 1; +- err = -EOPNOTSUPP; +- goto out; +- } ++ unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; ++ void *aux_payload_buf; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { +@@ -6326,6 +6262,25 @@ int smb2_read(struct ksmbd_work *work) + return smb2_read_pipe(work); + } + ++ if (work->next_smb2_rcv_hdr_off) { ++ req = ksmbd_req_buf_next(work); ++ rsp = ksmbd_resp_buf_next(work); ++ if (!has_file_id(req->VolatileFileId)) { ++ ksmbd_debug(SMB, "Compound request set FID = %llu\n", ++ work->compound_fid); ++ id = work->compound_fid; ++ pid = work->compound_pfid; ++ } ++ } else { ++ req = smb2_get_msg(work->request_buf); ++ rsp = smb2_get_msg(work->response_buf); ++ } ++ ++ if (!has_file_id(id)) { ++ id = req->VolatileFileId; ++ pid = req->PersistentFileId; ++ } ++ + if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || + req->Channel == SMB2_CHANNEL_RDMA_V1) { + is_rdma_channel = true; +@@ -6348,7 +6303,7 @@ int smb2_read(struct ksmbd_work *work) + goto out; + } + +- fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); ++ fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) { + err = -ENOENT; + goto out; +@@ -6374,21 +6329,20 @@ int smb2_read(struct ksmbd_work *work) + ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", + fp->filp, offset, length); + +- work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); +- if (!work->aux_payload_buf) { ++ aux_payload_buf = kvzalloc(length, GFP_KERNEL); ++ if (!aux_payload_buf) { + err = -ENOMEM; + goto out; + } + +- nbytes = ksmbd_vfs_read(work, fp, length, &offset); ++ nbytes = ksmbd_vfs_read(work, fp, length, &offset, aux_payload_buf); + if (nbytes < 0) { + err = nbytes; + goto out; + } + + if ((nbytes == 0 && length != 0) || nbytes < mincount) { +- kvfree(work->aux_payload_buf); +- work->aux_payload_buf = NULL; ++ kvfree(aux_payload_buf); + rsp->hdr.Status = STATUS_END_OF_FILE; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); +@@ -6401,11 +6355,10 @@ int smb2_read(struct ksmbd_work *work) + if (is_rdma_channel == true) { + /* write data to the client using rdma channel */ + remain_bytes = smb2_read_rdma_channel(work, req, +- work->aux_payload_buf, ++ aux_payload_buf, + nbytes); +- kvfree(work->aux_payload_buf); +- work->aux_payload_buf = NULL; +- ++ kvfree(aux_payload_buf); ++ aux_payload_buf = NULL; + nbytes = 0; + if (remain_bytes < 0) { + err = (int)remain_bytes; +@@ -6419,10 +6372,11 @@ int smb2_read(struct ksmbd_work *work) + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = cpu_to_le32(remain_bytes); + rsp->Flags = 0; +- inc_rfc1001_len(work->response_buf, 16); +- work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; +- work->aux_payload_sz = nbytes; +- inc_rfc1001_len(work->response_buf, nbytes); ++ err = ksmbd_iov_pin_rsp_read(work, (void *)rsp, ++ offsetof(struct smb2_read_rsp, Buffer), ++ aux_payload_buf, nbytes); ++ if (err) ++ goto out; + ksmbd_fd_put(work, fp); + return 0; + +@@ -6505,8 +6459,8 @@ static noinline int smb2_write_pipe(struct ksmbd_work *work) + rsp->DataLength = cpu_to_le32(length); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; +- inc_rfc1001_len(work->response_buf, 16); +- return 0; ++ err = ksmbd_iov_pin_rsp(work, (void *)rsp, ++ offsetof(struct smb2_write_rsp, Buffer)); + out: + if (err) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; +@@ -6525,7 +6479,7 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, + int ret; + ssize_t nbytes; + +- data_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); ++ data_buf = kvzalloc(length, GFP_KERNEL); + if (!data_buf) + return -ENOMEM; + +@@ -6662,7 +6616,9 @@ int smb2_write(struct ksmbd_work *work) + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; +- inc_rfc1001_len(work->response_buf, 16); ++ err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_write_rsp, Buffer)); ++ if (err) ++ goto out; + ksmbd_fd_put(work, fp); + return 0; + +@@ -6709,15 +6665,11 @@ int smb2_flush(struct ksmbd_work *work) + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; +- inc_rfc1001_len(work->response_buf, 4); +- return 0; ++ return ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_flush_rsp)); + + out: +- if (err) { +- rsp->hdr.Status = STATUS_INVALID_HANDLE; +- smb2_set_err_rsp(work); +- } +- ++ rsp->hdr.Status = STATUS_INVALID_HANDLE; ++ smb2_set_err_rsp(work); + return err; + } + +@@ -6843,7 +6795,7 @@ static int smb2_set_flock_flags(struct file_lock *flock, int flags) + case SMB2_LOCKFLAG_UNLOCK: + ksmbd_debug(SMB, "received unlock request\n"); + flock->fl_type = F_UNLCK; +- cmd = 0; ++ cmd = F_SETLK; + break; + } + +@@ -6949,6 +6901,7 @@ int smb2_lock(struct ksmbd_work *work) + if (lock_start > U64_MAX - lock_length) { + pr_err("Invalid lock range requested\n"); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; ++ locks_free_lock(flock); + goto out; + } + +@@ -6968,6 +6921,7 @@ int smb2_lock(struct ksmbd_work *work) + "the end offset(%llx) is smaller than the start offset(%llx)\n", + flock->fl_end, flock->fl_start); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; ++ locks_free_lock(flock); + goto out; + } + +@@ -6979,6 +6933,7 @@ int smb2_lock(struct ksmbd_work *work) + flock->fl_type != F_UNLCK) { + pr_err("conflict two locks in one request\n"); + err = -EINVAL; ++ locks_free_lock(flock); + goto out; + } + } +@@ -6987,6 +6942,7 @@ int smb2_lock(struct ksmbd_work *work) + smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list); + if (!smb_lock) { + err = -EINVAL; ++ locks_free_lock(flock); + goto out; + } + } +@@ -7118,10 +7074,6 @@ skip: + + ksmbd_debug(SMB, + "would have to wait for getting lock\n"); +- spin_lock(&work->conn->llist_lock); +- list_add_tail(&smb_lock->clist, +- &work->conn->lock_list); +- spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); + + argv = kmalloc(sizeof(void *), GFP_KERNEL); +@@ -7147,19 +7099,12 @@ skip: + + ksmbd_vfs_posix_lock_wait(flock); + +- spin_lock(&work->conn->request_lock); + spin_lock(&fp->f_lock); + list_del(&work->fp_entry); +- work->cancel_fn = NULL; +- kfree(argv); + spin_unlock(&fp->f_lock); +- spin_unlock(&work->conn->request_lock); + + if (work->state != KSMBD_WORK_ACTIVE) { + list_del(&smb_lock->llist); +- spin_lock(&work->conn->llist_lock); +- list_del(&smb_lock->clist); +- spin_unlock(&work->conn->llist_lock); + locks_free_lock(flock); + + if (work->state == KSMBD_WORK_CANCELLED) { +@@ -7171,8 +7116,7 @@ skip: + work->send_no_response = 1; + goto out; + } +- init_smb2_rsp_hdr(work); +- smb2_set_err_rsp(work); ++ + rsp->hdr.Status = + STATUS_RANGE_NOT_LOCKED; + kfree(smb_lock); +@@ -7180,19 +7124,16 @@ skip: + } + + list_del(&smb_lock->llist); +- spin_lock(&work->conn->llist_lock); +- list_del(&smb_lock->clist); +- spin_unlock(&work->conn->llist_lock); +- ++ release_async_work(work); + goto retry; + } else if (!rc) { ++ list_add(&smb_lock->llist, &rollback_list); + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + list_add_tail(&smb_lock->flist, + &fp->lock_list); + spin_unlock(&work->conn->llist_lock); +- list_add(&smb_lock->llist, &rollback_list); + ksmbd_debug(SMB, "successful in taking lock\n"); + } else { + goto out; +@@ -7207,7 +7148,10 @@ skip: + ksmbd_debug(SMB, "successful in taking lock\n"); + rsp->hdr.Status = STATUS_SUCCESS; + rsp->Reserved = 0; +- inc_rfc1001_len(work->response_buf, 4); ++ err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lock_rsp)); ++ if (err) ++ goto out; ++ + ksmbd_fd_put(work, fp); + return 0; + +@@ -7226,7 +7170,7 @@ out: + rlock->fl_start = smb_lock->start; + rlock->fl_end = smb_lock->end; + +- rc = vfs_lock_file(filp, 0, rlock, NULL); ++ rc = vfs_lock_file(filp, F_SETLK, rlock, NULL); + if (rc) + pr_err("rollback unlock fail : %d\n", rc); + +@@ -7648,7 +7592,8 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, + + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + ret = ksmbd_vfs_set_dos_attrib_xattr(user_ns, +- fp->filp->f_path.dentry, &da); ++ &fp->filp->f_path, ++ &da, true); + if (ret) + fp->f_ci->m_fattr = old_fattr; + } +@@ -8003,9 +7948,9 @@ dup_ext_out: + rsp->Reserved = cpu_to_le16(0); + rsp->Flags = cpu_to_le32(0); + rsp->Reserved2 = cpu_to_le32(0); +- inc_rfc1001_len(work->response_buf, 48 + nbytes); +- +- return 0; ++ ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_ioctl_rsp) + nbytes); ++ if (!ret) ++ return ret; + + out: + if (ret == -EACCES) +@@ -8140,8 +8085,9 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) + rsp->Reserved2 = 0; + rsp->VolatileFid = volatile_id; + rsp->PersistentFid = persistent_id; +- inc_rfc1001_len(work->response_buf, 24); +- return; ++ ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_oplock_break)); ++ if (!ret) ++ return; + + err_out: + opinfo->op_state = OPLOCK_STATE_NONE; +@@ -8273,6 +8219,11 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) + le32_to_cpu(req->LeaseState)); + } + ++ if (ret < 0) { ++ rsp->hdr.Status = err; ++ goto err_out; ++ } ++ + lease_state = lease->state; + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); +@@ -8280,22 +8231,17 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) + wake_up_interruptible_all(&opinfo->oplock_brk); + opinfo_put(opinfo); + +- if (ret < 0) { +- rsp->hdr.Status = err; +- goto err_out; +- } +- + rsp->StructureSize = cpu_to_le16(36); + rsp->Reserved = 0; + rsp->Flags = 0; + memcpy(rsp->LeaseKey, req->LeaseKey, 16); + rsp->LeaseState = lease_state; + rsp->LeaseDuration = 0; +- inc_rfc1001_len(work->response_buf, 36); +- return; ++ ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lease_ack)); ++ if (!ret) ++ return; + + err_out: +- opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + atomic_dec(&opinfo->breaking_cnt); + wake_up_interruptible_all(&opinfo->oplock_brk); +@@ -8430,43 +8376,19 @@ int smb2_check_sign_req(struct ksmbd_work *work) + void smb2_set_sign_rsp(struct ksmbd_work *work) + { + struct smb2_hdr *hdr; +- struct smb2_hdr *req_hdr; + char signature[SMB2_HMACSHA256_SIZE]; +- struct kvec iov[2]; +- size_t len; ++ struct kvec *iov; + int n_vec = 1; + +- hdr = smb2_get_msg(work->response_buf); +- if (work->next_smb2_rsp_hdr_off) +- hdr = ksmbd_resp_buf_next(work); +- +- req_hdr = ksmbd_req_buf_next(work); +- +- if (!work->next_smb2_rsp_hdr_off) { +- len = get_rfc1002_len(work->response_buf); +- if (req_hdr->NextCommand) +- len = ALIGN(len, 8); +- } else { +- len = get_rfc1002_len(work->response_buf) - +- work->next_smb2_rsp_hdr_off; +- len = ALIGN(len, 8); +- } +- +- if (req_hdr->NextCommand) +- hdr->NextCommand = cpu_to_le32(len); +- ++ hdr = ksmbd_resp_buf_curr(work); + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + +- iov[0].iov_base = (char *)&hdr->ProtocolId; +- iov[0].iov_len = len; +- +- if (work->aux_payload_sz) { +- iov[0].iov_len -= work->aux_payload_sz; +- +- iov[1].iov_base = work->aux_payload_buf; +- iov[1].iov_len = work->aux_payload_sz; ++ if (hdr->Command == SMB2_READ) { ++ iov = &work->iov[work->iov_idx - 1]; + n_vec++; ++ } else { ++ iov = &work->iov[work->iov_idx]; + } + + if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, +@@ -8542,29 +8464,14 @@ int smb3_check_sign_req(struct ksmbd_work *work) + void smb3_set_sign_rsp(struct ksmbd_work *work) + { + struct ksmbd_conn *conn = work->conn; +- struct smb2_hdr *req_hdr, *hdr; ++ struct smb2_hdr *hdr; + struct channel *chann; + char signature[SMB2_CMACAES_SIZE]; +- struct kvec iov[2]; ++ struct kvec *iov; + int n_vec = 1; +- size_t len; + char *signing_key; + +- hdr = smb2_get_msg(work->response_buf); +- if (work->next_smb2_rsp_hdr_off) +- hdr = ksmbd_resp_buf_next(work); +- +- req_hdr = ksmbd_req_buf_next(work); +- +- if (!work->next_smb2_rsp_hdr_off) { +- len = get_rfc1002_len(work->response_buf); +- if (req_hdr->NextCommand) +- len = ALIGN(len, 8); +- } else { +- len = get_rfc1002_len(work->response_buf) - +- work->next_smb2_rsp_hdr_off; +- len = ALIGN(len, 8); +- } ++ hdr = ksmbd_resp_buf_curr(work); + + if (conn->binding == false && + le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { +@@ -8580,21 +8487,18 @@ void smb3_set_sign_rsp(struct ksmbd_work *work) + if (!signing_key) + return; + +- if (req_hdr->NextCommand) +- hdr->NextCommand = cpu_to_le32(len); +- + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); +- iov[0].iov_base = (char *)&hdr->ProtocolId; +- iov[0].iov_len = len; +- if (work->aux_payload_sz) { +- iov[0].iov_len -= work->aux_payload_sz; +- iov[1].iov_base = work->aux_payload_buf; +- iov[1].iov_len = work->aux_payload_sz; ++ ++ if (hdr->Command == SMB2_READ) { ++ iov = &work->iov[work->iov_idx - 1]; + n_vec++; ++ } else { ++ iov = &work->iov[work->iov_idx]; + } + +- if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature)) ++ if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, ++ signature)) + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); + } + +@@ -8661,45 +8565,22 @@ static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type) + + int smb3_encrypt_resp(struct ksmbd_work *work) + { +- char *buf = work->response_buf; +- struct kvec iov[3]; ++ struct kvec *iov = work->iov; + int rc = -ENOMEM; +- int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0); +- +- if (ARRAY_SIZE(iov) < rq_nvec) +- return -ENOMEM; ++ void *tr_buf; + +- work->tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); +- if (!work->tr_buf) ++ tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); ++ if (!tr_buf) + return rc; + + /* fill transform header */ +- fill_transform_hdr(work->tr_buf, buf, work->conn->cipher_type); ++ fill_transform_hdr(tr_buf, work->response_buf, work->conn->cipher_type); + +- iov[0].iov_base = work->tr_buf; ++ iov[0].iov_base = tr_buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; +- buf_size += iov[0].iov_len - 4; +- +- iov[1].iov_base = buf + 4; +- iov[1].iov_len = get_rfc1002_len(buf); +- if (work->aux_payload_sz) { +- iov[1].iov_len = work->resp_hdr_sz - 4; +- +- iov[2].iov_base = work->aux_payload_buf; +- iov[2].iov_len = work->aux_payload_sz; +- buf_size += iov[2].iov_len; +- } +- buf_size += iov[1].iov_len; +- work->resp_hdr_sz = iov[1].iov_len; +- +- rc = ksmbd_crypt_message(work, iov, rq_nvec, 1); +- if (rc) +- return rc; +- +- memmove(buf, iov[1].iov_base, iov[1].iov_len); +- *(__be32 *)work->tr_buf = cpu_to_be32(buf_size); ++ work->tr_buf = tr_buf; + +- return rc; ++ return ksmbd_crypt_message(work, iov, work->iov_idx + 1, 1); + } + + bool smb3_is_transform_hdr(void *buf) +diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h +index 665a837378540..59e3de95961c1 100644 +--- a/fs/smb/server/smb2pdu.h ++++ b/fs/smb/server/smb2pdu.h +@@ -446,7 +446,7 @@ struct smb2_posix_info { + /* SidBuffer contain two sids (UNIX user sid(16), UNIX group sid(16)) */ + u8 SidBuffer[32]; + __le32 name_len; +- u8 name[1]; ++ u8 name[]; + /* + * var sized owner SID + * var sized group SID +@@ -488,6 +488,7 @@ int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects, + struct file_lock *smb_flock_init(struct file *f); + int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), + void **arg); ++void release_async_work(struct ksmbd_work *work); + void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); + struct channel *lookup_chann_list(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c +index adc41b57b84c6..d160363c09ebc 100644 +--- a/fs/smb/server/smb_common.c ++++ b/fs/smb/server/smb_common.c +@@ -266,7 +266,7 @@ static int ksmbd_negotiate_smb_dialect(void *buf) + if (smb2_neg_size > smb_buf_length) + goto err_out; + +- if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > ++ if (struct_size(req, Dialects, le16_to_cpu(req->DialectCount)) > + smb_buf_length) + goto err_out; + +@@ -319,12 +319,6 @@ static int init_smb1_rsp_hdr(struct ksmbd_work *work) + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf; + +- /* +- * Remove 4 byte direct TCP header. +- */ +- *(__be32 *)work->response_buf = +- cpu_to_be32(sizeof(struct smb_hdr) - 4); +- + rsp_hdr->Command = SMB_COM_NEGOTIATE; + *(__le32 *)rsp_hdr->Protocol = SMB1_PROTO_NUMBER; + rsp_hdr->Flags = SMBFLG_RESPONSE; +@@ -359,8 +353,8 @@ static int smb1_check_user_session(struct ksmbd_work *work) + */ + static int smb1_allocate_rsp_buf(struct ksmbd_work *work) + { +- work->response_buf = kmalloc(MAX_CIFS_SMALL_BUFFER_SIZE, +- GFP_KERNEL | __GFP_ZERO); ++ work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, ++ GFP_KERNEL); + work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + + if (!work->response_buf) { +@@ -571,10 +565,11 @@ static int smb_handle_negotiate(struct ksmbd_work *work) + + ksmbd_debug(SMB, "Unsupported SMB1 protocol\n"); + +- /* Add 2 byte bcc and 2 byte DialectIndex. */ +- inc_rfc1001_len(work->response_buf, 4); +- neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS; ++ if (ksmbd_iov_pin_rsp(work, (void *)neg_rsp, ++ sizeof(struct smb_negotiate_rsp) - 4)) ++ return -ENOMEM; + ++ neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS; + neg_rsp->hdr.WordCount = 1; + neg_rsp->DialectIndex = cpu_to_le16(work->conn->dialect); + neg_rsp->ByteCount = 0; +diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h +index 1cbb492cdefec..f1092519c0c28 100644 +--- a/fs/smb/server/smb_common.h ++++ b/fs/smb/server/smb_common.h +@@ -200,7 +200,7 @@ struct smb_hdr { + struct smb_negotiate_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; +- unsigned char DialectsArray[1]; ++ unsigned char DialectsArray[]; + } __packed; + + struct smb_negotiate_rsp { +@@ -263,14 +263,14 @@ struct file_directory_info { + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; +- char FileName[1]; ++ char FileName[]; + } __packed; /* level 0x101 FF resp data */ + + struct file_names_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le32 FileNameLength; +- char FileName[1]; ++ char FileName[]; + } __packed; /* level 0xc FF resp data */ + + struct file_full_directory_info { +@@ -285,7 +285,7 @@ struct file_full_directory_info { + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; +- char FileName[1]; ++ char FileName[]; + } __packed; /* level 0x102 FF resp */ + + struct file_both_directory_info { +@@ -303,7 +303,7 @@ struct file_both_directory_info { + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; +- char FileName[1]; ++ char FileName[]; + } __packed; /* level 0x104 FFrsp data */ + + struct file_id_both_directory_info { +@@ -323,7 +323,7 @@ struct file_id_both_directory_info { + __u8 ShortName[24]; + __le16 Reserved2; + __le64 UniqueId; +- char FileName[1]; ++ char FileName[]; + } __packed; + + struct file_id_full_dir_info { +@@ -340,7 +340,7 @@ struct file_id_full_dir_info { + __le32 EaSize; /* EA size */ + __le32 Reserved; + __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ +- char FileName[1]; ++ char FileName[]; + } __packed; /* level 0x105 FF rsp data */ + + struct smb_version_values { +diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c +index c24df86eb112b..d9bbd2eb89c35 100644 +--- a/fs/smb/server/smbacl.c ++++ b/fs/smb/server/smbacl.c +@@ -97,7 +97,7 @@ int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) + /* compare all of the subauth values if any */ + num_sat = ctsid->num_subauth; + num_saw = cwsid->num_subauth; +- num_subauth = num_sat < num_saw ? num_sat : num_saw; ++ num_subauth = min(num_sat, num_saw); + if (num_subauth) { + for (i = 0; i < num_subauth; ++i) { + if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { +@@ -1185,8 +1185,7 @@ pass: + pntsd_size += sizeof(struct smb_acl) + nt_size; + } + +- ksmbd_vfs_set_sd_xattr(conn, user_ns, +- path->dentry, pntsd, pntsd_size); ++ ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, pntsd_size, false); + kfree(pntsd); + } + +@@ -1313,7 +1312,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); +- if (posix_acls && !found) { ++ if (!IS_ERR_OR_NULL(posix_acls) && !found) { + unsigned int id = -1; + + pa_entry = posix_acls->a_entries; +@@ -1337,7 +1336,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + } + } + } +- if (posix_acls) ++ if (!IS_ERR_OR_NULL(posix_acls)) + posix_acl_release(posix_acls); + } + +@@ -1378,7 +1377,7 @@ err_out: + + int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, +- bool type_check) ++ bool type_check, bool get_write) + { + int rc; + struct smb_fattr fattr = {{0}}; +@@ -1406,7 +1405,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + newattrs.ia_valid |= ATTR_MODE; + newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777); + +- ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry); ++ ksmbd_vfs_remove_acl_xattrs(user_ns, path); + /* Update posix acls */ + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) { + rc = set_posix_acl(user_ns, inode, +@@ -1437,15 +1436,14 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + + if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { + /* Update WinACL in xattr */ +- ksmbd_vfs_remove_sd_xattrs(user_ns, path->dentry); +- ksmbd_vfs_set_sd_xattr(conn, user_ns, +- path->dentry, pntsd, ntsd_len); ++ ksmbd_vfs_remove_sd_xattrs(user_ns, path); ++ ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, ntsd_len, ++ get_write); + } + + out: + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); +- mark_inode_dirty(inode); + return rc; + } + +diff --git a/fs/smb/server/smbacl.h b/fs/smb/server/smbacl.h +index 618f2e0236b31..9651a25518881 100644 +--- a/fs/smb/server/smbacl.h ++++ b/fs/smb/server/smbacl.h +@@ -207,7 +207,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + __le32 *pdaccess, int uid); + int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, +- bool type_check); ++ bool type_check, bool get_write); + void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); + void ksmbd_init_domain(u32 *sub_auth); + +diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c +index 40c721f9227e4..b49d47bdafc94 100644 +--- a/fs/smb/server/transport_ipc.c ++++ b/fs/smb/server/transport_ipc.c +@@ -229,7 +229,7 @@ static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) + struct ksmbd_ipc_msg *msg; + size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); + +- msg = kvmalloc(msg_sz, GFP_KERNEL | __GFP_ZERO); ++ msg = kvzalloc(msg_sz, GFP_KERNEL); + if (msg) + msg->sz = sz; + return msg; +@@ -268,7 +268,7 @@ static int handle_response(int type, void *payload, size_t sz) + entry->type + 1, type); + } + +- entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); ++ entry->response = kvzalloc(sz, GFP_KERNEL); + if (!entry->response) { + ret = -ENOMEM; + break; +diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c +index 7578200f63b1d..c5629a68c8b73 100644 +--- a/fs/smb/server/transport_rdma.c ++++ b/fs/smb/server/transport_rdma.c +@@ -1241,14 +1241,12 @@ static int smb_direct_writev(struct ksmbd_transport *t, + + //FIXME: skip RFC1002 header.. + buflen -= 4; +- iov[0].iov_base += 4; +- iov[0].iov_len -= 4; + + remaining_data_length = buflen; + ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen); + + smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key); +- start = i = 0; ++ start = i = 1; + buflen = 0; + while (true) { + buflen += iov[i].iov_len; +@@ -2142,8 +2140,7 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev) + if (ib_dev->node_type != RDMA_NODE_IB_CA) + smb_direct_port = SMB_DIRECT_PORT_IWARP; + +- if (!ib_dev->ops.get_netdev || +- !rdma_frwr_is_supported(&ib_dev->attrs)) ++ if (!rdma_frwr_is_supported(&ib_dev->attrs)) + return 0; + + smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL); +@@ -2243,17 +2240,38 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev) + for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) { + struct net_device *ndev; + +- ndev = smb_dev->ib_dev->ops.get_netdev(smb_dev->ib_dev, +- i + 1); +- if (!ndev) +- continue; ++ if (smb_dev->ib_dev->ops.get_netdev) { ++ ndev = smb_dev->ib_dev->ops.get_netdev( ++ smb_dev->ib_dev, i + 1); ++ if (!ndev) ++ continue; + +- if (ndev == netdev) { ++ if (ndev == netdev) { ++ dev_put(ndev); ++ rdma_capable = true; ++ goto out; ++ } + dev_put(ndev); +- rdma_capable = true; +- goto out; ++ /* if ib_dev does not implement ops.get_netdev ++ * check for matching infiniband GUID in hw_addr ++ */ ++ } else if (netdev->type == ARPHRD_INFINIBAND) { ++ struct netdev_hw_addr *ha; ++ union ib_gid gid; ++ u32 port_num; ++ int ret; ++ ++ netdev_hw_addr_list_for_each( ++ ha, &netdev->dev_addrs) { ++ memcpy(&gid, ha->addr + 4, sizeof(gid)); ++ ret = ib_find_gid(smb_dev->ib_dev, &gid, ++ &port_num, NULL); ++ if (!ret) { ++ rdma_capable = true; ++ goto out; ++ } ++ } + } +- dev_put(ndev); + } + } + out: +diff --git a/fs/smb/server/unicode.c b/fs/smb/server/unicode.c +index a0db699ddafda..33fc6d45c0f38 100644 +--- a/fs/smb/server/unicode.c ++++ b/fs/smb/server/unicode.c +@@ -14,46 +14,10 @@ + #include "uniupr.h" + #include "smb_common.h" + +-/* +- * smb_utf16_bytes() - how long will a string be after conversion? +- * @from: pointer to input string +- * @maxbytes: don't go past this many bytes of input string +- * @codepage: destination codepage +- * +- * Walk a utf16le string and return the number of bytes that the string will +- * be after being converted to the given charset, not including any null +- * termination required. Don't walk past maxbytes in the source buffer. +- * +- * Return: string length after conversion +- */ +-static int smb_utf16_bytes(const __le16 *from, int maxbytes, +- const struct nls_table *codepage) +-{ +- int i; +- int charlen, outlen = 0; +- int maxwords = maxbytes / 2; +- char tmp[NLS_MAX_CHARSET_SIZE]; +- __u16 ftmp; +- +- for (i = 0; i < maxwords; i++) { +- ftmp = get_unaligned_le16(&from[i]); +- if (ftmp == 0) +- break; +- +- charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE); +- if (charlen > 0) +- outlen += charlen; +- else +- outlen++; +- } +- +- return outlen; +-} +- + /* + * cifs_mapchar() - convert a host-endian char to proper char in codepage + * @target: where converted character should be copied +- * @src_char: 2 byte host-endian source character ++ * @from: host-endian source string + * @cp: codepage to which character should be converted + * @mapchar: should character be mapped according to mapchars mount option? + * +@@ -64,10 +28,13 @@ static int smb_utf16_bytes(const __le16 *from, int maxbytes, + * Return: string length after conversion + */ + static int +-cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, ++cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp, + bool mapchar) + { + int len = 1; ++ __u16 src_char; ++ ++ src_char = *from; + + if (!mapchar) + goto cp_convert; +@@ -105,30 +72,66 @@ out: + + cp_convert: + len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); +- if (len <= 0) { +- *target = '?'; +- len = 1; +- } ++ if (len <= 0) ++ goto surrogate_pair; ++ ++ goto out; ++ ++surrogate_pair: ++ /* convert SURROGATE_PAIR and IVS */ ++ if (strcmp(cp->charset, "utf8")) ++ goto unknown; ++ len = utf16s_to_utf8s(from, 3, UTF16_LITTLE_ENDIAN, target, 6); ++ if (len <= 0) ++ goto unknown; ++ return len; + ++unknown: ++ *target = '?'; ++ len = 1; + goto out; + } + + /* +- * is_char_allowed() - check for valid character +- * @ch: input character to be checked ++ * smb_utf16_bytes() - compute converted string length ++ * @from: pointer to input string ++ * @maxbytes: input string length ++ * @codepage: destination codepage ++ * ++ * Walk a utf16le string and return the number of bytes that the string will ++ * be after being converted to the given charset, not including any null ++ * termination required. Don't walk past maxbytes in the source buffer. + * +- * Return: 1 if char is allowed, otherwise 0 ++ * Return: string length after conversion + */ +-static inline int is_char_allowed(char *ch) ++static int smb_utf16_bytes(const __le16 *from, int maxbytes, ++ const struct nls_table *codepage) + { +- /* check for control chars, wildcards etc. */ +- if (!(*ch & 0x80) && +- (*ch <= 0x1f || +- *ch == '?' || *ch == '"' || *ch == '<' || +- *ch == '>' || *ch == '|')) +- return 0; +- +- return 1; ++ int i, j; ++ int charlen, outlen = 0; ++ int maxwords = maxbytes / 2; ++ char tmp[NLS_MAX_CHARSET_SIZE]; ++ __u16 ftmp[3]; ++ ++ for (i = 0; i < maxwords; i++) { ++ ftmp[0] = get_unaligned_le16(&from[i]); ++ if (ftmp[0] == 0) ++ break; ++ for (j = 1; j <= 2; j++) { ++ if (i + j < maxwords) ++ ftmp[j] = get_unaligned_le16(&from[i + j]); ++ else ++ ftmp[j] = 0; ++ } ++ ++ charlen = cifs_mapchar(tmp, ftmp, codepage, 0); ++ if (charlen > 0) ++ outlen += charlen; ++ else ++ outlen++; ++ } ++ ++ return outlen; + } + + /* +@@ -158,12 +161,12 @@ static inline int is_char_allowed(char *ch) + static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, bool mapchar) + { +- int i, charlen, safelen; ++ int i, j, charlen, safelen; + int outlen = 0; + int nullsize = nls_nullsize(codepage); + int fromwords = fromlen / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; +- __u16 ftmp; ++ __u16 ftmp[3]; /* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */ + + /* + * because the chars can be of varying widths, we need to take care +@@ -174,9 +177,15 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); + + for (i = 0; i < fromwords; i++) { +- ftmp = get_unaligned_le16(&from[i]); +- if (ftmp == 0) ++ ftmp[0] = get_unaligned_le16(&from[i]); ++ if (ftmp[0] == 0) + break; ++ for (j = 1; j <= 2; j++) { ++ if (i + j < fromwords) ++ ftmp[j] = get_unaligned_le16(&from[i + j]); ++ else ++ ftmp[j] = 0; ++ } + + /* + * check to see if converting this character might make the +@@ -191,6 +200,19 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + /* put converted char into 'to' buffer */ + charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); + outlen += charlen; ++ ++ /* ++ * charlen (=bytes of UTF-8 for 1 character) ++ * 4bytes UTF-8(surrogate pair) is charlen=4 ++ * (4bytes UTF-16 code) ++ * 7-8bytes UTF-8(IVS) is charlen=3+4 or 4+4 ++ * (2 UTF-8 pairs divided to 2 UTF-16 pairs) ++ */ ++ if (charlen == 4) ++ i++; ++ else if (charlen >= 5) ++ /* 5-6bytes UTF-8 */ ++ i += 2; + } + + /* properly null-terminate string */ +@@ -325,6 +347,9 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + char src_char; + __le16 dst_char; + wchar_t tmp; ++ wchar_t wchar_to[6]; /* UTF-16 */ ++ int ret; ++ unicode_t u; + + if (!mapchars) + return smb_strtoUTF16(target, source, srclen, cp); +@@ -367,11 +392,57 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + * if no match, use question mark, which at least in + * some cases serves as wild card + */ +- if (charlen < 1) { +- dst_char = cpu_to_le16(0x003f); +- charlen = 1; ++ if (charlen > 0) ++ goto ctoUTF16; ++ ++ /* convert SURROGATE_PAIR */ ++ if (strcmp(cp->charset, "utf8")) ++ goto unknown; ++ if (*(source + i) & 0x80) { ++ charlen = utf8_to_utf32(source + i, 6, &u); ++ if (charlen < 0) ++ goto unknown; ++ } else ++ goto unknown; ++ ret = utf8s_to_utf16s(source + i, charlen, ++ UTF16_LITTLE_ENDIAN, ++ wchar_to, 6); ++ if (ret < 0) ++ goto unknown; ++ ++ i += charlen; ++ dst_char = cpu_to_le16(*wchar_to); ++ if (charlen <= 3) ++ /* 1-3bytes UTF-8 to 2bytes UTF-16 */ ++ put_unaligned(dst_char, &target[j]); ++ else if (charlen == 4) { ++ /* ++ * 4bytes UTF-8(surrogate pair) to 4bytes UTF-16 ++ * 7-8bytes UTF-8(IVS) divided to 2 UTF-16 ++ * (charlen=3+4 or 4+4) ++ */ ++ put_unaligned(dst_char, &target[j]); ++ dst_char = cpu_to_le16(*(wchar_to + 1)); ++ j++; ++ put_unaligned(dst_char, &target[j]); ++ } else if (charlen >= 5) { ++ /* 5-6bytes UTF-8 to 6bytes UTF-16 */ ++ put_unaligned(dst_char, &target[j]); ++ dst_char = cpu_to_le16(*(wchar_to + 1)); ++ j++; ++ put_unaligned(dst_char, &target[j]); ++ dst_char = cpu_to_le16(*(wchar_to + 2)); ++ j++; ++ put_unaligned(dst_char, &target[j]); + } ++ continue; ++ ++unknown: ++ dst_char = cpu_to_le16(0x003f); ++ charlen = 1; + } ++ ++ctoUTF16: + /* + * character may take more than one byte in the source string, + * but will take exactly two bytes in the target string +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index 36914db8b6616..fe2c80ea2e47e 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + + #include "../../internal.h" /* for vfs_path_lookup */ + +@@ -36,19 +37,6 @@ + #include "mgmt/user_session.h" + #include "mgmt/user_config.h" + +-static char *extract_last_component(char *path) +-{ +- char *p = strrchr(path, '/'); +- +- if (p && p[1] != '\0') { +- *p = '\0'; +- p++; +- } else { +- p = NULL; +- } +- return p; +-} +- + static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, + struct inode *parent_inode, + struct inode *inode) +@@ -62,67 +50,96 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, + + /** + * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable +- * +- * the parent dentry got by dget_parent or @parent could be +- * unstable, we try to lock a parent inode and lookup the +- * child dentry again. +- * +- * the reference count of @parent isn't incremented. + */ +-int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, +- struct dentry *child) ++int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child) + { +- struct dentry *dentry; +- int ret = 0; +- + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); +- dentry = lookup_one(user_ns, child->d_name.name, parent, +- child->d_name.len); +- if (IS_ERR(dentry)) { +- ret = PTR_ERR(dentry); +- goto out_err; +- } +- +- if (dentry != child) { +- ret = -ESTALE; +- dput(dentry); +- goto out_err; ++ if (child->d_parent != parent) { ++ inode_unlock(d_inode(parent)); ++ return -ENOENT; + } + +- dput(dentry); + return 0; +-out_err: +- inode_unlock(d_inode(parent)); +- return ret; + } + +-int ksmbd_vfs_may_delete(struct user_namespace *user_ns, +- struct dentry *dentry) ++static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, ++ char *pathname, unsigned int flags, ++ struct path *parent_path, ++ struct path *path) + { +- struct dentry *parent; +- int ret; ++ struct qstr last; ++ struct filename *filename; ++ struct path *root_share_path = &share_conf->vfs_path; ++ int err, type; ++ struct dentry *d; ++ ++ if (pathname[0] == '\0') { ++ pathname = share_conf->path; ++ root_share_path = NULL; ++ } else { ++ flags |= LOOKUP_BENEATH; ++ } ++ ++ filename = getname_kernel(pathname); ++ if (IS_ERR(filename)) ++ return PTR_ERR(filename); + +- parent = dget_parent(dentry); +- ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); +- if (ret) { +- dput(parent); +- return ret; ++ err = vfs_path_parent_lookup(filename, flags, ++ parent_path, &last, &type, ++ root_share_path); ++ if (err) { ++ putname(filename); ++ return err; + } + +- ret = inode_permission(user_ns, d_inode(parent), +- MAY_EXEC | MAY_WRITE); ++ if (unlikely(type != LAST_NORM)) { ++ path_put(parent_path); ++ putname(filename); ++ return -ENOENT; ++ } + +- inode_unlock(d_inode(parent)); +- dput(parent); +- return ret; ++ err = mnt_want_write(parent_path->mnt); ++ if (err) { ++ path_put(parent_path); ++ putname(filename); ++ return -ENOENT; ++ } ++ ++ inode_lock_nested(parent_path->dentry->d_inode, I_MUTEX_PARENT); ++ d = lookup_one_qstr_excl(&last, parent_path->dentry, 0); ++ if (IS_ERR(d)) ++ goto err_out; ++ ++ if (d_is_negative(d)) { ++ dput(d); ++ goto err_out; ++ } ++ ++ path->dentry = d; ++ path->mnt = mntget(parent_path->mnt); ++ ++ if (test_share_config_flag(share_conf, KSMBD_SHARE_FLAG_CROSSMNT)) { ++ err = follow_down(path); ++ if (err < 0) { ++ path_put(path); ++ goto err_out; ++ } ++ } ++ ++ putname(filename); ++ return 0; ++ ++err_out: ++ inode_unlock(d_inode(parent_path->dentry)); ++ mnt_drop_write(parent_path->mnt); ++ path_put(parent_path); ++ putname(filename); ++ return -ENOENT; + } + +-int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, ++void ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess) + { +- struct dentry *parent; +- int ret = 0; +- + *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); + + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE)) +@@ -137,19 +154,8 @@ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC)) + *daccess |= FILE_EXECUTE_LE; + +- parent = dget_parent(dentry); +- ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); +- if (ret) { +- dput(parent); +- return ret; +- } +- +- if (!inode_permission(user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) ++ if (!inode_permission(user_ns, d_inode(dentry->d_parent), MAY_EXEC | MAY_WRITE)) + *daccess |= FILE_DELETE_LE; +- +- inode_unlock(d_inode(parent)); +- dput(parent); +- return ret; + } + + /** +@@ -185,6 +191,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) + } else { + pr_err("File(%s): creation failed (err:%d)\n", name, err); + } ++ + done_path_create(&path, dentry); + return err; + } +@@ -218,27 +225,26 @@ int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) + user_ns = mnt_user_ns(path.mnt); + mode |= S_IFDIR; + err = vfs_mkdir(user_ns, d_inode(path.dentry), dentry, mode); +- if (err) { +- goto out; +- } else if (d_unhashed(dentry)) { ++ if (!err && d_unhashed(dentry)) { + struct dentry *d; + + d = lookup_one(user_ns, dentry->d_name.name, dentry->d_parent, + dentry->d_name.len); + if (IS_ERR(d)) { + err = PTR_ERR(d); +- goto out; ++ goto out_err; + } + if (unlikely(d_is_negative(d))) { + dput(d); + err = -ENOENT; +- goto out; ++ goto out_err; + } + + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); + dput(d); + } +-out: ++ ++out_err: + done_path_create(&path, dentry); + if (err) + pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); +@@ -358,15 +364,15 @@ out: + * @fid: file id of open file + * @count: read byte count + * @pos: file pos ++ * @rbuf: read data buffer + * + * Return: number of read bytes on success, otherwise error + */ + int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, +- loff_t *pos) ++ loff_t *pos, char *rbuf) + { + struct file *filp = fp->filp; + ssize_t nbytes = 0; +- char *rbuf = work->aux_payload_buf; + struct inode *inode = file_inode(filp); + + if (S_ISDIR(inode->i_mode)) +@@ -410,7 +416,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + { + char *stream_buf = NULL, *wbuf; + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); +- size_t size, v_len; ++ size_t size; ++ ssize_t v_len; + int err = 0; + + ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n", +@@ -427,14 +434,14 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + fp->stream.name, + fp->stream.size, + &stream_buf); +- if ((int)v_len < 0) { ++ if (v_len < 0) { + pr_err("not found stream in xattr : %zd\n", v_len); +- err = (int)v_len; ++ err = v_len; + goto out; + } + + if (v_len < size) { +- wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); ++ wbuf = kvzalloc(size, GFP_KERNEL); + if (!wbuf) { + err = -ENOMEM; + goto out; +@@ -449,11 +456,12 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + memcpy(&stream_buf[*pos], buf, count); + + err = ksmbd_vfs_setxattr(user_ns, +- fp->filp->f_path.dentry, ++ &fp->filp->f_path, + fp->stream.name, + (void *)stream_buf, + size, +- 0); ++ 0, ++ true); + if (err < 0) + goto out; + +@@ -510,6 +518,9 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + } + } + ++ /* Reserve lease break for parent dir at closing time */ ++ fp->reserve_lease_break = true; ++ + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); + +@@ -581,54 +592,32 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) + * + * Return: 0 on success, otherwise error + */ +-int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) ++int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path) + { + struct user_namespace *user_ns; +- struct path path; +- struct dentry *parent; ++ struct dentry *parent = path->dentry->d_parent; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + +- err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false); +- if (err) { +- ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); +- ksmbd_revert_fsids(work); +- return err; +- } +- +- user_ns = mnt_user_ns(path.mnt); +- parent = dget_parent(path.dentry); +- err = ksmbd_vfs_lock_parent(user_ns, parent, path.dentry); +- if (err) { +- dput(parent); +- path_put(&path); +- ksmbd_revert_fsids(work); +- return err; +- } +- +- if (!d_inode(path.dentry)->i_nlink) { ++ if (!d_inode(path->dentry)->i_nlink) { + err = -ENOENT; + goto out_err; + } + +- if (S_ISDIR(d_inode(path.dentry)->i_mode)) { +- err = vfs_rmdir(user_ns, d_inode(parent), path.dentry); ++ user_ns = mnt_user_ns(path->mnt); ++ if (S_ISDIR(d_inode(path->dentry)->i_mode)) { ++ err = vfs_rmdir(user_ns, d_inode(parent), path->dentry); + if (err && err != -ENOTEMPTY) +- ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, +- err); ++ ksmbd_debug(VFS, "rmdir failed, err %d\n", err); + } else { +- err = vfs_unlink(user_ns, d_inode(parent), path.dentry, NULL); ++ err = vfs_unlink(user_ns, d_inode(parent), path->dentry, NULL); + if (err) +- ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, +- err); ++ ksmbd_debug(VFS, "unlink failed, err %d\n", err); + } + + out_err: +- inode_unlock(d_inode(parent)); +- dput(parent); +- path_put(&path); + ksmbd_revert_fsids(work); + return err; + } +@@ -687,149 +676,120 @@ out1: + return err; + } + +-static int ksmbd_validate_entry_in_use(struct dentry *src_dent) ++int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, ++ char *newname, int flags) + { +- struct dentry *dst_dent; ++ struct dentry *old_parent, *new_dentry, *trap; ++ struct dentry *old_child = old_path->dentry; ++ struct path new_path; ++ struct qstr new_last; ++ struct renamedata rd; ++ struct filename *to; ++ struct ksmbd_share_config *share_conf = work->tcon->share_conf; ++ struct ksmbd_file *parent_fp; ++ int new_type; ++ int err, lookup_flags = LOOKUP_NO_SYMLINKS; ++ ++ if (ksmbd_override_fsids(work)) ++ return -ENOMEM; + +- spin_lock(&src_dent->d_lock); +- list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { +- struct ksmbd_file *child_fp; ++ to = getname_kernel(newname); ++ if (IS_ERR(to)) { ++ err = PTR_ERR(to); ++ goto revert_fsids; ++ } + +- if (d_really_is_negative(dst_dent)) +- continue; ++retry: ++ err = vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH, ++ &new_path, &new_last, &new_type, ++ &share_conf->vfs_path); ++ if (err) ++ goto out1; + +- child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); +- if (child_fp) { +- spin_unlock(&src_dent->d_lock); +- ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); +- return -EACCES; +- } ++ if (old_path->mnt != new_path.mnt) { ++ err = -EXDEV; ++ goto out2; + } +- spin_unlock(&src_dent->d_lock); + +- return 0; +-} ++ err = mnt_want_write(old_path->mnt); ++ if (err) ++ goto out2; + +-static int __ksmbd_vfs_rename(struct ksmbd_work *work, +- struct user_namespace *src_user_ns, +- struct dentry *src_dent_parent, +- struct dentry *src_dent, +- struct user_namespace *dst_user_ns, +- struct dentry *dst_dent_parent, +- struct dentry *trap_dent, +- char *dst_name) +-{ +- struct dentry *dst_dent; +- int err; ++ trap = lock_rename_child(old_child, new_path.dentry); + +- if (!work->tcon->posix_extensions) { +- err = ksmbd_validate_entry_in_use(src_dent); +- if (err) +- return err; ++ old_parent = dget(old_child->d_parent); ++ if (d_unhashed(old_child)) { ++ err = -EINVAL; ++ goto out3; + } + +- if (d_really_is_negative(src_dent_parent)) +- return -ENOENT; +- if (d_really_is_negative(dst_dent_parent)) +- return -ENOENT; +- if (d_really_is_negative(src_dent)) +- return -ENOENT; +- if (src_dent == trap_dent) +- return -EINVAL; +- +- if (ksmbd_override_fsids(work)) +- return -ENOMEM; ++ parent_fp = ksmbd_lookup_fd_inode(old_child->d_parent); ++ if (parent_fp) { ++ if (parent_fp->daccess & FILE_DELETE_LE) { ++ pr_err("parent dir is opened with delete access\n"); ++ err = -ESHARE; ++ ksmbd_fd_put(work, parent_fp); ++ goto out3; ++ } ++ ksmbd_fd_put(work, parent_fp); ++ } + +- dst_dent = lookup_one(dst_user_ns, dst_name, dst_dent_parent, +- strlen(dst_name)); +- err = PTR_ERR(dst_dent); +- if (IS_ERR(dst_dent)) { +- pr_err("lookup failed %s [%d]\n", dst_name, err); +- goto out; ++ new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry, ++ lookup_flags | LOOKUP_RENAME_TARGET); ++ if (IS_ERR(new_dentry)) { ++ err = PTR_ERR(new_dentry); ++ goto out3; + } + +- err = -ENOTEMPTY; +- if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) { +- struct renamedata rd = { +- .old_mnt_userns = src_user_ns, +- .old_dir = d_inode(src_dent_parent), +- .old_dentry = src_dent, +- .new_mnt_userns = dst_user_ns, +- .new_dir = d_inode(dst_dent_parent), +- .new_dentry = dst_dent, +- }; +- err = vfs_rename(&rd); ++ if (d_is_symlink(new_dentry)) { ++ err = -EACCES; ++ goto out4; + } +- if (err) +- pr_err("vfs_rename failed err %d\n", err); +- if (dst_dent) +- dput(dst_dent); +-out: +- ksmbd_revert_fsids(work); +- return err; +-} + +-int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, +- char *newname) +-{ +- struct user_namespace *user_ns; +- struct path dst_path; +- struct dentry *src_dent_parent, *dst_dent_parent; +- struct dentry *src_dent, *trap_dent, *src_child; +- char *dst_name; +- int err; ++ if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) { ++ err = -EEXIST; ++ goto out4; ++ } + +- dst_name = extract_last_component(newname); +- if (!dst_name) { +- dst_name = newname; +- newname = ""; ++ if (old_child == trap) { ++ err = -EINVAL; ++ goto out4; + } + +- src_dent_parent = dget_parent(fp->filp->f_path.dentry); +- src_dent = fp->filp->f_path.dentry; ++ if (new_dentry == trap) { ++ err = -ENOTEMPTY; ++ goto out4; ++ } ++ ++ rd.old_mnt_userns = mnt_user_ns(old_path->mnt), ++ rd.old_dir = d_inode(old_parent), ++ rd.old_dentry = old_child, ++ rd.new_mnt_userns = mnt_user_ns(new_path.mnt), ++ rd.new_dir = new_path.dentry->d_inode, ++ rd.new_dentry = new_dentry, ++ rd.flags = flags, ++ rd.delegated_inode = NULL, ++ err = vfs_rename(&rd); ++ if (err) ++ ksmbd_debug(VFS, "vfs_rename failed err %d\n", err); + +- err = ksmbd_vfs_kern_path(work, newname, +- LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, +- &dst_path, false); +- if (err) { +- ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); +- goto out; ++out4: ++ dput(new_dentry); ++out3: ++ dput(old_parent); ++ unlock_rename(old_parent, new_path.dentry); ++ mnt_drop_write(old_path->mnt); ++out2: ++ path_put(&new_path); ++ ++ if (retry_estale(err, lookup_flags)) { ++ lookup_flags |= LOOKUP_REVAL; ++ goto retry; + } +- dst_dent_parent = dst_path.dentry; +- +- trap_dent = lock_rename(src_dent_parent, dst_dent_parent); +- dget(src_dent); +- dget(dst_dent_parent); +- user_ns = file_mnt_user_ns(fp->filp); +- src_child = lookup_one(user_ns, src_dent->d_name.name, src_dent_parent, +- src_dent->d_name.len); +- if (IS_ERR(src_child)) { +- err = PTR_ERR(src_child); +- goto out_lock; +- } +- +- if (src_child != src_dent) { +- err = -ESTALE; +- dput(src_child); +- goto out_lock; +- } +- dput(src_child); +- +- err = __ksmbd_vfs_rename(work, +- user_ns, +- src_dent_parent, +- src_dent, +- mnt_user_ns(dst_path.mnt), +- dst_dent_parent, +- trap_dent, +- dst_name); +-out_lock: +- dput(src_dent); +- dput(dst_dent_parent); +- unlock_rename(src_dent_parent, dst_dent_parent); +- path_put(&dst_path); +-out: +- dput(src_dent_parent); ++out1: ++ putname(to); ++revert_fsids: ++ ksmbd_revert_fsids(work); + return err; + } + +@@ -892,7 +852,7 @@ ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) + if (size <= 0) + return size; + +- vlist = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); ++ vlist = kvzalloc(size, GFP_KERNEL); + if (!vlist) + return -ENOMEM; + +@@ -950,28 +910,38 @@ ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + /** + * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value + * @user_ns: user namespace +- * @dentry: dentry to set XATTR at +- * @name: xattr name for setxattr +- * @value: xattr value to set +- * @size: size of xattr value ++ * @path: path of dentry to set XATTR at ++ * @attr_name: xattr name for setxattr ++ * @attr_value: xattr value to set ++ * @attr_size: size of xattr value + * @flags: destination buffer length ++ * @get_write: get write access to a mount + * + * Return: 0 on success, otherwise error + */ + int ksmbd_vfs_setxattr(struct user_namespace *user_ns, +- struct dentry *dentry, const char *attr_name, +- void *attr_value, size_t attr_size, int flags) ++ const struct path *path, const char *attr_name, ++ void *attr_value, size_t attr_size, int flags, ++ bool get_write) + { + int err; + ++ if (get_write == true) { ++ err = mnt_want_write(path->mnt); ++ if (err) ++ return err; ++ } ++ + err = vfs_setxattr(user_ns, +- dentry, ++ path->dentry, + attr_name, + attr_value, + attr_size, + flags); + if (err) + ksmbd_debug(VFS, "setxattr failed, err %d\n", err); ++ if (get_write == true) ++ mnt_drop_write(path->mnt); + return err; + } + +@@ -1075,19 +1045,34 @@ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + } + + int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, +- struct dentry *dentry, char *attr_name) ++ const struct path *path, char *attr_name) + { +- return vfs_removexattr(user_ns, dentry, attr_name); ++ int err; ++ ++ err = mnt_want_write(path->mnt); ++ if (err) ++ return err; ++ ++ err = vfs_removexattr(user_ns, path->dentry, attr_name); ++ mnt_drop_write(path->mnt); ++ ++ return err; + } + +-int ksmbd_vfs_unlink(struct user_namespace *user_ns, +- struct dentry *dir, struct dentry *dentry) ++int ksmbd_vfs_unlink(struct file *filp) + { + int err = 0; ++ struct dentry *dir, *dentry = filp->f_path.dentry; ++ struct user_namespace *user_ns = file_mnt_user_ns(filp); + +- err = ksmbd_vfs_lock_parent(user_ns, dir, dentry); ++ err = mnt_want_write(filp->f_path.mnt); + if (err) + return err; ++ ++ dir = dget_parent(dentry); ++ err = ksmbd_vfs_lock_parent(dir, dentry); ++ if (err) ++ goto out; + dget(dentry); + + if (S_ISDIR(d_inode(dentry)->i_mode)) +@@ -1099,6 +1084,9 @@ int ksmbd_vfs_unlink(struct user_namespace *user_ns, + inode_unlock(d_inode(dir)); + if (err) + ksmbd_debug(VFS, "failed to delete, err %d\n", err); ++out: ++ dput(dir); ++ mnt_drop_write(filp->f_path.mnt); + + return err; + } +@@ -1201,32 +1189,29 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, + } + + /** +- * ksmbd_vfs_kern_path() - lookup a file and get path info +- * @name: file path that is relative to share +- * @flags: lookup flags +- * @path: if lookup succeed, return path info ++ * ksmbd_vfs_kern_path_locked() - lookup a file and get path info ++ * @name: file path that is relative to share ++ * @flags: lookup flags ++ * @parent_path: if lookup succeed, return parent_path info ++ * @path: if lookup succeed, return path info + * @caseless: caseless filename lookup + * + * Return: 0 on success, otherwise error + */ +-int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, +- unsigned int flags, struct path *path, bool caseless) ++int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, ++ unsigned int flags, struct path *parent_path, ++ struct path *path, bool caseless) + { + struct ksmbd_share_config *share_conf = work->tcon->share_conf; + int err; + +- flags |= LOOKUP_BENEATH; +- err = vfs_path_lookup(share_conf->vfs_path.dentry, +- share_conf->vfs_path.mnt, +- name, +- flags, +- path); ++ err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, parent_path, ++ path); + if (!err) + return 0; + + if (caseless) { + char *filepath; +- struct path parent; + size_t path_len, remain_len; + + filepath = kstrdup(name, GFP_KERNEL); +@@ -1236,10 +1221,10 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + path_len = strlen(filepath); + remain_len = path_len; + +- parent = share_conf->vfs_path; +- path_get(&parent); ++ *parent_path = share_conf->vfs_path; ++ path_get(parent_path); + +- while (d_can_lookup(parent.dentry)) { ++ while (d_can_lookup(parent_path->dentry)) { + char *filename = filepath + path_len - remain_len; + char *next = strchrnul(filename, '/'); + size_t filename_len = next - filename; +@@ -1248,12 +1233,11 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + if (filename_len == 0) + break; + +- err = ksmbd_vfs_lookup_in_dir(&parent, filename, ++ err = ksmbd_vfs_lookup_in_dir(parent_path, filename, + filename_len, + work->conn->um); +- path_put(&parent); + if (err) +- goto out; ++ goto out2; + + next[0] = '\0'; + +@@ -1261,26 +1245,50 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + share_conf->vfs_path.mnt, + filepath, + flags, +- &parent); ++ path); + if (err) +- goto out; +- else if (is_last) { +- *path = parent; +- goto out; +- } ++ goto out2; ++ else if (is_last) ++ goto out1; ++ path_put(parent_path); ++ *parent_path = *path; + + next[0] = '/'; + remain_len -= filename_len + 1; + } + +- path_put(&parent); + err = -EINVAL; +-out: ++out2: ++ path_put(parent_path); ++out1: + kfree(filepath); + } ++ ++ if (!err) { ++ err = mnt_want_write(parent_path->mnt); ++ if (err) { ++ path_put(path); ++ path_put(parent_path); ++ return err; ++ } ++ ++ err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry); ++ if (err) { ++ path_put(path); ++ path_put(parent_path); ++ } ++ } + return err; + } + ++void ksmbd_vfs_kern_path_unlock(struct path *parent_path, struct path *path) ++{ ++ inode_unlock(d_inode(parent_path->dentry)); ++ mnt_drop_write(parent_path->mnt); ++ path_put(path); ++ path_put(parent_path); ++} ++ + struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, +@@ -1299,13 +1307,13 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + } + + int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, +- struct dentry *dentry) ++ const struct path *path) + { + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + +- xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); ++ xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { +@@ -1321,25 +1329,25 @@ int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, + sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) || + !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, + sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) { +- err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); ++ err = ksmbd_vfs_remove_xattr(user_ns, path, name); + if (err) + ksmbd_debug(SMB, + "remove acl xattr failed : %s\n", name); + } + } ++ + out: + kvfree(xattr_list); + return err; + } + +-int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, +- struct dentry *dentry) ++int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, const struct path *path) + { + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + +- xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); ++ xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { +@@ -1352,7 +1360,7 @@ int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) { +- err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); ++ err = ksmbd_vfs_remove_xattr(user_ns, path, name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", name); + } +@@ -1376,7 +1384,7 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespac + return NULL; + + posix_acls = get_acl(inode, acl_type); +- if (!posix_acls) ++ if (IS_ERR_OR_NULL(posix_acls)) + return NULL; + + smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + +@@ -1429,13 +1437,15 @@ out: + + int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, +- struct dentry *dentry, +- struct smb_ntsd *pntsd, int len) ++ const struct path *path, ++ struct smb_ntsd *pntsd, int len, ++ bool get_write) + { + int rc; + struct ndr sd_ndr = {0}, acl_ndr = {0}; + struct xattr_ntacl acl = {0}; + struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL; ++ struct dentry *dentry = path->dentry; + struct inode *inode = d_inode(dentry); + + acl.version = 4; +@@ -1487,9 +1497,9 @@ int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + goto out; + } + +- rc = ksmbd_vfs_setxattr(user_ns, dentry, ++ rc = ksmbd_vfs_setxattr(user_ns, path, + XATTR_NAME_SD, sd_ndr.data, +- sd_ndr.offset, 0); ++ sd_ndr.offset, 0, get_write); + if (rc < 0) + pr_err("Failed to store XATTR ntacl :%d\n", rc); + +@@ -1577,8 +1587,9 @@ free_n_data: + } + + int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, +- struct dentry *dentry, +- struct xattr_dos_attrib *da) ++ const struct path *path, ++ struct xattr_dos_attrib *da, ++ bool get_write) + { + struct ndr n; + int err; +@@ -1587,8 +1598,8 @@ int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + if (err) + return err; + +- err = ksmbd_vfs_setxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, +- (void *)n.data, n.offset, 0); ++ err = ksmbd_vfs_setxattr(user_ns, path, XATTR_NAME_DOS_ATTRIBUTE, ++ (void *)n.data, n.offset, 0, get_write); + if (err) + ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); + kfree(n.data); +@@ -1824,10 +1835,11 @@ void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) + } + + int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, +- struct inode *inode) ++ struct path *path) + { + struct posix_acl_state acl_state; + struct posix_acl *acls; ++ struct inode *inode = d_inode(path->dentry); + int rc; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) +@@ -1856,6 +1868,7 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + return -ENOMEM; + } + posix_state_to_acl(&acl_state, acls->a_entries); ++ + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", +@@ -1868,23 +1881,25 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } ++ + free_acl_state(&acl_state); + posix_acl_release(acls); + return rc; + } + + int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, +- struct inode *inode, struct inode *parent_inode) ++ struct path *path, struct inode *parent_inode) + { + struct posix_acl *acls; + struct posix_acl_entry *pace; ++ struct inode *inode = d_inode(path->dentry); + int rc, i; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return -EOPNOTSUPP; + + acls = get_acl(parent_inode, ACL_TYPE_DEFAULT); +- if (!acls) ++ if (IS_ERR_OR_NULL(acls)) + return -ENOENT; + pace = acls->a_entries; + +@@ -1906,6 +1921,7 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } ++ + posix_acl_release(acls); + return rc; + } +diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h +index 593059ca85112..e761dde2443e2 100644 +--- a/fs/smb/server/vfs.h ++++ b/fs/smb/server/vfs.h +@@ -71,25 +71,23 @@ struct ksmbd_kstat { + __le32 file_attributes; + }; + +-int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, +- struct dentry *child); +-int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry); +-int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, ++int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child); ++void ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess); + int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); + int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); +-int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, +- size_t count, loff_t *pos); ++int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, ++ loff_t *pos, char *rbuf); + int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written); + int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); +-int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); ++int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path); + int ksmbd_vfs_link(struct ksmbd_work *work, + const char *oldname, const char *newname); + int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat); +-int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, +- char *newname); ++int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, ++ char *newname, int flags); + int ksmbd_vfs_truncate(struct ksmbd_work *work, + struct ksmbd_file *fp, loff_t size); + struct srv_copychunk; +@@ -110,15 +108,17 @@ ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len); + int ksmbd_vfs_setxattr(struct user_namespace *user_ns, +- struct dentry *dentry, const char *attr_name, +- void *attr_value, size_t attr_size, int flags); ++ const struct path *path, const char *attr_name, ++ void *attr_value, size_t attr_size, int flags, ++ bool get_write); + int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type); + int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, +- struct dentry *dentry, char *attr_name); +-int ksmbd_vfs_kern_path(struct ksmbd_work *work, +- char *name, unsigned int flags, struct path *path, +- bool caseless); ++ const struct path *path, char *attr_name); ++int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, ++ unsigned int flags, struct path *parent_path, ++ struct path *path, bool caseless); ++void ksmbd_vfs_kern_path_unlock(struct path *parent_path, struct path *path); + struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, +@@ -131,8 +131,7 @@ struct file_allocated_range_buffer; + int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + struct file_allocated_range_buffer *ranges, + unsigned int in_count, unsigned int *out_count); +-int ksmbd_vfs_unlink(struct user_namespace *user_ns, +- struct dentry *dir, struct dentry *dentry); ++int ksmbd_vfs_unlink(struct file *filp); + void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); + int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct user_namespace *user_ns, +@@ -142,26 +141,27 @@ void ksmbd_vfs_posix_lock_wait(struct file_lock *flock); + int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); + void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); + int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, +- struct dentry *dentry); +-int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, +- struct dentry *dentry); ++ const struct path *path); ++int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, const struct path *path); + int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, +- struct dentry *dentry, +- struct smb_ntsd *pntsd, int len); ++ const struct path *path, ++ struct smb_ntsd *pntsd, int len, ++ bool get_write); + int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd **pntsd); + int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, +- struct dentry *dentry, +- struct xattr_dos_attrib *da); ++ const struct path *path, ++ struct xattr_dos_attrib *da, ++ bool get_write); + int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da); + int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, +- struct inode *inode); ++ struct path *path); + int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, +- struct inode *inode, ++ struct path *path, + struct inode *parent_inode); + #endif /* __KSMBD_VFS_H__ */ +diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c +index 6ec6c129465d3..2528ce8aeebbe 100644 +--- a/fs/smb/server/vfs_cache.c ++++ b/fs/smb/server/vfs_cache.c +@@ -65,14 +65,14 @@ static unsigned long inode_hash(struct super_block *sb, unsigned long hashval) + return tmp & inode_hash_mask; + } + +-static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) ++static struct ksmbd_inode *__ksmbd_inode_lookup(struct dentry *de) + { + struct hlist_head *head = inode_hashtable + +- inode_hash(inode->i_sb, inode->i_ino); ++ inode_hash(d_inode(de)->i_sb, (unsigned long)de); + struct ksmbd_inode *ci = NULL, *ret_ci = NULL; + + hlist_for_each_entry(ci, head, m_hash) { +- if (ci->m_inode == inode) { ++ if (ci->m_de == de) { + if (atomic_inc_not_zero(&ci->m_count)) + ret_ci = ci; + break; +@@ -83,26 +83,27 @@ static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) + + static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) + { +- return __ksmbd_inode_lookup(file_inode(fp->filp)); ++ return __ksmbd_inode_lookup(fp->filp->f_path.dentry); + } + +-static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode) ++struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d) + { + struct ksmbd_inode *ci; + + read_lock(&inode_hash_lock); +- ci = __ksmbd_inode_lookup(inode); ++ ci = __ksmbd_inode_lookup(d); + read_unlock(&inode_hash_lock); ++ + return ci; + } + +-int ksmbd_query_inode_status(struct inode *inode) ++int ksmbd_query_inode_status(struct dentry *dentry) + { + struct ksmbd_inode *ci; + int ret = KSMBD_INODE_STATUS_UNKNOWN; + + read_lock(&inode_hash_lock); +- ci = __ksmbd_inode_lookup(inode); ++ ci = __ksmbd_inode_lookup(dentry); + if (ci) { + ret = KSMBD_INODE_STATUS_OK; + if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS)) +@@ -142,7 +143,7 @@ void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + static void ksmbd_inode_hash(struct ksmbd_inode *ci) + { + struct hlist_head *b = inode_hashtable + +- inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino); ++ inode_hash(d_inode(ci->m_de)->i_sb, (unsigned long)ci->m_de); + + hlist_add_head(&ci->m_hash, b); + } +@@ -156,7 +157,6 @@ static void ksmbd_inode_unhash(struct ksmbd_inode *ci) + + static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) + { +- ci->m_inode = file_inode(fp->filp); + atomic_set(&ci->m_count, 1); + atomic_set(&ci->op_count, 0); + atomic_set(&ci->sop_count, 0); +@@ -165,6 +165,7 @@ static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) + INIT_LIST_HEAD(&ci->m_fp_list); + INIT_LIST_HEAD(&ci->m_op_list); + rwlock_init(&ci->m_lock); ++ ci->m_de = fp->filp->f_path.dentry; + return 0; + } + +@@ -208,7 +209,7 @@ static void ksmbd_inode_free(struct ksmbd_inode *ci) + kfree(ci); + } + +-static void ksmbd_inode_put(struct ksmbd_inode *ci) ++void ksmbd_inode_put(struct ksmbd_inode *ci) + { + if (atomic_dec_and_test(&ci->m_count)) + ksmbd_inode_free(ci); +@@ -243,7 +244,6 @@ void ksmbd_release_inode_hash(void) + + static void __ksmbd_inode_close(struct ksmbd_file *fp) + { +- struct dentry *dir, *dentry; + struct ksmbd_inode *ci = fp->f_ci; + int err; + struct file *filp; +@@ -252,7 +252,7 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) + if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) { + ci->m_flags &= ~S_DEL_ON_CLS_STREAM; + err = ksmbd_vfs_remove_xattr(file_mnt_user_ns(filp), +- filp->f_path.dentry, ++ &filp->f_path, + fp->stream.name); + if (err) + pr_err("remove xattr failed : %s\n", +@@ -262,11 +262,9 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) + if (atomic_dec_and_test(&ci->m_count)) { + write_lock(&ci->m_lock); + if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { +- dentry = filp->f_path.dentry; +- dir = dentry->d_parent; + ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); + write_unlock(&ci->m_lock); +- ksmbd_vfs_unlink(file_mnt_user_ns(filp), dir, dentry); ++ ksmbd_vfs_unlink(filp); + write_lock(&ci->m_lock); + } + write_unlock(&ci->m_lock); +@@ -335,6 +333,9 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) + + static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) + { ++ if (fp->f_state != FP_INITED) ++ return NULL; ++ + if (!atomic_inc_not_zero(&fp->refcount)) + return NULL; + return fp; +@@ -384,15 +385,20 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) + return 0; + + ft = &work->sess->file_table; +- read_lock(&ft->lock); ++ write_lock(&ft->lock); + fp = idr_find(ft->idr, id); + if (fp) { + set_close_state_blocked_works(fp); + +- if (!atomic_dec_and_test(&fp->refcount)) ++ if (fp->f_state != FP_INITED) + fp = NULL; ++ else { ++ fp->f_state = FP_CLOSED; ++ if (!atomic_dec_and_test(&fp->refcount)) ++ fp = NULL; ++ } + } +- read_unlock(&ft->lock); ++ write_unlock(&ft->lock); + + if (!fp) + return -EINVAL; +@@ -482,12 +488,15 @@ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) + return fp; + } + +-struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) ++struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry) + { + struct ksmbd_file *lfp; + struct ksmbd_inode *ci; ++ struct inode *inode = d_inode(dentry); + +- ci = ksmbd_inode_lookup_by_vfsinode(inode); ++ read_lock(&inode_hash_lock); ++ ci = __ksmbd_inode_lookup(dentry); ++ read_unlock(&inode_hash_lock); + if (!ci) + return NULL; + +@@ -572,6 +581,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) + fp->tcon = work->tcon; + fp->volatile_id = KSMBD_NO_FID; + fp->persistent_id = KSMBD_NO_FID; ++ fp->f_state = FP_NEW; + fp->f_ci = ksmbd_inode_get(fp); + + if (!fp->f_ci) { +@@ -593,6 +603,17 @@ err_out: + return ERR_PTR(ret); + } + ++void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, ++ unsigned int state) ++{ ++ if (!fp) ++ return; ++ ++ write_lock(&ft->lock); ++ fp->f_state = state; ++ write_unlock(&ft->lock); ++} ++ + static int + __close_file_table_ids(struct ksmbd_file_table *ft, + struct ksmbd_tree_connect *tcon, +diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h +index fcb13413fa8d9..a528f0cc775ae 100644 +--- a/fs/smb/server/vfs_cache.h ++++ b/fs/smb/server/vfs_cache.h +@@ -51,7 +51,7 @@ struct ksmbd_inode { + atomic_t op_count; + /* opinfo count for streams */ + atomic_t sop_count; +- struct inode *m_inode; ++ struct dentry *m_de; + unsigned int m_flags; + struct hlist_node m_hash; + struct list_head m_fp_list; +@@ -60,6 +60,12 @@ struct ksmbd_inode { + __le32 m_fattr; + }; + ++enum { ++ FP_NEW = 0, ++ FP_INITED, ++ FP_CLOSED ++}; ++ + struct ksmbd_file { + struct file *filp; + u64 persistent_id; +@@ -98,6 +104,8 @@ struct ksmbd_file { + /* if ls is happening on directory, below is valid*/ + struct ksmbd_readdir_data readdir_data; + int dot_dotdot[2]; ++ unsigned int f_state; ++ bool reserve_lease_break; + }; + + static inline void set_ctx_actor(struct dir_context *ctx, +@@ -131,9 +139,11 @@ struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); + struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + u64 pid); + void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); ++struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d); ++void ksmbd_inode_put(struct ksmbd_inode *ci); + struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); + struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); +-struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode); ++struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); + unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); + struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); + void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); +@@ -142,6 +152,8 @@ int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); + int ksmbd_init_global_file_table(void); + void ksmbd_free_global_file_table(void); + void ksmbd_set_fd_limit(unsigned long limit); ++void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, ++ unsigned int state); + + /* + * INODE hash +@@ -155,7 +167,7 @@ enum KSMBD_INODE_STATUS { + KSMBD_INODE_STATUS_PENDING_DELETE, + }; + +-int ksmbd_query_inode_status(struct inode *inode); ++int ksmbd_query_inode_status(struct dentry *dentry); + bool ksmbd_inode_pending_delete(struct ksmbd_file *fp); + void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); + void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); +diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h +index 57674b3c58774..07a7eeef47d39 100644 +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -565,7 +565,7 @@ struct request_queue { + #define QUEUE_FLAG_NOXMERGES 9 /* No extended merges */ + #define QUEUE_FLAG_ADD_RANDOM 10 /* Contributes to random pool */ + #define QUEUE_FLAG_SAME_FORCE 12 /* force complete on same CPU */ +-#define QUEUE_FLAG_HW_WC 18 /* Write back caching supported */ ++#define QUEUE_FLAG_HW_WC 13 /* Write back caching supported */ + #define QUEUE_FLAG_INIT_DONE 14 /* queue is initialized */ + #define QUEUE_FLAG_STABLE_WRITES 15 /* don't modify blks until WB is done */ + #define QUEUE_FLAG_POLL 16 /* IO polling enabled if set */ +diff --git a/include/linux/export-internal.h b/include/linux/export-internal.h +index fe7e6ba918f10..29de29af9546c 100644 +--- a/include/linux/export-internal.h ++++ b/include/linux/export-internal.h +@@ -12,6 +12,7 @@ + + #define SYMBOL_CRC(sym, crc, sec) \ + asm(".section \"___kcrctab" sec "+" #sym "\",\"a\"" "\n" \ ++ ".balign 4" "\n" \ + "__crc_" #sym ":" "\n" \ + ".long " #crc "\n" \ + ".previous" "\n") +diff --git a/include/linux/module.h b/include/linux/module.h +index ec61fb53979a9..35876e89eb93f 100644 +--- a/include/linux/module.h ++++ b/include/linux/module.h +@@ -879,8 +879,17 @@ static inline bool module_sig_ok(struct module *module) + } + #endif /* CONFIG_MODULE_SIG */ + ++#if defined(CONFIG_MODULES) && defined(CONFIG_KALLSYMS) + int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, + struct module *, unsigned long), + void *data); ++#else ++static inline int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, ++ struct module *, unsigned long), ++ void *data) ++{ ++ return -EOPNOTSUPP; ++} ++#endif /* CONFIG_MODULES && CONFIG_KALLSYMS */ + + #endif /* _LINUX_MODULE_H */ +diff --git a/include/linux/namei.h b/include/linux/namei.h +index 00fee52df8423..5c0149603dc3d 100644 +--- a/include/linux/namei.h ++++ b/include/linux/namei.h +@@ -57,12 +57,18 @@ static inline int user_path_at(int dfd, const char __user *name, unsigned flags, + return user_path_at_empty(dfd, name, flags, path, NULL); + } + ++struct dentry *lookup_one_qstr_excl(const struct qstr *name, ++ struct dentry *base, ++ unsigned int flags); + extern int kern_path(const char *, unsigned, struct path *); + + extern struct dentry *kern_path_create(int, const char *, struct path *, unsigned int); + extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int); + extern void done_path_create(struct path *, struct dentry *); + extern struct dentry *kern_path_locked(const char *, struct path *); ++int vfs_path_parent_lookup(struct filename *filename, unsigned int flags, ++ struct path *parent, struct qstr *last, int *type, ++ const struct path *root); + + extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); + extern struct dentry *lookup_one_len(const char *, struct dentry *, int); +@@ -81,6 +87,7 @@ extern int follow_down(struct path *); + extern int follow_up(struct path *); + + extern struct dentry *lock_rename(struct dentry *, struct dentry *); ++extern struct dentry *lock_rename_child(struct dentry *, struct dentry *); + extern void unlock_rename(struct dentry *, struct dentry *); + + extern int __must_check nd_jump_link(const struct path *path); +diff --git a/include/linux/property.h b/include/linux/property.h +index 117cc200c656d..587b5b666b5bb 100644 +--- a/include/linux/property.h ++++ b/include/linux/property.h +@@ -32,7 +32,12 @@ enum dev_dma_attr { + DEV_DMA_COHERENT, + }; + +-struct fwnode_handle *dev_fwnode(const struct device *dev); ++const struct fwnode_handle *__dev_fwnode_const(const struct device *dev); ++struct fwnode_handle *__dev_fwnode(struct device *dev); ++#define dev_fwnode(dev) \ ++ _Generic((dev), \ ++ const struct device *: __dev_fwnode_const, \ ++ struct device *: __dev_fwnode)(dev) + + bool device_property_present(struct device *dev, const char *propname); + int device_property_read_u8_array(struct device *dev, const char *propname, +diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h +index 877395e075afe..8e9054d9f6df0 100644 +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -263,6 +263,26 @@ static inline void *spi_get_drvdata(struct spi_device *spi) + return dev_get_drvdata(&spi->dev); + } + ++static inline u8 spi_get_chipselect(const struct spi_device *spi, u8 idx) ++{ ++ return spi->chip_select; ++} ++ ++static inline void spi_set_chipselect(struct spi_device *spi, u8 idx, u8 chipselect) ++{ ++ spi->chip_select = chipselect; ++} ++ ++static inline struct gpio_desc *spi_get_csgpiod(const struct spi_device *spi, u8 idx) ++{ ++ return spi->cs_gpiod; ++} ++ ++static inline void spi_set_csgpiod(struct spi_device *spi, u8 idx, struct gpio_desc *csgpiod) ++{ ++ spi->cs_gpiod = csgpiod; ++} ++ + struct spi_message; + + /** +@@ -1515,6 +1535,9 @@ extern void spi_unregister_device(struct spi_device *spi); + extern const struct spi_device_id * + spi_get_device_id(const struct spi_device *sdev); + ++extern const void * ++spi_get_device_match_data(const struct spi_device *sdev); ++ + static inline bool + spi_transfer_is_last(struct spi_controller *ctlr, struct spi_transfer *xfer) + { +diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c +index f5c5c9175333d..4523f99b03589 100644 +--- a/kernel/module/kallsyms.c ++++ b/kernel/module/kallsyms.c +@@ -494,7 +494,6 @@ unsigned long module_kallsyms_lookup_name(const char *name) + return ret; + } + +-#ifdef CONFIG_LIVEPATCH + int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, + struct module *, unsigned long), + void *data) +@@ -531,4 +530,3 @@ out: + mutex_unlock(&module_mutex); + return ret; + } +-#endif /* CONFIG_LIVEPATCH */ +diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c +index 61803208706a5..06d52525407b8 100644 +--- a/kernel/trace/ring_buffer.c ++++ b/kernel/trace/ring_buffer.c +@@ -705,48 +705,6 @@ rb_time_read_cmpxchg(local_t *l, unsigned long expect, unsigned long set) + return ret == expect; + } + +-static int rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set) +-{ +- unsigned long cnt, top, bottom, msb; +- unsigned long cnt2, top2, bottom2, msb2; +- u64 val; +- +- /* Any interruptions in this function should cause a failure */ +- cnt = local_read(&t->cnt); +- +- /* The cmpxchg always fails if it interrupted an update */ +- if (!__rb_time_read(t, &val, &cnt2)) +- return false; +- +- if (val != expect) +- return false; +- +- if ((cnt & 3) != cnt2) +- return false; +- +- cnt2 = cnt + 1; +- +- rb_time_split(val, &top, &bottom, &msb); +- msb = rb_time_val_cnt(msb, cnt); +- top = rb_time_val_cnt(top, cnt); +- bottom = rb_time_val_cnt(bottom, cnt); +- +- rb_time_split(set, &top2, &bottom2, &msb2); +- msb2 = rb_time_val_cnt(msb2, cnt); +- top2 = rb_time_val_cnt(top2, cnt2); +- bottom2 = rb_time_val_cnt(bottom2, cnt2); +- +- if (!rb_time_read_cmpxchg(&t->cnt, cnt, cnt2)) +- return false; +- if (!rb_time_read_cmpxchg(&t->msb, msb, msb2)) +- return false; +- if (!rb_time_read_cmpxchg(&t->top, top, top2)) +- return false; +- if (!rb_time_read_cmpxchg(&t->bottom, bottom, bottom2)) +- return false; +- return true; +-} +- + #else /* 64 bits */ + + /* local64_t always succeeds */ +@@ -760,13 +718,6 @@ static void rb_time_set(rb_time_t *t, u64 val) + { + local64_set(&t->time, val); + } +- +-static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set) +-{ +- u64 val; +- val = local64_cmpxchg(&t->time, expect, set); +- return val == expect; +-} + #endif + + /* +@@ -935,9 +886,14 @@ static __always_inline bool full_hit(struct trace_buffer *buffer, int cpu, int f + if (!nr_pages || !full) + return true; + +- dirty = ring_buffer_nr_dirty_pages(buffer, cpu); ++ /* ++ * Add one as dirty will never equal nr_pages, as the sub-buffer ++ * that the writer is on is not counted as dirty. ++ * This is needed if "buffer_percent" is set to 100. ++ */ ++ dirty = ring_buffer_nr_dirty_pages(buffer, cpu) + 1; + +- return (dirty * 100) > (full * nr_pages); ++ return (dirty * 100) >= (full * nr_pages); + } + + /* +@@ -997,7 +953,8 @@ void ring_buffer_wake_waiters(struct trace_buffer *buffer, int cpu) + /* make sure the waiters see the new index */ + smp_wmb(); + +- rb_wake_up_waiters(&rbwork->work); ++ /* This can be called in any context */ ++ irq_work_queue(&rbwork->work); + } + + /** +@@ -2981,25 +2938,6 @@ static unsigned rb_calculate_event_length(unsigned length) + return length; + } + +-static u64 rb_time_delta(struct ring_buffer_event *event) +-{ +- switch (event->type_len) { +- case RINGBUF_TYPE_PADDING: +- return 0; +- +- case RINGBUF_TYPE_TIME_EXTEND: +- return rb_event_time_stamp(event); +- +- case RINGBUF_TYPE_TIME_STAMP: +- return 0; +- +- case RINGBUF_TYPE_DATA: +- return event->time_delta; +- default: +- return 0; +- } +-} +- + static inline int + rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, + struct ring_buffer_event *event) +@@ -3008,8 +2946,6 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, + struct buffer_page *bpage; + unsigned long index; + unsigned long addr; +- u64 write_stamp; +- u64 delta; + + new_index = rb_event_index(event); + old_index = new_index + rb_event_ts_length(event); +@@ -3018,14 +2954,10 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, + + bpage = READ_ONCE(cpu_buffer->tail_page); + +- delta = rb_time_delta(event); +- +- if (!rb_time_read(&cpu_buffer->write_stamp, &write_stamp)) +- return 0; +- +- /* Make sure the write stamp is read before testing the location */ +- barrier(); +- ++ /* ++ * Make sure the tail_page is still the same and ++ * the next write location is the end of this event ++ */ + if (bpage->page == (void *)addr && rb_page_write(bpage) == old_index) { + unsigned long write_mask = + local_read(&bpage->write) & ~RB_WRITE_MASK; +@@ -3036,20 +2968,20 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, + * to make sure that the next event adds an absolute + * value and does not rely on the saved write stamp, which + * is now going to be bogus. ++ * ++ * By setting the before_stamp to zero, the next event ++ * is not going to use the write_stamp and will instead ++ * create an absolute timestamp. This means there's no ++ * reason to update the wirte_stamp! + */ + rb_time_set(&cpu_buffer->before_stamp, 0); + +- /* Something came in, can't discard */ +- if (!rb_time_cmpxchg(&cpu_buffer->write_stamp, +- write_stamp, write_stamp - delta)) +- return 0; +- + /* + * If an event were to come in now, it would see that the + * write_stamp and the before_stamp are different, and assume + * that this event just added itself before updating + * the write stamp. The interrupting event will fix the +- * write stamp for us, and use the before stamp as its delta. ++ * write stamp for us, and use an absolute timestamp. + */ + + /* +@@ -3488,7 +3420,7 @@ static void check_buffer(struct ring_buffer_per_cpu *cpu_buffer, + return; + + /* +- * If this interrupted another event, ++ * If this interrupted another event, + */ + if (atomic_inc_return(this_cpu_ptr(&checking)) != 1) + goto out; +@@ -3632,20 +3564,36 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, + } else { + u64 ts; + /* SLOW PATH - Interrupted between A and C */ +- a_ok = rb_time_read(&cpu_buffer->write_stamp, &info->after); +- /* Was interrupted before here, write_stamp must be valid */ ++ ++ /* Save the old before_stamp */ ++ a_ok = rb_time_read(&cpu_buffer->before_stamp, &info->before); + RB_WARN_ON(cpu_buffer, !a_ok); ++ ++ /* ++ * Read a new timestamp and update the before_stamp to make ++ * the next event after this one force using an absolute ++ * timestamp. This is in case an interrupt were to come in ++ * between E and F. ++ */ + ts = rb_time_stamp(cpu_buffer->buffer); ++ rb_time_set(&cpu_buffer->before_stamp, ts); ++ + barrier(); +- /*E*/ if (write == (local_read(&tail_page->write) & RB_WRITE_MASK) && +- info->after < ts && +- rb_time_cmpxchg(&cpu_buffer->write_stamp, +- info->after, ts)) { +- /* Nothing came after this event between C and E */ ++ /*E*/ a_ok = rb_time_read(&cpu_buffer->write_stamp, &info->after); ++ /* Was interrupted before here, write_stamp must be valid */ ++ RB_WARN_ON(cpu_buffer, !a_ok); ++ barrier(); ++ /*F*/ if (write == (local_read(&tail_page->write) & RB_WRITE_MASK) && ++ info->after == info->before && info->after < ts) { ++ /* ++ * Nothing came after this event between C and F, it is ++ * safe to use info->after for the delta as it ++ * matched info->before and is still valid. ++ */ + info->delta = ts - info->after; + } else { + /* +- * Interrupted between C and E: ++ * Interrupted between C and F: + * Lost the previous events time stamp. Just set the + * delta to zero, and this will be the same time as + * the event this event interrupted. And the events that +diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c +index 87eca95b57fb3..deae65af76ecf 100644 +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -1850,6 +1850,9 @@ update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu, + __update_max_tr(tr, tsk, cpu); + + arch_spin_unlock(&tr->max_lock); ++ ++ /* Any waiters on the old snapshot buffer need to wake up */ ++ ring_buffer_wake_waiters(tr->array_buffer.buffer, RING_BUFFER_ALL_CPUS); + } + + /** +@@ -1901,12 +1904,23 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu) + + static int wait_on_pipe(struct trace_iterator *iter, int full) + { ++ int ret; ++ + /* Iterators are static, they should be filled or empty */ + if (trace_buffer_iter(iter, iter->cpu_file)) + return 0; + +- return ring_buffer_wait(iter->array_buffer->buffer, iter->cpu_file, +- full); ++ ret = ring_buffer_wait(iter->array_buffer->buffer, iter->cpu_file, full); ++ ++#ifdef CONFIG_TRACER_MAX_TRACE ++ /* ++ * Make sure this is still the snapshot buffer, as if a snapshot were ++ * to happen, this would now be the main buffer. ++ */ ++ if (iter->snapshot) ++ iter->array_buffer = &iter->tr->max_buffer; ++#endif ++ return ret; + } + + #ifdef CONFIG_FTRACE_STARTUP_TEST +@@ -8433,7 +8447,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, + + wait_index = READ_ONCE(iter->wait_index); + +- ret = wait_on_pipe(iter, iter->tr->buffer_percent); ++ ret = wait_on_pipe(iter, iter->snapshot ? 0 : iter->tr->buffer_percent); + if (ret) + goto out; + +diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c +index a34a4fcdab7b1..e3993d19687db 100644 +--- a/kernel/trace/trace_kprobe.c ++++ b/kernel/trace/trace_kprobe.c +@@ -714,14 +714,31 @@ static int count_symbols(void *data, unsigned long unused) + return 0; + } + ++struct sym_count_ctx { ++ unsigned int count; ++ const char *name; ++}; ++ ++static int count_mod_symbols(void *data, const char *name, ++ struct module *module, unsigned long unused) ++{ ++ struct sym_count_ctx *ctx = data; ++ ++ if (strcmp(name, ctx->name) == 0) ++ ctx->count++; ++ ++ return 0; ++} ++ + static unsigned int number_of_same_symbols(char *func_name) + { +- unsigned int count; ++ struct sym_count_ctx ctx = { .count = 0, .name = func_name }; ++ ++ kallsyms_on_each_match_symbol(count_symbols, func_name, &ctx.count); + +- count = 0; +- kallsyms_on_each_match_symbol(count_symbols, func_name, &count); ++ module_kallsyms_on_each_symbol(count_mod_symbols, &ctx); + +- return count; ++ return ctx.count; + } + + static int __trace_kprobe_create(int argc, const char *argv[]) +diff --git a/mm/filemap.c b/mm/filemap.c +index d633ab8cd56f1..10fe6430693bd 100644 +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -2744,6 +2744,15 @@ ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter, + goto put_folios; + end_offset = min_t(loff_t, isize, iocb->ki_pos + iter->count); + ++ /* ++ * Pairs with a barrier in ++ * block_write_end()->mark_buffer_dirty() or other page ++ * dirtying routines like iomap_write_end() to ensure ++ * changes to page contents are visible before we see ++ * increased inode size. ++ */ ++ smp_rmb(); ++ + /* + * Once we start copying data, we don't want to be touching any + * cachelines that might be contended: +diff --git a/mm/memory-failure.c b/mm/memory-failure.c +index 99de0328d1bed..ebd717157c813 100644 +--- a/mm/memory-failure.c ++++ b/mm/memory-failure.c +@@ -1421,7 +1421,7 @@ static bool hwpoison_user_mappings(struct page *p, unsigned long pfn, + * This check implies we don't kill processes if their pages + * are in the swap cache early. Those are always late kills. + */ +- if (!page_mapped(hpage)) ++ if (!page_mapped(p)) + return true; + + if (PageKsm(p)) { +@@ -1477,10 +1477,10 @@ static bool hwpoison_user_mappings(struct page *p, unsigned long pfn, + try_to_unmap(folio, ttu); + } + +- unmap_success = !page_mapped(hpage); ++ unmap_success = !page_mapped(p); + if (!unmap_success) + pr_err("%#lx: failed to unmap page (mapcount=%d)\n", +- pfn, page_mapcount(hpage)); ++ pfn, page_mapcount(p)); + + /* + * try_to_unmap() might put mlocked page in lru cache, so call +@@ -1560,7 +1560,7 @@ static void unmap_and_kill(struct list_head *to_kill, unsigned long pfn, + * mapping being torn down is communicated in siginfo, see + * kill_proc() + */ +- loff_t start = (index << PAGE_SHIFT) & ~(size - 1); ++ loff_t start = ((loff_t)index << PAGE_SHIFT) & ~(size - 1); + + unmap_mapping_range(mapping, start, size, 0); + } +diff --git a/mm/migrate.c b/mm/migrate.c +index 9372a826e6d08..91bd69c61148e 100644 +--- a/mm/migrate.c ++++ b/mm/migrate.c +@@ -388,6 +388,7 @@ int folio_migrate_mapping(struct address_space *mapping, + int dirty; + int expected_count = folio_expected_refs(mapping, folio) + extra_count; + long nr = folio_nr_pages(folio); ++ long entries, i; + + if (!mapping) { + /* Anonymous page without mapping */ +@@ -425,8 +426,10 @@ int folio_migrate_mapping(struct address_space *mapping, + folio_set_swapcache(newfolio); + newfolio->private = folio_get_private(folio); + } ++ entries = nr; + } else { + VM_BUG_ON_FOLIO(folio_test_swapcache(folio), folio); ++ entries = 1; + } + + /* Move dirty while page refs frozen and newpage not yet exposed */ +@@ -436,7 +439,11 @@ int folio_migrate_mapping(struct address_space *mapping, + folio_set_dirty(newfolio); + } + +- xas_store(&xas, newfolio); ++ /* Swap cache still stores N entries instead of a high-order entry */ ++ for (i = 0; i < entries; i++) { ++ xas_store(&xas, newfolio); ++ xas_next(&xas); ++ } + + /* + * Drop cache reference from old page by unfreezing +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 05fa5141af516..3d6ebb9877a4e 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -9480,7 +9480,7 @@ static void nft_set_commit_update(struct list_head *set_update_list) + list_for_each_entry_safe(set, next, set_update_list, pending_update) { + list_del_init(&set->pending_update); + +- if (!set->ops->commit) ++ if (!set->ops->commit || set->dead) + continue; + + set->ops->commit(set); diff --git a/patch/kernel/odroidxu4-current/patch-6.1.71-72.patch b/patch/kernel/odroidxu4-current/patch-6.1.71-72.patch new file mode 100644 index 0000000000..2496793b28 --- /dev/null +++ b/patch/kernel/odroidxu4-current/patch-6.1.71-72.patch @@ -0,0 +1,9332 @@ +diff --git a/MAINTAINERS b/MAINTAINERS +index 07a9c274c0e29..13d1078808bb5 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -10803,6 +10803,8 @@ L: linux-kernel@vger.kernel.org + S: Maintained + T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core + F: kernel/irq/ ++F: include/linux/group_cpus.h ++F: lib/group_cpus.c + + IRQCHIP DRIVERS + M: Thomas Gleixner +diff --git a/Makefile b/Makefile +index 2840e36fd5596..bad3387b3251c 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + VERSION = 6 + PATCHLEVEL = 1 +-SUBLEVEL = 71 ++SUBLEVEL = 72 + EXTRAVERSION = + NAME = Curry Ramen + +diff --git a/arch/Kconfig b/arch/Kconfig +index b60d271bf76a9..14273a6203dfc 100644 +--- a/arch/Kconfig ++++ b/arch/Kconfig +@@ -34,6 +34,9 @@ config ARCH_HAS_SUBPAGE_FAULTS + config HOTPLUG_SMT + bool + ++config SMT_NUM_THREADS_DYNAMIC ++ bool ++ + config GENERIC_ENTRY + bool + +diff --git a/arch/arm/mach-sunxi/mc_smp.c b/arch/arm/mach-sunxi/mc_smp.c +index 26cbce1353387..b2f5f4f28705f 100644 +--- a/arch/arm/mach-sunxi/mc_smp.c ++++ b/arch/arm/mach-sunxi/mc_smp.c +@@ -808,12 +808,12 @@ static int __init sunxi_mc_smp_init(void) + break; + } + +- is_a83t = sunxi_mc_smp_data[i].is_a83t; +- + of_node_put(node); + if (ret) + return -ENODEV; + ++ is_a83t = sunxi_mc_smp_data[i].is_a83t; ++ + if (!sunxi_mc_smp_cpu_table_init()) + return -EINVAL; + +diff --git a/arch/arm64/boot/dts/qcom/sdm845-cheza.dtsi b/arch/arm64/boot/dts/qcom/sdm845-cheza.dtsi +index a5c0c788969fb..43ee28db61aa8 100644 +--- a/arch/arm64/boot/dts/qcom/sdm845-cheza.dtsi ++++ b/arch/arm64/boot/dts/qcom/sdm845-cheza.dtsi +@@ -150,15 +150,15 @@ + }; + + &psci { +- /delete-node/ cpu0; +- /delete-node/ cpu1; +- /delete-node/ cpu2; +- /delete-node/ cpu3; +- /delete-node/ cpu4; +- /delete-node/ cpu5; +- /delete-node/ cpu6; +- /delete-node/ cpu7; +- /delete-node/ cpu-cluster0; ++ /delete-node/ power-domain-cpu0; ++ /delete-node/ power-domain-cpu1; ++ /delete-node/ power-domain-cpu2; ++ /delete-node/ power-domain-cpu3; ++ /delete-node/ power-domain-cpu4; ++ /delete-node/ power-domain-cpu5; ++ /delete-node/ power-domain-cpu6; ++ /delete-node/ power-domain-cpu7; ++ /delete-node/ power-domain-cluster; + }; + + &cpus { +@@ -351,7 +351,9 @@ + + + &apps_rsc { +- pm8998-rpmh-regulators { ++ /delete-property/ power-domains; ++ ++ regulators-0 { + compatible = "qcom,pm8998-rpmh-regulators"; + qcom,pmic-id = "a"; + +@@ -633,7 +635,7 @@ + }; + }; + +- pm8005-rpmh-regulators { ++ regulators-1 { + compatible = "qcom,pm8005-rpmh-regulators"; + qcom,pmic-id = "c"; + +diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts +index c9efcb894a52f..8c9ccf5b4ea41 100644 +--- a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts ++++ b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts +@@ -271,7 +271,7 @@ + }; + + &apps_rsc { +- pm8998-rpmh-regulators { ++ regulators-0 { + compatible = "qcom,pm8998-rpmh-regulators"; + qcom,pmic-id = "a"; + vdd-s1-supply = <&vph_pwr>; +@@ -396,7 +396,7 @@ + }; + }; + +- pmi8998-rpmh-regulators { ++ regulators-1 { + compatible = "qcom,pmi8998-rpmh-regulators"; + qcom,pmic-id = "b"; + +diff --git a/arch/arm64/boot/dts/qcom/sdm845-lg-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-lg-common.dtsi +index 20f275f8694dc..e2921640880a1 100644 +--- a/arch/arm64/boot/dts/qcom/sdm845-lg-common.dtsi ++++ b/arch/arm64/boot/dts/qcom/sdm845-lg-common.dtsi +@@ -166,7 +166,7 @@ + }; + + &apps_rsc { +- pm8998-rpmh-regulators { ++ regulators-0 { + compatible = "qcom,pm8998-rpmh-regulators"; + qcom,pmic-id = "a"; + +@@ -419,7 +419,7 @@ + }; + }; + +- pmi8998-rpmh-regulators { ++ regulators-1 { + compatible = "qcom,pmi8998-rpmh-regulators"; + qcom,pmic-id = "b"; + +@@ -433,7 +433,7 @@ + }; + }; + +- pm8005-rpmh-regulators { ++ regulators-2 { + compatible = "qcom,pm8005-rpmh-regulators"; + qcom,pmic-id = "c"; + +diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dts b/arch/arm64/boot/dts/qcom/sdm845-mtp.dts +index 64958dee17d8b..b47e333aa3510 100644 +--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dts ++++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dts +@@ -117,7 +117,7 @@ + }; + + &apps_rsc { +- pm8998-rpmh-regulators { ++ regulators-0 { + compatible = "qcom,pm8998-rpmh-regulators"; + qcom,pmic-id = "a"; + +@@ -382,7 +382,7 @@ + }; + }; + +- pmi8998-rpmh-regulators { ++ regulators-1 { + compatible = "qcom,pmi8998-rpmh-regulators"; + qcom,pmic-id = "b"; + +@@ -396,7 +396,7 @@ + }; + }; + +- pm8005-rpmh-regulators { ++ regulators-2 { + compatible = "qcom,pm8005-rpmh-regulators"; + qcom,pmic-id = "c"; + +diff --git a/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi +index 392461c29e76e..0713b774a97be 100644 +--- a/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi ++++ b/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi +@@ -144,7 +144,7 @@ + }; + + &apps_rsc { +- pm8998-rpmh-regulators { ++ regulators-0 { + compatible = "qcom,pm8998-rpmh-regulators"; + qcom,pmic-id = "a"; + +@@ -280,7 +280,7 @@ + }; + }; + +- pmi8998-rpmh-regulators { ++ regulators-1 { + compatible = "qcom,pmi8998-rpmh-regulators"; + qcom,pmic-id = "b"; + +@@ -294,7 +294,7 @@ + }; + }; + +- pm8005-rpmh-regulators { ++ regulators-2 { + compatible = "qcom,pm8005-rpmh-regulators"; + qcom,pmic-id = "c"; + +diff --git a/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts b/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts +index 83261c9bb4f23..b65c35865dab9 100644 +--- a/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts ++++ b/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts +@@ -110,7 +110,7 @@ + }; + + &apps_rsc { +- pm8998-rpmh-regulators { ++ regulators-0 { + compatible = "qcom,pm8998-rpmh-regulators"; + qcom,pmic-id = "a"; + +@@ -375,7 +375,7 @@ + }; + }; + +- pmi8998-rpmh-regulators { ++ regulators-1 { + compatible = "qcom,pmi8998-rpmh-regulators"; + qcom,pmic-id = "b"; + +@@ -389,7 +389,7 @@ + }; + }; + +- pm8005-rpmh-regulators { ++ regulators-2 { + compatible = "qcom,pm8005-rpmh-regulators"; + qcom,pmic-id = "c"; + +diff --git a/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi +index d6918e6d19799..249a715d5aae1 100644 +--- a/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi ++++ b/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi +@@ -78,7 +78,7 @@ + }; + + &apps_rsc { +- pm8998-rpmh-regulators { ++ regulators-0 { + compatible = "qcom,pm8998-rpmh-regulators"; + qcom,pmic-id = "a"; + +@@ -308,7 +308,7 @@ + }; + }; + +- pmi8998-rpmh-regulators { ++ regulators-1 { + compatible = "qcom,pmi8998-rpmh-regulators"; + qcom,pmic-id = "b"; + +@@ -319,7 +319,7 @@ + }; + }; + +- pm8005-rpmh-regulators { ++ regulators-2 { + compatible = "qcom,pm8005-rpmh-regulators"; + qcom,pmic-id = "c"; + +diff --git a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium.dts b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium.dts +index 0f470cf1ed1c1..6d6b3dd699475 100644 +--- a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium.dts ++++ b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium.dts +@@ -125,7 +125,7 @@ + }; + + &apps_rsc { +- pm8998-rpmh-regulators { ++ regulators-0 { + compatible = "qcom,pm8998-rpmh-regulators"; + qcom,pmic-id = "a"; + +diff --git a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts +index 093b04359ec39..ffbe45a99b74a 100644 +--- a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts ++++ b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts +@@ -143,7 +143,7 @@ + }; + + &apps_rsc { +- pm8998-rpmh-regulators { ++ regulators-0 { + compatible = "qcom,pm8998-rpmh-regulators"; + qcom,pmic-id = "a"; + +@@ -343,7 +343,7 @@ + }; + }; + +- pmi8998-rpmh-regulators { ++ regulators-1 { + compatible = "qcom,pmi8998-rpmh-regulators"; + qcom,pmic-id = "b"; + +@@ -355,7 +355,7 @@ + }; + }; + +- pm8005-rpmh-regulators { ++ regulators-2 { + compatible = "qcom,pm8005-rpmh-regulators"; + qcom,pmic-id = "c"; + +diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts +index 74f43da51fa50..48a41ace8fc58 100644 +--- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts ++++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts +@@ -99,7 +99,7 @@ + }; + + &apps_rsc { +- pm8998-rpmh-regulators { ++ regulators-0 { + compatible = "qcom,pm8998-rpmh-regulators"; + qcom,pmic-id = "a"; + +diff --git a/arch/arm64/boot/dts/qcom/sdm850-samsung-w737.dts b/arch/arm64/boot/dts/qcom/sdm850-samsung-w737.dts +index d028a7eb364a6..c169d2870bdf4 100644 +--- a/arch/arm64/boot/dts/qcom/sdm850-samsung-w737.dts ++++ b/arch/arm64/boot/dts/qcom/sdm850-samsung-w737.dts +@@ -129,7 +129,7 @@ + }; + + &apps_rsc { +- pm8998-rpmh-regulators { ++ regulators-0 { + compatible = "qcom,pm8998-rpmh-regulators"; + qcom,pmic-id = "a"; + +diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c +index f043a7ff220b7..28fa80fd69fa0 100644 +--- a/arch/s390/kernel/perf_cpum_cf.c ++++ b/arch/s390/kernel/perf_cpum_cf.c +@@ -2,7 +2,7 @@ + /* + * Performance event support for s390x - CPU-measurement Counter Facility + * +- * Copyright IBM Corp. 2012, 2021 ++ * Copyright IBM Corp. 2012, 2022 + * Author(s): Hendrik Brueckner + * Thomas Richter + */ +@@ -434,6 +434,12 @@ static void cpumf_hw_inuse(void) + mutex_unlock(&pmc_reserve_mutex); + } + ++static int is_userspace_event(u64 ev) ++{ ++ return cpumf_generic_events_user[PERF_COUNT_HW_CPU_CYCLES] == ev || ++ cpumf_generic_events_user[PERF_COUNT_HW_INSTRUCTIONS] == ev; ++} ++ + static int __hw_perf_event_init(struct perf_event *event, unsigned int type) + { + struct perf_event_attr *attr = &event->attr; +@@ -456,19 +462,26 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type) + if (is_sampling_event(event)) /* No sampling support */ + return -ENOENT; + ev = attr->config; +- /* Count user space (problem-state) only */ + if (!attr->exclude_user && attr->exclude_kernel) { +- if (ev >= ARRAY_SIZE(cpumf_generic_events_user)) +- return -EOPNOTSUPP; +- ev = cpumf_generic_events_user[ev]; +- +- /* No support for kernel space counters only */ ++ /* ++ * Count user space (problem-state) only ++ * Handle events 32 and 33 as 0:u and 1:u ++ */ ++ if (!is_userspace_event(ev)) { ++ if (ev >= ARRAY_SIZE(cpumf_generic_events_user)) ++ return -EOPNOTSUPP; ++ ev = cpumf_generic_events_user[ev]; ++ } + } else if (!attr->exclude_kernel && attr->exclude_user) { ++ /* No support for kernel space counters only */ + return -EOPNOTSUPP; +- } else { /* Count user and kernel space */ +- if (ev >= ARRAY_SIZE(cpumf_generic_events_basic)) +- return -EOPNOTSUPP; +- ev = cpumf_generic_events_basic[ev]; ++ } else { ++ /* Count user and kernel space, incl. events 32 + 33 */ ++ if (!is_userspace_event(ev)) { ++ if (ev >= ARRAY_SIZE(cpumf_generic_events_basic)) ++ return -EOPNOTSUPP; ++ ev = cpumf_generic_events_basic[ev]; ++ } + } + break; + +diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c +index 9a0ce5315f36d..3cbb461820666 100644 +--- a/arch/s390/mm/vmem.c ++++ b/arch/s390/mm/vmem.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -44,8 +45,11 @@ void *vmem_crst_alloc(unsigned long val) + unsigned long *table; + + table = vmem_alloc_pages(CRST_ALLOC_ORDER); +- if (table) +- crst_table_init(table, val); ++ if (!table) ++ return NULL; ++ crst_table_init(table, val); ++ if (slab_is_available()) ++ arch_set_page_dat(virt_to_page(table), CRST_ALLOC_ORDER); + return table; + } + +diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c +index 2fb5e1541efc1..949129443b1c0 100644 +--- a/arch/x86/events/intel/core.c ++++ b/arch/x86/events/intel/core.c +@@ -4033,12 +4033,17 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr, void *data) + u64 pebs_mask = cpuc->pebs_enabled & x86_pmu.pebs_capable; + int global_ctrl, pebs_enable; + ++ /* ++ * In addition to obeying exclude_guest/exclude_host, remove bits being ++ * used for PEBS when running a guest, because PEBS writes to virtual ++ * addresses (not physical addresses). ++ */ + *nr = 0; + global_ctrl = (*nr)++; + arr[global_ctrl] = (struct perf_guest_switch_msr){ + .msr = MSR_CORE_PERF_GLOBAL_CTRL, + .host = intel_ctrl & ~cpuc->intel_ctrl_guest_mask, +- .guest = intel_ctrl & (~cpuc->intel_ctrl_host_mask | ~pebs_mask), ++ .guest = intel_ctrl & ~cpuc->intel_ctrl_host_mask & ~pebs_mask, + }; + + if (!x86_pmu.pebs) +diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c +index ea155f0cf545c..6120f25b0d5cc 100644 +--- a/arch/x86/kernel/kprobes/core.c ++++ b/arch/x86/kernel/kprobes/core.c +@@ -549,7 +549,8 @@ static void kprobe_emulate_call_indirect(struct kprobe *p, struct pt_regs *regs) + { + unsigned long offs = addrmode_regoffs[p->ainsn.indirect.reg]; + +- int3_emulate_call(regs, regs_get_register(regs, offs)); ++ int3_emulate_push(regs, regs->ip - INT3_INSN_SIZE + p->ainsn.size); ++ int3_emulate_jmp(regs, regs_get_register(regs, offs)); + } + NOKPROBE_SYMBOL(kprobe_emulate_call_indirect); + +diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c +index 4686c1d9d0cfd..b69aee6245e4a 100644 +--- a/arch/x86/net/bpf_jit_comp.c ++++ b/arch/x86/net/bpf_jit_comp.c +@@ -893,6 +893,10 @@ static void emit_nops(u8 **pprog, int len) + + #define INSN_SZ_DIFF (((addrs[i] - addrs[i - 1]) - (prog - temp))) + ++/* mov rax, qword ptr [rbp - rounded_stack_depth - 8] */ ++#define RESTORE_TAIL_CALL_CNT(stack) \ ++ EMIT3_off32(0x48, 0x8B, 0x85, -round_up(stack, 8) - 8) ++ + static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image, + int oldproglen, struct jit_context *ctx, bool jmp_padding) + { +@@ -1436,9 +1440,7 @@ st: if (is_imm8(insn->off)) + case BPF_JMP | BPF_CALL: + func = (u8 *) __bpf_call_base + imm32; + if (tail_call_reachable) { +- /* mov rax, qword ptr [rbp - rounded_stack_depth - 8] */ +- EMIT3_off32(0x48, 0x8B, 0x85, +- -round_up(bpf_prog->aux->stack_depth, 8) - 8); ++ RESTORE_TAIL_CALL_CNT(bpf_prog->aux->stack_depth); + if (!imm32 || emit_call(&prog, func, image + addrs[i - 1] + 7)) + return -EINVAL; + } else { +@@ -1623,16 +1625,24 @@ emit_cond_jmp: /* Convert BPF opcode to x86 */ + break; + + case BPF_JMP | BPF_JA: +- if (insn->off == -1) +- /* -1 jmp instructions will always jump +- * backwards two bytes. Explicitly handling +- * this case avoids wasting too many passes +- * when there are long sequences of replaced +- * dead code. +- */ +- jmp_offset = -2; +- else +- jmp_offset = addrs[i + insn->off] - addrs[i]; ++ case BPF_JMP32 | BPF_JA: ++ if (BPF_CLASS(insn->code) == BPF_JMP) { ++ if (insn->off == -1) ++ /* -1 jmp instructions will always jump ++ * backwards two bytes. Explicitly handling ++ * this case avoids wasting too many passes ++ * when there are long sequences of replaced ++ * dead code. ++ */ ++ jmp_offset = -2; ++ else ++ jmp_offset = addrs[i + insn->off] - addrs[i]; ++ } else { ++ if (insn->imm == -1) ++ jmp_offset = -2; ++ else ++ jmp_offset = addrs[i + insn->imm] - addrs[i]; ++ } + + if (!jmp_offset) { + /* +@@ -1750,63 +1760,37 @@ emit_jmp: + return proglen; + } + +-static void save_regs(const struct btf_func_model *m, u8 **prog, int nr_args, ++static void save_regs(const struct btf_func_model *m, u8 **prog, int nr_regs, + int stack_size) + { +- int i, j, arg_size, nr_regs; ++ int i; ++ + /* Store function arguments to stack. + * For a function that accepts two pointers the sequence will be: + * mov QWORD PTR [rbp-0x10],rdi + * mov QWORD PTR [rbp-0x8],rsi + */ +- for (i = 0, j = 0; i < min(nr_args, 6); i++) { +- if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) { +- nr_regs = (m->arg_size[i] + 7) / 8; +- arg_size = 8; +- } else { +- nr_regs = 1; +- arg_size = m->arg_size[i]; +- } +- +- while (nr_regs) { +- emit_stx(prog, bytes_to_bpf_size(arg_size), +- BPF_REG_FP, +- j == 5 ? X86_REG_R9 : BPF_REG_1 + j, +- -(stack_size - j * 8)); +- nr_regs--; +- j++; +- } +- } ++ for (i = 0; i < min(nr_regs, 6); i++) ++ emit_stx(prog, BPF_DW, BPF_REG_FP, ++ i == 5 ? X86_REG_R9 : BPF_REG_1 + i, ++ -(stack_size - i * 8)); + } + +-static void restore_regs(const struct btf_func_model *m, u8 **prog, int nr_args, ++static void restore_regs(const struct btf_func_model *m, u8 **prog, int nr_regs, + int stack_size) + { +- int i, j, arg_size, nr_regs; ++ int i; + + /* Restore function arguments from stack. + * For a function that accepts two pointers the sequence will be: + * EMIT4(0x48, 0x8B, 0x7D, 0xF0); mov rdi,QWORD PTR [rbp-0x10] + * EMIT4(0x48, 0x8B, 0x75, 0xF8); mov rsi,QWORD PTR [rbp-0x8] + */ +- for (i = 0, j = 0; i < min(nr_args, 6); i++) { +- if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) { +- nr_regs = (m->arg_size[i] + 7) / 8; +- arg_size = 8; +- } else { +- nr_regs = 1; +- arg_size = m->arg_size[i]; +- } +- +- while (nr_regs) { +- emit_ldx(prog, bytes_to_bpf_size(arg_size), +- j == 5 ? X86_REG_R9 : BPF_REG_1 + j, +- BPF_REG_FP, +- -(stack_size - j * 8)); +- nr_regs--; +- j++; +- } +- } ++ for (i = 0; i < min(nr_regs, 6); i++) ++ emit_ldx(prog, BPF_DW, ++ i == 5 ? X86_REG_R9 : BPF_REG_1 + i, ++ BPF_REG_FP, ++ -(stack_size - i * 8)); + } + + static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog, +@@ -2031,8 +2015,8 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i + struct bpf_tramp_links *tlinks, + void *func_addr) + { +- int ret, i, nr_args = m->nr_args, extra_nregs = 0; +- int regs_off, ip_off, args_off, stack_size = nr_args * 8, run_ctx_off; ++ int i, ret, nr_regs = m->nr_args, stack_size = 0; ++ int regs_off, nregs_off, ip_off, run_ctx_off; + struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY]; + struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; + struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; +@@ -2041,17 +2025,14 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i + u8 *prog; + bool save_ret; + +- /* x86-64 supports up to 6 arguments. 7+ can be added in the future */ +- if (nr_args > 6) +- return -ENOTSUPP; +- +- for (i = 0; i < MAX_BPF_FUNC_ARGS; i++) { ++ /* extra registers for struct arguments */ ++ for (i = 0; i < m->nr_args; i++) + if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) +- extra_nregs += (m->arg_size[i] + 7) / 8 - 1; +- } +- if (nr_args + extra_nregs > 6) ++ nr_regs += (m->arg_size[i] + 7) / 8 - 1; ++ ++ /* x86-64 supports up to 6 arguments. 7+ can be added in the future */ ++ if (nr_regs > 6) + return -ENOTSUPP; +- stack_size += extra_nregs * 8; + + /* Generated trampoline stack layout: + * +@@ -2065,11 +2046,12 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i + * [ ... ] + * RBP - regs_off [ reg_arg1 ] program's ctx pointer + * +- * RBP - args_off [ arg regs count ] always ++ * RBP - nregs_off [ regs count ] always + * + * RBP - ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag + * + * RBP - run_ctx_off [ bpf_tramp_run_ctx ] ++ * RSP [ tail_call_cnt ] BPF_TRAMP_F_TAIL_CALL_CTX + */ + + /* room for return value of orig_call or fentry prog */ +@@ -2077,11 +2059,12 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i + if (save_ret) + stack_size += 8; + ++ stack_size += nr_regs * 8; + regs_off = stack_size; + +- /* args count */ ++ /* regs count */ + stack_size += 8; +- args_off = stack_size; ++ nregs_off = stack_size; + + if (flags & BPF_TRAMP_F_IP_ARG) + stack_size += 8; /* room for IP address argument */ +@@ -2106,14 +2089,16 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i + EMIT1(0x55); /* push rbp */ + EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */ + EMIT4(0x48, 0x83, 0xEC, stack_size); /* sub rsp, stack_size */ ++ if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) ++ EMIT1(0x50); /* push rax */ + EMIT1(0x53); /* push rbx */ + + /* Store number of argument registers of the traced function: +- * mov rax, nr_args + extra_nregs +- * mov QWORD PTR [rbp - args_off], rax ++ * mov rax, nr_regs ++ * mov QWORD PTR [rbp - nregs_off], rax + */ +- emit_mov_imm64(&prog, BPF_REG_0, 0, (u32) nr_args + extra_nregs); +- emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -args_off); ++ emit_mov_imm64(&prog, BPF_REG_0, 0, (u32) nr_regs); ++ emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -nregs_off); + + if (flags & BPF_TRAMP_F_IP_ARG) { + /* Store IP address of the traced function: +@@ -2124,7 +2109,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i + emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -ip_off); + } + +- save_regs(m, &prog, nr_args, regs_off); ++ save_regs(m, &prog, nr_regs, regs_off); + + if (flags & BPF_TRAMP_F_CALL_ORIG) { + /* arg1: mov rdi, im */ +@@ -2154,11 +2139,17 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i + } + + if (flags & BPF_TRAMP_F_CALL_ORIG) { +- restore_regs(m, &prog, nr_args, regs_off); ++ restore_regs(m, &prog, nr_regs, regs_off); ++ ++ if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) ++ /* Before calling the original function, restore the ++ * tail_call_cnt from stack to rax. ++ */ ++ RESTORE_TAIL_CALL_CNT(stack_size); + + if (flags & BPF_TRAMP_F_ORIG_STACK) { +- emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, 8); +- EMIT2(0xff, 0xd0); /* call *rax */ ++ emit_ldx(&prog, BPF_DW, BPF_REG_6, BPF_REG_FP, 8); ++ EMIT2(0xff, 0xd3); /* call *rbx */ + } else { + /* call original function */ + if (emit_call(&prog, orig_call, prog)) { +@@ -2195,7 +2186,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i + } + + if (flags & BPF_TRAMP_F_RESTORE_REGS) +- restore_regs(m, &prog, nr_args, regs_off); ++ restore_regs(m, &prog, nr_regs, regs_off); + + /* This needs to be done regardless. If there were fmod_ret programs, + * the return value is only updated on the stack and still needs to be +@@ -2209,7 +2200,12 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i + ret = -EINVAL; + goto cleanup; + } +- } ++ } else if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) ++ /* Before running the original function, restore the ++ * tail_call_cnt from stack to rax. ++ */ ++ RESTORE_TAIL_CALL_CNT(stack_size); ++ + /* restore return value of orig_call or fentry prog back into RAX */ + if (save_ret) + emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8); +diff --git a/block/bdev.c b/block/bdev.c +index d699ecdb32604..b61502ec8da06 100644 +--- a/block/bdev.c ++++ b/block/bdev.c +@@ -507,6 +507,8 @@ struct block_device *bdev_alloc(struct gendisk *disk, u8 partno) + + void bdev_add(struct block_device *bdev, dev_t dev) + { ++ if (bdev_stable_writes(bdev)) ++ mapping_set_stable_writes(bdev->bd_inode->i_mapping); + bdev->bd_dev = dev; + bdev->bd_inode->i_rdev = dev; + bdev->bd_inode->i_ino = dev; +diff --git a/block/blk-mq.c b/block/blk-mq.c +index 100fb0c3114f8..383d94615e502 100644 +--- a/block/blk-mq.c ++++ b/block/blk-mq.c +@@ -2855,11 +2855,8 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q, + }; + struct request *rq; + +- if (unlikely(bio_queue_enter(bio))) +- return NULL; +- + if (blk_mq_attempt_bio_merge(q, bio, nsegs)) +- goto queue_exit; ++ return NULL; + + rq_qos_throttle(q, bio); + +@@ -2875,35 +2872,23 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q, + rq_qos_cleanup(q, bio); + if (bio->bi_opf & REQ_NOWAIT) + bio_wouldblock_error(bio); +-queue_exit: +- blk_queue_exit(q); + return NULL; + } + +-static inline struct request *blk_mq_get_cached_request(struct request_queue *q, +- struct blk_plug *plug, struct bio **bio, unsigned int nsegs) ++/* return true if this @rq can be used for @bio */ ++static bool blk_mq_can_use_cached_rq(struct request *rq, struct blk_plug *plug, ++ struct bio *bio) + { +- struct request *rq; +- enum hctx_type type, hctx_type; ++ enum hctx_type type = blk_mq_get_hctx_type(bio->bi_opf); ++ enum hctx_type hctx_type = rq->mq_hctx->type; + +- if (!plug) +- return NULL; +- rq = rq_list_peek(&plug->cached_rq); +- if (!rq || rq->q != q) +- return NULL; +- +- if (blk_mq_attempt_bio_merge(q, *bio, nsegs)) { +- *bio = NULL; +- return NULL; +- } ++ WARN_ON_ONCE(rq_list_peek(&plug->cached_rq) != rq); + +- type = blk_mq_get_hctx_type((*bio)->bi_opf); +- hctx_type = rq->mq_hctx->type; + if (type != hctx_type && + !(type == HCTX_TYPE_READ && hctx_type == HCTX_TYPE_DEFAULT)) +- return NULL; +- if (op_is_flush(rq->cmd_flags) != op_is_flush((*bio)->bi_opf)) +- return NULL; ++ return false; ++ if (op_is_flush(rq->cmd_flags) != op_is_flush(bio->bi_opf)) ++ return false; + + /* + * If any qos ->throttle() end up blocking, we will have flushed the +@@ -2911,11 +2896,11 @@ static inline struct request *blk_mq_get_cached_request(struct request_queue *q, + * before we throttle. + */ + plug->cached_rq = rq_list_next(rq); +- rq_qos_throttle(q, *bio); ++ rq_qos_throttle(rq->q, bio); + +- rq->cmd_flags = (*bio)->bi_opf; ++ rq->cmd_flags = bio->bi_opf; + INIT_LIST_HEAD(&rq->queuelist); +- return rq; ++ return true; + } + + static void bio_set_ioprio(struct bio *bio) +@@ -2944,7 +2929,7 @@ void blk_mq_submit_bio(struct bio *bio) + struct request_queue *q = bdev_get_queue(bio->bi_bdev); + struct blk_plug *plug = blk_mq_plug(bio); + const int is_sync = op_is_sync(bio->bi_opf); +- struct request *rq; ++ struct request *rq = NULL; + unsigned int nr_segs = 1; + blk_status_t ret; + +@@ -2955,20 +2940,36 @@ void blk_mq_submit_bio(struct bio *bio) + return; + } + +- if (!bio_integrity_prep(bio)) +- return; +- + bio_set_ioprio(bio); + +- rq = blk_mq_get_cached_request(q, plug, &bio, nr_segs); +- if (!rq) { +- if (!bio) ++ if (plug) { ++ rq = rq_list_peek(&plug->cached_rq); ++ if (rq && rq->q != q) ++ rq = NULL; ++ } ++ if (rq) { ++ if (!bio_integrity_prep(bio)) + return; +- rq = blk_mq_get_new_requests(q, plug, bio, nr_segs); +- if (unlikely(!rq)) ++ if (blk_mq_attempt_bio_merge(q, bio, nr_segs)) + return; ++ if (blk_mq_can_use_cached_rq(rq, plug, bio)) ++ goto done; ++ percpu_ref_get(&q->q_usage_counter); ++ } else { ++ if (unlikely(bio_queue_enter(bio))) ++ return; ++ if (!bio_integrity_prep(bio)) ++ goto fail; ++ } ++ ++ rq = blk_mq_get_new_requests(q, plug, bio, nr_segs); ++ if (unlikely(!rq)) { ++fail: ++ blk_queue_exit(q); ++ return; + } + ++done: + trace_block_getrq(bio); + + rq_qos_track(q, rq, bio); +diff --git a/block/fops.c b/block/fops.c +index 6197d1c41652d..01cb6260fa24d 100644 +--- a/block/fops.c ++++ b/block/fops.c +@@ -655,24 +655,35 @@ static long blkdev_fallocate(struct file *file, int mode, loff_t start, + + filemap_invalidate_lock(inode->i_mapping); + +- /* Invalidate the page cache, including dirty pages. */ +- error = truncate_bdev_range(bdev, file->f_mode, start, end); +- if (error) +- goto fail; +- ++ /* ++ * Invalidate the page cache, including dirty pages, for valid ++ * de-allocate mode calls to fallocate(). ++ */ + switch (mode) { + case FALLOC_FL_ZERO_RANGE: + case FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE: ++ error = truncate_bdev_range(bdev, file->f_mode, start, end); ++ if (error) ++ goto fail; ++ + error = blkdev_issue_zeroout(bdev, start >> SECTOR_SHIFT, + len >> SECTOR_SHIFT, GFP_KERNEL, + BLKDEV_ZERO_NOUNMAP); + break; + case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE: ++ error = truncate_bdev_range(bdev, file->f_mode, start, end); ++ if (error) ++ goto fail; ++ + error = blkdev_issue_zeroout(bdev, start >> SECTOR_SHIFT, + len >> SECTOR_SHIFT, GFP_KERNEL, + BLKDEV_ZERO_NOFALLBACK); + break; + case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE | FALLOC_FL_NO_HIDE_STALE: ++ error = truncate_bdev_range(bdev, file->f_mode, start, end); ++ if (error) ++ goto fail; ++ + error = blkdev_issue_discard(bdev, start >> SECTOR_SHIFT, + len >> SECTOR_SHIFT, GFP_KERNEL); + break; +diff --git a/drivers/base/memory.c b/drivers/base/memory.c +index 9aa0da991cfb9..5d39f3e374dae 100644 +--- a/drivers/base/memory.c ++++ b/drivers/base/memory.c +@@ -175,6 +175,9 @@ int memory_notify(unsigned long val, void *v) + return blocking_notifier_call_chain(&memory_chain, val, v); + } + ++/* ++ * Must acquire mem_hotplug_lock in write mode. ++ */ + static int memory_block_online(struct memory_block *mem) + { + unsigned long start_pfn = section_nr_to_pfn(mem->start_section_nr); +@@ -193,10 +196,11 @@ static int memory_block_online(struct memory_block *mem) + * stage helps to keep accounting easier to follow - e.g vmemmaps + * belong to the same zone as the memory they backed. + */ ++ mem_hotplug_begin(); + if (nr_vmemmap_pages) { + ret = mhp_init_memmap_on_memory(start_pfn, nr_vmemmap_pages, zone); + if (ret) +- return ret; ++ goto out; + } + + ret = online_pages(start_pfn + nr_vmemmap_pages, +@@ -204,7 +208,7 @@ static int memory_block_online(struct memory_block *mem) + if (ret) { + if (nr_vmemmap_pages) + mhp_deinit_memmap_on_memory(start_pfn, nr_vmemmap_pages); +- return ret; ++ goto out; + } + + /* +@@ -216,9 +220,14 @@ static int memory_block_online(struct memory_block *mem) + nr_vmemmap_pages); + + mem->zone = zone; ++out: ++ mem_hotplug_done(); + return ret; + } + ++/* ++ * Must acquire mem_hotplug_lock in write mode. ++ */ + static int memory_block_offline(struct memory_block *mem) + { + unsigned long start_pfn = section_nr_to_pfn(mem->start_section_nr); +@@ -233,6 +242,7 @@ static int memory_block_offline(struct memory_block *mem) + * Unaccount before offlining, such that unpopulated zone and kthreads + * can properly be torn down in offline_pages(). + */ ++ mem_hotplug_begin(); + if (nr_vmemmap_pages) + adjust_present_page_count(pfn_to_page(start_pfn), mem->group, + -nr_vmemmap_pages); +@@ -244,13 +254,15 @@ static int memory_block_offline(struct memory_block *mem) + if (nr_vmemmap_pages) + adjust_present_page_count(pfn_to_page(start_pfn), + mem->group, nr_vmemmap_pages); +- return ret; ++ goto out; + } + + if (nr_vmemmap_pages) + mhp_deinit_memmap_on_memory(start_pfn, nr_vmemmap_pages); + + mem->zone = NULL; ++out: ++ mem_hotplug_done(); + return ret; + } + +diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c +index 17c9d825188bb..667ff40f39353 100644 +--- a/drivers/firewire/ohci.c ++++ b/drivers/firewire/ohci.c +@@ -279,6 +279,51 @@ static char ohci_driver_name[] = KBUILD_MODNAME; + #define QUIRK_TI_SLLZ059 0x20 + #define QUIRK_IR_WAKE 0x40 + ++// On PCI Express Root Complex in any type of AMD Ryzen machine, VIA VT6306/6307/6308 with Asmedia ++// ASM1083/1085 brings an inconvenience that the read accesses to 'Isochronous Cycle Timer' register ++// (at offset 0xf0 in PCI I/O space) often causes unexpected system reboot. The mechanism is not ++// clear, since the read access to the other registers is enough safe; e.g. 'Node ID' register, ++// while it is probable due to detection of any type of PCIe error. ++#define QUIRK_REBOOT_BY_CYCLE_TIMER_READ 0x80000000 ++ ++#if IS_ENABLED(CONFIG_X86) ++ ++static bool has_reboot_by_cycle_timer_read_quirk(const struct fw_ohci *ohci) ++{ ++ return !!(ohci->quirks & QUIRK_REBOOT_BY_CYCLE_TIMER_READ); ++} ++ ++#define PCI_DEVICE_ID_ASMEDIA_ASM108X 0x1080 ++ ++static bool detect_vt630x_with_asm1083_on_amd_ryzen_machine(const struct pci_dev *pdev) ++{ ++ const struct pci_dev *pcie_to_pci_bridge; ++ ++ // Detect any type of AMD Ryzen machine. ++ if (!static_cpu_has(X86_FEATURE_ZEN)) ++ return false; ++ ++ // Detect VIA VT6306/6307/6308. ++ if (pdev->vendor != PCI_VENDOR_ID_VIA) ++ return false; ++ if (pdev->device != PCI_DEVICE_ID_VIA_VT630X) ++ return false; ++ ++ // Detect Asmedia ASM1083/1085. ++ pcie_to_pci_bridge = pdev->bus->self; ++ if (pcie_to_pci_bridge->vendor != PCI_VENDOR_ID_ASMEDIA) ++ return false; ++ if (pcie_to_pci_bridge->device != PCI_DEVICE_ID_ASMEDIA_ASM108X) ++ return false; ++ ++ return true; ++} ++ ++#else ++#define has_reboot_by_cycle_timer_read_quirk(ohci) false ++#define detect_vt630x_with_asm1083_on_amd_ryzen_machine(pdev) false ++#endif ++ + /* In case of multiple matches in ohci_quirks[], only the first one is used. */ + static const struct { + unsigned short vendor, device, revision, flags; +@@ -1713,6 +1758,9 @@ static u32 get_cycle_time(struct fw_ohci *ohci) + s32 diff01, diff12; + int i; + ++ if (has_reboot_by_cycle_timer_read_quirk(ohci)) ++ return 0; ++ + c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer); + + if (ohci->quirks & QUIRK_CYCLE_TIMER) { +@@ -3615,6 +3663,9 @@ static int pci_probe(struct pci_dev *dev, + if (param_quirks) + ohci->quirks = param_quirks; + ++ if (detect_vt630x_with_asm1083_on_amd_ryzen_machine(dev)) ++ ohci->quirks |= QUIRK_REBOOT_BY_CYCLE_TIMER_READ; ++ + /* + * Because dma_alloc_coherent() allocates at least one page, + * we save space by using a common buffer for the AR request/ +diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c +index 431bda9165c3d..2775bcafe40f6 100644 +--- a/drivers/firmware/arm_scmi/perf.c ++++ b/drivers/firmware/arm_scmi/perf.c +@@ -131,7 +131,7 @@ struct perf_dom_info { + u32 opp_count; + u32 sustained_freq_khz; + u32 sustained_perf_level; +- u32 mult_factor; ++ unsigned long mult_factor; + char name[SCMI_MAX_STR_SIZE]; + struct scmi_opp opp[MAX_OPPS]; + struct scmi_fc_info *fc_info; +@@ -223,8 +223,8 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, + dom_info->mult_factor = 1000; + else + dom_info->mult_factor = +- (dom_info->sustained_freq_khz * 1000) / +- dom_info->sustained_perf_level; ++ (dom_info->sustained_freq_khz * 1000UL) ++ / dom_info->sustained_perf_level; + strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE); + } + +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +index 8a1b84aaaf717..a5352e5e2bd47 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +@@ -1976,15 +1976,8 @@ static int amdgpu_device_parse_gpu_info_fw(struct amdgpu_device *adev) + + adev->firmware.gpu_info_fw = NULL; + +- if (adev->mman.discovery_bin) { +- /* +- * FIXME: The bounding box is still needed by Navi12, so +- * temporarily read it from gpu_info firmware. Should be dropped +- * when DAL no longer needs it. +- */ +- if (adev->asic_type != CHIP_NAVI12) +- return 0; +- } ++ if (adev->mman.discovery_bin) ++ return 0; + + switch (adev->asic_type) { + default: +diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c +index 9d224bb2b3df6..ce893fe1c69f4 100644 +--- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c ++++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c +@@ -438,7 +438,115 @@ struct _vcs_dpi_soc_bounding_box_st dcn2_0_nv14_soc = { + .use_urgent_burst_bw = 0 + }; + +-struct _vcs_dpi_soc_bounding_box_st dcn2_0_nv12_soc = { 0 }; ++struct _vcs_dpi_soc_bounding_box_st dcn2_0_nv12_soc = { ++ .clock_limits = { ++ { ++ .state = 0, ++ .dcfclk_mhz = 560.0, ++ .fabricclk_mhz = 560.0, ++ .dispclk_mhz = 513.0, ++ .dppclk_mhz = 513.0, ++ .phyclk_mhz = 540.0, ++ .socclk_mhz = 560.0, ++ .dscclk_mhz = 171.0, ++ .dram_speed_mts = 1069.0, ++ }, ++ { ++ .state = 1, ++ .dcfclk_mhz = 694.0, ++ .fabricclk_mhz = 694.0, ++ .dispclk_mhz = 642.0, ++ .dppclk_mhz = 642.0, ++ .phyclk_mhz = 600.0, ++ .socclk_mhz = 694.0, ++ .dscclk_mhz = 214.0, ++ .dram_speed_mts = 1324.0, ++ }, ++ { ++ .state = 2, ++ .dcfclk_mhz = 875.0, ++ .fabricclk_mhz = 875.0, ++ .dispclk_mhz = 734.0, ++ .dppclk_mhz = 734.0, ++ .phyclk_mhz = 810.0, ++ .socclk_mhz = 875.0, ++ .dscclk_mhz = 245.0, ++ .dram_speed_mts = 1670.0, ++ }, ++ { ++ .state = 3, ++ .dcfclk_mhz = 1000.0, ++ .fabricclk_mhz = 1000.0, ++ .dispclk_mhz = 1100.0, ++ .dppclk_mhz = 1100.0, ++ .phyclk_mhz = 810.0, ++ .socclk_mhz = 1000.0, ++ .dscclk_mhz = 367.0, ++ .dram_speed_mts = 2000.0, ++ }, ++ { ++ .state = 4, ++ .dcfclk_mhz = 1200.0, ++ .fabricclk_mhz = 1200.0, ++ .dispclk_mhz = 1284.0, ++ .dppclk_mhz = 1284.0, ++ .phyclk_mhz = 810.0, ++ .socclk_mhz = 1200.0, ++ .dscclk_mhz = 428.0, ++ .dram_speed_mts = 2000.0, ++ }, ++ { ++ .state = 5, ++ .dcfclk_mhz = 1200.0, ++ .fabricclk_mhz = 1200.0, ++ .dispclk_mhz = 1284.0, ++ .dppclk_mhz = 1284.0, ++ .phyclk_mhz = 810.0, ++ .socclk_mhz = 1200.0, ++ .dscclk_mhz = 428.0, ++ .dram_speed_mts = 2000.0, ++ }, ++ }, ++ ++ .num_states = 5, ++ .sr_exit_time_us = 1.9, ++ .sr_enter_plus_exit_time_us = 4.4, ++ .urgent_latency_us = 3.0, ++ .urgent_latency_pixel_data_only_us = 4.0, ++ .urgent_latency_pixel_mixed_with_vm_data_us = 4.0, ++ .urgent_latency_vm_data_only_us = 4.0, ++ .urgent_out_of_order_return_per_channel_pixel_only_bytes = 4096, ++ .urgent_out_of_order_return_per_channel_pixel_and_vm_bytes = 4096, ++ .urgent_out_of_order_return_per_channel_vm_only_bytes = 4096, ++ .pct_ideal_dram_sdp_bw_after_urgent_pixel_only = 40.0, ++ .pct_ideal_dram_sdp_bw_after_urgent_pixel_and_vm = 40.0, ++ .pct_ideal_dram_sdp_bw_after_urgent_vm_only = 40.0, ++ .max_avg_sdp_bw_use_normal_percent = 40.0, ++ .max_avg_dram_bw_use_normal_percent = 40.0, ++ .writeback_latency_us = 12.0, ++ .ideal_dram_bw_after_urgent_percent = 40.0, ++ .max_request_size_bytes = 256, ++ .dram_channel_width_bytes = 16, ++ .fabric_datapath_to_dcn_data_return_bytes = 64, ++ .dcn_downspread_percent = 0.5, ++ .downspread_percent = 0.5, ++ .dram_page_open_time_ns = 50.0, ++ .dram_rw_turnaround_time_ns = 17.5, ++ .dram_return_buffer_per_channel_bytes = 8192, ++ .round_trip_ping_latency_dcfclk_cycles = 131, ++ .urgent_out_of_order_return_per_channel_bytes = 4096, ++ .channel_interleave_bytes = 256, ++ .num_banks = 8, ++ .num_chans = 16, ++ .vmm_page_size_bytes = 4096, ++ .dram_clock_change_latency_us = 45.0, ++ .writeback_dram_clock_change_latency_us = 23.0, ++ .return_bus_width_bytes = 64, ++ .dispclk_dppclk_vco_speed_mhz = 3850, ++ .xfc_bus_transport_time_us = 20, ++ .xfc_xbuf_latency_tolerance_us = 50, ++ .use_urgent_burst_bw = 0, ++}; + + struct _vcs_dpi_ip_params_st dcn2_1_ip = { + .odm_capable = 1, +diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c +index 1b5c27ed27370..ff4d0564122a3 100644 +--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c ++++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c +@@ -527,6 +527,7 @@ static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux, + u32 request_val = AUX_CMD_REQ(msg->request); + u8 *buf = msg->buffer; + unsigned int len = msg->size; ++ unsigned int short_len; + unsigned int val; + int ret; + u8 addr_len[SN_AUX_LENGTH_REG + 1 - SN_AUX_ADDR_19_16_REG]; +@@ -600,7 +601,8 @@ static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux, + } + + if (val & AUX_IRQ_STATUS_AUX_SHORT) { +- ret = regmap_read(pdata->regmap, SN_AUX_LENGTH_REG, &len); ++ ret = regmap_read(pdata->regmap, SN_AUX_LENGTH_REG, &short_len); ++ len = min(len, short_len); + if (ret) + goto exit; + } else if (val & AUX_IRQ_STATUS_NAT_I2C_FAIL) { +diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c +index 5970f4149090f..4699c21102261 100644 +--- a/drivers/gpu/drm/i915/display/intel_dp.c ++++ b/drivers/gpu/drm/i915/display/intel_dp.c +@@ -3707,7 +3707,7 @@ static void intel_dp_process_phy_request(struct intel_dp *intel_dp, + intel_dp->train_set, crtc_state->lane_count); + + drm_dp_set_phy_test_pattern(&intel_dp->aux, data, +- link_status[DP_DPCD_REV]); ++ intel_dp->dpcd[DP_DPCD_REV]); + } + + static u8 intel_dp_autotest_phy_pattern(struct intel_dp *intel_dp) +diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h +index f0c2349404b46..aebd09e2d4087 100644 +--- a/drivers/gpu/drm/mgag200/mgag200_drv.h ++++ b/drivers/gpu/drm/mgag200/mgag200_drv.h +@@ -390,6 +390,11 @@ void mgag200_primary_plane_helper_atomic_disable(struct drm_plane *plane, + .destroy = drm_plane_cleanup, \ + DRM_GEM_SHADOW_PLANE_FUNCS + ++void mgag200_crtc_set_gamma_linear(struct mga_device *mdev, const struct drm_format_info *format); ++void mgag200_crtc_set_gamma(struct mga_device *mdev, ++ const struct drm_format_info *format, ++ struct drm_color_lut *lut); ++ + enum drm_mode_status mgag200_crtc_helper_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode); + int mgag200_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state); +diff --git a/drivers/gpu/drm/mgag200/mgag200_g200er.c b/drivers/gpu/drm/mgag200/mgag200_g200er.c +index bce267e0f7de3..8d4538b710477 100644 +--- a/drivers/gpu/drm/mgag200/mgag200_g200er.c ++++ b/drivers/gpu/drm/mgag200/mgag200_g200er.c +@@ -202,6 +202,11 @@ static void mgag200_g200er_crtc_helper_atomic_enable(struct drm_crtc *crtc, + + mgag200_g200er_reset_tagfifo(mdev); + ++ if (crtc_state->gamma_lut) ++ mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data); ++ else ++ mgag200_crtc_set_gamma_linear(mdev, format); ++ + mgag200_enable_display(mdev); + + if (funcs->enable_vidrst) +diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ev.c b/drivers/gpu/drm/mgag200/mgag200_g200ev.c +index ac957f42abe18..56e6f986bff31 100644 +--- a/drivers/gpu/drm/mgag200/mgag200_g200ev.c ++++ b/drivers/gpu/drm/mgag200/mgag200_g200ev.c +@@ -203,6 +203,11 @@ static void mgag200_g200ev_crtc_helper_atomic_enable(struct drm_crtc *crtc, + + mgag200_g200ev_set_hiprilvl(mdev); + ++ if (crtc_state->gamma_lut) ++ mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data); ++ else ++ mgag200_crtc_set_gamma_linear(mdev, format); ++ + mgag200_enable_display(mdev); + + if (funcs->enable_vidrst) +diff --git a/drivers/gpu/drm/mgag200/mgag200_g200se.c b/drivers/gpu/drm/mgag200/mgag200_g200se.c +index bd6e573c9a1a3..ff2b3c6622e7a 100644 +--- a/drivers/gpu/drm/mgag200/mgag200_g200se.c ++++ b/drivers/gpu/drm/mgag200/mgag200_g200se.c +@@ -334,6 +334,11 @@ static void mgag200_g200se_crtc_helper_atomic_enable(struct drm_crtc *crtc, + + mgag200_g200se_set_hiprilvl(mdev, adjusted_mode, format); + ++ if (crtc_state->gamma_lut) ++ mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data); ++ else ++ mgag200_crtc_set_gamma_linear(mdev, format); ++ + mgag200_enable_display(mdev); + + if (funcs->enable_vidrst) +diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c +index ae90b260312a5..554adf05e0734 100644 +--- a/drivers/gpu/drm/mgag200/mgag200_mode.c ++++ b/drivers/gpu/drm/mgag200/mgag200_mode.c +@@ -28,8 +28,8 @@ + * This file contains setup code for the CRTC. + */ + +-static void mgag200_crtc_set_gamma_linear(struct mga_device *mdev, +- const struct drm_format_info *format) ++void mgag200_crtc_set_gamma_linear(struct mga_device *mdev, ++ const struct drm_format_info *format) + { + int i; + +@@ -65,9 +65,9 @@ static void mgag200_crtc_set_gamma_linear(struct mga_device *mdev, + } + } + +-static void mgag200_crtc_set_gamma(struct mga_device *mdev, +- const struct drm_format_info *format, +- struct drm_color_lut *lut) ++void mgag200_crtc_set_gamma(struct mga_device *mdev, ++ const struct drm_format_info *format, ++ struct drm_color_lut *lut) + { + int i; + +diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h +index 05b8b8dfa9bdd..36587f38dff3d 100644 +--- a/drivers/i2c/i2c-core.h ++++ b/drivers/i2c/i2c-core.h +@@ -3,6 +3,7 @@ + * i2c-core.h - interfaces internal to the I2C framework + */ + ++#include + #include + + struct i2c_devinfo { +@@ -29,7 +30,8 @@ int i2c_dev_irq_from_resources(const struct resource *resources, + */ + static inline bool i2c_in_atomic_xfer_mode(void) + { +- return system_state > SYSTEM_RUNNING && !preemptible(); ++ return system_state > SYSTEM_RUNNING && ++ (IS_ENABLED(CONFIG_PREEMPT_COUNT) ? !preemptible() : irqs_disabled()); + } + + static inline int __i2c_lock_bus_helper(struct i2c_adapter *adap) +diff --git a/drivers/interconnect/qcom/sm8250.c b/drivers/interconnect/qcom/sm8250.c +index 9c2dd40d9a559..5cdb058fa0959 100644 +--- a/drivers/interconnect/qcom/sm8250.c ++++ b/drivers/interconnect/qcom/sm8250.c +@@ -551,7 +551,6 @@ static struct platform_driver qnoc_driver = { + .driver = { + .name = "qnoc-sm8250", + .of_match_table = qnoc_of_match, +- .sync_state = icc_sync_state, + }, + }; + module_platform_driver(qnoc_driver); +diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c +index 904208f6f9546..0147cc062e1ae 100644 +--- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c ++++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c +@@ -334,13 +334,14 @@ static const struct csid_format csid_formats[] = { + }, + }; + +-static void csid_configure_stream(struct csid_device *csid, u8 enable) ++static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 vc) + { + struct csid_testgen_config *tg = &csid->testgen; + u32 val; + u32 phy_sel = 0; + u8 lane_cnt = csid->phy.lane_cnt; +- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_SRC]; ++ /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ ++ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + const struct csid_format *format = csid_get_fmt_entry(csid->formats, csid->nformats, + input_format->code); + +@@ -351,8 +352,19 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) + phy_sel = csid->phy.csiphy_id; + + if (enable) { +- u8 vc = 0; /* Virtual Channel 0 */ +- u8 dt_id = vc * 4; ++ /* ++ * DT_ID is a two bit bitfield that is concatenated with ++ * the four least significant bits of the five bit VC ++ * bitfield to generate an internal CID value. ++ * ++ * CSID_RDI_CFG0(vc) ++ * DT_ID : 28:27 ++ * VC : 26:22 ++ * DT : 21:16 ++ * ++ * CID : VC 3:0 << 2 | DT_ID 1:0 ++ */ ++ u8 dt_id = vc & 0x03; + + if (tg->enabled) { + /* configure one DT, infinite frames */ +@@ -392,42 +404,42 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) + val |= format->data_type << RDI_CFG0_DATA_TYPE; + val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; + val |= dt_id << RDI_CFG0_DT_ID; +- writel_relaxed(val, csid->base + CSID_RDI_CFG0(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + + /* CSID_TIMESTAMP_STB_POST_IRQ */ + val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL; +- writel_relaxed(val, csid->base + CSID_RDI_CFG1(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc)); + + val = 1; +- writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + + val = 0; +- writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc)); + + val = 1; +- writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + + val = 0; +- writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + + val = 1; +- writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc)); + + val = 0; +- writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc)); + + val = 1; +- writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc)); + + val = 0; +- writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc)); + + val = 0; +- writel_relaxed(val, csid->base + CSID_RDI_CTRL(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); + +- val = readl_relaxed(csid->base + CSID_RDI_CFG0(0)); ++ val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc)); + val |= 1 << RDI_CFG0_ENABLE; +- writel_relaxed(val, csid->base + CSID_RDI_CFG0(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + } + + if (tg->enabled) { +@@ -446,6 +458,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) + writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG0); + + val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN; ++ if (vc > 3) ++ val |= 1 << CSI2_RX_CFG1_VC_MODE; + val |= 1 << CSI2_RX_CFG1_MISR_EN; + writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); + +@@ -453,7 +467,16 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) + val = HALT_CMD_RESUME_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; + else + val = HALT_CMD_HALT_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; +- writel_relaxed(val, csid->base + CSID_RDI_CTRL(0)); ++ writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); ++} ++ ++static void csid_configure_stream(struct csid_device *csid, u8 enable) ++{ ++ u8 i; ++ /* Loop through all enabled VCs and configure stream for each */ ++ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) ++ if (csid->phy.en_vc & BIT(i)) ++ __csid_configure_stream(csid, enable, i); + } + + static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val) +@@ -499,6 +522,7 @@ static irqreturn_t csid_isr(int irq, void *dev) + struct csid_device *csid = dev; + u32 val; + u8 reset_done; ++ int i; + + val = readl_relaxed(csid->base + CSID_TOP_IRQ_STATUS); + writel_relaxed(val, csid->base + CSID_TOP_IRQ_CLEAR); +@@ -507,8 +531,12 @@ static irqreturn_t csid_isr(int irq, void *dev) + val = readl_relaxed(csid->base + CSID_CSI2_RX_IRQ_STATUS); + writel_relaxed(val, csid->base + CSID_CSI2_RX_IRQ_CLEAR); + +- val = readl_relaxed(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(0)); +- writel_relaxed(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(0)); ++ /* Read and clear IRQ status for each enabled RDI channel */ ++ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) ++ if (csid->phy.en_vc & BIT(i)) { ++ val = readl_relaxed(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i)); ++ writel_relaxed(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i)); ++ } + + val = 1 << IRQ_CMD_CLEAR; + writel_relaxed(val, csid->base + CSID_IRQ_CMD); +diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c +index 88f188e0f7501..6360314f04a63 100644 +--- a/drivers/media/platform/qcom/camss/camss-csid.c ++++ b/drivers/media/platform/qcom/camss/camss-csid.c +@@ -196,6 +196,8 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) + return ret; + } + ++ csid->phy.need_vc_update = true; ++ + enable_irq(csid->irq); + + ret = csid->ops->reset(csid); +@@ -249,7 +251,10 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) + return -ENOLINK; + } + +- csid->ops->configure_stream(csid, enable); ++ if (csid->phy.need_vc_update) { ++ csid->ops->configure_stream(csid, enable); ++ csid->phy.need_vc_update = false; ++ } + + return 0; + } +@@ -460,6 +465,7 @@ static int csid_set_format(struct v4l2_subdev *sd, + { + struct csid_device *csid = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; ++ int i; + + format = __csid_get_format(csid, sd_state, fmt->pad, fmt->which); + if (format == NULL) +@@ -468,14 +474,14 @@ static int csid_set_format(struct v4l2_subdev *sd, + csid_try_format(csid, sd_state, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + +- /* Propagate the format from sink to source */ ++ /* Propagate the format from sink to source pads */ + if (fmt->pad == MSM_CSID_PAD_SINK) { +- format = __csid_get_format(csid, sd_state, MSM_CSID_PAD_SRC, +- fmt->which); ++ for (i = MSM_CSID_PAD_FIRST_SRC; i < MSM_CSID_PADS_NUM; ++i) { ++ format = __csid_get_format(csid, sd_state, i, fmt->which); + +- *format = fmt->format; +- csid_try_format(csid, sd_state, MSM_CSID_PAD_SRC, format, +- fmt->which); ++ *format = fmt->format; ++ csid_try_format(csid, sd_state, i, format, fmt->which); ++ } + } + + return 0; +@@ -738,7 +744,6 @@ static int csid_link_setup(struct media_entity *entity, + struct csid_device *csid; + struct csiphy_device *csiphy; + struct csiphy_lanes_cfg *lane_cfg; +- struct v4l2_subdev_format format = { 0 }; + + sd = media_entity_to_v4l2_subdev(entity); + csid = v4l2_get_subdevdata(sd); +@@ -761,11 +766,22 @@ static int csid_link_setup(struct media_entity *entity, + lane_cfg = &csiphy->cfg.csi2->lane_cfg; + csid->phy.lane_cnt = lane_cfg->num_data; + csid->phy.lane_assign = csid_get_lane_assign(lane_cfg); ++ } ++ /* Decide which virtual channels to enable based on which source pads are enabled */ ++ if (local->flags & MEDIA_PAD_FL_SOURCE) { ++ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); ++ struct csid_device *csid = v4l2_get_subdevdata(sd); ++ struct device *dev = csid->camss->dev; ++ ++ if (flags & MEDIA_LNK_FL_ENABLED) ++ csid->phy.en_vc |= BIT(local->index - 1); ++ else ++ csid->phy.en_vc &= ~BIT(local->index - 1); + +- /* Reset format on source pad to sink pad format */ +- format.pad = MSM_CSID_PAD_SRC; +- format.which = V4L2_SUBDEV_FORMAT_ACTIVE; +- csid_set_format(&csid->subdev, NULL, &format); ++ csid->phy.need_vc_update = true; ++ ++ dev_dbg(dev, "%s: Enabled CSID virtual channels mask 0x%x\n", ++ __func__, csid->phy.en_vc); + } + + return 0; +@@ -816,6 +832,7 @@ int msm_csid_register_entity(struct csid_device *csid, + struct v4l2_subdev *sd = &csid->subdev; + struct media_pad *pads = csid->pads; + struct device *dev = csid->camss->dev; ++ int i; + int ret; + + v4l2_subdev_init(sd, &csid_v4l2_ops); +@@ -852,7 +869,8 @@ int msm_csid_register_entity(struct csid_device *csid, + } + + pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK; +- pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; ++ for (i = MSM_CSID_PAD_FIRST_SRC; i < MSM_CSID_PADS_NUM; ++i) ++ pads[i].flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + sd->entity.ops = &csid_media_ops; +diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h +index f06040e44c515..d4b48432a0973 100644 +--- a/drivers/media/platform/qcom/camss/camss-csid.h ++++ b/drivers/media/platform/qcom/camss/camss-csid.h +@@ -19,8 +19,13 @@ + #include + + #define MSM_CSID_PAD_SINK 0 +-#define MSM_CSID_PAD_SRC 1 +-#define MSM_CSID_PADS_NUM 2 ++#define MSM_CSID_PAD_FIRST_SRC 1 ++#define MSM_CSID_PADS_NUM 5 ++ ++#define MSM_CSID_PAD_SRC (MSM_CSID_PAD_FIRST_SRC) ++ ++/* CSID hardware can demultiplex up to 4 outputs */ ++#define MSM_CSID_MAX_SRC_STREAMS 4 + + #define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12 + #define DATA_TYPE_YUV420_8BIT 0x18 +@@ -81,6 +86,8 @@ struct csid_phy_config { + u8 csiphy_id; + u8 lane_cnt; + u32 lane_assign; ++ u32 en_vc; ++ u8 need_vc_update; + }; + + struct csid_device; +diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c +index 770490234c872..e9ce53d200bc1 100644 +--- a/drivers/mmc/core/block.c ++++ b/drivers/mmc/core/block.c +@@ -866,9 +866,10 @@ static const struct block_device_operations mmc_bdops = { + static int mmc_blk_part_switch_pre(struct mmc_card *card, + unsigned int part_type) + { ++ const unsigned int mask = EXT_CSD_PART_CONFIG_ACC_RPMB; + int ret = 0; + +- if (part_type == EXT_CSD_PART_CONFIG_ACC_RPMB) { ++ if ((part_type & mask) == mask) { + if (card->ext_csd.cmdq_en) { + ret = mmc_cmdq_disable(card); + if (ret) +@@ -883,9 +884,10 @@ static int mmc_blk_part_switch_pre(struct mmc_card *card, + static int mmc_blk_part_switch_post(struct mmc_card *card, + unsigned int part_type) + { ++ const unsigned int mask = EXT_CSD_PART_CONFIG_ACC_RPMB; + int ret = 0; + +- if (part_type == EXT_CSD_PART_CONFIG_ACC_RPMB) { ++ if ((part_type & mask) == mask) { + mmc_retune_unpause(card->host); + if (card->reenable_cmdq && !card->ext_csd.cmdq_en) + ret = mmc_cmdq_enable(card); +@@ -3180,4 +3182,3 @@ module_exit(mmc_blk_exit); + + MODULE_LICENSE("GPL"); + MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver"); +- +diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c +index b89dca1f15e9c..25c152ef5d60e 100644 +--- a/drivers/mmc/core/host.c ++++ b/drivers/mmc/core/host.c +@@ -670,6 +670,7 @@ EXPORT_SYMBOL(mmc_remove_host); + */ + void mmc_free_host(struct mmc_host *host) + { ++ cancel_delayed_work_sync(&host->detect); + mmc_pwrseq_free(host); + put_device(&host->class_dev); + } +diff --git a/drivers/mmc/host/meson-mx-sdhc-mmc.c b/drivers/mmc/host/meson-mx-sdhc-mmc.c +index da85c2f2acb83..c0e3b1634a88a 100644 +--- a/drivers/mmc/host/meson-mx-sdhc-mmc.c ++++ b/drivers/mmc/host/meson-mx-sdhc-mmc.c +@@ -269,7 +269,7 @@ static int meson_mx_sdhc_enable_clks(struct mmc_host *mmc) + static int meson_mx_sdhc_set_clk(struct mmc_host *mmc, struct mmc_ios *ios) + { + struct meson_mx_sdhc_host *host = mmc_priv(mmc); +- u32 rx_clk_phase; ++ u32 val, rx_clk_phase; + int ret; + + meson_mx_sdhc_disable_clks(mmc); +@@ -290,27 +290,11 @@ static int meson_mx_sdhc_set_clk(struct mmc_host *mmc, struct mmc_ios *ios) + mmc->actual_clock = clk_get_rate(host->sd_clk); + + /* +- * according to Amlogic the following latching points are +- * selected with empirical values, there is no (known) formula +- * to calculate these. ++ * Phase 90 should work in most cases. For data transmission, ++ * meson_mx_sdhc_execute_tuning() will find a accurate value + */ +- if (mmc->actual_clock > 100000000) { +- rx_clk_phase = 1; +- } else if (mmc->actual_clock > 45000000) { +- if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) +- rx_clk_phase = 15; +- else +- rx_clk_phase = 11; +- } else if (mmc->actual_clock >= 25000000) { +- rx_clk_phase = 15; +- } else if (mmc->actual_clock > 5000000) { +- rx_clk_phase = 23; +- } else if (mmc->actual_clock > 1000000) { +- rx_clk_phase = 55; +- } else { +- rx_clk_phase = 1061; +- } +- ++ regmap_read(host->regmap, MESON_SDHC_CLKC, &val); ++ rx_clk_phase = FIELD_GET(MESON_SDHC_CLKC_CLK_DIV, val) / 4; + regmap_update_bits(host->regmap, MESON_SDHC_CLK2, + MESON_SDHC_CLK2_RX_CLK_PHASE, + FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE, +diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c +index 2101b6e794c0e..66c1782823d89 100644 +--- a/drivers/mmc/host/sdhci-sprd.c ++++ b/drivers/mmc/host/sdhci-sprd.c +@@ -228,15 +228,19 @@ static inline void _sdhci_sprd_set_clock(struct sdhci_host *host, + div = ((div & 0x300) >> 2) | ((div & 0xFF) << 8); + sdhci_enable_clk(host, div); + ++ val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI); ++ mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN | SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN; + /* Enable CLK_AUTO when the clock is greater than 400K. */ + if (clk > 400000) { +- val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI); +- mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN | +- SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN; + if (mask != (val & mask)) { + val |= mask; + sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI); + } ++ } else { ++ if (val & mask) { ++ val &= ~mask; ++ sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI); ++ } + } + } + +diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c +index 623cdeb29ed90..df4d88d35701b 100644 +--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c ++++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c +@@ -12081,6 +12081,8 @@ static void bnxt_sp_task(struct work_struct *work) + bnxt_cfg_ntp_filters(bp); + if (test_and_clear_bit(BNXT_HWRM_EXEC_FWD_REQ_SP_EVENT, &bp->sp_event)) + bnxt_hwrm_exec_fwd_req(bp); ++ if (test_and_clear_bit(BNXT_HWRM_PF_UNLOAD_SP_EVENT, &bp->sp_event)) ++ netdev_info(bp->dev, "Receive PF driver unload event!\n"); + if (test_and_clear_bit(BNXT_PERIODIC_STATS_SP_EVENT, &bp->sp_event)) { + bnxt_hwrm_port_qstats(bp, 0); + bnxt_hwrm_port_qstats_ext(bp, 0); +@@ -13059,8 +13061,6 @@ static void bnxt_cfg_ntp_filters(struct bnxt *bp) + } + } + } +- if (test_and_clear_bit(BNXT_HWRM_PF_UNLOAD_SP_EVENT, &bp->sp_event)) +- netdev_info(bp->dev, "Receive PF driver unload event!\n"); + } + + #else +diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c +index 1ae082eb9e905..c2a9913082153 100644 +--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c ++++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c +@@ -2131,8 +2131,10 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) + /* Note: if we ever change from DMA_TX_APPEND_CRC below we + * will need to restore software padding of "runt" packets + */ ++ len_stat |= DMA_TX_APPEND_CRC; ++ + if (!i) { +- len_stat |= DMA_TX_APPEND_CRC | DMA_SOP; ++ len_stat |= DMA_SOP; + if (skb->ip_summed == CHECKSUM_PARTIAL) + len_stat |= DMA_TX_DO_CSUM; + } +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +index b58162ce81d87..de62eee58a00e 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +@@ -509,8 +509,6 @@ static struct sk_buff *dpaa2_eth_copybreak(struct dpaa2_eth_channel *ch, + + memcpy(skb->data, fd_vaddr + fd_offset, fd_length); + +- dpaa2_eth_recycle_buf(priv, ch, dpaa2_fd_get_addr(fd)); +- + return skb; + } + +@@ -528,6 +526,7 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_drv_stats *percpu_extras; + struct device *dev = priv->net_dev->dev.parent; + struct dpaa2_fas *fas; ++ bool recycle_rx_buf = false; + void *buf_data; + u32 status = 0; + u32 xdp_act; +@@ -560,6 +559,8 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, + dma_unmap_page(dev, addr, priv->rx_buf_size, + DMA_BIDIRECTIONAL); + skb = dpaa2_eth_build_linear_skb(ch, fd, vaddr); ++ } else { ++ recycle_rx_buf = true; + } + } else if (fd_format == dpaa2_fd_sg) { + WARN_ON(priv->xdp_prog); +@@ -607,6 +608,8 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, + + list_add_tail(&skb->list, ch->rx_list); + ++ if (recycle_rx_buf) ++ dpaa2_eth_recycle_buf(priv, ch, dpaa2_fd_get_addr(fd)); + return; + + err_build_skb: +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +index eea7d7a07c007..59888826469b9 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +@@ -227,17 +227,8 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, + struct ethtool_stats *stats, + u64 *data) + { +- int i = 0; +- int j, k, err; +- int num_cnt; +- union dpni_statistics dpni_stats; +- u32 fcnt, bcnt; +- u32 fcnt_rx_total = 0, fcnt_tx_total = 0; +- u32 bcnt_rx_total = 0, bcnt_tx_total = 0; +- u32 buf_cnt; + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); +- struct dpaa2_eth_drv_stats *extras; +- struct dpaa2_eth_ch_stats *ch_stats; ++ union dpni_statistics dpni_stats; + int dpni_stats_page_size[DPNI_STATISTICS_CNT] = { + sizeof(dpni_stats.page_0), + sizeof(dpni_stats.page_1), +@@ -247,6 +238,13 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev, + sizeof(dpni_stats.page_5), + sizeof(dpni_stats.page_6), + }; ++ u32 fcnt_rx_total = 0, fcnt_tx_total = 0; ++ u32 bcnt_rx_total = 0, bcnt_tx_total = 0; ++ struct dpaa2_eth_ch_stats *ch_stats; ++ struct dpaa2_eth_drv_stats *extras; ++ int j, k, err, num_cnt, i = 0; ++ u32 fcnt, bcnt; ++ u32 buf_cnt; + + memset(data, 0, + sizeof(u64) * (DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS)); +diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c +index b4157ff370a31..63d43ef86f9b9 100644 +--- a/drivers/net/ethernet/intel/i40e/i40e_main.c ++++ b/drivers/net/ethernet/intel/i40e/i40e_main.c +@@ -104,12 +104,18 @@ static struct workqueue_struct *i40e_wq; + static void netdev_hw_addr_refcnt(struct i40e_mac_filter *f, + struct net_device *netdev, int delta) + { ++ struct netdev_hw_addr_list *ha_list; + struct netdev_hw_addr *ha; + + if (!f || !netdev) + return; + +- netdev_for_each_mc_addr(ha, netdev) { ++ if (is_unicast_ether_addr(f->macaddr) || is_link_local_ether_addr(f->macaddr)) ++ ha_list = &netdev->uc; ++ else ++ ha_list = &netdev->mc; ++ ++ netdev_hw_addr_list_for_each(ha, ha_list) { + if (ether_addr_equal(ha->addr, f->macaddr)) { + ha->refcount += delta; + if (ha->refcount <= 0) +@@ -16444,6 +16450,9 @@ static void i40e_pci_error_reset_done(struct pci_dev *pdev) + return; + + i40e_reset_and_rebuild(pf, false, false); ++#ifdef CONFIG_PCI_IOV ++ i40e_restore_all_vfs_msi_state(pdev); ++#endif /* CONFIG_PCI_IOV */ + } + + /** +diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +index cb925baf72ce0..c7d761426d6ce 100644 +--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c ++++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +@@ -152,6 +152,32 @@ void i40e_vc_notify_reset(struct i40e_pf *pf) + (u8 *)&pfe, sizeof(struct virtchnl_pf_event)); + } + ++#ifdef CONFIG_PCI_IOV ++void i40e_restore_all_vfs_msi_state(struct pci_dev *pdev) ++{ ++ u16 vf_id; ++ u16 pos; ++ ++ /* Continue only if this is a PF */ ++ if (!pdev->is_physfn) ++ return; ++ ++ if (!pci_num_vf(pdev)) ++ return; ++ ++ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); ++ if (pos) { ++ struct pci_dev *vf_dev = NULL; ++ ++ pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &vf_id); ++ while ((vf_dev = pci_get_device(pdev->vendor, vf_id, vf_dev))) { ++ if (vf_dev->is_virtfn && vf_dev->physfn == pdev) ++ pci_restore_msi_state(vf_dev); ++ } ++ } ++} ++#endif /* CONFIG_PCI_IOV */ ++ + /** + * i40e_vc_notify_vf_reset + * @vf: pointer to the VF structure +@@ -3451,16 +3477,16 @@ static int i40e_validate_cloud_filter(struct i40e_vf *vf, + bool found = false; + int bkt; + +- if (!tc_filter->action) { ++ if (tc_filter->action != VIRTCHNL_ACTION_TC_REDIRECT) { + dev_info(&pf->pdev->dev, +- "VF %d: Currently ADq doesn't support Drop Action\n", +- vf->vf_id); ++ "VF %d: ADQ doesn't support this action (%d)\n", ++ vf->vf_id, tc_filter->action); + goto err; + } + + /* action_meta is TC number here to which the filter is applied */ + if (!tc_filter->action_meta || +- tc_filter->action_meta > I40E_MAX_VF_VSI) { ++ tc_filter->action_meta > vf->num_tc) { + dev_info(&pf->pdev->dev, "VF %d: Invalid TC number %u\n", + vf->vf_id, tc_filter->action_meta); + goto err; +diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +index 358bbdb587951..bd497cc5303a1 100644 +--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h ++++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +@@ -135,6 +135,9 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable); + + void i40e_vc_notify_link_state(struct i40e_pf *pf); + void i40e_vc_notify_reset(struct i40e_pf *pf); ++#ifdef CONFIG_PCI_IOV ++void i40e_restore_all_vfs_msi_state(struct pci_dev *pdev); ++#endif /* CONFIG_PCI_IOV */ + int i40e_get_vf_stats(struct net_device *netdev, int vf_id, + struct ifla_vf_stats *vf_stats); + +diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c +index f0f39364819ac..ab46cfca4028d 100644 +--- a/drivers/net/ethernet/intel/ice/ice_main.c ++++ b/drivers/net/ethernet/intel/ice/ice_main.c +@@ -2138,7 +2138,7 @@ static int ice_configure_phy(struct ice_vsi *vsi) + + /* Ensure we have media as we cannot configure a medialess port */ + if (!(phy->link_info.link_info & ICE_AQ_MEDIA_AVAILABLE)) +- return -EPERM; ++ return -ENOMEDIUM; + + ice_print_topo_conflict(vsi); + +@@ -9065,8 +9065,14 @@ int ice_stop(struct net_device *netdev) + int link_err = ice_force_phys_link_state(vsi, false); + + if (link_err) { +- netdev_err(vsi->netdev, "Failed to set physical link down, VSI %d error %d\n", +- vsi->vsi_num, link_err); ++ if (link_err == -ENOMEDIUM) ++ netdev_info(vsi->netdev, "Skipping link reconfig - no media attached, VSI %d\n", ++ vsi->vsi_num); ++ else ++ netdev_err(vsi->netdev, "Failed to set physical link down, VSI %d error %d\n", ++ vsi->vsi_num, link_err); ++ ++ ice_vsi_close(vsi); + return -EIO; + } + } +diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h +index 43c05b41627f7..2a894ca49d93b 100644 +--- a/drivers/net/ethernet/intel/igc/igc.h ++++ b/drivers/net/ethernet/intel/igc/igc.h +@@ -538,6 +538,7 @@ struct igc_nfc_filter { + u16 etype; + __be16 vlan_etype; + u16 vlan_tci; ++ u16 vlan_tci_mask; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; + u8 user_data[8]; +diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c +index 81897f7a90a91..2bee9cace5983 100644 +--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c ++++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c +@@ -957,6 +957,7 @@ static int igc_ethtool_set_coalesce(struct net_device *netdev, + } + + #define ETHER_TYPE_FULL_MASK ((__force __be16)~0) ++#define VLAN_TCI_FULL_MASK ((__force __be16)~0) + static int igc_ethtool_get_nfc_rule(struct igc_adapter *adapter, + struct ethtool_rxnfc *cmd) + { +@@ -979,10 +980,16 @@ static int igc_ethtool_get_nfc_rule(struct igc_adapter *adapter, + fsp->m_u.ether_spec.h_proto = ETHER_TYPE_FULL_MASK; + } + ++ if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_ETYPE) { ++ fsp->flow_type |= FLOW_EXT; ++ fsp->h_ext.vlan_etype = rule->filter.vlan_etype; ++ fsp->m_ext.vlan_etype = ETHER_TYPE_FULL_MASK; ++ } ++ + if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) { + fsp->flow_type |= FLOW_EXT; + fsp->h_ext.vlan_tci = htons(rule->filter.vlan_tci); +- fsp->m_ext.vlan_tci = htons(VLAN_PRIO_MASK); ++ fsp->m_ext.vlan_tci = htons(rule->filter.vlan_tci_mask); + } + + if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) { +@@ -1217,6 +1224,7 @@ static void igc_ethtool_init_nfc_rule(struct igc_nfc_rule *rule, + + if ((fsp->flow_type & FLOW_EXT) && fsp->m_ext.vlan_tci) { + rule->filter.vlan_tci = ntohs(fsp->h_ext.vlan_tci); ++ rule->filter.vlan_tci_mask = ntohs(fsp->m_ext.vlan_tci); + rule->filter.match_flags |= IGC_FILTER_FLAG_VLAN_TCI; + } + +@@ -1254,11 +1262,19 @@ static void igc_ethtool_init_nfc_rule(struct igc_nfc_rule *rule, + memcpy(rule->filter.user_mask, fsp->m_ext.data, sizeof(fsp->m_ext.data)); + } + +- /* When multiple filter options or user data or vlan etype is set, use a +- * flex filter. ++ /* The i225/i226 has various different filters. Flex filters provide a ++ * way to match up to the first 128 bytes of a packet. Use them for: ++ * a) For specific user data ++ * b) For VLAN EtherType ++ * c) For full TCI match ++ * d) Or in case multiple filter criteria are set ++ * ++ * Otherwise, use the simple MAC, VLAN PRIO or EtherType filters. + */ + if ((rule->filter.match_flags & IGC_FILTER_FLAG_USER_DATA) || + (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_ETYPE) || ++ ((rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) && ++ rule->filter.vlan_tci_mask == ntohs(VLAN_TCI_FULL_MASK)) || + (rule->filter.match_flags & (rule->filter.match_flags - 1))) + rule->flex = true; + else +@@ -1328,6 +1344,26 @@ static int igc_ethtool_add_nfc_rule(struct igc_adapter *adapter, + return -EINVAL; + } + ++ /* There are two ways to match the VLAN TCI: ++ * 1. Match on PCP field and use vlan prio filter for it ++ * 2. Match on complete TCI field and use flex filter for it ++ */ ++ if ((fsp->flow_type & FLOW_EXT) && ++ fsp->m_ext.vlan_tci && ++ fsp->m_ext.vlan_tci != htons(VLAN_PRIO_MASK) && ++ fsp->m_ext.vlan_tci != VLAN_TCI_FULL_MASK) { ++ netdev_dbg(netdev, "VLAN mask not supported\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ /* VLAN EtherType can only be matched by full mask. */ ++ if ((fsp->flow_type & FLOW_EXT) && ++ fsp->m_ext.vlan_etype && ++ fsp->m_ext.vlan_etype != ETHER_TYPE_FULL_MASK) { ++ netdev_dbg(netdev, "VLAN EtherType mask not supported\n"); ++ return -EOPNOTSUPP; ++ } ++ + if (fsp->location >= IGC_MAX_RXNFC_RULES) { + netdev_dbg(netdev, "Invalid location\n"); + return -EINVAL; +diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c +index 725db36e399d2..31ea0781b65ec 100644 +--- a/drivers/net/ethernet/intel/igc/igc_tsn.c ++++ b/drivers/net/ethernet/intel/igc/igc_tsn.c +@@ -178,7 +178,7 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter) + wr32(IGC_TQAVCC(i), tqavcc); + + wr32(IGC_TQAVHC(i), +- 0x80000000 + ring->hicredit * 0x7735); ++ 0x80000000 + ring->hicredit * 0x7736); + } else { + /* Disable any CBS for the queue */ + txqctl &= ~(IGC_TXQCTL_QAV_SEL_MASK); +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +index 65c0373d34d12..90be87dc105d3 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +@@ -78,7 +78,7 @@ static bool is_dev_rpm(void *cgxd) + + bool is_lmac_valid(struct cgx *cgx, int lmac_id) + { +- if (!cgx || lmac_id < 0 || lmac_id >= MAX_LMAC_PER_CGX) ++ if (!cgx || lmac_id < 0 || lmac_id >= cgx->max_lmac_per_mac) + return false; + return test_bit(lmac_id, &cgx->lmac_bmap); + } +@@ -90,7 +90,7 @@ static int get_sequence_id_of_lmac(struct cgx *cgx, int lmac_id) + { + int tmp, id = 0; + +- for_each_set_bit(tmp, &cgx->lmac_bmap, MAX_LMAC_PER_CGX) { ++ for_each_set_bit(tmp, &cgx->lmac_bmap, cgx->max_lmac_per_mac) { + if (tmp == lmac_id) + break; + id++; +@@ -121,7 +121,7 @@ u64 cgx_read(struct cgx *cgx, u64 lmac, u64 offset) + + struct lmac *lmac_pdata(u8 lmac_id, struct cgx *cgx) + { +- if (!cgx || lmac_id >= MAX_LMAC_PER_CGX) ++ if (!cgx || lmac_id >= cgx->max_lmac_per_mac) + return NULL; + + return cgx->lmac_idmap[lmac_id]; +@@ -1410,7 +1410,7 @@ int cgx_get_fwdata_base(u64 *base) + if (!cgx) + return -ENXIO; + +- first_lmac = find_first_bit(&cgx->lmac_bmap, MAX_LMAC_PER_CGX); ++ first_lmac = find_first_bit(&cgx->lmac_bmap, cgx->max_lmac_per_mac); + req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FWD_BASE, req); + err = cgx_fwi_cmd_generic(req, &resp, cgx, first_lmac); + if (!err) +@@ -1499,7 +1499,7 @@ static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool enable) + + static inline int cgx_fwi_read_version(u64 *resp, struct cgx *cgx) + { +- int first_lmac = find_first_bit(&cgx->lmac_bmap, MAX_LMAC_PER_CGX); ++ int first_lmac = find_first_bit(&cgx->lmac_bmap, cgx->max_lmac_per_mac); + u64 req = 0; + + req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FW_VER, req); +@@ -1537,7 +1537,7 @@ static void cgx_lmac_linkup_work(struct work_struct *work) + int i, err; + + /* Do Link up for all the enabled lmacs */ +- for_each_set_bit(i, &cgx->lmac_bmap, MAX_LMAC_PER_CGX) { ++ for_each_set_bit(i, &cgx->lmac_bmap, cgx->max_lmac_per_mac) { + err = cgx_fwi_link_change(cgx, i, true); + if (err) + dev_info(dev, "cgx port %d:%d Link up command failed\n", +@@ -1557,14 +1557,6 @@ int cgx_lmac_linkup_start(void *cgxd) + return 0; + } + +-static void cgx_lmac_get_fifolen(struct cgx *cgx) +-{ +- u64 cfg; +- +- cfg = cgx_read(cgx, 0, CGX_CONST); +- cgx->mac_ops->fifo_len = FIELD_GET(CGX_CONST_RXFIFO_SIZE, cfg); +-} +- + static int cgx_configure_interrupt(struct cgx *cgx, struct lmac *lmac, + int cnt, bool req_free) + { +@@ -1619,17 +1611,14 @@ static int cgx_lmac_init(struct cgx *cgx) + u64 lmac_list; + int i, err; + +- cgx_lmac_get_fifolen(cgx); +- +- cgx->lmac_count = cgx->mac_ops->get_nr_lmacs(cgx); + /* lmac_list specifies which lmacs are enabled + * when bit n is set to 1, LMAC[n] is enabled + */ + if (cgx->mac_ops->non_contiguous_serdes_lane) + lmac_list = cgx_read(cgx, 0, CGXX_CMRX_RX_LMACS) & 0xFULL; + +- if (cgx->lmac_count > MAX_LMAC_PER_CGX) +- cgx->lmac_count = MAX_LMAC_PER_CGX; ++ if (cgx->lmac_count > cgx->max_lmac_per_mac) ++ cgx->lmac_count = cgx->max_lmac_per_mac; + + for (i = 0; i < cgx->lmac_count; i++) { + lmac = kzalloc(sizeof(struct lmac), GFP_KERNEL); +@@ -1707,7 +1696,7 @@ static int cgx_lmac_exit(struct cgx *cgx) + } + + /* Free all lmac related resources */ +- for_each_set_bit(i, &cgx->lmac_bmap, MAX_LMAC_PER_CGX) { ++ for_each_set_bit(i, &cgx->lmac_bmap, cgx->max_lmac_per_mac) { + lmac = cgx->lmac_idmap[i]; + if (!lmac) + continue; +@@ -1723,6 +1712,12 @@ static int cgx_lmac_exit(struct cgx *cgx) + + static void cgx_populate_features(struct cgx *cgx) + { ++ u64 cfg; ++ ++ cfg = cgx_read(cgx, 0, CGX_CONST); ++ cgx->mac_ops->fifo_len = FIELD_GET(CGX_CONST_RXFIFO_SIZE, cfg); ++ cgx->max_lmac_per_mac = FIELD_GET(CGX_CONST_MAX_LMACS, cfg); ++ + if (is_dev_rpm(cgx)) + cgx->hw_features = (RVU_LMAC_FEAT_DMACF | RVU_MAC_RPM | + RVU_LMAC_FEAT_FC | RVU_LMAC_FEAT_PTP); +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h +index 04338db38671b..09ddb00f63cc7 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h ++++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h +@@ -18,11 +18,8 @@ + /* PCI BAR nos */ + #define PCI_CFG_REG_BAR_NUM 0 + +-#define CGX_ID_MASK 0x7 +-#define MAX_LMAC_PER_CGX 4 ++#define CGX_ID_MASK 0xF + #define MAX_DMAC_ENTRIES_PER_CGX 32 +-#define CGX_FIFO_LEN 65536 /* 64K for both Rx & Tx */ +-#define CGX_OFFSET(x) ((x) * MAX_LMAC_PER_CGX) + + /* Registers */ + #define CGXX_CMRX_CFG 0x00 +@@ -56,6 +53,7 @@ + #define CGXX_SCRATCH1_REG 0x1058 + #define CGX_CONST 0x2000 + #define CGX_CONST_RXFIFO_SIZE GENMASK_ULL(23, 0) ++#define CGX_CONST_MAX_LMACS GENMASK_ULL(31, 24) + #define CGXX_SPUX_CONTROL1 0x10000 + #define CGXX_SPUX_LNX_FEC_CORR_BLOCKS 0x10700 + #define CGXX_SPUX_LNX_FEC_UNCORR_BLOCKS 0x10800 +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h +index 52b6016789fa4..697cfec74aa1e 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h ++++ b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h +@@ -128,7 +128,10 @@ struct cgx { + struct pci_dev *pdev; + u8 cgx_id; + u8 lmac_count; +- struct lmac *lmac_idmap[MAX_LMAC_PER_CGX]; ++ /* number of LMACs per MAC could be 4 or 8 */ ++ u8 max_lmac_per_mac; ++#define MAX_LMAC_COUNT 8 ++ struct lmac *lmac_idmap[MAX_LMAC_COUNT]; + struct work_struct cgx_cmd_work; + struct workqueue_struct *cgx_cmd_workq; + struct list_head cgx_list; +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/npc.h +index d027c23b8ef8e..aaff91bc7415a 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/npc.h ++++ b/drivers/net/ethernet/marvell/octeontx2/af/npc.h +@@ -514,7 +514,7 @@ struct npc_lt_def { + u8 ltype_mask; + u8 ltype_match; + u8 lid; +-}; ++} __packed; + + struct npc_lt_def_ipsec { + u8 ltype_mask; +@@ -522,7 +522,7 @@ struct npc_lt_def_ipsec { + u8 lid; + u8 spi_offset; + u8 spi_nz; +-}; ++} __packed; + + struct npc_lt_def_apad { + u8 ltype_mask; +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c +index a70e1153fa04b..6b4792a942d84 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c +@@ -283,6 +283,11 @@ void rpm_lmac_pause_frm_config(void *rpmd, int lmac_id, bool enable) + cfg = FIELD_SET(RPM_PFC_CLASS_MASK, 0, cfg); + rpm_write(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL, cfg); + ++ /* Disable forward pause to driver */ ++ cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); ++ cfg &= ~RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_FWD; ++ rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); ++ + /* Enable channel mask for all LMACS */ + rpm_write(rpm, 0, RPMX_CMR_CHAN_MSK_OR, ~0ULL); + } +@@ -451,12 +456,10 @@ int rpm_lmac_pfc_config(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause, u16 p + + if (rx_pause) { + cfg &= ~(RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE | +- RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE | +- RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_FWD); ++ RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE); + } else { + cfg |= (RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE | +- RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE | +- RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_FWD); ++ RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE); + } + + if (tx_pause) { +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +index 95a7bc396e8ea..0b76dfa979d4e 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +@@ -480,7 +480,7 @@ struct rvu { + u8 cgx_mapped_pfs; + u8 cgx_cnt_max; /* CGX port count max */ + u8 *pf2cgxlmac_map; /* pf to cgx_lmac map */ +- u16 *cgxlmac2pf_map; /* bitmap of mapped pfs for ++ u64 *cgxlmac2pf_map; /* bitmap of mapped pfs for + * every cgx lmac port + */ + unsigned long pf_notify_bmap; /* Flags for PF notification */ +@@ -850,6 +850,7 @@ u32 rvu_cgx_get_fifolen(struct rvu *rvu); + void *rvu_first_cgx_pdata(struct rvu *rvu); + int cgxlmac_to_pf(struct rvu *rvu, int cgx_id, int lmac_id); + int rvu_cgx_config_tx(void *cgxd, int lmac_id, bool enable); ++int rvu_cgx_tx_enable(struct rvu *rvu, u16 pcifunc, bool enable); + int rvu_cgx_prio_flow_ctrl_cfg(struct rvu *rvu, u16 pcifunc, u8 tx_pause, u8 rx_pause, + u16 pfc_en); + int rvu_cgx_cfg_pause_frm(struct rvu *rvu, u16 pcifunc, u8 tx_pause, u8 rx_pause); +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +index c60b9580ca969..bcb4385d0621c 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +@@ -55,8 +55,9 @@ bool is_mac_feature_supported(struct rvu *rvu, int pf, int feature) + return (cgx_features_get(cgxd) & feature); + } + ++#define CGX_OFFSET(x) ((x) * rvu->hw->lmac_per_cgx) + /* Returns bitmap of mapped PFs */ +-static u16 cgxlmac_to_pfmap(struct rvu *rvu, u8 cgx_id, u8 lmac_id) ++static u64 cgxlmac_to_pfmap(struct rvu *rvu, u8 cgx_id, u8 lmac_id) + { + return rvu->cgxlmac2pf_map[CGX_OFFSET(cgx_id) + lmac_id]; + } +@@ -71,7 +72,8 @@ int cgxlmac_to_pf(struct rvu *rvu, int cgx_id, int lmac_id) + if (!pfmap) + return -ENODEV; + else +- return find_first_bit(&pfmap, 16); ++ return find_first_bit(&pfmap, ++ rvu->cgx_cnt_max * rvu->hw->lmac_per_cgx); + } + + static u8 cgxlmac_id_to_bmap(u8 cgx_id, u8 lmac_id) +@@ -129,14 +131,14 @@ static int rvu_map_cgx_lmac_pf(struct rvu *rvu) + if (!cgx_cnt_max) + return 0; + +- if (cgx_cnt_max > 0xF || MAX_LMAC_PER_CGX > 0xF) ++ if (cgx_cnt_max > 0xF || rvu->hw->lmac_per_cgx > 0xF) + return -EINVAL; + + /* Alloc map table + * An additional entry is required since PF id starts from 1 and + * hence entry at offset 0 is invalid. + */ +- size = (cgx_cnt_max * MAX_LMAC_PER_CGX + 1) * sizeof(u8); ++ size = (cgx_cnt_max * rvu->hw->lmac_per_cgx + 1) * sizeof(u8); + rvu->pf2cgxlmac_map = devm_kmalloc(rvu->dev, size, GFP_KERNEL); + if (!rvu->pf2cgxlmac_map) + return -ENOMEM; +@@ -145,9 +147,10 @@ static int rvu_map_cgx_lmac_pf(struct rvu *rvu) + memset(rvu->pf2cgxlmac_map, 0xFF, size); + + /* Reverse map table */ +- rvu->cgxlmac2pf_map = devm_kzalloc(rvu->dev, +- cgx_cnt_max * MAX_LMAC_PER_CGX * sizeof(u16), +- GFP_KERNEL); ++ rvu->cgxlmac2pf_map = ++ devm_kzalloc(rvu->dev, ++ cgx_cnt_max * rvu->hw->lmac_per_cgx * sizeof(u64), ++ GFP_KERNEL); + if (!rvu->cgxlmac2pf_map) + return -ENOMEM; + +@@ -156,7 +159,7 @@ static int rvu_map_cgx_lmac_pf(struct rvu *rvu) + if (!rvu_cgx_pdata(cgx, rvu)) + continue; + lmac_bmap = cgx_get_lmac_bmap(rvu_cgx_pdata(cgx, rvu)); +- for_each_set_bit(iter, &lmac_bmap, MAX_LMAC_PER_CGX) { ++ for_each_set_bit(iter, &lmac_bmap, rvu->hw->lmac_per_cgx) { + lmac = cgx_get_lmacid(rvu_cgx_pdata(cgx, rvu), + iter); + rvu->pf2cgxlmac_map[pf] = cgxlmac_id_to_bmap(cgx, lmac); +@@ -235,7 +238,8 @@ static void cgx_notify_pfs(struct cgx_link_event *event, struct rvu *rvu) + pfmap = cgxlmac_to_pfmap(rvu, event->cgx_id, event->lmac_id); + + do { +- pfid = find_first_bit(&pfmap, 16); ++ pfid = find_first_bit(&pfmap, ++ rvu->cgx_cnt_max * rvu->hw->lmac_per_cgx); + clear_bit(pfid, &pfmap); + + /* check if notification is enabled */ +@@ -310,7 +314,7 @@ static int cgx_lmac_event_handler_init(struct rvu *rvu) + if (!cgxd) + continue; + lmac_bmap = cgx_get_lmac_bmap(cgxd); +- for_each_set_bit(lmac, &lmac_bmap, MAX_LMAC_PER_CGX) { ++ for_each_set_bit(lmac, &lmac_bmap, rvu->hw->lmac_per_cgx) { + err = cgx_lmac_evh_register(&cb, cgxd, lmac); + if (err) + dev_err(rvu->dev, +@@ -396,7 +400,7 @@ int rvu_cgx_exit(struct rvu *rvu) + if (!cgxd) + continue; + lmac_bmap = cgx_get_lmac_bmap(cgxd); +- for_each_set_bit(lmac, &lmac_bmap, MAX_LMAC_PER_CGX) ++ for_each_set_bit(lmac, &lmac_bmap, rvu->hw->lmac_per_cgx) + cgx_lmac_evh_unregister(cgxd, lmac); + } + +@@ -456,6 +460,23 @@ int rvu_cgx_config_rxtx(struct rvu *rvu, u16 pcifunc, bool start) + return mac_ops->mac_rx_tx_enable(cgxd, lmac_id, start); + } + ++int rvu_cgx_tx_enable(struct rvu *rvu, u16 pcifunc, bool enable) ++{ ++ int pf = rvu_get_pf(pcifunc); ++ struct mac_ops *mac_ops; ++ u8 cgx_id, lmac_id; ++ void *cgxd; ++ ++ if (!is_cgx_config_permitted(rvu, pcifunc)) ++ return LMAC_AF_ERR_PERM_DENIED; ++ ++ rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); ++ cgxd = rvu_cgx_pdata(cgx_id, rvu); ++ mac_ops = get_mac_ops(cgxd); ++ ++ return mac_ops->mac_tx_enable(cgxd, lmac_id, enable); ++} ++ + int rvu_cgx_config_tx(void *cgxd, int lmac_id, bool enable) + { + struct mac_ops *mac_ops; +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +index 5c9dc3f9262f5..cc5d342e026c7 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +@@ -2618,7 +2618,7 @@ static void rvu_dbg_cgx_init(struct rvu *rvu) + rvu->rvu_dbg.cgx = debugfs_create_dir(dname, + rvu->rvu_dbg.cgx_root); + +- for_each_set_bit(lmac_id, &lmac_bmap, MAX_LMAC_PER_CGX) { ++ for_each_set_bit(lmac_id, &lmac_bmap, rvu->hw->lmac_per_cgx) { + /* lmac debugfs dir */ + sprintf(dname, "lmac%d", lmac_id); + rvu->rvu_dbg.lmac = +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +index 959f36efdc4a6..bb99302eab67a 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +@@ -3923,90 +3923,18 @@ static void nix_find_link_frs(struct rvu *rvu, + req->minlen = minlen; + } + +-static int +-nix_config_link_credits(struct rvu *rvu, int blkaddr, int link, +- u16 pcifunc, u64 tx_credits) +-{ +- struct rvu_hwinfo *hw = rvu->hw; +- int pf = rvu_get_pf(pcifunc); +- u8 cgx_id = 0, lmac_id = 0; +- unsigned long poll_tmo; +- bool restore_tx_en = 0; +- struct nix_hw *nix_hw; +- u64 cfg, sw_xoff = 0; +- u32 schq = 0; +- u32 credits; +- int rc; +- +- nix_hw = get_nix_hw(rvu->hw, blkaddr); +- if (!nix_hw) +- return NIX_AF_ERR_INVALID_NIXBLK; +- +- if (tx_credits == nix_hw->tx_credits[link]) +- return 0; +- +- /* Enable cgx tx if disabled for credits to be back */ +- if (is_pf_cgxmapped(rvu, pf)) { +- rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); +- restore_tx_en = !rvu_cgx_config_tx(rvu_cgx_pdata(cgx_id, rvu), +- lmac_id, true); +- } +- +- mutex_lock(&rvu->rsrc_lock); +- /* Disable new traffic to link */ +- if (hw->cap.nix_shaping) { +- schq = nix_get_tx_link(rvu, pcifunc); +- sw_xoff = rvu_read64(rvu, blkaddr, NIX_AF_TL1X_SW_XOFF(schq)); +- rvu_write64(rvu, blkaddr, +- NIX_AF_TL1X_SW_XOFF(schq), BIT_ULL(0)); +- } +- +- rc = NIX_AF_ERR_LINK_CREDITS; +- poll_tmo = jiffies + usecs_to_jiffies(200000); +- /* Wait for credits to return */ +- do { +- if (time_after(jiffies, poll_tmo)) +- goto exit; +- usleep_range(100, 200); +- +- cfg = rvu_read64(rvu, blkaddr, +- NIX_AF_TX_LINKX_NORM_CREDIT(link)); +- credits = (cfg >> 12) & 0xFFFFFULL; +- } while (credits != nix_hw->tx_credits[link]); +- +- cfg &= ~(0xFFFFFULL << 12); +- cfg |= (tx_credits << 12); +- rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link), cfg); +- rc = 0; +- +- nix_hw->tx_credits[link] = tx_credits; +- +-exit: +- /* Enable traffic back */ +- if (hw->cap.nix_shaping && !sw_xoff) +- rvu_write64(rvu, blkaddr, NIX_AF_TL1X_SW_XOFF(schq), 0); +- +- /* Restore state of cgx tx */ +- if (restore_tx_en) +- rvu_cgx_config_tx(rvu_cgx_pdata(cgx_id, rvu), lmac_id, false); +- +- mutex_unlock(&rvu->rsrc_lock); +- return rc; +-} +- + int rvu_mbox_handler_nix_set_hw_frs(struct rvu *rvu, struct nix_frs_cfg *req, + struct msg_rsp *rsp) + { + struct rvu_hwinfo *hw = rvu->hw; + u16 pcifunc = req->hdr.pcifunc; + int pf = rvu_get_pf(pcifunc); +- int blkaddr, schq, link = -1; +- struct nix_txsch *txsch; +- u64 cfg, lmac_fifo_len; ++ int blkaddr, link = -1; + struct nix_hw *nix_hw; + struct rvu_pfvf *pfvf; + u8 cgx = 0, lmac = 0; + u16 max_mtu; ++ u64 cfg; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc); + if (blkaddr < 0) +@@ -4027,25 +3955,6 @@ int rvu_mbox_handler_nix_set_hw_frs(struct rvu *rvu, struct nix_frs_cfg *req, + if (req->update_minlen && req->minlen < NIC_HW_MIN_FRS) + return NIX_AF_ERR_FRS_INVALID; + +- /* Check if requester wants to update SMQ's */ +- if (!req->update_smq) +- goto rx_frscfg; +- +- /* Update min/maxlen in each of the SMQ attached to this PF/VF */ +- txsch = &nix_hw->txsch[NIX_TXSCH_LVL_SMQ]; +- mutex_lock(&rvu->rsrc_lock); +- for (schq = 0; schq < txsch->schq.max; schq++) { +- if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc) +- continue; +- cfg = rvu_read64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq)); +- cfg = (cfg & ~(0xFFFFULL << 8)) | ((u64)req->maxlen << 8); +- if (req->update_minlen) +- cfg = (cfg & ~0x7FULL) | ((u64)req->minlen & 0x7F); +- rvu_write64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq), cfg); +- } +- mutex_unlock(&rvu->rsrc_lock); +- +-rx_frscfg: + /* Check if config is for SDP link */ + if (req->sdp_link) { + if (!hw->sdp_links) +@@ -4068,7 +3977,6 @@ rx_frscfg: + if (link < 0) + return NIX_AF_ERR_RX_LINK_INVALID; + +- + linkcfg: + nix_find_link_frs(rvu, req, pcifunc); + +@@ -4078,19 +3986,7 @@ linkcfg: + cfg = (cfg & ~0xFFFFULL) | req->minlen; + rvu_write64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link), cfg); + +- if (req->sdp_link || pf == 0) +- return 0; +- +- /* Update transmit credits for CGX links */ +- lmac_fifo_len = rvu_cgx_get_lmac_fifolen(rvu, cgx, lmac); +- if (!lmac_fifo_len) { +- dev_err(rvu->dev, +- "%s: Failed to get CGX/RPM%d:LMAC%d FIFO size\n", +- __func__, cgx, lmac); +- return 0; +- } +- return nix_config_link_credits(rvu, blkaddr, link, pcifunc, +- (lmac_fifo_len - req->maxlen) / 16); ++ return 0; + } + + int rvu_mbox_handler_nix_set_rx_cfg(struct rvu *rvu, struct nix_rx_cfg *req, +@@ -4183,7 +4079,7 @@ static void nix_link_config(struct rvu *rvu, int blkaddr, + + /* Get LMAC id's from bitmap */ + lmac_bmap = cgx_get_lmac_bmap(rvu_cgx_pdata(cgx, rvu)); +- for_each_set_bit(iter, &lmac_bmap, MAX_LMAC_PER_CGX) { ++ for_each_set_bit(iter, &lmac_bmap, rvu->hw->lmac_per_cgx) { + lmac_fifo_len = rvu_cgx_get_lmac_fifolen(rvu, cgx, iter); + if (!lmac_fifo_len) { + dev_err(rvu->dev, +@@ -4610,7 +4506,13 @@ int rvu_mbox_handler_nix_lf_stop_rx(struct rvu *rvu, struct msg_req *req, + pfvf = rvu_get_pfvf(rvu, pcifunc); + clear_bit(NIXLF_INITIALIZED, &pfvf->flags); + +- return rvu_cgx_start_stop_io(rvu, pcifunc, false); ++ err = rvu_cgx_start_stop_io(rvu, pcifunc, false); ++ if (err) ++ return err; ++ ++ rvu_cgx_tx_enable(rvu, pcifunc, true); ++ ++ return 0; + } + + #define RX_SA_BASE GENMASK_ULL(52, 7) +diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c +index 34fa59575fa91..54e0dfdc9d984 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c +@@ -1999,7 +1999,9 @@ int rvu_npc_exact_init(struct rvu *rvu) + /* Install SDP drop rule */ + drop_mcam_idx = &table->num_drop_rules; + +- max_lmac_cnt = rvu->cgx_cnt_max * MAX_LMAC_PER_CGX + PF_CGXMAP_BASE; ++ max_lmac_cnt = rvu->cgx_cnt_max * rvu->hw->lmac_per_cgx + ++ PF_CGXMAP_BASE; ++ + for (i = PF_CGXMAP_BASE; i < max_lmac_cnt; i++) { + if (rvu->pf2cgxlmac_map[i] == 0xFF) + continue; +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c +index d136360ac6a98..a6d3fc96e1685 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c +@@ -25,7 +25,7 @@ + struct mlx5_irq { + struct atomic_notifier_head nh; + cpumask_var_t mask; +- char name[MLX5_MAX_IRQ_NAME]; ++ char name[MLX5_MAX_IRQ_FORMATTED_NAME]; + struct mlx5_irq_pool *pool; + int refcount; + u32 index; +@@ -236,8 +236,8 @@ struct mlx5_irq *mlx5_irq_alloc(struct mlx5_irq_pool *pool, int i, + else + irq_sf_set_name(pool, name, i); + ATOMIC_INIT_NOTIFIER_HEAD(&irq->nh); +- snprintf(irq->name, MLX5_MAX_IRQ_NAME, +- "%s@pci:%s", name, pci_name(dev->pdev)); ++ snprintf(irq->name, MLX5_MAX_IRQ_FORMATTED_NAME, ++ MLX5_IRQ_NAME_FORMAT_STR, name, pci_name(dev->pdev)); + err = request_irq(irq->irqn, irq_int_handler, 0, irq->name, + &irq->nh); + if (err) { +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h +index 5c7e68bee43a0..4047179307c4a 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h ++++ b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h +@@ -7,6 +7,9 @@ + #include + + #define MLX5_MAX_IRQ_NAME (32) ++#define MLX5_IRQ_NAME_FORMAT_STR ("%s@pci:%s") ++#define MLX5_MAX_IRQ_FORMATTED_NAME \ ++ (MLX5_MAX_IRQ_NAME + sizeof(MLX5_IRQ_NAME_FORMAT_STR)) + /* max irq_index is 2047, so four chars */ + #define MLX5_MAX_IRQ_IDX_CHARS (4) + #define MLX5_EQ_REFS_PER_IRQ (2) +diff --git a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c +index 0d5a41a2ae010..227d01cace3f0 100644 +--- a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c ++++ b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c +@@ -267,6 +267,13 @@ static bool mlxbf_gige_rx_packet(struct mlxbf_gige *priv, int *rx_pkts) + priv->stats.rx_truncate_errors++; + } + ++ /* Read receive consumer index before replenish so that this routine ++ * returns accurate return value even if packet is received into ++ * just-replenished buffer prior to exiting this routine. ++ */ ++ rx_ci = readq(priv->base + MLXBF_GIGE_RX_CQE_PACKET_CI); ++ rx_ci_rem = rx_ci % priv->rx_q_entries; ++ + /* Let hardware know we've replenished one buffer */ + rx_pi++; + +@@ -279,8 +286,6 @@ static bool mlxbf_gige_rx_packet(struct mlxbf_gige *priv, int *rx_pkts) + rx_pi_rem = rx_pi % priv->rx_q_entries; + if (rx_pi_rem == 0) + priv->valid_polarity ^= 1; +- rx_ci = readq(priv->base + MLXBF_GIGE_RX_CQE_PACKET_CI); +- rx_ci_rem = rx_ci % priv->rx_q_entries; + + if (skb) + netif_receive_skb(skb); +diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c +index 0d57ffcedf0c6..fc78bc959ded8 100644 +--- a/drivers/net/ethernet/qlogic/qla3xxx.c ++++ b/drivers/net/ethernet/qlogic/qla3xxx.c +@@ -2591,6 +2591,7 @@ static int ql_alloc_buffer_queues(struct ql3_adapter *qdev) + + if (qdev->lrg_buf_q_alloc_virt_addr == NULL) { + netdev_err(qdev->ndev, "lBufQ failed\n"); ++ kfree(qdev->lrg_buf); + return -ENOMEM; + } + qdev->lrg_buf_q_virt_addr = qdev->lrg_buf_q_alloc_virt_addr; +@@ -2615,6 +2616,7 @@ static int ql_alloc_buffer_queues(struct ql3_adapter *qdev) + qdev->lrg_buf_q_alloc_size, + qdev->lrg_buf_q_alloc_virt_addr, + qdev->lrg_buf_q_alloc_phy_addr); ++ kfree(qdev->lrg_buf); + return -ENOMEM; + } + +diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c +index d22457f2cf9cf..06663c11ca96d 100644 +--- a/drivers/net/ethernet/realtek/r8169_main.c ++++ b/drivers/net/ethernet/realtek/r8169_main.c +@@ -1145,7 +1145,7 @@ static void rtl8168ep_driver_start(struct rtl8169_private *tp) + { + r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_START); + r8168ep_ocp_write(tp, 0x01, 0x30, r8168ep_ocp_read(tp, 0x30) | 0x01); +- rtl_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10000, 10); ++ rtl_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10000, 30); + } + + static void rtl8168_driver_start(struct rtl8169_private *tp) +diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c +index 68cb5616ef991..c2c56a5289caf 100644 +--- a/drivers/net/ethernet/renesas/ravb_main.c ++++ b/drivers/net/ethernet/renesas/ravb_main.c +@@ -68,16 +68,27 @@ int ravb_wait(struct net_device *ndev, enum ravb_reg reg, u32 mask, u32 value) + return -ETIMEDOUT; + } + +-static int ravb_config(struct net_device *ndev) ++static int ravb_set_opmode(struct net_device *ndev, u32 opmode) + { ++ u32 csr_ops = 1U << (opmode & CCC_OPC); ++ u32 ccc_mask = CCC_OPC; + int error; + +- /* Set config mode */ +- ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_CONFIG); +- /* Check if the operating mode is changed to the config mode */ +- error = ravb_wait(ndev, CSR, CSR_OPS, CSR_OPS_CONFIG); +- if (error) +- netdev_err(ndev, "failed to switch device to config mode\n"); ++ /* If gPTP active in config mode is supported it needs to be configured ++ * along with CSEL and operating mode in the same access. This is a ++ * hardware limitation. ++ */ ++ if (opmode & CCC_GAC) ++ ccc_mask |= CCC_GAC | CCC_CSEL; ++ ++ /* Set operating mode */ ++ ravb_modify(ndev, CCC, ccc_mask, opmode); ++ /* Check if the operating mode is changed to the requested one */ ++ error = ravb_wait(ndev, CSR, CSR_OPS, csr_ops); ++ if (error) { ++ netdev_err(ndev, "failed to switch device to requested mode (%u)\n", ++ opmode & CCC_OPC); ++ } + + return error; + } +@@ -675,7 +686,7 @@ static int ravb_dmac_init(struct net_device *ndev) + int error; + + /* Set CONFIG mode */ +- error = ravb_config(ndev); ++ error = ravb_set_opmode(ndev, CCC_OPC_CONFIG); + if (error) + return error; + +@@ -684,9 +695,7 @@ static int ravb_dmac_init(struct net_device *ndev) + return error; + + /* Setting the control will start the AVB-DMAC process. */ +- ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_OPERATION); +- +- return 0; ++ return ravb_set_opmode(ndev, CCC_OPC_OPERATION); + } + + static void ravb_get_tx_tstamp(struct net_device *ndev) +@@ -1048,7 +1057,7 @@ static int ravb_stop_dma(struct net_device *ndev) + return error; + + /* Stop AVB-DMAC process */ +- return ravb_config(ndev); ++ return ravb_set_opmode(ndev, CCC_OPC_CONFIG); + } + + /* E-MAC interrupt handler */ +@@ -2576,21 +2585,25 @@ static int ravb_set_gti(struct net_device *ndev) + return 0; + } + +-static void ravb_set_config_mode(struct net_device *ndev) ++static int ravb_set_config_mode(struct net_device *ndev) + { + struct ravb_private *priv = netdev_priv(ndev); + const struct ravb_hw_info *info = priv->info; ++ int error; + + if (info->gptp) { +- ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_CONFIG); ++ error = ravb_set_opmode(ndev, CCC_OPC_CONFIG); ++ if (error) ++ return error; + /* Set CSEL value */ + ravb_modify(ndev, CCC, CCC_CSEL, CCC_CSEL_HPB); + } else if (info->ccc_gac) { +- ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_CONFIG | +- CCC_GAC | CCC_CSEL_HPB); ++ error = ravb_set_opmode(ndev, CCC_OPC_CONFIG | CCC_GAC | CCC_CSEL_HPB); + } else { +- ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_CONFIG); ++ error = ravb_set_opmode(ndev, CCC_OPC_CONFIG); + } ++ ++ return error; + } + + /* Set tx and rx clock internal delay modes */ +@@ -2810,7 +2823,9 @@ static int ravb_probe(struct platform_device *pdev) + ndev->ethtool_ops = &ravb_ethtool_ops; + + /* Set AVB config mode */ +- ravb_set_config_mode(ndev); ++ error = ravb_set_config_mode(ndev); ++ if (error) ++ goto out_disable_gptp_clk; + + if (info->gptp || info->ccc_gac) { + /* Set GTI value */ +@@ -2933,8 +2948,7 @@ static int ravb_remove(struct platform_device *pdev) + dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat, + priv->desc_bat_dma); + +- /* Set reset mode */ +- ravb_write(ndev, CCC_OPC_RESET, CCC); ++ ravb_set_opmode(ndev, CCC_OPC_RESET); + + clk_disable_unprepare(priv->gptp_clk); + clk_disable_unprepare(priv->refclk); +@@ -3018,8 +3032,11 @@ static int __maybe_unused ravb_resume(struct device *dev) + int ret = 0; + + /* If WoL is enabled set reset mode to rearm the WoL logic */ +- if (priv->wol_enabled) +- ravb_write(ndev, CCC_OPC_RESET, CCC); ++ if (priv->wol_enabled) { ++ ret = ravb_set_opmode(ndev, CCC_OPC_RESET); ++ if (ret) ++ return ret; ++ } + + /* All register have been reset to default values. + * Restore all registers which where setup at probe time and +@@ -3027,7 +3044,9 @@ static int __maybe_unused ravb_resume(struct device *dev) + */ + + /* Set AVB config mode */ +- ravb_set_config_mode(ndev); ++ ret = ravb_set_config_mode(ndev); ++ if (ret) ++ return ret; + + if (info->gptp || info->ccc_gac) { + /* Set GTI value */ +diff --git a/drivers/net/ethernet/sfc/rx_common.c b/drivers/net/ethernet/sfc/rx_common.c +index 9220afeddee81..3f290791df1c4 100644 +--- a/drivers/net/ethernet/sfc/rx_common.c ++++ b/drivers/net/ethernet/sfc/rx_common.c +@@ -820,8 +820,10 @@ int efx_probe_filters(struct efx_nic *efx) + } + + if (!success) { +- efx_for_each_channel(channel, efx) ++ efx_for_each_channel(channel, efx) { + kfree(channel->rps_flow_id); ++ channel->rps_flow_id = NULL; ++ } + efx->type->filter_table_remove(efx); + rc = -ENOMEM; + goto out_unlock; +diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c +index 477b4d4f860bd..bace989591f75 100644 +--- a/drivers/net/gtp.c ++++ b/drivers/net/gtp.c +@@ -629,7 +629,7 @@ static void __gtp_encap_destroy(struct sock *sk) + gtp->sk0 = NULL; + else + gtp->sk1u = NULL; +- udp_sk(sk)->encap_type = 0; ++ WRITE_ONCE(udp_sk(sk)->encap_type, 0); + rcu_assign_sk_user_data(sk, NULL); + release_sock(sk); + sock_put(sk); +@@ -681,7 +681,7 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) + + netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk); + +- switch (udp_sk(sk)->encap_type) { ++ switch (READ_ONCE(udp_sk(sk)->encap_type)) { + case UDP_ENCAP_GTP0: + netdev_dbg(gtp->dev, "received GTP0 packet\n"); + ret = gtp0_udp_encap_recv(gtp, skb); +diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c +index 3777c7e2e6fc0..e47bb125048d4 100644 +--- a/drivers/net/usb/ax88172a.c ++++ b/drivers/net/usb/ax88172a.c +@@ -161,7 +161,9 @@ static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf) + u8 buf[ETH_ALEN]; + struct ax88172a_private *priv; + +- usbnet_get_endpoints(dev, intf); ++ ret = usbnet_get_endpoints(dev, intf); ++ if (ret) ++ return ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) +diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +index 157d1f31c4871..c5a306b01fe20 100644 +--- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h ++++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +@@ -348,8 +348,8 @@ + #define RFIC_REG_RD 0xAD0470 + #define WFPM_CTRL_REG 0xA03030 + #define WFPM_OTP_CFG1_ADDR 0x00a03098 +-#define WFPM_OTP_CFG1_IS_JACKET_BIT BIT(4) +-#define WFPM_OTP_CFG1_IS_CDB_BIT BIT(5) ++#define WFPM_OTP_CFG1_IS_JACKET_BIT BIT(5) ++#define WFPM_OTP_CFG1_IS_CDB_BIT BIT(4) + + #define WFPM_GP2 0xA030B4 + +diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +index 69b95ad5993b0..2ec4ee8ab317c 100644 +--- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h ++++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +@@ -745,7 +745,7 @@ static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) + } + } + +-void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans); ++void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans, bool from_irq); + + static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) + { +@@ -792,7 +792,7 @@ static inline bool iwl_pcie_dbg_on(struct iwl_trans *trans) + return (trans->dbg.dest_tlv || iwl_trans_dbg_ini_valid(trans)); + } + +-void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state); ++void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq); + void iwl_trans_pcie_dump_regs(struct iwl_trans *trans); + + #ifdef CONFIG_IWLWIFI_DEBUGFS +diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +index 90a46faaaffdf..57a11ee05bc36 100644 +--- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c ++++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +@@ -1781,7 +1781,7 @@ static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans) + return inta; + } + +-void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans) ++void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans, bool from_irq) + { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct isr_statistics *isr_stats = &trans_pcie->isr_stats; +@@ -1805,7 +1805,7 @@ void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans) + isr_stats->rfkill++; + + if (prev != report) +- iwl_trans_pcie_rf_kill(trans, report); ++ iwl_trans_pcie_rf_kill(trans, report, from_irq); + mutex_unlock(&trans_pcie->mutex); + + if (hw_rfkill) { +@@ -1945,7 +1945,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) + + /* HW RF KILL switch toggled */ + if (inta & CSR_INT_BIT_RF_KILL) { +- iwl_pcie_handle_rfkill_irq(trans); ++ iwl_pcie_handle_rfkill_irq(trans, true); + handled |= CSR_INT_BIT_RF_KILL; + } + +@@ -2362,7 +2362,7 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) + + /* HW RF KILL switch toggled */ + if (inta_hw & MSIX_HW_INT_CAUSES_REG_RF_KILL) +- iwl_pcie_handle_rfkill_irq(trans); ++ iwl_pcie_handle_rfkill_irq(trans, true); + + if (inta_hw & MSIX_HW_INT_CAUSES_REG_HW_ERR) { + IWL_ERR(trans, +diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +index 796972f224326..c7ed35b3dd8d5 100644 +--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c ++++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +@@ -1080,7 +1080,7 @@ bool iwl_pcie_check_hw_rf_kill(struct iwl_trans *trans) + report = test_bit(STATUS_RFKILL_OPMODE, &trans->status); + + if (prev != report) +- iwl_trans_pcie_rf_kill(trans, report); ++ iwl_trans_pcie_rf_kill(trans, report, false); + + return hw_rfkill; + } +@@ -1234,7 +1234,7 @@ static void iwl_pcie_init_msix(struct iwl_trans_pcie *trans_pcie) + trans_pcie->hw_mask = trans_pcie->hw_init_mask; + } + +-static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans) ++static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool from_irq) + { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + +@@ -1261,7 +1261,8 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans) + if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) { + IWL_DEBUG_INFO(trans, + "DEVICE_ENABLED bit was set and is now cleared\n"); +- iwl_pcie_synchronize_irqs(trans); ++ if (!from_irq) ++ iwl_pcie_synchronize_irqs(trans); + iwl_pcie_rx_napi_sync(trans); + iwl_pcie_tx_stop(trans); + iwl_pcie_rx_stop(trans); +@@ -1451,7 +1452,7 @@ void iwl_trans_pcie_handle_stop_rfkill(struct iwl_trans *trans, + clear_bit(STATUS_RFKILL_OPMODE, &trans->status); + } + if (hw_rfkill != was_in_rfkill) +- iwl_trans_pcie_rf_kill(trans, hw_rfkill); ++ iwl_trans_pcie_rf_kill(trans, hw_rfkill, false); + } + + static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) +@@ -1466,12 +1467,12 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) + mutex_lock(&trans_pcie->mutex); + trans_pcie->opmode_down = true; + was_in_rfkill = test_bit(STATUS_RFKILL_OPMODE, &trans->status); +- _iwl_trans_pcie_stop_device(trans); ++ _iwl_trans_pcie_stop_device(trans, false); + iwl_trans_pcie_handle_stop_rfkill(trans, was_in_rfkill); + mutex_unlock(&trans_pcie->mutex); + } + +-void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state) ++void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq) + { + struct iwl_trans_pcie __maybe_unused *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); +@@ -1484,7 +1485,7 @@ void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state) + if (trans->trans_cfg->gen2) + _iwl_trans_pcie_gen2_stop_device(trans); + else +- _iwl_trans_pcie_stop_device(trans); ++ _iwl_trans_pcie_stop_device(trans, from_irq); + } + } + +@@ -2815,7 +2816,7 @@ static ssize_t iwl_dbgfs_rfkill_write(struct file *file, + IWL_WARN(trans, "changing debug rfkill %d->%d\n", + trans_pcie->debug_rfkill, new_value); + trans_pcie->debug_rfkill = new_value; +- iwl_pcie_handle_rfkill_irq(trans); ++ iwl_pcie_handle_rfkill_irq(trans, false); + + return count; + } +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index 8df156c28aade..5368a37154cf9 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -1302,6 +1302,9 @@ static int pci_set_full_power_state(struct pci_dev *dev) + pci_restore_bars(dev); + } + ++ if (dev->bus->self) ++ pcie_aspm_pm_state_change(dev->bus->self); ++ + return 0; + } + +@@ -1396,6 +1399,9 @@ static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state) + pci_power_name(dev->current_state), + pci_power_name(state)); + ++ if (dev->bus->self) ++ pcie_aspm_pm_state_change(dev->bus->self); ++ + return 0; + } + +diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h +index ffccb03933e27..ed6d75d138c7a 100644 +--- a/drivers/pci/pci.h ++++ b/drivers/pci/pci.h +@@ -561,10 +561,12 @@ bool pcie_wait_for_link(struct pci_dev *pdev, bool active); + #ifdef CONFIG_PCIEASPM + void pcie_aspm_init_link_state(struct pci_dev *pdev); + void pcie_aspm_exit_link_state(struct pci_dev *pdev); ++void pcie_aspm_pm_state_change(struct pci_dev *pdev); + void pcie_aspm_powersave_config_link(struct pci_dev *pdev); + #else + static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { } + static inline void pcie_aspm_exit_link_state(struct pci_dev *pdev) { } ++static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev) { } + static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) { } + #endif + +diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c +index 5d1756f53ba84..25736d408e88e 100644 +--- a/drivers/pci/pcie/aspm.c ++++ b/drivers/pci/pcie/aspm.c +@@ -1055,6 +1055,25 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) + up_read(&pci_bus_sem); + } + ++/* @pdev: the root port or switch downstream port */ ++void pcie_aspm_pm_state_change(struct pci_dev *pdev) ++{ ++ struct pcie_link_state *link = pdev->link_state; ++ ++ if (aspm_disabled || !link) ++ return; ++ /* ++ * Devices changed PM state, we should recheck if latency ++ * meets all functions' requirement ++ */ ++ down_read(&pci_bus_sem); ++ mutex_lock(&aspm_lock); ++ pcie_update_aspm_capable(link->root); ++ pcie_config_aspm_path(link); ++ mutex_unlock(&aspm_lock); ++ up_read(&pci_bus_sem); ++} ++ + void pcie_aspm_powersave_config_link(struct pci_dev *pdev) + { + struct pcie_link_state *link = pdev->link_state; +diff --git a/drivers/video/fbdev/imsttfb.c b/drivers/video/fbdev/imsttfb.c +index b194e71f07bfc..aa51cb72cbba5 100644 +--- a/drivers/video/fbdev/imsttfb.c ++++ b/drivers/video/fbdev/imsttfb.c +@@ -1419,7 +1419,6 @@ static int init_imstt(struct fb_info *info) + if ((info->var.xres * info->var.yres) * (info->var.bits_per_pixel >> 3) > info->fix.smem_len + || !(compute_imstt_regvals(par, info->var.xres, info->var.yres))) { + printk("imsttfb: %ux%ux%u not supported\n", info->var.xres, info->var.yres, info->var.bits_per_pixel); +- framebuffer_release(info); + return -ENODEV; + } + +@@ -1452,10 +1451,11 @@ static int init_imstt(struct fb_info *info) + FBINFO_HWACCEL_FILLRECT | + FBINFO_HWACCEL_YPAN; + +- fb_alloc_cmap(&info->cmap, 0, 0); ++ if (fb_alloc_cmap(&info->cmap, 0, 0)) ++ return -ENODEV; + + if (register_framebuffer(info) < 0) { +- framebuffer_release(info); ++ fb_dealloc_cmap(&info->cmap); + return -ENODEV; + } + +diff --git a/fs/9p/cache.c b/fs/9p/cache.c +index cebba4eaa0b57..12c0ae29f1857 100644 +--- a/fs/9p/cache.c ++++ b/fs/9p/cache.c +@@ -68,6 +68,8 @@ void v9fs_cache_inode_get_cookie(struct inode *inode) + &path, sizeof(path), + &version, sizeof(version), + i_size_read(&v9inode->netfs.inode)); ++ if (v9inode->netfs.cache) ++ mapping_set_release_always(inode->i_mapping); + + p9_debug(P9_DEBUG_FSC, "inode %p get cookie %p\n", + inode, v9fs_inode_cookie(v9inode)); +diff --git a/fs/afs/internal.h b/fs/afs/internal.h +index fcbb598d8c85d..a25fdc3e52310 100644 +--- a/fs/afs/internal.h ++++ b/fs/afs/internal.h +@@ -682,6 +682,8 @@ static inline void afs_vnode_set_cache(struct afs_vnode *vnode, + { + #ifdef CONFIG_AFS_FSCACHE + vnode->netfs.cache = cookie; ++ if (cookie) ++ mapping_set_release_always(vnode->netfs.inode.i_mapping); + #endif + } + +diff --git a/fs/btrfs/delalloc-space.c b/fs/btrfs/delalloc-space.c +index 0b62ce77053f5..f2bc5563c0f92 100644 +--- a/fs/btrfs/delalloc-space.c ++++ b/fs/btrfs/delalloc-space.c +@@ -197,7 +197,7 @@ void btrfs_free_reserved_data_space(struct btrfs_inode *inode, + start = round_down(start, fs_info->sectorsize); + + btrfs_free_reserved_data_space_noquota(fs_info, len); +- btrfs_qgroup_free_data(inode, reserved, start, len); ++ btrfs_qgroup_free_data(inode, reserved, start, len, NULL); + } + + /** +diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c +index b14d2da9b26d3..14478da875313 100644 +--- a/fs/btrfs/file-item.c ++++ b/fs/btrfs/file-item.c +@@ -602,7 +602,7 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, + } + + sums->bytenr = start; +- sums->len = (int)size; ++ sums->len = size; + + offset = (start - key.offset) >> fs_info->sectorsize_bits; + offset *= csum_size; +diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c +index 0a46fff3dd067..1783a0fbf1665 100644 +--- a/fs/btrfs/file.c ++++ b/fs/btrfs/file.c +@@ -3191,7 +3191,7 @@ static long btrfs_fallocate(struct file *file, int mode, + qgroup_reserved -= range->len; + } else if (qgroup_reserved > 0) { + btrfs_qgroup_free_data(BTRFS_I(inode), data_reserved, +- range->start, range->len); ++ range->start, range->len, NULL); + qgroup_reserved -= range->len; + } + list_del(&range->list); +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index 81eac121c6b23..9a7d77c410e22 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -466,7 +466,7 @@ out: + * And at reserve time, it's always aligned to page size, so + * just free one page here. + */ +- btrfs_qgroup_free_data(inode, NULL, 0, PAGE_SIZE); ++ btrfs_qgroup_free_data(inode, NULL, 0, PAGE_SIZE, NULL); + btrfs_free_path(path); + btrfs_end_transaction(trans); + return ret; +@@ -5372,7 +5372,7 @@ static void evict_inode_truncate_pages(struct inode *inode) + */ + if (state_flags & EXTENT_DELALLOC) + btrfs_qgroup_free_data(BTRFS_I(inode), NULL, start, +- end - start + 1); ++ end - start + 1, NULL); + + clear_extent_bit(io_tree, start, end, + EXTENT_CLEAR_ALL_BITS | EXTENT_DO_ACCOUNTING, +@@ -8440,7 +8440,7 @@ next: + * reserved data space. + * Since the IO will never happen for this page. + */ +- btrfs_qgroup_free_data(inode, NULL, cur, range_end + 1 - cur); ++ btrfs_qgroup_free_data(inode, NULL, cur, range_end + 1 - cur, NULL); + if (!inode_evicting) { + clear_extent_bit(tree, cur, range_end, EXTENT_LOCKED | + EXTENT_DELALLOC | EXTENT_UPTODATE | +@@ -9902,7 +9902,7 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent( + struct btrfs_path *path; + u64 start = ins->objectid; + u64 len = ins->offset; +- int qgroup_released; ++ u64 qgroup_released = 0; + int ret; + + memset(&stack_fi, 0, sizeof(stack_fi)); +@@ -9915,9 +9915,9 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent( + btrfs_set_stack_file_extent_compression(&stack_fi, BTRFS_COMPRESS_NONE); + /* Encryption and other encoding is reserved and all 0 */ + +- qgroup_released = btrfs_qgroup_release_data(inode, file_offset, len); +- if (qgroup_released < 0) +- return ERR_PTR(qgroup_released); ++ ret = btrfs_qgroup_release_data(inode, file_offset, len, &qgroup_released); ++ if (ret < 0) ++ return ERR_PTR(ret); + + if (trans) { + ret = insert_reserved_file_extent(trans, inode, +@@ -10903,7 +10903,7 @@ out_delalloc_release: + btrfs_delalloc_release_metadata(inode, disk_num_bytes, ret < 0); + out_qgroup_free_data: + if (ret < 0) +- btrfs_qgroup_free_data(inode, data_reserved, start, num_bytes); ++ btrfs_qgroup_free_data(inode, data_reserved, start, num_bytes, NULL); + out_free_data_space: + /* + * If btrfs_reserve_extent() succeeded, then we already decremented +diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c +index 0321753c16b9f..1b2af4785c0e2 100644 +--- a/fs/btrfs/ordered-data.c ++++ b/fs/btrfs/ordered-data.c +@@ -172,11 +172,12 @@ int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset, + struct rb_node *node; + struct btrfs_ordered_extent *entry; + int ret; ++ u64 qgroup_rsv = 0; + + if (flags & + ((1 << BTRFS_ORDERED_NOCOW) | (1 << BTRFS_ORDERED_PREALLOC))) { + /* For nocow write, we can release the qgroup rsv right now */ +- ret = btrfs_qgroup_free_data(inode, NULL, file_offset, num_bytes); ++ ret = btrfs_qgroup_free_data(inode, NULL, file_offset, num_bytes, &qgroup_rsv); + if (ret < 0) + return ret; + ret = 0; +@@ -185,7 +186,7 @@ int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset, + * The ordered extent has reserved qgroup space, release now + * and pass the reserved number for qgroup_record to free. + */ +- ret = btrfs_qgroup_release_data(inode, file_offset, num_bytes); ++ ret = btrfs_qgroup_release_data(inode, file_offset, num_bytes, &qgroup_rsv); + if (ret < 0) + return ret; + } +@@ -203,7 +204,7 @@ int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset, + entry->inode = igrab(&inode->vfs_inode); + entry->compress_type = compress_type; + entry->truncated_len = (u64)-1; +- entry->qgroup_rsv = ret; ++ entry->qgroup_rsv = qgroup_rsv; + entry->physical = (u64)-1; + + ASSERT((flags & ~BTRFS_ORDERED_TYPE_FLAGS) == 0); +diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h +index f59f2dbdb25ed..cc3ca4bb9bd54 100644 +--- a/fs/btrfs/ordered-data.h ++++ b/fs/btrfs/ordered-data.h +@@ -20,7 +20,7 @@ struct btrfs_ordered_sum { + /* + * this is the length in bytes covered by the sums array below. + */ +- int len; ++ u32 len; + struct list_head list; + /* last field is a variable length array of csums */ + u8 sums[]; +diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c +index 26cabffd59710..96ec9ccc2ef61 100644 +--- a/fs/btrfs/qgroup.c ++++ b/fs/btrfs/qgroup.c +@@ -3833,13 +3833,14 @@ int btrfs_qgroup_reserve_data(struct btrfs_inode *inode, + + /* Free ranges specified by @reserved, normally in error path */ + static int qgroup_free_reserved_data(struct btrfs_inode *inode, +- struct extent_changeset *reserved, u64 start, u64 len) ++ struct extent_changeset *reserved, ++ u64 start, u64 len, u64 *freed_ret) + { + struct btrfs_root *root = inode->root; + struct ulist_node *unode; + struct ulist_iterator uiter; + struct extent_changeset changeset; +- int freed = 0; ++ u64 freed = 0; + int ret; + + extent_changeset_init(&changeset); +@@ -3880,7 +3881,9 @@ static int qgroup_free_reserved_data(struct btrfs_inode *inode, + } + btrfs_qgroup_free_refroot(root->fs_info, root->root_key.objectid, freed, + BTRFS_QGROUP_RSV_DATA); +- ret = freed; ++ if (freed_ret) ++ *freed_ret = freed; ++ ret = 0; + out: + extent_changeset_release(&changeset); + return ret; +@@ -3888,7 +3891,7 @@ out: + + static int __btrfs_qgroup_release_data(struct btrfs_inode *inode, + struct extent_changeset *reserved, u64 start, u64 len, +- int free) ++ u64 *released, int free) + { + struct extent_changeset changeset; + int trace_op = QGROUP_RELEASE; +@@ -3900,7 +3903,7 @@ static int __btrfs_qgroup_release_data(struct btrfs_inode *inode, + /* In release case, we shouldn't have @reserved */ + WARN_ON(!free && reserved); + if (free && reserved) +- return qgroup_free_reserved_data(inode, reserved, start, len); ++ return qgroup_free_reserved_data(inode, reserved, start, len, released); + extent_changeset_init(&changeset); + ret = clear_record_extent_bits(&inode->io_tree, start, start + len -1, + EXTENT_QGROUP_RESERVED, &changeset); +@@ -3915,7 +3918,8 @@ static int __btrfs_qgroup_release_data(struct btrfs_inode *inode, + btrfs_qgroup_free_refroot(inode->root->fs_info, + inode->root->root_key.objectid, + changeset.bytes_changed, BTRFS_QGROUP_RSV_DATA); +- ret = changeset.bytes_changed; ++ if (released) ++ *released = changeset.bytes_changed; + out: + extent_changeset_release(&changeset); + return ret; +@@ -3934,9 +3938,10 @@ out: + * NOTE: This function may sleep for memory allocation. + */ + int btrfs_qgroup_free_data(struct btrfs_inode *inode, +- struct extent_changeset *reserved, u64 start, u64 len) ++ struct extent_changeset *reserved, ++ u64 start, u64 len, u64 *freed) + { +- return __btrfs_qgroup_release_data(inode, reserved, start, len, 1); ++ return __btrfs_qgroup_release_data(inode, reserved, start, len, freed, 1); + } + + /* +@@ -3954,9 +3959,9 @@ int btrfs_qgroup_free_data(struct btrfs_inode *inode, + * + * NOTE: This function may sleep for memory allocation. + */ +-int btrfs_qgroup_release_data(struct btrfs_inode *inode, u64 start, u64 len) ++int btrfs_qgroup_release_data(struct btrfs_inode *inode, u64 start, u64 len, u64 *released) + { +- return __btrfs_qgroup_release_data(inode, NULL, start, len, 0); ++ return __btrfs_qgroup_release_data(inode, NULL, start, len, released, 0); + } + + static void add_root_meta_rsv(struct btrfs_root *root, int num_bytes, +diff --git a/fs/btrfs/qgroup.h b/fs/btrfs/qgroup.h +index 578c77e94200f..c382923f7628e 100644 +--- a/fs/btrfs/qgroup.h ++++ b/fs/btrfs/qgroup.h +@@ -360,10 +360,10 @@ int btrfs_verify_qgroup_counts(struct btrfs_fs_info *fs_info, u64 qgroupid, + /* New io_tree based accurate qgroup reserve API */ + int btrfs_qgroup_reserve_data(struct btrfs_inode *inode, + struct extent_changeset **reserved, u64 start, u64 len); +-int btrfs_qgroup_release_data(struct btrfs_inode *inode, u64 start, u64 len); ++int btrfs_qgroup_release_data(struct btrfs_inode *inode, u64 start, u64 len, u64 *released); + int btrfs_qgroup_free_data(struct btrfs_inode *inode, + struct extent_changeset *reserved, u64 start, +- u64 len); ++ u64 len, u64 *freed); + int btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes, + enum btrfs_qgroup_rsv_type type, bool enforce); + int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes, +diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c +index 03ca8f2f657ab..50b2ee163af60 100644 +--- a/fs/cachefiles/namei.c ++++ b/fs/cachefiles/namei.c +@@ -584,6 +584,8 @@ static bool cachefiles_open_file(struct cachefiles_object *object, + if (ret < 0) + goto check_failed; + ++ clear_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &object->cookie->flags); ++ + object->file = file; + + /* Always update the atime on an object we've just looked up (this is +diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c +index 177d8e8d73fe4..de1dee46d3df7 100644 +--- a/fs/ceph/cache.c ++++ b/fs/ceph/cache.c +@@ -36,6 +36,8 @@ void ceph_fscache_register_inode_cookie(struct inode *inode) + &ci->i_vino, sizeof(ci->i_vino), + &ci->i_version, sizeof(ci->i_version), + i_size_read(inode)); ++ if (ci->netfs.cache) ++ mapping_set_release_always(inode->i_mapping); + } + + void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info *ci) +diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c +index 044e34cd835c1..dedc9d445f243 100644 +--- a/fs/ext4/move_extent.c ++++ b/fs/ext4/move_extent.c +@@ -253,6 +253,7 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode, + { + struct inode *orig_inode = file_inode(o_filp); + struct page *pagep[2] = {NULL, NULL}; ++ struct folio *folio[2] = {NULL, NULL}; + handle_t *handle; + ext4_lblk_t orig_blk_offset, donor_blk_offset; + unsigned long blocksize = orig_inode->i_sb->s_blocksize; +@@ -313,6 +314,13 @@ again: + * hold page's lock, if it is still the case data copy is not + * necessary, just swap data blocks between orig and donor. + */ ++ folio[0] = page_folio(pagep[0]); ++ folio[1] = page_folio(pagep[1]); ++ ++ VM_BUG_ON_FOLIO(folio_test_large(folio[0]), folio[0]); ++ VM_BUG_ON_FOLIO(folio_test_large(folio[1]), folio[1]); ++ VM_BUG_ON_FOLIO(folio_nr_pages(folio[0]) != folio_nr_pages(folio[1]), folio[1]); ++ + if (unwritten) { + ext4_double_down_write_data_sem(orig_inode, donor_inode); + /* If any of extents in range became initialized we have to +@@ -331,10 +339,8 @@ again: + ext4_double_up_write_data_sem(orig_inode, donor_inode); + goto data_copy; + } +- if ((page_has_private(pagep[0]) && +- !try_to_release_page(pagep[0], 0)) || +- (page_has_private(pagep[1]) && +- !try_to_release_page(pagep[1], 0))) { ++ if (!filemap_release_folio(folio[0], 0) || ++ !filemap_release_folio(folio[1], 0)) { + *err = -EBUSY; + goto drop_data_sem; + } +@@ -344,19 +350,19 @@ again: + block_len_in_page, 1, err); + drop_data_sem: + ext4_double_up_write_data_sem(orig_inode, donor_inode); +- goto unlock_pages; ++ goto unlock_folios; + } + data_copy: +- *err = mext_page_mkuptodate(pagep[0], from, from + replaced_size); ++ *err = mext_page_mkuptodate(&folio[0]->page, from, from + replaced_size); + if (*err) +- goto unlock_pages; ++ goto unlock_folios; + + /* At this point all buffers in range are uptodate, old mapping layout + * is no longer required, try to drop it now. */ +- if ((page_has_private(pagep[0]) && !try_to_release_page(pagep[0], 0)) || +- (page_has_private(pagep[1]) && !try_to_release_page(pagep[1], 0))) { ++ if (!filemap_release_folio(folio[0], 0) || ++ !filemap_release_folio(folio[1], 0)) { + *err = -EBUSY; +- goto unlock_pages; ++ goto unlock_folios; + } + ext4_double_down_write_data_sem(orig_inode, donor_inode); + replaced_count = ext4_swap_extents(handle, orig_inode, donor_inode, +@@ -369,13 +375,13 @@ data_copy: + replaced_size = + block_len_in_page << orig_inode->i_blkbits; + } else +- goto unlock_pages; ++ goto unlock_folios; + } + /* Perform all necessary steps similar write_begin()/write_end() + * but keeping in mind that i_size will not change */ +- if (!page_has_buffers(pagep[0])) +- create_empty_buffers(pagep[0], 1 << orig_inode->i_blkbits, 0); +- bh = page_buffers(pagep[0]); ++ if (!folio_buffers(folio[0])) ++ create_empty_buffers(&folio[0]->page, 1 << orig_inode->i_blkbits, 0); ++ bh = folio_buffers(folio[0]); + for (i = 0; i < data_offset_in_page; i++) + bh = bh->b_this_page; + for (i = 0; i < block_len_in_page; i++) { +@@ -385,7 +391,7 @@ data_copy: + bh = bh->b_this_page; + } + if (!*err) +- *err = block_commit_write(pagep[0], from, from + replaced_size); ++ *err = block_commit_write(&folio[0]->page, from, from + replaced_size); + + if (unlikely(*err < 0)) + goto repair_branches; +@@ -395,11 +401,11 @@ data_copy: + *err = ext4_jbd2_inode_add_write(handle, orig_inode, + (loff_t)orig_page_offset << PAGE_SHIFT, replaced_size); + +-unlock_pages: +- unlock_page(pagep[0]); +- put_page(pagep[0]); +- unlock_page(pagep[1]); +- put_page(pagep[1]); ++unlock_folios: ++ folio_unlock(folio[0]); ++ folio_put(folio[0]); ++ folio_unlock(folio[1]); ++ folio_put(folio[1]); + stop_journal: + ext4_journal_stop(handle); + if (*err == -ENOSPC && +@@ -430,7 +436,7 @@ repair_branches: + *err = -EIO; + } + replaced_count = 0; +- goto unlock_pages; ++ goto unlock_folios; + } + + /** +diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c +index 5df04ed010cae..eb4d69f53337f 100644 +--- a/fs/f2fs/checkpoint.c ++++ b/fs/f2fs/checkpoint.c +@@ -984,7 +984,7 @@ int f2fs_get_valid_checkpoint(struct f2fs_sb_info *sbi) + + cp_blk_no = le32_to_cpu(fsb->cp_blkaddr); + if (cur_page == cp2) +- cp_blk_no += 1 << le32_to_cpu(fsb->log_blocks_per_seg); ++ cp_blk_no += BIT(le32_to_cpu(fsb->log_blocks_per_seg)); + + for (i = 1; i < cp_blks; i++) { + void *sit_bitmap_ptr; +diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c +index 11d9dce994dbe..4cb58e8d699e2 100644 +--- a/fs/f2fs/compress.c ++++ b/fs/f2fs/compress.c +@@ -241,7 +241,7 @@ static int lz4_init_compress_ctx(struct compress_ctx *cc) + unsigned int size = LZ4_MEM_COMPRESS; + + #ifdef CONFIG_F2FS_FS_LZ4HC +- if (F2FS_I(cc->inode)->i_compress_flag >> COMPRESS_LEVEL_OFFSET) ++ if (F2FS_I(cc->inode)->i_compress_level) + size = LZ4HC_MEM_COMPRESS; + #endif + +@@ -267,8 +267,7 @@ static void lz4_destroy_compress_ctx(struct compress_ctx *cc) + #ifdef CONFIG_F2FS_FS_LZ4HC + static int lz4hc_compress_pages(struct compress_ctx *cc) + { +- unsigned char level = F2FS_I(cc->inode)->i_compress_flag >> +- COMPRESS_LEVEL_OFFSET; ++ unsigned char level = F2FS_I(cc->inode)->i_compress_level; + int len; + + if (level) +@@ -332,17 +331,15 @@ static const struct f2fs_compress_ops f2fs_lz4_ops = { + #endif + + #ifdef CONFIG_F2FS_FS_ZSTD +-#define F2FS_ZSTD_DEFAULT_CLEVEL 1 +- + static int zstd_init_compress_ctx(struct compress_ctx *cc) + { + zstd_parameters params; + zstd_cstream *stream; + void *workspace; + unsigned int workspace_size; +- unsigned char level = F2FS_I(cc->inode)->i_compress_flag >> +- COMPRESS_LEVEL_OFFSET; ++ unsigned char level = F2FS_I(cc->inode)->i_compress_level; + ++ /* Need to remain this for backward compatibility */ + if (!level) + level = F2FS_ZSTD_DEFAULT_CLEVEL; + +@@ -675,7 +672,7 @@ static int f2fs_compress_pages(struct compress_ctx *cc) + + cc->cbuf->clen = cpu_to_le32(cc->clen); + +- if (fi->i_compress_flag & 1 << COMPRESS_CHKSUM) ++ if (fi->i_compress_flag & BIT(COMPRESS_CHKSUM)) + chksum = f2fs_crc32(F2FS_I_SB(cc->inode), + cc->cbuf->cdata, cc->clen); + cc->cbuf->chksum = cpu_to_le32(chksum); +@@ -773,7 +770,7 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task) + + ret = cops->decompress_pages(dic); + +- if (!ret && (fi->i_compress_flag & 1 << COMPRESS_CHKSUM)) { ++ if (!ret && (fi->i_compress_flag & BIT(COMPRESS_CHKSUM))) { + u32 provided = le32_to_cpu(dic->cbuf->chksum); + u32 calculated = f2fs_crc32(sbi, dic->cbuf->cdata, dic->clen); + +diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c +index ea05710ca9bdf..3666c1fd77a64 100644 +--- a/fs/f2fs/data.c ++++ b/fs/f2fs/data.c +@@ -95,17 +95,17 @@ static enum count_type __read_io_type(struct page *page) + /* postprocessing steps for read bios */ + enum bio_post_read_step { + #ifdef CONFIG_FS_ENCRYPTION +- STEP_DECRYPT = 1 << 0, ++ STEP_DECRYPT = BIT(0), + #else + STEP_DECRYPT = 0, /* compile out the decryption-related code */ + #endif + #ifdef CONFIG_F2FS_FS_COMPRESSION +- STEP_DECOMPRESS = 1 << 1, ++ STEP_DECOMPRESS = BIT(1), + #else + STEP_DECOMPRESS = 0, /* compile out the decompression-related code */ + #endif + #ifdef CONFIG_FS_VERITY +- STEP_VERITY = 1 << 2, ++ STEP_VERITY = BIT(2), + #else + STEP_VERITY = 0, /* compile out the verity-related code */ + #endif +@@ -409,7 +409,7 @@ int f2fs_target_device_index(struct f2fs_sb_info *sbi, block_t blkaddr) + + static blk_opf_t f2fs_io_flags(struct f2fs_io_info *fio) + { +- unsigned int temp_mask = (1 << NR_TEMP_TYPE) - 1; ++ unsigned int temp_mask = GENMASK(NR_TEMP_TYPE - 1, 0); + unsigned int fua_flag, meta_flag, io_flag; + blk_opf_t op_flags = 0; + +@@ -431,9 +431,9 @@ static blk_opf_t f2fs_io_flags(struct f2fs_io_info *fio) + * 5 | 4 | 3 | 2 | 1 | 0 | + * Cold | Warm | Hot | Cold | Warm | Hot | + */ +- if ((1 << fio->temp) & meta_flag) ++ if (BIT(fio->temp) & meta_flag) + op_flags |= REQ_META; +- if ((1 << fio->temp) & fua_flag) ++ if (BIT(fio->temp) & fua_flag) + op_flags |= REQ_FUA; + return op_flags; + } +diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c +index 8373eba3a1337..510736d2ae110 100644 +--- a/fs/f2fs/dir.c ++++ b/fs/f2fs/dir.c +@@ -29,7 +29,7 @@ static unsigned long dir_blocks(struct inode *inode) + static unsigned int dir_buckets(unsigned int level, int dir_level) + { + if (level + dir_level < MAX_DIR_HASH_DEPTH / 2) +- return 1 << (level + dir_level); ++ return BIT(level + dir_level); + else + return MAX_DIR_BUCKETS; + } +diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h +index f56abb39601ac..5c76ba764b71f 100644 +--- a/fs/f2fs/f2fs.h ++++ b/fs/f2fs/f2fs.h +@@ -64,7 +64,7 @@ enum { + }; + + #ifdef CONFIG_F2FS_FAULT_INJECTION +-#define F2FS_ALL_FAULT_TYPE ((1 << FAULT_MAX) - 1) ++#define F2FS_ALL_FAULT_TYPE (GENMASK(FAULT_MAX - 1, 0)) + + struct f2fs_fault_info { + atomic_t inject_ops; +@@ -73,7 +73,7 @@ struct f2fs_fault_info { + }; + + extern const char *f2fs_fault_name[FAULT_MAX]; +-#define IS_FAULT_SET(fi, type) ((fi)->inject_type & (1 << (type))) ++#define IS_FAULT_SET(fi, type) ((fi)->inject_type & BIT(type)) + #endif + + /* +@@ -840,7 +840,7 @@ struct f2fs_inode_info { + unsigned char i_compress_algorithm; /* algorithm type */ + unsigned char i_log_cluster_size; /* log of cluster size */ + unsigned char i_compress_level; /* compress level (lz4hc,zstd) */ +- unsigned short i_compress_flag; /* compress flag */ ++ unsigned char i_compress_flag; /* compress flag */ + unsigned int i_cluster_size; /* cluster size */ + + unsigned int atomic_write_cnt; +@@ -1412,7 +1412,7 @@ static inline void set_page_private_##name(struct page *page) \ + static inline void clear_page_private_##name(struct page *page) \ + { \ + clear_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \ +- if (page_private(page) == 1 << PAGE_PRIVATE_NOT_POINTER) { \ ++ if (page_private(page) == BIT(PAGE_PRIVATE_NOT_POINTER)) { \ + set_page_private(page, 0); \ + if (PagePrivate(page)) { \ + ClearPagePrivate(page); \ +@@ -1462,8 +1462,8 @@ static inline void set_page_private_data(struct page *page, unsigned long data) + + static inline void clear_page_private_data(struct page *page) + { +- page_private(page) &= (1 << PAGE_PRIVATE_MAX) - 1; +- if (page_private(page) == 1 << PAGE_PRIVATE_NOT_POINTER) { ++ page_private(page) &= GENMASK(PAGE_PRIVATE_MAX - 1, 0); ++ if (page_private(page) == BIT(PAGE_PRIVATE_NOT_POINTER)) { + set_page_private(page, 0); + if (PagePrivate(page)) { + ClearPagePrivate(page); +@@ -1501,6 +1501,8 @@ struct compress_data { + + #define F2FS_COMPRESSED_PAGE_MAGIC 0xF5F2C000 + ++#define F2FS_ZSTD_DEFAULT_CLEVEL 1 ++ + #define COMPRESS_LEVEL_OFFSET 8 + + /* compress context */ +@@ -2882,7 +2884,7 @@ static inline int f2fs_test_bit(unsigned int nr, char *addr) + int mask; + + addr += (nr >> 3); +- mask = 1 << (7 - (nr & 0x07)); ++ mask = BIT(7 - (nr & 0x07)); + return mask & *addr; + } + +@@ -2891,7 +2893,7 @@ static inline void f2fs_set_bit(unsigned int nr, char *addr) + int mask; + + addr += (nr >> 3); +- mask = 1 << (7 - (nr & 0x07)); ++ mask = BIT(7 - (nr & 0x07)); + *addr |= mask; + } + +@@ -2900,7 +2902,7 @@ static inline void f2fs_clear_bit(unsigned int nr, char *addr) + int mask; + + addr += (nr >> 3); +- mask = 1 << (7 - (nr & 0x07)); ++ mask = BIT(7 - (nr & 0x07)); + *addr &= ~mask; + } + +@@ -2910,7 +2912,7 @@ static inline int f2fs_test_and_set_bit(unsigned int nr, char *addr) + int ret; + + addr += (nr >> 3); +- mask = 1 << (7 - (nr & 0x07)); ++ mask = BIT(7 - (nr & 0x07)); + ret = mask & *addr; + *addr |= mask; + return ret; +@@ -2922,7 +2924,7 @@ static inline int f2fs_test_and_clear_bit(unsigned int nr, char *addr) + int ret; + + addr += (nr >> 3); +- mask = 1 << (7 - (nr & 0x07)); ++ mask = BIT(7 - (nr & 0x07)); + ret = mask & *addr; + *addr &= ~mask; + return ret; +@@ -2933,7 +2935,7 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr) + int mask; + + addr += (nr >> 3); +- mask = 1 << (7 - (nr & 0x07)); ++ mask = BIT(7 - (nr & 0x07)); + *addr ^= mask; + } + +@@ -4333,15 +4335,14 @@ static inline int set_compress_context(struct inode *inode) + F2FS_OPTION(sbi).compress_log_size; + F2FS_I(inode)->i_compress_flag = + F2FS_OPTION(sbi).compress_chksum ? +- 1 << COMPRESS_CHKSUM : 0; ++ BIT(COMPRESS_CHKSUM) : 0; + F2FS_I(inode)->i_cluster_size = +- 1 << F2FS_I(inode)->i_log_cluster_size; ++ BIT(F2FS_I(inode)->i_log_cluster_size); + if ((F2FS_I(inode)->i_compress_algorithm == COMPRESS_LZ4 || + F2FS_I(inode)->i_compress_algorithm == COMPRESS_ZSTD) && + F2FS_OPTION(sbi).compress_level) +- F2FS_I(inode)->i_compress_flag |= +- F2FS_OPTION(sbi).compress_level << +- COMPRESS_LEVEL_OFFSET; ++ F2FS_I(inode)->i_compress_level = ++ F2FS_OPTION(sbi).compress_level; + F2FS_I(inode)->i_flags |= F2FS_COMPR_FL; + set_inode_flag(inode, FI_COMPRESSED_FILE); + stat_inc_compr_inode(inode); +diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c +index d0c17366ebf48..9b9fb3c57ec6c 100644 +--- a/fs/f2fs/file.c ++++ b/fs/f2fs/file.c +@@ -3983,7 +3983,16 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg) + + F2FS_I(inode)->i_compress_algorithm = option.algorithm; + F2FS_I(inode)->i_log_cluster_size = option.log_cluster_size; +- F2FS_I(inode)->i_cluster_size = 1 << option.log_cluster_size; ++ F2FS_I(inode)->i_cluster_size = BIT(option.log_cluster_size); ++ /* Set default level */ ++ if (F2FS_I(inode)->i_compress_algorithm == COMPRESS_ZSTD) ++ F2FS_I(inode)->i_compress_level = F2FS_ZSTD_DEFAULT_CLEVEL; ++ else ++ F2FS_I(inode)->i_compress_level = 0; ++ /* Adjust mount option level */ ++ if (option.algorithm == F2FS_OPTION(sbi).compress_algorithm && ++ F2FS_OPTION(sbi).compress_level) ++ F2FS_I(inode)->i_compress_level = F2FS_OPTION(sbi).compress_level; + f2fs_mark_inode_dirty_sync(inode, true); + + if (!f2fs_is_compress_backend_ready(inode)) +diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c +index 1fc7760499f10..0010579f17368 100644 +--- a/fs/f2fs/inode.c ++++ b/fs/f2fs/inode.c +@@ -450,12 +450,18 @@ static int do_read_inode(struct inode *inode) + (fi->i_flags & F2FS_COMPR_FL)) { + if (F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, + i_log_cluster_size)) { ++ unsigned short compress_flag; ++ + atomic_set(&fi->i_compr_blocks, + le64_to_cpu(ri->i_compr_blocks)); + fi->i_compress_algorithm = ri->i_compress_algorithm; + fi->i_log_cluster_size = ri->i_log_cluster_size; +- fi->i_compress_flag = le16_to_cpu(ri->i_compress_flag); +- fi->i_cluster_size = 1 << fi->i_log_cluster_size; ++ compress_flag = le16_to_cpu(ri->i_compress_flag); ++ fi->i_compress_level = compress_flag >> ++ COMPRESS_LEVEL_OFFSET; ++ fi->i_compress_flag = compress_flag & ++ GENMASK(COMPRESS_LEVEL_OFFSET - 1, 0); ++ fi->i_cluster_size = BIT(fi->i_log_cluster_size); + set_inode_flag(inode, FI_COMPRESSED_FILE); + } + } +@@ -675,13 +681,17 @@ void f2fs_update_inode(struct inode *inode, struct page *node_page) + if (f2fs_sb_has_compression(F2FS_I_SB(inode)) && + F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, + i_log_cluster_size)) { ++ unsigned short compress_flag; ++ + ri->i_compr_blocks = + cpu_to_le64(atomic_read( + &F2FS_I(inode)->i_compr_blocks)); + ri->i_compress_algorithm = + F2FS_I(inode)->i_compress_algorithm; +- ri->i_compress_flag = +- cpu_to_le16(F2FS_I(inode)->i_compress_flag); ++ compress_flag = F2FS_I(inode)->i_compress_flag | ++ F2FS_I(inode)->i_compress_level << ++ COMPRESS_LEVEL_OFFSET; ++ ri->i_compress_flag = cpu_to_le16(compress_flag); + ri->i_log_cluster_size = + F2FS_I(inode)->i_log_cluster_size; + } +diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h +index 0aa48704c77a0..7068f3ac036a5 100644 +--- a/fs/f2fs/node.h ++++ b/fs/f2fs/node.h +@@ -93,17 +93,15 @@ static inline void copy_node_info(struct node_info *dst, + static inline void set_nat_flag(struct nat_entry *ne, + unsigned int type, bool set) + { +- unsigned char mask = 0x01 << type; + if (set) +- ne->ni.flag |= mask; ++ ne->ni.flag |= BIT(type); + else +- ne->ni.flag &= ~mask; ++ ne->ni.flag &= ~BIT(type); + } + + static inline bool get_nat_flag(struct nat_entry *ne, unsigned int type) + { +- unsigned char mask = 0x01 << type; +- return ne->ni.flag & mask; ++ return ne->ni.flag & BIT(type); + } + + static inline void nat_reset_flag(struct nat_entry *ne) +@@ -224,7 +222,7 @@ static inline pgoff_t next_nat_addr(struct f2fs_sb_info *sbi, + struct f2fs_nm_info *nm_i = NM_I(sbi); + + block_addr -= nm_i->nat_blkaddr; +- block_addr ^= 1 << sbi->log_blocks_per_seg; ++ block_addr ^= BIT(sbi->log_blocks_per_seg); + return block_addr + nm_i->nat_blkaddr; + } + +@@ -394,7 +392,7 @@ static inline nid_t get_nid(struct page *p, int off, bool i) + static inline int is_node(struct page *page, int type) + { + struct f2fs_node *rn = F2FS_NODE(page); +- return le32_to_cpu(rn->footer.flag) & (1 << type); ++ return le32_to_cpu(rn->footer.flag) & BIT(type); + } + + #define is_cold_node(page) is_node(page, COLD_BIT_SHIFT) +@@ -407,9 +405,9 @@ static inline void set_cold_node(struct page *page, bool is_dir) + unsigned int flag = le32_to_cpu(rn->footer.flag); + + if (is_dir) +- flag &= ~(0x1 << COLD_BIT_SHIFT); ++ flag &= ~BIT(COLD_BIT_SHIFT); + else +- flag |= (0x1 << COLD_BIT_SHIFT); ++ flag |= BIT(COLD_BIT_SHIFT); + rn->footer.flag = cpu_to_le32(flag); + } + +@@ -418,9 +416,9 @@ static inline void set_mark(struct page *page, int mark, int type) + struct f2fs_node *rn = F2FS_NODE(page); + unsigned int flag = le32_to_cpu(rn->footer.flag); + if (mark) +- flag |= (0x1 << type); ++ flag |= BIT(type); + else +- flag &= ~(0x1 << type); ++ flag &= ~BIT(type); + rn->footer.flag = cpu_to_le32(flag); + + #ifdef CONFIG_F2FS_CHECK_FS +diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c +index 1ba85ef97cbd3..3805162dcef2b 100644 +--- a/fs/f2fs/super.c ++++ b/fs/f2fs/super.c +@@ -613,14 +613,12 @@ static int f2fs_set_lz4hc_level(struct f2fs_sb_info *sbi, const char *str) + { + #ifdef CONFIG_F2FS_FS_LZ4HC + unsigned int level; +-#endif + + if (strlen(str) == 3) { + F2FS_OPTION(sbi).compress_level = 0; + return 0; + } + +-#ifdef CONFIG_F2FS_FS_LZ4HC + str += 3; + + if (str[0] != ':') { +@@ -638,6 +636,10 @@ static int f2fs_set_lz4hc_level(struct f2fs_sb_info *sbi, const char *str) + F2FS_OPTION(sbi).compress_level = level; + return 0; + #else ++ if (strlen(str) == 3) { ++ F2FS_OPTION(sbi).compress_level = 0; ++ return 0; ++ } + f2fs_info(sbi, "kernel doesn't support lz4hc compression"); + return -EINVAL; + #endif +@@ -651,7 +653,7 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str) + int len = 4; + + if (strlen(str) == len) { +- F2FS_OPTION(sbi).compress_level = 0; ++ F2FS_OPTION(sbi).compress_level = F2FS_ZSTD_DEFAULT_CLEVEL; + return 0; + } + +@@ -664,7 +666,7 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str) + if (kstrtouint(str + 1, 10, &level)) + return -EINVAL; + +- if (!level || level > zstd_max_clevel()) { ++ if (level < zstd_min_clevel() || level > zstd_max_clevel()) { + f2fs_info(sbi, "invalid zstd compress level: %d", level); + return -EINVAL; + } +@@ -898,8 +900,8 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) + if (args->from && match_int(args, &arg)) + return -EINVAL; + if (arg <= 0 || arg > __ilog2_u32(BIO_MAX_VECS)) { +- f2fs_warn(sbi, "Not support %d, larger than %d", +- 1 << arg, BIO_MAX_VECS); ++ f2fs_warn(sbi, "Not support %ld, larger than %d", ++ BIT(arg), BIO_MAX_VECS); + return -EINVAL; + } + F2FS_OPTION(sbi).write_io_size_bits = arg; +@@ -1340,7 +1342,7 @@ default_check: + #endif + + if (F2FS_IO_SIZE_BITS(sbi) && !f2fs_lfs_mode(sbi)) { +- f2fs_err(sbi, "Should set mode=lfs with %uKB-sized IO", ++ f2fs_err(sbi, "Should set mode=lfs with %luKB-sized IO", + F2FS_IO_SIZE_KB(sbi)); + return -EINVAL; + } +@@ -3356,7 +3358,7 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi, + total_sections = le32_to_cpu(raw_super->section_count); + + /* blocks_per_seg should be 512, given the above check */ +- blocks_per_seg = 1 << le32_to_cpu(raw_super->log_blocks_per_seg); ++ blocks_per_seg = BIT(le32_to_cpu(raw_super->log_blocks_per_seg)); + + if (segment_count > F2FS_MAX_SEGMENT || + segment_count < F2FS_MIN_SEGMENTS) { +@@ -3625,9 +3627,9 @@ static void init_sb_info(struct f2fs_sb_info *sbi) + sbi->log_sectors_per_block = + le32_to_cpu(raw_super->log_sectors_per_block); + sbi->log_blocksize = le32_to_cpu(raw_super->log_blocksize); +- sbi->blocksize = 1 << sbi->log_blocksize; ++ sbi->blocksize = BIT(sbi->log_blocksize); + sbi->log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg); +- sbi->blocks_per_seg = 1 << sbi->log_blocks_per_seg; ++ sbi->blocks_per_seg = BIT(sbi->log_blocks_per_seg); + sbi->segs_per_sec = le32_to_cpu(raw_super->segs_per_sec); + sbi->secs_per_zone = le32_to_cpu(raw_super->secs_per_zone); + sbi->total_sections = le32_to_cpu(raw_super->section_count); +@@ -3883,7 +3885,7 @@ void f2fs_handle_stop(struct f2fs_sb_info *sbi, unsigned char reason) + + f2fs_down_write(&sbi->sb_lock); + +- if (raw_super->s_stop_reason[reason] < ((1 << BITS_PER_BYTE) - 1)) ++ if (raw_super->s_stop_reason[reason] < GENMASK(BITS_PER_BYTE - 1, 0)) + raw_super->s_stop_reason[reason]++; + + err = f2fs_commit_super(sbi, false); +@@ -4033,7 +4035,7 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi) + FDEV(i).start_blk, FDEV(i).end_blk); + } + f2fs_info(sbi, +- "IO Block Size: %8d KB", F2FS_IO_SIZE_KB(sbi)); ++ "IO Block Size: %8ld KB", F2FS_IO_SIZE_KB(sbi)); + return 0; + } + +diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c +index 3d68bfa75cf2a..751a108e612ff 100644 +--- a/fs/f2fs/sysfs.c ++++ b/fs/f2fs/sysfs.c +@@ -451,7 +451,7 @@ out: + if (ret < 0) + return ret; + #ifdef CONFIG_F2FS_FAULT_INJECTION +- if (a->struct_type == FAULT_INFO_TYPE && t >= (1 << FAULT_MAX)) ++ if (a->struct_type == FAULT_INFO_TYPE && t >= BIT(FAULT_MAX)) + return -EINVAL; + if (a->struct_type == FAULT_INFO_RATE && t >= UINT_MAX) + return -EINVAL; +diff --git a/fs/inode.c b/fs/inode.c +index 73ad1b0d47758..8cfda7a6d5900 100644 +--- a/fs/inode.c ++++ b/fs/inode.c +@@ -215,6 +215,8 @@ int inode_init_always(struct super_block *sb, struct inode *inode) + lockdep_set_class_and_name(&mapping->invalidate_lock, + &sb->s_type->invalidate_lock_key, + "mapping.invalidate_lock"); ++ if (sb->s_iflags & SB_I_STABLE_WRITES) ++ mapping_set_stable_writes(mapping); + inode->i_private = NULL; + inode->i_mapping = mapping; + INIT_HLIST_HEAD(&inode->i_dentry); /* buggered by rcu freeing */ +diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c +index e731c00a9fcbc..d3c938dd2b12a 100644 +--- a/fs/nfs/fscache.c ++++ b/fs/nfs/fscache.c +@@ -176,6 +176,9 @@ void nfs_fscache_init_inode(struct inode *inode) + &auxdata, /* aux_data */ + sizeof(auxdata), + i_size_read(inode)); ++ ++ if (netfs_inode(inode)->cache) ++ mapping_set_release_always(inode->i_mapping); + } + + /* +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 512ac9dea9787..7f1aea4c11b9c 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -972,7 +972,6 @@ release_iface(struct kref *ref) + struct cifs_server_iface *iface = container_of(ref, + struct cifs_server_iface, + refcount); +- list_del_init(&iface->iface_head); + kfree(iface); + } + +diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c +index f725a119ce312..49fdc6dfdcf8d 100644 +--- a/fs/smb/client/connect.c ++++ b/fs/smb/client/connect.c +@@ -258,10 +258,13 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) { + /* check if iface is still active */ +- if (!cifs_chan_is_iface_active(ses, server)) ++ spin_lock(&ses->chan_lock); ++ if (!cifs_chan_is_iface_active(ses, server)) { ++ spin_unlock(&ses->chan_lock); + cifs_chan_update_iface(ses, server); ++ spin_lock(&ses->chan_lock); ++ } + +- spin_lock(&ses->chan_lock); + if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) { + spin_unlock(&ses->chan_lock); + continue; +diff --git a/fs/smb/client/fscache.c b/fs/smb/client/fscache.c +index e73625b5d0cc6..f64bad513ba6d 100644 +--- a/fs/smb/client/fscache.c ++++ b/fs/smb/client/fscache.c +@@ -108,6 +108,8 @@ void cifs_fscache_get_inode_cookie(struct inode *inode) + &cifsi->uniqueid, sizeof(cifsi->uniqueid), + &cd, sizeof(cd), + i_size_read(&cifsi->netfs.inode)); ++ if (cifsi->netfs.cache) ++ mapping_set_release_always(inode->i_mapping); + } + + void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) +diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c +index 7be51f9d2fa18..5343898bac8a6 100644 +--- a/fs/smb/client/inode.c ++++ b/fs/smb/client/inode.c +@@ -264,7 +264,7 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info, + fattr->cf_dtype = DT_REG; + break; + case UNIX_SYMLINK: +- fattr->cf_mode |= S_IFLNK; ++ fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_LNK; + break; + case UNIX_DIR: +diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c +index ba6cc50af390f..a7475bc05cac0 100644 +--- a/fs/smb/client/smb2file.c ++++ b/fs/smb/client/smb2file.c +@@ -34,7 +34,7 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov) + len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp, + ErrorContextData) + + sizeof(struct smb2_symlink_err_rsp)); +- if (le32_to_cpu(err->ByteCount) < len || iov->iov_len < len + sizeof(*err)) ++ if (le32_to_cpu(err->ByteCount) < len || iov->iov_len < len + sizeof(*err) + 1) + return ERR_PTR(-EINVAL); + + p = (struct smb2_error_context_rsp *)err->ErrorData; +diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c +index 88942b1fb4318..fdf7a7f188c5f 100644 +--- a/fs/smb/client/smb2misc.c ++++ b/fs/smb/client/smb2misc.c +@@ -113,7 +113,7 @@ static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len, + } else if (nc_offset + 1 == non_ctxlen) { + cifs_dbg(FYI, "no SPNEGO security blob in negprot rsp\n"); + size_of_pad_before_neg_ctxts = 0; +- } else if (non_ctxlen == SMB311_NEGPROT_BASE_SIZE) ++ } else if (non_ctxlen == SMB311_NEGPROT_BASE_SIZE + 1) + /* has padding, but no SPNEGO blob */ + size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen + 1; + else +diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c +index df03d80ab6d5f..4596d2dfdec3a 100644 +--- a/fs/smb/client/smb2ops.c ++++ b/fs/smb/client/smb2ops.c +@@ -588,16 +588,12 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, + } + + /* +- * Go through iface_list and do kref_put to remove +- * any unused ifaces. ifaces in use will be removed +- * when the last user calls a kref_put on it ++ * Go through iface_list and mark them as inactive + */ + list_for_each_entry_safe(iface, niface, &ses->iface_list, +- iface_head) { ++ iface_head) + iface->is_active = 0; +- kref_put(&iface->refcount, release_iface); +- ses->iface_count--; +- } ++ + spin_unlock(&ses->iface_lock); + + /* +@@ -672,10 +668,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, + iface_head) { + ret = iface_cmp(iface, &tmp_iface); + if (!ret) { +- /* just get a ref so that it doesn't get picked/freed */ + iface->is_active = 1; +- kref_get(&iface->refcount); +- ses->iface_count++; + spin_unlock(&ses->iface_lock); + goto next_iface; + } else if (ret < 0) { +@@ -742,6 +735,20 @@ next_iface: + } + + out: ++ /* ++ * Go through the list again and put the inactive entries ++ */ ++ spin_lock(&ses->iface_lock); ++ list_for_each_entry_safe(iface, niface, &ses->iface_list, ++ iface_head) { ++ if (!iface->is_active) { ++ list_del(&iface->iface_head); ++ kref_put(&iface->refcount, release_iface); ++ ses->iface_count--; ++ } ++ } ++ spin_unlock(&ses->iface_lock); ++ + return rc; + } + +@@ -778,9 +785,14 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_ + goto out; + + /* check if iface is still active */ ++ spin_lock(&ses->chan_lock); + pserver = ses->chans[0].server; +- if (pserver && !cifs_chan_is_iface_active(ses, pserver)) ++ if (pserver && !cifs_chan_is_iface_active(ses, pserver)) { ++ spin_unlock(&ses->chan_lock); + cifs_chan_update_iface(ses, pserver); ++ spin_lock(&ses->chan_lock); ++ } ++ spin_unlock(&ses->chan_lock); + + out: + kfree(out_buf); +@@ -5752,7 +5764,7 @@ struct smb_version_values smb20_values = { + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, +- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, ++ .read_rsp_size = sizeof(struct smb2_read_rsp), + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, +@@ -5774,7 +5786,7 @@ struct smb_version_values smb21_values = { + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, +- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, ++ .read_rsp_size = sizeof(struct smb2_read_rsp), + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, +@@ -5795,7 +5807,7 @@ struct smb_version_values smb3any_values = { + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, +- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, ++ .read_rsp_size = sizeof(struct smb2_read_rsp), + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, +@@ -5816,7 +5828,7 @@ struct smb_version_values smbdefault_values = { + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, +- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, ++ .read_rsp_size = sizeof(struct smb2_read_rsp), + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, +@@ -5837,7 +5849,7 @@ struct smb_version_values smb30_values = { + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, +- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, ++ .read_rsp_size = sizeof(struct smb2_read_rsp), + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, +@@ -5858,7 +5870,7 @@ struct smb_version_values smb302_values = { + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, +- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, ++ .read_rsp_size = sizeof(struct smb2_read_rsp), + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, +@@ -5879,7 +5891,7 @@ struct smb_version_values smb311_values = { + .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, +- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, ++ .read_rsp_size = sizeof(struct smb2_read_rsp), + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, +diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c +index 05ff8a457a3d7..2dfbf1b23cfa0 100644 +--- a/fs/smb/client/smb2pdu.c ++++ b/fs/smb/client/smb2pdu.c +@@ -1386,7 +1386,7 @@ SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) + + /* Testing shows that buffer offset must be at location of Buffer[0] */ + req->SecurityBufferOffset = +- cpu_to_le16(sizeof(struct smb2_sess_setup_req) - 1 /* pad */); ++ cpu_to_le16(sizeof(struct smb2_sess_setup_req)); + req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len); + + memset(&rqst, 0, sizeof(struct smb_rqst)); +@@ -1905,8 +1905,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, + iov[0].iov_len = total_len - 1; + + /* Testing shows that buffer offset must be at location of Buffer[0] */ +- req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req) +- - 1 /* pad */); ++ req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req)); + req->PathLength = cpu_to_le16(unc_path_len - 2); + iov[1].iov_base = unc_path; + iov[1].iov_len = unc_path_len; +@@ -3796,7 +3795,7 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + ses->Suid, (u8)watch_tree, completion_filter); + /* validate that notify information is plausible */ + if ((rsp_iov.iov_base == NULL) || +- (rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp))) ++ (rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp) + 1)) + goto cnotify_exit; + + smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base; +@@ -5009,7 +5008,7 @@ int SMB2_query_directory_init(const unsigned int xid, + memcpy(bufptr, &asteriks, len); + + req->FileNameOffset = +- cpu_to_le16(sizeof(struct smb2_query_directory_req) - 1); ++ cpu_to_le16(sizeof(struct smb2_query_directory_req)); + req->FileNameLength = cpu_to_le16(len); + /* + * BB could be 30 bytes or so longer if we used SMB2 specific +@@ -5205,8 +5204,7 @@ SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, + req->VolatileFileId = volatile_fid; + req->AdditionalInformation = cpu_to_le32(additional_info); + +- req->BufferOffset = +- cpu_to_le16(sizeof(struct smb2_set_info_req) - 1); ++ req->BufferOffset = cpu_to_le16(sizeof(struct smb2_set_info_req)); + req->BufferLength = cpu_to_le32(*size); + + memcpy(req->Buffer, *data, *size); +@@ -5440,9 +5438,9 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, + req->VolatileFileId = volatile_fid; + /* 1 for pad */ + req->InputBufferOffset = +- cpu_to_le16(sizeof(struct smb2_query_info_req) - 1); ++ cpu_to_le16(sizeof(struct smb2_query_info_req)); + req->OutputBufferLength = cpu_to_le32( +- outbuf_len + sizeof(struct smb2_query_info_rsp) - 1); ++ outbuf_len + sizeof(struct smb2_query_info_rsp)); + + iov->iov_base = (char *)req; + iov->iov_len = total_len; +diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h +index 1237bb86e93a8..a5773a06aba8e 100644 +--- a/fs/smb/client/smb2pdu.h ++++ b/fs/smb/client/smb2pdu.h +@@ -57,7 +57,7 @@ struct smb2_rdma_crypto_transform { + #define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL + + #define SMB2_SYMLINK_STRUCT_SIZE \ +- (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp)) ++ (sizeof(struct smb2_err_rsp) + sizeof(struct smb2_symlink_err_rsp)) + + #define SYMLINK_ERROR_TAG 0x4c4d5953 + +diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h +index 07549957b3099..5593bb49954c6 100644 +--- a/fs/smb/common/smb2pdu.h ++++ b/fs/smb/common/smb2pdu.h +@@ -189,7 +189,7 @@ struct smb2_err_rsp { + __u8 ErrorContextCount; + __u8 Reserved; + __le32 ByteCount; /* even if zero, at least one byte follows */ +- __u8 ErrorData[1]; /* variable length */ ++ __u8 ErrorData[]; /* variable length */ + } __packed; + + #define SMB3_AES_CCM_NONCE 11 +@@ -330,7 +330,7 @@ struct smb2_tree_connect_req { + __le16 Flags; /* Flags in SMB3.1.1 */ + __le16 PathOffset; + __le16 PathLength; +- __u8 Buffer[1]; /* variable length */ ++ __u8 Buffer[]; /* variable length */ + } __packed; + + /* Possible ShareType values */ +@@ -617,7 +617,7 @@ struct smb2_negotiate_rsp { + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */ +- __u8 Buffer[1]; /* variable length GSS security buffer */ ++ __u8 Buffer[]; /* variable length GSS security buffer */ + } __packed; + + +@@ -638,7 +638,7 @@ struct smb2_sess_setup_req { + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le64 PreviousSessionId; +- __u8 Buffer[1]; /* variable length GSS security buffer */ ++ __u8 Buffer[]; /* variable length GSS security buffer */ + } __packed; + + /* Currently defined SessionFlags */ +@@ -655,7 +655,7 @@ struct smb2_sess_setup_rsp { + __le16 SessionFlags; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; +- __u8 Buffer[1]; /* variable length GSS security buffer */ ++ __u8 Buffer[]; /* variable length GSS security buffer */ + } __packed; + + +@@ -737,7 +737,7 @@ struct smb2_read_req { + __le32 RemainingBytes; + __le16 ReadChannelInfoOffset; + __le16 ReadChannelInfoLength; +- __u8 Buffer[1]; ++ __u8 Buffer[]; + } __packed; + + /* Read flags */ +@@ -752,7 +752,7 @@ struct smb2_read_rsp { + __le32 DataLength; + __le32 DataRemaining; + __le32 Flags; +- __u8 Buffer[1]; ++ __u8 Buffer[]; + } __packed; + + +@@ -776,7 +776,7 @@ struct smb2_write_req { + __le16 WriteChannelInfoOffset; + __le16 WriteChannelInfoLength; + __le32 Flags; +- __u8 Buffer[1]; ++ __u8 Buffer[]; + } __packed; + + struct smb2_write_rsp { +@@ -787,7 +787,7 @@ struct smb2_write_rsp { + __le32 DataLength; + __le32 DataRemaining; + __u32 Reserved2; +- __u8 Buffer[1]; ++ __u8 Buffer[]; + } __packed; + + +@@ -834,7 +834,10 @@ struct smb2_lock_req { + __u64 PersistentFileId; + __u64 VolatileFileId; + /* Followed by at least one */ +- struct smb2_lock_element locks[1]; ++ union { ++ struct smb2_lock_element lock; ++ DECLARE_FLEX_ARRAY(struct smb2_lock_element, locks); ++ }; + } __packed; + + struct smb2_lock_rsp { +@@ -888,7 +891,7 @@ struct smb2_query_directory_req { + __le16 FileNameOffset; + __le16 FileNameLength; + __le32 OutputBufferLength; +- __u8 Buffer[1]; ++ __u8 Buffer[]; + } __packed; + + struct smb2_query_directory_rsp { +@@ -896,7 +899,7 @@ struct smb2_query_directory_rsp { + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; +- __u8 Buffer[1]; ++ __u8 Buffer[]; + } __packed; + + /* +@@ -919,7 +922,7 @@ struct smb2_set_info_req { + __le32 AdditionalInformation; + __u64 PersistentFileId; + __u64 VolatileFileId; +- __u8 Buffer[1]; ++ __u8 Buffer[]; + } __packed; + + struct smb2_set_info_rsp { +@@ -974,7 +977,7 @@ struct smb2_change_notify_rsp { + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; +- __u8 Buffer[1]; /* array of file notify structs */ ++ __u8 Buffer[]; /* array of file notify structs */ + } __packed; + + +@@ -1180,7 +1183,7 @@ struct smb2_create_rsp { + __u64 VolatileFileId; + __le32 CreateContextsOffset; + __le32 CreateContextsLength; +- __u8 Buffer[1]; ++ __u8 Buffer[]; + } __packed; + + struct create_posix { +@@ -1524,7 +1527,7 @@ struct smb2_query_info_req { + __le32 Flags; + __u64 PersistentFileId; + __u64 VolatileFileId; +- __u8 Buffer[1]; ++ __u8 Buffer[]; + } __packed; + + struct smb2_query_info_rsp { +@@ -1532,7 +1535,7 @@ struct smb2_query_info_rsp { + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; +- __u8 Buffer[1]; ++ __u8 Buffer[]; + } __packed; + + /* +@@ -1593,7 +1596,10 @@ struct smb2_file_all_info { /* data block encoding of response to level 18 */ + __le32 Mode; + __le32 AlignmentRequirement; + __le32 FileNameLength; +- char FileName[1]; ++ union { ++ char __pad; /* Legacy structure padding */ ++ DECLARE_FLEX_ARRAY(char, FileName); ++ }; + } __packed; /* level 18 Query */ + + struct smb2_file_eof_info { /* encoding of request for level 10 */ +diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c +index 535402629655e..27a9dce3e03ab 100644 +--- a/fs/smb/server/smb2ops.c ++++ b/fs/smb/server/smb2ops.c +@@ -26,7 +26,7 @@ static struct smb_version_values smb21_server_values = { + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, +- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, ++ .read_rsp_size = sizeof(struct smb2_read_rsp), + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, +@@ -52,7 +52,7 @@ static struct smb_version_values smb30_server_values = { + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, +- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, ++ .read_rsp_size = sizeof(struct smb2_read_rsp), + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, +@@ -79,7 +79,7 @@ static struct smb_version_values smb302_server_values = { + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, +- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, ++ .read_rsp_size = sizeof(struct smb2_read_rsp), + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, +@@ -106,7 +106,7 @@ static struct smb_version_values smb311_server_values = { + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, +- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, ++ .read_rsp_size = sizeof(struct smb2_read_rsp), + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index ea48dd06d4da3..6e5ed0ac578a6 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -294,8 +294,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) + if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; + err = ksmbd_iov_pin_rsp(work, rsp, +- sizeof(struct smb2_negotiate_rsp) - +- sizeof(rsp->Buffer) + AUTH_GSS_LENGTH); ++ sizeof(struct smb2_negotiate_rsp) + AUTH_GSS_LENGTH); + if (err) + return err; + conn->use_spnego = true; +@@ -1263,9 +1262,8 @@ err_out: + + if (!rc) + rc = ksmbd_iov_pin_rsp(work, rsp, +- sizeof(struct smb2_negotiate_rsp) - +- sizeof(rsp->Buffer) + +- AUTH_GSS_LENGTH + neg_ctxt_len); ++ sizeof(struct smb2_negotiate_rsp) + ++ AUTH_GSS_LENGTH + neg_ctxt_len); + if (rc < 0) + smb2_set_err_rsp(work); + return rc; +diff --git a/fs/splice.c b/fs/splice.c +index 5969b7a1d353a..d0230cf8ec571 100644 +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -65,8 +65,7 @@ static bool page_cache_pipe_buf_try_steal(struct pipe_inode_info *pipe, + */ + folio_wait_writeback(folio); + +- if (folio_has_private(folio) && +- !filemap_release_folio(folio, GFP_KERNEL)) ++ if (!filemap_release_folio(folio, GFP_KERNEL)) + goto out_unlock; + + /* +@@ -764,6 +763,17 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + return out->f_op->splice_write(pipe, out, ppos, len, flags); + } + ++/* ++ * Indicate to the caller that there was a premature EOF when reading from the ++ * source and the caller didn't indicate they would be sending more data after ++ * this. ++ */ ++static void do_splice_eof(struct splice_desc *sd) ++{ ++ if (sd->splice_eof) ++ sd->splice_eof(sd); ++} ++ + /* + * Attempt to initiate a splice from a file to a pipe. + */ +@@ -864,7 +874,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd, + + ret = do_splice_to(in, &pos, pipe, len, flags); + if (unlikely(ret <= 0)) +- goto out_release; ++ goto read_failure; + + read_len = ret; + sd->total_len = read_len; +@@ -904,6 +914,15 @@ done: + file_accessed(in); + return bytes; + ++read_failure: ++ /* ++ * If the user did *not* set SPLICE_F_MORE *and* we didn't hit that ++ * "use all of len" case that cleared SPLICE_F_MORE, *and* we did a ++ * "->splice_in()" that returned EOF (ie zero) *and* we have sent at ++ * least 1 byte *then* we will also do the ->splice_eof() call. ++ */ ++ if (ret == 0 && !more && len > 0 && bytes) ++ do_splice_eof(sd); + out_release: + /* + * If we did an incomplete transfer we must release +@@ -932,6 +951,14 @@ static int direct_splice_actor(struct pipe_inode_info *pipe, + sd->flags); + } + ++static void direct_file_splice_eof(struct splice_desc *sd) ++{ ++ struct file *file = sd->u.file; ++ ++ if (file->f_op->splice_eof) ++ file->f_op->splice_eof(file); ++} ++ + /** + * do_splice_direct - splices data directly between two files + * @in: file to splice from +@@ -957,6 +984,7 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, + .flags = flags, + .pos = *ppos, + .u.file = out, ++ .splice_eof = direct_file_splice_eof, + .opos = opos, + }; + long ret; +diff --git a/include/linux/bpf.h b/include/linux/bpf.h +index 3ce9e39ecdb85..ba22cf4f5fc0e 100644 +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -702,10 +702,14 @@ bpf_ctx_record_field_size(struct bpf_insn_access_aux *aux, u32 size) + aux->ctx_field_size = size; + } + ++static bool bpf_is_ldimm64(const struct bpf_insn *insn) ++{ ++ return insn->code == (BPF_LD | BPF_IMM | BPF_DW); ++} ++ + static inline bool bpf_pseudo_func(const struct bpf_insn *insn) + { +- return insn->code == (BPF_LD | BPF_IMM | BPF_DW) && +- insn->src_reg == BPF_PSEUDO_FUNC; ++ return bpf_is_ldimm64(insn) && insn->src_reg == BPF_PSEUDO_FUNC; + } + + struct bpf_prog_ops { +@@ -825,6 +829,11 @@ struct btf_func_model { + */ + #define BPF_TRAMP_F_SHARE_IPMODIFY BIT(6) + ++/* Indicate that current trampoline is in a tail call context. Then, it has to ++ * cache and restore tail_call_cnt to avoid infinite tail call loop. ++ */ ++#define BPF_TRAMP_F_TAIL_CALL_CTX BIT(7) ++ + /* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50 + * bytes on x86. + */ +diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h +index 1a32baa78ce26..f080ccf27d256 100644 +--- a/include/linux/bpf_verifier.h ++++ b/include/linux/bpf_verifier.h +@@ -429,6 +429,7 @@ struct bpf_insn_aux_data { + /* below fields are initialized once */ + unsigned int orig_idx; /* original instruction index */ + bool prune_point; ++ bool jmp_point; + }; + + #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ +diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h +index ee0d75d9a302d..1e0df607e40c4 100644 +--- a/include/linux/f2fs_fs.h ++++ b/include/linux/f2fs_fs.h +@@ -40,9 +40,8 @@ + + #define F2FS_ENC_UTF8_12_1 1 + +-#define F2FS_IO_SIZE(sbi) (1 << F2FS_OPTION(sbi).write_io_size_bits) /* Blocks */ +-#define F2FS_IO_SIZE_KB(sbi) (1 << (F2FS_OPTION(sbi).write_io_size_bits + 2)) /* KB */ +-#define F2FS_IO_SIZE_BYTES(sbi) (1 << (F2FS_OPTION(sbi).write_io_size_bits + 12)) /* B */ ++#define F2FS_IO_SIZE(sbi) BIT(F2FS_OPTION(sbi).write_io_size_bits) /* Blocks */ ++#define F2FS_IO_SIZE_KB(sbi) BIT(F2FS_OPTION(sbi).write_io_size_bits + 2) /* KB */ + #define F2FS_IO_SIZE_BITS(sbi) (F2FS_OPTION(sbi).write_io_size_bits) /* power of 2 */ + #define F2FS_IO_SIZE_MASK(sbi) (F2FS_IO_SIZE(sbi) - 1) + #define F2FS_IO_ALIGNED(sbi) (F2FS_IO_SIZE(sbi) > 1) +@@ -340,7 +339,7 @@ enum { + OFFSET_BIT_SHIFT + }; + +-#define OFFSET_BIT_MASK (0x07) /* (0x01 << OFFSET_BIT_SHIFT) - 1 */ ++#define OFFSET_BIT_MASK GENMASK(OFFSET_BIT_SHIFT - 1, 0) + + struct node_footer { + __le32 nid; /* node id */ +@@ -545,7 +544,7 @@ typedef __le32 f2fs_hash_t; + #define MAX_DIR_HASH_DEPTH 63 + + /* MAX buckets in one level of dir */ +-#define MAX_DIR_BUCKETS (1 << ((MAX_DIR_HASH_DEPTH / 2) - 1)) ++#define MAX_DIR_BUCKETS BIT((MAX_DIR_HASH_DEPTH / 2) - 1) + + /* + * space utilization of regular dentry and inline dentry (w/o extra reservation) +diff --git a/include/linux/fs.h b/include/linux/fs.h +index b6af6abc7a77f..4a1911dcf834b 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -2177,6 +2177,7 @@ struct file_operations { + int (*flock) (struct file *, int, struct file_lock *); + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); + ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); ++ void (*splice_eof)(struct file *file); + int (*setlease)(struct file *, long, struct file_lock **, void **); + long (*fallocate)(struct file *file, int mode, loff_t offset, + loff_t len); +diff --git a/include/linux/group_cpus.h b/include/linux/group_cpus.h +new file mode 100644 +index 0000000000000..e42807ec61f6e +--- /dev/null ++++ b/include/linux/group_cpus.h +@@ -0,0 +1,14 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (C) 2016 Thomas Gleixner. ++ * Copyright (C) 2016-2017 Christoph Hellwig. ++ */ ++ ++#ifndef __LINUX_GROUP_CPUS_H ++#define __LINUX_GROUP_CPUS_H ++#include ++#include ++ ++struct cpumask *group_cpus_evenly(unsigned int numgrps); ++ ++#endif +diff --git a/include/linux/net.h b/include/linux/net.h +index 18d942bbdf6e0..25baca60f6cba 100644 +--- a/include/linux/net.h ++++ b/include/linux/net.h +@@ -209,6 +209,7 @@ struct proto_ops { + int offset, size_t size, int flags); + ssize_t (*splice_read)(struct socket *sock, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, unsigned int flags); ++ void (*splice_eof)(struct socket *sock); + int (*set_peek_off)(struct sock *sk, int val); + int (*peek_len)(struct socket *sock); + +diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h +index 03307b72de6c6..1be5a1fa6a3a8 100644 +--- a/include/linux/pagemap.h ++++ b/include/linux/pagemap.h +@@ -199,6 +199,9 @@ enum mapping_flags { + /* writeback related tags are not used */ + AS_NO_WRITEBACK_TAGS = 5, + AS_LARGE_FOLIO_SUPPORT = 6, ++ AS_RELEASE_ALWAYS, /* Call ->release_folio(), even if no private data */ ++ AS_STABLE_WRITES, /* must wait for writeback before modifying ++ folio contents */ + }; + + /** +@@ -269,6 +272,36 @@ static inline int mapping_use_writeback_tags(struct address_space *mapping) + return !test_bit(AS_NO_WRITEBACK_TAGS, &mapping->flags); + } + ++static inline bool mapping_release_always(const struct address_space *mapping) ++{ ++ return test_bit(AS_RELEASE_ALWAYS, &mapping->flags); ++} ++ ++static inline void mapping_set_release_always(struct address_space *mapping) ++{ ++ set_bit(AS_RELEASE_ALWAYS, &mapping->flags); ++} ++ ++static inline void mapping_clear_release_always(struct address_space *mapping) ++{ ++ clear_bit(AS_RELEASE_ALWAYS, &mapping->flags); ++} ++ ++static inline bool mapping_stable_writes(const struct address_space *mapping) ++{ ++ return test_bit(AS_STABLE_WRITES, &mapping->flags); ++} ++ ++static inline void mapping_set_stable_writes(struct address_space *mapping) ++{ ++ set_bit(AS_STABLE_WRITES, &mapping->flags); ++} ++ ++static inline void mapping_clear_stable_writes(struct address_space *mapping) ++{ ++ clear_bit(AS_STABLE_WRITES, &mapping->flags); ++} ++ + static inline gfp_t mapping_gfp_mask(struct address_space * mapping) + { + return mapping->gfp_mask; +diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h +index c1637515a8a41..c953b8c0d2f43 100644 +--- a/include/linux/skmsg.h ++++ b/include/linux/skmsg.h +@@ -106,6 +106,7 @@ struct sk_psock { + struct mutex work_mutex; + struct sk_psock_work_state work_state; + struct delayed_work work; ++ struct sock *sk_pair; + struct rcu_work rwork; + }; + +diff --git a/include/linux/socket.h b/include/linux/socket.h +index 1db29aab8f9c3..b3c58042bd254 100644 +--- a/include/linux/socket.h ++++ b/include/linux/socket.h +@@ -324,6 +324,7 @@ struct ucred { + */ + + #define MSG_ZEROCOPY 0x4000000 /* Use user data in kernel path */ ++#define MSG_SPLICE_PAGES 0x8000000 /* Splice the pages from the iterator in sendmsg() */ + #define MSG_FASTOPEN 0x20000000 /* Send data in TCP SYN */ + #define MSG_CMSG_CLOEXEC 0x40000000 /* Set close_on_exec for file + descriptor received through +@@ -334,6 +335,8 @@ struct ucred { + #define MSG_CMSG_COMPAT 0 /* We never have 32 bit fixups */ + #endif + ++/* Flags to be cleared on entry by sendmsg and sendmmsg syscalls */ ++#define MSG_INTERNAL_SENDMSG_FLAGS (MSG_SPLICE_PAGES) + + /* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */ + #define SOL_IP 0 +diff --git a/include/linux/splice.h b/include/linux/splice.h +index a55179fd60fc3..41a70687be853 100644 +--- a/include/linux/splice.h ++++ b/include/linux/splice.h +@@ -38,6 +38,7 @@ struct splice_desc { + struct file *file; /* file to read/write */ + void *data; /* cookie */ + } u; ++ void (*splice_eof)(struct splice_desc *sd); /* Unexpected EOF handler */ + loff_t pos; /* file position */ + loff_t *opos; /* sendfile: output position */ + size_t num_spliced; /* number of bytes already spliced */ +diff --git a/include/linux/udp.h b/include/linux/udp.h +index e96da4157d04d..efd9ab6df3797 100644 +--- a/include/linux/udp.h ++++ b/include/linux/udp.h +@@ -30,25 +30,33 @@ static inline u32 udp_hashfn(const struct net *net, u32 num, u32 mask) + return (num + net_hash_mix(net)) & mask; + } + ++enum { ++ UDP_FLAGS_CORK, /* Cork is required */ ++ UDP_FLAGS_NO_CHECK6_TX, /* Send zero UDP6 checksums on TX? */ ++ UDP_FLAGS_NO_CHECK6_RX, /* Allow zero UDP6 checksums on RX? */ ++ UDP_FLAGS_GRO_ENABLED, /* Request GRO aggregation */ ++ UDP_FLAGS_ACCEPT_FRAGLIST, ++ UDP_FLAGS_ACCEPT_L4, ++ UDP_FLAGS_ENCAP_ENABLED, /* This socket enabled encap */ ++}; ++ + struct udp_sock { + /* inet_sock has to be the first member */ + struct inet_sock inet; + #define udp_port_hash inet.sk.__sk_common.skc_u16hashes[0] + #define udp_portaddr_hash inet.sk.__sk_common.skc_u16hashes[1] + #define udp_portaddr_node inet.sk.__sk_common.skc_portaddr_node ++ ++ unsigned long udp_flags; ++ + int pending; /* Any pending frames ? */ +- unsigned int corkflag; /* Cork is required */ + __u8 encap_type; /* Is this an Encapsulation socket? */ +- unsigned char no_check6_tx:1,/* Send zero UDP6 checksums on TX? */ +- no_check6_rx:1,/* Allow zero UDP6 checksums on RX? */ +- encap_enabled:1, /* This socket enabled encap +- * processing; UDP tunnels and +- * different encapsulation layer set +- * this +- */ +- gro_enabled:1, /* Request GRO aggregation */ +- accept_udp_l4:1, +- accept_udp_fraglist:1; ++ ++/* indicator bits used by pcflag: */ ++#define UDPLITE_BIT 0x1 /* set by udplite proto init function */ ++#define UDPLITE_SEND_CC 0x2 /* set via udplite setsockopt */ ++#define UDPLITE_RECV_CC 0x4 /* set via udplite setsocktopt */ ++ __u8 pcflag; /* marks socket as UDP-Lite if > 0 */ + /* + * Following member retains the information to create a UDP header + * when the socket is uncorked. +@@ -60,12 +68,6 @@ struct udp_sock { + */ + __u16 pcslen; + __u16 pcrlen; +-/* indicator bits used by pcflag: */ +-#define UDPLITE_BIT 0x1 /* set by udplite proto init function */ +-#define UDPLITE_SEND_CC 0x2 /* set via udplite setsockopt */ +-#define UDPLITE_RECV_CC 0x4 /* set via udplite setsocktopt */ +- __u8 pcflag; /* marks socket as UDP-Lite if > 0 */ +- __u8 unused[3]; + /* + * For encapsulation sockets. + */ +@@ -89,6 +91,17 @@ struct udp_sock { + int forward_deficit; + }; + ++#define udp_test_bit(nr, sk) \ ++ test_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags) ++#define udp_set_bit(nr, sk) \ ++ set_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags) ++#define udp_test_and_set_bit(nr, sk) \ ++ test_and_set_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags) ++#define udp_clear_bit(nr, sk) \ ++ clear_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags) ++#define udp_assign_bit(nr, sk, val) \ ++ assign_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags, val) ++ + #define UDP_MAX_SEGMENTS (1 << 6UL) + + static inline struct udp_sock *udp_sk(const struct sock *sk) +@@ -98,22 +111,22 @@ static inline struct udp_sock *udp_sk(const struct sock *sk) + + static inline void udp_set_no_check6_tx(struct sock *sk, bool val) + { +- udp_sk(sk)->no_check6_tx = val; ++ udp_assign_bit(NO_CHECK6_TX, sk, val); + } + + static inline void udp_set_no_check6_rx(struct sock *sk, bool val) + { +- udp_sk(sk)->no_check6_rx = val; ++ udp_assign_bit(NO_CHECK6_RX, sk, val); + } + +-static inline bool udp_get_no_check6_tx(struct sock *sk) ++static inline bool udp_get_no_check6_tx(const struct sock *sk) + { +- return udp_sk(sk)->no_check6_tx; ++ return udp_test_bit(NO_CHECK6_TX, sk); + } + +-static inline bool udp_get_no_check6_rx(struct sock *sk) ++static inline bool udp_get_no_check6_rx(const struct sock *sk) + { +- return udp_sk(sk)->no_check6_rx; ++ return udp_test_bit(NO_CHECK6_RX, sk); + } + + static inline void udp_cmsg_recv(struct msghdr *msg, struct sock *sk, +@@ -132,10 +145,12 @@ static inline bool udp_unexpected_gso(struct sock *sk, struct sk_buff *skb) + if (!skb_is_gso(skb)) + return false; + +- if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && !udp_sk(sk)->accept_udp_l4) ++ if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && ++ !udp_test_bit(ACCEPT_L4, sk)) + return true; + +- if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST && !udp_sk(sk)->accept_udp_fraglist) ++ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST && ++ !udp_test_bit(ACCEPT_FRAGLIST, sk)) + return true; + + return false; +@@ -143,8 +158,8 @@ static inline bool udp_unexpected_gso(struct sock *sk, struct sk_buff *skb) + + static inline void udp_allow_gso(struct sock *sk) + { +- udp_sk(sk)->accept_udp_l4 = 1; +- udp_sk(sk)->accept_udp_fraglist = 1; ++ udp_set_bit(ACCEPT_L4, sk); ++ udp_set_bit(ACCEPT_FRAGLIST, sk); + } + + #define udp_portaddr_for_each_entry(__sk, list) \ +diff --git a/include/net/af_unix.h b/include/net/af_unix.h +index 480fa579787e5..55ca217c626b7 100644 +--- a/include/net/af_unix.h ++++ b/include/net/af_unix.h +@@ -77,6 +77,7 @@ static inline struct unix_sock *unix_sk(const struct sock *sk) + { + return (struct unix_sock *)sk; + } ++#define unix_peer(sk) (unix_sk(sk)->peer) + + #define peer_wait peer_wq.wait + +diff --git a/include/net/inet_common.h b/include/net/inet_common.h +index cec453c18f1d6..4673bbfd2811f 100644 +--- a/include/net/inet_common.h ++++ b/include/net/inet_common.h +@@ -33,6 +33,7 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags, + bool kern); + int inet_send_prepare(struct sock *sk); + int inet_sendmsg(struct socket *sock, struct msghdr *msg, size_t size); ++void inet_splice_eof(struct socket *sock); + ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset, + size_t size, int flags); + int inet_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, +diff --git a/include/net/ip.h b/include/net/ip.h +index c286344628dba..c83c09c65623f 100644 +--- a/include/net/ip.h ++++ b/include/net/ip.h +@@ -95,7 +95,7 @@ static inline void ipcm_init_sk(struct ipcm_cookie *ipcm, + ipcm_init(ipcm); + + ipcm->sockc.mark = READ_ONCE(inet->sk.sk_mark); +- ipcm->sockc.tsflags = inet->sk.sk_tsflags; ++ ipcm->sockc.tsflags = READ_ONCE(inet->sk.sk_tsflags); + ipcm->oif = READ_ONCE(inet->sk.sk_bound_dev_if); + ipcm->addr = inet->inet_saddr; + ipcm->protocol = inet->inet_num; +diff --git a/include/net/netfilter/nf_conntrack_act_ct.h b/include/net/netfilter/nf_conntrack_act_ct.h +index 078d3c52c03f9..e5f2f0b73a9a0 100644 +--- a/include/net/netfilter/nf_conntrack_act_ct.h ++++ b/include/net/netfilter/nf_conntrack_act_ct.h +@@ -20,7 +20,22 @@ static inline struct nf_conn_act_ct_ext *nf_conn_act_ct_ext_find(const struct nf + #endif + } + +-static inline struct nf_conn_act_ct_ext *nf_conn_act_ct_ext_add(struct nf_conn *ct) ++static inline void nf_conn_act_ct_ext_fill(struct sk_buff *skb, struct nf_conn *ct, ++ enum ip_conntrack_info ctinfo) ++{ ++#if IS_ENABLED(CONFIG_NET_ACT_CT) ++ struct nf_conn_act_ct_ext *act_ct_ext; ++ ++ act_ct_ext = nf_conn_act_ct_ext_find(ct); ++ if (dev_net(skb->dev) == &init_net && act_ct_ext) ++ act_ct_ext->ifindex[CTINFO2DIR(ctinfo)] = skb->dev->ifindex; ++#endif ++} ++ ++static inline struct ++nf_conn_act_ct_ext *nf_conn_act_ct_ext_add(struct sk_buff *skb, ++ struct nf_conn *ct, ++ enum ip_conntrack_info ctinfo) + { + #if IS_ENABLED(CONFIG_NET_ACT_CT) + struct nf_conn_act_ct_ext *act_ct = nf_ct_ext_find(ct, NF_CT_EXT_ACT_CT); +@@ -29,22 +44,11 @@ static inline struct nf_conn_act_ct_ext *nf_conn_act_ct_ext_add(struct nf_conn * + return act_ct; + + act_ct = nf_ct_ext_add(ct, NF_CT_EXT_ACT_CT, GFP_ATOMIC); ++ nf_conn_act_ct_ext_fill(skb, ct, ctinfo); + return act_ct; + #else + return NULL; + #endif + } + +-static inline void nf_conn_act_ct_ext_fill(struct sk_buff *skb, struct nf_conn *ct, +- enum ip_conntrack_info ctinfo) +-{ +-#if IS_ENABLED(CONFIG_NET_ACT_CT) +- struct nf_conn_act_ct_ext *act_ct_ext; +- +- act_ct_ext = nf_conn_act_ct_ext_find(ct); +- if (dev_net(skb->dev) == &init_net && act_ct_ext) +- act_ct_ext->ifindex[CTINFO2DIR(ctinfo)] = skb->dev->ifindex; +-#endif +-} +- + #endif /* _NF_CONNTRACK_ACT_CT_H */ +diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h +index cd982f4a0f50c..dde4dd9c4012c 100644 +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -53,14 +53,17 @@ struct nf_flowtable_type { + struct list_head list; + int family; + int (*init)(struct nf_flowtable *ft); ++ bool (*gc)(const struct flow_offload *flow); + int (*setup)(struct nf_flowtable *ft, + struct net_device *dev, + enum flow_block_command cmd); + int (*action)(struct net *net, +- const struct flow_offload *flow, ++ struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule); + void (*free)(struct nf_flowtable *ft); ++ void (*get)(struct nf_flowtable *ft); ++ void (*put)(struct nf_flowtable *ft); + nf_hookfn *hook; + struct module *owner; + }; +@@ -164,6 +167,8 @@ enum nf_flow_flags { + NF_FLOW_HW_DYING, + NF_FLOW_HW_DEAD, + NF_FLOW_HW_PENDING, ++ NF_FLOW_HW_BIDIRECTIONAL, ++ NF_FLOW_HW_ESTABLISHED, + }; + + enum flow_offload_type { +@@ -237,6 +242,11 @@ nf_flow_table_offload_add_cb(struct nf_flowtable *flow_table, + } + + list_add_tail(&block_cb->list, &block->cb_list); ++ up_write(&flow_table->flow_block_lock); ++ ++ if (flow_table->type->get) ++ flow_table->type->get(flow_table); ++ return 0; + + unlock: + up_write(&flow_table->flow_block_lock); +@@ -259,6 +269,9 @@ nf_flow_table_offload_del_cb(struct nf_flowtable *flow_table, + WARN_ON(true); + } + up_write(&flow_table->flow_block_lock); ++ ++ if (flow_table->type->put) ++ flow_table->type->put(flow_table); + } + + int flow_offload_route_init(struct flow_offload *flow, +@@ -266,7 +279,7 @@ int flow_offload_route_init(struct flow_offload *flow, + + int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow); + void flow_offload_refresh(struct nf_flowtable *flow_table, +- struct flow_offload *flow); ++ struct flow_offload *flow, bool force); + + struct flow_offload_tuple_rhash *flow_offload_lookup(struct nf_flowtable *flow_table, + struct flow_offload_tuple *tuple); +@@ -312,10 +325,10 @@ void nf_flow_table_offload_flush_cleanup(struct nf_flowtable *flowtable); + int nf_flow_table_offload_setup(struct nf_flowtable *flowtable, + struct net_device *dev, + enum flow_block_command cmd); +-int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow, ++int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule); +-int nf_flow_rule_route_ipv6(struct net *net, const struct flow_offload *flow, ++int nf_flow_rule_route_ipv6(struct net *net, struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule); + +diff --git a/include/net/netfilter/nf_tables_ipv4.h b/include/net/netfilter/nf_tables_ipv4.h +index c4a6147b0ef8c..5225d2bd1a6e9 100644 +--- a/include/net/netfilter/nf_tables_ipv4.h ++++ b/include/net/netfilter/nf_tables_ipv4.h +@@ -29,8 +29,8 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt) + if (iph->ihl < 5 || iph->version != 4) + return -1; + +- len = ntohs(iph->tot_len); +- thoff = iph->ihl * 4; ++ len = iph_totlen(pkt->skb, iph); ++ thoff = skb_network_offset(pkt->skb) + (iph->ihl * 4); + if (pkt->skb->len < len) + return -1; + else if (len < thoff) +@@ -62,7 +62,7 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt) + if (iph->ihl < 5 || iph->version != 4) + goto inhdr_error; + +- len = ntohs(iph->tot_len); ++ len = iph_totlen(pkt->skb, iph); + thoff = iph->ihl * 4; + if (pkt->skb->len < len) { + __IP_INC_STATS(nft_net(pkt), IPSTATS_MIB_INTRUNCATEDPKTS); +diff --git a/include/net/sock.h b/include/net/sock.h +index b6027b01c2455..6b51e85ae69e3 100644 +--- a/include/net/sock.h ++++ b/include/net/sock.h +@@ -1279,6 +1279,7 @@ struct proto { + size_t len, int flags, int *addr_len); + int (*sendpage)(struct sock *sk, struct page *page, + int offset, size_t size, int flags); ++ void (*splice_eof)(struct socket *sock); + int (*bind)(struct sock *sk, + struct sockaddr *addr, int addr_len); + int (*bind_add)(struct sock *sk, +@@ -1928,7 +1929,9 @@ struct sockcm_cookie { + static inline void sockcm_init(struct sockcm_cookie *sockc, + const struct sock *sk) + { +- *sockc = (struct sockcm_cookie) { .tsflags = sk->sk_tsflags }; ++ *sockc = (struct sockcm_cookie) { ++ .tsflags = READ_ONCE(sk->sk_tsflags) ++ }; + } + + int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg, +@@ -2741,9 +2744,9 @@ void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk, + static inline void + sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) + { +- ktime_t kt = skb->tstamp; + struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); +- ++ u32 tsflags = READ_ONCE(sk->sk_tsflags); ++ ktime_t kt = skb->tstamp; + /* + * generate control messages if + * - receive time stamping in software requested +@@ -2751,10 +2754,10 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) + * - hardware time stamps available and wanted + */ + if (sock_flag(sk, SOCK_RCVTSTAMP) || +- (sk->sk_tsflags & SOF_TIMESTAMPING_RX_SOFTWARE) || +- (kt && sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) || ++ (tsflags & SOF_TIMESTAMPING_RX_SOFTWARE) || ++ (kt && tsflags & SOF_TIMESTAMPING_SOFTWARE) || + (hwtstamps->hwtstamp && +- (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE))) ++ (tsflags & SOF_TIMESTAMPING_RAW_HARDWARE))) + __sock_recv_timestamp(msg, sk, skb); + else + sock_write_timestamp(sk, kt); +@@ -2776,7 +2779,8 @@ static inline void sock_recv_cmsgs(struct msghdr *msg, struct sock *sk, + #define TSFLAGS_ANY (SOF_TIMESTAMPING_SOFTWARE | \ + SOF_TIMESTAMPING_RAW_HARDWARE) + +- if (sk->sk_flags & FLAGS_RECV_CMSGS || sk->sk_tsflags & TSFLAGS_ANY) ++ if (sk->sk_flags & FLAGS_RECV_CMSGS || ++ READ_ONCE(sk->sk_tsflags) & TSFLAGS_ANY) + __sock_recv_cmsgs(msg, sk, skb); + else if (unlikely(sock_flag(sk, SOCK_TIMESTAMP))) + sock_write_timestamp(sk, skb->tstamp); +@@ -2825,6 +2829,11 @@ static inline bool sk_is_tcp(const struct sock *sk) + return sk->sk_type == SOCK_STREAM && sk->sk_protocol == IPPROTO_TCP; + } + ++static inline bool sk_is_stream_unix(const struct sock *sk) ++{ ++ return sk->sk_family == AF_UNIX && sk->sk_type == SOCK_STREAM; ++} ++ + /** + * sk_eat_skb - Release a skb if it is no longer needed + * @sk: socket to eat this skb from +diff --git a/include/net/tcp.h b/include/net/tcp.h +index c3d56b337f358..4c838f7290dd9 100644 +--- a/include/net/tcp.h ++++ b/include/net/tcp.h +@@ -332,6 +332,7 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size); + int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size); + int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg, int *copied, + size_t size, struct ubuf_info *uarg); ++void tcp_splice_eof(struct socket *sock); + int tcp_sendpage(struct sock *sk, struct page *page, int offset, size_t size, + int flags); + int tcp_sendpage_locked(struct sock *sk, struct page *page, int offset, +diff --git a/include/net/udp.h b/include/net/udp.h +index fee053bcd17c6..fa4cdbe55552c 100644 +--- a/include/net/udp.h ++++ b/include/net/udp.h +@@ -269,6 +269,7 @@ int udp_get_port(struct sock *sk, unsigned short snum, + int udp_err(struct sk_buff *, u32); + int udp_abort(struct sock *sk, int err); + int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); ++void udp_splice_eof(struct socket *sock); + int udp_push_pending_frames(struct sock *sk); + void udp_flush_pending_frames(struct sock *sk); + int udp_cmsg_send(struct sock *sk, struct msghdr *msg, u16 *gso_size); +diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h +index 72394f441dad8..e5f81710b18f4 100644 +--- a/include/net/udp_tunnel.h ++++ b/include/net/udp_tunnel.h +@@ -174,16 +174,13 @@ static inline int udp_tunnel_handle_offloads(struct sk_buff *skb, bool udp_csum) + } + #endif + +-static inline void udp_tunnel_encap_enable(struct socket *sock) ++static inline void udp_tunnel_encap_enable(struct sock *sk) + { +- struct udp_sock *up = udp_sk(sock->sk); +- +- if (up->encap_enabled) ++ if (udp_test_and_set_bit(ENCAP_ENABLED, sk)) + return; + +- up->encap_enabled = 1; + #if IS_ENABLED(CONFIG_IPV6) +- if (sock->sk->sk_family == PF_INET6) ++ if (READ_ONCE(sk->sk_family) == PF_INET6) + ipv6_stub->udpv6_encap_enable(); + #endif + udp_encap_enable(); +diff --git a/io_uring/net.c b/io_uring/net.c +index 57c626cb4d1a5..67f09a40bcb21 100644 +--- a/io_uring/net.c ++++ b/io_uring/net.c +@@ -389,6 +389,7 @@ int io_send(struct io_kiocb *req, unsigned int issue_flags) + if (flags & MSG_WAITALL) + min_ret = iov_iter_count(&msg.msg_iter); + ++ flags &= ~MSG_INTERNAL_SENDMSG_FLAGS; + msg.msg_flags = flags; + ret = sock_sendmsg(sock, &msg); + if (ret < min_ret) { +@@ -1137,6 +1138,7 @@ int io_send_zc(struct io_kiocb *req, unsigned int issue_flags) + msg_flags |= MSG_DONTWAIT; + if (msg_flags & MSG_WAITALL) + min_ret = iov_iter_count(&msg.msg_iter); ++ msg_flags &= ~MSG_INTERNAL_SENDMSG_FLAGS; + + msg.msg_flags = msg_flags; + msg.msg_ubuf = &io_notif_to_data(zc->notif)->uarg; +diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c +index 7225cb67c0d3a..76bf1de261152 100644 +--- a/kernel/bpf/core.c ++++ b/kernel/bpf/core.c +@@ -365,9 +365,18 @@ static int bpf_adj_delta_to_imm(struct bpf_insn *insn, u32 pos, s32 end_old, + static int bpf_adj_delta_to_off(struct bpf_insn *insn, u32 pos, s32 end_old, + s32 end_new, s32 curr, const bool probe_pass) + { +- const s32 off_min = S16_MIN, off_max = S16_MAX; ++ s64 off_min, off_max, off; + s32 delta = end_new - end_old; +- s32 off = insn->off; ++ ++ if (insn->code == (BPF_JMP32 | BPF_JA)) { ++ off = insn->imm; ++ off_min = S32_MIN; ++ off_max = S32_MAX; ++ } else { ++ off = insn->off; ++ off_min = S16_MIN; ++ off_max = S16_MAX; ++ } + + if (curr < pos && curr + off + 1 >= end_old) + off += delta; +@@ -375,8 +384,12 @@ static int bpf_adj_delta_to_off(struct bpf_insn *insn, u32 pos, s32 end_old, + off -= delta; + if (off < off_min || off > off_max) + return -ERANGE; +- if (!probe_pass) +- insn->off = off; ++ if (!probe_pass) { ++ if (insn->code == (BPF_JMP32 | BPF_JA)) ++ insn->imm = off; ++ else ++ insn->off = off; ++ } + return 0; + } + +@@ -1586,6 +1599,7 @@ EXPORT_SYMBOL_GPL(__bpf_call_base); + INSN_3(JMP, JSLE, K), \ + INSN_3(JMP, JSET, K), \ + INSN_2(JMP, JA), \ ++ INSN_2(JMP32, JA), \ + /* Store instructions. */ \ + /* Register based. */ \ + INSN_3(STX, MEM, B), \ +@@ -1862,6 +1876,9 @@ out: + JMP_JA: + insn += insn->off; + CONT; ++ JMP32_JA: ++ insn += insn->imm; ++ CONT; + JMP_EXIT: + return BPF_R0; + /* JMP */ +diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c +index c4381dfcd6b09..748ac86169941 100644 +--- a/kernel/bpf/trampoline.c ++++ b/kernel/bpf/trampoline.c +@@ -443,8 +443,8 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut + goto out; + } + +- /* clear all bits except SHARE_IPMODIFY */ +- tr->flags &= BPF_TRAMP_F_SHARE_IPMODIFY; ++ /* clear all bits except SHARE_IPMODIFY and TAIL_CALL_CTX */ ++ tr->flags &= (BPF_TRAMP_F_SHARE_IPMODIFY | BPF_TRAMP_F_TAIL_CALL_CTX); + + if (tlinks[BPF_TRAMP_FEXIT].nr_links || + tlinks[BPF_TRAMP_MODIFY_RETURN].nr_links) { +diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c +index 12d360d80c149..142e10d49fd81 100644 +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -2254,7 +2254,10 @@ static int check_subprogs(struct bpf_verifier_env *env) + goto next; + if (BPF_OP(code) == BPF_EXIT || BPF_OP(code) == BPF_CALL) + goto next; +- off = i + insn[i].off + 1; ++ if (code == (BPF_JMP32 | BPF_JA)) ++ off = i + insn[i].imm + 1; ++ else ++ off = i + insn[i].off + 1; + if (off < subprog_start || off >= subprog_end) { + verbose(env, "jump out of range from insn %d to %d\n", i, off); + return -EINVAL; +@@ -2266,6 +2269,7 @@ next: + * or unconditional jump back + */ + if (code != (BPF_JMP | BPF_EXIT) && ++ code != (BPF_JMP32 | BPF_JA) && + code != (BPF_JMP | BPF_JA)) { + verbose(env, "last insn is not an exit or jmp\n"); + return -EINVAL; +@@ -2512,6 +2516,16 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno, + return 0; + } + ++static void mark_jmp_point(struct bpf_verifier_env *env, int idx) ++{ ++ env->insn_aux_data[idx].jmp_point = true; ++} ++ ++static bool is_jmp_point(struct bpf_verifier_env *env, int insn_idx) ++{ ++ return env->insn_aux_data[insn_idx].jmp_point; ++} ++ + /* for any branch, call, exit record the history of jmps in the given state */ + static int push_jmp_history(struct bpf_verifier_env *env, + struct bpf_verifier_state *cur) +@@ -2520,6 +2534,9 @@ static int push_jmp_history(struct bpf_verifier_env *env, + struct bpf_idx_pair *p; + size_t alloc_size; + ++ if (!is_jmp_point(env, env->insn_idx)) ++ return 0; ++ + cnt++; + alloc_size = kmalloc_size_roundup(size_mul(cnt, sizeof(*p))); + p = krealloc(cur->jmp_history, alloc_size, GFP_USER); +@@ -2534,12 +2551,29 @@ static int push_jmp_history(struct bpf_verifier_env *env, + + /* Backtrack one insn at a time. If idx is not at the top of recorded + * history then previous instruction came from straight line execution. ++ * Return -ENOENT if we exhausted all instructions within given state. ++ * ++ * It's legal to have a bit of a looping with the same starting and ending ++ * insn index within the same state, e.g.: 3->4->5->3, so just because current ++ * instruction index is the same as state's first_idx doesn't mean we are ++ * done. If there is still some jump history left, we should keep going. We ++ * need to take into account that we might have a jump history between given ++ * state's parent and itself, due to checkpointing. In this case, we'll have ++ * history entry recording a jump from last instruction of parent state and ++ * first instruction of given state. + */ + static int get_prev_insn_idx(struct bpf_verifier_state *st, int i, + u32 *history) + { + u32 cnt = *history; + ++ if (i == st->first_insn_idx) { ++ if (cnt == 0) ++ return -ENOENT; ++ if (cnt == 1 && st->jmp_history[0].idx == i) ++ return -ENOENT; ++ } ++ + if (cnt && st->jmp_history[cnt - 1].idx == i) { + i = st->jmp_history[cnt - 1].prev_idx; + (*history)--; +@@ -3035,9 +3069,9 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int frame, int r + * Nothing to be tracked further in the parent state. + */ + return 0; +- if (i == first_idx) +- break; + i = get_prev_insn_idx(st, i, &history); ++ if (i == -ENOENT) ++ break; + if (i >= env->prog->len) { + /* This can happen if backtracking reached insn 0 + * and there are still reg_mask or stack_mask +@@ -11000,11 +11034,16 @@ static struct bpf_verifier_state_list **explored_state( + return &env->explored_states[(idx ^ state->callsite) % state_htab_size(env)]; + } + +-static void init_explored_state(struct bpf_verifier_env *env, int idx) ++static void mark_prune_point(struct bpf_verifier_env *env, int idx) + { + env->insn_aux_data[idx].prune_point = true; + } + ++static bool is_prune_point(struct bpf_verifier_env *env, int insn_idx) ++{ ++ return env->insn_aux_data[insn_idx].prune_point; ++} ++ + enum { + DONE_EXPLORING = 0, + KEEP_EXPLORING = 1, +@@ -11033,9 +11072,11 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env, + return -EINVAL; + } + +- if (e == BRANCH) ++ if (e == BRANCH) { + /* mark branch target for state pruning */ +- init_explored_state(env, w); ++ mark_prune_point(env, w); ++ mark_jmp_point(env, w); ++ } + + if (insn_state[w] == 0) { + /* tree-edge */ +@@ -11062,21 +11103,23 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env, + return DONE_EXPLORING; + } + +-static int visit_func_call_insn(int t, int insn_cnt, +- struct bpf_insn *insns, ++static int visit_func_call_insn(int t, struct bpf_insn *insns, + struct bpf_verifier_env *env, + bool visit_callee) + { +- int ret; ++ int ret, insn_sz; + +- ret = push_insn(t, t + 1, FALLTHROUGH, env, false); ++ insn_sz = bpf_is_ldimm64(&insns[t]) ? 2 : 1; ++ ret = push_insn(t, t + insn_sz, FALLTHROUGH, env, false); + if (ret) + return ret; + +- if (t + 1 < insn_cnt) +- init_explored_state(env, t + 1); ++ mark_prune_point(env, t + insn_sz); ++ /* when we exit from subprog, we need to record non-linear history */ ++ mark_jmp_point(env, t + insn_sz); ++ + if (visit_callee) { +- init_explored_state(env, t); ++ mark_prune_point(env, t); + ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env, + /* It's ok to allow recursion from CFG point of + * view. __check_func_call() will do the actual +@@ -11092,65 +11135,64 @@ static int visit_func_call_insn(int t, int insn_cnt, + * DONE_EXPLORING - the instruction was fully explored + * KEEP_EXPLORING - there is still work to be done before it is fully explored + */ +-static int visit_insn(int t, int insn_cnt, struct bpf_verifier_env *env) ++static int visit_insn(int t, struct bpf_verifier_env *env) + { +- struct bpf_insn *insns = env->prog->insnsi; +- int ret; ++ struct bpf_insn *insns = env->prog->insnsi, *insn = &insns[t]; ++ int ret, off, insn_sz; + +- if (bpf_pseudo_func(insns + t)) +- return visit_func_call_insn(t, insn_cnt, insns, env, true); ++ if (bpf_pseudo_func(insn)) ++ return visit_func_call_insn(t, insns, env, true); + + /* All non-branch instructions have a single fall-through edge. */ +- if (BPF_CLASS(insns[t].code) != BPF_JMP && +- BPF_CLASS(insns[t].code) != BPF_JMP32) +- return push_insn(t, t + 1, FALLTHROUGH, env, false); ++ if (BPF_CLASS(insn->code) != BPF_JMP && ++ BPF_CLASS(insn->code) != BPF_JMP32) { ++ insn_sz = bpf_is_ldimm64(insn) ? 2 : 1; ++ return push_insn(t, t + insn_sz, FALLTHROUGH, env, false); ++ } + +- switch (BPF_OP(insns[t].code)) { ++ switch (BPF_OP(insn->code)) { + case BPF_EXIT: + return DONE_EXPLORING; + + case BPF_CALL: +- if (insns[t].imm == BPF_FUNC_timer_set_callback) +- /* Mark this call insn to trigger is_state_visited() check +- * before call itself is processed by __check_func_call(). +- * Otherwise new async state will be pushed for further +- * exploration. ++ if (insn->imm == BPF_FUNC_timer_set_callback) ++ /* Mark this call insn as a prune point to trigger ++ * is_state_visited() check before call itself is ++ * processed by __check_func_call(). Otherwise new ++ * async state will be pushed for further exploration. + */ +- init_explored_state(env, t); +- return visit_func_call_insn(t, insn_cnt, insns, env, +- insns[t].src_reg == BPF_PSEUDO_CALL); ++ mark_prune_point(env, t); ++ return visit_func_call_insn(t, insns, env, insn->src_reg == BPF_PSEUDO_CALL); + + case BPF_JA: +- if (BPF_SRC(insns[t].code) != BPF_K) ++ if (BPF_SRC(insn->code) != BPF_K) + return -EINVAL; + ++ if (BPF_CLASS(insn->code) == BPF_JMP) ++ off = insn->off; ++ else ++ off = insn->imm; ++ + /* unconditional jump with single edge */ +- ret = push_insn(t, t + insns[t].off + 1, FALLTHROUGH, env, ++ ret = push_insn(t, t + off + 1, FALLTHROUGH, env, + true); + if (ret) + return ret; + +- /* unconditional jmp is not a good pruning point, +- * but it's marked, since backtracking needs +- * to record jmp history in is_state_visited(). +- */ +- init_explored_state(env, t + insns[t].off + 1); +- /* tell verifier to check for equivalent states +- * after every call and jump +- */ +- if (t + 1 < insn_cnt) +- init_explored_state(env, t + 1); ++ mark_prune_point(env, t + off + 1); ++ mark_jmp_point(env, t + off + 1); + + return ret; + + default: + /* conditional jump with two edges */ +- init_explored_state(env, t); ++ mark_prune_point(env, t); ++ + ret = push_insn(t, t + 1, FALLTHROUGH, env, true); + if (ret) + return ret; + +- return push_insn(t, t + insns[t].off + 1, BRANCH, env, true); ++ return push_insn(t, t + insn->off + 1, BRANCH, env, true); + } + } + +@@ -11181,7 +11223,7 @@ static int check_cfg(struct bpf_verifier_env *env) + while (env->cfg.cur_stack > 0) { + int t = insn_stack[env->cfg.cur_stack - 1]; + +- ret = visit_insn(t, insn_cnt, env); ++ ret = visit_insn(t, env); + switch (ret) { + case DONE_EXPLORING: + insn_state[t] = EXPLORED; +@@ -11205,11 +11247,21 @@ static int check_cfg(struct bpf_verifier_env *env) + } + + for (i = 0; i < insn_cnt; i++) { ++ struct bpf_insn *insn = &env->prog->insnsi[i]; ++ + if (insn_state[i] != EXPLORED) { + verbose(env, "unreachable insn %d\n", i); + ret = -EINVAL; + goto err_free; + } ++ if (bpf_is_ldimm64(insn)) { ++ if (insn_state[i + 1] != 0) { ++ verbose(env, "jump into the middle of ldimm64 insn %d\n", i); ++ ret = -EINVAL; ++ goto err_free; ++ } ++ i++; /* skip second half of ldimm64 */ ++ } + } + ret = 0; /* cfg looks good */ + +@@ -12178,11 +12230,11 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) + bool add_new_state = env->test_state_freq ? true : false; + + cur->last_insn_idx = env->prev_insn_idx; +- if (!env->insn_aux_data[insn_idx].prune_point) ++ if (!is_prune_point(env, insn_idx)) + /* this 'insn_idx' instruction wasn't marked, so we will not + * be doing state search here + */ +- return 0; ++ return push_jmp_history(env, cur); + + /* bpf progs typically have pruning point every 4 instructions + * http://vger.kernel.org/bpfconf2019.html#session-1 +@@ -12674,15 +12726,18 @@ static int do_check(struct bpf_verifier_env *env) + return err; + } else if (opcode == BPF_JA) { + if (BPF_SRC(insn->code) != BPF_K || +- insn->imm != 0 || + insn->src_reg != BPF_REG_0 || + insn->dst_reg != BPF_REG_0 || +- class == BPF_JMP32) { ++ (class == BPF_JMP && insn->imm != 0) || ++ (class == BPF_JMP32 && insn->off != 0)) { + verbose(env, "BPF_JA uses reserved fields\n"); + return -EINVAL; + } + +- env->insn_idx += insn->off + 1; ++ if (class == BPF_JMP) ++ env->insn_idx += insn->off + 1; ++ else ++ env->insn_idx += insn->imm + 1; + continue; + + } else if (opcode == BPF_EXIT) { +@@ -13508,13 +13563,13 @@ static bool insn_is_cond_jump(u8 code) + { + u8 op; + ++ op = BPF_OP(code); + if (BPF_CLASS(code) == BPF_JMP32) +- return true; ++ return op != BPF_JA; + + if (BPF_CLASS(code) != BPF_JMP) + return false; + +- op = BPF_OP(code); + return op != BPF_JA && op != BPF_EXIT && op != BPF_CALL; + } + +@@ -15442,6 +15497,9 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) + if (!tr) + return -ENOMEM; + ++ if (tgt_prog && tgt_prog->aux->tail_call_reachable) ++ tr->flags = BPF_TRAMP_F_TAIL_CALL_CTX; ++ + prog->aux->dst_trampoline = tr; + return 0; + } +diff --git a/kernel/cpu.c b/kernel/cpu.c +index 551468d9c5a85..e6f0101941ed8 100644 +--- a/kernel/cpu.c ++++ b/kernel/cpu.c +@@ -446,9 +446,31 @@ static int __init smt_cmdline_disable(char *str) + } + early_param("nosmt", smt_cmdline_disable); + +-static inline bool cpu_smt_allowed(unsigned int cpu) ++/* ++ * For Archicture supporting partial SMT states check if the thread is allowed. ++ * Otherwise this has already been checked through cpu_smt_max_threads when ++ * setting the SMT level. ++ */ ++static inline bool cpu_smt_thread_allowed(unsigned int cpu) + { +- if (cpu_smt_control == CPU_SMT_ENABLED) ++#ifdef CONFIG_SMT_NUM_THREADS_DYNAMIC ++ return topology_smt_thread_allowed(cpu); ++#else ++ return true; ++#endif ++} ++ ++static inline bool cpu_bootable(unsigned int cpu) ++{ ++ if (cpu_smt_control == CPU_SMT_ENABLED && cpu_smt_thread_allowed(cpu)) ++ return true; ++ ++ /* All CPUs are bootable if controls are not configured */ ++ if (cpu_smt_control == CPU_SMT_NOT_IMPLEMENTED) ++ return true; ++ ++ /* All CPUs are bootable if CPU is not SMT capable */ ++ if (cpu_smt_control == CPU_SMT_NOT_SUPPORTED) + return true; + + if (topology_is_primary_thread(cpu)) +@@ -471,7 +493,7 @@ bool cpu_smt_possible(void) + } + EXPORT_SYMBOL_GPL(cpu_smt_possible); + #else +-static inline bool cpu_smt_allowed(unsigned int cpu) { return true; } ++static inline bool cpu_bootable(unsigned int cpu) { return true; } + #endif + + static inline enum cpuhp_state +@@ -574,10 +596,10 @@ static int bringup_wait_for_ap(unsigned int cpu) + * SMT soft disabling on X86 requires to bring the CPU out of the + * BIOS 'wait for SIPI' state in order to set the CR4.MCE bit. The + * CPU marked itself as booted_once in notify_cpu_starting() so the +- * cpu_smt_allowed() check will now return false if this is not the ++ * cpu_bootable() check will now return false if this is not the + * primary sibling. + */ +- if (!cpu_smt_allowed(cpu)) ++ if (!cpu_bootable(cpu)) + return -ECANCELED; + + if (st->target <= CPUHP_AP_ONLINE_IDLE) +@@ -1464,7 +1486,7 @@ static int cpu_up(unsigned int cpu, enum cpuhp_state target) + err = -EBUSY; + goto out; + } +- if (!cpu_smt_allowed(cpu)) { ++ if (!cpu_bootable(cpu)) { + err = -EPERM; + goto out; + } +@@ -2294,6 +2316,12 @@ int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval) + for_each_online_cpu(cpu) { + if (topology_is_primary_thread(cpu)) + continue; ++ /* ++ * Disable can be called with CPU_SMT_ENABLED when changing ++ * from a higher to lower number of SMT threads per core. ++ */ ++ if (ctrlval == CPU_SMT_ENABLED && cpu_smt_thread_allowed(cpu)) ++ continue; + ret = cpu_down_maps_locked(cpu, CPUHP_OFFLINE); + if (ret) + break; +@@ -2328,6 +2356,8 @@ int cpuhp_smt_enable(void) + /* Skip online CPUs and CPUs on offline nodes */ + if (cpu_online(cpu) || !node_online(cpu_to_node(cpu))) + continue; ++ if (!cpu_smt_thread_allowed(cpu)) ++ continue; + ret = _cpu_up(cpu, 0, CPUHP_ONLINE); + if (ret) + break; +diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c +index d9a5c1d65a79d..44a4eba80315c 100644 +--- a/kernel/irq/affinity.c ++++ b/kernel/irq/affinity.c +@@ -7,398 +7,7 @@ + #include + #include + #include +-#include +- +-static void irq_spread_init_one(struct cpumask *irqmsk, struct cpumask *nmsk, +- unsigned int cpus_per_vec) +-{ +- const struct cpumask *siblmsk; +- int cpu, sibl; +- +- for ( ; cpus_per_vec > 0; ) { +- cpu = cpumask_first(nmsk); +- +- /* Should not happen, but I'm too lazy to think about it */ +- if (cpu >= nr_cpu_ids) +- return; +- +- cpumask_clear_cpu(cpu, nmsk); +- cpumask_set_cpu(cpu, irqmsk); +- cpus_per_vec--; +- +- /* If the cpu has siblings, use them first */ +- siblmsk = topology_sibling_cpumask(cpu); +- for (sibl = -1; cpus_per_vec > 0; ) { +- sibl = cpumask_next(sibl, siblmsk); +- if (sibl >= nr_cpu_ids) +- break; +- if (!cpumask_test_and_clear_cpu(sibl, nmsk)) +- continue; +- cpumask_set_cpu(sibl, irqmsk); +- cpus_per_vec--; +- } +- } +-} +- +-static cpumask_var_t *alloc_node_to_cpumask(void) +-{ +- cpumask_var_t *masks; +- int node; +- +- masks = kcalloc(nr_node_ids, sizeof(cpumask_var_t), GFP_KERNEL); +- if (!masks) +- return NULL; +- +- for (node = 0; node < nr_node_ids; node++) { +- if (!zalloc_cpumask_var(&masks[node], GFP_KERNEL)) +- goto out_unwind; +- } +- +- return masks; +- +-out_unwind: +- while (--node >= 0) +- free_cpumask_var(masks[node]); +- kfree(masks); +- return NULL; +-} +- +-static void free_node_to_cpumask(cpumask_var_t *masks) +-{ +- int node; +- +- for (node = 0; node < nr_node_ids; node++) +- free_cpumask_var(masks[node]); +- kfree(masks); +-} +- +-static void build_node_to_cpumask(cpumask_var_t *masks) +-{ +- int cpu; +- +- for_each_possible_cpu(cpu) +- cpumask_set_cpu(cpu, masks[cpu_to_node(cpu)]); +-} +- +-static int get_nodes_in_cpumask(cpumask_var_t *node_to_cpumask, +- const struct cpumask *mask, nodemask_t *nodemsk) +-{ +- int n, nodes = 0; +- +- /* Calculate the number of nodes in the supplied affinity mask */ +- for_each_node(n) { +- if (cpumask_intersects(mask, node_to_cpumask[n])) { +- node_set(n, *nodemsk); +- nodes++; +- } +- } +- return nodes; +-} +- +-struct node_vectors { +- unsigned id; +- +- union { +- unsigned nvectors; +- unsigned ncpus; +- }; +-}; +- +-static int ncpus_cmp_func(const void *l, const void *r) +-{ +- const struct node_vectors *ln = l; +- const struct node_vectors *rn = r; +- +- return ln->ncpus - rn->ncpus; +-} +- +-/* +- * Allocate vector number for each node, so that for each node: +- * +- * 1) the allocated number is >= 1 +- * +- * 2) the allocated numbver is <= active CPU number of this node +- * +- * The actual allocated total vectors may be less than @numvecs when +- * active total CPU number is less than @numvecs. +- * +- * Active CPUs means the CPUs in '@cpu_mask AND @node_to_cpumask[]' +- * for each node. +- */ +-static void alloc_nodes_vectors(unsigned int numvecs, +- cpumask_var_t *node_to_cpumask, +- const struct cpumask *cpu_mask, +- const nodemask_t nodemsk, +- struct cpumask *nmsk, +- struct node_vectors *node_vectors) +-{ +- unsigned n, remaining_ncpus = 0; +- +- for (n = 0; n < nr_node_ids; n++) { +- node_vectors[n].id = n; +- node_vectors[n].ncpus = UINT_MAX; +- } +- +- for_each_node_mask(n, nodemsk) { +- unsigned ncpus; +- +- cpumask_and(nmsk, cpu_mask, node_to_cpumask[n]); +- ncpus = cpumask_weight(nmsk); +- +- if (!ncpus) +- continue; +- remaining_ncpus += ncpus; +- node_vectors[n].ncpus = ncpus; +- } +- +- numvecs = min_t(unsigned, remaining_ncpus, numvecs); +- +- sort(node_vectors, nr_node_ids, sizeof(node_vectors[0]), +- ncpus_cmp_func, NULL); +- +- /* +- * Allocate vectors for each node according to the ratio of this +- * node's nr_cpus to remaining un-assigned ncpus. 'numvecs' is +- * bigger than number of active numa nodes. Always start the +- * allocation from the node with minimized nr_cpus. +- * +- * This way guarantees that each active node gets allocated at +- * least one vector, and the theory is simple: over-allocation +- * is only done when this node is assigned by one vector, so +- * other nodes will be allocated >= 1 vector, since 'numvecs' is +- * bigger than number of numa nodes. +- * +- * One perfect invariant is that number of allocated vectors for +- * each node is <= CPU count of this node: +- * +- * 1) suppose there are two nodes: A and B +- * ncpu(X) is CPU count of node X +- * vecs(X) is the vector count allocated to node X via this +- * algorithm +- * +- * ncpu(A) <= ncpu(B) +- * ncpu(A) + ncpu(B) = N +- * vecs(A) + vecs(B) = V +- * +- * vecs(A) = max(1, round_down(V * ncpu(A) / N)) +- * vecs(B) = V - vecs(A) +- * +- * both N and V are integer, and 2 <= V <= N, suppose +- * V = N - delta, and 0 <= delta <= N - 2 +- * +- * 2) obviously vecs(A) <= ncpu(A) because: +- * +- * if vecs(A) is 1, then vecs(A) <= ncpu(A) given +- * ncpu(A) >= 1 +- * +- * otherwise, +- * vecs(A) <= V * ncpu(A) / N <= ncpu(A), given V <= N +- * +- * 3) prove how vecs(B) <= ncpu(B): +- * +- * if round_down(V * ncpu(A) / N) == 0, vecs(B) won't be +- * over-allocated, so vecs(B) <= ncpu(B), +- * +- * otherwise: +- * +- * vecs(A) = +- * round_down(V * ncpu(A) / N) = +- * round_down((N - delta) * ncpu(A) / N) = +- * round_down((N * ncpu(A) - delta * ncpu(A)) / N) >= +- * round_down((N * ncpu(A) - delta * N) / N) = +- * cpu(A) - delta +- * +- * then: +- * +- * vecs(A) - V >= ncpu(A) - delta - V +- * => +- * V - vecs(A) <= V + delta - ncpu(A) +- * => +- * vecs(B) <= N - ncpu(A) +- * => +- * vecs(B) <= cpu(B) +- * +- * For nodes >= 3, it can be thought as one node and another big +- * node given that is exactly what this algorithm is implemented, +- * and we always re-calculate 'remaining_ncpus' & 'numvecs', and +- * finally for each node X: vecs(X) <= ncpu(X). +- * +- */ +- for (n = 0; n < nr_node_ids; n++) { +- unsigned nvectors, ncpus; +- +- if (node_vectors[n].ncpus == UINT_MAX) +- continue; +- +- WARN_ON_ONCE(numvecs == 0); +- +- ncpus = node_vectors[n].ncpus; +- nvectors = max_t(unsigned, 1, +- numvecs * ncpus / remaining_ncpus); +- WARN_ON_ONCE(nvectors > ncpus); +- +- node_vectors[n].nvectors = nvectors; +- +- remaining_ncpus -= ncpus; +- numvecs -= nvectors; +- } +-} +- +-static int __irq_build_affinity_masks(unsigned int startvec, +- unsigned int numvecs, +- unsigned int firstvec, +- cpumask_var_t *node_to_cpumask, +- const struct cpumask *cpu_mask, +- struct cpumask *nmsk, +- struct irq_affinity_desc *masks) +-{ +- unsigned int i, n, nodes, cpus_per_vec, extra_vecs, done = 0; +- unsigned int last_affv = firstvec + numvecs; +- unsigned int curvec = startvec; +- nodemask_t nodemsk = NODE_MASK_NONE; +- struct node_vectors *node_vectors; +- +- if (cpumask_empty(cpu_mask)) +- return 0; +- +- nodes = get_nodes_in_cpumask(node_to_cpumask, cpu_mask, &nodemsk); +- +- /* +- * If the number of nodes in the mask is greater than or equal the +- * number of vectors we just spread the vectors across the nodes. +- */ +- if (numvecs <= nodes) { +- for_each_node_mask(n, nodemsk) { +- /* Ensure that only CPUs which are in both masks are set */ +- cpumask_and(nmsk, cpu_mask, node_to_cpumask[n]); +- cpumask_or(&masks[curvec].mask, &masks[curvec].mask, nmsk); +- if (++curvec == last_affv) +- curvec = firstvec; +- } +- return numvecs; +- } +- +- node_vectors = kcalloc(nr_node_ids, +- sizeof(struct node_vectors), +- GFP_KERNEL); +- if (!node_vectors) +- return -ENOMEM; +- +- /* allocate vector number for each node */ +- alloc_nodes_vectors(numvecs, node_to_cpumask, cpu_mask, +- nodemsk, nmsk, node_vectors); +- +- for (i = 0; i < nr_node_ids; i++) { +- unsigned int ncpus, v; +- struct node_vectors *nv = &node_vectors[i]; +- +- if (nv->nvectors == UINT_MAX) +- continue; +- +- /* Get the cpus on this node which are in the mask */ +- cpumask_and(nmsk, cpu_mask, node_to_cpumask[nv->id]); +- ncpus = cpumask_weight(nmsk); +- if (!ncpus) +- continue; +- +- WARN_ON_ONCE(nv->nvectors > ncpus); +- +- /* Account for rounding errors */ +- extra_vecs = ncpus - nv->nvectors * (ncpus / nv->nvectors); +- +- /* Spread allocated vectors on CPUs of the current node */ +- for (v = 0; v < nv->nvectors; v++, curvec++) { +- cpus_per_vec = ncpus / nv->nvectors; +- +- /* Account for extra vectors to compensate rounding errors */ +- if (extra_vecs) { +- cpus_per_vec++; +- --extra_vecs; +- } +- +- /* +- * wrapping has to be considered given 'startvec' +- * may start anywhere +- */ +- if (curvec >= last_affv) +- curvec = firstvec; +- irq_spread_init_one(&masks[curvec].mask, nmsk, +- cpus_per_vec); +- } +- done += nv->nvectors; +- } +- kfree(node_vectors); +- return done; +-} +- +-/* +- * build affinity in two stages: +- * 1) spread present CPU on these vectors +- * 2) spread other possible CPUs on these vectors +- */ +-static int irq_build_affinity_masks(unsigned int startvec, unsigned int numvecs, +- unsigned int firstvec, +- struct irq_affinity_desc *masks) +-{ +- unsigned int curvec = startvec, nr_present = 0, nr_others = 0; +- cpumask_var_t *node_to_cpumask; +- cpumask_var_t nmsk, npresmsk; +- int ret = -ENOMEM; +- +- if (!zalloc_cpumask_var(&nmsk, GFP_KERNEL)) +- return ret; +- +- if (!zalloc_cpumask_var(&npresmsk, GFP_KERNEL)) +- goto fail_nmsk; +- +- node_to_cpumask = alloc_node_to_cpumask(); +- if (!node_to_cpumask) +- goto fail_npresmsk; +- +- /* Stabilize the cpumasks */ +- cpus_read_lock(); +- build_node_to_cpumask(node_to_cpumask); +- +- /* Spread on present CPUs starting from affd->pre_vectors */ +- ret = __irq_build_affinity_masks(curvec, numvecs, firstvec, +- node_to_cpumask, cpu_present_mask, +- nmsk, masks); +- if (ret < 0) +- goto fail_build_affinity; +- nr_present = ret; +- +- /* +- * Spread on non present CPUs starting from the next vector to be +- * handled. If the spreading of present CPUs already exhausted the +- * vector space, assign the non present CPUs to the already spread +- * out vectors. +- */ +- if (nr_present >= numvecs) +- curvec = firstvec; +- else +- curvec = firstvec + nr_present; +- cpumask_andnot(npresmsk, cpu_possible_mask, cpu_present_mask); +- ret = __irq_build_affinity_masks(curvec, numvecs, firstvec, +- node_to_cpumask, npresmsk, nmsk, +- masks); +- if (ret >= 0) +- nr_others = ret; +- +- fail_build_affinity: +- cpus_read_unlock(); +- +- if (ret >= 0) +- WARN_ON(nr_present + nr_others < numvecs); +- +- free_node_to_cpumask(node_to_cpumask); +- +- fail_npresmsk: +- free_cpumask_var(npresmsk); +- +- fail_nmsk: +- free_cpumask_var(nmsk); +- return ret < 0 ? ret : 0; +-} ++#include + + static void default_calc_sets(struct irq_affinity *affd, unsigned int affvecs) + { +@@ -461,14 +70,18 @@ irq_create_affinity_masks(unsigned int nvecs, struct irq_affinity *affd) + */ + for (i = 0, usedvecs = 0; i < affd->nr_sets; i++) { + unsigned int this_vecs = affd->set_size[i]; +- int ret; ++ int j; ++ struct cpumask *result = group_cpus_evenly(this_vecs); + +- ret = irq_build_affinity_masks(curvec, this_vecs, +- curvec, masks); +- if (ret) { ++ if (!result) { + kfree(masks); + return NULL; + } ++ ++ for (j = 0; j < this_vecs; j++) ++ cpumask_copy(&masks[curvec + j].mask, &result[j]); ++ kfree(result); ++ + curvec += this_vecs; + usedvecs += this_vecs; + } +diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c +index 8fdf076720384..929dcbc04d29c 100644 +--- a/kernel/rcu/srcutree.c ++++ b/kernel/rcu/srcutree.c +@@ -1100,10 +1100,37 @@ static unsigned long srcu_gp_start_if_needed(struct srcu_struct *ssp, + spin_lock_irqsave_sdp_contention(sdp, &flags); + if (rhp) + rcu_segcblist_enqueue(&sdp->srcu_cblist, rhp); ++ /* ++ * The snapshot for acceleration must be taken _before_ the read of the ++ * current gp sequence used for advancing, otherwise advancing may fail ++ * and acceleration may then fail too. ++ * ++ * This could happen if: ++ * ++ * 1) The RCU_WAIT_TAIL segment has callbacks (gp_num = X + 4) and the ++ * RCU_NEXT_READY_TAIL also has callbacks (gp_num = X + 8). ++ * ++ * 2) The grace period for RCU_WAIT_TAIL is seen as started but not ++ * completed so rcu_seq_current() returns X + SRCU_STATE_SCAN1. ++ * ++ * 3) This value is passed to rcu_segcblist_advance() which can't move ++ * any segment forward and fails. ++ * ++ * 4) srcu_gp_start_if_needed() still proceeds with callback acceleration. ++ * But then the call to rcu_seq_snap() observes the grace period for the ++ * RCU_WAIT_TAIL segment as completed and the subsequent one for the ++ * RCU_NEXT_READY_TAIL segment as started (ie: X + 4 + SRCU_STATE_SCAN1) ++ * so it returns a snapshot of the next grace period, which is X + 12. ++ * ++ * 5) The value of X + 12 is passed to rcu_segcblist_accelerate() but the ++ * freshly enqueued callback in RCU_NEXT_TAIL can't move to ++ * RCU_NEXT_READY_TAIL which already has callbacks for a previous grace ++ * period (gp_num = X + 8). So acceleration fails. ++ */ ++ s = rcu_seq_snap(&ssp->srcu_gp_seq); + rcu_segcblist_advance(&sdp->srcu_cblist, + rcu_seq_current(&ssp->srcu_gp_seq)); +- s = rcu_seq_snap(&ssp->srcu_gp_seq); +- (void)rcu_segcblist_accelerate(&sdp->srcu_cblist, s); ++ WARN_ON_ONCE(!rcu_segcblist_accelerate(&sdp->srcu_cblist, s) && rhp); + if (ULONG_CMP_LT(sdp->srcu_gp_seq_needed, s)) { + sdp->srcu_gp_seq_needed = s; + needgp = true; +diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c +index 06d52525407b8..71cad4f1323c6 100644 +--- a/kernel/trace/ring_buffer.c ++++ b/kernel/trace/ring_buffer.c +@@ -646,8 +646,8 @@ static inline bool __rb_time_read(rb_time_t *t, u64 *ret, unsigned long *cnt) + + *cnt = rb_time_cnt(top); + +- /* If top and msb counts don't match, this interrupted a write */ +- if (*cnt != rb_time_cnt(msb)) ++ /* If top, msb or bottom counts don't match, this interrupted a write */ ++ if (*cnt != rb_time_cnt(msb) || *cnt != rb_time_cnt(bottom)) + return false; + + /* The shift to msb will lose its cnt bits */ +diff --git a/lib/Makefile b/lib/Makefile +index 5ffe72ec99797..6f1611d053e6a 100644 +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -361,6 +361,8 @@ obj-$(CONFIG_SBITMAP) += sbitmap.o + + obj-$(CONFIG_PARMAN) += parman.o + ++obj-y += group_cpus.o ++ + # GCC library routines + obj-$(CONFIG_GENERIC_LIB_ASHLDI3) += ashldi3.o + obj-$(CONFIG_GENERIC_LIB_ASHRDI3) += ashrdi3.o +diff --git a/lib/group_cpus.c b/lib/group_cpus.c +new file mode 100644 +index 0000000000000..0292611901b8b +--- /dev/null ++++ b/lib/group_cpus.c +@@ -0,0 +1,438 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2016 Thomas Gleixner. ++ * Copyright (C) 2016-2017 Christoph Hellwig. ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_SMP ++ ++static void grp_spread_init_one(struct cpumask *irqmsk, struct cpumask *nmsk, ++ unsigned int cpus_per_grp) ++{ ++ const struct cpumask *siblmsk; ++ int cpu, sibl; ++ ++ for ( ; cpus_per_grp > 0; ) { ++ cpu = cpumask_first(nmsk); ++ ++ /* Should not happen, but I'm too lazy to think about it */ ++ if (cpu >= nr_cpu_ids) ++ return; ++ ++ cpumask_clear_cpu(cpu, nmsk); ++ cpumask_set_cpu(cpu, irqmsk); ++ cpus_per_grp--; ++ ++ /* If the cpu has siblings, use them first */ ++ siblmsk = topology_sibling_cpumask(cpu); ++ for (sibl = -1; cpus_per_grp > 0; ) { ++ sibl = cpumask_next(sibl, siblmsk); ++ if (sibl >= nr_cpu_ids) ++ break; ++ if (!cpumask_test_and_clear_cpu(sibl, nmsk)) ++ continue; ++ cpumask_set_cpu(sibl, irqmsk); ++ cpus_per_grp--; ++ } ++ } ++} ++ ++static cpumask_var_t *alloc_node_to_cpumask(void) ++{ ++ cpumask_var_t *masks; ++ int node; ++ ++ masks = kcalloc(nr_node_ids, sizeof(cpumask_var_t), GFP_KERNEL); ++ if (!masks) ++ return NULL; ++ ++ for (node = 0; node < nr_node_ids; node++) { ++ if (!zalloc_cpumask_var(&masks[node], GFP_KERNEL)) ++ goto out_unwind; ++ } ++ ++ return masks; ++ ++out_unwind: ++ while (--node >= 0) ++ free_cpumask_var(masks[node]); ++ kfree(masks); ++ return NULL; ++} ++ ++static void free_node_to_cpumask(cpumask_var_t *masks) ++{ ++ int node; ++ ++ for (node = 0; node < nr_node_ids; node++) ++ free_cpumask_var(masks[node]); ++ kfree(masks); ++} ++ ++static void build_node_to_cpumask(cpumask_var_t *masks) ++{ ++ int cpu; ++ ++ for_each_possible_cpu(cpu) ++ cpumask_set_cpu(cpu, masks[cpu_to_node(cpu)]); ++} ++ ++static int get_nodes_in_cpumask(cpumask_var_t *node_to_cpumask, ++ const struct cpumask *mask, nodemask_t *nodemsk) ++{ ++ int n, nodes = 0; ++ ++ /* Calculate the number of nodes in the supplied affinity mask */ ++ for_each_node(n) { ++ if (cpumask_intersects(mask, node_to_cpumask[n])) { ++ node_set(n, *nodemsk); ++ nodes++; ++ } ++ } ++ return nodes; ++} ++ ++struct node_groups { ++ unsigned id; ++ ++ union { ++ unsigned ngroups; ++ unsigned ncpus; ++ }; ++}; ++ ++static int ncpus_cmp_func(const void *l, const void *r) ++{ ++ const struct node_groups *ln = l; ++ const struct node_groups *rn = r; ++ ++ return ln->ncpus - rn->ncpus; ++} ++ ++/* ++ * Allocate group number for each node, so that for each node: ++ * ++ * 1) the allocated number is >= 1 ++ * ++ * 2) the allocated number is <= active CPU number of this node ++ * ++ * The actual allocated total groups may be less than @numgrps when ++ * active total CPU number is less than @numgrps. ++ * ++ * Active CPUs means the CPUs in '@cpu_mask AND @node_to_cpumask[]' ++ * for each node. ++ */ ++static void alloc_nodes_groups(unsigned int numgrps, ++ cpumask_var_t *node_to_cpumask, ++ const struct cpumask *cpu_mask, ++ const nodemask_t nodemsk, ++ struct cpumask *nmsk, ++ struct node_groups *node_groups) ++{ ++ unsigned n, remaining_ncpus = 0; ++ ++ for (n = 0; n < nr_node_ids; n++) { ++ node_groups[n].id = n; ++ node_groups[n].ncpus = UINT_MAX; ++ } ++ ++ for_each_node_mask(n, nodemsk) { ++ unsigned ncpus; ++ ++ cpumask_and(nmsk, cpu_mask, node_to_cpumask[n]); ++ ncpus = cpumask_weight(nmsk); ++ ++ if (!ncpus) ++ continue; ++ remaining_ncpus += ncpus; ++ node_groups[n].ncpus = ncpus; ++ } ++ ++ numgrps = min_t(unsigned, remaining_ncpus, numgrps); ++ ++ sort(node_groups, nr_node_ids, sizeof(node_groups[0]), ++ ncpus_cmp_func, NULL); ++ ++ /* ++ * Allocate groups for each node according to the ratio of this ++ * node's nr_cpus to remaining un-assigned ncpus. 'numgrps' is ++ * bigger than number of active numa nodes. Always start the ++ * allocation from the node with minimized nr_cpus. ++ * ++ * This way guarantees that each active node gets allocated at ++ * least one group, and the theory is simple: over-allocation ++ * is only done when this node is assigned by one group, so ++ * other nodes will be allocated >= 1 groups, since 'numgrps' is ++ * bigger than number of numa nodes. ++ * ++ * One perfect invariant is that number of allocated groups for ++ * each node is <= CPU count of this node: ++ * ++ * 1) suppose there are two nodes: A and B ++ * ncpu(X) is CPU count of node X ++ * grps(X) is the group count allocated to node X via this ++ * algorithm ++ * ++ * ncpu(A) <= ncpu(B) ++ * ncpu(A) + ncpu(B) = N ++ * grps(A) + grps(B) = G ++ * ++ * grps(A) = max(1, round_down(G * ncpu(A) / N)) ++ * grps(B) = G - grps(A) ++ * ++ * both N and G are integer, and 2 <= G <= N, suppose ++ * G = N - delta, and 0 <= delta <= N - 2 ++ * ++ * 2) obviously grps(A) <= ncpu(A) because: ++ * ++ * if grps(A) is 1, then grps(A) <= ncpu(A) given ++ * ncpu(A) >= 1 ++ * ++ * otherwise, ++ * grps(A) <= G * ncpu(A) / N <= ncpu(A), given G <= N ++ * ++ * 3) prove how grps(B) <= ncpu(B): ++ * ++ * if round_down(G * ncpu(A) / N) == 0, vecs(B) won't be ++ * over-allocated, so grps(B) <= ncpu(B), ++ * ++ * otherwise: ++ * ++ * grps(A) = ++ * round_down(G * ncpu(A) / N) = ++ * round_down((N - delta) * ncpu(A) / N) = ++ * round_down((N * ncpu(A) - delta * ncpu(A)) / N) >= ++ * round_down((N * ncpu(A) - delta * N) / N) = ++ * cpu(A) - delta ++ * ++ * then: ++ * ++ * grps(A) - G >= ncpu(A) - delta - G ++ * => ++ * G - grps(A) <= G + delta - ncpu(A) ++ * => ++ * grps(B) <= N - ncpu(A) ++ * => ++ * grps(B) <= cpu(B) ++ * ++ * For nodes >= 3, it can be thought as one node and another big ++ * node given that is exactly what this algorithm is implemented, ++ * and we always re-calculate 'remaining_ncpus' & 'numgrps', and ++ * finally for each node X: grps(X) <= ncpu(X). ++ * ++ */ ++ for (n = 0; n < nr_node_ids; n++) { ++ unsigned ngroups, ncpus; ++ ++ if (node_groups[n].ncpus == UINT_MAX) ++ continue; ++ ++ WARN_ON_ONCE(numgrps == 0); ++ ++ ncpus = node_groups[n].ncpus; ++ ngroups = max_t(unsigned, 1, ++ numgrps * ncpus / remaining_ncpus); ++ WARN_ON_ONCE(ngroups > ncpus); ++ ++ node_groups[n].ngroups = ngroups; ++ ++ remaining_ncpus -= ncpus; ++ numgrps -= ngroups; ++ } ++} ++ ++static int __group_cpus_evenly(unsigned int startgrp, unsigned int numgrps, ++ cpumask_var_t *node_to_cpumask, ++ const struct cpumask *cpu_mask, ++ struct cpumask *nmsk, struct cpumask *masks) ++{ ++ unsigned int i, n, nodes, cpus_per_grp, extra_grps, done = 0; ++ unsigned int last_grp = numgrps; ++ unsigned int curgrp = startgrp; ++ nodemask_t nodemsk = NODE_MASK_NONE; ++ struct node_groups *node_groups; ++ ++ if (cpumask_empty(cpu_mask)) ++ return 0; ++ ++ nodes = get_nodes_in_cpumask(node_to_cpumask, cpu_mask, &nodemsk); ++ ++ /* ++ * If the number of nodes in the mask is greater than or equal the ++ * number of groups we just spread the groups across the nodes. ++ */ ++ if (numgrps <= nodes) { ++ for_each_node_mask(n, nodemsk) { ++ /* Ensure that only CPUs which are in both masks are set */ ++ cpumask_and(nmsk, cpu_mask, node_to_cpumask[n]); ++ cpumask_or(&masks[curgrp], &masks[curgrp], nmsk); ++ if (++curgrp == last_grp) ++ curgrp = 0; ++ } ++ return numgrps; ++ } ++ ++ node_groups = kcalloc(nr_node_ids, ++ sizeof(struct node_groups), ++ GFP_KERNEL); ++ if (!node_groups) ++ return -ENOMEM; ++ ++ /* allocate group number for each node */ ++ alloc_nodes_groups(numgrps, node_to_cpumask, cpu_mask, ++ nodemsk, nmsk, node_groups); ++ for (i = 0; i < nr_node_ids; i++) { ++ unsigned int ncpus, v; ++ struct node_groups *nv = &node_groups[i]; ++ ++ if (nv->ngroups == UINT_MAX) ++ continue; ++ ++ /* Get the cpus on this node which are in the mask */ ++ cpumask_and(nmsk, cpu_mask, node_to_cpumask[nv->id]); ++ ncpus = cpumask_weight(nmsk); ++ if (!ncpus) ++ continue; ++ ++ WARN_ON_ONCE(nv->ngroups > ncpus); ++ ++ /* Account for rounding errors */ ++ extra_grps = ncpus - nv->ngroups * (ncpus / nv->ngroups); ++ ++ /* Spread allocated groups on CPUs of the current node */ ++ for (v = 0; v < nv->ngroups; v++, curgrp++) { ++ cpus_per_grp = ncpus / nv->ngroups; ++ ++ /* Account for extra groups to compensate rounding errors */ ++ if (extra_grps) { ++ cpus_per_grp++; ++ --extra_grps; ++ } ++ ++ /* ++ * wrapping has to be considered given 'startgrp' ++ * may start anywhere ++ */ ++ if (curgrp >= last_grp) ++ curgrp = 0; ++ grp_spread_init_one(&masks[curgrp], nmsk, ++ cpus_per_grp); ++ } ++ done += nv->ngroups; ++ } ++ kfree(node_groups); ++ return done; ++} ++ ++/** ++ * group_cpus_evenly - Group all CPUs evenly per NUMA/CPU locality ++ * @numgrps: number of groups ++ * ++ * Return: cpumask array if successful, NULL otherwise. And each element ++ * includes CPUs assigned to this group ++ * ++ * Try to put close CPUs from viewpoint of CPU and NUMA locality into ++ * same group, and run two-stage grouping: ++ * 1) allocate present CPUs on these groups evenly first ++ * 2) allocate other possible CPUs on these groups evenly ++ * ++ * We guarantee in the resulted grouping that all CPUs are covered, and ++ * no same CPU is assigned to multiple groups ++ */ ++struct cpumask *group_cpus_evenly(unsigned int numgrps) ++{ ++ unsigned int curgrp = 0, nr_present = 0, nr_others = 0; ++ cpumask_var_t *node_to_cpumask; ++ cpumask_var_t nmsk, npresmsk; ++ int ret = -ENOMEM; ++ struct cpumask *masks = NULL; ++ ++ if (!zalloc_cpumask_var(&nmsk, GFP_KERNEL)) ++ return NULL; ++ ++ if (!zalloc_cpumask_var(&npresmsk, GFP_KERNEL)) ++ goto fail_nmsk; ++ ++ node_to_cpumask = alloc_node_to_cpumask(); ++ if (!node_to_cpumask) ++ goto fail_npresmsk; ++ ++ masks = kcalloc(numgrps, sizeof(*masks), GFP_KERNEL); ++ if (!masks) ++ goto fail_node_to_cpumask; ++ ++ build_node_to_cpumask(node_to_cpumask); ++ ++ /* ++ * Make a local cache of 'cpu_present_mask', so the two stages ++ * spread can observe consistent 'cpu_present_mask' without holding ++ * cpu hotplug lock, then we can reduce deadlock risk with cpu ++ * hotplug code. ++ * ++ * Here CPU hotplug may happen when reading `cpu_present_mask`, and ++ * we can live with the case because it only affects that hotplug ++ * CPU is handled in the 1st or 2nd stage, and either way is correct ++ * from API user viewpoint since 2-stage spread is sort of ++ * optimization. ++ */ ++ cpumask_copy(npresmsk, data_race(cpu_present_mask)); ++ ++ /* grouping present CPUs first */ ++ ret = __group_cpus_evenly(curgrp, numgrps, node_to_cpumask, ++ npresmsk, nmsk, masks); ++ if (ret < 0) ++ goto fail_build_affinity; ++ nr_present = ret; ++ ++ /* ++ * Allocate non present CPUs starting from the next group to be ++ * handled. If the grouping of present CPUs already exhausted the ++ * group space, assign the non present CPUs to the already ++ * allocated out groups. ++ */ ++ if (nr_present >= numgrps) ++ curgrp = 0; ++ else ++ curgrp = nr_present; ++ cpumask_andnot(npresmsk, cpu_possible_mask, npresmsk); ++ ret = __group_cpus_evenly(curgrp, numgrps, node_to_cpumask, ++ npresmsk, nmsk, masks); ++ if (ret >= 0) ++ nr_others = ret; ++ ++ fail_build_affinity: ++ if (ret >= 0) ++ WARN_ON(nr_present + nr_others < numgrps); ++ ++ fail_node_to_cpumask: ++ free_node_to_cpumask(node_to_cpumask); ++ ++ fail_npresmsk: ++ free_cpumask_var(npresmsk); ++ ++ fail_nmsk: ++ free_cpumask_var(nmsk); ++ if (ret < 0) { ++ kfree(masks); ++ return NULL; ++ } ++ return masks; ++} ++#else /* CONFIG_SMP */ ++struct cpumask *group_cpus_evenly(unsigned int numgrps) ++{ ++ struct cpumask *masks = kcalloc(numgrps, sizeof(*masks), GFP_KERNEL); ++ ++ if (!masks) ++ return NULL; ++ ++ /* assign all CPUs(cpu 0) to the 1st group only */ ++ cpumask_copy(&masks[0], cpu_possible_mask); ++ return masks; ++} ++#endif /* CONFIG_SMP */ +diff --git a/mm/filemap.c b/mm/filemap.c +index 10fe6430693bd..2809b1174f04e 100644 +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -4005,6 +4005,8 @@ bool filemap_release_folio(struct folio *folio, gfp_t gfp) + struct address_space * const mapping = folio->mapping; + + BUG_ON(!folio_test_locked(folio)); ++ if (!folio_needs_release(folio)) ++ return true; + if (folio_test_writeback(folio)) + return false; + +diff --git a/mm/huge_memory.c b/mm/huge_memory.c +index 2753fb54cdf38..59577946735b1 100644 +--- a/mm/huge_memory.c ++++ b/mm/huge_memory.c +@@ -2694,8 +2694,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list) + gfp = current_gfp_context(mapping_gfp_mask(mapping) & + GFP_RECLAIM_MASK); + +- if (folio_test_private(folio) && +- !filemap_release_folio(folio, gfp)) { ++ if (!filemap_release_folio(folio, gfp)) { + ret = -EBUSY; + goto out; + } +diff --git a/mm/internal.h b/mm/internal.h +index 6b7ef495b56d3..d01130efce5fb 100644 +--- a/mm/internal.h ++++ b/mm/internal.h +@@ -163,6 +163,17 @@ static inline void set_page_refcounted(struct page *page) + set_page_count(page, 1); + } + ++/* ++ * Return true if a folio needs ->release_folio() calling upon it. ++ */ ++static inline bool folio_needs_release(struct folio *folio) ++{ ++ struct address_space *mapping = folio_mapping(folio); ++ ++ return folio_has_private(folio) || ++ (mapping && mapping_release_always(mapping)); ++} ++ + extern unsigned long highest_memmap_pfn; + + /* +diff --git a/mm/khugepaged.c b/mm/khugepaged.c +index ef72d3df4b65b..65bd0b105266a 100644 +--- a/mm/khugepaged.c ++++ b/mm/khugepaged.c +@@ -1818,6 +1818,7 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr, + xas_set(&xas, start); + for (index = start; index < end; index++) { + struct page *page = xas_next(&xas); ++ struct folio *folio; + + VM_BUG_ON(index != xas.xa_index); + if (is_shmem) { +@@ -1844,8 +1845,6 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr, + } + + if (xa_is_value(page) || !PageUptodate(page)) { +- struct folio *folio; +- + xas_unlock_irq(&xas); + /* swap in or instantiate fallocated page */ + if (shmem_get_folio(mapping->host, index, +@@ -1933,13 +1932,15 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr, + goto out_unlock; + } + +- if (page_mapping(page) != mapping) { ++ folio = page_folio(page); ++ ++ if (folio_mapping(folio) != mapping) { + result = SCAN_TRUNCATED; + goto out_unlock; + } + +- if (!is_shmem && (PageDirty(page) || +- PageWriteback(page))) { ++ if (!is_shmem && (folio_test_dirty(folio) || ++ folio_test_writeback(folio))) { + /* + * khugepaged only works on read-only fd, so this + * page is dirty because it hasn't been flushed +@@ -1949,20 +1950,19 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr, + goto out_unlock; + } + +- if (isolate_lru_page(page)) { ++ if (folio_isolate_lru(folio)) { + result = SCAN_DEL_PAGE_LRU; + goto out_unlock; + } + +- if (page_has_private(page) && +- !try_to_release_page(page, GFP_KERNEL)) { ++ if (!filemap_release_folio(folio, GFP_KERNEL)) { + result = SCAN_PAGE_HAS_PRIVATE; +- putback_lru_page(page); ++ folio_putback_lru(folio); + goto out_unlock; + } + +- if (page_mapped(page)) +- try_to_unmap(page_folio(page), ++ if (folio_mapped(folio)) ++ try_to_unmap(folio, + TTU_IGNORE_MLOCK | TTU_BATCH_FLUSH); + + xas_lock_irq(&xas); +diff --git a/mm/memory-failure.c b/mm/memory-failure.c +index ebd717157c813..5b846ed5dcbe9 100644 +--- a/mm/memory-failure.c ++++ b/mm/memory-failure.c +@@ -827,16 +827,15 @@ static int truncate_error_page(struct page *p, unsigned long pfn, + int ret = MF_FAILED; + + if (mapping->a_ops->error_remove_page) { ++ struct folio *folio = page_folio(p); + int err = mapping->a_ops->error_remove_page(mapping, p); + +- if (err != 0) { ++ if (err != 0) + pr_info("%#lx: Failed to punch page: %d\n", pfn, err); +- } else if (page_has_private(p) && +- !try_to_release_page(p, GFP_NOIO)) { ++ else if (!filemap_release_folio(folio, GFP_NOIO)) + pr_info("%#lx: failed to release buffers\n", pfn); +- } else { ++ else + ret = MF_RECOVERED; +- } + } else { + /* + * If the file system doesn't support it just invalidate +diff --git a/mm/memory.c b/mm/memory.c +index 0d1b3ee8fcd7a..fc8b264ec0cac 100644 +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -3617,8 +3617,8 @@ EXPORT_SYMBOL_GPL(unmap_mapping_pages); + void unmap_mapping_range(struct address_space *mapping, + loff_t const holebegin, loff_t const holelen, int even_cows) + { +- pgoff_t hba = holebegin >> PAGE_SHIFT; +- pgoff_t hlen = (holelen + PAGE_SIZE - 1) >> PAGE_SHIFT; ++ pgoff_t hba = (pgoff_t)(holebegin) >> PAGE_SHIFT; ++ pgoff_t hlen = ((pgoff_t)(holelen) + PAGE_SIZE - 1) >> PAGE_SHIFT; + + /* Check for overflow. */ + if (sizeof(holelen) > sizeof(hlen)) { +diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c +index bd2570b4f9b7b..3b9d3a4b43869 100644 +--- a/mm/memory_hotplug.c ++++ b/mm/memory_hotplug.c +@@ -1069,6 +1069,9 @@ void mhp_deinit_memmap_on_memory(unsigned long pfn, unsigned long nr_pages) + kasan_remove_zero_shadow(__va(PFN_PHYS(pfn)), PFN_PHYS(nr_pages)); + } + ++/* ++ * Must be called with mem_hotplug_lock in write mode. ++ */ + int __ref online_pages(unsigned long pfn, unsigned long nr_pages, + struct zone *zone, struct memory_group *group) + { +@@ -1089,7 +1092,6 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, + !IS_ALIGNED(pfn + nr_pages, PAGES_PER_SECTION))) + return -EINVAL; + +- mem_hotplug_begin(); + + /* associate pfn range with the zone */ + move_pfn_range_to_zone(zone, pfn, nr_pages, NULL, MIGRATE_ISOLATE); +@@ -1148,7 +1150,6 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, + writeback_set_ratelimit(); + + memory_notify(MEM_ONLINE, &arg); +- mem_hotplug_done(); + return 0; + + failed_addition: +@@ -1157,7 +1158,6 @@ failed_addition: + (((unsigned long long) pfn + nr_pages) << PAGE_SHIFT) - 1); + memory_notify(MEM_CANCEL_ONLINE, &arg); + remove_pfn_range_from_zone(zone, pfn, nr_pages); +- mem_hotplug_done(); + return ret; + } + +@@ -1382,7 +1382,7 @@ int __ref add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags) + ret = create_memory_block_devices(start, size, mhp_altmap.alloc, + group); + if (ret) { +- arch_remove_memory(start, size, NULL); ++ arch_remove_memory(start, size, params.altmap); + goto error; + } + +@@ -1787,6 +1787,9 @@ static int count_system_ram_pages_cb(unsigned long start_pfn, + return 0; + } + ++/* ++ * Must be called with mem_hotplug_lock in write mode. ++ */ + int __ref offline_pages(unsigned long start_pfn, unsigned long nr_pages, + struct zone *zone, struct memory_group *group) + { +@@ -1809,8 +1812,6 @@ int __ref offline_pages(unsigned long start_pfn, unsigned long nr_pages, + !IS_ALIGNED(start_pfn + nr_pages, PAGES_PER_SECTION))) + return -EINVAL; + +- mem_hotplug_begin(); +- + /* + * Don't allow to offline memory blocks that contain holes. + * Consequently, memory blocks with holes can never get onlined +@@ -1946,7 +1947,6 @@ int __ref offline_pages(unsigned long start_pfn, unsigned long nr_pages, + + memory_notify(MEM_OFFLINE, &arg); + remove_pfn_range_from_zone(zone, start_pfn, nr_pages); +- mem_hotplug_done(); + return 0; + + failed_removal_isolated: +@@ -1961,7 +1961,6 @@ failed_removal: + (unsigned long long) start_pfn << PAGE_SHIFT, + ((unsigned long long) end_pfn << PAGE_SHIFT) - 1, + reason); +- mem_hotplug_done(); + return ret; + } + +diff --git a/mm/migrate.c b/mm/migrate.c +index 91bd69c61148e..c93dd6a31c31a 100644 +--- a/mm/migrate.c ++++ b/mm/migrate.c +@@ -914,8 +914,7 @@ static int fallback_migrate_folio(struct address_space *mapping, + * Buffers may be managed in a filesystem specific way. + * We must have no buffers or drop them. + */ +- if (folio_test_private(src) && +- !filemap_release_folio(src, GFP_KERNEL)) ++ if (!filemap_release_folio(src, GFP_KERNEL)) + return mode == MIGRATE_SYNC ? -EAGAIN : -EBUSY; + + return migrate_folio(mapping, dst, src, mode); +diff --git a/mm/page-writeback.c b/mm/page-writeback.c +index 7e9d8d857ecca..de5f69921b946 100644 +--- a/mm/page-writeback.c ++++ b/mm/page-writeback.c +@@ -3078,7 +3078,7 @@ EXPORT_SYMBOL_GPL(folio_wait_writeback_killable); + */ + void folio_wait_stable(struct folio *folio) + { +- if (folio_inode(folio)->i_sb->s_iflags & SB_I_STABLE_WRITES) ++ if (mapping_stable_writes(folio_mapping(folio))) + folio_wait_writeback(folio); + } + EXPORT_SYMBOL_GPL(folio_wait_stable); +diff --git a/mm/truncate.c b/mm/truncate.c +index c0be77e5c0083..0d4dd233f5187 100644 +--- a/mm/truncate.c ++++ b/mm/truncate.c +@@ -19,7 +19,6 @@ + #include + #include + #include +-#include /* grr. try_to_release_page */ + #include + #include + #include "internal.h" +@@ -276,7 +275,7 @@ static long mapping_evict_folio(struct address_space *mapping, + if (folio_ref_count(folio) > + folio_nr_pages(folio) + folio_has_private(folio) + 1) + return 0; +- if (folio_has_private(folio) && !filemap_release_folio(folio, 0)) ++ if (!filemap_release_folio(folio, 0)) + return 0; + + return remove_mapping(mapping, folio); +@@ -581,8 +580,7 @@ static int invalidate_complete_folio2(struct address_space *mapping, + if (folio->mapping != mapping) + return 0; + +- if (folio_has_private(folio) && +- !filemap_release_folio(folio, GFP_KERNEL)) ++ if (!filemap_release_folio(folio, GFP_KERNEL)) + return 0; + + spin_lock(&mapping->host->i_lock); +diff --git a/mm/vmscan.c b/mm/vmscan.c +index 3f090faa6377f..9f3cfb7caa48d 100644 +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -1992,7 +1992,7 @@ retry: + * (refcount == 1) it can be freed. Otherwise, leave + * the folio on the LRU so it is swappable. + */ +- if (folio_has_private(folio)) { ++ if (folio_needs_release(folio)) { + if (!filemap_release_folio(folio, sc->gfp_mask)) + goto activate_locked; + if (!mapping && folio_ref_count(folio) == 1) { +@@ -2618,9 +2618,9 @@ static void shrink_active_list(unsigned long nr_to_scan, + } + + if (unlikely(buffer_heads_over_limit)) { +- if (folio_test_private(folio) && folio_trylock(folio)) { +- if (folio_test_private(folio)) +- filemap_release_folio(folio, 0); ++ if (folio_needs_release(folio) && ++ folio_trylock(folio)) { ++ filemap_release_folio(folio, 0); + folio_unlock(folio); + } + } +diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c +index 9c828067b4481..b0be23559243c 100644 +--- a/net/can/j1939/socket.c ++++ b/net/can/j1939/socket.c +@@ -974,6 +974,7 @@ static void __j1939_sk_errqueue(struct j1939_session *session, struct sock *sk, + struct sock_exterr_skb *serr; + struct sk_buff *skb; + char *state = "UNK"; ++ u32 tsflags; + int err; + + jsk = j1939_sk(sk); +@@ -981,13 +982,14 @@ static void __j1939_sk_errqueue(struct j1939_session *session, struct sock *sk, + if (!(jsk->state & J1939_SOCK_ERRQUEUE)) + return; + ++ tsflags = READ_ONCE(sk->sk_tsflags); + switch (type) { + case J1939_ERRQUEUE_TX_ACK: +- if (!(sk->sk_tsflags & SOF_TIMESTAMPING_TX_ACK)) ++ if (!(tsflags & SOF_TIMESTAMPING_TX_ACK)) + return; + break; + case J1939_ERRQUEUE_TX_SCHED: +- if (!(sk->sk_tsflags & SOF_TIMESTAMPING_TX_SCHED)) ++ if (!(tsflags & SOF_TIMESTAMPING_TX_SCHED)) + return; + break; + case J1939_ERRQUEUE_TX_ABORT: +@@ -997,7 +999,7 @@ static void __j1939_sk_errqueue(struct j1939_session *session, struct sock *sk, + case J1939_ERRQUEUE_RX_DPO: + fallthrough; + case J1939_ERRQUEUE_RX_ABORT: +- if (!(sk->sk_tsflags & SOF_TIMESTAMPING_RX_SOFTWARE)) ++ if (!(tsflags & SOF_TIMESTAMPING_RX_SOFTWARE)) + return; + break; + default: +@@ -1054,7 +1056,7 @@ static void __j1939_sk_errqueue(struct j1939_session *session, struct sock *sk, + } + + serr->opt_stats = true; +- if (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) ++ if (tsflags & SOF_TIMESTAMPING_OPT_ID) + serr->ee.ee_data = session->tskey; + + netdev_dbg(session->priv->ndev, "%s: 0x%p tskey: %i, state: %s\n", +diff --git a/net/can/raw.c b/net/can/raw.c +index 8c104339d538d..488320738e319 100644 +--- a/net/can/raw.c ++++ b/net/can/raw.c +@@ -881,6 +881,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) + + skb->dev = dev; + skb->priority = sk->sk_priority; ++ skb->mark = sk->sk_mark; + skb->tstamp = sockc.transmit_time; + + skb_setup_tx_timestamp(skb, sockc.tsflags); +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index 73b1e0e53534e..8a819d0a7bfb0 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -4913,7 +4913,7 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb, + serr->ee.ee_info = tstype; + serr->opt_stats = opt_stats; + serr->header.h4.iif = skb->dev ? skb->dev->ifindex : 0; +- if (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) { ++ if (READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) { + serr->ee.ee_data = skb_shinfo(skb)->tskey; + if (sk_is_tcp(sk)) + serr->ee.ee_data -= atomic_read(&sk->sk_tskey); +@@ -4969,21 +4969,23 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb, + { + struct sk_buff *skb; + bool tsonly, opt_stats = false; ++ u32 tsflags; + + if (!sk) + return; + +- if (!hwtstamps && !(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TX_SWHW) && ++ tsflags = READ_ONCE(sk->sk_tsflags); ++ if (!hwtstamps && !(tsflags & SOF_TIMESTAMPING_OPT_TX_SWHW) && + skb_shinfo(orig_skb)->tx_flags & SKBTX_IN_PROGRESS) + return; + +- tsonly = sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TSONLY; ++ tsonly = tsflags & SOF_TIMESTAMPING_OPT_TSONLY; + if (!skb_may_tx_timestamp(sk, tsonly)) + return; + + if (tsonly) { + #ifdef CONFIG_INET +- if ((sk->sk_tsflags & SOF_TIMESTAMPING_OPT_STATS) && ++ if ((tsflags & SOF_TIMESTAMPING_OPT_STATS) && + sk_is_tcp(sk)) { + skb = tcp_get_timestamping_opt_stats(sk, orig_skb, + ack_skb); +diff --git a/net/core/skmsg.c b/net/core/skmsg.c +index a5c1f67dc96ec..3818035ea0021 100644 +--- a/net/core/skmsg.c ++++ b/net/core/skmsg.c +@@ -825,6 +825,8 @@ static void sk_psock_destroy(struct work_struct *work) + + if (psock->sk_redir) + sock_put(psock->sk_redir); ++ if (psock->sk_pair) ++ sock_put(psock->sk_pair); + sock_put(psock->sk); + kfree(psock); + } +diff --git a/net/core/sock.c b/net/core/sock.c +index 4305e55dbfba4..c50a14a02edd4 100644 +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -890,7 +890,7 @@ static int sock_timestamping_bind_phc(struct sock *sk, int phc_index) + if (!match) + return -EINVAL; + +- sk->sk_bind_phc = phc_index; ++ WRITE_ONCE(sk->sk_bind_phc, phc_index); + + return 0; + } +@@ -926,7 +926,7 @@ int sock_set_timestamping(struct sock *sk, int optname, + return ret; + } + +- sk->sk_tsflags = val; ++ WRITE_ONCE(sk->sk_tsflags, val); + sock_valbool_flag(sk, SOCK_TSTAMP_NEW, optname == SO_TIMESTAMPING_NEW); + + if (val & SOF_TIMESTAMPING_RX_SOFTWARE) +@@ -1704,9 +1704,16 @@ int sk_getsockopt(struct sock *sk, int level, int optname, + break; + + case SO_TIMESTAMPING_OLD: ++ case SO_TIMESTAMPING_NEW: + lv = sizeof(v.timestamping); +- v.timestamping.flags = sk->sk_tsflags; +- v.timestamping.bind_phc = sk->sk_bind_phc; ++ /* For the later-added case SO_TIMESTAMPING_NEW: Be strict about only ++ * returning the flags when they were set through the same option. ++ * Don't change the beviour for the old case SO_TIMESTAMPING_OLD. ++ */ ++ if (optname == SO_TIMESTAMPING_OLD || sock_flag(sk, SOCK_TSTAMP_NEW)) { ++ v.timestamping.flags = READ_ONCE(sk->sk_tsflags); ++ v.timestamping.bind_phc = READ_ONCE(sk->sk_bind_phc); ++ } + break; + + case SO_RCVTIMEO_OLD: +@@ -2764,6 +2771,7 @@ int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg, + sockc->mark = *(u32 *)CMSG_DATA(cmsg); + break; + case SO_TIMESTAMPING_OLD: ++ case SO_TIMESTAMPING_NEW: + if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32))) + return -EINVAL; + +diff --git a/net/core/sock_map.c b/net/core/sock_map.c +index 38e01f82f2ef3..91140bc0541f3 100644 +--- a/net/core/sock_map.c ++++ b/net/core/sock_map.c +@@ -538,6 +538,8 @@ static bool sock_map_sk_state_allowed(const struct sock *sk) + { + if (sk_is_tcp(sk)) + return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_LISTEN); ++ if (sk_is_stream_unix(sk)) ++ return (1 << sk->sk_state) & TCPF_ESTABLISHED; + return true; + } + +diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c +index 03f8f33dc134c..8324e9f970668 100644 +--- a/net/dns_resolver/dns_key.c ++++ b/net/dns_resolver/dns_key.c +@@ -91,8 +91,6 @@ const struct cred *dns_resolver_cache; + static int + dns_resolver_preparse(struct key_preparsed_payload *prep) + { +- const struct dns_server_list_v1_header *v1; +- const struct dns_payload_header *bin; + struct user_key_payload *upayload; + unsigned long derrno; + int ret; +@@ -103,27 +101,28 @@ dns_resolver_preparse(struct key_preparsed_payload *prep) + return -EINVAL; + + if (data[0] == 0) { ++ const struct dns_server_list_v1_header *v1; ++ + /* It may be a server list. */ +- if (datalen <= sizeof(*bin)) ++ if (datalen <= sizeof(*v1)) + return -EINVAL; + +- bin = (const struct dns_payload_header *)data; +- kenter("[%u,%u],%u", bin->content, bin->version, datalen); +- if (bin->content != DNS_PAYLOAD_IS_SERVER_LIST) { ++ v1 = (const struct dns_server_list_v1_header *)data; ++ kenter("[%u,%u],%u", v1->hdr.content, v1->hdr.version, datalen); ++ if (v1->hdr.content != DNS_PAYLOAD_IS_SERVER_LIST) { + pr_warn_ratelimited( + "dns_resolver: Unsupported content type (%u)\n", +- bin->content); ++ v1->hdr.content); + return -EINVAL; + } + +- if (bin->version != 1) { ++ if (v1->hdr.version != 1) { + pr_warn_ratelimited( + "dns_resolver: Unsupported server list version (%u)\n", +- bin->version); ++ v1->hdr.version); + return -EINVAL; + } + +- v1 = (const struct dns_server_list_v1_header *)bin; + if ((v1->status != DNS_LOOKUP_GOOD && + v1->status != DNS_LOOKUP_GOOD_WITH_BAD)) { + if (prep->expiry == TIME64_MAX) +diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c +index 1a4c11356c96c..fc4ccecf9495c 100644 +--- a/net/ethtool/netlink.c ++++ b/net/ethtool/netlink.c +@@ -509,7 +509,7 @@ lock_and_cont: + cont: + idx++; + } +- ++ ret = 0; + } + rtnl_unlock(); + +diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c +index 5d379df90c826..347c3768df6e8 100644 +--- a/net/ipv4/af_inet.c ++++ b/net/ipv4/af_inet.c +@@ -838,6 +838,21 @@ int inet_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) + } + EXPORT_SYMBOL(inet_sendmsg); + ++void inet_splice_eof(struct socket *sock) ++{ ++ const struct proto *prot; ++ struct sock *sk = sock->sk; ++ ++ if (unlikely(inet_send_prepare(sk))) ++ return; ++ ++ /* IPV6_ADDRFORM can change sk->sk_prot under us. */ ++ prot = READ_ONCE(sk->sk_prot); ++ if (prot->splice_eof) ++ prot->splice_eof(sock); ++} ++EXPORT_SYMBOL_GPL(inet_splice_eof); ++ + ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset, + size_t size, int flags) + { +@@ -1057,6 +1072,7 @@ const struct proto_ops inet_stream_ops = { + #ifdef CONFIG_MMU + .mmap = tcp_mmap, + #endif ++ .splice_eof = inet_splice_eof, + .sendpage = inet_sendpage, + .splice_read = tcp_splice_read, + .read_sock = tcp_read_sock, +@@ -1091,6 +1107,7 @@ const struct proto_ops inet_dgram_ops = { + .read_skb = udp_read_skb, + .recvmsg = inet_recvmsg, + .mmap = sock_no_mmap, ++ .splice_eof = inet_splice_eof, + .sendpage = inet_sendpage, + .set_peek_off = sk_set_peek_off, + #ifdef CONFIG_COMPAT +@@ -1122,6 +1139,7 @@ static const struct proto_ops inet_sockraw_ops = { + .sendmsg = inet_sendmsg, + .recvmsg = inet_recvmsg, + .mmap = sock_no_mmap, ++ .splice_eof = inet_splice_eof, + .sendpage = inet_sendpage, + #ifdef CONFIG_COMPAT + .compat_ioctl = inet_compat_ioctl, +diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c +index 493c679ea54f3..e19ef88ae181f 100644 +--- a/net/ipv4/ip_output.c ++++ b/net/ipv4/ip_output.c +@@ -990,8 +990,8 @@ static int __ip_append_data(struct sock *sk, + mtu = cork->gso_size ? IP_MAX_MTU : cork->fragsize; + paged = !!cork->gso_size; + +- if (cork->tx_flags & SKBTX_ANY_SW_TSTAMP && +- sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) ++ if (cork->tx_flags & SKBTX_ANY_TSTAMP && ++ READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) + tskey = atomic_inc_return(&sk->sk_tskey) - 1; + + hh_len = LL_RESERVED_SPACE(rt->dst.dev); +diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c +index 63aa52becd880..c1fb7580ea581 100644 +--- a/net/ipv4/ip_sockglue.c ++++ b/net/ipv4/ip_sockglue.c +@@ -509,7 +509,7 @@ static bool ipv4_datagram_support_cmsg(const struct sock *sk, + * or without payload (SOF_TIMESTAMPING_OPT_TSONLY). + */ + info = PKTINFO_SKB_CB(skb); +- if (!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG) || ++ if (!(READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_CMSG) || + !info->ipi_ifindex) + return false; + +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index 58409ea2da0af..0b7844a8d5711 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -1492,6 +1492,22 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) + } + EXPORT_SYMBOL(tcp_sendmsg); + ++void tcp_splice_eof(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ struct tcp_sock *tp = tcp_sk(sk); ++ int mss_now, size_goal; ++ ++ if (!tcp_write_queue_tail(sk)) ++ return; ++ ++ lock_sock(sk); ++ mss_now = tcp_send_mss(sk, &size_goal, 0); ++ tcp_push(sk, 0, mss_now, tp->nonagle, size_goal); ++ release_sock(sk); ++} ++EXPORT_SYMBOL_GPL(tcp_splice_eof); ++ + /* + * Handle reading urgent data. BSD has very simple semantics for + * this, no blocking and very strange errors 8) +@@ -2359,14 +2375,14 @@ void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk, + } + } + +- if (sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) ++ if (READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_SOFTWARE) + has_timestamping = true; + else + tss->ts[0] = (struct timespec64) {0}; + } + + if (tss->ts[2].tv_sec || tss->ts[2].tv_nsec) { +- if (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) ++ if (READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_RAW_HARDWARE) + has_timestamping = true; + else + tss->ts[2] = (struct timespec64) {0}; +diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c +index 7ebbbe561e402..be2c807eed15d 100644 +--- a/net/ipv4/tcp_ipv4.c ++++ b/net/ipv4/tcp_ipv4.c +@@ -3067,6 +3067,7 @@ struct proto tcp_prot = { + .keepalive = tcp_set_keepalive, + .recvmsg = tcp_recvmsg, + .sendmsg = tcp_sendmsg, ++ .splice_eof = tcp_splice_eof, + .sendpage = tcp_sendpage, + .backlog_rcv = tcp_v4_do_rcv, + .release_cb = tcp_release_cb, +diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c +index 65abc92a81bd0..5672d9a86c5d2 100644 +--- a/net/ipv4/udp.c ++++ b/net/ipv4/udp.c +@@ -733,7 +733,7 @@ int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) + iph->saddr, uh->source, skb->dev->ifindex, + inet_sdif(skb), udptable, NULL); + +- if (!sk || udp_sk(sk)->encap_type) { ++ if (!sk || READ_ONCE(udp_sk(sk)->encap_type)) { + /* No socket for error: try tunnels before discarding */ + if (static_branch_unlikely(&udp_encap_needed_key)) { + sk = __udp4_lib_err_encap(net, iph, uh, udptable, sk, skb, +@@ -1068,7 +1068,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) + __be16 dport; + u8 tos; + int err, is_udplite = IS_UDPLITE(sk); +- int corkreq = READ_ONCE(up->corkflag) || msg->msg_flags&MSG_MORE; ++ int corkreq = udp_test_bit(CORK, sk) || msg->msg_flags & MSG_MORE; + int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); + struct sk_buff *skb; + struct ip_options_data opt_copy; +@@ -1332,57 +1332,33 @@ do_confirm: + } + EXPORT_SYMBOL(udp_sendmsg); + +-int udp_sendpage(struct sock *sk, struct page *page, int offset, +- size_t size, int flags) ++void udp_splice_eof(struct socket *sock) + { +- struct inet_sock *inet = inet_sk(sk); ++ struct sock *sk = sock->sk; + struct udp_sock *up = udp_sk(sk); +- int ret; + +- if (flags & MSG_SENDPAGE_NOTLAST) +- flags |= MSG_MORE; +- +- if (!up->pending) { +- struct msghdr msg = { .msg_flags = flags|MSG_MORE }; +- +- /* Call udp_sendmsg to specify destination address which +- * sendpage interface can't pass. +- * This will succeed only when the socket is connected. +- */ +- ret = udp_sendmsg(sk, &msg, 0); +- if (ret < 0) +- return ret; +- } ++ if (!up->pending || udp_test_bit(CORK, sk)) ++ return; + + lock_sock(sk); ++ if (up->pending && !udp_test_bit(CORK, sk)) ++ udp_push_pending_frames(sk); ++ release_sock(sk); ++} ++EXPORT_SYMBOL_GPL(udp_splice_eof); + +- if (unlikely(!up->pending)) { +- release_sock(sk); +- +- net_dbg_ratelimited("cork failed\n"); +- return -EINVAL; +- } ++int udp_sendpage(struct sock *sk, struct page *page, int offset, ++ size_t size, int flags) ++{ ++ struct bio_vec bvec; ++ struct msghdr msg = { .msg_flags = flags | MSG_SPLICE_PAGES }; + +- ret = ip_append_page(sk, &inet->cork.fl.u.ip4, +- page, offset, size, flags); +- if (ret == -EOPNOTSUPP) { +- release_sock(sk); +- return sock_no_sendpage(sk->sk_socket, page, offset, +- size, flags); +- } +- if (ret < 0) { +- udp_flush_pending_frames(sk); +- goto out; +- } ++ if (flags & MSG_SENDPAGE_NOTLAST) ++ msg.msg_flags |= MSG_MORE; + +- up->len += size; +- if (!(READ_ONCE(up->corkflag) || (flags&MSG_MORE))) +- ret = udp_push_pending_frames(sk); +- if (!ret) +- ret = size; +-out: +- release_sock(sk); +- return ret; ++ bvec_set_page(&bvec, page, size, offset); ++ iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, &bvec, 1, size); ++ return udp_sendmsg(sk, &msg, size); + } + + #define UDP_SKB_IS_STATELESS 0x80000000 +@@ -1925,7 +1901,7 @@ try_again: + (struct sockaddr *)sin); + } + +- if (udp_sk(sk)->gro_enabled) ++ if (udp_test_bit(GRO_ENABLED, sk)) + udp_cmsg_recv(msg, sk, skb); + + if (inet->cmsg_flags) +@@ -2138,7 +2114,8 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) + } + nf_reset_ct(skb); + +- if (static_branch_unlikely(&udp_encap_needed_key) && up->encap_type) { ++ if (static_branch_unlikely(&udp_encap_needed_key) && ++ READ_ONCE(up->encap_type)) { + int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); + + /* +@@ -2669,7 +2646,7 @@ void udp_destroy_sock(struct sock *sk) + if (encap_destroy) + encap_destroy(sk); + } +- if (up->encap_enabled) ++ if (udp_test_bit(ENCAP_ENABLED, sk)) + static_branch_dec(&udp_encap_needed_key); + } + } +@@ -2697,9 +2674,9 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, + switch (optname) { + case UDP_CORK: + if (val != 0) { +- WRITE_ONCE(up->corkflag, 1); ++ udp_set_bit(CORK, sk); + } else { +- WRITE_ONCE(up->corkflag, 0); ++ udp_clear_bit(CORK, sk); + lock_sock(sk); + push_pending_frames(sk); + release_sock(sk); +@@ -2723,10 +2700,8 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, + #endif + fallthrough; + case UDP_ENCAP_L2TPINUDP: +- up->encap_type = val; +- lock_sock(sk); +- udp_tunnel_encap_enable(sk->sk_socket); +- release_sock(sk); ++ WRITE_ONCE(up->encap_type, val); ++ udp_tunnel_encap_enable(sk); + break; + default: + err = -ENOPROTOOPT; +@@ -2735,11 +2710,11 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, + break; + + case UDP_NO_CHECK6_TX: +- up->no_check6_tx = valbool; ++ udp_set_no_check6_tx(sk, valbool); + break; + + case UDP_NO_CHECK6_RX: +- up->no_check6_rx = valbool; ++ udp_set_no_check6_rx(sk, valbool); + break; + + case UDP_SEGMENT: +@@ -2749,14 +2724,12 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, + break; + + case UDP_GRO: +- lock_sock(sk); + + /* when enabling GRO, accept the related GSO packet type */ + if (valbool) +- udp_tunnel_encap_enable(sk->sk_socket); +- up->gro_enabled = valbool; +- up->accept_udp_l4 = valbool; +- release_sock(sk); ++ udp_tunnel_encap_enable(sk); ++ udp_assign_bit(GRO_ENABLED, sk, valbool); ++ udp_assign_bit(ACCEPT_L4, sk, valbool); + break; + + /* +@@ -2824,19 +2797,19 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, + + switch (optname) { + case UDP_CORK: +- val = READ_ONCE(up->corkflag); ++ val = udp_test_bit(CORK, sk); + break; + + case UDP_ENCAP: +- val = up->encap_type; ++ val = READ_ONCE(up->encap_type); + break; + + case UDP_NO_CHECK6_TX: +- val = up->no_check6_tx; ++ val = udp_get_no_check6_tx(sk); + break; + + case UDP_NO_CHECK6_RX: +- val = up->no_check6_rx; ++ val = udp_get_no_check6_rx(sk); + break; + + case UDP_SEGMENT: +@@ -2844,7 +2817,7 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, + break; + + case UDP_GRO: +- val = up->gro_enabled; ++ val = udp_test_bit(GRO_ENABLED, sk); + break; + + /* The following two cannot be changed on UDP sockets, the return is +@@ -2946,6 +2919,7 @@ struct proto udp_prot = { + .getsockopt = udp_getsockopt, + .sendmsg = udp_sendmsg, + .recvmsg = udp_recvmsg, ++ .splice_eof = udp_splice_eof, + .sendpage = udp_sendpage, + .release_cb = ip4_datagram_release_cb, + .hash = udp_lib_hash, +diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c +index 6d1a4bec2614d..8096576fd9bde 100644 +--- a/net/ipv4/udp_offload.c ++++ b/net/ipv4/udp_offload.c +@@ -549,10 +549,10 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb, + NAPI_GRO_CB(skb)->is_flist = 0; + if (!sk || !udp_sk(sk)->gro_receive) { + if (skb->dev->features & NETIF_F_GRO_FRAGLIST) +- NAPI_GRO_CB(skb)->is_flist = sk ? !udp_sk(sk)->gro_enabled : 1; ++ NAPI_GRO_CB(skb)->is_flist = sk ? !udp_test_bit(GRO_ENABLED, sk) : 1; + + if ((!sk && (skb->dev->features & NETIF_F_GRO_UDP_FWD)) || +- (sk && udp_sk(sk)->gro_enabled) || NAPI_GRO_CB(skb)->is_flist) ++ (sk && udp_test_bit(GRO_ENABLED, sk)) || NAPI_GRO_CB(skb)->is_flist) + return call_gro_receive(udp_gro_receive_segment, head, skb); + + /* no GRO, be sure flush the current packet */ +diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c +index 5f8104cf082d0..732e21b75ba28 100644 +--- a/net/ipv4/udp_tunnel_core.c ++++ b/net/ipv4/udp_tunnel_core.c +@@ -78,7 +78,7 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock, + udp_sk(sk)->gro_receive = cfg->gro_receive; + udp_sk(sk)->gro_complete = cfg->gro_complete; + +- udp_tunnel_encap_enable(sock); ++ udp_tunnel_encap_enable(sk); + } + EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock); + +diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c +index eac206a290d05..183f6dc372429 100644 +--- a/net/ipv4/xfrm4_input.c ++++ b/net/ipv4/xfrm4_input.c +@@ -85,11 +85,11 @@ int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) + struct udphdr *uh; + struct iphdr *iph; + int iphlen, len; +- + __u8 *udpdata; + __be32 *udpdata32; +- __u16 encap_type = up->encap_type; ++ u16 encap_type; + ++ encap_type = READ_ONCE(up->encap_type); + /* if this is not encapsulated socket, then just return now */ + if (!encap_type) + return 1; +diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c +index b5309ae87fd79..a2f29ca516000 100644 +--- a/net/ipv6/af_inet6.c ++++ b/net/ipv6/af_inet6.c +@@ -711,6 +711,7 @@ const struct proto_ops inet6_stream_ops = { + #ifdef CONFIG_MMU + .mmap = tcp_mmap, + #endif ++ .splice_eof = inet_splice_eof, + .sendpage = inet_sendpage, + .sendmsg_locked = tcp_sendmsg_locked, + .sendpage_locked = tcp_sendpage_locked, +diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c +index 3c2b2a85de367..e9ae084d038d1 100644 +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -1506,8 +1506,8 @@ static int __ip6_append_data(struct sock *sk, + mtu = cork->gso_size ? IP6_MAX_MTU : cork->fragsize; + orig_mtu = mtu; + +- if (cork->tx_flags & SKBTX_ANY_SW_TSTAMP && +- sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) ++ if (cork->tx_flags & SKBTX_ANY_TSTAMP && ++ READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) + tskey = atomic_inc_return(&sk->sk_tskey) - 1; + + hh_len = LL_RESERVED_SPACE(rt->dst.dev); +diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c +index 4d5a27dd9a4b2..a5d7d1915ba7e 100644 +--- a/net/ipv6/ping.c ++++ b/net/ipv6/ping.c +@@ -119,7 +119,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) + return -EINVAL; + + ipcm6_init_sk(&ipc6, np); +- ipc6.sockc.tsflags = sk->sk_tsflags; ++ ipc6.sockc.tsflags = READ_ONCE(sk->sk_tsflags); + ipc6.sockc.mark = READ_ONCE(sk->sk_mark); + + fl6.flowi6_oif = oif; +diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c +index df3abd9e5237c..dc31752a7edcc 100644 +--- a/net/ipv6/raw.c ++++ b/net/ipv6/raw.c +@@ -776,7 +776,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) + fl6.flowi6_uid = sk->sk_uid; + + ipcm6_init(&ipc6); +- ipc6.sockc.tsflags = sk->sk_tsflags; ++ ipc6.sockc.tsflags = READ_ONCE(sk->sk_tsflags); + ipc6.sockc.mark = fl6.flowi6_mark; + + if (sin6) { +diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c +index 7be89dcfd5fc5..ba9a22db5805c 100644 +--- a/net/ipv6/tcp_ipv6.c ++++ b/net/ipv6/tcp_ipv6.c +@@ -2158,6 +2158,7 @@ struct proto tcpv6_prot = { + .keepalive = tcp_set_keepalive, + .recvmsg = tcp_recvmsg, + .sendmsg = tcp_sendmsg, ++ .splice_eof = tcp_splice_eof, + .sendpage = tcp_sendpage, + .backlog_rcv = tcp_v6_do_rcv, + .release_cb = tcp_release_cb, +diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c +index 64b36c2ba774a..961106eda69d0 100644 +--- a/net/ipv6/udp.c ++++ b/net/ipv6/udp.c +@@ -440,7 +440,7 @@ try_again: + (struct sockaddr *)sin6); + } + +- if (udp_sk(sk)->gro_enabled) ++ if (udp_test_bit(GRO_ENABLED, sk)) + udp_cmsg_recv(msg, sk, skb); + + if (np->rxopt.all) +@@ -598,7 +598,7 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source, + inet6_iif(skb), inet6_sdif(skb), udptable, NULL); + +- if (!sk || udp_sk(sk)->encap_type) { ++ if (!sk || READ_ONCE(udp_sk(sk)->encap_type)) { + /* No socket for error: try tunnels before discarding */ + if (static_branch_unlikely(&udpv6_encap_needed_key)) { + sk = __udp6_lib_err_encap(net, hdr, offset, uh, +@@ -712,7 +712,8 @@ static int udpv6_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) + } + nf_reset_ct(skb); + +- if (static_branch_unlikely(&udpv6_encap_needed_key) && up->encap_type) { ++ if (static_branch_unlikely(&udpv6_encap_needed_key) && ++ READ_ONCE(up->encap_type)) { + int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); + + /* +@@ -882,7 +883,7 @@ start_lookup: + /* If zero checksum and no_check is not on for + * the socket then skip it. + */ +- if (!uh->check && !udp_sk(sk)->no_check6_rx) ++ if (!uh->check && !udp_get_no_check6_rx(sk)) + continue; + if (!first) { + first = sk; +@@ -1000,7 +1001,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, + if (unlikely(rcu_dereference(sk->sk_rx_dst) != dst)) + udp6_sk_rx_dst_set(sk, dst); + +- if (!uh->check && !udp_sk(sk)->no_check6_rx) { ++ if (!uh->check && !udp_get_no_check6_rx(sk)) { + if (refcounted) + sock_put(sk); + goto report_csum_error; +@@ -1022,7 +1023,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, + /* Unicast */ + sk = __udp6_lib_lookup_skb(skb, uh->source, uh->dest, udptable); + if (sk) { +- if (!uh->check && !udp_sk(sk)->no_check6_rx) ++ if (!uh->check && !udp_get_no_check6_rx(sk)) + goto report_csum_error; + return udp6_unicast_rcv_skb(sk, skb, uh); + } +@@ -1260,7 +1261,7 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6, + kfree_skb(skb); + return -EINVAL; + } +- if (udp_sk(sk)->no_check6_tx) { ++ if (udp_get_no_check6_tx(sk)) { + kfree_skb(skb); + return -EINVAL; + } +@@ -1281,7 +1282,7 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6, + + if (is_udplite) + csum = udplite_csum(skb); +- else if (udp_sk(sk)->no_check6_tx) { /* UDP csum disabled */ ++ else if (udp_get_no_check6_tx(sk)) { /* UDP csum disabled */ + skb->ip_summed = CHECKSUM_NONE; + goto send; + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ +@@ -1351,14 +1352,14 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) + int addr_len = msg->msg_namelen; + bool connected = false; + int ulen = len; +- int corkreq = READ_ONCE(up->corkflag) || msg->msg_flags&MSG_MORE; ++ int corkreq = udp_test_bit(CORK, sk) || msg->msg_flags & MSG_MORE; + int err; + int is_udplite = IS_UDPLITE(sk); + int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); + + ipcm6_init(&ipc6); + ipc6.gso_size = READ_ONCE(up->gso_size); +- ipc6.sockc.tsflags = sk->sk_tsflags; ++ ipc6.sockc.tsflags = READ_ONCE(sk->sk_tsflags); + ipc6.sockc.mark = READ_ONCE(sk->sk_mark); + + /* destination address check */ +@@ -1657,6 +1658,20 @@ do_confirm: + goto out; + } + ++static void udpv6_splice_eof(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ struct udp_sock *up = udp_sk(sk); ++ ++ if (!up->pending || udp_test_bit(CORK, sk)) ++ return; ++ ++ lock_sock(sk); ++ if (up->pending && !udp_test_bit(CORK, sk)) ++ udp_v6_push_pending_frames(sk); ++ release_sock(sk); ++} ++ + void udpv6_destroy_sock(struct sock *sk) + { + struct udp_sock *up = udp_sk(sk); +@@ -1674,7 +1689,7 @@ void udpv6_destroy_sock(struct sock *sk) + if (encap_destroy) + encap_destroy(sk); + } +- if (up->encap_enabled) { ++ if (udp_test_bit(ENCAP_ENABLED, sk)) { + static_branch_dec(&udpv6_encap_needed_key); + udp_encap_disable(); + } +@@ -1768,6 +1783,7 @@ struct proto udpv6_prot = { + .getsockopt = udpv6_getsockopt, + .sendmsg = udpv6_sendmsg, + .recvmsg = udpv6_recvmsg, ++ .splice_eof = udpv6_splice_eof, + .release_cb = ip6_datagram_release_cb, + .hash = udp_lib_hash, + .unhash = udp_lib_unhash, +diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c +index 4907ab241d6be..4156387248e40 100644 +--- a/net/ipv6/xfrm6_input.c ++++ b/net/ipv6/xfrm6_input.c +@@ -81,14 +81,14 @@ int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) + struct ipv6hdr *ip6h; + int len; + int ip6hlen = sizeof(struct ipv6hdr); +- + __u8 *udpdata; + __be32 *udpdata32; +- __u16 encap_type = up->encap_type; ++ u16 encap_type; + + if (skb->protocol == htons(ETH_P_IP)) + return xfrm4_udp_encap_rcv(sk, skb); + ++ encap_type = READ_ONCE(up->encap_type); + /* if this is not encapsulated socket, then just return now */ + if (!encap_type) + return 1; +diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c +index 03608d3ded4b8..8d21ff25f1602 100644 +--- a/net/l2tp/l2tp_core.c ++++ b/net/l2tp/l2tp_core.c +@@ -1139,9 +1139,9 @@ static void l2tp_tunnel_destruct(struct sock *sk) + switch (tunnel->encap) { + case L2TP_ENCAPTYPE_UDP: + /* No longer an encapsulation socket. See net/ipv4/udp.c */ +- (udp_sk(sk))->encap_type = 0; +- (udp_sk(sk))->encap_rcv = NULL; +- (udp_sk(sk))->encap_destroy = NULL; ++ WRITE_ONCE(udp_sk(sk)->encap_type, 0); ++ udp_sk(sk)->encap_rcv = NULL; ++ udp_sk(sk)->encap_destroy = NULL; + break; + case L2TP_ENCAPTYPE_IP: + break; +diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c +index d611783c2601f..8ed7769cae836 100644 +--- a/net/mptcp/subflow.c ++++ b/net/mptcp/subflow.c +@@ -1899,6 +1899,17 @@ static void tcp_release_cb_override(struct sock *ssk) + tcp_release_cb(ssk); + } + ++static int tcp_abort_override(struct sock *ssk, int err) ++{ ++ /* closing a listener subflow requires a great deal of care. ++ * keep it simple and just prevent such operation ++ */ ++ if (inet_sk_state_load(ssk) == TCP_LISTEN) ++ return -EINVAL; ++ ++ return tcp_abort(ssk, err); ++} ++ + static struct tcp_ulp_ops subflow_ulp_ops __read_mostly = { + .name = "mptcp", + .owner = THIS_MODULE, +@@ -1942,6 +1953,7 @@ void __init mptcp_subflow_init(void) + + tcp_prot_override = tcp_prot; + tcp_prot_override.release_cb = tcp_release_cb_override; ++ tcp_prot_override.diag_destroy = tcp_abort_override; + + #if IS_ENABLED(CONFIG_MPTCP_IPV6) + /* In struct mptcp_subflow_request_sock, we assume the TCP request sock +@@ -1977,6 +1989,7 @@ void __init mptcp_subflow_init(void) + + tcpv6_prot_override = tcpv6_prot; + tcpv6_prot_override.release_cb = tcp_release_cb_override; ++ tcpv6_prot_override.diag_destroy = tcp_abort_override; + #endif + + mptcp_diag_subflow_init(&subflow_ulp_ops); +diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c +index 7243079ef3546..b452eb3ddcecb 100644 +--- a/net/netfilter/ipvs/ip_vs_xmit.c ++++ b/net/netfilter/ipvs/ip_vs_xmit.c +@@ -994,7 +994,7 @@ ip_vs_prepare_tunneled_skb(struct sk_buff *skb, int skb_af, + old_dsfield = ipv4_get_dsfield(old_iph); + *ttl = old_iph->ttl; + if (payload_len) +- *payload_len = ntohs(old_iph->tot_len); ++ *payload_len = skb_ip_totlen(skb); + } + + /* Implement full-functionality option for ECN encapsulation */ +diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c +index 81c26a96c30bb..c1d99cb370b44 100644 +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -314,12 +314,12 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) + EXPORT_SYMBOL_GPL(flow_offload_add); + + void flow_offload_refresh(struct nf_flowtable *flow_table, +- struct flow_offload *flow) ++ struct flow_offload *flow, bool force) + { + u32 timeout; + + timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow); +- if (timeout - READ_ONCE(flow->timeout) > HZ) ++ if (force || timeout - READ_ONCE(flow->timeout) > HZ) + WRITE_ONCE(flow->timeout, timeout); + else + return; +@@ -416,11 +416,18 @@ nf_flow_table_iterate(struct nf_flowtable *flow_table, + return err; + } + ++static bool nf_flow_custom_gc(struct nf_flowtable *flow_table, ++ const struct flow_offload *flow) ++{ ++ return flow_table->type->gc && flow_table->type->gc(flow); ++} ++ + static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table, + struct flow_offload *flow, void *data) + { + if (nf_flow_has_expired(flow) || +- nf_ct_is_dying(flow->ct)) ++ nf_ct_is_dying(flow->ct) || ++ nf_flow_custom_gc(flow_table, flow)) + flow_offload_teardown(flow); + + if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) { +diff --git a/net/netfilter/nf_flow_table_inet.c b/net/netfilter/nf_flow_table_inet.c +index 0ccabf3fa6aa3..9505f9d188ff2 100644 +--- a/net/netfilter/nf_flow_table_inet.c ++++ b/net/netfilter/nf_flow_table_inet.c +@@ -39,7 +39,7 @@ nf_flow_offload_inet_hook(void *priv, struct sk_buff *skb, + } + + static int nf_flow_rule_route_inet(struct net *net, +- const struct flow_offload *flow, ++ struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) + { +diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c +index b350fe9d00b0b..6feaac9ab05c8 100644 +--- a/net/netfilter/nf_flow_table_ip.c ++++ b/net/netfilter/nf_flow_table_ip.c +@@ -384,7 +384,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, + if (skb_try_make_writable(skb, thoff + hdrsize)) + return NF_DROP; + +- flow_offload_refresh(flow_table, flow); ++ flow_offload_refresh(flow_table, flow, false); + + nf_flow_encap_pop(skb, tuplehash); + thoff -= offset; +@@ -646,7 +646,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, + if (skb_try_make_writable(skb, thoff + hdrsize)) + return NF_DROP; + +- flow_offload_refresh(flow_table, flow); ++ flow_offload_refresh(flow_table, flow, false); + + nf_flow_encap_pop(skb, tuplehash); + +diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c +index 4d9b99abe37d6..1c26f03fc6617 100644 +--- a/net/netfilter/nf_flow_table_offload.c ++++ b/net/netfilter/nf_flow_table_offload.c +@@ -679,7 +679,7 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow, + return 0; + } + +-int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow, ++int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) + { +@@ -704,7 +704,7 @@ int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow, + } + EXPORT_SYMBOL_GPL(nf_flow_rule_route_ipv4); + +-int nf_flow_rule_route_ipv6(struct net *net, const struct flow_offload *flow, ++int nf_flow_rule_route_ipv6(struct net *net, struct flow_offload *flow, + enum flow_offload_tuple_dir dir, + struct nf_flow_rule *flow_rule) + { +@@ -735,7 +735,7 @@ nf_flow_offload_rule_alloc(struct net *net, + { + const struct nf_flowtable *flowtable = offload->flowtable; + const struct flow_offload_tuple *tuple, *other_tuple; +- const struct flow_offload *flow = offload->flow; ++ struct flow_offload *flow = offload->flow; + struct dst_entry *other_dst = NULL; + struct nf_flow_rule *flow_rule; + int err = -ENOMEM; +@@ -895,8 +895,9 @@ static int flow_offload_rule_add(struct flow_offload_work *offload, + + ok_count += flow_offload_tuple_add(offload, flow_rule[0], + FLOW_OFFLOAD_DIR_ORIGINAL); +- ok_count += flow_offload_tuple_add(offload, flow_rule[1], +- FLOW_OFFLOAD_DIR_REPLY); ++ if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags)) ++ ok_count += flow_offload_tuple_add(offload, flow_rule[1], ++ FLOW_OFFLOAD_DIR_REPLY); + if (ok_count == 0) + return -ENOENT; + +@@ -926,7 +927,8 @@ static void flow_offload_work_del(struct flow_offload_work *offload) + { + clear_bit(IPS_HW_OFFLOAD_BIT, &offload->flow->ct->status); + flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_ORIGINAL); +- flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY); ++ if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags)) ++ flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY); + set_bit(NF_FLOW_HW_DEAD, &offload->flow->flags); + } + +@@ -946,7 +948,9 @@ static void flow_offload_work_stats(struct flow_offload_work *offload) + u64 lastused; + + flow_offload_tuple_stats(offload, FLOW_OFFLOAD_DIR_ORIGINAL, &stats[0]); +- flow_offload_tuple_stats(offload, FLOW_OFFLOAD_DIR_REPLY, &stats[1]); ++ if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags)) ++ flow_offload_tuple_stats(offload, FLOW_OFFLOAD_DIR_REPLY, ++ &stats[1]); + + lastused = max_t(u64, stats[0].lastused, stats[1].lastused); + offload->flow->timeout = max_t(u64, offload->flow->timeout, +diff --git a/net/netfilter/nf_log_syslog.c b/net/netfilter/nf_log_syslog.c +index cb894f0d63e9d..c66689ad2b491 100644 +--- a/net/netfilter/nf_log_syslog.c ++++ b/net/netfilter/nf_log_syslog.c +@@ -322,7 +322,7 @@ dump_ipv4_packet(struct net *net, struct nf_log_buf *m, + + /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ + nf_log_buf_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", +- ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK, ++ iph_totlen(skb, ih), ih->tos & IPTOS_TOS_MASK, + ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id)); + + /* Max length: 6 "CE DF MF " */ +diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c +index cee3e4e905ec8..e0c117229ee9d 100644 +--- a/net/netfilter/nf_tables_core.c ++++ b/net/netfilter/nf_tables_core.c +@@ -141,7 +141,7 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr, + else { + if (!(pkt->flags & NFT_PKTINFO_L4PROTO)) + return false; +- ptr = skb_network_header(skb) + nft_thoff(pkt); ++ ptr = skb->data + nft_thoff(pkt); + } + + ptr += priv->offset; +diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c +index 5f59dbab3e933..55fcf0280c5c3 100644 +--- a/net/netfilter/nft_immediate.c ++++ b/net/netfilter/nft_immediate.c +@@ -78,7 +78,7 @@ static int nft_immediate_init(const struct nft_ctx *ctx, + case NFT_GOTO: + err = nf_tables_bind_chain(ctx, chain); + if (err < 0) +- return err; ++ goto err1; + break; + default: + break; +diff --git a/net/netfilter/xt_length.c b/net/netfilter/xt_length.c +index 9fbfad13176f0..ca730cedb5d41 100644 +--- a/net/netfilter/xt_length.c ++++ b/net/netfilter/xt_length.c +@@ -21,7 +21,7 @@ static bool + length_mt(const struct sk_buff *skb, struct xt_action_param *par) + { + const struct xt_length_info *info = par->matchinfo; +- u_int16_t pktlen = ntohs(ip_hdr(skb)->tot_len); ++ u32 pktlen = skb_ip_totlen(skb); + + return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; + } +diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c +index 1dac28136e6a3..18be13fb9b75a 100644 +--- a/net/nfc/llcp_core.c ++++ b/net/nfc/llcp_core.c +@@ -145,6 +145,13 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool device, + + static struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local) + { ++ /* Since using nfc_llcp_local may result in usage of nfc_dev, whenever ++ * we hold a reference to local, we also need to hold a reference to ++ * the device to avoid UAF. ++ */ ++ if (!nfc_get_device(local->dev->idx)) ++ return NULL; ++ + kref_get(&local->ref); + + return local; +@@ -177,10 +184,18 @@ static void local_release(struct kref *ref) + + int nfc_llcp_local_put(struct nfc_llcp_local *local) + { ++ struct nfc_dev *dev; ++ int ret; ++ + if (local == NULL) + return 0; + +- return kref_put(&local->ref, local_release); ++ dev = local->dev; ++ ++ ret = kref_put(&local->ref, local_release); ++ nfc_put_device(dev); ++ ++ return ret; + } + + static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local, +@@ -959,8 +974,17 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local, + } + + new_sock = nfc_llcp_sock(new_sk); +- new_sock->dev = local->dev; ++ + new_sock->local = nfc_llcp_local_get(local); ++ if (!new_sock->local) { ++ reason = LLCP_DM_REJ; ++ sock_put(&new_sock->sk); ++ release_sock(&sock->sk); ++ sock_put(&sock->sk); ++ goto fail; ++ } ++ ++ new_sock->dev = local->dev; + new_sock->rw = sock->rw; + new_sock->miux = sock->miux; + new_sock->nfc_protocol = sock->nfc_protocol; +@@ -1597,7 +1621,16 @@ int nfc_llcp_register_device(struct nfc_dev *ndev) + if (local == NULL) + return -ENOMEM; + +- local->dev = ndev; ++ /* As we are going to initialize local's refcount, we need to get the ++ * nfc_dev to avoid UAF, otherwise there is no point in continuing. ++ * See nfc_llcp_local_get(). ++ */ ++ local->dev = nfc_get_device(ndev->idx); ++ if (!local->dev) { ++ kfree(local); ++ return -ENODEV; ++ } ++ + INIT_LIST_HEAD(&local->list); + kref_init(&local->ref); + mutex_init(&local->sdp_lock); +diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c +index c8eaf4234b2e0..0591cfb289d50 100644 +--- a/net/openvswitch/conntrack.c ++++ b/net/openvswitch/conntrack.c +@@ -1252,7 +1252,7 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key, + if (err) + return err; + +- nf_conn_act_ct_ext_add(ct); ++ nf_conn_act_ct_ext_add(skb, ct, ctinfo); + } else if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && + labels_nonzero(&info->labels.mask)) { + err = ovs_ct_set_labels(ct, key, &info->labels.value, +diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c +index 4c7f7861ea967..d6d33f854050a 100644 +--- a/net/sched/act_ct.c ++++ b/net/sched/act_ct.c +@@ -168,11 +168,11 @@ tcf_ct_flow_table_add_action_nat_udp(const struct nf_conntrack_tuple *tuple, + + static void tcf_ct_flow_table_add_action_meta(struct nf_conn *ct, + enum ip_conntrack_dir dir, ++ enum ip_conntrack_info ctinfo, + struct flow_action *action) + { + struct nf_conn_labels *ct_labels; + struct flow_action_entry *entry; +- enum ip_conntrack_info ctinfo; + u32 *act_ct_labels; + + entry = tcf_ct_flow_table_flow_action_get_next(action); +@@ -180,8 +180,6 @@ static void tcf_ct_flow_table_add_action_meta(struct nf_conn *ct, + #if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) + entry->ct_metadata.mark = READ_ONCE(ct->mark); + #endif +- ctinfo = dir == IP_CT_DIR_ORIGINAL ? IP_CT_ESTABLISHED : +- IP_CT_ESTABLISHED_REPLY; + /* aligns with the CT reference on the SKB nf_ct_set */ + entry->ct_metadata.cookie = (unsigned long)ct | ctinfo; + entry->ct_metadata.orig_dir = dir == IP_CT_DIR_ORIGINAL; +@@ -235,22 +233,26 @@ static int tcf_ct_flow_table_add_action_nat(struct net *net, + } + + static int tcf_ct_flow_table_fill_actions(struct net *net, +- const struct flow_offload *flow, ++ struct flow_offload *flow, + enum flow_offload_tuple_dir tdir, + struct nf_flow_rule *flow_rule) + { + struct flow_action *action = &flow_rule->rule->action; + int num_entries = action->num_entries; + struct nf_conn *ct = flow->ct; ++ enum ip_conntrack_info ctinfo; + enum ip_conntrack_dir dir; + int i, err; + + switch (tdir) { + case FLOW_OFFLOAD_DIR_ORIGINAL: + dir = IP_CT_DIR_ORIGINAL; ++ ctinfo = IP_CT_ESTABLISHED; ++ set_bit(NF_FLOW_HW_ESTABLISHED, &flow->flags); + break; + case FLOW_OFFLOAD_DIR_REPLY: + dir = IP_CT_DIR_REPLY; ++ ctinfo = IP_CT_ESTABLISHED_REPLY; + break; + default: + return -EOPNOTSUPP; +@@ -260,7 +262,7 @@ static int tcf_ct_flow_table_fill_actions(struct net *net, + if (err) + goto err_nat; + +- tcf_ct_flow_table_add_action_meta(ct, dir, action); ++ tcf_ct_flow_table_add_action_meta(ct, dir, ctinfo, action); + return 0; + + err_nat: +@@ -272,8 +274,39 @@ err_nat: + return err; + } + ++static bool tcf_ct_flow_is_outdated(const struct flow_offload *flow) ++{ ++ return test_bit(IPS_SEEN_REPLY_BIT, &flow->ct->status) && ++ test_bit(IPS_HW_OFFLOAD_BIT, &flow->ct->status) && ++ !test_bit(NF_FLOW_HW_PENDING, &flow->flags) && ++ !test_bit(NF_FLOW_HW_ESTABLISHED, &flow->flags); ++} ++ ++static void tcf_ct_flow_table_get_ref(struct tcf_ct_flow_table *ct_ft); ++ ++static void tcf_ct_nf_get(struct nf_flowtable *ft) ++{ ++ struct tcf_ct_flow_table *ct_ft = ++ container_of(ft, struct tcf_ct_flow_table, nf_ft); ++ ++ tcf_ct_flow_table_get_ref(ct_ft); ++} ++ ++static void tcf_ct_flow_table_put(struct tcf_ct_flow_table *ct_ft); ++ ++static void tcf_ct_nf_put(struct nf_flowtable *ft) ++{ ++ struct tcf_ct_flow_table *ct_ft = ++ container_of(ft, struct tcf_ct_flow_table, nf_ft); ++ ++ tcf_ct_flow_table_put(ct_ft); ++} ++ + static struct nf_flowtable_type flowtable_ct = { ++ .gc = tcf_ct_flow_is_outdated, + .action = tcf_ct_flow_table_fill_actions, ++ .get = tcf_ct_nf_get, ++ .put = tcf_ct_nf_put, + .owner = THIS_MODULE, + }; + +@@ -322,9 +355,13 @@ err_alloc: + return err; + } + ++static void tcf_ct_flow_table_get_ref(struct tcf_ct_flow_table *ct_ft) ++{ ++ refcount_inc(&ct_ft->ref); ++} ++ + static void tcf_ct_flow_table_cleanup_work(struct work_struct *work) + { +- struct flow_block_cb *block_cb, *tmp_cb; + struct tcf_ct_flow_table *ct_ft; + struct flow_block *block; + +@@ -332,24 +369,18 @@ static void tcf_ct_flow_table_cleanup_work(struct work_struct *work) + rwork); + nf_flow_table_free(&ct_ft->nf_ft); + +- /* Remove any remaining callbacks before cleanup */ + block = &ct_ft->nf_ft.flow_block; + down_write(&ct_ft->nf_ft.flow_block_lock); +- list_for_each_entry_safe(block_cb, tmp_cb, &block->cb_list, list) { +- list_del(&block_cb->list); +- flow_block_cb_free(block_cb); +- } ++ WARN_ON(!list_empty(&block->cb_list)); + up_write(&ct_ft->nf_ft.flow_block_lock); + kfree(ct_ft); + + module_put(THIS_MODULE); + } + +-static void tcf_ct_flow_table_put(struct tcf_ct_params *params) ++static void tcf_ct_flow_table_put(struct tcf_ct_flow_table *ct_ft) + { +- struct tcf_ct_flow_table *ct_ft = params->ct_ft; +- +- if (refcount_dec_and_test(¶ms->ct_ft->ref)) { ++ if (refcount_dec_and_test(&ct_ft->ref)) { + rhashtable_remove_fast(&zones_ht, &ct_ft->node, zones_params); + INIT_RCU_WORK(&ct_ft->rwork, tcf_ct_flow_table_cleanup_work); + queue_rcu_work(act_ct_wq, &ct_ft->rwork); +@@ -363,9 +394,20 @@ static void tcf_ct_flow_tc_ifidx(struct flow_offload *entry, + entry->tuplehash[dir].tuple.tc.iifidx = act_ct_ext->ifindex[dir]; + } + ++static void tcf_ct_flow_ct_ext_ifidx_update(struct flow_offload *entry) ++{ ++ struct nf_conn_act_ct_ext *act_ct_ext; ++ ++ act_ct_ext = nf_conn_act_ct_ext_find(entry->ct); ++ if (act_ct_ext) { ++ tcf_ct_flow_tc_ifidx(entry, act_ct_ext, FLOW_OFFLOAD_DIR_ORIGINAL); ++ tcf_ct_flow_tc_ifidx(entry, act_ct_ext, FLOW_OFFLOAD_DIR_REPLY); ++ } ++} ++ + static void tcf_ct_flow_table_add(struct tcf_ct_flow_table *ct_ft, + struct nf_conn *ct, +- bool tcp) ++ bool tcp, bool bidirectional) + { + struct nf_conn_act_ct_ext *act_ct_ext; + struct flow_offload *entry; +@@ -384,6 +426,8 @@ static void tcf_ct_flow_table_add(struct tcf_ct_flow_table *ct_ft, + ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; + ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; + } ++ if (bidirectional) ++ __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &entry->flags); + + act_ct_ext = nf_conn_act_ct_ext_find(ct); + if (act_ct_ext) { +@@ -407,26 +451,34 @@ static void tcf_ct_flow_table_process_conn(struct tcf_ct_flow_table *ct_ft, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo) + { +- bool tcp = false; +- +- if ((ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) || +- !test_bit(IPS_ASSURED_BIT, &ct->status)) +- return; ++ bool tcp = false, bidirectional = true; + + switch (nf_ct_protonum(ct)) { + case IPPROTO_TCP: +- tcp = true; +- if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) ++ if ((ctinfo != IP_CT_ESTABLISHED && ++ ctinfo != IP_CT_ESTABLISHED_REPLY) || ++ !test_bit(IPS_ASSURED_BIT, &ct->status) || ++ ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) + return; ++ ++ tcp = true; + break; + case IPPROTO_UDP: ++ if (!nf_ct_is_confirmed(ct)) ++ return; ++ if (!test_bit(IPS_ASSURED_BIT, &ct->status)) ++ bidirectional = false; + break; + #ifdef CONFIG_NF_CT_PROTO_GRE + case IPPROTO_GRE: { + struct nf_conntrack_tuple *tuple; + +- if (ct->status & IPS_NAT_MASK) ++ if ((ctinfo != IP_CT_ESTABLISHED && ++ ctinfo != IP_CT_ESTABLISHED_REPLY) || ++ !test_bit(IPS_ASSURED_BIT, &ct->status) || ++ ct->status & IPS_NAT_MASK) + return; ++ + tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + /* No support for GRE v1 */ + if (tuple->src.u.gre.key || tuple->dst.u.gre.key) +@@ -442,7 +494,7 @@ static void tcf_ct_flow_table_process_conn(struct tcf_ct_flow_table *ct_ft, + ct->status & IPS_SEQ_ADJUST) + return; + +- tcf_ct_flow_table_add(ct_ft, ct, tcp); ++ tcf_ct_flow_table_add(ct_ft, ct, tcp, bidirectional); + } + + static bool +@@ -596,6 +648,7 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p, + struct flow_offload_tuple tuple = {}; + enum ip_conntrack_info ctinfo; + struct tcphdr *tcph = NULL; ++ bool force_refresh = false; + struct flow_offload *flow; + struct nf_conn *ct; + u8 dir; +@@ -621,15 +674,40 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p, + flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); + ct = flow->ct; + ++ if (dir == FLOW_OFFLOAD_DIR_REPLY && ++ !test_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags)) { ++ /* Only offload reply direction after connection became ++ * assured. ++ */ ++ if (test_bit(IPS_ASSURED_BIT, &ct->status)) ++ set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags); ++ else if (test_bit(NF_FLOW_HW_ESTABLISHED, &flow->flags)) ++ /* If flow_table flow has already been updated to the ++ * established state, then don't refresh. ++ */ ++ return false; ++ force_refresh = true; ++ } ++ + if (tcph && (unlikely(tcph->fin || tcph->rst))) { + flow_offload_teardown(flow); + return false; + } + +- ctinfo = dir == FLOW_OFFLOAD_DIR_ORIGINAL ? IP_CT_ESTABLISHED : +- IP_CT_ESTABLISHED_REPLY; ++ if (dir == FLOW_OFFLOAD_DIR_ORIGINAL) ++ ctinfo = test_bit(IPS_SEEN_REPLY_BIT, &ct->status) ? ++ IP_CT_ESTABLISHED : IP_CT_NEW; ++ else ++ ctinfo = IP_CT_ESTABLISHED_REPLY; ++ ++ nf_conn_act_ct_ext_fill(skb, ct, ctinfo); ++ tcf_ct_flow_ct_ext_ifidx_update(flow); ++ flow_offload_refresh(nf_ft, flow, force_refresh); ++ if (!test_bit(IPS_ASSURED_BIT, &ct->status)) { ++ /* Process this flow in SW to allow promoting to ASSURED */ ++ return false; ++ } + +- flow_offload_refresh(nf_ft, flow); + nf_conntrack_get(&ct->ct_general); + nf_ct_set(skb, ct, ctinfo); + if (nf_ft->flags & NF_FLOWTABLE_COUNTER) +@@ -832,18 +910,23 @@ out_free: + return err; + } + +-static void tcf_ct_params_free(struct rcu_head *head) ++static void tcf_ct_params_free(struct tcf_ct_params *params) + { +- struct tcf_ct_params *params = container_of(head, +- struct tcf_ct_params, rcu); +- +- tcf_ct_flow_table_put(params); +- ++ if (params->ct_ft) ++ tcf_ct_flow_table_put(params->ct_ft); + if (params->tmpl) + nf_ct_put(params->tmpl); + kfree(params); + } + ++static void tcf_ct_params_free_rcu(struct rcu_head *head) ++{ ++ struct tcf_ct_params *params; ++ ++ params = container_of(head, struct tcf_ct_params, rcu); ++ tcf_ct_params_free(params); ++} ++ + #if IS_ENABLED(CONFIG_NF_NAT) + /* Modelled after nf_nat_ipv[46]_fn(). + * range is only used for new, uninitialized NAT state. +@@ -1121,7 +1204,7 @@ do_nat: + tcf_ct_act_set_labels(ct, p->labels, p->labels_mask); + + if (!nf_ct_is_confirmed(ct)) +- nf_conn_act_ct_ext_add(ct); ++ nf_conn_act_ct_ext_add(skb, ct, ctinfo); + + /* This will take care of sending queued events + * even if the connection is already confirmed. +@@ -1390,7 +1473,7 @@ static int tcf_ct_init(struct net *net, struct nlattr *nla, + + err = tcf_ct_flow_table_get(net, params); + if (err) +- goto cleanup_params; ++ goto cleanup; + + spin_lock_bh(&c->tcf_lock); + goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); +@@ -1401,17 +1484,15 @@ static int tcf_ct_init(struct net *net, struct nlattr *nla, + if (goto_ch) + tcf_chain_put_by_act(goto_ch); + if (params) +- call_rcu(¶ms->rcu, tcf_ct_params_free); ++ call_rcu(¶ms->rcu, tcf_ct_params_free_rcu); + + return res; + +-cleanup_params: +- if (params->tmpl) +- nf_ct_put(params->tmpl); + cleanup: + if (goto_ch) + tcf_chain_put_by_act(goto_ch); +- kfree(params); ++ if (params) ++ tcf_ct_params_free(params); + tcf_idr_release(*a, bind); + return err; + } +@@ -1423,7 +1504,7 @@ static void tcf_ct_cleanup(struct tc_action *a) + + params = rcu_dereference_protected(c->params, 1); + if (params) +- call_rcu(¶ms->rcu, tcf_ct_params_free); ++ call_rcu(¶ms->rcu, tcf_ct_params_free_rcu); + } + + static int tcf_ct_dump_key_val(struct sk_buff *skb, +diff --git a/net/sched/em_text.c b/net/sched/em_text.c +index 6f3c1fb2fb44c..f176afb70559e 100644 +--- a/net/sched/em_text.c ++++ b/net/sched/em_text.c +@@ -97,8 +97,10 @@ retry: + + static void em_text_destroy(struct tcf_ematch *m) + { +- if (EM_TEXT_PRIV(m) && EM_TEXT_PRIV(m)->config) ++ if (EM_TEXT_PRIV(m) && EM_TEXT_PRIV(m)->config) { + textsearch_destroy(EM_TEXT_PRIV(m)->config); ++ kfree(EM_TEXT_PRIV(m)); ++ } + } + + static int em_text_dump(struct sk_buff *skb, struct tcf_ematch *m) +diff --git a/net/smc/smc_diag.c b/net/smc/smc_diag.c +index 80ea7d954eceb..801044e7d1949 100644 +--- a/net/smc/smc_diag.c ++++ b/net/smc/smc_diag.c +@@ -153,8 +153,7 @@ static int __smc_diag_dump(struct sock *sk, struct sk_buff *skb, + .lnk[0].link_id = link->link_id, + }; + +- memcpy(linfo.lnk[0].ibname, +- smc->conn.lgr->lnk[0].smcibdev->ibdev->name, ++ memcpy(linfo.lnk[0].ibname, link->smcibdev->ibdev->name, + sizeof(link->smcibdev->ibdev->name)); + smc_gid_be16_convert(linfo.lnk[0].gid, link->gid); + smc_gid_be16_convert(linfo.lnk[0].peer_gid, link->peer_gid); +diff --git a/net/socket.c b/net/socket.c +index 04cba91c7cbe5..639d76f20384e 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -130,6 +130,7 @@ static ssize_t sock_sendpage(struct file *file, struct page *page, + static ssize_t sock_splice_read(struct file *file, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags); ++static void sock_splice_eof(struct file *file); + + #ifdef CONFIG_PROC_FS + static void sock_show_fdinfo(struct seq_file *m, struct file *f) +@@ -164,6 +165,7 @@ static const struct file_operations socket_file_ops = { + .sendpage = sock_sendpage, + .splice_write = generic_splice_sendpage, + .splice_read = sock_splice_read, ++ .splice_eof = sock_splice_eof, + .show_fdinfo = sock_show_fdinfo, + }; + +@@ -740,6 +742,7 @@ int sock_sendmsg(struct socket *sock, struct msghdr *msg) + { + struct sockaddr_storage *save_addr = (struct sockaddr_storage *)msg->msg_name; + struct sockaddr_storage address; ++ int save_len = msg->msg_namelen; + int ret; + + if (msg->msg_name) { +@@ -749,6 +752,7 @@ int sock_sendmsg(struct socket *sock, struct msghdr *msg) + + ret = __sock_sendmsg(sock, msg); + msg->msg_name = save_addr; ++ msg->msg_namelen = save_len; + + return ret; + } +@@ -826,7 +830,7 @@ static bool skb_is_swtx_tstamp(const struct sk_buff *skb, int false_tstamp) + + static ktime_t get_timestamp(struct sock *sk, struct sk_buff *skb, int *if_index) + { +- bool cycles = sk->sk_tsflags & SOF_TIMESTAMPING_BIND_PHC; ++ bool cycles = READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_BIND_PHC; + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); + struct net_device *orig_dev; + ktime_t hwtstamp; +@@ -878,12 +882,12 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, + int need_software_tstamp = sock_flag(sk, SOCK_RCVTSTAMP); + int new_tstamp = sock_flag(sk, SOCK_TSTAMP_NEW); + struct scm_timestamping_internal tss; +- + int empty = 1, false_tstamp = 0; + struct skb_shared_hwtstamps *shhwtstamps = + skb_hwtstamps(skb); + int if_index; + ktime_t hwtstamp; ++ u32 tsflags; + + /* Race occurred between timestamp enabling and packet + receiving. Fill in the current time for now. */ +@@ -925,11 +929,12 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, + } + + memset(&tss, 0, sizeof(tss)); +- if ((sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) && ++ tsflags = READ_ONCE(sk->sk_tsflags); ++ if ((tsflags & SOF_TIMESTAMPING_SOFTWARE) && + ktime_to_timespec64_cond(skb->tstamp, tss.ts + 0)) + empty = 0; + if (shhwtstamps && +- (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) && ++ (tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) && + !skb_is_swtx_tstamp(skb, false_tstamp)) { + if_index = 0; + if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP_NETDEV) +@@ -937,14 +942,14 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, + else + hwtstamp = shhwtstamps->hwtstamp; + +- if (sk->sk_tsflags & SOF_TIMESTAMPING_BIND_PHC) ++ if (tsflags & SOF_TIMESTAMPING_BIND_PHC) + hwtstamp = ptp_convert_timestamp(&hwtstamp, +- sk->sk_bind_phc); ++ READ_ONCE(sk->sk_bind_phc)); + + if (ktime_to_timespec64_cond(hwtstamp, tss.ts + 2)) { + empty = 0; + +- if ((sk->sk_tsflags & SOF_TIMESTAMPING_OPT_PKTINFO) && ++ if ((tsflags & SOF_TIMESTAMPING_OPT_PKTINFO) && + !skb_is_err_queue(skb)) + put_ts_pktinfo(msg, skb, if_index); + } +@@ -1088,6 +1093,14 @@ static ssize_t sock_splice_read(struct file *file, loff_t *ppos, + return sock->ops->splice_read(sock, ppos, pipe, len, flags); + } + ++static void sock_splice_eof(struct file *file) ++{ ++ struct socket *sock = file->private_data; ++ ++ if (sock->ops->splice_eof) ++ sock->ops->splice_eof(sock); ++} ++ + static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to) + { + struct file *file = iocb->ki_filp; +@@ -2128,6 +2141,7 @@ int __sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags, + msg.msg_name = (struct sockaddr *)&address; + msg.msg_namelen = addr_len; + } ++ flags &= ~MSG_INTERNAL_SENDMSG_FLAGS; + if (sock->file->f_flags & O_NONBLOCK) + flags |= MSG_DONTWAIT; + msg.msg_flags = flags; +@@ -2479,6 +2493,7 @@ static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys, + msg_sys->msg_control = ctl_buf; + msg_sys->msg_control_is_user = false; + } ++ flags &= ~MSG_INTERNAL_SENDMSG_FLAGS; + msg_sys->msg_flags = flags; + + if (sock->file->f_flags & O_NONBLOCK) +diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c +index 6dbeb80073338..be2ed7b0fe21c 100644 +--- a/net/unix/af_unix.c ++++ b/net/unix/af_unix.c +@@ -211,8 +211,6 @@ static inline bool unix_secdata_eq(struct scm_cookie *scm, struct sk_buff *skb) + } + #endif /* CONFIG_SECURITY_NETWORK */ + +-#define unix_peer(sk) (unix_sk(sk)->peer) +- + static inline int unix_our_peer(struct sock *sk, struct sock *osk) + { + return unix_peer(osk) == sk; +diff --git a/net/unix/unix_bpf.c b/net/unix/unix_bpf.c +index 2f9d8271c6ec7..7ea7c3a0d0d06 100644 +--- a/net/unix/unix_bpf.c ++++ b/net/unix/unix_bpf.c +@@ -159,12 +159,17 @@ int unix_dgram_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool re + + int unix_stream_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool restore) + { ++ struct sock *sk_pair; ++ + if (restore) { + sk->sk_write_space = psock->saved_write_space; + sock_replace_proto(sk, psock->sk_proto); + return 0; + } + ++ sk_pair = unix_peer(sk); ++ sock_hold(sk_pair); ++ psock->sk_pair = sk_pair; + unix_stream_bpf_check_needs_rebuild(psock->sk_proto); + sock_replace_proto(sk, &unix_stream_bpf_prot); + return 0; +diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c +index a88ed60dcd96a..1c8ffc5cf97f6 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -9581,6 +9581,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x84da, "HP OMEN dc0019-ur", ALC295_FIXUP_HP_OMEN), + SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360), ++ SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), +@@ -9663,6 +9664,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), ++ SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), +@@ -9707,6 +9709,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), ++ SND_PCI_QUIRK(0x103c, 0x8c96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), +@@ -9904,6 +9907,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1558, 0xc019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xc022, "Clevo NH77[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS), ++ SND_PCI_QUIRK(0x17aa, 0x3882, "Lenovo Yoga Pro 7 14APH8", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x1048, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), + SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), +diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c +index bf94838bdbefe..5c07a8ff0c9c0 100644 +--- a/sound/soc/fsl/fsl_rpmsg.c ++++ b/sound/soc/fsl/fsl_rpmsg.c +@@ -231,7 +231,7 @@ static int fsl_rpmsg_probe(struct platform_device *pdev) + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component, + &fsl_rpmsg_dai, 1); + if (ret) +- return ret; ++ goto err_pm_disable; + + rpmsg->card_pdev = platform_device_register_data(&pdev->dev, + "imx-audio-rpmsg", +@@ -241,16 +241,22 @@ static int fsl_rpmsg_probe(struct platform_device *pdev) + if (IS_ERR(rpmsg->card_pdev)) { + dev_err(&pdev->dev, "failed to register rpmsg card\n"); + ret = PTR_ERR(rpmsg->card_pdev); +- return ret; ++ goto err_pm_disable; + } + + return 0; ++ ++err_pm_disable: ++ pm_runtime_disable(&pdev->dev); ++ return ret; + } + + static int fsl_rpmsg_remove(struct platform_device *pdev) + { + struct fsl_rpmsg *rpmsg = platform_get_drvdata(pdev); + ++ pm_runtime_disable(&pdev->dev); ++ + if (rpmsg->card_pdev) + platform_device_unregister(rpmsg->card_pdev); + +diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-adda.c b/sound/soc/mediatek/mt8186/mt8186-dai-adda.c +index 094402470dc23..858b95b199dcb 100644 +--- a/sound/soc/mediatek/mt8186/mt8186-dai-adda.c ++++ b/sound/soc/mediatek/mt8186/mt8186-dai-adda.c +@@ -499,7 +499,7 @@ static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = { + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("AUD_PAD_TOP", SUPPLY_SEQ_ADDA_AUD_PAD_TOP, +- 0, 0, 0, ++ AFE_AUD_PAD_TOP, RG_RX_FIFO_ON_SFT, 0, + mtk_adda_pad_top_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY_S("ADDA_MTKAIF_CFG", SUPPLY_SEQ_ADDA_MTKAIF_CFG, +diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c +index ddc667956cf5e..8d8d848ebd58b 100644 +--- a/sound/soc/meson/g12a-toacodec.c ++++ b/sound/soc/meson/g12a-toacodec.c +@@ -71,6 +71,9 @@ static int g12a_toacodec_mux_put_enum(struct snd_kcontrol *kcontrol, + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, reg; + ++ if (ucontrol->value.enumerated.item[0] >= e->items) ++ return -EINVAL; ++ + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); + regmap_field_read(priv->field_dat_sel, ®); + +@@ -101,7 +104,7 @@ static int g12a_toacodec_mux_put_enum(struct snd_kcontrol *kcontrol, + + snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); + +- return 0; ++ return 1; + } + + static SOC_ENUM_SINGLE_DECL(g12a_toacodec_mux_enum, TOACODEC_CTRL0, +diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c +index 579a04ad4d197..154c324fdd42a 100644 +--- a/sound/soc/meson/g12a-tohdmitx.c ++++ b/sound/soc/meson/g12a-tohdmitx.c +@@ -45,6 +45,9 @@ static int g12a_tohdmitx_i2s_mux_put_enum(struct snd_kcontrol *kcontrol, + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, changed; + ++ if (ucontrol->value.enumerated.item[0] >= e->items) ++ return -EINVAL; ++ + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); + changed = snd_soc_component_test_bits(component, e->reg, + CTRL0_I2S_DAT_SEL, +@@ -93,6 +96,9 @@ static int g12a_tohdmitx_spdif_mux_put_enum(struct snd_kcontrol *kcontrol, + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, changed; + ++ if (ucontrol->value.enumerated.item[0] >= e->items) ++ return -EINVAL; ++ + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); + changed = snd_soc_component_test_bits(component, TOHDMITX_CTRL0, + CTRL0_SPDIF_SEL, +@@ -112,7 +118,7 @@ static int g12a_tohdmitx_spdif_mux_put_enum(struct snd_kcontrol *kcontrol, + + snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); + +- return 0; ++ return 1; + } + + static SOC_ENUM_SINGLE_DECL(g12a_tohdmitx_spdif_mux_enum, TOHDMITX_CTRL0, +diff --git a/tools/testing/selftests/bpf/verifier/ld_imm64.c b/tools/testing/selftests/bpf/verifier/ld_imm64.c +index f9297900cea6d..78f19c255f20b 100644 +--- a/tools/testing/selftests/bpf/verifier/ld_imm64.c ++++ b/tools/testing/selftests/bpf/verifier/ld_imm64.c +@@ -9,8 +9,8 @@ + BPF_MOV64_IMM(BPF_REG_0, 2), + BPF_EXIT_INSN(), + }, +- .errstr = "invalid BPF_LD_IMM insn", +- .errstr_unpriv = "R1 pointer comparison", ++ .errstr = "jump into the middle of ldimm64 insn 1", ++ .errstr_unpriv = "jump into the middle of ldimm64 insn 1", + .result = REJECT, + }, + { +@@ -23,8 +23,8 @@ + BPF_LD_IMM64(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, +- .errstr = "invalid BPF_LD_IMM insn", +- .errstr_unpriv = "R1 pointer comparison", ++ .errstr = "jump into the middle of ldimm64 insn 1", ++ .errstr_unpriv = "jump into the middle of ldimm64 insn 1", + .result = REJECT, + }, + { +diff --git a/tools/testing/selftests/drivers/net/bonding/bond-arp-interval-causes-panic.sh b/tools/testing/selftests/drivers/net/bonding/bond-arp-interval-causes-panic.sh +index 71c00bfafbc99..2ff58fed76e28 100755 +--- a/tools/testing/selftests/drivers/net/bonding/bond-arp-interval-causes-panic.sh ++++ b/tools/testing/selftests/drivers/net/bonding/bond-arp-interval-causes-panic.sh +@@ -33,16 +33,16 @@ ip netns add "client" + ip link set dev link1_1 netns client down name eth0 + ip netns exec client ip link add dev bond0 down type bond mode 1 \ + miimon 100 all_slaves_active 1 +-ip netns exec client ip link set dev eth0 down master bond0 ++ip netns exec client ip link set dev eth0 master bond0 + ip netns exec client ip link set dev bond0 up + ip netns exec client ip addr add ${client_ip4}/24 dev bond0 + ip netns exec client ping -c 5 $server_ip4 >/dev/null + +-ip netns exec client ip link set dev eth0 down nomaster ++ip netns exec client ip link set dev eth0 nomaster + ip netns exec client ip link set dev bond0 down + ip netns exec client ip link set dev bond0 type bond mode 0 \ + arp_interval 1000 arp_ip_target "+${server_ip4}" +-ip netns exec client ip link set dev eth0 down master bond0 ++ip netns exec client ip link set dev eth0 master bond0 + ip netns exec client ip link set dev bond0 up + ip netns exec client ping -c 5 $server_ip4 >/dev/null + +diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh +index e52d513009fb0..2107579e2939d 100755 +--- a/tools/testing/selftests/net/mptcp/mptcp_join.sh ++++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh +@@ -2167,9 +2167,9 @@ link_failure_tests() + pm_nl_set_limits $ns1 0 2 + pm_nl_add_endpoint $ns1 10.0.2.1 dev ns1eth2 flags signal + pm_nl_set_limits $ns2 1 2 +- FAILING_LINKS="1" + pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow,backup +- run_tests $ns1 $ns2 10.0.1.1 1 ++ FAILING_LINKS="1" \ ++ run_tests $ns1 $ns2 10.0.1.1 1 + chk_join_nr 2 2 2 + chk_add_nr 1 1 + chk_link_usage $ns2 ns2eth3 $cinsent 0 +@@ -2183,8 +2183,8 @@ link_failure_tests() + pm_nl_add_endpoint $ns1 10.0.2.1 dev ns1eth2 flags signal + pm_nl_set_limits $ns2 1 2 + pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow,backup +- FAILING_LINKS="1 2" +- run_tests $ns1 $ns2 10.0.1.1 1 ++ FAILING_LINKS="1 2" \ ++ run_tests $ns1 $ns2 10.0.1.1 1 + chk_join_nr 2 2 2 + chk_add_nr 1 1 + chk_stale_nr $ns2 2 4 2 +@@ -2199,8 +2199,8 @@ link_failure_tests() + pm_nl_add_endpoint $ns1 10.0.2.1 dev ns1eth2 flags signal + pm_nl_set_limits $ns2 1 3 + pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow,backup +- FAILING_LINKS="1 2" +- run_tests $ns1 $ns2 10.0.1.1 2 ++ FAILING_LINKS="1 2" \ ++ run_tests $ns1 $ns2 10.0.1.1 2 + chk_join_nr 2 2 2 + chk_add_nr 1 1 + chk_stale_nr $ns2 1 -1 2 +@@ -3041,7 +3041,7 @@ fastclose_tests() + + if reset_check_counter "fastclose server test" "MPTcpExtMPFastcloseRx"; then + run_tests $ns1 $ns2 10.0.1.1 1024 0 fastclose_server +- chk_join_nr 0 0 0 ++ chk_join_nr 0 0 0 0 0 0 1 + chk_fclose_nr 1 1 invert + chk_rst_nr 1 1 + fi +diff --git a/tools/testing/selftests/vm/memfd_secret.c b/tools/testing/selftests/vm/memfd_secret.c +index 957b9e18c7295..9b298f6a04b37 100644 +--- a/tools/testing/selftests/vm/memfd_secret.c ++++ b/tools/testing/selftests/vm/memfd_secret.c +@@ -62,6 +62,9 @@ static void test_mlock_limit(int fd) + char *mem; + + len = mlock_limit_cur; ++ if (len % page_size != 0) ++ len = (len/page_size) * page_size; ++ + mem = mmap(NULL, len, prot, mode, fd, 0); + if (mem == MAP_FAILED) { + fail("unable to mmap secret memory\n"); diff --git a/patch/kernel/odroidxu4-current/patch-6.1.72-73.patch b/patch/kernel/odroidxu4-current/patch-6.1.72-73.patch new file mode 100644 index 0000000000..feadcd413e --- /dev/null +++ b/patch/kernel/odroidxu4-current/patch-6.1.72-73.patch @@ -0,0 +1,294 @@ +diff --git a/Makefile b/Makefile +index bad3387b3251c..e4f2d019ca745 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + VERSION = 6 + PATCHLEVEL = 1 +-SUBLEVEL = 72 ++SUBLEVEL = 73 + EXTRAVERSION = + NAME = Curry Ramen + +diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c +index b3b4542e31ed5..573de0d49e172 100644 +--- a/fs/nfsd/nfsctl.c ++++ b/fs/nfsd/nfsctl.c +@@ -716,10 +716,8 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred + + err = svc_addsock(nn->nfsd_serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred); + +- if (err < 0 && !nn->nfsd_serv->sv_nrthreads && !nn->keep_active) +- nfsd_last_thread(net); +- else if (err >= 0 && +- !nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1)) ++ if (err >= 0 && ++ !nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1)) + svc_get(nn->nfsd_serv); + + nfsd_put(net); +@@ -769,9 +767,6 @@ out_close: + svc_xprt_put(xprt); + } + out_err: +- if (!nn->nfsd_serv->sv_nrthreads && !nn->keep_active) +- nfsd_last_thread(net); +- + nfsd_put(net); + return err; + } +diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h +index 53166cce7062c..09726c5b9a317 100644 +--- a/fs/nfsd/nfsd.h ++++ b/fs/nfsd/nfsd.h +@@ -97,12 +97,7 @@ int nfsd_pool_stats_open(struct inode *, struct file *); + int nfsd_pool_stats_release(struct inode *, struct file *); + void nfsd_shutdown_threads(struct net *net); + +-static inline void nfsd_put(struct net *net) +-{ +- struct nfsd_net *nn = net_generic(net, nfsd_net_id); +- +- svc_put(nn->nfsd_serv); +-} ++void nfsd_put(struct net *net); + + bool i_am_nfsd(void); + +@@ -139,7 +134,6 @@ int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change); + int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change); + void nfsd_reset_versions(struct nfsd_net *nn); + int nfsd_create_serv(struct net *net); +-void nfsd_last_thread(struct net *net); + + extern int nfsd_max_blksize; + +diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c +index 350c6c72f793f..c7695ebd28dc3 100644 +--- a/fs/nfsd/nfssvc.c ++++ b/fs/nfsd/nfssvc.c +@@ -523,14 +523,9 @@ static struct notifier_block nfsd_inet6addr_notifier = { + /* Only used under nfsd_mutex, so this atomic may be overkill: */ + static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0); + +-void nfsd_last_thread(struct net *net) ++static void nfsd_last_thread(struct svc_serv *serv, struct net *net) + { + struct nfsd_net *nn = net_generic(net, nfsd_net_id); +- struct svc_serv *serv = nn->nfsd_serv; +- +- spin_lock(&nfsd_notifier_lock); +- nn->nfsd_serv = NULL; +- spin_unlock(&nfsd_notifier_lock); + + /* check if the notifier still has clients */ + if (atomic_dec_return(&nfsd_notifier_refcount) == 0) { +@@ -540,8 +535,6 @@ void nfsd_last_thread(struct net *net) + #endif + } + +- svc_xprt_destroy_all(serv, net); +- + /* + * write_ports can create the server without actually starting + * any threads--if we get shut down before any threads are +@@ -632,8 +625,7 @@ void nfsd_shutdown_threads(struct net *net) + svc_get(serv); + /* Kill outstanding nfsd threads */ + svc_set_num_threads(serv, NULL, 0); +- nfsd_last_thread(net); +- svc_put(serv); ++ nfsd_put(net); + mutex_unlock(&nfsd_mutex); + } + +@@ -663,6 +655,9 @@ int nfsd_create_serv(struct net *net) + serv->sv_maxconn = nn->max_connections; + error = svc_bind(serv, net); + if (error < 0) { ++ /* NOT nfsd_put() as notifiers (see below) haven't ++ * been set up yet. ++ */ + svc_put(serv); + return error; + } +@@ -705,6 +700,29 @@ int nfsd_get_nrthreads(int n, int *nthreads, struct net *net) + return 0; + } + ++/* This is the callback for kref_put() below. ++ * There is no code here as the first thing to be done is ++ * call svc_shutdown_net(), but we cannot get the 'net' from ++ * the kref. So do all the work when kref_put returns true. ++ */ ++static void nfsd_noop(struct kref *ref) ++{ ++} ++ ++void nfsd_put(struct net *net) ++{ ++ struct nfsd_net *nn = net_generic(net, nfsd_net_id); ++ ++ if (kref_put(&nn->nfsd_serv->sv_refcnt, nfsd_noop)) { ++ svc_xprt_destroy_all(nn->nfsd_serv, net); ++ nfsd_last_thread(nn->nfsd_serv, net); ++ svc_destroy(&nn->nfsd_serv->sv_refcnt); ++ spin_lock(&nfsd_notifier_lock); ++ nn->nfsd_serv = NULL; ++ spin_unlock(&nfsd_notifier_lock); ++ } ++} ++ + int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) + { + int i = 0; +@@ -755,7 +773,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) + if (err) + break; + } +- svc_put(nn->nfsd_serv); ++ nfsd_put(net); + return err; + } + +@@ -770,7 +788,6 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred) + int error; + bool nfsd_up_before; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); +- struct svc_serv *serv; + + mutex_lock(&nfsd_mutex); + dprintk("nfsd: creating service\n"); +@@ -790,25 +807,22 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred) + goto out; + + nfsd_up_before = nn->nfsd_net_up; +- serv = nn->nfsd_serv; + + error = nfsd_startup_net(net, cred); + if (error) + goto out_put; +- error = svc_set_num_threads(serv, NULL, nrservs); ++ error = svc_set_num_threads(nn->nfsd_serv, NULL, nrservs); + if (error) + goto out_shutdown; +- error = serv->sv_nrthreads; +- if (error == 0) +- nfsd_last_thread(net); ++ error = nn->nfsd_serv->sv_nrthreads; + out_shutdown: + if (error < 0 && !nfsd_up_before) + nfsd_shutdown_net(net); + out_put: + /* Threads now hold service active */ + if (xchg(&nn->keep_active, 0)) +- svc_put(serv); +- svc_put(serv); ++ nfsd_put(net); ++ nfsd_put(net); + out: + mutex_unlock(&nfsd_mutex); + return error; +diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c +index 2e15b182e59fc..7286a56aebfa9 100644 +--- a/fs/smb/client/cifsfs.c ++++ b/fs/smb/client/cifsfs.c +@@ -1240,7 +1240,7 @@ static int cifs_flush_folio(struct inode *inode, loff_t pos, loff_t *_fstart, lo + int rc = 0; + + folio = filemap_get_folio(inode->i_mapping, index); +- if (IS_ERR(folio)) ++ if (!folio) + return 0; + + size = folio_size(folio); +diff --git a/include/net/dst_ops.h b/include/net/dst_ops.h +index 88ff7bb2bb9bd..632086b2f644a 100644 +--- a/include/net/dst_ops.h ++++ b/include/net/dst_ops.h +@@ -16,7 +16,7 @@ struct dst_ops { + unsigned short family; + unsigned int gc_thresh; + +- int (*gc)(struct dst_ops *ops); ++ void (*gc)(struct dst_ops *ops); + struct dst_entry * (*check)(struct dst_entry *, __u32 cookie); + unsigned int (*default_advmss)(const struct dst_entry *); + unsigned int (*mtu)(const struct dst_entry *); +diff --git a/net/core/dst.c b/net/core/dst.c +index bc9c9be4e0801..d178c564138ee 100644 +--- a/net/core/dst.c ++++ b/net/core/dst.c +@@ -82,12 +82,8 @@ void *dst_alloc(struct dst_ops *ops, struct net_device *dev, + + if (ops->gc && + !(flags & DST_NOCOUNT) && +- dst_entries_get_fast(ops) > ops->gc_thresh) { +- if (ops->gc(ops)) { +- pr_notice_ratelimited("Route cache is full: consider increasing sysctl net.ipv6.route.max_size.\n"); +- return NULL; +- } +- } ++ dst_entries_get_fast(ops) > ops->gc_thresh) ++ ops->gc(ops); + + dst = kmem_cache_alloc(ops->kmem_cachep, GFP_ATOMIC); + if (!dst) +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index 0bcdb675ba2c1..7f65dc750feb8 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -91,7 +91,7 @@ static struct dst_entry *ip6_negative_advice(struct dst_entry *); + static void ip6_dst_destroy(struct dst_entry *); + static void ip6_dst_ifdown(struct dst_entry *, + struct net_device *dev, int how); +-static int ip6_dst_gc(struct dst_ops *ops); ++static void ip6_dst_gc(struct dst_ops *ops); + + static int ip6_pkt_discard(struct sk_buff *skb); + static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); +@@ -3288,11 +3288,10 @@ out: + return dst; + } + +-static int ip6_dst_gc(struct dst_ops *ops) ++static void ip6_dst_gc(struct dst_ops *ops) + { + struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); + int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; +- int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; + int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; + int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; + unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; +@@ -3300,11 +3299,10 @@ static int ip6_dst_gc(struct dst_ops *ops) + int entries; + + entries = dst_entries_get_fast(ops); +- if (entries > rt_max_size) ++ if (entries > ops->gc_thresh) + entries = dst_entries_get_slow(ops); + +- if (time_after(rt_last_gc + rt_min_interval, jiffies) && +- entries <= rt_max_size) ++ if (time_after(rt_last_gc + rt_min_interval, jiffies)) + goto out; + + fib6_run_gc(atomic_inc_return(&net->ipv6.ip6_rt_gc_expire), net, true); +@@ -3314,7 +3312,6 @@ static int ip6_dst_gc(struct dst_ops *ops) + out: + val = atomic_read(&net->ipv6.ip6_rt_gc_expire); + atomic_set(&net->ipv6.ip6_rt_gc_expire, val - (val >> rt_elasticity)); +- return entries > rt_max_size; + } + + static int ip6_nh_lookup_table(struct net *net, struct fib6_config *cfg, +@@ -6517,7 +6514,7 @@ static int __net_init ip6_route_net_init(struct net *net) + #endif + + net->ipv6.sysctl.flush_delay = 0; +- net->ipv6.sysctl.ip6_rt_max_size = 4096; ++ net->ipv6.sysctl.ip6_rt_max_size = INT_MAX; + net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; + net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; + net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ;