summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Lord <lord@sgi.com>2003-06-19 04:22:47 -0500
committerStephen Lord <lord@sgi.com>2003-06-19 04:22:47 -0500
commitcecd52a72d18334f3d417f92828b8b78e72d0a21 (patch)
tree291cc4eaccf9f1db86e119e134d107195499abd5
parentf8b4e5fa1b07e6dbdde7cd4b3ffd7ec0c3f60158 (diff)
[XFS] Fix deadlock between xfs_finish_reclaim and xfs_iget_core. An inode being
reclaimed and removed from memory by one thread while another thread is attempting to reuse the inode and bring it back to life. There was a window between the iget starting to reuse the inode and the reclaim starting. Close the window by marking the inode as being reused under the hash lock, and by abandoning the reclaim if this is detected when it obtains the hash lock. SGI Modid: 2.5.x-xfs:slinx:151123a
-rw-r--r--fs/xfs/xfs_iget.c10
-rw-r--r--fs/xfs/xfs_inode.h1
-rw-r--r--fs/xfs/xfs_vnodeops.c25
3 files changed, 25 insertions, 11 deletions
diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c
index be218976502e..267d2f95211c 100644
--- a/fs/xfs/xfs_iget.c
+++ b/fs/xfs/xfs_iget.c
@@ -214,7 +214,13 @@ again:
XFS_STATS_INC(xfsstats.xs_ig_found);
+ ip->i_flags &= ~XFS_IRECLAIMABLE;
read_unlock(&ih->ih_lock);
+
+ XFS_MOUNT_ILOCK(mp);
+ list_del_init(&ip->i_reclaim);
+ XFS_MOUNT_IUNLOCK(mp);
+
goto finish_inode;
} else if (vp != inode_vp) {
@@ -253,10 +259,6 @@ finish_inode:
xfs_iocore_inode_reinit(ip);
}
- XFS_MOUNT_ILOCK(mp);
- list_del_init(&ip->i_reclaim);
- XFS_MOUNT_IUNLOCK(mp);
-
vn_trace_exit(vp, "xfs_iget.found",
(inst_t *)__return_address);
goto return_ip;
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index 614ddb6d2018..be2c12208fc6 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -362,6 +362,7 @@ void xfs_ifork_next_set(xfs_inode_t *ip, int w, int n);
#define XFS_IUIOSZ 0x0002 /* inode i/o sizes have been explicitly set */
#define XFS_IQUIESCE 0x0004 /* we have started quiescing for this inode */
#define XFS_IRECLAIM 0x0008 /* we have started reclaiming this inode */
+#define XFS_IRECLAIMABLE 0x0010 /* inode can be reclaimed */
/*
* Flags for inode locking.
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
index 2bb9ee6d334f..b51c355ebd88 100644
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -3930,6 +3930,7 @@ xfs_reclaim(
*/
if (!ip->i_update_core && (ip->i_itemp == NULL)) {
xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_iflock(ip);
return xfs_finish_reclaim(ip, 1, XFS_IFLUSH_DELWRI_ELSE_SYNC);
} else {
xfs_mount_t *mp = ip->i_mount;
@@ -3938,7 +3939,7 @@ xfs_reclaim(
XFS_MOUNT_ILOCK(mp);
vn_bhv_remove(VN_BHV_HEAD(vp), XFS_ITOBHV(ip));
list_add_tail(&ip->i_reclaim, &mp->m_del_inodes);
-
+ ip->i_flags |= XFS_IRECLAIMABLE;
XFS_MOUNT_IUNLOCK(mp);
}
return 0;
@@ -3953,19 +3954,20 @@ xfs_finish_reclaim(
xfs_ihash_t *ih = ip->i_hash;
int error;
- if (!locked)
- xfs_ilock(ip, XFS_ILOCK_EXCL);
-
/* The hash lock here protects a thread in xfs_iget_core from
* racing with us on linking the inode back with a vnode.
* Once we have the XFS_IRECLAIM flag set it will not touch
* us.
*/
write_lock(&ih->ih_lock);
- if (ip->i_flags & XFS_IRECLAIM || (!locked && XFS_ITOV_NULL(ip))) {
+ if ((ip->i_flags & XFS_IRECLAIM) ||
+ (!(ip->i_flags & XFS_IRECLAIMABLE) &&
+ (XFS_ITOV_NULL(ip) == NULL))) {
write_unlock(&ih->ih_lock);
- if (!locked)
+ if (locked) {
+ xfs_ifunlock(ip);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ }
return(1);
}
ip->i_flags |= XFS_IRECLAIM;
@@ -3984,6 +3986,7 @@ xfs_finish_reclaim(
*/
if (!XFS_FORCED_SHUTDOWN(ip->i_mount)) {
if (!locked) {
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_iflock(ip);
}
@@ -4007,8 +4010,16 @@ xfs_finish_reclaim(
ASSERT(ip->i_update_core == 0);
ASSERT(ip->i_itemp == NULL ||
ip->i_itemp->ili_format.ilf_fields == 0);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ } else if (locked) {
+ /*
+ * We are not interested in doing an iflush if we're
+ * in the process of shutting down the filesystem forcibly.
+ * So, just reclaim the inode.
+ */
+ xfs_ifunlock(ip);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
}
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_ireclaim(ip);
return 0;