remoteproc: add actual recovery implementation
Fernando Guzman Lugo [Thu, 30 Aug 2012 18:26:13 +0000 (13:26 -0500)]
Add rproc_trigger_recovery() which takes care of the recovery itself,
by removing, and re-adding, all of the remoteproc's virtio devices.

This resets all virtio users of the remote processor, during which
the remote processor is powered off and on again.

Signed-off-by: Fernando Guzman Lugo <fernando.lugo@ti.com>
[ohad: introduce rproc_add_virtio_devices to avoid 1.copying code 2.anomaly]
[ohad: some white space, naming and commit log changes]
Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>

drivers/remoteproc/remoteproc_core.c
drivers/remoteproc/remoteproc_internal.h
include/linux/remoteproc.h

index 93e2b35..5000d75 100644 (file)
@@ -884,6 +884,60 @@ out:
        complete_all(&rproc->firmware_loading_complete);
 }
 
+static int rproc_add_virtio_devices(struct rproc *rproc)
+{
+       int ret;
+
+       /* rproc_del() calls must wait until async loader completes */
+       init_completion(&rproc->firmware_loading_complete);
+
+       /*
+        * We must retrieve early virtio configuration info from
+        * the firmware (e.g. whether to register a virtio device,
+        * what virtio features does it support, ...).
+        *
+        * We're initiating an asynchronous firmware loading, so we can
+        * be built-in kernel code, without hanging the boot process.
+        */
+       ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+                                     rproc->firmware, &rproc->dev, GFP_KERNEL,
+                                     rproc, rproc_fw_config_virtio);
+       if (ret < 0) {
+               dev_err(&rproc->dev, "request_firmware_nowait err: %d\n", ret);
+               complete_all(&rproc->firmware_loading_complete);
+       }
+
+       return ret;
+}
+
+/**
+ * rproc_trigger_recovery() - recover a remoteproc
+ * @rproc: the remote processor
+ *
+ * The recovery is done by reseting all the virtio devices, that way all the
+ * rpmsg drivers will be reseted along with the remote processor making the
+ * remoteproc functional again.
+ *
+ * This function can sleep, so it cannot be called from atomic context.
+ */
+int rproc_trigger_recovery(struct rproc *rproc)
+{
+       struct rproc_vdev *rvdev, *rvtmp;
+
+       dev_err(&rproc->dev, "recovering %s\n", rproc->name);
+
+       init_completion(&rproc->crash_comp);
+
+       /* clean up remote vdev entries */
+       list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node)
+               rproc_remove_virtio_dev(rvdev);
+
+       /* wait until there is no more rproc users */
+       wait_for_completion(&rproc->crash_comp);
+
+       return rproc_add_virtio_devices(rproc);
+}
+
 /**
  * rproc_crash_handler_work() - handle a crash
  *
@@ -911,7 +965,7 @@ static void rproc_crash_handler_work(struct work_struct *work)
 
        mutex_unlock(&rproc->lock);
 
-       /* TODO: handle crash */
+       rproc_trigger_recovery(rproc);
 }
 
 /**
@@ -1035,6 +1089,10 @@ void rproc_shutdown(struct rproc *rproc)
 
        rproc_disable_iommu(rproc);
 
+       /* if in crash state, unlock crash handler */
+       if (rproc->state == RPROC_CRASHED)
+               complete_all(&rproc->crash_comp);
+
        rproc->state = RPROC_OFFLINE;
 
        dev_info(dev, "stopped remote processor %s\n", rproc->name);
@@ -1069,7 +1127,7 @@ EXPORT_SYMBOL(rproc_shutdown);
 int rproc_add(struct rproc *rproc)
 {
        struct device *dev = &rproc->dev;
-       int ret = 0;
+       int ret;
 
        ret = device_add(dev);
        if (ret < 0)
@@ -1083,26 +1141,7 @@ int rproc_add(struct rproc *rproc)
        /* create debugfs entries */
        rproc_create_debug_dir(rproc);
 
-       /* rproc_del() calls must wait until async loader completes */
-       init_completion(&rproc->firmware_loading_complete);
-
-       /*
-        * We must retrieve early virtio configuration info from
-        * the firmware (e.g. whether to register a virtio device,
-        * what virtio features does it support, ...).
-        *
-        * We're initiating an asynchronous firmware loading, so we can
-        * be built-in kernel code, without hanging the boot process.
-        */
-       ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
-                                       rproc->firmware, dev, GFP_KERNEL,
-                                       rproc, rproc_fw_config_virtio);
-       if (ret < 0) {
-               dev_err(dev, "request_firmware_nowait failed: %d\n", ret);
-               complete_all(&rproc->firmware_loading_complete);
-       }
-
-       return ret;
+       return rproc_add_virtio_devices(rproc);
 }
 EXPORT_SYMBOL(rproc_add);
 
@@ -1209,6 +1248,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
        INIT_LIST_HEAD(&rproc->rvdevs);
 
        INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work);
+       init_completion(&rproc->crash_comp);
 
        rproc->state = RPROC_OFFLINE;
 
index a690ebe..7bb6648 100644 (file)
@@ -63,6 +63,7 @@ void rproc_free_vring(struct rproc_vring *rvring);
 int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
 
 void *rproc_da_to_va(struct rproc *rproc, u64 da, int len);
+int rproc_trigger_recovery(struct rproc *rproc);
 
 static inline
 int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
index a46ed27..0c1a2f9 100644 (file)
@@ -398,6 +398,7 @@ enum rproc_crash_type {
  * @index: index of this rproc device
  * @crash_handler: workqueue for handling a crash
  * @crash_cnt: crash counter
+ * @crash_comp: completion used to sync crash handler and the rproc reload
  */
 struct rproc {
        struct klist_node node;
@@ -423,6 +424,7 @@ struct rproc {
        int index;
        struct work_struct crash_handler;
        unsigned crash_cnt;
+       struct completion crash_comp;
 };
 
 /* we currently support only two vrings per rvdev */