diff mbox series

[1/6] KVM: Fix coalesced_mmio_has_room()

Message ID 20240710085259.2125131-2-ilstam@amazon.com (mailing list archive)
State New, archived
Headers show
Series KVM: Improve MMIO Coalescing API | expand

Commit Message

Ilias Stamatis July 10, 2024, 8:52 a.m. UTC
The following calculation used in coalesced_mmio_has_room() to check
whether the ring buffer is full is wrong and only allows half the buffer
to be used.

avail = (ring->first - last - 1) % KVM_COALESCED_MMIO_MAX;
if (avail == 0)
	/* full */

The % operator in C is not the modulo operator but the remainder
operator. Modulo and remainder operators differ with respect to negative
values. But all values are unsigned in this case anyway.

The above might have worked as expected in python for example:
>>> (-86) % 170
84

However it doesn't work the same way in C.

printf("avail: %d\n", (-86) % 170);
printf("avail: %u\n", (-86) % 170);
printf("avail: %u\n", (-86u) % 170u);

Using gcc-11 these print:

avail: -86
avail: 4294967210
avail: 0

Fix the calculation and allow all but one entries in the buffer to be
used as originally intended.

Fixes: 105f8d40a737 ("KVM: Calculate available entries in coalesced mmio ring")
Signed-off-by: Ilias Stamatis <ilstam@amazon.com>
---
 virt/kvm/coalesced_mmio.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

Comments

Paul Durrant July 12, 2024, 1:13 p.m. UTC | #1
On 10/07/2024 10:52, Ilias Stamatis wrote:
> The following calculation used in coalesced_mmio_has_room() to check
> whether the ring buffer is full is wrong and only allows half the buffer
> to be used.
> 
> avail = (ring->first - last - 1) % KVM_COALESCED_MMIO_MAX;
> if (avail == 0)
> 	/* full */
> 
> The % operator in C is not the modulo operator but the remainder
> operator. Modulo and remainder operators differ with respect to negative
> values. But all values are unsigned in this case anyway.
> 
> The above might have worked as expected in python for example:
>>>> (-86) % 170
> 84
> 
> However it doesn't work the same way in C.
> 
> printf("avail: %d\n", (-86) % 170);
> printf("avail: %u\n", (-86) % 170);
> printf("avail: %u\n", (-86u) % 170u);
> 
> Using gcc-11 these print:
> 
> avail: -86
> avail: 4294967210
> avail: 0
> 
> Fix the calculation and allow all but one entries in the buffer to be
> used as originally intended.
> 
> Fixes: 105f8d40a737 ("KVM: Calculate available entries in coalesced mmio ring")
> Signed-off-by: Ilias Stamatis <ilstam@amazon.com>
> ---
>   virt/kvm/coalesced_mmio.c | 4 +---
>   1 file changed, 1 insertion(+), 3 deletions(-)
> 

Reviewed-by: Paul Durrant <paul@xen.org>
Kagan, Roman July 12, 2024, 3:55 p.m. UTC | #2
On Wed, Jul 10, 2024 at 09:52:54AM +0100, Ilias Stamatis wrote:
> The following calculation used in coalesced_mmio_has_room() to check
> whether the ring buffer is full is wrong and only allows half the buffer
> to be used.
> 
> avail = (ring->first - last - 1) % KVM_COALESCED_MMIO_MAX;
> if (avail == 0)
> 	/* full */
> 
> The % operator in C is not the modulo operator but the remainder
> operator. Modulo and remainder operators differ with respect to negative
> values. But all values are unsigned in this case anyway.
> 
> The above might have worked as expected in python for example:
> >>> (-86) % 170
> 84
> 
> However it doesn't work the same way in C.
> 
> printf("avail: %d\n", (-86) % 170);
> printf("avail: %u\n", (-86) % 170);
> printf("avail: %u\n", (-86u) % 170u);
> 
> Using gcc-11 these print:
> 
> avail: -86
> avail: 4294967210
> avail: 0

Where exactly do you see a problem?  As you correctly point out, all
values are unsigned, so unsigned arithmetics with wraparound applies,
and then % operator is applied to the resulting unsigned value.  Out
your three examples, only the last one is relevant, and it's perfectly
the intended behavior.

Thanks,
Roman.



Amazon Web Services Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597
Stamatis, Ilias July 12, 2024, 7:03 p.m. UTC | #3
On Fri, 2024-07-12 at 17:55 +0200, Roman Kagan wrote:
> On Wed, Jul 10, 2024 at 09:52:54AM +0100, Ilias Stamatis wrote:
> > The following calculation used in coalesced_mmio_has_room() to check
> > whether the ring buffer is full is wrong and only allows half the buffer
> > to be used.
> > 
> > avail = (ring->first - last - 1) % KVM_COALESCED_MMIO_MAX;
> > if (avail == 0)
> > 	/* full */
> > 
> > The % operator in C is not the modulo operator but the remainder
> > operator. Modulo and remainder operators differ with respect to negative
> > values. But all values are unsigned in this case anyway.
> > 
> > The above might have worked as expected in python for example:
> > > > > (-86) % 170
> > 84
> > 
> > However it doesn't work the same way in C.
> > 
> > printf("avail: %d\n", (-86) % 170);
> > printf("avail: %u\n", (-86) % 170);
> > printf("avail: %u\n", (-86u) % 170u);
> > 
> > Using gcc-11 these print:
> > 
> > avail: -86
> > avail: 4294967210
> > avail: 0
> 
> Where exactly do you see a problem?  As you correctly point out, all
> values are unsigned, so unsigned arithmetics with wraparound applies,
> and then % operator is applied to the resulting unsigned value.  Out
> your three examples, only the last one is relevant, and it's perfectly
> the intended behavior.
> 
> Thanks,
> Roman.

KVM_COALESCED_MMIO_MAX on x86 is 170, which means the ring buffer has
170 entries (169 of which should be usable). 

If first = 0 and last = 85 then the calculation gives 0 available
entries in which case we consider the buffer to be full and we exit to
userspace. But we shouldn't as there are still 84 unused entries.

So I don't see how this could have been the intended behaviour. 

Ilias
Kagan, Roman July 15, 2024, 9:30 a.m. UTC | #4
On Fri, Jul 12, 2024 at 09:03:32PM +0200, Stamatis, Ilias wrote:
> On Fri, 2024-07-12 at 17:55 +0200, Roman Kagan wrote:
> > On Wed, Jul 10, 2024 at 09:52:54AM +0100, Ilias Stamatis wrote:
> > > The following calculation used in coalesced_mmio_has_room() to check
> > > whether the ring buffer is full is wrong and only allows half the buffer
> > > to be used.
> > > 
> > > avail = (ring->first - last - 1) % KVM_COALESCED_MMIO_MAX;
> > > if (avail == 0)
> > > 	/* full */
> > > 
> > > The % operator in C is not the modulo operator but the remainder
> > > operator. Modulo and remainder operators differ with respect to negative
> > > values. But all values are unsigned in this case anyway.
> > > 
> > > The above might have worked as expected in python for example:
> > > > > > (-86) % 170
> > > 84
> > > 
> > > However it doesn't work the same way in C.
> > > 
> > > printf("avail: %d\n", (-86) % 170);
> > > printf("avail: %u\n", (-86) % 170);
> > > printf("avail: %u\n", (-86u) % 170u);
> > > 
> > > Using gcc-11 these print:
> > > 
> > > avail: -86
> > > avail: 4294967210
> > > avail: 0
> > 
> > Where exactly do you see a problem?  As you correctly point out, all
> > values are unsigned, so unsigned arithmetics with wraparound applies,
> > and then % operator is applied to the resulting unsigned value.  Out
> > your three examples, only the last one is relevant, and it's perfectly
> > the intended behavior.
> > 
> > Thanks,
> > Roman.
> 
> KVM_COALESCED_MMIO_MAX on x86 is 170, which means the ring buffer has
> 170 entries (169 of which should be usable). 
> 
> If first = 0 and last = 85 then the calculation gives 0 available
> entries in which case we consider the buffer to be full and we exit to
> userspace. But we shouldn't as there are still 84 unused entries.

You want to add a stride instead

	avail = (ring->first + KVM_COALESCED_MMIO_MAX - 1 - last) %
		KVM_COALESCED_MMIO_MAX;

As long as the stride is smaller than half the wraparound, you can think
of the values being on an infinite non-negative axis.

Roman.



Amazon Web Services Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597
diff mbox series

Patch

diff --git a/virt/kvm/coalesced_mmio.c b/virt/kvm/coalesced_mmio.c
index 1b90acb6e3fe..184c5c40c9c1 100644
--- a/virt/kvm/coalesced_mmio.c
+++ b/virt/kvm/coalesced_mmio.c
@@ -43,7 +43,6 @@  static int coalesced_mmio_in_range(struct kvm_coalesced_mmio_dev *dev,
 static int coalesced_mmio_has_room(struct kvm_coalesced_mmio_dev *dev, u32 last)
 {
 	struct kvm_coalesced_mmio_ring *ring;
-	unsigned avail;
 
 	/* Are we able to batch it ? */
 
@@ -52,8 +51,7 @@  static int coalesced_mmio_has_room(struct kvm_coalesced_mmio_dev *dev, u32 last)
 	 * there is always one unused entry in the buffer
 	 */
 	ring = dev->kvm->coalesced_mmio_ring;
-	avail = (ring->first - last - 1) % KVM_COALESCED_MMIO_MAX;
-	if (avail == 0) {
+	if ((last + 1) % KVM_COALESCED_MMIO_MAX == READ_ONCE(ring->first)) {
 		/* full */
 		return 0;
 	}