diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-10 21:34:42 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-10 21:34:42 -0500 |
commit | b6da0076bab5a12afb19312ffee41c95490af2a0 (patch) | |
tree | 52a5675b9c2ff95d88b981d5b9a3822f6073c112 /fs/proc | |
parent | cbfe0de303a55ed96d8831c2d5f56f8131cd6612 (diff) | |
parent | a53b831549141aa060a8b54b76e3a42870d74cc0 (diff) |
Merge branch 'akpm' (patchbomb from Andrew)
Merge first patchbomb from Andrew Morton:
- a few minor cifs fixes
- dma-debug upadtes
- ocfs2
- slab
- about half of MM
- procfs
- kernel/exit.c
- panic.c tweaks
- printk upates
- lib/ updates
- checkpatch updates
- fs/binfmt updates
- the drivers/rtc tree
- nilfs
- kmod fixes
- more kernel/exit.c
- various other misc tweaks and fixes
* emailed patches from Andrew Morton <akpm@linux-foundation.org>: (190 commits)
exit: pidns: fix/update the comments in zap_pid_ns_processes()
exit: pidns: alloc_pid() leaks pid_namespace if child_reaper is exiting
exit: exit_notify: re-use "dead" list to autoreap current
exit: reparent: call forget_original_parent() under tasklist_lock
exit: reparent: avoid find_new_reaper() if no children
exit: reparent: introduce find_alive_thread()
exit: reparent: introduce find_child_reaper()
exit: reparent: document the ->has_child_subreaper checks
exit: reparent: s/while_each_thread/for_each_thread/ in find_new_reaper()
exit: reparent: fix the cross-namespace PR_SET_CHILD_SUBREAPER reparenting
exit: reparent: fix the dead-parent PR_SET_CHILD_SUBREAPER reparenting
exit: proc: don't try to flush /proc/tgid/task/tgid
exit: release_task: fix the comment about group leader accounting
exit: wait: drop tasklist_lock before psig->c* accounting
exit: wait: don't use zombie->real_parent
exit: wait: cleanup the ptrace_reparented() checks
usermodehelper: kill the kmod_thread_locker logic
usermodehelper: don't use CLONE_VFORK for ____call_usermodehelper()
fs/hfs/catalog.c: fix comparison bug in hfs_cat_keycmp
nilfs2: fix the nilfs_iget() vs. nilfs_new_inode() races
...
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/array.c | 47 | ||||
-rw-r--r-- | fs/proc/base.c | 3 | ||||
-rw-r--r-- | fs/proc/generic.c | 163 | ||||
-rw-r--r-- | fs/proc/internal.h | 11 | ||||
-rw-r--r-- | fs/proc/proc_net.c | 1 | ||||
-rw-r--r-- | fs/proc/root.c | 1 | ||||
-rw-r--r-- | fs/proc/task_mmu.c | 104 |
7 files changed, 205 insertions, 125 deletions
diff --git a/fs/proc/array.c b/fs/proc/array.c index cd3653e4f35c..bd117d065b82 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c | |||
@@ -157,20 +157,29 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, | |||
157 | struct user_namespace *user_ns = seq_user_ns(m); | 157 | struct user_namespace *user_ns = seq_user_ns(m); |
158 | struct group_info *group_info; | 158 | struct group_info *group_info; |
159 | int g; | 159 | int g; |
160 | struct fdtable *fdt = NULL; | 160 | struct task_struct *tracer; |
161 | const struct cred *cred; | 161 | const struct cred *cred; |
162 | pid_t ppid, tpid; | 162 | pid_t ppid, tpid = 0, tgid, ngid; |
163 | unsigned int max_fds = 0; | ||
163 | 164 | ||
164 | rcu_read_lock(); | 165 | rcu_read_lock(); |
165 | ppid = pid_alive(p) ? | 166 | ppid = pid_alive(p) ? |
166 | task_tgid_nr_ns(rcu_dereference(p->real_parent), ns) : 0; | 167 | task_tgid_nr_ns(rcu_dereference(p->real_parent), ns) : 0; |
167 | tpid = 0; | 168 | |
168 | if (pid_alive(p)) { | 169 | tracer = ptrace_parent(p); |
169 | struct task_struct *tracer = ptrace_parent(p); | 170 | if (tracer) |
170 | if (tracer) | 171 | tpid = task_pid_nr_ns(tracer, ns); |
171 | tpid = task_pid_nr_ns(tracer, ns); | 172 | |
172 | } | 173 | tgid = task_tgid_nr_ns(p, ns); |
174 | ngid = task_numa_group_id(p); | ||
173 | cred = get_task_cred(p); | 175 | cred = get_task_cred(p); |
176 | |||
177 | task_lock(p); | ||
178 | if (p->files) | ||
179 | max_fds = files_fdtable(p->files)->max_fds; | ||
180 | task_unlock(p); | ||
181 | rcu_read_unlock(); | ||
182 | |||
174 | seq_printf(m, | 183 | seq_printf(m, |
175 | "State:\t%s\n" | 184 | "State:\t%s\n" |
176 | "Tgid:\t%d\n" | 185 | "Tgid:\t%d\n" |
@@ -179,12 +188,10 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, | |||
179 | "PPid:\t%d\n" | 188 | "PPid:\t%d\n" |
180 | "TracerPid:\t%d\n" | 189 | "TracerPid:\t%d\n" |
181 | "Uid:\t%d\t%d\t%d\t%d\n" | 190 | "Uid:\t%d\t%d\t%d\t%d\n" |
182 | "Gid:\t%d\t%d\t%d\t%d\n", | 191 | "Gid:\t%d\t%d\t%d\t%d\n" |
192 | "FDSize:\t%d\nGroups:\t", | ||
183 | get_task_state(p), | 193 | get_task_state(p), |
184 | task_tgid_nr_ns(p, ns), | 194 | tgid, ngid, pid_nr_ns(pid, ns), ppid, tpid, |
185 | task_numa_group_id(p), | ||
186 | pid_nr_ns(pid, ns), | ||
187 | ppid, tpid, | ||
188 | from_kuid_munged(user_ns, cred->uid), | 195 | from_kuid_munged(user_ns, cred->uid), |
189 | from_kuid_munged(user_ns, cred->euid), | 196 | from_kuid_munged(user_ns, cred->euid), |
190 | from_kuid_munged(user_ns, cred->suid), | 197 | from_kuid_munged(user_ns, cred->suid), |
@@ -192,20 +199,10 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, | |||
192 | from_kgid_munged(user_ns, cred->gid), | 199 | from_kgid_munged(user_ns, cred->gid), |
193 | from_kgid_munged(user_ns, cred->egid), | 200 | from_kgid_munged(user_ns, cred->egid), |
194 | from_kgid_munged(user_ns, cred->sgid), | 201 | from_kgid_munged(user_ns, cred->sgid), |
195 | from_kgid_munged(user_ns, cred->fsgid)); | 202 | from_kgid_munged(user_ns, cred->fsgid), |
196 | 203 | max_fds); | |
197 | task_lock(p); | ||
198 | if (p->files) | ||
199 | fdt = files_fdtable(p->files); | ||
200 | seq_printf(m, | ||
201 | "FDSize:\t%d\n" | ||
202 | "Groups:\t", | ||
203 | fdt ? fdt->max_fds : 0); | ||
204 | rcu_read_unlock(); | ||
205 | 204 | ||
206 | group_info = cred->group_info; | 205 | group_info = cred->group_info; |
207 | task_unlock(p); | ||
208 | |||
209 | for (g = 0; g < group_info->ngroups; g++) | 206 | for (g = 0; g < group_info->ngroups; g++) |
210 | seq_printf(m, "%d ", | 207 | seq_printf(m, "%d ", |
211 | from_kgid_munged(user_ns, GROUP_AT(group_info, g))); | 208 | from_kgid_munged(user_ns, GROUP_AT(group_info, g))); |
diff --git a/fs/proc/base.c b/fs/proc/base.c index 64891f3e41bd..590aeda5af12 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -2618,6 +2618,9 @@ static void proc_flush_task_mnt(struct vfsmount *mnt, pid_t pid, pid_t tgid) | |||
2618 | dput(dentry); | 2618 | dput(dentry); |
2619 | } | 2619 | } |
2620 | 2620 | ||
2621 | if (pid == tgid) | ||
2622 | return; | ||
2623 | |||
2621 | name.name = buf; | 2624 | name.name = buf; |
2622 | name.len = snprintf(buf, sizeof(buf), "%d", tgid); | 2625 | name.len = snprintf(buf, sizeof(buf), "%d", tgid); |
2623 | leader = d_hash_and_lookup(mnt->mnt_root, &name); | 2626 | leader = d_hash_and_lookup(mnt->mnt_root, &name); |
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 317b72641ebf..7fea13229f33 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c | |||
@@ -31,9 +31,73 @@ static DEFINE_SPINLOCK(proc_subdir_lock); | |||
31 | 31 | ||
32 | static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de) | 32 | static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de) |
33 | { | 33 | { |
34 | if (de->namelen != len) | 34 | if (len < de->namelen) |
35 | return 0; | 35 | return -1; |
36 | return !memcmp(name, de->name, len); | 36 | if (len > de->namelen) |
37 | return 1; | ||
38 | |||
39 | return memcmp(name, de->name, len); | ||
40 | } | ||
41 | |||
42 | static struct proc_dir_entry *pde_subdir_first(struct proc_dir_entry *dir) | ||
43 | { | ||
44 | return rb_entry_safe(rb_first(&dir->subdir), struct proc_dir_entry, | ||
45 | subdir_node); | ||
46 | } | ||
47 | |||
48 | static struct proc_dir_entry *pde_subdir_next(struct proc_dir_entry *dir) | ||
49 | { | ||
50 | return rb_entry_safe(rb_next(&dir->subdir_node), struct proc_dir_entry, | ||
51 | subdir_node); | ||
52 | } | ||
53 | |||
54 | static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir, | ||
55 | const char *name, | ||
56 | unsigned int len) | ||
57 | { | ||
58 | struct rb_node *node = dir->subdir.rb_node; | ||
59 | |||
60 | while (node) { | ||
61 | struct proc_dir_entry *de = container_of(node, | ||
62 | struct proc_dir_entry, | ||
63 | subdir_node); | ||
64 | int result = proc_match(len, name, de); | ||
65 | |||
66 | if (result < 0) | ||
67 | node = node->rb_left; | ||
68 | else if (result > 0) | ||
69 | node = node->rb_right; | ||
70 | else | ||
71 | return de; | ||
72 | } | ||
73 | return NULL; | ||
74 | } | ||
75 | |||
76 | static bool pde_subdir_insert(struct proc_dir_entry *dir, | ||
77 | struct proc_dir_entry *de) | ||
78 | { | ||
79 | struct rb_root *root = &dir->subdir; | ||
80 | struct rb_node **new = &root->rb_node, *parent = NULL; | ||
81 | |||
82 | /* Figure out where to put new node */ | ||
83 | while (*new) { | ||
84 | struct proc_dir_entry *this = | ||
85 | container_of(*new, struct proc_dir_entry, subdir_node); | ||
86 | int result = proc_match(de->namelen, de->name, this); | ||
87 | |||
88 | parent = *new; | ||
89 | if (result < 0) | ||
90 | new = &(*new)->rb_left; | ||
91 | else if (result > 0) | ||
92 | new = &(*new)->rb_right; | ||
93 | else | ||
94 | return false; | ||
95 | } | ||
96 | |||
97 | /* Add new node and rebalance tree. */ | ||
98 | rb_link_node(&de->subdir_node, parent, new); | ||
99 | rb_insert_color(&de->subdir_node, root); | ||
100 | return true; | ||
37 | } | 101 | } |
38 | 102 | ||
39 | static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) | 103 | static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) |
@@ -92,10 +156,7 @@ static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret, | |||
92 | break; | 156 | break; |
93 | 157 | ||
94 | len = next - cp; | 158 | len = next - cp; |
95 | for (de = de->subdir; de ; de = de->next) { | 159 | de = pde_subdir_find(de, cp, len); |
96 | if (proc_match(len, cp, de)) | ||
97 | break; | ||
98 | } | ||
99 | if (!de) { | 160 | if (!de) { |
100 | WARN(1, "name '%s'\n", name); | 161 | WARN(1, "name '%s'\n", name); |
101 | return -ENOENT; | 162 | return -ENOENT; |
@@ -183,19 +244,16 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir, | |||
183 | struct inode *inode; | 244 | struct inode *inode; |
184 | 245 | ||
185 | spin_lock(&proc_subdir_lock); | 246 | spin_lock(&proc_subdir_lock); |
186 | for (de = de->subdir; de ; de = de->next) { | 247 | de = pde_subdir_find(de, dentry->d_name.name, dentry->d_name.len); |
187 | if (de->namelen != dentry->d_name.len) | 248 | if (de) { |
188 | continue; | 249 | pde_get(de); |
189 | if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { | 250 | spin_unlock(&proc_subdir_lock); |
190 | pde_get(de); | 251 | inode = proc_get_inode(dir->i_sb, de); |
191 | spin_unlock(&proc_subdir_lock); | 252 | if (!inode) |
192 | inode = proc_get_inode(dir->i_sb, de); | 253 | return ERR_PTR(-ENOMEM); |
193 | if (!inode) | 254 | d_set_d_op(dentry, &simple_dentry_operations); |
194 | return ERR_PTR(-ENOMEM); | 255 | d_add(dentry, inode); |
195 | d_set_d_op(dentry, &simple_dentry_operations); | 256 | return NULL; |
196 | d_add(dentry, inode); | ||
197 | return NULL; | ||
198 | } | ||
199 | } | 257 | } |
200 | spin_unlock(&proc_subdir_lock); | 258 | spin_unlock(&proc_subdir_lock); |
201 | return ERR_PTR(-ENOENT); | 259 | return ERR_PTR(-ENOENT); |
@@ -225,7 +283,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file, | |||
225 | return 0; | 283 | return 0; |
226 | 284 | ||
227 | spin_lock(&proc_subdir_lock); | 285 | spin_lock(&proc_subdir_lock); |
228 | de = de->subdir; | 286 | de = pde_subdir_first(de); |
229 | i = ctx->pos - 2; | 287 | i = ctx->pos - 2; |
230 | for (;;) { | 288 | for (;;) { |
231 | if (!de) { | 289 | if (!de) { |
@@ -234,7 +292,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file, | |||
234 | } | 292 | } |
235 | if (!i) | 293 | if (!i) |
236 | break; | 294 | break; |
237 | de = de->next; | 295 | de = pde_subdir_next(de); |
238 | i--; | 296 | i--; |
239 | } | 297 | } |
240 | 298 | ||
@@ -249,7 +307,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file, | |||
249 | } | 307 | } |
250 | spin_lock(&proc_subdir_lock); | 308 | spin_lock(&proc_subdir_lock); |
251 | ctx->pos++; | 309 | ctx->pos++; |
252 | next = de->next; | 310 | next = pde_subdir_next(de); |
253 | pde_put(de); | 311 | pde_put(de); |
254 | de = next; | 312 | de = next; |
255 | } while (de); | 313 | } while (de); |
@@ -286,9 +344,8 @@ static const struct inode_operations proc_dir_inode_operations = { | |||
286 | 344 | ||
287 | static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) | 345 | static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) |
288 | { | 346 | { |
289 | struct proc_dir_entry *tmp; | ||
290 | int ret; | 347 | int ret; |
291 | 348 | ||
292 | ret = proc_alloc_inum(&dp->low_ino); | 349 | ret = proc_alloc_inum(&dp->low_ino); |
293 | if (ret) | 350 | if (ret) |
294 | return ret; | 351 | return ret; |
@@ -304,21 +361,21 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp | |||
304 | dp->proc_iops = &proc_file_inode_operations; | 361 | dp->proc_iops = &proc_file_inode_operations; |
305 | } else { | 362 | } else { |
306 | WARN_ON(1); | 363 | WARN_ON(1); |
364 | proc_free_inum(dp->low_ino); | ||
307 | return -EINVAL; | 365 | return -EINVAL; |
308 | } | 366 | } |
309 | 367 | ||
310 | spin_lock(&proc_subdir_lock); | 368 | spin_lock(&proc_subdir_lock); |
311 | |||
312 | for (tmp = dir->subdir; tmp; tmp = tmp->next) | ||
313 | if (strcmp(tmp->name, dp->name) == 0) { | ||
314 | WARN(1, "proc_dir_entry '%s/%s' already registered\n", | ||
315 | dir->name, dp->name); | ||
316 | break; | ||
317 | } | ||
318 | |||
319 | dp->next = dir->subdir; | ||
320 | dp->parent = dir; | 369 | dp->parent = dir; |
321 | dir->subdir = dp; | 370 | if (pde_subdir_insert(dir, dp) == false) { |
371 | WARN(1, "proc_dir_entry '%s/%s' already registered\n", | ||
372 | dir->name, dp->name); | ||
373 | spin_unlock(&proc_subdir_lock); | ||
374 | if (S_ISDIR(dp->mode)) | ||
375 | dir->nlink--; | ||
376 | proc_free_inum(dp->low_ino); | ||
377 | return -EEXIST; | ||
378 | } | ||
322 | spin_unlock(&proc_subdir_lock); | 379 | spin_unlock(&proc_subdir_lock); |
323 | 380 | ||
324 | return 0; | 381 | return 0; |
@@ -354,6 +411,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent, | |||
354 | ent->namelen = qstr.len; | 411 | ent->namelen = qstr.len; |
355 | ent->mode = mode; | 412 | ent->mode = mode; |
356 | ent->nlink = nlink; | 413 | ent->nlink = nlink; |
414 | ent->subdir = RB_ROOT; | ||
357 | atomic_set(&ent->count, 1); | 415 | atomic_set(&ent->count, 1); |
358 | spin_lock_init(&ent->pde_unload_lock); | 416 | spin_lock_init(&ent->pde_unload_lock); |
359 | INIT_LIST_HEAD(&ent->pde_openers); | 417 | INIT_LIST_HEAD(&ent->pde_openers); |
@@ -485,7 +543,6 @@ void pde_put(struct proc_dir_entry *pde) | |||
485 | */ | 543 | */ |
486 | void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | 544 | void remove_proc_entry(const char *name, struct proc_dir_entry *parent) |
487 | { | 545 | { |
488 | struct proc_dir_entry **p; | ||
489 | struct proc_dir_entry *de = NULL; | 546 | struct proc_dir_entry *de = NULL; |
490 | const char *fn = name; | 547 | const char *fn = name; |
491 | unsigned int len; | 548 | unsigned int len; |
@@ -497,14 +554,9 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | |||
497 | } | 554 | } |
498 | len = strlen(fn); | 555 | len = strlen(fn); |
499 | 556 | ||
500 | for (p = &parent->subdir; *p; p=&(*p)->next ) { | 557 | de = pde_subdir_find(parent, fn, len); |
501 | if (proc_match(len, fn, *p)) { | 558 | if (de) |
502 | de = *p; | 559 | rb_erase(&de->subdir_node, &parent->subdir); |
503 | *p = de->next; | ||
504 | de->next = NULL; | ||
505 | break; | ||
506 | } | ||
507 | } | ||
508 | spin_unlock(&proc_subdir_lock); | 560 | spin_unlock(&proc_subdir_lock); |
509 | if (!de) { | 561 | if (!de) { |
510 | WARN(1, "name '%s'\n", name); | 562 | WARN(1, "name '%s'\n", name); |
@@ -516,16 +568,15 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | |||
516 | if (S_ISDIR(de->mode)) | 568 | if (S_ISDIR(de->mode)) |
517 | parent->nlink--; | 569 | parent->nlink--; |
518 | de->nlink = 0; | 570 | de->nlink = 0; |
519 | WARN(de->subdir, "%s: removing non-empty directory " | 571 | WARN(pde_subdir_first(de), |
520 | "'%s/%s', leaking at least '%s'\n", __func__, | 572 | "%s: removing non-empty directory '%s/%s', leaking at least '%s'\n", |
521 | de->parent->name, de->name, de->subdir->name); | 573 | __func__, de->parent->name, de->name, pde_subdir_first(de)->name); |
522 | pde_put(de); | 574 | pde_put(de); |
523 | } | 575 | } |
524 | EXPORT_SYMBOL(remove_proc_entry); | 576 | EXPORT_SYMBOL(remove_proc_entry); |
525 | 577 | ||
526 | int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) | 578 | int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) |
527 | { | 579 | { |
528 | struct proc_dir_entry **p; | ||
529 | struct proc_dir_entry *root = NULL, *de, *next; | 580 | struct proc_dir_entry *root = NULL, *de, *next; |
530 | const char *fn = name; | 581 | const char *fn = name; |
531 | unsigned int len; | 582 | unsigned int len; |
@@ -537,24 +588,18 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) | |||
537 | } | 588 | } |
538 | len = strlen(fn); | 589 | len = strlen(fn); |
539 | 590 | ||
540 | for (p = &parent->subdir; *p; p=&(*p)->next ) { | 591 | root = pde_subdir_find(parent, fn, len); |
541 | if (proc_match(len, fn, *p)) { | ||
542 | root = *p; | ||
543 | *p = root->next; | ||
544 | root->next = NULL; | ||
545 | break; | ||
546 | } | ||
547 | } | ||
548 | if (!root) { | 592 | if (!root) { |
549 | spin_unlock(&proc_subdir_lock); | 593 | spin_unlock(&proc_subdir_lock); |
550 | return -ENOENT; | 594 | return -ENOENT; |
551 | } | 595 | } |
596 | rb_erase(&root->subdir_node, &parent->subdir); | ||
597 | |||
552 | de = root; | 598 | de = root; |
553 | while (1) { | 599 | while (1) { |
554 | next = de->subdir; | 600 | next = pde_subdir_first(de); |
555 | if (next) { | 601 | if (next) { |
556 | de->subdir = next->next; | 602 | rb_erase(&next->subdir_node, &de->subdir); |
557 | next->next = NULL; | ||
558 | de = next; | 603 | de = next; |
559 | continue; | 604 | continue; |
560 | } | 605 | } |
diff --git a/fs/proc/internal.h b/fs/proc/internal.h index aa7a0ee182e1..7fb1a4869fd0 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h | |||
@@ -24,10 +24,9 @@ struct mempolicy; | |||
24 | * tree) of these proc_dir_entries, so that we can dynamically | 24 | * tree) of these proc_dir_entries, so that we can dynamically |
25 | * add new files to /proc. | 25 | * add new files to /proc. |
26 | * | 26 | * |
27 | * The "next" pointer creates a linked list of one /proc directory, | 27 | * parent/subdir are used for the directory structure (every /proc file has a |
28 | * while parent/subdir create the directory structure (every | 28 | * parent, but "subdir" is empty for all non-directory entries). |
29 | * /proc file has a parent, but "subdir" is NULL for all | 29 | * subdir_node is used to build the rb tree "subdir" of the parent. |
30 | * non-directory entries). | ||
31 | */ | 30 | */ |
32 | struct proc_dir_entry { | 31 | struct proc_dir_entry { |
33 | unsigned int low_ino; | 32 | unsigned int low_ino; |
@@ -38,7 +37,9 @@ struct proc_dir_entry { | |||
38 | loff_t size; | 37 | loff_t size; |
39 | const struct inode_operations *proc_iops; | 38 | const struct inode_operations *proc_iops; |
40 | const struct file_operations *proc_fops; | 39 | const struct file_operations *proc_fops; |
41 | struct proc_dir_entry *next, *parent, *subdir; | 40 | struct proc_dir_entry *parent; |
41 | struct rb_root subdir; | ||
42 | struct rb_node subdir_node; | ||
42 | void *data; | 43 | void *data; |
43 | atomic_t count; /* use count */ | 44 | atomic_t count; /* use count */ |
44 | atomic_t in_use; /* number of callers into module in progress; */ | 45 | atomic_t in_use; /* number of callers into module in progress; */ |
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index a63af3e0a612..1bde894bc624 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c | |||
@@ -192,6 +192,7 @@ static __net_init int proc_net_ns_init(struct net *net) | |||
192 | if (!netd) | 192 | if (!netd) |
193 | goto out; | 193 | goto out; |
194 | 194 | ||
195 | netd->subdir = RB_ROOT; | ||
195 | netd->data = net; | 196 | netd->data = net; |
196 | netd->nlink = 2; | 197 | netd->nlink = 2; |
197 | netd->namelen = 3; | 198 | netd->namelen = 3; |
diff --git a/fs/proc/root.c b/fs/proc/root.c index 094e44d4a6be..e74ac9f1a2c0 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c | |||
@@ -251,6 +251,7 @@ struct proc_dir_entry proc_root = { | |||
251 | .proc_iops = &proc_root_inode_operations, | 251 | .proc_iops = &proc_root_inode_operations, |
252 | .proc_fops = &proc_root_operations, | 252 | .proc_fops = &proc_root_operations, |
253 | .parent = &proc_root, | 253 | .parent = &proc_root, |
254 | .subdir = RB_ROOT, | ||
254 | .name = "/proc", | 255 | .name = "/proc", |
255 | }; | 256 | }; |
256 | 257 | ||
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index f6734c6b66a6..246eae84b13b 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -447,58 +447,91 @@ struct mem_size_stats { | |||
447 | u64 pss; | 447 | u64 pss; |
448 | }; | 448 | }; |
449 | 449 | ||
450 | static void smaps_account(struct mem_size_stats *mss, struct page *page, | ||
451 | unsigned long size, bool young, bool dirty) | ||
452 | { | ||
453 | int mapcount; | ||
454 | |||
455 | if (PageAnon(page)) | ||
456 | mss->anonymous += size; | ||
450 | 457 | ||
451 | static void smaps_pte_entry(pte_t ptent, unsigned long addr, | 458 | mss->resident += size; |
452 | unsigned long ptent_size, struct mm_walk *walk) | 459 | /* Accumulate the size in pages that have been accessed. */ |
460 | if (young || PageReferenced(page)) | ||
461 | mss->referenced += size; | ||
462 | mapcount = page_mapcount(page); | ||
463 | if (mapcount >= 2) { | ||
464 | u64 pss_delta; | ||
465 | |||
466 | if (dirty || PageDirty(page)) | ||
467 | mss->shared_dirty += size; | ||
468 | else | ||
469 | mss->shared_clean += size; | ||
470 | pss_delta = (u64)size << PSS_SHIFT; | ||
471 | do_div(pss_delta, mapcount); | ||
472 | mss->pss += pss_delta; | ||
473 | } else { | ||
474 | if (dirty || PageDirty(page)) | ||
475 | mss->private_dirty += size; | ||
476 | else | ||
477 | mss->private_clean += size; | ||
478 | mss->pss += (u64)size << PSS_SHIFT; | ||
479 | } | ||
480 | } | ||
481 | |||
482 | static void smaps_pte_entry(pte_t *pte, unsigned long addr, | ||
483 | struct mm_walk *walk) | ||
453 | { | 484 | { |
454 | struct mem_size_stats *mss = walk->private; | 485 | struct mem_size_stats *mss = walk->private; |
455 | struct vm_area_struct *vma = mss->vma; | 486 | struct vm_area_struct *vma = mss->vma; |
456 | pgoff_t pgoff = linear_page_index(vma, addr); | 487 | pgoff_t pgoff = linear_page_index(vma, addr); |
457 | struct page *page = NULL; | 488 | struct page *page = NULL; |
458 | int mapcount; | ||
459 | 489 | ||
460 | if (pte_present(ptent)) { | 490 | if (pte_present(*pte)) { |
461 | page = vm_normal_page(vma, addr, ptent); | 491 | page = vm_normal_page(vma, addr, *pte); |
462 | } else if (is_swap_pte(ptent)) { | 492 | } else if (is_swap_pte(*pte)) { |
463 | swp_entry_t swpent = pte_to_swp_entry(ptent); | 493 | swp_entry_t swpent = pte_to_swp_entry(*pte); |
464 | 494 | ||
465 | if (!non_swap_entry(swpent)) | 495 | if (!non_swap_entry(swpent)) |
466 | mss->swap += ptent_size; | 496 | mss->swap += PAGE_SIZE; |
467 | else if (is_migration_entry(swpent)) | 497 | else if (is_migration_entry(swpent)) |
468 | page = migration_entry_to_page(swpent); | 498 | page = migration_entry_to_page(swpent); |
469 | } else if (pte_file(ptent)) { | 499 | } else if (pte_file(*pte)) { |
470 | if (pte_to_pgoff(ptent) != pgoff) | 500 | if (pte_to_pgoff(*pte) != pgoff) |
471 | mss->nonlinear += ptent_size; | 501 | mss->nonlinear += PAGE_SIZE; |
472 | } | 502 | } |
473 | 503 | ||
474 | if (!page) | 504 | if (!page) |
475 | return; | 505 | return; |
476 | 506 | ||
477 | if (PageAnon(page)) | ||
478 | mss->anonymous += ptent_size; | ||
479 | |||
480 | if (page->index != pgoff) | 507 | if (page->index != pgoff) |
481 | mss->nonlinear += ptent_size; | 508 | mss->nonlinear += PAGE_SIZE; |
482 | 509 | ||
483 | mss->resident += ptent_size; | 510 | smaps_account(mss, page, PAGE_SIZE, pte_young(*pte), pte_dirty(*pte)); |
484 | /* Accumulate the size in pages that have been accessed. */ | 511 | } |
485 | if (pte_young(ptent) || PageReferenced(page)) | 512 | |
486 | mss->referenced += ptent_size; | 513 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
487 | mapcount = page_mapcount(page); | 514 | static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr, |
488 | if (mapcount >= 2) { | 515 | struct mm_walk *walk) |
489 | if (pte_dirty(ptent) || PageDirty(page)) | 516 | { |
490 | mss->shared_dirty += ptent_size; | 517 | struct mem_size_stats *mss = walk->private; |
491 | else | 518 | struct vm_area_struct *vma = mss->vma; |
492 | mss->shared_clean += ptent_size; | 519 | struct page *page; |
493 | mss->pss += (ptent_size << PSS_SHIFT) / mapcount; | 520 | |
494 | } else { | 521 | /* FOLL_DUMP will return -EFAULT on huge zero page */ |
495 | if (pte_dirty(ptent) || PageDirty(page)) | 522 | page = follow_trans_huge_pmd(vma, addr, pmd, FOLL_DUMP); |
496 | mss->private_dirty += ptent_size; | 523 | if (IS_ERR_OR_NULL(page)) |
497 | else | 524 | return; |
498 | mss->private_clean += ptent_size; | 525 | mss->anonymous_thp += HPAGE_PMD_SIZE; |
499 | mss->pss += (ptent_size << PSS_SHIFT); | 526 | smaps_account(mss, page, HPAGE_PMD_SIZE, |
500 | } | 527 | pmd_young(*pmd), pmd_dirty(*pmd)); |
501 | } | 528 | } |
529 | #else | ||
530 | static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr, | ||
531 | struct mm_walk *walk) | ||
532 | { | ||
533 | } | ||
534 | #endif | ||
502 | 535 | ||
503 | static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, | 536 | static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, |
504 | struct mm_walk *walk) | 537 | struct mm_walk *walk) |
@@ -509,9 +542,8 @@ static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, | |||
509 | spinlock_t *ptl; | 542 | spinlock_t *ptl; |
510 | 543 | ||
511 | if (pmd_trans_huge_lock(pmd, vma, &ptl) == 1) { | 544 | if (pmd_trans_huge_lock(pmd, vma, &ptl) == 1) { |
512 | smaps_pte_entry(*(pte_t *)pmd, addr, HPAGE_PMD_SIZE, walk); | 545 | smaps_pmd_entry(pmd, addr, walk); |
513 | spin_unlock(ptl); | 546 | spin_unlock(ptl); |
514 | mss->anonymous_thp += HPAGE_PMD_SIZE; | ||
515 | return 0; | 547 | return 0; |
516 | } | 548 | } |
517 | 549 | ||
@@ -524,7 +556,7 @@ static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, | |||
524 | */ | 556 | */ |
525 | pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); | 557 | pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); |
526 | for (; addr != end; pte++, addr += PAGE_SIZE) | 558 | for (; addr != end; pte++, addr += PAGE_SIZE) |
527 | smaps_pte_entry(*pte, addr, PAGE_SIZE, walk); | 559 | smaps_pte_entry(pte, addr, walk); |
528 | pte_unmap_unlock(pte - 1, ptl); | 560 | pte_unmap_unlock(pte - 1, ptl); |
529 | cond_resched(); | 561 | cond_resched(); |
530 | return 0; | 562 | return 0; |