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:
Linus Torvalds 2015-07-12 09:36:59 -07:00
Родитель c4bc680cf7 c4d029f2d4
Коммит 7b732169e9
8 изменённых файлов: 155 добавлений и 74 удалений

Просмотреть файл

@ -1464,7 +1464,7 @@ static inline void handle_rx_packet(struct sync_port *port)
if (port->write_ts_idx == NBR_IN_DESCR) if (port->write_ts_idx == NBR_IN_DESCR)
port->write_ts_idx = 0; port->write_ts_idx = 0;
idx = port->write_ts_idx++; 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; port->in_buffer_len += port->inbufchunk;
} }
spin_unlock_irqrestore(&port->lock, flags); 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(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
CLOCKSOURCE_OF_DECLARE(imx21_timer, "fsl,imx21-gpt", imx21_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(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
CLOCKSOURCE_OF_DECLARE(imx25_timer, "fsl,imx25-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); 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) { } static inline void tick_broadcast_control(enum tick_broadcast_mode mode) { }
#endif /* BROADCAST */ #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); extern int tick_broadcast_oneshot_control(enum tick_broadcast_state state);
#else #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 #endif
static inline void tick_broadcast_enable(void) static inline void tick_broadcast_enable(void)

Просмотреть файл

@ -145,7 +145,6 @@ static inline void getboottime(struct timespec *ts)
} }
#endif #endif
#define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts)
#define ktime_get_real_ts64(ts) getnstimeofday64(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. */ /* The clockevent device is getting replaced. Shut it down. */
case CLOCK_EVT_STATE_SHUTDOWN: case CLOCK_EVT_STATE_SHUTDOWN:
if (dev->set_state_shutdown)
return dev->set_state_shutdown(dev); return dev->set_state_shutdown(dev);
return 0;
case CLOCK_EVT_STATE_PERIODIC: case CLOCK_EVT_STATE_PERIODIC:
/* Core internal bug */ /* Core internal bug */
if (!(dev->features & CLOCK_EVT_FEAT_PERIODIC)) if (!(dev->features & CLOCK_EVT_FEAT_PERIODIC))
return -ENOSYS; return -ENOSYS;
if (dev->set_state_periodic)
return dev->set_state_periodic(dev); return dev->set_state_periodic(dev);
return 0;
case CLOCK_EVT_STATE_ONESHOT: case CLOCK_EVT_STATE_ONESHOT:
/* Core internal bug */ /* Core internal bug */
if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT)) if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT))
return -ENOSYS; return -ENOSYS;
if (dev->set_state_oneshot)
return dev->set_state_oneshot(dev); return dev->set_state_oneshot(dev);
return 0;
case CLOCK_EVT_STATE_ONESHOT_STOPPED: case CLOCK_EVT_STATE_ONESHOT_STOPPED:
/* Core internal bug */ /* Core internal bug */
@ -471,18 +477,6 @@ static int clockevents_sanity_check(struct clock_event_device *dev)
if (dev->features & CLOCK_EVT_FEAT_DUMMY) if (dev->features & CLOCK_EVT_FEAT_DUMMY)
return 0; 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; 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; struct clock_event_device *bc = tick_broadcast_device.evtdev;
unsigned long flags; unsigned long flags;
int ret; int ret = 0;
raw_spin_lock_irqsave(&tick_broadcast_lock, flags); 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, * If we kept the cpu in the broadcast mask,
* tell the caller to leave the per cpu device * tell the caller to leave the per cpu device
* in shutdown state. The periodic interrupt * 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.
*/ */
if (bc && !(bc->features & CLOCK_EVT_FEAT_HRTIMER))
ret = cpumask_test_cpu(cpu, tick_broadcast_mask); ret = cpumask_test_cpu(cpu, tick_broadcast_mask);
break; break;
default: default:
/* Nothing to do */
ret = 0;
break; break;
} }
} }
@ -265,8 +266,22 @@ static bool tick_do_broadcast(struct cpumask *mask)
* Check, if the current cpu is in the mask * Check, if the current cpu is in the mask
*/ */
if (cpumask_test_cpu(cpu, mask)) { if (cpumask_test_cpu(cpu, mask)) {
struct clock_event_device *bc = tick_broadcast_device.evtdev;
cpumask_clear_cpu(cpu, mask); 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)) { if (!cpumask_empty(mask)) {
@ -301,6 +316,13 @@ static void tick_handle_periodic_broadcast(struct clock_event_device *dev)
bool bc_local; bool bc_local;
raw_spin_lock(&tick_broadcast_lock); 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(); bc_local = tick_do_periodic_broadcast();
if (clockevent_state_oneshot(dev)) { if (clockevent_state_oneshot(dev)) {
@ -359,8 +381,16 @@ void tick_broadcast_control(enum tick_broadcast_mode mode)
case TICK_BROADCAST_ON: case TICK_BROADCAST_ON:
cpumask_set_cpu(cpu, tick_broadcast_on); cpumask_set_cpu(cpu, tick_broadcast_on);
if (!cpumask_test_and_set_cpu(cpu, tick_broadcast_mask)) { 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); clockevents_shutdown(dev);
} }
break; break;
@ -379,6 +409,7 @@ void tick_broadcast_control(enum tick_broadcast_mode mode)
break; break;
} }
if (bc) {
if (cpumask_empty(tick_broadcast_mask)) { if (cpumask_empty(tick_broadcast_mask)) {
if (!bc_stopped) if (!bc_stopped)
clockevents_shutdown(bc); clockevents_shutdown(bc);
@ -388,6 +419,7 @@ void tick_broadcast_control(enum tick_broadcast_mode mode)
else else
tick_broadcast_setup_oneshot(bc); tick_broadcast_setup_oneshot(bc);
} }
}
raw_spin_unlock(&tick_broadcast_lock); raw_spin_unlock(&tick_broadcast_lock);
} }
EXPORT_SYMBOL_GPL(tick_broadcast_control); EXPORT_SYMBOL_GPL(tick_broadcast_control);
@ -662,71 +694,82 @@ static void broadcast_shutdown_local(struct clock_event_device *bc,
clockevents_switch_state(dev, CLOCK_EVT_STATE_SHUTDOWN); clockevents_switch_state(dev, CLOCK_EVT_STATE_SHUTDOWN);
} }
/** int __tick_broadcast_oneshot_control(enum tick_broadcast_state state)
* 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 clock_event_device *bc, *dev; struct clock_event_device *bc, *dev;
struct tick_device *td;
int cpu, ret = 0; int cpu, ret = 0;
ktime_t now; ktime_t now;
/* /*
* Periodic mode does not care about the enter/exit of power * If there is no broadcast device, tell the caller not to go
* states * into deep idle.
*/ */
if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) if (!tick_broadcast_device.evtdev)
return 0; return -EBUSY;
/* dev = this_cpu_ptr(&tick_cpu_device)->evtdev;
* 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;
raw_spin_lock(&tick_broadcast_lock); raw_spin_lock(&tick_broadcast_lock);
bc = tick_broadcast_device.evtdev; bc = tick_broadcast_device.evtdev;
cpu = smp_processor_id(); cpu = smp_processor_id();
if (state == TICK_BROADCAST_ENTER) { 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)) { if (!cpumask_test_and_set_cpu(cpu, tick_broadcast_oneshot_mask)) {
WARN_ON_ONCE(cpumask_test_cpu(cpu, tick_broadcast_pending_mask)); WARN_ON_ONCE(cpumask_test_cpu(cpu, tick_broadcast_pending_mask));
/* Conditionally shut down the local timer. */
broadcast_shutdown_local(bc, dev); broadcast_shutdown_local(bc, dev);
/* /*
* We only reprogram the broadcast timer if we * We only reprogram the broadcast timer if we
* did not mark ourself in the force mask and * did not mark ourself in the force mask and
* if the cpu local event is earlier than the * if the cpu local event is earlier than the
* broadcast event. If the current CPU is in * broadcast event. If the current CPU is in
* the force mask, then we are going to be * 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) && if (cpumask_test_cpu(cpu, tick_broadcast_force_mask)) {
dev->next_event.tv64 < bc->next_event.tv64) ret = -EBUSY;
} else if (dev->next_event.tv64 < bc->next_event.tv64) {
tick_broadcast_set_event(bc, cpu, dev->next_event); tick_broadcast_set_event(bc, cpu, dev->next_event);
}
/* /*
* If the current CPU owns the hrtimer broadcast * In case of hrtimer broadcasts the
* mechanism, it cannot go deep idle and we remove the * programming might have moved the
* CPU from the broadcast mask. We don't have to go * timer to this cpu. If yes, remove
* through the EXIT path as the local timer is not * us from the broadcast mask and
* shutdown. * return busy.
*/ */
ret = broadcast_needs_cpu(bc, cpu); ret = broadcast_needs_cpu(bc, cpu);
if (ret) if (ret) {
cpumask_clear_cpu(cpu, tick_broadcast_oneshot_mask); cpumask_clear_cpu(cpu,
tick_broadcast_oneshot_mask);
}
}
}
} else { } else {
if (cpumask_test_and_clear_cpu(cpu, tick_broadcast_oneshot_mask)) { if (cpumask_test_and_clear_cpu(cpu, tick_broadcast_oneshot_mask)) {
clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT); 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; 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 #endif
void __init tick_broadcast_init(void) void __init tick_broadcast_init(void)

Просмотреть файл

@ -343,6 +343,27 @@ out_bc:
tick_install_broadcast_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 #ifdef CONFIG_HOTPLUG_CPU
/* /*
* Transfer the do_timer job away from a dying 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) { } static inline void tick_cancel_sched_timer(int cpu) { }
#endif #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 #endif