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 \
|
protocol.o \
|
||||||
control.o \
|
control.o \
|
||||||
svc.o \
|
svc.o \
|
||||||
|
svc_watchdog.o \
|
||||||
firmware.o \
|
firmware.o \
|
||||||
operation.o \
|
operation.o \
|
||||||
legacy.o
|
legacy.o
|
||||||
|
|
|
@ -436,6 +436,13 @@ static int gb_svc_hello(struct gb_operation *op)
|
||||||
return ret;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -963,6 +970,7 @@ void gb_svc_del(struct gb_svc *svc)
|
||||||
* from the request handler.
|
* from the request handler.
|
||||||
*/
|
*/
|
||||||
if (device_is_registered(&svc->dev)) {
|
if (device_is_registered(&svc->dev)) {
|
||||||
|
gb_svc_watchdog_destroy(svc);
|
||||||
input_unregister_device(svc->input);
|
input_unregister_device(svc->input);
|
||||||
device_del(&svc->dev);
|
device_del(&svc->dev);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ enum gb_svc_state {
|
||||||
GB_SVC_STATE_SVC_HELLO,
|
GB_SVC_STATE_SVC_HELLO,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct gb_svc_watchdog;
|
||||||
|
|
||||||
struct gb_svc {
|
struct gb_svc {
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
|
||||||
|
@ -33,6 +35,7 @@ struct gb_svc {
|
||||||
|
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
char *input_phys;
|
char *input_phys;
|
||||||
|
struct gb_svc_watchdog *watchdog;
|
||||||
};
|
};
|
||||||
#define to_gb_svc(d) container_of(d, struct gb_svc, d)
|
#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 rx_mode, u8 rx_gear, u8 rx_nlanes,
|
||||||
u8 flags, u32 quirks);
|
u8 flags, u32 quirks);
|
||||||
int gb_svc_ping(struct gb_svc *svc);
|
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);
|
int gb_svc_protocol_init(void);
|
||||||
void gb_svc_protocol_exit(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