diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/resource.c | 103 |
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 | } |
1022 | EXPORT_SYMBOL(__release_region); | 1022 | EXPORT_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 | */ | ||
1045 | int 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 | */ |