diff options
author | Kirill A. Shutemov <kirill.shutemov@linux.intel.com> | 2014-06-04 19:08:13 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-04 19:54:05 -0400 |
commit | fa5bb2093a1d2ba552309a81139e0abebf5325d8 (patch) | |
tree | e9be5d6d7e59cb3e8894aad43617c30ea5f31456 /mm/gup.c | |
parent | 1674448345cdb56e724483a2a26622771f4e3a10 (diff) |
mm: cleanup __get_user_pages()
Get rid of two nested loops over nr_pages, extract vma flags checking to
separate function and other random cleanups.
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/gup.c')
-rw-r--r-- | mm/gup.c | 218 |
1 files changed, 107 insertions, 111 deletions
@@ -315,6 +315,44 @@ static int faultin_page(struct task_struct *tsk, struct vm_area_struct *vma, | |||
315 | return 0; | 315 | return 0; |
316 | } | 316 | } |
317 | 317 | ||
318 | static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags) | ||
319 | { | ||
320 | vm_flags_t vm_flags = vma->vm_flags; | ||
321 | |||
322 | if (vm_flags & (VM_IO | VM_PFNMAP)) | ||
323 | return -EFAULT; | ||
324 | |||
325 | if (gup_flags & FOLL_WRITE) { | ||
326 | if (!(vm_flags & VM_WRITE)) { | ||
327 | if (!(gup_flags & FOLL_FORCE)) | ||
328 | return -EFAULT; | ||
329 | /* | ||
330 | * We used to let the write,force case do COW in a | ||
331 | * VM_MAYWRITE VM_SHARED !VM_WRITE vma, so ptrace could | ||
332 | * set a breakpoint in a read-only mapping of an | ||
333 | * executable, without corrupting the file (yet only | ||
334 | * when that file had been opened for writing!). | ||
335 | * Anon pages in shared mappings are surprising: now | ||
336 | * just reject it. | ||
337 | */ | ||
338 | if (!is_cow_mapping(vm_flags)) { | ||
339 | WARN_ON_ONCE(vm_flags & VM_MAYWRITE); | ||
340 | return -EFAULT; | ||
341 | } | ||
342 | } | ||
343 | } else if (!(vm_flags & VM_READ)) { | ||
344 | if (!(gup_flags & FOLL_FORCE)) | ||
345 | return -EFAULT; | ||
346 | /* | ||
347 | * Is there actually any vma we can reach here which does not | ||
348 | * have VM_MAYREAD set? | ||
349 | */ | ||
350 | if (!(vm_flags & VM_MAYREAD)) | ||
351 | return -EFAULT; | ||
352 | } | ||
353 | return 0; | ||
354 | } | ||
355 | |||
318 | /** | 356 | /** |
319 | * __get_user_pages() - pin user pages in memory | 357 | * __get_user_pages() - pin user pages in memory |
320 | * @tsk: task_struct of target task | 358 | * @tsk: task_struct of target task |
@@ -369,9 +407,9 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, | |||
369 | unsigned int gup_flags, struct page **pages, | 407 | unsigned int gup_flags, struct page **pages, |
370 | struct vm_area_struct **vmas, int *nonblocking) | 408 | struct vm_area_struct **vmas, int *nonblocking) |
371 | { | 409 | { |
372 | long i; | 410 | long i = 0; |
373 | unsigned long vm_flags; | ||
374 | unsigned int page_mask; | 411 | unsigned int page_mask; |
412 | struct vm_area_struct *vma = NULL; | ||
375 | 413 | ||
376 | if (!nr_pages) | 414 | if (!nr_pages) |
377 | return 0; | 415 | return 0; |
@@ -386,124 +424,82 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, | |||
386 | if (!(gup_flags & FOLL_FORCE)) | 424 | if (!(gup_flags & FOLL_FORCE)) |
387 | gup_flags |= FOLL_NUMA; | 425 | gup_flags |= FOLL_NUMA; |
388 | 426 | ||
389 | i = 0; | ||
390 | |||
391 | do { | 427 | do { |
392 | struct vm_area_struct *vma; | 428 | struct page *page; |
393 | 429 | unsigned int foll_flags = gup_flags; | |
394 | vma = find_extend_vma(mm, start); | 430 | unsigned int page_increm; |
395 | if (!vma && in_gate_area(mm, start)) { | 431 | |
396 | int ret; | 432 | /* first iteration or cross vma bound */ |
397 | ret = get_gate_page(mm, start & PAGE_MASK, gup_flags, | 433 | if (!vma || start >= vma->vm_end) { |
398 | &vma, pages ? &pages[i] : NULL); | 434 | vma = find_extend_vma(mm, start); |
399 | if (ret) | 435 | if (!vma && in_gate_area(mm, start)) { |
400 | goto efault; | 436 | int ret; |
401 | page_mask = 0; | 437 | ret = get_gate_page(mm, start & PAGE_MASK, |
402 | goto next_page; | 438 | gup_flags, &vma, |
403 | } | 439 | pages ? &pages[i] : NULL); |
440 | if (ret) | ||
441 | return i ? : ret; | ||
442 | page_mask = 0; | ||
443 | goto next_page; | ||
444 | } | ||
404 | 445 | ||
405 | if (!vma) | 446 | if (!vma || check_vma_flags(vma, gup_flags)) |
406 | goto efault; | 447 | return i ? : -EFAULT; |
407 | vm_flags = vma->vm_flags; | 448 | if (is_vm_hugetlb_page(vma)) { |
408 | if (vm_flags & (VM_IO | VM_PFNMAP)) | 449 | i = follow_hugetlb_page(mm, vma, pages, vmas, |
409 | goto efault; | 450 | &start, &nr_pages, i, |
410 | 451 | gup_flags); | |
411 | if (gup_flags & FOLL_WRITE) { | 452 | continue; |
412 | if (!(vm_flags & VM_WRITE)) { | ||
413 | if (!(gup_flags & FOLL_FORCE)) | ||
414 | goto efault; | ||
415 | /* | ||
416 | * We used to let the write,force case do COW | ||
417 | * in a VM_MAYWRITE VM_SHARED !VM_WRITE vma, so | ||
418 | * ptrace could set a breakpoint in a read-only | ||
419 | * mapping of an executable, without corrupting | ||
420 | * the file (yet only when that file had been | ||
421 | * opened for writing!). Anon pages in shared | ||
422 | * mappings are surprising: now just reject it. | ||
423 | */ | ||
424 | if (!is_cow_mapping(vm_flags)) { | ||
425 | WARN_ON_ONCE(vm_flags & VM_MAYWRITE); | ||
426 | goto efault; | ||
427 | } | ||
428 | } | 453 | } |
429 | } else { | 454 | } |
430 | if (!(vm_flags & VM_READ)) { | 455 | retry: |
431 | if (!(gup_flags & FOLL_FORCE)) | 456 | /* |
432 | goto efault; | 457 | * If we have a pending SIGKILL, don't keep faulting pages and |
433 | /* | 458 | * potentially allocating memory. |
434 | * Is there actually any vma we can reach here | 459 | */ |
435 | * which does not have VM_MAYREAD set? | 460 | if (unlikely(fatal_signal_pending(current))) |
436 | */ | 461 | return i ? i : -ERESTARTSYS; |
437 | if (!(vm_flags & VM_MAYREAD)) | 462 | cond_resched(); |
438 | goto efault; | 463 | page = follow_page_mask(vma, start, foll_flags, &page_mask); |
464 | if (!page) { | ||
465 | int ret; | ||
466 | ret = faultin_page(tsk, vma, start, &foll_flags, | ||
467 | nonblocking); | ||
468 | switch (ret) { | ||
469 | case 0: | ||
470 | goto retry; | ||
471 | case -EFAULT: | ||
472 | case -ENOMEM: | ||
473 | case -EHWPOISON: | ||
474 | return i ? i : ret; | ||
475 | case -EBUSY: | ||
476 | return i; | ||
477 | case -ENOENT: | ||
478 | goto next_page; | ||
439 | } | 479 | } |
480 | BUG(); | ||
440 | } | 481 | } |
441 | 482 | if (IS_ERR(page)) | |
442 | if (is_vm_hugetlb_page(vma)) { | 483 | return i ? i : PTR_ERR(page); |
443 | i = follow_hugetlb_page(mm, vma, pages, vmas, | 484 | if (pages) { |
444 | &start, &nr_pages, i, gup_flags); | 485 | pages[i] = page; |
445 | continue; | 486 | flush_anon_page(vma, page, start); |
487 | flush_dcache_page(page); | ||
488 | page_mask = 0; | ||
446 | } | 489 | } |
447 | |||
448 | do { | ||
449 | struct page *page; | ||
450 | unsigned int foll_flags = gup_flags; | ||
451 | unsigned int page_increm; | ||
452 | |||
453 | /* | ||
454 | * If we have a pending SIGKILL, don't keep faulting | ||
455 | * pages and potentially allocating memory. | ||
456 | */ | ||
457 | if (unlikely(fatal_signal_pending(current))) | ||
458 | return i ? i : -ERESTARTSYS; | ||
459 | |||
460 | cond_resched(); | ||
461 | while (!(page = follow_page_mask(vma, start, | ||
462 | foll_flags, &page_mask))) { | ||
463 | int ret; | ||
464 | ret = faultin_page(tsk, vma, start, &foll_flags, | ||
465 | nonblocking); | ||
466 | switch (ret) { | ||
467 | case 0: | ||
468 | break; | ||
469 | case -EFAULT: | ||
470 | case -ENOMEM: | ||
471 | case -EHWPOISON: | ||
472 | return i ? i : ret; | ||
473 | case -EBUSY: | ||
474 | return i; | ||
475 | case -ENOENT: | ||
476 | goto next_page; | ||
477 | default: | ||
478 | BUG(); | ||
479 | } | ||
480 | cond_resched(); | ||
481 | } | ||
482 | if (IS_ERR(page)) | ||
483 | return i ? i : PTR_ERR(page); | ||
484 | if (pages) { | ||
485 | pages[i] = page; | ||
486 | |||
487 | flush_anon_page(vma, page, start); | ||
488 | flush_dcache_page(page); | ||
489 | page_mask = 0; | ||
490 | } | ||
491 | next_page: | 490 | next_page: |
492 | if (vmas) { | 491 | if (vmas) { |
493 | vmas[i] = vma; | 492 | vmas[i] = vma; |
494 | page_mask = 0; | 493 | page_mask = 0; |
495 | } | 494 | } |
496 | page_increm = 1 + (~(start >> PAGE_SHIFT) & page_mask); | 495 | page_increm = 1 + (~(start >> PAGE_SHIFT) & page_mask); |
497 | if (page_increm > nr_pages) | 496 | if (page_increm > nr_pages) |
498 | page_increm = nr_pages; | 497 | page_increm = nr_pages; |
499 | i += page_increm; | 498 | i += page_increm; |
500 | start += page_increm * PAGE_SIZE; | 499 | start += page_increm * PAGE_SIZE; |
501 | nr_pages -= page_increm; | 500 | nr_pages -= page_increm; |
502 | } while (nr_pages && start < vma->vm_end); | ||
503 | } while (nr_pages); | 501 | } while (nr_pages); |
504 | return i; | 502 | return i; |
505 | efault: | ||
506 | return i ? : -EFAULT; | ||
507 | } | 503 | } |
508 | EXPORT_SYMBOL(__get_user_pages); | 504 | EXPORT_SYMBOL(__get_user_pages); |
509 | 505 | ||