Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

tick/broadcast: Program wakeup timer when entering idle if required

When configuring the broadcast timer on entry to and exit from deep idle
states, prefer a per-CPU wakeup timer if one exists.

On entry to idle, stop the tick device and transfer the next event into
the oneshot wakeup device, which will serve as the wakeup from idle. To
avoid the overhead of additional hardware accesses on exit from idle,
leave the timer armed and treat the inevitable interrupt as a (possibly
spurious) tick event.

Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20210524221818.15850-5-will@kernel.org

authored by

Will Deacon and committed by
Thomas Gleixner
ea5c7f1b c94a8537

+43 -1
+43 -1
kernel/time/tick-broadcast.c
··· 96 96 return per_cpu(tick_oneshot_wakeup_device, cpu); 97 97 } 98 98 99 + static void tick_oneshot_wakeup_handler(struct clock_event_device *wd) 100 + { 101 + /* 102 + * If we woke up early and the tick was reprogrammed in the 103 + * meantime then this may be spurious but harmless. 104 + */ 105 + tick_receive_broadcast(); 106 + } 107 + 99 108 static bool tick_set_oneshot_wakeup_device(struct clock_event_device *newdev, 100 109 int cpu) 101 110 { ··· 130 121 if (!try_module_get(newdev->owner)) 131 122 return false; 132 123 124 + newdev->event_handler = tick_oneshot_wakeup_handler; 133 125 set_device: 134 126 clockevents_exchange_device(curdev, newdev); 135 127 per_cpu(tick_oneshot_wakeup_device, cpu) = newdev; ··· 919 909 return ret; 920 910 } 921 911 912 + static int tick_oneshot_wakeup_control(enum tick_broadcast_state state, 913 + struct tick_device *td, 914 + int cpu) 915 + { 916 + struct clock_event_device *dev, *wd; 917 + 918 + dev = td->evtdev; 919 + if (td->mode != TICKDEV_MODE_ONESHOT) 920 + return -EINVAL; 921 + 922 + wd = tick_get_oneshot_wakeup_device(cpu); 923 + if (!wd) 924 + return -ENODEV; 925 + 926 + switch (state) { 927 + case TICK_BROADCAST_ENTER: 928 + clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT_STOPPED); 929 + clockevents_switch_state(wd, CLOCK_EVT_STATE_ONESHOT); 930 + clockevents_program_event(wd, dev->next_event, 1); 931 + break; 932 + case TICK_BROADCAST_EXIT: 933 + /* We may have transitioned to oneshot mode while idle */ 934 + if (clockevent_get_state(wd) != CLOCK_EVT_STATE_ONESHOT) 935 + return -ENODEV; 936 + } 937 + 938 + return 0; 939 + } 940 + 922 941 int __tick_broadcast_oneshot_control(enum tick_broadcast_state state) 923 942 { 924 943 struct tick_device *td = this_cpu_ptr(&tick_cpu_device); 925 944 int cpu = smp_processor_id(); 926 945 946 + if (!tick_oneshot_wakeup_control(state, td, cpu)) 947 + return 0; 948 + 927 949 if (tick_broadcast_device.evtdev) 928 950 return ___tick_broadcast_oneshot_control(state, td, cpu); 929 951 930 952 /* 931 - * If there is no broadcast device, tell the caller not 953 + * If there is no broadcast or wakeup device, tell the caller not 932 954 * to go into deep idle. 933 955 */ 934 956 return -EBUSY;