diff mbox series

[v3,03/10] exec: Fix for qemu_ram_resize() callback

Message ID 20200311172014.33052-4-shameerali.kolothum.thodi@huawei.com
State Superseded
Headers show
Series ARM virt: Add NVDIMM support | expand

Commit Message

Shameerali Kolothum Thodi March 11, 2020, 5:20 p.m. UTC
From: David Hildenbrand <david@redhat.com>


Summarizing the issue:
1. Memory regions contain ram blocks with a different size,  if the
   size is  not properly aligned. While memory regions can have an
   unaligned size, ram blocks can't. This is true when creating
   resizable memory region with  an unaligned size.
2. When resizing a ram block/memory region, the size of the memory
   region  is set to the aligned size. The callback is called with
   the aligned size. The unaligned piece is lost.

Because of the above, if ACPI blob length modifications happens
after the initial virt_acpi_build() call, and the changed blob
length is within the PAGE size boundary, then the revised size
is not seen by the firmware on Guest reboot.

Hence make sure callback is called if memory region size is changed,
irrespective of aligned or not.

Signed-off-by: David Hildenbrand <david@redhat.com>

[Shameer: added commit log]
Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>

---
Please find the discussion here,
https://patchwork.kernel.org/patch/11339591/
---
 exec.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

-- 
2.17.1

Comments

David Hildenbrand March 11, 2020, 5:44 p.m. UTC | #1
On 11.03.20 18:20, Shameer Kolothum wrote:
> From: David Hildenbrand <david@redhat.com>

> 

> Summarizing the issue:

> 1. Memory regions contain ram blocks with a different size,  if the

>    size is  not properly aligned. While memory regions can have an

>    unaligned size, ram blocks can't. This is true when creating

>    resizable memory region with  an unaligned size.

> 2. When resizing a ram block/memory region, the size of the memory

>    region  is set to the aligned size. The callback is called with

>    the aligned size. The unaligned piece is lost.

> 

> Because of the above, if ACPI blob length modifications happens

> after the initial virt_acpi_build() call, and the changed blob

> length is within the PAGE size boundary, then the revised size

> is not seen by the firmware on Guest reboot.

> 

> Hence make sure callback is called if memory region size is changed,

> irrespective of aligned or not.

> 

> Signed-off-by: David Hildenbrand <david@redhat.com>

> [Shameer: added commit log]

> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>

> ---

> Please find the discussion here,

> https://patchwork.kernel.org/patch/11339591/

> ---

>  exec.c | 14 ++++++++++++--

>  1 file changed, 12 insertions(+), 2 deletions(-)

> 

> diff --git a/exec.c b/exec.c

> index 0cc500d53a..f8974cd303 100644

> --- a/exec.c

> +++ b/exec.c

> @@ -2073,11 +2073,21 @@ static int memory_try_enable_merging(void *addr, size_t len)

>   */

>  int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp)

>  {

> +    const ram_addr_t unaligned_size = newsize;

> +

>      assert(block);

>  

>      newsize = HOST_PAGE_ALIGN(newsize);

>  

>      if (block->used_length == newsize) {

> +        /*

> +         * We don't have to resize the ram block (which only knows aligned

> +         * sizes), however, we have to notify if the unaligned size changed.

> +         */

> +        if (block->resized && unaligned_size != memory_region_size(block->mr)) {

> +            block->resized(block->idstr, unaligned_size, block->host);

> +            memory_region_set_size(block->mr, unaligned_size);

> +        }


I guess we should do

if (unaligned_size != memory_region_size(block->mr)) {
    memory_region_set_size(block->mr, unaligned_size);
    if (block->resized) {
        block->resized(block->idstr, unaligned_size, block->host);
    }
}

Instead - like in the case below.


Note: This is not completely clean, the RAM block code should'n have to
care about unaligned stuff. Also, the resized() callback for the RAM
block is ugly, it should be a resized callback for the memory region.
But these things imply requires bigger refactorings, so I guess this is
good and simple enough for now.

-- 
Thanks,

David / dhildenb
Igor Mammedov March 23, 2020, 1:03 p.m. UTC | #2
On Wed, 11 Mar 2020 17:20:07 +0000
Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> wrote:

> From: David Hildenbrand <david@redhat.com>

> 

> Summarizing the issue:

> 1. Memory regions contain ram blocks with a different size,  if the

>    size is  not properly aligned. While memory regions can have an

>    unaligned size, ram blocks can't. This is true when creating

>    resizable memory region with  an unaligned size.

> 2. When resizing a ram block/memory region, the size of the memory

>    region  is set to the aligned size. The callback is called with

>    the aligned size. The unaligned piece is lost.

> 

> Because of the above, if ACPI blob length modifications happens

> after the initial virt_acpi_build() call, and the changed blob

> length is within the PAGE size boundary, then the revised size

> is not seen by the firmware on Guest reboot.

> 

> Hence make sure callback is called if memory region size is changed,

> irrespective of aligned or not.

> 

> Signed-off-by: David Hildenbrand <david@redhat.com>

> [Shameer: added commit log]

> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>


Reviewed-by: Igor Mammedov <imammedo@redhat.com>


> ---

> Please find the discussion here,

> https://patchwork.kernel.org/patch/11339591/

> ---

>  exec.c | 14 ++++++++++++--

>  1 file changed, 12 insertions(+), 2 deletions(-)

> 

> diff --git a/exec.c b/exec.c

> index 0cc500d53a..f8974cd303 100644

> --- a/exec.c

> +++ b/exec.c

> @@ -2073,11 +2073,21 @@ static int memory_try_enable_merging(void *addr, size_t len)

>   */

>  int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp)

>  {

> +    const ram_addr_t unaligned_size = newsize;

> +

>      assert(block);

>  

>      newsize = HOST_PAGE_ALIGN(newsize);

>  

>      if (block->used_length == newsize) {

> +        /*

> +         * We don't have to resize the ram block (which only knows aligned

> +         * sizes), however, we have to notify if the unaligned size changed.

> +         */

> +        if (block->resized && unaligned_size != memory_region_size(block->mr)) {

> +            block->resized(block->idstr, unaligned_size, block->host);

> +            memory_region_set_size(block->mr, unaligned_size);

> +        }

>          return 0;

>      }

>  

> @@ -2101,9 +2111,9 @@ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp)

>      block->used_length = newsize;

>      cpu_physical_memory_set_dirty_range(block->offset, block->used_length,

>                                          DIRTY_CLIENTS_ALL);

> -    memory_region_set_size(block->mr, newsize);

> +    memory_region_set_size(block->mr, unaligned_size);

>      if (block->resized) {

> -        block->resized(block->idstr, newsize, block->host);

> +        block->resized(block->idstr, unaligned_size, block->host);

>      }

>      return 0;

>  }
diff mbox series

Patch

diff --git a/exec.c b/exec.c
index 0cc500d53a..f8974cd303 100644
--- a/exec.c
+++ b/exec.c
@@ -2073,11 +2073,21 @@  static int memory_try_enable_merging(void *addr, size_t len)
  */
 int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp)
 {
+    const ram_addr_t unaligned_size = newsize;
+
     assert(block);
 
     newsize = HOST_PAGE_ALIGN(newsize);
 
     if (block->used_length == newsize) {
+        /*
+         * We don't have to resize the ram block (which only knows aligned
+         * sizes), however, we have to notify if the unaligned size changed.
+         */
+        if (block->resized && unaligned_size != memory_region_size(block->mr)) {
+            block->resized(block->idstr, unaligned_size, block->host);
+            memory_region_set_size(block->mr, unaligned_size);
+        }
         return 0;
     }
 
@@ -2101,9 +2111,9 @@  int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp)
     block->used_length = newsize;
     cpu_physical_memory_set_dirty_range(block->offset, block->used_length,
                                         DIRTY_CLIENTS_ALL);
-    memory_region_set_size(block->mr, newsize);
+    memory_region_set_size(block->mr, unaligned_size);
     if (block->resized) {
-        block->resized(block->idstr, newsize, block->host);
+        block->resized(block->idstr, unaligned_size, block->host);
     }
     return 0;
 }