SYSCTL # 44
;
vm
- CTL
+ INFO
;
uid 0;
};
# define VM_NOTIFY_SIG_ENDPOINT m1_i1
# define VM_NOTIFY_SIG_IPC m1_i2
-#define VM_CTL (VM_RQ_BASE+40)
-#define VCTL_WHAT m1_i1
-#define VCTL_PARAM m1_i2
-
-/* VCTL_PARAMs */
-#define VCTLP_STATS_MEM 1
-#define VCTLP_STATS_EP 2
+#define VM_INFO (VM_RQ_BASE+40)
+# define VMI_WHAT m2_i1
+# define VMI_EP m2_i2
+# define VMI_COUNT m2_i3
+# define VMI_PTR m2_p1
+# define VMI_NEXT m2_l1
+
+/* VMI_WHAT values. */
+#define VMIW_STATS 1
+#define VMIW_USAGE 2
+#define VMIW_REGION 3
/* Total. */
#define NR_VM_CALLS 41
_PROTOTYPE( int vm_set_priv, (int procnr, void *buf));
_PROTOTYPE( int vm_query_exit, (int *endpt));
+struct vm_stats_info {
+ int vsi_pagesize; /* page size */
+ int vsi_total; /* total number of memory pages */
+ int vsi_free; /* number of free pages */
+ int vsi_largest; /* largest number of consecutive free pages */
+};
+
+struct vm_usage_info {
+ vir_bytes vui_total; /* total amount of process memory */
+ vir_bytes vui_common; /* part of memory mapped in more than once */
+ vir_bytes vui_shared; /* shared (non-COW) part of common memory */
+};
+
+struct vm_region_info {
+ int vri_seg; /* segment of virtual region (T or D) */
+ vir_bytes vri_addr; /* base address of region */
+ vir_bytes vri_length; /* length of region */
+ int vri_prot; /* protection flags (PROT_) */
+ int vri_flags; /* memory flags (subset of MAP_) */
+};
+
+#define MAX_VRI_COUNT 64 /* max. number of regions provided at once */
+
+_PROTOTYPE( int vm_info_stats, (struct vm_stats_info *vfi) );
+_PROTOTYPE( int vm_info_usage, (endpoint_t who,
+ struct vm_usage_info *vui) );
+_PROTOTYPE( int vm_info_region, (endpoint_t who,
+ struct vm_region_info *vri, int count, vir_bytes *next) );
+
#endif /* _MINIX_VM_H */
taskcall.c \
ds.c \
vm_brk.c \
- vm_ctl.c \
vm_exec_newmem.c \
vm_exit.c \
vm_notify_sig.c \
vm_fork.c \
+ vm_info.c \
vm_map_phys.c \
vm_umap.c \
vm_push_sig.c"
+++ /dev/null
-
-#include "syslib.h"
-
-#include <minix/vm.h>
-
-/*===========================================================================*
- * vm_umap *
- *===========================================================================*/
-PUBLIC int vm_ctl(int what, int param)
-{
- message m;
- int result;
-
- m.VCTL_WHAT = what;
- m.VCTL_PARAM = param;
- return _taskcall(VM_PROC_NR, VM_CTL, &m);
-}
-
--- /dev/null
+
+#include "syslib.h"
+
+#include <minix/vm.h>
+
+/*===========================================================================*
+ * vm_info_stats *
+ *===========================================================================*/
+PUBLIC int vm_info_stats(struct vm_stats_info *vsi)
+{
+ message m;
+
+ m.VMI_WHAT = VMIW_STATS;
+ m.VMI_PTR = (void *) vsi;
+
+ return _taskcall(VM_PROC_NR, VM_INFO, &m);
+}
+
+/*===========================================================================*
+ * vm_info_usage *
+ *===========================================================================*/
+PUBLIC int vm_info_usage(endpoint_t who, struct vm_usage_info *vui)
+{
+ message m;
+
+ m.VMI_WHAT = VMIW_USAGE;
+ m.VMI_EP = who;
+ m.VMI_PTR = (void *) vui;
+
+ return _taskcall(VM_PROC_NR, VM_INFO, &m);
+}
+
+/*===========================================================================*
+ * vm_info_region *
+ *===========================================================================*/
+PUBLIC int vm_info_region(endpoint_t who, struct vm_region_info *vri,
+ int count, vir_bytes *next)
+{
+ message m;
+ int result;
+
+ m.VMI_WHAT = VMIW_REGION;
+ m.VMI_EP = who;
+ m.VMI_COUNT = count;
+ m.VMI_PTR = (void *) vri;
+ m.VMI_NEXT = *next;
+
+ if ((result = _taskcall(VM_PROC_NR, VM_INFO, &m)) != OK)
+ return result;
+
+ *next = m.VMI_NEXT;
+ return m.VMI_COUNT;
+}
+
LDFLAGS = -i
LIBS = -lsys
-OBJ = main.o dmp.o dmp_kernel.o dmp_pm.o dmp_fs.o dmp_rs.o dmp_ds.o
+OBJ = main.o dmp.o dmp_kernel.o dmp_pm.o dmp_fs.o dmp_rs.o dmp_ds.o dmp_vm.o
# build local binary
all build: $(SERVER)
{ F5, monparams_dmp, "Boot monitor parameters" },
{ F6, irqtab_dmp, "IRQ hooks and policies" },
{ F7, kmessages_dmp, "Kernel messages" },
- { F8, vm_dmp, "VM status" },
+ { F8, vm_dmp, "VM status and process maps" },
{ F10, kenv_dmp, "Kernel parameters" },
{ F11, timing_dmp, "Timing details (if enabled)" },
{ SF1, mproc_dmp, "Process manager process table" },
printf(" %10s. %s\n", key_name(hooks[h].key), hooks[h].name);
printf("\n");
}
-
-/*===========================================================================*
- * vm_dmp *
- *===========================================================================*/
-PUBLIC void vm_dmp(void)
-{
- vm_ctl(VCTLP_STATS_MEM, 0);
-
-}
#include "inc.h"
#include "../ds/store.h"
+#define LINES 22
+
PRIVATE struct data_store ds_store[NR_DS_KEYS];
PUBLIC void data_store_dmp()
printf("Data store contents:\n");
printf("-slot- ------key------ -----owner----- ---type--- ----value---\n");
- for(i = prev_i; i < NR_DS_KEYS; i++) {
+ for(i = prev_i; i < NR_DS_KEYS && n < LINES; i++) {
p = &ds_store[i];
if(!(p->flags & DSF_IN_USE))
continue;
return;
}
- if(n++ == 21)
- break;
+ n++;
}
if (i >= NR_DS_KEYS) i = 0;
--- /dev/null
+/* Debugging dump procedures for the VM server. */
+
+#include "inc.h"
+#include <sys/mman.h>
+#include <minix/vm.h>
+#include <timers.h>
+#include "../../kernel/proc.h"
+
+#define LINES 24
+
+PRIVATE void print_region(struct vm_region_info *vri)
+{
+ char c;
+
+ switch (vri->vri_seg) {
+ case T: c = 'T'; break;
+ case D: c = 'D'; break;
+ default: c = '?';
+ }
+
+ printf(" %c %08lx-%08lx %c%c%c %c (%lu kB)\n", c, vri->vri_addr,
+ vri->vri_addr + vri->vri_length,
+ (vri->vri_prot & PROT_READ) ? 'r' : '-',
+ (vri->vri_prot & PROT_WRITE) ? 'w' : '-',
+ (vri->vri_prot & PROT_EXEC) ? 'x' : '-',
+ (vri->vri_flags & MAP_SHARED) ? 's' : 'p',
+ vri->vri_length / 1024L);
+}
+
+PUBLIC void vm_dmp()
+{
+ static struct proc proc[NR_TASKS + NR_PROCS];
+ static struct vm_region_info vri[LINES];
+ struct vm_stats_info vsi;
+ struct vm_usage_info vui;
+ static int prev_i = -1;
+ static vir_bytes prev_base = 0;
+ int r, r2, i, j, first, n = 0;
+
+ if (prev_i == -1) {
+ if ((r = vm_info_stats(&vsi)) != OK) {
+ report("IS", "warning: couldn't talk to VM", r);
+ return;
+ }
+
+ printf("Total %u kB, free %u kB, largest free area %u kB\n",
+ vsi.vsi_total * (vsi.vsi_pagesize / 1024),
+ vsi.vsi_free * (vsi.vsi_pagesize / 1024),
+ vsi.vsi_largest * (vsi.vsi_pagesize / 1024));
+ n++;
+ printf("\n");
+ n++;
+
+ prev_i++;
+ }
+
+ if ((r = sys_getproctab(proc)) != OK) {
+ report("IS", "warning: couldn't get copy of process table", r);
+ return;
+ }
+
+ for (i = prev_i; i < NR_TASKS + NR_PROCS && n < LINES; i++, prev_base = 0) {
+ if (i < NR_TASKS || isemptyp(&proc[i])) continue;
+
+ /* The first batch dump for each process contains a header line. */
+ first = prev_base == 0;
+
+ r = vm_info_region(proc[i].p_endpoint, vri, LINES - first, &prev_base);
+
+ if (r < 0) {
+ printf("Process %d (%s): error %d\n",
+ proc[i].p_endpoint, proc[i].p_name, r);
+ n++;
+ continue;
+ }
+
+ if (first) {
+ /* The entire batch should fit on the screen. */
+ if (n + 1 + r > LINES) {
+ prev_base = 0; /* restart on next page */
+ break;
+ }
+
+ if ((r2 = vm_info_usage(proc[i].p_endpoint, &vui)) != OK) {
+ printf("Process %d (%s): error %d\n",
+ proc[i].p_endpoint, proc[i].p_name, r2);
+ n++;
+ continue;
+ }
+
+ printf("Process %d (%s): total %lu kB, common %lu kB, "
+ "shared %lu kB\n",
+ proc[i].p_endpoint, proc[i].p_name,
+ vui.vui_total / 1024L, vui.vui_common / 1024L,
+ vui.vui_shared / 1024L);
+ n++;
+ }
+
+ for (j = 0; j < r; j++) {
+ print_region(&vri[j]);
+ n++;
+ }
+
+ if (n > LINES) printf("IS: internal error\n");
+ if (n == LINES) break;
+
+ /* This may have to wipe out the "--more--" from below. */
+ printf(" \n");
+ n++;
+ }
+
+ if (i >= NR_TASKS + NR_PROCS) {
+ i = -1;
+ prev_base = 0;
+ }
+ else printf("--more--\r");
+ prev_i = i;
+}
+
{ "GETPHYS", VM_GETPHYS },
{ "GETREFCNT", VM_GETREF },
{ "QUERYEXIT", VM_QUERY_EXIT },
- { "CTL", VM_CTL },
+ { "INFO", VM_INFO },
{ NULL, 0 },
};
addr_init(&addravl);
+ total_pages = 0;
+
/* Use the chunks of physical memory to allocate holes. */
for (i=NR_MEMS-1; i>=0; i--) {
if (chunks[i].size > 0) {
if(first || from < mem_low) mem_low = from;
if(first || to > mem_high) mem_high = to;
FREE_MEM(chunks[i].base, chunks[i].size);
+ total_pages += chunks[i].size;
first = 0;
}
}
EXTERN long vm_sanitychecklevel;
#endif
+/* total number of memory pages */
+EXTERN int total_pages;
+
/* vm operation mode state and values */
EXTERN long vm_paged;
return bufstr;
}
+/*===========================================================================*
+ * arch_map2info *
+ *===========================================================================*/
+PUBLIC vir_bytes arch_map2info(struct vmproc *vmp, vir_bytes addr, int *seg,
+ int *prot)
+{
+ vir_bytes textstart = CLICK2ABS(vmp->vm_arch.vm_seg[T].mem_phys);
+ vir_bytes textend = textstart +
+ CLICK2ABS(vmp->vm_arch.vm_seg[T].mem_len);
+ vir_bytes datastart = CLICK2ABS(vmp->vm_arch.vm_seg[D].mem_phys);
+
+ /* The protection to be returned here is that of the segment. */
+ if(addr < textstart) {
+ *seg = D;
+ *prot = PROT_READ | PROT_WRITE | PROT_EXEC;
+ return addr;
+ } else if(addr < datastart) {
+ *seg = T;
+ *prot = PROT_READ | PROT_EXEC;
+ return addr - textstart;
+ } else {
+ *seg = D;
+ if (textstart == textend) /* common I&D? */
+ *prot = PROT_READ | PROT_WRITE | PROT_EXEC;
+ else
+ *prot = PROT_READ | PROT_WRITE;
+ return addr - datastart;
+ }
+}
+
/*===========================================================================*
* arch_addrok *
*===========================================================================*/
CALLMAP(VM_GETPHYS, do_get_phys);
CALLMAP(VM_SHM_UNMAP, do_shared_unmap);
CALLMAP(VM_GETREF, do_get_refcount);
- CALLMAP(VM_CTL, do_ctl);
+ CALLMAP(VM_INFO, do_info);
CALLMAP(VM_QUERY_EXIT, do_query_exit);
/* Sanity checks */
#include <minix/ipc.h>
#include <minix/endpoint.h>
#include <minix/safecopies.h>
+#include <minix/vm.h>
#include <timers.h>
#include <stdio.h>
#include <pagetable.h>
struct mem_map *map_ptr));
_PROTOTYPE( int vm_isokendpt, (endpoint_t ep, int *proc) );
_PROTOTYPE( int get_stack_ptr, (int proc_nr, vir_bytes *sp) );
-_PROTOTYPE( int do_ctl, (message *) );
+_PROTOTYPE( int do_info, (message *) );
/* exit.c */
_PROTOTYPE( void clear_proc, (struct vmproc *vmp) );
struct vir_region *region, struct phys_region *ph));
_PROTOTYPE(void pb_unreferenced, (struct vir_region *region,
struct phys_region *pr));
+_PROTOTYPE(void get_usage_info, (struct vmproc *vmp,
+ struct vm_usage_info *vui));
+_PROTOTYPE(int get_region_info, (struct vmproc *vmp,
+ struct vm_region_info *vri, int count, vir_bytes *nextp));
#if SANITYCHECKS
_PROTOTYPE(void map_sanitycheck,(char *file, int line));
#endif
/* $(ARCH)/vm.c */
-_PROTOTYPE( vir_bytes, arch_map2vir(struct vmproc *vmp, vir_bytes addr));
-_PROTOTYPE( char *, arch_map2str(struct vmproc *vmp, vir_bytes addr));
-_PROTOTYPE( vir_bytes, arch_vir2map(struct vmproc *vmp, vir_bytes addr));
-_PROTOTYPE( vir_bytes, arch_vir2map_text(struct vmproc *vmp, vir_bytes addr));
-_PROTOTYPE( vir_bytes, arch_addrok(struct vmproc *vmp, vir_bytes addr));
+_PROTOTYPE( vir_bytes arch_map2vir, (struct vmproc *vmp, vir_bytes addr));
+_PROTOTYPE( char *arch_map2str, (struct vmproc *vmp, vir_bytes addr));
+_PROTOTYPE( vir_bytes arch_map2info, (struct vmproc *vmp, vir_bytes addr,
+ int *space, int *prot));
+_PROTOTYPE( vir_bytes arch_vir2map, (struct vmproc *vmp, vir_bytes addr));
+_PROTOTYPE( vir_bytes arch_vir2map_text, (struct vmproc *vmp, vir_bytes addr));
+_PROTOTYPE( vir_bytes arch_addrok, (struct vmproc *vmp, vir_bytes addr));
/* rs.c */
_PROTOTYPE(int do_rs_set_priv, (message *m));
return OK;
}
+/*========================================================================*
+ * get_usage_info *
+ *========================================================================*/
+PUBLIC void get_usage_info(struct vmproc *vmp, struct vm_usage_info *vui)
+{
+ struct vir_region *vr;
+ physr_iter iter;
+ struct phys_region *ph;
+ vir_bytes len;
+
+ memset(vui, 0, sizeof(*vui));
+
+ for(vr = vmp->vm_regions; vr; vr = vr->next) {
+ physr_start_iter_least(vr->phys, &iter);
+ while((ph = physr_get_iter(&iter))) {
+ len = ph->ph->length;
+
+ /* All present pages are counted towards the total. */
+ vui->vui_total += len;
+
+ if (ph->ph->refcount > 1) {
+ /* Any page with a refcount > 1 is common. */
+ vui->vui_common += len;
+
+ /* Any common, non-COW page is shared. */
+ if (vr->flags & VR_SHARED ||
+ ph->ph->share_flag == PBSH_SMAP)
+ vui->vui_shared += len;
+ }
+ physr_incr_iter(&iter);
+ }
+ }
+}
+
+/*===========================================================================*
+ * get_region_info *
+ *===========================================================================*/
+PUBLIC int get_region_info(struct vmproc *vmp, struct vm_region_info *vri,
+ int max, vir_bytes *nextp)
+{
+ struct vir_region *vr;
+ vir_bytes next;
+ int count;
+
+ next = *nextp;
+
+ if (!max) return 0;
+
+ for(vr = vmp->vm_regions; vr; vr = vr->next)
+ if (vr->vaddr >= next) break;
+
+ if (!vr) return 0;
+
+ for(count = 0; vr && count < max; vr = vr->next, count++, vri++) {
+ vri->vri_addr = arch_map2info(vmp, vr->vaddr, &vri->vri_seg,
+ &vri->vri_prot);
+ vri->vri_length = vr->length;
+
+ /* "AND" the provided protection with per-page protection. */
+ if (!(vr->flags & VR_WRITABLE))
+ vri->vri_prot &= ~PROT_WRITE;
+
+ vri->vri_flags = (vr->flags & VR_SHARED) ? MAP_SHARED : 0;
+
+ next = vr->vaddr + vr->length;
+ }
+
+ *nextp = next;
+ return count;
+}
/*========================================================================*
* regionprintstats *
{
*proc = _ENDPOINT_P(endpoint);
if(*proc < 0 || *proc >= NR_PROCS)
- vm_panic("crazy slot number", *proc);
+ return EINVAL;
if(*proc >= 0 && endpoint != vmproc[*proc].vm_endpoint)
return EDEADSRCDST;
if(*proc >= 0 && !(vmproc[*proc].vm_flags & VMF_INUSE))
}
/*===========================================================================*
- * do_ctl *
+ * do_info *
*===========================================================================*/
-PUBLIC int do_ctl(message *m)
+PUBLIC int do_info(message *m)
{
- int pages, nodes;
- int pr;
+ struct vm_stats_info vsi;
+ struct vm_usage_info vui;
+ static struct vm_region_info vri[MAX_VRI_COUNT];
struct vmproc *vmp;
+ vir_bytes addr, size, next, ptr;
+ int r, pr, dummy, count;
- switch(m->VCTL_WHAT) {
- case VCTLP_STATS_MEM:
- printmemstats();
- break;
- case VCTLP_STATS_EP:
- if(vm_isokendpt(m->VCTL_PARAM, &pr) != OK)
- return EINVAL;
- printregionstats(&vmproc[pr]);
- break;
- default:
+ if (vm_isokendpt(m->m_source, &pr) != OK)
+ return EINVAL;
+ vmp = &vmproc[pr];
+
+ ptr = (vir_bytes) m->VMI_PTR;
+
+ switch(m->VMI_WHAT) {
+ case VMIW_STATS:
+ vsi.vsi_pagesize = VM_PAGE_SIZE;
+ vsi.vsi_total = total_pages;
+ memstats(&dummy, &vsi.vsi_free, &vsi.vsi_largest);
+
+ addr = (vir_bytes) &vsi;
+ size = sizeof(vsi);
+
+ break;
+
+ case VMIW_USAGE:
+ if (vm_isokendpt(m->VMI_EP, &pr) != OK)
+ return EINVAL;
+
+ get_usage_info(&vmproc[pr], &vui);
+
+ addr = (vir_bytes) &vui;
+ size = sizeof(vui);
+
+ break;
+
+ case VMIW_REGION:
+ if (vm_isokendpt(m->VMI_EP, &pr) != OK)
return EINVAL;
+
+ count = MIN(m->VMI_COUNT, MAX_VRI_COUNT);
+ next = m->VMI_NEXT;
+
+ count = get_region_info(&vmproc[pr], vri, count, &next);
+
+ m->VMI_COUNT = count;
+ m->VMI_NEXT = next;
+
+ addr = (vir_bytes) vri;
+ size = sizeof(vri[0]) * count;
+
+ break;
+
+ default:
+ return EINVAL;
}
- return OK;
+ if (size == 0)
+ return OK;
+
+ /* Make sure that no page faults can occur while copying out. A page
+ * fault would cause the kernel to send a notify to us, while we would
+ * be waiting for the result of the copy system call, resulting in a
+ * deadlock. Note that no memory mapping can be undone without the
+ * involvement of VM, so we are safe until we're done.
+ */
+ r = handle_memory(vmp, arch_vir2map(vmp, ptr), size, 1 /*wrflag*/);
+ if (r != OK) return r;
+
+ /* Now that we know the copy out will succeed, perform the actual copy
+ * operation.
+ */
+ return sys_datacopy(SELF, addr,
+ (vir_bytes) vmp->vm_endpoint, ptr, size);
}