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>

authored by

Brice Goglin and committed by
Linus Torvalds
80bba129 52b9582d

+46 -13
+46 -13
mm/migrate.c
··· 987 987 /* 988 988 * Determine the nodes of an array of pages and store it in an array of status. 989 989 */ 990 - static int do_pages_stat(struct mm_struct *mm, unsigned long nr_pages, 991 - const void __user * __user *pages, 992 - int __user *status) 990 + static void do_pages_stat_array(struct mm_struct *mm, unsigned long nr_pages, 991 + const void __user **pages, int *status) 993 992 { 994 993 unsigned long i; 995 - int err; 996 994 997 995 down_read(&mm->mmap_sem); 998 996 999 997 for (i = 0; i < nr_pages; i++) { 1000 - const void __user *p; 1001 - unsigned long addr; 998 + unsigned long addr = (unsigned long)(*pages); 1002 999 struct vm_area_struct *vma; 1003 1000 struct page *page; 1004 - 1005 - err = -EFAULT; 1006 - if (get_user(p, pages+i)) 1007 - goto out; 1008 - addr = (unsigned long) p; 1001 + int err; 1009 1002 1010 1003 vma = find_vma(mm, addr); 1011 1004 if (!vma) ··· 1017 1024 1018 1025 err = page_to_nid(page); 1019 1026 set_status: 1020 - put_user(err, status+i); 1027 + *status = err; 1028 + 1029 + pages++; 1030 + status++; 1031 + } 1032 + 1033 + up_read(&mm->mmap_sem); 1034 + } 1035 + 1036 + /* 1037 + * Determine the nodes of a user array of pages and store it in 1038 + * a user array of status. 1039 + */ 1040 + static int do_pages_stat(struct mm_struct *mm, unsigned long nr_pages, 1041 + const void __user * __user *pages, 1042 + int __user *status) 1043 + { 1044 + #define DO_PAGES_STAT_CHUNK_NR 16 1045 + const void __user *chunk_pages[DO_PAGES_STAT_CHUNK_NR]; 1046 + int chunk_status[DO_PAGES_STAT_CHUNK_NR]; 1047 + unsigned long i, chunk_nr = DO_PAGES_STAT_CHUNK_NR; 1048 + int err; 1049 + 1050 + for (i = 0; i < nr_pages; i += chunk_nr) { 1051 + if (chunk_nr + i > nr_pages) 1052 + chunk_nr = nr_pages - i; 1053 + 1054 + err = copy_from_user(chunk_pages, &pages[i], 1055 + chunk_nr * sizeof(*chunk_pages)); 1056 + if (err) { 1057 + err = -EFAULT; 1058 + goto out; 1059 + } 1060 + 1061 + do_pages_stat_array(mm, chunk_nr, chunk_pages, chunk_status); 1062 + 1063 + err = copy_to_user(&status[i], chunk_status, 1064 + chunk_nr * sizeof(*chunk_status)); 1065 + if (err) { 1066 + err = -EFAULT; 1067 + goto out; 1068 + } 1021 1069 } 1022 1070 err = 0; 1023 1071 1024 1072 out: 1025 - up_read(&mm->mmap_sem); 1026 1073 return err; 1027 1074 } 1028 1075