|
@@ -305,7 +305,6 @@ static void nilfs_transaction_lock(struct super_block *sb,
|
|
|
ti->ti_count = 0;
|
|
|
ti->ti_save = cur_ti;
|
|
|
ti->ti_magic = NILFS_TI_MAGIC;
|
|
|
- INIT_LIST_HEAD(&ti->ti_garbage);
|
|
|
current->journal_info = ti;
|
|
|
|
|
|
for (;;) {
|
|
@@ -332,8 +331,6 @@ static void nilfs_transaction_unlock(struct super_block *sb)
|
|
|
|
|
|
up_write(&nilfs->ns_segctor_sem);
|
|
|
current->journal_info = ti->ti_save;
|
|
|
- if (!list_empty(&ti->ti_garbage))
|
|
|
- nilfs_dispose_list(nilfs, &ti->ti_garbage, 0);
|
|
|
}
|
|
|
|
|
|
static void *nilfs_segctor_map_segsum_entry(struct nilfs_sc_info *sci,
|
|
@@ -746,6 +743,15 @@ static void nilfs_dispose_list(struct the_nilfs *nilfs,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void nilfs_iput_work_func(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct nilfs_sc_info *sci = container_of(work, struct nilfs_sc_info,
|
|
|
+ sc_iput_work);
|
|
|
+ struct the_nilfs *nilfs = sci->sc_super->s_fs_info;
|
|
|
+
|
|
|
+ nilfs_dispose_list(nilfs, &sci->sc_iput_queue, 0);
|
|
|
+}
|
|
|
+
|
|
|
static int nilfs_test_metadata_dirty(struct the_nilfs *nilfs,
|
|
|
struct nilfs_root *root)
|
|
|
{
|
|
@@ -1900,8 +1906,8 @@ static int nilfs_segctor_collect_dirty_files(struct nilfs_sc_info *sci,
|
|
|
static void nilfs_segctor_drop_written_files(struct nilfs_sc_info *sci,
|
|
|
struct the_nilfs *nilfs)
|
|
|
{
|
|
|
- struct nilfs_transaction_info *ti = current->journal_info;
|
|
|
struct nilfs_inode_info *ii, *n;
|
|
|
+ int defer_iput = false;
|
|
|
|
|
|
spin_lock(&nilfs->ns_inode_lock);
|
|
|
list_for_each_entry_safe(ii, n, &sci->sc_dirty_files, i_dirty) {
|
|
@@ -1912,9 +1918,24 @@ static void nilfs_segctor_drop_written_files(struct nilfs_sc_info *sci,
|
|
|
clear_bit(NILFS_I_BUSY, &ii->i_state);
|
|
|
brelse(ii->i_bh);
|
|
|
ii->i_bh = NULL;
|
|
|
- list_move_tail(&ii->i_dirty, &ti->ti_garbage);
|
|
|
+ list_del_init(&ii->i_dirty);
|
|
|
+ if (!ii->vfs_inode.i_nlink) {
|
|
|
+ /*
|
|
|
+ * Defer calling iput() to avoid a deadlock
|
|
|
+ * over I_SYNC flag for inodes with i_nlink == 0
|
|
|
+ */
|
|
|
+ list_add_tail(&ii->i_dirty, &sci->sc_iput_queue);
|
|
|
+ defer_iput = true;
|
|
|
+ } else {
|
|
|
+ spin_unlock(&nilfs->ns_inode_lock);
|
|
|
+ iput(&ii->vfs_inode);
|
|
|
+ spin_lock(&nilfs->ns_inode_lock);
|
|
|
+ }
|
|
|
}
|
|
|
spin_unlock(&nilfs->ns_inode_lock);
|
|
|
+
|
|
|
+ if (defer_iput)
|
|
|
+ schedule_work(&sci->sc_iput_work);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -2583,6 +2604,8 @@ static struct nilfs_sc_info *nilfs_segctor_new(struct super_block *sb,
|
|
|
INIT_LIST_HEAD(&sci->sc_segbufs);
|
|
|
INIT_LIST_HEAD(&sci->sc_write_logs);
|
|
|
INIT_LIST_HEAD(&sci->sc_gc_inodes);
|
|
|
+ INIT_LIST_HEAD(&sci->sc_iput_queue);
|
|
|
+ INIT_WORK(&sci->sc_iput_work, nilfs_iput_work_func);
|
|
|
init_timer(&sci->sc_timer);
|
|
|
|
|
|
sci->sc_interval = HZ * NILFS_SC_DEFAULT_TIMEOUT;
|
|
@@ -2609,6 +2632,8 @@ static void nilfs_segctor_write_out(struct nilfs_sc_info *sci)
|
|
|
ret = nilfs_segctor_construct(sci, SC_LSEG_SR);
|
|
|
nilfs_transaction_unlock(sci->sc_super);
|
|
|
|
|
|
+ flush_work(&sci->sc_iput_work);
|
|
|
+
|
|
|
} while (ret && retrycount-- > 0);
|
|
|
}
|
|
|
|
|
@@ -2633,6 +2658,9 @@ static void nilfs_segctor_destroy(struct nilfs_sc_info *sci)
|
|
|
|| sci->sc_seq_request != sci->sc_seq_done);
|
|
|
spin_unlock(&sci->sc_state_lock);
|
|
|
|
|
|
+ if (flush_work(&sci->sc_iput_work))
|
|
|
+ flag = true;
|
|
|
+
|
|
|
if (flag || !nilfs_segctor_confirm(sci))
|
|
|
nilfs_segctor_write_out(sci);
|
|
|
|
|
@@ -2642,6 +2670,12 @@ static void nilfs_segctor_destroy(struct nilfs_sc_info *sci)
|
|
|
nilfs_dispose_list(nilfs, &sci->sc_dirty_files, 1);
|
|
|
}
|
|
|
|
|
|
+ if (!list_empty(&sci->sc_iput_queue)) {
|
|
|
+ nilfs_warning(sci->sc_super, __func__,
|
|
|
+ "iput queue is not empty\n");
|
|
|
+ nilfs_dispose_list(nilfs, &sci->sc_iput_queue, 1);
|
|
|
+ }
|
|
|
+
|
|
|
WARN_ON(!list_empty(&sci->sc_segbufs));
|
|
|
WARN_ON(!list_empty(&sci->sc_write_logs));
|
|
|
|