diff -u -p -L linux-2.2.14-5.0/arch/i386/config.in.orig linux-2.2.14-5.0/arch/i386/config.in --- linux-2.2.14-5.0/arch/i386/config.in +++ linux-2.2.14-5.0/arch/i386/config.in Sun Sep 17 11:10:14 2000 @@ -40,6 +40,7 @@ bool 'Enable CPU Specific (MMX/MMX2) Opt bool 'Math emulation' CONFIG_MATH_EMULATION bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR bool 'Symmetric multi-processing support' CONFIG_SMP +bool 'Bigphysarea support' CONFIG_BIGPHYSAREA choice 'Maximum Physical Memory' \ "1GB CONFIG_1GB \ diff -u -p -L linux-2.2.14-5.0/arch/i386/kernel/ptrace.c.orig linux-2.2.14-5.0/arch/i386/kernel/ptrace.c --- linux-2.2.14-5.0/arch/i386/kernel/ptrace.c +++ linux-2.2.14-5.0/arch/i386/kernel/ptrace.c Sun Sep 17 11:17:58 2000 @@ -20,6 +20,10 @@ #include #include +#ifdef CONFIG_BIGPHYSAREA +#include +#endif + /* * does not yet catch signals sent when the child dies. * in exit.c or in signal.c. @@ -124,14 +128,25 @@ repeat: return 0; } page = pte_page(*pgtable); -/* this is a hack for non-kernel-mapped video buffers and similar */ - if (MAP_NR(page) >= max_mapnr) - return 0; - page += addr & ~PAGE_MASK; - page = kmap(page, KM_READ); - retval = *(unsigned long *) page; - kunmap(page, KM_READ); - return retval; + if (MAP_NR(page) >= max_mapnr) { + /* for high addresses, map the page into kernel space */ + unsigned long result, vpage; + + vpage = (unsigned long)ioremap (__pa(page), PAGE_SIZE); + if (!vpage) { + printk ("ptrace: out of memory\n"); + return 0; + } + result = *(unsigned long *)(vpage + (addr & ~PAGE_MASK)); + iounmap ((void *)vpage); + return result; + } else { + page += addr & ~PAGE_MASK; + page = kmap(page, KM_READ); + retval = *(unsigned long *) page; + kunmap(page, KM_READ); + return retval; + } } /* @@ -200,13 +215,23 @@ repeat: return; } /* this is a hack for non-kernel-mapped video buffers and similar */ - if (MAP_NR(page) < max_mapnr) - { + if (MAP_NR(page) < max_mapnr) { unsigned long vaddr; vaddr = kmap(page, KM_WRITE); *(unsigned long *) (vaddr + (addr & ~PAGE_MASK)) = data; kunmap(vaddr, KM_WRITE); + } else { + /* for high addresses, map the page into kernel space */ + unsigned long vpage; + + vpage = (unsigned long)ioremap (__pa(page), PAGE_SIZE); + if (!vpage) { + printk ("ptrace: out of memory\n"); + return; + } + *(unsigned long *)(vpage + (addr & ~PAGE_MASK)) = data; + iounmap ((void *)(vpage)); } /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ /* this should also re-instate whatever read-only mode there was before */ diff -u -p -L linux-2.2.14-5.0/fs/proc/array.c.orig linux-2.2.14-5.0/fs/proc/array.c --- linux-2.2.14-5.0/fs/proc/array.c +++ linux-2.2.14-5.0/fs/proc/array.c Sun Sep 17 11:07:29 2000 @@ -1324,6 +1324,7 @@ extern int get_cpuinfo(char *); extern int get_pci_list(char *); extern int get_md_status (char *); extern int get_rtc_status (char *); +extern int get_bigphysarea_info(char *); extern int get_locks_status (char *, char **, off_t, int); extern int get_swaparea_info (char *); extern int get_hardware_list(char *); @@ -1413,6 +1414,10 @@ static long get_root_array(char * page, #ifdef CONFIG_SGI_DS1286 case PROC_RTC: return get_ds1286_status(page); +#endif +#ifdef CONFIG_BIGPHYSAREA + case PROC_BIGPHYSAREA: + return get_bigphysarea_info(page); #endif case PROC_LOCKS: return get_locks_status(page, start, offset, length); diff -u -p -L linux-2.2.14-5.0/fs/proc/root.c.orig linux-2.2.14-5.0/fs/proc/root.c --- linux-2.2.14-5.0/fs/proc/root.c +++ linux-2.2.14-5.0/fs/proc/root.c Sun Sep 17 11:07:29 2000 @@ -640,6 +640,13 @@ static struct proc_dir_entry proc_root_d 0, &proc_array_inode_operations }; #endif +#ifdef CONFIG_BIGPHYSAREA +static struct proc_dir_entry proc_root_bigphysarea = { + PROC_BIGPHYSAREA, 11, "bigphysarea", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_array_inode_operations +}; +#endif static struct proc_dir_entry proc_root_locks = { PROC_LOCKS, 5, "locks", S_IFREG | S_IRUGO, 1, 0, 0, @@ -719,6 +726,9 @@ __initfunc(void proc_root_init(void)) #endif #ifdef CONFIG_SGI_DS1286 proc_register(&proc_root, &proc_root_ds1286); +#endif +#ifdef CONFIG_BIGPHYSAREA + proc_register(&proc_root, &proc_root_bigphysarea); #endif proc_register(&proc_root, &proc_root_locks); diff -u -p -L linux-2.2.14-5.0/include/linux/bigphysarea.h.orig linux-2.2.14-5.0/include/linux/bigphysarea.h --- linux-2.2.14-5.0/include/linux/bigphysarea.h +++ linux-2.2.14-5.0/include/linux/bigphysarea.h Sun Sep 17 11:07:29 2000 @@ -0,0 +1,64 @@ +/* linux/mm/bigphysarea.h, M. Welsh (mdw@cs.cornell.edu) + * Copyright (c) 1996 by Matt Welsh. + * Extended by Roger Butenuth (butenuth@uni-paderborn.de), October 1997 + * + * This is a set of routines which allow you to reserve a large (?) + * amount of physical memory at boot-time, which can be allocated/deallocated + * by drivers. This memory is intended to be used for devices such as + * video framegrabbers which need a lot of physical RAM (above the amount + * allocated by kmalloc). This is by no means efficient or recommended; + * to be used only in extreme circumstances. + * + */ + +#ifndef __LINUX_BIGPHYSAREA_H +#define __LINUX_BIGPHYSAREA_H + +#include + +extern caddr_t bigphysarea; + +/* original interface */ +extern void bigphysarea_setup(char *str, int *ints); +extern unsigned long bigphysarea_init(unsigned long mem_start, unsigned long m\ +em_end); +extern caddr_t bigphysarea_alloc(int size); +extern void bigphysarea_free(caddr_t addr, int size); + +/* new interface */ +extern caddr_t bigphysarea_alloc_pages(int count, int align, int priority); +extern void bigphysarea_free_pages(caddr_t base); + +#endif __LINUX_BIGPHYSAREA_H +/* linux/mm/bigphysarea.h, M. Welsh (mdw@cs.cornell.edu) + * Copyright (c) 1996 by Matt Welsh. + * Extended by Roger Butenuth (butenuth@uni-paderborn.de), October 1997 + * + * This is a set of routines which allow you to reserve a large (?) + * amount of physical memory at boot-time, which can be allocated/deallocated + * by drivers. This memory is intended to be used for devices such as + * video framegrabbers which need a lot of physical RAM (above the amount + * allocated by kmalloc). This is by no means efficient or recommended; + * to be used only in extreme circumstances. + * + */ + +#ifndef __LINUX_BIGPHYSAREA_H +#define __LINUX_BIGPHYSAREA_H + +#include + +extern caddr_t bigphysarea; + +/* original interface */ +extern void bigphysarea_setup(char *str, int *ints); +extern unsigned long bigphysarea_init(unsigned long mem_start, unsigned long m\ +em_end); +extern caddr_t bigphysarea_alloc(int size); +extern void bigphysarea_free(caddr_t addr, int size); + +/* new interface */ +extern caddr_t bigphysarea_alloc_pages(int count, int align, int priority); +extern void bigphysarea_free_pages(caddr_t base); + +#endif __LINUX_BIGPHYSAREA_H diff -u -p -L linux-2.2.14-5.0/include/linux/proc_fs.h.orig linux-2.2.14-5.0/include/linux/proc_fs.h --- linux-2.2.14-5.0/include/linux/proc_fs.h +++ linux-2.2.14-5.0/include/linux/proc_fs.h Sun Sep 17 11:07:29 2000 @@ -44,6 +44,7 @@ enum root_directory_inos { PROC_SWAP, PROC_MD, PROC_RTC, + PROC_BIGPHYSAREA, PROC_LOCKS, PROC_HARDWARE, PROC_SLABINFO, diff -u -p -L linux-2.2.14-5.0/init/main.c.orig linux-2.2.14-5.0/init/main.c --- linux-2.2.14-5.0/init/main.c +++ linux-2.2.14-5.0/init/main.c Sun Sep 17 11:07:29 2000 @@ -369,6 +369,9 @@ extern void cpqarray_setup(char *str, in #if defined(CONFIG_SYSVIPC) extern void ipc_init(void); #endif +#if defined(CONFIG_BIGPHYSAREA) +extern void bigphysarea_setup(char *str, int *ints); +#endif #if defined(CONFIG_QUOTA) extern void dquot_init_hash(void); #endif @@ -983,6 +986,9 @@ static struct kernel_param cooked_params #ifdef CONFIG_AGP { "agp_try_unsupported=", agp_setup}, #endif +#ifdef CONFIG_BIGPHYSAREA + { "bigphysarea=", bigphysarea_setup }, +#endif { 0, 0 } }; @@ -1294,6 +1300,9 @@ asmlinkage void __init start_kernel(void "disabling it.\n",initrd_start,memory_start); initrd_start = 0; } +#endif +#ifdef CONFIG_BIGPHYSAREA + memory_start = bigphysarea_init(memory_start,memory_end); #endif mem_init(memory_start,memory_end); kmem_cache_sizes_init(); diff -u -p -L linux-2.2.14-5.0/kernel/ksyms.c.orig linux-2.2.14-5.0/kernel/ksyms.c --- linux-2.2.14-5.0/kernel/ksyms.c +++ linux-2.2.14-5.0/kernel/ksyms.c Sun Sep 17 11:07:29 2000 @@ -39,6 +39,9 @@ #include #include #include +#if defined(CONFIG_BIGPHYSAREA) +#include +#endif #if defined(CONFIG_PROC_FS) #include @@ -113,6 +116,18 @@ EXPORT_SYMBOL(update_vm_cache_conditiona EXPORT_SYMBOL(vmtruncate); EXPORT_SYMBOL(find_vma); EXPORT_SYMBOL(get_unmapped_area); +#ifdef CONFIG_BIGPHYSAREA +/* following symbols are used by the SCI driver */ +EXPORT_SYMBOL(page_cache_size); +EXPORT_SYMBOL(page_hash_table); +EXPORT_SYMBOL(zap_page_range); +EXPORT_SYMBOL(insert_vm_struct); +/* Large physical area memory management */ +EXPORT_SYMBOL(bigphysarea_alloc); +EXPORT_SYMBOL(bigphysarea_free); +EXPORT_SYMBOL(bigphysarea_alloc_pages); +EXPORT_SYMBOL(bigphysarea_free_pages); +#endif /* filesystem internal functions */ EXPORT_SYMBOL(in_group_p); diff -u -p -L linux-2.2.14-5.0/mm/Makefile.orig linux-2.2.14-5.0/mm/Makefile --- linux-2.2.14-5.0/mm/Makefile +++ linux-2.2.14-5.0/mm/Makefile Sun Sep 17 11:07:29 2000 @@ -16,4 +16,8 @@ ifeq ($(CONFIG_BIGMEM),y) O_OBJS += bigmem.o endif +ifeq ($(CONFIG_BIGPHYSAREA),y) +O_OBJS += bigphysarea.o +endif + include $(TOPDIR)/Rules.make diff -u -p -L linux-2.2.14-5.0/mm/bigphysarea.c.orig linux-2.2.14-5.0/mm/bigphysarea.c --- linux-2.2.14-5.0/mm/bigphysarea.c +++ linux-2.2.14-5.0/mm/bigphysarea.c Sun Sep 17 11:07:29 2000 @@ -0,0 +1,337 @@ +/* linux/mm/bigphysarea.c, M. Welsh (mdw@cs.cornell.edu) + * Copyright (c) 1996 by Matt Welsh. + * Extended by Roger Butenuth (butenuth@uni-paderborn.de), October 1997 + * + * This is a set of routines which allow you to reserve a large (?) + * amount of physical memory at boot-time, which can be allocated/deallocated + * by drivers. This memory is intended to be used for devices such as + * video framegrabbers which need a lot of physical RAM (above the amount + * allocated by kmalloc). This is by no means efficient or recommended; + * to be used only in extreme circumstances. + * + * 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 of the License, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct range_struct { + struct range_struct *next; + caddr_t base; /* base of allocated block */ + size_t size; /* size in bytes */ +} range_t; + +static int bigphysarea_pages = 0; +/* + * 0: nothing initialized + * 1: bigphysarea_pages initialized + * 2: free list initialized + */ +static int init_level = 0; + +/* + * The free list and the used list are protected by one lock. + */ +static spinlock_t big_lock = SPIN_LOCK_UNLOCKED; +static range_t *free_list = NULL; +static range_t *used_list = NULL; + +caddr_t bigphysarea = 0; + +void bigphysarea_setup(char *str, int *ints) +{ + if (ints[0] == 1) + bigphysarea_pages = ints[1]; + else + bigphysarea_pages = 0; +} + +unsigned long bigphysarea_init(unsigned long mem_start, unsigned long mem_end) { + if (bigphysarea_pages == 0) + return mem_start; + + bigphysarea = (caddr_t)((mem_start + (PAGE_SIZE - 1)) & + ~(PAGE_SIZE - 1)); + + init_level = 1; + + printk("bigphysarea: Allocated %d pages at 0x%08lx.\n", + bigphysarea_pages, (unsigned long)bigphysarea); + + return (unsigned long)(bigphysarea + (bigphysarea_pages * PAGE_SIZE)); +} + +/* + * When we have pages but don't have a freelist, put all pages in + * one free list entry. Return 0 on success, 1 on error. + */ +static int init2(int priority) +{ + int res; + + spin_lock(&big_lock); /* >>>>>>>>>> */ + if (init_level == 1) { + free_list = kmalloc(sizeof(range_t), priority); + if (free_list != NULL) { + free_list->next = NULL; + free_list->base = bigphysarea; + free_list->size = bigphysarea_pages * PAGE_SIZE; + init_level = 2; + res = 0; + } else + res = 1; + } else + res = 1; + + spin_unlock(&big_lock); /* <<<<<<<<<< */ + return res; +} + + +/* + * Allocate `count' pages from the big physical area. Pages are aligned to + * a multiple of `align'. `priority' has the same meaning in kmalloc, it + * is needed for management information. + * This function may not be called from an interrupt, this is the reason + * we can use lock functions without disabling all interrupts. + */ +caddr_t bigphysarea_alloc_pages(int count, int align, int priority) +{ + range_t *range, **range_ptr, *new_range, *align_range; + caddr_t aligned_base; + caddr_t res = 0; + + if (init_level < 2) + if (init2(priority)) + return 0; + new_range = NULL; + align_range = NULL; + + if (align == 0) + align = PAGE_SIZE; + else + align = align * PAGE_SIZE; + + spin_lock(&big_lock); /* >>>>>>>>>> */ + /* + * Search a free block which is large enough, even with alignment. + */ + range_ptr = &free_list; + while (*range_ptr != NULL) { + range = *range_ptr; + aligned_base = + (caddr_t)((((long)range->base + align - 1) / align) * align); + if (aligned_base + count * PAGE_SIZE <= + range->base + range->size) + break; + range_ptr = &range->next; + } + if (*range_ptr == NULL) { + res = 0; + goto ret_label; + } + range = *range_ptr; + /* + * When we have to align, the pages needed for alignment can + * be put back to the free pool. + * We check here if we need a second range data structure later + * and allocate it now, so that we don't have to check for a + * failed kmalloc later. + */ + if (aligned_base - range->base + count * PAGE_SIZE < range->size) { + new_range = kmalloc(sizeof(range_t), priority); + if (new_range == NULL) { + res = 0; + goto ret_label; + } + } + if (aligned_base != range->base) { + align_range = kmalloc(sizeof(range_t), priority); + if (align_range == NULL) { + if (new_range != NULL) + kfree(new_range); + res = 0; + goto ret_label; + } + align_range->base = range->base; + align_range->size = aligned_base - range->base; + range->base = aligned_base; + range->size -= align_range->size; + align_range->next = range; + *range_ptr = align_range; + range_ptr = &align_range->next; + } + if (new_range != NULL) { + /* + * Range is larger than needed, create a new list element for + * the used list and shrink the element in the free list. + */ + new_range->base = range->base; + new_range->size = count * PAGE_SIZE; + range->base = new_range->base + new_range->size; + range->size = range->size - new_range->size; + } else { + /* + * Range fits perfectly, remove it from free list. + */ + *range_ptr = range->next; + new_range = range; + } + /* + * Insert block into used list + */ + new_range->next = used_list; + used_list = new_range; + + res = new_range->base; + ret_label: + spin_unlock(&big_lock); /* <<<<<<<<<< */ + return res; + +} + + +/* + * Free pages allocated with `bigphysarea_alloc_pages'. `base' must be an + * address returned by `bigphysarea_alloc_pages'. + * This function my not be called from an interrupt, for this reason we + * can use a lock without interrupt disabling. + */ +void bigphysarea_free_pages(caddr_t base) +{ + range_t *prev, *next, *range, **range_ptr; + + spin_lock(&big_lock); /* >>>>>>>>>> */ + /* + * Search the block in the used list. + */ + for (range_ptr = &used_list; + *range_ptr != NULL; + range_ptr = &(*range_ptr)->next) + if ((*range_ptr)->base == base) + break; + if (*range_ptr == NULL) { + printk("bigphysarea_free_pages(0x%08x), not allocated!\n", + (unsigned)base); + goto ret_label; + } + range = *range_ptr; + /* + * Remove range from the used list: + */ + *range_ptr = (*range_ptr)->next; + /* + * The free-list is sorted by address, search insertion point + * and insert block in free list. + */ + for (range_ptr = &free_list, prev = NULL; + *range_ptr != NULL; + prev = *range_ptr, range_ptr = &(*range_ptr)->next) + if ((*range_ptr)->base >= base) + break; + range->next = *range_ptr; + *range_ptr = range; + /* + * Concatenate free range with neighbors, if possible. + * Try for upper neighbor (next in list) first, then + * for lower neighbor (predecessor in list). + */ + if (range->next != NULL && + range->base + range->size == range->next->base) { + next = range->next; + range->size += range->next->size; + range->next = next->next; + kfree(next); + } + if (prev != NULL && + prev->base + prev->size == range->base) { + prev->size += prev->next->size; + prev->next = range->next; + kfree(range); + } + ret_label: + spin_unlock(&big_lock); /* <<<<<<<<<< */ +} + +caddr_t bigphysarea_alloc(int size) +{ + int pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; + + return bigphysarea_alloc_pages(pages, 1, GFP_KERNEL); +} + +void bigphysarea_free(caddr_t addr, int size) +{ + (void)size; + bigphysarea_free_pages(addr); +} + +int get_bigphysarea_info(char *buf) +{ + char *p = buf; + range_t *ptr; + int free_count, free_total, free_max; + int used_count, used_total, used_max; + + if (init_level == 1) + init2(GFP_KERNEL); + + spin_lock(&big_lock); /* >>>>>>>>>> */ + free_count = 0; + free_total = 0; + free_max = 0; + for (ptr = free_list; ptr != NULL; ptr = ptr->next) { + free_count++; + free_total += ptr->size; + if (ptr->size > free_max) + free_max = ptr->size; + } + + used_count = 0; + used_total = 0; + used_max = 0; + for (ptr = used_list; ptr != NULL; ptr = ptr->next) { + used_count++; + used_total += ptr->size; + if (ptr->size > used_max) + used_max = ptr->size; + } + spin_unlock(&big_lock); /* <<<<<<<<<< */ + + if (bigphysarea_pages == 0) { + p += sprintf(p, "No big physical area allocated!\n"); + return p - buf; + } + + p += sprintf(p, "Big physical area, size %ld kB\n", + bigphysarea_pages * PAGE_SIZE / 1024); + p += sprintf(p, " free list: used list:\n"); + p += sprintf(p, "number of blocks: %8d %8d\n", + free_count, used_count); + p += sprintf(p, "size of largest block: %8d kB %8d kB\n", + free_max / 1024, used_max / 1024); + p += sprintf(p, "total: %8d kB %8d kB\n", + free_total / 1024, used_total /1024); + + return p - buf; +}