Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 17 Apr 2015 07:07:07 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r281644 - head/sys/dev/usb/video
Message-ID:  <201504170707.t3H777HV089365@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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;



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