blob: 542527f6630f523e64a4151a90f6319d4b439d84 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/fs/proc/proc_misc.c
3 *
4 * linux/fs/proc/array.c
5 * Copyright (C) 1992 by Linus Torvalds
6 * based on ideas by Darren Senn
7 *
8 * This used to be the part of array.c. See the rest of history and credits
9 * there. I took this into a separate file and switched the thing to generic
10 * proc_file_inode_operations, leaving in array.c only per-process stuff.
11 * Inumbers allocation made dynamic (via create_proc_entry()). AV, May 1999.
12 *
13 * Changes:
14 * Fulton Green : Encapsulated position metric calculations.
15 * <kernel@FultonGreen.com>
16 */
17
18#include <linux/types.h>
19#include <linux/errno.h>
20#include <linux/time.h>
21#include <linux/kernel.h>
22#include <linux/kernel_stat.h>
Neil Horman7170be52006-01-14 13:20:38 -080023#include <linux/fs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <linux/tty.h>
25#include <linux/string.h>
26#include <linux/mman.h>
KOSAKI Motohiro4b856152008-09-02 14:35:53 -070027#include <linux/quicklist.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/proc_fs.h>
29#include <linux/ioport.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/mm.h>
31#include <linux/mmzone.h>
32#include <linux/pagemap.h>
Yinghai Luc7fb03a2008-08-19 20:50:12 -070033#include <linux/irq.h>
Adrian Bunkf74596d2008-02-06 01:36:35 -080034#include <linux/interrupt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <linux/swap.h>
36#include <linux/slab.h>
Adrian Bunka0db7012008-03-04 11:23:50 +010037#include <linux/genhd.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include <linux/smp.h>
39#include <linux/signal.h>
40#include <linux/module.h>
41#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/seq_file.h>
43#include <linux/times.h>
44#include <linux/profile.h>
Andrew Morton7bf65382006-12-08 02:41:14 -080045#include <linux/utsname.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <linux/blkdev.h>
47#include <linux/hugetlb.h>
48#include <linux/jiffies.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <linux/vmalloc.h>
Vivek Goyal666bfdd2005-06-25 14:58:21 -070050#include <linux/crash_dump.h>
Sukadev Bhattiprolu61a58c62006-12-08 02:37:58 -080051#include <linux/pid_namespace.h>
Matt Mackall161f47b2008-02-04 22:29:05 -080052#include <linux/bootmem.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#include <asm/uaccess.h>
54#include <asm/pgtable.h>
55#include <asm/io.h>
56#include <asm/tlb.h>
57#include <asm/div64.h>
58#include "internal.h"
59
Linus Torvalds1da177e2005-04-16 15:20:36 -070060/*
61 * Warning: stuff below (imported functions) assumes that its output will fit
62 * into one page. For some of those functions it may be wrong. Moreover, we
63 * have a way to deal with that gracefully. Right now I used straightforward
64 * wrappers, but this needs further analysis wrt potential overflows.
65 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070066extern int get_stram_list(char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070067extern int get_exec_domain_list(char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
69static int proc_calc_metrics(char *page, char **start, off_t off,
70 int count, int *eof, int len)
71{
72 if (len <= off+count) *eof = 1;
73 *start = page + off;
74 len -= off;
75 if (len>count) len = count;
76 if (len<0) len = 0;
77 return len;
78}
79
Linus Torvalds1da177e2005-04-16 15:20:36 -070080static int fragmentation_open(struct inode *inode, struct file *file)
81{
82 (void)inode;
83 return seq_open(file, &fragmentation_op);
84}
85
Arjan van de Ven00977a52007-02-12 00:55:34 -080086static const struct file_operations fragmentation_file_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 .open = fragmentation_open,
88 .read = seq_read,
89 .llseek = seq_lseek,
90 .release = seq_release,
91};
92
Mel Gorman467c9962007-10-16 01:26:02 -070093static int pagetypeinfo_open(struct inode *inode, struct file *file)
94{
95 return seq_open(file, &pagetypeinfo_op);
96}
97
98static const struct file_operations pagetypeinfo_file_ops = {
99 .open = pagetypeinfo_open,
100 .read = seq_read,
101 .llseek = seq_lseek,
102 .release = seq_release,
103};
104
Nikita Danilov295ab932005-06-21 17:14:38 -0700105static int zoneinfo_open(struct inode *inode, struct file *file)
106{
107 return seq_open(file, &zoneinfo_op);
108}
109
Arjan van de Ven00977a52007-02-12 00:55:34 -0800110static const struct file_operations proc_zoneinfo_file_operations = {
Nikita Danilov295ab932005-06-21 17:14:38 -0700111 .open = zoneinfo_open,
112 .read = seq_read,
113 .llseek = seq_lseek,
114 .release = seq_release,
115};
116
Jan Engelhardt03a44822008-02-08 04:21:19 -0800117extern const struct seq_operations cpuinfo_op;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118static int cpuinfo_open(struct inode *inode, struct file *file)
119{
120 return seq_open(file, &cpuinfo_op);
121}
Neil Horman7170be52006-01-14 13:20:38 -0800122
Arjan van de Ven00977a52007-02-12 00:55:34 -0800123static const struct file_operations proc_cpuinfo_operations = {
Joe Korty68eef3b2006-03-31 02:30:32 -0800124 .open = cpuinfo_open,
Neil Horman7170be52006-01-14 13:20:38 -0800125 .read = seq_read,
126 .llseek = seq_lseek,
127 .release = seq_release,
128};
129
Joe Korty68eef3b2006-03-31 02:30:32 -0800130static int devinfo_show(struct seq_file *f, void *v)
131{
132 int i = *(loff_t *) v;
133
134 if (i < CHRDEV_MAJOR_HASH_SIZE) {
135 if (i == 0)
136 seq_printf(f, "Character devices:\n");
137 chrdev_show(f, i);
David Howells93614012006-09-30 20:45:40 +0200138 }
139#ifdef CONFIG_BLOCK
140 else {
Joe Korty68eef3b2006-03-31 02:30:32 -0800141 i -= CHRDEV_MAJOR_HASH_SIZE;
142 if (i == 0)
143 seq_printf(f, "\nBlock devices:\n");
144 blkdev_show(f, i);
145 }
David Howells93614012006-09-30 20:45:40 +0200146#endif
Joe Korty68eef3b2006-03-31 02:30:32 -0800147 return 0;
148}
149
150static void *devinfo_start(struct seq_file *f, loff_t *pos)
151{
152 if (*pos < (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE))
153 return pos;
154 return NULL;
155}
156
157static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos)
158{
159 (*pos)++;
160 if (*pos >= (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE))
161 return NULL;
162 return pos;
163}
164
165static void devinfo_stop(struct seq_file *f, void *v)
166{
167 /* Nothing to do */
168}
169
Jan Engelhardt03a44822008-02-08 04:21:19 -0800170static const struct seq_operations devinfo_ops = {
Joe Korty68eef3b2006-03-31 02:30:32 -0800171 .start = devinfo_start,
172 .next = devinfo_next,
173 .stop = devinfo_stop,
174 .show = devinfo_show
175};
176
177static int devinfo_open(struct inode *inode, struct file *filp)
178{
179 return seq_open(filp, &devinfo_ops);
180}
181
Arjan van de Ven00977a52007-02-12 00:55:34 -0800182static const struct file_operations proc_devinfo_operations = {
Joe Korty68eef3b2006-03-31 02:30:32 -0800183 .open = devinfo_open,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 .read = seq_read,
185 .llseek = seq_lseek,
186 .release = seq_release,
187};
188
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189static int vmstat_open(struct inode *inode, struct file *file)
190{
191 return seq_open(file, &vmstat_op);
192}
Arjan van de Ven00977a52007-02-12 00:55:34 -0800193static const struct file_operations proc_vmstat_file_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 .open = vmstat_open,
195 .read = seq_read,
196 .llseek = seq_lseek,
197 .release = seq_release,
198};
199
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200#ifdef CONFIG_STRAM_PROC
201static int stram_read_proc(char *page, char **start, off_t off,
202 int count, int *eof, void *data)
203{
204 int len = get_stram_list(page);
205 return proc_calc_metrics(page, start, off, count, eof, len);
206}
207#endif
208
David Howells93614012006-09-30 20:45:40 +0200209#ifdef CONFIG_BLOCK
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210static int partitions_open(struct inode *inode, struct file *file)
211{
212 return seq_open(file, &partitions_op);
213}
Arjan van de Ven00977a52007-02-12 00:55:34 -0800214static const struct file_operations proc_partitions_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 .open = partitions_open,
216 .read = seq_read,
217 .llseek = seq_lseek,
218 .release = seq_release,
219};
220
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221static int diskstats_open(struct inode *inode, struct file *file)
222{
223 return seq_open(file, &diskstats_op);
224}
Arjan van de Ven00977a52007-02-12 00:55:34 -0800225static const struct file_operations proc_diskstats_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 .open = diskstats_open,
227 .read = seq_read,
228 .llseek = seq_lseek,
229 .release = seq_release,
230};
David Howells93614012006-09-30 20:45:40 +0200231#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232
233#ifdef CONFIG_MODULES
Jan Engelhardt03a44822008-02-08 04:21:19 -0800234extern const struct seq_operations modules_op;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235static int modules_open(struct inode *inode, struct file *file)
236{
237 return seq_open(file, &modules_op);
238}
Arjan van de Ven00977a52007-02-12 00:55:34 -0800239static const struct file_operations proc_modules_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 .open = modules_open,
241 .read = seq_read,
242 .llseek = seq_lseek,
243 .release = seq_release,
244};
245#endif
246
Linus Torvalds158a9622008-01-02 13:04:48 -0800247#ifdef CONFIG_SLABINFO
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248static int slabinfo_open(struct inode *inode, struct file *file)
249{
250 return seq_open(file, &slabinfo_op);
251}
Arjan van de Ven00977a52007-02-12 00:55:34 -0800252static const struct file_operations proc_slabinfo_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 .open = slabinfo_open,
254 .read = seq_read,
255 .write = slabinfo_write,
256 .llseek = seq_lseek,
257 .release = seq_release,
258};
Al Viro871751e2006-03-25 03:06:39 -0800259
260#ifdef CONFIG_DEBUG_SLAB_LEAK
Jan Engelhardt03a44822008-02-08 04:21:19 -0800261extern const struct seq_operations slabstats_op;
Al Viro871751e2006-03-25 03:06:39 -0800262static int slabstats_open(struct inode *inode, struct file *file)
263{
264 unsigned long *n = kzalloc(PAGE_SIZE, GFP_KERNEL);
265 int ret = -ENOMEM;
266 if (n) {
267 ret = seq_open(file, &slabstats_op);
268 if (!ret) {
269 struct seq_file *m = file->private_data;
270 *n = PAGE_SIZE / (2 * sizeof(unsigned long));
271 m->private = n;
272 n = NULL;
273 }
274 kfree(n);
275 }
276 return ret;
277}
278
Arjan van de Ven00977a52007-02-12 00:55:34 -0800279static const struct file_operations proc_slabstats_operations = {
Al Viro871751e2006-03-25 03:06:39 -0800280 .open = slabstats_open,
281 .read = seq_read,
282 .llseek = seq_lseek,
Martin Peschke09f08922007-05-08 00:29:26 -0700283 .release = seq_release_private,
Al Viro871751e2006-03-25 03:06:39 -0800284};
285#endif
Matt Mackall10cef602006-01-08 01:01:45 -0800286#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287
Christoph Lametera10aa572008-04-28 02:12:40 -0700288#ifdef CONFIG_MMU
289static int vmalloc_open(struct inode *inode, struct file *file)
290{
Eric Dumazeta47a1262008-07-23 21:27:38 -0700291 unsigned int *ptr = NULL;
292 int ret;
293
294 if (NUMA_BUILD)
295 ptr = kmalloc(nr_node_ids * sizeof(unsigned int), GFP_KERNEL);
296 ret = seq_open(file, &vmalloc_op);
297 if (!ret) {
298 struct seq_file *m = file->private_data;
299 m->private = ptr;
300 } else
301 kfree(ptr);
302 return ret;
Christoph Lametera10aa572008-04-28 02:12:40 -0700303}
304
305static const struct file_operations proc_vmalloc_operations = {
306 .open = vmalloc_open,
307 .read = seq_read,
308 .llseek = seq_lseek,
Eric Dumazeta47a1262008-07-23 21:27:38 -0700309 .release = seq_release_private,
Christoph Lametera10aa572008-04-28 02:12:40 -0700310};
311#endif
312
Jan Beulicha2eddfa2008-05-12 15:44:41 +0200313#ifndef arch_irq_stat_cpu
314#define arch_irq_stat_cpu(cpu) 0
315#endif
316#ifndef arch_irq_stat
317#define arch_irq_stat() 0
318#endif
319
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320static int show_stat(struct seq_file *p, void *v)
321{
Yinghai Luc7fb03a2008-08-19 20:50:12 -0700322 int i, j;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 unsigned long jif;
324 cputime64_t user, nice, system, idle, iowait, irq, softirq, steal;
Laurent Vivier5e84cfd2007-10-15 17:00:19 +0200325 cputime64_t guest;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 u64 sum = 0;
Tomas Janousek924b42d2007-07-15 23:39:42 -0700327 struct timespec boottime;
Yinghai Luc7fb03a2008-08-19 20:50:12 -0700328 unsigned int per_irq_sum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
330 user = nice = system = idle = iowait =
331 irq = softirq = steal = cputime64_zero;
Laurent Vivier5e84cfd2007-10-15 17:00:19 +0200332 guest = cputime64_zero;
Tomas Janousek924b42d2007-07-15 23:39:42 -0700333 getboottime(&boottime);
334 jif = boottime.tv_sec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
KAMEZAWA Hiroyuki0a945022006-03-28 01:56:37 -0800336 for_each_possible_cpu(i) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 user = cputime64_add(user, kstat_cpu(i).cpustat.user);
338 nice = cputime64_add(nice, kstat_cpu(i).cpustat.nice);
339 system = cputime64_add(system, kstat_cpu(i).cpustat.system);
340 idle = cputime64_add(idle, kstat_cpu(i).cpustat.idle);
341 iowait = cputime64_add(iowait, kstat_cpu(i).cpustat.iowait);
342 irq = cputime64_add(irq, kstat_cpu(i).cpustat.irq);
343 softirq = cputime64_add(softirq, kstat_cpu(i).cpustat.softirq);
344 steal = cputime64_add(steal, kstat_cpu(i).cpustat.steal);
Laurent Vivier5e84cfd2007-10-15 17:00:19 +0200345 guest = cputime64_add(guest, kstat_cpu(i).cpustat.guest);
Yinghai Luc7fb03a2008-08-19 20:50:12 -0700346
Thomas Gleixner2be3b522008-10-16 14:50:27 +0200347 for_each_irq_nr(j)
Thomas Gleixner2cc21ef2008-10-15 14:16:55 +0200348 sum += kstat_irqs_cpu(j, i);
349
Jan Beulicha2eddfa2008-05-12 15:44:41 +0200350 sum += arch_irq_stat_cpu(i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 }
Jan Beulicha2eddfa2008-05-12 15:44:41 +0200352 sum += arch_irq_stat();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
Laurent Vivier5e84cfd2007-10-15 17:00:19 +0200354 seq_printf(p, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 (unsigned long long)cputime64_to_clock_t(user),
356 (unsigned long long)cputime64_to_clock_t(nice),
357 (unsigned long long)cputime64_to_clock_t(system),
358 (unsigned long long)cputime64_to_clock_t(idle),
359 (unsigned long long)cputime64_to_clock_t(iowait),
360 (unsigned long long)cputime64_to_clock_t(irq),
361 (unsigned long long)cputime64_to_clock_t(softirq),
Laurent Vivier5e84cfd2007-10-15 17:00:19 +0200362 (unsigned long long)cputime64_to_clock_t(steal),
363 (unsigned long long)cputime64_to_clock_t(guest));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 for_each_online_cpu(i) {
365
366 /* Copy values here to work around gcc-2.95.3, gcc-2.96 */
367 user = kstat_cpu(i).cpustat.user;
368 nice = kstat_cpu(i).cpustat.nice;
369 system = kstat_cpu(i).cpustat.system;
370 idle = kstat_cpu(i).cpustat.idle;
371 iowait = kstat_cpu(i).cpustat.iowait;
372 irq = kstat_cpu(i).cpustat.irq;
373 softirq = kstat_cpu(i).cpustat.softirq;
374 steal = kstat_cpu(i).cpustat.steal;
Laurent Vivier5e84cfd2007-10-15 17:00:19 +0200375 guest = kstat_cpu(i).cpustat.guest;
376 seq_printf(p,
377 "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 i,
379 (unsigned long long)cputime64_to_clock_t(user),
380 (unsigned long long)cputime64_to_clock_t(nice),
381 (unsigned long long)cputime64_to_clock_t(system),
382 (unsigned long long)cputime64_to_clock_t(idle),
383 (unsigned long long)cputime64_to_clock_t(iowait),
384 (unsigned long long)cputime64_to_clock_t(irq),
385 (unsigned long long)cputime64_to_clock_t(softirq),
Laurent Vivier5e84cfd2007-10-15 17:00:19 +0200386 (unsigned long long)cputime64_to_clock_t(steal),
387 (unsigned long long)cputime64_to_clock_t(guest));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 }
389 seq_printf(p, "intr %llu", (unsigned long long)sum);
390
Yinghai Luc7fb03a2008-08-19 20:50:12 -0700391 /* sum again ? it could be updated? */
Thomas Gleixner2be3b522008-10-16 14:50:27 +0200392 for_each_irq_nr(j) {
Yinghai Luc7fb03a2008-08-19 20:50:12 -0700393 per_irq_sum = 0;
Yinghai Luc7fb03a2008-08-19 20:50:12 -0700394
Thomas Gleixner2cc21ef2008-10-15 14:16:55 +0200395 for_each_possible_cpu(i)
396 per_irq_sum += kstat_irqs_cpu(j, i);
Yinghai Luc7fb03a2008-08-19 20:50:12 -0700397
Yinghai Luc7fb03a2008-08-19 20:50:12 -0700398 seq_printf(p, " %u", per_irq_sum);
Yinghai Luc7fb03a2008-08-19 20:50:12 -0700399 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400
401 seq_printf(p,
402 "\nctxt %llu\n"
403 "btime %lu\n"
404 "processes %lu\n"
405 "procs_running %lu\n"
406 "procs_blocked %lu\n",
407 nr_context_switches(),
408 (unsigned long)jif,
409 total_forks,
410 nr_running(),
411 nr_iowait());
412
413 return 0;
414}
415
416static int stat_open(struct inode *inode, struct file *file)
417{
418 unsigned size = 4096 * (1 + num_possible_cpus() / 32);
419 char *buf;
420 struct seq_file *m;
421 int res;
422
423 /* don't ask for more than the kmalloc() max size, currently 128 KB */
424 if (size > 128 * 1024)
425 size = 128 * 1024;
426 buf = kmalloc(size, GFP_KERNEL);
427 if (!buf)
428 return -ENOMEM;
429
430 res = single_open(file, show_stat, NULL);
431 if (!res) {
432 m = file->private_data;
433 m->buf = buf;
434 m->size = size;
435 } else
436 kfree(buf);
437 return res;
438}
Arjan van de Ven00977a52007-02-12 00:55:34 -0800439static const struct file_operations proc_stat_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 .open = stat_open,
441 .read = seq_read,
442 .llseek = seq_lseek,
443 .release = single_release,
444};
445
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446/*
447 * /proc/interrupts
448 */
449static void *int_seq_start(struct seq_file *f, loff_t *pos)
450{
Yinghai Luda27c112008-08-19 20:49:56 -0700451 return (*pos <= nr_irqs) ? pos : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452}
453
Yinghai Lu52b17322008-08-19 20:50:20 -0700454
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455static void *int_seq_next(struct seq_file *f, void *v, loff_t *pos)
456{
457 (*pos)++;
Yinghai Lu52b17322008-08-19 20:50:20 -0700458 return (*pos <= nr_irqs) ? pos : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459}
460
461static void int_seq_stop(struct seq_file *f, void *v)
462{
463 /* Nothing to do */
464}
465
Jan Engelhardt03a44822008-02-08 04:21:19 -0800466static const struct seq_operations int_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 .start = int_seq_start,
468 .next = int_seq_next,
469 .stop = int_seq_stop,
470 .show = show_interrupts
471};
472
473static int interrupts_open(struct inode *inode, struct file *filp)
474{
475 return seq_open(filp, &int_seq_ops);
476}
477
Arjan van de Ven00977a52007-02-12 00:55:34 -0800478static const struct file_operations proc_interrupts_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 .open = interrupts_open,
480 .read = seq_read,
481 .llseek = seq_lseek,
482 .release = seq_release,
483};
484
485static int filesystems_read_proc(char *page, char **start, off_t off,
486 int count, int *eof, void *data)
487{
488 int len = get_filesystem_list(page);
489 return proc_calc_metrics(page, start, off, count, eof, len);
490}
491
492static int cmdline_read_proc(char *page, char **start, off_t off,
493 int count, int *eof, void *data)
494{
495 int len;
496
497 len = sprintf(page, "%s\n", saved_command_line);
498 return proc_calc_metrics(page, start, off, count, eof, len);
499}
500
Thomas Petazzonibfcd17a2008-08-06 15:12:22 +0200501#ifdef CONFIG_FILE_LOCKING
Pavel Emelyanov7f8ada92007-10-01 14:41:15 -0700502static int locks_open(struct inode *inode, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503{
Pavel Emelyanov7f8ada92007-10-01 14:41:15 -0700504 return seq_open(filp, &locks_seq_operations);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505}
506
Pavel Emelyanov7f8ada92007-10-01 14:41:15 -0700507static const struct file_operations proc_locks_operations = {
508 .open = locks_open,
509 .read = seq_read,
510 .llseek = seq_lseek,
511 .release = seq_release,
512};
Thomas Petazzonibfcd17a2008-08-06 15:12:22 +0200513#endif /* CONFIG_FILE_LOCKING */
Pavel Emelyanov7f8ada92007-10-01 14:41:15 -0700514
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515static int execdomains_read_proc(char *page, char **start, off_t off,
516 int count, int *eof, void *data)
517{
518 int len = get_exec_domain_list(page);
519 return proc_calc_metrics(page, start, off, count, eof, len);
520}
521
Matt Mackall1e883282008-02-04 22:29:07 -0800522#ifdef CONFIG_PROC_PAGE_MONITOR
Matt Mackall161f47b2008-02-04 22:29:05 -0800523#define KPMSIZE sizeof(u64)
524#define KPMMASK (KPMSIZE - 1)
525/* /proc/kpagecount - an array exposing page counts
526 *
527 * Each entry is a u64 representing the corresponding
528 * physical page count.
529 */
530static ssize_t kpagecount_read(struct file *file, char __user *buf,
531 size_t count, loff_t *ppos)
532{
533 u64 __user *out = (u64 __user *)buf;
534 struct page *ppage;
535 unsigned long src = *ppos;
536 unsigned long pfn;
537 ssize_t ret = 0;
538 u64 pcount;
539
540 pfn = src / KPMSIZE;
541 count = min_t(size_t, count, (max_pfn * KPMSIZE) - src);
542 if (src & KPMMASK || count & KPMMASK)
Thomas Tuttle4710d1a2008-06-05 22:46:58 -0700543 return -EINVAL;
Matt Mackall161f47b2008-02-04 22:29:05 -0800544
545 while (count > 0) {
Matt Mackall304daa82008-02-04 22:29:06 -0800546 ppage = NULL;
547 if (pfn_valid(pfn))
548 ppage = pfn_to_page(pfn);
549 pfn++;
Matt Mackall161f47b2008-02-04 22:29:05 -0800550 if (!ppage)
551 pcount = 0;
552 else
Thomas Tuttlebbcdac02008-06-05 22:46:58 -0700553 pcount = page_mapcount(ppage);
Matt Mackall161f47b2008-02-04 22:29:05 -0800554
555 if (put_user(pcount, out++)) {
556 ret = -EFAULT;
557 break;
558 }
559
560 count -= KPMSIZE;
561 }
562
563 *ppos += (char __user *)out - buf;
564 if (!ret)
565 ret = (char __user *)out - buf;
566 return ret;
567}
568
569static struct file_operations proc_kpagecount_operations = {
570 .llseek = mem_lseek,
571 .read = kpagecount_read,
572};
573
Matt Mackall304daa82008-02-04 22:29:06 -0800574/* /proc/kpageflags - an array exposing page flags
575 *
576 * Each entry is a u64 representing the corresponding
577 * physical page flags.
578 */
579
580/* These macros are used to decouple internal flags from exported ones */
581
582#define KPF_LOCKED 0
583#define KPF_ERROR 1
584#define KPF_REFERENCED 2
585#define KPF_UPTODATE 3
586#define KPF_DIRTY 4
587#define KPF_LRU 5
588#define KPF_ACTIVE 6
589#define KPF_SLAB 7
590#define KPF_WRITEBACK 8
591#define KPF_RECLAIM 9
592#define KPF_BUDDY 10
593
594#define kpf_copy_bit(flags, srcpos, dstpos) (((flags >> srcpos) & 1) << dstpos)
595
596static ssize_t kpageflags_read(struct file *file, char __user *buf,
597 size_t count, loff_t *ppos)
598{
599 u64 __user *out = (u64 __user *)buf;
600 struct page *ppage;
601 unsigned long src = *ppos;
602 unsigned long pfn;
603 ssize_t ret = 0;
604 u64 kflags, uflags;
605
606 pfn = src / KPMSIZE;
607 count = min_t(unsigned long, count, (max_pfn * KPMSIZE) - src);
608 if (src & KPMMASK || count & KPMMASK)
Thomas Tuttle4710d1a2008-06-05 22:46:58 -0700609 return -EINVAL;
Matt Mackall304daa82008-02-04 22:29:06 -0800610
611 while (count > 0) {
612 ppage = NULL;
613 if (pfn_valid(pfn))
614 ppage = pfn_to_page(pfn);
615 pfn++;
616 if (!ppage)
617 kflags = 0;
618 else
619 kflags = ppage->flags;
620
621 uflags = kpf_copy_bit(KPF_LOCKED, PG_locked, kflags) |
622 kpf_copy_bit(kflags, KPF_ERROR, PG_error) |
623 kpf_copy_bit(kflags, KPF_REFERENCED, PG_referenced) |
624 kpf_copy_bit(kflags, KPF_UPTODATE, PG_uptodate) |
625 kpf_copy_bit(kflags, KPF_DIRTY, PG_dirty) |
626 kpf_copy_bit(kflags, KPF_LRU, PG_lru) |
627 kpf_copy_bit(kflags, KPF_ACTIVE, PG_active) |
628 kpf_copy_bit(kflags, KPF_SLAB, PG_slab) |
629 kpf_copy_bit(kflags, KPF_WRITEBACK, PG_writeback) |
630 kpf_copy_bit(kflags, KPF_RECLAIM, PG_reclaim) |
631 kpf_copy_bit(kflags, KPF_BUDDY, PG_buddy);
632
633 if (put_user(uflags, out++)) {
634 ret = -EFAULT;
635 break;
636 }
637
638 count -= KPMSIZE;
639 }
640
641 *ppos += (char __user *)out - buf;
642 if (!ret)
643 ret = (char __user *)out - buf;
644 return ret;
645}
646
647static struct file_operations proc_kpageflags_operations = {
648 .llseek = mem_lseek,
649 .read = kpageflags_read,
650};
Matt Mackall1e883282008-02-04 22:29:07 -0800651#endif /* CONFIG_PROC_PAGE_MONITOR */
Matt Mackall304daa82008-02-04 22:29:06 -0800652
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653struct proc_dir_entry *proc_root_kcore;
654
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655void __init proc_misc_init(void)
656{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 static struct {
658 char *name;
659 int (*read_proc)(char*,char**,off_t,int,int*,void*);
660 } *p, simple_ones[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661#ifdef CONFIG_STRAM_PROC
662 {"stram", stram_read_proc},
663#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 {"filesystems", filesystems_read_proc},
665 {"cmdline", cmdline_read_proc},
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 {"execdomains", execdomains_read_proc},
667 {NULL,}
668 };
669 for (p = simple_ones; p->name; p++)
670 create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL);
671
672 proc_symlink("mounts", NULL, "self/mounts");
673
674 /* And now for trickier ones */
Mike Galbraithc36264d2006-12-06 20:37:42 -0800675#ifdef CONFIG_PRINTK
Alexey Dobriyanc74c1202008-04-29 01:01:44 -0700676 proc_create("kmsg", S_IRUSR, NULL, &proc_kmsg_operations);
Mike Galbraithc36264d2006-12-06 20:37:42 -0800677#endif
Thomas Petazzonibfcd17a2008-08-06 15:12:22 +0200678#ifdef CONFIG_FILE_LOCKING
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700679 proc_create("locks", 0, NULL, &proc_locks_operations);
Thomas Petazzonibfcd17a2008-08-06 15:12:22 +0200680#endif
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700681 proc_create("devices", 0, NULL, &proc_devinfo_operations);
682 proc_create("cpuinfo", 0, NULL, &proc_cpuinfo_operations);
David Howells93614012006-09-30 20:45:40 +0200683#ifdef CONFIG_BLOCK
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700684 proc_create("partitions", 0, NULL, &proc_partitions_operations);
David Howells93614012006-09-30 20:45:40 +0200685#endif
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700686 proc_create("stat", 0, NULL, &proc_stat_operations);
687 proc_create("interrupts", 0, NULL, &proc_interrupts_operations);
Linus Torvalds158a9622008-01-02 13:04:48 -0800688#ifdef CONFIG_SLABINFO
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700689 proc_create("slabinfo",S_IWUSR|S_IRUGO,NULL,&proc_slabinfo_operations);
Al Viro871751e2006-03-25 03:06:39 -0800690#ifdef CONFIG_DEBUG_SLAB_LEAK
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700691 proc_create("slab_allocators", 0, NULL, &proc_slabstats_operations);
Al Viro871751e2006-03-25 03:06:39 -0800692#endif
Matt Mackall10cef602006-01-08 01:01:45 -0800693#endif
Christoph Lametera10aa572008-04-28 02:12:40 -0700694#ifdef CONFIG_MMU
695 proc_create("vmallocinfo", S_IRUSR, NULL, &proc_vmalloc_operations);
696#endif
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700697 proc_create("buddyinfo", S_IRUGO, NULL, &fragmentation_file_operations);
698 proc_create("pagetypeinfo", S_IRUGO, NULL, &pagetypeinfo_file_ops);
699 proc_create("vmstat", S_IRUGO, NULL, &proc_vmstat_file_operations);
700 proc_create("zoneinfo", S_IRUGO, NULL, &proc_zoneinfo_file_operations);
David Howells93614012006-09-30 20:45:40 +0200701#ifdef CONFIG_BLOCK
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700702 proc_create("diskstats", 0, NULL, &proc_diskstats_operations);
David Howells93614012006-09-30 20:45:40 +0200703#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704#ifdef CONFIG_MODULES
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700705 proc_create("modules", 0, NULL, &proc_modules_operations);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706#endif
707#ifdef CONFIG_SCHEDSTATS
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700708 proc_create("schedstat", 0, NULL, &proc_schedstat_operations);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709#endif
710#ifdef CONFIG_PROC_KCORE
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700711 proc_root_kcore = proc_create("kcore", S_IRUSR, NULL, &proc_kcore_operations);
712 if (proc_root_kcore)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 proc_root_kcore->size =
714 (size_t)high_memory - PAGE_OFFSET + PAGE_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715#endif
Matt Mackall1e883282008-02-04 22:29:07 -0800716#ifdef CONFIG_PROC_PAGE_MONITOR
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700717 proc_create("kpagecount", S_IRUSR, NULL, &proc_kpagecount_operations);
718 proc_create("kpageflags", S_IRUSR, NULL, &proc_kpageflags_operations);
Matt Mackall1e883282008-02-04 22:29:07 -0800719#endif
Vivek Goyal666bfdd2005-06-25 14:58:21 -0700720#ifdef CONFIG_PROC_VMCORE
Alexey Dobriyan0d5c9f52008-04-29 01:01:37 -0700721 proc_vmcore = proc_create("vmcore", S_IRUSR, NULL, &proc_vmcore_operations);
Vivek Goyal666bfdd2005-06-25 14:58:21 -0700722#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723}