--- sys/vm/device_pager.c.orig +++ sys/vm/device_pager.c @@ -213,7 +213,8 @@ object1 = NULL; object->handle = handle; object->un_pager.devp.ops = ops; - TAILQ_INIT(&object->un_pager.devp.devp_pglist); + if (object->type == OBJT_DEVICE) + vm_object_set_flag(object, OBJ_PG_DTOR); TAILQ_INSERT_TAIL(&dev_pager_object_list, object, pager_object_list); mtx_unlock(&dev_pager_mtx); @@ -325,15 +326,12 @@ KASSERT((object->type == OBJT_DEVICE && (m->oflags & VPO_UNMANAGED) != 0), ("Managed device or page obj %p m %p", object, m)); - TAILQ_REMOVE(&object->un_pager.devp.devp_pglist, m, plinks.q); vm_page_putfake(m); } static void dev_pager_dealloc(vm_object_t object) { - vm_page_t m; - VM_OBJECT_WUNLOCK(object); object->un_pager.devp.ops->cdev_pg_dtor(object->un_pager.devp.handle); @@ -343,15 +341,25 @@ VM_OBJECT_WLOCK(object); if (object->type == OBJT_DEVICE) { - /* - * Free up our fake pages. - */ - while ((m = TAILQ_FIRST(&object->un_pager.devp.devp_pglist)) - != NULL) { - if (vm_page_busy_acquire(m, VM_ALLOC_WAITFAIL) == 0) - continue; + struct pctrie_iter pages; + vm_page_t m; - dev_pager_free_page(object, m); + vm_page_iter_init(&pages, object); +restart: + VM_RADIX_FOREACH(m, &pages) { + if (!vm_page_busy_acquire(m, VM_ALLOC_WAITFAIL)) { + pctrie_iter_reset(&pages); + goto restart; + } + if (vm_page_iter_remove(&pages, m)) { + /* + * We could end up with invalid pages installed + * by the generic page fault handler. Typically + * these are replaced by the device pager. + */ + vm_page_free(m); + } else if ((m->flags & PG_FICTITIOUS) != 0) + dev_pager_free_page(object, m); } } object->handle = NULL; @@ -380,10 +388,6 @@ (object->type == OBJT_MGTDEVICE && (ma[0]->oflags & VPO_UNMANAGED) == 0), ("Wrong page type %p %p", ma[0], object)); - if (object->type == OBJT_DEVICE) { - TAILQ_INSERT_TAIL(&object->un_pager.devp.devp_pglist, - ma[0], plinks.q); - } if (rbehind) *rbehind = 0; if (rahead) --- sys/vm/vm_object.h.orig +++ sys/vm/vm_object.h @@ -134,7 +134,7 @@ * devp_pglist - list of allocated pages */ struct { - TAILQ_HEAD(, vm_page) devp_pglist; + void *spare[2]; const struct cdev_pager_ops *ops; void *handle; } devp; --- sys/vm/vm_page.c.orig +++ sys/vm/vm_page.c @@ -1363,8 +1363,10 @@ KASSERT((m->oflags & VPO_UNMANAGED) != 0, ("managed %p", m)); KASSERT((m->flags & PG_FICTITIOUS) != 0, ("vm_page_putfake: bad page %p", m)); - vm_page_assert_xbusied(m); - vm_page_busy_free(m); + if (m->object != NULL) { + vm_page_assert_xbusied(m); + vm_page_busy_free(m); + } uma_zfree(fakepg_zone, m); } --- tests/sys/vm/mmap_test.c.orig +++ tests/sys/vm/mmap_test.c @@ -362,6 +362,35 @@ ATF_REQUIRE(close(fd) == 0); } +/* A regression test for a bug in the device pager. */ +ATF_TC_WITHOUT_HEAD(mmap__device_reinsert); +ATF_TC_BODY(mmap__device_reinsert, tc) +{ + void *p; + int fd; + + fd = open("/dev/devstat", O_RDONLY); + ATF_REQUIRE(fd >= 0); + + p = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, 0); + ATF_REQUIRE(p != MAP_FAILED); + + /* + * Use mlock() to trigger a fault, since msync() will not actually + * remove the page from the process page tables. + */ + ATF_REQUIRE(mlock(p, getpagesize()) == 0); + ATF_REQUIRE(munlock(p, getpagesize()) == 0); + ATF_REQUIRE(msync(p, getpagesize(), MS_INVALIDATE) == 0); + ATF_REQUIRE(mlock(p, getpagesize()) == 0); + ATF_REQUIRE(munlock(p, getpagesize()) == 0); + ATF_REQUIRE(msync(p, getpagesize(), MS_INVALIDATE) == 0); + ATF_REQUIRE(mlock(p, getpagesize()) == 0); + ATF_REQUIRE(munlock(p, getpagesize()) == 0); + + ATF_REQUIRE(munmap(p, getpagesize()) == 0); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, mmap__map_at_zero); @@ -371,6 +400,7 @@ ATF_TP_ADD_TC(tp, mmap__write_only); ATF_TP_ADD_TC(tp, mmap__maxprot_basic); ATF_TP_ADD_TC(tp, mmap__maxprot_shm); + ATF_TP_ADD_TC(tp, mmap__device_reinsert); return (atf_no_error()); }