Commit 73513126 authored by Alexander Sverdlin's avatar Alexander Sverdlin Committed by William Breathitt Gray
Browse files

counter: interrupt-cnt: Protect enable/disable OPs with mutex



Enable/disable seems to be racy on SMP, consider the following scenario:

CPU0					CPU1

interrupt_cnt_enable_write(true)
{
	if (priv->enabled == enable)
		return 0;

	if (enable) {
		priv->enabled = true;
					interrupt_cnt_enable_write(false)
					{
						if (priv->enabled == enable)
							return 0;

						if (enable) {
							priv->enabled = true;
							enable_irq(priv->irq);
						} else {
							disable_irq(priv->irq)
							priv->enabled = false;
						}
		enable_irq(priv->irq);
	} else {
		disable_irq(priv->irq);
		priv->enabled = false;
	}

The above would result in priv->enabled == false, but IRQ left enabled.
Protect both write (above race) and read (to propagate the value on SMP)
callbacks with a mutex.

Signed-off-by: default avatarAlexander Sverdlin <alexander.sverdlin@siemens.com>
Fixes: a55ebd47 ("counter: add IRQ or GPIO based counter")
Acked-by: default avatarOleksij Rempel <o.rempel@pengutronix.de>
Link: https://lore.kernel.org/r/20250331163642.2382651-1-alexander.sverdlin@siemens.com


Signed-off-by: default avatarWilliam Breathitt Gray <wbg@kernel.org>
parent b4432656
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -3,12 +3,14 @@
 * Copyright (c) 2021 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
 */

#include <linux/cleanup.h>
#include <linux/counter.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/types.h>

@@ -19,6 +21,7 @@ struct interrupt_cnt_priv {
	struct gpio_desc *gpio;
	int irq;
	bool enabled;
	struct mutex lock;
	struct counter_signal signals;
	struct counter_synapse synapses;
	struct counter_count cnts;
@@ -41,6 +44,8 @@ static int interrupt_cnt_enable_read(struct counter_device *counter,
{
	struct interrupt_cnt_priv *priv = counter_priv(counter);

	guard(mutex)(&priv->lock);

	*enable = priv->enabled;

	return 0;
@@ -51,6 +56,8 @@ static int interrupt_cnt_enable_write(struct counter_device *counter,
{
	struct interrupt_cnt_priv *priv = counter_priv(counter);

	guard(mutex)(&priv->lock);

	if (priv->enabled == enable)
		return 0;

@@ -227,6 +234,8 @@ static int interrupt_cnt_probe(struct platform_device *pdev)
	if (ret)
		return ret;

	mutex_init(&priv->lock);

	ret = devm_counter_add(dev, counter);
	if (ret < 0)
		return dev_err_probe(dev, ret, "Failed to add counter\n");