Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 5 May 2011 18:56:48 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r221508 - head/sys/x86/x86
Message-ID:  <201105051856.p45IumkV011824@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Thu May  5 18:56:48 2011
New Revision: 221508
URL: http://svn.freebsd.org/changeset/base/221508

Log:
  Some changes around LAPIC timer programming.
  
  This fixes heavy interrupt storm and resulting system freeze when using
  LAPIC timer in one-shot mode under Xen HVM. There, unlike real hardware,
  programming timer with zero period almost immediately causes interrupt.

Modified:
  head/sys/x86/x86/local_apic.c

Modified: head/sys/x86/x86/local_apic.c
==============================================================================
--- head/sys/x86/x86/local_apic.c	Thu May  5 18:50:52 2011	(r221507)
+++ head/sys/x86/x86/local_apic.c	Thu May  5 18:56:48 2011	(r221508)
@@ -159,9 +159,8 @@ static struct eventtimer lapic_et;
 
 static void	lapic_enable(void);
 static void	lapic_resume(struct pic *pic);
-static void	lapic_timer_enable_intr(void);
-static void	lapic_timer_oneshot(u_int count);
-static void	lapic_timer_periodic(u_int count);
+static void	lapic_timer_oneshot(u_int count, int enable_int);
+static void	lapic_timer_periodic(u_int count, int enable_int);
 static void	lapic_timer_stop(void);
 static void	lapic_timer_set_divisor(u_int divisor);
 static uint32_t	lvt_mode(struct lapic *la, u_int pin, uint32_t value);
@@ -379,13 +378,11 @@ lapic_setup(int boot)
 	if (la->la_timer_mode != 0) {
 		KASSERT(la->la_timer_period != 0, ("lapic%u: zero divisor",
 		    lapic_id()));
-		lapic_timer_stop();
 		lapic_timer_set_divisor(lapic_timer_divisor);
-		lapic_timer_enable_intr();
 		if (la->la_timer_mode == 1)
-			lapic_timer_periodic(la->la_timer_period);
+			lapic_timer_periodic(la->la_timer_period, 1);
 		else
-			lapic_timer_oneshot(la->la_timer_period);
+			lapic_timer_oneshot(la->la_timer_period, 1);
 	}
 
 	/* Program error LVT and clear any existing errors. */
@@ -496,7 +493,7 @@ lapic_et_start(struct eventtimer *et,
 		/* Try to calibrate the local APIC timer. */
 		do {
 			lapic_timer_set_divisor(lapic_timer_divisor);
-			lapic_timer_oneshot(APIC_TIMER_MAX_COUNT);
+			lapic_timer_oneshot(APIC_TIMER_MAX_COUNT, 0);
 			DELAY(1000000);
 			value = APIC_TIMER_MAX_COUNT - lapic->ccr_timer;
 			if (value != APIC_TIMER_MAX_COUNT)
@@ -516,9 +513,7 @@ lapic_et_start(struct eventtimer *et,
 		et->et_max_period.frac =
 		    ((0xfffffffeLLU << 32) / et->et_frequency) << 32;
 	}
-	lapic_timer_stop();
 	lapic_timer_set_divisor(lapic_timer_divisor);
-	lapic_timer_enable_intr();
 	la = &lapics[lapic_id()];
 	if (period != NULL) {
 		la->la_timer_mode = 1;
@@ -526,14 +521,14 @@ lapic_et_start(struct eventtimer *et,
 		    (et->et_frequency * (period->frac >> 32)) >> 32;
 		if (period->sec != 0)
 			la->la_timer_period += et->et_frequency * period->sec;
-		lapic_timer_periodic(la->la_timer_period);
+		lapic_timer_periodic(la->la_timer_period, 1);
 	} else {
 		la->la_timer_mode = 2;
 		la->la_timer_period =
 		    (et->et_frequency * (first->frac >> 32)) >> 32;
 		if (first->sec != 0)
 			la->la_timer_period += et->et_frequency * first->sec;
-		lapic_timer_oneshot(la->la_timer_period);
+		lapic_timer_oneshot(la->la_timer_period, 1);
 	}
 	return (0);
 }
@@ -838,25 +833,29 @@ lapic_timer_set_divisor(u_int divisor)
 }
 
 static void
-lapic_timer_oneshot(u_int count)
+lapic_timer_oneshot(u_int count, int enable_int)
 {
 	u_int32_t value;
 
 	value = lapic->lvt_timer;
 	value &= ~APIC_LVTT_TM;
 	value |= APIC_LVTT_TM_ONE_SHOT;
+	if (enable_int)
+		value &= ~APIC_LVT_M;
 	lapic->lvt_timer = value;
 	lapic->icr_timer = count;
 }
 
 static void
-lapic_timer_periodic(u_int count)
+lapic_timer_periodic(u_int count, int enable_int)
 {
 	u_int32_t value;
 
 	value = lapic->lvt_timer;
 	value &= ~APIC_LVTT_TM;
 	value |= APIC_LVTT_TM_PERIODIC;
+	if (enable_int)
+		value &= ~APIC_LVT_M;
 	lapic->lvt_timer = value;
 	lapic->icr_timer = count;
 }
@@ -870,17 +869,6 @@ lapic_timer_stop(void)
 	value &= ~APIC_LVTT_TM;
 	value |= APIC_LVT_M;
 	lapic->lvt_timer = value;
-	lapic->icr_timer = 0;
-}
-
-static void
-lapic_timer_enable_intr(void)
-{
-	u_int32_t value;
-
-	value = lapic->lvt_timer;
-	value &= ~APIC_LVT_M;
-	lapic->lvt_timer = value;
 }
 
 void



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