patch-2.1.89 linux/mm/page_io.c

Next file: linux/mm/swap_state.c
Previous file: linux/mm/page_alloc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.88/linux/mm/page_io.c linux/mm/page_io.c
@@ -6,6 +6,7 @@
  *  Swap reorganised 29.12.95, 
  *  Asynchronous swapping added 30.12.95. Stephen Tweedie
  *  Removed race in async swapping. 14.4.1996. Bruno Haible
+ *  Add swap of shared pages through the page cache. 20.2.1998. Stephen Tweedie
  */
 
 #include <linux/mm.h>
@@ -27,26 +28,36 @@
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
 
-static struct wait_queue * lock_queue = NULL;
-
 /*
  * Reads or writes a swap page.
  * wait=1: start I/O and wait for completion. wait=0: start asynchronous I/O.
+ * All IO to swap files (as opposed to swap partitions) is done
+ * synchronously.
  *
- * Important prevention of race condition: The first thing we do is set a lock
- * on this swap page, which lasts until I/O completes. This way a
- * write_swap_page(entry) immediately followed by a read_swap_page(entry)
- * on the same entry will first complete the write_swap_page(). Fortunately,
- * not more than one write_swap_page() request can be pending per entry. So
- * all races the caller must catch are: multiple read_swap_page() requests
- * on the same entry.
+ * Important prevention of race condition: the caller *must* atomically 
+ * create a unique swap cache entry for this swap page before calling
+ * rw_swap_page, and must lock that page.  By ensuring that there is a
+ * single page of memory reserved for the swap entry, the normal VM page
+ * lock on that page also doubles as a lock on swap entries.  Having only
+ * one lock to deal with per swap entry (rather than locking swap and memory
+ * independently) also makes it easier to make certain swapping operations
+ * atomic, which is particularly important when we are trying to ensure 
+ * that shared pages stay shared while being swapped.
  */
+
 void rw_swap_page(int rw, unsigned long entry, char * buf, int wait)
 {
 	unsigned long type, offset;
 	struct swap_info_struct * p;
 	struct page *page = mem_map + MAP_NR(buf);
 
+#ifdef DEBUG_SWAP
+	printk ("DebugVM: %s_swap_page entry %08lx, page %p (count %d), %s\n",
+		(rw == READ) ? "read" : "write", 
+		entry, buf, atomic_read(&page->count),
+		wait ? "wait" : "nowait");
+#endif
+
 	if (page->inode && page->inode != &swapper_inode)
 		panic ("Tried to swap a non-swapper page");
 	type = SWP_TYPE(entry);
@@ -61,31 +72,28 @@
 		return;
 	}
 	if (p->swap_map && !p->swap_map[offset]) {
-		printk("Hmm.. Trying to use unallocated swap (%08lx)\n", entry);
+		printk("Hmm.. Trying to %s unallocated swap (%08lx)\n", 
+		       (rw == READ) ? "read" : "write", 
+		       entry);
 		return;
 	}
 	if (!(p->flags & SWP_USED)) {
 		printk("Trying to swap to unused swap-device\n");
 		return;
 	}
-	/*
-	 * For now, this is not legal!
-	 */
-	if (PageSwapCache(page))
-		panic ("Trying to swap a swap-cache page!");
 
-	/* Make sure we are the only process doing I/O with this swap page. */
-	while (test_and_set_bit(offset,p->swap_lockmap)) {
-		run_task_queue(&tq_disk);
-		sleep_on(&lock_queue);
+	if (!PageLocked(page)) {
+		printk("VM: swap page is unlocked\n");
+		return;
 	}
-
-	if (rw == READ)
+	
+	if (rw == READ) {
+		clear_bit(PG_uptodate, &page->flags);
 		kstat.pswpin++;
-	else
+	} else
 		kstat.pswpout++;
+
 	atomic_inc(&page->count);
-	wait_on_page(page);
 	/* 
 	 * Make sure that we have a swap cache association for this
 	 * page.  We need this to find which swap page to unlock once
@@ -94,17 +102,19 @@
 	 * as if it were: we are not allowed to manipulate the inode
 	 * hashing for locked pages.
 	 */
-	if (PageSwapCache(page)) {
-		if (page->offset != entry)
-			panic ("swap entry mismatch");
-	} else 
-		page->offset = entry;
+	if (!PageSwapCache(page)) {
+		printk("VM: swap page is not in swap cache\n");
+		return;
+	}
+	if (page->offset != entry) {
+		printk ("swap entry mismatch");
+		return;
+	}
 
 	if (p->swap_device) {
 		if (!wait) {
 			set_bit(PG_free_after, &page->flags);
 			set_bit(PG_decr_after, &page->flags);
-			set_bit(PG_swap_unlock_after, &page->flags);
 			atomic_inc(&nr_async_pages);
 		}
 		ll_rw_page(rw,p->swap_device,offset,buf);
@@ -155,38 +165,51 @@
 				}
 		}
 		ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf);
+		/* Unlike ll_rw_page, ll_rw_swap_file won't unlock the
+		   page for us. */
+		clear_bit(PG_locked, &page->flags);
+		wake_up(&page->wait);
 	} else
 		printk("rw_swap_page: no swap file or device\n");
 
 	atomic_dec(&page->count);
-	if (offset && !test_and_clear_bit(offset,p->swap_lockmap))
-		printk("rw_swap_page: lock already cleared\n");
-	wake_up(&lock_queue);
+#ifdef DEBUG_SWAP
+	printk ("DebugVM: %s_swap_page finished on page %p (count %d)\n",
+		(rw == READ) ? "read" : "write", 
+		buf, atomic_read(&page->count));
+#endif
 }
 
-
-/* This is run when asynchronous page I/O has completed. */
-void swap_after_unlock_page (unsigned long entry)
+/*
+ * Setting up a new swap file needs a simple wrapper just to read the 
+ * swap signature.  SysV shared memory also needs a simple wrapper.
+ */
+void rw_swap_page_nocache(int rw, unsigned long entry, char *buffer)
 {
-	unsigned long type, offset;
-	struct swap_info_struct * p;
-
-	type = SWP_TYPE(entry);
-	if (type >= nr_swapfiles) {
-		printk("swap_after_unlock_page: bad swap-device\n");
+	struct page *page;
+	
+	page = mem_map + MAP_NR((unsigned long) buffer);
+	wait_on_page(page);
+	set_bit(PG_locked, &page->flags);
+	if (test_and_set_bit(PG_swap_cache, &page->flags)) {
+		printk ("VM: read_swap_page: page already in swap cache!\n");
 		return;
 	}
-	p = &swap_info[type];
-	offset = SWP_OFFSET(entry);
-	if (offset >= p->max) {
-		printk("swap_after_unlock_page: weirdness\n");
+	if (page->inode) {
+		printk ("VM: read_swap_page: page already in page cache!\n");
 		return;
 	}
-	if (!test_and_clear_bit(offset,p->swap_lockmap))
-		printk("swap_after_unlock_page: lock already cleared\n");
-	wake_up(&lock_queue);
+	page->inode = &swapper_inode;
+	page->offset = entry;
+	atomic_inc(&page->count);	/* Protect from shrink_mmap() */
+	rw_swap_page(rw, entry, buffer, 1);
+	atomic_dec(&page->count);
+	page->inode = 0;
+	clear_bit(PG_swap_cache, &page->flags);
 }
 
+
+
 /*
  * Swap partitions are now read via brw_page.  ll_rw_page is an
  * asynchronous function now --- we must call wait_on_page afterwards
@@ -211,7 +234,7 @@
 			panic("ll_rw_page: bad block dev cmd, must be R/W");
 	}
 	page = mem_map + MAP_NR(buffer);
-	if (test_and_set_bit(PG_locked, &page->flags))
-		panic ("ll_rw_page: page already locked");
+	if (!PageLocked(page))
+		panic ("ll_rw_page: page not already locked");
 	brw_page(rw, page, dev, &block, PAGE_SIZE, 0);
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov