Prechádzať zdrojové kódy

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 <[email protected]>
Signed-off-by: Santosh Sakore <[email protected]>
Santosh Sakore 2 rokov pred
rodič
commit
e696574fb5
2 zmenil súbory, kde vykonal 48 pridanie a 5 odobranie
  1. 46 5
      dsp/adsprpc.c
  2. 2 0
      dsp/adsprpc_shared.h

+ 46 - 5
dsp/adsprpc.c

@@ -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 &&
 			map->raddr + map->len == va + len &&
 			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 */
 			!map->is_filemap) {
 			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) {
 		spin_lock_irqsave(&me->hlock, irq_flags);
 		map->refs--;
-		if (!map->refs && !map->is_persistent)
+		if (!map->refs && !map->is_persistent && !map->ctx_refs)
 			hlist_del_init(&map->hn);
 		spin_unlock_irqrestore(&me->hlock, irq_flags);
 		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);
 	} else {
 		map->refs--;
-		if (!map->refs)
+		if (!map->refs && !map->ctx_refs)
 			hlist_del_init(&map->hn);
 		if (map->refs > 0 && !flags)
 			return;
@@ -1953,8 +1955,15 @@ static void context_free(struct smq_invoke_ctx *ctx)
 	spin_unlock(&ctx->fl->hlock);
 
 	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);
+	}
 	mutex_unlock(&ctx->fl->map_mutex);
 
 	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,
 					ctx->attrs[i], buf, len,
 					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);
 		if (err)
 			goto bail;
@@ -2369,10 +2384,23 @@ static int get_args(uint32_t kernel, struct smq_invoke_ctx *ctx)
 					FASTRPC_ATTR_NOVA, 0, 0, dmaflags,
 					&ctx->maps[i]);
 		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);
+			}
 			mutex_unlock(&ctx->fl->map_mutex);
 			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;
 	}
@@ -2704,6 +2732,12 @@ static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx,
 			}
 		} else {
 			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);
 			mutex_unlock(&ctx->fl->map_mutex);
 			ctx->maps[i] = NULL;
@@ -2714,8 +2748,15 @@ static int put_args(uint32_t kernel, struct smq_invoke_ctx *ctx,
 		if (!fdlist[i])
 			break;
 		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);
+		}
 	}
 	mutex_unlock(&ctx->fl->map_mutex);
 	if (ctx->crc && crclist && rpra)

+ 2 - 0
dsp/adsprpc_shared.h

@@ -1031,6 +1031,8 @@ struct fastrpc_mmap {
 	/* Mapping for fastrpc shell */
 	bool is_filemap;
 	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 {