diff -r --new-file -u linux.orig/Documentation/Configure.help linux/Documentation/Configure.help
--- linux.orig/Documentation/Configure.help	2002-11-19 21:36:44.000000000 -0500
+++ linux/Documentation/Configure.help	2002-11-20 15:59:06.000000000 -0500
@@ -5381,6 +5381,35 @@
 
   Say Y unless you know what you are doing.
 
+Swapping via network sockets (EXPERIMENTAL)
+CONFIG_NETSWAP
+  If you want to be able to have your swap files or partitions located
+  on some other machine  on the network then say  `Y'. This option  is
+  also    automatically enabled  when     saying   `Y'   or  `M'    to
+  CONFIG_SWAP_VIA_NFS. Make sure to read `Documentation/netswap.txt'.
+
+  If unsure, say `N'.
+
+Swapping via NFS (EXPERIMENTAL)
+CONFIG_SWAP_VIA_NFS
+  If  you  want to  swap to  files  located on  NFS  file systems, say
+  `Y'. Normally you  don't need this except  when your system does not
+  have  any hard disks  at  all . Please  note  that swapping to files
+  located on NFS file systems is slow  and insecure. Make sure to read
+  `Documentation/nfsswap.txt'.
+
+  This   code is also available   as a module   ( = code  which can be
+  inserted in and removed from the running  kernel whenever you want).
+  The module will be called `nfsswap.o'. If you  want to compile it as
+  a  module, say `M' here and  read Documentation/modules.txt. You can
+  use     the  kernel      module    auto-loading  features       (see
+  `Documentation/kmod.txt') to load this  module on demand  during the
+  `swapon' system call.  For this to work you  should specify an alias
+  `alias   swapfile-mod     nfsswap'  in       `/etc/modules.conf'  or
+  `/etc/conf.modules'.
+
+  If unsure, say `N'.
+
 The IPv6 protocol
 CONFIG_IPV6
   This is experimental support for the next version of the Internet
@@ -12162,6 +12191,23 @@
   If you are running Linux on an IBM iSeries system and you want to
   read a CD drive owned by OS/400, say Y here.
 
+Swapping to block devices
+CONFIG_BLKDEV_SWAP
+  Say yes unless you know  what you are doing.  The only case when you
+  may want to say `N' is when compiling for a disk-less system.
+
+  This code is  also  available as a module  (  =  code which  can  be
+  inserted in and removed from  the running kernel whenever you want).
+  The module will be called `blkdev_swap.o'. If you want to compile it
+  as a module, say  `M' here and read `Documentation/modules.txt'. You
+  can  use   the    kernel   module     auto-loading  features    (see
+  `Documentation/kmod.txt') to load this  module on demand  during the
+  `swapon' system call.  For this to work  you should specify an alias
+  `alias   swapfile-mod blkdev_swap'     in     `/etc/modules.conf' or
+  `/etc/conf.modules'.
+
+  If unsure, say `Y'.
+
 Quota support
 CONFIG_QUOTA
   If you say Y here, you will be able to set per user limits for disk
diff -r --new-file -u linux.orig/Documentation/netswap.txt linux/Documentation/netswap.txt
--- linux.orig/Documentation/netswap.txt	1969-12-31 19:00:00.000000000 -0500
+++ linux/Documentation/netswap.txt	2002-11-20 15:59:06.000000000 -0500
@@ -0,0 +1,51 @@
+                     Swapping over network
+
+Support for this is enabled via the CONFIG_NETSWAP option, which is
+automatically enabled when enabling swap files located on NFS volumes
+(CONFIG_SWAP_VIA_NFS).
+
+When swapping to files located on a network file system like NFS or
+CODA or others or to nbd (network block device, see `nbd.txt')
+partitions there is the problem that this requires additional memory,
+besides the page which is currently swapped in or out, probably at
+least two more pages for each page in question.
+
+This means that not only there needs to be free space left in the swap
+file or the swap partition, but in addition there must be enough free
+memory left in the system to perform the swap out of pages.
+
+This is particularly painful as receiving data over the network itself
+consumes memory, and this memory is allocated from an interrupt
+context (i.e. in the interrupt handler of the network card). That
+means that on a congested network there are chances that the machine
+runs out of memory, simply because the network device's interrupt
+routines allocate memory faster that it is freed by swapping via
+network.
+
+To cope with this problem, there is a new socket option `SO_SWAPPING'
+which has to be set on the `SOL_SOCKET' level with setsockopt() (see
+setsockopt(2)). When this option is set on any network socket, then
+the system will start to drop network packets it receives on any other
+socket when the number of free pages falls below a certain threshold.
+
+This threshold initially is 4 pages less than `freepages.min' (see
+`Documentation/sysctl/vm.txt') but can be tuned using the sysctl
+interface by writing to the file `/proc/sys/net/swapping/threshold'
+
+There are two other files:
+
+`/proc/sys/net/swapping/dropped':
+    how many network packets have been dropped so far. This file is
+    writable, writing to it simply sets the counter to the given value
+    (useful for resetting the counter).
+
+`/proc/sys/net/swapping/sock_count':
+    How many network sockets have the `SO_SWAPPING' option set (read
+    only, of course).
+
+When using swap-files on NFS volumes, then the `SO_SWAPPING' option is
+set or cleared by swapon/swapoff system calls, so the user need not
+care about it.
+
+Swapping over the network is insecure unless the data would be
+encrypted, which is not the case with NFS. It is also very slow.
diff -r --new-file -u linux.orig/Documentation/nfsswap.txt linux/Documentation/nfsswap.txt
--- linux.orig/Documentation/nfsswap.txt	1969-12-31 19:00:00.000000000 -0500
+++ linux/Documentation/nfsswap.txt	2002-11-20 15:59:06.000000000 -0500
@@ -0,0 +1,41 @@
+                   Swapping to files on NFS volumes
+
+To  do  this you have to  say  `Y' or  `M'  to the CONFIG_SWAP_VIA_NFS
+configuration  option. When compling support  for this as a module you
+should  read  `Documentation/modules.txt'.  For  auto-loading  of  the
+module during the `swapon' system call you have to place a line like
+
+alias swapfile-mod nfsswap
+
+in   `/etc/modules.conf'  (or `/etc/conf.modules',  depending  on your
+setup). NFS volumes  holding swapfile should  be mounted  with `rsize'
+and `wsize' set to something  less than the size  of a page, otherwise
+deadlocks  caused by memory fragmentation can  happen,  i.e. mount the
+volume which is to hold the swapfiles with
+
+mount -t nfs -o rsize=2048,wsize=2048 NFS_SERVER_IP:/server_volume /mount_point
+
+or set the option in `/etc/fstab'. Read `Documentation/nfsroot.txt' to
+learn how to set mount options for the root file  system, if your swap
+files are to be located on the root file system.
+
+Setting the  `rsize' and `wsize' to  anything less than PAGE_SIZE is a
+performance hit, so  you probably want to  have  at least two  volumes
+mounted, one for the swapfiles, one for the rest.
+
+You may want to read `Documentation/netswap.txt' as well.
+
+Swapfiles on NFS volumes can be treated like any other swapfile,
+i.e.
+
+dd if=/dev/zero of=/swapfiles/SWAPFILE bs=1k count=20480
+mkswap /swapfiles/SWAPFILE
+swapon /swapfiles/SWAPFILE
+
+will  create a 20M swapfile and  tell the system  to use it. Actually,
+one could use lseek(2) to create  an empty swapfile. This is different
+from swapfiles located on local harddisk.
+
+Swapping  over  the network is   insecure   unless the data  would  be
+encrypted, which is not the case with NFS. It is also very slow.
+
diff -r --new-file -u linux.orig/drivers/block/blkpg.c linux/drivers/block/blkpg.c
--- linux.orig/drivers/block/blkpg.c	2002-11-19 21:36:46.000000000 -0500
+++ linux/drivers/block/blkpg.c	2002-11-20 15:59:06.000000000 -0500
@@ -34,7 +34,7 @@
 #include <linux/blk.h>			/* for set_device_ro() */
 #include <linux/blkpg.h>
 #include <linux/genhd.h>
-#include <linux/swap.h>			/* for is_swap_partition() */
+#include <linux/swap.h>			/* for swap_run_test() */
 #include <linux/module.h>               /* for EXPORT_SYMBOL */
 
 #include <asm/uaccess.h>
@@ -114,6 +114,29 @@
 	return 0;
 }
 
+/* swap_run_test() applies this hook to all swapfiles until it returns
+ * "1".  If it never return "1", the result of swap_run_test() is "0",
+ * otherwise "1".
+ */
+static int is_swap_partition_hook(unsigned int flags, struct file *swap_file,
+				  void *testdata)
+{
+	kdev_t swap_dev = S_ISBLK(swap_file->f_dentry->d_inode->i_mode)
+		? swap_file->f_dentry->d_inode->i_rdev : 0;
+	kdev_t dev = *((kdev_t *)testdata);
+	
+	if (flags & SWP_USED && dev == swap_dev) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+static inline int is_swap_partition(kdev_t dev)
+{
+	return swap_run_test(is_swap_partition_hook, &dev);
+}
+
 /*
  * Delete a partition given by partition number
  *
diff -r --new-file -u linux.orig/fs/Config.in linux/fs/Config.in
--- linux.orig/fs/Config.in	2002-11-19 21:36:48.000000000 -0500
+++ linux/fs/Config.in	2002-11-20 15:59:06.000000000 -0500
@@ -4,6 +4,12 @@
 mainmenu_option next_comment
 comment 'File systems'
 
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   tristate 'Swapping to block devices' CONFIG_BLKDEV_SWAP
+else
+   define_bool CONFIG_BLKDEV_SWAP y
+fi
+
 bool 'Quota support' CONFIG_QUOTA
 tristate 'Kernel automounter support' CONFIG_AUTOFS_FS
 tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS
@@ -96,6 +102,12 @@
    dep_tristate 'NFS file system support' CONFIG_NFS_FS $CONFIG_INET
    dep_mbool '  Provide NFSv3 client support' CONFIG_NFS_V3 $CONFIG_NFS_FS
    dep_bool '  Root file system on NFS' CONFIG_ROOT_NFS $CONFIG_NFS_FS $CONFIG_IP_PNP
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      dep_tristate '  Swapping via NFS (EXPERIMENTAL)' CONFIG_SWAP_VIA_NFS $CONFIG_NFS_FS
+      if [ "$CONFIG_SWAP_VIA_NFS" = "y" -o "$CONFIG_SWAP_VIA_NFS" = "m" ]; then
+         define_bool CONFIG_NETSWAP  y
+      fi
+   fi
 
    dep_tristate 'NFS server support' CONFIG_NFSD $CONFIG_INET
    dep_mbool '  Provide NFSv3 server support' CONFIG_NFSD_V3 $CONFIG_NFSD
diff -r --new-file -u linux.orig/fs/Makefile linux/fs/Makefile
--- linux.orig/fs/Makefile	2002-05-16 21:30:28.000000000 -0400
+++ linux/fs/Makefile	2002-11-20 15:59:06.000000000 -0500
@@ -8,7 +8,7 @@
 O_TARGET := fs.o
 
 export-objs :=	filesystems.o open.o dcache.o buffer.o
-mod-subdirs :=	nls
+mod-subdirs :=	nls nfs
 
 obj-y :=	open.o read_write.o devices.o file_table.o buffer.o \
 		super.o block_dev.o char_dev.o stat.o exec.o pipe.o namei.o \
@@ -68,6 +68,7 @@
 subdir-$(CONFIG_DEVPTS_FS)	+= devpts
 subdir-$(CONFIG_SUN_OPENPROMFS)	+= openpromfs
 
+obj-$(CONFIG_BLKDEV_SWAP)       += blkdev_swap.o
 
 obj-$(CONFIG_BINFMT_AOUT)	+= binfmt_aout.o
 obj-$(CONFIG_BINFMT_EM86)	+= binfmt_em86.o
diff -r --new-file -u linux.orig/fs/blkdev_swap.c linux/fs/blkdev_swap.c
--- linux.orig/fs/blkdev_swap.c	1969-12-31 19:00:00.000000000 -0500
+++ linux/fs/blkdev_swap.c	2002-11-20 15:59:06.000000000 -0500
@@ -0,0 +1,315 @@
+/*
+ * Swapping to partitions or files located on partitions.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/fs.h>
+
+#ifdef DEBUG_BLKDEV_SWAP
+# define dprintk(fmt...) printk(##fmt)
+#else
+# define dprintk(fmt...) do { /* */ } while (0)
+#endif
+
+#define BLKDEV_SWAP_ID      "blkdev"
+#define BLKDEV_FILE_SWAP_ID "blkdev file"
+
+/*
+ * Helper function, copied here from buffer.c
+ */
+
+/*
+ * Start I/O on a page.
+ * This function expects the page to be locked and may return
+ * before I/O is complete. You then have to check page->locked,
+ * page->uptodate, and maybe wait on page->wait.
+ *
+ * brw_page() is SMP-safe, although it's being called with the
+ * kernel lock held - but the code is ready.
+ *
+ * FIXME: we need a swapper_inode->get_block function to remove
+ *        some of the bmap kludges and interface ugliness here.
+ */
+int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size)
+{
+	struct buffer_head *head, *bh;
+
+	if (!PageLocked(page))
+		panic("brw_page: page not locked for I/O");
+
+	if (!page->buffers)
+		create_empty_buffers(page, dev, size);
+	head = bh = page->buffers;
+
+	/* Stage 1: lock all the buffers */
+	do {
+		lock_buffer(bh);
+		bh->b_blocknr = *(b++);
+		set_bit(BH_Mapped, &bh->b_state);
+		set_buffer_async_io(bh);
+		bh = bh->b_this_page;
+	} while (bh != head);
+
+	/* Stage 2: start the IO */
+	do {
+		struct buffer_head *next = bh->b_this_page;
+		submit_bh(rw, bh);
+		bh = next;
+	} while (bh != head);
+	return 0;
+}
+
+/*
+ * We implement to methods: swapping to partitions, and swapping to files
+ * located on partitions.
+ */
+
+struct blkdev_swap_data {
+	kdev_t dev;
+};
+
+struct test_data {
+	struct file * filp;
+	kdev_t dev;
+};
+
+static int is_blkdev_swapping(unsigned int flags,
+			      struct file * swapf,
+			      void *data)
+{
+	struct test_data *testdata = (struct test_data *) data;
+	struct file * filp = testdata->filp;
+	kdev_t dev = testdata->dev;
+
+	/* Only check filp's that don't match the one already opened
+	 * for us by sys_swapon(). Otherwise, we will always flag a
+	 * busy swap file.
+	 */
+
+	if (swapf != filp) {
+		if (dev == swapf->f_dentry->d_inode->i_rdev)
+			return 1;
+	}
+	return 0;
+}
+
+static int blkdev_swap_open(struct file * filp, void **dptr)
+{
+	int swapfilesize;
+	kdev_t dev;
+	struct blkdev_swap_data *data;
+	int error;
+	struct test_data testdata;
+
+	MOD_INC_USE_COUNT;
+
+	if (!S_ISBLK(filp->f_dentry->d_inode->i_mode)) {
+		dprintk(__FUNCTION__": can't handle this swap file: %s\n",
+		       swapf->d_name.name);
+		error = 0; /* not for us */
+		goto bad_swap;
+	}
+	
+	dev = filp->f_dentry->d_inode->i_rdev;
+	set_blocksize(dev, PAGE_SIZE);
+	error = -ENODEV;
+	if (!dev ||
+	    (blk_size[MAJOR(dev)] && !blk_size[MAJOR(dev)][MINOR(dev)])) {
+		printk("blkdev_swap_open: blkdev weirdness for %s\n",
+		       filp->f_dentry->d_name.name);
+		goto bad_swap;
+	}
+		
+	/* Check to make sure that we aren't already swapping. */
+	error = -EBUSY;
+	testdata.filp = filp;
+	testdata.dev = dev;
+	if (swap_run_test(is_blkdev_swapping, &testdata)) {
+		printk("blkdev_swap_open: already swapping to %s\n",
+		       filp->f_dentry->d_name.name);
+		goto bad_swap;
+ 	}
+
+	/* Check to make sure that we aren't already swapping. */
+	error = -EBUSY;
+	testdata.filp = filp;
+	testdata.dev = dev;
+	if (swap_run_test(is_blkdev_swapping, &testdata)) {
+		printk("blkdev_swap_open: already swapping to %s\n",
+		       filp->f_dentry->d_name.name);
+		goto bad_swap;
+ 	}
+
+	swapfilesize = 0;
+	if (blk_size[MAJOR(dev)])
+		swapfilesize = blk_size[MAJOR(dev)][MINOR(dev)]
+			>> (PAGE_SHIFT - 10);
+
+	if ((data = kmalloc(sizeof(*data), GFP_KERNEL)) == NULL) {
+		printk("blkdev_swap_open: can't allocate data for %s\n",
+		       filp->f_dentry->d_name.name);
+		error = -ENOMEM;
+		goto bad_swap;
+	}
+	data->dev = dev;
+	*dptr = data;
+
+	dprintk("blkdev_swap_open: returning %d\n", swapfilesize);
+	return swapfilesize;
+
+ bad_swap:
+	MOD_DEC_USE_COUNT;
+	return error; /* this swap thing is not for us */	
+}
+
+static int blkdev_swap_release(struct file * filp, void *data)
+{
+	dprintk("blkdev_swap_release: releasing swap device %s\n",
+		filp->f_dentry->d_name.name);
+	kfree(data);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int blkdev_rw_page(int rw, struct page *page, unsigned long offset,
+			  void *ptr)
+{
+	struct blkdev_swap_data *data = (struct blkdev_swap_data *)ptr;
+ 	brw_page(rw, page, data->dev, (int *)&offset, PAGE_SIZE);
+	return 1;
+}
+
+static struct swap_ops blkdev_swap_ops = {
+	blkdev_swap_open,
+	blkdev_swap_release,
+	blkdev_rw_page
+};
+
+struct blkdevfile_swap_data {
+	struct inode *swapf;
+};
+
+static int is_blkdevfile_swapping(unsigned int flags,
+				  struct file * swapf,
+				  void * data)
+{
+	struct file * filp = (struct file *) data;
+
+	/* Only check filp's that don't match the one already opened
+	 * for us by sys_swapon(). Otherwise, we will always flag a
+	 * busy swap file.
+	 */
+
+	if (swapf != filp) {
+		if (filp->f_dentry->d_inode == swapf->f_dentry->d_inode)
+			return 1;
+	}
+	return 0;
+}
+
+static int blkdevfile_swap_open(struct file *swapf, void **dptr)
+{
+	int error = 0;
+	int swapfilesize;
+	struct blkdevfile_swap_data *data;
+
+	MOD_INC_USE_COUNT;
+
+	/* first check whether this is a regular file located on a local 
+	 * hard disk
+	 */
+	if (!S_ISREG(swapf->f_dentry->d_inode->i_mode)) {
+		dprintk("blkdevfile_swap_open: "
+			"can't handle this swap file: %s\n",
+			swapf->d_name.name);
+		error = 0; /* not for us */
+		goto bad_swap;
+	}
+	if (!swapf->f_dentry->d_inode->i_mapping->a_ops->bmap) {
+		dprintk("blkdevfile_swap_open: no bmap for file: %s\n",
+			swapf->d_name.name);
+		error = 0; /* not for us */
+		goto bad_swap;
+	}
+
+	if (swap_run_test(is_blkdevfile_swapping, swapf)) {
+		dprintk("blkdevfile_swap_open: already swapping to %s\n",
+			swapf->d_name.name);
+		error = -EBUSY;
+		goto bad_swap;
+	}
+	swapfilesize = swapf->f_dentry->d_inode->i_size >> PAGE_SHIFT;
+	if ((data = kmalloc(sizeof(*data), GFP_KERNEL)) == NULL) {
+		error = -ENOMEM;
+		goto bad_swap;
+	}
+	data->swapf = swapf->f_dentry->d_inode;
+	*dptr = data;
+	return swapfilesize;
+
+ bad_swap:
+	MOD_DEC_USE_COUNT;
+	return error;
+}
+
+static int blkdevfile_swap_release(struct file *swapf, void *data)
+{
+	kfree(data);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int blkdevfile_rw_page(int rw, struct page *page, unsigned long offset,
+			      void *ptr)
+{
+	struct blkdevfile_swap_data *data = (struct blkdevfile_swap_data *)ptr;
+	struct inode * swapf = data->swapf;
+	int i, j;
+	unsigned int block = offset
+		<< (PAGE_SHIFT - swapf->i_sb->s_blocksize_bits);
+	kdev_t dev = swapf->i_dev;
+	int block_size;
+	int zones[PAGE_SIZE/512];
+	int zones_used;
+
+	block_size = swapf->i_sb->s_blocksize;
+	for (i=0, j=0; j< PAGE_SIZE ; i++, j += block_size)
+		if (!(zones[i] = bmap(swapf,block++))) {
+			printk("blkdevfile_rw_page: bad swap file\n");
+			return 0;
+			}
+	zones_used = i;
+	
+ 	/* block_size == PAGE_SIZE/zones_used */
+ 	brw_page(rw, page, dev, zones, block_size);
+	return 1;
+}
+
+static struct swap_ops blkdevfile_swap_ops = {
+	blkdevfile_swap_open,
+	blkdevfile_swap_release,
+	blkdevfile_rw_page
+ };
+
+int __init blkdev_swap_init(void)
+{
+	(void)register_swap_method(BLKDEV_SWAP_ID, &blkdev_swap_ops);
+	(void)register_swap_method(BLKDEV_FILE_SWAP_ID, &blkdevfile_swap_ops);
+	return 0;
+}
+
+void __exit blkdev_swap_exit(void)
+{
+	unregister_swap_method(BLKDEV_SWAP_ID);
+	unregister_swap_method(BLKDEV_FILE_SWAP_ID);
+}
+
+module_init(blkdev_swap_init)
+module_exit(blkdev_swap_exit)
diff -r --new-file -u linux.orig/fs/buffer.c linux/fs/buffer.c
--- linux.orig/fs/buffer.c	2002-11-19 21:36:48.000000000 -0500
+++ linux/fs/buffer.c	2002-11-20 16:00:25.000000000 -0500
@@ -748,7 +748,7 @@
 	bh->b_private = private;
 }
 
-static void end_buffer_io_async(struct buffer_head * bh, int uptodate)
+void end_buffer_io_async(struct buffer_head * bh, int uptodate)
 {
 	static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED;
 	unsigned long flags;
@@ -2326,47 +2326,6 @@
 	return err;
 }
 
-/*
- * Start I/O on a page.
- * This function expects the page to be locked and may return
- * before I/O is complete. You then have to check page->locked
- * and page->uptodate.
- *
- * brw_page() is SMP-safe, although it's being called with the
- * kernel lock held - but the code is ready.
- *
- * FIXME: we need a swapper_inode->get_block function to remove
- *        some of the bmap kludges and interface ugliness here.
- */
-int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size)
-{
-	struct buffer_head *head, *bh;
-
-	if (!PageLocked(page))
-		panic("brw_page: page not locked for I/O");
-
-	if (!page->buffers)
-		create_empty_buffers(page, dev, size);
-	head = bh = page->buffers;
-
-	/* Stage 1: lock all the buffers */
-	do {
-		lock_buffer(bh);
-		bh->b_blocknr = *(b++);
-		set_bit(BH_Mapped, &bh->b_state);
-		set_buffer_async_io(bh);
-		bh = bh->b_this_page;
-	} while (bh != head);
-
-	/* Stage 2: start the IO */
-	do {
-		struct buffer_head *next = bh->b_this_page;
-		submit_bh(rw, bh);
-		bh = next;
-	} while (bh != head);
-	return 0;
-}
-
 int block_symlink(struct inode *inode, const char *symname, int len)
 {
 	struct address_space *mapping = inode->i_mapping;
diff -r --new-file -u linux.orig/fs/nfs/Makefile linux/fs/nfs/Makefile
--- linux.orig/fs/nfs/Makefile	2002-01-06 23:21:03.000000000 -0500
+++ linux/fs/nfs/Makefile	2002-11-20 15:59:06.000000000 -0500
@@ -15,6 +15,14 @@
 obj-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o      
 obj-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
 
-obj-m   := $(O_TARGET)
+obj-$(CONFIG_SWAP_VIA_NFS) += nfsswap.o
+ifeq ($(CONFIG_SWAP_VIA_NFS),m)
+export-objs := nfs_syms.o
+obj-y += nfs_syms.o
+endif
+
+ifeq ($(CONFIG_NFS_FS),m)
+obj-m   += $(O_TARGET)
+endif
 
 include $(TOPDIR)/Rules.make
diff -r --new-file -u linux.orig/fs/nfs/file.c linux/fs/nfs/file.c
--- linux.orig/fs/nfs/file.c	2002-05-16 21:30:28.000000000 -0400
+++ linux/fs/nfs/file.c	2002-11-20 15:59:06.000000000 -0500
@@ -58,11 +58,6 @@
 	setattr:	nfs_notify_change,
 };
 
-/* Hack for future NFS swap support */
-#ifndef IS_SWAPFILE
-# define IS_SWAPFILE(inode)	(0)
-#endif
-
 /*
  * Flush all dirty pages, and check for write errors.
  *
@@ -217,8 +212,6 @@
 		inode->i_ino, (unsigned long) count, (unsigned long) *ppos);
 
 	result = -EBUSY;
-	if (IS_SWAPFILE(inode))
-		goto out_swapfile;
 	result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
 	if (result)
 		goto out;
@@ -230,10 +223,6 @@
 	result = generic_file_write(file, buf, count, ppos);
 out:
 	return result;
-
-out_swapfile:
-	printk(KERN_INFO "NFS: attempt to write to active swap file!\n");
-	goto out;
 }
 
 /*
diff -r --new-file -u linux.orig/fs/nfs/nfs_syms.c linux/fs/nfs/nfs_syms.c
--- linux.orig/fs/nfs/nfs_syms.c	1969-12-31 19:00:00.000000000 -0500
+++ linux/fs/nfs/nfs_syms.c	2002-11-20 15:59:06.000000000 -0500
@@ -0,0 +1,10 @@
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
+
+EXPORT_SYMBOL(__nfs_refresh_inode);
+EXPORT_SYMBOL(nfs_write_attributes);
+
diff -r --new-file -u linux.orig/fs/nfs/nfsswap.c linux/fs/nfs/nfsswap.c
--- linux.orig/fs/nfs/nfsswap.c	1969-12-31 19:00:00.000000000 -0500
+++ linux/fs/nfs/nfsswap.c	2002-11-20 15:59:06.000000000 -0500
@@ -0,0 +1,333 @@
+/*
+ * Swapping to files located on NFS mounted volumes
+ * Copyright (c) 2000 Claus-Justus Heine
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/swap.h>
+#include <linux/pagemap.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/socket.h>
+#include <linux/smp_lock.h>
+#include <net/netswapping.h>
+#include <net/sock.h>
+
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_fs_sb.h>
+#include <asm/uaccess.h>
+
+#define NFSDBG_FACILITY		NFSDBG_SWAP
+
+#define NFS_SWAP_ID "nfs file"
+
+/* we cache some values here. In principle, we only need the file.
+ */
+struct nfs_swap_data {
+	struct file       *file;
+	struct inode      *inode;
+	struct nfs_server *server;
+	struct socket     *socket;
+};
+
+/* Nearly a clone of nfs_readpage_sync() in read.c, but "struct page" does not
+ * contain information about the file offset when swapping. So.
+ */
+static int nfs_read_swap_page(struct page *page,
+			      unsigned long offset,
+			      struct nfs_server *server,
+			      struct inode *inode,
+			      struct file  *file)
+{
+	unsigned int     rsize   = server->rsize;
+	unsigned int     count   = PAGE_SIZE;
+	u8               *buffer = (char *)kmap(page);
+	int		 result, eof;
+	struct nfs_fattr fattr;
+
+	do {
+		if (count < rsize)
+			rsize = count;
+
+		result = NFS_PROTO(inode)->read(inode, nfs_file_cred(file),
+						&fattr,
+						NFS_RPC_SWAPFLAGS,
+						offset, rsize, buffer, &eof);
+		nfs_refresh_inode(inode, &fattr);
+
+		/*
+		 * Even if we had a partial success we can't mark the page
+		 * cache valid.
+		 */
+		if (result < 0) {
+			if (result == -EISDIR)
+				result = -EINVAL;
+			goto io_error;
+		}
+		count  -= result;
+		offset += result;
+		buffer += result;
+		if (result < rsize)	/* NFSv2ism */
+			break;
+	} while (count);
+
+	memset(buffer, 0, count);
+	flush_dcache_page(page);
+	result = 0;
+
+io_error:
+	return result;
+}
+
+/* Like nfs_writepage_sync(), but when swapping page->index does not encode
+ * the offset in the swap file alone.
+ */
+static int nfs_write_swap_page(struct page *page,
+			       unsigned long offset, 
+			       struct nfs_server *server,
+			       struct inode *inode,
+			       struct file *file)
+{
+	unsigned int     wsize   = server->wsize;
+	unsigned int     count   = PAGE_SIZE;
+	u8               *buffer = (char *)kmap(page);
+	int              result;
+	struct nfs_writeverf verf;
+	struct nfs_fattr fattr;
+
+	do {
+		if (count < wsize)
+			wsize = count;
+
+		result = NFS_PROTO(inode)->write(inode, nfs_file_cred(file),
+						 &fattr,
+						 NFS_RW_SWAP|NFS_RW_SYNC, 
+						 offset, wsize, buffer, &verf);
+		nfs_write_attributes(inode, &fattr);
+
+		if (result < 0) {
+			goto io_error;
+		}
+		if (result != wsize)
+			printk("NFS: short write, wsize=%u, result=%d\n",
+			wsize, result);
+		buffer  += wsize;
+		offset  += wsize;
+		count   -= wsize;
+		/*
+		 * If we've extended the file, update the inode
+		 * now so we don't invalidate the cache.
+		 */
+		if (offset > inode->i_size)
+			inode->i_size = offset;
+	} while (count);
+
+	result = 0;
+
+io_error:
+
+	return result;
+}
+
+static int nfs_rw_swap_page(int rw, struct page *page,
+			    unsigned long offset, void *dptr)
+{
+	int error;
+	struct nfs_swap_data *data = dptr;
+	unsigned long alloc_flag = current->flags & PF_MEMALLOC;
+
+	if (!PageLocked(page))
+		panic("nfs_rw_swap_page: page not locked for I/O");
+
+	lock_kernel(); /* don't know why, but read/write_pagesync() have 
+			* this set, too.
+			*/
+
+	offset *= PAGE_SIZE; /* behold: offset's unit is PAGE_SIZE */
+
+	/* prevent memory deadlocks */
+	if (!(current->flags & PF_MEMALLOC)) {
+		dprintk("nfs_rw_swap_page: Setting PF_MEMALLOC\n");
+	}
+	current->flags |= PF_MEMALLOC;
+
+	if (rw == WRITE) {
+		error = nfs_write_swap_page(page, offset,
+					    data->server,
+					    data->inode,
+					    data->file);
+	} else {
+		error = nfs_read_swap_page(page, offset,
+					   data->server,
+					   data->inode,
+					   data->file);
+	}
+	
+	if (!alloc_flag) {
+		current->flags &= ~PF_MEMALLOC;
+	}
+
+	if (error) {
+		/* Must mark the page invalid after I/O error */
+		SetPageError(page);
+		ClearPageUptodate(page);
+	} else {
+		ClearPageError(page);
+		SetPageUptodate(page);
+	}
+
+	kunmap(page);
+
+	/*
+	 * Run the hooks that have to be done when a page I/O has completed.
+	 */
+	test_and_clear_bit(PG_decr_after, &page->flags);
+	//		atomic_dec(&nr_async_pages);
+
+	UnlockPage(page);
+
+	unlock_kernel();
+	
+	return error < 0 ? 0 : 1;
+}
+
+static int is_nfsfile_swapping(unsigned int flags,
+			       struct file * swapf,
+			       void * data)
+{
+	struct file * filp = (struct file *) data;
+
+	/* Only check filp's that don't match the one already opened
+	 * for us by sys_swapon(). Otherwise, we will always flag a
+	 * busy swap file.
+	 */
+
+	if (swapf != filp) {
+		if (filp->f_dentry->d_inode == swapf->f_dentry->d_inode)
+			return 1;
+	}
+	return 0;
+}
+
+static int nfs_swap_open(struct file *swapf, void **dptr)
+{
+	int error = 0;
+	int swapfilesize;
+	struct nfs_swap_data *data;
+	int on = 1;
+	mm_segment_t fs;
+	struct inode *inode = swapf->f_dentry->d_inode;
+
+	MOD_INC_USE_COUNT;
+
+	if (!S_ISREG(inode->i_mode)) {
+		dprintk("nfs_swap_open: can't handle this swap file: %s\n",
+			swapf->f_dentry->d_name.name);
+		error = 0; /* not for us */
+		goto bad_swap;
+	}
+	/* determine whether this file really is located on an NFS mounted
+	 * volume
+	 */
+	if (!inode->i_sb || inode->i_sb->s_magic != NFS_SUPER_MAGIC) {
+		dprintk("nfs_swap_open: %s is not an NFS file.\n",
+			swapf->f_dentry->d_name.name);
+		error = 0; /* not for us */
+		goto bad_swap;
+	}
+
+	if (swap_run_test(is_nfsfile_swapping, swapf)) {
+		dprintk("nfs_swap_open: already swapping to %s\n",
+			swapf->f_dentry->d_name.name);
+		error = -EBUSY;
+		goto bad_swap;
+	}
+	swapfilesize = inode->i_size >> PAGE_SHIFT;
+	if ((data = kmalloc(sizeof(*data), GFP_KERNEL)) == NULL) {
+		error = -ENOMEM;
+		goto bad_swap;
+	}
+	data->file   = swapf;
+	data->inode  = inode;
+	data->server = NFS_SERVER(inode);
+	data->socket = data->server->client->cl_xprt->sock;
+	
+	/* set socket option SO_SWAPPING */
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	error = sock_setsockopt(data->socket, SOL_SOCKET, SO_SWAPPING,
+				(char *)&on, sizeof(on));
+	set_fs(fs);
+	if (error) {
+		dprintk("nfs_swap_open: error setting SO_SWAPPING\n");
+		goto bad_swap_2;
+	}
+
+	*dptr = data;
+	return swapfilesize;
+
+ bad_swap_2:
+	kfree(data);
+ bad_swap:
+	MOD_DEC_USE_COUNT;
+	return error;
+}
+
+static int nfs_swap_release(struct file *swapf, void *dptr)
+{
+	struct nfs_swap_data *data = (struct nfs_swap_data *)dptr;
+	int off = 0;
+	mm_segment_t fs;
+	int error;
+
+#if 1
+	if (swapf != data->file ||
+	    swapf->f_dentry->d_inode != data->inode ||
+	    !swapf->f_dentry->d_inode->i_sb ||
+	    swapf->f_dentry->d_inode->i_sb->s_magic != NFS_SUPER_MAGIC ||
+	    NFS_SERVER(swapf->f_dentry->d_inode) != data->server ||
+	    data->socket != data->server->client->cl_xprt->sock) {
+		panic("nfs_swap_release: nfs swap data messed up");
+	}
+#endif
+
+	/* remove socket option SO_SWAPPING */
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	error = sock_setsockopt(data->socket, SOL_SOCKET, SO_SWAPPING,
+				(char *)&off, sizeof(off));
+	set_fs(fs);
+	if (error) {
+		dprintk("nfs_swap_open: error clearing SO_SWAPPING\n");
+	}
+	kfree(data);
+	MOD_DEC_USE_COUNT;
+	return error;
+}
+
+static struct swap_ops nfs_swap_ops = {
+	open: nfs_swap_open,
+	release: nfs_swap_release,
+	rw_page: nfs_rw_swap_page
+};
+
+int __init nfs_swap_init(void)
+{
+	(void)register_swap_method(NFS_SWAP_ID, &nfs_swap_ops);
+	return 0;
+}
+
+void __exit nfs_swap_exit(void)
+{
+	unregister_swap_method(NFS_SWAP_ID);
+}
+
+module_init(nfs_swap_init)
+module_exit(nfs_swap_exit)
+
diff -r --new-file -u linux.orig/fs/nfs/read.c linux/fs/nfs/read.c
--- linux.orig/fs/nfs/read.c	2002-11-19 21:36:48.000000000 -0500
+++ linux/fs/nfs/read.c	2002-11-20 15:59:06.000000000 -0500
@@ -49,11 +49,6 @@
  */
 static void	nfs_readpage_result(struct rpc_task *task);
 
-/* Hack for future NFS swap support */
-#ifndef IS_SWAPFILE
-# define IS_SWAPFILE(inode)	(0)
-#endif
-
 static kmem_cache_t *nfs_rdata_cachep;
 
 static __inline__ struct nfs_read_data *nfs_readdata_alloc(void)
@@ -91,7 +86,6 @@
 	int		rsize = NFS_SERVER(inode)->rsize;
 	int		result;
 	int		count = PAGE_CACHE_SIZE;
-	int		flags = IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0;
 	int		eof;
 
 	dprintk("NFS: nfs_readpage_sync(%p)\n", page);
@@ -114,7 +108,7 @@
 			(long long)offset, rsize, buffer);
 
 		lock_kernel();
-		result = NFS_PROTO(inode)->read(inode, cred, &fattr, flags,
+		result = NFS_PROTO(inode)->read(inode, cred, &fattr, 0,
 						offset, rsize, buffer, &eof);
 		nfs_refresh_inode(inode, &fattr);
 		unlock_kernel();
@@ -246,7 +240,7 @@
 	task = &data->task;
 
 	/* N.B. Do we need to test? Never called for swapfile inode */
-	flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
+	flags = RPC_TASK_ASYNC;
 
 	nfs_read_rpcsetup(head, data);
 
@@ -472,8 +466,6 @@
 	}
 
 	error = nfs_readpage_sync(file, inode, page);
-	if (error < 0 && IS_SWAPFILE(inode))
-		printk("Aiee.. nfs swap-in of page failed!\n");
 out:
 	return error;
 
diff -r --new-file -u linux.orig/fs/nfs/write.c linux/fs/nfs/write.c
--- linux.orig/fs/nfs/write.c	2002-11-19 21:36:48.000000000 -0500
+++ linux/fs/nfs/write.c	2002-11-20 15:59:06.000000000 -0500
@@ -49,7 +49,6 @@
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/slab.h>
-#include <linux/swap.h>
 #include <linux/pagemap.h>
 #include <linux/file.h>
 
@@ -91,11 +90,6 @@
 static void	nfs_commit_done(struct rpc_task *);
 #endif
 
-/* Hack for future NFS swap support */
-#ifndef IS_SWAPFILE
-# define IS_SWAPFILE(inode)	(0)
-#endif
-
 static kmem_cache_t *nfs_wdata_cachep;
 
 static __inline__ struct nfs_write_data *nfs_writedata_alloc(void)
@@ -126,7 +120,7 @@
  * For the moment, we just call nfs_refresh_inode().
  */
 static __inline__ int
-nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr)
+__nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr)
 {
 	if ((fattr->valid & NFS_ATTR_FATTR) && !(fattr->valid & NFS_ATTR_WCC)) {
 		fattr->pre_size  = NFS_CACHE_ISIZE(inode);
@@ -137,6 +131,11 @@
 	return nfs_refresh_inode(inode, fattr);
 }
 
+int nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr)
+{
+	return __nfs_write_attributes(inode, fattr);
+}
+
 /*
  * Write a page synchronously.
  * Offset is the data offset within the page.
@@ -148,7 +147,7 @@
 	struct rpc_cred	*cred = NULL;
 	loff_t		base;
 	unsigned int	wsize = NFS_SERVER(inode)->wsize;
-	int		result, refresh = 0, written = 0, flags;
+	int		result, refresh = 0, written = 0;
 	u8		*buffer;
 	struct nfs_fattr fattr;
 	struct nfs_writeverf verf;
@@ -166,15 +165,14 @@
 	buffer = kmap(page) + offset;
 	base = page_offset(page) + offset;
 
-	flags = ((IS_SWAPFILE(inode)) ? NFS_RW_SWAP : 0) | NFS_RW_SYNC;
-
 	do {
-		if (count < wsize && !IS_SWAPFILE(inode))
+		if (count < wsize)
 			wsize = count;
 
-		result = NFS_PROTO(inode)->write(inode, cred, &fattr, flags,
+		result = NFS_PROTO(inode)->write(inode, cred, &fattr,
+						 NFS_RW_SYNC,
 						 base, wsize, buffer, &verf);
-		nfs_write_attributes(inode, &fattr);
+		__nfs_write_attributes(inode, &fattr);
 
 		if (result < 0) {
 			/* Must mark the page invalid after I/O error */
@@ -1058,7 +1056,7 @@
 	 *	  writebacks since the page->count is kept > 1 for as long
 	 *	  as the page has a write request pending.
 	 */
-	nfs_write_attributes(inode, resp->fattr);
+	__nfs_write_attributes(inode, resp->fattr);
 	while (!list_empty(&data->pages)) {
 		req = nfs_list_entry(data->pages.next);
 		nfs_list_remove_request(req);
@@ -1214,7 +1212,7 @@
 	if (nfs_async_handle_jukebox(task))
 		return;
 
-	nfs_write_attributes(inode, resp->fattr);
+	__nfs_write_attributes(inode, resp->fattr);
 	while (!list_empty(&data->pages)) {
 		req = nfs_list_entry(data->pages.next);
 		nfs_list_remove_request(req);
diff -r --new-file -u linux.orig/include/linux/fs.h linux/include/linux/fs.h
--- linux.orig/include/linux/fs.h	2002-11-19 21:36:49.000000000 -0500
+++ linux/include/linux/fs.h	2002-11-20 16:13:59.000000000 -0500
@@ -1497,6 +1497,10 @@
 extern int inode_change_ok(struct inode *, struct iattr *);
 extern int inode_setattr(struct inode *, struct iattr *);
 
+/* for swapping to block devices */
+void create_empty_buffers(struct page *page, kdev_t dev, unsigned long blocksize);
+void end_buffer_io_async(struct buffer_head * bh, int uptodate);
+
 /*
  * Common dentry functions for inclusion in the VFS
  * or in other stackable file systems.  Some of these
diff -r --new-file -u linux.orig/include/linux/mm.h linux/include/linux/mm.h
--- linux.orig/include/linux/mm.h	2002-11-19 21:36:49.000000000 -0500
+++ linux/include/linux/mm.h	2002-11-20 16:13:59.000000000 -0500
@@ -297,6 +297,7 @@
 #define PG_arch_1		13
 #define PG_reserved		14
 #define PG_launder		15	/* written out by VM pressure.. */
+#define PG_decr_after                 5
 
 /* Make it prettier to test the above... */
 #define UnlockPage(page)	unlock_page(page)
@@ -314,6 +315,9 @@
 #define PageLaunder(page)	test_bit(PG_launder, &(page)->flags)
 #define SetPageLaunder(page)	set_bit(PG_launder, &(page)->flags)
 #define ClearPageLaunder(page)	clear_bit(PG_launder, &(page)->flags)
+#define PageDecrAfter(page)	test_bit(PG_decr_after, &(page)->flags)
+#define SetPageDecrAfter(page)	set_bit(PG_decr_after, &(page)->flags)
+#define PageTestandClearDecrAfter(page)	test_and_clear_bit(PG_decr_after, &(page)->flags)
 
 /*
  * The zone field is never updated after free_area_init_core()
diff -r --new-file -u linux.orig/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- linux.orig/include/linux/nfs_fs.h	2002-11-19 21:36:49.000000000 -0500
+++ linux/include/linux/nfs_fs.h	2002-11-20 16:14:04.000000000 -0500
@@ -40,8 +40,8 @@
  */
 #define NFS_MAX_DIRCACHE		16
 
-#define NFS_MAX_FILE_IO_BUFFER_SIZE	32768
-#define NFS_DEF_FILE_IO_BUFFER_SIZE	4096
+#define NFS_MAX_FILE_IO_BUFFER_SIZE	(8*PAGE_SIZE)
+#define NFS_DEF_FILE_IO_BUFFER_SIZE	PAGE_SIZE
 
 /*
  * The upper limit on timeouts for the exponential backoff algorithm.
@@ -205,6 +205,8 @@
 extern int  nfs_writepage(struct page *);
 extern int  nfs_flush_incompatible(struct file *file, struct page *page);
 extern int  nfs_updatepage(struct file *, struct page *, unsigned int, unsigned int);
+extern int nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr);
+
 /*
  * Try to write back everything synchronously (but check the
  * return value!)
@@ -375,6 +377,7 @@
 #define NFSDBG_XDR		0x0020
 #define NFSDBG_FILE		0x0040
 #define NFSDBG_ROOT		0x0080
+#define NFSDBG_SWAP             0x0100
 #define NFSDBG_ALL		0xFFFF
 
 #ifdef __KERNEL__
diff -r --new-file -u linux.orig/include/linux/slab.h linux/include/linux/slab.h
--- linux.orig/include/linux/slab.h	2002-11-19 21:36:49.000000000 -0500
+++ linux/include/linux/slab.h	2002-11-20 16:14:00.000000000 -0500
@@ -38,6 +38,7 @@
 #define	SLAB_NO_REAP		0x00001000UL	/* never reap from the cache */
 #define	SLAB_HWCACHE_ALIGN	0x00002000UL	/* align objs on a h/w cache lines */
 #define SLAB_CACHE_DMA		0x00004000UL	/* use GFP_DMA memory */
+#define SLAB_LOW_GFP_ORDER	0x00008000UL	/* use as low a gfp order as possible */
 #define SLAB_MUST_HWCACHE_ALIGN	0x00008000UL	/* force alignment */
 
 /* flags passed to a constructor func */
diff -r --new-file -u linux.orig/include/linux/swap.h linux/include/linux/swap.h
--- linux.orig/include/linux/swap.h	2002-11-19 21:36:49.000000000 -0500
+++ linux/include/linux/swap.h	2002-11-20 16:13:59.000000000 -0500
@@ -58,15 +58,29 @@
 #define SWAP_MAP_MAX	0x7fff
 #define SWAP_MAP_BAD	0x8000
 
+struct swap_ops {
+	int (*open)(struct file *swapf, void **data);
+	int (*release)(struct file *swapf, void *data);
+	int (*rw_page)(int rw,
+		       struct page *page, unsigned long offset, void *data);
+};
+
+struct swap_method {
+	struct swap_method *next;
+	char * name;
+	struct swap_ops *ops;
+	int use_count;
+};
+
 /*
  * The in-memory structure used to track swap areas.
  */
 struct swap_info_struct {
 	unsigned int flags;
-	kdev_t swap_device;
+	struct file *swap_file;
+	struct swap_method *method;
+	void *data;
 	spinlock_t sdev_lock;
-	struct dentry * swap_file;
-	struct vfsmount *swap_vfsmnt;
 	unsigned short * swap_map;
 	unsigned int lowest_bit;
 	unsigned int highest_bit;
@@ -141,11 +155,15 @@
 extern int total_swap_pages;
 extern unsigned int nr_swapfiles;
 extern struct swap_info_struct swap_info[];
-extern int is_swap_partition(kdev_t);
+extern int register_swap_method(char *name, struct swap_ops *ops);
+extern int unregister_swap_method(char *name);
+extern int swap_run_test(int (*test_fct)(unsigned int flags,
+					 struct file *swap_file,
+					 void *testdata), void *testdata);
 extern void si_swapinfo(struct sysinfo *);
 extern swp_entry_t get_swap_page(void);
-extern void get_swaphandle_info(swp_entry_t, unsigned long *, kdev_t *, 
-					struct inode **);
+struct swap_method *get_swaphandle_info(swp_entry_t entry,
+					unsigned long *offset, void **data);
 extern int swap_duplicate(swp_entry_t);
 extern int swap_count(struct page *);
 extern int valid_swaphandles(swp_entry_t, unsigned long *);
diff -r --new-file -u linux.orig/include/net/netswapping.h linux/include/net/netswapping.h
--- linux.orig/include/net/netswapping.h	1969-12-31 19:00:00.000000000 -0500
+++ linux/include/net/netswapping.h	2002-11-20 16:14:33.000000000 -0500
@@ -0,0 +1,47 @@
+#ifndef _LINUX_NETSWAPPING_H
+#define _LINUX_NETSWAPPING_H
+
+#include <linux/swap.h>
+#include <linux/init.h>
+
+/* It is a mess. Socket options are defined in asm-ARCH/socket.h */
+
+#define SO_SWAPPING 0x00100000 /* hopefully not used by anybody else */
+
+#ifdef __KERNEL__
+
+#define CTL_NETSWAP 0x00100000
+
+enum {
+	NET_SWAP_DROPPED = 1,
+	NET_SWAP_DROP_THRESHOLD = 2,
+	NET_SWAP_SOCK_COUNT = 3
+};
+
+extern unsigned int netswap_free_pages_min;
+extern int netswap_sock_count;
+extern unsigned int netswap_dropped;
+
+/* this is "#defined" and not inline because sock.h includes us, but we need
+ * the "struct sock" definition.
+ */
+#define netswap_low_memory(sk, skb)					   \
+({									   \
+	int _ret = 0;							   \
+									   \
+	if (netswap_sock_count > 0 && /* anybody swapping via network? */  \
+	    !(sk)->swapping &&    /* but we are not needed for swapping */ \
+	    nr_free_pages() < netswap_free_pages_min) { /* so drop us */   \
+		printk("netswap_low_memory: "				   \
+		       "dropping skb 0x%p@0x%p\n", skb, sk);		   \
+		netswap_dropped ++;					   \
+		_ret = 1;						   \
+	}								   \
+	_ret;								   \
+})
+
+extern int __init netswap_init(void);
+
+#endif
+
+#endif
diff -r --new-file -u linux.orig/include/net/sock.h linux/include/net/sock.h
--- linux.orig/include/net/sock.h	2002-11-19 21:36:49.000000000 -0500
+++ linux/include/net/sock.h	2002-11-20 16:14:33.000000000 -0500
@@ -103,6 +103,10 @@
 #include <linux/filter.h>
 #endif
 
+#ifdef CONFIG_NETSWAP
+#include <net/netswapping.h>
+#endif
+
 #include <asm/atomic.h>
 #include <net/dst.h>
 
@@ -539,6 +543,12 @@
 				no_check,
 				broadcast,
 				bsdism;
+#ifdef CONFIG_NETSWAP
+	/* Increased by SO_SWAPPING with arg != 0, decreased by
+	 * SO_SWAPPING with arg 0
+	 */
+	int 		        swapping;
+#endif
 	unsigned char		debug;
 	unsigned char		rcvtstamp;
 	unsigned char		use_write_queue;
@@ -1168,7 +1178,12 @@
 			return err;	/* Toss packet */
 	}
 #endif /* CONFIG_FILTER */
-
+#ifdef CONFIG_NETSWAP
+	/* an inline function defined in net/netswapping.h */
+	if (netswap_low_memory(sk, skb))
+		return -ENOMEM;
+#endif /* CONFIG_NETSWAP */
+ 
 	skb->dev = NULL;
 	skb_set_owner_r(skb, sk);
 	skb_queue_tail(&sk->receive_queue, skb);
diff -r --new-file -u linux.orig/kernel/ksyms.c linux/kernel/ksyms.c
--- linux.orig/kernel/ksyms.c	2002-11-19 21:36:49.000000000 -0500
+++ linux/kernel/ksyms.c	2002-11-20 15:59:06.000000000 -0500
@@ -41,6 +41,7 @@
 #include <linux/mm.h>
 #include <linux/capability.h>
 #include <linux/highuid.h>
+#include <linux/swapctl.h>
 #include <linux/brlock.h>
 #include <linux/fs.h>
 #include <linux/tty.h>
@@ -123,6 +124,11 @@
 EXPORT_SYMBOL(highmem_start_page);
 EXPORT_SYMBOL(create_bounce);
 #endif
+EXPORT_SYMBOL(nr_free_pages);
+/* EXPORT_SYMBOL(freepages); */
+EXPORT_SYMBOL(register_swap_method);
+EXPORT_SYMBOL(unregister_swap_method);
+EXPORT_SYMBOL(swap_run_test);
 
 /* filesystem internal functions */
 EXPORT_SYMBOL(def_blk_fops);
@@ -526,7 +532,8 @@
 EXPORT_SYMBOL(make_bad_inode);
 EXPORT_SYMBOL(is_bad_inode);
 EXPORT_SYMBOL(event);
-EXPORT_SYMBOL(brw_page);
+//EXPORT_SYMBOL(create_empty_buffers);
+EXPORT_SYMBOL(end_buffer_io_async);
 
 #ifdef CONFIG_UID16
 EXPORT_SYMBOL(overflowuid);
diff -r --new-file -u linux.orig/mm/page_io.c linux/mm/page_io.c
--- linux.orig/mm/page_io.c	2002-11-19 21:36:49.000000000 -0500
+++ linux/mm/page_io.c	2002-11-20 15:59:06.000000000 -0500
@@ -36,42 +36,28 @@
 static int rw_swap_page_base(int rw, swp_entry_t entry, struct page *page)
 {
 	unsigned long offset;
-	int zones[PAGE_SIZE/512];
-	int zones_used;
-	kdev_t dev = 0;
-	int block_size;
-	struct inode *swapf = 0;
-
+	struct swap_method *method;
+	void *data;
+	int wait = 0;
+	
 	if (rw == READ) {
 		ClearPageUptodate(page);
 		kstat.pswpin++;
 	} else
 		kstat.pswpout++;
 
-	get_swaphandle_info(entry, &offset, &dev, &swapf);
-	if (dev) {
-		zones[0] = offset;
-		zones_used = 1;
-		block_size = PAGE_SIZE;
-	} else if (swapf) {
-		int i, j;
-		unsigned int block = offset
-			<< (PAGE_SHIFT - swapf->i_sb->s_blocksize_bits);
-
-		block_size = swapf->i_sb->s_blocksize;
-		for (i=0, j=0; j< PAGE_SIZE ; i++, j += block_size)
-			if (!(zones[i] = bmap(swapf,block++))) {
-				printk("rw_swap_page: bad swap file\n");
-				return 0;
-			}
-		zones_used = i;
-		dev = swapf->i_dev;
-	} else {
-		return 0;
-	}
-
- 	/* block_size == PAGE_SIZE/zones_used */
- 	brw_page(rw, page, dev, zones, block_size);
+ 	method = get_swaphandle_info(entry, &offset, &data);
+ 	if (method) {
+	  //if (!wait) {
+	  //	set_bit(PG_decr_after, &page->flags);
+	  //	atomic_inc(&nr_async_pages);
+	  //}
+ 		if (!method->ops->rw_page(rw, page, offset, data)) {
+ 			return 0;
+ 		}
+  	} else {
+  		return 0;
+   	}
 
  	/* Note! For consistency we do all of the logic,
  	 * decrementing the page count, and unlocking the page in the
diff -r --new-file -u linux.orig/mm/slab.c linux/mm/slab.c
--- linux.orig/mm/slab.c	2002-11-19 21:36:49.000000000 -0500
+++ linux/mm/slab.c	2002-11-20 15:59:06.000000000 -0500
@@ -111,10 +111,11 @@
 # define CREATE_MASK	(SLAB_DEBUG_INITIAL | SLAB_RED_ZONE | \
 			 SLAB_POISON | SLAB_HWCACHE_ALIGN | \
 			 SLAB_NO_REAP | SLAB_CACHE_DMA | \
-			 SLAB_MUST_HWCACHE_ALIGN)
+			 SLAB_MUST_HWCACHE_ALIGN | SLAB_LOW_GFP_ORDER)
 #else
 # define CREATE_MASK	(SLAB_HWCACHE_ALIGN | SLAB_NO_REAP | \
-			 SLAB_CACHE_DMA | SLAB_MUST_HWCACHE_ALIGN)
+			 SLAB_CACHE_DMA | SLAB_MUST_HWCACHE_ALIGN | \
+             SLAB_LOW_GFP_ORDER)
 #endif
 
 /*
@@ -452,7 +453,12 @@
 		snprintf(name, sizeof(name), "size-%Zd",sizes->cs_size);
 		if (!(sizes->cs_cachep =
 			kmem_cache_create(name, sizes->cs_size,
-					0, SLAB_HWCACHE_ALIGN, NULL, NULL))) {
+					  0,
+#if CONFIG_NETSWAP
+					  SLAB_LOW_GFP_ORDER|  /* sorry */
+#endif
+					  SLAB_HWCACHE_ALIGN,
+					  NULL, NULL))) {
 			BUG();
 		}
 
@@ -732,6 +738,9 @@
 			break;
 		if (!cachep->num)
 			goto next;
+		if (cachep->gfporder == 0 && (flags & SLAB_LOW_GFP_ORDER))
+			break;
+
 		if (flags & CFLGS_OFF_SLAB && cachep->num > offslab_limit) {
 			/* Oops, this num of objs will cause problems. */
 			cachep->gfporder--;
diff -r --new-file -u linux.orig/mm/swapfile.c linux/mm/swapfile.c
--- linux.orig/mm/swapfile.c	2002-11-19 21:36:49.000000000 -0500
+++ linux/mm/swapfile.c	2002-11-20 15:59:06.000000000 -0500
@@ -11,12 +11,17 @@
 #include <linux/swap.h>
 #include <linux/swapctl.h>
 #include <linux/blkdev.h> /* for blk_size */
+#include <linux/file.h>
 #include <linux/vmalloc.h>
 #include <linux/pagemap.h>
 #include <linux/shm.h>
 
 #include <asm/pgtable.h>
 
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
 spinlock_t swaplock = SPIN_LOCK_UNLOCKED;
 unsigned int nr_swapfiles;
 int total_swap_pages;
@@ -31,8 +36,78 @@
 
 struct swap_info_struct swap_info[MAX_SWAPFILES];
 
+static struct swap_method *swap_methods = NULL;
+
 #define SWAPFILE_CLUSTER 256
 
+int register_swap_method(char *name, struct swap_ops *ops)
+{
+	struct swap_method *pos;
+	struct swap_method *new;
+	int result = 0;
+
+	lock_kernel();
+
+	for (pos = swap_methods; pos; pos = pos->next) {
+		if (strcmp(pos->name, name) == 0) {
+			printk(KERN_ERR "register_swap_method: "
+			       "method %s already registered\n", name);
+			result = -EBUSY;
+			goto out;
+		}
+	}
+
+	if (!(new = kmalloc(sizeof(*new), GFP_KERNEL))) {
+		printk(KERN_ERR "register_swap_method: "
+		       "no memory for new method \"%s\"\n", name);
+		result = -ENOMEM;
+		goto out;
+	}
+
+	new->name      = name;
+	new->ops       = ops;
+	new->use_count = 0;
+
+	/* ok, insert at top of list */
+	printk("register_swap_method: method %s\n", name);
+	new->next    = swap_methods;
+	swap_methods = new;
+ out:
+	unlock_kernel();
+	return result;
+}
+
+int unregister_swap_method(char *name)
+{
+	struct swap_method **method, *next;
+	int result = 0;
+
+	lock_kernel();
+
+	for (method = &swap_methods; *method; method = &(*method)->next) {
+		if (strcmp((*method)->name, name) == 0) {
+			if ((*method)->use_count > 0) {
+				printk(KERN_ERR "unregister_swap_method: "
+				       "method \"%s\" is in use\n", name);
+				result = -EBUSY;
+				goto out;
+			}
+
+			next = (*method)->next;
+			kfree(*method);
+			*method = next;			
+			printk("unregister_swap_method: method %s\n", name);
+			goto out;
+		}
+	}
+	/* not found */
+	printk("unregister_swap_method: no such method %s\n", name);
+	result = -ENOENT;
+ out:
+	unlock_kernel();
+	return result;
+}
+
 static inline int scan_swap_map(struct swap_info_struct *si)
 {
 	unsigned long offset;
@@ -711,13 +786,14 @@
 	struct nameidata nd;
 	int i, type, prev;
 	int err;
+	struct file *swap_file;
 	
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
 	err = user_path_walk(specialfile, &nd);
 	if (err)
-		goto out;
+		return err;
 
 	lock_kernel();
 	prev = -1;
@@ -725,15 +801,20 @@
 	for (type = swap_list.head; type >= 0; type = swap_info[type].next) {
 		p = swap_info + type;
 		if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
-			if (p->swap_file == nd.dentry)
-			  break;
+			if (p->swap_file &&
+			    p->swap_file->f_dentry == nd.dentry)
+				break;
 		}
 		prev = type;
 	}
 	err = -EINVAL;
+	/* p->swap_file contains all needed info, no need to keep nd, so
+	 * release it now.
+	 */
+	path_release(&nd);
 	if (type < 0) {
 		swap_list_unlock();
-		goto out_dput;
+		goto out;
 	}
 
 	if (prev < 0) {
@@ -767,32 +848,30 @@
 		total_swap_pages += p->pages;
 		p->flags = SWP_WRITEOK;
 		swap_list_unlock();
-		goto out_dput;
+		goto out;
 	}
-	if (p->swap_device)
-		blkdev_put(p->swap_file->d_inode->i_bdev, BDEV_SWAP);
-	path_release(&nd);
 
+	if (p->method->ops->release)
+		p->method->ops->release(p->swap_file, p->data);
 	swap_list_lock();
 	swap_device_lock(p);
-	nd.mnt = p->swap_vfsmnt;
-	nd.dentry = p->swap_file;
-	p->swap_vfsmnt = NULL;
+	p->method->use_count --;
+	p->method = NULL;
+	p->data   = NULL;
+	swap_file = p->swap_file;
 	p->swap_file = NULL;
-	p->swap_device = 0;
 	p->max = 0;
 	swap_map = p->swap_map;
 	p->swap_map = NULL;
 	p->flags = 0;
 	swap_device_unlock(p);
 	swap_list_unlock();
+	filp_close(swap_file, NULL);
 	vfree(swap_map);
 	err = 0;
 
-out_dput:
-	unlock_kernel();
-	path_release(&nd);
 out:
+	unlock_kernel();
 	return err;
 }
 
@@ -805,18 +884,17 @@
 	if (!page)
 		return -ENOMEM;
 
-	len += sprintf(buf, "Filename\t\t\tType\t\tSize\tUsed\tPriority\n");
+	len += sprintf(buf, "%-32s%-16s%-8s%-8sPriority\n",
+		       "Filename", "Type", "Size", "Used");
 	for (i = 0 ; i < nr_swapfiles ; i++, ptr++) {
 		if ((ptr->flags & SWP_USED) && ptr->swap_map) {
-			char * path = d_path(ptr->swap_file, ptr->swap_vfsmnt,
-						page, PAGE_SIZE);
+			char * path = d_path(ptr->swap_file->f_dentry,
+					     ptr->swap_file->f_vfsmnt,
+					     page, PAGE_SIZE);
 
 			len += sprintf(buf + len, "%-31s ", path);
 
-			if (!ptr->swap_device)
-				len += sprintf(buf + len, "file\t\t");
-			else
-				len += sprintf(buf + len, "partition\t");
+			len += sprintf(buf + len, "%-15s ", ptr->method->name);
 
 			usedswap = 0;
 			for (j = 0; j < ptr->max; ++j)
@@ -827,7 +905,7 @@
 					default:
 						usedswap++;
 				}
-			len += sprintf(buf + len, "%d\t%d\t%d\n", ptr->pages << (PAGE_SHIFT - 10), 
+			len += sprintf(buf + len, "%-8d%-8d%d\n", ptr->pages << (PAGE_SHIFT - 10), 
 				usedswap << (PAGE_SHIFT - 10), ptr->prio);
 		}
 	}
@@ -835,18 +913,55 @@
 	return len;
 }
 
-int is_swap_partition(kdev_t dev) {
+/* apply a test function to all active swap objects. E.g. for checking
+ * whether a partition is used for swapping
+ */
+int swap_run_test(int (*test_fct)(unsigned int flags,
+				  struct file * swap_file,
+				  void *testdata), void *testdata)
+{
 	struct swap_info_struct *ptr = swap_info;
 	int i;
 
 	for (i = 0 ; i < nr_swapfiles ; i++, ptr++) {
-		if (ptr->flags & SWP_USED)
-			if (ptr->swap_device == dev)
-				return 1;
+		if (ptr->swap_file && 
+		    test_fct(ptr->flags, ptr->swap_file, testdata))
+			return 1;
 	}
 	return 0;
 }
 
+/* Walk through the list of known swap method until somebody wants to
+ * handle this file. Pick the first one which claims to be able to
+ * swap to this kind of file.
+ *
+ * return value: < 0: error, 0: not found, > 0: swapfilesize
+ */
+int find_swap_method(struct file *swap_file,
+		     struct swap_info_struct *p)
+{
+	int swapfilesize = 0;
+	struct swap_method *method;
+
+	p->method = NULL;
+	for (method = swap_methods; method; method = method->next) {
+		swapfilesize = method->ops->open(swap_file, &p->data);
+		if (swapfilesize == 0) {
+			continue;
+		}
+		if (swapfilesize > 0) {
+			p->method = method;
+			p->method->use_count ++;
+			p->swap_file = swap_file;
+			break;
+		}
+		if (swapfilesize < 0) {
+			break;
+		}
+	}
+	return swapfilesize;
+}
+
 /*
  * Written 01/25/92 by Simmule Turner, heavily changed by Linus.
  *
@@ -855,8 +970,6 @@
 asmlinkage long sys_swapon(const char * specialfile, int swap_flags)
 {
 	struct swap_info_struct * p;
-	struct nameidata nd;
-	struct inode * swap_inode;
 	unsigned int type;
 	int i, j, prev;
 	int error;
@@ -866,9 +979,10 @@
 	int nr_good_pages = 0;
 	unsigned long maxpages = 1;
 	int swapfilesize;
-	struct block_device *bdev = NULL;
 	unsigned short *swap_map;
-	
+	char * tmp_specialfile;
+	struct file *swap_file;
+
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 	lock_kernel();
@@ -886,8 +1000,7 @@
 		nr_swapfiles = type+1;
 	p->flags = SWP_USED;
 	p->swap_file = NULL;
-	p->swap_vfsmnt = NULL;
-	p->swap_device = 0;
+	p->method = NULL;
 	p->swap_map = NULL;
 	p->lowest_bit = 0;
 	p->highest_bit = 0;
@@ -901,56 +1014,60 @@
 		p->prio = --least_priority;
 	}
 	swap_list_unlock();
-	error = user_path_walk(specialfile, &nd);
-	if (error)
-		goto bad_swap_2;
-
-	p->swap_file = nd.dentry;
-	p->swap_vfsmnt = nd.mnt;
-	swap_inode = nd.dentry->d_inode;
-	error = -EINVAL;
-
-	if (S_ISBLK(swap_inode->i_mode)) {
-		kdev_t dev = swap_inode->i_rdev;
-		struct block_device_operations *bdops;
-		devfs_handle_t de;
 
-		p->swap_device = dev;
-		set_blocksize(dev, PAGE_SIZE);
-		
-		bd_acquire(swap_inode);
-		bdev = swap_inode->i_bdev;
-		de = devfs_get_handle_from_inode(swap_inode);
-		bdops = devfs_get_ops(de);  /*  Increments module use count  */
-		if (bdops) bdev->bd_op = bdops;
-
-		error = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_SWAP);
-		devfs_put_ops(de);/*Decrement module use count now we're safe*/
-		if (error)
-			goto bad_swap_2;
-		set_blocksize(dev, PAGE_SIZE);
-		error = -ENODEV;
-		if (!dev || (blk_size[MAJOR(dev)] &&
-		     !blk_size[MAJOR(dev)][MINOR(dev)]))
-			goto bad_swap;
-		swapfilesize = 0;
-		if (blk_size[MAJOR(dev)])
-			swapfilesize = blk_size[MAJOR(dev)][MINOR(dev)]
-				>> (PAGE_SHIFT - 10);
-	} else if (S_ISREG(swap_inode->i_mode))
-		swapfilesize = swap_inode->i_size >> PAGE_SHIFT;
-	else
-		goto bad_swap;
+	/* Open the swap using filp_open. Bail out on any errors. */
+ 	tmp_specialfile = getname(specialfile);
+ 	if (IS_ERR(tmp_specialfile)) {
+ 		error = PTR_ERR(tmp_specialfile);
+  		goto bad_swap_2;
+ 	}
+ 	p->swap_file = filp_open(tmp_specialfile, O_RDWR, 0600);
+ 	putname(tmp_specialfile);
+ 	if (IS_ERR(p->swap_file)) {
+ 		error = PTR_ERR(p->swap_file);
+ 		goto bad_swap_1;
+ 	}
+  
+ 	error = -EINVAL;
+ 
+  	swapfilesize = find_swap_method(p->swap_file, p);
+ 	if (swapfilesize < 0) {
+ 		error = swapfilesize;
+ 		goto bad_swap_1;
+ 	}
+#ifdef CONFIG_KMOD
+ 	if (swapfilesize == 0) {
+ 		(void)request_module("swapfile-mod");
+ 
+ 		swapfilesize = find_swap_method(p->swap_file, p);
+ 		if (swapfilesize < 0) {
+ 			error = swapfilesize;
+ 			goto bad_swap_1;
+ 		}
+ 	}
+#endif		
+ 	if (swapfilesize == 0) {
+ 		printk("Don't know how to swap to this kind of file\n");
+ 		goto bad_swap_1; /* free swap map */
+ 	}
+   
+ 	/* After this point, the swap-file has been opened by the swap
+ 	 * method. We must make sure to use the bad_swap label for any
+ 	 * errors.
+ 	 */
 
 	error = -EBUSY;
 	for (i = 0 ; i < nr_swapfiles ; i++) {
 		struct swap_info_struct *q = &swap_info[i];
 		if (i == type || !q->swap_file)
 			continue;
-		if (swap_inode->i_mapping == q->swap_file->d_inode->i_mapping)
+ 		if (p->swap_file->f_dentry->d_inode->i_mapping
+ 		    ==
+ 		    q->swap_file->f_dentry->d_inode->i_mapping)
 			goto bad_swap;
 	}
 
+ 	/* Obtain a free page to store the swap header. */
 	swap_header = (void *) __get_free_page(GFP_USER);
 	if (!swap_header) {
 		printk("Unable to start swapping: out of memory :-)\n");
@@ -1083,17 +1200,27 @@
 	swap_list_unlock();
 	error = 0;
 	goto out;
+
 bad_swap:
-	if (bdev)
-		blkdev_put(bdev, BDEV_SWAP);
+	if (p->method->ops->release)
+		p->method->ops->release(p->swap_file, p->data);
+	swap_list_lock();
+	p->method->use_count --;
+	p->method = NULL;
+	p->data = NULL;
+	swap_list_unlock();
+
+bad_swap_1:
+	swap_list_lock();
+	swap_file = p->swap_file;
+	p->swap_file = NULL;
+	swap_list_unlock();
+	filp_close(swap_file, NULL);
+	
 bad_swap_2:
+
 	swap_list_lock();
 	swap_map = p->swap_map;
-	nd.mnt = p->swap_vfsmnt;
-	nd.dentry = p->swap_file;
-	p->swap_device = 0;
-	p->swap_file = NULL;
-	p->swap_vfsmnt = NULL;
 	p->swap_map = NULL;
 	p->flags = 0;
 	if (!(swap_flags & SWAP_FLAG_PREFER))
@@ -1101,7 +1228,7 @@
 	swap_list_unlock();
 	if (swap_map)
 		vfree(swap_map);
-	path_release(&nd);
+
 out:
 	if (swap_header)
 		free_page((long) swap_header);
@@ -1217,8 +1344,8 @@
 /*
  * Prior swap_duplicate protects against swap device deletion.
  */
-void get_swaphandle_info(swp_entry_t entry, unsigned long *offset, 
-			kdev_t *dev, struct inode **swapf)
+struct swap_method *get_swaphandle_info(swp_entry_t entry,
+					unsigned long *offset, void **data)
 {
 	unsigned long type;
 	struct swap_info_struct *p;
@@ -1226,32 +1353,26 @@
 	type = SWP_TYPE(entry);
 	if (type >= nr_swapfiles) {
 		printk(KERN_ERR "rw_swap_page: %s%08lx\n", Bad_file, entry.val);
-		return;
+		return NULL;
 	}
 
 	p = &swap_info[type];
 	*offset = SWP_OFFSET(entry);
 	if (*offset >= p->max && *offset != 0) {
 		printk(KERN_ERR "rw_swap_page: %s%08lx\n", Bad_offset, entry.val);
-		return;
+		return NULL;
 	}
 	if (p->swap_map && !p->swap_map[*offset]) {
 		printk(KERN_ERR "rw_swap_page: %s%08lx\n", Unused_offset, entry.val);
-		return;
+		return NULL;
 	}
 	if (!(p->flags & SWP_USED)) {
 		printk(KERN_ERR "rw_swap_page: %s%08lx\n", Unused_file, entry.val);
-		return;
+		return NULL;
 	}
 
-	if (p->swap_device) {
-		*dev = p->swap_device;
-	} else if (p->swap_file) {
-		*swapf = p->swap_file->d_inode;
-	} else {
-		printk(KERN_ERR "rw_swap_page: no swap file or device\n");
-	}
-	return;
+	*data = p->data;
+	return p->method;
 }
 
 /*
diff -r --new-file -u linux.orig/net/Config.in linux/net/Config.in
--- linux.orig/net/Config.in	2002-11-19 21:36:49.000000000 -0500
+++ linux/net/Config.in	2002-11-20 15:59:06.000000000 -0500
@@ -16,6 +16,9 @@
 fi
 bool 'Socket Filtering'  CONFIG_FILTER
 tristate 'Unix domain sockets' CONFIG_UNIX
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    bool 'Swapping via network sockets (EXPERIMENTAL)' CONFIG_NETSWAP
+fi
 bool 'TCP/IP networking' CONFIG_INET
 if [ "$CONFIG_INET" = "y" ]; then
    source net/ipv4/Config.in
diff -r --new-file -u linux.orig/net/Makefile linux/net/Makefile
--- linux.orig/net/Makefile	2002-11-19 21:36:49.000000000 -0500
+++ linux/net/Makefile	2002-11-20 15:59:06.000000000 -0500
@@ -51,6 +51,7 @@
 ifeq ($(CONFIG_NET),y)
 obj-$(CONFIG_MODULES)		+= netsyms.o
 obj-$(CONFIG_SYSCTL)		+= sysctl_net.o
+obj-$(CONFIG_NETSWAP)           += netswapping.o
 endif
 
 include $(TOPDIR)/Rules.make
diff -r --new-file -u linux.orig/net/core/sock.c linux/net/core/sock.c
--- linux.orig/net/core/sock.c	2002-11-19 21:36:49.000000000 -0500
+++ linux/net/core/sock.c	2002-11-20 15:59:06.000000000 -0500
@@ -403,6 +403,21 @@
 			ret = -ENONET;
 			break;
 #endif
+#ifdef CONFIG_NETSWAP
+		case SO_SWAPPING:
+			if (valbool) {
+				if (!sk->swapping) {
+					netswap_sock_count ++;
+				}
+				sk->swapping ++;
+			} else if (sk->swapping > 0) {
+				sk->swapping --;
+				if (!sk->swapping) {
+					netswap_sock_count --;
+				}
+			}
+			break;
+#endif
 		/* We implement the SO_SNDLOWAT etc to
 		   not be settable (1003.1g 5.3) */
 		default:
@@ -553,6 +568,12 @@
 			goto lenout;
 		}
 
+#ifdef CONFIG_NETSWAP
+		case SO_SWAPPING:
+			v.val = sk->swapping;
+			break;
+#endif
+
 		/* Dubious BSD thing... Probably nobody even uses it, but
 		 * the UNIX standard wants it for whatever reason... -DaveM
 		 */
diff -r --new-file -u linux.orig/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- linux.orig/net/ipv4/tcp_ipv4.c	2002-11-19 21:36:49.000000000 -0500
+++ linux/net/ipv4/tcp_ipv4.c	2002-11-20 15:59:06.000000000 -0500
@@ -1657,6 +1657,12 @@
 	if (filter && sk_filter(skb, filter))
 		goto discard;
 #endif /* CONFIG_FILTER */
+#ifdef CONFIG_NETSWAP
+	/* tcp doesn't use sock_queue_rcv_skb() ... */
+	/* an inline function defined in net/netswapping.h */
+	if (netswap_low_memory(sk, skb))
+		goto discard;
+#endif /* CONFIG_NETSWAP */
 
   	IP_INC_STATS_BH(IpInDelivers);
 
diff -r --new-file -u linux.orig/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- linux.orig/net/ipv6/tcp_ipv6.c	2002-11-19 21:36:49.000000000 -0500
+++ linux/net/ipv6/tcp_ipv6.c	2002-11-20 15:59:06.000000000 -0500
@@ -1433,6 +1433,12 @@
 	if (filter && sk_filter(skb, filter))
 		goto discard;
 #endif /* CONFIG_FILTER */
+#ifdef CONFIG_NETSWAP
+	/* tcp doesn't use sock_queue_rcv_skb() ... */
+	/* an inline function defined in net/netswapping.h */
+	if (netswap_low_memory(sk, skb))
+		goto discard;
+#endif /* CONFIG_NETSWAP */
 
 	/*
 	 *	socket locking is here for SMP purposes as backlog rcv
diff -r --new-file -u linux.orig/net/netswapping.c linux/net/netswapping.c
--- linux.orig/net/netswapping.c	1969-12-31 19:00:00.000000000 -0500
+++ linux/net/netswapping.c	2002-11-20 15:59:06.000000000 -0500
@@ -0,0 +1,77 @@
+/*
+ * linux/net/swapping.c
+ *
+ * Support paging over network connections (inet only)
+ *
+ * (c) 2000 Claus-Justus Heine <heine@instmath.rwth-aachen.de>
+ */
+
+//#include <linux/malloc.h>
+#include <linux/slab.h>
+#include <linux/swap.h>
+#include <linux/swapctl.h>
+#include <linux/skbuff.h>
+#include <linux/module.h>
+#include <linux/sysctl.h>
+#include <linux/init.h>
+#include <net/netswapping.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+
+unsigned int netswap_dropped; /* statistics */
+unsigned int netswap_free_pages_min;
+int netswap_sock_count;   /* how many sockets have swapping option set */
+
+#ifdef CONFIG_SYSCTL
+
+static ctl_table netswap_table[] = {
+	{NET_SWAP_DROPPED, "dropped",
+	 &netswap_dropped, sizeof(int), 0644, NULL, &proc_dointvec },
+	{NET_SWAP_DROP_THRESHOLD, "threshold",
+	 &netswap_free_pages_min, sizeof(int), 0644, NULL, &proc_dointvec },
+	{NET_SWAP_SOCK_COUNT, "sock_count",
+	 &netswap_sock_count, sizeof(int), 0444, NULL, &proc_dointvec },
+	{0},
+};
+
+static struct ctl_table_header *netswap_sysctl_header;
+
+static ctl_table netswap_net_table[] = {
+	{CTL_NETSWAP, "swapping", NULL, 0, 0555, netswap_table},
+	{0}
+};
+
+static ctl_table netswap_root_table[] = {
+	{CTL_NET, "net", NULL, 0, 0555, netswap_net_table},
+	{0}
+};
+
+#endif
+
+int __init netswap_init(void)
+{
+	/* drop packets when below this threshold */
+	netswap_free_pages_min = 32 /* freepages.min */;
+#ifdef CONFIG_SYSCTL
+	netswap_sysctl_header = register_sysctl_table(netswap_root_table, 0);
+#endif
+	return 0;
+}
+
+void __exit netswap_exit(void)
+{
+#ifdef CONFIG_SYSCTL
+	unregister_sysctl_table(netswap_sysctl_header);
+#endif
+}
+
+/* linux/init.h -- VERY nice :-)
+ *
+ * On the other hand, we have no control over the order the initcalls
+ * are performed ...
+ *
+ * Actually, we are not compiled as module ...
+ */
+
+module_init(netswap_init)
+module_exit(netswap_exit)
diff -r --new-file -u linux.orig/net/netsyms.c linux/net/netsyms.c
--- linux.orig/net/netsyms.c	2002-11-19 21:36:49.000000000 -0500
+++ linux/net/netsyms.c	2002-11-20 15:59:06.000000000 -0500
@@ -588,4 +588,10 @@
 EXPORT_SYMBOL(net_call_rx_atomic);
 EXPORT_SYMBOL(softnet_data);
 
+#ifdef CONFIG_NETSWAP
+EXPORT_SYMBOL(netswap_sock_count);
+EXPORT_SYMBOL(netswap_free_pages_min);
+EXPORT_SYMBOL(netswap_dropped);
+#endif
+
 #endif  /* CONFIG_NET */
diff -r --new-file -u linux.orig/net/packet/af_packet.c linux/net/packet/af_packet.c
--- linux.orig/net/packet/af_packet.c	2002-11-19 21:36:49.000000000 -0500
+++ linux/net/packet/af_packet.c	2002-11-20 15:59:06.000000000 -0500
@@ -453,6 +453,12 @@
 			snaplen = res;
 	}
 #endif /* CONFIG_FILTER */
+#ifdef CONFIG_NETSWAP
+	/* packet doesn't use sock_queue_rcv_skb() ... */
+	/* an inline function defined in net/netswapping.h */
+	if (netswap_low_memory(sk, skb))
+		goto drop_n_restore;
+#endif /* CONFIG_NETSWAP */
 
 	if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf)
 		goto drop_n_acct;
@@ -500,7 +506,7 @@
 	po->stats.tp_drops++;
 	spin_unlock(&sk->receive_queue.lock);
 
-#ifdef CONFIG_FILTER
+#if defined(CONFIG_FILTER) || defined(CONFIG_NETSWAP)
 drop_n_restore:
 #endif
 	if (skb_head != skb->data && skb_shared(skb)) {
@@ -561,6 +567,12 @@
 			snaplen = res;
 	}
 #endif
+#ifdef CONFIG_NETSWAP
+	/* packet doesn't use sock_queue_rcv_skb() ... */
+	/* an inline function defined in net/netswapping.h */
+	if (netswap_low_memory(sk, skb))
+		goto drop_n_restore;
+#endif /* CONFIG_NETSWAP */
 
 	if (sk->type == SOCK_DGRAM) {
 		macoff = netoff = TPACKET_ALIGN(TPACKET_HDRLEN) + 16;
diff -r --new-file -u linux.orig/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- linux.orig/net/sunrpc/sched.c	2002-11-19 21:36:49.000000000 -0500
+++ linux/net/sunrpc/sched.c	2002-11-20 16:04:14.000000000 -0500
@@ -79,10 +79,11 @@
  */
 static spinlock_t rpc_sched_lock = SPIN_LOCK_UNLOCKED;
 
+#if CONFIG_SWAP_VIA_NFS || CONFIG_SWAP_VIA_NFS_MODULE
 /*
  * This is the last-ditch buffer for NFS swap requests
  */
-static u32			swap_buffer[PAGE_SIZE >> 2];
+static u32			swap_buffer[2*PAGE_SIZE >> 2];
 static long			swap_buffer_used;
 
 /*
@@ -96,6 +97,7 @@
 {
 	clear_bit(1, &swap_buffer_used);
 }
+#endif
 
 /*
  * Disable the timer for a given RPC task. Should be called with
@@ -545,6 +547,7 @@
 __rpc_execute(struct rpc_task *task)
 {
 	int		status = 0;
+	unsigned long alloc_flag = current->flags & PF_MEMALLOC;
 
 	dprintk("RPC: %4d rpc_execute flgs %x\n",
 				task->tk_pid, task->tk_flags);
@@ -554,6 +557,13 @@
 		return 0;
 	}
 
+	if (task->tk_flags & RPC_TASK_SWAPPER) {
+		if (!current->flags & PF_MEMALLOC) {
+			dprintk("__rpc_execute: Setting PF_MEMALLOC\n");
+		}
+		current->flags |= PF_MEMALLOC;
+	}
+
  restarted:
 	while (1) {
 		/*
@@ -598,7 +608,8 @@
 			rpc_set_sleeping(task);
 			if (RPC_IS_ASYNC(task)) {
 				spin_unlock_bh(&rpc_queue_lock);
-				return 0;
+				status = 0;
+				goto out;
 			}
 		}
 		spin_unlock_bh(&rpc_queue_lock);
@@ -652,6 +663,10 @@
 	/* Release all resources associated with the task */
 	rpc_release_task(task);
 
+ out:
+	if (!alloc_flag) {
+		current->flags &= ~PF_MEMALLOC;
+	}	
 	return status;
 }
 
@@ -750,10 +765,16 @@
 {
 	u32	*buffer;
 	int	gfp;
+	unsigned long alloc_flag = current->flags & PF_MEMALLOC;
+	void    *ret = NULL;
 
-	if (flags & RPC_TASK_SWAPPER)
+	if (flags & RPC_TASK_SWAPPER) {
 		gfp = GFP_ATOMIC;
-	else if (flags & RPC_TASK_ASYNC)
+		if (!(current->flags & PF_MEMALLOC)) {
+			dprintk("rpc_allocate: Setting PF_MEMALLOC\n");
+		}
+		current->flags |= PF_MEMALLOC;
+	} else if (flags & RPC_TASK_ASYNC)
 		gfp = GFP_RPC;
 	else
 		gfp = GFP_KERNEL;
@@ -761,30 +782,42 @@
 	do {
 		if ((buffer = (u32 *) kmalloc(size, gfp)) != NULL) {
 			dprintk("RPC:      allocated buffer %p\n", buffer);
-			return buffer;
+			ret = buffer;
+			goto out;
 		}
+#if CONFIG_SWAP_VIA_NFS || CONFIG_SWAP_VIA_NFS_MODULE
 		if ((flags & RPC_TASK_SWAPPER) && size <= sizeof(swap_buffer)
 		    && rpc_lock_swapbuf()) {
 			dprintk("RPC:      used last-ditch swap buffer\n");
-			return swap_buffer;
+			ret = swap_buffer;
+			goto out;
 		}
+#endif
 		if (flags & RPC_TASK_ASYNC)
 			return NULL;
 		current->policy |= SCHED_YIELD;
 		schedule();
 	} while (!signalled());
-
-	return NULL;
+ out:
+	if (!alloc_flag) {
+		current->flags &= ~PF_MEMALLOC;
+	}
+	return ret;
 }
 
 void
 rpc_free(void *buffer)
 {
+#if CONFIG_SWAP_VIA_NFS || CONFIG_SWAP_VIA_NFS_MODULE
 	if (buffer != swap_buffer) {
+#endif
 		kfree(buffer);
 		return;
+#if CONFIG_SWAP_VIA_NFS || CONFIG_SWAP_VIA_NFS_MODULE
 	}
 	rpc_unlock_swapbuf();
+	printk("RPC:      Released swap buffer\n");
+#endif
 }
 
 /*
diff -r --new-file -u linux.orig/net/sunrpc/xprt.c linux/net/sunrpc/xprt.c
--- linux.orig/net/sunrpc/xprt.c	2002-11-19 21:36:49.000000000 -0500
+++ linux/net/sunrpc/xprt.c	2002-11-20 15:59:06.000000000 -0500
@@ -1204,7 +1204,8 @@
 	} else if (task->tk_rqstp) {
 		/* We've already been given a request slot: NOP */
 	} else {
-		if (RPCXPRT_CONGESTED(xprt) || !(req = xprt->free))
+		if (!(req = xprt->free) ||
+		    (!RPC_IS_SWAPPER(task) && RPCXPRT_CONGESTED(xprt)))
 			goto out_nofree;
 		/* OK: There's room for us. Grab a free slot and bump
 		 * congestion value */

