For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org.
***
Subject: Re: [syzbot] [nilfs?] INFO: task hung in find_inode (2)
Author: konishi.ryusuke@gmail.com
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 89a312991dc6
Avoid a three-way deadlock involving find_inode(),
nilfs_evict_inode(), and the log writer by checking the inode bitmap
during normal inode loading.
diff --git a/fs/nilfs2/alloc.c b/fs/nilfs2/alloc.c
index 7b1cd2baefcf..32f54382f95d 100644
--- a/fs/nilfs2/alloc.c
+++ b/fs/nilfs2/alloc.c
@@ -404,6 +404,13 @@ static size_t nilfs_palloc_bitmap_offset(const struct buffer_head *bh)
return offset_in_folio(bh->b_folio, bh->b_data);
}
+static size_t nilfs_palloc_entry_offset_in_block(
+ const struct inode *inode, unsigned long entry_index_in_group)
+{
+ return (entry_index_in_group % NILFS_MDT(inode)->mi_entries_per_block)
+ * NILFS_MDT(inode)->mi_entry_size;
+}
+
/**
* nilfs_palloc_entry_offset - calculate the byte offset of an entry in the
* folio containing it
@@ -416,14 +423,61 @@ static size_t nilfs_palloc_bitmap_offset(const struct buffer_head *bh)
size_t nilfs_palloc_entry_offset(const struct inode *inode, __u64 nr,
const struct buffer_head *bh)
{
- unsigned long entry_index_in_group, entry_index_in_block;
+ unsigned long entry_index_in_group;
nilfs_palloc_group(inode, nr, &entry_index_in_group);
- entry_index_in_block = entry_index_in_group %
- NILFS_MDT(inode)->mi_entries_per_block;
return offset_in_folio(bh->b_folio, bh->b_data) +
- entry_index_in_block * NILFS_MDT(inode)->mi_entry_size;
+ nilfs_palloc_entry_offset_in_block(inode, entry_index_in_group);
+}
+
+/**
+ * nilfs_palloc_kmap_entry - map the page containing the entry and calculate
+ * the entry's memory address on it
+ * @inode: inode of metadata file using this allocator
+ * @nr: serial number of the entry (e.g. inode number)
+ * @entry_bh: buffer head of the entry block
+ * @bitmap_check: whether to perform bitmap testing
+ *
+ * Return: Pointer to the mapped entry on success, or an ERR_PTR() encoded
+ * error code on failure. Specifically, returns %-ENOENT if @bitmap_check
+ * is true and the bitmap block managing the entry does not exist, or if the
+ * corresponding bit for the entry is not set in the bitmap. Note that this
+ * function never fails when @bitmap_check is false.
+ */
+void *nilfs_palloc_kmap_entry(struct inode *inode, __u64 nr,
+ const struct buffer_head *entry_bh, bool bitmap_check)
+{
+ unsigned long group, entry_index_in_group;
+ size_t entry_offset;
+
+ group = nilfs_palloc_group(inode, nr, &entry_index_in_group);
+ if (bitmap_check) {
+ struct buffer_head *bitmap_bh;
+ unsigned char *bitmap;
+ size_t bitmap_offset;
+ int ret;
+
+ ret = nilfs_palloc_get_bitmap_block(inode, group, 0,
+ &bitmap_bh);
+ if (unlikely(ret))
+ return ERR_PTR(ret);
+
+ bitmap_offset = nilfs_palloc_bitmap_offset(bitmap_bh);
+ bitmap = kmap_local_folio(bitmap_bh->b_folio, bitmap_offset);
+ if (!test_bit_le(entry_index_in_group, bitmap))
+ ret = -ENOENT;
+ kunmap_local(bitmap);
+ brelse(bitmap_bh);
+
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ entry_offset = offset_in_folio(entry_bh->b_folio, entry_bh->b_data) +
+ nilfs_palloc_entry_offset_in_block(inode, entry_index_in_group);
+
+ return kmap_local_folio(entry_bh->b_folio, entry_offset);
}
/**
diff --git a/fs/nilfs2/alloc.h b/fs/nilfs2/alloc.h
index 046d876ea3e0..2c184d0ff097 100644
--- a/fs/nilfs2/alloc.h
+++ b/fs/nilfs2/alloc.h
@@ -35,6 +35,8 @@ int nilfs_palloc_get_entry_block(struct inode *, __u64, int,
struct buffer_head **);
size_t nilfs_palloc_entry_offset(const struct inode *inode, __u64 nr,
const struct buffer_head *bh);
+void *nilfs_palloc_kmap_entry(struct inode *inode, __u64 nr,
+ const struct buffer_head *entry_bh, bool bitmap_check);
int nilfs_palloc_count_max_entries(struct inode *, u64, u64 *);
diff --git a/fs/nilfs2/ifile.h b/fs/nilfs2/ifile.h
index d38a46f5ae41..bc47bb139e1c 100644
--- a/fs/nilfs2/ifile.h
+++ b/fs/nilfs2/ifile.h
@@ -19,11 +19,10 @@
static inline struct nilfs_inode *
-nilfs_ifile_map_inode(struct inode *ifile, u64 ino, struct buffer_head *ibh)
+nilfs_ifile_map_inode(struct inode *ifile, u64 ino,
+ const struct buffer_head *ibh, bool bitmap_check)
{
- size_t __offset_in_folio = nilfs_palloc_entry_offset(ifile, ino, ibh);
-
- return kmap_local_folio(ibh->b_folio, __offset_in_folio);
+ return nilfs_palloc_kmap_entry(ifile, ino, ibh, bitmap_check);
}
static inline void nilfs_ifile_unmap_inode(struct nilfs_inode *raw_inode)
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index 34e6096069ad..09de917b4b82 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -455,7 +455,14 @@ static int __nilfs_read_inode(struct super_block *sb,
if (unlikely(err))
goto bad_inode;
- raw_inode = nilfs_ifile_map_inode(root->ifile, ino, bh);
+ raw_inode = nilfs_ifile_map_inode(root->ifile, ino, bh, true);
+ if (IS_ERR(raw_inode)) {
+ err = PTR_ERR(raw_inode);
+ if (err == -ENOENT)
+ err = -ESTALE;
+ brelse(bh);
+ goto bad_inode;
+ }
err = nilfs_read_inode_common(inode, raw_inode);
if (err)
@@ -743,7 +750,7 @@ void nilfs_update_inode(struct inode *inode, struct buffer_head *ibh, int flags)
struct inode *ifile = ii->i_root->ifile;
struct nilfs_inode *raw_inode;
- raw_inode = nilfs_ifile_map_inode(ifile, ino, ibh);
+ raw_inode = nilfs_ifile_map_inode(ifile, ino, ibh, false);
if (test_and_clear_bit(NILFS_I_NEW, &ii->i_state))
memset(raw_inode, 0, NILFS_MDT(ifile)->mi_entry_size);
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index 829573cb6131..23b24373befc 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -895,7 +895,7 @@ static void nilfs_fill_in_file_bmap(struct inode *ifile,
ibh = ii->i_bh;
BUG_ON(!ibh);
raw_inode = nilfs_ifile_map_inode(ifile, ii->vfs_inode.i_ino,
- ibh);
+ ibh, false);
nilfs_bmap_write(ii->i_bmap, raw_inode);
nilfs_ifile_unmap_inode(raw_inode);
}
--
2.53.0
© 2016 - 2026 Red Hat, Inc.