1 |
/* Pager for ext2fs |
/* Pager for ext2fs |
2 |
|
|
3 |
Copyright (C) 1994,95,96,97,98,99,2000,02 Free Software Foundation, Inc. |
Copyright (C) 1994,95,96,97,98,99,2000,02,05 Free Software Foundation, Inc. |
4 |
|
|
5 |
Converted for ext2fs by Miles Bader <miles@gnu.org> |
Converted for ext2fs by Miles Bader <miles@gnu.org> |
6 |
|
|
18 |
along with this program; if not, write to the Free Software |
along with this program; if not, write to the Free Software |
19 |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
20 |
|
|
21 |
|
#include <unistd.h> |
22 |
#include <string.h> |
#include <string.h> |
23 |
#include <errno.h> |
#include <errno.h> |
24 |
#include <hurd/store.h> |
#include <hurd/store.h> |
25 |
#include "ext2fs.h" |
#include "ext2fs.h" |
26 |
|
|
27 |
|
/* XXX */ |
28 |
|
#include "../libpager/priv.h" |
29 |
|
|
30 |
/* A ports bucket to hold pager ports. */ |
/* A ports bucket to hold pager ports. */ |
31 |
struct port_bucket *pager_bucket; |
struct port_bucket *pager_bucket; |
32 |
|
|
|
/* Mapped image of the disk. */ |
|
|
void *disk_image; |
|
|
|
|
33 |
spin_lock_t node_to_page_lock = SPIN_LOCK_INITIALIZER; |
spin_lock_t node_to_page_lock = SPIN_LOCK_INITIALIZER; |
34 |
|
|
35 |
#ifdef DONT_CACHE_MEMORY_OBJECTS |
#ifdef DONT_CACHE_MEMORY_OBJECTS |
164 |
block_t pending_blocks = 0; |
block_t pending_blocks = 0; |
165 |
int num_pending_blocks = 0; |
int num_pending_blocks = 0; |
166 |
|
|
167 |
|
ext2_debug ("reading inode %Ld page %u[%d]", |
168 |
|
node->cache_id, page, vm_page_size); |
169 |
|
|
170 |
/* Read the NUM_PENDING_BLOCKS blocks in PENDING_BLOCKS, into the buffer |
/* Read the NUM_PENDING_BLOCKS blocks in PENDING_BLOCKS, into the buffer |
171 |
pointed to by BUF (allocating it if necessary) at offset OFFS. OFFS in |
pointed to by BUF (allocating it if necessary) at offset OFFS. OFFS in |
172 |
adjusted by the amount read, and NUM_PENDING_BLOCKS is zeroed. Any read |
adjusted by the amount read, and NUM_PENDING_BLOCKS is zeroed. Any read |
175 |
{ |
{ |
176 |
if (num_pending_blocks > 0) |
if (num_pending_blocks > 0) |
177 |
{ |
{ |
178 |
block_t dev_block = pending_blocks << log2_dev_blocks_per_fs_block; |
store_offset_t dev_block = (store_offset_t) pending_blocks |
179 |
|
<< log2_dev_blocks_per_fs_block; |
180 |
size_t amount = num_pending_blocks << log2_block_size; |
size_t amount = num_pending_blocks << log2_block_size; |
181 |
/* The buffer we try to read into; on the first read, we pass in a |
/* The buffer we try to read into; on the first read, we pass in a |
182 |
size of zero, so that the read is guaranteed to allocate a new |
size of zero, so that the read is guaranteed to allocate a new |
203 |
else |
else |
204 |
/* We've already got some buffer, so copy into it. */ |
/* We've already got some buffer, so copy into it. */ |
205 |
{ |
{ |
206 |
bcopy (new_buf, *buf + offs, new_len); |
memcpy (*buf + offs, new_buf, new_len); |
207 |
free_page_buf (new_buf); /* Return NEW_BUF to our pool. */ |
free_page_buf (new_buf); /* Return NEW_BUF to our pool. */ |
208 |
STAT_INC (file_pagein_freed_bufs); |
STAT_INC (file_pagein_freed_bufs); |
209 |
} |
} |
259 |
break; |
break; |
260 |
STAT_INC (file_pagein_alloced_bufs); |
STAT_INC (file_pagein_alloced_bufs); |
261 |
} |
} |
262 |
bzero (*buf + offs, block_size); |
memset (*buf + offs, 0, block_size); |
263 |
offs += block_size; |
offs += block_size; |
264 |
} |
} |
265 |
else |
else |
300 |
if (pb->num > 0) |
if (pb->num > 0) |
301 |
{ |
{ |
302 |
error_t err; |
error_t err; |
303 |
block_t dev_block = pb->block << log2_dev_blocks_per_fs_block; |
store_offset_t dev_block = (store_offset_t) pb->block |
304 |
|
<< log2_dev_blocks_per_fs_block; |
305 |
size_t length = pb->num << log2_block_size, amount; |
size_t length = pb->num << log2_block_size, amount; |
306 |
|
|
307 |
ext2_debug ("writing block %u[%ld]", pb->block, pb->num); |
ext2_debug ("writing block %u[%Ld]", pb->block, pb->num); |
308 |
|
|
309 |
if (pb->offs > 0) |
if (pb->offs > 0) |
310 |
/* Put what we're going to write into a page-aligned buffer. */ |
/* Put what we're going to write into a page-aligned buffer. */ |
311 |
{ |
{ |
312 |
void *page_buf = get_page_buf (); |
void *page_buf = get_page_buf (); |
313 |
bcopy (pb->buf + pb->offs, (void *)page_buf, length); |
memcpy ((void *)page_buf, pb->buf + pb->offs, length); |
314 |
err = store_write (store, dev_block, page_buf, length, &amount); |
err = store_write (store, dev_block, page_buf, length, &amount); |
315 |
free_page_buf (page_buf); |
free_page_buf (page_buf); |
316 |
} |
} |
363 |
return 0; |
return 0; |
364 |
} |
} |
365 |
|
|
366 |
/* 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 |
367 |
may need to write several filesystem blocks to satisfy one page, and tries |
may need to write several filesystem blocks to satisfy one page, and tries |
368 |
to consolidate the i/o if possible. */ |
to consolidate the i/o if possible. */ |
369 |
static error_t |
static error_t |
387 |
else if (offset + left > node->allocsize) |
else if (offset + left > node->allocsize) |
388 |
left = node->allocsize - offset; |
left = node->allocsize - offset; |
389 |
|
|
390 |
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); |
391 |
|
|
392 |
STAT_INC (file_pageouts); |
STAT_INC (file_pageouts); |
393 |
|
|
415 |
{ |
{ |
416 |
error_t err; |
error_t err; |
417 |
size_t length = vm_page_size, read = 0; |
size_t length = vm_page_size, read = 0; |
418 |
vm_size_t dev_end = store->size; |
store_offset_t offset = page, dev_end = store->size; |
419 |
|
|
420 |
if (page + vm_page_size > dev_end) |
mutex_lock (&disk_cache_lock); |
421 |
length = dev_end - page; |
int index = offset >> log2_block_size; |
422 |
|
offset = ((store_offset_t) disk_cache_info[index].block << log2_block_size) |
423 |
|
+ offset % block_size; |
424 |
|
disk_cache_info[index].flags |= DC_INCORE; |
425 |
|
disk_cache_info[index].flags &=~ DC_UNTOUCHED; |
426 |
|
#ifndef NDEBUG |
427 |
|
disk_cache_info[index].last_read = disk_cache_info[index].block; |
428 |
|
disk_cache_info[index].last_read_xor |
429 |
|
= disk_cache_info[index].block ^ DISK_CACHE_LAST_READ_XOR; |
430 |
|
#endif |
431 |
|
ext2_debug ("(%Ld)", offset >> log2_block_size); |
432 |
|
mutex_unlock (&disk_cache_lock); |
433 |
|
|
434 |
|
if (offset + vm_page_size > dev_end) |
435 |
|
length = dev_end - offset; |
436 |
|
|
437 |
err = store_read (store, page >> store->log2_block_size, length, buf, &read); |
err = store_read (store, offset >> store->log2_block_size, length, |
438 |
|
buf, &read); |
439 |
if (read != length) |
if (read != length) |
440 |
return EIO; |
return EIO; |
441 |
if (!err && length != vm_page_size) |
if (!err && length != vm_page_size) |
442 |
bzero ((void *)(*buf + length), vm_page_size - length); |
memset ((void *)(*buf + length), 0, vm_page_size - length); |
443 |
|
|
444 |
*writelock = 0; |
*writelock = 0; |
445 |
|
|
451 |
{ |
{ |
452 |
error_t err = 0; |
error_t err = 0; |
453 |
size_t length = vm_page_size, amount; |
size_t length = vm_page_size, amount; |
454 |
vm_size_t dev_end = store->size; |
store_offset_t offset = page, dev_end = store->size; |
455 |
|
|
456 |
|
mutex_lock (&disk_cache_lock); |
457 |
|
int index = offset >> log2_block_size; |
458 |
|
assert (disk_cache_info[index].block != DC_NO_BLOCK); |
459 |
|
offset = ((store_offset_t) disk_cache_info[index].block << log2_block_size) |
460 |
|
+ offset % block_size; |
461 |
|
#ifndef NDEBUG /* Not strictly needed. */ |
462 |
|
assert ((disk_cache_info[index].last_read ^ DISK_CACHE_LAST_READ_XOR) |
463 |
|
== disk_cache_info[index].last_read_xor); |
464 |
|
assert (disk_cache_info[index].last_read |
465 |
|
== disk_cache_info[index].block); |
466 |
|
#endif |
467 |
|
mutex_unlock (&disk_cache_lock); |
468 |
|
|
469 |
if (page + vm_page_size > dev_end) |
if (offset + vm_page_size > dev_end) |
470 |
length = dev_end - page; |
length = dev_end - offset; |
471 |
|
|
472 |
ext2_debug ("writing disk page %d[%d]", page, length); |
ext2_debug ("writing disk page %Ld[%d]", offset, length); |
473 |
|
|
474 |
STAT_INC (disk_pageouts); |
STAT_INC (disk_pageouts); |
475 |
|
|
476 |
if (modified_global_blocks) |
if (modified_global_blocks) |
477 |
/* Be picky about which blocks in a page that we write. */ |
/* Be picky about which blocks in a page that we write. */ |
478 |
{ |
{ |
|
vm_offset_t offs = page; |
|
479 |
struct pending_blocks pb; |
struct pending_blocks pb; |
480 |
|
|
481 |
pending_blocks_init (&pb, buf); |
pending_blocks_init (&pb, buf); |
482 |
|
|
483 |
while (length > 0 && !err) |
while (length > 0 && !err) |
484 |
{ |
{ |
485 |
block_t block = boffs_block (offs); |
block_t block = boffs_block (offset); |
486 |
|
|
487 |
/* We don't clear the block modified bit here because this paging |
/* We don't clear the block modified bit here because this paging |
488 |
write request may not be the same one that actually set the bit, |
write request may not be the same one that actually set the bit, |
500 |
/* Otherwise just skip it. */ |
/* Otherwise just skip it. */ |
501 |
err = pending_blocks_skip (&pb); |
err = pending_blocks_skip (&pb); |
502 |
|
|
503 |
offs += block_size; |
offset += block_size; |
504 |
length -= block_size; |
length -= block_size; |
505 |
} |
} |
506 |
|
|
509 |
} |
} |
510 |
else |
else |
511 |
{ |
{ |
512 |
err = store_write (store, page >> store->log2_block_size, |
err = store_write (store, offset >> store->log2_block_size, |
513 |
buf, length, &amount); |
buf, length, &amount); |
514 |
if (!err && length != amount) |
if (!err && length != amount) |
515 |
err = EIO; |
err = EIO; |
517 |
|
|
518 |
return err; |
return err; |
519 |
} |
} |
520 |
|
|
521 |
|
static void |
522 |
|
disk_pager_notify_evict (vm_offset_t page) |
523 |
|
{ |
524 |
|
int index = page >> log2_block_size; |
525 |
|
|
526 |
|
ext2_debug ("(block %u)", index); |
527 |
|
|
528 |
|
mutex_lock (&disk_cache_lock); |
529 |
|
disk_cache_info[index].flags &= ~DC_INCORE; |
530 |
|
mutex_unlock (&disk_cache_lock); |
531 |
|
} |
532 |
|
|
533 |
/* Satisfy a pager read request for either the disk pager or file pager |
/* Satisfy a pager read request for either the disk pager or file pager |
534 |
PAGER, to the page at offset PAGE into BUF. WRITELOCK should be set if |
PAGER, to the page at offset PAGE into BUF. WRITELOCK should be set if |
554 |
else |
else |
555 |
return file_pager_write_page (pager->node, page, (void *)buf); |
return file_pager_write_page (pager->node, page, (void *)buf); |
556 |
} |
} |
557 |
|
|
558 |
|
void |
559 |
|
pager_notify_evict (struct user_pager_info *pager, vm_offset_t page) |
560 |
|
{ |
561 |
|
if (pager->type == DISK) |
562 |
|
disk_pager_notify_evict (page); |
563 |
|
} |
564 |
|
|
565 |
|
|
566 |
/* Make page PAGE writable, at least up to ALLOCSIZE. This function and |
/* Make page PAGE writable, at least up to ALLOCSIZE. This function and |
567 |
diskfs_grow are the only places that blocks are actually added to the |
diskfs_grow are the only places that blocks are actually added to the |
611 |
|
|
612 |
#ifdef EXT2FS_DEBUG |
#ifdef EXT2FS_DEBUG |
613 |
if (dn->last_page_partially_writable) |
if (dn->last_page_partially_writable) |
614 |
ext2_debug ("made page %u[%lu] in inode %d partially writable", |
ext2_debug ("made page %u[%Lu] in inode %Ld partially writable", |
615 |
page, node->allocsize - page, node->cache_id); |
page, node->allocsize - page, node->cache_id); |
616 |
else |
else |
617 |
ext2_debug ("made page %u[%u] in inode %d writable", |
ext2_debug ("made page %u[%u] in inode %Ld writable", |
618 |
page, vm_page_size, node->cache_id); |
page, vm_page_size, node->cache_id); |
619 |
#endif |
#endif |
620 |
|
|
672 |
block_t old_page_end_block = |
block_t old_page_end_block = |
673 |
round_page (old_size) >> log2_block_size; |
round_page (old_size) >> log2_block_size; |
674 |
|
|
675 |
ext2_debug ("growing inode %d to %lu bytes (from %lu)", node->cache_id, |
ext2_debug ("growing inode %Ld to %Lu bytes (from %Lu)", |
676 |
new_size, old_size); |
node->cache_id, new_size, old_size); |
677 |
|
|
678 |
if (dn->last_page_partially_writable |
if (dn->last_page_partially_writable |
679 |
&& old_page_end_block > end_block) |
&& old_page_end_block > end_block) |
709 |
|
|
710 |
STAT_INC (file_grows); |
STAT_INC (file_grows); |
711 |
|
|
712 |
ext2_debug ("new size: %ld%s.", new_size, |
ext2_debug ("new size: %Lu%s.", new_size, |
713 |
dn->last_page_partially_writable |
dn->last_page_partially_writable |
714 |
? " (last page writable)": ""); |
? " (last page writable)": ""); |
715 |
if (err) |
if (err) |
716 |
ext2_warning ("inode=%Ld, target=%Ld: %s", |
ext2_warning ("inode=%Ld, target=%Lu: %s", |
717 |
node->cache_id, new_size, strerror (err)); |
node->cache_id, new_size, strerror (err)); |
718 |
|
|
719 |
node->allocsize = new_size; |
node->allocsize = new_size; |
818 |
{ |
{ |
819 |
} |
} |
820 |
|
|
821 |
|
/* Cached blocks from disk. */ |
822 |
|
void *disk_cache; |
823 |
|
|
824 |
|
/* DISK_CACHE size in bytes and blocks. */ |
825 |
|
store_offset_t disk_cache_size; |
826 |
|
int disk_cache_blocks; |
827 |
|
|
828 |
|
/* block num --> pointer to in-memory block */ |
829 |
|
hurd_ihash_t disk_cache_bptr; |
830 |
|
/* Cached blocks' info. */ |
831 |
|
struct disk_cache_info *disk_cache_info; |
832 |
|
/* Hint index for which cache block to reuse next. */ |
833 |
|
int disk_cache_hint; |
834 |
|
/* Lock for these structures. */ |
835 |
|
struct mutex disk_cache_lock; |
836 |
|
/* Fired when a re-association is done. */ |
837 |
|
struct condition disk_cache_reassociation; |
838 |
|
|
839 |
|
/* Finish mapping initialization. */ |
840 |
|
static void |
841 |
|
disk_cache_init (void) |
842 |
|
{ |
843 |
|
if (block_size != vm_page_size) |
844 |
|
ext2_panic ("Block size %d != vm_page_size %d", |
845 |
|
block_size, vm_page_size); |
846 |
|
|
847 |
|
mutex_init (&disk_cache_lock); |
848 |
|
condition_init (&disk_cache_reassociation); |
849 |
|
|
850 |
|
/* Allocate space for block num -> in-memory pointer mapping. */ |
851 |
|
if (hurd_ihash_create (&disk_cache_bptr, HURD_IHASH_NO_LOCP)) |
852 |
|
ext2_panic ("Can't allocate memory for disk_pager_bptr"); |
853 |
|
|
854 |
|
/* Allocate space for disk cache blocks' info. */ |
855 |
|
disk_cache_info = malloc ((sizeof *disk_cache_info) * disk_cache_blocks); |
856 |
|
if (!disk_cache_info) |
857 |
|
ext2_panic ("Cannot allocate space for disk cache info"); |
858 |
|
|
859 |
|
/* Initialize disk_cache_info. */ |
860 |
|
for (int i = 0; i < disk_cache_blocks; i++) |
861 |
|
{ |
862 |
|
disk_cache_info[i].block = DC_NO_BLOCK; |
863 |
|
disk_cache_info[i].flags = 0; |
864 |
|
disk_cache_info[i].ref_count = 0; |
865 |
|
#ifndef NDEBUG |
866 |
|
disk_cache_info[i].last_read = DC_NO_BLOCK; |
867 |
|
disk_cache_info[i].last_read_xor |
868 |
|
= DC_NO_BLOCK ^ DISK_CACHE_LAST_READ_XOR; |
869 |
|
#endif |
870 |
|
} |
871 |
|
disk_cache_hint = 0; |
872 |
|
|
873 |
|
/* Map the superblock and the block group descriptors. */ |
874 |
|
block_t fixed_first = boffs_block (SBLOCK_OFFS); |
875 |
|
block_t fixed_last = fixed_first |
876 |
|
+ (round_block ((sizeof *group_desc_image) * groups_count) |
877 |
|
>> log2_block_size); |
878 |
|
ext2_debug ("%d-%d\n", fixed_first, fixed_last); |
879 |
|
assert (fixed_last - fixed_first + 1 <= (block_t)disk_cache_blocks + 3); |
880 |
|
for (block_t i = fixed_first; i <= fixed_last; i++) |
881 |
|
{ |
882 |
|
buffer_lookup (i); |
883 |
|
assert (disk_cache_info[i-fixed_first].block == i); |
884 |
|
disk_cache_info[i-fixed_first].flags |= DC_FIXED; |
885 |
|
} |
886 |
|
} |
887 |
|
|
888 |
|
static void |
889 |
|
disk_cache_return_unused (void) |
890 |
|
{ |
891 |
|
int index; |
892 |
|
|
893 |
|
/* XXX: Touch all pages. It seems that sometimes GNU Mach "forgets" |
894 |
|
to notify us about evicted pages. Disk cache must be |
895 |
|
unlocked. */ |
896 |
|
for (vm_offset_t i = 0; i < disk_cache_size; i += vm_page_size) |
897 |
|
*(volatile char *)(disk_cache + i); |
898 |
|
|
899 |
|
/* Release some references to cached blocks. */ |
900 |
|
pokel_sync (&global_pokel, 1); |
901 |
|
|
902 |
|
/* Return unused pages that are in core. */ |
903 |
|
int pending_begin = -1, pending_end = -1; |
904 |
|
mutex_lock (&disk_cache_lock); |
905 |
|
for (index = 0; index < disk_cache_blocks; index++) |
906 |
|
if (! (disk_cache_info[index].flags & (DC_DONT_REUSE & ~DC_INCORE)) |
907 |
|
&& ! disk_cache_info[index].ref_count) |
908 |
|
{ |
909 |
|
ext2_debug ("return %u -> %d", |
910 |
|
disk_cache_info[index].block, index); |
911 |
|
if (index != pending_end) |
912 |
|
{ |
913 |
|
/* Return previous region, if there is such, ... */ |
914 |
|
if (pending_end >= 0) |
915 |
|
{ |
916 |
|
mutex_unlock (&disk_cache_lock); |
917 |
|
pager_return_some (diskfs_disk_pager, |
918 |
|
pending_begin * vm_page_size, |
919 |
|
(pending_end - pending_begin) |
920 |
|
* vm_page_size, |
921 |
|
1); |
922 |
|
mutex_lock (&disk_cache_lock); |
923 |
|
} |
924 |
|
/* ... and start new region. */ |
925 |
|
pending_begin = index; |
926 |
|
} |
927 |
|
pending_end = index + 1; |
928 |
|
} |
929 |
|
|
930 |
|
mutex_unlock (&disk_cache_lock); |
931 |
|
|
932 |
|
/* Return last region, if there is such. */ |
933 |
|
if (pending_end >= 0) |
934 |
|
pager_return_some (diskfs_disk_pager, |
935 |
|
pending_begin * vm_page_size, |
936 |
|
(pending_end - pending_begin) * vm_page_size, |
937 |
|
1); |
938 |
|
else |
939 |
|
{ |
940 |
|
printf ("ext2fs: disk cache is starving\n"); |
941 |
|
|
942 |
|
/* Give it some time. This should happen rarely. */ |
943 |
|
sleep (1); |
944 |
|
} |
945 |
|
} |
946 |
|
|
947 |
|
/* Map block and return pointer to it. */ |
948 |
|
void * |
949 |
|
buffer_lookup (block_t block) |
950 |
|
{ |
951 |
|
int index; |
952 |
|
void *bptr; |
953 |
|
|
954 |
|
assert (0 <= block && block < store->size >> log2_block_size); |
955 |
|
|
956 |
|
ext2_debug ("(%u)", block); |
957 |
|
|
958 |
|
mutex_lock (&disk_cache_lock); |
959 |
|
|
960 |
|
bptr = hurd_ihash_find (disk_cache_bptr, block); |
961 |
|
if (bptr) |
962 |
|
/* Already mapped. */ |
963 |
|
{ |
964 |
|
index = bptr_index (bptr); |
965 |
|
|
966 |
|
/* In process of re-associating? */ |
967 |
|
if (disk_cache_info[index].flags & DC_UNTOUCHED) |
968 |
|
{ |
969 |
|
/* Wait re-association to finish. */ |
970 |
|
condition_wait (&disk_cache_reassociation, &disk_cache_lock); |
971 |
|
mutex_unlock (&disk_cache_lock); |
972 |
|
|
973 |
|
/* Try again. */ |
974 |
|
return buffer_lookup (block); /* tail recursion */ |
975 |
|
} |
976 |
|
|
977 |
|
/* Just increment reference and return. */ |
978 |
|
assert (disk_cache_info[index].ref_count + 1 |
979 |
|
> disk_cache_info[index].ref_count); |
980 |
|
disk_cache_info[index].ref_count++; |
981 |
|
|
982 |
|
ext2_debug ("cached %u -> %d (ref_count = %d, flags = 0x%x, ptr = %p)", |
983 |
|
disk_cache_info[index].block, index, |
984 |
|
disk_cache_info[index].ref_count, |
985 |
|
disk_cache_info[index].flags, bptr); |
986 |
|
|
987 |
|
mutex_unlock (&disk_cache_lock); |
988 |
|
|
989 |
|
return bptr; |
990 |
|
} |
991 |
|
|
992 |
|
/* Search for a block that is not in core and is not referenced. */ |
993 |
|
index = disk_cache_hint; |
994 |
|
while ((disk_cache_info[index].flags & DC_DONT_REUSE) |
995 |
|
|| (disk_cache_info[index].ref_count)) |
996 |
|
{ |
997 |
|
ext2_debug ("reject %u -> %d (ref_count = %d, flags = 0x%x)", |
998 |
|
disk_cache_info[index].block, index, |
999 |
|
disk_cache_info[index].ref_count, |
1000 |
|
disk_cache_info[index].flags); |
1001 |
|
|
1002 |
|
/* Just move to next block. */ |
1003 |
|
index++; |
1004 |
|
if (index >= disk_cache_blocks) |
1005 |
|
index -= disk_cache_blocks; |
1006 |
|
|
1007 |
|
/* If we return to where we started, than there is no suitable |
1008 |
|
block. */ |
1009 |
|
if (index == disk_cache_hint) |
1010 |
|
break; |
1011 |
|
} |
1012 |
|
|
1013 |
|
/* The next place in the disk cache becomes the current hint. */ |
1014 |
|
disk_cache_hint = index + 1; |
1015 |
|
if (disk_cache_hint >= disk_cache_blocks) |
1016 |
|
disk_cache_hint -= disk_cache_blocks; |
1017 |
|
|
1018 |
|
/* Is suitable place found? */ |
1019 |
|
if ((disk_cache_info[index].flags & DC_DONT_REUSE) |
1020 |
|
|| disk_cache_info[index].ref_count) |
1021 |
|
/* No place is found. Try to release some blocks and try |
1022 |
|
again. */ |
1023 |
|
{ |
1024 |
|
ext2_debug ("flush %u -> %d", disk_cache_info[index].block, index); |
1025 |
|
|
1026 |
|
mutex_unlock (&disk_cache_lock); |
1027 |
|
|
1028 |
|
disk_cache_return_unused (); |
1029 |
|
|
1030 |
|
return buffer_lookup (block); /* tail recursion */ |
1031 |
|
} |
1032 |
|
|
1033 |
|
/* Suitable place is found. */ |
1034 |
|
|
1035 |
|
/* Calculate pointer to data. */ |
1036 |
|
bptr = (char *)disk_cache + (index << log2_block_size); |
1037 |
|
ext2_debug ("map %u -> %d (%p)", block, index, bptr); |
1038 |
|
|
1039 |
|
/* This pager_return_some is used only to set PM_FORCEREAD for the |
1040 |
|
page. DC_UNTOUCHED is set so that we catch if someone has |
1041 |
|
referenced the block while we didn't hold disk_cache_lock. */ |
1042 |
|
disk_cache_info[index].flags |= DC_UNTOUCHED; |
1043 |
|
|
1044 |
|
#if 0 /* XXX: Let's see if this is needed at all. */ |
1045 |
|
|
1046 |
|
mutex_unlock (&disk_cache_lock); |
1047 |
|
pager_return_some (diskfs_disk_pager, bptr - disk_cache, vm_page_size, 1); |
1048 |
|
mutex_lock (&disk_cache_lock); |
1049 |
|
|
1050 |
|
/* Has someone used our bptr? Has someone mapped requested block |
1051 |
|
while we have unlocked disk_cache_lock? If so, environment has |
1052 |
|
changed and we have to restart operation. */ |
1053 |
|
if ((! (disk_cache_info[index].flags & DC_UNTOUCHED)) |
1054 |
|
|| hurd_ihash_find (disk_cache_bptr, block)) |
1055 |
|
{ |
1056 |
|
mutex_unlock (&disk_cache_lock); |
1057 |
|
return buffer_lookup (block); /* tail recursion */ |
1058 |
|
} |
1059 |
|
|
1060 |
|
#elif 0 |
1061 |
|
|
1062 |
|
/* XXX: Use libpager internals. */ |
1063 |
|
|
1064 |
|
mutex_lock (&diskfs_disk_pager->interlock); |
1065 |
|
int page = (bptr - disk_cache) / vm_page_size; |
1066 |
|
assert (page >= 0); |
1067 |
|
int is_incore = (page < diskfs_disk_pager->pagemapsize |
1068 |
|
&& (diskfs_disk_pager->pagemap[page] & PM_INCORE)); |
1069 |
|
mutex_unlock (&diskfs_disk_pager->interlock); |
1070 |
|
if (is_incore) |
1071 |
|
{ |
1072 |
|
mutex_unlock (&disk_cache_lock); |
1073 |
|
return buffer_lookup (block); /* tail recursion */ |
1074 |
|
} |
1075 |
|
|
1076 |
|
#endif |
1077 |
|
|
1078 |
|
/* Re-associate. */ |
1079 |
|
if (disk_cache_info[index].block != DC_NO_BLOCK) |
1080 |
|
/* Remove old association. */ |
1081 |
|
hurd_ihash_remove (disk_cache_bptr, disk_cache_info[index].block); |
1082 |
|
/* New association. */ |
1083 |
|
if (hurd_ihash_add (disk_cache_bptr, block, bptr)) |
1084 |
|
ext2_panic ("Couldn't hurd_ihash_add new disk block"); |
1085 |
|
assert (! (disk_cache_info[index].flags & DC_DONT_REUSE & ~DC_UNTOUCHED)); |
1086 |
|
disk_cache_info[index].block = block; |
1087 |
|
assert (! disk_cache_info[index].ref_count); |
1088 |
|
disk_cache_info[index].ref_count = 1; |
1089 |
|
|
1090 |
|
/* All data structures are set up. */ |
1091 |
|
mutex_unlock (&disk_cache_lock); |
1092 |
|
|
1093 |
|
/* Try to read page. */ |
1094 |
|
*(volatile char *) bptr; |
1095 |
|
|
1096 |
|
/* Check if it's actually read. */ |
1097 |
|
mutex_lock (&disk_cache_lock); |
1098 |
|
if (disk_cache_info[index].flags & DC_UNTOUCHED) |
1099 |
|
/* It's not read. */ |
1100 |
|
{ |
1101 |
|
/* Remove newly created association. */ |
1102 |
|
hurd_ihash_remove (disk_cache_bptr, block); |
1103 |
|
disk_cache_info[index].block = DC_NO_BLOCK; |
1104 |
|
disk_cache_info[index].flags &=~ DC_UNTOUCHED; |
1105 |
|
disk_cache_info[index].ref_count = 0; |
1106 |
|
mutex_unlock (&disk_cache_lock); |
1107 |
|
|
1108 |
|
/* Prepare next time association of this page to succeed. */ |
1109 |
|
pager_flush_some (diskfs_disk_pager, bptr - disk_cache, |
1110 |
|
vm_page_size, 0); |
1111 |
|
|
1112 |
|
/* Try again. */ |
1113 |
|
return buffer_lookup (block); /* tail recursion */ |
1114 |
|
} |
1115 |
|
mutex_unlock (&disk_cache_lock); |
1116 |
|
|
1117 |
|
/* Re-association was successful. */ |
1118 |
|
condition_broadcast (&disk_cache_reassociation); |
1119 |
|
|
1120 |
|
ext2_debug ("(%u) = %p", block, bptr); |
1121 |
|
return bptr; |
1122 |
|
} |
1123 |
|
|
1124 |
|
void |
1125 |
|
buffer_ref (void *ptr) |
1126 |
|
{ |
1127 |
|
int index; |
1128 |
|
|
1129 |
|
mutex_lock (&disk_cache_lock); |
1130 |
|
index = bptr_index (ptr); |
1131 |
|
assert (disk_cache_info[index].ref_count >= 1); |
1132 |
|
assert (disk_cache_info[index].ref_count + 1 |
1133 |
|
> disk_cache_info[index].ref_count); |
1134 |
|
disk_cache_info[index].ref_count++; |
1135 |
|
assert (! (disk_cache_info[index].flags & DC_UNTOUCHED)); |
1136 |
|
ext2_debug ("(%p) (ref_count = %d, flags = 0x%x)", |
1137 |
|
ptr, |
1138 |
|
disk_cache_info[index].ref_count, |
1139 |
|
disk_cache_info[index].flags); |
1140 |
|
mutex_unlock (&disk_cache_lock); |
1141 |
|
} |
1142 |
|
|
1143 |
|
void |
1144 |
|
buffer_put (void *ptr) |
1145 |
|
{ |
1146 |
|
int index; |
1147 |
|
|
1148 |
|
assert (disk_cache <= ptr && ptr <= disk_cache + disk_cache_size); |
1149 |
|
|
1150 |
|
mutex_lock (&disk_cache_lock); |
1151 |
|
index = bptr_index (ptr); |
1152 |
|
ext2_debug ("(%p) (ref_count = %d, flags = 0x%x)", |
1153 |
|
ptr, |
1154 |
|
disk_cache_info[index].ref_count - 1, |
1155 |
|
disk_cache_info[index].flags); |
1156 |
|
assert (! (disk_cache_info[index].flags & DC_UNTOUCHED)); |
1157 |
|
assert (disk_cache_info[index].ref_count >= 1); |
1158 |
|
disk_cache_info[index].ref_count--; |
1159 |
|
mutex_unlock (&disk_cache_lock); |
1160 |
|
} |
1161 |
|
|
1162 |
|
/* Used only in an assertion. */ |
1163 |
|
int |
1164 |
|
buffer_is_ref (block_t block) |
1165 |
|
{ |
1166 |
|
int ref; |
1167 |
|
void *ptr; |
1168 |
|
|
1169 |
|
mutex_lock (&disk_cache_lock); |
1170 |
|
ptr = hurd_ihash_find (disk_cache_bptr, block); |
1171 |
|
if (! ptr) |
1172 |
|
ref = 0; |
1173 |
|
else /* XXX: Should check for DC_UNTOUCHED too. */ |
1174 |
|
ref = disk_cache_info[bptr_index (ptr)].ref_count; |
1175 |
|
mutex_unlock (&disk_cache_lock); |
1176 |
|
|
1177 |
|
return ref; |
1178 |
|
} |
1179 |
|
|
1180 |
/* Create the DISK pager. */ |
/* Create the DISK pager. */ |
1181 |
void |
void |
1182 |
create_disk_pager (void) |
create_disk_pager (void) |
1186 |
ext2_panic ("can't create disk pager: %s", strerror (errno)); |
ext2_panic ("can't create disk pager: %s", strerror (errno)); |
1187 |
upi->type = DISK; |
upi->type = DISK; |
1188 |
pager_bucket = ports_create_bucket (); |
pager_bucket = ports_create_bucket (); |
1189 |
diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, store->size, |
get_hypermetadata (); |
1190 |
&disk_image); |
disk_cache_blocks = DISK_CACHE_BLOCKS; |
1191 |
|
disk_cache_size = disk_cache_blocks << log2_block_size; |
1192 |
|
diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, 1, |
1193 |
|
disk_cache_size, &disk_cache); |
1194 |
|
disk_cache_init (); |
1195 |
} |
} |
1196 |
|
|
1197 |
/* Call this to create a FILE_DATA pager and return a send right. |
/* Call this to create a FILE_DATA pager and return a send right. |
1231 |
diskfs_nref_light (node); |
diskfs_nref_light (node); |
1232 |
node->dn->pager = |
node->dn->pager = |
1233 |
pager_create (upi, pager_bucket, MAY_CACHE, |
pager_create (upi, pager_bucket, MAY_CACHE, |
1234 |
MEMORY_OBJECT_COPY_DELAY); |
MEMORY_OBJECT_COPY_DELAY, 0); |
1235 |
if (node->dn->pager == 0) |
if (node->dn->pager == 0) |
1236 |
{ |
{ |
1237 |
diskfs_nrele_light (node); |
diskfs_nrele_light (node); |