diff mbox series

[RFC,V2,1/10] mm/resource: Move child to new resource when release mem region.

Message ID 20200107130950.2983-2-Tianyu.Lan@microsoft.com (mailing list archive)
State New, archived
Headers show
Series x86/Hyper-V: Add Dynamic memory hot-remove function | expand

Commit Message

Tianyu Lan Jan. 7, 2020, 1:09 p.m. UTC
From: Tianyu Lan <Tianyu.Lan@microsoft.com>

When release mem region, old mem region may be splited to
two regions. Current allocate new struct resource for high
end mem region but not move child resources whose ranges are
in the high end range to new resource. When adjust old mem
region's range, adjust_resource() detects child region's range
is out of new range and return error. Move child resources to
high end resource before adjusting old mem range.

Signed-off-by: Tianyu Lan <Tianyu.Lan@microsoft.com>
---
 kernel/resource.c | 38 ++++++++++++++++++++++++++++++++++----
 1 file changed, 34 insertions(+), 4 deletions(-)

Comments

Michael Kelley (LINUX) Jan. 20, 2020, 6:34 p.m. UTC | #1
From: Tianyu Lan <Tianyu.Lan@microsoft.com> Sent: Tuesday, January 7, 2020 5:10 AM
> 
> When release mem region, old mem region may be splited to
> two regions. Current allocate new struct resource for high
> end mem region but not move child resources whose ranges are
> in the high end range to new resource. When adjust old mem
> region's range, adjust_resource() detects child region's range
> is out of new range and return error. Move child resources to
> high end resource before adjusting old mem range.
> 
> Signed-off-by: Tianyu Lan <Tianyu.Lan@microsoft.com>
> ---
>  kernel/resource.c | 38 ++++++++++++++++++++++++++++++++++----
>  1 file changed, 34 insertions(+), 4 deletions(-)
> 
> diff --git a/kernel/resource.c b/kernel/resource.c
> index 76036a41143b..1c7362825134 100644
> --- a/kernel/resource.c
> +++ b/kernel/resource.c
> @@ -181,6 +181,38 @@ static struct resource *alloc_resource(gfp_t flags)
>  	return res;
>  }
> 
> +static void move_child_to_newresource(struct resource *old,
> +				      struct resource *new)
> +{
> +	struct resource *tmp, **p, **np;
> +
> +	if (!old->child)
> +		return;

I don't think the above test is needed.  This case is handled by the first
three lines of the "for" loop.

> +
> +	p = &old->child;
> +	np = &new->child;
> +
> +	for (;;) {
> +		tmp = *p;
> +		if (!tmp)
> +			break;
> +
> +		if (tmp->start >= new->start && tmp->end <= new->end) {
> +			tmp->parent = new;
> +			*np = tmp;
> +			np = &tmp->sibling;
> +			*p = tmp->sibling;
> +
> +			if (!tmp->sibling)
> +				*np = NULL;

I don't think the above two lines are right.  They seem tautological.  If the ! were
removed it would be clearing the sibling link for the child as it exists under its new
parent, which should be done.  But the child that is moved to the new parent always
becomes the last entry in the new parent's child list.  So could you just unconditionally
do tmp->sibling = NULL?   That link will get fixed up if another child is moved.

Michael

> +			continue;
> +		}
> +
> +		p = &tmp->sibling;
> +	}
> +}
> +
> +
>  /* Return the conflict entry if you can't request it */
>  static struct resource * __request_resource(struct resource *root, struct resource *new)
>  {
> @@ -1246,9 +1278,6 @@ EXPORT_SYMBOL(__release_region);
>   * Note:
>   * - Additional release conditions, such as overlapping region, can be
>   *   supported after they are confirmed as valid cases.
> - * - When a busy memory resource gets split into two entries, the code
> - *   assumes that all children remain in the lower address entry for
> - *   simplicity.  Enhance this logic when necessary.
>   */
>  int release_mem_region_adjustable(struct resource *parent,
>  				  resource_size_t start, resource_size_t size)
> @@ -1331,11 +1360,12 @@ int release_mem_region_adjustable(struct resource *parent,
>  			new_res->sibling = res->sibling;
>  			new_res->child = NULL;
> 
> +			move_child_to_newresource(res, new_res);
> +			res->sibling = new_res;
>  			ret = __adjust_resource(res, res->start,
>  						start - res->start);
>  			if (ret)
>  				break;
> -			res->sibling = new_res;
>  			new_res = NULL;
>  		}
> 
> --
> 2.14.5
Michael Kelley (LINUX) Jan. 20, 2020, 7:20 p.m. UTC | #2
From: Tianyu Lan <Tianyu.Lan@microsoft.com> Sent: Tuesday, January 7, 2020 5:10 AM
> 
> When release mem region, old mem region may be splited to
> two regions. Current allocate new struct resource for high
> end mem region but not move child resources whose ranges are
> in the high end range to new resource. When adjust old mem
> region's range, adjust_resource() detects child region's range
> is out of new range and return error. Move child resources to
> high end resource before adjusting old mem range.

Let me also suggests some wording improvements to the commit message:

When releasing a mem region, the old mem region may need to be
split into two regions.  In this case, the current code allocates the new
region and adjust the original region to specify a smaller range.  But child
regions that fall into the range of the new region are not moved to that
new region.  Consequently, when running __adjust_resource() on the
original region, it detects that the child region's range is out of the new
range, and returns an error.

Fix this by moving appropriate child resources to the new region before
adjusting the original mem region range.

> 
> Signed-off-by: Tianyu Lan <Tianyu.Lan@microsoft.com>
> ---
>  kernel/resource.c | 38 ++++++++++++++++++++++++++++++++++----
>  1 file changed, 34 insertions(+), 4 deletions(-)
>
diff mbox series

Patch

diff --git a/kernel/resource.c b/kernel/resource.c
index 76036a41143b..1c7362825134 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -181,6 +181,38 @@  static struct resource *alloc_resource(gfp_t flags)
 	return res;
 }
 
+static void move_child_to_newresource(struct resource *old,
+				      struct resource *new)
+{
+	struct resource *tmp, **p, **np;
+
+	if (!old->child)
+		return;
+
+	p = &old->child;
+	np = &new->child;
+
+	for (;;) {
+		tmp = *p;
+		if (!tmp)
+			break;
+
+		if (tmp->start >= new->start && tmp->end <= new->end) {
+			tmp->parent = new;
+			*np = tmp;
+			np = &tmp->sibling;
+			*p = tmp->sibling;
+
+			if (!tmp->sibling)
+				*np = NULL;
+			continue;
+		}
+
+		p = &tmp->sibling;
+	}
+}
+
+
 /* Return the conflict entry if you can't request it */
 static struct resource * __request_resource(struct resource *root, struct resource *new)
 {
@@ -1246,9 +1278,6 @@  EXPORT_SYMBOL(__release_region);
  * Note:
  * - Additional release conditions, such as overlapping region, can be
  *   supported after they are confirmed as valid cases.
- * - When a busy memory resource gets split into two entries, the code
- *   assumes that all children remain in the lower address entry for
- *   simplicity.  Enhance this logic when necessary.
  */
 int release_mem_region_adjustable(struct resource *parent,
 				  resource_size_t start, resource_size_t size)
@@ -1331,11 +1360,12 @@  int release_mem_region_adjustable(struct resource *parent,
 			new_res->sibling = res->sibling;
 			new_res->child = NULL;
 
+			move_child_to_newresource(res, new_res);
+			res->sibling = new_res;
 			ret = __adjust_resource(res, res->start,
 						start - res->start);
 			if (ret)
 				break;
-			res->sibling = new_res;
 			new_res = NULL;
 		}