vm: mmap support
. test74 for mmap functionality . vm: add a mem_file memory type that specifies an mmap()ped memory range, backed by a file . add fdref, an object that keeps track of FD references within VM per process and so knows how to de-duplicate the use of FD's by various mmap()ped ranges; there can be many more than there can be FD's . turned off for now, enable with 'filemap=1' as boot option Change-Id: I640b1126cdaa522a0560301cf6732b7661555672
This commit is contained in:
parent
f369157d95
commit
49b9165251
22 changed files with 953 additions and 59 deletions
|
@ -4643,6 +4643,7 @@
|
||||||
./usr/tests/minix-posix/test71 minix-sys
|
./usr/tests/minix-posix/test71 minix-sys
|
||||||
./usr/tests/minix-posix/test72 minix-sys
|
./usr/tests/minix-posix/test72 minix-sys
|
||||||
./usr/tests/minix-posix/test73 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/test7 minix-sys
|
||||||
./usr/tests/minix-posix/test8 minix-sys
|
./usr/tests/minix-posix/test8 minix-sys
|
||||||
./usr/tests/minix-posix/test9 minix-sys
|
./usr/tests/minix-posix/test9 minix-sys
|
||||||
|
|
|
@ -4,5 +4,6 @@ default=2
|
||||||
menu=Start MINIX 3:load_mods /boot/minix_default/mod*;multiboot /boot/minix_default/kernel rootdevname=$rootdevname $args
|
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: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 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=Edit menu option:edit
|
||||||
menu=Drop to boot prompt:prompt
|
menu=Drop to boot prompt:prompt
|
||||||
|
|
|
@ -6,7 +6,7 @@ SRCS= main.c alloc.c utility.c exit.c fork.c break.c \
|
||||||
mmap.c slaballoc.c region.c pagefaults.c \
|
mmap.c slaballoc.c region.c pagefaults.c \
|
||||||
rs.c queryexit.c pb.c regionavl.c \
|
rs.c queryexit.c pb.c regionavl.c \
|
||||||
mem_anon.c mem_directphys.c mem_anon_contig.c mem_shared.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"
|
.if ${MACHINE_ARCH} == "earm"
|
||||||
LDFLAGS+= -T ${.CURDIR}/arch/${MACHINE_ARCH}/vm.lds
|
LDFLAGS+= -T ${.CURDIR}/arch/${MACHINE_ARCH}/vm.lds
|
||||||
|
|
|
@ -1330,10 +1330,6 @@ int pt_bind(pt_t *pt, struct vmproc *who)
|
||||||
pdeslot * ARCH_PAGEDIR_SIZE);
|
pdeslot * ARCH_PAGEDIR_SIZE);
|
||||||
#endif
|
#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. */
|
/* Tell kernel about new page table root. */
|
||||||
return sys_vmctl_set_addrspace(who->vm_endpoint, pt->pt_dir_phys, pdes);
|
return sys_vmctl_set_addrspace(who->vm_endpoint, pt->pt_dir_phys, pdes);
|
||||||
}
|
}
|
||||||
|
|
177
servers/vm/fdref.c
Normal file
177
servers/vm/fdref.c
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
|
29
servers/vm/fdref.h
Normal file
29
servers/vm/fdref.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
EXTERN struct vmproc vmproc[VMP_NR];
|
EXTERN struct vmproc vmproc[VMP_NR];
|
||||||
|
|
||||||
|
long enable_filemap;
|
||||||
|
|
||||||
EXTERN kinfo_t kernel_boot_info;
|
EXTERN kinfo_t kernel_boot_info;
|
||||||
|
|
||||||
#if SANITYCHECKS
|
#if SANITYCHECKS
|
||||||
|
|
|
@ -323,6 +323,9 @@ void init_vm(void)
|
||||||
panic("couldn't get bootinfo: %d", s);
|
panic("couldn't get bootinfo: %d", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Turn file mmap on? */
|
||||||
|
env_parse("filemap", "d", 0, &enable_filemap, 0, 1);
|
||||||
|
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
assert(kernel_boot_info.mmap_size > 0);
|
assert(kernel_boot_info.mmap_size > 0);
|
||||||
assert(kernel_boot_info.mods_with_kernel > 0);
|
assert(kernel_boot_info.mods_with_kernel > 0);
|
||||||
|
@ -414,6 +417,10 @@ void init_vm(void)
|
||||||
CALLMAP(VM_WILLEXIT, do_willexit);
|
CALLMAP(VM_WILLEXIT, do_willexit);
|
||||||
CALLMAP(VM_NOTIFY_SIG, do_notify_sig);
|
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 */
|
/* Calls from RS */
|
||||||
CALLMAP(VM_RS_SET_PRIV, do_rs_set_priv);
|
CALLMAP(VM_RS_SET_PRIV, do_rs_set_priv);
|
||||||
CALLMAP(VM_RS_UPDATE, do_rs_update);
|
CALLMAP(VM_RS_UPDATE, do_rs_update);
|
||||||
|
|
|
@ -118,7 +118,6 @@ do_mapcache(message *msg)
|
||||||
printf("VM: map_pf failed\n");
|
printf("VM: map_pf failed\n");
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!vr->param.pb_cache);
|
assert(!vr->param.pb_cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
251
servers/vm/mem_file.c
Normal file
251
servers/vm/mem_file.c
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
|
@ -81,6 +81,127 @@ static struct vir_region *mmap_region(struct vmproc *vmp, vir_bytes addr,
|
||||||
return vr;
|
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 *
|
* do_mmap *
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
|
@ -111,11 +232,16 @@ int do_mmap(message *m)
|
||||||
|
|
||||||
vmp = &vmproc[n];
|
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)) {
|
if(m->VMM_FD == -1 || (m->VMM_FLAGS & MAP_ANON)) {
|
||||||
/* actual memory in some form */
|
/* actual memory in some form */
|
||||||
mem_type_t *mt = NULL;
|
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);
|
printf("VM: mmap: fd %d, len 0x%x\n", m->VMM_FD, len);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -134,7 +260,23 @@ int do_mmap(message *m)
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
}
|
}
|
||||||
} else {
|
} 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. */
|
/* Return mapping, as seen from process. */
|
||||||
|
|
|
@ -228,5 +228,14 @@ int vfs_request(int reqno, int fd, struct vmproc *vmp, u64_t offset,
|
||||||
int do_vfs_reply(message *m);
|
int do_vfs_reply(message *m);
|
||||||
|
|
||||||
/* mem_file.c */
|
/* mem_file.c */
|
||||||
void mappedfile_setfile(struct vir_region *region, int fd, u64_t offset,
|
int mappedfile_setfile(struct vmproc *owner, struct vir_region *region,
|
||||||
dev_t dev, ino_t ino, u16_t clearend, int prefill);
|
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);
|
||||||
|
|
|
@ -833,6 +833,8 @@ struct vir_region *map_copy_region(struct vmproc *vmp, struct vir_region *vr)
|
||||||
if(!(newvr = region_new(vr->parent, vr->vaddr, vr->length, vr->flags, vr->def_memtype)))
|
if(!(newvr = region_new(vr->parent, vr->vaddr, vr->length, vr->flags, vr->def_memtype)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
USE(newvr, newvr->parent = vmp;);
|
||||||
|
|
||||||
if(vr->def_memtype->ev_copy && (r=vr->def_memtype->ev_copy(vr, newvr)) != OK) {
|
if(vr->def_memtype->ev_copy && (r=vr->def_memtype->ev_copy(vr, newvr)) != OK) {
|
||||||
map_free(newvr);
|
map_free(newvr);
|
||||||
printf("VM: memtype-specific copy failed (%d)\n", r);
|
printf("VM: memtype-specific copy failed (%d)\n", r);
|
||||||
|
@ -980,7 +982,6 @@ struct vir_region *start_src_vr;
|
||||||
map_free_proc(dst);
|
map_free_proc(dst);
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
}
|
}
|
||||||
USE(newvr, newvr->parent = dst;);
|
|
||||||
region_insert(&dst->vm_regions_avl, newvr);
|
region_insert(&dst->vm_regions_avl, newvr);
|
||||||
assert(vr->length == newvr->length);
|
assert(vr->length == newvr->length);
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "phys_region.h"
|
#include "phys_region.h"
|
||||||
#include "memtype.h"
|
#include "memtype.h"
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
#include "fdref.h"
|
||||||
|
|
||||||
struct phys_block {
|
struct phys_block {
|
||||||
#if SANITYCHECKS
|
#if SANITYCHECKS
|
||||||
|
@ -53,11 +54,9 @@ typedef struct vir_region {
|
||||||
} shared;
|
} shared;
|
||||||
struct phys_block *pb_cache;
|
struct phys_block *pb_cache;
|
||||||
struct {
|
struct {
|
||||||
int procfd; /* cloned fd in proc for mmap */
|
|
||||||
dev_t dev;
|
|
||||||
ino_t ino;
|
|
||||||
u64_t offset;
|
|
||||||
int inited;
|
int inited;
|
||||||
|
struct fdref *fdref;
|
||||||
|
u64_t offset;
|
||||||
u16_t clearend;
|
u16_t clearend;
|
||||||
} file;
|
} file;
|
||||||
} param;
|
} param;
|
||||||
|
|
144
servers/vm/vfs.c
Normal file
144
servers/vm/vfs.c
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ OBJS.test57= test57loop.o
|
||||||
# Cache testing programs
|
# Cache testing programs
|
||||||
OBJS.test71+= testcache.o
|
OBJS.test71+= testcache.o
|
||||||
OBJS.test72+= testcache.o
|
OBJS.test72+= testcache.o
|
||||||
|
OBJS.test74+= testcache.o
|
||||||
LDADD.test72+= -lminixfs
|
LDADD.test72+= -lminixfs
|
||||||
|
|
||||||
PROGS += testvm
|
PROGS += testvm
|
||||||
|
@ -47,7 +48,7 @@ MINIX_TESTS= \
|
||||||
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
|
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 \
|
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 \
|
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"
|
.if ${MACHINE_ARCH} == "i386"
|
||||||
MINIX_TESTS+= \
|
MINIX_TESTS+= \
|
||||||
|
|
|
@ -13,8 +13,7 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
int common_test_nr = -1, errct = 0, subtest;
|
int common_test_nr = -1, errct = 0, subtest;
|
||||||
|
int quietflag = 1, bigflag = 0;
|
||||||
int quietflag = 1;
|
|
||||||
|
|
||||||
/* provide a default max_error symbol as Max_error with a value
|
/* 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
|
* of 5. The test program can override it wit its own max_error
|
||||||
|
@ -30,6 +29,11 @@ int test_nr;
|
||||||
char buf[64];
|
char buf[64];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/* if this variable is set, specify to tests we are running
|
||||||
|
* in 'overnight' mode
|
||||||
|
*/
|
||||||
|
bigflag = !!getenv(BIGVARNAME);
|
||||||
|
|
||||||
common_test_nr = test_nr;
|
common_test_nr = test_nr;
|
||||||
printf("Test %2d ", test_nr);
|
printf("Test %2d ", test_nr);
|
||||||
fflush(stdout); /* since stdout is probably line buffered */
|
fflush(stdout); /* since stdout is probably line buffered */
|
||||||
|
|
5
test/run
5
test/run
|
@ -28,6 +28,11 @@ alltests=" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
|
||||||
sh1.sh sh2.sh interp.sh"
|
sh1.sh sh2.sh interp.sh"
|
||||||
tests_no=`expr 0`
|
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
|
# If root, make sure the setuid tests have the correct permissions
|
||||||
# and make the dir bin-owned.
|
# and make the dir bin-owned.
|
||||||
if [ "$ROOT" ]
|
if [ "$ROOT" ]
|
||||||
|
|
|
@ -23,32 +23,6 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "testcache.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
|
int
|
||||||
dowriteblock(int b, int blocksize, u32_t seed, char *data)
|
dowriteblock(int b, int blocksize, u32_t seed, char *data)
|
||||||
{
|
{
|
||||||
|
@ -86,19 +60,14 @@ void testend(void) { }
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int f, big = !!getenv(BIGVARNAME), iter = 2;
|
int iter = 2;
|
||||||
|
|
||||||
start(71);
|
start(71);
|
||||||
|
|
||||||
cachequiet(!big);
|
cachequiet(!bigflag);
|
||||||
if(big) iter = 3;
|
if(bigflag) iter = 3;
|
||||||
|
|
||||||
for(f = 0; f < MAXFILES; f++) {
|
makefiles(MAXFILES);
|
||||||
char tempfilename[] = "cachetest.XXXXXXXX";
|
|
||||||
fds[f] = mkstemp(tempfilename);
|
|
||||||
if(fds[f] < 0) { perror("mkstemp"); e(20); return 1; }
|
|
||||||
assert(fds[f] > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try various combinations working set sizes
|
/* Try various combinations working set sizes
|
||||||
* and block sizes in order to specifically
|
* and block sizes in order to specifically
|
||||||
|
@ -112,18 +81,13 @@ main(int argc, char *argv[])
|
||||||
if(dotest(PAGE_SIZE*3, 100, iter)) e(3);
|
if(dotest(PAGE_SIZE*3, 100, iter)) e(3);
|
||||||
if(dotest(PAGE_SIZE, 20000, iter)) e(5);
|
if(dotest(PAGE_SIZE, 20000, iter)) e(5);
|
||||||
|
|
||||||
if(big) {
|
if(bigflag) {
|
||||||
u32_t totalmem, freemem, cachedmem;
|
u32_t totalmem, freemem, cachedmem;
|
||||||
if(dotest(PAGE_SIZE, 150000, iter)) e(5);
|
if(dotest(PAGE_SIZE, 150000, iter)) e(5);
|
||||||
getmem(&totalmem, &freemem, &cachedmem);
|
getmem(&totalmem, &freemem, &cachedmem);
|
||||||
if(dotest(PAGE_SIZE, totalmem*1.5, iter)) e(6);
|
if(dotest(PAGE_SIZE, totalmem*1.5, iter)) e(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(f = 0; f < MAXFILES; f++) {
|
|
||||||
assert(fds[f] > 0);
|
|
||||||
close(fds[f]);
|
|
||||||
}
|
|
||||||
|
|
||||||
quit();
|
quit();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
113
test/test74.c
Normal file
113
test/test74.c
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
|
|
||||||
extern int quietflag;
|
extern int quietflag;
|
||||||
|
|
||||||
|
int fds[MAXFILES];
|
||||||
|
|
||||||
static void
|
static void
|
||||||
genblock(int b, char *blockdata, int blocksize, u32_t seed)
|
genblock(int b, char *blockdata, int blocksize, u32_t seed)
|
||||||
{
|
{
|
||||||
|
@ -210,6 +212,37 @@ dotest(int blocksize, int nblocks, int iterations)
|
||||||
return 0;
|
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)
|
void cachequiet(int quiet)
|
||||||
{
|
{
|
||||||
quietflag = quiet;
|
quietflag = quiet;
|
||||||
|
|
|
@ -14,5 +14,21 @@ int readblock(int b, int blocksize, u32_t seed, char *block);
|
||||||
void testend(void);
|
void testend(void);
|
||||||
int dotest(int blocksize, int nblocks, int iterations);
|
int dotest(int blocksize, int nblocks, int iterations);
|
||||||
void cachequiet(int quiet);
|
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
|
#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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue