|
|
GNU Mach 1.4
/*
* Linux scheduling support.
*
* Copyright (C) 1996 The University of Utah and the Computer Systems
* Laboratory at the University of Utah (CSL)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Author: Shantanu Goel, University of Utah CSL
*/
/*
* linux/kernel/sched.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#include <sys/types.h>
#include <machine/spl.h>
#include <mach/boolean.h>
#include <kern/thread.h>
#include <kern/sched_prim.h>
#include <kern/printf.h>
#include <machine/machspl.h>
#define MACH_INCLUDE
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
#include <linux/dev/glue/glue.h>
#include <asm/system.h>
#include <asm/atomic.h>
int securelevel = 0;
static void timer_bh (void);
DECLARE_TASK_QUEUE (tq_timer);
DECLARE_TASK_QUEUE (tq_immediate);
DECLARE_TASK_QUEUE (tq_scheduler);
static struct wait_queue **auto_config_queue;
static inline void
handle_soft_intr (void)
{
if (bh_active & bh_mask)
{
intr_count = 1;
linux_soft_intr ();
intr_count = 0;
}
}
static void
tqueue_bh (void)
{
run_task_queue(&tq_timer);
}
static void
immediate_bh (void)
{
run_task_queue (&tq_immediate);
}
void
add_wait_queue (struct wait_queue **q, struct wait_queue *wait)
{
unsigned long flags;
if (! linux_auto_config)
{
save_flags (flags);
cli ();
assert_wait ((event_t) q, FALSE);
restore_flags (flags);
return;
}
if (auto_config_queue)
printf ("add_wait_queue: queue not empty\n");
auto_config_queue = q;
}
void
remove_wait_queue (struct wait_queue **q, struct wait_queue *wait)
{
unsigned long flags;
if (! linux_auto_config)
{
save_flags (flags);
thread_wakeup ((event_t) q);
restore_flags (flags);
return;
}
auto_config_queue = NULL;
}
static inline int
waking_non_zero (struct semaphore *sem)
{
int ret;
unsigned long flags;
get_buzz_lock (&sem->lock);
save_flags (flags);
cli ();
if ((ret = (sem->waking > 0)))
sem->waking--;
restore_flags (flags);
give_buzz_lock (&sem->lock);
return ret;
}
void
__up (struct semaphore *sem)
{
atomic_inc (&sem->waking);
wake_up (&sem->wait);
}
int
__do_down (struct semaphore *sem, int task_state)
{
unsigned long flags;
int ret = 0;
int s;
if (!linux_auto_config)
{
save_flags (flags);
s = splhigh ();
for (;;)
{
if (waking_non_zero (sem))
break;
if (task_state == TASK_INTERRUPTIBLE && issig ())
{
ret = -EINTR;
atomic_inc (&sem->count);
break;
}
assert_wait ((event_t) &sem->wait,
task_state == TASK_INTERRUPTIBLE ? TRUE : FALSE);
splx (s);
schedule ();
s = splhigh ();
}
splx (s);
restore_flags (flags);
return ret;
}
while (!waking_non_zero (sem))
{
if (task_state == TASK_INTERRUPTIBLE && issig ())
{
ret = -EINTR;
atomic_inc (&sem->count);
break;
}
schedule ();
}
return ret;
}
void
__down (struct semaphore *sem)
{
__do_down(sem, TASK_UNINTERRUPTIBLE);
}
int
__down_interruptible (struct semaphore *sem)
{
return __do_down (sem, TASK_INTERRUPTIBLE);
}
void
__sleep_on (struct wait_queue **q, int state)
{
unsigned long flags;
if (!q)
return;
save_flags (flags);
if (!linux_auto_config)
{
assert_wait ((event_t) q, state == TASK_INTERRUPTIBLE ? TRUE : FALSE);
sti ();
schedule ();
restore_flags (flags);
return;
}
add_wait_queue (q, NULL);
sti ();
while (auto_config_queue)
schedule ();
restore_flags (flags);
}
void
sleep_on (struct wait_queue **q)
{
__sleep_on (q, TASK_UNINTERRUPTIBLE);
}
void
interruptible_sleep_on (struct wait_queue **q)
{
__sleep_on (q, TASK_INTERRUPTIBLE);
}
void
wake_up (struct wait_queue **q)
{
unsigned long flags;
if (! linux_auto_config)
{
if (q != &wait_for_request) /* ??? by OKUJI Yoshinori. */
{
save_flags (flags);
thread_wakeup ((event_t) q);
restore_flags (flags);
}
return;
}
if (auto_config_queue == q)
auto_config_queue = NULL;
}
void
__wait_on_buffer (struct buffer_head *bh)
{
unsigned long flags;
save_flags (flags);
if (! linux_auto_config)
{
while (1)
{
cli ();
run_task_queue (&tq_disk);
if (! buffer_locked (bh))
break;
bh->b_wait = (struct wait_queue *) 1;
assert_wait ((event_t) bh, FALSE);
sti ();
schedule ();
}
restore_flags (flags);
return;
}
sti ();
while (buffer_locked (bh))
{
run_task_queue (&tq_disk);
schedule ();
}
restore_flags (flags);
}
void
unlock_buffer (struct buffer_head *bh)
{
unsigned long flags;
save_flags (flags);
cli ();
clear_bit (BH_Lock, &bh->b_state);
if (bh->b_wait && ! linux_auto_config)
{
bh->b_wait = NULL;
thread_wakeup ((event_t) bh);
}
restore_flags (flags);
}
void
schedule (void)
{
if (intr_count)
printk ("Aiee: scheduling in interrupt %p\n",
__builtin_return_address (0));
handle_soft_intr ();
run_task_queue (&tq_scheduler);
if (!linux_auto_config)
thread_block (0);
}
void
linux_sched_init (void)
{
/*
* Install software interrupt handlers.
*/
init_bh (TIMER_BH, timer_bh);
init_bh (TQUEUE_BH, tqueue_bh);
init_bh (IMMEDIATE_BH, immediate_bh);
}
/*
* Linux timers.
*
* Copyright (C) 1996 The University of Utah and the Computer Systems
* Laboratory at the University of Utah (CSL)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Author: Shantanu Goel, University of Utah CSL
*/
unsigned long volatile jiffies = 0;
/*
* Mask of active timers.
*/
unsigned long timer_active = 0;
/*
* List of timeout routines.
*/
struct timer_struct timer_table[32];
#define TVN_BITS 6
#define TVR_BITS 8
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)
#define SLOW_BUT_DEBUGGING_TIMERS 0
struct timer_vec
{
int index;
struct timer_list *vec[TVN_SIZE];
};
struct timer_vec_root
{
int index;
struct timer_list *vec[TVR_SIZE];
};
static struct timer_vec tv5 =
{0};
static struct timer_vec tv4 =
{0};
static struct timer_vec tv3 =
{0};
static struct timer_vec tv2 =
{0};
static struct timer_vec_root tv1 =
{0};
static struct timer_vec *const tvecs[] =
{
(struct timer_vec *) &tv1, &tv2, &tv3, &tv4, &tv5
};
#define NOOF_TVECS (sizeof(tvecs) / sizeof(tvecs[0]))
static unsigned long timer_jiffies = 0;
static inline void
insert_timer (struct timer_list *timer, struct timer_list **vec, int idx)
{
if ((timer->next = vec[idx]))
vec[idx]->prev = timer;
vec[idx] = timer;
timer->prev = (struct timer_list *) &vec[idx];
}
static inline void
internal_add_timer (struct timer_list *timer)
{
/*
* must be cli-ed when calling this
*/
unsigned long expires = timer->expires;
unsigned long idx = expires - timer_jiffies;
if (idx < TVR_SIZE)
{
int i = expires & TVR_MASK;
insert_timer (timer, tv1.vec, i);
}
else if (idx < 1 << (TVR_BITS + TVN_BITS))
{
int i = (expires >> TVR_BITS) & TVN_MASK;
insert_timer (timer, tv2.vec, i);
}
else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS))
{
int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
insert_timer (timer, tv3.vec, i);
}
else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS))
{
int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
insert_timer (timer, tv4.vec, i);
}
else if (expires < timer_jiffies)
{
/* can happen if you add a timer with expires == jiffies,
* or you set a timer to go off in the past
*/
insert_timer (timer, tv1.vec, tv1.index);
}
else if (idx < 0xffffffffUL)
{
int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
insert_timer (timer, tv5.vec, i);
}
else
{
/* Can only get here on architectures with 64-bit jiffies */
timer->next = timer->prev = timer;
}
}
void
add_timer (struct timer_list *timer)
{
unsigned long flags;
save_flags (flags);
cli ();
#if SLOW_BUT_DEBUGGING_TIMERS
if (timer->next || timer->prev)
{
printk ("add_timer() called with non-zero list from %p\n",
__builtin_return_address (0));
goto out;
}
#endif
internal_add_timer (timer);
#if SLOW_BUT_DEBUGGING_TIMERS
out:
#endif
restore_flags (flags);
}
static inline int
detach_timer (struct timer_list *timer)
{
int ret = 0;
struct timer_list *next, *prev;
next = timer->next;
prev = timer->prev;
if (next)
{
next->prev = prev;
}
if (prev)
{
ret = 1;
prev->next = next;
}
return ret;
}
int
del_timer (struct timer_list *timer)
{
int ret;
unsigned long flags;
save_flags (flags);
cli ();
ret = detach_timer (timer);
timer->next = timer->prev = 0;
restore_flags (flags);
return ret;
}
static inline void
run_old_timers (void)
{
struct timer_struct *tp;
unsigned long mask;
for (mask = 1, tp = timer_table + 0; mask; tp++, mask += mask)
{
if (mask > timer_active)
break;
if (!(mask & timer_active))
continue;
if (tp->expires > jiffies)
continue;
timer_active &= ~mask;
tp->fn ();
sti ();
}
}
static inline void
cascade_timers (struct timer_vec *tv)
{
/* cascade all the timers from tv up one level */
struct timer_list *timer;
timer = tv->vec[tv->index];
/*
* We are removing _all_ timers from the list, so we don't have to
* detach them individually, just clear the list afterwards.
*/
while (timer)
{
struct timer_list *tmp = timer;
timer = timer->next;
internal_add_timer (tmp);
}
tv->vec[tv->index] = NULL;
tv->index = (tv->index + 1) & TVN_MASK;
}
static inline void
run_timer_list (void)
{
cli ();
while ((long) (jiffies - timer_jiffies) >= 0)
{
struct timer_list *timer;
if (!tv1.index)
{
int n = 1;
do
{
cascade_timers (tvecs[n]);
}
while (tvecs[n]->index == 1 && ++n < NOOF_TVECS);
}
while ((timer = tv1.vec[tv1.index]))
{
void (*fn) (unsigned long) = timer->function;
unsigned long data = timer->data;
detach_timer (timer);
timer->next = timer->prev = NULL;
sti ();
fn (data);
cli ();
}
++timer_jiffies;
tv1.index = (tv1.index + 1) & TVR_MASK;
}
sti ();
}
/*
* Timer software interrupt handler.
*/
static void
timer_bh (void)
{
run_old_timers ();
run_timer_list ();
}
#if 0
int linux_timer_print = 0;
#endif
/*
* Timer interrupt handler.
*/
void
linux_timer_intr (void)
{
(*(unsigned long *) &jiffies)++;
mark_bh (TIMER_BH);
if (tq_timer)
mark_bh (TQUEUE_BH);
#if 0
if (linux_timer_print)
printf ("linux_timer_intr: pic_mask[0] %x\n", pic_mask[0]);
#endif
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.