Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright 2000, International Business Machines Corporation and others. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This software has been released under the terms of the IBM Public | |
6 | * License. For details, see the LICENSE file in the top-level source | |
7 | * directory or online at http://www.openafs.org/dl/license10.html | |
8 | */ | |
9 | ||
10 | #include <afsconfig.h> | |
11 | #include "afs/param.h" | |
12 | ||
13 | #include "afs/sysincludes.h" /*Standard vendor system headers */ | |
14 | #include "afsincludes.h" /*AFS-based standard headers */ | |
15 | ||
16 | #include "osi_compat.h" | |
17 | ||
18 | static void | |
19 | TryEvictDirDentries(struct inode *inode) | |
20 | { | |
21 | struct dentry *dentry; | |
22 | #if defined(D_ALIAS_IS_HLIST) && !defined(HLIST_ITERATOR_NO_NODE) | |
23 | struct hlist_node *p; | |
24 | #endif | |
25 | ||
26 | afs_d_alias_lock(inode); | |
27 | ||
28 | restart: | |
29 | #if defined(D_ALIAS_IS_HLIST) | |
30 | # if defined(HLIST_ITERATOR_NO_NODE) | |
31 | hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) { | |
32 | # else | |
33 | hlist_for_each_entry(dentry, p, &inode->i_dentry, d_alias) { | |
34 | # endif | |
35 | #else | |
36 | list_for_each_entry(dentry, &inode->i_dentry, d_alias) { | |
37 | #endif | |
38 | spin_lock(&dentry->d_lock); | |
39 | if (d_unhashed(dentry)) { | |
40 | spin_unlock(&dentry->d_lock); | |
41 | continue; | |
42 | } | |
43 | spin_unlock(&dentry->d_lock); | |
44 | afs_linux_dget(dentry); | |
45 | ||
46 | afs_d_alias_unlock(inode); | |
47 | ||
48 | /* | |
49 | * Once we have dropped the d_alias lock (above), it is no longer safe | |
50 | * to 'continue' our iteration over d_alias because the list may change | |
51 | * out from under us. Therefore, we must either leave the loop, or | |
52 | * restart from the beginning. To avoid looping forever, we must only | |
53 | * restart if we know we've d_drop'd an alias. In all other cases we | |
54 | * must leave the loop. | |
55 | */ | |
56 | ||
57 | /* | |
58 | * For a long time we used d_invalidate() for this purpose, but | |
59 | * using shrink_dcache_parent() and checking the refcount ourselves is | |
60 | * better, for two reasons: it avoids causing ENOENT issues for the | |
61 | * CWD in linux versions since 3.11, and it avoids dropping Linux | |
62 | * submounts. | |
63 | * | |
64 | * For non-fakestat, AFS mountpoints look like directories and end up here. | |
65 | */ | |
66 | ||
67 | shrink_dcache_parent(dentry); | |
68 | spin_lock(&dentry->d_lock); | |
69 | if (afs_dentry_count(dentry) > 1) /* still has references */ { | |
70 | if (dentry->d_inode != NULL) /* is not a negative dentry */ { | |
71 | spin_unlock(&dentry->d_lock); | |
72 | dput(dentry); | |
73 | goto inuse; | |
74 | } | |
75 | } | |
76 | /* | |
77 | * This is either a negative dentry, or a dentry with no references. | |
78 | * Either way, it is okay to unhash it now. | |
79 | * Do so under the d_lock (that is, via __d_drop() instead of d_drop()) | |
80 | * to avoid a race with another process picking up a reference. | |
81 | */ | |
82 | __d_drop(dentry); | |
83 | spin_unlock(&dentry->d_lock); | |
84 | ||
85 | dput(dentry); | |
86 | afs_d_alias_lock(inode); | |
87 | goto restart; | |
88 | } | |
89 | afs_d_alias_unlock(inode); | |
90 | ||
91 | inuse: | |
92 | return; | |
93 | } | |
94 | ||
95 | ||
96 | int | |
97 | osi_TryEvictVCache(struct vcache *avc, int *slept, int defersleep) | |
98 | { | |
99 | int code; | |
100 | ||
101 | /* First, see if we can evict the inode from the dcache */ | |
102 | if (defersleep && avc != afs_globalVp && VREFCOUNT(avc) > 1 | |
103 | && avc->opens == 0) { | |
104 | struct inode *ip = AFSTOV(avc); | |
105 | ||
106 | *slept = 1; | |
107 | AFS_FAST_HOLD(avc); | |
108 | ReleaseWriteLock(&afs_xvcache); | |
109 | AFS_GUNLOCK(); | |
110 | ||
111 | if (S_ISDIR(ip->i_mode)) | |
112 | TryEvictDirDentries(ip); | |
113 | else | |
114 | d_prune_aliases(ip); | |
115 | ||
116 | AFS_GLOCK(); | |
117 | ObtainWriteLock(&afs_xvcache, 733); | |
118 | AFS_FAST_RELE(avc); | |
119 | } | |
120 | ||
121 | /* See if we can evict it from the VLRUQ */ | |
122 | if (VREFCOUNT_GT(avc, 0) && !VREFCOUNT_GT(avc, 1) && avc->opens == 0 | |
123 | && (avc->f.states & CUnlinkedDel) == 0) { | |
124 | int didsleep = *slept; | |
125 | ||
126 | code = afs_FlushVCache(avc, slept); | |
127 | /* flushvcache wipes slept; restore slept if we did before */ | |
128 | if (didsleep) | |
129 | *slept = didsleep; | |
130 | ||
131 | if (code == 0) | |
132 | return 1; | |
133 | } | |
134 | ||
135 | return 0; | |
136 | } | |
137 | ||
138 | struct vcache * | |
139 | osi_NewVnode(void) | |
140 | { | |
141 | struct inode *ip; | |
142 | struct vcache *tvc; | |
143 | ||
144 | AFS_GUNLOCK(); | |
145 | ip = new_inode(afs_globalVFS); | |
146 | if (!ip) | |
147 | osi_Panic("afs_NewVCache: no more inodes"); | |
148 | AFS_GLOCK(); | |
149 | #if defined(STRUCT_SUPER_OPERATIONS_HAS_ALLOC_INODE) | |
150 | tvc = VTOAFS(ip); | |
151 | #else | |
152 | tvc = afs_osi_Alloc(sizeof(struct vcache)); | |
153 | ip->u.generic_ip = tvc; | |
154 | tvc->v = ip; | |
155 | #endif | |
156 | ||
157 | INIT_LIST_HEAD(&tvc->pagewriters); | |
158 | spin_lock_init(&tvc->pagewriter_lock); | |
159 | ||
160 | return tvc; | |
161 | } | |
162 | ||
163 | void | |
164 | osi_PrePopulateVCache(struct vcache *avc) | |
165 | { | |
166 | avc->uncred = 0; | |
167 | memset(&(avc->f), 0, sizeof(struct fvcache)); | |
168 | avc->cred = NULL; | |
169 | } | |
170 | ||
171 | void | |
172 | osi_AttachVnode(struct vcache *avc, int seq) | |
173 | { | |
174 | /* Nada */ | |
175 | } | |
176 | ||
177 | void | |
178 | osi_PostPopulateVCache(struct vcache *avc) | |
179 | { | |
180 | vSetType(avc, VREG); | |
181 | } | |
182 | ||
183 | /** | |
184 | * osi_ResetRootVCache - Reset the root vcache | |
185 | * Reset the dentry associated with the afs root. | |
186 | * Called from afs_CheckRootVolume when we notice that | |
187 | * the root volume ID has changed. | |
188 | * | |
189 | * @volid: volume ID for the afs root | |
190 | */ | |
191 | void | |
192 | osi_ResetRootVCache(afs_uint32 volid) | |
193 | { | |
194 | struct vrequest *treq = NULL; | |
195 | struct vattr vattr; | |
196 | cred_t *credp; | |
197 | struct dentry *dp; | |
198 | struct vcache *vcp; | |
199 | struct inode *root = AFSTOV(afs_globalVp); | |
200 | ||
201 | afs_rootFid.Fid.Volume = volid; | |
202 | afs_rootFid.Fid.Vnode = 1; | |
203 | afs_rootFid.Fid.Unique = 1; | |
204 | ||
205 | credp = crref(); | |
206 | if (afs_CreateReq(&treq, credp)) | |
207 | goto out; | |
208 | vcp = afs_GetVCache(&afs_rootFid, treq, NULL, NULL); | |
209 | if (!vcp) | |
210 | goto out; | |
211 | afs_getattr(vcp, &vattr, credp); | |
212 | afs_fill_inode(AFSTOV(vcp), &vattr); | |
213 | ||
214 | dp = d_find_alias(root); | |
215 | ||
216 | afs_d_alias_lock(AFSTOV(vcp)); | |
217 | ||
218 | spin_lock(&dp->d_lock); | |
219 | #if defined(D_ALIAS_IS_HLIST) | |
220 | hlist_del_init(&dp->d_alias); | |
221 | hlist_add_head(&dp->d_alias, &(AFSTOV(vcp)->i_dentry)); | |
222 | #else | |
223 | list_del_init(&dp->d_alias); | |
224 | list_add(&dp->d_alias, &(AFSTOV(vcp)->i_dentry)); | |
225 | #endif | |
226 | dp->d_inode = AFSTOV(vcp); | |
227 | spin_unlock(&dp->d_lock); | |
228 | ||
229 | afs_d_alias_unlock(AFSTOV(vcp)); | |
230 | ||
231 | dput(dp); | |
232 | ||
233 | AFS_RELE(root); | |
234 | afs_globalVp = vcp; | |
235 | out: | |
236 | crfree(credp); | |
237 | afs_DestroyReq(treq); | |
238 | } |