|
@@ -490,15 +490,16 @@ void btrfs_wait_logged_extents(struct btrfs_trans_handle *trans,
|
|
|
|
|
|
spin_lock_irq(&log->log_extents_lock[index]);
|
|
|
while (!list_empty(&log->logged_list[index])) {
|
|
|
+ struct inode *inode;
|
|
|
ordered = list_first_entry(&log->logged_list[index],
|
|
|
struct btrfs_ordered_extent,
|
|
|
log_list);
|
|
|
list_del_init(&ordered->log_list);
|
|
|
+ inode = ordered->inode;
|
|
|
spin_unlock_irq(&log->log_extents_lock[index]);
|
|
|
|
|
|
if (!test_bit(BTRFS_ORDERED_IO_DONE, &ordered->flags) &&
|
|
|
!test_bit(BTRFS_ORDERED_DIRECT, &ordered->flags)) {
|
|
|
- struct inode *inode = ordered->inode;
|
|
|
u64 start = ordered->file_offset;
|
|
|
u64 end = ordered->file_offset + ordered->len - 1;
|
|
|
|
|
@@ -509,20 +510,25 @@ void btrfs_wait_logged_extents(struct btrfs_trans_handle *trans,
|
|
|
&ordered->flags));
|
|
|
|
|
|
/*
|
|
|
- * If our ordered extent completed it means it updated the
|
|
|
- * fs/subvol and csum trees already, so no need to make the
|
|
|
- * current transaction's commit wait for it, as we end up
|
|
|
- * holding memory unnecessarily and delaying the inode's iput
|
|
|
- * until the transaction commit (we schedule an iput for the
|
|
|
- * inode when the ordered extent's refcount drops to 0), which
|
|
|
- * prevents it from being evictable until the transaction
|
|
|
- * commits.
|
|
|
+ * In order to keep us from losing our ordered extent
|
|
|
+ * information when committing the transaction we have to make
|
|
|
+ * sure that any logged extents are completed when we go to
|
|
|
+ * commit the transaction. To do this we simply increase the
|
|
|
+ * current transactions pending_ordered counter and decrement it
|
|
|
+ * when the ordered extent completes.
|
|
|
*/
|
|
|
- if (test_bit(BTRFS_ORDERED_COMPLETE, &ordered->flags))
|
|
|
- btrfs_put_ordered_extent(ordered);
|
|
|
- else
|
|
|
- list_add_tail(&ordered->trans_list, &trans->ordered);
|
|
|
-
|
|
|
+ if (!test_bit(BTRFS_ORDERED_COMPLETE, &ordered->flags)) {
|
|
|
+ struct btrfs_ordered_inode_tree *tree;
|
|
|
+
|
|
|
+ tree = &BTRFS_I(inode)->ordered_tree;
|
|
|
+ spin_lock_irq(&tree->lock);
|
|
|
+ if (!test_bit(BTRFS_ORDERED_COMPLETE, &ordered->flags)) {
|
|
|
+ set_bit(BTRFS_ORDERED_PENDING, &ordered->flags);
|
|
|
+ atomic_inc(&trans->transaction->pending_ordered);
|
|
|
+ }
|
|
|
+ spin_unlock_irq(&tree->lock);
|
|
|
+ }
|
|
|
+ btrfs_put_ordered_extent(ordered);
|
|
|
spin_lock_irq(&log->log_extents_lock[index]);
|
|
|
}
|
|
|
spin_unlock_irq(&log->log_extents_lock[index]);
|
|
@@ -584,6 +590,7 @@ void btrfs_remove_ordered_extent(struct inode *inode,
|
|
|
struct btrfs_ordered_inode_tree *tree;
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
struct rb_node *node;
|
|
|
+ bool dec_pending_ordered = false;
|
|
|
|
|
|
tree = &BTRFS_I(inode)->ordered_tree;
|
|
|
spin_lock_irq(&tree->lock);
|
|
@@ -593,8 +600,37 @@ void btrfs_remove_ordered_extent(struct inode *inode,
|
|
|
if (tree->last == node)
|
|
|
tree->last = NULL;
|
|
|
set_bit(BTRFS_ORDERED_COMPLETE, &entry->flags);
|
|
|
+ if (test_and_clear_bit(BTRFS_ORDERED_PENDING, &entry->flags))
|
|
|
+ dec_pending_ordered = true;
|
|
|
spin_unlock_irq(&tree->lock);
|
|
|
|
|
|
+ /*
|
|
|
+ * The current running transaction is waiting on us, we need to let it
|
|
|
+ * know that we're complete and wake it up.
|
|
|
+ */
|
|
|
+ if (dec_pending_ordered) {
|
|
|
+ struct btrfs_transaction *trans;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The checks for trans are just a formality, it should be set,
|
|
|
+ * but if it isn't we don't want to deref/assert under the spin
|
|
|
+ * lock, so be nice and check if trans is set, but ASSERT() so
|
|
|
+ * if it isn't set a developer will notice.
|
|
|
+ */
|
|
|
+ spin_lock(&root->fs_info->trans_lock);
|
|
|
+ trans = root->fs_info->running_transaction;
|
|
|
+ if (trans)
|
|
|
+ atomic_inc(&trans->use_count);
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
|
+
|
|
|
+ ASSERT(trans);
|
|
|
+ if (trans) {
|
|
|
+ if (atomic_dec_and_test(&trans->pending_ordered))
|
|
|
+ wake_up(&trans->pending_wait);
|
|
|
+ btrfs_put_transaction(trans);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
spin_lock(&root->ordered_extent_lock);
|
|
|
list_del_init(&entry->root_extent_list);
|
|
|
root->nr_ordered_extents--;
|