[PATCH] EXA: Defragment offscreen memory.

Michel Dänzer michel at daenzer.net
Tue Feb 24 01:41:31 PST 2009


From: Michel Dänzer <daenzer at vmware.com>

At most once per second, under the following circumstances:

* We can't satisfy an offscreen memory allocation, but there seems to be enough
  offscreen memory available in total.
* The server has been idle for at least 100ms.

Signed-off-by: Michel Dänzer <daenzer at vmware.com>
---
 exa/exa.c           |   55 +++++++++++++
 exa/exa.h           |    3 +
 exa/exa_offscreen.c |  222 +++++++++++++++++++++++++++++++++++++++++++++++++--
 exa/exa_priv.h      |    8 ++
 4 files changed, 281 insertions(+), 7 deletions(-)

I verified in gdb that ExaDefragTimerCallback gets called occasionally, but not
when the server is busy (so e.g. x11perf -aa*text numbers are unaffected).

That said, I would have expected it to get called every 100ms when the server
is idle, but that isn't the case, so maybe I'm missing something and was just
lucky it didn't blow up for me. I'd like to get confirmation from Adam that the
BlockHandler/WakeupHandler/Timer idea makes sense.

diff --git a/exa/exa.c b/exa/exa.c
index 9329f10..b0c96fb 100644
--- a/exa/exa.c
+++ b/exa/exa.c
@@ -882,6 +882,49 @@ exaCreateScreenResources(ScreenPtr pScreen)
     return TRUE;
 }
 
+static CARD32 ExaDefragTimerCallback(OsTimerPtr pTimer, CARD32 time,
+				     pointer arg)
+{
+    /* Try and keep the offscreen memory area tidy every now and then when the
+     * server has been idle for at least 100ms.
+     */
+    ExaOffscreenDefragment(arg);
+
+    return 0;
+}
+
+static void
+ExaBlockHandler(int screenNum, pointer blockData, pointer pTimeout,
+		pointer pReadmask)
+{
+    ScreenPtr pScreen = screenInfo.screens[screenNum];
+    ExaScreenPriv(pScreen);
+
+    unwrap(pExaScr, pScreen, BlockHandler);
+    (*pScreen->BlockHandler) (screenNum, blockData, pTimeout, pReadmask);
+    wrap(pExaScr, pScreen, BlockHandler, ExaBlockHandler);
+
+    pExaScr->defragTimer = TimerSet(pExaScr->defragTimer, 0, 100,
+				    ExaDefragTimerCallback, pScreen);
+}
+
+static void
+ExaWakeupHandler(int screenNum, pointer wakeupData, unsigned long result,
+		 pointer pReadmask)
+{
+    ScreenPtr pScreen = screenInfo.screens[screenNum];
+    ExaScreenPriv(pScreen);
+
+    /* Prevent the timer callback from being called if Select() didn't time out.
+     */
+    if (result != 0)
+	TimerCancel(pExaScr->defragTimer);
+
+    unwrap(pExaScr, pScreen, WakeupHandler);
+    (*pScreen->WakeupHandler) (screenNum, wakeupData, result, pReadmask);
+    wrap(pExaScr, pScreen, WakeupHandler, ExaWakeupHandler);
+}
+
 /**
  * exaCloseScreen() unwraps its wrapped screen functions and tears down EXA's
  * screen private, before calling down to the next CloseSccreen.
@@ -897,6 +940,13 @@ exaCloseScreen(int i, ScreenPtr pScreen)
     if (ps->Glyphs == exaGlyphs)
 	exaGlyphsFini(pScreen);
 
+    if (pExaScr->defragTimer)
+	TimerFree(pExaScr->defragTimer);
+
+    if (pScreen->BlockHandler == ExaBlockHandler)
+	unwrap(pExaScr, pScreen, BlockHandler);
+    if (pScreen->WakeupHandler == ExaWakeupHandler)
+	unwrap(pExaScr, pScreen, WakeupHandler);
     unwrap(pExaScr, pScreen, CreateGC);
     unwrap(pExaScr, pScreen, CloseScreen);
     unwrap(pExaScr, pScreen, GetImage);
@@ -1056,6 +1106,11 @@ exaDriverInit (ScreenPtr		pScreen,
     /*
      * Replace various fb screen functions
      */
+    if ((pExaScr->info->flags & EXA_OFFSCREEN_PIXMAPS) &&
+	!pExaScr->info->CreatePixmap) {
+	wrap(pExaScr, pScreen, BlockHandler, ExaBlockHandler);
+	wrap(pExaScr, pScreen, WakeupHandler, ExaWakeupHandler);
+    }
     wrap(pExaScr, pScreen, CreateGC, exaCreateGC);
     wrap(pExaScr, pScreen, CloseScreen, exaCloseScreen);
     wrap(pExaScr, pScreen, GetImage, exaGetImage);
diff --git a/exa/exa.h b/exa/exa.h
index b80d0d4..e5f18a4 100644
--- a/exa/exa.h
+++ b/exa/exa.h
@@ -75,6 +75,9 @@ struct _ExaOffscreenArea {
     ExaOffscreenArea    *next;
 
     unsigned            eviction_cost;
+
+    ExaOffscreenArea    *prev;          /* Double-linked list for defragmentation */
+    int                 align;          /* required alignment */
 };
 
 /**
diff --git a/exa/exa_offscreen.c b/exa/exa_offscreen.c
index c45df67..95d5e43 100644
--- a/exa/exa_offscreen.c
+++ b/exa/exa_offscreen.c
@@ -172,7 +172,7 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align,
 {
     ExaOffscreenArea *area;
     ExaScreenPriv (pScreen);
-    int tmp, real_size = 0;
+    int tmp, real_size = 0, free_total = 0, largest_avail = 0;
 #if DEBUG_OFFSCREEN
     static int number = 0;
     ErrorF("================= ============ allocating a new pixmap %d\n", ++number);
@@ -213,6 +213,27 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align,
 	/* does it fit? */
 	if (real_size <= area->size)
 	    break;
+
+	free_total += area->size;
+
+	if (area->size > largest_avail)
+	    largest_avail = area->size;
+    }
+
+    if (!area && free_total >= size) {
+	area = ExaOffscreenDefragment(pScreen);
+
+	if (area) {
+	    /* adjust size to match alignment requirement */
+	    real_size = size;
+	    tmp = area->base_offset % align;
+	    if (tmp)
+		real_size += (align - tmp);
+
+	    /* does it fit? */
+	    if (real_size > area->size)
+		area = NULL;
+	}
     }
 
     if (!area)
@@ -255,14 +276,27 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align,
 	if (!new_area)
 	    return NULL;
 	new_area->base_offset = area->base_offset + real_size;
+
+#if DEBUG_OFFSCREEN
+	if (new_area->base_offset >= pExaScr->info->memorySize)
+	    ErrorF("new_area->base_offset = 0x%08x >= memorySize!\n",
+		   new_area->base_offset);
+#endif
+
 	new_area->offset = new_area->base_offset;
+	new_area->align = 0;
 	new_area->size = area->size - real_size;
 	new_area->state = ExaOffscreenAvail;
 	new_area->save = NULL;
 	new_area->last_use = 0;
 	new_area->eviction_cost = 0;
 	new_area->next = area->next;
+	if (area->next)
+	    area->next->prev = new_area;
+	else
+	    pExaScr->info->offScreenAreas->prev = new_area;
 	area->next = new_area;
+	new_area->prev = area;
 	area->size = real_size;
     }
     /*
@@ -277,6 +311,7 @@ exaOffscreenAlloc (ScreenPtr pScreen, int size, int align,
     area->last_use = pExaScr->offScreenCounter++;
     area->offset = (area->base_offset + align - 1);
     area->offset -= area->offset % align;
+    area->align = align;
 
     ExaOffscreenValidate (pScreen);
 
@@ -341,7 +376,7 @@ exaEnableDisableFBAccess (int index, Bool enable)
 
 /* merge the next free area into this one */
 static void
-ExaOffscreenMerge (ExaOffscreenArea *area)
+ExaOffscreenMerge (ExaScreenPrivPtr pExaScr, ExaOffscreenArea *area)
 {
     ExaOffscreenArea	*next = area->next;
 
@@ -349,6 +384,10 @@ ExaOffscreenMerge (ExaOffscreenArea *area)
     area->size += next->size;
     /* frob pointer */
     area->next = next->next;
+    if (area->next)
+	area->next->prev = area;
+    else
+	pExaScr->info->offScreenAreas->prev = area;
     xfree (next);
 }
 
@@ -386,19 +425,17 @@ exaOffscreenFree (ScreenPtr pScreen, ExaOffscreenArea *area)
     if (area == pExaScr->info->offScreenAreas)
 	prev = NULL;
     else
-	for (prev = pExaScr->info->offScreenAreas; prev; prev = prev->next)
-	    if (prev->next == area)
-		break;
+	prev = area->prev;
 
     /* link with next area if free */
     if (next && next->state == ExaOffscreenAvail)
-	ExaOffscreenMerge (area);
+	ExaOffscreenMerge (pExaScr, area);
 
     /* link with prev area if free */
     if (prev && prev->state == ExaOffscreenAvail)
     {
 	area = prev;
-	ExaOffscreenMerge (area);
+	ExaOffscreenMerge (pExaScr, area);
     }
 
     ExaOffscreenValidate (pScreen);
@@ -419,6 +456,175 @@ ExaOffscreenMarkUsed (PixmapPtr pPixmap)
 }
 
 /**
+ * Defragment offscreen memory by compacting allocated areas at the end of it,
+ * leaving the total amount of memory available as a single area at the
+ * beginning (when there are no pinned allocations).
+ */
+_X_HIDDEN ExaOffscreenArea*
+ExaOffscreenDefragment (ScreenPtr pScreen)
+{
+    ExaScreenPriv (pScreen);
+    ExaOffscreenArea *area, *largest_available = NULL;
+    int largest_size = 0;
+    PixmapPtr pDstPix;
+    ExaPixmapPrivPtr pExaDstPix;
+    CARD32 now = GetTimeInMillis();
+
+    /* Don't defragment more than once per second, to avoid adding more overhead
+     * than we're trying to prevent
+     */
+    if (abs((INT32) (now - pExaScr->lastDefragment)) < 1000)
+	return NULL;
+
+    pDstPix = (*pScreen->CreatePixmap) (pScreen, 0, 0, 0, 0);
+
+    if (!pDstPix)
+	return NULL;
+
+    pExaScr->lastDefragment = now;
+
+    pExaDstPix = ExaGetPixmapPriv (pDstPix);
+    pExaDstPix->offscreen = TRUE;
+
+    for (area = pExaScr->info->offScreenAreas->prev;
+	 area != pExaScr->info->offScreenAreas;
+	 )
+    {
+	ExaOffscreenArea *prev = area->prev;
+	PixmapPtr pSrcPix;
+	ExaPixmapPrivPtr pExaSrcPix;
+	Bool save_offscreen;
+	int save_pitch;
+
+	if (area->state != ExaOffscreenAvail ||
+	    prev->state == ExaOffscreenLocked ||
+	    (prev->state == ExaOffscreenRemovable &&
+	     prev->save != exaPixmapSave)) {
+	    area = prev;
+	    continue;
+	}
+
+	if (prev->state == ExaOffscreenAvail) {
+	    if (area == largest_available) {
+		largest_available = prev;
+		largest_size += prev->size;
+	    }
+	    area = prev;
+	    ExaOffscreenMerge (pExaScr, area);
+	    continue;
+	}
+
+	if (area->size > largest_size) {
+	    largest_available = area;
+	    largest_size = area->size;
+	}
+
+	pSrcPix = prev->privData;
+	pExaSrcPix = ExaGetPixmapPriv (pSrcPix);
+
+	pExaDstPix->fb_ptr = pExaScr->info->memoryBase +
+	    area->base_offset + area->size - prev->size + prev->base_offset -
+	    prev->offset;
+	pExaDstPix->fb_ptr -= (unsigned long)pExaDstPix->fb_ptr % prev->align;
+
+	if (pExaDstPix->fb_ptr <= pExaSrcPix->fb_ptr) {
+	    area = prev;
+	    continue;
+	}
+
+	save_offscreen = pExaSrcPix->offscreen;
+	save_pitch = pSrcPix->devKind;
+
+	pExaSrcPix->offscreen = TRUE;
+	pSrcPix->devKind = pExaSrcPix->fb_pitch;
+
+	pDstPix->drawable.width = pSrcPix->drawable.width;
+	pDstPix->devKind = pSrcPix->devKind;
+	pDstPix->drawable.height = pSrcPix->drawable.height;
+	pDstPix->drawable.depth = pSrcPix->drawable.depth;
+	pDstPix->drawable.bitsPerPixel = pSrcPix->drawable.bitsPerPixel;
+
+	if (!pExaScr->info->PrepareCopy (pSrcPix, pDstPix, -1, -1, GXcopy, ~0)) {
+	    /* If we can't move the area, just kick it out - apparently it's
+	     * not usable even for trivial acceleration anyway
+	     */
+	    pExaSrcPix->offscreen = save_offscreen;
+	    pSrcPix->devKind = save_pitch;
+
+	    area = ExaOffscreenKickOut(pScreen, area);
+
+	    continue;
+	}
+
+	pExaScr->info->Copy (pDstPix, 0, 0, 0, 0, pDstPix->drawable.width,
+			     pDstPix->drawable.height);
+	pExaScr->info->DoneCopy (pDstPix);
+	exaMarkSync (pScreen);
+
+	DBG_OFFSCREEN(("Before swap: prev=0x%08x-0x%08x-0x%08x area=0x%08x-0x%08x-0x%08x\n",
+		       prev->base_offset, prev->offset, prev->base_offset + prev->size,
+		       area->base_offset, area->offset, area->base_offset + area->size));
+
+	/* Calculate swapped area offsets and sizes */
+	area->base_offset = prev->base_offset;
+	area->offset = area->base_offset;
+	prev->offset += pExaDstPix->fb_ptr - pExaSrcPix->fb_ptr;
+	assert(prev->offset >= pExaScr->info->offScreenBase &&
+	       prev->offset < pExaScr->info->memorySize);
+	prev->base_offset = prev->offset;
+	if (area->next)
+	    prev->size = area->next->base_offset - prev->base_offset;
+	else
+	    prev->size = pExaScr->info->memorySize - prev->base_offset;
+	area->size = prev->base_offset - area->base_offset;
+
+	DBG_OFFSCREEN(("After swap: area=0x%08x-0x%08x-0x%08x prev=0x%08x-0x%08x-0x%08x\n",
+		       area->base_offset, area->offset, area->base_offset + area->size,
+		       prev->base_offset, prev->offset, prev->base_offset + prev->size));
+
+	/* Swap areas in list */
+	if (area->next)
+	    area->next->prev = prev;
+	else
+	    pExaScr->info->offScreenAreas->prev = prev;
+	if (prev->prev->next)
+	    prev->prev->next = area;
+	else
+	    pExaScr->info->offScreenAreas = area;
+	prev->next = area->next;
+	area->next = prev;
+	area->prev = prev->prev;
+	prev->prev = area;
+	if (!area->prev->next)
+	    pExaScr->info->offScreenAreas = area;
+
+#if DEBUG_OFFSCREEN
+	if (prev->prev == prev || prev->next == prev)
+	    ErrorF("Whoops, prev points to itself!\n");
+
+	if (area->prev == area || area->next == area)
+	    ErrorF("Whoops, area points to itself!\n");
+#endif
+
+	pExaSrcPix->fb_ptr = pExaDstPix->fb_ptr;
+	pExaSrcPix->offscreen = save_offscreen;
+	pSrcPix->devKind = save_pitch;
+    }
+
+    pDstPix->drawable.width = 0;
+    pDstPix->drawable.height = 0;
+    pDstPix->drawable.depth = 0;
+    pDstPix->drawable.bitsPerPixel = 0;
+
+    (*pScreen->DestroyPixmap) (pDstPix);
+
+    if (area->state == ExaOffscreenAvail && area->size > largest_size)
+	return area;
+
+    return largest_available;
+}
+
+/**
  * exaOffscreenInit initializes the offscreen memory manager.
  *
  * @param pScreen current screen
@@ -441,9 +647,11 @@ exaOffscreenInit (ScreenPtr pScreen)
     area->state = ExaOffscreenAvail;
     area->base_offset = pExaScr->info->offScreenBase;
     area->offset = area->base_offset;
+    area->align = 0;
     area->size = pExaScr->info->memorySize - area->base_offset;
     area->save = NULL;
     area->next = NULL;
+    area->prev = area;
     area->last_use = 0;
     area->eviction_cost = 0;
 
diff --git a/exa/exa_priv.h b/exa/exa_priv.h
index ea8c3da..957d176 100644
--- a/exa/exa_priv.h
+++ b/exa/exa_priv.h
@@ -133,6 +133,8 @@ typedef struct {
 typedef void (*EnableDisableFBAccessProcPtr)(int, Bool);
 typedef struct {
     ExaDriverPtr info;
+    ScreenBlockHandlerProcPtr	 SavedBlockHandler;
+    ScreenWakeupHandlerProcPtr	 SavedWakeupHandler;
     CreateGCProcPtr 		 SavedCreateGC;
     CloseScreenProcPtr 		 SavedCloseScreen;
     GetImageProcPtr 		 SavedGetImage;
@@ -158,6 +160,9 @@ typedef struct {
     unsigned			 disableFbCount;
     Bool			 optimize_migration;
     unsigned			 offScreenCounter;
+    CARD32			 lastDefragment;
+    OsTimerPtr			 defragTimer;
+
     /* Holds information on fallbacks that cannot be relayed otherwise. */
     unsigned int fallback_flags;
 
@@ -433,6 +438,9 @@ ExaCheckComposite (CARD8      op,
 #endif
 
 /* exa_offscreen.c */
+ExaOffscreenArea*
+ExaOffscreenDefragment (ScreenPtr pScreen);
+
 Bool
 exaOffscreenInit(ScreenPtr pScreen);
 
-- 
1.6.2.rc1



More information about the xorg-devel mailing list