/* 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 #include #include #include 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));