Author: hselasky
Date: Fri Apr 17 07:07:06 2015
New Revision: 281644
URL: https://svnweb.freebsd.org/changeset/base/281644

Log:
  Buffers which can be memory mapped into userspace should never be
  freed. Recycle the buffers instead. This patch also fixes a panic at
  reboot issue when an UDL adapter is attached to the system.

Modified:
  head/sys/dev/usb/video/udl.c
  head/sys/dev/usb/video/udl.h

Modified: head/sys/dev/usb/video/udl.c
==============================================================================
--- head/sys/dev/usb/video/udl.c        Fri Apr 17 06:39:15 2015        
(r281643)
+++ head/sys/dev/usb/video/udl.c        Fri Apr 17 07:07:06 2015        
(r281644)
@@ -76,6 +76,11 @@ static int udl_fps = 25;
 SYSCTL_INT(_hw_usb_udl, OID_AUTO, fps, CTLFLAG_RWTUN,
     &udl_fps, 0, "Frames Per Second, 1-60");
 
+static struct mtx udl_buffer_mtx;
+static struct udl_buffer_head udl_buffer_head;
+
+MALLOC_DEFINE(M_USB_DL, "USB", "USB DisplayLink");
+
 /*
  * Prototypes.
  */
@@ -175,6 +180,56 @@ static const STRUCT_USB_HOST_ID udl_devs
        {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421, DLUNK)}
 };
 
+static void
+udl_buffer_init(void *arg)
+{
+       mtx_init(&udl_buffer_mtx, "USB", "UDL", MTX_DEF);
+       TAILQ_INIT(&udl_buffer_head);
+}
+SYSINIT(udl_buffer_init, SI_SUB_LOCK, SI_ORDER_FIRST, udl_buffer_init, NULL);
+
+CTASSERT(sizeof(struct udl_buffer) < PAGE_SIZE);
+
+static void *
+udl_buffer_alloc(uint32_t size)
+{
+       struct udl_buffer *buf;
+       mtx_lock(&udl_buffer_mtx);
+       TAILQ_FOREACH(buf, &udl_buffer_head, entry) {
+               if (buf->size == size) {
+                       TAILQ_REMOVE(&udl_buffer_head, buf, entry);
+                       break;
+               }
+       }
+       mtx_unlock(&udl_buffer_mtx);
+       if (buf != NULL) {
+               /* wipe and recycle buffer */
+               memset(buf, 0, size);
+               return (buf);
+       }
+       /* allocate new buffer */
+       return (malloc(size, M_USB_DL, M_WAITOK | M_ZERO));
+}
+
+static void
+udl_buffer_free(void *_buf, uint32_t size)
+{
+       struct udl_buffer *buf;
+
+       buf = (struct udl_buffer *)_buf;
+       if (buf == NULL)
+               return;
+
+       /*
+        * Memory mapped buffers should never be freed.
+        * Put display buffer into a recycle list.
+        */
+       mtx_lock(&udl_buffer_mtx);
+       buf->size = size;
+       TAILQ_INSERT_TAIL(&udl_buffer_head, buf, entry);
+       mtx_unlock(&udl_buffer_mtx);
+}
+
 static uint32_t
 udl_get_fb_size(struct udl_softc *sc)
 {
@@ -403,11 +458,11 @@ udl_detach(device_t dev)
        mtx_destroy(&sc->sc_mtx);
        cv_destroy(&sc->sc_cv);
 
-       /*
-        * Free framebuffer memory, if any.
-        */
-       free(sc->sc_fb_addr, M_DEVBUF);
-       free(sc->sc_fb_copy, M_DEVBUF);
+       /* put main framebuffer into a recycle list, if any */
+       udl_buffer_free(sc->sc_fb_addr, sc->sc_fb_size);
+
+       /* free shadow framebuffer memory, if any */
+       free(sc->sc_fb_copy, M_USB_DL);
 
        return (0);
 }
@@ -782,13 +837,15 @@ udl_fbmem_alloc(struct udl_softc *sc)
 
        size = udl_get_fb_size(sc);
        size = round_page(size);
-
+       /* check for zero size */
+       if (size == 0)
+               size = PAGE_SIZE;
        /*
         * It is assumed that allocations above PAGE_SIZE bytes will
         * be PAGE_SIZE aligned for use with mmap()
         */
-       sc->sc_fb_addr = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
-       sc->sc_fb_copy = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
+       sc->sc_fb_addr = udl_buffer_alloc(size);
+       sc->sc_fb_copy = malloc(size, M_USB_DL, M_WAITOK | M_ZERO);
        sc->sc_fb_size = size;
 }
 

Modified: head/sys/dev/usb/video/udl.h
==============================================================================
--- head/sys/dev/usb/video/udl.h        Fri Apr 17 06:39:15 2015        
(r281643)
+++ head/sys/dev/usb/video/udl.h        Fri Apr 17 07:07:06 2015        
(r281644)
@@ -34,6 +34,15 @@
 #define        UDL_FONT_HEIGHT         16      /* pixels */
 #define        UDL_MAX_MODES           25      /* units */
 
+MALLOC_DECLARE(M_USB_DL);
+
+struct udl_buffer {
+       TAILQ_ENTRY(udl_buffer) entry;
+       uint32_t size;
+};
+
+TAILQ_HEAD(udl_buffer_head, udl_buffer);
+
 struct udl_cmd_buf {
        TAILQ_ENTRY(udl_cmd_buf) entry;
        uint32_t off;
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to