summaryrefslogtreecommitdiffstats
path: root/mm/swapfile.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-09-19 19:15:08 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-09-19 19:15:08 +0000
commit03ba4131783cc9e872f8bb26a03f15bc11f27564 (patch)
tree88db8dba75ae06ba3bad08e42c5e52efc162535c /mm/swapfile.c
parent257730f99381dd26e10b832fce4c94cae7ac1176 (diff)
- Merge with Linux 2.1.121.
- Bugfixes.
Diffstat (limited to 'mm/swapfile.c')
-rw-r--r--mm/swapfile.c205
1 files changed, 141 insertions, 64 deletions
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 45f73de02..b7446b3b5 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -9,7 +9,6 @@
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/sched.h>
-#include <linux/head.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
@@ -22,6 +21,7 @@
#include <linux/blkdev.h> /* for blk_size */
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
+#include <linux/shm.h>
#include <asm/bitops.h>
#include <asm/pgtable.h>
@@ -116,10 +116,7 @@ unsigned long get_swap_page(void)
}
}
-/*
- * If the swap count overflows (swap_map[] == 127), the entry is considered
- * "permanent" and can't be reclaimed until the swap device is closed.
- */
+
void swap_free(unsigned long entry)
{
struct swap_info_struct * p;
@@ -147,7 +144,7 @@ void swap_free(unsigned long entry)
p->highest_bit = offset;
if (!p->swap_map[offset])
goto bad_free;
- if (p->swap_map[offset] < 127) {
+ if (p->swap_map[offset] < SWAP_MAP_MAX) {
if (!--p->swap_map[offset])
nr_swap_pages++;
}
@@ -299,44 +296,55 @@ static int try_to_unuse(unsigned int type)
{
struct swap_info_struct * si = &swap_info[type];
struct task_struct *p;
- unsigned long page = 0;
struct page *page_map;
- unsigned long entry;
+ unsigned long entry, page;
int i;
while (1) {
/*
* Find a swap page in use and read it in.
*/
- for (i = 1 , entry = 0; i < si->max ; i++) {
- if (si->swap_map[i] > 0 && si->swap_map[i] != 0x80) {
- entry = SWP_ENTRY(type, i);
- break;
+ for (i = 1; i < si->max ; i++) {
+ if (si->swap_map[i] > 0 && si->swap_map[i] != SWAP_MAP_BAD) {
+ goto found_entry;
}
}
- if (!entry)
- break;
+ break;
+
+ found_entry:
+ entry = SWP_ENTRY(type, i);
/* Get a page for the entry, using the existing swap
cache page if there is one. Otherwise, get a clean
page and read the swap into it. */
page_map = read_swap_cache(entry, 0);
- if (!page_map)
+ if (!page_map) {
+ /*
+ * Continue searching if the entry became unused.
+ */
+ if (si->swap_map[i] == 0)
+ continue;
return -ENOMEM;
+ }
page = page_address(page_map);
read_lock(&tasklist_lock);
for_each_task(p)
unuse_process(p->mm, entry, page);
read_unlock(&tasklist_lock);
+ shm_unuse(entry, page);
/* Now get rid of the extra reference to the temporary
page we've been using. */
if (PageSwapCache(page_map))
delete_from_swap_cache(page_map);
- free_page(page);
+ __free_page(page_map);
+ /*
+ * Check for and clear any overflowed swap map counts.
+ */
if (si->swap_map[i] != 0) {
- if (si->swap_map[i] != 127)
- printk("try_to_unuse: entry %08lx "
- "not in use\n", entry);
+ if (si->swap_map[i] != SWAP_MAP_MAX)
+ printk(KERN_ERR
+ "try_to_unuse: entry %08lx count=%d\n",
+ entry, si->swap_map[i]);
si->swap_map[i] = 0;
nr_swap_pages++;
}
@@ -377,10 +385,9 @@ asmlinkage int sys_swapoff(const char * specialfile)
prev = type;
}
err = -EINVAL;
- if (type < 0){
- dput(dentry);
- goto out;
- }
+ if (type < 0)
+ goto out_dput;
+
if (prev < 0) {
swap_list.head = p->next;
} else {
@@ -393,7 +400,6 @@ asmlinkage int sys_swapoff(const char * specialfile)
p->flags = SWP_USED;
err = try_to_unuse(type);
if (err) {
- dput(dentry);
/* re-insert swap space back into swap_list */
for (prev = -1, i = swap_list.head; i >= 0; prev = i, i = swap_info[i].next)
if (p->prio >= swap_info[i].prio)
@@ -404,7 +410,7 @@ asmlinkage int sys_swapoff(const char * specialfile)
else
swap_info[prev].next = p - swap_info;
p->flags = SWP_WRITEOK;
- goto out;
+ goto out_dput;
}
if(p->swap_device){
memset(&filp, 0, sizeof(filp));
@@ -419,16 +425,19 @@ asmlinkage int sys_swapoff(const char * specialfile)
}
dput(dentry);
- nr_swap_pages -= p->pages;
- dput(p->swap_file);
+ dentry = p->swap_file;
p->swap_file = NULL;
+ nr_swap_pages -= p->pages;
p->swap_device = 0;
vfree(p->swap_map);
p->swap_map = NULL;
- free_page((long) p->swap_lockmap);
+ vfree(p->swap_lockmap);
p->swap_lockmap = NULL;
p->flags = 0;
err = 0;
+
+out_dput:
+ dput(dentry);
out:
unlock_kernel();
return err;
@@ -458,7 +467,7 @@ int get_swaparea_info(char *buf)
usedswap = 0;
for (j = 0; j < ptr->max; ++j)
switch (ptr->swap_map[j]) {
- case 128:
+ case SWAP_MAP_BAD:
case 0:
continue;
default:
@@ -486,7 +495,12 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
int error = -EPERM;
struct file filp;
static int least_priority = 0;
-
+ union swap_header *swap_header = 0;
+ int swap_header_version;
+ int lock_map_size = PAGE_SIZE;
+ int nr_good_pages = 0;
+ unsigned long tmp_lock_map = 0;
+
lock_kernel();
if (!capable(CAP_SYS_ADMIN))
goto out;
@@ -547,54 +561,114 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
}
} else if (!S_ISREG(swap_dentry->d_inode->i_mode))
goto bad_swap;
- p->swap_lockmap = (unsigned char *) get_free_page(GFP_USER);
- if (!p->swap_lockmap) {
+ swap_header = (void *) __get_free_page(GFP_USER);
+ if (!swap_header) {
printk("Unable to start swapping: out of memory :-)\n");
error = -ENOMEM;
goto bad_swap;
}
- rw_swap_page_nocache(READ, SWP_ENTRY(type,0), (char *) p->swap_lockmap);
- if (memcmp("SWAP-SPACE",p->swap_lockmap+PAGE_SIZE-10,10)) {
+
+ p->swap_lockmap = (char *) &tmp_lock_map;
+ rw_swap_page_nocache(READ, SWP_ENTRY(type,0), (char *) swap_header);
+ p->swap_lockmap = NULL;
+
+ if (!memcmp("SWAP-SPACE",swap_header->magic.magic,10))
+ swap_header_version = 1;
+ else if (!memcmp("SWAPSPACE2",swap_header->magic.magic,10))
+ swap_header_version = 2;
+ else {
printk("Unable to find swap-space signature\n");
error = -EINVAL;
goto bad_swap;
}
- memset(p->swap_lockmap+PAGE_SIZE-10,0,10);
- j = 0;
- p->lowest_bit = 0;
- p->highest_bit = 0;
- for (i = 1 ; i < 8*PAGE_SIZE ; i++) {
- if (test_bit(i,p->swap_lockmap)) {
- if (!p->lowest_bit)
- p->lowest_bit = i;
- p->highest_bit = i;
- p->max = i+1;
- j++;
+
+ switch (swap_header_version) {
+ case 1:
+ memset(((char *) swap_header)+PAGE_SIZE-10,0,10);
+ j = 0;
+ p->lowest_bit = 0;
+ p->highest_bit = 0;
+ for (i = 1 ; i < 8*PAGE_SIZE ; i++) {
+ if (test_bit(i,(char *) swap_header)) {
+ if (!p->lowest_bit)
+ p->lowest_bit = i;
+ p->highest_bit = i;
+ p->max = i+1;
+ j++;
+ }
+ }
+ nr_good_pages = j;
+ p->swap_map = vmalloc(p->max * sizeof(short));
+ if (!p->swap_map) {
+ error = -ENOMEM;
+ goto bad_swap;
+ }
+ for (i = 1 ; i < p->max ; i++) {
+ if (test_bit(i,(char *) swap_header))
+ p->swap_map[i] = 0;
+ else
+ p->swap_map[i] = SWAP_MAP_BAD;
+ }
+ break;
+
+ case 2:
+ /* Check the swap header's sub-version and the size of
+ the swap file and bad block lists */
+ if (swap_header->info.version != 1) {
+ printk(KERN_WARNING
+ "Unable to handle swap header version %d\n",
+ swap_header->info.version);
+ error = -EINVAL;
+ goto bad_swap;
+ }
+
+ p->lowest_bit = 1;
+ p->highest_bit = swap_header->info.last_page - 1;
+ p->max = swap_header->info.last_page;
+
+ if (p->max >= 0x7fffffffL/PAGE_SIZE ||
+ (void *) &swap_header->info.badpages[swap_header->info.nr_badpages-1] >= (void *) swap_header->magic.magic) {
+ error = -EINVAL;
+ goto bad_swap;
}
+
+ /* OK, set up the swap map and apply the bad block list */
+ if (!(p->swap_map = vmalloc (p->max * sizeof(short)))) {
+ error = -ENOMEM;
+ goto bad_swap;
+ }
+
+ error = 0;
+ memset(p->swap_map, 0, p->max * sizeof(short));
+ for (i=0; i<swap_header->info.nr_badpages; i++) {
+ int page = swap_header->info.badpages[i];
+ if (page <= 0 || page >= swap_header->info.last_page)
+ error = -EINVAL;
+ else
+ p->swap_map[page] = SWAP_MAP_BAD;
+ }
+ nr_good_pages = swap_header->info.last_page - i;
+ lock_map_size = (p->max + 7) / 8;
+ if (error)
+ goto bad_swap;
}
- if (!j) {
- printk("Empty swap-file\n");
+
+ if (!nr_good_pages) {
+ printk(KERN_WARNING "Empty swap-file\n");
error = -EINVAL;
goto bad_swap;
}
- p->swap_map = (unsigned char *) vmalloc(p->max);
- if (!p->swap_map) {
+ p->swap_map[0] = SWAP_MAP_BAD;
+ if (!(p->swap_lockmap = vmalloc (lock_map_size))) {
error = -ENOMEM;
goto bad_swap;
}
- for (i = 1 ; i < p->max ; i++) {
- if (test_bit(i,p->swap_lockmap))
- p->swap_map[i] = 0;
- else
- p->swap_map[i] = 0x80;
- }
- p->swap_map[0] = 0x80;
- memset(p->swap_lockmap,0,PAGE_SIZE);
+ memset(p->swap_lockmap,0,lock_map_size);
p->flags = SWP_WRITEOK;
- p->pages = j;
- nr_swap_pages += j;
+ p->pages = nr_good_pages;
+ nr_swap_pages += nr_good_pages;
printk(KERN_INFO "Adding Swap: %dk swap-space (priority %d)\n",
- j<<(PAGE_SHIFT-10), p->prio);
+ nr_good_pages<<(PAGE_SHIFT-10), p->prio);
/* insert swap space into swap_list: */
prev = -1;
@@ -616,8 +690,10 @@ bad_swap:
if(filp.f_op && filp.f_op->release)
filp.f_op->release(filp.f_dentry->d_inode,&filp);
bad_swap_2:
- free_page((long) p->swap_lockmap);
- vfree(p->swap_map);
+ if (p->swap_lockmap)
+ vfree(p->swap_lockmap);
+ if (p->swap_map)
+ vfree(p->swap_map);
dput(p->swap_file);
p->swap_device = 0;
p->swap_file = NULL;
@@ -625,6 +701,8 @@ bad_swap_2:
p->swap_lockmap = NULL;
p->flags = 0;
out:
+ if (swap_header)
+ free_page((long) swap_header);
unlock_kernel();
return error;
}
@@ -639,7 +717,7 @@ void si_swapinfo(struct sysinfo *val)
continue;
for (j = 0; j < swap_info[i].max; ++j)
switch (swap_info[i].swap_map[j]) {
- case 128:
+ case SWAP_MAP_BAD:
continue;
case 0:
++val->freeswap;
@@ -651,4 +729,3 @@ void si_swapinfo(struct sysinfo *val)
val->totalswap <<= PAGE_SHIFT;
return;
}
-