* [PATCH] misc: fastrpc: Add cache maintenance for non-coherent platforms
@ 2026-09-16 18:58 Abhinav Parihar
2026-09-16 19:09 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Abhinav Parihar @ 2026-09-16 18:58 UTC (permalink / raw)
To: srinivas.kandagatla, linux-arm-msm
Cc: Abhinav Parihar, gregkh, quic_bkumar, ekansh.gupta, linux-kernel,
quic_chennak, dri-devel, arnd
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 <abhinav.parihar@oss.qualcomm.com>
---
Patch [v1]: https://lore.kernel.org/all/20260604194811.2437567-1-abhinav.parihar@oss.qualcomm.com/
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;
} else {
+ if ((max_raix < inbufs && ctx->olaps[i].raix + 1 > inbufs) ||
+ (ctx->olaps[i].raix < inbufs && max_raix + 1 > 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) {
+ 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) {
+ 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))
+ 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");
dev_set_drvdata(dev, sess);
sess->sid = sid;
--
2.34.1
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH] misc: fastrpc: Add cache maintenance for non-coherent platforms
2026-09-16 18:58 [PATCH] misc: fastrpc: Add cache maintenance for non-coherent platforms Abhinav Parihar
@ 2026-09-16 19:09 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-09-16 19:09 UTC (permalink / raw)
To: Abhinav Parihar; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] misc: fastrpc: missing error handling for dma_buf_begin_cpu_access()
- [High] misc: fastrpc: cache coherency bypass due to user pointer overlap detection
- [High] misc: fastrpc: missing max_raix update causes incorrect do_cmo evaluation
- [Low] misc: fastrpc: dead code in RPC control header overlap check
--
commit 131a71612f32d48a67415e31fef5d7317be532ce
Author: Abhinav Parihar <abhinav.parihar@oss.qualcomm.com>
Subject: misc: fastrpc: Add cache maintenance for non-coherent platforms
This commit adds cache maintenance for fastrpc argument buffers on
non-coherent platforms. It introduces fastrpc_flush_args() and
fastrpc_inv_args() to flush input buffers and invalidate output buffers based
on the "dma-coherent" property.
> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
> index d4fac2caca867..83d04b9d2ea02 100644
> --- a/drivers/misc/fastrpc.c
> +++ b/drivers/misc/fastrpc.c
[ ... ]
> @@ -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;
[Severity: High]
Does this code miss updating max_raix when max_end is extended?
If a partially overlapping buffer extends max_end, max_raix remains stale.
Subsequent fully subsumed buffers might then evaluate their boundary crossing
(do_cmo) against this stale max_raix, potentially skipping necessary cache
maintenance.
> } else {
> + if ((max_raix < inbufs && ctx->olaps[i].raix + 1 > inbufs) ||
> + (ctx->olaps[i].raix < inbufs && max_raix + 1 > inbufs))
> + ctx->olaps[i].do_cmo = true;
> ctx->olaps[i].mend = 0;
> ctx->olaps[i].mstart = 0;
> }
[ ... ]
> @@ -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) {
> + 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)) {
[Severity: High]
Can cache maintenance be incorrectly skipped here due to relying on mstart?
The fastrpc_get_buff_overlaps() function calculates overlaps and sets mstart
using the user-provided virtual address (ctx->args[i].ptr), rather than the
underlying dma-buf identity.
If a user passes the same ptr value for two distinct dma-buf fds, the overlap
logic merges them and sets mstart = 0 for the second buffer. Additionally, if
ptr == 0, mstart also evaluates to 0. This could allow untrusted userspace to
bypass cache maintenance, leading to silent data corruption or coherency
issues.
> + dma_buf_begin_cpu_access(map->buf, DMA_TO_DEVICE);
[Severity: High]
Is it safe to ignore the return value of dma_buf_begin_cpu_access() here?
This function can fail (e.g., returning -ERESTARTSYS if interrupted by a
signal while waiting on fences).
Ignoring the failure causes the driver to proceed with unstabilized buffers
(which could lead to data corruption) and unconditionally calls
dma_buf_end_cpu_access() on the error path, violating API pairing and
potentially causing exporter crashes or state corruption.
> + 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) {
> + 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))
> + continue;
[Severity: Low]
Does this code perform a meaningless comparison?
The overlap check compares a kernel virtual address (rpra) with a user-space
virtual address (rpra[raix].buf.pv).
Because of the `if (!map || !map->buf) continue;` check above it, this code
is only reached for explicit user dma-buf mappings, where pv is explicitly set
to the user-space ptr. These addresses will virtually never match, making this
dead code.
> +
> + if (ctx->olaps[i].mstart || ctx->olaps[i].do_cmo) {
> + dma_buf_begin_cpu_access(map->buf, DMA_FROM_DEVICE);
[Severity: High]
Should this check the return value of dma_buf_begin_cpu_access() and handle
errors appropriately before proceeding and calling dma_buf_end_cpu_access()?
> + dma_buf_end_cpu_access(map->buf, DMA_FROM_DEVICE);
> + }
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260916185850.3686010-1-abhinav.parihar@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-16 19:09 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-16 18:58 [PATCH] misc: fastrpc: Add cache maintenance for non-coherent platforms Abhinav Parihar
2026-09-16 19:09 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox