387e73ea68431c45109ece626cb4dcd09b4db86a
[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  * $DragonFly: src/sys/emulation/linux/i386/linprocfs/linprocfs_misc.c,v 1.9 2004/12/22 11:01:49 joerg Exp $
43  */
44
45 #include <sys/param.h>
46 #include <sys/blist.h>
47 #include <sys/dkstat.h>
48 #include <sys/jail.h>
49 #include <sys/kernel.h>
50 #include <sys/proc.h>
51 #include <sys/resourcevar.h>
52 #include <sys/systm.h>
53 #include <sys/tty.h>
54 #include <sys/vnode.h>
55 #include <sys/lock.h>
56
57 #include <vm/vm.h>
58 #include <vm/pmap.h>
59 #include <vm/vm_map.h>
60 #include <vm/vm_param.h>
61 #include <vm/vm_object.h>
62 #include <vm/swap_pager.h>
63 #include <sys/vmmeter.h>
64 #include <sys/exec.h>
65
66 #include <machine/clock.h>
67 #include <machine/cputypes.h>
68 #include <machine/md_var.h>
69
70 #include "linprocfs.h"
71
72 /*
73  * Various conversion macros
74  */
75 #define T2J(x) (((x) * 100) / (stathz ? stathz : hz))   /* ticks to jiffies */
76 #define T2S(x) ((x) / (stathz ? stathz : hz))           /* ticks to seconds */
77 #define B2K(x) ((unsigned long)((x) >> 10))                     /* bytes to kbytes */
78 #define P2B(x) ((x) << PAGE_SHIFT)                      /* pages to bytes */
79 #define P2K(x) ((x) << (PAGE_SHIFT - 10))               /* pages to kbytes */
80
81 int
82 linprocfs_domeminfo(curp, p, pfs, uio)
83         struct proc *curp;
84         struct proc *p;
85         struct pfsnode *pfs;
86         struct uio *uio;
87 {
88         char *ps;
89         char psbuf[512];                /* XXX - conservative */
90         unsigned long memtotal;         /* total memory in bytes */
91         unsigned long memused;          /* used memory in bytes */
92         unsigned long memfree;          /* free memory in bytes */
93         unsigned long memshared;        /* shared memory ??? */
94         unsigned long buffers, cached;  /* buffer / cache memory ??? */
95         unsigned long long swaptotal;   /* total swap space in bytes */
96         unsigned long long swapused;    /* used swap space in bytes */
97         unsigned long long swapfree;    /* free swap space in bytes */
98         vm_object_t object;
99
100         if (uio->uio_rw != UIO_READ)
101                 return (EOPNOTSUPP);
102
103         memtotal = physmem * PAGE_SIZE;
104         /*
105          * The correct thing here would be:
106          *
107         memfree = vmstats.v_free_count * PAGE_SIZE;
108         memused = memtotal - memfree;
109          *
110          * but it might mislead linux binaries into thinking there
111          * is very little memory left, so we cheat and tell them that
112          * all memory that isn't wired down is free.
113          */
114         memused = vmstats.v_wire_count * PAGE_SIZE;
115         memfree = memtotal - memused;
116         if (swapblist == NULL) {
117                 swaptotal = 0;
118                 swapfree = 0;
119         } else {
120                 swaptotal = swapblist->bl_blocks * 1024LL; /* XXX why 1024? */
121                 swapfree = (unsigned long long)swapblist->bl_root->u.bmu_avail * PAGE_SIZE;
122         }
123         swapused = swaptotal - swapfree;
124         memshared = 0;
125         for (object = TAILQ_FIRST(&vm_object_list); object != NULL;
126             object = TAILQ_NEXT(object, object_list))
127                 if (object->shadow_count > 1)
128                         memshared += object->resident_page_count;
129         memshared *= PAGE_SIZE;
130         /*
131          * We'd love to be able to write:
132          *
133         buffers = bufspace;
134          *
135          * but bufspace is internal to vfs_bio.c and we don't feel
136          * like unstaticizing it just for linprocfs's sake.
137          */
138         buffers = 0;
139         cached = vmstats.v_cache_count * PAGE_SIZE;
140
141         ps = psbuf;
142         ps += sprintf(ps,
143                 "        total:    used:    free:  shared: buffers:  cached:\n"
144                 "Mem:  %lu %lu %lu %lu %lu %lu\n"
145                 "Swap: %llu %llu %llu\n"
146                 "MemTotal: %9lu kB\n"
147                 "MemFree:  %9lu kB\n"
148                 "MemShared:%9lu kB\n"
149                 "Buffers:  %9lu kB\n"
150                 "Cached:   %9lu kB\n"
151                 "SwapTotal:%9lu kB\n"
152                 "SwapFree: %9lu kB\n",
153                 memtotal, memused, memfree, memshared, buffers, cached,
154                 swaptotal, swapused, swapfree,
155                 B2K(memtotal), B2K(memfree),
156                 B2K(memshared), B2K(buffers), B2K(cached),
157                 B2K(swaptotal), B2K(swapfree));
158
159         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
160 }
161
162 int
163 linprocfs_docpuinfo(curp, p, pfs, uio)
164         struct proc *curp;
165         struct proc *p;
166         struct pfsnode *pfs;
167         struct uio *uio;
168 {
169         char *ps;
170         char psbuf[512];                /* XXX - conservative */
171         int class;
172         int i;
173 #if 0
174         extern char *cpu_model;         /* Yuck */
175 #endif
176         /* We default the flags to include all non-conflicting flags,
177            and the Intel versions of conflicting flags.  Note the space
178            before each name; that is significant, and should be 
179            preserved. */
180
181         static char *flags[] = {
182                 "fpu",      "vme",     "de",       "pse",      "tsc",
183                 "msr",      "pae",     "mce",      "cx8",      "apic",
184                 "sep",      "sep",     "mtrr",     "pge",      "mca",
185                 "cmov",     "pat",     "pse36",    "pn",       "b19",
186                 "b20",      "b21",     "mmxext",   "mmx",      "fxsr",
187                 "xmm",      "b26",     "b27",      "b28",      "b29",
188                 "3dnowext", "3dnow"
189         };
190
191         if (uio->uio_rw != UIO_READ)
192                 return (EOPNOTSUPP);
193
194         switch (cpu_class) {
195         case CPUCLASS_286:
196                 class = 2;
197                 break;
198         case CPUCLASS_386:
199                 class = 3;
200                 break;
201         case CPUCLASS_486:
202                 class = 4;
203                 break;
204         case CPUCLASS_586:
205                 class = 5;
206                 break;
207         case CPUCLASS_686:
208                 class = 6;
209                 break;
210         default:
211                 class = 0;
212                 break;
213         }
214
215         ps = psbuf;
216         ps += sprintf(ps,
217                         "processor\t: %d\n"
218                         "vendor_id\t: %.20s\n"
219                         "cpu family\t: %d\n"
220                         "model\t\t: %d\n"
221                         "stepping\t: %d\n",
222                         0, cpu_vendor, class, cpu, cpu_id & 0xf);
223
224         ps += sprintf(ps,
225                         "flags\t\t:");
226
227         if (!strcmp(cpu_vendor, "AuthenticAMD") && (class < 6)) {
228                 flags[16] = "fcmov";
229         } else if (!strcmp(cpu_vendor, "CyrixInstead")) {
230                 flags[24] = "cxmmx";
231         }
232         
233         for (i = 0; i < 32; i++)
234                 if (cpu_feature & (1 << i))
235                         ps += sprintf(ps, " %s", flags[i]);
236         ps += sprintf(ps, "\n");
237         if (class >= 5) {
238                 ps += sprintf(ps,
239                         "cpu MHz\t\t: %d.%02d\n"
240                         "bogomips\t: %d.%02d\n",
241                         (tsc_freq + 4999) / 1000000,
242                         ((tsc_freq + 4999) / 10000) % 100,
243                         (tsc_freq + 4999) / 1000000,
244                         ((tsc_freq + 4999) / 10000) % 100);
245         }
246         
247         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
248 }
249
250 static unsigned int
251 cpucnt(int offset)
252 {
253     int i;
254     int count = 0;
255
256     for (i = 0; i < ncpus; ++i) {
257         struct globaldata *gd = globaldata_find(i);
258         count += *(unsigned int *)((char *)&gd->gd_cnt + offset);
259     }
260     return(count);
261 }
262
263 int
264 linprocfs_dostat(curp, p, pfs, uio)
265         struct proc *curp;
266         struct proc *p;
267         struct pfsnode *pfs;
268         struct uio *uio;
269 {
270         char *ps;
271         char psbuf[512];
272
273         ps = psbuf;
274         ps += sprintf(ps,
275                       "cpu %ld %ld %ld %ld\n"
276                       "disk 0 0 0 0\n"
277                       "page %u %u\n"
278                       "swap %u %u\n"
279                       "intr %u\n"
280                       "ctxt %u\n"
281                       "btime %ld\n",
282                       T2J(cp_time.cp_user),
283                       T2J(cp_time.cp_nice),
284                       T2J(cp_time.cp_sys /*+ cp_time[CP_INTR]*/),
285                       T2J(cp_time.cp_idle),
286                       cpucnt(offsetof(struct vmmeter, v_vnodepgsin)),
287                       cpucnt(offsetof(struct vmmeter, v_vnodepgsout)),
288                       cpucnt(offsetof(struct vmmeter, v_swappgsin)),
289                       cpucnt(offsetof(struct vmmeter, v_swappgsout)),
290                       cpucnt(offsetof(struct vmmeter, v_intr)),
291                       cpucnt(offsetof(struct vmmeter, v_swtch)),
292                       boottime.tv_sec);
293         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
294 }
295
296 int
297 linprocfs_douptime(curp, p, pfs, uio)
298         struct proc *curp;
299         struct proc *p;
300         struct pfsnode *pfs;
301         struct uio *uio;
302 {
303         char *ps;
304         char psbuf[64];
305         struct timeval tv;
306
307         getmicrouptime(&tv);
308         ps = psbuf;
309         ps += sprintf(ps, "%ld.%02ld %ld.%02ld\n",
310                       tv.tv_sec, tv.tv_usec / 10000,
311                       T2S(cp_time.cp_idle), T2J(cp_time.cp_idle) % 100);
312         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
313 }
314
315 int
316 linprocfs_doversion(curp, p, pfs, uio)
317         struct proc *curp;
318         struct proc *p;
319         struct pfsnode *pfs;
320         struct uio *uio;
321 {
322         char *ps;
323         int xlen;
324
325         ps = version; /* XXX not entirely correct */
326         for (xlen = 0; ps[xlen] != '\n'; ++xlen)
327                 /* nothing */ ;
328         ++xlen;
329         return (uiomove_frombuf(ps, xlen, uio));
330 }
331
332 int
333 linprocfs_doprocstat(curp, p, pfs, uio)
334         struct proc *curp;
335         struct proc *p;
336         struct pfsnode *pfs;
337         struct uio *uio;
338 {
339         char *ps, psbuf[1024];
340
341         ps = psbuf;
342         ps += sprintf(ps, "%d", p->p_pid);
343 #define PS_ADD(name, fmt, arg) ps += sprintf(ps, " " fmt, arg)
344         PS_ADD("comm",          "(%s)", p->p_comm);
345         PS_ADD("statr",         "%c",   '0'); /* XXX */
346         PS_ADD("ppid",          "%d",   p->p_pptr ? p->p_pptr->p_pid : 0);
347         PS_ADD("pgrp",          "%d",   p->p_pgid);
348         PS_ADD("session",       "%d",   p->p_session->s_sid);
349         PS_ADD("tty",           "%d",   0); /* XXX */
350         PS_ADD("tpgid",         "%d",   0); /* XXX */
351         PS_ADD("flags",         "%u",   0); /* XXX */
352         PS_ADD("minflt",        "%u",   0); /* XXX */
353         PS_ADD("cminflt",       "%u",   0); /* XXX */
354         PS_ADD("majflt",        "%u",   0); /* XXX */
355         PS_ADD("cminflt",       "%u",   0); /* XXX */
356         PS_ADD("utime",         "%d",   0); /* XXX */
357         PS_ADD("stime",         "%d",   0); /* XXX */
358         PS_ADD("cutime",        "%d",   0); /* XXX */
359         PS_ADD("cstime",        "%d",   0); /* XXX */
360         PS_ADD("counter",       "%d",   0); /* XXX */
361         PS_ADD("priority",      "%d",   0); /* XXX */
362         PS_ADD("timeout",       "%u",   0); /* XXX */
363         PS_ADD("itrealvalue",   "%u",   0); /* XXX */
364         PS_ADD("starttime",     "%d",   0); /* XXX */
365         PS_ADD("vsize",         "%u",   0); /* XXX */
366         PS_ADD("rss",           "%u",   0); /* XXX */
367         PS_ADD("rlim",          "%u",   0); /* XXX */
368         PS_ADD("startcode",     "%u",   0); /* XXX */
369         PS_ADD("endcode",       "%u",   0); /* XXX */
370         PS_ADD("startstack",    "%u",   0); /* XXX */
371         PS_ADD("kstkesp",       "%u",   0); /* XXX */
372         PS_ADD("kstkeip",       "%u",   0); /* XXX */
373         PS_ADD("signal",        "%d",   0); /* XXX */
374         PS_ADD("blocked",       "%d",   0); /* XXX */
375         PS_ADD("sigignore",     "%d",   0); /* XXX */
376         PS_ADD("sigcatch",      "%d",   0); /* XXX */
377         PS_ADD("wchan",         "%u",   0); /* XXX */
378 #undef PS_ADD
379         ps += sprintf(ps, "\n");
380         
381         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
382 }
383
384 /*
385  * Map process state to descriptive letter. Note that this does not
386  * quite correspond to what Linux outputs, but it's close enough.
387  */
388 static char *state_str[] = {
389         "? (unknown)",
390         "I (idle)",
391         "R (running)",
392         "S (sleeping)",
393         "T (stopped)",
394         "Z (zombie)",
395         "W (waiting)",
396         "M (mutex)"
397 };
398
399 int
400 linprocfs_doprocstatus(curp, p, pfs, uio)
401         struct proc *curp;
402         struct proc *p;
403         struct pfsnode *pfs;
404         struct uio *uio;
405 {
406         char *ps, psbuf[1024];
407         char *state;
408         int i;
409
410         ps = psbuf;
411
412         if (p->p_stat > sizeof state_str / sizeof *state_str)
413                 state = state_str[0];
414         else
415                 state = state_str[(int)p->p_stat];
416
417 #define PS_ADD ps += sprintf
418         PS_ADD(ps, "Name:\t%s\n",         p->p_comm); /* XXX escape */
419         PS_ADD(ps, "State:\t%s\n",        state);
420
421         /*
422          * Credentials
423          */
424         PS_ADD(ps, "Pid:\t%d\n",          p->p_pid);
425         PS_ADD(ps, "PPid:\t%d\n",         p->p_pptr ? p->p_pptr->p_pid : 0);
426         PS_ADD(ps, "Uid:\t%d %d %d %d\n", p->p_ucred->cr_ruid,
427                                           p->p_ucred->cr_uid,
428                                           p->p_ucred->cr_svuid,
429                                           /* FreeBSD doesn't have fsuid */
430                                           p->p_ucred->cr_uid);
431         PS_ADD(ps, "Gid:\t%d %d %d %d\n", p->p_ucred->cr_rgid,
432                                           p->p_ucred->cr_gid,
433                                           p->p_ucred->cr_svgid,
434                                           /* FreeBSD doesn't have fsgid */
435                                           p->p_ucred->cr_gid);
436         PS_ADD(ps, "Groups:\t");
437         for (i = 0; i < p->p_ucred->cr_ngroups; i++)
438                 PS_ADD(ps, "%d ", p->p_ucred->cr_groups[i]);
439         PS_ADD(ps, "\n");
440         
441         /*
442          * Memory
443          */
444         PS_ADD(ps, "VmSize:\t%8lu kB\n",  B2K(p->p_vmspace->vm_map.size));
445         PS_ADD(ps, "VmLck:\t%8u kB\n",    P2K(0)); /* XXX */
446         /* XXX vm_rssize seems to always be zero, how can this be? */
447         PS_ADD(ps, "VmRss:\t%8u kB\n",    P2K(p->p_vmspace->vm_rssize));
448         PS_ADD(ps, "VmData:\t%8u kB\n",   P2K(p->p_vmspace->vm_dsize));
449         PS_ADD(ps, "VmStk:\t%8u kB\n",    P2K(p->p_vmspace->vm_ssize));
450         PS_ADD(ps, "VmExe:\t%8u kB\n",    P2K(p->p_vmspace->vm_tsize));
451         PS_ADD(ps, "VmLib:\t%8u kB\n",    P2K(0)); /* XXX */
452
453         /*
454          * Signal masks
455          *
456          * We support up to 128 signals, while Linux supports 32,
457          * but we only define 32 (the same 32 as Linux, to boot), so
458          * just show the lower 32 bits of each mask. XXX hack.
459          *
460          * NB: on certain platforms (Sparc at least) Linux actually
461          * supports 64 signals, but this code is a long way from
462          * running on anything but i386, so ignore that for now.
463          */
464         PS_ADD(ps, "SigPnd:\t%08x\n",     p->p_siglist.__bits[0]);
465         PS_ADD(ps, "SigBlk:\t%08x\n",     0); /* XXX */
466         PS_ADD(ps, "SigIgn:\t%08x\n",     p->p_sigignore.__bits[0]);
467         PS_ADD(ps, "SigCgt:\t%08x\n",     p->p_sigcatch.__bits[0]);
468         
469         /*
470          * Linux also prints the capability masks, but we don't have
471          * capabilities yet, and when we do get them they're likely to
472          * be meaningless to Linux programs, so we lie. XXX
473          */
474         PS_ADD(ps, "CapInh:\t%016x\n",    0);
475         PS_ADD(ps, "CapPrm:\t%016x\n",    0);
476         PS_ADD(ps, "CapEff:\t%016x\n",    0);
477 #undef PS_ADD
478         
479         return (uiomove_frombuf(psbuf, ps - psbuf, uio));
480 }
481
482 extern int nextpid;
483
484 int
485 linprocfs_doloadavg(struct proc *curp, struct proc *p,
486                     struct pfsnode *pfs, struct uio *uio)
487 {
488         char *ps, psbuf[512];
489
490         ps = psbuf;
491         ps += sprintf(ps, "%d.%02d %d.%02d %d.%02d %d/%d %d\n",
492             (int)(averunnable.ldavg[0] / averunnable.fscale),
493             (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100),
494             (int)(averunnable.ldavg[1] / averunnable.fscale),
495             (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100),
496             (int)(averunnable.ldavg[2] / averunnable.fscale),
497             (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100),
498             1,                      /* number of running tasks */
499             -1,                     /* number of tasks */
500             nextpid         /* The last pid */
501         );
502         return(uiomove_frombuf(psbuf, ps - psbuf, uio));
503 }
504