yet another fix for #1018, which has at this point blossomed into a reorg of how AP cpus are initialized.

the new cpuid stuff was apparently exacerbating an existing problem where various bits of low level
cpu code (specifically get_current_cpu) weren't really initialized before being used. Changed the
order to set up a fake set of threads to point each cpu at really early in boot to make sure that at
all points in code it can get the current 'thread' and thus the current cpu.
A probably better solution would be to have dr3 point to the current cpu which would then point to the 
current thread, but that has a race condition that would require an int disable, etc.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20160 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Travis Geiselbrecht
2007-02-19 00:11:24 +00:00
parent b2562a8d64
commit badc7b674e
11 changed files with 120 additions and 109 deletions
+1
View File
@@ -209,6 +209,7 @@ extern status_t register_kernel_daemon(daemon_hook hook, void *arg, int frequen
extern status_t unregister_kernel_daemon(daemon_hook hook, void *arg);
extern void call_all_cpus(void (*f)(void *, int), void *cookie);
extern void call_all_cpus_sync(void (*f)(void *, int), void *cookie);
/* safe methods to access user memory without having to lock it */
extern status_t user_memcpy(void *to, const void *from, size_t size);
+1
View File
@@ -216,6 +216,7 @@ extern status_t register_kernel_daemon(daemon_hook hook, void *arg, int frequen
extern status_t unregister_kernel_daemon(daemon_hook hook, void *arg);
extern void call_all_cpus(void (*f)(void *, int), void *cookie);
extern void call_all_cpus_sync(void (*f)(void *, int), void *cookie);
/* safe methods to access user memory without having to lock it */
extern status_t user_memcpy(void *to, const void *from, size_t size);
+1 -1
View File
@@ -19,7 +19,7 @@
extern "C" {
#endif
status_t arch_cpu_preboot_init(kernel_args *args);
status_t arch_cpu_preboot_init_percpu(kernel_args *args, int curr_cpu);
status_t arch_cpu_init(kernel_args *args);
status_t arch_cpu_init_percpu(kernel_args *args, int curr_cpu);
status_t arch_cpu_init_post_vm(kernel_args *args);
+1 -1
View File
@@ -43,7 +43,7 @@ extern cpu_ent gCPU[MAX_BOOT_CPUS];
extern "C" {
#endif
status_t cpu_preboot_init(struct kernel_args *args);
status_t cpu_preboot_init_percpu(struct kernel_args *args, int curr_cpu);
status_t cpu_init(struct kernel_args *args);
status_t cpu_init_percpu(kernel_args *ka, int curr_cpu);
status_t cpu_init_post_vm(struct kernel_args *args);
+1 -1
View File
@@ -30,7 +30,7 @@ void thread_at_kernel_entry(void);
void thread_at_kernel_exit(void);
status_t thread_init(struct kernel_args *args);
status_t thread_per_cpu_init(int32 cpuNum);
status_t thread_preboot_init_percpu(struct kernel_args *args, int32 cpuNum);
void thread_yield(void);
void thread_exit(void);
+1 -1
View File
@@ -17,7 +17,7 @@
static bool sHasTlbia;
status_t
arch_cpu_preboot_init(kernel_args *args)
arch_cpu_preboot_init_percpu(kernel_args *args, int curr_cpu)
{
// enable FPU
set_msr(get_msr() | MSR_FP_AVAILABLE);
+3 -5
View File
@@ -320,7 +320,7 @@ static void make_feature_string(cpu_ent *cpu, char *str, size_t strlen)
strlcat(str, "3dnow ", strlen);
}
static int detect_cpu(kernel_args *ka, int curr_cpu)
static int detect_cpu(int curr_cpu)
{
cpuid_info cpuid;
unsigned int data[4];
@@ -431,10 +431,8 @@ bool x86_check_feature(uint32 feature, enum x86_feature_type type)
// #pragma mark -
status_t
arch_cpu_preboot_init(kernel_args *args)
arch_cpu_preboot_init_percpu(kernel_args *args, int curr_cpu)
{
write_dr3(0);
x86_write_cr0(x86_read_cr0() & ~(CR0_FPU_EMULATION | CR0_MONITOR_FPU));
gX86SwapFPUFunc = i386_fnsave_swap;
@@ -445,7 +443,7 @@ arch_cpu_preboot_init(kernel_args *args)
status_t
arch_cpu_init_percpu(kernel_args *args, int curr_cpu)
{
detect_cpu(args, curr_cpu);
detect_cpu(curr_cpu);
// load the TSS for this cpu
// note the main cpu gets initialized in arch_cpu_init_post_vm()
+7 -10
View File
@@ -26,13 +26,6 @@ static spinlock sSetCpuLock;
status_t
cpu_init(kernel_args *args)
{
int i;
memset(gCPU, 0, sizeof(gCPU));
for (i = 0; i < MAX_BOOT_CPUS; i++) {
gCPU[i].cpu_num = i;
}
return arch_cpu_init(args);
}
@@ -57,11 +50,15 @@ cpu_init_post_modules(kernel_args *args)
status_t
cpu_preboot_init(kernel_args *args)
cpu_preboot_init_percpu(kernel_args *args, int curr_cpu)
{
return arch_cpu_preboot_init(args);
}
// set the cpu number in the local cpu structure so that
// we can use it for get_current_cpu
memset(&gCPU[curr_cpu], 0, sizeof(gCPU[curr_cpu]));
gCPU[curr_cpu].cpu_num = curr_cpu;
return arch_cpu_preboot_init_percpu(args, curr_cpu);
}
bigtime_t
cpu_get_active_time(int32 cpu)
+45 -46
View File
@@ -42,9 +42,9 @@
//#define TRACE_BOOT
#ifdef TRACE_BOOT
# define TRACE(x) dprintf x
# define TRACE(x...) dprintf("INIT : " x)
#else
# define TRACE(x) ;
# define TRACE(x...) ;
#endif
bool kernel_startup;
@@ -75,7 +75,8 @@ _start(kernel_args *bootKernelArgs, int currentCPU)
smp_set_num_cpus(sKernelArgs.num_cpus);
// do any pre-booting cpu config
cpu_preboot_init(&sKernelArgs);
cpu_preboot_init_percpu(&sKernelArgs, currentCPU);
thread_preboot_init_percpu(&sKernelArgs, currentCPU);
// if we're not a boot cpu, spin here until someone wakes us up
if (smp_trap_non_boot_cpus(currentCPU)) {
@@ -93,92 +94,90 @@ _start(kernel_args *bootKernelArgs, int currentCPU)
smp_wait_for_non_boot_cpus();
// init modules
TRACE(("init CPU\n"));
TRACE("init CPU\n");
cpu_init(&sKernelArgs);
cpu_init_percpu(&sKernelArgs, currentCPU);
TRACE(("init interrupts\n"));
TRACE("init interrupts\n");
int_init(&sKernelArgs);
TRACE(("init VM\n"));
TRACE("init VM\n");
vm_init(&sKernelArgs);
// Before vm_init_post_sem() is called, we have to make sure that
// the boot loader allocated region is not used anymore
// now we can use the heap and create areas
arch_platform_init_post_vm(&sKernelArgs);
TRACE(("init driver_settings\n"));
TRACE("init driver_settings\n");
boot_item_init();
driver_settings_init(&sKernelArgs);
debug_init_post_vm(&sKernelArgs);
int_init_post_vm(&sKernelArgs);
cpu_init_post_vm(&sKernelArgs);
TRACE(("init system info\n"));
TRACE("init system info\n");
system_info_init(&sKernelArgs);
TRACE(("init SMP\n"));
TRACE("init SMP\n");
smp_init(&sKernelArgs);
TRACE(("init timer\n"));
TRACE("init timer\n");
timer_init(&sKernelArgs);
TRACE(("init real time clock\n"));
TRACE("init real time clock\n");
rtc_init(&sKernelArgs);
TRACE(("init semaphores\n"));
TRACE("init semaphores\n");
sem_init(&sKernelArgs);
// now we can create and use semaphores
TRACE(("init VM semaphores\n"));
TRACE("init VM semaphores\n");
vm_init_post_sem(&sKernelArgs);
TRACE(("init driver_settings\n"));
TRACE("init driver_settings\n");
driver_settings_init_post_sem(&sKernelArgs);
TRACE(("init generic syscall\n"));
TRACE("init generic syscall\n");
generic_syscall_init();
TRACE(("init cbuf\n"));
TRACE("init cbuf\n");
cbuf_init();
TRACE(("init teams\n"));
TRACE("init teams\n");
team_init(&sKernelArgs);
TRACE(("init threads\n"));
TRACE("init threads\n");
thread_init(&sKernelArgs);
TRACE(("init ports\n"));
TRACE("init ports\n");
port_init(&sKernelArgs);
TRACE(("init kernel daemons\n"));
TRACE("init kernel daemons\n");
kernel_daemon_init();
arch_platform_init_post_thread(&sKernelArgs);
TRACE(("init VM threads\n"));
TRACE("init VM threads\n");
vm_init_post_thread(&sKernelArgs);
TRACE(("init ELF loader\n"));
TRACE("init ELF loader\n");
elf_init(&sKernelArgs);
TRACE(("init scheduler\n"));
TRACE("init scheduler\n");
scheduler_init();
TRACE(("init VFS\n"));
TRACE("init VFS\n");
vfs_init(&sKernelArgs);
// start a thread to finish initializing the rest of the system
thread = spawn_kernel_thread(&main2, "main2", B_NORMAL_PRIORITY, NULL);
smp_wake_up_non_boot_cpus();
TRACE(("enable interrupts, exit kernel startup\n"));
TRACE("enable interrupts, exit kernel startup\n");
kernel_startup = false;
enable_interrupts();
TRACE("waking up AP cpus\n");
smp_wake_up_non_boot_cpus();
enable_interrupts();
scheduler_start();
// start a thread to finish initializing the rest of the system
TRACE("starting main2 thread\n");
thread = spawn_kernel_thread(&main2, "main2", B_NORMAL_PRIORITY, NULL);
resume_thread(thread);
} else {
// this is run for each non boot processor after they've been set loose
// the order here is pretty important, and kind of arch specific, so it's sort of a hack at the moment.
// thread_* will set the current thread pointer, which lets low level code know what cpu it's on
// cpu_* will detect the current cpu and do any pending low level setup
// smp_* will set up the low level smp routines
thread_per_cpu_init(currentCPU);
cpu_init_percpu(&sKernelArgs, currentCPU);
smp_per_cpu_init(&sKernelArgs, currentCPU);
// welcome to the machine
enable_interrupts();
scheduler_start();
}
TRACE(("main: done... begin idle loop on cpu %d\n", currentCPU));
TRACE("main: done... begin idle loop on cpu %d\n", currentCPU);
for (;;)
arch_cpu_idle();
@@ -191,9 +190,9 @@ main2(void *unused)
{
(void)(unused);
TRACE(("start of main2: initializing devices\n"));
TRACE("start of main2: initializing devices\n");
TRACE(("Init modules\n"));
TRACE("Init modules\n");
module_init(&sKernelArgs);
// ToDo: the preloaded image debug data is placed in the kernel args, and
@@ -208,18 +207,18 @@ main2(void *unused)
}
// init userland debugging
TRACE(("Init Userland debugging\n"));
TRACE("Init Userland debugging\n");
init_user_debug();
// init the messaging service
TRACE(("Init Messaging Service\n"));
TRACE("Init Messaging Service\n");
init_messaging_service();
/* bootstrap all the filesystems */
TRACE(("Bootstrap file systems\n"));
TRACE("Bootstrap file systems\n");
vfs_bootstrap_file_systems();
TRACE(("Init Device Manager\n"));
TRACE("Init Device Manager\n");
device_manager_init(&sKernelArgs);
// ToDo: device manager starts here, bus_init()/dev_init() won't be necessary anymore,
@@ -227,7 +226,7 @@ main2(void *unused)
int_init_post_device_manager(&sKernelArgs);
TRACE(("Mount boot file system\n"));
TRACE("Mount boot file system\n");
vfs_mount_boot_file_system(&sKernelArgs);
// CPU specific modules may now be available
@@ -259,7 +258,7 @@ main2(void *unused)
thread = load_image(argc, args, NULL);
if (thread >= B_OK) {
resume_thread(thread);
TRACE(("Bootscript started\n"));
TRACE("Bootscript started\n");
} else
dprintf("error starting \"%s\" error = %ld \n", args[0], thread);
}
+28 -19
View File
@@ -551,8 +551,14 @@ smp_trap_non_boot_cpus(int32 cpu)
{
if (cpu > 0) {
boot_cpu_spin[cpu] = 1;
acquire_spinlock(&boot_cpu_spin[cpu]);
acquire_spinlock_nocheck(&boot_cpu_spin[cpu]);
return false;
// lets make sure we're in sync with the main cpu
// the boot processor has probably been sending us
// tlb sync messages all along the way, but we've
// been ignoring them
arch_cpu_global_TLB_invalidate();
}
return true;
@@ -562,23 +568,16 @@ smp_trap_non_boot_cpus(int32 cpu)
void
smp_wake_up_non_boot_cpus()
{
// resume non boot CPUs
int i;
for (i = 1; i < sNumCPUs; i++) {
release_spinlock(&boot_cpu_spin[i]);
}
// ICIs were previously being ignored
if (sNumCPUs > 1)
sICIEnabled = true;
// invalidate all of the other processors' TLB caches
arch_cpu_global_TLB_invalidate();
smp_send_broadcast_ici(SMP_MSG_GLOBAL_INVALIDATE_PAGES, 0, 0, 0, NULL,
SMP_MSG_FLAG_SYNC);
// start the other processors
smp_send_broadcast_ici(SMP_MSG_RESCHEDULE, 0, 0, 0, NULL, SMP_MSG_FLAG_ASYNC);
// resume non boot CPUs
for (i = 1; i < sNumCPUs; i++) {
release_spinlock(&boot_cpu_spin[i]);
}
}
@@ -651,13 +650,7 @@ smp_get_num_cpus()
int32
smp_get_current_cpu(void)
{
struct thread *thread = thread_get_current_thread();
if (thread)
return thread->cpu->cpu_num;
// this is not always correct during early boot, but it's okay
// for the boot process
return 0;
return thread_get_current_thread()->cpu->cpu_num;
}
@@ -681,3 +674,19 @@ call_all_cpus(void (*func)(void *, int), void *cookie)
restore_interrupts(state);
}
void
call_all_cpus_sync(void (*func)(void *, int), void *cookie)
{
cpu_status state = disable_interrupts();
if (smp_get_num_cpus() > 1) {
smp_send_broadcast_ici(SMP_MSG_CALL_FUNCTION, (uint32)cookie,
0, 0, (void *)func, SMP_MSG_FLAG_SYNC);
}
// we need to call this function ourselves as well
func(cookie, smp_get_current_cpu());
restore_interrupts(state);
}
+31 -25
View File
@@ -63,7 +63,7 @@ static status_t receive_data_etc(thread_id *_sender, void *buffer,
spinlock thread_spinlock = 0;
// thread list
static struct thread *sIdleThreads[B_MAX_CPU_COUNT];
static struct thread sIdleThreads[B_MAX_CPU_COUNT];
static void *sThreadHash = NULL;
static thread_id sNextThreadID = 1;
@@ -162,30 +162,36 @@ thread_struct_hash(void *_t, const void *_key, uint32 range)
return (uint32)key->id % range;
}
/** Allocates a thread structure (or reuses one from the dead queue).
/** Allocates and fills in thread structure (or reuses one from the dead queue).
*
* \param threadID The ID to be assigned to the new thread. If
* \code < 0 \endcode a fresh one is allocated.
* \param thread initialize this thread struct if nonnull
*/
static struct thread *
create_thread_struct(const char *name, thread_id threadID)
create_thread_struct(struct thread *inthread, const char *name, thread_id threadID)
{
struct thread *thread;
cpu_status state;
char temp[64];
state = disable_interrupts();
GRAB_THREAD_LOCK();
thread = thread_dequeue(&dead_q);
RELEASE_THREAD_LOCK();
restore_interrupts(state);
if (inthread == NULL) {
// try to recycle one from the dead queue first
state = disable_interrupts();
GRAB_THREAD_LOCK();
thread = thread_dequeue(&dead_q);
RELEASE_THREAD_LOCK();
restore_interrupts(state);
if (thread == NULL) {
thread = (struct thread *)malloc(sizeof(struct thread));
if (thread == NULL)
return NULL;
// if not, create a new one
if (thread == NULL) {
thread = (struct thread *)malloc(sizeof(struct thread));
if (thread == NULL)
return NULL;
}
} else {
thread = inthread;
}
if (name != NULL)
@@ -195,7 +201,8 @@ create_thread_struct(const char *name, thread_id threadID)
thread->id = threadID >= 0 ? threadID : allocate_thread_id();
thread->team = NULL;
thread->cpu = NULL;
// XXX terrible hack, this is to leave the early boot cpu pointers alone while being initialized
// thread->cpu = NULL;
thread->sem.blocking = -1;
thread->fault_handler = 0;
thread->page_faults_allowed = 1;
@@ -251,7 +258,8 @@ err2:
delete_sem(thread->exit.sem);
err1:
// ToDo: put them in the dead queue instead?
free(thread);
if (inthread == NULL)
free(thread);
return NULL;
}
@@ -352,7 +360,7 @@ create_thread(const char *name, team_id teamID, thread_entry_func entry,
TRACE(("create_thread(%s, id = %ld, %s)\n", name, threadID, kernel ? "kernel" : "user"));
thread = create_thread_struct(name, threadID);
thread = create_thread_struct(NULL, name, threadID);
if (thread == NULL)
return B_NO_MEMORY;
@@ -1461,7 +1469,7 @@ thread_init(kernel_args *args)
char name[64];
sprintf(name, "idle thread %lu", i + 1);
thread = create_thread_struct(name,
thread = create_thread_struct(&sIdleThreads[i], name,
i == 0 ? team_get_kernel_team_id() : -1);
if (thread == NULL) {
panic("error creating idle thread struct\n");
@@ -1483,11 +1491,6 @@ thread_init(kernel_args *args)
hash_insert(sThreadHash, thread);
insert_thread_into_team(thread->team, thread);
sIdleThreads[i] = thread;
if (i == 0)
arch_thread_set_current_thread(thread);
thread->cpu = &gCPU[i];
}
sUsedThreads = args->num_cpus;
@@ -1539,13 +1542,16 @@ thread_init(kernel_args *args)
status_t
thread_per_cpu_init(int32 cpuNum)
thread_preboot_init_percpu(struct kernel_args *args, int32 cpuNum)
{
arch_thread_set_current_thread(sIdleThreads[cpuNum]);
// set up the cpu pointer in the not yet initialized per-cpu idle thread
// so that get_current_cpu and friends will work, which is crucial for
// a lot of low level routines
sIdleThreads[cpuNum].cpu = &gCPU[cpuNum];
arch_thread_set_current_thread(&sIdleThreads[cpuNum]);
return B_OK;
}
// #pragma mark - public kernel API