1 /* This file contains the routines related to vnodes. 2 * The entry points are: 3 * 4 * get_vnode - increase counter and get details of an inode 5 * get_free_vnode - get a pointer to a free vnode obj 6 * find_vnode - find a vnode according to the FS endpoint and the inode num. 7 * dup_vnode - duplicate vnode (i.e. increase counter) 8 * put_vnode - drop vnode (i.e. decrease counter) 9 */ 10 11 #include "fs.h" 12 #include "vnode.h" 13 #include "vmnt.h" 14 #include "file.h" 15 #include <minix/vfsif.h> 16 #include <assert.h> 17 18 /* Is vnode pointer reasonable? */ 19 #if NDEBUG 20 #define SANEVP(v) 21 #define CHECKVN(v) 22 #define ASSERTVP(v) 23 #else 24 #define SANEVP(v) ((((v) >= &vnode[0] && (v) < &vnode[NR_VNODES]))) 25 26 #define BADVP(v, f, l) printf("%s:%d: bad vp %p\n", f, l, v) 27 28 /* vp check that returns 0 for use in check_vrefs() */ 29 #define CHECKVN(v) if(!SANEVP(v)) { \ 30 BADVP(v, __FILE__, __LINE__); \ 31 return 0; \ 32 } 33 34 /* vp check that panics */ 35 #define ASSERTVP(v) if(!SANEVP(v)) { \ 36 BADVP(v, __FILE__, __LINE__); panic("bad vp"); } 37 #endif 38 39 #if LOCK_DEBUG 40 /*===========================================================================* 41 * check_vnode_locks_by_me * 42 *===========================================================================*/ 43 void check_vnode_locks_by_me(struct fproc *rfp) 44 { 45 /* Check whether this thread still has locks held on vnodes */ 46 struct vnode *vp; 47 48 for (vp = &vnode[0]; vp < &vnode[NR_VNODES]; vp++) { 49 if (tll_locked_by_me(&vp->v_lock)) { 50 panic("Thread %d still holds vnode lock on vp %p call_nr=%d\n", 51 mthread_self(), vp, job_call_nr); 52 } 53 } 54 55 if (rfp->fp_vp_rdlocks != 0) 56 panic("Thread %d still holds read locks on a vnode (%d) call_nr=%d\n", 57 mthread_self(), rfp->fp_vp_rdlocks, job_call_nr); 58 } 59 #endif 60 61 /*===========================================================================* 62 * check_vnode_locks * 63 *===========================================================================*/ 64 void 65 check_vnode_locks(void) 66 { 67 struct vnode *vp; 68 int count = 0; 69 70 for (vp = &vnode[0]; vp < &vnode[NR_VNODES]; vp++) 71 if (is_vnode_locked(vp)) { 72 count++; 73 } 74 75 if (count) panic("%d locked vnodes\n", count); 76 #if 0 77 printf("check_vnode_locks OK\n"); 78 #endif 79 } 80 81 /*===========================================================================* 82 * get_free_vnode * 83 *===========================================================================*/ 84 struct vnode * 85 get_free_vnode(void) 86 { 87 /* Find a free vnode slot in the vnode table (it's not actually allocated) */ 88 struct vnode *vp; 89 90 for (vp = &vnode[0]; vp < &vnode[NR_VNODES]; ++vp) { 91 if (vp->v_ref_count == 0 && !is_vnode_locked(vp)) { 92 vp->v_uid = -1; 93 vp->v_gid = -1; 94 vp->v_sdev = NO_DEV; 95 vp->v_mapfs_e = NONE; 96 vp->v_mapfs_count = 0; 97 vp->v_mapinode_nr = 0; 98 return(vp); 99 } 100 } 101 102 err_code = ENFILE; 103 return(NULL); 104 } 105 106 107 /*===========================================================================* 108 * find_vnode * 109 *===========================================================================*/ 110 struct vnode *find_vnode(int fs_e, ino_t ino) 111 { 112 /* Find a specified (FS endpoint and inode number) vnode in the 113 * vnode table */ 114 struct vnode *vp; 115 116 for (vp = &vnode[0]; vp < &vnode[NR_VNODES]; ++vp) 117 if (vp->v_ref_count > 0 && vp->v_inode_nr == ino && vp->v_fs_e == fs_e) 118 return(vp); 119 120 return(NULL); 121 } 122 123 /*===========================================================================* 124 * is_vnode_locked * 125 *===========================================================================*/ 126 int is_vnode_locked(struct vnode *vp) 127 { 128 /* Find out whether a thread holds a lock on this vnode or is trying to obtain 129 * a lock. */ 130 ASSERTVP(vp); 131 132 return(tll_islocked(&vp->v_lock) || tll_haspendinglock(&vp->v_lock)); 133 } 134 135 /*===========================================================================* 136 * init_vnodes * 137 *===========================================================================*/ 138 void init_vnodes(void) 139 { 140 struct vnode *vp; 141 142 for (vp = &vnode[0]; vp < &vnode[NR_VNODES]; ++vp) { 143 vp->v_fs_e = NONE; 144 vp->v_mapfs_e = NONE; 145 vp->v_inode_nr = 0; 146 vp->v_ref_count = 0; 147 vp->v_fs_count = 0; 148 vp->v_mapfs_count = 0; 149 tll_init(&vp->v_lock); 150 } 151 } 152 153 /*===========================================================================* 154 * lock_vnode * 155 *===========================================================================*/ 156 int lock_vnode(struct vnode *vp, tll_access_t locktype) 157 { 158 int r; 159 160 ASSERTVP(vp); 161 162 r = tll_lock(&vp->v_lock, locktype); 163 164 #if LOCK_DEBUG 165 if (locktype == VNODE_READ) { 166 fp->fp_vp_rdlocks++; 167 } 168 #endif 169 170 if (r == EBUSY) return(r); 171 return(OK); 172 } 173 174 /*===========================================================================* 175 * unlock_vnode * 176 *===========================================================================*/ 177 void unlock_vnode(struct vnode *vp) 178 { 179 #if LOCK_DEBUG 180 int i; 181 register struct vnode *rvp; 182 struct worker_thread *w; 183 #endif 184 ASSERTVP(vp); 185 186 #if LOCK_DEBUG 187 /* Decrease read-only lock counter when not locked as VNODE_OPCL or 188 * VNODE_WRITE */ 189 if (!tll_locked_by_me(&vp->v_lock)) { 190 fp->fp_vp_rdlocks--; 191 } 192 193 for (i = 0; i < NR_VNODES; i++) { 194 rvp = &vnode[i]; 195 196 w = rvp->v_lock.t_write; 197 assert(w != self); 198 while (w && w->w_next != NULL) { 199 w = w->w_next; 200 assert(w != self); 201 } 202 203 w = rvp->v_lock.t_serial; 204 assert(w != self); 205 while (w && w->w_next != NULL) { 206 w = w->w_next; 207 assert(w != self); 208 } 209 } 210 #endif 211 212 tll_unlock(&vp->v_lock); 213 } 214 215 /*===========================================================================* 216 * vnode * 217 *===========================================================================*/ 218 void upgrade_vnode_lock(struct vnode *vp) 219 { 220 ASSERTVP(vp); 221 tll_upgrade(&vp->v_lock); 222 } 223 224 /*===========================================================================* 225 * dup_vnode * 226 *===========================================================================*/ 227 void dup_vnode(struct vnode *vp) 228 { 229 /* dup_vnode() is called to increment the vnode and therefore the 230 * referred inode's counter. 231 */ 232 ASSERTVP(vp); 233 vp->v_ref_count++; 234 } 235 236 237 /*===========================================================================* 238 * put_vnode * 239 *===========================================================================*/ 240 void put_vnode(struct vnode *vp) 241 { 242 /* Decrease vnode's usage counter and decrease inode's usage counter in the 243 * corresponding FS process. Decreasing the fs_count each time we decrease the 244 * ref count would lead to poor performance. Instead, only decrease fs_count 245 * when the ref count hits zero. However, this could lead to fs_count to wrap. 246 * To prevent this, we drop the counter to 1 when the counter hits 256. 247 * We maintain fs_count as a sanity check to make sure VFS and the FS are in 248 * sync. 249 */ 250 int r, lock_vp; 251 252 ASSERTVP(vp); 253 254 /* Lock vnode. It's quite possible this thread already has a lock on this 255 * vnode. That's no problem, because the reference counter will not decrease 256 * to zero in that case. However, if the counter does decrease to zero *and* 257 * is already locked, we have a consistency problem somewhere. */ 258 lock_vp = lock_vnode(vp, VNODE_OPCL); 259 260 if (vp->v_ref_count > 1) { 261 /* Decrease counter */ 262 vp->v_ref_count--; 263 if (vp->v_fs_count > 256) 264 vnode_clean_refs(vp); 265 if (lock_vp != EBUSY) unlock_vnode(vp); 266 return; 267 } 268 269 /* If we already had a lock, there is a consistency problem */ 270 assert(lock_vp != EBUSY); 271 upgrade_vnode_lock(vp); /* Acquire exclusive access */ 272 273 /* A vnode that's not in use can't be put back. */ 274 if (vp->v_ref_count <= 0) 275 panic("put_vnode failed: bad v_ref_count %d\n", vp->v_ref_count); 276 277 /* fs_count should indicate that the file is in use. */ 278 if (vp->v_fs_count <= 0) 279 panic("put_vnode failed: bad v_fs_count %d\n", vp->v_fs_count); 280 281 /* Tell FS we don't need this inode to be open anymore. */ 282 r = req_putnode(vp->v_fs_e, vp->v_inode_nr, vp->v_fs_count); 283 284 if (r != OK) { 285 printf("VFS: putnode failed: %d\n", r); 286 util_stacktrace(); 287 } 288 289 /* This inode could've been mapped. If so, tell mapped FS to close it as 290 * well. If mapped onto same FS, this putnode is not needed. */ 291 if (vp->v_mapfs_e != NONE && vp->v_mapfs_e != vp->v_fs_e) 292 req_putnode(vp->v_mapfs_e, vp->v_mapinode_nr, vp->v_mapfs_count); 293 294 vp->v_fs_count = 0; 295 vp->v_ref_count = 0; 296 vp->v_mapfs_count = 0; 297 298 unlock_vnode(vp); 299 } 300 301 302 /*===========================================================================* 303 * vnode_clean_refs * 304 *===========================================================================*/ 305 void vnode_clean_refs(struct vnode *vp) 306 { 307 /* Tell the underlying FS to drop all reference but one. */ 308 309 if (vp == NULL) return; 310 if (vp->v_fs_count <= 1) return; /* Nothing to do */ 311 312 /* Drop all references except one */ 313 req_putnode(vp->v_fs_e, vp->v_inode_nr, vp->v_fs_count - 1); 314 vp->v_fs_count = 1; 315 } 316 317