diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-03 20:27:17 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-03 20:27:17 -0400 |
commit | 6aebe7f9e8697531a11b007d1e8126ba1b6e0a53 (patch) | |
tree | 66952e4f08ceb24dad146790800d539fe642e3ce /arch/x86 | |
parent | a6c4e4cd44649b696038326f90161113d8569170 (diff) | |
parent | 6baf3d61821f5b38f27b4e9f044ad4d1e8f3d14f (diff) |
Merge branch 'x86-timers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 timer updates from Ingo Molnar:
"This tree includes a HPET overhead micro-optimization plus new TSC
frequencies for newer Intel CPUs"
* 'x86-timers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
x86/tsc: Add additional Intel CPU models to the crystal quirk list
x86/tsc: Use cpu id defines instead of hex constants
x86/hpet: Reduce HPET counter read contention
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/kernel/hpet.c | 94 | ||||
-rw-r--r-- | arch/x86/kernel/tsc.c | 12 |
2 files changed, 103 insertions, 3 deletions
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index c6dfd801df97..274fab99169d 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c | |||
@@ -756,10 +756,104 @@ static void hpet_reserve_msi_timers(struct hpet_data *hd) | |||
756 | /* | 756 | /* |
757 | * Clock source related code | 757 | * Clock source related code |
758 | */ | 758 | */ |
759 | #if defined(CONFIG_SMP) && defined(CONFIG_64BIT) | ||
760 | /* | ||
761 | * Reading the HPET counter is a very slow operation. If a large number of | ||
762 | * CPUs are trying to access the HPET counter simultaneously, it can cause | ||
763 | * massive delay and slow down system performance dramatically. This may | ||
764 | * happen when HPET is the default clock source instead of TSC. For a | ||
765 | * really large system with hundreds of CPUs, the slowdown may be so | ||
766 | * severe that it may actually crash the system because of a NMI watchdog | ||
767 | * soft lockup, for example. | ||
768 | * | ||
769 | * If multiple CPUs are trying to access the HPET counter at the same time, | ||
770 | * we don't actually need to read the counter multiple times. Instead, the | ||
771 | * other CPUs can use the counter value read by the first CPU in the group. | ||
772 | * | ||
773 | * This special feature is only enabled on x86-64 systems. It is unlikely | ||
774 | * that 32-bit x86 systems will have enough CPUs to require this feature | ||
775 | * with its associated locking overhead. And we also need 64-bit atomic | ||
776 | * read. | ||
777 | * | ||
778 | * The lock and the hpet value are stored together and can be read in a | ||
779 | * single atomic 64-bit read. It is explicitly assumed that arch_spinlock_t | ||
780 | * is 32 bits in size. | ||
781 | */ | ||
782 | union hpet_lock { | ||
783 | struct { | ||
784 | arch_spinlock_t lock; | ||
785 | u32 value; | ||
786 | }; | ||
787 | u64 lockval; | ||
788 | }; | ||
789 | |||
790 | static union hpet_lock hpet __cacheline_aligned = { | ||
791 | { .lock = __ARCH_SPIN_LOCK_UNLOCKED, }, | ||
792 | }; | ||
793 | |||
794 | static cycle_t read_hpet(struct clocksource *cs) | ||
795 | { | ||
796 | unsigned long flags; | ||
797 | union hpet_lock old, new; | ||
798 | |||
799 | BUILD_BUG_ON(sizeof(union hpet_lock) != 8); | ||
800 | |||
801 | /* | ||
802 | * Read HPET directly if in NMI. | ||
803 | */ | ||
804 | if (in_nmi()) | ||
805 | return (cycle_t)hpet_readl(HPET_COUNTER); | ||
806 | |||
807 | /* | ||
808 | * Read the current state of the lock and HPET value atomically. | ||
809 | */ | ||
810 | old.lockval = READ_ONCE(hpet.lockval); | ||
811 | |||
812 | if (arch_spin_is_locked(&old.lock)) | ||
813 | goto contended; | ||
814 | |||
815 | local_irq_save(flags); | ||
816 | if (arch_spin_trylock(&hpet.lock)) { | ||
817 | new.value = hpet_readl(HPET_COUNTER); | ||
818 | /* | ||
819 | * Use WRITE_ONCE() to prevent store tearing. | ||
820 | */ | ||
821 | WRITE_ONCE(hpet.value, new.value); | ||
822 | arch_spin_unlock(&hpet.lock); | ||
823 | local_irq_restore(flags); | ||
824 | return (cycle_t)new.value; | ||
825 | } | ||
826 | local_irq_restore(flags); | ||
827 | |||
828 | contended: | ||
829 | /* | ||
830 | * Contended case | ||
831 | * -------------- | ||
832 | * Wait until the HPET value change or the lock is free to indicate | ||
833 | * its value is up-to-date. | ||
834 | * | ||
835 | * It is possible that old.value has already contained the latest | ||
836 | * HPET value while the lock holder was in the process of releasing | ||
837 | * the lock. Checking for lock state change will enable us to return | ||
838 | * the value immediately instead of waiting for the next HPET reader | ||
839 | * to come along. | ||
840 | */ | ||
841 | do { | ||
842 | cpu_relax(); | ||
843 | new.lockval = READ_ONCE(hpet.lockval); | ||
844 | } while ((new.value == old.value) && arch_spin_is_locked(&new.lock)); | ||
845 | |||
846 | return (cycle_t)new.value; | ||
847 | } | ||
848 | #else | ||
849 | /* | ||
850 | * For UP or 32-bit. | ||
851 | */ | ||
759 | static cycle_t read_hpet(struct clocksource *cs) | 852 | static cycle_t read_hpet(struct clocksource *cs) |
760 | { | 853 | { |
761 | return (cycle_t)hpet_readl(HPET_COUNTER); | 854 | return (cycle_t)hpet_readl(HPET_COUNTER); |
762 | } | 855 | } |
856 | #endif | ||
763 | 857 | ||
764 | static struct clocksource clocksource_hpet = { | 858 | static struct clocksource clocksource_hpet = { |
765 | .name = "hpet", | 859 | .name = "hpet", |
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 78b9cb5a26af..46b2f41f8b05 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <asm/x86_init.h> | 23 | #include <asm/x86_init.h> |
24 | #include <asm/geode.h> | 24 | #include <asm/geode.h> |
25 | #include <asm/apic.h> | 25 | #include <asm/apic.h> |
26 | #include <asm/intel-family.h> | ||
26 | 27 | ||
27 | unsigned int __read_mostly cpu_khz; /* TSC clocks / usec, not used here */ | 28 | unsigned int __read_mostly cpu_khz; /* TSC clocks / usec, not used here */ |
28 | EXPORT_SYMBOL(cpu_khz); | 29 | EXPORT_SYMBOL(cpu_khz); |
@@ -686,11 +687,16 @@ unsigned long native_calibrate_tsc(void) | |||
686 | 687 | ||
687 | if (crystal_khz == 0) { | 688 | if (crystal_khz == 0) { |
688 | switch (boot_cpu_data.x86_model) { | 689 | switch (boot_cpu_data.x86_model) { |
689 | case 0x4E: /* SKL */ | 690 | case INTEL_FAM6_SKYLAKE_MOBILE: |
690 | case 0x5E: /* SKL */ | 691 | case INTEL_FAM6_SKYLAKE_DESKTOP: |
692 | case INTEL_FAM6_KABYLAKE_MOBILE: | ||
693 | case INTEL_FAM6_KABYLAKE_DESKTOP: | ||
691 | crystal_khz = 24000; /* 24.0 MHz */ | 694 | crystal_khz = 24000; /* 24.0 MHz */ |
692 | break; | 695 | break; |
693 | case 0x5C: /* BXT */ | 696 | case INTEL_FAM6_SKYLAKE_X: |
697 | crystal_khz = 25000; /* 25.0 MHz */ | ||
698 | break; | ||
699 | case INTEL_FAM6_ATOM_GOLDMONT: | ||
694 | crystal_khz = 19200; /* 19.2 MHz */ | 700 | crystal_khz = 19200; /* 19.2 MHz */ |
695 | break; | 701 | break; |
696 | } | 702 | } |