diff options
author | Jeff Garzik <jgarzik@pobox.com> | 2005-09-08 05:43:49 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-09-08 05:43:49 -0400 |
commit | 1d6ae775d7a948c9575658eb41184fd2e506c0df (patch) | |
tree | 8128a28e89d82f13bb8e3a2160382240c66e2816 /fs/proc | |
parent | 739cdbf1d8f0739b80035b80d69d871e33749b86 (diff) | |
parent | caf39e87cc1182f7dae84eefc43ca14d54c78ef9 (diff) |
Merge /spare/repo/linux-2.6/
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/base.c | 159 | ||||
-rw-r--r-- | fs/proc/generic.c | 13 | ||||
-rw-r--r-- | fs/proc/task_mmu.c | 357 |
3 files changed, 443 insertions, 86 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 491f2d9f89ac..84751f3f52d5 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -11,6 +11,40 @@ | |||
11 | * go into icache. We cache the reference to task_struct upon lookup too. | 11 | * go into icache. We cache the reference to task_struct upon lookup too. |
12 | * Eventually it should become a filesystem in its own. We don't use the | 12 | * Eventually it should become a filesystem in its own. We don't use the |
13 | * rest of procfs anymore. | 13 | * rest of procfs anymore. |
14 | * | ||
15 | * | ||
16 | * Changelog: | ||
17 | * 17-Jan-2005 | ||
18 | * Allan Bezerra | ||
19 | * Bruna Moreira <bruna.moreira@indt.org.br> | ||
20 | * Edjard Mota <edjard.mota@indt.org.br> | ||
21 | * Ilias Biris <ilias.biris@indt.org.br> | ||
22 | * Mauricio Lin <mauricio.lin@indt.org.br> | ||
23 | * | ||
24 | * Embedded Linux Lab - 10LE Instituto Nokia de Tecnologia - INdT | ||
25 | * | ||
26 | * A new process specific entry (smaps) included in /proc. It shows the | ||
27 | * size of rss for each memory area. The maps entry lacks information | ||
28 | * about physical memory size (rss) for each mapped file, i.e., | ||
29 | * rss information for executables and library files. | ||
30 | * This additional information is useful for any tools that need to know | ||
31 | * about physical memory consumption for a process specific library. | ||
32 | * | ||
33 | * Changelog: | ||
34 | * 21-Feb-2005 | ||
35 | * Embedded Linux Lab - 10LE Instituto Nokia de Tecnologia - INdT | ||
36 | * Pud inclusion in the page table walking. | ||
37 | * | ||
38 | * ChangeLog: | ||
39 | * 10-Mar-2005 | ||
40 | * 10LE Instituto Nokia de Tecnologia - INdT: | ||
41 | * A better way to walks through the page table as suggested by Hugh Dickins. | ||
42 | * | ||
43 | * Simo Piiroinen <simo.piiroinen@nokia.com>: | ||
44 | * Smaps information related to shared, private, clean and dirty pages. | ||
45 | * | ||
46 | * Paul Mundt <paul.mundt@nokia.com>: | ||
47 | * Overall revision about smaps. | ||
14 | */ | 48 | */ |
15 | 49 | ||
16 | #include <asm/uaccess.h> | 50 | #include <asm/uaccess.h> |
@@ -65,8 +99,10 @@ enum pid_directory_inos { | |||
65 | PROC_TGID_STAT, | 99 | PROC_TGID_STAT, |
66 | PROC_TGID_STATM, | 100 | PROC_TGID_STATM, |
67 | PROC_TGID_MAPS, | 101 | PROC_TGID_MAPS, |
102 | PROC_TGID_NUMA_MAPS, | ||
68 | PROC_TGID_MOUNTS, | 103 | PROC_TGID_MOUNTS, |
69 | PROC_TGID_WCHAN, | 104 | PROC_TGID_WCHAN, |
105 | PROC_TGID_SMAPS, | ||
70 | #ifdef CONFIG_SCHEDSTATS | 106 | #ifdef CONFIG_SCHEDSTATS |
71 | PROC_TGID_SCHEDSTAT, | 107 | PROC_TGID_SCHEDSTAT, |
72 | #endif | 108 | #endif |
@@ -83,7 +119,6 @@ enum pid_directory_inos { | |||
83 | #ifdef CONFIG_AUDITSYSCALL | 119 | #ifdef CONFIG_AUDITSYSCALL |
84 | PROC_TGID_LOGINUID, | 120 | PROC_TGID_LOGINUID, |
85 | #endif | 121 | #endif |
86 | PROC_TGID_FD_DIR, | ||
87 | PROC_TGID_OOM_SCORE, | 122 | PROC_TGID_OOM_SCORE, |
88 | PROC_TGID_OOM_ADJUST, | 123 | PROC_TGID_OOM_ADJUST, |
89 | PROC_TID_INO, | 124 | PROC_TID_INO, |
@@ -102,8 +137,10 @@ enum pid_directory_inos { | |||
102 | PROC_TID_STAT, | 137 | PROC_TID_STAT, |
103 | PROC_TID_STATM, | 138 | PROC_TID_STATM, |
104 | PROC_TID_MAPS, | 139 | PROC_TID_MAPS, |
140 | PROC_TID_NUMA_MAPS, | ||
105 | PROC_TID_MOUNTS, | 141 | PROC_TID_MOUNTS, |
106 | PROC_TID_WCHAN, | 142 | PROC_TID_WCHAN, |
143 | PROC_TID_SMAPS, | ||
107 | #ifdef CONFIG_SCHEDSTATS | 144 | #ifdef CONFIG_SCHEDSTATS |
108 | PROC_TID_SCHEDSTAT, | 145 | PROC_TID_SCHEDSTAT, |
109 | #endif | 146 | #endif |
@@ -120,9 +157,11 @@ enum pid_directory_inos { | |||
120 | #ifdef CONFIG_AUDITSYSCALL | 157 | #ifdef CONFIG_AUDITSYSCALL |
121 | PROC_TID_LOGINUID, | 158 | PROC_TID_LOGINUID, |
122 | #endif | 159 | #endif |
123 | PROC_TID_FD_DIR = 0x8000, /* 0x8000-0xffff */ | ||
124 | PROC_TID_OOM_SCORE, | 160 | PROC_TID_OOM_SCORE, |
125 | PROC_TID_OOM_ADJUST, | 161 | PROC_TID_OOM_ADJUST, |
162 | |||
163 | /* Add new entries before this */ | ||
164 | PROC_TID_FD_DIR = 0x8000, /* 0x8000-0xffff */ | ||
126 | }; | 165 | }; |
127 | 166 | ||
128 | struct pid_entry { | 167 | struct pid_entry { |
@@ -144,6 +183,9 @@ static struct pid_entry tgid_base_stuff[] = { | |||
144 | E(PROC_TGID_STAT, "stat", S_IFREG|S_IRUGO), | 183 | E(PROC_TGID_STAT, "stat", S_IFREG|S_IRUGO), |
145 | E(PROC_TGID_STATM, "statm", S_IFREG|S_IRUGO), | 184 | E(PROC_TGID_STATM, "statm", S_IFREG|S_IRUGO), |
146 | E(PROC_TGID_MAPS, "maps", S_IFREG|S_IRUGO), | 185 | E(PROC_TGID_MAPS, "maps", S_IFREG|S_IRUGO), |
186 | #ifdef CONFIG_NUMA | ||
187 | E(PROC_TGID_NUMA_MAPS, "numa_maps", S_IFREG|S_IRUGO), | ||
188 | #endif | ||
147 | E(PROC_TGID_MEM, "mem", S_IFREG|S_IRUSR|S_IWUSR), | 189 | E(PROC_TGID_MEM, "mem", S_IFREG|S_IRUSR|S_IWUSR), |
148 | #ifdef CONFIG_SECCOMP | 190 | #ifdef CONFIG_SECCOMP |
149 | E(PROC_TGID_SECCOMP, "seccomp", S_IFREG|S_IRUSR|S_IWUSR), | 191 | E(PROC_TGID_SECCOMP, "seccomp", S_IFREG|S_IRUSR|S_IWUSR), |
@@ -152,6 +194,7 @@ static struct pid_entry tgid_base_stuff[] = { | |||
152 | E(PROC_TGID_ROOT, "root", S_IFLNK|S_IRWXUGO), | 194 | E(PROC_TGID_ROOT, "root", S_IFLNK|S_IRWXUGO), |
153 | E(PROC_TGID_EXE, "exe", S_IFLNK|S_IRWXUGO), | 195 | E(PROC_TGID_EXE, "exe", S_IFLNK|S_IRWXUGO), |
154 | E(PROC_TGID_MOUNTS, "mounts", S_IFREG|S_IRUGO), | 196 | E(PROC_TGID_MOUNTS, "mounts", S_IFREG|S_IRUGO), |
197 | E(PROC_TGID_SMAPS, "smaps", S_IFREG|S_IRUGO), | ||
155 | #ifdef CONFIG_SECURITY | 198 | #ifdef CONFIG_SECURITY |
156 | E(PROC_TGID_ATTR, "attr", S_IFDIR|S_IRUGO|S_IXUGO), | 199 | E(PROC_TGID_ATTR, "attr", S_IFDIR|S_IRUGO|S_IXUGO), |
157 | #endif | 200 | #endif |
@@ -180,6 +223,9 @@ static struct pid_entry tid_base_stuff[] = { | |||
180 | E(PROC_TID_STAT, "stat", S_IFREG|S_IRUGO), | 223 | E(PROC_TID_STAT, "stat", S_IFREG|S_IRUGO), |
181 | E(PROC_TID_STATM, "statm", S_IFREG|S_IRUGO), | 224 | E(PROC_TID_STATM, "statm", S_IFREG|S_IRUGO), |
182 | E(PROC_TID_MAPS, "maps", S_IFREG|S_IRUGO), | 225 | E(PROC_TID_MAPS, "maps", S_IFREG|S_IRUGO), |
226 | #ifdef CONFIG_NUMA | ||
227 | E(PROC_TID_NUMA_MAPS, "numa_maps", S_IFREG|S_IRUGO), | ||
228 | #endif | ||
183 | E(PROC_TID_MEM, "mem", S_IFREG|S_IRUSR|S_IWUSR), | 229 | E(PROC_TID_MEM, "mem", S_IFREG|S_IRUSR|S_IWUSR), |
184 | #ifdef CONFIG_SECCOMP | 230 | #ifdef CONFIG_SECCOMP |
185 | E(PROC_TID_SECCOMP, "seccomp", S_IFREG|S_IRUSR|S_IWUSR), | 231 | E(PROC_TID_SECCOMP, "seccomp", S_IFREG|S_IRUSR|S_IWUSR), |
@@ -188,6 +234,7 @@ static struct pid_entry tid_base_stuff[] = { | |||
188 | E(PROC_TID_ROOT, "root", S_IFLNK|S_IRWXUGO), | 234 | E(PROC_TID_ROOT, "root", S_IFLNK|S_IRWXUGO), |
189 | E(PROC_TID_EXE, "exe", S_IFLNK|S_IRWXUGO), | 235 | E(PROC_TID_EXE, "exe", S_IFLNK|S_IRWXUGO), |
190 | E(PROC_TID_MOUNTS, "mounts", S_IFREG|S_IRUGO), | 236 | E(PROC_TID_MOUNTS, "mounts", S_IFREG|S_IRUGO), |
237 | E(PROC_TID_SMAPS, "smaps", S_IFREG|S_IRUGO), | ||
191 | #ifdef CONFIG_SECURITY | 238 | #ifdef CONFIG_SECURITY |
192 | E(PROC_TID_ATTR, "attr", S_IFDIR|S_IRUGO|S_IXUGO), | 239 | E(PROC_TID_ATTR, "attr", S_IFDIR|S_IRUGO|S_IXUGO), |
193 | #endif | 240 | #endif |
@@ -251,15 +298,21 @@ static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsm | |||
251 | return -ENOENT; | 298 | return -ENOENT; |
252 | } | 299 | } |
253 | 300 | ||
254 | static int proc_cwd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt) | 301 | static struct fs_struct *get_fs_struct(struct task_struct *task) |
255 | { | 302 | { |
256 | struct fs_struct *fs; | 303 | struct fs_struct *fs; |
257 | int result = -ENOENT; | 304 | task_lock(task); |
258 | task_lock(proc_task(inode)); | 305 | fs = task->fs; |
259 | fs = proc_task(inode)->fs; | ||
260 | if(fs) | 306 | if(fs) |
261 | atomic_inc(&fs->count); | 307 | atomic_inc(&fs->count); |
262 | task_unlock(proc_task(inode)); | 308 | task_unlock(task); |
309 | return fs; | ||
310 | } | ||
311 | |||
312 | static int proc_cwd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt) | ||
313 | { | ||
314 | struct fs_struct *fs = get_fs_struct(proc_task(inode)); | ||
315 | int result = -ENOENT; | ||
263 | if (fs) { | 316 | if (fs) { |
264 | read_lock(&fs->lock); | 317 | read_lock(&fs->lock); |
265 | *mnt = mntget(fs->pwdmnt); | 318 | *mnt = mntget(fs->pwdmnt); |
@@ -273,13 +326,8 @@ static int proc_cwd_link(struct inode *inode, struct dentry **dentry, struct vfs | |||
273 | 326 | ||
274 | static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt) | 327 | static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt) |
275 | { | 328 | { |
276 | struct fs_struct *fs; | 329 | struct fs_struct *fs = get_fs_struct(proc_task(inode)); |
277 | int result = -ENOENT; | 330 | int result = -ENOENT; |
278 | task_lock(proc_task(inode)); | ||
279 | fs = proc_task(inode)->fs; | ||
280 | if(fs) | ||
281 | atomic_inc(&fs->count); | ||
282 | task_unlock(proc_task(inode)); | ||
283 | if (fs) { | 331 | if (fs) { |
284 | read_lock(&fs->lock); | 332 | read_lock(&fs->lock); |
285 | *mnt = mntget(fs->rootmnt); | 333 | *mnt = mntget(fs->rootmnt); |
@@ -298,33 +346,6 @@ static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vf | |||
298 | (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \ | 346 | (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \ |
299 | security_ptrace(current,task) == 0)) | 347 | security_ptrace(current,task) == 0)) |
300 | 348 | ||
301 | static int may_ptrace_attach(struct task_struct *task) | ||
302 | { | ||
303 | int retval = 0; | ||
304 | |||
305 | task_lock(task); | ||
306 | |||
307 | if (!task->mm) | ||
308 | goto out; | ||
309 | if (((current->uid != task->euid) || | ||
310 | (current->uid != task->suid) || | ||
311 | (current->uid != task->uid) || | ||
312 | (current->gid != task->egid) || | ||
313 | (current->gid != task->sgid) || | ||
314 | (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) | ||
315 | goto out; | ||
316 | rmb(); | ||
317 | if (task->mm->dumpable != 1 && !capable(CAP_SYS_PTRACE)) | ||
318 | goto out; | ||
319 | if (security_ptrace(current, task)) | ||
320 | goto out; | ||
321 | |||
322 | retval = 1; | ||
323 | out: | ||
324 | task_unlock(task); | ||
325 | return retval; | ||
326 | } | ||
327 | |||
328 | static int proc_pid_environ(struct task_struct *task, char * buffer) | 349 | static int proc_pid_environ(struct task_struct *task, char * buffer) |
329 | { | 350 | { |
330 | int res = 0; | 351 | int res = 0; |
@@ -334,7 +355,7 @@ static int proc_pid_environ(struct task_struct *task, char * buffer) | |||
334 | if (len > PAGE_SIZE) | 355 | if (len > PAGE_SIZE) |
335 | len = PAGE_SIZE; | 356 | len = PAGE_SIZE; |
336 | res = access_process_vm(task, mm->env_start, buffer, len, 0); | 357 | res = access_process_vm(task, mm->env_start, buffer, len, 0); |
337 | if (!may_ptrace_attach(task)) | 358 | if (!ptrace_may_attach(task)) |
338 | res = -ESRCH; | 359 | res = -ESRCH; |
339 | mmput(mm); | 360 | mmput(mm); |
340 | } | 361 | } |
@@ -515,6 +536,46 @@ static struct file_operations proc_maps_operations = { | |||
515 | .release = seq_release, | 536 | .release = seq_release, |
516 | }; | 537 | }; |
517 | 538 | ||
539 | #ifdef CONFIG_NUMA | ||
540 | extern struct seq_operations proc_pid_numa_maps_op; | ||
541 | static int numa_maps_open(struct inode *inode, struct file *file) | ||
542 | { | ||
543 | struct task_struct *task = proc_task(inode); | ||
544 | int ret = seq_open(file, &proc_pid_numa_maps_op); | ||
545 | if (!ret) { | ||
546 | struct seq_file *m = file->private_data; | ||
547 | m->private = task; | ||
548 | } | ||
549 | return ret; | ||
550 | } | ||
551 | |||
552 | static struct file_operations proc_numa_maps_operations = { | ||
553 | .open = numa_maps_open, | ||
554 | .read = seq_read, | ||
555 | .llseek = seq_lseek, | ||
556 | .release = seq_release, | ||
557 | }; | ||
558 | #endif | ||
559 | |||
560 | extern struct seq_operations proc_pid_smaps_op; | ||
561 | static int smaps_open(struct inode *inode, struct file *file) | ||
562 | { | ||
563 | struct task_struct *task = proc_task(inode); | ||
564 | int ret = seq_open(file, &proc_pid_smaps_op); | ||
565 | if (!ret) { | ||
566 | struct seq_file *m = file->private_data; | ||
567 | m->private = task; | ||
568 | } | ||
569 | return ret; | ||
570 | } | ||
571 | |||
572 | static struct file_operations proc_smaps_operations = { | ||
573 | .open = smaps_open, | ||
574 | .read = seq_read, | ||
575 | .llseek = seq_lseek, | ||
576 | .release = seq_release, | ||
577 | }; | ||
578 | |||
518 | extern struct seq_operations mounts_op; | 579 | extern struct seq_operations mounts_op; |
519 | static int mounts_open(struct inode *inode, struct file *file) | 580 | static int mounts_open(struct inode *inode, struct file *file) |
520 | { | 581 | { |
@@ -597,7 +658,7 @@ static ssize_t mem_read(struct file * file, char __user * buf, | |||
597 | int ret = -ESRCH; | 658 | int ret = -ESRCH; |
598 | struct mm_struct *mm; | 659 | struct mm_struct *mm; |
599 | 660 | ||
600 | if (!MAY_PTRACE(task) || !may_ptrace_attach(task)) | 661 | if (!MAY_PTRACE(task) || !ptrace_may_attach(task)) |
601 | goto out; | 662 | goto out; |
602 | 663 | ||
603 | ret = -ENOMEM; | 664 | ret = -ENOMEM; |
@@ -623,7 +684,7 @@ static ssize_t mem_read(struct file * file, char __user * buf, | |||
623 | 684 | ||
624 | this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; | 685 | this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; |
625 | retval = access_process_vm(task, src, page, this_len, 0); | 686 | retval = access_process_vm(task, src, page, this_len, 0); |
626 | if (!retval || !MAY_PTRACE(task) || !may_ptrace_attach(task)) { | 687 | if (!retval || !MAY_PTRACE(task) || !ptrace_may_attach(task)) { |
627 | if (!ret) | 688 | if (!ret) |
628 | ret = -EIO; | 689 | ret = -EIO; |
629 | break; | 690 | break; |
@@ -661,7 +722,7 @@ static ssize_t mem_write(struct file * file, const char * buf, | |||
661 | struct task_struct *task = proc_task(file->f_dentry->d_inode); | 722 | struct task_struct *task = proc_task(file->f_dentry->d_inode); |
662 | unsigned long dst = *ppos; | 723 | unsigned long dst = *ppos; |
663 | 724 | ||
664 | if (!MAY_PTRACE(task) || !may_ptrace_attach(task)) | 725 | if (!MAY_PTRACE(task) || !ptrace_may_attach(task)) |
665 | return -ESRCH; | 726 | return -ESRCH; |
666 | 727 | ||
667 | page = (char *)__get_free_page(GFP_USER); | 728 | page = (char *)__get_free_page(GFP_USER); |
@@ -1524,6 +1585,12 @@ static struct dentry *proc_pident_lookup(struct inode *dir, | |||
1524 | case PROC_TGID_MAPS: | 1585 | case PROC_TGID_MAPS: |
1525 | inode->i_fop = &proc_maps_operations; | 1586 | inode->i_fop = &proc_maps_operations; |
1526 | break; | 1587 | break; |
1588 | #ifdef CONFIG_NUMA | ||
1589 | case PROC_TID_NUMA_MAPS: | ||
1590 | case PROC_TGID_NUMA_MAPS: | ||
1591 | inode->i_fop = &proc_numa_maps_operations; | ||
1592 | break; | ||
1593 | #endif | ||
1527 | case PROC_TID_MEM: | 1594 | case PROC_TID_MEM: |
1528 | case PROC_TGID_MEM: | 1595 | case PROC_TGID_MEM: |
1529 | inode->i_op = &proc_mem_inode_operations; | 1596 | inode->i_op = &proc_mem_inode_operations; |
@@ -1539,6 +1606,10 @@ static struct dentry *proc_pident_lookup(struct inode *dir, | |||
1539 | case PROC_TGID_MOUNTS: | 1606 | case PROC_TGID_MOUNTS: |
1540 | inode->i_fop = &proc_mounts_operations; | 1607 | inode->i_fop = &proc_mounts_operations; |
1541 | break; | 1608 | break; |
1609 | case PROC_TID_SMAPS: | ||
1610 | case PROC_TGID_SMAPS: | ||
1611 | inode->i_fop = &proc_smaps_operations; | ||
1612 | break; | ||
1542 | #ifdef CONFIG_SECURITY | 1613 | #ifdef CONFIG_SECURITY |
1543 | case PROC_TID_ATTR: | 1614 | case PROC_TID_ATTR: |
1544 | inode->i_nlink = 2; | 1615 | inode->i_nlink = 2; |
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index abe8920313fb..8a8c34461d48 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c | |||
@@ -249,6 +249,18 @@ out: | |||
249 | return error; | 249 | return error; |
250 | } | 250 | } |
251 | 251 | ||
252 | static int proc_getattr(struct vfsmount *mnt, struct dentry *dentry, | ||
253 | struct kstat *stat) | ||
254 | { | ||
255 | struct inode *inode = dentry->d_inode; | ||
256 | struct proc_dir_entry *de = PROC_I(inode)->pde; | ||
257 | if (de && de->nlink) | ||
258 | inode->i_nlink = de->nlink; | ||
259 | |||
260 | generic_fillattr(inode, stat); | ||
261 | return 0; | ||
262 | } | ||
263 | |||
252 | static struct inode_operations proc_file_inode_operations = { | 264 | static struct inode_operations proc_file_inode_operations = { |
253 | .setattr = proc_notify_change, | 265 | .setattr = proc_notify_change, |
254 | }; | 266 | }; |
@@ -475,6 +487,7 @@ static struct file_operations proc_dir_operations = { | |||
475 | */ | 487 | */ |
476 | static struct inode_operations proc_dir_inode_operations = { | 488 | static struct inode_operations proc_dir_inode_operations = { |
477 | .lookup = proc_lookup, | 489 | .lookup = proc_lookup, |
490 | .getattr = proc_getattr, | ||
478 | .setattr = proc_notify_change, | 491 | .setattr = proc_notify_change, |
479 | }; | 492 | }; |
480 | 493 | ||
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 28b4a0253a92..c7ef3e48e35b 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -2,8 +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> | ||
6 | #include <linux/pagemap.h> | ||
7 | #include <linux/mempolicy.h> | ||
8 | |||
5 | #include <asm/elf.h> | 9 | #include <asm/elf.h> |
6 | #include <asm/uaccess.h> | 10 | #include <asm/uaccess.h> |
11 | #include <asm/tlbflush.h> | ||
7 | #include "internal.h" | 12 | #include "internal.h" |
8 | 13 | ||
9 | char *task_mem(struct mm_struct *mm, char *buffer) | 14 | char *task_mem(struct mm_struct *mm, char *buffer) |
@@ -87,49 +92,58 @@ static void pad_len_spaces(struct seq_file *m, int len) | |||
87 | seq_printf(m, "%*c", len, ' '); | 92 | seq_printf(m, "%*c", len, ' '); |
88 | } | 93 | } |
89 | 94 | ||
90 | 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) | ||
91 | { | 105 | { |
92 | struct task_struct *task = m->private; | 106 | struct task_struct *task = m->private; |
93 | struct vm_area_struct *map = v; | 107 | struct vm_area_struct *vma = v; |
94 | struct mm_struct *mm = map->vm_mm; | 108 | struct mm_struct *mm = vma->vm_mm; |
95 | struct file *file = map->vm_file; | 109 | struct file *file = vma->vm_file; |
96 | int flags = map->vm_flags; | 110 | int flags = vma->vm_flags; |
97 | unsigned long ino = 0; | 111 | unsigned long ino = 0; |
98 | dev_t dev = 0; | 112 | dev_t dev = 0; |
99 | int len; | 113 | int len; |
100 | 114 | ||
101 | if (file) { | 115 | if (file) { |
102 | struct inode *inode = map->vm_file->f_dentry->d_inode; | 116 | struct inode *inode = vma->vm_file->f_dentry->d_inode; |
103 | dev = inode->i_sb->s_dev; | 117 | dev = inode->i_sb->s_dev; |
104 | ino = inode->i_ino; | 118 | ino = inode->i_ino; |
105 | } | 119 | } |
106 | 120 | ||
107 | 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", |
108 | map->vm_start, | 122 | vma->vm_start, |
109 | map->vm_end, | 123 | vma->vm_end, |
110 | flags & VM_READ ? 'r' : '-', | 124 | flags & VM_READ ? 'r' : '-', |
111 | flags & VM_WRITE ? 'w' : '-', | 125 | flags & VM_WRITE ? 'w' : '-', |
112 | flags & VM_EXEC ? 'x' : '-', | 126 | flags & VM_EXEC ? 'x' : '-', |
113 | flags & VM_MAYSHARE ? 's' : 'p', | 127 | flags & VM_MAYSHARE ? 's' : 'p', |
114 | map->vm_pgoff << PAGE_SHIFT, | 128 | vma->vm_pgoff << PAGE_SHIFT, |
115 | MAJOR(dev), MINOR(dev), ino, &len); | 129 | MAJOR(dev), MINOR(dev), ino, &len); |
116 | 130 | ||
117 | /* | 131 | /* |
118 | * Print the dentry name for named mappings, and a | 132 | * Print the dentry name for named mappings, and a |
119 | * special [heap] marker for the heap: | 133 | * special [heap] marker for the heap: |
120 | */ | 134 | */ |
121 | if (map->vm_file) { | 135 | if (file) { |
122 | pad_len_spaces(m, len); | 136 | pad_len_spaces(m, len); |
123 | seq_path(m, file->f_vfsmnt, file->f_dentry, ""); | 137 | seq_path(m, file->f_vfsmnt, file->f_dentry, "\n"); |
124 | } else { | 138 | } else { |
125 | if (mm) { | 139 | if (mm) { |
126 | if (map->vm_start <= mm->start_brk && | 140 | if (vma->vm_start <= mm->start_brk && |
127 | map->vm_end >= mm->brk) { | 141 | vma->vm_end >= mm->brk) { |
128 | pad_len_spaces(m, len); | 142 | pad_len_spaces(m, len); |
129 | seq_puts(m, "[heap]"); | 143 | seq_puts(m, "[heap]"); |
130 | } else { | 144 | } else { |
131 | if (map->vm_start <= mm->start_stack && | 145 | if (vma->vm_start <= mm->start_stack && |
132 | map->vm_end >= mm->start_stack) { | 146 | vma->vm_end >= mm->start_stack) { |
133 | 147 | ||
134 | pad_len_spaces(m, len); | 148 | pad_len_spaces(m, len); |
135 | seq_puts(m, "[stack]"); | 149 | seq_puts(m, "[stack]"); |
@@ -141,24 +155,146 @@ static int show_map(struct seq_file *m, void *v) | |||
141 | } | 155 | } |
142 | } | 156 | } |
143 | seq_putc(m, '\n'); | 157 | seq_putc(m, '\n'); |
144 | if (m->count < m->size) /* map is copied successfully */ | 158 | |
145 | 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; | ||
146 | return 0; | 176 | return 0; |
147 | } | 177 | } |
148 | 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 | |||
149 | static void *m_start(struct seq_file *m, loff_t *pos) | 285 | static void *m_start(struct seq_file *m, loff_t *pos) |
150 | { | 286 | { |
151 | struct task_struct *task = m->private; | 287 | struct task_struct *task = m->private; |
152 | unsigned long last_addr = m->version; | 288 | unsigned long last_addr = m->version; |
153 | struct mm_struct *mm; | 289 | struct mm_struct *mm; |
154 | struct vm_area_struct *map, *tail_map; | 290 | struct vm_area_struct *vma, *tail_vma; |
155 | loff_t l = *pos; | 291 | loff_t l = *pos; |
156 | 292 | ||
157 | /* | 293 | /* |
158 | * We remember last_addr rather than next_addr to hit with | 294 | * We remember last_addr rather than next_addr to hit with |
159 | * 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 |
160 | * 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 |
161 | * after the end of the maps. | 297 | * after the end of the vmas. |
162 | */ | 298 | */ |
163 | 299 | ||
164 | if (last_addr == -1UL) | 300 | if (last_addr == -1UL) |
@@ -168,47 +304,47 @@ static void *m_start(struct seq_file *m, loff_t *pos) | |||
168 | if (!mm) | 304 | if (!mm) |
169 | return NULL; | 305 | return NULL; |
170 | 306 | ||
171 | tail_map = get_gate_vma(task); | 307 | tail_vma = get_gate_vma(task); |
172 | down_read(&mm->mmap_sem); | 308 | down_read(&mm->mmap_sem); |
173 | 309 | ||
174 | /* Start with last addr hint */ | 310 | /* Start with last addr hint */ |
175 | if (last_addr && (map = find_vma(mm, last_addr))) { | 311 | if (last_addr && (vma = find_vma(mm, last_addr))) { |
176 | map = map->vm_next; | 312 | vma = vma->vm_next; |
177 | goto out; | 313 | goto out; |
178 | } | 314 | } |
179 | 315 | ||
180 | /* | 316 | /* |
181 | * Check the map index is within the range and do | 317 | * Check the vma index is within the range and do |
182 | * sequential scan until m_index. | 318 | * sequential scan until m_index. |
183 | */ | 319 | */ |
184 | map = NULL; | 320 | vma = NULL; |
185 | if ((unsigned long)l < mm->map_count) { | 321 | if ((unsigned long)l < mm->map_count) { |
186 | map = mm->mmap; | 322 | vma = mm->mmap; |
187 | while (l-- && map) | 323 | while (l-- && vma) |
188 | map = map->vm_next; | 324 | vma = vma->vm_next; |
189 | goto out; | 325 | goto out; |
190 | } | 326 | } |
191 | 327 | ||
192 | if (l != mm->map_count) | 328 | if (l != mm->map_count) |
193 | tail_map = NULL; /* After gate map */ | 329 | tail_vma = NULL; /* After gate vma */ |
194 | 330 | ||
195 | out: | 331 | out: |
196 | if (map) | 332 | if (vma) |
197 | return map; | 333 | return vma; |
198 | 334 | ||
199 | /* End of maps has reached */ | 335 | /* End of vmas has been reached */ |
200 | m->version = (tail_map != NULL)? 0: -1UL; | 336 | m->version = (tail_vma != NULL)? 0: -1UL; |
201 | up_read(&mm->mmap_sem); | 337 | up_read(&mm->mmap_sem); |
202 | mmput(mm); | 338 | mmput(mm); |
203 | return tail_map; | 339 | return tail_vma; |
204 | } | 340 | } |
205 | 341 | ||
206 | static void m_stop(struct seq_file *m, void *v) | 342 | static void m_stop(struct seq_file *m, void *v) |
207 | { | 343 | { |
208 | struct task_struct *task = m->private; | 344 | struct task_struct *task = m->private; |
209 | struct vm_area_struct *map = v; | 345 | struct vm_area_struct *vma = v; |
210 | if (map && map != get_gate_vma(task)) { | 346 | if (vma && vma != get_gate_vma(task)) { |
211 | struct mm_struct *mm = map->vm_mm; | 347 | struct mm_struct *mm = vma->vm_mm; |
212 | up_read(&mm->mmap_sem); | 348 | up_read(&mm->mmap_sem); |
213 | mmput(mm); | 349 | mmput(mm); |
214 | } | 350 | } |
@@ -217,14 +353,14 @@ static void m_stop(struct seq_file *m, void *v) | |||
217 | 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) |
218 | { | 354 | { |
219 | struct task_struct *task = m->private; | 355 | struct task_struct *task = m->private; |
220 | struct vm_area_struct *map = v; | 356 | struct vm_area_struct *vma = v; |
221 | struct vm_area_struct *tail_map = get_gate_vma(task); | 357 | struct vm_area_struct *tail_vma = get_gate_vma(task); |
222 | 358 | ||
223 | (*pos)++; | 359 | (*pos)++; |
224 | if (map && (map != tail_map) && map->vm_next) | 360 | if (vma && (vma != tail_vma) && vma->vm_next) |
225 | return map->vm_next; | 361 | return vma->vm_next; |
226 | m_stop(m, v); | 362 | m_stop(m, v); |
227 | return (map != tail_map)? tail_map: NULL; | 363 | return (vma != tail_vma)? tail_vma: NULL; |
228 | } | 364 | } |
229 | 365 | ||
230 | struct seq_operations proc_pid_maps_op = { | 366 | struct seq_operations proc_pid_maps_op = { |
@@ -233,3 +369,140 @@ struct seq_operations proc_pid_maps_op = { | |||
233 | .stop = m_stop, | 369 | .stop = m_stop, |
234 | .show = show_map | 370 | .show = show_map |
235 | }; | 371 | }; |
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 | |||
380 | #ifdef CONFIG_NUMA | ||
381 | |||
382 | struct numa_maps { | ||
383 | unsigned long pages; | ||
384 | unsigned long anon; | ||
385 | unsigned long mapped; | ||
386 | unsigned long mapcount_max; | ||
387 | unsigned long node[MAX_NUMNODES]; | ||
388 | }; | ||
389 | |||
390 | /* | ||
391 | * Calculate numa node maps for a vma | ||
392 | */ | ||
393 | static struct numa_maps *get_numa_maps(const struct vm_area_struct *vma) | ||
394 | { | ||
395 | struct page *page; | ||
396 | unsigned long vaddr; | ||
397 | struct mm_struct *mm = vma->vm_mm; | ||
398 | int i; | ||
399 | struct numa_maps *md = kmalloc(sizeof(struct numa_maps), GFP_KERNEL); | ||
400 | |||
401 | if (!md) | ||
402 | return NULL; | ||
403 | md->pages = 0; | ||
404 | md->anon = 0; | ||
405 | md->mapped = 0; | ||
406 | md->mapcount_max = 0; | ||
407 | for_each_node(i) | ||
408 | md->node[i] =0; | ||
409 | |||
410 | spin_lock(&mm->page_table_lock); | ||
411 | for (vaddr = vma->vm_start; vaddr < vma->vm_end; vaddr += PAGE_SIZE) { | ||
412 | page = follow_page(mm, vaddr, 0); | ||
413 | if (page) { | ||
414 | int count = page_mapcount(page); | ||
415 | |||
416 | if (count) | ||
417 | md->mapped++; | ||
418 | if (count > md->mapcount_max) | ||
419 | md->mapcount_max = count; | ||
420 | md->pages++; | ||
421 | if (PageAnon(page)) | ||
422 | md->anon++; | ||
423 | md->node[page_to_nid(page)]++; | ||
424 | } | ||
425 | } | ||
426 | spin_unlock(&mm->page_table_lock); | ||
427 | return md; | ||
428 | } | ||
429 | |||
430 | static int show_numa_map(struct seq_file *m, void *v) | ||
431 | { | ||
432 | struct task_struct *task = m->private; | ||
433 | struct vm_area_struct *vma = v; | ||
434 | struct mempolicy *pol; | ||
435 | struct numa_maps *md; | ||
436 | struct zone **z; | ||
437 | int n; | ||
438 | int first; | ||
439 | |||
440 | if (!vma->vm_mm) | ||
441 | return 0; | ||
442 | |||
443 | md = get_numa_maps(vma); | ||
444 | if (!md) | ||
445 | return 0; | ||
446 | |||
447 | seq_printf(m, "%08lx", vma->vm_start); | ||
448 | pol = get_vma_policy(task, vma, vma->vm_start); | ||
449 | /* Print policy */ | ||
450 | switch (pol->policy) { | ||
451 | case MPOL_PREFERRED: | ||
452 | seq_printf(m, " prefer=%d", pol->v.preferred_node); | ||
453 | break; | ||
454 | case MPOL_BIND: | ||
455 | seq_printf(m, " bind={"); | ||
456 | first = 1; | ||
457 | for (z = pol->v.zonelist->zones; *z; z++) { | ||
458 | |||
459 | if (!first) | ||
460 | seq_putc(m, ','); | ||
461 | else | ||
462 | first = 0; | ||
463 | seq_printf(m, "%d/%s", (*z)->zone_pgdat->node_id, | ||
464 | (*z)->name); | ||
465 | } | ||
466 | seq_putc(m, '}'); | ||
467 | break; | ||
468 | case MPOL_INTERLEAVE: | ||
469 | seq_printf(m, " interleave={"); | ||
470 | first = 1; | ||
471 | for_each_node(n) { | ||
472 | if (test_bit(n, pol->v.nodes)) { | ||
473 | if (!first) | ||
474 | seq_putc(m,','); | ||
475 | else | ||
476 | first = 0; | ||
477 | seq_printf(m, "%d",n); | ||
478 | } | ||
479 | } | ||
480 | seq_putc(m, '}'); | ||
481 | break; | ||
482 | default: | ||
483 | seq_printf(m," default"); | ||
484 | break; | ||
485 | } | ||
486 | seq_printf(m, " MaxRef=%lu Pages=%lu Mapped=%lu", | ||
487 | md->mapcount_max, md->pages, md->mapped); | ||
488 | if (md->anon) | ||
489 | seq_printf(m," Anon=%lu",md->anon); | ||
490 | |||
491 | for_each_online_node(n) { | ||
492 | if (md->node[n]) | ||
493 | seq_printf(m, " N%d=%lu", n, md->node[n]); | ||
494 | } | ||
495 | seq_putc(m, '\n'); | ||
496 | kfree(md); | ||
497 | if (m->count < m->size) /* vma is copied successfully */ | ||
498 | m->version = (vma != get_gate_vma(task)) ? vma->vm_start : 0; | ||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | struct seq_operations proc_pid_numa_maps_op = { | ||
503 | .start = m_start, | ||
504 | .next = m_next, | ||
505 | .stop = m_stop, | ||
506 | .show = show_numa_map | ||
507 | }; | ||
508 | #endif | ||