aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-12-10 21:34:42 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-12-10 21:34:42 -0500
commitb6da0076bab5a12afb19312ffee41c95490af2a0 (patch)
tree52a5675b9c2ff95d88b981d5b9a3822f6073c112 /fs/proc
parentcbfe0de303a55ed96d8831c2d5f56f8131cd6612 (diff)
parenta53b831549141aa060a8b54b76e3a42870d74cc0 (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.c47
-rw-r--r--fs/proc/base.c3
-rw-r--r--fs/proc/generic.c163
-rw-r--r--fs/proc/internal.h11
-rw-r--r--fs/proc/proc_net.c1
-rw-r--r--fs/proc/root.c1
-rw-r--r--fs/proc/task_mmu.c104
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
32static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de) 32static 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
42static 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
48static 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
54static 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
76static 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
39static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) 103static 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
287static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) 345static 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 */
486void remove_proc_entry(const char *name, struct proc_dir_entry *parent) 544void 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}
524EXPORT_SYMBOL(remove_proc_entry); 576EXPORT_SYMBOL(remove_proc_entry);
525 577
526int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) 578int 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 */
32struct proc_dir_entry { 31struct 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
450static 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
451static 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
482static 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); 514static 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
530static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr,
531 struct mm_walk *walk)
532{
533}
534#endif
502 535
503static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, 536static 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;