Merge branches 'acpi-cppc', 'acpi-pcc', 'acpi-apei' and 'acpi-osi'

Merge new material related to CPPC, PCC, APEI and OSI strings handling
for 6.1-rc1:

 - Disable frequency invariance in the CPPC library if registers used
   by cppc_get_perf_ctrs() are accessed via PCC (Jeremy Linton).

 - Add ACPI disabled check to acpi_cpc_valid() (Perry Yuan).

 - Fix Tx acknowledge in the PCC address space handler (Huisong Li).

 - Use wait_for_completion_timeout() for PCC mailbox operations (Huisong
   Li).

 - Release resources on PCC address space setup failure path (Rafael
   Mendonca).

 - Remove unneeded result variables from APEI code (ye xingchen).

 - Print total number of records found during BERT log parsing (Dmitry
   Monakhov).

 - Drop support for 3 _OSI strings that should not be necessary any
   more and update documentation on custom _OSI strings so that adding
   new ones is not encouraged any more (Mario Limonciello).

* acpi-cppc:
  ACPI: CPPC: Disable FIE if registers in PCC regions
  ACPI: CPPC: Add ACPI disabled check to acpi_cpc_valid()

* acpi-pcc:
  ACPI: PCC: Fix Tx acknowledge in the PCC address space handler
  ACPI: PCC: replace wait_for_completion()
  ACPI: PCC: Release resources on address space setup failure path

* acpi-apei:
  ACPI: APEI: Remove unneeded result variables
  ACPI: APEI: Add BERT error log footer

* acpi-osi:
  ACPI: OSI: Update Documentation on custom _OSI strings
  ACPI: OSI: Remove Linux-HPI-Hybrid-Graphics _OSI string
  ACPI: OSI: Remove Linux-Lenovo-NV-HDMI-Audio _OSI string
  ACPI: OSI: Remove Linux-Dell-Video _OSI string
This commit is contained in:
Rafael J. Wysocki 2022-10-03 19:49:05 +02:00
11 changed files with 115 additions and 57 deletions

View File

@ -41,26 +41,23 @@ But it is likely that they will all eventually be added.
What should an OEM do if they want to support Linux and Windows
using the same BIOS image? Often they need to do something different
for Linux to deal with how Linux is different from Windows.
Here the BIOS should ask exactly what it wants to know:
In this case, the OEM should create custom ASL to be executed by the
Linux kernel and changes to Linux kernel drivers to execute this custom
ASL. The easiest way to accomplish this is to introduce a device specific
method (_DSM) that is called from the Linux kernel.
In the past the kernel used to support something like:
_OSI("Linux-OEM-my_interface_name")
where 'OEM' is needed if this is an OEM-specific hook,
and 'my_interface_name' describes the hook, which could be a
quirk, a bug, or a bug-fix.
In addition, the OEM should send a patch to upstream Linux
via the linux-acpi@vger.kernel.org mailing list. When that patch
is checked into Linux, the OS will answer "YES" when the BIOS
on the OEM's system uses _OSI to ask if the interface is supported
by the OS. Linux distributors can back-port that patch for Linux
pre-installs, and it will be included by all distributions that
re-base to upstream. If the distribution can not update the kernel binary,
they can also add an acpi_osi=Linux-OEM-my_interface_name
cmdline parameter to the boot loader, as needed.
If the string refers to a feature where the upstream kernel
eventually grows support, a patch should be sent to remove
the string when that support is added to the kernel.
However this was discovered to be abused by other BIOS vendors to change
completely unrelated code on completely unrelated systems. This prompted
an evaluation of all of it's uses. This uncovered that they aren't needed
for any of the original reasons. As such, the kernel will not respond to
any custom Linux-* strings by default.
That was easy. Read on, to find out how to do it wrong.

View File

@ -23,6 +23,12 @@
#include <acpi/pcc.h>
/*
* Arbitrary retries in case the remote processor is slow to respond
* to PCC commands
*/
#define PCC_CMD_WAIT_RETRIES_NUM 500
struct pcc_data {
struct pcc_mbox_chan *pcc_chan;
void __iomem *pcc_comm_addr;
@ -63,6 +69,7 @@ acpi_pcc_address_space_setup(acpi_handle region_handle, u32 function,
if (IS_ERR(data->pcc_chan)) {
pr_err("Failed to find PCC channel for subspace %d\n",
ctx->subspace_id);
kfree(data);
return AE_NOT_FOUND;
}
@ -72,6 +79,8 @@ acpi_pcc_address_space_setup(acpi_handle region_handle, u32 function,
if (!data->pcc_comm_addr) {
pr_err("Failed to ioremap PCC comm region mem for %d\n",
ctx->subspace_id);
pcc_mbox_free_channel(data->pcc_chan);
kfree(data);
return AE_NO_MEMORY;
}
@ -86,6 +95,7 @@ acpi_pcc_address_space_handler(u32 function, acpi_physical_address addr,
{
int ret;
struct pcc_data *data = region_context;
u64 usecs_lat;
reinit_completion(&data->done);
@ -96,10 +106,22 @@ acpi_pcc_address_space_handler(u32 function, acpi_physical_address addr,
if (ret < 0)
return AE_ERROR;
if (data->pcc_chan->mchan->mbox->txdone_irq)
wait_for_completion(&data->done);
if (data->pcc_chan->mchan->mbox->txdone_irq) {
/*
* pcc_chan->latency is just a Nominal value. In reality the remote
* processor could be much slower to reply. So add an arbitrary
* amount of wait on top of Nominal.
*/
usecs_lat = PCC_CMD_WAIT_RETRIES_NUM * data->pcc_chan->latency;
ret = wait_for_completion_timeout(&data->done,
usecs_to_jiffies(usecs_lat));
if (ret == 0) {
pr_err("PCC command executed timeout!\n");
return AE_TIME;
}
}
mbox_client_txdone(data->pcc_chan->mchan, ret);
mbox_chan_txdone(data->pcc_chan->mchan, ret);
memcpy_fromio(value, data->pcc_comm_addr, data->ctx.length);

View File

@ -125,12 +125,9 @@ EXPORT_SYMBOL_GPL(apei_exec_write_register);
int apei_exec_write_register_value(struct apei_exec_context *ctx,
struct acpi_whea_header *entry)
{
int rc;
ctx->value = entry->value;
rc = apei_exec_write_register(ctx, entry);
return rc;
return apei_exec_write_register(ctx, entry);
}
EXPORT_SYMBOL_GPL(apei_exec_write_register_value);

View File

@ -90,6 +90,9 @@ static void __init bert_print_all(struct acpi_bert_region *region,
if (skipped)
pr_info(HW_ERR "Skipped %d error records\n", skipped);
if (printed + skipped)
pr_info("Total records found: %d\n", printed + skipped);
}
static int __init setup_bert_disable(char *str)

View File

@ -1020,14 +1020,10 @@ static int reader_pos;
static int erst_open_pstore(struct pstore_info *psi)
{
int rc;
if (erst_disable)
return -ENODEV;
rc = erst_get_record_id_begin(&reader_pos);
return rc;
return erst_get_record_id_begin(&reader_pos);
}
static int erst_close_pstore(struct pstore_info *psi)

View File

@ -424,6 +424,9 @@ bool acpi_cpc_valid(void)
struct cpc_desc *cpc_ptr;
int cpu;
if (acpi_disabled)
return false;
for_each_present_cpu(cpu) {
cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
if (!cpc_ptr)
@ -1240,6 +1243,48 @@ out_err:
}
EXPORT_SYMBOL_GPL(cppc_get_perf_caps);
/**
* cppc_perf_ctrs_in_pcc - Check if any perf counters are in a PCC region.
*
* CPPC has flexibility about how CPU performance counters are accessed.
* One of the choices is PCC regions, which can have a high access latency. This
* routine allows callers of cppc_get_perf_ctrs() to know this ahead of time.
*
* Return: true if any of the counters are in PCC regions, false otherwise
*/
bool cppc_perf_ctrs_in_pcc(void)
{
int cpu;
for_each_present_cpu(cpu) {
struct cpc_register_resource *ref_perf_reg;
struct cpc_desc *cpc_desc;
cpc_desc = per_cpu(cpc_desc_ptr, cpu);
if (CPC_IN_PCC(&cpc_desc->cpc_regs[DELIVERED_CTR]) ||
CPC_IN_PCC(&cpc_desc->cpc_regs[REFERENCE_CTR]) ||
CPC_IN_PCC(&cpc_desc->cpc_regs[CTR_WRAP_TIME]))
return true;
ref_perf_reg = &cpc_desc->cpc_regs[REFERENCE_PERF];
/*
* If reference perf register is not supported then we should
* use the nominal perf value
*/
if (!CPC_SUPPORTED(ref_perf_reg))
ref_perf_reg = &cpc_desc->cpc_regs[NOMINAL_PERF];
if (CPC_IN_PCC(ref_perf_reg))
return true;
}
return false;
}
EXPORT_SYMBOL_GPL(cppc_perf_ctrs_in_pcc);
/**
* cppc_get_perf_ctrs - Read a CPU's performance feedback counters.
* @cpunum: CPU from which to read counters.

View File

@ -44,30 +44,6 @@ osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = {
{"Processor Device", true},
{"3.0 _SCP Extensions", true},
{"Processor Aggregator Device", true},
/*
* Linux-Dell-Video is used by BIOS to disable RTD3 for NVidia graphics
* cards as RTD3 is not supported by drivers now. Systems with NVidia
* cards will hang without RTD3 disabled.
*
* Once NVidia drivers officially support RTD3, this _OSI strings can
* be removed if both new and old graphics cards are supported.
*/
{"Linux-Dell-Video", true},
/*
* Linux-Lenovo-NV-HDMI-Audio is used by BIOS to power on NVidia's HDMI
* audio device which is turned off for power-saving in Windows OS.
* This power management feature observed on some Lenovo Thinkpad
* systems which will not be able to output audio via HDMI without
* a BIOS workaround.
*/
{"Linux-Lenovo-NV-HDMI-Audio", true},
/*
* Linux-HPI-Hybrid-Graphics is used by BIOS to enable dGPU to
* output video directly to external monitors on HP Inc. mobile
* workstations as Nvidia and AMD VGA drivers provide limited
* hybrid graphics supports.
*/
{"Linux-HPI-Hybrid-Graphics", true},
};
static u32 acpi_osi_handler(acpi_string interface, u32 supported)

View File

@ -353,7 +353,7 @@ void topology_init_cpu_capacity_cppc(void)
struct cppc_perf_caps perf_caps;
int cpu;
if (likely(acpi_disabled || !acpi_cpc_valid()))
if (likely(!acpi_cpc_valid()))
return;
raw_capacity = kcalloc(num_possible_cpus(), sizeof(*raw_capacity),

View File

@ -673,7 +673,7 @@ static int __init amd_pstate_init(void)
return -ENODEV;
if (!acpi_cpc_valid()) {
pr_debug("the _CPC object is not present in SBIOS\n");
pr_warn_once("the _CPC object is not present in SBIOS or ACPI disabled\n");
return -ENODEV;
}

View File

@ -63,7 +63,15 @@ static struct cppc_workaround_oem_info wa_info[] = {
static struct cpufreq_driver cppc_cpufreq_driver;
static enum {
FIE_UNSET = -1,
FIE_ENABLED,
FIE_DISABLED
} fie_disabled = FIE_UNSET;
#ifdef CONFIG_ACPI_CPPC_CPUFREQ_FIE
module_param(fie_disabled, int, 0444);
MODULE_PARM_DESC(fie_disabled, "Disable Frequency Invariance Engine (FIE)");
/* Frequency invariance support */
struct cppc_freq_invariance {
@ -158,7 +166,7 @@ static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
struct cppc_freq_invariance *cppc_fi;
int cpu, ret;
if (cppc_cpufreq_driver.get == hisi_cppc_cpufreq_get_rate)
if (fie_disabled)
return;
for_each_cpu(cpu, policy->cpus) {
@ -199,7 +207,7 @@ static void cppc_cpufreq_cpu_fie_exit(struct cpufreq_policy *policy)
struct cppc_freq_invariance *cppc_fi;
int cpu;
if (cppc_cpufreq_driver.get == hisi_cppc_cpufreq_get_rate)
if (fie_disabled)
return;
/* policy->cpus will be empty here, use related_cpus instead */
@ -229,7 +237,15 @@ static void __init cppc_freq_invariance_init(void)
};
int ret;
if (cppc_cpufreq_driver.get == hisi_cppc_cpufreq_get_rate)
if (fie_disabled != FIE_ENABLED && fie_disabled != FIE_DISABLED) {
fie_disabled = FIE_ENABLED;
if (cppc_perf_ctrs_in_pcc()) {
pr_info("FIE not enabled on systems with registers in PCC\n");
fie_disabled = FIE_DISABLED;
}
}
if (fie_disabled)
return;
kworker_fie = kthread_create_worker(0, "cppc_fie");
@ -247,7 +263,7 @@ static void __init cppc_freq_invariance_init(void)
static void cppc_freq_invariance_exit(void)
{
if (cppc_cpufreq_driver.get == hisi_cppc_cpufreq_get_rate)
if (fie_disabled)
return;
kthread_destroy_worker(kworker_fie);
@ -936,6 +952,7 @@ static void cppc_check_hisi_workaround(void)
wa_info[i].oem_revision == tbl->oem_revision) {
/* Overwrite the get() callback */
cppc_cpufreq_driver.get = hisi_cppc_cpufreq_get_rate;
fie_disabled = FIE_DISABLED;
break;
}
}
@ -947,7 +964,7 @@ static int __init cppc_cpufreq_init(void)
{
int ret;
if ((acpi_disabled) || !acpi_cpc_valid())
if (!acpi_cpc_valid())
return -ENODEV;
cppc_check_hisi_workaround();

View File

@ -140,6 +140,7 @@ extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs);
extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls);
extern int cppc_set_enable(int cpu, bool enable);
extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps);
extern bool cppc_perf_ctrs_in_pcc(void);
extern bool acpi_cpc_valid(void);
extern bool cppc_allow_fast_switch(void);
extern int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data);
@ -173,6 +174,10 @@ static inline int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps)
{
return -ENOTSUPP;
}
static inline bool cppc_perf_ctrs_in_pcc(void)
{
return false;
}
static inline bool acpi_cpc_valid(void)
{
return false;