Skip to content

Commit f4e9c82

Browse files
jwrdegoedeWim Van Sebroeck
authored andcommitted
watchdog: Add Locking support
This patch fixes some potential multithreading issues, despite only allowing one process to open the /dev/watchdog device, we can still get called multiple times at the same time, since a program could be using thread, or could share the fd after a fork. This causes 2 potential problems: 1) watchdog_start / open do an unlocked test_n_set / test_n_clear, if these 2 race, the watchdog could be stopped while the active bit indicates it is running or visa versa. 2) Most watchdog_dev drivers probably assume that only one watchdog-op will get called at a time, this is not necessary true atm. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
1 parent 7a87982 commit f4e9c82

File tree

4 files changed

+29
-0
lines changed

4 files changed

+29
-0
lines changed

Documentation/watchdog/watchdog-kernel-api.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ struct watchdog_device {
5050
unsigned int min_timeout;
5151
unsigned int max_timeout;
5252
void *driver_data;
53+
struct mutex lock;
5354
unsigned long status;
5455
};
5556

@@ -74,6 +75,7 @@ It contains following fields:
7475
* driver_data: a pointer to the drivers private data of a watchdog device.
7576
This data should only be accessed via the watchdog_set_drvdata and
7677
watchdog_get_drvdata routines.
78+
* lock: Mutex for WatchDog Timer Driver Core internal use only.
7779
* status: this field contains a number of status bits that give extra
7880
information about the status of the device (Like: is the watchdog timer
7981
running/active, is the nowayout bit set, is the device opened via

drivers/watchdog/watchdog_core.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ int watchdog_register_device(struct watchdog_device *wdd)
7979
* corrupted in a later stage then we expect a kernel panic!
8080
*/
8181

82+
mutex_init(&wdd->lock);
8283
id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL);
8384
if (id < 0)
8485
return id;

drivers/watchdog/watchdog_dev.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ static int watchdog_ping(struct watchdog_device *wddev)
6363
{
6464
int err = 0;
6565

66+
mutex_lock(&wddev->lock);
67+
6668
if (!watchdog_active(wddev))
6769
goto out_ping;
6870

@@ -72,6 +74,7 @@ static int watchdog_ping(struct watchdog_device *wddev)
7274
err = wddev->ops->start(wddev); /* restart watchdog */
7375

7476
out_ping:
77+
mutex_unlock(&wddev->lock);
7578
return err;
7679
}
7780

@@ -88,6 +91,8 @@ static int watchdog_start(struct watchdog_device *wddev)
8891
{
8992
int err = 0;
9093

94+
mutex_lock(&wddev->lock);
95+
9196
if (watchdog_active(wddev))
9297
goto out_start;
9398

@@ -96,6 +101,7 @@ static int watchdog_start(struct watchdog_device *wddev)
96101
set_bit(WDOG_ACTIVE, &wddev->status);
97102

98103
out_start:
104+
mutex_unlock(&wddev->lock);
99105
return err;
100106
}
101107

@@ -113,6 +119,8 @@ static int watchdog_stop(struct watchdog_device *wddev)
113119
{
114120
int err = 0;
115121

122+
mutex_lock(&wddev->lock);
123+
116124
if (!watchdog_active(wddev))
117125
goto out_stop;
118126

@@ -127,6 +135,7 @@ static int watchdog_stop(struct watchdog_device *wddev)
127135
clear_bit(WDOG_ACTIVE, &wddev->status);
128136

129137
out_stop:
138+
mutex_unlock(&wddev->lock);
130139
return err;
131140
}
132141

@@ -147,8 +156,11 @@ static int watchdog_get_status(struct watchdog_device *wddev,
147156
if (!wddev->ops->status)
148157
return -EOPNOTSUPP;
149158

159+
mutex_lock(&wddev->lock);
160+
150161
*status = wddev->ops->status(wddev);
151162

163+
mutex_unlock(&wddev->lock);
152164
return err;
153165
}
154166

@@ -171,8 +183,11 @@ static int watchdog_set_timeout(struct watchdog_device *wddev,
171183
(timeout < wddev->min_timeout || timeout > wddev->max_timeout))
172184
return -EINVAL;
173185

186+
mutex_lock(&wddev->lock);
187+
174188
err = wddev->ops->set_timeout(wddev, timeout);
175189

190+
mutex_unlock(&wddev->lock);
176191
return err;
177192
}
178193

@@ -193,8 +208,11 @@ static int watchdog_get_timeleft(struct watchdog_device *wddev,
193208
if (!wddev->ops->get_timeleft)
194209
return -EOPNOTSUPP;
195210

211+
mutex_lock(&wddev->lock);
212+
196213
*timeleft = wddev->ops->get_timeleft(wddev);
197214

215+
mutex_unlock(&wddev->lock);
198216
return err;
199217
}
200218

@@ -213,8 +231,11 @@ static int watchdog_ioctl_op(struct watchdog_device *wddev, unsigned int cmd,
213231
if (!wddev->ops->ioctl)
214232
return -ENOIOCTLCMD;
215233

234+
mutex_lock(&wddev->lock);
235+
216236
err = wddev->ops->ioctl(wddev, cmd, arg);
217237

238+
mutex_unlock(&wddev->lock);
218239
return err;
219240
}
220241

include/linux/watchdog.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,17 @@ struct watchdog_ops {
104104
* @min_timeout:The watchdog devices minimum timeout value.
105105
* @max_timeout:The watchdog devices maximum timeout value.
106106
* @driver-data:Pointer to the drivers private data.
107+
* @lock: Lock for watchdog core internal use only.
107108
* @status: Field that contains the devices internal status bits.
108109
*
109110
* The watchdog_device structure contains all information about a
110111
* watchdog timer device.
111112
*
112113
* The driver-data field may not be accessed directly. It must be accessed
113114
* via the watchdog_set_drvdata and watchdog_get_drvdata helpers.
115+
*
116+
* The lock field is for watchdog core internal use only and should not be
117+
* touched.
114118
*/
115119
struct watchdog_device {
116120
int id;
@@ -124,6 +128,7 @@ struct watchdog_device {
124128
unsigned int min_timeout;
125129
unsigned int max_timeout;
126130
void *driver_data;
131+
struct mutex lock;
127132
unsigned long status;
128133
/* Bit numbers for status flags */
129134
#define WDOG_ACTIVE 0 /* Is the watchdog running/active */

0 commit comments

Comments
 (0)