diff options
| author | Borislav Petkov <borislav.petkov@amd.com> | 2010-03-31 15:56:42 -0400 |
|---|---|---|
| committer | H. Peter Anvin <hpa@zytor.com> | 2010-04-09 17:05:43 -0400 |
| commit | 73860c6b2fd159a35637e233d735e36887c266ad (patch) | |
| tree | 74c657fbd87569441ef0f0e0683637ff0de7700f | |
| parent | 5958f1d5d722df7a9e5d129676614a8e5219bacd (diff) | |
powernow-k8: Add core performance boost support
Starting with F10h, revE, AMD processors add support for a dynamic
core boosting feature called Core Performance Boost. When a specific
condition is present, a subset of the cores on a system are boosted
beyond their P0 operating frequency to speed up the performance of
single-threaded applications.
In the normal case, the system comes out of reset with core boosting
enabled. This patch adds a sysfs knob with which core boosting can be
switched on or off for benchmarking purposes.
While at it, make the CPB code hotplug-aware so that taking cores
offline wouldn't interfere with boosting the remaining online cores.
Furthermore, add cpu_online_mask hotplug protection as suggested by
Andrew.
Finally, cleanup the driver init codepath and update copyrights.
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
LKML-Reference: <1270065406-1814-3-git-send-email-bp@amd64.org>
Reviewed-by: Thomas Renninger <trenn@suse.de>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
| -rw-r--r-- | arch/x86/kernel/cpu/cpufreq/powernow-k8.c | 161 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/cpufreq/powernow-k8.h | 2 |
2 files changed, 151 insertions, 12 deletions
diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c index d360b56e9825..74ca34b5c003 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | |||
| 2 | /* | 1 | /* |
| 3 | * (c) 2003-2006 Advanced Micro Devices, Inc. | 2 | * (c) 2003-2010 Advanced Micro Devices, Inc. |
| 4 | * Your use of this code is subject to the terms and conditions of the | 3 | * Your use of this code is subject to the terms and conditions of the |
| 5 | * GNU general public license version 2. See "COPYING" or | 4 | * GNU general public license version 2. See "COPYING" or |
| 6 | * http://www.gnu.org/licenses/gpl.html | 5 | * http://www.gnu.org/licenses/gpl.html |
| @@ -54,6 +53,10 @@ static DEFINE_PER_CPU(struct powernow_k8_data *, powernow_data); | |||
| 54 | 53 | ||
| 55 | static int cpu_family = CPU_OPTERON; | 54 | static int cpu_family = CPU_OPTERON; |
| 56 | 55 | ||
| 56 | /* core performance boost */ | ||
| 57 | static bool cpb_capable, cpb_enabled; | ||
| 58 | static struct msr __percpu *msrs; | ||
| 59 | |||
| 57 | #ifndef CONFIG_SMP | 60 | #ifndef CONFIG_SMP |
| 58 | static inline const struct cpumask *cpu_core_mask(int cpu) | 61 | static inline const struct cpumask *cpu_core_mask(int cpu) |
| 59 | { | 62 | { |
| @@ -1393,8 +1396,77 @@ out: | |||
| 1393 | return khz; | 1396 | return khz; |
| 1394 | } | 1397 | } |
| 1395 | 1398 | ||
| 1399 | static void _cpb_toggle_msrs(bool t) | ||
| 1400 | { | ||
| 1401 | int cpu; | ||
| 1402 | |||
| 1403 | get_online_cpus(); | ||
| 1404 | |||
| 1405 | rdmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs); | ||
| 1406 | |||
| 1407 | for_each_cpu(cpu, cpu_online_mask) { | ||
| 1408 | struct msr *reg = per_cpu_ptr(msrs, cpu); | ||
| 1409 | if (t) | ||
| 1410 | reg->l &= ~BIT(25); | ||
| 1411 | else | ||
| 1412 | reg->l |= BIT(25); | ||
| 1413 | } | ||
| 1414 | wrmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs); | ||
| 1415 | |||
| 1416 | put_online_cpus(); | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | /* | ||
| 1420 | * Switch on/off core performance boosting. | ||
| 1421 | * | ||
| 1422 | * 0=disable | ||
| 1423 | * 1=enable. | ||
| 1424 | */ | ||
| 1425 | static void cpb_toggle(bool t) | ||
| 1426 | { | ||
| 1427 | if (!cpb_capable) | ||
| 1428 | return; | ||
| 1429 | |||
| 1430 | if (t && !cpb_enabled) { | ||
| 1431 | cpb_enabled = true; | ||
| 1432 | _cpb_toggle_msrs(t); | ||
| 1433 | printk(KERN_INFO PFX "Core Boosting enabled.\n"); | ||
| 1434 | } else if (!t && cpb_enabled) { | ||
| 1435 | cpb_enabled = false; | ||
| 1436 | _cpb_toggle_msrs(t); | ||
| 1437 | printk(KERN_INFO PFX "Core Boosting disabled.\n"); | ||
| 1438 | } | ||
| 1439 | } | ||
| 1440 | |||
| 1441 | static ssize_t store_cpb(struct cpufreq_policy *policy, const char *buf, | ||
| 1442 | size_t count) | ||
| 1443 | { | ||
| 1444 | int ret = -EINVAL; | ||
| 1445 | unsigned long val = 0; | ||
| 1446 | |||
| 1447 | ret = strict_strtoul(buf, 10, &val); | ||
| 1448 | if (!ret && (val == 0 || val == 1) && cpb_capable) | ||
| 1449 | cpb_toggle(val); | ||
| 1450 | else | ||
| 1451 | return -EINVAL; | ||
| 1452 | |||
| 1453 | return count; | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | static ssize_t show_cpb(struct cpufreq_policy *policy, char *buf) | ||
| 1457 | { | ||
| 1458 | return sprintf(buf, "%u\n", cpb_enabled); | ||
| 1459 | } | ||
| 1460 | |||
| 1461 | #define define_one_rw(_name) \ | ||
| 1462 | static struct freq_attr _name = \ | ||
| 1463 | __ATTR(_name, 0644, show_##_name, store_##_name) | ||
| 1464 | |||
| 1465 | define_one_rw(cpb); | ||
| 1466 | |||
| 1396 | static struct freq_attr *powernow_k8_attr[] = { | 1467 | static struct freq_attr *powernow_k8_attr[] = { |
| 1397 | &cpufreq_freq_attr_scaling_available_freqs, | 1468 | &cpufreq_freq_attr_scaling_available_freqs, |
| 1469 | &cpb, | ||
| 1398 | NULL, | 1470 | NULL, |
| 1399 | }; | 1471 | }; |
| 1400 | 1472 | ||
| @@ -1410,10 +1482,51 @@ static struct cpufreq_driver cpufreq_amd64_driver = { | |||
| 1410 | .attr = powernow_k8_attr, | 1482 | .attr = powernow_k8_attr, |
| 1411 | }; | 1483 | }; |
| 1412 | 1484 | ||
| 1485 | /* | ||
| 1486 | * Clear the boost-disable flag on the CPU_DOWN path so that this cpu | ||
| 1487 | * cannot block the remaining ones from boosting. On the CPU_UP path we | ||
| 1488 | * simply keep the boost-disable flag in sync with the current global | ||
| 1489 | * state. | ||
| 1490 | */ | ||
| 1491 | static int __cpuinit cpb_notify(struct notifier_block *nb, unsigned long action, | ||
| 1492 | void *hcpu) | ||
| 1493 | { | ||
| 1494 | unsigned cpu = (long)hcpu; | ||
| 1495 | u32 lo, hi; | ||
| 1496 | |||
| 1497 | switch (action) { | ||
| 1498 | case CPU_UP_PREPARE: | ||
| 1499 | case CPU_UP_PREPARE_FROZEN: | ||
| 1500 | |||
| 1501 | if (!cpb_enabled) { | ||
| 1502 | rdmsr_on_cpu(cpu, MSR_K7_HWCR, &lo, &hi); | ||
| 1503 | lo |= BIT(25); | ||
| 1504 | wrmsr_on_cpu(cpu, MSR_K7_HWCR, lo, hi); | ||
| 1505 | } | ||
| 1506 | break; | ||
| 1507 | |||
| 1508 | case CPU_DOWN_PREPARE: | ||
| 1509 | case CPU_DOWN_PREPARE_FROZEN: | ||
| 1510 | rdmsr_on_cpu(cpu, MSR_K7_HWCR, &lo, &hi); | ||
| 1511 | lo &= ~BIT(25); | ||
| 1512 | wrmsr_on_cpu(cpu, MSR_K7_HWCR, lo, hi); | ||
| 1513 | break; | ||
| 1514 | |||
| 1515 | default: | ||
| 1516 | break; | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | return NOTIFY_OK; | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | static struct notifier_block __cpuinitdata cpb_nb = { | ||
| 1523 | .notifier_call = cpb_notify, | ||
| 1524 | }; | ||
| 1525 | |||
| 1413 | /* driver entry point for init */ | 1526 | /* driver entry point for init */ |
| 1414 | static int __cpuinit powernowk8_init(void) | 1527 | static int __cpuinit powernowk8_init(void) |
| 1415 | { | 1528 | { |
| 1416 | unsigned int i, supported_cpus = 0; | 1529 | unsigned int i, supported_cpus = 0, cpu; |
| 1417 | 1530 | ||
| 1418 | for_each_online_cpu(i) { | 1531 | for_each_online_cpu(i) { |
| 1419 | int rc; | 1532 | int rc; |
| @@ -1422,15 +1535,36 @@ static int __cpuinit powernowk8_init(void) | |||
| 1422 | supported_cpus++; | 1535 | supported_cpus++; |
| 1423 | } | 1536 | } |
| 1424 | 1537 | ||
| 1425 | if (supported_cpus == num_online_cpus()) { | 1538 | if (supported_cpus != num_online_cpus()) |
| 1426 | printk(KERN_INFO PFX "Found %d %s " | 1539 | return -ENODEV; |
| 1427 | "processors (%d cpu cores) (" VERSION ")\n", | 1540 | |
| 1428 | num_online_nodes(), | 1541 | printk(KERN_INFO PFX "Found %d %s (%d cpu cores) (" VERSION ")\n", |
| 1429 | boot_cpu_data.x86_model_id, supported_cpus); | 1542 | num_online_nodes(), boot_cpu_data.x86_model_id, supported_cpus); |
| 1430 | return cpufreq_register_driver(&cpufreq_amd64_driver); | 1543 | |
| 1544 | if (boot_cpu_has(X86_FEATURE_CPB)) { | ||
| 1545 | |||
| 1546 | cpb_capable = true; | ||
| 1547 | |||
| 1548 | register_cpu_notifier(&cpb_nb); | ||
| 1549 | |||
| 1550 | msrs = msrs_alloc(); | ||
| 1551 | if (!msrs) { | ||
| 1552 | printk(KERN_ERR "%s: Error allocating msrs!\n", __func__); | ||
| 1553 | return -ENOMEM; | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | rdmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs); | ||
| 1557 | |||
| 1558 | for_each_cpu(cpu, cpu_online_mask) { | ||
| 1559 | struct msr *reg = per_cpu_ptr(msrs, cpu); | ||
| 1560 | cpb_enabled |= !(!!(reg->l & BIT(25))); | ||
| 1561 | } | ||
| 1562 | |||
| 1563 | printk(KERN_INFO PFX "Core Performance Boosting: %s.\n", | ||
| 1564 | (cpb_enabled ? "on" : "off")); | ||
| 1431 | } | 1565 | } |
| 1432 | 1566 | ||
| 1433 | return -ENODEV; | 1567 | return cpufreq_register_driver(&cpufreq_amd64_driver); |
| 1434 | } | 1568 | } |
| 1435 | 1569 | ||
| 1436 | /* driver entry point for term */ | 1570 | /* driver entry point for term */ |
| @@ -1438,6 +1572,13 @@ static void __exit powernowk8_exit(void) | |||
| 1438 | { | 1572 | { |
| 1439 | dprintk("exit\n"); | 1573 | dprintk("exit\n"); |
| 1440 | 1574 | ||
| 1575 | if (boot_cpu_has(X86_FEATURE_CPB)) { | ||
| 1576 | msrs_free(msrs); | ||
| 1577 | msrs = NULL; | ||
| 1578 | |||
| 1579 | unregister_cpu_notifier(&cpb_nb); | ||
| 1580 | } | ||
| 1581 | |||
| 1441 | cpufreq_unregister_driver(&cpufreq_amd64_driver); | 1582 | cpufreq_unregister_driver(&cpufreq_amd64_driver); |
| 1442 | } | 1583 | } |
| 1443 | 1584 | ||
diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.h b/arch/x86/kernel/cpu/cpufreq/powernow-k8.h index 02ce824073cb..df3529b1c02d 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.h +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.h | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | * http://www.gnu.org/licenses/gpl.html | 5 | * http://www.gnu.org/licenses/gpl.html |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | |||
| 9 | enum pstate { | 8 | enum pstate { |
| 10 | HW_PSTATE_INVALID = 0xff, | 9 | HW_PSTATE_INVALID = 0xff, |
| 11 | HW_PSTATE_0 = 0, | 10 | HW_PSTATE_0 = 0, |
| @@ -55,7 +54,6 @@ struct powernow_k8_data { | |||
| 55 | struct cpumask *available_cores; | 54 | struct cpumask *available_cores; |
| 56 | }; | 55 | }; |
| 57 | 56 | ||
| 58 | |||
| 59 | /* processor's cpuid instruction support */ | 57 | /* processor's cpuid instruction support */ |
| 60 | #define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */ | 58 | #define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */ |
| 61 | #define CPUID_XFAM 0x0ff00000 /* extended family */ | 59 | #define CPUID_XFAM 0x0ff00000 /* extended family */ |
