aboutsummaryrefslogtreecommitdiff
path: root/sys/vm
diff options
context:
space:
mode:
authorAlan Cox <alc@FreeBSD.org>2007-09-27 04:21:59 +0000
committerAlan Cox <alc@FreeBSD.org>2007-09-27 04:21:59 +0000
commitc94449142630395f78e47691cac3491f9476cb6e (patch)
tree6b7cecdc04aa30481a7ed30edd9cc6bb042741b9 /sys/vm
parentfed3ad6334aa3eaa943ae147d69a50e58009a534 (diff)
downloadsrc-c94449142630395f78e47691cac3491f9476cb6e.tar.gz
src-c94449142630395f78e47691cac3491f9476cb6e.zip
Correct an error of omission in the reimplementation of the page
cache: vm_object_page_remove() should convert any cached pages that fall with the specified range to free pages. Otherwise, there could be a problem if a file is first truncated and then regrown. Specifically, some old data from prior to the truncation might reappear. Generalize vm_page_cache_free() to support the conversion of either a subset or the entirety of an object's cached pages. Reported by: tegge Reviewed by: tegge Approved by: re (kensmith)
Notes
Notes: svn path=/head/; revision=172341
Diffstat (limited to 'sys/vm')
-rw-r--r--sys/vm/vm_object.c6
-rw-r--r--sys/vm/vm_page.c58
-rw-r--r--sys/vm/vm_page.h2
3 files changed, 48 insertions, 18 deletions
diff --git a/sys/vm/vm_object.c b/sys/vm/vm_object.c
index bf380fa39105..c2275164db3d 100644
--- a/sys/vm/vm_object.c
+++ b/sys/vm/vm_object.c
@@ -653,7 +653,7 @@ vm_object_terminate(vm_object_t object)
vm_page_unlock_queues();
if (__predict_false(object->cache != NULL))
- vm_page_cache_free(object);
+ vm_page_cache_free(object, 0, 0);
/*
* Let the pager know object is dead.
@@ -1680,7 +1680,7 @@ vm_object_collapse(vm_object_t object)
* Free any cached pages from backing_object.
*/
if (__predict_false(backing_object->cache != NULL))
- vm_page_cache_free(backing_object);
+ vm_page_cache_free(backing_object, 0, 0);
}
/*
* Object now shadows whatever backing_object did.
@@ -1849,6 +1849,8 @@ again:
}
vm_page_unlock_queues();
vm_object_pip_wakeup(object);
+ if (__predict_false(object->cache != NULL))
+ vm_page_cache_free(object, start, end);
}
/*
diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c
index 36fee281a157..87c892d60c7a 100644
--- a/sys/vm/vm_page.c
+++ b/sys/vm/vm_page.c
@@ -794,28 +794,55 @@ vm_page_rename(vm_page_t m, vm_object_t new_object, vm_pindex_t new_pindex)
}
/*
- * Convert all of the cached pages belonging to the given object
- * into free pages. If the given object has cached pages and is
- * backed by a vnode, reduce the vnode's hold count.
+ * Convert all of the given object's cached pages that have a
+ * pindex within the given range into free pages. If the value
+ * zero is given for "end", then the range's upper bound is
+ * infinity. If the given object is backed by a vnode and it
+ * transitions from having one or more cached pages to none, the
+ * vnode's hold count is reduced.
*/
void
-vm_page_cache_free(vm_object_t object)
+vm_page_cache_free(vm_object_t object, vm_pindex_t start, vm_pindex_t end)
{
- vm_page_t m, root;
+ vm_page_t m, m_next;
boolean_t empty;
mtx_lock(&vm_page_queue_free_mtx);
- empty = object->cache == NULL;
- while ((m = object->cache) != NULL) {
- if (m->left == NULL)
- root = m->right;
- else if (m->right == NULL)
- root = m->left;
+ if (__predict_false(object->cache == NULL)) {
+ mtx_unlock(&vm_page_queue_free_mtx);
+ return;
+ }
+ m = object->cache = vm_page_splay(start, object->cache);
+ if (m->pindex < start) {
+ if (m->right == NULL)
+ m = NULL;
else {
- root = vm_page_splay(m->pindex, m->left);
- root->right = m->right;
+ m_next = vm_page_splay(start, m->right);
+ m_next->left = m;
+ m->right = NULL;
+ m = object->cache = m_next;
}
- m->object->cache = root;
+ }
+
+ /*
+ * At this point, "m" is either (1) a reference to the page
+ * with the least pindex that is greater than or equal to
+ * "start" or (2) NULL.
+ */
+ for (; m != NULL && (m->pindex < end || end == 0); m = m_next) {
+ /*
+ * Find "m"'s successor and remove "m" from the
+ * object's cache.
+ */
+ if (m->right == NULL) {
+ object->cache = m->left;
+ m_next = NULL;
+ } else {
+ m_next = vm_page_splay(start, m->right);
+ m_next->left = m->left;
+ object->cache = m_next;
+ }
+ /* Convert "m" to a free page. */
m->object = NULL;
m->valid = 0;
/* Clear PG_CACHED and set PG_FREE. */
@@ -825,8 +852,9 @@ vm_page_cache_free(vm_object_t object)
cnt.v_cache_count--;
cnt.v_free_count++;
}
+ empty = object->cache == NULL;
mtx_unlock(&vm_page_queue_free_mtx);
- if (object->type == OBJT_VNODE && !empty)
+ if (object->type == OBJT_VNODE && empty)
vdrop(object->handle);
}
diff --git a/sys/vm/vm_page.h b/sys/vm/vm_page.h
index 3ed2f75c7166..c13511db6297 100644
--- a/sys/vm/vm_page.h
+++ b/sys/vm/vm_page.h
@@ -320,7 +320,7 @@ void vm_page_activate (vm_page_t);
vm_page_t vm_page_alloc (vm_object_t, vm_pindex_t, int);
vm_page_t vm_page_grab (vm_object_t, vm_pindex_t, int);
void vm_page_cache (register vm_page_t);
-void vm_page_cache_free(vm_object_t);
+void vm_page_cache_free(vm_object_t, vm_pindex_t, vm_pindex_t);
void vm_page_cache_remove(vm_page_t);
void vm_page_cache_transfer(vm_object_t, vm_pindex_t, vm_object_t);
int vm_page_try_to_cache (vm_page_t);