Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 29 Sep 2011 03:10:42 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-user@freebsd.org
Subject:   svn commit: r225860 - user/adrian/if_ath_tx/sys/mips/mips
Message-ID:  <201109290310.p8T3AgZO001901@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: adrian
Date: Thu Sep 29 03:10:42 2011
New Revision: 225860
URL: http://svn.freebsd.org/changeset/base/225860

Log:
  The MIPS cpu_idle() routine is unfortunately buggy now that
  mav's eventtimer code has gone into the tree.
  
  If an interrupt comes in after critical_enter() is called but
  before "wait" occurs, the interrupt will be handled but it
  won't:
  
  * preempt the current idle thread, if PREEMPTION is enabled and
    you're using the 4BSD scheduler;
  * won't interrupt "wait", because it occured before it;
  * won't be handled until the next interrupt that occurs after
    the "wait" instruction is entered.
  
  So the issues with if_arge and if_ath can be traced down to
  a race with this particular behaviour in MIPS cpu_idle().
  The interrupt would be posted but not handled until another
  interrupt triggers things.
  
  I've tried doing what is done in i386 -
  
  * interrupts are disabled
  * sched_runnable() is checked - if it's runnable, skip
    calling wait and instead reenable interrupts
  * otherwise atomically enable interrupts and call hlt.
  
  If an interrupt has come in and scheduled a netisr or
  taskqueue call, it'll (hopefully!) trip sched_runnable() and
  thus skip the wait.
  
  The atomic nature of "sti; hlt" is apparently documented
  for the i386. This way there's no race where an interrupt
  comes in before the hlt and doesn't wake it up.
  
  Now, I've tried the same for MIPS but it doesn't seem to
  fix things. The behaviour of wait is unfortunately implementation
  dependent and someone who is much more well versed in the art
  of MIPS hackery will have to fix it.
  
  So for now, I've #if 0'ed the whole thing and left cpu_idle
  to just do nothing. This fixes all the issues I've been seeing
  and allows me to acheive 230mbit UDP RX/TX and 150mbit
  TCP TX/RX without TX/RX queue servicing issues. Whatever is left
  is due to ath interrupt handling and packet scheduling.

Modified:
  user/adrian/if_ath_tx/sys/mips/mips/machdep.c

Modified: user/adrian/if_ath_tx/sys/mips/mips/machdep.c
==============================================================================
--- user/adrian/if_ath_tx/sys/mips/mips/machdep.c	Thu Sep 29 02:57:08 2011	(r225859)
+++ user/adrian/if_ath_tx/sys/mips/mips/machdep.c	Thu Sep 29 03:10:42 2011	(r225860)
@@ -488,20 +488,33 @@ spinlock_exit(void)
 void
 cpu_idle(int busy)
 {
+#if 0
+	register_t m;
+
 	KASSERT((mips_rd_status() & MIPS_SR_INT_IE) != 0,
 		("interrupts disabled in idle process."));
 	KASSERT((mips_rd_status() & MIPS_INT_MASK) != 0,
 		("all interrupts masked in idle process."));
 
+	m = intr_disable();
 	if (!busy) {
 		critical_enter();
 		cpu_idleclock();
 	}
-	__asm __volatile ("wait");
+
+	if (sched_runnable())
+		intr_restore(m);
+	else {
+		/* XXX not atomic! */
+		intr_restore(m);
+		__asm __volatile ("wait\n");
+	}
+
 	if (!busy) {
 		cpu_activeclock();
 		critical_exit();
 	}
+#endif
 }
 
 int



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