Skip to contentSkip to conversation

kvm: fix to update memslots properly

PATCH In review Tiejun Chen 4 participants 7 messages Dec 26, 2014 – Mar 10, 2015 KVM

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 kvm: fix to update memslots properly

After commit, 0e60b0799fed, "kvm: change memslot sorting rule from size to GFN" is introduced, we're missing but need to consider such a case, (!new->base_gfn && !mslots[i - 1].base_gfn && !mslots[i - 1].npages), then re-sort kvm_memslots wrong in next case to issue the following,

KVM internal error. Suberror: 1
emulation failure
EAX=000dee58 EBX=00000000 ECX=00000000 EDX=00000cfd ESI=00000059 EDI=00000000 EBP=00000000 ESP=00006fc4 EIP=000f17f4 EFL=00010012 [----A--] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00c09300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00c09b00 DPL=0 CS32 [-RA]

SS =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00c09300 DPL=0 DS   [-WA]

LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 000f6c58 00000037
IDT= 000f6c96 00000000
CR0=60000011 CR2=00000000 CR3=00000000 CR4=00000000 DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000
DR6=00000000ffff0ff0 DR7=0000000000000400 EFER=0000000000000000
Code=e8 75 fc ff ff 89 f2 a8 10 89 d8 75 0a b9 74 17 ff ff ff d1 <5b> 5e c3 5b 5e e9 76 ff ff ff 57 56 53 8b 35 38 65 0f 00 85 f6 0f 88 be 00 00 00 0f b7 f6

And we also should set flag as 0 in case of (new->npages == 0) && (new->base_gfn == 0).

---

I test this both in Andy' case and Jamie's case.

 virt/kvm/kvm_main.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
virt/kvm/kvm_main.c +4 -1
index f528343..6e52f3f 100644
@@ -672,6 +672,7 @@ static void update_memslots(struct kvm_memslots *slots,
 	WARN_ON(mslots[i].id != id);
 	if (!new->npages) {
 		new->base_gfn = 0;
+		new->flags = 0;
 		if (mslots[i].npages)
 			slots->used_slots--;
 	} else {
@@ -688,7 +689,9 @@ static void update_memslots(struct kvm_memslots *slots,
 		i++;
 	}
 	while (i > 0 &&
-	       new->base_gfn > mslots[i - 1].base_gfn) {
+	       ((new->base_gfn > mslots[i - 1].base_gfn) ||
+	        (!new->base_gfn &&
+	         !mslots[i - 1].base_gfn && !mslots[i - 1].npages))) {
 		mslots[i] = mslots[i - 1];
 		slots->id_to_index[mslots[i].id] = i;
 		i--;
Signature · 1 line
1.9.1
maintainer
#1 lore raw

virt/kvm/kvm_main.c static void update_memslots(struct kvm_memslots *slots, · line 678

Quoted 12 lines
virt/kvm/kvm_main.c +1 -0
index f528343..6e52f3f 100644
@@ -672,6 +672,7 @@ static void update_memslots(struct kvm_memslots *slots,
 	WARN_ON(mslots[i].id != id);
 	if (!new->npages) {
 		new->base_gfn = 0;
+		new->flags = 0;
 		if (mslots[i].npages)
 			slots->used_slots--;
 	} else {

This should not be necessary. The part of the mslots array that has base_gfn == npages == 0 is entirely unused, and such a slot can never be returned by search_memslots because this:

if (gfn >= memslots[slot].base_gfn &&
gfn < memslots[slot].base_gfn + memslots[slot].npages)

can never be true.

virt/kvm/kvm_main.c static void update_memslots(struct kvm_memslots *slots, · line 697

Quoted 12 lines
virt/kvm/kvm_main.c +3 -1
@@ -688,7 +689,9 @@ static void update_memslots(struct kvm_memslots *slots,
 		i++;
 	}
 	while (i > 0 &&
-	       new->base_gfn > mslots[i - 1].base_gfn) {
+	       ((new->base_gfn > mslots[i - 1].base_gfn) ||
+	        (!new->base_gfn &&
+	         !mslots[i - 1].base_gfn && !mslots[i - 1].npages))) {
 		mslots[i] = mslots[i - 1];
 		slots->id_to_index[mslots[i].id] = i;
 		i--;

You should have explained _why_ this fixes the bug, and what invariant is not being respected, something like this:

kvm: fix sorting of memslots with base_gfn == 0

Before commit 0e60b0799fed (kvm: change memslot sorting rule from size to GFN, 2014-12-01), the memslots' sorting key was npages, meaning that a valid memslot couldn't have its sorting key equal to zero. On the other hand, a valid memslot can have base_gfn == 0, and invalid memslots are identified by base_gfn == npages == 0.

Because of this, commit 0e60b0799fed broke the invariant that invalid memslots are at the end of the mslots array. When a memslot with base_gfn == 0 was created, any invalid memslot before it were left in place.

This suggests another fix. We can change the insertion to use a ">=" comparison, as in your first patch. Alone it is not correct, but we only need to take some care and avoid breaking the case of deleting a memslot.

It's enough to wrap the second loop (that you patched) with "if (new->npages)". In the new->npages == 0 case the first loop has already set i to the right value, and moving i back would be wrong:

virt/kvm/kvm_main.c +17 -5
index f5283438ee05..050974c051b5 100644
@@ -687,11 +687,23 @@ static void update_memslots(struct kvm_memslots *slots,
 		slots->id_to_index[mslots[i].id] = i;
 		i++;
 	}
-	while (i > 0 &&
-	       new->base_gfn > mslots[i - 1].base_gfn) {
-		mslots[i] = mslots[i - 1];
-		slots->id_to_index[mslots[i].id] = i;
-		i--;
+
+	/*
+	 * The ">=" is needed when creating a slot with base_gfn == 0,
+	 * so that it moves before all those with base_gfn == npages == 0.
+	 *
+	 * On the other hand, if new->npages is zero, the above loop has
+	 * already left i pointing to the beginning of the empty part of
+	 * mslots, and the ">=" would move the hole backwards in this
+	 * case---which is wrong.  So skip the loop when deleting a slot.
+	 */
+	if (new->npages) {
+		while (i > 0 &&
+		       new->base_gfn >= mslots[i - 1].base_gfn) {
+			mslots[i] = mslots[i - 1];
+			slots->id_to_index[mslots[i].id] = i;
+			i--;
+		}
 	}
 
 	mslots[i] = *new;

Paolo

#2 lore raw
Quoted 32 lines · Paolo Bonzini

This suggests another fix. We can change the insertion to use a ">=" comparison, as in your first patch. Alone it is not correct, but we only need to take some care and avoid breaking the case of deleting a memslot.

It's enough to wrap the second loop (that you patched) with "if (new->npages)". In the new->npages == 0 case the first loop has already set i to the right value, and moving i back would be wrong:

virt/kvm/kvm_main.c +10 -5
index f5283438ee05..050974c051b5 100644
@@ -687,11 +687,23 @@ static void update_memslots(struct kvm_memslots *slots,
 		slots->id_to_index[mslots[i].id] = i;
 		i++;
 	}
-	while (i > 0 &&
-	       new->base_gfn > mslots[i - 1].base_gfn) {
-		mslots[i] = mslots[i - 1];
-		slots->id_to_index[mslots[i].id] = i;
-		i--;
+
+	/*
+	 * The ">=" is needed when creating a slot with base_gfn == 0,
+	 * so that it moves before all those with base_gfn == npages == 0.
+	 *
+	 * On the other hand, if new->npages is zero, the above loop has
+	 * already left i pointing to the beginning of the empty part of
+	 * mslots, and the ">=" would move the hole backwards in this
+	 * case---which is wrong.  So skip the loop when deleting a slot.
+	 */

virt/kvm/kvm_main.c static void update_memslots(struct kvm_memslots *slots, · line 709

Quoted 10 lines
virt/kvm/kvm_main.c +7 -0
+	if (new->npages) {
+		while (i > 0 &&
+		       new->base_gfn >= mslots[i - 1].base_gfn) {
+			mslots[i] = mslots[i - 1];
+			slots->id_to_index[mslots[i].id] = i;
+			i--;
+		}
 	}
 
 	mslots[i] = *new;

I gave this a try, and it works just fine for me too.

Signature · 1 line
Jamie Heilman                     http://audible.transient.net/~jamie/
#3 lore raw
In reply to Paolo Bonzini
Quoted 17 lines · Paolo Bonzini

virt/kvm/kvm_main.c static void update_memslots(struct kvm_memslots *slots, · line 678

Quoted 12 lines
virt/kvm/kvm_main.c +1 -0
index f528343..6e52f3f 100644
@@ -672,6 +672,7 @@ static void update_memslots(struct kvm_memslots *slots,
  	WARN_ON(mslots[i].id != id);
  	if (!new->npages) {
  		new->base_gfn = 0;
+		new->flags = 0;
  		if (mslots[i].npages)
  			slots->used_slots--;
  	} else {

This should not be necessary. The part of the mslots array that has base_gfn == npages == 0 is entirely unused, and such a slot can never be returned by search_memslots because this:

Quoted 4 lines

if (gfn >= memslots[slot].base_gfn &&
gfn < memslots[slot].base_gfn + memslots[slot].npages)

can never be true.

Yeah, but its really a little ugly to see some slots, base_gfn:npages:falgs = 0:0:(!0), to resort again when debug something inside of update_memslots().

Quoted 15 lines

virt/kvm/kvm_main.c static void update_memslots(struct kvm_memslots *slots, · line 697

Quoted 13 lines
virt/kvm/kvm_main.c +3 -1
@@ -688,7 +689,9 @@ static void update_memslots(struct kvm_memslots *slots,
  		i++;
  	}
  	while (i > 0 &&
-	       new->base_gfn > mslots[i - 1].base_gfn) {
+	       ((new->base_gfn > mslots[i - 1].base_gfn) ||
+	        (!new->base_gfn &&
+	         !mslots[i - 1].base_gfn && !mslots[i - 1].npages))) {
  		mslots[i] = mslots[i - 1];
  		slots->id_to_index[mslots[i].id] = i;
  		i--;

You should have explained _why_ this fixes the bug, and what invariant

Yeah.

Quoted 48 lines

is not being respected, something like this:

kvm: fix sorting of memslots with base_gfn == 0

Before commit 0e60b0799fed (kvm: change memslot sorting rule from size to GFN, 2014-12-01), the memslots' sorting key was npages, meaning that a valid memslot couldn't have its sorting key equal to zero. On the other hand, a valid memslot can have base_gfn == 0, and invalid memslots are identified by base_gfn == npages == 0.

Because of this, commit 0e60b0799fed broke the invariant that invalid memslots are at the end of the mslots array. When a memslot with base_gfn == 0 was created, any invalid memslot before it were left in place.

This suggests another fix. We can change the insertion to use a ">=" comparison, as in your first patch. Alone it is not correct, but we only need to take some care and avoid breaking the case of deleting a memslot.

It's enough to wrap the second loop (that you patched) with "if (new->npages)". In the new->npages == 0 case the first loop has already set i to the right value, and moving i back would be wrong:

virt/kvm/kvm_main.c +10 -5
index f5283438ee05..050974c051b5 100644
@@ -687,11 +687,23 @@ static void update_memslots(struct kvm_memslots *slots,
  		slots->id_to_index[mslots[i].id] = i;
  		i++;
  	}
-	while (i > 0 &&
-	       new->base_gfn > mslots[i - 1].base_gfn) {
-		mslots[i] = mslots[i - 1];
-		slots->id_to_index[mslots[i].id] = i;
-		i--;
+
+	/*
+	 * The ">=" is needed when creating a slot with base_gfn == 0,
+	 * so that it moves before all those with base_gfn == npages == 0.
+	 *
+	 * On the other hand, if new->npages is zero, the above loop has
+	 * already left i pointing to the beginning of the empty part of
+	 * mslots, and the ">=" would move the hole backwards in this
+	 * case---which is wrong.  So skip the loop when deleting a slot.
+	 */

virt/kvm/kvm_main.c static void update_memslots(struct kvm_memslots *slots, · line 709

Quoted 10 lines
virt/kvm/kvm_main.c +7 -0
+	if (new->npages) {
+		while (i > 0 &&
+		       new->base_gfn >= mslots[i - 1].base_gfn) {
+			mslots[i] = mslots[i - 1];
+			slots->id_to_index[mslots[i].id] = i;
+			i--;
+		}
  	}
 
  	mslots[i] = *new;

This looks better.

Tiejun

#4 lore raw
In reply to Paolo Bonzini
Quoted 94 lines · Paolo Bonzini

virt/kvm/kvm_main.c static void update_memslots(struct kvm_memslots *slots, · line 678

Quoted 12 lines
virt/kvm/kvm_main.c +1 -0
index f528343..6e52f3f 100644
@@ -672,6 +672,7 @@ static void update_memslots(struct kvm_memslots *slots,
 	WARN_ON(mslots[i].id != id);
 	if (!new->npages) {
 		new->base_gfn = 0;
+		new->flags = 0;
 		if (mslots[i].npages)
 			slots->used_slots--;
 	} else {

This should not be necessary. The part of the mslots array that has base_gfn == npages == 0 is entirely unused, and such a slot can never be returned by search_memslots because this:

if (gfn >= memslots[slot].base_gfn &&
gfn < memslots[slot].base_gfn + memslots[slot].npages)

can never be true.

virt/kvm/kvm_main.c static void update_memslots(struct kvm_memslots *slots, · line 697

Quoted 12 lines
virt/kvm/kvm_main.c +3 -1
@@ -688,7 +689,9 @@ static void update_memslots(struct kvm_memslots *slots,
 		i++;
 	}
 	while (i > 0 &&
-	       new->base_gfn > mslots[i - 1].base_gfn) {
+	       ((new->base_gfn > mslots[i - 1].base_gfn) ||
+	        (!new->base_gfn &&
+	         !mslots[i - 1].base_gfn && !mslots[i - 1].npages))) {
 		mslots[i] = mslots[i - 1];
 		slots->id_to_index[mslots[i].id] = i;
 		i--;

You should have explained _why_ this fixes the bug, and what invariant is not being respected, something like this:

kvm: fix sorting of memslots with base_gfn == 0

Before commit 0e60b0799fed (kvm: change memslot sorting rule from size to GFN, 2014-12-01), the memslots' sorting key was npages, meaning that a valid memslot couldn't have its sorting key equal to zero. On the other hand, a valid memslot can have base_gfn == 0, and invalid memslots are identified by base_gfn == npages == 0.

Because of this, commit 0e60b0799fed broke the invariant that invalid memslots are at the end of the mslots array. When a memslot with base_gfn == 0 was created, any invalid memslot before it were left in place.

This suggests another fix. We can change the insertion to use a ">=" comparison, as in your first patch. Alone it is not correct, but we only need to take some care and avoid breaking the case of deleting a memslot.

It's enough to wrap the second loop (that you patched) with "if (new->npages)". In the new->npages == 0 case the first loop has already set i to the right value, and moving i back would be wrong:

virt/kvm/kvm_main.c +17 -5
index f5283438ee05..050974c051b5 100644
@@ -687,11 +687,23 @@ static void update_memslots(struct kvm_memslots *slots,
 		slots->id_to_index[mslots[i].id] = i;
 		i++;
 	}
-	while (i > 0 &&
-	       new->base_gfn > mslots[i - 1].base_gfn) {
-		mslots[i] = mslots[i - 1];
-		slots->id_to_index[mslots[i].id] = i;
-		i--;
+
+	/*
+	 * The ">=" is needed when creating a slot with base_gfn == 0,
+	 * so that it moves before all those with base_gfn == npages == 0.
+	 *
+	 * On the other hand, if new->npages is zero, the above loop has
+	 * already left i pointing to the beginning of the empty part of
+	 * mslots, and the ">=" would move the hole backwards in this
+	 * case---which is wrong.  So skip the loop when deleting a slot.
+	 */
+	if (new->npages) {
+		while (i > 0 &&
+		       new->base_gfn >= mslots[i - 1].base_gfn) {
+			mslots[i] = mslots[i - 1];
+			slots->id_to_index[mslots[i].id] = i;
+			i--;
+		}
 	}
 
 	mslots[i] = *new;
Quoted 1 line

Paolo

Paolo,

Can you include a proper changelog for this patch?

#5 lore raw
Quoted 93 lines · Marcelo Tosatti
Quoted 92 lines · Paolo Bonzini

virt/kvm/kvm_main.c static void update_memslots(struct kvm_memslots *slots, · line 678

Quoted 12 lines
virt/kvm/kvm_main.c +1 -0
index f528343..6e52f3f 100644
@@ -672,6 +672,7 @@ static void update_memslots(struct kvm_memslots *slots,
  	WARN_ON(mslots[i].id != id);
  	if (!new->npages) {
  		new->base_gfn = 0;
+		new->flags = 0;
  		if (mslots[i].npages)
  			slots->used_slots--;
  	} else {

This should not be necessary. The part of the mslots array that has base_gfn == npages == 0 is entirely unused, and such a slot can never be returned by search_memslots because this:

if (gfn >= memslots[slot].base_gfn &&
gfn < memslots[slot].base_gfn + memslots[slot].npages)

can never be true.

virt/kvm/kvm_main.c static void update_memslots(struct kvm_memslots *slots, · line 697

Quoted 12 lines
virt/kvm/kvm_main.c +3 -1
@@ -688,7 +689,9 @@ static void update_memslots(struct kvm_memslots *slots,
  		i++;
  	}
  	while (i > 0 &&
-	       new->base_gfn > mslots[i - 1].base_gfn) {
+	       ((new->base_gfn > mslots[i - 1].base_gfn) ||
+	        (!new->base_gfn &&
+	         !mslots[i - 1].base_gfn && !mslots[i - 1].npages))) {
  		mslots[i] = mslots[i - 1];
  		slots->id_to_index[mslots[i].id] = i;
  		i--;

You should have explained _why_ this fixes the bug, and what invariant is not being respected, something like this:

kvm: fix sorting of memslots with base_gfn == 0

Before commit 0e60b0799fed (kvm: change memslot sorting rule from size to GFN, 2014-12-01), the memslots' sorting key was npages, meaning that a valid memslot couldn't have its sorting key equal to zero. On the other hand, a valid memslot can have base_gfn == 0, and invalid memslots are identified by base_gfn == npages == 0.

Because of this, commit 0e60b0799fed broke the invariant that invalid memslots are at the end of the mslots array. When a memslot with base_gfn == 0 was created, any invalid memslot before it were left in place.

This suggests another fix. We can change the insertion to use a ">=" comparison, as in your first patch. Alone it is not correct, but we only need to take some care and avoid breaking the case of deleting a memslot.

It's enough to wrap the second loop (that you patched) with "if (new->npages)". In the new->npages == 0 case the first loop has already set i to the right value, and moving i back would be wrong:

virt/kvm/kvm_main.c +17 -5
index f5283438ee05..050974c051b5 100644
@@ -687,11 +687,23 @@ static void update_memslots(struct kvm_memslots *slots,
  		slots->id_to_index[mslots[i].id] = i;
  		i++;
  	}
-	while (i > 0 &&
-	       new->base_gfn > mslots[i - 1].base_gfn) {
-		mslots[i] = mslots[i - 1];
-		slots->id_to_index[mslots[i].id] = i;
-		i--;
+
+	/*
+	 * The ">=" is needed when creating a slot with base_gfn == 0,
+	 * so that it moves before all those with base_gfn == npages == 0.
+	 *
+	 * On the other hand, if new->npages is zero, the above loop has
+	 * already left i pointing to the beginning of the empty part of
+	 * mslots, and the ">=" would move the hole backwards in this
+	 * case---which is wrong.  So skip the loop when deleting a slot.
+	 */
+	if (new->npages) {
+		while (i > 0 &&
+		       new->base_gfn >= mslots[i - 1].base_gfn) {
+			mslots[i] = mslots[i - 1];
+			slots->id_to_index[mslots[i].id] = i;
+			i--;
+		}
  	}
 
Quoted 8 lines
Quoted 3 lines
virt/kvm/kvm_main.c +0 -0
  	mslots[i] = *new;

Paolo

Paolo,

Can you include a proper changelog for this patch?

But this is already applied long time ago...

Tiejun

maintainer
#6 lore raw
Quoted 45 lines
Quoted 45 lines
Quoted 45 lines

This suggests another fix. We can change the insertion to use a ">=" comparison, as in your first patch. Alone it is not correct, but we only need to take some care and avoid breaking the case of deleting a memslot.

It's enough to wrap the second loop (that you patched) with "if (new->npages)". In the new->npages == 0 case the first loop has already set i to the right value, and moving i back would be wrong:

virt/kvm/kvm_main.c +17 -5
index f5283438ee05..050974c051b5 100644
@@ -687,11 +687,23 @@ static void update_memslots(struct kvm_memslots
 *slots,
          slots->id_to_index[mslots[i].id] = i;
          i++;
      }
-    while (i > 0 &&
-           new->base_gfn > mslots[i - 1].base_gfn) {
-        mslots[i] = mslots[i - 1];
-        slots->id_to_index[mslots[i].id] = i;
-        i--;
+
+    /*
+     * The ">=" is needed when creating a slot with base_gfn == 0,
+     * so that it moves before all those with base_gfn == npages == 0.
+     *
+     * On the other hand, if new->npages is zero, the above loop has
+     * already left i pointing to the beginning of the empty part of
+     * mslots, and the ">=" would move the hole backwards in this
+     * case---which is wrong.  So skip the loop when deleting a slot.
+     */
+    if (new->npages) {
+        while (i > 0 &&
+               new->base_gfn >= mslots[i - 1].base_gfn) {
+            mslots[i] = mslots[i - 1];
+            slots->id_to_index[mslots[i].id] = i;
+            i--;
+        }
      }
 
      mslots[i] = *new;
Quoted 8 lines
Quoted 6 lines
Quoted 1 line

Paolo

Paolo,

Can you include a proper changelog for this patch?

But this is already applied long time ago...

Yes, this is commit efbeec7098eee2b3d2359d0cc24bbba0436e7f21.

Paolo