[PATCH] remoteproc: allocate vrings on demand, free when not needed
Guzman Lugo, Fernando
fernando.lugo at ti.com
Mon May 21 12:49:10 EDT 2012
Looks good to me.
Tested-by: Fernando Guzman Lugo <fernando.lugo at ti.com>
Regards,
Fernando.
On Sun, May 20, 2012 at 7:00 AM, Ohad Ben-Cohen <ohad at wizery.com> wrote:
>
> Dynamically allocate the vrings' DMA when the remote processor
> is about to be powered on (i.e. when ->find_vqs() is invoked),
> and release them as soon as it is powered off (i.e. when ->del_vqs()
> is invoked).
>
> The obvious and immediate benefit is better memory utilization, since
> memory for the vrings is now only allocated when the relevant remote
> processor is being used.
>
> Additionally, this approach also makes recovery of a (crashing)
> remote processor easier: one just needs to remove the relevant
> vdevs, and the entire vrings cleanup takes place automagically.
>
> Tested-by: Fernando Guzman Lugo <fernando.lugo at ti.com>
> Signed-off-by: Ohad Ben-Cohen <ohad at wizery.com>
> ---
> drivers/remoteproc/remoteproc_core.c | 109
> +++++++++++++++---------------
> drivers/remoteproc/remoteproc_internal.h | 2 +
> drivers/remoteproc/remoteproc_virtio.c | 13 +++-
> 3 files changed, 67 insertions(+), 57 deletions(-)
>
> diff --git a/drivers/remoteproc/remoteproc_core.c
> b/drivers/remoteproc/remoteproc_core.c
> index e756a0d..40e2b2d 100644
> --- a/drivers/remoteproc/remoteproc_core.c
> +++ b/drivers/remoteproc/remoteproc_core.c
> @@ -279,34 +279,17 @@ rproc_load_segments(struct rproc *rproc, const u8
> *elf_data, size_t len)
> return ret;
> }
>
> -static int
> -__rproc_handle_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc,
> int i)
> +int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
> {
> struct rproc *rproc = rvdev->rproc;
> struct device *dev = rproc->dev;
> - struct fw_rsc_vdev_vring *vring = &rsc->vring[i];
> + struct rproc_vring *rvring = &rvdev->vring[i];
> dma_addr_t dma;
> void *va;
> int ret, size, notifyid;
>
> - 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;
> - }
> -
> - /* verify queue size and vring alignment are sane */
> - if (!vring->num || !vring->align) {
> - dev_err(dev, "invalid qsz (%d) or alignment (%d)\n",
> - vring->num, vring->align);
> - return -EINVAL;
> - }
> -
> /* actual size of vring (in bytes) */
> - size = PAGE_ALIGN(vring_size(vring->num, vring->align));
> + size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
>
> if (!idr_pre_get(&rproc->notifyids, GFP_KERNEL)) {
> dev_err(dev, "idr_pre_get failed\n");
> @@ -316,6 +299,7 @@ __rproc_handle_vring(struct rproc_vdev *rvdev, struct
> fw_rsc_vdev *rsc, int i)
> /*
> * Allocate non-cacheable memory for the vring. In the future
> * this call will also configure the IOMMU for us
> + * TODO: let the rproc know the da of this vring
> */
> va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL);
> if (!va) {
> @@ -323,44 +307,67 @@ __rproc_handle_vring(struct rproc_vdev *rvdev,
> struct fw_rsc_vdev *rsc, int i)
> return -EINVAL;
> }
>
> - /* 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);
> + /*
> + * Assign an rproc-wide unique index for this vring
> + * TODO: assign a notifyid for rvdev updates as well
> + * TODO: let the rproc know the notifyid of this vring
> + * TODO: support predefined notifyids (via resource table)
> + */
> + ret = idr_get_new(&rproc->notifyids, rvring, ¬ifyid);
> if (ret) {
> dev_err(dev, "idr_get_new failed: %d\n", ret);
> dma_free_coherent(dev, size, va, dma);
> return ret;
> }
>
> - /* let the rproc know the da and notifyid of this vring */
> - /* TODO: expose this to remote processor */
> - vring->da = dma;
> - vring->notifyid = notifyid;
> -
> 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].align = vring->align;
> - rvdev->vring[i].va = va;
> - rvdev->vring[i].dma = dma;
> - rvdev->vring[i].notifyid = notifyid;
> - rvdev->vring[i].rvdev = rvdev;
> + rvring->va = va;
> + rvring->dma = dma;
> + rvring->notifyid = notifyid;
>
> return 0;
> }
>
> -static void __rproc_free_vrings(struct rproc_vdev *rvdev, int i)
> +static int
> +rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int
> i)
> {
> struct rproc *rproc = rvdev->rproc;
> + struct device *dev = rproc->dev;
> + struct fw_rsc_vdev_vring *vring = &rsc->vring[i];
> + struct rproc_vring *rvring = &rvdev->vring[i];
>
> - for (i--; i >= 0; i--) {
> - struct rproc_vring *rvring = &rvdev->vring[i];
> - int size = PAGE_ALIGN(vring_size(rvring->len,
> rvring->align));
> + 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;
> + }
>
> - dma_free_coherent(rproc->dev, size, rvring->va,
> rvring->dma);
> - idr_remove(&rproc->notifyids, rvring->notifyid);
> + /* verify queue size and vring alignment are sane */
> + if (!vring->num || !vring->align) {
> + dev_err(dev, "invalid qsz (%d) or alignment (%d)\n",
> + vring->num, vring->align);
> + return -EINVAL;
> }
> +
> + rvring->len = vring->num;
> + rvring->align = vring->align;
> + rvring->rvdev = rvdev;
> +
> + 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;
> +
> + dma_free_coherent(rproc->dev, size, rvring->va, rvring->dma);
> + idr_remove(&rproc->notifyids, rvring->notifyid);
> }
>
> /**
> @@ -425,11 +432,11 @@ static int rproc_handle_vdev(struct rproc *rproc,
> struct fw_rsc_vdev *rsc,
>
> rvdev->rproc = rproc;
>
> - /* allocate the vrings */
> + /* parse the vrings */
> for (i = 0; i < rsc->num_of_vrings; i++) {
> - ret = __rproc_handle_vring(rvdev, rsc, i);
> + ret = rproc_parse_vring(rvdev, rsc, i);
> if (ret)
> - goto free_vrings;
> + goto free_rvdev;
> }
>
> /* remember the device features */
> @@ -440,12 +447,11 @@ static int rproc_handle_vdev(struct rproc *rproc,
> struct fw_rsc_vdev *rsc,
> /* it is now safe to add the virtio device */
> ret = rproc_add_virtio_dev(rvdev, rsc->id);
> if (ret)
> - goto free_vrings;
> + goto free_rvdev;
>
> return 0;
>
> -free_vrings:
> - __rproc_free_vrings(rvdev, i);
> +free_rvdev:
> kfree(rvdev);
> return ret;
> }
> @@ -1265,18 +1271,11 @@ 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);
>
> - /* 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()
> @@ -1547,7 +1546,7 @@ EXPORT_SYMBOL(rproc_free);
> */
> int rproc_unregister(struct rproc *rproc)
> {
> - struct rproc_vdev *rvdev;
> + struct rproc_vdev *rvdev, *tmp;
>
> if (!rproc)
> return -EINVAL;
> @@ -1556,7 +1555,7 @@ int rproc_unregister(struct rproc *rproc)
> wait_for_completion(&rproc->firmware_loading_complete);
>
> /* clean up remote vdev entries */
> - list_for_each_entry(rvdev, &rproc->rvdevs, node)
> + list_for_each_entry_safe(rvdev, tmp, &rproc->rvdevs, node)
> rproc_remove_virtio_dev(rvdev);
>
> /* the rproc is downref'ed as soon as it's removed from the klist
> */
> diff --git a/drivers/remoteproc/remoteproc_internal.h
> b/drivers/remoteproc/remoteproc_internal.h
> index 9f336d6..f4957cf 100644
> --- a/drivers/remoteproc/remoteproc_internal.h
> +++ b/drivers/remoteproc/remoteproc_internal.h
> @@ -41,4 +41,6 @@ void rproc_create_debug_dir(struct rproc *rproc);
> void rproc_init_debugfs(void);
> void rproc_exit_debugfs(void);
>
> +void rproc_free_vring(struct rproc_vring *rvring);
> +int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
> #endif /* REMOTEPROC_INTERNAL_H */
> diff --git a/drivers/remoteproc/remoteproc_virtio.c
> b/drivers/remoteproc/remoteproc_virtio.c
> index ecf6121..26a7144 100644
> --- a/drivers/remoteproc/remoteproc_virtio.c
> +++ b/drivers/remoteproc/remoteproc_virtio.c
> @@ -77,14 +77,17 @@ static struct virtqueue *rp_find_vq(struct
> virtio_device *vdev,
> struct rproc_vring *rvring;
> struct virtqueue *vq;
> void *addr;
> - int len, size;
> + int len, size, ret;
>
> /* we're temporarily limited to two virtqueues per rvdev */
> if (id >= ARRAY_SIZE(rvdev->vring))
> return ERR_PTR(-EINVAL);
>
> - rvring = &rvdev->vring[id];
> + ret = rproc_alloc_vring(rvdev, id);
> + if (ret)
> + return ERR_PTR(ret);
>
> + rvring = &rvdev->vring[id];
> addr = rvring->va;
> len = rvring->len;
>
> @@ -103,6 +106,7 @@ static struct virtqueue *rp_find_vq(struct
> virtio_device *vdev,
> rproc_virtio_notify, callback,
> name);
> if (!vq) {
> dev_err(rproc->dev, "vring_new_virtqueue %s failed\n",
> name);
> + rproc_free_vring(rvring);
> return ERR_PTR(-ENOMEM);
> }
>
> @@ -125,6 +129,7 @@ static void rproc_virtio_del_vqs(struct virtio_device
> *vdev)
> rvring = vq->priv;
> rvring->vq = NULL;
> vring_del_virtqueue(vq);
> + rproc_free_vring(rvring);
> }
> }
>
> @@ -228,8 +233,12 @@ static struct virtio_config_ops
> rproc_virtio_config_ops = {
> static void rproc_vdev_release(struct device *dev)
> {
> struct virtio_device *vdev = dev_to_virtio(dev);
> + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
> struct rproc *rproc = vdev_to_rproc(vdev);
>
> + list_del(&rvdev->node);
> + kfree(rvdev);
> +
> kref_put(&rproc->refcount, rproc_release);
> }
>
> --
> 1.7.5.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
More information about the linux-arm-kernel
mailing list