From 59ec5ca501a2f5d9fc2b2c41d474ec30e4fd67ee Mon Sep 17 00:00:00 2001 From: Thomas Bushnell Date: Mon, 26 Apr 1999 05:49:38 +0000 Subject: 1998-12-02 OKUJI Yoshinori * configure.in: Fix linuxdev option handling. * linux/Drivers.in: Remove linuxdev option and fix linking files. * linux/Makefile.in: Replace @DEFS@ with -DLINUX_DEV. * linux/dev/arch/i386/kernel/irq.c: Include missing header files. * linux/dev/arch/i386/kernel/setup.c: Include . * linux/dev/glue/kmem.c: Add printf declaration. * linux/dev/glue/misc.c: Include . * linux/dev/init/main.c: Call linux_sched_init instead of sched_init. * linux/dev/kernel/sched.c: Add timer_bh declaration. (tqueue_bh): Fix the argument. (linux_sched_init): Renamed from sched_init. 1998-11-30 OKUJI Yoshinori Clean up linux emulation code to make it architecture-independent as much as possible. * linux: Renamed from linuxdev. * Makefile.in (objfiles): Add linux.o instead of linuxdev.o. (MAKE): New variable. Used for the linux.o target. * configure.in: Add AC_CHECK_TOOL(MAKE, make). * i386/i386/spl.h: Include , for compatibility with OSF Mach 3.0. Suggested by Elgin Lee . * linux/src: Renamed from linux/linux. * linux/dev: Renamed from linux/mach. * linux/Drivers.in (AC_INIT): Use dev/include/linux/autoconf.h, instead of mach/include/linux/autoconf.h. * Makefile.in (all): Target ../linux.o instead of ../linuxdev.o. * linux/dev/drivers/block/genhd.c: Include instead of . * linux/dev/drivers/net/auto_irq.c: Remove unneeded header files, and . * linux/dev/init/main.c: Many i386-dependent codes moved to ... * linux/dev/arch/i386/irq.c: ... here. * linux/dev/arch/i386/setup.c: New file. * linux/dev/arch/i386/linux_emul.h: Likewise. * linux/dev/arch/i386/glue/timer.c: Merged into sched.c. * linux/dev/arch/i386/glue/sched.c: Include instead of , and moved to ... * linux/dev/kernel/sched.c: ... here. * linux/dev/arch/i386/glue/block.c: Include and , instead of i386-dependent header files, and moved to ... * linux/dev/glue/blocl.c: ... here. * linux/dev/arch/i386/glue/net.c: Include and , instead of i386-dependent header files, and moved to ... * linux/dev/glue/net.c: ... here. * linux/dev/arch/i386/glue/misc.c: Remove `x86' and moved to ... * linux/dev/glue/misc.c: ... here. * linux/dev/arch/i386/glue/kmem.c: Moved to ... * linux/dev/glue/kmem.c: ... here. --- linux/dev/init/main.c | 391 +++++++++++++++++++++++++++ linux/dev/init/version.c | 32 +++ linux/dev/kernel/dma.c | 109 ++++++++ linux/dev/kernel/printk.c | 80 ++++++ linux/dev/kernel/resource.c | 145 ++++++++++ linux/dev/kernel/sched.c | 642 ++++++++++++++++++++++++++++++++++++++++++++ linux/dev/kernel/softirq.c | 46 ++++ 7 files changed, 1445 insertions(+) create mode 100644 linux/dev/init/main.c create mode 100644 linux/dev/init/version.c create mode 100644 linux/dev/kernel/dma.c create mode 100644 linux/dev/kernel/printk.c create mode 100644 linux/dev/kernel/resource.c create mode 100644 linux/dev/kernel/sched.c create mode 100644 linux/dev/kernel/softirq.c (limited to 'linux/dev') diff --git a/linux/dev/init/main.c b/linux/dev/init/main.c new file mode 100644 index 0000000..0c14353 --- /dev/null +++ b/linux/dev/init/main.c @@ -0,0 +1,391 @@ +/* + * Linux initialization. + * + * 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/init/main.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define MACH_INCLUDE +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* + * Timing loop count. + */ +unsigned long loops_per_sec = 1; + +/* + * End of physical memory. + */ +unsigned long high_memory; + +/* + * Flag to indicate auto-configuration is in progress. + */ +int linux_auto_config = 1; + +/* + * Hard drive parameters obtained from the BIOS. + */ +struct drive_info_struct +{ + char dummy[32]; +} drive_info; + +/* + * Forward declarations. + */ +static void calibrate_delay (void); + +extern int hz; +extern vm_offset_t phys_last_addr; + +extern void *alloc_contig_mem (unsigned, unsigned, unsigned, vm_page_t *); +extern void free_contig_mem (vm_page_t); +extern void init_IRQ (void); +extern void restore_IRQ (void); +extern void startrtclock (void); +extern void linux_version_init (void); +extern void linux_kmem_init (void); +extern unsigned long pci_init (unsigned long, unsigned long); +extern void linux_net_emulation_init (void); +extern void device_setup (void); +extern void linux_printk (char *,...); +extern int linux_timer_intr (void); +extern spl_t spl0 (void); +extern spl_t splhigh (void); +extern void form_pic_mask (void); +extern int linux_bad_intr (int); +extern int prtnull (); +extern int intnull (); +extern void linux_sched_init (void); + + +/* + * Amount of contiguous memory to allocate for initialization. + */ +#define CONTIG_ALLOC (512 * 1024) + +/* + * Initialize Linux drivers. + */ +void +linux_init (void) +{ + int addr; + unsigned memory_start, memory_end; + vm_page_t pages; + + /* + * Initialize memory size. + */ + high_memory = phys_last_addr; + init_IRQ (); + linux_sched_init (); + + /* + * Set loop count. + */ + calibrate_delay (); + + /* + * Initialize drive info. + */ + addr = *((unsigned *) phystokv (0x104)); + memcpy (&drive_info, + (void *) ((addr & 0xffff) + ((addr >> 12) & 0xffff0)), 16); + addr = *((unsigned *) phystokv (0x118)); + memcpy ((char *) &drive_info + 16, + (void *) ((addr & 0xffff) + ((addr >> 12) & 0xffff0)), 16); + + /* + * Initialize Linux memory allocator. + */ + linux_kmem_init (); + + /* + * Allocate contiguous memory below 16 MB. + */ + memory_start = (unsigned long) alloc_contig_mem (CONTIG_ALLOC, + 16 * 1024 * 1024, + 0, &pages); + if (memory_start == 0) + panic ("linux_init: alloc_contig_mem failed"); + memory_end = memory_start + CONTIG_ALLOC; + + /* + * Initialize PCI bus. + */ + memory_start = pci_init (memory_start, memory_end); + + if (memory_start > memory_end) + panic ("linux_init: ran out memory"); + + /* + * Free unused memory. + */ + while (pages && pages->phys_addr < round_page (memory_start)) + pages = (vm_page_t) pages->pageq.next; + if (pages) + free_contig_mem (pages); + + /* + * Initialize devices. + */ +#ifdef CONFIG_INET + linux_net_emulation_init (); +#endif + cli (); + device_setup (); + + restore_IRQ (); + linux_auto_config = 0; +} + +#ifndef NBPW +#define NBPW 32 +#endif + +/* + * Allocate contiguous memory with the given constraints. + * This routine is horribly inefficient but it is presently + * only used during initialization so it's not that bad. + */ +void * +alloc_contig_mem (unsigned size, unsigned limit, + unsigned mask, vm_page_t * pages) +{ + int i, j, bits_len; + unsigned *bits, len; + void *m; + vm_page_t p, page_list, tail, prev; + vm_offset_t addr, max_addr; + + if (size == 0) + return (NULL); + size = round_page (size); + if ((size >> PAGE_SHIFT) > vm_page_free_count) + return (NULL); + + /* Allocate bit array. */ + max_addr = phys_last_addr; + if (max_addr > limit) + max_addr = limit; + bits_len = ((((max_addr >> PAGE_SHIFT) + NBPW - 1) / NBPW) + * sizeof (unsigned)); + bits = (unsigned *) kalloc (bits_len); + if (!bits) + return (NULL); + memset (bits, 0, bits_len); + + /* + * Walk the page free list and set a bit for every usable page. + */ + simple_lock (&vm_page_queue_free_lock); + p = vm_page_queue_free; + while (p) + { + if (p->phys_addr < limit) + (bits[(p->phys_addr >> PAGE_SHIFT) / NBPW] + |= 1 << ((p->phys_addr >> PAGE_SHIFT) % NBPW)); + p = (vm_page_t) p->pageq.next; + } + + /* + * Scan bit array for contiguous pages. + */ + len = 0; + m = NULL; + for (i = 0; len < size && i < bits_len / sizeof (unsigned); i++) + for (j = 0; len < size && j < NBPW; j++) + if (!(bits[i] & (1 << j))) + { + len = 0; + m = NULL; + } + else + { + if (len == 0) + { + addr = ((vm_offset_t) (i * NBPW + j) + << PAGE_SHIFT); + if ((addr & mask) == 0) + { + len += PAGE_SIZE; + m = (void *) addr; + } + } + else + len += PAGE_SIZE; + } + + if (len != size) + { + simple_unlock (&vm_page_queue_free_lock); + kfree ((vm_offset_t) bits, bits_len); + return (NULL); + } + + /* + * Remove pages from free list + * and construct list to return to caller. + */ + page_list = NULL; + for (len = 0; len < size; len += PAGE_SIZE, addr += PAGE_SIZE) + { + prev = NULL; + for (p = vm_page_queue_free; p; p = (vm_page_t) p->pageq.next) + { + if (p->phys_addr == addr) + break; + prev = p; + } + if (!p) + panic ("alloc_contig_mem: page not on free list"); + if (prev) + prev->pageq.next = p->pageq.next; + else + vm_page_queue_free = (vm_page_t) p->pageq.next; + p->free = FALSE; + p->pageq.next = NULL; + if (!page_list) + page_list = tail = p; + else + { + tail->pageq.next = (queue_entry_t) p; + tail = p; + } + vm_page_free_count--; + } + + simple_unlock (&vm_page_queue_free_lock); + kfree ((vm_offset_t) bits, bits_len); + if (pages) + *pages = page_list; + return (m); +} + +/* + * Free memory allocated by alloc_contig_mem. + */ +void +free_contig_mem (vm_page_t pages) +{ + int i; + vm_page_t p; + + for (p = pages, i = 0; p->pageq.next; p = (vm_page_t) p->pageq.next, i++) + p->free = TRUE; + p->free = TRUE; + simple_lock (&vm_page_queue_free_lock); + vm_page_free_count += i + 1; + p->pageq.next = (queue_entry_t) vm_page_queue_free; + vm_page_queue_free = pages; + simple_unlock (&vm_page_queue_free_lock); +} + +/* This is the number of bits of precision for the loops_per_second. Each + * bit takes on average 1.5/HZ seconds. This (like the original) is a little + * better than 1% + */ +#define LPS_PREC 8 + +static void +calibrate_delay (void) +{ + int ticks; + int loopbit; + int lps_precision = LPS_PREC; + + loops_per_sec = (1 << 12); + +#ifndef MACH + printk ("Calibrating delay loop.. "); +#endif + while (loops_per_sec <<= 1) + { + /* wait for "start of" clock tick */ + ticks = jiffies; + while (ticks == jiffies) + /* nothing */ ; + /* Go .. */ + ticks = jiffies; + __delay (loops_per_sec); + ticks = jiffies - ticks; + if (ticks) + break; + } + + /* Do a binary approximation to get loops_per_second set to equal one clock + * (up to lps_precision bits) + */ + loops_per_sec >>= 1; + loopbit = loops_per_sec; + while (lps_precision-- && (loopbit >>= 1)) + { + loops_per_sec |= loopbit; + ticks = jiffies; + while (ticks == jiffies); + ticks = jiffies; + __delay (loops_per_sec); + if (jiffies != ticks) /* longer than 1 tick */ + loops_per_sec &= ~loopbit; + } + + /* finally, adjust loops per second in terms of seconds instead of clocks */ + loops_per_sec *= HZ; + /* Round the value and print it */ +#ifndef MACH + printk ("ok - %lu.%02lu BogoMIPS\n", + (loops_per_sec + 2500) / 500000, + ((loops_per_sec + 2500) / 5000) % 100); +#endif + +#if defined(__SMP__) && defined(__i386__) + smp_loops_per_tick = loops_per_sec / 400; +#endif +} diff --git a/linux/dev/init/version.c b/linux/dev/init/version.c new file mode 100644 index 0000000..1989483 --- /dev/null +++ b/linux/dev/init/version.c @@ -0,0 +1,32 @@ +/* + * linux/version.c + * + * Copyright (C) 1992 Theodore Ts'o + * + * May be freely distributed as part of Linux. + */ + +#define MACH_INCLUDE +#include +#include +#include +#include + +/* make the "checkconfig" script happy: we really need to include config.h */ +#ifdef CONFIG_BOGUS +#endif + +#define version(a) Version_ ## a +#define version_string(a) version(a) + +int version_string (LINUX_VERSION_CODE) = 0; + +struct new_utsname system_utsname = +{ + UTS_SYSNAME, UTS_NODENAME, UTS_RELEASE, UTS_VERSION, + UTS_MACHINE, UTS_DOMAINNAME +}; + +const char *linux_banner = +"Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@" +LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n"; diff --git a/linux/dev/kernel/dma.c b/linux/dev/kernel/dma.c new file mode 100644 index 0000000..4b56978 --- /dev/null +++ b/linux/dev/kernel/dma.c @@ -0,0 +1,109 @@ +/* $Id: dma.c,v 1.1 1999/04/26 05:49:35 tb Exp $ + * linux/kernel/dma.c: A DMA channel allocator. Inspired by linux/kernel/irq.c. + * + * Written by Hennus Bergman, 1992. + * + * 1994/12/26: Changes by Alex Nash to fix a minor bug in /proc/dma. + * In the previous version the reported device could end up being wrong, + * if a device requested a DMA channel that was already in use. + * [It also happened to remove the sizeof(char *) == sizeof(int) + * assumption introduced because of those /proc/dma patches. -- Hennus] + */ + +#define MACH_INCLUDE +#include +#include +#include +#include + + +/* A note on resource allocation: + * + * All drivers needing DMA channels, should allocate and release them + * through the public routines `request_dma()' and `free_dma()'. + * + * In order to avoid problems, all processes should allocate resources in + * the same sequence and release them in the reverse order. + * + * So, when allocating DMAs and IRQs, first allocate the IRQ, then the DMA. + * When releasing them, first release the DMA, then release the IRQ. + * If you don't, you may cause allocation requests to fail unnecessarily. + * This doesn't really matter now, but it will once we get real semaphores + * in the kernel. + */ + + + +/* Channel n is busy iff dma_chan_busy[n].lock != 0. + * DMA0 used to be reserved for DRAM refresh, but apparently not any more... + * DMA4 is reserved for cascading. + */ + +struct dma_chan +{ + int lock; + const char *device_id; +}; + +static struct dma_chan dma_chan_busy[MAX_DMA_CHANNELS] = +{ + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 1, "cascade" }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } +}; + +#ifndef MACH +int +get_dma_list (char *buf) +{ + int i, len = 0; + + for (i = 0 ; i < MAX_DMA_CHANNELS ; i++) + { + if (dma_chan_busy[i].lock) + { + len += linux_sprintf (buf+len, "%2d: %s\n", + i, + dma_chan_busy[i].device_id); + } + } + return len; +} /* get_dma_list */ +#endif + +int +request_dma (unsigned int dmanr, const char *device_id) +{ + if (dmanr >= MAX_DMA_CHANNELS) + return -LINUX_EINVAL; + + if (xchg (&dma_chan_busy[dmanr].lock, 1) != 0) + return -LINUX_EBUSY; + + dma_chan_busy[dmanr].device_id = device_id; + + /* old flag was 0, now contains 1 to indicate busy */ + return 0; +} /* request_dma */ + + +void +free_dma (unsigned int dmanr) +{ + if (dmanr >= MAX_DMA_CHANNELS) + { + printk ("Trying to free DMA%d\n", dmanr); + return; + } + + if (xchg (&dma_chan_busy[dmanr].lock, 0) == 0) + { + printk ("Trying to free free DMA%d\n", dmanr); + return; + } +} /* free_dma */ diff --git a/linux/dev/kernel/printk.c b/linux/dev/kernel/printk.c new file mode 100644 index 0000000..2ff52d1 --- /dev/null +++ b/linux/dev/kernel/printk.c @@ -0,0 +1,80 @@ +/* + * Linux kernel print routine. + * Copyright (C) 1995 Shantanu Goel. + * + * 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. + */ + +/* + * linux/kernel/printk.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#define MACH_INCLUDE +#include +#include + +static char buf[2048]; + +#define DEFAULT_MESSAGE_LOGLEVEL 4 +#define DEFAULT_CONSOLE_LOGLEVEL 7 + +int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL; + +int +printk (char *fmt, ...) +{ + va_list args; + int n, flags; + extern void cnputc (); + extern int linux_vsprintf (char *buf, char *fmt,...); + char *p, *msg, *buf_end; + static int msg_level = -1; + + save_flags (flags); + cli (); + va_start (args, fmt); + n = linux_vsprintf (buf + 3, fmt, args); + buf_end = buf + 3 + n; + va_end (args); + for (p = buf + 3; p < buf_end; p++) + { + msg = p; + if (msg_level < 0) + { + if (p[0] != '<' || p[1] < '0' || p[1] > '7' || p[2] != '>') + { + p -= 3; + p[0] = '<'; + p[1] = DEFAULT_MESSAGE_LOGLEVEL + '0'; + p[2] = '>'; + } + else + msg += 3; + msg_level = p[1] - '0'; + } + for (; p < buf_end; p++) + if (*p == '\n') + break; + if (msg_level < console_loglevel) + while (msg <= p) + cnputc (*msg++); + if (*p == '\n') + msg_level = -1; + } + restore_flags (flags); + return n; +} diff --git a/linux/dev/kernel/resource.c b/linux/dev/kernel/resource.c new file mode 100644 index 0000000..7a18755 --- /dev/null +++ b/linux/dev/kernel/resource.c @@ -0,0 +1,145 @@ +/* + * linux/kernel/resource.c + * + * Copyright (C) 1995 Linus Torvalds + * David Hinds + * + * Kernel io-region resource management + */ + +#include + +#define MACH_INCLUDE +#include +#include +#include +#include +#include + +#define IOTABLE_SIZE 128 + +typedef struct resource_entry_t +{ + u_long from, num; + const char *name; + struct resource_entry_t *next; +} resource_entry_t; + +static resource_entry_t iolist = { 0, 0, "", NULL }; + +static resource_entry_t iotable[IOTABLE_SIZE]; + +/* + * This generates the report for /proc/ioports + */ +#ifndef MACH +int +get_ioport_list (char *buf) +{ + resource_entry_t *p; + int len = 0; + + for (p = iolist.next; (p) && (len < 4000); p = p->next) + len += linux_sprintf (buf+len, "%04lx-%04lx : %s\n", + p->from, p->from+p->num-1, p->name); + if (p) + len += linux_sprintf (buf+len, "4K limit reached!\n"); + return len; +} +#endif + +/* + * The workhorse function: find where to put a new entry + */ +static resource_entry_t * +find_gap (resource_entry_t *root, u_long from, u_long num) +{ + unsigned long flags; + resource_entry_t *p; + + if (from > from+num-1) + return NULL; + save_flags (flags); + cli (); + for (p = root; ; p = p->next) + { + if ((p != root) && (p->from+p->num-1 >= from)) + { + p = NULL; + break; + } + if ((p->next == NULL) || (p->next->from > from+num-1)) + break; + } + restore_flags (flags); + return p; +} + +/* + * Call this from the device driver to register the ioport region. + */ +void +request_region (unsigned int from, unsigned int num, const char *name) +{ + resource_entry_t *p; + int i; + + for (i = 0; i < IOTABLE_SIZE; i++) + if (iotable[i].num == 0) + break; + if (i == IOTABLE_SIZE) + printk ("warning: ioport table is full\n"); + else + { + p = find_gap (&iolist, from, num); + if (p == NULL) + return; + iotable[i].name = name; + iotable[i].from = from; + iotable[i].num = num; + iotable[i].next = p->next; + p->next = &iotable[i]; + return; + } +} + +/* + * Call this when the device driver is unloaded + */ +void +release_region (unsigned int from, unsigned int num) +{ + resource_entry_t *p, *q; + + for (p = &iolist; ; p = q) + { + q = p->next; + if (q == NULL) + break; + if ((q->from == from) && (q->num == num)) + { + q->num = 0; + p->next = q->next; + return; + } + } +} + +/* + * Call this to check the ioport region before probing + */ +int +check_region (unsigned int from, unsigned int num) +{ + return (find_gap (&iolist, from, num) == NULL) ? -LINUX_EBUSY : 0; +} + +/* Called from init/main.c to reserve IO ports. */ +void +reserve_setup(char *str, int *ints) +{ + int i; + + for (i = 1; i < ints[0]; i += 2) + request_region (ints[i], ints[i+1], "reserved"); +} diff --git a/linux/dev/kernel/sched.c b/linux/dev/kernel/sched.c new file mode 100644 index 0000000..792c2da --- /dev/null +++ b/linux/dev/kernel/sched.c @@ -0,0 +1,642 @@ +/* + * 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 + +#define MACH_INCLUDE +#include +#include +#include +#include +#include + +#include +#include + +int securelevel = 0; + +extern void *alloc_contig_mem (unsigned, unsigned, unsigned, vm_page_t *); +extern void free_contig_mem (vm_page_t); +extern spl_t splhigh (void); +extern spl_t splx (spl_t); +extern void linux_soft_intr (void); +extern int issig (void); +extern int printf (const char *, ...); +extern int linux_auto_config; + +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 = -LINUX_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 = -LINUX_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 +cdrom_sleep (int t) +{ + int xxx; + + assert_wait ((event_t) &xxx, TRUE); + thread_set_timeout (t); + schedule (); +} + +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 +} diff --git a/linux/dev/kernel/softirq.c b/linux/dev/kernel/softirq.c new file mode 100644 index 0000000..96102a7 --- /dev/null +++ b/linux/dev/kernel/softirq.c @@ -0,0 +1,46 @@ +/* + * linux/kernel/softirq.c + * + * Copyright (C) 1992 Linus Torvalds + * + * do_bottom_half() runs at normal kernel priority: all interrupts + * enabled. do_bottom_half() is atomic with respect to itself: a + * bottom_half handler need not be re-entrant. + */ + +#define MACH_INCLUDE +#include +#include +#include + +int bh_mask_count[32]; +unsigned long bh_active = 0; +unsigned long bh_mask = 0; +void (*bh_base[32]) (void); + +void +linux_soft_intr (void) +{ + unsigned long active; + unsigned long mask, left; + void (**bh) (void); + + sti (); + bh = bh_base; + active = bh_active & bh_mask; + for (mask = 1, left = ~0; left & active; bh++, mask += mask, left += left) + { + if (mask & active) + { + void (*fn) (void); + bh_active &= ~mask; + fn = *bh; + if (!fn) + goto bad_bh; + fn (); + } + } + return; +bad_bh: + printk ("linux_soft_intr:bad interrupt handler entry %08lx\n", mask); +} -- cgit v1.2.3