585 lines
15 KiB
C
585 lines
15 KiB
C
/* $Id: boot_restart.c,v 1.19 2009/06/29 10:40:15 atic Exp $
|
|
*
|
|
* Architecture-specific recovery.
|
|
*
|
|
* Copyright 2001-2003 Salavat S. Guiliazov (atic@mcst.ru)
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/init_task.h>
|
|
#include <linux/console.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/device.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/syscalls.h>
|
|
|
|
#include <asm/system.h>
|
|
#include <asm/page.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/head.h>
|
|
#include <asm/boot_head.h>
|
|
#include <asm/boot_init.h>
|
|
#include <asm/boot_map.h>
|
|
#include <asm/cnt_point.h>
|
|
#include <asm/boot_smp.h>
|
|
#include <asm/cpu_regs_access.h>
|
|
#include <asm/mmu_regs.h>
|
|
#include <asm/bootinfo.h>
|
|
#include <asm/e2k_syswork.h>
|
|
#include <asm/smp.h>
|
|
#include <asm/machdep.h>
|
|
#include <asm/process.h>
|
|
#include <asm/bootinfo.h>
|
|
#include <asm/regs_state.h>
|
|
#include <asm/mmu_context.h>
|
|
#ifdef CONFIG_STATE_SAVE
|
|
#include <asm/state_save.h>
|
|
#endif /* CONFIG_STATE_SAVE */
|
|
|
|
#include <asm/e2k_debug.h>
|
|
|
|
#undef boot_printk
|
|
#define DEBUG_RECOVERY_MODE 0 /* system recovery */
|
|
#define DebugR if (DEBUG_RECOVERY_MODE) printk
|
|
#define BDEBUG_RECOVERY_MODE 0 /* system recovery */
|
|
#define boot_printk if (BDEBUG_RECOVERY_MODE) do_boot_printk
|
|
|
|
#define DEBUG
|
|
#undef dev_dbg
|
|
#ifdef DEBUG
|
|
#define dev_dbg(dev, format, arg...) \
|
|
dev_printk(KERN_ERR , dev , format , ## arg)
|
|
#else
|
|
#define dev_dbg(dev, format, arg...) do { (void)(dev); } while (0)
|
|
#endif
|
|
|
|
DEFINE_SEMAPHORE(restart_sem);
|
|
|
|
static void reset_kernel(void);
|
|
static int recover_system(int async_mode);
|
|
|
|
#ifdef CONFIG_SMP
|
|
static void halt_other_cpus(void * einfop);
|
|
#endif /* CONFIG_SMP */
|
|
|
|
#ifdef CONFIG_SMP
|
|
struct task_struct *tasks_to_recover[NR_CPUS] = {NULL, };
|
|
struct task_struct *tasks_to_restart[NR_CPUS] = {NULL, };
|
|
#else
|
|
struct task_struct *task_to_recover = NULL;
|
|
struct task_struct *task_to_restart = NULL;
|
|
#endif /* CONFIG_SMP */
|
|
|
|
/**
|
|
* device_suspend - call ->suspend() on each device to suspend device.
|
|
*/
|
|
|
|
static inline int
|
|
device_restart_prepare(void)
|
|
{
|
|
int error;
|
|
|
|
error = dpm_suspend_start(PMSG_SUSPEND);
|
|
if (error) {
|
|
printk("Some devices failed to suspend, error %d\n", error);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* device_resume - call ->resume() on each device to recover.
|
|
*/
|
|
static inline void
|
|
do_device_recovery(void)
|
|
{
|
|
dpm_resume_end(PMSG_RESUME);
|
|
pm_restore_console();
|
|
}
|
|
|
|
void
|
|
device_recovery(void)
|
|
{
|
|
do_device_recovery();
|
|
}
|
|
|
|
/*
|
|
* Creation of control point consists of two part:
|
|
* 1. Freezing current state of all processes and devices
|
|
* (creation control point in the memory).
|
|
* Reset of machine to start creation of next control point or
|
|
* restart from the control point, created in the memory.
|
|
* We call this part restart system.
|
|
* 2. Restart from control point in the memory returns control to
|
|
* second part of this function to contine execution from
|
|
* this control point. We call this part recovery system.
|
|
* We separate second part to the new function recovery_system() and
|
|
* restart_system() only calls them.
|
|
* But both functions is common mechanism of control point,
|
|
* so mutex restart_sem down in the first function and up in the
|
|
* second.
|
|
*/
|
|
int
|
|
restart_system(rest_type_t restart_type, int async_mode)
|
|
{
|
|
int error = 0;
|
|
struct extd_info einfo = {NULL, restart_type};
|
|
DebugR("restart_system(type %d) entered on cpu #%d\n",
|
|
restart_type, raw_smp_processor_id());
|
|
|
|
#ifdef CONFIG_STATE_SAVE
|
|
init_state_save();
|
|
#endif /* CONFIG_STATE_SAVE */
|
|
|
|
raw_local_irq_enable();
|
|
|
|
#if CONFIG_CNT_POINTS_NUM
|
|
/*
|
|
* Save on the disk previous created control points, if any
|
|
*/
|
|
if (restart_type != CORE_DUMP_REST_TYPE) {
|
|
DebugR("restart_system() starts cntr points saving on disk\n");
|
|
error = save_control_points();
|
|
if (error) {
|
|
DebugR("restart_system() control points saving on disk "
|
|
"failed with error %d\n", error);
|
|
goto Error_End;
|
|
}
|
|
}
|
|
#endif /* CONFIG_CNT_POINTS_NUM != 0 */
|
|
|
|
/*
|
|
* Prepare processes and devices to restart system
|
|
*/
|
|
|
|
pm_prepare_console();
|
|
sys_sync();
|
|
|
|
DebugR("restart_system() is continuing on cpu #%d\n",
|
|
raw_smp_processor_id());
|
|
|
|
if (restart_type != CORE_DUMP_REST_TYPE) {
|
|
DebugR("restart_system() starts devices suspending\n");
|
|
error = device_restart_prepare();
|
|
if (error) {
|
|
DebugR("restart_system() devices suspending failed "
|
|
"with error %d\n", error);
|
|
goto Error_Return;
|
|
}
|
|
DebugR("restart_system() device_restart_prepare() OK\n");
|
|
}
|
|
|
|
#ifdef CONFIG_SMP
|
|
DebugR("restart_system() starts halting of other CPUs\n");
|
|
halt_other_cpus((void *)&einfo);
|
|
DebugR("restart_system() other CPUs are halted\n");
|
|
#endif /* CONFIG_SMP */
|
|
|
|
switch_to_restart_process((void *)&einfo);
|
|
|
|
/*
|
|
* After restart machine and recovery CPU and processes state
|
|
* control returns here to continue recovery process:
|
|
* resuming of devices and saving or restoring control points
|
|
*/
|
|
|
|
error = recover_system(async_mode);
|
|
return error;
|
|
|
|
Error_Return:
|
|
pm_restore_console();
|
|
#if CONFIG_CNT_POINTS_NUM
|
|
Error_End:
|
|
#endif /* CONFIG_CNT_POINTS_NUM != 0 */
|
|
up(&restart_sem);
|
|
DebugR("restart_system() returns to interrupted point\n");
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Recovery process is divided into to parts: online and background
|
|
* Online part do the most necessary and background process do the rest
|
|
* actions to start runnig of user processes as erly as enable.
|
|
* Function should return:
|
|
* N - number of created control points in the memory, if it started
|
|
* control points creation mode. It can be only on first start
|
|
* from first created point, which was caused by restart_system()
|
|
* 0 - if function started when all points created and it is emergent
|
|
* restart of the system
|
|
*/
|
|
static int
|
|
recover_system(int async_mode)
|
|
{
|
|
int rval = 0;
|
|
|
|
if (!cnt_points_created) {
|
|
DebugR("Restart of the system from control point "
|
|
"#%d to continue running\n", cur_cnt_point);
|
|
rval = mem_cnt_points + 1;
|
|
} else {
|
|
DebugR("Emergent restart of the system from control point "
|
|
"#%d\n", cur_cnt_point);
|
|
rval = 0;
|
|
}
|
|
if (async_mode) {
|
|
background_recover_system();
|
|
} else {
|
|
DebugR("Wake up the system restart daemon on cpu #%d "
|
|
"to recover from control point\n",
|
|
raw_smp_processor_id());
|
|
restart_goal = RECOVER_REST_GOAL;
|
|
wake_up_restartd();
|
|
}
|
|
DebugR("recover_system() returns to continue system running\n");
|
|
return rval;
|
|
}
|
|
|
|
void
|
|
background_recover_system(void)
|
|
{
|
|
#if (CONFIG_CNT_POINTS_NUM > 1)
|
|
int error;
|
|
#endif /* CONFIG_CNT_POINTS_NUM > 1 */
|
|
|
|
DebugR("background_recover_system() starts devices recovery\n");
|
|
do_device_recovery();
|
|
|
|
#if (CONFIG_CNT_POINTS_NUM > 1)
|
|
/*
|
|
* Save on the disk previous created control points
|
|
*/
|
|
DebugR("background_recover_system() starts control points "
|
|
"saving on disk\n");
|
|
error = save_control_points();
|
|
if (error) {
|
|
DebugR("background_recover_system() control points "
|
|
"saving on disk failed with error %d\n", error);
|
|
}
|
|
if (!cnt_points_created) {
|
|
if (disk_cnt_points >= cnt_points_num) {
|
|
cnt_points_created = 1;
|
|
set_bootblock_cntp_created(bootblock_phys);
|
|
DebugR("background_recover_system() all %d control "
|
|
"points are created\n", disk_cnt_points);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Restore from the disk previous created control points
|
|
* in the memory, if any
|
|
*/
|
|
DebugR("background_recover_system() starts control points "
|
|
"restoring from the disk\n");
|
|
error = restore_control_points();
|
|
if (error) {
|
|
DebugR("background_recover_system() control points "
|
|
"restoring from disk failed with error %d\n", error);
|
|
}
|
|
#elif (CONFIG_CNT_POINTS_NUM == 1)
|
|
cnt_points_created = 1;
|
|
set_bootblock_cntp_created(bootblock_phys);
|
|
DebugR("background_recover_system() control point for quick restart "
|
|
"is created\n");
|
|
#endif /* CONFIG_CNT_POINTS_NUM > 1 */
|
|
|
|
up(&restart_sem); /* should be down() in restart_system() */
|
|
DebugR("background_recover_system() completed\n");
|
|
}
|
|
|
|
void
|
|
switch_to_restart_process(void * einfop)
|
|
{
|
|
int cpuid = raw_smp_processor_id();
|
|
#ifdef CONFIG_SMP
|
|
struct task_struct *task_to_restart = NULL;
|
|
thread_info_t *thread_info = NULL;
|
|
#endif
|
|
void *info = NULL;
|
|
rest_type_t restart_type = INVALID_REST_TYPE;
|
|
struct extd_info *einfo = (struct extd_info *)einfop;
|
|
|
|
#ifdef CONFIG_SMP
|
|
init_set_smp_processors_num(phys_cpu_present_num);
|
|
#endif /* CONFIG_SMP */
|
|
if (einfo) {
|
|
info = einfo->info;
|
|
restart_type = einfo->restart_type;
|
|
}
|
|
raw_local_irq_enable();
|
|
|
|
/*
|
|
* Save state registers of current process to enable
|
|
* switching to this task as end of recovery of the system
|
|
*/
|
|
|
|
if ((e2k_addr_t)current >= TASK_SIZE) {
|
|
interrupted_task(cpuid) = current;
|
|
SAVE_TASK_REGS_TO_SWITCH(current, 1);
|
|
AW(current->thread.sw_regs.cr0_lo) = E2K_GET_DSREG_NV(cr0.lo);
|
|
AW(current->thread.sw_regs.cr0_hi) = E2K_GET_DSREG_NV(cr0.hi);
|
|
} else {
|
|
cpuid = boot_smp_processor_id();
|
|
}
|
|
|
|
DebugR("switch_to_restart_process() started on CPU #%d\n", cpuid);
|
|
if ((BootStrap(arch_apic_read(APIC_BSP)) != 0) == cpuid) {
|
|
printk("switch_to_restart_process() bad CPU #%d "
|
|
"bootstrap flag %d\n",
|
|
cpuid, BootStrap(arch_apic_read(APIC_BSP)) != 0);
|
|
print_stack(current);
|
|
}
|
|
|
|
/*
|
|
* Switch to kernel boot-time stack to continue restart process
|
|
* and call function to do real restart of the system
|
|
*/
|
|
|
|
raw_local_irq_disable();
|
|
|
|
/*
|
|
* Switch current task and thread info to special structures
|
|
* to restart
|
|
*/
|
|
|
|
#ifdef CONFIG_SMP
|
|
task_to_restart = restart_task(cpuid);
|
|
if (task_to_restart == NULL) {
|
|
panic("Could not create task structure to "
|
|
"restart CPU #%d\n", cpuid);
|
|
}
|
|
thread_info = task_thread_info(task_to_restart);
|
|
set_current_thread_info(thread_info, task_to_restart);
|
|
E2K_SET_DGREG_NV(19, (u64) cpuid);
|
|
thread_info->cpu = cpuid;
|
|
thread_info->pt_regs = NULL;
|
|
|
|
SWITCH_TO_KERNEL_STACK(
|
|
kernel_boot_ps_virt_base(cpuid),
|
|
kernel_boot_ps_size(cpuid),
|
|
kernel_boot_pcs_virt_base(cpuid),
|
|
kernel_boot_pcs_size(cpuid),
|
|
kernel_boot_stack_virt_base(cpuid),
|
|
kernel_boot_stack_size(cpuid));
|
|
if (thread_info != NULL) {
|
|
thread_info->k_stk_base = kernel_boot_stack_virt_base(cpuid);
|
|
thread_info->k_stk_sz = kernel_boot_stack_size(cpuid);
|
|
thread_info->k_usd_hi = READ_USD_HI_REG();
|
|
thread_info->k_usd_lo = READ_USD_LO_REG();
|
|
}
|
|
#endif /* CONFIG_SMP */
|
|
|
|
#ifdef CONFIG_STATE_SAVE
|
|
/* Save memory */
|
|
e2k_save_state();
|
|
#endif /* CONFIG_STATE_SAVE */
|
|
|
|
set_kernel_MMU_state();
|
|
|
|
do_restart_system(restart_type);
|
|
|
|
/*
|
|
* Never should be here
|
|
*/
|
|
|
|
BUG();
|
|
}
|
|
|
|
#ifdef CONFIG_SMP
|
|
static atomic_t reset_kernel_ready = ATOMIC_INIT(0);
|
|
#endif /* CONFIG_SMP */
|
|
|
|
void
|
|
do_restart_system(rest_type_t restart_type)
|
|
{
|
|
int cpuid;
|
|
|
|
if ((e2k_addr_t)current >= TASK_SIZE) {
|
|
cpuid = raw_smp_processor_id();
|
|
} else {
|
|
cpuid = boot_smp_processor_id();
|
|
}
|
|
|
|
DebugR("do_restart_system() started on CPU #%d\n", cpuid);
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
/*
|
|
* SYNCHRONIZATION POINT #1
|
|
* At this point all processors should be switched to
|
|
* restart task and boot-time stacks
|
|
*/
|
|
atomic_set(&reset_kernel_ready, 0);
|
|
DebugR("do_restart_system() will start boot_sync_all_processors() "
|
|
"for SYNC POINT #1 on CPU #%d\n", cpuid);
|
|
(void) boot_sync_all_processors(BOOT_NO_ERROR_FLAG);
|
|
#endif /* CONFIG_SMP */
|
|
|
|
#ifdef CONFIG_SMP
|
|
if (IS_BOOT_STRAP_CPU()) {
|
|
#endif /* CONFIG_SMP */
|
|
|
|
/*
|
|
* Init the boot-time support of physical areas mapping
|
|
* to virtual space
|
|
*/
|
|
|
|
if (restart_type != CORE_DUMP_REST_TYPE) {
|
|
DebugR("do_restart_system() will start reset_kernel() "
|
|
"on CPU #%d\n", cpuid);
|
|
reset_kernel();
|
|
}
|
|
#ifdef CONFIG_SMP
|
|
/*
|
|
* Bootstrap processor completed initialization of support
|
|
* of physical areas mapping to virtual space
|
|
*/
|
|
boot_set_event(&reset_kernel_ready);
|
|
} else {
|
|
/*
|
|
* Other processors are waiting for completion of
|
|
* initialization to start mapping
|
|
*/
|
|
boot_wait_for_event(&reset_kernel_ready);
|
|
}
|
|
#endif /* CONFIG_SMP */
|
|
|
|
/*
|
|
* Reset machine and start harware boot sequence
|
|
*/
|
|
|
|
#ifdef CONFIG_SMP
|
|
if (IS_BOOT_STRAP_CPU()) {
|
|
boot_reset_smp_processors_num();
|
|
#endif /* CONFIG_SMP */
|
|
e2k_reset_machine();
|
|
#ifdef CONFIG_SMP
|
|
}
|
|
#endif /* CONFIG_SMP */
|
|
|
|
/*
|
|
* Wait for reset
|
|
*/
|
|
|
|
while(1);
|
|
}
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
static void
|
|
halt_other_cpus(void * einfop)
|
|
{
|
|
DebugR("halt_other_cpus() entered.\n");
|
|
DebugR("halt_other_cpus() will start smp_call_function() to start "
|
|
"switch_to_restart_process()\n");
|
|
smp_call_function(switch_to_restart_process, einfop, 0);
|
|
DebugR("halt_other_cpus() smp_call_function() finished\n");
|
|
DebugR("halt_other_cpus() returns.\n");
|
|
}
|
|
|
|
#endif /* CONFIG_SMP */
|
|
|
|
static void
|
|
reset_kernel(void)
|
|
{
|
|
int new_cnt_point;
|
|
e2k_addr_t new_cntp_kernel_base;
|
|
#ifdef CONFIG_NUMA
|
|
e2k_addr_t kernel_phys_base =
|
|
init_node_kernel_phys_base(BOOT_BS_NODE_ID);
|
|
#endif /* CONFIG_NUMA */
|
|
pg_data_t *pgdat;
|
|
int cntp_num;
|
|
|
|
DebugR("reset_kernel() entered init_bootinfo_phys_base:0x%lx.\n",
|
|
init_bootinfo_phys_base);
|
|
DebugR("reset_kernel() bootblock physical address is 0x%p kernel "
|
|
"image base 0x%lx\n",
|
|
bootblock_phys, kernel_phys_base);
|
|
for_each_online_pgdat(pgdat) {
|
|
DebugR("reset_kernel() pgdat 0x%p\n", pgdat);
|
|
}
|
|
mem_cnt_points ++;
|
|
write_bootblock_mem_cnt_points(bootblock_phys, mem_cnt_points);
|
|
write_bootblock_cntp_kernel_base(bootblock_phys, cur_cnt_point,
|
|
kernel_phys_base);
|
|
write_bootblock_cntp_node_data(bootblock_phys, cur_cnt_point,
|
|
kernel_va_to_pa(first_online_pgdat()));
|
|
write_bootblock_cntp_nosave_areas(bootblock_phys, cur_cnt_point,
|
|
kernel_va_to_pa(nosave_areas));
|
|
write_bootblock_cntp_nosaves_num(bootblock_phys, cur_cnt_point,
|
|
nosave_areas_num);
|
|
set_bootblock_cntp_mem_valid(bootblock_phys, cur_cnt_point);
|
|
if (is_bootblock_cntp_disk_valid(bootblock_phys, cur_cnt_point)) {
|
|
reset_bootblock_cntp_disk_valid(bootblock_phys, cur_cnt_point);
|
|
disk_cnt_points --;
|
|
write_bootblock_disk_cnt_points(bootblock_phys,
|
|
disk_cnt_points);
|
|
}
|
|
#if CONFIG_CNT_POINTS_NUM
|
|
new_cnt_point = cur_cnt_point + 1;
|
|
cntp_num = get_cnt_points_num(cnt_points_num);
|
|
#else /* CONFIG_CNT_POINTS_NUM == 0 */
|
|
new_cnt_point = cur_cnt_point;
|
|
cntp_num = 1;
|
|
#endif /* CONFIG_CNT_POINTS_NUM != 0 */
|
|
|
|
#if CONFIG_CNT_POINTS_NUM < 2
|
|
if (dump_analyze_opt)
|
|
reset_bootblock_flags(bootblock_phys, DUMP_ANALYZE_BB_FLAG);
|
|
#endif /* CONFIG_CNT_POINTS_NUM < 2 */
|
|
|
|
if (mem_cnt_points < cntp_num) {
|
|
if (new_cnt_point >= get_cnt_points_num(cnt_points_num)) {
|
|
panic("reset_kernel() new control point #%d > "
|
|
"%d (max points number)\n",
|
|
new_cnt_point,
|
|
get_cnt_points_num(cnt_points_num));
|
|
}
|
|
set_bootblock_flags(bootblock_phys,
|
|
RECOVERY_BB_FLAG | CNT_POINT_BB_FLAG);
|
|
write_bootblock_cur_cnt_point(bootblock_phys, new_cnt_point);
|
|
new_cntp_kernel_base = get_cntp_kernel_base(new_cnt_point);
|
|
write_bootblock_kernel_base(bootblock_phys,
|
|
new_cntp_kernel_base);
|
|
} else {
|
|
/* last control point */
|
|
#if CONFIG_CNT_POINTS_NUM
|
|
if (cur_cnt_point >= get_cnt_points_num(cnt_points_num)) {
|
|
panic("reset_kernel() current control point #%d >= "
|
|
"%d (max points number)\n",
|
|
cur_cnt_point,
|
|
get_cnt_points_num(cnt_points_num));
|
|
}
|
|
#endif /* CONFIG_CNT_POINTS_NUM != 0 */
|
|
reset_bootblock_flags(bootblock_phys, CNT_POINT_BB_FLAG);
|
|
set_next_control_point();
|
|
}
|
|
|
|
DebugR("reset_kernel() set bootblock recovery flag 0x%p to 0x%lx\n",
|
|
&bootblock_phys->boot_flags,
|
|
read_bootblock_flags(bootblock_phys));
|
|
|
|
DebugR("reset_kernel() finished.\n");
|
|
}
|
|
|
|
int
|
|
emergency_restart_system(void)
|
|
{
|
|
DebugR("emergency_restart_system() started on cpu #%d\n",
|
|
raw_smp_processor_id());
|
|
|
|
/*
|
|
* This function can be called only to simulate emeregency
|
|
* restart of the system.
|
|
*/
|
|
if (down_trylock(&restart_sem))
|
|
return -EBUSY;
|
|
e2k_reset_machine();
|
|
return 0;
|
|
}
|
|
|