diff options
Diffstat (limited to 'kernel/resource.c')
-rw-r--r-- | kernel/resource.c | 198 |
1 files changed, 176 insertions, 22 deletions
diff --git a/kernel/resource.c b/kernel/resource.c index 73f35d4b30b9..d7386986e10e 100644 --- a/kernel/resource.c +++ b/kernel/resource.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/seq_file.h> | 21 | #include <linux/seq_file.h> |
22 | #include <linux/device.h> | 22 | #include <linux/device.h> |
23 | #include <linux/pfn.h> | 23 | #include <linux/pfn.h> |
24 | #include <linux/mm.h> | ||
24 | #include <asm/io.h> | 25 | #include <asm/io.h> |
25 | 26 | ||
26 | 27 | ||
@@ -50,6 +51,14 @@ struct resource_constraint { | |||
50 | 51 | ||
51 | static DEFINE_RWLOCK(resource_lock); | 52 | static DEFINE_RWLOCK(resource_lock); |
52 | 53 | ||
54 | /* | ||
55 | * For memory hotplug, there is no way to free resource entries allocated | ||
56 | * by boot mem after the system is up. So for reusing the resource entry | ||
57 | * we need to remember the resource. | ||
58 | */ | ||
59 | static struct resource *bootmem_resource_free; | ||
60 | static DEFINE_SPINLOCK(bootmem_resource_lock); | ||
61 | |||
53 | static void *r_next(struct seq_file *m, void *v, loff_t *pos) | 62 | static void *r_next(struct seq_file *m, void *v, loff_t *pos) |
54 | { | 63 | { |
55 | struct resource *p = v; | 64 | struct resource *p = v; |
@@ -151,6 +160,40 @@ __initcall(ioresources_init); | |||
151 | 160 | ||
152 | #endif /* CONFIG_PROC_FS */ | 161 | #endif /* CONFIG_PROC_FS */ |
153 | 162 | ||
163 | static void free_resource(struct resource *res) | ||
164 | { | ||
165 | if (!res) | ||
166 | return; | ||
167 | |||
168 | if (!PageSlab(virt_to_head_page(res))) { | ||
169 | spin_lock(&bootmem_resource_lock); | ||
170 | res->sibling = bootmem_resource_free; | ||
171 | bootmem_resource_free = res; | ||
172 | spin_unlock(&bootmem_resource_lock); | ||
173 | } else { | ||
174 | kfree(res); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | static struct resource *alloc_resource(gfp_t flags) | ||
179 | { | ||
180 | struct resource *res = NULL; | ||
181 | |||
182 | spin_lock(&bootmem_resource_lock); | ||
183 | if (bootmem_resource_free) { | ||
184 | res = bootmem_resource_free; | ||
185 | bootmem_resource_free = res->sibling; | ||
186 | } | ||
187 | spin_unlock(&bootmem_resource_lock); | ||
188 | |||
189 | if (res) | ||
190 | memset(res, 0, sizeof(struct resource)); | ||
191 | else | ||
192 | res = kzalloc(sizeof(struct resource), flags); | ||
193 | |||
194 | return res; | ||
195 | } | ||
196 | |||
154 | /* Return the conflict entry if you can't request it */ | 197 | /* Return the conflict entry if you can't request it */ |
155 | static struct resource * __request_resource(struct resource *root, struct resource *new) | 198 | static struct resource * __request_resource(struct resource *root, struct resource *new) |
156 | { | 199 | { |
@@ -706,24 +749,13 @@ void insert_resource_expand_to_fit(struct resource *root, struct resource *new) | |||
706 | write_unlock(&resource_lock); | 749 | write_unlock(&resource_lock); |
707 | } | 750 | } |
708 | 751 | ||
709 | /** | 752 | static int __adjust_resource(struct resource *res, resource_size_t start, |
710 | * adjust_resource - modify a resource's start and size | 753 | resource_size_t size) |
711 | * @res: resource to modify | ||
712 | * @start: new start value | ||
713 | * @size: new size | ||
714 | * | ||
715 | * Given an existing resource, change its start and size to match the | ||
716 | * arguments. Returns 0 on success, -EBUSY if it can't fit. | ||
717 | * Existing children of the resource are assumed to be immutable. | ||
718 | */ | ||
719 | int adjust_resource(struct resource *res, resource_size_t start, resource_size_t size) | ||
720 | { | 754 | { |
721 | struct resource *tmp, *parent = res->parent; | 755 | struct resource *tmp, *parent = res->parent; |
722 | resource_size_t end = start + size - 1; | 756 | resource_size_t end = start + size - 1; |
723 | int result = -EBUSY; | 757 | int result = -EBUSY; |
724 | 758 | ||
725 | write_lock(&resource_lock); | ||
726 | |||
727 | if (!parent) | 759 | if (!parent) |
728 | goto skip; | 760 | goto skip; |
729 | 761 | ||
@@ -751,6 +783,26 @@ skip: | |||
751 | result = 0; | 783 | result = 0; |
752 | 784 | ||
753 | out: | 785 | out: |
786 | return result; | ||
787 | } | ||
788 | |||
789 | /** | ||
790 | * adjust_resource - modify a resource's start and size | ||
791 | * @res: resource to modify | ||
792 | * @start: new start value | ||
793 | * @size: new size | ||
794 | * | ||
795 | * Given an existing resource, change its start and size to match the | ||
796 | * arguments. Returns 0 on success, -EBUSY if it can't fit. | ||
797 | * Existing children of the resource are assumed to be immutable. | ||
798 | */ | ||
799 | int adjust_resource(struct resource *res, resource_size_t start, | ||
800 | resource_size_t size) | ||
801 | { | ||
802 | int result; | ||
803 | |||
804 | write_lock(&resource_lock); | ||
805 | result = __adjust_resource(res, start, size); | ||
754 | write_unlock(&resource_lock); | 806 | write_unlock(&resource_lock); |
755 | return result; | 807 | return result; |
756 | } | 808 | } |
@@ -762,7 +814,7 @@ static void __init __reserve_region_with_split(struct resource *root, | |||
762 | { | 814 | { |
763 | struct resource *parent = root; | 815 | struct resource *parent = root; |
764 | struct resource *conflict; | 816 | struct resource *conflict; |
765 | struct resource *res = kzalloc(sizeof(*res), GFP_ATOMIC); | 817 | struct resource *res = alloc_resource(GFP_ATOMIC); |
766 | struct resource *next_res = NULL; | 818 | struct resource *next_res = NULL; |
767 | 819 | ||
768 | if (!res) | 820 | if (!res) |
@@ -787,7 +839,7 @@ static void __init __reserve_region_with_split(struct resource *root, | |||
787 | /* conflict covered whole area */ | 839 | /* conflict covered whole area */ |
788 | if (conflict->start <= res->start && | 840 | if (conflict->start <= res->start && |
789 | conflict->end >= res->end) { | 841 | conflict->end >= res->end) { |
790 | kfree(res); | 842 | free_resource(res); |
791 | WARN_ON(next_res); | 843 | WARN_ON(next_res); |
792 | break; | 844 | break; |
793 | } | 845 | } |
@@ -797,10 +849,9 @@ static void __init __reserve_region_with_split(struct resource *root, | |||
797 | end = res->end; | 849 | end = res->end; |
798 | res->end = conflict->start - 1; | 850 | res->end = conflict->start - 1; |
799 | if (conflict->end < end) { | 851 | if (conflict->end < end) { |
800 | next_res = kzalloc(sizeof(*next_res), | 852 | next_res = alloc_resource(GFP_ATOMIC); |
801 | GFP_ATOMIC); | ||
802 | if (!next_res) { | 853 | if (!next_res) { |
803 | kfree(res); | 854 | free_resource(res); |
804 | break; | 855 | break; |
805 | } | 856 | } |
806 | next_res->name = name; | 857 | next_res->name = name; |
@@ -890,7 +941,7 @@ struct resource * __request_region(struct resource *parent, | |||
890 | const char *name, int flags) | 941 | const char *name, int flags) |
891 | { | 942 | { |
892 | DECLARE_WAITQUEUE(wait, current); | 943 | DECLARE_WAITQUEUE(wait, current); |
893 | struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); | 944 | struct resource *res = alloc_resource(GFP_KERNEL); |
894 | 945 | ||
895 | if (!res) | 946 | if (!res) |
896 | return NULL; | 947 | return NULL; |
@@ -924,7 +975,7 @@ struct resource * __request_region(struct resource *parent, | |||
924 | continue; | 975 | continue; |
925 | } | 976 | } |
926 | /* Uhhuh, that didn't work out.. */ | 977 | /* Uhhuh, that didn't work out.. */ |
927 | kfree(res); | 978 | free_resource(res); |
928 | res = NULL; | 979 | res = NULL; |
929 | break; | 980 | break; |
930 | } | 981 | } |
@@ -958,7 +1009,7 @@ int __check_region(struct resource *parent, resource_size_t start, | |||
958 | return -EBUSY; | 1009 | return -EBUSY; |
959 | 1010 | ||
960 | release_resource(res); | 1011 | release_resource(res); |
961 | kfree(res); | 1012 | free_resource(res); |
962 | return 0; | 1013 | return 0; |
963 | } | 1014 | } |
964 | EXPORT_SYMBOL(__check_region); | 1015 | EXPORT_SYMBOL(__check_region); |
@@ -998,7 +1049,7 @@ void __release_region(struct resource *parent, resource_size_t start, | |||
998 | write_unlock(&resource_lock); | 1049 | write_unlock(&resource_lock); |
999 | if (res->flags & IORESOURCE_MUXED) | 1050 | if (res->flags & IORESOURCE_MUXED) |
1000 | wake_up(&muxed_resource_wait); | 1051 | wake_up(&muxed_resource_wait); |
1001 | kfree(res); | 1052 | free_resource(res); |
1002 | return; | 1053 | return; |
1003 | } | 1054 | } |
1004 | p = &res->sibling; | 1055 | p = &res->sibling; |
@@ -1012,6 +1063,109 @@ void __release_region(struct resource *parent, resource_size_t start, | |||
1012 | } | 1063 | } |
1013 | EXPORT_SYMBOL(__release_region); | 1064 | EXPORT_SYMBOL(__release_region); |
1014 | 1065 | ||
1066 | #ifdef CONFIG_MEMORY_HOTREMOVE | ||
1067 | /** | ||
1068 | * release_mem_region_adjustable - release a previously reserved memory region | ||
1069 | * @parent: parent resource descriptor | ||
1070 | * @start: resource start address | ||
1071 | * @size: resource region size | ||
1072 | * | ||
1073 | * This interface is intended for memory hot-delete. The requested region | ||
1074 | * is released from a currently busy memory resource. The requested region | ||
1075 | * must either match exactly or fit into a single busy resource entry. In | ||
1076 | * the latter case, the remaining resource is adjusted accordingly. | ||
1077 | * Existing children of the busy memory resource must be immutable in the | ||
1078 | * request. | ||
1079 | * | ||
1080 | * Note: | ||
1081 | * - Additional release conditions, such as overlapping region, can be | ||
1082 | * supported after they are confirmed as valid cases. | ||
1083 | * - When a busy memory resource gets split into two entries, the code | ||
1084 | * assumes that all children remain in the lower address entry for | ||
1085 | * simplicity. Enhance this logic when necessary. | ||
1086 | */ | ||
1087 | int release_mem_region_adjustable(struct resource *parent, | ||
1088 | resource_size_t start, resource_size_t size) | ||
1089 | { | ||
1090 | struct resource **p; | ||
1091 | struct resource *res; | ||
1092 | struct resource *new_res; | ||
1093 | resource_size_t end; | ||
1094 | int ret = -EINVAL; | ||
1095 | |||
1096 | end = start + size - 1; | ||
1097 | if ((start < parent->start) || (end > parent->end)) | ||
1098 | return ret; | ||
1099 | |||
1100 | /* The alloc_resource() result gets checked later */ | ||
1101 | new_res = alloc_resource(GFP_KERNEL); | ||
1102 | |||
1103 | p = &parent->child; | ||
1104 | write_lock(&resource_lock); | ||
1105 | |||
1106 | while ((res = *p)) { | ||
1107 | if (res->start >= end) | ||
1108 | break; | ||
1109 | |||
1110 | /* look for the next resource if it does not fit into */ | ||
1111 | if (res->start > start || res->end < end) { | ||
1112 | p = &res->sibling; | ||
1113 | continue; | ||
1114 | } | ||
1115 | |||
1116 | if (!(res->flags & IORESOURCE_MEM)) | ||
1117 | break; | ||
1118 | |||
1119 | if (!(res->flags & IORESOURCE_BUSY)) { | ||
1120 | p = &res->child; | ||
1121 | continue; | ||
1122 | } | ||
1123 | |||
1124 | /* found the target resource; let's adjust accordingly */ | ||
1125 | if (res->start == start && res->end == end) { | ||
1126 | /* free the whole entry */ | ||
1127 | *p = res->sibling; | ||
1128 | free_resource(res); | ||
1129 | ret = 0; | ||
1130 | } else if (res->start == start && res->end != end) { | ||
1131 | /* adjust the start */ | ||
1132 | ret = __adjust_resource(res, end + 1, | ||
1133 | res->end - end); | ||
1134 | } else if (res->start != start && res->end == end) { | ||
1135 | /* adjust the end */ | ||
1136 | ret = __adjust_resource(res, res->start, | ||
1137 | start - res->start); | ||
1138 | } else { | ||
1139 | /* split into two entries */ | ||
1140 | if (!new_res) { | ||
1141 | ret = -ENOMEM; | ||
1142 | break; | ||
1143 | } | ||
1144 | new_res->name = res->name; | ||
1145 | new_res->start = end + 1; | ||
1146 | new_res->end = res->end; | ||
1147 | new_res->flags = res->flags; | ||
1148 | new_res->parent = res->parent; | ||
1149 | new_res->sibling = res->sibling; | ||
1150 | new_res->child = NULL; | ||
1151 | |||
1152 | ret = __adjust_resource(res, res->start, | ||
1153 | start - res->start); | ||
1154 | if (ret) | ||
1155 | break; | ||
1156 | res->sibling = new_res; | ||
1157 | new_res = NULL; | ||
1158 | } | ||
1159 | |||
1160 | break; | ||
1161 | } | ||
1162 | |||
1163 | write_unlock(&resource_lock); | ||
1164 | free_resource(new_res); | ||
1165 | return ret; | ||
1166 | } | ||
1167 | #endif /* CONFIG_MEMORY_HOTREMOVE */ | ||
1168 | |||
1015 | /* | 1169 | /* |
1016 | * Managed region resource | 1170 | * Managed region resource |
1017 | */ | 1171 | */ |