xref: /minix3/minix/servers/vm/fdref.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1 
2 /* File that implements the 'fdref' data structure. It keeps track
3  * of how many times a particular fd (per process) is referenced by
4  * mmapped objects.
5  *
6  * This is used to
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)
9  *
10  * Examples:
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.
22  */
23 
24 #include <assert.h>
25 #include <string.h>
26 
27 #include <minix/hash.h>
28 
29 #include "proto.h"
30 #include "vm.h"
31 #include "fdref.h"
32 #include "vmproc.h"
33 #include "glo.h"
34 
35 static struct fdref *fdrefs;
36 
fdref_sanitycheck(void)37 void fdref_sanitycheck(void)
38 {
39 	struct vmproc *vmp;
40 	region_iter v_iter;
41 	struct fdref *fr;
42 	static int prevopen = 0;
43 	int openfd = 0;
44 
45 	for(fr = fdrefs; fr; fr = fr->next) {
46 		struct fdref *fr2;
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");
51 				util_stacktrace();
52 			}
53 			if(fr->ino == fr2->ino && fr->dev == fr2->dev) {
54 				printf("equal metadata omg\n");
55 				util_stacktrace();
56 			}
57 		}
58 		openfd++;
59 	}
60 
61 	for(fr = fdrefs; fr; fr = fr->next) {
62 		fr->counting = 0;
63 	}
64 
65 	for(vmp = vmproc; vmp < &vmproc[VMP_NR]; vmp++) {
66 		struct vir_region *vr;
67                 if(!(vmp->vm_flags & VMF_INUSE))
68 			continue;
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++;
73 			}
74 			region_incr_iter(&v_iter);
75 		}
76 
77 	}
78 
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);
83 			util_stacktrace();
84 		}
85 	}
86 
87 	if(prevopen != openfd && openfd > 100) {
88 		printf("%d open\n", openfd);
89 		prevopen = openfd;
90 	}
91 }
92 
fdref_new(struct vmproc * owner,ino_t ino,dev_t dev,int fd)93 struct fdref *fdref_new(struct vmproc *owner, ino_t ino, dev_t dev, int fd)
94 {
95 	struct fdref *nfdref;
96 
97 	if(!SLABALLOC(nfdref)) return NULL;
98 
99 	nfdref->fd = fd;
100 	nfdref->refcount = 0;
101 	nfdref->dev = dev;
102 	nfdref->ino = ino;
103 	nfdref->next = fdrefs;
104 	fdrefs = nfdref;
105 
106 	return nfdref;
107 }
108 
fdref_ref(struct fdref * ref,struct vir_region * region)109 void fdref_ref(struct fdref *ref, struct vir_region *region)
110 {
111 	assert(ref);
112 	region->param.file.fdref = ref;
113 	ref->refcount++;
114 }
115 
fdref_deref(struct vir_region * region)116 void fdref_deref(struct vir_region *region)
117 {
118 	struct fdref *ref = region->param.file.fdref;
119 	int fd;
120 
121 	assert(ref);
122 	assert(ref->refcount > 0);
123 
124 	fd = ref->fd;
125 	region->param.file.fdref = NULL;
126 	ref->refcount--;
127 	assert(ref->refcount >= 0);
128 	if(ref->refcount > 0) return;
129 
130 	if(fdrefs == ref) fdrefs = ref->next;
131 	else {
132 		struct fdref *r;
133 		for(r = fdrefs; r->next != ref; r = r->next)
134 			;
135 		assert(r);
136 		assert(r->next == ref);
137 		r->next = ref->next;
138 	}
139 
140 	SLABFREE(ref);
141 	ref = NULL;
142 
143 	/* If the last reference has disappeared, free the
144 	 * ref object and asynchronously close the fd in VFS.
145 	 *
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.
149 	 */
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");
153 	}
154 }
155 
fdref_dedup_or_new(struct vmproc * owner,ino_t ino,dev_t dev,int fd,int mayclose)156 struct fdref *fdref_dedup_or_new(struct vmproc *owner,
157 	ino_t ino, dev_t dev, int fd, int mayclose)
158 {
159 	struct fdref *fr;
160 
161 	for(fr = fdrefs; fr; fr = fr->next) {
162 		if(ino == fr->ino && dev == fr->dev) {
163 			if(fd == fr->fd) {
164 				return fr;
165 			}
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");
170 			}
171 			return fr;
172 		}
173 	}
174 
175 	return fdref_new(owner, ino, dev, fd);
176 }
177 
178