nilfs2: fix lockdep warnings during disk space reclamation
authorRyusuke Konishi <konishi.ryusuke@gmail.com>
Fri, 1 Apr 2022 18:28:21 +0000 (11:28 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 25 May 2022 07:57:26 +0000 (09:57 +0200)
[ 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
fs/nilfs2/inode.c
fs/nilfs2/mdt.c
fs/nilfs2/mdt.h
fs/nilfs2/nilfs.h

index 8bccdf1158fce280a5fe43cdd91a0c60ba743943..1a3d183027b9e171524099d59620aa725d6d573a 100644 (file)
@@ -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)
index 7b2bc41733131e51859b9dc0e05c0e2a58aafb8e..2466f8b8be95c09ab26f7ba8f0192f93140eaf97 100644 (file)
@@ -30,6 +30,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;
@@ -37,6 +38,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);
@@ -317,7 +319,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);
@@ -536,6 +538,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;
@@ -557,6 +565,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;
 }
 
@@ -565,7 +575,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);
@@ -576,7 +586,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);
@@ -608,7 +618,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;
@@ -655,6 +665,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);
@@ -690,6 +701,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)
 {
index 21acd2d4b4d42c275d66ae37018a0694180b7b90..131b5add32eebe925e91b595b1aeb7bddc56d3fb 100644 (file)
@@ -470,9 +470,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;
+       }
 }
 
 /**
@@ -506,12 +515,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;
 }
@@ -525,13 +537,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;
@@ -548,7 +561,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;
 
@@ -580,7 +593,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;
@@ -621,11 +634,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);
 
@@ -640,10 +653,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);
 }
index e77aea4bb921c37c212558b11137022e796a5bae..9d8ac0d27c16e7528929fdd6bed2226672593ff0 100644 (file)
 /**
  * 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;
 };
 
index 6c5148de84ffc46efde96bca7fc24edc372654b0..7dcb77d387593376422ed043f2015a93c438afd2 100644 (file)
@@ -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 */
 };
 
 /*
@@ -263,6 +264,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 *);