Skip to content

Commit 57577c9

Browse files
spandruvadarafaeljw
authored andcommitted
cpufreq: intel_pstate: Process HWP Guaranteed change notification
It is possible that HWP guaranteed ratio is changed in response to change in power and thermal limits. For example when Intel Speed Select performance profile is changed or there is change in TDP, hardware can send notifications. It is possible that the guaranteed ratio is increased. This creates an issue when turbo is disabled, as the old limits set in MSR_HWP_REQUEST are still lower and hardware will clip to older limits. This change enables HWP interrupt and process HWP interrupts. When guaranteed is changed, calls cpufreq_update_policy() so that driver callbacks are called to update to new HWP limits. This callback is called from a delayed workqueue of 10ms to avoid frequent updates. Although the scope of IA32_HWP_INTERRUPT is per logical cpu, on some plaforms interrupt is generated on all CPUs. This is particularly a problem during initialization, when the driver didn't allocated data for other CPUs. So this change uses a cpumask of enabled CPUs and process interrupts on those CPUs only. When the cpufreq offline() or suspend() callback is called, HWP interrupt is disabled on those CPUs and also cancels any pending work item. Spin lock is used to protect data and processing shared with interrupt handler. Here READ_ONCE(), WRITE_ONCE() macros are used to designate shared data, even though spin lock act as an optimization barrier here. Signed-off-by: Srinivas Pandruvada <[email protected]> Tested-by: [email protected] Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 5816b3e commit 57577c9

File tree

1 file changed

+111
-6
lines changed

1 file changed

+111
-6
lines changed

drivers/cpufreq/intel_pstate.c

Lines changed: 111 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <asm/cpu_device_id.h>
3333
#include <asm/cpufeature.h>
3434
#include <asm/intel-family.h>
35+
#include "../drivers/thermal/intel/thermal_interrupt.h"
3536

3637
#define INTEL_PSTATE_SAMPLING_INTERVAL (10 * NSEC_PER_MSEC)
3738

@@ -219,6 +220,7 @@ struct global_params {
219220
* @sched_flags: Store scheduler flags for possible cross CPU update
220221
* @hwp_boost_min: Last HWP boosted min performance
221222
* @suspended: Whether or not the driver has been suspended.
223+
* @hwp_notify_work: workqueue for HWP notifications.
222224
*
223225
* This structure stores per CPU instance data for all CPUs.
224226
*/
@@ -257,6 +259,7 @@ struct cpudata {
257259
unsigned int sched_flags;
258260
u32 hwp_boost_min;
259261
bool suspended;
262+
struct delayed_work hwp_notify_work;
260263
};
261264

262265
static struct cpudata **all_cpu_data;
@@ -985,11 +988,15 @@ static void intel_pstate_hwp_set(unsigned int cpu)
985988
wrmsrl_on_cpu(cpu, MSR_HWP_REQUEST, value);
986989
}
987990

991+
static void intel_pstate_disable_hwp_interrupt(struct cpudata *cpudata);
992+
988993
static void intel_pstate_hwp_offline(struct cpudata *cpu)
989994
{
990995
u64 value = READ_ONCE(cpu->hwp_req_cached);
991996
int min_perf;
992997

998+
intel_pstate_disable_hwp_interrupt(cpu);
999+
9931000
if (boot_cpu_has(X86_FEATURE_HWP_EPP)) {
9941001
/*
9951002
* In case the EPP has been set to "performance" by the
@@ -1053,6 +1060,9 @@ static int intel_pstate_suspend(struct cpufreq_policy *policy)
10531060

10541061
cpu->suspended = true;
10551062

1063+
/* disable HWP interrupt and cancel any pending work */
1064+
intel_pstate_disable_hwp_interrupt(cpu);
1065+
10561066
return 0;
10571067
}
10581068

@@ -1546,15 +1556,105 @@ static void intel_pstate_sysfs_hide_hwp_dynamic_boost(void)
15461556

15471557
/************************** sysfs end ************************/
15481558

1559+
static void intel_pstate_notify_work(struct work_struct *work)
1560+
{
1561+
struct cpudata *cpudata =
1562+
container_of(to_delayed_work(work), struct cpudata, hwp_notify_work);
1563+
1564+
cpufreq_update_policy(cpudata->cpu);
1565+
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_STATUS, 0);
1566+
}
1567+
1568+
static DEFINE_SPINLOCK(hwp_notify_lock);
1569+
static cpumask_t hwp_intr_enable_mask;
1570+
1571+
void notify_hwp_interrupt(void)
1572+
{
1573+
unsigned int this_cpu = smp_processor_id();
1574+
struct cpudata *cpudata;
1575+
unsigned long flags;
1576+
u64 value;
1577+
1578+
if (!READ_ONCE(hwp_active) || !boot_cpu_has(X86_FEATURE_HWP_NOTIFY))
1579+
return;
1580+
1581+
rdmsrl_safe(MSR_HWP_STATUS, &value);
1582+
if (!(value & 0x01))
1583+
return;
1584+
1585+
spin_lock_irqsave(&hwp_notify_lock, flags);
1586+
1587+
if (!cpumask_test_cpu(this_cpu, &hwp_intr_enable_mask))
1588+
goto ack_intr;
1589+
1590+
/*
1591+
* Currently we never free all_cpu_data. And we can't reach here
1592+
* without this allocated. But for safety for future changes, added
1593+
* check.
1594+
*/
1595+
if (unlikely(!READ_ONCE(all_cpu_data)))
1596+
goto ack_intr;
1597+
1598+
/*
1599+
* The free is done during cleanup, when cpufreq registry is failed.
1600+
* We wouldn't be here if it fails on init or switch status. But for
1601+
* future changes, added check.
1602+
*/
1603+
cpudata = READ_ONCE(all_cpu_data[this_cpu]);
1604+
if (unlikely(!cpudata))
1605+
goto ack_intr;
1606+
1607+
schedule_delayed_work(&cpudata->hwp_notify_work, msecs_to_jiffies(10));
1608+
1609+
spin_unlock_irqrestore(&hwp_notify_lock, flags);
1610+
1611+
return;
1612+
1613+
ack_intr:
1614+
wrmsrl_safe(MSR_HWP_STATUS, 0);
1615+
spin_unlock_irqrestore(&hwp_notify_lock, flags);
1616+
}
1617+
1618+
static void intel_pstate_disable_hwp_interrupt(struct cpudata *cpudata)
1619+
{
1620+
unsigned long flags;
1621+
1622+
/* wrmsrl_on_cpu has to be outside spinlock as this can result in IPC */
1623+
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x00);
1624+
1625+
spin_lock_irqsave(&hwp_notify_lock, flags);
1626+
if (cpumask_test_and_clear_cpu(cpudata->cpu, &hwp_intr_enable_mask))
1627+
cancel_delayed_work(&cpudata->hwp_notify_work);
1628+
spin_unlock_irqrestore(&hwp_notify_lock, flags);
1629+
}
1630+
1631+
static void intel_pstate_enable_hwp_interrupt(struct cpudata *cpudata)
1632+
{
1633+
/* Enable HWP notification interrupt for guaranteed performance change */
1634+
if (boot_cpu_has(X86_FEATURE_HWP_NOTIFY)) {
1635+
unsigned long flags;
1636+
1637+
spin_lock_irqsave(&hwp_notify_lock, flags);
1638+
INIT_DELAYED_WORK(&cpudata->hwp_notify_work, intel_pstate_notify_work);
1639+
cpumask_set_cpu(cpudata->cpu, &hwp_intr_enable_mask);
1640+
spin_unlock_irqrestore(&hwp_notify_lock, flags);
1641+
1642+
/* wrmsrl_on_cpu has to be outside spinlock as this can result in IPC */
1643+
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x01);
1644+
}
1645+
}
1646+
15491647
static void intel_pstate_hwp_enable(struct cpudata *cpudata)
15501648
{
1551-
/* First disable HWP notification interrupt as we don't process them */
1649+
/* First disable HWP notification interrupt till we activate again */
15521650
if (boot_cpu_has(X86_FEATURE_HWP_NOTIFY))
15531651
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x00);
15541652

15551653
wrmsrl_on_cpu(cpudata->cpu, MSR_PM_ENABLE, 0x1);
15561654
if (cpudata->epp_default == -EINVAL)
15571655
cpudata->epp_default = intel_pstate_get_epp(cpudata, 0);
1656+
1657+
intel_pstate_enable_hwp_interrupt(cpudata);
15581658
}
15591659

15601660
static int atom_get_min_pstate(void)
@@ -2266,7 +2366,7 @@ static int intel_pstate_init_cpu(unsigned int cpunum)
22662366
if (!cpu)
22672367
return -ENOMEM;
22682368

2269-
all_cpu_data[cpunum] = cpu;
2369+
WRITE_ONCE(all_cpu_data[cpunum], cpu);
22702370

22712371
cpu->cpu = cpunum;
22722372

@@ -2929,8 +3029,10 @@ static void intel_pstate_driver_cleanup(void)
29293029
if (intel_pstate_driver == &intel_pstate)
29303030
intel_pstate_clear_update_util_hook(cpu);
29313031

3032+
spin_lock(&hwp_notify_lock);
29323033
kfree(all_cpu_data[cpu]);
2933-
all_cpu_data[cpu] = NULL;
3034+
WRITE_ONCE(all_cpu_data[cpu], NULL);
3035+
spin_unlock(&hwp_notify_lock);
29343036
}
29353037
}
29363038
cpus_read_unlock();
@@ -3199,6 +3301,7 @@ static bool intel_pstate_hwp_is_enabled(void)
31993301

32003302
static int __init intel_pstate_init(void)
32013303
{
3304+
static struct cpudata **_all_cpu_data;
32023305
const struct x86_cpu_id *id;
32033306
int rc;
32043307

@@ -3224,7 +3327,7 @@ static int __init intel_pstate_init(void)
32243327
* deal with it.
32253328
*/
32263329
if ((!no_hwp && boot_cpu_has(X86_FEATURE_HWP_EPP)) || hwp_forced) {
3227-
hwp_active++;
3330+
WRITE_ONCE(hwp_active, 1);
32283331
hwp_mode_bdw = id->driver_data;
32293332
intel_pstate.attr = hwp_cpufreq_attrs;
32303333
intel_cpufreq.attr = hwp_cpufreq_attrs;
@@ -3275,10 +3378,12 @@ static int __init intel_pstate_init(void)
32753378

32763379
pr_info("Intel P-state driver initializing\n");
32773380

3278-
all_cpu_data = vzalloc(array_size(sizeof(void *), num_possible_cpus()));
3279-
if (!all_cpu_data)
3381+
_all_cpu_data = vzalloc(array_size(sizeof(void *), num_possible_cpus()));
3382+
if (!_all_cpu_data)
32803383
return -ENOMEM;
32813384

3385+
WRITE_ONCE(all_cpu_data, _all_cpu_data);
3386+
32823387
intel_pstate_request_control_from_smm();
32833388

32843389
intel_pstate_sysfs_expose_params();

0 commit comments

Comments
 (0)