diff options
author | Marcus Brinkmann <marcus@gnu.org> | 2002-12-03 20:52:59 +0000 |
---|---|---|
committer | Marcus Brinkmann <marcus@gnu.org> | 2002-12-03 20:52:59 +0000 |
commit | 8f48e6fa4324fc242af66ab0d49e467f98656f15 (patch) | |
tree | cc222f7c92b8aa3d267833a401e69b897aad92a2 /fatfs/pager.c | |
parent | f56926743a89f4aa10302b5837aebaf5817a4e01 (diff) |
Initial check-in.
Diffstat (limited to 'fatfs/pager.c')
-rw-r--r-- | fatfs/pager.c | 1019 |
1 files changed, 1019 insertions, 0 deletions
diff --git a/fatfs/pager.c b/fatfs/pager.c new file mode 100644 index 00000000..606dc4d8 --- /dev/null +++ b/fatfs/pager.c @@ -0,0 +1,1019 @@ +/* pager.c - Pager for fatfs. + Copyright (C) 1997, 1999, 2002 Free Software Foundation, Inc. + Written by Thomas Bushnell, n/BSG and Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <string.h> +#include <hurd/store.h> +#include "fatfs.h" + +/* A ports bucket to hold pager ports. */ +struct port_bucket *pager_bucket; + +/* Mapped image of the FAT. */ +void *fat_image; + +spin_lock_t node_to_page_lock = SPIN_LOCK_INITIALIZER; + +#ifdef DONT_CACHE_MEMORY_OBJECTS +#define MAY_CACHE 0 +#else +#define MAY_CACHE 1 +#endif + +#define STAT_INC(field) /* nop */0 + +#define MAX_FREE_PAGE_BUFS 32 + +static spin_lock_t free_page_bufs_lock = SPIN_LOCK_INITIALIZER; +static void *free_page_bufs = 0; +static int num_free_page_bufs = 0; + +/* Returns a single page page-aligned buffer. */ +static void * +get_page_buf () +{ + void *buf; + + spin_lock (&free_page_bufs_lock); + + buf = free_page_bufs; + if (buf == 0) + { + spin_unlock (&free_page_bufs_lock); + buf = mmap (0, vm_page_size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + if (buf == (void *) -1) + buf = 0; + } + else + { + free_page_bufs = *(void **)buf; + num_free_page_bufs--; + spin_unlock (&free_page_bufs_lock); + } + + return buf; +} + +/* Frees a block returned by get_page_buf. */ +static void +free_page_buf (void *buf) +{ + spin_lock (&free_page_bufs_lock); + if (num_free_page_bufs < MAX_FREE_PAGE_BUFS) + { + *(void **)buf = free_page_bufs; + free_page_bufs = buf; + num_free_page_bufs++; + spin_unlock (&free_page_bufs_lock); + } + else + { + spin_unlock (&free_page_bufs_lock); + munmap (buf, vm_page_size); + } +} + +/* Find the location on disk of page OFFSET in NODE. Return the disk + cluster in CLUSTER. If *LOCK is 0, then it a reader + lock is aquired on NODE's ALLOC_LOCK before doing anything, and left + locked after return -- even if an error is returned. 0 on success or an + error code otherwise is returned. */ +static error_t +find_cluster (struct node *node, vm_offset_t offset, + cluster_t *cluster, struct rwlock **lock) +{ + error_t err; + + if (!*lock) + { + *lock = &node->dn->alloc_lock; + rwlock_reader_lock (*lock); + } + + if (offset + bytes_per_cluster > node->allocsize) + return EIO; + + err = fat_getcluster (node, offset >> log2_bytes_per_cluster, 0, cluster); + + return err; +} + +/* Read one page for the root dir pager at offset PAGE, into BUF. This + may need to select several filesystem sectors to satisfy one page. + Assumes that fat_type is FAT12 or FAT16, and that vm_page_size is a + power of two multiple of bytes_per_sector (which happens to be true). +*/ +static error_t +root_dir_pager_read_page (vm_offset_t page, void **buf, int *writelock) +{ + error_t err; + daddr_t addr; + int overrun = 0; + size_t read = 0; + + *writelock = 0; + + if (page >= diskfs_root_node->allocsize) + { + return EIO; + } + + rwlock_reader_lock(&diskfs_root_node->dn->alloc_lock); + + addr = first_root_dir_byte + page; + if (page + vm_page_size > diskfs_root_node->allocsize) + overrun = page + vm_page_size - diskfs_root_node->allocsize; + + err = store_read (store, addr >> store->log2_block_size, + vm_page_size, (void **) buf, &read); + if (!err && read != vm_page_size) + err = EIO; + + rwlock_reader_unlock (&diskfs_root_node->dn->alloc_lock); + + if (overrun) + bzero ((void *) *buf + vm_page_size - overrun, overrun); + + return err; +} + +/* Read one page for the pager backing NODE at offset PAGE, into BUF. This + may need to select only a part of a filesystem block to satisfy one page. + Assumes that bytes_per_cluster is a power of two multiple of vm_page_size. +*/ +static error_t +file_pager_read_small_page (struct node *node, vm_offset_t page, + void **buf, int *writelock) +{ + error_t err; + struct rwlock *lock = NULL; + cluster_t cluster; + size_t read = 0; + + *writelock = 0; + + if (page >= node->allocsize) + { + return EIO; + } + + err = find_cluster (node, page, &cluster, &lock); + + if (!err) + { + err = store_read (store, + (fat_first_cluster_byte(cluster) + + (page % bytes_per_cluster)) >> store->log2_block_size, + vm_page_size, (void **) buf, &read); + if (read != vm_page_size) + err = EIO; + } + + if (lock) + rwlock_reader_unlock (lock); + + return err; +} + +/* Read one page for the pager backing NODE at offset PAGE, into BUF. This + may need to read several filesystem blocks to satisfy one page, and tries + to consolidate the i/o if possible. + Assumes that vm_page_size is a power of two multiple of bytes_per_cluster. +*/ +static error_t +file_pager_read_huge_page (struct node *node, vm_offset_t page, + void **buf, int *writelock) +{ + error_t err; + int offs = 0; + struct rwlock *lock = NULL; + int left = vm_page_size; + cluster_t pending_clusters = 0; + int num_pending_clusters = 0; + + /* Read the NUM_PENDING_CLUSTERS cluster in PENDING_CLUSTERS, into the buffer + pointed to by BUF (allocating it if necessary) at offset OFFS. OFFS in + adjusted by the amount read, and NUM_PENDING_CLUSTERS is zeroed. Any read + error is returned. */ + error_t do_pending_reads () + { + if (num_pending_clusters > 0) + { + size_t dev_block = fat_first_cluster_byte(pending_clusters) >> store->log2_block_size; + size_t amount = num_pending_clusters << log2_bytes_per_cluster; + /* 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 + buffer, otherwise, we try to read directly into the tail of the + buffer we've already got. */ + void *new_buf = *buf + offs; + size_t new_len = offs == 0 ? 0 : vm_page_size - offs; + + STAT_INC (file_pagein_reads); + + err = store_read (store, dev_block, amount, &new_buf, &new_len); + if (err) + return err; + else if (amount != new_len) + return EIO; + + if (new_buf != *buf + offs) + { + /* The read went into a different buffer than the one we + passed. */ + if (offs == 0) + /* First read, make the returned page be our buffer. */ + *buf = new_buf; + else + /* We've already got some buffer, so copy into it. */ + { + memcpy (*buf + offs, new_buf, new_len); + free_page_buf (new_buf); /* Return NEW_BUF to our pool. */ + STAT_INC (file_pagein_freed_bufs); + } + } + + offs += new_len; + num_pending_clusters = 0; + } + + return 0; + } + + STAT_INC (file_pageins); + + *writelock = 0; + + if (page >= node->allocsize) + { + err = EIO; + left = 0; + } + else if (page + left > node->allocsize) + left = node->allocsize - page; + + while (left > 0) + { + cluster_t cluster; + + err = find_cluster (node, page, &cluster, &lock); + if (err) + break; + + if (cluster != pending_clusters + num_pending_clusters) + { + err = do_pending_reads (); + if (err) + break; + pending_clusters = cluster; + } + + num_pending_clusters++; + + page += bytes_per_cluster; + left -= bytes_per_cluster; + } + + if (!err && num_pending_clusters > 0) + err = do_pending_reads(); + + if (lock) + rwlock_reader_unlock (lock); + + return err; +} + +struct pending_clusters + { + /* The cluster number of the first of the clusters. */ + cluster_t cluster; + /* How many clusters we have. */ + loff_t num; + /* A (page-aligned) buffer pointing to the data we're dealing with. */ + void *buf; + /* And an offset into BUF. */ + int offs; +}; + +/* Write the any pending clusters in PC. */ +static error_t +pending_clusters_write (struct pending_clusters *pc) +{ + if (pc->num > 0) + { + error_t err; + size_t dev_block = fat_first_cluster_byte(pc->cluster) >> store->log2_block_size; + size_t length = pc->num << log2_bytes_per_cluster, amount; + + if (pc->offs > 0) + /* Put what we're going to write into a page-aligned buffer. */ + { + void *page_buf = get_page_buf (); + memcpy ((void *) page_buf, pc->buf + pc->offs, length); + err = store_write (store, dev_block, page_buf, length, &amount); + free_page_buf (page_buf); + } + else + err = store_write (store, dev_block, pc->buf, length, &amount); + if (err) + return err; + else if (amount != length) + return EIO; + + pc->offs += length; + pc->num = 0; + } + + return 0; +} + +static void +pending_clusters_init (struct pending_clusters *pc, void *buf) +{ + pc->buf = buf; + pc->cluster = 0; + pc->num = 0; + pc->offs = 0; +} + +/* Add the disk cluster CLUSTER to the list of destination disk clusters pending in + PC. */ +static error_t +pending_clusters_add (struct pending_clusters *pc, cluster_t cluster) +{ + if (cluster != pc->cluster + pc->num) + { + error_t err = pending_clusters_write (pc); + if (err) + return err; + pc->cluster = cluster; + } + pc->num++; + return 0; +} + +/* Write one page for the pager backing NODE, at offset PAGE, into BUF. This + may need to write several filesystem blocks to satisfy one page, and tries + to consolidate the i/o if possible. + Assumes that vm_page_size is a power of two multiple of bytes_per_cluster. +*/ +static error_t +file_pager_write_huge_page (struct node *node, vm_offset_t offset, void *buf) +{ + error_t err = 0; + struct pending_clusters pc; + struct rwlock *lock = &node->dn->alloc_lock; + cluster_t cluster; + int left = vm_page_size; + + pending_clusters_init (&pc, buf); + + /* Holding NODE->dn->alloc_lock effectively locks NODE->allocsize, + at least for the cases we care about: pager_unlock_page, + diskfs_grow and diskfs_truncate. */ + rwlock_reader_lock (&node->dn->alloc_lock); + + if (offset >= node->allocsize) + left = 0; + else if (offset + left > node->allocsize) + left = node->allocsize - offset; + + STAT_INC (file_pageouts); + + while (left > 0) + { + err = find_cluster (node, offset, &cluster, &lock); + if (err) + break; + pending_clusters_add (&pc, cluster); + offset += bytes_per_cluster; + left -= bytes_per_cluster; + } + + if (!err) + pending_clusters_write (&pc); + + rwlock_reader_unlock (&node->dn->alloc_lock); + + return err; +} + +/* Write one page for the root dir pager, at offset OFFSET, into BUF. This + may need to write several filesystem blocks to satisfy one page, and tries + to consolidate the i/o if possible. + Assumes that fat_type is FAT12 or FAT16 and that vm_page_size is a + power of two multiple of bytes_per_sector. +*/ +static error_t +root_dir_pager_write_page (vm_offset_t offset, void *buf) +{ + error_t err; + daddr_t addr; + size_t length; + size_t write = 0; + + if (offset >= diskfs_root_node->allocsize) + return 0; + + /* Holding NODE->dn->alloc_lock effectively locks NODE->allocsize, + at least for the cases we care about: pager_unlock_page, + diskfs_grow and diskfs_truncate. */ + rwlock_reader_lock (&diskfs_root_node->dn->alloc_lock); + + addr = first_root_dir_byte + offset; + + if (offset + vm_page_size > diskfs_root_node->allocsize) + length = diskfs_root_node->allocsize - offset; + else + length = vm_page_size; + + err = store_write (store, addr >> store->log2_block_size, (void **) buf, + length, &write); + if (!err && write != length) + err = EIO; + + rwlock_reader_unlock (&diskfs_root_node->dn->alloc_lock); + + return err; +} + +/* Write one page for the pager backing NODE, at offset OFFSET, into BUF. This + may need to write several filesystem blocks to satisfy one page, and tries + to consolidate the i/o if possible. + Assumes that bytes_per_cluster is a power of two multiple of vm_page_size. +*/ +static error_t +file_pager_write_small_page (struct node *node, vm_offset_t offset, void *buf) +{ + error_t err; + struct rwlock *lock = NULL; + cluster_t cluster; + size_t write = 0; + + if (offset >= node->allocsize) + return 0; + + /* Holding NODE->dn->alloc_lock effectively locks NODE->allocsize, + at least for the cases we care about: pager_unlock_page, + diskfs_grow and diskfs_truncate. */ + rwlock_reader_lock (&node->dn->alloc_lock); + + err = find_cluster (node, offset, &cluster, &lock); + + if (!err) + { + err = store_write (store, + (fat_first_cluster_byte(cluster) + + (offset % bytes_per_cluster)) >> store->log2_block_size, + (void **) buf, vm_page_size, &write); + if (write != vm_page_size) + err = EIO; + } + + if (lock) + rwlock_reader_unlock (lock); + + return err; +} + +static error_t +fat_pager_read_page (vm_offset_t page, void **buf, int *writelock) +{ + error_t err; + size_t length = vm_page_size, read = 0; + vm_size_t fat_end = bytes_per_sector * sectors_per_fat; + + if (page + vm_page_size > fat_end) + length = fat_end - page; + + page += first_fat_sector * bytes_per_sector; + err = store_read (store, page >> store->log2_block_size, length, buf, &read); + if (read != length) + return EIO; + if (!err && length != vm_page_size) + memset ((void *)(*buf + length), 0, vm_page_size - length); + + *writelock = 0; + + return err; +} + +static error_t +fat_pager_write_page (vm_offset_t page, void *buf) +{ + error_t err = 0; + size_t length = vm_page_size, amount; + vm_size_t fat_end = bytes_per_sector * sectors_per_fat; + + if (page + vm_page_size > fat_end) + length = fat_end - page; + + page += first_fat_sector * bytes_per_sector; + err = store_write (store, page >> store->log2_block_size, + buf, length, &amount); + if (!err && length != amount) + err = EIO; + + return err; +} + +/* 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) +{ + if (pager->type == FAT) + return fat_pager_read_page (page, (void **)buf, writelock); + else + { + if (pager->node == diskfs_root_node + && (fat_type == FAT12 || fat_type == FAT16)) + return root_dir_pager_read_page (page, (void **)buf, writelock); + else + { + if (bytes_per_cluster < vm_page_size) + return file_pager_read_huge_page (pager->node, page, + (void **)buf, writelock); + else + return file_pager_read_small_page (pager->node, page, + (void **)buf, writelock); + } + } +} + +/* Satisfy a pager write request for either the disk pager or file pager + PAGER, from the page at offset PAGE from BUF. */ +error_t +pager_write_page (struct user_pager_info *pager, vm_offset_t page, + vm_address_t buf) +{ + if (pager->type == FAT) + return fat_pager_write_page (page, (void *)buf); + else + { + if (pager->node == diskfs_root_node + && (fat_type == FAT12 || fat_type == FAT16)) + return root_dir_pager_write_page (page, (void *)buf); + else + { + if (bytes_per_cluster < vm_page_size) + return file_pager_write_huge_page (pager->node, page, + (void *)buf); + else + return file_pager_write_small_page (pager->node, page, + (void *)buf); + } + } +} + +/* Make page PAGE writable, at least up to ALLOCSIZE. */ +error_t +pager_unlock_page (struct user_pager_info *pager, + vm_offset_t page) +{ + /* All pages are writeable. The disk pages anyway, and the file + pages because blocks are directly allocated in diskfs_grow. */ + 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 + identifies the user responsible for the call. Note that this will + only be called for real files, so there is no need to be careful + about the root dir node on FAT12/16. */ +error_t +diskfs_grow (struct node *node, loff_t size, struct protid *cred) +{ + diskfs_check_readonly (); + assert (!diskfs_readonly); + + if (size > node->allocsize) + { + error_t err = 0; + loff_t old_size; + volatile loff_t new_size; + volatile cluster_t end_cluster; + cluster_t new_end_cluster; + struct disknode *dn = node->dn; + + rwlock_writer_lock (&dn->alloc_lock); + + old_size = node->allocsize; + new_size = ((size + bytes_per_cluster - 1) >> log2_bytes_per_cluster) + << log2_bytes_per_cluster; + + /* The first unallocated clusters after the old and new ends of + the file, respectively. */ + end_cluster = old_size >> log2_bytes_per_cluster; + new_end_cluster = new_size >> log2_bytes_per_cluster; + + if (new_end_cluster > end_cluster) + { + err = diskfs_catch_exception (); + while (!err && end_cluster < new_end_cluster) + { + cluster_t disk_cluster; + err = fat_getcluster (node, end_cluster++, 1, &disk_cluster); + } + diskfs_end_catch_exception (); + + if (err) + /* Reflect how much we allocated successfully. */ + new_size = (end_cluster - 1) >> log2_bytes_per_cluster; + } + + STAT_INC (file_grows); + + node->allocsize = new_size; + + rwlock_writer_unlock (&dn->alloc_lock); + + return err; + } + else + return 0; +} + +/* This syncs a single file (NODE) to disk. Wait for all I/O to + complete if WAIT is set. NODE->lock must be held. */ +void +diskfs_file_update (struct node *node, int wait) +{ + struct pager *pager; + + spin_lock (&node_to_page_lock); + pager = node->dn->pager; + if (pager) + ports_port_ref (pager); + spin_unlock (&node_to_page_lock); + + if (pager) + { + pager_sync (pager, wait); + ports_port_deref (pager); + } + + diskfs_node_update (node, wait); +} + +/* Invalidate any pager data associated with NODE. */ +void +flush_node_pager (struct node *node) +{ + struct pager *pager; + struct disknode *dn = node->dn; + + spin_lock (&node_to_page_lock); + pager = dn->pager; + if (pager) + ports_port_ref (pager); + spin_unlock (&node_to_page_lock); + + if (pager) + { + pager_flush (pager, 1); + ports_port_deref (pager); + } +} + +/* Return in *OFFSET and *SIZE the minimum valid address the pager + will accept and the size of the object. */ +inline error_t +pager_report_extent (struct user_pager_info *pager, + vm_address_t *offset, vm_size_t *size) +{ + assert (pager->type == FAT || pager->type == FILE_DATA); + + *offset = 0; + + if (pager->type == FAT) + *size = bytes_per_sector * sectors_per_fat; + else + *size = pager->node->allocsize; + + return 0; +} + +/* This is called when a pager is being deallocated after all extant + send rights have been destroyed. */ +void +pager_clear_user_data (struct user_pager_info *upi) +{ + if (upi->type == FILE_DATA) + { + struct pager *pager; + + spin_lock (&node_to_page_lock); + pager = upi->node->dn->pager; + if (pager && pager_get_upi (pager) == upi) + upi->node->dn->pager = 0; + spin_unlock (&node_to_page_lock); + + diskfs_nrele_light (upi->node); + } + + free (upi); +} + +/* This will be called when the ports library wants to drop weak + references. The pager library creates no weak references itself. + If the user doesn't either, then it's OK for this function to do + nothing. */ +void +pager_dropweak (struct user_pager_info *p __attribute__ ((unused))) +{ +} + +/* Create the disk pager. */ +void +create_fat_pager (void) +{ + struct user_pager_info *upi = malloc (sizeof (struct user_pager_info)); + upi->type = FAT; + pager_bucket = ports_create_bucket (); + diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, + bytes_per_sector * sectors_per_fat, + &fat_image); +} + +/* Call this to create a FILE_DATA pager and return a send right. + NODE must be locked. */ +mach_port_t +diskfs_get_filemap (struct node *node, vm_prot_t prot) +{ + mach_port_t right; + + assert (S_ISDIR (node->dn_stat.st_mode) + || S_ISREG (node->dn_stat.st_mode) + || (S_ISLNK (node->dn_stat.st_mode))); + + spin_lock (&node_to_page_lock); + do + { + struct pager *pager = node->dn->pager; + if (pager) + { + /* Because PAGER is not a real reference, this might be + nearly deallocated. If that's so, then the port right + will be null. In that case, clear here and loop. The + deallocation will complete separately. */ + right = pager_get_port (pager); + if (right == MACH_PORT_NULL) + node->dn->pager = 0; + else + pager_get_upi (pager)->max_prot |= prot; + } + else + { + struct user_pager_info *upi = + malloc (sizeof (struct user_pager_info)); + upi->type = FILE_DATA; + upi->node = node; + upi->max_prot = 0; + diskfs_nref_light (node); + node->dn->pager = + pager_create (upi, pager_bucket, MAY_CACHE, + MEMORY_OBJECT_COPY_DELAY); + if (node->dn->pager == 0) + { + diskfs_nrele_light (node); + free (upi); + spin_unlock (&node_to_page_lock); + return MACH_PORT_NULL; + } + + right = pager_get_port (node->dn->pager); + ports_port_deref (node->dn->pager); + } + } + while (right == MACH_PORT_NULL); + spin_unlock (&node_to_page_lock); + + mach_port_insert_right (mach_task_self (), right, right, + MACH_MSG_TYPE_MAKE_SEND); + + return right; +} + +/* Call this when we should turn off caching so that unused memory + object ports get freed. */ +void +drop_pager_softrefs (struct node *node) +{ + struct pager *pager; + + spin_lock (&node_to_page_lock); + pager = node->dn->pager; + if (pager) + ports_port_ref (pager); + spin_unlock (&node_to_page_lock); + + if (MAY_CACHE && pager) + pager_change_attributes (pager, 0, MEMORY_OBJECT_COPY_DELAY, 0); + if (pager) + ports_port_deref (pager); +} + +/* Call this when we should turn on caching because it's no longer + important for unused memory object ports to get freed. */ +void +allow_pager_softrefs (struct node *node) +{ + struct pager *pager; + + spin_lock (&node_to_page_lock); + pager = node->dn->pager; + if (pager) + ports_port_ref (pager); + spin_unlock (&node_to_page_lock); + + if (MAY_CACHE && pager) + pager_change_attributes (pager, 1, MEMORY_OBJECT_COPY_DELAY, 0); + if (pager) + ports_port_deref (pager); +} + +/* Call this to find out the struct pager * corresponding to the + FILE_DATA pager of inode IP. This should be used *only* as a + subsequent argument to register_memory_fault_area, and will be + deleted when the kernel interface is fixed. NODE must be + locked. */ +struct pager * +diskfs_get_filemap_pager_struct (struct node *node) +{ + /* This is safe because pager can't be cleared; there must be an + active mapping for this to be called. */ + return node->dn->pager; +} + +/* Shutdown all the pagers (except the disk pager). */ +void +diskfs_shutdown_pager () +{ + error_t shutdown_one (void *v_p) + { + struct pager *p = v_p; + if (p != diskfs_disk_pager) + pager_shutdown (p); + return 0; + } + + write_all_disknodes (); + + ports_bucket_iterate (pager_bucket, shutdown_one); + + pager_sync (diskfs_disk_pager, 1); + + /* Despite the name of this function, we never actually shutdown the + disk pager, just make sure it's synced. */ +} + +/* Sync all the pagers. */ +void +diskfs_sync_everything (int wait) +{ + error_t sync_one (void *v_p) + { + struct pager *p = v_p; + if (p != diskfs_disk_pager) + pager_sync (p, wait); + return 0; + } + + write_all_disknodes (); + ports_bucket_iterate (pager_bucket, sync_one); + pager_sync (diskfs_disk_pager, wait); +} + +static void +disable_caching () +{ + error_t block_cache (void *arg) + { + struct pager *p = arg; + + pager_change_attributes (p, 0, MEMORY_OBJECT_COPY_DELAY, 1); + return 0; + } + + /* Loop through the pagers and turn off caching one by one, + synchronously. That should cause termination of each pager. */ + ports_bucket_iterate (pager_bucket, block_cache); +} + +static void +enable_caching () +{ + error_t enable_cache (void *arg) + { + struct pager *p = arg; + struct user_pager_info *upi = pager_get_upi (p); + + pager_change_attributes (p, 1, MEMORY_OBJECT_COPY_DELAY, 0); + + /* It's possible that we didn't have caching on before, because + the user here is the only reference to the underlying node + (actually, that's quite likely inside this particular + routine), and if that node has no links. So dinkle the node + ref counting scheme here, which will cause caching to be + turned off, if that's really necessary. */ + if (upi->type == FILE_DATA) + { + diskfs_nref (upi->node); + diskfs_nrele (upi->node); + } + + return 0; + } + + ports_bucket_iterate (pager_bucket, enable_cache); +} + +/* Tell diskfs if there are pagers exported, and if none, then + prevent any new ones from showing up. */ +int +diskfs_pager_users () +{ + int npagers = ports_count_bucket (pager_bucket); + + if (npagers <= 1) + return 0; + + if (MAY_CACHE) + { + disable_caching (); + + /* Give it a second; the kernel doesn't actually shutdown + immediately. XXX */ + sleep (1); + + npagers = ports_count_bucket (pager_bucket); + if (npagers <= 1) + return 0; + + /* Darn, there are actual honest users. Turn caching back on, + and return failure. */ + enable_caching (); + } + + ports_enable_bucket (pager_bucket); + + return 1; +} + +/* Return the bitwise or of the maximum prot parameter (the second arg + to diskfs_get_filemap) for all active user pagers. */ +vm_prot_t +diskfs_max_user_pager_prot () +{ + vm_prot_t max_prot = 0; + int npagers = ports_count_bucket (pager_bucket); + + if (npagers > 1) + /* More than just the disk pager. */ + { + error_t add_pager_max_prot (void *v_p) + { + struct pager *p = v_p; + struct user_pager_info *upi = pager_get_upi (p); + if (upi->type == FILE_DATA) + max_prot |= upi->max_prot; + /* Stop iterating if MAX_PROT is as filled as it is going to + get. */ + return (max_prot + == (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE)) ? 1 : 0; + } + + disable_caching (); /* Make any silly pagers go away. */ + + /* Give it a second; the kernel doesn't actually shutdown + immediately. XXX */ + sleep (1); + + ports_bucket_iterate (pager_bucket, add_pager_max_prot); + + enable_caching (); + } + + ports_enable_bucket (pager_bucket); + + return max_prot; +} |