summaryrefslogtreecommitdiffstats
path: root/sys/vm
diff options
context:
space:
mode:
authordillon <dillon@FreeBSD.org>2001-10-12 18:17:34 +0000
committerdillon <dillon@FreeBSD.org>2001-10-12 18:17:34 +0000
commit8a2a967bbcca05b13f23d48ad418b90d3f181874 (patch)
treec5b4de4e527c4fd8807da4310826f2f59cf9f41a /sys/vm
parent7ed22e2aa0bd38f7569dabe49b488e59c093ad12 (diff)
downloadFreeBSD-src-8a2a967bbcca05b13f23d48ad418b90d3f181874.zip
FreeBSD-src-8a2a967bbcca05b13f23d48ad418b90d3f181874.tar.gz
Finally fix the VM bug where a file whos EOF occurs in the middle of a page
would sometimes prevent a dirty page from being cleaned, even when synced, resulting in the dirty page being re-flushed to disk every 30-60 seconds or so, forever. The problem is that when the filesystem flushes a page to its backing file it typically does not clear dirty bits representing areas of the page that are beyond the file EOF. If the file is also mmap()'d and a fault is taken, vm_fault (properly, is required to) set the vm_page_t->dirty bits to VM_PAGE_BITS_ALL. This combination could leave us with an uncleanable, unfreeable page. The solution is to have the vnode_pager detect the edge case and manually clear the dirty bits representing areas beyond the file EOF. The filesystem does the rest and the page comes up clean after the write completes. MFC after: 3 days
Diffstat (limited to 'sys/vm')
-rw-r--r--sys/vm/vnode_pager.c24
1 files changed, 21 insertions, 3 deletions
diff --git a/sys/vm/vnode_pager.c b/sys/vm/vnode_pager.c
index 97e7039..412bfab 100644
--- a/sys/vm/vnode_pager.c
+++ b/sys/vm/vnode_pager.c
@@ -951,12 +951,30 @@ vnode_pager_generic_putpages(vp, m, bytecount, flags, rtvals)
ncount = count;
poffset = IDX_TO_OFF(m[0]->pindex);
+
+ /*
+ * If the page-aligned write is larger then the actual file we
+ * have to invalidate pages occuring beyond the file EOF. However,
+ * there is an edge case where a file may not be page-aligned where
+ * the last page is partially invalid. In this case the filesystem
+ * may not properly clear the dirty bits for the entire page (which
+ * could be VM_PAGE_BITS_ALL due to the page having been mmap()d).
+ * With the page locked we are free to fix-up the dirty bits here.
+ */
if (maxsize + poffset > object->un_pager.vnp.vnp_size) {
- if (object->un_pager.vnp.vnp_size > poffset)
+ if (object->un_pager.vnp.vnp_size > poffset) {
+ int pgoff;
+
maxsize = object->un_pager.vnp.vnp_size - poffset;
- else
+ ncount = btoc(maxsize);
+ if ((pgoff = (int)maxsize & PAGE_MASK) != 0) {
+ vm_page_clear_dirty(m[ncount - 1], pgoff,
+ PAGE_SIZE - pgoff);
+ }
+ } else {
maxsize = 0;
- ncount = btoc(maxsize);
+ ncount = 0;
+ }
if (ncount < count) {
for (i = ncount; i < count; i++) {
rtvals[i] = VM_PAGER_BAD;
OpenPOWER on IntegriCloud