/* * Create a new thread based on an existing one. * * The new thread has name NAME, and starts executing in function * ENTRYPOINT. DATA1 and DATA2 are passed to ENTRYPOINT. * * The new thread is created in the process P. If P is null, the * process is inherited from the caller. It will start on the same CPU * as the caller, unless the scheduler intervenes first. */ int thread_fork(const char *name, struct proc *proc, void (*entrypoint)(void *data1, unsigned long data2), void *data1, unsigned long data2) { struct thread *newthread; int result; newthread = thread_create(name); if (newthread == NULL) { return ENOMEM; } /* Allocate a stack */ newthread->t_stack = kmalloc(STACK_SIZE); if (newthread->t_stack == NULL) { thread_destroy(newthread); return ENOMEM; } thread_checkstack_init(newthread); /* * Now we clone various fields from the parent thread. */ /* Thread subsystem fields */ newthread->t_cpu = curthread->t_cpu; /* Attach the new thread to its process */ if (proc == NULL) { proc = curthread->t_proc; } result = proc_addthread(proc, newthread); if (result) { /* thread_destroy will clean up the stack */ thread_destroy(newthread); return result; } /* * Because new threads come out holding the cpu runqueue lock * (see notes at bottom of thread_switch), we need to account * for the spllower() that will be done releasing it. */ newthread->t_iplhigh_count++; spinlock_acquire(&thread_count_lock); ++thread_count; wchan_wakeall(thread_count_wchan, &thread_count_lock); spinlock_release(&thread_count_lock); /* Set up the switchframe so entrypoint() gets called */ switchframe_init(newthread, entrypoint, data1, data2); /* Lock the current cpu's run queue and make the new thread runnable */ thread_make_runnable(newthread, false); return 0; }
/* * Create a CPU structure. This is used for the bootup CPU and * also for secondary CPUs. * * The hardware number (the number assigned by firmware or system * board config or whatnot) is tracked separately because it is not * necessarily anything sane or meaningful. */ struct cpu * cpu_create(unsigned hardware_number) { struct cpu *c; int result; char namebuf[16]; c = kmalloc(sizeof(*c)); if (c == NULL) { panic("cpu_create: Out of memory\n"); } c->c_self = c; c->c_hardware_number = hardware_number; c->c_curthread = NULL; threadlist_init(&c->c_zombies); c->c_hardclocks = 0; c->c_spinlocks = 0; c->c_isidle = false; threadlist_init(&c->c_runqueue); spinlock_init(&c->c_runqueue_lock); c->c_ipi_pending = 0; c->c_numshootdown = 0; spinlock_init(&c->c_ipi_lock); result = cpuarray_add(&allcpus, c, &c->c_number); if (result != 0) { panic("cpu_create: array_add: %s\n", strerror(result)); } snprintf(namebuf, sizeof(namebuf), "<boot #%d>", c->c_number); c->c_curthread = thread_create(namebuf); if (c->c_curthread == NULL) { panic("cpu_create: thread_create failed\n"); } result = proc_addthread(kproc, c->c_curthread); if (result) { panic("cpu_create: proc_addthread:: %s\n", strerror(result)); } if (c->c_number == 0) { /* * Leave c->c_curthread->t_stack NULL for the boot * cpu. This means we're using the boot stack, which * can't be freed. (Exercise: what would it take to * make it possible to free the boot stack?) */ /*c->c_curthread->t_stack = ... */ } else { c->c_curthread->t_stack = kmalloc(STACK_SIZE); if (c->c_curthread->t_stack == NULL) { panic("cpu_create: couldn't allocate stack"); } thread_checkstack_init(c->c_curthread); } c->c_curthread->t_cpu = c; cpu_machdep_init(c); return c; }
/* * Create a CPU structure. This is used for the bootup CPU and * also for secondary CPUs. * * The hardware number (the number assigned by firmware or system * board config or whatnot) is tracked separately because it is not * necessarily anything sane or meaningful. */ struct cpu * cpu_create(unsigned hardware_number) { struct cpu *c; int result; char namebuf[16]; c = kmalloc(sizeof(*c)); if (c == NULL) { panic("cpu_create: Out of memory\n"); } c->c_self = c; c->c_hardware_number = hardware_number; c->c_curthread = NULL; threadlist_init(&c->c_zombies); c->c_hardclocks = 0; c->c_spinlocks = 0; c->c_isidle = false; threadlist_init(&c->c_runqueue); spinlock_init(&c->c_runqueue_lock); c->c_ipi_pending = 0; c->c_numshootdown = 0; spinlock_init(&c->c_ipi_lock); result = cpuarray_add(&allcpus, c, &c->c_number); if (result != 0) { panic("cpu_create: array_add: %s\n", strerror(result)); } snprintf(namebuf, sizeof(namebuf), "<boot #%d>", c->c_number); c->c_curthread = thread_create(namebuf); if (c->c_curthread == NULL) { panic("cpu_create: thread_create failed\n"); } c->c_curthread->t_cpu = c; if (c->c_number == 0) { /* * Leave c->c_curthread->t_stack NULL for the boot * cpu. This means we're using the boot stack, which * can't be freed. (Exercise: what would it take to * make it possible to free the boot stack?) */ /*c->c_curthread->t_stack = ... */ } else { c->c_curthread->t_stack = kmalloc(STACK_SIZE); if (c->c_curthread->t_stack == NULL) { panic("cpu_create: couldn't allocate stack"); } thread_checkstack_init(c->c_curthread); } /* * If there is no curcpu (or curthread) yet, we are creating * the first (boot) cpu. Initialize curcpu and curthread as * early as possible so that other code can take locks without * exploding. */ if (!CURCPU_EXISTS()) { /* * Initializing curcpu and curthread is * machine-dependent because either of curcpu and * curthread might be defined in terms of the other. */ INIT_CURCPU(c, c->c_curthread); /* * Now make sure both t_cpu and c_curthread are * set. This might be partially redundant with * INIT_CURCPU depending on how things are defined. */ curthread->t_cpu = curcpu; curcpu->c_curthread = curthread; } result = proc_addthread(kproc, c->c_curthread); if (result) { panic("cpu_create: proc_addthread:: %s\n", strerror(result)); } cpu_machdep_init(c); return c; }