diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r-- | fs/btrfs/tree-log.c | 40 |
1 files changed, 37 insertions, 3 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 3fc8d854d7fb..6c8297bcfeb7 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c | |||
@@ -3323,6 +3323,30 @@ int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans, | |||
3323 | } | 3323 | } |
3324 | 3324 | ||
3325 | /* | 3325 | /* |
3326 | * Check if an inode was logged in the current transaction. We can't always rely | ||
3327 | * on an inode's logged_trans value, because it's an in-memory only field and | ||
3328 | * therefore not persisted. This means that its value is lost if the inode gets | ||
3329 | * evicted and loaded again from disk (in which case it has a value of 0, and | ||
3330 | * certainly it is smaller then any possible transaction ID), when that happens | ||
3331 | * the full_sync flag is set in the inode's runtime flags, so on that case we | ||
3332 | * assume eviction happened and ignore the logged_trans value, assuming the | ||
3333 | * worst case, that the inode was logged before in the current transaction. | ||
3334 | */ | ||
3335 | static bool inode_logged(struct btrfs_trans_handle *trans, | ||
3336 | struct btrfs_inode *inode) | ||
3337 | { | ||
3338 | if (inode->logged_trans == trans->transid) | ||
3339 | return true; | ||
3340 | |||
3341 | if (inode->last_trans == trans->transid && | ||
3342 | test_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &inode->runtime_flags) && | ||
3343 | !test_bit(BTRFS_FS_LOG_RECOVERING, &trans->fs_info->flags)) | ||
3344 | return true; | ||
3345 | |||
3346 | return false; | ||
3347 | } | ||
3348 | |||
3349 | /* | ||
3326 | * If both a file and directory are logged, and unlinks or renames are | 3350 | * If both a file and directory are logged, and unlinks or renames are |
3327 | * mixed in, we have a few interesting corners: | 3351 | * mixed in, we have a few interesting corners: |
3328 | * | 3352 | * |
@@ -3356,7 +3380,7 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans, | |||
3356 | int bytes_del = 0; | 3380 | int bytes_del = 0; |
3357 | u64 dir_ino = btrfs_ino(dir); | 3381 | u64 dir_ino = btrfs_ino(dir); |
3358 | 3382 | ||
3359 | if (dir->logged_trans < trans->transid) | 3383 | if (!inode_logged(trans, dir)) |
3360 | return 0; | 3384 | return 0; |
3361 | 3385 | ||
3362 | ret = join_running_log_trans(root); | 3386 | ret = join_running_log_trans(root); |
@@ -3460,7 +3484,7 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans, | |||
3460 | u64 index; | 3484 | u64 index; |
3461 | int ret; | 3485 | int ret; |
3462 | 3486 | ||
3463 | if (inode->logged_trans < trans->transid) | 3487 | if (!inode_logged(trans, inode)) |
3464 | return 0; | 3488 | return 0; |
3465 | 3489 | ||
3466 | ret = join_running_log_trans(root); | 3490 | ret = join_running_log_trans(root); |
@@ -5420,9 +5444,19 @@ log_extents: | |||
5420 | } | 5444 | } |
5421 | } | 5445 | } |
5422 | 5446 | ||
5447 | /* | ||
5448 | * Don't update last_log_commit if we logged that an inode exists after | ||
5449 | * it was loaded to memory (full_sync bit set). | ||
5450 | * This is to prevent data loss when we do a write to the inode, then | ||
5451 | * the inode gets evicted after all delalloc was flushed, then we log | ||
5452 | * it exists (due to a rename for example) and then fsync it. This last | ||
5453 | * fsync would do nothing (not logging the extents previously written). | ||
5454 | */ | ||
5423 | spin_lock(&inode->lock); | 5455 | spin_lock(&inode->lock); |
5424 | inode->logged_trans = trans->transid; | 5456 | inode->logged_trans = trans->transid; |
5425 | inode->last_log_commit = inode->last_sub_trans; | 5457 | if (inode_only != LOG_INODE_EXISTS || |
5458 | !test_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &inode->runtime_flags)) | ||
5459 | inode->last_log_commit = inode->last_sub_trans; | ||
5426 | spin_unlock(&inode->lock); | 5460 | spin_unlock(&inode->lock); |
5427 | out_unlock: | 5461 | out_unlock: |
5428 | mutex_unlock(&inode->log_mutex); | 5462 | mutex_unlock(&inode->log_mutex); |