Skip to content

Commit 8b050d3

Browse files
Miao XieJosef Bacik
authored andcommitted
Btrfs: fix skipped error handle when log sync failed
It is possible that many tasks sync the log tree at the same time, but only one task can do the sync work, the others will wait for it. But those wait tasks didn't get the result of the log sync, and returned 0 when they ended the wait. It caused those tasks skipped the error handle, and the serious problem was they told the users the file sync succeeded but in fact they failed. This patch fixes this problem by introducing a log context structure, we insert it into the a global list. When the sync fails, we will set the error number of every log context in the list, then the waiting tasks get the error number of the log context and handle the error if need. Signed-off-by: Miao Xie <[email protected]> Signed-off-by: Josef Bacik <[email protected]>
1 parent bb14a59 commit 8b050d3

File tree

5 files changed

+111
-31
lines changed

5 files changed

+111
-31
lines changed

fs/btrfs/ctree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,6 +1718,7 @@ struct btrfs_root {
17181718
struct mutex log_mutex;
17191719
wait_queue_head_t log_writer_wait;
17201720
wait_queue_head_t log_commit_wait[2];
1721+
struct list_head log_ctxs[2];
17211722
atomic_t log_writers;
17221723
atomic_t log_commit[2];
17231724
atomic_t log_batch;

fs/btrfs/disk-io.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,8 @@ static void __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
12001200
init_waitqueue_head(&root->log_writer_wait);
12011201
init_waitqueue_head(&root->log_commit_wait[0]);
12021202
init_waitqueue_head(&root->log_commit_wait[1]);
1203+
INIT_LIST_HEAD(&root->log_ctxs[0]);
1204+
INIT_LIST_HEAD(&root->log_ctxs[1]);
12031205
atomic_set(&root->log_commit[0], 0);
12041206
atomic_set(&root->log_commit[1], 0);
12051207
atomic_set(&root->log_writers, 0);

fs/btrfs/file.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,8 +1864,9 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
18641864
struct dentry *dentry = file->f_path.dentry;
18651865
struct inode *inode = dentry->d_inode;
18661866
struct btrfs_root *root = BTRFS_I(inode)->root;
1867-
int ret = 0;
18681867
struct btrfs_trans_handle *trans;
1868+
struct btrfs_log_ctx ctx;
1869+
int ret = 0;
18691870
bool full_sync = 0;
18701871

18711872
trace_btrfs_sync_file(file, datasync);
@@ -1959,7 +1960,9 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
19591960
}
19601961
trans->sync = true;
19611962

1962-
ret = btrfs_log_dentry_safe(trans, root, dentry);
1963+
btrfs_init_log_ctx(&ctx);
1964+
1965+
ret = btrfs_log_dentry_safe(trans, root, dentry, &ctx);
19631966
if (ret < 0) {
19641967
/* Fallthrough and commit/free transaction. */
19651968
ret = 1;
@@ -1979,7 +1982,7 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
19791982

19801983
if (ret != BTRFS_NO_LOG_SYNC) {
19811984
if (!ret) {
1982-
ret = btrfs_sync_log(trans, root);
1985+
ret = btrfs_sync_log(trans, root, &ctx);
19831986
if (!ret) {
19841987
ret = btrfs_end_transaction(trans, root);
19851988
goto out;

fs/btrfs/tree-log.c

Lines changed: 88 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,10 @@ static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans,
136136
* syncing the tree wait for us to finish
137137
*/
138138
static int start_log_trans(struct btrfs_trans_handle *trans,
139-
struct btrfs_root *root)
139+
struct btrfs_root *root,
140+
struct btrfs_log_ctx *ctx)
140141
{
142+
int index;
141143
int ret;
142144

143145
mutex_lock(&root->log_mutex);
@@ -151,6 +153,10 @@ static int start_log_trans(struct btrfs_trans_handle *trans,
151153

152154
atomic_inc(&root->log_batch);
153155
atomic_inc(&root->log_writers);
156+
if (ctx) {
157+
index = root->log_transid % 2;
158+
list_add_tail(&ctx->list, &root->log_ctxs[index]);
159+
}
154160
mutex_unlock(&root->log_mutex);
155161
return 0;
156162
}
@@ -172,6 +178,10 @@ static int start_log_trans(struct btrfs_trans_handle *trans,
172178
root->log_start_pid = current->pid;
173179
atomic_inc(&root->log_batch);
174180
atomic_inc(&root->log_writers);
181+
if (ctx) {
182+
index = root->log_transid % 2;
183+
list_add_tail(&ctx->list, &root->log_ctxs[index]);
184+
}
175185
out:
176186
mutex_unlock(&root->log_mutex);
177187
return ret;
@@ -2361,25 +2371,18 @@ static int update_log_root(struct btrfs_trans_handle *trans,
23612371
return ret;
23622372
}
23632373

2364-
static int wait_log_commit(struct btrfs_trans_handle *trans,
2365-
struct btrfs_root *root, int transid)
2374+
static void wait_log_commit(struct btrfs_trans_handle *trans,
2375+
struct btrfs_root *root, int transid)
23662376
{
23672377
DEFINE_WAIT(wait);
23682378
int index = transid % 2;
2369-
int ret = 0;
23702379

23712380
/*
23722381
* we only allow two pending log transactions at a time,
23732382
* so we know that if ours is more than 2 older than the
23742383
* current transaction, we're done
23752384
*/
23762385
do {
2377-
if (ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) ==
2378-
trans->transid) {
2379-
ret = -EAGAIN;
2380-
break;
2381-
}
2382-
23832386
prepare_to_wait(&root->log_commit_wait[index],
23842387
&wait, TASK_UNINTERRUPTIBLE);
23852388
mutex_unlock(&root->log_mutex);
@@ -2392,27 +2395,55 @@ static int wait_log_commit(struct btrfs_trans_handle *trans,
23922395
mutex_lock(&root->log_mutex);
23932396
} while (root->log_transid < transid + 2 &&
23942397
atomic_read(&root->log_commit[index]));
2395-
2396-
return ret;
23972398
}
23982399

23992400
static void wait_for_writer(struct btrfs_trans_handle *trans,
24002401
struct btrfs_root *root)
24012402
{
24022403
DEFINE_WAIT(wait);
2403-
while (ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) !=
2404-
trans->transid && atomic_read(&root->log_writers)) {
2404+
2405+
while (atomic_read(&root->log_writers)) {
24052406
prepare_to_wait(&root->log_writer_wait,
24062407
&wait, TASK_UNINTERRUPTIBLE);
24072408
mutex_unlock(&root->log_mutex);
2408-
if (ACCESS_ONCE(root->fs_info->last_trans_log_full_commit) !=
2409-
trans->transid && atomic_read(&root->log_writers))
2409+
if (atomic_read(&root->log_writers))
24102410
schedule();
24112411
mutex_lock(&root->log_mutex);
24122412
finish_wait(&root->log_writer_wait, &wait);
24132413
}
24142414
}
24152415

2416+
static inline void btrfs_remove_log_ctx(struct btrfs_root *root,
2417+
struct btrfs_log_ctx *ctx)
2418+
{
2419+
if (!ctx)
2420+
return;
2421+
2422+
mutex_lock(&root->log_mutex);
2423+
list_del_init(&ctx->list);
2424+
mutex_unlock(&root->log_mutex);
2425+
}
2426+
2427+
/*
2428+
* Invoked in log mutex context, or be sure there is no other task which
2429+
* can access the list.
2430+
*/
2431+
static inline void btrfs_remove_all_log_ctxs(struct btrfs_root *root,
2432+
int index, int error)
2433+
{
2434+
struct btrfs_log_ctx *ctx;
2435+
2436+
if (!error) {
2437+
INIT_LIST_HEAD(&root->log_ctxs[index]);
2438+
return;
2439+
}
2440+
2441+
list_for_each_entry(ctx, &root->log_ctxs[index], list)
2442+
ctx->log_ret = error;
2443+
2444+
INIT_LIST_HEAD(&root->log_ctxs[index]);
2445+
}
2446+
24162447
/*
24172448
* btrfs_sync_log does sends a given tree log down to the disk and
24182449
* updates the super blocks to record it. When this call is done,
@@ -2426,7 +2457,7 @@ static void wait_for_writer(struct btrfs_trans_handle *trans,
24262457
* that has happened.
24272458
*/
24282459
int btrfs_sync_log(struct btrfs_trans_handle *trans,
2429-
struct btrfs_root *root)
2460+
struct btrfs_root *root, struct btrfs_log_ctx *ctx)
24302461
{
24312462
int index1;
24322463
int index2;
@@ -2435,15 +2466,16 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
24352466
struct btrfs_root *log = root->log_root;
24362467
struct btrfs_root *log_root_tree = root->fs_info->log_root_tree;
24372468
int log_transid = 0;
2469+
struct btrfs_log_ctx root_log_ctx;
24382470
struct blk_plug plug;
24392471

24402472
mutex_lock(&root->log_mutex);
24412473
log_transid = root->log_transid;
24422474
index1 = root->log_transid % 2;
24432475
if (atomic_read(&root->log_commit[index1])) {
2444-
ret = wait_log_commit(trans, root, root->log_transid);
2476+
wait_log_commit(trans, root, root->log_transid);
24452477
mutex_unlock(&root->log_mutex);
2446-
return ret;
2478+
return ctx->log_ret;
24472479
}
24482480
atomic_set(&root->log_commit[index1], 1);
24492481

@@ -2534,13 +2566,18 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
25342566
}
25352567

25362568
index2 = log_root_tree->log_transid % 2;
2569+
2570+
btrfs_init_log_ctx(&root_log_ctx);
2571+
list_add_tail(&root_log_ctx.list, &log_root_tree->log_ctxs[index2]);
2572+
25372573
if (atomic_read(&log_root_tree->log_commit[index2])) {
25382574
blk_finish_plug(&plug);
25392575
btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
2540-
ret = wait_log_commit(trans, log_root_tree,
2541-
log_root_tree->log_transid);
2576+
wait_log_commit(trans, log_root_tree,
2577+
log_root_tree->log_transid);
25422578
btrfs_free_logged_extents(log, log_transid);
25432579
mutex_unlock(&log_root_tree->log_mutex);
2580+
ret = root_log_ctx.log_ret;
25442581
goto out;
25452582
}
25462583
atomic_set(&log_root_tree->log_commit[index2], 1);
@@ -2609,12 +2646,31 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
26092646
mutex_unlock(&root->log_mutex);
26102647

26112648
out_wake_log_root:
2649+
/*
2650+
* We needn't get log_mutex here because we are sure all
2651+
* the other tasks are blocked.
2652+
*/
2653+
btrfs_remove_all_log_ctxs(log_root_tree, index2, ret);
2654+
2655+
/*
2656+
* It is dangerous if log_commit is changed before we set
2657+
* ->log_ret of log ctx. Because the readers may not get
2658+
* the return value.
2659+
*/
2660+
smp_wmb();
2661+
26122662
atomic_set(&log_root_tree->log_commit[index2], 0);
26132663
smp_mb();
26142664
if (waitqueue_active(&log_root_tree->log_commit_wait[index2]))
26152665
wake_up(&log_root_tree->log_commit_wait[index2]);
26162666
out:
2667+
/* See above. */
2668+
btrfs_remove_all_log_ctxs(root, index1, ret);
2669+
2670+
/* See above. */
2671+
smp_wmb();
26172672
atomic_set(&root->log_commit[index1], 0);
2673+
26182674
smp_mb();
26192675
if (waitqueue_active(&root->log_commit_wait[index1]))
26202676
wake_up(&root->log_commit_wait[index1]);
@@ -4076,7 +4132,8 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,
40764132
*/
40774133
static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
40784134
struct btrfs_root *root, struct inode *inode,
4079-
struct dentry *parent, int exists_only)
4135+
struct dentry *parent, int exists_only,
4136+
struct btrfs_log_ctx *ctx)
40804137
{
40814138
int inode_only = exists_only ? LOG_INODE_EXISTS : LOG_INODE_ALL;
40824139
struct super_block *sb;
@@ -4113,7 +4170,7 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
41134170
goto end_no_trans;
41144171
}
41154172

4116-
ret = start_log_trans(trans, root);
4173+
ret = start_log_trans(trans, root, ctx);
41174174
if (ret)
41184175
goto end_no_trans;
41194176

@@ -4163,6 +4220,9 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
41634220
root->fs_info->last_trans_log_full_commit = trans->transid;
41644221
ret = 1;
41654222
}
4223+
4224+
if (ret)
4225+
btrfs_remove_log_ctx(root, ctx);
41664226
btrfs_end_log_trans(root);
41674227
end_no_trans:
41684228
return ret;
@@ -4175,12 +4235,14 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
41754235
* data on disk.
41764236
*/
41774237
int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
4178-
struct btrfs_root *root, struct dentry *dentry)
4238+
struct btrfs_root *root, struct dentry *dentry,
4239+
struct btrfs_log_ctx *ctx)
41794240
{
41804241
struct dentry *parent = dget_parent(dentry);
41814242
int ret;
41824243

4183-
ret = btrfs_log_inode_parent(trans, root, dentry->d_inode, parent, 0);
4244+
ret = btrfs_log_inode_parent(trans, root, dentry->d_inode, parent,
4245+
0, ctx);
41844246
dput(parent);
41854247

41864248
return ret;
@@ -4417,6 +4479,6 @@ int btrfs_log_new_name(struct btrfs_trans_handle *trans,
44174479
root->fs_info->last_trans_committed))
44184480
return 0;
44194481

4420-
return btrfs_log_inode_parent(trans, root, inode, parent, 1);
4482+
return btrfs_log_inode_parent(trans, root, inode, parent, 1, NULL);
44214483
}
44224484

fs/btrfs/tree-log.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,26 @@
2222
/* return value for btrfs_log_dentry_safe that means we don't need to log it at all */
2323
#define BTRFS_NO_LOG_SYNC 256
2424

25+
struct btrfs_log_ctx {
26+
int log_ret;
27+
struct list_head list;
28+
};
29+
30+
static inline void btrfs_init_log_ctx(struct btrfs_log_ctx *ctx)
31+
{
32+
ctx->log_ret = 0;
33+
INIT_LIST_HEAD(&ctx->list);
34+
}
35+
2536
int btrfs_sync_log(struct btrfs_trans_handle *trans,
26-
struct btrfs_root *root);
37+
struct btrfs_root *root, struct btrfs_log_ctx *ctx);
2738
int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root);
2839
int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
2940
struct btrfs_fs_info *fs_info);
3041
int btrfs_recover_log_trees(struct btrfs_root *tree_root);
3142
int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
32-
struct btrfs_root *root, struct dentry *dentry);
43+
struct btrfs_root *root, struct dentry *dentry,
44+
struct btrfs_log_ctx *ctx);
3345
int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
3446
struct btrfs_root *root,
3547
const char *name, int name_len,

0 commit comments

Comments
 (0)