add directory kernel

This commit is contained in:
gohigh
2024-02-19 00:24:53 -05:00
parent eec934fe6c
commit a4964ba92d
749 changed files with 100620 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
CC =gcc
CFLAGS =-O -Wall -fstrength-reduce -fomit-frame-pointer \
-finline-functions -nostdinc -I../include
AS =as
AR =ar
LD =ld
CPP =gcc -E -nostdinc -I../include
.c.o:
$(CC) $(CFLAGS) \
-c -o $*.o $<
.s.o:
$(AS) -o $*.o $<
.c.s:
$(CC) $(CFLAGS) \
-S -o $*.s $<
OBJS = memory.o swap.o
all: mm.o
mm.o: $(OBJS)
$(LD) -r -o mm.o $(OBJS)
clean:
rm -f core *.o *.a tmp_make
for i in *.c;do rm -f `basename $$i .c`.s;done
dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
(for i in *.c;do $(CPP) -M $$i;done) >> tmp_make
cp tmp_make Makefile
### Dependencies:
memory.o : memory.c ../include/signal.h ../include/sys/types.h \
../include/asm/system.h ../include/linux/sched.h ../include/linux/head.h \
../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h
swap.o : swap.c ../include/string.h ../include/errno.h \
../include/linux/mm.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/kernel.h ../include/signal.h ../include/sys/stat.h \
../include/linux/sched.h ../include/linux/head.h ../include/sys/param.h \
../include/sys/time.h ../include/time.h ../include/sys/resource.h

View File

@@ -0,0 +1,583 @@
/*
* linux/mm/memory.c
*
* (C) 1991 Linus Torvalds
*/
/*
* demand-loading started 01.12.91 - seems it is high on the list of
* things wanted, and it should be easy to implement. - Linus
*/
/*
* Ok, demand-loading was easy, shared pages a little bit tricker. Shared
* pages started 02.12.91, seems to work. - Linus.
*
* Tested sharing by executing about 30 /bin/sh: under the old kernel it
* would have taken more than the 6M I have free, but it worked well as
* far as I could see.
*
* Also corrected some "invalidate()"s - I wasn't doing enough of them.
*/
/*
* Real VM (paging to/from disk) started 18.12.91. Much more work and
* thought has to go into this. Oh, well..
* 19.12.91 - works, somewhat. Sometimes I get faults, don't know why.
* Found it. Everything seems to work now.
* 20.12.91 - Ok, making the swap-device changeable like the root.
*/
#include <signal.h>
#include <asm/system.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \
current->start_code + current->end_code)
unsigned long HIGH_MEMORY = 0;
#define copy_page(from,to) \
__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")
#define CHECK_LAST_NR 16
static unsigned long last_pages[CHECK_LAST_NR] = { 0, };
unsigned char mem_map [ PAGING_PAGES ] = {0,};
/*
* Free a page of memory at physical address 'addr'. Used by
* 'free_page_tables()'
*/
void free_page(unsigned long addr)
{
if (addr < LOW_MEM) return;
if (addr < HIGH_MEMORY) {
addr -= LOW_MEM;
addr >>= 12;
if (mem_map[addr]--)
return;
mem_map[addr]=0;
}
printk("trying to free free page: memory probably corrupted");
}
/*
* This function frees a continuos block of page tables, as needed
* by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks.
*/
int free_page_tables(unsigned long from,unsigned long size)
{
unsigned long page;
unsigned long page_dir;
unsigned long *pg_table;
unsigned long * dir, nr;
if (from & 0x3fffff)
panic("free_page_tables called with wrong alignment");
if (!from)
panic("Trying to free up swapper memory space");
size = (size + 0x3fffff) >> 22;
dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
for ( ; size-->0 ; dir++) {
if (!(page_dir = *dir))
continue;
*dir = 0;
if (!(page_dir & 1)) {
printk("free_page_tables: bad page directory.");
continue;
}
pg_table = (unsigned long *) (0xfffff000 & page_dir);
for (nr=0 ; nr<1024 ; nr++,pg_table++) {
if (!(page = *pg_table))
continue;
*pg_table = 0;
if (1 & page)
free_page(0xfffff000 & page);
else
swap_free(page >> 1);
}
free_page(0xfffff000 & page_dir);
}
invalidate();
for (page = 0; page < CHECK_LAST_NR ; page++)
last_pages[page] = 0;
return 0;
}
/*
* Well, here is one of the most complicated functions in mm. It
* copies a range of linerar addresses by copying only the pages.
* Let's hope this is bug-free, 'cause this one I don't want to debug :-)
*
* Note! We don't copy just any chunks of memory - addresses have to
* be divisible by 4Mb (one page-directory entry), as this makes the
* function easier. It's used only by fork anyway.
*
* NOTE 2!! When from==0 we are copying kernel space for the first
* fork(). Then we DONT want to copy a full page-directory entry, as
* that would lead to some serious memory waste - we just copy the
* first 160 pages - 640kB. Even that is more than we need, but it
* doesn't take any more memory - we don't copy-on-write in the low
* 1 Mb-range, so the pages can be shared with the kernel. Thus the
* special case for nr=xxxx.
*/
int copy_page_tables(unsigned long from,unsigned long to,long size)
{
unsigned long * from_page_table;
unsigned long * to_page_table;
unsigned long this_page;
unsigned long * from_dir, * to_dir;
unsigned long new_page;
unsigned long nr;
if ((from&0x3fffff) || (to&0x3fffff))
panic("copy_page_tables called with wrong alignment");
from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
to_dir = (unsigned long *) ((to>>20) & 0xffc);
size = ((unsigned) (size+0x3fffff)) >> 22;
for( ; size-->0 ; from_dir++,to_dir++) {
if (*to_dir)
printk("copy_page_tables: already exist, "
"probable memory corruption\n");
if (!*from_dir)
continue;
if (!(1 & *from_dir)) {
printk("copy_page_tables: page table swapped out, "
"probable memory corruption");
*from_dir = 0;
continue;
}
from_page_table = (unsigned long *) (0xfffff000 & *from_dir);
if (!(to_page_table = (unsigned long *) get_free_page()))
return -1; /* Out of memory, see freeing */
*to_dir = ((unsigned long) to_page_table) | 7;
nr = (from==0)?0xA0:1024;
for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
this_page = *from_page_table;
if (!this_page)
continue;
if (!(1 & this_page)) {
if (!(new_page = get_free_page()))
return -1;
read_swap_page(this_page>>1, (char *) new_page);
*to_page_table = this_page;
*from_page_table = new_page | (PAGE_DIRTY | 7);
continue;
}
this_page &= ~2;
*to_page_table = this_page;
if (this_page > LOW_MEM) {
*from_page_table = this_page;
this_page -= LOW_MEM;
this_page >>= 12;
mem_map[this_page]++;
}
}
}
invalidate();
return 0;
}
/*
* This function puts a page in memory at the wanted address.
* It returns the physical address of the page gotten, 0 if
* out of memory (either when trying to access page-table or
* page.)
*/
static unsigned long put_page(unsigned long page,unsigned long address)
{
unsigned long tmp, *page_table;
/* NOTE !!! This uses the fact that _pg_dir=0 */
if (page < LOW_MEM || page >= HIGH_MEMORY) {
printk("put_page: trying to put page %p at %p\n",page,address);
return 0;
}
if (mem_map[(page-LOW_MEM)>>12] != 1) {
printk("mem_map disagrees with %p at %p\n",page,address);
return 0;
}
page_table = (unsigned long *) ((address>>20) & 0xffc);
if ((*page_table)&1)
page_table = (unsigned long *) (0xfffff000 & *page_table);
else {
if (!(tmp=get_free_page()))
return 0;
*page_table = tmp | 7;
page_table = (unsigned long *) tmp;
}
page_table[(address>>12) & 0x3ff] = page | 7;
/* no need for invalidate */
return page;
}
/*
* The previous function doesn't work very well if you also want to mark
* the page dirty: exec.c wants this, as it has earlier changed the page,
* and we want the dirty-status to be correct (for VM). Thus the same
* routine, but this time we mark it dirty too.
*/
unsigned long put_dirty_page(unsigned long page, unsigned long address)
{
unsigned long tmp, *page_table;
/* NOTE !!! This uses the fact that _pg_dir=0 */
if (page < LOW_MEM || page >= HIGH_MEMORY)
printk("put_dirty_page: trying to put page %p at %p\n",page,address);
if (mem_map[(page-LOW_MEM)>>12] != 1)
printk("mem_map disagrees with %p at %p\n",page,address);
page_table = (unsigned long *) ((address>>20) & 0xffc);
if ((*page_table)&1)
page_table = (unsigned long *) (0xfffff000 & *page_table);
else {
if (!(tmp=get_free_page()))
return 0;
*page_table = tmp|7;
page_table = (unsigned long *) tmp;
}
page_table[(address>>12) & 0x3ff] = page | (PAGE_DIRTY | 7);
/* no need for invalidate */
return page;
}
void un_wp_page(unsigned long * table_entry)
{
unsigned long old_page;
unsigned long new_page = 0;
unsigned long dirty;
repeat:
old_page = *table_entry;
dirty = old_page & PAGE_DIRTY;
if (!(old_page & 1)) {
if (new_page)
free_page(new_page);
return;
}
old_page &= 0xfffff000;
if (old_page >= HIGH_MEMORY) {
if (new_page)
free_page(new_page);
printk("bad page address\n\r");
do_exit(SIGSEGV);
}
if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {
*table_entry |= 2;
invalidate();
if (new_page)
free_page(new_page);
return;
}
if (!new_page) {
if (!(new_page=get_free_page()))
oom();
goto repeat;
}
copy_page(old_page,new_page);
*table_entry = new_page | dirty | 7;
free_page(old_page);
invalidate();
}
/*
* This routine handles present pages, when users try to write
* to a shared page. It is done by copying the page to a new address
* and decrementing the shared-page counter for the old page.
*
* If it's in code space we exit with a segment error.
*/
void do_wp_page(unsigned long error_code,unsigned long address)
{
if (address < TASK_SIZE)
printk("\n\rBAD! KERNEL MEMORY WP-ERR!\n\r");
if (address - current->start_code >= TASK_SIZE) {
printk("Bad things happen: page error in do_wp_page\n\r");
do_exit(SIGSEGV);
}
un_wp_page((unsigned long *)
(((address>>10) & 0xffc) + (0xfffff000 &
*((unsigned long *) ((address>>20) &0xffc)))));
}
void write_verify(unsigned long address)
{
unsigned long page;
if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1))
return;
page &= 0xfffff000;
page += ((address>>10) & 0xffc);
if ((3 & *(unsigned long *) page) == 1) /* non-writeable, present */
un_wp_page((unsigned long *) page);
return;
}
void get_empty_page(unsigned long address)
{
unsigned long tmp;
if (!(tmp=get_free_page()) || !put_page(tmp,address)) {
free_page(tmp); /* 0 is ok - ignored */
oom();
}
}
/*
* try_to_share() checks the page at address "address" in the task "p",
* to see if it exists, and if it is clean. If so, share it with the current
* task.
*
* NOTE! This assumes we have checked that p != current, and that they
* share the same executable or library.
*/
static int try_to_share(unsigned long address, struct task_struct * p)
{
unsigned long from;
unsigned long to;
unsigned long from_page;
unsigned long to_page;
unsigned long phys_addr;
from_page = to_page = ((address>>20) & 0xffc);
from_page += ((p->start_code>>20) & 0xffc);
to_page += ((current->start_code>>20) & 0xffc);
/* is there a page-directory at from? */
from = *(unsigned long *) from_page;
if (!(from & 1))
return 0;
from &= 0xfffff000;
from_page = from + ((address>>10) & 0xffc);
phys_addr = *(unsigned long *) from_page;
/* is the page clean and present? */
if ((phys_addr & 0x41) != 0x01)
return 0;
phys_addr &= 0xfffff000;
if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM)
return 0;
to = *(unsigned long *) to_page;
if (!(to & 1)) {
if (to = get_free_page())
*(unsigned long *) to_page = to | 7;
else
oom();
}
to &= 0xfffff000;
to_page = to + ((address>>10) & 0xffc);
if (1 & *(unsigned long *) to_page)
panic("try_to_share: to_page already exists");
/* share them: write-protect */
*(unsigned long *) from_page &= ~2;
*(unsigned long *) to_page = *(unsigned long *) from_page;
invalidate();
phys_addr -= LOW_MEM;
phys_addr >>= 12;
mem_map[phys_addr]++;
return 1;
}
/*
* share_page() tries to find a process that could share a page with
* the current one. Address is the address of the wanted page relative
* to the current data space.
*
* We first check if it is at all feasible by checking executable->i_count.
* It should be >1 if there are other tasks sharing this inode.
*/
static int share_page(struct inode * inode, unsigned long address)
{
struct task_struct ** p;
if (inode->i_count < 2 || !inode)
return 0;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
if (!*p)
continue;
if (current == *p)
continue;
if (address < LIBRARY_OFFSET) {
if (inode != (*p)->executable)
continue;
} else {
if (inode != (*p)->library)
continue;
}
if (try_to_share(address,*p))
return 1;
}
return 0;
}
void do_no_page(unsigned long error_code,
unsigned long address, struct task_struct *tsk)
{
static unsigned int last_checked = 0;
int nr[4];
unsigned long tmp;
unsigned long page;
int block,i;
struct inode * inode;
/* Trashing ? Make it interruptible, but don't penalize otherwise */
for (i = 0; i < CHECK_LAST_NR; i++)
if ((address & 0xfffff000) == last_pages[i]) {
current->counter = 0;
schedule();
}
last_checked++;
if (last_checked >= CHECK_LAST_NR)
last_checked = 0;
last_pages[last_checked] = address & 0xfffff000;
if (address < TASK_SIZE)
printk("\n\rBAD!! KERNEL PAGE MISSING\n\r");
if (address - tsk->start_code >= TASK_SIZE) {
printk("Bad things happen: nonexistent page error in do_no_page\n\r");
do_exit(SIGSEGV);
}
page = *(unsigned long *) ((address >> 20) & 0xffc);
/* check the page directory: make a page dir entry if no such exists */
if (page & 1) {
page &= 0xfffff000;
page += (address >> 10) & 0xffc;
tmp = *(unsigned long *) page;
if (tmp && !(1 & tmp)) {
swap_in((unsigned long *) page);
return;
}
} else {
if (page)
printk("do_no_page: bad page directory\n");
if (!(page = get_free_page()))
oom();
page |= 7;
*(unsigned long *) ((address >> 20) & 0xffc) = page;
}
address &= 0xfffff000;
tmp = address - tsk->start_code;
if (tmp >= LIBRARY_OFFSET ) {
inode = tsk->library;
block = 1 + (tmp-LIBRARY_OFFSET) / BLOCK_SIZE;
} else if (tmp < tsk->end_data) {
inode = tsk->executable;
block = 1 + tmp / BLOCK_SIZE;
} else {
inode = NULL;
block = 0;
}
if (!inode) {
get_empty_page(address);
return;
}
if (tsk == current)
if (share_page(inode,tmp))
return;
if (!(page = get_free_page()))
oom();
/* remember that 1 block is used for header */
for (i=0 ; i<4 ; block++,i++)
nr[i] = bmap(inode,block);
bread_page(page,inode->i_dev,nr);
i = tmp + 4096 - tsk->end_data;
if (i>4095)
i = 0;
tmp = page + 4096;
while (i-- > 0) {
tmp--;
*(char *)tmp = 0;
}
if (put_page(page,address))
return;
free_page(page);
oom();
}
void mem_init(long start_mem, long end_mem)
{
int i;
swap_device = 0;
swap_file = NULL;
HIGH_MEMORY = end_mem;
for (i=0 ; i<PAGING_PAGES ; i++)
mem_map[i] = USED;
i = MAP_NR(start_mem);
end_mem -= start_mem;
end_mem >>= 12;
while (end_mem-->0)
mem_map[i++]=0;
}
void show_mem(void)
{
int i,j,k,free=0,total=0;
int shared=0;
unsigned long * pg_tbl;
printk("Mem-info:\n\r");
for(i=0 ; i<PAGING_PAGES ; i++) {
if (mem_map[i] == USED)
continue;
total++;
if (!mem_map[i])
free++;
else
shared += mem_map[i]-1;
}
printk("%d free pages of %d\n\r",free,total);
printk("%d pages shared\n\r",shared);
k = 0;
for(i=4 ; i<1024 ;) {
if (1&pg_dir[i]) {
if (pg_dir[i]>HIGH_MEMORY) {
printk("page directory[%d]: %08X\n\r",
i,pg_dir[i]);
i++;
continue;
}
if (pg_dir[i]>LOW_MEM)
free++,k++;
pg_tbl=(unsigned long *) (0xfffff000 & pg_dir[i]);
for(j=0 ; j<1024 ; j++)
if ((pg_tbl[j]&1) && pg_tbl[j]>LOW_MEM)
if (pg_tbl[j]>HIGH_MEMORY)
printk("page_dir[%d][%d]: %08X\n\r",
i,j, pg_tbl[j]);
else
k++,free++;
}
i++;
if (!(i&15) && k) {
k++,free++; /* one page/process for task_struct */
printk("Process %d: %d pages\n\r",(i>>4)-1,k);
k = 0;
}
}
printk("Memory found: %d (%d)\n\r",free-shared,total);
}
/* This routine handles page faults. It determines the address,
and the problem then passes it off to one of the appropriate
routines. */
void do_page_fault (unsigned long *esp, unsigned long error_code)
{
unsigned long address;
/* get the address */
__asm__ ("movl %%cr2,%0":"=r" (address));
if (!(error_code & 1)) {
do_no_page(error_code, address, current);
return;
} else {
do_wp_page(error_code, address);
return;
}
}

View File

@@ -0,0 +1,290 @@
/*
* linux/mm/swap.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This file should contain most things doing the swapping from/to disk.
* Started 18.12.91
*/
#include <string.h>
#include <errno.h>
#include <linux/mm.h>
#include <sys/stat.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#define SWAP_BITS (4096<<3)
#define bitop(name,op) \
static inline int name(char * addr,unsigned int nr) \
{ \
int __res; \
__asm__ __volatile__("bt" op " %1,%2; adcl $0,%0" \
:"=g" (__res) \
:"r" (nr),"m" (*(addr)),"0" (0)); \
return __res; \
}
bitop(bit,"")
bitop(setbit,"s")
bitop(clrbit,"r")
static char * swap_bitmap = NULL;
unsigned int swap_device = 0;
struct inode * swap_file = NULL;
void rw_swap_page(int rw, unsigned int nr, char * buf)
{
unsigned int zones[4];
int i;
if (swap_device) {
ll_rw_page(rw,swap_device,nr,buf);
return;
}
if (swap_file) {
nr <<= 2;
for (i = 0; i < 4; i++)
if (!(zones[i] = bmap(swap_file,nr++))) {
printk("rw_swap_page: bad swap file\n");
return;
}
ll_rw_swap_file(rw,swap_file->i_dev, zones,4,buf);
return;
}
printk("ll_swap_page: no swap file or device\n");
}
/*
* We never page the pages in task[0] - kernel memory.
* We page all other pages.
*/
#define FIRST_VM_PAGE (TASK_SIZE>>12)
#define LAST_VM_PAGE (1024*1024)
#define VM_PAGES (LAST_VM_PAGE - FIRST_VM_PAGE)
static int get_swap_page(void)
{
int nr;
if (!swap_bitmap)
return 0;
for (nr = 1; nr < SWAP_BITS ; nr++)
if (clrbit(swap_bitmap,nr))
return nr;
return 0;
}
void swap_free(int swap_nr)
{
if (!swap_nr)
return;
if (swap_bitmap && swap_nr < SWAP_BITS)
if (!setbit(swap_bitmap,swap_nr))
return;
printk("swap_free: swap-space bitmap bad\n");
return;
}
void swap_in(unsigned long *table_ptr)
{
int swap_nr;
unsigned long page;
if (!swap_bitmap) {
printk("Trying to swap in without swap bit-map");
return;
}
if (1 & *table_ptr) {
printk("trying to swap in present page\n\r");
return;
}
swap_nr = *table_ptr >> 1;
if (!swap_nr) {
printk("No swap page in swap_in\n\r");
return;
}
if (!(page = get_free_page()))
oom();
read_swap_page(swap_nr, (char *) page);
if (setbit(swap_bitmap,swap_nr))
printk("swapping in multiply from same page\n\r");
*table_ptr = page | (PAGE_DIRTY | 7);
}
int try_to_swap_out(unsigned long * table_ptr)
{
unsigned long page;
unsigned long swap_nr;
page = *table_ptr;
if (!(PAGE_PRESENT & page))
return 0;
if (page - LOW_MEM > PAGING_MEMORY)
return 0;
if (PAGE_DIRTY & page) {
page &= 0xfffff000;
if (mem_map[MAP_NR(page)] != 1)
return 0;
if (!(swap_nr = get_swap_page()))
return 0;
*table_ptr = swap_nr<<1;
invalidate();
write_swap_page(swap_nr, (char *) page);
free_page(page);
return 1;
}
page &= 0xfffff000;
*table_ptr = 0;
invalidate();
free_page(page);
return 1;
}
/*
* Go through the page tables, searching for a user page that
* we can swap out.
*/
int swap_out(void)
{
static int dir_entry = 1024;
static int page_entry = -1;
int counter = VM_PAGES;
int pg_table = 0;
repeat:
while (counter > 0) {
counter -= 1024;
dir_entry++;
if (dir_entry >= 1024)
dir_entry = FIRST_VM_PAGE>>10;
if (pg_table = pg_dir[dir_entry])
break;
}
if (counter <= 0) {
printk("Out of swap-memory\n");
return 0;
}
if (!(pg_table & 1)) {
printk("bad page-table at pg_dir[%d]: %08x\n\r",dir_entry,
pg_table);
return 0;
}
pg_table &= 0xfffff000;
while (counter > 0) {
counter--;
page_entry++;
if (page_entry >= 1024) {
page_entry = -1;
goto repeat;
}
if (try_to_swap_out(page_entry + (unsigned long *) pg_table))
return 1;
}
printk("Out of swap-memory\n\r");
return 0;
}
/*
* Get physical address of first (actually last :-) free page, and mark it
* used. If no free pages left, return 0.
*/
unsigned long get_free_page(void)
{
unsigned long result;
repeat:
__asm__("std ; repne ; scasb\n\t"
"jne 1f\n\t"
"movb $1,1(%%edi)\n\t"
"sall $12,%%ecx\n\t"
"addl %2,%%ecx\n\t"
"movl %%ecx,%%edx\n\t"
"movl $1024,%%ecx\n\t"
"leal 4092(%%edx),%%edi\n\t"
"rep ; stosl\n\t"
"movl %%edx,%%eax\n"
"1:\tcld"
:"=a" (result)
:"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
"D" (mem_map+PAGING_PAGES-1)
:"di","cx","dx");
if (result >= HIGH_MEMORY)
goto repeat;
if ((result && result < LOW_MEM) || (result & 0xfff)) {
printk("weird result: %08x\n",result);
result = 0;
}
if (!result && swap_out())
goto repeat;
return result;
}
/*
* Written 01/25/92 by Simmule Turner, heavily changed by Linus.
*
* The swapon system call
*/
int sys_swapon(const char * specialfile)
{
struct inode * swap_inode;
int i,j;
if (!suser())
return -EPERM;
if (!(swap_inode = namei(specialfile)))
return -ENOENT;
if (swap_file || swap_device || swap_bitmap) {
iput(swap_inode);
return -EBUSY;
}
if (S_ISBLK(swap_inode->i_mode)) {
swap_device = swap_inode->i_rdev;
iput(swap_inode);
} else if (S_ISREG(swap_inode->i_mode))
swap_file = swap_inode;
else {
iput(swap_inode);
return -EINVAL;
}
swap_bitmap = (char *) get_free_page();
if (!swap_bitmap) {
iput(swap_file);
swap_device = 0;
swap_file = NULL;
printk("Unable to start swapping: out of memory :-)\n");
return -ENOMEM;
}
read_swap_page(0,swap_bitmap);
if (strncmp("SWAP-SPACE",swap_bitmap+4086,10)) {
printk("Unable to find swap-space signature\n\r");
free_page((long) swap_bitmap);
iput(swap_file);
swap_device = 0;
swap_file = NULL;
swap_bitmap = NULL;
return -EINVAL;
}
memset(swap_bitmap+4086,0,10);
j = 0;
for (i = 1 ; i < SWAP_BITS ; i++)
if (bit(swap_bitmap,i))
j++;
if (!j) {
printk("Empty swap-file\n");
free_page((long) swap_bitmap);
iput(swap_file);
swap_device = 0;
swap_file = NULL;
swap_bitmap = NULL;
return -EINVAL;
}
printk("Adding Swap: %d pages (%d bytes) swap-space\n\r",j,j*4096);
return 0;
}