[RFC] Event timers on MIPS

Neel Natu neelnatu at gmail.com
Sat Jul 31 00:47:58 UTC 2010


Hi,

On Tue, Jul 27, 2010 at 7:43 AM, Jayachandran C.
<c.jayachandran at gmail.com> wrote:
> On Tue, Jul 27, 2010 at 6:04 PM, Alexander Motin <mav at freebsd.org> wrote:
>> Jayachandran C. wrote:
>>> On Sun, Jul 18, 2010 at 1:04 AM, Alexander Motin <mav at freebsd.org> wrote:
>>>> Jayachandran C. wrote:
>>>>> On XLR we would like to use the count/compare which is faster but less
>>>>> accurate on all cpus - we can have upto 32 cpus now.  We also have a
>>>>> PIC which can provide a better timestamp and timer interrupts.  This
>>>>> PIC timestamp can be read from all CPUs but the timer interrupt can be
>>>>> delivered to just one CPU at a time.  I think this is how we ended up
>>>>> with the current implementation, but any suggestions on how to improve
>>>>> this is welcome.
>>>
>>> As a first step, I have copied the count /compare code from mips with
>>> minor modifications into mips/rmi, this lets me boot up (checked in as
>>> r210528).
>>>
>>> I would like to add the PIC based clock next.
>>
>> Thanks.
>>
>>>> I would prefer to not mix the things.
>>>>
>>>> I think:
>>>>  - PIC timestamp looks like the best candidate for system timecounter.
>>>>  - per-CPU counters could be registered as per-CPU timecounters with
>>>> set_cputicker() - the main criteria there is a speed.
>>>>  - if per-CPU counters are synchronized between CPUs - they could be
>>>> registered as alternative timecounter for people who wish fastest
>>>> timecounting; if they are not - they are useless in that role.
>>>>  - both PIC timer and per-CPU comparators should be independently
>>>> registered as eventtimers - it is better to have two of them to from
>>>> accounting correctness PoV, and it will allow user to experiment which
>>>> one he likes more.
>>>>  - if there is any other timer hardware - it also should be registered -
>>>> it will give additional flexibility.
>>>
>>> The per-cpu count/compare counters are not synchronized on XLR.
>>
>> Then tick_ticker() function looks broken. counter_lower_last and
>> counter_upper should be tracked per-CPU. Otherwise you will have huge
>> forward jumps due to false overflows.
>>
>>> So your suggestion would be to add a PIC based clock which calls
>>> tc_init() and et_register(), and to leave the set_cputicker() to be
>>> the count/compare?
>>
>> Yes. And I would leave count/compare also calling tc_init() and
>> et_register() as it is now. It won't hurt.
>>
>>> Also, with just the count/compare, I get these print on early mutiuser bootup.
>>> ---
>>> calcru: runtime went backwards from 85936878 usec to 236488 usec for
>>> pid 1286 (rpcbind)
>>> calcru: runtime went backwards from 7158742 usec to 19700 usec for pid
>>> 1285 (nfsiod 0)
>>> calcru: runtime went backwards from 111005442 usec to 305474 usec for
>>> pid 1257 (syslogd)
>>> calcru: runtime went backwards from 10740196 usec to 29555 usec for
>>> pid 1048 (devd)
>>> --
>>> Did not get much time to investigate, any idea what the cause  can be?
>>
>> I think it can easily be result of broken tick_ticker().
>
> I'm planning to check-in the attached patch for mips/rmi, I think
> mips/mips would need something similar.
>

Here is the patch for mips/mips/tick.c to fix tick_ticker().

In addition to incorporating the changes made in rmi/tick.c it fixes
the following:

- There is a race between clock_intr() and tick_ticker() updating
counter_upper and counter_lower_last. This race exists because
interrupts are enabled even though tick_ticker() executes in a
critical section.

- Fix a bug in clock_intr() in how it updates counter_upper and
counter_lower_last. It updates it only once every time the COUNT
register wraps around. More interestingly it will *never* update the
cached values of 'counter_upper' and 'counter_lower_last' if the
previous value of 'counter_lower_last' happens to be '0'.

- Get rid of the superfluous critical section in clock_intr(). There
is no reason for it because clock_intr() executes in hard interrupt
context.

Comments?

best
Neel

Index: sys/mips/sibyte/sb_machdep.c
===================================================================
--- sys/mips/sibyte/sb_machdep.c	(revision 210664)
+++ sys/mips/sibyte/sb_machdep.c	(working copy)
@@ -454,6 +454,4 @@
 	mips_init();

 	mips_timer_init_params(sb_cpu_speed(), 0);
-
-	set_cputicker(sb_zbbus_cycle_count, sb_cpu_speed() / 2, 1);
 }
Index: sys/mips/mips/tick.c
===================================================================
--- sys/mips/mips/tick.c	(revision 210664)
+++ sys/mips/mips/tick.c	(working copy)
@@ -60,9 +60,8 @@
 static DPCPU_DEFINE(uint32_t, cycles_per_tick);
 static uint32_t cycles_per_usec;

-static u_int32_t counter_upper = 0;
-static u_int32_t counter_lower_last = 0;
-
+static DPCPU_DEFINE(uint32_t, counter_upper);
+static DPCPU_DEFINE(uint32_t, counter_lower_last);
 static DPCPU_DEFINE(uint32_t, compare_ticks);
 static DPCPU_DEFINE(uint32_t, lost_ticks);

@@ -104,21 +103,32 @@
 {
 	uint64_t ret;
 	uint32_t ticktock;
+	uint32_t t_lower_last, t_upper;

 	/*
-	 * XXX: MIPS64 platforms can read 64-bits of counter directly.
-	 * Also: the tc code is supposed to cope with things wrapping
-	 * from the time counter, so I'm not sure why all these hoops
-	 * are even necessary.
+	 * Disable preemption because we are working with cpu specific data.
 	 */
+	critical_enter();
+
+	/*
+	 * Note that even though preemption is disabled, interrupts are
+	 * still enabled. In particular there is a race with clock_intr()
+	 * reading the values of 'counter_upper' and 'counter_lower_last'.
+	 */
+	do {
+		t_upper = DPCPU_GET(counter_upper);
+		t_lower_last = DPCPU_GET(counter_lower_last);
+	} while (t_upper != DPCPU_GET(counter_upper));
+
 	ticktock = mips_rd_count();
-	critical_enter();
-	if (ticktock < counter_lower_last)
-		counter_upper++;
-	counter_lower_last = ticktock;
+
 	critical_exit();

-	ret = ((uint64_t) counter_upper << 32) | counter_lower_last;
+	/* COUNT register wrapped around */
+	if (ticktock < t_lower_last)
+		t_upper++;
+
+	ret = ((uint64_t)t_upper << 32) | ticktock;
 	return (ret);
 }

@@ -262,11 +272,11 @@
 	} else	/* In one-shot mode timer should be stopped after the event. */
 		mips_wr_compare(0xffffffff);

-	critical_enter();
-	if (count < counter_lower_last) {
-		counter_upper++;
-		counter_lower_last = count;
+	/* COUNT register wrapped around */
+	if (count < DPCPU_GET(counter_lower_last)) {
+		DPCPU_SET(counter_upper, DPCPU_GET(counter_upper) + 1);
 	}
+	DPCPU_SET(counter_lower_last, count);

 	if (cycles_per_tick > 0) {

@@ -296,7 +306,6 @@
 	}
 	if (sc->et.et_active)
 		sc->et.et_event_cb(&sc->et, sc->et.et_arg);
-	critical_exit();
 	return (FILTER_HANDLED);
 }


> JC.
>


More information about the freebsd-mips mailing list