Re: Linux 6.12.105
From: Greg Kroah-Hartman
Date: Sun Aug 23 2026 - 08:48:48 EST
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 {
<GIC_PPI 11
(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 10
+ (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 15
(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
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 <linux/types.h>
#include <linux/ccp.h>
+#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 <linux/vfio.h>
+#include <linux/nospec.h>
#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 <linux/slab.h>
+#include <linux/nospec.h>
#include <linux/vfio.h>
#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 <linux/slab.h>
#include <linux/gfp.h>
#include <linux/sched.h>
+#include <linux/sched/mm.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/ratelimit.h>
@@ -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 <linux/kernel.h>
@@ -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 <linux/mm.h>
#include <linux/mm_inline.h>
#include <linux/pagemap.h>
+#include <linux/pagewalk.h>
#include <linux/rmap.h>
#include <linux/swap.h>
#include <linux/swapops.h>
@@ -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 <linux/swap.h>
#include <linux/swapops.h>
+#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 <linux/debugfs.h>
#include <linux/ptdump.h>
#include <linux/kasan.h>
+#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 <linux/kernel.h>
#include <linux/kernel_read_file.h>
#include <linux/lsm_hooks.h>
-#include <linux/fsnotify.h>
#include <linux/mman.h>
#include <linux/mount.h>
#include <linux/personality.h>
@@ -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
}