summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/remoteproc/remoteproc_core.c84
-rw-r--r--drivers/remoteproc/remoteproc_internal.h1
-rw-r--r--include/linux/remoteproc.h2
3 files changed, 65 insertions, 22 deletions
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 93e2b3526543..5000d7589cf5 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -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;
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index a690ebe7aa51..7bb66482d061 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -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)
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index a46ed2723803..0c1a2f95be76 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -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 */