178 lines
4.2 KiB
C
178 lines
4.2 KiB
C
|
|
||
|
/* 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 *nfdref;
|
||
|
|
||
|
if(!SLABALLOC(nfdref)) return NULL;
|
||
|
|
||
|
nfdref->fd = fd;
|
||
|
nfdref->refcount = 0;
|
||
|
nfdref->dev = dev;
|
||
|
nfdref->ino = ino;
|
||
|
nfdref->next = fdrefs;
|
||
|
fdrefs = nfdref;
|
||
|
|
||
|
return nfdref;
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|