Skip to content

Commit ad9bb7a

Browse files
committed
hv_exc: Improve multi-core scalability
The HV tick polling now only runs on CPU#0. All CPUs have the 1000Hz HV tick, but secondaries only use it to poll the FIQ state and that path does not take the BHL if no other FIQ was pending. Signed-off-by: Hector Martin <marcan@marcan.st>
1 parent 3020e26 commit ad9bb7a

File tree

3 files changed

+45
-16
lines changed

3 files changed

+45
-16
lines changed

src/hv.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,12 @@ void hv_rearm(void)
307307
msr(CNTP_CTL_EL0, CNTx_CTL_ENABLE);
308308
}
309309

310-
void hv_check_rendezvous(struct exc_info *ctx)
310+
bool hv_want_rendezvous(void)
311+
{
312+
return hv_want_cpu != -1;
313+
}
314+
315+
void hv_do_rendezvous(struct exc_info *ctx)
311316
{
312317
if (hv_want_cpu == smp_id()) {
313318
hv_want_cpu = -1;
@@ -323,12 +328,15 @@ void hv_check_rendezvous(struct exc_info *ctx)
323328
}
324329
}
325330

326-
void hv_tick(struct exc_info *ctx)
331+
void hv_maybe_exit(void)
327332
{
328333
if (hv_should_exit) {
329-
spin_unlock(&bhl);
330334
hv_exit_guest();
331335
}
336+
}
337+
338+
void hv_tick(struct exc_info *ctx)
339+
{
332340
hv_wdt_pet();
333341
iodev_handle_events(uartproxy_iodev);
334342
if (iodev_can_read(uartproxy_iodev)) {

src/hv.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ void hv_rendezvous(void);
9797
void hv_switch_cpu(int cpu);
9898
void hv_arm_tick(void);
9999
void hv_rearm(void);
100-
void hv_check_rendezvous(struct exc_info *ctx);
100+
bool hv_want_rendezvous(void);
101+
void hv_do_rendezvous(struct exc_info *ctx);
102+
void hv_maybe_exit(void);
101103
void hv_tick(struct exc_info *ctx);
102104

103105
#endif

src/hv_exc.c

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@ extern spinlock_t bhl;
1919
((op2) << ESR_ISS_MSR_OP2_SHIFT))
2020
#define SYSREG_ISS(...) _SYSREG_ISS(__VA_ARGS__)
2121

22-
#define D_PERCPU(t, x) t x[MAX_CPUS]
23-
#define PERCPU(x) x[mrs(TPIDR_EL2)]
22+
#define PERCPU(x) pcpu[mrs(TPIDR_EL2)].x
2423

25-
D_PERCPU(static bool, ipi_queued);
26-
D_PERCPU(static bool, ipi_pending);
27-
D_PERCPU(static bool, pmc_pending);
28-
D_PERCPU(static u64, pmc_irq_mode);
24+
struct hv_pcpu_data {
25+
u32 ipi_queued;
26+
u32 ipi_pending;
27+
u32 pmc_pending;
28+
u64 pmc_irq_mode;
29+
u64 exc_entry_pmcr0_cnt;
30+
} ALIGNED(64);
2931

30-
D_PERCPU(static u64, exc_entry_pmcr0_cnt);
32+
struct hv_pcpu_data pcpu[MAX_CPUS];
3133

3234
void hv_exit_guest(void) __attribute__((noreturn));
3335

@@ -216,7 +218,7 @@ static bool hv_handle_msr(struct exc_info *ctx, u64 iss)
216218
msr(SYS_IMP_APL_IPI_RR_LOCAL_EL1, regs[rt]);
217219
for (int i = 0; i < MAX_CPUS; i++)
218220
if (mpidr == smp_get_mpidr(i))
219-
ipi_queued[i] = true;
221+
pcpu[i].ipi_queued = true;
220222
return true;
221223
}
222224
case SYSREG_ISS(SYS_IMP_APL_IPI_RR_GLOBAL_EL1):
@@ -225,7 +227,7 @@ static bool hv_handle_msr(struct exc_info *ctx, u64 iss)
225227
msr(SYS_IMP_APL_IPI_RR_GLOBAL_EL1, regs[rt]);
226228
for (int i = 0; i < MAX_CPUS; i++) {
227229
if (mpidr == (smp_get_mpidr(i) & 0xffff))
228-
ipi_queued[i] = true;
230+
pcpu[i].ipi_queued = true;
229231
}
230232
return true;
231233
case SYSREG_ISS(SYS_IMP_APL_IPI_SR_EL1):
@@ -366,10 +368,27 @@ void hv_exc_irq(struct exc_info *ctx)
366368

367369
void hv_exc_fiq(struct exc_info *ctx)
368370
{
369-
hv_wdt_breadcrumb('F');
370-
hv_exc_entry(ctx);
371+
bool tick = false;
372+
373+
hv_maybe_exit();
374+
371375
if (mrs(CNTP_CTL_EL0) == (CNTx_CTL_ISTATUS | CNTx_CTL_ENABLE)) {
372376
msr(CNTP_CTL_EL0, CNTx_CTL_ISTATUS | CNTx_CTL_IMASK | CNTx_CTL_ENABLE);
377+
tick = true;
378+
}
379+
380+
if (mrs(TPIDR_EL2) != 0 && !(mrs(ISR_EL1) & 0x40)) {
381+
// Secondary CPU and it was just a timer tick (or spurious), so just update FIQs
382+
hv_update_fiq();
383+
return;
384+
}
385+
386+
// Slow (single threaded) path
387+
hv_wdt_breadcrumb('F');
388+
hv_exc_entry(ctx);
389+
390+
// Only poll for HV events in CPU 0
391+
if (tick && mrs(TPIDR_EL2) == 0) {
373392
hv_tick(ctx);
374393
hv_arm_tick();
375394
}
@@ -403,7 +422,7 @@ void hv_exc_fiq(struct exc_info *ctx)
403422
msr(SYS_IMP_APL_IPI_SR_EL1, IPI_SR_PENDING);
404423
sysop("isb");
405424
}
406-
hv_check_rendezvous(ctx);
425+
hv_do_rendezvous(ctx);
407426

408427
// Handles guest timers
409428
hv_exc_exit(ctx);

0 commit comments

Comments
 (0)