diff options
-rw-r--r-- | drivers/remoteproc/remoteproc_core.c | 84 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_internal.h | 1 | ||||
-rw-r--r-- | include/linux/remoteproc.h | 2 |
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 */ |