From c94449142630395f78e47691cac3491f9476cb6e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 27 Sep 2007 04:21:59 +0000 Subject: 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) --- sys/vm/vm_object.c | 6 ++++-- sys/vm/vm_page.c | 58 ++++++++++++++++++++++++++++++++++++++++-------------- sys/vm/vm_page.h | 2 +- 3 files changed, 48 insertions(+), 18 deletions(-) (limited to 'sys/vm') 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); -- cgit v1.2.3