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

Commit 6d3dab7d authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

PM / wakeirq: Avoid setting power.wakeirq too hastily



If dev_pm_attach_wake_irq() fails, the device's power.wakeirq field
should not be set to point to the struct wake_irq passed to that
function, as that object will be freed going forward.

For this reason, make dev_pm_attach_wake_irq() first call
device_wakeup_attach_irq() and only set the device's power.wakeirq
field if that's successful.

That requires device_wakeup_attach_irq() to be called under the
device's power.lock lock, but since dev_pm_attach_wake_irq() is
the only caller of it, the requisite changes are easy to make.

Fixes: 4990d4fe (PM / Wakeirq: Add automated device wake IRQ handling)
Reported-by: default avatarFelipe Balbi <balbi@ti.com>
Tested-by: default avatarTony Lindgren <tony@atomide.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent db874c7e
Loading
Loading
Loading
Loading
+5 −7
Original line number Original line Diff line number Diff line
@@ -45,14 +45,12 @@ static int dev_pm_attach_wake_irq(struct device *dev, int irq,
		return -EEXIST;
		return -EEXIST;
	}
	}


	err = device_wakeup_attach_irq(dev, wirq);
	if (!err)
		dev->power.wakeirq = wirq;
		dev->power.wakeirq = wirq;
	spin_unlock_irqrestore(&dev->power.lock, flags);


	err = device_wakeup_attach_irq(dev, wirq);
	spin_unlock_irqrestore(&dev->power.lock, flags);
	if (err)
	return err;
	return err;

	return 0;
}
}


/**
/**
@@ -105,10 +103,10 @@ void dev_pm_clear_wake_irq(struct device *dev)
		return;
		return;


	spin_lock_irqsave(&dev->power.lock, flags);
	spin_lock_irqsave(&dev->power.lock, flags);
	device_wakeup_detach_irq(dev);
	dev->power.wakeirq = NULL;
	dev->power.wakeirq = NULL;
	spin_unlock_irqrestore(&dev->power.lock, flags);
	spin_unlock_irqrestore(&dev->power.lock, flags);


	device_wakeup_detach_irq(dev);
	if (wirq->dedicated_irq)
	if (wirq->dedicated_irq)
		free_irq(wirq->irq, wirq);
		free_irq(wirq->irq, wirq);
	kfree(wirq);
	kfree(wirq);
+10 −21
Original line number Original line Diff line number Diff line
@@ -247,32 +247,25 @@ EXPORT_SYMBOL_GPL(device_wakeup_enable);
 * Attach a device wakeirq to the wakeup source so the device
 * Attach a device wakeirq to the wakeup source so the device
 * wake IRQ can be configured automatically for suspend and
 * wake IRQ can be configured automatically for suspend and
 * resume.
 * resume.
 *
 * Call under the device's power.lock lock.
 */
 */
int device_wakeup_attach_irq(struct device *dev,
int device_wakeup_attach_irq(struct device *dev,
			     struct wake_irq *wakeirq)
			     struct wake_irq *wakeirq)
{
{
	struct wakeup_source *ws;
	struct wakeup_source *ws;
	int ret = 0;


	spin_lock_irq(&dev->power.lock);
	ws = dev->power.wakeup;
	ws = dev->power.wakeup;
	if (!ws) {
	if (!ws) {
		dev_err(dev, "forgot to call call device_init_wakeup?\n");
		dev_err(dev, "forgot to call call device_init_wakeup?\n");
		ret = -EINVAL;
		return -EINVAL;
		goto unlock;
	}
	}


	if (ws->wakeirq) {
	if (ws->wakeirq)
		ret = -EEXIST;
		return -EEXIST;
		goto unlock;
	}


	ws->wakeirq = wakeirq;
	ws->wakeirq = wakeirq;

	return 0;
unlock:
	spin_unlock_irq(&dev->power.lock);

	return ret;
}
}


/**
/**
@@ -280,20 +273,16 @@ int device_wakeup_attach_irq(struct device *dev,
 * @dev: Device to handle
 * @dev: Device to handle
 *
 *
 * Removes a device wakeirq from the wakeup source.
 * Removes a device wakeirq from the wakeup source.
 *
 * Call under the device's power.lock lock.
 */
 */
void device_wakeup_detach_irq(struct device *dev)
void device_wakeup_detach_irq(struct device *dev)
{
{
	struct wakeup_source *ws;
	struct wakeup_source *ws;


	spin_lock_irq(&dev->power.lock);
	ws = dev->power.wakeup;
	ws = dev->power.wakeup;
	if (!ws)
	if (ws)
		goto unlock;

		ws->wakeirq = NULL;
		ws->wakeirq = NULL;

unlock:
	spin_unlock_irq(&dev->power.lock);
}
}


/**
/**