diff --git a/sys/vm/swap_pager.c b/sys/vm/swap_pager.c --- a/sys/vm/swap_pager.c +++ b/sys/vm/swap_pager.c @@ -2379,10 +2379,24 @@ * * NOTE: Decrement swb_count after the freeing operation (which * might block) to prevent racing destruction of the swblock. + * + * DF-0946: previously this was a `while' loop that re-read + * swap->swb_pages[index] after the (potentially blocking) + * call to swp_pager_freeswapspace(). swp_pager_freeswapspace() + * acquires vm_token and may block in blist_free(); while + * blocked, the lwkt object token is shed and a concurrent + * meta_build() on another CPU can store a NEW swapblk into + * the same slot. On resume the first thread would re-read + * the slot, see the new value, and free that swapblk out + * from under the second caller. Using `if' instead of + * `while' frees only the value we observed before blocking + * and leaves any value inserted by a concurrent meta_build() + * intact. */ index &= SWAP_META_MASK; - while ((v = swap->swb_pages[index]) != SWAPBLK_NONE) { + v = swap->swb_pages[index]; + if (v != SWAPBLK_NONE) { swap->swb_pages[index] = SWAPBLK_NONE; /* can block */ swp_pager_freeswapspace(object, v, 1);