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>
This commit is contained in:
parent
7a87982420
commit
f4e9c82f64
|
@ -50,6 +50,7 @@ struct watchdog_device {
|
||||||
unsigned int min_timeout;
|
unsigned int min_timeout;
|
||||||
unsigned int max_timeout;
|
unsigned int max_timeout;
|
||||||
void *driver_data;
|
void *driver_data;
|
||||||
|
struct mutex lock;
|
||||||
unsigned long status;
|
unsigned long status;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,6 +75,7 @@ It contains following fields:
|
||||||
* driver_data: a pointer to the drivers private data of a watchdog device.
|
* driver_data: a pointer to the drivers private data of a watchdog device.
|
||||||
This data should only be accessed via the watchdog_set_drvdata and
|
This data should only be accessed via the watchdog_set_drvdata and
|
||||||
watchdog_get_drvdata routines.
|
watchdog_get_drvdata routines.
|
||||||
|
* lock: Mutex for WatchDog Timer Driver Core internal use only.
|
||||||
* status: this field contains a number of status bits that give extra
|
* status: this field contains a number of status bits that give extra
|
||||||
information about the status of the device (Like: is the watchdog timer
|
information about the status of the device (Like: is the watchdog timer
|
||||||
running/active, is the nowayout bit set, is the device opened via
|
running/active, is the nowayout bit set, is the device opened via
|
||||||
|
|
|
@ -79,6 +79,7 @@ int watchdog_register_device(struct watchdog_device *wdd)
|
||||||
* corrupted in a later stage then we expect a kernel panic!
|
* corrupted in a later stage then we expect a kernel panic!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
mutex_init(&wdd->lock);
|
||||||
id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL);
|
id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL);
|
||||||
if (id < 0)
|
if (id < 0)
|
||||||
return id;
|
return id;
|
||||||
|
|
|
@ -63,6 +63,8 @@ static int watchdog_ping(struct watchdog_device *wddev)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
mutex_lock(&wddev->lock);
|
||||||
|
|
||||||
if (!watchdog_active(wddev))
|
if (!watchdog_active(wddev))
|
||||||
goto out_ping;
|
goto out_ping;
|
||||||
|
|
||||||
|
@ -72,6 +74,7 @@ static int watchdog_ping(struct watchdog_device *wddev)
|
||||||
err = wddev->ops->start(wddev); /* restart watchdog */
|
err = wddev->ops->start(wddev); /* restart watchdog */
|
||||||
|
|
||||||
out_ping:
|
out_ping:
|
||||||
|
mutex_unlock(&wddev->lock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +91,8 @@ static int watchdog_start(struct watchdog_device *wddev)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
mutex_lock(&wddev->lock);
|
||||||
|
|
||||||
if (watchdog_active(wddev))
|
if (watchdog_active(wddev))
|
||||||
goto out_start;
|
goto out_start;
|
||||||
|
|
||||||
|
@ -96,6 +101,7 @@ static int watchdog_start(struct watchdog_device *wddev)
|
||||||
set_bit(WDOG_ACTIVE, &wddev->status);
|
set_bit(WDOG_ACTIVE, &wddev->status);
|
||||||
|
|
||||||
out_start:
|
out_start:
|
||||||
|
mutex_unlock(&wddev->lock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +119,8 @@ static int watchdog_stop(struct watchdog_device *wddev)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
mutex_lock(&wddev->lock);
|
||||||
|
|
||||||
if (!watchdog_active(wddev))
|
if (!watchdog_active(wddev))
|
||||||
goto out_stop;
|
goto out_stop;
|
||||||
|
|
||||||
|
@ -127,6 +135,7 @@ static int watchdog_stop(struct watchdog_device *wddev)
|
||||||
clear_bit(WDOG_ACTIVE, &wddev->status);
|
clear_bit(WDOG_ACTIVE, &wddev->status);
|
||||||
|
|
||||||
out_stop:
|
out_stop:
|
||||||
|
mutex_unlock(&wddev->lock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,8 +156,11 @@ static int watchdog_get_status(struct watchdog_device *wddev,
|
||||||
if (!wddev->ops->status)
|
if (!wddev->ops->status)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
mutex_lock(&wddev->lock);
|
||||||
|
|
||||||
*status = wddev->ops->status(wddev);
|
*status = wddev->ops->status(wddev);
|
||||||
|
|
||||||
|
mutex_unlock(&wddev->lock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,8 +183,11 @@ static int watchdog_set_timeout(struct watchdog_device *wddev,
|
||||||
(timeout < wddev->min_timeout || timeout > wddev->max_timeout))
|
(timeout < wddev->min_timeout || timeout > wddev->max_timeout))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
mutex_lock(&wddev->lock);
|
||||||
|
|
||||||
err = wddev->ops->set_timeout(wddev, timeout);
|
err = wddev->ops->set_timeout(wddev, timeout);
|
||||||
|
|
||||||
|
mutex_unlock(&wddev->lock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,8 +208,11 @@ static int watchdog_get_timeleft(struct watchdog_device *wddev,
|
||||||
if (!wddev->ops->get_timeleft)
|
if (!wddev->ops->get_timeleft)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
mutex_lock(&wddev->lock);
|
||||||
|
|
||||||
*timeleft = wddev->ops->get_timeleft(wddev);
|
*timeleft = wddev->ops->get_timeleft(wddev);
|
||||||
|
|
||||||
|
mutex_unlock(&wddev->lock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,8 +231,11 @@ static int watchdog_ioctl_op(struct watchdog_device *wddev, unsigned int cmd,
|
||||||
if (!wddev->ops->ioctl)
|
if (!wddev->ops->ioctl)
|
||||||
return -ENOIOCTLCMD;
|
return -ENOIOCTLCMD;
|
||||||
|
|
||||||
|
mutex_lock(&wddev->lock);
|
||||||
|
|
||||||
err = wddev->ops->ioctl(wddev, cmd, arg);
|
err = wddev->ops->ioctl(wddev, cmd, arg);
|
||||||
|
|
||||||
|
mutex_unlock(&wddev->lock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,7 @@ struct watchdog_ops {
|
||||||
* @min_timeout:The watchdog devices minimum timeout value.
|
* @min_timeout:The watchdog devices minimum timeout value.
|
||||||
* @max_timeout:The watchdog devices maximum timeout value.
|
* @max_timeout:The watchdog devices maximum timeout value.
|
||||||
* @driver-data:Pointer to the drivers private data.
|
* @driver-data:Pointer to the drivers private data.
|
||||||
|
* @lock: Lock for watchdog core internal use only.
|
||||||
* @status: Field that contains the devices internal status bits.
|
* @status: Field that contains the devices internal status bits.
|
||||||
*
|
*
|
||||||
* The watchdog_device structure contains all information about a
|
* The watchdog_device structure contains all information about a
|
||||||
|
@ -111,6 +112,9 @@ struct watchdog_ops {
|
||||||
*
|
*
|
||||||
* The driver-data field may not be accessed directly. It must be accessed
|
* The driver-data field may not be accessed directly. It must be accessed
|
||||||
* via the watchdog_set_drvdata and watchdog_get_drvdata helpers.
|
* via the watchdog_set_drvdata and watchdog_get_drvdata helpers.
|
||||||
|
*
|
||||||
|
* The lock field is for watchdog core internal use only and should not be
|
||||||
|
* touched.
|
||||||
*/
|
*/
|
||||||
struct watchdog_device {
|
struct watchdog_device {
|
||||||
int id;
|
int id;
|
||||||
|
@ -124,6 +128,7 @@ struct watchdog_device {
|
||||||
unsigned int min_timeout;
|
unsigned int min_timeout;
|
||||||
unsigned int max_timeout;
|
unsigned int max_timeout;
|
||||||
void *driver_data;
|
void *driver_data;
|
||||||
|
struct mutex lock;
|
||||||
unsigned long status;
|
unsigned long status;
|
||||||
/* Bit numbers for status flags */
|
/* Bit numbers for status flags */
|
||||||
#define WDOG_ACTIVE 0 /* Is the watchdog running/active */
|
#define WDOG_ACTIVE 0 /* Is the watchdog running/active */
|
||||||
|
|
Loading…
Reference in New Issue