Skip to content

Commit 8793ed8

Browse files
fdmananakdave
authored andcommitted
btrfs: avoid tree mod log ENOMEM failures when we don't need to log
When logging tree mod log operations we start by checking, in a lockless manner, if we need to log - if we don't, we just return and do nothing, otherwise we will allocate one or more tree mod log operations and then check again if we need to log. This second check will take the tree mod log lock in write mode if we need to log, otherwise it will do nothing and we just free the allocated memory and return success. We can improve on this by not returning an error in case the memory allocations fail, unless the second check tells us that we actually need to log. That is, if we fail to allocate memory and the second check tells use that we don't need to log, we can just return success and avoid returning -ENOMEM to the caller. Currently tree mod log failures are dealt with either a BUG_ON() or a transaction abort, as tree mod log operations are logged in code paths that modify a b+tree. So just avoid failing with -ENOMEM if we fail to allocate a tree mod log operation unless we actually need to log the operations, that is, if tree_mod_dont_log() returns true. Signed-off-by: Filipe Manana <fdmanana@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent ede600e commit 8793ed8

1 file changed

Lines changed: 114 additions & 34 deletions

File tree

fs/btrfs/tree-mod-log.c

Lines changed: 114 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -226,21 +226,32 @@ int btrfs_tree_mod_log_insert_key(struct extent_buffer *eb, int slot,
226226
enum btrfs_mod_log_op op)
227227
{
228228
struct tree_mod_elem *tm;
229-
int ret;
229+
int ret = 0;
230230

231231
if (!tree_mod_need_log(eb->fs_info, eb))
232232
return 0;
233233

234234
tm = alloc_tree_mod_elem(eb, slot, op);
235235
if (!tm)
236-
return -ENOMEM;
236+
ret = -ENOMEM;
237237

238238
if (tree_mod_dont_log(eb->fs_info, eb)) {
239239
kfree(tm);
240+
/*
241+
* Don't error if we failed to allocate memory because we don't
242+
* need to log.
243+
*/
240244
return 0;
245+
} else if (ret != 0) {
246+
/*
247+
* We previously failed to allocate memory and we need to log,
248+
* so we have to fail.
249+
*/
250+
goto out_unlock;
241251
}
242252

243253
ret = tree_mod_log_insert(eb->fs_info, tm);
254+
out_unlock:
244255
write_unlock(&eb->fs_info->tree_mod_log_lock);
245256
if (ret)
246257
kfree(tm);
@@ -282,29 +293,45 @@ int btrfs_tree_mod_log_insert_move(struct extent_buffer *eb,
282293
return 0;
283294

284295
tm_list = kcalloc(nr_items, sizeof(struct tree_mod_elem *), GFP_NOFS);
285-
if (!tm_list)
286-
return -ENOMEM;
296+
if (!tm_list) {
297+
ret = -ENOMEM;
298+
goto lock;
299+
}
287300

288301
tm = tree_mod_log_alloc_move(eb, dst_slot, src_slot, nr_items);
289302
if (IS_ERR(tm)) {
290303
ret = PTR_ERR(tm);
291304
tm = NULL;
292-
goto free_tms;
305+
goto lock;
293306
}
294307

295308
for (i = 0; i + dst_slot < src_slot && i < nr_items; i++) {
296309
tm_list[i] = alloc_tree_mod_elem(eb, i + dst_slot,
297310
BTRFS_MOD_LOG_KEY_REMOVE_WHILE_MOVING);
298311
if (!tm_list[i]) {
299312
ret = -ENOMEM;
300-
goto free_tms;
313+
goto lock;
301314
}
302315
}
303316

304-
if (tree_mod_dont_log(eb->fs_info, eb))
317+
lock:
318+
if (tree_mod_dont_log(eb->fs_info, eb)) {
319+
/*
320+
* Don't error if we failed to allocate memory because we don't
321+
* need to log.
322+
*/
323+
ret = 0;
305324
goto free_tms;
325+
}
306326
locked = true;
307327

328+
/*
329+
* We previously failed to allocate memory and we need to log, so we
330+
* have to fail.
331+
*/
332+
if (ret != 0)
333+
goto free_tms;
334+
308335
/*
309336
* When we override something during the move, we log these removals.
310337
* This can only happen when we move towards the beginning of the
@@ -325,10 +352,12 @@ int btrfs_tree_mod_log_insert_move(struct extent_buffer *eb,
325352
return 0;
326353

327354
free_tms:
328-
for (i = 0; i < nr_items; i++) {
329-
if (tm_list[i] && !RB_EMPTY_NODE(&tm_list[i]->node))
330-
rb_erase(&tm_list[i]->node, &eb->fs_info->tree_mod_log);
331-
kfree(tm_list[i]);
355+
if (tm_list) {
356+
for (i = 0; i < nr_items; i++) {
357+
if (tm_list[i] && !RB_EMPTY_NODE(&tm_list[i]->node))
358+
rb_erase(&tm_list[i]->node, &eb->fs_info->tree_mod_log);
359+
kfree(tm_list[i]);
360+
}
332361
}
333362
if (locked)
334363
write_unlock(&eb->fs_info->tree_mod_log_lock);
@@ -378,22 +407,22 @@ int btrfs_tree_mod_log_insert_root(struct extent_buffer *old_root,
378407
GFP_NOFS);
379408
if (!tm_list) {
380409
ret = -ENOMEM;
381-
goto free_tms;
410+
goto lock;
382411
}
383412
for (i = 0; i < nritems; i++) {
384413
tm_list[i] = alloc_tree_mod_elem(old_root, i,
385414
BTRFS_MOD_LOG_KEY_REMOVE_WHILE_FREEING);
386415
if (!tm_list[i]) {
387416
ret = -ENOMEM;
388-
goto free_tms;
417+
goto lock;
389418
}
390419
}
391420
}
392421

393422
tm = kzalloc(sizeof(*tm), GFP_NOFS);
394423
if (!tm) {
395424
ret = -ENOMEM;
396-
goto free_tms;
425+
goto lock;
397426
}
398427

399428
tm->logical = new_root->start;
@@ -402,14 +431,28 @@ int btrfs_tree_mod_log_insert_root(struct extent_buffer *old_root,
402431
tm->generation = btrfs_header_generation(old_root);
403432
tm->op = BTRFS_MOD_LOG_ROOT_REPLACE;
404433

405-
if (tree_mod_dont_log(fs_info, NULL))
434+
lock:
435+
if (tree_mod_dont_log(fs_info, NULL)) {
436+
/*
437+
* Don't error if we failed to allocate memory because we don't
438+
* need to log.
439+
*/
440+
ret = 0;
406441
goto free_tms;
442+
} else if (ret != 0) {
443+
/*
444+
* We previously failed to allocate memory and we need to log,
445+
* so we have to fail.
446+
*/
447+
goto out_unlock;
448+
}
407449

408450
if (tm_list)
409451
ret = tree_mod_log_free_eb(fs_info, tm_list, nritems);
410452
if (!ret)
411453
ret = tree_mod_log_insert(fs_info, tm);
412454

455+
out_unlock:
413456
write_unlock(&fs_info->tree_mod_log_lock);
414457
if (ret)
415458
goto free_tms;
@@ -501,7 +544,8 @@ int btrfs_tree_mod_log_eb_copy(struct extent_buffer *dst,
501544
struct btrfs_fs_info *fs_info = dst->fs_info;
502545
int ret = 0;
503546
struct tree_mod_elem **tm_list = NULL;
504-
struct tree_mod_elem **tm_list_add, **tm_list_rem;
547+
struct tree_mod_elem **tm_list_add = NULL;
548+
struct tree_mod_elem **tm_list_rem = NULL;
505549
int i;
506550
bool locked = false;
507551
struct tree_mod_elem *dst_move_tm = NULL;
@@ -517,16 +561,18 @@ int btrfs_tree_mod_log_eb_copy(struct extent_buffer *dst,
517561

518562
tm_list = kcalloc(nr_items * 2, sizeof(struct tree_mod_elem *),
519563
GFP_NOFS);
520-
if (!tm_list)
521-
return -ENOMEM;
564+
if (!tm_list) {
565+
ret = -ENOMEM;
566+
goto lock;
567+
}
522568

523569
if (dst_move_nr_items) {
524570
dst_move_tm = tree_mod_log_alloc_move(dst, dst_offset + nr_items,
525571
dst_offset, dst_move_nr_items);
526572
if (IS_ERR(dst_move_tm)) {
527573
ret = PTR_ERR(dst_move_tm);
528574
dst_move_tm = NULL;
529-
goto free_tms;
575+
goto lock;
530576
}
531577
}
532578
if (src_move_nr_items) {
@@ -536,7 +582,7 @@ int btrfs_tree_mod_log_eb_copy(struct extent_buffer *dst,
536582
if (IS_ERR(src_move_tm)) {
537583
ret = PTR_ERR(src_move_tm);
538584
src_move_tm = NULL;
539-
goto free_tms;
585+
goto lock;
540586
}
541587
}
542588

@@ -547,21 +593,35 @@ int btrfs_tree_mod_log_eb_copy(struct extent_buffer *dst,
547593
BTRFS_MOD_LOG_KEY_REMOVE);
548594
if (!tm_list_rem[i]) {
549595
ret = -ENOMEM;
550-
goto free_tms;
596+
goto lock;
551597
}
552598

553599
tm_list_add[i] = alloc_tree_mod_elem(dst, i + dst_offset,
554600
BTRFS_MOD_LOG_KEY_ADD);
555601
if (!tm_list_add[i]) {
556602
ret = -ENOMEM;
557-
goto free_tms;
603+
goto lock;
558604
}
559605
}
560606

561-
if (tree_mod_dont_log(fs_info, NULL))
607+
lock:
608+
if (tree_mod_dont_log(fs_info, NULL)) {
609+
/*
610+
* Don't error if we failed to allocate memory because we don't
611+
* need to log.
612+
*/
613+
ret = 0;
562614
goto free_tms;
615+
}
563616
locked = true;
564617

618+
/*
619+
* We previously failed to allocate memory and we need to log, so we
620+
* have to fail.
621+
*/
622+
if (ret != 0)
623+
goto free_tms;
624+
565625
if (dst_move_tm) {
566626
ret = tree_mod_log_insert(fs_info, dst_move_tm);
567627
if (ret)
@@ -593,10 +653,12 @@ int btrfs_tree_mod_log_eb_copy(struct extent_buffer *dst,
593653
if (src_move_tm && !RB_EMPTY_NODE(&src_move_tm->node))
594654
rb_erase(&src_move_tm->node, &fs_info->tree_mod_log);
595655
kfree(src_move_tm);
596-
for (i = 0; i < nr_items * 2; i++) {
597-
if (tm_list[i] && !RB_EMPTY_NODE(&tm_list[i]->node))
598-
rb_erase(&tm_list[i]->node, &fs_info->tree_mod_log);
599-
kfree(tm_list[i]);
656+
if (tm_list) {
657+
for (i = 0; i < nr_items * 2; i++) {
658+
if (tm_list[i] && !RB_EMPTY_NODE(&tm_list[i]->node))
659+
rb_erase(&tm_list[i]->node, &fs_info->tree_mod_log);
660+
kfree(tm_list[i]);
661+
}
600662
}
601663
if (locked)
602664
write_unlock(&fs_info->tree_mod_log_lock);
@@ -617,22 +679,38 @@ int btrfs_tree_mod_log_free_eb(struct extent_buffer *eb)
617679

618680
nritems = btrfs_header_nritems(eb);
619681
tm_list = kcalloc(nritems, sizeof(struct tree_mod_elem *), GFP_NOFS);
620-
if (!tm_list)
621-
return -ENOMEM;
682+
if (!tm_list) {
683+
ret = -ENOMEM;
684+
goto lock;
685+
}
622686

623687
for (i = 0; i < nritems; i++) {
624688
tm_list[i] = alloc_tree_mod_elem(eb, i,
625689
BTRFS_MOD_LOG_KEY_REMOVE_WHILE_FREEING);
626690
if (!tm_list[i]) {
627691
ret = -ENOMEM;
628-
goto free_tms;
692+
goto lock;
629693
}
630694
}
631695

632-
if (tree_mod_dont_log(eb->fs_info, eb))
696+
lock:
697+
if (tree_mod_dont_log(eb->fs_info, eb)) {
698+
/*
699+
* Don't error if we failed to allocate memory because we don't
700+
* need to log.
701+
*/
702+
ret = 0;
633703
goto free_tms;
704+
} else if (ret != 0) {
705+
/*
706+
* We previously failed to allocate memory and we need to log,
707+
* so we have to fail.
708+
*/
709+
goto out_unlock;
710+
}
634711

635712
ret = tree_mod_log_free_eb(eb->fs_info, tm_list, nritems);
713+
out_unlock:
636714
write_unlock(&eb->fs_info->tree_mod_log_lock);
637715
if (ret)
638716
goto free_tms;
@@ -641,9 +719,11 @@ int btrfs_tree_mod_log_free_eb(struct extent_buffer *eb)
641719
return 0;
642720

643721
free_tms:
644-
for (i = 0; i < nritems; i++)
645-
kfree(tm_list[i]);
646-
kfree(tm_list);
722+
if (tm_list) {
723+
for (i = 0; i < nritems; i++)
724+
kfree(tm_list[i]);
725+
kfree(tm_list);
726+
}
647727

648728
return ret;
649729
}

0 commit comments

Comments
 (0)