diff --git a/etc/system.conf b/etc/system.conf index d8d0c9a9f..276f832b4 100644 --- a/etc/system.conf +++ b/etc/system.conf @@ -200,7 +200,7 @@ service is SYSCTL # 44 ; vm - CTL + INFO ; uid 0; }; diff --git a/include/minix/com.h b/include/minix/com.h index 0386e6c24..443e50426 100644 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -974,13 +974,17 @@ # 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 +#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 -/* VCTL_PARAMs */ -#define VCTLP_STATS_MEM 1 -#define VCTLP_STATS_EP 2 +/* VMI_WHAT values. */ +#define VMIW_STATS 1 +#define VMIW_USAGE 2 +#define VMIW_REGION 3 /* Total. */ #define NR_VM_CALLS 41 diff --git a/include/minix/vm.h b/include/minix/vm.h index 496abc662..3755dfa96 100644 --- a/include/minix/vm.h +++ b/include/minix/vm.h @@ -27,5 +27,34 @@ _PROTOTYPE( int vm_ctl, (int what, int param)); _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 */ diff --git a/lib/syslib/Makefile.in b/lib/syslib/Makefile.in index 9549cc31b..34416a6ce 100644 --- a/lib/syslib/Makefile.in +++ b/lib/syslib/Makefile.in @@ -80,11 +80,11 @@ libsys_FILES=" \ 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" diff --git a/lib/syslib/vm_ctl.c b/lib/syslib/vm_ctl.c deleted file mode 100644 index 545d5cce5..000000000 --- a/lib/syslib/vm_ctl.c +++ /dev/null @@ -1,18 +0,0 @@ - -#include "syslib.h" - -#include - -/*===========================================================================* - * 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); -} - diff --git a/lib/syslib/vm_info.c b/lib/syslib/vm_info.c new file mode 100644 index 000000000..88fdc07d2 --- /dev/null +++ b/lib/syslib/vm_info.c @@ -0,0 +1,54 @@ + +#include "syslib.h" + +#include + +/*===========================================================================* + * 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; +} + diff --git a/servers/is/Makefile b/servers/is/Makefile index 5edfab3ff..59404167e 100644 --- a/servers/is/Makefile +++ b/servers/is/Makefile @@ -20,7 +20,7 @@ CFLAGS = $(CPROFILE) $(CPPFLAGS) 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) diff --git a/servers/is/dmp.c b/servers/is/dmp.c index 75618a0f3..f5d9c782b 100644 --- a/servers/is/dmp.c +++ b/servers/is/dmp.c @@ -23,7 +23,7 @@ struct hook_entry { { 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" }, @@ -126,12 +126,3 @@ PUBLIC void mapping_dmp(void) 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); - -} diff --git a/servers/is/dmp_ds.c b/servers/is/dmp_ds.c index 5e9086cd1..8d6769078 100644 --- a/servers/is/dmp_ds.c +++ b/servers/is/dmp_ds.c @@ -1,6 +1,8 @@ #include "inc.h" #include "../ds/store.h" +#define LINES 22 + PRIVATE struct data_store ds_store[NR_DS_KEYS]; PUBLIC void data_store_dmp() @@ -16,7 +18,7 @@ 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; @@ -43,8 +45,7 @@ PUBLIC void data_store_dmp() return; } - if(n++ == 21) - break; + n++; } if (i >= NR_DS_KEYS) i = 0; diff --git a/servers/is/dmp_vm.c b/servers/is/dmp_vm.c new file mode 100644 index 000000000..3201a1fdd --- /dev/null +++ b/servers/is/dmp_vm.c @@ -0,0 +1,119 @@ +/* Debugging dump procedures for the VM server. */ + +#include "inc.h" +#include +#include +#include +#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; +} + diff --git a/servers/rs/service.c b/servers/rs/service.c index d6d26dc98..695ece7b4 100644 --- a/servers/rs/service.c +++ b/servers/rs/service.c @@ -803,7 +803,7 @@ struct { "GETPHYS", VM_GETPHYS }, { "GETREFCNT", VM_GETREF }, { "QUERYEXIT", VM_QUERY_EXIT }, - { "CTL", VM_CTL }, + { "INFO", VM_INFO }, { NULL, 0 }, }; diff --git a/servers/vm/alloc.c b/servers/vm/alloc.c index 0af48ed8a..5ce525927 100644 --- a/servers/vm/alloc.c +++ b/servers/vm/alloc.c @@ -405,6 +405,8 @@ struct memory *chunks; /* list of free memory chunks */ 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) { @@ -413,6 +415,7 @@ struct memory *chunks; /* list of free memory chunks */ 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; } } diff --git a/servers/vm/glo.h b/servers/vm/glo.h index 0889a8f06..49f82ec82 100644 --- a/servers/vm/glo.h +++ b/servers/vm/glo.h @@ -24,6 +24,9 @@ EXTERN int incheck; EXTERN long vm_sanitychecklevel; #endif +/* total number of memory pages */ +EXTERN int total_pages; + /* vm operation mode state and values */ EXTERN long vm_paged; diff --git a/servers/vm/i386/vm.c b/servers/vm/i386/vm.c index 0f4be46ea..f23534e7a 100644 --- a/servers/vm/i386/vm.c +++ b/servers/vm/i386/vm.c @@ -61,6 +61,36 @@ PUBLIC char *arch_map2str(struct vmproc *vmp, vir_bytes addr) 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 * *===========================================================================*/ diff --git a/servers/vm/main.c b/servers/vm/main.c index fcf097666..c734f6c0e 100644 --- a/servers/vm/main.c +++ b/servers/vm/main.c @@ -347,7 +347,7 @@ PRIVATE int sef_cb_init_fresh(int type, sef_init_info_t *info) 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 */ diff --git a/servers/vm/proto.h b/servers/vm/proto.h index daf6ac983..d7d296d84 100644 --- a/servers/vm/proto.h +++ b/servers/vm/proto.h @@ -10,6 +10,7 @@ struct phys_region; #include #include #include +#include #include #include #include @@ -42,7 +43,7 @@ _PROTOTYPE( void reserve_proc_mem, (struct memory *mem_chunks, 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) ); @@ -168,16 +169,22 @@ _PROTOTYPE(int map_copy_ph_block, (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)); diff --git a/servers/vm/region.c b/servers/vm/region.c index 33768ada7..616c5c51e 100644 --- a/servers/vm/region.c +++ b/servers/vm/region.c @@ -1455,6 +1455,76 @@ PUBLIC int map_get_ref(struct vmproc *vmp, vir_bytes addr, u8_t *cnt) 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 * diff --git a/servers/vm/utility.c b/servers/vm/utility.c index a9d4182b6..bebf9fb51 100644 --- a/servers/vm/utility.c +++ b/servers/vm/utility.c @@ -122,7 +122,7 @@ PUBLIC int vm_isokendpt(endpoint_t endpoint, int *proc) { *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)) @@ -166,27 +166,82 @@ char *brk_addr; } /*===========================================================================* - * 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); }