msm: adsprpc: block smmu unmap of buffer used in pending rpc call

A dynamic SMMU mapping created as part of an RPC call can potentially
be removed by a parallel munmap ioctl call before the RPC call is
complete, leading to SMMU faults.

Maintain a ref-count that indicates that the mapping is being used by
a pending RPC call and allow the mapping to be removed only if this
count is 0.

Change-Id: Ieb4ff6b298ff9c48953bc5b3539fdfe19a14b442
Acked-by: Santosh Sakore <ssakore@qti.qualcomm.com>
Signed-off-by: Santosh Sakore <quic_ssakore@quicinc.com>
This commit is contained in:
Santosh Sakore
2023-04-23 11:20:31 +05:30
committed by Gerrit - the friendly Code Review server
parent 64813776c7
commit e696574fb5
2 changed files with 48 additions and 5 deletions

View File

@@ -957,6 +957,8 @@ static int fastrpc_mmap_remove(struct fastrpc_file *fl, int fd, uintptr_t va,
if ((fd < 0 || map->fd == fd) && map->raddr == va && if ((fd < 0 || map->fd == fd) && map->raddr == va &&
map->raddr + map->len == va + len && map->raddr + map->len == va + len &&
map->refs == 1 && map->refs == 1 &&
/* Remove map only if it isn't being used in any pending RPC calls */
!map->ctx_refs &&
/* Skip unmap if it is fastrpc shell memory */ /* Skip unmap if it is fastrpc shell memory */
!map->is_filemap) { !map->is_filemap) {
match = map; match = map;
@@ -997,7 +999,7 @@ static void fastrpc_mmap_free(struct fastrpc_mmap *map, uint32_t flags)
map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) { map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
spin_lock_irqsave(&me->hlock, irq_flags); spin_lock_irqsave(&me->hlock, irq_flags);
map->refs--; map->refs--;
if (!map->refs && !map->is_persistent) if (!map->refs && !map->is_persistent && !map->ctx_refs)
hlist_del_init(&map->hn); hlist_del_init(&map->hn);
spin_unlock_irqrestore(&me->hlock, irq_flags); spin_unlock_irqrestore(&me->hlock, irq_flags);
if (map->refs > 0) { if (map->refs > 0) {
@@ -1012,7 +1014,7 @@ static void fastrpc_mmap_free(struct fastrpc_mmap *map, uint32_t flags)
spin_unlock_irqrestore(&me->hlock, irq_flags); spin_unlock_irqrestore(&me->hlock, irq_flags);
} else { } else {
map->refs--; map->refs--;
if (!map->refs) if (!map->refs && !map->ctx_refs)
hlist_del_init(&map->hn); hlist_del_init(&map->hn);
if (map->refs > 0 && !flags) if (map->refs > 0 && !flags)
return; return;
@@ -1953,8 +1955,15 @@ static void context_free(struct smq_invoke_ctx *ctx)
spin_unlock(&ctx->fl->hlock); spin_unlock(&ctx->fl->hlock);
mutex_lock(&ctx->fl->map_mutex); mutex_lock(&ctx->fl->map_mutex);
for (i = 0; i < nbufs; ++i) for (i = 0; i < nbufs; ++i) {
/*
* Decrement ctx refs count before mmap free,
* indicate remote call no longer using it
*/
if (ctx->maps[i] && ctx->maps[i]->ctx_refs)
ctx->maps[i]->ctx_refs--;
fastrpc_mmap_free(ctx->maps[i], 0); fastrpc_mmap_free(ctx->maps[i], 0);
}
mutex_unlock(&ctx->fl->map_mutex); mutex_unlock(&ctx->fl->map_mutex);
fastrpc_buf_free(ctx->buf, 1); fastrpc_buf_free(ctx->buf, 1);
@@ -2342,6 +2351,12 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
err = fastrpc_mmap_create(ctx->fl, ctx->fds[i], NULL, err = fastrpc_mmap_create(ctx->fl, ctx->fds[i], NULL,
ctx->attrs[i], buf, len, ctx->attrs[i], buf, len,
mflags, &ctx->maps[i]); mflags, &ctx->maps[i]);
/*
* Increment ctx refs count for in/out buffer if map created,
* indicate map under use in remote call
*/
if (ctx->maps[i])
ctx->maps[i]->ctx_refs++;
mutex_unlock(&ctx->fl->map_mutex); mutex_unlock(&ctx->fl->map_mutex);
if (err) if (err)
goto bail; goto bail;
@@ -2369,10 +2384,23 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
FASTRPC_ATTR_NOVA, 0, 0, dmaflags, FASTRPC_ATTR_NOVA, 0, 0, dmaflags,
&ctx->maps[i]); &ctx->maps[i]);
if (err) { if (err) {
for (j = bufs; j < i; j++) for (j = bufs; j < i; j++) {
/*
* Due to error decrement ctx refs count before mmap free
* for each in/out handle, if map created
*/
if (ctx->maps[j] && ctx->maps[j]->ctx_refs)
ctx->maps[j]->ctx_refs--;
fastrpc_mmap_free(ctx->maps[j], 0); fastrpc_mmap_free(ctx->maps[j], 0);
}
mutex_unlock(&ctx->fl->map_mutex); mutex_unlock(&ctx->fl->map_mutex);
goto bail; goto bail;
} else {
/*
* Increment ctx refs count for in/out handle if map created
* and no error, indicate map under use in remote call
*/
ctx->maps[i]->ctx_refs++;
} }
ipage += 1; ipage += 1;
} }
@@ -2704,6 +2732,12 @@ static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx,
} }
} else { } else {
mutex_lock(&ctx->fl->map_mutex); mutex_lock(&ctx->fl->map_mutex);
/*
* Decrement ctx refs count before mmap free,
* indicate remote call no longer using it
*/
if (ctx->maps[i]->ctx_refs)
ctx->maps[i]->ctx_refs--;
fastrpc_mmap_free(ctx->maps[i], 0); fastrpc_mmap_free(ctx->maps[i], 0);
mutex_unlock(&ctx->fl->map_mutex); mutex_unlock(&ctx->fl->map_mutex);
ctx->maps[i] = NULL; ctx->maps[i] = NULL;
@@ -2714,8 +2748,15 @@ static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx,
if (!fdlist[i]) if (!fdlist[i])
break; break;
if (!fastrpc_mmap_find(ctx->fl, (int)fdlist[i], NULL, 0, 0, if (!fastrpc_mmap_find(ctx->fl, (int)fdlist[i], NULL, 0, 0,
0, 0, &mmap)) 0, 0, &mmap)) {
/*
* Decrement ctx refs count before mmap free,
* indicate remote call no longer using it
*/
if (mmap && mmap->ctx_refs)
mmap->ctx_refs--;
fastrpc_mmap_free(mmap, 0); fastrpc_mmap_free(mmap, 0);
}
} }
mutex_unlock(&ctx->fl->map_mutex); mutex_unlock(&ctx->fl->map_mutex);
if (ctx->crc && crclist && rpra) if (ctx->crc && crclist && rpra)

View File

@@ -1031,6 +1031,8 @@ struct fastrpc_mmap {
/* Mapping for fastrpc shell */ /* Mapping for fastrpc shell */
bool is_filemap; bool is_filemap;
char *servloc_name; /* Indicate which daemon mapped this */ char *servloc_name; /* Indicate which daemon mapped this */
/* Indicates map is being used by a pending RPC call */
unsigned int ctx_refs;
}; };
enum fastrpc_perfkeys { enum fastrpc_perfkeys {