aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>2014-03-25 22:55:26 +0900
committerDaniel Phillips <daniel@tux3.org>2014-03-25 22:55:26 +0900
commit9d190848f26c1b07332d9872cf973f09bf2746f7 (patch)
tree4d0ee0d7e723d23e7ed9de2f8f169a658bb96701
parent9b8da2c37b75acb68de614ce9a0340303db92226 (diff)
downloadlinux-tux3-9d190848f26c1b07332d9872cf973f09bf2746f7.tar.gz
tux3: Fix deferred inode deletion locking
We defer inode deletion from itree and in-core inode destroy. To defer, we dirty inode when inode refcount became 0. But, iput_final() is not expecting to dirty inode. So, for now, we are using a dirty way to defer. I.e. set dirty inode and return false from ->drop_inode(). (this is fragile, and can have issue) Well, so, to dirty inode, we want to call mark_inode_dirty(). But mark_inode_dirty() is not callable from iput_final() (by reason of locking order). To workaround this locking order, we unlock inode->i_lock temporary. But unlocking inode->i_lock makes chance to freeing inode again by cache reclaim. To avoid, cache reclaimer frees the inode, we set I_WILL_FREE before drop inode->i_lock. [FIXME: - ->drop_inode() can't prevent to be freed while umount. So, if vfs (like fsnotify) is taking refcount of inode while umount, inodes may not be flushed by last sync_system(), and we may have problem. - I_WILL_FREE affects to igrab which we don't want to affect.] Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
-rw-r--r--fs/tux3/writeback_inodedelete.c45
1 files changed, 30 insertions, 15 deletions
diff --git a/fs/tux3/writeback_inodedelete.c b/fs/tux3/writeback_inodedelete.c
index 87aa176534aaef..b14523bde51ed7 100644
--- a/fs/tux3/writeback_inodedelete.c
+++ b/fs/tux3/writeback_inodedelete.c
@@ -22,10 +22,7 @@ static int tux3_inode_is_dead(struct tux3_inode *tuxnode)
(tuxnode->flags & TUX3_INODE_DEAD);
}
-/*
- * Mark inode dirty to delete. (called from ->drop_inode()).
- * Caller must hold inode->i_lock.
- */
+/* Mark inode dirty to delete. (called from ->drop_inode()). */
static void __tux3_mark_inode_to_delete(struct inode *inode, unsigned delta)
{
struct tux3_inode *tuxnode = tux_inode(inode);
@@ -39,19 +36,12 @@ static void __tux3_mark_inode_to_delete(struct inode *inode, unsigned delta)
/* Mark inode dirty to delete on this delta */
tuxnode->flags |= tux3_deadsta_delta(delta);
spin_unlock(&tuxnode->lock);
-
- /*
- * Tell dead inode to backend by marking as dirty.
- *
- * Hack: this is called under inode->i_lock. So, we call
- * internal ->dirty_inode(), and change inode->i_flags here
- * directly.
- */
- tux3_dirty_inode(inode, I_DIRTY_SYNC);
- inode->i_state |= I_DIRTY_SYNC;
- /* FIXME: we should wake up flusher if inode was clean */
}
+/*
+ * Mark inode dirty to delete. (called from ->drop_inode()).
+ * Caller must hold inode->i_lock.
+ */
void tux3_mark_inode_to_delete(struct inode *inode)
{
struct sb *sb = tux_sb(inode->i_sb);
@@ -67,6 +57,31 @@ void tux3_mark_inode_to_delete(struct inode *inode)
delta = tux3_inode_delta(inode);
__tux3_mark_inode_to_delete(inode, delta);
+ /*
+ * Hack: this is called under inode->i_lock. So, we have to
+ * release inode->i_lock to call mark_inode_dirty_sync().
+ *
+ * FIXME: we want to set I_DIRTY_SYNC (I_DIRTY_SYNC will
+ * prevent the indo is freed) and wakeup flusher if need,
+ * while preventing inode is freed. Need better way to do.
+ */
+ if (!(tux3_dirty_flags(inode, delta) & I_DIRTY_SYNC)) {
+ /* FIXME: I_REFERENCED can't prevent completely */
+ //inode->i_state |= I_REFERENCED;
+ /* FIXME: I_WILL_FREE will bother igrab() grabs reference */
+ inode->i_state |= I_WILL_FREE;
+ spin_unlock(&inode->i_lock);
+
+ /* Tell dead inode to backend by marking as dirty. */
+ tux3_mark_inode_dirty_sync(inode);
+
+ spin_lock(&inode->i_lock);
+ inode->i_state &= ~I_WILL_FREE;
+#ifdef __KERNEL__
+ wake_up_bit(&inode->i_state, __I_NEW);
+#endif
+ }
+
change_end_atomic(sb);
}