summaryrefslogtreecommitdiff
path: root/nfs/pager.c
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1996-07-18 04:35:29 +0000
committerMichael I. Bushnell <mib@gnu.org>1996-07-18 04:35:29 +0000
commit94cef36797600d11a50d09828fa80df8a73dfd1c (patch)
treeb7cba9afef95489eedef534d3e6946eb13f595ba /nfs/pager.c
parent88dbbbf9e48e24f1ac007c1e4eeffd9caf8e2fad (diff)
*** empty log message ***
Diffstat (limited to 'nfs/pager.c')
-rw-r--r--nfs/pager.c448
1 files changed, 448 insertions, 0 deletions
diff --git a/nfs/pager.c b/nfs/pager.c
new file mode 100644
index 00000000..687a0e2f
--- /dev/null
+++ b/nfs/pager.c
@@ -0,0 +1,448 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ 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 "nfs.h"
+#include <unistd.h>
+#include <hurd/pager.h>
+#include <netinet/in.h>
+#include <string.h>
+
+struct user_pager_info
+{
+ struct node *np;
+ struct pager *p;
+ int max_prot;
+};
+
+struct pager_cache_rec
+{
+ struct pager_cache_rec *next;
+ vm_offset_t offset;
+ struct pager *p;
+ time_t fetched;
+};
+
+static struct pager_cache_rec *pager_cache_recs;
+static spin_lock_t pager_cache_rec_lock = SPIN_LOCK_INITIALIZER;
+static spin_lock_t node2pagelock = SPIN_LOCK_INITIALIZER;
+static struct port_bucket *pager_bucket;
+
+void
+register_new_page (struct pager *p, vm_offset_t offset)
+{
+ struct pager_cache_rec *pc;
+
+ pc = malloc (sizeof (struct pager_cache_rec));
+ pc->offset = offset;
+ pc->p = p;
+ ports_port_ref (p);
+ pc->fetched = mapped_time->seconds;
+
+ spin_lock (&pager_cache_rec_lock);
+ pc->next = pager_cache_recs;
+ pager_cache_recs = pc;
+ spin_unlock (&pager_cache_rec_lock);
+}
+
+any_t
+flush_pager_cache_thread (any_t foo2)
+{
+ struct pager_cache_rec *pc, *next, **ppc, *list;
+
+ for (;;)
+ {
+ sleep (1);
+
+ /* Dequeue from the main list and queue locally the recs
+ for expired pages. */
+ list = 0;
+ spin_lock (&pager_cache_rec_lock);
+ for (pc = pager_cache_recs, ppc = &pager_cache_recs;
+ pc;
+ ppc = &pc->next, pc = next)
+ {
+ next = pc->next;
+ if (mapped_time->seconds - pc->fetched > cache_timeout)
+ {
+ *ppc = pc->next;
+ pc->next = list;
+ list = pc;
+ }
+ }
+ spin_unlock (&pager_cache_rec_lock);
+
+ /* And now, one at a time, expire them */
+ for (pc = list; pc; pc = next)
+ {
+ pager_return_some (pc->p, pc->offset, vm_page_size, 0);
+ next = pc->next;
+ ports_port_deref (pc->p);
+ free (pc);
+ }
+ }
+}
+
+error_t
+pager_read_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t *buf,
+ int *writelock)
+{
+ error_t err;
+ int *p;
+ void *rpcbuf;
+ struct node *np;
+ size_t amt, thisamt, trans_len;
+ void *data;
+ off_t offset;
+
+ np = pager->np;
+
+ mutex_lock (&np->lock);
+
+ vm_allocate (mach_task_self (), buf, vm_page_size, 1);
+ data = (char *) *buf;
+ amt = vm_page_size;
+ offset = page;
+
+ while (amt)
+ {
+ thisamt = amt;
+ if (thisamt > read_size)
+ thisamt = read_size;
+
+ p = nfs_initialize_rpc (NFSPROC_READ, (struct netcred *)-1, 0,
+ &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *p++ = htonl (offset);
+ *p++ = htonl (vm_page_size);
+ *p++ = 0;
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ if (err)
+ {
+ mutex_unlock (&np->lock);
+ free (rpcbuf);
+ vm_deallocate (mach_task_self (), *buf, vm_page_size);
+ return err;
+ }
+
+ p = register_fresh_stat (np, p);
+ trans_len = ntohl (*p++);
+ if (trans_len > thisamt)
+ trans_len = thisamt; /* ??? */
+
+ bcopy (p, data, trans_len);
+
+ free (rpcbuf);
+
+ data += trans_len;
+ offset += trans_len;
+ amt -= trans_len;
+
+ /* If we got a short count, we're all done. */
+ if (trans_len < thisamt)
+ break;
+ }
+
+ register_new_page (pager->p, page);
+ mutex_unlock (&np->lock);
+ return 0;
+}
+
+
+error_t
+pager_write_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t buf)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ size_t amt, thisamt;
+ off_t offset;
+ struct node *np;
+ void *data;
+
+ np = pager->np;
+ mutex_lock (&np->lock);
+
+ amt = vm_page_size;
+ offset = page;
+ data = (void *) buf;
+
+ while (amt)
+ {
+ thisamt = amt;
+ if (amt > write_size)
+ amt = write_size;
+
+ p = nfs_initialize_rpc (NFSPROC_WRITE, (struct netcred *) -1,
+ amt, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *p++ = 0;
+ *p++ = htonl (offset);
+ *p++ = 0;
+ p = xdr_encode_data (p, data, thisamt);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ if (err)
+ {
+ free (rpcbuf);
+ vm_deallocate (mach_task_self (), buf, vm_page_size);
+ return err;
+ }
+ register_fresh_stat (np, p);
+ free (rpcbuf);
+ amt -= thisamt;
+ data += thisamt;
+ offset += thisamt;
+ }
+
+ vm_deallocate (mach_task_self (), buf, vm_page_size);
+ mutex_unlock (&np->lock);
+ return 0;
+}
+
+error_t
+pager_unlock_page (struct user_pager_info *pager,
+ vm_offset_t address)
+{
+ abort ();
+}
+
+error_t
+pager_report_extent (struct user_pager_info *pager,
+ vm_address_t *offset,
+ vm_size_t *size)
+{
+ struct node *np;
+ error_t err;
+
+ np = pager->np;
+ mutex_lock (&np->lock);
+
+ err = netfs_validate_stat (np, 0);
+ if (!err)
+ *size = round_page (np->nn_stat.st_size);
+ mutex_unlock (&np->lock);
+ return err;
+}
+
+void
+pager_clear_user_data (struct user_pager_info *upi)
+{
+ spin_lock (&node2pagelock);
+ if (upi->np->nn->fileinfo == upi)
+ upi->np->nn->fileinfo = 0;
+ spin_unlock (&node2pagelock);
+ netfs_nrele (upi->np);
+ free (upi);
+}
+
+void
+pager_dropweak (struct user_pager_info *upi)
+{
+ abort ();
+}
+
+mach_port_t
+netfs_get_filemap (struct node *np, vm_prot_t prot)
+{
+ struct user_pager_info *upi;
+ mach_port_t right;
+
+ spin_lock (&node2pagelock);
+ do
+ if (!np->nn->fileinfo)
+ {
+ upi = malloc (sizeof (struct user_pager_info));
+ upi->np = np;
+ netfs_nref (np);
+ upi->max_prot = prot;
+ upi->p = pager_create (upi, pager_bucket, 1, MEMORY_OBJECT_COPY_NONE);
+ np->nn->fileinfo = upi;
+ right = pager_get_port (np->nn->fileinfo->p);
+ ports_port_deref (np->nn->fileinfo->p);
+ }
+ else
+ {
+ np->nn->fileinfo->max_prot |= prot;
+ /* Because NP->dn->fileinfo->p 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 (np->nn->fileinfo->p);
+ if (right == MACH_PORT_NULL)
+ np->nn->fileinfo = 0;
+ }
+ while (right == MACH_PORT_NULL);
+
+ spin_unlock (&node2pagelock);
+
+ mach_port_insert_right (mach_task_self (), right, right,
+ MACH_MSG_TYPE_MAKE_SEND);
+ return right;
+}
+
+void
+drop_pager_softrefs (struct node *np)
+{
+ struct user_pager_info *upi;
+
+ spin_lock (&node2pagelock);
+ upi = np->nn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ spin_unlock (&node2pagelock);
+
+ if (upi)
+ {
+ pager_change_attributes (upi->p, 0, MEMORY_OBJECT_COPY_NONE, 0);
+ ports_port_deref (upi->p);
+ }
+}
+
+void
+allow_pager_softrefs (struct node *np)
+{
+ struct user_pager_info *upi;
+
+ spin_lock (&node2pagelock);
+ upi = np->nn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ spin_unlock (&node2pagelock);
+
+ if (upi)
+ {
+ pager_change_attributes (upi->p, 1, MEMORY_OBJECT_COPY_NONE, 0);
+ ports_port_deref (upi->p);
+ }
+}
+
+void
+block_caching ()
+{
+ error_t block_cache (void *arg)
+ {
+ struct pager *p = arg;
+ pager_change_attributes (p, 0, MEMORY_OBJECT_COPY_NONE, 1);
+ return 0;
+ }
+ ports_bucket_iterate (pager_bucket, block_cache);
+}
+
+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_NONE, 0);
+ return 0;
+ }
+
+ ports_bucket_iterate (pager_bucket, enable_cache);
+}
+
+int
+netfs_pager_users ()
+{
+ int npagers = ports_count_bucket (pager_bucket);
+
+ if (!npagers)
+ return 0;
+
+ block_caching ();
+ /* Give it a sec; the kernel doesn't issue the shutdown right away */
+ sleep (1);
+ npagers = ports_count_bucket (pager_bucket);
+ if (!npagers)
+ return 0;
+
+ enable_caching ();
+
+ ports_enable_bucket (pager_bucket);
+}
+
+vm_prot_t
+netfs_max_user_pager_prot ()
+{
+ vm_prot_t max_prot;
+ int npagers = ports_count_bucket (pager_bucket);
+
+ if (npagers)
+ {
+ error_t add_pager_max_prot (void *v_p)
+ {
+ struct pager *p = v_p;
+ struct user_pager_info *upi = pager_get_upi (p);
+ max_prot |= upi->max_prot;
+ return max_prot == (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);
+ }
+
+ block_caching ();
+ sleep (1);
+
+ ports_bucket_iterate (pager_bucket, add_pager_max_prot);
+ enable_caching ();
+ }
+
+ ports_enable_bucket (pager_bucket);
+ return max_prot;
+}
+
+void
+netfs_shutdown_pager ()
+{
+ error_t shutdown_one (void *arg)
+ {
+ pager_shutdown ((struct pager *) arg);
+ return 0;
+ }
+
+ ports_bucket_iterate (pager_bucket, shutdown_one);
+}
+
+void
+netfs_sync_everything (int wait)
+{
+ error_t sync_one (void *arg)
+ {
+ pager_sync ((struct pager *) arg, wait);
+ return 0;
+ }
+ ports_bucket_iterate (pager_bucket, sync_one);
+}
+
+void
+pager_initialize (void)
+{
+ pager_bucket = ports_create_bucket ();
+ cthread_detach (cthread_fork (flush_pager_cache_thread, 0));
+