[PATCH] mieq: Provide better adaptability and diagnostics during mieq overflow
Jeremy Huddleston
jeremyhu at apple.com
Sat Oct 15 23:09:30 PDT 2011
This patch changes from a static length event queue (512) to one that starts
at 128 and grows to 4096 as it overflows, logging each time it grows.
This change also allows for multiple backtraces to be printed when the server
is wedged rather than just one. This increased sampling should help identify
the true hog in cases where one backtrace might be insufficient.
Signed-off-by: Jeremy Huddleston <jeremyhu at apple.com>
---
I tested the logic by setting:
#define INITIAL_QUEUE_SIZE 2
#define MAXIMUM_QUEUE_SIZE 8
#define DROP_BACKTRACE_FREQUENCY 2
#define DROP_BACKTRACE_MAX 2
but I was so focused on trying to generate many quick events, that I didn't verify that mieqGrowQueue preserved order properly. It looks like it does by code analysis, but I'm the guy who wrote it, so... yeah... please audit it, and make sure it's right
mi/mieq.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++--------------
1 files changed, 80 insertions(+), 24 deletions(-)
diff --git a/mi/mieq.c b/mi/mieq.c
index b75bde9..00934c5 100644
--- a/mi/mieq.c
+++ b/mi/mieq.c
@@ -59,7 +59,11 @@ in this Software without prior written authorization from The Open Group.
# include <X11/extensions/dpmsconst.h>
#endif
-#define QUEUE_SIZE 512
+/* Queue size must be a power of 2 */
+#define INITIAL_QUEUE_SIZE 128
+#define MAXIMUM_QUEUE_SIZE 4096
+#define DROP_BACKTRACE_FREQUENCY 100
+#define DROP_BACKTRACE_MAX 20
#define EnqueueScreen(dev) dev->spriteInfo->sprite->pEnqueueScreen
#define DequeueScreen(dev) dev->spriteInfo->sprite->pDequeueScreen
@@ -74,7 +78,9 @@ typedef struct _EventQueue {
HWEventQueueType head, tail; /* long for SetInputCheck */
CARD32 lastEventTime; /* to avoid time running backwards */
int lastMotion; /* device ID if last event motion? */
- EventRec events[QUEUE_SIZE]; /* static allocation for signals */
+ EventRec *events; /* our queue as an array */
+ size_t q_size; /* the size of our queue */
+ size_t dropped; /* counter for number of consecutive dropped events */
mieqHandler handlers[128]; /* custom event handler */
} EventQueueRec, *EventQueuePtr;
@@ -99,6 +105,38 @@ static inline void wait_for_server_init(void) {
}
#endif
+/* Pre-condition: Called with miEventQueueMutex held */
+static void
+mieqGrowQueue(void) {
+ size_t i;
+ size_t new_size = miEventQueue.q_size << 1;
+ EventRec *new_queue = calloc(new_size, sizeof(EventRec));
+
+ if (new_queue == NULL)
+ FatalError("Unable to allocate memory for the event queue.\n");
+
+ /* First copy the existing events */
+ memcpy(new_queue, &miEventQueue.events[miEventQueue.head],
+ (miEventQueue.q_size - miEventQueue.head) * sizeof(EventRec));
+ memcpy(&new_queue[miEventQueue.q_size - miEventQueue.head], miEventQueue.events,
+ (miEventQueue.head) * sizeof(EventRec));
+
+ /* Initialize the new portion */
+ for (i = miEventQueue.q_size; i < new_size; i++) {
+ InternalEvent* evlist = InitEventList(1);
+ if (!evlist)
+ FatalError("Could not allocate event queue.\n");
+ new_queue[i].events = evlist;
+ }
+
+ /* And update our record */
+ miEventQueue.tail = (miEventQueue.tail - miEventQueue.head) % miEventQueue.q_size;
+ miEventQueue.head = 0;
+ miEventQueue.q_size = new_size;
+ free(miEventQueue.events);
+ miEventQueue.events = new_queue;
+}
+
Bool
mieqInit(void)
{
@@ -109,7 +147,11 @@ mieqInit(void)
miEventQueue.lastMotion = FALSE;
for (i = 0; i < 128; i++)
miEventQueue.handlers[i] = NULL;
- for (i = 0; i < QUEUE_SIZE; i++)
+ miEventQueue.q_size = INITIAL_QUEUE_SIZE;
+ miEventQueue.events = calloc(miEventQueue.q_size, sizeof(EventRec));
+ if(miEventQueue.events == NULL)
+ FatalError("Could not allocate event queue.\n");
+ for (i = 0; i < miEventQueue.q_size; i++)
{
if (miEventQueue.events[i].events == NULL) {
InternalEvent* evlist = InitEventList(1);
@@ -127,13 +169,14 @@ void
mieqFini(void)
{
int i;
- for (i = 0; i < QUEUE_SIZE; i++)
+ for (i = 0; i < miEventQueue.q_size; i++)
{
if (miEventQueue.events[i].events != NULL) {
FreeEventList(miEventQueue.events[i].events, 1);
miEventQueue.events[i].events = NULL;
}
}
+ free(miEventQueue.events);
}
/*
@@ -165,27 +208,40 @@ mieqEnqueue(DeviceIntPtr pDev, InternalEvent *e)
if (isMotion && isMotion == miEventQueue.lastMotion &&
oldtail != miEventQueue.head) {
- oldtail = (oldtail - 1) % QUEUE_SIZE;
- }
- else {
- static int stuck = 0;
- /* Toss events which come in late. Usually this means your server's
- * stuck in an infinite loop somewhere, but SIGIO is still getting
- * handled. */
- if (((oldtail + 1) % QUEUE_SIZE) == miEventQueue.head) {
- if (!stuck) {
- ErrorF("[mi] EQ overflowing. The server is probably stuck "
- "in an infinite loop.\n");
- xorg_backtrace();
- stuck = 1;
- }
+ oldtail = (oldtail - 1) % miEventQueue.q_size;
+ } else if (((oldtail + 1) % miEventQueue.q_size) == miEventQueue.head) {
+ if (miEventQueue.q_size < MAXIMUM_QUEUE_SIZE) {
+ ErrorF("[mi] EQ overflowing. Increasing queue size to %lu to accomidate.\n", miEventQueue.q_size << 1);
+ ErrorF("[mi] This may be indicative of a misbehaving driver monopolizing the server.\n");
+ mieqGrowQueue();
+ } else {
+ /* Toss events which come in late. Usually this means your server's
+ * stuck in an infinite loop somewhere, but SIGIO is still getting
+ * handled. */
+ miEventQueue.dropped++;
+ if (miEventQueue.dropped == 1) {
+ ErrorF("[mi] EQ overflowing. Maximum queue size reached. Events will be discarded.\n");
+ xorg_backtrace();
+ } else if (miEventQueue.dropped % DROP_BACKTRACE_FREQUENCY == 0 &&
+ miEventQueue.dropped / DROP_BACKTRACE_FREQUENCY <= DROP_BACKTRACE_MAX) {
+ ErrorF("[mi] EQ overflow continuing. %lu events have been dropped.\n", miEventQueue.dropped);
+ if (miEventQueue.dropped / DROP_BACKTRACE_FREQUENCY == DROP_BACKTRACE_MAX) {
+ ErrorF("[mi] No further overflow reports will be reported until the clog is cleared.\n");
+ }
+ xorg_backtrace();
+ }
+
#ifdef XQUARTZ
pthread_mutex_unlock(&miEventQueueMutex);
#endif
- return;
- }
- stuck = 0;
- }
+ return;
+ }
+ }
+
+ if (miEventQueue.dropped) {
+ ErrorF("[mi] EQ processing has resumed after %lu dropped events.\n", miEventQueue.dropped);
+ miEventQueue.dropped = 0;
+ }
evlen = e->any.length;
evt = miEventQueue.events[oldtail].events;
@@ -203,7 +259,7 @@ mieqEnqueue(DeviceIntPtr pDev, InternalEvent *e)
miEventQueue.events[oldtail].pDev = pDev;
miEventQueue.lastMotion = isMotion;
- miEventQueue.tail = (oldtail + 1) % QUEUE_SIZE;
+ miEventQueue.tail = (oldtail + 1) % miEventQueue.q_size;
#ifdef XQUARTZ
pthread_mutex_unlock(&miEventQueueMutex);
#endif
@@ -449,7 +505,7 @@ mieqProcessInputEvents(void)
dev = e->pDev;
screen = e->pScreen;
- miEventQueue.head = (miEventQueue.head + 1) % QUEUE_SIZE;
+ miEventQueue.head = (miEventQueue.head + 1) % miEventQueue.q_size;
#ifdef XQUARTZ
pthread_mutex_unlock(&miEventQueueMutex);
--
1.7.6.1
More information about the xorg-devel
mailing list