On Wed, Sep 17, 2025 at 3:59 PM Max Kellermann max.kellermann@ionos.com wrote:
+void ceph_iput_async(struct inode *inode) +{
if (unlikely(!inode))
return;
if (likely(atomic_add_unless(&inode->i_count, -1, 1)))
/* somebody else is holding another reference -
* nothing left to do for us
*/
return;
doutc(ceph_inode_to_fs_client(inode)->client, "%p %llx.%llx\n", inode, ceph_vinop(inode));
/* simply queue a ceph_inode_work() (donating the remaining
* reference) without setting i_work_mask bit; other than
* putting the reference, there is nothing to do
*/
WARN_ON_ONCE(!queue_work(ceph_inode_to_fs_client(inode)->inode_wq,
&ceph_inode(inode)->i_work));
/* note: queue_work() cannot fail; it i_work were already
* queued, then it would be holding another reference, but no
* such reference exists
*/
+}
Folks! Guess what I just found in Linux 5.13: commit 23c2c76ead54 ("ceph: eliminate ceph_async_iput()")
-/* - * Put reference to inode, but avoid calling iput_final() in current thread. - * iput_final() may wait for reahahead pages. The wait can cause deadlock in - * some contexts. - */ -void ceph_async_iput(struct inode *inode) -{ - if (!inode) - return; - for (;;) { - if (atomic_add_unless(&inode->i_count, -1, 1)) - break; - if (queue_work(ceph_inode_to_client(inode)->inode_wq, - &ceph_inode(inode)->i_work)) - break; - /* queue work failed, i_count must be at least 2 */ - } -}
This looks very much like the code I wrote - only with a loop for queue_work() failures that cannot happen here.
Jeff Layton removed this because "Now that we don't need to hold session->s_mutex or the snap_rwsem when calling ceph_check_caps, we can eliminate ceph_async_iput and just use normal iput calls." Our servers proved this assessment wrong. Calling iput() while holding a lock is obviously bad, but there are more reasons why iput() calls can be dangerous and inappropriate. I guess I can add a "Fixes" tag, after all.