| From ab43f63645dc1e5b4e7771a28fa7bd84a1e44907 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 1 Apr 2022 11:28:21 -0700 |
| Subject: nilfs2: fix lockdep warnings during disk space reclamation |
| |
| From: Ryusuke Konishi <konishi.ryusuke@gmail.com> |
| |
| [ Upstream commit 6e211930f79aa45d422009a5f2e5467d2369ffe5 ] |
| |
| During disk space reclamation, nilfs2 still emits the following lockdep |
| warning due to page/folio operations on shadowed page caches that nilfs2 |
| uses to get a snapshot of DAT file in memory: |
| |
| WARNING: CPU: 0 PID: 2643 at include/linux/backing-dev.h:272 __folio_mark_dirty+0x645/0x670 |
| ... |
| RIP: 0010:__folio_mark_dirty+0x645/0x670 |
| ... |
| Call Trace: |
| filemap_dirty_folio+0x74/0xd0 |
| __set_page_dirty_nobuffers+0x85/0xb0 |
| nilfs_copy_dirty_pages+0x288/0x510 [nilfs2] |
| nilfs_mdt_save_to_shadow_map+0x50/0xe0 [nilfs2] |
| nilfs_clean_segments+0xee/0x5d0 [nilfs2] |
| nilfs_ioctl_clean_segments.isra.19+0xb08/0xf40 [nilfs2] |
| nilfs_ioctl+0xc52/0xfb0 [nilfs2] |
| __x64_sys_ioctl+0x11d/0x170 |
| |
| This fixes the remaining warning by using inode objects to hold those |
| page caches. |
| |
| Link: https://lkml.kernel.org/r/1647867427-30498-3-git-send-email-konishi.ryusuke@gmail.com |
| Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com> |
| Tested-by: Ryusuke Konishi <konishi.ryusuke@gmail.com> |
| Cc: Matthew Wilcox <willy@infradead.org> |
| Cc: David Hildenbrand <david@redhat.com> |
| Cc: Hao Sun <sunhao.th@gmail.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/nilfs2/dat.c | 4 ++- |
| fs/nilfs2/inode.c | 63 ++++++++++++++++++++++++++++++++++++++++++++--- |
| fs/nilfs2/mdt.c | 38 +++++++++++++++++++--------- |
| fs/nilfs2/mdt.h | 6 ++--- |
| fs/nilfs2/nilfs.h | 2 ++ |
| 5 files changed, 92 insertions(+), 21 deletions(-) |
| |
| diff --git a/fs/nilfs2/dat.c b/fs/nilfs2/dat.c |
| index 6f4066636be9..a3523a243e11 100644 |
| --- a/fs/nilfs2/dat.c |
| +++ b/fs/nilfs2/dat.c |
| @@ -497,7 +497,9 @@ int nilfs_dat_read(struct super_block *sb, size_t entry_size, |
| di = NILFS_DAT_I(dat); |
| lockdep_set_class(&di->mi.mi_sem, &dat_lock_key); |
| nilfs_palloc_setup_cache(dat, &di->palloc_cache); |
| - nilfs_mdt_setup_shadow_map(dat, &di->shadow); |
| + err = nilfs_mdt_setup_shadow_map(dat, &di->shadow); |
| + if (err) |
| + goto failed; |
| |
| err = nilfs_read_inode_common(dat, raw_inode); |
| if (err) |
| diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c |
| index b0a0822e371c..35b0bfe9324f 100644 |
| --- a/fs/nilfs2/inode.c |
| +++ b/fs/nilfs2/inode.c |
| @@ -29,6 +29,7 @@ |
| * @root: pointer on NILFS root object (mounted checkpoint) |
| * @for_gc: inode for GC flag |
| * @for_btnc: inode for B-tree node cache flag |
| + * @for_shadow: inode for shadowed page cache flag |
| */ |
| struct nilfs_iget_args { |
| u64 ino; |
| @@ -36,6 +37,7 @@ struct nilfs_iget_args { |
| struct nilfs_root *root; |
| bool for_gc; |
| bool for_btnc; |
| + bool for_shadow; |
| }; |
| |
| static int nilfs_iget_test(struct inode *inode, void *opaque); |
| @@ -325,7 +327,7 @@ static int nilfs_insert_inode_locked(struct inode *inode, |
| { |
| struct nilfs_iget_args args = { |
| .ino = ino, .root = root, .cno = 0, .for_gc = false, |
| - .for_btnc = false |
| + .for_btnc = false, .for_shadow = false |
| }; |
| |
| return insert_inode_locked4(inode, ino, nilfs_iget_test, &args); |
| @@ -543,6 +545,12 @@ static int nilfs_iget_test(struct inode *inode, void *opaque) |
| } else if (args->for_btnc) { |
| return 0; |
| } |
| + if (test_bit(NILFS_I_SHADOW, &ii->i_state)) { |
| + if (!args->for_shadow) |
| + return 0; |
| + } else if (args->for_shadow) { |
| + return 0; |
| + } |
| |
| if (!test_bit(NILFS_I_GCINODE, &ii->i_state)) |
| return !args->for_gc; |
| @@ -564,6 +572,8 @@ static int nilfs_iget_set(struct inode *inode, void *opaque) |
| NILFS_I(inode)->i_state = BIT(NILFS_I_GCINODE); |
| if (args->for_btnc) |
| NILFS_I(inode)->i_state |= BIT(NILFS_I_BTNC); |
| + if (args->for_shadow) |
| + NILFS_I(inode)->i_state |= BIT(NILFS_I_SHADOW); |
| return 0; |
| } |
| |
| @@ -572,7 +582,7 @@ struct inode *nilfs_ilookup(struct super_block *sb, struct nilfs_root *root, |
| { |
| struct nilfs_iget_args args = { |
| .ino = ino, .root = root, .cno = 0, .for_gc = false, |
| - .for_btnc = false |
| + .for_btnc = false, .for_shadow = false |
| }; |
| |
| return ilookup5(sb, ino, nilfs_iget_test, &args); |
| @@ -583,7 +593,7 @@ struct inode *nilfs_iget_locked(struct super_block *sb, struct nilfs_root *root, |
| { |
| struct nilfs_iget_args args = { |
| .ino = ino, .root = root, .cno = 0, .for_gc = false, |
| - .for_btnc = false |
| + .for_btnc = false, .for_shadow = false |
| }; |
| |
| return iget5_locked(sb, ino, nilfs_iget_test, nilfs_iget_set, &args); |
| @@ -615,7 +625,7 @@ struct inode *nilfs_iget_for_gc(struct super_block *sb, unsigned long ino, |
| { |
| struct nilfs_iget_args args = { |
| .ino = ino, .root = NULL, .cno = cno, .for_gc = true, |
| - .for_btnc = false |
| + .for_btnc = false, .for_shadow = false |
| }; |
| struct inode *inode; |
| int err; |
| @@ -662,6 +672,7 @@ int nilfs_attach_btree_node_cache(struct inode *inode) |
| args.cno = ii->i_cno; |
| args.for_gc = test_bit(NILFS_I_GCINODE, &ii->i_state) != 0; |
| args.for_btnc = true; |
| + args.for_shadow = test_bit(NILFS_I_SHADOW, &ii->i_state) != 0; |
| |
| btnc_inode = iget5_locked(inode->i_sb, inode->i_ino, nilfs_iget_test, |
| nilfs_iget_set, &args); |
| @@ -697,6 +708,50 @@ void nilfs_detach_btree_node_cache(struct inode *inode) |
| } |
| } |
| |
| +/** |
| + * nilfs_iget_for_shadow - obtain inode for shadow mapping |
| + * @inode: inode object that uses shadow mapping |
| + * |
| + * nilfs_iget_for_shadow() allocates a pair of inodes that holds page |
| + * caches for shadow mapping. The page cache for data pages is set up |
| + * in one inode and the one for b-tree node pages is set up in the |
| + * other inode, which is attached to the former inode. |
| + * |
| + * Return Value: On success, a pointer to the inode for data pages is |
| + * returned. On errors, one of the following negative error code is returned |
| + * in a pointer type. |
| + * |
| + * %-ENOMEM - Insufficient memory available. |
| + */ |
| +struct inode *nilfs_iget_for_shadow(struct inode *inode) |
| +{ |
| + struct nilfs_iget_args args = { |
| + .ino = inode->i_ino, .root = NULL, .cno = 0, .for_gc = false, |
| + .for_btnc = false, .for_shadow = true |
| + }; |
| + struct inode *s_inode; |
| + int err; |
| + |
| + s_inode = iget5_locked(inode->i_sb, inode->i_ino, nilfs_iget_test, |
| + nilfs_iget_set, &args); |
| + if (unlikely(!s_inode)) |
| + return ERR_PTR(-ENOMEM); |
| + if (!(s_inode->i_state & I_NEW)) |
| + return inode; |
| + |
| + NILFS_I(s_inode)->i_flags = 0; |
| + memset(NILFS_I(s_inode)->i_bmap, 0, sizeof(struct nilfs_bmap)); |
| + mapping_set_gfp_mask(s_inode->i_mapping, GFP_NOFS); |
| + |
| + err = nilfs_attach_btree_node_cache(s_inode); |
| + if (unlikely(err)) { |
| + iget_failed(s_inode); |
| + return ERR_PTR(err); |
| + } |
| + unlock_new_inode(s_inode); |
| + return s_inode; |
| +} |
| + |
| void nilfs_write_inode_common(struct inode *inode, |
| struct nilfs_inode *raw_inode, int has_bmap) |
| { |
| diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c |
| index 3a1200220b97..7c9055d767d1 100644 |
| --- a/fs/nilfs2/mdt.c |
| +++ b/fs/nilfs2/mdt.c |
| @@ -469,9 +469,18 @@ int nilfs_mdt_init(struct inode *inode, gfp_t gfp_mask, size_t objsz) |
| void nilfs_mdt_clear(struct inode *inode) |
| { |
| struct nilfs_mdt_info *mdi = NILFS_MDT(inode); |
| + struct nilfs_shadow_map *shadow = mdi->mi_shadow; |
| |
| if (mdi->mi_palloc_cache) |
| nilfs_palloc_destroy_cache(inode); |
| + |
| + if (shadow) { |
| + struct inode *s_inode = shadow->inode; |
| + |
| + shadow->inode = NULL; |
| + iput(s_inode); |
| + mdi->mi_shadow = NULL; |
| + } |
| } |
| |
| /** |
| @@ -505,12 +514,15 @@ int nilfs_mdt_setup_shadow_map(struct inode *inode, |
| struct nilfs_shadow_map *shadow) |
| { |
| struct nilfs_mdt_info *mi = NILFS_MDT(inode); |
| + struct inode *s_inode; |
| |
| INIT_LIST_HEAD(&shadow->frozen_buffers); |
| - address_space_init_once(&shadow->frozen_data); |
| - nilfs_mapping_init(&shadow->frozen_data, inode); |
| - address_space_init_once(&shadow->frozen_btnodes); |
| - nilfs_mapping_init(&shadow->frozen_btnodes, inode); |
| + |
| + s_inode = nilfs_iget_for_shadow(inode); |
| + if (IS_ERR(s_inode)) |
| + return PTR_ERR(s_inode); |
| + |
| + shadow->inode = s_inode; |
| mi->mi_shadow = shadow; |
| return 0; |
| } |
| @@ -524,13 +536,14 @@ int nilfs_mdt_save_to_shadow_map(struct inode *inode) |
| struct nilfs_mdt_info *mi = NILFS_MDT(inode); |
| struct nilfs_inode_info *ii = NILFS_I(inode); |
| struct nilfs_shadow_map *shadow = mi->mi_shadow; |
| + struct inode *s_inode = shadow->inode; |
| int ret; |
| |
| - ret = nilfs_copy_dirty_pages(&shadow->frozen_data, inode->i_mapping); |
| + ret = nilfs_copy_dirty_pages(s_inode->i_mapping, inode->i_mapping); |
| if (ret) |
| goto out; |
| |
| - ret = nilfs_copy_dirty_pages(&shadow->frozen_btnodes, |
| + ret = nilfs_copy_dirty_pages(NILFS_I(s_inode)->i_assoc_inode->i_mapping, |
| ii->i_assoc_inode->i_mapping); |
| if (ret) |
| goto out; |
| @@ -547,7 +560,7 @@ int nilfs_mdt_freeze_buffer(struct inode *inode, struct buffer_head *bh) |
| struct page *page; |
| int blkbits = inode->i_blkbits; |
| |
| - page = grab_cache_page(&shadow->frozen_data, bh->b_page->index); |
| + page = grab_cache_page(shadow->inode->i_mapping, bh->b_page->index); |
| if (!page) |
| return -ENOMEM; |
| |
| @@ -579,7 +592,7 @@ nilfs_mdt_get_frozen_buffer(struct inode *inode, struct buffer_head *bh) |
| struct page *page; |
| int n; |
| |
| - page = find_lock_page(&shadow->frozen_data, bh->b_page->index); |
| + page = find_lock_page(shadow->inode->i_mapping, bh->b_page->index); |
| if (page) { |
| if (page_has_buffers(page)) { |
| n = bh_offset(bh) >> inode->i_blkbits; |
| @@ -620,11 +633,11 @@ void nilfs_mdt_restore_from_shadow_map(struct inode *inode) |
| nilfs_palloc_clear_cache(inode); |
| |
| nilfs_clear_dirty_pages(inode->i_mapping, true); |
| - nilfs_copy_back_pages(inode->i_mapping, &shadow->frozen_data); |
| + nilfs_copy_back_pages(inode->i_mapping, shadow->inode->i_mapping); |
| |
| nilfs_clear_dirty_pages(ii->i_assoc_inode->i_mapping, true); |
| nilfs_copy_back_pages(ii->i_assoc_inode->i_mapping, |
| - &shadow->frozen_btnodes); |
| + NILFS_I(shadow->inode)->i_assoc_inode->i_mapping); |
| |
| nilfs_bmap_restore(ii->i_bmap, &shadow->bmap_store); |
| |
| @@ -639,10 +652,11 @@ void nilfs_mdt_clear_shadow_map(struct inode *inode) |
| { |
| struct nilfs_mdt_info *mi = NILFS_MDT(inode); |
| struct nilfs_shadow_map *shadow = mi->mi_shadow; |
| + struct inode *shadow_btnc_inode = NILFS_I(shadow->inode)->i_assoc_inode; |
| |
| down_write(&mi->mi_sem); |
| nilfs_release_frozen_buffers(shadow); |
| - truncate_inode_pages(&shadow->frozen_data, 0); |
| - truncate_inode_pages(&shadow->frozen_btnodes, 0); |
| + truncate_inode_pages(shadow->inode->i_mapping, 0); |
| + truncate_inode_pages(shadow_btnc_inode->i_mapping, 0); |
| up_write(&mi->mi_sem); |
| } |
| diff --git a/fs/nilfs2/mdt.h b/fs/nilfs2/mdt.h |
| index e77aea4bb921..9d8ac0d27c16 100644 |
| --- a/fs/nilfs2/mdt.h |
| +++ b/fs/nilfs2/mdt.h |
| @@ -18,14 +18,12 @@ |
| /** |
| * struct nilfs_shadow_map - shadow mapping of meta data file |
| * @bmap_store: shadow copy of bmap state |
| - * @frozen_data: shadowed dirty data pages |
| - * @frozen_btnodes: shadowed dirty b-tree nodes' pages |
| + * @inode: holder of page caches used in shadow mapping |
| * @frozen_buffers: list of frozen buffers |
| */ |
| struct nilfs_shadow_map { |
| struct nilfs_bmap_store bmap_store; |
| - struct address_space frozen_data; |
| - struct address_space frozen_btnodes; |
| + struct inode *inode; |
| struct list_head frozen_buffers; |
| }; |
| |
| diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h |
| index a63aa5b5993c..8699bdc9e391 100644 |
| --- a/fs/nilfs2/nilfs.h |
| +++ b/fs/nilfs2/nilfs.h |
| @@ -92,6 +92,7 @@ enum { |
| NILFS_I_BMAP, /* has bmap and btnode_cache */ |
| NILFS_I_GCINODE, /* inode for GC, on memory only */ |
| NILFS_I_BTNC, /* inode for btree node cache */ |
| + NILFS_I_SHADOW, /* inode for shadowed page cache */ |
| }; |
| |
| /* |
| @@ -261,6 +262,7 @@ extern struct inode *nilfs_iget_for_gc(struct super_block *sb, |
| unsigned long ino, __u64 cno); |
| int nilfs_attach_btree_node_cache(struct inode *inode); |
| void nilfs_detach_btree_node_cache(struct inode *inode); |
| +struct inode *nilfs_iget_for_shadow(struct inode *inode); |
| extern void nilfs_update_inode(struct inode *, struct buffer_head *, int); |
| extern void nilfs_truncate(struct inode *); |
| extern void nilfs_evict_inode(struct inode *); |
| -- |
| 2.35.1 |
| |