Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e9758b6e authored by Ryusuke Konishi's avatar Ryusuke Konishi Committed by Greg Kroah-Hartman
Browse files

nilfs2: fix lockdep warnings during disk space reclamation

[ 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: default avatarRyusuke Konishi <konishi.ryusuke@gmail.com>
Tested-by: default avatarRyusuke 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 605babb9
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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)
+59 −4
Original line number Diff line number Diff line
@@ -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)
{
+26 −12
Original line number Diff line number Diff line
@@ -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);
}
+2 −4
Original line number Diff line number Diff line
@@ -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;
};

+2 −0
Original line number Diff line number Diff line
@@ -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 *);