/* * 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 #include #include #include #include #include #include #define MACH_INCLUDE #include #include #include #include #include #include #include #include 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 }