Index: sys/vm/swap_pager.c =================================================================== --- sys/vm/swap_pager.c +++ sys/vm/swap_pager.c @@ -2216,33 +2216,62 @@ start = (sb != NULL && sb->p < pindex) ? pindex - sb->p : 0; for (; sb != NULL; sb = swblk_start_limit(srcobject, pindex, last), start = 0) { - limit = MIN(last - sb->p, SWAP_META_PAGES); - for (i = start; i < limit; i++) { + daddr_t d[SWAP_META_PAGES]; + int d_mask = 0; + + pindex = sb->p; + limit = MIN(last - pindex, SWAP_META_PAGES); + for (i = start; i < limit; i++, pindex += SWAP_META_PAGES) { if (sb->d[i] == SWAPBLK_NONE) continue; blk = swp_pager_meta_build(dstobject, - sb->p + i - offset, sb->d[i], true); + pindex + i - offset, sb->d[i], true); if (blk == sb->d[i]) { /* * Destination has no swapblk and is not - * resident, so transfer source. + * resident, so transfer source. But * swp_pager_meta_build() failed memory * allocation already, likely to sleep in retry. */ - VM_OBJECT_WUNLOCK(srcobject); - swp_pager_meta_build(dstobject, - sb->p + i - offset, sb->d[i], false); - VM_OBJECT_WLOCK(srcobject); + d[i] = blk; + d_mask |= 1 << i; } else if (blk != SWAPBLK_NONE) swp_pager_update_freerange(&range, sb->d[i]); sb->d[i] = SWAPBLK_NONE; } - pindex = sb->p + SWAP_META_PAGES; if (swp_pager_swblk_empty(sb, 0, start) && swp_pager_swblk_empty(sb, limit, SWAP_META_PAGES)) { swblk_lookup_remove(srcobject, sb); uma_zfree(swblk_zone, sb); } + + /* + * Try transferring un-transferred blocks again, since it's + * likely that just-freed memory will allow success this time, + * without releasing the write lock. + */ + while (d_mask != 0) { + i = ffs(d_mask) - 1; + blk = swp_pager_meta_build(dstobject, + pindex + i - offset, d[i], true); + if (blk == d[i]) + break; + d_mask ^= 1 << i; + } + if (d_mask == 0) + continue; + + /* + * Finish transferring, with the lock released. + */ + VM_OBJECT_WUNLOCK(srcobject); + do { + i = ffs(d_mask) - 1; + swp_pager_meta_build(dstobject, + pindex + i - offset, d[i], false); + d_mask ^= 1 << i; + } while (d_mask != 0); + VM_OBJECT_WLOCK(srcobject); } swp_pager_freeswapspace(&range); }