diff options
author | Hans de Goede <hdegoede@redhat.com> | 2012-05-22 05:40:26 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2012-05-30 01:55:23 -0400 |
commit | f4e9c82f64b524314a390b13d3ba7d483f09258f (patch) | |
tree | 82d688ae7782234dc01c6a340596bac21531aae4 | |
parent | 7a87982420e5e126bfefeb42232d1fd92052794e (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.txt | 2 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_dev.c | 21 | ||||
-rw-r--r-- | include/linux/watchdog.h | 5 |
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 | ||
74 | out_ping: | 76 | out_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 | ||
98 | out_start: | 103 | out_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 | ||
129 | out_stop: | 137 | out_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 | */ |
115 | struct watchdog_device { | 119 | struct 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 */ |