Skip to content

Commit

Permalink
Merge tag 'remoteproc-for-3.7' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…kernel/git/ohad/remoteproc

Pull remoteproc update from Ohad Ben-Cohen:

 - Remoteproc Recovery - by Fernando Guzman Lugo

   When a remote processor crash is detected, this mechanism will remove
   all virtio children devices, wait until their drivers let go, hard
   reset the remote processor and reload the firmware (resulting in the
   relevant virtio children devices re-added).  Essentially the entire
   software stack is reset, together with the relevant hardware, so
   users don't have to reset the entire phone.

 - STE Modem driver is added - by Sjur Brændeland

 - OMAP DSP boot address support is added - by Juan Gutierrez

 - A handful of fixes/cleanups - Sjur Brændeland, Dan Carpenter, Emil
   Goode

* tag 'remoteproc-for-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc:
  remoteproc: Fix use of format specifyer
  remoteproc: fix a potential NULL-dereference on cleanup
  remoteproc: select VIRTIO to avoid build breakage
  remoteproc: return -EFAULT on copy_from_user failure
  remoteproc: snprintf() can return more than was printed
  remoteproc: Add STE modem driver
  remtoteproc: maintain max notifyid
  remoteproc: create a 'recovery' debugfs entry
  remoteproc: add actual recovery implementation
  remoteproc: add rproc_report_crash function to notify rproc crashes
  remoteproc: Add dependency to HAS_DMA
  remoteproc/omap: set bootaddr support
  • Loading branch information
Linus Torvalds committed Oct 4, 2012
2 parents d66e673 + d09f53a commit 4d6d367
Show file tree
Hide file tree
Showing 11 changed files with 682 additions and 42 deletions.
7 changes: 7 additions & 0 deletions Documentation/remoteproc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ int dummy_rproc_example(struct rproc *my_rproc)

Returns 0 on success and -EINVAL if @rproc isn't valid.

void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type)
- Report a crash in a remoteproc
This function must be called every time a crash is detected by the
platform specific rproc implementation. This should not be called from a
non-remoteproc driver. This function can be called from atomic/interrupt
context.

5. Implementation callbacks

These callbacks should be provided by platform-specific remoteproc
Expand Down
14 changes: 14 additions & 0 deletions drivers/remoteproc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ menu "Remoteproc drivers (EXPERIMENTAL)"
config REMOTEPROC
tristate
depends on EXPERIMENTAL
depends on HAS_DMA
select FW_CONFIG
select VIRTIO

config OMAP_REMOTEPROC
tristate "OMAP remoteproc support"
depends on EXPERIMENTAL
depends on HAS_DMA
depends on ARCH_OMAP4
depends on OMAP_IOMMU
select REMOTEPROC
Expand All @@ -27,4 +30,15 @@ config OMAP_REMOTEPROC
It's safe to say n here if you're not interested in multimedia
offloading or just want a bare minimum kernel.

config STE_MODEM_RPROC
tristate "STE-Modem remoteproc support"
depends on EXPERIMENTAL
depends on HAS_DMA
select REMOTEPROC
default n
help
Say y or m here to support STE-Modem shared memory driver.
This can be either built-in or a loadable module.
If unsure say N.

endmenu
1 change: 1 addition & 0 deletions drivers/remoteproc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ remoteproc-y += remoteproc_debugfs.o
remoteproc-y += remoteproc_virtio.o
remoteproc-y += remoteproc_elf_loader.o
obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o
3 changes: 3 additions & 0 deletions drivers/remoteproc/omap_remoteproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ static int omap_rproc_start(struct rproc *rproc)
struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
int ret;

if (pdata->set_bootaddr)
pdata->set_bootaddr(rproc->bootaddr);

oproc->nb.notifier_call = omap_rproc_mbox_callback;

/* every omap rproc is assigned a mailbox instance for messaging */
Expand Down
209 changes: 169 additions & 40 deletions drivers/remoteproc/remoteproc_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,25 +50,38 @@ typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail);
/* Unique indices for remoteproc devices */
static DEFINE_IDA(rproc_dev_index);

static const char * const rproc_crash_names[] = {
[RPROC_MMUFAULT] = "mmufault",
};

/* translate rproc_crash_type to string */
static const char *rproc_crash_to_string(enum rproc_crash_type type)
{
if (type < ARRAY_SIZE(rproc_crash_names))
return rproc_crash_names[type];
return "unkown";
}

/*
* This is the IOMMU fault handler we register with the IOMMU API
* (when relevant; not all remote processors access memory through
* an IOMMU).
*
* IOMMU core will invoke this handler whenever the remote processor
* will try to access an unmapped device address.
*
* Currently this is mostly a stub, but it will be later used to trigger
* the recovery of the remote processor.
*/
static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev,
unsigned long iova, int flags, void *token)
{
struct rproc *rproc = token;

dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags);

rproc_report_crash(rproc, RPROC_MMUFAULT);

/*
* Let the iommu core know we're not really handling this fault;
* we just plan to use this as a recovery trigger.
* we just used it as a recovery trigger.
*/
return -ENOSYS;
}
Expand Down Expand Up @@ -215,8 +228,11 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
return ret;
}

dev_dbg(dev, "vring%d: va %p dma %x size %x idr %d\n", i, va,
dma, size, notifyid);
/* Store largest notifyid */
rproc->max_notifyid = max(rproc->max_notifyid, notifyid);

dev_dbg(dev, "vring%d: va %p dma %llx size %x idr %d\n", i, va,
(unsigned long long)dma, size, notifyid);

rvring->va = va;
rvring->dma = dma;
Expand Down Expand Up @@ -256,13 +272,25 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
return 0;
}

static int rproc_max_notifyid(int id, void *p, void *data)
{
int *maxid = data;
*maxid = max(*maxid, id);
return 0;
}

void rproc_free_vring(struct rproc_vring *rvring)
{
int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
struct rproc *rproc = rvring->rvdev->rproc;
int maxid = 0;

dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma);
idr_remove(&rproc->notifyids, rvring->notifyid);

/* Find the largest remaining notifyid */
idr_for_each(&rproc->notifyids, rproc_max_notifyid, &maxid);
rproc->max_notifyid = maxid;
}

/**
Expand Down Expand Up @@ -545,17 +573,10 @@ static int rproc_handle_carveout(struct rproc *rproc,
dev_dbg(dev, "carveout rsc: da %x, pa %x, len %x, flags %x\n",
rsc->da, rsc->pa, rsc->len, rsc->flags);

mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
if (!mapping) {
dev_err(dev, "kzalloc mapping failed\n");
return -ENOMEM;
}

carveout = kzalloc(sizeof(*carveout), GFP_KERNEL);
if (!carveout) {
dev_err(dev, "kzalloc carveout failed\n");
ret = -ENOMEM;
goto free_mapping;
return -ENOMEM;
}

va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL);
Expand All @@ -565,7 +586,8 @@ static int rproc_handle_carveout(struct rproc *rproc,
goto free_carv;
}

dev_dbg(dev, "carveout va %p, dma %x, len 0x%x\n", va, dma, rsc->len);
dev_dbg(dev, "carveout va %p, dma %llx, len 0x%x\n", va,
(unsigned long long)dma, rsc->len);

/*
* Ok, this is non-standard.
Expand All @@ -585,11 +607,18 @@ static int rproc_handle_carveout(struct rproc *rproc,
* physical address in this case.
*/
if (rproc->domain) {
mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
if (!mapping) {
dev_err(dev, "kzalloc mapping failed\n");
ret = -ENOMEM;
goto dma_free;
}

ret = iommu_map(rproc->domain, rsc->da, dma, rsc->len,
rsc->flags);
if (ret) {
dev_err(dev, "iommu_map failed: %d\n", ret);
goto dma_free;
goto free_mapping;
}

/*
Expand All @@ -603,7 +632,8 @@ static int rproc_handle_carveout(struct rproc *rproc,
mapping->len = rsc->len;
list_add_tail(&mapping->node, &rproc->mappings);

dev_dbg(dev, "carveout mapped 0x%x to 0x%x\n", rsc->da, dma);
dev_dbg(dev, "carveout mapped 0x%x to 0x%llx\n",
rsc->da, (unsigned long long)dma);
}

/*
Expand Down Expand Up @@ -634,12 +664,12 @@ static int rproc_handle_carveout(struct rproc *rproc,

return 0;

free_mapping:
kfree(mapping);
dma_free:
dma_free_coherent(dev->parent, rsc->len, va, dma);
free_carv:
kfree(carveout);
free_mapping:
kfree(mapping);
return ret;
}

Expand Down Expand Up @@ -871,6 +901,91 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
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
*
* This function needs to handle everything related to a crash, like cpu
* registers and stack dump, information to help to debug the fatal error, etc.
*/
static void rproc_crash_handler_work(struct work_struct *work)
{
struct rproc *rproc = container_of(work, struct rproc, crash_handler);
struct device *dev = &rproc->dev;

dev_dbg(dev, "enter %s\n", __func__);

mutex_lock(&rproc->lock);

if (rproc->state == RPROC_CRASHED || rproc->state == RPROC_OFFLINE) {
/* handle only the first crash detected */
mutex_unlock(&rproc->lock);
return;
}

rproc->state = RPROC_CRASHED;
dev_err(dev, "handling crash #%u in %s\n", ++rproc->crash_cnt,
rproc->name);

mutex_unlock(&rproc->lock);

if (!rproc->recovery_disabled)
rproc_trigger_recovery(rproc);
}

/**
* rproc_boot() - boot a remote processor
* @rproc: handle of a remote processor
Expand Down Expand Up @@ -992,6 +1107,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);
Expand Down Expand Up @@ -1026,7 +1145,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)
Expand All @@ -1040,26 +1159,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);

Expand Down Expand Up @@ -1165,6 +1265,9 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
INIT_LIST_HEAD(&rproc->traces);
INIT_LIST_HEAD(&rproc->rvdevs);

INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work);
init_completion(&rproc->crash_comp);

rproc->state = RPROC_OFFLINE;

return rproc;
Expand Down Expand Up @@ -1221,6 +1324,32 @@ int rproc_del(struct rproc *rproc)
}
EXPORT_SYMBOL(rproc_del);

/**
* rproc_report_crash() - rproc crash reporter function
* @rproc: remote processor
* @type: crash type
*
* This function must be called every time a crash is detected by the low-level
* drivers implementing a specific remoteproc. This should not be called from a
* non-remoteproc driver.
*
* This function can be called from atomic/interrupt context.
*/
void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type)
{
if (!rproc) {
pr_err("NULL rproc pointer\n");
return;
}

dev_err(&rproc->dev, "crash detected in %s: type %s\n",
rproc->name, rproc_crash_to_string(type));

/* create a new task to handle the error */
schedule_work(&rproc->crash_handler);
}
EXPORT_SYMBOL(rproc_report_crash);

static int __init remoteproc_init(void)
{
rproc_init_debugfs();
Expand Down
Loading

0 comments on commit 4d6d367

Please sign in to comment.