Skip to content
Open
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
8 changes: 8 additions & 0 deletions kernel/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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*);

Expand Down
1 change: 1 addition & 0 deletions kernel/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
183 changes: 183 additions & 0 deletions kernel/mlfq.c
Original file line number Diff line number Diff line change
@@ -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;
}
}

18 changes: 18 additions & 0 deletions kernel/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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){
Expand Down Expand Up @@ -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;
}

Expand Down
21 changes: 21 additions & 0 deletions kernel/proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

};