/** * @brief Signals the occurrence of an event on all applicable devices. * This function should be called on timer interrupts to determine that * the interrupt corresponds to the event frequency of a device. If the * interrupt corresponded to the interrupt frequency of a device, this * function should ensure that the task is made ready to run */ void dev_update(unsigned long millis __attribute__((unused))) { disable_interrupts(); int i = 0; bool_e flag = FALSE; for (i = 0; i < NUM_DEVICES; i++) { if (devices[i].next_match <= millis) { devices[i].next_match += dev_freq[i]; tcb_t* sleep_queue = devices[i].sleep_q; if (sleep_queue != 0) { flag = TRUE; while(sleep_queue != 0) { runqueue_add(sleep_queue,sleep_queue->native_prio); sleep_queue = sleep_queue->sleep_queue; } devices[i].sleep_q = 0; } } } if(flag == TRUE) dispatch_save(); enable_interrupts(); }
/** * @brief Signals the occurrence of an event on all applicable devices. * This function should be called on timer interrupts to determine that * the interrupt corresponds to the event frequency of a device. If the * interrupt corresponded to the interrupt frequency of a device, this * function should ensure that the task is made ready to run */ void dev_update(unsigned long millis) { disable_interrupts(); int i; for (i = 0; i < NUM_DEVICES; i++) { // if a devices task match the time the add all sleep takes to the run queue if (devices[i].next_match <= millis) { while (devices[i].sleep_queue) { //print_sleep_queue(); tcb_t* head = devices[i].sleep_queue; runqueue_add(head, head->cur_prio); // go to the next sleep task devices[i].sleep_queue = head->sleep_queue; // set the current task's next to null head->sleep_queue = NULL; } // update next match devices[i].next_match += dev_freq[i]; } } // do context switch after the devices state are updated dispatch_save(); }
/** * @brief Context switch to the highest priority task while saving off the * current task state. * * This function needs to be externally synchronized. * We could be switching from the idle task. The priority searcher has been tuned * to return IDLE_PRIO for a completely empty run_queue case. */ void dispatch_save(void) { if(cur_tcb->cur_prio == HIGHEST_PRIO) return; tcb_t *dest, *temp; temp = cur_tcb; uint8_t hp = highest_prio(); /* If the idle task is he only next high prio task that means we are already running idle task */ if(hp == IDLE_PRIO) dest = cur_tcb; else dest = runqueue_remove(hp); /* Set cur_tcb to the task that we are about to run */ cur_tcb = dest; /* Set the cur_kstack var of the task that we are about to run */ cur_kstack = (int)dest->kstack_high; /* Add the task that we just switched from back to the queue */ runqueue_add(temp, temp->cur_prio); ctx_switch_full(&(dest->context), &(temp->context)); }
/** * @brief Signals the occurrence of an event on all applicable devices. * This function should be called on timer interrupts to determine that * the interrupt corresponds to the event frequency of a device. If the * interrupt corresponded to the interrupt frequency of a device, this * function should ensure that the task is made ready to run */ void dev_update(unsigned long millis __attribute__((unused))) { tcb_t* work; tcb_t* prev; unsigned int i; disable_interrupts(); // go through each device and wake up tasks for ready devices for(i = 0; i < NUM_DEVICES; i++) { // if device is ready (within a timer tick of frequency) if(millis % dev_freq[i] <= OS_TIMER_RESOLUTION) { work = devices[i].sleep_queue; // while sleep queue is not empty while(work != (tcb_t*) 0) { // add task to run queue and update device sleep queue runqueue_add(work, work->cur_prio); devices[i].sleep_queue = work->sleep_queue; prev = work; work = work->sleep_queue; prev->sleep_queue = (tcb_t*) 0; } } } enable_interrupts(); }
/** * @brief Context switch to the highest priority task while saving off the * current task state. * * This function needs to beexternally synchronized. * We could be switching from the idle task. The priority searcher has been tuned * to return IDLE_PRIO for a completely empty run_queue case. */ void dispatch_save(void) { uint8_t next_prio; tcb_t *next_tcb, *saved_cur_tcb; // printf("inside dispatch save\n"); // printf("added cur_tcb %u %p to run queue\n", cur_tcb->cur_prio, cur_tcb); next_prio = highest_prio(); /* * add the current task to the run queue... */ runqueue_add(cur_tcb, cur_tcb->cur_prio); // printf("next_prio is %u\n", next_prio); next_tcb = runqueue_remove(next_prio); // printf(" d save: removed next_tcb %u %p from run queue\n", next_tcb->cur_prio, next_tcb); // print_run_queue(); saved_cur_tcb = cur_tcb; cur_tcb = next_tcb; #if 0 printf("before calling ctx sw full, cur->context is %p\n", &(saved_cur_tcb->context)); printf("hexdump of cur->context is\n"); hexdump(&saved_cur_tcb->context, 160); printf("before calling ctx sw full, next->context is %p\n", &(next_tcb->context)); printf("hexdump of next->context is\n"); hexdump(&next_tcb->context, 160); #endif // disable_interrupts(); ctx_switch_full((volatile void *)(&(next_tcb->context)), (volatile void *)(&(saved_cur_tcb->context))); // while(1); }
/** * @brief This function initializes the idle TCB and makes it runnable */ static void idle_init(void){ task_t idle_task; idle_task.lambda = (void *) idle; idle_task.data = 0; idle_task.C = 0; idle_task.T = 0; idle_task.stack_pos = (void *) idle_stack_high; tcb_init(&idle_task, &system_tcb[IDLE_PRIO], IDLE_PRIO); runqueue_add(&system_tcb[IDLE_PRIO], IDLE_PRIO); }
/** * @brief Creates the main user program as a main task * and sets it to run. * * This function is only called when the user program is * first run. * */ void sched_init(task_t* main_task) { main_task->lambda = (void *)0xa0000000; main_task->data = 0; main_task->stack_pos = (void *)0xa3000000; main_task->C = 1; main_task->T = 1; tcb_init(main_task, &system_tcb[FIRST_MAIN_PRIO], FIRST_MAIN_PRIO); runqueue_add(&system_tcb[FIRST_MAIN_PRIO], FIRST_MAIN_PRIO); dispatch_init(&system_tcb[FIRST_MAIN_PRIO]); }
/** * @brief Context switch to the highest priority task that is not this task -- * don't save the current task state. * * There is always an idle task to switch to. */ void dispatch_nosave(void) { uint8_t prio = highest_prio(); tcb_t *next_task = runqueue_remove(prio); //Enqueue current task runqueue_add(cur_tcb, cur_tcb->cur_prio); cur_tcb = next_task; ctx_switch_half(&(next_task->context)); }
void sched_init(task_t* main_task) { *idle_count = 0; main_task->lambda = (task_fun_t)idle; main_task->data = NULL; main_task->stack_pos = system_tcb[IDLE_PRIO].kstack_high; main_task->C = 0; main_task->T = 0; printf("setting stuff in idle's tcb %p\n", &system_tcb[IDLE_PRIO]); // setup the context for the idle task setup_task_context(main_task, &system_tcb[IDLE_PRIO], IDLE_PRIO); runqueue_add(&system_tcb[IDLE_PRIO], IDLE_PRIO); }
/** * @brief Allocate user-stacks and initializes the kernel contexts of the * given threads. * * This function assumes that: * - num_tasks < number of tasks allowed on the system. * - the tasks have already been deemed schedulable and have been appropriately * scheduled. In particular, this means that the task list is sorted in order * of priority -- higher priority tasks come first. * * @param tasks A list of scheduled task descriptors. * @param size The number of tasks is the list. */ void allocate_tasks(task_t** tasks, size_t num_tasks) { size_t i = 1; for(; i <= num_tasks; i++) { tcb_t *task_tcb = &(system_tcb[i]); task_tcb->native_prio = i; task_tcb->cur_prio = i; // Context sched_context_t *ctx = &(task_tcb->context); ctx->r4 = (uint32_t) (tasks[i-1]->lambda); ctx->r5 = (uint32_t) (tasks[i-1]->data); ctx->r6 = (uint32_t) (tasks[i-1]->stack_pos); ctx->r8 = global_data; ctx->sp = ((char *)(task_tcb->kstack) + OS_KSTACK_SIZE); // Easy way to launch task fo first time ctx->lr = (void *)launch_task; task_tcb->holds_lock = 0; task_tcb->sleep_queue = (tcb_t *)0; } initialize_idle(); runqueue_init(); runqueue_add(&(system_tcb[IDLE_PRIO]), IDLE_PRIO); for(i=1; i <= num_tasks; i++) { runqueue_add(&(system_tcb[i]), i); } // Set current tcb as IDLE dispatch_init(&(system_tcb[IDLE_PRIO])); }
/** * @brief Allocate user-stacks and initializes the kernel contexts of the * given threads. * * This function assumes that: * - num_tasks < number of tasks allowed on the system. * - the tasks have already been deemed schedulable and have been appropriately * scheduled. In particular, this means that the task list is sorted in order * of priority -- higher priority tasks come first. * * @param tasks A list of scheduled task descriptors. * @param size The number of tasks is the list. */ void allocate_tasks(task_t** ptasks, size_t num_tasks) { //set up system tcb for each task in 'tasks' - loop through each task uint8_t i; runqueue_init(); dev_init(); for(i = 1; i <= num_tasks; i++) { tcb_init(ptasks[i], &system_tcb[i], i); runqueue_add(&system_tcb[i], i); } /* add the idle task */ idle_init(); }
/* ------------------------------------------------ Function: mutex_unlock Description: Unlocks the mutex and assigns lock to next-in-queue. If queue is empty, it simply unlocks the mutex ------------------------------------------------ */ int mutex_unlock(int mutex) { tcb_t* curr_tcb; tcb_t* temp_tcb; curr_tcb = get_cur_tcb(); if (mutex<1 || (gtMutex[mutex-1].bAvailable!=1)){ return -EINVAL; } if(curr_tcb != gtMutex[mutex-1].pHolding_Tcb){ return -EPERM; } gtMutex[mutex-1].pHolding_Tcb->holds_lock = 0; gtMutex[mutex-1].bLock = 0; curr_tcb->cur_prio = curr_tcb->native_prio; /*Changing lock status in current_tcb*/ if(gtMutex[mutex-1].pSleep_queue!=0){ /* Updating status of next in line for lock*/ /* If queue is not single-element */ runqueue_add(gtMutex[mutex-1].pSleep_queue, gtMutex[mutex-1].pSleep_queue->native_prio); if(gtMutex[mutex-1].pSleep_queue->sleep_queue!=0){ temp_tcb = gtMutex[mutex-1].pSleep_queue; gtMutex[mutex-1].pSleep_queue = temp_tcb->sleep_queue; temp_tcb->sleep_queue = 0; } else { /* If queue is single-element */ temp_tcb = gtMutex[mutex-1].pSleep_queue; gtMutex[mutex-1].pSleep_queue = 0; temp_tcb->sleep_queue = 0; rear[mutex-1] = 0; } /* Updating status of mutex */ gtMutex[mutex-1].bLock = 1; gtMutex[mutex-1].pHolding_Tcb = temp_tcb; gtMutex[mutex-1].pHolding_Tcb->holds_lock = 1; } else { /* If sleep queue is empty, unlock mutex*/ gtMutex[mutex-1].bLock = 0; gtMutex[mutex-1].pHolding_Tcb = 0; } return 0; }
/** * @brief Context switch to the highest priority task while saving off the * current task state. * * This function needs to be externally synchronized. * We could be switching from the idle task. The priority searcher has been tuned * to return IDLE_PRIO for a completely empty run_queue case. */ void dispatch_save(void) { tcb_t *new_task; tcb_t *old_task = cur_tcb; uint8_t prio = highest_prio(); /* If the current task is not the highest priority task */ if (old_task -> cur_prio > prio) { new_task = runqueue_remove(prio); sched_context_t *old_ctx = &(old_task -> context); runqueue_add(old_task, old_task -> cur_prio); cur_tcb = new_task; /* !!! ctx_switch_full has not yet implemented */ ctx_switch_full(&(new_task -> context), old_ctx); } }
int mutex_unlock_syscall(int mutex __attribute__((unused))) { // if mutex invalid if (mutex >= OS_NUM_MUTEX || gtMutex[mutex].bAvailable == 1) { return EINVAL; } if (gtMutex[mutex].pHolding_Tcb != get_cur_tcb()) return EPERM; // first tcb in pSleep_queue tcb_t* nextTcb = gtMutex[mutex].pSleep_queue; //if (nextTcb != (void *)0) { // set pSleep_queue to next one // gtMutex[mutex].pSleep_queue = (gtMutex[mutex].pSleep_queue)->sleep_queue; // if there are no other tcbs blocked if (nextTcb == (void *)0) { gtMutex[mutex].bLock = 0; gtMutex[mutex].pHolding_Tcb = (void *)0; // test //printf("mutex_unlock, %d\n", mutex); //printf("mutex holding tcb: %d\n", (gtMutex[mutex].pHolding_Tcb)->native_prio); } else { gtMutex[mutex].pSleep_queue = (gtMutex[mutex].pSleep_queue)->sleep_queue; nextTcb->sleep_queue = (void *)0; gtMutex[mutex].pHolding_Tcb = nextTcb; runqueue_add(nextTcb, nextTcb->native_prio); //printf("mutex_unlock, change to %d\n", mutex); //printf("mutex holding tcb: %d\n", (gtMutex[mutex].pHolding_Tcb)->native_prio); //printf("mutex sleep queue\n"); /* tcb_t* tmp = gtMutex[mutex].pSleep_queue; while (tmp != (void *)0) { printf("sleep %d\n", tmp->native_prio); tmp = tmp->sleep_queue; } */ } return 0; }
int mutex_unlock(int mutex_num) { if(debug_enabled1 == 1) printf("\tmutex::mutex_unlock:: id= %d tcb = %d\n", mutex_num, get_cur_tcb()->cur_prio); disable_interrupts(); //check for valid mutex number if(mutex_num < 0 || mutex_num > mutexID) return -EINVAL; //mutex is valid mutex_t* mutex = >Mutex[mutex_num-1]; //mutex we are referencing //check if we own this mutex if(mutex->pHolding_Tcb != get_cur_tcb()) return -EPERM; get_cur_tcb()->holds_lock--; //if the sleep queue is empty if(mutex->pSleep_queue == NULL_TCB) { mutex->bAvailable = TRUE; mutex->bLock = FALSE; mutex->pHolding_Tcb = NULL_TCB; } //queue not empty else { //next is up tcb_t* next = mutex->pSleep_queue; //mutex held by next mutex->pSleep_queue = next->sleep_queue; mutex->pHolding_Tcb = next; //next holds mutex next->sleep_queue = NULL_TCB; next->holds_lock++; //next is ready to run runqueue_add(next, next->cur_prio); } enable_interrupts(); return 0; //success }
void wq_wake(struct waitqueue_head *q, int nr_task) { struct list *p = q->list.next; struct task *task; unsigned irqflag; spin_lock_irqsave(q->lock, irqflag); while (p != &q->list && nr_task) { task = get_container_of(p, struct waitqueue, link)->task; set_task_state(task, TASK_RUNNING); runqueue_add(task); list_del(p); p = q->list.next; nr_task--; } spin_unlock_irqrestore(q->lock, irqflag); }
/** * @brief Signals the occurrence of an event on all applicable devices. * This function should be called on timer interrupts to determine that * the interrupt corresponds to the event frequency of a device. If the * interrupt corresponded to the interrupt frequency of a device, this * function should ensure that the task is made ready to run */ void dev_update(unsigned long millis ) { disable_interrupts(); //check each device for an event int i; int have_some = 0; //if(debug_enabled ==1) puts("dev_update::checking \n"); for( i = 0; i < NUM_DEVICES - 1; i++) { if( millis >= devices[i].next_match) { if(debug_enabled ==1) printf("dev_update::waking up %d\n",i); //wake up device! //make tasks ready to run //for each sleeper tcb_t* sleepy = devices[i].sleep_queue; tcb_t* next; while( sleepy != 0) { //WAKE UP SLEEPY runqueue_add(sleepy, sleepy->cur_prio); if(debug_enabled == 1)printf("dev_update...dev = %d:::woke up (prio)= %d::next = %x\n", i, (unsigned)sleepy->cur_prio, (unsigned)sleepy->sleep_queue); //another sleeper? next = sleepy->sleep_queue; sleepy->sleep_queue = 0; sleepy = next; //set flag that there is a reason to update have_some = 1; } //set next match point in millis devices[i].next_match += dev_freq[i]; } } //re-evaluate our priorities, if we have a reason to if(have_some == 1 ) dispatch_save(); enable_interrupts(); }
int mutex_unlock(int mutex __attribute__((unused))) { if (mutex >= OS_NUM_MUTEX) { return EINVAL; } disable_interrupts(); tcb_t* cur_tcb = get_cur_tcb(); mutex_t cur_mutex = gtMutex[mutex]; /* check if provided mutex identifier is valid */ if (!cur_mutex.bAvailable) { enable_interrupts(); return EINVAL; } /* check if current task is holding the mutex */ if (cur_tcb != cur_mutex.pHolding_Tcb) { enable_interrupts(); return EPERM; } cur_mutex.pHolding_Tcb = 0; /* add first task in sleep queue to run queue */ if (cur_mutex.pSleep_queue != 0) { tcb_t* h_tcb = cur_mutex.pSleep_queue; cur_mutex.pHolding_Tcb = h_tcb; runqueue_add(h_tcb, h_tcb->cur_prio); cur_mutex.pSleep_queue = h_tcb->sleep_queue; cur_mutex.bLock = 1; } cur_mutex.bLock = 0; enable_interrupts(); return 0; }
int mutex_unlock(int mutex __attribute__((unused))) { disable_interrupts(); /* invalid mutex id, return error code */ if(mutex < 0 || mutex >= OS_NUM_MUTEX || gtMutex[mutex].bAvailable){ enable_interrupts(); return -EINVAL; } else{ tcb_t* cur_tcb = get_cur_tcb(); /* current task does not hold the lock, return error code */ if(cur_tcb != gtMutex[mutex].pHolding_Tcb){ enable_interrupts(); return -EPERM; } else{ /* release the lock */ gtMutex[mutex].pHolding_Tcb = NULL; gtMutex[mutex].bLock = FALSE; /* there is task waiting in the sleep queue, make it able to run */ if(gtMutex[mutex].pSleep_queue != NULL){ tcb_t* tmp = gtMutex[mutex].pSleep_queue; gtMutex[mutex].pSleep_queue = tmp->sleep_queue; tmp->sleep_queue = NULL; runqueue_add(tmp, tmp->cur_prio); } /* decrease the num of locks hold by current task by one */ if(cur_tcb->holds_lock != 0) cur_tcb->holds_lock --; enable_interrupts(); return 0; } } }
static void __init load_user_task() { extern char _user_task_list; struct task *p; unsigned int pri; for (p = (struct task *)&_user_task_list; *(unsigned int *)p; p++) { if (p->addr == NULL) continue; /* share the init kernel stack to save memory */ if (alloc_mm(p, &init, STACK_SHARED)) continue; pri = get_task_pri(p); set_task_dressed(p, p->flags | STACK_SHARED, p->addr); set_task_pri(p, pri); set_task_context(p, wrapper); /* make it runnable, and add into runqueue */ set_task_state(p, TASK_RUNNING); runqueue_add(p); } }
int mutex_unlock(int mutex __attribute__((unused))) { tcb_t* temp; disable_interrupts(); // if the provided mutex identifier if invalid if(mutex > OS_NUM_MUTEX || gtMutex[mutex].bAvailable == TRUE) { enable_interrupts(); return -EINVAL; } if(gtMutex[mutex].pHolding_Tcb != get_cur_tcb()) { // if the urrent task does not hold the mutex enable_interrupts(); return -EPERM; } else { temp = gtMutex[mutex].pHolding_Tcb; temp->holds_lock = 0; #ifdef HLP temp->cur_prio = temp->native_prio; #endif // HLP // if there is no element in sleep queue if(gtMutex[mutex].pSleep_queue == 0) { gtMutex[mutex].bLock = 0; gtMutex[mutex].pHolding_Tcb = 0; enable_interrupts(); return 0; } else { // if the sleep queue have tasks waiting for the mutex // first tcb in sleeping queue gets the mutex gtMutex[mutex].pHolding_Tcb = gtMutex[mutex].pSleep_queue; // move the head of mutex sleeping queue to next gtMutex[mutex].pSleep_queue = gtMutex[mutex].pSleep_queue->sleep_queue; // clear the sleeping queue of the holding tcb gtMutex[mutex].pHolding_Tcb->sleep_queue = 0; gtMutex[mutex].pHolding_Tcb->holds_lock = 1; #ifdef HLP gtMutex[mutex].pHolding_Tcb->cur_prio = 0; #endif // HLP // put the wake up task into runqueue runqueue_add(gtMutex[mutex].pHolding_Tcb, gtMutex[mutex].pHolding_Tcb->cur_prio); // check the wake up task's priority and current running task's priority if(highest_prio() < get_cur_prio()) { // switch to the highest task dispatch_save(); } else { // stay the same enable_interrupts(); return 0; } } } // the function will not get here return 1; }