diff options
Diffstat (limited to 'fs/proc/task_mmu.c')
-rw-r--r-- | fs/proc/task_mmu.c | 225 |
1 files changed, 183 insertions, 42 deletions
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 64e84cadfa3c..c7ef3e48e35b 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -2,10 +2,13 @@ | |||
2 | #include <linux/hugetlb.h> | 2 | #include <linux/hugetlb.h> |
3 | #include <linux/mount.h> | 3 | #include <linux/mount.h> |
4 | #include <linux/seq_file.h> | 4 | #include <linux/seq_file.h> |
5 | #include <linux/highmem.h> | ||
5 | #include <linux/pagemap.h> | 6 | #include <linux/pagemap.h> |
6 | #include <linux/mempolicy.h> | 7 | #include <linux/mempolicy.h> |
8 | |||
7 | #include <asm/elf.h> | 9 | #include <asm/elf.h> |
8 | #include <asm/uaccess.h> | 10 | #include <asm/uaccess.h> |
11 | #include <asm/tlbflush.h> | ||
9 | #include "internal.h" | 12 | #include "internal.h" |
10 | 13 | ||
11 | char *task_mem(struct mm_struct *mm, char *buffer) | 14 | char *task_mem(struct mm_struct *mm, char *buffer) |
@@ -89,49 +92,58 @@ static void pad_len_spaces(struct seq_file *m, int len) | |||
89 | seq_printf(m, "%*c", len, ' '); | 92 | seq_printf(m, "%*c", len, ' '); |
90 | } | 93 | } |
91 | 94 | ||
92 | static int show_map(struct seq_file *m, void *v) | 95 | struct mem_size_stats |
96 | { | ||
97 | unsigned long resident; | ||
98 | unsigned long shared_clean; | ||
99 | unsigned long shared_dirty; | ||
100 | unsigned long private_clean; | ||
101 | unsigned long private_dirty; | ||
102 | }; | ||
103 | |||
104 | static int show_map_internal(struct seq_file *m, void *v, struct mem_size_stats *mss) | ||
93 | { | 105 | { |
94 | struct task_struct *task = m->private; | 106 | struct task_struct *task = m->private; |
95 | struct vm_area_struct *map = v; | 107 | struct vm_area_struct *vma = v; |
96 | struct mm_struct *mm = map->vm_mm; | 108 | struct mm_struct *mm = vma->vm_mm; |
97 | struct file *file = map->vm_file; | 109 | struct file *file = vma->vm_file; |
98 | int flags = map->vm_flags; | 110 | int flags = vma->vm_flags; |
99 | unsigned long ino = 0; | 111 | unsigned long ino = 0; |
100 | dev_t dev = 0; | 112 | dev_t dev = 0; |
101 | int len; | 113 | int len; |
102 | 114 | ||
103 | if (file) { | 115 | if (file) { |
104 | struct inode *inode = map->vm_file->f_dentry->d_inode; | 116 | struct inode *inode = vma->vm_file->f_dentry->d_inode; |
105 | dev = inode->i_sb->s_dev; | 117 | dev = inode->i_sb->s_dev; |
106 | ino = inode->i_ino; | 118 | ino = inode->i_ino; |
107 | } | 119 | } |
108 | 120 | ||
109 | seq_printf(m, "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu %n", | 121 | seq_printf(m, "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu %n", |
110 | map->vm_start, | 122 | vma->vm_start, |
111 | map->vm_end, | 123 | vma->vm_end, |
112 | flags & VM_READ ? 'r' : '-', | 124 | flags & VM_READ ? 'r' : '-', |
113 | flags & VM_WRITE ? 'w' : '-', | 125 | flags & VM_WRITE ? 'w' : '-', |
114 | flags & VM_EXEC ? 'x' : '-', | 126 | flags & VM_EXEC ? 'x' : '-', |
115 | flags & VM_MAYSHARE ? 's' : 'p', | 127 | flags & VM_MAYSHARE ? 's' : 'p', |
116 | map->vm_pgoff << PAGE_SHIFT, | 128 | vma->vm_pgoff << PAGE_SHIFT, |
117 | MAJOR(dev), MINOR(dev), ino, &len); | 129 | MAJOR(dev), MINOR(dev), ino, &len); |
118 | 130 | ||
119 | /* | 131 | /* |
120 | * Print the dentry name for named mappings, and a | 132 | * Print the dentry name for named mappings, and a |
121 | * special [heap] marker for the heap: | 133 | * special [heap] marker for the heap: |
122 | */ | 134 | */ |
123 | if (map->vm_file) { | 135 | if (file) { |
124 | pad_len_spaces(m, len); | 136 | pad_len_spaces(m, len); |
125 | seq_path(m, file->f_vfsmnt, file->f_dentry, ""); | 137 | seq_path(m, file->f_vfsmnt, file->f_dentry, "\n"); |
126 | } else { | 138 | } else { |
127 | if (mm) { | 139 | if (mm) { |
128 | if (map->vm_start <= mm->start_brk && | 140 | if (vma->vm_start <= mm->start_brk && |
129 | map->vm_end >= mm->brk) { | 141 | vma->vm_end >= mm->brk) { |
130 | pad_len_spaces(m, len); | 142 | pad_len_spaces(m, len); |
131 | seq_puts(m, "[heap]"); | 143 | seq_puts(m, "[heap]"); |
132 | } else { | 144 | } else { |
133 | if (map->vm_start <= mm->start_stack && | 145 | if (vma->vm_start <= mm->start_stack && |
134 | map->vm_end >= mm->start_stack) { | 146 | vma->vm_end >= mm->start_stack) { |
135 | 147 | ||
136 | pad_len_spaces(m, len); | 148 | pad_len_spaces(m, len); |
137 | seq_puts(m, "[stack]"); | 149 | seq_puts(m, "[stack]"); |
@@ -143,24 +155,146 @@ static int show_map(struct seq_file *m, void *v) | |||
143 | } | 155 | } |
144 | } | 156 | } |
145 | seq_putc(m, '\n'); | 157 | seq_putc(m, '\n'); |
146 | if (m->count < m->size) /* map is copied successfully */ | 158 | |
147 | m->version = (map != get_gate_vma(task))? map->vm_start: 0; | 159 | if (mss) |
160 | seq_printf(m, | ||
161 | "Size: %8lu kB\n" | ||
162 | "Rss: %8lu kB\n" | ||
163 | "Shared_Clean: %8lu kB\n" | ||
164 | "Shared_Dirty: %8lu kB\n" | ||
165 | "Private_Clean: %8lu kB\n" | ||
166 | "Private_Dirty: %8lu kB\n", | ||
167 | (vma->vm_end - vma->vm_start) >> 10, | ||
168 | mss->resident >> 10, | ||
169 | mss->shared_clean >> 10, | ||
170 | mss->shared_dirty >> 10, | ||
171 | mss->private_clean >> 10, | ||
172 | mss->private_dirty >> 10); | ||
173 | |||
174 | if (m->count < m->size) /* vma is copied successfully */ | ||
175 | m->version = (vma != get_gate_vma(task))? vma->vm_start: 0; | ||
148 | return 0; | 176 | return 0; |
149 | } | 177 | } |
150 | 178 | ||
179 | static int show_map(struct seq_file *m, void *v) | ||
180 | { | ||
181 | return show_map_internal(m, v, 0); | ||
182 | } | ||
183 | |||
184 | static void smaps_pte_range(struct vm_area_struct *vma, pmd_t *pmd, | ||
185 | unsigned long addr, unsigned long end, | ||
186 | struct mem_size_stats *mss) | ||
187 | { | ||
188 | pte_t *pte, ptent; | ||
189 | unsigned long pfn; | ||
190 | struct page *page; | ||
191 | |||
192 | pte = pte_offset_map(pmd, addr); | ||
193 | do { | ||
194 | ptent = *pte; | ||
195 | if (pte_none(ptent) || !pte_present(ptent)) | ||
196 | continue; | ||
197 | |||
198 | mss->resident += PAGE_SIZE; | ||
199 | pfn = pte_pfn(ptent); | ||
200 | if (!pfn_valid(pfn)) | ||
201 | continue; | ||
202 | |||
203 | page = pfn_to_page(pfn); | ||
204 | if (page_count(page) >= 2) { | ||
205 | if (pte_dirty(ptent)) | ||
206 | mss->shared_dirty += PAGE_SIZE; | ||
207 | else | ||
208 | mss->shared_clean += PAGE_SIZE; | ||
209 | } else { | ||
210 | if (pte_dirty(ptent)) | ||
211 | mss->private_dirty += PAGE_SIZE; | ||
212 | else | ||
213 | mss->private_clean += PAGE_SIZE; | ||
214 | } | ||
215 | } while (pte++, addr += PAGE_SIZE, addr != end); | ||
216 | pte_unmap(pte - 1); | ||
217 | cond_resched_lock(&vma->vm_mm->page_table_lock); | ||
218 | } | ||
219 | |||
220 | static inline void smaps_pmd_range(struct vm_area_struct *vma, pud_t *pud, | ||
221 | unsigned long addr, unsigned long end, | ||
222 | struct mem_size_stats *mss) | ||
223 | { | ||
224 | pmd_t *pmd; | ||
225 | unsigned long next; | ||
226 | |||
227 | pmd = pmd_offset(pud, addr); | ||
228 | do { | ||
229 | next = pmd_addr_end(addr, end); | ||
230 | if (pmd_none_or_clear_bad(pmd)) | ||
231 | continue; | ||
232 | smaps_pte_range(vma, pmd, addr, next, mss); | ||
233 | } while (pmd++, addr = next, addr != end); | ||
234 | } | ||
235 | |||
236 | static inline void smaps_pud_range(struct vm_area_struct *vma, pgd_t *pgd, | ||
237 | unsigned long addr, unsigned long end, | ||
238 | struct mem_size_stats *mss) | ||
239 | { | ||
240 | pud_t *pud; | ||
241 | unsigned long next; | ||
242 | |||
243 | pud = pud_offset(pgd, addr); | ||
244 | do { | ||
245 | next = pud_addr_end(addr, end); | ||
246 | if (pud_none_or_clear_bad(pud)) | ||
247 | continue; | ||
248 | smaps_pmd_range(vma, pud, addr, next, mss); | ||
249 | } while (pud++, addr = next, addr != end); | ||
250 | } | ||
251 | |||
252 | static inline void smaps_pgd_range(struct vm_area_struct *vma, | ||
253 | unsigned long addr, unsigned long end, | ||
254 | struct mem_size_stats *mss) | ||
255 | { | ||
256 | pgd_t *pgd; | ||
257 | unsigned long next; | ||
258 | |||
259 | pgd = pgd_offset(vma->vm_mm, addr); | ||
260 | do { | ||
261 | next = pgd_addr_end(addr, end); | ||
262 | if (pgd_none_or_clear_bad(pgd)) | ||
263 | continue; | ||
264 | smaps_pud_range(vma, pgd, addr, next, mss); | ||
265 | } while (pgd++, addr = next, addr != end); | ||
266 | } | ||
267 | |||
268 | static int show_smap(struct seq_file *m, void *v) | ||
269 | { | ||
270 | struct vm_area_struct *vma = v; | ||
271 | struct mm_struct *mm = vma->vm_mm; | ||
272 | struct mem_size_stats mss; | ||
273 | |||
274 | memset(&mss, 0, sizeof mss); | ||
275 | |||
276 | if (mm) { | ||
277 | spin_lock(&mm->page_table_lock); | ||
278 | smaps_pgd_range(vma, vma->vm_start, vma->vm_end, &mss); | ||
279 | spin_unlock(&mm->page_table_lock); | ||
280 | } | ||
281 | |||
282 | return show_map_internal(m, v, &mss); | ||
283 | } | ||
284 | |||
151 | static void *m_start(struct seq_file *m, loff_t *pos) | 285 | static void *m_start(struct seq_file *m, loff_t *pos) |
152 | { | 286 | { |
153 | struct task_struct *task = m->private; | 287 | struct task_struct *task = m->private; |
154 | unsigned long last_addr = m->version; | 288 | unsigned long last_addr = m->version; |
155 | struct mm_struct *mm; | 289 | struct mm_struct *mm; |
156 | struct vm_area_struct *map, *tail_map; | 290 | struct vm_area_struct *vma, *tail_vma; |
157 | loff_t l = *pos; | 291 | loff_t l = *pos; |
158 | 292 | ||
159 | /* | 293 | /* |
160 | * We remember last_addr rather than next_addr to hit with | 294 | * We remember last_addr rather than next_addr to hit with |
161 | * mmap_cache most of the time. We have zero last_addr at | 295 | * mmap_cache most of the time. We have zero last_addr at |
162 | * the begining and also after lseek. We will have -1 last_addr | 296 | * the beginning and also after lseek. We will have -1 last_addr |
163 | * after the end of the maps. | 297 | * after the end of the vmas. |
164 | */ | 298 | */ |
165 | 299 | ||
166 | if (last_addr == -1UL) | 300 | if (last_addr == -1UL) |
@@ -170,47 +304,47 @@ static void *m_start(struct seq_file *m, loff_t *pos) | |||
170 | if (!mm) | 304 | if (!mm) |
171 | return NULL; | 305 | return NULL; |
172 | 306 | ||
173 | tail_map = get_gate_vma(task); | 307 | tail_vma = get_gate_vma(task); |
174 | down_read(&mm->mmap_sem); | 308 | down_read(&mm->mmap_sem); |
175 | 309 | ||
176 | /* Start with last addr hint */ | 310 | /* Start with last addr hint */ |
177 | if (last_addr && (map = find_vma(mm, last_addr))) { | 311 | if (last_addr && (vma = find_vma(mm, last_addr))) { |
178 | map = map->vm_next; | 312 | vma = vma->vm_next; |
179 | goto out; | 313 | goto out; |
180 | } | 314 | } |
181 | 315 | ||
182 | /* | 316 | /* |
183 | * Check the map index is within the range and do | 317 | * Check the vma index is within the range and do |
184 | * sequential scan until m_index. | 318 | * sequential scan until m_index. |
185 | */ | 319 | */ |
186 | map = NULL; | 320 | vma = NULL; |
187 | if ((unsigned long)l < mm->map_count) { | 321 | if ((unsigned long)l < mm->map_count) { |
188 | map = mm->mmap; | 322 | vma = mm->mmap; |
189 | while (l-- && map) | 323 | while (l-- && vma) |
190 | map = map->vm_next; | 324 | vma = vma->vm_next; |
191 | goto out; | 325 | goto out; |
192 | } | 326 | } |
193 | 327 | ||
194 | if (l != mm->map_count) | 328 | if (l != mm->map_count) |
195 | tail_map = NULL; /* After gate map */ | 329 | tail_vma = NULL; /* After gate vma */ |
196 | 330 | ||
197 | out: | 331 | out: |
198 | if (map) | 332 | if (vma) |
199 | return map; | 333 | return vma; |
200 | 334 | ||
201 | /* End of maps has reached */ | 335 | /* End of vmas has been reached */ |
202 | m->version = (tail_map != NULL)? 0: -1UL; | 336 | m->version = (tail_vma != NULL)? 0: -1UL; |
203 | up_read(&mm->mmap_sem); | 337 | up_read(&mm->mmap_sem); |
204 | mmput(mm); | 338 | mmput(mm); |
205 | return tail_map; | 339 | return tail_vma; |
206 | } | 340 | } |
207 | 341 | ||
208 | static void m_stop(struct seq_file *m, void *v) | 342 | static void m_stop(struct seq_file *m, void *v) |
209 | { | 343 | { |
210 | struct task_struct *task = m->private; | 344 | struct task_struct *task = m->private; |
211 | struct vm_area_struct *map = v; | 345 | struct vm_area_struct *vma = v; |
212 | if (map && map != get_gate_vma(task)) { | 346 | if (vma && vma != get_gate_vma(task)) { |
213 | struct mm_struct *mm = map->vm_mm; | 347 | struct mm_struct *mm = vma->vm_mm; |
214 | up_read(&mm->mmap_sem); | 348 | up_read(&mm->mmap_sem); |
215 | mmput(mm); | 349 | mmput(mm); |
216 | } | 350 | } |
@@ -219,14 +353,14 @@ static void m_stop(struct seq_file *m, void *v) | |||
219 | static void *m_next(struct seq_file *m, void *v, loff_t *pos) | 353 | static void *m_next(struct seq_file *m, void *v, loff_t *pos) |
220 | { | 354 | { |
221 | struct task_struct *task = m->private; | 355 | struct task_struct *task = m->private; |
222 | struct vm_area_struct *map = v; | 356 | struct vm_area_struct *vma = v; |
223 | struct vm_area_struct *tail_map = get_gate_vma(task); | 357 | struct vm_area_struct *tail_vma = get_gate_vma(task); |
224 | 358 | ||
225 | (*pos)++; | 359 | (*pos)++; |
226 | if (map && (map != tail_map) && map->vm_next) | 360 | if (vma && (vma != tail_vma) && vma->vm_next) |
227 | return map->vm_next; | 361 | return vma->vm_next; |
228 | m_stop(m, v); | 362 | m_stop(m, v); |
229 | return (map != tail_map)? tail_map: NULL; | 363 | return (vma != tail_vma)? tail_vma: NULL; |
230 | } | 364 | } |
231 | 365 | ||
232 | struct seq_operations proc_pid_maps_op = { | 366 | struct seq_operations proc_pid_maps_op = { |
@@ -236,6 +370,13 @@ struct seq_operations proc_pid_maps_op = { | |||
236 | .show = show_map | 370 | .show = show_map |
237 | }; | 371 | }; |
238 | 372 | ||
373 | struct seq_operations proc_pid_smaps_op = { | ||
374 | .start = m_start, | ||
375 | .next = m_next, | ||
376 | .stop = m_stop, | ||
377 | .show = show_smap | ||
378 | }; | ||
379 | |||
239 | #ifdef CONFIG_NUMA | 380 | #ifdef CONFIG_NUMA |
240 | 381 | ||
241 | struct numa_maps { | 382 | struct numa_maps { |