From 7a186941626d19f668b08108db158379b32e6e02 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Mon, 13 Feb 2012 22:30:39 +0100 Subject: remoteproc: remove the single rpmsg vdev limitation Now that the resource table supports publishing a virtio device in a single resource entry, firmware images can start supporting more than a single vdev. This patch removes the single vdev limitation of the remoteproc framework so multi-vdev firmwares can be leveraged: VDEV resource entries are parsed when the rproc is registered, and as a result their vrings are set up and the virtio devices are registered (and they go away when the rproc goes away). Moreover, we no longer only support VIRTIO_ID_RPMSG vdevs; any virtio device type goes now. As a result, there's no more any rpmsg-specific APIs or code in remoteproc: it all becomes generic virtio handling. Signed-off-by: Ohad Ben-Cohen Cc: Brian Swetland Cc: Iliyan Malchev Cc: Arnd Bergmann Cc: Grant Likely Cc: Rusty Russell Cc: Mark Grosen Cc: John Williams Cc: Michal Simek Cc: Loic PALLARDY Cc: Ludovic BARRE Cc: Omar Ramirez Luna Cc: Guzman Lugo Fernando Cc: Anna Suman Cc: Clark Rob Cc: Stephen Boyd Cc: Saravana Kannan Cc: David Brown Cc: Kieran Bingham Cc: Tony Lindgren --- drivers/remoteproc/remoteproc_core.c | 292 ++++++++++++++++++----------------- 1 file changed, 150 insertions(+), 142 deletions(-) (limited to 'drivers/remoteproc/remoteproc_core.c') diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 10348451c6c9..ca02f128b435 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -52,8 +52,8 @@ static void klist_rproc_put(struct klist_node *n); * We need this in order to support name-based lookups (needed by the * rproc_get_by_name()). * - * That said, we don't use rproc_get_by_name() anymore within the rpmsg - * framework. The use cases that do require its existence should be + * That said, we don't use rproc_get_by_name() at this point. + * The use cases that do require its existence should be * scrutinized, and hopefully migrated to rproc_boot() using device-based * binding. * @@ -279,80 +279,112 @@ rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len) return ret; } -/** - * rproc_handle_early_vdev() - early handle a virtio header resource - * @rproc: the remote processor - * @rsc: the resource descriptor - * @avail: size of available data (for sanity checking the image) - * - * The existence of this virtio hdr resource entry means that the firmware - * of this @rproc supports this virtio device. - * - * Currently we support only a single virtio device of type VIRTIO_ID_RPMSG, - * but the plan is to remove this limitation and support any number - * of virtio devices (and of any type). We'll also add support for dynamically - * adding (and removing) virtio devices over the rpmsg bus, but simple - * firmwares that doesn't want to get involved with rpmsg will be able - * to simply use the resource table for this. - * - * Returns 0 on success, or an appropriate error code otherwise - */ -static int rproc_handle_early_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, - int avail) +static int +__rproc_handle_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) { - struct rproc_vdev *rvdev; + struct rproc *rproc = rvdev->rproc; + struct device *dev = rproc->dev; + struct fw_rsc_vdev_vring *vring = &rsc->vring[i]; + dma_addr_t dma; + void *va; + int ret, size, notifyid; - /* make sure resource isn't truncated */ - if (sizeof(*rsc) > avail) { - dev_err(rproc->dev, "vdev rsc is truncated\n"); + dev_dbg(dev, "vdev rsc: vring%d: da %x, qsz %d, align %d\n", + i, vring->da, vring->num, vring->align); + + /* make sure reserved bytes are zeroes */ + if (vring->reserved) { + dev_err(dev, "vring rsc has non zero reserved bytes\n"); return -EINVAL; } - /* we only support VIRTIO_ID_RPMSG devices for now */ - if (rsc->id != VIRTIO_ID_RPMSG) { - dev_warn(rproc->dev, "unsupported vdev: %d\n", rsc->id); + /* the firmware must provide the expected queue size */ + if (!vring->num) { + dev_err(dev, "invalid qsz (%d)\n", vring->num); return -EINVAL; } - /* we only support a single vdev per rproc for now */ - if (rproc->rvdev) { - dev_warn(rproc->dev, "redundant vdev entry\n"); + /* actual size of vring (in bytes) */ + size = PAGE_ALIGN(vring_size(vring->num, AMP_VRING_ALIGN)); + + if (!idr_pre_get(&rproc->notifyids, GFP_KERNEL)) { + dev_err(dev, "idr_pre_get failed\n"); + return -ENOMEM; + } + + /* + * Allocate non-cacheable memory for the vring. In the future + * this call will also configure the IOMMU for us + */ + va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL); + if (!va) { + dev_err(dev, "dma_alloc_coherent failed\n"); return -EINVAL; } - rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL); - if (!rvdev) - return -ENOMEM; + /* assign an rproc-wide unique index for this vring */ + /* TODO: assign a notifyid for rvdev updates as well */ + ret = idr_get_new(&rproc->notifyids, &rvdev->vring[i], ¬ifyid); + if (ret) { + dev_err(dev, "idr_get_new failed: %d\n", ret); + dma_free_coherent(dev, size, va, dma); + return ret; + } - /* remember the device features */ - rvdev->dfeatures = rsc->dfeatures; + /* let the rproc know the da and notifyid of this vring */ + /* TODO: expose this to remote processor */ + vring->da = dma; + vring->notifyid = notifyid; - rproc->rvdev = rvdev; - rvdev->rproc = rproc; + dev_dbg(dev, "vring%d: va %p dma %x size %x idr %d\n", i, va, + dma, size, notifyid); + + rvdev->vring[i].len = vring->num; + rvdev->vring[i].va = va; + rvdev->vring[i].dma = dma; + rvdev->vring[i].notifyid = notifyid; + rvdev->vring[i].rvdev = rvdev; return 0; } +static void __rproc_free_vrings(struct rproc_vdev *rvdev, int i) +{ + struct rproc *rproc = rvdev->rproc; + + for (i--; i > 0; i--) { + struct rproc_vring *rvring = &rvdev->vring[i]; + int size = PAGE_ALIGN(vring_size(rvring->len, AMP_VRING_ALIGN)); + + dma_free_coherent(rproc->dev, size, rvring->va, rvring->dma); + idr_remove(&rproc->notifyids, rvring->notifyid); + } +} + /** * rproc_handle_vdev() - handle a vdev fw resource * @rproc: the remote processor * @rsc: the vring resource descriptor * @avail: size of available data (for sanity checking the image) * - * This resource entry requires allocation of non-cacheable memory - * for a virtio vring. Currently we only support two vrings per remote - * processor, required for the virtio rpmsg device. - * - * The 'len' member of @rsc should contain the number of buffers this vring - * support and 'da' should either contain the device address where - * the remote processor is expecting the vring, or indicate that - * dynamically allocation of the vring's device address is supported. - * - * Note: 'da' is currently not handled. This will be revised when the generic - * iommu-based DMA API will arrive, or a dynanic & non-iommu use case show - * up. Meanwhile, statically-addressed iommu-based images should use - * RSC_DEVMEM resource entries to map their require 'da' to the physical - * address of their base CMA region. + * This resource entry requests the host to statically register a virtio + * device (vdev), and setup everything needed to support it. It contains + * everything needed to make it possible: the virtio device id, virtio + * device features, vrings information, virtio config space, etc... + * + * Before registering the vdev, the vrings are allocated from non-cacheable + * physically contiguous memory. Currently we only support two vrings per + * remote processor (temporary limitation). We might also want to consider + * doing the vring allocation only later when ->find_vqs() is invoked, and + * then release them upon ->del_vqs(). + * + * Note: @da is currently not really handled correctly: we dynamically + * allocate it using the DMA API, ignoring requested hard coded addresses, + * and we don't take care of any required IOMMU programming. This is all + * going to be taken care of when the generic iommu-based DMA API will be + * merged. Meanwhile, statically-addressed iommu-based firmware images should + * use RSC_DEVMEM resource entries to map their required @da to the physical + * address of their base CMA region (ouch, hacky!). * * Returns 0 on success, or an appropriate error code otherwise */ @@ -360,8 +392,8 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, int avail) { struct device *dev = rproc->dev; - struct rproc_vdev *rvdev = rproc->rvdev; - int i; + struct rproc_vdev *rvdev; + int i, ret; /* make sure resource isn't truncated */ if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) @@ -379,61 +411,41 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, dev_dbg(dev, "vdev rsc: id %d, dfeatures %x, cfg len %d, %d vrings\n", rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings); - /* no vdev is in place ? */ - if (!rvdev) { - dev_err(dev, "vring requested without a virtio dev entry\n"); - return -EINVAL; - } - - /* we currently support two vrings per rproc (for rx and tx) */ - if (rsc->num_of_vrings != ARRAY_SIZE(rvdev->vring)) { + /* we currently support only two vrings per rvdev */ + if (rsc->num_of_vrings > ARRAY_SIZE(rvdev->vring)) { dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings); return -EINVAL; } - /* initialize the vrings */ - for (i = 0; i < rsc->num_of_vrings; i++) { - struct fw_rsc_vdev_vring *vring = &rsc->vring[i]; - dma_addr_t dma; - int size; - void *va; - - /* make sure reserved bytes are zeroes */ - if (vring->reserved) { - dev_err(dev, "vring rsc has non zero reserved bytes\n"); - return -EINVAL; - } + rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL); + if (!rvdev) + return -ENOMEM; - /* the firmware must provide the expected queue size */ - if (!vring->num) { - dev_err(dev, "missing expected queue size\n"); - /* potential cleanups are taken care of later on */ - return -EINVAL; - } + rvdev->rproc = rproc; - /* actual size of vring (in bytes) */ - size = PAGE_ALIGN(vring_size(vring->num, AMP_VRING_ALIGN)); + /* allocate the vrings */ + for (i = 0; i < rsc->num_of_vrings; i++) { + ret = __rproc_handle_vring(rvdev, rsc, i); + if (ret) + goto free_vrings; + } - /* - * Allocate non-cacheable memory for the vring. In the future - * this call will also configure the IOMMU for us - */ - va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL); - if (!va) { - dev_err(dev, "dma_alloc_coherent failed\n"); - /* potential cleanups are taken care of later on */ - return -EINVAL; - } + /* remember the device features */ + rvdev->dfeatures = rsc->dfeatures; - dev_dbg(dev, "vring%d: va %p dma %x qsz %d ring size %x\n", i, - va, dma, vring->num, size); + list_add_tail(&rvdev->node, &rproc->rvdevs); - rvdev->vring[i].len = vring->num; - rvdev->vring[i].va = va; - rvdev->vring[i].dma = dma; - } + /* it is now safe to add the virtio device */ + ret = rproc_add_virtio_dev(rvdev, rsc->id); + if (ret) + goto free_vrings; return 0; + +free_vrings: + __rproc_free_vrings(rvdev, i); + kfree(rvdev); + return ret; } /** @@ -731,7 +743,7 @@ static rproc_handle_resource_t rproc_handle_rsc[] = { [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout, [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem, [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace, - [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev, + [RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */ }; /* handle firmware resource entries before booting the remote processor */ @@ -784,6 +796,7 @@ rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int l int offset = table->offset[i]; struct fw_rsc_hdr *hdr = (void *)table + offset; int avail = len - offset - sizeof(*hdr); + struct fw_rsc_vdev *vrsc; /* make sure table isn't truncated */ if (avail < 0) { @@ -793,12 +806,14 @@ rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int l dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type); - if (hdr->type == RSC_VDEV) { - struct fw_rsc_vdev *vrsc = - (struct fw_rsc_vdev *)hdr->data; - ret = rproc_handle_early_vdev(rproc, vrsc, avail); + if (hdr->type != RSC_VDEV) + continue; + + vrsc = (struct fw_rsc_vdev *)hdr->data; + + ret = rproc_handle_vdev(rproc, vrsc, avail); + if (ret) break; - } } return ret; @@ -889,14 +904,12 @@ static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data, * @rproc: rproc handle * * This function will free all resources acquired for @rproc, and it - * is called when @rproc shuts down, or just failed booting. + * is called whenever @rproc either shuts down or fails to boot. */ static void rproc_resource_cleanup(struct rproc *rproc) { struct rproc_mem_entry *entry, *tmp; struct device *dev = rproc->dev; - struct rproc_vdev *rvdev = rproc->rvdev; - int i; /* clean up debugfs trace entries */ list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { @@ -906,23 +919,6 @@ static void rproc_resource_cleanup(struct rproc *rproc) kfree(entry); } - /* free the coherent memory allocated for the vrings */ - for (i = 0; rvdev && i < ARRAY_SIZE(rvdev->vring); i++) { - int qsz = rvdev->vring[i].len; - void *va = rvdev->vring[i].va; - int dma = rvdev->vring[i].dma; - - /* virtqueue size is expressed in number of buffers supported */ - if (qsz) { - /* how many bytes does this vring really occupy ? */ - int size = PAGE_ALIGN(vring_size(qsz, AMP_VRING_ALIGN)); - - dma_free_coherent(rproc->dev, size, va, dma); - - rvdev->vring[i].len = 0; - } - } - /* clean up carveout allocations */ list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { dma_free_coherent(dev, entry->len, entry->va, entry->dma); @@ -1100,11 +1096,6 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context) goto out; } - /* add the virtio device (currently only rpmsg vdevs are supported) */ - ret = rproc_add_rpmsg_vdev(rproc); - if (ret) - goto out; - out: if (fw) release_firmware(fw); @@ -1266,13 +1257,23 @@ EXPORT_SYMBOL(rproc_shutdown); void rproc_release(struct kref *kref) { struct rproc *rproc = container_of(kref, struct rproc, refcount); + struct rproc_vdev *rvdev, *rvtmp; dev_info(rproc->dev, "removing %s\n", rproc->name); rproc_delete_debug_dir(rproc); - /* at this point no one holds a reference to rproc anymore */ - kfree(rproc); + /* clean up remote vdev entries */ + list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) { + __rproc_free_vrings(rvdev, RVDEV_NUM_VRINGS); + list_del(&rvdev->node); + } + + /* + * At this point no one holds a reference to rproc anymore, + * so we can directly unroll rproc_alloc() + */ + rproc_free(rproc); } /* will be called when an rproc is added to the rprocs klist */ @@ -1316,7 +1317,7 @@ static struct rproc *next_rproc(struct klist_iter *i) * use rproc_put() to decrement it back once rproc isn't needed anymore. * * Note: currently this function (and its counterpart rproc_put()) are not - * used anymore by the rpmsg subsystem. We need to scrutinize the use cases + * being used. We need to scrutinize the use cases * that still need them, and see if we can migrate them to use the non * name-based boot/shutdown interface. */ @@ -1391,11 +1392,8 @@ EXPORT_SYMBOL(rproc_put); * firmware. * * If found, those virtio devices will be created and added, so as a result - * of registering this remote processor, additional virtio drivers will be + * of registering this remote processor, additional virtio drivers might be * probed. - * - * Currently, though, we only support a single RPMSG virtio vdev per remote - * processor. */ int rproc_register(struct rproc *rproc) { @@ -1418,7 +1416,7 @@ int rproc_register(struct rproc *rproc) /* * We must retrieve early virtio configuration info from - * the firmware (e.g. whether to register a virtio rpmsg device, + * 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 @@ -1487,9 +1485,12 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, mutex_init(&rproc->lock); + idr_init(&rproc->notifyids); + INIT_LIST_HEAD(&rproc->carveouts); INIT_LIST_HEAD(&rproc->mappings); INIT_LIST_HEAD(&rproc->traces); + INIT_LIST_HEAD(&rproc->rvdevs); rproc->state = RPROC_OFFLINE; @@ -1509,6 +1510,9 @@ EXPORT_SYMBOL(rproc_alloc); */ void rproc_free(struct rproc *rproc) { + idr_remove_all(&rproc->notifyids); + idr_destroy(&rproc->notifyids); + kfree(rproc); } EXPORT_SYMBOL(rproc_free); @@ -1535,18 +1539,22 @@ EXPORT_SYMBOL(rproc_free); */ int rproc_unregister(struct rproc *rproc) { + struct rproc_vdev *rvdev; + if (!rproc) return -EINVAL; /* if rproc is just being registered, wait */ wait_for_completion(&rproc->firmware_loading_complete); - /* was an rpmsg vdev created ? */ - if (rproc->rvdev) - rproc_remove_rpmsg_vdev(rproc); + /* clean up remote vdev entries */ + list_for_each_entry(rvdev, &rproc->rvdevs, node) + rproc_remove_virtio_dev(rvdev); - klist_remove(&rproc->node); + /* the rproc is downref'ed as soon as it's removed from the klist */ + klist_del(&rproc->node); + /* the rproc will only be released after its refcount drops to zero */ kref_put(&rproc->refcount, rproc_release); return 0; -- cgit v1.2.3