aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorBrice Goglin <Brice.Goglin@inria.fr>2008-12-09 16:14:23 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2008-12-10 11:01:53 -0500
commit80bba1290ab5122c60cdb73332b26d288dc8aedd (patch)
tree1a97a6f5070818ffcb3573c1968471110bed3a42 /mm
parent52b9582dd5983ac888c494bd3e15b5cd40195c53 (diff)
mm: no get_user/put_user while holding mmap_sem in do_pages_stat?
Since commit 2f007e74bb85b9fc4eab28524052161703300f1a, do_pages_stat() gets the page address from user-space and puts the corresponding status back while holding the mmap_sem for read. There is no need to hold mmap_sem there while some page-faults may occur. This patch adds a temporary address and status buffer so as to only hold mmap_sem while working on these kernel buffers. This is implemented by extracting do_pages_stat_array() out of do_pages_stat(). Signed-off-by: Brice Goglin <Brice.Goglin@inria.fr> Cc: Christoph Lameter <clameter@sgi.com> Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Nick Piggin <npiggin@suse.de> Cc: Hugh Dickins <hugh@veritas.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/migrate.c59
1 files changed, 46 insertions, 13 deletions
diff --git a/mm/migrate.c b/mm/migrate.c
index 1e0d6b237f44..d8f07667fc80 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -987,25 +987,18 @@ out:
987/* 987/*
988 * Determine the nodes of an array of pages and store it in an array of status. 988 * Determine the nodes of an array of pages and store it in an array of status.
989 */ 989 */
990static int do_pages_stat(struct mm_struct *mm, unsigned long nr_pages, 990static void do_pages_stat_array(struct mm_struct *mm, unsigned long nr_pages,
991 const void __user * __user *pages, 991 const void __user **pages, int *status)
992 int __user *status)
993{ 992{
994 unsigned long i; 993 unsigned long i;
995 int err;
996 994
997 down_read(&mm->mmap_sem); 995 down_read(&mm->mmap_sem);
998 996
999 for (i = 0; i < nr_pages; i++) { 997 for (i = 0; i < nr_pages; i++) {
1000 const void __user *p; 998 unsigned long addr = (unsigned long)(*pages);
1001 unsigned long addr;
1002 struct vm_area_struct *vma; 999 struct vm_area_struct *vma;
1003 struct page *page; 1000 struct page *page;
1004 1001 int err;
1005 err = -EFAULT;
1006 if (get_user(p, pages+i))
1007 goto out;
1008 addr = (unsigned long) p;
1009 1002
1010 vma = find_vma(mm, addr); 1003 vma = find_vma(mm, addr);
1011 if (!vma) 1004 if (!vma)
@@ -1024,12 +1017,52 @@ static int do_pages_stat(struct mm_struct *mm, unsigned long nr_pages,
1024 1017
1025 err = page_to_nid(page); 1018 err = page_to_nid(page);
1026set_status: 1019set_status:
1027 put_user(err, status+i); 1020 *status = err;
1021
1022 pages++;
1023 status++;
1024 }
1025
1026 up_read(&mm->mmap_sem);
1027}
1028
1029/*
1030 * Determine the nodes of a user array of pages and store it in
1031 * a user array of status.
1032 */
1033static int do_pages_stat(struct mm_struct *mm, unsigned long nr_pages,
1034 const void __user * __user *pages,
1035 int __user *status)
1036{
1037#define DO_PAGES_STAT_CHUNK_NR 16
1038 const void __user *chunk_pages[DO_PAGES_STAT_CHUNK_NR];
1039 int chunk_status[DO_PAGES_STAT_CHUNK_NR];
1040 unsigned long i, chunk_nr = DO_PAGES_STAT_CHUNK_NR;
1041 int err;
1042
1043 for (i = 0; i < nr_pages; i += chunk_nr) {
1044 if (chunk_nr + i > nr_pages)
1045 chunk_nr = nr_pages - i;
1046
1047 err = copy_from_user(chunk_pages, &pages[i],
1048 chunk_nr * sizeof(*chunk_pages));
1049 if (err) {
1050 err = -EFAULT;
1051 goto out;
1052 }
1053
1054 do_pages_stat_array(mm, chunk_nr, chunk_pages, chunk_status);
1055
1056 err = copy_to_user(&status[i], chunk_status,
1057 chunk_nr * sizeof(*chunk_status));
1058 if (err) {
1059 err = -EFAULT;
1060 goto out;
1061 }
1028 } 1062 }
1029 err = 0; 1063 err = 0;
1030 1064
1031out: 1065out:
1032 up_read(&mm->mmap_sem);
1033 return err; 1066 return err;
1034} 1067}
1035 1068