Further work-out the handling of the high FP registers. The most

important change is in cpu_switch() where we disable the high FP
registers for the thread that we switch-out if the CPU currently
has its high FP registers. This avoids that the high FP registers
remain enabled for the thread even when the CPU has unloaded them
or the thread migrated to another processor.
Likewise, when we switch-in a thread of that has its high FP
registers on the CPU, we enable them. This avoids an otherwise
harmless, but unnecessary trap to have them enabled.

The code that handles the disabled high FP trap (in trap()) has
been turned into a critical section for the most part to avoid
being preempted. If there's a race, we bail out and have the
processor trap again if necessary.

Avoid using the generic ia64_highfp_save() function when the
context is predictable. The function adds unnecessary overhead.
Don't use ia64_highfp_load() for the same reason. The function
is now unused and can be removed.

These changes make the lazy context switching of the high FP
registers in an UP kernel functional.
This commit is contained in:
marcel 2003-11-12 01:26:02 +00:00
parent dfaa7af61b
commit 19c69bbd49
3 changed files with 50 additions and 69 deletions

View File

@ -200,8 +200,12 @@ interrupt(u_int64_t vector, struct trapframe *framep)
asts[PCPU_GET(cpuid)]++;
CTR1(KTR_SMP, "IPI_AST, cpuid=%d", PCPU_GET(cpuid));
} else if (vector == ipi_vector[IPI_HIGH_FP]) {
if (PCPU_GET(fpcurthread) != NULL)
ia64_highfp_save(PCPU_GET(fpcurthread));
struct thread *thr = PCPU_GET(fpcurthread);
if (thr != NULL) {
save_high_fp(&thr->td_pcb->pcb_high_fp);
thr->td_pcb->pcb_fpcpu = NULL;
PCPU_SET(fpcurthread, NULL);
}
} else if (vector == ipi_vector[IPI_RENDEZVOUS]) {
rdvs[PCPU_GET(cpuid)]++;
CTR1(KTR_SMP, "IPI_RENDEZVOUS, cpuid=%d", PCPU_GET(cpuid));

View File

@ -311,6 +311,8 @@ cpu_switch(struct thread *old, struct thread *new)
#if IA32
ia32_savectx(oldpcb);
#endif
if (PCPU_GET(fpcurthread) == old)
old->td_frame->tf_special.psr |= IA64_PSR_DFH;
if (!savectx(oldpcb)) {
newpcb = new->td_pcb;
oldpcb->pcb_current_pmap =
@ -319,6 +321,8 @@ cpu_switch(struct thread *old, struct thread *new)
#if IA32
ia32_restorectx(newpcb);
#endif
if (PCPU_GET(fpcurthread) == new)
new->td_frame->tf_special.psr &= ~IA64_PSR_DFH;
restorectx(newpcb);
/* We should not get here. */
panic("cpu_switch: restorectx() returned");

View File

@ -570,81 +570,54 @@ trap(int vector, struct trapframe *framep)
if (!user)
trap_panic(vector, framep);
critical_enter();
thr = PCPU_GET(fpcurthread);
if (thr == td) {
/*
* Short-circuit handling the trap when this CPU
* already holds the high FP registers for this
* thread. We really shouldn't get the trap in the
* first place, but since it's only a performance
* issue and not a correctness issue, we emit a
* message for now, enable the high FP registers and
* return.
*/
printf("XXX: bogusly disabled high FP regs\n");
framep->tf_special.psr &= ~IA64_PSR_DFH;
critical_exit();
goto out;
} else if (thr != NULL) {
pcb = thr->td_pcb;
save_high_fp(&pcb->pcb_high_fp);
pcb->pcb_fpcpu = NULL;
PCPU_SET(fpcurthread, NULL);
thr = NULL;
}
pcb = td->td_pcb;
pcpu = pcb->pcb_fpcpu;
#if 0
printf("XXX: td %p: highfp on cpu %p\n", td, pcpu);
#endif
/*
* The pcpu variable holds the address of the per-CPU
* structure of the CPU currently holding this threads
* high FP registers (or NULL if no CPU holds these
* registers). We have to interrupt that CPU and wait
* for it to have saved the registers.
*/
if (pcpu != NULL) {
thr = pcpu->pc_fpcurthread;
KASSERT(thr == td, ("High FP state out of sync"));
if (pcpu == pcpup) {
/*
* Short-circuit handling the trap when this
* CPU already holds the high FP registers for
* this thread. We really shouldn't get the
* trap in the first place, but since it's
* only a performance issue and not a
* correctness issue, we emit a message for
* now, enable the high FP registers and
* return.
*/
printf("XXX: bogusly disabled high FP regs\n");
framep->tf_special.psr &= ~IA64_PSR_DFH;
goto out;
}
#ifdef SMP
/*
* Interrupt the other CPU so that it saves the high
* FP registers of this thread. Note that this can
* only happen for the SMP case.
*/
if (pcpu != NULL) {
ipi_send(pcpu->pc_lid, IPI_HIGH_FP);
critical_exit();
while (pcb->pcb_fpcpu != pcpu)
DELAY(100);
critical_enter();
pcpu = pcb->pcb_fpcpu;
thr = PCPU_GET(fpcurthread);
}
#endif
#ifdef DIAGNOSTICS
} else {
KASSERT(PCPU_GET(fpcurthread) != td,
("High FP state out of sync"));
#endif
if (thr == NULL && pcpu == NULL) {
restore_high_fp(&pcb->pcb_high_fp);
PCPU_SET(fpcurthread, td);
pcb->pcb_fpcpu = pcpup;
framep->tf_special.psr &= ~IA64_PSR_MFH;
framep->tf_special.psr &= ~IA64_PSR_DFH;
}
thr = PCPU_GET(fpcurthread);
#if 0
printf("XXX: cpu %p: highfp belongs to td %p\n", pcpup, thr);
#endif
/*
* The thr variable holds the thread that owns the high FP
* registers currently on this CPU. Free this CPU so that
* we can load the current threads high FP registers.
*/
if (thr != NULL) {
KASSERT(thr != td, ("High FP state out of sync"));
pcb = thr->td_pcb;
KASSERT(pcb->pcb_fpcpu == pcpup,
("High FP state out of sync"));
ia64_highfp_save(thr);
}
/*
* Wait for the other CPU to have saved out high FP
* registers (if applicable).
*/
while (pcpu && pcpu->pc_fpcurthread == td);
ia64_highfp_load(td);
framep->tf_special.psr &= ~IA64_PSR_DFH;
critical_exit();
goto out;
}