diff options
Diffstat (limited to 'fs/proc')
| -rw-r--r-- | fs/proc/array.c | 47 | ||||
| -rw-r--r-- | fs/proc/base.c | 7 | ||||
| -rw-r--r-- | fs/proc/fd.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 | 107 |
8 files changed, 212 insertions, 128 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 772efa45a452..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); |
| @@ -2789,7 +2792,7 @@ retry: | |||
| 2789 | int proc_pid_readdir(struct file *file, struct dir_context *ctx) | 2792 | int proc_pid_readdir(struct file *file, struct dir_context *ctx) |
| 2790 | { | 2793 | { |
| 2791 | struct tgid_iter iter; | 2794 | struct tgid_iter iter; |
| 2792 | struct pid_namespace *ns = file->f_dentry->d_sb->s_fs_info; | 2795 | struct pid_namespace *ns = file_inode(file)->i_sb->s_fs_info; |
| 2793 | loff_t pos = ctx->pos; | 2796 | loff_t pos = ctx->pos; |
| 2794 | 2797 | ||
| 2795 | if (pos >= PID_MAX_LIMIT + TGID_OFFSET) | 2798 | if (pos >= PID_MAX_LIMIT + TGID_OFFSET) |
| @@ -3095,7 +3098,7 @@ static int proc_task_readdir(struct file *file, struct dir_context *ctx) | |||
| 3095 | /* f_version caches the tgid value that the last readdir call couldn't | 3098 | /* f_version caches the tgid value that the last readdir call couldn't |
| 3096 | * return. lseek aka telldir automagically resets f_version to 0. | 3099 | * return. lseek aka telldir automagically resets f_version to 0. |
| 3097 | */ | 3100 | */ |
| 3098 | ns = file->f_dentry->d_sb->s_fs_info; | 3101 | ns = inode->i_sb->s_fs_info; |
| 3099 | tid = (int)file->f_version; | 3102 | tid = (int)file->f_version; |
| 3100 | file->f_version = 0; | 3103 | file->f_version = 0; |
| 3101 | for (task = first_tid(proc_pid(inode), tid, ctx->pos - 2, ns); | 3104 | for (task = first_tid(proc_pid(inode), tid, ctx->pos - 2, ns); |
diff --git a/fs/proc/fd.c b/fs/proc/fd.c index e11d7c590bb0..8e5ad83b629a 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c | |||
| @@ -53,7 +53,8 @@ static int seq_show(struct seq_file *m, void *v) | |||
| 53 | (long long)file->f_pos, f_flags, | 53 | (long long)file->f_pos, f_flags, |
| 54 | real_mount(file->f_path.mnt)->mnt_id); | 54 | real_mount(file->f_path.mnt)->mnt_id); |
| 55 | if (file->f_op->show_fdinfo) | 55 | if (file->f_op->show_fdinfo) |
| 56 | ret = file->f_op->show_fdinfo(m, file); | 56 | file->f_op->show_fdinfo(m, file); |
| 57 | ret = seq_has_overflowed(m); | ||
| 57 | fput(file); | 58 | fput(file); |
| 58 | } | 59 | } |
| 59 | 60 | ||
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 4e0388cffe3d..246eae84b13b 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
| @@ -447,59 +447,92 @@ 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. */ | ||
| 485 | if (pte_young(ptent) || PageReferenced(page)) | ||
| 486 | mss->referenced += ptent_size; | ||
| 487 | mapcount = page_mapcount(page); | ||
| 488 | if (mapcount >= 2) { | ||
| 489 | if (pte_dirty(ptent) || PageDirty(page)) | ||
| 490 | mss->shared_dirty += ptent_size; | ||
| 491 | else | ||
| 492 | mss->shared_clean += ptent_size; | ||
| 493 | mss->pss += (ptent_size << PSS_SHIFT) / mapcount; | ||
| 494 | } else { | ||
| 495 | if (pte_dirty(ptent) || PageDirty(page)) | ||
| 496 | mss->private_dirty += ptent_size; | ||
| 497 | else | ||
| 498 | mss->private_clean += ptent_size; | ||
| 499 | mss->pss += (ptent_size << PSS_SHIFT); | ||
| 500 | } | ||
| 501 | } | 511 | } |
| 502 | 512 | ||
| 513 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
| 514 | static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr, | ||
| 515 | struct mm_walk *walk) | ||
| 516 | { | ||
| 517 | struct mem_size_stats *mss = walk->private; | ||
| 518 | struct vm_area_struct *vma = mss->vma; | ||
| 519 | struct page *page; | ||
| 520 | |||
| 521 | /* FOLL_DUMP will return -EFAULT on huge zero page */ | ||
| 522 | page = follow_trans_huge_pmd(vma, addr, pmd, FOLL_DUMP); | ||
| 523 | if (IS_ERR_OR_NULL(page)) | ||
| 524 | return; | ||
| 525 | mss->anonymous_thp += HPAGE_PMD_SIZE; | ||
| 526 | smaps_account(mss, page, HPAGE_PMD_SIZE, | ||
| 527 | pmd_young(*pmd), pmd_dirty(*pmd)); | ||
| 528 | } | ||
| 529 | #else | ||
| 530 | static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr, | ||
| 531 | struct mm_walk *walk) | ||
| 532 | { | ||
| 533 | } | ||
| 534 | #endif | ||
| 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) |
| 505 | { | 538 | { |
| @@ -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; |
| @@ -552,6 +584,9 @@ static void show_smap_vma_flags(struct seq_file *m, struct vm_area_struct *vma) | |||
| 552 | [ilog2(VM_GROWSDOWN)] = "gd", | 584 | [ilog2(VM_GROWSDOWN)] = "gd", |
| 553 | [ilog2(VM_PFNMAP)] = "pf", | 585 | [ilog2(VM_PFNMAP)] = "pf", |
| 554 | [ilog2(VM_DENYWRITE)] = "dw", | 586 | [ilog2(VM_DENYWRITE)] = "dw", |
| 587 | #ifdef CONFIG_X86_INTEL_MPX | ||
| 588 | [ilog2(VM_MPX)] = "mp", | ||
| 589 | #endif | ||
| 555 | [ilog2(VM_LOCKED)] = "lo", | 590 | [ilog2(VM_LOCKED)] = "lo", |
| 556 | [ilog2(VM_IO)] = "io", | 591 | [ilog2(VM_IO)] = "io", |
| 557 | [ilog2(VM_SEQ_READ)] = "sr", | 592 | [ilog2(VM_SEQ_READ)] = "sr", |
