Commit ed7279ae authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

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: default avatarGreg Kroah-Hartman <gregkh@google.com>
Reviewed-by: default avatarRui Miguel Silva <rui.silva@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent 0f65fb1e
......@@ -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);
......
/*
* 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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment