Merge branch 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer fixes from Thomas Gleixner: "This update from the timer departement contains: - A series of patches which address a shortcoming in the tick broadcast code. If the broadcast device is not available or an hrtimer emulated broadcast device, some of the original assumptions lead to boot failures. I rather plugged all of the corner cases instead of only addressing the issue reported, so the change got a little larger. Has been extensivly tested on x86 and arm. - Get rid of the last holdouts using do_posix_clock_monotonic_gettime() - A regression fix for the imx clocksource driver - An update to the new state callbacks mechanism for clockevents. This is required to simplify the conversion, which will take place in 4.3" * 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: tick/broadcast: Prevent NULL pointer dereference time: Get rid of do_posix_clock_monotonic_gettime cris: Replace do_posix_clock_monotonic_gettime() tick/broadcast: Unbreak CONFIG_GENERIC_CLOCKEVENTS=n build tick/broadcast: Handle spurious interrupts gracefully tick/broadcast: Check for hrtimer broadcast active early tick/broadcast: Return busy when IPI is pending tick/broadcast: Return busy if periodic mode and hrtimer broadcast tick/broadcast: Move the check for periodic mode inside state handling tick/broadcast: Prevent deep idle if no broadcast device available tick/broadcast: Make idle check independent from mode and config tick/broadcast: Sanity check the shutdown of the local clock_event tick/broadcast: Prevent hrtimer recursion clockevents: Allow set-state callbacks to be optional clocksource/imx: Define clocksource for mx27
This commit is contained in:
commit
7b732169e9
8 changed files with 155 additions and 74 deletions
|
@ -1464,7 +1464,7 @@ static inline void handle_rx_packet(struct sync_port *port)
|
|||
if (port->write_ts_idx == NBR_IN_DESCR)
|
||||
port->write_ts_idx = 0;
|
||||
idx = port->write_ts_idx++;
|
||||
do_posix_clock_monotonic_gettime(&port->timestamp[idx]);
|
||||
ktime_get_ts(&port->timestamp[idx]);
|
||||
port->in_buffer_len += port->inbufchunk;
|
||||
}
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
|
|
@ -529,6 +529,7 @@ static void __init imx6dl_timer_init_dt(struct device_node *np)
|
|||
|
||||
CLOCKSOURCE_OF_DECLARE(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
|
||||
CLOCKSOURCE_OF_DECLARE(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt);
|
||||
CLOCKSOURCE_OF_DECLARE(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt);
|
||||
CLOCKSOURCE_OF_DECLARE(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
|
||||
CLOCKSOURCE_OF_DECLARE(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt);
|
||||
CLOCKSOURCE_OF_DECLARE(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt);
|
||||
|
|
|
@ -67,10 +67,13 @@ extern void tick_broadcast_control(enum tick_broadcast_mode mode);
|
|||
static inline void tick_broadcast_control(enum tick_broadcast_mode mode) { }
|
||||
#endif /* BROADCAST */
|
||||
|
||||
#if defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) && defined(CONFIG_TICK_ONESHOT)
|
||||
#ifdef CONFIG_GENERIC_CLOCKEVENTS
|
||||
extern int tick_broadcast_oneshot_control(enum tick_broadcast_state state);
|
||||
#else
|
||||
static inline int tick_broadcast_oneshot_control(enum tick_broadcast_state state) { return 0; }
|
||||
static inline int tick_broadcast_oneshot_control(enum tick_broadcast_state state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void tick_broadcast_enable(void)
|
||||
|
|
|
@ -145,7 +145,6 @@ static inline void getboottime(struct timespec *ts)
|
|||
}
|
||||
#endif
|
||||
|
||||
#define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts)
|
||||
#define ktime_get_real_ts64(ts) getnstimeofday64(ts)
|
||||
|
||||
/*
|
||||
|
|
|
@ -120,19 +120,25 @@ static int __clockevents_switch_state(struct clock_event_device *dev,
|
|||
/* The clockevent device is getting replaced. Shut it down. */
|
||||
|
||||
case CLOCK_EVT_STATE_SHUTDOWN:
|
||||
return dev->set_state_shutdown(dev);
|
||||
if (dev->set_state_shutdown)
|
||||
return dev->set_state_shutdown(dev);
|
||||
return 0;
|
||||
|
||||
case CLOCK_EVT_STATE_PERIODIC:
|
||||
/* Core internal bug */
|
||||
if (!(dev->features & CLOCK_EVT_FEAT_PERIODIC))
|
||||
return -ENOSYS;
|
||||
return dev->set_state_periodic(dev);
|
||||
if (dev->set_state_periodic)
|
||||
return dev->set_state_periodic(dev);
|
||||
return 0;
|
||||
|
||||
case CLOCK_EVT_STATE_ONESHOT:
|
||||
/* Core internal bug */
|
||||
if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT))
|
||||
return -ENOSYS;
|
||||
return dev->set_state_oneshot(dev);
|
||||
if (dev->set_state_oneshot)
|
||||
return dev->set_state_oneshot(dev);
|
||||
return 0;
|
||||
|
||||
case CLOCK_EVT_STATE_ONESHOT_STOPPED:
|
||||
/* Core internal bug */
|
||||
|
@ -471,18 +477,6 @@ static int clockevents_sanity_check(struct clock_event_device *dev)
|
|||
if (dev->features & CLOCK_EVT_FEAT_DUMMY)
|
||||
return 0;
|
||||
|
||||
/* New state-specific callbacks */
|
||||
if (!dev->set_state_shutdown)
|
||||
return -EINVAL;
|
||||
|
||||
if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&
|
||||
!dev->set_state_periodic)
|
||||
return -EINVAL;
|
||||
|
||||
if ((dev->features & CLOCK_EVT_FEAT_ONESHOT) &&
|
||||
!dev->set_state_oneshot)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
|
|||
{
|
||||
struct clock_event_device *bc = tick_broadcast_device.evtdev;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
raw_spin_lock_irqsave(&tick_broadcast_lock, flags);
|
||||
|
||||
|
@ -221,13 +221,14 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
|
|||
* If we kept the cpu in the broadcast mask,
|
||||
* tell the caller to leave the per cpu device
|
||||
* in shutdown state. The periodic interrupt
|
||||
* is delivered by the broadcast device.
|
||||
* is delivered by the broadcast device, if
|
||||
* the broadcast device exists and is not
|
||||
* hrtimer based.
|
||||
*/
|
||||
ret = cpumask_test_cpu(cpu, tick_broadcast_mask);
|
||||
if (bc && !(bc->features & CLOCK_EVT_FEAT_HRTIMER))
|
||||
ret = cpumask_test_cpu(cpu, tick_broadcast_mask);
|
||||
break;
|
||||
default:
|
||||
/* Nothing to do */
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -265,8 +266,22 @@ static bool tick_do_broadcast(struct cpumask *mask)
|
|||
* Check, if the current cpu is in the mask
|
||||
*/
|
||||
if (cpumask_test_cpu(cpu, mask)) {
|
||||
struct clock_event_device *bc = tick_broadcast_device.evtdev;
|
||||
|
||||
cpumask_clear_cpu(cpu, mask);
|
||||
local = true;
|
||||
/*
|
||||
* We only run the local handler, if the broadcast
|
||||
* device is not hrtimer based. Otherwise we run into
|
||||
* a hrtimer recursion.
|
||||
*
|
||||
* local timer_interrupt()
|
||||
* local_handler()
|
||||
* expire_hrtimers()
|
||||
* bc_handler()
|
||||
* local_handler()
|
||||
* expire_hrtimers()
|
||||
*/
|
||||
local = !(bc->features & CLOCK_EVT_FEAT_HRTIMER);
|
||||
}
|
||||
|
||||
if (!cpumask_empty(mask)) {
|
||||
|
@ -301,6 +316,13 @@ static void tick_handle_periodic_broadcast(struct clock_event_device *dev)
|
|||
bool bc_local;
|
||||
|
||||
raw_spin_lock(&tick_broadcast_lock);
|
||||
|
||||
/* Handle spurious interrupts gracefully */
|
||||
if (clockevent_state_shutdown(tick_broadcast_device.evtdev)) {
|
||||
raw_spin_unlock(&tick_broadcast_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
bc_local = tick_do_periodic_broadcast();
|
||||
|
||||
if (clockevent_state_oneshot(dev)) {
|
||||
|
@ -359,8 +381,16 @@ void tick_broadcast_control(enum tick_broadcast_mode mode)
|
|||
case TICK_BROADCAST_ON:
|
||||
cpumask_set_cpu(cpu, tick_broadcast_on);
|
||||
if (!cpumask_test_and_set_cpu(cpu, tick_broadcast_mask)) {
|
||||
if (tick_broadcast_device.mode ==
|
||||
TICKDEV_MODE_PERIODIC)
|
||||
/*
|
||||
* Only shutdown the cpu local device, if:
|
||||
*
|
||||
* - the broadcast device exists
|
||||
* - the broadcast device is not a hrtimer based one
|
||||
* - the broadcast device is in periodic mode to
|
||||
* avoid a hickup during switch to oneshot mode
|
||||
*/
|
||||
if (bc && !(bc->features & CLOCK_EVT_FEAT_HRTIMER) &&
|
||||
tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC)
|
||||
clockevents_shutdown(dev);
|
||||
}
|
||||
break;
|
||||
|
@ -379,14 +409,16 @@ void tick_broadcast_control(enum tick_broadcast_mode mode)
|
|||
break;
|
||||
}
|
||||
|
||||
if (cpumask_empty(tick_broadcast_mask)) {
|
||||
if (!bc_stopped)
|
||||
clockevents_shutdown(bc);
|
||||
} else if (bc_stopped) {
|
||||
if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC)
|
||||
tick_broadcast_start_periodic(bc);
|
||||
else
|
||||
tick_broadcast_setup_oneshot(bc);
|
||||
if (bc) {
|
||||
if (cpumask_empty(tick_broadcast_mask)) {
|
||||
if (!bc_stopped)
|
||||
clockevents_shutdown(bc);
|
||||
} else if (bc_stopped) {
|
||||
if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC)
|
||||
tick_broadcast_start_periodic(bc);
|
||||
else
|
||||
tick_broadcast_setup_oneshot(bc);
|
||||
}
|
||||
}
|
||||
raw_spin_unlock(&tick_broadcast_lock);
|
||||
}
|
||||
|
@ -662,71 +694,82 @@ static void broadcast_shutdown_local(struct clock_event_device *bc,
|
|||
clockevents_switch_state(dev, CLOCK_EVT_STATE_SHUTDOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* tick_broadcast_oneshot_control - Enter/exit broadcast oneshot mode
|
||||
* @state: The target state (enter/exit)
|
||||
*
|
||||
* The system enters/leaves a state, where affected devices might stop
|
||||
* Returns 0 on success, -EBUSY if the cpu is used to broadcast wakeups.
|
||||
*
|
||||
* Called with interrupts disabled, so clockevents_lock is not
|
||||
* required here because the local clock event device cannot go away
|
||||
* under us.
|
||||
*/
|
||||
int tick_broadcast_oneshot_control(enum tick_broadcast_state state)
|
||||
int __tick_broadcast_oneshot_control(enum tick_broadcast_state state)
|
||||
{
|
||||
struct clock_event_device *bc, *dev;
|
||||
struct tick_device *td;
|
||||
int cpu, ret = 0;
|
||||
ktime_t now;
|
||||
|
||||
/*
|
||||
* Periodic mode does not care about the enter/exit of power
|
||||
* states
|
||||
* If there is no broadcast device, tell the caller not to go
|
||||
* into deep idle.
|
||||
*/
|
||||
if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC)
|
||||
return 0;
|
||||
if (!tick_broadcast_device.evtdev)
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* We are called with preemtion disabled from the depth of the
|
||||
* idle code, so we can't be moved away.
|
||||
*/
|
||||
td = this_cpu_ptr(&tick_cpu_device);
|
||||
dev = td->evtdev;
|
||||
|
||||
if (!(dev->features & CLOCK_EVT_FEAT_C3STOP))
|
||||
return 0;
|
||||
dev = this_cpu_ptr(&tick_cpu_device)->evtdev;
|
||||
|
||||
raw_spin_lock(&tick_broadcast_lock);
|
||||
bc = tick_broadcast_device.evtdev;
|
||||
cpu = smp_processor_id();
|
||||
|
||||
if (state == TICK_BROADCAST_ENTER) {
|
||||
/*
|
||||
* If the current CPU owns the hrtimer broadcast
|
||||
* mechanism, it cannot go deep idle and we do not add
|
||||
* the CPU to the broadcast mask. We don't have to go
|
||||
* through the EXIT path as the local timer is not
|
||||
* shutdown.
|
||||
*/
|
||||
ret = broadcast_needs_cpu(bc, cpu);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* If the broadcast device is in periodic mode, we
|
||||
* return.
|
||||
*/
|
||||
if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) {
|
||||
/* If it is a hrtimer based broadcast, return busy */
|
||||
if (bc->features & CLOCK_EVT_FEAT_HRTIMER)
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!cpumask_test_and_set_cpu(cpu, tick_broadcast_oneshot_mask)) {
|
||||
WARN_ON_ONCE(cpumask_test_cpu(cpu, tick_broadcast_pending_mask));
|
||||
|
||||
/* Conditionally shut down the local timer. */
|
||||
broadcast_shutdown_local(bc, dev);
|
||||
|
||||
/*
|
||||
* We only reprogram the broadcast timer if we
|
||||
* did not mark ourself in the force mask and
|
||||
* if the cpu local event is earlier than the
|
||||
* broadcast event. If the current CPU is in
|
||||
* the force mask, then we are going to be
|
||||
* woken by the IPI right away.
|
||||
* woken by the IPI right away; we return
|
||||
* busy, so the CPU does not try to go deep
|
||||
* idle.
|
||||
*/
|
||||
if (!cpumask_test_cpu(cpu, tick_broadcast_force_mask) &&
|
||||
dev->next_event.tv64 < bc->next_event.tv64)
|
||||
if (cpumask_test_cpu(cpu, tick_broadcast_force_mask)) {
|
||||
ret = -EBUSY;
|
||||
} else if (dev->next_event.tv64 < bc->next_event.tv64) {
|
||||
tick_broadcast_set_event(bc, cpu, dev->next_event);
|
||||
/*
|
||||
* In case of hrtimer broadcasts the
|
||||
* programming might have moved the
|
||||
* timer to this cpu. If yes, remove
|
||||
* us from the broadcast mask and
|
||||
* return busy.
|
||||
*/
|
||||
ret = broadcast_needs_cpu(bc, cpu);
|
||||
if (ret) {
|
||||
cpumask_clear_cpu(cpu,
|
||||
tick_broadcast_oneshot_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If the current CPU owns the hrtimer broadcast
|
||||
* mechanism, it cannot go deep idle and we remove the
|
||||
* CPU from the broadcast mask. We don't have to go
|
||||
* through the EXIT path as the local timer is not
|
||||
* shutdown.
|
||||
*/
|
||||
ret = broadcast_needs_cpu(bc, cpu);
|
||||
if (ret)
|
||||
cpumask_clear_cpu(cpu, tick_broadcast_oneshot_mask);
|
||||
} else {
|
||||
if (cpumask_test_and_clear_cpu(cpu, tick_broadcast_oneshot_mask)) {
|
||||
clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
|
||||
|
@ -938,6 +981,16 @@ bool tick_broadcast_oneshot_available(void)
|
|||
return bc ? bc->features & CLOCK_EVT_FEAT_ONESHOT : false;
|
||||
}
|
||||
|
||||
#else
|
||||
int __tick_broadcast_oneshot_control(enum tick_broadcast_state state)
|
||||
{
|
||||
struct clock_event_device *bc = tick_broadcast_device.evtdev;
|
||||
|
||||
if (!bc || (bc->features & CLOCK_EVT_FEAT_HRTIMER))
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void __init tick_broadcast_init(void)
|
||||
|
|
|
@ -343,6 +343,27 @@ void tick_check_new_device(struct clock_event_device *newdev)
|
|||
tick_install_broadcast_device(newdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* tick_broadcast_oneshot_control - Enter/exit broadcast oneshot mode
|
||||
* @state: The target state (enter/exit)
|
||||
*
|
||||
* The system enters/leaves a state, where affected devices might stop
|
||||
* Returns 0 on success, -EBUSY if the cpu is used to broadcast wakeups.
|
||||
*
|
||||
* Called with interrupts disabled, so clockevents_lock is not
|
||||
* required here because the local clock event device cannot go away
|
||||
* under us.
|
||||
*/
|
||||
int tick_broadcast_oneshot_control(enum tick_broadcast_state state)
|
||||
{
|
||||
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
|
||||
|
||||
if (!(td->evtdev->features & CLOCK_EVT_FEAT_C3STOP))
|
||||
return 0;
|
||||
|
||||
return __tick_broadcast_oneshot_control(state);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
/*
|
||||
* Transfer the do_timer job away from a dying cpu.
|
||||
|
|
|
@ -71,4 +71,14 @@ extern void tick_cancel_sched_timer(int cpu);
|
|||
static inline void tick_cancel_sched_timer(int cpu) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
|
||||
extern int __tick_broadcast_oneshot_control(enum tick_broadcast_state state);
|
||||
#else
|
||||
static inline int
|
||||
__tick_broadcast_oneshot_control(enum tick_broadcast_state state)
|
||||
{
|
||||
return -EBUSY;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue