Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * SVC Greybus "watchdog" driver.
0004  *
0005  * Copyright 2016 Google Inc.
0006  */
0007 
0008 #include <linux/delay.h>
0009 #include <linux/suspend.h>
0010 #include <linux/workqueue.h>
0011 #include <linux/greybus.h>
0012 
0013 #define SVC_WATCHDOG_PERIOD (2 * HZ)
0014 
0015 struct gb_svc_watchdog {
0016     struct delayed_work work;
0017     struct gb_svc       *svc;
0018     bool            enabled;
0019     struct notifier_block pm_notifier;
0020 };
0021 
0022 static struct delayed_work reset_work;
0023 
0024 static int svc_watchdog_pm_notifier(struct notifier_block *notifier,
0025                     unsigned long pm_event, void *unused)
0026 {
0027     struct gb_svc_watchdog *watchdog =
0028         container_of(notifier, struct gb_svc_watchdog, pm_notifier);
0029 
0030     switch (pm_event) {
0031     case PM_SUSPEND_PREPARE:
0032         gb_svc_watchdog_disable(watchdog->svc);
0033         break;
0034     case PM_POST_SUSPEND:
0035         gb_svc_watchdog_enable(watchdog->svc);
0036         break;
0037     default:
0038         break;
0039     }
0040 
0041     return NOTIFY_DONE;
0042 }
0043 
0044 static void greybus_reset(struct work_struct *work)
0045 {
0046     static char const start_path[] = "/system/bin/start";
0047     static char *envp[] = {
0048         "HOME=/",
0049         "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin",
0050         NULL,
0051     };
0052     static char *argv[] = {
0053         (char *)start_path,
0054         "unipro_reset",
0055         NULL,
0056     };
0057 
0058     pr_err("svc_watchdog: calling \"%s %s\" to reset greybus network!\n",
0059            argv[0], argv[1]);
0060     call_usermodehelper(start_path, argv, envp, UMH_WAIT_EXEC);
0061 }
0062 
0063 static void do_work(struct work_struct *work)
0064 {
0065     struct gb_svc_watchdog *watchdog;
0066     struct gb_svc *svc;
0067     int retval;
0068 
0069     watchdog = container_of(work, struct gb_svc_watchdog, work.work);
0070     svc = watchdog->svc;
0071 
0072     dev_dbg(&svc->dev, "%s: ping.\n", __func__);
0073     retval = gb_svc_ping(svc);
0074     if (retval) {
0075         /*
0076          * Something went really wrong, let's warn userspace and then
0077          * pull the plug and reset the whole greybus network.
0078          * We need to do this outside of this workqueue as we will be
0079          * tearing down the svc device itself.  So queue up
0080          * yet-another-callback to do that.
0081          */
0082         dev_err(&svc->dev,
0083             "SVC ping has returned %d, something is wrong!!!\n",
0084             retval);
0085 
0086         if (svc->action == GB_SVC_WATCHDOG_BITE_PANIC_KERNEL) {
0087             panic("SVC is not responding\n");
0088         } else if (svc->action == GB_SVC_WATCHDOG_BITE_RESET_UNIPRO) {
0089             dev_err(&svc->dev, "Resetting the greybus network, watch out!!!\n");
0090 
0091             INIT_DELAYED_WORK(&reset_work, greybus_reset);
0092             schedule_delayed_work(&reset_work, HZ / 2);
0093 
0094             /*
0095              * Disable ourselves, we don't want to trip again unless
0096              * userspace wants us to.
0097              */
0098             watchdog->enabled = false;
0099         }
0100     }
0101 
0102     /* resubmit our work to happen again, if we are still "alive" */
0103     if (watchdog->enabled)
0104         schedule_delayed_work(&watchdog->work, SVC_WATCHDOG_PERIOD);
0105 }
0106 
0107 int gb_svc_watchdog_create(struct gb_svc *svc)
0108 {
0109     struct gb_svc_watchdog *watchdog;
0110     int retval;
0111 
0112     if (svc->watchdog)
0113         return 0;
0114 
0115     watchdog = kmalloc(sizeof(*watchdog), GFP_KERNEL);
0116     if (!watchdog)
0117         return -ENOMEM;
0118 
0119     watchdog->enabled = false;
0120     watchdog->svc = svc;
0121     INIT_DELAYED_WORK(&watchdog->work, do_work);
0122     svc->watchdog = watchdog;
0123 
0124     watchdog->pm_notifier.notifier_call = svc_watchdog_pm_notifier;
0125     retval = register_pm_notifier(&watchdog->pm_notifier);
0126     if (retval) {
0127         dev_err(&svc->dev, "error registering pm notifier(%d)\n",
0128             retval);
0129         goto svc_watchdog_create_err;
0130     }
0131 
0132     retval = gb_svc_watchdog_enable(svc);
0133     if (retval) {
0134         dev_err(&svc->dev, "error enabling watchdog (%d)\n", retval);
0135         unregister_pm_notifier(&watchdog->pm_notifier);
0136         goto svc_watchdog_create_err;
0137     }
0138     return retval;
0139 
0140 svc_watchdog_create_err:
0141     svc->watchdog = NULL;
0142     kfree(watchdog);
0143 
0144     return retval;
0145 }
0146 
0147 void gb_svc_watchdog_destroy(struct gb_svc *svc)
0148 {
0149     struct gb_svc_watchdog *watchdog = svc->watchdog;
0150 
0151     if (!watchdog)
0152         return;
0153 
0154     unregister_pm_notifier(&watchdog->pm_notifier);
0155     gb_svc_watchdog_disable(svc);
0156     svc->watchdog = NULL;
0157     kfree(watchdog);
0158 }
0159 
0160 bool gb_svc_watchdog_enabled(struct gb_svc *svc)
0161 {
0162     if (!svc || !svc->watchdog)
0163         return false;
0164     return svc->watchdog->enabled;
0165 }
0166 
0167 int gb_svc_watchdog_enable(struct gb_svc *svc)
0168 {
0169     struct gb_svc_watchdog *watchdog;
0170 
0171     if (!svc->watchdog)
0172         return -ENODEV;
0173 
0174     watchdog = svc->watchdog;
0175     if (watchdog->enabled)
0176         return 0;
0177 
0178     watchdog->enabled = true;
0179     schedule_delayed_work(&watchdog->work, SVC_WATCHDOG_PERIOD);
0180     return 0;
0181 }
0182 
0183 int gb_svc_watchdog_disable(struct gb_svc *svc)
0184 {
0185     struct gb_svc_watchdog *watchdog;
0186 
0187     if (!svc->watchdog)
0188         return -ENODEV;
0189 
0190     watchdog = svc->watchdog;
0191     if (!watchdog->enabled)
0192         return 0;
0193 
0194     watchdog->enabled = false;
0195     cancel_delayed_work_sync(&watchdog->work);
0196     return 0;
0197 }