Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a7abbe4f authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "hrtimer: make sure PINNED flag is cleared after removing hrtimer"

parents e7dbaac3 55efbbcc
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ enum hrtimer_restart {
 *
 * 0x00		inactive
 * 0x01		enqueued into rbtree
 * 0x02		timer is pinned to a cpu
 *
 * The callback state is not part of the timer->state because clearing it would
 * mean touching the timer after the callback, this makes it impossible to free
@@ -92,6 +93,8 @@ enum hrtimer_restart {
 */
#define HRTIMER_STATE_INACTIVE	0x00
#define HRTIMER_STATE_ENQUEUED	0x01
#define HRTIMER_PINNED_SHIFT	1
#define HRTIMER_STATE_PINNED	(1 << HRTIMER_PINNED_SHIFT)

/**
 * struct hrtimer - the basic hrtimer structure
@@ -371,6 +374,9 @@ static inline void hrtimer_cancel_wait_running(struct hrtimer *timer)

/* Exported timer functions: */

/* To be used from cpusets, only */
extern void hrtimer_quiesce_cpu(void *cpup);

/* Initialize timers: */
extern void hrtimer_init(struct hrtimer *timer, clockid_t which_clock,
			 enum hrtimer_mode mode);
+59 −16
Original line number Diff line number Diff line
@@ -966,7 +966,7 @@ static int enqueue_hrtimer(struct hrtimer *timer,

	base->cpu_base->active_bases |= 1 << base->index;

	timer->state = HRTIMER_STATE_ENQUEUED;
	timer->state |= HRTIMER_STATE_ENQUEUED;

	return timerqueue_add(&base->active, &timer->node);
}
@@ -986,11 +986,9 @@ static void __remove_hrtimer(struct hrtimer *timer,
			     u8 newstate, int reprogram)
{
	struct hrtimer_cpu_base *cpu_base = base->cpu_base;
	u8 state = timer->state;

	timer->state = newstate;
	if (!(state & HRTIMER_STATE_ENQUEUED))
		return;
	if (!(timer->state & HRTIMER_STATE_ENQUEUED))
		goto out;

	if (!timerqueue_del(&base->active, &timer->node))
		cpu_base->active_bases &= ~(1 << base->index);
@@ -1005,6 +1003,13 @@ static void __remove_hrtimer(struct hrtimer *timer,
	 */
	if (reprogram && timer == cpu_base->next_timer)
		hrtimer_force_reprogram(cpu_base, 1);

out:
	/*
	* We need to preserve PINNED state here, otherwise we may end up
	* migrating pinned hrtimers as well.
	*/
	timer->state = newstate | (timer->state & HRTIMER_STATE_PINNED);
}

/*
@@ -1032,6 +1037,7 @@ remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base, bool rest
			state = HRTIMER_STATE_INACTIVE;

		__remove_hrtimer(timer, base, state, reprogram);
		timer->state &= ~HRTIMER_STATE_PINNED;
		return 1;
	}
	return 0;
@@ -1097,6 +1103,10 @@ static int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
	/* Switch the timer base, if necessary: */
	new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED);

	/* Update pinned state */
	timer->state &= ~HRTIMER_STATE_PINNED;
	timer->state |= (!!(mode & HRTIMER_MODE_PINNED)) << HRTIMER_PINNED_SHIFT;

	return enqueue_hrtimer(timer, new_base, mode);
}

@@ -2004,13 +2014,17 @@ int hrtimers_prepare_cpu(unsigned int cpu)
	return 0;
}

#ifdef CONFIG_HOTPLUG_CPU

#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_CPUSETS)
static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
				struct hrtimer_clock_base *new_base)
				 struct hrtimer_clock_base *new_base,
				 bool remove_pinned)
{
	struct hrtimer *timer;
	struct timerqueue_node *node;
	struct timerqueue_head pinned;
	int is_pinned;

	timerqueue_init_head(&pinned);

	while ((node = timerqueue_getnext(&old_base->active))) {
		timer = container_of(node, struct hrtimer, node);
@@ -2023,6 +2037,13 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
		 * under us on another CPU
		 */
		__remove_hrtimer(timer, old_base, HRTIMER_STATE_ENQUEUED, 0);

		is_pinned = timer->state & HRTIMER_STATE_PINNED;
		if (!remove_pinned && is_pinned) {
			timerqueue_add(&pinned, &timer->node);
			continue;
		}

		timer->base = new_base;
		/*
		 * Enqueue the timers on the new cpu. This does not
@@ -2034,23 +2055,29 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
		 */
		enqueue_hrtimer(timer, new_base, HRTIMER_MODE_ABS);
	}

	/* Re-queue pinned timers for non-hotplug usecase */
	while ((node = timerqueue_getnext(&pinned))) {
		timer = container_of(node, struct hrtimer, node);

		timerqueue_del(&pinned, &timer->node);
		enqueue_hrtimer(timer, old_base, HRTIMER_MODE_ABS);
	}
}

int hrtimers_dead_cpu(unsigned int scpu)
static void __migrate_hrtimers(unsigned int scpu, bool remove_pinned)
{
	struct hrtimer_cpu_base *old_base, *new_base;
	unsigned long flags;
	int i;

	BUG_ON(cpu_online(scpu));
	tick_cancel_sched_timer(scpu);

	/*
	 * this BH disable ensures that raise_softirq_irqoff() does
	 * not wakeup ksoftirqd (and acquire the pi-lock) while
	 * holding the cpu_base lock
	 */
	local_bh_disable();
	local_irq_disable();
	local_irq_save(flags);
	old_base = &per_cpu(hrtimer_bases, scpu);
	new_base = this_cpu_ptr(&hrtimer_bases);
	/*
@@ -2062,7 +2089,7 @@ int hrtimers_dead_cpu(unsigned int scpu)

	for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
		migrate_hrtimer_list(&old_base->clock_base[i],
				     &new_base->clock_base[i]);
				     &new_base->clock_base[i], remove_pinned);
	}

	/*
@@ -2076,13 +2103,29 @@ int hrtimers_dead_cpu(unsigned int scpu)

	/* Check, if we got expired work to do */
	__hrtimer_peek_ahead_timers();
	local_irq_enable();
	local_irq_restore(flags);
	local_bh_enable();
	return 0;
}
#endif /* CONFIG_HOTPLUG_CPU || CONFIG_CPUSETS */

#ifdef CONFIG_HOTPLUG_CPU
int hrtimers_dead_cpu(unsigned int scpu)
{
	BUG_ON(cpu_online(scpu));
	tick_cancel_sched_timer(scpu);

	__migrate_hrtimers(scpu, true);
	return 0;
}
#endif /* CONFIG_HOTPLUG_CPU */

#ifdef CONFIG_CPUSETS
void hrtimer_quiesce_cpu(void *cpup)
{
	__migrate_hrtimers(*(int *)cpup, false);
}
#endif /* CONFIG_CPUSETS */

void __init hrtimers_init(void)
{
	hrtimers_prepare_cpu(smp_processor_id());