greybus: svc: add a "watchdog" to check the network health
Now that we have a svc ping command, let's add a watchdog to call it every so often (1 second at the moment.) If it finds something went wrong, post a stern message to the kernel log and call: start unipro_reset to reset the whole greybus hardware subsystem. Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Reviewed-by: Rui Miguel Silva <rui.silva@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
parent
0f65fb1ea2
commit
ed7279ae31
|
@ -8,6 +8,7 @@ greybus-y := core.o \
|
|||
protocol.o \
|
||||
control.o \
|
||||
svc.o \
|
||||
svc_watchdog.o \
|
||||
firmware.o \
|
||||
operation.o \
|
||||
legacy.o
|
||||
|
|
|
@ -436,6 +436,13 @@ static int gb_svc_hello(struct gb_operation *op)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = gb_svc_watchdog_create(svc);
|
||||
if (ret) {
|
||||
dev_err(&svc->dev, "failed to create watchdog: %d\n", ret);
|
||||
input_unregister_device(svc->input);
|
||||
device_del(&svc->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -963,6 +970,7 @@ void gb_svc_del(struct gb_svc *svc)
|
|||
* from the request handler.
|
||||
*/
|
||||
if (device_is_registered(&svc->dev)) {
|
||||
gb_svc_watchdog_destroy(svc);
|
||||
input_unregister_device(svc->input);
|
||||
device_del(&svc->dev);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ enum gb_svc_state {
|
|||
GB_SVC_STATE_SVC_HELLO,
|
||||
};
|
||||
|
||||
struct gb_svc_watchdog;
|
||||
|
||||
struct gb_svc {
|
||||
struct device dev;
|
||||
|
||||
|
@ -33,6 +35,7 @@ struct gb_svc {
|
|||
|
||||
struct input_dev *input;
|
||||
char *input_phys;
|
||||
struct gb_svc_watchdog *watchdog;
|
||||
};
|
||||
#define to_gb_svc(d) container_of(d, struct gb_svc, d)
|
||||
|
||||
|
@ -56,6 +59,8 @@ int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series,
|
|||
u8 rx_mode, u8 rx_gear, u8 rx_nlanes,
|
||||
u8 flags, u32 quirks);
|
||||
int gb_svc_ping(struct gb_svc *svc);
|
||||
int gb_svc_watchdog_create(struct gb_svc *svc);
|
||||
void gb_svc_watchdog_destroy(struct gb_svc *svc);
|
||||
|
||||
int gb_svc_protocol_init(void);
|
||||
void gb_svc_protocol_exit(void);
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* SVC Greybus "watchdog" driver.
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "greybus.h"
|
||||
|
||||
#define SVC_WATCHDOG_PERIOD (2*HZ)
|
||||
|
||||
struct gb_svc_watchdog {
|
||||
struct delayed_work work;
|
||||
struct gb_svc *svc;
|
||||
bool finished;
|
||||
};
|
||||
|
||||
static struct delayed_work reset_work;
|
||||
|
||||
static void greybus_reset(struct work_struct *work)
|
||||
{
|
||||
static char start_path[256] = "/system/bin/start";
|
||||
static char *envp[] = {
|
||||
"HOME=/",
|
||||
"PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin",
|
||||
NULL,
|
||||
};
|
||||
static char *argv[] = {
|
||||
start_path,
|
||||
"unipro_reset",
|
||||
NULL,
|
||||
};
|
||||
|
||||
printk(KERN_ERR "svc_watchdog: calling \"%s %s\" to reset greybus network!\n",
|
||||
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.\n", __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!!!\n",
|
||||
retval);
|
||||
dev_err(&svc->dev, "Resetting the greybus network, watch out!!!\n");
|
||||
|
||||
INIT_DELAYED_WORK(&reset_work, greybus_reset);
|
||||
queue_delayed_work(system_wq, &reset_work, HZ/2);
|
||||
return;
|
||||
}
|
||||
|
||||
/* resubmit our work to happen again, if we are still "alive" */
|
||||
if (!watchdog->finished)
|
||||
queue_delayed_work(system_wq, &watchdog->work,
|
||||
SVC_WATCHDOG_PERIOD);
|
||||
}
|
||||
|
||||
int gb_svc_watchdog_create(struct gb_svc *svc)
|
||||
{
|
||||
struct gb_svc_watchdog *watchdog;
|
||||
|
||||
if (svc->watchdog)
|
||||
return 0;
|
||||
|
||||
watchdog = kmalloc(sizeof(*watchdog), GFP_KERNEL);
|
||||
if (!watchdog)
|
||||
return -ENOMEM;
|
||||
|
||||
watchdog->finished = false;
|
||||
watchdog->svc = svc;
|
||||
INIT_DELAYED_WORK(&watchdog->work, do_work);
|
||||
svc->watchdog = watchdog;
|
||||
|
||||
queue_delayed_work(system_wq, &watchdog->work,
|
||||
SVC_WATCHDOG_PERIOD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gb_svc_watchdog_destroy(struct gb_svc *svc)
|
||||
{
|
||||
struct gb_svc_watchdog *watchdog = svc->watchdog;
|
||||
|
||||
if (!watchdog)
|
||||
return;
|
||||
|
||||
watchdog->finished = true;
|
||||
cancel_delayed_work_sync(&watchdog->work);
|
||||
svc->watchdog = NULL;
|
||||
kfree(watchdog);
|
||||
}
|
Loading…
Reference in New Issue