Skip to content
Permalink
Browse files

Introduce SMP-support for panic()!

Now once any of the machine cores panics, it broadcasts an IPI to
all other cores instructing them to disable interrupts and halt.

Measures are taken to avoid races in the concurrent panic() case,
and to avoid deadlocking the system if other cores were inside a
lock-protected critical region.
  • Loading branch information...
a-darwish committed Jan 31, 2012
1 parent 7c7c666 commit 4963ed25c7d701f2eac895c4115fff91d509023c
Showing with 131 additions and 61 deletions.
  1. +1 −1 Makefile
  2. +5 −3 dev/apic.c
  3. +2 −0 include/kernel.h
  4. +2 −1 include/smpboot.h
  5. +1 −0 include/vectors.h
  6. +0 −52 kern/common.c
  7. +10 −4 kern/idt.S
  8. +3 −0 kern/main.c
  9. +86 −0 kern/panic.c
  10. +10 −0 kern/smpboot.c
  11. +11 −0 lib/printf.c
@@ -194,7 +194,7 @@ KERN_OBJS = \
kern/smpboot.o \
kern/sched.o \
kern/kthread.o \
kern/common.o \
kern/panic.o \
kern/percpu.o \
kern/ramdisk.o \
kern/main.o
@@ -312,8 +312,10 @@ void apic_monotonic(int ms, uint8_t vector)
* Inter-Processor Interrupts
*/

static void __apic_send_ipi(int dst_apic_id, int delivery_mode, int vector,
enum irq_dest dest)
/* NOTE! This function is implicitly called by panic
* code: it should not include any asserts or panics. */
static void __apic_send_ipi(int dst_apic_id, int delivery_mode,
int vector, enum irq_dest dest)
{
union apic_icr icr = { .value = 0 };

@@ -329,7 +331,7 @@ static void __apic_send_ipi(int dst_apic_id, int delivery_mode, int vector,
icr.dest = dst_apic_id;
break;
default:
assert(false);
compiler_assert(false);
}

/* "Level" and "deassert" are for 82489DX */
@@ -101,6 +101,7 @@
* Main kernel print methods
*/
int vsnprintf(char *buf, int size, const char *fmt, va_list args);
void printk_bust_all_locks(void);
void printk(const char *fmt, ...);
void prints(const char *fmt, ...);
void putc(char c);
@@ -116,6 +117,7 @@ static void __unused printk_run_tests(void) { }
* Critical failures
*/
void __no_return panic(const char *fmt, ...);
extern void halt_cpu_ipi_handler(void);

#define assert(condition) \
do { \
@@ -43,8 +43,9 @@
#define TRAMPOLINE_START VIRTUAL(SMPBOOT_START)
#define TRAMPOLINE_PARAMS VIRTUAL(SMPBOOT_PARAMS)

void smpboot_init(void);
void __no_return secondary_start(void); /* Silence-out GCC */
int smpboot_get_nr_alive_cpus(void);
void smpboot_init(void);

#endif /* !__ASSEMBLY__ */

@@ -36,6 +36,7 @@

// Priority 0xf - Highest priority
#define TICKS_IRQ_VECTOR 0xf0
#define HALT_CPU_IPI_VECTOR 0xf1
#define APIC_SPURIOUS_VECTOR 0xff // Intel-defined default

// Priority 0x4 - APIC vectors

This file was deleted.

@@ -298,30 +298,36 @@ kb_handler:
call __kb_handler
jmp irq_end

/*
* Once a CPU panic()s, it sends an IPI to other cores to jump
* here. We just disable local interrupts and halt in response.
*/
.globl halt_cpu_ipi_handler
halt_cpu_ipi_handler:
cli
1: hlt
jmp 1b

/*
* PIT periodic mode test handler
*/
#if PIT_TESTS

.globl pit_periodic_handler
pit_periodic_handler:
PUSH_REGS
call __pit_periodic_handler
jmp irq_end

#endif /* PIT_TESTS */

/*
* APIC periodic mode test handler
*/
#if APIC_TESTS

.globl apic_timer_handler
apic_timer_handler:
PUSH_REGS
call __apic_timer_handler
jmp irq_end

#endif /* APIC_TESTS */

.data
@@ -12,6 +12,7 @@
#include <sections.h>
#include <segment.h>
#include <idt.h>
#include <vectors.h>
#include <mptables.h>
#include <serial.h>
#include <pit.h>
@@ -35,6 +36,8 @@ static void setup_idt(void)
for (int i = 0; i < EXCEPTION_GATES; i ++)
set_intr_gate(i, &idt_exception_stubs[i]);

set_intr_gate(HALT_CPU_IPI_VECTOR, halt_cpu_ipi_handler);

load_idt(&idtdesc);
}

@@ -0,0 +1,86 @@
/*
* Panic("msg"): to be called on unresolvable fatal errors!
*
* Copyright (C) 2009, 2012 Ahmed S. Darwish <darwish.07@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*/

#include <kernel.h>
#include <apic.h>
#include <idt.h>
#include <vectors.h>
#include <spinlock.h>
#include <percpu.h>
#include <smpboot.h>

static char buf[256];
static spinlock_t panic_lock = SPIN_UNLOCKED();

/*
* Quickly disable system interrupts upon entrance! Now the
* kernel is in an inconsistent state, just gracefully stop
* the machine and halt :-(
*
* An interesting case faced from not disabling interrupts
* early on (disabling them at the function end instead) was
* having other threads getting scheduled between printk()
* and disabling interrupts, scrolling-away the caller panic
* message and losing information FOREVER.
*/
void __no_return panic(const char *fmt, ...)
{
va_list args;
int n;

/* NOTE! Do not put anything above this */
local_irq_disable();

/*
* NOTE! Manually assure that all the functions called
* below are void of any asserts or panics.
*/

/* Avoid concurrent panic()s: first call holds the most
* important facts; the rest are usually side-effects. */
if (!spin_trylock(&panic_lock))
goto halt;

/* If other cores are alive, send them a fixed IPI, which
* intentionally avoids interrupting cores with IF=0 till
* they re-accept interrupts. Why?
*
* An interrupted critical region may deadlock our panic
* code if we tried to acquire the same lock. The other
* cores may not also be in long-mode before they enable
* interrupts (e.g. in the 16-bit SMP trampoline step.)
*
* IPIs are sent only if more than one core is alive: we
* might be on so early a stage that our APIC registers
* are not yet memory mapped, leading to memory faults if
* locally accessed!
*
* If destination CPUs were alive but have not yet inited
* their local APICs, they will not be able to catch this
* IPI and will normally continue execution. Beware.
*/
if (smpboot_get_nr_alive_cpus() > 1)
apic_broadcast_ipi(APIC_DELMOD_FIXED, HALT_CPU_IPI_VECTOR);

va_start(args, fmt);
n = vsnprintf(buf, sizeof(buf) - 1, fmt, args);
va_end(args);

buf[n] = 0;
printk("\nCPU#%d-PANIC: %s", percpu_get(apic_id), buf);

/* Since the other cores are stopped only after they re-
* accept interrupts, they may print on-screen and scroll
* away our message. Acquire all screen locks, forever. */
printk_bust_all_locks();

halt:
halt();
}
@@ -237,6 +237,16 @@ void __no_return secondary_start(void)
halt();
}

/*
* NOTE! This function is called by panic(): it should
* not include any asserts or panics.
*/
int smpboot_get_nr_alive_cpus(void)
{
barrier();
return nr_alive_cpus;
}

void smpboot_init(void)
{
int nr_cpus;
@@ -440,6 +440,17 @@ void prints(const char *fmt, ...)
spin_unlock(&sbuf_lock);
}

/*
* Do not permit any access to screen state after calling
* this method. This is for panic(), which is important
* not to scroll away its critical messages afterwards.
*/
void printk_bust_all_locks(void)
{
spin_lock(&vga_lock);
spin_lock(&kbuf_lock);
}

/*
* Minimal printk test cases
*/

0 comments on commit 4963ed2

Please sign in to comment.
You can’t perform that action at this time.