void schedule() { /* azioni da compiere quando non c'e' nessun thread pronto ad eseguire */ if (emptyThreadQ(&readyQueue) && currentThread == NULL) { prova(); if (threadCount == 1)/* se c'e' solo il SSI -> normal system shutdown */ HALT(); /* chiamo la HALT ROM routine */ else if (threadCount > 0 && softBlockCount == 0) {/* deadlock */ PANIC(); /* chiamo la PANIC ROM routine */ } else if (threadCount > 0 && softBlockCount > 0) { /* in attesa di un interrupt -> wait state */ /* se ci sono thread in attesa dello pseudo tick, * carico il valore dello pseudo clock nel registro della cpu.*/ if (!emptyThreadQ(&waitForPseudoClockQueue)) { SET_IT(SCHED_PSEUDO_CLOCK); } /* impostiamo lo stato del processore con gli interrupt abilitati*/ setSTATUS(getSTATUS() | STATUS_IEc | STATUS_INT_UNMASKED); for (;;); } } else { /* Se non c'è nessun Thread in esecuzione ma c'e n'è almeno uno nella readyQueue allora carico un thread*/ if (currentThread == NULL) { currentThread = removeThread(&readyQueue); currentThread->elapsedTime = 0; currentThread->startTime = GET_TODLOW; SET_IT(SCHED_TIME_SLICE); /* Altrimenti se è passato il SCHED_TIME_SLICE rimuovo il thread corrente dall'esecuzione*/ } else if (currentThread->elapsedTime >= SCHED_TIME_SLICE) { //in questo modo do priorità all'SSI if (currentThread != tcb_SSI) { insertThread(&readyQueue, currentThread); /*Carico un nuovo thread*/ currentThread = removeThread(&readyQueue); } currentThread->elapsedTime = 0; currentThread->startTime = GET_TODLOW; /* Se e' scattato lo pseudo clock non settiamo il timer a 5 ms * dato che scattera' subito l'interrupt dello pseudo clock */ if (!isPseudoClock) SET_IT(SCHED_TIME_SLICE); } /* carico lo stato del thread nel processore dalla sua tcb */ LDST(&(currentThread->t_state)); } }
/* * carica il processore con lo snapshot salvato nel TCB in testa * alla ready queue */ HIDDEN void upThread(void) { tcb_t *current; signed int time_snap; /* inizio del conteggio del tempo passato in codice kernel, solo per la prima * esecuzione, altrimenti inizializzazione prevista negli vari handler */ if (first_up) time_in_kernel = TOD_SNAPSHOT; current = removeThreadQ(&ready_queue); current_thread = current->tid; current->status = RUNN_THREAD; current = resolveTid(current_thread); if (current->cpu_remain == 0) current->cpu_remain = SCHED_TIME_SLICE; /* aggiorna il valore dello pseudo count evitando un possibile underflow */ if (pseudo_count < (last_time_slice + STCK(time_snap) - time_in_kernel)) /* STCK piu' preciso */ pseudo_count = 0; else pseudo_count -= last_time_slice + time_snap - time_in_kernel; /* settaggio interval timer considerando il tempo rimasto allo scadere dello pseudo clock*/ if (pseudo_count > SCHED_TIME_SLICE) { if (current->cpu_remain < SCHED_TIME_SLICE) { last_time_slice = TOD_SNAPSHOT; /* inizio intervallo di tempo del prossimo time slice */ SET_IT(current->cpu_remain); /* settaggio dell'interval timer */ } else { last_time_slice = TOD_SNAPSHOT; /* inizio intervallo di tempo del prossimo time slice */ SET_IT(SCHED_TIME_SLICE); /* settaggio dell'interval timer */ } } else { last_time_slice = TOD_SNAPSHOT; /* inizio intervallo di tempo del prossimo time slice */ SET_IT(pseudo_count); /* settaggio dell'interval timer */ } /* carica il processore con lo stato del thread */ LDST(&(current->cpu_snapshot)); /* esegue codice del thread appena caricato */ }
/* * funzione principale */ void schedule(void) { tcb_t *SSI_t, *tmp_q; unsigned int execKill; /* esami tutta la ready queue, spostando tutti i thread contrassegnati to_kill * nella killable_queue. Inizia la scansione da capo ad ogni thread spostato */ if (!emptyThreadQ(ready_queue)){ tmp_q = ready_queue; do{ execKill = FALSE; if(tmp_q->to_kill == TRUE){ tmp_q = outThreadQ(&ready_queue, tmp_q); insertBackThreadQ(&killable_queue, tmp_q); execKill = TRUE; } tmp_q = tmp_q->t_prev; if (execKill == TRUE) tmp_q = ready_queue; if (emptyThreadQ(ready_queue)) execKill = FALSE; } while(((tmp_q != ready_queue) && (!emptyThreadQ(ready_queue))) || execKill == TRUE); } /* termina tutti i thread, svuotando contestualmente killable_queue */ if (!emptyThreadQ(killable_queue)){ tmp_q = killable_queue; do{ outThreadQ(&killable_queue, tmp_q); force_terminate(tmp_q->tid); tmp_q = tmp_q->t_prev; } while(!emptyThreadQ(killable_queue)); } if (emptyThreadQ(ready_queue)) { if (thread_count == 1) HALT(); /* dovrebbe bastare questo tipo di controllo */ SSI_t = resolveTid(SSI_TID); if (thread_count > 0 && softb_count == 0) PANIC(); /* anche la SSI non puo' eseguire perche' e' in recv */ if (thread_count > 0 && softb_count > 0 && SSI_t->status == W4_ANYTID) { all_blocked = TRUE; /* tutti i thread sospesi */ last_time_slice = TOD_SNAPSHOT; SET_IT(pseudo_count); /* settaggio interval timer con il valore di pseudo clock */ ENABLE_INTERRUPT; for (;;); /* aspetta una interrupt */ } } upThread(); /* dispatch di un nuovo thread */ }
/********************************************************************** INT_HANDLER Caricato all'arrivo di un interrupt, scandisce sequenzialmente, quindi garantendo priorità, le linee, da quelle con indice minore (più veloci) a quelle con indice maggiore (più lente). Ciò viene fatto in base ai bit IP del registro CAUSE, cercando poi quale/i device fra gli 8 possibili ha generato l'interrupt. A questo punto viene gestito l'interrupt, mandando un messaggio a SSI e ACKando il device register, principalmente, più altre azioni a seconda dello specifico device. **********************************************************************/ void int_handler(){ int int_cause; int *status; int *status_trans; int *status_rec; int *int_bitmap; int *command; int *command_trans; int *command_rec; memaddr device_baseaddr; int dev_number; /* Se c'era un processo sulla CPU salvo il precedente stato del processore nel campo t_state del tcb attivo in quel momento. Lo stato è salvato nella old area dell'attuale eccezione. */ if (current_thread != NULL) { /* Aggiornamento tempo thread */ current_thread->cpu_slice += (GET_TODLOW - current_thread_tod); current_thread->cpu_time += (GET_TODLOW - current_thread_tod); /* Salvataggio stato */ save_state(int_oldarea, &(current_thread->t_state)); } /* Prelevo registro cause */ int_cause = int_oldarea->cause; /* Non presenti interrupt da linee 0 e 1 (software) in AmiKaya11 */ /* Linea 2 Interval Timer Interrupt + Gestione PSEUDO CLOCK ****************************/ if (CAUSE_IP_GET(int_cause, INT_TIMER)) { /* Aggiornamento pseudo clock */ pseudo_tick = pseudo_tick + (GET_TODLOW - start_pseudo_tick); start_pseudo_tick = GET_TODLOW; /* Interrupt per Pseudo-clock Tick */ if (pseudo_tick >= SCHED_PSEUDO_CLOCK) { /* Messaggio all'SSI che dovrà gestire lo sbloccaggio dei thread */ interrupt_msg_array[i].service = PSEUDOCLOCK_MSG; interrupt_msg_array[i].arg = 0; interrupt_msg_array[i].reply = NOREPLY; if ( send((tcb_t *)BUS_INTERVALTIMER, SSI_tcb, (U32)&interrupt_msg_array[i]) == MSGNOGOOD ) PANIC(); /* Faccio ripartire il clock del device virtuale */ pseudo_tick = 0; start_pseudo_tick = GET_TODLOW; } /* Gestione per Slice processo corrente scaduto */ if ((current_thread != NULL) && (current_thread->cpu_slice >= SCHED_TIME_SLICE)) { insertThread(&ready_queue, current_thread); current_thread = NULL; } /* Default */ else SET_IT(SCHED_PSEUDO_CLOCK - pseudo_tick); } /* Interval Timer ********************************************************************/ /* Linea 3 Disk interrupt **************************************************************/ else if (CAUSE_IP_GET(int_cause, INT_DISK)) { /* Cerco la bitmap della linea attuale */ int_bitmap = (int *)(PENDING_BITMAP_START + (WORD_SIZE * (INT_DISK - INT_LOWEST))); /* Cerco il device a più alta priorità su questa linea con interrupt pendente */ dev_number = which_device(*int_bitmap); /* Salvo indirizzo del Device Register */ device_baseaddr = (memaddr)(DEV_REGS_START + ((INT_DISK - INT_LOWEST) * 0x80) + (dev_number * 0x10)); /* Salvo valore del campo Status del Device Register */ status = (int *)device_baseaddr; /* Puntatore a campo command del Device Register */ command = (int *)(device_baseaddr + 0x4); /* ACK al device */ *command = DEV_C_ACK; /* Invio messaggio a SSI, come sender il device register, come payload il valore di status */ interrupt_msg_array[i].service = INTERRUPT_MSG; interrupt_msg_array[i].arg = *status; interrupt_msg_array[i].reply = NOREPLY; if ( send((tcb_t *)device_baseaddr, SSI_tcb, (U32)&interrupt_msg_array[i]) == MSGNOGOOD ) PANIC(); } /* Disk ******************************************************************************/ /* Linea 4 Tape interrupt **************************************************************/ else if (CAUSE_IP_GET(int_cause, INT_TAPE)) { /* Cerco la bitmap della linea attuale */ int_bitmap = (int *)(PENDING_BITMAP_START + (WORD_SIZE * (INT_TAPE - INT_LOWEST))); /* Cerco il device a più alta priorità su questa linea con interrupt pendente */ dev_number = which_device(*int_bitmap); /* Salvo indirizzo del Device Register */ device_baseaddr = (memaddr)(DEV_REGS_START + ((INT_TAPE - INT_LOWEST) * 0x80) + (dev_number * 0x10)); /* Salvo valore del campo Status del Device Register */ status = (int *)device_baseaddr; /* Puntatore a campo command del Device Register */ command = (int *)(device_baseaddr + 0x4); /* ACK al device */ *command = DEV_C_ACK; /* Invio messaggio a SSI, come sender il device register, come payload il valore di status */ interrupt_msg_array[i].service = INTERRUPT_MSG; interrupt_msg_array[i].arg = *status; interrupt_msg_array[i].reply = NOREPLY; if ( send((tcb_t *)device_baseaddr, SSI_tcb, (U32)&interrupt_msg_array[i]) == MSGNOGOOD ) PANIC(); } /* Tape ******************************************************************************/ /* Linea 5 Unused line interrupt *******************************************************/ else if (CAUSE_IP_GET(int_cause, INT_UNUSED)) { /* Cerco la bitmap della linea attuale */ int_bitmap = (int *)(PENDING_BITMAP_START + (WORD_SIZE * (INT_UNUSED - INT_LOWEST))); /* Cerco il device a più alta priorità su questa linea con interrupt pendente */ dev_number = which_device(*int_bitmap); /* Salvo indirizzo del Device Register */ device_baseaddr = (memaddr)(DEV_REGS_START + ((INT_UNUSED - INT_LOWEST) * 0x80) + (dev_number * 0x10)); /* Salvo valore del campo Status del Device Register */ status = (int *)device_baseaddr; /* Puntatore a campo command del Device Register */ command = (int *)(device_baseaddr + 0x4); /* ACK al device */ *command = DEV_C_ACK; /* Invio messaggio a SSI, come sender il device register, come payload il valore di status */ interrupt_msg_array[i].service = INTERRUPT_MSG; interrupt_msg_array[i].arg = *status; interrupt_msg_array[i].reply = NOREPLY; if ( send((tcb_t *)device_baseaddr, SSI_tcb, (U32)&interrupt_msg_array[i]) == MSGNOGOOD ) PANIC(); } /* Unused ****************************************************************************/ /* Linea 6 Printer interrupt ***********************************************************/ else if (CAUSE_IP_GET(int_cause, INT_PRINTER)) { /* Cerco la bitmap della linea attuale */ int_bitmap = (int *)(PENDING_BITMAP_START + (WORD_SIZE * (INT_PRINTER - INT_LOWEST))); /* Cerco il device a più alta priorità su questa linea con interrupt pendente */ dev_number = which_device(*int_bitmap); /* Salvo indirizzo del Device Register */ device_baseaddr = (memaddr)(DEV_REGS_START + ((INT_PRINTER - INT_LOWEST) * 0x80) + (dev_number * 0x10)); /* Salvo valore del campo Status del Device Register */ status = (int *)device_baseaddr; /* Puntatore a campo command del Device Register */ command = (int *)(device_baseaddr + 0x4); /* ACK al device */ *command = DEV_C_ACK; /* Invio messaggio a SSI, come sender il device register, come payload il valore di status */ interrupt_msg_array[i].service = INTERRUPT_MSG; interrupt_msg_array[i].arg = *status; interrupt_msg_array[i].reply = NOREPLY; if ( send((tcb_t *)device_baseaddr, SSI_tcb, (U32)&interrupt_msg_array[i]) == MSGNOGOOD ) PANIC(); } /* Printer ***************************************************************************/ /* Linea 7 Terminal interrupt **********************************************************/ else if (CAUSE_IP_GET(int_cause, INT_TERMINAL)) { /* Cerco la bitmap della linea attuale */ int_bitmap = (int *)(PENDING_BITMAP_START + (WORD_SIZE * (INT_TERMINAL - INT_LOWEST))); /* Cerco il device a più alta priorità su questa linea con interrupt pendente */ dev_number = which_device(*int_bitmap); /* Salvo indirizzo del Device Register */ device_baseaddr = (memaddr)(DEV_REGS_START + ((INT_TERMINAL - INT_LOWEST) * 0x80) + (dev_number * 0x10)); /* Salvo lo stato e il puntatore al campo command del Device Register in trasmissione */ status_trans = (int *)(device_baseaddr + 0x8); command_trans = (int *)(device_baseaddr + 0xC); /* Salvo lo stato e il puntatore al campo command del Device Register in ricezione */ status_rec = (int *)device_baseaddr; command_rec = (int *)(device_baseaddr + 0x4); /* Analizzo lo stato per estrarre la causa dell'interrupt e agisco di conseguenza*/ /* Un carattere è stato trasmesso -> Priorità alla trasmissione */ if (((*status_trans) & STATUSMASK) == DEV_TTRS_S_CHARTRSM) { /* Invio messaggio a SSI, come sender il device register, come payload il valore di status */ interrupt_msg_array[i].service = INTERRUPT_MSG; interrupt_msg_array[i].arg = *status_trans; interrupt_msg_array[i].reply = NOREPLY; if ( send((tcb_t *)status_trans, SSI_tcb, (U32)&interrupt_msg_array[i]) == MSGNOGOOD ) PANIC(); /* ACK al device */ *command_trans = DEV_C_ACK; } /* Un carattere è stato ricevuto */ else if (((*status_rec) & STATUSMASK) == DEV_TRCV_S_CHARRECV) { /* Invio messaggio a SSI, come sender il device register, come payload il valore di status */ interrupt_msg_array[i].service = INTERRUPT_MSG; interrupt_msg_array[i].arg = *status_rec; interrupt_msg_array[i].reply = NOREPLY; if ( send((tcb_t *)status_rec, SSI_tcb, (U32)&interrupt_msg_array[i]) == MSGNOGOOD ) PANIC(); /* ACK al device */ *command_rec = DEV_C_ACK; } } /* Terminal **************************************************************************/ /* Incremento i in modo circolare */ i = (i+1)%48; scheduler(); }