Add support into the rpmsg char driver to skip copying the data into an skb if the endpoint supports rpmsg_rx_done. If the endpoint supports the rx_done operation, allocate a zero sized skb and set the data to the buffer returned in the rx callback. When the packet is read from the character device, release the memory by calling rpmsg_rx_done(). Signed-off-by: Chris Lew <quic_clew@xxxxxxxxxxx> --- drivers/rpmsg/rpmsg_char.c | 50 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index b6183d4f62a2..be62ddcf356c 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -91,8 +91,8 @@ int rpmsg_chrdev_eptdev_destroy(struct device *dev, void *data) } EXPORT_SYMBOL(rpmsg_chrdev_eptdev_destroy); -static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, - void *priv, u32 addr) +static int rpmsg_ept_copy_cb(struct rpmsg_device *rpdev, void *buf, int len, + void *priv, u32 addr) { struct rpmsg_eptdev *eptdev = priv; struct sk_buff *skb; @@ -113,6 +113,43 @@ static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, return 0; } +static int rpmsg_ept_no_copy_cb(struct rpmsg_device *rpdev, void *buf, int len, + void *priv, u32 addr) +{ + struct rpmsg_eptdev *eptdev = priv; + struct sk_buff *skb; + + skb = alloc_skb(0, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + skb->head = buf; + skb->data = buf; + skb_reset_tail_pointer(skb); + skb_set_end_offset(skb, len); + skb_put(skb, len); + + spin_lock(&eptdev->queue_lock); + skb_queue_tail(&eptdev->queue, skb); + spin_unlock(&eptdev->queue_lock); + + /* wake up any blocking processes, waiting for new data */ + wake_up_interruptible(&eptdev->readq); + + return RPMSG_DEFER; +} + +static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, + void *priv, u32 addr) +{ + struct rpmsg_eptdev *eptdev = priv; + rpmsg_rx_cb_t cb; + + cb = (eptdev->ept->rx_done) ? rpmsg_ept_no_copy_cb : rpmsg_ept_copy_cb; + + return cb(rpdev, buf, len, priv, addr); +} + static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) { struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); @@ -210,6 +247,15 @@ static ssize_t rpmsg_eptdev_read_iter(struct kiocb *iocb, struct iov_iter *to) if (copy_to_iter(skb->data, use, to) != use) use = -EFAULT; + if (eptdev->ept->rx_done) { + rpmsg_rx_done(eptdev->ept, skb->data); + /* + * Data memory is freed by rpmsg_rx_done(), reset the skb data + * pointers so kfree_skb() does not try to free a second time. + */ + skb->head = NULL; + skb->data = NULL; + } kfree_skb(skb); return use; -- 2.7.4