Linux-kernel mailing list archive 1998-10,: Re: [updated patch] large swap areas

# Re: [updated patch] large swap areas

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

- Messages sorted by: [ date ][ thread ][ subject ][ author ]
- Next message: Thomas Molina: "ip forwarding question"
- Previous message: Marijn Meijles: "Re: mmap() versus read()"

--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

- Next message: Thomas Molina: "ip forwarding question"
- Previous message: Marijn Meijles: "Re: mmap() versus read()"