summaryrefslogtreecommitdiffstats
path: root/sys/sparc64
diff options
context:
space:
mode:
authormarius <marius@FreeBSD.org>2006-03-29 00:14:08 +0000
committermarius <marius@FreeBSD.org>2006-03-29 00:14:08 +0000
commitea86aa372663046ef03bd6c84de06e036144cb35 (patch)
tree0c7ba4be3788d81849fc46a81ff80f81e1dc34fa /sys/sparc64
parent98a829e8eac9d84c6225039ca00f1e21fa520426 (diff)
downloadFreeBSD-src-ea86aa372663046ef03bd6c84de06e036144cb35.zip
FreeBSD-src-ea86aa372663046ef03bd6c84de06e036144cb35.tar.gz
- We only lock the local per-CPU page in the local dTLB, so accessing the
foreign per-CPU pages in cpu_ipi_send() in order to get the module IDs of the other CPUs can cause a page fault. If this happens when doing a TLB shootdown while dealing with another page fault this causes a panic due to the recursive page fault. As I don't spot other code that assumes or requires that accessing foreign per-CPU pages must not page fault solve this by adding a statically allocated (and therefore locked in the kernel pages) array which establishes a FreeBSD CPU ID -> module ID relation and use that in cpu_ipi_selected() (instead of statically allocating the per-CPU pages which would just waste memory on say a dual CPU machine as sun4u theoretically supports up to 128 CPUs or wasting dTLB slots for the foreign per-CPU pages). [1] - Fix a potential race in cpu_ipi_send(); as we don't serialize the access to cpu_ipi_selected() between MI and MD use (only MI-MI and MD-MD) we might catch the NACK bit caused by sending another IPI. Solve this by checking the NACK bit in the contents of the interrupt dispatch status reg read while interrupts were still turned off instead of reading that reg anew after interrupts were turned on again. This is also what the CPU docs suggest to do. - Add a workaround for the SpitFire erratum #54 bug (affecting interrupt dispatch). While public info regarding what this CPU bug actually causes is not available testing shows that with the workaround in place it's less likely to get a "couldn't send ipi" panic, it doesn't solve these panics entirely though. [2] Reported by: kris [1] Some clue from: kmacy [1] Info from: Linux, OpenSolaris [2] Additional testing by: kris MFC after: 3 days
Diffstat (limited to 'sys/sparc64')
-rw-r--r--sys/sparc64/sparc64/mp_machdep.c22
1 files changed, 17 insertions, 5 deletions
diff --git a/sys/sparc64/sparc64/mp_machdep.c b/sys/sparc64/sparc64/mp_machdep.c
index e4e8280..cb13663 100644
--- a/sys/sparc64/sparc64/mp_machdep.c
+++ b/sys/sparc64/sparc64/mp_machdep.c
@@ -108,6 +108,7 @@ vm_offset_t mp_tramp;
u_int mp_boot_mid;
+static u_int cpuid_to_mid[MAXCPU];
static volatile u_int shutdown_cpus;
void cpu_mp_unleash(void *);
@@ -238,6 +239,8 @@ cpu_mp_start(void)
-1, NULL, NULL);
intr_setup(PIL_STOP, cpu_ipi_stop, -1, NULL, NULL);
+ cpuid_to_mid[PCPU_GET(cpuid)] = mp_boot_mid;
+
root = OF_peer(0);
csa = &cpu_start_args;
for (child = OF_child(root); child != 0; child = OF_peer(child)) {
@@ -266,6 +269,7 @@ cpu_mp_start(void)
intr_restore(s);
cpuid = mp_ncpus++;
+ cpuid_to_mid[cpuid] = mid;
cpu_identify(csa->csa_ver, clock, cpuid);
va = kmem_alloc(kernel_map, PCPU_PAGES * PAGE_SIZE);
@@ -413,20 +417,19 @@ cpu_ipi_stop(struct trapframe *tf)
void
cpu_ipi_selected(u_int cpus, u_long d0, u_long d1, u_long d2)
{
- struct pcpu *pc;
u_int cpu;
while (cpus) {
cpu = ffs(cpus) - 1;
cpus &= ~(1 << cpu);
- pc = pcpu_find(cpu);
- cpu_ipi_send(pc->pc_mid, d0, d1, d2);
+ cpu_ipi_send(cpuid_to_mid[cpu], d0, d1, d2);
}
}
void
cpu_ipi_send(u_int mid, u_long d0, u_long d1, u_long d2)
{
+ u_long ids;
u_long s;
int i;
@@ -438,11 +441,20 @@ cpu_ipi_send(u_int mid, u_long d0, u_long d1, u_long d2)
stxa(AA_SDB_INTR_D1, ASI_SDB_INTR_W, d1);
stxa(AA_SDB_INTR_D2, ASI_SDB_INTR_W, d2);
stxa(AA_INTR_SEND | (mid << 14), ASI_SDB_INTR_W, 0);
+ /*
+ * Workaround for SpitFire erratum #54; do a dummy read
+ * from a SDB internal register before the MEMBAR #Sync
+ * for the write to ASI_SDB_INTR_W (requiring another
+ * MEMBAR #Sync in order to make sure the write has
+ * occurred before the load).
+ */
+ membar(Sync);
+ (void)ldxa(AA_SDB_CNTL_HIGH, ASI_SDB_CONTROL_R);
membar(Sync);
- while (ldxa(0, ASI_INTR_DISPATCH_STATUS) & IDR_BUSY)
+ while ((ids = ldxa(0, ASI_INTR_DISPATCH_STATUS)) & IDR_BUSY)
;
intr_restore(s);
- if ((ldxa(0, ASI_INTR_DISPATCH_STATUS) & IDR_NACK) == 0)
+ if ((ids & IDR_NACK) == 0)
return;
}
if (
OpenPOWER on IntegriCloud