| 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 | } |