]> Zhao Yanbai Git Server - minix.git/commitdiff
MIB: initial tree population 46/3246/2
authorDavid van Moolenbroek <david@minix3.org>
Tue, 27 Oct 2015 21:32:33 +0000 (21:32 +0000)
committerLionel Sambuc <lionel.sambuc@gmail.com>
Wed, 13 Jan 2016 19:32:44 +0000 (20:32 +0100)
Change-Id: I28ef0a81a59faaf341bfc15178df89474779a136

13 files changed:
minix/drivers/tty/tty/tty.h
minix/include/minix/config.h
minix/include/minix/dmap.h
minix/lib/libminc/Makefile
minix/lib/libsys/getuptime.c
minix/servers/mib/Makefile
minix/servers/mib/hw.c [new file with mode: 0644]
minix/servers/mib/kern.c
minix/servers/mib/main.c
minix/servers/mib/mib.h
minix/servers/mib/proc.c [new file with mode: 0644]
minix/servers/mib/vm.c [new file with mode: 0644]
minix/usr.bin/trace/service/mib.c

index 12c42526ad4048ea0a647ebaa4cc5908271ad02f..c079aeab8a0bc5ccda60a25ef57149cafa8c1959 100644 (file)
@@ -4,7 +4,7 @@
 #include <minix/timers.h>
 
 /* First minor numbers for the various classes of TTY devices. */
-#define CONS_MINOR        0
+/* CONS_MINOR is defined in minix/dmap.h */
 #define LOG_MINOR        15
 #define RS232_MINOR      16
 #define VIDEO_MINOR     125
index 1e543272d6ad0b45aaf3f3bd4de18405512c6318..27631f5492883ad4788b48f5c2144fc861608003 100644 (file)
@@ -3,7 +3,8 @@
 
 /* Minix release and version numbers. */
 #define OS_NAME "Minix"
-#define OS_RELEASE "3.3.0"
+#define OS_RELEASE "3.3.0"     /* 3.m.p */
+#define OS_REV 303000000       /* see NetBSD sys/param.h: 3mm00pp00 */
 #define OS_CONFIG "GENERIC"
 #define OS_VERSION OS_NAME " " OS_RELEASE " (" OS_CONFIG ")"
 
index 00ae632cade33b003f95e974c3c3fcf53652f2ec..5cf821478dde1159c795e8a9c6b73c8fc3a73181 100644 (file)
@@ -91,6 +91,9 @@
 #  define IMGRD_DEV               6    /* minor device for /dev/imgrd */
 #  define RAM_DEV_FIRST                   7    /* first minor device for /dev/ram* */
 
+/* Minor device numbers for the TTY driver. */
+#  define CONS_MINOR              0    /* console device */
+
 #define CTRLR(n) ((n)==0 ? 3 : (8 + 2*((n)-1)))        /* magic formula */
 
 /* Minor device numbers for log driver. */
index 4375cf88b2d48a10a894da59a2b119accb4b7a59..804c8b35833e4658ea9f75ff44f814402025e6ed 100644 (file)
@@ -288,7 +288,7 @@ CLEANFILES+= ${f:C/\.o/.bc/}
        init.o kernel_utils.o link.o loadname.o lseek.o _mcontext.o mknod.o \
        mmap.o nanosleep.o open.o pread.o pwrite.o read.o sbrk.o \
        select.o setuid.o sigprocmask.o stack_utils.o stat.o stime.o \
-       syscall.o _ucontext.o umask.o unlink.o wait4.o write.o \
+       svrctl.o syscall.o _ucontext.o umask.o unlink.o wait4.o write.o \
        kill.o
 ${f} ${f:C/\.o/.bc/}:  ${LIBMINIXCDIR}/sys/${f:C/\.o/.c/}
 OBJS+= ${f}
index 6b7f687704a12114e2ce162fc2cbcdace9b3c860..298c787fcd0856fabfcc08cd0365b85ce728bb5c 100644 (file)
@@ -13,9 +13,12 @@ getuptime(clock_t * uptime, clock_t * realtime, time_t * boottime)
        minix_kerninfo = get_minix_kerninfo();
 
        /* We assume atomic 32-bit field retrieval.  TODO: 64-bit support. */
-       *uptime = minix_kerninfo->kclockinfo->uptime;
-       *realtime = minix_kerninfo->kclockinfo->realtime;
-       *boottime = minix_kerninfo->kclockinfo->boottime;
+       if (uptime != NULL)
+               *uptime = minix_kerninfo->kclockinfo->uptime;
+       if (realtime != NULL)
+               *realtime = minix_kerninfo->kclockinfo->realtime;
+       if (boottime != NULL)
+               *boottime = minix_kerninfo->kclockinfo->boottime;
 
        return OK;
 }
index ca4de71514796173623f3301e7a6846919ee3207..cc5b04f5cccdd09d94aab30a57db37a92d95578f 100644 (file)
@@ -1,7 +1,9 @@
 # Makefile for the Management Information Base (MIB) server
 
 PROG=  mib
-SRCS=  main.c tree.c kern.c minix.c
+SRCS=  main.c tree.c kern.c vm.c hw.c proc.c minix.c
+
+CPPFLAGS+= -I${NETBSDSRCDIR}/minix
 
 DPADD+=        ${LIBSYS}
 LDADD+=        -lsys
diff --git a/minix/servers/mib/hw.c b/minix/servers/mib/hw.c
new file mode 100644 (file)
index 0000000..63de32c
--- /dev/null
@@ -0,0 +1,140 @@
+/* MIB service - hw.c - implementation of the CTL_HW subtree */
+
+#include "mib.h"
+
+#if defined(__i386__)
+static const char mach[] = "i386";     /* machine (cpu) type */
+static const char arch[] = "i386";     /* architecture */
+#elif defined(__arm__)
+static const char mach[] = "evbarm";   /* machine (cpu) type */
+static const char arch[] = "evbarm";   /* architecture */
+#else
+#error "unknown machine architecture"
+#endif
+
+/*
+ * Implementation of CTL_HW HW_PHYSMEM/HW_PHYSMEM64.
+ */
+static ssize_t
+mib_hw_physmem(struct mib_call * call __unused, struct mib_node * node,
+       struct mib_oldp * oldp, struct mib_newp * newp __unused)
+{
+       struct vm_stats_info vsi;
+       u_quad_t physmem64;
+       unsigned int physmem;
+
+       if (vm_info_stats(&vsi) != OK)
+               return EINVAL;
+
+       physmem64 = (u_quad_t)vsi.vsi_total * vsi.vsi_pagesize;
+
+       if (node->node_size == sizeof(int)) {
+               if (physmem64 > UINT_MAX)
+                       physmem = UINT_MAX;
+               else
+                       physmem = (unsigned int)physmem64;
+
+               return mib_copyout(oldp, 0, &physmem, sizeof(physmem));
+       } else
+               return mib_copyout(oldp, 0, &physmem64, sizeof(physmem64));
+}
+
+/*
+ * Implementation of CTL_HW HW_USERMEM/HW_USERMEM64.
+ */
+static ssize_t
+mib_hw_usermem(struct mib_call * call __unused, struct mib_node * node,
+       struct mib_oldp * oldp, struct mib_newp * newp __unused)
+{
+       struct vm_stats_info vsi;
+       struct vm_usage_info vui;
+       u_quad_t usermem64;
+       unsigned int usermem;
+
+       if (vm_info_stats(&vsi) != OK)
+               return EINVAL;
+
+       usermem64 = (u_quad_t)vsi.vsi_total * vsi.vsi_pagesize;
+
+       if (vm_info_usage(KERNEL, &vui) != OK)
+               return EINVAL;
+
+       if (usermem64 >= vui.vui_total)
+               usermem64 -= vui.vui_total;
+       else
+               usermem64 = 0;
+
+       if (node->node_size == sizeof(int)) {
+               if (usermem64 > UINT_MAX)
+                       usermem = UINT_MAX;
+               else
+                       usermem = (unsigned int)usermem64;
+
+               return mib_copyout(oldp, 0, &usermem, sizeof(usermem));
+       } else
+               return mib_copyout(oldp, 0, &usermem64, sizeof(usermem64));
+}
+
+/*
+ * Implementation of CTL_HW HW_NCPUONLINE.
+ */
+static ssize_t
+mib_hw_ncpuonline(struct mib_call * call __unused,
+       struct mib_node * node __unused, struct mib_oldp * oldp,
+       struct mib_newp * newp __unused)
+{
+       struct machine machine;
+       int ncpuonline;
+
+       if (sys_getmachine(&machine) != OK)
+               return EINVAL;
+
+       ncpuonline = machine.processors_count;
+
+       return mib_copyout(oldp, 0, &ncpuonline, sizeof(ncpuonline));
+}
+
+/* The CTL_HW nodes. */
+static struct mib_node mib_hw_table[] = {
+/* 1*/ [HW_MACHINE]            = MIB_STRING(_P | _RO, mach, "machine",
+                                   "Machine class"),
+/* 2*/ /* HW_MODEL: not yet supported */
+/* 3*/ [HW_NCPU]               = MIB_INT(_P | _RO, CONFIG_MAX_CPUS,
+                                   "ncpu", "Number of CPUs configured"),
+/* 4*/ [HW_BYTEORDER]          = MIB_INT(_P | _RO, BYTE_ORDER, "byteorder",
+                                   "System byte order"),
+/* 5*/ [HW_PHYSMEM]            = MIB_FUNC(_P | _RO | CTLFLAG_UNSIGNED |
+                                   CTLTYPE_INT, sizeof(int), mib_hw_physmem,
+                                   "physmem", "Bytes of physical memory"),
+/* 6*/ [HW_USERMEM]            = MIB_FUNC(_P | _RO | CTLFLAG_UNSIGNED |
+                                   CTLTYPE_INT, sizeof(int), mib_hw_usermem,
+                                   "usermem", "Bytes of non-kernel memory"),
+/* 7*/ [HW_PAGESIZE]           = MIB_INT(_P | _RO, PAGE_SIZE, "pagesize",
+                                   "Software page size"),
+/* 8*/ /* HW_DISKNAMES: not yet supported */
+/* 9*/ /* HW_IOSTATS: not yet supported */
+/*10*/ [HW_MACHINE_ARCH]       = MIB_STRING(_P | _RO, arch, "machine_arch",
+                                   "Machine CPU class"),
+/*11*/ /* HW_ALIGNBYTES: not yet supported */
+/*12*/ /* HW_CNMAGIC: not yet supported */
+/*13*/ [HW_PHYSMEM64]          = MIB_FUNC(_P | _RO | CTLTYPE_QUAD,
+                                   sizeof(u_quad_t), mib_hw_physmem,
+                                   "physmem64", "Bytes of physical memory"),
+/*14*/ [HW_USERMEM64]          = MIB_FUNC(_P | _RO | CTLTYPE_QUAD,
+                                   sizeof(u_quad_t), mib_hw_usermem,
+                                   "usermem64", "Bytes of non-kernel memory"),
+/*15*/ /* HW_IOSTATNAMES: not yet supported */
+/*16*/ [HW_NCPUONLINE]         = MIB_FUNC(_P | _RO | CTLTYPE_INT, sizeof(int),
+                                   mib_hw_ncpuonline, "ncpuonline",
+                                   "Number of CPUs online"),
+};
+
+/*
+ * Initialize the CTL_HW subtree.
+ */
+void
+mib_hw_init(struct mib_node * node)
+{
+
+       MIB_INIT_ENODE(node, mib_hw_table);
+}
index 688c3ca627d01e4f9004087c43443ea99c9c7abe..e1565a23000a5b37aaa9bb086a57cca0d010d5cd 100644 (file)
 
 #include "mib.h"
 
+#include <sys/svrctl.h>
+#include <minix/sysinfo.h>
+#include <machine/partition.h>
+
+#include "servers/vfs/const.h"
+#include "servers/vfs/dmap.h"
+
+static char hostname[MAXHOSTNAMELEN], domainname[MAXHOSTNAMELEN];
+
+/*
+ * Verification for CTL_KERN KERN_SECURELVL.
+ */
+static int
+mib_kern_securelvl(struct mib_call * call __unused, struct mib_node * node,
+       void * ptr, size_t size __unused)
+{
+       int v;
+
+       memcpy(&v, ptr, sizeof(v));
+
+       /*
+        * Only ever allow the security level to be increased.  This is a mock
+        * implementation.  TODO: implement actual support for security levels.
+        */
+       return (v >= node->node_int);
+}
+
+/*
+ * Implementation of CTL_KERN KERN_CLOCKRATE.
+ */
+static ssize_t
+mib_kern_clockrate(struct mib_call * call __unused,
+       struct mib_node * node __unused, struct mib_oldp * oldp,
+       struct mib_newp * newp __unused)
+{
+       struct clockinfo clockinfo;
+
+       memset(&clockinfo, 0, sizeof(clockinfo));
+
+       clockinfo.hz = sys_hz();
+       clockinfo.tick = 1000000 / clockinfo.hz;
+       clockinfo.profhz = clockinfo.hz;
+       clockinfo.stathz = clockinfo.hz;
+
+       /*
+        * Number of microseconds that can be corrected per clock tick through
+        * adjtime(2).  The kernel allows correction of one clock tick per
+        * clock tick, which means it should be the same as .tick.. I think.
+        * TODO: get this from the kernel itself.
+        */
+       clockinfo.tickadj = clockinfo.tick;
+
+       return mib_copyout(oldp, 0, &clockinfo, sizeof(clockinfo));
+}
+
+/*
+ * Implementation of CTL_KERN KERN_PROFILING.
+ */
+static ssize_t
+mib_kern_profiling(struct mib_call * call __unused,
+       struct mib_node * node __unused, struct mib_oldp * oldp __unused,
+       struct mib_newp * newp __unused)
+{
+
+       /* As per sysctl(7).  We have a different profiling API. */
+       return EOPNOTSUPP;
+}
+
+/*
+ * Implementation of CTL_KERN KERN_HARDCLOCK_TICKS.
+ */
+static ssize_t
+mib_kern_hardclock_ticks(struct mib_call * call __unused,
+       struct mib_node * node __unused, struct mib_oldp * oldp,
+       struct mib_newp * newp __unused)
+{
+       int uptime;
+
+       /*
+        * The number of hardclock (hardware clock driver) ticks is what we
+        * call the number of monotonic clock ticks AKA the uptime clock ticks.
+        */
+       uptime = (int)getticks();
+
+       return mib_copyout(oldp, 0, &uptime, sizeof(uptime));
+}
+
+/*
+ * Implementation of CTL_KERN KERN_ROOT_DEVICE.
+ */
+static ssize_t
+mib_kern_root_device(struct mib_call * call __unused,
+       struct mib_node * node __unused, struct mib_oldp * oldp,
+       struct mib_newp * newp __unused)
+{
+       char name[PATH_MAX];
+       struct sysgetenv sysgetenv;
+
+       sysgetenv.key = __UNCONST("rootdevname");
+       sysgetenv.keylen = strlen(sysgetenv.key) + 1;
+       sysgetenv.val = name;
+       sysgetenv.vallen = sizeof(name);
+
+       if (svrctl(PMGETPARAM, &sysgetenv) != 0)
+               return EINVAL;
+
+       name[MIN(sysgetenv.vallen, sizeof(name) - 1)] = '\0';
+
+       return mib_copyout(oldp, 0, name, strlen(name) + 1);
+}
+
+/*
+ * Implementation of CTL_KERN KERN_CCPU.
+ */
+static ssize_t
+mib_kern_ccpu(struct mib_call * call __unused,
+       struct mib_node * node __unused, struct mib_oldp * oldp,
+       struct mib_newp * newp __unused)
+{
+       int ccpu;
+
+       ccpu = (int)cpuavg_getccpu();
+
+       return mib_copyout(oldp, 0, &ccpu, sizeof(ccpu));
+}
+
+/*
+ * Implementation of CTL_KERN KERN_CP_TIME.
+ */
+static ssize_t
+mib_kern_cp_time(struct mib_call * call, struct mib_node * node __unused,
+       struct mib_oldp * oldp, struct mib_newp * newp __unused)
+{
+       uint64_t ticks[MINIX_CPUSTATES], sum[MINIX_CPUSTATES];
+       unsigned int cpu;
+       int i, r, do_sum;
+
+       /*
+        * If a subnode is provided, it identifies the CPU number for which to
+        * return information.  If no subnode is provided, but a size is given
+        * that allows returning information for all CPUs, return information
+        * for all of them in an array.  If no such size is given either,
+        * return a summation of all CPU statistics.  Both we and the kernel
+        * are considering the number of configured CPUs (hw.ncpu).
+        */
+       if (call->call_namelen > 1)
+               return EINVAL;
+
+       if (call->call_namelen == 1) {
+               /* Do not bother saving on this call if oldp is NULL. */
+               if ((r = sys_getcputicks(ticks, call->call_name[0])) != OK)
+                       return r;
+
+               return mib_copyout(oldp, 0, ticks, sizeof(ticks));
+       }
+
+       if (oldp == NULL)
+               return sizeof(ticks); /* implying a summation request */
+
+       do_sum = (mib_getoldlen(oldp) == sizeof(ticks));
+
+       if (do_sum)
+               memset(&sum, 0, sizeof(sum));
+
+       for (cpu = 0; cpu < CONFIG_MAX_CPUS; cpu++) {
+               if ((r = sys_getcputicks(ticks, cpu)) != OK)
+                       return r;
+
+               if (do_sum) {
+                       for (i = 0; i < MINIX_CPUSTATES; i++)
+                               sum[i] += ticks[i];
+               } else {
+                       if ((r = mib_copyout(oldp, cpu * sizeof(ticks), ticks,
+                           sizeof(ticks))) < 0)
+                               return r;
+               }
+       }
+
+       if (do_sum)
+               return mib_copyout(oldp, 0, sum, sizeof(sum));
+       else
+               return cpu * sizeof(ticks);
+}
+
+/*
+ * Implementation of CTL_KERN KERN_CONSDEV.
+ */
+static ssize_t
+mib_kern_consdev(struct mib_call * call __unused,
+       struct mib_node * node __unused, struct mib_oldp * oldp,
+       struct mib_newp * newp __unused)
+{
+       dev_t dev;
+
+       dev = makedev(TTY_MAJOR, CONS_MINOR);
+
+       /* No support for legacy 32-bit requests. */
+       return mib_copyout(oldp, 0, &dev, sizeof(dev));
+}
+
+/*
+ * Verification for CTL_KERN KERN_FORKFSLEEP.
+ */
+static int
+mib_kern_forkfsleep(struct mib_call * call __unused,
+       struct mib_node * node __unused, void * ptr, size_t size __unused)
+{
+       int v;
+
+       memcpy(&v, ptr, sizeof(v));
+
+       return (v >= 0 && v <= MAXSLP * 1000); /* rules from NetBSD */
+}
+
+/*
+ * Implementation of CTL_KERN KERN_DRIVERS.
+ */
+static ssize_t
+mib_kern_drivers(struct mib_call * call __unused,
+       struct mib_node * node __unused, struct mib_oldp * oldp,
+       struct mib_newp * newp __unused)
+{
+       struct dmap dmap_tab[NR_DEVICES];
+       struct kinfo_drivers drivers[NR_DEVICES + 1];
+       unsigned int count;
+       devmajor_t maj;
+
+       /*
+        * On MINIX3, we list only drivers that are actually running.
+        */
+
+       if (getsysinfo(VFS_PROC_NR, SI_DMAP_TAB, dmap_tab,
+           sizeof(dmap_tab)) != OK)
+               return EINVAL;
+
+       count = 0;
+
+       /*
+        * Compatibility hack.  NetBSD userland expects that the name of the
+        * PTY driver is "pts".  Add an extra entry for this purpose if needed.
+        */
+       if (dmap_tab[PTY_MAJOR].dmap_driver != NONE &&
+           strcmp(dmap_tab[PTY_MAJOR].dmap_label, "pts")) {
+               if (mib_inrange(oldp, 0)) {
+                       memset(&drivers[0], 0, sizeof(drivers[0]));
+                       strlcpy(drivers[count].d_name, "pts",
+                           sizeof(drivers[0].d_name));
+                       drivers[count].d_bmajor = -1;
+                       drivers[count].d_cmajor = PTY_MAJOR;
+               }
+               count++;
+       }
+
+       for (maj = 0; maj < NR_DEVICES; maj++) {
+               if (dmap_tab[maj].dmap_driver == NONE)
+                       continue;
+
+               if (mib_inrange(oldp, sizeof(drivers[0]) * count)) {
+                       memset(&drivers[count], 0, sizeof(drivers[0]));
+
+                       strlcpy(drivers[count].d_name,
+                           dmap_tab[maj].dmap_label,
+                           sizeof(drivers[0].d_name));
+
+                       /*
+                        * We do not know whether the device is a block device,
+                        * character device, or both.  In any case, a driver
+                        * has only one major number.
+                        */
+                       drivers[count].d_bmajor = maj;
+                       drivers[count].d_cmajor = maj;
+               }
+               count++;
+       }
+
+       return mib_copyout(oldp, 0, drivers, count * sizeof(drivers[0]));
+}
+
+/*
+ * Implementation of CTL_KERN KERN_BOOTTIME.
+ */
+static ssize_t
+mib_kern_boottime(struct mib_call * call __unused,
+       struct mib_node * node __unused, struct mib_oldp * oldp,
+       struct mib_newp * newp __unused)
+{
+       struct timeval tv;
+
+       memset(&tv, 0, sizeof(tv));
+
+       if (getuptime(NULL, NULL, &tv.tv_sec) != OK)
+               return EINVAL;
+
+       return mib_copyout(oldp, 0, &tv, sizeof(tv));
+}
+
+/* The CTL_KERN KERN_SYSVIPC nodes. */
+static struct mib_node mib_kern_ipc_table[] = {
+/* 1*/ /* KERN_SYSVIPC_INFO: not yet supported */
+/* 2*/ [KERN_SYSVIPC_MSG]      = MIB_INT(_P | _RO, 0, "sysvmsg", "System V "
+                                   "style message support available"),
+/* 3*/ [KERN_SYSVIPC_SEM]      = MIB_INT(_P | _RO, 1, "sysvsem", "System V "
+                                   "style semaphore support available"),
+/* 4*/ [KERN_SYSVIPC_SHM]      = MIB_INT(_P | _RO, 1, "sysvshm", "System V "
+                                   "style shared memory support available"),
+/* 5*/ /* KERN_SYSVIPC_SHMMAX: not yet supported */
+/* 6*/ /* KERN_SYSVIPC_SHMMNI: not yet supported */
+/* 7*/ /* KERN_SYSVIPC_SHMSEG: not yet supported */
+/* 8*/ /* KERN_SYSVIPC_SHMMAXPGS: not yet supported */
+/* 9*/ /* KERN_SYSVIPC_SHMUSEPHYS: not yet supported */
+       /* In addition, NetBSD has a number of dynamic nodes here. */
+};
+
+/* The CTL_KERN nodes. */
 static struct mib_node mib_kern_table[] = {
+/* 1*/ [KERN_OSTYPE]           = MIB_STRING(_P | _RO, OS_NAME, "ostype",
+                                   "Operating system type"),
+/* 2*/ [KERN_OSRELEASE]        = MIB_STRING(_P | _RO, OS_RELEASE, "osrelease",
+                                   "Operating system release"),
+/* 3*/ [KERN_OSREV]            = MIB_INT(_P | _RO , OS_REV, "osrevision",
+                                   "Operating system revision"),
+/* 4*/ [KERN_VERSION]          = MIB_STRING(_P | _RO, OS_VERSION, "version",
+                                   "Kernel version"),
+/* 5*/ [KERN_MAXVNODES]        = MIB_INT(_P | _RO, NR_VNODES, "maxvnodes",
+                                   "Maximum number of vnodes"),
+/* 6*/ [KERN_MAXPROC]          = MIB_INT(_P | _RO, NR_PROCS, "maxproc",
+                                   "Maximum number of simultaneous "
+                                   "processes"),
+/* 7*/ [KERN_MAXFILES]         = MIB_INT(_P | _RO, NR_VNODES, "maxfiles",
+                                   "Maximum number of open files"),
 /* 8*/ [KERN_ARGMAX]           = MIB_INT(_P | _RO, ARG_MAX, "argmax",
                                    "Maximum number of bytes of arguments to "
                                    "execve(2)"),
+/* 9*/ [KERN_SECURELVL]        = MIB_INTV(_P | _RW, -1, mib_kern_securelvl,
+                                   "securelevel", "System security level"),
+/*10*/ [KERN_HOSTNAME]         = MIB_STRING(_P | _RW, hostname, "hostname",
+                                   "System hostname"),
+/*11*/ [KERN_HOSTID]           = MIB_INT(_P | _RW | CTLFLAG_HEX, 0, "hostid",
+                                   "System host ID number"),
+/*12*/ [KERN_CLOCKRATE]        = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT,
+                                   sizeof(struct clockinfo),
+                                   mib_kern_clockrate, "clockrate",
+                                   "Kernel clock rates"),
+/*13*/ /* KERN_VNODE: not yet implemented */
+/*14*/ /* KERN_PROC: not yet implemented */
+/*15*/ /* KERN_FILE: not yet implemented */
+/*16*/ [KERN_PROF]             = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
+                                   mib_kern_profiling, "profiling",
+                                   "Profiling information (not available)"),
+/*17*/ [KERN_POSIX1]           = MIB_INT(_P | _RO, _POSIX_VERSION,
+                                   "posix1version", "Version of ISO/IEC 9945 "
+                                   "(POSIX 1003.1) with which the operating "
+                                   "system attempts to comply"),
+/*18*/ [KERN_NGROUPS]          = MIB_INT(_P | _RO, NGROUPS_MAX, "ngroups",
+                                   "Maximum number of supplemental groups"),
+/*19*/ [KERN_JOB_CONTROL]      = MIB_INT(_P | _RO, 0, "job_control",
+                                   "Whether job control is available"),
+/*20*/ [KERN_SAVED_IDS]        = MIB_INT(_P | _RO, 0, "saved_ids",
+                                   "Whether POSIX saved set-group/user ID is "
+                                   "available"),
+/*21*/ /* KERN_OBOOTTIME: obsolete */
+/*22*/ [KERN_DOMAINNAME]       = MIB_STRING(_P | _RW, domainname,
+                                   "domainname", "YP domain name"),
+/*23*/ [KERN_MAXPARTITIONS]    = MIB_INT(_P | _RO, NR_PARTITIONS,
+                                   "maxpartitions", "Maximum number of "
+                                   "partitions allowed per disk"),
+/*24*/ /* KERN_RAWPARTITION: incompatible with our device node scheme */
+/*25*/ /* KERN_NTPTIME: not yet supported */
+/*26*/ /* KERN_TIMEX: not yet supported */
+/*27*/ /* KERN_AUTONICETIME: not yet supported */
+/*28*/ /* KERN_AUTONICEVAL: not yet supported */
+/*29*/ [KERN_RTC_OFFSET]       = MIB_INT(_P | _RW, 0, "rtc_offset", "Offset "
+                                   "of real time clock from UTC in minutes"),
+/*30*/ [KERN_ROOT_DEVICE]      = MIB_FUNC(_P | _RO | CTLTYPE_STRING, 0,
+                                   mib_kern_root_device, "root_device",
+                                   "Name of the root device"),
+/*31*/ [KERN_MSGBUFSIZE]       = MIB_INT(_P | _RO, DIAG_BUFSIZE, "msgbufsize",
+                                   "Size of the kernel message buffer"),
+/*32*/ [KERN_FSYNC]            = MIB_INT(_P | _RO, 1, "fsync", "Whether the "
+                                   "POSIX 1003.1b File Synchronization Option"
+                                   " is available on this system"),
+/*33*/ /* KERN_OLDSYSVMSG: obsolete */
+/*34*/ /* KERN_OLDSYSVSEM: obsolete */
+/*35*/ /* KERN_OLDSYSVSHM: obsolete */
+/*36*/ /* KERN_OLDSHORTCORENAME: obsolete */
+/*37*/ [KERN_SYNCHRONIZED_IO]  = MIB_INT(_P | _RO, 0, "synchronized_io",
+                                   "Whether the POSIX 1003.1b Synchronized "
+                                   "I/O Option is available on this system"),
+/*38*/ [KERN_IOV_MAX]          = MIB_INT(_P | _RO, IOV_MAX, "iov_max",
+                                   "Maximum number of iovec structures per "
+                                   "process"),
+/*39*/ /* KERN_MBUF: not yet supported */
+/*40*/ [KERN_MAPPED_FILES]     = MIB_INT(_P | _RO, 1, "mapped_files",
+                                   "Whether the POSIX 1003.1b Memory Mapped "
+                                   "Files Option is available on this "
+                                   "system"),
+/*41*/ [KERN_MEMLOCK]          = MIB_INT(_P | _RO, 0, "memlock", "Whether "
+                                   "the POSIX 1003.1b Process Memory Locking "
+                                   "Option is available on this system"),
+/*42*/ [KERN_MEMLOCK_RANGE]    = MIB_INT(_P | _RO, 0, "memlock_range",
+                                   "Whether the POSIX 1003.1b Range Memory "
+                                   "Locking Option is available on this "
+                                   "system"),
+/*43*/ [KERN_MEMORY_PROTECTION]= MIB_INT(_P | _RO, 0, "memory_protection",
+                                   "Whether the POSIX 1003.1b Memory "
+                                   "Protection Option is available on this "
+                                   "system"),
+/*44*/ /* KERN_LOGIN_NAME_MAX: not yet supported */
+/*45*/ /* KERN_DEFCORENAME: obsolete */
+/*46*/ /* KERN_LOGSIGEXIT: not yet supported */
+/*47*/ [KERN_PROC2]            = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
+                                   mib_kern_proc2, "proc2",
+                                   "Machine-independent process information"),
+/*48*/ [KERN_PROC_ARGS]        = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
+                                   mib_kern_proc_args, "proc_args",
+                                   "Process argument information"),
+/*49*/ [KERN_FSCALE]           = MIB_INT(_P | _RO, FSCALE, "fscale",
+                                   "Kernel fixed-point scale factor"),
+/*50*/ [KERN_CCPU]             = MIB_FUNC(_P | _RO | CTLTYPE_INT, sizeof(int),
+                                   mib_kern_ccpu, "ccpu",
+                                   "Scheduler exponential decay value"),
+/*51*/ [KERN_CP_TIME]          = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
+                                   mib_kern_cp_time, "cp_time", "Clock ticks "
+                                   "spent in different CPU states"),
+/*52*/ /* KERN_OLDSYSVIPC_INFO: obsolete */
+/*53*/ /* KERN_MSGBUF: not yet supported */
+/*54*/ [KERN_CONSDEV]          = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT,
+                                   sizeof(dev_t), mib_kern_consdev, "consdev",
+                                   "Console device"),
+/*55*/ [KERN_MAXPTYS]          = MIB_INT(_P | _RO, NR_PTYS, "maxptys",
+                                   "Maximum number of pseudo-ttys"),
+/*56*/ /* KERN_PIPE: not yet supported */
+/*57*/ [KERN_MAXPHYS]          = MIB_INT(_P | _RO, 4*1024*1024, "maxphys",
+                                   "Maximum raw I/O transfer size"),
+                                   /* 4MB is the upper limit for AHCI */
+/*58*/ /* KERN_SBMAX: not yet supported */
+/*59*/ /* KERN_TKSTAT: not yet supported */
+/*60*/ [KERN_MONOTONIC_CLOCK]  = MIB_INT(_P | _RO, _POSIX_MONOTONIC_CLOCK,
+                                   "monotonic_clock",
+                                   "Implementation version of the POSIX "
+                                   "1003.1b Monotonic Clock Option"),
+/*61*/ /* KERN_URND: not yet supported */
+/*62*/ /* KERN_LABELSECTOR: not yet supported */
+/*63*/ /* KERN_LABELOFFSET: not yet supported */
+/*64*/ [KERN_LWP]              = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
+                                   mib_kern_lwp, "lwp",
+                                   "System-wide LWP information"),
+/*65*/ [KERN_FORKFSLEEP]       = MIB_INTV(_P | _RW, 0, mib_kern_forkfsleep,
+                                   "forkfsleep", "Milliseconds to sleep on "
+                                   "fork failure due to process limits"),
+/*66*/ /* KERN_POSIX_THREADS: not yet supported */
+/*67*/ /* KERN_POSIX_SEMAPHORES: not yet supported */
+/*68*/ /* KERN_POSIX_BARRIERS: not yet supported */
+/*69*/ /* KERN_POSIX_TIMERS: not yet supported */
+/*70*/ /* KERN_POSIX_SPIN_LOCKS: not yet supported */
+/*71*/ /* KERN_POSIX_READER_WRITER_LOCKS: not yet supported */
+/*72*/ [KERN_DUMP_ON_PANIC]    = MIB_INT(_P | _RO, 0, "dump_on_panic",
+                                   "Perform a crash dump on system panic"),
+/*73*/ /* KERN_SOMAXKVA: not yet supported */
+/*74*/ /* KERN_ROOT_PARTITION: incompatible with our device node scheme */
+/*75*/ [KERN_DRIVERS]          = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT, 0,
+                                   mib_kern_drivers, "drivers",
+                                   "List of all drivers with block and "
+                                   "character device numbers"),
+/*76*/ /* KERN_BUF: not yet supported */
+/*77*/ /* KERN_FILE2: not yet supported */
+/*78*/ /* KERN_VERIEXEC: not yet supported */
+/*79*/ /* KERN_CP_ID: not yet supported */
+/*80*/ [KERN_HARDCLOCK_TICKS]  = MIB_FUNC(_P | _RO | CTLFLAG_UNSIGNED |
+                                   CTLTYPE_INT, sizeof(int),
+                                   mib_kern_hardclock_ticks,
+                                   "hardclock_ticks",
+                                   "Number of hardclock ticks"),
+/*81*/ /* KERN_ARND: not yet supported */
+/*82*/ [KERN_SYSVIPC]          = MIB_NODE(_P | _RO, mib_kern_ipc_table, "ipc",
+                                   "SysV IPC options"),
+/*83*/ [KERN_BOOTTIME]         = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT,
+                                   sizeof(struct timeval), mib_kern_boottime,
+                                   "boottime", "System boot time"),
+/*84*/ /* KERN_EVCNT: not yet supported */
 };
 
 /*
index 327231a043e6874560dfb76c702a1bebdad62faa..8101c0e030e3346993b4ab6d28a7f9c35c6758e4 100644 (file)
@@ -32,6 +32,8 @@
  */
 static struct mib_node mib_table[] = {
 /* 1*/ [CTL_KERN]      = MIB_ENODE(_P | _RO, "kern", "High kernel"),
+/* 2*/ [CTL_VM]        = MIB_ENODE(_P | _RO, "vm", "Virtual memory"),
+/* 6*/ [CTL_HW]        = MIB_ENODE(_P | _RO, "hw", "Generic CPU, I/O"),
 /* 8*/ [CTL_USER]      = MIB_ENODE(_P | _RO, "user", "User-level"),
 /*11*/ [CTL_VENDOR]    = MIB_ENODE(_P | _RW, "vendor", "Vendor specific"),
 /*32*/ [CTL_MINIX]     = MIB_ENODE(_P | _RO, "minix", "MINIX3 specific"),
@@ -322,6 +324,8 @@ mib_init(int type __unused, sef_init_info_t * info __unused)
         * large enough to store the entry.
         */
        mib_kern_init(&mib_table[CTL_KERN]);
+       mib_vm_init(&mib_table[CTL_VM]);
+       mib_hw_init(&mib_table[CTL_HW]);
        mib_minix_init(&mib_table[CTL_MINIX]);
 
        /*
index b21fd8c68f854ebccd4a0af86f5656b5a4ec684f..0896284350780fa093dd4a29464c0a808db39afe 100644 (file)
@@ -6,6 +6,14 @@
 #include <machine/vmparam.h>
 #include <assert.h>
 
+#if defined(__i386__)
+#include "kernel/arch/i386/include/archconst.h"
+#endif
+
+#ifndef CONFIG_MAX_CPUS
+#define CONFIG_MAX_CPUS 1
+#endif
+
 /*
  * The following setting toggles the existence of the minix.test subtree.  For
  * production environments, it should probably be disabled, although it should
@@ -261,8 +269,18 @@ void mib_tree_init(struct mib_node *);
 extern unsigned int nodes;
 extern unsigned int objects;
 
+/* proc.c */
+ssize_t mib_kern_lwp(struct mib_call *, struct mib_node *, struct mib_oldp *,
+       struct mib_newp *);
+ssize_t mib_kern_proc2(struct mib_call *, struct mib_node *, struct mib_oldp *,
+       struct mib_newp *);
+ssize_t mib_kern_proc_args(struct mib_call *, struct mib_node *,
+       struct mib_oldp *, struct mib_newp *);
+
 /* subtree modules */
 void mib_kern_init(struct mib_node *);
+void mib_vm_init(struct mib_node *);
+void mib_hw_init(struct mib_node *);
 void mib_minix_init(struct mib_node *);
 
 #endif /* !_MINIX_MIB_MIB_H */
diff --git a/minix/servers/mib/proc.c b/minix/servers/mib/proc.c
new file mode 100644 (file)
index 0000000..1c03187
--- /dev/null
@@ -0,0 +1,1169 @@
+/* MIB service - proc.c - functionality based on service process tables */
+/* Eventually, the CTL_PROC subtree might end up here as well. */
+
+#include "mib.h"
+
+#include <sys/exec.h>
+#include <minix/sysinfo.h>
+
+#include <machine/archtypes.h>
+#include "kernel/proc.h"
+#include "servers/pm/mproc.h"
+#include "servers/vfs/const.h"
+#include "servers/vfs/fproc.h"
+
+typedef struct proc ixfer_proc_t;
+typedef struct mproc ixfer_mproc_t;
+typedef struct fproc ixfer_fproc_t;
+
+static ixfer_proc_t proc_tab[NR_TASKS + NR_PROCS];
+static ixfer_mproc_t mproc_tab[NR_PROCS];
+static ixfer_fproc_t fproc_tab[NR_PROCS];
+
+/*
+ * The number of processes added to the current number of processes when doing
+ * a size estimation, so that the actual data retrieval does not end up with
+ * too little space if new processes have forked between the two calls.  We do
+ * a process table update only once per clock tick, which means that typically
+ * no update will take place between the user process's size estimation request
+ * and its subsequent data retrieval request.  On the other hand, if we do
+ * update process tables in between, quite a bit might have changed.
+ */
+#define EXTRA_PROCS    8
+
+#define HASH_SLOTS     (NR_PROCS / 4)  /* expected nr. of processes in use */
+#define NO_SLOT                (-1)
+static int hash_tab[HASH_SLOTS];       /* hash table mapping from PID.. */
+static int hnext_tab[NR_PROCS];                /* ..to PM process slot */
+
+static clock_t tabs_updated = 0;       /* when the tables were last updated */
+static int tabs_valid = TRUE;          /* FALSE if obtaining tables failed */
+
+/*
+ * Update the process tables by pulling in new copies from the kernel, PM, and
+ * VFS, but only every so often and only if it has not failed before.  Return
+ * TRUE iff the tables are now valid.
+ */
+static int
+update_tables(void)
+{
+       clock_t now;
+       pid_t pid;
+       int r, kslot, mslot, hslot;
+
+       /*
+        * If retrieving the tables failed at some point, do not keep trying
+        * all the time.  Such a failure is very unlikely to be transient.
+        */
+       if (tabs_valid == FALSE)
+               return FALSE;
+
+       /*
+        * Update the tables once per clock tick at most.  The update operation
+        * is rather heavy, transferring several hundreds of kilobytes between
+        * servers.  Userland should be able to live with information that is
+        * outdated by at most one clock tick.
+        */
+       now = getticks();
+
+       if (tabs_updated != 0 && tabs_updated == now)
+               return TRUE;
+
+       /* Perform an actual update now. */
+       tabs_valid = FALSE;
+
+       /* Retrieve and check the kernel process table. */
+       if ((r = sys_getproctab(proc_tab)) != OK) {
+               printf("MIB: unable to obtain kernel process table (%d)\n", r);
+
+               return FALSE;
+       }
+
+       for (kslot = 0; kslot < NR_TASKS + NR_PROCS; kslot++) {
+               if (proc_tab[kslot].p_magic != PMAGIC) {
+                       printf("MIB: kernel process table mismatch\n");
+
+                       return FALSE;
+               }
+       }
+
+       /* Retrieve and check the PM process table. */
+       r = getsysinfo(PM_PROC_NR, SI_PROC_TAB, mproc_tab, sizeof(mproc_tab));
+       if (r != OK) {
+               printf("MIB: unable to obtain PM process table (%d)\n", r);
+
+               return FALSE;
+       }
+
+       for (mslot = 0; mslot < NR_PROCS; mslot++) {
+               if (mproc_tab[mslot].mp_magic != MP_MAGIC) {
+                       printf("MIB: PM process table mismatch\n");
+
+                       return FALSE;
+               }
+       }
+
+       /* Retrieve the VFS process table, which has no magic number. */
+       r = getsysinfo(VFS_PROC_NR, SI_PROC_TAB, fproc_tab, sizeof(fproc_tab));
+       if (r != OK) {
+               printf("MIB: unable to obtain VFS process table (%d)\n", r);
+
+               return FALSE;
+       }
+
+       tabs_valid = TRUE;
+       tabs_updated = now;
+
+       /*
+        * Build a hash table mapping from process IDs to slot numbers, for
+        * fast access.  TODO: decide if this is better done on demand only.
+        */
+       for (hslot = 0; hslot < HASH_SLOTS; hslot++)
+               hash_tab[hslot] = NO_SLOT;
+
+       for (mslot = 0; mslot < NR_PROCS; mslot++) {
+               if (mproc_tab[mslot].mp_flags & IN_USE) {
+                       if ((pid = mproc_tab[mslot].mp_pid) <= 0)
+                               continue;
+
+                       hslot = mproc_tab[mslot].mp_pid % HASH_SLOTS;
+
+                       hnext_tab[mslot] = hash_tab[hslot];
+                       hash_tab[hslot] = mslot;
+               }
+       }
+
+       return TRUE;
+}
+
+/*
+ * Return the PM slot number for the given PID, or NO_SLOT if the PID is not in
+ * use by a process.
+ */
+static int
+get_mslot(pid_t pid)
+{
+       int mslot;
+
+       /* PID 0 identifies the kernel; checking this is up to the caller. */
+       if (pid <= 0)
+               return NO_SLOT;
+
+       for (mslot = hash_tab[pid % HASH_SLOTS]; mslot != NO_SLOT;
+           mslot = hnext_tab[mslot])
+               if (mproc_tab[mslot].mp_pid == pid)
+                       break;
+
+       return mslot;
+}
+
+/*
+ * Store the given number of clock ticks as a timeval structure.
+ */
+static void
+ticks_to_timeval(struct timeval * tv, clock_t ticks)
+{
+       clock_t hz;
+
+       hz = sys_hz();
+
+       tv->tv_sec = ticks / hz;
+       tv->tv_usec = (long)((ticks % hz) * 1000000ULL / hz);
+}
+
+/*
+ * Generate a wchan message text for the cases that the process is blocked on
+ * IPC with another process, of which the endpoint is given as 'endpt' here.
+ * The name of the other process is to be stored in 'wmesg', which is a buffer
+ * of size 'wmsz'.  The result should be null terminated.  If 'ipc' is set, the
+ * process is blocked on a direct IPC call, in which case the name of the other
+ * process is enclosed in parentheses.  If 'ipc' is not set, the call is made
+ * indirectly through VFS, and the name of the other process should not be
+ * enclosed in parentheses.  If no name can be obtained, we use the endpoint of
+ * the other process instead.
+ */
+static void
+fill_wmesg(char * wmesg, size_t wmsz, endpoint_t endpt, int ipc)
+{
+       const char *name;
+       int mslot;
+
+       switch (endpt) {
+       case ANY:
+               name = "any";
+               break;
+       case SELF:
+               name = "self";
+               break;
+       case NONE:
+               name = "none";
+               break;
+       default:
+               mslot = _ENDPOINT_P(endpt);
+               if (mslot >= -NR_TASKS && mslot < NR_PROCS &&
+                   (mslot < 0 || (mproc_tab[mslot].mp_flags & IN_USE)))
+                       name = proc_tab[NR_TASKS + mslot].p_name;
+               else
+                       name = NULL;
+       }
+
+       if (name != NULL)
+               snprintf(wmesg, wmsz, "%s%s%s",
+                   ipc ? "(" : "", name, ipc ? ")" : "");
+       else
+               snprintf(wmesg, wmsz, "%s%d%s",
+                   ipc ? "(" : "", endpt, ipc ? ")" : "");
+}
+
+/*
+ * Return the LWP status of a process, along with additional information in
+ * case the process is sleeping (LSSLEEP): a wchan value and text to indicate
+ * what the process is sleeping on, and possibly a flag field modification to
+ * indicate that the sleep is interruptible.
+ */
+static int
+get_lwp_stat(int mslot, uint64_t * wcptr, char * wmptr, size_t wmsz,
+       int32_t * flag)
+{
+       struct mproc *mp;
+       struct fproc *fp;
+       struct proc *kp;
+       const char *wmesg;
+       uint64_t wchan;
+       endpoint_t endpt;
+
+       mp = &mproc_tab[mslot];
+       fp = &fproc_tab[mslot];
+       kp = &proc_tab[NR_TASKS + mslot];
+
+       /*
+        * First cover all the cases that the process is not sleeping.  In
+        * those cases, we need not return additional sleep information either.
+        */
+       if (mp->mp_flags & (TRACE_ZOMBIE | ZOMBIE))
+               return LSZOMB;
+
+       if (mp->mp_flags & EXITING)
+               return LSDEAD;
+
+       if ((mp->mp_flags & TRACE_STOPPED) || RTS_ISSET(kp, RTS_P_STOP))
+               return LSSTOP;
+
+       if (proc_is_runnable(kp))
+               return LSRUN;
+
+       /*
+        * The process is sleeping.  In that case, we must also figure out why,
+        * and return an appropriate wchan value and human-readable wmesg text.
+        *
+        * The process can be blocked on either a known sleep state in PM or
+        * VFS, or otherwise on IPC communication with another process, or
+        * otherwise on a kernel RTS flag.  In each case, decide what to use as
+        * wchan value and wmesg text, and whether the sleep is interruptible.
+        *
+        * The wchan value should be unique for the sleep reason.  We use its
+        * lower eight bits to indicate a class:
+        *   0x00 = kernel task
+        *   0x01 = kerel RTS block
+        *   0x02 = PM call
+        *   0x03 = VFS call
+        *   0x04 = MIB call
+        *   0xff = blocked on process
+        * The upper bits are used for class-specific information.  The actual
+        * value does not really matter, as long as it is nonzero and there is
+        * no overlap between the different values.
+        */
+       wchan = 0;
+       wmesg = NULL;
+
+       /*
+        * First see if the process is marked as blocked in the tables of PM or
+        * VFS.  Such a block reason is always an interruptible sleep.  Note
+        * that we do not use the kernel table at all in this case: each of the
+        * three tables is consistent within itself, but not necessarily
+        * consistent with any of the other tables, so we avoid internal
+        * mismatches if we can.
+        */
+       if (mp->mp_flags & WAITING) {
+               wchan = 0x102;
+               wmesg = "wait";
+       } else if (mp->mp_flags & SIGSUSPENDED) {
+               wchan = 0x202;
+               wmesg = "pause";
+       } else if (fp->fp_blocked_on != FP_BLOCKED_ON_NONE) {
+               wchan = (fp->fp_blocked_on << 8) | 0x03;
+               switch (fp->fp_blocked_on) {
+               case FP_BLOCKED_ON_PIPE:
+                       wmesg = "pipe";
+                       break;
+               case FP_BLOCKED_ON_LOCK:
+                       wmesg = "lock";
+                       break;
+               case FP_BLOCKED_ON_POPEN:
+                       wmesg = "popen";
+                       break;
+               case FP_BLOCKED_ON_SELECT:
+                       wmesg = "select";
+                       break;
+               case FP_BLOCKED_ON_OTHER:
+                       /*
+                        * Add the task (= character driver) endpoint to the
+                        * wchan value, and use the driver's process name,
+                        * without parentheses, as wmesg text.
+                        */
+                       wchan |= (uint64_t)fp->fp_task << 16;
+                       fill_wmesg(wmptr, wmsz, fp->fp_task, FALSE /*ipc*/);
+                       break;
+               default:
+                       /* A newly added flag we don't yet know about? */
+                       wmesg = "???";
+                       break;
+               }
+       }
+       if (wchan != 0) {
+               *wcptr = wchan;
+               if (wmesg != NULL) /* NULL means "already set" here */
+                       strlcpy(wmptr, wmesg, wmsz);
+               *flag |= L_SINTR;
+       }
+
+       /*
+        * See if the process is blocked on sending or receiving.  If not, then
+        * use one of the kernel RTS flags as reason.
+        */
+       endpt = P_BLOCKEDON(kp);
+
+       switch (endpt) {
+       case MIB_PROC_NR:
+               /* This is really just aesthetics. */
+               wchan = 0x04;
+               wmesg = "sysctl";
+               break;
+       case NONE:
+               /*
+                * The process is not running, but also not blocked on IPC with
+                * another process.  This means it must be stopped on a kernel
+                * RTS flag.
+                */
+               wchan = ((uint64_t)kp->p_rts_flags << 8) | 0x01;
+               if (RTS_ISSET(kp, RTS_PROC_STOP))
+                       wmesg = "kstop";
+               else if (RTS_ISSET(kp, RTS_SIGNALED) ||
+                   RTS_ISSET(kp, RTS_SIGNALED))
+                       wmesg = "ksignal";
+               else if (RTS_ISSET(kp, RTS_NO_PRIV))
+                       wmesg = "knopriv";
+               else if (RTS_ISSET(kp, RTS_PAGEFAULT) ||
+                   RTS_ISSET(kp, RTS_VMREQTARGET))
+                       wmesg = "fault";
+               else if (RTS_ISSET(kp, RTS_NO_QUANTUM))
+                       wmesg = "sched";
+               else
+                       wmesg = "kflag";
+               break;
+       case ANY:
+               /*
+                * If the process is blocked receiving from ANY, mark it as
+                * being in an interruptible sleep.  This looks nicer, even
+                * though "interruptible" is not applicable to services at all.
+                */
+               *flag |= L_SINTR;
+               break;
+       }
+
+       /*
+        * If at this point wchan is still zero, the process is blocked sending
+        * or receiving.  Use a wchan value based on the target endpoint, and
+        * use "(procname)" as wmesg text.
+        */
+       if (wchan == 0) {
+               *wcptr = ((uint64_t)endpt << 8) | 0xff;
+               fill_wmesg(wmptr, wmsz, endpt, TRUE /*ipc*/);
+       } else {
+               *wcptr = wchan;
+               if (wmesg != NULL) /* NULL means "already set" here */
+                       strlcpy(wmptr, wmesg, wmsz);
+       }
+
+       return LSSLEEP;
+}
+
+
+/*
+ * Fill the part of a LWP structure that is common between kernel tasks and
+ * user processes.  Also return a CPU estimate in 'estcpu', because we generate
+ * the value as a side effect here, and the LWP structure has no estcpu field.
+ */
+static void
+fill_lwp_common(struct kinfo_lwp * l, int kslot, uint32_t * estcpu)
+{
+       struct proc *kp;
+       struct timeval tv;
+       clock_t uptime;
+       uint32_t hz;
+
+       kp = &proc_tab[kslot];
+
+       uptime = getticks();
+       hz = sys_hz();
+
+       /*
+        * We use the process endpoint as the LWP ID.  Not only does this allow
+        * users to obtain process endpoints with "ps -s" (thus replacing the
+        * MINIX3 ps(1)'s "ps -E"), but if we ever do implement kernel threads,
+        * this is probably still going to be accurate.
+        */
+       l->l_lid = kp->p_endpoint;
+
+       /*
+        * The time during which the process has not been swapped in or out is
+        * not applicable for us, and thus, we set it to the time the process
+        * has been running (in seconds).  This value is relevant mostly for
+        * ps(1)'s CPU usage correction for processes that have just started.
+        */
+       if (kslot >= NR_TASKS)
+               l->l_swtime = uptime - mproc_tab[kslot - NR_TASKS].mp_started;
+       else
+               l->l_swtime = uptime;
+       l->l_swtime /= hz;
+
+       /*
+        * Sleep (dequeue) times are not maintained for kernel tasks, so
+        * pretend they are never asleep (which is pretty accurate).
+        */
+       if (kslot < NR_TASKS)
+               l->l_slptime = 0;
+       else
+               l->l_slptime = (uptime - kp->p_dequeued) / hz;
+
+       l->l_priority = kp->p_priority;
+       l->l_usrpri = kp->p_priority;
+       l->l_cpuid = kp->p_cpu;
+       ticks_to_timeval(&tv, kp->p_user_time + kp->p_sys_time);
+       l->l_rtime_sec = tv.tv_sec;
+       l->l_rtime_usec = tv.tv_usec;
+
+       /*
+        * Obtain CPU usage percentages and estimates through library code
+        * shared between the kernel and this service; see its source for
+        * details.  We note that the produced estcpu value is rather different
+        * from the one produced by NetBSD, but this should not be a problem.
+        */
+       l->l_pctcpu = cpuavg_getstats(&kp->p_cpuavg, &l->l_cpticks, estcpu,
+           uptime, hz);
+}
+
+/*
+ * Fill a LWP structure for a kernel task.  Each kernel task has its own LWP,
+ * and all of them have negative PIDs.
+ */
+static void
+fill_lwp_kern(struct kinfo_lwp * l, int kslot)
+{
+       uint32_t estcpu;
+
+       memset(l, 0, sizeof(*l));
+
+       l->l_flag = L_INMEM | L_SINTR | L_SYSTEM;
+       l->l_stat = LSSLEEP;
+       l->l_pid = kslot - NR_TASKS;
+
+       /*
+        * When showing LWP entries, ps(1) uses the process name rather than
+        * the LWP name.  All kernel tasks are therefore shown as "[kernel]"
+        * anyway.  We use the wmesg field to show the actual kernel task name.
+        */
+       l->l_wchan = ((uint64_t)(l->l_pid) << 8) | 0x00;
+       strlcpy(l->l_wmesg, proc_tab[kslot].p_name, sizeof(l->l_wmesg));
+       strlcpy(l->l_name, "kernel", sizeof(l->l_name));
+
+       fill_lwp_common(l, kslot, &estcpu);
+}
+
+/*
+ * Fill a LWP structure for a user process.
+ */
+static void
+fill_lwp_user(struct kinfo_lwp * l, int mslot)
+{
+       struct mproc *mp;
+       uint32_t estcpu;
+
+       memset(l, 0, sizeof(*l));
+
+       mp = &mproc_tab[mslot];
+
+       l->l_flag = L_INMEM;
+       l->l_stat = get_lwp_stat(mslot, &l->l_wchan, l->l_wmesg,
+           sizeof(l->l_wmesg), &l->l_flag);
+       l->l_pid = mp->mp_pid;
+       strlcpy(l->l_name, mp->mp_name, sizeof(l->l_name));
+
+       fill_lwp_common(l, NR_TASKS + mslot, &estcpu);
+}
+
+/*
+ * Implementation of CTL_KERN KERN_LWP.
+ */
+ssize_t
+mib_kern_lwp(struct mib_call * call, struct mib_node * node __unused,
+       struct mib_oldp * oldp, struct mib_newp * newp __unused)
+{
+       struct kinfo_lwp lwp;
+       struct mproc *mp;
+       size_t copysz;
+       ssize_t off;
+       pid_t pid;
+       int r, elsz, elmax, kslot, mslot, last_mslot;
+
+       if (call->call_namelen != 3)
+               return EINVAL;
+
+       pid = (pid_t)call->call_name[0];
+       elsz = call->call_name[1];
+       elmax = call->call_name[2]; /* redundant with the given oldlen.. */
+
+       if (pid < -1 || elsz <= 0 || elmax < 0)
+               return EINVAL;
+
+       if (!update_tables())
+               return EINVAL;
+
+       off = 0;
+       copysz = MIN((size_t)elsz, sizeof(lwp));
+
+       /*
+        * We model kernel tasks as LWP threads of the kernel (with PID 0).
+        * Modeling the kernel tasks as processes with negative PIDs, like
+        * ProcFS does, conflicts with the KERN_LWP API here: a PID of -1
+        * indicates that the caller wants a full listing of LWPs.
+        */
+       if (pid <= 0) {
+               for (kslot = 0; kslot < NR_TASKS; kslot++) {
+                       if (mib_inrange(oldp, off) && elmax > 0) {
+                               fill_lwp_kern(&lwp, kslot);
+                               if ((r = mib_copyout(oldp, off, &lwp,
+                                   copysz)) < 0)
+                                       return r;
+                               elmax--;
+                       }
+                       off += elsz;
+               }
+
+               /* No need to add extra space here: NR_TASKS is static. */
+               if (pid == 0)
+                       return off;
+       }
+
+       /*
+        * With PID 0 out of the way: the user requested the LWP for either a
+        * specific user process (pid > 0), or for all processes (pid < 0).
+        */
+       if (pid > 0) {
+               if ((mslot = get_mslot(pid)) == NO_SLOT ||
+                   (mproc_tab[mslot].mp_flags & (TRACE_ZOMBIE | ZOMBIE)))
+                       return ESRCH;
+               last_mslot = mslot;
+       } else {
+               mslot = 0;
+               last_mslot = NR_PROCS - 1;
+       }
+
+       for (; mslot <= last_mslot; mslot++) {
+               mp = &mproc_tab[mslot];
+
+               if ((mp->mp_flags & (IN_USE | TRACE_ZOMBIE | ZOMBIE)) !=
+                   IN_USE)
+                       continue;
+
+               if (mib_inrange(oldp, off) && elmax > 0) {
+                       fill_lwp_user(&lwp, mslot);
+                       if ((r = mib_copyout(oldp, off, &lwp, copysz)) < 0)
+                               return r;
+                       elmax--;
+               }
+               off += elsz;
+       }
+
+       if (oldp == NULL && pid < 0)
+               off += EXTRA_PROCS * elsz;
+
+       return off;
+}
+
+
+/*
+ * Fill the part of a process structure that is common between kernel tasks and
+ * user processes.
+ */
+static void
+fill_proc2_common(struct kinfo_proc2 * p, int kslot)
+{
+       struct vm_usage_info vui;
+       struct timeval tv;
+       struct proc *kp;
+       struct kinfo_lwp l;
+
+       kp = &proc_tab[kslot];
+
+       /*
+        * Much of the information in the LWP structure also ends up in the
+        * process structure.  In order to avoid duplication of some important
+        * code, first generate LWP values and then copy it them into the
+        * process structure.
+        */
+       memset(&l, 0, sizeof(l));
+       fill_lwp_common(&l, kslot, &p->p_estcpu);
+
+       /* Obtain memory usage information from VM.  Ignore failures. */
+       memset(&vui, 0, sizeof(vui));
+       (void)vm_info_usage(kp->p_endpoint, &vui);
+
+       ticks_to_timeval(&tv, kp->p_user_time + kp->p_sys_time);
+       p->p_rtime_sec = l.l_rtime_sec;
+       p->p_rtime_usec = l.l_rtime_usec;
+       p->p_cpticks = l.l_cpticks;
+       p->p_pctcpu = l.l_pctcpu;
+       p->p_swtime = l.l_swtime;
+       p->p_slptime = l.l_slptime;
+       p->p_uticks = kp->p_user_time;
+       p->p_sticks = kp->p_sys_time;
+       /* TODO: p->p_iticks */
+       ticks_to_timeval(&tv, kp->p_user_time);
+       p->p_uutime_sec = tv.tv_sec;
+       p->p_uutime_usec = tv.tv_usec;
+       ticks_to_timeval(&tv, kp->p_sys_time);
+       p->p_ustime_sec = tv.tv_sec;
+       p->p_ustime_usec = tv.tv_usec;
+
+       p->p_priority = l.l_priority;
+       p->p_usrpri = l.l_usrpri;
+
+       p->p_vm_rssize = howmany(vui.vui_total, PAGE_SIZE);
+       p->p_vm_vsize = howmany(vui.vui_virtual, PAGE_SIZE);
+       p->p_vm_msize = howmany(vui.vui_mvirtual, PAGE_SIZE);
+
+       p->p_uru_maxrss = vui.vui_maxrss;
+       p->p_uru_minflt = vui.vui_minflt;
+       p->p_uru_majflt = vui.vui_majflt;
+
+       p->p_cpuid = l.l_cpuid;
+}
+
+/*
+ * Fill a process structure for the kernel pseudo-process (with PID 0).
+ */
+static void
+fill_proc2_kern(struct kinfo_proc2 * p)
+{
+
+       memset(p, 0, sizeof(*p));
+
+       p->p_flag = L_INMEM | L_SYSTEM | L_SINTR;
+       p->p_pid = 0;
+       p->p_stat = LSSLEEP;
+       p->p_nice = NZERO;
+
+       /* Use the KERNEL task wchan, for consistency between ps and top. */
+       p->p_wchan = ((uint64_t)KERNEL << 8) | 0x00;
+       strlcpy(p->p_wmesg, "kernel", sizeof(p->p_wmesg));
+
+       strlcpy(p->p_comm, "kernel", sizeof(p->p_comm));
+       p->p_realflag = P_INMEM | P_SYSTEM | P_SINTR;
+       p->p_realstat = SACTIVE;
+       p->p_nlwps = NR_TASKS;
+
+       /*
+        * By using the KERNEL slot here, the kernel process will get a proper
+        * CPU usage average.
+        */
+       fill_proc2_common(p, KERNEL + NR_TASKS);
+}
+
+/*
+ * Fill a process structure for a user process.
+ */
+static void
+fill_proc2_user(struct kinfo_proc2 * p, int mslot)
+{
+       struct mproc *mp;
+       struct fproc *fp;
+       time_t boottime;
+       dev_t tty;
+       struct timeval tv;
+       int i, r, kslot, zombie;
+
+       memset(p, 0, sizeof(*p));
+
+       if ((r = getuptime(NULL, NULL, &boottime)) != OK)
+               panic("getuptime failed: %d", r);
+
+       kslot = NR_TASKS + mslot;
+       mp = &mproc_tab[mslot];
+       fp = &fproc_tab[mslot];
+
+       zombie = (mp->mp_flags & (TRACE_ZOMBIE | ZOMBIE));
+       tty = (!zombie) ? fp->fp_tty : NO_DEV;
+
+       p->p_eflag = 0;
+       if (tty != NO_DEV)
+               p->p_eflag |= EPROC_CTTY;
+       if (mp->mp_pid == mp->mp_procgrp) /* TODO: job control support */
+               p->p_eflag |= EPROC_SLEADER;
+
+       p->p_exitsig = SIGCHLD; /* TODO */
+
+       p->p_flag = P_INMEM;
+       if (mp->mp_flags & TAINTED)
+               p->p_flag |= P_SUGID;
+       if (mp->mp_tracer != NO_TRACER)
+               p->p_flag |= P_TRACED;
+       if (tty != NO_DEV)
+               p->p_flag |= P_CONTROLT;
+       p->p_pid = mp->mp_pid;
+       if (mp->mp_parent >= 0 && mp->mp_parent < NR_PROCS)
+               p->p_ppid = mproc_tab[mp->mp_parent].mp_pid;
+       p->p_sid = mp->mp_procgrp; /* TODO: job control supported */
+       p->p__pgid = mp->mp_procgrp;
+       p->p_tpgid = (tty != NO_DEV) ? mp->mp_procgrp : 0;
+       p->p_uid = mp->mp_effuid;
+       p->p_ruid = mp->mp_realuid;
+       p->p_gid = mp->mp_effgid;
+       p->p_rgid = mp->mp_realgid;
+       p->p_ngroups = MIN(mp->mp_ngroups, KI_NGROUPS);
+       for (i = 0; i < p->p_ngroups; i++)
+               p->p_groups[i] = mp->mp_sgroups[i];
+       p->p_tdev = tty;
+       memcpy(&p->p_siglist, &mp->mp_sigpending, sizeof(p->p_siglist));
+       memcpy(&p->p_sigmask, &mp->mp_sigmask, sizeof(p->p_sigmask));
+       memcpy(&p->p_sigcatch, &mp->mp_catch, sizeof(p->p_sigcatch));
+       memcpy(&p->p_sigignore, &mp->mp_ignore, sizeof(p->p_sigignore));
+       p->p_nice = mp->mp_nice + NZERO;
+       strlcpy(p->p_comm, mp->mp_name, sizeof(p->p_comm));
+       p->p_uvalid = 1;
+       ticks_to_timeval(&tv, mp->mp_started);
+       p->p_ustart_sec = boottime + tv.tv_sec;
+       p->p_ustart_usec = tv.tv_usec;
+       /* TODO: other rusage fields */
+       ticks_to_timeval(&tv, mp->mp_child_utime + mp->mp_child_stime);
+       p->p_uctime_sec = tv.tv_sec;
+       p->p_uctime_usec = tv.tv_usec;
+       p->p_realflag = p->p_flag;
+       p->p_nlwps = (zombie) ? 0 : 1;
+
+       p->p_stat = get_lwp_stat(mslot, &p->p_wchan, p->p_wmesg,
+           sizeof(p->p_wmesg), &p->p_flag);
+
+       switch (p->p_stat) {
+       case LSRUN:
+               p->p_realstat = SACTIVE;
+               p->p_nrlwps = 1;
+               break;
+       case LSSLEEP:
+               p->p_realstat = SACTIVE;
+               if (p->p_flag & L_SINTR)
+                       p->p_realflag |= P_SINTR;
+               break;
+       case LSSTOP:
+               p->p_realstat = SSTOP;
+               break;
+       case LSZOMB:
+               p->p_realstat = SZOMB;
+               break;
+       case LSDEAD:
+               p->p_stat = LSZOMB; /* ps(1) STAT does not know LSDEAD */
+               p->p_realstat = SDEAD;
+               break;
+       default:
+               assert(0);
+       }
+
+       if (!zombie)
+               fill_proc2_common(p, kslot);
+}
+
+/*
+ * Implementation of CTL_KERN KERN_PROC2.
+ */
+ssize_t
+mib_kern_proc2(struct mib_call * call, struct mib_node * node __unused,
+       struct mib_oldp * oldp, struct mib_newp * newp __unused)
+{
+       struct kinfo_proc2 proc2;
+       struct mproc *mp;
+       size_t copysz;
+       ssize_t off;
+       dev_t tty;
+       int r, req, arg, elsz, elmax, kmatch, zombie, mslot;
+
+       if (call->call_namelen != 4)
+               return EINVAL;
+
+       req = call->call_name[0];
+       arg = call->call_name[1];
+       elsz = call->call_name[2];
+       elmax = call->call_name[3]; /* redundant with the given oldlen.. */
+
+       /*
+        * The kernel is special, in that it does not have a slot in the PM or
+        * VFS tables.  As such, it is dealt with separately.  While checking
+        * arguments, we might as well check whether the kernel is matched.
+        */
+       switch (req) {
+       case KERN_PROC_ALL:
+               kmatch = TRUE;
+               break;
+       case KERN_PROC_PID:
+       case KERN_PROC_SESSION:
+       case KERN_PROC_PGRP:
+       case KERN_PROC_UID:
+       case KERN_PROC_RUID:
+       case KERN_PROC_GID:
+       case KERN_PROC_RGID:
+               kmatch = (arg == 0);
+               break;
+       case KERN_PROC_TTY:
+               kmatch = ((dev_t)arg == KERN_PROC_TTY_NODEV);
+               break;
+       default:
+               return EINVAL;
+       }
+
+       if (elsz <= 0 || elmax < 0)
+               return EINVAL;
+
+       if (!update_tables())
+               return EINVAL;
+
+       off = 0;
+       copysz = MIN((size_t)elsz, sizeof(proc2));
+
+       if (kmatch) {
+               if (mib_inrange(oldp, off) && elmax > 0) {
+                       fill_proc2_kern(&proc2);
+                       if ((r = mib_copyout(oldp, off, &proc2, copysz)) < 0)
+                               return r;
+                       elmax--;
+               }
+               off += elsz;
+       }
+
+       for (mslot = 0; mslot < NR_PROCS; mslot++) {
+               mp = &mproc_tab[mslot];
+
+               if (!(mp->mp_flags & IN_USE))
+                       continue;
+
+               switch (req) {
+               case KERN_PROC_PID:
+                       if ((pid_t)arg != mp->mp_pid)
+                               continue;
+                       break;
+               case KERN_PROC_SESSION: /* TODO: job control support */
+               case KERN_PROC_PGRP:
+                       if ((pid_t)arg != mp->mp_procgrp)
+                               continue;
+                       break;
+               case KERN_PROC_TTY:
+                       if ((dev_t)arg == KERN_PROC_TTY_REVOKE)
+                               continue; /* TODO: revoke(2) support */
+                       /* Do not access the fproc_tab slot of zombies. */
+                       zombie = (mp->mp_flags & (TRACE_ZOMBIE | ZOMBIE));
+                       tty = (zombie) ? fproc_tab[mslot].fp_tty : NO_DEV;
+                       if ((dev_t)arg == KERN_PROC_TTY_NODEV) {
+                               if (tty != NO_DEV)
+                                       continue;
+                       } else if ((dev_t)arg == NO_DEV || (dev_t)arg != tty)
+                               continue;
+                       break;
+               case KERN_PROC_UID:
+                       if ((uid_t)arg != mp->mp_effuid)
+                               continue;
+                       break;
+               case KERN_PROC_RUID:
+                       if ((uid_t)arg != mp->mp_realuid)
+                               continue;
+                       break;
+               case KERN_PROC_GID:
+                       if ((gid_t)arg != mp->mp_effgid)
+                               continue;
+                       break;
+               case KERN_PROC_RGID:
+                       if ((gid_t)arg != mp->mp_realgid)
+                               continue;
+                       break;
+               }
+
+               if (mib_inrange(oldp, off) && elmax > 0) {
+                       fill_proc2_user(&proc2, mslot);
+                       if ((r = mib_copyout(oldp, off, &proc2, copysz)) < 0)
+                               return r;
+                       elmax--;
+               }
+               off += elsz;
+       }
+
+       if (oldp == NULL && req != KERN_PROC_PID)
+               off += EXTRA_PROCS * elsz;
+
+       return off;
+}
+
+/*
+ * Implementation of CTL_KERN KERN_PROC_ARGS.
+ */
+ssize_t
+mib_kern_proc_args(struct mib_call * call, struct mib_node * node __unused,
+       struct mib_oldp * oldp, struct mib_newp * newp __unused)
+{
+       char vbuf[PAGE_SIZE], sbuf[PAGE_SIZE], obuf[PAGE_SIZE];
+       struct ps_strings pss;
+       struct mproc *mp;
+       char *buf, *p, *q, *pptr;
+       vir_bytes vaddr, vpage, spage, paddr, ppage;
+       size_t max, off, olen, oleft, oldlen, bytes, pleft;
+       unsigned int copybudget;
+       pid_t pid;
+       int req, mslot, count, aborted, ended;
+       ssize_t r;
+
+       if (call->call_namelen != 2)
+               return EINVAL;
+
+       pid = call->call_name[0];
+       req = call->call_name[1];
+
+       switch (req) {
+       case KERN_PROC_ARGV:
+       case KERN_PROC_ENV:
+       case KERN_PROC_NARGV:
+       case KERN_PROC_NENV:
+               break;
+       default:
+               return EOPNOTSUPP;
+       }
+
+       if (!update_tables())
+               return EINVAL;
+
+       if ((mslot = get_mslot(pid)) == NO_SLOT)
+               return ESRCH;
+       mp = &mproc_tab[mslot];
+       if (mp->mp_flags & (TRACE_ZOMBIE | ZOMBIE))
+               return ESRCH;
+
+       /* We can return the count field size without copying in any data. */
+       if (oldp == NULL && (req == KERN_PROC_NARGV || req == KERN_PROC_NENV))
+               return sizeof(count);
+
+       if (sys_datacopy(mp->mp_endpoint,
+           mp->mp_frame_addr + mp->mp_frame_len - sizeof(pss),
+           SELF, (vir_bytes)&pss, sizeof(pss)) != OK)
+               return EINVAL;
+
+       /*
+        * Determine the upper size limit of the requested data.  Not only may
+        * the size never exceed ARG_MAX, it may also not exceed the frame
+        * length as given in its original exec call.  In fact, the frame
+        * length should be substantially larger: all strings for both the
+        * arguments and the environment are in there, along with other stuff,
+        * and there must be no overlap between strings.  It is possible that
+        * the application called setproctitle(3), in which case the ps_strings
+        * pointers refer to data outside the frame altogether.  However, this
+        * data should not exceed 2048 bytes, and we cover this by rounding up
+        * the frame length to a multiple of the page size.  Anyhow, NetBSD
+        * blindly returns ARG_MAX when asked for a size estimate, so with this
+        * maximum we are already quite a bit more accurate.
+        */
+       max = roundup(MIN(mp->mp_frame_len, ARG_MAX), PAGE_SIZE);
+
+       switch (req) {
+       case KERN_PROC_NARGV:
+               count = pss.ps_nargvstr;
+               return mib_copyout(oldp, 0, &count, sizeof(count));
+       case KERN_PROC_NENV:
+               count = pss.ps_nenvstr;
+               return mib_copyout(oldp, 0, &count, sizeof(count));
+       case KERN_PROC_ARGV:
+               if (oldp == NULL)
+                       return max;
+               vaddr = (vir_bytes)pss.ps_argvstr;
+               count = pss.ps_nargvstr;
+               break;
+       case KERN_PROC_ENV:
+               if (oldp == NULL)
+                       return max;
+               vaddr = (vir_bytes)pss.ps_envstr;
+               count = pss.ps_nenvstr;
+               break;
+       }
+
+       /*
+        * Go through the strings.  Copy in entire, machine-aligned pages at
+        * once, in the hope that all data is stored consecutively, which it
+        * should be: we expect that the vector is followed by the strings, and
+        * that the strings are stored in order of vector reference.  We keep
+        * up to two pages with copied-in data: one for the vector, and
+        * optionally one for string data.  In addition, we keep one page with
+        * data to be copied out, so that we do not cause a lot of copy
+        * overhead for short strings.
+        *
+        * We stop whenever any of the following conditions are met:
+        * - copying in data from the target process fails for any reason;
+        * - we have processed the last index ('count') into the vector;
+        * - the current vector element is a NULL pointer;
+        * - the requested number of output bytes ('oldlen') has been reached;
+        * - the maximum number of output bytes ('max') has been reached;
+        * - the number of page copy-ins exceeds an estimated threshold;
+        * - copying out data fails for any reason (we then return the error).
+        *
+        * We limit the number of page copy-ins because otherwise a rogue
+        * process could create an argument vector consisting of only two-byte
+        * strings that all span two pages, causing us to copy up to 1GB of
+        * data with the current ARG_MAX value of 256K.  No reasonable vector
+        * should cause more than (ARG_MAX / PAGE_SIZE) page copies for
+        * strings; we are nice enough to allow twice that.  Vector copies do
+        * not count, as they are linear anyway.
+        *
+        * Unlike every other sysctl(2) call, we are supposed to truncate the
+        * resulting size (the returned 'oldlen') to the requested size (the
+        * given 'oldlen') *and* return the resulting size, rather than ENOMEM
+        * and the real size.  Unfortunately, libkvm actually relies on this.
+        *
+        * Generally speaking, upon failure we just return a truncated result.
+        * In case of truncation, the data we copy out need not be null
+        * terminated.  It is up to userland to process the data correctly.
+        */
+       if (trunc_page(vaddr) == 0 || vaddr % sizeof(char *) != 0)
+               return 0;
+
+       off = 0;
+       olen = 0;
+       aborted = FALSE;
+
+       oldlen = mib_getoldlen(oldp);
+       if (oldlen > max)
+               oldlen = max;
+
+       copybudget = (ARG_MAX / PAGE_SIZE) * 2;
+
+       vpage = 0;
+       spage = 0;
+
+       while (count > 0 && off + olen < oldlen && !aborted) {
+               /*
+                * Start by fetching the page containing the current vector
+                * element, if needed.  We could limit the fetch to the vector
+                * size, but our hope is that for the simple cases, the strings
+                * are on the remainder of the same page, so we save a copy
+                * call.  TODO: since the strings should follow the vector, we
+                * could start the copy at the base of the vector.
+                */
+               if (trunc_page(vaddr) != vpage) {
+                       vpage = trunc_page(vaddr);
+                       if (sys_datacopy(mp->mp_endpoint, vpage, SELF,
+                           (vir_bytes)vbuf, PAGE_SIZE) != OK)
+                               break;
+               }
+
+               /* Get the current vector element, pointing to a string. */
+               memcpy(&pptr, &vbuf[vaddr - vpage], sizeof(pptr));
+               paddr = (vir_bytes)pptr;
+               ppage = trunc_page(paddr);
+               if (ppage == 0)
+                       break;
+
+               /* Fetch the string itself, one page at a time at most. */
+               do {
+                       /*
+                        * See if the string pointer falls inside either the
+                        * vector page or the previously fetched string page
+                        * (if any).  If not, fetch a string page.
+                        */
+                       if (ppage == vpage) {
+                               buf = vbuf;
+                       } else if (ppage == spage) {
+                               buf = sbuf;
+                       } else {
+                               if (--copybudget == 0) {
+                                       aborted = TRUE;
+                                       break;
+                               }
+                               spage = ppage;
+                               if (sys_datacopy(mp->mp_endpoint, spage, SELF,
+                                   (vir_bytes)sbuf, PAGE_SIZE) != OK) {
+                                       aborted = TRUE;
+                                       break;
+                               }
+                               buf = sbuf;
+                       }
+
+                       /*
+                        * We now have a string fragment in a buffer.  See if
+                        * the string is null terminated.  If not, all the data
+                        * up to the buffer end is part of the string, and the
+                        * string continues on the next page.
+                        */
+                       p = &buf[paddr - ppage];
+                       pleft = PAGE_SIZE - (paddr - ppage);
+                       assert(pleft > 0);
+
+                       if ((q = memchr(p, '\0', pleft)) != NULL) {
+                               bytes = (size_t)(q - p + 1);
+                               assert(bytes <= pleft);
+                               ended = TRUE;
+                       } else {
+                               bytes = pleft;
+                               ended = FALSE;
+                       }
+
+                       /* Limit the result to the requested length. */
+                       if (off + olen + bytes > oldlen)
+                               bytes = oldlen - off - olen;
+
+                       /*
+                        * Add 'bytes' bytes from string pointer 'p' to the
+                        * output buffer, copying out its contents to userland
+                        * if it has filled up.
+                        */
+                       if (olen + bytes > sizeof(obuf)) {
+                               oleft = sizeof(obuf) - olen;
+                               memcpy(&obuf[olen], p, oleft);
+
+                               if ((r = mib_copyout(oldp, off, obuf,
+                                   sizeof(obuf))) < 0)
+                                       return r;
+                               off += sizeof(obuf);
+                               olen = 0;
+
+                               p += oleft;
+                               bytes -= oleft;
+                       }
+                       if (bytes > 0) {
+                               memcpy(&obuf[olen], p, bytes);
+                               olen += bytes;
+                       }
+
+                       /*
+                        * Continue as long as we have not yet found the string
+                        * end, and we have not yet filled the output buffer.
+                        */
+                       paddr += pleft;
+                       assert(trunc_page(paddr) == paddr);
+                       ppage = paddr;
+               } while (!ended && off + olen < oldlen);
+
+               vaddr += sizeof(char *);
+               count--;
+       }
+
+       /* Copy out any remainder of the output buffer. */
+       if (olen > 0) {
+               if ((r = mib_copyout(oldp, off, obuf, olen)) < 0)
+                       return r;
+               off += olen;
+       }
+
+       assert(off <= oldlen);
+       return off;
+}
diff --git a/minix/servers/mib/vm.c b/minix/servers/mib/vm.c
new file mode 100644 (file)
index 0000000..5a9844f
--- /dev/null
@@ -0,0 +1,154 @@
+/* MIB service - vm.c - implementation of the CTL_VM subtree */
+
+#include "mib.h"
+
+#include <sys/resource.h>
+#include <uvm/uvm_extern.h>
+
+/*
+ * Implementation of CTL_VM VM_LOADAVG.
+ */
+static ssize_t
+mib_vm_loadavg(struct mib_call * call __unused,
+       struct mib_node * node __unused, struct mib_oldp * oldp,
+       struct mib_newp * newp __unused)
+{
+       struct loadavg loadavg;
+       struct loadinfo loadinfo;
+       unsigned long proc_load;
+       u32_t ticks_per_slot, ticks;
+       unsigned int p;
+       int unfilled_ticks;
+       int h, slots, latest, slot;
+       int minutes[3] = { 1, 5, 15 };
+
+       assert(__arraycount(loadavg.ldavg) == __arraycount(minutes));
+
+       if (sys_getloadinfo(&loadinfo) != OK)
+               return EINVAL;
+
+       memset(&loadavg, 0, sizeof(loadavg));
+
+       /*
+        * The following code is inherited from the old MINIX libc.
+        */
+
+       /* How many ticks are missing from the newest-filled slot? */
+       ticks_per_slot = _LOAD_UNIT_SECS * sys_hz();
+       unfilled_ticks =
+           ticks_per_slot - (loadinfo.last_clock % ticks_per_slot);
+
+       for (p = 0; p < __arraycount(loadavg.ldavg); p++) {
+               latest = loadinfo.proc_last_slot;
+               slots = minutes[p] * 60 / _LOAD_UNIT_SECS;
+               proc_load = 0;
+
+               /*
+                * Add up the total number of process ticks for this number
+                * of minutes (minutes[p]).  Start with the newest slot, which
+                * is latest, and count back for the number of slots that
+                * correspond to the right number of minutes.  Take wraparound
+                * into account by calculating the index modulo _LOAD_HISTORY,
+                * which is the number of slots of history kept.
+                */
+               for (h = 0; h < slots; h++) {
+                       slot = (latest - h + _LOAD_HISTORY) % _LOAD_HISTORY;
+                       proc_load += loadinfo.proc_load_history[slot];
+               }
+
+               /*
+                * The load average over this number of minutes is the number
+                * of process-ticks divided by the number of ticks, not
+                * counting the number of ticks the last slot hasn't been
+                * around yet.
+                */
+               ticks = slots * ticks_per_slot - unfilled_ticks;
+
+               loadavg.ldavg[p] = 100UL * proc_load / ticks;
+       }
+
+       loadavg.fscale = 100L;
+
+       return mib_copyout(oldp, 0, &loadavg, sizeof(loadavg));
+}
+
+/*
+ * Implementation of CTL_VM VM_UVMEXP2.
+ */
+static ssize_t
+mib_vm_uvmexp2(struct mib_call * call __unused,
+       struct mib_node * node __unused, struct mib_oldp * oldp,
+       struct mib_newp * newp __unused)
+{
+       struct vm_stats_info vsi;
+       struct uvmexp_sysctl ues;
+       unsigned int shift;
+
+       if (vm_info_stats(&vsi) != OK)
+               return EINVAL;
+
+       memset(&ues, 0, sizeof(ues));
+
+       /*
+        * TODO: by far most of the structure is not filled correctly yet,
+        * since the MINIX3 system does not provide much of the information
+        * exposed by NetBSD.  This will gradually have to be filled in.
+        * For now, we provide just some basic information used by top(1).
+        */
+       ues.pagesize = vsi.vsi_pagesize;
+       ues.pagemask = vsi.vsi_pagesize - 1;
+       for (shift = 0; shift < CHAR_BIT * sizeof(void *); shift++)
+               if ((1U << shift) == vsi.vsi_pagesize)
+                       break;
+       if (shift < CHAR_BIT * sizeof(void *))
+               ues.pageshift = shift;
+       ues.npages = vsi.vsi_total;
+       ues.free = vsi.vsi_free;
+       ues.filepages = vsi.vsi_cached;
+       /*
+        * We use one of the structure's unused fields to expose information
+        * not exposed by NetBSD, namely the largest area of physically
+        * contiguous memory.  If NetBSD repurposes this field, we have to find
+        * another home for it (or expose it through a separate node or so).
+        */
+       ues.unused1 = vsi.vsi_largest;
+
+       return mib_copyout(oldp, 0, &ues, sizeof(ues));
+}
+
+/* The CTL_VM nodes. */
+static struct mib_node mib_vm_table[] = {
+/* 1*/ /* VM_METER: not yet supported */
+/* 2*/ [VM_LOADAVG]            = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT,
+                                   sizeof(struct loadavg), mib_vm_loadavg,
+                                   "loadavg", "System load average history"),
+/* 3*/ /* VM_UVMEXP: not yet supported */
+/* 4*/ /* VM_NKMEMPAGES: not yet supported */
+/* 5*/ [VM_UVMEXP2]            = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT,
+                                   sizeof(struct uvmexp_sysctl),
+                                   mib_vm_uvmexp2, "uvmexp2",
+                                   "Detailed system-wide virtual memory "
+                                   "statistics (MI)"),
+/* 6*/ /* VM_ANONMIN: not yet supported */
+/* 7*/ /* VM_EXECMIN: not yet supported */
+/* 8*/ /* VM_FILEMIN: not yet supported */
+/* 9*/ [VM_MAXSLP]             = MIB_INT(_P | _RO, MAXSLP, "maxslp",
+                                   "Maximum process sleep time before being "
+                                   "swapped"),
+/*10*/ [VM_USPACE]             = MIB_INT(_P | _RO, 0, "uspace", "Number of "
+                                   "bytes allocated for a kernel stack"),
+                                   /* MINIX3 processes don't have k-stacks */
+/*11*/ /* VM_ANONMAX: not yet supported */
+/*12*/ /* VM_EXECMAX: not yet supported */
+/*13*/ /* VM_FILEMAX: not yet supported */
+};
+
+/*
+ * Initialize the CTL_VM subtree.
+ */
+void
+mib_vm_init(struct mib_node * node)
+{
+
+       MIB_INIT_ENODE(node, mib_vm_table);
+}
index 5f3fd6e5ccf8aa009dd988e8aa4f880cfeb60d7a..32cf8c4dbad762cf04b7d4cbee760c9c443ccd7a 100644 (file)
@@ -1,7 +1,10 @@
 
 #include "inc.h"
 
+#include <sys/time.h>
 #include <sys/sysctl.h>
+#include <sys/sched.h>
+#include <sys/resource.h>
 
 struct sysctl_tab {
        int id;
@@ -13,13 +16,272 @@ struct sysctl_tab {
 #define NODE(i,t) { .id = i, .size = __arraycount(t), .tab = t }
 #define PROC(i,s,p) { .id = i, .size = s, .proc = p }
 
+/*
+ * Print CTL_KERN KERN_CLOCKRATE.
+ */
+static int
+put_kern_clockrate(struct trace_proc * proc, const char * name,
+       int type __unused, const void * ptr, vir_bytes addr __unused,
+       size_t size __unused)
+{
+       struct clockinfo *ci;
+
+       ci = (struct clockinfo *)ptr;
+
+       put_value(proc, "hz", "%d", ci->hz);
+       put_value(proc, "tick", "%d", ci->tick);
+       if (verbose > 0) {
+               put_value(proc, "tickadj", "%d", ci->tickadj);
+               put_value(proc, "stathz", "%d", ci->stathz);
+               put_value(proc, "profhz", "%d", ci->profhz);
+               return TRUE;
+       } else
+               return FALSE;
+}
+
+/*
+ * Print CTL_KERN KERN_PROC2.
+ */
+static int
+put_kern_proc2(struct trace_proc * proc, const char * name, int type,
+       const void * ptr, vir_bytes addr, size_t size)
+{
+       const int *mib;
+       const char *text;
+       int i;
+
+       if (type == ST_NAME) {
+               mib = (const int *)ptr;
+
+               for (i = 0; i < size; i++) {
+                       text = NULL;
+
+                       if (i == 0) {
+                               switch (mib[i]) {
+                               case KERN_PROC_ALL: text = "<all>"; break;
+                               case KERN_PROC_PID: text = "<pid>"; break;
+                               case KERN_PROC_PGRP: text = "<pgrp>"; break;
+                               case KERN_PROC_SESSION:
+                                       text = "<session>"; break;
+                               case KERN_PROC_TTY: text = "<tty>"; break;
+                               case KERN_PROC_UID: text = "<uid>"; break;
+                               case KERN_PROC_RUID: text = "<ruid>"; break;
+                               case KERN_PROC_GID: text = "<gid>"; break;
+                               case KERN_PROC_RGID: text = "<rgid>"; break;
+                               }
+                       } else if (i == 1 && mib[0] == KERN_PROC_TTY) {
+                               switch ((dev_t)mib[i]) {
+                               case KERN_PROC_TTY_NODEV:
+                                       text = "<nodev>"; break;
+                               case KERN_PROC_TTY_REVOKE:
+                                       text = "<revoke>"; break;
+                               }
+                       }
+
+                       if (!valuesonly && text != NULL)
+                               put_field(proc, NULL, text);
+                       else
+                               put_value(proc, NULL, "%d", mib[i]);
+               }
+
+               /*
+                * Save the requested structure length, so that we can later
+                * determine how many elements were returned (see below).
+                */
+               proc->sctl_arg = (size == 4) ? mib[2] : 0;
+
+               return 0;
+       }
+
+       if (proc->sctl_arg > 0) {
+               /* TODO: optionally dump struct kinfo_drivers array */
+               put_open(proc, name, 0, "[", ", ");
+               if (size > 0)
+                       put_tail(proc, size / proc->sctl_arg, 0);
+               put_close(proc, "]");
+       } else
+               put_ptr(proc, name, addr);
+
+       return TRUE;
+}
+
+/*
+ * Print CTL_KERN KERN_PROC_ARGS.
+ */
+static int
+put_kern_proc_args(struct trace_proc * proc, const char * name, int type,
+       const void * ptr, vir_bytes addr, size_t size)
+{
+       const int *mib;
+       const char *text;
+       int i, v;
+
+       if (type == ST_NAME) {
+               mib = (const int *)ptr;
+
+               for (i = 0; i < size; i++) {
+                       text = NULL;
+
+                       if (i == 1) {
+                               switch (mib[i]) {
+                               case KERN_PROC_ARGV: text = "<argv>"; break;
+                               case KERN_PROC_ENV: text = "<env>"; break;
+                               case KERN_PROC_NARGV: text = "<nargv>"; break;
+                               case KERN_PROC_NENV: text = "<nenv>"; break;
+                               }
+                       }
+
+                       if (!valuesonly && text != NULL)
+                               put_field(proc, NULL, text);
+                       else
+                               put_value(proc, NULL, "%d", mib[i]);
+               }
+
+               /* Save the subrequest, so that we can later print data. */
+               proc->sctl_arg = (size == 2) ? mib[1] : -999;
+
+               return 0;
+       }
+
+       if ((proc->sctl_arg == KERN_PROC_NARGV ||
+           proc->sctl_arg == KERN_PROC_NENV) && size == sizeof(v) &&
+           mem_get_data(proc->pid, addr, &v, sizeof(v)) >= 0) {
+               put_open(proc, name, PF_NONAME, "{", ", ");
+
+               put_value(proc, NULL, "%d", v);
+
+               put_close(proc, "}");
+       } else
+               put_ptr(proc, name, addr);
+
+       return TRUE;
+}
+
+/*
+ * Print CTL_KERN KERN_CP_TIME.
+ */
+static int
+put_kern_cp_time(struct trace_proc * proc, const char * name __unused,
+       int type, const void * ptr, vir_bytes addr __unused, size_t size)
+{
+       uint64_t *p;
+       unsigned int i;
+       const int *mib;
+
+       if (type == ST_NAME) {
+               mib = (const int *)ptr;
+               for (i = 0; i < size; i++)
+                       put_value(proc, NULL, "%d", mib[i]);
+
+               return 0;
+       }
+
+       p = (uint64_t *)ptr;
+
+       /* TODO: support for multi-CPU results */
+       for (i = 0; i < CPUSTATES; i++)
+               put_value(proc, NULL, "%"PRIu64, p[i]);
+
+       return TRUE;
+}
+
+/*
+ * Print CTL_KERN KERN_CONSDEV.
+ */
+static int
+put_kern_consdev(struct trace_proc * proc, const char * name,
+       int type __unused, const void * ptr, vir_bytes addr __unused,
+       size_t size __unused)
+{
+
+       put_dev(proc, NULL, *(dev_t *)ptr);
+
+       return TRUE;
+}
+
+/*
+ * Print CTL_KERN KERN_DRIVERS.
+ */
+static int
+put_kern_drivers(struct trace_proc * proc, const char * name,
+       int type __unused, const void * ptr __unused, vir_bytes addr __unused,
+       size_t size)
+{
+
+       /* TODO: optionally dump struct kinfo_drivers array */
+       put_open(proc, name, 0, "[", ", ");
+       if (size > 0)
+               put_tail(proc, size / sizeof(struct kinfo_drivers), 0);
+       put_close(proc, "]");
+
+       return TRUE;
+}
+
+/*
+ * Print CTL_KERN KERN_BOOTTIME.
+ */
+static int
+put_kern_boottime(struct trace_proc * proc, const char * name,
+       int type __unused, const void * ptr __unused, vir_bytes addr,
+       size_t size)
+{
+
+       if (size == sizeof(struct timeval))
+               put_struct_timeval(proc, name, 0, addr);
+       else
+               put_ptr(proc, name, addr);
+
+       return TRUE;
+}
+
 /* The CTL_KERN table. */
 static const struct sysctl_tab kern_tab[] = {
+       PROC(KERN_CLOCKRATE, sizeof(struct clockinfo), put_kern_clockrate),
+       PROC(KERN_PROC2, 0, put_kern_proc2),
+       PROC(KERN_PROC_ARGS, 0, put_kern_proc_args),
+       PROC(KERN_CP_TIME, sizeof(uint64_t) * CPUSTATES, put_kern_cp_time),
+       PROC(KERN_CONSDEV, sizeof(dev_t), put_kern_consdev),
+       PROC(KERN_DRIVERS, 0, put_kern_drivers),
+       PROC(KERN_BOOTTIME, 0, put_kern_boottime),
+};
+
+/*
+ * Print CTL_VM VM_LOADAVG.
+ */
+static int
+put_vm_loadavg(struct trace_proc * proc, const char * name __unused,
+       int type __unused, const void * ptr, vir_bytes addr __unused,
+       size_t size __unused)
+{
+       struct loadavg *loadavg;
+       unsigned int i;
+
+       loadavg = (struct loadavg *)ptr;
+
+       put_open(proc, "ldavg", 0, "{", ", ");
+
+       for (i = 0; i < __arraycount(loadavg->ldavg); i++)
+               put_value(proc, NULL, "%"PRIu32, loadavg->ldavg[i]);
+
+       put_close(proc, "}");
+
+       if (verbose > 0) {
+               put_value(proc, "fscale", "%ld", loadavg->fscale);
+
+               return TRUE;
+       } else
+               return FALSE;
+}
+
+/* The CTL_VM table. */
+static const struct sysctl_tab vm_tab[] = {
+       PROC(VM_LOADAVG, sizeof(struct loadavg), put_vm_loadavg),
 };
 
 /* The top-level table, which is indexed by identifier. */
 static const struct sysctl_tab root_tab[] = {
        [CTL_KERN]      = NODE(0, kern_tab),
+       [CTL_VM]        = NODE(0, vm_tab),
 };
 
 /*