Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 */ |