Re: [updated patch] large swap areas

Sylvain Pion (Sylvain.Pion@sophia.inria.fr)
Sun, 8 Mar 1998 20:28:38 +0100


--C7zPtVaVf+AK4Oqc
Content-Type: text/plain; charset=us-ascii

Hi,

Here's the updated version of the large swap areas patch, against 2.1.89.
The new mkswap.c is here too.

To sum up the whole thing:
- old kernels can use ~128MB of the new format, when "-o" is specified to
mkswap, otherwise the first 8KB aren't modified.
- new kernels can use both formats (support for the old should be removed in
the future, ie 2.3, if this feature is integrated in 2.2)
- the new format is as suggested, see the patch.

I don't know how to compute the new limit, and it depends on the architecture,
any hint ? (I guess it has to do with SWP_ENTRY in pgtable.h)

Also, I added a check to prevent swapon'ing twice the same file, and have put
kswapd_setup() as __initfunc.

Comments welcome.

-- 
Sylvain

--C7zPtVaVf+AK4Oqc Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=patch_large_swap_89

diff -ur linux-2.1.89.orig/mm/swapfile.c linux/mm/swapfile.c --- linux-2.1.89.orig/mm/swapfile.c Sat Mar 7 20:53:35 1998 +++ linux/mm/swapfile.c Sun Mar 8 19:40:04 1998 @@ -35,6 +35,25 @@ struct swap_info_struct swap_info[MAX_SWAPFILES]; +struct swap_header { + char magic[10]; + __u16 version; + __u32 page_size; /* This just to make sure the swap was + not created with wrong PAGE_SIZE */ + __u32 swap_size; /* In PAGE_SIZE blocks */ + __u32 bad_blocks_size; /* Number of bad_blocks entries */ +}; + +#define MAX_BAD_PAGES ((PAGE_SIZE - 1024)/4) + +struct swap_first_page { + union { + struct swap_header hdr; + char reserved[1024]; + } header; + __u32 bad_blocks[MAX_BAD_PAGES]; /* In units of PAGE_SIZE */ +}; + static inline int scan_swap_map(struct swap_info_struct *si) { @@ -476,14 +495,15 @@ */ asmlinkage int sys_swapon(const char * specialfile, int swap_flags) { - struct swap_info_struct * p; + struct swap_info_struct * p, * q; struct dentry * swap_dentry; unsigned int type; - int i, j, prev; + int i, j, k, prev, format; int error = -EPERM; struct file filp; static int least_priority = 0; unsigned char *avail_map = 0; + struct swap_first_page *first_page = 0; lock_kernel(); if (!suser()) @@ -518,8 +538,17 @@ goto bad_swap_2; p->swap_file = swap_dentry; - error = -EINVAL; + /* Check if swap file is already used */ + error = -EBUSY; + for (i = swap_list.head; i >= 0; i = swap_info[i].next) { + q = swap_info + i; + if ((p != q) && ((q->flags & SWP_USED) == SWP_USED) + && (q->swap_file == swap_dentry)) + goto bad_swap_2; + } + + error = -EINVAL; if (S_ISBLK(swap_dentry->d_inode->i_mode)) { p->swap_device = swap_dentry->d_inode->i_rdev; set_blocksize(p->swap_device, PAGE_SIZE); @@ -549,40 +578,85 @@ error = -ENOMEM; goto bad_swap; } - rw_swap_page_nocache(READ, SWP_ENTRY(type,0), (char *) avail_map); - if (memcmp("SWAP-SPACE",avail_map+PAGE_SIZE-10,10)) { - printk("Unable to find swap-space signature\n"); - error = -EINVAL; - goto bad_swap; - } - memset(avail_map+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,avail_map)) { - if (!p->lowest_bit) - p->lowest_bit = i; - p->highest_bit = i; - p->max = i+1; - j++; + + /* First try to recognize the new format. */ + p->max = 3; + rw_swap_page_nocache(READ, SWP_ENTRY(type,8192/PAGE_SIZE), (char *) avail_map); + if (!memcmp("SWAPSPACE2",avail_map,10)) { + first_page = (struct swap_first_page *) avail_map; + if ((first_page->header.hdr.version == 1) && + (first_page->header.hdr.page_size == PAGE_SIZE)) { + format = 1; + } + else { + printk("Swap-space signature doesn't match version or PAGE_SIZE\n"); + error = -EINVAL; + goto bad_swap; } } - if (!j) { - printk("Empty swap-file\n"); - error = -EINVAL; - goto bad_swap; + else { + p->max = 1; + printk("Unable to find new swap-space signature, trying old\n"); + rw_swap_page_nocache(READ, SWP_ENTRY(type,0), (char *) avail_map); + if (memcmp("SWAP-SPACE",avail_map+PAGE_SIZE-10,10)) { + printk("Unable to find old swap-space signature\n"); + error = -EINVAL; + goto bad_swap; + } + format = 0; } - p->swap_map = (unsigned char *) vmalloc(p->max); - if (!p->swap_map) { - error = -ENOMEM; - goto bad_swap; + j = 0; + if (format == 0) { + memset(avail_map+PAGE_SIZE-10,0,10); + for (i = 1 ; i < 8*PAGE_SIZE ; i++) { + if (test_bit(i,avail_map)) { + if (!p->lowest_bit) + p->lowest_bit = i; + p->highest_bit = i; + p->max = i+1; + j++; + } + } + if (!j) { + printk("Empty swap-file\n"); + error = -EINVAL; + goto bad_swap; + } + p->swap_map = (unsigned char *) vmalloc(p->max); + if (!p->swap_map) { + error = -ENOMEM; + goto bad_swap; + } + for (i = 1 ; i < p->max ; i++) { + if (test_bit(i,avail_map)) + p->swap_map[i] = 0; + else + p->swap_map[i] = 0x80; + } } - for (i = 1 ; i < p->max ; i++) { - if (test_bit(i,avail_map)) - p->swap_map[i] = 0; - else - p->swap_map[i] = 0x80; + else if (format == 1) { + p->max = first_page->header.hdr.swap_size; + p->swap_map = (unsigned char *) vmalloc(p->max); + if (!p->swap_map) { + printk("Unable to start swapping: out of memory :-(\n"); + error = -ENOMEM; + goto bad_swap; + } + k = 0; + for (i = 0 ; i < p->max ; i++) { + if ( (k < first_page->header.hdr.bad_blocks_size) && + (first_page->bad_blocks[k] == i) ) { + p->swap_map[i] = 0x80; + k++; + } + else { + if (!p->lowest_bit) + p->lowest_bit = i; + p->highest_bit = i; + p->swap_map[i] = 0; + j++; + } + } } p->swap_map[0] = 0x80; p->flags = SWP_WRITEOK; diff -ur linux-2.1.89.orig/mm/vmscan.c linux/mm/vmscan.c --- linux-2.1.89.orig/mm/vmscan.c Sat Mar 7 20:53:35 1998 +++ linux/mm/vmscan.c Sat Mar 7 22:45:13 1998 @@ -22,6 +22,7 @@ #include <linux/smp_lock.h> #include <linux/slab.h> #include <linux/dcache.h> +#include <linux/init.h> #include <asm/bitops.h> #include <asm/pgtable.h> @@ -498,7 +499,7 @@ * may be printed in the middle of another driver's init * message). It looks very bad when that happens. */ -void kswapd_setup(void) +__initfunc(void kswapd_setup(void)) { int i; char *revision="$Revision: 1.5 $", *s, *e;

--C7zPtVaVf+AK4Oqc Content-Type: text/plain Content-Disposition: attachment; filename="mkswap.c"

/* * mkswap.c - set up a linux swap device * * (C) 1991 Linus Torvalds. This file may be redistributed as per * the Linux copyright. */

/* * 20.12.91 - time began. Got VM working yesterday by doing this by hand. * * Usage: mkswap [-c] [-o] {device,file} [size-in-blocks] * * -c for readability checking (use it unless you are SURE!) * -o for writing the old signature too (default is off because it can * erase disk labels (on UltraSparc)). Selecting this option allows * old kernels to use the first 128MB. * * The device may be a block device or a image of one, but this isn't * enforced (but it's not much fun on a character device :-). * * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995. * * Patch from Sylvain.Pion@sophia.inria.fr to support large swap areas. * (25.2.98) */

#include <stdio.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <stdlib.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <asm/page.h> #include <linux/fs.h>

static char * program_name = "mkswap"; static char * device_name = NULL; static int DEV = -1; static long PAGES = 0; static int check = 0; static int write_old = 0; static int badpages = 0;

#define OLD_MAX_PAGES (8 * (PAGE_SIZE - 10)) static int old_signature_page[PAGE_SIZE/sizeof(int)];

#define MAGIC_STRING "SWAPSPACE2" #define VERSION 1 #define MAX_BAD_PAGES ((PAGE_SIZE - 1024)/4) #define OFFSET 8192 /* New format leaves first 8kB untouched */ #define HDR_PAGES (1 + (OFFSET + PAGE_SIZE - 1) / PAGE_SIZE)

struct swap_header { char magic[10]; __u16 version; __u32 page_size; /* This just to make sure the swap was not created with wrong PAGE_SIZE */ __u32 swap_size; /* In PAGE_SIZE blocks */ __u32 bad_blocks_size; /* Number of bad_blocks entries */ };

struct swap_first_page { union { struct swap_header hdr; char reserve[1024]; } header; __u32 bad_blocks[MAX_BAD_PAGES]; /* In units of PAGE_SIZE */ };

static struct swap_first_page first_page;

static void bit_set (unsigned int *addr, unsigned int nr) { unsigned int r, m;

addr += nr / (8 * sizeof(int)); r = *addr; m = 1 << (nr & (8 * sizeof(int) - 1)); *addr = r | m; }

static int bit_test_and_clear (unsigned int *addr, unsigned int nr) { unsigned int r, m;

addr += nr / (8 * sizeof(int)); r = *addr; m = 1 << (nr & (8 * sizeof(int) - 1)); *addr = r & ~m; return (r & m) != 0; }

void fatal_error(const char * fmt_string) { fprintf(stderr,fmt_string,program_name,device_name); exit(1); }

void usage (void) { fatal_error("Usage: %s [-c] [-o] {devicename,filename} [blocks]\n"); }

#define die(str) fatal_error("%s: " str "\n")

void check_blocks(void) { unsigned int current_page = HDR_PAGES; int do_seek = 1; static char buffer[PAGE_SIZE];

while (current_page < PAGES) { if (!check) { if (current_page<OLD_MAX_PAGES) bit_set(old_signature_page,current_page); current_page++; continue; } if (do_seek && lseek(DEV,current_page*PAGE_SIZE,SEEK_SET) != current_page*PAGE_SIZE) die("seek failed in check_blocks"); if ((do_seek = (PAGE_SIZE != read(DEV, buffer, PAGE_SIZE)))) { if (current_page<OLD_MAX_PAGES) bit_test_and_clear(old_signature_page,current_page); if (badpages == MAX_BAD_PAGES) die("Too many bad pages !"); first_page.bad_blocks[badpages++] = current_page++; continue; } if (current_page<OLD_MAX_PAGES) bit_set(old_signature_page,current_page++); } if (badpages-HDR_PAGES) printf("%d bad page%s\n", badpages-(int)HDR_PAGES, (badpages>(1+HDR_PAGES))?"s":""); }

static long valid_offset (int fd, int offset) { char ch;

if (lseek (fd, offset, 0) < 0) return 0; if (read (fd, &ch, 1) < 1) return 0; return 1; }

static int count_blocks (int fd) { int high, low;

low = 0; for (high = 1; valid_offset (fd, high); high *= 2) low = high; while (low < high - 1) { const int mid = (low + high) / 2;

if (valid_offset (fd, mid)) low = mid; else high = mid; } valid_offset (fd, 0); return (low + 1); }

static int get_size(const char *file) { int fd; int size;

fd = open(file, O_RDWR); if (fd < 0) { perror(file); exit(1); } if (ioctl(fd, BLKGETSIZE, &size) >= 0) { close(fd); return (size * 512); } size = count_blocks(fd); close(fd); return size; }

int main(int argc, char ** argv) { char * tmp; struct stat statbuf; int goodpages, i;

if (argc && *argv) program_name = *argv; while (argc-- > 1) { argv++; if (argv[0][0] != '-') if (device_name) { PAGES = strtol(argv[0],&tmp,0)>>(PAGE_SHIFT-10); if (*tmp) usage(); } else device_name = argv[0]; else while (*++argv[0]) switch (argv[0][0]) { case 'c': check=1; break; case 'o': write_old=1; break; default: usage(); } } if (device_name && !PAGES) { PAGES = get_size(device_name) / PAGE_SIZE; } if (!device_name) { fprintf(stderr, "%s: error: Nowhere to set up swap on?\n", program_name); usage(); } if (PAGES < 10) { fprintf(stderr, "%s: error: swap area needs to be at least %ldkB\n", program_name, 10 * PAGE_SIZE / 1024); usage(); } if (PAGES > OLD_MAX_PAGES && write_old) fprintf(stderr, "%s: warning: old kernels will only support 128MB.\n", program_name); DEV = open(device_name,O_RDWR); if (DEV < 0 || fstat(DEV, &statbuf) < 0) { perror(device_name); exit(1); } if (check && !S_ISBLK(statbuf.st_mode)) { fprintf(stderr, "%s: warning: can only check block devices.\n", program_name); check=0; } else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) die("Will not try to make swapdevice on '%s'");

/* Fill the signature pages. */ memset(old_signature_page,0,PAGE_SIZE); memset(&first_page, 0, PAGE_SIZE); strncpy((char*)old_signature_page+PAGE_SIZE-10, "SWAP-SPACE", 10); strncpy(first_page.header.hdr.magic, MAGIC_STRING, 10); first_page.header.hdr.version = VERSION; first_page.header.hdr.page_size = PAGE_SIZE; first_page.header.hdr.swap_size = PAGES; first_page.header.hdr.bad_blocks_size = badpages = HDR_PAGES; for (i=0; i<HDR_PAGES; i++) first_page.bad_blocks[i] = i;

/* Check for bad blocks. */ check_blocks(); first_page.header.hdr.bad_blocks_size = badpages;

goodpages = PAGES - badpages; if (goodpages <= 0) die("Unable to set up swap-space: unreadable"); printf("Setting up swapspace, size = %ld bytes\n", goodpages*PAGE_SIZE);

/* Write the signature page(s). */ if (write_old) { if (lseek(DEV, 0, SEEK_SET)) die("unable to rewind swap-device"); if (PAGE_SIZE != write(DEV, old_signature_page, PAGE_SIZE)) die("unable to write (old format) signature page"); } if (OFFSET != lseek(DEV, OFFSET, SEEK_SET)) die("unable to rewind swap-device"); if (PAGE_SIZE != write(DEV, &first_page, PAGE_SIZE)) die("unable to write signature page"); /* * A subsequent swapon() will fail if the signature * is not actually on disk. */ if (fsync(DEV)) die("fsync failed"); return 0; }

--C7zPtVaVf+AK4Oqc--

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu