ext2fs/
2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>

	In essence, block layer (disk_cache_* functions) is added and
	used, instead of mapping the whole store to address space.
	libpager is still used, so that microkernel decides which blocks
	are least used.

	* ext2fs.h: Include <hurd/store.h> and <hurd/ihash.h>.
	(DISK_CACHE_BLOCKS): New macro constant.
	Remove declaration for disk_image.
	Add declarations for disk_cache, disk_cache_size and
	disk_cache_blocks.
	(DC_INCORE): New macro constant.
	(DC_REMAPPING): Likewise.
	(DC_FIXED): Likewise.
	(DC_DONT_REUSE): Likewise.
	(struct disk_cache_info): New struct.
	Add declarations for disk_cache_bptr, disk_cache_info,
	disk_cache_lock and disk_cache_remapping.
	Add declarations for functions disk_cache_map,
	disk_cache_block_ref, disk_cache_block_ref_ptr,
	disk_cache_block_deref and disk_cache_block_is_ref.
	Add declaration for map_hypermetadata.
	(bptr_index): New macro.
	(boffs_ptr): Instead of macro, now it's inline function that uses
	disk_cache_bptr.
	(bptr_offs): Likewise.
	(dino): Renamed to...
	(dino_ref): ...this function that uses disk_cache functions.
	(dino_deref): New function.
	(record_global_poke): Use disk_cache functions.
	(sync_global_ptr): Likewise.
	(record_indir_poke): Likewise.

	* pager.c: #include <unistd.h>
	Removed definition of disk_image.
	(file_pager_read_page): New argument NOTIFY_ON_FLUSH.
	Debug message when this function is called.
	Use store_offset_t instead of block_t for DEV_BLOCK.
	Use memcpy instead of bcopy.  Use memset instead of bzero.
	(pending_blocks_write): Use store_offset_t instead of block_t for
	DEV_BLOCK.
	Use %Ld instead of %ld for PB->num.
	Use memcpy instead of bcopy.
	(file_pager_write_page): Use %Ld instead of %d for
	NODE->cache_id and %u instead of %d for OFFSET.
	(disk_pager_read_page): New argument NOTIFY_ON_FLUSH.
	New local variable OFFSET.
	Use disk_cache_info to find which block has to be read.
	Set DC_INCORE and clear DC_REMAPPING for this block.
	Use memset instead of bzero.
	(disk_pager_write_page): Use disk_cache_info to find which block
	has to be written.
	New local variable OFFSET instead of OFFS.
	Use %Ld instead of %d for OFFSET (previously it was PAGE).
	(disk_pager_notify_pageout): New function.
	(pager_read_page): New argument NOTIFY_ON_FLUSH.
	Use this argument when calling file_pager_write_page and
	disk_pager_read_page.
	(pager_notify_pageout): New function.
	(pager_unlock_page): Use %Ld instead of %d for NODE->cache_id.
	(diskfs_grow): Use %Ld instead of %d for NODE->cache_id.
	Use %Lu instead of %u for NEW_SIZE and OLD_SIZE.
	Add definitions for disk_cache, disk_cache_size,
	disk_cache_blocks, disk_cache_bptr, disk_cache_info and
	disk_cache_hint.
	Add definitions for synchronizing variables disk_cache_lock and
	disk_cache_remapping.
	(disk_cache_init): New function.
	(disk_cache_wait_remapping): New function.
	(disk_cache_map): New function.
	(disk_cache_block_ref): New function.
	(disk_cache_block_ref_ptr): New function.
	(disk_cache_block_deref): New function.
	(disk_cache_block_is_ref): New function.
	(create_disk_pager): In addition to calling
	diskfs_start_disk_pager with disk_cache instead of disk_image, get
	hypermetadata and initialize disk_cache.

	* pokel.c (pokel_add): Call disk_cache_block_deref for blocks
	overlapping with another pokel entry.
	In search loop, instead of breaking when there is exact	match,
	break when given data is completely overlapped by pokel entry.
	(_pokel_exec): Call disk_cache_block_deref for all flushed or
	synced blocks.

	* inode.c (diskfs_cached_lookup): Pass disk_cache to pokel_init
	instead of disk_image.
	(read_node): Use dino_ref and dino_deref instead of dino.
	Debug message that read_node is entered.
	Call diskfs_end_catch_exception before returning EFBIG.
	Use round_block macro instead of calculating.
	(write_node): Use dino_ref and dino_deref instead of dino.
	Debug message that read_node is entered.
	Use %Ld instead of %u for NP->cache_id.
	(diskfs_write_disknode): Call dino_deref as required by
	write_node.
	(diskfs_set_translator): Use dino_ref and dino_deref instead of
	dino.
	Use memcpy instead of bcopy.
	Use disk_cache_block_ref and disk_cache_block_deref instead of
	bptr.
	(diskfs_get_translator): Use dino_ref and dino_deref instead of
	dino.
	Use disk_cache_block_ref and disk_cache_block_deref instead of
	bptr.
	(write_symlink): Use memcpy instead of bcopy.
	(read_symlink): Use memcpy instead of bcopy.

	* ialloc.c: Use disk_cache_block_ref instead of bptr.
	Call disk_cache_block_deref when BH is no more used.
	Call disk_cache_block_ref_ptr before record_global_poke when
	block will be used more.
	(diskfs_free_node): Use %Lu instead of %u for INUM.
	(ext2_alloc_inode):  BH is initialized with 0.  After each goto
	label, an assertion whether it's ==0 or !=0 is made.  After
	each direct or indirect disk_cache_block_deref(BH), BH is zeroed.
	Zero inode before returning INUM.

	* getblk.c: Use disk_cache_block_ref instead of bptr.
	Call disk_cache_block_deref when BH is no more used.
	(ext2_discard_prealloc): Use %Ld instead of %d for
	NODE->cache_id.
	(ext2_alloc_block): Use memset instead of bzero.
	(inode_getblk): Add assertion that NR must be unsigned below
	EXT2_N_BLOCKS.

	* balloc.c: Use disk_cache_block_ref instead of bptr.
	Call disk_cache_block_deref before BH=0.
	Call disk_cache_block_ref_ptr(GDP) before
	record_global_poke(GDP).
	(ext2_new_block): BH is initialized with 0.  After each goto
	label, an assertion whether it's ==0 or !=0 is made.

	* truncate.c (trunc_indirect): Use disk_cache_block_ref and
	disk_cache_block_deref instead of bptr.
	Use bptr_index instead of boffs.

	* hyper.c (get_hypermetadata): Use store_read to read superblock.
	Move initalization of group_desc to map_hypermetadata.
	(map_hypermetadata): New function.
	(diskfs_set_hypermetadata): Call disk_cache_block_ref_ptr(sblock)
	before record_global_poke(sblock).
	(diskfs_readonly_changed): mprotect disk_cache instead of
	disk_image.

	* ext2fs.c (parse_opt): Use memset instead of bzero.
	(main): Pass disk_cache to pokel_init instead of disk_image.
	Call map_hypermetadata instead of get_hypermetadata.
	(diskfs_reload_global_state): Set SBLOCK to 0.
	Call map_hypermetadata.

libpager/
2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>

	* priv.h (PM_FORCEREAD): New macro constant.
	(PM_NOTIFY_PAGEOUT): Likewise.

	* pager.h (pager_read_page): New argument NOTIFY_ON_PAGEOUT.
	(pager_notify_pageout): New function declaration.

	* lock-object.c (_pager_lock_object): When flushing page and it
	has PM_NOTIFY_PAGEOUT, set PM_FORCEREAD to assure that fast
	data return + data_request will re-read page, that is, call
	callback pager_read_page.

	* data-return.c (_pager_do_write_request): New local variable
	NOTIFIED which holds which pages should be notified on pageout.
	When there is no kernel copy of page, call PAGER_NOTIFY_PAGEOUT,
	including when pages are not dirty.
	Don't memory_object_data_supply when
	_pager_seqnos_memory_object_data_request waits for that page in
	order to read it.

	* data-request.c (_pager_seqnos_memory_object_data_request): New
	local variable NOTIFY_ON_PAGEOUT.
	Move call to _pager_block_termination lower.
	Use goto release_out instead of stuff + goto allow_term_out.
	Call _pager_allow_termination before goto release_out when
	_pager_pagemap_resize fails.
	When PM_PAGINGOUT, wait paging out to finish instead of just
	skipping the read.
	Call callback pager_read_page with argument &NOTIFY_ON_PAGEOUT and
	mark *PM_ENTRY accordingly.
	Use NOTIFY_ON_PAGEOUT when calling memory_object_data_supply.

console/
2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>

	* pager.c (pager_read_page): New argument: NOTIFY_ON_PAGEOUT.
	(pager_notify_pageout): New function.
	
fatfs/
2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>

	* pager.c (pager_read_page): New argument NOTIFY_ON_PAGEOUT.
	(pager_notify_pageout): New function.

isofs/
2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>

	* pager.c (pager_read_page): New argument NOTIFY_ON_PAGEOUT.
	(pager_notify_pageout): New function.

storeio/
2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>

	* pager.c (pager_read_page): New argument NOTIFY_ON_PAGEOUT.
	(pager_notify_pageout): New function.

tmpfs/
2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>

	* pager-stubs.c (pager_read_page): New argument
	NOTIFY_ON_PAGEOUT.
	(pager_notify_pageout): New function.

ufs/
2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>

	* pager.c (pager_read_page): New argument NOTIFY_ON_PAGEOUT.
	(pager_notify_pageout): New function.


diff -urpN --exclude='*~' --exclude=CVS --exclude='*.[ch]' ../../cvs/hurd/console/ChangeLog hurd/console/ChangeLog
--- ../../cvs/hurd/console/ChangeLog	2003-06-04 00:19:00.000000000 +0300
+++ hurd/console/ChangeLog	2004-01-21 09:29:14.000000000 +0200
@@ -1,3 +1,8 @@
+2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>
+
+	* pager.c (pager_read_page): New argument: NOTIFY_ON_PAGEOUT.
+	(pager_notify_pageout): New function.
+
 2003-04-29  Marco Gerards   <metgerards@student.han.nl>
 
 	* display.c (display_create): Added arguments width, height and
diff -urpN --exclude='*~' --exclude=CVS --exclude='*.[ch]' ../../cvs/hurd/ext2fs/ChangeLog hurd/ext2fs/ChangeLog
--- ../../cvs/hurd/ext2fs/ChangeLog	2004-01-11 00:09:06.000000000 +0200
+++ hurd/ext2fs/ChangeLog	2004-01-21 11:05:07.000000000 +0200
@@ -1,3 +1,155 @@
+2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>
+
+	In essence, block layer (disk_cache_* functions) is added and
+	used, instead of mapping the whole store to address space.
+	libpager is still used, so that microkernel decides which blocks
+	are least used.
+
+	* ext2fs.h: Include <hurd/store.h> and <hurd/ihash.h>.
+	(DISK_CACHE_BLOCKS): New macro constant.
+	Remove declaration for disk_image.
+	Add declarations for disk_cache, disk_cache_size and
+	disk_cache_blocks.
+	(DC_INCORE): New macro constant.
+	(DC_REMAPPING): Likewise.
+	(DC_FIXED): Likewise.
+	(DC_DONT_REUSE): Likewise.
+	(struct disk_cache_info): New struct.
+	Add declarations for disk_cache_bptr, disk_cache_info,
+	disk_cache_lock and disk_cache_remapping.
+	Add declarations for functions disk_cache_map,
+	disk_cache_block_ref, disk_cache_block_ref_ptr,
+	disk_cache_block_deref and disk_cache_block_is_ref.
+	Add declaration for map_hypermetadata.
+	(bptr_index): New macro.
+	(boffs_ptr): Instead of macro, now it's inline function that uses
+	disk_cache_bptr.
+	(bptr_offs): Likewise.
+	(dino): Renamed to...
+	(dino_ref): ...this function that uses disk_cache functions.
+	(dino_deref): New function.
+	(record_global_poke): Use disk_cache functions.
+	(sync_global_ptr): Likewise.
+	(record_indir_poke): Likewise.
+
+	* pager.c: #include <unistd.h>
+	Removed definition of disk_image.
+	(file_pager_read_page): New argument NOTIFY_ON_FLUSH.
+	Debug message when this function is called.
+	Use store_offset_t instead of block_t for DEV_BLOCK.
+	Use memcpy instead of bcopy.  Use memset instead of bzero.
+	(pending_blocks_write): Use store_offset_t instead of block_t for
+	DEV_BLOCK.
+	Use %Ld instead of %ld for PB->num.
+	Use memcpy instead of bcopy.
+	(file_pager_write_page): Use %Ld instead of %d for
+	NODE->cache_id and %u instead of %d for OFFSET.
+	(disk_pager_read_page): New argument NOTIFY_ON_FLUSH.
+	New local variable OFFSET.
+	Use disk_cache_info to find which block has to be read.
+	Set DC_INCORE and clear DC_REMAPPING for this block.
+	Use memset instead of bzero.
+	(disk_pager_write_page): Use disk_cache_info to find which block
+	has to be written.
+	New local variable OFFSET instead of OFFS.
+	Use %Ld instead of %d for OFFSET (previously it was PAGE).
+	(disk_pager_notify_pageout): New function.
+	(pager_read_page): New argument NOTIFY_ON_FLUSH.
+	Use this argument when calling file_pager_write_page and
+	disk_pager_read_page.
+	(pager_notify_pageout): New function.
+	(pager_unlock_page): Use %Ld instead of %d for NODE->cache_id.
+	(diskfs_grow): Use %Ld instead of %d for NODE->cache_id.
+	Use %Lu instead of %u for NEW_SIZE and OLD_SIZE.
+	Add definitions for disk_cache, disk_cache_size,
+	disk_cache_blocks, disk_cache_bptr, disk_cache_info and
+	disk_cache_hint.
+	Add definitions for synchronizing variables disk_cache_lock and
+	disk_cache_remapping.
+	(disk_cache_init): New function.
+	(disk_cache_wait_remapping): New function.
+	(disk_cache_map): New function.
+	(disk_cache_block_ref): New function.
+	(disk_cache_block_ref_ptr): New function.
+	(disk_cache_block_deref): New function.
+	(disk_cache_block_is_ref): New function.
+	(create_disk_pager): In addition to calling
+	diskfs_start_disk_pager with disk_cache instead of disk_image, get
+	hypermetadata and initialize disk_cache.
+
+	* pokel.c (pokel_add): Call disk_cache_block_deref for blocks
+	overlapping with another pokel entry.
+	In search loop, instead of breaking when there is exact	match,
+	break when given data is completely overlapped by pokel entry.
+	(_pokel_exec): Call disk_cache_block_deref for all flushed or
+	synced blocks.
+
+	* inode.c (diskfs_cached_lookup): Pass disk_cache to pokel_init
+	instead of disk_image.
+	(read_node): Use dino_ref and dino_deref instead of dino.
+	Debug message that read_node is entered.
+	Call diskfs_end_catch_exception before returning EFBIG.
+	Use round_block macro instead of calculating.
+	(write_node): Use dino_ref and dino_deref instead of dino.
+	Debug message that read_node is entered.
+	Use %Ld instead of %u for NP->cache_id.
+	(diskfs_write_disknode): Call dino_deref as required by
+	write_node.
+	(diskfs_set_translator): Use dino_ref and dino_deref instead of
+	dino.
+	Use memcpy instead of bcopy.
+	Use disk_cache_block_ref and disk_cache_block_deref instead of
+	bptr.
+	(diskfs_get_translator): Use dino_ref and dino_deref instead of
+	dino.
+	Use disk_cache_block_ref and disk_cache_block_deref instead of
+	bptr.
+	(write_symlink): Use memcpy instead of bcopy.
+	(read_symlink): Use memcpy instead of bcopy.
+
+	* ialloc.c: Use disk_cache_block_ref instead of bptr.
+	Call disk_cache_block_deref when BH is no more used.
+	Call disk_cache_block_ref_ptr before record_global_poke when
+	block will be used more.
+	(diskfs_free_node): Use %Lu instead of %u for INUM.
+	(ext2_alloc_inode):  BH is initialized with 0.  After each goto
+	label, an assertion whether it's ==0 or !=0 is made.  After
+	each direct or indirect disk_cache_block_deref(BH), BH is zeroed.
+	Zero inode before returning INUM.
+
+	* getblk.c: Use disk_cache_block_ref instead of bptr.
+	Call disk_cache_block_deref when BH is no more used.
+	(ext2_discard_prealloc): Use %Ld instead of %d for
+	NODE->cache_id.
+	(ext2_alloc_block): Use memset instead of bzero.
+	(inode_getblk): Add assertion that NR must be unsigned below
+	EXT2_N_BLOCKS.
+
+	* balloc.c: Use disk_cache_block_ref instead of bptr.
+	Call disk_cache_block_deref before BH=0.
+	Call disk_cache_block_ref_ptr(GDP) before
+	record_global_poke(GDP).
+	(ext2_new_block): BH is initialized with 0.  After each goto
+	label, an assertion whether it's ==0 or !=0 is made.
+
+	* truncate.c (trunc_indirect): Use disk_cache_block_ref and
+	disk_cache_block_deref instead of bptr.
+	Use bptr_index instead of boffs.
+
+	* hyper.c (get_hypermetadata): Use store_read to read superblock.
+	Move initalization of group_desc to map_hypermetadata.
+	(map_hypermetadata): New function.
+	(diskfs_set_hypermetadata): Call disk_cache_block_ref_ptr(sblock)
+	before record_global_poke(sblock).
+	(diskfs_readonly_changed): mprotect disk_cache instead of
+	disk_image.
+
+	* ext2fs.c (parse_opt): Use memset instead of bzero.
+	(main): Pass disk_cache to pokel_init instead of disk_image.
+	Call map_hypermetadata instead of get_hypermetadata.
+	(diskfs_reload_global_state): Set SBLOCK to 0.
+	Call map_hypermetadata.
+
 2004-01-10  Roberto Reale  <rreale@iol.it>
 
 	* ext2fs.h (ext2_getblk): Fix typo in comment.
diff -urpN --exclude='*~' --exclude=CVS --exclude='*.[ch]' ../../cvs/hurd/fatfs/ChangeLog hurd/fatfs/ChangeLog
--- ../../cvs/hurd/fatfs/ChangeLog	2003-08-09 19:40:17.000000000 +0300
+++ hurd/fatfs/ChangeLog	2004-01-21 09:31:31.000000000 +0200
@@ -1,3 +1,8 @@
+2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>
+
+	* pager.c (pager_read_page): New argument NOTIFY_ON_PAGEOUT.
+	(pager_notify_pageout): New function.
+
 2003-08-09  Marcus Brinkmann  <marcus@gnu.org>
 
 	* fat.h: Include <byteswap.h>.
diff -urpN --exclude='*~' --exclude=CVS --exclude='*.[ch]' ../../cvs/hurd/isofs/ChangeLog hurd/isofs/ChangeLog
--- ../../cvs/hurd/isofs/ChangeLog	2003-08-31 14:16:07.000000000 +0300
+++ hurd/isofs/ChangeLog	2004-01-21 09:31:57.000000000 +0200
@@ -1,3 +1,8 @@
+2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>
+
+	* pager.c (pager_read_page): New argument NOTIFY_ON_PAGEOUT.
+	(pager_notify_pageout): New function.
+
 2003-08-31  Marcus Brinkmann  <marcus@gnu.org>
 
 	* main.c (diskfs_readonly, diskfs_hard_readonly): Removed.
diff -urpN --exclude='*~' --exclude=CVS --exclude='*.[ch]' ../../cvs/hurd/libpager/ChangeLog hurd/libpager/ChangeLog
--- ../../cvs/hurd/libpager/ChangeLog	2002-05-13 04:57:27.000000000 +0300
+++ hurd/libpager/ChangeLog	2004-01-21 11:17:54.000000000 +0200
@@ -1,3 +1,36 @@
+2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>
+
+	* priv.h (PM_FORCEREAD): New macro constant.
+	(PM_NOTIFY_PAGEOUT): Likewise.
+
+	* pager.h (pager_read_page): New argument NOTIFY_ON_PAGEOUT.
+	(pager_notify_pageout): New function declaration.
+
+	* lock-object.c (_pager_lock_object): When flushing page and it
+	has PM_NOTIFY_PAGEOUT, set PM_FORCEREAD to assure that fast
+	data return + data_request will re-read page, that is, call
+	callback pager_read_page.
+
+	* data-return.c (_pager_do_write_request): New local variable
+	NOTIFIED which holds which pages should be notified on pageout.
+	When there is no kernel copy of page, call PAGER_NOTIFY_PAGEOUT,
+	including when pages are not dirty.
+	Don't memory_object_data_supply when
+	_pager_seqnos_memory_object_data_request waits for that page in
+	order to read it.
+
+	* data-request.c (_pager_seqnos_memory_object_data_request): New
+	local variable NOTIFY_ON_PAGEOUT.
+	Move call to _pager_block_termination lower.
+	Use goto release_out instead of stuff + goto allow_term_out.
+	Call _pager_allow_termination before goto release_out when
+	_pager_pagemap_resize fails.
+	When PM_PAGINGOUT, wait paging out to finish instead of just
+	skipping the read.
+	Call callback pager_read_page with argument &NOTIFY_ON_PAGEOUT and
+	mark *PM_ENTRY accordingly.
+	Use NOTIFY_ON_PAGEOUT when calling memory_object_data_supply.
+
 2002-05-13  Marcus Brinkmann  <marcus@gnu.org>
 
 	* demuxer.c (pager_demuxer): Lookup INP->msgh_local_port, rather
diff -urpN --exclude='*~' --exclude=CVS --exclude='*.[ch]' ../../cvs/hurd/storeio/ChangeLog hurd/storeio/ChangeLog
--- ../../cvs/hurd/storeio/ChangeLog	2002-06-23 00:13:31.000000000 +0300
+++ hurd/storeio/ChangeLog	2004-01-21 09:35:28.000000000 +0200
@@ -1,3 +1,8 @@
+2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>
+
+	* pager.c (pager_read_page): New argument NOTIFY_ON_PAGEOUT.
+	(pager_notify_pageout): New function.
+
 2002-06-22  Roland McGrath  <roland@frob.com>
 
 	* storeio.c (parse_opt): Fix fencepost error in -n arg parsing.
diff -urpN --exclude='*~' --exclude=CVS --exclude='*.[ch]' ../../cvs/hurd/tmpfs/ChangeLog hurd/tmpfs/ChangeLog
--- ../../cvs/hurd/tmpfs/ChangeLog	2002-06-25 08:07:46.000000000 +0300
+++ hurd/tmpfs/ChangeLog	2004-01-21 09:34:26.000000000 +0200
@@ -1,3 +1,9 @@
+2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>
+
+	* pager-stubs.c (pager_read_page): New argument
+	NOTIFY_ON_PAGEOUT.
+	(pager_notify_pageout): New function.
+
 2002-06-14  Roland McGrath  <roland@frob.com>
 
 	* tmpfs.c (main): If get_privileged_ports fails with EPERM, try to
diff -urpN --exclude='*~' --exclude=CVS --exclude='*.[ch]' ../../cvs/hurd/ufs/ChangeLog hurd/ufs/ChangeLog
--- ../../cvs/hurd/ufs/ChangeLog	2002-10-04 23:55:55.000000000 +0300
+++ hurd/ufs/ChangeLog	2004-01-21 09:34:39.000000000 +0200
@@ -1,3 +1,8 @@
+2004-01-21  Ognyan Kulev  <ogi@fmi.uni-sofia.bg>
+
+	* pager.c (pager_read_page): New argument NOTIFY_ON_PAGEOUT.
+	(pager_notify_pageout): New function.
+
 2002-10-03  Roland McGrath  <roland@frob.com>
 
 	* dir.h (MAXNAMLEN): #undef before defining.


diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/console/pager.c hurd/console/pager.c
--- ../../cvs/hurd/console/pager.c	2002-09-22 04:28:35.000000000 +0300
+++ hurd/console/pager.c	2004-01-21 09:05:26.000000000 +0200
@@ -1,5 +1,5 @@
 /* pager.c - The pager for the display component of a virtual console.
-   Copyright (C) 2002 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2004 Free Software Foundation, Inc.
    Written by Marcus Brinkmann.
 
    This file is part of the GNU Hurd.
@@ -58,10 +58,11 @@ pager_clear_user_data (struct user_pager
 
 error_t
 pager_read_page (struct user_pager_info *upi, vm_offset_t page,
-                 vm_address_t *buf, int *writelock)
+                 vm_address_t *buf, int *writelock, int *notify_on_pageout)
 {
   /* XXX clients should get a read only object.  */
   *writelock = 0;
+  *notify_on_pageout = 0;
 
   if (upi->memobj_pages[page / vm_page_size] != (vm_address_t) NULL)
     {
@@ -93,6 +94,13 @@ pager_unlock_page (struct user_pager_inf
   return 0;
 }
 
+error_t
+pager_notify_pageout (struct user_pager_info *pager,
+		      vm_offset_t page)
+{
+  assert (!"unrequested notify pageout");
+  return 0;
+}
 
 /* Tell how big the file is. */
 error_t
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/ext2fs/balloc.c hurd/ext2fs/balloc.c
--- ../../cvs/hurd/ext2fs/balloc.c	2000-03-10 06:54:55.000000000 +0200
+++ hurd/ext2fs/balloc.c	2004-01-21 09:06:25.000000000 +0200
@@ -1,6 +1,6 @@
 /* Block allocation routines
 
-   Copyright (C) 1995,99,2000 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1999, 2000, 2004 Free Software Foundation, Inc.
 
    Converted to work under the hurd by Miles Bader <miles@gnu.org>
 
@@ -92,7 +92,7 @@ ext2_free_blocks (block_t block, unsigne
 		      block, count);
 	}
       gdp = group_desc (block_group);
-      bh = bptr (gdp->bg_block_bitmap);
+      bh = disk_cache_block_ref (gdp->bg_block_bitmap);
 
       if (in_range (gdp->bg_block_bitmap, block, gcount) ||
 	  in_range (gdp->bg_inode_bitmap, block, gcount) ||
@@ -114,6 +114,7 @@ ext2_free_blocks (block_t block, unsigne
 	}
 
       record_global_poke (bh);
+      disk_cache_block_ref_ptr (gdp);
       record_global_poke (gdp);
 
       block += gcount;
@@ -139,7 +140,7 @@ ext2_new_block (block_t goal,
 		block_t prealloc_goal,
 		block_t *prealloc_count, block_t *prealloc_block)
 {
-  char *bh;
+  char *bh = 0;
   char *p, *r;
   int i, j, k, tmp;
   unsigned long lmap;
@@ -164,9 +165,10 @@ ext2_new_block (block_t goal,
 
   ext2_debug ("goal=%u", goal);
 
-repeat:
+ repeat:
+  assert (! bh);
   /*
-     * First, test whether the goal block is free.
+   * First, test whether the goal block is free.
    */
   if (goal < sblock->s_first_data_block || goal >= sblock->s_blocks_count)
     goal = sblock->s_first_data_block;
@@ -179,7 +181,7 @@ repeat:
       if (j)
 	goal_attempts++;
 #endif
-      bh = bptr (gdp->bg_block_bitmap);
+      bh = disk_cache_block_ref (gdp->bg_block_bitmap);
 
       ext2_debug ("goal is at %d:%d", i, j);
 
@@ -194,8 +196,8 @@ repeat:
       if (j)
 	{
 	  /*
-	     * The goal was occupied; search forward for a free
-	     * block within the next 32 blocks
+	   * The goal was occupied; search forward for a free
+	   * block within the next 32 blocks
 	   */
 	  lmap = ((((unsigned long *) bh)[j >> 5]) >>
 		  ((j & 31) + 1));
@@ -242,13 +244,16 @@ repeat:
 	  j = k;
 	  goto got_block;
 	}
+
+      disk_cache_block_deref (bh);
+      bh = 0;
     }
 
   ext2_debug ("bit not found in block group %d", i);
 
   /*
-     * Now search the rest of the groups.  We assume that
-     * i and gdp correctly point to the last group visited.
+   * Now search the rest of the groups.  We assume that
+   * i and gdp correctly point to the last group visited.
    */
   for (k = 0; k < groups_count; k++)
     {
@@ -264,7 +269,8 @@ repeat:
       spin_unlock (&global_lock);
       return 0;
     }
-  bh = bptr (gdp->bg_block_bitmap);
+  assert (! bh);
+  bh = disk_cache_block_ref (gdp->bg_block_bitmap);
   r = memscan (bh, 0, sblock->s_blocks_per_group >> 3);
   j = (r - bh) << 3;
   if (j < sblock->s_blocks_per_group)
@@ -274,21 +280,25 @@ repeat:
 			     sblock->s_blocks_per_group);
   if (j >= sblock->s_blocks_per_group)
     {
+      disk_cache_block_deref (bh);
+      bh = 0;
       ext2_error ("free blocks count corrupted for block group %d", i);
       spin_unlock (&global_lock);
       return 0;
     }
 
-search_back:
+ search_back:
+  assert (bh);
   /*
-     * We have succeeded in finding a free byte in the block
-     * bitmap.  Now search backwards up to 7 bits to find the
-     * start of this group of free blocks.
+   * We have succeeded in finding a free byte in the block
+   * bitmap.  Now search backwards up to 7 bits to find the
+   * start of this group of free blocks.
    */
   for (k = 0; k < 7 && j > 0 && !test_bit (j - 1, bh); k++, j--);
 
-got_block:
-
+ got_block:
+  assert (bh);
+  
   ext2_debug ("using block group %d (%d)", i, gdp->bg_free_blocks_count);
 
   tmp = j + i * sblock->s_blocks_per_group + sblock->s_first_data_block;
@@ -301,6 +311,8 @@ got_block:
   if (set_bit (j, bh))
     {
       ext2_warning ("bit already set for block %d", j);
+      disk_cache_block_deref (bh);
+      bh = 0;
       goto repeat;
     }
 
@@ -317,7 +329,7 @@ got_block:
   ext2_debug ("found bit %d", j);
 
   /*
-     * Do block preallocation now if required.
+   * Do block preallocation now if required.
    */
 #ifdef EXT2_PREALLOCATE
   if (prealloc_goal)
@@ -348,6 +360,7 @@ got_block:
   j = tmp;
 
   record_global_poke (bh);
+  bh = 0;
 
   if (j >= sblock->s_blocks_count)
     {
@@ -360,12 +373,14 @@ got_block:
 	      j, goal_hits, goal_attempts);
 
   gdp->bg_free_blocks_count--;
+  disk_cache_block_ref_ptr (gdp);
   record_global_poke (gdp);
 
   sblock->s_free_blocks_count--;
   sblock_dirty = 1;
 
  sync_out:
+  assert (! bh);
   spin_unlock (&global_lock);
   alloc_sync (0);
 
@@ -387,9 +402,12 @@ ext2_count_free_blocks ()
   gdp = NULL;
   for (i = 0; i < groups_count; i++)
     {
+      void *bh;
       gdp = group_desc (i);
       desc_count += gdp->bg_free_blocks_count;
-      x = count_free (bptr (gdp->bg_block_bitmap), block_size);
+      bh = disk_cache_block_ref (gdp->bg_block_bitmap);
+      x = count_free (bh, block_size);
+      disk_cache_block_deref (bh);
       printf ("group %d: stored = %d, counted = %lu",
 	      i, gdp->bg_free_blocks_count, x);
       bitmap_count += x;
@@ -450,7 +468,7 @@ ext2_check_blocks_bitmap ()
 
       gdp = group_desc (i);
       desc_count += gdp->bg_free_blocks_count;
-      bh = bptr (gdp->bg_block_bitmap);
+      bh = disk_cache_block_ref (gdp->bg_block_bitmap);
 
       if (!EXT2_HAS_RO_COMPAT_FEATURE (sblock,
 				       EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
@@ -476,6 +494,7 @@ ext2_check_blocks_bitmap ()
 	  ext2_error ("block #%d of the inode table in group %d is marked free", j, i);
 
       x = count_free (bh, block_size);
+      disk_cache_block_deref (bh);
       if (gdp->bg_free_blocks_count != x)
 	ext2_error ("wrong free blocks count for group %d,"
 		    " stored = %d, counted = %lu",
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/ext2fs/ext2fs.c hurd/ext2fs/ext2fs.c
--- ../../cvs/hurd/ext2fs/ext2fs.c	2002-06-03 00:40:56.000000000 +0300
+++ hurd/ext2fs/ext2fs.c	2004-01-21 09:07:09.000000000 +0200
@@ -1,6 +1,6 @@
 /* Main entry point for the ext2 file system translator
 
-   Copyright (C) 1994,95,96,97,98,99,2002 Free Software Foundation, Inc.
+   Copyright (C) 1994,95,96,97,98,99,2002,04 Free Software Foundation, Inc.
 
    Converted for ext2fs by Miles Bader <miles@gnu.ai.mit.edu>
 
@@ -106,7 +106,7 @@ parse_opt (int key, char *arg, struct ar
       if (values == 0)
 	return ENOMEM;
       state->hook = values;
-      bzero (values, sizeof *values);
+      memset (values, 0, sizeof *values);
       values->sb_block = SBLOCK_BLOCK;
       break;
 
@@ -181,9 +181,9 @@ main (int argc, char **argv)
   /* Map the entire disk. */
   create_disk_pager ();
 
-  pokel_init (&global_pokel, diskfs_disk_pager, disk_image);
+  pokel_init (&global_pokel, diskfs_disk_pager, disk_cache);
 
-  get_hypermetadata();
+  map_hypermetadata ();
 
   inode_init ();
 
@@ -211,6 +211,8 @@ diskfs_reload_global_state ()
 {
   pokel_flush (&global_pokel);
   pager_flush (diskfs_disk_pager, 1);
+  sblock = 0;
   get_hypermetadata ();
+  map_hypermetadata ();
   return 0;
 }
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/ext2fs/ext2fs.h hurd/ext2fs/ext2fs.h
--- ../../cvs/hurd/ext2fs/ext2fs.h	2004-01-11 00:08:58.000000000 +0200
+++ hurd/ext2fs/ext2fs.h	2004-01-21 10:27:11.000000000 +0200
@@ -23,7 +23,9 @@
 #include <hurd/pager.h>
 #include <hurd/fshelp.h>
 #include <hurd/iohelp.h>
+#include <hurd/store.h>
 #include <hurd/diskfs.h>
+#include <hurd/ihash.h>
 #include <assert.h>
 #include <rwlock.h>
 #include <sys/mman.h>
@@ -186,6 +188,8 @@ struct user_pager_info
 /* ---------------------------------------------------------------- */
 /* pager.c */
 
+#define DISK_CACHE_BLOCKS	100
+
 #include <hurd/diskfs-pager.h>
 
 /* Set up the disk pager.  */
@@ -209,10 +213,43 @@ extern struct store *store;
 /* What the user specified.  */
 extern struct store_parsed *store_parsed;
 
-/* Mapped image of the disk.  */
-extern void *disk_image;
+/* Mapped image of cached blocks of the disk.  */
+extern void *disk_cache;
+extern store_offset_t disk_cache_size;
+extern int disk_cache_blocks;
+
+#define DC_INCORE	0x01
+#define DC_REMAPPING	0x02
+#define DC_FIXED	0x04
+
+#define DC_DONT_REUSE	(DC_INCORE | DC_REMAPPING | DC_FIXED)
+
+/* Disk cache blocks' meta info.  */
+struct disk_cache_info
+{
+  block_t block;		/* 0 means invalid */
+  uint16_t flags;
+  uint16_t ref_count;
+};
+
+/* block num --> pointer to in-memory block */
+extern ihash_t disk_cache_bptr;
+/* Metadata about cached block. */
+extern struct disk_cache_info *disk_cache_info;
+/* lock for these mappings */
+extern struct mutex disk_cache_lock;
+/* Fired when a remapping is done.  */
+extern struct condition disk_cache_remapping;
+
+/* Used by boffs_ptr.  */
+void *disk_cache_map (block_t block);
+
+void *disk_cache_block_ref (block_t block);
+void disk_cache_block_ref_ptr (void *ptr);
+void disk_cache_block_deref (void *ptr);
+int disk_cache_block_is_ref (block_t block);
 
-/* Our in-core copy of the super-block (pointer into the disk_image).  */
+/* Our in-core copy of the super-block (pointer into the disk_cache).  */
 struct ext2_super_block *sblock;
 /* True if sblock has been modified.  */
 int sblock_dirty;
@@ -242,6 +279,9 @@ vm_address_t zeroblock;
 
 /* Get the superblock from the disk, & setup various global info from it.  */
 void get_hypermetadata ();
+
+/* Map `sblock' and `group_desc_image' pointers to disk cache.  */
+void map_hypermetadata ();
 
 /* ---------------------------------------------------------------- */
 /* Random stuff calculated from the super block.  */
@@ -265,7 +305,7 @@ spin_lock_t generation_lock;
 unsigned long next_generation;
 
 /* ---------------------------------------------------------------- */
-/* Functions for looking inside disk_image */
+/* Functions for looking inside disk_cache */
 
 #define trunc_block(offs) (((offs) >> log2_block_size) << log2_block_size)
 #define round_block(offs) \
@@ -276,10 +316,38 @@ unsigned long next_generation;
 /* byte offset on disk --> block num */
 #define boffs_block(offs) ((offs) >> log2_block_size)
 
+/* pointer to in-memory block -> index in disk_cache_info */
+#define bptr_index(ptr) (((char *)ptr - (char *)disk_cache) >> log2_block_size)
+
 /* byte offset on disk --> pointer to in-memory block */
-#define boffs_ptr(offs) (((char *)disk_image) + (offs))
+EXT2FS_EI char *
+boffs_ptr (off_t offset)
+{
+  block_t block = boffs_block (offset);
+  mutex_lock (&disk_cache_lock);
+  char *ptr = ihash_find (disk_cache_bptr, block);
+  mutex_unlock (&disk_cache_lock);
+  assert (ptr);
+  ptr += offset % block_size;
+  ext2_debug ("(%Ld) = %p", offset, ptr);
+  return ptr;
+}
+
 /* pointer to in-memory block --> byte offset on disk */
-#define bptr_offs(ptr) ((char *)(ptr) - ((char *)disk_image))
+EXT2FS_EI off_t
+bptr_offs (void *ptr)
+{
+  vm_offset_t mem_offset = (char *)ptr - (char *)disk_cache;
+  off_t offset;
+  assert (mem_offset < disk_cache_size);
+  mutex_lock (&disk_cache_lock);
+  offset = disk_cache_info[boffs_block (mem_offset)].block << log2_block_size;
+  assert (offset || mem_offset < block_size);
+  offset += mem_offset % block_size;
+  mutex_unlock (&disk_cache_lock);
+  ext2_debug ("(%p) = %Ld", ptr, offset);
+  return offset;
+}
 
 /* block num --> pointer to in-memory block */
 #define bptr(block) boffs_ptr(boffs(block))
@@ -296,14 +364,24 @@ struct ext2_group_desc *group_desc_image
 
 /* Convert an inode number to the dinode on disk. */
 EXT2FS_EI struct ext2_inode *
-dino (ino_t inum)
+dino_ref (ino_t inum)
 {
   unsigned long inodes_per_group = sblock->s_inodes_per_group;
   unsigned long bg_num = (inum - 1) / inodes_per_group;
   unsigned long group_inum = (inum - 1) % inodes_per_group;
-  struct ext2_group_desc *bg = group_desc(bg_num);
+  struct ext2_group_desc *bg = group_desc (bg_num);
   block_t block = bg->bg_inode_table + (group_inum / inodes_per_block);
-  return ((struct ext2_inode *)bptr(block)) + group_inum % inodes_per_block;
+  struct ext2_inode *inode = disk_cache_block_ref (block);
+  inode += group_inum % inodes_per_block;
+  ext2_debug ("(%qd) = %p", inum, inode);
+  return inode;
+}
+
+EXT2FS_EI void
+dino_deref (struct ext2_inode *inode)
+{
+  ext2_debug ("(%p)", inode);
+  disk_cache_block_deref (inode);
 }
 
 /* ---------------------------------------------------------------- */
@@ -356,27 +434,36 @@ global_block_modified (block_t block)
 EXT2FS_EI void
 record_global_poke (void *ptr)
 {
-  int boffs = trunc_block (bptr_offs (ptr));
-  global_block_modified (boffs_block (boffs));
-  pokel_add (&global_pokel, boffs_ptr(boffs), block_size);
+  block_t block = boffs_block (bptr_offs (ptr));
+  void *block_ptr = bptr (block);
+  ext2_debug ("(%p = %p)", ptr, block_ptr);
+  assert (disk_cache_block_is_ref (block));
+  global_block_modified (block);
+  pokel_add (&global_pokel, block_ptr, block_size);
 }
 
 /* This syncs a modification to a non-file block.  */
 EXT2FS_EI void
 sync_global_ptr (void *bptr, int wait)
 {
-  vm_offset_t boffs = trunc_block (bptr_offs (bptr));
-  global_block_modified (boffs_block (boffs));
-  pager_sync_some (diskfs_disk_pager, trunc_page (boffs), vm_page_size, wait);
+  block_t block = boffs_block (bptr_offs (bptr));
+  void *block_ptr = bptr (block);
+  ext2_debug ("(%p -> %u)", bptr, (block_t)block);
+  global_block_modified (block);
+  pager_sync_some (diskfs_disk_pager,
+		   block_ptr - disk_cache, block_size, wait);
 }
 
 /* This records a modification to one of a file's indirect blocks.  */
 EXT2FS_EI void
 record_indir_poke (struct node *node, void *ptr)
 {
-  int boffs = trunc_block (bptr_offs (ptr));
-  global_block_modified (boffs_block (boffs));
-  pokel_add (&node->dn->indir_pokel, boffs_ptr(boffs), block_size);
+  block_t block = boffs_block (bptr_offs (ptr));
+  void *block_ptr = bptr (block);
+  ext2_debug ("(%d, %p)", (int)node->cache_id, ptr);
+  assert (disk_cache_block_is_ref (block));
+  global_block_modified (block);
+  pokel_add (&node->dn->indir_pokel, block_ptr, block_size);
 }
 
 /* ---------------------------------------------------------------- */
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/ext2fs/getblk.c hurd/ext2fs/getblk.c
--- ../../cvs/hurd/ext2fs/getblk.c	2004-01-11 00:08:58.000000000 +0200
+++ hurd/ext2fs/getblk.c	2004-01-21 10:31:50.000000000 +0200
@@ -52,7 +52,7 @@ ext2_discard_prealloc (struct node *node
   if (node->dn->info.i_prealloc_count)
     {
       int i = node->dn->info.i_prealloc_count;
-      ext2_debug ("discarding %d prealloced blocks for inode %d",
+      ext2_debug ("discarding %d prealloced blocks for inode %Ld",
 		  i, node->cache_id);
       node->dn->info.i_prealloc_count = 0;
       ext2_free_blocks (node->dn->info.i_prealloc_block, i);
@@ -104,8 +104,8 @@ ext2_alloc_block (struct node *node, blo
 
   if (result && zero)
     {
-      char *bh = bptr (result);
-      bzero (bh, block_size);
+      char *bh = disk_cache_block_ref (result);
+      memset (bh, 0, block_size);
       record_indir_poke (node, bh);
     }
 
@@ -122,6 +122,8 @@ inode_getblk (struct node *node, int nr,
   block_t hint;
 #endif
 
+  assert (0 <= nr && nr < EXT2_N_BLOCKS);
+
   *result = node->dn->info.i_data[nr];
   if (*result)
     return 0;
@@ -180,14 +182,20 @@ block_getblk (struct node *node, block_t
 {
   int i;
   block_t goal = 0;
-  block_t *bh = (block_t *)bptr (block);
+  block_t *bh = (block_t *)disk_cache_block_ref (block);
 
   *result = bh[nr];
   if (*result)
-    return 0;
+    {
+      disk_cache_block_deref (bh);
+      return 0;
+    }
 
   if (!create)
-    return EINVAL;
+    {
+      disk_cache_block_deref (bh);
+      return EINVAL;
+    }
 
   if (node->dn->info.i_next_alloc_block == new_block)
     goal = node->dn->info.i_next_alloc_goal;
@@ -207,12 +215,18 @@ block_getblk (struct node *node, block_t
 
   *result = ext2_alloc_block (node, goal, zero);
   if (!*result)
-    return ENOSPC;
+    {
+      disk_cache_block_deref (bh);
+      return ENOSPC;
+    }
 
   bh[nr] = *result;
 
   if (diskfs_synchronous || node->dn->info.i_osync)
-    sync_global_ptr (bh, 1);
+    {
+      sync_global_ptr (bh, 1);
+      disk_cache_block_deref (bh);
+    }
   else
     record_indir_poke (node, bh);
 
@@ -243,9 +257,9 @@ ext2_getblk (struct node *node, block_t 
       return EIO;
     }
   /*
-     * If this is a sequential block allocation, set the next_alloc_block
-     * to this block now so that all the indblock and data block
-     * allocations use the same goal zone
+   * If this is a sequential block allocation, set the next_alloc_block
+   * to this block now so that all the indblock and data block
+   * allocations use the same goal zone
    */
 
   ext2_debug ("block = %u, next = %u, goal = %u", block,
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/ext2fs/hyper.c hurd/ext2fs/hyper.c
--- ../../cvs/hurd/ext2fs/hyper.c	2002-06-03 00:40:59.000000000 +0300
+++ hurd/ext2fs/hyper.c	2004-01-21 09:08:04.000000000 +0200
@@ -1,6 +1,6 @@
 /* Fetching and storing the hypermetadata (superblock and bg summary info)
 
-   Copyright (C) 1994,95,96,99,2001,02 Free Software Foundation, Inc.
+   Copyright (C) 1994,95,96,99,2001,02,04 Free Software Foundation, Inc.
    Written by Miles Bader <miles@gnu.org>
 
    This program is free software; you can redistribute it and/or
@@ -58,12 +58,15 @@ static int ext2fs_clean;	/* fs clean bef
 void
 get_hypermetadata (void)
 {
-  error_t err = diskfs_catch_exception ();
-  if (err)
-    ext2_panic ("can't read superblock: %s", strerror (err));
-
-  sblock = (struct ext2_super_block *) boffs_ptr (SBLOCK_OFFS);
+  error_t err;
+  size_t read;
 
+  assert (! sblock);
+  err = store_read (store, SBLOCK_OFFS >> store->log2_block_size,
+		    SBLOCK_SIZE, (void **)&sblock, &read);
+  if (err || read != SBLOCK_SIZE)
+    ext2_panic ("Cannot read hypermetadata");
+  
   if (sblock->s_magic != EXT2_SUPER_MAGIC
 #ifdef EXT2FS_PRE_02B_COMPAT
       && sblock->s_magic != EXT2_PRE_02B_MAGIC
@@ -152,15 +155,22 @@ get_hypermetadata (void)
 
   allocate_mod_map ();
 
-  diskfs_end_catch_exception ();
+  /* A handy source of page-aligned zeros.  */
+  if (zeroblock == 0)
+    zeroblock = (vm_address_t) mmap (0, block_size, PROT_READ, MAP_ANON, 0, 0);
+
+  munmap (sblock, SBLOCK_SIZE);
+  sblock = NULL;
+}
+
+void
+map_hypermetadata (void)
+{
+  sblock = (struct ext2_super_block *) boffs_ptr (SBLOCK_OFFS);
 
   /* Cache a convenient pointer to the block group descriptors for allocation.
      These are stored in the filesystem blocks following the superblock.  */
   group_desc_image = (struct ext2_group_desc *) bptr (bptr_block (sblock) + 1);
-
-  /* A handy source of page-aligned zeros.  */
-  if (zeroblock == 0)
-    zeroblock = (vm_address_t) mmap (0, block_size, PROT_READ, MAP_ANON, 0, 0);
 }
 
 error_t
@@ -183,6 +193,7 @@ diskfs_set_hypermetadata (int wait, int 
  if (sblock_dirty)
    {
      sblock_dirty = 0;
+     disk_cache_block_ref_ptr (sblock);
      record_global_poke (sblock);
    }
 
@@ -199,7 +210,8 @@ diskfs_readonly_changed (int readonly)
 
   (*(readonly ? store_set_flags : store_clear_flags)) (store, STORE_READONLY);
 
-  mprotect (disk_image, store->size, PROT_READ | (readonly ? 0 : PROT_WRITE));
+  mprotect (disk_cache, disk_cache_size,
+	    PROT_READ | (readonly ? 0 : PROT_WRITE));
 
   if (!readonly && !(sblock->s_state & EXT2_VALID_FS))
     ext2_warning ("UNCLEANED FILESYSTEM NOW WRITABLE");
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/ext2fs/ialloc.c hurd/ext2fs/ialloc.c
--- ../../cvs/hurd/ext2fs/ialloc.c	2002-10-09 02:10:09.000000000 +0300
+++ hurd/ext2fs/ialloc.c	2004-01-21 09:08:16.000000000 +0200
@@ -1,6 +1,6 @@
 /* Inode allocation routines.
 
-   Copyright (C) 1995,96,99,2000,02 Free Software Foundation, Inc.
+   Copyright (C) 1995,96,99,2000,02,04 Free Software Foundation, Inc.
 
    Converted to work under the hurd by Miles Bader <miles@gnu.org>
 
@@ -60,7 +60,7 @@ diskfs_free_node (struct node *np, mode_
 
   assert (!diskfs_readonly);
 
-  ext2_debug ("freeing inode %u", inum);
+  ext2_debug ("freeing inode %Lu", inum);
 
   spin_lock (&global_lock);
 
@@ -75,22 +75,25 @@ diskfs_free_node (struct node *np, mode_
   bit = (inum - 1) % sblock->s_inodes_per_group;
 
   gdp = group_desc (block_group);
-  bh = bptr (gdp->bg_inode_bitmap);
+  bh = disk_cache_block_ref (gdp->bg_inode_bitmap);
 
   if (!clear_bit (bit, bh))
     ext2_warning ("bit already cleared for inode %Ld", inum);
   else
     {
+      disk_cache_block_ref_ptr (bh);
       record_global_poke (bh);
 
       gdp->bg_free_inodes_count++;
       if (S_ISDIR (old_mode))
 	gdp->bg_used_dirs_count--;
+      disk_cache_block_ref_ptr (gdp);
       record_global_poke (gdp);
 
       sblock->s_free_inodes_count++;
     }
 
+  disk_cache_block_deref (bh);
   sblock_dirty = 1;
   spin_unlock (&global_lock);
   alloc_sync(0);
@@ -111,14 +114,15 @@ diskfs_free_node (struct node *np, mode_
 ino_t
 ext2_alloc_inode (ino_t dir_inum, mode_t mode)
 {
-  char *bh;
+  char *bh = 0;
   int i, j, inum, avefreei;
   struct ext2_group_desc *gdp;
   struct ext2_group_desc *tmp;
 
   spin_lock (&global_lock);
 
-repeat:
+ repeat:
+  assert (! bh);
   gdp = NULL;
   i = 0;
 
@@ -213,7 +217,7 @@ repeat:
       return 0;
     }
 
-  bh = bptr (gdp->bg_inode_bitmap);
+  bh = disk_cache_block_ref (gdp->bg_inode_bitmap);
   if ((inum =
        find_first_zero_bit ((unsigned long *) bh, sblock->s_inodes_per_group))
       < sblock->s_inodes_per_group)
@@ -221,12 +225,17 @@ repeat:
       if (set_bit (inum, bh))
 	{
 	  ext2_warning ("bit already set for inode %d", inum);
+	  disk_cache_block_deref (bh);
+	  bh = 0;
 	  goto repeat;
 	}
       record_global_poke (bh);
+      bh = 0;
     }
   else
     {
+      disk_cache_block_deref (bh);
+      bh = 0;
       if (gdp->bg_free_inodes_count != 0)
 	{
 	  ext2_error ("free inodes count corrupted in group %d", i);
@@ -248,15 +257,25 @@ repeat:
   gdp->bg_free_inodes_count--;
   if (S_ISDIR (mode))
     gdp->bg_used_dirs_count++;
+  disk_cache_block_ref_ptr (gdp);
   record_global_poke (gdp);
 
   sblock->s_free_inodes_count--;
   sblock_dirty = 1;
 
  sync_out:
+  assert (! bh);
   spin_unlock (&global_lock);
   alloc_sync (0);
 
+  /* Make sure the coming read_node won't complain about bad
+     fields.  */
+  {
+    struct ext2_inode *di = dino_ref (inum);
+    memset (di, 0, sizeof *di);
+    dino_deref (di);
+  }
+
   return inum;
 }
 
@@ -354,10 +373,12 @@ ext2_count_free_inodes ()
   gdp = NULL;
   for (i = 0; i < groups_count; i++)
     {
+      void *bh;
       gdp = group_desc (i);
       desc_count += gdp->bg_free_inodes_count;
-      x = count_free (bptr (gdp->bg_inode_bitmap),
-		      sblock->s_inodes_per_group / 8);
+      bh = disk_cache_block_ref (gdp->bg_inode_bitmap);
+      x = count_free (bh, sblock->s_inodes_per_group / 8);
+      disk_cache_block_deref (bh);
       ext2_debug ("group %d: stored = %d, counted = %lu",
 		  i, gdp->bg_free_inodes_count, x);
       bitmap_count += x;
@@ -387,10 +408,12 @@ ext2_check_inodes_bitmap ()
   gdp = NULL;
   for (i = 0; i < groups_count; i++)
     {
+      void *bh;
       gdp = group_desc (i);
       desc_count += gdp->bg_free_inodes_count;
-      x = count_free (bptr (gdp->bg_inode_bitmap),
-		      sblock->s_inodes_per_group / 8);
+      bh = disk_cache_block_ref (gdp->bg_inode_bitmap);
+      x = count_free (bh, sblock->s_inodes_per_group / 8);
+      disk_cache_block_deref (bh);
       if (gdp->bg_free_inodes_count != x)
 	ext2_error ("wrong free inodes count in group %d, "
 		    "stored = %d, counted = %lu",
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/ext2fs/inode.c hurd/ext2fs/inode.c
--- ../../cvs/hurd/ext2fs/inode.c	2002-10-09 02:10:09.000000000 +0300
+++ hurd/ext2fs/inode.c	2004-01-21 10:47:57.000000000 +0200
@@ -1,6 +1,6 @@
 /* Inode management routines
 
-   Copyright (C) 1994,95,96,97,98,99,2000,01,02 Free Software Foundation, Inc.
+   Copyright (C) 1994,95,96,97,98,99,2000,01,02,04 Free Software Foundation, Inc.
 
    Converted for ext2fs by Miles Bader <miles@gnu.org>
 
@@ -91,7 +91,7 @@ diskfs_cached_lookup (ino_t inum, struct
   dn->dir_idx = 0;
   dn->pager = 0;
   rwlock_init (&dn->alloc_lock);
-  pokel_init (&dn->indir_pokel, diskfs_disk_pager, disk_image);
+  pokel_init (&dn->indir_pokel, diskfs_disk_pager, disk_cache);
 
   /* Create the new node.  */
   np = diskfs_make_node (dn);
@@ -200,13 +200,17 @@ read_node (struct node *np)
   error_t err;
   struct stat *st = &np->dn_stat;
   struct disknode *dn = np->dn;
-  struct ext2_inode *di = dino (np->cache_id);
+  struct ext2_inode *di;
   struct ext2_inode_info *info = &dn->info;
 
+  ext2_debug ("(%d)", np->cache_id);
+
   err = diskfs_catch_exception ();
   if (err)
     return err;
 
+  di = dino_ref (np->cache_id);
+
   st->st_fstype = FSTYPE_EXT2FS;
   st->st_fsid = getpid ();	/* This call is very cheap.  */
   st->st_ino = np->cache_id;
@@ -275,7 +279,9 @@ read_node (struct node *np)
       info->i_high_size = di->i_size_high;
       if (info->i_high_size)	/* XXX */
 	{
+	  dino_deref (di);
 	  ext2_warning ("cannot handle large file inode %Ld", np->cache_id);
+	  diskfs_end_catch_exception ();
 	  return EFBIG;
 	}
     }
@@ -297,20 +303,12 @@ read_node (struct node *np)
     }
   dn->info_i_translator = di->i_translator;
 
+  dino_deref (di);
   diskfs_end_catch_exception ();
 
   if (S_ISREG (st->st_mode) || S_ISDIR (st->st_mode)
       || (S_ISLNK (st->st_mode) && st->st_blocks))
-    {
-      unsigned offset;
-
-      np->allocsize = np->dn_stat.st_size;
-
-      /* Round up to a block multiple.  */
-      offset = np->allocsize & ((1 << log2_block_size) - 1);
-      if (offset > 0)
-	np->allocsize += block_size - offset;
-    }
+    np->allocsize = round_block (np->dn_stat.st_size);
   else
     /* Allocsize should be zero for anything except directories, files, and
        long symlinks.  These are the only things allowed to have any blocks
@@ -398,7 +396,9 @@ write_node (struct node *np)
 {
   error_t err;
   struct stat *st = &np->dn_stat;
-  struct ext2_inode *di = dino (np->cache_id);
+  struct ext2_inode *di;
+
+  ext2_debug ("(%d)", np->cache_id);
 
   if (np->dn->info.i_prealloc_count)
     ext2_discard_prealloc (np);
@@ -409,12 +409,14 @@ write_node (struct node *np)
 
       assert (!diskfs_readonly);
 
-      ext2_debug ("writing inode %d to disk", np->cache_id);
+      ext2_debug ("writing inode %Ld to disk", np->cache_id);
 
       err = diskfs_catch_exception ();
       if (err)
 	return NULL;
 
+      di = dino_ref (np->cache_id);
+
       di->i_generation = st->st_gen;
 
       /* We happen to know that the stat mode bits are the same
@@ -490,6 +492,7 @@ write_node (struct node *np)
       diskfs_end_catch_exception ();
       np->dn_stat_dirty = 0;
 
+      /* Leave invoking dino_deref (di) to the caller.  */
       return di;
     }
   else
@@ -599,7 +602,10 @@ diskfs_write_disknode (struct node *np, 
   if (di)
     {
       if (wait)
-	sync_global_ptr (di, 1);
+	{
+	  sync_global_ptr (di, 1);
+	  dino_deref (di);
+	}
       else
 	record_global_poke (di);
     }
@@ -649,7 +655,7 @@ diskfs_set_translator (struct node *np, 
   if (err)
     return err;
 
-  di = dino (np->cache_id);
+  di = dino_ref (np->cache_id);
   blkno = di->i_translator;
 
   if (namelen && !blkno)
@@ -662,6 +668,7 @@ diskfs_set_translator (struct node *np, 
 			0, 0, 0);
       if (blkno == 0)
 	{
+	  dino_deref (di);
 	  diskfs_end_catch_exception ();
 	  return ENOSPC;
 	}
@@ -685,15 +692,20 @@ diskfs_set_translator (struct node *np, 
       np->dn_stat.st_mode &= ~S_IPTRANS;
       np->dn_set_ctime = 1;
     }
+  else
+    dino_deref (di);
 
   if (namelen)
     {
+      void *blkptr;
+
       buf[0] = namelen & 0xFF;
       buf[1] = (namelen >> 8) & 0xFF;
-      bcopy (name, buf + 2, namelen);
+      memcpy (buf + 2, name, namelen);
 
-      bcopy (buf, bptr (blkno), block_size);
-      record_global_poke (bptr (blkno));
+      blkptr = disk_cache_block_ref (blkno);
+      memcpy (blkptr, buf, block_size);
+      record_global_poke (blkptr);
 
       np->dn_stat.st_mode |= S_IPTRANS;
       np->dn_set_ctime = 1;
@@ -719,9 +731,11 @@ diskfs_get_translator (struct node *np, 
   if (err)
     return err;
 
-  blkno = (dino (np->cache_id))->i_translator;
+  struct ext2_inode *di = dino_ref (np->cache_id);
+  blkno = di->i_translator;
+  dino_deref (di);
   assert (blkno);
-  transloc = bptr (blkno);
+  transloc = disk_cache_block_ref (blkno);
 
   datalen =
     ((unsigned char *)transloc)[0] + (((unsigned char *)transloc)[1] << 8);
@@ -736,6 +750,7 @@ diskfs_get_translator (struct node *np, 
 	memcpy (*namep, transloc + 2, datalen);
     }
 
+  disk_cache_block_deref (transloc);
   diskfs_end_catch_exception ();
 
   *namelen = datalen;
@@ -757,7 +772,7 @@ write_symlink (struct node *node, const 
 
   assert (node->dn_stat.st_blocks == 0);
 
-  bcopy (target, node->dn->info.i_data, len);
+  memcpy (node->dn->info.i_data, target, len);
   node->dn_stat.st_size = len - 1;
   node->dn_set_ctime = 1;
   node->dn_set_mtime = 1;
@@ -774,7 +789,7 @@ read_symlink (struct node *node, char *t
 
   assert (node->dn_stat.st_size < MAX_INODE_SYMLINK);
 
-  bcopy (node->dn->info.i_data, target, node->dn_stat.st_size);
+  memcpy (target, node->dn->info.i_data, node->dn_stat.st_size);
   return 0;
 }
 
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/ext2fs/pager.c hurd/ext2fs/pager.c
--- ../../cvs/hurd/ext2fs/pager.c	2002-06-12 00:38:01.000000000 +0300
+++ hurd/ext2fs/pager.c	2004-01-21 09:08:42.000000000 +0200
@@ -1,6 +1,6 @@
 /* Pager for ext2fs
 
-   Copyright (C) 1994,95,96,97,98,99,2000,02 Free Software Foundation, Inc.
+   Copyright (C) 1994,95,96,97,98,99,2000,02,04 Free Software Foundation, Inc.
 
    Converted for ext2fs by Miles Bader <miles@gnu.org>
 
@@ -18,6 +18,7 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
 
+#include <unistd.h>
 #include <string.h>
 #include <errno.h>
 #include <hurd/store.h>
@@ -26,9 +27,6 @@
 /* A ports bucket to hold pager ports.  */
 struct port_bucket *pager_bucket;
 
-/* Mapped image of the disk.  */
-void *disk_image;
-
 spin_lock_t node_to_page_lock = SPIN_LOCK_INITIALIZER;
 
 #ifdef DONT_CACHE_MEMORY_OBJECTS
@@ -153,7 +151,7 @@ find_block (struct node *node, vm_offset
    to consolidate the i/o if possible.  */
 static error_t
 file_pager_read_page (struct node *node, vm_offset_t page,
-		      void **buf, int *writelock)
+		      void **buf, int *writelock, int *notify_on_flush)
 {
   error_t err;
   int offs = 0;
@@ -163,6 +161,9 @@ file_pager_read_page (struct node *node,
   block_t pending_blocks = 0;
   int num_pending_blocks = 0;
 
+  ext2_debug ("reading inode %Ld page %u[%d]",
+	      node->cache_id, page, vm_page_size);
+
   /* Read the NUM_PENDING_BLOCKS blocks in PENDING_BLOCKS, into the buffer
      pointed to by BUF (allocating it if necessary) at offset OFFS.  OFFS in
      adjusted by the amount read, and NUM_PENDING_BLOCKS is zeroed.  Any read
@@ -171,7 +172,8 @@ file_pager_read_page (struct node *node,
     {
       if (num_pending_blocks > 0)
 	{
-	  block_t dev_block = pending_blocks << log2_dev_blocks_per_fs_block;
+	  store_offset_t dev_block = (store_offset_t) pending_blocks
+	    << log2_dev_blocks_per_fs_block;
 	  size_t amount = num_pending_blocks << log2_block_size;
 	  /* The buffer we try to read into; on the first read, we pass in a
 	     size of zero, so that the read is guaranteed to allocate a new
@@ -198,7 +200,7 @@ file_pager_read_page (struct node *node,
 	      else
 		/* We've already got some buffer, so copy into it.  */
 		{
-		  bcopy (new_buf, *buf + offs, new_len);
+		  memcpy (*buf + offs, new_buf, new_len);
 		  free_page_buf (new_buf); /* Return NEW_BUF to our pool.  */
 		  STAT_INC (file_pagein_freed_bufs);
 		}
@@ -254,7 +256,7 @@ file_pager_read_page (struct node *node,
 		break;
 	      STAT_INC (file_pagein_alloced_bufs);
 	    }
-	  bzero (*buf + offs, block_size);
+	  memset (*buf + offs, 0, block_size);
 	  offs += block_size;
 	}
       else
@@ -273,6 +275,8 @@ file_pager_read_page (struct node *node,
   if (lock)
     rwlock_reader_unlock (lock);
 
+  *notify_on_flush = 0;
+
   return err;
 }
 
@@ -295,16 +299,17 @@ pending_blocks_write (struct pending_blo
   if (pb->num > 0)
     {
       error_t err;
-      block_t dev_block = pb->block << log2_dev_blocks_per_fs_block;
+      store_offset_t dev_block = (store_offset_t) pb->block
+	<< log2_dev_blocks_per_fs_block;
       size_t length = pb->num << log2_block_size, amount;
 
-      ext2_debug ("writing block %u[%ld]", pb->block, pb->num);
+      ext2_debug ("writing block %u[%Ld]", pb->block, pb->num);
 
       if (pb->offs > 0)
 	/* Put what we're going to write into a page-aligned buffer.  */
 	{
 	  void *page_buf = get_page_buf ();
-	  bcopy (pb->buf + pb->offs, (void *)page_buf, length);
+	  memcpy ((void *)page_buf, pb->buf + pb->offs, length);
 	  err = store_write (store, dev_block, page_buf, length, &amount);
 	  free_page_buf (page_buf);
 	}
@@ -357,7 +362,7 @@ pending_blocks_add (struct pending_block
   return 0;
 }
 
-/* Write one page for the pager backing NODE, at offset PAGE, into BUF.  This
+/* Write one page for the pager backing NODE, at OFFSET, into BUF.  This
    may need to write several filesystem blocks to satisfy one page, and tries
    to consolidate the i/o if possible.  */
 static error_t
@@ -381,7 +386,7 @@ file_pager_write_page (struct node *node
   else if (offset + left > node->allocsize)
     left = node->allocsize - offset;
 
-  ext2_debug ("writing inode %d page %d[%d]", node->cache_id, offset, left);
+  ext2_debug ("writing inode %Ld page %u[%d]", node->cache_id, offset, left);
 
   STAT_INC (file_pageouts);
 
@@ -405,22 +410,38 @@ file_pager_write_page (struct node *node
 }
 
 static error_t
-disk_pager_read_page (vm_offset_t page, void **buf, int *writelock)
+disk_pager_read_page (vm_offset_t page, void **buf,
+		      int *writelock, int *notify_on_flush)
 {
   error_t err;
   size_t length = vm_page_size, read = 0;
-  vm_size_t dev_end = store->size;
+  store_offset_t offset = page, dev_end = store->size;
 
-  if (page + vm_page_size > dev_end)
-    length = dev_end - page;
+  mutex_lock (&disk_cache_lock);
+  int index = offset >> log2_block_size;
+  offset = (disk_cache_info[index].block << log2_block_size)
+    + offset % block_size;
+  disk_cache_info[index].flags |= DC_INCORE;
+  if (disk_cache_info[index].flags & DC_REMAPPING)
+    {
+      condition_broadcast (&disk_cache_remapping);
+      disk_cache_info[index].flags &= ~DC_REMAPPING;
+    }
+  ext2_debug ("(%Ld)", offset >> log2_block_size);
+  mutex_unlock (&disk_cache_lock);
+
+  if (offset + vm_page_size > dev_end)
+    length = dev_end - offset;
 
-  err = store_read (store, page >> store->log2_block_size, length, buf, &read);
+  err = store_read (store, offset >> store->log2_block_size, length,
+		    buf, &read);
   if (read != length)
     return EIO;
   if (!err && length != vm_page_size)
-    bzero ((void *)(*buf + length), vm_page_size - length);
+    memset ((void *)(*buf + length), 0, vm_page_size - length);
 
   *writelock = 0;
+  *notify_on_flush = 1;
 
   return err;
 }
@@ -430,26 +451,31 @@ disk_pager_write_page (vm_offset_t page,
 {
   error_t err = 0;
   size_t length = vm_page_size, amount;
-  vm_size_t dev_end = store->size;
+  store_offset_t offset = page, dev_end = store->size;
+ 
+  mutex_lock (&disk_cache_lock);
+  int index = offset >> log2_block_size;
+  offset = (disk_cache_info[index].block << log2_block_size)
+    + offset % block_size;
+  mutex_unlock (&disk_cache_lock);
 
-  if (page + vm_page_size > dev_end)
-    length = dev_end - page;
+  if (offset + vm_page_size > dev_end)
+    length = dev_end - offset;
 
-  ext2_debug ("writing disk page %d[%d]", page, length);
+  ext2_debug ("writing disk page %Ld[%d]", offset, length);
 
   STAT_INC (disk_pageouts);
 
   if (modified_global_blocks)
     /* Be picky about which blocks in a page that we write.  */
     {
-      vm_offset_t offs = page;
       struct pending_blocks pb;
 
       pending_blocks_init (&pb, buf);
 
       while (length > 0 && !err)
 	{
-	  block_t block = boffs_block (offs);
+	  block_t block = boffs_block (offset);
 
 	  /* We don't clear the block modified bit here because this paging
 	     write request may not be the same one that actually set the bit,
@@ -467,7 +493,7 @@ disk_pager_write_page (vm_offset_t page,
 	    /* Otherwise just skip it.  */
 	    err = pending_blocks_skip (&pb);
 
-	  offs += block_size;
+	  offset += block_size;
 	  length -= block_size;
 	}
 
@@ -476,7 +502,7 @@ disk_pager_write_page (vm_offset_t page,
     }
   else
     {
-      err = store_write (store, page >> store->log2_block_size,
+      err = store_write (store, offset >> store->log2_block_size,
 			 buf, length, &amount);
       if (!err && length != amount)
 	err = EIO;
@@ -484,18 +510,34 @@ disk_pager_write_page (vm_offset_t page,
 
   return err;
 }
+
+static error_t
+disk_pager_notify_pageout (vm_offset_t page)
+{
+  int index = page >> log2_block_size;
+
+  ext2_debug ("(block %u)", index);
+
+  mutex_lock (&disk_cache_lock);
+  disk_cache_info[index].flags &= ~DC_INCORE;
+  mutex_unlock (&disk_cache_lock);
+
+  return 0;
+}
 
 /* Satisfy a pager read request for either the disk pager or file pager
    PAGER, to the page at offset PAGE into BUF.  WRITELOCK should be set if
    the pager should make the page writeable.  */
 error_t
 pager_read_page (struct user_pager_info *pager, vm_offset_t page,
-		 vm_address_t *buf, int *writelock)
+		 vm_address_t *buf, int *writelock, int *notify_on_flush)
 {
   if (pager->type == DISK)
-    return disk_pager_read_page (page, (void **)buf, writelock);
+    return disk_pager_read_page (page, (void **)buf,
+				 writelock, notify_on_flush);
   else
-    return file_pager_read_page (pager->node, page, (void **)buf, writelock);
+    return file_pager_read_page (pager->node, page, (void **)buf,
+				 writelock, notify_on_flush);
 }
 
 /* Satisfy a pager write request for either the disk pager or file pager
@@ -509,6 +551,16 @@ pager_write_page (struct user_pager_info
   else
     return file_pager_write_page (pager->node, page, (void *)buf);
 }
+
+error_t
+pager_notify_pageout (struct user_pager_info *pager, vm_offset_t page)
+{
+  if (pager->type == DISK)
+    return disk_pager_notify_pageout (page);
+  else
+    return 0;
+}
+
 
 /* Make page PAGE writable, at least up to ALLOCSIZE.  This function and
    diskfs_grow are the only places that blocks are actually added to the
@@ -558,10 +610,10 @@ pager_unlock_page (struct user_pager_inf
 
 #ifdef EXT2FS_DEBUG
       if (dn->last_page_partially_writable)
-	ext2_debug ("made page %u[%lu] in inode %d partially writable",
+	ext2_debug ("made page %u[%Lu] in inode %Ld partially writable",
 		    page, node->allocsize - page, node->cache_id);
       else
-	ext2_debug ("made page %u[%u] in inode %d writable",
+	ext2_debug ("made page %u[%u] in inode %Ld writable",
 		    page, vm_page_size, node->cache_id);
 #endif
 
@@ -619,8 +671,8 @@ diskfs_grow (struct node *node, off_t si
 	  block_t old_page_end_block =
 	    round_page (old_size) >> log2_block_size;
 
-	  ext2_debug ("growing inode %d to %lu bytes (from %lu)", node->cache_id,
-		      new_size, old_size);
+	  ext2_debug ("growing inode %Ld to %Lu bytes (from %Lu)",
+		      node->cache_id, new_size, old_size);
 
 	  if (dn->last_page_partially_writable
 	      && old_page_end_block > end_block)
@@ -656,11 +708,11 @@ diskfs_grow (struct node *node, off_t si
 
       STAT_INC (file_grows);
 
-      ext2_debug ("new size: %ld%s.", new_size,
+      ext2_debug ("new size: %Lu%s.", new_size,
 		  dn->last_page_partially_writable
 		  ? " (last page writable)": "");
       if (err)
-	ext2_warning ("inode=%Ld, target=%Ld: %s",
+	ext2_warning ("inode=%Ld, target=%Lu: %s",
 		      node->cache_id, new_size, strerror (err));
 
       node->allocsize = new_size;
@@ -765,6 +817,309 @@ pager_dropweak (struct user_pager_info *
 {
 }
 
+/* Cached blocks from disk.  */
+void *disk_cache;
+
+/* DISK_CACHE size in bytes and blocks.  */
+store_offset_t disk_cache_size;
+int disk_cache_blocks;
+
+/* block num --> pointer to in-memory block */
+ihash_t disk_cache_bptr;
+/* Cached blocks' info.  */
+struct disk_cache_info *disk_cache_info;
+/* Hint index for which cache block to reuse next.  */
+int disk_cache_hint;
+/* Lock for these structures.  */
+struct mutex disk_cache_lock;
+struct condition disk_cache_remapping;
+
+/* Finish mapping initialization. */
+static void
+disk_cache_init (void)
+{
+  if (block_size != vm_page_size)
+    ext2_panic ("Block size %d != vm_page_size %d",
+		block_size, vm_page_size);
+
+  mutex_init (&disk_cache_lock);
+  condition_init (&disk_cache_remapping);
+
+  /* Allocate space for block num -> in-memory pointer mapping.  */
+  if (ihash_create (&disk_cache_bptr))
+    ext2_panic ("Can't allocate memory for disk_pager_bptr");
+
+  /* Allocate space for disk cache blocks' info.  */
+  disk_cache_info = malloc ((sizeof *disk_cache_info) * disk_cache_blocks);
+  if (!disk_cache_info)
+    ext2_panic ("Cannot allocate space for disk cache info");
+
+  /* Initialize disk_cache_info.  */
+  for (int i = 0; i < disk_cache_blocks; i++)
+    {
+      disk_cache_info[i].block = -1;
+      disk_cache_info[i].flags = 0;
+      disk_cache_info[i].ref_count = 0;
+    }
+  disk_cache_hint = 0;
+
+  /* Map the superblock and the block group descriptors.  */
+  block_t fixed_first = boffs_block (SBLOCK_OFFS);
+  block_t fixed_last = fixed_first
+    + (round_block ((sizeof *group_desc_image) * groups_count)
+       >> log2_block_size);
+  ext2_debug ("%d-%d\n", fixed_first, fixed_last);
+  assert (fixed_last - fixed_first + 1 <= (block_t)disk_cache_blocks + 3);
+  for (block_t i = fixed_first; i <= fixed_last; i++)
+    {
+      disk_cache_map (i);
+      assert (disk_cache_info[i-fixed_first].block == i);
+      disk_cache_info[i-fixed_first].flags |= DC_FIXED;
+    }
+}
+
+static void
+disk_cache_wait_remapping (int index, const char *funcname)
+{
+  while (disk_cache_info[index].flags & DC_REMAPPING)
+    {
+      ext2_debug ("%s: Wait for remapping to finish (%u -> %d)\n",
+		  funcname, disk_cache_info[index].block, index);
+      condition_wait (&disk_cache_remapping, &disk_cache_lock);
+    }
+}
+
+/* Map block and return pointer to it.  */
+void *
+disk_cache_map (block_t block)
+{
+  int index;
+  void *bptr;
+
+  ext2_debug ("(%u)", block);
+
+  mutex_lock (&disk_cache_lock);
+
+  bptr = ihash_find (disk_cache_bptr, block);
+  if (bptr)
+    /* Mapped in the meantime.  */
+    {
+      index = bptr_index (bptr);
+      disk_cache_info[index].ref_count++;
+
+      ext2_debug ("cached %u -> %d (ref_count = %d, flags = 0x%x, ptr = %p)",
+		  disk_cache_info[index].block, index,
+		  disk_cache_info[index].ref_count,
+		  disk_cache_info[index].flags, bptr);
+
+      disk_cache_wait_remapping (index, "disk_cache_map (cached)");
+
+      mutex_unlock (&disk_cache_lock);
+
+      return bptr;
+    }
+
+  /* Search for a block that is not in the core and is not referenced.  */
+  index = disk_cache_hint;
+  while ((disk_cache_info[index].flags & DC_DONT_REUSE)
+	 || (disk_cache_info[index].ref_count))
+    {
+      ext2_debug ("reject %u -> %d (ref_count = %d, flags = 0x%x)",
+		  disk_cache_info[index].block, index,
+		  disk_cache_info[index].ref_count,
+		  disk_cache_info[index].flags);
+      index++;
+      if (index >= disk_cache_blocks)
+	index -= disk_cache_blocks;
+      if (index == disk_cache_hint)
+	break;
+    }
+
+  if (! (disk_cache_info[index].flags & DC_DONT_REUSE)
+      && (! disk_cache_info[index].ref_count))
+    /* Suitable place is found.  */
+    {
+      bptr = (char *)disk_cache + (index << log2_block_size);
+
+      ext2_debug ("map %u -> %d (%p)", block, index, bptr);
+
+      if (disk_cache_info[index].block != (block_t) -1)
+	/* Unmap old mapping.  */
+	ihash_remove (disk_cache_bptr, disk_cache_info[index].block);
+
+      /* New mapping.  */
+      if (ihash_add (disk_cache_bptr, block, bptr, NULL))
+        ext2_panic ("Couldn't ihash_add new disk block");
+      disk_cache_info[index].flags |= DC_REMAPPING;
+      disk_cache_info[index].block = block;
+      assert (! disk_cache_info[index].ref_count);
+      disk_cache_info[index].ref_count = 1;
+    }
+
+  /* The next place in the disk cache becomes the current hint.  */
+  disk_cache_hint = index + 1;
+  if (disk_cache_hint >= disk_cache_blocks)
+    disk_cache_hint -= disk_cache_blocks;
+
+  if (! bptr)
+    /* No place is found.  Flush the hint and try again.  */
+    {
+      ext2_debug ("flush %u -> %d",
+		  disk_cache_info[disk_cache_hint].block,
+		  disk_cache_hint);
+      mutex_unlock (&disk_cache_lock);
+
+      /* XXX: Touch all pages.  It seems that sometimes GNU Mach
+	 "forgets" to notify us about evicted pages.  */
+      for (vm_offset_t i = 0; i < disk_cache_size; i += vm_page_size)
+	*(volatile char *)(disk_cache + i);
+
+      /* Release some references to cached blocks.  */
+      pokel_sync (&global_pokel, 1); 
+
+      /* Return unused pages that are in core.  */
+      int pending_begin, pending_end = -1;
+      mutex_lock (&disk_cache_lock);
+      for (index = 0; index < disk_cache_blocks; index++)
+	if (! (disk_cache_info[index].flags & (DC_DONT_REUSE & ~DC_INCORE))
+	    && ! disk_cache_info[index].ref_count)
+	  {
+	    ext2_debug ("return %u -> %d",
+			disk_cache_info[index].block, index);
+	    if (index != pending_end)
+	      {
+		if (pending_end >= 0)
+		  {
+		    mutex_unlock (&disk_cache_lock);
+		    pager_return_some (diskfs_disk_pager,
+				       pending_begin * vm_page_size,
+				       (pending_end - pending_begin)
+				       * vm_page_size,
+				       1);
+		    mutex_lock (&disk_cache_lock);
+		  }
+		pending_begin = index;
+	      }
+	    pending_end = index + 1;
+	  }
+      mutex_unlock (&disk_cache_lock);
+      if (pending_end >= 0)
+	pager_return_some (diskfs_disk_pager,
+			   pending_begin * vm_page_size,
+			   (pending_end - pending_begin) * vm_page_size,
+			   1);
+      else
+	printf ("ext2fs: disk cache is starving\n");
+
+      /* Give it some time.  This should happen rarely.  */
+      sleep (1);
+
+      return disk_cache_map (block);
+    }
+
+  mutex_unlock (&disk_cache_lock);
+
+  pager_return_some (diskfs_disk_pager, bptr - disk_cache, vm_page_size, 1);
+  *(volatile char *)bptr;	/* Clear DC_REMAPPING */
+
+#ifndef NDEBUG
+  /* Is the block mapped correctly?  */
+  mutex_lock (&disk_cache_lock);
+  assert (disk_cache_info[index].block == block
+	  && ! (disk_cache_info[index].flags & DC_REMAPPING));
+  mutex_unlock (&disk_cache_lock);
+#endif
+
+  ext2_debug ("(%u) = %p", block, bptr);
+  return bptr;
+}
+
+void *
+disk_cache_block_ref (block_t block)
+{
+  void *ptr;
+  int index;
+
+  assert (0 <= block && block <= store->size >> log2_block_size);
+
+  mutex_lock (&disk_cache_lock);
+
+  ptr = ihash_find (disk_cache_bptr, block);
+  if (! ptr)
+    {
+      mutex_unlock (&disk_cache_lock);
+      ptr = disk_cache_map (block);
+      ext2_debug ("(%u) = %p", block, ptr);
+      return ptr;
+    }
+
+  index = bptr_index (ptr);
+  disk_cache_info[index].ref_count++;
+  ext2_debug ("(%u) = %p (ref_count = %d, flags = 0x%x)",
+	      block, ptr,
+	      disk_cache_info[index].ref_count,
+	      disk_cache_info[index].flags);
+
+  disk_cache_wait_remapping (index, "disk_cache_block_ref");
+
+  mutex_unlock (&disk_cache_lock);
+
+  return ptr;
+}
+
+void
+disk_cache_block_ref_ptr (void *ptr)
+{
+  int index;
+
+  assert (disk_cache <= ptr && ptr <= disk_cache + disk_cache_size);
+
+  mutex_lock (&disk_cache_lock);
+  index = bptr_index (ptr);
+  disk_cache_info[index].ref_count++;
+  ext2_debug ("(%p) (ref_count = %d, flags = 0x%x)",
+	      ptr,
+	      disk_cache_info[index].ref_count,
+	      disk_cache_info[index].flags);
+  disk_cache_wait_remapping (index, "disk_cache_block_ref_ptr");
+  mutex_unlock (&disk_cache_lock);
+}
+
+void
+disk_cache_block_deref (void *ptr)
+{
+  int index;
+
+  assert (disk_cache <= ptr && ptr <= disk_cache + disk_cache_size);
+
+  mutex_lock (&disk_cache_lock);
+  index = bptr_index (ptr);
+  assert (disk_cache_info[index].ref_count);
+  disk_cache_info[index].ref_count--;
+  ext2_debug ("(%p) (ref_count = %d, flags = 0x%x)",
+	      ptr,
+	      disk_cache_info[index].ref_count,
+	      disk_cache_info[index].flags);
+  mutex_unlock (&disk_cache_lock);
+}
+
+int
+disk_cache_block_is_ref (block_t block)
+{
+  int ref;
+  void *ptr;
+
+  mutex_lock (&disk_cache_lock);
+  ptr = ihash_find (disk_cache_bptr, block);
+  if (! ptr)
+    ref = 0;
+  else
+    ref = disk_cache_info[bptr_index (ptr)].ref_count;
+  mutex_unlock (&disk_cache_lock);
+
+  return ref;
+}
+
 /* Create the DISK pager.  */
 void
 create_disk_pager (void)
@@ -774,8 +1129,12 @@ create_disk_pager (void)
     ext2_panic ("can't create disk pager: %s", strerror (errno));
   upi->type = DISK;
   pager_bucket = ports_create_bucket ();
-  diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, store->size,
-			   &disk_image);
+  get_hypermetadata ();
+  disk_cache_blocks = DISK_CACHE_BLOCKS;
+  disk_cache_size = disk_cache_blocks << log2_block_size;
+  diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE,
+			   disk_cache_size, &disk_cache);
+  disk_cache_init ();
 }
 
 /* Call this to create a FILE_DATA pager and return a send right.
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/ext2fs/pokel.c hurd/ext2fs/pokel.c
--- ../../cvs/hurd/ext2fs/pokel.c	1996-01-16 00:28:56.000000000 +0200
+++ hurd/ext2fs/pokel.c	2004-01-21 09:09:06.000000000 +0200
@@ -1,6 +1,6 @@
 /* A data structure to remember modifications to a memory region
 
-   Copyright (C) 1995 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 2004 Free Software Foundation, Inc.
 
    Written by Miles Bader <miles@gnu.ai.mit.edu>
 
@@ -67,12 +67,27 @@ pokel_add (struct pokel *pokel, void *lo
       vm_offset_t p_offs = pl->offset;
       vm_size_t p_end = p_offs + pl->length;
 
-      if (p_offs == offset && p_end == end)
-	break;
+      if (p_offs <= offset && end <= p_end)
+	{
+	  if (pokel->image == disk_cache)
+	    for (vm_offset_t i = offset; i < end; i += block_size)
+	      disk_cache_block_deref (disk_cache + i);
+
+	  break;
+	}
       else if (p_end >= offset && end >= p_offs)
 	{
 	  pl->offset = offset < p_offs ? offset : p_offs;
 	  pl->length = (end > p_end ? end : p_end) - pl->offset;
+
+	  if (pokel->image == disk_cache)
+	    {
+	      vm_offset_t i_begin = p_offs > offset ? p_offs : offset;
+	      vm_offset_t i_end = p_end < end ? p_end : end;
+	      for (vm_offset_t i = i_begin; i < i_end; i += block_size)
+		disk_cache_block_deref (disk_cache + i);
+	    }
+
 	  ext2_debug ("extended 0x%x[%ul] to 0x%x[%ul]",
 		      p_offs, p_end - p_offs, pl->offset, pl->length);
 	  break;
@@ -106,18 +121,25 @@ void
 _pokel_exec (struct pokel *pokel, int sync, int wait)
 {
   struct poke *pl, *pokes, *last = NULL;
-  
+
   spin_lock (&pokel->lock);
   pokes = pokel->pokes;
   pokel->pokes = NULL;
   spin_unlock (&pokel->lock);
 
   for (pl = pokes; pl; last = pl, pl = pl->next)
-    if (sync)
-      {
-	ext2_debug ("syncing 0x%x[%ul]", pl->offset, pl->length);
-	pager_sync_some (pokel->pager, pl->offset, pl->length, wait);
-      }
+    {
+      if (sync)
+	{
+	  ext2_debug ("syncing 0x%x[%ul]", pl->offset, pl->length);
+	  pager_sync_some (pokel->pager, pl->offset, pl->length, wait);
+	}
+
+      vm_offset_t begin = trunc_block (pl->offset);
+      vm_offset_t end = round_block (pl->offset + pl->length);
+      for (vm_offset_t i = begin; i != end; i += block_size)
+	disk_cache_block_deref (pokel->image + i);
+    }
 
   if (last)
     {
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/ext2fs/truncate.c hurd/ext2fs/truncate.c
--- ../../cvs/hurd/ext2fs/truncate.c	2000-12-03 06:41:37.000000000 +0200
+++ hurd/ext2fs/truncate.c	2004-01-21 09:09:47.000000000 +0200
@@ -1,6 +1,6 @@
 /* File truncation
 
-   Copyright (C) 1995,96,97,99,2000 Free Software Foundation, Inc.
+   Copyright (C) 1995,96,97,99,2000,04 Free Software Foundation, Inc.
 
    Written by Miles Bader <miles@gnu.org>
 
@@ -124,7 +124,7 @@ trunc_indirect (struct node *node, block
     {
       unsigned index;
       int modified = 0, all_freed = 1;
-      block_t *ind_bh = (block_t *)bptr (*p);
+      block_t *ind_bh = (block_t *)disk_cache_block_ref (*p);
       unsigned first = end < offset ? 0 : end - offset;
 
       for (index = first; index < addr_per_block; index++)
@@ -139,11 +139,16 @@ trunc_indirect (struct node *node, block
 
       if (first == 0 && all_freed)
 	{
-	  pager_flush_some (diskfs_disk_pager, boffs (*p), block_size, 1);
+	  pager_flush_some (diskfs_disk_pager,
+			    bptr_index (ind_bh) << log2_block_size,
+			    block_size, 1);
 	  free_block_run_free_ptr (fbr, p);
+	  disk_cache_block_deref (ind_bh);
 	}
       else if (modified)
 	record_indir_poke (node, ind_bh);
+      else
+	disk_cache_block_deref (ind_bh);
     }
 }
 
@@ -218,7 +223,7 @@ poke_pages (memory_object_t obj, vm_offs
 /* Flush all the data past the new size from the kernel.  Also force any
    delayed copies of this data to take place immediately.  (We are implicitly
    changing the data to zeros and doing it without the kernel's immediate
-   knowledge; accordingl we must help out the kernel thusly.) */
+   knowledge; accordingly we must help out the kernel thusly.) */
 static void
 force_delayed_copies (struct node *node, off_t length)
 {
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/fatfs/pager.c hurd/fatfs/pager.c
--- ../../cvs/hurd/fatfs/pager.c	2003-07-29 01:42:26.000000000 +0300
+++ hurd/fatfs/pager.c	2004-01-21 09:10:46.000000000 +0200
@@ -1,5 +1,5 @@
 /* pager.c - Pager for fatfs.
-   Copyright (C) 1997, 1999, 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 1997, 1999, 2002, 2003, 2004 Free Software Foundation, Inc.
    Written by Thomas Bushnell, n/BSG and Marcus Brinkmann.
 
    This file is part of the GNU Hurd.
@@ -540,8 +540,9 @@ fat_pager_write_page (vm_offset_t page, 
    the pager should make the page writeable.  */
 error_t
 pager_read_page (struct user_pager_info *pager, vm_offset_t page,
-                 vm_address_t *buf, int *writelock)
+                 vm_address_t *buf, int *writelock, int *notify_on_pageout)
 {
+  *notify_on_pageout = 0;
   if (pager->type == FAT)
     return fat_pager_read_page (page, (void **)buf, writelock);
   else
@@ -596,6 +597,14 @@ pager_unlock_page (struct user_pager_inf
   return 0;
 }
 
+error_t
+pager_notify_pageout (struct user_pager_info *pager,
+		      vm_offset_t page)
+{
+  assert (!"unrequested pager_notify_pageout");
+  return 0;
+}
+
 /* Grow the disk allocated to locked node NODE to be at least SIZE
    bytes, and set NODE->allocsize to the actual allocated size.  (If
    the allocated size is already SIZE bytes, do nothing.)  CRED
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/isofs/pager.c hurd/isofs/pager.c
--- ../../cvs/hurd/isofs/pager.c	2001-01-07 19:06:26.000000000 +0200
+++ hurd/isofs/pager.c	2004-01-21 09:11:01.000000000 +0200
@@ -1,5 +1,5 @@
 /* 
-   Copyright (C) 1997, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1997, 1999, 2004 Free Software Foundation, Inc.
    Written by Thomas Bushnell, n/BSG.
 
    This file is part of the GNU Hurd.
@@ -36,7 +36,8 @@ error_t
 pager_read_page (struct user_pager_info *upi,
 		 vm_offset_t page,
 		 vm_address_t *buf,
-		 int *writelock)
+		 int *writelock,
+		 int *notify_on_pageout)
 {
   error_t err;
   daddr_t addr;
@@ -46,6 +47,7 @@ pager_read_page (struct user_pager_info 
 
   /* This is a read-only medium */
   *writelock = 1;
+  *notify_on_pageout = 0;
 
   if (upi->type == FILE_DATA)
     {
@@ -94,6 +96,14 @@ pager_unlock_page (struct user_pager_inf
   return EROFS;
 }
 
+error_t
+pager_notify_pageout (struct user_pager_info *pager,
+		      vm_offset_t address)
+{
+  assert (!"unrequested pager_notify_pagout");
+  return 0;
+}
+
 /* Tell how big the file is. */
 error_t
 pager_report_extent (struct user_pager_info *pager,
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/libpager/data-request.c hurd/libpager/data-request.c
--- ../../cvs/hurd/libpager/data-request.c	2002-05-08 12:22:14.000000000 +0300
+++ hurd/libpager/data-request.c	2004-01-21 09:33:22.000000000 +0200
@@ -1,5 +1,5 @@
 /* Implementation of memory_object_data_request for pager library
-   Copyright (C) 1994,95,96,97,2000,02 Free Software Foundation
+   Copyright (C) 1994,95,96,97,2000,02,04 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -34,17 +34,17 @@ _pager_seqnos_memory_object_data_request
   int doread, doerror;
   error_t err;
   vm_address_t page;
-  int write_lock;
+  int write_lock, notify_on_pageout;
 
   p = ports_lookup_port (0, object, _pager_class);
   if (!p)
     return EOPNOTSUPP;
 
-  /* Acquire the right to meddle with the pagemap */
+  /* Acquire the right to meddle with the pagemap.  */
   mutex_lock (&p->interlock);
   _pager_wait_for_seqno (p, seqno);
 
-  /* sanity checks -- we don't do multi-page requests yet.  */
+  /* Sanity checks -- we don't do multi-page requests yet.  */
   if (control != p->memobjcntl)
     {
       printf ("incg data request: wrong control port\n");
@@ -61,20 +61,21 @@ _pager_seqnos_memory_object_data_request
       goto release_out;
     }
 
-  _pager_block_termination (p);	/* prevent termination until
-				   mark_object_error is done */
-
   if (p->pager_state != NORMAL)
     {
       printf ("pager in wrong state for read\n");
-      _pager_release_seqno (p, seqno);
-      mutex_unlock (&p->interlock);
-      goto allow_term_out;
+      goto release_out;
     }
 
+  _pager_block_termination (p);	/* prevent termination until
+				   mark_object_error is done */
+
   err = _pager_pagemap_resize (p, offset + length);
   if (err)
-    goto release_out;		/* Can't do much about the actual error.  */
+    {
+      _pager_allow_termination (p);
+      goto release_out;	  /* Can't do much about the actual error.  */
+    }
 
   /* If someone is paging this out right now, the disk contents are
      unreliable, so we have to wait.  It is too expensive (right now) to
@@ -84,8 +85,24 @@ _pager_seqnos_memory_object_data_request
   pm_entry = &p->pagemap[offset / __vm_page_size];
   if (*pm_entry & PM_PAGINGOUT)
     {
+#if 1
+      *pm_entry |= PM_PAGEINWAIT | PM_FORCEREAD;
+      while (*pm_entry & PM_PAGINGOUT)
+	condition_wait (&p->wakeup, &p->interlock);
+      doread = 1;
+#else
+      /* XXX: This is more fine-grained logic, but it needs more
+	 testing. */
       doread = 0;
       *pm_entry |= PM_PAGEINWAIT;
+
+      if (*pm_entry & PM_FORCEREAD)
+	{
+	  while (*pm_entry & PM_PAGEINWAIT)
+	    condition_wait (&p->wakeup, &p->interlock);
+	  doread = 1;
+	}
+#endif
     }
   else
     doread = 1;
@@ -116,16 +133,23 @@ _pager_seqnos_memory_object_data_request
   if (doerror)
     goto error_read;
 
-  err = pager_read_page (p->upi, offset, &page, &write_lock);
+  err = pager_read_page (p->upi, offset, &page,
+			 &write_lock, &notify_on_pageout);
   if (err)
     goto error_read;
 
   memory_object_data_supply (p->memobjcntl, offset, page, length, 1,
-			     write_lock ? VM_PROT_WRITE : VM_PROT_NONE, 0,
+			     write_lock ? VM_PROT_WRITE : VM_PROT_NONE,
+			     notify_on_pageout ? 1 : 0,
 			     MACH_PORT_NULL);
   mutex_lock (&p->interlock);
   _pager_mark_object_error (p, offset, length, 0);
   _pager_allow_termination (p);
+  *pm_entry &= ~PM_FORCEREAD;
+  if (notify_on_pageout)
+    *pm_entry |= PM_NOTIFY_PAGEOUT;
+  else
+    *pm_entry &= ~PM_NOTIFY_PAGEOUT;
   mutex_unlock (&p->interlock);
   ports_port_deref (p);
   return 0;
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/libpager/data-return.c hurd/libpager/data-return.c
--- ../../cvs/hurd/libpager/data-return.c	2002-05-08 12:22:14.000000000 +0300
+++ hurd/libpager/data-return.c	2004-01-21 09:11:24.000000000 +0200
@@ -1,5 +1,5 @@
 /* Implementation of memory_object_data_return for pager library
-   Copyright (C) 1994,95,96,99,2000,02 Free Software Foundation, Inc.
+   Copyright (C) 1994,95,96,99,2000,02,04 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -38,6 +38,7 @@ _pager_do_write_request (mach_port_t obj
 {
   struct pager *p;
   short *pm_entries;
+  char *notified;
   int npages, i;
   error_t *pagerrs;
   struct lock_request *lr;
@@ -71,9 +72,6 @@ _pager_do_write_request (mach_port_t obj
       goto release_out;
     }
 
-  if (! dirty)
-    goto release_out;
-
   if (p->pager_state != NORMAL)
     {
       printf ("pager in wrong state for write\n");
@@ -90,6 +88,24 @@ _pager_do_write_request (mach_port_t obj
 
   pm_entries = &p->pagemap[offset / __vm_page_size];
 
+  notified = alloca (npages * sizeof *notified);
+  memset (notified, 0, npages * sizeof *notified);
+
+  if (! dirty && ! kcopy)
+    {
+      for (i = 0; i < npages; i++)
+	notified[i] = (pm_entries[i] & PM_NOTIFY_PAGEOUT)
+	  && ! (pm_entries[i] & PM_PAGEINWAIT);
+      _pager_release_seqno (p, seqno);
+      goto notify;
+    }
+
+  if (! dirty)
+    {
+      _pager_allow_termination (p);
+      goto release_out;
+    }
+
   /* Make sure there are no other in-progress writes for any of these
      pages before we begin.  This imposes a little more serialization
      than we really have to require (because *all* future writes on
@@ -179,14 +195,26 @@ _pager_do_write_request (mach_port_t obj
 	pm_entries[i] |= PM_INVALID;
 
       if (pm_entries[i] & PM_PAGEINWAIT)
-	memory_object_data_supply (p->memobjcntl,
-				   offset + (vm_page_size * i),
-				   data + (vm_page_size * i),
-				   vm_page_size, 1,
-				   VM_PROT_NONE, 0, MACH_PORT_NULL);
+	{
+	  if (! (pm_entries[i] & PM_FORCEREAD))
+	    memory_object_data_supply (p->memobjcntl,
+				       offset + (vm_page_size * i),
+				       data + (vm_page_size * i),
+				       vm_page_size, 1,
+				       VM_PROT_NONE, 0, MACH_PORT_NULL);
+	  else
+	    {
+	      munmap ((caddr_t) (data + (vm_page_size * i)),
+		      vm_page_size);
+	      wakeup = 1;   /* _p_s_m_o_data_request will read it.  */
+	    }
+	}
       else
-	munmap ((caddr_t) (data + (vm_page_size * i)),
-		vm_page_size);
+	{
+	  munmap ((caddr_t) (data + (vm_page_size * i)),
+		  vm_page_size);
+	  notified[i] = !! (pm_entries[i] & PM_NOTIFY_PAGEOUT);
+	}
 
       pm_entries[i] &= ~(PM_PAGINGOUT | PM_PAGEINWAIT | PM_WRITEWAIT);
     }
@@ -198,10 +226,15 @@ _pager_do_write_request (mach_port_t obj
   if (wakeup)
     condition_broadcast (&p->wakeup);
 
+ notify:
   _pager_allow_termination (p);
-
   mutex_unlock (&p->interlock);
 
+  if (! kcopy)
+    for (i = 0; i < npages; i++)
+      if (notified[i])
+	pager_notify_pageout (p->upi, offset + (i * vm_page_size));
+
   ports_port_deref (p);
   return 0;
 
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/libpager/lock-object.c hurd/libpager/lock-object.c
--- ../../cvs/hurd/libpager/lock-object.c	2000-07-25 22:40:27.000000000 +0300
+++ hurd/libpager/lock-object.c	2004-01-21 09:11:54.000000000 +0200
@@ -1,5 +1,5 @@
 /* Synchronous wrapper for memory_object_lock_request
-   Copyright (C) 1993, 1994, 1996, 1997, 2000 Free Software Foundation
+   Copyright (C) 1993,1994,1996,1997,2000,2004 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -92,11 +92,18 @@ _pager_lock_object (struct pager *p, 
 	      short *pm_entries = &p->pagemap[pm_offs];
 	      vm_offset_t bound = size / vm_page_size;
 
+	      /* XXX: Isn't this check redundant when
+		 _pager_pagemap_resize is called above?  Moreover, it
+		 should be p->pagemapsize - pm_offs.  */
 	      if (bound > p->pagemapsize)
 		bound = p->pagemapsize;
 
 	      for (i = 0; i < bound; i++)
-		pm_entries[i] &= ~PM_INCORE;
+		{
+		  pm_entries[i] &= ~PM_INCORE;
+		  if (pm_entries[i] & PM_NOTIFY_PAGEOUT)
+		    pm_entries[i] |= PM_FORCEREAD;
+		}
 	    }
 	}
     }
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/libpager/pager.h hurd/libpager/pager.h
--- ../../cvs/hurd/libpager/pager.h	1999-07-04 02:51:02.000000000 +0300
+++ hurd/libpager/pager.h	2004-01-21 09:12:11.000000000 +0200
@@ -1,5 +1,5 @@
 /* Definitions for multi-threaded pager library
-   Copyright (C) 1994, 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1994,1995,1996,1997,1999,2004 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -110,7 +110,7 @@ pager_offer_page (struct pager *pager,
 /* Change the attributes of the memory object underlying pager PAGER.
    Args MAY_CACHE and COPY_STRATEGY are as for
    memory_object_change_atributes.  Wait for the kernel to report completion
-   off WAIT is set.*/
+   iff WAIT is set.  */
 void
 pager_change_attributes (struct pager *pager,
 			 boolean_t may_cache,
@@ -148,15 +148,18 @@ pager_memcpy (struct pager *pager, memor
 	      vm_offset_t offset, void *other, size_t *size,
 	      vm_prot_t prot);
 
-/* The user must define this function.  For pager PAGER, read one
-   page from offset PAGE.  Set *BUF to be the address of the page,
-   and set *WRITE_LOCK if the page must be provided read-only.
-   The only permissable error returns are EIO, EDQUOT, and ENOSPC. */
+/* The user must define this function.  For pager PAGER, read one page
+   from offset PAGE.  Set *BUF to be the address of the page, set
+   *WRITE_LOCK if the page must be provided read-only, and set
+   *NOTIFY_ON_PAGEOUT if notification via pager_notify_pageout must be
+   sent when the page is evicted.  The only permissable error returns
+   are EIO, EDQUOT, and ENOSPC. */
 error_t
 pager_read_page (struct user_pager_info *pager,
 		 vm_offset_t page,
 		 vm_address_t *buf,
-		 int *write_lock);
+		 int *write_lock,
+		 int *notify_on_pageout);
 
 /* The user must define this function.  For pager PAGER, synchronously
    write one page from BUF to offset PAGE.  In addition, mfree
@@ -172,6 +175,25 @@ error_t
 pager_unlock_page (struct user_pager_info *pager,
 		   vm_offset_t address);
 
+/* The user must define this function.  It is used when you want be
+able to remap pages.  To use it, pass non-zero value in
+NOTIFY_ON_PAGEOUT when pager_read_page is called.  When you want to
+remap a page, do the following:
+
+1. Assure pager_notify_pageout is called for the page.  This is
+usually achieved by having a flag for each page that tells if the page
+is paged out.  This is not mandatory and you can remap paged in page,
+but usually you want to remap only paged out pages.
+2. Lock the page.  No other thread must touch the page from now on.
+3. pager_return_some (pager, offset, vm_page_size, 1);
+4. Change your internal structures so that next pager_read_page will
+read the new page.
+5. Unlock the page.  Other threads are allowed to touch the page.
+*/
+error_t
+pager_notify_pageout (struct user_pager_info *pager,
+		      vm_offset_t page);
+
 /* The user must define this function.  It should report back (in
    *OFFSET and *SIZE the minimum valid address the pager will accept
    and the size of the object.   */
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/libpager/priv.h hurd/libpager/priv.h
--- ../../cvs/hurd/libpager/priv.h	2000-07-25 22:40:27.000000000 +0300
+++ hurd/libpager/priv.h	2004-01-21 09:12:30.000000000 +0200
@@ -1,5 +1,5 @@
 /* Private data for pager library.
-   Copyright (C) 1994,95,96,97,99, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1994,95,96,97,99,2000,04 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -108,6 +108,8 @@ extern int _pager_page_errors[];
 
 /* Pagemap format */
 /* These are binary state bits */
+#define PM_FORCEREAD		0x0800 /* force pager_read_page */
+#define PM_NOTIFY_PAGEOUT	0x0400 /* notify on page out */
 #define PM_WRITEWAIT  0x0200	/* queue wakeup once write is done */
 #define PM_INIT       0x0100    /* data has been written */
 #define PM_INCORE     0x0080	/* kernel might have a copy */
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/storeio/pager.c hurd/storeio/pager.c
--- ../../cvs/hurd/storeio/pager.c	2002-05-08 13:17:41.000000000 +0300
+++ hurd/storeio/pager.c	2004-01-21 09:12:51.000000000 +0200
@@ -1,6 +1,6 @@
 /* Paging interface for storeio devices
 
-   Copyright (C) 1995,96,97,99,2002 Free Software Foundation, Inc.
+   Copyright (C) 1995,96,97,99,2002,04 Free Software Foundation, Inc.
 
    Written by Miles Bader <miles@gnu.ai.mit.edu>
 
@@ -37,7 +37,8 @@
    ENOSPC. */
 error_t
 pager_read_page (struct user_pager_info *upi,
-		 vm_offset_t page, vm_address_t *buf, int *writelock)
+		 vm_offset_t page, vm_address_t *buf,
+		 int *writelock, int *notify_on_pageout)
 {
   error_t err;
   size_t read = 0;		/* bytes actually read */
@@ -57,6 +58,7 @@ pager_read_page (struct user_pager_info 
     memset ((char *)*buf + want, '\0', vm_page_size - want);
 
   *writelock = (store->flags & STORE_READONLY);
+  *notify_on_pageout = 0;
 
   if (err || read < want)
     return EIO;
@@ -109,6 +111,14 @@ pager_unlock_page (struct user_pager_inf
     return 0;
 }
 
+error_t
+pager_notify_pageout (struct user_pager_info *upi,
+		      vm_address_t offset)
+{
+  assert (!"unrequested pager_notify_pageout");
+  return 0;
+}
+
 /* The user must define this function.  It should report back (in
    *OFFSET and *SIZE the minimum valid address the pager will accept
    and the size of the object.   */
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/tmpfs/pager-stubs.c hurd/tmpfs/pager-stubs.c
--- ../../cvs/hurd/tmpfs/pager-stubs.c	2001-02-26 06:13:58.000000000 +0200
+++ hurd/tmpfs/pager-stubs.c	2004-01-21 09:13:12.000000000 +0200
@@ -1,5 +1,5 @@
 /* stupid stub functions never called, needed because libdiskfs uses libpager
-   Copyright (C) 2001 Free Software Foundation, Inc.
+   Copyright (C) 2001, 2004 Free Software Foundation, Inc.
 
 This file is part of the GNU Hurd.
 
@@ -29,7 +29,8 @@ error_t
 pager_read_page (struct user_pager_info *pager,
 		 vm_offset_t page,
 		 vm_address_t *buf,
-		 int *write_lock)
+		 int *write_lock,
+		 int *notify_on_pageout)
 {
   abort();
   return EIEIO;
@@ -57,6 +58,14 @@ pager_unlock_page (struct user_pager_inf
   return EIEIO;
 }
 
+error_t
+pager_notify_pageout (struct user_pager_info *pager,
+		      vm_offset_t offset)
+{
+  abort ();
+  return EIEIO;
+}
+
 /* The user must define this function.  It should report back (in
    *OFFSET and *SIZE the minimum valid address the pager will accept
    and the size of the object.   */
diff -urpN --exclude='*~' --exclude=CVS --exclude=ChangeLog ../../cvs/hurd/ufs/pager.c hurd/ufs/pager.c
--- ../../cvs/hurd/ufs/pager.c	1999-09-13 09:35:07.000000000 +0300
+++ hurd/ufs/pager.c	2004-01-21 09:16:13.000000000 +0200
@@ -1,5 +1,5 @@
 /* Pager for ufs
-   Copyright (C) 1994, 1995, 1996, 1997, 1999 Free Software Foundation
+   Copyright (C) 1994,1995,1996,1997,1999,2004 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -159,7 +159,8 @@ error_t
 pager_read_page (struct user_pager_info *pager,
 		 vm_offset_t page,
 		 vm_address_t *buf,
-		 int *writelock)
+		 int *writelock,
+		 int *notify_on_pageout)
 {
   error_t err;
   struct rwlock *nplock;
@@ -195,6 +196,8 @@ pager_read_page (struct user_pager_info 
   if (nplock)
     rwlock_reader_unlock (nplock);
 
+  *notify_on_pageout = 0;
+
   return err;
 }
 
@@ -425,6 +428,14 @@ pager_unlock_page (struct user_pager_inf
   return err;
 }
 
+error_t
+pager_notify_pageout (struct user_pager_info *pager,
+		      vm_offset_t offset)
+{
+  assert (!"unrequested pager_notify_pageout");
+  return 0;
+}
+
 /* Implement the pager_report_extent callback from the pager library.  See
    <hurd/pager.h> for the interface description. */
 inline error_t
