From: Hugh Dickins <hugh@veritas.com>

Good example of "swapper_space considered harmful": swap_unplug_io_fn was
originally designed for calling via swapper_space.backing_dev_info; but
that way it loses track of which device is to be unplugged, so had to
unplug all swap devices.  But now sync_page tests SwapCache anyway, can
call swap_unplug_io_fn with page, which leads direct to the device.

Reverted -mc4's CONFIG_SWAP=n fix, just add another NOTHING for it. 
Reverted -mc3's editorial adjustments to swap_backing_dev_info and
swapper_space initializations: they document the few fields which are
actually used now, as comment above them says (sound of slapped wrist).


---

 25-akpm/include/linux/swap.h |    5 ++---
 25-akpm/mm/filemap.c         |    2 +-
 25-akpm/mm/nommu.c           |    5 -----
 25-akpm/mm/swap_state.c      |    4 ++--
 25-akpm/mm/swapfile.c        |   23 +++++++++++++++--------
 5 files changed, 20 insertions(+), 19 deletions(-)

diff -puN include/linux/swap.h~rmap-5-swap_unplug-page include/linux/swap.h
--- 25/include/linux/swap.h~rmap-5-swap_unplug-page	2004-04-11 12:39:03.598313048 -0700
+++ 25-akpm/include/linux/swap.h	2004-04-11 12:39:03.608311528 -0700
@@ -181,8 +181,6 @@ extern int vm_swappiness;
 extern int shmem_unuse(swp_entry_t entry, struct page *page);
 #endif /* CONFIG_MMU */
 
-extern void swap_unplug_io_fn(struct backing_dev_info *);
-
 #ifdef CONFIG_SWAP
 /* linux/mm/page_io.c */
 extern int swap_readpage(struct file *, struct page *);
@@ -218,7 +216,7 @@ extern sector_t map_swap_page(struct swa
 extern struct swap_info_struct *get_swap_info_struct(unsigned);
 extern int can_share_swap_page(struct page *);
 extern int remove_exclusive_swap_page(struct page *);
-struct backing_dev_info;
+extern void swap_unplug_io_fn(struct page *);
 
 extern struct swap_list_t swap_list;
 extern spinlock_t swaplock;
@@ -252,6 +250,7 @@ extern spinlock_t swaplock;
 #define move_from_swap_cache(p, i, m)		1
 #define __delete_from_swap_cache(p)		/*NOTHING*/
 #define delete_from_swap_cache(p)		/*NOTHING*/
+#define swap_unplug_io_fn(p)			/*NOTHING*/
 
 static inline int remove_exclusive_swap_page(struct page *p)
 {
diff -puN mm/filemap.c~rmap-5-swap_unplug-page mm/filemap.c
--- 25/mm/filemap.c~rmap-5-swap_unplug-page	2004-04-11 12:39:03.600312744 -0700
+++ 25-akpm/mm/filemap.c	2004-04-11 12:39:03.609311376 -0700
@@ -127,7 +127,7 @@ static inline int sync_page(struct page 
 		if (mapping->a_ops && mapping->a_ops->sync_page)
 			return mapping->a_ops->sync_page(page);
 	} else if (PageSwapCache(page)) {
-		swap_unplug_io_fn(NULL);
+		swap_unplug_io_fn(page);
 	}
 	return 0;
 }
diff -puN mm/nommu.c~rmap-5-swap_unplug-page mm/nommu.c
--- 25/mm/nommu.c~rmap-5-swap_unplug-page	2004-04-11 12:39:03.601312592 -0700
+++ 25-akpm/mm/nommu.c	2004-04-11 12:39:03.610311224 -0700
@@ -18,7 +18,6 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/blkdev.h>
-#include <linux/backing-dev.h>
 
 #include <asm/pgalloc.h>
 #include <asm/uaccess.h>
@@ -572,7 +571,3 @@ unsigned long get_unmapped_area(struct f
 void pte_chain_init(void)
 {
 }
-
-void swap_unplug_io_fn(struct backing_dev_info *)
-{
-}
diff -puN mm/swapfile.c~rmap-5-swap_unplug-page mm/swapfile.c
--- 25/mm/swapfile.c~rmap-5-swap_unplug-page	2004-04-11 12:39:03.603312288 -0700
+++ 25-akpm/mm/swapfile.c	2004-04-11 12:39:03.612310920 -0700
@@ -86,19 +86,26 @@ static void remove_swap_bdev(struct bloc
 	BUG();
 }
 
-void swap_unplug_io_fn(struct backing_dev_info *unused_bdi)
+/*
+ * Unlike a standard unplug_io_fn, swap_unplug_io_fn is never called
+ * through swap's backing_dev_info (which is only used by shrink_list),
+ * but directly from sync_page when PageSwapCache: and takes the page
+ * as argument, so that it can find the right device from swp_entry_t.
+ */
+void swap_unplug_io_fn(struct page *page)
 {
-	int i;
+	swp_entry_t entry;
 
 	down(&swap_bdevs_sem);
-	for (i = 0; i < MAX_SWAPFILES; i++) {
-		struct block_device *bdev = swap_bdevs[i];
+	entry.val = page->private;
+	if (PageSwapCache(page)) {
+		struct block_device *bdev = swap_bdevs[swp_type(entry)];
 		struct backing_dev_info *bdi;
 
-		if (bdev == NULL)
-			break;
-		bdi = bdev->bd_inode->i_mapping->backing_dev_info;
-		(*bdi->unplug_io_fn)(bdi);
+		if (bdev) {
+			bdi = bdev->bd_inode->i_mapping->backing_dev_info;
+			(*bdi->unplug_io_fn)(bdi);
+		}
 	}
 	up(&swap_bdevs_sem);
 }
diff -puN mm/swap_state.c~rmap-5-swap_unplug-page mm/swap_state.c
--- 25/mm/swap_state.c~rmap-5-swap_unplug-page	2004-04-11 12:39:03.605311984 -0700
+++ 25-akpm/mm/swap_state.c	2004-04-11 12:39:03.611311072 -0700
@@ -25,13 +25,13 @@ static struct address_space_operations s
 };
 
 static struct backing_dev_info swap_backing_dev_info = {
-	.memory_backed	= 1,	/* Does not contribute to dirty memory */
-	.unplug_io_fn	= swap_unplug_io_fn,
+	.state		= 0,	/* uncongested */
 };
 
 struct address_space swapper_space = {
 	.page_tree	= RADIX_TREE_INIT(GFP_ATOMIC),
 	.tree_lock	= SPIN_LOCK_UNLOCKED,
+	.nrpages	= 0,	/* total_swapcache_pages */
 	.a_ops		= &swap_aops,
 	.backing_dev_info = &swap_backing_dev_info,
 };

_