From: Erwan Yvin <erwan.y...@stericsson.com>

Implement the vringh callback functions in order
to manage host virtio rings and handle kicks.
This allows virtio device to request host-virtio-rings.

Signed-off-by: Erwan Yvin <erwan.y...@stericsson.com>
---
 drivers/remoteproc/remoteproc_virtio.c |  115 +++++++++++++++++++++++++++++++-
 include/linux/remoteproc.h             |   22 ++++++
 2 files changed, 134 insertions(+), 3 deletions(-)

diff --git a/drivers/remoteproc/remoteproc_virtio.c 
b/drivers/remoteproc/remoteproc_virtio.c
index ef5ec8a..4ad87c5 100644
--- a/drivers/remoteproc/remoteproc_virtio.c
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -23,12 +23,15 @@
 #include <linux/virtio_config.h>
 #include <linux/virtio_ids.h>
 #include <linux/virtio_ring.h>
+#include <linux/vringh.h>
 #include <linux/err.h>
 #include <linux/kref.h>
 #include <linux/slab.h>
 
 #include "remoteproc_internal.h"
 
+static u32 rproc_virtio_get_features(struct virtio_device *vdev);
+
 /* kick the remote processor, and let it know which virtqueue to poke at */
 static void rproc_virtio_notify(struct virtqueue *vq)
 {
@@ -41,6 +44,18 @@ static void rproc_virtio_notify(struct virtqueue *vq)
        rproc->ops->kick(rproc, notifyid);
 }
 
+/* kick the remote processor, and let it know which vring to poke at */
+static void rproc_virtio_vringh_notify(struct vringh *vrh)
+{
+       struct rproc_vring *rvring = vringh_to_rvring(vrh);
+       struct rproc *rproc = rvring->rvdev->rproc;
+       int notifyid = rvring->notifyid;
+
+       dev_dbg(&rproc->dev, "kicking vq index: %d\n", notifyid);
+
+       rproc->ops->kick(rproc, notifyid);
+}
+
 /**
  * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
  * @rproc: handle to the remote processor
@@ -60,10 +75,18 @@ irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int 
notifyid)
        dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid);
 
        rvring = idr_find(&rproc->notifyids, notifyid);
-       if (!rvring || !rvring->vq)
+       if (!rvring)
                return IRQ_NONE;
 
-       return vring_interrupt(0, rvring->vq);
+       if (rvring->rvringh && rvring->rvringh->vringh_cb) {
+               rvring->rvringh->vringh_cb(&rvring->rvdev->vdev,
+                                               &rvring->rvringh->vrh);
+               return IRQ_HANDLED;
+       } else if (rvring->vq) {
+               return vring_interrupt(0, rvring->vq);
+       } else {
+               return IRQ_NONE;
+       }
 }
 EXPORT_SYMBOL(rproc_vq_interrupt);
 
@@ -103,7 +126,7 @@ static int __rproc_virtio_find_rings(struct virtio_device 
*vdev,
         */
        for (id = rng = 0; id < nrings && rng < max; ++rng) {
                rvring = &rvdev->vring[rng];
-               if (rvring->vq)
+               if (rvring->vq || rvring->rvringh)
                        continue;
 
                /* Allocate and initialize the virtio ring */
@@ -199,6 +222,86 @@ static void rproc_virtio_del_vqs(struct virtio_device 
*vdev)
        __rproc_virtio_del_vqs(vdev);
 }
 
+static void __rproc_virtio_del_vrhs(struct virtio_device *vdev)
+{
+       struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+       int i, num_of_vrings = ARRAY_SIZE(rvdev->vring);
+
+       for (i = 0; i < num_of_vrings; i++) {
+               struct rproc_vring *rvring = &rvdev->vring[i];
+               if (!rvring->rvringh)
+                       continue;
+               kfree(rvring->rvringh);
+               rvring->rvringh = NULL;
+               rproc_free_vring(rvring);
+       }
+}
+
+/* Helper function that creates and initializes the host virtio ring */
+static void *__create_new_vringh(struct rproc_vring *rvring,
+                                unsigned int index,
+                                void *callback,
+                                const char *name)
+{
+       struct rproc_vringh *rvrh = NULL;
+       struct rproc_vdev *rvdev = rvring->rvdev;
+       int err;
+
+       rvrh = kzalloc(sizeof(*rvrh), GFP_KERNEL);
+       err = -ENOMEM;
+       if (!rvrh)
+               goto err;
+
+       /* initialize the host virtio ring */
+       rvrh->rvring = rvring;
+       rvrh->vringh_cb = callback;
+       rvrh->vrh.notify = rproc_virtio_vringh_notify;
+       memset(rvring->va, 0, vring_size(rvring->len, rvring->align));
+       vring_init(&rvrh->vrh.vring, rvring->len, rvring->va, rvring->align);
+
+       /*
+        * Create the new vring host, and tell we're not interested in
+        * the 'weak' smp barriers, since we're talking with a real device.
+        */
+       err = vringh_init_kern(&rvrh->vrh,
+                               rproc_virtio_get_features(&rvdev->vdev),
+                               rvring->len,
+                               false,
+                               rvrh->vrh.vring.desc,
+                               rvrh->vrh.vring.avail,
+                               rvrh->vrh.vring.used);
+       if (err)
+               goto err;
+
+       rvring->rvringh = rvrh;
+       return &rvrh->vrh;
+err:
+       kfree(rvrh);
+       return ERR_PTR(err);
+}
+
+static int rproc_virtio_find_vrhs(struct virtio_device *vdev, unsigned nhvrs,
+                        struct vringh *vrhs[],
+                        vrh_callback_t *callbacks[])
+{
+       void *cbs = callbacks, *rings = vrhs;
+       int err = __rproc_virtio_find_rings(vdev, nhvrs, rings, cbs,
+                                               NULL, __create_new_vringh);
+       if (err)
+               __rproc_virtio_del_vrhs(vdev);
+       return err;
+}
+
+static void rproc_virtio_del_vrhs(struct virtio_device *vdev)
+{
+       struct rproc *rproc = vdev_to_rproc(vdev);
+
+       /* power down the remote processor before deleting vqs */
+       rproc_shutdown(rproc);
+
+       __rproc_virtio_del_vrhs(vdev);
+}
+
 /*
  * We don't support yet real virtio status semantics.
  *
@@ -258,6 +361,11 @@ static struct virtio_config_ops rproc_virtio_config_ops = {
        .get_status     = rproc_virtio_get_status,
 };
 
+static struct vringh_config_ops rproc_virtio_vringh_ops = {
+       .find_vrhs      = rproc_virtio_find_vrhs,
+       .del_vrhs       = rproc_virtio_del_vrhs,
+};
+
 /*
  * This function is called whenever vdev is released, and is responsible
  * to decrement the remote processor's refcount which was taken when vdev was
@@ -296,6 +404,7 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
 
        vdev->id.device = id,
        vdev->config = &rproc_virtio_config_ops,
+       vdev->vringh_config = &rproc_virtio_vringh_ops;
        vdev->dev.parent = dev;
        vdev->dev.release = rproc_vdev_release;
 
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index faf3332..9796562 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -39,6 +39,7 @@
 #include <linux/klist.h>
 #include <linux/mutex.h>
 #include <linux/virtio.h>
+#include <linux/vringh.h>
 #include <linux/completion.h>
 #include <linux/idr.h>
 
@@ -435,6 +436,19 @@ struct rproc {
 #define RVDEV_NUM_VRINGS 2
 
 /**
+ * struct rproc_vringh - remoteproc host vring
+ * @vrh: Host side virtio ring
+ * @rvring: Virtio ring associated with the device
+ * @vringh_cb: Callback notifying virtio driver about new buffers
+ */
+struct rproc_vring;
+struct rproc_vringh {
+       struct vringh vrh;
+       struct rproc_vring *rvring;
+       vrh_callback_t *vringh_cb;
+};
+
+/**
  * struct rproc_vring - remoteproc vring state
  * @va:        virtual address
  * @dma: dma address
@@ -444,6 +458,7 @@ struct rproc {
  * @notifyid: rproc-specific unique vring index
  * @rvdev: remote vdev
  * @vq: the virtqueue of this vring
+ * @rvringh: the reversed host-side vring
  */
 struct rproc_vring {
        void *va;
@@ -454,6 +469,7 @@ struct rproc_vring {
        int notifyid;
        struct rproc_vdev *rvdev;
        struct virtqueue *vq;
+       struct rproc_vringh *rvringh;
 };
 
 /**
@@ -497,4 +513,10 @@ static inline struct rproc *vdev_to_rproc(struct 
virtio_device *vdev)
        return rvdev->rproc;
 }
 
+static inline struct rproc_vring *vringh_to_rvring(struct vringh *vrh)
+{
+       struct rproc_vringh *rvrh = container_of(vrh, struct rproc_vringh, vrh);
+       return rvrh->rvring;
+}
+
 #endif /* REMOTEPROC_H */
-- 
1.7.9.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to