diff --git a/Makefile b/Makefile index b262c0a24e..5504c2a698 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ OBJS = \ $K/main.o \ $K/vm.o \ $K/proc.o \ + $K/mlfq.o \ $K/swtch.o \ $K/trampoline.o \ $K/trap.o \ diff --git a/kernel/defs.h b/kernel/defs.h index 122d9cae89..657b1a306b 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -102,6 +102,14 @@ int either_copyout(int user_dst, uint64 dst, void *src, uint64 len); int either_copyin(void *dst, int user_src, uint64 src, uint64 len); void procdump(void); +// mlfq.c +void mlfq_init(void); +void mlfq_enqueue(struct proc *p, int priority); +struct proc* mlfq_dequeue(int priority); +int mlfq_queue_empty(int priority); +int mlfq_queue_size(int priority); +void mlfq_remove(struct proc *p); + // swtch.S void swtch(struct context*, struct context*); diff --git a/kernel/main.c b/kernel/main.c index f0d3171d4e..218b40d6b3 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -20,6 +20,7 @@ main() kvminit(); // create kernel page table kvminithart(); // turn on paging procinit(); // process table + mlfq_init(); // initialize MLFQ run queues trapinit(); // trap vectors trapinithart(); // install kernel trap vector plicinit(); // set up interrupt controller diff --git a/kernel/mlfq.c b/kernel/mlfq.c new file mode 100644 index 0000000000..2e17d09843 --- /dev/null +++ b/kernel/mlfq.c @@ -0,0 +1,183 @@ +#include "param.h" +#include "types.h" +#include "memlayout.h" +#include "riscv.h" +#include "defs.h" +#include "spinlock.h" +#include "proc.h" + +// Default number of priority levels if not provided elsewhere. +#ifndef MLFQ_LEVELS +#define MLFQ_LEVELS 5 +#endif + +struct mlfq_queue { + struct spinlock lock; + struct proc *head; + struct proc *tail; + int size; +}; + +static struct mlfq_queue mlfq[MLFQ_LEVELS]; + +static int +mlfq_valid_priority(int priority) +{ + return priority >= 0 && priority < MLFQ_LEVELS; +} + +static struct mlfq_queue * +mlfq_q(int priority) +{ + if(!mlfq_valid_priority(priority)) + panic("mlfq: bad priority"); + return &mlfq[priority]; +} + +void +mlfq_init(void) +{ + for(int i = 0; i < MLFQ_LEVELS; i++){ + initlock(&mlfq[i].lock, "mlfq"); + mlfq[i].head = 0; + mlfq[i].tail = 0; + mlfq[i].size = 0; + } +} + +void +mlfq_enqueue(struct proc *p, int priority) +{ + if(p == 0) + panic("mlfq_enqueue: null proc"); + if(!mlfq_valid_priority(priority)) + panic("mlfq_enqueue: bad priority"); + + struct mlfq_queue *q = mlfq_q(priority); + + acquire(&q->lock); + + // Enqueue is O(1). Caller is responsible for not enqueueing a proc twice. + if(p->mlfq_level != -1) + panic("mlfq_enqueue: proc already queued"); + + p->mlfq_next = 0; + p->mlfq_level = priority; + + if(q->tail){ + q->tail->mlfq_next = p; + q->tail = p; + } else { + q->head = p; + q->tail = p; + } + q->size++; + + release(&q->lock); +} + +struct proc* +mlfq_dequeue(int priority) +{ + if(!mlfq_valid_priority(priority)) + panic("mlfq_dequeue: bad priority"); + + struct mlfq_queue *q = mlfq_q(priority); + + acquire(&q->lock); + + struct proc *p = q->head; + if(p == 0){ + release(&q->lock); + return 0; + } + + q->head = p->mlfq_next; + if(q->head == 0) + q->tail = 0; + + p->mlfq_next = 0; + p->mlfq_level = -1; + q->size--; + + release(&q->lock); + return p; +} + +int +mlfq_queue_empty(int priority) +{ + if(!mlfq_valid_priority(priority)) + panic("mlfq_queue_empty: bad priority"); + + struct mlfq_queue *q = mlfq_q(priority); + acquire(&q->lock); + int empty = (q->size == 0); + release(&q->lock); + return empty; +} + +int +mlfq_queue_size(int priority) +{ + if(!mlfq_valid_priority(priority)) + panic("mlfq_queue_size: bad priority"); + + struct mlfq_queue *q = mlfq_q(priority); + acquire(&q->lock); + int n = q->size; + release(&q->lock); + return n; +} + +static int +mlfq_remove_from_q(struct proc *p, int priority) +{ + struct mlfq_queue *q = mlfq_q(priority); + + acquire(&q->lock); + + struct proc *prev = 0; + for(struct proc *cur = q->head; cur; cur = cur->mlfq_next){ + if(cur == p){ + if(prev) + prev->mlfq_next = cur->mlfq_next; + else + q->head = cur->mlfq_next; + + if(q->tail == cur) + q->tail = prev; + + cur->mlfq_next = 0; + cur->mlfq_level = -1; + q->size--; + + release(&q->lock); + return 1; + } + prev = cur; + } + + release(&q->lock); + return 0; +} + +void +mlfq_remove(struct proc *p) +{ + if(p == 0) + return; + + int level = p->mlfq_level; + if(mlfq_valid_priority(level)){ + if(mlfq_remove_from_q(p, level)) + return; + // Fall through and scan if the hint was stale. + } + + for(int i = 0; i < MLFQ_LEVELS; i++){ + if(mlfq_remove_from_q(p, i)) + return; + } +} + diff --git a/kernel/proc.c b/kernel/proc.c index 22a5401903..b3295f003e 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -6,6 +6,8 @@ #include "proc.h" #include "defs.h" +static const uint64 base_time_quantum[] = { 1 }; + struct cpu cpus[NCPU]; struct proc proc[NPROC]; @@ -124,6 +126,20 @@ allocproc(void) found: p->pid = allocpid(); p->state = USED; + p->mlfq_level = -1; + + // Initialize MLFQ fields. + p->priority = 0; + p->time_slice_remaining = base_time_quantum[0]; + p->cpu_time_used = 0; + p->last_run_time = 0; + p->wait_time = 0; + p->io_count = 0; + p->voluntary_yields = 0; + p->cpu_usage_avg = 0; + p->priority_boost_time = 0; + p->mlfq_next = 0; + p->mlfq_prev = 0; // Allocate a trapframe page. if((p->trapframe = (struct trapframe *)kalloc()) == 0){ @@ -168,6 +184,8 @@ freeproc(struct proc *p) p->chan = 0; p->killed = 0; p->xstate = 0; + p->mlfq_next = 0; + p->mlfq_level = -1; p->state = UNUSED; } diff --git a/kernel/proc.h b/kernel/proc.h index d021857a34..89d27ec676 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -104,4 +104,25 @@ struct proc { struct file *ofile[NOFILE]; // Open files struct inode *cwd; // Current directory char name[16]; // Process name (debugging) + + // MLFQ scheduling fields + int priority; // Current priority level (0=highest) + uint64 time_slice_remaining; // Remaining quantum in ticks + uint64 cpu_time_used; // Total CPU time consumed + uint64 last_run_time; // Timestamp of last execution + uint64 wait_time; // Time spent waiting + // Behavior tracking + uint64 io_count; // Number of I/O operations + uint64 voluntary_yields; // Voluntary context switches + uint64 cpu_usage_avg; // Exponential moving average + // Aging support + uint64 priority_boost_time; // Last priority boost timestamp + // Queue management + struct proc *mlfq_next; // Next process in queue + struct proc *mlfq_prev; // Previous process in queue + + // MLFQ run-queue linkage (protected by the per-queue lock). + // mlfq_level == -1 means "not currently enqueued". + int mlfq_level; + };