counter: interrupt-cnt: Protect enable/disable OPs with mutex
[ Upstream commit 7351312632e831e51383f48957d47712fae791ef ]
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: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Fixes: a55ebd47f2
("counter: add IRQ or GPIO based counter")
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Link: https://lore.kernel.org/r/20250331163642.2382651-1-alexander.sverdlin@siemens.com
Signed-off-by: William Breathitt Gray <wbg@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
72166de0a1
commit
32b3f3382d
@@ -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");
|
||||
|
Reference in New Issue
Block a user