The branch stable/12 has been updated by ian:

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

commit 6fea4b82e7b86ac680d5615f8361863353737325
Author:     Ian Lepore <[email protected]>
AuthorDate: 2021-09-28 19:29:10 +0000
Commit:     Ian Lepore <[email protected]>
CommitDate: 2021-10-06 16:08:34 +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.
    
    Differential Revision:  https://reviews.freebsd.org/D32208
    
    (cherry picked from commit dc91a9715f8fda4b3633388830a28a99f73cbe59)
---
 sys/dev/usb/usb_busdma.c | 17 +++++++++++++----
 sys/dev/usb/usb_busdma.h |  1 +
 2 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/sys/dev/usb/usb_busdma.c b/sys/dev/usb/usb_busdma.c
index b7f0464c1767..9df2b243d232 100644
--- a/sys/dev/usb/usb_busdma.c
+++ b/sys/dev/usb/usb_busdma.c
@@ -611,6 +611,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);
@@ -623,6 +624,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);
@@ -637,12 +639,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;
        }
 }
 
@@ -674,7 +677,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.
@@ -687,6 +691,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 {
@@ -695,7 +700,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
@@ -706,6 +712,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) {
                        /*
@@ -798,6 +805,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 d75e67eee8fe..d55ab5c00af0 100644
--- a/sys/dev/usb/usb_busdma.h
+++ b/sys/dev/usb/usb_busdma.h
@@ -102,6 +102,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
 };
 

Reply via email to