From: Julien STEPHAN <jstep...@baylibre.com>

In order to improve performances and flexibility,
add support of async request.

Signed-off-by: Julien STEPHAN <jstep...@baylibre.com>
Signed-off-by: Alexandre Bailon <abai...@baylibre.com>
---
 drivers/rpmsg/apu_rpmsg.c      | 208 ++++++++++++++++++++++-----------
 include/uapi/linux/apu_rpmsg.h |   6 +-
 2 files changed, 144 insertions(+), 70 deletions(-)

diff --git a/drivers/rpmsg/apu_rpmsg.c b/drivers/rpmsg/apu_rpmsg.c
index 5131b8b8e1f2..e14597c467d7 100644
--- a/drivers/rpmsg/apu_rpmsg.c
+++ b/drivers/rpmsg/apu_rpmsg.c
@@ -34,11 +34,16 @@ struct rpmsg_apu {
        struct iommu_domain *domain;
        struct iova_domain *iovad;
        int iova_limit_pfn;
+       wait_queue_head_t waitqueue;
+       u8 available_response;
+       spinlock_t ctx_lock;
+       struct list_head requests;
 };
 
 struct rpmsg_request {
-       struct completion completion;
+       u8 ready;
        struct list_head node;
+       struct apu_buffer *buffer;
        void *req;
 };
 
@@ -68,25 +73,35 @@ static dev_t rpmsg_major;
 static DEFINE_IDA(rpmsg_ctrl_ida);
 static DEFINE_IDA(rpmsg_minor_ida);
 static DEFINE_IDA(req_ida);
-static LIST_HEAD(requests);
 static struct apu_iova_domain *apu_iovad;
 
-static int apu_rpmsg_callback(struct rpmsg_device *dev, void *data, int count,
+
+static int apu_rpmsg_callback(struct rpmsg_device *rpdev, void *data, int 
count,
                              void *priv, u32 addr)
 {
+       struct rpmsg_apu *apu = dev_get_drvdata(&rpdev->dev);
        struct rpmsg_request *rpmsg_req;
        struct apu_dev_request *hdr = data;
+       unsigned long flags;
 
-       list_for_each_entry(rpmsg_req, &requests, node) {
-               struct apu_dev_request *tmp_hdr = rpmsg_req->req;
+       spin_lock_irqsave(&apu->ctx_lock, flags);
+       list_for_each_entry(rpmsg_req, &apu->requests, node) {
+               struct apu_request *tmp_hdr = rpmsg_req->req;
 
                if (hdr->id == tmp_hdr->id) {
-                       memcpy(rpmsg_req->req, data, count);
-                       complete(&rpmsg_req->completion);
-
-                       return 0;
+                       rpmsg_req->ready = 1;
+                       apu->available_response++;
+                       tmp_hdr->result = hdr->result;
+                       tmp_hdr->size_in = hdr->size_in;
+                       tmp_hdr->size_out = hdr->size_out;
+                       memcpy(tmp_hdr->data, hdr->data,
+                              hdr->size_in+hdr->size_out);
+
+                       wake_up_interruptible(&apu->waitqueue);
+                       break;
                }
        }
+       spin_unlock_irqrestore(&apu->ctx_lock, flags);
 
        return 0;
 }
@@ -177,48 +192,6 @@ static void apu_device_memory_unmap(struct rpmsg_apu *apu,
        dma_buf_put(buffer->dma_buf);
 }
 
-static int _apu_send_request(struct rpmsg_apu *apu,
-                            struct rpmsg_device *rpdev,
-                            struct apu_dev_request *req, int len)
-{
-
-       struct rpmsg_request *rpmsg_req;
-       int ret = 0;
-
-       req->id = ida_simple_get(&req_ida, 0, 0xffff, GFP_KERNEL);
-       if (req->id < 0)
-               return ret;
-
-       rpmsg_req = kzalloc(sizeof(*rpmsg_req), GFP_KERNEL);
-       if (!rpmsg_req)
-               return -ENOMEM;
-
-       rpmsg_req->req = req;
-       init_completion(&rpmsg_req->completion);
-       list_add(&rpmsg_req->node, &requests);
-
-       ret = rpmsg_send(rpdev->ept, req, len);
-       if (ret)
-               goto free_req;
-
-       /* be careful with race here between timeout and callback*/
-       ret = wait_for_completion_timeout(&rpmsg_req->completion,
-                                         msecs_to_jiffies(1000));
-       if (!ret)
-               ret = -ETIMEDOUT;
-       else
-               ret = 0;
-
-       ida_simple_remove(&req_ida, req->id);
-
-free_req:
-
-       list_del(&rpmsg_req->node);
-       kfree(rpmsg_req);
-
-       return ret;
-}
-
 static int apu_send_request(struct rpmsg_apu *apu,
                            struct apu_request *req)
 {
@@ -226,6 +199,8 @@ static int apu_send_request(struct rpmsg_apu *apu,
        struct rpmsg_device *rpdev = apu->rpdev;
        struct apu_dev_request *dev_req;
        struct apu_buffer *buffer;
+       struct rpmsg_request *rpmsg_req;
+       unsigned long flags;
 
        int size = req->size_in + req->size_out +
                sizeof(u32) * req->count * 2 + sizeof(*dev_req);
@@ -257,24 +232,63 @@ static int apu_send_request(struct rpmsg_apu *apu,
                dev_req_buffer_size[i] = buffer_size[i];
        }
 
-       ret = _apu_send_request(apu, rpdev, dev_req, size);
+       ret = ida_simple_get(&req_ida, 0, 0xffff, GFP_KERNEL);
+       if (ret < 0)
+               goto err_free_memory;
+
+       dev_req->id = ret;
+
+       rpmsg_req = kzalloc(sizeof(*rpmsg_req), GFP_KERNEL);
+       if (!rpmsg_req) {
+               ret =  -ENOMEM;
+               goto err_ida_remove;
+       }
 
+       req->id = dev_req->id;
+       rpmsg_req->req = req;
+       rpmsg_req->buffer = buffer;
+       spin_lock_irqsave(&apu->ctx_lock, flags);
+       list_add(&rpmsg_req->node, &apu->requests);
+       spin_unlock_irqrestore(&apu->ctx_lock, flags);
+
+       ret = rpmsg_send(rpdev->ept, dev_req, size);
+       if (ret < 0)
+               goto err;
+
+       kfree(dev_req);
+
+       return req->id;
+
+err:
+       list_del(&rpmsg_req->node);
+       kfree(rpmsg_req);
+       kfree(req);
+err_ida_remove:
+       ida_simple_remove(&req_ida, dev_req->id);
 err_free_memory:
        for (i--; i >= 0; i--)
                apu_device_memory_unmap(apu, &buffer[i]);
 
-       req->result = dev_req->result;
-       req->size_in = dev_req->size_in;
-       req->size_out = dev_req->size_out;
-       memcpy(req->data, dev_req->data, dev_req->size_in + dev_req->size_out +
-              sizeof(u32) * req->count);
-
        kfree(buffer);
        kfree(dev_req);
 
        return ret;
 }
 
+unsigned int rpmsg_eptdev_poll(struct file *fp, struct poll_table_struct *wait)
+{
+       struct rpmsg_apu *apu = fp->private_data;
+       unsigned long flags;
+
+       poll_wait(fp, &apu->waitqueue, wait);
+       spin_lock_irqsave(&apu->ctx_lock, flags);
+       if (apu->available_response) {
+               spin_unlock_irqrestore(&apu->ctx_lock, flags);
+               return POLLIN;
+       }
+       spin_unlock_irqrestore(&apu->ctx_lock, flags);
+       return 0;
+}
 
 static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd,
                               unsigned long arg)
@@ -285,6 +299,11 @@ static long rpmsg_eptdev_ioctl(struct file *fp, unsigned 
int cmd,
        void __user *argp = (void __user *)arg;
        int len;
        int ret;
+       unsigned long flags;
+       struct rpmsg_request *rpmsg_req;
+       int i;
+
+       ret = 0;
 
        switch (cmd) {
        case APU_SEND_REQ_IOCTL:
@@ -306,24 +325,69 @@ static long rpmsg_eptdev_ioctl(struct file *fp, unsigned 
int cmd,
                }
 
                ret = apu_send_request(apu, apu_req_full);
-               if (ret) {
-                       kfree(apu_req_full);
-                       return ret;
+
+               break;
+       case APU_GET_NEXT_AVAILABLE_IOCTL:
+               ret = -ENOMSG;
+               spin_lock_irqsave(&apu->ctx_lock, flags);
+               list_for_each_entry(rpmsg_req, &apu->requests, node) {
+                       if (rpmsg_req->ready == 1) {
+                               struct apu_request *req =
+                                       rpmsg_req->req;
+
+                               ret = 0;
+                               if (copy_to_user(argp, &req->id, sizeof(__u16)))
+                                       ret = -EFAULT;
+                               break;
+                       }
                }
+               spin_unlock_irqrestore(&apu->ctx_lock, flags);
+               break;
+       case APU_GET_RESP:
+               /* Get the header */
+               if (!argp)
+                       return -EINVAL;
 
-               if (copy_to_user(argp, apu_req_full, sizeof(apu_req) +
-                                sizeof(u32) * apu_req_full->count +
-                                apu_req_full->size_in + 
apu_req_full->size_out))
-                       ret = -EFAULT;
+               if (copy_from_user(&apu_req, argp,
+                                  sizeof(apu_req)))
+                       return -EFAULT;
 
-               kfree(apu_req_full);
-               return ret;
+               spin_lock_irqsave(&apu->ctx_lock, flags);
+               list_for_each_entry(rpmsg_req, &apu->requests, node) {
+                       struct apu_request *req = rpmsg_req->req;
+
+                       if ((apu_req.id == req->id) &&
+                           (rpmsg_req->ready == 1)) {
+                               int req_len = sizeof(struct apu_request) +
+                                       req->size_in + req->size_out +
+                                       req->count * sizeof(u32)*2;
+                               int apu_req_len = sizeof(struct apu_request) +
+                                       req->size_in + req->size_out +
+                                       req->count * sizeof(u32)*2;
+
+                               len = min(req_len, apu_req_len);
+                               if (copy_to_user(argp, req, len))
+                                       ret = -EFAULT;
+                               apu->available_response--;
+                               ida_simple_remove(&req_ida, req->id);
+                               for (i = 0; i < req->count ; i++)
+                                       apu_device_memory_unmap(apu,
+                                                       &rpmsg_req->buffer[i]);
+                               list_del(&rpmsg_req->node);
+                               kfree(rpmsg_req->buffer);
+                               kfree(rpmsg_req->req);
+                               kfree(rpmsg_req);
+                               break;
+                       }
+               }
+               spin_unlock_irqrestore(&apu->ctx_lock, flags);
 
+               break;
        default:
-               return -EINVAL;
+               ret = -EINVAL;
        }
 
-       return 0;
+       return ret;
 }
 
 static int rpmsg_eptdev_open(struct inode *inode, struct file *filp)
@@ -351,6 +415,7 @@ static const struct file_operations rpmsg_eptdev_fops = {
        .release = rpmsg_eptdev_release,
        .unlocked_ioctl = rpmsg_eptdev_ioctl,
        .compat_ioctl = rpmsg_eptdev_ioctl,
+       .poll = rpmsg_eptdev_poll,
 };
 
 static void iova_domain_release(struct kref *ref)
@@ -512,6 +577,11 @@ static int apu_rpmsg_probe(struct rpmsg_device *rpdev)
        dev->id = ret;
        dev_set_name(&apu->dev, "apu%d", ret);
 
+       init_waitqueue_head(&apu->waitqueue);
+       spin_lock_init(&apu->ctx_lock);
+       apu->available_response = 0;
+       INIT_LIST_HEAD(&apu->requests);
+
        ret = cdev_add(&apu->cdev, dev->devt, 1);
        if (ret)
                goto free_ctrl_ida;
diff --git a/include/uapi/linux/apu_rpmsg.h b/include/uapi/linux/apu_rpmsg.h
index 81c9e4af9a94..f61207520254 100644
--- a/include/uapi/linux/apu_rpmsg.h
+++ b/include/uapi/linux/apu_rpmsg.h
@@ -21,6 +21,7 @@
  *        by the APU.
  */
 struct apu_request {
+       __u16 id;
        __u16 cmd;
        __u16 result;
        __u16 size_in;
@@ -31,6 +32,9 @@ struct apu_request {
 };
 
 /* Send synchronous request to an APU */
-#define APU_SEND_REQ_IOCTL     _IOWR(0xb7, 0x2, struct apu_request)
+
+#define APU_SEND_REQ_IOCTL             _IOW(0xb7, 0x2, struct apu_request)
+#define APU_GET_NEXT_AVAILABLE_IOCTL   _IOR(0xb7, 0x3, __u16)
+#define APU_GET_RESP                   _IOWR(0xb7, 0x4, struct apu_request)
 
 #endif
-- 
2.26.2

Reply via email to