aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-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 */