diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-09-19 19:15:08 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-09-19 19:15:08 +0000 |
commit | 03ba4131783cc9e872f8bb26a03f15bc11f27564 (patch) | |
tree | 88db8dba75ae06ba3bad08e42c5e52efc162535c /mm/swapfile.c | |
parent | 257730f99381dd26e10b832fce4c94cae7ac1176 (diff) |
- Merge with Linux 2.1.121.
- Bugfixes.
Diffstat (limited to 'mm/swapfile.c')
-rw-r--r-- | mm/swapfile.c | 205 |
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; } - |