aboutsummaryrefslogtreecommitdiffstats
path: root/mm/hugetlb.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/hugetlb.c')
-rw-r--r--mm/hugetlb.c58
1 files changed, 38 insertions, 20 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 454b311373c5..5a2515a774b5 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -135,15 +135,8 @@ static inline struct hugepage_subpool *subpool_vma(struct vm_area_struct *vma)
135 * Region tracking -- allows tracking of reservations and instantiated pages 135 * Region tracking -- allows tracking of reservations and instantiated pages
136 * across the pages in a mapping. 136 * across the pages in a mapping.
137 * 137 *
138 * The region data structures are protected by a combination of the mmap_sem 138 * The region data structures are embedded into a resv_map and
139 * and the hugetlb_instantiation_mutex. To access or modify a region the caller 139 * protected by a resv_map's lock
140 * must either hold the mmap_sem for write, or the mmap_sem for read and
141 * the hugetlb_instantiation_mutex:
142 *
143 * down_write(&mm->mmap_sem);
144 * or
145 * down_read(&mm->mmap_sem);
146 * mutex_lock(&hugetlb_instantiation_mutex);
147 */ 140 */
148struct file_region { 141struct file_region {
149 struct list_head link; 142 struct list_head link;
@@ -156,6 +149,7 @@ static long region_add(struct resv_map *resv, long f, long t)
156 struct list_head *head = &resv->regions; 149 struct list_head *head = &resv->regions;
157 struct file_region *rg, *nrg, *trg; 150 struct file_region *rg, *nrg, *trg;
158 151
152 spin_lock(&resv->lock);
159 /* Locate the region we are either in or before. */ 153 /* Locate the region we are either in or before. */
160 list_for_each_entry(rg, head, link) 154 list_for_each_entry(rg, head, link)
161 if (f <= rg->to) 155 if (f <= rg->to)
@@ -185,15 +179,18 @@ static long region_add(struct resv_map *resv, long f, long t)
185 } 179 }
186 nrg->from = f; 180 nrg->from = f;
187 nrg->to = t; 181 nrg->to = t;
182 spin_unlock(&resv->lock);
188 return 0; 183 return 0;
189} 184}
190 185
191static long region_chg(struct resv_map *resv, long f, long t) 186static long region_chg(struct resv_map *resv, long f, long t)
192{ 187{
193 struct list_head *head = &resv->regions; 188 struct list_head *head = &resv->regions;
194 struct file_region *rg, *nrg; 189 struct file_region *rg, *nrg = NULL;
195 long chg = 0; 190 long chg = 0;
196 191
192retry:
193 spin_lock(&resv->lock);
197 /* Locate the region we are before or in. */ 194 /* Locate the region we are before or in. */
198 list_for_each_entry(rg, head, link) 195 list_for_each_entry(rg, head, link)
199 if (f <= rg->to) 196 if (f <= rg->to)
@@ -203,15 +200,21 @@ static long region_chg(struct resv_map *resv, long f, long t)
203 * Subtle, allocate a new region at the position but make it zero 200 * Subtle, allocate a new region at the position but make it zero
204 * size such that we can guarantee to record the reservation. */ 201 * size such that we can guarantee to record the reservation. */
205 if (&rg->link == head || t < rg->from) { 202 if (&rg->link == head || t < rg->from) {
206 nrg = kmalloc(sizeof(*nrg), GFP_KERNEL); 203 if (!nrg) {
207 if (!nrg) 204 spin_unlock(&resv->lock);
208 return -ENOMEM; 205 nrg = kmalloc(sizeof(*nrg), GFP_KERNEL);
209 nrg->from = f; 206 if (!nrg)
210 nrg->to = f; 207 return -ENOMEM;
211 INIT_LIST_HEAD(&nrg->link); 208
212 list_add(&nrg->link, rg->link.prev); 209 nrg->from = f;
210 nrg->to = f;
211 INIT_LIST_HEAD(&nrg->link);
212 goto retry;
213 }
213 214
214 return t - f; 215 list_add(&nrg->link, rg->link.prev);
216 chg = t - f;
217 goto out_nrg;
215 } 218 }
216 219
217 /* Round our left edge to the current segment if it encloses us. */ 220 /* Round our left edge to the current segment if it encloses us. */
@@ -224,7 +227,7 @@ static long region_chg(struct resv_map *resv, long f, long t)
224 if (&rg->link == head) 227 if (&rg->link == head)
225 break; 228 break;
226 if (rg->from > t) 229 if (rg->from > t)
227 return chg; 230 goto out;
228 231
229 /* We overlap with this area, if it extends further than 232 /* We overlap with this area, if it extends further than
230 * us then we must extend ourselves. Account for its 233 * us then we must extend ourselves. Account for its
@@ -235,6 +238,14 @@ static long region_chg(struct resv_map *resv, long f, long t)
235 } 238 }
236 chg -= rg->to - rg->from; 239 chg -= rg->to - rg->from;
237 } 240 }
241
242out:
243 spin_unlock(&resv->lock);
244 /* We already know we raced and no longer need the new region */
245 kfree(nrg);
246 return chg;
247out_nrg:
248 spin_unlock(&resv->lock);
238 return chg; 249 return chg;
239} 250}
240 251
@@ -244,12 +255,13 @@ static long region_truncate(struct resv_map *resv, long end)
244 struct file_region *rg, *trg; 255 struct file_region *rg, *trg;
245 long chg = 0; 256 long chg = 0;
246 257
258 spin_lock(&resv->lock);
247 /* Locate the region we are either in or before. */ 259 /* Locate the region we are either in or before. */
248 list_for_each_entry(rg, head, link) 260 list_for_each_entry(rg, head, link)
249 if (end <= rg->to) 261 if (end <= rg->to)
250 break; 262 break;
251 if (&rg->link == head) 263 if (&rg->link == head)
252 return 0; 264 goto out;
253 265
254 /* If we are in the middle of a region then adjust it. */ 266 /* If we are in the middle of a region then adjust it. */
255 if (end > rg->from) { 267 if (end > rg->from) {
@@ -266,6 +278,9 @@ static long region_truncate(struct resv_map *resv, long end)
266 list_del(&rg->link); 278 list_del(&rg->link);
267 kfree(rg); 279 kfree(rg);
268 } 280 }
281
282out:
283 spin_unlock(&resv->lock);
269 return chg; 284 return chg;
270} 285}
271 286
@@ -275,6 +290,7 @@ static long region_count(struct resv_map *resv, long f, long t)
275 struct file_region *rg; 290 struct file_region *rg;
276 long chg = 0; 291 long chg = 0;
277 292
293 spin_lock(&resv->lock);
278 /* Locate each segment we overlap with, and count that overlap. */ 294 /* Locate each segment we overlap with, and count that overlap. */
279 list_for_each_entry(rg, head, link) { 295 list_for_each_entry(rg, head, link) {
280 long seg_from; 296 long seg_from;
@@ -290,6 +306,7 @@ static long region_count(struct resv_map *resv, long f, long t)
290 306
291 chg += seg_to - seg_from; 307 chg += seg_to - seg_from;
292 } 308 }
309 spin_unlock(&resv->lock);
293 310
294 return chg; 311 return chg;
295} 312}
@@ -387,6 +404,7 @@ struct resv_map *resv_map_alloc(void)
387 return NULL; 404 return NULL;
388 405
389 kref_init(&resv_map->refs); 406 kref_init(&resv_map->refs);
407 spin_lock_init(&resv_map->lock);
390 INIT_LIST_HEAD(&resv_map->regions); 408 INIT_LIST_HEAD(&resv_map->regions);
391 409
392 return resv_map; 410 return resv_map;