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;
+}

Reply via email to