/* Execute one instruction from each running context. */ void ke_run(void) { struct ctx_t *ctx, *ctx_trav; int k = 0; /* Run an instruction from every running process */ for (k=0, ctx = ke->suspended_list_head; ctx; ctx = ctx->suspended_next, k++); //printf ("Instruction number: %lld, suspended processes:%d, queue_size: %d\n", instr_num, k, sched_count); for (ctx = ke->running_list_head; ctx; ctx = ctx->running_next) { int i; for ( i = 0 ; i < ctx->instr_slice && ctx_get_status(ctx, ctx_running); ++i) { while (interrupts_exist() && instr_num >= next_interrupt_num()) handle_interrupt (pop_interrupt()); ctx_execute_inst(ctx); instr_num++; } } /* Free finished contexts */ while (ke->finished_list_head) ctx_free(ke->finished_list_head); /* Process list of suspended contexts */ //ke_process_events(); while (!(ke->running_list_head) && interrupts_exist()) { //printf ("Instruction number updated from %lld to %lld\n", instr_num, next_interrupt_num()); instr_num = next_interrupt_num(); handle_interrupt(pop_interrupt()); } }
/* Finalization */ void ke_done(void) { struct ctx_t *ctx; /* Finish all contexts */ for (ctx = ke->context_list_head; ctx; ctx = ctx->context_next) if (!ctx_get_status(ctx, ctx_finished)) ctx_finish(ctx, 0); /* Free contexts */ while (ke->context_list_head) ctx_free(ke->context_list_head); /* Finalize GPU kernel */ gk_done(); /* End */ free(ke); isa_done(); syscall_summary(); }
/* Return the reason why a thread cannot be dispatched. If it can, * return di_stall_used. */ static enum di_stall_t can_dispatch_thread(int core, int thread) { struct list_t *uopq = THREAD.uopq; struct uop_t *uop; /* Uop queue empty. */ uop = list_get(uopq, 0); if (!uop) return !THREAD.ctx || !ctx_get_status(THREAD.ctx, ctx_running) ? di_stall_ctx : di_stall_uopq; /* If iq/lq/sq/rob full, done */ if (!rob_can_enqueue(uop)) return di_stall_rob; if (!(uop->flags & X86_UINST_MEM) && !iq_can_insert(uop)) return di_stall_iq; if ((uop->flags & X86_UINST_MEM) && !lsq_can_insert(uop)) return di_stall_lsq; if (!rf_can_rename(uop)) return di_stall_rename; return di_stall_used; }
/* Check for events detected in spawned host threads, like waking up contexts or * sending signals. * The list is only processed if flag 'ke->process_events_force' is set. */ void ke_process_events() { struct ctx_t *ctx, *next; uint64_t now = ke_timer(); /* Check if events need actually be checked. */ pthread_mutex_lock(&ke->process_events_mutex); if (!ke->process_events_force) { pthread_mutex_unlock(&ke->process_events_mutex); return; } /* By default, no subsequent call to 'ke_process_events' is assumed */ ke->process_events_force = 0; /* * LOOP 1 * Look at the list of suspended contexts and try to find * one that needs to be woken up. */ for (ctx = ke->suspended_list_head; ctx; ctx = next) { /* Save next */ next = ctx->suspended_next; /* Context is suspended in 'nanosleep' system call. */ if (ctx_get_status(ctx, ctx_nanosleep)) { uint32_t rmtp = ctx->regs->ecx; uint64_t zero = 0; uint32_t sec, usec; uint64_t diff; /* If 'ke_host_thread_suspend' is still running for this context, do nothing. */ if (ctx->host_thread_suspend_active) continue; /* Timeout expired */ if (ctx->wakeup_time <= now) { if (rmtp) mem_write(ctx->mem, rmtp, 8, &zero); syscall_debug("syscall 'nanosleep' - continue (pid %d)\n", ctx->pid); syscall_debug(" return=0x%x\n", ctx->regs->eax); ctx_clear_status(ctx, ctx_suspended | ctx_nanosleep); continue; } /* Context received a signal */ if (ctx->signal_masks->pending & ~ctx->signal_masks->blocked) { if (rmtp) { diff = ctx->wakeup_time - now; sec = diff / 1000000; usec = diff % 1000000; mem_write(ctx->mem, rmtp, 4, &sec); mem_write(ctx->mem, rmtp + 4, 4, &usec); } ctx->regs->eax = -EINTR; syscall_debug("syscall 'nanosleep' - interrupted by signal (pid %d)\n", ctx->pid); ctx_clear_status(ctx, ctx_suspended | ctx_nanosleep); continue; } /* No event available, launch 'ke_host_thread_suspend' again */ ctx->host_thread_suspend_active = 1; if (pthread_create(&ctx->host_thread_suspend, NULL, ke_host_thread_suspend, ctx)) fatal("syscall 'poll': could not create child thread"); continue; } /* Context suspended in 'rt_sigsuspend' system call */ if (ctx_get_status(ctx, ctx_sigsuspend)) { /* Context received a signal */ if (ctx->signal_masks->pending & ~ctx->signal_masks->blocked) { signal_handler_check_intr(ctx); ctx->signal_masks->blocked = ctx->signal_masks->backup; syscall_debug("syscall 'rt_sigsuspend' - interrupted by signal (pid %d)\n", ctx->pid); ctx_clear_status(ctx, ctx_suspended | ctx_sigsuspend); continue; } /* No event available. The context will never awake on its own, so no * 'ke_host_thread_suspend' is necessary. */ continue; } /* Context suspended in 'poll' system call */ if (ctx_get_status(ctx, ctx_poll)) { uint32_t prevents = ctx->regs->ebx + 6; uint16_t revents = 0; struct fd_t *fd; struct pollfd host_fds; int err; /* If 'ke_host_thread_suspend' is still running for this context, do nothing. */ if (ctx->host_thread_suspend_active) continue; /* Get file descriptor */ fd = fdt_entry_get(ctx->fdt, ctx->wakeup_fd); if (!fd) fatal("syscall 'poll': invalid 'wakeup_fd'"); /* Context received a signal */ if (ctx->signal_masks->pending & ~ctx->signal_masks->blocked) { signal_handler_check_intr(ctx); syscall_debug("syscall 'poll' - interrupted by signal (pid %d)\n", ctx->pid); ctx_clear_status(ctx, ctx_suspended | ctx_poll); continue; } /* Perform host 'poll' call */ host_fds.fd = fd->host_fd; host_fds.events = ((ctx->wakeup_events & 4) ? POLLOUT : 0) | ((ctx->wakeup_events & 1) ? POLLIN : 0); err = poll(&host_fds, 1, 0); if (err < 0) fatal("syscall 'poll': unexpected error in host 'poll'"); /* POLLOUT event available */ if (ctx->wakeup_events & host_fds.revents & POLLOUT) { revents = POLLOUT; mem_write(ctx->mem, prevents, 2, &revents); ctx->regs->eax = 1; syscall_debug("syscall poll - continue (pid %d) - POLLOUT occurred in file\n", ctx->pid); syscall_debug(" retval=%d\n", ctx->regs->eax); ctx_clear_status(ctx, ctx_suspended | ctx_poll); continue; } /* POLLIN event available */ if (ctx->wakeup_events & host_fds.revents & POLLIN) { revents = POLLIN; mem_write(ctx->mem, prevents, 2, &revents); ctx->regs->eax = 1; syscall_debug("syscall poll - continue (pid %d) - POLLIN occurred in file\n", ctx->pid); syscall_debug(" retval=%d\n", ctx->regs->eax); ctx_clear_status(ctx, ctx_suspended | ctx_poll); continue; } /* Timeout expired */ if (ctx->wakeup_time && ctx->wakeup_time < now) { revents = 0; mem_write(ctx->mem, prevents, 2, &revents); syscall_debug("syscall poll - continue (pid %d) - time out\n", ctx->pid); syscall_debug(" return=0x%x\n", ctx->regs->eax); ctx_clear_status(ctx, ctx_suspended | ctx_poll); continue; } /* No event available, launch 'ke_host_thread_suspend' again */ ctx->host_thread_suspend_active = 1; if (pthread_create(&ctx->host_thread_suspend, NULL, ke_host_thread_suspend, ctx)) fatal("syscall 'poll': could not create child thread"); continue; } /* Context suspended in a 'write' system call */ if (ctx_get_status(ctx, ctx_write)) { struct fd_t *fd; int count, err; uint32_t pbuf; void *buf; struct pollfd host_fds; /* If 'ke_host_thread_suspend' is still running for this context, do nothing. */ if (ctx->host_thread_suspend_active) continue; /* Context received a signal */ if (ctx->signal_masks->pending & ~ctx->signal_masks->blocked) { signal_handler_check_intr(ctx); syscall_debug("syscall 'write' - interrupted by signal (pid %d)\n", ctx->pid); ctx_clear_status(ctx, ctx_suspended | ctx_write); continue; } /* Get file descriptor */ fd = fdt_entry_get(ctx->fdt, ctx->wakeup_fd); if (!fd) fatal("syscall 'write': invalid 'wakeup_fd'"); /* Check if data is ready in file by polling it */ host_fds.fd = fd->host_fd; host_fds.events = POLLOUT; err = poll(&host_fds, 1, 0); if (err < 0) fatal("syscall 'write': unexpected error in host 'poll'"); /* If data is ready in the file, wake up context */ if (host_fds.revents) { pbuf = ctx->regs->ecx; count = ctx->regs->edx; buf = malloc(count); mem_read(ctx->mem, pbuf, count, buf); count = write(fd->host_fd, buf, count); if (count < 0) fatal("syscall 'write': unexpected error in host 'write'"); ctx->regs->eax = count; free(buf); syscall_debug("syscall write - continue (pid %d)\n", ctx->pid); syscall_debug(" return=0x%x\n", ctx->regs->eax); ctx_clear_status(ctx, ctx_suspended | ctx_write); continue; } /* Data is not ready to be written - launch 'ke_host_thread_suspend' again */ ctx->host_thread_suspend_active = 1; if (pthread_create(&ctx->host_thread_suspend, NULL, ke_host_thread_suspend, ctx)) fatal("syscall 'write': could not create child thread"); continue; } /* Context suspended in 'read' system call */ if (ctx_get_status(ctx, ctx_read)) { struct fd_t *fd; uint32_t pbuf; int count, err; void *buf; struct pollfd host_fds; /* If 'ke_host_thread_suspend' is still running for this context, do nothing. */ if (ctx->host_thread_suspend_active) continue; /* Context received a signal */ if (ctx->signal_masks->pending & ~ctx->signal_masks->blocked) { signal_handler_check_intr(ctx); syscall_debug("syscall 'read' - interrupted by signal (pid %d)\n", ctx->pid); ctx_clear_status(ctx, ctx_suspended | ctx_read); continue; } /* Get file descriptor */ fd = fdt_entry_get(ctx->fdt, ctx->wakeup_fd); if (!fd) fatal("syscall 'read': invalid 'wakeup_fd'"); /* Check if data is ready in file by polling it */ host_fds.fd = fd->host_fd; host_fds.events = POLLIN; err = poll(&host_fds, 1, 0); if (err < 0) fatal("syscall 'read': unexpected error in host 'poll'"); /* If data is ready, perform host 'read' call and wake up */ if (host_fds.revents) { pbuf = ctx->regs->ecx; count = ctx->regs->edx; buf = malloc(count); count = read(fd->host_fd, buf, count); if (count < 0) fatal("syscall 'read': unexpected error in host 'read'"); ctx->regs->eax = count; mem_write(ctx->mem, pbuf, count, buf); free(buf); syscall_debug("syscall 'read' - continue (pid %d)\n", ctx->pid); syscall_debug(" return=0x%x\n", ctx->regs->eax); ctx_clear_status(ctx, ctx_suspended | ctx_read); continue; } /* Data is not ready. Launch 'ke_host_thread_suspend' again */ ctx->host_thread_suspend_active = 1; if (pthread_create(&ctx->host_thread_suspend, NULL, ke_host_thread_suspend, ctx)) fatal("syscall 'read': could not create child thread"); continue; } /* Context suspended in a 'waitpid' system call */ if (ctx_get_status(ctx, ctx_waitpid)) { struct ctx_t *child; uint32_t pstatus; /* A zombie child is available to 'waitpid' it */ child = ctx_get_zombie(ctx, ctx->wakeup_pid); if (child) { /* Continue with 'waitpid' system call */ pstatus = ctx->regs->ecx; ctx->regs->eax = child->pid; if (pstatus) mem_write(ctx->mem, pstatus, 4, &child->exit_code); ctx_set_status(child, ctx_finished); syscall_debug("syscall waitpid - continue (pid %d)\n", ctx->pid); syscall_debug(" return=0x%x\n", ctx->regs->eax); ctx_clear_status(ctx, ctx_suspended | ctx_waitpid); continue; } /* No event available. Since this context won't awake on its own, no * 'ke_host_thread_suspend' is needed. */ continue; } } /* * LOOP 2 * Check list of all contexts for expired timers. */ for (ctx = ke->context_list_head; ctx; ctx = ctx->context_next) { int sig[3] = { 14, 26, 27 }; /* SIGALRM, SIGVTALRM, SIGPROF */ int i; /* If there is already a 'ke_host_thread_timer' running, do nothing. */ if (ctx->host_thread_timer_active) continue; /* Check for any expired 'itimer': itimer_value < now * In this case, send corresponding signal to process. * Then calculate next 'itimer' occurrence: itimer_value = now + itimer_interval */ for (i = 0; i < 3; i++ ) { /* Timer inactive or not expired yet */ if (!ctx->itimer_value[i] || ctx->itimer_value[i] > now) continue; /* Timer expired - send a signal. * The target process might be suspended, so the host thread is canceled, and a new * call to 'ke_process_events' is scheduled. Since 'ke_process_events_mutex' is * already locked, the thread-unsafe version of 'ctx_host_thread_suspend_cancel' is used. */ __ctx_host_thread_suspend_cancel(ctx); ke->process_events_force = 1; sim_sigset_add(&ctx->signal_masks->pending, sig[i]); /* Calculate next occurrence */ ctx->itimer_value[i] = 0; if (ctx->itimer_interval[i]) ctx->itimer_value[i] = now + ctx->itimer_interval[i]; } /* Calculate the time when next wakeup occurs. */ ctx->host_thread_timer_wakeup = 0; for (i = 0; i < 3; i++) { if (!ctx->itimer_value[i]) continue; assert(ctx->itimer_value[i] >= now); if (!ctx->host_thread_timer_wakeup || ctx->itimer_value[i] < ctx->host_thread_timer_wakeup) ctx->host_thread_timer_wakeup = ctx->itimer_value[i]; } /* If a new timer was set, launch 'ke_host_thread_timer' again */ if (ctx->host_thread_timer_wakeup) { ctx->host_thread_timer_active = 1; if (pthread_create(&ctx->host_thread_timer, NULL, ke_host_thread_timer, ctx)) fatal("%s: could not create child thread", __FUNCTION__); } } /* * LOOP 3 * Process pending signals in running contexts to launch signal handlers */ for (ctx = ke->running_list_head; ctx; ctx = ctx->running_next) { signal_handler_check(ctx); } /* Unlock */ pthread_mutex_unlock(&ke->process_events_mutex); }
/* Function that suspends the host thread waiting for an event to occur. * When the event finally occurs (i.e., before the function finishes, a * call to 'ke_process_events' is scheduled. * The argument 'arg' is the associated guest context. */ void *ke_host_thread_suspend(void *arg) { struct ctx_t *ctx = (struct ctx_t *) arg; uint64_t now = ke_timer(); /* Detach this thread - we don't want the parent to have to join it to release * its resources. The thread termination can be observed by atomically checking * the 'ctx->host_thread_suspend_active' flag. */ pthread_detach(pthread_self()); /* Context suspended in 'poll' system call */ if (ctx_get_status(ctx, ctx_nanosleep)) { uint64_t timeout; /* Calculate remaining sleep time in microseconds */ timeout = ctx->wakeup_time > now ? ctx->wakeup_time - now : 0; usleep(timeout); } else if (ctx_get_status(ctx, ctx_poll)) { struct fd_t *fd; struct pollfd host_fds; int err, timeout; /* Get file descriptor */ fd = fdt_entry_get(ctx->fdt, ctx->wakeup_fd); if (!fd) fatal("syscall 'poll': invalid 'wakeup_fd'"); /* Calculate timeout for host call in milliseconds from now */ if (!ctx->wakeup_time) timeout = -1; else if (ctx->wakeup_time < now) timeout = 0; else timeout = (ctx->wakeup_time - now) / 1000; /* Perform blocking host 'poll' */ host_fds.fd = fd->host_fd; host_fds.events = ((ctx->wakeup_events & 4) ? POLLOUT : 0) | ((ctx->wakeup_events & 1) ? POLLIN : 0); err = poll(&host_fds, 1, timeout); if (err < 0) fatal("syscall 'poll': unexpected error in host 'poll'"); } else if (ctx_get_status(ctx, ctx_read)) { struct fd_t *fd; struct pollfd host_fds; int err; /* Get file descriptor */ fd = fdt_entry_get(ctx->fdt, ctx->wakeup_fd); if (!fd) fatal("syscall 'read': invalid 'wakeup_fd'"); /* Perform blocking host 'poll' */ host_fds.fd = fd->host_fd; host_fds.events = POLLIN; err = poll(&host_fds, 1, -1); if (err < 0) fatal("syscall 'read': unexpected error in host 'poll'"); } else if (ctx_get_status(ctx, ctx_write)) { struct fd_t *fd; struct pollfd host_fds; int err; /* Get file descriptor */ fd = fdt_entry_get(ctx->fdt, ctx->wakeup_fd); if (!fd) fatal("syscall 'write': invalid 'wakeup_fd'"); /* Perform blocking host 'poll' */ host_fds.fd = fd->host_fd; host_fds.events = POLLOUT; err = poll(&host_fds, 1, -1); if (err < 0) fatal("syscall 'write': unexpected error in host 'write'"); } /* Event occurred - thread finishes */ pthread_mutex_lock(&ke->process_events_mutex); ke->process_events_force = 1; ctx->host_thread_suspend_active = 0; pthread_mutex_unlock(&ke->process_events_mutex); return NULL; }
/* Execute one instruction from each running context. */ void ke_run(void) { //printf("ke run called\n"); struct ctx_t *ctx, *ctx_trav; int flag = 0; if(!ke->running_list_head){ if (!print_no_more) { printf("============ Running list empty ! . Fast forward to next interrupt. ============\n"); print_no_more = true; pl = true; } if(!isEmpty()){ isa_inst_count = findMin().instr_no; //printf("non-empty instruction list\n"); handle_top_interrupt(); } else{ if (!print_no_more || (print_no_more && pl)) { printf("Interrupt queue is also empty.\n"); print_no_more = true; pl = false; } } } /* Run an instruction from every running process */ for (ctx = ke->running_list_head; ctx; ctx = ctx->running_next) { print_no_more = false; int i; //printf ("out - %p\n", ctx); //printf("after\n"); printf("[%d] exec instr\n", ctx->pid); for ( i = 0 ; i < ctx->instr_slice ; ++i) { //printf(" non-empty instruction list\n"); handle_top_interrupt(); if(!ctx_get_status(ctx,ctx_running)){ printf("%d no more running\n", ctx->pid); break; } page_in_out_count = 0; fault_count_in_instruction = 0; //value printed in ctx_execute_inst in context.c ctx_execute_inst(ctx); printf("ins_count: %"PRIu64" | [%d] : page_in_out_count : %d\n", isa_inst_count, ctx->pid, page_in_out_count); //if page_in_out_count > 0, then put the process to suspended list and insert an interrupt if(page_in_out_count > 0){ printf("[%d] : Blocking process for page fault\n", ctx->pid); ctx_update_status(isa_ctx, ctx_suspended); struct interrupt in; in.instr_no = isa_inst_count + page_in_out_count * 10; in.type = iocomplete; in.ctx = isa_ctx; insert(in); } // if (ctx!=ke->running_list_head) // break; } } /* Free finished contexts */ while (ke->finished_list_head) ctx_free(ke->finished_list_head); /* Process list of suspended contexts */ ke_process_events(); }
/* Execute one instruction from each running context. */ void ke_run(void) { struct ctx_t *ctx, *ctx_trav; int flag = 0; ctx = ke->running_list_head; if (!ctx) { printf ("Running List is Empty\n"); if (!my_top(interrupt_list)) { /* Free finished contexts */ while (ke->finished_list_head) ctx_free(ke->finished_list_head); /* Process list of suspended contexts */ // ke_process_events(); return; } else { num_instr_executed = my_top(interrupt_list)->inst_no; struct ctx_t *interrupting_process_context = my_top(interrupt_list)->ctx; if (!ctx_get_status(interrupting_process_context, ctx_suspended)) { printf("suspended process is not suspended which means we don't know what is happening\n"); fflush(stdout); return; } printf("Inside the ISR2\n"); fflush(stdout); ke_list_remove(ke_list_suspended, interrupting_process_context); //ctx_set_status(interrupting_process_context, ctx_running); interrupting_process_context->status = ctx_running; ke_list_insert_tail(ke_list_running, interrupting_process_context); my_delete(interrupt_list); /* Free finished contexts */ while (ke->finished_list_head) ctx_free(ke->finished_list_head); // comment this ?? /* Process list of suspended contexts */ // ke_process_events(); return; } } /* Run an instruction from every running process */ while (ctx) { struct ctx_t * ctx_temp = ctx->running_next; int i; //printf ("out - %p\n", ctx); for ( i = 0 ; i < ctx->instr_slice ; ++i) { if (ctx_get_status(ctx, ctx_suspended)) { printf("Process suspended\n"); break; } if (ctx_get_status(ctx, ctx_finished)) { break; } // CHECK IF THERE ARE INTERRUPTS WAITING TO HAPPEN node* interrupt_list_head = my_top(interrupt_list); while (interrupt_list_head != NULL && interrupt_list_head->inst_no <= num_instr_executed) { printf ("Instruction number %d\n", interrupt_list_head->inst_no); // service interrupt here struct ctx_t *interrupting_process_context = interrupt_list_head->ctx; if (!ctx_get_status(interrupting_process_context, ctx_suspended)) { printf("suspended process is not suspended which means we don't know what is happening\n"); fflush(stdout); return; } printf("Inside the ISR1\n"); fflush(stdout); ke_list_remove(ke_list_suspended, interrupting_process_context); //ctx_set_status(interrupting_process_context, ctx_running); interrupting_process_context->status = ctx_running; ke_list_insert_tail(ke_list_running, interrupting_process_context); my_delete(interrupt_list); interrupt_list_head = my_top(interrupt_list); } fflush(stdout); ctx_execute_inst(ctx); num_instr_executed++; fflush(stdout); } ctx = ctx_temp; } /* Free finished contexts */ while (ke->finished_list_head) ctx_free(ke->finished_list_head); /* Process list of suspended contexts */ // ke_process_events(); }