The backwards-coalescing check in kvm_dirty_ring_reset() spells out "the shift left must not lose any set bits" as a shift-out-and-back identity: if (delta > -BITS_PER_LONG && delta < 0 && (mask << -delta >> -delta) == mask) { That is exactly what check_shl_overflow() means, so say so directly. The macro also rejects a shift wider than the destination on its own, which subsumes the delta > -BITS_PER_LONG term, leaving only the direction test. Compare the offsets as unsigned and derive each shift from them in the direction that applies, rather than deriving both from the sign of a signed delta. The forward case keeps its explicit width check, since there is no shifted result to validate there, just a bit position. Also use BITS_PER_TYPE(mask) so the bound comes from the mask being shifted, and BIT() for the individual bits. No functional change intended for offsets within a memslot.Comparing unsigned does change how a wrapped offset is treated -- it becomes a distant offset rather than a small backwards step, so it ends the batch instead of being folded into it, and the legitimate entries gathered so far are no longer discarded along with it by kvm_reset_dirty_gfn()'s bounds check: harvested { 10, 0xfffffffffffffffd } offsets reset before: [], after: [10] Such an offset is bogus either way and is still rejected by the bounds check added in commit 577a8d3bae05 ("KVM: Reject wrapped offset in kvm_reset_dirty_gfn()"); only the collateral damage to its batch-mates is gone. The new form never coalesces where the old form refused. Signed-off-by: Peng Hao --- virt/kvm/dirty_ring.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/virt/kvm/dirty_ring.c b/virt/kvm/dirty_ring.c index 572b854edf74..8a9728fd7c09 100644 --- a/virt/kvm/dirty_ring.c +++ b/virt/kvm/dirty_ring.c @@ -6,6 +6,7 @@ */ #include #include +#include #include #include #include @@ -163,18 +164,21 @@ int kvm_dirty_ring_reset(struct kvm *kvm, struct kvm_dirty_ring *ring, * is scanning pages in the same slot. */ if (next_slot == cur_slot) { - s64 delta = next_offset - cur_offset; - - if (delta >= 0 && delta < BITS_PER_LONG) { - mask |= 1ull << delta; - continue; - } - - /* Backwards visit, careful about overflows! */ - if (delta > -BITS_PER_LONG && delta < 0 && - (mask << -delta >> -delta) == mask) { + unsigned long shifted_mask; + + if (next_offset >= cur_offset) { + u64 shift = next_offset - cur_offset; + + if (shift < BITS_PER_TYPE(mask)) { + mask |= BIT(shift); + continue; + } + } else if (!check_shl_overflow(mask, + cur_offset - next_offset, + &shifted_mask)) { + /* Backwards visit, move the batch's base down. */ cur_offset = next_offset; - mask = (mask << -delta) | 1; + mask = shifted_mask | BIT(0); continue; } } -- 2.43.7