Skip to content

Commit 0f8ce49

Browse files
fdmananakdave
authored andcommitted
btrfs: avoid inode logging during rename and link when possible
During a rename or link operation, we need to determine if an inode was previously logged or not, and if it was, do some update to the logged inode. We used to rely exclusively on the logged_trans field of struct btrfs_inode to determine that, but that was not reliable because the value of that field is not persisted in the inode item, so it's lost when an inode is evicted and loaded back again. That led to several issues in the past, such as not persisting deletions (such as the case fixed by commit 803f0f6 ("Btrfs: fix fsync not persisting dentry deletions due to inode evictions")), or resulting in losing a file after an inode eviction followed by a rename (commit ecc64fa ("btrfs: fix lost inode on log replay after mix of fsync, rename and inode eviction")), besides other issues. So the inode_logged() helper was introduced and used to determine if an inode was possibly logged before in the current transaction, with the caveat that it could return false positives, in the sense that even if an inode was not logged before in the current transaction, it could still return true, but never to return false in case the inode was logged. >From a functional point of view that is fine, but from a performance perspective it can introduce significant latencies to rename and link operations, as they will end up doing inode logging even when it is not necessary. Recently on a 5.15 kernel, an openSUSE Tumbleweed user reported package installations and upgrades, with the zypper tool, were often taking a long time to complete. With strace it could be observed that zypper was spending about 99% of its time on rename operations, and then with further analysis we checked that directory logging was happening too frequently. Taking into account that installation/upgrade of some of the packages needed a few thousand file renames, the slowdown was very noticeable for the user. The issue was caused indirectly due to an excessive number of inode evictions on a 5.15 kernel, about 100x more compared to a 5.13, 5.14 or a 5.16-rc8 kernel. While triggering the inode evictions if something outside btrfs' control, btrfs could still behave better by eliminating the false positives from the inode_logged() helper. So change inode_logged() to actually eliminate such false positives caused by inode eviction and when an inode was never logged since the filesystem was mounted, as both cases relate to when the logged_trans field of struct btrfs_inode has a value of zero. When it can not determine if the inode was logged based only on the logged_trans value, lookup for the existence of the inode item in the log tree - if it's there then we known the inode was logged, if it's not there then it can not have been logged in the current transaction. Once we determine if the inode was logged, update the logged_trans value to avoid future calls to have to search in the log tree again. Alternatively, we could start storing logged_trans in the on disk inode item structure (struct btrfs_inode_item) in the unused space it still has, but that would be a bit odd because: 1) We only care about logged_trans since the filesystem was mounted, we don't care about its value from a previous mount. Having it persisted in the inode item structure would not make the best use of the precious unused space; 2) In order to get logged_trans persisted before inode eviction, we would have to update the delayed inode when we finish logging the inode and update its logged_trans in struct btrfs_inode, which makes it a bit cumbersome since we need to check if the delayed inode exists, if not create it and populate it and deal with any errors (-ENOMEM mostly). This change is part of a patchset comprised of the following patches: 1/5 btrfs: add helper to delete a dir entry from a log tree 2/5 btrfs: pass the dentry to btrfs_log_new_name() instead of the inode 3/5 btrfs: avoid logging all directory changes during renames 4/5 btrfs: stop doing unnecessary log updates during a rename 5/5 btrfs: avoid inode logging during rename and link when possible The following test script mimics part of what the zypper tool does during package installations/upgrades. It does not triggers inode evictions, but it's similar because it triggers false positives from the inode_logged() helper, because the inodes have a logged_trans of 0, there's a log tree due to a fsync of an unrelated file and the directory inode has its last_trans field set to the current transaction: $ cat test.sh #!/bin/bash DEV=/dev/nvme0n1 MNT=/mnt/nvme0n1 NUM_FILES=10000 mkfs.btrfs -f $DEV mount $DEV $MNT mkdir $MNT/testdir for ((i = 1; i <= $NUM_FILES; i++)); do echo -n > $MNT/testdir/file_$i done sync # Now do some change to an unrelated file and fsync it. # This is just to create a log tree to make sure that inode_logged() # does not return false when called against "testdir". xfs_io -f -c "pwrite 0 4K" -c "fsync" $MNT/foo # Do some change to testdir. This is to make sure inode_logged() # will return true when called against "testdir", because its # logged_trans is 0, it was changed in the current transaction # and there's a log tree. echo -n > $MNT/testdir/file_$((NUM_FILES + 1)) echo "Renaming $NUM_FILES files..." start=$(date +%s%N) for ((i = 1; i <= $NUM_FILES; i++)); do mv $MNT/testdir/file_$i $MNT/testdir/file_$i-RPMDELETE done end=$(date +%s%N) dur=$(( (end - start) / 1000000 )) echo "Renames took $dur milliseconds" umount $MNT Testing this change on a box using a non-debug kernel (Debian's default kernel config) gave the following results: NUM_FILES=10000, before patchset: 27837 ms NUM_FILES=10000, after patches 1/5 to 4/5 applied: 9236 ms (-66.8%) NUM_FILES=10000, after whole patchset applied: 8902 ms (-68.0%) NUM_FILES=5000, before patchset: 9127 ms NUM_FILES=5000, after patches 1/5 to 4/5 applied: 4640 ms (-49.2%) NUM_FILES=5000, after whole patchset applied: 4441 ms (-51.3%) NUM_FILES=2000, before patchset: 2528 ms NUM_FILES=2000, after patches 1/5 to 4/5 applied: 1983 ms (-21.6%) NUM_FILES=2000, after whole patchset applied: 1747 ms (-30.9%) NUM_FILES=1000, before patchset: 1085 ms NUM_FILES=1000, after patches 1/5 to 4/5 applied: 893 ms (-17.7%) NUM_FILES=1000, after whole patchset applied: 867 ms (-20.1%) Running dbench on the same physical machine with the following script: $ cat run-dbench.sh #!/bin/bash NUM_JOBS=$(nproc --all) DEV=/dev/nvme0n1 MNT=/mnt/nvme0n1 MOUNT_OPTIONS="-o ssd" MKFS_OPTIONS="-O no-holes -R free-space-tree" echo "performance" | \ tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor mkfs.btrfs -f $MKFS_OPTIONS $DEV mount $MOUNT_OPTIONS $DEV $MNT dbench -D $MNT -t 120 $NUM_JOBS umount $MNT Before patchset: Operation Count AvgLat MaxLat ---------------------------------------- NTCreateX 3761352 0.032 143.843 Close 2762770 0.002 2.273 Rename 159304 0.291 67.037 Unlink 759784 0.207 143.998 Deltree 72 4.028 15.977 Mkdir 36 0.003 0.006 Qpathinfo 3409780 0.013 9.678 Qfileinfo 596772 0.001 0.878 Qfsinfo 625189 0.003 1.245 Sfileinfo 306443 0.006 1.840 Find 1318106 0.063 19.798 WriteX 1871137 0.021 8.532 ReadX 5897325 0.003 3.567 LockX 12252 0.003 0.258 UnlockX 12252 0.002 0.100 Flush 263666 3.327 155.632 Throughput 980.047 MB/sec 12 clients 12 procs max_latency=155.636 ms After whole patchset applied: Operation Count AvgLat MaxLat ---------------------------------------- NTCreateX 4195584 0.033 107.742 Close 3081932 0.002 1.935 Rename 177641 0.218 14.905 Unlink 847333 0.166 107.822 Deltree 118 5.315 15.247 Mkdir 59 0.004 0.048 Qpathinfo 3802612 0.014 10.302 Qfileinfo 666748 0.001 1.034 Qfsinfo 697329 0.003 0.944 Sfileinfo 341712 0.006 2.099 Find 1470365 0.065 9.359 WriteX 2093921 0.021 8.087 ReadX 6576234 0.003 3.407 LockX 13660 0.003 0.308 UnlockX 13660 0.002 0.114 Flush 294090 2.906 115.539 Throughput 1093.11 MB/sec 12 clients 12 procs max_latency=115.544 ms +11.5% throughput -25.8% max latency rename max latency -77.8% Link: https://bugzilla.opensuse.org/show_bug.cgi?id=1193549 Signed-off-by: Filipe Manana <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 259c4b9 commit 0f8ce49

File tree

2 files changed

+183
-65
lines changed

2 files changed

+183
-65
lines changed

fs/btrfs/tree-log.c

Lines changed: 180 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3477,35 +3477,121 @@ int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
34773477
}
34783478

34793479
/*
3480-
* Check if an inode was logged in the current transaction. This may often
3481-
* return some false positives, because logged_trans is an in memory only field,
3482-
* not persisted anywhere. This is meant to be used in contexts where a false
3483-
* positive has no functional consequences.
3480+
* Check if an inode was logged in the current transaction. This correctly deals
3481+
* with the case where the inode was logged but has a logged_trans of 0, which
3482+
* happens if the inode is evicted and loaded again, as logged_trans is an in
3483+
* memory only field (not persisted).
3484+
*
3485+
* Returns 1 if the inode was logged before in the transaction, 0 if it was not,
3486+
* and < 0 on error.
34843487
*/
3485-
static bool inode_logged(struct btrfs_trans_handle *trans,
3486-
struct btrfs_inode *inode)
3488+
static int inode_logged(struct btrfs_trans_handle *trans,
3489+
struct btrfs_inode *inode,
3490+
struct btrfs_path *path_in)
34873491
{
3492+
struct btrfs_path *path = path_in;
3493+
struct btrfs_key key;
3494+
int ret;
3495+
34883496
if (inode->logged_trans == trans->transid)
3489-
return true;
3497+
return 1;
34903498

3491-
if (!test_bit(BTRFS_ROOT_HAS_LOG_TREE, &inode->root->state))
3492-
return false;
3499+
/*
3500+
* If logged_trans is not 0, then we know the inode logged was not logged
3501+
* in this transaction, so we can return false right away.
3502+
*/
3503+
if (inode->logged_trans > 0)
3504+
return 0;
3505+
3506+
/*
3507+
* If no log tree was created for this root in this transaction, then
3508+
* the inode can not have been logged in this transaction. In that case
3509+
* set logged_trans to anything greater than 0 and less than the current
3510+
* transaction's ID, to avoid the search below in a future call in case
3511+
* a log tree gets created after this.
3512+
*/
3513+
if (!test_bit(BTRFS_ROOT_HAS_LOG_TREE, &inode->root->state)) {
3514+
inode->logged_trans = trans->transid - 1;
3515+
return 0;
3516+
}
3517+
3518+
/*
3519+
* We have a log tree and the inode's logged_trans is 0. We can't tell
3520+
* for sure if the inode was logged before in this transaction by looking
3521+
* only at logged_trans. We could be pessimistic and assume it was, but
3522+
* that can lead to unnecessarily logging an inode during rename and link
3523+
* operations, and then further updating the log in followup rename and
3524+
* link operations, specially if it's a directory, which adds latency
3525+
* visible to applications doing a series of rename or link operations.
3526+
*
3527+
* A logged_trans of 0 here can mean several things:
3528+
*
3529+
* 1) The inode was never logged since the filesystem was mounted, and may
3530+
* or may have not been evicted and loaded again;
3531+
*
3532+
* 2) The inode was logged in a previous transaction, then evicted and
3533+
* then loaded again;
3534+
*
3535+
* 3) The inode was logged in the current transaction, then evicted and
3536+
* then loaded again.
3537+
*
3538+
* For cases 1) and 2) we don't want to return true, but we need to detect
3539+
* case 3) and return true. So we do a search in the log root for the inode
3540+
* item.
3541+
*/
3542+
key.objectid = btrfs_ino(inode);
3543+
key.type = BTRFS_INODE_ITEM_KEY;
3544+
key.offset = 0;
3545+
3546+
if (!path) {
3547+
path = btrfs_alloc_path();
3548+
if (!path)
3549+
return -ENOMEM;
3550+
}
3551+
3552+
ret = btrfs_search_slot(NULL, inode->root->log_root, &key, path, 0, 0);
3553+
3554+
if (path_in)
3555+
btrfs_release_path(path);
3556+
else
3557+
btrfs_free_path(path);
34933558

34943559
/*
3495-
* The inode's logged_trans is always 0 when we load it (because it is
3496-
* not persisted in the inode item or elsewhere). So if it is 0, the
3497-
* inode was last modified in the current transaction then the inode may
3498-
* have been logged before in the current transaction, then evicted and
3499-
* loaded again in the current transaction - or may have never been logged
3500-
* in the current transaction, but since we can not be sure, we have to
3501-
* assume it was, otherwise our callers can leave an inconsistent log.
3560+
* Logging an inode always results in logging its inode item. So if we
3561+
* did not find the item we know the inode was not logged for sure.
35023562
*/
3503-
if (inode->logged_trans == 0 &&
3504-
inode->last_trans == trans->transid &&
3505-
!test_bit(BTRFS_FS_LOG_RECOVERING, &trans->fs_info->flags))
3506-
return true;
3563+
if (ret < 0) {
3564+
return ret;
3565+
} else if (ret > 0) {
3566+
/*
3567+
* Set logged_trans to a value greater than 0 and less then the
3568+
* current transaction to avoid doing the search in future calls.
3569+
*/
3570+
inode->logged_trans = trans->transid - 1;
3571+
return 0;
3572+
}
3573+
3574+
/*
3575+
* The inode was previously logged and then evicted, set logged_trans to
3576+
* the current transacion's ID, to avoid future tree searches as long as
3577+
* the inode is not evicted again.
3578+
*/
3579+
inode->logged_trans = trans->transid;
3580+
3581+
/*
3582+
* If it's a directory, then we must set last_dir_index_offset to the
3583+
* maximum possible value, so that the next attempt to log the inode does
3584+
* not skip checking if dir index keys found in modified subvolume tree
3585+
* leaves have been logged before, otherwise it would result in attempts
3586+
* to insert duplicate dir index keys in the log tree. This must be done
3587+
* because last_dir_index_offset is an in-memory only field, not persisted
3588+
* in the inode item or any other on-disk structure, so its value is lost
3589+
* once the inode is evicted.
3590+
*/
3591+
if (S_ISDIR(inode->vfs_inode.i_mode))
3592+
inode->last_dir_index_offset = (u64)-1;
35073593

3508-
return false;
3594+
return 1;
35093595
}
35103596

35113597
/*
@@ -3572,8 +3658,13 @@ void btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
35723658
struct btrfs_path *path;
35733659
int ret;
35743660

3575-
if (!inode_logged(trans, dir))
3661+
ret = inode_logged(trans, dir, NULL);
3662+
if (ret == 0)
3663+
return;
3664+
else if (ret < 0) {
3665+
btrfs_set_log_full_commit(trans);
35763666
return;
3667+
}
35773668

35783669
ret = join_running_log_trans(root);
35793670
if (ret)
@@ -3607,8 +3698,13 @@ void btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
36073698
u64 index;
36083699
int ret;
36093700

3610-
if (!inode_logged(trans, inode))
3701+
ret = inode_logged(trans, inode, NULL);
3702+
if (ret == 0)
36113703
return;
3704+
else if (ret < 0) {
3705+
btrfs_set_log_full_commit(trans);
3706+
return;
3707+
}
36123708

36133709
ret = join_running_log_trans(root);
36143710
if (ret)
@@ -3740,7 +3836,6 @@ static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
37403836
struct extent_buffer *src = path->nodes[0];
37413837
const int nritems = btrfs_header_nritems(src);
37423838
const u64 ino = btrfs_ino(inode);
3743-
const bool inode_logged_before = inode_logged(trans, inode);
37443839
bool last_found = false;
37453840
int batch_start = 0;
37463841
int batch_size = 0;
@@ -3816,14 +3911,16 @@ static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
38163911
ctx->log_new_dentries = true;
38173912
}
38183913

3819-
if (!inode_logged_before)
3914+
if (!ctx->logged_before)
38203915
goto add_to_batch;
38213916

38223917
/*
38233918
* If we were logged before and have logged dir items, we can skip
38243919
* checking if any item with a key offset larger than the last one
38253920
* we logged is in the log tree, saving time and avoiding adding
3826-
* contention on the log tree.
3921+
* contention on the log tree. We can only rely on the value of
3922+
* last_dir_index_offset when we know for sure that the inode was
3923+
* previously logged in the current transaction.
38273924
*/
38283925
if (key.offset > inode->last_dir_index_offset)
38293926
goto add_to_batch;
@@ -4066,21 +4163,6 @@ static noinline int log_directory_changes(struct btrfs_trans_handle *trans,
40664163
u64 max_key;
40674164
int ret;
40684165

4069-
/*
4070-
* If this is the first time we are being logged in the current
4071-
* transaction, or we were logged before but the inode was evicted and
4072-
* reloaded later, in which case its logged_trans is 0, reset the value
4073-
* of the last logged key offset. Note that we don't use the helper
4074-
* function inode_logged() here - that is because the function returns
4075-
* true after an inode eviction, assuming the worst case as it can not
4076-
* know for sure if the inode was logged before. So we can not skip key
4077-
* searches in the case the inode was evicted, because it may not have
4078-
* been logged in this transaction and may have been logged in a past
4079-
* transaction, so we need to reset the last dir index offset to (u64)-1.
4080-
*/
4081-
if (inode->logged_trans != trans->transid)
4082-
inode->last_dir_index_offset = (u64)-1;
4083-
40844166
min_key = BTRFS_DIR_START_INDEX;
40854167
max_key = 0;
40864168
ctx->last_dir_item_offset = inode->last_dir_index_offset;
@@ -4117,9 +4199,6 @@ static int drop_inode_items(struct btrfs_trans_handle *trans,
41174199
struct btrfs_key found_key;
41184200
int start_slot;
41194201

4120-
if (!inode_logged(trans, inode))
4121-
return 0;
4122-
41234202
key.objectid = btrfs_ino(inode);
41244203
key.type = max_key_type;
41254204
key.offset = (u64)-1;
@@ -4617,7 +4696,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,
46174696
* are small, with a root at level 2 or 3 at most, due to their short
46184697
* life span.
46194698
*/
4620-
if (inode_logged(trans, inode)) {
4699+
if (ctx->logged_before) {
46214700
drop_args.path = path;
46224701
drop_args.start = em->start;
46234702
drop_args.end = em->start + em->len;
@@ -5633,6 +5712,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
56335712
bool xattrs_logged = false;
56345713
bool recursive_logging = false;
56355714
bool inode_item_dropped = true;
5715+
const bool orig_logged_before = ctx->logged_before;
56365716

56375717
path = btrfs_alloc_path();
56385718
if (!path)
@@ -5682,6 +5762,18 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
56825762
mutex_lock(&inode->log_mutex);
56835763
}
56845764

5765+
/*
5766+
* Before logging the inode item, cache the value returned by
5767+
* inode_logged(), because after that we have the need to figure out if
5768+
* the inode was previously logged in this transaction.
5769+
*/
5770+
ret = inode_logged(trans, inode, path);
5771+
if (ret < 0) {
5772+
err = ret;
5773+
goto out_unlock;
5774+
}
5775+
ctx->logged_before = (ret == 1);
5776+
56855777
/*
56865778
* This is for cases where logging a directory could result in losing a
56875779
* a file after replaying the log. For example, if we move a file from a
@@ -5707,9 +5799,11 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
57075799
clear_bit(BTRFS_INODE_COPY_EVERYTHING, &inode->runtime_flags);
57085800
if (inode_only == LOG_INODE_EXISTS)
57095801
max_key_type = BTRFS_XATTR_ITEM_KEY;
5710-
ret = drop_inode_items(trans, log, path, inode, max_key_type);
5802+
if (ctx->logged_before)
5803+
ret = drop_inode_items(trans, log, path, inode,
5804+
max_key_type);
57115805
} else {
5712-
if (inode_only == LOG_INODE_EXISTS && inode_logged(trans, inode)) {
5806+
if (inode_only == LOG_INODE_EXISTS && ctx->logged_before) {
57135807
/*
57145808
* Make sure the new inode item we write to the log has
57155809
* the same isize as the current one (if it exists).
@@ -5731,14 +5825,15 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
57315825
&inode->runtime_flags)) {
57325826
if (inode_only == LOG_INODE_EXISTS) {
57335827
max_key.type = BTRFS_XATTR_ITEM_KEY;
5734-
ret = drop_inode_items(trans, log, path, inode,
5735-
max_key.type);
5828+
if (ctx->logged_before)
5829+
ret = drop_inode_items(trans, log, path,
5830+
inode, max_key.type);
57365831
} else {
57375832
clear_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
57385833
&inode->runtime_flags);
57395834
clear_bit(BTRFS_INODE_COPY_EVERYTHING,
57405835
&inode->runtime_flags);
5741-
if (inode_logged(trans, inode))
5836+
if (ctx->logged_before)
57425837
ret = truncate_inode_items(trans, log,
57435838
inode, 0, 0);
57445839
}
@@ -5748,8 +5843,9 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
57485843
if (inode_only == LOG_INODE_ALL)
57495844
fast_search = true;
57505845
max_key.type = BTRFS_XATTR_ITEM_KEY;
5751-
ret = drop_inode_items(trans, log, path, inode,
5752-
max_key.type);
5846+
if (ctx->logged_before)
5847+
ret = drop_inode_items(trans, log, path, inode,
5848+
max_key.type);
57535849
} else {
57545850
if (inode_only == LOG_INODE_ALL)
57555851
fast_search = true;
@@ -5869,6 +5965,10 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
58695965
out:
58705966
btrfs_free_path(path);
58715967
btrfs_free_path(dst_path);
5968+
5969+
if (recursive_logging)
5970+
ctx->logged_before = orig_logged_before;
5971+
58725972
return err;
58735973
}
58745974

@@ -6813,7 +6913,7 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
68136913
struct btrfs_root *root = inode->root;
68146914
struct btrfs_log_ctx ctx;
68156915
bool log_pinned = false;
6816-
int ret = 0;
6916+
int ret;
68176917

68186918
/*
68196919
* this will force the logging code to walk the dentry chain
@@ -6826,9 +6926,24 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
68266926
* if this inode hasn't been logged and directory we're renaming it
68276927
* from hasn't been logged, we don't need to log it
68286928
*/
6829-
if (!inode_logged(trans, inode) &&
6830-
(!old_dir || !inode_logged(trans, old_dir)))
6831-
return;
6929+
ret = inode_logged(trans, inode, NULL);
6930+
if (ret < 0) {
6931+
goto out;
6932+
} else if (ret == 0) {
6933+
if (!old_dir)
6934+
return;
6935+
/*
6936+
* If the inode was not logged and we are doing a rename (old_dir is not
6937+
* NULL), check if old_dir was logged - if it was not we can return and
6938+
* do nothing.
6939+
*/
6940+
ret = inode_logged(trans, old_dir, NULL);
6941+
if (ret < 0)
6942+
goto out;
6943+
else if (ret == 0)
6944+
return;
6945+
}
6946+
ret = 0;
68326947

68336948
/*
68346949
* If we are doing a rename (old_dir is not NULL) from a directory that
@@ -6900,15 +7015,15 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
69007015
*/
69017016
btrfs_log_inode_parent(trans, inode, parent, LOG_INODE_EXISTS, &ctx);
69027017
out:
6903-
if (log_pinned) {
6904-
/*
6905-
* If an error happened mark the log for a full commit because
6906-
* it's not consistent and up to date. Do it before unpinning the
6907-
* log, to avoid any races with someone else trying to commit it.
6908-
*/
6909-
if (ret < 0)
6910-
btrfs_set_log_full_commit(trans);
7018+
/*
7019+
* If an error happened mark the log for a full commit because it's not
7020+
* consistent and up to date or we couldn't find out if one of the
7021+
* inodes was logged before in this transaction. Do it before unpinning
7022+
* the log, to avoid any races with someone else trying to commit it.
7023+
*/
7024+
if (ret < 0)
7025+
btrfs_set_log_full_commit(trans);
7026+
if (log_pinned)
69117027
btrfs_end_log_trans(root);
6912-
}
69137028
}
69147029

0 commit comments

Comments
 (0)