vm_fault: Fix vm_fault_populate()'s handling of VM_FAULT_WIRE

vm_map_wire() works by calling vm_fault(VM_FAULT_WIRE) on each page in
the rage.  (For largepage mappings, it calls vm_fault() once per large
page.)

A pager's populate method may return more than one page to be mapped.
If VM_FAULT_WIRE is also specified, we'd wire each page in the run, not
just the fault page.  Consider an object with two pages mapped in a
vm_map_entry, and suppose vm_map_wire() is called on the entry.  Then,
the first vm_fault() would allocate and wire both pages, and the second
would encounter a valid page upon lookup and wire it again in the
regular fault handler.  So the second page is wired twice and will be
leaked when the object is destroyed.

Fix the problem by modify vm_fault_populate() to wire only the fault
page.  Also modify the error handler for pmap_enter(psind=1) to not test
fs->wired, since it must be false.

PR:		260347
Reviewed by:	alc, kib
MFC after:	2 weeks
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D33416
This commit is contained in:
Mark Johnston 2021-12-14 15:10:46 -05:00
parent 509f1a0f40
commit 88642d978a

View File

@ -597,21 +597,23 @@ vm_fault_populate(struct faultstate *fs)
(psind > 0 && rv == KERN_PROTECTION_FAILURE));
if (__predict_false(psind > 0 &&
rv == KERN_PROTECTION_FAILURE)) {
MPASS(!fs->wired);
for (i = 0; i < npages; i++) {
rv = pmap_enter(fs->map->pmap, vaddr + ptoa(i),
&m[i], fs->prot, fs->fault_type |
(fs->wired ? PMAP_ENTER_WIRED : 0), 0);
&m[i], fs->prot, fs->fault_type, 0);
MPASS(rv == KERN_SUCCESS);
}
}
VM_OBJECT_WLOCK(fs->first_object);
for (i = 0; i < npages; i++) {
if ((fs->fault_flags & VM_FAULT_WIRE) != 0)
if ((fs->fault_flags & VM_FAULT_WIRE) != 0 &&
m[i].pindex == fs->first_pindex)
vm_page_wire(&m[i]);
else
vm_page_activate(&m[i]);
if (fs->m_hold != NULL && m[i].pindex == fs->first_pindex) {
if (fs->m_hold != NULL &&
m[i].pindex == fs->first_pindex) {
(*fs->m_hold) = &m[i];
vm_page_wire(&m[i]);
}