[PATCH v1 3/3] misc: fastrpc: Skip reference for DMA handles
Dmitry Baryshkov
dmitry.baryshkov at linaro.org
Thu Sep 19 08:46:16 UTC 2024
On Thu, Sep 19, 2024 at 12:00:33PM GMT, Ekansh Gupta wrote:
>
>
> On 8/30/2024 3:03 PM, Dmitry Baryshkov wrote:
> > On Thu, Aug 22, 2024 at 04:29:33PM GMT, Ekansh Gupta wrote:
> >> If multiple dma handles are passed with same fd over a remote call
> >> the kernel driver takes a reference and expects that put for the
> >> map will be called as many times to free the map.
> >> But DSP only
> >> updates the fd one time in the fd list when the DSP refcount
> >> goes to zero
> > I'm sorry, I couldn't understand this phrase. Could you plese clarify
> > what do you mean here?
> DMA handle are buffers passed to DSP which are only unmapped when DSP updated
> the buffer fd in fdlist.
> fdlist implementation: misc: fastrpc: Add fdlist implementation - kernel/git/next/linux-next.git - The linux-next integration testing tree <https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/drivers/misc/fastrpc.c?id=8f6c1d8c4f0cc316b0456788fff8373554d1d99d>
>
> A remote call payload carries both input/output buffers and dma handles. The lifetime
> of input/output buffer is a remote call which means that any buffer allocated or mapped
> for a remote call will be freed or unmapped when the remote call is completing. Whereas,
> dma handles can get freed over some other remote call whenever the DSP will update
> fdlist. So if a remote call passed multiple dma handles with same fd to DSP, on driver, ref
> count will be incremented, but DSP can update fdlist only 1 time for the same fd as DSP also
> has a ref counting happening for the dma handle and fdlist is updated when the DSP ref
> count goes to 0. In this case, the map will not get freed even though it is no longer in use.
OK, I started looking at the related code. Pleas fix possible map leak
in fastrpc_put_args(), happening if the copy_to_user() fails.
Second. Please merge fastrpc_map_lookup() + fastrpc_map_put() invocation
into a single call, effectively dropping take_ref argument from
fastrpc_map_lookup() (which can now become fastrpc_map_get()).
Now back to your patch.
Please clarify if my understanding is correct:
The driver maps dma bufs and passes them to DSP. Then once DSP firmware
finds out that a particular buffer is no longer needed, it returns
its fd via the fdlist part of the invoke_buf. As these buffers are
returned only once, when they are no longer necessary, the kernel should
not take additional references on the long-living dma-bufs.
If that's the case, see my comments below.
> >
> >> and hence kernel make put call only once for the
> >> fd. This can cause SMMU fault issue as the same fd can be used
> >> in future for some other call.
> >>
> >> Fixes: 35a82b87135d ("misc: fastrpc: Add dma handle implementation")
> >> Cc: stable <stable at kernel.org>
> >> Signed-off-by: Ekansh Gupta <quic_ekangupt at quicinc.com>
> >> ---
> >> drivers/misc/fastrpc.c | 13 ++++++++-----
> >> 1 file changed, 8 insertions(+), 5 deletions(-)
> >>
> >> diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
> >> index ebe828770a8d..ad56e918e1f8 100644
> >> --- a/drivers/misc/fastrpc.c
> >> +++ b/drivers/misc/fastrpc.c
> >> @@ -755,7 +755,7 @@ static const struct dma_buf_ops fastrpc_dma_buf_ops = {
> >>
> >> static int fastrpc_map_create(struct fastrpc_user *fl, int fd,
> >> u64 va, u64 len, u32 attr,
> >> - struct fastrpc_map **ppmap)
> >> + struct fastrpc_map **ppmap, bool take_ref)
> >> {
> >> struct fastrpc_session_ctx *sess = fl->sctx;
> >> struct fastrpc_map *map = NULL;
> >> @@ -763,7 +763,7 @@ static int fastrpc_map_create(struct fastrpc_user *fl, int fd,
> >> struct scatterlist *sgl = NULL;
> >> int err = 0, sgl_index = 0;
> >>
> >> - if (!fastrpc_map_lookup(fl, fd, va, len, ppmap, true))
> >> + if (!fastrpc_map_lookup(fl, fd, va, len, ppmap, take_ref))
> >> return 0;
Do not add the take_ref argument to fastrpc_map_create(). Instead
extract the rest of the code to the function fastrpc_map_attach() (or
something like that).
> >>
> >> map = kzalloc(sizeof(*map), GFP_KERNEL);
> >> @@ -917,14 +917,17 @@ static int fastrpc_create_maps(struct fastrpc_invoke_ctx *ctx)
> >> int i, err;
> >>
> >> for (i = 0; i < ctx->nscalars; ++i) {
> >> + bool take_ref = true;
> >>
> >> if (ctx->args[i].fd == 0 || ctx->args[i].fd == -1 ||
> >> ctx->args[i].length == 0)
> >> continue;
> >>
> >> + if (i >= ctx->nbufs)
> >> + take_ref = false;
> > Please clarify too.
> nbufs -> total input/output buffers
> nscalars -> nbufs + dma handles
> So here, avoiding ref increment for dma handles.
> >
> >> err = fastrpc_map_create(ctx->fl, ctx->args[i].fd,
> >> (u64)ctx->args[i].ptr, ctx->args[i].length,
> >> - ctx->args[i].attr, &ctx->maps[i]);
> >> + ctx->args[i].attr, &ctx->maps[i], take_ref);
Call conditionally either fastrpc_map_create() or fastrpc_map_attach().
> >> if (err) {
> >> dev_err(dev, "Error Creating map %d\n", err);
> >> return -EINVAL;
> >> @@ -1417,7 +1420,7 @@ static int fastrpc_init_create_process(struct fastrpc_user *fl,
> >>
> >> if (init.filelen && init.filefd) {
> >> err = fastrpc_map_create(fl, init.filefd, init.file,
> >> - init.filelen, 0, &map);
> >> + init.filelen, 0, &map, true);
> >> if (err)
> >> goto err;
> >> }
> >> @@ -2040,7 +2043,7 @@ static int fastrpc_req_mem_map(struct fastrpc_user *fl, char __user *argp)
> >>
> >> /* create SMMU mapping */
> >> err = fastrpc_map_create(fl, req.fd, req.vaddrin, req.length,
> >> - 0, &map);
> >> + 0, &map, true);
> >> if (err) {
> >> dev_err(dev, "failed to map buffer, fd = %d\n", req.fd);
> >> return err;
> >> --
> >> 2.34.1
> >>
>
--
With best wishes
Dmitry
More information about the dri-devel
mailing list