Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  sun4v watchdog timer
0004  *  (c) Copyright 2016 Oracle Corporation
0005  *
0006  *  Implement a simple watchdog driver using the built-in sun4v hypervisor
0007  *  watchdog support. If time expires, the hypervisor stops or bounces
0008  *  the guest domain.
0009  */
0010 
0011 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0012 
0013 #include <linux/errno.h>
0014 #include <linux/init.h>
0015 #include <linux/kernel.h>
0016 #include <linux/module.h>
0017 #include <linux/moduleparam.h>
0018 #include <linux/watchdog.h>
0019 #include <asm/hypervisor.h>
0020 #include <asm/mdesc.h>
0021 
0022 #define WDT_TIMEOUT         60
0023 #define WDT_MAX_TIMEOUT         31536000
0024 #define WDT_MIN_TIMEOUT         1
0025 #define WDT_DEFAULT_RESOLUTION_MS   1000    /* 1 second */
0026 
0027 static unsigned int timeout;
0028 module_param(timeout, uint, 0);
0029 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
0030     __MODULE_STRING(WDT_TIMEOUT) ")");
0031 
0032 static bool nowayout = WATCHDOG_NOWAYOUT;
0033 module_param(nowayout, bool, S_IRUGO);
0034 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0035     __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0036 
0037 static int sun4v_wdt_stop(struct watchdog_device *wdd)
0038 {
0039     sun4v_mach_set_watchdog(0, NULL);
0040 
0041     return 0;
0042 }
0043 
0044 static int sun4v_wdt_ping(struct watchdog_device *wdd)
0045 {
0046     int hverr;
0047 
0048     /*
0049      * HV watchdog timer will round up the timeout
0050      * passed in to the nearest multiple of the
0051      * watchdog resolution in milliseconds.
0052      */
0053     hverr = sun4v_mach_set_watchdog(wdd->timeout * 1000, NULL);
0054     if (hverr == HV_EINVAL)
0055         return -EINVAL;
0056 
0057     return 0;
0058 }
0059 
0060 static int sun4v_wdt_set_timeout(struct watchdog_device *wdd,
0061                  unsigned int timeout)
0062 {
0063     wdd->timeout = timeout;
0064 
0065     return 0;
0066 }
0067 
0068 static const struct watchdog_info sun4v_wdt_ident = {
0069     .options =  WDIOF_SETTIMEOUT |
0070             WDIOF_MAGICCLOSE |
0071             WDIOF_KEEPALIVEPING,
0072     .identity = "sun4v hypervisor watchdog",
0073     .firmware_version = 0,
0074 };
0075 
0076 static const struct watchdog_ops sun4v_wdt_ops = {
0077     .owner =    THIS_MODULE,
0078     .start =    sun4v_wdt_ping,
0079     .stop =     sun4v_wdt_stop,
0080     .ping =     sun4v_wdt_ping,
0081     .set_timeout =  sun4v_wdt_set_timeout,
0082 };
0083 
0084 static struct watchdog_device wdd = {
0085     .info = &sun4v_wdt_ident,
0086     .ops = &sun4v_wdt_ops,
0087     .min_timeout = WDT_MIN_TIMEOUT,
0088     .max_timeout = WDT_MAX_TIMEOUT,
0089     .timeout = WDT_TIMEOUT,
0090 };
0091 
0092 static int __init sun4v_wdt_init(void)
0093 {
0094     struct mdesc_handle *handle;
0095     u64 node;
0096     const u64 *value;
0097     int err = 0;
0098     unsigned long major = 1, minor = 1;
0099 
0100     /*
0101      * There are 2 properties that can be set from the control
0102      * domain for the watchdog.
0103      * watchdog-resolution
0104      * watchdog-max-timeout
0105      *
0106      * We can expect a handle to be returned otherwise something
0107      * serious is wrong. Correct to return -ENODEV here.
0108      */
0109 
0110     handle = mdesc_grab();
0111     if (!handle)
0112         return -ENODEV;
0113 
0114     node = mdesc_node_by_name(handle, MDESC_NODE_NULL, "platform");
0115     err = -ENODEV;
0116     if (node == MDESC_NODE_NULL)
0117         goto out_release;
0118 
0119     /*
0120      * This is a safe way to validate if we are on the right
0121      * platform.
0122      */
0123     if (sun4v_hvapi_register(HV_GRP_CORE, major, &minor))
0124         goto out_hv_unreg;
0125 
0126     /* Allow value of watchdog-resolution up to 1s (default) */
0127     value = mdesc_get_property(handle, node, "watchdog-resolution", NULL);
0128     err = -EINVAL;
0129     if (value) {
0130         if (*value == 0 ||
0131             *value > WDT_DEFAULT_RESOLUTION_MS)
0132             goto out_hv_unreg;
0133     }
0134 
0135     value = mdesc_get_property(handle, node, "watchdog-max-timeout", NULL);
0136     if (value) {
0137         /*
0138          * If the property value (in ms) is smaller than
0139          * min_timeout, return -EINVAL.
0140          */
0141         if (*value < wdd.min_timeout * 1000)
0142             goto out_hv_unreg;
0143 
0144         /*
0145          * If the property value is smaller than
0146          * default max_timeout  then set watchdog max_timeout to
0147          * the value of the property in seconds.
0148          */
0149         if (*value < wdd.max_timeout * 1000)
0150             wdd.max_timeout = *value  / 1000;
0151     }
0152 
0153     watchdog_init_timeout(&wdd, timeout, NULL);
0154 
0155     watchdog_set_nowayout(&wdd, nowayout);
0156 
0157     err = watchdog_register_device(&wdd);
0158     if (err)
0159         goto out_hv_unreg;
0160 
0161     pr_info("initialized (timeout=%ds, nowayout=%d)\n",
0162          wdd.timeout, nowayout);
0163 
0164     mdesc_release(handle);
0165 
0166     return 0;
0167 
0168 out_hv_unreg:
0169     sun4v_hvapi_unregister(HV_GRP_CORE);
0170 
0171 out_release:
0172     mdesc_release(handle);
0173     return err;
0174 }
0175 
0176 static void __exit sun4v_wdt_exit(void)
0177 {
0178     sun4v_hvapi_unregister(HV_GRP_CORE);
0179     watchdog_unregister_device(&wdd);
0180 }
0181 
0182 module_init(sun4v_wdt_init);
0183 module_exit(sun4v_wdt_exit);
0184 
0185 MODULE_AUTHOR("Wim Coekaerts <wim.coekaerts@oracle.com>");
0186 MODULE_DESCRIPTION("sun4v watchdog driver");
0187 MODULE_LICENSE("GPL");