blob: 9bd68f4f4cf7c138ef556fbaeca01480e9d446bc [file] [log] [blame]
Greg Kroah-Hartmaned7279a2016-01-20 22:51:49 -08001/*
2 * SVC Greybus "watchdog" driver.
3 *
4 * Copyright 2016 Google Inc.
5 *
6 * Released under the GPLv2 only.
7 */
8
9#include <linux/delay.h>
10#include <linux/workqueue.h>
11#include "greybus.h"
12
13#define SVC_WATCHDOG_PERIOD (2*HZ)
14
15struct gb_svc_watchdog {
16 struct delayed_work work;
17 struct gb_svc *svc;
Greg Kroah-Hartmand5628532016-01-26 15:17:08 -080018 bool enabled;
Greg Kroah-Hartmaned7279a2016-01-20 22:51:49 -080019};
20
21static struct delayed_work reset_work;
22
23static void greybus_reset(struct work_struct *work)
24{
25 static char start_path[256] = "/system/bin/start";
26 static char *envp[] = {
27 "HOME=/",
28 "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin",
29 NULL,
30 };
31 static char *argv[] = {
32 start_path,
33 "unipro_reset",
34 NULL,
35 };
36
37 printk(KERN_ERR "svc_watchdog: calling \"%s %s\" to reset greybus network!\n",
38 argv[0], argv[1]);
39 call_usermodehelper(start_path, argv, envp, UMH_WAIT_EXEC);
40}
41
42static void do_work(struct work_struct *work)
43{
44 struct gb_svc_watchdog *watchdog;
45 struct gb_svc *svc;
46 int retval;
47
48 watchdog = container_of(work, struct gb_svc_watchdog, work.work);
49 svc = watchdog->svc;
50
51 dev_dbg(&svc->dev, "%s: ping.\n", __func__);
52 retval = gb_svc_ping(svc);
53 if (retval) {
54 /*
55 * Something went really wrong, let's warn userspace and then
56 * pull the plug and reset the whole greybus network.
57 * We need to do this outside of this workqueue as we will be
58 * tearing down the svc device itself. So queue up
59 * yet-another-callback to do that.
60 */
61 dev_err(&svc->dev,
62 "SVC ping has returned %d, something is wrong!!!\n",
63 retval);
64 dev_err(&svc->dev, "Resetting the greybus network, watch out!!!\n");
65
66 INIT_DELAYED_WORK(&reset_work, greybus_reset);
67 queue_delayed_work(system_wq, &reset_work, HZ/2);
Greg Kroah-Hartmand5628532016-01-26 15:17:08 -080068
69 /*
70 * Disable ourselves, we don't want to trip again unless
71 * userspace wants us to.
72 */
73 watchdog->enabled = false;
Greg Kroah-Hartmaned7279a2016-01-20 22:51:49 -080074 }
75
76 /* resubmit our work to happen again, if we are still "alive" */
Greg Kroah-Hartmand5628532016-01-26 15:17:08 -080077 if (watchdog->enabled)
Greg Kroah-Hartmaned7279a2016-01-20 22:51:49 -080078 queue_delayed_work(system_wq, &watchdog->work,
79 SVC_WATCHDOG_PERIOD);
80}
81
82int gb_svc_watchdog_create(struct gb_svc *svc)
83{
84 struct gb_svc_watchdog *watchdog;
85
86 if (svc->watchdog)
87 return 0;
88
89 watchdog = kmalloc(sizeof(*watchdog), GFP_KERNEL);
90 if (!watchdog)
91 return -ENOMEM;
92
Greg Kroah-Hartmand5628532016-01-26 15:17:08 -080093 watchdog->enabled = false;
Greg Kroah-Hartmaned7279a2016-01-20 22:51:49 -080094 watchdog->svc = svc;
95 INIT_DELAYED_WORK(&watchdog->work, do_work);
96 svc->watchdog = watchdog;
97
Greg Kroah-Hartmand5628532016-01-26 15:17:08 -080098 return gb_svc_watchdog_enable(svc);
Greg Kroah-Hartmaned7279a2016-01-20 22:51:49 -080099}
100
101void gb_svc_watchdog_destroy(struct gb_svc *svc)
102{
103 struct gb_svc_watchdog *watchdog = svc->watchdog;
104
105 if (!watchdog)
106 return;
107
Greg Kroah-Hartmand5628532016-01-26 15:17:08 -0800108 gb_svc_watchdog_disable(svc);
Greg Kroah-Hartmaned7279a2016-01-20 22:51:49 -0800109 svc->watchdog = NULL;
110 kfree(watchdog);
111}
Greg Kroah-Hartmand5628532016-01-26 15:17:08 -0800112
113bool gb_svc_watchdog_enabled(struct gb_svc *svc)
114{
115 if (!svc || !svc->watchdog)
116 return false;
117 return svc->watchdog->enabled;
118}
119
120int gb_svc_watchdog_enable(struct gb_svc *svc)
121{
122 struct gb_svc_watchdog *watchdog;
123
124 if (!svc->watchdog)
125 return -ENODEV;
126
127 watchdog = svc->watchdog;
128 if (watchdog->enabled)
129 return 0;
130
131 watchdog->enabled = true;
132 queue_delayed_work(system_wq, &watchdog->work,
133 SVC_WATCHDOG_PERIOD);
134 return 0;
135}
136
137int gb_svc_watchdog_disable(struct gb_svc *svc)
138{
139 struct gb_svc_watchdog *watchdog;
140
141 if (!svc->watchdog)
142 return -ENODEV;
143
144 watchdog = svc->watchdog;
145 if (!watchdog->enabled)
146 return 0;
147
148 watchdog->enabled = false;
149 cancel_delayed_work_sync(&watchdog->work);
150 return 0;
151}