Skip to content

Commit e897be1

Browse files
konistorvalds
authored andcommitted
nilfs2: fix lockdep warnings in page operations for btree nodes
Patch series "nilfs2 lockdep warning fixes". The first two are to resolve the lockdep warning issue, and the last one is the accompanying cleanup and low priority. Based on your comment, this series solves the issue by separating inode object as needed. Since I was worried about the impact of the object composition changes, I tested the series carefully not to cause regressions especially for delicate functions such like disk space reclamation and snapshots. This patch (of 3): If CONFIG_LOCKDEP is enabled, nilfs2 hits lockdep warnings at inode_to_wb() during page/folio operations for btree nodes: WARNING: CPU: 0 PID: 6575 at include/linux/backing-dev.h:269 inode_to_wb include/linux/backing-dev.h:269 [inline] WARNING: CPU: 0 PID: 6575 at include/linux/backing-dev.h:269 folio_account_dirtied mm/page-writeback.c:2460 [inline] WARNING: CPU: 0 PID: 6575 at include/linux/backing-dev.h:269 __folio_mark_dirty+0xa7c/0xe30 mm/page-writeback.c:2509 Modules linked in: ... RIP: 0010:inode_to_wb include/linux/backing-dev.h:269 [inline] RIP: 0010:folio_account_dirtied mm/page-writeback.c:2460 [inline] RIP: 0010:__folio_mark_dirty+0xa7c/0xe30 mm/page-writeback.c:2509 ... Call Trace: __set_page_dirty include/linux/pagemap.h:834 [inline] mark_buffer_dirty+0x4e6/0x650 fs/buffer.c:1145 nilfs_btree_propagate_p fs/nilfs2/btree.c:1889 [inline] nilfs_btree_propagate+0x4ae/0xea0 fs/nilfs2/btree.c:2085 nilfs_bmap_propagate+0x73/0x170 fs/nilfs2/bmap.c:337 nilfs_collect_dat_data+0x45/0xd0 fs/nilfs2/segment.c:625 nilfs_segctor_apply_buffers+0x14a/0x470 fs/nilfs2/segment.c:1009 nilfs_segctor_scan_file+0x47a/0x700 fs/nilfs2/segment.c:1048 nilfs_segctor_collect_blocks fs/nilfs2/segment.c:1224 [inline] nilfs_segctor_collect fs/nilfs2/segment.c:1494 [inline] nilfs_segctor_do_construct+0x14f3/0x6c60 fs/nilfs2/segment.c:2036 nilfs_segctor_construct+0x7a7/0xb30 fs/nilfs2/segment.c:2372 nilfs_segctor_thread_construct fs/nilfs2/segment.c:2480 [inline] nilfs_segctor_thread+0x3c3/0xf90 fs/nilfs2/segment.c:2563 kthread+0x405/0x4f0 kernel/kthread.c:327 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:295 This is because nilfs2 uses two page caches for each inode and inode->i_mapping never points to one of them, the btree node cache. This causes inode_to_wb(inode) to refer to a different page cache than the caller page/folio operations such like __folio_start_writeback(), __folio_end_writeback(), or __folio_mark_dirty() acquired the lock. This patch resolves the issue by allocating and using an additional inode to hold the page cache of btree nodes. The inode is attached one-to-one to the traditional nilfs2 inode if it requires a block mapping with b-tree. This setup change is in memory only and does not affect the disk format. Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Link: https://lore.kernel.org/r/[email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Ryusuke Konishi <[email protected]> Reported-by: [email protected] Reported-by: [email protected] Reported-by: Hao Sun <[email protected]> Reported-by: David Hildenbrand <[email protected]> Tested-by: Ryusuke Konishi <[email protected]> Cc: Matthew Wilcox <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent de19433 commit e897be1

File tree

10 files changed

+154
-50
lines changed

10 files changed

+154
-50
lines changed

fs/nilfs2/btnode.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,23 @@
2020
#include "page.h"
2121
#include "btnode.h"
2222

23+
24+
/**
25+
* nilfs_init_btnc_inode - initialize B-tree node cache inode
26+
* @btnc_inode: inode to be initialized
27+
*
28+
* nilfs_init_btnc_inode() sets up an inode for B-tree node cache.
29+
*/
30+
void nilfs_init_btnc_inode(struct inode *btnc_inode)
31+
{
32+
struct nilfs_inode_info *ii = NILFS_I(btnc_inode);
33+
34+
btnc_inode->i_mode = S_IFREG;
35+
ii->i_flags = 0;
36+
memset(&ii->i_bmap_data, 0, sizeof(struct nilfs_bmap));
37+
mapping_set_gfp_mask(btnc_inode->i_mapping, GFP_NOFS);
38+
}
39+
2340
void nilfs_btnode_cache_clear(struct address_space *btnc)
2441
{
2542
invalidate_mapping_pages(btnc, 0, -1);
@@ -29,7 +46,7 @@ void nilfs_btnode_cache_clear(struct address_space *btnc)
2946
struct buffer_head *
3047
nilfs_btnode_create_block(struct address_space *btnc, __u64 blocknr)
3148
{
32-
struct inode *inode = NILFS_BTNC_I(btnc);
49+
struct inode *inode = btnc->host;
3350
struct buffer_head *bh;
3451

3552
bh = nilfs_grab_buffer(inode, btnc, blocknr, BIT(BH_NILFS_Node));
@@ -57,7 +74,7 @@ int nilfs_btnode_submit_block(struct address_space *btnc, __u64 blocknr,
5774
struct buffer_head **pbh, sector_t *submit_ptr)
5875
{
5976
struct buffer_head *bh;
60-
struct inode *inode = NILFS_BTNC_I(btnc);
77+
struct inode *inode = btnc->host;
6178
struct page *page;
6279
int err;
6380

@@ -157,7 +174,7 @@ int nilfs_btnode_prepare_change_key(struct address_space *btnc,
157174
struct nilfs_btnode_chkey_ctxt *ctxt)
158175
{
159176
struct buffer_head *obh, *nbh;
160-
struct inode *inode = NILFS_BTNC_I(btnc);
177+
struct inode *inode = btnc->host;
161178
__u64 oldkey = ctxt->oldkey, newkey = ctxt->newkey;
162179
int err;
163180

fs/nilfs2/btnode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct nilfs_btnode_chkey_ctxt {
3030
struct buffer_head *newbh;
3131
};
3232

33+
void nilfs_init_btnc_inode(struct inode *btnc_inode);
3334
void nilfs_btnode_cache_clear(struct address_space *);
3435
struct buffer_head *nilfs_btnode_create_block(struct address_space *btnc,
3536
__u64 blocknr);

fs/nilfs2/btree.c

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ static void nilfs_btree_free_path(struct nilfs_btree_path *path)
5858
static int nilfs_btree_get_new_block(const struct nilfs_bmap *btree,
5959
__u64 ptr, struct buffer_head **bhp)
6060
{
61-
struct address_space *btnc = &NILFS_BMAP_I(btree)->i_btnode_cache;
61+
struct inode *btnc_inode = NILFS_BMAP_I(btree)->i_assoc_inode;
62+
struct address_space *btnc = btnc_inode->i_mapping;
6263
struct buffer_head *bh;
6364

6465
bh = nilfs_btnode_create_block(btnc, ptr);
@@ -470,7 +471,8 @@ static int __nilfs_btree_get_block(const struct nilfs_bmap *btree, __u64 ptr,
470471
struct buffer_head **bhp,
471472
const struct nilfs_btree_readahead_info *ra)
472473
{
473-
struct address_space *btnc = &NILFS_BMAP_I(btree)->i_btnode_cache;
474+
struct inode *btnc_inode = NILFS_BMAP_I(btree)->i_assoc_inode;
475+
struct address_space *btnc = btnc_inode->i_mapping;
474476
struct buffer_head *bh, *ra_bh;
475477
sector_t submit_ptr = 0;
476478
int ret;
@@ -1741,6 +1743,10 @@ nilfs_btree_prepare_convert_and_insert(struct nilfs_bmap *btree, __u64 key,
17411743
dat = nilfs_bmap_get_dat(btree);
17421744
}
17431745

1746+
ret = nilfs_attach_btree_node_cache(&NILFS_BMAP_I(btree)->vfs_inode);
1747+
if (ret < 0)
1748+
return ret;
1749+
17441750
ret = nilfs_bmap_prepare_alloc_ptr(btree, dreq, dat);
17451751
if (ret < 0)
17461752
return ret;
@@ -1913,7 +1919,7 @@ static int nilfs_btree_prepare_update_v(struct nilfs_bmap *btree,
19131919
path[level].bp_ctxt.newkey = path[level].bp_newreq.bpr_ptr;
19141920
path[level].bp_ctxt.bh = path[level].bp_bh;
19151921
ret = nilfs_btnode_prepare_change_key(
1916-
&NILFS_BMAP_I(btree)->i_btnode_cache,
1922+
NILFS_BMAP_I(btree)->i_assoc_inode->i_mapping,
19171923
&path[level].bp_ctxt);
19181924
if (ret < 0) {
19191925
nilfs_dat_abort_update(dat,
@@ -1939,7 +1945,7 @@ static void nilfs_btree_commit_update_v(struct nilfs_bmap *btree,
19391945

19401946
if (buffer_nilfs_node(path[level].bp_bh)) {
19411947
nilfs_btnode_commit_change_key(
1942-
&NILFS_BMAP_I(btree)->i_btnode_cache,
1948+
NILFS_BMAP_I(btree)->i_assoc_inode->i_mapping,
19431949
&path[level].bp_ctxt);
19441950
path[level].bp_bh = path[level].bp_ctxt.bh;
19451951
}
@@ -1958,7 +1964,7 @@ static void nilfs_btree_abort_update_v(struct nilfs_bmap *btree,
19581964
&path[level].bp_newreq.bpr_req);
19591965
if (buffer_nilfs_node(path[level].bp_bh))
19601966
nilfs_btnode_abort_change_key(
1961-
&NILFS_BMAP_I(btree)->i_btnode_cache,
1967+
NILFS_BMAP_I(btree)->i_assoc_inode->i_mapping,
19621968
&path[level].bp_ctxt);
19631969
}
19641970

@@ -2134,7 +2140,8 @@ static void nilfs_btree_add_dirty_buffer(struct nilfs_bmap *btree,
21342140
static void nilfs_btree_lookup_dirty_buffers(struct nilfs_bmap *btree,
21352141
struct list_head *listp)
21362142
{
2137-
struct address_space *btcache = &NILFS_BMAP_I(btree)->i_btnode_cache;
2143+
struct inode *btnc_inode = NILFS_BMAP_I(btree)->i_assoc_inode;
2144+
struct address_space *btcache = btnc_inode->i_mapping;
21382145
struct list_head lists[NILFS_BTREE_LEVEL_MAX];
21392146
struct pagevec pvec;
21402147
struct buffer_head *bh, *head;
@@ -2188,12 +2195,12 @@ static int nilfs_btree_assign_p(struct nilfs_bmap *btree,
21882195
path[level].bp_ctxt.newkey = blocknr;
21892196
path[level].bp_ctxt.bh = *bh;
21902197
ret = nilfs_btnode_prepare_change_key(
2191-
&NILFS_BMAP_I(btree)->i_btnode_cache,
2198+
NILFS_BMAP_I(btree)->i_assoc_inode->i_mapping,
21922199
&path[level].bp_ctxt);
21932200
if (ret < 0)
21942201
return ret;
21952202
nilfs_btnode_commit_change_key(
2196-
&NILFS_BMAP_I(btree)->i_btnode_cache,
2203+
NILFS_BMAP_I(btree)->i_assoc_inode->i_mapping,
21972204
&path[level].bp_ctxt);
21982205
*bh = path[level].bp_ctxt.bh;
21992206
}
@@ -2398,6 +2405,10 @@ int nilfs_btree_init(struct nilfs_bmap *bmap)
23982405

23992406
if (nilfs_btree_root_broken(nilfs_btree_get_root(bmap), bmap->b_inode))
24002407
ret = -EIO;
2408+
else
2409+
ret = nilfs_attach_btree_node_cache(
2410+
&NILFS_BMAP_I(bmap)->vfs_inode);
2411+
24012412
return ret;
24022413
}
24032414

fs/nilfs2/gcinode.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,10 @@ int nilfs_gccache_submit_read_data(struct inode *inode, sector_t blkoff,
126126
int nilfs_gccache_submit_read_node(struct inode *inode, sector_t pbn,
127127
__u64 vbn, struct buffer_head **out_bh)
128128
{
129+
struct inode *btnc_inode = NILFS_I(inode)->i_assoc_inode;
129130
int ret;
130131

131-
ret = nilfs_btnode_submit_block(&NILFS_I(inode)->i_btnode_cache,
132+
ret = nilfs_btnode_submit_block(btnc_inode->i_mapping,
132133
vbn ? : pbn, pbn, REQ_OP_READ, 0,
133134
out_bh, &pbn);
134135
if (ret == -EEXIST) /* internal code (cache hit) */
@@ -170,7 +171,7 @@ int nilfs_init_gcinode(struct inode *inode)
170171
ii->i_flags = 0;
171172
nilfs_bmap_init_gc(ii->i_bmap);
172173

173-
return 0;
174+
return nilfs_attach_btree_node_cache(inode);
174175
}
175176

176177
/**
@@ -185,7 +186,7 @@ void nilfs_remove_all_gcinodes(struct the_nilfs *nilfs)
185186
ii = list_first_entry(head, struct nilfs_inode_info, i_dirty);
186187
list_del_init(&ii->i_dirty);
187188
truncate_inode_pages(&ii->vfs_inode.i_data, 0);
188-
nilfs_btnode_cache_clear(&ii->i_btnode_cache);
189+
nilfs_btnode_cache_clear(ii->i_assoc_inode->i_mapping);
189190
iput(&ii->vfs_inode);
190191
}
191192
}

fs/nilfs2/inode.c

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@
2929
* @cno: checkpoint number
3030
* @root: pointer on NILFS root object (mounted checkpoint)
3131
* @for_gc: inode for GC flag
32+
* @for_btnc: inode for B-tree node cache flag
3233
*/
3334
struct nilfs_iget_args {
3435
u64 ino;
3536
__u64 cno;
3637
struct nilfs_root *root;
37-
int for_gc;
38+
bool for_gc;
39+
bool for_btnc;
3840
};
3941

4042
static int nilfs_iget_test(struct inode *inode, void *opaque);
@@ -312,7 +314,8 @@ static int nilfs_insert_inode_locked(struct inode *inode,
312314
unsigned long ino)
313315
{
314316
struct nilfs_iget_args args = {
315-
.ino = ino, .root = root, .cno = 0, .for_gc = 0
317+
.ino = ino, .root = root, .cno = 0, .for_gc = false,
318+
.for_btnc = false
316319
};
317320

318321
return insert_inode_locked4(inode, ino, nilfs_iget_test, &args);
@@ -525,6 +528,13 @@ static int nilfs_iget_test(struct inode *inode, void *opaque)
525528
return 0;
526529

527530
ii = NILFS_I(inode);
531+
if (test_bit(NILFS_I_BTNC, &ii->i_state)) {
532+
if (!args->for_btnc)
533+
return 0;
534+
} else if (args->for_btnc) {
535+
return 0;
536+
}
537+
528538
if (!test_bit(NILFS_I_GCINODE, &ii->i_state))
529539
return !args->for_gc;
530540

@@ -536,23 +546,24 @@ static int nilfs_iget_set(struct inode *inode, void *opaque)
536546
struct nilfs_iget_args *args = opaque;
537547

538548
inode->i_ino = args->ino;
539-
if (args->for_gc) {
549+
NILFS_I(inode)->i_cno = args->cno;
550+
NILFS_I(inode)->i_root = args->root;
551+
if (args->root && args->ino == NILFS_ROOT_INO)
552+
nilfs_get_root(args->root);
553+
554+
if (args->for_gc)
540555
NILFS_I(inode)->i_state = BIT(NILFS_I_GCINODE);
541-
NILFS_I(inode)->i_cno = args->cno;
542-
NILFS_I(inode)->i_root = NULL;
543-
} else {
544-
if (args->root && args->ino == NILFS_ROOT_INO)
545-
nilfs_get_root(args->root);
546-
NILFS_I(inode)->i_root = args->root;
547-
}
556+
if (args->for_btnc)
557+
NILFS_I(inode)->i_state |= BIT(NILFS_I_BTNC);
548558
return 0;
549559
}
550560

551561
struct inode *nilfs_ilookup(struct super_block *sb, struct nilfs_root *root,
552562
unsigned long ino)
553563
{
554564
struct nilfs_iget_args args = {
555-
.ino = ino, .root = root, .cno = 0, .for_gc = 0
565+
.ino = ino, .root = root, .cno = 0, .for_gc = false,
566+
.for_btnc = false
556567
};
557568

558569
return ilookup5(sb, ino, nilfs_iget_test, &args);
@@ -562,7 +573,8 @@ struct inode *nilfs_iget_locked(struct super_block *sb, struct nilfs_root *root,
562573
unsigned long ino)
563574
{
564575
struct nilfs_iget_args args = {
565-
.ino = ino, .root = root, .cno = 0, .for_gc = 0
576+
.ino = ino, .root = root, .cno = 0, .for_gc = false,
577+
.for_btnc = false
566578
};
567579

568580
return iget5_locked(sb, ino, nilfs_iget_test, nilfs_iget_set, &args);
@@ -593,7 +605,8 @@ struct inode *nilfs_iget_for_gc(struct super_block *sb, unsigned long ino,
593605
__u64 cno)
594606
{
595607
struct nilfs_iget_args args = {
596-
.ino = ino, .root = NULL, .cno = cno, .for_gc = 1
608+
.ino = ino, .root = NULL, .cno = cno, .for_gc = true,
609+
.for_btnc = false
597610
};
598611
struct inode *inode;
599612
int err;
@@ -613,6 +626,68 @@ struct inode *nilfs_iget_for_gc(struct super_block *sb, unsigned long ino,
613626
return inode;
614627
}
615628

629+
/**
630+
* nilfs_attach_btree_node_cache - attach a B-tree node cache to the inode
631+
* @inode: inode object
632+
*
633+
* nilfs_attach_btree_node_cache() attaches a B-tree node cache to @inode,
634+
* or does nothing if the inode already has it. This function allocates
635+
* an additional inode to maintain page cache of B-tree nodes one-on-one.
636+
*
637+
* Return Value: On success, 0 is returned. On errors, one of the following
638+
* negative error code is returned.
639+
*
640+
* %-ENOMEM - Insufficient memory available.
641+
*/
642+
int nilfs_attach_btree_node_cache(struct inode *inode)
643+
{
644+
struct nilfs_inode_info *ii = NILFS_I(inode);
645+
struct inode *btnc_inode;
646+
struct nilfs_iget_args args;
647+
648+
if (ii->i_assoc_inode)
649+
return 0;
650+
651+
args.ino = inode->i_ino;
652+
args.root = ii->i_root;
653+
args.cno = ii->i_cno;
654+
args.for_gc = test_bit(NILFS_I_GCINODE, &ii->i_state) != 0;
655+
args.for_btnc = true;
656+
657+
btnc_inode = iget5_locked(inode->i_sb, inode->i_ino, nilfs_iget_test,
658+
nilfs_iget_set, &args);
659+
if (unlikely(!btnc_inode))
660+
return -ENOMEM;
661+
if (btnc_inode->i_state & I_NEW) {
662+
nilfs_init_btnc_inode(btnc_inode);
663+
unlock_new_inode(btnc_inode);
664+
}
665+
NILFS_I(btnc_inode)->i_assoc_inode = inode;
666+
NILFS_I(btnc_inode)->i_bmap = ii->i_bmap;
667+
ii->i_assoc_inode = btnc_inode;
668+
669+
return 0;
670+
}
671+
672+
/**
673+
* nilfs_detach_btree_node_cache - detach the B-tree node cache from the inode
674+
* @inode: inode object
675+
*
676+
* nilfs_detach_btree_node_cache() detaches the B-tree node cache and its
677+
* holder inode bound to @inode, or does nothing if @inode doesn't have it.
678+
*/
679+
void nilfs_detach_btree_node_cache(struct inode *inode)
680+
{
681+
struct nilfs_inode_info *ii = NILFS_I(inode);
682+
struct inode *btnc_inode = ii->i_assoc_inode;
683+
684+
if (btnc_inode) {
685+
NILFS_I(btnc_inode)->i_assoc_inode = NULL;
686+
ii->i_assoc_inode = NULL;
687+
iput(btnc_inode);
688+
}
689+
}
690+
616691
void nilfs_write_inode_common(struct inode *inode,
617692
struct nilfs_inode *raw_inode, int has_bmap)
618693
{
@@ -760,7 +835,8 @@ static void nilfs_clear_inode(struct inode *inode)
760835
if (test_bit(NILFS_I_BMAP, &ii->i_state))
761836
nilfs_bmap_clear(ii->i_bmap);
762837

763-
nilfs_btnode_cache_clear(&ii->i_btnode_cache);
838+
if (!test_bit(NILFS_I_BTNC, &ii->i_state))
839+
nilfs_detach_btree_node_cache(inode);
764840

765841
if (ii->i_root && inode->i_ino == NILFS_ROOT_INO)
766842
nilfs_put_root(ii->i_root);

fs/nilfs2/mdt.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ int nilfs_mdt_save_to_shadow_map(struct inode *inode)
533533
goto out;
534534

535535
ret = nilfs_copy_dirty_pages(&shadow->frozen_btnodes,
536-
&ii->i_btnode_cache);
536+
ii->i_assoc_inode->i_mapping);
537537
if (ret)
538538
goto out;
539539

@@ -624,8 +624,9 @@ void nilfs_mdt_restore_from_shadow_map(struct inode *inode)
624624
nilfs_clear_dirty_pages(inode->i_mapping, true);
625625
nilfs_copy_back_pages(inode->i_mapping, &shadow->frozen_data);
626626

627-
nilfs_clear_dirty_pages(&ii->i_btnode_cache, true);
628-
nilfs_copy_back_pages(&ii->i_btnode_cache, &shadow->frozen_btnodes);
627+
nilfs_clear_dirty_pages(ii->i_assoc_inode->i_mapping, true);
628+
nilfs_copy_back_pages(ii->i_assoc_inode->i_mapping,
629+
&shadow->frozen_btnodes);
629630

630631
nilfs_bmap_restore(ii->i_bmap, &shadow->bmap_store);
631632

0 commit comments

Comments
 (0)