Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : /* 3 : : * This file contains functions which manage high resolution tick 4 : : * related events. 5 : : * 6 : : * Copyright(C) 2005-2006, Thomas Gleixner <tglx@linutronix.de> 7 : : * Copyright(C) 2005-2007, Red Hat, Inc., Ingo Molnar 8 : : * Copyright(C) 2006-2007, Timesys Corp., Thomas Gleixner 9 : : */ 10 : : #include <linux/cpu.h> 11 : : #include <linux/err.h> 12 : : #include <linux/hrtimer.h> 13 : : #include <linux/interrupt.h> 14 : : #include <linux/percpu.h> 15 : : #include <linux/profile.h> 16 : : #include <linux/sched.h> 17 : : 18 : : #include "tick-internal.h" 19 : : 20 : : /** 21 : : * tick_program_event 22 : : */ 23 : 21898 : int tick_program_event(ktime_t expires, int force) 24 : : { 25 [ + + ]: 21898 : struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); 26 : : 27 [ + + ]: 21898 : if (unlikely(expires == KTIME_MAX)) { 28 : : /* 29 : : * We don't need the clock event device any more, stop it. 30 : : */ 31 : 195 : clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT_STOPPED); 32 : 195 : dev->next_event = KTIME_MAX; 33 : 195 : return 0; 34 : : } 35 : : 36 [ + + ]: 21703 : if (unlikely(clockevent_state_oneshot_stopped(dev))) { 37 : : /* 38 : : * We need the clock event again, configure it in ONESHOT mode 39 : : * before using it. 40 : : */ 41 : 195 : clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT); 42 : : } 43 : : 44 : 21703 : return clockevents_program_event(dev, expires, force); 45 : : } 46 : : 47 : : /** 48 : : * tick_resume_onshot - resume oneshot mode 49 : : */ 50 : 0 : void tick_resume_oneshot(void) 51 : : { 52 : 0 : struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); 53 : : 54 : 0 : clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT); 55 : 0 : clockevents_program_event(dev, ktime_get(), true); 56 : 0 : } 57 : : 58 : : /** 59 : : * tick_setup_oneshot - setup the event device for oneshot mode (hres or nohz) 60 : : */ 61 : 0 : void tick_setup_oneshot(struct clock_event_device *newdev, 62 : : void (*handler)(struct clock_event_device *), 63 : : ktime_t next_event) 64 : : { 65 : 0 : newdev->event_handler = handler; 66 : 0 : clockevents_switch_state(newdev, CLOCK_EVT_STATE_ONESHOT); 67 : 0 : clockevents_program_event(newdev, next_event, true); 68 : 0 : } 69 : : 70 : : /** 71 : : * tick_switch_to_oneshot - switch to oneshot mode 72 : : */ 73 : 11 : int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *)) 74 : : { 75 : 11 : struct tick_device *td = this_cpu_ptr(&tick_cpu_device); 76 : 11 : struct clock_event_device *dev = td->evtdev; 77 : : 78 [ + - + - : 11 : if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) || - + ] 79 : : !tick_device_is_functional(dev)) { 80 : : 81 : 0 : pr_info("Clockevents: could not switch to one-shot mode:"); 82 [ # # ]: 0 : if (!dev) { 83 : 0 : pr_cont(" no tick device\n"); 84 : : } else { 85 [ # # ]: 0 : if (!tick_device_is_functional(dev)) 86 : 0 : pr_cont(" %s is not functional.\n", dev->name); 87 : : else 88 : 0 : pr_cont(" %s does not support one-shot mode.\n", 89 : : dev->name); 90 : : } 91 : 0 : return -EINVAL; 92 : : } 93 : : 94 : 11 : td->mode = TICKDEV_MODE_ONESHOT; 95 : 11 : dev->event_handler = handler; 96 : 11 : clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT); 97 : 11 : tick_broadcast_switch_to_oneshot(); 98 : 11 : return 0; 99 : : } 100 : : 101 : : /** 102 : : * tick_check_oneshot_mode - check whether the system is in oneshot mode 103 : : * 104 : : * returns 1 when either nohz or highres are enabled. otherwise 0. 105 : : */ 106 : 88 : int tick_oneshot_mode_active(void) 107 : : { 108 : 88 : unsigned long flags; 109 : 88 : int ret; 110 : : 111 : 88 : local_irq_save(flags); 112 : 88 : ret = __this_cpu_read(tick_cpu_device.mode) == TICKDEV_MODE_ONESHOT; 113 : 88 : local_irq_restore(flags); 114 : : 115 : 88 : return ret; 116 : : } 117 : : 118 : : #ifdef CONFIG_HIGH_RES_TIMERS 119 : : /** 120 : : * tick_init_highres - switch to high resolution mode 121 : : * 122 : : * Called with interrupts disabled. 123 : : */ 124 : 11 : int tick_init_highres(void) 125 : : { 126 : 11 : return tick_switch_to_oneshot(hrtimer_interrupt); 127 : : } 128 : : #endif