⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sched.c

📁 pebble
💻 C
📖 第 1 页 / 共 2 页
字号:
/* 
 * Copyright 1999, 2000, 2001, 2002 Lucent Technologies Inc.
 * All Rights Reserved.
 * Information Sciences Research Center, Bell Labs.
 *
 * LUCENT TECHNOLOGIES DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE 
 * OR THE SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The
 * software is provided "as is" without expressed or implied warranty 
 * of any kind.
 *
 * These notices must be retained in any copies of any part of this
 * software.
 *
 */

/*
 * User-level scheduler.
 * This code is always executed with interrupts disabled.
 * This version assumes a uniprocessor.
 *
 * *** important ***
 * All portal calls to the scheduler MUST save at least partial
 * state (but never minimal state), since the calling thread may
 * be suspended inside the scheduler.
 *
 * Note:
 * The Thread data structure is allocated in kernel space.
 * So we cannot access it directly. We only keep those pointers
 * and never de-referece them.
 */

#include <string.h>
#include <unistd.h>		/* for write */
#include "diag.h"
#include "pebble.h"
#include "mem.h"		/* for NASID */
#include "log.h"
#include "nucleus.h"
#include "time.h"
#include "inside.h"
#include <assert.h>

typedef	struct elem_t	{
	Thread	*thread;
	int	val;		/* return value from system call */
	Time	wakeup;		/* wakeup time if waiting on timer queue */
	int	asid;		/* thread's asid */
	int	pri;		/* wakeup priority (not always asid2pri[asid])*/
	/* semaphore list */
	struct elem_t *sem_next;
	struct elem_t *sem_last;
	void *sem_q;		/* waiting on a semaphore */
	/* timer list */
	struct elem_t *time_next;
	struct elem_t *time_last;
} Elem;

typedef	struct	{
	Elem	*head;
	Elem	*tail;
} Queue;

typedef	struct semaphore_t {
	int	count;
	struct semaphore_t *next;
	int	asid;		/* ASID that created this semaphore */
	int	portal_no;	/* portal number in requestor's portal table */
	Queue	q;		/* FIFO queue of waiting threads */
} Semaphore;

typedef	struct	{
	Semaphore write_lock;
	Semaphore full;
	Semaphore read_lock;
	Semaphore empty;
	char	*buf;		/* FIFO (pipe) buffer */
	int	head;		/* offset of head pointer into buffer */
	int	tail;		/* offset of tail pointer into buffer */
} Pipe;

typedef	struct	{
	Queue	free_q;		/* values list */
	Queue	wait_q;		/* waiting threads */
} ValQueue;

#define	NELEM		256
#define	PIPE_OFFSET	128
				/* is too short for actual sleep and wakeup */

static	Queue readyq[NPRIORITY];
static	Elem e[NELEM];
static	Elem *free_list;
static	Elem *wakeup_q = NULL;	/* threads timer queue */
static	int asid2pri[NASID];
static	ValQueue val_q[NASID];	/* values queue per domain */

static	Time fuzz;		/* cannot set the next wakup closer than this */
				/* to current time to avoid missing the event */
				/* in high-resolution ticks */
static	Time next_wakeup;	/* next wakeup time in high-resolution ticks */
static	Semaphore *sem_list = NULL;	/* list of all semaphores */


const char PORTAL_CREATE[] = "too many open portals";
const char SEM_OVERFLOW[] = "too many open semaphores";
const char MEM_ALLOC[] = "memory allocation failed";
const char BUF_LEN[] = "invalid buffer length";
const char HRSLEEP_AMOUNT[] = "invalid sleep time";
const char PRIORITY_ERROR[] = "invalid priority or asid";
const char SEM_MAGIC_ERROR[] = "no semaphore was allocated for this domain";
const char NO_CALLER[] = "no domain called current domain";

Time
min_time(Time x, Time y)
{
	return (x < y) ? x : y;
}

Time
max_time(Time x, Time y)
{
	return (x > y) ? x : y;
}

void
elem_init(void)
{
	int i;

	free_list = NULL;
	for (i = 0; i < NELEM; i++) {
		e[i].sem_next = free_list;
		free_list = &e[i];
	}
}

static Elem *
new_elem(void)
{
	Elem *ep;

	if ((ep = free_list) == NULL)
		panic("sched: no free elem");
	free_list = ep->sem_next;

	return ep;
}


static void
free_elem(Elem *ep)
{
	ep->sem_next = free_list;
	free_list = ep;
}


/* append to end of queue */
Elem *
enqueue(Queue *q, Thread *t, int asid, int pri, int ret_val)
{
	Elem *ep;

	ep = new_elem();
	ep->thread = t;
	ep->asid = asid;
	ep->pri = pri;
	ep->val = ret_val;
	
	if (q->tail == NULL)
		q->head = ep;
	else
		q->tail->sem_next = ep;
	ep->sem_next = NULL;
	ep->sem_last = q->tail;
	q->tail = ep;
	ep->sem_q = q;

	/* do not insert in timer queue */
	ep->time_next = ep->time_last = NULL;
	ep->wakeup = 0;

	return ep;
}

/* insert at head of queue (this is never a semaphore) */
Elem *
enqueue_head(Queue *q, Thread *t, int asid, int pri, int ret_val)
{
	Elem *ep;

	ep = new_elem();
	ep->thread = t;
	ep->asid = asid;
	ep->pri = pri;
	ep->val = ret_val;
	
	ep->sem_next = q->head;
	ep->sem_last = NULL;
	if (q->head == NULL)
		q->tail = ep;
	else
		q->head->sem_last = ep;
	q->head = ep;
	ep->sem_q = NULL;

	/* do not insert in timer queue */
	ep->time_next = ep->time_last = NULL;
	ep->wakeup = 0;

	return ep;
}

/* dequeue element from the head of the queue */
Thread *
dequeue(Queue *q, int *caller_asid, int *pri, int *ret_val)
{
	Elem *ep;
	Thread *t;

	if ((ep = q->head) == NULL)
		return NULL;

	assert(ep->sem_last == NULL);
	q->head = ep->sem_next;
	if (q->head == NULL)
		q->tail = NULL;
	else
		q->head->sem_last = NULL;

	/* remove element also from timer queue */
	if (ep->wakeup != 0) {
		if (ep->time_last != NULL)
			ep->time_last->time_next = ep->time_next;
		else
			wakeup_q = ep->time_next;

		if (ep->time_next != NULL)
			ep->time_next->time_last = ep->time_last;
	}

	t = ep->thread;
	*ret_val = ep->val;
	*caller_asid = ep->asid;
	*pri = ep->pri;
	free_elem(ep);

	return t;
}

/* insert into sorted wakeup queue */
void
enqueue_timer(Elem *ep, Time wakeup)
{
	Elem *p, *last_p;
	register Time next_t;

	ep->wakeup = wakeup;

	last_p = NULL;
	for (p = wakeup_q; p != NULL && ep->wakeup >= p->wakeup;
	     p = p->time_next)
		last_p = p;

	if (last_p == NULL)
		wakeup_q = ep;
	else
		last_p->time_next = ep;
	ep->time_next = p;
	ep->time_last = last_p;
	if (p != NULL)
		p->time_last = ep;

	/* reset timer */
	if (next_wakeup != wakeup_q->wakeup) {
		/* ensure that we reset the timer to a future time, */
		/* after the actual time that set_timer() is called */
		next_t = max_time(wakeup_q->wakeup, hrtime() + fuzz);
		if (next_t < next_wakeup) {
	 		next_wakeup = next_t;
			set_timer(next_t);
		}
	}
}

int
sys_dump_sched(Thread *t)
{
	int n = 0;
	Elem *p;
	Semaphore *s;
	int i;

	fdprintf(2, "ready queue:\n");
	for (i = 0; i < NPRIORITY; i++)
		for (p = readyq[i].head; p != NULL; p = p->sem_next) {
			n++;
			fdprintf(2, "ready thread %p asid %d priority %d\n",
				p->thread, p->asid, i);
		}
	if (n == 0)
		fdprintf(2, "ready queue is empty\n");

	fdprintf(2, "semaphores:\n");
	n = 0;
	for (s = sem_list ; s != NULL; s = s->next) {
		if (s->q.head != NULL)
			fdprintf(2,
		"semaphore %p created by asid %d portal %d count=%d\n",
			s, s->asid, s->portal_no, s->count);
		for (p = s->q.head; p != NULL; p = p->sem_next) {
			n++;
			fdprintf(2, "thread %p asid %d waiting on sem %p\n",
				p->thread, p->asid, s);
		}
	}
	if (n == 0)
		fdprintf(2, "no thread waiting on a semaphore\n");

	fdprintf(2, "timer queue:\n");
	n = 0;
	for (p = wakeup_q; p != NULL; p = p->time_next) {
		n++;
		fdprintf(2, "thread %p waiting until %u %u\n",
			p->thread, (uint)(p->wakeup>>32), (uint)p->wakeup);
	}
	if (n == 0)
		fdprintf(2, "timer queue is empty\n");

	return 0;
}

int
sys_ready(Thread *caller, Thread *p, int asid, int ret_val)
{
	int pri;

	pri = asid2pri[asid];
	enqueue(&readyq[pri], p, asid, pri, ret_val);

	return(0);
}


/*
 * Calls the scheduler and never returns.
 * 
 * This routine is called whenever the current thread has to be de-scheduled
 * either voluntarily (waiting on a semaphore or timer event) or involuntarily
 * (by an interrupt).
 * In the latter case, we return the thread to the *head* of the ready queue.
 *
 * This routine is called from sem_post with the caller's ASID.
 * If sem_post was called by the thread, it may be de-scheduled on the spot.
 * If sem_post was called by the interrupt dispatcher, sys_sched() will
 * never de-schedule the intrrupt dispatcher, since it has the highest
 * priority.
 *
 * The last line of the interrupt dispatcher calls sys_sched() with the
 * identity of the poor thread that was interrupted.
 * This call is done via a null portal, which does not save state on the
 * call stack.
 * In this case, sys_sched() may decide to schedule a thread with a higher
 * priority. So the interrupted thread is inserted at the head of the
 * ready queue, since it should not suffer unduly from the interrupt.
 *
 * Note that sys_sched() may be also called by the last sem_post in the
 * scheduler or by the timer_handler, also from the interrupt dispatcher.
 * In both cases, the last call (corresponding to the portal traversal
 * from the interrupt dispatcher to the scheduler) is removed from the
 * call stack prior to calling this routine.
 *
 * >> Future enhancement <<
 * If we will have time-slicing, we will append the thread at the tail
 * for timer interrupts, and to the head of the queue in all other cases.
 */
void
sys_sched(Thread *t, int caller_asid, int caller_val)
{
	Thread *p = NULL;
	int val;
	int caller_pri = -1;
	int i;
	int asid, pri;
	Time now;

	if (SCHED_LOG) {
		put_log(SCHED_LOG_SCHED, (int)t);
		put_log(SCHED_LOG_ASID, caller_asid);
	}

	if (t != NULL)
		caller_pri = asid2pri[caller_asid];

	for (i = PRI_MAX; i > caller_pri; i--)
		if ((p = dequeue(&readyq[i], &asid, &pri, &val)) != NULL)
			break;

	if (p != NULL) {
		/* if we have to be de-scheduled, return to head of queue */
		if (t != NULL) {
			enqueue_head(&readyq[caller_pri], t, caller_asid,
				caller_pri, caller_val);
			if (SCHED_LOG) {
				put_log(SCHED_LOG_READY, (int)t);
				put_log(SCHED_LOG_RUN_PRIORITY, caller_pri);
			}
		}
		if (SCHED_LOG) {
			put_log(SCHED_LOG_RUN, (int)p);
			put_log(SCHED_LOG_RUN_PRIORITY, i);
		}
		run(p, val);
	}

	/* there is no thread of a higher priority than the caller */
	if (t != NULL) {
		if (SCHED_LOG) {
			put_log(SCHED_LOG_RUN, (int)t);
			put_log(SCHED_LOG_RUN_PRIORITY, caller_pri);
		}
		run(t, caller_val);
	}

	if (SCHED_DIAG) {
		now = hrtime();
		fdprintf(2, "*** scheduler has nothing to run *** time=%u %u\n",
			(uint)(now>>32), (uint)now);
		sys_dump_sched(t);
		dump_lock();
	}
	panic("scheduler: nothing to run: caller asid=%d priority=%d thread=%p",
		caller_asid, caller_pri, t);
}

void
sys_yield(Thread *t, int asid)
{
	int pri;
//	Time now;

	if (SCHED_DIAG) {
		fdprintf(2, "yield called from thread %p, asid %d\n", t, asid);
	}

	/* the following condition will be eliminated by the compiler */
	/* if INTR_INSIDE is zero. */
	if (INTR_INSIDE && is_intr_stack())
		panic("interrupt handler thread %p called yield", t);

	if (SCHED_LOG)
		put_log(SCHED_LOG_YIELD, (int)t);

#if 0
	if (SCHED_DIAG) {
		now = hrtime();
		fdprintf(2, "*** dumping scheduler queue in yield.. *** time=%u %u\n",
			(uint)(now>>32), (uint)now);
		sys_dump_sched(t);
		dump_lock();
	}
#endif

	pri = asid2pri[asid];
	if (SCHED_DIAG) {
		fdprintf(2, "yield enqueing thread %p, asid %d, pri %d\n", t, asid, pri);
	}
	enqueue(&readyq[pri], t, asid, pri, 0);
	/* if the is a ready thread of higher or equal priority, run it */
	sys_sched(0, 0, 0);
}


int
sys_sem_wait(int asid, int caller_pri, Time wakeup, Semaphore *s, Thread *t)
{
	Elem *ep;
	int pri, caller_asid;

	/* the following condition will be eliminated by the compiler */
	/* if INTR_INSIDE is zero. */
	if (INTR_INSIDE && is_intr_stack())
		panic("interrupt handler thread %p blocked on sem_wait on %p",
			t, s);

	if (SCHED_LOG)
		put_log(SCHED_LOG_SEM_WAIT, (int)s);

	if (s->count > 0) {
		s->count--;
		if (SCHED_LOG)
			put_log((int)s,  s->count);
		return (0);
	}

	/* timeout in the past */
	if (wakeup != 0 && wakeup < hrtime())
		error(HRSLEEP_AMOUNT);
	
	if (caller_pri) {
		caller_asid = get_caller_asid(2);
		if (caller_asid < 0)
			error(NO_CALLER);
		pri = asid2pri[caller_asid];
	} else
		pri = asid2pri[asid];

	pri = asid2pri[asid];
	assert(t != NULL);
	ep = enqueue(&s->q, t, asid, pri, 0);
	if (wakeup != 0) {
		enqueue_timer(ep, wakeup);
	}
	sys_sched(0, 0, 0);

	/* not reached */
	return (0);
}


/*
 *	Post semaphore "s".
 *
 *	If this is the last call from the interrupt dispatcher,
 *	then this routine was called by a null portal, so we
 *	return to the thread that was running at the time of the
 *	interrupt. In this case, the interrupt dispatcher sets the
 *	2nd argument to contain the interrupted thread's ASID.
 * 	actually, it sets it to -ASID to distinguish between a regular
 *	sem_post() and the last sem_post() in the interrupt dispatcher.
 */
void
sys_sem_post(Thread *t, int caller_asid, Semaphore *s)
{
	Thread *p;
	int val;
	int asid;
	int pri;

	s->count++;
	if (SCHED_LOG) {
		put_log(SCHED_LOG_SEM_POST, (int)s);
		put_log((int)s, s->count);
	}

	/* Note that waiting threads are stored in a FIFO queue */
	if (s->count > 0 && (p = dequeue(&s->q, &asid, &pri, &val)) != NULL) {
		s->count--;
		if (SCHED_LOG) {
			put_log(SCHED_LOG_READY, (int)p);
			put_log(SCHED_LOG_RUN_PRIORITY, pri);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -