Index: sys/vm/swap_pager.c =================================================================== --- sys/vm/swap_pager.c +++ sys/vm/swap_pager.c @@ -2201,7 +2201,8 @@ struct swblk *sb; daddr_t blk; vm_pindex_t offset, last; - int i, limit, start; + daddr_t d[SWAP_META_PAGES]; + int i, failed, limit, start; VM_OBJECT_ASSERT_WLOCKED(srcobject); VM_OBJECT_ASSERT_WLOCKED(dstobject); @@ -2210,39 +2211,53 @@ return; swp_pager_init_freerange(&range); + failed = 0; offset = pindex; last = pindex + count; sb = swblk_start_limit(srcobject, pindex, last); 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); + pindex = sb->p; + limit = MIN(last - pindex, SWAP_META_PAGES); for (i = start; i < limit; i++) { 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. - * swp_pager_meta_build() failed memory - * allocation already, likely to sleep in retry. + * Failed memory allocation stopped transfer; + * save remaining blocks for transfer with lock + * released. */ - VM_OBJECT_WUNLOCK(srcobject); - swp_pager_meta_build(dstobject, - sb->p + i - offset, sb->d[i], false); - VM_OBJECT_WLOCK(srcobject); + d[i++] = blk; + for (failed = i; i < limit; i++) { + d[i] = sb->d[i]; + sb->d[i] = SWAPBLK_NONE; + } } 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); } + if (failed > 0) { + /* Finish block transfer, with the lock released. */ + VM_OBJECT_WUNLOCK(srcobject); + for (i = failed - 1; i < limit; i++) { + if (d[i] == SWAPBLK_NONE) + continue; + swp_pager_meta_build(dstobject, + pindex + i - offset, d[i], false); + } + VM_OBJECT_WLOCK(srcobject); + failed = 0; + } + pindex += SWAP_META_PAGES; } swp_pager_freeswapspace(&range); }