Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 28 Sep 2021 19:31:43 GMT
From:      Ian Lepore <ian@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: dc91a9715f8f - main - Fix busdma resource leak on usb device detach.
Message-ID:  <202109281931.18SJVhel047435@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by ian:

URL: https://cgit.FreeBSD.org/src/commit/?id=dc91a9715f8fda4b3633388830a28a99f73cbe59

commit dc91a9715f8fda4b3633388830a28a99f73cbe59
Author:     Ian Lepore <ian@FreeBSD.org>
AuthorDate: 2021-09-28 19:29:10 +0000
Commit:     Ian Lepore <ian@FreeBSD.org>
CommitDate: 2021-09-28 19:29:10 +0000

    Fix busdma resource leak on usb device detach.
    
    When a usb device is detached, usb_pc_dmamap_destroy() called
    bus_dmamap_destroy() while the map was still loaded. That's harmless on x86
    architectures, but on all other platforms it causes bus_dmamap_destroy() to
    return EBUSY and leak away any memory resources (including bounce buffers)
    associated with the mapping, as well as any allocated map structure itself.
    
    This change introduces a new is_loaded flag to the usb_page_cache struct to
    track whether a map is loaded or not. If the map is loaded,
    bus_dmamap_unload() is called before bus_dmamap_destroy() to avoid leaking
    away resources.
    
    MFC after:      7 days
    Differential Revision:  https://reviews.freebsd.org/D32208
---
 sys/dev/usb/usb_busdma.c | 16 +++++++++++++---
 sys/dev/usb/usb_busdma.h |  1 +
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/sys/dev/usb/usb_busdma.c b/sys/dev/usb/usb_busdma.c
index 4806903fa83a..62e22805b39c 100644
--- a/sys/dev/usb/usb_busdma.c
+++ b/sys/dev/usb/usb_busdma.c
@@ -600,6 +600,7 @@ usb_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg,
 		bus_dmamem_free(utag->tag, ptr, map);
 		goto error;
 	}
+	pc->isloaded = 1;
 	memset(ptr, 0, size);
 
 	usb_pc_cpu_flush(pc);
@@ -612,6 +613,7 @@ error:
 	pc->page_start = NULL;
 	pc->page_offset_buf = 0;
 	pc->page_offset_end = 0;
+	pc->isloaded = 0;
 	pc->map = NULL;
 	pc->tag = NULL;
 	return (1);
@@ -626,11 +628,13 @@ void
 usb_pc_free_mem(struct usb_page_cache *pc)
 {
 	if (pc && pc->buffer) {
-		bus_dmamap_unload(pc->tag, pc->map);
+		if (pc->isloaded)
+			bus_dmamap_unload(pc->tag, pc->map);
 
 		bus_dmamem_free(pc->tag, pc->buffer, pc->map);
 
 		pc->buffer = NULL;
+		pc->isloaded = 0;
 	}
 }
 
@@ -662,7 +666,8 @@ usb_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync)
 			 * We have to unload the previous loaded DMA
 			 * pages before trying to load a new one!
 			 */
-			bus_dmamap_unload(pc->tag, pc->map);
+			if (pc->isloaded)
+				bus_dmamap_unload(pc->tag, pc->map);
 
 			/*
 			 * Try to load memory into DMA.
@@ -675,6 +680,7 @@ usb_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync)
 				err = 0;
 			}
 			if (err || uptag->dma_error) {
+				pc->isloaded = 0;
 				return (1);
 			}
 		} else {
@@ -682,7 +688,8 @@ usb_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync)
 			 * We have to unload the previous loaded DMA
 			 * pages before trying to load a new one!
 			 */
-			bus_dmamap_unload(pc->tag, pc->map);
+			if (pc->isloaded)
+				bus_dmamap_unload(pc->tag, pc->map);
 
 			/*
 			 * Try to load memory into DMA. The callback
@@ -693,6 +700,7 @@ usb_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync)
 			    &usb_pc_load_mem_cb, pc, BUS_DMA_WAITOK)) {
 			}
 		}
+		pc->isloaded = 1;
 	} else {
 		if (!sync) {
 			/*
@@ -785,6 +793,8 @@ void
 usb_pc_dmamap_destroy(struct usb_page_cache *pc)
 {
 	if (pc && pc->tag) {
+		if (pc->isloaded)
+			bus_dmamap_unload(pc->tag, pc->map);
 		bus_dmamap_destroy(pc->tag, pc->map);
 		pc->tag = NULL;
 		pc->map = NULL;
diff --git a/sys/dev/usb/usb_busdma.h b/sys/dev/usb/usb_busdma.h
index ca0631e2ce53..2f60b30963d2 100644
--- a/sys/dev/usb/usb_busdma.h
+++ b/sys/dev/usb/usb_busdma.h
@@ -101,6 +101,7 @@ struct usb_page_cache {
 					 * from the memory. Else write. */
 	uint8_t	ismultiseg:1;		/* set if we can have multiple
 					 * segments */
+	uint8_t isloaded:1;		/* Set if map is currently loaded. */
 #endif
 };
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202109281931.18SJVhel047435>