From ed7279ae31b0a0a97e832bdd0eb6edb31ec2736f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 20 Jan 2016 22:51:49 -0800 Subject: [PATCH] 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 Reviewed-by: Rui Miguel Silva Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/svc.c | 8 ++ drivers/staging/greybus/svc.h | 5 ++ drivers/staging/greybus/svc_watchdog.c | 109 +++++++++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 drivers/staging/greybus/svc_watchdog.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 1e24509bdb4d..1e45416a2796 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -8,6 +8,7 @@ greybus-y := core.o \ protocol.o \ control.o \ svc.o \ + svc_watchdog.o \ firmware.o \ operation.o \ legacy.o diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 85eb7eaae737..fc5747031721 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -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); } diff --git a/drivers/staging/greybus/svc.h b/drivers/staging/greybus/svc.h index f3e8479b9438..0f81e97a2e88 100644 --- a/drivers/staging/greybus/svc.h +++ b/drivers/staging/greybus/svc.h @@ -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); diff --git a/drivers/staging/greybus/svc_watchdog.c b/drivers/staging/greybus/svc_watchdog.c new file mode 100644 index 000000000000..edb73efd53f7 --- /dev/null +++ b/drivers/staging/greybus/svc_watchdog.c @@ -0,0 +1,109 @@ +/* + * SVC Greybus "watchdog" driver. + * + * Copyright 2016 Google Inc. + * + * Released under the GPLv2 only. + */ + +#include +#include +#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); +} -- 2.20.1