[PATCH] fs: add might_sleep() annotation to iput() and more

Max Kellermann posted 1 patch 2 weeks ago
fs/inode.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
[PATCH] fs: add might_sleep() annotation to iput() and more
Posted by Max Kellermann 2 weeks ago
When iput() drops the reference counter to zero, it may sleep via
inode_wait_for_writeback().  This happens rarely because it's usually
the dcache which evicts inodes, but really iput() should only ever be
called in contexts where sleeping is allowed.  This annotation allows
finding buggy callers.

Additionally, this patch annotates a few low-level functions that can
call iput() conditionally.

Cc: Mateusz Guzik <mjguzik@gmail.com>
Signed-off-by: Max Kellermann <max.kellermann@ionos.com>
---
For discussion of a loosely-related Ceph deadlock bug, see:
 https://lore.kernel.org/ceph-devel/CAKPOu+-xr+nQuzfjtQCgZCqPtec=8uQiz29H5+5AeFzTbp=1rw@mail.gmail.com/T/
 https://lore.kernel.org/ceph-devel/CAGudoHF0+JfqxB_fQxeo7Pbadjq7UA1JFH4QmfFS1hDHunNmtw@mail.gmail.com/T/
---
 fs/inode.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/fs/inode.c b/fs/inode.c
index fc2edb5a4dbe..ec9339024ac3 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1279,6 +1279,8 @@ struct inode *inode_insert5(struct inode *inode, unsigned long hashval,
 	struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval);
 	struct inode *old;
 
+	might_sleep();
+
 again:
 	spin_lock(&inode_hash_lock);
 	old = find_inode(inode->i_sb, head, test, data, true);
@@ -1382,6 +1384,8 @@ struct inode *iget5_locked_rcu(struct super_block *sb, unsigned long hashval,
 	struct hlist_head *head = inode_hashtable + hash(sb, hashval);
 	struct inode *inode, *new;
 
+	might_sleep();
+
 again:
 	inode = find_inode(sb, head, test, data, false);
 	if (inode) {
@@ -1422,6 +1426,9 @@ struct inode *iget_locked(struct super_block *sb, unsigned long ino)
 {
 	struct hlist_head *head = inode_hashtable + hash(sb, ino);
 	struct inode *inode;
+
+	might_sleep();
+
 again:
 	inode = find_inode_fast(sb, head, ino, false);
 	if (inode) {
@@ -1605,6 +1612,9 @@ struct inode *ilookup5(struct super_block *sb, unsigned long hashval,
 		int (*test)(struct inode *, void *), void *data)
 {
 	struct inode *inode;
+
+	might_sleep();
+
 again:
 	inode = ilookup5_nowait(sb, hashval, test, data);
 	if (inode) {
@@ -1630,6 +1640,9 @@ struct inode *ilookup(struct super_block *sb, unsigned long ino)
 {
 	struct hlist_head *head = inode_hashtable + hash(sb, ino);
 	struct inode *inode;
+
+	might_sleep();
+
 again:
 	inode = find_inode_fast(sb, head, ino, false);
 
@@ -1780,6 +1793,8 @@ int insert_inode_locked(struct inode *inode)
 	ino_t ino = inode->i_ino;
 	struct hlist_head *head = inode_hashtable + hash(sb, ino);
 
+	might_sleep();
+
 	while (1) {
 		struct inode *old = NULL;
 		spin_lock(&inode_hash_lock);
@@ -1826,6 +1841,8 @@ int insert_inode_locked4(struct inode *inode, unsigned long hashval,
 {
 	struct inode *old;
 
+	might_sleep();
+
 	inode->i_state |= I_CREATING;
 	old = inode_insert5(inode, hashval, test, NULL, data);
 
@@ -1908,6 +1925,7 @@ static void iput_final(struct inode *inode)
  */
 void iput(struct inode *inode)
 {
+	might_sleep();
 	if (unlikely(!inode))
 		return;
 
-- 
2.47.3
Re: [PATCH] fs: add might_sleep() annotation to iput() and more
Posted by Christian Brauner 1 week, 5 days ago
On Wed, 17 Sep 2025 17:36:31 +0200, Max Kellermann wrote:
> When iput() drops the reference counter to zero, it may sleep via
> inode_wait_for_writeback().  This happens rarely because it's usually
> the dcache which evicts inodes, but really iput() should only ever be
> called in contexts where sleeping is allowed.  This annotation allows
> finding buggy callers.
> 
> Additionally, this patch annotates a few low-level functions that can
> call iput() conditionally.
> 
> [...]

Applied to the vfs-6.18.inode.refcount.preliminaries branch of the vfs/vfs.git tree.
Patches in the vfs-6.18.inode.refcount.preliminaries branch should appear in linux-next soon.

Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.

It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.

Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.

tree:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs-6.18.inode.refcount.preliminaries

[1/1] fs: add might_sleep() annotation to iput() and more
      https://git.kernel.org/vfs/vfs/c/2ef435a872ab
Re: [PATCH] fs: add might_sleep() annotation to iput() and more
Posted by Jan Kara 2 weeks ago
On Wed 17-09-25 17:36:31, Max Kellermann wrote:
> When iput() drops the reference counter to zero, it may sleep via
> inode_wait_for_writeback().  This happens rarely because it's usually
> the dcache which evicts inodes, but really iput() should only ever be
> called in contexts where sleeping is allowed.  This annotation allows
> finding buggy callers.
> 
> Additionally, this patch annotates a few low-level functions that can
> call iput() conditionally.
> 
> Cc: Mateusz Guzik <mjguzik@gmail.com>
> Signed-off-by: Max Kellermann <max.kellermann@ionos.com>

Looks sensible. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
> For discussion of a loosely-related Ceph deadlock bug, see:
>  https://lore.kernel.org/ceph-devel/CAKPOu+-xr+nQuzfjtQCgZCqPtec=8uQiz29H5+5AeFzTbp=1rw@mail.gmail.com/T/
>  https://lore.kernel.org/ceph-devel/CAGudoHF0+JfqxB_fQxeo7Pbadjq7UA1JFH4QmfFS1hDHunNmtw@mail.gmail.com/T/
> ---
>  fs/inode.c | 18 ++++++++++++++++++
>  1 file changed, 18 insertions(+)
> 
> diff --git a/fs/inode.c b/fs/inode.c
> index fc2edb5a4dbe..ec9339024ac3 100644
> --- a/fs/inode.c
> +++ b/fs/inode.c
> @@ -1279,6 +1279,8 @@ struct inode *inode_insert5(struct inode *inode, unsigned long hashval,
>  	struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval);
>  	struct inode *old;
>  
> +	might_sleep();
> +
>  again:
>  	spin_lock(&inode_hash_lock);
>  	old = find_inode(inode->i_sb, head, test, data, true);
> @@ -1382,6 +1384,8 @@ struct inode *iget5_locked_rcu(struct super_block *sb, unsigned long hashval,
>  	struct hlist_head *head = inode_hashtable + hash(sb, hashval);
>  	struct inode *inode, *new;
>  
> +	might_sleep();
> +
>  again:
>  	inode = find_inode(sb, head, test, data, false);
>  	if (inode) {
> @@ -1422,6 +1426,9 @@ struct inode *iget_locked(struct super_block *sb, unsigned long ino)
>  {
>  	struct hlist_head *head = inode_hashtable + hash(sb, ino);
>  	struct inode *inode;
> +
> +	might_sleep();
> +
>  again:
>  	inode = find_inode_fast(sb, head, ino, false);
>  	if (inode) {
> @@ -1605,6 +1612,9 @@ struct inode *ilookup5(struct super_block *sb, unsigned long hashval,
>  		int (*test)(struct inode *, void *), void *data)
>  {
>  	struct inode *inode;
> +
> +	might_sleep();
> +
>  again:
>  	inode = ilookup5_nowait(sb, hashval, test, data);
>  	if (inode) {
> @@ -1630,6 +1640,9 @@ struct inode *ilookup(struct super_block *sb, unsigned long ino)
>  {
>  	struct hlist_head *head = inode_hashtable + hash(sb, ino);
>  	struct inode *inode;
> +
> +	might_sleep();
> +
>  again:
>  	inode = find_inode_fast(sb, head, ino, false);
>  
> @@ -1780,6 +1793,8 @@ int insert_inode_locked(struct inode *inode)
>  	ino_t ino = inode->i_ino;
>  	struct hlist_head *head = inode_hashtable + hash(sb, ino);
>  
> +	might_sleep();
> +
>  	while (1) {
>  		struct inode *old = NULL;
>  		spin_lock(&inode_hash_lock);
> @@ -1826,6 +1841,8 @@ int insert_inode_locked4(struct inode *inode, unsigned long hashval,
>  {
>  	struct inode *old;
>  
> +	might_sleep();
> +
>  	inode->i_state |= I_CREATING;
>  	old = inode_insert5(inode, hashval, test, NULL, data);
>  
> @@ -1908,6 +1925,7 @@ static void iput_final(struct inode *inode)
>   */
>  void iput(struct inode *inode)
>  {
> +	might_sleep();
>  	if (unlikely(!inode))
>  		return;
>  
> -- 
> 2.47.3
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR