|
Message
From: cvs at opencores.org<cvs@o...>
Date: Tue Dec 20 12:47:10 CET 2005
Subject: [cvs-checkins] MODIFIED: or1k ...
Date: 00/05/12 20:12:47 Added: or1k/rc203soc/sw/uClinux/mmnommu Makefile filemap.c kmalloc.c memory.c mlock.c mmap.c mprotect.c mremap.c page_alloc.c swap.c swapfile.c vmalloc.c vmscan.c Log: First Import of RC20x uClinux Revision Changes Path 1.1 or1k/rc203soc/sw/uClinux/mmnommu/Makefile http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/Makefile?rev=1.1&content-type=text/x-cvsweb-markup Index: Makefile =================================================================== # # Makefile for the uClinux memory manager. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here # unless it's something special (ie not a .c file). # # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := mm.o O_OBJS := memory.o mmap.o filemap.o mprotect.o mlock.o mremap.o \ kmalloc.o vmalloc.o \ swap.o vmscan.o page_alloc.o swapfile.o include $(TOPDIR)/Rules.make 1.1 or1k/rc203soc/sw/uClinux/mmnommu/filemap.c http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/filemap.c?rev=1.1&content-type=text/x-cvsweb-markup Index: filemap.c =================================================================== /* * linux/mm/filemap.c * * Copyright (C) 1994, 1995 Linus Torvalds * * uClinux revisions * Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>, * The Silver Hammer Group, Ltd. * Copyright (C) 1999 D. Jeff Dionne <jeff@u...>, * Rt-Control, Inc. */ /* * This file handles the generic file mmap semantics used by * most "normal" filesystems (but you don't /have/ to use this: * the NFS filesystem does this differently, for example) */ #include <linux/config.h> /* CONFIG_READA_SMALL */ #include <linux/stat.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/shm.h> #include <linux/errno.h> #include <linux/mman.h> #include <linux/string.h> #include <linux/malloc.h> #include <linux/fs.h> #include <linux/locks.h> #include <linux/pagemap.h> #include <linux/swap.h> #include <asm/segment.h> #include <asm/system.h> #include <asm/pgtable.h> /* * Shared mappings implemented 30.11.1994. It's not fully working yet, * though. * * Shared mappings now work. 15.8.1995 Bruno. */ unsigned long page_cache_size = 0; struct page * page_hash_table[PAGE_HASH_SIZE]; /* * Simple routines for both non-shared and shared mappings. */ #define release_page(page) __free_page((page)) /* * Invalidate the pages of an inode, removing all pages that aren't * locked down (those are sure to be up-to-date anyway, so we shouldn't * invalidate them). */ void invalidate_inode_pages(struct inode * inode) { struct page ** p; struct page * page;
p = &inode->i_pages;
while ((page = *p) != NULL) {
if (PageLocked(page)) {
p = &page->next;
continue;
}
inode->i_nrpages--;
if ((*p = page->next) != NULL)
(*p)->prev = page->prev;
page->dirty = 0;
page->next = NULL;
page->prev = NULL;
remove_page_from_hash_queue(page);
page->inode = NULL;
__free_page(page);
continue;
}
}
/*
* Truncate the page cache at a set offset, removing the pages
* that are beyond that offset (and zeroing out partial pages).
*/
void truncate_inode_pages(struct inode * inode, unsigned long start)
{
struct page ** p;
struct page * page;
repeat:
p = &inode->i_pages;
while ((page = *p) != NULL) {
unsigned long offset = page->offset;
/* page wholly truncated - free it */
if (offset >= start) {
if (PageLocked(page)) {
__wait_on_page(page);
goto repeat;
}
inode->i_nrpages--;
if ((*p = page->next) != NULL)
(*p)->prev = page->prev;
page->dirty = 0;
page->next = NULL;
page->prev = NULL;
remove_page_from_hash_queue(page);
page->inode = NULL;
__free_page(page);
continue;
}
p = &page->next;
offset = start - offset;
/* partial truncate, clear end of page */
if (offset < PAGE_SIZE) {
unsigned long address = page_address(page);
memset((void *) (offset + address), 0, PAGE_SIZE - offset);
flush_page_to_ram(address);
}
}
}
/*
* This is called from try_to_swap_out() when we try to get rid of some
* pages.. If we're unmapping the last occurrence of this page, we also
* free it from the page hash-queues etc, as we don't want to keep it
* in-core unnecessarily.
*/
unsigned long page_unuse(unsigned long page)
{
struct page * p = mem_map + MAP_NR(page);
int count = p->count;
if (count != 2)
return count;
if (!p->inode)
return count;
remove_page_from_hash_queue(p);
remove_page_from_inode_queue(p);
free_page(page);
return 1;
}
/*
* Update a page cache copy, when we're doing a "write()" system call
* See also "update_vm_cache()".
*/
void update_vm_cache(struct inode * inode, unsigned long pos, const char * buf, int count)
{
unsigned long offset, len;
offset = (pos & ~PAGE_MASK);
pos = pos & PAGE_MASK;
len = PAGE_SIZE - offset;
do {
struct page * page;
if (len > count)
len = count;
page = find_page(inode, pos);
if (page) {
wait_on_page(page);
memcpy((void *) (offset + page_address(page)), buf, len);
release_page(page);
}
count -= len;
buf += len;
len = PAGE_SIZE;
offset = 0;
pos += PAGE_SIZE;
} while (count);
}
static inline void add_to_page_cache(struct page * page,
struct inode * inode, unsigned long offset,
struct page **hash)
{
page->count++;
page->flags &= ~((1 << PG_uptodate) | (1 << PG_error));
page->offset = offset;
add_page_to_inode_queue(inode, page);
__add_page_to_hash_queue(page, hash);
}
/*
* Try to read ahead in the file. "page_cache" is a potentially free page
* that we could use for the cache (if it is 0 we can try to create one,
* this is all overlapped with the IO on the previous page finishing anyway)
*/
static unsigned long try_to_read_ahead(struct inode * inode, unsigned long offset, unsigned long page_cache)
{
struct page * page;
struct page ** hash;
offset &= PAGE_MASK;
switch (page_cache) {
case 0:
page_cache = __get_free_page(GFP_KERNEL);
if (!page_cache)
break;
default:
if (offset >= inode->i_size)
break;
hash = page_hash(inode, offset);
page = __find_page(inode, offset, *hash);
if (!page) {
/*
* Ok, add the new page to the hash-queues...
*/
page = mem_map + MAP_NR(page_cache);
add_to_page_cache(page, inode, offset, hash);
inode->i_op->readpage(inode, page);
page_cache = 0;
}
release_page(page);
}
return page_cache;
}
/*
* Wait for IO to complete on a locked page.
*
* This must be called with the caller "holding" the page,
* ie with increased "page->count" so that the page won't
* go away during the wait..
*/
void __wait_on_page(struct page *page)
{
struct wait_queue wait = { current, NULL };
add_wait_queue(&page->wait, &wait);
repeat:
run_task_queue(&tq_disk);
current->state = TASK_UNINTERRUPTIBLE;
if (PageLocked(page)) {
schedule();
goto repeat;
}
remove_wait_queue(&page->wait, &wait);
current->state = TASK_RUNNING;
}
#if 0
#define PROFILE_READAHEAD
#define DEBUG_READAHEAD
#endif
/*
* Read-ahead profiling information
* --------------------------------
* Every PROFILE_MAXREADCOUNT, the following information is written
* to the syslog:
* Percentage of asynchronous read-ahead.
* Average of read-ahead fields context value.
* If DEBUG_READAHEAD is defined, a snapshot of these fields is written
* to the syslog.
*/
#ifdef PROFILE_READAHEAD
#define PROFILE_MAXREADCOUNT 1000
static unsigned long total_reada;
static unsigned long total_async;
static unsigned long total_ramax;
static unsigned long total_ralen;
static unsigned long total_rawin;
static void profile_readahead(int async, struct file *filp)
{
unsigned long flags;
++total_reada;
if (async)
++total_async;
total_ramax += filp->f_ramax;
total_ralen += filp->f_ralen;
total_rawin += filp->f_rawin;
if (total_reada > PROFILE_MAXREADCOUNT) {
save_flags(flags);
cli();
if (!(total_reada > PROFILE_MAXREADCOUNT)) {
restore_flags(flags);
return;
}
printk("Readahead average: max=%ld, len=%ld, win=%ld, async=%ld%%\n",
total_ramax/total_reada,
total_ralen/total_reada,
total_rawin/total_reada,
(total_async*100)/total_reada);
#ifdef DEBUG_READAHEAD
printk("Readahead snapshot: max=%ld, len=%ld, win=%ld, raend=%ld\n",
filp->f_ramax, filp->f_ralen, filp->f_rawin, filp->f_raend);
#endif
total_reada = 0;
total_async = 0;
total_ramax = 0;
total_ralen = 0;
total_rawin = 0;
restore_flags(flags);
}
}
#endif /* defined PROFILE_READAHEAD */
/*
* Read-ahead context:
* -------------------
* The read ahead context fields of the "struct file" are the following:
* - f_raend : position of the first byte after the last page we tried to
* read ahead.
* - f_ramax : current read-ahead maximum size.
* - f_ralen : length of the current IO read block we tried to read-ahead.
* - f_rawin : length of the current read-ahead window.
* if last read-ahead was synchronous then
* f_rawin = f_ralen
* otherwise (was asynchronous)
* f_rawin = previous value of f_ralen + f_ralen
*
* Read-ahead limits:
* ------------------
* MIN_READAHEAD : minimum read-ahead size when read-ahead.
* MAX_READAHEAD : maximum read-ahead size when read-ahead.
*
* Synchronous read-ahead benefits:
* --------------------------------
* Using reasonable IO xfer length from peripheral devices increase system
* performances.
* Reasonable means, in this context, not too large but not too small.
* The actual maximum value is:
* MAX_READAHEAD + PAGE_SIZE = 76k is CONFIG_READA_SMALL is undefined
* and 32K if defined (4K page size assumed).
*
* Asynchronous read-ahead benefits:
* ---------------------------------
* Overlapping next read request and user process execution increase system
* performance.
*
* Read-ahead risks:
* -----------------
* We have to guess which further data are needed by the user process.
* If these data are often not really needed, it's bad for system
* performances.
* However, we know that files are often accessed sequentially by
* application programs and it seems that it is possible to have some good
* strategy in that guessing.
* We only try to read-ahead files that seems to be read sequentially.
*
* Asynchronous read-ahead risks:
* ------------------------------
* In order to maximize overlapping, we must start some asynchronous read
* request from the device, as soon as possible.
* We must be very careful about:
* - The number of effective pending IO read requests.
* ONE seems to be the only reasonable value.
* - The total memory pool usage for the file access stream.
* This maximum memory usage is implicitly 2 IO read chunks:
* 2*(MAX_READAHEAD + PAGE_SIZE) = 156K if CONFIG_READA_SMALL is undefined,
* 64k if defined (4K page size assumed).
*/
#define PageAlignSize(size) (((size) + PAGE_SIZE -1) & PAGE_MASK)
#ifdef CONFIG_READA_SMALL /* small readahead */
#define MAX_READAHEAD PageAlignSize(4096*7)
#define MIN_READAHEAD PageAlignSize(4096*2)
#else /* large readahead */
#define MAX_READAHEAD PageAlignSize(4096*18)
#define MIN_READAHEAD PageAlignSize(4096*3)
#endif
static inline unsigned long generic_file_readahead(int reada_ok, struct file * filp, struct inode * inode,
unsigned long ppos, struct page * page,
unsigned long page_cache)
{
unsigned long max_ahead, ahead;
unsigned long raend;
raend = filp->f_raend & PAGE_MASK;
max_ahead = 0;
/*
* The current page is locked.
* If the current position is inside the previous read IO request, do not
* try to reread previously read ahead pages.
* Otherwise decide or not to read ahead some pages synchronously.
* If we are not going to read ahead, set the read ahead context for this
* page only.
*/
if (PageLocked(page)) {
if (!filp->f_ralen || ppos >= raend || ppos + filp->f_ralen < raend) {
raend = ppos;
if (raend < inode->i_size)
max_ahead = filp->f_ramax;
filp->f_rawin = 0;
filp->f_ralen = PAGE_SIZE;
if (!max_ahead) {
filp->f_raend = ppos + filp->f_ralen;
filp->f_rawin += filp->f_ralen;
}
}
}
/*
* The current page is not locked.
* If we were reading ahead and,
* if the current max read ahead size is not zero and,
* if the current position is inside the last read-ahead IO request,
* it is the moment to try to read ahead asynchronously.
* We will later force unplug device in order to force asynchronous read IO.
*/
else if (reada_ok && filp->f_ramax && raend >= PAGE_SIZE &&
ppos <= raend && ppos + filp->f_ralen >= raend) {
/*
* Add ONE page to max_ahead in order to try to have about the same IO max size
* as synchronous read-ahead (MAX_READAHEAD + 1)*PAGE_SIZE.
* Compute the position of the last page we have tried to read in order to
* begin to read ahead just at the next page.
*/
raend -= PAGE_SIZE;
if (raend < inode->i_size)
max_ahead = filp->f_ramax + PAGE_SIZE;
if (max_ahead) {
filp->f_rawin = filp->f_ralen;
filp->f_ralen = 0;
reada_ok = 2;
}
}
/*
* Try to read ahead pages.
* We hope that ll_rw_blk() plug/unplug, coalescence, requests sort and the
* scheduler, will work enough for us to avoid too bad actuals IO requests.
*/
ahead = 0;
while (ahead < max_ahead) {
ahead += PAGE_SIZE;
page_cache = try_to_read_ahead(inode, raend + ahead, page_cache);
}
/*
* If we tried to read ahead some pages,
* If we tried to read ahead asynchronously,
* Try to force unplug of the device in order to start an asynchronous
* read IO request.
* Update the read-ahead context.
* Store the length of the current read-ahead window.
* Double the current max read ahead size.
* That heuristic avoid to do some large IO for files that are not really
* accessed sequentially.
*/
if (ahead) {
if (reada_ok == 2) {
run_task_queue(&tq_disk);
}
filp->f_ralen += ahead;
filp->f_rawin += filp->f_ralen;
filp->f_raend = raend + ahead + PAGE_SIZE;
filp->f_ramax += filp->f_ramax;
if (filp->f_ramax > MAX_READAHEAD)
filp->f_ramax = MAX_READAHEAD;
#ifdef PROFILE_READAHEAD
profile_readahead((reada_ok == 2), filp);
#endif
}
return page_cache;
}
/*
* This is a generic file read routine, and uses the
* inode->i_op->readpage() function for the actual low-level
* stuff.
*
* This is really ugly. But the goto's actually try to clarify some
* of the logic when it comes to error handling etc.
*/
int generic_file_read(struct inode * inode, struct file * filp, char * buf, int count)
{
int error, read;
unsigned long pos, ppos, page_cache;
int reada_ok;
error = 0;
read = 0;
page_cache = 0;
pos = filp->f_pos;
ppos = pos & PAGE_MASK;
#ifdef MAGIC_ROM_PTR
/* Logic: if romptr f_op is available, try to get a pointer into ROM
* for the data, bypassing the buffer cache entirely. This is only a
* win if the ROM is reasonably fast, of course.
*
* Note that this path only requires that the pointer (and the data
* it points to) to be valid until the memcpy_tofs is complete.
*
* -- Kenneth Albanowski
*/
if (filp->f_op->romptr) {
struct vm_area_struct vma;
vma.vm_start = 0;
vma.vm_offset = pos;
vma.vm_flags = VM_READ;
if (!filp->f_op->romptr(inode, filp, &vma)) {
if (count > inode->i_size - pos)
count = inode->i_size - pos;
memcpy_tofs(buf, (void*)vma.vm_start, count);
filp->f_pos += count;
return count;
}
}
#endif /* MAGIC_ROM_PTR */
/*
* If the current position is outside the previous read-ahead window,
* we reset the current read-ahead context and set read ahead max to zero
* (will be set to just needed value later),
* otherwise, we assume that the file accesses are sequential enough to
* continue read-ahead.
*/
if (ppos > filp->f_raend || ppos + filp->f_rawin < filp->f_raend) {
reada_ok = 0;
filp->f_raend = 0;
filp->f_ralen = 0;
filp->f_ramax = 0;
filp->f_rawin = 0;
} else {
reada_ok = 1;
}
/*
* Adjust the current value of read-ahead max.
* If the read operation stay in the first half page, force no readahead.
* Otherwise try to increase read ahead max just enough to do the read request.
* Then, at least MIN_READAHEAD if read ahead is ok,
* and at most MAX_READAHEAD in all cases.
*/
if (pos + count <= (PAGE_SIZE >> 1)) {
filp->f_ramax = 0;
} else {
unsigned long needed;
needed = ((pos + count) & PAGE_MASK) - ppos;
if (filp->f_ramax < needed)
filp->f_ramax = needed;
if (reada_ok && filp->f_ramax < MIN_READAHEAD)
filp->f_ramax = MIN_READAHEAD;
if (filp->f_ramax > MAX_READAHEAD)
filp->f_ramax = MAX_READAHEAD;
}
for (;;) {
struct page *page, **hash;
if (pos >= inode->i_size)
break;
/*
* Try to find the data in the page cache..
*/
hash = page_hash(inode, pos & PAGE_MASK);
page = __find_page(inode, pos & PAGE_MASK, *hash);
if (!page)
goto no_cached_page;
found_page:
/*
* Try to read ahead only if the current page is filled or being filled.
* Otherwise, if we were reading ahead, decrease max read ahead size to
* the minimum value.
* In this context, that seems to may happen only on some read error or if
* the page has been rewritten.
*/
if (PageUptodate(page) || PageLocked(page))
page_cache = generic_file_readahead(reada_ok, filp, inode, pos & PAGE_MASK, page, page_cache);
else if (reada_ok && filp->f_ramax > MIN_READAHEAD)
filp->f_ramax = MIN_READAHEAD;
wait_on_page(page);
if (!PageUptodate(page))
goto page_read_error;
success:
/*
* Ok, we have the page, it's up-to-date and ok,
* so now we can finally copy it to user space...
*/
{
unsigned long offset, nr;
offset = pos & ~PAGE_MASK;
nr = PAGE_SIZE - offset;
if (nr > count)
nr = count;
if (nr > inode->i_size - pos)
nr = inode->i_size - pos;
memcpy_tofs(buf, (void *) (page_address(page) + offset), nr);
release_page(page);
buf += nr;
pos += nr;
read += nr;
count -= nr;
if (count) {
/*
* to prevent hogging the CPU on well-cached systems,
* schedule if needed, it's safe to do it here:
*/
if (need_resched)
schedule();
continue;
}
break;
}
no_cached_page:
/*
* Ok, it wasn't cached, so we need to create a new
* page..
*/
if (!page_cache) {
page_cache = __get_free_page(GFP_KERNEL);
/*
* That could have slept, so go around to the
* very beginning..
*/
if (page_cache)
continue;
error = -ENOMEM;
break;
}
/*
* Ok, add the new page to the hash-queues...
*/
page = mem_map + MAP_NR(page_cache);
page_cache = 0;
add_to_page_cache(page, inode, pos & PAGE_MASK, hash);
/*
* Error handling is tricky. If we get a read error,
* the cached page stays in the cache (but uptodate=0),
* and the next process that accesses it will try to
* re-read it. This is needed for NFS etc, where the
* identity of the reader can decide if we can read the
* page or not..
*/
/*
* We have to read the page.
* If we were reading ahead, we had previously tried to read this page,
* That means that the page has probably been removed from the cache before
* the application process needs it, or has been rewritten.
* Decrease max readahead size to the minimum value in that situation.
*/
if (reada_ok && filp->f_ramax > MIN_READAHEAD)
filp->f_ramax = MIN_READAHEAD;
error = inode->i_op->readpage(inode, page);
if (!error)
goto found_page;
release_page(page);
break;
page_read_error:
/*
* We found the page, but it wasn't up-to-date.
* Try to re-read it _once_. We do this synchronously,
* because this happens only if there were errors.
*/
error = inode->i_op->readpage(inode, page);
if (!error) {
wait_on_page(page);
if (PageUptodate(page) && !PageError(page))
goto success;
error = -EIO; /* Some unspecified error occurred.. */
}
release_page(page);
break;
}
filp->f_pos = pos;
filp->f_reada = 1;
if (page_cache)
free_page(page_cache);
UPDATE_ATIME(inode)
if (!read)
read = error;
return read;
}
int shrink_mmap(int priority, int dma, int free_buf)
{
static int clock = 0;
struct page * page;
unsigned long limit = MAP_NR(high_memory);
struct buffer_head *tmp, *bh;
int count_max, count_min;
count_max = (limit<<1) >> (priority>>1);
count_min = (limit<<1) >> (priority);
page = mem_map + clock;
do {
count_max--;
if (page->inode || page->buffers)
count_min--;
if (PageLocked(page))
goto next;
if (dma && !PageDMA(page))
goto next;
/* First of all, regenerate the page's referenced bit
from any buffers in the page */
bh = page->buffers;
if (bh) {
tmp = bh;
do {
if (buffer_touched(tmp)) {
clear_bit(BH_Touched, &tmp->b_state);
set_bit(PG_referenced, &page->flags);
}
tmp = tmp->b_this_page;
} while (tmp != bh);
}
/* We can't throw away shared pages, but we do mark
them as referenced. This relies on the fact that
no page is currently in both the page cache and the
buffer cache; we'd have to modify the following
test to allow for that case. */
switch (page->count) {
case 1:
/* If it has been referenced recently, don't free it */
if (clear_bit(PG_referenced, &page->flags)) {
/* age this page potential used */
if (priority < 4)
age_page(page);
break;
}
/* is it a page cache page? */
if (page->inode) {
remove_page_from_hash_queue(page);
remove_page_from_inode_queue(page);
__free_page(page);
return 1;
}
/* is it a buffer cache page? */
if (free_buf && bh && try_to_free_buffer(bh, &bh, 6))
return 1;
break;
default:
/* more than one users: we can't throw it away */
set_bit(PG_referenced, &page->flags);
/* fall through */
case 0:
/* nothing */
}
next:
page++;
clock++;
if (clock >= limit) {
clock = 0;
page = mem_map;
}
} while (count_max > 0 && count_min > 0);
return 0;
}
asmlinkage int sys_msync(unsigned long start, size_t len, int flags)
{
return 0;
}
int generic_file_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
{
return -ENOSYS;
}
1.1 or1k/rc203soc/sw/uClinux/mmnommu/kmalloc.c
http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/kmalloc.c?rev=1.1&content-type=text/x-cvsweb-markup
Index: kmalloc.c
===================================================================
/*
* linux/mm/kmalloc.c
*
* Copyright (C) 1991, 1992 Linus Torvalds & Roger Wolff.
*
* Written by R.E. Wolff Sept/Oct '93.
*
*/
/*
* Modified by Alex Bligh (alex@c...) 4 Apr 1994 to use multiple
* pages. So for 'page' throughout, read 'area'.
*
* Largely rewritten.. Linus
*/
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <asm/system.h>
#include <asm/dma.h>
/* Define this if you want slow routines that try to trip errors */
#undef SADISTIC_KMALLOC
/* Private flags. */
#define MF_USED 0xffaa0055
#define MF_DMA 0xff00aa55
#define MF_FREE 0x0055ffaa
/*
* Much care has gone into making these routines in this file reentrant.
*
* The fancy bookkeeping of nbytesmalloced and the like are only used to
* report them to the user (oooohhhhh, aaaaahhhhh....) are not
* protected by cli(). (If that goes wrong. So what?)
*
* These routines restore the interrupt status to allow calling with ints
* off.
*/
/*
* A block header. This is in front of every malloc-block, whether free or not.
*/
struct block_header {
unsigned long bh_flags;
union {
unsigned long ubh_length;
struct block_header *fbh_next;
} vp;
};
#define bh_length vp.ubh_length
#define bh_next vp.fbh_next
#define BH(p) ((struct block_header *)(p))
/*
* The page descriptor is at the front of every page that malloc has in use.
*/
struct page_descriptor {
struct page_descriptor *next;
struct block_header *firstfree;
int order;
int nfree;
};
#define PAGE_DESC(p) ((struct page_descriptor *)(((unsigned long)(p)) & PAGE_MASK))
/*
* A size descriptor describes a specific class of malloc sizes.
* Each class of sizes has its own freelist.
*/
struct size_descriptor {
struct page_descriptor *firstfree;
struct page_descriptor *dmafree; /* DMA-able memory */
int nblocks;
int nmallocs;
int nfrees;
int nbytesmalloced;
int npages;
unsigned long gfporder; /* number of pages in the area required */
};
/*
* For now it is unsafe to allocate bucket sizes between n and
* n-sizeof(page_descriptor) where n is PAGE_SIZE * any power of two
*
* The blocksize and sizes arrays _must_ match!
*/
#if PAGE_SIZE == 4096
static const unsigned int blocksize[] = {
32,
64,
128,
252,
508,
1020,
2040,
4096 - 16,
8192 - 16,
16384 - 16,
32768 - 16,
65536 - 16,
131072 - 16,
262144 - 16,
#ifdef BIGALLOCS
524288 - 16,
1048576 - 16,
/* SIMON: change of source file - te get mw working */
2097152 - 16,
#endif
0
};
static struct size_descriptor sizes[] =
{
{NULL, NULL, 127, 0, 0, 0, 0, 0},
{NULL, NULL, 63, 0, 0, 0, 0, 0},
{NULL, NULL, 31, 0, 0, 0, 0, 0},
{NULL, NULL, 16, 0, 0, 0, 0, 0},
{NULL, NULL, 8, 0, 0, 0, 0, 0},
{NULL, NULL, 4, 0, 0, 0, 0, 0},
{NULL, NULL, 2, 0, 0, 0, 0, 0},
{NULL, NULL, 1, 0, 0, 0, 0, 0},
{NULL, NULL, 1, 0, 0, 0, 0, 1},
{NULL, NULL, 1, 0, 0, 0, 0, 2},
{NULL, NULL, 1, 0, 0, 0, 0, 3},
{NULL, NULL, 1, 0, 0, 0, 0, 4},
{NULL, NULL, 1, 0, 0, 0, 0, 5},
{NULL, NULL, 1, 0, 0, 0, 0, 6},
#ifdef BIGALLOCS
{NULL, NULL, 1, 0, 0, 0, 0, 7},
{NULL, NULL, 1, 0, 0, 0, 0, 8},
{NULL, NULL, 1, 0, 0, 0, 0, 9},
#endif
{NULL, NULL, 0, 0, 0, 0, 0, 0},
};
#elif PAGE_SIZE == 8192
static const unsigned int blocksize[] = {
64,
128,
248,
504,
1016,
2040,
4080,
8192 - 32,
16384 - 32,
32768 - 32,
65536 - 32,
131072 - 32,
262144 - 32,
0
};
struct size_descriptor sizes[] =
{
{NULL, NULL, 127, 0, 0, 0, 0, 0},
{NULL, NULL, 63, 0, 0, 0, 0, 0},
{NULL, NULL, 31, 0, 0, 0, 0, 0},
{NULL, NULL, 16, 0, 0, 0, 0, 0},
{NULL, NULL, 8, 0, 0, 0, 0, 0},
{NULL, NULL, 4, 0, 0, 0, 0, 0},
{NULL, NULL, 2, 0, 0, 0, 0, 0},
{NULL, NULL, 1, 0, 0, 0, 0, 0},
{NULL, NULL, 1, 0, 0, 0, 0, 1},
{NULL, NULL, 1, 0, 0, 0, 0, 2},
{NULL, NULL, 1, 0, 0, 0, 0, 3},
{NULL, NULL, 1, 0, 0, 0, 0, 4},
{NULL, NULL, 1, 0, 0, 0, 0, 5},
{NULL, NULL, 0, 0, 0, 0, 0, 0}
};
#else
#error you need to make a version for your pagesize
#endif
#define NBLOCKS(order) (sizes[order].nblocks)
#define BLOCKSIZE(order) (blocksize[order])
#define AREASIZE(order) (PAGE_SIZE<<(sizes[order].gfporder))
/*
* Create a small cache of page allocations: this helps a bit with
* those pesky 8kB+ allocations for NFS when we're temporarily
* out of memory..
*
* This is a _truly_ small cache, we just cache one single page
* order (for orders 0, 1 and 2, that is 4, 8 and 16kB on x86).
*/
#define MAX_CACHE_ORDER 3
struct page_descriptor * kmalloc_cache[MAX_CACHE_ORDER];
static inline struct page_descriptor * get_kmalloc_pages(unsigned long priority,
unsigned long order, int dma)
{
return (struct page_descriptor *) __get_free_pages(priority, order, dma);
}
static inline void free_kmalloc_pages(struct page_descriptor * page,
unsigned long order, int dma)
{
if (!dma && order < MAX_CACHE_ORDER) {
page = xchg(kmalloc_cache+order, page);
if (!page)
return;
}
free_pages((unsigned long) page, order);
}
long kmalloc_init(long start_mem, long end_mem)
{
int order;
/*
* Check the static info array. Things will blow up terribly if it's
* incorrect. This is a late "compile time" check.....
*/
for (order = 0; BLOCKSIZE(order); order++) {
if ((NBLOCKS(order) * BLOCKSIZE(order) + sizeof(struct page_descriptor)) >
AREASIZE(order)) {
printk("Cannot use %d bytes out of %d in order = %d block mallocs\n",
(int) (NBLOCKS(order) * BLOCKSIZE(order) +
sizeof(struct page_descriptor)),
(int) AREASIZE(order),
BLOCKSIZE(order));
panic("This only happens if someone messes with kmalloc");
}
}
return start_mem;
}
/*
* Ugh, this is ugly, but we want the default case to run
* straight through, which is why we have the ugly goto's
*/
void *kmalloc(size_t size, int priority)
{
unsigned long flags;
unsigned long type;
int order, dma;
struct block_header *p;
struct page_descriptor *page, **pg;
struct size_descriptor *bucket = sizes;
/* Get order */
order = 0;
{
unsigned int realsize = size + sizeof(struct block_header);
for (;;) {
int ordersize = BLOCKSIZE(order);
if (realsize <= ordersize)
break;
order++;
bucket++;
if (ordersize)
continue;
printk("kmalloc of too large a block (%d bytes).\n", (int) size);
return NULL;
}
}
dma = 0;
type = MF_USED;
pg = &bucket->firstfree;
if (priority & GFP_DMA) {
dma = 1;
type = MF_DMA;
pg = &bucket->dmafree;
}
priority &= GFP_LEVEL_MASK;
/* Sanity check... */
if (intr_count && priority != GFP_ATOMIC) {
static int count = 0;
if (++count < 5) {
printk("kmalloc called nonatomically from interrupt %p\n",
__builtin_return_address(0));
priority = GFP_ATOMIC;
}
}
save_flags(flags);
cli();
page = *pg;
if (!page)
goto no_bucket_page;
p = page->firstfree;
if (p->bh_flags != MF_FREE)
goto not_free_on_freelist;
found_it:
page->firstfree = p->bh_next;
page->nfree--;
if (!page->nfree)
*pg = page->next;
restore_flags(flags);
bucket->nmallocs++;
bucket->nbytesmalloced += size;
p->bh_flags = type; /* As of now this block is officially in use */
p->bh_length = size;
#ifdef SADISTIC_KMALLOC
memset(p+1, 0xf0, size);
#endif
return p + 1; /* Pointer arithmetic: increments past header */
no_bucket_page:
/*
* If we didn't find a page already allocated for this
* bucket size, we need to get one..
*
* This can be done with ints on: it is private to this invocation
*/
restore_flags(flags);
{
int i, sz;
/* sz is the size of the blocks we're dealing with */
sz = BLOCKSIZE(order);
page = get_kmalloc_pages(priority, bucket->gfporder, dma);
if (!page)
goto no_free_page;
found_cached_page:
bucket->npages++;
page->order = order;
/* Loop for all but last block: */
i = (page->nfree = bucket->nblocks) - 1;
p = BH(page + 1);
while (i > 0) {
i--;
p->bh_flags = MF_FREE;
p->bh_next = BH(((long) p) + sz);
p = p->bh_next;
}
/* Last block: */
p->bh_flags = MF_FREE;
p->bh_next = NULL;
p = BH(page+1);
}
/*
* Now we're going to muck with the "global" freelist
* for this size: this should be uninterruptible
*/
cli();
page->next = *pg;
*pg = page;
goto found_it;
no_free_page:
/*
* No free pages, check the kmalloc cache of
* pages to see if maybe we have something available
*/
if (!dma && order < MAX_CACHE_ORDER) {
page = xchg(kmalloc_cache+order, page);
if (page)
goto found_cached_page;
}
{
static unsigned long last = 0;
if (priority != GFP_BUFFER && priority != GFP_IO &&
(last + 10 * HZ < jiffies)) {
last = jiffies;
printk("Couldn't get a free page.....\n");
}
return NULL;
}
not_free_on_freelist:
restore_flags(flags);
printk("Problem: block on freelist at %08lx isn't free.\n", (long) p);
return NULL;
}
void kfree(void *__ptr)
{
int dma;
unsigned long flags;
unsigned int order;
struct page_descriptor *page, **pg;
struct size_descriptor *bucket;
if (!__ptr)
goto null_kfree;
#define ptr ((struct block_header *) __ptr)
page = PAGE_DESC(ptr);
__ptr = ptr - 1;
if (~PAGE_MASK & (unsigned long)page->next)
goto bad_order;
order = page->order;
if (order >= sizeof(sizes) / sizeof(sizes[0]))
goto bad_order;
bucket = sizes + order;
dma = 0;
pg = &bucket->firstfree;
if (ptr->bh_flags == MF_DMA) {
dma = 1;
ptr->bh_flags = MF_USED;
pg = &bucket->dmafree;
}
if (ptr->bh_flags != MF_USED)
goto bad_order;
ptr->bh_flags = MF_FREE; /* As of now this block is officially free */
#ifdef SADISTIC_KMALLOC
memset(ptr+1, 0xe0, ptr->bh_length);
#endif
save_flags(flags);
cli();
bucket->nfrees++;
bucket->nbytesmalloced -= ptr->bh_length;
ptr->bh_next = page->firstfree;
page->firstfree = ptr;
if (!page->nfree++) {
/* Page went from full to one free block: put it on the freelist. */
if (bucket->nblocks == 1)
goto free_page;
page->next = *pg;
*pg = page;
}
/* If page is completely free, free it */
if (page->nfree == bucket->nblocks) {
for (;;) {
struct page_descriptor *tmp = *pg;
if (!tmp)
goto not_on_freelist;
if (tmp == page)
break;
pg = &tmp->next;
}
*pg = page->next;
free_page:
bucket->npages--;
free_kmalloc_pages(page, bucket->gfporder, dma);
}
restore_flags(flags);
null_kfree:
return;
bad_order:
printk("kfree of non-kmalloced memory: %p, next= %p, order=%d\n",
ptr+1, page->next, page->order);
return;
not_on_freelist:
restore_flags(flags);
printk("Ooops. page %p doesn't show on freelist.\n", page);
}
unsigned int ksize(void *__ptr)
{
unsigned int order;
struct page_descriptor *page;
if (!ptr) return 0;
#define ptr ((struct block_header *) __ptr)
page = PAGE_DESC(ptr);
__ptr = ptr - 1;
if (~PAGE_MASK & (unsigned long)page->next) return 0;
order = page->order;
if (order >= sizeof(sizes) / sizeof(sizes[0])) return 0;
return BLOCKSIZE(order);
}
1.1 or1k/rc203soc/sw/uClinux/mmnommu/memory.c
http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/memory.c?rev=1.1&content-type=text/x-cvsweb-markup
Index: memory.c
===================================================================
/*
* linux/mm/memory.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* uClinux revisions for NO_MM
* Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>,
* The Silver Hammer Group, Ltd.
*/
#include <linux/config.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/swap.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/pgtable.h>
#include <asm/string.h>
unsigned long high_memory = 0;
mem_map_t * mem_map = NULL;
/*
* oom() prints a message (so that the user knows why the process died),
* and gives the process an untrappable SIGKILL.
*/
void oom(struct task_struct * task)
{
printk("\nOut of memory for %s.\n", task->comm);
task->sig->action[SIGKILL-1].sa_handler = NULL;
task->blocked &= ~(1<<(SIGKILL-1));
send_sig(SIGKILL,task,1);
}
#ifdef DEBUG_VERIFY_AREA
#undef verify_area
int verify_area_flf(int type, const void * addr, unsigned long size, char *file, int line, char * function)
{
int result;
result = verify_area(type, addr, size);
if (result)
printk("%s:%d %s> verify_area(%d,%p,%ld) = %d\n",file,line, function, type, addr, size, result);
return result;
}
#endif /* DEBUG_VERIFY_AREA */
/* FIXME: Need to check the architecture memory map
* DJD.
*/
int verify_area(int type, const void * addr, unsigned long size)
{
#ifdef CONFIG_COLDFIRE
extern unsigned long _ramend;
if ((unsigned long)addr > _ramend) {
#else
if ((unsigned long)addr > 0xf0800000) {
#endif
printk("Bad verify_area in process %d: %lx\n",
current->pid, (unsigned long)addr);
return -EFAULT;
}
return 0;
}
1.1 or1k/rc203soc/sw/uClinux/mmnommu/mlock.c
http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/mlock.c?rev=1.1&content-type=text/x-cvsweb-markup
Index: mlock.c
===================================================================
/*
* linux/mm/mlock.c
*
* (C) Copyright 1995 Linus Torvalds
*/
/*
* uClinux revisions for NO_MM
* Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>,
* The Silver Hammer Group, Ltd.
* Copyright (C) 1999 D. Jeff Dionne <jeff@u...>,
* Rt-Control, Inc.
*/
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h>
asmlinkage int sys_mlock(unsigned long start, size_t len)
{
return -ENOSYS;
}
asmlinkage int sys_munlock(unsigned long start, size_t len)
{
return -ENOSYS;
}
asmlinkage int sys_mlockall(int flags)
{
return -ENOSYS;
}
asmlinkage int sys_munlockall(void)
{
return -ENOSYS;
}
1.1 or1k/rc203soc/sw/uClinux/mmnommu/mmap.c
http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/mmap.c?rev=1.1&content-type=text/x-cvsweb-markup
Index: mmap.c
===================================================================
/*
* linux/mm/mmap.c
*
* Written by obz.
*/
/*
* uClinux revisions for NO_MM
* Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>,
* The Silver Hammer Group, Ltd.
* Copyright (C) 1999 D. Jeff Dionne <jeff@u...>,
* Rt-Control, Inc.
*/
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/pgtable.h>
/* If defined, warn whenever someone mmaps a block that has more then this
number of bytes in slack space (due to kmalloc granularity). */
/*#define WARN_ON_SLACK 1024*/
/*
* Combine the mmap "prot" and "flags" argument into one "vm_flags" used
* internally. Essentially, translate the "PROT_xxx" and "MAP_xxx" bits
* into "VM_xxx".
*/
static inline unsigned long vm_flags(unsigned long prot, unsigned long flags)
{
#define _trans(x,bit1,bit2) \
((bit1==bit2)?(x&bit1):(x&bit1)?bit2:0)
unsigned long prot_bits, flag_bits;
prot_bits =
_trans(prot, PROT_READ, VM_READ) |
_trans(prot, PROT_WRITE, VM_WRITE) |
_trans(prot, PROT_EXEC, VM_EXEC);
flag_bits =
_trans(flags, MAP_GROWSDOWN, VM_GROWSDOWN) |
_trans(flags, MAP_DENYWRITE, VM_DENYWRITE) |
_trans(flags, MAP_EXECUTABLE, VM_EXECUTABLE);
return prot_bits | flag_bits;
#undef _trans
}
asmlinkage unsigned long sys_brk(unsigned long brk)
{
return -ENOSYS;
}
#ifdef DEBUG_MMAP
#undef do_mmap
unsigned long do_mmap_flf(struct file * file, unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long off, char*filename, int line, char*function)
{
unsigned long result;
printk("%s @%s:%d: do_mmap by pid %d of %lu", function, filename, line, current->pid, len);
result=do_mmap(file,addr,len,prot,flags,off);
if (result >= -4096) /* so to speak... */
printk(" = %lx\n", result);
else
if (is_in_rom(result))
printk("-%lu = %lx\n", len, result);
else
printk("+%lu+%lu+%lu = %lx\n",
ksize(result)-len,
ksize(current->mm->tblock.next),
ksize(current->mm->tblock.next->rblock),
result);
return result;
}
#endif /* DEBUG_MMAP */
#ifdef DEBUG
static void show_process_blocks(void)
{
struct mm_tblock_struct * tblock, *tmp;
printk("Process blocks %d:", current->pid);
tmp = ¤t->mm->tblock;
while (tmp) {
printk(" %p: %p", tmp, tmp->rblock);
if (tmp->rblock)
printk(" (%d @%p #%d)", ksize(tmp->rblock->kblock), tmp->rblock->kblock, tmp->rblock->refcount);
if (tmp->next)
printk(" ->");
else
printk(".");
tmp = tmp->next;
}
printk("\n");
}
#endif /* DEBUG */
extern unsigned long askedalloc, realalloc;
unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long off)
{
void * result;
struct mm_tblock_struct * tblock;
if ((flags & MAP_SHARED) && (prot & PROT_WRITE) && (file)) {
printk("MAP_SHARED not supported (cannot write mappings to disk)\n");
return -EINVAL;
}
if ((prot & PROT_WRITE) && (flags & MAP_PRIVATE)) {
printk("Private writable mappings not supported\n");
return -EINVAL;
}
/*
* determine the object being mapped and call the appropriate
* specific mapper.
*/
if (file) {
struct vm_area_struct vma;
int error;
if (!file->f_op)
return -ENODEV;
vma.vm_start = addr;
vma.vm_end = addr + len;
vma.vm_flags = vm_flags(prot,flags) /*| mm->def_flags*/;
if (file->f_mode & 1)
vma.vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
if (flags & MAP_SHARED) {
vma.vm_flags |= VM_SHARED | VM_MAYSHARE;
/*
* This looks strange, but when we don't have the file open
* for writing, we can demote the shared mapping to a simpler
* private mapping. That also takes care of a security hole
* with ptrace() writing to a shared mapping without write
* permissions.
*
* We leave the VM_MAYSHARE bit on, just to get correct output
* from /proc/xxx/maps..
*/
if (!(file->f_mode & 2))
vma.vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
}
vma.vm_offset = off;
#ifdef MAGIC_ROM_PTR
/* First, try simpler routine designed to give us a ROM pointer. */
if (file->f_op->romptr && !(prot & PROT_WRITE)) {
error = file->f_op->romptr(file->f_inode, file, &vma);
/*printk("romptr mmap returned %d /%x\n", error, vma.vm_start);*/
if (!error)
return vma.vm_start;
else if (error != -ENOSYS)
return error;
} else
#endif /* MAGIC_ROM_PTR */
/* Then try full mmap routine, which might return a RAM pointer,
or do something truly complicated. */
if (file->f_op->mmap) {
error = file->f_op->mmap(file->f_inode, file, &vma);
/*printk("mmap mmap returned %d /%x\n", error, vma.vm_start);*/
if (!error)
return vma.vm_start;
else if (error != -ENOSYS)
return error;
} else
return -ENODEV; /* No mapping operations defined */
/* An ENOSYS error indicates that mmap isn't possible (as opposed to
tried but failed) so we'll fall through to the copy. */
}
tblock = (struct mm_tblock_struct *)
kmalloc(sizeof(struct mm_tblock_struct), GFP_KERNEL);
if (!tblock) {
printk("Allocation of tblock for %lu byte allocation from process %d failed\n", len, current->pid);
show_buffers();
show_free_areas();
return -ENOMEM;
}
tblock->rblock = (struct mm_rblock_struct *)
kmalloc(sizeof(struct mm_rblock_struct), GFP_KERNEL);
if (!tblock->rblock) {
printk("Allocation of rblock for %lu byte allocation from process %d failed\n", len, current->pid);
show_buffers();
show_free_areas();
kfree(tblock);
return -ENOMEM;
}
result = kmalloc(len, GFP_KERNEL);
if (!result) {
printk("Allocation of length %lu from process %d failed\n", len, current->pid);
show_buffers();
show_free_areas();
kfree(tblock->rblock);
kfree(tblock);
return -ENOMEM;
}
tblock->rblock->refcount = 1;
tblock->rblock->kblock = result;
tblock->rblock->size = len;
realalloc += ksize(result);
askedalloc += len;
#ifdef WARN_ON_SLACK
if ((len+WARN_ON_SLACK) <= ksize(result))
printk("Allocation of %lu bytes from process %d has %lu bytes of slack\n", len, current->pid, ksize(result)-len);
#endif
if (file) {
int error;
int old_fs = get_fs();
set_fs(KERNEL_DS);
error = file->f_op->read(file->f_inode, file, (char*)result, len);
set_fs(old_fs);
if (error < 0) {
kfree(result);
kfree(tblock->rblock);
kfree(tblock);
return error;
}
if (error<len)
memset(result+error, '\0', len-error);
} else {
memset(result, '\0', len);
}
realalloc += ksize(tblock);
askedalloc += sizeof(struct mm_tblock_struct);
realalloc += ksize(tblock->rblock);
askedalloc += sizeof(struct mm_rblock_struct);
tblock->next = current->mm->tblock.next;
current->mm->tblock.next = tblock;
#ifdef DEBUG
printk("do_mmap:\n");
show_process_blocks();
#endif
return (unsigned long)result;
}
asmlinkage int sys_munmap(unsigned long addr, size_t len)
{
return do_munmap(addr, len);
}
#ifdef DEBUG_MMAP
#undef do_munmap
int do_munmap_flf(unsigned long addr, size_t len, char * filename, int line, char*function)
{
printk("do_munmap of %lx bytes at %x invoked from %s @%s:%d\n", ksize(addr), addr, function, filename, line);
return do_munmap(addr,len);
}
#endif
/*
* Munmap is split into 2 main parts -- this part which finds
* what needs doing, and the areas themselves, which do the
* work. This now handles partial unmappings.
* Jeremy Fitzhardine <jeremy@s...>
*/
int do_munmap(unsigned long addr, size_t len)
{
struct mm_tblock_struct * tblock, *tmp;
#ifdef MAGIC_ROM_PTR
/* For efficiency's sake, if the pointer is obviously in ROM,
don't bother walking the lists to free it */
if (is_in_rom(addr))
return 0;
#endif
#ifdef DEBUG
printk("do_munmap:\n");
#endif
tmp = ¤t->mm->tblock;
while ((tblock=tmp->next) && (tblock->rblock) && (tblock->rblock->kblock != (void*)addr))
tmp = tblock;
if (!tblock) {
printk("munmap of non-mmaped memory by process %d (%s): %p\n", current->pid, current->comm, (void*)addr);
return -EINVAL;
}
if(tblock->rblock)
if(!--tblock->rblock->refcount) {
if (tblock->rblock->kblock) {
realalloc -= ksize(tblock->rblock->kblock);
askedalloc -= tblock->rblock->size;
kfree(tblock->rblock->kblock);
}
realalloc -= ksize(tblock->rblock);
askedalloc -= sizeof(struct mm_rblock_struct);
kfree(tblock->rblock);
}
tmp->next = tblock->next;
realalloc -= ksize(tblock);
askedalloc -= sizeof(struct mm_tblock_struct);
kfree(tblock);
#ifdef DEBUG
show_process_blocks();
#endif
return -EINVAL;
}
/* Release all mmaps. */
void exit_mmap(struct mm_struct * mm)
{
struct mm_tblock_struct * tmp = &mm->tblock;
/*unsigned long flags;*/
if (!mm)
return;
if (mm->executable)
iput(mm->executable);
mm->executable = 0;
/*save_flags(flags); cli();*/
if (mm->count > 1) {
/*restore_flags(flags);*/
return;
}
#ifdef DEBUG
printk("Exit_mmap:\n");
#endif
while((tmp = tmp->next)) {
if (tmp->rblock) {
if (!--tmp->rblock->refcount) {
if (tmp->rblock->kblock) {
realalloc -= ksize(tmp->rblock->kblock);
askedalloc -= tmp->rblock->size;
kfree(tmp->rblock->kblock);
}
realalloc -= ksize(tmp->rblock);
askedalloc -= sizeof(struct mm_rblock_struct);
kfree(tmp->rblock);
}
tmp->rblock = 0;
}
mm->tblock.next = tmp->next;
realalloc -= ksize(tmp);
askedalloc -= sizeof(struct mm_tblock_struct);
kfree(tmp);
}
#ifdef DEBUG
show_process_blocks();
#endif
/*restore_flags(flags);*/
}
1.1 or1k/rc203soc/sw/uClinux/mmnommu/mprotect.c
http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/mprotect.c?rev=1.1&content-type=text/x-cvsweb-markup
Index: mprotect.c
===================================================================
/*
* linux/mm/mprotect.c
*
* (C) Copyright 1994 Linus Torvalds
*/
/*
* uClinux revisions for NO_MM
* Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>,
* The Silver Hammer Group, Ltd.
* Copyright (C) 1999 D. Jeff Dionne <jeff@u...>,
* Rt-Control, Inc.
*/
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h>
asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot)
{
return -ENOSYS;
}
1.1 or1k/rc203soc/sw/uClinux/mmnommu/mremap.c
http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/mremap.c?rev=1.1&content-type=text/x-cvsweb-markup
Index: mremap.c
===================================================================
/*
* linux/mm/remap.c
*
* (C) Copyright 1996 Linus Torvalds
*/
/*
* uClinux revisions for NO_MM
* Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>,
* The Silver Hammer Group, Ltd.
* Copyright (C) 1999 D. Jeff Dionne <jeff@u...>,
* Rt-Control, Inc.
*/
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/swap.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/pgtable.h>
/*
* FIXME: Could do a tradional realloc() in some cases.
*/
asmlinkage unsigned long sys_mremap(unsigned long addr,
unsigned long old_len, unsigned long new_len,
unsigned long flags)
{
return -ENOSYS;
}
1.1 or1k/rc203soc/sw/uClinux/mmnommu/page_alloc.c
http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/page_alloc.c?rev=1.1&content-type=text/x-cvsweb-markup
Index: page_alloc.c
===================================================================
/*
* linux/mm/page_alloc.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
* Swap reorganised 29.12.95, Stephen Tweedie
*/
/*
* uClinux revisions for NO_MM
* Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>,
* The Silver Hammer Group, Ltd.
* Copyright (C) 1999 D. Jeff Dionne <jeff@u...>,
* Rt-Control, Inc.
*/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/swap.h>
#include <linux/fs.h>
#include <linux/swapctl.h>
#include <linux/interrupt.h>
#include <asm/dma.h>
#include <asm/system.h> /* for cli()/sti() */
#include <asm/segment.h> /* for memcpy_to/fromfs */
#include <asm/bitops.h>
#include <asm/pgtable.h>
int nr_swap_pages = 0;
int nr_free_pages = 0;
extern struct wait_queue *buffer_wait;
/*
* Free area management
*
* The free_area_list arrays point to the queue heads of the free areas
* of different sizes
*/
#ifdef BIGALLOCS
/* SIMON: change of source file - te get mw working */
#define NR_MEM_LISTS 10
#else
#define NR_MEM_LISTS 7
#endif
/* The start of this MUST match the start of "struct page" */
struct free_area_struct {
struct page *next;
struct page *prev;
unsigned int * map;
};
#define memory_head(x) ((struct page *)(x))
static struct free_area_struct free_area[NR_MEM_LISTS];
static inline void init_mem_queue(struct free_area_struct * head)
{
head->next = memory_head(head);
head->prev = memory_head(head);
}
static inline void add_mem_queue(struct free_area_struct * head, struct page * entry)
{
struct page * next = head->next;
entry->prev = memory_head(head);
entry->next = next;
next->prev = entry;
head->next = entry;
}
static inline void remove_mem_queue(struct page * entry)
{
struct page * next = entry->next;
struct page * prev = entry->prev;
next->prev = prev;
prev->next = next;
}
/*
* Free_page() adds the page to the free lists. This is optimized for
* fast normal cases (no error jumps taken normally).
*
* The way to optimize jumps for gcc-2.2.2 is to:
* - select the "normal" case and put it inside the if () { XXX }
* - no else-statements if you can avoid them
*
* With the above two rules, you get a straight-line execution path
* for the normal case, giving better asm-code.
*
* free_page() may sleep since the page being freed may be a buffer
* page or present in the swap cache. It will not sleep, however,
* for a freshly allocated page (get_free_page()).
*/
/*
* Buddy system. Hairy. You really aren't expected to understand this
*
* Hint: -mask = 1+~mask
*/
static inline void free_pages_ok(unsigned long map_nr, unsigned long order)
{
struct free_area_struct *area = free_area + order;
unsigned long index = map_nr >> (1 + order);
unsigned long mask = (~0UL) << order;
unsigned long flags;
save_flags(flags);
cli();
#define list(x) (mem_map+(x))
map_nr &= mask;
nr_free_pages -= mask;
while (mask + (1 << (NR_MEM_LISTS-1))) {
if (!change_bit(index, area->map))
break;
remove_mem_queue(list(map_nr ^ -mask));
mask <<= 1;
area++;
index >>= 1;
map_nr &= mask;
}
add_mem_queue(area, list(map_nr));
#undef list
restore_flags(flags);
if (!waitqueue_active(&buffer_wait))
return;
wake_up(&buffer_wait);
}
#ifdef DEBUG_FREE_PAGES
#undef __free_page
void __free_page_flf(struct page *page, char*file, int line, char*function)
{
printk("Freeing page %p from %s @%s:%d\n", page, function, file, line);
__free_page(page);
}
#undef free_pages
void free_pages_flf(unsigned long addr, unsigned long order, char*file, int line, char*function)
{
printk("Freeing %lu byte page %lx from %s @%s:%d\n", 4096 << order, addr, function, file, line);
free_pages(addr, order);
}
#undef __get_free_pages
unsigned long __get_free_pages_flf(int priority, unsigned long order, int dma, char * file, int line, char * function)
{
printk("Allocating %d byte page from %s @%s:%d\n", 4096 << order, function, file, line);
return __get_free_pages(priority, order, dma);
}
#undef get_free_page
unsigned long get_free_page_flf(int priority, char * file, int line, char * function)
{
void * result = (void*)__get_free_pages_flf(priority, 0, 0, file, line, function);
if (result)
memset(result, 0, PAGE_SIZE);
return result;
}
#endif /* DEBUG_FREE_PAGES */
void __free_page(struct page *page)
{
if (!PageReserved(page) && atomic_dec_and_test(&page->count)) {
unsigned long map_nr = page->map_nr;
free_pages_ok(map_nr, 0);
}
}
void free_pages(unsigned long addr, unsigned long order)
{
unsigned long map_nr = MAP_NR(addr);
if (map_nr < MAP_NR(high_memory)) {
mem_map_t * map = mem_map + map_nr;
if (PageReserved(map))
return;
if (atomic_dec_and_test(&map->count)) {
free_pages_ok(map_nr, order);
return;
}
}
}
/*
* Some ugly macros to speed up __get_free_pages()..
*/
#define MARK_USED(index, order, area) \
change_bit((index) >> (1+(order)), (area)->map)
#define CAN_DMA(x) (PageDMA(x))
#define ADDRESS(x) (PAGE_OFFSET + ((x) << PAGE_SHIFT))
#define RMQUEUE(order, dma) \
do { struct free_area_struct * area = free_area+order; \
unsigned long new_order = order; \
do { struct page *prev = memory_head(area), *ret; \
while (memory_head(area) != (ret = prev->next)) { \
if (!dma || CAN_DMA(ret)) { \
unsigned long map_nr = ret->map_nr; \
(prev->next = ret->next)->prev = prev; \
MARK_USED(map_nr, new_order, area); \
nr_free_pages -= 1 << order; \
EXPAND(ret, map_nr, order, new_order, area); \
restore_flags(flags); \
return ADDRESS(map_nr); \
} \
prev = ret; \
} \
new_order++; area++; \
} while (new_order < NR_MEM_LISTS); \
} while (0)
#define EXPAND(map,index,low,high,area) \
do { unsigned long size = 1 << high; \
while (high > low) { \
area--; high--; size >>= 1; \
add_mem_queue(area, map); \
MARK_USED(index, high, area); \
index += size; \
map += size; \
} \
map->count = 1; \
map->age = PAGE_INITIAL_AGE; \
} while (0)
unsigned long __get_free_pages(int priority, unsigned long order, int dma)
{
unsigned long flags;
int reserved_pages;
if (order >= NR_MEM_LISTS)
return 0;
if (intr_count && priority != GFP_ATOMIC) {
static int count = 0;
if (++count < 5) {
printk("gfp called nonatomically from interrupt %p\n",
__builtin_return_address(0));
priority = GFP_ATOMIC;
}
}
reserved_pages = 5;
#ifndef CONFIG_REDUCED_MEMORY
if (priority != GFP_NFS)
reserved_pages = min_free_pages;
if ((priority == GFP_BUFFER || priority == GFP_IO) && reserved_pages >= 48)
reserved_pages -= (12 + (reserved_pages>>3));
#endif /* !CONFIG_REDUCED_MEMORY */
save_flags(flags);
repeat:
cli();
if ((priority==GFP_ATOMIC) || nr_free_pages > reserved_pages) {
RMQUEUE(order, dma);
restore_flags(flags);
#ifdef DEBUG
printk("fragmentation preventing allocation, re-attempting to free\n");
#endif
}
restore_flags(flags);
if (priority != GFP_BUFFER && try_to_free_page(priority, dma, 1))
goto repeat;
return 0;
}
/*
* Show free area list (used inside shift_scroll-lock stuff)
* We also calculate the percentage fragmentation. We do this by counting the
* memory on each free list with the exception of the first item on the list.
*/
/*
* That's as may be, but I added an explicit fragmentation percentage, just
* to make it obvious. -kja
*/
/* totals held by do_mmap to compute memory wastage */
unsigned long realalloc, askedalloc;
void show_free_areas(void)
{
unsigned long order, flags;
unsigned long total = 0;
unsigned long fragmented = 0;
unsigned long slack;
printk("Free pages: %6dkB\n ( ",nr_free_pages<<(PAGE_SHIFT-10));
save_flags(flags);
cli();
for (order=0 ; order < NR_MEM_LISTS; order++) {
struct page * tmp;
unsigned long nr = 0;
for (tmp = free_area[order].next ; tmp != memory_head(free_area+order) ; tmp = tmp->next) {
nr ++;
}
total += nr * ((PAGE_SIZE>>10) << order);
if ((nr > 1) && (order < (NR_MEM_LISTS-1)))
fragmented += (nr-1) * (1 << order);
printk("%lu*%lukB ", nr, (PAGE_SIZE>>10) << order);
}
restore_flags(flags);
fragmented *= 100;
fragmented /= nr_free_pages;
if (realalloc)
slack = (realalloc-askedalloc) * 100 / realalloc;
else
slack = 0;
printk("= %lukB, %%%lu frag, %%%lu slack)\n", total, fragmented, slack);
#ifdef SWAP_CACHE_INFO
show_swap_cache_info();
#endif
}
#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
/*
* set up the free-area data structures:
* - mark all pages reserved
* - mark all memory queues empty
* - clear the memory bitmaps
*/
unsigned long free_area_init(unsigned long start_mem, unsigned long end_mem)
{
mem_map_t * p;
unsigned long mask = PAGE_MASK;
int i;
/*
* select nr of pages we try to keep free for important stuff
* with a minimum of 48 pages. This is totally arbitrary
*/
i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+7);
if (i < 24)
i = 24;
i += 24; /* The limit for buffer pages in __get_free_pages is
* decreased by 12+(i>>3) */
min_free_pages = i;
free_pages_low = i + (i>>1);
free_pages_high = i + i;
mem_map = (mem_map_t *) start_mem;
p = mem_map + MAP_NR(end_mem);
start_mem = LONG_ALIGN((unsigned long) p);
memset(mem_map, 0, start_mem - (unsigned long) mem_map);
do {
--p;
p->flags = (1 << PG_DMA) | (1 << PG_reserved);
p->map_nr = p - mem_map;
} while (p > mem_map);
for (i = 0 ; i < NR_MEM_LISTS ; i++) {
unsigned long bitmap_size;
init_mem_queue(free_area+i);
mask += mask;
end_mem = (end_mem + ~mask) & mask;
bitmap_size = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT + i);
bitmap_size = (bitmap_size + 7) >> 3;
bitmap_size = LONG_ALIGN(bitmap_size);
free_area[i].map = (unsigned int *) start_mem;
memset((void *) start_mem, 0, bitmap_size);
start_mem += bitmap_size;
}
return start_mem;
}
1.1 or1k/rc203soc/sw/uClinux/mmnommu/swap.c
http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/swap.c?rev=1.1&content-type=text/x-cvsweb-markup
Index: swap.c
===================================================================
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* DJD: This file should go away for uClinux...
* Need to look for dependancies.
*/
/*
* This file should contain most things doing the swapping from/to disk.
* Started 18.12.91
*
* Swap aging added 23.2.95, Stephen Tweedie.
*/
/*
* uClinux revisions for NO_MM
* Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>,
* The Silver Hammer Group, Ltd.
* Copyright (C) 1999 D. Jeff Dionne <jeff@u...>,
* Rt-Control, Inc.
*/
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/swap.h>
#include <linux/fs.h>
#include <linux/swapctl.h>
#include <linux/pagemap.h>
#include <asm/dma.h>
#include <asm/system.h> /* for cli()/sti() */
#include <asm/segment.h> /* for memcpy_to/fromfs */
#include <asm/bitops.h>
#include <asm/pgtable.h>
/*
* We identify three levels of free memory. We never let free mem
* fall below the min_free_pages except for atomic allocations. We
* start background swapping if we fall below free_pages_high free
* pages, and we begin intensive swapping below free_pages_low.
*
* Keep these three variables contiguous for sysctl(2).
*/
int min_free_pages = 20;
int free_pages_low = 30;
int free_pages_high = 40;
/* We track the number of pages currently being asynchronously swapped
out, so that we don't try to swap TOO many pages out at once */
atomic_t nr_async_pages = 0;
/*
* Constants for the page aging mechanism: the maximum age (actually,
* the maximum "youthfulness"); the quanta by which pages rejuvenate
* and age; and the initial age for new pages.
*/
swap_control_t swap_control = {
20, 3, 1, 3, /* Page aging */
10, 2, 2, 4, /* Buffer aging */
32, 4, /* Aging cluster */
8192, 8192, /* Pageout and bufferout weights */
-200, /* Buffer grace */
1, 1, /* Buffs/pages to free */
RCL_ROUND_ROBIN /* Balancing policy */
};
swapstat_t swapstats = {0};
/* General swap control */
/* Parse the kernel command line "swap=" option at load time: */
void swap_setup(char *str, int *ints)
{
}
/* Parse the kernel command line "buff=" option at load time: */
void buff_setup(char *str, int *ints)
{
}
1.1 or1k/rc203soc/sw/uClinux/mmnommu/swapfile.c
http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/swapfile.c?rev=1.1&content-type=text/x-cvsweb-markup
Index: swapfile.c
===================================================================
/*
* linux/mm/swapfile.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
* Swap reorganised 29.12.95, Stephen Tweedie
*
* uClinux revisions for NO_MM
* Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>,
* The Silver Hammer Group, Ltd.
* Copyright (C) 1999 D. Jeff Dionne
* Rt-Control, Inc.
*/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/swap.h>
#include <linux/fs.h>
#include <linux/swapctl.h>
#include <linux/blkdev.h> /* for blk_size */
#include <linux/shm.h>
/*
* Compatibility functions mostly
*/
asmlinkage int sys_swapoff(const char * specialfile)
{
return -ENOSYS;
}
asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
{
return -ENOSYS;
}
void si_swapinfo(struct sysinfo *val)
{
val->freeswap = val->totalswap = 0;
return;
}
1.1 or1k/rc203soc/sw/uClinux/mmnommu/vmalloc.c
http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/vmalloc.c?rev=1.1&content-type=text/x-cvsweb-markup
Index: vmalloc.c
===================================================================
/*
* linux/mm/vmalloc.c
*
* Copyright (C) 1993 Linus Torvalds
*/
/*
* uClinux revisions for NO_MM
* Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>,
* The Silver Hammer Group, Ltd.
* Copyright (C) 1999 D. Jeff Dionne <jeff@u...>,
* Rt-Control, Inc.
*/
#include <asm/system.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/malloc.h>
#include <linux/mm.h>
/*
* These routines just punt in a flat address space
*/
void vfree(void * addr)
{
kfree(addr);
}
void * vmalloc(unsigned long size)
{
return kmalloc(size, GFP_KERNEL);
}
/*
* In a flat address space, there is no translation needed
*/
void * vremap(unsigned long offset, unsigned long size)
{
return (void*)offset;
}
int vread(char *buf, char *addr, int count)
{
memcpy_tofs(buf, addr, count);
return count;
}
1.1 or1k/rc203soc/sw/uClinux/mmnommu/vmscan.c
http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/mmnommu/vmscan.c?rev=1.1&content-type=text/x-cvsweb-markup
Index: vmscan.c
===================================================================
/*
* linux/mm/vmscan.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*
* Swap reorganised 29.12.95, Stephen Tweedie.
* kswapd added: 7.1.96 sct
* Version: $Id: vmscan.c,v 1.1 2005/12/20 11:47:06 jcastillo Exp $
*/
/*
* uClinux revisions for NO_MM
* Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>,
* The Silver Hammer Group, Ltd.
* Copyright (C) 1998, 1999 D. Jeff Dionne <jeff@u...>,
* Rt-Control, Inc.
*/
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/swap.h>
#include <linux/fs.h>
#include <linux/swapctl.h>
#include <linux/pagemap.h>
#include <linux/smp_lock.h>
/*
* We can't swap, so all we can do is shrink mmap.
*/
int try_to_free_page(int priority, int dma, int wait)
{
int i=6;
int stop, can_do_io;
#ifdef DEBUG
printk("trying to find free page within pid %d...\n", current->pid);
#endif
/* we don't try as hard if we're not waiting.. */
stop = 3;
can_do_io = 1;
if (wait)
stop = 0;
if (priority == GFP_IO)
can_do_io = 0;
do {
#ifdef DEBUG
printk("Attempting to shrink_mmap\n");
#endif
if (shrink_mmap(i, dma, can_do_io))
return 1;
i--;
} while ((i - stop) >= 0);
printk("Failed to free page\n");
return 0;
}
/*
* In case someone wants to re-implement swapping...
*/
void init_swap_timer(void)
{
}
|
 |