From: Julien STEPHAN <jstephan@xxxxxxxxxxxx> In order to improve performances and flexibility, add support of async request. Signed-off-by: Julien STEPHAN <jstephan@xxxxxxxxxxxx> Signed-off-by: Alexandre Bailon <abailon@xxxxxxxxxxxx> --- 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