From bd51834d1cf65a2c801295d230c220aeebf87a73 Mon Sep 17 00:00:00 2001 From: Ming Wang Date: Thu, 24 Apr 2025 20:15:47 +0800 Subject: [PATCH 01/16] LoongArch: Return NULL from huge_pte_offset() for invalid PMD LoongArch's huge_pte_offset() currently returns a pointer to a PMD slot even if the underlying entry points to invalid_pte_table (indicating no mapping). Callers like smaps_hugetlb_range() fetch this invalid entry value (the address of invalid_pte_table) via this pointer. The generic is_swap_pte() check then incorrectly identifies this address as a swap entry on LoongArch, because it satisfies the "!pte_present() && !pte_none()" conditions. This misinterpretation, combined with a coincidental match by is_migration_entry() on the address bits, leads to kernel crashes in pfn_swap_entry_to_page(). Fix this at the architecture level by modifying huge_pte_offset() to check the PMD entry's content using pmd_none() before returning. If the entry is invalid (i.e., it points to invalid_pte_table), return NULL instead of the pointer to the slot. Cc: stable@vger.kernel.org Acked-by: Peter Xu Co-developed-by: Hongchen Zhang Signed-off-by: Hongchen Zhang Signed-off-by: Ming Wang Signed-off-by: Huacai Chen --- arch/loongarch/mm/hugetlbpage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/loongarch/mm/hugetlbpage.c b/arch/loongarch/mm/hugetlbpage.c index e4068906143b..cea84d7f2b91 100644 --- a/arch/loongarch/mm/hugetlbpage.c +++ b/arch/loongarch/mm/hugetlbpage.c @@ -47,7 +47,7 @@ pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr, pmd = pmd_offset(pud, addr); } } - return (pte_t *) pmd; + return pmd_none(pmdp_get(pmd)) ? NULL : (pte_t *) pmd; } uint64_t pmd_to_entrylo(unsigned long pmd_val) -- 2.51.0 From 8b2d01fec800081dd68271c01e4d239ef4d7115e Mon Sep 17 00:00:00 2001 From: Yulong Han Date: Thu, 24 Apr 2025 20:15:52 +0800 Subject: [PATCH 02/16] LoongArch: KVM: Fix multiple typos of KVM code Fix multiple typos inside arch/loongarch/kvm. Cc: stable@vger.kernel.org Reviewed-by: Yuli Wang Reviewed-by: Bibo Mao Signed-off-by: Yulong Han Signed-off-by: Huacai Chen --- arch/loongarch/kvm/intc/ipi.c | 4 ++-- arch/loongarch/kvm/main.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/loongarch/kvm/intc/ipi.c b/arch/loongarch/kvm/intc/ipi.c index 93f4acd44523..fe734dc062ed 100644 --- a/arch/loongarch/kvm/intc/ipi.c +++ b/arch/loongarch/kvm/intc/ipi.c @@ -111,7 +111,7 @@ static int send_ipi_data(struct kvm_vcpu *vcpu, gpa_t addr, uint64_t data) ret = kvm_io_bus_read(vcpu, KVM_IOCSR_BUS, addr, sizeof(val), &val); srcu_read_unlock(&vcpu->kvm->srcu, idx); if (unlikely(ret)) { - kvm_err("%s: : read date from addr %llx failed\n", __func__, addr); + kvm_err("%s: : read data from addr %llx failed\n", __func__, addr); return ret; } /* Construct the mask by scanning the bit 27-30 */ @@ -127,7 +127,7 @@ static int send_ipi_data(struct kvm_vcpu *vcpu, gpa_t addr, uint64_t data) ret = kvm_io_bus_write(vcpu, KVM_IOCSR_BUS, addr, sizeof(val), &val); srcu_read_unlock(&vcpu->kvm->srcu, idx); if (unlikely(ret)) - kvm_err("%s: : write date to addr %llx failed\n", __func__, addr); + kvm_err("%s: : write data to addr %llx failed\n", __func__, addr); return ret; } diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c index d165cd38c6bb..80ea63d465b8 100644 --- a/arch/loongarch/kvm/main.c +++ b/arch/loongarch/kvm/main.c @@ -296,10 +296,10 @@ int kvm_arch_enable_virtualization_cpu(void) /* * Enable virtualization features granting guest direct control of * certain features: - * GCI=2: Trap on init or unimplement cache instruction. + * GCI=2: Trap on init or unimplemented cache instruction. * TORU=0: Trap on Root Unimplement. * CACTRL=1: Root control cache. - * TOP=0: Trap on Previlege. + * TOP=0: Trap on Privilege. * TOE=0: Trap on Exception. * TIT=0: Trap on Timer. */ -- 2.51.0 From 9ea86232a5520d9d21832d06031ea80f055a6ff8 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 24 Apr 2025 20:15:52 +0800 Subject: [PATCH 03/16] LoongArch: KVM: Fully clear some CSRs when VM reboot Some registers such as LOONGARCH_CSR_ESTAT and LOONGARCH_CSR_GINTC are partly cleared with function _kvm_setcsr(). This comes from the hardware specification, some bits are read only in VM mode, and however they can be written in host mode. So they are partly cleared in VM mode, and can be fully cleared in host mode. These read only bits show pending interrupt or exception status. When VM reset, the read-only bits should be cleared, otherwise vCPU will receive unknown interrupts in boot stage. Here registers LOONGARCH_CSR_ESTAT/LOONGARCH_CSR_GINTC are fully cleared in ioctl KVM_REG_LOONGARCH_VCPU_RESET vCPU reset path. Cc: stable@vger.kernel.org Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 8e427b379661..2d3c2a2d1d1c 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -902,6 +902,13 @@ static int kvm_set_one_reg(struct kvm_vcpu *vcpu, vcpu->arch.st.guest_addr = 0; memset(&vcpu->arch.irq_pending, 0, sizeof(vcpu->arch.irq_pending)); memset(&vcpu->arch.irq_clear, 0, sizeof(vcpu->arch.irq_clear)); + + /* + * When vCPU reset, clear the ESTAT and GINTC registers + * Other CSR registers are cleared with function _kvm_setcsr(). + */ + kvm_write_sw_gcsr(vcpu->arch.csr, LOONGARCH_CSR_GINTC, 0); + kvm_write_sw_gcsr(vcpu->arch.csr, LOONGARCH_CSR_ESTAT, 0); break; default: ret = -EINVAL; -- 2.51.0 From 5add0dbbebd60628b55e5eb8426612dedab7311a Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 24 Apr 2025 20:15:52 +0800 Subject: [PATCH 04/16] LoongArch: KVM: Fix PMU pass-through issue if VM exits to host finally In function kvm_pre_enter_guest(), it prepares to enter guest and check whether there are pending signals or events. And it will not enter guest if there are, PMU pass-through preparation for guest should be cancelled and host should own PMU hardware. Cc: stable@vger.kernel.org Fixes: f4e40ea9f78f ("LoongArch: KVM: Add PMU support for guest") Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen --- arch/loongarch/kvm/vcpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c index 2d3c2a2d1d1c..5af32ec62cb1 100644 --- a/arch/loongarch/kvm/vcpu.c +++ b/arch/loongarch/kvm/vcpu.c @@ -294,6 +294,7 @@ static int kvm_pre_enter_guest(struct kvm_vcpu *vcpu) vcpu->arch.aux_inuse &= ~KVM_LARCH_SWCSR_LATEST; if (kvm_request_pending(vcpu) || xfer_to_guest_mode_work_pending()) { + kvm_lose_pmu(vcpu); /* make sure the vcpu mode has been written */ smp_store_mb(vcpu->mode, OUTSIDE_GUEST_MODE); local_irq_enable(); -- 2.51.0 From 3318dc299b072a0511d6dfd8367f3304fb6d9827 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Tue, 22 Apr 2025 17:16:16 +0100 Subject: [PATCH 05/16] irqchip/gic-v2m: Prevent use after free of gicv2m_get_fwnode() With ACPI in place, gicv2m_get_fwnode() is registered with the pci subsystem as pci_msi_get_fwnode_cb(), which may get invoked at runtime during a PCI host bridge probe. But, the call back is wrongly marked as __init, causing it to be freed, while being registered with the PCI subsystem and could trigger: Unable to handle kernel paging request at virtual address ffff8000816c0400 gicv2m_get_fwnode+0x0/0x58 (P) pci_set_bus_msi_domain+0x74/0x88 pci_register_host_bridge+0x194/0x548 This is easily reproducible on a Juno board with ACPI boot. Retain the function for later use. Fixes: 0644b3daca28 ("irqchip/gic-v2m: acpi: Introducing GICv2m ACPI support") Signed-off-by: Suzuki K Poulose Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Reviewed-by: Marc Zyngier Cc: stable@vger.kernel.org --- drivers/irqchip/irq-gic-v2m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index c69894861866..dc98c39d2b20 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -421,7 +421,7 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle, #ifdef CONFIG_ACPI static int acpi_num_msi; -static __init struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) +static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) { struct v2m_data *data; -- 2.51.0 From bbce3de72be56e4b5f68924b7da9630cc89aa1a8 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 25 Apr 2025 01:51:24 -0700 Subject: [PATCH 06/16] sched/eevdf: Fix se->slice being set to U64_MAX and resulting crash There is a code path in dequeue_entities() that can set the slice of a sched_entity to U64_MAX, which sometimes results in a crash. The offending case is when dequeue_entities() is called to dequeue a delayed group entity, and then the entity's parent's dequeue is delayed. In that case: 1. In the if (entity_is_task(se)) else block at the beginning of dequeue_entities(), slice is set to cfs_rq_min_slice(group_cfs_rq(se)). If the entity was delayed, then it has no queued tasks, so cfs_rq_min_slice() returns U64_MAX. 2. The first for_each_sched_entity() loop dequeues the entity. 3. If the entity was its parent's only child, then the next iteration tries to dequeue the parent. 4. If the parent's dequeue needs to be delayed, then it breaks from the first for_each_sched_entity() loop _without updating slice_. 5. The second for_each_sched_entity() loop sets the parent's ->slice to the saved slice, which is still U64_MAX. This throws off subsequent calculations with potentially catastrophic results. A manifestation we saw in production was: 6. In update_entity_lag(), se->slice is used to calculate limit, which ends up as a huge negative number. 7. limit is used in se->vlag = clamp(vlag, -limit, limit). Because limit is negative, vlag > limit, so se->vlag is set to the same huge negative number. 8. In place_entity(), se->vlag is scaled, which overflows and results in another huge (positive or negative) number. 9. The adjusted lag is subtracted from se->vruntime, which increases or decreases se->vruntime by a huge number. 10. pick_eevdf() calls entity_eligible()/vruntime_eligible(), which incorrectly returns false because the vruntime is so far from the other vruntimes on the queue, causing the (vruntime - cfs_rq->min_vruntime) * load calulation to overflow. 11. Nothing appears to be eligible, so pick_eevdf() returns NULL. 12. pick_next_entity() tries to dereference the return value of pick_eevdf() and crashes. Dumping the cfs_rq states from the core dumps with drgn showed tell-tale huge vruntime ranges and bogus vlag values, and I also traced se->slice being set to U64_MAX on live systems (which was usually "benign" since the rest of the runqueue needed to be in a particular state to crash). Fix it in dequeue_entities() by always setting slice from the first non-empty cfs_rq. Fixes: aef6987d8954 ("sched/eevdf: Propagate min_slice up the cgroup hierarchy") Signed-off-by: Omar Sandoval Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/f0c2d1072be229e1bdddc73c0703919a8b00c652.1745570998.git.osandov@fb.com --- kernel/sched/fair.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index e43993a4e580..0fb9bf995a47 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7081,9 +7081,6 @@ static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags) h_nr_idle = task_has_idle_policy(p); if (task_sleep || task_delayed || !se->sched_delayed) h_nr_runnable = 1; - } else { - cfs_rq = group_cfs_rq(se); - slice = cfs_rq_min_slice(cfs_rq); } for_each_sched_entity(se) { @@ -7093,6 +7090,7 @@ static int dequeue_entities(struct rq *rq, struct sched_entity *se, int flags) if (p && &p->se == se) return -1; + slice = cfs_rq_min_slice(cfs_rq); break; } -- 2.51.0 From 831e3f545b0771f91fa94cdb8aa569a73b9ec580 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Thu, 24 Apr 2025 09:27:35 -0400 Subject: [PATCH 07/16] Revert "sunrpc: clean cache_detail immediately when flush is written frequently" Ondrej reports that certain SELinux tests are failing after commit fc2a169c56de ("sunrpc: clean cache_detail immediately when flush is written frequently"), merged during the v6.15 merge window. Reported-by: Ondrej Mosnacek Fixes: fc2a169c56de ("sunrpc: clean cache_detail immediately when flush is written frequently") Signed-off-by: Chuck Lever --- net/sunrpc/cache.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 004cdb59f010..7ce5e28a6c03 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -1536,13 +1536,9 @@ static ssize_t write_flush(struct file *file, const char __user *buf, * or by one second if it has already reached the current time. * Newly added cache entries will always have ->last_refresh greater * that ->flush_time, so they don't get flushed prematurely. - * - * If someone frequently calls the flush interface, we should - * immediately clean the corresponding cache_detail instead of - * continuously accumulating nextcheck. */ - if (cd->flush_time >= now && cd->flush_time < (now + 5)) + if (cd->flush_time >= now) now = cd->flush_time + 1; cd->flush_time = now; -- 2.51.0 From b4432656b36e5cc1d50a1f2dc15357543add530e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 27 Apr 2025 15:19:23 -0700 Subject: [PATCH 08/16] Linux 6.15-rc4 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 07f818186151..5aa9ee52a765 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 6 PATCHLEVEL = 15 SUBLEVEL = 0 -EXTRAVERSION = -rc3 +EXTRAVERSION = -rc4 NAME = Baby Opossum Posse # *DOCUMENTATION* -- 2.51.0 From 8960c45a0f41399770d408921180231395604e2f Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 11 Apr 2025 12:31:43 +0100 Subject: [PATCH 09/16] ACPI: PCC: Simplify PCC shared memory region handling The PCC driver now handles mapping and unmapping of shared memory areas as part of pcc_mbox_{request,free}_channel(). Without these before, this ACPI PCC opregion driver did handling of those mappings like several other PCC mailbox client drivers. There were redundant operations, leading to unnecessary code. Maintaining the consistency across these driver was harder due to scattered handling of shmem. Just use the mapped shmem and remove all redundant operations from this driver. Tested-by: Adam Young Signed-off-by: Sudeep Holla Link: https://patch.msgid.link/20250411113144.1151094-1-sudeep.holla@arm.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_pcc.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/acpi_pcc.c b/drivers/acpi/acpi_pcc.c index 07a034a53aca..97064e943768 100644 --- a/drivers/acpi/acpi_pcc.c +++ b/drivers/acpi/acpi_pcc.c @@ -31,7 +31,6 @@ struct pcc_data { struct pcc_mbox_chan *pcc_chan; - void __iomem *pcc_comm_addr; struct completion done; struct mbox_client cl; struct acpi_pcc_info ctx; @@ -81,14 +80,6 @@ acpi_pcc_address_space_setup(acpi_handle region_handle, u32 function, ret = AE_SUPPORT; goto err_free_channel; } - data->pcc_comm_addr = acpi_os_ioremap(pcc_chan->shmem_base_addr, - pcc_chan->shmem_size); - if (!data->pcc_comm_addr) { - pr_err("Failed to ioremap PCC comm region mem for %d\n", - ctx->subspace_id); - ret = AE_NO_MEMORY; - goto err_free_channel; - } *region_context = data; return AE_OK; @@ -113,7 +104,7 @@ acpi_pcc_address_space_handler(u32 function, acpi_physical_address addr, reinit_completion(&data->done); /* Write to Shared Memory */ - memcpy_toio(data->pcc_comm_addr, (void *)value, data->ctx.length); + memcpy_toio(data->pcc_chan->shmem, (void *)value, data->ctx.length); ret = mbox_send_message(data->pcc_chan->mchan, NULL); if (ret < 0) @@ -134,7 +125,7 @@ acpi_pcc_address_space_handler(u32 function, acpi_physical_address addr, mbox_chan_txdone(data->pcc_chan->mchan, ret); - memcpy_fromio(value, data->pcc_comm_addr, data->ctx.length); + memcpy_fromio(value, data->pcc_chan->shmem, data->ctx.length); return AE_OK; } -- 2.51.0 From 3a3ce10e7adcc02b333ffebbc414162e5d22fd16 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 11 Apr 2025 12:31:44 +0100 Subject: [PATCH 10/16] ACPI: CPPC: Simplify PCC shared memory region handling The PCC driver now handles mapping and unmapping of shared memory areas as part of pcc_mbox_{request,free}_channel(). Without these before, this ACPI CPPC driver did handling of those mappings like several other PCC mailbox client drivers. There were redundant operations, leading to unnecessary code. Maintaining the consistency across these driver was harder due to scattered handling of shmem. Just use the mapped shmem and remove all redundant operations from this driver. Signed-off-by: Sudeep Holla Link: https://patch.msgid.link/20250411113144.1151094-2-sudeep.holla@arm.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index f193e713825a..d972157a79b6 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -47,7 +47,6 @@ struct cppc_pcc_data { struct pcc_mbox_chan *pcc_channel; - void __iomem *pcc_comm_addr; bool pcc_channel_acquired; unsigned int deadline_us; unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; @@ -95,7 +94,7 @@ static DEFINE_PER_CPU(int, cpu_pcc_subspace_idx); static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); /* pcc mapped address + header size + offset within PCC subspace */ -#define GET_PCC_VADDR(offs, pcc_ss_id) (pcc_data[pcc_ss_id]->pcc_comm_addr + \ +#define GET_PCC_VADDR(offs, pcc_ss_id) (pcc_data[pcc_ss_id]->pcc_channel->shmem + \ 0x8 + (offs)) /* Check if a CPC register is in PCC */ @@ -223,7 +222,7 @@ static int check_pcc_chan(int pcc_ss_id, bool chk_err_bit) int ret, status; struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id]; struct acpi_pcct_shared_memory __iomem *generic_comm_base = - pcc_ss_data->pcc_comm_addr; + pcc_ss_data->pcc_channel->shmem; if (!pcc_ss_data->platform_owns_pcc) return 0; @@ -258,7 +257,7 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd) int ret = -EIO, i; struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id]; struct acpi_pcct_shared_memory __iomem *generic_comm_base = - pcc_ss_data->pcc_comm_addr; + pcc_ss_data->pcc_channel->shmem; unsigned int time_delta; /* @@ -571,15 +570,6 @@ static int register_pcc_channel(int pcc_ss_idx) pcc_data[pcc_ss_idx]->pcc_mpar = pcc_chan->max_access_rate; pcc_data[pcc_ss_idx]->pcc_nominal = pcc_chan->latency; - pcc_data[pcc_ss_idx]->pcc_comm_addr = - acpi_os_ioremap(pcc_chan->shmem_base_addr, - pcc_chan->shmem_size); - if (!pcc_data[pcc_ss_idx]->pcc_comm_addr) { - pr_err("Failed to ioremap PCC comm region mem for %d\n", - pcc_ss_idx); - return -ENOMEM; - } - /* Set flag so that we don't come here for each CPU. */ pcc_data[pcc_ss_idx]->pcc_channel_acquired = true; } -- 2.51.0 From e3d7935a6c6138da51626d2b956a079dd0e6671b Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Fri, 11 Apr 2025 17:38:48 +0800 Subject: [PATCH 11/16] ACPI: CPPC: Add IS_OPTIONAL_CPC_REG macro to judge if a cpc_reg is optional In ACPI 6.5, s8.4.6.1 _CPC (Continuous Performance Control), whether each of the per-cpu cpc_regs[] is mandatory or optional is defined. Since the CPC_SUPPORTED() check is only for optional _CPC fields, another macro to check if the field is optional is needed. Reviewed-by: Pierre Gondois Signed-off-by: Lifeng Zheng Reviewed-by: Mario Limonciello Link: https://patch.msgid.link/20250411093855.982491-2-zhenglifeng1@huawei.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index d972157a79b6..34df03755699 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -128,6 +128,20 @@ static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); #define CPC_SUPPORTED(cpc) ((cpc)->type == ACPI_TYPE_INTEGER ? \ !!(cpc)->cpc_entry.int_value : \ !IS_NULL_REG(&(cpc)->cpc_entry.reg)) + +/* + * Each bit indicates the optionality of the register in per-cpu + * cpc_regs[] with the corresponding index. 0 means mandatory and 1 + * means optional. + */ +#define REG_OPTIONAL (0x1FC7D0) + +/* + * Use the index of the register in per-cpu cpc_regs[] to check if + * it's an optional one. + */ +#define IS_OPTIONAL_CPC_REG(reg_idx) (REG_OPTIONAL & (1U << (reg_idx))) + /* * Arbitrary Retries in case the remote processor is slow to respond * to PCC commands. Keeping it high enough to cover emulators where -- 2.51.0 From 45f3763a2122553e548fa0430b77605dc23f00cc Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Fri, 11 Apr 2025 17:38:49 +0800 Subject: [PATCH 12/16] ACPI: CPPC: Optimize cppc_get_perf() Optimize cppc_get_perf() with three changes: 1. Change the error kind to "no such device" when pcc_ss_id < 0, as other register value getting functions. 2. Add a check to ensure the pointer 'perf' is no null. 3. Add a check to verify if the register is supported to be read before using it. The logic is: (1) If the register is of the integer type, check whether the register is optional and its value is 0. If yes, the register is not supported. (2) If the register is of other types, a null one is not supported. 4. Return the result of cpc_read() instead of 0. Reviewed-by: Pierre Gondois Signed-off-by: Lifeng Zheng Reviewed-by: Mario Limonciello Link: https://patch.msgid.link/20250411093855.982491-3-zhenglifeng1@huawei.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 34df03755699..a0774517642f 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1184,6 +1184,9 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); struct cpc_register_resource *reg; + if (perf == NULL) + return -EINVAL; + if (!cpc_desc) { pr_debug("No CPC descriptor for CPU:%d\n", cpunum); return -ENODEV; @@ -1191,20 +1194,29 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) reg = &cpc_desc->cpc_regs[reg_idx]; + if ((reg->type == ACPI_TYPE_INTEGER && IS_OPTIONAL_CPC_REG(reg_idx) && + !reg->cpc_entry.int_value) || (reg->type != ACPI_TYPE_INTEGER && + IS_NULL_REG(®->cpc_entry.reg))) { + pr_debug("CPC register is not supported\n"); + return -EOPNOTSUPP; + } + if (CPC_IN_PCC(reg)) { int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum); struct cppc_pcc_data *pcc_ss_data = NULL; - int ret = 0; + int ret; - if (pcc_ss_id < 0) - return -EIO; + if (pcc_ss_id < 0) { + pr_debug("Invalid pcc_ss_id\n"); + return -ENODEV; + } pcc_ss_data = pcc_data[pcc_ss_id]; down_write(&pcc_ss_data->pcc_lock); if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) - cpc_read(cpunum, reg, perf); + ret = cpc_read(cpunum, reg, perf); else ret = -EIO; @@ -1213,9 +1225,7 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) return ret; } - cpc_read(cpunum, reg, perf); - - return 0; + return cpc_read(cpunum, reg, perf); } /** -- 2.51.0 From 714d103ce868cd0718d0e5262cbc76384a2eb03a Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Fri, 11 Apr 2025 17:38:50 +0800 Subject: [PATCH 13/16] ACPI: CPPC: Rename cppc_get_perf() to cppc_get_reg_val() Rename cppc_get_perf() to cppc_get_reg_val() as a generic function to read CPPC registers. Reviewed-by: Pierre Gondois Signed-off-by: Lifeng Zheng Reviewed-by: Mario Limonciello Link: https://patch.msgid.link/20250411093855.982491-4-zhenglifeng1@huawei.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index a0774517642f..c460272b86c3 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1179,16 +1179,16 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) return ret_val; } -static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) +static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val) { - struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); struct cpc_register_resource *reg; - if (perf == NULL) + if (val == NULL) return -EINVAL; if (!cpc_desc) { - pr_debug("No CPC descriptor for CPU:%d\n", cpunum); + pr_debug("No CPC descriptor for CPU:%d\n", cpu); return -ENODEV; } @@ -1202,7 +1202,7 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) } if (CPC_IN_PCC(reg)) { - int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum); + int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); struct cppc_pcc_data *pcc_ss_data = NULL; int ret; @@ -1216,7 +1216,7 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) down_write(&pcc_ss_data->pcc_lock); if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) - ret = cpc_read(cpunum, reg, perf); + ret = cpc_read(cpu, reg, val); else ret = -EIO; @@ -1225,7 +1225,7 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) return ret; } - return cpc_read(cpunum, reg, perf); + return cpc_read(cpu, reg, val); } /** @@ -1237,7 +1237,7 @@ static int cppc_get_perf(int cpunum, enum cppc_regs reg_idx, u64 *perf) */ int cppc_get_desired_perf(int cpunum, u64 *desired_perf) { - return cppc_get_perf(cpunum, DESIRED_PERF, desired_perf); + return cppc_get_reg_val(cpunum, DESIRED_PERF, desired_perf); } EXPORT_SYMBOL_GPL(cppc_get_desired_perf); @@ -1250,7 +1250,7 @@ EXPORT_SYMBOL_GPL(cppc_get_desired_perf); */ int cppc_get_nominal_perf(int cpunum, u64 *nominal_perf) { - return cppc_get_perf(cpunum, NOMINAL_PERF, nominal_perf); + return cppc_get_reg_val(cpunum, NOMINAL_PERF, nominal_perf); } /** @@ -1262,7 +1262,7 @@ int cppc_get_nominal_perf(int cpunum, u64 *nominal_perf) */ int cppc_get_highest_perf(int cpunum, u64 *highest_perf) { - return cppc_get_perf(cpunum, HIGHEST_PERF, highest_perf); + return cppc_get_reg_val(cpunum, HIGHEST_PERF, highest_perf); } EXPORT_SYMBOL_GPL(cppc_get_highest_perf); @@ -1275,7 +1275,7 @@ EXPORT_SYMBOL_GPL(cppc_get_highest_perf); */ int cppc_get_epp_perf(int cpunum, u64 *epp_perf) { - return cppc_get_perf(cpunum, ENERGY_PERF, epp_perf); + return cppc_get_reg_val(cpunum, ENERGY_PERF, epp_perf); } EXPORT_SYMBOL_GPL(cppc_get_epp_perf); -- 2.51.0 From b5ef45e6a1777fe4e857a1cd48631dc14064eae4 Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Fri, 11 Apr 2025 17:38:51 +0800 Subject: [PATCH 14/16] ACPI: CPPC: Extract cppc_get_reg_val_in_pcc() Extract the operations if register is in pcc out from cppc_get_reg_val() as cppc_get_reg_val_in_pcc(). Reviewed-by: Pierre Gondois Signed-off-by: Lifeng Zheng Reviewed-by: Mario Limonciello Link: https://patch.msgid.link/20250411093855.982491-5-zhenglifeng1@huawei.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 50 ++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index c460272b86c3..14dd206b4454 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1179,6 +1179,31 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val) return ret_val; } +static int cppc_get_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u64 *val) +{ + int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); + struct cppc_pcc_data *pcc_ss_data = NULL; + int ret; + + if (pcc_ss_id < 0) { + pr_debug("Invalid pcc_ss_id\n"); + return -ENODEV; + } + + pcc_ss_data = pcc_data[pcc_ss_id]; + + down_write(&pcc_ss_data->pcc_lock); + + if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) + ret = cpc_read(cpu, reg, val); + else + ret = -EIO; + + up_write(&pcc_ss_data->pcc_lock); + + return ret; +} + static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val) { struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); @@ -1201,29 +1226,8 @@ static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val) return -EOPNOTSUPP; } - if (CPC_IN_PCC(reg)) { - int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); - struct cppc_pcc_data *pcc_ss_data = NULL; - int ret; - - if (pcc_ss_id < 0) { - pr_debug("Invalid pcc_ss_id\n"); - return -ENODEV; - } - - pcc_ss_data = pcc_data[pcc_ss_id]; - - down_write(&pcc_ss_data->pcc_lock); - - if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) - ret = cpc_read(cpu, reg, val); - else - ret = -EIO; - - up_write(&pcc_ss_data->pcc_lock); - - return ret; - } + if (CPC_IN_PCC(reg)) + return cppc_get_reg_val_in_pcc(cpu, reg, val); return cpc_read(cpu, reg, val); } -- 2.51.0 From e05c75072c2eaa6e0b152a558b3be5cbcf79a587 Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Fri, 11 Apr 2025 17:38:52 +0800 Subject: [PATCH 15/16] ACPI: CPPC: Add cppc_set_reg_val() Add cppc_set_reg_val() as a generic function for setting CPPC register values, with this features: 1. Check register. If a register is writeable, it must be a buffer and can not be null. 2. Extract the operations if register is in PCC out as cppc_set_reg_val_in_pcc(). This function can be used to reduce some existing code duplication. Reviewed-by: Pierre Gondois Signed-off-by: Lifeng Zheng Reviewed-by: Mario Limonciello Link: https://patch.msgid.link/20250411093855.982491-6-zhenglifeng1@huawei.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 14dd206b4454..e8236ec0fd0d 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1232,6 +1232,55 @@ static int cppc_get_reg_val(int cpu, enum cppc_regs reg_idx, u64 *val) return cpc_read(cpu, reg, val); } +static int cppc_set_reg_val_in_pcc(int cpu, struct cpc_register_resource *reg, u64 val) +{ + int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); + struct cppc_pcc_data *pcc_ss_data = NULL; + int ret; + + if (pcc_ss_id < 0) { + pr_debug("Invalid pcc_ss_id\n"); + return -ENODEV; + } + + ret = cpc_write(cpu, reg, val); + if (ret) + return ret; + + pcc_ss_data = pcc_data[pcc_ss_id]; + + down_write(&pcc_ss_data->pcc_lock); + /* after writing CPC, transfer the ownership of PCC to platform */ + ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE); + up_write(&pcc_ss_data->pcc_lock); + + return ret; +} + +static int cppc_set_reg_val(int cpu, enum cppc_regs reg_idx, u64 val) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); + struct cpc_register_resource *reg; + + if (!cpc_desc) { + pr_debug("No CPC descriptor for CPU:%d\n", cpu); + return -ENODEV; + } + + reg = &cpc_desc->cpc_regs[reg_idx]; + + /* if a register is writeable, it must be a buffer and not null */ + if ((reg->type != ACPI_TYPE_BUFFER) || IS_NULL_REG(®->cpc_entry.reg)) { + pr_debug("CPC register is not supported\n"); + return -EOPNOTSUPP; + } + + if (CPC_IN_PCC(reg)) + return cppc_set_reg_val_in_pcc(cpu, reg, val); + + return cpc_write(cpu, reg, val); +} + /** * cppc_get_desired_perf - Get the desired performance register value. * @cpunum: CPU from which to get desired performance. -- 2.51.0 From ab482f1bac6b128b8fe910b6663a4f74a3a9796c Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Fri, 11 Apr 2025 17:38:53 +0800 Subject: [PATCH 16/16] ACPI: CPPC: Refactor register value get and set ABIs Refactor register value get and set ABIs by using cppc_get_reg_val(), cppc_set_reg_val() and CPPC_REG_VAL_READ(). Reviewed-by: Pierre Gondois Signed-off-by: Lifeng Zheng Reviewed-by: Mario Limonciello Link: https://patch.msgid.link/20250411093855.982491-7-zhenglifeng1@huawei.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 111 +++------------------------------------ 1 file changed, 7 insertions(+), 104 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index e8236ec0fd0d..62c4673c3ef8 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1608,44 +1608,14 @@ EXPORT_SYMBOL_GPL(cppc_set_epp_perf); */ int cppc_get_auto_sel_caps(int cpunum, struct cppc_perf_caps *perf_caps) { - struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); - struct cpc_register_resource *auto_sel_reg; - u64 auto_sel; - - if (!cpc_desc) { - pr_debug("No CPC descriptor for CPU:%d\n", cpunum); - return -ENODEV; - } - - auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE]; - - if (!CPC_SUPPORTED(auto_sel_reg)) - pr_warn_once("Autonomous mode is not unsupported!\n"); - - if (CPC_IN_PCC(auto_sel_reg)) { - int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum); - struct cppc_pcc_data *pcc_ss_data = NULL; - int ret = 0; - - if (pcc_ss_id < 0) - return -ENODEV; - - pcc_ss_data = pcc_data[pcc_ss_id]; - - down_write(&pcc_ss_data->pcc_lock); - - if (send_pcc_cmd(pcc_ss_id, CMD_READ) >= 0) { - cpc_read(cpunum, auto_sel_reg, &auto_sel); - perf_caps->auto_sel = (bool)auto_sel; - } else { - ret = -EIO; - } - - up_write(&pcc_ss_data->pcc_lock); + u64 auto_sel; + int ret; + ret = cppc_get_reg_val(cpunum, AUTO_SEL_ENABLE, &auto_sel); + if (ret) return ret; - } + perf_caps->auto_sel = (bool)auto_sel; return 0; } EXPORT_SYMBOL_GPL(cppc_get_auto_sel_caps); @@ -1657,43 +1627,7 @@ EXPORT_SYMBOL_GPL(cppc_get_auto_sel_caps); */ int cppc_set_auto_sel(int cpu, bool enable) { - int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); - struct cpc_register_resource *auto_sel_reg; - struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); - struct cppc_pcc_data *pcc_ss_data = NULL; - int ret = -EINVAL; - - if (!cpc_desc) { - pr_debug("No CPC descriptor for CPU:%d\n", cpu); - return -ENODEV; - } - - auto_sel_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE]; - - if (CPC_IN_PCC(auto_sel_reg)) { - if (pcc_ss_id < 0) { - pr_debug("Invalid pcc_ss_id\n"); - return -ENODEV; - } - - if (CPC_SUPPORTED(auto_sel_reg)) { - ret = cpc_write(cpu, auto_sel_reg, enable); - if (ret) - return ret; - } - - pcc_ss_data = pcc_data[pcc_ss_id]; - - down_write(&pcc_ss_data->pcc_lock); - /* after writing CPC, transfer the ownership of PCC to platform */ - ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE); - up_write(&pcc_ss_data->pcc_lock); - } else { - ret = -ENOTSUPP; - pr_debug("_CPC in PCC is not supported\n"); - } - - return ret; + return cppc_set_reg_val(cpu, AUTO_SEL_ENABLE, enable); } EXPORT_SYMBOL_GPL(cppc_set_auto_sel); @@ -1707,38 +1641,7 @@ EXPORT_SYMBOL_GPL(cppc_set_auto_sel); */ int cppc_set_enable(int cpu, bool enable) { - int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); - struct cpc_register_resource *enable_reg; - struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); - struct cppc_pcc_data *pcc_ss_data = NULL; - int ret = -EINVAL; - - if (!cpc_desc) { - pr_debug("No CPC descriptor for CPU:%d\n", cpu); - return -EINVAL; - } - - enable_reg = &cpc_desc->cpc_regs[ENABLE]; - - if (CPC_IN_PCC(enable_reg)) { - - if (pcc_ss_id < 0) - return -EIO; - - ret = cpc_write(cpu, enable_reg, enable); - if (ret) - return ret; - - pcc_ss_data = pcc_data[pcc_ss_id]; - - down_write(&pcc_ss_data->pcc_lock); - /* after writing CPC, transfer the ownership of PCC to platfrom */ - ret = send_pcc_cmd(pcc_ss_id, CMD_WRITE); - up_write(&pcc_ss_data->pcc_lock); - return ret; - } - - return cpc_write(cpu, enable_reg, enable); + return cppc_set_reg_val(cpu, ENABLE, enable); } EXPORT_SYMBOL_GPL(cppc_set_enable); -- 2.51.0