aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/resource.c
diff options
context:
space:
mode:
authorToshi Kani <toshi.kani@hp.com>2013-04-29 18:08:19 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-04-29 18:54:37 -0400
commit825f787bb49676083b97c1de1f8f2f8f26b5c908 (patch)
treef0cda2ad649b442fffc064dc625aa85e2b005ae2 /kernel/resource.c
parentae8e3a915aef5af5ace5936c56f05f0b1502ded1 (diff)
resource: add release_mem_region_adjustable()
Add release_mem_region_adjustable(), which releases a requested region from a currently busy memory resource. This interface adjusts the matched memory resource accordingly even if the requested region does not match exactly but still fits into. This new interface is intended for memory hot-delete. During bootup, memory resources are inserted from the boot descriptor table, such as EFI Memory Table and e820. Each memory resource entry usually covers the whole contigous memory range. Memory hot-delete request, on the other hand, may target to a particular range of memory resource, and its size can be much smaller than the whole contiguous memory. Since the existing release interfaces like __release_region() require a requested region to be exactly matched to a resource entry, they do not allow a partial resource to be released. This new interface is restrictive (i.e. release under certain conditions), which is consistent with other release interfaces, __release_region() and __release_resource(). Additional release conditions, such as an overlapping region to a resource entry, can be supported after they are confirmed as valid cases. There is no change to the existing interfaces since their restriction is valid for I/O resources. [akpm@linux-foundation.org: use GFP_ATOMIC under write_lock()] [akpm@linux-foundation.org: switch back to GFP_KERNEL, less buggily] [akpm@linux-foundation.org: remove unneeded and wrong kfree(), per Toshi] Signed-off-by: Toshi Kani <toshi.kani@hp.com> Reviewed-by : Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com> Cc: David Rientjes <rientjes@google.com> Reviewed-by: Ram Pai <linuxram@us.ibm.com> Cc: T Makphaibulchoke <tmac@hp.com> Cc: Wen Congyang <wency@cn.fujitsu.com> Cc: Tang Chen <tangchen@cn.fujitsu.com> Cc: Jiang Liu <jiang.liu@huawei.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/resource.c')
-rw-r--r--kernel/resource.c103
1 files changed, 103 insertions, 0 deletions
diff --git a/kernel/resource.c b/kernel/resource.c
index ae246f97c5d3..4aef8867fd4b 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -1021,6 +1021,109 @@ void __release_region(struct resource *parent, resource_size_t start,
1021} 1021}
1022EXPORT_SYMBOL(__release_region); 1022EXPORT_SYMBOL(__release_region);
1023 1023
1024#ifdef CONFIG_MEMORY_HOTREMOVE
1025/**
1026 * release_mem_region_adjustable - release a previously reserved memory region
1027 * @parent: parent resource descriptor
1028 * @start: resource start address
1029 * @size: resource region size
1030 *
1031 * This interface is intended for memory hot-delete. The requested region
1032 * is released from a currently busy memory resource. The requested region
1033 * must either match exactly or fit into a single busy resource entry. In
1034 * the latter case, the remaining resource is adjusted accordingly.
1035 * Existing children of the busy memory resource must be immutable in the
1036 * request.
1037 *
1038 * Note:
1039 * - Additional release conditions, such as overlapping region, can be
1040 * supported after they are confirmed as valid cases.
1041 * - When a busy memory resource gets split into two entries, the code
1042 * assumes that all children remain in the lower address entry for
1043 * simplicity. Enhance this logic when necessary.
1044 */
1045int release_mem_region_adjustable(struct resource *parent,
1046 resource_size_t start, resource_size_t size)
1047{
1048 struct resource **p;
1049 struct resource *res;
1050 struct resource *new_res;
1051 resource_size_t end;
1052 int ret = -EINVAL;
1053
1054 end = start + size - 1;
1055 if ((start < parent->start) || (end > parent->end))
1056 return ret;
1057
1058 /* The kzalloc() result gets checked later */
1059 new_res = kzalloc(sizeof(struct resource), GFP_KERNEL);
1060
1061 p = &parent->child;
1062 write_lock(&resource_lock);
1063
1064 while ((res = *p)) {
1065 if (res->start >= end)
1066 break;
1067
1068 /* look for the next resource if it does not fit into */
1069 if (res->start > start || res->end < end) {
1070 p = &res->sibling;
1071 continue;
1072 }
1073
1074 if (!(res->flags & IORESOURCE_MEM))
1075 break;
1076
1077 if (!(res->flags & IORESOURCE_BUSY)) {
1078 p = &res->child;
1079 continue;
1080 }
1081
1082 /* found the target resource; let's adjust accordingly */
1083 if (res->start == start && res->end == end) {
1084 /* free the whole entry */
1085 *p = res->sibling;
1086 kfree(res);
1087 ret = 0;
1088 } else if (res->start == start && res->end != end) {
1089 /* adjust the start */
1090 ret = __adjust_resource(res, end + 1,
1091 res->end - end);
1092 } else if (res->start != start && res->end == end) {
1093 /* adjust the end */
1094 ret = __adjust_resource(res, res->start,
1095 start - res->start);
1096 } else {
1097 /* split into two entries */
1098 if (!new_res) {
1099 ret = -ENOMEM;
1100 break;
1101 }
1102 new_res->name = res->name;
1103 new_res->start = end + 1;
1104 new_res->end = res->end;
1105 new_res->flags = res->flags;
1106 new_res->parent = res->parent;
1107 new_res->sibling = res->sibling;
1108 new_res->child = NULL;
1109
1110 ret = __adjust_resource(res, res->start,
1111 start - res->start);
1112 if (ret)
1113 break;
1114 res->sibling = new_res;
1115 new_res = NULL;
1116 }
1117
1118 break;
1119 }
1120
1121 write_unlock(&resource_lock);
1122 kfree(new_res);
1123 return ret;
1124}
1125#endif /* CONFIG_MEMORY_HOTREMOVE */
1126
1024/* 1127/*
1025 * Managed region resource 1128 * Managed region resource
1026 */ 1129 */