fastrpc_rpmsg_callback() drops the invoke reference taken before sending
the request. The callback can currently consume a reference for a guessed
context ID before the request has been sent, and duplicate replies can
schedule multiple puts for the same send reference.

Track whether an invoke has been sent and whether a reply has already
been accepted while holding the channel IDR lock. Reject unsent and
duplicate replies so only the first valid response consumes the send
reference.

Signed-off-by: Yousef Alhouseen <[email protected]>
---
 drivers/misc/fastrpc.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index c91d5da42..4eaecee1b 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -250,6 +250,8 @@ struct fastrpc_invoke_ctx {
        uintptr_t *buf_pv;
        struct fastrpc_buf_overlap *olaps;
        struct fastrpc_channel_ctx *cctx;
+       bool sent;
+       bool completed;
 };
 
 struct fastrpc_session_ctx {
@@ -1239,6 +1241,8 @@ static int fastrpc_invoke_send(struct fastrpc_session_ctx 
*sctx,
        struct fastrpc_channel_ctx *cctx;
        struct fastrpc_user *fl = ctx->fl;
        struct fastrpc_msg *msg = &ctx->msg;
+       unsigned long flags;
+       bool put_ref = true;
        int ret;
 
        cctx = fl->cctx;
@@ -1255,10 +1259,23 @@ static int fastrpc_invoke_send(struct 
fastrpc_session_ctx *sctx,
        msg->size = roundup(ctx->msg_sz, PAGE_SIZE);
        fastrpc_context_get(ctx);
 
+       spin_lock_irqsave(&cctx->lock, flags);
+       ctx->sent = true;
+       spin_unlock_irqrestore(&cctx->lock, flags);
+
        ret = rpmsg_send(cctx->rpdev->ept, (void *)msg, sizeof(*msg));
 
-       if (ret)
-               fastrpc_context_put(ctx);
+       if (ret) {
+               spin_lock_irqsave(&cctx->lock, flags);
+               if (ctx->completed)
+                       put_ref = false;
+               else
+                       ctx->sent = false;
+               spin_unlock_irqrestore(&cctx->lock, flags);
+
+               if (put_ref)
+                       fastrpc_context_put(ctx);
+       }
 
        return ret;
 
@@ -2586,6 +2603,10 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device 
*rpdev, void *data,
 
        spin_lock_irqsave(&cctx->lock, flags);
        ctx = idr_find(&cctx->ctx_idr, ctxid);
+       if (ctx && (!ctx->sent || ctx->completed))
+               ctx = NULL;
+       if (ctx)
+               ctx->completed = true;
        spin_unlock_irqrestore(&cctx->lock, flags);
 
        if (!ctx) {
-- 
2.54.0

Reply via email to