kernel - Major SMP performance patch / VM system, bus-fault/seg-fault fixes
[dragonfly.git] / sys / emulation / linux / i386 / linprocfs / linprocfs_misc.c
1 /*
2  * Copyright (c) 2000 Dag-Erling Coïdan Smørgrav
3  * Copyright (c) 1999 Pierre Beyssac
4  * Copyright (c) 1993 Jan-Simon Pendry
5  * Copyright (c) 1993
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  *      @(#)procfs_status.c     8.4 (Berkeley) 6/15/94
40  *
41  * $FreeBSD: src/sys/i386/linux/linprocfs/linprocfs_misc.c,v 1.3.2.8 2001/06/25 19:46:47 pirzyk Exp $
42  */
43
44 #include <sys/param.h>
45 #include <sys/blist.h>
46 #include <sys/kernel.h>
47 #include <sys/kinfo.h>
48 #include <sys/proc.h>
49 #include <sys/jail.h>
50 #include <sys/resourcevar.h>
51 #include <sys/systm.h>
52 #include <sys/tty.h>
53 #include <sys/vnode.h>
54 #include <sys/lock.h>
55 #include <sys/sbuf.h>
56 #include <sys/mount.h>
57 #include <sys/sysctl.h>
58
59 #include <vm/vm.h>
60 #include <vm/pmap.h>
61 #include <vm/vm_map.h>
62 #include <vm/vm_param.h>
63 #include <vm/vm_object.h>
64 #include <vm/swap_pager.h>
65 #include <sys/vmmeter.h>
66 #include <sys/exec.h>
67
68 #include <machine/clock.h>
69 #include <machine/cputypes.h>
70 #include <machine/inttypes.h>
71 #include <machine/md_var.h>
72 #include <machine/vmparam.h>
73
74 #include "linprocfs.h"
75 #include "../linux.h"
76 #include "../../linux_ioctl.h"
77 #include "../../linux_mib.h"
78
79 /*
80  * Various conversion macros
81  */
82 #define T2J(x) (((x) * 100) / (stathz ? stathz : hz))   /* ticks to jiffies */
83 #define T2S(x) ((x) / (stathz ? stathz : hz))           /* ticks to seconds */
84 #define B2K(x) ((unsigned long)((x) >> 10))                     /* bytes to kbytes */
85 #define P2B(x) ((x) << PAGE_SHIFT)                      /* pages to bytes */
86 #define P2K(x) ((x) << (PAGE_SHIFT - 10))               /* pages to kbytes */
87
88 int
89 linprocfs_domeminfo(struct proc *curp, struct proc *p, struct pfsnode *pfs,
90                     struct uio *uio)
91 {
92         char *ps;
93         char psbuf[512];                /* XXX - conservative */
94         unsigned long memtotal;         /* total memory in bytes */
95         unsigned long memused;          /* used memory in bytes */
96         unsigned long memfree;          /* free memory in bytes */
97         unsigned long memshared;        /* shared memory ??? */
98         unsigned long buffers, cached;  /* buffer / cache memory ??? */
99         unsigned long long swaptotal;   /* total swap space in bytes */
100         unsigned long long swapused;    /* used swap space in bytes */
101         unsigned long long swapfree;    /* free swap space in bytes */
102         vm_object_t object;
103
104         if (uio->uio_rw != UIO_READ)
105                 return (EOPNOTSUPP);
106
107         memtotal = Maxmem * PAGE_SIZE;
108         /*
109          * The correct thing here would be:
110          *
111         memfree = vmstats.v_free_count * PAGE_SIZE;
112         memused = memtotal - memfree;
113          *
114          * but it might mislead linux binaries into thinking there
115          * is very little memory left, so we cheat and tell them that
116          * all memory that isn't wired down is free.
117          */
118         memused = vmstats.v_wire_count * PAGE_SIZE;
119         memfree = memtotal - memused;
120         if (swapblist == NULL) {
121                 swaptotal = 0;
122                 swapfree = 0;
123         } else {
124                 swaptotal = swapblist->bl_blocks * 1024LL; /* XXX why 1024? */
125                 swapfree = (unsigned long long)swapblist->bl_root->u.bmu_avail * PAGE_SIZE;
126         }
127         swapused = swaptotal - swapfree;
128         memshared = 0;
129
130         lwkt_gettoken(&vmobj_token);
131         for (object = TAILQ_FIRST(&vm_object_list); object != NULL;
132             object = TAILQ_NEXT(object, object_list)) {
133                 if (object->type == OBJT_MARKER)
134                         continue;
135                 if (object->shadow_count > 1)
136                         memshared += object->resident_page_count;
137         }
138         lwkt_reltoken(&vmobj_token);
139         memshared *= PAGE_SIZE;
140         /*
141          * We'd love to be able to write:
142          *
143         buffers = bufspace;
144          *
145          * but bufspace is internal to vfs_bio.c and we don't feel
146          * like unstaticizing it just for linprocfs's sake.
147          */
148         buffers = 0;
149         cached = vmstats.v_cache_count * PAGE_SIZE;
150
151         ps = psbuf;
152         ps += ksprintf(ps,
153                 "        total:    used:    free:  shared: buffers:  cached:\n"
154                 "Mem:  %lu %lu %lu %lu %lu %lu\n"
155                 "Swap: %llu %llu %llu\n"
156                 "MemTotal: %9lu kB\n"
157                 "MemFree:  %9lu kB\n"
158                 "MemShared:%9lu kB\n"
159                 "Buffers:  %9lu kB\n"
160                 "Cached:   %9lu kB\n"
161                 "SwapTotal:%9lu kB\n"
162                 "SwapFree: %9lu kB\n",
163                 memtotal, memused, memfree, memshared, buffers, cached,
164                 swaptotal, swapused, swapfree,
165                 B2K(memtotal), B2K(memfree),
166                 B2K(memshared), B2K(buffers), B2K(cached),
167                 B2K(swaptotal), B2K(swapfree));
168
169         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
170 }
171
172 int
173 linprocfs_docpuinfo(struct proc *curp, struct proc *p, struct pfsnode *pfs,
174                     struct uio *uio)
175 {
176         char *ps;
177         char psbuf[8192];
178         char hwmodel[128];
179         size_t  modellen = sizeof(hwmodel);
180         int mib[] = { CTL_HW, HW_MODEL };
181         int class;
182         int cpu;
183         int i;
184         int error;
185 #if 0
186         extern char *cpu_model;         /* Yuck */
187 #endif
188         /* We default the flags to include all non-conflicting flags,
189            and the Intel versions of conflicting flags.  Note the space
190            before each name; that is significant, and should be 
191            preserved. */
192
193         static char *flags[] = {
194                 "fpu",      "vme",     "de",       "pse",      "tsc",
195                 "msr",      "pae",     "mce",      "cx8",      "apic",
196                 "sep",      "sep",     "mtrr",     "pge",      "mca",
197                 "cmov",     "pat",     "pse36",    "pn",       "b19",
198                 "b20",      "b21",     "mmxext",   "mmx",      "fxsr",
199                 "xmm",      "b26",     "b27",      "b28",      "b29",
200                 "3dnowext", "3dnow"
201         };
202
203         if (uio->uio_rw != UIO_READ)
204                 return (EOPNOTSUPP);
205
206         switch (cpu_class) {
207         case CPUCLASS_286:
208                 class = 2;
209                 break;
210         case CPUCLASS_386:
211                 class = 3;
212                 break;
213         case CPUCLASS_486:
214                 class = 4;
215                 break;
216         case CPUCLASS_586:
217                 class = 5;
218                 break;
219         case CPUCLASS_686:
220                 class = 6;
221                 break;
222         default:
223                 class = 0;
224                 break;
225         }
226
227         ps = psbuf;
228
229         error = kernel_sysctl(mib, 2, hwmodel, &modellen, NULL, 0, NULL);
230         if (error)
231                 strcpy(hwmodel, "unknown");
232
233         for (cpu = 0; cpu < ncpus; cpu++) {
234                 ps += ksprintf(ps,
235                     "processor\t: %d\n"
236                     "vendor_id\t: %.20s\n"
237                     "cpu family\t: %d\n"
238                     "model\t\t: %d\n"
239                     "model name\t: %s\n"
240                     "stepping\t: %d\n",
241                     cpu, cpu_vendor, class, cpu, hwmodel, cpu_id & 0xf);
242         }
243
244         ps += ksprintf(ps,
245                         "flags\t\t:");
246
247         if (cpu_vendor_id == CPU_VENDOR_AMD && (class < 6)) {
248                 flags[16] = "fcmov";
249         } else if (cpu_vendor_id == CPU_VENDOR_CYRIX) {
250                 flags[24] = "cxmmx";
251         }
252         
253         for (i = 0; i < 32; i++)
254                 if (cpu_feature & (1 << i))
255                         ps += ksprintf(ps, " %s", flags[i]);
256         ps += ksprintf(ps, "\n");
257         if (class >= 5) {
258                 ps += ksprintf(ps,
259                         "cpu MHz\t\t: %d.%02d\n"
260                         "bogomips\t: %d.%02d\n",
261                         (int)((tsc_frequency + 4999) / 1000000),
262                         (int)((tsc_frequency + 4999) / 10000) % 100,
263                         (int)((tsc_frequency + 4999) / 1000000),
264                         (int)((tsc_frequency + 4999) / 10000) % 100);
265         }
266         
267         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
268 }
269
270 static unsigned int
271 cpucnt(int offset)
272 {
273     int i;
274     int count = 0;
275
276     for (i = 0; i < ncpus; ++i) {
277         struct globaldata *gd = globaldata_find(i);
278         count += *(unsigned int *)((char *)&gd->gd_cnt + offset);
279     }
280     return(count);
281 }
282
283 static int
284 linprocfs_domounts_callback(struct mount *mp, void *data)
285 {
286         struct statfs *st;
287         struct sbuf *sb = (struct sbuf *)data;
288         char *to, *from, *fs;
289
290         st = &mp->mnt_stat;
291
292         from = st->f_mntfromname;
293         to = st->f_mntonname;
294         fs = st->f_fstypename;
295
296         if (!strcmp(st->f_fstypename, "linprocfs"))
297                 fs = "proc";
298         else if (!strcmp(st->f_fstypename, "ext2fs"))
299                 fs = "ext2";
300         else if (!strcmp(st->f_fstypename, "msdos"))
301                 fs = "vfat";
302         else if (!strcmp(st->f_fstypename, "msdosfs"))
303                 fs = "vfat";
304
305         sbuf_printf(sb, "%s %s %s %s", from, to, fs,
306             st->f_flags & MNT_RDONLY ? "ro" : "rw");
307
308 #define OPT_ADD(name, flag) if (st->f_flags & (flag)) sbuf_printf(sb, "," name)
309         OPT_ADD("sync",         MNT_SYNCHRONOUS);
310         OPT_ADD("noexec",       MNT_NOEXEC);
311         OPT_ADD("nosuid",       MNT_NOSUID);
312         OPT_ADD("nodev",        MNT_NODEV);
313         OPT_ADD("async",        MNT_ASYNC);
314         OPT_ADD("suiddir",      MNT_SUIDDIR);
315         OPT_ADD("nosymfollow",  MNT_NOSYMFOLLOW);
316         OPT_ADD("noatime",      MNT_NOATIME);
317 #undef OPT_ADD
318
319         sbuf_printf(sb, " 0 0\n");
320
321         return 0;
322 }
323
324 int
325 linprocfs_domounts(struct proc *curp, struct proc *p, struct pfsnode *pfs,
326                     struct uio *uio)
327 {
328         struct sbuf *sb;
329         int error;
330
331         sb = sbuf_new_auto();
332
333         error = mountlist_scan(linprocfs_domounts_callback, sb, MNTSCAN_FORWARD);
334
335         sbuf_finish(sb);
336         if (error == 0)
337                 error = uiomove_frombuf(sbuf_data(sb), sbuf_len(sb), uio);
338         sbuf_delete(sb);
339         return (error);
340 }
341
342 int
343 linprocfs_dostat(struct proc *curp, struct proc *p, struct pfsnode *pfs,
344                  struct uio *uio)
345 {
346         char *ps;
347         char psbuf[8192];
348         int cpu;
349
350         ps = psbuf;
351         ps += ksprintf(ps,
352                       "cpu %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64"\n",
353                       T2J(cpu_time.cp_user),
354                       T2J(cpu_time.cp_nice),
355                       T2J(cpu_time.cp_sys),
356                       T2J(cpu_time.cp_idle));
357
358         for (cpu = 0; cpu < ncpus; cpu++) {
359                 ps += ksprintf(ps,
360                       "cpu%d %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64"\n",
361                       cpu,
362                       T2J(cputime_percpu[cpu].cp_user),
363                       T2J(cputime_percpu[cpu].cp_nice),
364                       T2J(cputime_percpu[cpu].cp_sys),
365                       T2J(cputime_percpu[cpu].cp_idle));                        
366         }
367
368         ps += ksprintf(ps,
369                       "disk 0 0 0 0\n"
370                       "page %u %u\n"
371                       "swap %u %u\n"
372                       "intr %u\n"
373                       "ctxt %u\n"
374                       "btime %ld\n",
375                       cpucnt(offsetof(struct vmmeter, v_vnodepgsin)),
376                       cpucnt(offsetof(struct vmmeter, v_vnodepgsout)),
377                       cpucnt(offsetof(struct vmmeter, v_swappgsin)),
378                       cpucnt(offsetof(struct vmmeter, v_swappgsout)),
379                       cpucnt(offsetof(struct vmmeter, v_intr)),
380                       cpucnt(offsetof(struct vmmeter, v_swtch)),
381                       boottime.tv_sec);
382
383         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
384 }
385
386 int
387 linprocfs_douptime(struct proc *curp, struct proc *p, struct pfsnode *pfs,
388                    struct uio *uio)
389 {
390         char *ps;
391         char psbuf[64];
392         struct timeval tv;
393
394         getmicrouptime(&tv);
395         ps = psbuf;
396         ps += ksprintf(ps, "%ld.%02ld %"PRIu64".%02"PRIu64"\n",
397                       tv.tv_sec, tv.tv_usec / 10000,
398                       T2S(cpu_time.cp_idle), T2J(cpu_time.cp_idle) % 100);
399         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
400 }
401
402 int
403 linprocfs_doversion(struct proc *curp, struct proc *p, struct pfsnode *pfs,
404                     struct uio *uio)
405 {
406         char *ps;
407         size_t xlen;
408
409         ps = version; /* XXX not entirely correct */
410         for (xlen = 0; ps[xlen] != '\n'; ++xlen)
411                 /* nothing */ ;
412         ++xlen;
413         return (uiomove_frombuf(ps, xlen, uio));
414 }
415
416 #define B2P(x) ((x) >> PAGE_SHIFT)                      /* bytes to pages */
417 int
418 linprocfs_dostatm(struct proc *curp, struct proc *p, struct pfsnode *pfs,
419                     struct uio *uio)
420 {
421         char *ps, psbuf[1024];
422         struct kinfo_proc kp;
423
424         fill_kinfo_proc(p, &kp);
425
426         ps = psbuf;
427         ps += ksprintf(ps, "%d", p->p_pid);
428 #define PS_ADD(name, fmt, arg) ps += ksprintf(ps, " " fmt, arg)
429         PS_ADD("",      "%ju",  B2P((uintmax_t)(kp.kp_vm_tsize + kp.kp_vm_dsize + kp.kp_vm_ssize)));
430         PS_ADD("",      "%ju",  (uintmax_t)kp.kp_vm_rssize);
431         PS_ADD("",      "%ju",  (uintmax_t)0); /* XXX */
432         PS_ADD("",      "%ju",  (uintmax_t)kp.kp_vm_tsize);
433         PS_ADD("",      "%ju",  (uintmax_t)kp.kp_vm_dsize);
434         PS_ADD("",      "%ju",  (uintmax_t)kp.kp_vm_ssize);
435         PS_ADD("",      "%ju",  (uintmax_t)0); /* XXX */
436 #undef  PS_ADD
437         ps += ksprintf(ps, "\n");
438
439         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
440 }
441
442 #define P2K(x) ((x) << (PAGE_SHIFT - 10))               /* pages to kbytes */
443 int
444 linprocfs_doprocstat(struct proc *curp, struct proc *p, struct pfsnode *pfs,
445                      struct uio *uio)
446 {
447         vm_map_t map = &p->p_vmspace->vm_map;
448         vm_map_entry_t entry;
449         vm_offset_t start, end;
450         char *ps, psbuf[1024];
451         struct kinfo_proc kp;
452
453         fill_kinfo_proc(p, &kp);
454
455         start = 0;
456         end = 0;
457         vm_map_lock_read(map);
458         for (entry = map->header.next; entry != &map->header;
459                 entry = entry->next) {
460                 if (entry->maptype != VM_MAPTYPE_NORMAL &&
461                     entry->maptype != VM_MAPTYPE_VPAGETABLE) {
462                         continue;
463                 }
464                 /* Assuming that text is the first entry */
465                 start = entry->start;
466                 end = entry->end;
467         }
468         vm_map_unlock_read(map);
469
470         ps = psbuf;
471         ps += ksprintf(ps, "%d", p->p_pid);
472 #define PS_ADD(name, fmt, arg) ps += ksprintf(ps, " " fmt, arg)
473         PS_ADD("comm",          "(%s)", p->p_comm);
474         PS_ADD("statr",         "%c",   '0'); /* XXX */
475         PS_ADD("ppid",          "%d",   p->p_pptr ? p->p_pptr->p_pid : 0);
476         PS_ADD("pgrp",          "%d",   p->p_pgid);
477         PS_ADD("session",       "%d",   p->p_session->s_sid);
478         PS_ADD("tty",           "%d",   0); /* XXX */
479         PS_ADD("tpgid",         "%d",   kp.kp_tpgid); /* XXX */
480         PS_ADD("flags",         "%u",   0); /* XXX */
481         PS_ADD("minflt",        "%lu",  kp.kp_ru.ru_minflt); /* XXX */
482         PS_ADD("cminflt",       "%lu",  kp.kp_cru.ru_minflt); /* XXX */
483         PS_ADD("majflt",        "%lu",  kp.kp_ru.ru_majflt); /* XXX */
484         PS_ADD("cmajflt",       "%lu",  kp.kp_cru.ru_majflt); /* XXX */
485         PS_ADD("utime",         "%d",   T2J(tvtohz_high(&kp.kp_ru.ru_utime))); /* XXX */
486         PS_ADD("stime",         "%d",   T2J(tvtohz_high(&kp.kp_ru.ru_stime))); /* XXX */
487         PS_ADD("cutime",        "%d",   T2J(tvtohz_high(&kp.kp_cru.ru_utime))); /* XXX */
488         PS_ADD("cstime",        "%d",   T2J(tvtohz_high(&kp.kp_cru.ru_stime))); /* XXX */
489         PS_ADD("priority",      "%d",   0); /* XXX */
490         PS_ADD("nice",          "%d",   kp.kp_nice);
491         PS_ADD("timeout",       "%u",   0); /* XXX */
492         PS_ADD("itrealvalue",   "%u",   0); /* XXX */
493         PS_ADD("starttime",     "%d",   T2J(tvtohz_high(&kp.kp_start))); /* XXX */
494         PS_ADD("vsize",         "%ju",  P2K((uintmax_t)(kp.kp_vm_tsize + kp.kp_vm_dsize + kp.kp_vm_ssize))); /* XXX: not sure */
495         PS_ADD("rss",           "%ju",  (uintmax_t)kp.kp_vm_rssize); /* XXX */
496         PS_ADD("rlim",          "%lu",  kp.kp_ru.ru_maxrss); /* XXX */
497         PS_ADD("startcode",     "%lu",  start); /* XXX */
498         PS_ADD("endcode",       "%lu",  end); /* XXX */
499         PS_ADD("startstack",    "%lu",  (u_long)p->p_vmspace->vm_minsaddr); /* XXX */
500         PS_ADD("kstkesp",       "%u",   0); /* XXX */
501         PS_ADD("kstkeip",       "%u",   0); /* XXX */
502         PS_ADD("signal",        "%d",   0); /* XXX */
503         PS_ADD("blocked",       "%d",   0); /* XXX */
504         PS_ADD("sigignore",     "%d",   0); /* XXX */
505         PS_ADD("sigcatch",      "%d",   0); /* XXX */
506         PS_ADD("wchan",         "%u",   0); /* XXX */
507         PS_ADD("nswap",         "%lu",  kp.kp_ru.ru_nswap); /* XXX */
508         PS_ADD("cnswap",        "%lu",  kp.kp_cru.ru_nswap); /* XXX */
509         PS_ADD("exitsignal",    "%d",   0); /* XXX */
510         PS_ADD("processor",     "%u",   kp.kp_lwp.kl_cpuid); /* XXX */
511         PS_ADD("rt_priority",   "%u",   0); /* XXX */ /* >= 2.5.19 */
512         PS_ADD("policy",        "%u",   kp.kp_nice); /* XXX */ /* >= 2.5.19 */
513 #undef PS_ADD
514         ps += ksprintf(ps, "\n");
515         
516         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
517 }
518
519 /*
520  * Map process state to descriptive letter. Note that this does not
521  * quite correspond to what Linux outputs, but it's close enough.
522  */
523 static char *state_str[] = {
524         "? (unknown)",
525         "I (idle)",
526         "R (running)",
527         "T (stopped)",
528         "Z (zombie)",
529         "S (sleeping)",
530         "W (waiting)",
531         "M (mutex)"
532 };
533
534 int
535 linprocfs_doprocstatus(struct proc *curp, struct proc *p, struct pfsnode *pfs,
536                        struct uio *uio)
537 {
538         char *ps, psbuf[1024];
539         char *state;
540         int i;
541
542         ps = psbuf;
543
544         if (p->p_stat > sizeof state_str / sizeof *state_str)
545                 state = state_str[0];
546         else
547                 state = state_str[(int)p->p_stat];
548
549 #define PS_ADD ps += ksprintf
550         PS_ADD(ps, "Name:\t%s\n",         p->p_comm); /* XXX escape */
551         PS_ADD(ps, "State:\t%s\n",        state);
552
553         /*
554          * Credentials
555          */
556         PS_ADD(ps, "Pid:\t%d\n",          p->p_pid);
557         PS_ADD(ps, "PPid:\t%d\n",         p->p_pptr ? p->p_pptr->p_pid : 0);
558         PS_ADD(ps, "Uid:\t%d %d %d %d\n", p->p_ucred->cr_ruid,
559                                           p->p_ucred->cr_uid,
560                                           p->p_ucred->cr_svuid,
561                                           /* FreeBSD doesn't have fsuid */
562                                           p->p_ucred->cr_uid);
563         PS_ADD(ps, "Gid:\t%d %d %d %d\n", p->p_ucred->cr_rgid,
564                                           p->p_ucred->cr_gid,
565                                           p->p_ucred->cr_svgid,
566                                           /* FreeBSD doesn't have fsgid */
567                                           p->p_ucred->cr_gid);
568         PS_ADD(ps, "Groups:\t");
569         for (i = 0; i < p->p_ucred->cr_ngroups; i++)
570                 PS_ADD(ps, "%d ", p->p_ucred->cr_groups[i]);
571         PS_ADD(ps, "\n");
572         
573         /*
574          * Memory
575          */
576         PS_ADD(ps, "VmSize:\t%8lu kB\n",  B2K(p->p_vmspace->vm_map.size));
577         PS_ADD(ps, "VmLck:\t%8u kB\n",    P2K(0)); /* XXX */
578         /* XXX vm_rssize seems to always be zero, how can this be? */
579         PS_ADD(ps, "VmRss:\t%8u kB\n",    P2K(p->p_vmspace->vm_rssize));
580         PS_ADD(ps, "VmData:\t%8u kB\n",   P2K(p->p_vmspace->vm_dsize));
581         PS_ADD(ps, "VmStk:\t%8u kB\n",    P2K(p->p_vmspace->vm_ssize));
582         PS_ADD(ps, "VmExe:\t%8u kB\n",    P2K(p->p_vmspace->vm_tsize));
583         PS_ADD(ps, "VmLib:\t%8u kB\n",    P2K(0)); /* XXX */
584
585         /*
586          * Signal masks
587          *
588          * We support up to 128 signals, while Linux supports 32,
589          * but we only define 32 (the same 32 as Linux, to boot), so
590          * just show the lower 32 bits of each mask. XXX hack.
591          *
592          * NB: on certain platforms (Sparc at least) Linux actually
593          * supports 64 signals, but this code is a long way from
594          * running on anything but i386, so ignore that for now.
595          */
596         PS_ADD(ps, "SigPnd:\t%08x\n",     p->p_siglist.__bits[0]);
597         PS_ADD(ps, "SigBlk:\t%08x\n",     0); /* XXX */
598         PS_ADD(ps, "SigIgn:\t%08x\n",     p->p_sigignore.__bits[0]);
599         PS_ADD(ps, "SigCgt:\t%08x\n",     p->p_sigcatch.__bits[0]);
600         
601         /*
602          * Linux also prints the capability masks, but we don't have
603          * capabilities yet, and when we do get them they're likely to
604          * be meaningless to Linux programs, so we lie. XXX
605          */
606         PS_ADD(ps, "CapInh:\t%016x\n",    0);
607         PS_ADD(ps, "CapPrm:\t%016x\n",    0);
608         PS_ADD(ps, "CapEff:\t%016x\n",    0);
609 #undef PS_ADD
610         
611         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
612 }
613
614 int
615 linprocfs_doloadavg(struct proc *curp, struct proc *p,
616                     struct pfsnode *pfs, struct uio *uio)
617 {
618         char *ps, psbuf[512];
619
620         ps = psbuf;
621         ps += ksprintf(ps, "%d.%02d %d.%02d %d.%02d %d/%d %d\n",
622             (int)(averunnable.ldavg[0] / averunnable.fscale),
623             (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100),
624             (int)(averunnable.ldavg[1] / averunnable.fscale),
625             (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100),
626             (int)(averunnable.ldavg[2] / averunnable.fscale),
627             (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100),
628             1,                      /* number of running tasks */
629             -1,                     /* number of tasks */
630             1         /* The last pid, just kidding */
631         );
632         return(uiomove_frombuf(psbuf, ps - psbuf, uio));
633 }
634
635 int
636 linprocfs_donetdev(struct proc *curp, struct proc *p, struct pfsnode *pfs,
637                    struct uio *uio)
638 {
639         struct sbuf *sb;
640         char ifname[16]; /* XXX LINUX_IFNAMSIZ */
641         struct ifnet *ifp;
642         int error;
643
644         sb = sbuf_new_auto();
645
646         sbuf_printf(sb, "%6s|%58s|%s\n%6s|%58s|%58s\n",
647             "Inter-", "   Receive", "  Transmit", " face",
648             "bytes    packets errs drop fifo frame compressed",
649             "bytes    packets errs drop fifo frame compressed");
650
651         crit_enter();
652         TAILQ_FOREACH(ifp, &ifnet, if_link) {
653                 linux_ifname(ifp, ifname, sizeof ifname);
654                 sbuf_printf(sb, "%6.6s:", ifname);
655                 sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu ",
656                     0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
657                 sbuf_printf(sb, "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
658                     0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL);
659         }
660         crit_exit();
661         sbuf_finish(sb);
662         error = uiomove_frombuf(sbuf_data(sb), sbuf_len(sb), uio);
663         sbuf_delete(sb);
664         return (error);
665 }
666
667 int
668 linprocfs_dodevices(struct proc *curp, struct proc *p, struct pfsnode *pfs,
669                    struct uio *uio)
670 {
671         return 0;
672 }
673
674 int
675 linprocfs_doosrelease(struct proc *curp, struct proc *p, struct pfsnode *pfs,
676                    struct uio *uio)
677 {
678         char *osrelease;
679
680         osrelease = linux_get_osrelease(curthread);
681         return(uiomove_frombuf(osrelease, strlen(osrelease)+1, uio));
682 }
683
684 int
685 linprocfs_doostype(struct proc *curp, struct proc *p, struct pfsnode *pfs,
686                    struct uio *uio)
687 {
688         char *osname;
689
690         osname = linux_get_osname(curthread);
691         return(uiomove_frombuf(osname, strlen(osname)+1, uio));
692 }
693
694 int
695 linprocfs_dopidmax(struct proc *curp, struct proc *p, struct pfsnode *pfs,
696                    struct uio *uio)
697 {
698         char buf[32];
699
700         ksnprintf(buf, sizeof(buf), "%d", PID_MAX);
701         return(uiomove_frombuf(buf, strlen(buf)+1, uio));
702         return 0;
703 }
704
705 int
706 linprocfs_domaps(struct proc *curp, struct proc *p, struct pfsnode *pfs,
707              struct uio *uio)
708 {
709         int error;
710         vm_map_t map = &p->p_vmspace->vm_map;
711         vm_map_entry_t entry;
712         vm_ooffset_t off = 0;
713         struct sbuf *sb;
714         char *name = "", *freename = NULL;
715         struct vnode *vp;
716         struct vattr vat;
717         int major, minor;
718         ino_t ino;
719
720         if (uio->uio_rw != UIO_READ)
721                 return (EOPNOTSUPP);
722
723         sb = sbuf_new_auto();
724
725         error = 0;
726         vm_map_lock_read(map);
727         for (entry = map->header.next;
728                 ((uio->uio_resid > 0) && (entry != &map->header));
729                 entry = entry->next) {
730                 vm_object_t obj, tobj, lobj;
731                 vm_offset_t ostart;
732                 name = "";
733                 freename = NULL;
734                 ino = 0;
735                 if (entry->maptype != VM_MAPTYPE_NORMAL &&
736                     entry->maptype != VM_MAPTYPE_VPAGETABLE) {
737                         continue;
738                 }
739                 /*
740                  * Use map->hint as a poor man's ripout detector.
741                  */
742                 map->hint = entry;
743                 ostart = entry->start;
744
745                 /*
746                  * Find the bottom-most object, leaving the base object
747                  * and the bottom-most object held (but only one hold
748                  * if they happen to be the same).
749                  */
750                 obj = entry->object.vm_object;
751                 vm_object_hold(obj);
752
753                 lobj = obj;
754                 while (lobj && (tobj = lobj->backing_object) != NULL) {
755                         KKASSERT(tobj != obj);
756                         vm_object_hold(tobj);
757                         if (tobj == lobj->backing_object) {
758                                 if (lobj != obj) {
759                                         vm_object_lock_swap();
760                                         vm_object_drop(lobj);
761                                 }
762                                 lobj = tobj;
763                         } else {
764                                 vm_object_drop(tobj);
765                         }
766                 }
767
768                 if (lobj) {
769                         off = IDX_TO_OFF(lobj->size);
770                         if (lobj->type == OBJT_VNODE) {
771                                 vp = lobj->handle;
772                                 if (vp)
773                                         vref(vp);
774                         } else {
775                                 vp = NULL;
776                         }
777                         
778                         if (vp) {
779                                 vn_fullpath(curproc, vp, &name, &freename, 1);
780                                 vn_lock(vp, LK_SHARED | LK_RETRY);
781                                 VOP_GETATTR(vp, &vat);
782                                 ino = vat.va_fileid;
783                                 major = vat.va_rmajor;
784                                 minor = vat.va_rminor;
785                                 vput(vp);
786                         }
787                 }
788                 if (freename == NULL) {
789                         if (entry->eflags & MAP_ENTRY_STACK)
790                                 name = "[stack]";
791                 }
792
793                 if (lobj != obj)
794                         vm_object_drop(lobj);
795                 vm_object_drop(obj);
796
797                 /*
798                  * We cannot safely hold the map locked while accessing
799                  * userspace as a VM fault might recurse the locked map.
800                  */
801                 vm_map_unlock_read(map);
802
803                 /*
804                  * format:
805                  *  start-end access offset major:minor inode [.text file]
806                  */
807                 error = sbuf_printf(sb,
808                     "%08lx-%08lx %s%s%s%s %08llx %02x:%02x %llu%s%s\n",
809                     (u_long)entry->start, (u_long)entry->end,
810                     (entry->protection & VM_PROT_READ)?"r":"-",
811                     (entry->protection & VM_PROT_WRITE)?"w":"-",
812                     (entry->protection & VM_PROT_EXECUTE)?"x":"-",
813                     "p",
814                     off,        /* offset */
815                     0,          /* major */
816                     0,          /* minor */
817                     ino,        /* inode */
818                     (name && *name) ? "     " : "",
819                     name ? name : "");
820                 if (error == -1)
821                         error = ENOMEM;
822                 if (freename)
823                         kfree(freename, M_TEMP);
824
825                 vm_map_lock_read(map);
826                 if (error)
827                         break;
828
829                 /*
830                  * We use map->hint as a poor man's ripout detector.  If
831                  * it does not match the entry we set it to prior to
832                  * unlocking the map the entry MIGHT now be stale.  In
833                  * this case we do an expensive lookup to find our place
834                  * in the iteration again.
835                  */
836                 if (map->hint != entry) {
837                         vm_map_entry_t reentry;
838                 
839                         vm_map_lookup_entry(map, ostart, &reentry);
840                         entry = reentry;
841                 }
842         }
843         vm_map_unlock_read(map);
844
845         sbuf_finish(sb);
846         if (error == 0)
847                 error = uiomove_frombuf(sbuf_data(sb) + uio->uio_offset,
848                     sbuf_len(sb) - uio->uio_offset, uio);
849         sbuf_delete(sb);
850         return error;
851 }