2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-10-04 13:37:26 +04:00
/*
* Xen Watchdog Driver
*
* ( c ) Copyright 2010 Novell , Inc .
*/
2017-11-15 22:34:41 +03:00
# define DRV_NAME "xen_wdt"
2010-10-04 13:37:26 +04:00
# include <linux/bug.h>
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/hrtimer.h>
# include <linux/kernel.h>
# include <linux/ktime.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/platform_device.h>
# include <linux/watchdog.h>
# include <xen/xen.h>
# include <asm/xen/hypercall.h>
# include <xen/interface/sched.h>
static struct platform_device * platform_device ;
static struct sched_watchdog wdt ;
2017-10-19 18:05:48 +03:00
static time64_t wdt_expires ;
2010-10-04 13:37:26 +04:00
# define WATCHDOG_TIMEOUT 60 /* in seconds */
2017-11-15 22:34:41 +03:00
static unsigned int timeout ;
2010-10-04 13:37:26 +04:00
module_param ( timeout , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( timeout , " Watchdog timeout in seconds "
" (default= " __MODULE_STRING ( WATCHDOG_TIMEOUT ) " ) " ) ;
static bool nowayout = WATCHDOG_NOWAYOUT ;
module_param ( nowayout , bool , S_IRUGO ) ;
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started "
" (default= " __MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
2017-11-15 22:34:41 +03:00
static inline time64_t set_timeout ( struct watchdog_device * wdd )
2010-10-04 13:37:26 +04:00
{
2017-11-15 22:34:41 +03:00
wdt . timeout = wdd - > timeout ;
return ktime_get_seconds ( ) + wdd - > timeout ;
2010-10-04 13:37:26 +04:00
}
2017-11-15 22:34:41 +03:00
static int xen_wdt_start ( struct watchdog_device * wdd )
2010-10-04 13:37:26 +04:00
{
2017-10-19 18:05:48 +03:00
time64_t expires ;
2010-10-04 13:37:26 +04:00
int err ;
2017-11-15 22:34:41 +03:00
expires = set_timeout ( wdd ) ;
2010-10-04 13:37:26 +04:00
if ( ! wdt . id )
err = HYPERVISOR_sched_op ( SCHEDOP_watchdog , & wdt ) ;
else
err = - EBUSY ;
if ( err > 0 ) {
wdt . id = err ;
wdt_expires = expires ;
err = 0 ;
} else
BUG_ON ( ! err ) ;
return err ;
}
2017-11-15 22:34:41 +03:00
static int xen_wdt_stop ( struct watchdog_device * wdd )
2010-10-04 13:37:26 +04:00
{
int err = 0 ;
wdt . timeout = 0 ;
if ( wdt . id )
err = HYPERVISOR_sched_op ( SCHEDOP_watchdog , & wdt ) ;
if ( ! err )
wdt . id = 0 ;
return err ;
}
2017-11-15 22:34:41 +03:00
static int xen_wdt_kick ( struct watchdog_device * wdd )
2010-10-04 13:37:26 +04:00
{
2017-10-19 18:05:48 +03:00
time64_t expires ;
2010-10-04 13:37:26 +04:00
int err ;
2017-11-15 22:34:41 +03:00
expires = set_timeout ( wdd ) ;
2010-10-04 13:37:26 +04:00
if ( wdt . id )
err = HYPERVISOR_sched_op ( SCHEDOP_watchdog , & wdt ) ;
else
err = - ENXIO ;
if ( ! err )
wdt_expires = expires ;
return err ;
}
2017-11-15 22:34:41 +03:00
static unsigned int xen_wdt_get_timeleft ( struct watchdog_device * wdd )
2010-10-04 13:37:26 +04:00
{
2017-11-15 22:34:41 +03:00
return wdt_expires - ktime_get_seconds ( ) ;
2010-10-04 13:37:26 +04:00
}
2017-11-15 22:34:41 +03:00
static struct watchdog_info xen_wdt_info = {
. identity = DRV_NAME ,
. options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE ,
} ;
2010-10-04 13:37:26 +04:00
2017-11-15 22:34:41 +03:00
static const struct watchdog_ops xen_wdt_ops = {
. owner = THIS_MODULE ,
. start = xen_wdt_start ,
. stop = xen_wdt_stop ,
. ping = xen_wdt_kick ,
. get_timeleft = xen_wdt_get_timeleft ,
2010-10-04 13:37:26 +04:00
} ;
2017-11-15 22:34:41 +03:00
static struct watchdog_device xen_wdt_dev = {
. info = & xen_wdt_info ,
. ops = & xen_wdt_ops ,
. timeout = WATCHDOG_TIMEOUT ,
2010-10-04 13:37:26 +04:00
} ;
2017-11-15 22:34:41 +03:00
static int xen_wdt_probe ( struct platform_device * pdev )
2010-10-04 13:37:26 +04:00
{
2019-04-10 19:27:50 +03:00
struct device * dev = & pdev - > dev ;
2010-10-04 13:37:26 +04:00
struct sched_watchdog wd = { . id = ~ 0 } ;
int ret = HYPERVISOR_sched_op ( SCHEDOP_watchdog , & wd ) ;
2017-11-15 22:34:41 +03:00
if ( ret = = - ENOSYS ) {
2019-04-10 19:27:50 +03:00
dev_err ( dev , " watchdog not supported by hypervisor \n " ) ;
2017-11-15 22:34:41 +03:00
return - ENODEV ;
2010-10-04 13:37:26 +04:00
}
2017-11-15 22:34:41 +03:00
if ( ret ! = - EINVAL ) {
2019-04-10 19:27:50 +03:00
dev_err ( dev , " unexpected hypervisor error (%d) \n " , ret ) ;
2017-11-15 22:34:41 +03:00
return - ENODEV ;
}
2010-10-04 13:37:26 +04:00
2019-04-19 21:16:00 +03:00
watchdog_init_timeout ( & xen_wdt_dev , timeout , NULL ) ;
2017-11-15 22:34:41 +03:00
watchdog_set_nowayout ( & xen_wdt_dev , nowayout ) ;
watchdog_stop_on_reboot ( & xen_wdt_dev ) ;
watchdog_stop_on_unregister ( & xen_wdt_dev ) ;
2019-04-10 19:27:50 +03:00
ret = devm_watchdog_register_device ( dev , & xen_wdt_dev ) ;
2019-05-19 00:28:01 +03:00
if ( ret )
2017-11-15 22:34:41 +03:00
return ret ;
2010-10-04 13:37:26 +04:00
2019-04-10 19:27:50 +03:00
dev_info ( dev , " initialized (timeout=%ds, nowayout=%d) \n " ,
xen_wdt_dev . timeout , nowayout ) ;
2010-10-04 13:37:26 +04:00
return 0 ;
}
static int xen_wdt_suspend ( struct platform_device * dev , pm_message_t state )
{
2012-03-19 13:30:33 +04:00
typeof ( wdt . id ) id = wdt . id ;
2017-11-15 22:34:41 +03:00
int rc = xen_wdt_stop ( & xen_wdt_dev ) ;
2012-03-19 13:30:33 +04:00
wdt . id = id ;
return rc ;
2010-10-04 13:37:26 +04:00
}
static int xen_wdt_resume ( struct platform_device * dev )
{
2012-03-19 13:30:33 +04:00
if ( ! wdt . id )
return 0 ;
wdt . id = 0 ;
2017-11-15 22:34:41 +03:00
return xen_wdt_start ( & xen_wdt_dev ) ;
2010-10-04 13:37:26 +04:00
}
static struct platform_driver xen_wdt_driver = {
. probe = xen_wdt_probe ,
. suspend = xen_wdt_suspend ,
. resume = xen_wdt_resume ,
. driver = {
. name = DRV_NAME ,
} ,
} ;
static int __init xen_wdt_init_module ( void )
{
int err ;
if ( ! xen_domain ( ) )
return - ENODEV ;
err = platform_driver_register ( & xen_wdt_driver ) ;
if ( err )
return err ;
platform_device = platform_device_register_simple ( DRV_NAME ,
- 1 , NULL , 0 ) ;
if ( IS_ERR ( platform_device ) ) {
err = PTR_ERR ( platform_device ) ;
platform_driver_unregister ( & xen_wdt_driver ) ;
}
return err ;
}
static void __exit xen_wdt_cleanup_module ( void )
{
platform_device_unregister ( platform_device ) ;
platform_driver_unregister ( & xen_wdt_driver ) ;
}
module_init ( xen_wdt_init_module ) ;
module_exit ( xen_wdt_cleanup_module ) ;
MODULE_AUTHOR ( " Jan Beulich <jbeulich@novell.com> " ) ;
MODULE_DESCRIPTION ( " Xen WatchDog Timer Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;