summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/vm/vm_extern.h1
-rw-r--r--sys/vm/vm_fault.c3
-rw-r--r--sys/vm/vm_map.c42
-rw-r--r--sys/vm/vm_object.c72
-rw-r--r--sys/vm/vm_object.h2
5 files changed, 93 insertions, 27 deletions
diff --git a/sys/vm/vm_extern.h b/sys/vm/vm_extern.h
index 39a1a21..af567e6 100644
--- a/sys/vm/vm_extern.h
+++ b/sys/vm/vm_extern.h
@@ -81,7 +81,6 @@ int vm_fault_hold(vm_map_t map, vm_offset_t vaddr, vm_prot_t fault_type,
int fault_flags, vm_page_t *m_hold);
int vm_fault_quick_hold_pages(vm_map_t map, vm_offset_t addr, vm_size_t len,
vm_prot_t prot, vm_page_t *ma, int max_count);
-void vm_fault_unwire(vm_map_t, vm_offset_t, vm_offset_t, boolean_t);
int vm_fault_wire(vm_map_t, vm_offset_t, vm_offset_t, boolean_t);
int vm_forkproc(struct thread *, struct proc *, struct thread *, struct vmspace *, int);
void vm_waitproc(struct proc *);
diff --git a/sys/vm/vm_fault.c b/sys/vm/vm_fault.c
index ecaafdb..172e2f3 100644
--- a/sys/vm/vm_fault.c
+++ b/sys/vm/vm_fault.c
@@ -106,6 +106,7 @@ __FBSDID("$FreeBSD$");
#define PFFOR 4
static int vm_fault_additional_pages(vm_page_t, int, int, vm_page_t *, int *);
+static void vm_fault_unwire(vm_map_t, vm_offset_t, vm_offset_t, boolean_t);
#define VM_FAULT_READ_BEHIND 8
#define VM_FAULT_READ_MAX (1 + VM_FAULT_READ_AHEAD_MAX)
@@ -1186,7 +1187,7 @@ vm_fault_wire(vm_map_t map, vm_offset_t start, vm_offset_t end,
*
* Unwire a range of virtual addresses in a map.
*/
-void
+static void
vm_fault_unwire(vm_map_t map, vm_offset_t start, vm_offset_t end,
boolean_t fictitious)
{
diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c
index b5108e2..c9d8cfa 100644
--- a/sys/vm/vm_map.c
+++ b/sys/vm/vm_map.c
@@ -132,6 +132,7 @@ static void _vm_map_init(vm_map_t map, pmap_t pmap, vm_offset_t min,
vm_offset_t max);
static void vm_map_entry_deallocate(vm_map_entry_t entry, boolean_t system_map);
static void vm_map_entry_dispose(vm_map_t map, vm_map_entry_t entry);
+static void vm_map_entry_unwire(vm_map_t map, vm_map_entry_t entry);
#ifdef INVARIANTS
static void vm_map_zdtor(void *mem, int size, void *arg);
static void vmspace_zdtor(void *mem, int size, void *arg);
@@ -2393,16 +2394,10 @@ done:
(entry->eflags & MAP_ENTRY_USER_WIRED))) {
if (user_unwire)
entry->eflags &= ~MAP_ENTRY_USER_WIRED;
- entry->wired_count--;
- if (entry->wired_count == 0) {
- /*
- * Retain the map lock.
- */
- vm_fault_unwire(map, entry->start, entry->end,
- entry->object.vm_object != NULL &&
- (entry->object.vm_object->flags &
- OBJ_FICTITIOUS) != 0);
- }
+ if (entry->wired_count == 1)
+ vm_map_entry_unwire(map, entry);
+ else
+ entry->wired_count--;
}
KASSERT((entry->eflags & MAP_ENTRY_IN_TRANSITION) != 0,
("vm_map_unwire: in-transition flag missing %p", entry));
@@ -2635,19 +2630,12 @@ done:
* unnecessary.
*/
entry->wired_count = 0;
- } else {
- if (!user_wire ||
- (entry->eflags & MAP_ENTRY_USER_WIRED) == 0)
+ } else if (!user_wire ||
+ (entry->eflags & MAP_ENTRY_USER_WIRED) == 0) {
+ if (entry->wired_count == 1)
+ vm_map_entry_unwire(map, entry);
+ else
entry->wired_count--;
- if (entry->wired_count == 0) {
- /*
- * Retain the map lock.
- */
- vm_fault_unwire(map, entry->start, entry->end,
- entry->object.vm_object != NULL &&
- (entry->object.vm_object->flags &
- OBJ_FICTITIOUS) != 0);
- }
}
next_entry_done:
KASSERT((entry->eflags & MAP_ENTRY_IN_TRANSITION) != 0,
@@ -2783,9 +2771,13 @@ vm_map_sync(
static void
vm_map_entry_unwire(vm_map_t map, vm_map_entry_t entry)
{
- vm_fault_unwire(map, entry->start, entry->end,
- entry->object.vm_object != NULL &&
- (entry->object.vm_object->flags & OBJ_FICTITIOUS) != 0);
+
+ VM_MAP_ASSERT_LOCKED(map);
+ KASSERT(entry->wired_count > 0,
+ ("vm_map_entry_unwire: entry %p isn't wired", entry));
+ pmap_unwire(map->pmap, entry->start, entry->end);
+ vm_object_unwire(entry->object.vm_object, entry->offset, entry->end -
+ entry->start, PQ_ACTIVE);
entry->wired_count = 0;
}
diff --git a/sys/vm/vm_object.c b/sys/vm/vm_object.c
index 504ca77..c3af9d7 100644
--- a/sys/vm/vm_object.c
+++ b/sys/vm/vm_object.c
@@ -2202,6 +2202,78 @@ vm_object_set_writeable_dirty(vm_object_t object)
vm_object_set_flag(object, OBJ_MIGHTBEDIRTY);
}
+/*
+ * vm_object_unwire:
+ *
+ * For each page offset within the specified range of the given object,
+ * find the highest-level page in the shadow chain and unwire it. A page
+ * must exist at every page offset, and the highest-level page must be
+ * wired.
+ */
+void
+vm_object_unwire(vm_object_t object, vm_ooffset_t offset, vm_size_t length,
+ uint8_t queue)
+{
+ vm_object_t tobject;
+ vm_page_t m, tm;
+ vm_pindex_t end_pindex, pindex, tpindex;
+ int depth, locked_depth;
+
+ KASSERT((offset & PAGE_MASK) == 0,
+ ("vm_object_unwire: offset is not page aligned"));
+ KASSERT((length & PAGE_MASK) == 0,
+ ("vm_object_unwire: length is not a multiple of PAGE_SIZE"));
+ /* The wired count of a fictitious page never changes. */
+ if ((object->flags & OBJ_FICTITIOUS) != 0)
+ return;
+ pindex = OFF_TO_IDX(offset);
+ end_pindex = pindex + atop(length);
+ locked_depth = 1;
+ VM_OBJECT_RLOCK(object);
+ m = vm_page_find_least(object, pindex);
+ while (pindex < end_pindex) {
+ if (m == NULL || pindex < m->pindex) {
+ /*
+ * The first object in the shadow chain doesn't
+ * contain a page at the current index. Therefore,
+ * the page must exist in a backing object.
+ */
+ tobject = object;
+ tpindex = pindex;
+ depth = 0;
+ do {
+ tpindex +=
+ OFF_TO_IDX(tobject->backing_object_offset);
+ tobject = tobject->backing_object;
+ KASSERT(tobject != NULL,
+ ("vm_object_unwire: missing page"));
+ if ((tobject->flags & OBJ_FICTITIOUS) != 0)
+ goto next_page;
+ depth++;
+ if (depth == locked_depth) {
+ locked_depth++;
+ VM_OBJECT_RLOCK(tobject);
+ }
+ } while ((tm = vm_page_lookup(tobject, tpindex)) ==
+ NULL);
+ } else {
+ tm = m;
+ m = TAILQ_NEXT(m, listq);
+ }
+ vm_page_lock(tm);
+ vm_page_unwire(tm, queue);
+ vm_page_unlock(tm);
+next_page:
+ pindex++;
+ }
+ /* Release the accumulated object locks. */
+ for (depth = 0; depth < locked_depth; depth++) {
+ tobject = object->backing_object;
+ VM_OBJECT_RUNLOCK(object);
+ object = tobject;
+ }
+}
+
#include "opt_ddb.h"
#ifdef DDB
#include <sys/kernel.h>
diff --git a/sys/vm/vm_object.h b/sys/vm/vm_object.h
index 8381992..1f6c7c2 100644
--- a/sys/vm/vm_object.h
+++ b/sys/vm/vm_object.h
@@ -291,6 +291,8 @@ void vm_object_shadow (vm_object_t *, vm_ooffset_t *, vm_size_t);
void vm_object_split(vm_map_entry_t);
boolean_t vm_object_sync(vm_object_t, vm_ooffset_t, vm_size_t, boolean_t,
boolean_t);
+void vm_object_unwire(vm_object_t object, vm_ooffset_t offset,
+ vm_size_t length, uint8_t queue);
#endif /* _KERNEL */
#endif /* _VM_OBJECT_ */
OpenPOWER on IntegriCloud