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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
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 *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);
}
|