From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8EF503D6CA7; Sun, 23 Aug 2026 12:46:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787489236; cv=none; b=cBwaOo0W7WHE8daOd4NcRzZ0SQ7K8QnrTYOtrndM0IrmJ7qEzGlRRIlW/8u9irVISQnfw3jQBaMRLlE37Bup9TJHf3HLpShnRjmhv2agASxRzSNQhAY1HRp4xoJewfdMMCxJCJrEWVpiqJiU8xO0Kc+NkhyGmTK4K9CQsjj2P6A= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787489236; c=relaxed/simple; bh=7LAzim2cQ8FcZOgKawJP7FB1JMuATPqWKDGBJdLzq78=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QCKmttwq+viK3a03vnIydCpVEYGac3Pco0LKf1CrKG5atbIoQbei6aUtXjljY85oO9FyGReAH+4zpX9ERgvfHgnGUE727i8E0wglAaECFkUNDVqU/4q5jVrqGvzTLByIchccEB4mqqg7b4fKVudhE2nCsqoL6EeLou4qHYF8IhI= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=RF3MZXye; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="RF3MZXye" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E77661F000E9; Sun, 23 Aug 2026 12:46:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1787489209; bh=YDLVQunI/AcxQYGTWNagbEMoIFh+usu+S1v7Oi76qJM=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=RF3MZXyec4RtHZcW8Efeiurl6x53uF8mizL0aZuiIPfo1XVAItOUjO4bTr+S4pUV6 DHgsxk8ORDpzBEwJRPOrR8aOUwzs2yF73NEV5is58Vt9cQmM7A3hlxtDbu+1ZRTJLV IYqBZWqK+jWiYaJ38kCXurbEQiGrws91h0H0nHXo= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org, akpm@linux-foundation.org, torvalds@linux-foundation.org, stable@vger.kernel.org Cc: lwn@lwn.net, jslaby@suse.cz, Greg Kroah-Hartman Subject: Re: Linux 6.12.105 Date: Sun, 23 Aug 2026 14:44:59 +0200 Message-ID: <2026082359-snout-backlight-8385@gregkh> X-Mailer: git-send-email 2.55.0 In-Reply-To: <2026082359-absently-emission-682a@gregkh> References: <2026082359-absently-emission-682a@gregkh> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit diff --git a/Makefile b/Makefile index 69eb37d9d488..3368d1fe9e6d 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 12 -SUBLEVEL = 104 +SUBLEVEL = 105 EXTRAVERSION = NAME = Baby Opossum Posse diff --git a/arch/arm64/boot/dts/nvidia/tegra194.dtsi b/arch/arm64/boot/dts/nvidia/tegra194.dtsi index c36950774785..45d0606bd544 100644 --- a/arch/arm64/boot/dts/nvidia/tegra194.dtsi +++ b/arch/arm64/boot/dts/nvidia/tegra194.dtsi @@ -3161,6 +3161,8 @@ timer { , , + ; interrupt-parent = <&gic>; always-on; diff --git a/arch/loongarch/mm/pageattr.c b/arch/loongarch/mm/pageattr.c index aca4e86d2d88..3149d405d81e 100644 --- a/arch/loongarch/mm/pageattr.c +++ b/arch/loongarch/mm/pageattr.c @@ -118,7 +118,7 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, pgp return 0; mmap_write_lock(&init_mm); - ret = walk_page_range_novma(&init_mm, start, end, &pageattr_ops, NULL, &masks); + ret = walk_kernel_page_table_range(start, end, &pageattr_ops, NULL, &masks); mmap_write_unlock(&init_mm); flush_tlb_kernel_range(start, end); diff --git a/arch/m68k/Kconfig.cpu b/arch/m68k/Kconfig.cpu index c777a129768a..cc5d559b8134 100644 --- a/arch/m68k/Kconfig.cpu +++ b/arch/m68k/Kconfig.cpu @@ -538,6 +538,10 @@ config CACHE_COPYBACK endchoice endif # HAVE_CACHE_CB +config NR_CPUS + int + default "1" + # Coldfire cores that do not have a data cache configured can do coherent DMA. config COLDFIRE_COHERENT_DMA bool diff --git a/arch/openrisc/include/asm/processor.h b/arch/openrisc/include/asm/processor.h index e05d1b59e24e..edb1e069e202 100644 --- a/arch/openrisc/include/asm/processor.h +++ b/arch/openrisc/include/asm/processor.h @@ -26,6 +26,8 @@ | SPR_SR_DCE | SPR_SR_SM) #define USER_SR (SPR_SR_DME | SPR_SR_IME | SPR_SR_ICE \ | SPR_SR_DCE | SPR_SR_IEE | SPR_SR_TEE) +/* SR bits user space may change via sigreturn, the rest stay kernel owned */ +#define SPR_SR_USER_MASK (SPR_SR_F | SPR_SR_CY | SPR_SR_OV) /* * User space process size. This is hardcoded into a few places, diff --git a/arch/openrisc/kernel/dma.c b/arch/openrisc/kernel/dma.c index b3edbb33b621..0b4c20cc76cb 100644 --- a/arch/openrisc/kernel/dma.c +++ b/arch/openrisc/kernel/dma.c @@ -75,7 +75,7 @@ void *arch_dma_set_uncached(void *cpu_addr, size_t size) * them and setting the cache-inhibit bit. */ mmap_write_lock(&init_mm); - error = walk_page_range_novma(&init_mm, va, va + size, + error = walk_kernel_page_table_range(va, va + size, &set_nocache_walk_ops, NULL, NULL); mmap_write_unlock(&init_mm); @@ -90,7 +90,7 @@ void arch_dma_clear_uncached(void *cpu_addr, size_t size) mmap_write_lock(&init_mm); /* walk_page_range shouldn't be able to fail here */ - WARN_ON(walk_page_range_novma(&init_mm, va, va + size, + WARN_ON(walk_kernel_page_table_range(va, va + size, &clear_nocache_walk_ops, NULL, NULL)); mmap_write_unlock(&init_mm); } diff --git a/arch/openrisc/kernel/signal.c b/arch/openrisc/kernel/signal.c index c7ab42e2cb7a..2bf026a05ef4 100644 --- a/arch/openrisc/kernel/signal.c +++ b/arch/openrisc/kernel/signal.c @@ -74,6 +74,7 @@ static long save_fp_state(struct sigcontext __user *sc) static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { + unsigned long old_sr = regs->sr; int err = 0; /* Always make any pending restarted system calls return -EINTR */ @@ -89,8 +90,8 @@ static int restore_sigcontext(struct pt_regs *regs, err |= __copy_from_user(®s->sr, &sc->regs.sr, sizeof(unsigned long)); err |= restore_fp_state(sc); - /* make sure the SM-bit is cleared so user-mode cannot fool us */ - regs->sr &= ~SPR_SR_SM; + /* keep the privileged SR bits kernel owned, restore only user flags */ + regs->sr = (old_sr & ~SPR_SR_USER_MASK) | (regs->sr & SPR_SR_USER_MASK); regs->orig_gpr11 = -1; /* Avoid syscall restart checks */ diff --git a/arch/powerpc/platforms/pseries/lparcfg.c b/arch/powerpc/platforms/pseries/lparcfg.c index 62da20f9700a..3e5d5ebfa3c1 100644 --- a/arch/powerpc/platforms/pseries/lparcfg.c +++ b/arch/powerpc/platforms/pseries/lparcfg.c @@ -687,7 +687,7 @@ static ssize_t lparcfg_write(struct file *file, const char __user * buf, if (!firmware_has_feature(FW_FEATURE_SPLPAR)) return -EINVAL; - if (count > sizeof(kbuf)) + if (count == 0 || count > sizeof(kbuf)) return -EINVAL; if (copy_from_user(kbuf, buf, count)) diff --git a/arch/powerpc/platforms/pseries/pci.c b/arch/powerpc/platforms/pseries/pci.c index 6dbc73eb2ca2..82dc1ad52ac9 100644 --- a/arch/powerpc/platforms/pseries/pci.c +++ b/arch/powerpc/platforms/pseries/pci.c @@ -132,7 +132,7 @@ static int pseries_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs) /* First integer stores max config */ max_config_vfs = of_read_number(&max_vfs[0], 1); - if (max_config_vfs < num_vfs && num_vfs > MAX_VFS_FOR_MAP_PE) { + if (max_config_vfs < num_vfs || num_vfs > MAX_VFS_FOR_MAP_PE) { dev_err(&pdev->dev, "Num VFs %x > %x Configurable VFs\n", num_vfs, (num_vfs > MAX_VFS_FOR_MAP_PE) ? diff --git a/arch/riscv/mm/pageattr.c b/arch/riscv/mm/pageattr.c index 271d01a5ba4d..a35edf662d7e 100644 --- a/arch/riscv/mm/pageattr.c +++ b/arch/riscv/mm/pageattr.c @@ -299,7 +299,7 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, if (ret) goto unlock; - ret = walk_page_range_novma(&init_mm, lm_start, lm_end, + ret = walk_kernel_page_table_range(lm_start, lm_end, &pageattr_ops, NULL, &masks); if (ret) goto unlock; @@ -317,13 +317,13 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, if (ret) goto unlock; - ret = walk_page_range_novma(&init_mm, lm_start, lm_end, + ret = walk_kernel_page_table_range(lm_start, lm_end, &pageattr_ops, NULL, &masks); if (ret) goto unlock; } - ret = walk_page_range_novma(&init_mm, start, end, &pageattr_ops, NULL, + ret = walk_kernel_page_table_range(start, end, &pageattr_ops, NULL, &masks); unlock: @@ -335,7 +335,7 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, */ flush_tlb_all(); #else - ret = walk_page_range_novma(&init_mm, start, end, &pageattr_ops, NULL, + ret = walk_kernel_page_table_range(start, end, &pageattr_ops, NULL, &masks); mmap_write_unlock(&init_mm); diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c index dac4564e1d7c..4bb317a54f2e 100644 --- a/arch/x86/kernel/cpu/mce/amd.c +++ b/arch/x86/kernel/cpu/mce/amd.c @@ -674,6 +674,10 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c) u32 low = 0, high = 0, address = 0; int offset = -1; + mce_flags.overflow_recov = cpu_feature_enabled(X86_FEATURE_OVERFLOW_RECOV); + mce_flags.succor = cpu_feature_enabled(X86_FEATURE_SUCCOR); + mce_flags.smca = cpu_feature_enabled(X86_FEATURE_SMCA); + mce_flags.amd_threshold = 1; for (bank = 0; bank < this_cpu_read(mce_num_banks); ++bank) { if (mce_flags.smca) diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index d8f3d9af8acf..01a17e729a80 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -1836,8 +1836,6 @@ static void __mcheck_cpu_init_generic(void) bitmap_fill(all_banks, MAX_NR_BANKS); machine_check_poll(MCP_UC | MCP_QUEUE_LOG | m_fl, &all_banks); - cr4_set_bits(X86_CR4_MCE); - rdmsrl(MSR_IA32_MCG_CAP, cap); if (cap & MCG_CTL_P) wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff); @@ -2011,19 +2009,6 @@ static int __mcheck_cpu_ancient_init(struct cpuinfo_x86 *c) return 0; } -/* - * Init basic CPU features needed for early decoding of MCEs. - */ -static void __mcheck_cpu_init_early(struct cpuinfo_x86 *c) -{ - if (c->x86_vendor == X86_VENDOR_AMD || c->x86_vendor == X86_VENDOR_HYGON) { - mce_flags.overflow_recov = !!cpu_has(c, X86_FEATURE_OVERFLOW_RECOV); - mce_flags.succor = !!cpu_has(c, X86_FEATURE_SUCCOR); - mce_flags.smca = !!cpu_has(c, X86_FEATURE_SMCA); - mce_flags.amd_threshold = 1; - } -} - static void mce_centaur_feature_init(struct cpuinfo_x86 *c) { struct mca_config *cfg = &mca_cfg; @@ -2266,12 +2251,12 @@ void mcheck_cpu_init(struct cpuinfo_x86 *c) mca_cfg.initialized = 1; - __mcheck_cpu_init_early(c); + __mcheck_cpu_setup_timer(); __mcheck_cpu_init_generic(); __mcheck_cpu_init_vendor(c); __mcheck_cpu_init_clear_banks(); __mcheck_cpu_check_banks(); - __mcheck_cpu_setup_timer(); + cr4_set_bits(X86_CR4_MCE); } /* @@ -2439,6 +2424,7 @@ static void mce_syscore_resume(void) __mcheck_cpu_init_generic(); __mcheck_cpu_init_vendor(raw_cpu_ptr(&cpu_info)); __mcheck_cpu_init_clear_banks(); + cr4_set_bits(X86_CR4_MCE); } static struct syscore_ops mce_syscore_ops = { @@ -2458,6 +2444,7 @@ static void mce_cpu_restart(void *data) __mcheck_cpu_init_generic(); __mcheck_cpu_init_clear_banks(); __mcheck_cpu_init_timer(); + cr4_set_bits(X86_CR4_MCE); } /* Reinit MCEs after user configuration changes */ diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index bd0344bacaf1..a6971cd236be 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -82,6 +82,8 @@ static u64 sev_supported_vmsa_features; static u8 sev_enc_bit; static DECLARE_RWSEM(sev_deactivate_lock); static DEFINE_MUTEX(sev_bitmap_lock); +/* Protects kvm_sev_info's enc_context_owner, mirror_vms and mirror_entry. */ +static DEFINE_MUTEX(sev_mirror_lock); unsigned int max_sev_asid; static unsigned int min_sev_asid; static unsigned long sev_me_mask; @@ -1989,7 +1991,6 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm) dst->asid = src->asid; dst->handle = src->handle; dst->pages_locked = src->pages_locked; - dst->enc_context_owner = src->enc_context_owner; dst->es_active = src->es_active; dst->vmsa_features = src->vmsa_features; @@ -1997,11 +1998,12 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm) src->active = false; src->handle = 0; src->pages_locked = 0; - src->enc_context_owner = NULL; src->es_active = false; list_cut_before(&dst->regions_list, &src->regions_list, &src->regions_list); + mutex_lock(&sev_mirror_lock); + /* * If this VM has mirrors, "transfer" each mirror's refcount of the * source to the destination (this KVM). The caller holds a reference @@ -2018,13 +2020,15 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm) * If this VM is a mirror, remove the old mirror from the owners list * and add the new mirror to the list. */ - if (is_mirroring_enc_context(dst_kvm)) { - struct kvm_sev_info *owner_sev_info = - &to_kvm_svm(dst->enc_context_owner)->sev_info; + if (is_mirroring_enc_context(src_kvm)) { + struct kvm_sev_info *owner_sev_info = to_kvm_sev_info(src->enc_context_owner); + dst->enc_context_owner = src->enc_context_owner; + src->enc_context_owner = NULL; list_del(&src->mirror_entry); list_add_tail(&dst->mirror_entry, &owner_sev_info->mirror_vms); } + mutex_unlock(&sev_mirror_lock); kvm_for_each_vcpu(i, dst_vcpu, dst_kvm) { dst_svm = to_svm(dst_vcpu); @@ -2843,13 +2847,16 @@ int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd) * The mirror kvm holds an enc_context_owner ref so its asid can't * disappear until we're done with it */ - source_sev = &to_kvm_svm(source_kvm)->sev_info; - kvm_get_kvm(source_kvm); - mirror_sev = &to_kvm_svm(kvm)->sev_info; - list_add_tail(&mirror_sev->mirror_entry, &source_sev->mirror_vms); + source_sev = to_kvm_sev_info(source_kvm); + mirror_sev = to_kvm_sev_info(kvm); /* Set enc_context_owner and copy its encryption context over */ + mutex_lock(&sev_mirror_lock); + kvm_get_kvm(source_kvm); + list_add_tail(&mirror_sev->mirror_entry, &source_sev->mirror_vms); mirror_sev->enc_context_owner = source_kvm; + mutex_unlock(&sev_mirror_lock); + mirror_sev->active = true; mirror_sev->asid = source_sev->asid; mirror_sev->fd = source_sev->fd; @@ -2909,11 +2916,19 @@ void sev_vm_destroy(struct kvm *kvm) /* If this is a mirror_kvm release the enc_context_owner and skip sev cleanup */ if (is_mirroring_enc_context(kvm)) { - struct kvm *owner_kvm = sev->enc_context_owner; + struct kvm *owner_kvm; - mutex_lock(&owner_kvm->lock); + mutex_lock(&sev_mirror_lock); + owner_kvm = sev->enc_context_owner; list_del(&sev->mirror_entry); - mutex_unlock(&owner_kvm->lock); + sev->enc_context_owner = NULL; + + /* + * The reference to owner_kvm cannot move after sev_mirror_lock is + * released. Release it before kvm_put_kvm() so that owner_kvm is + * never destroyed inside sev_mirror_lock. + */ + mutex_unlock(&sev_mirror_lock); kvm_put_kvm(owner_kvm); return; } @@ -2992,6 +3007,16 @@ void __init sev_hardware_setup(void) WARN_ON_ONCE(!boot_cpu_has(X86_FEATURE_FLUSHBYASID))) goto out; + /* + * The kernel's initcall infrastructure lacks the ability to express + * dependencies between initcalls, whereas the modules infrastructure + * automatically handles dependencies via symbol loading. Ensure the + * PSP SEV driver is initialized before proceeding if KVM is built-in, + * as the dependency isn't handled by the initcall infrastructure. + */ + if (IS_BUILTIN(CONFIG_KVM_AMD) && sev_module_init()) + goto out; + /* Retrieve SEV CPUID information */ cpuid(0x8000001f, &eax, &ebx, &ecx, &edx); diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index eca9378172d1..c34cd7f1408f 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -103,6 +103,7 @@ struct kvm_sev_info { u64 ap_jump_table; /* SEV-ES AP Jump Table address */ u64 vmsa_features; u16 ghcb_version; /* Highest guest GHCB protocol version allowed */ + /* The three fields below are protected by sev_mirror_lock */ struct kvm *enc_context_owner; /* Owner of copied encryption context */ struct list_head mirror_vms; /* List of VMs mirroring */ struct list_head mirror_entry; /* Use as a list entry of mirrors */ diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 2b61aa28b5ed..9e7c9e3edb5b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -12982,6 +12982,19 @@ EXPORT_SYMBOL_GPL(__x86_set_memory_region); void kvm_arch_pre_destroy_vm(struct kvm *kvm) { + /* + * Cancel (and flush) the I/O APIC's delayed EOI handling before vCPUs + * are destroyed, as processing the EOI broadcast will inject another + * IRQ if the line is asserted, i.e. will try to deliver an IRQ to the + * target vCPU(s). + * + * Do NOT free the in-kernel PIC or I/O APIC here (but do make sure to + * flush any background work), as KVM expects interrupt routing + * structures to be valid until vCPUs are destroyed. + */ + if (kvm->arch.vioapic) + cancel_delayed_work_sync(&kvm->arch.vioapic->eoi_inject); + kvm_mmu_pre_destroy_vm(kvm); } diff --git a/block/genhd.c b/block/genhd.c index 31e1539e6e6a..a909817bf1c8 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1192,14 +1192,18 @@ static void disk_release(struct device *dev) /* * To undo the all initialization from blk_mq_init_allocated_queue in * case of a probe failure where add_disk is never called we have to - * call blk_mq_exit_queue here. We can't do this for the more common - * teardown case (yet) as the tagset can be gone by the time the disk - * is released once it was added. + * call blk_mq_exit_queue here, after stopping the timer and work items + * that I/O issued before add_disk may have left pending. We can't do + * this for the more common teardown case (yet) as the tagset can be + * gone by the time the disk is released once it was added. */ if (queue_is_mq(disk->queue) && test_bit(GD_OWNS_QUEUE, &disk->state) && - !test_bit(GD_ADDED, &disk->state)) + !test_bit(GD_ADDED, &disk->state)) { + blk_sync_queue(disk->queue); + blk_mq_cancel_work_sync(disk->queue); blk_mq_exit_queue(disk->queue); + } blkcg_exit_disk(disk); diff --git a/crypto/ccm.c b/crypto/ccm.c index 36f0acec32e1..88c932faf6ce 100644 --- a/crypto/ccm.c +++ b/crypto/ccm.c @@ -751,7 +751,7 @@ static int crypto_rfc4309_create(struct crypto_template *tmpl, inst->alg.ivsize = 8; inst->alg.chunksize = crypto_aead_alg_chunksize(alg); - inst->alg.maxauthsize = 16; + inst->alg.maxauthsize = crypto_aead_alg_maxauthsize(alg); inst->alg.base.cra_ctxsize = sizeof(struct crypto_rfc4309_ctx); diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index a1331274ffcb..32ec1cfd648e 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -650,29 +650,12 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, set_host_byte(scmd, DID_OK); ata_qc_for_each_raw(ap, qc, i) { - if (qc->scsicmd != scmd) - continue; - if ((qc->flags & ATA_QCFLAG_ACTIVE) || - qc == qc->dev->link->deferred_qc) + if (qc->scsicmd == scmd && + qc->flags & ATA_QCFLAG_ACTIVE) break; } - if (i < ATA_MAX_QUEUE && qc == qc->dev->link->deferred_qc) { - /* - * This is a deferred command that timed out while - * waiting for the command queue to drain. Since the qc - * is not active yet (deferred_qc is still set, so the - * deferred qc work has not issued the command yet), - * simply signal the timeout by finishing the SCSI - * command and clear the deferred qc to prevent the - * deferred qc work from issuing this qc. - */ - WARN_ON_ONCE(qc->flags & ATA_QCFLAG_ACTIVE); - qc->dev->link->deferred_qc = NULL; - cancel_work(&qc->dev->link->deferred_qc_work); - set_host_byte(scmd, DID_TIME_OUT); - scsi_eh_finish_cmd(scmd, &ap->eh_done_q); - } else if (i < ATA_MAX_QUEUE) { + if (i < ATA_MAX_QUEUE) { /* the scmd has an associated qc */ if (!(qc->flags & ATA_QCFLAG_EH)) { /* which hasn't failed yet, timeout */ @@ -946,10 +929,10 @@ static void ata_eh_set_pending(struct ata_port *ap, int fastdrain) ap->pflags |= ATA_PFLAG_EH_PENDING; /* - * If we have a deferred qc, requeue it so that it is retried once EH - * completes. + * If we have deferred QCs, requeue them so that the SCSI EH task can + * run. */ - ata_scsi_requeue_deferred_qc(ap); + ata_scsi_requeue_deferred_qc(ap, NULL); if (!fastdrain) return; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 710d1d66afb1..13c65b031bc0 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1694,30 +1694,81 @@ void ata_scsi_deferred_qc_work(struct work_struct *work) spin_unlock_irqrestore(ap->lock, flags); } -void ata_scsi_requeue_deferred_qc(struct ata_port *ap) +enum scsi_timeout_action ata_scsi_requeue_deferred_qc(struct ata_port *ap, + struct scsi_cmnd *timedout_scmd) { + enum scsi_timeout_action action = SCSI_EH_NOT_HANDLED; + struct ata_queued_cmd *qc; struct ata_link *link; + u32 host_byte; lockdep_assert_held(ap->lock); /* - * If we have a deferred qc when a reset occurs or NCQ commands fail, - * do not try to be smart about what to do with this deferred command - * and simply requeue it by completing it with DID_REQUEUE. + * If we have deferred QCs when a reset, a timeout or an NCQ command + * fails, do not try to be smart about what to do with the deferred + * commands and simply terminate them and let the SCSI layer decide + * what to do. */ ata_for_each_link(link, ap, PMP_FIRST) { - struct ata_queued_cmd *qc = link->deferred_qc; - struct scsi_cmnd *scmd; - - if (qc) { - scmd = qc->scsicmd; - link->deferred_qc = NULL; - cancel_work(&link->deferred_qc_work); - ata_qc_free(qc); - scmd->result = (DID_REQUEUE << 16); - scsi_done(scmd); + qc = link->deferred_qc; + if (!qc) + continue; + + /* + * Clear the deferred QC so that the deferred work does not try + * to issue it. + */ + link->deferred_qc = NULL; + cancel_work(&link->deferred_qc_work); + + /* + * We are going to complete some scsi command, either with + * DID_TIME_OUT if the command timed out while waiting for being + * issued, or with DID_REQUEUE if another command timed out or + * we had a failed command. However, the block layer may re-issue + * these commands immediately, keeping the scsi host busy and + * thus preventing the SCSI EH task from running. + * So schedule EH on the port to prevent accepting new commands + * until everything is sorted out with the error or timeout that + * got us here in the first place. Note that we set EH pending + * on the port before calling ata_port_schedule_eh() so that we + * do not reenter this function from ata_eh_set_pending() with + * timedout_scmd being NULL and erroneously retry deferred QCs + * that have timed out on other links. + */ + if (!ata_port_eh_scheduled(ap)) { + ap->pflags |= ATA_PFLAG_EH_PENDING; + ata_port_schedule_eh(ap); } + + /* + * If we are being called from scsi_timeout(), then we have a + * non-NULL timedout_scmd. If the timed out command is for a + * deferred QC, terminate that deferred QC with DID_TIME_OUT and + * requeue all other deferred QCs. In this case we need to + * return SCSI_EH_DONE, because the timed out command was + * handled. + * If the timed out command is not for a deferred QC, we need to + * requeue all deferred QCs, and return SCSI_EH_NOT_HANDLED so + * that the timed out command gets added to the EH work queue + * with scsi_eh_scmd_add(), for later handling with libata EH + * ata_scsi_cmd_error_handler(). + * If timedout_scmd is NULL, we simply need to requeue all + * deferred QCs and the return value does not matter as we were + * not called from scsi_timeout(). + */ + if (timedout_scmd && qc->scsicmd == timedout_scmd) { + host_byte = DID_TIME_OUT; + action = SCSI_EH_DONE; + } else { + host_byte = DID_REQUEUE; + } + qc->scsicmd->result = host_byte << 16; + ata_qc_done(qc); } + + return action; } static void ata_scsi_schedule_deferred_qc(struct ata_link *link) @@ -1736,13 +1787,41 @@ static void ata_scsi_schedule_deferred_qc(struct ata_link *link) return; if (ata_port_eh_scheduled(ap)) { - ata_scsi_requeue_deferred_qc(ap); + ata_scsi_requeue_deferred_qc(ap, NULL); return; } if (!ap->ops->qc_defer(qc)) queue_work(system_highpri_wq, &link->deferred_qc_work); } +static enum scsi_timeout_action +ata_scsi_retry_deferred_qc(struct ata_port *ap, struct scsi_cmnd *scmd) +{ + enum scsi_timeout_action action; + unsigned long flags; + + spin_lock_irqsave(ap->lock, flags); + action = ata_scsi_requeue_deferred_qc(ap, scmd); + spin_unlock_irqrestore(ap->lock, flags); + + return action; +} + +enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *scmd) +{ + struct ata_port *ap = ata_shost_to_port(scmd->device->host); + + /* + * ata_scsi_cmd_error_handler() takes care of commands that timed out + * while executing. However, if we have deferred QCs while a timeout + * triggers, we must requeue these commands for retry so that we do not + * unnecessarily delay starting the SCSI EH task until these deferred + * commands also time out. + */ + return ata_scsi_retry_deferred_qc(ap, scmd); +} +EXPORT_SYMBOL_GPL(ata_scsi_eh_timed_out); + static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) { struct ata_link *link = qc->dev->link; @@ -2945,7 +3024,13 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc) if (qc->cdb[0] == ALLOW_MEDIUM_REMOVAL && qc->dev->sdev) qc->dev->sdev->locked = 0; - qc->scsicmd->result = SAM_STAT_CHECK_CONDITION; + if (!cmd->result) + cmd->result = SAM_STAT_CHECK_CONDITION; + ata_qc_done(qc); + return; + } + + if (cmd->result) { ata_qc_done(qc); return; } diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 1415190c1854..4b3e246227e6 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -176,7 +176,8 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct queue_limits *lim, struct ata_device *dev); int __ata_scsi_queuecmd(struct scsi_cmnd *scmd, struct ata_device *dev); void ata_scsi_deferred_qc_work(struct work_struct *work); -void ata_scsi_requeue_deferred_qc(struct ata_port *ap); +enum scsi_timeout_action ata_scsi_requeue_deferred_qc(struct ata_port *ap, + struct scsi_cmnd *scmd); /* libata-eh.c */ extern unsigned int ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd); diff --git a/drivers/crypto/ccp/sp-dev.c b/drivers/crypto/ccp/sp-dev.c index ccbe009ad6e5..f204aa5df96e 100644 --- a/drivers/crypto/ccp/sp-dev.c +++ b/drivers/crypto/ccp/sp-dev.c @@ -19,6 +19,7 @@ #include #include +#include "sev-dev.h" #include "ccp-dev.h" #include "sp-dev.h" @@ -265,8 +266,12 @@ struct sp_device *sp_get_psp_master_device(void) static int __init sp_mod_init(void) { #ifdef CONFIG_X86 + static bool initialized; int ret; + if (initialized) + return 0; + ret = sp_pci_init(); if (ret) return ret; @@ -275,6 +280,8 @@ static int __init sp_mod_init(void) psp_pci_init(); #endif + initialized = true; + return 0; #endif @@ -291,6 +298,13 @@ static int __init sp_mod_init(void) return -ENODEV; } +#if IS_BUILTIN(CONFIG_KVM_AMD) && IS_ENABLED(CONFIG_KVM_AMD_SEV) +int __init sev_module_init(void) +{ + return sp_mod_init(); +} +#endif + static void __exit sp_mod_exit(void) { #ifdef CONFIG_X86 diff --git a/drivers/crypto/qce/core.c b/drivers/crypto/qce/core.c index 3ec8297ed3ff..fc6f1cf69deb 100644 --- a/drivers/crypto/qce/core.c +++ b/drivers/crypto/qce/core.c @@ -58,7 +58,7 @@ static int qce_register_algs(struct qce_device *qce) ret = ops->register_algs(qce); if (ret) { for (j = i - 1; j >= 0; j--) - ops->unregister_algs(qce); + qce_ops[j]->unregister_algs(qce); return ret; } } diff --git a/drivers/crypto/starfive/jh7110-aes.c b/drivers/crypto/starfive/jh7110-aes.c index 04f2f97ce238..4a38fb2ccdb1 100644 --- a/drivers/crypto/starfive/jh7110-aes.c +++ b/drivers/crypto/starfive/jh7110-aes.c @@ -681,7 +681,7 @@ static int starfive_aes_aead_do_one_req(struct crypto_engine *engine, void *areq if (cryp->total_in) sg_zero_buffer(rctx->in_sg, sg_nents(rctx->in_sg), - sg_dma_len(rctx->in_sg) - cryp->total_in, + rctx->in_sg->length - cryp->total_in, cryp->total_in); ctx->rctx = rctx; diff --git a/drivers/crypto/tegra/tegra-se-aes.c b/drivers/crypto/tegra/tegra-se-aes.c index 9094c03e991f..0fd1d7035899 100644 --- a/drivers/crypto/tegra/tegra-se-aes.c +++ b/drivers/crypto/tegra/tegra-se-aes.c @@ -45,7 +45,6 @@ struct tegra_aes_reqctx { struct tegra_aead_ctx { struct tegra_se *se; - unsigned int authsize; u32 alg; u32 key_id; u32 keylen; @@ -1290,7 +1289,7 @@ static int tegra_gcm_do_one_req(struct crypto_engine *engine, void *areq) if (rctx->encrypt) rctx->cryptlen = req->cryptlen; else - rctx->cryptlen = req->cryptlen - ctx->authsize; + rctx->cryptlen = req->cryptlen - rctx->authsize; memcpy(rctx->iv, req->iv, GCM_AES_IV_SIZE); rctx->iv[3] = (1 << 24); @@ -1394,8 +1393,6 @@ static int tegra_aead_cra_init(struct crypto_aead *tfm) static int tegra_ccm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) { - struct tegra_aead_ctx *ctx = crypto_aead_ctx(tfm); - switch (authsize) { case 4: case 6: @@ -1404,28 +1401,15 @@ static int tegra_ccm_setauthsize(struct crypto_aead *tfm, unsigned int authsize case 12: case 14: case 16: - break; + return 0; default: return -EINVAL; } - - ctx->authsize = authsize; - - return 0; } static int tegra_gcm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) { - struct tegra_aead_ctx *ctx = crypto_aead_ctx(tfm); - int ret; - - ret = crypto_gcm_check_authsize(authsize); - if (ret) - return ret; - - ctx->authsize = authsize; - - return 0; + return crypto_gcm_check_authsize(authsize); } static void tegra_aead_cra_exit(struct crypto_aead *tfm) diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c index 9c526aeadeba..4380b150a6cc 100644 --- a/drivers/gpio/gpio-sloppy-logic-analyzer.c +++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c @@ -301,7 +301,7 @@ static int gpio_la_poll_probe(struct platform_device *pdev) debugfs_create_ulong("delay_ns_acquisition", 0400, priv->debug_dir, &priv->acq_delay); debugfs_create_file_unsafe("buf_size", 0600, priv->debug_dir, priv, &fops_buf_size); debugfs_create_file_unsafe("capture", 0200, priv->debug_dir, priv, &fops_capture); - debugfs_create_file_unsafe("trigger", 0200, priv->debug_dir, priv, &fops_trigger); + debugfs_create_file("trigger", 0200, priv->debug_dir, priv, &fops_trigger); return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 43308c631037..1b4e21986e5f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -41,6 +41,26 @@ #include "amdgpu_gem.h" #include "amdgpu_ras.h" +/* + * Maximum IB length (dwords) for rings whose emit_ib packet format + * documents a 20-bit size field. + */ +#define AMDGPU_GFX_SDMA_IB_PACKET_SIZE_MAX_DW 0xFFFFF +#define AMDGPU_MM_IB_PACKET_SIZE_MAX_DW 0x7FFFF0 + +static u32 amdgpu_cs_ib_packet_size_max_dw(enum amdgpu_ring_type type) +{ + switch (type) { + case AMDGPU_RING_TYPE_GFX: + case AMDGPU_RING_TYPE_COMPUTE: + case AMDGPU_RING_TYPE_SDMA: + case AMDGPU_RING_TYPE_VPE: + return AMDGPU_GFX_SDMA_IB_PACKET_SIZE_MAX_DW; + default: + return AMDGPU_MM_IB_PACKET_SIZE_MAX_DW; + } +} + static int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, struct amdgpu_device *adev, struct drm_file *filp, @@ -253,6 +273,10 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p, if (size < sizeof(struct drm_amdgpu_cs_chunk_fence)) goto free_partial_kdata; + /* Only a single user fence is allowed to simplify handling. */ + if (p->uf_bo) + goto free_partial_kdata; + ret = amdgpu_cs_p1_user_fence(p, p->chunks[i].kdata, &uf_offset); if (ret) @@ -352,7 +376,6 @@ static int amdgpu_cs_p2_ib(struct amdgpu_cs_parser *p, job = p->jobs[r]; ring = amdgpu_job_ring(job); - ib = &job->ibs[job->num_ibs++]; /* MM engine doesn't support user fences */ if (p->uf_bo && ring->funcs->no_user_fence) @@ -371,6 +394,12 @@ static int amdgpu_cs_p2_ib(struct amdgpu_cs_parser *p, return -EINVAL; } + if (chunk_ib->ib_bytes / 4 > + amdgpu_cs_ib_packet_size_max_dw(ring->funcs->type)) + return -EINVAL; + + ib = &job->ibs[job->num_ibs++]; + if (chunk_ib->flags & AMDGPU_IB_FLAG_PREAMBLE) job->preamble_status |= AMDGPU_PREAMBLE_IB_PRESENT; @@ -1109,7 +1138,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) * We can't use gang submit on with reserved VMIDs when the VM changes * can't be invalidated by more than one engine at the same time. */ - if (p->gang_size > 1 && !p->adev->vm_manager.concurrent_flush) { + if (p->gang_size > 1 && !adev->vm_manager.concurrent_flush) { for (i = 0; i < p->gang_size; ++i) { struct drm_sched_entity *entity = p->entities[i]; struct drm_gpu_scheduler *sched = entity->rq->sched; @@ -1184,19 +1213,6 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) job->vm_pd_addr = amdgpu_gmc_pd_addr(vm->root.bo); } - if (adev->debug_vm) { - /* Invalidate all BOs to test for userspace bugs */ - amdgpu_bo_list_for_each_entry(e, p->bo_list) { - struct amdgpu_bo *bo = e->bo; - - /* ignore duplicates */ - if (!bo) - continue; - - amdgpu_vm_bo_invalidate(adev, bo, false); - } - } - return 0; } @@ -1383,6 +1399,8 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, /* Cleanup the parser structure */ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) { + struct amdgpu_device *adev = parser->adev; + struct amdgpu_bo_list_entry *e; unsigned int i; amdgpu_sync_free(&parser->sync); @@ -1398,8 +1416,21 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) if (parser->ctx) amdgpu_ctx_put(parser->ctx); - if (parser->bo_list) + if (parser->bo_list) { + if (adev->debug_vm) { + /* Invalidate all BOs to test for userspace bugs */ + amdgpu_bo_list_for_each_entry(e, parser->bo_list) { + struct amdgpu_bo *bo = e->bo; + + /* ignore duplicates */ + if (!bo) + continue; + + amdgpu_vm_bo_invalidate(bo, false); + } + } amdgpu_bo_list_put(parser->bo_list); + } for (i = 0; i < parser->nchunks; i++) kvfree(parser->chunks[i].kdata); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 2ec994a6dea6..20a13db7cd88 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1806,6 +1806,31 @@ static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev) #endif } +/* + * Some dGPUs expose their display endpoint below an internal PCIe switch. + * Use the switch upstream port to query the host-facing link. + */ +static struct pci_dev *amdgpu_device_get_aspm_pdev(struct amdgpu_device *adev) +{ + struct pci_dev *swds, *swus; + + swds = pci_upstream_bridge(adev->pdev); + if (!swds || + (swds->vendor != PCI_VENDOR_ID_ATI && + swds->vendor != PCI_VENDOR_ID_AMD) || + pci_pcie_type(swds) != PCI_EXP_TYPE_DOWNSTREAM) + return adev->pdev; + + swus = pci_upstream_bridge(swds); + if (!swus || + (swus->vendor != PCI_VENDOR_ID_ATI && + swus->vendor != PCI_VENDOR_ID_AMD) || + pci_pcie_type(swus) != PCI_EXP_TYPE_UPSTREAM) + return adev->pdev; + + return swus; +} + /** * amdgpu_device_should_use_aspm - check if the device should program ASPM * @@ -1818,6 +1843,9 @@ static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev) */ bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev) { + struct pci_dev *aspm_pdev, *parent; + bool enabled; + switch (amdgpu_aspm) { case -1: break; @@ -1832,7 +1860,27 @@ bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev) return false; if (amdgpu_device_aspm_support_quirk(adev)) return false; - return pcie_aspm_enabled(adev->pdev); + + /* + * pcie_aspm_enabled() checks the link between its argument and + * the immediate upstream bridge. Use SWUS for dGPUs with an + * internal switch so that this is the host-facing link. + */ + aspm_pdev = amdgpu_device_get_aspm_pdev(adev); + parent = pci_upstream_bridge(aspm_pdev); + if (!parent) { + dev_dbg(adev->dev, "ASPM: no upstream PCIe link for %s\n", + pci_name(aspm_pdev)); + return false; + } + + enabled = pcie_aspm_enabled(aspm_pdev); + /* Report the exact link used for the automatic ASPM decision. */ + dev_dbg(adev->dev, "ASPM: link %s <-> %s is %s\n", + pci_name(parent), pci_name(aspm_pdev), + enabled ? "enabled" : "disabled"); + + return enabled; } /* if we get transitioned to only one device, take VGA back */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c index 951ab989d668..0b576481f231 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c @@ -382,7 +382,7 @@ amdgpu_dma_buf_move_notify(struct dma_buf_attachment *attach) /* FIXME: This should be after the "if", but needs a fix to make sure * DMABuf imports are initialized in the right VM list. */ - amdgpu_vm_bo_invalidate(adev, bo, false); + amdgpu_vm_bo_invalidate(bo, false); if (!bo->tbo.resource || bo->tbo.resource->mem_type == TTM_PL_SYSTEM) return; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 51875f00084c..339cdb942948 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -304,6 +304,25 @@ const struct drm_gem_object_funcs amdgpu_gem_object_funcs = { .vm_ops = &amdgpu_gem_vm_ops, }; +static bool amdgpu_gem_are_domains_valid(u32 domains) +{ + u32 normal = AMDGPU_GEM_DOMAIN_CPU | + AMDGPU_GEM_DOMAIN_GTT | + AMDGPU_GEM_DOMAIN_VRAM; + /* Treat all non CPU/GTT/VRAM domains as special domains. */ + u32 special = AMDGPU_GEM_DOMAIN_MASK & ~normal; + u32 normal_mask = domains & normal; + u32 special_mask = domains & special; + + if (!special_mask) + return true; + + if (normal_mask) + return false; + + return !(special_mask & (special_mask - 1)); +} + /* * GEM ioctls. */ @@ -340,6 +359,8 @@ int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data, /* reject invalid gem domains */ if (args->in.domains & ~AMDGPU_GEM_DOMAIN_MASK) return -EINVAL; + if (!amdgpu_gem_are_domains_valid(args->in.domains)) + return -EINVAL; if (!amdgpu_is_tmz(adev) && (flags & AMDGPU_GEM_CREATE_ENCRYPTED)) { DRM_NOTE_ONCE("Cannot allocate secure buffer since TMZ is disabled\n"); @@ -840,7 +861,6 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { - struct amdgpu_device *adev = drm_to_adev(dev); struct drm_amdgpu_gem_op *args = data; struct drm_gem_object *gobj; struct amdgpu_vm_bo_base *base; @@ -900,7 +920,7 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, robj->allowed_domains |= AMDGPU_GEM_DOMAIN_GTT; if (robj->flags & AMDGPU_GEM_CREATE_VM_ALWAYS_VALID) - amdgpu_vm_bo_invalidate(adev, robj, true); + amdgpu_vm_bo_invalidate(robj, true); amdgpu_bo_unreserve(robj); break; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 1db8ca26f1e1..08f7927c1048 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -1152,7 +1152,6 @@ void amdgpu_bo_move_notify(struct ttm_buffer_object *bo, bool evict, struct ttm_resource *new_mem) { - struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev); struct ttm_resource *old_mem = bo->resource; struct amdgpu_bo *abo; @@ -1160,7 +1159,7 @@ void amdgpu_bo_move_notify(struct ttm_buffer_object *bo, return; abo = ttm_to_amdgpu_bo(bo); - amdgpu_vm_bo_invalidate(adev, abo, evict); + amdgpu_vm_bo_invalidate(abo, evict); amdgpu_bo_kunmap(abo); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index e161af6276b9..60c5e8803f60 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2066,8 +2066,6 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) */ void amdgpu_ttm_fini(struct amdgpu_device *adev) { - int idx; - if (!adev->mman.initialized) return; @@ -2090,13 +2088,9 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev) amdgpu_ttm_fw_reserve_vram_fini(adev); amdgpu_ttm_drv_reserve_vram_fini(adev); - if (drm_dev_enter(adev_to_drm(adev), &idx)) { - - if (adev->mman.aper_base_kaddr) - iounmap(adev->mman.aper_base_kaddr); + if (adev->mman.aper_base_kaddr) { + iounmap(adev->mman.aper_base_kaddr); adev->mman.aper_base_kaddr = NULL; - - drm_dev_exit(idx); } amdgpu_vram_mgr_fini(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 549a56b7a876..fc6b0650ee7a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -645,17 +645,15 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, unsigned int height = msg[7]; unsigned int dpb_size = msg[9]; unsigned int pitch = msg[28]; - unsigned int level = msg[57]; unsigned int width_in_mb = width / 16; unsigned int height_in_mb = ALIGN(height / 16, 2); - unsigned int fs_in_mb = width_in_mb * height_in_mb; unsigned int image_size, tmp, min_dpb_size, num_dpb_buffer; unsigned int min_ctx_size = ~0; - /* Reject invalid dimensions to prevent division by zero */ - if (width < 16 || height < 16) { + /* Reject invalid dimensions */ + if (width < 16 || height < 16 || width > 4096 || height > 4096) { dev_WARN_ONCE(adev->dev, 1, "Invalid UVD decoding dimensions (%dx%d)!\n", width, height); @@ -668,35 +666,9 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, switch (stream_type) { case 0: /* H264 */ - switch (level) { - case 30: - num_dpb_buffer = 8100 / fs_in_mb; - break; - case 31: - num_dpb_buffer = 18000 / fs_in_mb; - break; - case 32: - num_dpb_buffer = 20480 / fs_in_mb; - break; - case 41: - num_dpb_buffer = 32768 / fs_in_mb; - break; - case 42: - num_dpb_buffer = 34816 / fs_in_mb; - break; - case 50: - num_dpb_buffer = 110400 / fs_in_mb; - break; - case 51: - num_dpb_buffer = 184320 / fs_in_mb; - break; - default: - num_dpb_buffer = 184320 / fs_in_mb; - break; - } - num_dpb_buffer++; + num_dpb_buffer = ((msg[61] >> 16) & 0xff) + 1; if (num_dpb_buffer > 17) - num_dpb_buffer = 17; + return -EINVAL; /* reference picture buffer */ min_dpb_size = image_size * num_dpb_buffer; @@ -746,35 +718,9 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, break; case 7: /* H264 Perf */ - switch (level) { - case 30: - num_dpb_buffer = 8100 / fs_in_mb; - break; - case 31: - num_dpb_buffer = 18000 / fs_in_mb; - break; - case 32: - num_dpb_buffer = 20480 / fs_in_mb; - break; - case 41: - num_dpb_buffer = 32768 / fs_in_mb; - break; - case 42: - num_dpb_buffer = 34816 / fs_in_mb; - break; - case 50: - num_dpb_buffer = 110400 / fs_in_mb; - break; - case 51: - num_dpb_buffer = 184320 / fs_in_mb; - break; - default: - num_dpb_buffer = 184320 / fs_in_mb; - break; - } - num_dpb_buffer++; + num_dpb_buffer = ((msg[61] >> 16) & 0xff) + 1; if (num_dpb_buffer > 17) - num_dpb_buffer = 17; + return -EINVAL; /* reference picture buffer */ min_dpb_size = image_size * num_dpb_buffer; @@ -802,6 +748,9 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, image_size = ALIGN(image_size, 256); num_dpb_buffer = (le32_to_cpu(msg[59]) & 0xff) + 2; + if (num_dpb_buffer > 17) + return -EINVAL; + min_dpb_size = image_size * num_dpb_buffer; min_ctx_size = ((width + 255) / 16) * ((height + 255) / 16) * 16 * num_dpb_buffer + 52 * 1024; @@ -812,7 +761,7 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, return -EINVAL; } - if (width > pitch) { + if (width > pitch || pitch > 4096) { DRM_ERROR("Invalid UVD decoding target pitch!\n"); return -EINVAL; } @@ -824,7 +773,7 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, } buf_sizes[0x1] = dpb_size; - buf_sizes[0x2] = image_size; + buf_sizes[0x2] = (pitch * height) * 3 / 2; buf_sizes[0x4] = min_ctx_size; /* store image width to adjust nb memory pstate */ adev->uvd.decode_image_width = width; @@ -971,15 +920,16 @@ static int amdgpu_uvd_cs_pass2(struct amdgpu_uvd_cs_ctx *ctx) ctx->buf_sizes[cmd]); return -EINVAL; } + } else if (cmd == 0x204 || cmd == 0x206) { + unsigned int min_size = ctx->buf_sizes[cmd == 0x204 ? 5 : 4]; - } else if (cmd == 0x206) { - if ((end - start) < ctx->buf_sizes[4]) { + if ((end - start) < min_size) { DRM_ERROR("buffer (%d) to small (%d / %d)!\n", cmd, (unsigned int)(end - start), - ctx->buf_sizes[4]); + min_size); return -EINVAL; } - } else if ((cmd != 0x100) && (cmd != 0x204)) { + } else if ((cmd != 0x100)) { DRM_ERROR("invalid UVD command %X!\n", cmd); return -EINVAL; } @@ -1109,11 +1059,12 @@ int amdgpu_uvd_ring_parse_cs(struct amdgpu_cs_parser *parser, { struct amdgpu_uvd_cs_ctx ctx = {}; unsigned int buf_sizes[] = { - [0x00000000] = 2048, + [0x00000000] = 3556, [0x00000001] = 0xFFFFFFFF, [0x00000002] = 0xFFFFFFFF, [0x00000003] = 2048, [0x00000004] = 0xFFFFFFFF, + [0x00000005] = 992, }; int r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 8864e43a165e..ad3b71bc0b23 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2139,14 +2139,12 @@ bool amdgpu_vm_evictable(struct amdgpu_bo *bo) /** * amdgpu_vm_bo_invalidate - mark the bo as invalid * - * @adev: amdgpu_device pointer * @bo: amdgpu buffer object * @evicted: is the BO evicted * * Mark @bo as invalid. */ -void amdgpu_vm_bo_invalidate(struct amdgpu_device *adev, - struct amdgpu_bo *bo, bool evicted) +void amdgpu_vm_bo_invalidate(struct amdgpu_bo *bo, bool evicted) { struct amdgpu_vm_bo_base *bo_base; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index 095209a2cb33..9a951e1bfee1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -511,8 +511,7 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va, bool clear); bool amdgpu_vm_evictable(struct amdgpu_bo *bo); -void amdgpu_vm_bo_invalidate(struct amdgpu_device *adev, - struct amdgpu_bo *bo, bool evicted); +void amdgpu_vm_bo_invalidate(struct amdgpu_bo *bo, bool evicted); uint64_t amdgpu_vm_map_gart(const dma_addr_t *pages_addr, uint64_t addr); struct amdgpu_bo_va *amdgpu_vm_bo_find(struct amdgpu_vm *vm, struct amdgpu_bo *bo); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index 3a6d6ee76918..4d115538a956 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -1692,6 +1692,11 @@ static void gfx_v12_0_constants_init(struct amdgpu_device *adev) gfx_v12_0_get_tcc_info(adev); adev->gfx.config.pa_sc_tile_steering_override = 0; + /* Set whether texture coordinate truncation is conformant. */ + tmp = RREG32_SOC15(GC, 0, regTA_CNTL2); + adev->gfx.config.ta_cntl2_truncate_coord_mode = + REG_GET_FIELD(tmp, TA_CNTL2, TRUNCATE_COORD_MODE); + /* XXX SH_MEM regs */ /* where to put LDS, scratch, GPUVM in FSA64 space */ mutex_lock(&adev->srbm_mutex); diff --git a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c index 39919e0892c1..ba9fdb8f5209 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c +++ b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c @@ -315,7 +315,6 @@ static u32 nbif_v6_3_1_get_rom_offset(struct amdgpu_device *adev) static void nbif_v6_3_1_program_ltr(struct amdgpu_device *adev) { uint32_t def, data; - u16 devctl2; def = RREG32_SOC15(NBIO, 0, regRCC_EP_DEV0_0_EP_PCIE_TX_LTR_CNTL); data = 0x35EB; @@ -329,15 +328,8 @@ static void nbif_v6_3_1_program_ltr(struct amdgpu_device *adev) if (def != data) WREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_BIF_STRAP2, data); - pcie_capability_read_word(adev->pdev, PCI_EXP_DEVCTL2, &devctl2); - - if (adev->pdev->ltr_path == (devctl2 & PCI_EXP_DEVCTL2_LTR_EN)) - return; - - if (adev->pdev->ltr_path) - pcie_capability_set_word(adev->pdev, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_LTR_EN); - else - pcie_capability_clear_word(adev->pdev, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_LTR_EN); + pcie_capability_set_word(adev->pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_LTR_EN); } #endif @@ -345,7 +337,7 @@ static void nbif_v6_3_1_program_aspm(struct amdgpu_device *adev) { #ifdef CONFIG_PCIEASPM uint32_t def, data; - u16 devctl2, ltr; + u16 ltr; def = data = RREG32_SOC15(PCIE, 0, regPCIE_LC_CNTL); data &= ~PCIE_LC_CNTL__LC_L1_INACTIVITY_MASK; @@ -375,11 +367,8 @@ static void nbif_v6_3_1_program_aspm(struct amdgpu_device *adev) if (def != data) WREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_BIF_STRAP5, data); - pcie_capability_read_word(adev->pdev, PCI_EXP_DEVCTL2, &devctl2); - data = def = devctl2; - data &= ~PCI_EXP_DEVCTL2_LTR_EN; - if (def != data) - pcie_capability_set_word(adev->pdev, PCI_EXP_DEVCTL2, (u16)data); + pcie_capability_clear_word(adev->pdev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_LTR_EN); ltr = pci_find_ext_capability(adev->pdev, PCI_EXT_CAP_ID_LTR); @@ -387,15 +376,13 @@ static void nbif_v6_3_1_program_aspm(struct amdgpu_device *adev) pci_write_config_dword(adev->pdev, ltr + PCI_LTR_MAX_SNOOP_LAT, 0x10011001); } -#if 0 - /* regPSWUSP0_PCIE_LC_CNTL2 should be replace by PCIE_LC_CNTL2 or someone else ? */ - def = data = RREG32_SOC15(NBIO, 0, regPSWUSP0_PCIE_LC_CNTL2); - data |= PSWUSP0_PCIE_LC_CNTL2__LC_ALLOW_PDWN_IN_L1_MASK | - PSWUSP0_PCIE_LC_CNTL2__LC_ALLOW_PDWN_IN_L23_MASK; - data &= ~PSWUSP0_PCIE_LC_CNTL2__LC_RCV_L0_TO_RCV_L0S_DIS_MASK; + def = data = RREG32_SOC15(PCIE, 0, regPCIE_LC_CNTL2); + data |= PCIE_LC_CNTL2__LC_ALLOW_PDWN_IN_L1_MASK | + PCIE_LC_CNTL2__LC_ALLOW_PDWN_IN_L23_MASK; + data &= ~PCIE_LC_CNTL2__LC_RCV_L0_TO_RCV_L0S_DIS_MASK; if (def != data) - WREG32_SOC15(NBIO, 0, regPSWUSP0_PCIE_LC_CNTL2, data); -#endif + WREG32_SOC15(PCIE, 0, regPCIE_LC_CNTL2, data); + def = data = RREG32_SOC15(PCIE, 0, regPCIE_LC_CNTL4); data |= PCIE_LC_CNTL4__LC_L1_POWERDOWN_MASK; if (def != data) @@ -406,7 +393,12 @@ static void nbif_v6_3_1_program_aspm(struct amdgpu_device *adev) if (def != data) WREG32_SOC15(PCIE, 0, regPCIE_LC_RXRECOVER_RXSTANDBY_CNTL, data); - nbif_v6_3_1_program_ltr(adev); + /* + * Do not enable endpoint LTR unless the Root Complex and every + * upstream switch support it. + */ + if (adev->pdev->ltr_path) + nbif_v6_3_1_program_ltr(adev); def = data = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_BIF_STRAP3); data |= 0x5DE0 << RCC_STRAP0_RCC_BIF_STRAP3__STRAP_VLINK_ASPM_IDLE_TIMER__SHIFT; diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c index 31ca855a950a..ff0b7438ac76 100644 --- a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c @@ -871,6 +871,23 @@ static void vce_v3_0_ring_emit_ib(struct amdgpu_ring *ring, amdgpu_ring_write(ring, ib->length_dw); } +static void vce_v3_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, + u64 seq, unsigned flags) +{ + WARN_ON(flags & AMDGPU_FENCE_FLAG_64BIT); + + amdgpu_ring_write(ring, VCE_CMD_FENCE); + amdgpu_ring_write(ring, addr); + amdgpu_ring_write(ring, upper_32_bits(addr)); + amdgpu_ring_write(ring, seq); + amdgpu_ring_write(ring, VCE_CMD_TRAP); +} + +static void vce_v3_0_ring_insert_end(struct amdgpu_ring *ring) +{ + amdgpu_ring_write(ring, VCE_CMD_END); +} + static void vce_v3_0_emit_vm_flush(struct amdgpu_ring *ring, unsigned int vmid, uint64_t pd_addr) { @@ -880,7 +897,6 @@ static void vce_v3_0_emit_vm_flush(struct amdgpu_ring *ring, amdgpu_ring_write(ring, VCE_CMD_FLUSH_TLB); amdgpu_ring_write(ring, vmid); - amdgpu_ring_write(ring, VCE_CMD_END); } static void vce_v3_0_emit_pipeline_sync(struct amdgpu_ring *ring) @@ -952,17 +968,19 @@ static const struct amdgpu_ring_funcs vce_v3_0_ring_vm_funcs = { .set_wptr = vce_v3_0_ring_set_wptr, .patch_cs_in_place = amdgpu_vce_ring_parse_cs_vm, .emit_frame_size = - 6 + /* vce_v3_0_emit_vm_flush */ + 5 + /* vce_v3_0_emit_vm_flush */ 4 + /* vce_v3_0_emit_pipeline_sync */ - 6 + 6, /* amdgpu_vce_ring_emit_fence x2 vm fence */ + 5 + 5 + /* vce_v3_0_ring_emit_fence x2 vm fence */ + 1, /* vce_v3_0_ring_insert_end */ .emit_ib_size = 5, /* vce_v3_0_ring_emit_ib */ .emit_ib = vce_v3_0_ring_emit_ib, .emit_vm_flush = vce_v3_0_emit_vm_flush, .emit_pipeline_sync = vce_v3_0_emit_pipeline_sync, - .emit_fence = amdgpu_vce_ring_emit_fence, + .emit_fence = vce_v3_0_ring_emit_fence, .test_ring = amdgpu_vce_ring_test_ring, .test_ib = amdgpu_vce_ring_test_ib, .insert_nop = amdgpu_ring_insert_nop, + .insert_end = vce_v3_0_ring_insert_end, .pad_ib = amdgpu_ring_generic_pad_ib, .begin_use = amdgpu_vce_ring_begin_use, .end_use = amdgpu_vce_ring_end_use, diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index d62b00314682..5dcb0fb2f66a 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -50,7 +50,8 @@ enum dc_color_space_type { COLOR_SPACE_RGB_LIMITED_TYPE, COLOR_SPACE_YCBCR601_TYPE, COLOR_SPACE_YCBCR709_TYPE, - COLOR_SPACE_YCBCR2020_TYPE, + COLOR_SPACE_YCBCR2020_LIMITED_TYPE, + COLOR_SPACE_YCBCR2020_FULL_TYPE, COLOR_SPACE_YCBCR601_LIMITED_TYPE, COLOR_SPACE_YCBCR709_LIMITED_TYPE, COLOR_SPACE_YCBCR709_BLACK_TYPE, @@ -102,9 +103,15 @@ static const struct out_csc_color_matrix_type output_csc_matrix[] = { { 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3, 0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} }, - { COLOR_SPACE_YCBCR2020_TYPE, + /* Corrected. Not included in the TODO above. */ + { COLOR_SPACE_YCBCR2020_LIMITED_TYPE, + { 0x0E04, 0xF31D, 0xFEDF, 0x1004, + 0x0733, 0x1294, 0x01A0, 0x0201, + 0xFC16, 0xF5E6, 0x0E04, 0x1004} }, + /* Corrected. Not included in the TODO above. */ + { COLOR_SPACE_YCBCR2020_FULL_TYPE, { 0x1000, 0xF149, 0xFEB7, 0x1004, - 0x0868, 0x15B2, 0x01E6, 0x201, + 0x0868, 0x15B2, 0x01E6, 0, 0xFB88, 0xF478, 0x1000, 0x1004} }, { COLOR_SPACE_YCBCR709_BLACK_TYPE, { 0x0000, 0x0000, 0x0000, 0x1000, @@ -171,14 +178,14 @@ static bool is_ycbcr709_type( return ret; } -static bool is_ycbcr2020_type( - enum dc_color_space color_space) +static bool is_ycbcr2020_limited_type(enum dc_color_space color_space) { - bool ret = false; + return color_space == COLOR_SPACE_2020_YCBCR_LIMITED; +} - if (color_space == COLOR_SPACE_2020_YCBCR_LIMITED || color_space == COLOR_SPACE_2020_YCBCR_FULL) - ret = true; - return ret; +static bool is_ycbcr2020_full_type(enum dc_color_space color_space) +{ + return color_space == COLOR_SPACE_2020_YCBCR_FULL; } static bool is_ycbcr709_limited_type( @@ -207,8 +214,10 @@ static enum dc_color_space_type get_color_space_type(enum dc_color_space color_s type = COLOR_SPACE_YCBCR601_LIMITED_TYPE; else if (is_ycbcr709_limited_type(color_space)) type = COLOR_SPACE_YCBCR709_LIMITED_TYPE; - else if (is_ycbcr2020_type(color_space)) - type = COLOR_SPACE_YCBCR2020_TYPE; + else if (is_ycbcr2020_limited_type(color_space)) + type = COLOR_SPACE_YCBCR2020_LIMITED_TYPE; + else if (is_ycbcr2020_full_type(color_space)) + type = COLOR_SPACE_YCBCR2020_FULL_TYPE; else if (color_space == COLOR_SPACE_YCBCR709) type = COLOR_SPACE_YCBCR709_BLACK_TYPE; else if (color_space == COLOR_SPACE_YCBCR709_BLACK) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c index 23b35393bf42..351cc84e146b 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c @@ -115,10 +115,11 @@ static const struct out_csc_color_matrix global_color_matrix[] = { { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, { COLOR_SPACE_2020_RGB_LIMITEDRANGE, { 0x1B67, 0, 0, 0x201, 0, 0x1B67, 0, 0x201, 0, 0, 0x1B67, 0x201} }, -{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, - 0x15B2, 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} }, +/* COLOR_SPACE_2020_YCBCR_* values corrected. Not included in the TODO above. */ +{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x0E04, 0xF31D, 0xFEDF, 0x1004, 0x0733, + 0x1294, 0x01A0, 0x201, 0xFC16, 0xF5E6, 0x0E04, 0x1004} }, { COLOR_SPACE_2020_YCBCR_FULL, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2, - 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} } + 0x01E6, 0, 0xFB88, 0xF478, 0x1000, 0x1004} } }; static bool setup_scaling_configuration( diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c index f7b2be02333c..870ae2e3c1b1 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_opp_csc_v.c @@ -93,10 +93,11 @@ static const struct out_csc_color_matrix global_color_matrix[] = { { 0x2000, 0, 0, 0, 0, 0x2000, 0, 0, 0, 0, 0x2000, 0} }, { COLOR_SPACE_2020_RGB_LIMITEDRANGE, { 0x1B67, 0, 0, 0x201, 0, 0x1B67, 0, 0x201, 0, 0, 0x1B67, 0x201} }, -{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, - 0x15B2, 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} }, +/* COLOR_SPACE_2020_YCBCR_* values corrected. Not included in the TODO above. */ +{ COLOR_SPACE_2020_YCBCR_LIMITED, { 0x0E04, 0xF31D, 0xFEDF, 0x1004, 0x0733, + 0x1294, 0x01A0, 0x201, 0xFC16, 0xF5E6, 0x0E04, 0x1004} }, { COLOR_SPACE_2020_YCBCR_FULL, { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2, - 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} } + 0x01E6, 0, 0xFB88, 0xF478, 0x1000, 0x1004} } }; enum csc_color_mode { diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index 9dc82f4d7c93..17c04963a792 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -1059,17 +1059,28 @@ int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, return ret; } -int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table) +int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, + size_t size) { const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + char *pptable = NULL; int ret = 0; - if (!pp_funcs->get_pp_table) - return 0; + if ((!table && size) || (table && !size)) + return -EINVAL; + + if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled) + return -EOPNOTSUPP; mutex_lock(&adev->pm.mutex); ret = pp_funcs->get_pp_table(adev->powerplay.pp_handle, - table); + &pptable); + if (ret > 0 && !pptable) { + ret = -EINVAL; + } else if (ret > 0 && table) { + ret = min_t(size_t, ret, size); + memcpy(table, pptable, ret); + } mutex_unlock(&adev->pm.mutex); return ret; @@ -1318,17 +1329,23 @@ int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, return ret; } -int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table) +ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf, + size_t size) { const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - int ret = 0; + void *table; + ssize_t ret; if (!pp_funcs->get_gpu_metrics) return 0; mutex_lock(&adev->pm.mutex); ret = pp_funcs->get_gpu_metrics(adev->powerplay.pp_handle, - table); + &table); + if (ret > 0) { + ret = min_t(ssize_t, ret, size); + memcpy(buf, table, ret); + } mutex_unlock(&adev->pm.mutex); return ret; @@ -1569,7 +1586,10 @@ int amdgpu_dpm_set_pp_table(struct amdgpu_device *adev, const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; int ret = 0; - if (!pp_funcs->set_pp_table) + if (!buf || !size) + return -EINVAL; + + if (amdgpu_sriov_vf(adev) || !pp_funcs->set_pp_table || adev->scpm_enabled) return -EOPNOTSUPP; mutex_lock(&adev->pm.mutex); diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 2252c054c7dd..1e6818a859c5 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -538,7 +538,6 @@ static ssize_t amdgpu_get_pp_table(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); - char *table = NULL; int size, ret; if (amdgpu_in_reset(adev)) @@ -552,7 +551,7 @@ static ssize_t amdgpu_get_pp_table(struct device *dev, return ret; } - size = amdgpu_dpm_get_pp_table(adev, &table); + size = amdgpu_dpm_get_pp_table(adev, buf, PAGE_SIZE - 1); pm_runtime_mark_last_busy(ddev->dev); pm_runtime_put_autosuspend(ddev->dev); @@ -560,11 +559,6 @@ static ssize_t amdgpu_get_pp_table(struct device *dev, if (size <= 0) return size; - if (size >= PAGE_SIZE) - size = PAGE_SIZE - 1; - - memcpy(buf, table, size); - return size; } @@ -1889,7 +1883,6 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); - void *gpu_metrics; ssize_t size = 0; int ret; @@ -1904,15 +1897,10 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev, return ret; } - size = amdgpu_dpm_get_gpu_metrics(adev, &gpu_metrics); + size = amdgpu_dpm_get_gpu_metrics(adev, buf, PAGE_SIZE - 1); if (size <= 0) goto out; - if (size >= PAGE_SIZE) - size = PAGE_SIZE - 1; - - memcpy(buf, gpu_metrics, size); - out: pm_runtime_mark_last_busy(ddev->dev); pm_runtime_put_autosuspend(ddev->dev); @@ -2408,7 +2396,7 @@ static struct amdgpu_device_attr amdgpu_device_attrs[] = { AMDGPU_DEVICE_ATTR_RO(pp_num_states, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF), AMDGPU_DEVICE_ATTR_RO(pp_cur_state, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF), AMDGPU_DEVICE_ATTR_RW(pp_force_state, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF), - AMDGPU_DEVICE_ATTR_RW(pp_table, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF), + AMDGPU_DEVICE_ATTR_RW(pp_table, ATTR_FLAG_BASIC), AMDGPU_DEVICE_ATTR_RW(pp_dpm_sclk, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF, .attr_update = pp_dpm_clk_default_attr_update), AMDGPU_DEVICE_ATTR_RW(pp_dpm_mclk, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF, @@ -2535,6 +2523,14 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_ if (amdgpu_dpm_get_apu_thermal_limit(adev, &limit) == -EOPNOTSUPP) *states = ATTR_STATE_UNSUPPORTED; + } else if (DEVICE_ATTR_IS(pp_table)) { + int ret; + + ret = amdgpu_dpm_get_pp_table(adev, NULL, 0); + if (ret <= 0) + *states = ATTR_STATE_UNSUPPORTED; + else + *states = ATTR_STATE_SUPPORTED; } switch (gc_ver) { diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h index f5bf41f21c41..4d88c0a9f453 100644 --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h @@ -483,7 +483,8 @@ int amdgpu_dpm_get_pp_num_states(struct amdgpu_device *adev, int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, enum amd_pp_task task_id, enum amd_pm_state_type *user_state); -int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table); +int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, + size_t size); int amdgpu_dpm_set_fine_grain_clk_vol(struct amdgpu_device *adev, uint32_t type, long *input, @@ -513,7 +514,8 @@ int amdgpu_dpm_get_power_profile_mode(struct amdgpu_device *adev, char *buf); int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, long *input, uint32_t size); -int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table); +ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf, + size_t size); /** * @get_pm_metrics: Get one snapshot of power management metrics from PMFW. The diff --git a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c index 78721bce42fd..32839355a184 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c +++ b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c @@ -650,9 +650,12 @@ static int pp_dpm_get_pp_table(void *handle, char **table) { struct pp_hwmgr *hwmgr = handle; - if (!hwmgr || !hwmgr->pm_en || !hwmgr->soft_pp_table) + if (!hwmgr || !hwmgr->pm_en || !table) return -EINVAL; + if (!hwmgr->soft_pp_table) + return -EOPNOTSUPP; + *table = (char *)hwmgr->soft_pp_table; return hwmgr->soft_pp_table_size; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c index a6683a8cebf2..bc5d3758fbf6 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c @@ -580,7 +580,7 @@ static int smu_sys_get_pp_table(void *handle, return -EOPNOTSUPP; if (!smu_table->power_play_table && !smu_table->hardcode_pptable) - return -EINVAL; + return -EOPNOTSUPP; if (smu_table->hardcode_pptable) *table = smu_table->hardcode_pptable; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index 9ac694c4f1f7..b79a2b66d30d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -102,6 +102,12 @@ MODULE_FIRMWARE("amdgpu/smu_13_0_14.bin"); #define MCA_BANK_IPID(_ip, _hwid, _type) \ [AMDGPU_MCA_IP_##_ip] = { .hwid = _hwid, .mcatype = _type, } +static inline bool smu_v13_0_6_is_unified_metrics(struct smu_context *smu) +{ + return (smu->adev->flags & AMD_IS_APU) && + smu->smc_fw_version <= 0x4556900; +} + struct mca_bank_ipid { enum amdgpu_mca_ip ip; uint16_t hwid; @@ -253,7 +259,7 @@ struct PPTable_t { #define SMUQ10_TO_UINT(x) ((x) >> 10) #define SMUQ10_FRAC(x) ((x) & 0x3ff) #define SMUQ10_ROUND(x) ((SMUQ10_TO_UINT(x)) + ((SMUQ10_FRAC(x)) >= 0x200)) -#define GET_METRIC_FIELD(field) ((adev->flags & AMD_IS_APU) ?\ +#define GET_METRIC_FIELD(field, flag) ((flag) ?\ (metrics_a->field) : (metrics_x->field)) struct smu_v13_0_6_dpm_map { @@ -584,7 +590,7 @@ static int smu_v13_0_6_setup_driver_pptable(struct smu_context *smu) MetricsTableA_t *metrics_a = (MetricsTableA_t *)smu_table->metrics_table; struct PPTable_t *pptable = (struct PPTable_t *)smu_table->driver_pptable; - struct amdgpu_device *adev = smu->adev; + bool flag = smu_v13_0_6_is_unified_metrics(smu); int ret, i, retry = 100; uint32_t table_version; @@ -596,7 +602,7 @@ static int smu_v13_0_6_setup_driver_pptable(struct smu_context *smu) return ret; /* Ensure that metrics have been updated */ - if (GET_METRIC_FIELD(AccumulationCounter)) + if (GET_METRIC_FIELD(AccumulationCounter, flag)) break; usleep_range(1000, 1100); @@ -613,29 +619,29 @@ static int smu_v13_0_6_setup_driver_pptable(struct smu_context *smu) table_version; pptable->MaxSocketPowerLimit = - SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketPowerLimit)); + SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketPowerLimit, flag)); pptable->MaxGfxclkFrequency = - SMUQ10_ROUND(GET_METRIC_FIELD(MaxGfxclkFrequency)); + SMUQ10_ROUND(GET_METRIC_FIELD(MaxGfxclkFrequency, flag)); pptable->MinGfxclkFrequency = - SMUQ10_ROUND(GET_METRIC_FIELD(MinGfxclkFrequency)); + SMUQ10_ROUND(GET_METRIC_FIELD(MinGfxclkFrequency, flag)); for (i = 0; i < 4; ++i) { pptable->FclkFrequencyTable[i] = - SMUQ10_ROUND(GET_METRIC_FIELD(FclkFrequencyTable)[i]); + SMUQ10_ROUND(GET_METRIC_FIELD(FclkFrequencyTable, flag)[i]); pptable->UclkFrequencyTable[i] = - SMUQ10_ROUND(GET_METRIC_FIELD(UclkFrequencyTable)[i]); + SMUQ10_ROUND(GET_METRIC_FIELD(UclkFrequencyTable, flag)[i]); pptable->SocclkFrequencyTable[i] = SMUQ10_ROUND( - GET_METRIC_FIELD(SocclkFrequencyTable)[i]); + GET_METRIC_FIELD(SocclkFrequencyTable, flag)[i]); pptable->VclkFrequencyTable[i] = - SMUQ10_ROUND(GET_METRIC_FIELD(VclkFrequencyTable)[i]); + SMUQ10_ROUND(GET_METRIC_FIELD(VclkFrequencyTable, flag)[i]); pptable->DclkFrequencyTable[i] = - SMUQ10_ROUND(GET_METRIC_FIELD(DclkFrequencyTable)[i]); + SMUQ10_ROUND(GET_METRIC_FIELD(DclkFrequencyTable, flag)[i]); pptable->LclkFrequencyTable[i] = - SMUQ10_ROUND(GET_METRIC_FIELD(LclkFrequencyTable)[i]); + SMUQ10_ROUND(GET_METRIC_FIELD(LclkFrequencyTable, flag)[i]); } /* use AID0 serial number by default */ - pptable->PublicSerialNumber_AID = GET_METRIC_FIELD(PublicSerialNumber_AID)[0]; + pptable->PublicSerialNumber_AID = GET_METRIC_FIELD(PublicSerialNumber_AID, flag)[0]; pptable->Init = true; } @@ -958,6 +964,7 @@ static int smu_v13_0_6_get_smu_metrics_data(struct smu_context *smu, struct smu_table_context *smu_table = &smu->smu_table; MetricsTableX_t *metrics_x = (MetricsTableX_t *)smu_table->metrics_table; MetricsTableA_t *metrics_a = (MetricsTableA_t *)smu_table->metrics_table; + bool flag = smu_v13_0_6_is_unified_metrics(smu); struct amdgpu_device *adev = smu->adev; int ret = 0; int xcc_id; @@ -972,50 +979,50 @@ static int smu_v13_0_6_get_smu_metrics_data(struct smu_context *smu, case METRICS_AVERAGE_GFXCLK: if (smu->smc_fw_version >= 0x552F00) { xcc_id = GET_INST(GC, 0); - *value = SMUQ10_ROUND(GET_METRIC_FIELD(GfxclkFrequency)[xcc_id]); + *value = SMUQ10_ROUND(GET_METRIC_FIELD(GfxclkFrequency, flag)[xcc_id]); } else { *value = 0; } break; case METRICS_CURR_SOCCLK: case METRICS_AVERAGE_SOCCLK: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocclkFrequency)[0]); + *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocclkFrequency, flag)[0]); break; case METRICS_CURR_UCLK: case METRICS_AVERAGE_UCLK: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(UclkFrequency)); + *value = SMUQ10_ROUND(GET_METRIC_FIELD(UclkFrequency, flag)); break; case METRICS_CURR_VCLK: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(VclkFrequency)[0]); + *value = SMUQ10_ROUND(GET_METRIC_FIELD(VclkFrequency, flag)[0]); break; case METRICS_CURR_DCLK: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(DclkFrequency)[0]); + *value = SMUQ10_ROUND(GET_METRIC_FIELD(DclkFrequency, flag)[0]); break; case METRICS_CURR_FCLK: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(FclkFrequency)); + *value = SMUQ10_ROUND(GET_METRIC_FIELD(FclkFrequency, flag)); break; case METRICS_AVERAGE_GFXACTIVITY: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketGfxBusy)); + *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketGfxBusy, flag)); break; case METRICS_AVERAGE_MEMACTIVITY: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilization)); + *value = SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilization, flag)); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower)) << 8; + *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, flag)) << 8; break; case METRICS_TEMPERATURE_HOTSPOT: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature)) * + *value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature, flag)) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; break; case METRICS_TEMPERATURE_MEM: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxHbmTemperature)) * + *value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxHbmTemperature, flag)) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; break; /* This is the max of all VRs and not just SOC VR. * No need to define another data type for the same. */ case METRICS_TEMPERATURE_VRSOC: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxVrTemperature)) * + *value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxVrTemperature, flag)) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; break; default: @@ -2303,6 +2310,7 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table struct smu_table_context *smu_table = &smu->smu_table; struct gpu_metrics_v1_5 *gpu_metrics = (struct gpu_metrics_v1_5 *)smu_table->gpu_metrics_table; + bool flag = smu_v13_0_6_is_unified_metrics(smu); struct amdgpu_device *adev = smu->adev; int ret = 0, xcc_id, inst, i, j; MetricsTableX_t *metrics_x; @@ -2321,50 +2329,50 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 5); gpu_metrics->temperature_hotspot = - SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature)); + SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature, flag)); /* Individual HBM stack temperature is not reported */ gpu_metrics->temperature_mem = - SMUQ10_ROUND(GET_METRIC_FIELD(MaxHbmTemperature)); + SMUQ10_ROUND(GET_METRIC_FIELD(MaxHbmTemperature, flag)); /* Reports max temperature of all voltage rails */ gpu_metrics->temperature_vrsoc = - SMUQ10_ROUND(GET_METRIC_FIELD(MaxVrTemperature)); + SMUQ10_ROUND(GET_METRIC_FIELD(MaxVrTemperature, flag)); gpu_metrics->average_gfx_activity = - SMUQ10_ROUND(GET_METRIC_FIELD(SocketGfxBusy)); + SMUQ10_ROUND(GET_METRIC_FIELD(SocketGfxBusy, flag)); gpu_metrics->average_umc_activity = - SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilization)); + SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilization, flag)); gpu_metrics->curr_socket_power = - SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower)); + SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, flag)); /* Energy counter reported in 15.259uJ (2^-16) units */ - gpu_metrics->energy_accumulator = GET_METRIC_FIELD(SocketEnergyAcc); + gpu_metrics->energy_accumulator = GET_METRIC_FIELD(SocketEnergyAcc, flag); for (i = 0; i < MAX_GFX_CLKS; i++) { xcc_id = GET_INST(GC, i); if (xcc_id >= 0) gpu_metrics->current_gfxclk[i] = - SMUQ10_ROUND(GET_METRIC_FIELD(GfxclkFrequency)[xcc_id]); + SMUQ10_ROUND(GET_METRIC_FIELD(GfxclkFrequency, flag)[xcc_id]); if (i < MAX_CLKS) { gpu_metrics->current_socclk[i] = - SMUQ10_ROUND(GET_METRIC_FIELD(SocclkFrequency)[i]); + SMUQ10_ROUND(GET_METRIC_FIELD(SocclkFrequency, flag)[i]); inst = GET_INST(VCN, i); if (inst >= 0) { gpu_metrics->current_vclk0[i] = - SMUQ10_ROUND(GET_METRIC_FIELD(VclkFrequency)[inst]); + SMUQ10_ROUND(GET_METRIC_FIELD(VclkFrequency, flag)[inst]); gpu_metrics->current_dclk0[i] = - SMUQ10_ROUND(GET_METRIC_FIELD(DclkFrequency)[inst]); + SMUQ10_ROUND(GET_METRIC_FIELD(DclkFrequency, flag)[inst]); } } } - gpu_metrics->current_uclk = SMUQ10_ROUND(GET_METRIC_FIELD(UclkFrequency)); + gpu_metrics->current_uclk = SMUQ10_ROUND(GET_METRIC_FIELD(UclkFrequency, flag)); /* Throttle status is not reported through metrics now */ gpu_metrics->throttle_status = 0; /* Clock Lock Status. Each bit corresponds to each GFXCLK instance */ - gpu_metrics->gfxclk_lock_status = GET_METRIC_FIELD(GfxLockXCDMak) >> GET_INST(GC, 0); + gpu_metrics->gfxclk_lock_status = GET_METRIC_FIELD(GfxLockXCDMak, flag) >> GET_INST(GC, 0); if (!(adev->flags & AMD_IS_APU)) { /*Check smu version, PCIE link speed and width will be reported from pmfw metric @@ -2405,22 +2413,22 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table gpu_metrics->system_clock_counter = ktime_get_boottime_ns(); gpu_metrics->gfx_activity_acc = - SMUQ10_ROUND(GET_METRIC_FIELD(SocketGfxBusyAcc)); + SMUQ10_ROUND(GET_METRIC_FIELD(SocketGfxBusyAcc, flag)); gpu_metrics->mem_activity_acc = - SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilizationAcc)); + SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilizationAcc, flag)); for (i = 0; i < NUM_XGMI_LINKS; i++) { gpu_metrics->xgmi_read_data_acc[i] = - SMUQ10_ROUND(GET_METRIC_FIELD(XgmiReadDataSizeAcc)[i]); + SMUQ10_ROUND(GET_METRIC_FIELD(XgmiReadDataSizeAcc, flag)[i]); gpu_metrics->xgmi_write_data_acc[i] = - SMUQ10_ROUND(GET_METRIC_FIELD(XgmiWriteDataSizeAcc)[i]); + SMUQ10_ROUND(GET_METRIC_FIELD(XgmiWriteDataSizeAcc, flag)[i]); } for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) { inst = GET_INST(JPEG, i); for (j = 0; j < adev->jpeg.num_jpeg_rings; ++j) { gpu_metrics->jpeg_activity[(i * adev->jpeg.num_jpeg_rings) + j] = - SMUQ10_ROUND(GET_METRIC_FIELD(JpegBusy) + SMUQ10_ROUND(GET_METRIC_FIELD(JpegBusy, flag) [(inst * adev->jpeg.num_jpeg_rings) + j]); } } @@ -2428,13 +2436,13 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table for (i = 0; i < adev->vcn.num_vcn_inst; ++i) { inst = GET_INST(VCN, i); gpu_metrics->vcn_activity[i] = - SMUQ10_ROUND(GET_METRIC_FIELD(VcnBusy)[inst]); + SMUQ10_ROUND(GET_METRIC_FIELD(VcnBusy, flag)[inst]); } - gpu_metrics->xgmi_link_width = SMUQ10_ROUND(GET_METRIC_FIELD(XgmiWidth)); - gpu_metrics->xgmi_link_speed = SMUQ10_ROUND(GET_METRIC_FIELD(XgmiBitrate)); + gpu_metrics->xgmi_link_width = SMUQ10_ROUND(GET_METRIC_FIELD(XgmiWidth, flag)); + gpu_metrics->xgmi_link_speed = SMUQ10_ROUND(GET_METRIC_FIELD(XgmiBitrate, flag)); - gpu_metrics->firmware_timestamp = GET_METRIC_FIELD(Timestamp); + gpu_metrics->firmware_timestamp = GET_METRIC_FIELD(Timestamp, flag); *table = (void *)gpu_metrics; kfree(metrics_x); diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c index d2f11d82312f..c1a41610036d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c @@ -1051,64 +1051,6 @@ int smu_cmn_get_combo_pptable(struct smu_context *smu) false); } -void smu_cmn_init_soft_gpu_metrics(void *table, uint8_t frev, uint8_t crev) -{ - struct metrics_table_header *header = (struct metrics_table_header *)table; - uint16_t structure_size; - -#define METRICS_VERSION(a, b) ((a << 16) | b) - - switch (METRICS_VERSION(frev, crev)) { - case METRICS_VERSION(1, 0): - structure_size = sizeof(struct gpu_metrics_v1_0); - break; - case METRICS_VERSION(1, 1): - structure_size = sizeof(struct gpu_metrics_v1_1); - break; - case METRICS_VERSION(1, 2): - structure_size = sizeof(struct gpu_metrics_v1_2); - break; - case METRICS_VERSION(1, 3): - structure_size = sizeof(struct gpu_metrics_v1_3); - break; - case METRICS_VERSION(1, 4): - structure_size = sizeof(struct gpu_metrics_v1_4); - break; - case METRICS_VERSION(1, 5): - structure_size = sizeof(struct gpu_metrics_v1_5); - break; - case METRICS_VERSION(2, 0): - structure_size = sizeof(struct gpu_metrics_v2_0); - break; - case METRICS_VERSION(2, 1): - structure_size = sizeof(struct gpu_metrics_v2_1); - break; - case METRICS_VERSION(2, 2): - structure_size = sizeof(struct gpu_metrics_v2_2); - break; - case METRICS_VERSION(2, 3): - structure_size = sizeof(struct gpu_metrics_v2_3); - break; - case METRICS_VERSION(2, 4): - structure_size = sizeof(struct gpu_metrics_v2_4); - break; - case METRICS_VERSION(3, 0): - structure_size = sizeof(struct gpu_metrics_v3_0); - break; - default: - return; - } - -#undef METRICS_VERSION - - memset(header, 0xFF, structure_size); - - header->format_revision = frev; - header->content_revision = crev; - header->structure_size = structure_size; - -} - int smu_cmn_set_mp1_state(struct smu_context *smu, enum pp_mp1_state mp1_state) { diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h index a020277dec3e..ade36a8ffa66 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h @@ -40,6 +40,18 @@ #define SMU_IH_INTERRUPT_CONTEXT_ID_FAN_ABNORMAL 0x8 #define SMU_IH_INTERRUPT_CONTEXT_ID_FAN_RECOVERY 0x9 +#define smu_cmn_init_soft_gpu_metrics(ptr, frev, crev) \ + do { \ + typecheck(struct gpu_metrics_v##frev##_##crev, \ + typeof(*(ptr))); \ + struct metrics_table_header *header = \ + (struct metrics_table_header *)(ptr); \ + memset(header, 0xFF, sizeof(*(ptr))); \ + header->format_revision = frev; \ + header->content_revision = crev; \ + header->structure_size = sizeof(*(ptr)); \ + } while (0) + extern const int link_speed[]; /* Helper to Convert from PCIE Gen 1/2/3/4/5/6 to 0.1 GT/s speed units */ @@ -125,8 +137,6 @@ int smu_cmn_get_metrics_table(struct smu_context *smu, int smu_cmn_get_combo_pptable(struct smu_context *smu); -void smu_cmn_init_soft_gpu_metrics(void *table, uint8_t frev, uint8_t crev); - int smu_cmn_set_mp1_state(struct smu_context *smu, enum pp_mp1_state mp1_state); diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index 936a8f95d80f..32f7041bcb26 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -710,7 +710,8 @@ drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *co mutex_lock(&connector->hdmi.infoframes.lock); - memcpy(&infoframe->data, frame, sizeof(infoframe->data)); + BUILD_BUG_ON(sizeof(*frame) > sizeof(infoframe->data)); + memcpy(&infoframe->data, frame, sizeof(*frame)); infoframe->set = true; ret = write_infoframe(connector, infoframe); diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index f6a7a095fb0e..2df3f57bcb86 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -549,6 +549,9 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, return -EINVAL; } + if (!section_size) + return 0; + name_len = iter->size - iter->offset; section = drmm_kzalloc(&ptdev->base, sizeof(*section), GFP_KERNEL); diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index ba1446acd703..16adc5d5f67b 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -71,6 +71,7 @@ void radeon_driver_unload_kms(struct drm_device *dev) if (radeon_is_px(dev)) { pm_runtime_get_sync(dev->dev); pm_runtime_forbid(dev->dev); + pm_runtime_dont_use_autosuspend(dev->dev); } radeon_acpi_fini(rdev); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index 588d50ababf6..4c8466a85a90 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -658,9 +658,14 @@ void vmw_fence_fifo_down(struct vmw_fence_manager *fman) ret = vmw_fence_obj_wait(fence, false, false, VMW_FENCE_WAIT_TIMEOUT); + spin_lock(&fman->lock); if (unlikely(ret != 0)) { + bool cookie = dma_fence_begin_signalling(); + list_del_init(&fence->head); - dma_fence_signal(&fence->base); + dma_fence_signal_locked(&fence->base); + dma_fence_end_signalling(cookie); + INIT_LIST_HEAD(&action_list); list_splice_init(&fence->seq_passed_actions, &action_list); @@ -668,7 +673,10 @@ void vmw_fence_fifo_down(struct vmw_fence_manager *fman) } BUG_ON(!list_empty(&fence->head)); + spin_unlock(&fman->lock); + dma_fence_put(&fence->base); + spin_lock(&fman->lock); } spin_unlock(&fman->lock); diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index ce6d2167b94a..5ca9f7829912 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -1139,6 +1139,13 @@ void xe_lrc_write_ring(struct xe_lrc *lrc, const void *data, size_t size) __xe_lrc_write_ring(lrc, ring, &noop, sizeof(noop)); } + + /* + * The ring and the LRC context image are both WC, so the ring tail + * update which publishes these writes can become visible to the device + * first. Ensure the ring contents are visible before returning. + */ + xe_device_wmb(xe); } u64 xe_lrc_descriptor(struct xe_lrc *lrc) diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 476d887749a9..ad55c7f2b09f 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -1483,6 +1483,10 @@ static long xe_oa_config_locked(struct xe_oa_stream *stream, u64 arg) config = xchg(&stream->oa_config, config); drm_dbg(&stream->oa->xe->drm, "changed to oa config uuid=%s\n", stream->oa_config->uuid); + } else { + while (param.num_syncs--) + xe_sync_entry_cleanup(¶m.syncs[param.num_syncs]); + kfree(param.syncs); } err_config_put: diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index 10dac86158e4..3e26daa9f08b 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -161,6 +161,9 @@ void iforce_process_packet(struct iforce *iforce, switch (packet_id) { case 0x01: /* joystick position data */ + if (len < 7) + break; + input_report_abs(dev, ABS_X, (__s16) get_unaligned_le16(data)); input_report_abs(dev, ABS_Y, @@ -176,6 +179,9 @@ void iforce_process_packet(struct iforce *iforce, break; case 0x03: /* wheel position data */ + if (len < 7) + break; + input_report_abs(dev, ABS_WHEEL, (__s16) get_unaligned_le16(data)); input_report_abs(dev, ABS_GAS, 255 - data[2]); @@ -187,6 +193,9 @@ void iforce_process_packet(struct iforce *iforce, break; case 0x02: /* status report */ + if (len < 2) + break; + input_report_key(dev, BTN_DEAD, data[0] & 0x02); input_sync(dev); @@ -206,7 +215,7 @@ void iforce_process_packet(struct iforce *iforce, } } - for (j = 3; j < len; j += 2) + for (j = 3; j + sizeof(u16) <= len; j += sizeof(u16)) mark_core_as_ready(iforce, get_unaligned_le16(data + j)); break; diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index cba92bd590a8..927c1378b21a 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -159,6 +159,9 @@ static void iforce_usb_irq(struct urb *urb) goto exit; } + if (!urb->actual_length) + goto exit; + iforce_process_packet(iforce, iforce_usb->data_in[0], iforce_usb->data_in + 1, urb->actual_length - 1); diff --git a/drivers/input/joystick/psxpad-spi.c b/drivers/input/joystick/psxpad-spi.c index c47fc5f34bd0..4e5dd54089d5 100644 --- a/drivers/input/joystick/psxpad-spi.c +++ b/drivers/input/joystick/psxpad-spi.c @@ -369,6 +369,7 @@ static int psxpad_spi_probe(struct spi_device *spi) return err; } + spi_set_drvdata(spi, pad); pm_runtime_enable(&spi->dev); return 0; diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 4d35f730b585..53ab665e0ba5 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -418,6 +418,7 @@ static const struct xpad_device { { 0x3285, 0x0646, "Nacon Pro Compact", 0, XTYPE_XBOXONE }, { 0x3285, 0x0662, "Nacon Revolution5 Pro", 0, XTYPE_XBOX360 }, { 0x3285, 0x0663, "Nacon Evol-X", 0, XTYPE_XBOXONE }, + { 0x3507, 0x000b, "ZENAIM LEVERLESS", 0, XTYPE_XBOX360 }, { 0x3537, 0x1004, "GameSir T4 Kaleid", 0, XTYPE_XBOX360 }, { 0x3537, 0x100f, "GameSir Nova 2 Lite", 0, XTYPE_XBOX360 }, { 0x3537, 0x1010, "GameSir G7 SE", 0, XTYPE_XBOXONE }, @@ -577,6 +578,7 @@ static const struct usb_device_id xpad_table[] = { XPAD_XBOX360_VENDOR(0x31e3), /* Wooting Keyboards */ XPAD_XBOX360_VENDOR(0x3285), /* Nacon GC-100 */ XPAD_XBOXONE_VENDOR(0x3285), /* Nacon Evol-X */ + XPAD_XBOX360_VENDOR(0x3507), /* ZENAIM Controllers */ XPAD_XBOX360_VENDOR(0x3537), /* GameSir Controllers */ XPAD_XBOXONE_VENDOR(0x3537), /* GameSir Controllers */ XPAD_XBOX360_VENDOR(0x413d), /* Black Shark Green Ghost Controller */ diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index d8e1ca18e141..dea8ce003274 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1952,6 +1952,14 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { }, .callback = atkbd_deactivate_fixup, }, + { + /* Xiaomi Book Pro 14 (TM2424) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "XIAOMI"), + DMI_MATCH(DMI_PRODUCT_NAME, "Xiaomi Book Pro 14"), + }, + .callback = atkbd_deactivate_fixup, + }, { } }; diff --git a/drivers/input/misc/cs40l50-vibra.c b/drivers/input/misc/cs40l50-vibra.c index 330f09123631..06895ad112b2 100644 --- a/drivers/input/misc/cs40l50-vibra.c +++ b/drivers/input/misc/cs40l50-vibra.c @@ -139,10 +139,10 @@ static struct cs40l50_effect *cs40l50_find_effect(int id, struct list_head *effe static int cs40l50_effect_bank_set(struct cs40l50_work *work_data, struct cs40l50_effect *effect) { - s16 bank_type = work_data->custom_data[0] & CS40L50_CUSTOM_DATA_MASK; + u32 bank_type = work_data->custom_data[0] & CS40L50_CUSTOM_DATA_MASK; if (bank_type >= CS40L50_WVFRM_BANK_NUM) { - dev_err(work_data->vib->dev, "Invalid bank (%d)\n", bank_type); + dev_err(work_data->vib->dev, "Invalid bank (%u)\n", bank_type); return -EINVAL; } @@ -327,6 +327,12 @@ static int cs40l50_add(struct input_dev *dev, struct ff_effect *effect, return -EINVAL; } + if (periodic->custom_len < CS40L50_OWT_CUSTOM_DATA_SIZE) { + dev_err(vib->dev, "Invalid custom data length (%u)\n", + periodic->custom_len); + return -EINVAL; + } + work_data.custom_data = memdup_array_user(effect->u.periodic.custom_data, effect->u.periodic.custom_len, sizeof(s16)); diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index 356b99d48544..d1a3b48ee891 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -197,7 +197,7 @@ static void focaltech_process_rel_packet(struct psmouse *psmouse, { struct focaltech_data *priv = psmouse->private; struct focaltech_hw_state *state = &priv->state; - int finger1, finger2; + unsigned int finger1, finger2; state->pressed = packet[0] >> 7; finger1 = ((packet[0] >> 4) & 0x7) - 1; diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index a035b04b43ce..fe5e64253950 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -104,7 +104,9 @@ struct f54_data { enum rmi_f54_report_type report_type; u8 *report_data; + size_t max_report_size; int report_size; + int report_error; bool is_busy; struct mutex status_mutex; @@ -339,6 +341,12 @@ static void rmi_f54_buffer_queue(struct vb2_buffer *vb) mutex_lock(&f54->data_mutex); } + if (f54->report_error) { + dev_err(&f54->fn->dev, "Error acquiring report: %d\n", f54->report_error); + state = VB2_BUF_STATE_ERROR; + goto data_done; + } + ptr = vb2_plane_vaddr(vb, 0); if (!ptr) { dev_err(&f54->fn->dev, "Error acquiring frame ptr\n"); @@ -446,7 +454,12 @@ static int rmi_f54_set_input(struct f54_data *f54, unsigned int i) static int rmi_f54_vidioc_s_input(struct file *file, void *priv, unsigned int i) { - return rmi_f54_set_input(video_drvdata(file), i); + struct f54_data *f54 = video_drvdata(file); + + if (vb2_is_busy(&f54->queue)) + return -EBUSY; + + return rmi_f54_set_input(f54, i); } static int rmi_f54_vidioc_g_input(struct file *file, void *priv, @@ -547,7 +560,14 @@ static void rmi_f54_work(struct work_struct *work) dev_err(&fn->dev, "Bad report size, report type=%d\n", f54->report_type); error = -EINVAL; - goto error; /* retry won't help */ + goto out; /* retry won't help */ + } + + if (report_size > f54->max_report_size) { + dev_err(&fn->dev, "Report size %d exceeds buffer size %zu\n", + report_size, f54->max_report_size); + error = -EINVAL; + goto out; } /* @@ -558,7 +578,7 @@ static void rmi_f54_work(struct work_struct *work) &command); if (error) { dev_err(&fn->dev, "Failed to read back command\n"); - goto error; + goto out; } if (command & F54_GET_REPORT) { if (time_after(jiffies, f54->timeout)) { @@ -566,7 +586,7 @@ static void rmi_f54_work(struct work_struct *work) error = -ETIMEDOUT; } report_size = 0; - goto error; + goto out; } rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Get report command completed, reading data\n"); @@ -581,7 +601,7 @@ static void rmi_f54_work(struct work_struct *work) fifo, sizeof(fifo)); if (error) { dev_err(&fn->dev, "Failed to set fifo start offset\n"); - goto abort; + goto out; } error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr + @@ -590,16 +610,17 @@ static void rmi_f54_work(struct work_struct *work) if (error) { dev_err(&fn->dev, "%s: read [%d bytes] returned %d\n", __func__, size, error); - goto abort; + goto out; } } -abort: - f54->report_size = error ? 0 : report_size; -error: +out: if (error) report_size = 0; + f54->report_size = report_size; + f54->report_error = error; + if (report_size == 0 && !error) { queue_delayed_work(f54->workqueue, &f54->work, msecs_to_jiffies(1)); @@ -680,8 +701,8 @@ static int rmi_f54_probe(struct rmi_function *fn) rx = f54->num_rx_electrodes; tx = f54->num_tx_electrodes; - f54->report_data = devm_kzalloc(&fn->dev, - array3_size(tx, rx, sizeof(u16)), + f54->max_report_size = array3_size(tx, rx, sizeof(u16)); + f54->report_data = devm_kzalloc(&fn->dev, f54->max_report_size, GFP_KERNEL); if (f54->report_data == NULL) return -ENOMEM; diff --git a/drivers/input/rmi4/rmi_f55.c b/drivers/input/rmi4/rmi_f55.c index 488adaca4dd0..a0877d32a914 100644 --- a/drivers/input/rmi4/rmi_f55.c +++ b/drivers/input/rmi4/rmi_f55.c @@ -54,10 +54,10 @@ static int rmi_f55_detect(struct rmi_function *fn) f55->num_tx_electrodes = f55->qry[F55_NUM_TX_OFFSET]; f55->cfg_num_rx_electrodes = f55->num_rx_electrodes; - f55->cfg_num_tx_electrodes = f55->num_rx_electrodes; + f55->cfg_num_tx_electrodes = f55->num_tx_electrodes; drv_data->num_rx_electrodes = f55->cfg_num_rx_electrodes; - drv_data->num_tx_electrodes = f55->cfg_num_rx_electrodes; + drv_data->num_tx_electrodes = f55->cfg_num_tx_electrodes; if (f55->qry[F55_PHYS_CHAR_OFFSET] & F55_CAP_SENSOR_ASSIGN) { int i, total; diff --git a/drivers/input/touchscreen/hynitron_cstxxx.c b/drivers/input/touchscreen/hynitron_cstxxx.c index 1d8ca90dcda6..af66d91848b3 100644 --- a/drivers/input/touchscreen/hynitron_cstxxx.c +++ b/drivers/input/touchscreen/hynitron_cstxxx.c @@ -313,6 +313,12 @@ static void cst3xx_touch_report(struct i2c_client *client) return; touch_cnt = buf[5] & CST3XX_TOUCH_COUNT_MASK; + if (touch_cnt > ts_data->chip->max_touch_num) { + dev_err(&client->dev, "cst3xx invalid touch count (%d vs %d max)\n", + touch_cnt, ts_data->chip->max_touch_num); + return; + } + /* * Check the check bit of the last touch slot. The check bit is * always present after touch point 1 for valid data, and then @@ -335,9 +341,10 @@ static void cst3xx_touch_report(struct i2c_client *client) finger_id = (buf[idx] >> 4) & 0x0f; /* Sanity check we don't have more fingers than we expect */ - if (ts_data->chip->max_touch_num < finger_id) { - dev_err(&client->dev, "cst3xx touch read failure\n"); - break; + if (finger_id >= ts_data->chip->max_touch_num) { + dev_err(&client->dev, + "cst3xx invalid finger id %d\n", finger_id); + return; } /* sw value of 0 means no touch, 0x03 means touch */ diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 8365a2ac6fce..fe8c3221f1c2 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -725,21 +725,13 @@ static int sur40_probe(struct usb_interface *interface, goto err_free_input; } - /* register the polled input device */ - error = input_register_device(input); - if (error) { - dev_err(&interface->dev, - "Unable to register polled input device."); - goto err_free_buffer; - } - /* register the video master device */ snprintf(sur40->v4l2.name, sizeof(sur40->v4l2.name), "%s", DRIVER_LONG); error = v4l2_device_register(sur40->dev, &sur40->v4l2); if (error) { dev_err(&interface->dev, "Unable to register video master device."); - goto err_unreg_v4l2; + goto err_free_buffer; } /* initialize the lock and subdevice */ @@ -795,6 +787,14 @@ static int sur40_probe(struct usb_interface *interface, if (error) { dev_err(&interface->dev, "Unable to register video subdevice."); + goto err_free_ctrl; + } + + /* register the polled input device */ + error = input_register_device(input); + if (error) { + dev_err(&interface->dev, + "Unable to register polled input device."); goto err_unreg_video; } @@ -806,6 +806,8 @@ static int sur40_probe(struct usb_interface *interface, err_unreg_video: video_unregister_device(&sur40->vdev); +err_free_ctrl: + v4l2_ctrl_handler_free(&sur40->hdl); err_unreg_v4l2: v4l2_device_unregister(&sur40->v4l2); err_free_buffer: @@ -823,11 +825,12 @@ static void sur40_disconnect(struct usb_interface *interface) { struct sur40_state *sur40 = usb_get_intfdata(interface); + input_unregister_device(sur40->input); + v4l2_ctrl_handler_free(&sur40->hdl); video_unregister_device(&sur40->vdev); v4l2_device_unregister(&sur40->v4l2); - input_unregister_device(sur40->input); kfree(sur40->bulk_in_buffer); kfree(sur40); diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 74480ae6bfc0..494976d13bf4 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -453,7 +453,7 @@ static int iommufd_hwpt_replace_device(struct iommufd_device *idev, goto out_disable_iopf; if (old->fault) { - iommufd_auto_response_faults(hwpt, old_handle); + iommufd_auto_response_faults(old, old_handle); if (!hwpt->fault) iommufd_fault_iopf_disable(idev); } diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 8c0853dca8b9..451bcb4f1802 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -2619,6 +2619,8 @@ static void atmci_remove(struct platform_device *pdev) free_irq(platform_get_irq(pdev, 0), host); + cancel_work_sync(&host->bh_work); + clk_disable_unprepare(host->mck); pm_runtime_disable(dev); diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index e120aeb869b8..e87546dd87e3 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1358,7 +1358,7 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req) if (req->data == NULL) { OMAP_HSMMC_WRITE(host->base, BLK, 0); if (req->cmd->flags & MMC_RSP_BUSY) { - timeout = req->cmd->busy_timeout * NSEC_PER_MSEC; + timeout = (u64)req->cmd->busy_timeout * NSEC_PER_MSEC; /* * Set an arbitrary 100ms data timeout for commands with diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 5c8a10495bb8..80ef5421ff05 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -4134,6 +4134,14 @@ void __sdhci_read_caps(struct sdhci_host *host, const u16 *ver, } EXPORT_SYMBOL_GPL(__sdhci_read_caps); +static void sdhci_unmap_bounce_buffer(void *data) +{ + struct sdhci_host *host = data; + + dma_unmap_single(mmc_dev(host->mmc), host->bounce_addr, + host->bounce_buffer_size, DMA_BIDIRECTIONAL); +} + static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) { struct mmc_host *mmc = host->mmc; @@ -4188,6 +4196,14 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) } host->bounce_buffer_size = bounce_size; + ret = devm_add_action_or_reset(mmc_dev(mmc), + sdhci_unmap_bounce_buffer, host); + if (ret) { + devm_kfree(mmc_dev(mmc), host->bounce_buffer); + host->bounce_buffer = NULL; + host->bounce_buffer_size = 0; + return; + } /* Lie about this since we're bouncing */ mmc->max_segs = max_blocks; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index da1b0a46b1d9..2b7d8b93d25a 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -604,7 +604,7 @@ struct sdhci_host { unsigned int tuning_count; /* Timer count for re-tuning */ unsigned int tuning_mode; /* Re-tuning mode supported by host */ - unsigned int tuning_err; /* Error code for re-tuning */ + int tuning_err; /* Error code for re-tuning */ #define SDHCI_TUNING_MODE_1 0 #define SDHCI_TUNING_MODE_2 1 #define SDHCI_TUNING_MODE_3 2 diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c index adc47b87b38a..884171871d0e 100644 --- a/drivers/mtd/ubi/attach.c +++ b/drivers/mtd/ubi/attach.c @@ -1600,7 +1600,7 @@ int ubi_attach(struct ubi_device *ubi, int force_scan) err = ubi_read_volume_table(ubi, ai); if (err) - goto out_ai; + goto out_fm; err = ubi_wl_init(ubi, ai); if (err) @@ -1642,6 +1642,8 @@ int ubi_attach(struct ubi_device *ubi, int force_scan) out_vtbl: ubi_free_all_volumes(ubi); vfree(ubi->vtbl); +out_fm: + ubi_free_fastmap(ubi); out_ai: destroy_ai(ai); return err; diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c index 9bdb6525f128..e2bc1122bfd3 100644 --- a/drivers/mtd/ubi/fastmap-wl.c +++ b/drivers/mtd/ubi/fastmap-wl.c @@ -530,8 +530,6 @@ int ubi_is_erase_work(struct ubi_work *wrk) static void ubi_fastmap_close(struct ubi_device *ubi) { - int i; - return_unused_pool_pebs(ubi, &ubi->fm_pool); return_unused_pool_pebs(ubi, &ubi->fm_wl_pool); @@ -540,11 +538,7 @@ static void ubi_fastmap_close(struct ubi_device *ubi) ubi->fm_anchor = NULL; } - if (ubi->fm) { - for (i = 0; i < ubi->fm->used_blocks; i++) - kfree(ubi->fm->e[i]); - } - kfree(ubi->fm); + ubi_free_fastmap(ubi); } /** diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index a4999bce435f..915eb64cb001 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -868,6 +868,8 @@ int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, return -EROFS; } + memset((char *)ec_hdr + UBI_EC_HDR_SIZE, 0xFF, ubi->ec_hdr_alsize - UBI_EC_HDR_SIZE); + err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize); return err; } @@ -1150,6 +1152,14 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, return -EROFS; } + if (ubi->vid_hdr_shift) { + memset((char *)p, 0xFF, ubi->vid_hdr_shift); + memset((char *)p + ubi->vid_hdr_shift + UBI_VID_HDR_SIZE, 0xFF, + ubi->vid_hdr_alsize - (ubi->vid_hdr_shift + UBI_VID_HDR_SIZE)); + } else { + memset((char *)p + UBI_VID_HDR_SIZE, 0xFF, ubi->vid_hdr_alsize - UBI_VID_HDR_SIZE); + } + err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, ubi->vid_hdr_alsize); return err; diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 1c9e874e8ede..450a7b9a5bd7 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -970,10 +970,22 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, struct ubi_attach_info *scan_ai); int ubi_fastmap_init_checkmap(struct ubi_volume *vol, int leb_count); void ubi_fastmap_destroy_checkmap(struct ubi_volume *vol); +static inline void ubi_free_fastmap(struct ubi_device *ubi) +{ + if (ubi->fm) { + int i; + + for (i = 0; i < ubi->fm->used_blocks; i++) + kmem_cache_free(ubi_wl_entry_slab, ubi->fm->e[i]); + kfree(ubi->fm); + ubi->fm = NULL; + } +} #else static inline int ubi_update_fastmap(struct ubi_device *ubi) { return 0; } static inline int ubi_fastmap_init_checkmap(struct ubi_volume *vol, int leb_count) { return 0; } static inline void ubi_fastmap_destroy_checkmap(struct ubi_volume *vol) {} +static inline void ubi_free_fastmap(struct ubi_device *ubi) { } #endif /* block.c */ diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index ba662bef155c..de2fef265251 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -1989,27 +1989,27 @@ static int rcar_canfd_probe(struct platform_device *pdev) } } - err = reset_control_reset(gpriv->rstc1); - if (err) - goto fail_dev; - err = reset_control_reset(gpriv->rstc2); - if (err) { - reset_control_assert(gpriv->rstc1); - goto fail_dev; - } - /* Enable peripheral clock for register access */ err = clk_prepare_enable(gpriv->clkp); if (err) { dev_err(dev, "failed to enable peripheral clock: %pe\n", ERR_PTR(err)); - goto fail_reset; + goto fail_dev; + } + + err = reset_control_reset(gpriv->rstc1); + if (err) + goto fail_clk; + err = reset_control_reset(gpriv->rstc2); + if (err) { + reset_control_assert(gpriv->rstc1); + goto fail_clk; } err = rcar_canfd_reset_controller(gpriv); if (err) { dev_err(dev, "reset controller failed: %pe\n", ERR_PTR(err)); - goto fail_clk; + goto fail_reset; } /* Controller in Global reset & Channel reset mode */ @@ -2061,11 +2061,11 @@ static int rcar_canfd_probe(struct platform_device *pdev) rcar_canfd_channel_remove(gpriv, ch); fail_mode: rcar_canfd_disable_global_interrupts(gpriv); -fail_clk: - clk_disable_unprepare(gpriv->clkp); fail_reset: reset_control_assert(gpriv->rstc1); reset_control_assert(gpriv->rstc2); +fail_clk: + clk_disable_unprepare(gpriv->clkp); fail_dev: return err; } @@ -2085,9 +2085,9 @@ static void rcar_canfd_remove(struct platform_device *pdev) /* Enter global sleep mode */ rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR); - clk_disable_unprepare(gpriv->clkp); reset_control_assert(gpriv->rstc1); reset_control_assert(gpriv->rstc2); + clk_disable_unprepare(gpriv->clkp); } static int __maybe_unused rcar_canfd_suspend(struct device *dev) diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 71530ae79a87..9db720957c12 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -847,6 +847,30 @@ static void ice_notify_vf_reset(struct ice_vf *vf) NULL); } +/** + * ice_reset_interrupts - clear all queue interrupt configuration for a VSI + * @vsi: the VSI whose interrupt registers should be cleared + * + * Zero the QINT_RQCTL and QINT_TQCTL registers for all allocated queues + * in the VSI. This clears the entire register including MSIX_INDX, ITR_INDX, + * CAUSE_ENA and NEXTQ fields, unlike ice_vf_dis_rxq_interrupt() which only + * clears the CAUSE_ENA bit. + */ +void ice_reset_interrupts(struct ice_vsi *vsi) +{ + struct ice_pf *pf = vsi->back; + struct ice_hw *hw = &pf->hw; + int i; + + ice_for_each_alloc_rxq(vsi, i) + wr32(hw, QINT_RQCTL(vsi->rxq_map[i]), 0); + + ice_for_each_alloc_txq(vsi, i) + wr32(hw, QINT_TQCTL(vsi->txq_map[i]), 0); + + ice_flush(hw); +} + /** * ice_reset_vf - Reset a particular VF * @vf: pointer to the VF structure @@ -929,6 +953,9 @@ int ice_reset_vf(struct ice_vf *vf, u32 flags) ice_dis_vf_qs(vf); + /* cleanup interrupt registers */ + ice_reset_interrupts(vsi); + /* Call Disable LAN Tx queue AQ whether or not queues are * enabled. This is needed for successful completion of VFR. */ diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h b/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h index 5392b0404986..321d29c25b7c 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib_private.h @@ -26,6 +26,7 @@ void ice_initialize_vf_entry(struct ice_vf *vf); void ice_deinitialize_vf_entry(struct ice_vf *vf); void ice_dis_vf_qs(struct ice_vf *vf); +void ice_reset_interrupts(struct ice_vsi *vsi); int ice_check_vf_init(struct ice_vf *vf); enum virtchnl_status_code ice_err_to_virt_err(int err); struct ice_port_info *ice_vf_get_port_info(struct ice_vf *vf); diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.c b/drivers/net/ethernet/intel/ice/ice_virtchnl.c index e98acbc41a81..9077f2d22dca 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.c @@ -1277,6 +1277,24 @@ static void ice_vf_ena_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx) wr32(hw, QINT_RQCTL(pfq), reg | QINT_RQCTL_CAUSE_ENA_M); } +/** + * ice_vf_dis_rxq_interrupt - disable Rx queue interrupt via QINT_RQCTL + * @vsi: VSI of the VF to configure + * @q_idx: VF queue index used to determine the queue in the PF's space + */ +static void ice_vf_dis_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx) +{ + struct ice_hw *hw = &vsi->back->hw; + u32 pfq = vsi->rxq_map[q_idx]; + u32 reg; + + reg = rd32(hw, QINT_RQCTL(pfq)); + reg &= ~QINT_RQCTL_CAUSE_ENA_M; + wr32(hw, QINT_RQCTL(pfq), reg); + + ice_flush(hw); +} + /** * ice_vc_ena_qs_msg * @vf: pointer to the VF info @@ -1470,6 +1488,8 @@ static int ice_vc_dis_qs_msg(struct ice_vf *vf, u8 *msg) goto error_param; } + for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) + ice_vf_dis_rxq_interrupt(vsi, vf_q_id); bitmap_zero(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF); } else if (q_map) { for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) { @@ -1490,6 +1510,7 @@ static int ice_vc_dis_qs_msg(struct ice_vf *vf, u8 *msg) goto error_param; } + ice_vf_dis_rxq_interrupt(vsi, vf_q_id); /* Clear enabled queues flag */ clear_bit(vf_q_id, vf->rxq_ena); } diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 6fcf4fd7ee19..e4a4d930dd22 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -5199,7 +5199,6 @@ void igc_down(struct igc_adapter *adapter) for (i = 0; i < adapter->num_q_vectors; i++) { if (adapter->q_vector[i]) { - napi_synchronize(&adapter->q_vector[i]->napi); napi_disable(&adapter->q_vector[i]->napi); } } @@ -7405,11 +7404,13 @@ static int igc_resume(struct device *dev) if (netif_running(netdev)) { err = __igc_open(netdev, true); - if (!err) - netif_device_attach(netdev); + if (err) + return err; } - return err; + netif_device_attach(netdev); + + return 0; } static int igc_runtime_resume(struct device *dev) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 0d3346baa84e..42a4f52ecdc2 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -3215,11 +3215,23 @@ static int mtk_get_irqs(struct platform_device *pdev, struct mtk_eth *eth) int i; /* future SoCs beginning with MT7988 should use named IRQs in dts */ - eth->irq[MTK_FE_IRQ_TX] = platform_get_irq_byname(pdev, "fe1"); - eth->irq[MTK_FE_IRQ_RX] = platform_get_irq_byname(pdev, "fe2"); + eth->irq[MTK_FE_IRQ_TX] = platform_get_irq_byname_optional(pdev, "fe1"); + eth->irq[MTK_FE_IRQ_RX] = platform_get_irq_byname_optional(pdev, "fe2"); if (eth->irq[MTK_FE_IRQ_TX] >= 0 && eth->irq[MTK_FE_IRQ_RX] >= 0) return 0; + /* only use legacy mode if platform_get_irq_byname_optional returned -ENXIO */ + if (eth->irq[MTK_FE_IRQ_TX] != -ENXIO) + return dev_err_probe(&pdev->dev, eth->irq[MTK_FE_IRQ_TX], + "Error requesting FE TX IRQ\n"); + + if (eth->irq[MTK_FE_IRQ_RX] != -ENXIO) + return dev_err_probe(&pdev->dev, eth->irq[MTK_FE_IRQ_RX], + "Error requesting FE RX IRQ\n"); + + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT)) + dev_warn(&pdev->dev, "legacy DT: missing interrupt-names."); + /* legacy way: * On MTK_SHARED_INT SoCs (MT7621 + MT7628) the first IRQ is taken * from devicetree and used for both RX and TX - it is shared. diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 476f7ff0648e..70fe4d837a0b 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -3011,7 +3011,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending) if (!ndev) { if (i == 0) dev_err(dev, "No net device to remove\n"); - goto out; + break; } /* All cleanup actions should stay after rtnl_lock(), otherwise @@ -3039,7 +3039,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending) } mana_destroy_eq(ac); -out: + mana_gd_deregister_device(gd); if (suspending) diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index 68049bb2bd98..45cea845d4aa 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -1190,6 +1190,8 @@ static int am65_cpsw_nuss_rx_packets(struct am65_cpsw_rx_flow *flow, k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma); pkt_len = cppi5_hdesc_get_pktlen(desc_rx); cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL); + /* Port ID is contained in the lower 8-bits of the 16-bit Source Tag */ + port_id &= 0xFF; dev_dbg(dev, "%s rx port_id:%d\n", __func__, port_id); port = am65_common_get_port(common, port_id); ndev = port->ndev; diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index bce52c743f0e..1d7b9381839d 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -145,6 +145,8 @@ static int ipvlan_init(struct net_device *dev) dev->lltx = true; netif_inherit_tso_max(dev, phy_dev); dev->hard_header_len = phy_dev->hard_header_len; + dev->needed_headroom = phy_dev->needed_headroom; + dev->needed_tailroom = phy_dev->needed_tailroom; netdev_lockdep_set_classes(dev); @@ -770,6 +772,8 @@ static int ipvlan_device_event(struct notifier_block *unused, case NETDEV_FEAT_CHANGE: list_for_each_entry(ipvlan, &port->ipvlans, pnode) { netif_inherit_tso_max(ipvlan->dev, dev); + ipvlan->dev->needed_headroom = dev->needed_headroom; + ipvlan->dev->needed_tailroom = dev->needed_tailroom; netdev_update_features(ipvlan->dev); } break; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index e778367c1d29..4d1aae8e89cf 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -942,6 +942,8 @@ static int macvlan_init(struct net_device *dev) dev->lltx = true; netif_inherit_tso_max(dev, lowerdev); dev->hard_header_len = lowerdev->hard_header_len; + dev->needed_headroom = lowerdev->needed_headroom; + dev->needed_tailroom = lowerdev->needed_tailroom; macvlan_set_lockdep_class(dev); vlan->pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats); @@ -1813,6 +1815,8 @@ static int macvlan_device_event(struct notifier_block *unused, case NETDEV_FEAT_CHANGE: list_for_each_entry(vlan, &port->vlans, list) { netif_inherit_tso_max(vlan->dev, dev); + vlan->dev->needed_headroom = dev->needed_headroom; + vlan->dev->needed_tailroom = dev->needed_tailroom; netdev_update_features(vlan->dev); } break; diff --git a/drivers/net/tap.c b/drivers/net/tap.c index c460b1f39136..b3cb7d5a9585 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1199,10 +1199,21 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) skb_reset_mac_header(skb); skb->protocol = eth_hdr(skb)->h_proto; + rcu_read_lock(); + tap = rcu_dereference(q->tap); + if (!tap) { + kfree_skb(skb); + rcu_read_unlock(); + return 0; + } + skb->dev = tap->dev; + if (vnet_hdr_len) { err = virtio_net_hdr_to_skb(skb, gso, tap_is_little_endian(q)); - if (err) + if (err) { + rcu_read_unlock(); goto err_kfree; + } } /* Move network header to the right position for VLAN tagged packets */ @@ -1210,15 +1221,8 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); - rcu_read_lock(); - tap = rcu_dereference(q->tap); - if (tap) { - skb->dev = tap->dev; - skb_probe_transport_header(skb); - dev_queue_xmit(skb); - } else { - kfree_skb(skb); - } + skb_probe_transport_header(skb); + dev_queue_xmit(skb); rcu_read_unlock(); return 0; diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 2feb694f58bb..96e373d65897 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -967,7 +967,7 @@ static int veth_poll(struct napi_struct *napi, int budget) struct veth_rq *rq = container_of(napi, struct veth_rq, xdp_napi); struct veth_priv *priv = netdev_priv(rq->dev); - int queue_idx = rq->xdp_rxq.queue_index; + int queue_idx = rq - priv->rq; struct netdev_queue *peer_txq; struct veth_stats stats = {}; struct net_device *peer_dev; diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 93ecd9577f4d..e684d59291ef 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -122,16 +122,6 @@ struct net_vrf { int ifindex; }; -static void vrf_rx_stats(struct net_device *dev, int len) -{ - struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats); - - u64_stats_update_begin(&dstats->syncp); - u64_stats_inc(&dstats->rx_packets); - u64_stats_add(&dstats->rx_bytes, len); - u64_stats_update_end(&dstats->syncp); -} - static void vrf_tx_error(struct net_device *vrf_dev, struct sk_buff *skb) { vrf_dev->stats.tx_errors++; @@ -367,7 +357,7 @@ static bool qdisc_tx_is_default(const struct net_device *dev) static int vrf_local_xmit(struct sk_buff *skb, struct net_device *dev, struct dst_entry *dst) { - int len = skb->len; + unsigned int len = skb->len; skb_orphan(skb); @@ -380,15 +370,10 @@ static int vrf_local_xmit(struct sk_buff *skb, struct net_device *dev, skb->protocol = eth_type_trans(skb, dev); - if (likely(__netif_rx(skb) == NET_RX_SUCCESS)) { - vrf_rx_stats(dev, len); - } else { - struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats); - - u64_stats_update_begin(&dstats->syncp); - u64_stats_inc(&dstats->rx_drops); - u64_stats_update_end(&dstats->syncp); - } + if (likely(__netif_rx(skb) == NET_RX_SUCCESS)) + dev_dstats_rx_add(dev, len); + else + dev_dstats_rx_dropped(dev); return NETDEV_TX_OK; } @@ -576,20 +561,14 @@ static netdev_tx_t is_ip_tx_frame(struct sk_buff *skb, struct net_device *dev) static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev) { - struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats); - - int len = skb->len; - netdev_tx_t ret = is_ip_tx_frame(skb, dev); - - u64_stats_update_begin(&dstats->syncp); - if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) { + unsigned int len = skb->len; + netdev_tx_t ret; - u64_stats_inc(&dstats->tx_packets); - u64_stats_add(&dstats->tx_bytes, len); - } else { - u64_stats_inc(&dstats->tx_drops); - } - u64_stats_update_end(&dstats->syncp); + ret = is_ip_tx_frame(skb, dev); + if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) + dev_dstats_tx_add(dev, len); + else + dev_dstats_tx_dropped(dev); return ret; } @@ -1371,7 +1350,7 @@ static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev, if (!is_ndisc) { struct net_device *orig_dev = skb->dev; - vrf_rx_stats(vrf_dev, skb->len); + dev_dstats_rx_add(vrf_dev, skb->len); skb->dev = vrf_dev; skb->skb_iif = vrf_dev->ifindex; @@ -1427,7 +1406,7 @@ static struct sk_buff *vrf_ip_rcv(struct net_device *vrf_dev, goto out; } - vrf_rx_stats(vrf_dev, skb->len); + dev_dstats_rx_add(vrf_dev, skb->len); if (!list_empty(&vrf_dev->ptype_all)) { int err; diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 3c62957907ba..d6b69bb55347 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -1814,14 +1814,14 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) if (unlikely(!(vxlan->dev->flags & IFF_UP))) { rcu_read_unlock(); - dev_core_stats_rx_dropped_inc(vxlan->dev); + dev_dstats_rx_dropped(vxlan->dev); vxlan_vnifilter_count(vxlan, vni, vninode, VXLAN_VNI_STATS_RX_DROPS, 0); reason = SKB_DROP_REASON_DEV_READY; goto drop; } - dev_sw_netstats_rx_add(vxlan->dev, skb->len); + dev_dstats_rx_add(vxlan->dev, skb->len); vxlan_vnifilter_count(vxlan, vni, vninode, VXLAN_VNI_STATS_RX, skb->len); gro_cells_receive(&vxlan->gro_cells, skb); @@ -1875,8 +1875,8 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) if (dev->flags & IFF_NOARP) goto out; - if (!pskb_may_pull(skb, arp_hdr_len(dev))) { - dev_core_stats_tx_dropped_inc(dev); + if (!pskb_network_may_pull(skb, arp_hdr_len(dev))) { + dev_dstats_tx_dropped(dev); vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); goto out; @@ -1940,7 +1940,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) reply->pkt_type = PACKET_HOST; if (netif_rx(reply) == NET_RX_DROP) { - dev_core_stats_rx_dropped_inc(dev); + dev_dstats_rx_dropped(dev); vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_RX_DROPS, 0); } @@ -2104,7 +2104,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) goto out; if (netif_rx(reply) == NET_RX_DROP) { - dev_core_stats_rx_dropped_inc(dev); + dev_dstats_rx_dropped(dev); vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_RX_DROPS, 0); } @@ -2289,8 +2289,8 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, { union vxlan_addr loopback; union vxlan_addr *remote_ip = &dst_vxlan->default_dst.remote_ip; + unsigned int len = skb->len; struct net_device *dev; - int len = skb->len; skb->pkt_type = PACKET_HOST; skb->encapsulation = 0; @@ -2317,16 +2317,16 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, if ((dst_vxlan->cfg.flags & VXLAN_F_LEARN) && snoop) vxlan_snoop(dev, &loopback, eth_hdr(skb)->h_source, 0, vni); - dev_sw_netstats_tx_add(src_vxlan->dev, 1, len); + dev_dstats_tx_add(src_vxlan->dev, len); vxlan_vnifilter_count(src_vxlan, vni, NULL, VXLAN_VNI_STATS_TX, len); if (__netif_rx(skb) == NET_RX_SUCCESS) { - dev_sw_netstats_rx_add(dst_vxlan->dev, len); + dev_dstats_rx_add(dst_vxlan->dev, len); vxlan_vnifilter_count(dst_vxlan, vni, NULL, VXLAN_VNI_STATS_RX, len); } else { drop: - dev_core_stats_rx_dropped_inc(dev); + dev_dstats_rx_dropped(dev); vxlan_vnifilter_count(dst_vxlan, vni, NULL, VXLAN_VNI_STATS_RX_DROPS, 0); } @@ -2630,7 +2630,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, return; drop: - dev_core_stats_tx_dropped_inc(dev); + dev_dstats_tx_dropped(dev); vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); dev_kfree_skb(skb); return; @@ -2671,7 +2671,7 @@ static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev, return; drop: - dev_core_stats_tx_dropped_inc(dev); + dev_dstats_tx_dropped(dev); vxlan_vnifilter_count(netdev_priv(dev), vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); dev_kfree_skb(skb); @@ -2709,7 +2709,7 @@ static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev, return NETDEV_TX_OK; drop: - dev_core_stats_tx_dropped_inc(dev); + dev_dstats_tx_dropped(dev); vxlan_vnifilter_count(netdev_priv(dev), vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); dev_kfree_skb(skb); @@ -2757,8 +2757,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) return arp_reduce(dev, skb, vni); #if IS_ENABLED(CONFIG_IPV6) else if (ntohs(eth->h_proto) == ETH_P_IPV6 && - pskb_may_pull(skb, sizeof(struct ipv6hdr) + - sizeof(struct nd_msg)) && + pskb_network_may_pull(skb, sizeof(struct ipv6hdr) + + sizeof(struct nd_msg)) && ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) { struct nd_msg *m = (struct nd_msg *)(ipv6_hdr(skb) + 1); @@ -2808,7 +2808,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) !is_multicast_ether_addr(eth->h_dest)) vxlan_fdb_miss(vxlan, eth->h_dest); - dev_core_stats_tx_dropped_inc(dev); + dev_dstats_tx_dropped(dev); vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0); kfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET); @@ -3383,7 +3383,7 @@ static void vxlan_setup(struct net_device *dev) dev->min_mtu = ETH_MIN_MTU; dev->max_mtu = ETH_MAX_MTU; - dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS; INIT_LIST_HEAD(&vxlan->next); timer_setup(&vxlan->age_timer, vxlan_cleanup, TIMER_DEFERRABLE); diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c index 165b9f4a8eef..e15a8536dae9 100644 --- a/drivers/net/vxlan/vxlan_mdb.c +++ b/drivers/net/vxlan/vxlan_mdb.c @@ -1631,7 +1631,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, switch (skb->protocol) { case htons(ETH_P_IP): - if (!pskb_may_pull(skb, sizeof(struct iphdr))) + if (!pskb_network_may_pull(skb, sizeof(struct iphdr))) return NULL; group.dst.sa.sa_family = AF_INET; group.dst.sin.sin_addr.s_addr = ip_hdr(skb)->daddr; @@ -1640,7 +1640,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan, break; #if IS_ENABLED(CONFIG_IPV6) case htons(ETH_P_IPV6): - if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr))) return NULL; group.dst.sa.sa_family = AF_INET6; group.dst.sin6.sin6_addr = ipv6_hdr(skb)->daddr; diff --git a/drivers/pmdomain/arm/scmi_perf_domain.c b/drivers/pmdomain/arm/scmi_perf_domain.c index 3693423459c9..e390f902a444 100644 --- a/drivers/pmdomain/arm/scmi_perf_domain.c +++ b/drivers/pmdomain/arm/scmi_perf_domain.c @@ -33,7 +33,7 @@ scmi_pd_set_perf_state(struct generic_pm_domain *genpd, unsigned int state) return 0; if (!state) - return -EINVAL; + return 0; ret = pd->perf_ops->level_set(pd->ph, pd->domain_id, state, false); if (ret) diff --git a/drivers/s390/cio/vfio_ccw_async.c b/drivers/s390/cio/vfio_ccw_async.c index 420d89ba7f83..4aff0b58fa5d 100644 --- a/drivers/s390/cio/vfio_ccw_async.c +++ b/drivers/s390/cio/vfio_ccw_async.c @@ -8,6 +8,7 @@ */ #include +#include #include "vfio_ccw_private.h" @@ -24,11 +25,20 @@ static ssize_t vfio_ccw_async_region_read(struct vfio_ccw_private *private, return -EINVAL; mutex_lock(&private->io_mutex); + + if (i >= private->num_regions) { + ret = -EINVAL; + goto out_unlock; + } + + i = array_index_nospec(i, private->num_regions); region = private->region[i].data; if (copy_to_user(buf, (void *)region + pos, count)) ret = -EFAULT; else ret = count; + +out_unlock: mutex_unlock(&private->io_mutex); return ret; } @@ -48,6 +58,12 @@ static ssize_t vfio_ccw_async_region_write(struct vfio_ccw_private *private, if (!mutex_trylock(&private->io_mutex)) return -EAGAIN; + if (i >= private->num_regions) { + ret = -EINVAL; + goto out_unlock; + } + + i = array_index_nospec(i, private->num_regions); region = private->region[i].data; if (copy_from_user((void *)region + pos, buf, count)) { ret = -EFAULT; diff --git a/drivers/s390/cio/vfio_ccw_chp.c b/drivers/s390/cio/vfio_ccw_chp.c index 38c176cf6295..7708eb4d6de0 100644 --- a/drivers/s390/cio/vfio_ccw_chp.c +++ b/drivers/s390/cio/vfio_ccw_chp.c @@ -9,6 +9,7 @@ */ #include +#include #include #include "vfio_ccw_private.h" @@ -26,6 +27,13 @@ static ssize_t vfio_ccw_schib_region_read(struct vfio_ccw_private *private, return -EINVAL; mutex_lock(&private->io_mutex); + + if (i >= private->num_regions) { + ret = -EINVAL; + goto out; + } + + i = array_index_nospec(i, private->num_regions); region = private->region[i].data; if (cio_update_schib(sch)) { @@ -85,19 +93,30 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private, loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK; struct ccw_crw_region *region; struct vfio_ccw_crw *crw; + unsigned long flags; int ret; if (pos + count > sizeof(*region)) return -EINVAL; + mutex_lock(&private->io_mutex); + if (i >= private->num_regions) { + ret = -EINVAL; + goto out; + } + + i = array_index_nospec(i, private->num_regions); + region = private->region[i].data; + + spin_lock_irqsave(&private->crw_lock, flags); crw = list_first_entry_or_null(&private->crw, struct vfio_ccw_crw, next); if (crw) list_del(&crw->next); - mutex_lock(&private->io_mutex); - region = private->region[i].data; + /* Drop CRW lock while copying to userspace */ + spin_unlock_irqrestore(&private->crw_lock, flags); if (crw) memcpy(®ion->crw, &crw->crw, sizeof(region->crw)); @@ -108,14 +127,16 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private, ret = count; region->crw = 0; - - mutex_unlock(&private->io_mutex); - kfree(crw); /* Notify the guest if more CRWs are on our queue */ + spin_lock_irqsave(&private->crw_lock, flags); if (!list_empty(&private->crw) && private->crw_trigger) eventfd_signal(private->crw_trigger); + spin_unlock_irqrestore(&private->crw_lock, flags); + +out: + mutex_unlock(&private->io_mutex); return ret; } diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 5f6e10225627..36985aed125c 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -233,6 +233,7 @@ static void convert_ccw0_to_ccw1(struct ccw1 *source, unsigned long len) } #define idal_is_2k(_cp) (!(_cp)->orb.cmd.c64 || (_cp)->orb.cmd.i2k) +#define get_idaw_size(_cp) ((_cp)->orb.cmd.c64 ? sizeof(u64) : sizeof(u32)) /* * Helpers to operate ccwchain. @@ -332,6 +333,7 @@ static struct ccwchain *ccwchain_alloc(struct channel_program *cp, int len) goto out_err; list_add_tail(&chain->next, &cp->ccwchain_list); + cp->ccwchain_count++; return chain; @@ -376,11 +378,9 @@ static void ccwchain_cda_free(struct ccwchain *chain, int idx) static int ccwchain_calc_length(u64 iova, struct channel_program *cp) { struct ccw1 *ccw = cp->guest_cp; - int cnt = 0; - - do { - cnt++; + int cnt; + for (cnt = 1; cnt <= CCWCHAIN_LEN_MAX; cnt++, ccw++) { /* * We want to keep counting if the current CCW has the * command-chaining flag enabled, or if it is a TIC CCW @@ -390,15 +390,10 @@ static int ccwchain_calc_length(u64 iova, struct channel_program *cp) * after the TIC, depending on the results of its operation. */ if (!ccw_is_chain(ccw) && !is_tic_within_range(ccw, iova, cnt)) - break; - - ccw++; - } while (cnt < CCWCHAIN_LEN_MAX + 1); - - if (cnt == CCWCHAIN_LEN_MAX + 1) - cnt = -EINVAL; + return cnt; + } - return cnt; + return -EINVAL; } static int tic_target_chain_exists(struct ccw1 *tic, struct channel_program *cp) @@ -441,6 +436,10 @@ static int ccwchain_handle_ccw(dma32_t cda, struct channel_program *cp) if (len < 0) return len; + /* Limit number of chains in a single channel program */ + if (cp->ccwchain_count >= CCWCHAIN_COUNT_MAX) + return -EINVAL; + /* Need alloc a new chain for this one. */ chain = ccwchain_alloc(cp, len); if (!chain) @@ -455,9 +454,6 @@ static int ccwchain_handle_ccw(dma32_t cda, struct channel_program *cp) /* Loop for tics on this new chain. */ ret = ccwchain_loop_tic(chain, cp); - if (ret) - ccwchain_free(chain); - return ret; } @@ -486,6 +482,23 @@ static int ccwchain_loop_tic(struct ccwchain *chain, struct channel_program *cp) return 0; } +static int ccwchain_build_ccws(dma32_t cda, struct channel_program *cp) +{ + struct ccwchain *chain, *temp; + int ret; + + ret = ccwchain_handle_ccw(cda, cp); + + if (ret) { + /* Cleanup if an error occurred */ + list_for_each_entry_safe(chain, temp, &cp->ccwchain_list, next) { + ccwchain_free(chain); + } + } + + return ret; +} + static int ccwchain_fetch_tic(struct ccw1 *ccw, struct channel_program *cp) { @@ -511,7 +524,8 @@ static dma64_t *get_guest_idal(struct ccw1 *ccw, struct channel_program *cp, int &container_of(cp, struct vfio_ccw_private, cp)->vdev; dma64_t *idaws; dma32_t *idaws_f1; - int idal_len = idaw_nr * sizeof(*idaws); + u64 first_idaw; + int idal_len = idaw_nr * get_idaw_size(cp); int idaw_size = idal_is_2k(cp) ? PAGE_SIZE / 2 : PAGE_SIZE; int idaw_mask = ~(idaw_size - 1); int i, ret; @@ -527,6 +541,18 @@ static dma64_t *get_guest_idal(struct ccw1 *ccw, struct channel_program *cp, int kfree(idaws); return ERR_PTR(ret); } + + idaws_f1 = (dma32_t *)idaws; + if (cp->orb.cmd.c64) + first_idaw = dma64_to_u64(idaws[0]); + else + first_idaw = dma32_to_u32(idaws_f1[0]); + + /* Unexpected mismatch from earlier read */ + if (first_idaw != cp->guest_iova) { + kfree(idaws); + return ERR_PTR(-EINVAL); + } } else { /* Fabricate an IDAL based off CCW data address */ if (cp->orb.cmd.c64) { @@ -568,7 +594,7 @@ static int ccw_count_idaws(struct ccw1 *ccw, struct vfio_device *vdev = &container_of(cp, struct vfio_ccw_private, cp)->vdev; u64 iova; - int size = cp->orb.cmd.c64 ? sizeof(u64) : sizeof(u32); + int size = get_idaw_size(cp); int ret; int bytes = 1; @@ -592,6 +618,9 @@ static int ccw_count_idaws(struct ccw1 *ccw, iova = dma32_to_u32(ccw->cda); } + /* Save the read address for later */ + cp->guest_iova = iova; + /* Format-1 IDAWs operate on 2K each */ if (!cp->orb.cmd.c64) return idal_2k_nr_words((void *)iova, bytes); @@ -731,11 +760,12 @@ int cp_init(struct channel_program *cp, union orb *orb) vdev->dev, "Prefetching channel program even though prefetch not specified in ORB"); + cp->ccwchain_count = 0; INIT_LIST_HEAD(&cp->ccwchain_list); memcpy(&cp->orb, orb, sizeof(*orb)); /* Build a ccwchain for the first CCW segment */ - ret = ccwchain_handle_ccw(orb->cmd.cpa, cp); + ret = ccwchain_build_ccws(orb->cmd.cpa, cp); if (!ret) cp->initialized = true; @@ -947,17 +977,23 @@ void cp_update_scsw(struct channel_program *cp, union scsw *scsw) */ bool cp_iova_pinned(struct channel_program *cp, u64 iova, u64 length) { + struct vfio_ccw_private *private = + container_of(cp, struct vfio_ccw_private, cp); struct ccwchain *chain; int i; if (!cp->initialized) return false; + mutex_lock(&private->io_mutex); list_for_each_entry(chain, &cp->ccwchain_list, next) { for (i = 0; i < chain->ch_len; i++) - if (page_array_iova_pinned(&chain->ch_pa[i], iova, length)) + if (page_array_iova_pinned(&chain->ch_pa[i], iova, length)) { + mutex_unlock(&private->io_mutex); return true; + } } + mutex_unlock(&private->io_mutex); return false; } diff --git a/drivers/s390/cio/vfio_ccw_cp.h b/drivers/s390/cio/vfio_ccw_cp.h index fc31eb699807..9af98ff12d67 100644 --- a/drivers/s390/cio/vfio_ccw_cp.h +++ b/drivers/s390/cio/vfio_ccw_cp.h @@ -23,11 +23,19 @@ */ #define CCWCHAIN_LEN_MAX 256 +/* + * Maximum number of chains + */ +#define CCWCHAIN_COUNT_MAX 16 + /** * struct channel_program - manage information for channel program * @ccwchain_list: list head of ccwchains * @orb: orb for the currently processed ssch request * @initialized: whether this instance is actually initialized + * @guest_cp: copy of guest channel program + * @ccwchain_count: number of channel program segments (linked by TIC) + * @guest_iova: first data address of a guest channel program * * @ccwchain_list is the head of a ccwchain list, that contents the * translated result of the guest channel program that pointed out by @@ -38,6 +46,8 @@ struct channel_program { union orb orb; bool initialized; struct ccw1 *guest_cp; + unsigned int ccwchain_count; + u64 guest_iova; }; int cp_init(struct channel_program *cp, union orb *orb); diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c index 914dde041675..d19073f45158 100644 --- a/drivers/s390/cio/vfio_ccw_drv.c +++ b/drivers/s390/cio/vfio_ccw_drv.c @@ -91,6 +91,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work) is_final = !(scsw_actl(&irb->scsw) & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)); + mutex_lock(&private->io_mutex); if (scsw_is_solicited(&irb->scsw)) { cp_update_scsw(&private->cp, &irb->scsw); if (is_final && private->state == VFIO_CCW_STATE_CP_PENDING) { @@ -98,9 +99,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work) cp_is_finished = true; } } - mutex_lock(&private->io_mutex); memcpy(private->io_region->irb_area, irb, sizeof(*irb)); - mutex_unlock(&private->io_mutex); /* * Reset to IDLE only if processing of a channel program @@ -110,6 +109,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work) */ if (cp_is_finished) private->state = VFIO_CCW_STATE_IDLE; + mutex_unlock(&private->io_mutex); if (private->io_trigger) eventfd_signal(private->io_trigger); @@ -118,11 +118,25 @@ void vfio_ccw_sch_io_todo(struct work_struct *work) void vfio_ccw_crw_todo(struct work_struct *work) { struct vfio_ccw_private *private; + unsigned long flags; private = container_of(work, struct vfio_ccw_private, crw_work); + spin_lock_irqsave(&private->crw_lock, flags); if (!list_empty(&private->crw) && private->crw_trigger) eventfd_signal(private->crw_trigger); + spin_unlock_irqrestore(&private->crw_lock, flags); +} + +void vfio_ccw_notoper_todo(struct work_struct *work) +{ + struct vfio_ccw_private *private; + + private = container_of(work, struct vfio_ccw_private, notoper_work); + + mutex_lock(&private->io_mutex); + cp_free(&private->cp); + mutex_unlock(&private->io_mutex); } /* @@ -275,6 +289,7 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private, unsigned int rsid) { struct vfio_ccw_crw *crw; + unsigned long flags; /* * If unable to allocate a CRW, just drop the event and @@ -292,7 +307,9 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private, crw->crw.erc = erc; crw->crw.rsid = rsid; + spin_lock_irqsave(&private->crw_lock, flags); list_add_tail(&crw->next, &private->crw); + spin_unlock_irqrestore(&private->crw_lock, flags); queue_work(vfio_ccw_work_q, &private->crw_work); } diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c index 4d7988ea47ef..5fd94e9d5c61 100644 --- a/drivers/s390/cio/vfio_ccw_fsm.c +++ b/drivers/s390/cio/vfio_ccw_fsm.c @@ -170,8 +170,8 @@ static void fsm_notoper(struct vfio_ccw_private *private, css_sched_sch_todo(sch, SCH_TODO_UNREG); private->state = VFIO_CCW_STATE_NOT_OPER; - /* This is usually handled during CLOSE event */ - cp_free(&private->cp); + /* This routine could be called from IRQ context, so defer */ + queue_work(vfio_ccw_work_q, &private->notoper_work); } /* @@ -410,7 +410,11 @@ static void fsm_close(struct vfio_ccw_private *private, private->state = VFIO_CCW_STATE_STANDBY; spin_unlock_irq(&sch->lock); + + mutex_lock(&private->io_mutex); cp_free(&private->cp); + mutex_unlock(&private->io_mutex); + return; err_unlock: diff --git a/drivers/s390/cio/vfio_ccw_ops.c b/drivers/s390/cio/vfio_ccw_ops.c index ea532a8a4a0c..b7acc2532096 100644 --- a/drivers/s390/cio/vfio_ccw_ops.c +++ b/drivers/s390/cio/vfio_ccw_ops.c @@ -54,6 +54,8 @@ static int vfio_ccw_mdev_init_dev(struct vfio_device *vdev) INIT_LIST_HEAD(&private->crw); INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo); INIT_WORK(&private->crw_work, vfio_ccw_crw_todo); + INIT_WORK(&private->notoper_work, vfio_ccw_notoper_todo); + spin_lock_init(&private->crw_lock); private->cp.guest_cp = kcalloc(CCWCHAIN_LEN_MAX, sizeof(struct ccw1), GFP_KERNEL); @@ -131,11 +133,28 @@ static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev) struct vfio_ccw_private *private = container_of(vdev, struct vfio_ccw_private, vdev); struct vfio_ccw_crw *crw, *temp; + unsigned long flags; + /* + * Ensure these work items are fully drained, so none can + * fire after being released. + * + * notoper_work should have nothing to do here, because only + * open devices could have channel_program resources in use + * and those would be released during close. Nevertheless, + * call flush here as well to be certain anything that was + * allocated is freed. + */ + cancel_work_sync(&private->io_work); + cancel_work_sync(&private->crw_work); + flush_work(&private->notoper_work); + + spin_lock_irqsave(&private->crw_lock, flags); list_for_each_entry_safe(crw, temp, &private->crw, next) { list_del(&crw->next); kfree(crw); } + spin_unlock_irqrestore(&private->crw_lock, flags); kmem_cache_free(vfio_ccw_crw_region, private->crw_region); kmem_cache_free(vfio_ccw_schib_region, private->schib_region); @@ -203,6 +222,19 @@ static void vfio_ccw_mdev_close_device(struct vfio_device *vdev) container_of(vdev, struct vfio_ccw_private, vdev); vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE); + + /* + * Ensure these work items are drained, in the event the + * device is re-opened instead of released. + * + * notoper_work needs to be given a chance to run if it + * is queued, so any memory associated with the channel + * program can be returned. + */ + cancel_work_sync(&private->io_work); + cancel_work_sync(&private->crw_work); + flush_work(&private->notoper_work); + vfio_ccw_unregister_dev_regions(private); } @@ -244,6 +276,7 @@ static ssize_t vfio_ccw_mdev_read(struct vfio_device *vdev, return vfio_ccw_mdev_read_io_region(private, buf, count, ppos); default: index -= VFIO_CCW_NUM_REGIONS; + index = array_index_nospec(index, private->num_regions); return private->region[index].ops->read(private, buf, count, ppos); } @@ -296,6 +329,7 @@ static ssize_t vfio_ccw_mdev_write(struct vfio_device *vdev, return vfio_ccw_mdev_write_io_region(private, buf, count, ppos); default: index -= VFIO_CCW_NUM_REGIONS; + index = array_index_nospec(index, private->num_regions); return private->region[index].ops->write(private, buf, count, ppos); } @@ -338,11 +372,8 @@ static int vfio_ccw_mdev_get_region_info(struct vfio_ccw_private *private, VFIO_CCW_NUM_REGIONS + private->num_regions) return -EINVAL; - info->index = array_index_nospec(info->index, - VFIO_CCW_NUM_REGIONS + - private->num_regions); - i = info->index - VFIO_CCW_NUM_REGIONS; + i = array_index_nospec(i, private->num_regions); info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index); info->size = private->region[i].size; diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_ccw_private.h index b62bbc5c6376..7c16cd570043 100644 --- a/drivers/s390/cio/vfio_ccw_private.h +++ b/drivers/s390/cio/vfio_ccw_private.h @@ -88,7 +88,8 @@ struct vfio_ccw_parent { * @state: internal state of the device * @completion: synchronization helper of the I/O completion * @io_region: MMIO region to input/output I/O arguments/results - * @io_mutex: protect against concurrent update of I/O regions + * @io_mutex: protect against concurrent update of I/O resources + * and @cp lifecycle * @region: additional regions for other subchannel operations * @cmd_region: MMIO region for asynchronous I/O commands other than START * @schib_region: MMIO region for SCHIB information @@ -97,11 +98,14 @@ struct vfio_ccw_parent { * @cp: channel program for the current I/O operation * @irb: irb info received from interrupt * @scsw: scsw info + * @crw_lock: serialization of CRW list information + * @crw: list of Channel Report Word elements * @io_trigger: eventfd ctx for signaling userspace I/O results * @crw_trigger: eventfd ctx for signaling userspace CRW information * @req_trigger: eventfd ctx for signaling userspace to return device * @io_work: work for deferral process of I/O handling * @crw_work: work for deferral process of CRW handling + * @notoper_work: work for deferred processing in not-operational state */ struct vfio_ccw_private { struct vfio_device vdev; @@ -118,6 +122,8 @@ struct vfio_ccw_private { struct channel_program cp; struct irb irb; union scsw scsw; + + spinlock_t crw_lock; struct list_head crw; struct eventfd_ctx *io_trigger; @@ -125,11 +131,13 @@ struct vfio_ccw_private { struct eventfd_ctx *req_trigger; struct work_struct io_work; struct work_struct crw_work; + struct work_struct notoper_work; } __aligned(8); int vfio_ccw_sch_quiesce(struct subchannel *sch); void vfio_ccw_sch_io_todo(struct work_struct *work); void vfio_ccw_crw_todo(struct work_struct *work); +void vfio_ccw_notoper_todo(struct work_struct *work); extern struct mdev_driver vfio_ccw_mdev_driver; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 2861b6b38cb0..8de58b034198 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4710,6 +4710,9 @@ static int qeth_snmp_command(struct qeth_card *card, char __user *udata) if (req_len > QETH_BUFSIZE) return -EINVAL; + if (qinfo.udata_len < sizeof(struct qeth_snmp_ureq_hdr)) + return -EINVAL; + iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL, req_len); if (!iob) return -ENOMEM; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 04c64ce0a1ca..15558d2857b4 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1415,6 +1415,11 @@ static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) rc = -EFAULT; goto out; } + + if (qinfo.udata_len < QETH_QARP_ENTRIES_OFFSET) { + rc = -EINVAL; + goto out; + } qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL); if (!qinfo.udata) { rc = -ENOMEM; diff --git a/drivers/video/fbdev/core/fb_io_fops.c b/drivers/video/fbdev/core/fb_io_fops.c index 3408ff1b2b7a..bb5d268d0da1 100644 --- a/drivers/video/fbdev/core/fb_io_fops.c +++ b/drivers/video/fbdev/core/fb_io_fops.c @@ -60,6 +60,14 @@ ssize_t fb_io_read(struct fb_info *info, char __user *buf, size_t count, loff_t buf += c; cnt += c; count -= c; + + /* + * If there was a partial copy, the user buffer is faulty. + * Break out to avoid over-advancing the src pointer and + * reading out of bounds in the next iteration. + */ + if (trailing) + break; } kfree(buffer); diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 47335a0f4a61..0984ec63f6d7 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1257,7 +1257,7 @@ static int load_elf_binary(struct linux_binprm *bprm) } reloc_func_desc = interp_load_addr; - allow_write_access(interpreter); + exe_file_allow_write_access(interpreter); fput(interpreter); kfree(interp_elf_ex); @@ -1371,7 +1371,7 @@ static int load_elf_binary(struct linux_binprm *bprm) kfree(interp_elf_ex); kfree(interp_elf_phdata); out_free_file: - allow_write_access(interpreter); + exe_file_allow_write_access(interpreter); if (interpreter) fput(interpreter); out_free_ph: diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 0a18471adcb2..32b6ddb327db 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -398,7 +398,7 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) goto error; } - allow_write_access(interpreter); + exe_file_allow_write_access(interpreter); fput(interpreter); interpreter = NULL; } @@ -471,7 +471,7 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) error: if (interpreter) { - allow_write_access(interpreter); + exe_file_allow_write_access(interpreter); fput(interpreter); } kfree(interpreter_name); diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 3c4852bb8ccc..29d77daf9e29 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -161,8 +161,10 @@ static Node *get_binfmt_handler(struct binfmt_misc *misc, static void put_binfmt_handler(Node *e) { if (refcount_dec_and_test(&e->users)) { - if (e->flags & MISC_FMT_OPEN_FILE) + if (e->flags & MISC_FMT_OPEN_FILE) { + exe_file_allow_write_access(e->interp_file); filp_close(e->interp_file, NULL); + } kfree(e); } } @@ -246,8 +248,14 @@ static int load_misc_binary(struct linux_binprm *bprm) if (fmt->flags & MISC_FMT_OPEN_FILE) { interp_file = file_clone_open(fmt->interp_file); - if (!IS_ERR(interp_file)) - deny_write_access(interp_file); + if (!IS_ERR(interp_file)) { + int err = exe_file_deny_write_access(interp_file); + + if (err) { + fput(interp_file); + interp_file = ERR_PTR(err); + } + } } else { interp_file = open_exec(fmt->interpreter); } @@ -958,18 +966,9 @@ static const struct file_operations bm_status_operations = { /* Superblock handling */ -static void bm_put_super(struct super_block *sb) -{ - struct user_namespace *user_ns = sb->s_fs_info; - - sb->s_fs_info = NULL; - put_user_ns(user_ns); -} - static const struct super_operations s_ops = { .statfs = simple_statfs, .evict_inode = bm_evict_inode, - .put_super = bm_put_super, }; static int bm_fill_super(struct super_block *sb, struct fs_context *fc) @@ -1028,13 +1027,12 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc) /* * When the binfmt_misc superblock for this userns is shutdown * ->enabled might have been set to false and we don't reinitialize - * ->enabled again in put_super() as someone might already be mounting - * binfmt_misc again. It also would be pointless since by the time - * ->put_super() is called we know that the binary type list for this - * bintfmt_misc mount is empty making load_misc_binary() return - * -ENOEXEC independent of whether ->enabled is true. Instead, if - * someone mounts binfmt_misc for the first time or again we simply - * reset ->enabled to true. + * ->enabled again during shutdown as someone might already be mounting + * binfmt_misc again. It also would be pointless since by then we know + * that the binary type list for this binfmt_misc mount is empty making + * load_misc_binary() return -ENOEXEC independent of whether ->enabled + * is true. Instead, if someone mounts binfmt_misc for the first time or + * again we simply reset ->enabled to true. */ misc->enabled = true; @@ -1060,6 +1058,14 @@ static const struct fs_context_operations bm_context_ops = { .get_tree = bm_get_tree, }; +static void bm_kill_sb(struct super_block *sb) +{ + struct user_namespace *user_ns = sb->s_fs_info; + + kill_litter_super(sb); + put_user_ns(user_ns); +} + static int bm_init_fs_context(struct fs_context *fc) { fc->ops = &bm_context_ops; @@ -1076,7 +1082,7 @@ static struct file_system_type bm_fs_type = { .name = "binfmt_misc", .init_fs_context = bm_init_fs_context, .fs_flags = FS_USERNS_MOUNT, - .kill_sb = kill_litter_super, + .kill_sb = bm_kill_sb, }; MODULE_ALIAS_FS("binfmt_misc"); diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 3ca24a0845cb..a01c4a5b95db 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -3003,6 +3003,7 @@ int btrfs_inc_block_group_ro(struct btrfs_block_group *cache, bool do_chunk_alloc) { struct btrfs_fs_info *fs_info = cache->fs_info; + struct btrfs_space_info *space_info = cache->space_info; struct btrfs_trans_handle *trans; struct btrfs_root *root = btrfs_block_group_root(fs_info); u64 alloc_flags; @@ -3055,7 +3056,7 @@ int btrfs_inc_block_group_ro(struct btrfs_block_group *cache, */ alloc_flags = btrfs_get_alloc_profile(fs_info, cache->flags); if (alloc_flags != cache->flags) { - ret = btrfs_chunk_alloc(trans, alloc_flags, + ret = btrfs_chunk_alloc(trans, space_info, alloc_flags, CHUNK_ALLOC_FORCE); /* * ENOSPC is allowed here, we may have enough space @@ -3083,15 +3084,15 @@ int btrfs_inc_block_group_ro(struct btrfs_block_group *cache, (cache->flags & BTRFS_BLOCK_GROUP_SYSTEM)) goto unlock_out; - alloc_flags = btrfs_get_alloc_profile(fs_info, cache->space_info->flags); - ret = btrfs_chunk_alloc(trans, alloc_flags, CHUNK_ALLOC_FORCE); + alloc_flags = btrfs_get_alloc_profile(fs_info, space_info->flags); + ret = btrfs_chunk_alloc(trans, space_info, alloc_flags, CHUNK_ALLOC_FORCE); if (ret < 0) goto out; /* * We have allocated a new chunk. We also need to activate that chunk to * grant metadata tickets for zoned filesystem. */ - ret = btrfs_zoned_activate_one_bg(fs_info, cache->space_info, true); + ret = btrfs_zoned_activate_one_bg(space_info, true); if (ret < 0) goto out; @@ -3941,8 +3942,15 @@ static int should_alloc_chunk(const struct btrfs_fs_info *fs_info, int btrfs_force_chunk_alloc(struct btrfs_trans_handle *trans, u64 type) { u64 alloc_flags = btrfs_get_alloc_profile(trans->fs_info, type); + struct btrfs_space_info *space_info; + + space_info = btrfs_find_space_info(trans->fs_info, type); + if (!space_info) { + DEBUG_WARN(); + return -EINVAL; + } - return btrfs_chunk_alloc(trans, alloc_flags, CHUNK_ALLOC_FORCE); + return btrfs_chunk_alloc(trans, space_info, alloc_flags, CHUNK_ALLOC_FORCE); } static struct btrfs_block_group *do_chunk_alloc(struct btrfs_trans_handle *trans, u64 flags) @@ -4138,6 +4146,8 @@ static struct btrfs_block_group *do_chunk_alloc(struct btrfs_trans_handle *trans * * This function, btrfs_chunk_alloc(), belongs to phase 1. * + * @space_info: specify which space_info the new chunk should belong to. + * * If @force is CHUNK_ALLOC_FORCE: * - return 1 if it successfully allocates a chunk, * - return errors including -ENOSPC otherwise. @@ -4146,11 +4156,11 @@ static struct btrfs_block_group *do_chunk_alloc(struct btrfs_trans_handle *trans * - return 1 if it successfully allocates a chunk, * - return errors including -ENOSPC otherwise. */ -int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, u64 flags, +int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, + struct btrfs_space_info *space_info, u64 flags, enum btrfs_chunk_alloc_enum force) { struct btrfs_fs_info *fs_info = trans->fs_info; - struct btrfs_space_info *space_info; struct btrfs_block_group *ret_bg; bool wait_for_alloc = false; bool should_alloc = false; @@ -4189,9 +4199,6 @@ int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, u64 flags, if (flags & BTRFS_BLOCK_GROUP_SYSTEM) return -ENOSPC; - space_info = btrfs_find_space_info(fs_info, flags); - ASSERT(space_info); - do { spin_lock(&space_info->lock); if (force < space_info->force_alloc) @@ -4339,25 +4346,29 @@ static void reserve_chunk_space(struct btrfs_trans_handle *trans, if (IS_ERR(bg)) { ret = PTR_ERR(bg); } else { + int activate_ret; + /* * We have a new chunk. We also need to activate it for * zoned filesystem. */ - ret = btrfs_zoned_activate_one_bg(fs_info, info, true); - if (ret < 0) - return; - - /* - * If we fail to add the chunk item here, we end up - * trying again at phase 2 of chunk allocation, at - * btrfs_create_pending_block_groups(). So ignore - * any error here. An ENOSPC here could happen, due to - * the cases described at do_chunk_alloc() - the system - * block group we just created was just turned into RO - * mode by a scrub for example, or a running discard - * temporarily removed its free space entries, etc. - */ - btrfs_chunk_alloc_add_chunk_item(trans, bg); + activate_ret = btrfs_zoned_activate_one_bg(info, true); + if (activate_ret < 0) { + ret = activate_ret; + } else { + /* + * If we fail to add the chunk item here, we end + * up trying again at phase 2 of chunk allocation, + * at btrfs_create_pending_block_groups(). So + * ignore any error here. An ENOSPC here could + * happen, due to the cases described at + * do_chunk_alloc() - the system block group we + * just created was just turned into RO mode by a + * scrub for example, or a running discard + * temporarily removed its free space entries, etc. + */ + btrfs_chunk_alloc_add_chunk_item(trans, bg); + } } } diff --git a/fs/btrfs/block-group.h b/fs/btrfs/block-group.h index 36937eeab9b8..c01f3af726a1 100644 --- a/fs/btrfs/block-group.h +++ b/fs/btrfs/block-group.h @@ -342,7 +342,8 @@ int btrfs_add_reserved_bytes(struct btrfs_block_group *cache, bool force_wrong_size_class); void btrfs_free_reserved_bytes(struct btrfs_block_group *cache, u64 num_bytes, int delalloc); -int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, u64 flags, +int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, + struct btrfs_space_info *space_info, u64 flags, enum btrfs_chunk_alloc_enum force); int btrfs_force_chunk_alloc(struct btrfs_trans_handle *trans, u64 type); void check_system_chunk(struct btrfs_trans_handle *trans, const u64 type); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 28dca13fb9b0..399c7e10bc14 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4147,6 +4147,7 @@ static int can_allocate_chunk(struct btrfs_fs_info *fs_info, static int find_free_extent_update_loop(struct btrfs_fs_info *fs_info, struct btrfs_key *ins, struct find_free_extent_ctl *ffe_ctl, + struct btrfs_space_info *space_info, bool full_search) { struct btrfs_root *root = fs_info->chunk_root; @@ -4201,7 +4202,7 @@ static int find_free_extent_update_loop(struct btrfs_fs_info *fs_info, return ret; } - ret = btrfs_chunk_alloc(trans, ffe_ctl->flags, + ret = btrfs_chunk_alloc(trans, space_info, ffe_ctl->flags, CHUNK_ALLOC_FORCE_FOR_EXTENT); /* Do not bail out on ENOSPC since we can do more. */ @@ -4601,7 +4602,8 @@ static noinline int find_free_extent(struct btrfs_root *root, } up_read(&space_info->groups_sem); - ret = find_free_extent_update_loop(fs_info, ins, ffe_ctl, full_search); + ret = find_free_extent_update_loop(fs_info, ins, ffe_ctl, space_info, + full_search); if (ret > 0) goto search; diff --git a/fs/btrfs/messages.h b/fs/btrfs/messages.h index 08a9272399d2..66ea41aa3608 100644 --- a/fs/btrfs/messages.h +++ b/fs/btrfs/messages.h @@ -181,6 +181,13 @@ do { \ #define ASSERT(expr) (void)(expr) #endif +#ifdef CONFIG_BTRFS_DEBUG +/* Verbose warning only under debug build. */ +#define DEBUG_WARN(args...) WARN(1, KERN_ERR args) +#else +#define DEBUG_WARN(...) do {} while(0) +#endif + __printf(5, 6) __cold void __btrfs_handle_fs_error(struct btrfs_fs_info *fs_info, const char *function, diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index 2b71ed343b63..402eb3b54fb1 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -850,7 +850,7 @@ static void flush_space(struct btrfs_fs_info *fs_info, ret = PTR_ERR(trans); break; } - ret = btrfs_chunk_alloc(trans, + ret = btrfs_chunk_alloc(trans, space_info, btrfs_get_alloc_profile(fs_info, space_info->flags), (state == ALLOC_CHUNK) ? CHUNK_ALLOC_NO_FORCE : CHUNK_ALLOC_FORCE); diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 0a147bc5f090..da837e034030 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -764,9 +764,10 @@ start_transaction(struct btrfs_root *root, unsigned int num_items, * value here. */ if (do_chunk_alloc && num_bytes) { - u64 flags = h->block_rsv->space_info->flags; + struct btrfs_space_info *space_info = h->block_rsv->space_info; + u64 flags = space_info->flags; - btrfs_chunk_alloc(h, btrfs_get_alloc_profile(fs_info, flags), + btrfs_chunk_alloc(h, space_info, btrfs_get_alloc_profile(fs_info, flags), CHUNK_ALLOC_NO_FORCE); } diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index c64840b15597..46a792e113ee 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -2825,10 +2825,9 @@ int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info) return ret < 0 ? ret : 1; } -int btrfs_zoned_activate_one_bg(struct btrfs_fs_info *fs_info, - struct btrfs_space_info *space_info, - bool do_finish) +int btrfs_zoned_activate_one_bg(struct btrfs_space_info *space_info, bool do_finish) { + struct btrfs_fs_info *fs_info = space_info->fs_info; struct btrfs_block_group *bg; int index; diff --git a/fs/btrfs/zoned.h b/fs/btrfs/zoned.h index f7171ab6ed71..224e1889dc82 100644 --- a/fs/btrfs/zoned.h +++ b/fs/btrfs/zoned.h @@ -93,8 +93,7 @@ bool btrfs_zoned_should_reclaim(const struct btrfs_fs_info *fs_info); void btrfs_zoned_release_data_reloc_bg(struct btrfs_fs_info *fs_info, u64 logical, u64 length); int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info); -int btrfs_zoned_activate_one_bg(struct btrfs_fs_info *fs_info, - struct btrfs_space_info *space_info, bool do_finish); +int btrfs_zoned_activate_one_bg(struct btrfs_space_info *space_info, bool do_finish); void btrfs_check_active_zone_reservation(struct btrfs_fs_info *fs_info); #else /* CONFIG_BLK_DEV_ZONED */ @@ -258,8 +257,7 @@ static inline int btrfs_zone_finish_one_bg(struct btrfs_fs_info *fs_info) return 1; } -static inline int btrfs_zoned_activate_one_bg(struct btrfs_fs_info *fs_info, - struct btrfs_space_info *space_info, +static inline int btrfs_zoned_activate_one_bg(struct btrfs_space_info *space_info, bool do_finish) { /* Consider all the block groups are active */ diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 8d1a646e229a..e5ea8fb6d9a6 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -670,22 +670,23 @@ static u64 get_writepages_data_length(struct inode *inode, } /* - * Write a single page, but leave the page locked. + * Write a folio, but leave it locked. * * If we get a write error, mark the mapping for error, but still adjust the - * dirty page accounting (i.e., page is no longer dirty). + * dirty page accounting (i.e., folio is no longer dirty). */ -static int writepage_nounlock(struct page *page, struct writeback_control *wbc) +static int write_folio_nounlock(struct folio *folio, + struct writeback_control *wbc) { - struct folio *folio = page_folio(page); - struct inode *inode = page->mapping->host; + struct page *page = &folio->page; + struct inode *inode = folio->mapping->host; struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); struct ceph_client *cl = fsc->client; struct ceph_snap_context *snapc, *oldest; - loff_t page_off = page_offset(page); + loff_t page_off = folio_pos(folio); int err; - loff_t len = thp_size(page); + loff_t len = folio_size(folio); loff_t wlen; struct ceph_writeback_ctl ceph_wbc; struct ceph_osd_client *osdc = &fsc->client->osdc; @@ -693,27 +694,27 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) bool caching = ceph_is_cache_enabled(inode); struct page *bounce_page = NULL; - doutc(cl, "%llx.%llx page %p idx %lu\n", ceph_vinop(inode), page, - page->index); + doutc(cl, "%llx.%llx folio %p idx %lu\n", ceph_vinop(inode), folio, + folio->index); if (ceph_inode_is_shutdown(inode)) return -EIO; /* verify this is a writeable snap context */ - snapc = page_snap_context(page); + snapc = page_snap_context(&folio->page); if (!snapc) { - doutc(cl, "%llx.%llx page %p not dirty?\n", ceph_vinop(inode), - page); + doutc(cl, "%llx.%llx folio %p not dirty?\n", ceph_vinop(inode), + folio); return 0; } oldest = get_oldest_context(inode, &ceph_wbc, snapc); if (snapc->seq > oldest->seq) { - doutc(cl, "%llx.%llx page %p snapc %p not writeable - noop\n", - ceph_vinop(inode), page, snapc); + doutc(cl, "%llx.%llx folio %p snapc %p not writeable - noop\n", + ceph_vinop(inode), folio, snapc); /* we should only noop if called by kswapd */ WARN_ON(!(current->flags & PF_MEMALLOC)); ceph_put_snap_context(oldest); - redirty_page_for_writepage(wbc, page); + folio_redirty_for_writepage(wbc, folio); return 0; } ceph_put_snap_context(oldest); @@ -730,8 +731,8 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) len = ceph_wbc.i_size - page_off; wlen = IS_ENCRYPTED(inode) ? round_up(len, CEPH_FSCRYPT_BLOCK_SIZE) : len; - doutc(cl, "%llx.%llx page %p index %lu on %llu~%llu snapc %p seq %lld\n", - ceph_vinop(inode), page, page->index, page_off, wlen, snapc, + doutc(cl, "%llx.%llx folio %p index %lu on %llu~%llu snapc %p seq %lld\n", + ceph_vinop(inode), folio, folio->index, page_off, wlen, snapc, snapc->seq); if (atomic_long_inc_return(&fsc->writeback_count) > @@ -744,32 +745,38 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) ceph_wbc.truncate_seq, ceph_wbc.truncate_size, true); if (IS_ERR(req)) { - redirty_page_for_writepage(wbc, page); + folio_redirty_for_writepage(wbc, folio); + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; return PTR_ERR(req); } if (wlen < len) len = wlen; - set_page_writeback(page); + folio_start_writeback(folio); if (caching) - ceph_set_page_fscache(page); + ceph_set_page_fscache(&folio->page); ceph_fscache_write_to_cache(inode, page_off, len, caching); if (IS_ENCRYPTED(inode)) { - bounce_page = fscrypt_encrypt_pagecache_blocks(page, + bounce_page = fscrypt_encrypt_pagecache_blocks(&folio->page, CEPH_FSCRYPT_BLOCK_SIZE, 0, GFP_NOFS); if (IS_ERR(bounce_page)) { - redirty_page_for_writepage(wbc, page); - end_page_writeback(page); + folio_redirty_for_writepage(wbc, folio); + folio_end_writeback(folio); ceph_osdc_put_request(req); + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; return PTR_ERR(bounce_page); } } /* it may be a short write due to an object boundary */ - WARN_ON_ONCE(len > thp_size(page)); + WARN_ON_ONCE(len > folio_size(folio)); osd_req_op_extent_osd_data_pages(req, 0, bounce_page ? &bounce_page : &page, wlen, 0, false, false); @@ -795,25 +802,28 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) if (err == -ERESTARTSYS) { /* killed by SIGKILL */ doutc(cl, "%llx.%llx interrupted page %p\n", - ceph_vinop(inode), page); - redirty_page_for_writepage(wbc, page); - end_page_writeback(page); + ceph_vinop(inode), folio); + folio_redirty_for_writepage(wbc, folio); + folio_end_writeback(folio); + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; return err; } if (err == -EBLOCKLISTED) fsc->blocklisted = true; - doutc(cl, "%llx.%llx setting page/mapping error %d %p\n", - ceph_vinop(inode), err, page); + doutc(cl, "%llx.%llx setting mapping error %d %p\n", + ceph_vinop(inode), err, folio); mapping_set_error(&inode->i_data, err); wbc->pages_skipped++; } else { doutc(cl, "%llx.%llx cleaned page %p\n", - ceph_vinop(inode), page); + ceph_vinop(inode), folio); err = 0; /* vfs expects us to return 0 */ } - oldest = detach_page_private(page); + oldest = folio_detach_private(folio); WARN_ON_ONCE(oldest != snapc); - end_page_writeback(page); + folio_end_writeback(folio); ceph_put_wrbuffer_cap_refs(ci, 1, snapc); ceph_put_snap_context(snapc); /* page's reference */ @@ -824,32 +834,6 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) return err; } -static int ceph_writepage(struct page *page, struct writeback_control *wbc) -{ - int err; - struct inode *inode = page->mapping->host; - BUG_ON(!inode); - ihold(inode); - - if (wbc->sync_mode == WB_SYNC_NONE && - ceph_inode_to_fs_client(inode)->write_congested) { - redirty_page_for_writepage(wbc, page); - return AOP_WRITEPAGE_ACTIVATE; - } - - folio_wait_private_2(page_folio(page)); /* [DEPRECATED] */ - - err = writepage_nounlock(page, wbc); - if (err == -ERESTARTSYS) { - /* direct memory reclaimer was killed by SIGKILL. return 0 - * to prevent caller from setting mapping/page error */ - err = 0; - } - unlock_page(page); - iput(inode); - return err; -} - /* * async writeback completion handler. * @@ -1451,56 +1435,56 @@ static int context_is_writeable_or_written(struct inode *inode, /** * ceph_find_incompatible - find an incompatible context and return it - * @page: page being dirtied + * @folio: folio being dirtied * - * We are only allowed to write into/dirty a page if the page is + * We are only allowed to write into/dirty a folio if the folio is * clean, or already dirty within the same snap context. Returns a * conflicting context if there is one, NULL if there isn't, or a * negative error code on other errors. * - * Must be called with page lock held. + * Must be called with folio lock held. */ static struct ceph_snap_context * -ceph_find_incompatible(struct page *page) +ceph_find_incompatible(struct folio *folio) { - struct inode *inode = page->mapping->host; + struct inode *inode = folio->mapping->host; struct ceph_client *cl = ceph_inode_to_client(inode); struct ceph_inode_info *ci = ceph_inode(inode); if (ceph_inode_is_shutdown(inode)) { - doutc(cl, " %llx.%llx page %p is shutdown\n", - ceph_vinop(inode), page); + doutc(cl, " %llx.%llx folio %p is shutdown\n", + ceph_vinop(inode), folio); return ERR_PTR(-ESTALE); } for (;;) { struct ceph_snap_context *snapc, *oldest; - wait_on_page_writeback(page); + folio_wait_writeback(folio); - snapc = page_snap_context(page); + snapc = page_snap_context(&folio->page); if (!snapc || snapc == ci->i_head_snapc) break; /* - * this page is already dirty in another (older) snap + * this folio is already dirty in another (older) snap * context! is it writeable now? */ oldest = get_oldest_context(inode, NULL, NULL); if (snapc->seq > oldest->seq) { /* not writeable -- return it for the caller to deal with */ ceph_put_snap_context(oldest); - doutc(cl, " %llx.%llx page %p snapc %p not current or oldest\n", - ceph_vinop(inode), page, snapc); + doutc(cl, " %llx.%llx folio %p snapc %p not current or oldest\n", + ceph_vinop(inode), folio, snapc); return ceph_get_snap_context(snapc); } ceph_put_snap_context(oldest); - /* yay, writeable, do it now (without dropping page lock) */ - doutc(cl, " %llx.%llx page %p snapc %p not current, but oldest\n", - ceph_vinop(inode), page, snapc); - if (clear_page_dirty_for_io(page)) { - int r = writepage_nounlock(page, NULL); + /* yay, writeable, do it now (without dropping folio lock) */ + doutc(cl, " %llx.%llx folio %p snapc %p not current, but oldest\n", + ceph_vinop(inode), folio, snapc); + if (folio_clear_dirty_for_io(folio)) { + int r = write_folio_nounlock(folio, NULL); if (r < 0) return ERR_PTR(r); } @@ -1515,7 +1499,7 @@ static int ceph_netfs_check_write_begin(struct file *file, loff_t pos, unsigned struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_snap_context *snapc; - snapc = ceph_find_incompatible(folio_page(*foliop, 0)); + snapc = ceph_find_incompatible(*foliop); if (snapc) { int r; @@ -1598,7 +1582,6 @@ static int ceph_write_end(struct file *file, struct address_space *mapping, const struct address_space_operations ceph_aops = { .read_folio = netfs_read_folio, .readahead = netfs_readahead, - .writepage = ceph_writepage, .writepages = ceph_writepages_start, .write_begin = ceph_write_begin, .write_end = ceph_write_end, @@ -1606,6 +1589,7 @@ const struct address_space_operations ceph_aops = { .invalidate_folio = ceph_invalidate_folio, .release_folio = netfs_release_folio, .direct_IO = noop_direct_IO, + .migrate_folio = filemap_migrate_folio, }; static void ceph_block_sigs(sigset_t *oldset) @@ -1722,8 +1706,8 @@ static vm_fault_t ceph_page_mkwrite(struct vm_fault *vmf) struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_file_info *fi = vma->vm_file->private_data; struct ceph_cap_flush *prealloc_cf; - struct page *page = vmf->page; - loff_t off = page_offset(page); + struct folio *folio = page_folio(vmf->page); + loff_t off = folio_pos(folio); loff_t size = i_size_read(inode); size_t len; int want, got, err; @@ -1740,10 +1724,10 @@ static vm_fault_t ceph_page_mkwrite(struct vm_fault *vmf) sb_start_pagefault(inode->i_sb); ceph_block_sigs(&oldset); - if (off + thp_size(page) <= size) - len = thp_size(page); + if (off + folio_size(folio) <= size) + len = folio_size(folio); else - len = offset_in_thp(page, size); + len = offset_in_folio(folio, size); doutc(cl, "%llx.%llx %llu~%zd getting caps i_size %llu\n", ceph_vinop(inode), off, len, size); @@ -1760,30 +1744,30 @@ static vm_fault_t ceph_page_mkwrite(struct vm_fault *vmf) doutc(cl, "%llx.%llx %llu~%zd got cap refs on %s\n", ceph_vinop(inode), off, len, ceph_cap_string(got)); - /* Update time before taking page lock */ + /* Update time before taking folio lock */ file_update_time(vma->vm_file); inode_inc_iversion_raw(inode); do { struct ceph_snap_context *snapc; - lock_page(page); + folio_lock(folio); - if (page_mkwrite_check_truncate(page, inode) < 0) { - unlock_page(page); + if (folio_mkwrite_check_truncate(folio, inode) < 0) { + folio_unlock(folio); ret = VM_FAULT_NOPAGE; break; } - snapc = ceph_find_incompatible(page); + snapc = ceph_find_incompatible(folio); if (!snapc) { - /* success. we'll keep the page locked. */ - set_page_dirty(page); + /* success. we'll keep the folio locked. */ + folio_mark_dirty(folio); ret = VM_FAULT_LOCKED; break; } - unlock_page(page); + folio_unlock(folio); if (IS_ERR(snapc)) { ret = VM_FAULT_SIGBUS; diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 52e02996d2eb..b04465ae4ce8 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -3104,7 +3104,19 @@ int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi, int need, ret = -ERESTARTSYS; break; } - wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); + + /* + * If a cap update is lost after + * mds_wanted was raised, waiting + * forever will never make progress. + * Retry the renew path periodically + * so we can resend synchronously. + */ + if (!wait_woken(&wait, TASK_INTERRUPTIBLE, + CEPH_GET_CAPS_WAIT_TIMEOUT)) { + ret = -EUCLEAN; + break; + } } remove_wait_queue(&ci->i_cap_wq, &wait); @@ -3138,7 +3150,8 @@ int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi, int need, continue; } if (ret == -EUCLEAN) { - /* session was killed, try renew caps */ + /* session was killed or a waited cap + * request needs a retry */ ret = ceph_renew_caps(inode, flags); if (ret == 0) continue; diff --git a/fs/ceph/file.c b/fs/ceph/file.c index b21bd6b84c49..d86c1285fb02 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -295,7 +295,7 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode) } /* - * try renew caps after session gets killed. + * Retry cap acquisition after a stale session or a lost cap update. */ int ceph_renew_caps(struct inode *inode, int fmode) { @@ -303,14 +303,15 @@ int ceph_renew_caps(struct inode *inode, int fmode) struct ceph_client *cl = mdsc->fsc->client; struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_mds_request *req; - int err, flags, wanted; + int err, flags, wanted, issued; spin_lock(&ci->i_ceph_lock); __ceph_touch_fmode(ci, mdsc, fmode); wanted = __ceph_caps_file_wanted(ci); + issued = __ceph_caps_issued(ci, NULL); if (__ceph_is_any_real_caps(ci) && - (!(wanted & CEPH_CAP_ANY_WR) || ci->i_auth_cap)) { - int issued = __ceph_caps_issued(ci, NULL); + (!(wanted & CEPH_CAP_ANY_WR) || ci->i_auth_cap) && + (issued & wanted) == wanted) { spin_unlock(&ci->i_ceph_lock); doutc(cl, "%p %llx.%llx want %s issued %s updating mds_wanted\n", inode, ceph_vinop(inode), ceph_cap_string(wanted), diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 7188378df704..5f2b70b846e7 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -3842,6 +3843,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) struct ceph_mds_reply_head *head = msg->front.iov_base; struct ceph_mds_reply_info_parsed *rinfo; /* parsed reply info */ struct ceph_snap_realm *realm; + unsigned int nofs_flags; u64 tid; int err, result; int mds = session->s_mds; @@ -3984,6 +3986,14 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) /* insert trace into our cache */ mutex_lock(&req->r_fill_mutex); + + /* disable fs reclaim while we are using current->journal_info + * for our own purposes, or else shrinkers of other + * filesystems might dereference this pointer as a different + * type + */ + nofs_flags = memalloc_nofs_save(); + current->journal_info = req; err = ceph_fill_trace(mdsc->fsc->sb, req); if (err == 0) { @@ -3992,6 +4002,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) err = ceph_readdir_prepopulate(req, req->r_session); } current->journal_info = NULL; + memalloc_nofs_restore(nofs_flags); mutex_unlock(&req->r_fill_mutex); up_read(&mdsc->snap_rwsem); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 0a602080d8ef..8987d0ff9625 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -74,6 +74,7 @@ struct ceph_fs_client; struct ceph_cap; #define MDS_AUTH_UID_ANY -1 +#define CEPH_GET_CAPS_WAIT_TIMEOUT (5 * HZ) struct ceph_mds_cap_match { s64 uid; /* default to MDS_AUTH_UID_ANY */ diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index b228e5ecfb92..97ecf272a37d 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -15,7 +15,7 @@ #include "super.h" #define CEPH_MDS_IS_READY(i, ignore_laggy) \ - (m->m_info[i].state > 0 && ignore_laggy ? true : !m->m_info[i].laggy) + (m->m_info[i].state > 0 && (ignore_laggy ? true : !m->m_info[i].laggy)) static int __mdsmap_get_random_mds(struct ceph_mdsmap *m, bool ignore_laggy) { diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig index d7e004f8cfed..2d36d7b1b344 100644 --- a/fs/erofs/Kconfig +++ b/fs/erofs/Kconfig @@ -117,7 +117,8 @@ config EROFS_FS_ZIP_LZMA config EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS int "EROFS LZMA default maximum decompression streams" depends on EROFS_FS_ZIP_LZMA - range 1 NR_CPUS + range 1 NR_CPUS if SMP + range 1 1 if !SMP default 16 help By default EROFS allocates one LZMA decompression stream per CPU. diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 5a19edda2b77..665a1528ab57 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -404,11 +404,13 @@ static struct kmem_cache *pwq_cache __ro_after_init; /* * List of files with newly added links, where we may need to limit the number - * of emanating paths. Protected by the epnested_mutex. + * of emanating paths. Protected by the epnested_mutex. The ->file field holds + * a reference to the associated file while the head is on the list. */ struct epitems_head { struct hlist_head epitems; struct epitems_head *next; + struct file *file; }; static struct epitems_head *tfile_check_list = EP_UNACTIVE_PTR; @@ -426,6 +428,16 @@ static void list_file(struct file *file) head = container_of(file->f_ep, struct epitems_head, epitems); if (!head->next) { + /* + * The caller owns a reference to @file or holds the ep->mtx for the + * epitem that led here. The latter blocks eventpoll_release_file() + * before the file allocation can be freed and reused. A dying leaf + * can be skipped since removing links cannot increase the reverse + * path count. + */ + if (!atomic_long_inc_not_zero(&file->f_count)) + return; + head->file = file; head->next = tfile_check_list; tfile_check_list = head; } @@ -435,15 +447,18 @@ static void unlist_file(struct epitems_head *head) { struct epitems_head *to_free = head; struct hlist_node *p = rcu_dereference(hlist_first_rcu(&head->epitems)); + struct file *file = head->file; if (p) { struct epitem *epi= container_of(p, struct epitem, fllink); spin_lock(&epi->ffd.file->f_lock); if (!hlist_empty(&head->epitems)) to_free = NULL; head->next = NULL; + head->file = NULL; spin_unlock(&epi->ffd.file->f_lock); } free_ephead(to_free); + fput(file); } #ifdef CONFIG_SYSCTL diff --git a/fs/exec.c b/fs/exec.c index 20e165d48d3b..8d9775e15cd2 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -912,7 +912,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags) path_noexec(&file->f_path)) return ERR_PTR(-EACCES); - err = deny_write_access(file); + err = exe_file_deny_write_access(file); if (err) return ERR_PTR(err); @@ -927,7 +927,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags) * Returns ERR_PTR on failure or allocated struct file on success. * * As this is a wrapper for the internal do_open_execat(), callers - * must call allow_write_access() before fput() on release. Also see + * must call exe_file_allow_write_access() before fput() on release. Also see * do_close_execat(). */ struct file *open_exec(const char *name) @@ -1501,7 +1501,7 @@ static void do_close_execat(struct file *file) { if (!file) return; - allow_write_access(file); + exe_file_allow_write_access(file); fput(file); } @@ -1835,7 +1835,7 @@ static int exec_binprm(struct linux_binprm *bprm) bprm->file = bprm->interpreter; bprm->interpreter = NULL; - allow_write_access(exec); + exe_file_allow_write_access(exec); if (unlikely(bprm->have_execfd)) { if (bprm->executable) { fput(exec); diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 68891c086b38..16c15360605b 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -950,7 +950,7 @@ int f2fs_merge_page_bio(struct f2fs_io_info *fio) wbc_account_cgroup_owner(fio->io_wbc, page_folio(fio->page), PAGE_SIZE); - inc_page_count(fio->sbi, WB_DATA_TYPE(page, false)); + inc_page_count(fio->sbi, WB_DATA_TYPE(fio->page, false)); *fio->last_block = fio->new_blkaddr; *fio->bio = bio; diff --git a/fs/notify/fanotify/Kconfig b/fs/notify/fanotify/Kconfig index a511f9d8677b..0e36aaf379b7 100644 --- a/fs/notify/fanotify/Kconfig +++ b/fs/notify/fanotify/Kconfig @@ -15,7 +15,6 @@ config FANOTIFY config FANOTIFY_ACCESS_PERMISSIONS bool "fanotify permissions checking" depends on FANOTIFY - depends on SECURITY default n help Say Y here is you want fanotify listeners to be able to make permissions diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index f9b58a201fa3..528249a3a5c1 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -630,6 +630,44 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir, } EXPORT_SYMBOL_GPL(fsnotify); +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS +/* + * At open time we check fsnotify_sb_has_priority_watchers() and set the + * FMODE_NONOTIFY_ mode bits accordignly. + * Later, fsnotify permission hooks do not check if there are permission event + * watches, but that there were permission event watches at open time. + */ +void file_set_fsnotify_mode(struct file *file) +{ + struct super_block *sb = file->f_path.dentry->d_sb; + + /* Is it a file opened by fanotify? */ + if (FMODE_FSNOTIFY_NONE(file->f_mode)) + return; + + /* + * Permission events is a super set of pre-content events, so if there + * are no permission event watchers, there are also no pre-content event + * watchers and this is implied from the single FMODE_NONOTIFY_PERM bit. + */ + if (likely(!fsnotify_sb_has_priority_watchers(sb, + FSNOTIFY_PRIO_CONTENT))) { + file->f_mode |= FMODE_NONOTIFY_PERM; + return; + } + + /* + * If there are permission event watchers but no pre-content event + * watchers, set FMODE_NONOTIFY | FMODE_NONOTIFY_PERM to indicate that. + */ + if (likely(!fsnotify_sb_has_priority_watchers(sb, + FSNOTIFY_PRIO_PRE_CONTENT))) { + file->f_mode |= FMODE_NONOTIFY | FMODE_NONOTIFY_PERM; + return; + } +} +#endif + static __init int fsnotify_init(void) { int ret; diff --git a/fs/open.c b/fs/open.c index be7b55260a75..c591da3cb922 100644 --- a/fs/open.c +++ b/fs/open.c @@ -912,7 +912,7 @@ static int do_dentry_open(struct file *f, f->f_sb_err = file_sample_sb_err(f); if (unlikely(f->f_flags & O_PATH)) { - f->f_mode = FMODE_PATH | FMODE_OPENED; + f->f_mode = FMODE_PATH | FMODE_OPENED | FMODE_NONOTIFY; f->f_op = &empty_fops; return 0; } @@ -940,6 +940,16 @@ static int do_dentry_open(struct file *f, if (error) goto cleanup_all; + /* + * Set FMODE_NONOTIFY_* bits according to existing permission watches. + * If FMODE_NONOTIFY was already set for an fanotify fd, this doesn't + * change anything. + */ + file_set_fsnotify_mode(f); + error = fsnotify_open_perm(f); + if (error) + goto cleanup_all; + error = break_lease(file_inode(f), f->f_flags); if (error) goto cleanup_all; diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 2f666c92b027..08e7931cdda2 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -359,11 +359,27 @@ static long cifs_fallocate(struct file *file, int mode, loff_t off, loff_t len) struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); struct TCP_Server_Info *server = tcon->ses->server; + struct inode *inode = file_inode(file); + int rc; + + if (!server->ops->fallocate) + return -EOPNOTSUPP; + + rc = inode_lock_killable(inode); + if (rc) + return rc; + + netfs_wait_for_outstanding_io(inode); - if (server->ops->fallocate) - return server->ops->fallocate(file, tcon, mode, off, len); + rc = file_modified(file); + if (rc) + goto out_unlock; + + rc = server->ops->fallocate(file, tcon, mode, off, len); - return -EOPNOTSUPP; +out_unlock: + inode_unlock(inode); + return rc; } static int cifs_permission(struct mnt_idmap *idmap, diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index c054ee442694..3f9c03828cd4 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -606,27 +606,6 @@ struct smb_version_operations { const char *symname); }; -struct smb_version_values { - char *version_string; - __u16 protocol_id; - __u32 req_capabilities; - __u32 large_lock_type; - __u32 exclusive_lock_type; - __u32 shared_lock_type; - __u32 unlock_lock_type; - size_t header_preamble_size; - size_t header_size; - size_t max_header_size; - size_t read_rsp_size; - __le16 lock_cmd; - unsigned int cap_unix; - unsigned int cap_nt_find; - unsigned int cap_large_files; - __u16 signing_enabled; - __u16 signing_required; - size_t create_lease_size; -}; - #define HEADER_SIZE(server) (server->vals->header_size) #define MAX_HEADER_SIZE(server) (server->vals->max_header_size) #define HEADER_PREAMBLE_SIZE(server) (server->vals->header_preamble_size) @@ -664,12 +643,6 @@ struct cifs_mnt_data { int flags; }; -static inline unsigned int -get_rfc1002_length(void *buf) -{ - return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; -} - struct TCP_Server_Info { struct list_head tcp_ses_list; struct list_head smb_ses_list; diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index 882d535d17a4..01c78d9e7ec0 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -597,7 +597,7 @@ CIFSSMBEcho(struct TCP_Server_Info *server) iov[0].iov_len = 4; iov[0].iov_base = smb; - iov[1].iov_len = get_rfc1002_length(smb); + iov[1].iov_len = get_rfc1002_len(smb); iov[1].iov_base = (char *)smb + 4; rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL, @@ -1387,7 +1387,7 @@ cifs_async_readv(struct cifs_io_subrequest *rdata) rdata->iov[0].iov_base = smb; rdata->iov[0].iov_len = 4; rdata->iov[1].iov_base = (char *)smb + 4; - rdata->iov[1].iov_len = get_rfc1002_length(smb); + rdata->iov[1].iov_len = get_rfc1002_len(smb); rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive, cifs_readv_callback, NULL, rdata, 0, NULL); @@ -1743,7 +1743,7 @@ cifs_async_writev(struct cifs_io_subrequest *wdata) /* 4 for RFC1001 length + 1 for BCC */ iov[0].iov_len = 4; iov[0].iov_base = smb; - iov[1].iov_len = get_rfc1002_length(smb) + 1; + iov[1].iov_len = get_rfc1002_len(smb) + 1; iov[1].iov_base = (char *)smb + 4; rqst.rq_iov = iov; diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index a8c72b5f4818..52ac9b096a40 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -1213,7 +1213,7 @@ cifs_demultiplex_thread(void *p) * The right amount was read from socket - 4 bytes, * so we can now interpret the length field. */ - pdu_length = get_rfc1002_length(buf); + pdu_length = get_rfc1002_len(buf); cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length); if (!is_smb_response(server, buf[0])) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index e7fc8d0aebee..b67b316538b8 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -2965,6 +2965,8 @@ void cifs_setsize(struct inode *inode, loff_t offset) /* Cached inode must be refreshed on truncate */ cifs_i->time = 0; truncate_pagecache(inode, offset); + netfs_wait_for_outstanding_io(inode); + fscache_resize_cookie(cifs_inode_cookie(inode), offset); } static int diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 61309fead04e..e99b8e1f66d4 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3315,7 +3315,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid, ses->Suid, offset, len); - inode_lock(inode); filemap_invalidate_lock(inode->i_mapping); i_size = i_size_read(inode); @@ -3333,6 +3332,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, * first, otherwise the data may be inconsistent with the server. */ truncate_pagecache_range(inode, offset, offset + len - 1); + netfs_wait_for_outstanding_io(inode); /* if file not oplocked can't be sure whether asking to extend size */ rc = -EOPNOTSUPP; @@ -3361,7 +3361,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, zero_range_exit: filemap_invalidate_unlock(inode->i_mapping); - inode_unlock(inode); free_xid(xid); if (rc) trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid, @@ -3385,7 +3384,6 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, xid = get_xid(); - inode_lock(inode); /* Need to make file sparse, if not already, before freeing range. */ /* Consider adding equivalent for compressed since it could also work */ rc = smb2_set_sparse(xid, tcon, cfile, inode, set_sparse); @@ -3398,6 +3396,7 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, * caches first, otherwise the data may be inconsistent with the server. */ truncate_pagecache_range(inode, offset, offset + len - 1); + netfs_wait_for_outstanding_io(inode); cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); @@ -3432,7 +3431,6 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, unlock: filemap_invalidate_unlock(inode->i_mapping); out: - inode_unlock(inode); free_xid(xid); return rc; } @@ -3709,8 +3707,6 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, xid = get_xid(); - inode_lock(inode); - old_eof = i_size_read(inode); if ((off >= old_eof) || off + len >= old_eof) { @@ -3725,6 +3721,7 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, truncate_pagecache_range(inode, off, old_eof); ictx->zero_point = old_eof; + netfs_wait_for_outstanding_io(inode); rc = smb2_copychunk_range(xid, cfile, cfile, off + len, old_eof - off - len, off); @@ -3745,8 +3742,7 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, fscache_resize_cookie(cifs_inode_cookie(inode), new_eof); out_2: filemap_invalidate_unlock(inode->i_mapping); - out: - inode_unlock(inode); +out: free_xid(xid); return rc; } @@ -3763,8 +3759,6 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, xid = get_xid(); - inode_lock(inode); - old_eof = i_size_read(inode); if (off >= old_eof) { rc = -EINVAL; @@ -3779,6 +3773,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, if (rc < 0) goto out_2; truncate_pagecache_range(inode, off, old_eof); + netfs_wait_for_outstanding_io(inode); rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, cfile->pid, new_eof); @@ -3801,8 +3796,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, rc = 0; out_2: filemap_invalidate_unlock(inode->i_mapping); - out: - inode_unlock(inode); +out: free_xid(xid); return rc; } diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index 98c27dda2410..d7dbbd908cd9 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -886,7 +886,7 @@ SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, int resp_buf_type; iov[0].iov_base = in_buf; - iov[0].iov_len = get_rfc1002_length(in_buf) + 4; + iov[0].iov_len = get_rfc1002_len(in_buf) + 4; flags |= CIFS_NO_RSP_BUF; rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov); cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc); @@ -946,7 +946,7 @@ int cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, bool log_error) { - unsigned int len = get_rfc1002_length(mid->resp_buf) + 4; + unsigned int len = get_rfc1002_len(mid->resp_buf) + 4; dump_smb(mid->resp_buf, min_t(u32, 92, len)); @@ -1466,7 +1466,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, goto out; } - *pbytes_returned = get_rfc1002_length(midQ->resp_buf); + *pbytes_returned = get_rfc1002_len(midQ->resp_buf); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); rc = cifs_check_receive(midQ, server, 0); out: @@ -1651,7 +1651,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, goto out; } - *pbytes_returned = get_rfc1002_length(midQ->resp_buf); + *pbytes_returned = get_rfc1002_len(midQ->resp_buf); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); rc = cifs_check_receive(midQ, server, 0); out: diff --git a/fs/smb/common/cifsglob.h b/fs/smb/common/cifsglob.h index 00fd215e3eb5..371160fec1cd 100644 --- a/fs/smb/common/cifsglob.h +++ b/fs/smb/common/cifsglob.h @@ -9,6 +9,42 @@ #ifndef _COMMON_CIFS_GLOB_H #define _COMMON_CIFS_GLOB_H +struct smb_version_values { + char *version_string; + __u16 protocol_id; + __le16 lock_cmd; + __u32 req_capabilities; + __u32 max_read_size; + __u32 max_write_size; + __u32 max_trans_size; + __u32 max_credits; + __u32 large_lock_type; + __u32 exclusive_lock_type; + __u32 shared_lock_type; + __u32 unlock_lock_type; + size_t header_preamble_size; + size_t header_size; + size_t max_header_size; + size_t read_rsp_size; + unsigned int cap_unix; + unsigned int cap_nt_find; + unsigned int cap_large_files; + unsigned int cap_unicode; + __u16 signing_enabled; + __u16 signing_required; + size_t create_lease_size; + size_t create_durable_size; + size_t create_durable_v2_size; + size_t create_mxac_size; + size_t create_disk_id_size; + size_t create_posix_size; +}; + +static inline unsigned int get_rfc1002_len(void *buf) +{ + return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; +} + static inline void inc_rfc1001_len(void *buf, int count) { be32_add_cpu((__be32 *)buf, count); diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index fea62d1cc473..bd2e7a8cfbb3 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -920,7 +920,7 @@ int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, __u8 *pi_hash) { int rc; - struct smb2_hdr *rcv_hdr = smb2_get_msg(buf); + struct smb2_hdr *rcv_hdr = smb_get_msg(buf); char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; int msg_size = get_rfc1002_len(buf); struct ksmbd_crypto_ctx *ctx = NULL; @@ -1108,7 +1108,7 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, unsigned int nvec, int enc) { struct ksmbd_conn *conn = work->conn; - struct smb2_transform_hdr *tr_hdr = smb2_get_msg(iov[0].iov_base); + struct smb2_transform_hdr *tr_hdr = smb_get_msg(iov[0].iov_base); unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; int rc; DECLARE_CRYPTO_WAIT(wait); diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index 8af6e9621d32..5a896b146f43 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -376,8 +376,11 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn) return true; } -#define SMB1_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb_hdr)) -#define SMB2_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr) + 4) +/* "+2" for BCC field (ByteCount, 2 bytes) */ +#define SMB1_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb_hdr) + 2) +#define SMB2_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb2_pdu)) +#define SMB2_TRANSFORM_MIN_SUPPORTED_PDU_SIZE \ + (sizeof(struct smb2_transform_hdr) + sizeof(struct smb2_hdr)) /** * ksmbd_conn_handler_loop() - session thread to listen on new smb requests @@ -392,6 +395,7 @@ int ksmbd_conn_handler_loop(void *p) struct ksmbd_conn *conn = (struct ksmbd_conn *)p; struct ksmbd_transport *t = conn->transport; unsigned int pdu_size, max_allowed_pdu_size, max_req; + __le32 proto; char hdr_buf[4] = {0,}; int size; @@ -444,7 +448,7 @@ int ksmbd_conn_handler_loop(void *p) if (pdu_size > MAX_STREAM_PROT_LEN) break; - if (pdu_size < SMB1_MIN_SUPPORTED_HEADER_SIZE) + if (pdu_size < SMB1_MIN_SUPPORTED_PDU_SIZE) break; /* 4 for rfc1002 length field */ @@ -475,11 +479,14 @@ int ksmbd_conn_handler_loop(void *p) if (!ksmbd_smb_request(conn)) break; - if (((struct smb2_hdr *)smb2_get_msg(conn->request_buf))->ProtocolId == - SMB2_PROTO_NUMBER) { - if (pdu_size < SMB2_MIN_SUPPORTED_HEADER_SIZE) - break; - } + proto = *(__le32 *)smb_get_msg(conn->request_buf); + if (proto == SMB2_PROTO_NUMBER && + pdu_size < SMB2_MIN_SUPPORTED_PDU_SIZE) + break; + + if (proto == SMB2_TRANSFORM_PROTO_NUM && + pdu_size < SMB2_TRANSFORM_MIN_SUPPORTED_PDU_SIZE) + break; if (!default_conn_ops.process_fn) { pr_err("No connection request callback\n"); diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index 26e6204ef93e..8728899d5e75 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -195,6 +195,11 @@ void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn); * This is a hack. We will move status to a proper place once we land * a multi-sessions support. */ +static inline bool ksmbd_conn_new(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_NEW; +} + static inline bool ksmbd_conn_good(struct ksmbd_conn *conn) { return READ_ONCE(conn->status) == KSMBD_SESS_GOOD; diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index ff0dbf3f8cbf..d83370f53d6f 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -663,7 +663,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) goto out; } - rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr = smb_get_msg(work->response_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; @@ -677,7 +677,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) rsp_hdr->SessionId = 0; memset(rsp_hdr->Signature, 0, 16); - rsp = smb2_get_msg(work->response_buf); + rsp = smb_get_msg(work->response_buf); rsp->StructureSize = cpu_to_le16(24); if (!br_info->open_trunc && @@ -776,7 +776,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) goto out; } - rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr = smb_get_msg(work->response_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; @@ -790,7 +790,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) rsp_hdr->SessionId = 0; memset(rsp_hdr->Signature, 0, 16); - rsp = smb2_get_msg(work->response_buf); + rsp = smb_get_msg(work->response_buf); rsp->StructureSize = cpu_to_le16(44); rsp->Epoch = br_info->epoch; rsp->Flags = 0; diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c index 6317edbfe4fa..b11d854d3fcf 100644 --- a/fs/smb/server/smb2misc.c +++ b/fs/smb/server/smb2misc.c @@ -465,7 +465,7 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) } validate_credit: - if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && + if ((work->conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && smb2_validate_credit_charge(work->conn, hdr)) return 1; diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c index 606aa3c5189a..bcf05caa2304 100644 --- a/fs/smb/server/smb2ops.c +++ b/fs/smb/server/smb2ops.c @@ -15,7 +15,7 @@ static struct smb_version_values smb21_server_values = { .version_string = SMB21_VERSION_STRING, .protocol_id = SMB21_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, .max_read_size = SMB21_DEFAULT_IOSIZE, .max_write_size = SMB21_DEFAULT_IOSIZE, .max_trans_size = SMB21_DEFAULT_IOSIZE, @@ -41,7 +41,7 @@ static struct smb_version_values smb21_server_values = { static struct smb_version_values smb30_server_values = { .version_string = SMB30_VERSION_STRING, .protocol_id = SMB30_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, .max_read_size = SMB3_DEFAULT_IOSIZE, .max_write_size = SMB3_DEFAULT_IOSIZE, .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, @@ -68,7 +68,7 @@ static struct smb_version_values smb30_server_values = { static struct smb_version_values smb302_server_values = { .version_string = SMB302_VERSION_STRING, .protocol_id = SMB302_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, .max_read_size = SMB3_DEFAULT_IOSIZE, .max_write_size = SMB3_DEFAULT_IOSIZE, .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, @@ -95,7 +95,7 @@ static struct smb_version_values smb302_server_values = { static struct smb_version_values smb311_server_values = { .version_string = SMB311_VERSION_STRING, .protocol_id = SMB311_PROT_ID, - .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .req_capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, .max_read_size = SMB3_DEFAULT_IOSIZE, .max_write_size = SMB3_DEFAULT_IOSIZE, .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, @@ -204,7 +204,7 @@ void init_smb2_1_server(struct ksmbd_conn *conn) conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256_LE; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_LEASING; } /** @@ -221,20 +221,20 @@ 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->req_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) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + conn->vals->req_capabilities |= 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; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; } /** @@ -251,19 +251,19 @@ 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->req_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; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES; } /** @@ -280,14 +280,14 @@ 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->req_capabilities |= SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DIRECTORY_LEASING; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE) - conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES; + conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES; INIT_LIST_HEAD(&conn->preauth_sess_table); return 0; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index e9f047061632..1beafa9cd49c 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -47,8 +47,8 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) *req = ksmbd_req_buf_next(work); *rsp = ksmbd_resp_buf_next(work); } else { - *req = smb2_get_msg(work->request_buf); - *rsp = smb2_get_msg(work->response_buf); + *req = smb_get_msg(work->request_buf); + *rsp = smb_get_msg(work->response_buf); } } @@ -154,7 +154,7 @@ void smb2_set_err_rsp(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) err_rsp = ksmbd_resp_buf_next(work); else - err_rsp = smb2_get_msg(work->response_buf); + err_rsp = smb_get_msg(work->response_buf); if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { int err; @@ -180,7 +180,7 @@ void smb2_set_err_rsp(struct ksmbd_work *work) */ bool is_smb2_neg_cmd(struct ksmbd_work *work) { - struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + struct smb2_hdr *hdr = smb_get_msg(work->request_buf); /* is it SMB2 header ? */ if (hdr->ProtocolId != SMB2_PROTO_NUMBER) @@ -204,7 +204,7 @@ bool is_smb2_neg_cmd(struct ksmbd_work *work) */ bool is_smb2_rsp(struct ksmbd_work *work) { - struct smb2_hdr *hdr = smb2_get_msg(work->response_buf); + struct smb2_hdr *hdr = smb_get_msg(work->response_buf); /* is it SMB2 header ? */ if (hdr->ProtocolId != SMB2_PROTO_NUMBER) @@ -230,7 +230,7 @@ u16 get_smb2_cmd_val(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) rcv_hdr = ksmbd_req_buf_next(work); else - rcv_hdr = smb2_get_msg(work->request_buf); + rcv_hdr = smb_get_msg(work->request_buf); return le16_to_cpu(rcv_hdr->Command); } @@ -243,7 +243,7 @@ void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) { struct smb2_hdr *rsp_hdr; - rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr = smb_get_msg(work->response_buf); rsp_hdr->Status = err; work->iov_idx = 0; @@ -266,7 +266,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) struct ksmbd_conn *conn = work->conn; int err; - rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr = smb_get_msg(work->response_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; @@ -280,7 +280,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) rsp_hdr->SessionId = 0; memset(rsp_hdr->Signature, 0, 16); - rsp = smb2_get_msg(work->response_buf); + rsp = smb_get_msg(work->response_buf); WARN_ON(ksmbd_conn_good(conn)); @@ -290,7 +290,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) /* Not setting conn guid rsp->ServerGUID, as it * not used by client for identifying connection */ - rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities); /* Default Max Message Size till SMB2.0, 64K*/ rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); @@ -454,7 +454,7 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) */ bool is_chained_smb2_message(struct ksmbd_work *work) { - struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + struct smb2_hdr *hdr = smb_get_msg(work->request_buf); unsigned int len, next_cmd; if (hdr->ProtocolId != SMB2_PROTO_NUMBER) @@ -505,8 +505,8 @@ bool is_chained_smb2_message(struct ksmbd_work *work) */ 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 smb2_hdr *rsp_hdr = smb_get_msg(work->response_buf); + struct smb2_hdr *rcv_hdr = smb_get_msg(work->request_buf); memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; @@ -535,7 +535,7 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) */ int smb2_allocate_rsp_buf(struct ksmbd_work *work) { - struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + struct smb2_hdr *hdr = smb_get_msg(work->request_buf); size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; size_t large_sz = small_sz + work->conn->vals->max_trans_size; size_t sz = small_sz; @@ -551,7 +551,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) offsetof(struct smb2_query_info_req, OutputBufferLength)) return -EINVAL; - req = smb2_get_msg(work->request_buf); + req = smb_get_msg(work->request_buf); if ((req->InfoType == SMB2_O_INFO_FILE && (req->FileInfoClass == FILE_FULL_EA_INFORMATION || req->FileInfoClass == FILE_ALL_INFORMATION)) || @@ -725,10 +725,10 @@ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) } in_work->conn = work->conn; - memcpy(smb2_get_msg(in_work->response_buf), ksmbd_resp_buf_next(work), + memcpy(smb_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 = smb_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); @@ -969,7 +969,7 @@ bool smb3_encryption_negotiated(struct ksmbd_conn *conn) * SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag. * SMB 3.1.1 uses the cipher_type field. */ - return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || + return (conn->vals->req_capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || conn->cipher_type; } @@ -1101,25 +1101,21 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, * smb2_handle_negotiate() - handler for smb2 negotiate command * @work: smb work containing smb request buffer * + * The caller holds conn->srv_mutex. + * * Return: 0 */ int smb2_handle_negotiate(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf); - struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf); + struct smb2_negotiate_req *req = smb_get_msg(work->request_buf); + struct smb2_negotiate_rsp *rsp = smb_get_msg(work->response_buf); int rc = 0; unsigned int smb2_buf_len, smb2_neg_size, neg_ctxt_len = 0; __le32 status; ksmbd_debug(SMB, "Received negotiate request\n"); conn->need_neg = false; - if (ksmbd_conn_good(conn)) { - pr_err("conn->tcp_status is already in CifsGood State\n"); - work->send_no_response = 1; - return rc; - } - smb2_buf_len = get_rfc1002_len(work->request_buf); smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); if (smb2_neg_size > smb2_buf_len) { @@ -1222,7 +1218,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) rc = -EINVAL; goto err_out; } - rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities); /* For stats */ conn->connection_type = conn->dialect; @@ -3501,7 +3497,7 @@ int smb2_open(struct ksmbd_work *work) 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))) { + !(conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LEASING))) { if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { rc = share_ret; goto err_out1; @@ -6070,7 +6066,7 @@ int smb2_close(struct ksmbd_work *work) */ int smb2_echo(struct ksmbd_work *work) { - struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf); + struct smb2_echo_rsp *rsp = smb_get_msg(work->response_buf); if (work->next_smb2_rcv_hdr_off) rsp = ksmbd_resp_buf_next(work); @@ -6636,8 +6632,8 @@ int smb2_set_info(struct ksmbd_work *work) pid = work->compound_pfid; } } else { - req = smb2_get_msg(work->request_buf); - rsp = smb2_get_msg(work->response_buf); + req = smb_get_msg(work->request_buf); + rsp = smb_get_msg(work->response_buf); } if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { @@ -6863,8 +6859,8 @@ int smb2_read(struct ksmbd_work *work) pid = work->compound_pfid; } } else { - req = smb2_get_msg(work->request_buf); - rsp = smb2_get_msg(work->response_buf); + req = smb_get_msg(work->request_buf); + rsp = smb_get_msg(work->response_buf); } if (!has_file_id(id)) { @@ -7282,7 +7278,7 @@ int smb2_flush(struct ksmbd_work *work) int smb2_cancel(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; - struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + struct smb2_hdr *hdr = smb_get_msg(work->request_buf); struct smb2_hdr *chdr; struct ksmbd_work *iter; struct list_head *command_list; @@ -7299,7 +7295,7 @@ int smb2_cancel(struct ksmbd_work *work) spin_lock(&conn->request_lock); list_for_each_entry(iter, command_list, async_request_entry) { - chdr = smb2_get_msg(iter->request_buf); + chdr = smb_get_msg(iter->request_buf); if (iter->async_id != le64_to_cpu(hdr->Id.AsyncId)) @@ -7331,7 +7327,7 @@ int smb2_cancel(struct ksmbd_work *work) spin_lock(&conn->request_lock); list_for_each_entry(iter, command_list, request_entry) { - chdr = smb2_get_msg(iter->request_buf); + chdr = smb_get_msg(iter->request_buf); if (chdr->MessageId != hdr->MessageId || iter == work) @@ -8102,7 +8098,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, goto err_out; } - neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + neg_rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities); memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE); neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode); neg_rsp->Dialect = cpu_to_le16(conn->dialect); @@ -8275,8 +8271,8 @@ int smb2_ioctl(struct ksmbd_work *work) id = work->compound_fid; } } else { - req = smb2_get_msg(work->request_buf); - rsp = smb2_get_msg(work->response_buf); + req = smb_get_msg(work->request_buf); + rsp = smb_get_msg(work->response_buf); } if (!has_file_id(id)) @@ -8958,7 +8954,7 @@ int smb2_notify(struct ksmbd_work *work) */ bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) { - struct smb2_hdr *rcv_hdr2 = smb2_get_msg(work->request_buf); + struct smb2_hdr *rcv_hdr2 = smb_get_msg(work->request_buf); if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && command != SMB2_NEGOTIATE_HE && @@ -8983,7 +8979,7 @@ int smb2_check_sign_req(struct ksmbd_work *work) struct kvec iov[1]; size_t len; - hdr = smb2_get_msg(work->request_buf); + hdr = smb_get_msg(work->request_buf); if (work->next_smb2_rcv_hdr_off) hdr = ksmbd_req_buf_next(work); @@ -9058,7 +9054,7 @@ int smb3_check_sign_req(struct ksmbd_work *work) struct kvec iov[1]; size_t len; - hdr = smb2_get_msg(work->request_buf); + hdr = smb_get_msg(work->request_buf); if (work->next_smb2_rcv_hdr_off) hdr = ksmbd_req_buf_next(work); @@ -9194,7 +9190,7 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type) { struct smb2_transform_hdr *tr_hdr = tr_buf + 4; - struct smb2_hdr *hdr = smb2_get_msg(old_buf); + struct smb2_hdr *hdr = smb_get_msg(old_buf); unsigned int orig_len = get_rfc1002_len(old_buf); /* tr_buf must be cleared by the caller */ @@ -9233,7 +9229,7 @@ int smb3_encrypt_resp(struct ksmbd_work *work) bool smb3_is_transform_hdr(void *buf) { - struct smb2_transform_hdr *trhdr = smb2_get_msg(buf); + struct smb2_transform_hdr *trhdr = smb_get_msg(buf); return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; } @@ -9245,7 +9241,7 @@ int smb3_decrypt_req(struct ksmbd_work *work) unsigned int pdu_length = get_rfc1002_len(buf); struct kvec iov[2]; int buf_data_size = pdu_length - sizeof(struct smb2_transform_hdr); - struct smb2_transform_hdr *tr_hdr = smb2_get_msg(buf); + struct smb2_transform_hdr *tr_hdr = smb_get_msg(buf); int rc = 0; if (pdu_length < sizeof(struct smb2_transform_hdr) || @@ -9286,7 +9282,7 @@ bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; - struct smb2_hdr *rsp = smb2_get_msg(work->response_buf); + struct smb2_hdr *rsp = smb_get_msg(work->response_buf); if (conn->dialect < SMB30_PROT_ID) return false; diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h index 16ae8a10490b..f362892a9745 100644 --- a/fs/smb/server/smb2pdu.h +++ b/fs/smb/server/smb2pdu.h @@ -496,15 +496,6 @@ int smb2_ioctl(struct ksmbd_work *work); int smb2_oplock_break(struct ksmbd_work *work); int smb2_notify(struct ksmbd_work *ksmbd_work); -/* - * Get the body of the smb2 message excluding the 4 byte rfc1002 headers - * from request/response buffer. - */ -static inline void *smb2_get_msg(void *buf) -{ - return buf + 4; -} - #define POSIX_TYPE_FILE 0 #define POSIX_TYPE_DIR 1 #define POSIX_TYPE_SYMLINK 2 diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c index a29c0494dccb..214f169a80e1 100644 --- a/fs/smb/server/smb_common.c +++ b/fs/smb/server/smb_common.c @@ -163,7 +163,7 @@ bool ksmbd_smb_request(struct ksmbd_conn *conn) if (conn->request_buf[0] != 0) return false; - proto = (__le32 *)smb2_get_msg(conn->request_buf); + proto = (__le32 *)smb_get_msg(conn->request_buf); if (*proto == SMB2_COMPRESSION_TRANSFORM_ID) { pr_err_ratelimited("smb2 compression not support yet"); return false; @@ -259,14 +259,14 @@ int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) static int ksmbd_negotiate_smb_dialect(void *buf) { int smb_buf_length = get_rfc1002_len(buf); - __le32 proto = ((struct smb2_hdr *)smb2_get_msg(buf))->ProtocolId; + __le32 proto = ((struct smb2_hdr *)smb_get_msg(buf))->ProtocolId; if (proto == SMB2_PROTO_NUMBER) { struct smb2_negotiate_req *req; int smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); - req = (struct smb2_negotiate_req *)smb2_get_msg(buf); + req = (struct smb2_negotiate_req *)smb_get_msg(buf); if (smb2_neg_size > smb_buf_length) goto err_out; @@ -592,23 +592,46 @@ int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) struct ksmbd_conn *conn = work->conn; int ret; - conn->dialect = - ksmbd_negotiate_smb_dialect(work->request_buf); - ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); - if (command == SMB2_NEGOTIATE_HE) { + /* + * An SMB2 NEGOTIATE is valid for a new connection, or after an + * SMB1 multi-protocol negotiate has selected SMB2. Do not allow + * a second SMB2 NEGOTIATE to replace connection-wide state + * while a session setup is pending. KSMBD_SESS_NEED_RECONNECT + * is a transient session state and does not restart transport + * negotiation. + */ + ksmbd_conn_lock(conn); + if (!ksmbd_conn_new(conn) && + !ksmbd_conn_need_negotiate(conn)) { + work->send_no_response = 1; + ksmbd_conn_set_exiting(conn); + ksmbd_conn_unlock(conn); + return 0; + } + + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); ret = smb2_handle_negotiate(work); + ksmbd_conn_unlock(conn); return ret; } if (command == SMB_COM_NEGOTIATE) { + ksmbd_conn_lock(conn); + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); if (__smb2_negotiate(conn)) { init_smb3_11_server(conn); - init_smb2_neg_rsp(work); + ret = init_smb2_neg_rsp(work); ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n"); - return 0; + } else { + ret = smb_handle_negotiate(work); } - return smb_handle_negotiate(work); + ksmbd_conn_unlock(conn); + return ret; } pr_err("Unknown SMB negotiation command: %u\n", command); diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h index a353e187dfd7..46bc4f8eea48 100644 --- a/fs/smb/server/smb_common.h +++ b/fs/smb/server/smb_common.h @@ -3,8 +3,8 @@ * Copyright (C) 2018 Samsung Electronics Co., Ltd. */ -#ifndef __SMB_COMMON_H__ -#define __SMB_COMMON_H__ +#ifndef __SMB_SERVER_COMMON_H__ +#define __SMB_SERVER_COMMON_H__ #include @@ -336,35 +336,6 @@ struct file_id_full_dir_info { char FileName[]; } __packed; /* level 0x105 FF rsp data */ -struct smb_version_values { - char *version_string; - __u16 protocol_id; - __le16 lock_cmd; - __u32 capabilities; - __u32 max_read_size; - __u32 max_write_size; - __u32 max_trans_size; - __u32 max_credits; - __u32 large_lock_type; - __u32 exclusive_lock_type; - __u32 shared_lock_type; - __u32 unlock_lock_type; - size_t header_size; - size_t max_header_size; - size_t read_rsp_size; - unsigned int cap_unix; - unsigned int cap_nt_find; - unsigned int cap_large_files; - __u16 signing_enabled; - __u16 signing_required; - size_t create_lease_size; - size_t create_durable_size; - size_t create_durable_v2_size; - size_t create_mxac_size; - size_t create_disk_id_size; - size_t create_posix_size; -}; - struct filesystem_posix_info { /* For undefined recommended transfer size return -1 in that field */ __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ @@ -451,8 +422,12 @@ unsigned int ksmbd_server_side_copy_max_total_size(void); bool is_asterisk(char *p); __le32 smb_map_generic_desired_access(__le32 daccess); -static inline unsigned int get_rfc1002_len(void *buf) +/* + * Get the body of the smb message excluding the 4 byte rfc1002 headers + * from request/response buffer. + */ +static inline void *smb_get_msg(void *buf) { - return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; + return buf + 4; } -#endif /* __SMB_COMMON_H__ */ +#endif /* __SMB_SERVER_COMMON_H__ */ diff --git a/fs/super.c b/fs/super.c index c9c7223bc2a2..bccfadb37ff2 100644 --- a/fs/super.c +++ b/fs/super.c @@ -887,52 +887,48 @@ void drop_super_exclusive(struct super_block *sb) } EXPORT_SYMBOL(drop_super_exclusive); -static void __iterate_supers(void (*f)(struct super_block *)) -{ - struct super_block *sb, *p = NULL; - - spin_lock(&sb_lock); - list_for_each_entry(sb, &super_blocks, s_list) { - if (super_flags(sb, SB_DYING)) - continue; - sb->s_count++; - spin_unlock(&sb_lock); +enum super_iter_flags_t { + SUPER_ITER_EXCL = (1U << 0), + SUPER_ITER_UNLOCKED = (1U << 1), + SUPER_ITER_REVERSE = (1U << 2), +}; - f(sb); +static inline struct super_block *first_super(enum super_iter_flags_t flags) +{ + if (flags & SUPER_ITER_REVERSE) + return list_last_entry(&super_blocks, struct super_block, s_list); + return list_first_entry(&super_blocks, struct super_block, s_list); +} - spin_lock(&sb_lock); - if (p) - __put_super(p); - p = sb; - } - if (p) - __put_super(p); - spin_unlock(&sb_lock); +static inline struct super_block *next_super(struct super_block *sb, + enum super_iter_flags_t flags) +{ + if (flags & SUPER_ITER_REVERSE) + return list_prev_entry(sb, s_list); + return list_next_entry(sb, s_list); } -/** - * iterate_supers - call function for all active superblocks - * @f: function to call - * @arg: argument to pass to it - * - * Scans the superblock list and calls given function, passing it - * locked superblock and given argument. - */ -void iterate_supers(void (*f)(struct super_block *, void *), void *arg) + +static void __iterate_supers(void (*f)(struct super_block *, void *), void *arg, + enum super_iter_flags_t flags) { struct super_block *sb, *p = NULL; + bool excl = flags & SUPER_ITER_EXCL; - spin_lock(&sb_lock); - list_for_each_entry(sb, &super_blocks, s_list) { - bool locked; + guard(spinlock)(&sb_lock); + for (sb = first_super(flags); + !list_entry_is_head(sb, &super_blocks, s_list); + sb = next_super(sb, flags)) { + if (super_flags(sb, SB_DYING)) + continue; sb->s_count++; spin_unlock(&sb_lock); - locked = super_lock_shared(sb); - if (locked) { - if (sb->s_root) - f(sb, arg); - super_unlock_shared(sb); + if (flags & SUPER_ITER_UNLOCKED) { + f(sb, arg); + } else if (super_lock(sb, excl)) { + f(sb, arg); + super_unlock(sb, excl); } spin_lock(&sb_lock); @@ -942,7 +938,11 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg) } if (p) __put_super(p); - spin_unlock(&sb_lock); +} + +void iterate_supers(void (*f)(struct super_block *, void *), void *arg) +{ + __iterate_supers(f, arg, 0); } /** @@ -963,13 +963,15 @@ void iterate_supers_type(struct file_system_type *type, hlist_for_each_entry(sb, &type->fs_supers, s_instances) { bool locked; + if (super_flags(sb, SB_DYING)) + continue; + sb->s_count++; spin_unlock(&sb_lock); locked = super_lock_shared(sb); if (locked) { - if (sb->s_root) - f(sb, arg); + f(sb, arg); super_unlock_shared(sb); } @@ -991,18 +993,15 @@ struct super_block *user_get_super(dev_t dev, bool excl) spin_lock(&sb_lock); list_for_each_entry(sb, &super_blocks, s_list) { - if (sb->s_dev == dev) { + if (sb->s_dev == dev) { bool locked; sb->s_count++; spin_unlock(&sb_lock); /* still alive? */ locked = super_lock(sb, excl); - if (locked) { - if (sb->s_root) - return sb; - super_unlock(sb, excl); - } + if (locked) + return sb; /* caller will drop */ /* nope, got unmounted */ spin_lock(&sb_lock); __put_super(sb); @@ -1111,11 +1110,9 @@ int reconfigure_super(struct fs_context *fc) return retval; } -static void do_emergency_remount_callback(struct super_block *sb) +static void do_emergency_remount_callback(struct super_block *sb, void *unused) { - bool locked = super_lock_excl(sb); - - if (locked && sb->s_root && sb->s_bdev && !sb_rdonly(sb)) { + if (sb->s_bdev && !sb_rdonly(sb)) { struct fs_context *fc; fc = fs_context_for_reconfigure(sb->s_root, @@ -1126,13 +1123,12 @@ static void do_emergency_remount_callback(struct super_block *sb) put_fs_context(fc); } } - if (locked) - super_unlock_excl(sb); } static void do_emergency_remount(struct work_struct *work) { - __iterate_supers(do_emergency_remount_callback); + __iterate_supers(do_emergency_remount_callback, NULL, + SUPER_ITER_EXCL | SUPER_ITER_REVERSE); kfree(work); printk("Emergency Remount complete\n"); } @@ -1148,24 +1144,35 @@ void emergency_remount(void) } } -static void do_thaw_all_callback(struct super_block *sb) +static inline bool get_active_super(struct super_block *sb) { - bool locked = super_lock_excl(sb); + bool active = false; - if (locked && sb->s_root) { - if (IS_ENABLED(CONFIG_BLOCK)) - while (sb->s_bdev && !bdev_thaw(sb->s_bdev)) - pr_warn("Emergency Thaw on %pg\n", sb->s_bdev); - thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE); - return; - } - if (locked) + if (super_lock_excl(sb)) { + active = atomic_inc_not_zero(&sb->s_active); super_unlock_excl(sb); + } + return active; +} + +static void do_thaw_all_callback(struct super_block *sb, void *unused) +{ + if (!get_active_super(sb)) + return; + + /* fs_bdev_thaw() acquires s_umount so it must not be held here */ + if (IS_ENABLED(CONFIG_BLOCK)) + while (sb->s_bdev && !bdev_thaw(sb->s_bdev)) + pr_warn("Emergency Thaw on %pg\n", sb->s_bdev); + + if (super_lock_excl(sb)) + thaw_super_locked(sb, FREEZE_HOLDER_USERSPACE); + deactivate_super(sb); } static void do_thaw_all(struct work_struct *work) { - __iterate_supers(do_thaw_all_callback); + __iterate_supers(do_thaw_all_callback, NULL, SUPER_ITER_UNLOCKED); kfree(work); printk(KERN_WARNING "Emergency Thaw complete\n"); } diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c index aac691944959..b06e90aede90 100644 --- a/fs/xfs/libxfs/xfs_exchmaps.c +++ b/fs/xfs/libxfs/xfs_exchmaps.c @@ -957,6 +957,16 @@ xmi_can_exchange_reflink_flags( { struct xfs_mount *mp = req->ip1->i_mount; + /* + * The INO1_WRITTEN optimization can skip exchanging hole and + * unwritten mappings, which means we cannot guarantee that all + * shared extents actually moved to the other file. Clearing the + * reflink flag of an inode that still holds shared extents breaks + * the CoW write path, so refuse to exchange the flags in that case. + */ + if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN) + return false; + if (hweight32(reflink_state) != 1) return false; if (req->startoff1 != 0 || req->startoff2 != 0) diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c index e27b63281d01..eb46712e27d9 100644 --- a/fs/xfs/libxfs/xfs_sb.c +++ b/fs/xfs/libxfs/xfs_sb.c @@ -891,10 +891,10 @@ xfs_sb_read_verify( * because _verify_common checks the on-disk values. */ __xfs_sb_from_disk(&sb, dsb, false); - error = xfs_validate_sb_common(mp, bp, &sb); + error = xfs_validate_sb_read(mp, &sb); if (error) goto out_error; - error = xfs_validate_sb_read(mp, &sb); + error = xfs_validate_sb_common(mp, bp, &sb); out_error: if (error == -EFSCORRUPTED || error == -EFSBADCRC) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 321009741e58..235cefe9422c 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -979,6 +979,13 @@ xrep_agi_calc_from_btrees( return error; } +/* + * Magic value that means "not unlinked" because xfarrays don't support storing + * totally zeroed elements. There can't be a cluster that starts in daddr 0 so + * there can't be an inode #1 either. + */ +#define LINKED_AGINO (0x1) + /* * Record a forwards unlinked chain pointer from agino -> next_agino in our * staging information. @@ -1083,7 +1090,7 @@ xrep_iunlink_walk_ondisk_bucket( unsigned int bucket) { struct xfs_scrub *sc = ragi->sc; - struct xfs_agi *agi = sc->sa.agi_bp->b_addr; + struct xfs_agi *agi = ragi->agi_bp->b_addr; xfs_agino_t prev_agino = NULLAGINO; xfs_agino_t next_agino; int error = 0; @@ -1306,7 +1313,7 @@ xrep_iunlink_mark_ondisk_rec( * iunlink_bmp. We haven't checked the inobt yet, so we don't error out if * the btree is corrupt. */ -STATIC void +STATIC int xrep_iunlink_mark_ondisk( struct xrep_agi *ragi) { @@ -1318,6 +1325,14 @@ xrep_iunlink_mark_ondisk( cur = xfs_inobt_init_cursor(sc->sa.pag, sc->tp, agi_bp); error = xfs_btree_query_all(cur, xrep_iunlink_mark_ondisk_rec, ragi); xfs_btree_del_cursor(cur, error); + + /* + * Don't proceed if we couldn't set a bit in the bitmap. All other + * errors we ignore because we haven't actually checked the inobt yet. + */ + if (error == -ENOMEM) + return -ENOMEM; + return 0; } /* @@ -1330,15 +1345,32 @@ xrep_iunlink_resolve_bucket( struct xrep_agi *ragi, unsigned int bucket) { + struct xagino_bitmap seen; struct xfs_scrub *sc = ragi->sc; struct xfs_inode *ip; xfs_agino_t prev_agino = NULLAGINO; xfs_agino_t next_agino = ragi->iunlink_heads[bucket]; int error = 0; + xagino_bitmap_init(&seen); + while (next_agino != NULLAGINO) { + unsigned int len = 1; + if (xchk_should_terminate(ragi->sc, &error)) - return error; + goto out_bitmap; + + /* Inode already seen? We're stuck in a loop */ + if (xagino_bitmap_test(&seen, next_agino, &len)) { + trace_xrep_iunlink_resolve_infinite_loop(sc->sa.pag, + bucket, prev_agino, next_agino); + next_agino = NULLAGINO; + break; + } + + error = xagino_bitmap_set(&seen, next_agino, 1); + if (error) + goto out_bitmap; /* Find the next inode in the chain. */ ip = xfs_iunlink_lookup(sc->sa.pag, next_agino); @@ -1351,6 +1383,35 @@ xrep_iunlink_resolve_bucket( break; } + if (VFS_I(ip)->i_nlink != 0) { + /* + * Inode is linked somewhere! Blow out both unlinked + * list pointers, advance the list, and pretend we + * didn't see this inode. Clear it from iunlink_bmp + * because it's linked. + */ + trace_xrep_iunlink_resolve_allocated(sc->sa.pag, + bucket, prev_agino, next_agino); + + error = xrep_iunlink_store_next(ragi, next_agino, + NULLAGINO); + if (error) + goto out_bitmap; + + error = xrep_iunlink_store_prev(ragi, next_agino, + LINKED_AGINO); + if (error) + goto out_bitmap; + + error = xagino_bitmap_clear(&ragi->iunlink_bmp, + next_agino, 1); + if (error) + goto out_bitmap; + + next_agino = ip->i_next_unlinked; + continue; + } + if (next_agino % XFS_AGI_UNLINKED_BUCKETS != bucket) { /* * Inode is in the wrong bucket. Advance the list, @@ -1386,20 +1447,20 @@ xrep_iunlink_resolve_bucket( */ error = xagino_bitmap_clear(&ragi->iunlink_bmp, next_agino, 1); if (error) - return error; + goto out_bitmap; /* Remember the previous inode's next pointer. */ if (prev_agino != NULLAGINO) { error = xrep_iunlink_store_next(ragi, prev_agino, next_agino); if (error) - return error; + goto out_bitmap; } /* Remember this inode's previous pointer. */ error = xrep_iunlink_store_prev(ragi, next_agino, prev_agino); if (error) - return error; + goto out_bitmap; /* Advance the list and remember this inode. */ prev_agino = next_agino; @@ -1410,10 +1471,12 @@ xrep_iunlink_resolve_bucket( if (prev_agino != NULLAGINO) { error = xrep_iunlink_store_next(ragi, prev_agino, next_agino); if (error) - return error; + goto out_bitmap; } - return 0; +out_bitmap: + xagino_bitmap_destroy(&seen); + return error; } /* Reinsert this unlinked inode into the head of the staged bucket list. */ @@ -1438,6 +1501,10 @@ xrep_iunlink_add_to_bucket( if (error) return error; + error = xrep_iunlink_store_prev(ragi, agino, NULLAGINO); + if (error) + return error; + /* Remember the head inode's previous pointer. */ if (current_head != NULLAGINO) { error = xrep_iunlink_store_prev(ragi, current_head, agino); @@ -1505,7 +1572,9 @@ xrep_iunlink_rebuild_buckets( * If there are ondisk inodes that are unlinked and are not been loaded * into cache, record them in iunlink_bmp. */ - xrep_iunlink_mark_ondisk(ragi); + error = xrep_iunlink_mark_ondisk(ragi); + if (error) + return error; /* * Walk each iunlink bucket to (re)construct as much of the incore list @@ -1527,6 +1596,24 @@ xrep_iunlink_rebuild_buckets( xrep_iunlink_add_lost_inodes, ragi); } +static inline void +set_inode_prev_unlinked( + struct xfs_inode *ip, + xfs_agino_t prev_agino) +{ + /* + * Magic value that means "not unlinked" because xfarrays don't support + * storing totally zeroed elements. + */ + if (prev_agino == LINKED_AGINO) + prev_agino = 0; + + if (ip->i_prev_unlinked != prev_agino) { + trace_xrep_iunlink_relink_prev(ip, prev_agino); + ip->i_prev_unlinked = prev_agino; + } +} + /* Update i_next_iunlinked for the inode @agino. */ STATIC int xrep_iunlink_relink_next( @@ -1562,8 +1649,7 @@ xrep_iunlink_relink_next( if (error) goto out_rele; - trace_xrep_iunlink_relink_prev(ip, prev_agino); - ip->i_prev_unlinked = prev_agino; + set_inode_prev_unlinked(ip, prev_agino); } /* Update the forward pointer. */ @@ -1620,7 +1706,7 @@ xrep_iunlink_relink_prev( want_rele = true; /* Set the forward pointer since this just came off disk. */ - error = xfarray_load(ragi->iunlink_prev, agino, &next_agino); + error = xfarray_load(ragi->iunlink_next, agino, &next_agino); if (error) goto out_rele; @@ -1632,11 +1718,7 @@ xrep_iunlink_relink_prev( ip->i_next_unlinked = next_agino; } - /* Update the backward pointer. */ - if (ip->i_prev_unlinked != prev_agino) { - trace_xrep_iunlink_relink_prev(ip, prev_agino); - ip->i_prev_unlinked = prev_agino; - } + set_inode_prev_unlinked(ip, prev_agino); out_rele: /* @@ -1666,6 +1748,8 @@ xrep_iunlink_commit( if (error) return error; } + if (error < 0) + return error; /* Fix all the back links */ idx = XFARRAY_CURSOR_INIT; @@ -1674,6 +1758,8 @@ xrep_iunlink_commit( if (error) return error; } + if (error < 0) + return error; /* Copy the staged iunlink buckets to the new AGI. */ for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) { diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c index d1b8a4997dd2..acc103fd1709 100644 --- a/fs/xfs/scrub/alloc.c +++ b/fs/xfs/scrub/alloc.c @@ -136,7 +136,7 @@ xchk_allocbt_rec( const union xfs_btree_rec *rec) { struct xfs_alloc_rec_incore irec; - struct xchk_alloc *ca = bs->private; + struct xchk_alloc *ca = bs->private; xfs_alloc_btrec_to_irec(rec, &irec); if (xfs_alloc_check_irec(bs->cur->bc_ag.pag, &irec) != NULL) { @@ -144,7 +144,8 @@ xchk_allocbt_rec( return 0; } - xchk_allocbt_mergeable(bs, ca, &irec); + if (bs->sc->sm->sm_type == XFS_SCRUB_TYPE_BNOBT) + xchk_allocbt_mergeable(bs, ca, &irec); xchk_allocbt_xref(bs->sc, &irec); return 0; diff --git a/fs/xfs/scrub/attr_repair.c b/fs/xfs/scrub/attr_repair.c index dd24044c44ef..fa68ecabd983 100644 --- a/fs/xfs/scrub/attr_repair.c +++ b/fs/xfs/scrub/attr_repair.c @@ -1427,7 +1427,8 @@ xrep_xattr_rebuild_tree( * If we didn't find any attributes to salvage, repair the file by * zapping its attr fork. */ - if (rx->attrs_found == 0) { + if (rx->attrs_found == 0 && + (!xfs_has_parent(sc->mp) || xfarray_length(rx->pptr_recs) == 0)) { xfs_trans_ijoin(sc->tp, sc->ip, 0); error = xrep_xattr_reset_fork(sc); if (error) diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c index 5ab2ac53c920..20420b8401bf 100644 --- a/fs/xfs/scrub/bmap.c +++ b/fs/xfs/scrub/bmap.c @@ -1062,6 +1062,11 @@ xchk_bmap_attr( } error = xchk_bmap(sc, XFS_ATTR_FORK); + /* A repaired, empty attr fork no longer has mappings to check. */ + if (error == -ENOENT && (sc->flags & XREP_ALREADY_FIXED)) { + xchk_mark_healthy_if_clean(sc, XFS_SICK_INO_BMBTA_ZAPPED); + return 0; + } if (error) return error; diff --git a/fs/xfs/scrub/dirtree_repair.c b/fs/xfs/scrub/dirtree_repair.c index 5c04e70ba951..d6d07798903e 100644 --- a/fs/xfs/scrub/dirtree_repair.c +++ b/fs/xfs/scrub/dirtree_repair.c @@ -349,6 +349,8 @@ xrep_dirtree_unlink_iolock( ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL); + if (sc->ip == dp) + return 0; if (xfs_ilock_nowait(dp, XFS_IOLOCK_EXCL)) return 0; @@ -400,8 +402,18 @@ xrep_dirtree_unlink( * directory code can handle a reservationless update. */ resblks = xfs_remove_space_res(mp, step->name_len); - error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip, - &resblks, &sc->tp, &dontcare); + if (sc->ip == dp) { +again: + error = xfs_trans_alloc_inode(dp, &M_RES(mp)->tr_remove, + resblks, 0, false, &sc->tp); + if ((error == -ENOSPC || error == -EDQUOT) && resblks > 0) { + resblks = 0; + goto again; + } + } else { + error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip, + &resblks, &sc->tp, &dontcare); + } if (error) goto out_iolock; @@ -489,9 +501,11 @@ xrep_dirtree_unlink( xchk_trans_cancel(sc); out_ilock: xfs_iunlock(sc->ip, XFS_ILOCK_EXCL); - xfs_iunlock(dp, XFS_ILOCK_EXCL); + if (dp != sc->ip) + xfs_iunlock(dp, XFS_ILOCK_EXCL); out_iolock: - xfs_iunlock(dp, XFS_IOLOCK_EXCL); + if (dp != sc->ip) + xfs_iunlock(dp, XFS_IOLOCK_EXCL); return error; } diff --git a/fs/xfs/scrub/nlinks.c b/fs/xfs/scrub/nlinks.c index d386ee9e1364..a7bd1d3d60cf 100644 --- a/fs/xfs/scrub/nlinks.c +++ b/fs/xfs/scrub/nlinks.c @@ -382,6 +382,12 @@ xchk_nlinks_ilock_dir( { uint lock_mode = XFS_ILOCK_SHARED; + /* + * Take the IOLOCK so that other threads cannot start a directory + * update while we're scanning. + */ + xfs_ilock(ip, XFS_IOLOCK_SHARED); + /* * We're going to scan the directory entries, so we must be ready to * pull the data fork mappings into memory if they aren't already. @@ -397,13 +403,8 @@ xchk_nlinks_ilock_dir( xfs_need_iread_extents(&ip->i_af)) lock_mode = XFS_ILOCK_EXCL; - /* - * Take the IOLOCK so that other threads cannot start a directory - * update while we're scanning. - */ - lock_mode |= XFS_IOLOCK_SHARED; xfs_ilock(ip, lock_mode); - return lock_mode; + return lock_mode | XFS_IOLOCK_SHARED; } /* Walk a directory to bump the observed link counts of the children. */ diff --git a/fs/xfs/scrub/nlinks_repair.c b/fs/xfs/scrub/nlinks_repair.c index b3e707f47b7b..fdf4c5a8a955 100644 --- a/fs/xfs/scrub/nlinks_repair.c +++ b/fs/xfs/scrub/nlinks_repair.c @@ -234,9 +234,14 @@ xrep_nlinks_repair_inode( * unlinked list, put it on the unlinked list. */ if (total_links == 0 && !xfs_inode_on_unlinked_list(ip)) { + if (actual_nlink) + clear_nlink(VFS_I(ip)); error = xfs_iunlink(sc->tp, ip); - if (error) + if (error) { + if (actual_nlink) + set_nlink(VFS_I(ip), actual_nlink); goto out_trans; + } dirty = true; } diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c index a82d1fb67716..250177f5551a 100644 --- a/fs/xfs/scrub/parent.c +++ b/fs/xfs/scrub/parent.c @@ -472,7 +472,7 @@ xchk_parent_scan_attr( valuelen, &parent_ino, NULL); if (error) { xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0); - return error; + return -ECANCELED; } /* No self-referential parent pointers. */ diff --git a/fs/xfs/scrub/tempfile.c b/fs/xfs/scrub/tempfile.c index 177f922acfaf..56fef13bc773 100644 --- a/fs/xfs/scrub/tempfile.c +++ b/fs/xfs/scrub/tempfile.c @@ -173,6 +173,7 @@ xrep_tempfile_create( xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL); xfs_finish_inode_setup(sc->tempip); xchk_irele(sc, sc->tempip); + sc->tempip = NULL; } out_release_dquots: xfs_qm_dqrele(udqp); diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index 4c43e64b0091..0890221f71c4 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -3456,10 +3456,12 @@ DEFINE_EVENT(xrep_iunlink_resolve_class, name, \ TP_PROTO(struct xfs_perag *pag, unsigned int bucket, \ xfs_agino_t prev_agino, xfs_agino_t next_agino), \ TP_ARGS(pag, bucket, prev_agino, next_agino)) +DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_infinite_loop); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_uncached); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_wronglist); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_nolist); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_ok); +DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_allocated); TRACE_EVENT(xrep_iunlink_relink_next, TP_PROTO(struct xfs_inode *ip, xfs_agino_t next_agino), diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 9698f18695de..511e7b0d5871 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -773,7 +773,7 @@ xfs_dq_get_next_id( lock_flags = xfs_ilock_data_map_shared(quotip); error = xfs_iread_extents(NULL, quotip, XFS_DATA_FORK); if (error) - return error; + goto out_unlock; if (xfs_iext_lookup_extent(quotip, "ip->i_df, start, &cur, &got)) { /* contiguous chunk, bump startoff for the id calculation */ @@ -784,6 +784,7 @@ xfs_dq_get_next_id( error = -ENOENT; } +out_unlock: xfs_iunlock(quotip, lock_flags); return error; diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c index 2c2720ce6923..adda4e0dc41c 100644 --- a/fs/xfs/xfs_dquot_item_recover.c +++ b/fs/xfs/xfs_dquot_item_recover.c @@ -173,7 +173,7 @@ xlog_recover_dquot_commit_pass2( out_release: xfs_buf_relse(bp); - return 0; + return error; } const struct xlog_recover_item_ops xlog_dquot_item_ops = { diff --git a/include/linux/fs.h b/include/linux/fs.h index 466a76771b0a..19211eb49a8c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -171,13 +171,20 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset, #define FMODE_NOREUSE ((__force fmode_t)(1 << 23)) -/* FMODE_* bit 24 */ - /* File is embedded in backing_file object */ -#define FMODE_BACKING ((__force fmode_t)(1 << 25)) +#define FMODE_BACKING ((__force fmode_t)(1 << 24)) -/* File was opened by fanotify and shouldn't generate fanotify events */ -#define FMODE_NONOTIFY ((__force fmode_t)(1 << 26)) +/* + * Together with FMODE_NONOTIFY_PERM defines which fsnotify events shouldn't be + * generated (see below) + */ +#define FMODE_NONOTIFY ((__force fmode_t)(1 << 25)) + +/* + * Together with FMODE_NONOTIFY defines which fsnotify events shouldn't be + * generated (see below) + */ +#define FMODE_NONOTIFY_PERM ((__force fmode_t)(1 << 26)) /* File is capable of returning -EAGAIN if I/O will block */ #define FMODE_NOWAIT ((__force fmode_t)(1 << 27)) @@ -188,6 +195,32 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset, /* File does not contribute to nr_files count */ #define FMODE_NOACCOUNT ((__force fmode_t)(1 << 29)) +/* + * The two FMODE_NONOTIFY* define which fsnotify events should not be generated + * for a file. These are the possible values of (f->f_mode & + * FMODE_FSNOTIFY_MASK) and their meaning: + * + * FMODE_NONOTIFY - suppress all (incl. non-permission) events. + * FMODE_NONOTIFY_PERM - suppress permission (incl. pre-content) events. + * FMODE_NONOTIFY | FMODE_NONOTIFY_PERM - suppress only pre-content events. + */ +#define FMODE_FSNOTIFY_MASK \ + (FMODE_NONOTIFY | FMODE_NONOTIFY_PERM) + +#define FMODE_FSNOTIFY_NONE(mode) \ + ((mode & FMODE_FSNOTIFY_MASK) == FMODE_NONOTIFY) +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS +#define FMODE_FSNOTIFY_PERM(mode) \ + ((mode & FMODE_FSNOTIFY_MASK) == 0 || \ + (mode & FMODE_FSNOTIFY_MASK) == (FMODE_NONOTIFY | FMODE_NONOTIFY_PERM)) +#define FMODE_FSNOTIFY_HSM(mode) \ + ((mode & FMODE_FSNOTIFY_MASK) == 0) +#else +#define FMODE_FSNOTIFY_PERM(mode) 0 +#define FMODE_FSNOTIFY_HSM(mode) 0 +#endif + + /* * Attribute flags. These should be or-ed together to figure out what * has been changed! @@ -815,6 +848,11 @@ static inline void inode_lock(struct inode *inode) down_write(&inode->i_rwsem); } +static inline __must_check int inode_lock_killable(struct inode *inode) +{ + return down_write_killable(&inode->i_rwsem); +} + static inline void inode_unlock(struct inode *inode) { up_write(&inode->i_rwsem); @@ -825,6 +863,11 @@ static inline void inode_lock_shared(struct inode *inode) down_read(&inode->i_rwsem); } +static inline __must_check int inode_lock_shared_killable(struct inode *inode) +{ + return down_read_killable(&inode->i_rwsem); +} + static inline void inode_unlock_shared(struct inode *inode) { up_read(&inode->i_rwsem); @@ -3052,6 +3095,28 @@ static inline void allow_write_access(struct file *file) if (file) atomic_inc(&file_inode(file)->i_writecount); } + +/* + * Do not prevent write to executable file when watched by pre-content events. + * + * Note that FMODE_FSNOTIFY_HSM mode is set depending on pre-content watches at + * the time of file open and remains constant for entire lifetime of the file, + * so if pre-content watches are added post execution or removed before the end + * of the execution, it will not cause i_writecount reference leak. + */ +static inline int exe_file_deny_write_access(struct file *exe_file) +{ + if (unlikely(FMODE_FSNOTIFY_HSM(exe_file->f_mode))) + return 0; + return deny_write_access(exe_file); +} +static inline void exe_file_allow_write_access(struct file *exe_file) +{ + if (unlikely(!exe_file || FMODE_FSNOTIFY_HSM(exe_file->f_mode))) + return; + allow_write_access(exe_file); +} + static inline bool inode_is_open_for_write(const struct inode *inode) { return atomic_read(&inode->i_writecount) > 0; @@ -3398,7 +3463,7 @@ extern void put_filesystem(struct file_system_type *fs); extern struct file_system_type *get_fs_type(const char *name); extern void drop_super(struct super_block *sb); extern void drop_super_exclusive(struct super_block *sb); -extern void iterate_supers(void (*)(struct super_block *, void *), void *); +extern void iterate_supers(void (*f)(struct super_block *, void *), void *arg); extern void iterate_supers_type(struct file_system_type *, void (*)(struct super_block *, void *), void *); diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 278620e063ab..8d1849137a96 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -108,38 +108,35 @@ static inline void fsnotify_dentry(struct dentry *dentry, __u32 mask) fsnotify_parent(dentry, mask, dentry, FSNOTIFY_EVENT_DENTRY); } -static inline int fsnotify_file(struct file *file, __u32 mask) +static inline int fsnotify_path(const struct path *path, __u32 mask) { - const struct path *path; + return fsnotify_parent(path->dentry, mask, path, FSNOTIFY_EVENT_PATH); +} +static inline int fsnotify_file(struct file *file, __u32 mask) +{ /* * FMODE_NONOTIFY are fds generated by fanotify itself which should not * generate new events. We also don't want to generate events for * FMODE_PATH fds (involves open & close events) as they are just * handle creation / destruction events and not "real" file events. */ - if (file->f_mode & (FMODE_NONOTIFY | FMODE_PATH)) + if (FMODE_FSNOTIFY_NONE(file->f_mode)) return 0; - path = &file->f_path; - /* Permission events require group prio >= FSNOTIFY_PRIO_CONTENT */ - if (mask & ALL_FSNOTIFY_PERM_EVENTS && - !fsnotify_sb_has_priority_watchers(path->dentry->d_sb, - FSNOTIFY_PRIO_CONTENT)) - return 0; - - return fsnotify_parent(path->dentry, mask, path, FSNOTIFY_EVENT_PATH); + return fsnotify_path(&file->f_path, mask); } #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + +void file_set_fsnotify_mode(struct file *file); + /* * fsnotify_file_area_perm - permission hook before access to file range */ static inline int fsnotify_file_area_perm(struct file *file, int perm_mask, const loff_t *ppos, size_t count) { - __u32 fsnotify_mask = FS_ACCESS_PERM; - /* * filesystem may be modified in the context of permission events * (e.g. by HSM filling a file on access), so sb freeze protection @@ -150,7 +147,10 @@ static inline int fsnotify_file_area_perm(struct file *file, int perm_mask, if (!(perm_mask & MAY_READ)) return 0; - return fsnotify_file(file, fsnotify_mask); + if (likely(!FMODE_FSNOTIFY_PERM(file->f_mode))) + return 0; + + return fsnotify_path(&file->f_path, FS_ACCESS_PERM); } /* @@ -168,16 +168,23 @@ static inline int fsnotify_open_perm(struct file *file) { int ret; + if (likely(!FMODE_FSNOTIFY_PERM(file->f_mode))) + return 0; + if (file->f_flags & __FMODE_EXEC) { - ret = fsnotify_file(file, FS_OPEN_EXEC_PERM); + ret = fsnotify_path(&file->f_path, FS_OPEN_EXEC_PERM); if (ret) return ret; } - return fsnotify_file(file, FS_OPEN_PERM); + return fsnotify_path(&file->f_path, FS_OPEN_PERM); } #else +static inline void file_set_fsnotify_mode(struct file *file) +{ +} + static inline int fsnotify_file_area_perm(struct file *file, int perm_mask, const loff_t *ppos, size_t count) { diff --git a/include/linux/libata.h b/include/linux/libata.h index c41faa99feb3..3acf321e4c14 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1148,6 +1148,7 @@ extern int ata_scsi_ioctl(struct scsi_device *dev, unsigned int cmd, #define ATA_SCSI_COMPAT_IOCTL /* empty */ #endif extern int ata_scsi_queuecmd(struct Scsi_Host *h, struct scsi_cmnd *cmd); +enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *cmd); #if IS_REACHABLE(CONFIG_ATA) bool ata_scsi_dma_need_drain(struct request *rq); #else @@ -1461,6 +1462,7 @@ extern const struct attribute_group *ata_common_sdev_groups[]; .ioctl = ata_scsi_ioctl, \ ATA_SCSI_COMPAT_IOCTL \ .queuecommand = ata_scsi_queuecmd, \ + .eh_timed_out = ata_scsi_eh_timed_out, \ .dma_need_drain = ata_scsi_dma_need_drain, \ .this_id = ATA_SHT_THIS_ID, \ .emulated = ATA_SHT_EMULATED, \ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5c5e1c814ff2..0aa536df9cb5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2069,6 +2069,7 @@ struct net_device { unsigned long state; unsigned int flags; unsigned short hard_header_len; + enum netdev_stat_type pcpu_stat_type:8; netdev_features_t features; struct inet6_dev __rcu *ip6_ptr; __cacheline_group_end(net_device_read_txrx); @@ -2314,8 +2315,6 @@ struct net_device { void *ml_priv; enum netdev_ml_priv_type ml_priv_type; - enum netdev_stat_type pcpu_stat_type:8; - #if IS_ENABLED(CONFIG_GARP) struct garp_port __rcu *garp_port; #endif @@ -2746,9 +2745,9 @@ struct pcpu_sw_netstats { struct pcpu_dstats { u64_stats_t rx_packets; u64_stats_t rx_bytes; - u64_stats_t rx_drops; u64_stats_t tx_packets; u64_stats_t tx_bytes; + u64_stats_t rx_drops; u64_stats_t tx_drops; struct u64_stats_sync syncp; } __aligned(8 * sizeof(u64)); @@ -2793,6 +2792,46 @@ static inline void dev_lstats_add(struct net_device *dev, unsigned int len) u64_stats_update_end(&lstats->syncp); } +static inline void dev_dstats_rx_add(struct net_device *dev, + unsigned int len) +{ + struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats); + + u64_stats_update_begin(&dstats->syncp); + u64_stats_inc(&dstats->rx_packets); + u64_stats_add(&dstats->rx_bytes, len); + u64_stats_update_end(&dstats->syncp); +} + +static inline void dev_dstats_rx_dropped(struct net_device *dev) +{ + struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats); + + u64_stats_update_begin(&dstats->syncp); + u64_stats_inc(&dstats->rx_drops); + u64_stats_update_end(&dstats->syncp); +} + +static inline void dev_dstats_tx_add(struct net_device *dev, + unsigned int len) +{ + struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats); + + u64_stats_update_begin(&dstats->syncp); + u64_stats_inc(&dstats->tx_packets); + u64_stats_add(&dstats->tx_bytes, len); + u64_stats_update_end(&dstats->syncp); +} + +static inline void dev_dstats_tx_dropped(struct net_device *dev) +{ + struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats); + + u64_stats_update_begin(&dstats->syncp); + u64_stats_inc(&dstats->tx_drops); + u64_stats_update_end(&dstats->syncp); +} + #define __netdev_alloc_pcpu_stats(type, gfp) \ ({ \ typeof(type) __percpu *pcpu_stats = alloc_percpu_gfp(type, gfp);\ diff --git a/include/linux/pagewalk.h b/include/linux/pagewalk.h index f5eb5a32aeed..21d07542903c 100644 --- a/include/linux/pagewalk.h +++ b/include/linux/pagewalk.h @@ -117,10 +117,9 @@ struct mm_walk { int walk_page_range(struct mm_struct *mm, unsigned long start, unsigned long end, const struct mm_walk_ops *ops, void *private); -int walk_page_range_novma(struct mm_struct *mm, unsigned long start, - unsigned long end, const struct mm_walk_ops *ops, - pgd_t *pgd, - void *private); +int walk_kernel_page_table_range(unsigned long start, + unsigned long end, const struct mm_walk_ops *ops, + pgd_t *pgd, void *private); int walk_page_range_vma(struct vm_area_struct *vma, unsigned long start, unsigned long end, const struct mm_walk_ops *ops, void *private); diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h index 6a4435cfa977..0f5f94137f6d 100644 --- a/include/linux/psp-sev.h +++ b/include/linux/psp-sev.h @@ -816,6 +816,15 @@ struct sev_data_snp_commit { #ifdef CONFIG_CRYPTO_DEV_SP_PSP +/** + * sev_module_init - perform PSP SEV module initialization + * + * Returns: + * 0 if the PSP module is successfully initialized + * negative value if the PSP module initialization fails + */ +int sev_module_init(void); + /** * sev_platform_init - perform SEV INIT command * diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 5f56fa878013..eb7e36a130ca 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -282,6 +282,8 @@ struct tcp_sock { * Header prediction flags * 0x5?10 << 16 + snd_wnd in net byte order */ + u8 nonagle : 4,/* Disable Nagle algorithm? */ + rate_app_limited:1; /* rate_{delivered,interval_us} limited? */ __be32 pred_flags; u64 tcp_clock_cache; /* cache last tcp_clock_ns() (see tcp_mstamp_refresh()) */ u64 tcp_mstamp; /* most recent packet received/sent */ @@ -300,8 +302,6 @@ struct tcp_sock { * Options received (usually on last packet, some only on SYN packets). */ struct tcp_options_received rx_opt; - u8 nonagle : 4,/* Disable Nagle algorithm? */ - rate_app_limited:1; /* rate_{delivered,interval_us} limited? */ __cacheline_group_end(tcp_sock_write_txrx); /* RX read-write hotpath cache lines */ diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index 75342022d144..8ff68ce1860b 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -216,6 +216,12 @@ static inline bool vma_can_userfault(struct vm_area_struct *vma, unsigned long vm_flags, bool wp_async) { + if (vma->vm_flags & VM_SHADOW_STACK) + return false; + + if (!is_vm_hugetlb_page(vma) && (vma->vm_flags & VM_SPECIAL)) + return false; + vm_flags &= __VM_UFFD_FLAGS; if (vm_flags & VM_DROPPABLE) diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 583fd1afd238..e4c30bfa1cf1 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -651,13 +651,29 @@ static inline int iptunnel_pull_offloads(struct sk_buff *skb) static inline void iptunnel_xmit_stats(struct net_device *dev, int pkt_len) { if (pkt_len > 0) { - struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats); - - u64_stats_update_begin(&tstats->syncp); - u64_stats_add(&tstats->tx_bytes, pkt_len); - u64_stats_inc(&tstats->tx_packets); - u64_stats_update_end(&tstats->syncp); - put_cpu_ptr(tstats); + if (dev->pcpu_stat_type == NETDEV_PCPU_STAT_DSTATS) { + struct pcpu_dstats *dstats = get_cpu_ptr(dev->dstats); + + u64_stats_update_begin(&dstats->syncp); + u64_stats_add(&dstats->tx_bytes, pkt_len); + u64_stats_inc(&dstats->tx_packets); + u64_stats_update_end(&dstats->syncp); + put_cpu_ptr(dstats); + return; + } + if (dev->pcpu_stat_type == NETDEV_PCPU_STAT_TSTATS) { + struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats); + + u64_stats_update_begin(&tstats->syncp); + u64_stats_add(&tstats->tx_bytes, pkt_len); + u64_stats_inc(&tstats->tx_packets); + u64_stats_update_end(&tstats->syncp); + put_cpu_ptr(tstats); + return; + } + pr_err_once("iptunnel_xmit_stats pcpu_stat_type=%d\n", + dev->pcpu_stat_type); + WARN_ON_ONCE(1); return; } diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index e0077fd28ad1..35f012f62015 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -34,6 +34,12 @@ #define IP_VS_HDR_INVERSE 1 #define IP_VS_HDR_ICMP 2 +/* Destination Server Flags */ +#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */ + +/* Destination Server Config Flags */ +#define IP_VS_DEST_CF_AVAILABLE 0x0001 /* server is available */ + /* Generic access of ipvs struct */ static inline struct netns_ipvs *net_ipvs(struct net* net) { @@ -725,6 +731,7 @@ struct ip_vs_dest { volatile unsigned int flags; /* dest status flags */ atomic_t conn_flags; /* flags to copy to conn */ atomic_t weight; /* server weight */ + unsigned long cflags; /* config flags */ atomic_t last_weight; /* server latest weight */ __u16 tun_type; /* tunnel type */ __be16 tun_port; /* tunnel port */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 5d39b0e8dd90..1cc25e0b3bde 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -668,7 +668,7 @@ void tcp_send_active_reset(struct sock *sk, gfp_t priority, enum sk_rst_reason reason); int tcp_send_synack(struct sock *); void tcp_push_one(struct sock *, unsigned int mss_now); -void __tcp_send_ack(struct sock *sk, u32 rcv_nxt); +void __tcp_send_ack(struct sock *sk, u32 rcv_nxt, u16 flags); void tcp_send_ack(struct sock *sk); void tcp_send_delayed_ack(struct sock *sk); void tcp_send_loss_probe(struct sock *sk); @@ -768,33 +768,6 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp) return usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us); } -static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd) -{ - /* mptcp hooks are only on the slow path */ - if (sk_is_mptcp((struct sock *)tp)) - return; - - tp->pred_flags = htonl((tp->tcp_header_len << 26) | - ntohl(TCP_FLAG_ACK) | - snd_wnd); -} - -static inline void tcp_fast_path_on(struct tcp_sock *tp) -{ - __tcp_fast_path_on(tp, tp->snd_wnd >> tp->rx_opt.snd_wscale); -} - -static inline void tcp_fast_path_check(struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - - if (RB_EMPTY_ROOT(&tp->out_of_order_queue) && - tp->rcv_wnd && - atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && - !tp->urg_data) - tcp_fast_path_on(tp); -} - u32 tcp_delack_max(const struct sock *sk); /* Compute the actual rto_min value */ @@ -1743,8 +1716,37 @@ static inline bool tcp_paws_reject(const struct tcp_options_received *rx_opt, return true; } +static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd) +{ + /* mptcp hooks are only on the slow path */ + if (sk_is_mptcp((struct sock *)tp)) + return; + + tp->pred_flags = htonl((tp->tcp_header_len << 26) | + ntohl(TCP_FLAG_ACK) | + snd_wnd); +} + +static inline void tcp_fast_path_on(struct tcp_sock *tp) +{ + __tcp_fast_path_on(tp, tp->snd_wnd >> tp->rx_opt.snd_wscale); +} + +static inline void tcp_fast_path_check(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + + if (RB_EMPTY_ROOT(&tp->out_of_order_queue) && + tp->rcv_wnd && + atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && + !tp->urg_data) + tcp_fast_path_on(tp); +} + bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb, int mib_idx, u32 *last_oow_ack_time); +void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req); static inline void tcp_mib_init(struct net *net) { diff --git a/include/uapi/linux/ip_vs.h b/include/uapi/linux/ip_vs.h index 1ed234e7f251..2c37c6ac7525 100644 --- a/include/uapi/linux/ip_vs.h +++ b/include/uapi/linux/ip_vs.h @@ -28,12 +28,6 @@ #define IP_VS_SVC_F_SCHED_SH_FALLBACK IP_VS_SVC_F_SCHED1 /* SH fallback */ #define IP_VS_SVC_F_SCHED_SH_PORT IP_VS_SVC_F_SCHED2 /* SH use port */ -/* - * Destination Server Flags - */ -#define IP_VS_DEST_F_AVAILABLE 0x0001 /* server is available */ -#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */ - /* * IPVS sync daemon states */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 275c9fabc955..99b3f539e431 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -4635,27 +4635,30 @@ static bool __is_pointer_value(bool allow_ptr_leaks, return reg->type != SCALAR_VALUE; } +static void clear_scalar_id(struct bpf_reg_state *reg) +{ + reg->id = 0; + reg->off = 0; +} + static void assign_scalar_id_before_mov(struct bpf_verifier_env *env, struct bpf_reg_state *src_reg) { if (src_reg->type != SCALAR_VALUE) return; - - if (src_reg->id & BPF_ADD_CONST) { - /* - * The verifier is processing rX = rY insn and - * rY->id has special linked register already. - * Cleared it, since multiple rX += const are not supported. - */ - src_reg->id = 0; - src_reg->off = 0; - } - + /* + * The verifier is processing rX = rY insn and + * rY->id has special linked register already. + * Cleared it, since multiple rX += const are not supported. + */ + if (src_reg->id & BPF_ADD_CONST) + clear_scalar_id(src_reg); + /* + * Ensure that src_reg has a valid ID that will be copied to + * dst_reg and then will be used by sync_linked_regs() to + * propagate min/max range. + */ if (!src_reg->id && !tnum_is_const(src_reg->var_off)) - /* Ensure that src_reg has a valid ID that will be copied to - * dst_reg and then will be used by sync_linked_regs() to - * propagate min/max range. - */ src_reg->id = ++env->id_gen; } @@ -5093,7 +5096,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env, * coerce_reg_to_size will adjust the boundaries. */ if (get_reg_width(reg) > size * BITS_PER_BYTE) - state->regs[dst_regno].id = 0; + clear_scalar_id(&state->regs[dst_regno]); } else { int spill_cnt = 0, zero_cnt = 0; @@ -14544,7 +14547,8 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, */ if (env->bpf_capable && BPF_OP(insn->code) == BPF_ADD && !alu32 && - dst_reg->id && is_reg_const(src_reg, false)) { + dst_reg->id && is_reg_const(src_reg, false) && + !(BPF_SRC(insn->code) == BPF_X && insn->src_reg == insn->dst_reg)) { u64 val = reg_const_value(src_reg, false); if ((dst_reg->id & BPF_ADD_CONST) || @@ -14554,8 +14558,7 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, * If the register already went through rX += val * we cannot accumulate another val into rx->off. */ - dst_reg->off = 0; - dst_reg->id = 0; + clear_scalar_id(dst_reg); } else { dst_reg->id |= BPF_ADD_CONST; dst_reg->off = val; @@ -14565,7 +14568,7 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, * Make sure ID is cleared otherwise dst_reg min/max could be * incorrectly propagated into other registers by sync_linked_regs() */ - dst_reg->id = 0; + clear_scalar_id(dst_reg); } return 0; } @@ -14689,7 +14692,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) assign_scalar_id_before_mov(env, src_reg); copy_register_state(dst_reg, src_reg); if (!no_sext) - dst_reg->id = 0; + clear_scalar_id(dst_reg); coerce_reg_to_size_sx(dst_reg, insn->off >> 3); dst_reg->live |= REG_LIVE_WRITTEN; dst_reg->subreg_def = DEF_NOT_SUBREG; @@ -14716,7 +14719,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) * propagated into src_reg by sync_linked_regs() */ if (!is_src_reg_u32) - dst_reg->id = 0; + clear_scalar_id(dst_reg); dst_reg->live |= REG_LIVE_WRITTEN; dst_reg->subreg_def = env->insn_idx + 1; } else { @@ -14727,7 +14730,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) assign_scalar_id_before_mov(env, src_reg); copy_register_state(dst_reg, src_reg); if (!no_sext) - dst_reg->id = 0; + clear_scalar_id(dst_reg); dst_reg->live |= REG_LIVE_WRITTEN; dst_reg->subreg_def = env->insn_idx + 1; coerce_subreg_to_size_sx(dst_reg, insn->off >> 3); @@ -15558,7 +15561,7 @@ static void __collect_linked_regs(struct linked_regs *reg_set, struct bpf_reg_st e->is_reg = is_reg; e->regno = spi_or_reg; } else { - reg->id = 0; + clear_scalar_id(reg); } } diff --git a/kernel/events/core.c b/kernel/events/core.c index da77f856e1c8..dd4d535fcb42 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -2206,6 +2206,34 @@ static inline struct list_head *get_event_list(struct perf_event *event) &event->pmu_ctx->flexible_active; } +/* @sibling must already be unlinked from its old leader's sibling_list. */ +static void perf_promote_sibling_to_leader(struct perf_event *sibling, + struct perf_event_context *ctx, + int group_caps) +{ + /* + * Events that have PERF_EV_CAP_SIBLING require being part of + * a group and cannot exist on their own, schedule them out + * and move them into the ERROR state. Also see + * _perf_event_enable(), it will not be able to recover this + * ERROR state. + */ + if (sibling->event_caps & PERF_EV_CAP_SIBLING) + __event_disable(sibling, ctx, PERF_EVENT_STATE_ERROR); + + sibling->group_leader = sibling; + sibling->group_caps = group_caps; + + if (sibling->attach_state & PERF_ATTACH_CONTEXT) { + add_event_to_groups(sibling, ctx); + + if (sibling->state == PERF_EVENT_STATE_ACTIVE) + list_add_tail(&sibling->active_list, get_event_list(sibling)); + } + + perf_event__header_size(sibling); +} + static void perf_group_detach(struct perf_event *event) { struct perf_event *leader = event->group_leader; @@ -2229,8 +2257,9 @@ static void perf_group_detach(struct perf_event *event) */ if (leader != event) { list_del_init(&event->sibling_list); - event->group_leader->nr_siblings--; - event->group_leader->group_generation++; + leader->nr_siblings--; + leader->group_generation++; + perf_promote_sibling_to_leader(event, ctx, event->event_caps); goto out; } @@ -2240,32 +2269,14 @@ static void perf_group_detach(struct perf_event *event) * to whatever list we are on. */ list_for_each_entry_safe(sibling, tmp, &event->sibling_list, sibling_list) { - - /* - * Events that have PERF_EV_CAP_SIBLING require being part of - * a group and cannot exist on their own, schedule them out - * and move them into the ERROR state. Also see - * _perf_event_enable(), it will not be able to recover this - * ERROR state. - */ - if (sibling->event_caps & PERF_EV_CAP_SIBLING) - __event_disable(sibling, ctx, PERF_EVENT_STATE_ERROR); - - sibling->group_leader = sibling; list_del_init(&sibling->sibling_list); /* Inherit group flags from the previous leader */ - sibling->group_caps = event->group_caps; - - if (sibling->attach_state & PERF_ATTACH_CONTEXT) { - add_event_to_groups(sibling, event->ctx); - - if (sibling->state == PERF_EVENT_STATE_ACTIVE) - list_add_tail(&sibling->active_list, get_event_list(sibling)); - } + perf_promote_sibling_to_leader(sibling, ctx, event->group_caps); WARN_ON_ONCE(sibling->ctx != event->ctx); } + event->nr_siblings = 0; out: for_each_sibling_event(tmp, leader) @@ -2443,12 +2454,8 @@ __perf_remove_from_context(struct perf_event *event, if (flags & DETACH_DEAD) state = PERF_EVENT_STATE_DEAD; - event_sched_out(event, ctx); + __event_disable(event, ctx, state); - if (event->state > PERF_EVENT_STATE_OFF) - perf_cgroup_event_disable(event, ctx); - - perf_event_set_state(event, min(event->state, state)); if (flags & DETACH_GROUP) perf_group_detach(event); if (flags & DETACH_CHILD) @@ -2517,8 +2524,9 @@ static void __event_disable(struct perf_event *event, enum perf_event_state state) { event_sched_out(event, ctx); - perf_cgroup_event_disable(event, ctx); - perf_event_set_state(event, state); + if (event->state > PERF_EVENT_STATE_OFF) + perf_cgroup_event_disable(event, ctx); + perf_event_set_state(event, min(event->state, state)); } /* @@ -13522,13 +13530,11 @@ perf_event_exit_event(struct perf_event *event, perf_event_wakeup(event); } -static void perf_event_exit_task_context(struct task_struct *child) +static void perf_event_exit_task_context(struct task_struct *child, bool exit) { struct perf_event_context *child_ctx, *clone_ctx = NULL; struct perf_event *child_event, *next; - WARN_ON_ONCE(child != current); - child_ctx = perf_pin_task_context(child); if (!child_ctx) return; @@ -13551,7 +13557,8 @@ static void perf_event_exit_task_context(struct task_struct *child) * in. */ raw_spin_lock_irq(&child_ctx->lock); - task_ctx_sched_out(child_ctx, NULL, EVENT_ALL); + if (exit) + task_ctx_sched_out(child_ctx, NULL, EVENT_ALL); /* * Now that the context is inactive, destroy the task <-> ctx relation @@ -13560,7 +13567,7 @@ static void perf_event_exit_task_context(struct task_struct *child) RCU_INIT_POINTER(child->perf_event_ctxp, NULL); put_ctx(child_ctx); /* cannot be last */ WRITE_ONCE(child_ctx->task, TASK_TOMBSTONE); - put_task_struct(current); /* cannot be last */ + put_task_struct(child); /* cannot be last */ clone_ctx = unclone_ctx(child_ctx); raw_spin_unlock_irq(&child_ctx->lock); @@ -13573,13 +13580,31 @@ static void perf_event_exit_task_context(struct task_struct *child) * won't get any samples after PERF_RECORD_EXIT. We can however still * get a few PERF_RECORD_READ events. */ - perf_event_task(child, child_ctx, 0); + if (exit) + perf_event_task(child, child_ctx, 0); list_for_each_entry_safe(child_event, next, &child_ctx->event_list, event_entry) perf_event_exit_event(child_event, child_ctx, 0); mutex_unlock(&child_ctx->mutex); + if (!exit) { + /* + * perf_event_release_kernel() could still have a reference on + * this context. In that case we must wait for these events to + * have been freed (in particular all their references to this + * task must've been dropped). + * + * Without this copy_process() will unconditionally free this + * task (irrespective of its reference count) and + * _free_event()'s put_task_struct(event->hw.target) will be a + * use-after-free. + * + * Wait for all events to drop their context reference. + */ + wait_var_event(&child_ctx->refcount, + refcount_read(&child_ctx->refcount) == 1); + } put_ctx(child_ctx); } @@ -13607,7 +13632,7 @@ void perf_event_exit_task(struct task_struct *child) } mutex_unlock(&child->perf_event_mutex); - perf_event_exit_task_context(child); + perf_event_exit_task_context(child, true); /* * The perf_event_exit_task_context calls perf_event_task @@ -13618,25 +13643,6 @@ void perf_event_exit_task(struct task_struct *child) perf_event_task(child, NULL, 0); } -static void perf_free_event(struct perf_event *event, - struct perf_event_context *ctx) -{ - struct perf_event *parent = event->parent; - - if (WARN_ON_ONCE(!parent)) - return; - - mutex_lock(&parent->child_mutex); - list_del_init(&event->child_list); - mutex_unlock(&parent->child_mutex); - - raw_spin_lock_irq(&ctx->lock); - perf_group_detach(event); - list_del_event(event, ctx); - raw_spin_unlock_irq(&ctx->lock); - put_event(event); -} - /* * Free a context as created by inheritance by perf_event_init_task() below, * used by fork() in case of fail. @@ -13646,48 +13652,7 @@ static void perf_free_event(struct perf_event *event, */ void perf_event_free_task(struct task_struct *task) { - struct perf_event_context *ctx; - struct perf_event *event, *tmp; - - ctx = rcu_access_pointer(task->perf_event_ctxp); - if (!ctx) - return; - - mutex_lock(&ctx->mutex); - raw_spin_lock_irq(&ctx->lock); - /* - * Destroy the task <-> ctx relation and mark the context dead. - * - * This is important because even though the task hasn't been - * exposed yet the context has been (through child_list). - */ - RCU_INIT_POINTER(task->perf_event_ctxp, NULL); - WRITE_ONCE(ctx->task, TASK_TOMBSTONE); - put_task_struct(task); /* cannot be last */ - raw_spin_unlock_irq(&ctx->lock); - - - list_for_each_entry_safe(event, tmp, &ctx->event_list, event_entry) - perf_free_event(event, ctx); - - mutex_unlock(&ctx->mutex); - - /* - * perf_event_release_kernel() could've stolen some of our - * child events and still have them on its free_list. In that - * case we must wait for these events to have been freed (in - * particular all their references to this task must've been - * dropped). - * - * Without this copy_process() will unconditionally free this - * task (irrespective of its reference count) and - * _free_event()'s put_task_struct(event->hw.target) will be a - * use-after-free. - * - * Wait for all events to drop their context reference. - */ - wait_var_event(&ctx->refcount, refcount_read(&ctx->refcount) == 1); - put_ctx(ctx); /* must be last */ + perf_event_exit_task_context(task, false); } void perf_event_delayed_put(struct task_struct *task) diff --git a/kernel/fork.c b/kernel/fork.c index 2b7577edffa2..c0ecb097aa1d 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -627,8 +627,8 @@ static void dup_mm_exe_file(struct mm_struct *mm, struct mm_struct *oldmm) * We depend on the oldmm having properly denied write access to the * exe_file already. */ - if (exe_file && deny_write_access(exe_file)) - pr_warn_once("deny_write_access() failed in %s\n", __func__); + if (exe_file && exe_file_deny_write_access(exe_file)) + pr_warn_once("exe_file_deny_write_access() failed in %s\n", __func__); } #ifdef CONFIG_MMU @@ -1439,13 +1439,13 @@ int set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) * We expect the caller (i.e., sys_execve) to already denied * write access, so this is unlikely to fail. */ - if (unlikely(deny_write_access(new_exe_file))) + if (unlikely(exe_file_deny_write_access(new_exe_file))) return -EACCES; get_file(new_exe_file); } rcu_assign_pointer(mm->exe_file, new_exe_file); if (old_exe_file) { - allow_write_access(old_exe_file); + exe_file_allow_write_access(old_exe_file); fput(old_exe_file); } return 0; @@ -1486,7 +1486,7 @@ int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) return ret; } - ret = deny_write_access(new_exe_file); + ret = exe_file_deny_write_access(new_exe_file); if (ret) return -EACCES; get_file(new_exe_file); @@ -1498,7 +1498,7 @@ int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) mmap_write_unlock(mm); if (old_exe_file) { - allow_write_access(old_exe_file); + exe_file_allow_write_access(old_exe_file); fput(old_exe_file); } return 0; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index db4fed83957c..14c1bcbce106 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -2593,7 +2593,8 @@ unsigned long ftrace_find_rec_direct(unsigned long ip) { struct ftrace_func_entry *entry; - entry = __ftrace_lookup_ip(direct_functions, ip); + guard(preempt_notrace)(); + entry = __ftrace_lookup_ip(rcu_dereference_sched(direct_functions), ip); if (!entry) return 0; @@ -7769,7 +7770,8 @@ static void add_to_clear_hash_list(struct list_head *clear_list, void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr) { unsigned long start = (unsigned long)(start_ptr); - unsigned long end = (unsigned long)(end_ptr); + /* end is inclusive and end_ptr is exclusive */ + unsigned long end = (unsigned long)(end_ptr) - 1; struct ftrace_page **last_pg = &ftrace_pages_start; struct ftrace_page *tmp_page = NULL; struct ftrace_page *pg; @@ -7779,6 +7781,9 @@ void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr) struct ftrace_init_func *func, *func_next; LIST_HEAD(clear_hash); + if (start_ptr >= end_ptr) + return; + key.ip = start; key.flags = end; /* overload flags, as it is unsigned long */ diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 3f90c7f752eb..e59094a45898 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2193,7 +2193,7 @@ static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, static struct ring_buffer_per_cpu * rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) { - struct ring_buffer_per_cpu *cpu_buffer; + struct ring_buffer_per_cpu *cpu_buffer __free(kfree) = NULL; struct ring_buffer_meta *meta; struct buffer_page *bpage; struct page *page; @@ -2219,7 +2219,8 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) bpage = kzalloc_node(ALIGN(sizeof(*bpage), cache_line_size()), GFP_KERNEL, cpu_to_node(cpu)); if (!bpage) - goto fail_free_buffer; + return NULL; + bpage->order = cpu_buffer->buffer->subbuf_order; rb_check_bpage(cpu_buffer, bpage); @@ -2238,6 +2239,8 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) if (cpu_buffer->ring_meta->head_buffer) rb_meta_buffer_update(cpu_buffer, bpage); bpage->range = 1; + + atomic_inc(&cpu_buffer->resize_disabled); } else { page = alloc_pages_node(cpu_to_node(cpu), GFP_KERNEL | __GFP_COMP | __GFP_ZERO, @@ -2285,13 +2288,11 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) rb_head_page_activate(cpu_buffer); } - return cpu_buffer; + return_ptr(cpu_buffer); fail_free_reader: free_buffer_page(cpu_buffer->reader_page); - fail_free_buffer: - kfree(cpu_buffer); return NULL; } @@ -2335,7 +2336,7 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, unsigned long end, struct lock_class_key *key) { - struct trace_buffer *buffer; + struct trace_buffer *buffer __free(kfree) = NULL; long nr_pages; int subbuf_size; int bsize; @@ -2349,7 +2350,7 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, return NULL; if (!zalloc_cpumask_var(&buffer->cpumask, GFP_KERNEL)) - goto fail_free_buffer; + return NULL; buffer->subbuf_order = order; subbuf_size = (PAGE_SIZE << order); @@ -2441,7 +2442,7 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, atomic_notifier_chain_register(&panic_notifier_list, &buffer->flush_nb); } - return buffer; + return_ptr(buffer); fail_free_buffers: for_each_buffer_cpu(buffer, cpu) { @@ -2453,8 +2454,6 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, fail_free_cpumask: free_cpumask_var(buffer->cpumask); - fail_free_buffer: - kfree(buffer); return NULL; } @@ -6253,41 +6252,37 @@ int ring_buffer_swap_cpu(struct trace_buffer *buffer_a, { struct ring_buffer_per_cpu *cpu_buffer_a; struct ring_buffer_per_cpu *cpu_buffer_b; - int ret = -EINVAL; + int ret = -EBUSY; if (!cpumask_test_cpu(cpu, buffer_a->cpumask) || !cpumask_test_cpu(cpu, buffer_b->cpumask)) - goto out; + return -EINVAL; cpu_buffer_a = buffer_a->buffers[cpu]; cpu_buffer_b = buffer_b->buffers[cpu]; /* It's up to the callers to not try to swap mapped buffers */ - if (WARN_ON_ONCE(cpu_buffer_a->mapped || cpu_buffer_b->mapped)) { - ret = -EBUSY; - goto out; - } + if (WARN_ON_ONCE(cpu_buffer_a->mapped || cpu_buffer_b->mapped)) + return -EBUSY; /* At least make sure the two buffers are somewhat the same */ if (cpu_buffer_a->nr_pages != cpu_buffer_b->nr_pages) - goto out; + return -EINVAL; if (buffer_a->subbuf_order != buffer_b->subbuf_order) - goto out; - - ret = -EAGAIN; + return -EINVAL; if (atomic_read(&buffer_a->record_disabled)) - goto out; + return -EAGAIN; if (atomic_read(&buffer_b->record_disabled)) - goto out; + return -EAGAIN; if (atomic_read(&cpu_buffer_a->record_disabled)) - goto out; + return -EAGAIN; if (atomic_read(&cpu_buffer_b->record_disabled)) - goto out; + return -EAGAIN; /* * We can't do a synchronize_rcu here because this @@ -6298,10 +6293,10 @@ int ring_buffer_swap_cpu(struct trace_buffer *buffer_a, atomic_inc(&cpu_buffer_a->record_disabled); atomic_inc(&cpu_buffer_b->record_disabled); - ret = -EBUSY; - if (local_read(&cpu_buffer_a->committing)) + /* Do not swap if either buffer is in the process of writing */ + if (cpu_buffer_a->current_context) goto out_dec; - if (local_read(&cpu_buffer_b->committing)) + if (cpu_buffer_b->current_context) goto out_dec; /* @@ -6324,7 +6319,6 @@ int ring_buffer_swap_cpu(struct trace_buffer *buffer_a, out_dec: atomic_dec(&cpu_buffer_a->record_disabled); atomic_dec(&cpu_buffer_b->record_disabled); -out: return ret; } EXPORT_SYMBOL_GPL(ring_buffer_swap_cpu); @@ -6483,38 +6477,37 @@ int ring_buffer_read_page(struct trace_buffer *buffer, struct buffer_data_page *bpage; struct buffer_page *reader; unsigned long missed_events; - unsigned long flags; unsigned int commit; unsigned int read; u64 save_timestamp; - int ret = -1; if (!cpumask_test_cpu(cpu, buffer->cpumask)) - goto out; + return -1; /* * If len is not big enough to hold the page header, then * we can not copy anything. */ if (len <= BUF_PAGE_HDR_SIZE) - goto out; + return -1; len -= BUF_PAGE_HDR_SIZE; if (!data_page || !data_page->data) - goto out; + return -1; + if (data_page->order != buffer->subbuf_order) - goto out; + return -1; bpage = data_page->data; if (!bpage) - goto out; + return -1; - raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags); + guard(raw_spinlock_irqsave)(&cpu_buffer->reader_lock); reader = rb_get_reader_page(cpu_buffer); if (!reader) - goto out_unlock; + return -1; event = rb_reader_event(cpu_buffer); @@ -6548,7 +6541,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, if (full && (!read || (len < (commit - read)) || cpu_buffer->reader_page == cpu_buffer->commit_page)) - goto out_unlock; + return -1; if (len > (commit - read)) len = (commit - read); @@ -6557,7 +6550,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, size = rb_event_ts_length(event); if (len < size) - goto out_unlock; + return -1; /* save the current timestamp, since the user will need it */ save_timestamp = cpu_buffer->read_stamp; @@ -6615,7 +6608,6 @@ int ring_buffer_read_page(struct trace_buffer *buffer, if (reader->real_end) local_set(&bpage->commit, reader->real_end); } - ret = read; cpu_buffer->lost_events = 0; @@ -6642,11 +6634,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, if (commit < buffer->subbuf_size) memset(&bpage->data[commit], 0, buffer->subbuf_size - commit); - out_unlock: - raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags); - - out: - return ret; + return read; } EXPORT_SYMBOL_GPL(ring_buffer_read_page); @@ -7007,7 +6995,7 @@ static int __rb_map_vma(struct ring_buffer_per_cpu *cpu_buffer, { unsigned long nr_subbufs, nr_pages, nr_vma_pages, pgoff = vma->vm_pgoff; unsigned int subbuf_pages, subbuf_order; - struct page **pages; + struct page **pages __free(kfree) = NULL; int p = 0, s = 0; int err; @@ -7075,10 +7063,8 @@ static int __rb_map_vma(struct ring_buffer_per_cpu *cpu_buffer, struct page *page; int off = 0; - if (WARN_ON_ONCE(s >= nr_subbufs)) { - err = -EINVAL; - goto out; - } + if (WARN_ON_ONCE(s >= nr_subbufs)) + return -EINVAL; page = virt_to_page((void *)cpu_buffer->subbuf_ids[s]); @@ -7093,9 +7079,6 @@ static int __rb_map_vma(struct ring_buffer_per_cpu *cpu_buffer, err = vm_insert_pages(vma, vma->vm_start, pages, &nr_pages); -out: - kfree(pages); - return err; } #else @@ -7111,36 +7094,34 @@ int ring_buffer_map(struct trace_buffer *buffer, int cpu, { struct ring_buffer_per_cpu *cpu_buffer; unsigned long flags, *subbuf_ids; - int err = 0; + int err; if (!cpumask_test_cpu(cpu, buffer->cpumask)) return -EINVAL; cpu_buffer = buffer->buffers[cpu]; - mutex_lock(&cpu_buffer->mapping_lock); + guard(mutex)(&cpu_buffer->mapping_lock); if (cpu_buffer->user_mapped) { err = __rb_map_vma(cpu_buffer, vma); if (!err) err = __rb_inc_dec_mapped(cpu_buffer, true); - mutex_unlock(&cpu_buffer->mapping_lock); return err; } /* prevent another thread from changing buffer/sub-buffer sizes */ - mutex_lock(&buffer->mutex); + guard(mutex)(&buffer->mutex); err = rb_alloc_meta_page(cpu_buffer); if (err) - goto unlock; + return err; /* subbuf_ids include the reader while nr_pages does not */ subbuf_ids = kcalloc(cpu_buffer->nr_pages + 1, sizeof(*subbuf_ids), GFP_KERNEL); if (!subbuf_ids) { rb_free_meta_page(cpu_buffer); - err = -ENOMEM; - goto unlock; + return -ENOMEM; } atomic_inc(&cpu_buffer->resize_disabled); @@ -7168,11 +7149,7 @@ int ring_buffer_map(struct trace_buffer *buffer, int cpu, atomic_dec(&cpu_buffer->resize_disabled); } -unlock: - mutex_unlock(&buffer->mutex); - mutex_unlock(&cpu_buffer->mapping_lock); - - return err; + return 0; } /* @@ -7200,24 +7177,22 @@ int ring_buffer_unmap(struct trace_buffer *buffer, int cpu) { struct ring_buffer_per_cpu *cpu_buffer; unsigned long flags; - int err = 0; if (!cpumask_test_cpu(cpu, buffer->cpumask)) return -EINVAL; cpu_buffer = buffer->buffers[cpu]; - mutex_lock(&cpu_buffer->mapping_lock); + guard(mutex)(&cpu_buffer->mapping_lock); if (!cpu_buffer->user_mapped) { - err = -ENODEV; - goto out; + return -ENODEV; } else if (cpu_buffer->user_mapped > 1) { __rb_inc_dec_mapped(cpu_buffer, false); - goto out; + return 0; } - mutex_lock(&buffer->mutex); + guard(mutex)(&buffer->mutex); raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags); /* This is the last user space mapping */ @@ -7232,12 +7207,7 @@ int ring_buffer_unmap(struct trace_buffer *buffer, int cpu) rb_free_meta_page(cpu_buffer); atomic_dec(&cpu_buffer->resize_disabled); - mutex_unlock(&buffer->mutex); - -out: - mutex_unlock(&cpu_buffer->mapping_lock); - - return err; + return 0; } int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu) diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c index 4120a73f4933..d4a4d51750f5 100644 --- a/mm/damon/paddr.c +++ b/mm/damon/paddr.c @@ -414,6 +414,8 @@ static unsigned int damon_pa_migrate_folio_list(struct list_head *folio_list, while (!list_empty(folio_list)) { folio = lru_to_folio(folio_list); list_del(&folio->lru); + node_stat_sub_folio(folio, NR_ISOLATED_ANON + + folio_is_file_lru(folio)); folio_putback_lru(folio); } @@ -432,8 +434,17 @@ static unsigned long damon_pa_migrate_pages(struct list_head *folio_list, return nr_migrated; if (target_nid < 0 || target_nid >= MAX_NUMNODES || - !node_state(target_nid, N_MEMORY)) + !node_state(target_nid, N_MEMORY)) { + while (!list_empty(folio_list)) { + struct folio *folio = lru_to_folio(folio_list); + + list_del(&folio->lru); + node_stat_sub_folio(folio, NR_ISOLATED_ANON + + folio_is_file_lru(folio)); + folio_putback_lru(folio); + } return nr_migrated; + } noreclaim_flag = memalloc_noreclaim_save(); @@ -480,6 +491,8 @@ static unsigned long damon_pa_migrate(struct damon_region *r, struct damos *s) if (!folio_isolate_lru(folio)) goto put_folio; + node_stat_add_folio(folio, NR_ISOLATED_ANON + + folio_is_file_lru(folio)); list_add(&folio->lru, &folio_list); put_folio: addr += folio_size(folio); diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c index 57b7f591eee8..f794e313fb15 100644 --- a/mm/hugetlb_vmemmap.c +++ b/mm/hugetlb_vmemmap.c @@ -166,7 +166,7 @@ static int vmemmap_remap_range(unsigned long start, unsigned long end, VM_BUG_ON(!PAGE_ALIGNED(start | end)); mmap_read_lock(&init_mm); - ret = walk_page_range_novma(&init_mm, start, end, &vmemmap_remap_ops, + ret = walk_kernel_page_table_range(start, end, &vmemmap_remap_ops, NULL, walk); mmap_read_unlock(&init_mm); if (ret) diff --git a/mm/internal.h b/mm/internal.h index 151edc2f9ab2..9d277bc2c26e 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1540,4 +1541,9 @@ static inline void accept_page(struct page *page) } #endif /* CONFIG_UNACCEPTED_MEMORY */ +/* pagewalk.c */ +int walk_page_range_debug(struct mm_struct *mm, unsigned long start, + unsigned long end, const struct mm_walk_ops *ops, + pgd_t *pgd, void *private); + #endif /* __MM_INTERNAL_H */ diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 95b49ca6b196..a6b2c7d7d73b 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -346,6 +346,13 @@ struct attribute_group khugepaged_attr_group = { }; #endif /* CONFIG_SYSFS */ +static bool pte_none_or_zero(pte_t pte) +{ + if (pte_none(pte)) + return true; + return pte_present(pte) && is_zero_pfn(pte_pfn(pte)); +} + int hugepage_madvise(struct vm_area_struct *vma, unsigned long *vm_flags, int advice) { @@ -529,6 +536,7 @@ static void release_pte_pages(pte_t *pte, pte_t *_pte, if (pte_none(pteval)) continue; + VM_WARN_ON_ONCE(!pte_present(pteval)); pfn = pte_pfn(pteval); if (is_zero_pfn(pfn)) continue; @@ -572,8 +580,7 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma, for (_pte = pte; _pte < pte + HPAGE_PMD_NR; _pte++, address += PAGE_SIZE) { pte_t pteval = ptep_get(_pte); - if (pte_none(pteval) || (pte_present(pteval) && - is_zero_pfn(pte_pfn(pteval)))) { + if (pte_none_or_zero(pteval)) { ++none_or_zero; if (!userfaultfd_armed(vma) && (!cc->is_khugepaged || @@ -716,17 +723,17 @@ static void __collapse_huge_page_copy_succeeded(pte_t *pte, for (_pte = pte; _pte < pte + HPAGE_PMD_NR; _pte++, address += PAGE_SIZE) { pteval = ptep_get(_pte); - if (pte_none(pteval) || is_zero_pfn(pte_pfn(pteval))) { + if (pte_none_or_zero(pteval)) { add_mm_counter(vma->vm_mm, MM_ANONPAGES, 1); - if (is_zero_pfn(pte_pfn(pteval))) { - /* - * ptl mostly unnecessary. - */ - spin_lock(ptl); - ptep_clear(vma->vm_mm, address, _pte); - spin_unlock(ptl); - ksm_might_unmap_zero_page(vma->vm_mm, pteval); - } + if (pte_none(pteval)) + continue; + /* + * ptl mostly unnecessary. + */ + spin_lock(ptl); + ptep_clear(vma->vm_mm, address, _pte); + spin_unlock(ptl); + ksm_might_unmap_zero_page(vma->vm_mm, pteval); } else { struct page *src_page = pte_page(pteval); @@ -812,7 +819,7 @@ static int __collapse_huge_page_copy(pte_t *pte, struct folio *folio, unsigned long src_addr = address + i * PAGE_SIZE; struct page *src_page; - if (pte_none(pteval) || is_zero_pfn(pte_pfn(pteval))) { + if (pte_none_or_zero(pteval)) { clear_user_highpage(page, src_addr); continue; } @@ -1305,7 +1312,7 @@ static int hpage_collapse_scan_pmd(struct mm_struct *mm, goto out_unmap; } } - if (pte_none(pteval) || is_zero_pfn(pte_pfn(pteval))) { + if (pte_none_or_zero(pteval)) { ++none_or_zero; if (!userfaultfd_armed(vma) && (!cc->is_khugepaged || diff --git a/mm/pagewalk.c b/mm/pagewalk.c index 313a8fa9b5dc..3d795ad47a89 100644 --- a/mm/pagewalk.c +++ b/mm/pagewalk.c @@ -6,6 +6,8 @@ #include #include +#include "internal.h" + /* * We want to know the real level where a entry is located ignoring any * folding of levels which may be happening. For example if p4d is folded then @@ -500,8 +502,7 @@ int walk_page_range(struct mm_struct *mm, unsigned long start, } /** - * walk_page_range_novma - walk a range of pagetables not backed by a vma - * @mm: mm_struct representing the target process of page table walk + * walk_kernel_page_table_range - walk a range of kernel pagetables. * @start: start address of the virtual address range * @end: end address of the virtual address range * @ops: operation to call during the walk @@ -511,18 +512,17 @@ int walk_page_range(struct mm_struct *mm, unsigned long start, * Similar to walk_page_range() but can walk any page tables even if they are * not backed by VMAs. Because 'unusual' entries may be walked this function * will also not lock the PTEs for the pte_entry() callback. This is useful for - * walking the kernel pages tables or page tables for firmware. + * walking kernel pages tables or page tables for firmware. * * Note: Be careful to walk the kernel pages tables, the caller may be need to * take other effective approache (mmap lock may be insufficient) to prevent * the intermediate kernel page tables belonging to the specified address range * from being freed (e.g. memory hot-remove). */ -int walk_page_range_novma(struct mm_struct *mm, unsigned long start, - unsigned long end, const struct mm_walk_ops *ops, - pgd_t *pgd, - void *private) +int walk_kernel_page_table_range(unsigned long start, unsigned long end, + const struct mm_walk_ops *ops, pgd_t *pgd, void *private) { + struct mm_struct *mm = &init_mm; struct mm_walk walk = { .ops = ops, .mm = mm, @@ -531,32 +531,71 @@ int walk_page_range_novma(struct mm_struct *mm, unsigned long start, .no_vma = true }; - if (start >= end || !walk.mm) + if (start >= end) return -EINVAL; /* - * 1) For walking the user virtual address space: - * - * The mmap lock protects the page walker from changes to the page - * tables during the walk. However a read lock is insufficient to - * protect those areas which don't have a VMA as munmap() detaches - * the VMAs before downgrading to a read lock and actually tearing - * down PTEs/page tables. In which case, the mmap write lock should - * be hold. - * - * 2) For walking the kernel virtual address space: - * - * The kernel intermediate page tables usually do not be freed, so - * the mmap map read lock is sufficient. But there are some exceptions. + * Kernel intermediate page tables are usually not freed, so the mmap + * read lock is sufficient. But there are some exceptions. * E.g. memory hot-remove. In which case, the mmap lock is insufficient * to prevent the intermediate kernel pages tables belonging to the * specified address range from being freed. The caller should take * other actions to prevent this race. */ - if (mm == &init_mm) - mmap_assert_locked(walk.mm); - else - mmap_assert_write_locked(walk.mm); + mmap_assert_locked(mm); + + return walk_pgd_range(start, end, &walk); +} + +/** + * walk_page_range_debug - walk a range of pagetables not backed by a vma + * @mm: mm_struct representing the target process of page table walk + * @start: start address of the virtual address range + * @end: end address of the virtual address range + * @ops: operation to call during the walk + * @pgd: pgd to walk if different from mm->pgd + * @private: private data for callbacks' usage + * + * Similar to walk_page_range() but can walk any page tables even if they are + * not backed by VMAs. Because 'unusual' entries may be walked this function + * will also not lock the PTEs for the pte_entry() callback. + * + * This is for debugging purposes ONLY. + * + * The mmap write lock must be held. + */ +int walk_page_range_debug(struct mm_struct *mm, unsigned long start, + unsigned long end, const struct mm_walk_ops *ops, + pgd_t *pgd, void *private) +{ + struct mm_walk walk = { + .ops = ops, + .mm = mm, + .pgd = pgd, + .private = private, + .no_vma = true + }; + + /* + * When walking userland page tables, an mmap write lock must be held to + * account for munmap() downgrading to an mmap read lock when tearing + * down page tables. + * + * When walking kernel page tables, an mmap write lock must also be held + * to account for page table freeing on vmap huge page mapping. + */ + mmap_assert_write_locked(mm); + /* + * x86, arm64 ptdump allow walks of efi mm's and x86 ptdump allows walks + * of arbitrary mm's. + * + * However, they both must also hold the init_mm lock to account for + * concurrent kernel page table freeing. + */ + mmap_assert_write_locked(&init_mm); + + if (start >= end) + return -EINVAL; return walk_pgd_range(start, end, &walk); } diff --git a/mm/ptdump.c b/mm/ptdump.c index 3e78bf33da42..8c06fbf32685 100644 --- a/mm/ptdump.c +++ b/mm/ptdump.c @@ -4,6 +4,7 @@ #include #include #include +#include "internal.h" #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) /* @@ -155,11 +156,18 @@ void ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm, pgd_t *pgd) get_online_mems(); mmap_write_lock(mm); + /* To stabilise kernel page tables we must hold the init_mm lock too. */ + if (mm != &init_mm) + mmap_write_lock_nested(&init_mm, SINGLE_DEPTH_NESTING); + while (range->start != range->end) { - walk_page_range_novma(mm, range->start, range->end, + walk_page_range_debug(mm, range->start, range->end, &ptdump_ops, pgd, st); range++; } + + if (mm != &init_mm) + mmap_write_unlock(&init_mm); mmap_write_unlock(mm); put_online_mems(); diff --git a/mm/vmalloc.c b/mm/vmalloc.c index d4a42980b4d0..e0066c100b79 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -135,6 +135,8 @@ static int vmap_try_huge_pmd(pmd_t *pmd, unsigned long addr, unsigned long end, phys_addr_t phys_addr, pgprot_t prot, unsigned int max_page_shift) { + int ret; + if (max_page_shift < PMD_SHIFT) return 0; @@ -150,10 +152,28 @@ static int vmap_try_huge_pmd(pmd_t *pmd, unsigned long addr, unsigned long end, if (!IS_ALIGNED(phys_addr, PMD_SIZE)) return 0; - if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr)) + if (!pmd_present(*pmd)) + return pmd_set_huge(pmd, phys_addr, prot); + + /* + * Acquire the mmap read lock to exclude ptdump, which walks + * kernel page tables it does not own under the mmap write lock. + * + * Concurrent read lock holders are safe: each exclusively owns + * the range it operates on and cannot reach this page table. + */ + if (!mmap_read_trylock(&init_mm)) + return 0; + + if (!pmd_free_pte_page(pmd, addr)) { + mmap_read_unlock(&init_mm); return 0; + } - return pmd_set_huge(pmd, phys_addr, prot); + ret = pmd_set_huge(pmd, phys_addr, prot); + mmap_read_unlock(&init_mm); + + return ret; } static int vmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end, @@ -185,6 +205,8 @@ static int vmap_try_huge_pud(pud_t *pud, unsigned long addr, unsigned long end, phys_addr_t phys_addr, pgprot_t prot, unsigned int max_page_shift) { + int ret; + if (max_page_shift < PUD_SHIFT) return 0; @@ -200,10 +222,22 @@ static int vmap_try_huge_pud(pud_t *pud, unsigned long addr, unsigned long end, if (!IS_ALIGNED(phys_addr, PUD_SIZE)) return 0; - if (pud_present(*pud) && !pud_free_pmd_page(pud, addr)) + if (!pud_present(*pud)) + return pud_set_huge(pud, phys_addr, prot); + + /* See comment in vmap_try_huge_pmd(). */ + if (!mmap_read_trylock(&init_mm)) + return 0; + + if (!pud_free_pmd_page(pud, addr)) { + mmap_read_unlock(&init_mm); return 0; + } + + ret = pud_set_huge(pud, phys_addr, prot); + mmap_read_unlock(&init_mm); - return pud_set_huge(pud, phys_addr, prot); + return ret; } static int vmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end, @@ -236,6 +270,8 @@ static int vmap_try_huge_p4d(p4d_t *p4d, unsigned long addr, unsigned long end, phys_addr_t phys_addr, pgprot_t prot, unsigned int max_page_shift) { + int ret; + if (max_page_shift < P4D_SHIFT) return 0; @@ -251,10 +287,22 @@ static int vmap_try_huge_p4d(p4d_t *p4d, unsigned long addr, unsigned long end, if (!IS_ALIGNED(phys_addr, P4D_SIZE)) return 0; - if (p4d_present(*p4d) && !p4d_free_pud_page(p4d, addr)) + if (!p4d_present(*p4d)) + return p4d_set_huge(p4d, phys_addr, prot); + + /* See comment in vmap_try_huge_pmd(). */ + if (!mmap_read_trylock(&init_mm)) + return 0; + + if (!p4d_free_pud_page(p4d, addr)) { + mmap_read_unlock(&init_mm); return 0; + } + + ret = p4d_set_huge(p4d, phys_addr, prot); + mmap_read_unlock(&init_mm); - return p4d_set_huge(p4d, phys_addr, prot); + return ret; } static int vmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end, diff --git a/net/ceph/cls_lock_client.c b/net/ceph/cls_lock_client.c index 66136a4c1ce7..a9655678be6f 100644 --- a/net/ceph/cls_lock_client.c +++ b/net/ceph/cls_lock_client.c @@ -259,7 +259,8 @@ static int decode_locker(void **p, void *end, struct ceph_locker *locker) if (ret) return ret; - ceph_decode_copy(p, &locker->id.name, sizeof(locker->id.name)); + ceph_decode_copy_safe(p, end, &locker->id.name, + sizeof(locker->id.name), bad); s = ceph_extract_encoded_string(p, end, NULL, GFP_NOIO); if (IS_ERR(s)) return PTR_ERR(s); @@ -270,19 +271,23 @@ static int decode_locker(void **p, void *end, struct ceph_locker *locker) if (ret) return ret; - *p += sizeof(struct ceph_timespec); /* skip expiration */ + /* skip expiration */ + ceph_decode_skip_n(p, end, sizeof(struct ceph_timespec), bad); ret = ceph_decode_entity_addr(p, end, &locker->info.addr); if (ret) return ret; - len = ceph_decode_32(p); - *p += len; /* skip description */ + /* skip description */ + ceph_decode_skip_string(p, end, bad); dout("%s %s%llu cookie %s addr %s\n", __func__, ENTITY_NAME(locker->id.name), locker->id.cookie, ceph_pr_addr(&locker->info.addr)); return 0; + +bad: + return -EINVAL; } static int decode_lockers(void **p, void *end, u8 *type, char **tag, @@ -299,7 +304,7 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag, if (ret) return ret; - *num_lockers = ceph_decode_32(p); + ceph_decode_32_safe(p, end, *num_lockers, err_inval); *lockers = kcalloc(*num_lockers, sizeof(**lockers), GFP_NOIO); if (!*lockers) return -ENOMEM; @@ -310,7 +315,8 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag, goto err_free_lockers; } - *type = ceph_decode_8(p); + ret = -EINVAL; + ceph_decode_8_safe(p, end, *type, err_free_lockers); s = ceph_extract_encoded_string(p, end, NULL, GFP_NOIO); if (IS_ERR(s)) { ret = PTR_ERR(s); @@ -320,6 +326,9 @@ static int decode_lockers(void **p, void *end, u8 *type, char **tag, *tag = s; return 0; +err_inval: + return -EINVAL; + err_free_lockers: ceph_free_lockers(*lockers, *num_lockers); return ret; diff --git a/net/ceph/decode.c b/net/ceph/decode.c index bc109a1a4616..2f21af38cd9a 100644 --- a/net/ceph/decode.c +++ b/net/ceph/decode.c @@ -87,8 +87,9 @@ ceph_decode_entity_addr(void **p, void *end, struct ceph_entity_addr *addr) EXPORT_SYMBOL(ceph_decode_entity_addr); /* - * Return addr of desired type (MSGR2 or LEGACY) or error. - * Make sure there is only one match. + * Return addr of desired type (MSGR2 or LEGACY) or error. In case of + * multiple matches, use the first one for compatibility with userspace + * messenger. * * Assume encoding with MSG_ADDR2. */ @@ -121,14 +122,13 @@ int ceph_decode_entity_addrvec(void **p, void *end, bool msgr2, dout("%s i %d addr %s\n", __func__, i, ceph_pr_addr(&tmp_addr)); if (tmp_addr.type == my_type) { - if (found) { - pr_err("another match of type %d in addrvec\n", - le32_to_cpu(my_type)); - return -EINVAL; + if (!found) { + memcpy(addr, &tmp_addr, sizeof(*addr)); + found = true; + } else { + dout("%s skipping extra match of type %d in addrvec\n", + __func__, le32_to_cpu(my_type)); } - - memcpy(addr, &tmp_addr, sizeof(*addr)); - found = true; } } diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 30d75970be44..ff06ba2965cf 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -1440,7 +1440,7 @@ static struct ceph_pg_mapping *__decode_pg_temp(void **p, void *end, ceph_decode_32_safe(p, end, len, e_inval); if (len == 0 && incremental) return NULL; /* new_pg_temp: [] to remove */ - if (len > (SIZE_MAX - sizeof(*pg)) / sizeof(u32)) + if (len > CEPH_PG_MAX_SIZE) return ERR_PTR(-EINVAL); ceph_decode_need(p, end, len * sizeof(u32), e_inval); @@ -1621,7 +1621,7 @@ static struct ceph_pg_mapping *__decode_pg_upmap_items(void **p, void *end, u32 len, i; ceph_decode_32_safe(p, end, len, e_inval); - if (len > (SIZE_MAX - sizeof(*pg)) / (2 * sizeof(u32))) + if (len > CEPH_PG_MAX_SIZE) return ERR_PTR(-EINVAL); ceph_decode_need(p, end, 2 * len * sizeof(u32), e_inval); @@ -2811,9 +2811,10 @@ static void get_temp_osds(struct ceph_osdmap *osdmap, } } - /* primary_temp? */ + /* primary_temp? (shouldn't ever be a nonexistent or down OSD) */ pg = lookup_pg_mapping(&osdmap->primary_temp, pgid); - if (pg) + if (pg && !WARN_ON_ONCE(ceph_osd_is_down(osdmap, + pg->primary_temp.osd))) temp->primary = pg->primary_temp.osd; } diff --git a/net/core/dev.c b/net/core/dev.c index 5c037fe70d48..e08150baf0aa 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -11070,6 +11070,20 @@ struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, const struct net_device_ops *ops = dev->netdev_ops; const struct net_device_core_stats __percpu *p; + /* + * IPv{4,6} and udp tunnels share common stat helpers and use + * different stat type (NETDEV_PCPU_STAT_TSTATS vs + * NETDEV_PCPU_STAT_DSTATS). Ensure the accounting is consistent. + */ + BUILD_BUG_ON(offsetof(struct pcpu_sw_netstats, rx_bytes) != + offsetof(struct pcpu_dstats, rx_bytes)); + BUILD_BUG_ON(offsetof(struct pcpu_sw_netstats, rx_packets) != + offsetof(struct pcpu_dstats, rx_packets)); + BUILD_BUG_ON(offsetof(struct pcpu_sw_netstats, tx_bytes) != + offsetof(struct pcpu_dstats, tx_bytes)); + BUILD_BUG_ON(offsetof(struct pcpu_sw_netstats, tx_packets) != + offsetof(struct pcpu_dstats, tx_packets)); + if (ops->ndo_get_stats64) { memset(storage, 0, sizeof(*storage)); ops->ndo_get_stats64(dev, storage); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 762ede027899..118036dc16fa 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -285,7 +285,8 @@ struct pktgen_dev { int pkt_overhead; /* overhead for MPLS, VLANs, IPSEC etc */ int nfrags; int removal_mark; /* non-zero => the device is marked for - * removal by worker thread */ + * removal by worker thread + */ struct page *page; u64 delay; /* nano-seconds */ @@ -348,10 +349,12 @@ struct pktgen_dev { __u16 udp_dst_max; /* exclusive, dest UDP port */ /* DSCP + ECN */ - __u8 tos; /* six MSB of (former) IPv4 TOS - are for dscp codepoint */ - __u8 traffic_class; /* ditto for the (former) Traffic Class in IPv6 - (see RFC 3260, sec. 4) */ + __u8 tos; /* six MSB of (former) IPv4 TOS + * are for dscp codepoint + */ + __u8 traffic_class; /* ditto for the (former) Traffic Class in IPv6 + * (see RFC 3260, sec. 4) + */ /* IMIX */ unsigned int n_imix_entries; @@ -391,12 +394,12 @@ struct pktgen_dev { __u8 hh[14]; /* = { - 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB, - - We fill in SRC address later - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00 - }; + * 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB, + * + * We fill in SRC address later + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + * 0x08, 0x00 + * }; */ __u16 pad; /* pad out the hh struct to an even 16 bytes */ @@ -460,7 +463,8 @@ struct pktgen_thread { char result[512]; /* Field for thread to receive "posted" events terminate, - stop ifs etc. */ + * stop ifs etc. + */ u32 control; int cpu; @@ -2364,7 +2368,7 @@ static inline int f_pick(struct pktgen_dev *pkt_dev) #ifdef CONFIG_XFRM /* If there was already an IPSEC SA, we keep it as is, else * we go look for it ... -*/ + */ #define DUMMY_MARK 0 static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow) { @@ -2663,7 +2667,8 @@ static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev) if (!x) return 0; /* XXX: we dont support tunnel mode for now until - * we resolve the dst issue */ + * we resolve the dst issue + */ if ((x->props.mode != XFRM_MODE_TRANSPORT) && (pkt_dev->spi == 0)) return 0; @@ -3757,7 +3762,8 @@ static int add_dev_to_thread(struct pktgen_thread *t, * userspace on another CPU than the kthread. The if_lock() * is used here to sync with concurrent instances of * _rem_dev_from_if_list() invoked via kthread, which is also - * updating the if_list */ + * updating the if_list + */ if_lock(t); if (pkt_dev->pg_thread) { @@ -3928,6 +3934,7 @@ static void _rem_dev_from_if_list(struct pktgen_thread *t, struct pktgen_dev *p; if_lock(t); + proc_remove(pkt_dev->entry); list_for_each_safe(q, n, &t->if_list) { p = list_entry(q, struct pktgen_dev, list); if (p == pkt_dev) @@ -3955,10 +3962,8 @@ static int pktgen_remove_device(struct pktgen_thread *t, /* Remove proc before if_list entry, because add_device uses * list to determine if interface already exist, avoid race - * with proc_create_data() */ - proc_remove(pkt_dev->entry); - - /* And update the thread if_list */ + * with proc_create_data() + */ _rem_dev_from_if_list(t, pkt_dev); #ifdef CONFIG_XFRM diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c index 554804774628..e01492234b0b 100644 --- a/net/ipv4/bpf_tcp_ca.c +++ b/net/ipv4/bpf_tcp_ca.c @@ -121,7 +121,7 @@ static int bpf_tcp_ca_btf_struct_access(struct bpf_verifier_log *log, BPF_CALL_2(bpf_tcp_send_ack, struct tcp_sock *, tp, u32, rcv_nxt) { /* bpf_tcp_ca prog cannot have NULL tp */ - __tcp_send_ack((struct sock *)tp, rcv_nxt); + __tcp_send_ack((struct sock *)tp, rcv_nxt, 0); return 0; } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 747ca263e144..e44e2c97274b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -5061,7 +5061,7 @@ static void __init tcp_struct_check(void) /* 32bit arches with 8byte alignment on u64 fields might need padding * before tcp_clock_cache. */ - CACHELINE_ASSERT_GROUP_SIZE(struct tcp_sock, tcp_sock_write_txrx, 92 + 4); + CACHELINE_ASSERT_GROUP_SIZE(struct tcp_sock, tcp_sock_write_txrx, 91 + 4); /* RX read-write hotpath cache lines */ CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, bytes_received); @@ -5078,7 +5078,7 @@ static void __init tcp_struct_check(void) CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, bytes_acked); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, rcv_rtt_est); CACHELINE_ASSERT_GROUP_MEMBER(struct tcp_sock, tcp_sock_write_rx, rcvq_space); - CACHELINE_ASSERT_GROUP_SIZE(struct tcp_sock, tcp_sock_write_rx, 99); + CACHELINE_ASSERT_GROUP_SIZE(struct tcp_sock, tcp_sock_write_rx, 96); } void __init tcp_init(void) diff --git a/net/ipv4/tcp_dctcp.h b/net/ipv4/tcp_dctcp.h index d69a77cbd0c7..4b0259111d81 100644 --- a/net/ipv4/tcp_dctcp.h +++ b/net/ipv4/tcp_dctcp.h @@ -28,7 +28,7 @@ static inline void dctcp_ece_ack_update(struct sock *sk, enum tcp_ca_event evt, */ if (inet_csk(sk)->icsk_ack.pending & ICSK_ACK_TIMER) { dctcp_ece_ack_cwr(sk, *ce_state); - __tcp_send_ack(sk, *prior_rcv_nxt); + __tcp_send_ack(sk, *prior_rcv_nxt, 0); } inet_csk(sk)->icsk_ack.pending |= ICSK_ACK_NOW; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2fb68455679a..55b34cf309be 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -853,9 +853,9 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb) /* The fastest case is the first. */ icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + TCP_ATO_MIN / 2; } else if (m < icsk->icsk_ack.ato) { - icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + m; - if (icsk->icsk_ack.ato > icsk->icsk_rto) - icsk->icsk_ack.ato = icsk->icsk_rto; + icsk->icsk_ack.ato = min3((icsk->icsk_ack.ato >> 1) + (u32)m, + icsk->icsk_rto, + (u32)TCP_DELACK_MAX); } else if (m > icsk->icsk_rto) { /* Too long gap. Apparently sender failed to * restart window, so that we send ACKs quickly. @@ -3786,24 +3786,17 @@ bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb, return __tcp_oow_rate_limited(net, mib_idx, last_oow_ack_time); } -/* RFC 5961 7 [ACK Throttling] */ -static void tcp_send_challenge_ack(struct sock *sk) +/* Consume one slot from the per-netns RFC 5961 challenge ACK quota. + * Returns true if a challenge ACK may be sent. + */ +static bool tcp_challenge_ack_allowed(struct net *net) { - struct tcp_sock *tp = tcp_sk(sk); - struct net *net = sock_net(sk); u32 count, now, ack_limit; - /* First check our per-socket dupack rate limit. */ - if (__tcp_oow_rate_limited(net, - LINUX_MIB_TCPACKSKIPPEDCHALLENGE, - &tp->last_oow_ack_time)) - return; - ack_limit = READ_ONCE(net->ipv4.sysctl_tcp_challenge_ack_limit); if (ack_limit == INT_MAX) - goto send_ack; + return true; - /* Then check host-wide RFC 5961 rate limit. */ now = jiffies / HZ; if (now != READ_ONCE(net->ipv4.tcp_challenge_timestamp)) { u32 half = (ack_limit + 1) >> 1; @@ -3815,12 +3808,49 @@ static void tcp_send_challenge_ack(struct sock *sk) count = READ_ONCE(net->ipv4.tcp_challenge_count); if (count > 0) { WRITE_ONCE(net->ipv4.tcp_challenge_count, count - 1); -send_ack: + return true; + } + return false; +} + +/* RFC 5961 7 [ACK Throttling] */ +static void tcp_send_challenge_ack(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct net *net = sock_net(sk); + + /* First check our per-socket dupack rate limit. */ + if (__tcp_oow_rate_limited(net, + LINUX_MIB_TCPACKSKIPPEDCHALLENGE, + &tp->last_oow_ack_time)) + return; + + /* Then check the per-netns RFC 5961 rate limit. */ + if (tcp_challenge_ack_allowed(net)) { NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK); tcp_send_ack(sk); } } +/* Send a challenge ACK from a SYN-RECEIVED request socket. Uses + * __tcp_oow_rate_limited() directly so that an RST carrying payload + * cannot bypass the per-request rate limit. + */ +void tcp_reqsk_send_challenge_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req) +{ + struct net *net = sock_net(sk); + + if (__tcp_oow_rate_limited(net, LINUX_MIB_TCPACKSKIPPEDCHALLENGE, + &tcp_rsk(req)->last_oow_ack_time)) + return; + + if (tcp_challenge_ack_allowed(net)) { + NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK); + req->rsk_ops->send_ack(sk, skb, req); + } +} + static void tcp_store_ts_recent(struct tcp_sock *tp) { tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 1badb78baa7f..7149ddb09840 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -775,7 +775,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, * elsewhere and is checked directly against the child socket rather * than req because user data may have been sent out. */ - if ((flg & TCP_FLAG_ACK) && !fastopen && + if ((flg & TCP_FLAG_ACK) && !(flg & TCP_FLAG_RST) && !fastopen && (TCP_SKB_CB(skb)->ack_seq != tcp_rsk(req)->snt_isn + 1)) return sk; @@ -811,6 +811,16 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, flg &= ~TCP_FLAG_SYN; } + /* RFC 5961 section 3.2, as clarified by RFC 9293 section + * 3.10.7.4, requires a challenge ACK for a non-exact + * in-window RST in SYN-RECEIVED. + */ + if ((flg & TCP_FLAG_RST) && + TCP_SKB_CB(skb)->seq != tcp_rsk(req)->rcv_nxt) { + tcp_reqsk_send_challenge_ack(sk, skb, req); + return NULL; + } + /* RFC793: "second check the RST bit" and * "fourth, check the SYN bit" */ diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index d2d8e989aac7..296102ed4a96 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -4250,7 +4250,7 @@ void tcp_send_delayed_ack(struct sock *sk) } /* This routine sends an ack and also updates the window. */ -void __tcp_send_ack(struct sock *sk, u32 rcv_nxt) +void __tcp_send_ack(struct sock *sk, u32 rcv_nxt, u16 flags) { struct sk_buff *buff; @@ -4279,7 +4279,7 @@ void __tcp_send_ack(struct sock *sk, u32 rcv_nxt) /* Reserve space for headers and prepare control bits. */ skb_reserve(buff, MAX_TCP_HEADER); - tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK); + tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK | flags); /* We do not want pure acks influencing TCP Small Queues or fq/pacing * too much. @@ -4294,7 +4294,7 @@ EXPORT_SYMBOL_GPL(__tcp_send_ack); void tcp_send_ack(struct sock *sk) { - __tcp_send_ack(sk, tcp_sk(sk)->rcv_nxt); + __tcp_send_ack(sk, tcp_sk(sk)->rcv_nxt, 0); } /* This routine sends a packet with an out of date sequence diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 16a410386ffa..a58094b0acd4 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -332,7 +332,9 @@ void tcp_delack_timer_handler(struct sock *sk) if (inet_csk_ack_scheduled(sk)) { if (!inet_csk_in_pingpong_mode(sk)) { /* Delayed ACK missed: inflate ATO. */ - icsk->icsk_ack.ato = min_t(u32, icsk->icsk_ack.ato << 1, icsk->icsk_rto); + icsk->icsk_ack.ato = min3((u32)icsk->icsk_ack.ato << 1, + icsk->icsk_rto, + (u32)TCP_DELACK_MAX); } else { /* Delayed ACK missed: leave pingpong mode and * deflate ATO. diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c index d4dbdd3d5679..35b93adf1b73 100644 --- a/net/mptcp/fastopen.c +++ b/net/mptcp/fastopen.c @@ -24,12 +24,13 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf sk = subflow->conn; tp = tcp_sk(ssk); - subflow->is_mptfo = 1; - + /* A valid TFO cookie does not guarantee SYN data. */ skb = skb_peek(&ssk->sk_receive_queue); - if (WARN_ON_ONCE(!skb)) + if (!skb) return; + subflow->is_mptfo = 1; + /* dequeue the skb from sk receive queue */ __skb_unlink(skb, &ssk->sk_receive_queue); skb_ext_reset(skb); diff --git a/net/mptcp/options.c b/net/mptcp/options.c index cfb3ded02c7a..41aa6c944258 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -50,6 +50,14 @@ static void mptcp_parse_option(const struct sk_buff *skb, } } + /* Only the MPC + ACK can be used with a RM_ADDR */ + if (subopt == OPTION_MPTCP_MPC_ACK) { + if ((mp_opt->suboptions & ~OPTION_MPTCP_RM_ADDR) != 0) + break; + } else if (mp_opt->suboptions != 0) { + break; + } + /* Cfr RFC 8684 Section 3.3.0: * If a checksum is present but its use had * not been negotiated in the MP_CAPABLE handshake, the receiver MUST @@ -122,6 +130,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_MP_JOIN: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTION_MPTCP_RM_ADDR | + OPTION_MPTCP_PRIO)) != 0) + break; + if (opsize == TCPOLEN_MPTCP_MPJ_SYN) { mp_opt->suboptions |= OPTION_MPTCP_MPJ_SYN; mp_opt->backup = *ptr++ & MPTCPOPT_BACKUP; @@ -153,6 +166,14 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_DSS: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTION_MPTCP_ADD_ADDR | + OPTION_MPTCP_RM_ADDR | + OPTION_MPTCP_PRIO | + OPTION_MPTCP_FASTCLOSE | + OPTION_MPTCP_FAIL)) != 0) + break; + pr_debug("DSS\n"); ptr++; @@ -188,8 +209,14 @@ static void mptcp_parse_option(const struct sk_buff *skb, * RFC 8684 Section 3.3.0 checks later in subflow_data_ready */ if (opsize != expected_opsize && - opsize != expected_opsize + TCPOLEN_MPTCP_DSS_CHECKSUM) + opsize != expected_opsize + TCPOLEN_MPTCP_DSS_CHECKSUM) { + mp_opt->dsn64 = 0; + mp_opt->use_map = 0; + mp_opt->ack64 = 0; + mp_opt->use_ack = 0; + mp_opt->data_fin = 0; break; + } mp_opt->suboptions |= OPTION_MPTCP_DSS; if (mp_opt->use_ack) { @@ -234,6 +261,12 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_ADD_ADDR: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS | + OPTION_MPTCP_RM_ADDR | + OPTION_MPTCP_PRIO)) != 0) + break; + mp_opt->echo = (*ptr++) & MPTCP_ADDR_ECHO; if (!mp_opt->echo) { if (opsize == TCPOLEN_MPTCP_ADD_ADDR || @@ -293,6 +326,14 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_RM_ADDR: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTION_MPTCP_MPC_ACK | + OPTIONS_MPTCP_MPJ | + OPTIONS_MPTCP_DSS | + OPTION_MPTCP_ADD_ADDR | + OPTION_MPTCP_PRIO)) != 0) + break; + if (opsize < TCPOLEN_MPTCP_RM_ADDR_BASE + 1 || opsize > TCPOLEN_MPTCP_RM_ADDR_BASE + MPTCP_RM_IDS_MAX) break; @@ -307,6 +348,13 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_MP_PRIO: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_MPJ | + OPTIONS_MPTCP_DSS | + OPTION_MPTCP_ADD_ADDR | + OPTION_MPTCP_RM_ADDR)) != 0) + break; + if (opsize != TCPOLEN_MPTCP_PRIO) break; @@ -316,6 +364,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_MP_FASTCLOSE: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS | + OPTION_MPTCP_RST)) != 0) + break; + if (opsize != TCPOLEN_MPTCP_FASTCLOSE) break; @@ -327,6 +380,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_RST: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTION_MPTCP_FAIL | + OPTION_MPTCP_FASTCLOSE)) != 0) + break; + if (opsize != TCPOLEN_MPTCP_RST) break; @@ -342,6 +400,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_MP_FAIL: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS | + OPTION_MPTCP_RST)) != 0) + break; + if (opsize != TCPOLEN_MPTCP_FAIL) break; @@ -1416,7 +1479,7 @@ void mptcp_write_options(struct tcphdr *th, __be32 *ptr, struct tcp_sock *tp, * RM | C | C | C | P |------|------|------|------| * PRIO | X | C | C | C | C |------|------|------| * FAIL | X | X | C | X | X | X |------|------| - * FC | X | X | X | X | X | X | X |------| + * FC | X | X | P | X | X | X | X |------| * RST | X | X | X | X | X | X | O | O | * ------|------|------|------|------|------|------|------|------| * diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 04f33abd410d..e4540431b49c 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -37,6 +37,7 @@ OPTION_MPTCP_MPC_ACK) #define OPTIONS_MPTCP_MPJ (OPTION_MPTCP_MPJ_SYN | OPTION_MPTCP_MPJ_SYNACK | \ OPTION_MPTCP_MPJ_ACK) +#define OPTIONS_MPTCP_DSS (OPTION_MPTCP_DSS | OPTION_MPTCP_CSUMREQD) /* MPTCP option subtypes */ #define MPTCPOPT_MP_CAPABLE 0 diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index cf8468bf987d..e6a092cea011 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -77,7 +77,7 @@ mtype_flush(struct ip_set *set) mtype_ext_cleanup(set); bitmap_zero(map->members, map->elements); set->elements = 0; - atomic64_set(&set->ext_size, 0); + DEBUG_NET_WARN_ON_ONCE(atomic64_read(&set->ext_size) > 0); } /* Calculate the actual memory size of the set data */ diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 29bf5ee74fe3..e7c85b0af729 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -679,11 +679,18 @@ __ip_set_get(struct ip_set *set) } static void -__ip_set_put(struct ip_set *set) +__ip_set_put_locked(struct ip_set *set) { - write_lock_bh(&ip_set_ref_lock); + lockdep_assert_held(&ip_set_ref_lock); BUG_ON(set->ref == 0); set->ref--; +} + +static void +__ip_set_put(struct ip_set *set) +{ + write_lock_bh(&ip_set_ref_lock); + __ip_set_put_locked(set); write_unlock_bh(&ip_set_ref_lock); } @@ -854,11 +861,11 @@ __ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index) { struct ip_set *set; - rcu_read_lock(); - set = rcu_dereference(inst->ip_set_list)[index]; + write_lock_bh(&ip_set_ref_lock); + set = ip_set(inst, index); if (set) - __ip_set_put(set); - rcu_read_unlock(); + __ip_set_put_locked(set); + write_unlock_bh(&ip_set_ref_lock); } void diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 9d6ab69ca1a3..720f05340b47 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -301,9 +301,12 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, e->set = set; INIT_LIST_HEAD(&e->list); list_set_init_extensions(set, ext, e); - if (n) + if (n) { list_set_replace(set, e, n); - else if (next) + return 0; + } + + if (next) list_add_tail_rcu(&e->list, &next->list); else if (prev) list_add_rcu(&e->list, &prev->list); @@ -420,8 +423,7 @@ list_set_flush(struct ip_set *set) list_for_each_entry_safe(e, n, &map->members, list) list_set_del(set, e); - set->elements = 0; - atomic64_set(&set->ext_size, 0); + DEBUG_NET_WARN_ON_ONCE(set->elements > 0); } static void diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index f50fbcb13f89..d7a7e26e9c69 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -759,7 +759,7 @@ int ip_vs_check_template(struct ip_vs_conn *ct, struct ip_vs_dest *cdest) * Checking the dest server status. */ if ((dest == NULL) || - !(dest->flags & IP_VS_DEST_F_AVAILABLE) || + !(dest->cflags & IP_VS_DEST_CF_AVAILABLE) || expire_quiescent_template(ipvs, dest) || (cdest && (dest != cdest))) { IP_VS_DBG_BUF(9, "check_template: dest not available for " @@ -1402,7 +1402,7 @@ void ip_vs_expire_nodest_conn_flush(struct netns_ipvs *ipvs) continue; dest = cp->dest; - if (!dest || (dest->flags & IP_VS_DEST_F_AVAILABLE)) + if (!dest || (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) continue; if (atomic_read(&cp->n_control)) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index d121ea3d16bc..1f62ca2f7330 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -124,7 +124,7 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) struct ip_vs_dest *dest = cp->dest; struct netns_ipvs *ipvs = cp->ipvs; - if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { + if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) { struct ip_vs_cpu_stats *s; struct ip_vs_service *svc; @@ -160,7 +160,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) struct ip_vs_dest *dest = cp->dest; struct netns_ipvs *ipvs = cp->ipvs; - if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { + if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) { struct ip_vs_cpu_stats *s; struct ip_vs_service *svc; @@ -2030,7 +2030,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state } /* Check the server status */ - if (cp && cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) { + if (cp && cp->dest && !(cp->dest->cflags & IP_VS_DEST_CF_AVAILABLE)) { /* the destination server is not available */ if (sysctl_expire_nodest_conn(ipvs)) { bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 8aa29596b36f..1aec6a078a52 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1058,7 +1058,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, } /* set the dest status flags */ - dest->flags |= IP_VS_DEST_F_AVAILABLE; + dest->cflags |= IP_VS_DEST_CF_AVAILABLE; if (READ_ONCE(dest->u_threshold) != udest->u_threshold || READ_ONCE(dest->l_threshold) != udest->l_threshold) { @@ -1325,7 +1325,7 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, int svcupd) { - dest->flags &= ~IP_VS_DEST_F_AVAILABLE; + dest->cflags &= ~IP_VS_DEST_CF_AVAILABLE; /* * Remove it from the d-linked destination list. diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c index 75f4c231f4a0..f04d2530a093 100644 --- a/net/netfilter/ipvs/ip_vs_dh.c +++ b/net/netfilter/ipvs/ip_vs_dh.c @@ -220,8 +220,8 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, s = (struct ip_vs_dh_state *) svc->sched_data; dest = ip_vs_dh_get(svc->af, s, &iph->daddr); - if (!dest - || !(dest->flags & IP_VS_DEST_F_AVAILABLE) + if (!dest || + !(dest->cflags & IP_VS_DEST_CF_AVAILABLE) || atomic_read(&dest->weight) <= 0 || is_overloaded(dest)) { ip_vs_scheduler_err(svc, "no destination available"); diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 2423513d701d..d22f8154ff94 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -502,7 +502,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, */ dest = en->dest; - if ((dest->flags & IP_VS_DEST_F_AVAILABLE) && + if ((dest->cflags & IP_VS_DEST_CF_AVAILABLE) && atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc)) goto out; } diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index cdb1d4bf6761..abe9fd076495 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -170,8 +170,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) if (least->flags & IP_VS_DEST_F_OVERLOAD) continue; - if ((atomic_read(&least->weight) > 0) - && (least->flags & IP_VS_DEST_F_AVAILABLE)) { + if ((atomic_read(&least->weight) > 0) && + (least->cflags & IP_VS_DEST_CF_AVAILABLE)) { loh = ip_vs_dest_conn_overhead(least); goto nextstage; } @@ -187,8 +187,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) doh = ip_vs_dest_conn_overhead(dest); if (((__s64)loh * atomic_read(&dest->weight) > - (__s64)doh * atomic_read(&least->weight)) - && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { + (__s64)doh * atomic_read(&least->weight)) && + (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) { least = dest; loh = doh; } diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index df72b0376970..b9e3ac950894 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -279,17 +279,18 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) flow->timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow); err = rhashtable_insert_fast(&flow_table->rhashtable, - &flow->tuplehash[0].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].node, nf_flow_offload_rhash_params); if (err < 0) return err; + /* GC only iterates original-direction entries; publish original last. */ err = rhashtable_insert_fast(&flow_table->rhashtable, - &flow->tuplehash[1].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].node, nf_flow_offload_rhash_params); if (err < 0) { rhashtable_remove_fast(&flow_table->rhashtable, - &flow->tuplehash[0].node, + &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].node, nf_flow_offload_rhash_params); return err; } diff --git a/net/netfilter/nf_tables_offload.c b/net/netfilter/nf_tables_offload.c index 64675f1c7f29..196dd3c00ad0 100644 --- a/net/netfilter/nf_tables_offload.c +++ b/net/netfilter/nf_tables_offload.c @@ -551,7 +551,7 @@ static void nft_flow_rule_offload_abort(struct net *net, break; } - if (WARN_ON_ONCE(err)) + if (WARN_ON_ONCE(err && err != -ENOMEM)) break; } } diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 102055a8c1d6..7e55ddab7b96 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1995,13 +1995,13 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock) sock->type == SOCK_RAW) skb->protocol = dev_parse_header_protocol(skb); + skb_probe_transport_header(skb); + /* Move network header to the right position for VLAN tagged packets */ if (likely(skb->dev->type == ARPHRD_ETHER) && eth_type_vlan(skb->protocol) && vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); - - skb_probe_transport_header(skb); } /* @@ -2712,6 +2712,9 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, len = ((to_write > len_max) ? len_max : to_write); } + if (unlikely(!skb->len)) + return -EINVAL; + packet_parse_headers(skb, sock); return tp_len; diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 3b7ae280d32e..6774edbdd528 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -41,11 +41,9 @@ int tcf_dev_queue_xmit(struct sk_buff *skb, int (*xmit)(struct sk_buff *skb)) } EXPORT_SYMBOL_GPL(tcf_dev_queue_xmit); -static void tcf_action_goto_chain_exec(const struct tc_action *a, +static void tcf_action_goto_chain_exec(const struct tcf_chain *chain, struct tcf_result *res) { - const struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain); - res->goto_tp = rcu_dereference_bh(chain->filter_chain); } @@ -1117,12 +1115,14 @@ int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions, return TC_ACT_OK; } } else if (TC_ACT_EXT_CMP(ret, TC_ACT_GOTO_CHAIN)) { - if (unlikely(!rcu_access_pointer(a->goto_chain))) { + struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain); + + if (unlikely(!chain)) { tcf_set_drop_reason(skb, SKB_DROP_REASON_TC_CHAIN_NOTFOUND); return TC_ACT_SHOT; } - tcf_action_goto_chain_exec(a, res); + tcf_action_goto_chain_exec(chain, res); } if (ret != TC_ACT_PIPE) diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index e888d0aa9f21..a6003fa35c7c 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -374,7 +374,8 @@ static int cls_bpf_prog_from_ops(struct nlattr **tb, struct cls_bpf_prog *prog) } static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog, - u32 gen_flags, const struct tcf_proto *tp) + u32 gen_flags, const struct tcf_proto *tp, + struct netlink_ext_ack *extack) { struct bpf_prog *fp; char *name = NULL; @@ -388,6 +389,19 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog, if (IS_ERR(fp)) return PTR_ERR(fp); + if (bpf_prog_is_dev_bound(fp->aux)) { + struct tcf_block *block = tp->chain->block; + struct net_device *dev; + + dev = block->q ? qdisc_dev(block->q) : NULL; + if (!dev || !bpf_offload_dev_match(fp, dev)) { + NL_SET_ERR_MSG(extack, + "Program is bound to a different device"); + bpf_prog_put(fp); + return -EINVAL; + } + } + if (tb[TCA_BPF_NAME]) { name = nla_memdup(tb[TCA_BPF_NAME], GFP_KERNEL); if (!name) { @@ -492,7 +506,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, prog->gen_flags = gen_flags; ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog) : - cls_bpf_prog_from_efd(tb, prog, gen_flags, tp); + cls_bpf_prog_from_efd(tb, prog, gen_flags, tp, extack); if (ret < 0) goto errout_idr; diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 58e849c0acf4..a90de52e1ac8 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -1336,6 +1336,9 @@ static void u32_bind_class(void *fh, u32 classid, unsigned long cl, void *q, { struct tc_u_knode *n = fh; + if (TC_U32_KEY(n->handle) == 0) + return; + tc_cls_bind_class(classid, cl, q, &n->res, base); } diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 9a23fef527a2..e7d87e81bb92 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -413,12 +413,13 @@ static __u8 __detect_linklayer(struct tc_ratespec *r, __u32 *rtab) } static struct qdisc_rate_table *qdisc_rtab_list; +static DEFINE_SPINLOCK(qdisc_rtab_lock); struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *tab, struct netlink_ext_ack *extack) { - struct qdisc_rate_table *rtab; + struct qdisc_rate_table *rtab, *new_rtab; if (tab == NULL || r->rate == 0 || r->cell_log == 0 || r->cell_log >= 32 || @@ -427,15 +428,20 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, return NULL; } + new_rtab = kmalloc_obj(*new_rtab); + + spin_lock(&qdisc_rtab_lock); for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) { if (!memcmp(&rtab->rate, r, sizeof(struct tc_ratespec)) && !memcmp(&rtab->data, nla_data(tab), 1024)) { rtab->refcnt++; + spin_unlock(&qdisc_rtab_lock); + kfree(new_rtab); return rtab; } } - rtab = kmalloc(sizeof(*rtab), GFP_KERNEL); + rtab = new_rtab; if (rtab) { rtab->rate = *r; rtab->refcnt = 1; @@ -447,6 +453,7 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, } else { NL_SET_ERR_MSG(extack, "Failed to allocate new qdisc rate table"); } + spin_unlock(&qdisc_rtab_lock); return rtab; } EXPORT_SYMBOL(qdisc_get_rtab); @@ -455,18 +462,25 @@ void qdisc_put_rtab(struct qdisc_rate_table *tab) { struct qdisc_rate_table *rtab, **rtabp; - if (!tab || --tab->refcnt) + if (!tab) return; + spin_lock(&qdisc_rtab_lock); + if (--tab->refcnt) { + spin_unlock(&qdisc_rtab_lock); + return; + } + for (rtabp = &qdisc_rtab_list; (rtab = *rtabp) != NULL; rtabp = &rtab->next) { if (rtab == tab) { *rtabp = rtab->next; - kfree(rtab); - return; + break; } } + spin_unlock(&qdisc_rtab_lock); + kfree(tab); } EXPORT_SYMBOL(qdisc_put_rtab); diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index ceca15b43f35..dd8b70914ce1 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2284,6 +2284,11 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, if (err < 0) return err; + /* If crypto failed the connection is broken */ + err = ctx->async_wait.err; + if (err) + goto splice_read_end; + if (!skb_queue_empty(&ctx->rx_list)) { skb = __skb_dequeue(&ctx->rx_list); } else { diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 733c3616256a..5db55f3baf0b 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -362,6 +362,7 @@ static void x25_destroy_timer(struct timer_list *t) struct sock *sk = from_timer(sk, t, sk_timer); x25_destroy_socket_from_timer(sk); + sock_put(sk); } /* @@ -397,9 +398,8 @@ static void __x25_destroy_socket(struct sock *sk) if (sk_has_allocations(sk)) { /* Defer: outstanding buffers */ - sk->sk_timer.expires = jiffies + 10 * HZ; sk->sk_timer.function = x25_destroy_timer; - add_timer(&sk->sk_timer); + sk_reset_timer(sk, &sk->sk_timer, jiffies + 10 * HZ); } else { /* drop last reference so sock_put will free */ __sock_put(sk); diff --git a/net/x25/x25_timer.c b/net/x25/x25_timer.c index 9376365cdcc9..2afa8ecaf509 100644 --- a/net/x25/x25_timer.c +++ b/net/x25/x25_timer.c @@ -36,45 +36,45 @@ void x25_init_timers(struct sock *sk) void x25_start_heartbeat(struct sock *sk) { - mod_timer(&sk->sk_timer, jiffies + 5 * HZ); + sk_reset_timer(sk, &sk->sk_timer, jiffies + 5 * HZ); } void x25_stop_heartbeat(struct sock *sk) { - del_timer(&sk->sk_timer); + sk_stop_timer(sk, &sk->sk_timer); } void x25_start_t2timer(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - mod_timer(&x25->timer, jiffies + x25->t2); + sk_reset_timer(sk, &x25->timer, jiffies + x25->t2); } void x25_start_t21timer(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - mod_timer(&x25->timer, jiffies + x25->t21); + sk_reset_timer(sk, &x25->timer, jiffies + x25->t21); } void x25_start_t22timer(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - mod_timer(&x25->timer, jiffies + x25->t22); + sk_reset_timer(sk, &x25->timer, jiffies + x25->t22); } void x25_start_t23timer(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - mod_timer(&x25->timer, jiffies + x25->t23); + sk_reset_timer(sk, &x25->timer, jiffies + x25->t23); } void x25_stop_timer(struct sock *sk) { - del_timer(&x25_sk(sk)->timer); + sk_stop_timer(sk, &x25_sk(sk)->timer); } unsigned long x25_display_timer(struct sock *sk) @@ -108,7 +108,7 @@ static void x25_heartbeat_expiry(struct timer_list *t) sock_flag(sk, SOCK_DEAD))) { bh_unlock_sock(sk); x25_destroy_socket_from_timer(sk); - return; + goto out; } break; @@ -120,8 +120,14 @@ static void x25_heartbeat_expiry(struct timer_list *t) break; } restart_heartbeat: - x25_start_heartbeat(sk); + /* Do not rearm once __x25_destroy_socket() has unlinked the socket: + * it is past its cancel point and owns the teardown from there on. + */ + if (sk_hashed(sk)) + x25_start_heartbeat(sk); bh_unlock_sock(sk); +out: + sock_put(sk); } /* @@ -166,4 +172,5 @@ static void x25_timer_expiry(struct timer_list *t) } else x25_do_timer_expiry(sk); bh_unlock_sock(sk); + sock_put(sk); } diff --git a/security/security.c b/security/security.c index dd6b922c12de..de57d6e2b3a4 100644 --- a/security/security.c +++ b/security/security.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -3214,13 +3213,7 @@ int security_file_receive(struct file *file) */ int security_file_open(struct file *file) { - int ret; - - ret = call_int_hook(file_open, file); - if (ret) - return ret; - - return fsnotify_open_perm(file); + return call_int_hook(file_open, file); } /** diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 383f3ae82a73..304746b503a0 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -712,6 +712,7 @@ static inline void symtab_hash_eval(struct symtab *s) static int policydb_index(struct policydb *p) { int i, rc; + u32 v; if (p->mls_enabled) pr_debug( @@ -764,6 +765,24 @@ static int policydb_index(struct policydb *p) if (rc) goto out; } + + /* + * A sparse class value is absorbed by policydb_class_isvalid() and + * its siblings, but no such predicate exists for booleans: every + * user of bool_val_to_struct[] walks it by index and dereferences + * each entry -- cond_evaluate_expr(), the two getters and + * security_set_bools() -- so an unclaimed one has no consumer that + * can tolerate it. + */ + for (v = 0; v < p->p_bools.nprim; v++) { + if (!p->bool_val_to_struct[v]) { + pr_err("SELinux: boolean %u is declared but not defined\n", + v + 1); + rc = -EINVAL; + goto out; + } + } + rc = 0; out: return rc; @@ -1356,6 +1375,18 @@ static int class_read(struct policydb *p, struct symtab *s, void *fp) cladatum->comkey); goto bad; } + + /* + * security_get_permissions() maps the common's permissions + * into an array sized by this class's nprim, so a class must + * declare at least as many as the common it inherits. + */ + if (cladatum->permissions.nprim < + cladatum->comdatum->permissions.nprim) { + pr_err("SELinux: class %s has fewer permissions than common %s\n", + key, cladatum->comkey); + goto bad; + } } for (i = 0; i < nel; i++) { rc = perm_read(p, &cladatum->permissions, fp); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index f36332e64c4d..1a883471d538 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2175,7 +2175,9 @@ void selinux_policy_cancel(struct selinux_load_state *load_state) oldpolicy = rcu_dereference_protected(state->policy, lockdep_is_held(&state->policy_mutex)); - sidtab_cancel_convert(oldpolicy->sidtab); + /* a first load has no outgoing policy and converted nothing */ + if (oldpolicy) + sidtab_cancel_convert(oldpolicy->sidtab); selinux_policy_free(load_state->policy); kfree(load_state->convert_data); } @@ -3365,6 +3367,7 @@ int security_get_classes(struct selinux_policy *policy, char ***classes, u32 *nclasses) { struct policydb *policydb; + u32 i; int rc; policydb = &policy->policydb; @@ -3377,16 +3380,29 @@ int security_get_classes(struct selinux_policy *policy, rc = hashtab_map(&policydb->p_classes.table, get_classes_callback, *classes); - if (rc) { - u32 i; + if (rc) + goto err; - for (i = 0; i < *nclasses; i++) - kfree((*classes)[i]); - kfree(*classes); + /* + * The class symtab may be sparse, which policydb_class_isvalid() exists + * to absorb; the callback fills this array by value, so an unclaimed + * one leaves a NULL that sel_make_classes() hands to sel_make_dir(). + */ + for (i = 0; i < *nclasses; i++) { + if (!(*classes)[i]) { + rc = -EINVAL; + goto err; + } } out: return rc; + +err: + for (i = 0; i < *nclasses; i++) + kfree((*classes)[i]); + kfree(*classes); + return rc; } static int get_permissions_callback(void *k, void *d, void *args) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b0ee9b58e057..38e17ef34b8b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -13642,6 +13642,7 @@ static const struct hda_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26), SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x1e63, 0x6d9a, "F+ FLAPTOP r", ALC897_FIXUP_HP_HSMIC_VERB), #if 0 /* Below is a quirk table taken from the old code. diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index 1702f26049d3..b0fa80705be7 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -25,9 +25,9 @@ static const struct reg_default cs35l41_reg[] = { { CS35L41_GPIO_PAD_CONTROL, 0x00000000 }, { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, { CS35L41_TST_FS_MON0, 0x00020016 }, + { CS35L41_BSTCVRT_PEAK_CUR, 0x0000004A }, { CS35L41_BSTCVRT_COEFF, 0x00002424 }, { CS35L41_BSTCVRT_SLOPE_LBST, 0x00007500 }, - { CS35L41_BSTCVRT_PEAK_CUR, 0x0000004A }, { CS35L41_SP_ENABLES, 0x00000000 }, { CS35L41_SP_RATE_CTRL, 0x00000028 }, { CS35L41_SP_FORMAT, 0x18180200 }, diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c index 405dab137b3b..4b140b19102e 100644 --- a/sound/soc/codecs/cs35l45-tables.c +++ b/sound/soc/codecs/cs35l45-tables.c @@ -66,22 +66,6 @@ static const struct reg_default cs35l45_defaults[] = { { CS35L45_ASPTX3_INPUT, 0x00000020 }, { CS35L45_ASPTX4_INPUT, 0x00000028 }, { CS35L45_ASPTX5_INPUT, 0x00000048 }, - { CS35L45_DSP1_RX1_RATE, 0x00000001 }, - { CS35L45_DSP1_RX2_RATE, 0x00000001 }, - { CS35L45_DSP1_RX3_RATE, 0x00000001 }, - { CS35L45_DSP1_RX4_RATE, 0x00000001 }, - { CS35L45_DSP1_RX5_RATE, 0x00000001 }, - { CS35L45_DSP1_RX6_RATE, 0x00000001 }, - { CS35L45_DSP1_RX7_RATE, 0x00000001 }, - { CS35L45_DSP1_RX8_RATE, 0x00000001 }, - { CS35L45_DSP1_TX1_RATE, 0x00000001 }, - { CS35L45_DSP1_TX2_RATE, 0x00000001 }, - { CS35L45_DSP1_TX3_RATE, 0x00000001 }, - { CS35L45_DSP1_TX4_RATE, 0x00000001 }, - { CS35L45_DSP1_TX5_RATE, 0x00000001 }, - { CS35L45_DSP1_TX6_RATE, 0x00000001 }, - { CS35L45_DSP1_TX7_RATE, 0x00000001 }, - { CS35L45_DSP1_TX8_RATE, 0x00000001 }, { CS35L45_DSP1RX1_INPUT, 0x00000008 }, { CS35L45_DSP1RX2_INPUT, 0x00000009 }, { CS35L45_DSP1RX3_INPUT, 0x00000018 }, @@ -114,6 +98,22 @@ static const struct reg_default cs35l45_defaults[] = { { CS35L45_GPIO1_CTRL1, 0x81000001 }, { CS35L45_GPIO2_CTRL1, 0x81000001 }, { CS35L45_GPIO3_CTRL1, 0x81000001 }, + { CS35L45_DSP1_RX1_RATE, 0x00000001 }, + { CS35L45_DSP1_RX2_RATE, 0x00000001 }, + { CS35L45_DSP1_RX3_RATE, 0x00000001 }, + { CS35L45_DSP1_RX4_RATE, 0x00000001 }, + { CS35L45_DSP1_RX5_RATE, 0x00000001 }, + { CS35L45_DSP1_RX6_RATE, 0x00000001 }, + { CS35L45_DSP1_RX7_RATE, 0x00000001 }, + { CS35L45_DSP1_RX8_RATE, 0x00000001 }, + { CS35L45_DSP1_TX1_RATE, 0x00000001 }, + { CS35L45_DSP1_TX2_RATE, 0x00000001 }, + { CS35L45_DSP1_TX3_RATE, 0x00000001 }, + { CS35L45_DSP1_TX4_RATE, 0x00000001 }, + { CS35L45_DSP1_TX5_RATE, 0x00000001 }, + { CS35L45_DSP1_TX6_RATE, 0x00000001 }, + { CS35L45_DSP1_TX7_RATE, 0x00000001 }, + { CS35L45_DSP1_TX8_RATE, 0x00000001 }, }; static bool cs35l45_readable_reg(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 78ffb7fa7fc5..386a7d97da1f 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -46,11 +46,11 @@ static const struct reg_default cs4265_reg_defaults[] = { { CS4265_DAC_CHA_VOL, 0x00 }, { CS4265_DAC_CHB_VOL, 0x00 }, { CS4265_DAC_CTL2, 0xC0 }, - { CS4265_SPDIF_CTL1, 0x00 }, - { CS4265_SPDIF_CTL2, 0x00 }, { CS4265_INT_MASK, 0x00 }, { CS4265_STATUS_MODE_MSB, 0x00 }, { CS4265_STATUS_MODE_LSB, 0x00 }, + { CS4265_SPDIF_CTL1, 0x00 }, + { CS4265_SPDIF_CTL2, 0x00 }, }; static bool cs4265_readable_register(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 81bab8299eae..1f25630841e5 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -2166,7 +2166,7 @@ static int wsa_macro_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); - ucontrol->value.integer.value[0] = wsa->ear_spkr_gain; + ucontrol->value.enumerated.item[0] = wsa->ear_spkr_gain; return 0; } @@ -2177,7 +2177,7 @@ static int wsa_macro_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); - wsa->ear_spkr_gain = ucontrol->value.integer.value[0]; + wsa->ear_spkr_gain = ucontrol->value.enumerated.item[0]; return 0; } @@ -2191,7 +2191,7 @@ static int wsa_macro_rx_mux_get(struct snd_kcontrol *kcontrol, snd_soc_dapm_to_component(widget->dapm); struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); - ucontrol->value.integer.value[0] = + ucontrol->value.enumerated.item[0] = wsa->rx_port_value[widget->shift]; return 0; } @@ -2205,7 +2205,7 @@ static int wsa_macro_rx_mux_put(struct snd_kcontrol *kcontrol, snd_soc_dapm_to_component(widget->dapm); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct snd_soc_dapm_update *update = NULL; - u32 rx_port_value = ucontrol->value.integer.value[0]; + u32 rx_port_value = ucontrol->value.enumerated.item[0]; u32 bit_input; u32 aif_rst; struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index 6051027c8266..a725b026509a 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -472,10 +472,15 @@ static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); - int ret; + int ret, index; u32 reg_val; - reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2]; + index = ucontrol->value.integer.value[0] / 2; + if (index < 0 || index >= ARRAY_SIZE(float_vol_db_lookup)) + return -EINVAL; + + reg_val = float_vol_db_lookup[index]; + /* * The device applies the 32-bit coefficient to the playback path on * the write to DVC_CFG4 (the LSB, book 0 page 2 reg 0x0F), so the diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c index 3cc81dcf047e..7940280bf448 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -30,7 +30,12 @@ enum tplg_device_id { int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach, const char *prefix, const char ***tplg_files) { - struct snd_soc_acpi_mach_params mach_params = mach->mach_params; + struct snd_soc_acpi_mach *card_mach = dev_get_platdata(card->dev); + /* + * Use the acpi mach from the machine driver because the machine driver + * may change the dmic_num based on the machine driver quirk. + */ + struct snd_soc_acpi_mach_params mach_params = card_mach->mach_params; struct snd_soc_dai_link *dai_link; const struct firmware *fw; char platform[SOF_INTEL_PLATFORM_NAME_MAX]; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 1aba13a90973..fabd364d3316 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2654,6 +2654,15 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget ipc_size = ipc4_copier->ipc_config_size; ipc_data = ipc4_copier->ipc_config_data; + /* + * Refresh copier_data in ipc_config_data for host copiers. + * The node_id may have been updated by host_config after + * ipc_prepare, e.g. when host stream tags change after a + * suspend/resume cycle. + */ + if (swidget->id != snd_soc_dapm_buffer) + memcpy(ipc_data, &ipc4_copier->data, sizeof(ipc4_copier->data)); + msg = &ipc4_copier->msg; break; } @@ -2662,6 +2671,9 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget { struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier = dai->private; + struct sof_ipc4_copier_data *copier_data; + u32 gtw_cfg_config_length; + u32 tlv_size; pipeline = pipe_widget->private; if (pipeline->use_chain_dma) @@ -2670,6 +2682,27 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget ipc_size = ipc4_copier->ipc_config_size; ipc_data = ipc4_copier->ipc_config_data; + /* + * Refresh copier_data and dma_config_tlv in ipc_config_data. + * These may have been updated after ipc_prepare, e.g. when + * link DMA stream tags change after a suspend/resume cycle. + * + * copier_data->gtw_cfg.config_length does not include the + * TLV size (it was restored after sof_ipc4_prepare_copier_module), + * so temporarily inflate it to match the ipc_config_data layout. + */ + copier_data = &ipc4_copier->data; + gtw_cfg_config_length = copier_data->gtw_cfg.config_length * 4; + tlv_size = ipc_size - sizeof(*copier_data) - gtw_cfg_config_length; + + copier_data->gtw_cfg.config_length += tlv_size / 4; + memcpy(ipc_data, copier_data, sizeof(*copier_data)); + copier_data->gtw_cfg.config_length = gtw_cfg_config_length / 4; + + if (tlv_size) + memcpy(ipc_data + sizeof(*copier_data) + gtw_cfg_config_length, + &ipc4_copier->dma_config_tlv, tlv_size); + msg = &ipc4_copier->msg; break; } diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 9a52781bf8d8..f0a4e146d798 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -136,7 +136,6 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, { const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_pipeline *spipe = swidget->spipe; - bool use_count_decremented = false; int ret; int i; @@ -215,9 +214,10 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, return 0; widget_free: - /* widget use_count will be decremented by sof_widget_free() */ + /* widget use_count and core_put handled by sof_widget_free() */ sof_widget_free_unlocked(sdev, swidget); - use_count_decremented = true; + return ret; + pipe_widget_free: if (swidget->id != snd_soc_dapm_scheduler) { sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget); @@ -232,8 +232,7 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, } } use_count_dec: - if (!use_count_decremented) - swidget->use_count--; + swidget->use_count--; return ret; } diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c index 17ef05309469..45b178148b98 100644 --- a/sound/soc/xilinx/xlnx_formatter_pcm.c +++ b/sound/soc/xilinx/xlnx_formatter_pcm.c @@ -281,8 +281,7 @@ static irqreturn_t xlnx_mm2s_irq_handler(int irq, void *arg) { u32 val; void __iomem *reg; - struct device *dev = arg; - struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev); + struct xlnx_pcm_drv_data *adata = arg; reg = adata->mmio + XLNX_MM2S_OFFSET + XLNX_AUD_STS; val = readl(reg); @@ -300,8 +299,7 @@ static irqreturn_t xlnx_s2mm_irq_handler(int irq, void *arg) { u32 val; void __iomem *reg; - struct device *dev = arg; - struct xlnx_pcm_drv_data *adata = dev_get_drvdata(dev); + struct xlnx_pcm_drv_data *adata = arg; reg = adata->mmio + XLNX_S2MM_OFFSET + XLNX_AUD_STS; val = readl(reg); @@ -637,7 +635,7 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev) } ret = devm_request_irq(dev, aud_drv_data->mm2s_irq, xlnx_mm2s_irq_handler, 0, - "xlnx_formatter_pcm_mm2s_irq", dev); + "xlnx_formatter_pcm_mm2s_irq", aud_drv_data); if (ret) { dev_err(dev, "xlnx audio mm2s irq request failed\n"); goto clk_err; @@ -664,7 +662,7 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev) ret = devm_request_irq(dev, aud_drv_data->s2mm_irq, xlnx_s2mm_irq_handler, 0, "xlnx_formatter_pcm_s2mm_irq", - dev); + aud_drv_data); if (ret) { dev_err(dev, "xlnx audio s2mm irq request failed\n"); goto clk_err; diff --git a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c index 8f755d2464cf..fb44f1770dab 100644 --- a/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c +++ b/tools/testing/selftests/bpf/progs/verifier_linked_scalars.c @@ -31,4 +31,116 @@ l1: \ " ::: __clobber_all); } +/* + * Test that r += r (self-add, src_reg == dst_reg) clears the scalar ID + * so that sync_linked_regs() does not propagate an incorrect delta. + */ +SEC("socket") +__failure +__msg("div by zero") +__naked void scalars_self_add_clears_id(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r6 = r0; /* r6 unknown, id A */ \ + r7 = r6; /* r7 linked to r6, id A */ \ + call %[bpf_get_prandom_u32]; \ + r8 = r0; /* r8 unknown, id B */ \ + r9 = r8; /* r9 linked to r8, id B */ \ + if r7 != 1 goto l_exit_%=; \ + /* r7 == 1; sync propagates: r6 = 1 (known, id A) */ \ + r6 += r6; /* r6 = 2; should clear id */ \ + if r7 == r9 goto l_exit_%=; \ + /* Bug: r6 synced to r7(1)+delta(2)=3; Fix: r6 = 2 */ \ + if r6 == 3 goto l_exit_%=; \ + r0 /= 0; \ +l_exit_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* Same as above but with alu32 such that w6 += w6 also clears id. */ +SEC("socket") +__failure +__msg("div by zero") +__naked void scalars_self_add_alu32_clears_id(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + w6 = w0; \ + w7 = w6; \ + call %[bpf_get_prandom_u32]; \ + w8 = w0; \ + w9 = w8; \ + if w7 != 1 goto l_exit_%=; \ + w6 += w6; \ + if w7 == w9 goto l_exit_%=; \ + if w6 == 3 goto l_exit_%=; \ + r0 /= 0; \ +l_exit_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* + * Test that stale delta from a cleared BPF_ADD_CONST does not leak + * through assign_scalar_id_before_mov() into a new id, causing + * sync_linked_regs() to compute an incorrect offset. + */ +SEC("socket") +__failure +__msg("div by zero") +__naked void scalars_stale_delta_from_cleared_id(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + r6 = r0; /* r6 unknown, gets id A */ \ + r6 += 5; /* id A|ADD_CONST, delta 5 */ \ + r6 ^= 0; /* id cleared; delta stays 5 */ \ + r8 = r6; /* new id B, stale delta 5 */ \ + r8 += 3; /* id B|ADD_CONST, delta 3 */ \ + r9 = r6; /* id B, stale delta 5 */ \ + if r9 != 10 goto l_exit_%=; \ + /* Bug: r8 = 10+(3-5) = 8; Fix: r8 = 10+(3-0) = 13 */ \ + if r8 == 8 goto l_exit_%=; \ + r0 /= 0; \ +l_exit_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + +/* Same as above but with alu32. */ +SEC("socket") +__failure +__msg("div by zero") +__naked void scalars_stale_delta_from_cleared_id_alu32(void) +{ + asm volatile (" \ + call %[bpf_get_prandom_u32]; \ + w6 = w0; \ + w6 += 5; \ + w6 ^= 0; \ + w8 = w6; \ + w8 += 3; \ + w9 = w6; \ + if w9 != 10 goto l_exit_%=; \ + if w8 == 8 goto l_exit_%=; \ + r0 /= 0; \ +l_exit_%=: \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_get_prandom_u32) + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 4555a392d5f0..3eae0c4e27ba 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -552,7 +552,7 @@ check_transfer() mv "$tmpfile" "$out" tmpfile="" fi - cmp -l "$in" "$out" | while read -r i a b; do + while read -r i a b; do local sum=$((0${a} + 0${b})) if [ $check_invert -eq 0 ] || [ $sum -ne $((0xff)) ]; then fail_test "$what does not match (in, out):" @@ -563,7 +563,7 @@ check_transfer() else print_info "$what has inverted byte at ${i}" fi - done + done < <(cmp -l "$in" "$out") return 0 }