On 17-09-2026 00:28, Abhinav Parihar wrote: > Some platforms using fastrpc do not support DMA coherency on > HLOS. On such systems, explicit cache maintenance is > required to ensure data consistency for RPC argument buffers. > > Add cache maintenance for argument buffers when operating on > non-coherent platforms: > - Flush input buffers before invoking RPC to ensure CPU writes are > visible to the DSP > - Invalidate output buffers after RPC completion to ensure DSP > writes are visible to the CPU > > Introduce helper functions fastrpc_flush_args() and > fastrpc_inv_args() to perform the required dma-buf cache > operations. These are invoked only when the device is not marked > as DMA coherent. > > The coherency capability is determined using the "dma-coherent" > device tree property and stored per session context. > > This ensures correct data synchronization on platforms lacking > DMA coherency, while avoiding unnecessary overhead on coherent > systems. > > Signed-off-by: Abhinav Parihar <[email protected]> > --- > > Patch [v1]: > https://lore.kernel.org/all/[email protected]/ > > Changes in v2: > - Fix buffer overlap handling to ensure cache maintenance is performed > when input and output buffers overlap and the overlap calculation > suppresses the buffer's mstart/mend range. > - Add a do_cmo flag to track buffers that require cache maintenance > even when their overlap range is clipped. > - Invalidate output buffers before sending the RPC request, preventing > stale CPU cache lines from being written back while the DSP updates > the buffers. > - Use DMA_FROM_DEVICE consistently for both begin_cpu_access() and > end_cpu_access() when invalidating output buffers. > > drivers/misc/fastrpc.c | 77 +++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 76 insertions(+), 1 deletion(-) > > diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c > index d4fac2caca86..83d04b9d2ea0 100644 > --- a/drivers/misc/fastrpc.c > +++ b/drivers/misc/fastrpc.c > @@ -217,6 +217,7 @@ struct fastrpc_buf_overlap { > u64 mstart; > u64 mend; > u64 offset; > + bool do_cmo; > }; > > struct fastrpc_buf { > @@ -292,6 +293,7 @@ struct fastrpc_session_ctx { > int sid; > bool used; > bool valid; > + bool coherent; > }; > > struct fastrpc_soc_data { > @@ -655,7 +657,9 @@ static int olaps_cmp(const void *a, const void *b) > static void fastrpc_get_buff_overlaps(struct fastrpc_invoke_ctx *ctx) > { > u64 max_end = 0; > + int max_raix = -1; > int i; > + int inbufs = REMOTE_SCALARS_INBUFS(ctx->sc); > > for (i = 0; i < ctx->nbufs; ++i) { > ctx->olaps[i].start = ctx->args[i].ptr; > @@ -675,6 +679,9 @@ static void fastrpc_get_buff_overlaps(struct > fastrpc_invoke_ctx *ctx) > if (ctx->olaps[i].end > max_end) { > max_end = ctx->olaps[i].end; should you update max_raix here also?> } else { > + if ((max_raix < inbufs && ctx->olaps[i].raix + > 1 > inbufs) || > + (ctx->olaps[i].raix < inbufs && > max_raix + 1 > inbufs)) if ((max_raix < inbufs) != (ctx->olaps[i].raix < inbufs)) > + ctx->olaps[i].do_cmo = true; > ctx->olaps[i].mend = 0; > ctx->olaps[i].mstart = 0; > } > @@ -684,6 +691,7 @@ static void fastrpc_get_buff_overlaps(struct > fastrpc_invoke_ctx *ctx) > ctx->olaps[i].mstart = ctx->olaps[i].start; > ctx->olaps[i].offset = 0; > max_end = ctx->olaps[i].end; > + max_raix = ctx->olaps[i].raix; > } > } > } > @@ -703,6 +711,7 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc( > > INIT_LIST_HEAD(&ctx->node); > ctx->fl = user; > + ctx->sc = sc; > ctx->nscalars = REMOTE_SCALARS_LENGTH(sc); > ctx->nbufs = REMOTE_SCALARS_INBUFS(sc) + > REMOTE_SCALARS_OUTBUFS(sc); > @@ -728,7 +737,6 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc( > /* Take a reference to user, released in fastrpc_context_free() */ > fastrpc_user_get(user); > > - ctx->sc = sc; > ctx->retval = -1; > ctx->pid = current->pid; > ctx->client_id = user->client_id; > @@ -1067,6 +1075,64 @@ static int fastrpc_create_maps(struct > fastrpc_invoke_ctx *ctx) > return 0; > } > > +static void fastrpc_flush_args(struct fastrpc_invoke_ctx *ctx) > +{ > + union fastrpc_remote_arg *rpra = ctx->rpra; > + int i, inbufs, outbufs; > + > + inbufs = REMOTE_SCALARS_INBUFS(ctx->sc); > + outbufs = REMOTE_SCALARS_OUTBUFS(ctx->sc); > + > + for (i = 0; i < inbufs + outbufs; ++i) { ctx->nbufs instead of inbufs + outbufs > + int raix = ctx->olaps[i].raix; > + struct fastrpc_map *map = ctx->maps[raix]; > + > + if (raix + 1 > inbufs) > + continue; > + if (!map || !map->buf) > + continue; > + > + if (rpra[raix].buf.len && (ctx->olaps[i].mstart || > ctx->olaps[i].do_cmo)) { > + dma_buf_begin_cpu_access(map->buf, DMA_TO_DEVICE); > + dma_buf_end_cpu_access(map->buf, DMA_TO_DEVICE); > + } > + } > +} > + > +static void fastrpc_inv_args(struct fastrpc_invoke_ctx *ctx) > +{ > + union fastrpc_remote_arg *rpra = ctx->rpra; > + int i, inbufs, outbufs; > + > + inbufs = REMOTE_SCALARS_INBUFS(ctx->sc); > + outbufs = REMOTE_SCALARS_OUTBUFS(ctx->sc); > + > + for (i = 0; i < inbufs + outbufs; ++i) { ctx->nbufs> + int raix = ctx->olaps[i].raix; > + struct fastrpc_map *map = ctx->maps[raix]; > + > + if (raix + 1 <= inbufs) > + continue; > + if (!rpra[raix].buf.len) > + continue; > + if (!map || !map->buf) > + continue; > + > + /* > + * Skip invalidation if the argument overlaps with the > + * RPC control header page. > + */ > + if (((uintptr_t)rpra & PAGE_MASK) == > + ((uintptr_t)rpra[raix].buf.pv & PAGE_MASK)) is this possible? for fd buffers, buf.pv is going to be a userspace VA and rpta addr is going to be a kernel VA, those cannot be equal.> + continue; > + > + if (ctx->olaps[i].mstart || ctx->olaps[i].do_cmo) { > + dma_buf_begin_cpu_access(map->buf, DMA_FROM_DEVICE); > + dma_buf_end_cpu_access(map->buf, DMA_FROM_DEVICE); > + } > + } > +} > + > static struct fastrpc_invoke_buf *fastrpc_invoke_buf_start(union > fastrpc_remote_arg *pra, int len) > { > return (struct fastrpc_invoke_buf *)(&pra[len]); > @@ -1191,6 +1257,9 @@ static int fastrpc_get_args(u32 kernel, struct > fastrpc_invoke_ctx *ctx) > } > } > > + if (!ctx->fl->sctx->coherent) > + fastrpc_flush_args(ctx); > + > for (i = ctx->nbufs; i < ctx->nscalars; ++i) { > list[i].num = ctx->args[i].length ? 1 : 0; > list[i].pgidx = i; > @@ -1376,6 +1445,8 @@ static int fastrpc_internal_invoke(struct fastrpc_user > *fl, u32 kernel, > if (err) > goto bail; > > + if (!fl->sctx->coherent) > + fastrpc_inv_args(ctx); > /* make sure that all CPU memory writes are seen by DSP */ > dma_wmb(); > /* Send invoke buffer to remote dsp */ > @@ -1396,6 +1467,9 @@ static int fastrpc_internal_invoke(struct fastrpc_user > *fl, u32 kernel, > > /* make sure that all memory writes by DSP are seen by CPU */ > dma_rmb(); > + if (!fl->sctx->coherent) > + fastrpc_inv_args(ctx); > + > /* populate all the output buffers with results */ > err = fastrpc_put_args(ctx, kernel); > if (err) > @@ -2393,6 +2467,7 @@ static int fastrpc_cb_probe(struct platform_device > *pdev) > sess->used = false; > sess->valid = true; > sess->dev = dev; > + sess->coherent = of_property_read_bool(dev->of_node, "dma-coherent"); see if you can use of_dma_is_coherent() instead.> dev_set_drvdata(dev, sess); > sess->sid = sid; >
