Module Name: src Committed By: yamt Date: Wed Nov 2 21:55:39 UTC 2011
Added Files: src/sys/uvm [yamt-pagecache]: uvm_page_array.c uvm_page_array.h uvm_page_status.c Log Message: page cache related changes - maintain object pages in radix tree rather than rb tree. - reduce unnecessary page scan in putpages. esp. when an object has a ton of pages cached but only a few of them are dirty. - reduce the number of pmap operations by tracking page dirtiness more precisely in uvm layer. - fix nfs commit range tracking. - fix nfs write clustering. XXX hack To generate a diff of this commit: cvs rdiff -u -r0 -r1.1.2.1 src/sys/uvm/uvm_page_array.c \ src/sys/uvm/uvm_page_array.h src/sys/uvm/uvm_page_status.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Added files: Index: src/sys/uvm/uvm_page_array.c diff -u /dev/null src/sys/uvm/uvm_page_array.c:1.1.2.1 --- /dev/null Wed Nov 2 21:55:39 2011 +++ src/sys/uvm/uvm_page_array.c Wed Nov 2 21:55:39 2011 @@ -0,0 +1,163 @@ +/* $NetBSD: uvm_page_array.c,v 1.1.2.1 2011/11/02 21:55:39 yamt Exp $ */ + +/*- + * Copyright (c)2011 YAMAMOTO Takashi, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: uvm_page_array.c,v 1.1.2.1 2011/11/02 21:55:39 yamt Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> + +#include <uvm/uvm_extern.h> +#include <uvm/uvm_object.h> +#include <uvm/uvm_page.h> +#include <uvm/uvm_page_array.h> + +/* + * uvm_page_array_init: initialize the array. + */ + +void +uvm_page_array_init(struct uvm_page_array *ar) +{ + + ar->ar_idx = ar->ar_npages = 0; +} + +/* + * uvm_page_array_fini: clean up the array. + */ + +void +uvm_page_array_fini(struct uvm_page_array *ar) +{ + + /* + * currently nothing to do. + */ +#if defined(DIAGNOSTIC) + /* + * poison to trigger assertion in uvm_page_array_peek to + * detect usage errors. + */ + ar->ar_npages = 1; + ar->ar_idx = 1000; +#endif /* defined(DIAGNOSTIC) */ +} + +/* + * uvm_page_array_clear: forget the cached pages and initialize the array. + */ + +void +uvm_page_array_clear(struct uvm_page_array *ar) +{ + + KASSERT(ar->ar_idx <= ar->ar_npages); + uvm_page_array_init(ar); +} + +/* + * uvm_page_array_peek: return the next cached page. + */ + +struct vm_page * +uvm_page_array_peek(struct uvm_page_array *ar) +{ + + KASSERT(ar->ar_idx <= ar->ar_npages); + if (ar->ar_idx == ar->ar_npages) { + return NULL; + } + return ar->ar_pages[ar->ar_idx]; +} + +/* + * uvm_page_array_advance: advance the array to the next cached page + */ + +void +uvm_page_array_advance(struct uvm_page_array *ar) +{ + + KASSERT(ar->ar_idx <= ar->ar_npages); + ar->ar_idx++; + KASSERT(ar->ar_idx <= ar->ar_npages); +} + +/* + * uvm_page_array_fill: lookup pages and keep them cached. + * + * return 0 on success. in that case, cache the result in the array + * so that they will be picked by later uvm_page_array_peek. + * + * return ENOENT if no pages are found. + * + * called with object lock held. + */ + +int +uvm_page_array_fill(struct uvm_page_array *ar, struct uvm_object *uobj, + voff_t off, bool dirtyonly) +{ + unsigned int npages; +#if defined(DEBUG) + unsigned int i; +#endif /* defined(DEBUG) */ + const unsigned int maxpages = __arraycount(ar->ar_pages); + + KASSERT(mutex_owned(uobj->vmobjlock)); + KASSERT(uvm_page_array_peek(ar) == NULL); + if (dirtyonly) { + npages = radix_tree_gang_lookup_tagged_node( + &uobj->uo_pages, off >> PAGE_SHIFT, + (void **)ar->ar_pages, maxpages, + UVM_PAGE_DIRTY_TAG); + } else { + npages = radix_tree_gang_lookup_node( + &uobj->uo_pages, off >> PAGE_SHIFT, + (void **)ar->ar_pages, maxpages); + } + if (npages == 0) { + uvm_page_array_clear(ar); + return ENOENT; + } + KASSERT(npages <= maxpages); + ar->ar_npages = npages; + ar->ar_idx = 0; +#if defined(DEBUG) + for (i = 0; i < ar->ar_npages; i++) { + struct vm_page * const pg = ar->ar_pages[i]; + + KASSERT(pg != NULL); + KASSERT(pg->uobject == uobj); + KASSERT(pg->offset >= off); + KASSERT(i == 0 || pg->offset > ar->ar_pages[i - 1]->offset); + } +#endif /* defined(DEBUG) */ + return 0; +} Index: src/sys/uvm/uvm_page_array.h diff -u /dev/null src/sys/uvm/uvm_page_array.h:1.1.2.1 --- /dev/null Wed Nov 2 21:55:39 2011 +++ src/sys/uvm/uvm_page_array.h Wed Nov 2 21:55:39 2011 @@ -0,0 +1,68 @@ +/* $NetBSD: uvm_page_array.h,v 1.1.2.1 2011/11/02 21:55:39 yamt Exp $ */ + +/*- + * Copyright (c)2011 YAMAMOTO Takashi, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(_UVM_UVM_PAGE_ARRAY_H_) +#define _UVM_UVM_PAGE_ARRAY_H_ + +/* + * uvm_page_array: an array of pages. + * + * these structure and functions simply manipulate struct vm_page pointers. + * it's caller's responsibity to acquire and keep the object lock so that + * the result is valid. + * + * typical usage: + * + * struct uvm_page_array ar; + * + * uvm_page_array_init(&ar); + * for (uvm_page_array_fill(&ar, ...), pg = uvm_page_array_peek(&ar); + * pg != NULL; + * uvm_page_array_advance(&ar), pg = uvm_page_array_peek(&ar)) { + * do_something(pg); + * } + * uvm_page_array_fini(&it); + */ + +struct vm_page; + +struct uvm_page_array { + struct vm_page *ar_pages[16]; /* XXX tune */ + unsigned int ar_npages; /* valid elements in ar_pages */ + unsigned int ar_idx; /* index in ar_pages */ +}; + +void uvm_page_array_init(struct uvm_page_array *); +void uvm_page_array_fini(struct uvm_page_array *); +void uvm_page_array_clear(struct uvm_page_array *); +struct vm_page *uvm_page_array_peek(struct uvm_page_array *); +void uvm_page_array_advance(struct uvm_page_array *); +int uvm_page_array_fill(struct uvm_page_array *, struct uvm_object *, + voff_t, bool); + +#endif /* defined(_UVM_UVM_ARRAY_H_) */ Index: src/sys/uvm/uvm_page_status.c diff -u /dev/null src/sys/uvm/uvm_page_status.c:1.1.2.1 --- /dev/null Wed Nov 2 21:55:39 2011 +++ src/sys/uvm/uvm_page_status.c Wed Nov 2 21:55:39 2011 @@ -0,0 +1,155 @@ +/* $NetBSD: uvm_page_status.c,v 1.1.2.1 2011/11/02 21:55:39 yamt Exp $ */ + +/*- + * Copyright (c)2011 YAMAMOTO Takashi, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: uvm_page_status.c,v 1.1.2.1 2011/11/02 21:55:39 yamt Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> + +#include <uvm/uvm.h> + +/* + * page dirtiness status tracking + * + * separated from uvm_page.c mainly for rump + */ + +/* + * uvm_pagegetdirty: return the dirtiness status (one of UVM_PAGE_STATUS_ + * values) of the page. + */ + +unsigned int +uvm_pagegetdirty(struct vm_page *pg) +{ + struct uvm_object * const uobj __unused = pg->uobject; + const uint64_t idx __unused = pg->offset >> PAGE_SHIFT; + + KASSERT((~pg->flags & (PG_CLEAN|PG_DIRTY)) != 0); + KASSERT(uvm_page_locked_p(pg)); + KASSERT(uobj == NULL || ((pg->flags & PG_CLEAN) == 0) == + radix_tree_get_tag(&uobj->uo_pages, idx, UVM_PAGE_DIRTY_TAG)); + return pg->flags & (PG_CLEAN|PG_DIRTY); +} + +/* + * uvm_pagemarkdirty: set the dirtiness status (one of UVM_PAGE_STATUS_ values) + * of the page. + */ + +void +uvm_pagemarkdirty(struct vm_page *pg, unsigned int status) +{ + struct uvm_object * const uobj = pg->uobject; + const uint64_t idx = pg->offset >> PAGE_SHIFT; + + KASSERT((~status & (PG_CLEAN|PG_DIRTY)) != 0); + KASSERT((status & ~(PG_CLEAN|PG_DIRTY)) == 0); + KASSERT(uvm_page_locked_p(pg)); + KASSERT(uobj == NULL || ((pg->flags & PG_CLEAN) == 0) == + radix_tree_get_tag(&uobj->uo_pages, idx, UVM_PAGE_DIRTY_TAG)); + + if (uvm_pagegetdirty(pg) == status) { + return; + } + /* + * set UVM_PAGE_DIRTY_TAG tag unless known CLEAN so that putpages can + * find possibly-dirty pages quickly. + */ + if (uobj != NULL) { + if (status == UVM_PAGE_STATUS_CLEAN) { + radix_tree_clear_tag(&uobj->uo_pages, idx, + UVM_PAGE_DIRTY_TAG); + } else { + radix_tree_set_tag(&uobj->uo_pages, idx, + UVM_PAGE_DIRTY_TAG); + } + } + if (status == UVM_PAGE_STATUS_UNKNOWN) { + /* + * start relying on pmap-level dirtiness tracking. + */ + pmap_clear_modify(pg); + } + pg->flags &= ~(PG_CLEAN|PG_DIRTY); + pg->flags |= status; + KASSERT(uobj == NULL || ((pg->flags & PG_CLEAN) == 0) == + radix_tree_get_tag(&uobj->uo_pages, idx, UVM_PAGE_DIRTY_TAG)); +} + +/* + * uvm_pagecheckdirty: check if page is dirty, and remove its dirty-marks. + * + * called with the owner locked. + */ + +bool +uvm_pagecheckdirty(struct vm_page *pg, bool protected) +{ + const unsigned int oldstatus = uvm_pagegetdirty(pg); + bool modified; + + KASSERT(uvm_page_locked_p(pg)); + + /* + * if protected is true, mark the page CLEAN. + * otherwise mark the page UNKNOWN unless it's CLEAN. + * + * possible transitions: + * + * CLEAN -> CLEAN , modified = false + * UNKNOWN -> UNKNOWN, modified = true + * UNKNOWN -> UNKNOWN, modified = false + * UNKNOWN -> CLEAN , modified = true + * UNKNOWN -> CLEAN , modified = false + * DIRTY -> UNKNOWN, modified = true + * DIRTY -> CLEAN , modified = true + * + * pmap_clear_modify is necessary if either of + * oldstatus or newstatus is UVM_PAGE_STATUS_UNKNOWN. + */ + + if (oldstatus == UVM_PAGE_STATUS_CLEAN) { + modified = false; + } else { + const unsigned int newstatus = + protected ? UVM_PAGE_STATUS_CLEAN : UVM_PAGE_STATUS_UNKNOWN; + + if (oldstatus == UVM_PAGE_STATUS_DIRTY) { + modified = true; + if (newstatus == UVM_PAGE_STATUS_UNKNOWN) { + pmap_clear_modify(pg); + } + } else { + modified = pmap_clear_modify(pg); + } + uvm_pagemarkdirty(pg, newstatus); + } + return modified; +}