Module Name:    src
Committed By:   uebayasi
Date:           Wed Feb 10 02:12:40 UTC 2010

Modified Files:
        src/sys/uvm [uebayasi-xip]: uvm_page.c uvm_page.h

Log Message:
Initial MD per-page data (struct vm_page_md) lookup code for XIP'able device
pages.  Compile tested only.

Always define uvm_pageisdevice_p().  Always false if kernel is !DEVICE_PAGE.


To generate a diff of this commit:
cvs rdiff -u -r1.153.2.9 -r1.153.2.10 src/sys/uvm/uvm_page.c
cvs rdiff -u -r1.59.2.6 -r1.59.2.7 src/sys/uvm/uvm_page.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/uvm/uvm_page.c
diff -u src/sys/uvm/uvm_page.c:1.153.2.9 src/sys/uvm/uvm_page.c:1.153.2.10
--- src/sys/uvm/uvm_page.c:1.153.2.9	Tue Feb  9 14:12:00 2010
+++ src/sys/uvm/uvm_page.c	Wed Feb 10 02:12:39 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_page.c,v 1.153.2.9 2010/02/09 14:12:00 uebayasi Exp $	*/
+/*	$NetBSD: uvm_page.c,v 1.153.2.10 2010/02/10 02:12:39 uebayasi Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -71,11 +71,12 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_page.c,v 1.153.2.9 2010/02/09 14:12:00 uebayasi Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_page.c,v 1.153.2.10 2010/02/10 02:12:39 uebayasi Exp $");
 
 #include "opt_ddb.h"
 #include "opt_uvmhist.h"
 #include "opt_readahead.h"
+#include "opt_device_page.h"
 #include "opt_xip.h"
 
 #include <sys/param.h>
@@ -103,7 +104,7 @@
 
 struct vm_physseg vm_physmem[VM_PHYSSEG_MAX];	/* XXXCDC: uvm.physmem */
 int vm_nphysmem = 0;				/* XXXCDC: uvm.nphysmem */
-#ifdef XIP
+#ifdef DEVICE_PAGE
 struct vm_physseg vm_physdev[VM_PHYSSEG_MAX];	/* XXXCDC: uvm.physdev */
 int vm_nphysdev = 0;				/* XXXCDC: uvm.nphysdev */
 #endif
@@ -901,7 +902,7 @@
 	    pframe, NULL, offp);
 }
 
-#ifdef XIP
+#ifdef DEVICE_PAGE
 int
 vm_physseg_find_device(paddr_t pframe, int *offp)
 {
@@ -1029,12 +1030,22 @@
 }
 
 
+#ifdef DEVICE_PAGE
 /*
- * PHYS_TO_VM_PAGE: find vm_page for a PA.   used by MI code to get vm_pages
- * back from an I/O mapping (ugh!).   used in some MD code as well.
+ * Device pages don't have struct vm_page objects for various reasons:
+ *
+ * - Device pages are volatile; not paging involved.  Which means we have
+ *   much less state information to keep for each page.
+ *
+ * - Volatile, directly memory-mappable devices (framebuffers, audio devices,
+ *   etc.) only need physical address and attribute (protection and some
+ *   accelaration specific to physical bus) common to all the pages.
+ *   Allocating vm_page objects to keep such informations is wasteful.
+ *
+ * - Per-page MD information is only used for XIP vnodes' copy-on-write from
+ *   a device page to anon.
  */
 
-#ifdef XIP
 /* Assume struct vm_page * is aligned to 4 bytes. */
 #define	VM_PAGE_DEVICE_MAGIC		0x2
 #define	VM_PAGE_DEVICE_MAGIC_MASK	0x3
@@ -1064,6 +1075,12 @@
 }
 #endif
 
+
+/*
+ * PHYS_TO_VM_PAGE: find vm_page for a PA.   used by MI code to get vm_pages
+ * back from an I/O mapping (ugh!).   used in some MD code as well.
+ */
+
 struct vm_page *
 uvm_phys_to_vm_page(paddr_t pa)
 {
@@ -1071,7 +1088,7 @@
 	int	off;
 	int	psi;
 
-#ifdef XIP
+#ifdef DEVICE_PAGE
 	psi = vm_physseg_find_device(pf, &off);
 	if (psi != -1)
 		return(PHYS_TO_VM_PAGE_DEVICE(pa));
@@ -1088,7 +1105,7 @@
 	const struct vm_physseg *seg;
 	int psi;
 
-#ifdef XIP
+#ifdef DEVICE_PAGE
 	if (uvm_pageisdevice_p(pg)) {
 		return VM_PAGE_DEVICE_TO_PHYS(pg);
 	}
@@ -1099,6 +1116,132 @@
 	return (seg->start + pg - seg->pgs) * PAGE_SIZE;
 }
 
+
+#ifdef XIP
+/*
+ * Device page's mdpage lookup.
+ *
+ * - Hashing code is based on sys/arch/x86/x86/pmap.c.
+ *
+ * - 
+ * XXX Consider to allocate slots on-demand.
+ */
+
+void vm_page_device_mdpage_insert(struct vm_page *);
+void vm_page_device_mdpage_remove(struct vm_page *);
+struct vm_page_md *vm_page_device_mdpage_lookup(struct vm_page *);
+
+struct vm_page_device_mdpage_entry {
+	struct vm_page_md mde_mdpage;
+	SLIST_ENTRY(vm_page_device_mdpage_entry) mde_hash;
+	paddr_t mde_pf;
+};
+
+/*
+ * These can be optimized depending on the size of XIP'ed executables' .data
+ * segments.  If page size is 4K and .data is 1M, .data spans across 256
+ * pages.  Considering these pages' physical addresses are continuous, linear
+ * hash should suffice.
+ */
+#define	MDPG_HASH_SIZE		256	/* XXX */
+#define	MDPG_HASH_LOCK_CNT	4	/* XXX */
+
+struct vm_page_device_mdpage {
+	kmutex_t locks[MDPG_HASH_LOCK_CNT];
+	struct vm_page_device_mdpage_head {
+		SLIST_HEAD(, vm_page_device_mdpage_entry) list;
+	} heads[MDPG_HASH_SIZE];
+};
+
+/* Global for now.  Consider to make this per-vm_physseg. */
+struct vm_page_device_mdpage vm_page_device_mdpage;
+
+static u_int
+vm_page_device_mdpage_hash(struct vm_page *pg)
+{
+
+	return VM_PAGE_DEVICE_TO_PHYS(pg);
+}
+
+static struct vm_page_device_mdpage_head *
+vm_page_device_mdpage_head(u_int hash)
+{
+
+	return &vm_page_device_mdpage.heads[hash % MDPG_HASH_SIZE];
+}
+
+static kmutex_t *
+vm_page_device_mdpage_lock(u_int hash)
+{
+
+	return &vm_page_device_mdpage.locks[hash % MDPG_HASH_LOCK_CNT];
+}
+
+void
+vm_page_device_mdpage_insert(struct vm_page *pg)
+{
+	paddr_t pf = VM_PAGE_DEVICE_TO_PHYS(pg);
+	u_int hash = vm_page_device_mdpage_hash(pg);
+	kmutex_t *lock = vm_page_device_mdpage_lock(hash);
+	struct vm_page_device_mdpage_head *head = vm_page_device_mdpage_head(hash);
+
+	struct vm_page_device_mdpage_entry *mde = kmem_zalloc(sizeof(*mde), KM_SLEEP);
+	mde->mde_pf = pf;
+
+	mutex_spin_enter(lock);
+	SLIST_INSERT_HEAD(&head->list, mde, mde_hash);
+	mutex_spin_exit(lock);
+}
+
+void
+vm_page_device_mdpage_remove(struct vm_page *pg)
+{
+	paddr_t pf = VM_PAGE_DEVICE_TO_PHYS(pg);
+	u_int hash = vm_page_device_mdpage_hash(pg);
+	kmutex_t *lock = vm_page_device_mdpage_lock(hash);
+	struct vm_page_device_mdpage_head *head = vm_page_device_mdpage_head(hash);
+
+	struct vm_page_device_mdpage_entry *mde;
+	struct vm_page_device_mdpage_entry *prev = NULL;
+
+	mutex_spin_enter(lock);
+	SLIST_FOREACH(mde, &head->list, mde_hash) {
+		if (mde->mde_pf == pf) {
+			if (prev != NULL) {
+				SLIST_REMOVE_AFTER(prev, mde_hash);
+			} else {
+				SLIST_REMOVE_HEAD(&head->list, mde_hash);
+			}
+			break;
+		}
+		prev = mde;
+	}
+	mutex_spin_exit(lock);
+	KASSERT(mde != NULL);
+	kmem_free(mde, sizeof(*mde));
+}
+
+struct vm_page_md *
+vm_page_device_mdpage_lookup(struct vm_page *pg)
+{
+	paddr_t pf = VM_PAGE_DEVICE_TO_PHYS(pg);
+	u_int hash = vm_page_device_mdpage_hash(pg);
+	kmutex_t *lock = vm_page_device_mdpage_lock(hash);
+	struct vm_page_device_mdpage_head *head = vm_page_device_mdpage_head(hash);
+
+	struct vm_page_device_mdpage_entry *mde;
+
+	mutex_spin_enter(lock);
+	SLIST_FOREACH(mde, &head->list, mde_hash)
+		if (mde->mde_pf == pf)
+			break;
+	mutex_spin_exit(lock);
+	KASSERT(mde != NULL);
+	return &mde->mde_mdpage;
+}
+#endif
+
+
 /*
  * uvm_page_recolor: Recolor the pages if the new bucket count is
  * larger than the old one.
@@ -2094,7 +2237,7 @@
 {
 
 	return
-#ifdef XIP
+#ifdef DEVICE_PAGE
 	    (vm_physseg_find_device(atop(pa), NULL) != -1) ||
 #endif
 	    (vm_physseg_find(atop(pa), NULL) != -1);

Index: src/sys/uvm/uvm_page.h
diff -u src/sys/uvm/uvm_page.h:1.59.2.6 src/sys/uvm/uvm_page.h:1.59.2.7
--- src/sys/uvm/uvm_page.h:1.59.2.6	Tue Feb  9 13:06:17 2010
+++ src/sys/uvm/uvm_page.h	Wed Feb 10 02:12:39 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_page.h,v 1.59.2.6 2010/02/09 13:06:17 uebayasi Exp $	*/
+/*	$NetBSD: uvm_page.h,v 1.59.2.7 2010/02/10 02:12:39 uebayasi Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -291,8 +291,10 @@
 void uvm_pagewire(struct vm_page *);
 void uvm_pagezero(struct vm_page *);
 bool uvm_pageismanaged(paddr_t);
-#ifdef XIP
+#ifdef DEVICE_PAGE
 bool uvm_pageisdevice_p(const struct vm_page *);
+#else
+#define	uvm_pageisdevice_p	false
 #endif
 
 int uvm_page_lookup_freelist(struct vm_page *);

Reply via email to