diff options
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/Kconfig | 4 | ||||
-rw-r--r-- | fs/proc/array.c | 2 | ||||
-rw-r--r-- | fs/proc/consoles.c | 10 | ||||
-rw-r--r-- | fs/proc/fd.c | 2 | ||||
-rw-r--r-- | fs/proc/generic.c | 18 | ||||
-rw-r--r-- | fs/proc/inode.c | 24 | ||||
-rw-r--r-- | fs/proc/kcore.c | 3 | ||||
-rw-r--r-- | fs/proc/meminfo.c | 13 | ||||
-rw-r--r-- | fs/proc/namespaces.c | 8 | ||||
-rw-r--r-- | fs/proc/nommu.c | 12 | ||||
-rw-r--r-- | fs/proc/proc_devtree.c | 3 | ||||
-rw-r--r-- | fs/proc/root.c | 6 | ||||
-rw-r--r-- | fs/proc/self.c | 10 | ||||
-rw-r--r-- | fs/proc/task_mmu.c | 97 | ||||
-rw-r--r-- | fs/proc/task_nommu.c | 19 | ||||
-rw-r--r-- | fs/proc/vmcore.c | 154 |
16 files changed, 253 insertions, 132 deletions
diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig index 15af6222f8a4..2183fcf41d59 100644 --- a/fs/proc/Kconfig +++ b/fs/proc/Kconfig | |||
@@ -31,6 +31,10 @@ config PROC_FS | |||
31 | config PROC_KCORE | 31 | config PROC_KCORE |
32 | bool "/proc/kcore support" if !ARM | 32 | bool "/proc/kcore support" if !ARM |
33 | depends on PROC_FS && MMU | 33 | depends on PROC_FS && MMU |
34 | help | ||
35 | Provides a virtual ELF core file of the live kernel. This can | ||
36 | be read with gdb and other ELF tools. No modifications can be | ||
37 | made using this mechanism. | ||
34 | 38 | ||
35 | config PROC_VMCORE | 39 | config PROC_VMCORE |
36 | bool "/proc/vmcore support" | 40 | bool "/proc/vmcore support" |
diff --git a/fs/proc/array.c b/fs/proc/array.c index cbd0f1b324b9..1bd2077187fd 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c | |||
@@ -183,6 +183,7 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, | |||
183 | seq_printf(m, | 183 | seq_printf(m, |
184 | "State:\t%s\n" | 184 | "State:\t%s\n" |
185 | "Tgid:\t%d\n" | 185 | "Tgid:\t%d\n" |
186 | "Ngid:\t%d\n" | ||
186 | "Pid:\t%d\n" | 187 | "Pid:\t%d\n" |
187 | "PPid:\t%d\n" | 188 | "PPid:\t%d\n" |
188 | "TracerPid:\t%d\n" | 189 | "TracerPid:\t%d\n" |
@@ -190,6 +191,7 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, | |||
190 | "Gid:\t%d\t%d\t%d\t%d\n", | 191 | "Gid:\t%d\t%d\t%d\t%d\n", |
191 | get_task_state(p), | 192 | get_task_state(p), |
192 | task_tgid_nr_ns(p, ns), | 193 | task_tgid_nr_ns(p, ns), |
194 | task_numa_group_id(p), | ||
193 | pid_nr_ns(pid, ns), | 195 | pid_nr_ns(pid, ns), |
194 | ppid, tpid, | 196 | ppid, tpid, |
195 | from_kuid_munged(user_ns, cred->uid), | 197 | from_kuid_munged(user_ns, cred->uid), |
diff --git a/fs/proc/consoles.c b/fs/proc/consoles.c index b701eaa482bf..51942d5abcec 100644 --- a/fs/proc/consoles.c +++ b/fs/proc/consoles.c | |||
@@ -29,7 +29,6 @@ static int show_console_dev(struct seq_file *m, void *v) | |||
29 | char flags[ARRAY_SIZE(con_flags) + 1]; | 29 | char flags[ARRAY_SIZE(con_flags) + 1]; |
30 | struct console *con = v; | 30 | struct console *con = v; |
31 | unsigned int a; | 31 | unsigned int a; |
32 | int len; | ||
33 | dev_t dev = 0; | 32 | dev_t dev = 0; |
34 | 33 | ||
35 | if (con->device) { | 34 | if (con->device) { |
@@ -47,11 +46,10 @@ static int show_console_dev(struct seq_file *m, void *v) | |||
47 | con_flags[a].name : ' '; | 46 | con_flags[a].name : ' '; |
48 | flags[a] = 0; | 47 | flags[a] = 0; |
49 | 48 | ||
50 | seq_printf(m, "%s%d%n", con->name, con->index, &len); | 49 | seq_setwidth(m, 21 - 1); |
51 | len = 21 - len; | 50 | seq_printf(m, "%s%d", con->name, con->index); |
52 | if (len < 1) | 51 | seq_pad(m, ' '); |
53 | len = 1; | 52 | seq_printf(m, "%c%c%c (%s)", con->read ? 'R' : '-', |
54 | seq_printf(m, "%*c%c%c%c (%s)", len, ' ', con->read ? 'R' : '-', | ||
55 | con->write ? 'W' : '-', con->unblank ? 'U' : '-', | 53 | con->write ? 'W' : '-', con->unblank ? 'U' : '-', |
56 | flags); | 54 | flags); |
57 | if (dev) | 55 | if (dev) |
diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 0ff80f9b930f..985ea881b5bc 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c | |||
@@ -286,7 +286,7 @@ int proc_fd_permission(struct inode *inode, int mask) | |||
286 | int rv = generic_permission(inode, mask); | 286 | int rv = generic_permission(inode, mask); |
287 | if (rv == 0) | 287 | if (rv == 0) |
288 | return 0; | 288 | return 0; |
289 | if (task_pid(current) == proc_pid(inode)) | 289 | if (task_tgid(current) == proc_pid(inode)) |
290 | rv = 0; | 290 | rv = 0; |
291 | return rv; | 291 | return rv; |
292 | } | 292 | } |
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 737e15615b04..cca93b6fb9a9 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c | |||
@@ -175,22 +175,6 @@ static const struct inode_operations proc_link_inode_operations = { | |||
175 | }; | 175 | }; |
176 | 176 | ||
177 | /* | 177 | /* |
178 | * As some entries in /proc are volatile, we want to | ||
179 | * get rid of unused dentries. This could be made | ||
180 | * smarter: we could keep a "volatile" flag in the | ||
181 | * inode to indicate which ones to keep. | ||
182 | */ | ||
183 | static int proc_delete_dentry(const struct dentry * dentry) | ||
184 | { | ||
185 | return 1; | ||
186 | } | ||
187 | |||
188 | static const struct dentry_operations proc_dentry_operations = | ||
189 | { | ||
190 | .d_delete = proc_delete_dentry, | ||
191 | }; | ||
192 | |||
193 | /* | ||
194 | * Don't create negative dentries here, return -ENOENT by hand | 178 | * Don't create negative dentries here, return -ENOENT by hand |
195 | * instead. | 179 | * instead. |
196 | */ | 180 | */ |
@@ -209,7 +193,7 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir, | |||
209 | inode = proc_get_inode(dir->i_sb, de); | 193 | inode = proc_get_inode(dir->i_sb, de); |
210 | if (!inode) | 194 | if (!inode) |
211 | return ERR_PTR(-ENOMEM); | 195 | return ERR_PTR(-ENOMEM); |
212 | d_set_d_op(dentry, &proc_dentry_operations); | 196 | d_set_d_op(dentry, &simple_dentry_operations); |
213 | d_add(dentry, inode); | 197 | d_add(dentry, inode); |
214 | return NULL; | 198 | return NULL; |
215 | } | 199 | } |
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 073aea60cf8f..28955d4b7218 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c | |||
@@ -285,6 +285,28 @@ static int proc_reg_mmap(struct file *file, struct vm_area_struct *vma) | |||
285 | return rv; | 285 | return rv; |
286 | } | 286 | } |
287 | 287 | ||
288 | static unsigned long | ||
289 | proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr, | ||
290 | unsigned long len, unsigned long pgoff, | ||
291 | unsigned long flags) | ||
292 | { | ||
293 | struct proc_dir_entry *pde = PDE(file_inode(file)); | ||
294 | unsigned long rv = -EIO; | ||
295 | unsigned long (*get_area)(struct file *, unsigned long, unsigned long, | ||
296 | unsigned long, unsigned long) = NULL; | ||
297 | if (use_pde(pde)) { | ||
298 | #ifdef CONFIG_MMU | ||
299 | get_area = current->mm->get_unmapped_area; | ||
300 | #endif | ||
301 | if (pde->proc_fops->get_unmapped_area) | ||
302 | get_area = pde->proc_fops->get_unmapped_area; | ||
303 | if (get_area) | ||
304 | rv = get_area(file, orig_addr, len, pgoff, flags); | ||
305 | unuse_pde(pde); | ||
306 | } | ||
307 | return rv; | ||
308 | } | ||
309 | |||
288 | static int proc_reg_open(struct inode *inode, struct file *file) | 310 | static int proc_reg_open(struct inode *inode, struct file *file) |
289 | { | 311 | { |
290 | struct proc_dir_entry *pde = PDE(inode); | 312 | struct proc_dir_entry *pde = PDE(inode); |
@@ -356,6 +378,7 @@ static const struct file_operations proc_reg_file_ops = { | |||
356 | .compat_ioctl = proc_reg_compat_ioctl, | 378 | .compat_ioctl = proc_reg_compat_ioctl, |
357 | #endif | 379 | #endif |
358 | .mmap = proc_reg_mmap, | 380 | .mmap = proc_reg_mmap, |
381 | .get_unmapped_area = proc_reg_get_unmapped_area, | ||
359 | .open = proc_reg_open, | 382 | .open = proc_reg_open, |
360 | .release = proc_reg_release, | 383 | .release = proc_reg_release, |
361 | }; | 384 | }; |
@@ -368,6 +391,7 @@ static const struct file_operations proc_reg_file_ops_no_compat = { | |||
368 | .poll = proc_reg_poll, | 391 | .poll = proc_reg_poll, |
369 | .unlocked_ioctl = proc_reg_unlocked_ioctl, | 392 | .unlocked_ioctl = proc_reg_unlocked_ioctl, |
370 | .mmap = proc_reg_mmap, | 393 | .mmap = proc_reg_mmap, |
394 | .get_unmapped_area = proc_reg_get_unmapped_area, | ||
371 | .open = proc_reg_open, | 395 | .open = proc_reg_open, |
372 | .release = proc_reg_release, | 396 | .release = proc_reg_release, |
373 | }; | 397 | }; |
diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c index 06ea155e1a59..5ed0e52d6aa0 100644 --- a/fs/proc/kcore.c +++ b/fs/proc/kcore.c | |||
@@ -255,8 +255,7 @@ static int kcore_update_ram(void) | |||
255 | end_pfn = 0; | 255 | end_pfn = 0; |
256 | for_each_node_state(nid, N_MEMORY) { | 256 | for_each_node_state(nid, N_MEMORY) { |
257 | unsigned long node_end; | 257 | unsigned long node_end; |
258 | node_end = NODE_DATA(nid)->node_start_pfn + | 258 | node_end = node_end_pfn(nid); |
259 | NODE_DATA(nid)->node_spanned_pages; | ||
260 | if (end_pfn < node_end) | 259 | if (end_pfn < node_end) |
261 | end_pfn = node_end; | 260 | end_pfn = node_end; |
262 | } | 261 | } |
diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c index 5aa847a603c0..a77d2b299199 100644 --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c | |||
@@ -1,8 +1,8 @@ | |||
1 | #include <linux/fs.h> | 1 | #include <linux/fs.h> |
2 | #include <linux/hugetlb.h> | ||
3 | #include <linux/init.h> | 2 | #include <linux/init.h> |
4 | #include <linux/kernel.h> | 3 | #include <linux/kernel.h> |
5 | #include <linux/mm.h> | 4 | #include <linux/mm.h> |
5 | #include <linux/hugetlb.h> | ||
6 | #include <linux/mman.h> | 6 | #include <linux/mman.h> |
7 | #include <linux/mmzone.h> | 7 | #include <linux/mmzone.h> |
8 | #include <linux/proc_fs.h> | 8 | #include <linux/proc_fs.h> |
@@ -24,7 +24,6 @@ static int meminfo_proc_show(struct seq_file *m, void *v) | |||
24 | { | 24 | { |
25 | struct sysinfo i; | 25 | struct sysinfo i; |
26 | unsigned long committed; | 26 | unsigned long committed; |
27 | unsigned long allowed; | ||
28 | struct vmalloc_info vmi; | 27 | struct vmalloc_info vmi; |
29 | long cached; | 28 | long cached; |
30 | unsigned long pages[NR_LRU_LISTS]; | 29 | unsigned long pages[NR_LRU_LISTS]; |
@@ -37,8 +36,6 @@ static int meminfo_proc_show(struct seq_file *m, void *v) | |||
37 | si_meminfo(&i); | 36 | si_meminfo(&i); |
38 | si_swapinfo(&i); | 37 | si_swapinfo(&i); |
39 | committed = percpu_counter_read_positive(&vm_committed_as); | 38 | committed = percpu_counter_read_positive(&vm_committed_as); |
40 | allowed = ((totalram_pages - hugetlb_total_pages()) | ||
41 | * sysctl_overcommit_ratio / 100) + total_swap_pages; | ||
42 | 39 | ||
43 | cached = global_page_state(NR_FILE_PAGES) - | 40 | cached = global_page_state(NR_FILE_PAGES) - |
44 | total_swapcache_pages() - i.bufferram; | 41 | total_swapcache_pages() - i.bufferram; |
@@ -132,13 +129,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) | |||
132 | K(i.freeswap), | 129 | K(i.freeswap), |
133 | K(global_page_state(NR_FILE_DIRTY)), | 130 | K(global_page_state(NR_FILE_DIRTY)), |
134 | K(global_page_state(NR_WRITEBACK)), | 131 | K(global_page_state(NR_WRITEBACK)), |
135 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | ||
136 | K(global_page_state(NR_ANON_PAGES) | ||
137 | + global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) * | ||
138 | HPAGE_PMD_NR), | ||
139 | #else | ||
140 | K(global_page_state(NR_ANON_PAGES)), | 132 | K(global_page_state(NR_ANON_PAGES)), |
141 | #endif | ||
142 | K(global_page_state(NR_FILE_MAPPED)), | 133 | K(global_page_state(NR_FILE_MAPPED)), |
143 | K(global_page_state(NR_SHMEM)), | 134 | K(global_page_state(NR_SHMEM)), |
144 | K(global_page_state(NR_SLAB_RECLAIMABLE) + | 135 | K(global_page_state(NR_SLAB_RECLAIMABLE) + |
@@ -153,7 +144,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) | |||
153 | K(global_page_state(NR_UNSTABLE_NFS)), | 144 | K(global_page_state(NR_UNSTABLE_NFS)), |
154 | K(global_page_state(NR_BOUNCE)), | 145 | K(global_page_state(NR_BOUNCE)), |
155 | K(global_page_state(NR_WRITEBACK_TEMP)), | 146 | K(global_page_state(NR_WRITEBACK_TEMP)), |
156 | K(allowed), | 147 | K(vm_commit_limit()), |
157 | K(committed), | 148 | K(committed), |
158 | (unsigned long)VMALLOC_TOTAL >> 10, | 149 | (unsigned long)VMALLOC_TOTAL >> 10, |
159 | vmi.used >> 10, | 150 | vmi.used >> 10, |
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index 49a7fff2e83a..9ae46b87470d 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c | |||
@@ -42,12 +42,6 @@ static const struct inode_operations ns_inode_operations = { | |||
42 | .setattr = proc_setattr, | 42 | .setattr = proc_setattr, |
43 | }; | 43 | }; |
44 | 44 | ||
45 | static int ns_delete_dentry(const struct dentry *dentry) | ||
46 | { | ||
47 | /* Don't cache namespace inodes when not in use */ | ||
48 | return 1; | ||
49 | } | ||
50 | |||
51 | static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) | 45 | static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) |
52 | { | 46 | { |
53 | struct inode *inode = dentry->d_inode; | 47 | struct inode *inode = dentry->d_inode; |
@@ -59,7 +53,7 @@ static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) | |||
59 | 53 | ||
60 | const struct dentry_operations ns_dentry_operations = | 54 | const struct dentry_operations ns_dentry_operations = |
61 | { | 55 | { |
62 | .d_delete = ns_delete_dentry, | 56 | .d_delete = always_delete_dentry, |
63 | .d_dname = ns_dname, | 57 | .d_dname = ns_dname, |
64 | }; | 58 | }; |
65 | 59 | ||
diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c index ccfd99bd1c5a..5f9bc8a746c9 100644 --- a/fs/proc/nommu.c +++ b/fs/proc/nommu.c | |||
@@ -39,7 +39,7 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) | |||
39 | unsigned long ino = 0; | 39 | unsigned long ino = 0; |
40 | struct file *file; | 40 | struct file *file; |
41 | dev_t dev = 0; | 41 | dev_t dev = 0; |
42 | int flags, len; | 42 | int flags; |
43 | 43 | ||
44 | flags = region->vm_flags; | 44 | flags = region->vm_flags; |
45 | file = region->vm_file; | 45 | file = region->vm_file; |
@@ -50,8 +50,9 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) | |||
50 | ino = inode->i_ino; | 50 | ino = inode->i_ino; |
51 | } | 51 | } |
52 | 52 | ||
53 | seq_setwidth(m, 25 + sizeof(void *) * 6 - 1); | ||
53 | seq_printf(m, | 54 | seq_printf(m, |
54 | "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n", | 55 | "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ", |
55 | region->vm_start, | 56 | region->vm_start, |
56 | region->vm_end, | 57 | region->vm_end, |
57 | flags & VM_READ ? 'r' : '-', | 58 | flags & VM_READ ? 'r' : '-', |
@@ -59,13 +60,10 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) | |||
59 | flags & VM_EXEC ? 'x' : '-', | 60 | flags & VM_EXEC ? 'x' : '-', |
60 | flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p', | 61 | flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p', |
61 | ((loff_t)region->vm_pgoff) << PAGE_SHIFT, | 62 | ((loff_t)region->vm_pgoff) << PAGE_SHIFT, |
62 | MAJOR(dev), MINOR(dev), ino, &len); | 63 | MAJOR(dev), MINOR(dev), ino); |
63 | 64 | ||
64 | if (file) { | 65 | if (file) { |
65 | len = 25 + sizeof(void *) * 6 - len; | 66 | seq_pad(m, ' '); |
66 | if (len < 1) | ||
67 | len = 1; | ||
68 | seq_printf(m, "%*c", len, ' '); | ||
69 | seq_path(m, &file->f_path, ""); | 67 | seq_path(m, &file->f_path, ""); |
70 | } | 68 | } |
71 | 69 | ||
diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 106a83570630..70779b2fc209 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c | |||
@@ -14,16 +14,13 @@ | |||
14 | #include <linux/of.h> | 14 | #include <linux/of.h> |
15 | #include <linux/export.h> | 15 | #include <linux/export.h> |
16 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
17 | #include <asm/prom.h> | ||
18 | #include <asm/uaccess.h> | 17 | #include <asm/uaccess.h> |
19 | #include "internal.h" | 18 | #include "internal.h" |
20 | 19 | ||
21 | static inline void set_node_proc_entry(struct device_node *np, | 20 | static inline void set_node_proc_entry(struct device_node *np, |
22 | struct proc_dir_entry *de) | 21 | struct proc_dir_entry *de) |
23 | { | 22 | { |
24 | #ifdef HAVE_ARCH_DEVTREE_FIXUPS | ||
25 | np->pde = de; | 23 | np->pde = de; |
26 | #endif | ||
27 | } | 24 | } |
28 | 25 | ||
29 | static struct proc_dir_entry *proc_device_tree; | 26 | static struct proc_dir_entry *proc_device_tree; |
diff --git a/fs/proc/root.c b/fs/proc/root.c index e0a790da726d..87dbcbef7fe4 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c | |||
@@ -110,7 +110,11 @@ static struct dentry *proc_mount(struct file_system_type *fs_type, | |||
110 | ns = task_active_pid_ns(current); | 110 | ns = task_active_pid_ns(current); |
111 | options = data; | 111 | options = data; |
112 | 112 | ||
113 | if (!current_user_ns()->may_mount_proc) | 113 | if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type)) |
114 | return ERR_PTR(-EPERM); | ||
115 | |||
116 | /* Does the mounter have privilege over the pid namespace? */ | ||
117 | if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN)) | ||
114 | return ERR_PTR(-EPERM); | 118 | return ERR_PTR(-EPERM); |
115 | } | 119 | } |
116 | 120 | ||
diff --git a/fs/proc/self.c b/fs/proc/self.c index 6b6a993b5c25..ffeb202ec942 100644 --- a/fs/proc/self.c +++ b/fs/proc/self.c | |||
@@ -36,18 +36,10 @@ static void *proc_self_follow_link(struct dentry *dentry, struct nameidata *nd) | |||
36 | return NULL; | 36 | return NULL; |
37 | } | 37 | } |
38 | 38 | ||
39 | static void proc_self_put_link(struct dentry *dentry, struct nameidata *nd, | ||
40 | void *cookie) | ||
41 | { | ||
42 | char *s = nd_get_link(nd); | ||
43 | if (!IS_ERR(s)) | ||
44 | kfree(s); | ||
45 | } | ||
46 | |||
47 | static const struct inode_operations proc_self_inode_operations = { | 39 | static const struct inode_operations proc_self_inode_operations = { |
48 | .readlink = proc_self_readlink, | 40 | .readlink = proc_self_readlink, |
49 | .follow_link = proc_self_follow_link, | 41 | .follow_link = proc_self_follow_link, |
50 | .put_link = proc_self_put_link, | 42 | .put_link = kfree_put_link, |
51 | }; | 43 | }; |
52 | 44 | ||
53 | static unsigned self_inum; | 45 | static unsigned self_inum; |
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 107d026f5d6e..fb52b548080d 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c | |||
@@ -62,7 +62,8 @@ void task_mem(struct seq_file *m, struct mm_struct *mm) | |||
62 | total_rss << (PAGE_SHIFT-10), | 62 | total_rss << (PAGE_SHIFT-10), |
63 | data << (PAGE_SHIFT-10), | 63 | data << (PAGE_SHIFT-10), |
64 | mm->stack_vm << (PAGE_SHIFT-10), text, lib, | 64 | mm->stack_vm << (PAGE_SHIFT-10), text, lib, |
65 | (PTRS_PER_PTE*sizeof(pte_t)*mm->nr_ptes) >> 10, | 65 | (PTRS_PER_PTE * sizeof(pte_t) * |
66 | atomic_long_read(&mm->nr_ptes)) >> 10, | ||
66 | swap << (PAGE_SHIFT-10)); | 67 | swap << (PAGE_SHIFT-10)); |
67 | } | 68 | } |
68 | 69 | ||
@@ -83,14 +84,6 @@ unsigned long task_statm(struct mm_struct *mm, | |||
83 | return mm->total_vm; | 84 | return mm->total_vm; |
84 | } | 85 | } |
85 | 86 | ||
86 | static void pad_len_spaces(struct seq_file *m, int len) | ||
87 | { | ||
88 | len = 25 + sizeof(void*) * 6 - len; | ||
89 | if (len < 1) | ||
90 | len = 1; | ||
91 | seq_printf(m, "%*c", len, ' '); | ||
92 | } | ||
93 | |||
94 | #ifdef CONFIG_NUMA | 87 | #ifdef CONFIG_NUMA |
95 | /* | 88 | /* |
96 | * These functions are for numa_maps but called in generic **maps seq_file | 89 | * These functions are for numa_maps but called in generic **maps seq_file |
@@ -268,7 +261,6 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) | |||
268 | unsigned long long pgoff = 0; | 261 | unsigned long long pgoff = 0; |
269 | unsigned long start, end; | 262 | unsigned long start, end; |
270 | dev_t dev = 0; | 263 | dev_t dev = 0; |
271 | int len; | ||
272 | const char *name = NULL; | 264 | const char *name = NULL; |
273 | 265 | ||
274 | if (file) { | 266 | if (file) { |
@@ -286,7 +278,8 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) | |||
286 | if (stack_guard_page_end(vma, end)) | 278 | if (stack_guard_page_end(vma, end)) |
287 | end -= PAGE_SIZE; | 279 | end -= PAGE_SIZE; |
288 | 280 | ||
289 | seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n", | 281 | seq_setwidth(m, 25 + sizeof(void *) * 6 - 1); |
282 | seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ", | ||
290 | start, | 283 | start, |
291 | end, | 284 | end, |
292 | flags & VM_READ ? 'r' : '-', | 285 | flags & VM_READ ? 'r' : '-', |
@@ -294,14 +287,14 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) | |||
294 | flags & VM_EXEC ? 'x' : '-', | 287 | flags & VM_EXEC ? 'x' : '-', |
295 | flags & VM_MAYSHARE ? 's' : 'p', | 288 | flags & VM_MAYSHARE ? 's' : 'p', |
296 | pgoff, | 289 | pgoff, |
297 | MAJOR(dev), MINOR(dev), ino, &len); | 290 | MAJOR(dev), MINOR(dev), ino); |
298 | 291 | ||
299 | /* | 292 | /* |
300 | * Print the dentry name for named mappings, and a | 293 | * Print the dentry name for named mappings, and a |
301 | * special [heap] marker for the heap: | 294 | * special [heap] marker for the heap: |
302 | */ | 295 | */ |
303 | if (file) { | 296 | if (file) { |
304 | pad_len_spaces(m, len); | 297 | seq_pad(m, ' '); |
305 | seq_path(m, &file->f_path, "\n"); | 298 | seq_path(m, &file->f_path, "\n"); |
306 | goto done; | 299 | goto done; |
307 | } | 300 | } |
@@ -333,7 +326,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) | |||
333 | name = "[stack]"; | 326 | name = "[stack]"; |
334 | } else { | 327 | } else { |
335 | /* Thread stack in /proc/PID/maps */ | 328 | /* Thread stack in /proc/PID/maps */ |
336 | pad_len_spaces(m, len); | 329 | seq_pad(m, ' '); |
337 | seq_printf(m, "[stack:%d]", tid); | 330 | seq_printf(m, "[stack:%d]", tid); |
338 | } | 331 | } |
339 | } | 332 | } |
@@ -341,7 +334,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) | |||
341 | 334 | ||
342 | done: | 335 | done: |
343 | if (name) { | 336 | if (name) { |
344 | pad_len_spaces(m, len); | 337 | seq_pad(m, ' '); |
345 | seq_puts(m, name); | 338 | seq_puts(m, name); |
346 | } | 339 | } |
347 | seq_putc(m, '\n'); | 340 | seq_putc(m, '\n'); |
@@ -505,9 +498,9 @@ static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, | |||
505 | pte_t *pte; | 498 | pte_t *pte; |
506 | spinlock_t *ptl; | 499 | spinlock_t *ptl; |
507 | 500 | ||
508 | if (pmd_trans_huge_lock(pmd, vma) == 1) { | 501 | if (pmd_trans_huge_lock(pmd, vma, &ptl) == 1) { |
509 | smaps_pte_entry(*(pte_t *)pmd, addr, HPAGE_PMD_SIZE, walk); | 502 | smaps_pte_entry(*(pte_t *)pmd, addr, HPAGE_PMD_SIZE, walk); |
510 | spin_unlock(&walk->mm->page_table_lock); | 503 | spin_unlock(ptl); |
511 | mss->anonymous_thp += HPAGE_PMD_SIZE; | 504 | mss->anonymous_thp += HPAGE_PMD_SIZE; |
512 | return 0; | 505 | return 0; |
513 | } | 506 | } |
@@ -561,6 +554,9 @@ static void show_smap_vma_flags(struct seq_file *m, struct vm_area_struct *vma) | |||
561 | [ilog2(VM_NONLINEAR)] = "nl", | 554 | [ilog2(VM_NONLINEAR)] = "nl", |
562 | [ilog2(VM_ARCH_1)] = "ar", | 555 | [ilog2(VM_ARCH_1)] = "ar", |
563 | [ilog2(VM_DONTDUMP)] = "dd", | 556 | [ilog2(VM_DONTDUMP)] = "dd", |
557 | #ifdef CONFIG_MEM_SOFT_DIRTY | ||
558 | [ilog2(VM_SOFTDIRTY)] = "sd", | ||
559 | #endif | ||
564 | [ilog2(VM_MIXEDMAP)] = "mm", | 560 | [ilog2(VM_MIXEDMAP)] = "mm", |
565 | [ilog2(VM_HUGEPAGE)] = "hg", | 561 | [ilog2(VM_HUGEPAGE)] = "hg", |
566 | [ilog2(VM_NOHUGEPAGE)] = "nh", | 562 | [ilog2(VM_NOHUGEPAGE)] = "nh", |
@@ -740,6 +736,9 @@ static inline void clear_soft_dirty(struct vm_area_struct *vma, | |||
740 | ptent = pte_file_clear_soft_dirty(ptent); | 736 | ptent = pte_file_clear_soft_dirty(ptent); |
741 | } | 737 | } |
742 | 738 | ||
739 | if (vma->vm_flags & VM_SOFTDIRTY) | ||
740 | vma->vm_flags &= ~VM_SOFTDIRTY; | ||
741 | |||
743 | set_pte_at(vma->vm_mm, addr, pte, ptent); | 742 | set_pte_at(vma->vm_mm, addr, pte, ptent); |
744 | #endif | 743 | #endif |
745 | } | 744 | } |
@@ -938,6 +937,8 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | |||
938 | frame = pte_pfn(pte); | 937 | frame = pte_pfn(pte); |
939 | flags = PM_PRESENT; | 938 | flags = PM_PRESENT; |
940 | page = vm_normal_page(vma, addr, pte); | 939 | page = vm_normal_page(vma, addr, pte); |
940 | if (pte_soft_dirty(pte)) | ||
941 | flags2 |= __PM_SOFT_DIRTY; | ||
941 | } else if (is_swap_pte(pte)) { | 942 | } else if (is_swap_pte(pte)) { |
942 | swp_entry_t entry; | 943 | swp_entry_t entry; |
943 | if (pte_swp_soft_dirty(pte)) | 944 | if (pte_swp_soft_dirty(pte)) |
@@ -949,13 +950,15 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | |||
949 | if (is_migration_entry(entry)) | 950 | if (is_migration_entry(entry)) |
950 | page = migration_entry_to_page(entry); | 951 | page = migration_entry_to_page(entry); |
951 | } else { | 952 | } else { |
952 | *pme = make_pme(PM_NOT_PRESENT(pm->v2)); | 953 | if (vma->vm_flags & VM_SOFTDIRTY) |
954 | flags2 |= __PM_SOFT_DIRTY; | ||
955 | *pme = make_pme(PM_NOT_PRESENT(pm->v2) | PM_STATUS2(pm->v2, flags2)); | ||
953 | return; | 956 | return; |
954 | } | 957 | } |
955 | 958 | ||
956 | if (page && !PageAnon(page)) | 959 | if (page && !PageAnon(page)) |
957 | flags |= PM_FILE; | 960 | flags |= PM_FILE; |
958 | if (pte_soft_dirty(pte)) | 961 | if ((vma->vm_flags & VM_SOFTDIRTY)) |
959 | flags2 |= __PM_SOFT_DIRTY; | 962 | flags2 |= __PM_SOFT_DIRTY; |
960 | 963 | ||
961 | *pme = make_pme(PM_PFRAME(frame) | PM_STATUS2(pm->v2, flags2) | flags); | 964 | *pme = make_pme(PM_PFRAME(frame) | PM_STATUS2(pm->v2, flags2) | flags); |
@@ -974,7 +977,7 @@ static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *p | |||
974 | *pme = make_pme(PM_PFRAME(pmd_pfn(pmd) + offset) | 977 | *pme = make_pme(PM_PFRAME(pmd_pfn(pmd) + offset) |
975 | | PM_STATUS2(pm->v2, pmd_flags2) | PM_PRESENT); | 978 | | PM_STATUS2(pm->v2, pmd_flags2) | PM_PRESENT); |
976 | else | 979 | else |
977 | *pme = make_pme(PM_NOT_PRESENT(pm->v2)); | 980 | *pme = make_pme(PM_NOT_PRESENT(pm->v2) | PM_STATUS2(pm->v2, pmd_flags2)); |
978 | } | 981 | } |
979 | #else | 982 | #else |
980 | static inline void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | 983 | static inline void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, |
@@ -988,16 +991,21 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, | |||
988 | { | 991 | { |
989 | struct vm_area_struct *vma; | 992 | struct vm_area_struct *vma; |
990 | struct pagemapread *pm = walk->private; | 993 | struct pagemapread *pm = walk->private; |
994 | spinlock_t *ptl; | ||
991 | pte_t *pte; | 995 | pte_t *pte; |
992 | int err = 0; | 996 | int err = 0; |
993 | pagemap_entry_t pme = make_pme(PM_NOT_PRESENT(pm->v2)); | 997 | pagemap_entry_t pme = make_pme(PM_NOT_PRESENT(pm->v2)); |
994 | 998 | ||
995 | /* find the first VMA at or above 'addr' */ | 999 | /* find the first VMA at or above 'addr' */ |
996 | vma = find_vma(walk->mm, addr); | 1000 | vma = find_vma(walk->mm, addr); |
997 | if (vma && pmd_trans_huge_lock(pmd, vma) == 1) { | 1001 | if (vma && pmd_trans_huge_lock(pmd, vma, &ptl) == 1) { |
998 | int pmd_flags2; | 1002 | int pmd_flags2; |
999 | 1003 | ||
1000 | pmd_flags2 = (pmd_soft_dirty(*pmd) ? __PM_SOFT_DIRTY : 0); | 1004 | if ((vma->vm_flags & VM_SOFTDIRTY) || pmd_soft_dirty(*pmd)) |
1005 | pmd_flags2 = __PM_SOFT_DIRTY; | ||
1006 | else | ||
1007 | pmd_flags2 = 0; | ||
1008 | |||
1001 | for (; addr != end; addr += PAGE_SIZE) { | 1009 | for (; addr != end; addr += PAGE_SIZE) { |
1002 | unsigned long offset; | 1010 | unsigned long offset; |
1003 | 1011 | ||
@@ -1008,19 +1016,24 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, | |||
1008 | if (err) | 1016 | if (err) |
1009 | break; | 1017 | break; |
1010 | } | 1018 | } |
1011 | spin_unlock(&walk->mm->page_table_lock); | 1019 | spin_unlock(ptl); |
1012 | return err; | 1020 | return err; |
1013 | } | 1021 | } |
1014 | 1022 | ||
1015 | if (pmd_trans_unstable(pmd)) | 1023 | if (pmd_trans_unstable(pmd)) |
1016 | return 0; | 1024 | return 0; |
1017 | for (; addr != end; addr += PAGE_SIZE) { | 1025 | for (; addr != end; addr += PAGE_SIZE) { |
1026 | int flags2; | ||
1018 | 1027 | ||
1019 | /* check to see if we've left 'vma' behind | 1028 | /* check to see if we've left 'vma' behind |
1020 | * and need a new, higher one */ | 1029 | * and need a new, higher one */ |
1021 | if (vma && (addr >= vma->vm_end)) { | 1030 | if (vma && (addr >= vma->vm_end)) { |
1022 | vma = find_vma(walk->mm, addr); | 1031 | vma = find_vma(walk->mm, addr); |
1023 | pme = make_pme(PM_NOT_PRESENT(pm->v2)); | 1032 | if (vma && (vma->vm_flags & VM_SOFTDIRTY)) |
1033 | flags2 = __PM_SOFT_DIRTY; | ||
1034 | else | ||
1035 | flags2 = 0; | ||
1036 | pme = make_pme(PM_NOT_PRESENT(pm->v2) | PM_STATUS2(pm->v2, flags2)); | ||
1024 | } | 1037 | } |
1025 | 1038 | ||
1026 | /* check that 'vma' actually covers this address, | 1039 | /* check that 'vma' actually covers this address, |
@@ -1044,13 +1057,15 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, | |||
1044 | 1057 | ||
1045 | #ifdef CONFIG_HUGETLB_PAGE | 1058 | #ifdef CONFIG_HUGETLB_PAGE |
1046 | static void huge_pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, | 1059 | static void huge_pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, |
1047 | pte_t pte, int offset) | 1060 | pte_t pte, int offset, int flags2) |
1048 | { | 1061 | { |
1049 | if (pte_present(pte)) | 1062 | if (pte_present(pte)) |
1050 | *pme = make_pme(PM_PFRAME(pte_pfn(pte) + offset) | 1063 | *pme = make_pme(PM_PFRAME(pte_pfn(pte) + offset) | |
1051 | | PM_STATUS2(pm->v2, 0) | PM_PRESENT); | 1064 | PM_STATUS2(pm->v2, flags2) | |
1065 | PM_PRESENT); | ||
1052 | else | 1066 | else |
1053 | *pme = make_pme(PM_NOT_PRESENT(pm->v2)); | 1067 | *pme = make_pme(PM_NOT_PRESENT(pm->v2) | |
1068 | PM_STATUS2(pm->v2, flags2)); | ||
1054 | } | 1069 | } |
1055 | 1070 | ||
1056 | /* This function walks within one hugetlb entry in the single call */ | 1071 | /* This function walks within one hugetlb entry in the single call */ |
@@ -1059,12 +1074,22 @@ static int pagemap_hugetlb_range(pte_t *pte, unsigned long hmask, | |||
1059 | struct mm_walk *walk) | 1074 | struct mm_walk *walk) |
1060 | { | 1075 | { |
1061 | struct pagemapread *pm = walk->private; | 1076 | struct pagemapread *pm = walk->private; |
1077 | struct vm_area_struct *vma; | ||
1062 | int err = 0; | 1078 | int err = 0; |
1079 | int flags2; | ||
1063 | pagemap_entry_t pme; | 1080 | pagemap_entry_t pme; |
1064 | 1081 | ||
1082 | vma = find_vma(walk->mm, addr); | ||
1083 | WARN_ON_ONCE(!vma); | ||
1084 | |||
1085 | if (vma && (vma->vm_flags & VM_SOFTDIRTY)) | ||
1086 | flags2 = __PM_SOFT_DIRTY; | ||
1087 | else | ||
1088 | flags2 = 0; | ||
1089 | |||
1065 | for (; addr != end; addr += PAGE_SIZE) { | 1090 | for (; addr != end; addr += PAGE_SIZE) { |
1066 | int offset = (addr & ~hmask) >> PAGE_SHIFT; | 1091 | int offset = (addr & ~hmask) >> PAGE_SHIFT; |
1067 | huge_pte_to_pagemap_entry(&pme, pm, *pte, offset); | 1092 | huge_pte_to_pagemap_entry(&pme, pm, *pte, offset, flags2); |
1068 | err = add_to_pagemap(addr, &pme, pm); | 1093 | err = add_to_pagemap(addr, &pme, pm); |
1069 | if (err) | 1094 | if (err) |
1070 | return err; | 1095 | return err; |
@@ -1293,7 +1318,7 @@ static int gather_pte_stats(pmd_t *pmd, unsigned long addr, | |||
1293 | 1318 | ||
1294 | md = walk->private; | 1319 | md = walk->private; |
1295 | 1320 | ||
1296 | if (pmd_trans_huge_lock(pmd, md->vma) == 1) { | 1321 | if (pmd_trans_huge_lock(pmd, md->vma, &ptl) == 1) { |
1297 | pte_t huge_pte = *(pte_t *)pmd; | 1322 | pte_t huge_pte = *(pte_t *)pmd; |
1298 | struct page *page; | 1323 | struct page *page; |
1299 | 1324 | ||
@@ -1301,7 +1326,7 @@ static int gather_pte_stats(pmd_t *pmd, unsigned long addr, | |||
1301 | if (page) | 1326 | if (page) |
1302 | gather_stats(page, md, pte_dirty(huge_pte), | 1327 | gather_stats(page, md, pte_dirty(huge_pte), |
1303 | HPAGE_PMD_SIZE/PAGE_SIZE); | 1328 | HPAGE_PMD_SIZE/PAGE_SIZE); |
1304 | spin_unlock(&walk->mm->page_table_lock); | 1329 | spin_unlock(ptl); |
1305 | return 0; | 1330 | return 0; |
1306 | } | 1331 | } |
1307 | 1332 | ||
@@ -1359,8 +1384,8 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) | |||
1359 | struct mm_struct *mm = vma->vm_mm; | 1384 | struct mm_struct *mm = vma->vm_mm; |
1360 | struct mm_walk walk = {}; | 1385 | struct mm_walk walk = {}; |
1361 | struct mempolicy *pol; | 1386 | struct mempolicy *pol; |
1362 | int n; | 1387 | char buffer[64]; |
1363 | char buffer[50]; | 1388 | int nid; |
1364 | 1389 | ||
1365 | if (!mm) | 1390 | if (!mm) |
1366 | return 0; | 1391 | return 0; |
@@ -1430,9 +1455,9 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) | |||
1430 | if (md->writeback) | 1455 | if (md->writeback) |
1431 | seq_printf(m, " writeback=%lu", md->writeback); | 1456 | seq_printf(m, " writeback=%lu", md->writeback); |
1432 | 1457 | ||
1433 | for_each_node_state(n, N_MEMORY) | 1458 | for_each_node_state(nid, N_MEMORY) |
1434 | if (md->node[n]) | 1459 | if (md->node[nid]) |
1435 | seq_printf(m, " N%d=%lu", n, md->node[n]); | 1460 | seq_printf(m, " N%d=%lu", nid, md->node[nid]); |
1436 | out: | 1461 | out: |
1437 | seq_putc(m, '\n'); | 1462 | seq_putc(m, '\n'); |
1438 | 1463 | ||
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c index 56123a6f462e..678455d2d683 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c | |||
@@ -123,14 +123,6 @@ unsigned long task_statm(struct mm_struct *mm, | |||
123 | return size; | 123 | return size; |
124 | } | 124 | } |
125 | 125 | ||
126 | static void pad_len_spaces(struct seq_file *m, int len) | ||
127 | { | ||
128 | len = 25 + sizeof(void*) * 6 - len; | ||
129 | if (len < 1) | ||
130 | len = 1; | ||
131 | seq_printf(m, "%*c", len, ' '); | ||
132 | } | ||
133 | |||
134 | /* | 126 | /* |
135 | * display a single VMA to a sequenced file | 127 | * display a single VMA to a sequenced file |
136 | */ | 128 | */ |
@@ -142,7 +134,7 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, | |||
142 | unsigned long ino = 0; | 134 | unsigned long ino = 0; |
143 | struct file *file; | 135 | struct file *file; |
144 | dev_t dev = 0; | 136 | dev_t dev = 0; |
145 | int flags, len; | 137 | int flags; |
146 | unsigned long long pgoff = 0; | 138 | unsigned long long pgoff = 0; |
147 | 139 | ||
148 | flags = vma->vm_flags; | 140 | flags = vma->vm_flags; |
@@ -155,8 +147,9 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, | |||
155 | pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; | 147 | pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; |
156 | } | 148 | } |
157 | 149 | ||
150 | seq_setwidth(m, 25 + sizeof(void *) * 6 - 1); | ||
158 | seq_printf(m, | 151 | seq_printf(m, |
159 | "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n", | 152 | "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ", |
160 | vma->vm_start, | 153 | vma->vm_start, |
161 | vma->vm_end, | 154 | vma->vm_end, |
162 | flags & VM_READ ? 'r' : '-', | 155 | flags & VM_READ ? 'r' : '-', |
@@ -164,16 +157,16 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, | |||
164 | flags & VM_EXEC ? 'x' : '-', | 157 | flags & VM_EXEC ? 'x' : '-', |
165 | flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p', | 158 | flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p', |
166 | pgoff, | 159 | pgoff, |
167 | MAJOR(dev), MINOR(dev), ino, &len); | 160 | MAJOR(dev), MINOR(dev), ino); |
168 | 161 | ||
169 | if (file) { | 162 | if (file) { |
170 | pad_len_spaces(m, len); | 163 | seq_pad(m, ' '); |
171 | seq_path(m, &file->f_path, ""); | 164 | seq_path(m, &file->f_path, ""); |
172 | } else if (mm) { | 165 | } else if (mm) { |
173 | pid_t tid = vm_is_stack(priv->task, vma, is_pid); | 166 | pid_t tid = vm_is_stack(priv->task, vma, is_pid); |
174 | 167 | ||
175 | if (tid != 0) { | 168 | if (tid != 0) { |
176 | pad_len_spaces(m, len); | 169 | seq_pad(m, ' '); |
177 | /* | 170 | /* |
178 | * Thread stack in /proc/PID/task/TID/maps or | 171 | * Thread stack in /proc/PID/task/TID/maps or |
179 | * the main process stack. | 172 | * the main process stack. |
diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index a1a16eb97c7b..9100d6959886 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/crash_dump.h> | 21 | #include <linux/crash_dump.h> |
22 | #include <linux/list.h> | 22 | #include <linux/list.h> |
23 | #include <linux/vmalloc.h> | 23 | #include <linux/vmalloc.h> |
24 | #include <linux/pagemap.h> | ||
24 | #include <asm/uaccess.h> | 25 | #include <asm/uaccess.h> |
25 | #include <asm/io.h> | 26 | #include <asm/io.h> |
26 | #include "internal.h" | 27 | #include "internal.h" |
@@ -123,11 +124,65 @@ static ssize_t read_from_oldmem(char *buf, size_t count, | |||
123 | return read; | 124 | return read; |
124 | } | 125 | } |
125 | 126 | ||
127 | /* | ||
128 | * Architectures may override this function to allocate ELF header in 2nd kernel | ||
129 | */ | ||
130 | int __weak elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size) | ||
131 | { | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | /* | ||
136 | * Architectures may override this function to free header | ||
137 | */ | ||
138 | void __weak elfcorehdr_free(unsigned long long addr) | ||
139 | {} | ||
140 | |||
141 | /* | ||
142 | * Architectures may override this function to read from ELF header | ||
143 | */ | ||
144 | ssize_t __weak elfcorehdr_read(char *buf, size_t count, u64 *ppos) | ||
145 | { | ||
146 | return read_from_oldmem(buf, count, ppos, 0); | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * Architectures may override this function to read from notes sections | ||
151 | */ | ||
152 | ssize_t __weak elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos) | ||
153 | { | ||
154 | return read_from_oldmem(buf, count, ppos, 0); | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * Architectures may override this function to map oldmem | ||
159 | */ | ||
160 | int __weak remap_oldmem_pfn_range(struct vm_area_struct *vma, | ||
161 | unsigned long from, unsigned long pfn, | ||
162 | unsigned long size, pgprot_t prot) | ||
163 | { | ||
164 | return remap_pfn_range(vma, from, pfn, size, prot); | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * Copy to either kernel or user space | ||
169 | */ | ||
170 | static int copy_to(void *target, void *src, size_t size, int userbuf) | ||
171 | { | ||
172 | if (userbuf) { | ||
173 | if (copy_to_user((char __user *) target, src, size)) | ||
174 | return -EFAULT; | ||
175 | } else { | ||
176 | memcpy(target, src, size); | ||
177 | } | ||
178 | return 0; | ||
179 | } | ||
180 | |||
126 | /* Read from the ELF header and then the crash dump. On error, negative value is | 181 | /* Read from the ELF header and then the crash dump. On error, negative value is |
127 | * returned otherwise number of bytes read are returned. | 182 | * returned otherwise number of bytes read are returned. |
128 | */ | 183 | */ |
129 | static ssize_t read_vmcore(struct file *file, char __user *buffer, | 184 | static ssize_t __read_vmcore(char *buffer, size_t buflen, loff_t *fpos, |
130 | size_t buflen, loff_t *fpos) | 185 | int userbuf) |
131 | { | 186 | { |
132 | ssize_t acc = 0, tmp; | 187 | ssize_t acc = 0, tmp; |
133 | size_t tsz; | 188 | size_t tsz; |
@@ -144,7 +199,7 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer, | |||
144 | /* Read ELF core header */ | 199 | /* Read ELF core header */ |
145 | if (*fpos < elfcorebuf_sz) { | 200 | if (*fpos < elfcorebuf_sz) { |
146 | tsz = min(elfcorebuf_sz - (size_t)*fpos, buflen); | 201 | tsz = min(elfcorebuf_sz - (size_t)*fpos, buflen); |
147 | if (copy_to_user(buffer, elfcorebuf + *fpos, tsz)) | 202 | if (copy_to(buffer, elfcorebuf + *fpos, tsz, userbuf)) |
148 | return -EFAULT; | 203 | return -EFAULT; |
149 | buflen -= tsz; | 204 | buflen -= tsz; |
150 | *fpos += tsz; | 205 | *fpos += tsz; |
@@ -162,7 +217,7 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer, | |||
162 | 217 | ||
163 | tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)*fpos, buflen); | 218 | tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)*fpos, buflen); |
164 | kaddr = elfnotes_buf + *fpos - elfcorebuf_sz; | 219 | kaddr = elfnotes_buf + *fpos - elfcorebuf_sz; |
165 | if (copy_to_user(buffer, kaddr, tsz)) | 220 | if (copy_to(buffer, kaddr, tsz, userbuf)) |
166 | return -EFAULT; | 221 | return -EFAULT; |
167 | buflen -= tsz; | 222 | buflen -= tsz; |
168 | *fpos += tsz; | 223 | *fpos += tsz; |
@@ -178,7 +233,7 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer, | |||
178 | if (*fpos < m->offset + m->size) { | 233 | if (*fpos < m->offset + m->size) { |
179 | tsz = min_t(size_t, m->offset + m->size - *fpos, buflen); | 234 | tsz = min_t(size_t, m->offset + m->size - *fpos, buflen); |
180 | start = m->paddr + *fpos - m->offset; | 235 | start = m->paddr + *fpos - m->offset; |
181 | tmp = read_from_oldmem(buffer, tsz, &start, 1); | 236 | tmp = read_from_oldmem(buffer, tsz, &start, userbuf); |
182 | if (tmp < 0) | 237 | if (tmp < 0) |
183 | return tmp; | 238 | return tmp; |
184 | buflen -= tsz; | 239 | buflen -= tsz; |
@@ -195,6 +250,55 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer, | |||
195 | return acc; | 250 | return acc; |
196 | } | 251 | } |
197 | 252 | ||
253 | static ssize_t read_vmcore(struct file *file, char __user *buffer, | ||
254 | size_t buflen, loff_t *fpos) | ||
255 | { | ||
256 | return __read_vmcore((__force char *) buffer, buflen, fpos, 1); | ||
257 | } | ||
258 | |||
259 | /* | ||
260 | * The vmcore fault handler uses the page cache and fills data using the | ||
261 | * standard __vmcore_read() function. | ||
262 | * | ||
263 | * On s390 the fault handler is used for memory regions that can't be mapped | ||
264 | * directly with remap_pfn_range(). | ||
265 | */ | ||
266 | static int mmap_vmcore_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
267 | { | ||
268 | #ifdef CONFIG_S390 | ||
269 | struct address_space *mapping = vma->vm_file->f_mapping; | ||
270 | pgoff_t index = vmf->pgoff; | ||
271 | struct page *page; | ||
272 | loff_t offset; | ||
273 | char *buf; | ||
274 | int rc; | ||
275 | |||
276 | page = find_or_create_page(mapping, index, GFP_KERNEL); | ||
277 | if (!page) | ||
278 | return VM_FAULT_OOM; | ||
279 | if (!PageUptodate(page)) { | ||
280 | offset = (loff_t) index << PAGE_CACHE_SHIFT; | ||
281 | buf = __va((page_to_pfn(page) << PAGE_SHIFT)); | ||
282 | rc = __read_vmcore(buf, PAGE_SIZE, &offset, 0); | ||
283 | if (rc < 0) { | ||
284 | unlock_page(page); | ||
285 | page_cache_release(page); | ||
286 | return (rc == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS; | ||
287 | } | ||
288 | SetPageUptodate(page); | ||
289 | } | ||
290 | unlock_page(page); | ||
291 | vmf->page = page; | ||
292 | return 0; | ||
293 | #else | ||
294 | return VM_FAULT_SIGBUS; | ||
295 | #endif | ||
296 | } | ||
297 | |||
298 | static const struct vm_operations_struct vmcore_mmap_ops = { | ||
299 | .fault = mmap_vmcore_fault, | ||
300 | }; | ||
301 | |||
198 | /** | 302 | /** |
199 | * alloc_elfnotes_buf - allocate buffer for ELF note segment in | 303 | * alloc_elfnotes_buf - allocate buffer for ELF note segment in |
200 | * vmalloc memory | 304 | * vmalloc memory |
@@ -223,7 +327,7 @@ static inline char *alloc_elfnotes_buf(size_t notes_sz) | |||
223 | * regions in the 1st kernel pointed to by PT_LOAD entries) into | 327 | * regions in the 1st kernel pointed to by PT_LOAD entries) into |
224 | * virtually contiguous user-space in ELF layout. | 328 | * virtually contiguous user-space in ELF layout. |
225 | */ | 329 | */ |
226 | #if defined(CONFIG_MMU) && !defined(CONFIG_S390) | 330 | #ifdef CONFIG_MMU |
227 | static int mmap_vmcore(struct file *file, struct vm_area_struct *vma) | 331 | static int mmap_vmcore(struct file *file, struct vm_area_struct *vma) |
228 | { | 332 | { |
229 | size_t size = vma->vm_end - vma->vm_start; | 333 | size_t size = vma->vm_end - vma->vm_start; |
@@ -241,6 +345,7 @@ static int mmap_vmcore(struct file *file, struct vm_area_struct *vma) | |||
241 | 345 | ||
242 | vma->vm_flags &= ~(VM_MAYWRITE | VM_MAYEXEC); | 346 | vma->vm_flags &= ~(VM_MAYWRITE | VM_MAYEXEC); |
243 | vma->vm_flags |= VM_MIXEDMAP; | 347 | vma->vm_flags |= VM_MIXEDMAP; |
348 | vma->vm_ops = &vmcore_mmap_ops; | ||
244 | 349 | ||
245 | len = 0; | 350 | len = 0; |
246 | 351 | ||
@@ -282,9 +387,9 @@ static int mmap_vmcore(struct file *file, struct vm_area_struct *vma) | |||
282 | 387 | ||
283 | tsz = min_t(size_t, m->offset + m->size - start, size); | 388 | tsz = min_t(size_t, m->offset + m->size - start, size); |
284 | paddr = m->paddr + start - m->offset; | 389 | paddr = m->paddr + start - m->offset; |
285 | if (remap_pfn_range(vma, vma->vm_start + len, | 390 | if (remap_oldmem_pfn_range(vma, vma->vm_start + len, |
286 | paddr >> PAGE_SHIFT, tsz, | 391 | paddr >> PAGE_SHIFT, tsz, |
287 | vma->vm_page_prot)) | 392 | vma->vm_page_prot)) |
288 | goto fail; | 393 | goto fail; |
289 | size -= tsz; | 394 | size -= tsz; |
290 | start += tsz; | 395 | start += tsz; |
@@ -357,7 +462,7 @@ static int __init update_note_header_size_elf64(const Elf64_Ehdr *ehdr_ptr) | |||
357 | notes_section = kmalloc(max_sz, GFP_KERNEL); | 462 | notes_section = kmalloc(max_sz, GFP_KERNEL); |
358 | if (!notes_section) | 463 | if (!notes_section) |
359 | return -ENOMEM; | 464 | return -ENOMEM; |
360 | rc = read_from_oldmem(notes_section, max_sz, &offset, 0); | 465 | rc = elfcorehdr_read_notes(notes_section, max_sz, &offset); |
361 | if (rc < 0) { | 466 | if (rc < 0) { |
362 | kfree(notes_section); | 467 | kfree(notes_section); |
363 | return rc; | 468 | return rc; |
@@ -444,7 +549,8 @@ static int __init copy_notes_elf64(const Elf64_Ehdr *ehdr_ptr, char *notes_buf) | |||
444 | if (phdr_ptr->p_type != PT_NOTE) | 549 | if (phdr_ptr->p_type != PT_NOTE) |
445 | continue; | 550 | continue; |
446 | offset = phdr_ptr->p_offset; | 551 | offset = phdr_ptr->p_offset; |
447 | rc = read_from_oldmem(notes_buf, phdr_ptr->p_memsz, &offset, 0); | 552 | rc = elfcorehdr_read_notes(notes_buf, phdr_ptr->p_memsz, |
553 | &offset); | ||
448 | if (rc < 0) | 554 | if (rc < 0) |
449 | return rc; | 555 | return rc; |
450 | notes_buf += phdr_ptr->p_memsz; | 556 | notes_buf += phdr_ptr->p_memsz; |
@@ -536,7 +642,7 @@ static int __init update_note_header_size_elf32(const Elf32_Ehdr *ehdr_ptr) | |||
536 | notes_section = kmalloc(max_sz, GFP_KERNEL); | 642 | notes_section = kmalloc(max_sz, GFP_KERNEL); |
537 | if (!notes_section) | 643 | if (!notes_section) |
538 | return -ENOMEM; | 644 | return -ENOMEM; |
539 | rc = read_from_oldmem(notes_section, max_sz, &offset, 0); | 645 | rc = elfcorehdr_read_notes(notes_section, max_sz, &offset); |
540 | if (rc < 0) { | 646 | if (rc < 0) { |
541 | kfree(notes_section); | 647 | kfree(notes_section); |
542 | return rc; | 648 | return rc; |
@@ -623,7 +729,8 @@ static int __init copy_notes_elf32(const Elf32_Ehdr *ehdr_ptr, char *notes_buf) | |||
623 | if (phdr_ptr->p_type != PT_NOTE) | 729 | if (phdr_ptr->p_type != PT_NOTE) |
624 | continue; | 730 | continue; |
625 | offset = phdr_ptr->p_offset; | 731 | offset = phdr_ptr->p_offset; |
626 | rc = read_from_oldmem(notes_buf, phdr_ptr->p_memsz, &offset, 0); | 732 | rc = elfcorehdr_read_notes(notes_buf, phdr_ptr->p_memsz, |
733 | &offset); | ||
627 | if (rc < 0) | 734 | if (rc < 0) |
628 | return rc; | 735 | return rc; |
629 | notes_buf += phdr_ptr->p_memsz; | 736 | notes_buf += phdr_ptr->p_memsz; |
@@ -810,7 +917,7 @@ static int __init parse_crash_elf64_headers(void) | |||
810 | addr = elfcorehdr_addr; | 917 | addr = elfcorehdr_addr; |
811 | 918 | ||
812 | /* Read Elf header */ | 919 | /* Read Elf header */ |
813 | rc = read_from_oldmem((char*)&ehdr, sizeof(Elf64_Ehdr), &addr, 0); | 920 | rc = elfcorehdr_read((char *)&ehdr, sizeof(Elf64_Ehdr), &addr); |
814 | if (rc < 0) | 921 | if (rc < 0) |
815 | return rc; | 922 | return rc; |
816 | 923 | ||
@@ -837,7 +944,7 @@ static int __init parse_crash_elf64_headers(void) | |||
837 | if (!elfcorebuf) | 944 | if (!elfcorebuf) |
838 | return -ENOMEM; | 945 | return -ENOMEM; |
839 | addr = elfcorehdr_addr; | 946 | addr = elfcorehdr_addr; |
840 | rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz_orig, &addr, 0); | 947 | rc = elfcorehdr_read(elfcorebuf, elfcorebuf_sz_orig, &addr); |
841 | if (rc < 0) | 948 | if (rc < 0) |
842 | goto fail; | 949 | goto fail; |
843 | 950 | ||
@@ -866,7 +973,7 @@ static int __init parse_crash_elf32_headers(void) | |||
866 | addr = elfcorehdr_addr; | 973 | addr = elfcorehdr_addr; |
867 | 974 | ||
868 | /* Read Elf header */ | 975 | /* Read Elf header */ |
869 | rc = read_from_oldmem((char*)&ehdr, sizeof(Elf32_Ehdr), &addr, 0); | 976 | rc = elfcorehdr_read((char *)&ehdr, sizeof(Elf32_Ehdr), &addr); |
870 | if (rc < 0) | 977 | if (rc < 0) |
871 | return rc; | 978 | return rc; |
872 | 979 | ||
@@ -892,7 +999,7 @@ static int __init parse_crash_elf32_headers(void) | |||
892 | if (!elfcorebuf) | 999 | if (!elfcorebuf) |
893 | return -ENOMEM; | 1000 | return -ENOMEM; |
894 | addr = elfcorehdr_addr; | 1001 | addr = elfcorehdr_addr; |
895 | rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz_orig, &addr, 0); | 1002 | rc = elfcorehdr_read(elfcorebuf, elfcorebuf_sz_orig, &addr); |
896 | if (rc < 0) | 1003 | if (rc < 0) |
897 | goto fail; | 1004 | goto fail; |
898 | 1005 | ||
@@ -919,7 +1026,7 @@ static int __init parse_crash_elf_headers(void) | |||
919 | int rc=0; | 1026 | int rc=0; |
920 | 1027 | ||
921 | addr = elfcorehdr_addr; | 1028 | addr = elfcorehdr_addr; |
922 | rc = read_from_oldmem(e_ident, EI_NIDENT, &addr, 0); | 1029 | rc = elfcorehdr_read(e_ident, EI_NIDENT, &addr); |
923 | if (rc < 0) | 1030 | if (rc < 0) |
924 | return rc; | 1031 | return rc; |
925 | if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) { | 1032 | if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) { |
@@ -952,7 +1059,14 @@ static int __init vmcore_init(void) | |||
952 | { | 1059 | { |
953 | int rc = 0; | 1060 | int rc = 0; |
954 | 1061 | ||
955 | /* If elfcorehdr= has been passed in cmdline, then capture the dump.*/ | 1062 | /* Allow architectures to allocate ELF header in 2nd kernel */ |
1063 | rc = elfcorehdr_alloc(&elfcorehdr_addr, &elfcorehdr_size); | ||
1064 | if (rc) | ||
1065 | return rc; | ||
1066 | /* | ||
1067 | * If elfcorehdr= has been passed in cmdline or created in 2nd kernel, | ||
1068 | * then capture the dump. | ||
1069 | */ | ||
956 | if (!(is_vmcore_usable())) | 1070 | if (!(is_vmcore_usable())) |
957 | return rc; | 1071 | return rc; |
958 | rc = parse_crash_elf_headers(); | 1072 | rc = parse_crash_elf_headers(); |
@@ -960,6 +1074,8 @@ static int __init vmcore_init(void) | |||
960 | pr_warn("Kdump: vmcore not initialized\n"); | 1074 | pr_warn("Kdump: vmcore not initialized\n"); |
961 | return rc; | 1075 | return rc; |
962 | } | 1076 | } |
1077 | elfcorehdr_free(elfcorehdr_addr); | ||
1078 | elfcorehdr_addr = ELFCORE_ADDR_ERR; | ||
963 | 1079 | ||
964 | proc_vmcore = proc_create("vmcore", S_IRUSR, NULL, &proc_vmcore_operations); | 1080 | proc_vmcore = proc_create("vmcore", S_IRUSR, NULL, &proc_vmcore_operations); |
965 | if (proc_vmcore) | 1081 | if (proc_vmcore) |