Beispiel #1
0
struct sysret
sys_dispatcher_properties(struct capability *to,
                          enum task_type type, unsigned long deadline,
                          unsigned long wcet, unsigned long period,
                          unsigned long release, unsigned short weight)
{
    assert(to->type == ObjType_Dispatcher);

#ifdef CONFIG_SCHEDULER_RBED
    struct dcb *dcb = to->u.dispatcher.dcb;

    assert(type >= TASK_TYPE_BEST_EFFORT && type <= TASK_TYPE_HARD_REALTIME);
    assert(wcet <= deadline);
    assert(wcet <= period);
    assert(type != TASK_TYPE_BEST_EFFORT || weight > 0);

    trace_event(TRACE_SUBSYS_KERNEL, TRACE_EVENT_KERNEL_SCHED_REMOVE,
                152);
    scheduler_remove(dcb);

    /* Set task properties */
    dcb->type = type;
    dcb->deadline = deadline;
    dcb->wcet = wcet;
    dcb->period = period;
    dcb->release_time = (release == 0) ? kernel_now : release;
    dcb->weight = weight;

    make_runnable(dcb);
#endif

    return SYSRET(SYS_ERR_OK);
}
Beispiel #2
0
/*
 * Wake up one or more threads who are sleeping on "sleep address"
 * ADDR.
 */
void
thread_wakeup(const void *addr)
{
	int i, result;
	
	// meant to be called with interrupts off
	assert(curspl>0);
	
	// This is inefficient. Feel free to improve it.
	
	for (i=0; i<array_getnum(sleepers); i++) {
		struct thread *t = array_getguy(sleepers, i);
		if (t->t_sleepaddr == addr) {
			
			// Remove from list
			array_remove(sleepers, i);
			
			// must look at the same sleepers[i] again
			i--;

			/*
			 * Because we preallocate during thread_fork,
			 * this should never fail.
			 */
			result = make_runnable(t);
			assert(result==0);
		}
	}
}
Beispiel #3
0
/*
 *	Do an iteration of the swapper
 */
int do_swapper_run(register struct task_struct *swapin_target)
{
    while (make_runnable(swapin_target) == -1) {
	seg_t t = swap_strategy(swapin_target);
	if (!t || swap_out(t) == -1)
	    return -1;
    }
    return 0;
}
Beispiel #4
0
static void handle_event(event_t *event)
{
	thread_t *t = event->thread;
	// If nobody gives a shit, just leave
	if (!t) return;
	event->thread = NULL;

	// Schedule the thread we woke up
	make_runnable(t);
}
Beispiel #5
0
static void run_thread(void *threadp)
{
	thread_t *thread = threadp;
	//printf("running %d\n", thread->tid);
	bool runnable = true;
	// Run the thread until it has run for a while or has stopped being
	// runnable.
	for (int i = 0; runnable && i < MAX_ITERS; i++) {
		runnable = thread->cont(thread);
	}
	if (runnable) make_runnable(thread);
}
Beispiel #6
0
void register_channel_event(thread_t *thread, event_t *e)
{
	channel_t *ch = e->u.ch;

	// if there is data in the channel, keep running
	rt_mutex_lock(&ch->lock);
	if (Q_GET_HEAD(&ch->msgs)) {
		make_runnable(thread);
	} else {
		e->thread = thread;
	}
	rt_mutex_unlock(&ch->lock);
}
Beispiel #7
0
ER
rsm_tsk(ID tskid)
{
	TCB		*p_tcb;
	ER		ercd;

	LOG_RSM_TSK_ENTER(tskid);
	CHECK_TSKCTX_UNL();
	CHECK_TSKID(tskid);
	p_tcb = get_tcb(tskid);

	t_lock_cpu();
	if (p_tcb->p_tinib->tskatr == TA_NOEXS) {
		ercd = E_NOEXS;
	}
	else if (VIOLATE_ACPTN(p_tcb->p_tinib->acvct.acptn2)) {
		ercd = E_OACV;
	}
	else if (!TSTAT_SUSPENDED(p_tcb->tstat)) {
		ercd = E_OBJ;
	}
	else if (!TSTAT_WAITING(p_tcb->tstat)) {
		/*
		 *  強制待ち状態から実行できる状態への遷移
		 */
		p_tcb->tstat = TS_RUNNABLE;
		LOG_TSKSTAT(p_tcb);
		if (make_runnable(p_tcb)) {
			dispatch();
		}
		ercd = E_OK;
	}
	else {
		/*
		 *  二重待ち状態から待ち状態への遷移
		 */
		p_tcb->tstat &= ~TS_SUSPENDED;
		LOG_TSKSTAT(p_tcb);
		ercd = E_OK;
	}
	t_unlock_cpu();

  error_exit:
	LOG_RSM_TSK_LEAVE(ercd);
	return(ercd);
}
ER
rsm_tsk(ID tskid)
{
	TCB		*p_tcb;
	ER		ercd;

	LOG_RSM_TSK_ENTER(tskid);
	CHECK_TSKCTX_UNL();
	CHECK_ID(VALID_TSKID(tskid));
	p_tcb = get_tcb(tskid);

	lock_cpu();
	if (p_tcb->p_tinib->tskatr == TA_NOEXS) {
		ercd = E_NOEXS;
	}
	else if (!TSTAT_SUSPENDED(p_tcb->tstat)) {
		ercd = E_OBJ;
	}
	else if (!TSTAT_WAITING(p_tcb->tstat)) {
		/*
		 *  強制待ち状態から実行できる状態への遷移
		 */
		p_tcb->tstat = TS_RUNNABLE;
		LOG_TSKSTAT(p_tcb);
		make_runnable(p_tcb);
		if (p_runtsk != p_schedtsk) {
			dispatch();
		}
		ercd = E_OK;
	}
	else {
		/*
		 *  二重待ち状態から待ち状態への遷移
		 */
		p_tcb->tstat &= ~TS_SUSPENDED;
		LOG_TSKSTAT(p_tcb);
		ercd = E_OK;
	}
	unlock_cpu();

  error_exit:
	LOG_RSM_TSK_LEAVE(ercd);
	return(ercd);
}
Beispiel #9
0
/*
 *  待ち解除のためのタスク状態の更新
 *
 *  p_tcbで指定されるタスクを,待ち解除するようタスク状態を更新する.
 *  待ち解除するタスクが実行できる状態になる場合は,レディキューにつな
 *  ぐ.また,ディスパッチが必要な場合にはtrueを返す.
 */
Inline bool_t
make_non_wait(TCB *p_tcb)
{
	assert(TSTAT_WAITING(p_tcb->tstat));

	if (!TSTAT_SUSPENDED(p_tcb->tstat)) {
		/*
		 *  待ち状態から実行できる状態への遷移
		 */
		return(make_runnable(p_tcb));
	}
	else {
		/*
		 *  二重待ち状態から強制待ち状態への遷移
		 */
		p_tcb->tstat = TS_SUSPENDED;
		LOG_TSKSTAT(p_tcb);
		return(false);
	}
}
Beispiel #10
0
/* Handles timer interrupt.
 * By default, the timer fires at 18.222hz
 */
void timer_handler(struct regs *r)
{
    (void)r;    /* prevent 'unused' parameter warning */
    g_num_ticks++;

    if (get_current_thread() && get_current_thread()->id == 5) {
        DEBUGF("%s\n", "timer_handler in user!");
    }

    /* if the current thread has outlived the quantum, add it to the
     * run queue and schedule a new thread */
    thread_t* current = get_current_thread();
    if (current) {
        if (++current->num_ticks > THREAD_QUANTUM && preemption_enabled()) {
            /* DEBUGF("preempting thread %d\n", current->id); */
            make_runnable(current);
            g_need_reschedule = true;
        }
    }
}
Beispiel #11
0
/*
 *  待ち解除のためのタスク状態の更新
 *
 *  tcb で指定されるタスクを,待ち解除するようタスク状態を更新する.待
 *  ち解除するタスクが実行できる状態になる場合は,レディキューにつなぐ.
 *  また,ディスパッチが必要な場合には TRUE を返す.
 */
Inline BOOL
make_non_wait(TCB *tcb)
{
	assert(TSTAT_WAITING(tcb->tstat));

	if (!(TSTAT_SUSPENDED(tcb->tstat))) {
		/*
		 *  待ち状態から実行できる状態への遷移
		 */
		return(make_runnable(tcb));
	}
	else {
		/*
		 *  二重待ち状態から強制待ち状態への遷移
		 */
		tcb->tstat = TS_SUSPENDED;
		LOG_TSKSTAT(tcb);
		return(FALSE);
	}
}
Beispiel #12
0
/*
 * Wake up one thread that is sleeping on "sleep address"
 * ADDR.
 */
void
thread_wakeup_single(const void *addr)
{
	int result;
	
	// meant to be called with interrupts off
	assert(curspl>0);
	
	// This is inefficient. Feel free to improve it.
	
	
		struct thread *t = array_getguy(sleepers, 0);
		if (t->t_sleepaddr == addr) {
			
			// Remove from list
			array_remove(sleepers, 0);
			result = make_runnable(t);
			assert(result==0);
		
	}
}
/** @brief Function to wake up a thread waiting on the conditon
 *         variable
 *   
 *  Wake the first thread in the waiting list of the condition 
 *  variable.
 *
 *  @param *cv the cond variable we are signaling 
 *  @return void 
 */
void
cond_signal (cond_t * cv)
{
    assert (cv != NULL);

    cond_t *node_ptr = NULL;
    int tid = DEFAULT_TID;

    /* Lock the queue */
    spin_lock_request (&cv->spinlock);

    /* Get the head node(thread) in the queue */
    node_ptr = Q_GET_FRONT (&cv->waiting_list);

    /* If there are no thread in the queue, unlock and return */
    if (node_ptr == NULL) {
        lprintf ("No blocked threads!");
        spin_lock_release (&cv->spinlock);
        return;
    }		
    else {
        /* If get, remove the first node from the queue */
        Q_REMOVE (&cv->waiting_list, node_ptr, cond_link);
    }

    /* Got one waitting thread from the queue, unlock the queue */
    tid = node_ptr->tid;

    spin_lock_release (&cv->spinlock);

    /* Make that thread runnable */
    while (make_runnable (tid) < 0) {
        yield (tid);
    }

    /* Free */
    free (node_ptr);

    return;
}
Beispiel #14
0
SYSCALL ER
rsm_tsk(ID tskid)
{
	TCB	*tcb;
	UINT	tstat;
	ER	ercd;

	LOG_RSM_TSK_ENTER(tskid);
	CHECK_TSKCTX_UNL();
	CHECK_TSKID(tskid);
	tcb = get_tcb(tskid);

	t_lock_cpu();
	if (!(TSTAT_SUSPENDED(tstat = tcb->tstat))) {
		ercd = E_OBJ;
	}
	else if (!(TSTAT_WAITING(tstat))) {
		/*
		 *  強制待ち状態から実行できる状態への遷移
		 */
		if (make_runnable(tcb)) {
			dispatch();
		}
		ercd = E_OK;
	}
	else {
		/*
		 *  二重待ち状態から待ち状態への遷移
		 */
		tcb->tstat &= ~TS_SUSPENDED;
		LOG_TSKSTAT(tcb);
		ercd = E_OK;
	}
	t_unlock_cpu();

    exit:
	LOG_RSM_TSK_LEAVE(ercd);
	return(ercd);
}
Beispiel #15
0
/* FIXME: lots of missing argument checks in this function */
struct sysret
sys_dispatcher_setup(struct capability *to, capaddr_t cptr, int depth,
                     capaddr_t vptr, capaddr_t dptr, bool run, capaddr_t odptr)
{
    errval_t err = SYS_ERR_OK;
    assert(to->type == ObjType_Dispatcher);
    struct dcb *dcb = to->u.dispatcher.dcb;

    lpaddr_t lpaddr;

    /* 1. set cspace root */
    if (cptr != CPTR_NULL) {
        struct cte *root;
        err = caps_lookup_slot(&dcb_current->cspace.cap, cptr, depth,
                               &root, CAPRIGHTS_READ);
        if (err_is_fail(err)) {
            return SYSRET(err_push(err, SYS_ERR_DISP_CSPACE_ROOT));
        }
        if (root->cap.type != ObjType_CNode) {
            return SYSRET(err_push(err, SYS_ERR_DISP_CSPACE_INVALID));
        }
        err = caps_copy_to_cte(&dcb->cspace, root, false, 0, 0);
        if (err_is_fail(err)) {
            return SYSRET(err_push(err, SYS_ERR_DISP_CSPACE_ROOT));
        }
    }

    /* 2. set vspace root */
    if (vptr != CPTR_NULL) {
        struct capability *vroot;
        err = caps_lookup_cap(&dcb_current->cspace.cap, vptr, CPTR_BITS,
                              &vroot, CAPRIGHTS_WRITE);
        if (err_is_fail(err)) {
            return SYSRET(err_push(err, SYS_ERR_DISP_VSPACE_ROOT));
        }

        // Insert as dispatcher's VSpace root
        switch(vroot->type) {
        case ObjType_VNode_x86_64_pml4:
            dcb->vspace =
                (lvaddr_t)gen_phys_to_local_phys(vroot->u.vnode_x86_64_pml4.base);
            break;
#ifdef CONFIG_PAE
        case ObjType_VNode_x86_32_pdpt:
            dcb->vspace =
                (lvaddr_t)gen_phys_to_local_phys(vroot->u.vnode_x86_32_pdpt.base);
            break;
#else
        case ObjType_VNode_x86_32_pdir:
            dcb->vspace =
                (lvaddr_t)gen_phys_to_local_phys(vroot->u.vnode_x86_32_pdir.base);
            break;
#endif
        case ObjType_VNode_ARM_l1:
            dcb->vspace =
                (lvaddr_t)gen_phys_to_local_phys(vroot->u.vnode_arm_l1.base);
            break;

        default:
            return SYSRET(err_push(err, SYS_ERR_DISP_VSPACE_INVALID));
        }
    }

    /* 3. set dispatcher frame pointer */
    if (dptr != CPTR_NULL) {
        struct cte *dispcte;
        err = caps_lookup_slot(&dcb_current->cspace.cap, dptr, CPTR_BITS,
                               &dispcte, CAPRIGHTS_WRITE);
        if (err_is_fail(err)) {
            return SYSRET(err_push(err, SYS_ERR_DISP_FRAME));
        }
        struct capability *dispcap = &dispcte->cap;
        if (dispcap->type != ObjType_Frame) {
            return SYSRET(err_push(err, SYS_ERR_DISP_FRAME_INVALID));
        }

        /* FIXME: check rights, check size */

        lpaddr = gen_phys_to_local_phys(dispcap->u.frame.base);
        dcb->disp = local_phys_to_mem(lpaddr);
        // Copy the cap to dcb also
        err = caps_copy_to_cte(&dcb->disp_cte, dispcte, false, 0, 0);
        // If copy fails, something wrong in kernel
        assert(err_is_ok(err));
    }

    /* 5. Make runnable if desired -- Set pointer to ipi_data */
    if (run) {
        if (dcb->vspace == 0 ||
        (!dcb->is_vm_guest &&
        (dcb->disp == 0 || dcb->cspace.cap.type != ObjType_CNode))) {
            return SYSRET(err_push(err, SYS_ERR_DISP_NOT_RUNNABLE));
        }

        // XXX: dispatchers run disabled the first time they start
        dcb->disabled = 1;
        //printf("DCB: %p %.*s\n", dcb, DISP_NAME_LEN, dcb->disp->name);
        make_runnable(dcb);
    }

    /* 6. Copy domain ID off given dispatcher */
    if(odptr != CPTR_NULL) {
        struct capability *odisp;
        err = caps_lookup_cap(&dcb_current->cspace.cap, odptr, CPTR_BITS,
                              &odisp, CAPRIGHTS_READ_WRITE);
        if (err_is_fail(err)) {
            return SYSRET(err_push(err, SYS_ERR_DISP_OCAP_LOOKUP));
        }
        dcb->domain_id = odisp->u.dispatcher.dcb->domain_id;
    }

    /* 7. (HACK) Set current core id */
    {
    struct dispatcher_shared_generic *disp =
        get_dispatcher_shared_generic(dcb->disp);
    disp->curr_core_id = my_core_id;
    }

    if(!dcb->is_vm_guest) {
        struct dispatcher_shared_generic *disp =
                    get_dispatcher_shared_generic(dcb->disp);
        err = trace_new_application(disp->name, (uintptr_t) dcb);

        if (err == TRACE_ERR_NO_BUFFER) {
            // Try to use the boot buffer.
            trace_new_boot_application(disp->name, (uintptr_t) dcb);
        }
    }

    return SYSRET(SYS_ERR_OK);
}
Beispiel #16
0
/*
 * High level, machine-independent context switch code.
 */
static
void
mi_switch(threadstate_t nextstate)
{
	struct thread *cur, *next;
	int result;
	
	/* Interrupts should already be off. */
	assert(curspl>0);

	if (curthread != NULL && curthread->t_stack != NULL) {
		/*
		 * Check the magic number we put on the bottom end of
		 * the stack in thread_fork. If these assertions go
		 * off, it most likely means you overflowed your stack
		 * at some point, which can cause all kinds of
		 * mysterious other things to happen.
		 */
		assert(curthread->t_stack[0] == (char)0xae);
		assert(curthread->t_stack[1] == (char)0x11);
		assert(curthread->t_stack[2] == (char)0xda);
		assert(curthread->t_stack[3] == (char)0x33);
	}
	
	/* 
	 * We set curthread to NULL while the scheduler is running, to
	 * make sure we don't call it recursively (this could happen
	 * otherwise, if we get a timer interrupt in the idle loop.)
	 */
	if (curthread == NULL) {
		return;
	}
	cur = curthread;
	curthread = NULL;

	/*
	 * Stash the current thread on whatever list it's supposed to go on.
	 * Because we preallocate during thread_fork, this should not fail.
	 */

	if (nextstate==S_READY) {
		result = make_runnable(cur);
	}
	else if (nextstate==S_SLEEP) {
		/*
		 * Because we preallocate sleepers[] during thread_fork,
		 * this should never fail.
		 */
		result = array_add(sleepers, cur);
	}
	else {
		assert(nextstate==S_ZOMB);
		result = array_add(zombies, cur);
	}
	assert(result==0);

	/*
	 * Call the scheduler (must come *after* the array_adds)
	 */

	next = scheduler();

	/* update curthread */
	curthread = next;
	
	/* 
	 * Call the machine-dependent code that actually does the
	 * context switch.
	 */
	md_switch(&cur->t_pcb, &next->t_pcb);
	
	/*
	 * If we switch to a new thread, we don't come here, so anything
	 * done here must be in mi_threadstart() as well, or be skippable,
	 * or not apply to new threads.
	 *
	 * exorcise is skippable; as_activate is done in mi_threadstart.
	 */

	exorcise();

	if (curthread->t_vmspace) {
		as_activate(curthread->t_vmspace);
	}
}
Beispiel #17
0
/*
 * Create a new thread based on an existing one.
 * The new thread has name NAME, and starts executing in function FUNC.
 * DATA1 and DATA2 are passed to FUNC.
 */
int
thread_fork(const char *name, 
	    void *data1, unsigned long data2,
	    void (*func)(void *, unsigned long),
	    pid_t *ret)
{
	struct thread *newguy;
	int s, result;

	/* Allocate a thread */
	newguy = thread_create(name);
	if (newguy==NULL) {
		return ENOMEM;
	}

	/* ASST1: Allocate a pid */
	result = pid_alloc(&newguy->t_pid);
	if (result != 0) {
	  kfree(newguy->t_name);
	  kfree(newguy);
	  return result;
	}

	/* Allocate a stack */
	newguy->t_stack = kmalloc(STACK_SIZE);
	if (newguy->t_stack==NULL) {
		pid_unalloc(newguy->t_pid); /* ASST1: cleanup pid on fail */
		kfree(newguy->t_name);
		kfree(newguy);
		return ENOMEM;
	}

	/* stick a magic number on the bottom end of the stack */
	newguy->t_stack[0] = 0xae;
	newguy->t_stack[1] = 0x11;
	newguy->t_stack[2] = 0xda;
	newguy->t_stack[3] = 0x33;

	/* Inherit the current directory */
	if (curthread->t_cwd != NULL) {
		VOP_INCREF(curthread->t_cwd);
		newguy->t_cwd = curthread->t_cwd;
	}

	/* ASST1: copy address space if there is one */
	if (curthread->t_vmspace != NULL) {
		result = as_copy(curthread->t_vmspace, &newguy->t_vmspace);
		if (result) {
			pid_unalloc(newguy->t_pid); 
			kfree(newguy->t_name);
			kfree(newguy->t_stack);
			kfree(newguy);			
			return ENOMEM;
		}
	}


	/* Set up the pcb (this arranges for func to be called) */
	md_initpcb(&newguy->t_pcb, newguy->t_stack, data1, data2, func);

	/* Interrupts off for atomicity */
	s = splhigh();

	/*
	 * Make sure our data structures have enough space, so we won't
	 * run out later at an inconvenient time.
	 */
	result = array_preallocate(sleepers, numthreads+1);
	if (result) {
		goto fail;
	}
	result = array_preallocate(zombies, numthreads+1);
	if (result) {
		goto fail;
	}

	/* Do the same for the scheduler. */
	result = scheduler_preallocate(numthreads+1);
	if (result) {
		goto fail;
	}

	/* Make the new thread runnable */
	result = make_runnable(newguy);
	if (result != 0) {
		goto fail;
	}

	/*
	 * Increment the thread counter. This must be done atomically
	 * with the preallocate calls; otherwise the count can be
	 * temporarily too low, which would obviate its reason for
	 * existence.
	 */
	numthreads++;

	/* Done with stuff that needs to be atomic */
	splx(s);

	/*
	 * Return new thread structure if it's wanted.  Note that
	 * using the thread structure from the parent thread should be
	 * done only with caution, because in general the child thread
	 * might exit at any time.
	 */
	if (ret != NULL) {
		*ret = newguy->t_pid; /* ASST1 return pid, not thread struct */
	} else {
		/* Not returning the pid... better detach the new thread */
		result = thread_detach(newguy->t_pid);
		assert(result == 0); /* can't fail. */
	}

	return 0;

 fail:
	splx(s);

	/* ASST1: cleanup pid and vmspace on fail */
	pid_unalloc(newguy->t_pid); 

	if (newguy->t_vmspace) {
		as_destroy(newguy->t_vmspace);
	}

	if (newguy->t_cwd != NULL) {
		VOP_DECREF(newguy->t_cwd);
	}
	kfree(newguy->t_stack);
	kfree(newguy->t_name);
	kfree(newguy);

	return result;
}
Beispiel #18
0
/*
 * Create a new thread based on an existing one.
 * The new threaD has name NAME, and starts executing in function FUNC.
 * DATA1 and DATA2 are passed to FUNC.
 */
int
thread_fork(const char *name, 
	    void *data1, unsigned long data2,
	    void (*func)(void *, unsigned long),
	    struct thread **ret)
{
	
	struct thread *newguy;
	int s, result;

	/* Allocate a thread */
	newguy = thread_create(name);
	if (newguy==NULL) {
		return ENOMEM;
	}

	/* Allocate a stack */
	newguy->t_stack = kmalloc(STACK_SIZE);
	if (newguy->t_stack==NULL) {
		kfree(newguy->t_name);
		kfree(newguy);
		return ENOMEM;
	}

	/* stick a magic number on the bottom end of the stack */
	newguy->t_stack[0] = 0xae;
	newguy->t_stack[1] = 0x11;
	newguy->t_stack[2] = 0xda;
	newguy->t_stack[3] = 0x33;

	/* Inherit the current directory */
	if (curthread->t_cwd != NULL) {
		VOP_INCREF(curthread->t_cwd);
		newguy->t_cwd = curthread->t_cwd;
	}
	
	/* Set up the pcb (this arranges for func to be called) */
	md_initpcb(&newguy->t_pcb, newguy->t_stack, data1, data2, func);

	/* Interrupts off for atomicity */
	s = splhigh();
	
	/*
	 * Make sure our data structures have enough space, so we won't
	 * run out later at an inconvenient time.
	 */
	result = array_preallocate(sleepers, numthreads+1);
	if (result) {
		goto fail;
	}
	result = array_preallocate(zombies, numthreads+1);
	if (result) {
		goto fail;
	}

	/* Do the same for the scheduler. */
	result = scheduler_preallocate(numthreads+1);
	if (result) {
		goto fail;
	}

	/* Make the new thread runnable */
	result = make_runnable(newguy);
	if (result != 0) {
		goto fail;
	}

	/*
	 * Increment the thread counter. This must be done atomically
	 * with the preallocate calls; otherwise the count can be
	 * temporarily too low, which would obviate its reason for
	 * existence.
	 */
	numthreads++;

	//The child's Parent PID (curthread->pid) is stored in its PPID
	newguy->parent_pid = curthread->pid;


	struct pid_node *temp;
	struct pid_node *temp2;	
	
	temp = find_node(curthread->pid); //return the pid_node of the parent.
		
	temp2 = find_node(newguy->pid); //return the pid_node of the forked child.

	if (temp->next_children == NULL) //this means that the parent has no children.
	{
		temp->next_children = temp2;
	}	
	else{ //HAS CHILDREN.
		temp = temp->next_children;
		while (temp->next_siblings != NULL){
			temp = temp->next_siblings;
		}
		temp->next_siblings = temp2;		
	
	}	
	

	/* Done with stuff that needs to be atomic */
	splx(s);

	/*
	 * Return new thread structure if it's wanted.  Note that
	 * using the thread structure from the parent thread should be
	 * done only with caution, because in general the child thread
	 * might exit at any time.
	 */
	 
	
	
	if (ret != NULL) {
		*ret = newguy;
	}

	return 0;
	

 fail:
	splx(s);
	if (newguy->t_cwd != NULL) {
		VOP_DECREF(newguy->t_cwd);
	}
	kfree(newguy->t_stack);
	kfree(newguy->t_name);
	kfree(newguy);

	return result;
}
Beispiel #19
0
int main(int argc, char **argv)
{
    int tasks = 0, alltasks = MAXTASKS, runtime, quantum = 1;

    if(argc < 3) {
        printf("Usage: %s <config.cfg> <runtime> [quantum]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    runtime = atoi(argv[2]);
    if(argc >= 4) {
        quantum = atoi(argv[3]);
    }

    sched = malloc(sizeof(struct dcb) * runtime * alltasks);
    allptrs = calloc(alltasks, sizeof(struct dcb *));

    FILE *f = fopen(argv[1], "r");
    assert(f != NULL);
    bool readline = true;

    for(kernel_now = 0; kernel_now < runtime; kernel_now++) {
        unsigned long time, wcet, period, weight, id, blocktime, deadline, rd;
        char b[512], *r;

        for(;;) {
            if(readline) {
                do {
                    r = fgets(b, 512, f);
                } while(r != NULL && (b[0] == '#' || b[0] == '\n'));

                if(r == NULL) {
                    break;
                }
            } else {
                readline = true;
            }

            if((rd = sscanf(b, "%lu H %lu %lu %lu %lu", &time, &wcet, &period, &blocktime, &deadline)) >= 4) {
                if(time != kernel_now) { readline = false; break; }
                // Create new hard real-time task
                struct dcb *dcb = malloc(sizeof(struct dcb));
                init_dcb(dcb, tasks);
                dcb->type = TASK_TYPE_HARD_REALTIME;
                dcb->wcet = wcet;
                dcb->period = period;
                dcb->blocktime = blocktime;
                dcb->release_time = kernel_now;
                snprintf(dcb->dsg.name, DISP_NAME_LEN, "h %d", tasks);
                if(rd == 5) {
                    dcb->deadline = deadline;
                } else {
                    dcb->deadline = period;
                }
                make_runnable(dcb);
                assert(tasks < MAXTASKS);
                allptrs[tasks++] = dcb;
            } else if(sscanf(b, "%lu S %lu %lu", &time, &wcet, &period) == 3) {
                if(time != kernel_now) { readline = false; break; }
                // Create new soft real-time task
                struct dcb *dcb = malloc(sizeof(struct dcb));
                init_dcb(dcb, tasks);
                dcb->type = TASK_TYPE_SOFT_REALTIME;
                dcb->wcet = wcet;
                dcb->period = period;
                snprintf(dcb->dsg.name, DISP_NAME_LEN, "s %d", tasks);
                make_runnable(dcb);
                assert(tasks < MAXTASKS);
                allptrs[tasks++] = dcb;
            } else if(sscanf(b, "%lu B %lu", &time, &weight) == 2) {
                if(time != kernel_now) { readline = false; break; }
                // Create new best-effort task
                struct dcb *dcb = malloc(sizeof(struct dcb));
                init_dcb(dcb, tasks);
                dcb->type = TASK_TYPE_BEST_EFFORT;
                dcb->weight = weight;
                snprintf(dcb->dsg.name, DISP_NAME_LEN, "b %d", tasks);
                make_runnable(dcb);
                assert(tasks < MAXTASKS);
                allptrs[tasks++] = dcb;
            } else if(sscanf(b, "%lu d %lu", &time, &id) == 2) {
                if(time != kernel_now) { readline = false; break; }
                // Delete task with given ID
                assert(id < MAXTASKS);
                scheduler_remove(allptrs[id]);
            } else if(sscanf(b, "%lu r %lu", &time, &id) == 2) {
                if(time != kernel_now) { readline = false; break; }
                // Re-release task with given ID
                assert(id < MAXTASKS);
                if(allptrs[id]->type != TASK_TYPE_BEST_EFFORT) {
                    allptrs[id]->release_time = kernel_now;
                }
                make_runnable(allptrs[id]);
            } else if(sscanf(b, "%lu y %lu", &time, &id) == 2) {
                if(time != kernel_now) { readline = false; break; }
                // Yield task with given ID
                assert(id < MAXTASKS);
                scheduler_yield(allptrs[id]);
            } else if(sscanf(b, "%lu c %lu", &time, &id) == 2) {
                if(time != kernel_now) { readline = false; break; }
                // Context switch to task with given ID
                assert(id < MAXTASKS);
                dcb_current = allptrs[id];
                continue;
            } else {
                fprintf(stderr, "Invalid line: %s\n", b);
                abort();
            }

            dcb_current = schedule();
        }

        for(int i = 0; i < alltasks; i++) {
            struct dcb *cd = allptrs[i];
            if(cd != NULL) {
                cd->dispatched = false;

#if 0
                if(cd->type == TASK_TYPE_HARD_REALTIME) {
                    if(cd->etime >= cd->blocktime) {
                        scheduler_remove(cd);
                    }
                }
#endif
            }
        }

        if(kernel_now % quantum == 0) {
            dcb_current = schedule();
        }

        if(dcb_current != NULL) {
            dcb_current->dispatched = true;

            /* printf("%4d: dispatching %2d, release time: %4lu, deadline: %4lu, period: %3lu, WCET: %3lu/%3lu\n", kernel_now, dcb_current->id, dcb_current->release_time, dcb_current->deadline, dcb_current->period, dcb_current->etime, dcb_current->wcet); */
        }
        for(int i = 0; i < alltasks; i++) {
            if(allptrs[i] != NULL) {
                sched[kernel_now * alltasks + i] = *allptrs[i];
            }
        }
    }

    fclose(f);

    // Print schedule
    printf("     ");
    for(int t = 0; t < runtime; t++) {
        if(t % 1000 == 0) {
            printf("%d", (t / 1000) % 10);
        } else {
            printf(" ");
        }
    }
    printf("\n");
    printf("     ");
    for(int t = 0; t < runtime; t++) {
        if(t % 100 == 0) {
            printf("%d", (t / 100) % 10);
        } else {
            printf(" ");
        }
    }
    printf("\n");
    printf("     ");
    for(int t = 0; t < runtime; t++) {
        if(t % 10 == 0) {
            printf("%d", (t / 10) % 10);
        } else {
            printf(" ");
        }
    }
    printf("\n");

    printf("     ");
    for(int t = 0; t < runtime; t++) {
        printf("%d", t % 10);
    }
    printf("\n");

    for(int i = 0; i < tasks; i++) {
        struct dcb *ct = allptrs[i];
        printf("%c%2d: ", typechar(ct->type), i);
        for(int t = 0; t < runtime; t++) {
            struct dcb *s = &sched[t * alltasks + i];

            if(s->dispatched) {
                printf("#");
            } else {
                printf(" ");
            }
        }
        printf("\n");
        printf("     ");
        for(int t = 0; t < runtime; t++) {
            struct dcb *s = &sched[t * alltasks + i];

            if(s->release_time == t) {
                printf("r");
            } else {
                printf(" ");
            }
        }
        printf("\n");
    }

    free(sched);
    free(allptrs);
    return 0;
}
ER
mig_tsk(ID tskid, ID prcid)
{
	TCB		*p_tcb;
	ER		ercd = E_OK;
	PCB		*t_p_pcb;
	PCB		*f_p_pcb;
	bool_t	dspreq = false;
	PCB		*my_p_pcb;
#ifdef TOPPERS_SYSTIM_LOCAL
	EVTTIM	left_time;
#endif /* TOPPERS_SYSTIM_LOCAL */

	LOG_MIG_TSK_ENTER(tskid, prcid);
	CHECK_TSKCTX_UNL();
	CHECK_TSKID_SELF(tskid);
	CHECK_PRCID_INI(prcid);

	t_lock_cpu();
	p_tcb = get_tcb_self(tskid, get_my_p_pcb());
	prcid = (prcid == TPRC_INI)? p_tcb->p_tinib->iaffinity : prcid;
	T_CHECK_MIG(p_tcb->p_tinib->affinity_mask, prcid);

	/* 現在割り付けられているプロセッサと移動先のプロセッサのタスクロックを取得 */
	t_acquire_dual_tsk_lock(p_tcb, prcid, &f_p_pcb, &t_p_pcb);
	my_p_pcb = get_my_p_pcb();
	if (f_p_pcb != my_p_pcb) {
		/*
		 * 自タスクと同じプロセッサに割り付けられているタスクでなけれ
		 * ばエラー. mig_tsk を呼び出したタスクがシステムコール呼出し後,
		 * マイグレートされた場合にも,ここでエラーとなる
		 */
		ercd = E_OBJ;
	}
	else if (TSTAT_RUNNABLE(p_tcb->tstat)){
		/* 実行可能状態 */
		if(p_tcb == my_p_pcb->p_runtsk) {
			/* 自タスクに対して発行 */
			if (!(my_p_pcb->dspflg)) {
				/* ディスパッチ禁止中ならエラー */
				ercd = E_CTX;
			}
			else if (t_p_pcb == my_p_pcb) {
				/* 同一プロセッサを指定 */
				/* 優先順位を同一優先度のタスクの中で最低とする */
				dspreq = set_lowest_precedence(p_tcb, my_p_pcb);
				ercd = E_OK; 
			}
			else {
				/* マイグレーション要求を処理 */
				LOG_TSKMIG(p_tcb, my_p_pcb->prcid, prcid);
				dispatch_and_migrate(prcid);
				/* ここに戻ってくる時にはロックは解放されている */
				ercd = E_OK;
				t_unlock_cpu();
				goto error_exit;
			}
		} 
		else {
			/* 他タスクの場合 */
			if (t_p_pcb == my_p_pcb) {
				/* 同一プロセッサを指定 */
				/* 
				 *  優先順位を同一優先度のタスクの中で最低とする.
				 *  対象のタスクは最高優先順位のタスクでないため,タ
				 *  スク切り替えは発生しない
				 */
				(void)set_lowest_precedence(p_tcb, my_p_pcb);
				ercd = E_OK; 
			}
			else {
				/* 異なるプロセッサを指定 */
				/* レディーキューから外す */
				make_non_runnable(p_tcb);
				/* pcb の書き換え */
				p_tcb->p_pcb = t_p_pcb;
				LOG_TSKMIG(p_tcb, my_p_pcb->prcid, prcid);
				/* 移行先のプロセッサでmake_runnable する*/
				if (make_runnable(p_tcb)) {
					dispatch_request(t_p_pcb);
				}
				ercd = E_OK;
			}
		}
	}
	else if (TSTAT_DORMANT(p_tcb->tstat)) {
		/* 休止状態 */
		LOG_TSKMIG(p_tcb, my_p_pcb->prcid, prcid);
		p_tcb->p_pcb = t_p_pcb;
		ercd = E_OK;
	}
	else {
		/* 待ち状態 */
		if ((p_tcb->tmevtb).callback == NULL) {
			/* 時間待ちでない場合 */
			LOG_TSKMIG(p_tcb, my_p_pcb->prcid, prcid);
			p_tcb->p_pcb = t_p_pcb;
			ercd = E_OK;
		}
		else {
			/*
			 * 時間待ちの場合 グローバルタイマ方式 なら必要なし
			 */
#ifdef TOPPERS_SYSTIM_LOCAL
			/* キューから削除 */
			left_time = tmevtb_dequeue(f_p_pcb->p_tevtcb, &(p_tcb->tmevtb));
			LOG_TSKMIG(p_tcb, my_p_pcb->prcid, prcid);
			/* 移動先のプロセッサのキューに挿入 */
			tmevtb_insert(t_p_pcb->p_tevtcb, &(p_tcb->tmevtb), base_time(t_p_pcb->p_tevtcb) + left_time);
#else  /* TOPPERS_SYSTIM_GLOBAL */
			LOG_TSKMIG(p_tcb, my_p_pcb->prcid, prcid);
#endif /* TOPPERS_SYSTIM_GLOBAL */
			p_tcb->p_pcb = t_p_pcb;
			ercd = E_OK;
		}
	}
	release_dual_tsk_lock(f_p_pcb, t_p_pcb);
	if (dspreq) {
		dispatch();
	}
	t_unlock_cpu();

  error_exit:
	LOG_MIG_TSK_LEAVE(ercd);
	return(ercd);
}
Beispiel #21
0
/*
 * Create a new thread based on an existing one.
 * The new thread has name NAME, and starts executing in function FUNC.
 * DATA1 and DATA2 are passed to FUNC.
 */
int
thread_fork(const char *name,
            void *data1, unsigned long data2,
            void (*func)(void *, unsigned long),
            struct thread **ret)
{
	struct thread *newguy;
	int s, result;
    
	/* Allocate a thread */
	newguy = thread_create(name);
	if (newguy==NULL) {
		return ENOMEM;
	}
    
	/* Allocate a stack */
	newguy->t_stack = kmalloc(STACK_SIZE);
	if (newguy->t_stack==NULL) {
		kfree(newguy->t_name);
		kfree(newguy);
		return ENOMEM;
	}
    
	/* stick a magic number on the bottom end of the stack */
	newguy->t_stack[0] = 0xae;
	newguy->t_stack[1] = 0x11;
	newguy->t_stack[2] = 0xda;
	newguy->t_stack[3] = 0x33;
    
	/* Inherit the current directory */
	if (curthread->t_cwd != NULL) {
		VOP_INCREF(curthread->t_cwd);
		newguy->t_cwd = curthread->t_cwd;
	}
    
#if OPT_A2
    
    if(curthread->forkcalled) {
        int i, res, spl;
        //copy address space
        res = as_copy(curthread->t_vmspace,&newguy->t_vmspace);
        if (res) {
            //kprintf("as_copy failed in thread_fork, curthread_vmspace_addr: %p, newguy_vmspace_addr: %p\n", curthread->t_vmspace, newguy->t_vmspace);
            return res;
        }
        as_activate(curthread->t_vmspace); /* FIXME if wrong comment this out and retry */
        //copy vnodes
//        spl = splhigh();
        for(i=3;i<MAX_OPENED_FILES;i++) {
            if(curthread->files[i]!=NULL) {
                newguy->files[i] = files_create(kstrdup(curthread->files[i]->filename), curthread->files[i]->vn, curthread->files[i]->flags);
                VOP_INCOPEN(newguy->files[i]->vn);
                VOP_INCREF(newguy->files[i]->vn);
            } else {
                break;
            }
        }
//        splx(spl);
        
        curthread->forkcalled = 0;
    }
#endif
    
	/* Set up the pcb (this arranges for func to be called) */
	md_initpcb(&newguy->t_pcb, newguy->t_stack, data1, data2, func);
    
	/* Interrupts off for atomicity */
	s = splhigh();
    
	/*
	 * Make sure our data structures have enough space, so we won't
	 * run out later at an inconvenient time.
	 */
	result = array_preallocate(sleepers, numthreads+1);
	if (result) {
		goto fail;
	}
	result = array_preallocate(zombies, numthreads+1);
	if (result) {
		goto fail;
	}
    
	/* Do the same for the scheduler. */
	result = scheduler_preallocate(numthreads+1);
	if (result) {
		goto fail;
	}
    
	/* Make the new thread runnable */
	result = make_runnable(newguy);
	if (result != 0) {
		goto fail;
	}
    
	/*
	 * Increment the thread counter. This must be done atomically
	 * with the preallocate calls; otherwise the count can be
	 * temporarily too low, which would obviate its reason for
	 * existence.
	 */
	numthreads++;
    
	/* Done with stuff that needs to be atomic */
	splx(s);
    
	/*
	 * Return new thread structure if it's wanted.  Note that
	 * using the thread structure from the parent thread should be
	 * done only with caution, because in general the child thread
	 * might exit at any time.
	 */
	if (ret != NULL) {
		*ret = newguy;
	}
    
	return 0;
    
fail:
	splx(s);
	if (newguy->t_cwd != NULL) {
		VOP_DECREF(newguy->t_cwd);
	}
	kfree(newguy->t_stack);
	kfree(newguy->t_name);
	kfree(newguy);
    
	return result;
}
Beispiel #22
0
/*
 * Create a new thread based on an existing one.
 * The new thread has name NAME, and starts executing in function FUNC.
 * DATA1 and DATA2 are passed to FUNC.
 */
int
thread_fork(const char *name,
            void *data1, unsigned long data2,
            void (*func)(void *, unsigned long),
            struct thread **ret)
{
    struct thread *newguy;
    int s, result;

    /* Allocate a thread */
    newguy = thread_create(name);
    //kprintf("New thread created\n");
    if (newguy == NULL)
    {
        return ENOMEM;
    }

    /* Allocate a stack */
	    //newguy->t_stack = kmalloc(sizeof(char*));
	//kprintf("%d\n",sizeof(char*));
	//kprintf("%d\n",STACK_SIZE);	
    newguy->t_stack = kmalloc(STACK_SIZE);
    //kprintf("stack allocated!!!\n");
    if (newguy->t_stack == NULL)
    {
	kprintf("new guy have a null stack?!\n");
        kfree(newguy->t_name);
        kfree(newguy);
        return ENOMEM;
    }

    /* stick a magic number on the bottom end of the stack */
    newguy->t_stack[0] = 0xae;
    newguy->t_stack[1] = 0x11;
    newguy->t_stack[2] = 0xda;
    newguy->t_stack[3] = 0x33;

    /* Inherit the current directory */
    //kprintf("inheritting current directory...\n");
    if (curthread->t_cwd != NULL)
    {
        VOP_INCREF(curthread->t_cwd);
        newguy->t_cwd = curthread->t_cwd;
    }
    //kprintf("start setting up pcb!!!\n");
    /* Set up the pcb (this arranges for func to be called) */
    md_initpcb(&newguy->t_pcb, newguy->t_stack, data1, data2, func);

    /* Interrupts off for atomicity */
    s = splhigh();

    // create process for this thread
    result = create_process (newguy);
   // kprintf("process created!!!\n");
    if (result)
    {
        goto fail;
    }

    /*
     * Make sure our data structures have enough space, so we won't
     * run out later at an inconvenient time.
     */
    result = array_preallocate(sleepers, numthreads + 1);
    if (result)
    {
        goto fail;
    }
    result = array_preallocate(zombies, numthreads + 1);
    if (result)
    {
        goto fail;
    }

    /* Do the same for the scheduler. */
    result = scheduler_preallocate(numthreads + 1);
    if (result)
    {
        goto fail;
    }

    /* Make the new thread runnable */
	//kprintf("before make_runcable! - pid %d\n", curthread->t_pid);
    result = make_runnable(newguy);
	//kprintf("magical make_runcable! - pid %d\n", curthread->t_pid);
    if (result != 0)
    {
        goto fail;
    }

    /*
     * Increment the thread counter. This must be done atomically
     * with the preallocate calls; otherwise the count can be
     * temporarily too low, which would obviate its reason for
     * existence.
     */
    numthreads++;

    /* Done with stuff that needs to be atomic */
    splx(s);

    /*
     * Return new thread structure if it's wanted.  Note that
     * using the thread structure from the parent thread should be
     * done only with caution, because in general the child thread
     * might exit at any time.
     */
    if (ret != NULL)
    {
        *ret = newguy;
    }

    return 0;

fail:
    splx(s);
    if (newguy->t_cwd != NULL)
    {
        VOP_DECREF(newguy->t_cwd);
    }
    kfree(newguy->t_stack);
    kfree(newguy->t_name);
    kfree(newguy);

    return result;
}
Beispiel #23
0
struct sysret sys_yield(capaddr_t target)
{
    dispatcher_handle_t handle = dcb_current->disp;
    struct dispatcher_shared_generic *disp =
        get_dispatcher_shared_generic(handle);


    debug(SUBSYS_DISPATCH, "%.*s yields%s\n", DISP_NAME_LEN, disp->name,
          !disp->haswork && disp->lmp_delivered == disp->lmp_seen
           ? " and is removed from the runq" : "");

    if (!disp->disabled) {
        printk(LOG_ERR, "SYSCALL_YIELD while enabled\n");
        return SYSRET(SYS_ERR_CALLER_ENABLED);
    }

    struct capability *yield_to = NULL;
    if (target != CPTR_NULL) {
        errval_t err;

        /* directed yield */
        err = caps_lookup_cap(&dcb_current->cspace.cap, target, CPTR_BITS,
                              &yield_to, CAPRIGHTS_READ);
        if (err_is_fail(err)) {
            return SYSRET(err);
        } else if (yield_to == NULL ||
                   (yield_to->type != ObjType_EndPoint
                    && yield_to->type != ObjType_Dispatcher)) {
            return SYSRET(SYS_ERR_INVALID_YIELD_TARGET);
        }
        /* FIXME: check rights? */
    }

    disp->disabled = false;
    dcb_current->disabled = false;

    // Remove from queue when no work and no more messages and no missed wakeup
    systime_t wakeup = disp->wakeup;
    if (!disp->haswork && disp->lmp_delivered == disp->lmp_seen
        && (wakeup == 0 || wakeup > (kernel_now + kcb_current->kernel_off))) {

        trace_event(TRACE_SUBSYS_NNET, TRACE_EVENT_NNET_SCHED_REMOVE,
            (uint32_t)(lvaddr_t)dcb_current & 0xFFFFFFFF);
        trace_event(TRACE_SUBSYS_KERNEL, TRACE_EVENT_KERNEL_SCHED_REMOVE,
                151);

        scheduler_remove(dcb_current);
        if (wakeup != 0) {
            wakeup_set(dcb_current, wakeup);
        }
    } else {
        // Otherwise yield for the timeslice
        scheduler_yield(dcb_current);
    }

    if (yield_to != NULL) {
        struct dcb *target_dcb = NULL;
        if (yield_to->type == ObjType_EndPoint) {
            target_dcb = yield_to->u.endpoint.listener;
        } else if (yield_to->type == ObjType_Dispatcher) {
            target_dcb = yield_to->u.dispatcher.dcb;
        } else {
            panic("invalid type in yield cap");
        }

        trace_event(TRACE_SUBSYS_NNET, TRACE_EVENT_NNET_YIELD,
            (uint32_t)(lvaddr_t)target_dcb & 0xFFFFFFFF);
        make_runnable(target_dcb);
        dispatch(target_dcb);
    } else {
//        trace_event(TRACE_SUBSYS_BNET, TRACE_EVENT_BNET_YIELD,
//            0);

        /* undirected yield */
        dispatch(schedule());
    }

    panic("Yield returned!");
}
Beispiel #24
0
/*
 * Create a new thread based on an existing one.
 * The new thread has name NAME, and starts executing in function FUNC.
 * DATA1 and DATA2 are passed to FUNC.
 */
int
thread_fork(const char *name, 
	    void *data1, unsigned long data2,
	    void (*func)(void *, unsigned long),
	    struct thread **ret)
{
    
	struct thread *newguy;
	int s, result;

	/* Allocate a thread */
	newguy = thread_create(name);
	if (newguy==NULL) {
		return ENOMEM;
	}

	/* Allocate a stack */
	newguy->t_stack = kmalloc(STACK_SIZE);
	if (newguy->t_stack==NULL) {
		kfree(newguy->t_name);
		kfree(newguy);
		return ENOMEM;
	}

	/* stick a magic number on the bottom end of the stack */
	newguy->t_stack[0] = 0xae;
	newguy->t_stack[1] = 0x11;
	newguy->t_stack[2] = 0xda;
	newguy->t_stack[3] = 0x33;

	/* Inherit the current directory */
	if (curthread->t_cwd != NULL) {
		VOP_INCREF(curthread->t_cwd);
		newguy->t_cwd = curthread->t_cwd;
	}


        #if OPT_A2
        result = conSetup(newguy);
        if(result) goto exit;
           
        // pid
        pid_t pid = newguy->pid;
        struct process* child = p_table[pid];
        assert(child != NULL);
 
        if (call_from_fork){
           int i;
           for (i = 3 ; i < MAX_FILE ; ++i) {
              if (curthread->ft[i] != NULL) {
                 newguy->ft[i] = (struct filetable*)copy_ft(curthread->ft[i]);
                 if (newguy->ft[i] == NULL) return ENOMEM;
              }
           }
           child->ppid = curthread->pid;
           call_from_fork = 0;
        }

        assert(call_from_fork == 0);
        #endif
       
	/* Set up the pcb (this arranges for func to be called) */
	md_initpcb(&newguy->t_pcb, newguy->t_stack, data1, data2, func);

	/* Interrupts off for atomicity */
	s = splhigh();

	/*
	 * Make sure our data structures have enough space, so we won't
	 * run out later at an inconvenient time.
	 */
	result = array_preallocate(sleepers, numthreads+1);
	if (result) {
		goto fail;
	}
	result = array_preallocate(zombies, numthreads+1);
	if (result) {
		goto fail;
	}

	/* Do the same for the scheduler. */
	result = scheduler_preallocate(numthreads+1);
	if (result) {
		goto fail;
	}

	/* Make the new thread runnable */
	result = make_runnable(newguy);
	if (result != 0) {
		goto fail;
	}




//kprintf("gonna run fork func\n");
	/*
	 * Increment the thread counter. This must be done atomically
	 * with the preallocate calls; otherwise the count can be
	 * temporarily too low, which would obviate its reason for
	 * existence.
	 */
	numthreads++;

	/* Done with stuff that needs to be atomic */
	splx(s);

	/*
	 * Return new thread structure if it's wanted.  Note that
	 * using the thread structure from the parent thread should be
	 * done only with caution, because in general the child thread
	 * might exit at any time.
	 */
	if (ret != NULL) {
		*ret = newguy;
                //kprintf("new thread returns\n");
	}
      //  kprintf("comfirm the new pid is %d\n",(*ret)->pid);
	return 0;

 fail:
	splx(s);
 exit:	if (newguy->t_cwd != NULL) {
		VOP_DECREF(newguy->t_cwd);
	}
	kfree(newguy->t_stack);
	kfree(newguy->t_name);
	kfree(newguy);

	return result;
}