aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2012-05-22 05:40:26 -0400
committerWim Van Sebroeck <wim@iguana.be>2012-05-30 01:55:23 -0400
commitf4e9c82f64b524314a390b13d3ba7d483f09258f (patch)
tree82d688ae7782234dc01c6a340596bac21531aae4
parent7a87982420e5e126bfefeb42232d1fd92052794e (diff)
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>
-rw-r--r--Documentation/watchdog/watchdog-kernel-api.txt2
-rw-r--r--drivers/watchdog/watchdog_core.c1
-rw-r--r--drivers/watchdog/watchdog_dev.c21
-rw-r--r--include/linux/watchdog.h5
4 files changed, 29 insertions, 0 deletions
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt
index ce1fa22aa70b..08d34e11bc54 100644
--- a/Documentation/watchdog/watchdog-kernel-api.txt
+++ b/Documentation/watchdog/watchdog-kernel-api.txt
@@ -50,6 +50,7 @@ struct watchdog_device {
50 unsigned int min_timeout; 50 unsigned int min_timeout;
51 unsigned int max_timeout; 51 unsigned int max_timeout;
52 void *driver_data; 52 void *driver_data;
53 struct mutex lock;
53 unsigned long status; 54 unsigned long status;
54}; 55};
55 56
@@ -74,6 +75,7 @@ It contains following fields:
74* driver_data: a pointer to the drivers private data of a watchdog device. 75* driver_data: a pointer to the drivers private data of a watchdog device.
75 This data should only be accessed via the watchdog_set_drvdata and 76 This data should only be accessed via the watchdog_set_drvdata and
76 watchdog_get_drvdata routines. 77 watchdog_get_drvdata routines.
78* lock: Mutex for WatchDog Timer Driver Core internal use only.
77* status: this field contains a number of status bits that give extra 79* status: this field contains a number of status bits that give extra
78 information about the status of the device (Like: is the watchdog timer 80 information about the status of the device (Like: is the watchdog timer
79 running/active, is the nowayout bit set, is the device opened via 81 running/active, is the nowayout bit set, is the device opened via
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index 86a57673abf9..6aa46a90ff02 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -79,6 +79,7 @@ int watchdog_register_device(struct watchdog_device *wdd)
79 * corrupted in a later stage then we expect a kernel panic! 79 * corrupted in a later stage then we expect a kernel panic!
80 */ 80 */
81 81
82 mutex_init(&wdd->lock);
82 id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL); 83 id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL);
83 if (id < 0) 84 if (id < 0)
84 return id; 85 return id;
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 76d2572fed25..4d295d229a07 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -63,6 +63,8 @@ static int watchdog_ping(struct watchdog_device *wddev)
63{ 63{
64 int err = 0; 64 int err = 0;
65 65
66 mutex_lock(&wddev->lock);
67
66 if (!watchdog_active(wddev)) 68 if (!watchdog_active(wddev))
67 goto out_ping; 69 goto out_ping;
68 70
@@ -72,6 +74,7 @@ static int watchdog_ping(struct watchdog_device *wddev)
72 err = wddev->ops->start(wddev); /* restart watchdog */ 74 err = wddev->ops->start(wddev); /* restart watchdog */
73 75
74out_ping: 76out_ping:
77 mutex_unlock(&wddev->lock);
75 return err; 78 return err;
76} 79}
77 80
@@ -88,6 +91,8 @@ static int watchdog_start(struct watchdog_device *wddev)
88{ 91{
89 int err = 0; 92 int err = 0;
90 93
94 mutex_lock(&wddev->lock);
95
91 if (watchdog_active(wddev)) 96 if (watchdog_active(wddev))
92 goto out_start; 97 goto out_start;
93 98
@@ -96,6 +101,7 @@ static int watchdog_start(struct watchdog_device *wddev)
96 set_bit(WDOG_ACTIVE, &wddev->status); 101 set_bit(WDOG_ACTIVE, &wddev->status);
97 102
98out_start: 103out_start:
104 mutex_unlock(&wddev->lock);
99 return err; 105 return err;
100} 106}
101 107
@@ -113,6 +119,8 @@ static int watchdog_stop(struct watchdog_device *wddev)
113{ 119{
114 int err = 0; 120 int err = 0;
115 121
122 mutex_lock(&wddev->lock);
123
116 if (!watchdog_active(wddev)) 124 if (!watchdog_active(wddev))
117 goto out_stop; 125 goto out_stop;
118 126
@@ -127,6 +135,7 @@ static int watchdog_stop(struct watchdog_device *wddev)
127 clear_bit(WDOG_ACTIVE, &wddev->status); 135 clear_bit(WDOG_ACTIVE, &wddev->status);
128 136
129out_stop: 137out_stop:
138 mutex_unlock(&wddev->lock);
130 return err; 139 return err;
131} 140}
132 141
@@ -147,8 +156,11 @@ static int watchdog_get_status(struct watchdog_device *wddev,
147 if (!wddev->ops->status) 156 if (!wddev->ops->status)
148 return -EOPNOTSUPP; 157 return -EOPNOTSUPP;
149 158
159 mutex_lock(&wddev->lock);
160
150 *status = wddev->ops->status(wddev); 161 *status = wddev->ops->status(wddev);
151 162
163 mutex_unlock(&wddev->lock);
152 return err; 164 return err;
153} 165}
154 166
@@ -171,8 +183,11 @@ static int watchdog_set_timeout(struct watchdog_device *wddev,
171 (timeout < wddev->min_timeout || timeout > wddev->max_timeout)) 183 (timeout < wddev->min_timeout || timeout > wddev->max_timeout))
172 return -EINVAL; 184 return -EINVAL;
173 185
186 mutex_lock(&wddev->lock);
187
174 err = wddev->ops->set_timeout(wddev, timeout); 188 err = wddev->ops->set_timeout(wddev, timeout);
175 189
190 mutex_unlock(&wddev->lock);
176 return err; 191 return err;
177} 192}
178 193
@@ -193,8 +208,11 @@ static int watchdog_get_timeleft(struct watchdog_device *wddev,
193 if (!wddev->ops->get_timeleft) 208 if (!wddev->ops->get_timeleft)
194 return -EOPNOTSUPP; 209 return -EOPNOTSUPP;
195 210
211 mutex_lock(&wddev->lock);
212
196 *timeleft = wddev->ops->get_timeleft(wddev); 213 *timeleft = wddev->ops->get_timeleft(wddev);
197 214
215 mutex_unlock(&wddev->lock);
198 return err; 216 return err;
199} 217}
200 218
@@ -213,8 +231,11 @@ static int watchdog_ioctl_op(struct watchdog_device *wddev, unsigned int cmd,
213 if (!wddev->ops->ioctl) 231 if (!wddev->ops->ioctl)
214 return -ENOIOCTLCMD; 232 return -ENOIOCTLCMD;
215 233
234 mutex_lock(&wddev->lock);
235
216 err = wddev->ops->ioctl(wddev, cmd, arg); 236 err = wddev->ops->ioctl(wddev, cmd, arg);
217 237
238 mutex_unlock(&wddev->lock);
218 return err; 239 return err;
219} 240}
220 241
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
index c3545c5d918a..da1dc1b52744 100644
--- a/include/linux/watchdog.h
+++ b/include/linux/watchdog.h
@@ -104,6 +104,7 @@ struct watchdog_ops {
104 * @min_timeout:The watchdog devices minimum timeout value. 104 * @min_timeout:The watchdog devices minimum timeout value.
105 * @max_timeout:The watchdog devices maximum timeout value. 105 * @max_timeout:The watchdog devices maximum timeout value.
106 * @driver-data:Pointer to the drivers private data. 106 * @driver-data:Pointer to the drivers private data.
107 * @lock: Lock for watchdog core internal use only.
107 * @status: Field that contains the devices internal status bits. 108 * @status: Field that contains the devices internal status bits.
108 * 109 *
109 * The watchdog_device structure contains all information about a 110 * The watchdog_device structure contains all information about a
@@ -111,6 +112,9 @@ struct watchdog_ops {
111 * 112 *
112 * The driver-data field may not be accessed directly. It must be accessed 113 * The driver-data field may not be accessed directly. It must be accessed
113 * via the watchdog_set_drvdata and watchdog_get_drvdata helpers. 114 * 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.
114 */ 118 */
115struct watchdog_device { 119struct watchdog_device {
116 int id; 120 int id;
@@ -124,6 +128,7 @@ struct watchdog_device {
124 unsigned int min_timeout; 128 unsigned int min_timeout;
125 unsigned int max_timeout; 129 unsigned int max_timeout;
126 void *driver_data; 130 void *driver_data;
131 struct mutex lock;
127 unsigned long status; 132 unsigned long status;
128/* Bit numbers for status flags */ 133/* Bit numbers for status flags */
129#define WDOG_ACTIVE 0 /* Is the watchdog running/active */ 134#define WDOG_ACTIVE 0 /* Is the watchdog running/active */