./usr/tests/minix-posix/test71 minix-sys
./usr/tests/minix-posix/test72 minix-sys
./usr/tests/minix-posix/test73 minix-sys
+./usr/tests/minix-posix/test74 minix-sys
./usr/tests/minix-posix/test7 minix-sys
./usr/tests/minix-posix/test8 minix-sys
./usr/tests/minix-posix/test9 minix-sys
menu=Start MINIX 3:load_mods /boot/minix_default/mod*;multiboot /boot/minix_default/kernel rootdevname=$rootdevname $args
menu=Start latest MINIX 3:load_mods /boot/minix_latest/mod*;multiboot /boot/minix_latest/kernel rootdevname=$rootdevname $args
menu=Start latest MINIX 3 in single user mode:load_mods /boot/minix_latest/mod*;multiboot /boot/minix_latest/kernel rootdevname=$rootdevname bootopts=-s $args
+menu=Start latest MINIX 3 with file mmap:load_mods /boot/minix_latest/mod*;multiboot /boot/minix_latest/kernel rootdevname=$rootdevname filemap=1 $args
menu=Edit menu option:edit
menu=Drop to boot prompt:prompt
mmap.c slaballoc.c region.c pagefaults.c \
rs.c queryexit.c pb.c regionavl.c \
mem_anon.c mem_directphys.c mem_anon_contig.c mem_shared.c \
- mem_cache.c cache.c
+ mem_cache.c cache.c vfs.c mem_file.c fdref.c
.if ${MACHINE_ARCH} == "earm"
LDFLAGS+= -T ${.CURDIR}/arch/${MACHINE_ARCH}/vm.lds
pdeslot * ARCH_PAGEDIR_SIZE);
#endif
-#if 0
- printf("VM: slot %d endpoint %d has pde val 0x%lx at kernel address 0x%lx\n",
- slot, who->vm_endpoint, page_directories[slot], pdes);
-#endif
/* Tell kernel about new page table root. */
return sys_vmctl_set_addrspace(who->vm_endpoint, pt->pt_dir_phys, pdes);
}
--- /dev/null
+
+/* File that implements the 'fdref' data structure. It keeps track
+ * of how many times a particular fd (per process) is referenced by
+ * mmapped objects.
+ *
+ * This is used to
+ * - have many references to the same file, without needing an FD each
+ * - deciding when we have to close an FD (last reference disappears)
+ *
+ * Examples:
+ * - if a file-mmapped region is split, the refcount increases; there are
+ * now two regions referencing the same FD. We can't simply close the
+ * FD once either region is unmapped, as the pagefaults for the other
+ * would stop working. So we increase the refcount to that fd.
+ * - if a new file-maped region is requested, we might find out it's the
+ * same dev/inode the same process already has referenced. we could
+ * decide to close the new reference and use an existing one, so
+ * references to the same file aren't fd-limited.
+ * - if a file-mapped region is copied, we have to create a new
+ * fdref object, as the source process might disappear; we have to
+ * use the new process' fd for it.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <minix/hash.h>
+
+#include "proto.h"
+#include "vm.h"
+#include "fdref.h"
+#include "vmproc.h"
+#include "glo.h"
+
+static struct fdref *fdrefs;
+
+void fdref_sanitycheck(void)
+{
+ struct vmproc *vmp;
+ region_iter v_iter;
+ struct fdref *fr;
+ static int prevopen = 0;
+ int openfd = 0;
+
+ for(fr = fdrefs; fr; fr = fr->next) {
+ struct fdref *fr2;
+ for(fr2 = fdrefs; fr2; fr2 = fr2->next) {
+ if(fr == fr2) continue;
+ if(fr->fd == fr2->fd) {
+ printf("equal fd omg\n");
+ util_stacktrace();
+ }
+ if(fr->ino == fr2->ino && fr->dev == fr2->dev) {
+ printf("equal metadata omg\n");
+ util_stacktrace();
+ }
+ }
+ openfd++;
+ }
+
+ for(fr = fdrefs; fr; fr = fr->next) {
+ fr->counting = 0;
+ }
+
+ for(vmp = vmproc; vmp < &vmproc[VMP_NR]; vmp++) {
+ struct vir_region *vr;
+ if(!(vmp->vm_flags & VMF_INUSE))
+ continue;
+ region_start_iter_least(&vmp->vm_regions_avl, &v_iter);
+ while((vr = region_get_iter(&v_iter))) {
+ if(vr->def_memtype == &mem_type_mappedfile && vr->param.file.inited) {
+ vr->param.file.fdref->counting++;
+ }
+ region_incr_iter(&v_iter);
+ }
+
+ }
+
+ for(fr = fdrefs; fr; fr = fr->next) {
+ if(fr->counting != fr->refcount) {
+ printf("counting %d != refcount %d\n",
+ fr->counting, fr->refcount);
+ util_stacktrace();
+ }
+ }
+
+ if(prevopen != openfd && openfd > 100) {
+ printf("%d open\n", openfd);
+ prevopen = openfd;
+ }
+}
+
+struct fdref *fdref_new(struct vmproc *owner, ino_t ino, dev_t dev, int fd)
+{
+ struct fdref *fdref;
+
+ if(!SLABALLOC(fdref)) return NULL;
+
+ fdref->fd = fd;
+ fdref->refcount = 0;
+ fdref->dev = dev;
+ fdref->ino = ino;
+ fdref->next = fdrefs;
+ fdrefs = fdref;
+
+ return fdref;
+}
+
+void fdref_ref(struct fdref *ref, struct vir_region *region)
+{
+ assert(ref);
+ region->param.file.fdref = ref;
+ ref->refcount++;
+}
+
+void fdref_deref(struct vir_region *region)
+{
+ struct fdref *ref = region->param.file.fdref;
+ int fd;
+
+ assert(ref);
+ assert(ref->refcount > 0);
+
+ fd = ref->fd;
+ region->param.file.fdref = NULL;
+ ref->refcount--;
+ assert(ref->refcount >= 0);
+ if(ref->refcount > 0) return;
+
+ if(fdrefs == ref) fdrefs = ref->next;
+ else {
+ struct fdref *r;
+ for(r = fdrefs; r->next != ref; r = r->next)
+ ;
+ assert(r);
+ assert(r->next == ref);
+ r->next = ref->next;
+ }
+
+ SLABFREE(ref);
+ ref = NULL;
+
+ /* If the last reference has disappeared, free the
+ * ref object and asynchronously close the fd in VFS.
+ *
+ * We don't need a callback as a close failing, although
+ * unexpected, isn't a problem and can't be handled. VFS
+ * will print a diagnostic.
+ */
+ if(vfs_request(VMVFSREQ_FDCLOSE, fd, region->parent,
+ 0, 0, NULL, NULL, NULL, 0) != OK) {
+ panic("fdref_deref: could not send close request");
+ }
+}
+
+struct fdref *fdref_dedup_or_new(struct vmproc *owner,
+ ino_t ino, dev_t dev, int fd, int mayclose)
+{
+ struct fdref *fr;
+
+ for(fr = fdrefs; fr; fr = fr->next) {
+ if(ino == fr->ino && dev == fr->dev) {
+ if(fd == fr->fd) {
+ return fr;
+ }
+ if(!mayclose) continue;
+ if(vfs_request(VMVFSREQ_FDCLOSE, fd, owner,
+ 0, 0, NULL, NULL, NULL, 0) != OK) {
+ printf("fdref_dedup_or_new: could not close\n");
+ }
+ return fr;
+ }
+ }
+
+ return fdref_new(owner, ino, dev, fd);
+}
+
--- /dev/null
+
+#ifndef _FDREF_H
+#define _FDREF_H 1
+
+#include <minix/callnr.h>
+#include <minix/com.h>
+#include <minix/config.h>
+#include <minix/const.h>
+#include <minix/ds.h>
+#include <minix/endpoint.h>
+#include <minix/keymap.h>
+#include <minix/minlib.h>
+#include <minix/type.h>
+#include <minix/ipc.h>
+#include <minix/sysutil.h>
+#include <minix/syslib.h>
+#include <minix/const.h>
+
+struct fdref {
+ int fd;
+ int refcount;
+ dev_t dev;
+ ino_t ino;
+ struct fdref *next;
+ int counting; /* sanity check */
+} *fdref;
+
+#endif
+
EXTERN struct vmproc vmproc[VMP_NR];
+long enable_filemap;
+
EXTERN kinfo_t kernel_boot_info;
#if SANITYCHECKS
panic("couldn't get bootinfo: %d", s);
}
+ /* Turn file mmap on? */
+ env_parse("filemap", "d", 0, &enable_filemap, 0, 1);
+
/* Sanity check */
assert(kernel_boot_info.mmap_size > 0);
assert(kernel_boot_info.mods_with_kernel > 0);
CALLMAP(VM_WILLEXIT, do_willexit);
CALLMAP(VM_NOTIFY_SIG, do_notify_sig);
+ /* Calls from VFS. */
+ CALLMAP(VM_VFS_REPLY, do_vfs_reply);
+ CALLMAP(VM_VFS_MMAP, do_vfs_mmap);
+
/* Calls from RS */
CALLMAP(VM_RS_SET_PRIV, do_rs_set_priv);
CALLMAP(VM_RS_UPDATE, do_rs_update);
printf("VM: map_pf failed\n");
return ENOMEM;
}
-
assert(!vr->param.pb_cache);
}
--- /dev/null
+
+/* This file implements the methods of memory-mapped files. */
+
+#include <assert.h>
+
+#include "proto.h"
+#include "vm.h"
+#include "region.h"
+#include "glo.h"
+#include "cache.h"
+
+/* These functions are static so as to not pollute the
+ * global namespace, and are accessed through their function
+ * pointers.
+ */
+
+static void mappedfile_split(struct vmproc *vmp, struct vir_region *vr,
+ struct vir_region *r1, struct vir_region *r2);
+static int mappedfile_unreference(struct phys_region *pr);
+static int mappedfile_pagefault(struct vmproc *vmp, struct vir_region *region,
+ struct phys_region *ph, int write, vfs_callback_t callback, void *, int);
+static int mappedfile_sanitycheck(struct phys_region *pr, char *file, int line);
+static int mappedfile_writable(struct phys_region *pr);
+static int mappedfile_copy(struct vir_region *vr, struct vir_region *newvr);
+static int mappedfile_lowshrink(struct vir_region *vr, vir_bytes len);
+static void mappedfile_delete(struct vir_region *region);
+
+struct mem_type mem_type_mappedfile = {
+ .name = "file-mapped memory",
+ .ev_unreference = mappedfile_unreference,
+ .ev_pagefault = mappedfile_pagefault,
+ .ev_sanitycheck = mappedfile_sanitycheck,
+ .ev_copy = mappedfile_copy,
+ .writable = mappedfile_writable,
+ .ev_split = mappedfile_split,
+ .ev_lowshrink = mappedfile_lowshrink,
+ .ev_delete = mappedfile_delete,
+};
+
+static int mappedfile_unreference(struct phys_region *pr)
+{
+ assert(pr->ph->refcount == 0);
+ if(pr->ph->phys != MAP_NONE)
+ free_mem(ABS2CLICK(pr->ph->phys), 1);
+ return OK;
+}
+
+static int cow_block(struct vmproc *vmp, struct vir_region *region,
+ struct phys_region *ph, u16_t clearend)
+{
+ int r;
+
+ if((r=mem_cow(region, ph, MAP_NONE, MAP_NONE)) != OK) {
+ printf("mappedfile_pagefault: COW failed\n");
+ return r;
+ }
+
+ /* After COW we are a normal piece of anonymous memory. */
+ ph->memtype = &mem_type_anon;
+
+ if(clearend) {
+ phys_bytes phaddr = ph->ph->phys, po = VM_PAGE_SIZE-clearend;
+ assert(clearend < VM_PAGE_SIZE);
+ phaddr += po;
+ if(sys_memset(NONE, 0, phaddr, clearend) != OK) {
+ panic("cow_block: clearend failed\n");
+ }
+ }
+
+ return OK;
+}
+
+static int mappedfile_pagefault(struct vmproc *vmp, struct vir_region *region,
+ struct phys_region *ph, int write, vfs_callback_t cb,
+ void *state, int statelen)
+{
+ u32_t allocflags;
+ int procfd = region->param.file.fdref->fd;
+
+ allocflags = vrallocflags(region->flags);
+
+ assert(ph->ph->refcount > 0);
+ assert(region->param.file.inited);
+ assert(region->param.file.fdref);
+ assert(region->param.file.fdref->dev != NO_DEV);
+
+ /* Totally new block? Create it. */
+ if(ph->ph->phys == MAP_NONE) {
+ struct cached_page *cp;
+ u64_t referenced_offset =
+ region->param.file.offset + ph->offset;
+ if(region->param.file.fdref->ino == VMC_NO_INODE) {
+ cp = find_cached_page_bydev(region->param.file.fdref->dev,
+ referenced_offset, VMC_NO_INODE, 0, 1);
+ } else {
+ cp = find_cached_page_byino(region->param.file.fdref->dev,
+ region->param.file.fdref->ino, referenced_offset, 1);
+ }
+ if(cp) {
+ int result = OK;
+ pb_unreferenced(region, ph, 0);
+ pb_link(ph, cp->page, ph->offset, region);
+
+ if(roundup(ph->offset+region->param.file.clearend,
+ VM_PAGE_SIZE) >= region->length) {
+ result = cow_block(vmp, region, ph,
+ region->param.file.clearend);
+ } else if(result == OK && write) {
+ result = cow_block(vmp, region, ph, 0);
+ }
+
+ return result;
+ }
+
+ if(!cb) {
+ printf("VM: mem_file: no callback, returning EFAULT\n");
+ sys_sysctl_stacktrace(vmp->vm_endpoint);
+ return EFAULT;
+ }
+
+ if(vfs_request(VMVFSREQ_FDIO, procfd, vmp, referenced_offset,
+ VM_PAGE_SIZE, cb, NULL, state, statelen) != OK) {
+ printf("VM: mappedfile_pagefault: vfs_request failed\n");
+ return ENOMEM;
+ }
+
+ return SUSPEND;
+ }
+
+ if(!write) {
+ printf("mappedfile_pagefault: nonwrite fault?\n");
+ return EFAULT;
+ }
+
+ return cow_block(vmp, region, ph, 0);
+}
+
+static int mappedfile_sanitycheck(struct phys_region *pr, char *file, int line)
+{
+ MYASSERT(usedpages_add(pr->ph->phys, VM_PAGE_SIZE) == OK);
+ return OK;
+}
+
+static int mappedfile_writable(struct phys_region *pr)
+{
+ /* We are never writable. */
+ return 0;
+}
+
+int mappedfile_copy(struct vir_region *vr, struct vir_region *newvr)
+{
+ assert(vr->param.file.inited);
+ mappedfile_setfile(newvr->parent, newvr, vr->param.file.fdref->fd,
+ vr->param.file.offset,
+ vr->param.file.fdref->dev, vr->param.file.fdref->ino,
+ vr->param.file.clearend, 0, 0);
+ assert(newvr->param.file.inited);
+
+ return OK;
+}
+
+int mappedfile_setfile(struct vmproc *owner,
+ struct vir_region *region, int fd, u64_t offset,
+ dev_t dev, ino_t ino, u16_t clearend, int prefill, int mayclosefd)
+{
+ vir_bytes vaddr;
+ struct fdref *newref;
+
+ newref = fdref_dedup_or_new(owner, ino, dev, fd, mayclosefd);
+
+ assert(newref);
+ assert(!region->param.file.inited);
+ assert(dev != NO_DEV);
+ fdref_ref(newref, region);
+ region->param.file.offset = offset;
+ region->param.file.clearend = clearend;
+ region->param.file.inited = 1;
+
+ if(!prefill) return OK;
+
+ for(vaddr = 0; vaddr < region->length; vaddr+=VM_PAGE_SIZE) {
+ struct cached_page *cp = NULL;
+ struct phys_region *pr;
+ u64_t referenced_offset = offset + vaddr;
+
+ if(roundup(vaddr+region->param.file.clearend,
+ VM_PAGE_SIZE) >= region->length) {
+ break;
+ }
+
+ if(ino == VMC_NO_INODE) {
+ cp = find_cached_page_bydev(dev, referenced_offset,
+ VMC_NO_INODE, 0, 1);
+ } else {
+ cp = find_cached_page_byino(dev, ino,
+ referenced_offset, 1);
+ }
+ if(!cp) continue;
+ if(!(pr = pb_reference(cp->page, vaddr, region,
+ &mem_type_mappedfile))) {
+ printf("mappedfile_setfile: pb_reference failed\n");
+ break;
+ }
+ if(map_ph_writept(region->parent, region, pr) != OK) {
+ printf("mappedfile_setfile: map_ph_writept failed\n");
+ break;
+ }
+ }
+
+ return OK;
+}
+
+static void mappedfile_split(struct vmproc *vmp, struct vir_region *vr,
+ struct vir_region *r1, struct vir_region *r2)
+{
+ assert(!r1->param.file.inited);
+ assert(!r2->param.file.inited);
+ assert(vr->param.file.inited);
+ assert(r1->length + r2->length == vr->length);
+ assert(vr->def_memtype == &mem_type_mappedfile);
+ assert(r1->def_memtype == &mem_type_mappedfile);
+ assert(r2->def_memtype == &mem_type_mappedfile);
+
+ r1->param.file = vr->param.file;
+ r2->param.file = vr->param.file;
+
+ fdref_ref(vr->param.file.fdref, r1);
+ fdref_ref(vr->param.file.fdref, r2);
+
+ r1->param.file.clearend = 0;
+ r2->param.file.offset += r1->length;
+
+ assert(r1->param.file.inited);
+ assert(r2->param.file.inited);
+}
+
+static int mappedfile_lowshrink(struct vir_region *vr, vir_bytes len)
+{
+ assert(vr->param.file.inited);
+ vr->param.file.offset += len;
+ return OK;
+}
+
+static void mappedfile_delete(struct vir_region *region)
+{
+ assert(region->def_memtype == &mem_type_mappedfile);
+ assert(region->param.file.inited);
+ assert(region->param.file.fdref);
+ fdref_deref(region);
+ region->param.file.inited = 0;
+}
return vr;
}
+static int mmap_file(struct vmproc *vmp,
+ int vmfd, u32_t off_lo, u32_t off_hi, int flags,
+ ino_t ino, dev_t dev, u64_t filesize, vir_bytes addr, vir_bytes len,
+ vir_bytes *retaddr, u16_t clearend, int writable, int mayclosefd)
+{
+/* VFS has replied to a VMVFSREQ_FDLOOKUP request. */
+ struct vir_region *vr;
+ u64_t file_offset, page_offset;
+ int result = OK;
+ u32_t vrflags = 0;
+
+ if(writable) vrflags |= VR_WRITABLE;
+
+ if(flags & MAP_THIRDPARTY) {
+ file_offset = off_lo;
+ } else {
+ file_offset = make64(off_lo, off_hi);
+ if(off_hi && !off_lo) {
+ /* XXX clang compatability hack */
+ off_hi = file_offset = 0;
+ }
+ }
+
+ /* Do some page alignments. */
+ if((page_offset = (file_offset % VM_PAGE_SIZE))) {
+ file_offset -= page_offset;
+ len += page_offset;
+ }
+
+ len = roundup(len, VM_PAGE_SIZE);
+
+ /* All numbers should be page-aligned now. */
+ assert(!(len % VM_PAGE_SIZE));
+ assert(!(filesize % VM_PAGE_SIZE));
+ assert(!(file_offset % VM_PAGE_SIZE));
+
+#if 0
+ /* XXX ld.so relies on longer-than-file mapping */
+ if((u64_t) len + file_offset > filesize) {
+ printf("VM: truncating mmap dev 0x%x ino %d beyond file size in %d; offset %llu, len %lu, size %llu; ",
+ dev, ino, vmp->vm_endpoint,
+ file_offset, len, filesize);
+ len = filesize - file_offset;
+ return EINVAL;
+ }
+#endif
+
+ if(!(vr = mmap_region(vmp, addr, flags, len,
+ vrflags, &mem_type_mappedfile, 0))) {
+ result = ENOMEM;
+ } else {
+ *retaddr = vr->vaddr + page_offset;
+ result = OK;
+
+ mappedfile_setfile(vmp, vr, vmfd,
+ file_offset, dev, ino, clearend, 1, mayclosefd);
+ }
+
+ return result;
+}
+
+int do_vfs_mmap(message *m)
+{
+ vir_bytes v;
+ struct vmproc *vmp;
+ int r, n;
+ u16_t clearend, flags = 0;
+
+ /* It might be disabled */
+ if(!enable_filemap) return ENXIO;
+
+ clearend = (m->m_u.m_vm_vfs.clearend_and_flags & MVM_LENMASK);
+ flags = (m->m_u.m_vm_vfs.clearend_and_flags & MVM_FLAGSMASK);
+
+ if((r=vm_isokendpt(m->m_u.m_vm_vfs.who, &n)) != OK)
+ panic("bad ep %d from vfs", m->m_u.m_vm_vfs.who);
+ vmp = &vmproc[n];
+
+ return mmap_file(vmp, m->m_u.m_vm_vfs.fd, m->m_u.m_vm_vfs.offset, 0,
+ MAP_PRIVATE | MAP_FIXED,
+ m->m_u.m_vm_vfs.ino, m->m_u.m_vm_vfs.dev,
+ (u64_t) LONG_MAX * VM_PAGE_SIZE,
+ m->m_u.m_vm_vfs.vaddr, m->m_u.m_vm_vfs.len, &v,
+ clearend, flags, 0);
+}
+
+static void mmap_file_cont(struct vmproc *vmp, message *replymsg, void *cbarg,
+ void *origmsg_v)
+{
+ message *origmsg = (message *) origmsg_v;
+ message mmap_reply;
+ int result;
+ int writable = 0;
+ vir_bytes v = (vir_bytes) MAP_FAILED;
+
+ if(origmsg->VMM_PROT & PROT_WRITE)
+ writable = 1;
+
+ if(replymsg->VMV_RESULT != OK) {
+ printf("VM: VFS reply failed (%d)\n", replymsg->VMV_RESULT);
+ sys_sysctl_stacktrace(vmp->vm_endpoint);
+ result = origmsg->VMV_RESULT;
+ } else {
+ /* Finish mmap */
+ result = mmap_file(vmp, replymsg->VMV_FD, origmsg->VMM_OFFSET_LO,
+ origmsg->VMM_OFFSET_HI, origmsg->VMM_FLAGS,
+ replymsg->VMV_INO, replymsg->VMV_DEV,
+ (u64_t) replymsg->VMV_SIZE_PAGES*PAGE_SIZE,
+ origmsg->VMM_ADDR,
+ origmsg->VMM_LEN, &v, 0, writable, 1);
+ }
+
+ /* Unblock requesting process. */
+ memset(&mmap_reply, 0, sizeof(mmap_reply));
+ mmap_reply.m_type = result;
+ mmap_reply.VMM_ADDR = v;
+
+ if(send(vmp->vm_endpoint, &mmap_reply) != OK)
+ panic("VM: mmap_file_cont: send() failed");
+}
+
/*===========================================================================*
* do_mmap *
*===========================================================================*/
vmp = &vmproc[n];
+ /* "SUSv3 specifies that mmap() should fail if length is 0" */
+ if(len <= 0) {
+ return EINVAL;
+ }
+
if(m->VMM_FD == -1 || (m->VMM_FLAGS & MAP_ANON)) {
/* actual memory in some form */
mem_type_t *mt = NULL;
- if(m->VMM_FD != -1 || len <= 0) {
+ if(m->VMM_FD != -1) {
printf("VM: mmap: fd %d, len 0x%x\n", m->VMM_FD, len);
return EINVAL;
}
return ENOMEM;
}
} else {
- return ENXIO;
+ /* File mapping might be disabled */
+ if(!enable_filemap) return ENXIO;
+
+ /* files get private copies of pages on writes. */
+ if(!(m->VMM_FLAGS & MAP_PRIVATE)) {
+ printf("VM: mmap file must MAP_PRIVATE\n");
+ return ENXIO;
+ }
+
+ if(vfs_request(VMVFSREQ_FDLOOKUP, m->VMM_FD, vmp, 0, 0,
+ mmap_file_cont, NULL, m, sizeof(*m)) != OK) {
+ printf("VM: vfs_request for mmap failed\n");
+ return ENXIO;
+ }
+
+ /* request queued; don't reply. */
+ return SUSPEND;
}
/* Return mapping, as seen from process. */
int do_vfs_reply(message *m);
/* mem_file.c */
-void mappedfile_setfile(struct vir_region *region, int fd, u64_t offset,
- dev_t dev, ino_t ino, u16_t clearend, int prefill);
+int mappedfile_setfile(struct vmproc *owner, struct vir_region *region,
+ int fd, u64_t offset,
+ dev_t dev, ino_t ino, u16_t clearend, int prefill, int mayclose);
+
+/* fdref.c */
+struct fdref *fdref_new(struct vmproc *owner, ino_t ino, dev_t dev, int fd);
+struct fdref *fdref_dedup_or_new(struct vmproc *owner, ino_t ino, dev_t dev,
+ int fd, int mayclose);
+void fdref_ref(struct fdref *ref, struct vir_region *region);
+void fdref_deref(struct vir_region *region);
+void fdref_sanitycheck(void);
if(!(newvr = region_new(vr->parent, vr->vaddr, vr->length, vr->flags, vr->def_memtype)))
return NULL;
+ USE(newvr, newvr->parent = vmp;);
+
if(vr->def_memtype->ev_copy && (r=vr->def_memtype->ev_copy(vr, newvr)) != OK) {
map_free(newvr);
printf("VM: memtype-specific copy failed (%d)\n", r);
map_free_proc(dst);
return ENOMEM;
}
- USE(newvr, newvr->parent = dst;);
region_insert(&dst->vm_regions_avl, newvr);
assert(vr->length == newvr->length);
#include "phys_region.h"
#include "memtype.h"
#include "vm.h"
+#include "fdref.h"
struct phys_block {
#if SANITYCHECKS
} shared;
struct phys_block *pb_cache;
struct {
- int procfd; /* cloned fd in proc for mmap */
- dev_t dev;
- ino_t ino;
- u64_t offset;
int inited;
+ struct fdref *fdref;
+ u64_t offset;
u16_t clearend;
} file;
} param;
--- /dev/null
+
+/* Sending requests to VFS and handling the replies. */
+
+#define _SYSTEM 1
+
+#include <minix/callnr.h>
+#include <minix/com.h>
+#include <minix/config.h>
+#include <minix/const.h>
+#include <minix/ds.h>
+#include <minix/endpoint.h>
+#include <minix/minlib.h>
+#include <minix/type.h>
+#include <minix/ipc.h>
+#include <minix/sysutil.h>
+#include <minix/syslib.h>
+#include <minix/type.h>
+#include <minix/bitmap.h>
+#include <string.h>
+#include <errno.h>
+#include <env.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/param.h>
+
+#include "proto.h"
+#include "glo.h"
+#include "util.h"
+#include "region.h"
+#include "sanitycheck.h"
+
+#define STATELEN 50
+
+static struct vfs_request_node {
+ message reqmsg;
+ char reqstate[STATELEN];
+ void *opaque;
+ endpoint_t who;
+ u32_t req_id;
+ vfs_callback_t callback;
+ struct vfs_request_node *next;
+} *first_queued, *active;
+
+static void activate(void)
+{
+ assert(!active);
+ assert(first_queued);
+
+ active = first_queued;
+ first_queued = first_queued->next;
+
+ if(asynsend3(VFS_PROC_NR, &active->reqmsg, AMF_NOREPLY) != OK)
+ panic("VM: asynsend to VFS failed");
+}
+
+/*===========================================================================*
+ * vfs_request *
+ *===========================================================================*/
+int vfs_request(int reqno, int fd, struct vmproc *vmp, u64_t offset, u32_t len,
+ vfs_callback_t reply_callback, void *cbarg, void *state, int statelen)
+{
+/* Perform an asynchronous request to VFS.
+ * We send a message of type VFS_VMCALL to VFS. VFS will respond
+ * with message type VM_VFS_REPLY. We send the request asynchronously
+ * and then handle the reply as it if were a VM_VFS_REPLY request.
+ */
+ message *m;
+ static u32_t reqid = 0;
+ struct vfs_request_node *reqnode;
+
+ reqid++;
+
+ assert(statelen <= STATELEN);
+
+ if(!SLABALLOC(reqnode)) {
+ printf("vfs_request: no memory for request node\n");
+ return ENOMEM;
+ }
+
+ m = &reqnode->reqmsg;
+ m->m_type = VFS_VMCALL;
+ m->VFS_VMCALL_REQ = reqno;
+ m->VFS_VMCALL_FD = fd;
+ m->VFS_VMCALL_REQID = reqid;
+ m->VFS_VMCALL_ENDPOINT = vmp->vm_endpoint;
+ m->VFS_VMCALL_OFFSET_LO = ex64lo(offset);
+ m->VFS_VMCALL_OFFSET_HI = ex64hi(offset);
+ m->VFS_VMCALL_LENGTH = len;
+
+ reqnode->who = vmp->vm_endpoint;
+ reqnode->req_id = reqid;
+ reqnode->next = first_queued;
+ reqnode->callback = reply_callback;
+ reqnode->opaque = cbarg;
+ if(state) memcpy(reqnode->reqstate, state, statelen);
+ first_queued = reqnode;
+
+ /* Send the request message if none pending. */
+ if(!active)
+ activate();
+
+ return OK;
+}
+
+/*===========================================================================*
+ * do_vfs_reply *
+ *===========================================================================*/
+int do_vfs_reply(message *m)
+{
+/* VFS has handled a VM request and VFS has replied. It must be the
+ * active request.
+ */
+ struct vfs_request_node *orignode = active;
+ vfs_callback_t req_callback;
+ void *cbarg;
+ int n;
+ struct vmproc *vmp;
+ if(m->m_source != VFS_PROC_NR)
+ return ENOSYS;
+
+ assert(active);
+ assert(active->req_id == m->VMV_REQID);
+
+ /* the endpoint may have exited */
+ if(vm_isokendpt(m->VMV_ENDPOINT, &n) != OK)
+ vmp = NULL;
+ else vmp = &vmproc[n];
+
+ req_callback = active->callback;
+ cbarg = active->opaque;
+ active = NULL;
+
+ /* Invoke requested reply-callback within VM. */
+ if(req_callback) req_callback(vmp, m, cbarg, orignode->reqstate);
+
+ SLABFREE(orignode);
+
+ /* Send the next request message if any. */
+ if(first_queued)
+ activate();
+
+ return SUSPEND; /* don't reply to the reply */
+}
+
# Cache testing programs
OBJS.test71+= testcache.o
OBJS.test72+= testcache.o
+OBJS.test74+= testcache.o
LDADD.test72+= -lminixfs
PROGS += testvm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
41 42 43 44 45 46 48 49 50 52 53 54 55 56 58 59 60 \
-61 64 65 66 67 68 69 70 71 72 73
+61 64 65 66 67 68 69 70 71 72 73 74
.if ${MACHINE_ARCH} == "i386"
MINIX_TESTS+= \
#include "common.h"
int common_test_nr = -1, errct = 0, subtest;
-
-int quietflag = 1;
+int quietflag = 1, bigflag = 0;
/* provide a default max_error symbol as Max_error with a value
* of 5. The test program can override it wit its own max_error
char buf[64];
int i;
+ /* if this variable is set, specify to tests we are running
+ * in 'overnight' mode
+ */
+ bigflag = !!getenv(BIGVARNAME);
+
common_test_nr = test_nr;
printf("Test %2d ", test_nr);
fflush(stdout); /* since stdout is probably line buffered */
sh1.sh sh2.sh interp.sh"
tests_no=`expr 0`
+# test mmap only if enabled in sysenv
+if sysenv filemap >/dev/null
+then alltests="$alltests 74"
+fi
+
# If root, make sure the setuid tests have the correct permissions
# and make the dir bin-owned.
if [ "$ROOT" ]
#include "common.h"
#include "testcache.h"
-/* we want to flexibly split this test over multiple files
- * - for big working sets we might run over the 2GB MFS file limit
- * - we might want to test the FS being able to handle lots of
- * files / unusual metadata situations
- */
-#define MBPERFILE 100
-#define MB (1024*1024)
-#define MAXFILES ((u64_t) MAXBLOCKS * MAXBLOCKSIZE / MB / MBPERFILE + 1)
-
-static int fds[MAXFILES];
-
-static void
-get_fd_offset(int b, int blocksize, u64_t *file_offset, int *fd)
-{
- u64_t offset = (u64_t) b * blocksize;
- int filenumber;
-
- filenumber = offset / MB / MBPERFILE;
-
- assert(filenumber >= 0 && filenumber < MAXFILES);
- assert(fds[filenumber] > 0);
-
- *fd = fds[filenumber];
- *file_offset = offset - (filenumber * MBPERFILE * MB);
-}
-
int
dowriteblock(int b, int blocksize, u32_t seed, char *data)
{
int
main(int argc, char *argv[])
{
- int f, big = !!getenv(BIGVARNAME), iter = 2;
+ int iter = 2;
start(71);
- cachequiet(!big);
- if(big) iter = 3;
+ cachequiet(!bigflag);
+ if(bigflag) iter = 3;
- for(f = 0; f < MAXFILES; f++) {
- char tempfilename[] = "cachetest.XXXXXXXX";
- fds[f] = mkstemp(tempfilename);
- if(fds[f] < 0) { perror("mkstemp"); e(20); return 1; }
- assert(fds[f] > 0);
- }
+ makefiles(MAXFILES);
/* Try various combinations working set sizes
* and block sizes in order to specifically
if(dotest(PAGE_SIZE*3, 100, iter)) e(3);
if(dotest(PAGE_SIZE, 20000, iter)) e(5);
- if(big) {
+ if(bigflag) {
u32_t totalmem, freemem, cachedmem;
if(dotest(PAGE_SIZE, 150000, iter)) e(5);
getmem(&totalmem, &freemem, &cachedmem);
if(dotest(PAGE_SIZE, totalmem*1.5, iter)) e(6);
}
- for(f = 0; f < MAXFILES; f++) {
- assert(fds[f] > 0);
- close(fds[f]);
- }
-
quit();
return 0;
--- /dev/null
+/* Test 74 - mmap functionality test.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioc_memory.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "common.h"
+#include "testcache.h"
+
+int
+dowriteblock(int b, int blocksize, u32_t seed, char *data)
+{
+ u64_t offset;
+ int fd;
+
+ get_fd_offset(b, blocksize, &offset, &fd);
+
+ if(pwrite(fd, data, blocksize, offset) < blocksize) {
+ perror("pwrite");
+ return -1;
+ }
+
+ return blocksize;
+}
+
+int
+readblock(int b, int blocksize, u32_t seed, char *data)
+{
+ u64_t offset;
+ int fd;
+ char *mmapdata;
+ int pread_first = random() % 2;
+
+ get_fd_offset(b, blocksize, &offset, &fd);
+
+ if(pread_first) {
+ if(pread(fd, data, blocksize, offset) < blocksize) {
+ perror("pread");
+ return -1;
+ }
+ }
+
+ if((mmapdata = minix_mmap(NULL, blocksize, PROT_READ, MAP_PRIVATE | MAP_FILE,
+ fd, offset)) == MAP_FAILED) {
+ perror("mmap");
+ return -1;
+ }
+
+ if(!pread_first) {
+ if(pread(fd, data, blocksize, offset) < blocksize) {
+ perror("pread");
+ return -1;
+ }
+ }
+
+ if(memcmp(mmapdata, data, blocksize)) {
+ fprintf(stderr, "readblock: mmap, pread mismatch\n");
+ return -1;
+ }
+
+ if(minix_munmap(mmapdata, blocksize) < 0) {
+ perror("munmap");
+ return -1;
+ }
+
+ return blocksize;
+}
+
+void testend(void) { }
+
+int
+main(int argc, char *argv[])
+{
+ int iter = 2;
+
+ start(74);
+
+ makefiles(MAXFILES);
+
+ cachequiet(!bigflag);
+ if(bigflag) iter = 3;
+
+ /* Try various combinations working set sizes
+ * and block sizes in order to specifically
+ * target the primary cache, then primary+secondary
+ * cache, then primary+secondary cache+secondary
+ * cache eviction.
+ */
+
+ if(dotest(PAGE_SIZE, 100, iter)) e(5);
+ if(dotest(PAGE_SIZE*2, 100, iter)) e(2);
+ if(dotest(PAGE_SIZE*3, 100, iter)) e(3);
+ if(dotest(PAGE_SIZE, 20000, iter)) e(5);
+
+ if(bigflag) {
+ u32_t totalmem, freemem, cachedmem;
+ if(dotest(PAGE_SIZE, 150000, iter)) e(5);
+ getmem(&totalmem, &freemem, &cachedmem);
+ if(dotest(PAGE_SIZE, totalmem*1.5, iter)) e(6);
+ }
+
+ quit();
+
+ return 0;
+}
+
extern int quietflag;
+int fds[MAXFILES];
+
static void
genblock(int b, char *blockdata, int blocksize, u32_t seed)
{
return 0;
}
+void
+get_fd_offset(int b, int blocksize, u64_t *file_offset, int *fd)
+{
+ u64_t offset = (u64_t) b * blocksize;
+ int filenumber;
+
+ filenumber = offset / MB / MBPERFILE;
+
+ assert(filenumber >= 0 && filenumber < MAXFILES);
+ assert(fds[filenumber] > 0);
+
+ *fd = fds[filenumber];
+ *file_offset = offset - (filenumber * MBPERFILE * MB);
+}
+
+void
+makefiles(int n)
+{
+ int f;
+ for(f = 0; f < n; f++) {
+ char tempfilename[] = "cachetest.XXXXXXXX";
+ fds[f] = mkstemp(tempfilename);
+ if(fds[f] < 0) {
+ perror("mkstemp");
+ fprintf(stderr, "mkstemp %d/%d failed\n", f, n);
+ exit(1);
+ }
+ assert(fds[f] > 0);
+ }
+}
+
void cachequiet(int quiet)
{
quietflag = quiet;
void testend(void);
int dotest(int blocksize, int nblocks, int iterations);
void cachequiet(int quiet);
+void get_fd_offset(int b, int blocksize, u64_t *file_offset, int *fd);
+void makefiles(int n);
#define OK_BLOCK_GONE -999
+
+/* for file-oriented tests:
+ *
+ * we want to flexibly split tests over multiple files
+ * - for big working sets we might run over the 2GB MFS file limit
+ * - we might want to test the FS being able to handle lots of
+ * files / unusual metadata situations
+ */
+#define MBPERFILE 2000
+#define MB (1024*1024)
+#define MAXFILES ((u64_t) MAXBLOCKS * MAXBLOCKSIZE / MB / MBPERFILE + 1)
+
+extern int fds[MAXFILES], bigflag;
+