2 /* File that implements the 'fdref' data structure. It keeps track
3 * of how many times a particular fd (per process) is referenced by
7 * - have many references to the same file, without needing an FD each
8 * - deciding when we have to close an FD (last reference disappears)
11 * - if a file-mmapped region is split, the refcount increases; there are
12 * now two regions referencing the same FD. We can't simply close the
13 * FD once either region is unmapped, as the pagefaults for the other
14 * would stop working. So we increase the refcount to that fd.
15 * - if a new file-maped region is requested, we might find out it's the
16 * same dev/inode the same process already has referenced. we could
17 * decide to close the new reference and use an existing one, so
18 * references to the same file aren't fd-limited.
19 * - if a file-mapped region is copied, we have to create a new
20 * fdref object, as the source process might disappear; we have to
21 * use the new process' fd for it.
27 #include <minix/hash.h>
35 static struct fdref
*fdrefs
;
37 void fdref_sanitycheck(void)
42 static int prevopen
= 0;
45 for(fr
= fdrefs
; fr
; fr
= fr
->next
) {
47 for(fr2
= fdrefs
; fr2
; fr2
= fr2
->next
) {
48 if(fr
== fr2
) continue;
49 if(fr
->fd
== fr2
->fd
) {
50 printf("equal fd omg\n");
53 if(fr
->ino
== fr2
->ino
&& fr
->dev
== fr2
->dev
) {
54 printf("equal metadata omg\n");
61 for(fr
= fdrefs
; fr
; fr
= fr
->next
) {
65 for(vmp
= vmproc
; vmp
< &vmproc
[VMP_NR
]; vmp
++) {
66 struct vir_region
*vr
;
67 if(!(vmp
->vm_flags
& VMF_INUSE
))
69 region_start_iter_least(&vmp
->vm_regions_avl
, &v_iter
);
70 while((vr
= region_get_iter(&v_iter
))) {
71 if(vr
->def_memtype
== &mem_type_mappedfile
&& vr
->param
.file
.inited
) {
72 vr
->param
.file
.fdref
->counting
++;
74 region_incr_iter(&v_iter
);
79 for(fr
= fdrefs
; fr
; fr
= fr
->next
) {
80 if(fr
->counting
!= fr
->refcount
) {
81 printf("counting %d != refcount %d\n",
82 fr
->counting
, fr
->refcount
);
87 if(prevopen
!= openfd
&& openfd
> 100) {
88 printf("%d open\n", openfd
);
93 struct fdref
*fdref_new(struct vmproc
*owner
, ino_t ino
, dev_t dev
, int fd
)
97 if(!SLABALLOC(nfdref
)) return NULL
;
100 nfdref
->refcount
= 0;
103 nfdref
->next
= fdrefs
;
109 void fdref_ref(struct fdref
*ref
, struct vir_region
*region
)
112 region
->param
.file
.fdref
= ref
;
116 void fdref_deref(struct vir_region
*region
)
118 struct fdref
*ref
= region
->param
.file
.fdref
;
122 assert(ref
->refcount
> 0);
125 region
->param
.file
.fdref
= NULL
;
127 assert(ref
->refcount
>= 0);
128 if(ref
->refcount
> 0) return;
130 if(fdrefs
== ref
) fdrefs
= ref
->next
;
133 for(r
= fdrefs
; r
->next
!= ref
; r
= r
->next
)
136 assert(r
->next
== ref
);
143 /* If the last reference has disappeared, free the
144 * ref object and asynchronously close the fd in VFS.
146 * We don't need a callback as a close failing, although
147 * unexpected, isn't a problem and can't be handled. VFS
148 * will print a diagnostic.
150 if(vfs_request(VMVFSREQ_FDCLOSE
, fd
, region
->parent
,
151 0, 0, NULL
, NULL
, NULL
, 0) != OK
) {
152 panic("fdref_deref: could not send close request");
156 struct fdref
*fdref_dedup_or_new(struct vmproc
*owner
,
157 ino_t ino
, dev_t dev
, int fd
, int mayclose
)
161 for(fr
= fdrefs
; fr
; fr
= fr
->next
) {
162 if(ino
== fr
->ino
&& dev
== fr
->dev
) {
166 if(!mayclose
) continue;
167 if(vfs_request(VMVFSREQ_FDCLOSE
, fd
, owner
,
168 0, 0, NULL
, NULL
, NULL
, 0) != OK
) {
169 printf("fdref_dedup_or_new: could not close\n");
175 return fdref_new(owner
, ino
, dev
, fd
);