Thread (34 messages) 34 messages, 2 authors, 2014-09-01

Re: [PATCH 2/5 v3] irq / PM: Make wakeup interrupts work with suspend-to-idle

flat view

From: Rafael J. Wysocki <hidden>
Date: 2014-08-27 22:32:01
Also in: linux-pci, lkml

On Wednesday, August 27, 2014 10:32:23 PM Thomas Gleixner wrote:
On Wed, 27 Aug 2014, Rafael J. Wysocki wrote:
quoted
The line of reasoning leading to that is as follows.

The way suspend_device_irqs() works and the existing code in
check_wakeup_irqs(), called by syscore_suspend(), imply that:

  (1) Interrupt handlers are not invoked for wakeup interrupts
      after suspend_device_irqs().

  (2) All interrups from system wakeup IRQs received after\
      suspend_device_irqs() cause full system suspends to be aborted.

In addition to the above, there is the requirement that

  (3) System wakeup interrupts should wake up the system from
      suspend-to-idle.

It immediately follows from (1) and (2) that no effort is made to
distinguish "genuine" wakeup interrupts from "spurious" ones.  They
all are treated in the same way.  Since (3) means that "genuine"
wakeup interrupts are supposed to wake up the system from
suspend-to-idle too, consistency with (1) and (2) requires that
"spurious" wakeup interrupts should do the same thing.  Thus there is
no reason to invoke interrupt handlers for wakeup interrups after
suspend_device_irqs() in the suspend-to-idle case.  Moreover, doing
so would go against rule (1).
I agree with that, but I disagree with the implementation.

We now have two separate mechanisms to abort suspend:

1) The existing suspend_device_irqs() / check_wakeup_irqs() 

2) The new suspend_device_irqs() /
   reenable_stuff_and_fiddle_with_irq_action()

So why do we need those two mechanisms in the first place?

AFAICT there is no reason why we cant use the abort_suspend mechanics
to replace the suspend_device_irqs() / check_wakeup_irqs() pair.

All it needs is to do the handler substitution in
suspend_device_irqs() right away and replace the loop in
check_wakeup_irqs() with a check for abort_suspend == true. The roll
back of the handler substitution can happen in resume_device_irqs()
for both scenarios.
We can do that of course.
Aside of that the whole irqaction based substitution is silly. What's
wrong with doing it at the real interrupt handler level?
Nothing I suppose. :-)
static void handle_wakeup_irq(unsigned int irq, struct irq_desc *desc)
{
	raw_spin_lock(&desc->lock);

	desc->istate |= IRQS_SUSPENDED | IRQS_PENDING;
	desc->depth++;
	irq_disable(desc);
	pm_system_wakeup();

	raw_spin_unlock(&desc->lock);
}

void suspend_device_irqs(void)
{
	for_each_irq_desc(irq, desc) {
		/* Disable the interrupt unconditionally */	       
		disable_irq(irq);
We still need to skip the IRQF_NO_SUSPEND stuff (eg. timers), so I guess
everything left disabled here needs to be IRQS_SUSPENDED, so we know which
ones to re-enable in resume_device_irqs().
		/* Is the irq a wakeup source? */
		if (!irqd_is_wakeup_set(&desc->irq_data))
			continue;

		/* Replace the handler */
		raw_spin_lock_irqsave(&desc->lock, flags);
	     	desc->saved_handler = desc->handler;
		desc->handler = handle_wakeup_irq;
Hmm.  There's no handler field in struct irq_desc (/me is puzzled).

Did you mean handle_irq (I think you did)?
		raw_spin_unlock_irqrestore(&desc->lock, flags);

		/* Reenable the wakeup irq */
		enable_irq(irq);
	}
}

/* Move that into the pm core code */
bool check_wakeup_irqs(void)
{
	return abort_suspend;
}

void resume_device_irqs(void)
{
	for_each_irq_desc(irq, desc) {

		/* Prevent the wakeup handler from running */
		disable_irq();

		raw_spin_lock_irqsave(&desc->lock, flags);

		/* Do we need to restore the handler? */
		if (desc->handler == handle_wakeup_irq)
		   	desc->handler = desc->saved_handler;

		/* Is the irq a wakeup source? */
		if (!irqd_is_wakeup_set(&desc->irq_data))
		   	__enable_irq(irq, desc);

		/* Did it get disabled in the wakeup handler? */
		else if (desc->istate & IRQS_SUSPENDED)
		   	__enable_irq(irq, desc);

		raw_spin_unlock_irqrestore(&desc->lock, flags);

		enable_irq();
	}
}

Hmm?
OK

There is quite some ugliness related to resume_irqs(), the want_early thing
and IRQF_EARLY_RESUME / IRQF_FORCE_RESUME.  I guess that needs to be preserved?
One thing we might think about is having flow specific
handle_wakeup_irq variants as some hardware might require an ack or
eoi, but that's a simple to solve problem and way simpler than
fiddling with the irqaction chain and avoids the whole mess of
sprinkling irq_pm_saved_id() and irq_pm_restore_handler() calls all
over the place. I wonder why you added them to __free_irq() at all,
but no, we dont want that.
I was concerned about the (unlikely) possibility of freeing an interrupt
having a temporary handler.  Never mind.

Rafael
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help