summaryrefslogtreecommitdiffstats
path: root/sys/sparc64/sparc64/tick.c
blob: 67f22323abe0aa3d82e6b0d058f02b9bc1347886 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/*-
 * Copyright (c) 2001 Jake Burkholder.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/interrupt.h>
#include <sys/pcpu.h>
#include <sys/sysctl.h>
#include <sys/timetc.h>

#include <machine/clock.h>
#include <machine/cpu.h>
#include <machine/frame.h>
#include <machine/intr_machdep.h>
#include <machine/tick.h>
#include <machine/ver.h>

#define	TICK_GRACE	10000

SYSCTL_NODE(_machdep, OID_AUTO, tick, CTLFLAG_RD, 0, "tick statistics");

static int adjust_edges = 0;
SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_edges, CTLFLAG_RD, &adjust_edges,
    0, "total number of times tick interrupts got more than 12.5% behind");

static int adjust_excess = 0;
SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_excess, CTLFLAG_RD, &adjust_excess,
    0, "total number of ignored tick interrupts");

static int adjust_missed = 0;
SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_missed, CTLFLAG_RD, &adjust_missed,
    0, "total number of missed tick interrupts");

static int adjust_ticks = 0;
SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_ticks, CTLFLAG_RD, &adjust_ticks,
    0, "total number of tick interrupts with adjustment");

static void tick_hardclock(struct trapframe *);

static uint64_t
tick_cputicks(void)
{

	return (rd(tick));
}

void
cpu_initclocks(void)
{

	stathz = hz;
	tick_start();
}

static __inline void
tick_process(struct trapframe *tf)
{

	if (PCPU_GET(cpuid) == 0)
		hardclock(TRAPF_USERMODE(tf), TRAPF_PC(tf));
	else
		hardclock_cpu(TRAPF_USERMODE(tf));
	if (profprocs != 0)
		profclock(TRAPF_USERMODE(tf), TRAPF_PC(tf));
	statclock(TRAPF_USERMODE(tf));
}

static void
tick_hardclock(struct trapframe *tf)
{
	u_long adj, s, tick, ref;
	long delta;
	int count;

	/*
	 * The sequence of reading the TICK register, calculating the value
	 * of the next tick and writing it to the TICK_CMPR register must not
	 * be interrupted, not even by an IPI, otherwise a value that is in
	 * the past could be written in the worst case, causing hardclock to
	 * stop.
	 */
	critical_enter();
	adj = PCPU_GET(tickadj);
	s = intr_disable();
	tick = rd(tick);
	wrtickcmpr(tick + tick_increment - adj, 0);
	intr_restore(s);
	ref = PCPU_GET(tickref);
	delta = tick - ref;
	count = 0;
	while (delta >= tick_increment) {
		tick_process(tf);
		delta -= tick_increment;
		ref += tick_increment;
		if (adj != 0)
			adjust_ticks++;
		count++;
	}
	if (count > 0) {
		adjust_missed += count - 1;
		if (delta > (tick_increment >> 3)) {
			if (adj == 0)
				adjust_edges++;
			adj = tick_increment >> 4;
		} else
			adj = 0;
	} else {
		adj = 0;
		adjust_excess++;
	}
	PCPU_SET(tickref, ref);
	PCPU_SET(tickadj, adj);
	critical_exit();
}

void
tick_init(u_long clock)
{

	tick_freq = clock;
	tick_MHz = clock / 1000000;
	tick_increment = clock / hz;

	/*
	 * UltraSparc II[e,i] based systems come up with the tick interrupt
	 * enabled and a handler that resets the tick counter, causing DELAY()
	 * to not work properly when used early in boot.
	 * UltraSPARC III based systems come up with the system tick interrupt
	 * enabled, causing an interrupt storm on startup since they are not
	 * handled.
	 */
	tick_stop();

	set_cputicker(tick_cputicks, tick_freq, 0);
}

void
tick_start(void)
{
	u_long base, s;

	/*
	 * Avoid stopping of hardclock in terms of a lost tick interrupt
	 * by ensuring that the tick period is at least TICK_GRACE ticks.
	 * This check would be better placed in tick_init(), however we
	 * have to call tick_init() before cninit() in order to provide
	 * the low-level console drivers with a working DELAY() which in
	 * turn means we cannot use panic() in tick_init().
	 */
	if (tick_increment < TICK_GRACE)
		panic("%s: HZ too high, decrease to at least %ld", __func__,
		    tick_freq / TICK_GRACE);

	if (PCPU_GET(cpuid) == 0)
		intr_setup(PIL_TICK, tick_hardclock, -1, NULL, NULL);

	/*
	 * Try to make the tick interrupts as synchronously as possible on
	 * all CPUs to avoid inaccuracies for migrating processes. Leave out
	 * one tick to make sure that it is not missed.
	 */
	PCPU_SET(tickadj, 0);
	s = intr_disable();
	base = rd(tick);
	base = roundup(base, tick_increment);
	PCPU_SET(tickref, base);
	wrtickcmpr(base + tick_increment, 0);
	intr_restore(s);
}

void
tick_stop(void)
{

	if (cpu_impl >= CPU_IMPL_ULTRASPARCIII)
		wr(asr25, 1L << 63, 0);
	wrtickcmpr(1L << 63, 0);
}
OpenPOWER on IntegriCloud