mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] iio: ssp: Serialize watchdog timer state changes
@ 2026-09-30  7:03 Runyu Xiao
  2026-09-30  9:58 ` Andy Shevchenko
  0 siblings, 1 reply; 7+ messages in thread
From: Runyu Xiao @ 2026-09-30  7:03 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: David Lechner, Nuno Sá,
	Andy Shevchenko, Karol Wrona, Kyungmin Park, linux-iio,
	linux-kernel, stable, Runyu Xiao, Jianhao Xu

The SSP watchdog timer rearms itself from its callback, but the driver uses
timer_delete_sync() when the last sensor is disabled and during suspend.
Those operations do not prevent a concurrent enable or callback from
rearming the timer after the deletion has completed.  The final remove path
also used timer_delete_sync(), which does not provide the shutdown
guarantee needed before releasing the device state.

Protect the watchdog state and enable reference count with a mutex.  The
callback checks a state flag before rearming. Reusable stops clear the flag
before deleting the timer. Use timer_shutdown_sync() for the final remove
path so that any later rearm attempt is rejected permanently.

Cancel watchdog work after releasing wdt_lock because the reset work can
wait for the threaded IRQ handler, which may synchronously wait for refresh
work that re-enables sensors and takes wdt_lock. Remove the MFD children
before destroying the locks because IIO child teardown can disable an
active sensor.

Fixes: 50dd64d57eee ("iio: common: ssp_sensors: Add sensorhub driver")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
 drivers/iio/common/ssp_sensors/ssp.h     |  4 ++
 drivers/iio/common/ssp_sensors/ssp_dev.c | 56 ++++++++++++++++++++----
 2 files changed, 51 insertions(+), 9 deletions(-)

diff --git a/drivers/iio/common/ssp_sensors/ssp.h b/drivers/iio/common/ssp_sensors/ssp.h
index f649cdecc2774..2aa70eff06fc4 100644
--- a/drivers/iio/common/ssp_sensors/ssp.h
+++ b/drivers/iio/common/ssp_sensors/ssp.h
@@ -143,6 +143,8 @@ struct ssp_sensorhub_info {
  * @spi:		spi device
  * @sensorhub_info:	info about sensorhub board specific features
  * @wdt_timer:		watchdog timer
+ * @wdt_lock:		lock protecting watchdog timer state
+ * @wdt_enabled:		watchdog timer is allowed to rearm
  * @work_wdt:		watchdog work
  * @work_firmware:	firmware upgrade work queue
  * @work_refresh:	refresh work queue for reset request from MCU
@@ -180,6 +182,8 @@ struct ssp_data {
 	struct spi_device *spi;
 	const struct ssp_sensorhub_info *sensorhub_info;
 	struct timer_list wdt_timer;
+	struct mutex wdt_lock; /* protects watchdog timer state */
+	bool wdt_enabled;
 	struct work_struct work_wdt;
 	struct delayed_work work_refresh;
 
diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c
index 828fcfe1d4f10..44dfafcaeff71 100644
--- a/drivers/iio/common/ssp_sensors/ssp_dev.c
+++ b/drivers/iio/common/ssp_sensors/ssp_dev.c
@@ -179,17 +179,46 @@ static void ssp_wdt_timer_func(struct timer_list *t)
 	    data->com_fail_cnt > SSP_LIMIT_RESET_CNT)
 		queue_work(system_power_efficient_wq, &data->work_wdt);
 _mod:
+	if (READ_ONCE(data->wdt_enabled))
+		mod_timer(&data->wdt_timer,
+			  jiffies + msecs_to_jiffies(SSP_WDT_TIME));
+}
+
+static void __ssp_enable_wdt_timer(struct ssp_data *data)
+{
+	WRITE_ONCE(data->wdt_enabled, true);
 	mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
 }
 
+static void __ssp_stop_wdt_timer(struct ssp_data *data, bool shutdown)
+{
+	WRITE_ONCE(data->wdt_enabled, false);
+	if (shutdown)
+		timer_shutdown_sync(&data->wdt_timer);
+	else
+		timer_delete_sync(&data->wdt_timer);
+}
+
 static void ssp_enable_wdt_timer(struct ssp_data *data)
 {
-	mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
+	mutex_lock(&data->wdt_lock);
+	__ssp_enable_wdt_timer(data);
+	mutex_unlock(&data->wdt_lock);
 }
 
 static void ssp_disable_wdt_timer(struct ssp_data *data)
 {
-	timer_delete_sync(&data->wdt_timer);
+	mutex_lock(&data->wdt_lock);
+	__ssp_stop_wdt_timer(data, false);
+	mutex_unlock(&data->wdt_lock);
+	cancel_work_sync(&data->work_wdt);
+}
+
+static void ssp_shutdown_wdt_timer(struct ssp_data *data)
+{
+	mutex_lock(&data->wdt_lock);
+	__ssp_stop_wdt_timer(data, true);
+	mutex_unlock(&data->wdt_lock);
 	cancel_work_sync(&data->work_wdt);
 }
 
@@ -258,8 +287,10 @@ int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type,
 
 	data->delay_buf[type] = delay;
 
+	mutex_lock(&data->wdt_lock);
 	if (atomic_inc_return(&data->enable_refcount) == 1)
-		ssp_enable_wdt_timer(data);
+		__ssp_enable_wdt_timer(data);
+	mutex_unlock(&data->wdt_lock);
 
 	return 0;
 
@@ -310,6 +341,7 @@ EXPORT_SYMBOL_NS(ssp_change_delay, "IIO_SSP_SENSORS");
 int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type)
 {
 	int ret;
+	bool stop_wdt = false;
 	__le32 command;
 
 	if (data->sensor_enable & BIT(type)) {
@@ -329,8 +361,14 @@ int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type)
 
 	data->check_status[type] = SSP_ADD_SENSOR_STATE;
 
+	mutex_lock(&data->wdt_lock);
 	if (atomic_dec_and_test(&data->enable_refcount))
-		ssp_disable_wdt_timer(data);
+		stop_wdt = true;
+	if (stop_wdt)
+		__ssp_stop_wdt_timer(data, false);
+	mutex_unlock(&data->wdt_lock);
+	if (stop_wdt)
+		cancel_work_sync(&data->work_wdt);
 
 	return 0;
 }
@@ -510,6 +548,7 @@ static int ssp_probe(struct spi_device *spi)
 	spi_set_drvdata(spi, data);
 
 	mutex_init(&data->comm_lock);
+	mutex_init(&data->wdt_lock);
 
 	for (i = 0; i < SSP_SENSOR_MAX; ++i) {
 		data->delay_buf[i] = SSP_DEFAULT_POLLING_DELAY;
@@ -566,6 +605,7 @@ static int ssp_probe(struct spi_device *spi)
 	free_irq(data->spi->irq, data);
 err_setup_irq:
 	mutex_destroy(&data->pending_lock);
+	mutex_destroy(&data->wdt_lock);
 	mutex_destroy(&data->comm_lock);
 err_setup_spi:
 	mfd_remove_devices(&spi->dev);
@@ -584,20 +624,18 @@ static void ssp_remove(struct spi_device *spi)
 			"SSP_MSG2SSP_AP_STATUS_SHUTDOWN failed\n");
 
 	ssp_enable_mcu(data, false);
-	ssp_disable_wdt_timer(data);
+	ssp_shutdown_wdt_timer(data);
 
 	ssp_clean_pending_list(data);
 
 	free_irq(data->spi->irq, data);
 	cancel_delayed_work_sync(&data->work_refresh);
 
-	timer_delete_sync(&data->wdt_timer);
-	cancel_work_sync(&data->work_wdt);
+	mfd_remove_devices(&spi->dev);
 
 	mutex_destroy(&data->comm_lock);
+	mutex_destroy(&data->wdt_lock);
 	mutex_destroy(&data->pending_lock);
-
-	mfd_remove_devices(&spi->dev);
 }
 
 static int ssp_suspend(struct device *dev)
-- 
2.34.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-10-04  9:32 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-30  7:03 [PATCH] iio: ssp: Serialize watchdog timer state changes Runyu Xiao
2026-09-30  9:58 ` Andy Shevchenko
2026-10-04  5:39   ` Runyu Xiao
2026-10-04  8:37     ` Andriy Shevchenko
2026-10-04  9:31       ` Runyu Xiao
2026-10-04  5:39   ` [PATCH v2] " Runyu Xiao
2026-10-04  8:40     ` Andriy Shevchenko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®