diff options
author | Len Brown <len.brown@intel.com> | 2006-12-20 02:53:13 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2006-12-20 02:53:13 -0500 |
commit | 9774f3384125912eb491ca77f77907324db3ed05 (patch) | |
tree | 0bdc7486e911dd9e955b41283ee19ac74521f7bd /mm | |
parent | 3be11c8f4f2fa194834c2e83540f34da442b8977 (diff) | |
parent | f238085415c56618e042252894f2fcc971add645 (diff) |
merge linus into test branch
Diffstat (limited to 'mm')
-rw-r--r-- | mm/mincore.c | 183 |
1 files changed, 78 insertions, 105 deletions
diff --git a/mm/mincore.c b/mm/mincore.c index 72890780c1c9..8aca6f7167bb 100644 --- a/mm/mincore.c +++ b/mm/mincore.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * linux/mm/mincore.c | 2 | * linux/mm/mincore.c |
3 | * | 3 | * |
4 | * Copyright (C) 1994-1999 Linus Torvalds | 4 | * Copyright (C) 1994-2006 Linus Torvalds |
5 | */ | 5 | */ |
6 | 6 | ||
7 | /* | 7 | /* |
@@ -38,46 +38,51 @@ static unsigned char mincore_page(struct vm_area_struct * vma, | |||
38 | return present; | 38 | return present; |
39 | } | 39 | } |
40 | 40 | ||
41 | static long mincore_vma(struct vm_area_struct * vma, | 41 | /* |
42 | unsigned long start, unsigned long end, unsigned char __user * vec) | 42 | * Do a chunk of "sys_mincore()". We've already checked |
43 | * all the arguments, we hold the mmap semaphore: we should | ||
44 | * just return the amount of info we're asked for. | ||
45 | */ | ||
46 | static long do_mincore(unsigned long addr, unsigned char *vec, unsigned long pages) | ||
43 | { | 47 | { |
44 | long error, i, remaining; | 48 | unsigned long i, nr, pgoff; |
45 | unsigned char * tmp; | 49 | struct vm_area_struct *vma = find_vma(current->mm, addr); |
46 | |||
47 | error = -ENOMEM; | ||
48 | if (!vma->vm_file) | ||
49 | return error; | ||
50 | |||
51 | start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; | ||
52 | if (end > vma->vm_end) | ||
53 | end = vma->vm_end; | ||
54 | end = ((end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; | ||
55 | 50 | ||
56 | error = -EAGAIN; | 51 | /* |
57 | tmp = (unsigned char *) __get_free_page(GFP_KERNEL); | 52 | * find_vma() didn't find anything above us, or we're |
58 | if (!tmp) | 53 | * in an unmapped hole in the address space: ENOMEM. |
59 | return error; | 54 | */ |
55 | if (!vma || addr < vma->vm_start) | ||
56 | return -ENOMEM; | ||
60 | 57 | ||
61 | /* (end - start) is # of pages, and also # of bytes in "vec */ | 58 | /* |
62 | remaining = (end - start), | 59 | * Ok, got it. But check whether it's a segment we support |
60 | * mincore() on. Right now, we don't do any anonymous mappings. | ||
61 | * | ||
62 | * FIXME: This is just stupid. And returning ENOMEM is | ||
63 | * stupid too. We should just look at the page tables. But | ||
64 | * this is what we've traditionally done, so we'll just | ||
65 | * continue doing it. | ||
66 | */ | ||
67 | if (!vma->vm_file) | ||
68 | return -ENOMEM; | ||
63 | 69 | ||
64 | error = 0; | 70 | /* |
65 | for (i = 0; remaining > 0; remaining -= PAGE_SIZE, i++) { | 71 | * Calculate how many pages there are left in the vma, and |
66 | int j = 0; | 72 | * what the pgoff is for our address. |
67 | long thispiece = (remaining < PAGE_SIZE) ? | 73 | */ |
68 | remaining : PAGE_SIZE; | 74 | nr = (vma->vm_end - addr) >> PAGE_SHIFT; |
75 | if (nr > pages) | ||
76 | nr = pages; | ||
69 | 77 | ||
70 | while (j < thispiece) | 78 | pgoff = (addr - vma->vm_start) >> PAGE_SHIFT; |
71 | tmp[j++] = mincore_page(vma, start++); | 79 | pgoff += vma->vm_pgoff; |
72 | 80 | ||
73 | if (copy_to_user(vec + PAGE_SIZE * i, tmp, thispiece)) { | 81 | /* And then we just fill the sucker in.. */ |
74 | error = -EFAULT; | 82 | for (i = 0 ; i < nr; i++, pgoff++) |
75 | break; | 83 | vec[i] = mincore_page(vma, pgoff); |
76 | } | ||
77 | } | ||
78 | 84 | ||
79 | free_page((unsigned long) tmp); | 85 | return nr; |
80 | return error; | ||
81 | } | 86 | } |
82 | 87 | ||
83 | /* | 88 | /* |
@@ -107,82 +112,50 @@ static long mincore_vma(struct vm_area_struct * vma, | |||
107 | asmlinkage long sys_mincore(unsigned long start, size_t len, | 112 | asmlinkage long sys_mincore(unsigned long start, size_t len, |
108 | unsigned char __user * vec) | 113 | unsigned char __user * vec) |
109 | { | 114 | { |
110 | int index = 0; | 115 | long retval; |
111 | unsigned long end, limit; | 116 | unsigned long pages; |
112 | struct vm_area_struct * vma; | 117 | unsigned char *tmp; |
113 | size_t max; | ||
114 | int unmapped_error = 0; | ||
115 | long error; | ||
116 | |||
117 | /* check the arguments */ | ||
118 | if (start & ~PAGE_CACHE_MASK) | ||
119 | goto einval; | ||
120 | |||
121 | limit = TASK_SIZE; | ||
122 | if (start >= limit) | ||
123 | goto enomem; | ||
124 | |||
125 | if (!len) | ||
126 | return 0; | ||
127 | |||
128 | max = limit - start; | ||
129 | len = PAGE_CACHE_ALIGN(len); | ||
130 | if (len > max || !len) | ||
131 | goto enomem; | ||
132 | 118 | ||
133 | end = start + len; | 119 | /* Check the start address: needs to be page-aligned.. */ |
120 | if (start & ~PAGE_CACHE_MASK) | ||
121 | return -EINVAL; | ||
134 | 122 | ||
135 | /* check the output buffer whilst holding the lock */ | 123 | /* ..and we need to be passed a valid user-space range */ |
136 | error = -EFAULT; | 124 | if (!access_ok(VERIFY_READ, (void __user *) start, len)) |
137 | down_read(¤t->mm->mmap_sem); | 125 | return -ENOMEM; |
138 | 126 | ||
139 | if (!access_ok(VERIFY_WRITE, vec, len >> PAGE_SHIFT)) | 127 | /* This also avoids any overflows on PAGE_CACHE_ALIGN */ |
140 | goto out; | 128 | pages = len >> PAGE_SHIFT; |
129 | pages += (len & ~PAGE_MASK) != 0; | ||
141 | 130 | ||
142 | /* | 131 | if (!access_ok(VERIFY_WRITE, vec, pages)) |
143 | * If the interval [start,end) covers some unmapped address | 132 | return -EFAULT; |
144 | * ranges, just ignore them, but return -ENOMEM at the end. | ||
145 | */ | ||
146 | error = 0; | ||
147 | |||
148 | vma = find_vma(current->mm, start); | ||
149 | while (vma) { | ||
150 | /* Here start < vma->vm_end. */ | ||
151 | if (start < vma->vm_start) { | ||
152 | unmapped_error = -ENOMEM; | ||
153 | start = vma->vm_start; | ||
154 | } | ||
155 | 133 | ||
156 | /* Here vma->vm_start <= start < vma->vm_end. */ | 134 | tmp = (void *) __get_free_page(GFP_USER); |
157 | if (end <= vma->vm_end) { | 135 | if (!tmp) |
158 | if (start < end) { | 136 | return -EAGAIN; |
159 | error = mincore_vma(vma, start, end, | 137 | |
160 | &vec[index]); | 138 | retval = 0; |
161 | if (error) | 139 | while (pages) { |
162 | goto out; | 140 | /* |
163 | } | 141 | * Do at most PAGE_SIZE entries per iteration, due to |
164 | error = unmapped_error; | 142 | * the temporary buffer size. |
165 | goto out; | 143 | */ |
144 | down_read(¤t->mm->mmap_sem); | ||
145 | retval = do_mincore(start, tmp, min(pages, PAGE_SIZE)); | ||
146 | up_read(¤t->mm->mmap_sem); | ||
147 | |||
148 | if (retval <= 0) | ||
149 | break; | ||
150 | if (copy_to_user(vec, tmp, retval)) { | ||
151 | retval = -EFAULT; | ||
152 | break; | ||
166 | } | 153 | } |
167 | 154 | pages -= retval; | |
168 | /* Here vma->vm_start <= start < vma->vm_end < end. */ | 155 | vec += retval; |
169 | error = mincore_vma(vma, start, vma->vm_end, &vec[index]); | 156 | start += retval << PAGE_SHIFT; |
170 | if (error) | 157 | retval = 0; |
171 | goto out; | ||
172 | index += (vma->vm_end - start) >> PAGE_CACHE_SHIFT; | ||
173 | start = vma->vm_end; | ||
174 | vma = vma->vm_next; | ||
175 | } | 158 | } |
176 | 159 | free_page((unsigned long) tmp); | |
177 | /* we found a hole in the area queried if we arrive here */ | 160 | return retval; |
178 | error = -ENOMEM; | ||
179 | |||
180 | out: | ||
181 | up_read(¤t->mm->mmap_sem); | ||
182 | return error; | ||
183 | |||
184 | einval: | ||
185 | return -EINVAL; | ||
186 | enomem: | ||
187 | return -ENOMEM; | ||
188 | } | 161 | } |