Skip to content

Commit 1fa4055

Browse files
James Hoganralfbaechle
James Hogan
authored andcommitted
MIPS: cevt-r4k: Dynamically calculate min_delta_ns
Calculate the MIPS clockevent device's min_delta_ns dynamically based on the time it takes to perform the mips_next_event() sequence. Virtualisation in particular makes the current fixed min_delta of 0x300 inappropriate under some circumstances, as the CP0_Count and CP0_Compare registers may be being emulated by the hypervisor, and the frequency may not correspond directly to the CPU frequency. We actually use twice the median of multiple 75th percentiles of multiple measurements of how long the mips_next_event() sequence takes, in order to fairly efficiently eliminate outliers due to unexpected hypervisor latency (which would need handling with retries when it occurs during normal operation anyway). Signed-off-by: James Hogan <[email protected]> Cc: Daniel Lezcano <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: [email protected] Cc: [email protected] Patchwork: https://patchwork.linux-mips.org/patch/13176/ Signed-off-by: Ralf Baechle <[email protected]>
1 parent 24e1df6 commit 1fa4055

File tree

1 file changed

+80
-2
lines changed

1 file changed

+80
-2
lines changed

arch/mips/kernel/cevt-r4k.c

+80-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,83 @@ static int mips_next_event(unsigned long delta,
2828
return res;
2929
}
3030

31+
/**
32+
* calculate_min_delta() - Calculate a good minimum delta for mips_next_event().
33+
*
34+
* Running under virtualisation can introduce overhead into mips_next_event() in
35+
* the form of hypervisor emulation of CP0_Count/CP0_Compare registers,
36+
* potentially with an unnatural frequency, which makes a fixed min_delta_ns
37+
* value inappropriate as it may be too small.
38+
*
39+
* It can also introduce occasional latency from the guest being descheduled.
40+
*
41+
* This function calculates a good minimum delta based roughly on the 75th
42+
* percentile of the time taken to do the mips_next_event() sequence, in order
43+
* to handle potentially higher overhead while also eliminating outliers due to
44+
* unpredictable hypervisor latency (which can be handled by retries).
45+
*
46+
* Return: An appropriate minimum delta for the clock event device.
47+
*/
48+
static unsigned int calculate_min_delta(void)
49+
{
50+
unsigned int cnt, i, j, k, l;
51+
unsigned int buf1[4], buf2[3];
52+
unsigned int min_delta;
53+
54+
/*
55+
* Calculate the median of 5 75th percentiles of 5 samples of how long
56+
* it takes to set CP0_Compare = CP0_Count + delta.
57+
*/
58+
for (i = 0; i < 5; ++i) {
59+
for (j = 0; j < 5; ++j) {
60+
/*
61+
* This is like the code in mips_next_event(), and
62+
* directly measures the borderline "safe" delta.
63+
*/
64+
cnt = read_c0_count();
65+
write_c0_compare(cnt);
66+
cnt = read_c0_count() - cnt;
67+
68+
/* Sorted insert into buf1 */
69+
for (k = 0; k < j; ++k) {
70+
if (cnt < buf1[k]) {
71+
l = min_t(unsigned int,
72+
j, ARRAY_SIZE(buf1) - 1);
73+
for (; l > k; --l)
74+
buf1[l] = buf1[l - 1];
75+
break;
76+
}
77+
}
78+
if (k < ARRAY_SIZE(buf1))
79+
buf1[k] = cnt;
80+
}
81+
82+
/* Sorted insert of 75th percentile into buf2 */
83+
for (k = 0; k < i; ++k) {
84+
if (buf1[ARRAY_SIZE(buf1) - 1] < buf2[k]) {
85+
l = min_t(unsigned int,
86+
i, ARRAY_SIZE(buf2) - 1);
87+
for (; l > k; --l)
88+
buf2[l] = buf2[l - 1];
89+
break;
90+
}
91+
}
92+
if (k < ARRAY_SIZE(buf2))
93+
buf2[k] = buf1[ARRAY_SIZE(buf1) - 1];
94+
}
95+
96+
/* Use 2 * median of 75th percentiles */
97+
min_delta = buf2[ARRAY_SIZE(buf2) - 1] * 2;
98+
99+
/* Don't go too low */
100+
if (min_delta < 0x300)
101+
min_delta = 0x300;
102+
103+
pr_debug("%s: median 75th percentile=%#x, min_delta=%#x\n",
104+
__func__, buf2[ARRAY_SIZE(buf2) - 1], min_delta);
105+
return min_delta;
106+
}
107+
31108
DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
32109
int cp0_timer_irq_installed;
33110

@@ -177,7 +254,7 @@ int r4k_clockevent_init(void)
177254
{
178255
unsigned int cpu = smp_processor_id();
179256
struct clock_event_device *cd;
180-
unsigned int irq;
257+
unsigned int irq, min_delta;
181258

182259
if (!cpu_has_counter || !mips_hpt_frequency)
183260
return -ENXIO;
@@ -203,7 +280,8 @@ int r4k_clockevent_init(void)
203280

204281
/* Calculate the min / max delta */
205282
cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
206-
cd->min_delta_ns = clockevent_delta2ns(0x300, cd);
283+
min_delta = calculate_min_delta();
284+
cd->min_delta_ns = clockevent_delta2ns(min_delta, cd);
207285

208286
cd->rating = 300;
209287
cd->irq = irq;

0 commit comments

Comments
 (0)