cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tick-broadcast-hrtimer.c (3227B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Emulate a local clock event device via a pseudo clock device.
      4 */
      5#include <linux/cpu.h>
      6#include <linux/err.h>
      7#include <linux/hrtimer.h>
      8#include <linux/interrupt.h>
      9#include <linux/percpu.h>
     10#include <linux/profile.h>
     11#include <linux/clockchips.h>
     12#include <linux/sched.h>
     13#include <linux/smp.h>
     14#include <linux/module.h>
     15
     16#include "tick-internal.h"
     17
     18static struct hrtimer bctimer;
     19
     20static int bc_shutdown(struct clock_event_device *evt)
     21{
     22	/*
     23	 * Note, we cannot cancel the timer here as we might
     24	 * run into the following live lock scenario:
     25	 *
     26	 * cpu 0		cpu1
     27	 * lock(broadcast_lock);
     28	 *			hrtimer_interrupt()
     29	 *			bc_handler()
     30	 *			   tick_handle_oneshot_broadcast();
     31	 *			    lock(broadcast_lock);
     32	 * hrtimer_cancel()
     33	 *  wait_for_callback()
     34	 */
     35	hrtimer_try_to_cancel(&bctimer);
     36	return 0;
     37}
     38
     39/*
     40 * This is called from the guts of the broadcast code when the cpu
     41 * which is about to enter idle has the earliest broadcast timer event.
     42 */
     43static int bc_set_next(ktime_t expires, struct clock_event_device *bc)
     44{
     45	/*
     46	 * This is called either from enter/exit idle code or from the
     47	 * broadcast handler. In all cases tick_broadcast_lock is held.
     48	 *
     49	 * hrtimer_cancel() cannot be called here neither from the
     50	 * broadcast handler nor from the enter/exit idle code. The idle
     51	 * code can run into the problem described in bc_shutdown() and the
     52	 * broadcast handler cannot wait for itself to complete for obvious
     53	 * reasons.
     54	 *
     55	 * Each caller tries to arm the hrtimer on its own CPU, but if the
     56	 * hrtimer callback function is currently running, then
     57	 * hrtimer_start() cannot move it and the timer stays on the CPU on
     58	 * which it is assigned at the moment.
     59	 *
     60	 * As this can be called from idle code, the hrtimer_start()
     61	 * invocation has to be wrapped with RCU_NONIDLE() as
     62	 * hrtimer_start() can call into tracing.
     63	 */
     64	RCU_NONIDLE( {
     65		hrtimer_start(&bctimer, expires, HRTIMER_MODE_ABS_PINNED_HARD);
     66		/*
     67		 * The core tick broadcast mode expects bc->bound_on to be set
     68		 * correctly to prevent a CPU which has the broadcast hrtimer
     69		 * armed from going deep idle.
     70		 *
     71		 * As tick_broadcast_lock is held, nothing can change the cpu
     72		 * base which was just established in hrtimer_start() above. So
     73		 * the below access is safe even without holding the hrtimer
     74		 * base lock.
     75		 */
     76		bc->bound_on = bctimer.base->cpu_base->cpu;
     77	} );
     78	return 0;
     79}
     80
     81static struct clock_event_device ce_broadcast_hrtimer = {
     82	.name			= "bc_hrtimer",
     83	.set_state_shutdown	= bc_shutdown,
     84	.set_next_ktime		= bc_set_next,
     85	.features		= CLOCK_EVT_FEAT_ONESHOT |
     86				  CLOCK_EVT_FEAT_KTIME |
     87				  CLOCK_EVT_FEAT_HRTIMER,
     88	.rating			= 0,
     89	.bound_on		= -1,
     90	.min_delta_ns		= 1,
     91	.max_delta_ns		= KTIME_MAX,
     92	.min_delta_ticks	= 1,
     93	.max_delta_ticks	= ULONG_MAX,
     94	.mult			= 1,
     95	.shift			= 0,
     96	.cpumask		= cpu_possible_mask,
     97};
     98
     99static enum hrtimer_restart bc_handler(struct hrtimer *t)
    100{
    101	ce_broadcast_hrtimer.event_handler(&ce_broadcast_hrtimer);
    102
    103	return HRTIMER_NORESTART;
    104}
    105
    106void tick_setup_hrtimer_broadcast(void)
    107{
    108	hrtimer_init(&bctimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
    109	bctimer.function = bc_handler;
    110	clockevents_register_device(&ce_broadcast_hrtimer);
    111}