| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved. | 
|  | 3 | * | 
|  | 4 | * This program is free software; you can redistribute it and/or modify it | 
|  | 5 | * under the terms of version 2 of the GNU General Public License as | 
|  | 6 | * published by the Free Software Foundation. | 
|  | 7 | * | 
|  | 8 | * This program is distributed in the hope that it would be useful, but | 
|  | 9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | 
|  | 11 | * | 
|  | 12 | * Further, this software is distributed without any warranty that it is | 
|  | 13 | * free of the rightful claim of any third person regarding infringement | 
|  | 14 | * or the like.  Any license provided herein, whether implied or | 
|  | 15 | * otherwise, applies only to this software file.  Patent licenses, if | 
|  | 16 | * any, provided herein do not apply to combinations of this program with | 
|  | 17 | * other software, or any other product whatsoever. | 
|  | 18 | * | 
|  | 19 | * You should have received a copy of the GNU General Public License along | 
|  | 20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | 
|  | 21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | 
|  | 22 | * | 
|  | 23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | 
|  | 24 | * Mountain View, CA  94043, or: | 
|  | 25 | * | 
|  | 26 | * http://www.sgi.com | 
|  | 27 | * | 
|  | 28 | * For further information regarding this notice, see: | 
|  | 29 | * | 
|  | 30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | 
|  | 31 | */ | 
|  | 32 |  | 
|  | 33 | #include "xfs.h" | 
|  | 34 |  | 
|  | 35 |  | 
|  | 36 | uint64_t vn_generation;		/* vnode generation number */ | 
|  | 37 | DEFINE_SPINLOCK(vnumber_lock); | 
|  | 38 |  | 
|  | 39 | /* | 
|  | 40 | * Dedicated vnode inactive/reclaim sync semaphores. | 
|  | 41 | * Prime number of hash buckets since address is used as the key. | 
|  | 42 | */ | 
|  | 43 | #define NVSYNC                  37 | 
|  | 44 | #define vptosync(v)             (&vsync[((unsigned long)v) % NVSYNC]) | 
|  | 45 | sv_t vsync[NVSYNC]; | 
|  | 46 |  | 
|  | 47 | /* | 
|  | 48 | * Translate stat(2) file types to vnode types and vice versa. | 
|  | 49 | * Aware of numeric order of S_IFMT and vnode type values. | 
|  | 50 | */ | 
|  | 51 | enum vtype iftovt_tab[] = { | 
|  | 52 | VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON, | 
|  | 53 | VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VNON | 
|  | 54 | }; | 
|  | 55 |  | 
|  | 56 | u_short vttoif_tab[] = { | 
|  | 57 | 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFIFO, 0, S_IFSOCK | 
|  | 58 | }; | 
|  | 59 |  | 
|  | 60 |  | 
|  | 61 | void | 
|  | 62 | vn_init(void) | 
|  | 63 | { | 
|  | 64 | register sv_t *svp; | 
|  | 65 | register int i; | 
|  | 66 |  | 
|  | 67 | for (svp = vsync, i = 0; i < NVSYNC; i++, svp++) | 
|  | 68 | init_sv(svp, SV_DEFAULT, "vsy", i); | 
|  | 69 | } | 
|  | 70 |  | 
|  | 71 | /* | 
|  | 72 | * Clean a vnode of filesystem-specific data and prepare it for reuse. | 
|  | 73 | */ | 
|  | 74 | STATIC int | 
|  | 75 | vn_reclaim( | 
|  | 76 | struct vnode	*vp) | 
|  | 77 | { | 
|  | 78 | int		error; | 
|  | 79 |  | 
|  | 80 | XFS_STATS_INC(vn_reclaim); | 
|  | 81 | vn_trace_entry(vp, "vn_reclaim", (inst_t *)__return_address); | 
|  | 82 |  | 
|  | 83 | /* | 
|  | 84 | * Only make the VOP_RECLAIM call if there are behaviors | 
|  | 85 | * to call. | 
|  | 86 | */ | 
|  | 87 | if (vp->v_fbhv) { | 
|  | 88 | VOP_RECLAIM(vp, error); | 
|  | 89 | if (error) | 
|  | 90 | return -error; | 
|  | 91 | } | 
|  | 92 | ASSERT(vp->v_fbhv == NULL); | 
|  | 93 |  | 
|  | 94 | VN_LOCK(vp); | 
|  | 95 | vp->v_flag &= (VRECLM|VWAIT); | 
|  | 96 | VN_UNLOCK(vp, 0); | 
|  | 97 |  | 
|  | 98 | vp->v_type = VNON; | 
|  | 99 | vp->v_fbhv = NULL; | 
|  | 100 |  | 
|  | 101 | #ifdef XFS_VNODE_TRACE | 
|  | 102 | ktrace_free(vp->v_trace); | 
|  | 103 | vp->v_trace = NULL; | 
|  | 104 | #endif | 
|  | 105 |  | 
|  | 106 | return 0; | 
|  | 107 | } | 
|  | 108 |  | 
|  | 109 | STATIC void | 
|  | 110 | vn_wakeup( | 
|  | 111 | struct vnode	*vp) | 
|  | 112 | { | 
|  | 113 | VN_LOCK(vp); | 
|  | 114 | if (vp->v_flag & VWAIT) | 
|  | 115 | sv_broadcast(vptosync(vp)); | 
|  | 116 | vp->v_flag &= ~(VRECLM|VWAIT|VMODIFIED); | 
|  | 117 | VN_UNLOCK(vp, 0); | 
|  | 118 | } | 
|  | 119 |  | 
|  | 120 | int | 
|  | 121 | vn_wait( | 
|  | 122 | struct vnode	*vp) | 
|  | 123 | { | 
|  | 124 | VN_LOCK(vp); | 
|  | 125 | if (vp->v_flag & (VINACT | VRECLM)) { | 
|  | 126 | vp->v_flag |= VWAIT; | 
|  | 127 | sv_wait(vptosync(vp), PINOD, &vp->v_lock, 0); | 
|  | 128 | return 1; | 
|  | 129 | } | 
|  | 130 | VN_UNLOCK(vp, 0); | 
|  | 131 | return 0; | 
|  | 132 | } | 
|  | 133 |  | 
|  | 134 | struct vnode * | 
|  | 135 | vn_initialize( | 
|  | 136 | struct inode	*inode) | 
|  | 137 | { | 
|  | 138 | struct vnode	*vp = LINVFS_GET_VP(inode); | 
|  | 139 |  | 
|  | 140 | XFS_STATS_INC(vn_active); | 
|  | 141 | XFS_STATS_INC(vn_alloc); | 
|  | 142 |  | 
|  | 143 | vp->v_flag = VMODIFIED; | 
|  | 144 | spinlock_init(&vp->v_lock, "v_lock"); | 
|  | 145 |  | 
|  | 146 | spin_lock(&vnumber_lock); | 
|  | 147 | if (!++vn_generation)	/* v_number shouldn't be zero */ | 
|  | 148 | vn_generation++; | 
|  | 149 | vp->v_number = vn_generation; | 
|  | 150 | spin_unlock(&vnumber_lock); | 
|  | 151 |  | 
|  | 152 | ASSERT(VN_CACHED(vp) == 0); | 
|  | 153 |  | 
|  | 154 | /* Initialize the first behavior and the behavior chain head. */ | 
|  | 155 | vn_bhv_head_init(VN_BHV_HEAD(vp), "vnode"); | 
|  | 156 |  | 
|  | 157 | #ifdef	XFS_VNODE_TRACE | 
|  | 158 | vp->v_trace = ktrace_alloc(VNODE_TRACE_SIZE, KM_SLEEP); | 
|  | 159 | printk("Allocated VNODE_TRACE at 0x%p\n", vp->v_trace); | 
|  | 160 | #endif	/* XFS_VNODE_TRACE */ | 
|  | 161 |  | 
|  | 162 | vn_trace_exit(vp, "vn_initialize", (inst_t *)__return_address); | 
|  | 163 | return vp; | 
|  | 164 | } | 
|  | 165 |  | 
|  | 166 | /* | 
|  | 167 | * Get a reference on a vnode. | 
|  | 168 | */ | 
|  | 169 | vnode_t * | 
|  | 170 | vn_get( | 
|  | 171 | struct vnode	*vp, | 
|  | 172 | vmap_t		*vmap) | 
|  | 173 | { | 
|  | 174 | struct inode	*inode; | 
|  | 175 |  | 
|  | 176 | XFS_STATS_INC(vn_get); | 
|  | 177 | inode = LINVFS_GET_IP(vp); | 
|  | 178 | if (inode->i_state & I_FREEING) | 
|  | 179 | return NULL; | 
|  | 180 |  | 
|  | 181 | inode = ilookup(vmap->v_vfsp->vfs_super, vmap->v_ino); | 
|  | 182 | if (!inode)	/* Inode not present */ | 
|  | 183 | return NULL; | 
|  | 184 |  | 
|  | 185 | vn_trace_exit(vp, "vn_get", (inst_t *)__return_address); | 
|  | 186 |  | 
|  | 187 | return vp; | 
|  | 188 | } | 
|  | 189 |  | 
|  | 190 | /* | 
|  | 191 | * Revalidate the Linux inode from the vattr. | 
|  | 192 | * Note: i_size _not_ updated; we must hold the inode | 
|  | 193 | * semaphore when doing that - callers responsibility. | 
|  | 194 | */ | 
|  | 195 | void | 
|  | 196 | vn_revalidate_core( | 
|  | 197 | struct vnode	*vp, | 
|  | 198 | vattr_t		*vap) | 
|  | 199 | { | 
|  | 200 | struct inode	*inode = LINVFS_GET_IP(vp); | 
|  | 201 |  | 
|  | 202 | inode->i_mode	    = VTTOIF(vap->va_type) | vap->va_mode; | 
|  | 203 | inode->i_nlink	    = vap->va_nlink; | 
|  | 204 | inode->i_uid	    = vap->va_uid; | 
|  | 205 | inode->i_gid	    = vap->va_gid; | 
|  | 206 | inode->i_blocks	    = vap->va_nblocks; | 
|  | 207 | inode->i_mtime	    = vap->va_mtime; | 
|  | 208 | inode->i_ctime	    = vap->va_ctime; | 
|  | 209 | inode->i_atime	    = vap->va_atime; | 
|  | 210 | if (vap->va_xflags & XFS_XFLAG_IMMUTABLE) | 
|  | 211 | inode->i_flags |= S_IMMUTABLE; | 
|  | 212 | else | 
|  | 213 | inode->i_flags &= ~S_IMMUTABLE; | 
|  | 214 | if (vap->va_xflags & XFS_XFLAG_APPEND) | 
|  | 215 | inode->i_flags |= S_APPEND; | 
|  | 216 | else | 
|  | 217 | inode->i_flags &= ~S_APPEND; | 
|  | 218 | if (vap->va_xflags & XFS_XFLAG_SYNC) | 
|  | 219 | inode->i_flags |= S_SYNC; | 
|  | 220 | else | 
|  | 221 | inode->i_flags &= ~S_SYNC; | 
|  | 222 | if (vap->va_xflags & XFS_XFLAG_NOATIME) | 
|  | 223 | inode->i_flags |= S_NOATIME; | 
|  | 224 | else | 
|  | 225 | inode->i_flags &= ~S_NOATIME; | 
|  | 226 | } | 
|  | 227 |  | 
|  | 228 | /* | 
|  | 229 | * Revalidate the Linux inode from the vnode. | 
|  | 230 | */ | 
|  | 231 | int | 
|  | 232 | vn_revalidate( | 
|  | 233 | struct vnode	*vp) | 
|  | 234 | { | 
|  | 235 | vattr_t		va; | 
|  | 236 | int		error; | 
|  | 237 |  | 
|  | 238 | vn_trace_entry(vp, "vn_revalidate", (inst_t *)__return_address); | 
|  | 239 | ASSERT(vp->v_fbhv != NULL); | 
|  | 240 |  | 
|  | 241 | va.va_mask = XFS_AT_STAT|XFS_AT_XFLAGS; | 
|  | 242 | VOP_GETATTR(vp, &va, 0, NULL, error); | 
|  | 243 | if (!error) { | 
|  | 244 | vn_revalidate_core(vp, &va); | 
|  | 245 | VUNMODIFY(vp); | 
|  | 246 | } | 
|  | 247 | return -error; | 
|  | 248 | } | 
|  | 249 |  | 
|  | 250 | /* | 
|  | 251 | * purge a vnode from the cache | 
|  | 252 | * At this point the vnode is guaranteed to have no references (vn_count == 0) | 
|  | 253 | * The caller has to make sure that there are no ways someone could | 
|  | 254 | * get a handle (via vn_get) on the vnode (usually done via a mount/vfs lock). | 
|  | 255 | */ | 
|  | 256 | void | 
|  | 257 | vn_purge( | 
|  | 258 | struct vnode	*vp, | 
|  | 259 | vmap_t		*vmap) | 
|  | 260 | { | 
|  | 261 | vn_trace_entry(vp, "vn_purge", (inst_t *)__return_address); | 
|  | 262 |  | 
|  | 263 | again: | 
|  | 264 | /* | 
|  | 265 | * Check whether vp has already been reclaimed since our caller | 
|  | 266 | * sampled its version while holding a filesystem cache lock that | 
|  | 267 | * its VOP_RECLAIM function acquires. | 
|  | 268 | */ | 
|  | 269 | VN_LOCK(vp); | 
|  | 270 | if (vp->v_number != vmap->v_number) { | 
|  | 271 | VN_UNLOCK(vp, 0); | 
|  | 272 | return; | 
|  | 273 | } | 
|  | 274 |  | 
|  | 275 | /* | 
|  | 276 | * If vp is being reclaimed or inactivated, wait until it is inert, | 
|  | 277 | * then proceed.  Can't assume that vnode is actually reclaimed | 
|  | 278 | * just because the reclaimed flag is asserted -- a vn_alloc | 
|  | 279 | * reclaim can fail. | 
|  | 280 | */ | 
|  | 281 | if (vp->v_flag & (VINACT | VRECLM)) { | 
|  | 282 | ASSERT(vn_count(vp) == 0); | 
|  | 283 | vp->v_flag |= VWAIT; | 
|  | 284 | sv_wait(vptosync(vp), PINOD, &vp->v_lock, 0); | 
|  | 285 | goto again; | 
|  | 286 | } | 
|  | 287 |  | 
|  | 288 | /* | 
|  | 289 | * Another process could have raced in and gotten this vnode... | 
|  | 290 | */ | 
|  | 291 | if (vn_count(vp) > 0) { | 
|  | 292 | VN_UNLOCK(vp, 0); | 
|  | 293 | return; | 
|  | 294 | } | 
|  | 295 |  | 
|  | 296 | XFS_STATS_DEC(vn_active); | 
|  | 297 | vp->v_flag |= VRECLM; | 
|  | 298 | VN_UNLOCK(vp, 0); | 
|  | 299 |  | 
|  | 300 | /* | 
|  | 301 | * Call VOP_RECLAIM and clean vp. The FSYNC_INVAL flag tells | 
|  | 302 | * vp's filesystem to flush and invalidate all cached resources. | 
|  | 303 | * When vn_reclaim returns, vp should have no private data, | 
|  | 304 | * either in a system cache or attached to v_data. | 
|  | 305 | */ | 
|  | 306 | if (vn_reclaim(vp) != 0) | 
|  | 307 | panic("vn_purge: cannot reclaim"); | 
|  | 308 |  | 
|  | 309 | /* | 
|  | 310 | * Wakeup anyone waiting for vp to be reclaimed. | 
|  | 311 | */ | 
|  | 312 | vn_wakeup(vp); | 
|  | 313 | } | 
|  | 314 |  | 
|  | 315 | /* | 
|  | 316 | * Add a reference to a referenced vnode. | 
|  | 317 | */ | 
|  | 318 | struct vnode * | 
|  | 319 | vn_hold( | 
|  | 320 | struct vnode	*vp) | 
|  | 321 | { | 
|  | 322 | struct inode	*inode; | 
|  | 323 |  | 
|  | 324 | XFS_STATS_INC(vn_hold); | 
|  | 325 |  | 
|  | 326 | VN_LOCK(vp); | 
|  | 327 | inode = igrab(LINVFS_GET_IP(vp)); | 
|  | 328 | ASSERT(inode); | 
|  | 329 | VN_UNLOCK(vp, 0); | 
|  | 330 |  | 
|  | 331 | return vp; | 
|  | 332 | } | 
|  | 333 |  | 
|  | 334 | /* | 
|  | 335 | *  Call VOP_INACTIVE on last reference. | 
|  | 336 | */ | 
|  | 337 | void | 
|  | 338 | vn_rele( | 
|  | 339 | struct vnode	*vp) | 
|  | 340 | { | 
|  | 341 | int		vcnt; | 
|  | 342 | int		cache; | 
|  | 343 |  | 
|  | 344 | XFS_STATS_INC(vn_rele); | 
|  | 345 |  | 
|  | 346 | VN_LOCK(vp); | 
|  | 347 |  | 
|  | 348 | vn_trace_entry(vp, "vn_rele", (inst_t *)__return_address); | 
|  | 349 | vcnt = vn_count(vp); | 
|  | 350 |  | 
|  | 351 | /* | 
|  | 352 | * Since we always get called from put_inode we know | 
|  | 353 | * that i_count won't be decremented after we | 
|  | 354 | * return. | 
|  | 355 | */ | 
|  | 356 | if (!vcnt) { | 
|  | 357 | /* | 
|  | 358 | * As soon as we turn this on, noone can find us in vn_get | 
|  | 359 | * until we turn off VINACT or VRECLM | 
|  | 360 | */ | 
|  | 361 | vp->v_flag |= VINACT; | 
|  | 362 | VN_UNLOCK(vp, 0); | 
|  | 363 |  | 
|  | 364 | /* | 
|  | 365 | * Do not make the VOP_INACTIVE call if there | 
|  | 366 | * are no behaviors attached to the vnode to call. | 
|  | 367 | */ | 
|  | 368 | if (vp->v_fbhv) | 
|  | 369 | VOP_INACTIVE(vp, NULL, cache); | 
|  | 370 |  | 
|  | 371 | VN_LOCK(vp); | 
|  | 372 | if (vp->v_flag & VWAIT) | 
|  | 373 | sv_broadcast(vptosync(vp)); | 
|  | 374 |  | 
|  | 375 | vp->v_flag &= ~(VINACT|VWAIT|VRECLM|VMODIFIED); | 
|  | 376 | } | 
|  | 377 |  | 
|  | 378 | VN_UNLOCK(vp, 0); | 
|  | 379 |  | 
|  | 380 | vn_trace_exit(vp, "vn_rele", (inst_t *)__return_address); | 
|  | 381 | } | 
|  | 382 |  | 
|  | 383 | /* | 
|  | 384 | * Finish the removal of a vnode. | 
|  | 385 | */ | 
|  | 386 | void | 
|  | 387 | vn_remove( | 
|  | 388 | struct vnode	*vp) | 
|  | 389 | { | 
|  | 390 | vmap_t		vmap; | 
|  | 391 |  | 
|  | 392 | /* Make sure we don't do this to the same vnode twice */ | 
|  | 393 | if (!(vp->v_fbhv)) | 
|  | 394 | return; | 
|  | 395 |  | 
|  | 396 | XFS_STATS_INC(vn_remove); | 
|  | 397 | vn_trace_exit(vp, "vn_remove", (inst_t *)__return_address); | 
|  | 398 |  | 
|  | 399 | /* | 
|  | 400 | * After the following purge the vnode | 
|  | 401 | * will no longer exist. | 
|  | 402 | */ | 
|  | 403 | VMAP(vp, vmap); | 
|  | 404 | vn_purge(vp, &vmap); | 
|  | 405 | } | 
|  | 406 |  | 
|  | 407 |  | 
|  | 408 | #ifdef	XFS_VNODE_TRACE | 
|  | 409 |  | 
|  | 410 | #define KTRACE_ENTER(vp, vk, s, line, ra)			\ | 
|  | 411 | ktrace_enter(	(vp)->v_trace,				\ | 
|  | 412 | /*  0 */		(void *)(__psint_t)(vk),		\ | 
|  | 413 | /*  1 */		(void *)(s),				\ | 
|  | 414 | /*  2 */		(void *)(__psint_t) line,		\ | 
|  | 415 | /*  3 */		(void *)(vn_count(vp)), \ | 
|  | 416 | /*  4 */		(void *)(ra),				\ | 
|  | 417 | /*  5 */		(void *)(__psunsigned_t)(vp)->v_flag,	\ | 
|  | 418 | /*  6 */		(void *)(__psint_t)current_cpu(),	\ | 
|  | 419 | /*  7 */		(void *)(__psint_t)current_pid(),	\ | 
|  | 420 | /*  8 */		(void *)__return_address,		\ | 
|  | 421 | /*  9 */		0, 0, 0, 0, 0, 0, 0) | 
|  | 422 |  | 
|  | 423 | /* | 
|  | 424 | * Vnode tracing code. | 
|  | 425 | */ | 
|  | 426 | void | 
|  | 427 | vn_trace_entry(vnode_t *vp, char *func, inst_t *ra) | 
|  | 428 | { | 
|  | 429 | KTRACE_ENTER(vp, VNODE_KTRACE_ENTRY, func, 0, ra); | 
|  | 430 | } | 
|  | 431 |  | 
|  | 432 | void | 
|  | 433 | vn_trace_exit(vnode_t *vp, char *func, inst_t *ra) | 
|  | 434 | { | 
|  | 435 | KTRACE_ENTER(vp, VNODE_KTRACE_EXIT, func, 0, ra); | 
|  | 436 | } | 
|  | 437 |  | 
|  | 438 | void | 
|  | 439 | vn_trace_hold(vnode_t *vp, char *file, int line, inst_t *ra) | 
|  | 440 | { | 
|  | 441 | KTRACE_ENTER(vp, VNODE_KTRACE_HOLD, file, line, ra); | 
|  | 442 | } | 
|  | 443 |  | 
|  | 444 | void | 
|  | 445 | vn_trace_ref(vnode_t *vp, char *file, int line, inst_t *ra) | 
|  | 446 | { | 
|  | 447 | KTRACE_ENTER(vp, VNODE_KTRACE_REF, file, line, ra); | 
|  | 448 | } | 
|  | 449 |  | 
|  | 450 | void | 
|  | 451 | vn_trace_rele(vnode_t *vp, char *file, int line, inst_t *ra) | 
|  | 452 | { | 
|  | 453 | KTRACE_ENTER(vp, VNODE_KTRACE_RELE, file, line, ra); | 
|  | 454 | } | 
|  | 455 | #endif	/* XFS_VNODE_TRACE */ |