Index: sys/dev/md/md.c =================================================================== --- sys/dev/md/md.c +++ sys/dev/md/md.c @@ -1121,10 +1121,7 @@ } vm_page_valid(m); - if (m->dirty != VM_PAGE_BITS_ALL) { - vm_page_dirty(m); - vm_pager_page_unswapped(m); - } + vm_page_set_dirty(m, true); } else if (bp->bio_cmd == BIO_DELETE) { if (len == PAGE_SIZE || vm_page_all_valid(m)) rv = VM_PAGER_OK; @@ -1142,10 +1139,7 @@ /* Page is valid. */ if (len != PAGE_SIZE) { pmap_zero_page_area(m, offs, len); - if (m->dirty != VM_PAGE_BITS_ALL) { - vm_page_dirty(m); - vm_pager_page_unswapped(m); - } + vm_page_set_dirty(m, true); } else { vm_pager_page_unswapped(m); vm_page_free(m); Index: sys/fs/tmpfs/tmpfs_subr.c =================================================================== --- sys/fs/tmpfs/tmpfs_subr.c +++ sys/fs/tmpfs/tmpfs_subr.c @@ -1441,9 +1441,8 @@ } if (m != NULL) { pmap_zero_page_area(m, base, PAGE_SIZE - base); - vm_page_dirty(m); + vm_page_set_dirty(m, true); vm_page_xunbusy(m); - vm_pager_page_unswapped(m); } } Index: sys/kern/uipc_shm.c =================================================================== --- sys/kern/uipc_shm.c +++ sys/kern/uipc_shm.c @@ -198,7 +198,7 @@ * type object. */ rv = vm_page_grab_valid(&m, obj, idx, - VM_ALLOC_NORMAL | VM_ALLOC_WIRED | VM_ALLOC_NOBUSY); + VM_ALLOC_NORMAL | VM_ALLOC_SBUSY | VM_ALLOC_IGN_SBUSY); if (rv != VM_PAGER_OK) { VM_OBJECT_WUNLOCK(obj); printf("uiomove_object: vm_obj %p idx %jd pager error %d\n", @@ -207,13 +207,10 @@ } VM_OBJECT_WUNLOCK(obj); error = uiomove_fromphys(&m, offset, tlen, uio); - if (uio->uio_rw == UIO_WRITE && error == 0) { - VM_OBJECT_WLOCK(obj); - vm_page_dirty(m); - vm_pager_page_unswapped(m); - VM_OBJECT_WUNLOCK(obj); - } - vm_page_unwire(m, PQ_ACTIVE); + if (uio->uio_rw == UIO_WRITE && error == 0) + vm_page_set_dirty(m, false); + vm_page_aflag_set(m, PGA_REFERENCED); + vm_page_sunbusy(m); return (error); } @@ -529,9 +526,8 @@ pmap_zero_page_area(m, base, PAGE_SIZE - base); KASSERT(vm_page_all_valid(m), ("shm_dotruncate: page %p is invalid", m)); - vm_page_dirty(m); + vm_page_set_dirty(m, true); vm_page_xunbusy(m); - vm_pager_page_unswapped(m); } } delta = IDX_TO_OFF(object->size - nobjsize); Index: sys/vm/vm_fault.c =================================================================== --- sys/vm/vm_fault.c +++ sys/vm/vm_fault.c @@ -289,11 +289,9 @@ * the pager requires a write lock on the object. */ if (need_dirty) - vm_page_dirty(m); + vm_page_set_dirty(m, excl); if (!excl) vm_page_unlock(m); - else if (need_dirty) - vm_pager_page_unswapped(m); } /* Index: sys/vm/vm_page.h =================================================================== --- sys/vm/vm_page.h +++ sys/vm/vm_page.h @@ -422,6 +422,10 @@ * PGA_REQUEUE_HEAD is a special flag for enqueuing pages near the head of * the inactive queue, thus bypassing LRU. The page lock must be held to * set this flag, and the queue lock for the page must be held to clear it. + * + * PGA_UNSWAPPED is used to defer freeing swap space to the pageout daemon + * when the context that dirties the page does not have the object write lock + * held. */ #define PGA_WRITEABLE 0x0001 /* page may be mapped writeable */ #define PGA_REFERENCED 0x0002 /* page has been referenced */ @@ -431,6 +435,7 @@ #define PGA_REQUEUE 0x0020 /* page is due to be requeued */ #define PGA_REQUEUE_HEAD 0x0040 /* page requeue should bypass LRU */ #define PGA_NOSYNC 0x0080 /* do not collect for syncer */ +#define PGA_UNSWAPPED 0x0100 /* page with swap space was dirtied */ #define PGA_QUEUE_STATE_MASK (PGA_ENQUEUED | PGA_DEQUEUE | PGA_REQUEUE | \ PGA_REQUEUE_HEAD) @@ -640,6 +645,7 @@ int vm_page_sbusied(vm_page_t m); vm_page_t vm_page_scan_contig(u_long npages, vm_page_t m_start, vm_page_t m_end, u_long alignment, vm_paddr_t boundary, int options); +void vm_page_set_dirty(vm_page_t m, bool locked); void vm_page_set_valid_range(vm_page_t m, int base, int size); int vm_page_sleep_if_busy(vm_page_t m, const char *msg); int vm_page_sleep_if_xbusy(vm_page_t m, const char *msg); Index: sys/vm/vm_page.c =================================================================== --- sys/vm/vm_page.c +++ sys/vm/vm_page.c @@ -1583,6 +1583,12 @@ KASSERT((m->ref_count & VPRC_OBJREF) != 0, ("page %p is missing its object ref", m)); + /* Deferred free of swap space. */ + if ((m->aflags & PGA_UNSWAPPED) != 0) { + vm_pager_page_unswapped(m); + vm_page_aflag_clear(m, PGA_UNSWAPPED); + } + mrem = vm_radix_remove(&object->rtree, m->pindex); KASSERT(mrem == m, ("removed page %p, expected page %p", mrem, m)); @@ -4604,6 +4610,62 @@ #endif /* PAGE_SIZE */ } +static inline vm_page_bits_t +vm_page_bits_swap(vm_page_t m, vm_page_bits_t *bits, vm_page_bits_t newbits) +{ +#if PAGE_SIZE == 32768 + uint64_t old; + + old = *bits; + while (atomic_fcmpset_64(bits, &old, newbits) == 0); + return (old); +#elif PAGE_SIZE == 16384 + uint32_t old; + + old = *bits; + while (atomic_fcmpset_32(bits, &old, newbits) == 0); + return (old); +#elif (PAGE_SIZE == 8192) && defined(atomic_fcmpset_16) + uint16_t old; + + old = *bits; + while (atomic_fcmpset_16(bits, &old, newbits) == 0); + return (old); +#elif (PAGE_SIZE == 4096) && defined(atomic_fcmpset_8) + uint8_t old; + + old = *bits; + while (atomic_fcmpset_8(bits, &old, newbits) == 0); + return (old); +#else /* PAGE_SIZE <= 4096*/ + uintptr_t addr; + uint32_t old, new, mask; + int shift; + + addr = (uintptr_t)bits; + /* + * Use a trick to perform a 32-bit atomic on the + * containing aligned word, to not depend on the existence + * of atomic_{set, swap, clear}_{8, 16}. + */ + shift = addr & (sizeof(uint32_t) - 1); +#if BYTE_ORDER == BIG_ENDIAN + shift = (sizeof(uint32_t) - sizeof(vm_page_bits_t) - shift) * NBBY; +#else + shift *= NBBY; +#endif + addr &= ~(sizeof(uint32_t) - 1); + mask = VM_PAGE_BITS_ALL << shift; + + old = *bits; + do { + new = old & ~mask; + new |= newbits << shift; + } while (atomic_fcmpset_32((uint32_t *)addr, &old, new) == 0); + return (old >> shift); +#endif /* PAGE_SIZE */ +} + /* * vm_page_set_valid_range: * @@ -4661,6 +4723,35 @@ vm_page_bits_set(m, &m->valid, pagebits); } +/* + * Set the page dirty bits and free the invalid swap space or schedule it to + * be cleared later. If the locked parameter is true the object lock is + * expected to be write locked. + */ +void +vm_page_set_dirty(vm_page_t m, bool locked) +{ + vm_page_bits_t old; + vm_object_t obj; + + VM_PAGE_OBJECT_BUSY_ASSERT(m); + obj = m->object; + if (locked) + VM_OBJECT_ASSERT_WLOCKED(obj); + + if (vm_page_xbusied(m) && !pmap_page_is_write_mapped(m)) { + old = m->dirty; + m->dirty = VM_PAGE_BITS_ALL; + } else + old = vm_page_bits_swap(m, &m->dirty, VM_PAGE_BITS_ALL); + if (old == 0 && obj != NULL && obj->type == OBJT_SWAP) { + if (locked) + vm_pager_page_unswapped(m); + else + vm_page_aflag_set(m, PGA_UNSWAPPED); + } +} + /* * Clear the given bits from the specified page's dirty field. */ Index: sys/vm/vm_pageout.c =================================================================== --- sys/vm/vm_pageout.c +++ sys/vm/vm_pageout.c @@ -1307,6 +1307,16 @@ act_delta++; } + /* Deferred free of swap space. */ + if ((m->aflags & PGA_UNSWAPPED) != 0 && + VM_OBJECT_TRYWLOCK(object)) { + if (m->object == object) { + vm_pager_page_unswapped(m); + vm_page_aflag_clear(m, PGA_UNSWAPPED); + } + VM_OBJECT_WUNLOCK(object); + } + /* * Advance or decay the act_count based on recent usage. */ @@ -1542,6 +1552,12 @@ goto reinsert; } + /* Deferred free of swap space. */ + if ((m->aflags & PGA_UNSWAPPED) != 0) { + vm_pager_page_unswapped(m); + vm_page_aflag_clear(m, PGA_UNSWAPPED); + } + /* * Re-check for wirings now that we hold the object lock and * have verified that the page is unbusied. If the page is