Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions sched/.clang-files
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ user/faultreadkernel.c
user/faultnostack.c
user/breakpoint.c
user/fairness.c
user/sleeptest.c
user/priotest.c
user/threadtest.c
user/divzero.c
user/faultbadhandler.c
user/buggyhello2.c
Expand All @@ -36,6 +39,7 @@ lib/panic.c
lib/readline.c
lib/syscall.c
lib/fork.c
lib/thread.c
lib/printfmt.c
lib/exit.c
lib/string.c
Expand Down
8 changes: 8 additions & 0 deletions sched/inc/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum { ENV_FREE = 0, ENV_DYING, ENV_RUNNABLE, ENV_RUNNING, ENV_NOT_RUNNABLE };
// Special environment types
enum EnvType {
ENV_TYPE_USER = 0,
ENV_TYPE_THREAD, // Part 4: thread that shares its address space with its parent
};

struct Env {
Expand All @@ -59,6 +60,13 @@ struct Env {
uint32_t env_ipc_value; // Data value sent to us
envid_t env_ipc_from; // envid of the sender
int env_ipc_perm; // Perm of page mapping received

// Part 2: sleep based on timer ticks
uint32_t env_sleep_until; // global tick at which the env should wake up (0 = not sleeping)

// Part 3: priorities
uint32_t env_priority; // env's priority (higher value = higher priority)
uint32_t env_wait_ticks; // ticks accumulated waiting in the ready queue (for aging)
};

#endif // !JOS_INC_ENV_H
10 changes: 10 additions & 0 deletions sched/inc/lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ int sys_page_map(
int sys_page_unmap(envid_t env, void *pg);
int sys_ipc_try_send(envid_t to_env, uint32_t value, void *pg, int perm);
int sys_ipc_recv(void *rcv_pg);
// Part 2
int sys_sleep(uint32_t n);
// Part 3
int sys_getpriority(envid_t envid);
int sys_setpriority(envid_t envid, uint32_t priority);
// Part 4
envid_t sys_thread_create(void *entry, void *ustack_top);

// This must be inlined. Exercise for reader: why?
static inline envid_t __attribute__((always_inline)) sys_exofork(void)
Expand All @@ -72,6 +79,9 @@ envid_t ipc_find_env(enum EnvType type);
envid_t fork(void);
envid_t sfork(void); // Challenge!

// thread.c
envid_t thread_create(void (*func)(void *), void *arg);


/* File open modes */
#define O_RDONLY 0x0000 /* open for reading only */
Expand Down
7 changes: 7 additions & 0 deletions sched/inc/syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ enum {
SYS_yield,
SYS_ipc_try_send,
SYS_ipc_recv,
// Part 2: sleep based on timer ticks
SYS_sleep,
// Part 3: priority handling
SYS_getpriority,
SYS_setpriority,
// Part 4: thread creation
SYS_thread_create,
NSYSCALLS
};

Expand Down
5 changes: 4 additions & 1 deletion sched/kern/Makefrag
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ KERN_BINFILES += user/idle \
user/fairness \
user/pingpong \
user/pingpongs \
user/primes
user/primes \
user/sleeptest \
user/priotest \
user/threadtest
KERN_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES))
KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES))
KERN_OBJFILES := $(patsubst $(OBJDIR)/lib/%, $(OBJDIR)/kern/%, $(KERN_OBJFILES))
Expand Down
10 changes: 8 additions & 2 deletions sched/kern/env.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ region_alloc(struct Env *e, void *va, size_t len)
if (!(p = page_alloc(!ALLOC_ZERO)))
panic("region_alloc: could not alloc page\n");
if (page_insert(e->env_pgdir, p, (uint32_t *) va_act, PTE_U | PTE_W))
panic("region_alloc: page_insert falied\n");
panic("region_alloc: page_insert failed\n");
}
}

Expand Down Expand Up @@ -402,6 +402,12 @@ env_create(uint8_t *binary, enum EnvType type)
//
// Frees env e and all memory it uses.
//
// Part 4: the code below unmaps all of 'e's user-space memory and frees its
// page directory. A thread (ENV_TYPE_THREAD) shares its env_pgdir with its
// parent process: freeing it here would destroy the parent's memory and that
// of the other threads. Consider both directions of the parent/thread
// relationship when implementing this part.
//
void
env_free(struct Env *e)
{
Expand Down Expand Up @@ -509,7 +515,7 @@ env_run(struct Env *e)
// Your code here
curenv = e;

// Needed if we run with multiple procesors
// Needed if we run with multiple processors
// Record the CPU we are running on for user-space debugging
unlock_kernel();
curenv->env_cpunum = cpunum();
Expand Down
9 changes: 7 additions & 2 deletions sched/kern/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,18 @@ i386_init(void)
ENV_CREATE(TEST, ENV_TYPE_USER);
#else
// Touch all you want.
// To run the tests for each part, replace these lines with, e.g.:
// ENV_CREATE(user_sleeptest, ENV_TYPE_USER); // Part 2
// ENV_CREATE(user_priotest, ENV_TYPE_USER); // Part 3
// ENV_CREATE(user_threadtest, ENV_TYPE_USER); // Part 4
// The binaries are already declared in kern/Makefrag (KERN_BINFILES).
ENV_CREATE(user_hello, ENV_TYPE_USER);
ENV_CREATE(user_hello, ENV_TYPE_USER);
ENV_CREATE(user_hello, ENV_TYPE_USER);
#endif // TEST*

// Eliminar esta llamada una vez completada la parte 1
// e implementado sched_yield().
// Remove this call once part 1 is complete
// and sched_yield() is implemented.
env_run(&envs[0]);

// Schedule and run the first user environment!
Expand Down
49 changes: 33 additions & 16 deletions sched/kern/sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,50 @@
#include <kern/env.h>
#include <kern/pmap.h>
#include <kern/monitor.h>
#include <kern/trap.h>

void sched_halt(void);

// Wake up environments that were sleeping and whose env_sleep_until <= ticks.
// The global counter 'ticks' is declared in kern/trap.h.
// Call from trap_dispatch after incrementing ticks and before sched_yield.
void
sched_wakeup_sleeping(void)
{
// Part 2: Your code here - wake up sleeping envs
}

// Choose a user environment to run and run it.
void
sched_yield(void)
{
#ifdef SCHED_ROUND_ROBIN
// Implement simple round-robin scheduling.
// Implement round-robin scheduling with timer preemption.
//
// Search through 'envs' for an ENV_RUNNABLE environment in
// circular fashion starting just after the env this CPU was
// last running. Switch to the first such environment found.
// Walk 'envs' circularly looking for an ENV_RUNNABLE environment,
// starting right after the last environment run on this CPU. Switch
// to the first one found.
//
// If no envs are runnable, but the environment previously
// running on this CPU is still ENV_RUNNING, it's okay to
// choose that environment.
// If no env is runnable but the environment that was running on this
// CPU is still ENV_RUNNING, it's fine to pick it again.
//
// Never choose an environment that's currently running on
// another CPU (env_status == ENV_RUNNING). If there are
// no runnable environments, simply drop through to the code
// below to halt the cpu.
// Never pick an environment that's currently running on another CPU
// (env_status == ENV_RUNNING). If none is available, fall through
// to sched_halt.

// Your code here - Round robin
#endif

#ifdef SCHED_PRIORITIES
// Implement simple priorities scheduling.
// Implement priority scheduling with anti-starvation aging.
//
// Environments now have a "priority" so it must be consider
// when the selection is performed.
// Select the ENV_RUNNABLE environment with the highest env_priority.
// Increment env_wait_ticks for the environments that are not selected.
// Apply aging: if an environment has been waiting too long, temporarily
// increase its effective priority to avoid starvation.
//
// Be careful to not fall in "starvation" such that only one
// environment is selected and run every time.
// If the selected environment is the same as curenv and it's still
// ENV_RUNNING, it's fine to run it again.

// Your code here - Priorities
#endif
Expand All @@ -62,6 +72,13 @@ sched_halt(void)

// For debugging and testing purposes, if there are no runnable
// environments in the system, then drop into the kernel monitor.
//
// Part 2: careful, a sleeping process (sys_sleep) is ENV_NOT_RUNNABLE,
// so it doesn't count as "runnable" in this check. What happens, then,
// if every process is sleeping at the same time? Review this condition
// to distinguish "nothing to do right now" (should wait for the timer)
// from "no process is left alive" (only then should statistics be
// printed and the kernel halted for good).
for (i = 0; i < NENV; i++) {
if ((envs[i].env_status == ENV_RUNNABLE ||
envs[i].env_status == ENV_RUNNING ||
Expand Down
4 changes: 4 additions & 0 deletions sched/kern/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@
// This function does not return.
void sched_yield(void) __attribute__((noreturn));

// Wake up sleeping environments whose env_sleep_until has already elapsed.
// Part 2: implement in kern/sched.c, call from trap_dispatch.
void sched_wakeup_sleeping(void);

#endif // !JOS_KERN_SCHED_H
72 changes: 70 additions & 2 deletions sched/kern/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,7 @@ sys_page_unmap(envid_t envid, void *va)
// then no page mapping is transferred, but no error occurs.
// The ipc only happens when no errors occur.
//
// Returns 0 on success, < 0 on error.
// Errors are:
// Returns 0 on success, < 0 on error. Errors are:
// -E_BAD_ENV if environment envid doesn't currently exist.
// (No need to check permissions.)
// -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv,
Expand Down Expand Up @@ -429,6 +428,67 @@ sys_ipc_recv(void *dstva)
return 0;
}

// Put the current environment to sleep for 'n' timer interrupts.
// The environment becomes ENV_NOT_RUNNABLE and env_sleep_until is set to
// (global_ticks + n). The scheduler will wake it up once global_ticks >=
// env_sleep_until.
//
// This function only returns on error, but the system call will eventually
// return 0 on success.
// Return < 0 on error. Errors are:
// -E_INVAL if n == 0.
static int
sys_sleep(uint32_t n)
{
// Part 2: Your code here
//
// Hint: See how sys_ipc_recv and sys_ipc_try_send solve the problem
// related with not retuning but anyhow setting a "return value".
return -E_INVAL;
}

// Return the priority of the environment envid.
// Returns < 0 on error. Errors are:
// -E_BAD_ENV if envid doesn't exist or the caller lacks permission.
static int
sys_getpriority(envid_t envid)
{
// Part 3: Your code here
return -E_INVAL;
}

// Set the priority of the environment envid to 'priority'.
// A process CANNOT increase its own priority (only reduce it).
// A process CAN modify the priority of its direct children.
//
// Returns 0 on success, or < 0 on error. Errors are:
// -E_BAD_ENV if envid doesn't exist or without permission.
// -E_INVAL if trying to increase the current process's priority.
static int
sys_setpriority(envid_t envid, uint32_t priority)
{
// Part 3: Your code here
return -E_INVAL;
}

// Create a new thread that shares the current process's address
// space. The thread starts executing at 'entry' with the stack pointing to
// 'ustack_top'.
//
// Unlike fork, the thread does NOT have its own page directory; it uses the
// same env_pgdir as the parent process. When the parent process terminates,
// all of its threads are destroyed too.
//
// Returns the envid of the new thread on success, or < 0 on error. Errors are:
// -E_NO_FREE_ENV if there are no free entries in the PCB.
// -E_INVAL if entry or ustack_top are invalid.
static envid_t
sys_thread_create(void *entry, void *ustack_top)
{
// Part 4: Your code here
return -E_INVAL;
}

// Dispatches to the correct kernel function, passing the arguments.
int32_t
syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
Expand Down Expand Up @@ -464,6 +524,14 @@ syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4,
return sys_env_set_pgfault_upcall(a1, (void *) a2);
case SYS_yield:
sys_yield(); // No return
case SYS_sleep:
return sys_sleep(a1);
case SYS_getpriority:
return sys_getpriority(a1);
case SYS_setpriority:
return sys_setpriority(a1, a2);
case SYS_thread_create:
return sys_thread_create((void *) a1, (void *) a2);
default:
return -E_INVAL;
}
Expand Down
14 changes: 11 additions & 3 deletions sched/kern/trap.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ static struct Taskstate ts;
*/
static struct Trapframe *last_tf;

// Global counter of timer ticks.
// Part 2: initialize to 0 and increment on every timer interrupt.
uint32_t ticks = 0;

/* Interrupt descriptor table. (Must be built at run time because
* shifted function addresses can't be represented in relocation records.)
*/
Expand Down Expand Up @@ -222,11 +226,15 @@ trap_dispatch(struct Trapframe *tf)
return;
}

// Handle clock interrupts. Don't forget to acknowledge the
// interrupt using lapic_eoi() before calling the scheduler!
// Handle clock interrupts.
// - Acknowledge the interrupt using lapic_eoi()
// - Increment ticks
// - Wake up sleeping environments
// - Call the scheduler
switch (tf->tf_trapno - IRQ_OFFSET) {
case IRQ_TIMER:
lapic_eoi();
// Part 2: Your code here
sched_yield();
return;
}
Expand Down Expand Up @@ -269,7 +277,7 @@ trap(struct Trapframe *tf)
lock_kernel();
assert(curenv);

// Garbage collect if current enviroment is a zombie
// Garbage collect if current environment is a zombie
if (curenv->env_status == ENV_DYING) {
env_free(curenv);
curenv = NULL;
Expand Down
5 changes: 5 additions & 0 deletions sched/kern/trap.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
#error "This is a JOS kernel header; user programs should not #include it"
#endif

#include <inc/types.h>
#include <inc/trap.h>
#include <inc/mmu.h>

/* The kernel's interrupt descriptor table */
extern struct Gatedesc idt[];
extern struct Pseudodesc idt_pd;

/* Global counter of timer ticks (defined in kern/trap.c).
* Part 2: must be incremented on every IRQ_TIMER interrupt. */
extern uint32_t ticks;

void trap_init(void);
void trap_init_percpu(void);
void print_regs(struct PushRegs *regs);
Expand Down
3 changes: 2 additions & 1 deletion sched/lib/Makefrag
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ LIB_SRCFILES := $(LIB_SRCFILES) \
lib/pgfault.c \
lib/pfentry.S \
lib/fork.c \
lib/ipc.c
lib/ipc.c \
lib/thread.c



Expand Down
Loading
Loading