/* Indicates to BIOS that we want to use the newer SMM NMI handler */ staticunsignedlong uvh_nmi_mmrx_req; /* UVH_BIOS_KERNEL_MMR_ALIAS_2 */ staticint uvh_nmi_mmrx_req_shift; /* 62 */
new_nmi_method_only = true; /* Newer nmi always valid on UV5+ */
uvh_nmi_mmrx_req = 0; /* no request bit to clear */
} else {
pr_err("UV:%s:NMI support not available on this system\n", __func__); return;
}
/* Then find out if new NMI is supported */ if (new_nmi_method_only || uv_read_local_mmr(uvh_nmi_mmrx_supported)) { if (uvh_nmi_mmrx_req)
uv_write_local_mmr(uvh_nmi_mmrx_req, 1UL << uvh_nmi_mmrx_req_shift);
nmi_mmr = uvh_nmi_mmrx;
nmi_mmr_clear = uvh_nmi_mmrx_clear;
nmi_mmr_pending = 1UL << uvh_nmi_mmrx_shift;
pr_info("UV: SMI NMI support: %s\n", uvh_nmi_mmrx_type);
} else {
nmi_mmr = UVH_NMI_MMR;
nmi_mmr_clear = UVH_NMI_MMR_CLEAR;
nmi_mmr_pending = 1UL << UVH_NMI_MMR_SHIFT;
pr_info("UV: SMI NMI support: %s\n", UVH_NMI_MMR_TYPE);
}
}
/* Read NMI MMR and check if NMI flag was set by BMC. */ staticinlineint uv_nmi_test_mmr(struct uv_hub_nmi_s *hub_nmi)
{
hub_nmi->nmi_value = uv_read_local_mmr(nmi_mmr);
atomic_inc(&hub_nmi->read_mmr_count); return !!(hub_nmi->nmi_value & nmi_mmr_pending);
}
/* Check if this is a system NMI event */ staticint uv_check_nmi(struct uv_hub_nmi_s *hub_nmi)
{ int cpu = smp_processor_id(); int nmi = 0; int nmi_detected = 0;
/* Loop waiting as CPU's enter NMI handler */ staticint uv_nmi_wait_cpus(int first)
{ int i, j, k, n = num_online_cpus(); int last_k = 0, waiting = 0; int cpu = smp_processor_id();
if (first) {
cpumask_copy(uv_nmi_cpu_mask, cpu_online_mask);
k = 0;
} else {
k = n - cpumask_weight(uv_nmi_cpu_mask);
}
/* PCH NMI causes only one CPU to respond */ if (first && uv_pch_intr_now_enabled) {
cpumask_clear_cpu(cpu, uv_nmi_cpu_mask); return n - k - 1;
}
udelay(uv_nmi_initial_delay); for (i = 0; i < uv_nmi_retry_count; i++) { int loop_delay = uv_nmi_loop_delay;
for_each_cpu(j, uv_nmi_cpu_mask) { if (uv_cpu_nmi_per(j).state) {
cpumask_clear_cpu(j, uv_nmi_cpu_mask); if (++k >= n) break;
}
} if (k >= n) { /* all in? */
k = n; break;
} if (last_k != k) { /* abort if no new CPU's coming in */
last_k = k;
waiting = 0;
} elseif (++waiting > uv_nmi_wait_count) break;
/* Extend delay if waiting only for CPU 0: */ if (waiting && (n - k) == 1 &&
cpumask_test_cpu(0, uv_nmi_cpu_mask))
loop_delay *= 100;
udelay(loop_delay);
}
atomic_set(&uv_nmi_cpus_in_nmi, k); return n - k;
}
/* Wait until all slave CPU's have entered UV NMI handler */ staticvoid uv_nmi_wait(int master)
{ /* Indicate this CPU is in: */
this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_IN);
/* If not the first CPU in (the master), then we are a slave CPU */ if (!master) return;
do { /* Wait for all other CPU's to gather here */ if (!uv_nmi_wait_cpus(1)) break;
/* If not all made it in, send IPI NMI to them */
pr_alert("UV: Sending NMI IPI to %d CPUs: %*pbl\n",
cpumask_weight(uv_nmi_cpu_mask),
cpumask_pr_args(uv_nmi_cpu_mask));
uv_nmi_nr_cpus_ping();
/* If all CPU's are in, then done */ if (!uv_nmi_wait_cpus(0)) break;
pr_alert("UV: %d CPUs not in NMI loop: %*pbl\n",
cpumask_weight(uv_nmi_cpu_mask),
cpumask_pr_args(uv_nmi_cpu_mask));
} while (0);
pr_alert("UV: %d of %d CPUs in NMI\n",
atomic_read(&uv_nmi_cpus_in_nmi), num_online_cpus());
}
/* Trigger a slave CPU to dump its state */ staticvoid uv_nmi_trigger_dump(int cpu)
{ int retry = uv_nmi_trigger_delay;
if (uv_cpu_nmi_per(cpu).state != UV_NMI_STATE_IN) return;
uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_DUMP; do {
cpu_relax();
udelay(10); if (uv_cpu_nmi_per(cpu).state
!= UV_NMI_STATE_DUMP) return;
} while (--retry > 0);
pr_crit("UV: CPU %d stuck in process dump function\n", cpu);
uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_DUMP_DONE;
}
/* Wait until all CPU's ready to exit */ staticvoid uv_nmi_sync_exit(int master)
{
atomic_dec(&uv_nmi_cpus_in_nmi); if (master) { while (atomic_read(&uv_nmi_cpus_in_nmi) > 0)
cpu_relax();
atomic_set(&uv_nmi_slave_continue, SLAVE_CLEAR);
} else { while (atomic_read(&uv_nmi_slave_continue))
cpu_relax();
}
}
/* Current "health" check is to check which CPU's are responsive */ staticvoid uv_nmi_action_health(int cpu, struct pt_regs *regs, int master)
{ if (master) { int in = atomic_read(&uv_nmi_cpus_in_nmi); int out = num_online_cpus() - in;
pr_alert("UV: NMI CPU health check (non-responding:%d)\n", out);
atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT);
} else { while (!atomic_read(&uv_nmi_slave_continue))
cpu_relax();
}
uv_nmi_sync_exit(master);
}
/* Walk through CPU list and dump state of each */ staticvoid uv_nmi_dump_state(int cpu, struct pt_regs *regs, int master)
{ if (master) { int tcpu; int ignored = 0; int saved_console_loglevel = console_loglevel;
pr_alert("UV: tracing %s for %d CPUs from CPU %d\n",
uv_nmi_action == nmi_act_ips ? "IPs" : "processes",
atomic_read(&uv_nmi_cpus_in_nmi), cpu);
staticvoid uv_nmi_kdump(int cpu, int main, struct pt_regs *regs)
{ /* Check if kdump kernel loaded for both main and secondary CPUs */ if (!kexec_crash_image) { if (main)
pr_err("UV: NMI error: kdump kernel not loaded\n"); return;
}
/* Call crash to dump system state */ if (main) {
pr_emerg("UV: NMI executing crash_kexec on CPU%d\n", cpu);
crash_kexec(regs);
/* If kdump kernel fails, secondaries will exit this loop */ while (atomic_read(&uv_nmi_kexec_failed) == 0) {
/* Once shootdown cpus starts, they do not return */
run_crash_ipi_callback(regs);
mdelay(10);
}
}
}
#ifdef CONFIG_KGDB #ifdef CONFIG_KGDB_KDB staticinlineint uv_nmi_kdb_reason(void)
{ return KDB_REASON_SYSTEM_NMI;
} #else/* !CONFIG_KGDB_KDB */ staticinlineint uv_nmi_kdb_reason(void)
{ /* Ensure user is expecting to attach gdb remote */ if (uv_nmi_action == nmi_act_kgdb) return0;
pr_err("UV: NMI error: KDB is not enabled in this kernel\n"); return -1;
} #endif/* CONFIG_KGDB_KDB */
/* *CallKGDB/KDBfromNMIhandler * *NotethatifbothKGDBandKDBareconfigured,thentheactionof'kgdb'or *'kdb'hasnoaffectonwhichisused.SeetheKGDBdocumentationforfurther *information.
*/ staticvoid uv_call_kgdb_kdb(int cpu, struct pt_regs *regs, int master)
{ if (master) { int reason = uv_nmi_kdb_reason(); int ret;
if (reason < 0) return;
/* Call KGDB NMI handler as MASTER */
ret = kgdb_nmicallin(cpu, X86_TRAP_NMI, regs, reason,
&uv_nmi_slave_continue); if (ret) {
pr_alert("KGDB returned error, is kgdboc set?\n");
atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT);
}
} else { /* Wait for KGDB signal that it's ready for slaves to enter */ int sig;
do {
cpu_relax();
sig = atomic_read(&uv_nmi_slave_continue);
} while (!sig);
/* Call KGDB as slave */ if (sig == SLAVE_CONTINUE)
kgdb_nmicallback(cpu, regs);
}
uv_nmi_sync_exit(master);
}
#else/* !CONFIG_KGDB */ staticinlinevoid uv_call_kgdb_kdb(int cpu, struct pt_regs *regs, int master)
{
pr_err("UV: NMI error: KGDB is not enabled in this kernel\n");
} #endif/* !CONFIG_KGDB */
/* *UVNMIhandler
*/ staticint uv_handle_nmi(unsignedint reason, struct pt_regs *regs)
{ struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi; int cpu = smp_processor_id(); int master = 0; unsignedlong flags;
local_irq_save(flags);
/* If not a UV System NMI, ignore */ if (!this_cpu_read(uv_cpu_nmi.pinging) && !uv_check_nmi(hub_nmi)) {
local_irq_restore(flags); return NMI_DONE;
}
/* Indicate we are the first CPU into the NMI handler */
master = (atomic_read(&uv_nmi_cpu) == cpu);
/* If NMI action is "kdump", then attempt to do it */ if (uv_nmi_action == nmi_act_kdump) {
uv_nmi_kdump(cpu, master, regs);
/* Unexpected return, revert action to "dump" */ if (master)
uv_nmi_action = nmi_act_dump;
}
/* Pause as all CPU's enter the NMI handler */
uv_nmi_wait(master);
/* Process actions other than "kdump": */ switch (uv_nmi_action) { case nmi_act_health:
uv_nmi_action_health(cpu, regs, master); break; case nmi_act_ips: case nmi_act_dump:
uv_nmi_dump_state(cpu, regs, master); break; case nmi_act_kdb: case nmi_act_kgdb:
uv_call_kgdb_kdb(cpu, regs, master); break; default: if (master)
pr_alert("UV: unknown NMI action: %d\n", uv_nmi_action);
uv_nmi_sync_exit(master); break;
}
/* Clear per_cpu "in_nmi" flag */
this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_OUT);
/* Clear MMR NMI flag on each hub */
uv_clear_nmi(cpu);
/* Clear global flags */ if (master) { if (!cpumask_empty(uv_nmi_cpu_mask))
uv_nmi_cleanup_mask();
atomic_set(&uv_nmi_cpus_in_nmi, -1);
atomic_set(&uv_nmi_cpu, -1);
atomic_set(&uv_in_nmi, 0);
atomic_set(&uv_nmi_kexec_failed, 0);
atomic_set(&uv_nmi_slave_continue, SLAVE_CLEAR);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.