diff options
Diffstat (limited to 'fs/xfs/xfs_iget.c')
-rw-r--r-- | fs/xfs/xfs_iget.c | 47 |
1 files changed, 35 insertions, 12 deletions
diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 9fae47556604..04ed09b907b8 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c | |||
@@ -80,6 +80,7 @@ xfs_inode_alloc( | |||
80 | ASSERT(atomic_read(&ip->i_pincount) == 0); | 80 | ASSERT(atomic_read(&ip->i_pincount) == 0); |
81 | ASSERT(!spin_is_locked(&ip->i_flags_lock)); | 81 | ASSERT(!spin_is_locked(&ip->i_flags_lock)); |
82 | ASSERT(completion_done(&ip->i_flush)); | 82 | ASSERT(completion_done(&ip->i_flush)); |
83 | ASSERT(ip->i_ino == 0); | ||
83 | 84 | ||
84 | mrlock_init(&ip->i_iolock, MRLOCK_BARRIER, "xfsio", ip->i_ino); | 85 | mrlock_init(&ip->i_iolock, MRLOCK_BARRIER, "xfsio", ip->i_ino); |
85 | lockdep_set_class_and_name(&ip->i_iolock.mr_lock, | 86 | lockdep_set_class_and_name(&ip->i_iolock.mr_lock, |
@@ -98,9 +99,6 @@ xfs_inode_alloc( | |||
98 | ip->i_size = 0; | 99 | ip->i_size = 0; |
99 | ip->i_new_size = 0; | 100 | ip->i_new_size = 0; |
100 | 101 | ||
101 | /* prevent anyone from using this yet */ | ||
102 | VFS_I(ip)->i_state = I_NEW; | ||
103 | |||
104 | return ip; | 102 | return ip; |
105 | } | 103 | } |
106 | 104 | ||
@@ -159,6 +157,16 @@ xfs_inode_free( | |||
159 | ASSERT(!spin_is_locked(&ip->i_flags_lock)); | 157 | ASSERT(!spin_is_locked(&ip->i_flags_lock)); |
160 | ASSERT(completion_done(&ip->i_flush)); | 158 | ASSERT(completion_done(&ip->i_flush)); |
161 | 159 | ||
160 | /* | ||
161 | * Because we use RCU freeing we need to ensure the inode always | ||
162 | * appears to be reclaimed with an invalid inode number when in the | ||
163 | * free state. The ip->i_flags_lock provides the barrier against lookup | ||
164 | * races. | ||
165 | */ | ||
166 | spin_lock(&ip->i_flags_lock); | ||
167 | ip->i_flags = XFS_IRECLAIM; | ||
168 | ip->i_ino = 0; | ||
169 | spin_unlock(&ip->i_flags_lock); | ||
162 | call_rcu((struct rcu_head *)&VFS_I(ip)->i_dentry, __xfs_inode_free); | 170 | call_rcu((struct rcu_head *)&VFS_I(ip)->i_dentry, __xfs_inode_free); |
163 | } | 171 | } |
164 | 172 | ||
@@ -169,14 +177,29 @@ static int | |||
169 | xfs_iget_cache_hit( | 177 | xfs_iget_cache_hit( |
170 | struct xfs_perag *pag, | 178 | struct xfs_perag *pag, |
171 | struct xfs_inode *ip, | 179 | struct xfs_inode *ip, |
180 | xfs_ino_t ino, | ||
172 | int flags, | 181 | int flags, |
173 | int lock_flags) __releases(pag->pag_ici_lock) | 182 | int lock_flags) __releases(RCU) |
174 | { | 183 | { |
175 | struct inode *inode = VFS_I(ip); | 184 | struct inode *inode = VFS_I(ip); |
176 | struct xfs_mount *mp = ip->i_mount; | 185 | struct xfs_mount *mp = ip->i_mount; |
177 | int error; | 186 | int error; |
178 | 187 | ||
188 | /* | ||
189 | * check for re-use of an inode within an RCU grace period due to the | ||
190 | * radix tree nodes not being updated yet. We monitor for this by | ||
191 | * setting the inode number to zero before freeing the inode structure. | ||
192 | * If the inode has been reallocated and set up, then the inode number | ||
193 | * will not match, so check for that, too. | ||
194 | */ | ||
179 | spin_lock(&ip->i_flags_lock); | 195 | spin_lock(&ip->i_flags_lock); |
196 | if (ip->i_ino != ino) { | ||
197 | trace_xfs_iget_skip(ip); | ||
198 | XFS_STATS_INC(xs_ig_frecycle); | ||
199 | error = EAGAIN; | ||
200 | goto out_error; | ||
201 | } | ||
202 | |||
180 | 203 | ||
181 | /* | 204 | /* |
182 | * If we are racing with another cache hit that is currently | 205 | * If we are racing with another cache hit that is currently |
@@ -219,7 +242,7 @@ xfs_iget_cache_hit( | |||
219 | ip->i_flags |= XFS_IRECLAIM; | 242 | ip->i_flags |= XFS_IRECLAIM; |
220 | 243 | ||
221 | spin_unlock(&ip->i_flags_lock); | 244 | spin_unlock(&ip->i_flags_lock); |
222 | read_unlock(&pag->pag_ici_lock); | 245 | rcu_read_unlock(); |
223 | 246 | ||
224 | error = -inode_init_always(mp->m_super, inode); | 247 | error = -inode_init_always(mp->m_super, inode); |
225 | if (error) { | 248 | if (error) { |
@@ -227,7 +250,7 @@ xfs_iget_cache_hit( | |||
227 | * Re-initializing the inode failed, and we are in deep | 250 | * Re-initializing the inode failed, and we are in deep |
228 | * trouble. Try to re-add it to the reclaim list. | 251 | * trouble. Try to re-add it to the reclaim list. |
229 | */ | 252 | */ |
230 | read_lock(&pag->pag_ici_lock); | 253 | rcu_read_lock(); |
231 | spin_lock(&ip->i_flags_lock); | 254 | spin_lock(&ip->i_flags_lock); |
232 | 255 | ||
233 | ip->i_flags &= ~XFS_INEW; | 256 | ip->i_flags &= ~XFS_INEW; |
@@ -261,7 +284,7 @@ xfs_iget_cache_hit( | |||
261 | 284 | ||
262 | /* We've got a live one. */ | 285 | /* We've got a live one. */ |
263 | spin_unlock(&ip->i_flags_lock); | 286 | spin_unlock(&ip->i_flags_lock); |
264 | read_unlock(&pag->pag_ici_lock); | 287 | rcu_read_unlock(); |
265 | trace_xfs_iget_hit(ip); | 288 | trace_xfs_iget_hit(ip); |
266 | } | 289 | } |
267 | 290 | ||
@@ -275,7 +298,7 @@ xfs_iget_cache_hit( | |||
275 | 298 | ||
276 | out_error: | 299 | out_error: |
277 | spin_unlock(&ip->i_flags_lock); | 300 | spin_unlock(&ip->i_flags_lock); |
278 | read_unlock(&pag->pag_ici_lock); | 301 | rcu_read_unlock(); |
279 | return error; | 302 | return error; |
280 | } | 303 | } |
281 | 304 | ||
@@ -397,7 +420,7 @@ xfs_iget( | |||
397 | xfs_agino_t agino; | 420 | xfs_agino_t agino; |
398 | 421 | ||
399 | /* reject inode numbers outside existing AGs */ | 422 | /* reject inode numbers outside existing AGs */ |
400 | if (XFS_INO_TO_AGNO(mp, ino) >= mp->m_sb.sb_agcount) | 423 | if (!ino || XFS_INO_TO_AGNO(mp, ino) >= mp->m_sb.sb_agcount) |
401 | return EINVAL; | 424 | return EINVAL; |
402 | 425 | ||
403 | /* get the perag structure and ensure that it's inode capable */ | 426 | /* get the perag structure and ensure that it's inode capable */ |
@@ -406,15 +429,15 @@ xfs_iget( | |||
406 | 429 | ||
407 | again: | 430 | again: |
408 | error = 0; | 431 | error = 0; |
409 | read_lock(&pag->pag_ici_lock); | 432 | rcu_read_lock(); |
410 | ip = radix_tree_lookup(&pag->pag_ici_root, agino); | 433 | ip = radix_tree_lookup(&pag->pag_ici_root, agino); |
411 | 434 | ||
412 | if (ip) { | 435 | if (ip) { |
413 | error = xfs_iget_cache_hit(pag, ip, flags, lock_flags); | 436 | error = xfs_iget_cache_hit(pag, ip, ino, flags, lock_flags); |
414 | if (error) | 437 | if (error) |
415 | goto out_error_or_again; | 438 | goto out_error_or_again; |
416 | } else { | 439 | } else { |
417 | read_unlock(&pag->pag_ici_lock); | 440 | rcu_read_unlock(); |
418 | XFS_STATS_INC(xs_ig_missed); | 441 | XFS_STATS_INC(xs_ig_missed); |
419 | 442 | ||
420 | error = xfs_iget_cache_miss(mp, pag, tp, ino, &ip, | 443 | error = xfs_iget_cache_miss(mp, pag, tp, ino, &ip, |