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 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 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 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 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 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