Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / afs / LINUX / osi_vcache.c
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 }