2014-02-07 12:06:32 +04:00
/*
* linux / kernel / time / tick - broadcast - hrtimer . c
* This file emulates a local clock event device
* via a pseudo clock device .
*/
# include <linux/cpu.h>
# include <linux/err.h>
# include <linux/hrtimer.h>
# include <linux/interrupt.h>
# include <linux/percpu.h>
# include <linux/profile.h>
# include <linux/clockchips.h>
# include <linux/sched.h>
# include <linux/smp.h>
# include <linux/module.h>
# include "tick-internal.h"
static struct hrtimer bctimer ;
2015-07-16 14:26:35 +03:00
static int bc_shutdown ( struct clock_event_device * evt )
2014-02-07 12:06:32 +04:00
{
2015-07-16 14:26:35 +03:00
/*
* Note , we cannot cancel the timer here as we might
* run into the following live lock scenario :
*
* cpu 0 cpu1
* lock ( broadcast_lock ) ;
* hrtimer_interrupt ( )
* bc_handler ( )
* tick_handle_oneshot_broadcast ( ) ;
* lock ( broadcast_lock ) ;
* hrtimer_cancel ( )
* wait_for_callback ( )
*/
hrtimer_try_to_cancel ( & bctimer ) ;
return 0 ;
2014-02-07 12:06:32 +04:00
}
/*
* This is called from the guts of the broadcast code when the cpu
* which is about to enter idle has the earliest broadcast timer event .
*/
static int bc_set_next ( ktime_t expires , struct clock_event_device * bc )
{
2015-03-18 13:49:27 +03:00
int bc_moved ;
2014-02-07 12:06:32 +04:00
/*
* We try to cancel the timer first . If the callback is on
* flight on some other cpu then we let it handle it . If we
* were able to cancel the timer nothing can rearm it as we
* own broadcast_lock .
*
* However we can also be called from the event handler of
* ce_broadcast_hrtimer itself when it expires . We cannot
* restart the timer because we are in the callback , but we
* can set the expiry time and let the callback return
* HRTIMER_RESTART .
2015-03-18 13:49:27 +03:00
*
* Since we are in the idle loop at this point and because
* hrtimer_ { start / cancel } functions call into tracing ,
* calls to these functions must be bound within RCU_NONIDLE .
2014-02-07 12:06:32 +04:00
*/
2015-04-15 00:09:22 +03:00
RCU_NONIDLE ( {
bc_moved = hrtimer_try_to_cancel ( & bctimer ) > = 0 ;
if ( bc_moved )
hrtimer_start ( & bctimer , expires ,
HRTIMER_MODE_ABS_PINNED ) ; } ) ;
2015-03-18 13:49:27 +03:00
if ( bc_moved ) {
2014-02-07 12:06:32 +04:00
/* Bind the "device" to the cpu */
bc - > bound_on = smp_processor_id ( ) ;
} else if ( bc - > bound_on = = smp_processor_id ( ) ) {
hrtimer_set_expires ( & bctimer , expires ) ;
}
return 0 ;
}
static struct clock_event_device ce_broadcast_hrtimer = {
2016-07-05 11:57:51 +03:00
. name = " bc_hrtimer " ,
2015-07-16 14:26:35 +03:00
. set_state_shutdown = bc_shutdown ,
2014-02-07 12:06:32 +04:00
. set_next_ktime = bc_set_next ,
. features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_KTIME |
CLOCK_EVT_FEAT_HRTIMER ,
. rating = 0 ,
. bound_on = - 1 ,
. min_delta_ns = 1 ,
. max_delta_ns = KTIME_MAX ,
. min_delta_ticks = 1 ,
2014-02-09 10:02:22 +04:00
. max_delta_ticks = ULONG_MAX ,
2014-02-07 12:06:32 +04:00
. mult = 1 ,
. shift = 0 ,
. cpumask = cpu_all_mask ,
} ;
static enum hrtimer_restart bc_handler ( struct hrtimer * t )
{
ce_broadcast_hrtimer . event_handler ( & ce_broadcast_hrtimer ) ;
2015-07-16 14:26:35 +03:00
if ( clockevent_state_oneshot ( & ce_broadcast_hrtimer ) )
2016-12-25 13:38:40 +03:00
if ( ce_broadcast_hrtimer . next_event ! = KTIME_MAX )
2015-04-24 16:06:05 +03:00
return HRTIMER_RESTART ;
2015-07-16 14:26:35 +03:00
return HRTIMER_NORESTART ;
2014-02-07 12:06:32 +04:00
}
void tick_setup_hrtimer_broadcast ( void )
{
hrtimer_init ( & bctimer , CLOCK_MONOTONIC , HRTIMER_MODE_ABS ) ;
bctimer . function = bc_handler ;
clockevents_register_device ( & ce_broadcast_hrtimer ) ;
}