Blame view

drivers/greybus/svc_watchdog.c 4.37 KB
eb50fd3a2   Greg Kroah-Hartman   staging: greybus:...
1
  // SPDX-License-Identifier: GPL-2.0
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
2
3
4
5
  /*
   * SVC Greybus "watchdog" driver.
   *
   * Copyright 2016 Google Inc.
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
6
7
8
   */
  
  #include <linux/delay.h>
192c70dcf   David Lin   greybus: svc watc...
9
  #include <linux/suspend.h>
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
10
  #include <linux/workqueue.h>
ec0ad8681   Greg Kroah-Hartman   staging: greybus:...
11
  #include <linux/greybus.h>
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
12

28cf20352   Emmanuil Chatzipetru   staging: greybus:...
13
  #define SVC_WATCHDOG_PERIOD	(2 * HZ)
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
14
15
16
17
  
  struct gb_svc_watchdog {
  	struct delayed_work	work;
  	struct gb_svc		*svc;
d562853d3   Greg Kroah-Hartman   greybus: svc watc...
18
  	bool			enabled;
192c70dcf   David Lin   greybus: svc watc...
19
  	struct notifier_block pm_notifier;
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
20
21
22
  };
  
  static struct delayed_work reset_work;
192c70dcf   David Lin   greybus: svc watc...
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  static int svc_watchdog_pm_notifier(struct notifier_block *notifier,
  				    unsigned long pm_event, void *unused)
  {
  	struct gb_svc_watchdog *watchdog =
  		container_of(notifier, struct gb_svc_watchdog, pm_notifier);
  
  	switch (pm_event) {
  	case PM_SUSPEND_PREPARE:
  		gb_svc_watchdog_disable(watchdog->svc);
  		break;
  	case PM_POST_SUSPEND:
  		gb_svc_watchdog_enable(watchdog->svc);
  		break;
  	default:
  		break;
  	}
  
  	return NOTIFY_DONE;
  }
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
42
43
  static void greybus_reset(struct work_struct *work)
  {
377e7a27c   Greg Kroah-Hartman   Make static userm...
44
  	static char const start_path[] = "/system/bin/start";
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
45
46
47
48
49
50
  	static char *envp[] = {
  		"HOME=/",
  		"PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin",
  		NULL,
  	};
  	static char *argv[] = {
377e7a27c   Greg Kroah-Hartman   Make static userm...
51
  		(char *)start_path,
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
52
53
54
  		"unipro_reset",
  		NULL,
  	};
62730c957   Emmanuil Chatzipetru   staging: greybus:...
55
56
  	pr_err("svc_watchdog: calling \"%s %s\" to reset greybus network!
  ",
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  	       argv[0], argv[1]);
  	call_usermodehelper(start_path, argv, envp, UMH_WAIT_EXEC);
  }
  
  static void do_work(struct work_struct *work)
  {
  	struct gb_svc_watchdog *watchdog;
  	struct gb_svc *svc;
  	int retval;
  
  	watchdog = container_of(work, struct gb_svc_watchdog, work.work);
  	svc = watchdog->svc;
  
  	dev_dbg(&svc->dev, "%s: ping.
  ", __func__);
  	retval = gb_svc_ping(svc);
  	if (retval) {
  		/*
  		 * Something went really wrong, let's warn userspace and then
  		 * pull the plug and reset the whole greybus network.
  		 * We need to do this outside of this workqueue as we will be
  		 * tearing down the svc device itself.  So queue up
  		 * yet-another-callback to do that.
  		 */
  		dev_err(&svc->dev,
  			"SVC ping has returned %d, something is wrong!!!
  ",
  			retval);
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
85

7c4a0edb3   David Lin   greybus: svc_watc...
86
87
88
89
90
91
  		if (svc->action == GB_SVC_WATCHDOG_BITE_PANIC_KERNEL) {
  			panic("SVC is not responding
  ");
  		} else if (svc->action == GB_SVC_WATCHDOG_BITE_RESET_UNIPRO) {
  			dev_err(&svc->dev, "Resetting the greybus network, watch out!!!
  ");
d562853d3   Greg Kroah-Hartman   greybus: svc watc...
92

7c4a0edb3   David Lin   greybus: svc_watc...
93
  			INIT_DELAYED_WORK(&reset_work, greybus_reset);
b6fc2876a   David Lin   greybus: svc_watc...
94
  			schedule_delayed_work(&reset_work, HZ / 2);
7c4a0edb3   David Lin   greybus: svc_watc...
95
96
97
98
99
100
101
  
  			/*
  			 * Disable ourselves, we don't want to trip again unless
  			 * userspace wants us to.
  			 */
  			watchdog->enabled = false;
  		}
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
102
103
104
  	}
  
  	/* resubmit our work to happen again, if we are still "alive" */
d562853d3   Greg Kroah-Hartman   greybus: svc watc...
105
  	if (watchdog->enabled)
b6fc2876a   David Lin   greybus: svc_watc...
106
  		schedule_delayed_work(&watchdog->work, SVC_WATCHDOG_PERIOD);
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
107
108
109
110
111
  }
  
  int gb_svc_watchdog_create(struct gb_svc *svc)
  {
  	struct gb_svc_watchdog *watchdog;
192c70dcf   David Lin   greybus: svc watc...
112
  	int retval;
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
113
114
115
116
117
118
119
  
  	if (svc->watchdog)
  		return 0;
  
  	watchdog = kmalloc(sizeof(*watchdog), GFP_KERNEL);
  	if (!watchdog)
  		return -ENOMEM;
d562853d3   Greg Kroah-Hartman   greybus: svc watc...
120
  	watchdog->enabled = false;
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
121
122
123
  	watchdog->svc = svc;
  	INIT_DELAYED_WORK(&watchdog->work, do_work);
  	svc->watchdog = watchdog;
192c70dcf   David Lin   greybus: svc watc...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
  	watchdog->pm_notifier.notifier_call = svc_watchdog_pm_notifier;
  	retval = register_pm_notifier(&watchdog->pm_notifier);
  	if (retval) {
  		dev_err(&svc->dev, "error registering pm notifier(%d)
  ",
  			retval);
  		goto svc_watchdog_create_err;
  	}
  
  	retval = gb_svc_watchdog_enable(svc);
  	if (retval) {
  		dev_err(&svc->dev, "error enabling watchdog (%d)
  ", retval);
  		unregister_pm_notifier(&watchdog->pm_notifier);
  		goto svc_watchdog_create_err;
  	}
  	return retval;
  
  svc_watchdog_create_err:
  	svc->watchdog = NULL;
  	kfree(watchdog);
  
  	return retval;
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
147
148
149
150
151
152
153
154
  }
  
  void gb_svc_watchdog_destroy(struct gb_svc *svc)
  {
  	struct gb_svc_watchdog *watchdog = svc->watchdog;
  
  	if (!watchdog)
  		return;
192c70dcf   David Lin   greybus: svc watc...
155
  	unregister_pm_notifier(&watchdog->pm_notifier);
d562853d3   Greg Kroah-Hartman   greybus: svc watc...
156
  	gb_svc_watchdog_disable(svc);
ed7279ae3   Greg Kroah-Hartman   greybus: svc: add...
157
158
159
  	svc->watchdog = NULL;
  	kfree(watchdog);
  }
d562853d3   Greg Kroah-Hartman   greybus: svc watc...
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  
  bool gb_svc_watchdog_enabled(struct gb_svc *svc)
  {
  	if (!svc || !svc->watchdog)
  		return false;
  	return svc->watchdog->enabled;
  }
  
  int gb_svc_watchdog_enable(struct gb_svc *svc)
  {
  	struct gb_svc_watchdog *watchdog;
  
  	if (!svc->watchdog)
  		return -ENODEV;
  
  	watchdog = svc->watchdog;
  	if (watchdog->enabled)
  		return 0;
  
  	watchdog->enabled = true;
b6fc2876a   David Lin   greybus: svc_watc...
180
  	schedule_delayed_work(&watchdog->work, SVC_WATCHDOG_PERIOD);
d562853d3   Greg Kroah-Hartman   greybus: svc watc...
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
  	return 0;
  }
  
  int gb_svc_watchdog_disable(struct gb_svc *svc)
  {
  	struct gb_svc_watchdog *watchdog;
  
  	if (!svc->watchdog)
  		return -ENODEV;
  
  	watchdog = svc->watchdog;
  	if (!watchdog->enabled)
  		return 0;
  
  	watchdog->enabled = false;
  	cancel_delayed_work_sync(&watchdog->work);
  	return 0;
  }