diff options
Diffstat (limited to 'fs/xfs/linux-2.6/xfs_vnode.c')
-rw-r--r-- | fs/xfs/linux-2.6/xfs_vnode.c | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/fs/xfs/linux-2.6/xfs_vnode.c b/fs/xfs/linux-2.6/xfs_vnode.c new file mode 100644 index 000000000000..849c61c74f3c --- /dev/null +++ b/fs/xfs/linux-2.6/xfs_vnode.c | |||
@@ -0,0 +1,455 @@ | |||
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 */ | ||