From 36b9017f0250a5299bb715b3b8c41b5e2b05b320 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:41 +0200 Subject: [PATCH 01/29] x86/hpet: Simplify CPU online code The indirection via work scheduled on the upcoming CPU was necessary with the old hotplug code because the online callback was invoked on the control CPU not on the upcoming CPU. The rework of the CPU hotplug core guarantees that the online callbacks are invoked on the upcoming CPU. Remove the now pointless work redirection. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132434.047254075@linutronix.de --- arch/x86/kernel/hpet.c | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index a0573f2e7763..a6aa22677768 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -547,12 +547,10 @@ static int hpet_setup_irq(struct hpet_dev *dev) return 0; } -/* This should be called in specific @cpu */ static void init_one_hpet_msi_clockevent(struct hpet_dev *hdev, int cpu) { struct clock_event_device *evt = &hdev->evt; - WARN_ON(cpu != smp_processor_id()); if (!(hdev->flags & HPET_DEV_VALID)) return; @@ -684,36 +682,12 @@ static struct hpet_dev *hpet_get_unused_timer(void) return NULL; } -struct hpet_work_struct { - struct delayed_work work; - struct completion complete; -}; - -static void hpet_work(struct work_struct *w) -{ - struct hpet_dev *hdev; - int cpu = smp_processor_id(); - struct hpet_work_struct *hpet_work; - - hpet_work = container_of(w, struct hpet_work_struct, work.work); - - hdev = hpet_get_unused_timer(); - if (hdev) - init_one_hpet_msi_clockevent(hdev, cpu); - - complete(&hpet_work->complete); -} - static int hpet_cpuhp_online(unsigned int cpu) { - struct hpet_work_struct work; + struct hpet_dev *hdev = hpet_get_unused_timer(); - INIT_DELAYED_WORK_ONSTACK(&work.work, hpet_work); - init_completion(&work.complete); - /* FIXME: add schedule_work_on() */ - schedule_delayed_work_on(cpu, &work.work, 0); - wait_for_completion(&work.complete); - destroy_delayed_work_on_stack(&work.work); + if (hdev) + init_one_hpet_msi_clockevent(hdev, cpu); return 0; } @@ -1045,7 +1019,6 @@ static __init int hpet_late_init(void) if (boot_cpu_has(X86_FEATURE_ARAT)) return 0; - /* This notifier should be called after workqueue is ready */ ret = cpuhp_setup_state(CPUHP_AP_X86_HPET_ONLINE, "x86/hpet:online", hpet_cpuhp_online, NULL); if (ret) From 46e5b64fdeb49e6f95b875fa4702cedf6c37188d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:42 +0200 Subject: [PATCH 02/29] x86/hpet: Replace printk(KERN...) with pr_...() And sanitize the format strings while at it. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132434.140411339@linutronix.de --- arch/x86/kernel/hpet.c | 45 ++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index a6aa22677768..cf3dbf43e548 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -20,6 +20,9 @@ #include #include +#undef pr_fmt +#define pr_fmt(fmt) "hpet: " fmt + #define HPET_MASK CLOCKSOURCE_MASK(32) #define HPET_DEV_USED_BIT 2 @@ -137,31 +140,28 @@ EXPORT_SYMBOL_GPL(is_hpet_enabled); static void _hpet_print_config(const char *function, int line) { u32 i, timers, l, h; - printk(KERN_INFO "hpet: %s(%d):\n", function, line); + pr_info("%s(%d):\n", function, line); l = hpet_readl(HPET_ID); h = hpet_readl(HPET_PERIOD); timers = ((l & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; - printk(KERN_INFO "hpet: ID: 0x%x, PERIOD: 0x%x\n", l, h); + pr_info("ID: 0x%x, PERIOD: 0x%x\n", l, h); l = hpet_readl(HPET_CFG); h = hpet_readl(HPET_STATUS); - printk(KERN_INFO "hpet: CFG: 0x%x, STATUS: 0x%x\n", l, h); + pr_info("CFG: 0x%x, STATUS: 0x%x\n", l, h); l = hpet_readl(HPET_COUNTER); h = hpet_readl(HPET_COUNTER+4); - printk(KERN_INFO "hpet: COUNTER_l: 0x%x, COUNTER_h: 0x%x\n", l, h); + pr_info("COUNTER_l: 0x%x, COUNTER_h: 0x%x\n", l, h); for (i = 0; i < timers; i++) { l = hpet_readl(HPET_Tn_CFG(i)); h = hpet_readl(HPET_Tn_CFG(i)+4); - printk(KERN_INFO "hpet: T%d: CFG_l: 0x%x, CFG_h: 0x%x\n", - i, l, h); + pr_info("T%d: CFG_l: 0x%x, CFG_h: 0x%x\n", i, l, h); l = hpet_readl(HPET_Tn_CMP(i)); h = hpet_readl(HPET_Tn_CMP(i)+4); - printk(KERN_INFO "hpet: T%d: CMP_l: 0x%x, CMP_h: 0x%x\n", - i, l, h); + pr_info("T%d: CMP_l: 0x%x, CMP_h: 0x%x\n", i, l, h); l = hpet_readl(HPET_Tn_ROUTE(i)); h = hpet_readl(HPET_Tn_ROUTE(i)+4); - printk(KERN_INFO "hpet: T%d ROUTE_l: 0x%x, ROUTE_h: 0x%x\n", - i, l, h); + pr_info("T%d ROUTE_l: 0x%x, ROUTE_h: 0x%x\n", i, l, h); } } @@ -287,7 +287,7 @@ static void hpet_legacy_clockevent_register(void) clockevents_config_and_register(&hpet_clockevent, hpet_freq, HPET_MIN_PROG_DELTA, 0x7FFFFFFF); global_clock_event = &hpet_clockevent; - printk(KERN_DEBUG "hpet clockevent registered\n"); + pr_debug("Clockevent registered\n"); } static int hpet_set_periodic(struct clock_event_device *evt, int timer) @@ -520,8 +520,7 @@ static irqreturn_t hpet_interrupt_handler(int irq, void *data) struct clock_event_device *hevt = &dev->evt; if (!hevt->event_handler) { - printk(KERN_INFO "Spurious HPET timer interrupt on HPET timer %d\n", - dev->num); + pr_info("Spurious interrupt HPET timer %d\n", dev->num); return IRQ_HANDLED; } @@ -541,8 +540,7 @@ static int hpet_setup_irq(struct hpet_dev *dev) irq_set_affinity(dev->irq, cpumask_of(dev->cpu)); enable_irq(dev->irq); - printk(KERN_DEBUG "hpet: %s irq %d for MSI\n", - dev->name, dev->irq); + pr_debug("%s irq %d for MSI\n", dev->name, dev->irq); return 0; } @@ -638,7 +636,7 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) break; } - printk(KERN_INFO "HPET: %d timers in total, %d timers will be used for per-cpu timer\n", + pr_info("%d channels of %d reserved for per-cpu timers\n", num_timers, num_timers_used); } @@ -856,8 +854,7 @@ static int hpet_clocksource_register(void) } while ((now - start) < 200000UL); if (t1 == hpet_readl(HPET_COUNTER)) { - printk(KERN_WARNING - "HPET counter not counting. HPET disabled\n"); + pr_warn("Counter not counting. HPET disabled\n"); return -ENODEV; } @@ -903,9 +900,7 @@ int __init hpet_enable(void) */ for (i = 0; hpet_readl(HPET_CFG) == 0xFFFFFFFF; i++) { if (i == 1000) { - printk(KERN_WARNING - "HPET config register value = 0xFFFFFFFF. " - "Disabling HPET\n"); + pr_warn("Config register invalid. Disabling HPET\n"); goto out_nohpet; } } @@ -949,7 +944,7 @@ int __init hpet_enable(void) cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY); hpet_writel(cfg, HPET_CFG); if (cfg) - pr_warn("Unrecognized bits %#x set in global cfg\n", cfg); + pr_warn("Global config: Unknown bits %#x\n", cfg); for (i = 0; i <= last; ++i) { cfg = hpet_readl(HPET_Tn_CFG(i)); @@ -961,8 +956,7 @@ int __init hpet_enable(void) | HPET_TN_64BIT_CAP | HPET_TN_32BIT | HPET_TN_ROUTE | HPET_TN_FSB | HPET_TN_FSB_CAP); if (cfg) - pr_warn("Unrecognized bits %#x set in cfg#%u\n", - cfg, i); + pr_warn("Channel #%u config: Unknown bits %#x\n", i, cfg); } hpet_print_config(); @@ -1290,8 +1284,7 @@ static void hpet_rtc_timer_reinit(void) if (hpet_rtc_flags & RTC_PIE) hpet_pie_count += lost_ints; if (printk_ratelimit()) - printk(KERN_WARNING "hpet1: lost %d rtc interrupts\n", - lost_ints); + pr_warn("Lost %d RTC interrupts\n", lost_ints); } } From 9b0b28de837a3a59b409613d15e90d5569938945 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:43 +0200 Subject: [PATCH 03/29] x86/hpet: Restructure init code As a preparatory change for further consolidation, restructure the HPET init code so it becomes more readable. Fix up misleading and stale comments and rename variables so they actually make sense. No intended functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132434.247842972@linutronix.de --- arch/x86/kernel/hpet.c | 81 ++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index cf3dbf43e548..daa97e14296b 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -45,6 +45,7 @@ bool hpet_msi_disable; static unsigned int hpet_num_timers; #endif static void __iomem *hpet_virt_address; +static u32 *hpet_boot_cfg; struct hpet_dev { struct clock_event_device evt; @@ -862,7 +863,34 @@ static int hpet_clocksource_register(void) return 0; } -static u32 *hpet_boot_cfg; +/* + * AMD SB700 based systems with spread spectrum enabled use a SMM based + * HPET emulation to provide proper frequency setting. + * + * On such systems the SMM code is initialized with the first HPET register + * access and takes some time to complete. During this time the config + * register reads 0xffffffff. We check for max 1000 loops whether the + * config register reads a non-0xffffffff value to make sure that the + * HPET is up and running before we proceed any further. + * + * A counting loop is safe, as the HPET access takes thousands of CPU cycles. + * + * On non-SB700 based machines this check is only done once and has no + * side effects. + */ +static bool __init hpet_cfg_working(void) +{ + int i; + + for (i = 0; i < 1000; i++) { + if (hpet_readl(HPET_CFG) != 0xFFFFFFFF) + return true; + } + + pr_warn("Config register invalid. Disabling HPET\n"); + return false; +} + /** * hpet_enable - Try to setup the HPET timer. Returns 1 on success. @@ -870,8 +898,8 @@ static u32 *hpet_boot_cfg; int __init hpet_enable(void) { u32 hpet_period, cfg, id; + unsigned int i, channels; u64 freq; - unsigned int i, last; if (!is_hpet_capable()) return 0; @@ -880,38 +908,18 @@ int __init hpet_enable(void) if (!hpet_virt_address) return 0; + /* Validate that the config register is working */ + if (!hpet_cfg_working()) + goto out_nohpet; + /* * Read the period and check for a sane value: */ hpet_period = hpet_readl(HPET_PERIOD); - - /* - * AMD SB700 based systems with spread spectrum enabled use a - * SMM based HPET emulation to provide proper frequency - * setting. The SMM code is initialized with the first HPET - * register access and takes some time to complete. During - * this time the config register reads 0xffffffff. We check - * for max. 1000 loops whether the config register reads a non - * 0xffffffff value to make sure that HPET is up and running - * before we go further. A counting loop is safe, as the HPET - * access takes thousands of CPU cycles. On non SB700 based - * machines this check is only done once and has no side - * effects. - */ - for (i = 0; hpet_readl(HPET_CFG) == 0xFFFFFFFF; i++) { - if (i == 1000) { - pr_warn("Config register invalid. Disabling HPET\n"); - goto out_nohpet; - } - } - if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD) goto out_nohpet; - /* - * The period is a femto seconds value. Convert it to a - * frequency. - */ + /* The period is a femtoseconds value. Convert it to a frequency. */ freq = FSEC_PER_SEC; do_div(freq, hpet_period); hpet_freq = freq; @@ -923,19 +931,21 @@ int __init hpet_enable(void) id = hpet_readl(HPET_ID); hpet_print_config(); - last = (id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT; + /* This is the HPET channel number which is zero based */ + channels = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; #ifdef CONFIG_HPET_EMULATE_RTC /* * The legacy routing mode needs at least two channels, tick timer * and the rtc emulation channel. */ - if (!last) + if (channels < 2) goto out_nohpet; #endif cfg = hpet_readl(HPET_CFG); - hpet_boot_cfg = kmalloc_array(last + 2, sizeof(*hpet_boot_cfg), + /* Allocate entries for the global and the channel configurations */ + hpet_boot_cfg = kmalloc_array(channels + 1, sizeof(*hpet_boot_cfg), GFP_KERNEL); if (hpet_boot_cfg) *hpet_boot_cfg = cfg; @@ -946,7 +956,7 @@ int __init hpet_enable(void) if (cfg) pr_warn("Global config: Unknown bits %#x\n", cfg); - for (i = 0; i <= last; ++i) { + for (i = 0; i < channels; ++i) { cfg = hpet_readl(HPET_Tn_CFG(i)); if (hpet_boot_cfg) hpet_boot_cfg[i + 1] = cfg; @@ -976,18 +986,13 @@ out_nohpet: } /* - * Needs to be late, as the reserve_timer code calls kalloc ! - * - * Not a problem on i386 as hpet_enable is called from late_time_init, - * but on x86_64 it is necessary ! + * The late initialization runs after the PCI quirks have been invoked + * which might have detected a system on which the HPET can be enforced. */ static __init int hpet_late_init(void) { int ret; - if (boot_hpet_disable) - return -ENODEV; - if (!hpet_address) { if (!force_hpet_address) return -ENODEV; From 7c4b0e0898ebff4d4821d5dd7a564903a1e88821 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:44 +0200 Subject: [PATCH 04/29] x86/hpet: Remove pointless x86-64 specific #include Nothing requires asm/pgtable.h here anymore. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132434.339011567@linutronix.de --- arch/x86/kernel/hpet.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index daa97e14296b..76d63ed62ce8 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -71,10 +71,6 @@ static inline void hpet_writel(unsigned int d, unsigned int a) writel(d, hpet_virt_address + a); } -#ifdef CONFIG_X86_64 -#include -#endif - static inline void hpet_set_mapping(void) { hpet_virt_address = ioremap_nocache(hpet_address, HPET_MMAP_SIZE); From 853acaf064acf3aad6189b36de814bd381d35133 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:45 +0200 Subject: [PATCH 05/29] x86/hpet: Remove unused parameter from hpet_next_event() The clockevent device pointer is not used in this function. While at it, rename the misnamed 'timer' parameter to 'channel', which makes it clear what this parameter means. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132434.447880978@linutronix.de --- arch/x86/kernel/hpet.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 76d63ed62ce8..b2ec52a7773d 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -347,15 +347,14 @@ static int hpet_resume(struct clock_event_device *evt) return 0; } -static int hpet_next_event(unsigned long delta, - struct clock_event_device *evt, int timer) +static int hpet_next_event(unsigned long delta, int channel) { u32 cnt; s32 res; cnt = hpet_readl(HPET_COUNTER); cnt += (u32) delta; - hpet_writel(cnt, HPET_Tn_CMP(timer)); + hpet_writel(cnt, HPET_Tn_CMP(channel)); /* * HPETs are a complete disaster. The compare register is @@ -407,7 +406,7 @@ static int hpet_legacy_resume(struct clock_event_device *evt) static int hpet_legacy_next_event(unsigned long delta, struct clock_event_device *evt) { - return hpet_next_event(delta, evt, 0); + return hpet_next_event(delta, 0); } /* @@ -508,7 +507,8 @@ static int hpet_msi_next_event(unsigned long delta, struct clock_event_device *evt) { struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); - return hpet_next_event(delta, evt, hdev->num); + + return hpet_next_event(delta, hdev->num); } static irqreturn_t hpet_interrupt_handler(int irq, void *data) From eb8ec32c45a87efbc6683b771597084c4d904a17 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:46 +0200 Subject: [PATCH 06/29] x86/hpet: Remove the unused hpet_msi_read() function No users. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132434.553729327@linutronix.de --- arch/x86/include/asm/hpet.h | 1 - arch/x86/kernel/hpet.c | 7 ------- 2 files changed, 8 deletions(-) diff --git a/arch/x86/include/asm/hpet.h b/arch/x86/include/asm/hpet.h index 67385d56d4f4..e3209f5de65d 100644 --- a/arch/x86/include/asm/hpet.h +++ b/arch/x86/include/asm/hpet.h @@ -81,7 +81,6 @@ struct irq_domain; extern void hpet_msi_unmask(struct irq_data *data); extern void hpet_msi_mask(struct irq_data *data); extern void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg); -extern void hpet_msi_read(struct hpet_dev *hdev, struct msi_msg *msg); extern struct irq_domain *hpet_create_irq_domain(int hpet_id); extern int hpet_assign_irq(struct irq_domain *domain, struct hpet_dev *dev, int dev_num); diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index b2ec52a7773d..69cd0829f432 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -462,13 +462,6 @@ void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg) hpet_writel(msg->address_lo, HPET_Tn_ROUTE(hdev->num) + 4); } -void hpet_msi_read(struct hpet_dev *hdev, struct msi_msg *msg) -{ - msg->data = hpet_readl(HPET_Tn_ROUTE(hdev->num)); - msg->address_lo = hpet_readl(HPET_Tn_ROUTE(hdev->num) + 4); - msg->address_hi = 0; -} - static int hpet_msi_shutdown(struct clock_event_device *evt) { struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); From 433526cc0502ff13d9b2fd63ba546a202dac0463 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:47 +0200 Subject: [PATCH 07/29] x86/hpet: Mark init functions __init They are only called from init code. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132434.645357869@linutronix.de --- arch/x86/kernel/hpet.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 69cd0829f432..638aaff39819 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -176,7 +176,7 @@ do { \ static void hpet_reserve_msi_timers(struct hpet_data *hd); -static void hpet_reserve_platform_timers(unsigned int id) +static void __init hpet_reserve_platform_timers(unsigned int id) { struct hpet __iomem *hpet = hpet_virt_address; struct hpet_timer __iomem *timer = &hpet->hpet_timers[2]; @@ -572,7 +572,7 @@ static void init_one_hpet_msi_clockevent(struct hpet_dev *hdev, int cpu) #define RESERVE_TIMERS 0 #endif -static void hpet_msi_capability_lookup(unsigned int start_timer) +static void __init hpet_msi_capability_lookup(unsigned int start_timer) { unsigned int id; unsigned int num_timers; @@ -631,7 +631,7 @@ static void hpet_msi_capability_lookup(unsigned int start_timer) } #ifdef CONFIG_HPET -static void hpet_reserve_msi_timers(struct hpet_data *hd) +static void __init hpet_reserve_msi_timers(struct hpet_data *hd) { int i; From 4ce78e2094fc2736f8ecd04ec85e5566acaed516 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:48 +0200 Subject: [PATCH 08/29] x86/hpet: Sanitize stub functions Mark them inline and remove the pointless 'return;' statement. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132434.754768274@linutronix.de --- arch/x86/kernel/hpet.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 638aaff39819..cb120e412dc6 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -692,16 +692,10 @@ static int hpet_cpuhp_dead(unsigned int cpu) } #else -static void hpet_msi_capability_lookup(unsigned int start_timer) -{ - return; -} +static inline void hpet_msi_capability_lookup(unsigned int start_timer) { } #ifdef CONFIG_HPET -static void hpet_reserve_msi_timers(struct hpet_data *hd) -{ - return; -} +static inline void hpet_reserve_msi_timers(struct hpet_data *hd) { } #endif #define hpet_cpuhp_online NULL @@ -820,7 +814,7 @@ static struct clocksource clocksource_hpet = { .resume = hpet_resume_counter, }; -static int hpet_clocksource_register(void) +static int __init hpet_clocksource_register(void) { u64 start, now; u64 t1; From 8c273f2c81f0756f65b24771196c0eff7ac90e7b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:49 +0200 Subject: [PATCH 09/29] x86/hpet: Move static and global variables to one place Having static and global variables sprinkled all over the code is just annoying to read. Move them all to the top of the file. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132434.860549134@linutronix.de --- arch/x86/kernel/hpet.c | 50 +++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index cb120e412dc6..d6bd0ed6885b 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -23,6 +23,15 @@ #undef pr_fmt #define pr_fmt(fmt) "hpet: " fmt +struct hpet_dev { + struct clock_event_device evt; + unsigned int num; + int cpu; + unsigned int irq; + unsigned int flags; + char name[10]; +}; + #define HPET_MASK CLOCKSOURCE_MASK(32) #define HPET_DEV_USED_BIT 2 @@ -43,18 +52,22 @@ bool hpet_msi_disable; #ifdef CONFIG_PCI_MSI static unsigned int hpet_num_timers; +static struct hpet_dev *hpet_devs; +static DEFINE_PER_CPU(struct hpet_dev *, cpu_hpet_dev); +static struct irq_domain *hpet_domain; #endif + static void __iomem *hpet_virt_address; static u32 *hpet_boot_cfg; -struct hpet_dev { - struct clock_event_device evt; - unsigned int num; - int cpu; - unsigned int irq; - unsigned int flags; - char name[10]; -}; +static bool hpet_legacy_int_enabled; +static unsigned long hpet_freq; + +bool boot_hpet_disable; +bool hpet_force_user; +static bool hpet_verbose; + +static struct clock_event_device hpet_clockevent; static inline struct hpet_dev *EVT_TO_HPET_DEV(struct clock_event_device *evtdev) { @@ -85,10 +98,6 @@ static inline void hpet_clear_mapping(void) /* * HPET command line enable / disable */ -bool boot_hpet_disable; -bool hpet_force_user; -static bool hpet_verbose; - static int __init hpet_setup(char *str) { while (str) { @@ -120,11 +129,6 @@ static inline int is_hpet_capable(void) return !boot_hpet_disable && hpet_address; } -/* - * HPET timer interrupt enable / disable - */ -static bool hpet_legacy_int_enabled; - /** * is_hpet_enabled - check whether the hpet timer interrupt is enabled */ @@ -217,13 +221,7 @@ static void __init hpet_reserve_platform_timers(unsigned int id) static void hpet_reserve_platform_timers(unsigned int id) { } #endif -/* - * Common hpet info - */ -static unsigned long hpet_freq; - -static struct clock_event_device hpet_clockevent; - +/* Common hpet functions */ static void hpet_stop_counter(void) { u32 cfg = hpet_readl(HPET_CFG); @@ -430,10 +428,6 @@ static struct clock_event_device hpet_clockevent = { */ #ifdef CONFIG_PCI_MSI -static DEFINE_PER_CPU(struct hpet_dev *, cpu_hpet_dev); -static struct hpet_dev *hpet_devs; -static struct irq_domain *hpet_domain; - void hpet_msi_unmask(struct irq_data *data) { struct hpet_dev *hdev = irq_data_get_irq_handler_data(data); From 6bdec41a0cbcbda35c9044915fc8f45503a595a0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:50 +0200 Subject: [PATCH 10/29] x86/hpet: Shuffle code around for readability sake It doesn't make sense to have init functions in the middle of other code. Aside of that, further changes in that area create horrible diffs if the code stays where it is. No functional change Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132434.951733064@linutronix.de --- arch/x86/kernel/hpet.c | 81 +++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index d6bd0ed6885b..71533f53fa1d 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -559,6 +559,47 @@ static void init_one_hpet_msi_clockevent(struct hpet_dev *hdev, int cpu) 0x7FFFFFFF); } +static struct hpet_dev *hpet_get_unused_timer(void) +{ + int i; + + if (!hpet_devs) + return NULL; + + for (i = 0; i < hpet_num_timers; i++) { + struct hpet_dev *hdev = &hpet_devs[i]; + + if (!(hdev->flags & HPET_DEV_VALID)) + continue; + if (test_and_set_bit(HPET_DEV_USED_BIT, + (unsigned long *)&hdev->flags)) + continue; + return hdev; + } + return NULL; +} + +static int hpet_cpuhp_online(unsigned int cpu) +{ + struct hpet_dev *hdev = hpet_get_unused_timer(); + + if (hdev) + init_one_hpet_msi_clockevent(hdev, cpu); + return 0; +} + +static int hpet_cpuhp_dead(unsigned int cpu) +{ + struct hpet_dev *hdev = per_cpu(cpu_hpet_dev, cpu); + + if (!hdev) + return 0; + free_irq(hdev->irq, hdev); + hdev->flags &= ~HPET_DEV_USED; + per_cpu(cpu_hpet_dev, cpu) = NULL; + return 0; +} + #ifdef CONFIG_HPET /* Reserve at least one timer for userspace (/dev/hpet) */ #define RESERVE_TIMERS 1 @@ -644,46 +685,6 @@ static void __init hpet_reserve_msi_timers(struct hpet_data *hd) } #endif -static struct hpet_dev *hpet_get_unused_timer(void) -{ - int i; - - if (!hpet_devs) - return NULL; - - for (i = 0; i < hpet_num_timers; i++) { - struct hpet_dev *hdev = &hpet_devs[i]; - - if (!(hdev->flags & HPET_DEV_VALID)) - continue; - if (test_and_set_bit(HPET_DEV_USED_BIT, - (unsigned long *)&hdev->flags)) - continue; - return hdev; - } - return NULL; -} - -static int hpet_cpuhp_online(unsigned int cpu) -{ - struct hpet_dev *hdev = hpet_get_unused_timer(); - - if (hdev) - init_one_hpet_msi_clockevent(hdev, cpu); - return 0; -} - -static int hpet_cpuhp_dead(unsigned int cpu) -{ - struct hpet_dev *hdev = per_cpu(cpu_hpet_dev, cpu); - - if (!hdev) - return 0; - free_irq(hdev->irq, hdev); - hdev->flags &= ~HPET_DEV_USED; - per_cpu(cpu_hpet_dev, cpu) = NULL; - return 0; -} #else static inline void hpet_msi_capability_lookup(unsigned int start_timer) { } From 3222daf970f30133cc4c639cbecdc29c4ae91b2b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:51 +0200 Subject: [PATCH 11/29] x86/hpet: Separate counter check out of clocksource register code The init code checks whether the HPET counter works late in the init function when the clocksource is registered. That should happen right with the other sanity checks. Split it into a separate validation function and move it to the other sanity checks. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132435.058540608@linutronix.de --- arch/x86/kernel/hpet.c | 65 ++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 71533f53fa1d..8c57dbf15e3b 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -809,38 +809,6 @@ static struct clocksource clocksource_hpet = { .resume = hpet_resume_counter, }; -static int __init hpet_clocksource_register(void) -{ - u64 start, now; - u64 t1; - - /* Start the counter */ - hpet_restart_counter(); - - /* Verify whether hpet counter works */ - t1 = hpet_readl(HPET_COUNTER); - start = rdtsc(); - - /* - * We don't know the TSC frequency yet, but waiting for - * 200000 TSC cycles is safe: - * 4 GHz == 50us - * 1 GHz == 200us - */ - do { - rep_nop(); - now = rdtsc(); - } while ((now - start) < 200000UL); - - if (t1 == hpet_readl(HPET_COUNTER)) { - pr_warn("Counter not counting. HPET disabled\n"); - return -ENODEV; - } - - clocksource_register_hz(&clocksource_hpet, (u32)hpet_freq); - return 0; -} - /* * AMD SB700 based systems with spread spectrum enabled use a SMM based * HPET emulation to provide proper frequency setting. @@ -869,6 +837,32 @@ static bool __init hpet_cfg_working(void) return false; } +static bool __init hpet_counting(void) +{ + u64 start, now, t1; + + hpet_restart_counter(); + + t1 = hpet_readl(HPET_COUNTER); + start = rdtsc(); + + /* + * We don't know the TSC frequency yet, but waiting for + * 200000 TSC cycles is safe: + * 4 GHz == 50us + * 1 GHz == 200us + */ + do { + rep_nop(); + now = rdtsc(); + } while ((now - start) < 200000UL); + + if (t1 == hpet_readl(HPET_COUNTER)) { + pr_warn("Counter not counting. HPET disabled\n"); + return false; + } + return true; +} /** * hpet_enable - Try to setup the HPET timer. Returns 1 on success. @@ -890,6 +884,10 @@ int __init hpet_enable(void) if (!hpet_cfg_working()) goto out_nohpet; + /* Validate that the counter is counting */ + if (!hpet_counting()) + goto out_nohpet; + /* * Read the period and check for a sane value: */ @@ -948,8 +946,7 @@ int __init hpet_enable(void) } hpet_print_config(); - if (hpet_clocksource_register()) - goto out_nohpet; + clocksource_register_hz(&clocksource_hpet, (u32)hpet_freq); if (id & HPET_ID_LEGSUP) { hpet_legacy_clockevent_register(); From 44b5be5733e119300115b98409cbcf9a45b8d3f1 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:52 +0200 Subject: [PATCH 12/29] x86/hpet: Simplify counter validation There is no point to loop for 200k TSC cycles to check afterwards whether the HPET counter is working. Read the counter inside of the loop and break out when the counter value changed. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132435.149535103@linutronix.de --- arch/x86/kernel/hpet.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 8c57dbf15e3b..74756c0a3a10 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -853,15 +853,13 @@ static bool __init hpet_counting(void) * 1 GHz == 200us */ do { - rep_nop(); + if (t1 != hpet_readl(HPET_COUNTER)) + return true; now = rdtsc(); } while ((now - start) < 200000UL); - if (t1 == hpet_readl(HPET_COUNTER)) { - pr_warn("Counter not counting. HPET disabled\n"); - return false; - } - return true; + pr_warn("Counter not counting. HPET disabled\n"); + return false; } /** From 3535aa12f7f26fc755514b13aee8fac15741267e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:53 +0200 Subject: [PATCH 13/29] x86/hpet: Decapitalize and rename EVT_TO_HPET_DEV It's a function not a macro and the upcoming changes use channel for the individual hpet timer units to allow a step by step refactoring approach. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132435.241032433@linutronix.de --- arch/x86/kernel/hpet.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 74756c0a3a10..4cf93294bacc 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -69,9 +69,10 @@ static bool hpet_verbose; static struct clock_event_device hpet_clockevent; -static inline struct hpet_dev *EVT_TO_HPET_DEV(struct clock_event_device *evtdev) +static inline +struct hpet_dev *clockevent_to_channel(struct clock_event_device *evt) { - return container_of(evtdev, struct hpet_dev, evt); + return container_of(evt, struct hpet_dev, evt); } inline unsigned int hpet_readl(unsigned int a) @@ -458,28 +459,22 @@ void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg) static int hpet_msi_shutdown(struct clock_event_device *evt) { - struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); - - return hpet_shutdown(evt, hdev->num); + return hpet_shutdown(evt, clockevent_to_channel(evt)->num); } static int hpet_msi_set_oneshot(struct clock_event_device *evt) { - struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); - - return hpet_set_oneshot(evt, hdev->num); + return hpet_set_oneshot(evt, clockevent_to_channel(evt)->num); } static int hpet_msi_set_periodic(struct clock_event_device *evt) { - struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); - - return hpet_set_periodic(evt, hdev->num); + return hpet_set_periodic(evt, clockevent_to_channel(evt)->num); } static int hpet_msi_resume(struct clock_event_device *evt) { - struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); + struct hpet_dev *hdev = clockevent_to_channel(evt); struct irq_data *data = irq_get_irq_data(hdev->irq); struct msi_msg msg; @@ -491,16 +486,14 @@ static int hpet_msi_resume(struct clock_event_device *evt) } static int hpet_msi_next_event(unsigned long delta, - struct clock_event_device *evt) + struct clock_event_device *evt) { - struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); - - return hpet_next_event(delta, hdev->num); + return hpet_next_event(delta, clockevent_to_channel(evt)->num); } static irqreturn_t hpet_interrupt_handler(int irq, void *data) { - struct hpet_dev *dev = (struct hpet_dev *)data; + struct hpet_dev *dev = data; struct clock_event_device *hevt = &dev->evt; if (!hevt->event_handler) { From 9bc9e1d4c139497553599a73839ea846dce63f72 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sun, 23 Jun 2019 15:23:54 +0200 Subject: [PATCH 14/29] x86/hpet: Remove not required includes Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132435.348089155@linutronix.de --- arch/x86/kernel/hpet.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 4cf93294bacc..96daae404b29 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -1,22 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only -#include #include #include -#include #include #include -#include -#include -#include #include -#include #include -#include -#include +#include -#include -#include -#include #include #include From 3fe50c34dc1fa8ae2c24ec202b9decbbef72921d Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sun, 23 Jun 2019 15:23:55 +0200 Subject: [PATCH 15/29] x86/hpet: Make naming consistent Use 'evt' for clockevents pointers and capitalize HPET in comments. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132435.454138339@linutronix.de --- arch/x86/kernel/hpet.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 96daae404b29..823e8d32182a 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -121,7 +121,7 @@ static inline int is_hpet_capable(void) } /** - * is_hpet_enabled - check whether the hpet timer interrupt is enabled + * is_hpet_enabled - Check whether the legacy HPET timer interrupt is enabled */ int is_hpet_enabled(void) { @@ -164,7 +164,7 @@ do { \ } while (0) /* - * When the hpet driver (/dev/hpet) is enabled, we need to reserve + * When the HPET driver (/dev/hpet) is enabled, we need to reserve * timer 0 and timer 1 in case of RTC emulation. */ #ifdef CONFIG_HPET @@ -212,7 +212,7 @@ static void __init hpet_reserve_platform_timers(unsigned int id) static void hpet_reserve_platform_timers(unsigned int id) { } #endif -/* Common hpet functions */ +/* Common HPET functions */ static void hpet_stop_counter(void) { u32 cfg = hpet_readl(HPET_CFG); @@ -266,7 +266,7 @@ static void hpet_legacy_clockevent_register(void) hpet_enable_legacy_int(); /* - * Start hpet with the boot cpu mask and make it + * Start HPET with the boot cpu mask and make it * global after the IO_APIC has been initialized. */ hpet_clockevent.cpumask = cpumask_of(boot_cpu_data.cpu_index); @@ -399,7 +399,7 @@ static int hpet_legacy_next_event(unsigned long delta, } /* - * The hpet clock event device + * The HPET clock event device */ static struct clock_event_device hpet_clockevent = { .name = "hpet", @@ -484,14 +484,14 @@ static int hpet_msi_next_event(unsigned long delta, static irqreturn_t hpet_interrupt_handler(int irq, void *data) { struct hpet_dev *dev = data; - struct clock_event_device *hevt = &dev->evt; + struct clock_event_device *evt = &dev->evt; - if (!hevt->event_handler) { + if (!evt->event_handler) { pr_info("Spurious interrupt HPET timer %d\n", dev->num); return IRQ_HANDLED; } - hevt->event_handler(hevt); + evt->event_handler(evt); return IRQ_HANDLED; } @@ -703,7 +703,7 @@ static inline void hpet_reserve_msi_timers(struct hpet_data *hd) { } * with its associated locking overhead. And we also need 64-bit atomic * read. * - * The lock and the hpet value are stored together and can be read in a + * The lock and the HPET value are stored together and can be read in a * single atomic 64-bit read. It is explicitly assumed that arch_spinlock_t * is 32 bits in size. */ @@ -1053,7 +1053,7 @@ static unsigned long hpet_pie_limit; static rtc_irq_handler irq_handler; /* - * Check that the hpet counter c1 is ahead of the c2 + * Check that the HPET counter c1 is ahead of the c2 */ static inline int hpet_cnt_ahead(u32 c1, u32 c2) { From dfe36b573ed320ce311b2cb9251d2543be9e52ac Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sun, 23 Jun 2019 15:23:56 +0200 Subject: [PATCH 16/29] x86/hpet: Clean up comments Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132435.545653922@linutronix.de --- arch/x86/kernel/hpet.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 823e8d32182a..1a389a2ff42a 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -266,8 +266,8 @@ static void hpet_legacy_clockevent_register(void) hpet_enable_legacy_int(); /* - * Start HPET with the boot cpu mask and make it - * global after the IO_APIC has been initialized. + * Start HPET with the boot CPU's cpumask and make it global after + * the IO_APIC has been initialized. */ hpet_clockevent.cpumask = cpumask_of(boot_cpu_data.cpu_index); clockevents_config_and_register(&hpet_clockevent, hpet_freq, @@ -688,10 +688,10 @@ static inline void hpet_reserve_msi_timers(struct hpet_data *hd) { } /* * Reading the HPET counter is a very slow operation. If a large number of * CPUs are trying to access the HPET counter simultaneously, it can cause - * massive delay and slow down system performance dramatically. This may + * massive delays and slow down system performance dramatically. This may * happen when HPET is the default clock source instead of TSC. For a * really large system with hundreds of CPUs, the slowdown may be so - * severe that it may actually crash the system because of a NMI watchdog + * severe, that it can actually crash the system because of a NMI watchdog * soft lockup, for example. * * If multiple CPUs are trying to access the HPET counter at the same time, @@ -700,8 +700,7 @@ static inline void hpet_reserve_msi_timers(struct hpet_data *hd) { } * * This special feature is only enabled on x86-64 systems. It is unlikely * that 32-bit x86 systems will have enough CPUs to require this feature - * with its associated locking overhead. And we also need 64-bit atomic - * read. + * with its associated locking overhead. We also need 64-bit atomic read. * * The lock and the HPET value are stored together and can be read in a * single atomic 64-bit read. It is explicitly assumed that arch_spinlock_t @@ -1020,19 +1019,25 @@ void hpet_disable(void) #ifdef CONFIG_HPET_EMULATE_RTC -/* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET +/* + * HPET in LegacyReplacement mode eats up the RTC interrupt line. When HPET * is enabled, we support RTC interrupt functionality in software. + * * RTC has 3 kinds of interrupts: - * 1) Update Interrupt - generate an interrupt, every sec, when RTC clock - * is updated - * 2) Alarm Interrupt - generate an interrupt at a specific time of day - * 3) Periodic Interrupt - generate periodic interrupt, with frequencies - * 2Hz-8192Hz (2Hz-64Hz for non-root user) (all freqs in powers of 2) - * (1) and (2) above are implemented using polling at a frequency of - * 64 Hz. The exact frequency is a tradeoff between accuracy and interrupt - * overhead. (DEFAULT_RTC_INT_FREQ) - * For (3), we use interrupts at 64Hz or user specified periodic - * frequency, whichever is higher. + * + * 1) Update Interrupt - generate an interrupt, every second, when the + * RTC clock is updated + * 2) Alarm Interrupt - generate an interrupt at a specific time of day + * 3) Periodic Interrupt - generate periodic interrupt, with frequencies + * 2Hz-8192Hz (2Hz-64Hz for non-root user) (all frequencies in powers of 2) + * + * (1) and (2) above are implemented using polling at a frequency of 64 Hz: + * DEFAULT_RTC_INT_FREQ. + * + * The exact frequency is a tradeoff between accuracy and interrupt overhead. + * + * For (3), we use interrupts at 64 Hz, or the user specified periodic frequency, + * if it's higher. */ #include #include @@ -1053,7 +1058,7 @@ static unsigned long hpet_pie_limit; static rtc_irq_handler irq_handler; /* - * Check that the HPET counter c1 is ahead of the c2 + * Check that the HPET counter c1 is ahead of c2 */ static inline int hpet_cnt_ahead(u32 c1, u32 c2) { From 0b5c597de6aa30000480d6add2f37ef7de3f9312 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sun, 23 Jun 2019 15:23:57 +0200 Subject: [PATCH 17/29] x86/hpet: Coding style cleanup Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132435.637420368@linutronix.de --- arch/x86/kernel/hpet.c | 43 +++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 1a389a2ff42a..ed2d556f2c96 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -131,26 +131,33 @@ EXPORT_SYMBOL_GPL(is_hpet_enabled); static void _hpet_print_config(const char *function, int line) { - u32 i, timers, l, h; + u32 i, id, period, cfg, status, channels, l, h; + pr_info("%s(%d):\n", function, line); - l = hpet_readl(HPET_ID); - h = hpet_readl(HPET_PERIOD); - timers = ((l & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; - pr_info("ID: 0x%x, PERIOD: 0x%x\n", l, h); - l = hpet_readl(HPET_CFG); - h = hpet_readl(HPET_STATUS); - pr_info("CFG: 0x%x, STATUS: 0x%x\n", l, h); + + id = hpet_readl(HPET_ID); + period = hpet_readl(HPET_PERIOD); + pr_info("ID: 0x%x, PERIOD: 0x%x\n", id, period); + + cfg = hpet_readl(HPET_CFG); + status = hpet_readl(HPET_STATUS); + pr_info("CFG: 0x%x, STATUS: 0x%x\n", cfg, status); + l = hpet_readl(HPET_COUNTER); h = hpet_readl(HPET_COUNTER+4); pr_info("COUNTER_l: 0x%x, COUNTER_h: 0x%x\n", l, h); - for (i = 0; i < timers; i++) { + channels = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; + + for (i = 0; i < channels; i++) { l = hpet_readl(HPET_Tn_CFG(i)); h = hpet_readl(HPET_Tn_CFG(i)+4); pr_info("T%d: CFG_l: 0x%x, CFG_h: 0x%x\n", i, l, h); + l = hpet_readl(HPET_Tn_CMP(i)); h = hpet_readl(HPET_Tn_CMP(i)+4); pr_info("T%d: CMP_l: 0x%x, CMP_h: 0x%x\n", i, l, h); + l = hpet_readl(HPET_Tn_ROUTE(i)); h = hpet_readl(HPET_Tn_ROUTE(i)+4); pr_info("T%d ROUTE_l: 0x%x, ROUTE_h: 0x%x\n", i, l, h); @@ -216,6 +223,7 @@ static void hpet_reserve_platform_timers(unsigned int id) { } static void hpet_stop_counter(void) { u32 cfg = hpet_readl(HPET_CFG); + cfg &= ~HPET_CFG_ENABLE; hpet_writel(cfg, HPET_CFG); } @@ -229,6 +237,7 @@ static void hpet_reset_counter(void) static void hpet_start_counter(void) { unsigned int cfg = hpet_readl(HPET_CFG); + cfg |= HPET_CFG_ENABLE; hpet_writel(cfg, HPET_CFG); } @@ -393,7 +402,7 @@ static int hpet_legacy_resume(struct clock_event_device *evt) } static int hpet_legacy_next_event(unsigned long delta, - struct clock_event_device *evt) + struct clock_event_device *evt) { return hpet_next_event(delta, 0); } @@ -1142,6 +1151,7 @@ EXPORT_SYMBOL_GPL(hpet_rtc_timer_init); static void hpet_disable_rtc_channel(void) { u32 cfg = hpet_readl(HPET_T1_CFG); + cfg &= ~HPET_TN_ENABLE; hpet_writel(cfg, HPET_T1_CFG); } @@ -1183,8 +1193,7 @@ int hpet_set_rtc_irq_bit(unsigned long bit_mask) } EXPORT_SYMBOL_GPL(hpet_set_rtc_irq_bit); -int hpet_set_alarm_time(unsigned char hrs, unsigned char min, - unsigned char sec) +int hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec) { if (!is_hpet_enabled()) return 0; @@ -1204,15 +1213,16 @@ int hpet_set_periodic_freq(unsigned long freq) if (!is_hpet_enabled()) return 0; - if (freq <= DEFAULT_RTC_INT_FREQ) + if (freq <= DEFAULT_RTC_INT_FREQ) { hpet_pie_limit = DEFAULT_RTC_INT_FREQ / freq; - else { + } else { clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; do_div(clc, freq); clc >>= hpet_clockevent.shift; hpet_pie_delta = clc; hpet_pie_limit = 0; } + return 1; } EXPORT_SYMBOL_GPL(hpet_set_periodic_freq); @@ -1272,8 +1282,7 @@ irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id) hpet_prev_update_sec = curr_time.tm_sec; } - if (hpet_rtc_flags & RTC_PIE && - ++hpet_pie_count >= hpet_pie_limit) { + if (hpet_rtc_flags & RTC_PIE && ++hpet_pie_count >= hpet_pie_limit) { rtc_int_flag |= RTC_PF; hpet_pie_count = 0; } @@ -1282,7 +1291,7 @@ irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id) (curr_time.tm_sec == hpet_alarm_time.tm_sec) && (curr_time.tm_min == hpet_alarm_time.tm_min) && (curr_time.tm_hour == hpet_alarm_time.tm_hour)) - rtc_int_flag |= RTC_AF; + rtc_int_flag |= RTC_AF; if (rtc_int_flag) { rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8)); From e37f0881e9d9ec8b12f242cc2b78d93259aa7f0f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:58 +0200 Subject: [PATCH 18/29] x86/hpet: Introduce struct hpet_base and struct hpet_channel Introduce new data structures to replace the ad hoc collection of separate variables and pointers. Replace the boot configuration store and restore as a first step. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132435.728456320@linutronix.de --- arch/x86/kernel/hpet.c | 82 ++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index ed2d556f2c96..59a81d7fd05b 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -22,6 +22,17 @@ struct hpet_dev { char name[10]; }; +struct hpet_channel { + unsigned int num; + unsigned int boot_cfg; +}; + +struct hpet_base { + unsigned int nr_channels; + unsigned int boot_cfg; + struct hpet_channel *channels; +}; + #define HPET_MASK CLOCKSOURCE_MASK(32) #define HPET_DEV_USED_BIT 2 @@ -48,7 +59,7 @@ static struct irq_domain *hpet_domain; #endif static void __iomem *hpet_virt_address; -static u32 *hpet_boot_cfg; +static struct hpet_base hpet_base; static bool hpet_legacy_int_enabled; static unsigned long hpet_freq; @@ -860,6 +871,7 @@ int __init hpet_enable(void) { u32 hpet_period, cfg, id; unsigned int i, channels; + struct hpet_channel *hc; u64 freq; if (!is_hpet_capable()) @@ -899,34 +911,39 @@ int __init hpet_enable(void) /* This is the HPET channel number which is zero based */ channels = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; -#ifdef CONFIG_HPET_EMULATE_RTC /* * The legacy routing mode needs at least two channels, tick timer * and the rtc emulation channel. */ - if (channels < 2) + if (IS_ENABLED(CONFIG_HPET_EMULATE_RTC) && channels < 2) goto out_nohpet; -#endif + hc = kcalloc(channels, sizeof(*hc), GFP_KERNEL); + if (!hc) { + pr_warn("Disabling HPET.\n"); + goto out_nohpet; + } + hpet_base.channels = hc; + hpet_base.nr_channels = channels; + + /* Read, store and sanitize the global configuration */ cfg = hpet_readl(HPET_CFG); - /* Allocate entries for the global and the channel configurations */ - hpet_boot_cfg = kmalloc_array(channels + 1, sizeof(*hpet_boot_cfg), - GFP_KERNEL); - if (hpet_boot_cfg) - *hpet_boot_cfg = cfg; - else - pr_warn("HPET initial state will not be saved\n"); + hpet_base.boot_cfg = cfg; cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY); hpet_writel(cfg, HPET_CFG); if (cfg) pr_warn("Global config: Unknown bits %#x\n", cfg); - for (i = 0; i < channels; ++i) { + /* Read, store and sanitize the per channel configuration */ + for (i = 0; i < channels; i++, hc++) { + hc->num = i; + cfg = hpet_readl(HPET_Tn_CFG(i)); - if (hpet_boot_cfg) - hpet_boot_cfg[i + 1] = cfg; + hc->boot_cfg = cfg; + cfg &= ~(HPET_TN_ENABLE | HPET_TN_LEVEL | HPET_TN_FSB); hpet_writel(cfg, HPET_Tn_CFG(i)); + cfg &= ~(HPET_TN_PERIODIC | HPET_TN_PERIODIC_CAP | HPET_TN_64BIT_CAP | HPET_TN_32BIT | HPET_TN_ROUTE | HPET_TN_FSB | HPET_TN_FSB_CAP); @@ -944,6 +961,9 @@ int __init hpet_enable(void) return 0; out_nohpet: + kfree(hpet_base.channels); + hpet_base.channels = NULL; + hpet_base.nr_channels = 0; hpet_clear_mapping(); hpet_address = 0; return 0; @@ -1000,30 +1020,24 @@ fs_initcall(hpet_late_init); void hpet_disable(void) { - if (is_hpet_capable() && hpet_virt_address) { - unsigned int cfg = hpet_readl(HPET_CFG), id, last; + unsigned int i; + u32 cfg; - if (hpet_boot_cfg) - cfg = *hpet_boot_cfg; - else if (hpet_legacy_int_enabled) { - cfg &= ~HPET_CFG_LEGACY; - hpet_legacy_int_enabled = false; - } - cfg &= ~HPET_CFG_ENABLE; - hpet_writel(cfg, HPET_CFG); + if (!is_hpet_capable() || !hpet_virt_address) + return; - if (!hpet_boot_cfg) - return; + /* Restore boot configuration with the enable bit cleared */ + cfg = hpet_base.boot_cfg; + cfg &= ~HPET_CFG_ENABLE; + hpet_writel(cfg, HPET_CFG); - id = hpet_readl(HPET_ID); - last = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); + /* Restore the channel boot configuration */ + for (i = 0; i < hpet_base.nr_channels; i++) + hpet_writel(hpet_base.channels[i].boot_cfg, HPET_Tn_CFG(i)); - for (id = 0; id <= last; ++id) - hpet_writel(hpet_boot_cfg[id + 1], HPET_Tn_CFG(id)); - - if (*hpet_boot_cfg & HPET_CFG_ENABLE) - hpet_writel(*hpet_boot_cfg, HPET_CFG); - } + /* If the HPET was enabled at boot time, reenable it */ + if (hpet_base.boot_cfg & HPET_CFG_ENABLE) + hpet_writel(hpet_base.boot_cfg, HPET_CFG); } #ifdef CONFIG_HPET_EMULATE_RTC From 2460d5878ad69c178f9ff1cc3eee9f09b017e15f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:23:59 +0200 Subject: [PATCH 19/29] x86/hpet: Use cached channel data Instead of rereading the HPET registers over and over use the information which was cached in hpet_enable(). Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132435.821728550@linutronix.de --- arch/x86/kernel/hpet.c | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 59a81d7fd05b..8711f1fdef8f 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -24,6 +24,7 @@ struct hpet_dev { struct hpet_channel { unsigned int num; + unsigned int irq; unsigned int boot_cfg; }; @@ -52,7 +53,6 @@ u8 hpet_blockid; /* OS timer block num */ bool hpet_msi_disable; #ifdef CONFIG_PCI_MSI -static unsigned int hpet_num_timers; static struct hpet_dev *hpet_devs; static DEFINE_PER_CPU(struct hpet_dev *, cpu_hpet_dev); static struct irq_domain *hpet_domain; @@ -189,19 +189,15 @@ do { \ static void hpet_reserve_msi_timers(struct hpet_data *hd); -static void __init hpet_reserve_platform_timers(unsigned int id) +static void __init hpet_reserve_platform_timers(void) { - struct hpet __iomem *hpet = hpet_virt_address; - struct hpet_timer __iomem *timer = &hpet->hpet_timers[2]; - unsigned int nrtimers, i; struct hpet_data hd; - - nrtimers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; + unsigned int i; memset(&hd, 0, sizeof(hd)); hd.hd_phys_address = hpet_address; - hd.hd_address = hpet; - hd.hd_nirqs = nrtimers; + hd.hd_address = hpet_virt_address; + hd.hd_nirqs = hpet_base.nr_channels; hpet_reserve_timer(&hd, 0); #ifdef CONFIG_HPET_EMULATE_RTC @@ -216,10 +212,8 @@ static void __init hpet_reserve_platform_timers(unsigned int id) hd.hd_irq[0] = HPET_LEGACY_8254; hd.hd_irq[1] = HPET_LEGACY_RTC; - for (i = 2; i < nrtimers; timer++, i++) { - hd.hd_irq[i] = (readl(&timer->hpet_config) & - Tn_INT_ROUTE_CNF_MASK) >> Tn_INT_ROUTE_CNF_SHIFT; - } + for (i = 2; i < hpet_base.nr_channels; i++) + hd.hd_irq[i] = hpet_base.channels[i].irq; hpet_reserve_msi_timers(&hd); @@ -227,7 +221,7 @@ static void __init hpet_reserve_platform_timers(unsigned int id) } #else -static void hpet_reserve_platform_timers(unsigned int id) { } +static inline void hpet_reserve_platform_timers(void) { } #endif /* Common HPET functions */ @@ -569,7 +563,7 @@ static struct hpet_dev *hpet_get_unused_timer(void) if (!hpet_devs) return NULL; - for (i = 0; i < hpet_num_timers; i++) { + for (i = 0; i < hpet_base.nr_channels; i++) { struct hpet_dev *hdev = &hpet_devs[i]; if (!(hdev->flags & HPET_DEV_VALID)) @@ -612,7 +606,6 @@ static int hpet_cpuhp_dead(unsigned int cpu) static void __init hpet_msi_capability_lookup(unsigned int start_timer) { - unsigned int id; unsigned int num_timers; unsigned int num_timers_used = 0; int i, irq; @@ -622,10 +615,8 @@ static void __init hpet_msi_capability_lookup(unsigned int start_timer) if (boot_cpu_has(X86_FEATURE_ARAT)) return; - id = hpet_readl(HPET_ID); - num_timers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); - num_timers++; /* Value read out starts from 0 */ + num_timers = hpet_base.nr_channels; hpet_print_config(); hpet_domain = hpet_create_irq_domain(hpet_blockid); @@ -636,11 +627,9 @@ static void __init hpet_msi_capability_lookup(unsigned int start_timer) if (!hpet_devs) return; - hpet_num_timers = num_timers; - for (i = start_timer; i < num_timers - RESERVE_TIMERS; i++) { struct hpet_dev *hdev = &hpet_devs[num_timers_used]; - unsigned int cfg = hpet_readl(HPET_Tn_CFG(i)); + unsigned int cfg = hpet_base.channels[i].boot_cfg; /* Only consider HPET timer with MSI support */ if (!(cfg & HPET_TN_FSB_CAP)) @@ -676,7 +665,7 @@ static void __init hpet_reserve_msi_timers(struct hpet_data *hd) if (!hpet_devs) return; - for (i = 0; i < hpet_num_timers; i++) { + for (i = 0; i < hpet_base.nr_channels; i++) { struct hpet_dev *hdev = &hpet_devs[i]; if (!(hdev->flags & HPET_DEV_VALID)) @@ -869,7 +858,7 @@ static bool __init hpet_counting(void) */ int __init hpet_enable(void) { - u32 hpet_period, cfg, id; + u32 hpet_period, cfg, id, irq; unsigned int i, channels; struct hpet_channel *hc; u64 freq; @@ -940,6 +929,8 @@ int __init hpet_enable(void) cfg = hpet_readl(HPET_Tn_CFG(i)); hc->boot_cfg = cfg; + irq = (cfg & Tn_INT_ROUTE_CNF_MASK) >> Tn_INT_ROUTE_CNF_SHIFT; + hc->irq = irq; cfg &= ~(HPET_TN_ENABLE | HPET_TN_LEVEL | HPET_TN_FSB); hpet_writel(cfg, HPET_Tn_CFG(i)); @@ -993,7 +984,7 @@ static __init int hpet_late_init(void) else hpet_msi_capability_lookup(0); - hpet_reserve_platform_timers(hpet_readl(HPET_ID)); + hpet_reserve_platform_timers(); hpet_print_config(); if (hpet_msi_disable) From 9e16e4933e48819a259b8967e72e5765349953b1 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:24:00 +0200 Subject: [PATCH 20/29] x86/hpet: Add mode information to struct hpet_channel The usage of the individual HPET channels is not tracked in a central place. The information is scattered in different data structures. Also the HPET reservation in the HPET character device is split out into several places which makes the code hard to follow. Assigning a mode to the channel allows to consolidate the reservation code and paves the way for further simplifications. As a first step set the mode of the legacy channels when the HPET is in legacy mode. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132435.911652981@linutronix.de --- arch/x86/kernel/hpet.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 8711f1fdef8f..3a8ec363d569 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -22,9 +22,17 @@ struct hpet_dev { char name[10]; }; +enum hpet_mode { + HPET_MODE_UNUSED, + HPET_MODE_LEGACY, + HPET_MODE_CLOCKEVT, + HPET_MODE_DEVICE, +}; + struct hpet_channel { unsigned int num; unsigned int irq; + enum hpet_mode mode; unsigned int boot_cfg; }; @@ -947,6 +955,9 @@ int __init hpet_enable(void) if (id & HPET_ID_LEGSUP) { hpet_legacy_clockevent_register(); + hpet_base.channels[0].mode = HPET_MODE_LEGACY; + if (IS_ENABLED(CONFIG_HPET_EMULATE_RTC)) + hpet_base.channels[1].mode = HPET_MODE_LEGACY; return 1; } return 0; From af5a1dadf3fcf673906af1a1129b2b7528494ee5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:24:01 +0200 Subject: [PATCH 21/29] x86/hpet: Add function to select a /dev/hpet channel If CONFIG_HPET=y is enabled the x86 specific HPET code should reserve at least one channel for the /dev/hpet character device, so that not all channels are absorbed for per CPU clockevent devices. Create a function to assign HPET_MODE_DEVICE so the rework of the clockevents allocation code can utilize the mode information instead of reducing the number of evaluated channels by #ifdef hackery. The function is not yet used, but provided as a separate patch for ease of review. It will be used when the rework of the clockevent selection takes place. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132436.002758910@linutronix.de --- arch/x86/kernel/hpet.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 3a8ec363d569..640ff75cc523 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -228,8 +228,25 @@ static void __init hpet_reserve_platform_timers(void) hpet_alloc(&hd); } + +static void __init hpet_select_device_channel(void) +{ + int i; + + for (i = 0; i < hpet_base.nr_channels; i++) { + struct hpet_channel *hc = hpet_base.channels + i; + + /* Associate the first unused channel to /dev/hpet */ + if (hc->mode == HPET_MODE_UNUSED) { + hc->mode = HPET_MODE_DEVICE; + return; + } + } +} + #else static inline void hpet_reserve_platform_timers(void) { } +static inline void hpet_select_device_channel(void) {} #endif /* Common HPET functions */ From d415c7543140f77fe1d2d9d3942cbf51a9737993 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Sun, 23 Jun 2019 15:24:02 +0200 Subject: [PATCH 22/29] x86/hpet: Rename variables to prepare for switching to channels struct hpet_dev is gone with the next change as the clockevent storage moves into struct hpet_channel. So the variable name hdev will not make sense anymore. Ditto for timer vs. channel and similar details. Doing the rename in the change makes the patch harder to review. Doing it afterward is problematic vs. tracking down issues. Doing it upfront is the easiest solution as it does not change functionality. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132436.093113681@linutronix.de --- arch/x86/kernel/hpet.c | 124 ++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 640ff75cc523..32f21b429881 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -315,7 +315,7 @@ static void hpet_legacy_clockevent_register(void) pr_debug("Clockevent registered\n"); } -static int hpet_set_periodic(struct clock_event_device *evt, int timer) +static int hpet_set_periodic(struct clock_event_device *evt, int channel) { unsigned int cfg, cmp, now; uint64_t delta; @@ -325,11 +325,11 @@ static int hpet_set_periodic(struct clock_event_device *evt, int timer) delta >>= evt->shift; now = hpet_readl(HPET_COUNTER); cmp = now + (unsigned int)delta; - cfg = hpet_readl(HPET_Tn_CFG(timer)); + cfg = hpet_readl(HPET_Tn_CFG(channel)); cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL | HPET_TN_32BIT; - hpet_writel(cfg, HPET_Tn_CFG(timer)); - hpet_writel(cmp, HPET_Tn_CMP(timer)); + hpet_writel(cfg, HPET_Tn_CFG(channel)); + hpet_writel(cmp, HPET_Tn_CMP(channel)); udelay(1); /* * HPET on AMD 81xx needs a second write (with HPET_TN_SETVAL @@ -338,32 +338,32 @@ static int hpet_set_periodic(struct clock_event_device *evt, int timer) * (See AMD-8111 HyperTransport I/O Hub Data Sheet, * Publication # 24674) */ - hpet_writel((unsigned int)delta, HPET_Tn_CMP(timer)); + hpet_writel((unsigned int)delta, HPET_Tn_CMP(channel)); hpet_start_counter(); hpet_print_config(); return 0; } -static int hpet_set_oneshot(struct clock_event_device *evt, int timer) +static int hpet_set_oneshot(struct clock_event_device *evt, int channel) { unsigned int cfg; - cfg = hpet_readl(HPET_Tn_CFG(timer)); + cfg = hpet_readl(HPET_Tn_CFG(channel)); cfg &= ~HPET_TN_PERIODIC; cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; - hpet_writel(cfg, HPET_Tn_CFG(timer)); + hpet_writel(cfg, HPET_Tn_CFG(channel)); return 0; } -static int hpet_shutdown(struct clock_event_device *evt, int timer) +static int hpet_shutdown(struct clock_event_device *evt, int channel) { unsigned int cfg; - cfg = hpet_readl(HPET_Tn_CFG(timer)); + cfg = hpet_readl(HPET_Tn_CFG(channel)); cfg &= ~HPET_TN_ENABLE; - hpet_writel(cfg, HPET_Tn_CFG(timer)); + hpet_writel(cfg, HPET_Tn_CFG(channel)); return 0; } @@ -460,30 +460,30 @@ static struct clock_event_device hpet_clockevent = { void hpet_msi_unmask(struct irq_data *data) { - struct hpet_dev *hdev = irq_data_get_irq_handler_data(data); + struct hpet_dev *hc = irq_data_get_irq_handler_data(data); unsigned int cfg; /* unmask it */ - cfg = hpet_readl(HPET_Tn_CFG(hdev->num)); + cfg = hpet_readl(HPET_Tn_CFG(hc->num)); cfg |= HPET_TN_ENABLE | HPET_TN_FSB; - hpet_writel(cfg, HPET_Tn_CFG(hdev->num)); + hpet_writel(cfg, HPET_Tn_CFG(hc->num)); } void hpet_msi_mask(struct irq_data *data) { - struct hpet_dev *hdev = irq_data_get_irq_handler_data(data); + struct hpet_dev *hc = irq_data_get_irq_handler_data(data); unsigned int cfg; /* mask it */ - cfg = hpet_readl(HPET_Tn_CFG(hdev->num)); + cfg = hpet_readl(HPET_Tn_CFG(hc->num)); cfg &= ~(HPET_TN_ENABLE | HPET_TN_FSB); - hpet_writel(cfg, HPET_Tn_CFG(hdev->num)); + hpet_writel(cfg, HPET_Tn_CFG(hc->num)); } -void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg) +void hpet_msi_write(struct hpet_dev *hc, struct msi_msg *msg) { - hpet_writel(msg->data, HPET_Tn_ROUTE(hdev->num)); - hpet_writel(msg->address_lo, HPET_Tn_ROUTE(hdev->num) + 4); + hpet_writel(msg->data, HPET_Tn_ROUTE(hc->num)); + hpet_writel(msg->address_lo, HPET_Tn_ROUTE(hc->num) + 4); } static int hpet_msi_shutdown(struct clock_event_device *evt) @@ -503,13 +503,13 @@ static int hpet_msi_set_periodic(struct clock_event_device *evt) static int hpet_msi_resume(struct clock_event_device *evt) { - struct hpet_dev *hdev = clockevent_to_channel(evt); - struct irq_data *data = irq_get_irq_data(hdev->irq); + struct hpet_dev *hc = clockevent_to_channel(evt); + struct irq_data *data = irq_get_irq_data(hc->irq); struct msi_msg msg; /* Restore the MSI msg and unmask the interrupt */ irq_chip_compose_msi_msg(data, &msg); - hpet_msi_write(hdev, &msg); + hpet_msi_write(hc, &msg); hpet_msi_unmask(data); return 0; } @@ -522,11 +522,11 @@ static int hpet_msi_next_event(unsigned long delta, static irqreturn_t hpet_interrupt_handler(int irq, void *data) { - struct hpet_dev *dev = data; - struct clock_event_device *evt = &dev->evt; + struct hpet_dev *hc = data; + struct clock_event_device *evt = &hc->evt; if (!evt->event_handler) { - pr_info("Spurious interrupt HPET timer %d\n", dev->num); + pr_info("Spurious interrupt HPET channel %d\n", hc->num); return IRQ_HANDLED; } @@ -551,22 +551,22 @@ static int hpet_setup_irq(struct hpet_dev *dev) return 0; } -static void init_one_hpet_msi_clockevent(struct hpet_dev *hdev, int cpu) +static void init_one_hpet_msi_clockevent(struct hpet_dev *hc, int cpu) { - struct clock_event_device *evt = &hdev->evt; + struct clock_event_device *evt = &hc->evt; - if (!(hdev->flags & HPET_DEV_VALID)) + if (!(hc->flags & HPET_DEV_VALID)) return; - hdev->cpu = cpu; - per_cpu(cpu_hpet_dev, cpu) = hdev; - evt->name = hdev->name; - hpet_setup_irq(hdev); - evt->irq = hdev->irq; + hc->cpu = cpu; + per_cpu(cpu_hpet_dev, cpu) = hc; + evt->name = hc->name; + hpet_setup_irq(hc); + evt->irq = hc->irq; evt->rating = 110; evt->features = CLOCK_EVT_FEAT_ONESHOT; - if (hdev->flags & HPET_DEV_PERI_CAP) { + if (hc->flags & HPET_DEV_PERI_CAP) { evt->features |= CLOCK_EVT_FEAT_PERIODIC; evt->set_state_periodic = hpet_msi_set_periodic; } @@ -575,7 +575,7 @@ static void init_one_hpet_msi_clockevent(struct hpet_dev *hdev, int cpu) evt->set_state_oneshot = hpet_msi_set_oneshot; evt->tick_resume = hpet_msi_resume; evt->set_next_event = hpet_msi_next_event; - evt->cpumask = cpumask_of(hdev->cpu); + evt->cpumask = cpumask_of(hc->cpu); clockevents_config_and_register(evt, hpet_freq, HPET_MIN_PROG_DELTA, 0x7FFFFFFF); @@ -589,35 +589,35 @@ static struct hpet_dev *hpet_get_unused_timer(void) return NULL; for (i = 0; i < hpet_base.nr_channels; i++) { - struct hpet_dev *hdev = &hpet_devs[i]; + struct hpet_dev *hc = &hpet_devs[i]; - if (!(hdev->flags & HPET_DEV_VALID)) + if (!(hc->flags & HPET_DEV_VALID)) continue; if (test_and_set_bit(HPET_DEV_USED_BIT, - (unsigned long *)&hdev->flags)) + (unsigned long *)&hc->flags)) continue; - return hdev; + return hc; } return NULL; } static int hpet_cpuhp_online(unsigned int cpu) { - struct hpet_dev *hdev = hpet_get_unused_timer(); + struct hpet_dev *hc = hpet_get_unused_timer(); - if (hdev) - init_one_hpet_msi_clockevent(hdev, cpu); + if (hc) + init_one_hpet_msi_clockevent(hc, cpu); return 0; } static int hpet_cpuhp_dead(unsigned int cpu) { - struct hpet_dev *hdev = per_cpu(cpu_hpet_dev, cpu); + struct hpet_dev *hc = per_cpu(cpu_hpet_dev, cpu); - if (!hdev) + if (!hc) return 0; - free_irq(hdev->irq, hdev); - hdev->flags &= ~HPET_DEV_USED; + free_irq(hc->irq, hc); + hc->flags &= ~HPET_DEV_USED; per_cpu(cpu_hpet_dev, cpu) = NULL; return 0; } @@ -653,26 +653,26 @@ static void __init hpet_msi_capability_lookup(unsigned int start_timer) return; for (i = start_timer; i < num_timers - RESERVE_TIMERS; i++) { - struct hpet_dev *hdev = &hpet_devs[num_timers_used]; + struct hpet_dev *hc = &hpet_devs[num_timers_used]; unsigned int cfg = hpet_base.channels[i].boot_cfg; /* Only consider HPET timer with MSI support */ if (!(cfg & HPET_TN_FSB_CAP)) continue; - hdev->flags = 0; + hc->flags = 0; if (cfg & HPET_TN_PERIODIC_CAP) - hdev->flags |= HPET_DEV_PERI_CAP; - sprintf(hdev->name, "hpet%d", i); - hdev->num = i; + hc->flags |= HPET_DEV_PERI_CAP; + sprintf(hc->name, "hpet%d", i); + hc->num = i; - irq = hpet_assign_irq(hpet_domain, hdev, hdev->num); + irq = hpet_assign_irq(hpet_domain, hc, hc->num); if (irq <= 0) continue; - hdev->irq = irq; - hdev->flags |= HPET_DEV_FSB_CAP; - hdev->flags |= HPET_DEV_VALID; + hc->irq = irq; + hc->flags |= HPET_DEV_FSB_CAP; + hc->flags |= HPET_DEV_VALID; num_timers_used++; if (num_timers_used == num_possible_cpus()) break; @@ -691,13 +691,13 @@ static void __init hpet_reserve_msi_timers(struct hpet_data *hd) return; for (i = 0; i < hpet_base.nr_channels; i++) { - struct hpet_dev *hdev = &hpet_devs[i]; + struct hpet_dev *hc = &hpet_devs[i]; - if (!(hdev->flags & HPET_DEV_VALID)) + if (!(hc->flags & HPET_DEV_VALID)) continue; - hd->hd_irq[hdev->num] = hdev->irq; - hpet_reserve_timer(hd, hdev->num); + hd->hd_irq[hc->num] = hc->irq; + hpet_reserve_timer(hd, hc->num); } } #endif @@ -1138,8 +1138,8 @@ void hpet_unregister_irq_handler(rtc_irq_handler handler) EXPORT_SYMBOL_GPL(hpet_unregister_irq_handler); /* - * Timer 1 for RTC emulation. We use one shot mode, as periodic mode - * is not supported by all HPET implementations for timer 1. + * Channel 1 for RTC emulation. We use one shot mode, as periodic mode + * is not supported by all HPET implementations for channel 1. * * hpet_rtc_timer_init() is called when the rtc is initialized. */ From 4d5e68330df4e79633bcde2bebcbfed1ba0421d5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:24:03 +0200 Subject: [PATCH 23/29] x86/hpet: Move clockevents into channels Instead of allocating yet another data structure, move the clock event data into the channel structure. This allows further consolidation of the reservation code and the reuse of the cached boot config to replace the extra flags in the clockevent data. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132436.185851116@linutronix.de --- arch/x86/include/asm/hpet.h | 6 +- arch/x86/kernel/apic/msi.c | 4 +- arch/x86/kernel/hpet.c | 141 +++++++++++++++--------------------- 3 files changed, 65 insertions(+), 86 deletions(-) diff --git a/arch/x86/include/asm/hpet.h b/arch/x86/include/asm/hpet.h index e3209f5de65d..6352dee37cda 100644 --- a/arch/x86/include/asm/hpet.h +++ b/arch/x86/include/asm/hpet.h @@ -75,15 +75,15 @@ extern unsigned int hpet_readl(unsigned int a); extern void force_hpet_resume(void); struct irq_data; -struct hpet_dev; +struct hpet_channel; struct irq_domain; extern void hpet_msi_unmask(struct irq_data *data); extern void hpet_msi_mask(struct irq_data *data); -extern void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg); +extern void hpet_msi_write(struct hpet_channel *hc, struct msi_msg *msg); extern struct irq_domain *hpet_create_irq_domain(int hpet_id); extern int hpet_assign_irq(struct irq_domain *domain, - struct hpet_dev *dev, int dev_num); + struct hpet_channel *hc, int dev_num); #ifdef CONFIG_HPET_EMULATE_RTC diff --git a/arch/x86/kernel/apic/msi.c b/arch/x86/kernel/apic/msi.c index dad0dd759de2..7f7533462474 100644 --- a/arch/x86/kernel/apic/msi.c +++ b/arch/x86/kernel/apic/msi.c @@ -370,14 +370,14 @@ struct irq_domain *hpet_create_irq_domain(int hpet_id) return d; } -int hpet_assign_irq(struct irq_domain *domain, struct hpet_dev *dev, +int hpet_assign_irq(struct irq_domain *domain, struct hpet_channel *hc, int dev_num) { struct irq_alloc_info info; init_irq_alloc_info(&info, NULL); info.type = X86_IRQ_ALLOC_TYPE_HPET; - info.hpet_data = dev; + info.hpet_data = hc; info.hpet_id = hpet_dev_id(domain); info.hpet_index = dev_num; diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 32f21b429881..7f76f07138a6 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -13,15 +13,6 @@ #undef pr_fmt #define pr_fmt(fmt) "hpet: " fmt -struct hpet_dev { - struct clock_event_device evt; - unsigned int num; - int cpu; - unsigned int irq; - unsigned int flags; - char name[10]; -}; - enum hpet_mode { HPET_MODE_UNUSED, HPET_MODE_LEGACY, @@ -30,14 +21,19 @@ enum hpet_mode { }; struct hpet_channel { + struct clock_event_device evt; unsigned int num; + unsigned int cpu; unsigned int irq; enum hpet_mode mode; + unsigned int flags; unsigned int boot_cfg; + char name[10]; }; struct hpet_base { unsigned int nr_channels; + unsigned int nr_clockevents; unsigned int boot_cfg; struct hpet_channel *channels; }; @@ -61,8 +57,7 @@ u8 hpet_blockid; /* OS timer block num */ bool hpet_msi_disable; #ifdef CONFIG_PCI_MSI -static struct hpet_dev *hpet_devs; -static DEFINE_PER_CPU(struct hpet_dev *, cpu_hpet_dev); +static DEFINE_PER_CPU(struct hpet_channel *, cpu_hpet_channel); static struct irq_domain *hpet_domain; #endif @@ -79,9 +74,9 @@ static bool hpet_verbose; static struct clock_event_device hpet_clockevent; static inline -struct hpet_dev *clockevent_to_channel(struct clock_event_device *evt) +struct hpet_channel *clockevent_to_channel(struct clock_event_device *evt) { - return container_of(evt, struct hpet_dev, evt); + return container_of(evt, struct hpet_channel, evt); } inline unsigned int hpet_readl(unsigned int a) @@ -460,10 +455,9 @@ static struct clock_event_device hpet_clockevent = { void hpet_msi_unmask(struct irq_data *data) { - struct hpet_dev *hc = irq_data_get_irq_handler_data(data); + struct hpet_channel *hc = irq_data_get_irq_handler_data(data); unsigned int cfg; - /* unmask it */ cfg = hpet_readl(HPET_Tn_CFG(hc->num)); cfg |= HPET_TN_ENABLE | HPET_TN_FSB; hpet_writel(cfg, HPET_Tn_CFG(hc->num)); @@ -471,16 +465,15 @@ void hpet_msi_unmask(struct irq_data *data) void hpet_msi_mask(struct irq_data *data) { - struct hpet_dev *hc = irq_data_get_irq_handler_data(data); + struct hpet_channel *hc = irq_data_get_irq_handler_data(data); unsigned int cfg; - /* mask it */ cfg = hpet_readl(HPET_Tn_CFG(hc->num)); cfg &= ~(HPET_TN_ENABLE | HPET_TN_FSB); hpet_writel(cfg, HPET_Tn_CFG(hc->num)); } -void hpet_msi_write(struct hpet_dev *hc, struct msi_msg *msg) +void hpet_msi_write(struct hpet_channel *hc, struct msi_msg *msg) { hpet_writel(msg->data, HPET_Tn_ROUTE(hc->num)); hpet_writel(msg->address_lo, HPET_Tn_ROUTE(hc->num) + 4); @@ -503,7 +496,7 @@ static int hpet_msi_set_periodic(struct clock_event_device *evt) static int hpet_msi_resume(struct clock_event_device *evt) { - struct hpet_dev *hc = clockevent_to_channel(evt); + struct hpet_channel *hc = clockevent_to_channel(evt); struct irq_data *data = irq_get_irq_data(hc->irq); struct msi_msg msg; @@ -522,7 +515,7 @@ static int hpet_msi_next_event(unsigned long delta, static irqreturn_t hpet_interrupt_handler(int irq, void *data) { - struct hpet_dev *hc = data; + struct hpet_channel *hc = data; struct clock_event_device *evt = &hc->evt; if (!evt->event_handler) { @@ -534,24 +527,23 @@ static irqreturn_t hpet_interrupt_handler(int irq, void *data) return IRQ_HANDLED; } -static int hpet_setup_irq(struct hpet_dev *dev) +static int hpet_setup_irq(struct hpet_channel *hc) { - - if (request_irq(dev->irq, hpet_interrupt_handler, + if (request_irq(hc->irq, hpet_interrupt_handler, IRQF_TIMER | IRQF_NOBALANCING, - dev->name, dev)) + hc->name, hc)) return -1; - disable_irq(dev->irq); - irq_set_affinity(dev->irq, cpumask_of(dev->cpu)); - enable_irq(dev->irq); + disable_irq(hc->irq); + irq_set_affinity(hc->irq, cpumask_of(hc->cpu)); + enable_irq(hc->irq); - pr_debug("%s irq %d for MSI\n", dev->name, dev->irq); + pr_debug("%s irq %u for MSI\n", hc->name, hc->irq); return 0; } -static void init_one_hpet_msi_clockevent(struct hpet_dev *hc, int cpu) +static void init_one_hpet_msi_clockevent(struct hpet_channel *hc, int cpu) { struct clock_event_device *evt = &hc->evt; @@ -559,7 +551,7 @@ static void init_one_hpet_msi_clockevent(struct hpet_dev *hc, int cpu) return; hc->cpu = cpu; - per_cpu(cpu_hpet_dev, cpu) = hc; + per_cpu(cpu_hpet_channel, cpu) = hc; evt->name = hc->name; hpet_setup_irq(hc); evt->irq = hc->irq; @@ -581,15 +573,12 @@ static void init_one_hpet_msi_clockevent(struct hpet_dev *hc, int cpu) 0x7FFFFFFF); } -static struct hpet_dev *hpet_get_unused_timer(void) +static struct hpet_channel *hpet_get_unused_clockevent(void) { int i; - if (!hpet_devs) - return NULL; - for (i = 0; i < hpet_base.nr_channels; i++) { - struct hpet_dev *hc = &hpet_devs[i]; + struct hpet_channel *hc = hpet_base.channels + i; if (!(hc->flags & HPET_DEV_VALID)) continue; @@ -603,7 +592,7 @@ static struct hpet_dev *hpet_get_unused_timer(void) static int hpet_cpuhp_online(unsigned int cpu) { - struct hpet_dev *hc = hpet_get_unused_timer(); + struct hpet_channel *hc = hpet_get_unused_clockevent(); if (hc) init_one_hpet_msi_clockevent(hc, cpu); @@ -612,59 +601,47 @@ static int hpet_cpuhp_online(unsigned int cpu) static int hpet_cpuhp_dead(unsigned int cpu) { - struct hpet_dev *hc = per_cpu(cpu_hpet_dev, cpu); + struct hpet_channel *hc = per_cpu(cpu_hpet_channel, cpu); if (!hc) return 0; free_irq(hc->irq, hc); hc->flags &= ~HPET_DEV_USED; - per_cpu(cpu_hpet_dev, cpu) = NULL; + per_cpu(cpu_hpet_channel, cpu) = NULL; return 0; } -#ifdef CONFIG_HPET -/* Reserve at least one timer for userspace (/dev/hpet) */ -#define RESERVE_TIMERS 1 -#else -#define RESERVE_TIMERS 0 -#endif - -static void __init hpet_msi_capability_lookup(unsigned int start_timer) +static void __init hpet_select_clockevents(void) { - unsigned int num_timers; - unsigned int num_timers_used = 0; - int i, irq; + unsigned int i; - if (hpet_msi_disable) + hpet_base.nr_clockevents = 0; + + /* No point if MSI is disabled or CPU has an Always Runing APIC Timer */ + if (hpet_msi_disable || boot_cpu_has(X86_FEATURE_ARAT)) return; - if (boot_cpu_has(X86_FEATURE_ARAT)) - return; - - num_timers = hpet_base.nr_channels; hpet_print_config(); hpet_domain = hpet_create_irq_domain(hpet_blockid); if (!hpet_domain) return; - hpet_devs = kcalloc(num_timers, sizeof(struct hpet_dev), GFP_KERNEL); - if (!hpet_devs) - return; + for (i = 0; i < hpet_base.nr_channels; i++) { + struct hpet_channel *hc = hpet_base.channels + i; + int irq; - for (i = start_timer; i < num_timers - RESERVE_TIMERS; i++) { - struct hpet_dev *hc = &hpet_devs[num_timers_used]; - unsigned int cfg = hpet_base.channels[i].boot_cfg; + if (hc->mode != HPET_MODE_UNUSED) + continue; - /* Only consider HPET timer with MSI support */ - if (!(cfg & HPET_TN_FSB_CAP)) + /* Only consider HPET channel with MSI support */ + if (!(hc->boot_cfg & HPET_TN_FSB_CAP)) continue; hc->flags = 0; - if (cfg & HPET_TN_PERIODIC_CAP) + if (hc->boot_cfg & HPET_TN_PERIODIC_CAP) hc->flags |= HPET_DEV_PERI_CAP; sprintf(hc->name, "hpet%d", i); - hc->num = i; irq = hpet_assign_irq(hpet_domain, hc, hc->num); if (irq <= 0) @@ -673,13 +650,14 @@ static void __init hpet_msi_capability_lookup(unsigned int start_timer) hc->irq = irq; hc->flags |= HPET_DEV_FSB_CAP; hc->flags |= HPET_DEV_VALID; - num_timers_used++; - if (num_timers_used == num_possible_cpus()) + hc->mode = HPET_MODE_CLOCKEVT; + + if (++hpet_base.nr_clockevents == num_possible_cpus()) break; } pr_info("%d channels of %d reserved for per-cpu timers\n", - num_timers, num_timers_used); + hpet_base.nr_channels, hpet_base.nr_clockevents); } #ifdef CONFIG_HPET @@ -687,11 +665,8 @@ static void __init hpet_reserve_msi_timers(struct hpet_data *hd) { int i; - if (!hpet_devs) - return; - for (i = 0; i < hpet_base.nr_channels; i++) { - struct hpet_dev *hc = &hpet_devs[i]; + struct hpet_channel *hc = hpet_base.channels + i; if (!(hc->flags & HPET_DEV_VALID)) continue; @@ -704,7 +679,7 @@ static void __init hpet_reserve_msi_timers(struct hpet_data *hd) #else -static inline void hpet_msi_capability_lookup(unsigned int start_timer) { } +static inline void hpet_select_clockevents(void) { } #ifdef CONFIG_HPET static inline void hpet_reserve_msi_timers(struct hpet_data *hd) { } @@ -991,6 +966,16 @@ out_nohpet: /* * The late initialization runs after the PCI quirks have been invoked * which might have detected a system on which the HPET can be enforced. + * + * Also, the MSI machinery is not working yet when the HPET is initialized + * early. + * + * If the HPET is enabled, then: + * + * 1) Reserve one channel for /dev/hpet if CONFIG_HPET=y + * 2) Reserve up to num_possible_cpus() channels as per CPU clockevents + * 3) Setup /dev/hpet if CONFIG_HPET=y + * 4) Register hotplug callbacks when clockevents are available */ static __init int hpet_late_init(void) { @@ -1007,18 +992,12 @@ static __init int hpet_late_init(void) if (!hpet_virt_address) return -ENODEV; - if (hpet_readl(HPET_ID) & HPET_ID_LEGSUP) - hpet_msi_capability_lookup(2); - else - hpet_msi_capability_lookup(0); - + hpet_select_device_channel(); + hpet_select_clockevents(); hpet_reserve_platform_timers(); hpet_print_config(); - if (hpet_msi_disable) - return 0; - - if (boot_cpu_has(X86_FEATURE_ARAT)) + if (!hpet_base.nr_clockevents) return 0; ret = cpuhp_setup_state(CPUHP_AP_X86_HPET_ONLINE, "x86/hpet:online", From 45e0a415634600e608188480bc355b20344f9e3f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:24:04 +0200 Subject: [PATCH 24/29] x86/hpet: Use cached info instead of extra flags Now that HPET clockevent support is integrated into the channel data, reuse the cached boot configuration instead of copying the same information into a flags field. This also allows to consolidate the reservation code into one place, which can now solely depend on the mode information. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132436.277510163@linutronix.de --- arch/x86/kernel/hpet.c | 76 +++++++++++++----------------------------- 1 file changed, 23 insertions(+), 53 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 7f76f07138a6..985a2246d20c 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -25,8 +25,8 @@ struct hpet_channel { unsigned int num; unsigned int cpu; unsigned int irq; + unsigned int in_use; enum hpet_mode mode; - unsigned int flags; unsigned int boot_cfg; char name[10]; }; @@ -40,12 +40,6 @@ struct hpet_base { #define HPET_MASK CLOCKSOURCE_MASK(32) -#define HPET_DEV_USED_BIT 2 -#define HPET_DEV_USED (1 << HPET_DEV_USED_BIT) -#define HPET_DEV_VALID 0x8 -#define HPET_DEV_FSB_CAP 0x1000 -#define HPET_DEV_PERI_CAP 0x2000 - #define HPET_MIN_CYCLES 128 #define HPET_MIN_PROG_DELTA (HPET_MIN_CYCLES + (HPET_MIN_CYCLES >> 1)) @@ -62,6 +56,7 @@ static struct irq_domain *hpet_domain; #endif static void __iomem *hpet_virt_address; + static struct hpet_base hpet_base; static bool hpet_legacy_int_enabled; @@ -190,8 +185,6 @@ do { \ */ #ifdef CONFIG_HPET -static void hpet_reserve_msi_timers(struct hpet_data *hd); - static void __init hpet_reserve_platform_timers(void) { struct hpet_data hd; @@ -201,11 +194,6 @@ static void __init hpet_reserve_platform_timers(void) hd.hd_phys_address = hpet_address; hd.hd_address = hpet_virt_address; hd.hd_nirqs = hpet_base.nr_channels; - hpet_reserve_timer(&hd, 0); - -#ifdef CONFIG_HPET_EMULATE_RTC - hpet_reserve_timer(&hd, 1); -#endif /* * NOTE that hd_irq[] reflects IOAPIC input pins (LEGACY_8254 @@ -215,13 +203,25 @@ static void __init hpet_reserve_platform_timers(void) hd.hd_irq[0] = HPET_LEGACY_8254; hd.hd_irq[1] = HPET_LEGACY_RTC; - for (i = 2; i < hpet_base.nr_channels; i++) - hd.hd_irq[i] = hpet_base.channels[i].irq; + for (i = 0; i < hpet_base.nr_channels; i++) { + struct hpet_channel *hc = hpet_base.channels + i; - hpet_reserve_msi_timers(&hd); + if (i >= 2) + hd.hd_irq[i] = hc->irq; + + switch (hc->mode) { + case HPET_MODE_UNUSED: + case HPET_MODE_DEVICE: + hc->mode = HPET_MODE_DEVICE; + break; + case HPET_MODE_CLOCKEVT: + case HPET_MODE_LEGACY: + hpet_reserve_timer(&hd, hc->num); + break; + } + } hpet_alloc(&hd); - } static void __init hpet_select_device_channel(void) @@ -543,13 +543,11 @@ static int hpet_setup_irq(struct hpet_channel *hc) return 0; } +/* Invoked from the hotplug callback on @cpu */ static void init_one_hpet_msi_clockevent(struct hpet_channel *hc, int cpu) { struct clock_event_device *evt = &hc->evt; - if (!(hc->flags & HPET_DEV_VALID)) - return; - hc->cpu = cpu; per_cpu(cpu_hpet_channel, cpu) = hc; evt->name = hc->name; @@ -558,7 +556,7 @@ static void init_one_hpet_msi_clockevent(struct hpet_channel *hc, int cpu) evt->rating = 110; evt->features = CLOCK_EVT_FEAT_ONESHOT; - if (hc->flags & HPET_DEV_PERI_CAP) { + if (hc->boot_cfg & HPET_TN_PERIODIC) { evt->features |= CLOCK_EVT_FEAT_PERIODIC; evt->set_state_periodic = hpet_msi_set_periodic; } @@ -580,11 +578,9 @@ static struct hpet_channel *hpet_get_unused_clockevent(void) for (i = 0; i < hpet_base.nr_channels; i++) { struct hpet_channel *hc = hpet_base.channels + i; - if (!(hc->flags & HPET_DEV_VALID)) - continue; - if (test_and_set_bit(HPET_DEV_USED_BIT, - (unsigned long *)&hc->flags)) + if (hc->mode != HPET_MODE_CLOCKEVT || hc->in_use) continue; + hc->in_use = 1; return hc; } return NULL; @@ -606,7 +602,7 @@ static int hpet_cpuhp_dead(unsigned int cpu) if (!hc) return 0; free_irq(hc->irq, hc); - hc->flags &= ~HPET_DEV_USED; + hc->in_use = 0; per_cpu(cpu_hpet_channel, cpu) = NULL; return 0; } @@ -638,9 +634,6 @@ static void __init hpet_select_clockevents(void) if (!(hc->boot_cfg & HPET_TN_FSB_CAP)) continue; - hc->flags = 0; - if (hc->boot_cfg & HPET_TN_PERIODIC_CAP) - hc->flags |= HPET_DEV_PERI_CAP; sprintf(hc->name, "hpet%d", i); irq = hpet_assign_irq(hpet_domain, hc, hc->num); @@ -648,8 +641,6 @@ static void __init hpet_select_clockevents(void) continue; hc->irq = irq; - hc->flags |= HPET_DEV_FSB_CAP; - hc->flags |= HPET_DEV_VALID; hc->mode = HPET_MODE_CLOCKEVT; if (++hpet_base.nr_clockevents == num_possible_cpus()) @@ -660,31 +651,10 @@ static void __init hpet_select_clockevents(void) hpet_base.nr_channels, hpet_base.nr_clockevents); } -#ifdef CONFIG_HPET -static void __init hpet_reserve_msi_timers(struct hpet_data *hd) -{ - int i; - - for (i = 0; i < hpet_base.nr_channels; i++) { - struct hpet_channel *hc = hpet_base.channels + i; - - if (!(hc->flags & HPET_DEV_VALID)) - continue; - - hd->hd_irq[hc->num] = hc->irq; - hpet_reserve_timer(hd, hc->num); - } -} -#endif - #else static inline void hpet_select_clockevents(void) { } -#ifdef CONFIG_HPET -static inline void hpet_reserve_msi_timers(struct hpet_data *hd) { } -#endif - #define hpet_cpuhp_online NULL #define hpet_cpuhp_dead NULL From 18e84a2dff00c3c817161a105332cd3fc7592648 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:24:05 +0200 Subject: [PATCH 25/29] x86/hpet: Wrap legacy clockevent in hpet_channel For HPET channel 0 there exist two clockevent structures right now: - the static hpet_clockevent - the clockevent in channel 0 storage The goal is to use the clockevent in the channel storage, remove the static variable and share code with the MSI implementation. As a first step wrap the legacy clockevent into a hpet_channel struct and convert the users. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132436.368141247@linutronix.de --- arch/x86/kernel/hpet.c | 49 +++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 985a2246d20c..19e3ac81c3b9 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -66,7 +66,7 @@ bool boot_hpet_disable; bool hpet_force_user; static bool hpet_verbose; -static struct clock_event_device hpet_clockevent; +static struct hpet_channel hpet_channel0; static inline struct hpet_channel *clockevent_to_channel(struct clock_event_device *evt) @@ -294,7 +294,7 @@ static void hpet_enable_legacy_int(void) hpet_legacy_int_enabled = true; } -static void hpet_legacy_clockevent_register(void) +static void hpet_legacy_clockevent_register(struct hpet_channel *hc) { /* Start HPET legacy interrupts */ hpet_enable_legacy_int(); @@ -303,10 +303,10 @@ static void hpet_legacy_clockevent_register(void) * Start HPET with the boot CPU's cpumask and make it global after * the IO_APIC has been initialized. */ - hpet_clockevent.cpumask = cpumask_of(boot_cpu_data.cpu_index); - clockevents_config_and_register(&hpet_clockevent, hpet_freq, + hc->evt.cpumask = cpumask_of(boot_cpu_data.cpu_index); + clockevents_config_and_register(&hc->evt, hpet_freq, HPET_MIN_PROG_DELTA, 0x7FFFFFFF); - global_clock_event = &hpet_clockevent; + global_clock_event = &hc->evt; pr_debug("Clockevent registered\n"); } @@ -433,19 +433,21 @@ static int hpet_legacy_next_event(unsigned long delta, } /* - * The HPET clock event device + * The HPET clock event device wrapped in a channel for conversion */ -static struct clock_event_device hpet_clockevent = { - .name = "hpet", - .features = CLOCK_EVT_FEAT_PERIODIC | - CLOCK_EVT_FEAT_ONESHOT, - .set_state_periodic = hpet_legacy_set_periodic, - .set_state_oneshot = hpet_legacy_set_oneshot, - .set_state_shutdown = hpet_legacy_shutdown, - .tick_resume = hpet_legacy_resume, - .set_next_event = hpet_legacy_next_event, - .irq = 0, - .rating = 50, +static struct hpet_channel hpet_channel0 = { + .evt = { + .name = "hpet", + .features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT, + .set_state_periodic = hpet_legacy_set_periodic, + .set_state_oneshot = hpet_legacy_set_oneshot, + .set_state_shutdown = hpet_legacy_shutdown, + .tick_resume = hpet_legacy_resume, + .set_next_event = hpet_legacy_next_event, + .irq = 0, + .rating = 50, + } }; /* @@ -916,7 +918,7 @@ int __init hpet_enable(void) clocksource_register_hz(&clocksource_hpet, (u32)hpet_freq); if (id & HPET_ID_LEGSUP) { - hpet_legacy_clockevent_register(); + hpet_legacy_clockevent_register(&hpet_channel0); hpet_base.channels[0].mode = HPET_MODE_LEGACY; if (IS_ENABLED(CONFIG_HPET_EMULATE_RTC)) hpet_base.channels[1].mode = HPET_MODE_LEGACY; @@ -1101,10 +1103,11 @@ int hpet_rtc_timer_init(void) return 0; if (!hpet_default_delta) { + struct clock_event_device *evt = &hpet_channel0.evt; uint64_t clc; - clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; - clc >>= hpet_clockevent.shift + DEFAULT_RTC_SHIFT; + clc = (uint64_t) evt->mult * NSEC_PER_SEC; + clc >>= evt->shift + DEFAULT_RTC_SHIFT; hpet_default_delta = clc; } @@ -1198,9 +1201,11 @@ int hpet_set_periodic_freq(unsigned long freq) if (freq <= DEFAULT_RTC_INT_FREQ) { hpet_pie_limit = DEFAULT_RTC_INT_FREQ / freq; } else { - clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; + struct clock_event_device *evt = &hpet_channel0.evt; + + clc = (uint64_t) evt->mult * NSEC_PER_SEC; do_div(clc, freq); - clc >>= hpet_clockevent.shift; + clc >>= evt->shift; hpet_pie_delta = clc; hpet_pie_limit = 0; } From 310b5b3eb6ba5d3a92d783b9fa1c5a3ffb5932e9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:24:06 +0200 Subject: [PATCH 26/29] x86/hpet: Consolidate clockevent functions Now that the legacy clockevent is wrapped in a hpet_channel struct most clockevent functions can be shared between the legacy and the MSI based clockevents. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132436.461437795@linutronix.de --- arch/x86/kernel/hpet.c | 92 ++++++++++++------------------------------ 1 file changed, 25 insertions(+), 67 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 19e3ac81c3b9..47eb4d36864e 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -310,8 +310,9 @@ static void hpet_legacy_clockevent_register(struct hpet_channel *hc) pr_debug("Clockevent registered\n"); } -static int hpet_set_periodic(struct clock_event_device *evt, int channel) +static int hpet_clkevt_set_state_periodic(struct clock_event_device *evt) { + unsigned int channel = clockevent_to_channel(evt)->num; unsigned int cfg, cmp, now; uint64_t delta; @@ -340,8 +341,9 @@ static int hpet_set_periodic(struct clock_event_device *evt, int channel) return 0; } -static int hpet_set_oneshot(struct clock_event_device *evt, int channel) +static int hpet_clkevt_set_state_oneshot(struct clock_event_device *evt) { + unsigned int channel = clockevent_to_channel(evt)->num; unsigned int cfg; cfg = hpet_readl(HPET_Tn_CFG(channel)); @@ -352,8 +354,9 @@ static int hpet_set_oneshot(struct clock_event_device *evt, int channel) return 0; } -static int hpet_shutdown(struct clock_event_device *evt, int channel) +static int hpet_clkevt_set_state_shutdown(struct clock_event_device *evt) { + unsigned int channel = clockevent_to_channel(evt)->num; unsigned int cfg; cfg = hpet_readl(HPET_Tn_CFG(channel)); @@ -363,15 +366,17 @@ static int hpet_shutdown(struct clock_event_device *evt, int channel) return 0; } -static int hpet_resume(struct clock_event_device *evt) +static int hpet_clkevt_legacy_resume(struct clock_event_device *evt) { hpet_enable_legacy_int(); hpet_print_config(); return 0; } -static int hpet_next_event(unsigned long delta, int channel) +static int +hpet_clkevt_set_next_event(unsigned long delta, struct clock_event_device *evt) { + unsigned int channel = clockevent_to_channel(evt)->num; u32 cnt; s32 res; @@ -406,32 +411,6 @@ static int hpet_next_event(unsigned long delta, int channel) return res < HPET_MIN_CYCLES ? -ETIME : 0; } -static int hpet_legacy_shutdown(struct clock_event_device *evt) -{ - return hpet_shutdown(evt, 0); -} - -static int hpet_legacy_set_oneshot(struct clock_event_device *evt) -{ - return hpet_set_oneshot(evt, 0); -} - -static int hpet_legacy_set_periodic(struct clock_event_device *evt) -{ - return hpet_set_periodic(evt, 0); -} - -static int hpet_legacy_resume(struct clock_event_device *evt) -{ - return hpet_resume(evt); -} - -static int hpet_legacy_next_event(unsigned long delta, - struct clock_event_device *evt) -{ - return hpet_next_event(delta, 0); -} - /* * The HPET clock event device wrapped in a channel for conversion */ @@ -440,11 +419,11 @@ static struct hpet_channel hpet_channel0 = { .name = "hpet", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .set_state_periodic = hpet_legacy_set_periodic, - .set_state_oneshot = hpet_legacy_set_oneshot, - .set_state_shutdown = hpet_legacy_shutdown, - .tick_resume = hpet_legacy_resume, - .set_next_event = hpet_legacy_next_event, + .set_state_periodic = hpet_clkevt_set_state_periodic, + .set_state_oneshot = hpet_clkevt_set_state_oneshot, + .set_state_shutdown = hpet_clkevt_set_state_shutdown, + .tick_resume = hpet_clkevt_legacy_resume, + .set_next_event = hpet_clkevt_set_next_event, .irq = 0, .rating = 50, } @@ -481,22 +460,7 @@ void hpet_msi_write(struct hpet_channel *hc, struct msi_msg *msg) hpet_writel(msg->address_lo, HPET_Tn_ROUTE(hc->num) + 4); } -static int hpet_msi_shutdown(struct clock_event_device *evt) -{ - return hpet_shutdown(evt, clockevent_to_channel(evt)->num); -} - -static int hpet_msi_set_oneshot(struct clock_event_device *evt) -{ - return hpet_set_oneshot(evt, clockevent_to_channel(evt)->num); -} - -static int hpet_msi_set_periodic(struct clock_event_device *evt) -{ - return hpet_set_periodic(evt, clockevent_to_channel(evt)->num); -} - -static int hpet_msi_resume(struct clock_event_device *evt) +static int hpet_clkevt_msi_resume(struct clock_event_device *evt) { struct hpet_channel *hc = clockevent_to_channel(evt); struct irq_data *data = irq_get_irq_data(hc->irq); @@ -509,13 +473,7 @@ static int hpet_msi_resume(struct clock_event_device *evt) return 0; } -static int hpet_msi_next_event(unsigned long delta, - struct clock_event_device *evt) -{ - return hpet_next_event(delta, clockevent_to_channel(evt)->num); -} - -static irqreturn_t hpet_interrupt_handler(int irq, void *data) +static irqreturn_t hpet_msi_interrupt_handler(int irq, void *data) { struct hpet_channel *hc = data; struct clock_event_device *evt = &hc->evt; @@ -529,9 +487,9 @@ static irqreturn_t hpet_interrupt_handler(int irq, void *data) return IRQ_HANDLED; } -static int hpet_setup_irq(struct hpet_channel *hc) +static int hpet_setup_msi_irq(struct hpet_channel *hc) { - if (request_irq(hc->irq, hpet_interrupt_handler, + if (request_irq(hc->irq, hpet_msi_interrupt_handler, IRQF_TIMER | IRQF_NOBALANCING, hc->name, hc)) return -1; @@ -553,20 +511,20 @@ static void init_one_hpet_msi_clockevent(struct hpet_channel *hc, int cpu) hc->cpu = cpu; per_cpu(cpu_hpet_channel, cpu) = hc; evt->name = hc->name; - hpet_setup_irq(hc); + hpet_setup_msi_irq(hc); evt->irq = hc->irq; evt->rating = 110; evt->features = CLOCK_EVT_FEAT_ONESHOT; if (hc->boot_cfg & HPET_TN_PERIODIC) { evt->features |= CLOCK_EVT_FEAT_PERIODIC; - evt->set_state_periodic = hpet_msi_set_periodic; + evt->set_state_periodic = hpet_clkevt_set_state_periodic; } - evt->set_state_shutdown = hpet_msi_shutdown; - evt->set_state_oneshot = hpet_msi_set_oneshot; - evt->tick_resume = hpet_msi_resume; - evt->set_next_event = hpet_msi_next_event; + evt->set_state_shutdown = hpet_clkevt_set_state_shutdown; + evt->set_state_oneshot = hpet_clkevt_set_state_oneshot; + evt->set_next_event = hpet_clkevt_set_next_event; + evt->tick_resume = hpet_clkevt_msi_resume; evt->cpumask = cpumask_of(hc->cpu); clockevents_config_and_register(evt, hpet_freq, HPET_MIN_PROG_DELTA, From ea99110dd024d2f31bde19dda049f3fbf3816a70 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:24:07 +0200 Subject: [PATCH 27/29] x86/hpet: Carve out shareable parts of init_one_hpet_msi_clockevent() To finally remove the static channel0/clockevent storage and to utilize the channel 0 storage in hpet_base, it's required to run time initialize the clockevent. The MSI clockevents already have a run time init function. Carve out the parts which can be shared between the legacy and the MSI implementation. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132436.552451082@linutronix.de --- arch/x86/kernel/hpet.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 47eb4d36864e..80497fe5354c 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -411,6 +411,25 @@ hpet_clkevt_set_next_event(unsigned long delta, struct clock_event_device *evt) return res < HPET_MIN_CYCLES ? -ETIME : 0; } +static void hpet_init_clockevent(struct hpet_channel *hc, unsigned int rating) +{ + struct clock_event_device *evt = &hc->evt; + + evt->rating = rating; + evt->irq = hc->irq; + evt->name = hc->name; + evt->cpumask = cpumask_of(hc->cpu); + evt->set_state_oneshot = hpet_clkevt_set_state_oneshot; + evt->set_next_event = hpet_clkevt_set_next_event; + evt->set_state_shutdown = hpet_clkevt_set_state_shutdown; + + evt->features = CLOCK_EVT_FEAT_ONESHOT; + if (hc->boot_cfg & HPET_TN_PERIODIC) { + evt->features |= CLOCK_EVT_FEAT_PERIODIC; + evt->set_state_periodic = hpet_clkevt_set_state_periodic; + } +} + /* * The HPET clock event device wrapped in a channel for conversion */ @@ -510,22 +529,10 @@ static void init_one_hpet_msi_clockevent(struct hpet_channel *hc, int cpu) hc->cpu = cpu; per_cpu(cpu_hpet_channel, cpu) = hc; - evt->name = hc->name; hpet_setup_msi_irq(hc); - evt->irq = hc->irq; - evt->rating = 110; - evt->features = CLOCK_EVT_FEAT_ONESHOT; - if (hc->boot_cfg & HPET_TN_PERIODIC) { - evt->features |= CLOCK_EVT_FEAT_PERIODIC; - evt->set_state_periodic = hpet_clkevt_set_state_periodic; - } - - evt->set_state_shutdown = hpet_clkevt_set_state_shutdown; - evt->set_state_oneshot = hpet_clkevt_set_state_oneshot; - evt->set_next_event = hpet_clkevt_set_next_event; + hpet_init_clockevent(hc, 110); evt->tick_resume = hpet_clkevt_msi_resume; - evt->cpumask = cpumask_of(hc->cpu); clockevents_config_and_register(evt, hpet_freq, HPET_MIN_PROG_DELTA, 0x7FFFFFFF); From 49adaa60fa75a04457d30f38321378cdc3547212 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:24:08 +0200 Subject: [PATCH 28/29] x86/hpet: Use common init for legacy clockevent Replace the static initialization of the legacy clockevent with runtime initialization utilizing the common init function as the last preparatory step to switch the legacy clockevent over to the channel 0 storage in hpet_base. This comes with a twist. The static clockevent initializer has selected support for periodic and oneshot mode unconditionally whether the HPET config advertised periodic mode or not. Even the pre clockevents code did this. But.... Using the conditional in hpet_init_clockevent() makes at least Qemu and one hardware machine fail to boot. There are two issues which cause the boot failure: #1 After the timer delivery test in IOAPIC and the IOAPIC setup the next interrupt is not delivered despite the HPET channel being programmed correctly. Reprogramming the HPET after switching to IOAPIC makes it work again. After fixing this, the next issue surfaces: #2 Due to the unconditional periodic mode 'availability' the Local APIC timer calibration can hijack the global clockevents event handler without causing damage. Using oneshot at this stage makes if hang because the HPET does not get reprogrammed due to the handler hijacking. Duh, stupid me! Both issues require major surgery and especially the kick HPET again after enabling IOAPIC results in really nasty hackery. This 'assume periodic works' magic has survived since HPET support got added, so it's questionable whether this should be fixed. Both Qemu and the failing hardware machine support periodic mode despite the fact that both don't advertise it in the configuration register and both need that extra kick after switching to IOAPIC. Seems to be a feature... Keep the 'assume periodic works' magic around and add a big fat comment. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132436.646565913@linutronix.de --- arch/x86/kernel/hpet.c | 87 ++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 80497fe5354c..35633e577d21 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -66,6 +66,9 @@ bool boot_hpet_disable; bool hpet_force_user; static bool hpet_verbose; +/* + * The HPET clock event device wrapped in a channel for conversion + */ static struct hpet_channel hpet_channel0; static inline @@ -294,22 +297,6 @@ static void hpet_enable_legacy_int(void) hpet_legacy_int_enabled = true; } -static void hpet_legacy_clockevent_register(struct hpet_channel *hc) -{ - /* Start HPET legacy interrupts */ - hpet_enable_legacy_int(); - - /* - * Start HPET with the boot CPU's cpumask and make it global after - * the IO_APIC has been initialized. - */ - hc->evt.cpumask = cpumask_of(boot_cpu_data.cpu_index); - clockevents_config_and_register(&hc->evt, hpet_freq, - HPET_MIN_PROG_DELTA, 0x7FFFFFFF); - global_clock_event = &hc->evt; - pr_debug("Clockevent registered\n"); -} - static int hpet_clkevt_set_state_periodic(struct clock_event_device *evt) { unsigned int channel = clockevent_to_channel(evt)->num; @@ -430,23 +417,57 @@ static void hpet_init_clockevent(struct hpet_channel *hc, unsigned int rating) } } -/* - * The HPET clock event device wrapped in a channel for conversion - */ -static struct hpet_channel hpet_channel0 = { - .evt = { - .name = "hpet", - .features = CLOCK_EVT_FEAT_PERIODIC | - CLOCK_EVT_FEAT_ONESHOT, - .set_state_periodic = hpet_clkevt_set_state_periodic, - .set_state_oneshot = hpet_clkevt_set_state_oneshot, - .set_state_shutdown = hpet_clkevt_set_state_shutdown, - .tick_resume = hpet_clkevt_legacy_resume, - .set_next_event = hpet_clkevt_set_next_event, - .irq = 0, - .rating = 50, - } -}; +static void __init hpet_legacy_clockevent_register(struct hpet_channel *hc) +{ + /* + * Start HPET with the boot CPU's cpumask and make it global after + * the IO_APIC has been initialized. + */ + hc->cpu = boot_cpu_data.cpu_index; + strncpy(hc->name, "hpet", sizeof(hc->name)); + hpet_init_clockevent(hc, 50); + + hc->evt.tick_resume = hpet_clkevt_legacy_resume; + + /* + * Legacy horrors and sins from the past. HPET used periodic mode + * unconditionally forever on the legacy channel 0. Removing the + * below hack and using the conditional in hpet_init_clockevent() + * makes at least Qemu and one hardware machine fail to boot. + * There are two issues which cause the boot failure: + * + * #1 After the timer delivery test in IOAPIC and the IOAPIC setup + * the next interrupt is not delivered despite the HPET channel + * being programmed correctly. Reprogramming the HPET after + * switching to IOAPIC makes it work again. After fixing this, + * the next issue surfaces: + * + * #2 Due to the unconditional periodic mode availability the Local + * APIC timer calibration can hijack the global clockevents + * event handler without causing damage. Using oneshot at this + * stage makes if hang because the HPET does not get + * reprogrammed due to the handler hijacking. Duh, stupid me! + * + * Both issues require major surgery and especially the kick HPET + * again after enabling IOAPIC results in really nasty hackery. + * This 'assume periodic works' magic has survived since HPET + * support got added, so it's questionable whether this should be + * fixed. Both Qemu and the failing hardware machine support + * periodic mode despite the fact that both don't advertise it in + * the configuration register and both need that extra kick after + * switching to IOAPIC. Seems to be a feature... + */ + hc->evt.features |= CLOCK_EVT_FEAT_PERIODIC; + hc->evt.set_state_periodic = hpet_clkevt_set_state_periodic; + + /* Start HPET legacy interrupts */ + hpet_enable_legacy_int(); + + clockevents_config_and_register(&hc->evt, hpet_freq, + HPET_MIN_PROG_DELTA, 0x7FFFFFFF); + global_clock_event = &hc->evt; + pr_debug("Clockevent registered\n"); +} /* * HPET MSI Support From e44252f4fe79dd9ca93bcf4e8f74389a5b8452f5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Jun 2019 15:24:09 +0200 Subject: [PATCH 29/29] x86/hpet: Use channel for legacy clockevent storage All preparations are done. Use the channel storage for the legacy clockevent and remove the static variable. Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Cc: Peter Zijlstra Cc: Ricardo Neri Cc: Ashok Raj Cc: Andi Kleen Cc: Suravee Suthikulpanit Cc: Stephane Eranian Cc: Ravi Shankar Link: https://lkml.kernel.org/r/20190623132436.737689919@linutronix.de --- arch/x86/kernel/hpet.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 35633e577d21..c43e96a938d0 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -66,11 +66,6 @@ bool boot_hpet_disable; bool hpet_force_user; static bool hpet_verbose; -/* - * The HPET clock event device wrapped in a channel for conversion - */ -static struct hpet_channel hpet_channel0; - static inline struct hpet_channel *clockevent_to_channel(struct clock_event_device *evt) { @@ -904,7 +899,7 @@ int __init hpet_enable(void) clocksource_register_hz(&clocksource_hpet, (u32)hpet_freq); if (id & HPET_ID_LEGSUP) { - hpet_legacy_clockevent_register(&hpet_channel0); + hpet_legacy_clockevent_register(&hpet_base.channels[0]); hpet_base.channels[0].mode = HPET_MODE_LEGACY; if (IS_ENABLED(CONFIG_HPET_EMULATE_RTC)) hpet_base.channels[1].mode = HPET_MODE_LEGACY; @@ -1089,7 +1084,7 @@ int hpet_rtc_timer_init(void) return 0; if (!hpet_default_delta) { - struct clock_event_device *evt = &hpet_channel0.evt; + struct clock_event_device *evt = &hpet_base.channels[0].evt; uint64_t clc; clc = (uint64_t) evt->mult * NSEC_PER_SEC; @@ -1187,7 +1182,7 @@ int hpet_set_periodic_freq(unsigned long freq) if (freq <= DEFAULT_RTC_INT_FREQ) { hpet_pie_limit = DEFAULT_RTC_INT_FREQ / freq; } else { - struct clock_event_device *evt = &hpet_channel0.evt; + struct clock_event_device *evt = &hpet_base.channels[0].evt; clc = (uint64_t) evt->mult * NSEC_PER_SEC; do_div(clc, freq);