diff options
Diffstat (limited to 'mm/mempolicy.c')
-rw-r--r-- | mm/mempolicy.c | 110 |
1 files changed, 72 insertions, 38 deletions
diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 08c41da429cf..cb41c31e7c87 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c | |||
@@ -238,46 +238,80 @@ static struct mempolicy *mpol_new(int mode, unsigned long *nodes) | |||
238 | } | 238 | } |
239 | 239 | ||
240 | /* Ensure all existing pages follow the policy. */ | 240 | /* Ensure all existing pages follow the policy. */ |
241 | static int | 241 | static int check_pte_range(struct mm_struct *mm, pmd_t *pmd, |
242 | verify_pages(struct mm_struct *mm, | 242 | unsigned long addr, unsigned long end, unsigned long *nodes) |
243 | unsigned long addr, unsigned long end, unsigned long *nodes) | ||
244 | { | 243 | { |
245 | while (addr < end) { | 244 | pte_t *orig_pte; |
246 | struct page *p; | 245 | pte_t *pte; |
247 | pte_t *pte; | 246 | |
248 | pmd_t *pmd; | 247 | spin_lock(&mm->page_table_lock); |
249 | pud_t *pud; | 248 | orig_pte = pte = pte_offset_map(pmd, addr); |
250 | pgd_t *pgd; | 249 | do { |
251 | pgd = pgd_offset(mm, addr); | 250 | unsigned long pfn; |
252 | if (pgd_none(*pgd)) { | 251 | unsigned int nid; |
253 | unsigned long next = (addr + PGDIR_SIZE) & PGDIR_MASK; | 252 | |
254 | if (next > addr) | 253 | if (!pte_present(*pte)) |
255 | break; | ||
256 | addr = next; | ||
257 | continue; | 254 | continue; |
258 | } | 255 | pfn = pte_pfn(*pte); |
259 | pud = pud_offset(pgd, addr); | 256 | if (!pfn_valid(pfn)) |
260 | if (pud_none(*pud)) { | ||
261 | addr = (addr + PUD_SIZE) & PUD_MASK; | ||
262 | continue; | 257 | continue; |
263 | } | 258 | nid = pfn_to_nid(pfn); |
264 | pmd = pmd_offset(pud, addr); | 259 | if (!test_bit(nid, nodes)) |
265 | if (pmd_none(*pmd)) { | 260 | break; |
266 | addr = (addr + PMD_SIZE) & PMD_MASK; | 261 | } while (pte++, addr += PAGE_SIZE, addr != end); |
262 | pte_unmap(orig_pte); | ||
263 | spin_unlock(&mm->page_table_lock); | ||
264 | return addr != end; | ||
265 | } | ||
266 | |||
267 | static inline int check_pmd_range(struct mm_struct *mm, pud_t *pud, | ||
268 | unsigned long addr, unsigned long end, unsigned long *nodes) | ||
269 | { | ||
270 | pmd_t *pmd; | ||
271 | unsigned long next; | ||
272 | |||
273 | pmd = pmd_offset(pud, addr); | ||
274 | do { | ||
275 | next = pmd_addr_end(addr, end); | ||
276 | if (pmd_none_or_clear_bad(pmd)) | ||
267 | continue; | 277 | continue; |
268 | } | 278 | if (check_pte_range(mm, pmd, addr, next, nodes)) |
269 | p = NULL; | 279 | return -EIO; |
270 | pte = pte_offset_map(pmd, addr); | 280 | } while (pmd++, addr = next, addr != end); |
271 | if (pte_present(*pte)) | 281 | return 0; |
272 | p = pte_page(*pte); | 282 | } |
273 | pte_unmap(pte); | 283 | |
274 | if (p) { | 284 | static inline int check_pud_range(struct mm_struct *mm, pgd_t *pgd, |
275 | unsigned nid = page_to_nid(p); | 285 | unsigned long addr, unsigned long end, unsigned long *nodes) |
276 | if (!test_bit(nid, nodes)) | 286 | { |
277 | return -EIO; | 287 | pud_t *pud; |
278 | } | 288 | unsigned long next; |
279 | addr += PAGE_SIZE; | 289 | |
280 | } | 290 | pud = pud_offset(pgd, addr); |
291 | do { | ||
292 | next = pud_addr_end(addr, end); | ||
293 | if (pud_none_or_clear_bad(pud)) | ||
294 | continue; | ||
295 | if (check_pmd_range(mm, pud, addr, next, nodes)) | ||
296 | return -EIO; | ||
297 | } while (pud++, addr = next, addr != end); | ||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | static inline int check_pgd_range(struct mm_struct *mm, | ||
302 | unsigned long addr, unsigned long end, unsigned long *nodes) | ||
303 | { | ||
304 | pgd_t *pgd; | ||
305 | unsigned long next; | ||
306 | |||
307 | pgd = pgd_offset(mm, addr); | ||
308 | do { | ||
309 | next = pgd_addr_end(addr, end); | ||
310 | if (pgd_none_or_clear_bad(pgd)) | ||
311 | continue; | ||
312 | if (check_pud_range(mm, pgd, addr, next, nodes)) | ||
313 | return -EIO; | ||
314 | } while (pgd++, addr = next, addr != end); | ||
281 | return 0; | 315 | return 0; |
282 | } | 316 | } |
283 | 317 | ||
@@ -299,7 +333,7 @@ check_range(struct mm_struct *mm, unsigned long start, unsigned long end, | |||
299 | if (prev && prev->vm_end < vma->vm_start) | 333 | if (prev && prev->vm_end < vma->vm_start) |
300 | return ERR_PTR(-EFAULT); | 334 | return ERR_PTR(-EFAULT); |
301 | if ((flags & MPOL_MF_STRICT) && !is_vm_hugetlb_page(vma)) { | 335 | if ((flags & MPOL_MF_STRICT) && !is_vm_hugetlb_page(vma)) { |
302 | err = verify_pages(vma->vm_mm, | 336 | err = check_pgd_range(vma->vm_mm, |
303 | vma->vm_start, vma->vm_end, nodes); | 337 | vma->vm_start, vma->vm_end, nodes); |
304 | if (err) { | 338 | if (err) { |
305 | first = ERR_PTR(err); | 339 | first = ERR_PTR(err); |
@@ -721,7 +755,7 @@ static struct page *alloc_page_interleave(unsigned int __nocast gfp, unsigned or | |||
721 | zl = NODE_DATA(nid)->node_zonelists + (gfp & GFP_ZONEMASK); | 755 | zl = NODE_DATA(nid)->node_zonelists + (gfp & GFP_ZONEMASK); |
722 | page = __alloc_pages(gfp, order, zl); | 756 | page = __alloc_pages(gfp, order, zl); |
723 | if (page && page_zone(page) == zl->zones[0]) { | 757 | if (page && page_zone(page) == zl->zones[0]) { |
724 | zl->zones[0]->pageset[get_cpu()].interleave_hit++; | 758 | zone_pcp(zl->zones[0],get_cpu())->interleave_hit++; |
725 | put_cpu(); | 759 | put_cpu(); |
726 | } | 760 | } |
727 | return page; | 761 | return page; |